From e1a69d4a1219f83778472494080949b6825be8f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Garillot?= <4142+huitseeker@users.noreply.github.com> Date: Mon, 19 Feb 2024 11:59:09 -0500 Subject: [PATCH] Fold the non_hiding_kzg module in relevant files (#328) * Revert "feat: Implement batch operations in non_hiding_kzg module (#269)" This reverts commit db4375f58356f121f706447b95fb76af230092af. * refactor: remove non_hiding_kzg module, split code where dependents need it - Moved `UniversalParams` and several dependent structures for the KZG10 scheme in the `kzg_commitment.rs` file. - Deleted the `non_hiding_kzg.rs` file, - Consolidated KZG related structs under the `kzg_commitment` module, - Updated `mod.rs` to reflect the removal of the `non_hiding_kzg` module. --- src/provider/hyperkzg.rs | 39 +- src/provider/kzg_commitment.rs | 217 ++++++++++- src/provider/mod.rs | 1 - src/provider/non_hiding_kzg.rs | 529 --------------------------- src/provider/non_hiding_zeromorph.rs | 117 +++++- src/provider/shplonk.rs | 4 +- 6 files changed, 342 insertions(+), 565 deletions(-) delete mode 100644 src/provider/non_hiding_kzg.rs diff --git a/src/provider/hyperkzg.rs b/src/provider/hyperkzg.rs index 7ba1e7505..01c4d9a65 100644 --- a/src/provider/hyperkzg.rs +++ b/src/provider/hyperkzg.rs @@ -9,8 +9,7 @@ use crate::{ errors::NovaError, provider::{ - kzg_commitment::KZGCommitmentEngine, - non_hiding_kzg::{trim, KZGProverKey, KZGVerifierKey, UniversalKZGParam}, + kzg_commitment::{KZGCommitmentEngine, KZGProverKey, KZGVerifierKey, UniversalKZGParam}, pedersen::Commitment, traits::DlogGroup, util::iterators::DoubleEndedIteratorExt as _, @@ -127,7 +126,7 @@ where fn setup(ck: Arc>) -> (Self::ProverKey, Self::VerifierKey) { let len = ck.length() - 1; - trim(ck, len) + UniversalKZGParam::trim(ck, len) } fn prove( @@ -437,36 +436,52 @@ mod tests { let ck: CommitmentKey = as CommitmentEngineTrait>::setup(b"test", n); let ck = Arc::new(ck); - let (pk, _vk): (KZGProverKey, KZGVerifierKey) = + let (pk, vk): (KZGProverKey, KZGVerifierKey) = EvaluationEngine::::setup(ck.clone()); // poly is in eval. representation; evaluated at [(0,0), (0,1), (1,0), (1,1)] let poly = vec![Fr::from(1), Fr::from(2), Fr::from(2), Fr::from(4)]; let C = as CommitmentEngineTrait>::commit(&ck, &poly); - let mut tr = Keccak256Transcript::::new(b"TestEval"); - // Call the prover with a (point, eval) pair. The prover recomputes - // poly(point) = eval', and fails if eval' != eval + let test_inner = |point: Vec, eval: Fr| -> Result<(), NovaError> { + let mut tr = Keccak256Transcript::::new(b"TestEval"); + let proof = + EvaluationEngine::::prove(&ck, &pk, &mut tr, &C, &poly, &point, &eval).unwrap(); + let mut tr = Keccak256Transcript::new(b"TestEval"); + EvaluationEngine::::verify(&vk, &mut tr, &C, &point, &eval, &proof) + }; + + // Call the prover with a (point, eval) pair. + // The prover does not recompute so it may produce a proof, but it should not verify let point = vec![Fr::from(0), Fr::from(0)]; let eval = Fr::ONE; - assert!(EvaluationEngine::::prove(&ck, &pk, &mut tr, &C, &poly, &point, &eval).is_ok()); + assert!(test_inner(point, eval).is_ok()); let point = vec![Fr::from(0), Fr::from(1)]; let eval = Fr::from(2); - assert!(EvaluationEngine::::prove(&ck, &pk, &mut tr, &C, &poly, &point, &eval).is_ok()); + assert!(test_inner(point, eval).is_ok()); let point = vec![Fr::from(1), Fr::from(1)]; let eval = Fr::from(4); - assert!(EvaluationEngine::::prove(&ck, &pk, &mut tr, &C, &poly, &point, &eval).is_ok()); + assert!(test_inner(point, eval).is_ok()); let point = vec![Fr::from(0), Fr::from(2)]; let eval = Fr::from(3); - assert!(EvaluationEngine::::prove(&ck, &pk, &mut tr, &C, &poly, &point, &eval).is_ok()); + assert!(test_inner(point, eval).is_ok()); let point = vec![Fr::from(2), Fr::from(2)]; let eval = Fr::from(9); - assert!(EvaluationEngine::::prove(&ck, &pk, &mut tr, &C, &poly, &point, &eval).is_ok()); + assert!(test_inner(point, eval).is_ok()); + + // Try a couple incorrect evaluations and expect failure + let point = vec![Fr::from(2), Fr::from(2)]; + let eval = Fr::from(50); + assert!(test_inner(point, eval).is_err()); + + let point = vec![Fr::from(0), Fr::from(2)]; + let eval = Fr::from(4); + assert!(test_inner(point, eval).is_err()); } #[test] diff --git a/src/provider/kzg_commitment.rs b/src/provider/kzg_commitment.rs index 5d3828d6a..c195aac98 100644 --- a/src/provider/kzg_commitment.rs +++ b/src/provider/kzg_commitment.rs @@ -3,23 +3,218 @@ use std::marker::PhantomData; -use ff::PrimeFieldBits; -use group::{prime::PrimeCurveAffine, Curve}; +use abomonation_derive::Abomonation; +use ff::{Field, PrimeField, PrimeFieldBits}; +use group::{prime::PrimeCurveAffine, Curve, Group as _}; use pairing::Engine; use rand::rngs::StdRng; -use rand_core::SeedableRng; +use rand_core::{CryptoRng, RngCore, SeedableRng}; use serde::{Deserialize, Serialize}; +use std::sync::Arc; -use crate::traits::{ - commitment::{CommitmentEngineTrait, Len}, - Engine as NovaEngine, Group, +use crate::provider::pedersen::Commitment; +use crate::provider::traits::DlogGroup; +use crate::provider::util::fb_msm; +use crate::{ + digest::SimpleDigestible, + traits::{ + commitment::{CommitmentEngineTrait, Len}, + Engine as NovaEngine, Group, TranscriptReprTrait, + }, }; -use crate::provider::{ - non_hiding_kzg::{UVKZGCommitment, UniversalKZGParam}, - pedersen::Commitment, - traits::DlogGroup, -}; +/// `UniversalParams` are the universal parameters for the KZG10 scheme. +#[derive(Debug, Clone, Eq, Serialize, Deserialize, Abomonation)] +#[serde(bound( + serialize = "E::G1Affine: Serialize, E::G2Affine: Serialize", + deserialize = "E::G1Affine: Deserialize<'de>, E::G2Affine: Deserialize<'de>" +))] +#[abomonation_omit_bounds] +pub struct UniversalKZGParam { + /// Group elements of the form `{ β^i G }`, where `i` ranges from 0 to + /// `degree`. + // this is a hack; we just assume the size of the element. + // Look for the static assertions in provider macros for a justification + #[abomonate_with(Vec<[u64; 8]>)] + pub powers_of_g: Vec, + /// Group elements of the form `{ β^i H }`, where `i` ranges from 0 to + /// `degree`. + // this is a hack; we just assume the size of the element. + // Look for the static assertions in provider macros for a justification + #[abomonate_with(Vec<[u64; 16]>)] + pub powers_of_h: Vec, +} + +impl PartialEq for UniversalKZGParam { + fn eq(&self, other: &Self) -> bool { + self.powers_of_g == other.powers_of_g && self.powers_of_h == other.powers_of_h + } +} +// for the purpose of the Len trait, we count commitment bases, i.e. G1 elements +impl Len for UniversalKZGParam { + fn length(&self) -> usize { + self.powers_of_g.len() + } +} + +/// `UnivariateProverKey` is used to generate a proof +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct KZGProverKey { + /// generators from the universal parameters + uv_params: Arc>, + /// offset at which we start reading into the SRS + offset: usize, + /// maximum supported size + supported_size: usize, +} + +impl KZGProverKey { + pub(in crate::provider) fn new( + uv_params: Arc>, + offset: usize, + supported_size: usize, + ) -> Self { + assert!( + uv_params.max_degree() >= offset + supported_size, + "not enough bases (req: {} from offset {}) in the UVKZGParams (length: {})", + supported_size, + offset, + uv_params.max_degree() + ); + Self { + uv_params, + offset, + supported_size, + } + } + + pub fn powers_of_g(&self) -> &[E::G1Affine] { + &self.uv_params.powers_of_g[self.offset..self.offset + self.supported_size] + } +} + +/// `UVKZGVerifierKey` is used to check evaluation proofs for a given +/// commitment. +#[derive(Clone, Debug, Eq, PartialEq, Serialize)] +#[serde(bound(serialize = "E::G1Affine: Serialize, E::G2Affine: Serialize",))] +pub struct KZGVerifierKey { + /// The generator of G1. + pub g: E::G1Affine, + /// The generator of G2. + pub h: E::G2Affine, + /// β times the above generator of G2. + pub beta_h: E::G2Affine, +} + +impl SimpleDigestible for KZGVerifierKey +where + E::G1Affine: Serialize, + E::G2Affine: Serialize, +{ +} + +impl UniversalKZGParam { + /// Returns the maximum supported degree + pub fn max_degree(&self) -> usize { + self.powers_of_g.len() + } + + /// Trim the universal parameters to specialize the public parameters + /// for univariate polynomials to the given `supported_size`, and + /// returns prover key and verifier key. `supported_size` should + /// be in range `1..params.len()` + /// + /// # Panics + /// If `supported_size` is greater than `self.max_degree()`, or `self.max_degree()` is zero. + pub fn trim(ukzg: Arc, supported_size: usize) -> (KZGProverKey, KZGVerifierKey) { + assert!(ukzg.max_degree() > 0, "max_degree is zero"); + let g = ukzg.powers_of_g[0]; + let h = ukzg.powers_of_h[0]; + let beta_h = ukzg.powers_of_h[1]; + let pk = KZGProverKey::new(ukzg, 0, supported_size + 1); + let vk = KZGVerifierKey { g, h, beta_h }; + (pk, vk) + } +} + +impl UniversalKZGParam +where + E::Fr: PrimeFieldBits, +{ + /// Build SRS for testing. + /// WARNING: THIS FUNCTION IS FOR TESTING PURPOSE ONLY. + /// THE OUTPUT SRS SHOULD NOT BE USED IN PRODUCTION. + pub fn gen_srs_for_testing(mut rng: &mut R, max_degree: usize) -> Self { + let beta = E::Fr::random(&mut rng); + let g = E::G1::random(&mut rng); + let h = E::G2::random(rng); + + let nz_powers_of_beta = (0..=max_degree) + .scan(beta, |acc, _| { + let val = *acc; + *acc *= beta; + Some(val) + }) + .collect::>(); + + let window_size = fb_msm::get_mul_window_size(max_degree); + let scalar_bits = E::Fr::NUM_BITS as usize; + + let (powers_of_g_projective, powers_of_h_projective) = rayon::join( + || { + let g_table = fb_msm::get_window_table(scalar_bits, window_size, g); + fb_msm::multi_scalar_mul::(scalar_bits, window_size, &g_table, &nz_powers_of_beta) + }, + || { + let h_table = fb_msm::get_window_table(scalar_bits, window_size, h); + fb_msm::multi_scalar_mul::(scalar_bits, window_size, &h_table, &nz_powers_of_beta) + }, + ); + + let mut powers_of_g = vec![E::G1Affine::identity(); powers_of_g_projective.len()]; + let mut powers_of_h = vec![E::G2Affine::identity(); powers_of_h_projective.len()]; + + rayon::join( + || E::G1::batch_normalize(&powers_of_g_projective, &mut powers_of_g), + || E::G2::batch_normalize(&powers_of_h_projective, &mut powers_of_h), + ); + + Self { + powers_of_g, + powers_of_h, + } + } +} + +/// Commitments +#[derive(Debug, Clone, Copy, Eq, PartialEq, Default, Serialize, Deserialize)] +#[serde(bound( + serialize = "E::G1Affine: Serialize", + deserialize = "E::G1Affine: Deserialize<'de>" +))] +pub struct UVKZGCommitment( + /// the actual commitment is an affine point. + pub E::G1Affine, +); + +impl TranscriptReprTrait for UVKZGCommitment +where + E::G1: DlogGroup, + // Note: due to the move of the bound TranscriptReprTrait on G::Base from Group to Engine + ::Base: TranscriptReprTrait, +{ + fn to_transcript_bytes(&self) -> Vec { + // TODO: avoid the round-trip through the group (to_curve .. to_coordinates) + let (x, y, is_infinity) = self.0.to_curve().to_coordinates(); + let is_infinity_byte = (!is_infinity).into(); + [ + x.to_transcript_bytes(), + y.to_transcript_bytes(), + [is_infinity_byte].to_vec(), + ] + .concat() + } +} /// Provides a commitment engine #[derive(Clone, Debug, PartialEq, Eq)] diff --git a/src/provider/mod.rs b/src/provider/mod.rs index 60611ee6f..ae9494fec 100644 --- a/src/provider/mod.rs +++ b/src/provider/mod.rs @@ -15,7 +15,6 @@ pub(crate) mod secp_secq; pub(crate) mod traits; // a non-hiding variant of {kzg, zeromorph} mod kzg_commitment; -mod non_hiding_kzg; pub(crate) mod util; // crate-private modules diff --git a/src/provider/non_hiding_kzg.rs b/src/provider/non_hiding_kzg.rs deleted file mode 100644 index 4510bc8ce..000000000 --- a/src/provider/non_hiding_kzg.rs +++ /dev/null @@ -1,529 +0,0 @@ -//! Non-hiding variant of KZG10 scheme for univariate polynomials. -use crate::{digest::SimpleDigestible, zip_with_for_each}; -use abomonation_derive::Abomonation; -use ff::{Field, PrimeField, PrimeFieldBits}; -use group::{prime::PrimeCurveAffine, Curve, Group as _}; -use itertools::Itertools as _; -use pairing::{Engine, MillerLoopResult, MultiMillerLoop}; -use rand_core::{CryptoRng, RngCore}; -use rayon::iter::{IntoParallelIterator, ParallelIterator}; -use serde::{Deserialize, Serialize}; -use std::{borrow::Borrow, marker::PhantomData, ops::Mul, sync::Arc}; - -use crate::{ - errors::{NovaError, PCSError}, - provider::traits::DlogGroup, - provider::util::fb_msm, - traits::{commitment::Len, Group, TranscriptReprTrait}, -}; - -/// `UniversalParams` are the universal parameters for the KZG10 scheme. -#[derive(Debug, Clone, Eq, Serialize, Deserialize, Abomonation)] -#[serde(bound( - serialize = "E::G1Affine: Serialize, E::G2Affine: Serialize", - deserialize = "E::G1Affine: Deserialize<'de>, E::G2Affine: Deserialize<'de>" -))] -#[abomonation_omit_bounds] -pub struct UniversalKZGParam { - /// Group elements of the form `{ β^i G }`, where `i` ranges from 0 to - /// `degree`. - // this is a hack; we just assume the size of the element. - // Look for the static assertions in provider macros for a justification - #[abomonate_with(Vec<[u64; 8]>)] - pub powers_of_g: Vec, - /// Group elements of the form `{ β^i H }`, where `i` ranges from 0 to - /// `degree`. - // this is a hack; we just assume the size of the element. - // Look for the static assertions in provider macros for a justification - #[abomonate_with(Vec<[u64; 16]>)] - pub powers_of_h: Vec, -} - -impl PartialEq for UniversalKZGParam { - fn eq(&self, other: &Self) -> bool { - self.powers_of_g == other.powers_of_g && self.powers_of_h == other.powers_of_h - } -} - -// for the purpose of the Len trait, we count commitment bases, i.e. G1 elements -impl Len for UniversalKZGParam { - fn length(&self) -> usize { - self.powers_of_g.len() - } -} - -/// `UnivariateProverKey` is used to generate a proof -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct KZGProverKey { - /// generators from the universal parameters - uv_params: Arc>, - /// offset at which we start reading into the SRS - offset: usize, - /// maximum supported size - supported_size: usize, -} - -impl KZGProverKey { - pub(in crate::provider) fn new( - uv_params: Arc>, - offset: usize, - supported_size: usize, - ) -> Self { - assert!( - uv_params.max_degree() >= offset + supported_size, - "not enough bases (req: {} from offset {}) in the UVKZGParams (length: {})", - supported_size, - offset, - uv_params.max_degree() - ); - Self { - uv_params, - offset, - supported_size, - } - } - - pub fn powers_of_g(&self) -> &[E::G1Affine] { - &self.uv_params.powers_of_g[self.offset..self.offset + self.supported_size] - } -} - -/// `UVKZGVerifierKey` is used to check evaluation proofs for a given -/// commitment. -#[derive(Clone, Debug, Eq, PartialEq, Serialize)] -#[serde(bound(serialize = "E::G1Affine: Serialize, E::G2Affine: Serialize",))] -pub struct KZGVerifierKey { - /// The generator of G1. - pub g: E::G1Affine, - /// The generator of G2. - pub h: E::G2Affine, - /// β times the above generator of G2. - pub beta_h: E::G2Affine, -} - -impl SimpleDigestible for KZGVerifierKey -where - E::G1Affine: Serialize, - E::G2Affine: Serialize, -{ -} - -impl UniversalKZGParam { - /// Returns the maximum supported degree - pub fn max_degree(&self) -> usize { - self.powers_of_g.len() - } -} - -/// Trim the universal parameters to specialize the public parameters -/// for univariate polynomials to the given `supported_size`, and -/// returns prover key and verifier key. `supported_size` should -/// be in range `1..params.len()` -/// -/// # Panics -/// If `supported_size` is greater than `self.max_degree()`, or `self.max_degree()` is zero. -pub fn trim( - ukzg: Arc>, - supported_size: usize, -) -> (KZGProverKey, KZGVerifierKey) { - assert!(ukzg.max_degree() > 0, "max_degree is zero"); - let g = ukzg.powers_of_g[0]; - let h = ukzg.powers_of_h[0]; - let beta_h = ukzg.powers_of_h[1]; - let pk = KZGProverKey::new(ukzg, 0, supported_size + 1); - let vk = KZGVerifierKey { g, h, beta_h }; - (pk, vk) -} - -impl UniversalKZGParam -where - E::Fr: PrimeFieldBits, -{ - /// Build SRS for testing. - /// WARNING: THIS FUNCTION IS FOR TESTING PURPOSE ONLY. - /// THE OUTPUT SRS SHOULD NOT BE USED IN PRODUCTION. - pub fn gen_srs_for_testing(mut rng: &mut R, max_degree: usize) -> Self { - let beta = E::Fr::random(&mut rng); - let g = E::G1::random(&mut rng); - let h = E::G2::random(rng); - - let nz_powers_of_beta = (0..=max_degree) - .scan(beta, |acc, _| { - let val = *acc; - *acc *= beta; - Some(val) - }) - .collect::>(); - - let window_size = fb_msm::get_mul_window_size(max_degree); - let scalar_bits = E::Fr::NUM_BITS as usize; - - let (powers_of_g_projective, powers_of_h_projective) = rayon::join( - || { - let g_table = fb_msm::get_window_table(scalar_bits, window_size, g); - fb_msm::multi_scalar_mul::(scalar_bits, window_size, &g_table, &nz_powers_of_beta) - }, - || { - let h_table = fb_msm::get_window_table(scalar_bits, window_size, h); - fb_msm::multi_scalar_mul::(scalar_bits, window_size, &h_table, &nz_powers_of_beta) - }, - ); - - let mut powers_of_g = vec![E::G1Affine::identity(); powers_of_g_projective.len()]; - let mut powers_of_h = vec![E::G2Affine::identity(); powers_of_h_projective.len()]; - - rayon::join( - || E::G1::batch_normalize(&powers_of_g_projective, &mut powers_of_g), - || E::G2::batch_normalize(&powers_of_h_projective, &mut powers_of_h), - ); - - Self { - powers_of_g, - powers_of_h, - } - } -} -/// Commitments -#[derive(Debug, Clone, Copy, Eq, PartialEq, Default, Serialize, Deserialize)] -#[serde(bound( - serialize = "E::G1Affine: Serialize", - deserialize = "E::G1Affine: Deserialize<'de>" -))] -pub struct UVKZGCommitment( - /// the actual commitment is an affine point. - pub E::G1Affine, -); - -impl TranscriptReprTrait for UVKZGCommitment -where - E::G1: DlogGroup, - // Note: due to the move of the bound TranscriptReprTrait on G::Base from Group to Engine - ::Base: TranscriptReprTrait, -{ - fn to_transcript_bytes(&self) -> Vec { - // TODO: avoid the round-trip through the group (to_curve .. to_coordinates) - let (x, y, is_infinity) = self.0.to_curve().to_coordinates(); - let is_infinity_byte = (!is_infinity).into(); - [ - x.to_transcript_bytes(), - y.to_transcript_bytes(), - [is_infinity_byte].to_vec(), - ] - .concat() - } -} - -/// Polynomial Evaluation -#[derive(Debug, Eq, PartialEq, Default)] -pub struct UVKZGEvaluation(pub E::Fr); - -#[derive(Debug, Eq, PartialEq, Default)] - -/// Proofs -pub struct UVKZGProof { - /// proof - pub proof: E::G1Affine, -} - -/// Polynomial and its associated types -type UVKZGPoly = crate::spartan::polys::univariate::UniPoly; - -#[derive(Debug, Eq, PartialEq, Default)] -/// KZG Polynomial Commitment Scheme on univariate polynomial. -/// Note: this is non-hiding, which is why we will implement traits on this token struct, -/// as we expect to have several impls for the trait pegged on the same instance of a `pairing::Engine`. -#[allow(clippy::upper_case_acronyms)] -pub struct UVKZGPCS { - #[doc(hidden)] - phantom: PhantomData, -} - -impl UVKZGPCS -where - E::G1: DlogGroup, -{ - pub fn commit_offset( - prover_param: impl Borrow>, - poly: &UVKZGPoly, - offset: usize, - ) -> Result, NovaError> { - let prover_param = prover_param.borrow(); - - if poly.degree() > prover_param.powers_of_g().len() { - return Err(NovaError::PCSError(PCSError::LengthError)); - } - - let scalars = poly.coeffs.as_slice(); - let bases = prover_param.powers_of_g(); - - // We can avoid some scalar multiplications if 'scalars' contains a lot of leading zeroes using - // offset, that points where non-zero scalars start. - let C = ::vartime_multiscalar_mul( - &scalars[offset..], - &bases[offset..scalars.len()], - ); - - Ok(UVKZGCommitment(C.to_affine())) - } - - /// Generate a commitment for a polynomial - /// Note that the scheme is not hiding - pub fn commit( - prover_param: impl Borrow>, - poly: &UVKZGPoly, - ) -> Result, NovaError> { - let prover_param = prover_param.borrow(); - - if poly.degree() > prover_param.powers_of_g().len() { - return Err(NovaError::PCSError(PCSError::LengthError)); - } - let C = ::vartime_multiscalar_mul( - poly.coeffs.as_slice(), - &prover_param.powers_of_g()[..poly.coeffs.len()], - ); - Ok(UVKZGCommitment(C.to_affine())) - } - - /// Generate a commitment for a list of polynomials - #[allow(dead_code)] - pub fn batch_commit( - prover_param: impl Borrow>, - polys: &[UVKZGPoly], - ) -> Result>, NovaError> { - let prover_param = prover_param.borrow(); - - polys - .into_par_iter() - .map(|poly| Self::commit(prover_param, poly)) - .collect::>, NovaError>>() - } - - /// On input a polynomial `p` and a point `point`, outputs a proof for the - /// same. - pub fn open( - prover_param: impl Borrow>, - polynomial: &UVKZGPoly, - point: &E::Fr, - ) -> Result<(UVKZGProof, UVKZGEvaluation), NovaError> { - let prover_param = prover_param.borrow(); - let divisor = UVKZGPoly { - coeffs: vec![-*point, E::Fr::ONE], - }; - let witness_polynomial = polynomial - .divide_with_q_and_r(&divisor) - .map(|(q, _r)| q) - .ok_or(NovaError::PCSError(PCSError::ZMError))?; - let proof = ::vartime_multiscalar_mul( - witness_polynomial.coeffs.as_slice(), - &prover_param.powers_of_g()[..witness_polynomial.coeffs.len()], - ); - let evaluation = UVKZGEvaluation(polynomial.evaluate(point)); - - Ok(( - UVKZGProof { - proof: proof.to_affine(), - }, - evaluation, - )) - } - - /// Input a list of polynomials, and a same number of points, - /// compute a multi-opening for all the polynomials. - // This is a naive approach - // TODO: to implement a more efficient batch opening algorithm - #[allow(dead_code)] - pub fn batch_open( - prover_param: impl Borrow>, - polynomials: &[UVKZGPoly], - points: &[E::Fr], - ) -> Result<(Vec>, Vec>), NovaError> { - if polynomials.len() != points.len() { - // TODO: a better Error - return Err(NovaError::PCSError(PCSError::LengthError)); - } - let mut batch_proof = vec![]; - let mut evals = vec![]; - for (poly, point) in polynomials.iter().zip_eq(points.iter()) { - let (proof, eval) = Self::open(prover_param.borrow(), poly, point)?; - batch_proof.push(proof); - evals.push(eval); - } - - Ok((batch_proof, evals)) - } - - /// Verifies that `value` is the evaluation at `x` of the polynomial - /// committed inside `comm`. - #[allow(dead_code, clippy::unnecessary_wraps)] - fn verify( - verifier_param: impl Borrow>, - commitment: &UVKZGCommitment, - point: &E::Fr, - proof: &UVKZGProof, - evaluation: &UVKZGEvaluation, - ) -> Result { - let verifier_param = verifier_param.borrow(); - - let pairing_inputs: Vec<(E::G1Affine, E::G2Prepared)> = vec![ - ( - (verifier_param.g.mul(evaluation.0) - proof.proof.mul(point) - commitment.0.to_curve()) - .to_affine(), - verifier_param.h.into(), - ), - (proof.proof, verifier_param.beta_h.into()), - ]; - #[allow(clippy::map_identity)] // this does by_ref() on a tuple - let pairing_input_refs = pairing_inputs - .iter() - .map(|(a, b)| (a, b)) - .collect::>(); - let pairing_result = E::multi_miller_loop(pairing_input_refs.as_slice()).final_exponentiation(); - Ok(pairing_result.is_identity().into()) - } - - /// Verifies that `value_i` is the evaluation at `x_i` of the polynomial - /// `poly_i` committed inside `comm`. - // This is a naive approach - // TODO: to implement the more efficient batch verification algorithm - #[allow(dead_code, clippy::unnecessary_wraps)] - pub fn batch_verify( - verifier_params: impl Borrow>, - multi_commitment: &[UVKZGCommitment], - points: &[E::Fr], - values: &[UVKZGEvaluation], - batch_proof: &[UVKZGProof], - rng: &mut R, - ) -> Result { - let verifier_params = verifier_params.borrow(); - - let mut total_c = ::identity(); - let mut total_w = ::identity(); - - let mut randomizer = E::Fr::ONE; - // Instead of multiplying g and gamma_g in each turn, we simply accumulate - // their coefficients and perform a final multiplication at the end. - let mut g_multiplier = E::Fr::ZERO; - zip_with_for_each!( - into_iter, - (multi_commitment, points, values, batch_proof), - |c, z, v, proof| { - let w = proof.proof; - let mut temp = w.mul(*z); - temp += &c.0; - let c = temp; - g_multiplier += &(randomizer * v.0); - total_c += &c.mul(randomizer); - total_w += &w.mul(randomizer); - // We don't need to sample randomizers from the full field, - // only from 128-bit strings. - randomizer = E::Fr::from_u128(rand::Rng::gen::(rng)); - } - ); - total_c -= &verifier_params.g.mul(g_multiplier); - - let mut affine_points = vec![E::G1Affine::identity(); 2]; - E::G1::batch_normalize(&[-total_w, total_c], &mut affine_points); - let (total_w, total_c) = (affine_points[0], affine_points[1]); - - let result = E::multi_miller_loop(&[ - (&total_w, &verifier_params.beta_h.into()), - (&total_c, &verifier_params.h.into()), - ]) - .final_exponentiation() - .is_identity() - .into(); - - Ok(result) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::spartan::polys::univariate::UniPoly; - use rand::{thread_rng, Rng}; - use rand_core::{CryptoRng, RngCore}; - - fn random(degree: usize, mut rng: &mut R) -> UVKZGPoly { - let coeffs = (0..=degree).map(|_| F::random(&mut rng)).collect(); - UniPoly::new(coeffs) - } - - fn end_to_end_test_template() -> Result<(), NovaError> - where - E: MultiMillerLoop, - E::G1: DlogGroup, - E::Fr: PrimeFieldBits, - { - for _ in 0..100 { - let mut rng = &mut thread_rng(); - let degree = rng.gen_range(2..20); - - let pp = Arc::new(UniversalKZGParam::::gen_srs_for_testing( - &mut rng, degree, - )); - let (ck, vk) = trim(pp, degree); - let p = random(degree, rng); - let comm = UVKZGPCS::::commit(&ck, &p)?; - let point = E::Fr::random(rng); - let (proof, value) = UVKZGPCS::::open(&ck, &p, &point)?; - assert!( - UVKZGPCS::::verify(&vk, &comm, &point, &proof, &value)?, - "proof was incorrect for max_degree = {}, polynomial_degree = {}", - degree, - p.degree(), - ); - } - Ok(()) - } - - #[test] - fn end_to_end_test() { - end_to_end_test_template::().expect("test failed for Bn256"); - } - - fn batch_check_test_template() -> Result<(), NovaError> - where - E: MultiMillerLoop, - E::Fr: PrimeFieldBits, - E::G1: DlogGroup, - { - for _ in 0..10 { - let mut rng = &mut thread_rng(); - - let degree = rng.gen_range(2..20); - - let pp = Arc::new(UniversalKZGParam::::gen_srs_for_testing( - &mut rng, degree, - )); - let (ck, vk) = trim(pp, degree); - - let mut comms = Vec::new(); - let mut values = Vec::new(); - let mut points = Vec::new(); - let mut proofs = Vec::new(); - for _ in 0..10 { - let mut rng = rng.clone(); - let p = random(degree, &mut rng); - let comm = UVKZGPCS::::commit(&ck, &p)?; - let point = E::Fr::random(rng); - let (proof, value) = UVKZGPCS::::open(&ck, &p, &point)?; - - assert!(UVKZGPCS::::verify(&vk, &comm, &point, &proof, &value)?); - comms.push(comm); - values.push(value); - points.push(point); - proofs.push(proof); - } - assert!(UVKZGPCS::::batch_verify( - &vk, &comms, &points, &values, &proofs, &mut rng - )?); - } - Ok(()) - } - - #[test] - fn batch_check_test() { - batch_check_test_template::().expect("test failed for Bn256"); - } -} diff --git a/src/provider/non_hiding_zeromorph.rs b/src/provider/non_hiding_zeromorph.rs index cda8be714..f12c0acfe 100644 --- a/src/provider/non_hiding_zeromorph.rs +++ b/src/provider/non_hiding_zeromorph.rs @@ -6,13 +6,12 @@ use crate::{ digest::SimpleDigestible, errors::{NovaError, PCSError}, provider::{ - non_hiding_kzg::{ - KZGProverKey, KZGVerifierKey, UVKZGCommitment, UVKZGEvaluation, UVKZGProof, - UniversalKZGParam, UVKZGPCS, + kzg_commitment::{ + KZGCommitmentEngine, KZGProverKey, KZGVerifierKey, UVKZGCommitment, UniversalKZGParam, }, traits::DlogGroup, }, - spartan::polys::multilinear::MultilinearPolynomial, + spartan::polys::{multilinear::MultilinearPolynomial, univariate::UniPoly}, traits::{ commitment::Len, evaluation::EvaluationEngineTrait, Engine as NovaEngine, Group, TranscriptEngineTrait, TranscriptReprTrait, @@ -32,8 +31,106 @@ use serde::{Deserialize, Serialize}; use std::sync::Arc; use std::{borrow::Borrow, iter, marker::PhantomData}; -use crate::provider::kzg_commitment::KZGCommitmentEngine; -use crate::spartan::polys::univariate::UniPoly; +/// Polynomial Evaluation +#[derive(Debug, Clone, Eq, PartialEq, Default)] +pub struct UVKZGEvaluation(pub E::Fr); + +#[derive(Debug, Clone, Eq, PartialEq, Default)] + +/// Proofs +pub struct UVKZGProof { + /// proof + pub proof: E::G1Affine, +} + +/// Polynomial and its associated types +pub type UVKZGPoly = UniPoly; + +#[derive(Debug, Clone, Eq, PartialEq, Default)] +/// KZG Polynomial Commitment Scheme on univariate polynomial. +/// Note: this is non-hiding, which is why we will implement traits on this token struct, +/// as we expect to have several impls for the trait pegged on the same instance of a pairing::Engine. +#[allow(clippy::upper_case_acronyms)] +pub struct UVKZGPCS { + #[doc(hidden)] + phantom: PhantomData, +} + +impl UVKZGPCS +where + E::G1: DlogGroup, +{ + fn commit_offset( + prover_param: impl Borrow>, + poly: &UVKZGPoly, + offset: usize, + ) -> Result, NovaError> { + let prover_param = prover_param.borrow(); + + if poly.degree() > prover_param.powers_of_g().len() { + return Err(NovaError::PCSError(PCSError::LengthError)); + } + + let scalars = poly.coeffs.as_slice(); + let bases = prover_param.powers_of_g(); + + // We can avoid some scalar multiplications if 'scalars' contains a lot of leading zeroes using + // offset, that points where non-zero scalars start. + let C = ::vartime_multiscalar_mul( + &scalars[offset..], + &bases[offset..scalars.len()], + ); + + Ok(UVKZGCommitment(C.to_affine())) + } + + /// Generate a commitment for a polynomial + /// Note that the scheme is not hidding + pub fn commit( + prover_param: impl Borrow>, + poly: &UVKZGPoly, + ) -> Result, NovaError> { + let prover_param = prover_param.borrow(); + + if poly.degree() > prover_param.powers_of_g().len() { + return Err(NovaError::PCSError(PCSError::LengthError)); + } + let C = ::vartime_multiscalar_mul( + poly.coeffs.as_slice(), + &prover_param.powers_of_g()[..poly.coeffs.len()], + ); + Ok(UVKZGCommitment(C.to_affine())) + } + + /// On input a polynomial `p` and a point `point`, outputs a proof for the + /// same. + pub fn open( + prover_param: impl Borrow>, + polynomial: &UVKZGPoly, + point: &E::Fr, + ) -> Result<(UVKZGProof, UVKZGEvaluation), NovaError> { + let prover_param = prover_param.borrow(); + let divisor = UVKZGPoly { + coeffs: vec![-*point, E::Fr::ONE], + }; + let witness_polynomial = polynomial + .divide_with_q_and_r(&divisor) + .map(|(q, _r)| q) + .ok_or(NovaError::PCSError(PCSError::ZMError))?; + let proof = ::vartime_multiscalar_mul( + witness_polynomial.coeffs.as_slice(), + &prover_param.powers_of_g()[..witness_polynomial.coeffs.len()], + ); + let evaluation = UVKZGEvaluation(polynomial.evaluate(point)); + + Ok(( + UVKZGProof { + proof: proof.to_affine(), + }, + evaluation, + )) + } +} /// `ZMProverKey` is used to generate a proof #[derive(Clone, Debug, Eq, PartialEq)] @@ -72,7 +169,7 @@ fn trim_zeromorph( params: Arc>, max_degree: usize, ) -> (ZMProverKey, ZMVerifierKey) { - let (commit_pp, vp) = crate::provider::non_hiding_kzg::trim(params.clone(), max_degree); + let (commit_pp, vp) = UniversalKZGParam::trim(params.clone(), max_degree); let offset = params.max_degree() - max_degree; let s_offset_h = params.powers_of_h[offset]; let open_pp = KZGProverKey::new(params, offset, max_degree); @@ -519,13 +616,13 @@ mod test { use std::borrow::Borrow; use std::sync::Arc; - use super::quotients; + use super::{quotients, UVKZGPCS}; use crate::spartan::polys::univariate::UniPoly; use crate::{ errors::PCSError, provider::{ - non_hiding_kzg::{trim, KZGProverKey, UVKZGCommitment, UniversalKZGParam, UVKZGPCS}, + kzg_commitment::{KZGProverKey, UVKZGCommitment, UniversalKZGParam}, non_hiding_zeromorph::{batched_lifted_degree_quotient, eval_and_quotient_scalars, ZMPCS}, traits::DlogGroup, util::test_utils::prove_verify_from_num_vars, @@ -803,7 +900,7 @@ mod test { let pp = Arc::new(UniversalKZGParam::::gen_srs_for_testing( &mut rng, degree, )); - let (ck, _vk) = trim(pp, degree); + let (ck, _vk) = UniversalKZGParam::trim(pp, degree); let commitment_expected = UVKZGPCS::commit(&ck, &q_hat)?; let commitment_actual_1 = commit_filtered(&ck, &q_hat)?; diff --git a/src/provider/shplonk.rs b/src/provider/shplonk.rs index e0c75af86..c3c9fcbb6 100644 --- a/src/provider/shplonk.rs +++ b/src/provider/shplonk.rs @@ -1,6 +1,6 @@ //! Shplonk PCS use crate::provider::kzg_commitment::KZGCommitmentEngine; -use crate::provider::non_hiding_kzg::{trim, KZGProverKey, KZGVerifierKey, UniversalKZGParam}; +use crate::provider::kzg_commitment::{KZGProverKey, KZGVerifierKey, UniversalKZGParam}; use crate::provider::pedersen::Commitment; use crate::provider::traits::DlogGroup; use crate::provider::util::iterators::DoubleEndedIteratorExt; @@ -161,7 +161,7 @@ where fn setup(ck: Arc>) -> (KZGProverKey, KZGVerifierKey) { let len = ck.length() - 1; - trim(ck, len) + UniversalKZGParam::trim(ck, len) } fn prove(