From 3033b52d308d02235d66a574ba8146d4e436b9ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=C3=A7ois=20Garillot?= <4142+huitseeker@users.noreply.github.com> Date: Tue, 19 Dec 2023 09:43:39 -0500 Subject: [PATCH] Add MLKZG support (Nova forward port) (#172) * Support for multilinear KZG commitments (#269) * multilinear KZG PCS as a provider; builds * fix two tests * fix third test; cut duplicate code * Tidy up source code comments Signed-off-by: Greg Zaverucha * impl PairingGroup for bn256 * remove unneeded imports * simplify CommitmentKey * fix build; migrate G1Affine * fmt * checkpoint * migrate G2Affine and pairing * fix clippy; use unimplemented! * switch to affine form for compressed commitments * add a test with mlkzg * cargo fmt * cleanup * go back to compressed group * address clippy * rename * cleanup * add an alias * deduplicate * Revert "add an alias" This reverts commit 97cade6c8751deacbc8b5b0e0df1579e3baa1477. * Use an alias for PreprocessedGroupElements Signed-off-by: Greg Zaverucha * cargo fmt * update README.md --------- Signed-off-by: Greg Zaverucha Co-authored-by: Greg Zaverucha * refactor: clean up the needed scaffolding in MLKZG Summary: - THe MLKZG implementation re-implements some group traits, so as to give it maximum generality and depende maximally on the Nova traits. - However, the way in which it imports a pairing (using pairing::Engine) already implicitly constrains perfrectly usable group implementations to be available on the same types. This commit therefore removes the boilerplate and uses those external traits. - Finally, so as to mutualize part of the pairing implementation, this commit also leverages the MultiMillerLoop trait, a subtrait of `pairing::Engine`. - In sum, this commit only moves types - no actual data was harmed in its making. In detail: - Removed the `PairingGroup` trait and its related implementations from the `traits.rs` and `bn256_grumpkin.rs` files. - Simplified the imports from `halo2curves::bn256` in `bn256_grumpkin.rs` and removed unused types such as `pairing`, `G2Affine`, `G2Compressed`, `Gt`, and `G2`. - Deleted substantial amount of code associated with `G2` from `bn256_grumpkin.rs`. * make Minroot example generic over the supported curve cycles (#272) * make Minroot example generic over the supported curve cycles * upgrade version * refactor: Refactor and enhance point infinity handling in `to_transcript_bytes` - Enhanced the functionality of `to_transcript_bytes` method in `TranscriptReprTrait` for `Affine` in both `pasta.rs` and `traits.rs`. - Combined the x and y coordinates with the `is_infinity_byte` into a single byte stream for ease of handling. - Integrated additional checks for 'infinity' conditions to ensure accurate extractions of coordinate values. * refactor: Relocate multi-scalar multiplication module - Restructure the `provider` module by moving `msm` to the `util` subdirectory. * chore: Rename UV(KZG{ProverKey, VerifierKey}|UniversalKZGParam) -> \1 * refactor: Apply univariate polynomial evaluation - chore: move comment - fix: standardize power sequences computation - fix: parallelize several poly computations refactor: Refactor `EvaluationArgument` struct in mlkzg.rs - Renamed several fields in `EvaluationArgument` struct within `src/provider/mlkzg.rs` for increased clarity. - Adjusted the `prove` and `verify` methods in `src/provider/mlkzg.rs` to reflect these name changes. - Modified test code to align with the updates in the `EvaluationArgument` structure. --------- Signed-off-by: Greg Zaverucha Co-authored-by: Srinath Setty Co-authored-by: Greg Zaverucha --- examples/minroot.rs | 5 ++-- src/lib.rs | 15 ++++++----- src/provider/bn256_grumpkin.rs | 4 ++- src/provider/kzg_commitment.rs | 6 ++--- src/provider/mod.rs | 14 ++++++++++ src/provider/non_hiding_kzg.rs | 38 ++++++++++++++-------------- src/provider/non_hiding_zeromorph.rs | 22 ++++++++-------- src/provider/util/mod.rs | 3 ++- src/spartan/mod.rs | 9 +++---- 9 files changed, 66 insertions(+), 50 deletions(-) diff --git a/examples/minroot.rs b/examples/minroot.rs index 97f112659..de9539324 100644 --- a/examples/minroot.rs +++ b/examples/minroot.rs @@ -4,8 +4,9 @@ use bellpepper_core::{num::AllocatedNum, ConstraintSystem, SynthesisError}; use ff::Field; use flate2::{write::ZlibEncoder, Compression}; +use halo2curves::bn256::Bn256; use nova_snark::{ - provider::{hyperkzg::Bn256EngineKZG, GrumpkinEngine}, + provider::{Bn256EngineKZG, GrumpkinEngine}, traits::{ circuit::{StepCircuit, TrivialCircuit}, snark::RelaxedR1CSSNARKTrait, @@ -18,7 +19,7 @@ use std::time::Instant; type E1 = Bn256EngineKZG; type E2 = GrumpkinEngine; -type EE1 = nova_snark::provider::hyperkzg::EvaluationEngine; +type EE1 = nova_snark::provider::hyperkzg::EvaluationEngine; type EE2 = nova_snark::provider::ipa_pc::EvaluationEngine; type S1 = nova_snark::spartan::snark::RelaxedR1CSSNARK; // non-preprocessing SNARK type S2 = nova_snark::spartan::snark::RelaxedR1CSSNARK; // non-preprocessing SNARK diff --git a/src/lib.rs b/src/lib.rs index c62cb51bc..2b185573c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -846,7 +846,7 @@ mod tests { use super::*; use crate::{ provider::{ - hyperkzg::Bn256EngineKZG, traits::DlogGroup, Bn256Engine, Bn256EngineZM, + Bn256EngineKZG, traits::DlogGroup, Bn256Engine, Bn256EngineZM, GrumpkinEngine, PallasEngine, Secp256k1Engine, Secq256k1Engine, VestaEngine, non_hiding_zeromorph::ZMPCS, }, traits::{circuit::TrivialCircuit, evaluation::EvaluationEngineTrait, snark::default_ck_hint}, @@ -1224,18 +1224,19 @@ mod tests { test_ivc_nontrivial_with_compression_with::, EE<_>>(); test_ivc_nontrivial_with_compression_with::, EE<_>>(); - test_ivc_nontrivial_with_spark_compression_with::< - Bn256EngineKZG, - GrumpkinEngine, - provider::hyperkzg::EvaluationEngine<_>, - EE<_>, - >(); test_ivc_nontrivial_with_compression_with::< Bn256EngineZM, GrumpkinEngine, ZMPCS, EE<_>, >(); + + test_ivc_nontrivial_with_spark_compression_with::< + Bn256EngineKZG, + GrumpkinEngine, + provider::hyperkzg::EvaluationEngine, + EE<_>, + >(); } fn test_ivc_nontrivial_with_spark_compression_with() diff --git a/src/provider/bn256_grumpkin.rs b/src/provider/bn256_grumpkin.rs index d15779d63..f4f7eb6ac 100644 --- a/src/provider/bn256_grumpkin.rs +++ b/src/provider/bn256_grumpkin.rs @@ -1,7 +1,7 @@ //! This module implements the Nova traits for `bn256::Point`, `bn256::Scalar`, `grumpkin::Point`, `grumpkin::Scalar`. use crate::{ impl_traits, - provider::traits::{CompressedGroup, DlogGroup, PairingGroup}, + provider::traits::{CompressedGroup, DlogGroup}, traits::{Group, PrimeFieldExt, TranscriptReprTrait}, }; use digest::{ExtendableOutput, Update}; @@ -23,6 +23,8 @@ use rayon::prelude::*; use sha3::Shake256; use std::io::Read; +use super::traits::PairingGroup; + /// Re-exports that give access to the standard aliases used in the code base, for bn256 pub mod bn256 { pub use halo2curves::bn256::{Fq as Base, Fr as Scalar, G1Affine as Affine, G1 as Point}; diff --git a/src/provider/kzg_commitment.rs b/src/provider/kzg_commitment.rs index 37381e62b..360ec1edd 100644 --- a/src/provider/kzg_commitment.rs +++ b/src/provider/kzg_commitment.rs @@ -16,7 +16,7 @@ use crate::traits::{ }; use crate::provider::{ - non_hiding_kzg::{UVKZGCommitment, UVUniversalKZGParam}, + non_hiding_kzg::{UVKZGCommitment, UniversalKZGParam}, pedersen::Commitment, traits::DlogGroup, }; @@ -35,7 +35,7 @@ where E::G2Affine: Serialize + for<'de> Deserialize<'de>, E::Fr: PrimeFieldBits, // TODO due to use of gen_srs_for_testing, make optional { - type CommitmentKey = UVUniversalKZGParam; + type CommitmentKey = UniversalKZGParam; type Commitment = Commitment; fn setup(label: &'static [u8], n: usize) -> Self::CommitmentKey { @@ -44,7 +44,7 @@ where let len = label.len().min(32); bytes[..len].copy_from_slice(&label[..len]); let rng = &mut StdRng::from_seed(bytes); - UVUniversalKZGParam::gen_srs_for_testing(rng, n.next_power_of_two()) + UniversalKZGParam::gen_srs_for_testing(rng, n.next_power_of_two()) } fn commit(ck: &Self::CommitmentKey, v: &[::Scalar]) -> Self::Commitment { diff --git a/src/provider/mod.rs b/src/provider/mod.rs index 71d96b9d7..faa37787a 100644 --- a/src/provider/mod.rs +++ b/src/provider/mod.rs @@ -76,6 +76,19 @@ impl Engine for Bn256EngineZM { type TE = Keccak256Transcript; type CE = KZGCommitmentEngine; } +/// An implementation of Nova traits with multilinear KZG over the BN256 curve +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct Bn256EngineKZG; + +impl Engine for Bn256EngineKZG { + type Base = bn256::Base; + type Scalar = bn256::Scalar; + type GE = bn256::Point; + type RO = PoseidonRO; + type ROCircuit = PoseidonROCircuit; + type TE = Keccak256Transcript; + type CE = KZGCommitmentEngine; +} /// An implementation of the Nova `Engine` trait with Secp256k1 curve and Pedersen commitment scheme #[derive(Clone, Copy, Debug, Eq, PartialEq)] @@ -136,6 +149,7 @@ impl Engine for VestaEngine { #[cfg(test)] mod tests { use crate::provider::{bn256_grumpkin::bn256, secp_secq::secp256k1, traits::DlogGroup}; + use digest::{ExtendableOutput, Update}; use group::Curve; use halo2curves::CurveExt; diff --git a/src/provider/non_hiding_kzg.rs b/src/provider/non_hiding_kzg.rs index a2aaf12b7..3c4ceb113 100644 --- a/src/provider/non_hiding_kzg.rs +++ b/src/provider/non_hiding_kzg.rs @@ -19,7 +19,7 @@ use crate::{ serialize = "E::G1Affine: Serialize, E::G2Affine: Serialize", deserialize = "E::G1Affine: Deserialize<'de>, E::G2Affine: Deserialize<'de>" ))] -pub struct UVUniversalKZGParam { +pub struct UniversalKZGParam { /// Group elements of the form `{ β^i G }`, where `i` ranges from 0 to /// `degree`. pub powers_of_g: Vec, @@ -28,14 +28,14 @@ pub struct UVUniversalKZGParam { pub powers_of_h: Vec, } -impl PartialEq for UVUniversalKZGParam { - fn eq(&self, other: &UVUniversalKZGParam) -> bool { +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 UVUniversalKZGParam { +impl Len for UniversalKZGParam { fn length(&self) -> usize { self.powers_of_g.len() } @@ -47,7 +47,7 @@ impl Len for UVUniversalKZGParam { serialize = "E::G1Affine: Serialize", deserialize = "E::G1Affine: Deserialize<'de>" ))] -pub struct UVKZGProverKey { +pub struct KZGProverKey { /// generators pub powers_of_g: Vec, } @@ -59,7 +59,7 @@ pub struct UVKZGProverKey { serialize = "E::G1Affine: Serialize, E::G2Affine: Serialize", deserialize = "E::G1Affine: Deserialize<'de>, E::G2Affine: Deserialize<'de>" ))] -pub struct UVKZGVerifierKey { +pub struct KZGVerifierKey { /// The generator of G1. pub g: E::G1Affine, /// The generator of G2. @@ -68,7 +68,7 @@ pub struct UVKZGVerifierKey { pub beta_h: E::G2Affine, } -impl UVUniversalKZGParam { +impl UniversalKZGParam { /// Returns the maximum supported degree pub fn max_degree(&self) -> usize { self.powers_of_g.len() @@ -78,21 +78,21 @@ impl UVUniversalKZGParam { /// /// # Panics /// if `supported_size` is greater than `self.max_degree()` - pub fn extract_prover_key(&self, supported_size: usize) -> UVKZGProverKey { + pub fn extract_prover_key(&self, supported_size: usize) -> KZGProverKey { let powers_of_g = self.powers_of_g[..=supported_size].to_vec(); - UVKZGProverKey { powers_of_g } + KZGProverKey { powers_of_g } } /// Returns the verifier parameters /// /// # Panics /// If self.prover_params is empty. - pub fn extract_verifier_key(&self, supported_size: usize) -> UVKZGVerifierKey { + 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()" ); - UVKZGVerifierKey { + KZGVerifierKey { g: self.powers_of_g[0], h: self.powers_of_h[0], beta_h: self.powers_of_h[1], @@ -106,11 +106,11 @@ impl UVUniversalKZGParam { /// /// # Panics /// If `supported_size` is greater than `self.max_degree()`, or `self.max_degree()` is zero. - pub fn trim(&self, supported_size: usize) -> (UVKZGProverKey, UVKZGVerifierKey) { + pub fn trim(&self, supported_size: usize) -> (KZGProverKey, KZGVerifierKey) { let powers_of_g = self.powers_of_g[..=supported_size].to_vec(); - let pk = UVKZGProverKey { powers_of_g }; - let vk = UVKZGVerifierKey { + 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], @@ -119,7 +119,7 @@ impl UVUniversalKZGParam { } } -impl UVUniversalKZGParam +impl UniversalKZGParam where E::Fr: PrimeFieldBits, { @@ -229,7 +229,7 @@ where /// Generate a commitment for a polynomial /// Note that the scheme is not hidding pub fn commit( - prover_param: impl Borrow>, + prover_param: impl Borrow>, poly: &UVKZGPoly, ) -> Result, NovaError> { let prover_param = prover_param.borrow(); @@ -247,7 +247,7 @@ where /// On input a polynomial `p` and a point `point`, outputs a proof for the /// same. pub fn open( - prover_param: impl Borrow>, + prover_param: impl Borrow>, polynomial: &UVKZGPoly, point: &E::Fr, ) -> Result<(UVKZGProof, UVKZGEvaluation), NovaError> { @@ -277,7 +277,7 @@ where /// committed inside `comm`. #[allow(dead_code)] pub fn verify( - verifier_param: impl Borrow>, + verifier_param: impl Borrow>, commitment: &UVKZGCommitment, point: &E::Fr, proof: &UVKZGProof, @@ -324,7 +324,7 @@ mod tests { let mut rng = &mut thread_rng(); let degree = rng.gen_range(2..20); - let pp = UVUniversalKZGParam::::gen_srs_for_testing(&mut rng, degree); + 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)?; diff --git a/src/provider/non_hiding_zeromorph.rs b/src/provider/non_hiding_zeromorph.rs index b6c10d9a4..dd9b56ec0 100644 --- a/src/provider/non_hiding_zeromorph.rs +++ b/src/provider/non_hiding_zeromorph.rs @@ -6,8 +6,8 @@ use crate::{ errors::{NovaError, PCSError}, provider::{ non_hiding_kzg::{ - UVKZGCommitment, UVKZGEvaluation, UVKZGPoly, UVKZGProof, UVKZGProverKey, UVKZGVerifierKey, - UVUniversalKZGParam, UVKZGPCS, + KZGProverKey, KZGVerifierKey, UVKZGCommitment, UVKZGEvaluation, UVKZGPoly, UVKZGProof, + UniversalKZGParam, UVKZGPCS, }, traits::DlogGroup, }, @@ -39,8 +39,8 @@ use crate::provider::kzg_commitment::KZGCommitmentEngine; deserialize = "E::G1Affine: Deserialize<'de>" ))] pub struct ZMProverKey { - commit_pp: UVKZGProverKey, - open_pp: UVKZGProverKey, + commit_pp: KZGProverKey, + open_pp: KZGProverKey, } /// `ZMVerifierKey` is used to check evaluation proofs for a given @@ -51,7 +51,7 @@ pub struct ZMProverKey { deserialize = "E::G1Affine: Deserialize<'de>, E::G2Affine: Deserialize<'de>" ))] pub struct ZMVerifierKey { - vp: UVKZGVerifierKey, + vp: KZGVerifierKey, s_offset_h: E::G2Affine, } @@ -66,14 +66,14 @@ pub struct ZMVerifierKey { // TODO: important, we need a better way to handle that the commitment key should be 2^max_degree sized, // see the runtime error in commit() below pub fn trim( - params: &UVUniversalKZGParam, + params: &UniversalKZGParam, max_degree: usize, ) -> (ZMProverKey, ZMVerifierKey) { let (commit_pp, vp) = params.trim(max_degree); let offset = params.powers_of_g.len() - max_degree; let open_pp = { let offset_powers_of_g1 = params.powers_of_g[offset..].to_vec(); - UVKZGProverKey { + KZGProverKey { powers_of_g: offset_powers_of_g1, } }; @@ -470,12 +470,12 @@ where type EvaluationArgument = ZMProof; - fn setup(ck: &UVUniversalKZGParam) -> (Self::ProverKey, Self::VerifierKey) { + fn setup(ck: &UniversalKZGParam) -> (Self::ProverKey, Self::VerifierKey) { trim(ck, ck.length() - 1) } fn prove( - _ck: &UVUniversalKZGParam, + _ck: &UniversalKZGParam, pk: &Self::ProverKey, transcript: &mut NE::TE, comm: &Commitment, @@ -525,7 +525,7 @@ mod test { use crate::{ provider::{ keccak::Keccak256Transcript, - non_hiding_kzg::{UVKZGPoly, UVUniversalKZGParam}, + non_hiding_kzg::{UVKZGPoly, UniversalKZGParam}, non_hiding_zeromorph::{ batched_lifted_degree_quotient, eval_and_quotient_scalars, trim, ZMEvaluation, ZMPCS, }, @@ -545,7 +545,7 @@ mod test { let max_vars = 16; let mut rng = thread_rng(); let max_poly_size = 1 << (max_vars + 1); - let universal_setup = UVUniversalKZGParam::::gen_srs_for_testing(&mut rng, max_poly_size); + let universal_setup = UniversalKZGParam::::gen_srs_for_testing(&mut rng, max_poly_size); for num_vars in 3..max_vars { // Setup diff --git a/src/provider/util/mod.rs b/src/provider/util/mod.rs index 43a544123..40a0443fa 100644 --- a/src/provider/util/mod.rs +++ b/src/provider/util/mod.rs @@ -1,2 +1,3 @@ /// Utilities for provider module -pub(crate) mod fb_msm; +pub(in crate::provider) mod fb_msm; +pub(in crate::provider) mod msm; diff --git a/src/spartan/mod.rs b/src/spartan/mod.rs index 1189e531e..129955813 100644 --- a/src/spartan/mod.rs +++ b/src/spartan/mod.rs @@ -27,12 +27,9 @@ use rayon::{iter::IntoParallelRefIterator, prelude::*}; // Creates a vector of the first `n` powers of `s`. fn powers(s: &E::Scalar, n: usize) -> Vec { assert!(n >= 1); - let mut powers = Vec::with_capacity(n); - powers.push(E::Scalar::ONE); - for i in 1..n { - powers.push(powers[i - 1] * s); - } - powers + std::iter::successors(Some(E::Scalar::ONE), |&x| Some(x * s)) + .take(n) + .collect() } /// A type that holds a witness to a polynomial evaluation instance