From 23b44c5f6e5b76bb8a85cf14e6dff7b2c636a1d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Garillot?= Date: Fri, 26 Jan 2024 13:30:49 -0500 Subject: [PATCH] refactor: Minimize distraction to Nova module organization - 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 | 37 ++- src/provider/kzg_commitment.rs | 163 ++++++++++++- src/provider/mod.rs | 2 +- src/provider/non_hiding_kzg.rs | 342 --------------------------- src/provider/non_hiding_zeromorph.rs | 154 ++++++------ 5 files changed, 264 insertions(+), 434 deletions(-) delete mode 100644 src/provider/non_hiding_kzg.rs diff --git a/src/provider/hyperkzg.rs b/src/provider/hyperkzg.rs index 8e9a4689..0d703fbb 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::{KZGProverKey, KZGVerifierKey, UniversalKZGParam}, + kzg_commitment::{KZGCommitmentEngine, KZGProverKey, KZGVerifierKey, UniversalKZGParam}, pedersen::Commitment, traits::DlogGroup, }, @@ -439,35 +438,51 @@ mod tests { let n = 4; let ck: CommitmentKey = as CommitmentEngineTrait>::setup(b"test", n); - let (pk, _vk): (KZGProverKey, KZGVerifierKey) = EvaluationEngine::::setup(&ck); + let (pk, vk): (KZGProverKey, KZGVerifierKey) = EvaluationEngine::::setup(&ck); // 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 405808a6..75d24953 100644 --- a/src/provider/kzg_commitment.rs +++ b/src/provider/kzg_commitment.rs @@ -3,22 +3,169 @@ use std::marker::PhantomData; -use group::{prime::PrimeCurveAffine, Curve}; +use ff::Field; +use group::{prime::PrimeCurveAffine, Curve, Group as _}; use halo2curves::pairing::Engine; use rand::rngs::StdRng; -use rand_core::SeedableRng; +use rand_core::{CryptoRng, RngCore, SeedableRng}; use serde::{Deserialize, Serialize}; use crate::traits::{ commitment::{CommitmentEngineTrait, Len}, - Engine as NovaEngine, Group, + Engine as NovaEngine, Group, TranscriptReprTrait, }; -use crate::provider::{ - non_hiding_kzg::{UVKZGCommitment, UniversalKZGParam}, - pedersen::Commitment, - traits::DlogGroup, -}; +use crate::provider::{pedersen::Commitment, traits::DlogGroup}; + +/// `UniversalParams` are the universal parameters for the KZG10 scheme. +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +#[serde(bound( + serialize = "E::G1Affine: Serialize, E::G2Affine: Serialize", + deserialize = "E::G1Affine: Deserialize<'de>, E::G2Affine: Deserialize<'de>" +))] +pub struct UniversalKZGParam { + /// Group elements of the form `{ β^i G }`, where `i` ranges from 0 to + /// `degree`. + pub powers_of_g: Vec, + /// Group elements of the form `{ β^i H }`, where `i` ranges from 0 to + /// `degree`. + pub powers_of_h: Vec, +} + +// 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, Serialize, Deserialize)] +#[serde(bound( + serialize = "E::G1Affine: Serialize", + deserialize = "E::G1Affine: Deserialize<'de>" +))] +pub struct KZGProverKey { + /// generators + pub powers_of_g: Vec, +} + +/// `UVKZGVerifierKey` is used to check evaluation proofs for a given +/// commitment. +#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] +#[serde(bound( + serialize = "E::G1Affine: Serialize, E::G2Affine: Serialize", + deserialize = "E::G1Affine: Deserialize<'de>, E::G2Affine: Deserialize<'de>" +))] +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 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(&self, supported_size: usize) -> (KZGProverKey, KZGVerifierKey) { + let powers_of_g = self.powers_of_g[..=supported_size].to_vec(); + + let pk = KZGProverKey { powers_of_g }; + let vk = KZGVerifierKey { + g: self.powers_of_g[0], + h: self.powers_of_h[0], + beta_h: self.powers_of_h[1], + }; + (pk, vk) + } +} + +impl UniversalKZGParam { + /// 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 (powers_of_g_projective, powers_of_h_projective) = rayon::join( + || { + (0..=max_degree) + .scan(g, |acc, _| { + let val = *acc; + *acc *= beta; + Some(val) + }) + .collect::>() + }, + || { + (0..=max_degree) + .scan(h, |acc, _| { + let val = *acc; + *acc *= beta; + Some(val) + }) + .collect::>() + }, + ); + + 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, Serialize, Deserialize)] diff --git a/src/provider/mod.rs b/src/provider/mod.rs index 36aa5547..5cd3e60a 100644 --- a/src/provider/mod.rs +++ b/src/provider/mod.rs @@ -14,7 +14,7 @@ pub(crate) mod secp_secq; pub(crate) mod traits; // a non-hiding variant of {kzg, zeromorph} pub(crate) mod kzg_commitment; -pub(crate) mod non_hiding_kzg; + #[cfg(test)] pub(crate) mod test_utils; diff --git a/src/provider/non_hiding_kzg.rs b/src/provider/non_hiding_kzg.rs deleted file mode 100644 index 67f4d640..00000000 --- a/src/provider/non_hiding_kzg.rs +++ /dev/null @@ -1,342 +0,0 @@ -//! Non-hiding variant of KZG10 scheme for univariate polynomials. -use ff::Field; -use group::{prime::PrimeCurveAffine, Curve, Group as _}; -use halo2curves::pairing::{Engine, MillerLoopResult, MultiMillerLoop}; -use rand_core::{CryptoRng, RngCore}; -use serde::{Deserialize, Serialize}; -use std::{borrow::Borrow, marker::PhantomData, ops::Mul}; - -use crate::{ - errors::{NovaError, PCSError}, - provider::traits::DlogGroup, - traits::{commitment::Len, Group, TranscriptReprTrait}, -}; - -/// `UniversalParams` are the universal parameters for the KZG10 scheme. -#[derive(Debug, Clone, Eq, Serialize, Deserialize)] -#[serde(bound( - serialize = "E::G1Affine: Serialize, E::G2Affine: Serialize", - deserialize = "E::G1Affine: Deserialize<'de>, E::G2Affine: Deserialize<'de>" -))] -pub struct UniversalKZGParam { - /// Group elements of the form `{ β^i G }`, where `i` ranges from 0 to - /// `degree`. - pub powers_of_g: Vec, - /// Group elements of the form `{ β^i H }`, where `i` ranges from 0 to - /// `degree`. - pub powers_of_h: Vec, -} - -impl PartialEq for UniversalKZGParam { - fn eq(&self, other: &UniversalKZGParam) -> 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, Serialize, Deserialize)] -#[serde(bound( - serialize = "E::G1Affine: Serialize", - deserialize = "E::G1Affine: Deserialize<'de>" -))] -pub struct KZGProverKey { - /// generators - pub powers_of_g: Vec, -} - -/// `UVKZGVerifierKey` is used to check evaluation proofs for a given -/// commitment. -#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)] -#[serde(bound( - serialize = "E::G1Affine: Serialize, E::G2Affine: Serialize", - deserialize = "E::G1Affine: Deserialize<'de>, E::G2Affine: Deserialize<'de>" -))] -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 UniversalKZGParam { - /// Returns the maximum supported degree - pub fn max_degree(&self) -> usize { - self.powers_of_g.len() - } - - /// Returns the prover parameters - /// - /// # Panics - /// if `supported_size` is greater than `self.max_degree()` - pub fn extract_prover_key(&self, supported_size: usize) -> KZGProverKey { - let powers_of_g = self.powers_of_g[..=supported_size].to_vec(); - KZGProverKey { powers_of_g } - } - - /// Returns the verifier parameters - /// - /// # Panics - /// If self.prover_params is empty. - pub fn extract_verifier_key(&self, supported_size: usize) -> KZGVerifierKey { - assert!( - self.powers_of_g.len() >= supported_size, - "supported_size is greater than self.max_degree()" - ); - KZGVerifierKey { - g: self.powers_of_g[0], - h: self.powers_of_h[0], - beta_h: self.powers_of_h[1], - } - } - - /// 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(&self, supported_size: usize) -> (KZGProverKey, KZGVerifierKey) { - let powers_of_g = self.powers_of_g[..=supported_size].to_vec(); - - let pk = KZGProverKey { powers_of_g }; - let vk = KZGVerifierKey { - g: self.powers_of_g[0], - h: self.powers_of_h[0], - beta_h: self.powers_of_h[1], - }; - (pk, vk) - } -} - -impl UniversalKZGParam { - /// 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 (powers_of_g_projective, powers_of_h_projective) = rayon::join( - || { - (0..=max_degree) - .scan(g, |acc, _| { - let val = *acc; - *acc *= beta; - Some(val) - }) - .collect::>() - }, - || { - (0..=max_degree) - .scan(h, |acc, _| { - let val = *acc; - *acc *= beta; - Some(val) - }) - .collect::>() - }, - ); - - 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, 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 = crate::spartan::polys::univariate::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, -{ - /// 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.as_slice()[..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.as_slice()[..witness_polynomial.coeffs.len()], - ); - let evaluation = UVKZGEvaluation(polynomial.evaluate(point)); - - Ok(( - UVKZGProof { - proof: proof.to_affine(), - }, - evaluation, - )) - } - - /// Verifies that `value` is the evaluation at `x` of the polynomial - /// committed inside `comm`. - #[allow(dead_code)] - pub 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()), - ]; - 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()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::spartan::polys::univariate::UniPoly; - use ff::PrimeField; - 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, - { - for _ in 0..100 { - let mut rng = &mut thread_rng(); - let degree = rng.gen_range(2..20); - - let pp = UniversalKZGParam::::gen_srs_for_testing(&mut rng, degree); - let (ck, vk) = pp.trim(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"); - } -} diff --git a/src/provider/non_hiding_zeromorph.rs b/src/provider/non_hiding_zeromorph.rs index f5bf2c03..45d8ee1a 100644 --- a/src/provider/non_hiding_zeromorph.rs +++ b/src/provider/non_hiding_zeromorph.rs @@ -5,9 +5,8 @@ use crate::{ errors::{NovaError, PCSError}, provider::{ - non_hiding_kzg::{ - KZGProverKey, KZGVerifierKey, UVKZGCommitment, UVKZGEvaluation, UVKZGPoly, UVKZGProof, - UniversalKZGParam, UVKZGPCS, + kzg_commitment::{ + KZGCommitmentEngine, KZGProverKey, KZGVerifierKey, UVKZGCommitment, UniversalKZGParam, }, traits::DlogGroup, }, @@ -30,7 +29,82 @@ use ref_cast::RefCast; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use std::{borrow::Borrow, iter, marker::PhantomData}; -use crate::provider::kzg_commitment::KZGCommitmentEngine; +/// 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 = crate::spartan::polys::univariate::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, +{ + /// 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.as_slice()[..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.as_slice()[..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, Serialize, Deserialize)] @@ -509,90 +583,26 @@ where #[cfg(test)] mod test { - use std::iter; - use ff::{Field, PrimeField, PrimeFieldBits}; + use ff::{Field, PrimeField}; use halo2curves::bn256::Bn256; use halo2curves::bn256::Fr as Scalar; - use halo2curves::pairing::MultiMillerLoop; use itertools::Itertools as _; - use rand::thread_rng; use rand_chacha::ChaCha20Rng; use rand_core::SeedableRng; use super::quotients; use crate::{ provider::{ - keccak::Keccak256Transcript, - non_hiding_kzg::{UVKZGPoly, UniversalKZGParam}, + non_hiding_zeromorph::UVKZGPoly, non_hiding_zeromorph::{ - batched_lifted_degree_quotient, eval_and_quotient_scalars, trim, ZMEvaluation, ZMPCS, + batched_lifted_degree_quotient, eval_and_quotient_scalars, ZMPCS, }, - test_utils::prove_verify_from_num_vars, - traits::DlogGroup, - Bn256Engine, Bn256EngineZM, + test_utils::prove_verify_from_num_vars, Bn256EngineZM, }, spartan::polys::multilinear::MultilinearPolynomial, - traits::{Engine as NovaEngine, Group, TranscriptEngineTrait, TranscriptReprTrait}, }; - fn commit_open_verify_with>() - where - E::G1: DlogGroup, - ::Base: TranscriptReprTrait, // Note: due to the move of the bound TranscriptReprTrait on G::Base from Group to Engine - E::Fr: PrimeFieldBits, - { - let max_vars = 16; - let mut rng = thread_rng(); - let max_poly_size = 1 << (max_vars + 1); - let universal_setup = UniversalKZGParam::::gen_srs_for_testing(&mut rng, max_poly_size); - - for num_vars in 3..max_vars { - // Setup - let (pp, vk) = { - let poly_size = 1 << (num_vars + 1); - - trim(&universal_setup, poly_size) - }; - - // Commit and open - let mut transcript = Keccak256Transcript::::new(b"test"); - let poly = MultilinearPolynomial::::random(num_vars, &mut thread_rng()); - let comm = ZMPCS::::commit(&pp, &poly).unwrap(); - let point = iter::from_fn(|| transcript.squeeze(b"pt").ok()) - .take(num_vars) - .collect::>(); - let eval = ZMEvaluation(poly.evaluate(&point)); - - let mut transcript_prover = Keccak256Transcript::::new(b"test"); - let proof = ZMPCS::open(&pp, &comm, &poly, &point, &eval, &mut transcript_prover).unwrap(); - - // Verify - let mut transcript_verifier = Keccak256Transcript::::new(b"test"); - let result = ZMPCS::verify( - &vk, - &mut transcript_verifier, - &comm, - point.as_slice(), - &eval, - &proof, - ); - - // check both random oracles are synced, as expected - assert_eq!( - transcript_prover.squeeze(b"test"), - transcript_verifier.squeeze(b"test") - ); - - result.unwrap(); - } - } - - #[test] - fn test_commit_open_verify() { - commit_open_verify_with::(); - } - #[test] fn test_multiple_polynomial_size() { for num_vars in [4, 5, 6] {