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 authored Dec 19, 2023
1 parent 55d83ea commit a1e6feb
Show file tree
Hide file tree
Showing 15 changed files with 767 additions and 89 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Nova: Recursive SNARKs without trusted setup
# Nova: High-speed recursive arguments from folding schemes

> [!NOTE]
> This repository is a fork of the original hosted at [https://github.com/microsoft/nova](https://github.com/microsoft/nova). It's an incubator for experimenting with more advanced variants of the original software and working out the kinks in them.
Expand Down
84 changes: 44 additions & 40 deletions examples/minroot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,27 @@
//! iterations of the `MinRoot` function, thereby realizing a Nova-based verifiable delay function (VDF).
//! We execute a configurable number of iterations of the `MinRoot` function per step of Nova's recursion.
use arecibo::{
provider::{PallasEngine, VestaEngine},
provider::{Bn256EngineKZG, GrumpkinEngine},
traits::{
circuit::{StepCircuit, TrivialCircuit},
snark::default_ck_hint,
Engine,
snark::RelaxedR1CSSNARKTrait,
Engine, Group,
},
CompressedSNARK, PublicParams, RecursiveSNARK,
};
use bellpepper_core::{num::AllocatedNum, ConstraintSystem, SynthesisError};
use ff::PrimeField;
use ff::Field;
use flate2::{write::ZlibEncoder, Compression};
use halo2curves::bn256::Bn256;
use num_bigint::BigUint;
use std::time::Instant;
use tracing_subscriber::{fmt, prelude::*, EnvFilter, Registry};
use tracing_texray::TeXRayLayer;

type E1 = PallasEngine;
type E2 = VestaEngine;

#[cfg(feature = "abomonate")]
mod utils {
use super::*;
use ff::PrimeField;
use std::{io::Write, mem::size_of};

pub const FILEPATH: &str = "/tmp/data";
Expand All @@ -49,7 +48,7 @@ mod utils {
std::ptr::write(f, std::ptr::read(mine));
rest
}
impl<F: PrimeField> abomonation::Abomonation for MinRootIteration<F> {
impl<G: Group> abomonation::Abomonation for MinRootIteration<G> {
unsafe fn entomb<W: Write>(&self, bytes: &mut W) -> std::io::Result<()> {
entomb_F(&self.x_i, bytes)?;
entomb_F(&self.y_i, bytes)?;
Expand All @@ -73,29 +72,32 @@ mod utils {
}

#[derive(Clone, Debug, PartialEq)]
struct MinRootIteration<F: PrimeField> {
x_i: F,
y_i: F,
x_i_plus_1: F,
y_i_plus_1: F,
struct MinRootIteration<G: Group> {
x_i: G::Scalar,
y_i: G::Scalar,
x_i_plus_1: G::Scalar,
y_i_plus_1: G::Scalar,
}

impl<F: PrimeField> MinRootIteration<F> {
impl<G: Group> MinRootIteration<G> {
// produces a sample non-deterministic advice, executing one invocation of MinRoot per step
fn new(num_iters: usize, x_0: &F, y_0: &F) -> (Vec<F>, Vec<Self>) {
// although this code is written generically, it is tailored to Pallas' scalar field
// (p - 3 / 5)
let exp = BigUint::parse_bytes(
b"23158417847463239084714197001737581570690445185553317903743794198714690358477",
10,
)
.unwrap();
fn new(num_iters: usize, x_0: &G::Scalar, y_0: &G::Scalar) -> (Vec<G::Scalar>, Vec<Self>) {
// exp = (p - 3 / 5), where p is the order of the group
// x^{exp} mod p provides the fifth root of x
let exp = {
let p = G::group_params().2.to_biguint().unwrap();
let two = BigUint::parse_bytes(b"2", 10).unwrap();
let three = BigUint::parse_bytes(b"3", 10).unwrap();
let five = BigUint::parse_bytes(b"5", 10).unwrap();
let five_inv = five.modpow(&(&p - &two), &p);
(&five_inv * (&p - &three)) % &p
};

let mut res = Vec::new();
let mut x_i = *x_0;
let mut y_i = *y_0;
for _i in 0..num_iters {
let x_i_plus_1 = (x_i + y_i).pow_vartime(exp.to_u64_digits()); // computes the fifth root of x_i + y_i
let x_i_plus_1 = (x_i + y_i).pow_vartime(&exp.to_u64_digits()); // computes the fifth root of x_i + y_i

// sanity check
if cfg!(debug_assertions) {
Expand Down Expand Up @@ -125,21 +127,21 @@ impl<F: PrimeField> MinRootIteration<F> {
}

#[derive(Clone, Debug, PartialEq)]
struct MinRootCircuit<F: PrimeField> {
seq: Vec<MinRootIteration<F>>,
struct MinRootCircuit<G: Group> {
seq: Vec<MinRootIteration<G>>,
}

impl<F: PrimeField> StepCircuit<F> for MinRootCircuit<F> {
impl<G: Group> StepCircuit<G::Scalar> for MinRootCircuit<G> {
fn arity(&self) -> usize {
2
}

fn synthesize<CS: ConstraintSystem<F>>(
fn synthesize<CS: ConstraintSystem<G::Scalar>>(
&self,
cs: &mut CS,
z: &[AllocatedNum<F>],
) -> Result<Vec<AllocatedNum<F>>, SynthesisError> {
let mut z_out: Result<Vec<AllocatedNum<F>>, SynthesisError> =
z: &[AllocatedNum<G::Scalar>],
) -> Result<Vec<AllocatedNum<G::Scalar>>, SynthesisError> {
let mut z_out: Result<Vec<AllocatedNum<G::Scalar>>, SynthesisError> =
Err(SynthesisError::AssignmentMissing);

// use the provided inputs
Expand Down Expand Up @@ -220,13 +222,13 @@ fn main() {
let pp = PublicParams::<
E1,
E2,
MinRootCircuit<<E1 as Engine>::Scalar>,
MinRootCircuit<<E1 as Engine>::GE>,
TrivialCircuit<<E2 as Engine>::Scalar>,
>::setup(
&circuit_primary,
&circuit_secondary,
&*default_ck_hint(),
&*default_ck_hint(),
&*S1::ck_floor(),
&*S2::ck_floor(),
);
println!("PublicParams::setup, took {:?} ", start.elapsed());
#[cfg(feature = "abomonate")]
Expand Down Expand Up @@ -271,7 +273,7 @@ fn main() {
PublicParams<
E1,
E2,
MinRootCircuit<<E1 as Engine>::Scalar>,
MinRootCircuit<<E1 as Engine>::GE>,
TrivialCircuit<<E2 as Engine>::Scalar>,
>,
>(&mut bytes)
Expand All @@ -284,7 +286,7 @@ fn main() {
}

// produce non-deterministic advice
let (z0_primary, minroot_iterations) = MinRootIteration::new(
let (z0_primary, minroot_iterations) = MinRootIteration::<<E1 as Engine>::GE>::new(
num_iters_per_step * num_steps,
&<E1 as Engine>::Scalar::zero(),
&<E1 as Engine>::Scalar::one(),
Expand All @@ -304,7 +306,7 @@ fn main() {

let z0_secondary = vec![<E2 as Engine>::Scalar::zero()];

type C1 = MinRootCircuit<<E1 as Engine>::Scalar>;
type C1 = MinRootCircuit<<E1 as Engine>::GE>;
type C2 = TrivialCircuit<<E2 as Engine>::Scalar>;
// produce a recursive SNARK
println!("Generating a RecursiveSNARK...");
Expand Down Expand Up @@ -342,14 +344,16 @@ fn main() {
assert!(res.is_ok());

// produce a compressed SNARK
println!("Generating a CompressedSNARK using Spartan with IPA-PC...");
println!("Generating a CompressedSNARK using Spartan with multilinear KZG...");
let (pk, vk) = CompressedSNARK::<_, _, _, _, S1, S2>::setup(&pp).unwrap();

let start = Instant::now();
type EE1 = arecibo::provider::ipa_pc::EvaluationEngine<E1>;
type E1 = Bn256EngineKZG;
type E2 = GrumpkinEngine;
type EE1 = arecibo::provider::mlkzg::EvaluationEngine<Bn256, E1>;
type EE2 = arecibo::provider::ipa_pc::EvaluationEngine<E2>;
type S1 = arecibo::spartan::snark::RelaxedR1CSSNARK<E1, EE1>;
type S2 = arecibo::spartan::snark::RelaxedR1CSSNARK<E2, EE2>;
type S1 = arecibo::spartan::snark::RelaxedR1CSSNARK<E1, EE1>; // non-preprocessing SNARK
type S2 = arecibo::spartan::snark::RelaxedR1CSSNARK<E2, EE2>; // non-preprocessing SNARK

let res = CompressedSNARK::<_, _, _, _, S1, S2>::prove(&pp, &pk, &recursive_snark);
println!(
Expand Down
11 changes: 9 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1000,8 +1000,8 @@ mod tests {
use super::*;
use crate::{
provider::{
non_hiding_zeromorph::ZMPCS, traits::DlogGroup, Bn256Engine, Bn256EngineZM, GrumpkinEngine,
PallasEngine, Secp256k1Engine, Secq256k1Engine, VestaEngine,
non_hiding_zeromorph::ZMPCS, traits::DlogGroup, Bn256Engine, Bn256EngineKZG, Bn256EngineZM,
GrumpkinEngine, PallasEngine, Secp256k1Engine, Secq256k1Engine, VestaEngine,
},
traits::{evaluation::EvaluationEngineTrait, snark::default_ck_hint},
};
Expand Down Expand Up @@ -1387,6 +1387,13 @@ mod tests {
ZMPCS<Bn256, _>,
EE<_>,
>();

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

fn test_ivc_nontrivial_with_spark_compression_with<E1, E2, EE1, EE2>()
Expand Down
2 changes: 1 addition & 1 deletion src/provider/bn256_grumpkin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
use crate::{
impl_traits,
provider::{
msm::cpu_best_msm,
traits::{CompressedGroup, DlogGroup},
util::msm::cpu_best_msm,
},
traits::{Group, PrimeFieldExt, TranscriptReprTrait},
};
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
Loading

0 comments on commit a1e6feb

Please sign in to comment.