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 } }