From 0bae47bedb16c052c776a7ec05647556d0e40112 Mon Sep 17 00:00:00 2001 From: wborgeaud Date: Thu, 6 May 2021 17:09:55 +0200 Subject: [PATCH] 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, )