2022-01-27 12:58:56 +01:00
|
|
|
use anyhow::{ensure, Result};
|
2022-01-26 00:09:29 -08:00
|
|
|
use itertools::Itertools;
|
|
|
|
|
use plonky2::field::extension_field::Extendable;
|
2022-01-27 12:58:56 +01:00
|
|
|
use plonky2::field::field_types::Field;
|
2022-01-26 16:08:04 +01:00
|
|
|
use plonky2::field::polynomial::{PolynomialCoeffs, PolynomialValues};
|
|
|
|
|
use plonky2::field::zero_poly_coset::ZeroPolyOnCoset;
|
2022-01-26 00:09:29 -08:00
|
|
|
use plonky2::fri::oracle::PolynomialBatch;
|
|
|
|
|
use plonky2::hash::hash_types::RichField;
|
|
|
|
|
use plonky2::iop::challenger::Challenger;
|
|
|
|
|
use plonky2::plonk::config::GenericConfig;
|
|
|
|
|
use plonky2::timed;
|
|
|
|
|
use plonky2::util::timing::TimingTree;
|
|
|
|
|
use plonky2::util::transpose;
|
2022-02-04 16:36:22 +01:00
|
|
|
use plonky2_util::{log2_ceil, log2_strict};
|
2022-01-26 00:09:29 -08:00
|
|
|
use rayon::prelude::*;
|
|
|
|
|
|
|
|
|
|
use crate::config::StarkConfig;
|
2022-01-26 16:08:04 +01:00
|
|
|
use crate::constraint_consumer::ConstraintConsumer;
|
2022-01-29 12:49:00 +01:00
|
|
|
use crate::proof::{StarkOpeningSet, StarkProof, StarkProofWithPublicInputs};
|
2022-01-26 00:09:29 -08:00
|
|
|
use crate::stark::Stark;
|
2022-01-26 16:08:04 +01:00
|
|
|
use crate::vars::StarkEvaluationVars;
|
2022-01-26 00:09:29 -08:00
|
|
|
|
|
|
|
|
pub fn prove<F, C, S, const D: usize>(
|
|
|
|
|
stark: S,
|
2022-01-31 18:00:07 +01:00
|
|
|
config: &StarkConfig,
|
2022-01-26 00:09:29 -08:00
|
|
|
trace: Vec<[F; S::COLUMNS]>,
|
2022-01-28 05:02:31 +01:00
|
|
|
public_inputs: [F; S::PUBLIC_INPUTS],
|
2022-01-26 00:09:29 -08:00
|
|
|
timing: &mut TimingTree,
|
2022-01-29 12:49:00 +01:00
|
|
|
) -> Result<StarkProofWithPublicInputs<F, C, D>>
|
2022-01-26 00:09:29 -08:00
|
|
|
where
|
|
|
|
|
F: RichField + Extendable<D>,
|
|
|
|
|
C: GenericConfig<D, F = F>,
|
|
|
|
|
S: Stark<F, D>,
|
|
|
|
|
[(); S::COLUMNS]:,
|
2022-01-26 16:08:04 +01:00
|
|
|
[(); S::PUBLIC_INPUTS]:,
|
2022-01-26 00:09:29 -08:00
|
|
|
{
|
2022-01-27 12:58:56 +01:00
|
|
|
let degree = trace.len();
|
|
|
|
|
let degree_bits = log2_strict(degree);
|
2022-01-26 00:09:29 -08:00
|
|
|
|
|
|
|
|
let trace_vecs = trace.into_iter().map(|row| row.to_vec()).collect_vec();
|
|
|
|
|
let trace_col_major: Vec<Vec<F>> = transpose(&trace_vecs);
|
|
|
|
|
|
|
|
|
|
let trace_poly_values: Vec<PolynomialValues<F>> = timed!(
|
|
|
|
|
timing,
|
|
|
|
|
"compute trace polynomials",
|
|
|
|
|
trace_col_major
|
|
|
|
|
.par_iter()
|
|
|
|
|
.map(|column| PolynomialValues::new(column.clone()))
|
|
|
|
|
.collect()
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let rate_bits = config.fri_config.rate_bits;
|
|
|
|
|
let cap_height = config.fri_config.cap_height;
|
|
|
|
|
let trace_commitment = timed!(
|
|
|
|
|
timing,
|
|
|
|
|
"compute trace commitment",
|
|
|
|
|
PolynomialBatch::<F, C, D>::from_values(
|
|
|
|
|
trace_poly_values,
|
|
|
|
|
rate_bits,
|
|
|
|
|
false,
|
|
|
|
|
cap_height,
|
|
|
|
|
timing,
|
|
|
|
|
None,
|
|
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
|
2022-01-26 16:08:04 +01:00
|
|
|
let trace_cap = trace_commitment.merkle_tree.cap.clone();
|
|
|
|
|
let mut challenger = Challenger::new();
|
|
|
|
|
challenger.observe_cap(&trace_cap);
|
|
|
|
|
|
|
|
|
|
let alphas = challenger.get_n_challenges(config.num_challenges);
|
2022-01-27 12:58:56 +01:00
|
|
|
let quotient_polys = compute_quotient_polys::<F, C, S, D>(
|
2022-01-26 16:08:04 +01:00
|
|
|
&stark,
|
|
|
|
|
&trace_commitment,
|
2022-01-28 05:02:31 +01:00
|
|
|
public_inputs,
|
2022-01-28 17:06:40 +01:00
|
|
|
alphas,
|
2022-01-26 16:08:04 +01:00
|
|
|
degree_bits,
|
|
|
|
|
rate_bits,
|
|
|
|
|
);
|
2022-01-27 12:58:56 +01:00
|
|
|
let all_quotient_chunks = quotient_polys
|
|
|
|
|
.into_par_iter()
|
|
|
|
|
.flat_map(|mut quotient_poly| {
|
|
|
|
|
quotient_poly
|
2022-02-06 23:35:46 -08:00
|
|
|
.trim_to_len(degree * stark.quotient_degree_factor())
|
|
|
|
|
.expect("Quotient has failed, the vanishing polynomial is not divisible by Z_H");
|
2022-01-27 13:27:06 +01:00
|
|
|
// Split quotient into degree-n chunks.
|
2022-01-27 12:58:56 +01:00
|
|
|
quotient_poly.chunks(degree)
|
|
|
|
|
})
|
|
|
|
|
.collect();
|
|
|
|
|
let quotient_commitment = timed!(
|
|
|
|
|
timing,
|
|
|
|
|
"compute quotient commitment",
|
|
|
|
|
PolynomialBatch::from_coeffs(
|
|
|
|
|
all_quotient_chunks,
|
|
|
|
|
rate_bits,
|
|
|
|
|
false,
|
|
|
|
|
config.fri_config.cap_height,
|
|
|
|
|
timing,
|
|
|
|
|
None,
|
|
|
|
|
)
|
|
|
|
|
);
|
2022-01-31 18:00:07 +01:00
|
|
|
let quotient_polys_cap = quotient_commitment.merkle_tree.cap.clone();
|
2022-01-31 16:19:30 +01:00
|
|
|
challenger.observe_cap("ient_polys_cap);
|
2022-01-27 12:58:56 +01:00
|
|
|
|
|
|
|
|
let zeta = challenger.get_extension_challenge::<D>();
|
|
|
|
|
// To avoid leaking witness data, we want to ensure that our opening locations, `zeta` and
|
|
|
|
|
// `g * zeta`, are not in our subgroup `H`. It suffices to check `zeta` only, since
|
|
|
|
|
// `(g * zeta)^n = zeta^n`, where `n` is the order of `g`.
|
|
|
|
|
let g = F::Extension::primitive_root_of_unity(degree_bits);
|
|
|
|
|
ensure!(
|
|
|
|
|
zeta.exp_power_of_2(degree_bits) != F::Extension::ONE,
|
|
|
|
|
"Opening point is in the subgroup."
|
|
|
|
|
);
|
|
|
|
|
let openings = StarkOpeningSet::new(zeta, g, &trace_commitment, "ient_commitment);
|
2022-02-03 16:21:34 +01:00
|
|
|
challenger.observe_openings(&openings.to_fri_openings());
|
2022-01-26 00:09:29 -08:00
|
|
|
|
2022-01-27 12:58:56 +01:00
|
|
|
// TODO: Add permuation checks
|
|
|
|
|
let initial_merkle_trees = &[&trace_commitment, "ient_commitment];
|
2022-01-26 00:09:29 -08:00
|
|
|
let fri_params = config.fri_params(degree_bits);
|
|
|
|
|
|
2022-01-27 12:58:56 +01:00
|
|
|
let opening_proof = timed!(
|
2022-01-26 00:09:29 -08:00
|
|
|
timing,
|
2022-01-27 12:58:56 +01:00
|
|
|
"compute openings proof",
|
|
|
|
|
PolynomialBatch::prove_openings(
|
2022-02-04 17:04:07 +01:00
|
|
|
&stark.fri_instance(zeta, g, rate_bits, config.num_challenges),
|
2022-01-27 12:58:56 +01:00
|
|
|
initial_merkle_trees,
|
|
|
|
|
&mut challenger,
|
|
|
|
|
&fri_params,
|
|
|
|
|
timing,
|
|
|
|
|
)
|
2022-01-26 00:09:29 -08:00
|
|
|
);
|
2022-01-29 12:49:00 +01:00
|
|
|
let proof = StarkProof {
|
2022-01-26 00:09:29 -08:00
|
|
|
trace_cap,
|
2022-01-29 12:49:00 +01:00
|
|
|
quotient_polys_cap,
|
2022-01-26 00:09:29 -08:00
|
|
|
openings,
|
|
|
|
|
opening_proof,
|
2022-01-29 12:49:00 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Ok(StarkProofWithPublicInputs {
|
|
|
|
|
proof,
|
|
|
|
|
public_inputs: public_inputs.to_vec(),
|
2022-01-27 12:58:56 +01:00
|
|
|
})
|
2022-01-26 00:09:29 -08:00
|
|
|
}
|
2022-01-26 16:08:04 +01:00
|
|
|
|
2022-01-27 13:27:06 +01:00
|
|
|
/// Computes the quotient polynomials `(sum alpha^i C_i(x)) / Z_H(x)` for `alpha` in `alphas`,
|
|
|
|
|
/// where the `C_i`s are the Stark constraints.
|
2022-01-26 16:08:04 +01:00
|
|
|
fn compute_quotient_polys<F, C, S, const D: usize>(
|
|
|
|
|
stark: &S,
|
|
|
|
|
trace_commitment: &PolynomialBatch<F, C, D>,
|
2022-01-28 05:02:31 +01:00
|
|
|
public_inputs: [F; S::PUBLIC_INPUTS],
|
2022-01-28 17:06:40 +01:00
|
|
|
alphas: Vec<F>,
|
2022-01-26 16:08:04 +01:00
|
|
|
degree_bits: usize,
|
|
|
|
|
rate_bits: usize,
|
|
|
|
|
) -> Vec<PolynomialCoeffs<F>>
|
|
|
|
|
where
|
|
|
|
|
F: RichField + Extendable<D>,
|
|
|
|
|
C: GenericConfig<D, F = F>,
|
|
|
|
|
S: Stark<F, D>,
|
|
|
|
|
[(); S::COLUMNS]:,
|
|
|
|
|
[(); S::PUBLIC_INPUTS]:,
|
|
|
|
|
{
|
|
|
|
|
let degree = 1 << degree_bits;
|
|
|
|
|
|
2022-02-04 20:42:49 +01:00
|
|
|
let quotient_degree_bits = log2_ceil(stark.quotient_degree_factor());
|
2022-02-04 16:36:22 +01:00
|
|
|
assert!(
|
2022-02-04 20:42:49 +01:00
|
|
|
quotient_degree_bits <= rate_bits,
|
2022-02-04 16:36:22 +01:00
|
|
|
"Having constraints of degree higher than the rate is not supported yet."
|
|
|
|
|
);
|
2022-02-04 20:42:49 +01:00
|
|
|
let step = 1 << (rate_bits - quotient_degree_bits);
|
2022-02-04 16:36:22 +01:00
|
|
|
// When opening the `Z`s polys at the "next" point, need to look at the point `next_step` steps away.
|
2022-02-04 20:42:49 +01:00
|
|
|
let next_step = 1 << quotient_degree_bits;
|
2022-02-04 16:36:22 +01:00
|
|
|
|
2022-01-27 13:27:06 +01:00
|
|
|
// Evaluation of the first Lagrange polynomial on the LDE domain.
|
2022-02-06 23:06:04 -08:00
|
|
|
let lagrange_first = PolynomialValues::selector(degree, 0).lde_onto_coset(quotient_degree_bits);
|
2022-01-27 13:27:06 +01:00
|
|
|
// Evaluation of the last Lagrange polynomial on the LDE domain.
|
2022-02-06 23:06:04 -08:00
|
|
|
let lagrange_last =
|
|
|
|
|
PolynomialValues::selector(degree, degree - 1).lde_onto_coset(quotient_degree_bits);
|
2022-01-26 16:08:04 +01:00
|
|
|
|
2022-02-04 20:42:49 +01:00
|
|
|
let z_h_on_coset = ZeroPolyOnCoset::<F>::new(degree_bits, quotient_degree_bits);
|
2022-01-26 16:08:04 +01:00
|
|
|
|
2022-01-27 13:27:06 +01:00
|
|
|
// Retrieve the LDE values at index `i`.
|
|
|
|
|
let get_at_index = |comm: &PolynomialBatch<F, C, D>, i: usize| -> [F; S::COLUMNS] {
|
2022-02-04 16:36:22 +01:00
|
|
|
comm.get_lde_values(i * step).try_into().unwrap()
|
2022-01-27 13:27:06 +01:00
|
|
|
};
|
2022-02-01 17:34:03 +01:00
|
|
|
// Last element of the subgroup.
|
2022-01-31 18:00:07 +01:00
|
|
|
let last = F::primitive_root_of_unity(degree_bits).inverse();
|
2022-02-04 20:42:49 +01:00
|
|
|
let size = degree << quotient_degree_bits;
|
2022-01-31 18:00:07 +01:00
|
|
|
let coset = F::cyclic_subgroup_coset_known_order(
|
2022-02-04 20:42:49 +01:00
|
|
|
F::primitive_root_of_unity(degree_bits + quotient_degree_bits),
|
2022-01-31 18:00:07 +01:00
|
|
|
F::coset_shift(),
|
2022-02-04 16:36:22 +01:00
|
|
|
size,
|
2022-01-31 18:00:07 +01:00
|
|
|
);
|
2022-01-27 13:27:06 +01:00
|
|
|
|
2022-02-04 16:36:22 +01:00
|
|
|
let quotient_values = (0..size)
|
2022-01-28 17:06:40 +01:00
|
|
|
.into_par_iter()
|
|
|
|
|
.map(|i| {
|
|
|
|
|
// TODO: Set `P` to a genuine `PackedField` here.
|
|
|
|
|
let mut consumer = ConstraintConsumer::<F>::new(
|
|
|
|
|
alphas.clone(),
|
2022-02-02 11:23:03 +01:00
|
|
|
coset[i] - last,
|
2022-01-28 17:06:40 +01:00
|
|
|
lagrange_first.values[i],
|
|
|
|
|
lagrange_last.values[i],
|
2022-01-26 16:08:04 +01:00
|
|
|
);
|
2022-01-28 17:06:40 +01:00
|
|
|
let vars = StarkEvaluationVars::<F, F, { S::COLUMNS }, { S::PUBLIC_INPUTS }> {
|
|
|
|
|
local_values: &get_at_index(trace_commitment, i),
|
2022-02-04 16:36:22 +01:00
|
|
|
next_values: &get_at_index(trace_commitment, (i + next_step) % size),
|
2022-01-28 17:06:40 +01:00
|
|
|
public_inputs: &public_inputs,
|
|
|
|
|
};
|
|
|
|
|
stark.eval_packed_base(vars, &mut consumer);
|
2022-01-31 18:00:07 +01:00
|
|
|
// TODO: Fix this once we use a genuine `PackedField`.
|
2022-01-28 17:06:40 +01:00
|
|
|
let mut constraints_evals = consumer.accumulators();
|
2022-02-03 11:49:44 +01:00
|
|
|
// We divide the constraints evaluations by `Z_H(x)`.
|
2022-01-28 17:06:40 +01:00
|
|
|
let denominator_inv = z_h_on_coset.eval_inverse(i);
|
|
|
|
|
for eval in &mut constraints_evals {
|
2022-02-02 11:23:03 +01:00
|
|
|
*eval *= denominator_inv;
|
2022-01-28 17:06:40 +01:00
|
|
|
}
|
|
|
|
|
constraints_evals
|
2022-01-26 16:08:04 +01:00
|
|
|
})
|
2022-01-28 17:06:40 +01:00
|
|
|
.collect::<Vec<_>>();
|
|
|
|
|
|
|
|
|
|
transpose("ient_values)
|
|
|
|
|
.into_par_iter()
|
|
|
|
|
.map(PolynomialValues::new)
|
|
|
|
|
.map(|values| values.coset_ifft(F::coset_shift()))
|
2022-01-26 16:08:04 +01:00
|
|
|
.collect()
|
|
|
|
|
}
|