diff --git a/src/bin/bench_recursion.rs b/src/bin/bench_recursion.rs index 566fb056..6e035112 100644 --- a/src/bin/bench_recursion.rs +++ b/src/bin/bench_recursion.rs @@ -4,9 +4,11 @@ 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; +use plonky2::prover::PLONK_BLINDING; use plonky2::witness::PartialWitness; fn main() { @@ -34,11 +36,18 @@ fn bench_prove() { security_bits: 128, rate_bits: 3, num_checks: 3, + fri_config: FriConfig { + proof_of_work_bits: 1, + rate_bits: 3, + reduction_arity_bits: vec![1], + num_query_rounds: 1, + blinding: PLONK_BLINDING.to_vec(), + }, }; 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/circuit_builder.rs b/src/circuit_builder.rs index 2be48e25..18ff666c 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,22 @@ 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.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 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.rate_bits, + false, + ); - 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 +296,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..07713e8d 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: vec![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/fri/mod.rs b/src/fri/mod.rs index af931f28..6c8401a2 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, @@ -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 @@ -92,7 +93,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..9e2f0164 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,9 @@ fn fri_combine_initial( let e = proof .evals_proofs .iter() - .map(|(v, _)| v) - .flatten() + .enumerate() + .flat_map(|(i, (v, _))| &v[..v.len() - if config.blinding[i] { 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..e3fb61ee 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -6,33 +6,37 @@ 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; +use rayon::prelude::*; -struct ListPolynomialCommitment { +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 - .iter() + .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..2) - .map(|_| F::rand_vec(degree << fri_config.rate_bits)) + (0..SALT_SIZE) + .map(|_| F::rand_vec(degree << rate_bits)) .collect() } else { Vec::new() @@ -45,17 +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.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), @@ -65,7 +79,7 @@ impl ListPolynomialCommitment { } let evaluations = points - .iter() + .par_iter() .map(|&x| { self.polynomials .iter() @@ -88,23 +102,23 @@ 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::>(); 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); let fri_proof = fri_proof( - &[self.merkle_tree.clone()], + &[&self.merkle_tree], &lde_quotient, &lde_quotient_values, challenger, - &self.fri_config, + &config, ); ( @@ -116,6 +130,118 @@ impl ListPolynomialCommitment { ) } + pub fn batch_open( + commitments: &[&Self], + points: &[F], + challenger: &mut Challenger, + config: &FriConfig, + ) -> (OpeningProof, Vec>>) { + let degree = commitments[0].degree; + 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), + F::ONE, + "Opening point is in the subgroup." + ); + } + + let evaluations = points + .par_iter() + .map(|&x| { + commitments + .iter() + .map(move |c| c.polynomials.iter().map(|p| p.eval(x)).collect::>()) + .collect::>() + }) + .collect::>(); + for evals_per_point in &evaluations { + for evals in evals_per_point { + 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 + .par_iter() + .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); + + let lde_quotient = PolynomialCoeffs::from(quotient.clone()).lde(config.rate_bits); + let lde_quotient_values = lde_quotient + .clone() + .coset_fft(F::MULTIPLICATIVE_GROUP_GENERATOR); + + let fri_proof = fri_proof( + &commitments + .par_iter() + .map(|c| &c.merkle_tree) + .collect::>(), + &lde_quotient, + &lde_quotient_values, + challenger, + &config, + ); + + ( + OpeningProof { + fri_proof, + quotient_degree: quotient.len(), + }, + evaluations, + ) + } + + pub fn batch_open_plonk( + commitments: &[&Self; 5], + points: &[F], + challenger: &mut Challenger, + config: &FriConfig, + ) -> (OpeningProof, Vec>) { + let (op, mut evaluations) = Self::batch_open(commitments, points, challenger, config); + let opening_sets = evaluations + .par_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 { @@ -151,20 +277,27 @@ impl OpeningProof { pub fn verify( &self, points: &[F], - evaluations: &[Vec], - merkle_root: Hash, + 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)) + .par_iter() + .map(|v| { + v.iter() + .flatten() + .rev() + .fold(F::ZERO, |acc, &e| acc * alpha + e) + }) .collect::>(); let pairs = points @@ -177,7 +310,7 @@ impl OpeningProof { log2_strict(self.quotient_degree), &pairs, alpha, - &[merkle_root], + merkle_roots, &self.fri_proof, challenger, fri_config, @@ -221,16 +354,16 @@ 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, - lpc.merkle_tree.root, + &evaluations.into_iter().map(|e| vec![e]).collect::>(), + &[lpc.merkle_tree.root], &mut Challenger::new(), &fri_config, ) @@ -248,16 +381,102 @@ 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::>(), + &[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: 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.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, &evaluations, - lpc.merkle_tree.root, + &[ + 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: 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.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, + &evaluations, + &[ + lpc0.merkle_tree.root, + lpc1.merkle_tree.root, + lpc2.merkle_tree.root, + ], &mut Challenger::new(), &fri_config, ) 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..ff28abc7 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, OpeningProof}; use crate::polynomial::polynomial::PolynomialCoeffs; use crate::target::Target; use std::convert::TryInto; @@ -65,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 { @@ -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 cead7a6a..434d0084 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -4,67 +4,67 @@ 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::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::util::{transpose, transpose_poly_values}; +use crate::util::transpose; 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 + }}; +} + +/// 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, inputs: PartialWitness, ) -> Proof { + 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 wire_ldes = (0..num_wires) - .into_par_iter() - .map(|i| compute_wire_lde(i, &witness, degree, config.rate_bits)) - .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_wire_transpose = Instant::now(); - let wire_ldes_t = transpose_poly_values(wire_ldes); - 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() + let wires_commitment = timed!( + ListPolynomialCommitment::new(wires_polynomials, fri_config.rate_bits, true), + "to compute wires commitment" ); let mut challenger = Challenger::new(); @@ -72,70 +72,71 @@ 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", - start_plonk_z.elapsed().as_secs_f32() + 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.rate_bits, true), + "to commit to Z's" ); - let start_plonk_z_root = Instant::now(); - let plonk_zs_tree = MerkleTree::new(plonk_z_ldes_t, true); - 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); - let start_vanishing_polys = Instant::now(); - let vanishing_polys = compute_vanishing_polys( - common_data, - prover_data, - &wires_tree, - &plonk_zs_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, + &plonk_zs_commitment, + &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_chunk_ldes = 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); - } - let quotient_polys_tree = - MerkleTree::new(transpose_poly_values(all_quotient_poly_chunk_ldes), true); - challenger.observe_hash("ient_polys_tree.root); - 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.rate_bits, true) + }, + "to compute quotient polys and commit to them" ); - let openings = Vec::new(); // TODO + challenger.observe_hash("ient_polys_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 (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, + &common_data.config.fri_config + ), + "to compute opening proofs" + ); info!( "{:.3}s for overall witness & proof generation", @@ -143,30 +144,30 @@ 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, + opening_proof, } } -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. 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], @@ -181,11 +182,11 @@ fn compute_vanishing_polys( .enumerate() .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_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 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); debug_assert_eq!(local_wires.len(), common_data.config.num_wires); debug_assert_eq!(local_plonk_zs.len(), num_checks); @@ -264,12 +265,11 @@ fn compute_vanishing_poly_entry( reduce_with_powers_multi(&vanishing_terms, alphas) } -fn compute_wire_lde( +fn compute_wire_polynomial( input: usize, witness: &PartialWitness, degree: usize, - rate_bits: usize, -) -> PolynomialValues { +) -> 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 @@ -281,5 +281,5 @@ fn compute_wire_lde( .unwrap_or(F::ZERO) }) .collect(); - PolynomialValues::new(wire_values).lde(rate_bits) + PolynomialValues::new(wire_values).ifft() } 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 } }