diff --git a/Cargo.toml b/Cargo.toml index 72c827ab..21708d41 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ default-run = "bench_recursion" [dependencies] env_logger = "0.8.3" log = "0.4.14" +itertools = "0.10.0" num = "0.3" rand = "0.7.3" rand_chacha = "0.2.2" diff --git a/src/field/field.rs b/src/field/field.rs index 92d1f675..3c6f0e47 100644 --- a/src/field/field.rs +++ b/src/field/field.rs @@ -268,6 +268,7 @@ pub trait Field: } /// An iterator over the powers of a certain base element `b`: `b^0, b^1, b^2, ...`. +#[derive(Clone)] pub struct Powers { base: F, current: F, diff --git a/src/fri/mod.rs b/src/fri/mod.rs index 7c71eb77..c147e8c6 100644 --- a/src/fri/mod.rs +++ b/src/fri/mod.rs @@ -1,3 +1,5 @@ +use crate::polynomial::commitment::SALT_SIZE; + pub mod prover; pub mod verifier; @@ -25,6 +27,16 @@ pub struct FriConfig { pub blinding: Vec, } +impl FriConfig { + pub(crate) fn salt_size(&self, i: usize) -> usize { + if self.blinding[i] { + SALT_SIZE + } else { + 0 + } + } +} + fn fri_delta(rate_log: usize, conjecture: bool) -> f64 { let rate = (1 << rate_log) as f64; if conjecture { @@ -47,135 +59,3 @@ fn fri_l(codeword_len: usize, rate_log: usize, conjecture: bool) -> f64 { 1.0 / (2.0 * EPSILON * rate.sqrt()) } } - -#[cfg(test)] -mod tests { - use anyhow::Result; - use rand::rngs::ThreadRng; - use rand::Rng; - - use crate::field::crandall_field::CrandallField; - use crate::field::extension_field::Extendable; - use crate::field::field::Field; - use crate::fri::prover::fri_proof; - use crate::fri::verifier::verify_fri_proof; - use crate::merkle_tree::MerkleTree; - use crate::plonk_challenger::Challenger; - use crate::polynomial::polynomial::{PolynomialCoeffs, PolynomialValues}; - use crate::util::reverse_index_bits_in_place; - - use super::*; - - fn check_fri, const D: usize>( - degree_log: usize, - rate_bits: usize, - reduction_arity_bits: Vec, - num_query_rounds: usize, - ) -> Result<()> { - let n = 1 << degree_log; - let coeffs = PolynomialCoeffs::new(F::rand_vec(n)).lde(rate_bits); - let coset_lde = coeffs.clone().coset_fft(F::MULTIPLICATIVE_GROUP_GENERATOR); - let config = FriConfig { - num_query_rounds, - rate_bits, - proof_of_work_bits: 2, - reduction_arity_bits, - blinding: vec![false], - }; - let tree = { - let mut leaves = coset_lde - .values - .iter() - .map(|&x| vec![x]) - .collect::>(); - reverse_index_bits_in_place(&mut leaves); - MerkleTree::new(leaves, false) - }; - let coset_lde = PolynomialValues::new( - coset_lde - .values - .into_iter() - .map(F::Extension::from) - .collect(), - ); - let root = tree.root; - let mut challenger = Challenger::new(); - let proof = fri_proof::( - &[&tree], - &coeffs.to_extension::(), - &coset_lde, - &mut challenger, - &config, - ); - - let mut challenger = Challenger::new(); - verify_fri_proof( - degree_log, - &[], - F::Extension::ONE, - &[root], - &proof, - &mut challenger, - &config, - )?; - - Ok(()) - } - - fn gen_arities(degree_log: usize, rng: &mut ThreadRng) -> Vec { - let mut arities = Vec::new(); - let mut remaining = degree_log; - while remaining > 0 { - let arity = rng.gen_range(0, remaining + 1); - arities.push(arity); - remaining -= arity; - } - arities - } - - fn check_fri_multi_params, const D: usize>() -> Result<()> { - let mut rng = rand::thread_rng(); - for degree_log in 1..6 { - for rate_bits in 0..3 { - for num_query_round in 0..4 { - for _ in 0..3 { - check_fri::( - degree_log, - rate_bits, - gen_arities(degree_log, &mut rng), - num_query_round, - )?; - } - } - } - } - Ok(()) - } - - mod base { - use super::*; - - #[test] - fn test_fri_multi_params() -> Result<()> { - check_fri_multi_params::() - } - } - - mod quadratic { - use super::*; - - #[test] - fn test_fri_multi_params() -> Result<()> { - check_fri_multi_params::() - } - } - - mod quartic { - use super::*; - - #[test] - fn test_fri_multi_params() -> Result<()> { - check_fri_multi_params::() - } - } -} diff --git a/src/fri/verifier.rs b/src/fri/verifier.rs index 80997956..e6e8cae7 100644 --- a/src/fri/verifier.rs +++ b/src/fri/verifier.rs @@ -1,15 +1,16 @@ -use crate::field::extension_field::{flatten, Extendable, FieldExtension}; +use anyhow::{ensure, Result}; +use itertools::izip; + +use crate::field::extension_field::{flatten, Extendable, FieldExtension, OEF}; use crate::field::field::Field; use crate::field::lagrange::{barycentric_weights, interpolant, interpolate}; 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::plonk_common::reduce_with_iter; +use crate::proof::{FriInitialTreeProof, FriProof, FriQueryRound, Hash, OpeningSet}; use crate::util::{log2_strict, reverse_bits, reverse_index_bits_in_place}; -use anyhow::{ensure, Result}; /// Computes P'(x^arity) from {P(x*g^i)}_(i=0..arity), where g is a `arity`-th root of unity /// and P' is the FRI reduced polynomial. @@ -65,8 +66,10 @@ fn fri_verify_proof_of_work, const D: usize>( pub fn verify_fri_proof, const D: usize>( purported_degree_log: usize, - // Point-evaluation pairs for polynomial commitments. - points: &[(F::Extension, F::Extension)], + // Openings of the PLONK polynomials. + os: &OpeningSet, + // Point at which the PLONK polynomials are opened. + zeta: F::Extension, // Scaling factor to combine polynomials. alpha: F::Extension, initial_merkle_roots: &[Hash], @@ -108,11 +111,10 @@ pub fn verify_fri_proof, const D: usize>( "Number of reductions should be non-zero." ); - let interpolant = interpolant(points); for round_proof in &proof.query_round_proofs { fri_verifier_query_round( - &interpolant, - points, + os, + zeta, alpha, initial_merkle_roots, &proof, @@ -142,29 +144,77 @@ fn fri_verify_initial_proof( fn fri_combine_initial, const D: usize>( proof: &FriInitialTreeProof, alpha: F::Extension, - interpolant: &PolynomialCoeffs, - points: &[(F::Extension, F::Extension)], + os: &OpeningSet, + zeta: F::Extension, subgroup_x: F, config: &FriConfig, ) -> F::Extension { - let e = proof - .evals_proofs + assert!(D > 1, "Not implemented for D=1."); + let degree_log = proof.evals_proofs[0].1.siblings.len() - config.rate_bits; + let subgroup_x = F::Extension::from_basefield(subgroup_x); + let mut alpha_powers = alpha.powers(); + let mut sum = F::Extension::ZERO; + + // We will add three terms to `sum`: + // - one for polynomials opened at `x` only + // - one for polynomials opened at `x` and `g x` + // - one for polynomials opened at `x` and its conjugate + + let evals = [0, 1, 4] .iter() - .enumerate() - .flat_map(|(i, (v, _))| &v[..v.len() - if config.blinding[i] { SALT_SIZE } else { 0 }]) - .rev() - .fold(F::Extension::ZERO, |acc, &e| alpha * acc + e.into()); - let numerator = e - interpolant.eval(subgroup_x.into()); - let denominator = points + .flat_map(|&i| proof.unsalted_evals(i, config)) + .map(|&e| F::Extension::from_basefield(e)); + let openings = os + .constants .iter() - .map(|&(x, _)| F::Extension::from_basefield(subgroup_x) - x) - .product(); - numerator / denominator + .chain(&os.plonk_sigmas) + .chain(&os.quotient_polys); + let numerator = izip!(evals, openings, &mut alpha_powers) + .map(|(e, &o, a)| a * (e - o)) + .sum::(); + let denominator = subgroup_x - zeta; + sum += numerator / denominator; + + let ev: F::Extension = proof + .unsalted_evals(3, config) + .iter() + .zip(alpha_powers.clone()) + .map(|(&e, a)| a * e.into()) + .sum(); + let zeta_right = F::Extension::primitive_root_of_unity(degree_log) * zeta; + let zs_interpol = interpolant(&[ + (zeta, reduce_with_iter(&os.plonk_zs, alpha_powers.clone())), + ( + zeta_right, + reduce_with_iter(&os.plonk_zs_right, &mut alpha_powers), + ), + ]); + let numerator = ev - zs_interpol.eval(subgroup_x); + let denominator = (subgroup_x - zeta) * (subgroup_x - zeta_right); + sum += numerator / denominator; + + let ev: F::Extension = proof + .unsalted_evals(2, config) + .iter() + .zip(alpha_powers.clone()) + .map(|(&e, a)| a * e.into()) + .sum(); + let zeta_frob = zeta.frobenius(); + let wire_evals_frob = os.wires.iter().map(|e| e.frobenius()).collect::>(); + let wires_interpol = interpolant(&[ + (zeta, reduce_with_iter(&os.wires, alpha_powers.clone())), + (zeta_frob, reduce_with_iter(&wire_evals_frob, alpha_powers)), + ]); + let numerator = ev - wires_interpol.eval(subgroup_x); + let denominator = (subgroup_x - zeta) * (subgroup_x - zeta_frob); + sum += numerator / denominator; + + sum } fn fri_verifier_query_round, const D: usize>( - interpolant: &PolynomialCoeffs, - points: &[(F::Extension, F::Extension)], + os: &OpeningSet, + zeta: F::Extension, alpha: F::Extension, initial_merkle_roots: &[Hash], proof: &FriProof, @@ -195,8 +245,8 @@ fn fri_verifier_query_round, const D: usize>( fri_combine_initial( &round_proof.initial_trees_proof, alpha, - interpolant, - points, + os, + zeta, subgroup_x, config, ) diff --git a/src/plonk_challenger.rs b/src/plonk_challenger.rs index 4c8ff167..69891761 100644 --- a/src/plonk_challenger.rs +++ b/src/plonk_challenger.rs @@ -2,7 +2,7 @@ use crate::circuit_builder::CircuitBuilder; use crate::field::extension_field::{Extendable, FieldExtension}; use crate::field::field::Field; use crate::hash::{permute, SPONGE_RATE, SPONGE_WIDTH}; -use crate::proof::{Hash, HashTarget}; +use crate::proof::{Hash, HashTarget, OpeningSet}; use crate::target::Target; /// Observes prover messages, and generates challenges by hashing the transcript. @@ -61,6 +61,30 @@ impl Challenger { } } + pub fn observe_opening_set(&mut self, os: &OpeningSet) + where + F: Extendable, + { + let OpeningSet { + constants, + plonk_sigmas, + wires, + plonk_zs, + plonk_zs_right, + quotient_polys, + } = os; + for v in &[ + constants, + plonk_sigmas, + wires, + plonk_zs, + plonk_zs_right, + quotient_polys, + ] { + self.observe_extension_elements(v); + } + } + pub fn observe_hash(&mut self, hash: &Hash) { self.observe_elements(&hash.elements) } diff --git a/src/plonk_common.rs b/src/plonk_common.rs index 73c6d65c..990e6ea7 100644 --- a/src/plonk_common.rs +++ b/src/plonk_common.rs @@ -108,3 +108,14 @@ pub(crate) fn reduce_with_powers_recursive, const D: usize>( ) -> Target { todo!() } + +pub(crate) fn reduce_with_iter(terms: &[F], coeffs: I) -> F +where + I: IntoIterator, +{ + let mut sum = F::ZERO; + for (&term, coeff) in terms.iter().zip(coeffs) { + sum += coeff * term; + } + sum +} diff --git a/src/polynomial/commitment.rs b/src/polynomial/commitment.rs index 8727ada9..06194ad9 100644 --- a/src/polynomial/commitment.rs +++ b/src/polynomial/commitment.rs @@ -1,8 +1,8 @@ use anyhow::Result; use rayon::prelude::*; -use crate::field::extension_field::Extendable; use crate::field::extension_field::FieldExtension; +use crate::field::extension_field::{Extendable, OEF}; use crate::field::field::Field; use crate::field::lagrange::interpolant; use crate::fri::{prover::fri_proof, verifier::verify_fri_proof, FriConfig}; @@ -76,166 +76,128 @@ impl ListPolynomialCommitment { &leaf[0..leaf.len() - if self.blinding { SALT_SIZE } else { 0 }] } - pub fn open( - &self, - points: &[F::Extension], + /// Takes the commitments to the constants - sigmas - wires - zs - quotient — polynomials, + /// and an opening point `zeta` and produces a batched opening proof + opening set. + pub fn open_plonk( + commitments: &[&Self; 5], + zeta: F::Extension, challenger: &mut Challenger, config: &FriConfig, - ) -> (OpeningProof, Vec>) + ) -> (OpeningProof, OpeningSet) where F: Extendable, { - 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!(D > 1, "Not implemented for D=1."); + let degree_log = log2_strict(commitments[0].degree); + let g = F::Extension::primitive_root_of_unity(degree_log); + for &p in &[zeta, g * zeta] { assert_ne!( - p.exp(self.degree as u64), + p.exp(1 << degree_log as u64), F::Extension::ONE, "Opening point is in the subgroup." ); } - let evaluations = points - .par_iter() - .map(|&x| { - self.polynomials - .iter() - .map(|p| p.to_extension().eval(x)) - .collect::>() - }) - .collect::>(); - for evals in &evaluations { - for e in evals { - challenger.observe_extension_element(e); - } - } - - let alpha = challenger.get_extension_challenge(); - - // Scale polynomials by `alpha`. - let composition_poly = self - .polynomials - .iter() - .rev() - .fold(PolynomialCoeffs::zero(self.degree), |acc, p| { - &(&acc * alpha) + &p.to_extension() - }); - // Scale evaluations by `alpha`. - let composition_evals = evaluations - .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.rate_bits); - let lde_quotient_values = lde_quotient - .clone() - .coset_fft(F::MULTIPLICATIVE_GROUP_GENERATOR.into()); - - let fri_proof = fri_proof( - &[&self.merkle_tree], - &lde_quotient, - &lde_quotient_values, - challenger, - &config, + let os = OpeningSet::new( + zeta, + g, + commitments[0], + commitments[1], + commitments[2], + commitments[3], + commitments[4], ); - - ( - OpeningProof { - fri_proof, - quotient_degree: quotient.len(), - }, - evaluations, - ) - } - - pub fn batch_open( - commitments: &[&Self], - points: &[F::Extension], - challenger: &mut Challenger, - config: &FriConfig, - ) -> (OpeningProof, Vec>>) - where - F: Extendable, - { - 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(degree as u64), - F::Extension::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.to_extension().eval(x)) - .collect::>() - }) - .collect::>() - }) - .collect::>(); - for evals_per_point in &evaluations { - for evals in evals_per_point { - challenger.observe_extension_elements(evals); - } - } + challenger.observe_opening_set(&os); let alpha = challenger.get_extension_challenge(); + let mut cur_alpha = F::Extension::ONE; - // Scale polynomials by `alpha`. - let composition_poly = commitments + // Final low-degree polynomial that goes into FRI. + let mut final_poly = PolynomialCoeffs::empty(); + // Count the total number of polynomials accumulated into `final_poly`. + let mut poly_count = 0; + + // Polynomials opened at a single point. + let composition_poly = [0, 1, 4] .iter() - .flat_map(|c| &c.polynomials) + .flat_map(|&i| &commitments[i].polynomials) .rev() - .fold(PolynomialCoeffs::zero(degree), |acc, p| { + .fold(PolynomialCoeffs::empty(), |acc, p| { + poly_count += 1; &(&acc * alpha) + &p.to_extension() }); - // Scale evaluations by `alpha`. - let composition_evals = &evaluations - .par_iter() - .map(|v| { - v.iter() - .flatten() - .rev() - .fold(F::Extension::ZERO, |acc, &e| acc * alpha + e) - }) - .collect::>(); + let composition_eval = [&os.constants, &os.plonk_sigmas, &os.quotient_polys] + .iter() + .flat_map(|v| v.iter()) + .rev() + .fold(F::Extension::ZERO, |acc, &e| acc * alpha + e); - let quotient = Self::compute_quotient(points, &composition_evals, &composition_poly); + let quotient = Self::compute_quotient(&[zeta], &[composition_eval], &composition_poly); + final_poly = &final_poly + &("ient * cur_alpha); + cur_alpha = alpha.exp(poly_count); - let lde_quotient = PolynomialCoeffs::from(quotient.clone()).lde(config.rate_bits); - let lde_quotient_values = lde_quotient.clone().coset_fft(F::Extension::from_basefield( - F::MULTIPLICATIVE_GROUP_GENERATOR, - )); + // Zs polynomials are opened at `zeta` and `g*zeta`. + let zs_composition_poly = + commitments[3] + .polynomials + .iter() + .rev() + .fold(PolynomialCoeffs::empty(), |acc, p| { + poly_count += 1; + &(&acc * alpha) + &p.to_extension() + }); + let zs_composition_evals = [ + reduce_with_powers(&os.plonk_zs, alpha), + reduce_with_powers(&os.plonk_zs_right, alpha), + ]; + + let zs_quotient = Self::compute_quotient( + &[zeta, g * zeta], + &zs_composition_evals, + &zs_composition_poly, + ); + final_poly = &final_poly + &(&zs_quotient * cur_alpha); + cur_alpha = alpha.exp(poly_count); + + // When working in an extension field, need to check that wires are in the base field. + // Check this by opening the wires polynomials at `zeta` and `zeta.frobenius()` and using the fact that + // a polynomial `f` is over the base field iff `f(z).frobenius()=f(z.frobenius())` with high probability. + let wires_composition_poly = + commitments[2] + .polynomials + .iter() + .rev() + .fold(PolynomialCoeffs::empty(), |acc, p| { + poly_count += 1; + &(&acc * alpha) + &p.to_extension() + }); + let wire_evals_frob = os.wires.iter().map(|e| e.frobenius()).collect::>(); + let wires_composition_evals = [ + reduce_with_powers(&os.wires, alpha), + reduce_with_powers(&wire_evals_frob, alpha), + ]; + + let wires_quotient = Self::compute_quotient( + &[zeta, zeta.frobenius()], + &wires_composition_evals, + &wires_composition_poly, + ); + final_poly = &final_poly + &(&wires_quotient * cur_alpha); + + let lde_final_poly = final_poly.lde(config.rate_bits); + let lde_final_values = lde_final_poly + .clone() + .coset_fft(F::Extension::from_basefield( + F::MULTIPLICATIVE_GROUP_GENERATOR, + )); let fri_proof = fri_proof( &commitments .par_iter() .map(|c| &c.merkle_tree) .collect::>(), - &lde_quotient, - &lde_quotient_values, + &lde_final_poly, + &lde_final_values, challenger, &config, ); @@ -243,38 +205,12 @@ impl ListPolynomialCommitment { ( OpeningProof { fri_proof, - quotient_degree: quotient.len(), + quotient_degree: final_poly.len(), }, - evaluations, + os, ) } - pub fn batch_open_plonk( - commitments: &[&Self; 5], - points: &[F::Extension], - challenger: &mut Challenger, - config: &FriConfig, - ) -> (OpeningProof, Vec>) - where - F: Extendable, - { - 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( @@ -297,7 +233,7 @@ impl ListPolynomialCommitment { &acc * &PolynomialCoeffs::new(vec![-x, F::Extension::ONE]) }); let numerator = poly - &interpolant; - let (mut quotient, rem) = numerator.div_rem(&denominator); + let (quotient, rem) = numerator.div_rem(&denominator); debug_assert!(rem.is_zero()); quotient.padded(quotient.degree_plus_one().next_power_of_two()) @@ -313,39 +249,20 @@ pub struct OpeningProof, const D: usize> { impl, const D: usize> OpeningProof { pub fn verify( &self, - points: &[F::Extension], - evaluations: &[Vec>], + zeta: F::Extension, + os: &OpeningSet, merkle_roots: &[Hash], challenger: &mut Challenger, fri_config: &FriConfig, ) -> Result<()> { - for evals_per_point in evaluations { - for evals in evals_per_point { - challenger.observe_extension_elements(evals); - } - } + challenger.observe_opening_set(os); let alpha = challenger.get_extension_challenge(); - let scaled_evals = evaluations - .par_iter() - .map(|v| { - v.iter() - .flatten() - .rev() - .fold(F::Extension::ZERO, |acc, &e| acc * alpha + e) - }) - .collect::>(); - - let pairs = points - .iter() - .zip(&scaled_evals) - .map(|(&x, &e)| (x, e)) - .collect::>(); - verify_fri_proof( log2_strict(self.quotient_degree), - &pairs, + &os, + zeta, alpha, merkle_roots, &self.fri_proof, @@ -360,191 +277,102 @@ mod tests { use anyhow::Result; use super::*; + use rand::Rng; fn gen_random_test_case, const D: usize>( k: usize, degree_log: usize, - num_points: usize, - ) -> (Vec>, Vec) { + ) -> Vec> { let degree = 1 << degree_log; - let polys = (0..k) + (0..k) .map(|_| PolynomialCoeffs::new(F::rand_vec(degree))) - .collect(); - let mut points = F::Extension::rand_vec(num_points); - while points.iter().any(|&x| x.exp(degree as u64).is_one()) { - points = F::Extension::rand_vec(num_points); + .collect() + } + + fn gen_random_point, const D: usize>( + degree_log: usize, + ) -> F::Extension { + let degree = 1 << degree_log; + + let mut point = F::Extension::rand(); + while point.exp(degree as u64).is_one() { + point = F::Extension::rand(); } - (polys, points) + point } - fn check_polynomial_commitment, const D: usize>() -> Result<()> { - let k = 10; - let degree_log = 11; - let num_points = 3; - let fri_config = FriConfig { - proof_of_work_bits: 2, - rate_bits: 2, - reduction_arity_bits: vec![3, 2, 1, 2], - num_query_rounds: 3, - blinding: vec![false], - }; - let (polys, points) = gen_random_test_case::(k, degree_log, num_points); - - 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::>(), - &[lpc.merkle_tree.root], - &mut Challenger::new(), - &fri_config, - ) - } - - fn check_polynomial_commitment_blinding, const D: usize>() -> Result<()> - { - let k = 10; - let degree_log = 11; - let num_points = 3; - let fri_config = FriConfig { - proof_of_work_bits: 2, - rate_bits: 2, - reduction_arity_bits: vec![3, 2, 1, 2], - num_query_rounds: 3, - blinding: vec![true], - }; - let (polys, points) = gen_random_test_case::(k, degree_log, num_points); - - 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, - ) + fn gen_random_blindings() -> Vec { + let mut rng = rand::thread_rng(); + vec![ + rng.gen_bool(0.5), + rng.gen_bool(0.5), + rng.gen_bool(0.5), + rng.gen_bool(0.5), + rng.gen_bool(0.5), + ] } fn check_batch_polynomial_commitment, const D: usize>() -> Result<()> { - let k0 = 10; - let k1 = 3; - let k2 = 7; + let ks = [1, 2, 3, 5, 8]; 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], + blinding: gen_random_blindings(), }; - let (polys0, _) = gen_random_test_case::(k0, degree_log, num_points); - let (polys1, _) = gen_random_test_case::(k1, degree_log, num_points); - let (polys2, points) = gen_random_test_case::(k2, 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 lpcs = (0..5) + .map(|i| { + ListPolynomialCommitment::::new( + gen_random_test_case(ks[i], degree_log), + fri_config.rate_bits, + fri_config.blinding[i], + ) + }) + .collect::>(); - let (proof, evaluations) = ListPolynomialCommitment::batch_open::( - &[&lpc0, &lpc1, &lpc2], - &points, + let zeta = gen_random_point::(degree_log); + let (proof, os) = ListPolynomialCommitment::open_plonk::( + &[&lpcs[0], &lpcs[1], &lpcs[2], &lpcs[3], &lpcs[4]], + zeta, &mut Challenger::new(), &fri_config, ); + proof.verify( - &points, - &evaluations, + zeta, + &os, &[ - lpc0.merkle_tree.root, - lpc1.merkle_tree.root, - lpc2.merkle_tree.root, + lpcs[0].merkle_tree.root, + lpcs[1].merkle_tree.root, + lpcs[2].merkle_tree.root, + lpcs[3].merkle_tree.root, + lpcs[4].merkle_tree.root, ], &mut Challenger::new(), &fri_config, ) } - fn check_batch_polynomial_commitment_blinding, const D: usize>( - ) -> Result<()> { - 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::(k1, degree_log, num_points); - let (polys2, points) = gen_random_test_case::(k2, 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, - ) - } - - macro_rules! tests_commitments { - ($F:ty, $D:expr) => { - use super::*; - - #[test] - fn test_polynomial_commitment() -> Result<()> { - check_polynomial_commitment::<$F, $D>() - } - - #[test] - fn test_polynomial_commitment_blinding() -> Result<()> { - check_polynomial_commitment_blinding::<$F, $D>() - } - - #[test] - fn test_batch_polynomial_commitment() -> Result<()> { - check_batch_polynomial_commitment::<$F, $D>() - } - - #[test] - fn test_batch_polynomial_commitment_blinding() -> Result<()> { - check_batch_polynomial_commitment_blinding::<$F, $D>() - } - }; - } - - mod base { - tests_commitments!(crate::field::crandall_field::CrandallField, 1); - } - mod quadratic { - tests_commitments!(crate::field::crandall_field::CrandallField, 2); + use super::*; + use crate::field::crandall_field::CrandallField; + #[test] + fn test_batch_polynomial_commitment() -> Result<()> { + check_batch_polynomial_commitment::() + } } mod quartic { use super::*; - tests_commitments!(crate::field::crandall_field::CrandallField, 4); + use crate::field::crandall_field::CrandallField; + #[test] + fn test_batch_polynomial_commitment() -> Result<()> { + check_batch_polynomial_commitment::() + } } } diff --git a/src/proof.rs b/src/proof.rs index b12c24ec..46288808 100644 --- a/src/proof.rs +++ b/src/proof.rs @@ -1,5 +1,6 @@ use crate::field::extension_field::Extendable; use crate::field::field::Field; +use crate::fri::FriConfig; use crate::merkle_proofs::{MerkleProof, MerkleProofTarget}; use crate::polynomial::commitment::{ListPolynomialCommitment, OpeningProof}; use crate::polynomial::polynomial::PolynomialCoeffs; @@ -63,8 +64,8 @@ pub struct Proof, const D: usize> { /// Merkle root of LDEs of the quotient polynomial components. pub quotient_polys_root: Hash, - /// Purported values of each polynomial at each challenge point. - pub openings: Vec>, + /// Purported values of each polynomial at the challenge point. + pub openings: OpeningSet, /// A FRI argument for each FRI query. pub opening_proof: OpeningProof, @@ -99,6 +100,13 @@ pub struct FriInitialTreeProof { pub evals_proofs: Vec<(Vec, MerkleProof)>, } +impl FriInitialTreeProof { + pub(crate) fn unsalted_evals(&self, i: usize, config: &FriConfig) -> &[F] { + let evals = &self.evals_proofs[i].0; + &evals[..evals.len() - config.salt_size(i)] + } +} + /// Proof for a FRI query round. // TODO: Implement FriQueryRoundTarget pub struct FriQueryRound, const D: usize> { @@ -130,31 +138,37 @@ pub struct FriProofTarget { } /// The purported values of each polynomial at a single point. -pub struct OpeningSet { - pub constants: Vec, - pub plonk_sigmas: Vec, - pub wires: Vec, - pub plonk_zs: Vec, - pub quotient_polys: Vec, +pub struct OpeningSet, const D: usize> { + pub constants: Vec, + pub plonk_sigmas: Vec, + pub wires: Vec, + pub plonk_zs: Vec, + pub plonk_zs_right: Vec, + pub quotient_polys: Vec, } -impl OpeningSet { +impl, const D: usize> OpeningSet { pub fn new( - z: F, + z: F::Extension, + g: F::Extension, 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::>() + let eval_commitment = |z: F::Extension, c: &ListPolynomialCommitment| { + c.polynomials + .iter() + .map(|p| p.to_extension().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), + plonk_zs_right: eval_commitment(g * z, plonk_zs_commitment), quotient_polys: eval_commitment(z, quotient_polys_commitment), } } diff --git a/src/prover.rs b/src/prover.rs index 40cad1b9..d7dc0df8 100644 --- a/src/prover.rs +++ b/src/prover.rs @@ -116,7 +116,7 @@ pub(crate) fn prove, const D: usize>( let zeta = challenger.get_extension_challenge(); let (opening_proof, openings) = timed!( - ListPolynomialCommitment::batch_open_plonk( + ListPolynomialCommitment::open_plonk( &[ &prover_data.constants_commitment, &prover_data.sigmas_commitment, @@ -124,7 +124,7 @@ pub(crate) fn prove, const D: usize>( &plonk_zs_commitment, "ient_polys_commitment, ], - &[zeta], + zeta, &mut challenger, &common_data.config.fri_config ), diff --git a/src/verifier.rs b/src/verifier.rs index 7af0f8a8..0ad7a1a0 100644 --- a/src/verifier.rs +++ b/src/verifier.rs @@ -41,13 +41,9 @@ pub(crate) fn verify, const D: usize>( proof.quotient_polys_root, ]; - proof.opening_proof.verify( - &[zeta], - evaluations, - merkle_roots, - &mut challenger, - fri_config, - )?; + proof + .opening_proof + .verify(zeta, evaluations, merkle_roots, &mut challenger, fri_config)?; Ok(()) }