diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index 02d34fba..6f10c726 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -292,19 +292,13 @@ impl, const D: usize> CircuitBuilder { let subgroup = F::two_adic_subgroup(degree_bits); let constant_vecs = self.constant_polys(); - let constants_commitment = ListPolynomialCommitment::new( - constant_vecs.into_iter().map(|v| v.ifft()).collect(), - self.config.fri_config.rate_bits, - false, - ); + let constants_commitment = + ListPolynomialCommitment::new(constant_vecs, self.config.fri_config.rate_bits, false); let k_is = get_unique_coset_shifts(degree, self.config.num_routed_wires); let sigma_vecs = self.sigma_vecs(&k_is, &subgroup); - let sigmas_commitment = ListPolynomialCommitment::new( - sigma_vecs.into_iter().map(|v| v.ifft()).collect(), - self.config.fri_config.rate_bits, - false, - ); + let sigmas_commitment = + ListPolynomialCommitment::new(sigma_vecs, self.config.fri_config.rate_bits, false); let constants_root = constants_commitment.merkle_tree.root; let sigmas_root = sigmas_commitment.merkle_tree.root; @@ -339,6 +333,7 @@ impl, const D: usize> CircuitBuilder { config: self.config, degree_bits, gates, + max_filtered_constraint_degree_bits: 3, // TODO: compute this correctly once filters land. num_gate_constraints, k_is, circuit_digest, diff --git a/src/circuit_data.rs b/src/circuit_data.rs index 28dfbfac..3dc2d64d 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -143,6 +143,9 @@ pub(crate) struct CommonCircuitData, const D: usize> { /// The types of gates used in this circuit. pub(crate) gates: Vec>, + /// The largest number of constraints imposed by any gate. + pub(crate) max_filtered_constraint_degree_bits: usize, + /// The largest number of constraints imposed by any gate. pub(crate) num_gate_constraints: usize, @@ -176,7 +179,7 @@ impl, const D: usize> CommonCircuitData { } pub fn quotient_degree(&self) -> usize { - self.constraint_degree() - 1 + 1 << self.max_filtered_constraint_degree_bits - 1 } pub fn total_constraints(&self) -> usize { diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index c1fd08eb..b11e1ca2 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -9,37 +9,73 @@ use crate::fri::{prover::fri_proof, verifier::verify_fri_proof, FriConfig}; use crate::merkle_tree::MerkleTree; use crate::plonk_challenger::Challenger; use crate::plonk_common::{reduce_polys_with_iter, reduce_with_iter}; -use crate::polynomial::polynomial::PolynomialCoeffs; +use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; use crate::proof::{FriProof, FriProofTarget, Hash, OpeningSet}; use crate::timed; -use crate::util::{log2_strict, reverse_index_bits_in_place, transpose}; +use crate::util::{log2_strict, reverse_bits, reverse_index_bits_in_place, transpose}; pub const SALT_SIZE: usize = 2; pub struct ListPolynomialCommitment { pub polynomials: Vec>, + pub values: Vec>, pub merkle_tree: MerkleTree, pub degree: usize, + pub degree_log: usize, pub rate_bits: usize, pub blinding: bool, } impl ListPolynomialCommitment { - pub fn new(polynomials: Vec>, rate_bits: usize, blinding: bool) -> Self { - let degree = polynomials[0].len(); + /// Creates a list polynomial commitment for the polynomials interpolating the values in `values`. + pub fn new(values: Vec>, rate_bits: usize, blinding: bool) -> Self { + let degree = values[0].len(); + let polynomials = values.iter().map(|v| v.clone().ifft()).collect::>(); let lde_values = timed!( Self::lde_values(&polynomials, rate_bits, blinding), "to compute LDE" ); + Self::new_from_data(polynomials, values, lde_values, degree, rate_bits, blinding) + } + + /// Creates a list polynomial commitment for the polynomials `polynomials`. + pub fn new_from_polys( + polynomials: Vec>, + rate_bits: usize, + blinding: bool, + ) -> Self { + let degree = polynomials[0].len(); + let values = polynomials + .iter() + .map(|v| v.clone().fft()) + .collect::>(); + let lde_values = timed!( + Self::lde_values(&polynomials, rate_bits, blinding), + "to compute LDE" + ); + + Self::new_from_data(polynomials, values, lde_values, degree, rate_bits, blinding) + } + + fn new_from_data( + polynomials: Vec>, + values: Vec>, + lde_values: Vec>, + degree: usize, + rate_bits: usize, + blinding: bool, + ) -> Self { let mut leaves = timed!(transpose(&lde_values), "to transpose LDEs"); reverse_index_bits_in_place(&mut leaves); let merkle_tree = timed!(MerkleTree::new(leaves, false), "to build Merkle tree"); Self { polynomials, + values, merkle_tree, degree, + degree_log: log2_strict(degree), rate_bits, blinding, } @@ -71,9 +107,8 @@ impl ListPolynomialCommitment { .collect() } - pub fn leaf(&self, index: usize) -> &[F] { - let leaf = &self.merkle_tree.leaves[index]; - &leaf[0..leaf.len() - if self.blinding { SALT_SIZE } else { 0 }] + pub fn original_value(&self, index: usize) -> Vec { + self.values.iter().map(|v| v.values[index]).collect() } /// Takes the commitments to the constants - sigmas - wires - zs - quotient — polynomials, @@ -88,7 +123,7 @@ impl ListPolynomialCommitment { F: Extendable, { assert!(D > 1, "Not implemented for D=1."); - let degree_log = log2_strict(commitments[0].degree); + let degree_log = commitments[0].degree_log; let g = F::Extension::primitive_root_of_unity(degree_log); for p in &[zeta, g * zeta] { assert_ne!( @@ -267,11 +302,11 @@ mod tests { fn gen_random_test_case, const D: usize>( k: usize, degree_log: usize, - ) -> Vec> { + ) -> Vec> { let degree = 1 << degree_log; (0..k) - .map(|_| PolynomialCoeffs::new(F::rand_vec(degree))) + .map(|_| PolynomialValues::new(F::rand_vec(degree))) .collect() } diff --git a/src/prover.rs b/src/prover.rs index 116a6b24..08362ac3 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -17,7 +17,7 @@ use crate::timed; use crate::util::transpose; use crate::vars::EvaluationVarsBase; use crate::wire::Wire; -use crate::witness::PartialWitness; +use crate::witness::{PartialWitness, Witness}; /// Corresponds to constants - sigmas - wires - zs - quotient — polynomial commitments. pub const PLONK_BLINDING: [bool; 5] = [false, false, true, true, true]; @@ -31,10 +31,10 @@ pub(crate) fn prove, const D: usize>( let start_proof_gen = Instant::now(); - let mut witness = inputs; + let mut partial_witness = inputs; info!("Running {} generators", prover_data.generators.len()); timed!( - generate_partial_witness(&mut witness, &prover_data.generators), + generate_partial_witness(&mut partial_witness, &prover_data.generators), "to generate witness" ); @@ -42,12 +42,15 @@ pub(crate) fn prove, const D: usize>( let num_wires = config.num_wires; let num_challenges = config.num_challenges; let quotient_degree = common_data.quotient_degree(); - let degree = common_data.degree(); - let wires_polynomials: Vec> = timed!( - (0..num_wires) - .into_par_iter() - .map(|i| compute_wire_polynomial(i, &witness, degree)) + + let witness = partial_witness.full_witness(degree, num_wires); + + let wires_values: Vec> = timed!( + witness + .wire_values + .iter() + .map(|column| PolynomialValues::new(column.clone())) .collect(), "to compute wire polynomials" ); @@ -55,7 +58,7 @@ pub(crate) fn prove, const D: usize>( // TODO: Could try parallelizing the transpose, or not doing it explicitly, instead having // merkle_root_bit_rev_order do it implicitly. let wires_commitment = timed!( - ListPolynomialCommitment::new(wires_polynomials, fri_config.rate_bits, true), + ListPolynomialCommitment::new(wires_values, fri_config.rate_bits, true), "to compute wires commitment" ); @@ -68,7 +71,10 @@ pub(crate) fn prove, const D: usize>( let betas = challenger.get_n_challenges(num_challenges); let gammas = challenger.get_n_challenges(num_challenges); - let plonk_z_vecs = timed!(compute_zs(&common_data), "to compute Z's"); + let plonk_z_vecs = timed!( + compute_zs(&witness, &betas, &gammas, &prover_data, &common_data), + "to compute Z's" + ); let plonk_zs_commitment = timed!( ListPolynomialCommitment::new(plonk_z_vecs, fri_config.rate_bits, true), @@ -107,7 +113,11 @@ pub(crate) fn prove, const D: usize>( ); let quotient_polys_commitment = timed!( - ListPolynomialCommitment::new(all_quotient_poly_chunks, fri_config.rate_bits, true), + ListPolynomialCommitment::new_from_polys( + all_quotient_poly_chunks, + fri_config.rate_bits, + true + ), "to commit to quotient polys" ); @@ -146,37 +156,44 @@ pub(crate) fn prove, const D: usize>( } fn compute_zs, const D: usize>( + witness: &Witness, + betas: &[F], + gammas: &[F], + prover_data: &ProverOnlyCircuitData, common_data: &CommonCircuitData, -) -> Vec> { +) -> Vec> { (0..common_data.config.num_challenges) - .map(|i| compute_z(common_data, i)) + .map(|i| compute_z(witness, betas[i], gammas[i], prover_data, common_data)) .collect() } fn compute_z, const D: usize>( + witness: &Witness, + beta: F, + gamma: F, + prover_data: &ProverOnlyCircuitData, common_data: &CommonCircuitData, - _i: usize, -) -> PolynomialCoeffs { - todo!() - // let subgroup = - // let mut plonk_z_points = vec![F::ONE]; - // let k_is = common_data.k_is; - // for i in 1..common_data.degree() { - // let x = subgroup[i - 1]; - // let mut numerator = F::ONE; - // let mut denominator = F::ONE; - // for j in 0..NUM_ROUTED_WIRES { - // let wire_value = witness.get_indices(i - 1, j); - // let k_i = k_is[j]; - // let s_id = k_i * x; - // let s_sigma = sigma_values[j][8 * (i - 1)]; - // numerator = numerator * (wire_value + beta * s_id + gamma); - // denominator = denominator * (wire_value + beta * s_sigma + gamma); - // } - // let last = *plonk_z_points.last().unwrap(); - // plonk_z_points.push(last * numerator / denominator); - // } - // plonk_z_points +) -> PolynomialValues { + let subgroup = &prover_data.subgroup; + let mut plonk_z_points = vec![F::ONE]; + let k_is = &common_data.k_is; + for i in 1..common_data.degree() { + let x = subgroup[i - 1]; + let mut numerator = F::ONE; + let mut denominator = F::ONE; + let s_sigmas = prover_data.sigmas_commitment.original_value(i - 1); + for j in 0..common_data.config.num_routed_wires { + let wire_value = witness.get_wire(i - 1, j); + let k_i = k_is[j]; + let s_id = k_i * x; + let s_sigma = s_sigmas[j]; + numerator = numerator * (wire_value + beta * s_id + gamma); + denominator = denominator * (wire_value + beta * s_sigma + gamma); + } + let last = *plonk_z_points.last().unwrap(); + plonk_z_points.push(last * numerator / denominator); + } + plonk_z_points.into() } fn compute_vanishing_polys, const D: usize>( @@ -188,21 +205,38 @@ fn compute_vanishing_polys, const D: usize>( gammas: &[F], alphas: &[F], ) -> Vec> { - let lde_size = common_data.lde_size(); - let lde_gen = common_data.lde_generator(); let num_challenges = common_data.config.num_challenges; + let points = F::two_adic_subgroup( + common_data.degree_bits + common_data.max_filtered_constraint_degree_bits, + ); + let lde_size = points.len(); + + let commitment_to_lde = |comm: &ListPolynomialCommitment| -> Vec> { + comm.polynomials + .iter() + .map(|p| p.lde(common_data.max_filtered_constraint_degree_bits).fft()) + .collect() + }; + + let constants_lde = commitment_to_lde(&prover_data.constants_commitment); + let sigmas_lde = commitment_to_lde(&prover_data.sigmas_commitment); + let wires_lde = commitment_to_lde(wires_commitment); + let zs_lde = commitment_to_lde(plonk_zs_commitment); + + let get_at_index = |ldes: &[PolynomialValues], i: usize| { + ldes.iter().map(|l| l.values[i]).collect::>() + }; - let points = F::cyclic_subgroup_known_order(lde_gen, lde_size); let values: Vec> = points .into_par_iter() .enumerate() .map(|(i, x)| { let i_next = (i + 1) % lde_size; - let local_wires = wires_commitment.leaf(i); - let local_constants = prover_data.constants_commitment.leaf(i); - let local_plonk_zs = plonk_zs_commitment.leaf(i); - let next_plonk_zs = plonk_zs_commitment.leaf(i_next); - let s_sigmas = prover_data.sigmas_commitment.leaf(i); + let local_constants = &get_at_index(&constants_lde, i); + let s_sigmas = &get_at_index(&sigmas_lde, i); + let local_wires = &get_at_index(&wires_lde, i); + let local_plonk_zs = &get_at_index(&zs_lde, i); + let next_plonk_zs = &get_at_index(&zs_lde, i_next); debug_assert_eq!(local_wires.len(), common_data.config.num_wires); debug_assert_eq!(local_plonk_zs.len(), num_challenges); @@ -230,22 +264,3 @@ fn compute_vanishing_polys, const D: usize>( .map(PolynomialValues::new) .collect() } - -fn compute_wire_polynomial( - input: usize, - witness: &PartialWitness, - degree: usize, -) -> PolynomialCoeffs { - let wire_values = (0..degree) - // Some gates do not use all wires, and we do not require that generators populate unused - // wires, so some wire values will not be set. We can set these to any value; here we - // arbitrary pick zero. Ideally we would verify that no constraints operate on these unset - // wires, but that isn't trivial. - .map(|gate| { - witness - .try_get_wire(Wire { gate, input }) - .unwrap_or(F::ZERO) - }) - .collect(); - PolynomialValues::new(wire_values).ifft() -} diff --git a/src/witness.rs b/src/witness.rs index e71b1cfb..a870059b 100644 --- a/src/witness.rs +++ b/src/witness.rs @@ -1,11 +1,23 @@ use std::collections::HashMap; +use std::convert::TryInto; +use crate::circuit_data::{CircuitConfig, CommonCircuitData}; use crate::field::extension_field::target::ExtensionTarget; use crate::field::extension_field::{Extendable, FieldExtension}; use crate::field::field::Field; use crate::target::Target; use crate::wire::Wire; -use std::convert::TryInto; + +#[derive(Clone, Debug)] +pub struct Witness { + pub(crate) wire_values: Vec>, +} + +impl Witness { + pub fn get_wire(&self, gate: usize, input: usize) -> F { + self.wire_values[input][gate] + } +} #[derive(Clone, Debug)] pub struct PartialWitness { @@ -121,6 +133,16 @@ impl PartialWitness { self.set_target(target, value); } } + + pub fn full_witness(self, degree: usize, num_wires: usize) -> Witness { + let mut wire_values = vec![vec![F::ZERO; degree]; num_wires]; + self.target_values.into_iter().for_each(|(t, v)| { + if let Target::Wire(Wire { gate, input }) = t { + wire_values[input][gate] = v; + } + }); + Witness { wire_values } + } } impl Default for PartialWitness {