From 0bae47bedb16c052c776a7ec05647556d0e40112 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 6 May 2021 17:09:55 +0200 Subject: [PATCH 1/9] LPC batch opening --- src/fri/mod.rs | 4 +- src/fri/prover.rs | 6 +- src/fri/verifier.rs | 5 +- src/polynomial/commitment.rs | 176 +++++++++++++++++++++++++++++++++-- 4 files changed, 177 insertions(+), 14 deletions(-) diff --git a/src/fri/mod.rs b/src/fri/mod.rs index af931f28..9633face 100644 --- a/src/fri/mod.rs +++ b/src/fri/mod.rs @@ -5,7 +5,7 @@ pub mod verifier; /// while increasing L, potentially requiring more challenge points. const EPSILON: f64 = 0.01; -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Eq, PartialEq)] pub struct FriConfig { pub proof_of_work_bits: u32, @@ -92,7 +92,7 @@ mod tests { }; let root = tree.root; let mut challenger = Challenger::new(); - let proof = fri_proof(&[tree], &coeffs, &coset_lde, &mut challenger, &config); + let proof = fri_proof(&[&tree], &coeffs, &coset_lde, &mut challenger, &config); let mut challenger = Challenger::new(); verify_fri_proof( diff --git a/src/fri/prover.rs b/src/fri/prover.rs index 75e11bdb..084ccf08 100644 --- a/src/fri/prover.rs +++ b/src/fri/prover.rs @@ -10,7 +10,7 @@ use crate::util::reverse_index_bits_in_place; /// Builds a FRI proof. pub fn fri_proof( - initial_merkle_trees: &[MerkleTree], + initial_merkle_trees: &[&MerkleTree], // Coefficients of the polynomial on which the LDT is performed. Only the first `1/rate` coefficients are non-zero. lde_polynomial_coeffs: &PolynomialCoeffs, // Evaluation of the polynomial on the large domain. @@ -113,7 +113,7 @@ fn fri_proof_of_work(current_hash: Hash, config: &FriConfig) -> F { } fn fri_prover_query_rounds( - initial_merkle_trees: &[MerkleTree], + initial_merkle_trees: &[&MerkleTree], trees: &[MerkleTree], challenger: &mut Challenger, n: usize, @@ -125,7 +125,7 @@ fn fri_prover_query_rounds( } fn fri_prover_query_round( - initial_merkle_trees: &[MerkleTree], + initial_merkle_trees: &[&MerkleTree], trees: &[MerkleTree], challenger: &mut Challenger, n: usize, diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index 6fcd4b24..952ef1ce 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -4,6 +4,7 @@ use crate::fri::FriConfig; use crate::hash::hash_n_to_1; use crate::merkle_proofs::verify_merkle_proof; use crate::plonk_challenger::Challenger; +use crate::polynomial::commitment::SALT_SIZE; use crate::polynomial::polynomial::PolynomialCoeffs; use crate::proof::{FriInitialTreeProof, FriProof, FriQueryRound, Hash}; use crate::util::{log2_strict, reverse_bits, reverse_index_bits_in_place}; @@ -148,10 +149,8 @@ fn fri_combine_initial( let e = proof .evals_proofs .iter() - .map(|(v, _)| v) - .flatten() + .flat_map(|(v, _)| &v[..v.len() - if config.blinding { SALT_SIZE } else { 0 }]) .rev() - .skip(if config.blinding { 2 } else { 0 }) // If blinding, the last two element are salt. .fold(F::ZERO, |acc, &e| alpha * acc + e); let numerator = e - interpolant.eval(subgroup_x); let denominator = points.iter().map(|&(x, _)| subgroup_x - x).product(); diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index 610fbc55..953a11e9 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -10,6 +10,8 @@ use crate::proof::{FriProof, Hash}; use crate::util::{log2_strict, reverse_index_bits_in_place, transpose}; use anyhow::Result; +pub const SALT_SIZE: usize = 2; + struct ListPolynomialCommitment { pub polynomials: Vec>, pub fri_config: FriConfig, @@ -31,7 +33,7 @@ impl ListPolynomialCommitment { }) .chain(if fri_config.blinding { // If blinding, salt with two random elements to each leaf vector. - (0..2) + (0..SALT_SIZE) .map(|_| F::rand_vec(degree << fri_config.rate_bits)) .collect() } else { @@ -100,7 +102,7 @@ impl ListPolynomialCommitment { .coset_fft(F::MULTIPLICATIVE_GROUP_GENERATOR); let fri_proof = fri_proof( - &[self.merkle_tree.clone()], + &[&self.merkle_tree], &lde_quotient, &lde_quotient_values, challenger, @@ -116,6 +118,84 @@ impl ListPolynomialCommitment { ) } + pub fn batch_open( + commitments: &[&Self], + points: &[F], + challenger: &mut Challenger, + ) -> (OpeningProof, Vec>) { + let degree = commitments[0].degree; + assert!( + commitments.iter().all(|c| c.degree == degree), + "Trying to open polynomial commitments of different degrees." + ); + let fri_config = &commitments[0].fri_config; + assert!( + commitments.iter().all(|c| &c.fri_config == fri_config), + "Trying to open polynomial commitments with different config." + ); + for p in points { + assert_ne!( + p.exp_usize(degree), + F::ONE, + "Opening point is in the subgroup." + ); + } + + let evaluations = points + .iter() + .map(|&x| { + commitments + .iter() + .flat_map(move |c| c.polynomials.iter().map(|p| p.eval(x)).collect::>()) + .collect::>() + }) + .collect::>(); + for evals in &evaluations { + challenger.observe_elements(evals); + } + + let alpha = challenger.get_challenge(); + + // Scale polynomials by `alpha`. + let composition_poly = commitments + .iter() + .flat_map(|c| &c.polynomials) + .rev() + .map(|p| p.clone().into()) + .fold(Polynomial::empty(), |acc, p| acc.scalar_mul(alpha).add(&p)); + // Scale evaluations by `alpha`. + let composition_evals = evaluations + .iter() + .map(|e| reduce_with_powers(e, alpha)) + .collect::>(); + + let quotient = Self::compute_quotient(points, &composition_evals, &composition_poly); + + let lde_quotient = PolynomialCoeffs::from(quotient.clone()).lde(fri_config.rate_bits); + let lde_quotient_values = lde_quotient + .clone() + .coset_fft(F::MULTIPLICATIVE_GROUP_GENERATOR); + + let fri_proof = fri_proof( + &commitments + .iter() + .map(|c| &c.merkle_tree) + .collect::>(), + &lde_quotient, + &lde_quotient_values, + challenger, + &fri_config, + ); + + ( + OpeningProof { + fri_proof, + quotient_degree: quotient.len(), + }, + evaluations, + ) + } + /// Given `points=(x_i)`, `evals=(y_i)` and `poly=P` with `P(x_i)=y_i`, computes the polynomial /// `Q=(P-I)/Z` where `I` interpolates `(x_i, y_i)` and `Z` is the vanishing polynomial on `(x_i)`. fn compute_quotient(points: &[F], evals: &[F], poly: &Polynomial) -> Polynomial { @@ -152,7 +232,7 @@ impl OpeningProof { &self, points: &[F], evaluations: &[Vec], - merkle_root: Hash, + merkle_roots: &[Hash], challenger: &mut Challenger, fri_config: &FriConfig, ) -> Result<()> { @@ -177,7 +257,7 @@ impl OpeningProof { log2_strict(self.quotient_degree), &pairs, alpha, - &[merkle_root], + merkle_roots, &self.fri_proof, challenger, fri_config, @@ -230,7 +310,7 @@ mod tests { proof.verify( &points, &evaluations, - lpc.merkle_tree.root, + &[lpc.merkle_tree.root], &mut Challenger::new(), &fri_config, ) @@ -257,7 +337,91 @@ mod tests { proof.verify( &points, &evaluations, - lpc.merkle_tree.root, + &[lpc.merkle_tree.root], + &mut Challenger::new(), + &fri_config, + ) + } + + #[test] + fn test_batch_polynomial_commitment() -> Result<()> { + type F = CrandallField; + + let k0 = 10; + let k1 = 3; + let k2 = 7; + let degree_log = 11; + let num_points = 5; + let fri_config = FriConfig { + proof_of_work_bits: 2, + rate_bits: 2, + reduction_arity_bits: vec![2, 3, 1, 2], + num_query_rounds: 3, + blinding: false, + }; + let (polys0, _) = gen_random_test_case::(k0, degree_log, num_points); + let (polys1, _) = gen_random_test_case::(k0, degree_log, num_points); + let (polys2, points) = gen_random_test_case::(k0, degree_log, num_points); + + let lpc0 = ListPolynomialCommitment::new(polys0, &fri_config); + let lpc1 = ListPolynomialCommitment::new(polys1, &fri_config); + let lpc2 = ListPolynomialCommitment::new(polys2, &fri_config); + + let (proof, evaluations) = ListPolynomialCommitment::batch_open( + &[&lpc0, &lpc1, &lpc2], + &points, + &mut Challenger::new(), + ); + proof.verify( + &points, + &evaluations, + &[ + lpc0.merkle_tree.root, + lpc1.merkle_tree.root, + lpc2.merkle_tree.root, + ], + &mut Challenger::new(), + &fri_config, + ) + } + + #[test] + fn test_batch_polynomial_commitment_blinding() -> Result<()> { + type F = CrandallField; + + let k0 = 10; + let k1 = 3; + let k2 = 7; + let degree_log = 11; + let num_points = 5; + let fri_config = FriConfig { + proof_of_work_bits: 2, + rate_bits: 2, + reduction_arity_bits: vec![2, 3, 1, 2], + num_query_rounds: 3, + blinding: true, + }; + let (polys0, _) = gen_random_test_case::(k0, degree_log, num_points); + let (polys1, _) = gen_random_test_case::(k0, degree_log, num_points); + let (polys2, points) = gen_random_test_case::(k0, degree_log, num_points); + + let lpc0 = ListPolynomialCommitment::new(polys0, &fri_config); + let lpc1 = ListPolynomialCommitment::new(polys1, &fri_config); + let lpc2 = ListPolynomialCommitment::new(polys2, &fri_config); + + let (proof, evaluations) = ListPolynomialCommitment::batch_open( + &[&lpc0, &lpc1, &lpc2], + &points, + &mut Challenger::new(), + ); + proof.verify( + &points, + &evaluations, + &[ + lpc0.merkle_tree.root, + lpc1.merkle_tree.root, + lpc2.merkle_tree.root, + ], &mut Challenger::new(), &fri_config, ) From ea7926bd95dc0cc3e0de4e9934341bfba7f00fe0 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 6 May 2021 23:14:37 +0200 Subject: [PATCH 2/9] Update PLONK prover. --- src/polynomial/commitment.rs | 2 +- src/polynomial/polynomial.rs | 3 + src/proof.rs | 23 +++++++ src/prover.rs | 122 ++++++++++++++++++++++++----------- 4 files changed, 110 insertions(+), 40 deletions(-) diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index 953a11e9..8faed09a 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -12,7 +12,7 @@ use anyhow::Result; pub const SALT_SIZE: usize = 2; -struct ListPolynomialCommitment { +pub struct ListPolynomialCommitment { pub polynomials: Vec>, pub fri_config: FriConfig, pub merkle_tree: MerkleTree, diff --git a/src/polynomial/polynomial.rs b/src/polynomial/polynomial.rs index 113a6d2a..4db1ef0a 100644 --- a/src/polynomial/polynomial.rs +++ b/src/polynomial/polynomial.rs @@ -26,6 +26,9 @@ impl PolynomialValues { self.values.len() } + pub fn ifft(self) -> PolynomialCoeffs { + ifft(self) + } pub fn lde_multiple(polys: Vec, rate_bits: usize) -> Vec { polys.into_iter().map(|p| p.lde(rate_bits)).collect() } diff --git a/src/proof.rs b/src/proof.rs index 64bc339a..76163bd6 100644 --- a/src/proof.rs +++ b/src/proof.rs @@ -1,5 +1,6 @@ use crate::field::field::Field; use crate::merkle_proofs::{MerkleProof, MerkleProofTarget}; +use crate::polynomial::commitment::ListPolynomialCommitment; use crate::polynomial::polynomial::PolynomialCoeffs; use crate::target::Target; use std::convert::TryInto; @@ -136,6 +137,28 @@ pub struct OpeningSet { pub quotient_polys: Vec, } +impl OpeningSet { + pub fn new( + z: F, + constant_commitment: &ListPolynomialCommitment, + plonk_sigmas_commitment: &ListPolynomialCommitment, + wires_commitment: &ListPolynomialCommitment, + plonk_zs_commitment: &ListPolynomialCommitment, + quotient_polys_commitment: &ListPolynomialCommitment, + ) -> Self { + let eval_commitment = |z: F, c: &ListPolynomialCommitment| { + c.polynomials.iter().map(|p| p.eval(z)).collect::>() + }; + Self { + constants: eval_commitment(z, constant_commitment), + plonk_sigmas: eval_commitment(z, plonk_sigmas_commitment), + wires: eval_commitment(z, wires_commitment), + plonk_zs: eval_commitment(z, plonk_zs_commitment), + quotient_polys: eval_commitment(z, quotient_polys_commitment), + } + } +} + /// The purported values of each polynomial at a single point. pub struct OpeningSetTarget { pub constants: Vec, diff --git a/src/prover.rs b/src/prover.rs index 4801b79d..4e05c666 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -6,13 +6,15 @@ use rayon::prelude::*; use crate::circuit_data::{CommonCircuitData, ProverOnlyCircuitData}; use crate::field::fft::{fft, ifft}; use crate::field::field::Field; +use crate::fri::FriConfig; use crate::generator::generate_partial_witness; use crate::merkle_tree::MerkleTree; use crate::plonk_challenger::Challenger; use crate::plonk_common::{eval_l_1, evaluate_gate_constraints, reduce_with_powers_multi}; +use crate::polynomial::commitment::ListPolynomialCommitment; use crate::polynomial::division::divide_by_z_h; use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; -use crate::proof::Proof; +use crate::proof::{OpeningSet, Proof}; use crate::util::{transpose, transpose_poly_values}; use crate::vars::EvaluationVars; use crate::wire::Wire; @@ -23,6 +25,14 @@ pub(crate) fn prove( common_data: &CommonCircuitData, inputs: PartialWitness, ) -> Proof { + // TODO: Change this to real values. + let fri_config = FriConfig { + proof_of_work_bits: 1, + rate_bits: 1, + reduction_arity_bits: vec![1], + num_query_rounds: 1, + blinding: true, + }; let start_proof_gen = Instant::now(); let start_witness = Instant::now(); @@ -41,10 +51,10 @@ pub(crate) fn prove( let start_wire_ldes = Instant::now(); let degree = common_data.degree(); - let wire_ldes = (0..num_wires) + let wires_polynomials: Vec> = (0..num_wires) .into_par_iter() - .map(|i| compute_wire_lde(i, &witness, degree, config.rate_bits)) - .collect::>(); + .map(|i| compute_wire_polynomial(i, &witness, degree)) + .collect(); info!( "{:.3}s to compute wire LDEs", start_wire_ldes.elapsed().as_secs_f32() @@ -52,19 +62,11 @@ pub(crate) fn prove( // TODO: Could try parallelizing the transpose, or not doing it explicitly, instead having // merkle_root_bit_rev_order do it implicitly. - let start_wire_transpose = Instant::now(); - let wire_ldes_t = transpose_poly_values(wire_ldes); + let start_wires_commitment = Instant::now(); + let wires_commitment = ListPolynomialCommitment::new(wires_polynomials, &fri_config); info!( "{:.3}s to transpose wire LDEs", - start_wire_transpose.elapsed().as_secs_f32() - ); - - // TODO: Could avoid cloning if it's significant? - let start_wires_root = Instant::now(); - let wires_tree = MerkleTree::new(wire_ldes_t, true); - info!( - "{:.3}s to Merklize wire LDEs", - start_wires_root.elapsed().as_secs_f32() + start_wires_commitment.elapsed().as_secs_f32() ); let mut challenger = Challenger::new(); @@ -72,27 +74,25 @@ pub(crate) fn prove( // TODO: Need to include public inputs as well. challenger.observe_hash(&common_data.circuit_digest); - challenger.observe_hash(&wires_tree.root); + challenger.observe_hash(&wires_commitment.merkle_tree.root); let betas = challenger.get_n_challenges(num_checks); let gammas = challenger.get_n_challenges(num_checks); let start_plonk_z = Instant::now(); let plonk_z_vecs = compute_zs(&common_data); - let plonk_z_ldes = PolynomialValues::lde_multiple(plonk_z_vecs, config.rate_bits); - let plonk_z_ldes_t = transpose_poly_values(plonk_z_ldes); info!( - "{:.3}s to compute Z's and their LDEs", + "{:.3}s to compute Z's", start_plonk_z.elapsed().as_secs_f32() ); let start_plonk_z_root = Instant::now(); - let plonk_zs_tree = MerkleTree::new(plonk_z_ldes_t, true); + let plonk_zs_commitment = ListPolynomialCommitment::new(plonk_z_vecs, &fri_config); info!( "{:.3}s to Merklize Z's", start_plonk_z_root.elapsed().as_secs_f32() ); - challenger.observe_hash(&plonk_zs_tree.root); + challenger.observe_hash(&plonk_zs_commitment.merkle_tree.root); let alphas = challenger.get_n_challenges(num_checks); @@ -100,8 +100,8 @@ pub(crate) fn prove( let vanishing_polys = compute_vanishing_polys( common_data, prover_data, - &wires_tree, - &plonk_zs_tree, + &wires_commitment.merkle_tree, + &plonk_zs_commitment.merkle_tree, &betas, &gammas, &alphas, @@ -113,28 +113,53 @@ pub(crate) fn prove( // Compute the quotient polynomials, aka `t` in the Plonk paper. let quotient_polys_start = Instant::now(); - let mut all_quotient_poly_chunk_ldes = Vec::with_capacity(num_checks * quotient_degree); + let mut all_quotient_poly_chunks = Vec::with_capacity(num_checks * quotient_degree); for vanishing_poly in vanishing_polys.into_iter() { let vanishing_poly_coeff = ifft(vanishing_poly); let quotient_poly_coeff = divide_by_z_h(vanishing_poly_coeff, degree); // Split t into degree-n chunks. let quotient_poly_coeff_chunks = quotient_poly_coeff.chunks(degree); - let quotient_poly_coeff_ldes = - PolynomialCoeffs::lde_multiple(quotient_poly_coeff_chunks, config.rate_bits); - let quotient_poly_chunk_ldes: Vec> = - quotient_poly_coeff_ldes.into_par_iter().map(fft).collect(); - all_quotient_poly_chunk_ldes.extend(quotient_poly_chunk_ldes); + all_quotient_poly_chunks.extend(quotient_poly_coeff_chunks); } - let quotient_polys_tree = - MerkleTree::new(transpose_poly_values(all_quotient_poly_chunk_ldes), true); + let quotient_polys_commitment = + ListPolynomialCommitment::new(all_quotient_poly_chunks, &fri_config); info!( "{:.3}s to compute quotient polys and their LDEs", quotient_polys_start.elapsed().as_secs_f32() ); - let openings = Vec::new(); // TODO + challenger.observe_hash(&plonk_zs_commitment.merkle_tree.root); - let fri_proofs = Vec::new(); // TODO + // TODO: How many do we need? + let num_zetas = 2; + let zetas = challenger.get_n_challenges(num_zetas); + + let openings = zetas + .iter() + .map(|&z| { + OpeningSet::new( + z, + todo!(), + todo!(), + &wires_commitment, + &plonk_zs_commitment, + "ient_polys_commitment, + ) + }) + .collect::>(); + + // TODO: This re-evaluates the polynomial and is thus redundant with the openings above. + let fri_proofs = ListPolynomialCommitment::batch_open( + &[ + &todo!(), + &todo!(), + &wires_commitment, + &plonk_zs_commitment, + "ient_polys_commitment, + ], + &zetas, + &mut challenger, + ); info!( "{:.3}s for overall witness & proof generation", @@ -142,22 +167,22 @@ pub(crate) fn prove( ); Proof { - wires_root: wires_tree.root, - plonk_zs_root: plonk_zs_tree.root, - quotient_polys_root: quotient_polys_tree.root, + wires_root: wires_commitment.merkle_tree.root, + plonk_zs_root: plonk_zs_commitment.merkle_tree.root, + quotient_polys_root: quotient_polys_commitment.merkle_tree.root, openings, fri_proofs, } } -fn compute_zs(common_data: &CommonCircuitData) -> Vec> { +fn compute_zs(common_data: &CommonCircuitData) -> Vec> { (0..common_data.config.num_checks) .map(|i| compute_z(common_data, i)) .collect() } -fn compute_z(common_data: &CommonCircuitData, i: usize) -> PolynomialValues { - PolynomialValues::zero(common_data.degree()) // TODO +fn compute_z(common_data: &CommonCircuitData, i: usize) -> PolynomialCoeffs { + PolynomialCoeffs::zero(common_data.degree()) // TODO } // TODO: Parallelize. @@ -263,6 +288,25 @@ fn compute_vanishing_poly_entry( reduce_with_powers_multi(&vanishing_terms, alphas) } +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() +} + fn compute_wire_lde( input: usize, witness: &PartialWitness, From 1bae3a02f68127af5e404a836dd94982e1741ea8 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 7 May 2021 11:30:03 +0200 Subject: [PATCH 3/9] Batch open for PLONK --- src/bin/bench_recursion.rs | 8 +++ src/circuit_builder.rs | 27 ++++---- src/circuit_data.rs | 22 ++++-- src/polynomial/commitment.rs | 35 ++++++++-- src/prover.rs | 128 ++++++++++++++++------------------- src/target.rs | 2 +- src/wire.rs | 2 +- 7 files changed, 129 insertions(+), 95 deletions(-) diff --git a/src/bin/bench_recursion.rs b/src/bin/bench_recursion.rs index 566fb056..bb38c8aa 100644 --- a/src/bin/bench_recursion.rs +++ b/src/bin/bench_recursion.rs @@ -4,6 +4,7 @@ use plonky2::circuit_builder::CircuitBuilder; use plonky2::circuit_data::CircuitConfig; use plonky2::field::crandall_field::CrandallField; use plonky2::field::field::Field; +use plonky2::fri::FriConfig; use plonky2::gates::constant::ConstantGate; use plonky2::gates::gmimc::GMiMCGate; use plonky2::hash::GMIMC_ROUNDS; @@ -34,6 +35,13 @@ fn bench_prove() { security_bits: 128, rate_bits: 3, num_checks: 3, + fri_config: FriConfig { + proof_of_work_bits: 1, + rate_bits: 1, + reduction_arity_bits: vec![1], + num_query_rounds: 1, + blinding: true, + }, }; let mut builder = CircuitBuilder::::new(config); diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index 2be48e25..45e2b06a 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -16,6 +16,7 @@ use crate::generator::{CopyGenerator, WitnessGenerator}; use crate::hash::hash_n_to_hash; use crate::merkle_tree::MerkleTree; use crate::permutation_argument::TargetPartitions; +use crate::polynomial::commitment::ListPolynomialCommitment; use crate::polynomial::polynomial::PolynomialValues; use crate::target::Target; use crate::util::{log2_strict, transpose, transpose_poly_values}; @@ -138,11 +139,11 @@ impl CircuitBuilder { /// Both elements must be routable, otherwise this method will panic. pub fn assert_equal(&mut self, x: Target, y: Target) { assert!( - x.is_routable(self.config), + x.is_routable(&self.config), "Tried to route a wire that isn't routable" ); assert!( - y.is_routable(self.config), + y.is_routable(&self.config), "Tried to route a wire that isn't routable" ); self.copy_constraints.push((x, y)); @@ -271,18 +272,20 @@ impl CircuitBuilder { info!("degree after blinding & padding: {}", degree); let constant_vecs = self.constant_polys(); - let constant_ldes = PolynomialValues::lde_multiple(constant_vecs, self.config.rate_bits); - let constant_ldes_t = transpose_poly_values(constant_ldes); - let constants_tree = MerkleTree::new(constant_ldes_t, true); + let constants_commitment = ListPolynomialCommitment::new( + constant_vecs.into_iter().map(|v| v.ifft()).collect(), + &self.config.fri_config, + ); let k_is = get_unique_coset_shifts(degree, self.config.num_routed_wires); let sigma_vecs = self.sigma_vecs(&k_is); - let sigma_ldes = PolynomialValues::lde_multiple(sigma_vecs, self.config.rate_bits); - let sigma_ldes_t = transpose_poly_values(sigma_ldes); - let sigmas_tree = MerkleTree::new(sigma_ldes_t, true); + let sigmas_commitment = ListPolynomialCommitment::new( + sigma_vecs.into_iter().map(|v| v.ifft()).collect(), + &self.config.fri_config, + ); - let constants_root = constants_tree.root; - let sigmas_root = sigmas_tree.root; + let constants_root = constants_commitment.merkle_tree.root; + let sigmas_root = sigmas_commitment.merkle_tree.root; let verifier_only = VerifierOnlyCircuitData { constants_root, sigmas_root, @@ -291,8 +294,8 @@ impl CircuitBuilder { let generators = self.generators; let prover_only = ProverOnlyCircuitData { generators, - constants_tree, - sigmas_tree, + constants_commitment, + sigmas_commitment, }; // The HashSet of gates will have a non-deterministic order. When converting to a Vec, we diff --git a/src/circuit_data.rs b/src/circuit_data.rs index f75e7275..1c51e47d 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -1,13 +1,15 @@ use crate::field::field::Field; +use crate::fri::FriConfig; use crate::gates::gate::GateRef; use crate::generator::WitnessGenerator; use crate::merkle_tree::MerkleTree; +use crate::polynomial::commitment::ListPolynomialCommitment; use crate::proof::{Hash, HashTarget, Proof}; use crate::prover::prove; use crate::verifier::verify; use crate::witness::PartialWitness; -#[derive(Copy, Clone)] +#[derive(Clone)] pub struct CircuitConfig { pub num_wires: usize, pub num_routed_wires: usize, @@ -15,6 +17,9 @@ pub struct CircuitConfig { pub rate_bits: usize, /// The number of times to repeat checks that have soundness errors of (roughly) `degree / |F|`. pub num_checks: usize, + + // TODO: Find a better place for this. + pub fri_config: FriConfig, } impl Default for CircuitConfig { @@ -25,6 +30,13 @@ impl Default for CircuitConfig { security_bits: 128, rate_bits: 3, num_checks: 3, + fri_config: FriConfig { + proof_of_work_bits: 1, + rate_bits: 1, + reduction_arity_bits: vec![1], + num_query_rounds: 1, + blinding: true, + }, } } } @@ -85,10 +97,10 @@ impl VerifierCircuitData { /// Circuit data required by the prover, but not the verifier. pub(crate) struct ProverOnlyCircuitData { pub generators: Vec>>, - /// Merkle tree containing LDEs of each constant polynomial. - pub constants_tree: MerkleTree, - /// Merkle tree containing LDEs of each sigma polynomial. - pub sigmas_tree: MerkleTree, + /// Commitments to the constants polynomial. + pub constants_commitment: ListPolynomialCommitment, + /// Commitments to the sigma polynomial. + pub sigmas_commitment: ListPolynomialCommitment, } /// Circuit data required by the verifier, but not the prover. diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index 8faed09a..6454874e 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -6,7 +6,7 @@ use crate::plonk_challenger::Challenger; use crate::plonk_common::reduce_with_powers; use crate::polynomial::old_polynomial::Polynomial; use crate::polynomial::polynomial::PolynomialCoeffs; -use crate::proof::{FriProof, Hash}; +use crate::proof::{FriProof, Hash, OpeningSet}; use crate::util::{log2_strict, reverse_index_bits_in_place, transpose}; use anyhow::Result; @@ -122,7 +122,7 @@ impl ListPolynomialCommitment { commitments: &[&Self], points: &[F], challenger: &mut Challenger, - ) -> (OpeningProof, Vec>) { + ) -> (OpeningProof, Vec>>) { let degree = commitments[0].degree; assert!( commitments.iter().all(|c| c.degree == degree), @@ -146,12 +146,14 @@ impl ListPolynomialCommitment { .map(|&x| { commitments .iter() - .flat_map(move |c| c.polynomials.iter().map(|p| p.eval(x)).collect::>()) + .map(move |c| c.polynomials.iter().map(|p| p.eval(x)).collect::>()) .collect::>() }) .collect::>(); - for evals in &evaluations { - challenger.observe_elements(evals); + for evals_per_point in &evaluations { + for evals in evals_per_point { + challenger.observe_elements(evals); + } } let alpha = challenger.get_challenge(); @@ -166,6 +168,7 @@ impl ListPolynomialCommitment { // Scale evaluations by `alpha`. let composition_evals = evaluations .iter() + .flatten() .map(|e| reduce_with_powers(e, alpha)) .collect::>(); @@ -196,6 +199,28 @@ impl ListPolynomialCommitment { ) } + pub fn batch_open_plonk( + commitments: &[&Self; 5], + points: &[F], + challenger: &mut Challenger, + ) -> (OpeningProof, Vec>) { + let (op, mut evaluations) = Self::batch_open(commitments, points, challenger); + let opening_sets = evaluations + .iter_mut() + .map(|evals| { + evals.reverse(); + OpeningSet { + constants: evals.pop().unwrap(), + plonk_sigmas: evals.pop().unwrap(), + wires: evals.pop().unwrap(), + plonk_zs: evals.pop().unwrap(), + quotient_polys: evals.pop().unwrap(), + } + }) + .collect(); + (op, opening_sets) + } + /// Given `points=(x_i)`, `evals=(y_i)` and `poly=P` with `P(x_i)=y_i`, computes the polynomial /// `Q=(P-I)/Z` where `I` interpolates `(x_i, y_i)` and `Z` is the vanishing polynomial on `(x_i)`. fn compute_quotient(points: &[F], evals: &[F], poly: &Polynomial) -> Polynomial { diff --git a/src/prover.rs b/src/prover.rs index 4e05c666..6165944e 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -20,53 +20,49 @@ use crate::vars::EvaluationVars; use crate::wire::Wire; use crate::witness::PartialWitness; +macro_rules! timed { + ($a:expr, $msg:expr) => {{ + let timer = Instant::now(); + let res = $a; + info!("{:.3}s {}", timer.elapsed().as_secs_f32(), $msg); + res + }}; +} pub(crate) fn prove( prover_data: &ProverOnlyCircuitData, common_data: &CommonCircuitData, inputs: PartialWitness, ) -> Proof { - // TODO: Change this to real values. - let fri_config = FriConfig { - proof_of_work_bits: 1, - rate_bits: 1, - reduction_arity_bits: vec![1], - num_query_rounds: 1, - blinding: true, - }; + let fri_config = &common_data.config.fri_config; + let start_proof_gen = Instant::now(); - let start_witness = Instant::now(); let mut witness = inputs; info!("Running {} generators", prover_data.generators.len()); - generate_partial_witness(&mut witness, &prover_data.generators); - info!( - "{:.3}s to generate witness", - start_witness.elapsed().as_secs_f32() + timed!( + generate_partial_witness(&mut witness, &prover_data.generators), + "to generate witness" ); - let config = common_data.config; + let config = &common_data.config; let num_wires = config.num_wires; let num_checks = config.num_checks; let quotient_degree = common_data.quotient_degree(); - let start_wire_ldes = Instant::now(); let degree = common_data.degree(); - let wires_polynomials: Vec> = (0..num_wires) - .into_par_iter() - .map(|i| compute_wire_polynomial(i, &witness, degree)) - .collect(); - info!( - "{:.3}s to compute wire LDEs", - start_wire_ldes.elapsed().as_secs_f32() + let wires_polynomials: Vec> = timed!( + (0..num_wires) + .into_par_iter() + .map(|i| compute_wire_polynomial(i, &witness, degree)) + .collect(), + "to compute wire polynomials" ); // TODO: Could try parallelizing the transpose, or not doing it explicitly, instead having // merkle_root_bit_rev_order do it implicitly. - let start_wires_commitment = Instant::now(); - let wires_commitment = ListPolynomialCommitment::new(wires_polynomials, &fri_config); - info!( - "{:.3}s to transpose wire LDEs", - start_wires_commitment.elapsed().as_secs_f32() + let wires_commitment = timed!( + ListPolynomialCommitment::new(wires_polynomials, &fri_config), + "to compute wires commitment" ); let mut challenger = Challenger::new(); @@ -78,54 +74,44 @@ pub(crate) fn prove( let betas = challenger.get_n_challenges(num_checks); let gammas = challenger.get_n_challenges(num_checks); - let start_plonk_z = Instant::now(); - let plonk_z_vecs = compute_zs(&common_data); - info!( - "{:.3}s to compute Z's", - start_plonk_z.elapsed().as_secs_f32() - ); + let plonk_z_vecs = timed!(compute_zs(&common_data), "to compute Z's"); - let start_plonk_z_root = Instant::now(); - let plonk_zs_commitment = ListPolynomialCommitment::new(plonk_z_vecs, &fri_config); - info!( - "{:.3}s to Merklize Z's", - start_plonk_z_root.elapsed().as_secs_f32() + let plonk_zs_commitment = timed!( + ListPolynomialCommitment::new(plonk_z_vecs, &fri_config), + "to commit to Z's" ); challenger.observe_hash(&plonk_zs_commitment.merkle_tree.root); let alphas = challenger.get_n_challenges(num_checks); - let start_vanishing_polys = Instant::now(); - let vanishing_polys = compute_vanishing_polys( - common_data, - prover_data, - &wires_commitment.merkle_tree, - &plonk_zs_commitment.merkle_tree, - &betas, - &gammas, - &alphas, - ); - info!( - "{:.3}s to compute vanishing polys", - start_vanishing_polys.elapsed().as_secs_f32() + let vanishing_polys = timed!( + compute_vanishing_polys( + common_data, + prover_data, + &wires_commitment.merkle_tree, + &plonk_zs_commitment.merkle_tree, + &betas, + &gammas, + &alphas, + ), + "to compute vanishing polys" ); // Compute the quotient polynomials, aka `t` in the Plonk paper. - let quotient_polys_start = Instant::now(); - let mut all_quotient_poly_chunks = Vec::with_capacity(num_checks * quotient_degree); - for vanishing_poly in vanishing_polys.into_iter() { - let vanishing_poly_coeff = ifft(vanishing_poly); - let quotient_poly_coeff = divide_by_z_h(vanishing_poly_coeff, degree); - // Split t into degree-n chunks. - let quotient_poly_coeff_chunks = quotient_poly_coeff.chunks(degree); - all_quotient_poly_chunks.extend(quotient_poly_coeff_chunks); - } - let quotient_polys_commitment = - ListPolynomialCommitment::new(all_quotient_poly_chunks, &fri_config); - info!( - "{:.3}s to compute quotient polys and their LDEs", - quotient_polys_start.elapsed().as_secs_f32() + let quotient_polys_commitment = timed!( + { + let mut all_quotient_poly_chunks = Vec::with_capacity(num_checks * quotient_degree); + for vanishing_poly in vanishing_polys.into_iter() { + let vanishing_poly_coeff = ifft(vanishing_poly); + let quotient_poly_coeff = divide_by_z_h(vanishing_poly_coeff, degree); + // Split t into degree-n chunks. + let quotient_poly_coeff_chunks = quotient_poly_coeff.chunks(degree); + all_quotient_poly_chunks.extend(quotient_poly_coeff_chunks); + } + ListPolynomialCommitment::new(all_quotient_poly_chunks, &fri_config) + }, + "to compute quotient polys and commit to them" ); challenger.observe_hash(&plonk_zs_commitment.merkle_tree.root); @@ -149,10 +135,10 @@ pub(crate) fn prove( .collect::>(); // TODO: This re-evaluates the polynomial and is thus redundant with the openings above. - let fri_proofs = ListPolynomialCommitment::batch_open( + let fri_proofs = ListPolynomialCommitment::batch_open_plonk( &[ - &todo!(), - &todo!(), + &prover_data.constants_commitment, + &prover_data.sigmas_commitment, &wires_commitment, &plonk_zs_commitment, "ient_polys_commitment, @@ -171,7 +157,7 @@ pub(crate) fn prove( plonk_zs_root: plonk_zs_commitment.merkle_tree.root, quotient_polys_root: quotient_polys_commitment.merkle_tree.root, openings, - fri_proofs, + fri_proofs: todo!(), } } @@ -206,10 +192,10 @@ fn compute_vanishing_polys( .map(|(i, x)| { let i_next = (i + 1) % lde_size; let local_wires = &wires_tree.leaves[i]; - let local_constants = &prover_data.constants_tree.leaves[i]; + let local_constants = &prover_data.constants_commitment.merkle_tree.leaves[i]; let local_plonk_zs = &plonk_zs_tree.leaves[i]; let next_plonk_zs = &plonk_zs_tree.leaves[i_next]; - let s_sigmas = &prover_data.sigmas_tree.leaves[i]; + let s_sigmas = &prover_data.sigmas_commitment.merkle_tree.leaves[i]; debug_assert_eq!(local_wires.len(), common_data.config.num_wires); debug_assert_eq!(local_plonk_zs.len(), num_checks); diff --git a/src/target.rs b/src/target.rs index dfe428c2..b5736564 100644 --- a/src/target.rs +++ b/src/target.rs @@ -14,7 +14,7 @@ impl Target { Self::Wire(Wire { gate, input }) } - pub fn is_routable(&self, config: CircuitConfig) -> bool { + pub fn is_routable(&self, config: &CircuitConfig) -> bool { match self { Target::Wire(wire) => wire.is_routable(config), Target::PublicInput { .. } => true, diff --git a/src/wire.rs b/src/wire.rs index 86bc5779..61b7f5be 100644 --- a/src/wire.rs +++ b/src/wire.rs @@ -10,7 +10,7 @@ pub struct Wire { } impl Wire { - pub fn is_routable(&self, config: CircuitConfig) -> bool { + pub fn is_routable(&self, config: &CircuitConfig) -> bool { self.input < config.num_routed_wires } } From 6f13263d56ff1718b81b8344d9c7da7b783f4b24 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 7 May 2021 11:32:15 +0200 Subject: [PATCH 4/9] Minor --- src/prover.rs | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/src/prover.rs b/src/prover.rs index 6165944e..7536461f 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -120,22 +120,7 @@ pub(crate) fn prove( let num_zetas = 2; let zetas = challenger.get_n_challenges(num_zetas); - let openings = zetas - .iter() - .map(|&z| { - OpeningSet::new( - z, - todo!(), - todo!(), - &wires_commitment, - &plonk_zs_commitment, - "ient_polys_commitment, - ) - }) - .collect::>(); - - // TODO: This re-evaluates the polynomial and is thus redundant with the openings above. - let fri_proofs = ListPolynomialCommitment::batch_open_plonk( + let (fri_proofs, openings) = ListPolynomialCommitment::batch_open_plonk( &[ &prover_data.constants_commitment, &prover_data.sigmas_commitment, From 6ca1b28f7cc0f9ce030da0371da71b41266a675d Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 7 May 2021 16:22:13 +0200 Subject: [PATCH 5/9] Fix bug --- src/bin/bench_recursion.rs | 2 +- src/polynomial/commitment.rs | 39 +++++++++++++++++++++++++++--------- src/proof.rs | 4 ++-- src/prover.rs | 25 ++++++++++++----------- 4 files changed, 46 insertions(+), 24 deletions(-) diff --git a/src/bin/bench_recursion.rs b/src/bin/bench_recursion.rs index bb38c8aa..19068c77 100644 --- a/src/bin/bench_recursion.rs +++ b/src/bin/bench_recursion.rs @@ -37,7 +37,7 @@ fn bench_prove() { num_checks: 3, fri_config: FriConfig { proof_of_work_bits: 1, - rate_bits: 1, + rate_bits: 3, reduction_arity_bits: vec![1], num_query_rounds: 1, blinding: true, diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index 6454874e..f6371e30 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -53,6 +53,11 @@ impl ListPolynomialCommitment { } } + pub fn leaf(&self, index: usize) -> &[F] { + let leaf = &self.merkle_tree.leaves[index]; + &leaf[0..leaf.len() - if self.fri_config.blinding { 2 } else { 0 }] + } + pub fn open( &self, points: &[F], @@ -166,10 +171,14 @@ impl ListPolynomialCommitment { .map(|p| p.clone().into()) .fold(Polynomial::empty(), |acc, p| acc.scalar_mul(alpha).add(&p)); // Scale evaluations by `alpha`. - let composition_evals = evaluations + let composition_evals = &evaluations .iter() - .flatten() - .map(|e| reduce_with_powers(e, alpha)) + .map(|v| { + v.iter() + .flatten() + .rev() + .fold(F::ZERO, |acc, &e| acc * alpha + e) + }) .collect::>(); let quotient = Self::compute_quotient(points, &composition_evals, &composition_poly); @@ -256,21 +265,33 @@ impl OpeningProof { pub fn verify( &self, points: &[F], - evaluations: &[Vec], + evaluations: &[Vec>], merkle_roots: &[Hash], challenger: &mut Challenger, fri_config: &FriConfig, ) -> Result<()> { - for evals in evaluations { - challenger.observe_elements(evals); + for evals_per_point in evaluations { + for evals in evals_per_point { + challenger.observe_elements(evals); + } } let alpha = challenger.get_challenge(); let scaled_evals = evaluations .iter() - .map(|e| reduce_with_powers(e, alpha)) + .map(|v| { + v.iter() + .flatten() + .rev() + .fold(F::ZERO, |acc, &e| acc * alpha + e) + }) .collect::>(); + // let scaled_evals = evaluations + // .iter() + // .flatten() + // .map(|e| reduce_with_powers(e, alpha)) + // .collect::>(); let pairs = points .iter() @@ -334,7 +355,7 @@ mod tests { let (proof, evaluations) = lpc.open(&points, &mut Challenger::new()); proof.verify( &points, - &evaluations, + &evaluations.into_iter().map(|e| vec![e]).collect::>(), &[lpc.merkle_tree.root], &mut Challenger::new(), &fri_config, @@ -361,7 +382,7 @@ mod tests { let (proof, evaluations) = lpc.open(&points, &mut Challenger::new()); proof.verify( &points, - &evaluations, + &evaluations.into_iter().map(|e| vec![e]).collect::>(), &[lpc.merkle_tree.root], &mut Challenger::new(), &fri_config, diff --git a/src/proof.rs b/src/proof.rs index 76163bd6..ff28abc7 100644 --- a/src/proof.rs +++ b/src/proof.rs @@ -1,6 +1,6 @@ use crate::field::field::Field; use crate::merkle_proofs::{MerkleProof, MerkleProofTarget}; -use crate::polynomial::commitment::ListPolynomialCommitment; +use crate::polynomial::commitment::{ListPolynomialCommitment, OpeningProof}; use crate::polynomial::polynomial::PolynomialCoeffs; use crate::target::Target; use std::convert::TryInto; @@ -66,7 +66,7 @@ pub struct Proof { pub openings: Vec>, /// A FRI argument for each FRI query. - pub fri_proofs: Vec>, + pub opening_proof: OpeningProof, } pub struct ProofTarget { diff --git a/src/prover.rs b/src/prover.rs index 7536461f..ab7b89cb 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -89,8 +89,8 @@ pub(crate) fn prove( compute_vanishing_polys( common_data, prover_data, - &wires_commitment.merkle_tree, - &plonk_zs_commitment.merkle_tree, + &wires_commitment, + &plonk_zs_commitment, &betas, &gammas, &alphas, @@ -120,7 +120,7 @@ pub(crate) fn prove( let num_zetas = 2; let zetas = challenger.get_n_challenges(num_zetas); - let (fri_proofs, openings) = ListPolynomialCommitment::batch_open_plonk( + let (opening_proof, openings) = ListPolynomialCommitment::batch_open_plonk( &[ &prover_data.constants_commitment, &prover_data.sigmas_commitment, @@ -142,7 +142,7 @@ pub(crate) fn prove( plonk_zs_root: plonk_zs_commitment.merkle_tree.root, quotient_polys_root: quotient_polys_commitment.merkle_tree.root, openings, - fri_proofs: todo!(), + opening_proof, } } @@ -160,8 +160,8 @@ fn compute_z(common_data: &CommonCircuitData, i: usize) -> Polynomi fn compute_vanishing_polys( common_data: &CommonCircuitData, prover_data: &ProverOnlyCircuitData, - wires_tree: &MerkleTree, - plonk_zs_tree: &MerkleTree, + wires_commitment: &ListPolynomialCommitment, + plonk_zs_commitment: &ListPolynomialCommitment, betas: &[F], gammas: &[F], alphas: &[F], @@ -172,15 +172,16 @@ fn compute_vanishing_polys( let points = F::cyclic_subgroup_known_order(lde_gen, lde_size); let values: Vec> = points - .into_par_iter() + // .into_par_iter() + .into_iter() .enumerate() .map(|(i, x)| { let i_next = (i + 1) % lde_size; - let local_wires = &wires_tree.leaves[i]; - let local_constants = &prover_data.constants_commitment.merkle_tree.leaves[i]; - let local_plonk_zs = &plonk_zs_tree.leaves[i]; - let next_plonk_zs = &plonk_zs_tree.leaves[i_next]; - let s_sigmas = &prover_data.sigmas_commitment.merkle_tree.leaves[i]; + 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); + let s_sigmas = prover_data.sigmas_commitment.leaf(i); debug_assert_eq!(local_wires.len(), common_data.config.num_wires); debug_assert_eq!(local_plonk_zs.len(), num_checks); From d529afcdef72269641d44e9308010ca03ce0a207 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Fri, 7 May 2021 16:49:27 +0200 Subject: [PATCH 6/9] Minor --- src/bin/bench_recursion.rs | 2 +- src/polynomial/commitment.rs | 22 +++++++++------------- src/prover.rs | 26 ++++++++++++++------------ 3 files changed, 24 insertions(+), 26 deletions(-) diff --git a/src/bin/bench_recursion.rs b/src/bin/bench_recursion.rs index 19068c77..b361adbb 100644 --- a/src/bin/bench_recursion.rs +++ b/src/bin/bench_recursion.rs @@ -46,7 +46,7 @@ fn bench_prove() { let mut builder = CircuitBuilder::::new(config); - for _ in 0..5000 { + for _ in 0..10000 { builder.add_gate_no_constants(gmimc_gate.clone()); } diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index f6371e30..21fdf971 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -9,6 +9,7 @@ use crate::polynomial::polynomial::PolynomialCoeffs; use crate::proof::{FriProof, Hash, OpeningSet}; use crate::util::{log2_strict, reverse_index_bits_in_place, transpose}; use anyhow::Result; +use rayon::prelude::*; pub const SALT_SIZE: usize = 2; @@ -23,7 +24,7 @@ impl ListPolynomialCommitment { pub fn new(polynomials: Vec>, fri_config: &FriConfig) -> Self { let degree = polynomials[0].len(); let lde_values = polynomials - .iter() + .par_iter() .map(|p| { assert_eq!(p.len(), degree, "Polynomial degree invalid."); p.clone() @@ -72,7 +73,7 @@ impl ListPolynomialCommitment { } let evaluations = points - .iter() + .par_iter() .map(|&x| { self.polynomials .iter() @@ -95,7 +96,7 @@ impl ListPolynomialCommitment { .fold(Polynomial::empty(), |acc, p| acc.scalar_mul(alpha).add(&p)); // Scale evaluations by `alpha`. let composition_evals = evaluations - .iter() + .par_iter() .map(|e| reduce_with_powers(e, alpha)) .collect::>(); @@ -147,7 +148,7 @@ impl ListPolynomialCommitment { } let evaluations = points - .iter() + .par_iter() .map(|&x| { commitments .iter() @@ -172,7 +173,7 @@ impl ListPolynomialCommitment { .fold(Polynomial::empty(), |acc, p| acc.scalar_mul(alpha).add(&p)); // Scale evaluations by `alpha`. let composition_evals = &evaluations - .iter() + .par_iter() .map(|v| { v.iter() .flatten() @@ -190,7 +191,7 @@ impl ListPolynomialCommitment { let fri_proof = fri_proof( &commitments - .iter() + .par_iter() .map(|c| &c.merkle_tree) .collect::>(), &lde_quotient, @@ -215,7 +216,7 @@ impl ListPolynomialCommitment { ) -> (OpeningProof, Vec>) { let (op, mut evaluations) = Self::batch_open(commitments, points, challenger); let opening_sets = evaluations - .iter_mut() + .par_iter_mut() .map(|evals| { evals.reverse(); OpeningSet { @@ -279,7 +280,7 @@ impl OpeningProof { let alpha = challenger.get_challenge(); let scaled_evals = evaluations - .iter() + .par_iter() .map(|v| { v.iter() .flatten() @@ -287,11 +288,6 @@ impl OpeningProof { .fold(F::ZERO, |acc, &e| acc * alpha + e) }) .collect::>(); - // let scaled_evals = evaluations - // .iter() - // .flatten() - // .map(|e| reduce_with_powers(e, alpha)) - // .collect::>(); let pairs = points .iter() diff --git a/src/prover.rs b/src/prover.rs index ab7b89cb..45699b28 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -120,16 +120,19 @@ pub(crate) fn prove( let num_zetas = 2; let zetas = challenger.get_n_challenges(num_zetas); - let (opening_proof, openings) = ListPolynomialCommitment::batch_open_plonk( - &[ - &prover_data.constants_commitment, - &prover_data.sigmas_commitment, - &wires_commitment, - &plonk_zs_commitment, - "ient_polys_commitment, - ], - &zetas, - &mut challenger, + let (opening_proof, openings) = timed!( + ListPolynomialCommitment::batch_open_plonk( + &[ + &prover_data.constants_commitment, + &prover_data.sigmas_commitment, + &wires_commitment, + &plonk_zs_commitment, + "ient_polys_commitment, + ], + &zetas, + &mut challenger, + ), + "to compute opening proofs" ); info!( @@ -172,8 +175,7 @@ fn compute_vanishing_polys( let points = F::cyclic_subgroup_known_order(lde_gen, lde_size); let values: Vec> = points - // .into_par_iter() - .into_iter() + .into_par_iter() .enumerate() .map(|(i, x)| { let i_next = (i + 1) % lde_size; From ed805453c32ed0ba90ac95302a4f6d613a90b534 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Mon, 10 May 2021 13:10:40 +0200 Subject: [PATCH 7/9] Minor --- src/prover.rs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/prover.rs b/src/prover.rs index 45699b28..72d386d7 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -4,18 +4,16 @@ use log::info; use rayon::prelude::*; use crate::circuit_data::{CommonCircuitData, ProverOnlyCircuitData}; -use crate::field::fft::{fft, ifft}; +use crate::field::fft::ifft; use crate::field::field::Field; -use crate::fri::FriConfig; use crate::generator::generate_partial_witness; -use crate::merkle_tree::MerkleTree; use crate::plonk_challenger::Challenger; use crate::plonk_common::{eval_l_1, evaluate_gate_constraints, reduce_with_powers_multi}; use crate::polynomial::commitment::ListPolynomialCommitment; use crate::polynomial::division::divide_by_z_h; use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; -use crate::proof::{OpeningSet, Proof}; -use crate::util::{transpose, transpose_poly_values}; +use crate::proof::Proof; +use crate::util::transpose; use crate::vars::EvaluationVars; use crate::wire::Wire; use crate::witness::PartialWitness; @@ -155,7 +153,7 @@ fn compute_zs(common_data: &CommonCircuitData) -> Vec(common_data: &CommonCircuitData, i: usize) -> PolynomialCoeffs { +fn compute_z(common_data: &CommonCircuitData, _i: usize) -> PolynomialCoeffs { PolynomialCoeffs::zero(common_data.degree()) // TODO } @@ -182,7 +180,7 @@ fn compute_vanishing_polys( 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); + let next_plonk_zs = plonk_zs_commitment.leaf(i_next); let s_sigmas = prover_data.sigmas_commitment.leaf(i); debug_assert_eq!(local_wires.len(), common_data.config.num_wires); From ce0507ba12169332aa98a2ee6b838798636afcfd Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 11 May 2021 09:56:21 +0200 Subject: [PATCH 8/9] Blinding parameter can be set differently for each Merkle tree in a FRI proof. --- src/bin/bench_recursion.rs | 3 +- src/circuit_builder.rs | 6 ++- src/circuit_data.rs | 2 +- src/fri/mod.rs | 7 +-- src/fri/verifier.rs | 3 +- src/polynomial/commitment.rs | 83 +++++++++++++++++++++--------------- src/prover.rs | 11 +++-- 7 files changed, 69 insertions(+), 46 deletions(-) diff --git a/src/bin/bench_recursion.rs b/src/bin/bench_recursion.rs index b361adbb..6e035112 100644 --- a/src/bin/bench_recursion.rs +++ b/src/bin/bench_recursion.rs @@ -8,6 +8,7 @@ use plonky2::fri::FriConfig; use plonky2::gates::constant::ConstantGate; use plonky2::gates::gmimc::GMiMCGate; use plonky2::hash::GMIMC_ROUNDS; +use plonky2::prover::PLONK_BLINDING; use plonky2::witness::PartialWitness; fn main() { @@ -40,7 +41,7 @@ fn bench_prove() { rate_bits: 3, reduction_arity_bits: vec![1], num_query_rounds: 1, - blinding: true, + blinding: PLONK_BLINDING.to_vec(), }, }; diff --git a/src/circuit_builder.rs b/src/circuit_builder.rs index 45e2b06a..18ff666c 100644 --- a/src/circuit_builder.rs +++ b/src/circuit_builder.rs @@ -274,14 +274,16 @@ impl CircuitBuilder { let constant_vecs = self.constant_polys(); let constants_commitment = ListPolynomialCommitment::new( constant_vecs.into_iter().map(|v| v.ifft()).collect(), - &self.config.fri_config, + 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); let sigmas_commitment = ListPolynomialCommitment::new( sigma_vecs.into_iter().map(|v| v.ifft()).collect(), - &self.config.fri_config, + self.config.fri_config.rate_bits, + false, ); let constants_root = constants_commitment.merkle_tree.root; diff --git a/src/circuit_data.rs b/src/circuit_data.rs index 1c51e47d..07713e8d 100644 --- a/src/circuit_data.rs +++ b/src/circuit_data.rs @@ -35,7 +35,7 @@ impl Default for CircuitConfig { rate_bits: 1, reduction_arity_bits: vec![1], num_query_rounds: 1, - blinding: true, + blinding: vec![true], }, } } diff --git a/src/fri/mod.rs b/src/fri/mod.rs index 9633face..6c8401a2 100644 --- a/src/fri/mod.rs +++ b/src/fri/mod.rs @@ -20,8 +20,9 @@ pub struct FriConfig { /// Number of query rounds to perform. pub num_query_rounds: usize, - /// True if the last element of the Merkle trees' leaf vectors is a blinding element. - pub blinding: bool, + /// Vector of the same length as the number of initial Merkle trees. + /// `blinding[i]==true` iff the i-th tree is salted. + pub blinding: Vec, } fn fri_delta(rate_log: usize, conjecture: bool) -> f64 { @@ -79,7 +80,7 @@ mod tests { rate_bits, proof_of_work_bits: 2, reduction_arity_bits, - blinding: false, + blinding: vec![false], }; let tree = { let mut leaves = coset_lde diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index 952ef1ce..9e2f0164 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -149,7 +149,8 @@ fn fri_combine_initial( let e = proof .evals_proofs .iter() - .flat_map(|(v, _)| &v[..v.len() - if config.blinding { SALT_SIZE } else { 0 }]) + .enumerate() + .flat_map(|(i, (v, _))| &v[..v.len() - if config.blinding[i] { SALT_SIZE } else { 0 }]) .rev() .fold(F::ZERO, |acc, &e| alpha * acc + e); let numerator = e - interpolant.eval(subgroup_x); diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index 21fdf971..e3fb61ee 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -15,27 +15,28 @@ pub const SALT_SIZE: usize = 2; pub struct ListPolynomialCommitment { pub polynomials: Vec>, - pub fri_config: FriConfig, pub merkle_tree: MerkleTree, pub degree: usize, + pub rate_bits: usize, + pub blinding: bool, } impl ListPolynomialCommitment { - pub fn new(polynomials: Vec>, fri_config: &FriConfig) -> Self { + pub fn new(polynomials: Vec>, rate_bits: usize, blinding: bool) -> Self { let degree = polynomials[0].len(); let lde_values = polynomials .par_iter() .map(|p| { assert_eq!(p.len(), degree, "Polynomial degree invalid."); p.clone() - .lde(fri_config.rate_bits) + .lde(rate_bits) .coset_fft(F::MULTIPLICATIVE_GROUP_GENERATOR) .values }) - .chain(if fri_config.blinding { + .chain(if blinding { // If blinding, salt with two random elements to each leaf vector. (0..SALT_SIZE) - .map(|_| F::rand_vec(degree << fri_config.rate_bits)) + .map(|_| F::rand_vec(degree << rate_bits)) .collect() } else { Vec::new() @@ -48,22 +49,27 @@ impl ListPolynomialCommitment { Self { polynomials, - fri_config: fri_config.clone(), merkle_tree, degree, + rate_bits, + blinding, } } pub fn leaf(&self, index: usize) -> &[F] { let leaf = &self.merkle_tree.leaves[index]; - &leaf[0..leaf.len() - if self.fri_config.blinding { 2 } else { 0 }] + &leaf[0..leaf.len() - if self.blinding { SALT_SIZE } else { 0 }] } pub fn open( &self, points: &[F], challenger: &mut Challenger, + config: &FriConfig, ) -> (OpeningProof, Vec>) { + assert_eq!(self.rate_bits, config.rate_bits); + assert_eq!(config.blinding.len(), 1); + assert_eq!(self.blinding, config.blinding[0]); for p in points { assert_ne!( p.exp_usize(self.degree), @@ -102,7 +108,7 @@ impl ListPolynomialCommitment { let quotient = Self::compute_quotient(points, &composition_evals, &composition_poly); - let lde_quotient = PolynomialCoeffs::from(quotient.clone()).lde(self.fri_config.rate_bits); + let lde_quotient = PolynomialCoeffs::from(quotient.clone()).lde(self.rate_bits); let lde_quotient_values = lde_quotient .clone() .coset_fft(F::MULTIPLICATIVE_GROUP_GENERATOR); @@ -112,7 +118,7 @@ impl ListPolynomialCommitment { &lde_quotient, &lde_quotient_values, challenger, - &self.fri_config, + &config, ); ( @@ -128,17 +134,21 @@ impl ListPolynomialCommitment { commitments: &[&Self], points: &[F], challenger: &mut Challenger, + config: &FriConfig, ) -> (OpeningProof, Vec>>) { let degree = commitments[0].degree; - assert!( - commitments.iter().all(|c| c.degree == degree), - "Trying to open polynomial commitments of different degrees." - ); - let fri_config = &commitments[0].fri_config; - assert!( - commitments.iter().all(|c| &c.fri_config == fri_config), - "Trying to open polynomial commitments with different config." - ); + assert_eq!(config.blinding.len(), commitments.len()); + for (i, commitment) in commitments.iter().enumerate() { + assert_eq!(commitment.rate_bits, config.rate_bits, "Invalid rate."); + assert_eq!( + commitment.blinding, config.blinding[i], + "Invalid blinding paramater." + ); + assert_eq!( + commitment.degree, degree, + "Trying to open polynomial commitments of different degrees." + ); + } for p in points { assert_ne!( p.exp_usize(degree), @@ -184,7 +194,7 @@ impl ListPolynomialCommitment { let quotient = Self::compute_quotient(points, &composition_evals, &composition_poly); - let lde_quotient = PolynomialCoeffs::from(quotient.clone()).lde(fri_config.rate_bits); + let lde_quotient = PolynomialCoeffs::from(quotient.clone()).lde(config.rate_bits); let lde_quotient_values = lde_quotient .clone() .coset_fft(F::MULTIPLICATIVE_GROUP_GENERATOR); @@ -197,7 +207,7 @@ impl ListPolynomialCommitment { &lde_quotient, &lde_quotient_values, challenger, - &fri_config, + &config, ); ( @@ -213,8 +223,9 @@ impl ListPolynomialCommitment { commitments: &[&Self; 5], points: &[F], challenger: &mut Challenger, + config: &FriConfig, ) -> (OpeningProof, Vec>) { - let (op, mut evaluations) = Self::batch_open(commitments, points, challenger); + let (op, mut evaluations) = Self::batch_open(commitments, points, challenger, config); let opening_sets = evaluations .par_iter_mut() .map(|evals| { @@ -343,12 +354,12 @@ mod tests { rate_bits: 2, reduction_arity_bits: vec![3, 2, 1, 2], num_query_rounds: 3, - blinding: false, + blinding: vec![false], }; let (polys, points) = gen_random_test_case::(k, degree_log, num_points); - let lpc = ListPolynomialCommitment::new(polys, &fri_config); - let (proof, evaluations) = lpc.open(&points, &mut Challenger::new()); + let lpc = ListPolynomialCommitment::new(polys, fri_config.rate_bits, false); + let (proof, evaluations) = lpc.open(&points, &mut Challenger::new(), &fri_config); proof.verify( &points, &evaluations.into_iter().map(|e| vec![e]).collect::>(), @@ -370,12 +381,12 @@ mod tests { rate_bits: 2, reduction_arity_bits: vec![3, 2, 1, 2], num_query_rounds: 3, - blinding: true, + blinding: vec![true], }; let (polys, points) = gen_random_test_case::(k, degree_log, num_points); - let lpc = ListPolynomialCommitment::new(polys, &fri_config); - let (proof, evaluations) = lpc.open(&points, &mut Challenger::new()); + let lpc = ListPolynomialCommitment::new(polys, fri_config.rate_bits, true); + let (proof, evaluations) = lpc.open(&points, &mut Challenger::new(), &fri_config); proof.verify( &points, &evaluations.into_iter().map(|e| vec![e]).collect::>(), @@ -399,20 +410,21 @@ mod tests { rate_bits: 2, reduction_arity_bits: vec![2, 3, 1, 2], num_query_rounds: 3, - blinding: false, + blinding: vec![false, false, false], }; let (polys0, _) = gen_random_test_case::(k0, degree_log, num_points); let (polys1, _) = gen_random_test_case::(k0, degree_log, num_points); let (polys2, points) = gen_random_test_case::(k0, degree_log, num_points); - let lpc0 = ListPolynomialCommitment::new(polys0, &fri_config); - let lpc1 = ListPolynomialCommitment::new(polys1, &fri_config); - let lpc2 = ListPolynomialCommitment::new(polys2, &fri_config); + let lpc0 = ListPolynomialCommitment::new(polys0, fri_config.rate_bits, false); + let lpc1 = ListPolynomialCommitment::new(polys1, fri_config.rate_bits, false); + let lpc2 = ListPolynomialCommitment::new(polys2, fri_config.rate_bits, false); let (proof, evaluations) = ListPolynomialCommitment::batch_open( &[&lpc0, &lpc1, &lpc2], &points, &mut Challenger::new(), + &fri_config, ); proof.verify( &points, @@ -441,20 +453,21 @@ mod tests { rate_bits: 2, reduction_arity_bits: vec![2, 3, 1, 2], num_query_rounds: 3, - blinding: true, + blinding: vec![true, false, true], }; let (polys0, _) = gen_random_test_case::(k0, degree_log, num_points); let (polys1, _) = gen_random_test_case::(k0, degree_log, num_points); let (polys2, points) = gen_random_test_case::(k0, degree_log, num_points); - let lpc0 = ListPolynomialCommitment::new(polys0, &fri_config); - let lpc1 = ListPolynomialCommitment::new(polys1, &fri_config); - let lpc2 = ListPolynomialCommitment::new(polys2, &fri_config); + let lpc0 = ListPolynomialCommitment::new(polys0, fri_config.rate_bits, true); + let lpc1 = ListPolynomialCommitment::new(polys1, fri_config.rate_bits, false); + let lpc2 = ListPolynomialCommitment::new(polys2, fri_config.rate_bits, true); let (proof, evaluations) = ListPolynomialCommitment::batch_open( &[&lpc0, &lpc1, &lpc2], &points, &mut Challenger::new(), + &fri_config, ); proof.verify( &points, diff --git a/src/prover.rs b/src/prover.rs index 72d386d7..0ec2fa9a 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -26,6 +26,10 @@ macro_rules! timed { res }}; } + +/// Corresponds to constants - sigmas - wires - zs - quotient — polynomial commitments. +pub const PLONK_BLINDING: [bool; 5] = [false, false, true, true, true]; + pub(crate) fn prove( prover_data: &ProverOnlyCircuitData, common_data: &CommonCircuitData, @@ -59,7 +63,7 @@ pub(crate) fn prove( // 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), + ListPolynomialCommitment::new(wires_polynomials, fri_config.rate_bits, true), "to compute wires commitment" ); @@ -75,7 +79,7 @@ pub(crate) fn prove( let plonk_z_vecs = timed!(compute_zs(&common_data), "to compute Z's"); let plonk_zs_commitment = timed!( - ListPolynomialCommitment::new(plonk_z_vecs, &fri_config), + ListPolynomialCommitment::new(plonk_z_vecs, fri_config.rate_bits, true), "to commit to Z's" ); @@ -107,7 +111,7 @@ pub(crate) fn prove( let quotient_poly_coeff_chunks = quotient_poly_coeff.chunks(degree); all_quotient_poly_chunks.extend(quotient_poly_coeff_chunks); } - ListPolynomialCommitment::new(all_quotient_poly_chunks, &fri_config) + ListPolynomialCommitment::new(all_quotient_poly_chunks, fri_config.rate_bits, true) }, "to compute quotient polys and commit to them" ); @@ -129,6 +133,7 @@ pub(crate) fn prove( ], &zetas, &mut challenger, + &common_data.config.fri_config ), "to compute opening proofs" ); From cdbac7614976be2e293d94de7149e30374fe0973 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Tue, 11 May 2021 10:01:35 +0200 Subject: [PATCH 9/9] Fixes based on PR comments --- src/prover.rs | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/src/prover.rs b/src/prover.rs index 0ec2fa9a..434d0084 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -116,7 +116,7 @@ pub(crate) fn prove( "to compute quotient polys and commit to them" ); - challenger.observe_hash(&plonk_zs_commitment.merkle_tree.root); + challenger.observe_hash("ient_polys_commitment.merkle_tree.root); // TODO: How many do we need? let num_zetas = 2; @@ -283,23 +283,3 @@ fn compute_wire_polynomial( .collect(); PolynomialValues::new(wire_values).ifft() } - -fn compute_wire_lde( - input: usize, - witness: &PartialWitness, - degree: usize, - rate_bits: usize, -) -> PolynomialValues { - 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).lde(rate_bits) -}