Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add MLKZG support (Nova forward port) #172

Merged
merged 7 commits into from
Dec 19, 2023
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
Loading