Skip to content

Commit

Permalink
Add MLKZG support (Nova forward port) (microsoft#172)
Browse files Browse the repository at this point in the history
* Support for multilinear KZG commitments (microsoft#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 <gregz@microsoft.com>

* 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 97cade6.

* Use an alias for PreprocessedGroupElements

Signed-off-by: Greg Zaverucha <gregz@microsoft.com>

* cargo fmt

* update README.md

---------

Signed-off-by: Greg Zaverucha <gregz@microsoft.com>
Co-authored-by: Greg Zaverucha <gregz@microsoft.com>

* 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 (microsoft#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 <gregz@microsoft.com>
Co-authored-by: Srinath Setty <srinath@microsoft.com>
Co-authored-by: Greg Zaverucha <gregz@microsoft.com>
  • Loading branch information
3 people committed Jan 26, 2024
1 parent eea9f61 commit 3af23b6
Show file tree
Hide file tree
Showing 9 changed files with 66 additions and 50 deletions.
5 changes: 3 additions & 2 deletions examples/minroot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -18,7 +19,7 @@ use std::time::Instant;

type E1 = Bn256EngineKZG;
type E2 = GrumpkinEngine;
type EE1 = nova_snark::provider::hyperkzg::EvaluationEngine<E1>;
type EE1 = nova_snark::provider::hyperkzg::EvaluationEngine<Bn256, E1>;
type EE2 = nova_snark::provider::ipa_pc::EvaluationEngine<E2>;
type S1 = nova_snark::spartan::snark::RelaxedR1CSSNARK<E1, EE1>; // non-preprocessing SNARK
type S2 = nova_snark::spartan::snark::RelaxedR1CSSNARK<E2, EE2>; // non-preprocessing SNARK
Expand Down
15 changes: 8 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -1224,18 +1224,19 @@ mod tests {
test_ivc_nontrivial_with_compression_with::<Bn256Engine, GrumpkinEngine, EE<_>, EE<_>>();
test_ivc_nontrivial_with_compression_with::<Secp256k1Engine, Secq256k1Engine, EE<_>, EE<_>>();

test_ivc_nontrivial_with_spark_compression_with::<
Bn256EngineKZG,
GrumpkinEngine,
provider::hyperkzg::EvaluationEngine<_>,
EE<_>,
>();
test_ivc_nontrivial_with_compression_with::<
Bn256EngineZM,
GrumpkinEngine,
ZMPCS<Bn256, _>,
EE<_>,
>();

test_ivc_nontrivial_with_spark_compression_with::<
Bn256EngineKZG,
GrumpkinEngine,
provider::hyperkzg::EvaluationEngine<Bn256, _>,
EE<_>,
>();
}

fn test_ivc_nontrivial_with_spark_compression_with<E1, E2, EE1, EE2>()
Expand Down
4 changes: 3 additions & 1 deletion src/provider/bn256_grumpkin.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand All @@ -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};
Expand Down
6 changes: 3 additions & 3 deletions src/provider/kzg_commitment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use crate::traits::{
};

use crate::provider::{
non_hiding_kzg::{UVKZGCommitment, UVUniversalKZGParam},
non_hiding_kzg::{UVKZGCommitment, UniversalKZGParam},
pedersen::Commitment,
traits::DlogGroup,
};
Expand All @@ -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<E>;
type CommitmentKey = UniversalKZGParam<E>;
type Commitment = Commitment<NE>;

fn setup(label: &'static [u8], n: usize) -> Self::CommitmentKey {
Expand All @@ -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: &[<E::G1 as Group>::Scalar]) -> Self::Commitment {
Expand Down
14 changes: 14 additions & 0 deletions src/provider/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,19 @@ impl Engine for Bn256EngineZM {
type TE = Keccak256Transcript<Self>;
type CE = KZGCommitmentEngine<Bn256>;
}
/// 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<Self::Base, Self::Scalar>;
type ROCircuit = PoseidonROCircuit<Self::Base>;
type TE = Keccak256Transcript<Self>;
type CE = KZGCommitmentEngine<Bn256>;
}

/// An implementation of the Nova `Engine` trait with Secp256k1 curve and Pedersen commitment scheme
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
Expand Down Expand Up @@ -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;
Expand Down
38 changes: 19 additions & 19 deletions src/provider/non_hiding_kzg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<E: Engine> {
pub struct UniversalKZGParam<E: Engine> {
/// Group elements of the form `{ β^i G }`, where `i` ranges from 0 to
/// `degree`.
pub powers_of_g: Vec<E::G1Affine>,
Expand All @@ -28,14 +28,14 @@ pub struct UVUniversalKZGParam<E: Engine> {
pub powers_of_h: Vec<E::G2Affine>,
}

impl<E: Engine> PartialEq for UVUniversalKZGParam<E> {
fn eq(&self, other: &UVUniversalKZGParam<E>) -> bool {
impl<E: Engine> PartialEq for UniversalKZGParam<E> {
fn eq(&self, other: &UniversalKZGParam<E>) -> 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<E: Engine> Len for UVUniversalKZGParam<E> {
impl<E: Engine> Len for UniversalKZGParam<E> {
fn length(&self) -> usize {
self.powers_of_g.len()
}
Expand All @@ -47,7 +47,7 @@ impl<E: Engine> Len for UVUniversalKZGParam<E> {
serialize = "E::G1Affine: Serialize",
deserialize = "E::G1Affine: Deserialize<'de>"
))]
pub struct UVKZGProverKey<E: Engine> {
pub struct KZGProverKey<E: Engine> {
/// generators
pub powers_of_g: Vec<E::G1Affine>,
}
Expand All @@ -59,7 +59,7 @@ pub struct UVKZGProverKey<E: Engine> {
serialize = "E::G1Affine: Serialize, E::G2Affine: Serialize",
deserialize = "E::G1Affine: Deserialize<'de>, E::G2Affine: Deserialize<'de>"
))]
pub struct UVKZGVerifierKey<E: Engine> {
pub struct KZGVerifierKey<E: Engine> {
/// The generator of G1.
pub g: E::G1Affine,
/// The generator of G2.
Expand All @@ -68,7 +68,7 @@ pub struct UVKZGVerifierKey<E: Engine> {
pub beta_h: E::G2Affine,
}

impl<E: Engine> UVUniversalKZGParam<E> {
impl<E: Engine> UniversalKZGParam<E> {
/// Returns the maximum supported degree
pub fn max_degree(&self) -> usize {
self.powers_of_g.len()
Expand All @@ -78,21 +78,21 @@ impl<E: Engine> UVUniversalKZGParam<E> {
///
/// # Panics
/// if `supported_size` is greater than `self.max_degree()`
pub fn extract_prover_key(&self, supported_size: usize) -> UVKZGProverKey<E> {
pub fn extract_prover_key(&self, supported_size: usize) -> KZGProverKey<E> {
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<E> {
pub fn extract_verifier_key(&self, supported_size: usize) -> KZGVerifierKey<E> {
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],
Expand All @@ -106,11 +106,11 @@ impl<E: Engine> UVUniversalKZGParam<E> {
///
/// # Panics
/// If `supported_size` is greater than `self.max_degree()`, or `self.max_degree()` is zero.
pub fn trim(&self, supported_size: usize) -> (UVKZGProverKey<E>, UVKZGVerifierKey<E>) {
pub fn trim(&self, supported_size: usize) -> (KZGProverKey<E>, KZGVerifierKey<E>) {
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],
Expand All @@ -119,7 +119,7 @@ impl<E: Engine> UVUniversalKZGParam<E> {
}
}

impl<E: Engine> UVUniversalKZGParam<E>
impl<E: Engine> UniversalKZGParam<E>
where
E::Fr: PrimeFieldBits,
{
Expand Down Expand Up @@ -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<UVKZGProverKey<E>>,
prover_param: impl Borrow<KZGProverKey<E>>,
poly: &UVKZGPoly<E::Fr>,
) -> Result<UVKZGCommitment<E>, NovaError> {
let prover_param = prover_param.borrow();
Expand All @@ -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<UVKZGProverKey<E>>,
prover_param: impl Borrow<KZGProverKey<E>>,
polynomial: &UVKZGPoly<E::Fr>,
point: &E::Fr,
) -> Result<(UVKZGProof<E>, UVKZGEvaluation<E>), NovaError> {
Expand Down Expand Up @@ -277,7 +277,7 @@ where
/// committed inside `comm`.
#[allow(dead_code)]
pub fn verify(
verifier_param: impl Borrow<UVKZGVerifierKey<E>>,
verifier_param: impl Borrow<KZGVerifierKey<E>>,
commitment: &UVKZGCommitment<E>,
point: &E::Fr,
proof: &UVKZGProof<E>,
Expand Down Expand Up @@ -324,7 +324,7 @@ mod tests {
let mut rng = &mut thread_rng();
let degree = rng.gen_range(2..20);

let pp = UVUniversalKZGParam::<E>::gen_srs_for_testing(&mut rng, degree);
let pp = UniversalKZGParam::<E>::gen_srs_for_testing(&mut rng, degree);
let (ck, vk) = pp.trim(degree);
let p = random(degree, rng);
let comm = UVKZGPCS::<E>::commit(&ck, &p)?;
Expand Down
22 changes: 11 additions & 11 deletions src/provider/non_hiding_zeromorph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
Expand Down Expand Up @@ -39,8 +39,8 @@ use crate::provider::kzg_commitment::KZGCommitmentEngine;
deserialize = "E::G1Affine: Deserialize<'de>"
))]
pub struct ZMProverKey<E: Engine> {
commit_pp: UVKZGProverKey<E>,
open_pp: UVKZGProverKey<E>,
commit_pp: KZGProverKey<E>,
open_pp: KZGProverKey<E>,
}

/// `ZMVerifierKey` is used to check evaluation proofs for a given
Expand All @@ -51,7 +51,7 @@ pub struct ZMProverKey<E: Engine> {
deserialize = "E::G1Affine: Deserialize<'de>, E::G2Affine: Deserialize<'de>"
))]
pub struct ZMVerifierKey<E: Engine> {
vp: UVKZGVerifierKey<E>,
vp: KZGVerifierKey<E>,
s_offset_h: E::G2Affine,
}

Expand All @@ -66,14 +66,14 @@ pub struct ZMVerifierKey<E: Engine> {
// 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<E: Engine>(
params: &UVUniversalKZGParam<E>,
params: &UniversalKZGParam<E>,
max_degree: usize,
) -> (ZMProverKey<E>, ZMVerifierKey<E>) {
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,
}
};
Expand Down Expand Up @@ -470,12 +470,12 @@ where

type EvaluationArgument = ZMProof<E>;

fn setup(ck: &UVUniversalKZGParam<E>) -> (Self::ProverKey, Self::VerifierKey) {
fn setup(ck: &UniversalKZGParam<E>) -> (Self::ProverKey, Self::VerifierKey) {
trim(ck, ck.length() - 1)
}

fn prove(
_ck: &UVUniversalKZGParam<E>,
_ck: &UniversalKZGParam<E>,
pk: &Self::ProverKey,
transcript: &mut NE::TE,
comm: &Commitment<NE>,
Expand Down Expand Up @@ -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,
},
Expand All @@ -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::<E>::gen_srs_for_testing(&mut rng, max_poly_size);
let universal_setup = UniversalKZGParam::<E>::gen_srs_for_testing(&mut rng, max_poly_size);

for num_vars in 3..max_vars {
// Setup
Expand Down
3 changes: 2 additions & 1 deletion src/provider/util/mod.rs
Original file line number Diff line number Diff line change
@@ -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;
9 changes: 3 additions & 6 deletions src/spartan/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,9 @@ use rayon::{iter::IntoParallelRefIterator, prelude::*};
// Creates a vector of the first `n` powers of `s`.
fn powers<E: Engine>(s: &E::Scalar, n: usize) -> Vec<E::Scalar> {
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
Expand Down

0 comments on commit 3af23b6

Please sign in to comment.