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 Decider impl for Nova onchain #66

Merged
merged 2 commits into from
Feb 8, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ ark-crypto-primitives = { version = "^0.4.0", default-features = false, features
ark-poly-commit = "^0.4.0"
ark-relations = { version = "^0.4.0", default-features = false }
ark-r1cs-std = { default-features = false } # use latest version from the patch
ark-snark = { version = "^0.4.0"}
ark-serialize = "^0.4.0"
ark-circom = { git = "https://github.com/gakonst/ark-circom.git" }
thiserror = "1.0"
Expand All @@ -26,17 +27,17 @@ espresso_subroutines = {git="https://github.com/EspressoSystems/hyperplonk", pac
ark-pallas = {version="0.4.0", features=["r1cs"]}
ark-vesta = {version="0.4.0", features=["r1cs"]}
ark-bn254 = "0.4.0"
ark-mnt4-298 = {version="0.4.0", features=["r1cs"]}
ark-mnt6-298 = {version="0.4.0", features=["r1cs"]}
ark-groth16 = { version = "^0.4.0" }
rand = "0.8.5"
tracing = { version = "0.1", default-features = false, features = [ "attributes" ] }
tracing-subscriber = { version = "0.2" }

[features]
default = ["parallel"]

parallel = [
"ark-std/parallel",
"ark-ff/parallel",
"ark-poly/parallel",
]
parallel = []
arnaucube marked this conversation as resolved.
Show resolved Hide resolved

# The following patch is to use a version of ark-r1cs-std compatible with
# v0.4.0 but that includes a cherry-picked commit from after v0.4.0 which fixes
Expand Down
40 changes: 31 additions & 9 deletions src/folding/circuits/nonnative.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,26 @@ where
}
}

/// point_to_nonnative_limbs is used to compute (outside the circuit) the limbs representation of a
/// point that matches the one used in-circuit.
/// wrapper on top of point_to_nonnative_limbs_custom_opt which always uses
arnaucube marked this conversation as resolved.
Show resolved Hide resolved
/// OptimizationType::Weight.
arnaucube marked this conversation as resolved.
Show resolved Hide resolved
#[allow(clippy::type_complexity)]
pub fn point_to_nonnative_limbs<C: CurveGroup>(
p: C,
) -> Result<(Vec<C::ScalarField>, Vec<C::ScalarField>), SynthesisError>
where
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
{
point_to_nonnative_limbs_custom_opt(p, OptimizationType::Weight)
}

/// point_to_nonnative_limbs_custom_opt is used to compute (outside the circuit) the limbs
arnaucube marked this conversation as resolved.
Show resolved Hide resolved
/// representation of a point that matches the one used in-circuit, and in particular this method
/// allows to specify which OptimizationType to use.
arnaucube marked this conversation as resolved.
Show resolved Hide resolved
#[allow(clippy::type_complexity)]
pub fn point_to_nonnative_limbs_custom_opt<C: CurveGroup>(
p: C,
optimization_type: OptimizationType,
) -> Result<(Vec<C::ScalarField>, Vec<C::ScalarField>), SynthesisError>
where
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
{
Expand All @@ -68,24 +82,24 @@ where
let x =
AllocatedNonNativeFieldVar::<C::BaseField, C::ScalarField>::get_limbs_representations(
&C::BaseField::zero(),
OptimizationType::Weight,
optimization_type,
)?;
let y =
AllocatedNonNativeFieldVar::<C::BaseField, C::ScalarField>::get_limbs_representations(
&C::BaseField::one(),
OptimizationType::Weight,
optimization_type,
)?;
return Ok((x, y));
}

let (x, y) = affine.xy().unwrap();
let x = AllocatedNonNativeFieldVar::<C::BaseField, C::ScalarField>::get_limbs_representations(
x,
OptimizationType::Weight,
optimization_type,
)?;
let y = AllocatedNonNativeFieldVar::<C::BaseField, C::ScalarField>::get_limbs_representations(
y,
OptimizationType::Weight,
optimization_type,
)?;
Ok((x, y))
}
Expand All @@ -94,16 +108,24 @@ where
mod tests {
use super::*;
use ark_pallas::{Fr, Projective};
use ark_r1cs_std::alloc::AllocVar;
use ark_r1cs_std::{alloc::AllocVar, R1CSVar};
use ark_relations::r1cs::ConstraintSystem;
use ark_std::Zero;
use ark_std::{UniformRand, Zero};

#[test]
fn test_alloc_nonnativeaffinevar_zero() {
fn test_alloc_nonnativeaffinevar() {
let cs = ConstraintSystem::<Fr>::new_ref();

// dealing with the 'zero' point should not panic when doing the unwrap
let p = Projective::zero();
NonNativeAffineVar::<Fr>::new_witness(cs.clone(), || Ok(p)).unwrap();

// check that point_to_nonnative_limbs returns the expected values
let mut rng = ark_std::test_rng();
let p = Projective::rand(&mut rng);
let pVar = NonNativeAffineVar::<Fr>::new_witness(cs.clone(), || Ok(p)).unwrap();
let (x, y) = point_to_nonnative_limbs(p).unwrap();
assert_eq!(pVar.x.value().unwrap(), x);
assert_eq!(pVar.y.value().unwrap(), y);
}
}
205 changes: 205 additions & 0 deletions src/folding/nova/decider_eth.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
/// This file implements the onchain (Ethereum's EVM) decider.
use ark_crypto_primitives::sponge::Absorb;
use ark_ec::{CurveGroup, Group};
use ark_ff::PrimeField;
use ark_r1cs_std::{groups::GroupOpsBounds, prelude::CurveVar};
use ark_snark::SNARK;
use ark_std::rand::CryptoRng;
use ark_std::rand::RngCore;
use core::marker::PhantomData;

pub use super::decider_eth_circuit::DeciderEthCircuit;
use crate::commitment::{pedersen::Params as PedersenParams, CommitmentProver};
use crate::folding::circuits::nonnative::point_to_nonnative_limbs_custom_opt;
use crate::folding::nova::{circuits::CF2, CommittedInstance, Nova};
use crate::frontend::FCircuit;
use crate::Error;
use crate::{Decider as DeciderTrait, FoldingScheme};
use ark_r1cs_std::fields::nonnative::params::OptimizationType;

/// onchain decider, for ethereum use cases
arnaucube marked this conversation as resolved.
Show resolved Hide resolved
#[derive(Clone, Debug)]
pub struct Decider<C1, GC1, C2, GC2, FC, CP1, CP2, S, FS> {
_c1: PhantomData<C1>,
_gc1: PhantomData<GC1>,
_c2: PhantomData<C2>,
_gc2: PhantomData<GC2>,
_fc: PhantomData<FC>,
_cp1: PhantomData<CP1>,
_cp2: PhantomData<CP2>,
_s: PhantomData<S>,
_fs: PhantomData<FS>,
}

impl<C1, GC1, C2, GC2, FC, CP1, CP2, S, FS> DeciderTrait<C1, C2, FC, FS>
for Decider<C1, GC1, C2, GC2, FC, CP1, CP2, S, FS>
where
C1: CurveGroup,
C2: CurveGroup,
GC1: CurveVar<C1, CF2<C1>>,
GC2: CurveVar<C2, CF2<C2>>,
FC: FCircuit<C1::ScalarField>,
CP1: CommitmentProver<C1>,
// enforce that the CP2 is Pedersen commitment, since we're at Ethereum's EVM decider
CP2: CommitmentProver<C2, Params = PedersenParams<C2>>,
S: SNARK<C1::ScalarField>,
FS: FoldingScheme<C1, C2, FC>,
<C1 as CurveGroup>::BaseField: PrimeField,
<C2 as CurveGroup>::BaseField: PrimeField,
<C1 as Group>::ScalarField: Absorb,
<C2 as Group>::ScalarField: Absorb,
C1: CurveGroup<BaseField = C2::ScalarField, ScalarField = C2::BaseField>,
for<'b> &'b GC2: GroupOpsBounds<'b, C2, GC2>,
// constrain FS into Nova, since this is a Decider specificly for Nova
Nova<C1, GC1, C2, GC2, FC, CP1, CP2>: From<FS>,
{
type ProverParam = S::ProvingKey;
type Proof = S::Proof;
type VerifierParam = S::VerifyingKey;
type PublicInput = Vec<C1::ScalarField>;
type CommittedInstanceWithWitness = ();
type CommittedInstance = CommittedInstance<C1>;

fn prove(
pp: &Self::ProverParam,
mut rng: impl RngCore + CryptoRng,
folding_scheme: FS,
) -> Result<Self::Proof, Error> {
let circuit =
DeciderEthCircuit::<C1, GC1, C2, GC2, CP1, CP2>::from_nova::<FC>(folding_scheme.into());

let proof = S::prove(pp, circuit.clone(), &mut rng).unwrap();

Ok(proof)
}

fn verify(
vp: &Self::VerifierParam,
i: C1::ScalarField,
z_0: Vec<C1::ScalarField>,
z_i: Vec<C1::ScalarField>,
running_instance: &Self::CommittedInstance,
proof: Self::Proof,
) -> Result<bool, Error> {
let (cmE_x, cmE_y) = point_to_nonnative_limbs_custom_opt::<C1>(
running_instance.cmE,
OptimizationType::Constraints,
)?;
let (cmW_x, cmW_y) = point_to_nonnative_limbs_custom_opt::<C1>(
running_instance.cmW,
OptimizationType::Constraints,
)?;
let public_input: Vec<C1::ScalarField> = vec![
vec![i],
z_0,
z_i,
vec![running_instance.u],
running_instance.x.clone(),
cmE_x,
cmE_y,
cmW_x,
cmW_y,
]
.concat();
S::verify(vp, &public_input, &proof).map_err(|e| Error::Other(e.to_string()))
}
}

#[cfg(test)]
pub mod tests {
use super::*;
use ark_groth16::Groth16;
use ark_mnt4_298::{constraints::G1Var as GVar, Fr, G1Projective as Projective, MNT4_298};
use ark_mnt6_298::{constraints::G1Var as GVar2, G1Projective as Projective2};
use std::time::Instant;

use crate::commitment::pedersen::Pedersen;
use crate::folding::nova::{get_pedersen_params_len, ProverParams};
use crate::frontend::tests::CubicFCircuit;
use crate::transcript::poseidon::poseidon_test_config;

// Note: since we're testing a big circuit, this test takes a bit more of computation and time,
// do not run in the normal CI.
// To run the test use `--ignored` flag, eg. `cargo test -- --ignored`
#[test]
#[ignore]
fn test_decider() {
type NOVA = Nova<
Projective,
GVar,
Projective2,
GVar2,
CubicFCircuit<Fr>,
Pedersen<Projective>,
Pedersen<Projective2>,
>;
type DECIDER = Decider<
Projective,
GVar,
Projective2,
GVar2,
CubicFCircuit<Fr>,
Pedersen<Projective>,
Pedersen<Projective2>,
Groth16<MNT4_298>, // here we define the Snark to use in the decider
NOVA, // here we define the FoldingScheme to use
>;

let mut rng = ark_std::test_rng();
let poseidon_config = poseidon_test_config::<Fr>();

let F_circuit = CubicFCircuit::<Fr>::new(());
let z_0 = vec![Fr::from(3_u32)];

let (cm_len, cf_cm_len) =
get_pedersen_params_len::<Projective, GVar, Projective2, GVar2, CubicFCircuit<Fr>>(
&poseidon_config,
F_circuit,
)
.unwrap();
let pedersen_params = Pedersen::<Projective>::new_params(&mut rng, cm_len);
let cf_pedersen_params = Pedersen::<Projective2>::new_params(&mut rng, cf_cm_len);

let start = Instant::now();
let prover_params =
ProverParams::<Projective, Projective2, Pedersen<Projective>, Pedersen<Projective2>> {
poseidon_config: poseidon_config.clone(),
cm_params: pedersen_params,
cf_cm_params: cf_pedersen_params,
};
println!("generating pedersen params, {:?}", start.elapsed());

// use Nova as FoldingScheme
let start = Instant::now();
let mut nova = NOVA::init(&prover_params, F_circuit, z_0.clone()).unwrap();
println!("Nova initialized, {:?}", start.elapsed());
let start = Instant::now();
nova.prove_step().unwrap();
println!("prove_step, {:?}", start.elapsed());

// generate Groth16 setup
let circuit = DeciderEthCircuit::<
Projective,
GVar,
Projective2,
GVar2,
Pedersen<Projective>,
Pedersen<Projective2>,
>::from_nova::<CubicFCircuit<Fr>>(nova.clone());
let mut rng = rand::rngs::OsRng;

let start = Instant::now();
let (pk, vk) =
Groth16::<MNT4_298>::circuit_specific_setup(circuit.clone(), &mut rng).unwrap();
println!("Groth16 setup, {:?}", start.elapsed());

// decider proof generation
let start = Instant::now();
let proof = DECIDER::prove(&pk, rng, nova.clone()).unwrap();
println!("Decider Groth16 prove, {:?}", start.elapsed());

// decider proof verification
let verified = DECIDER::verify(&vk, nova.i, nova.z_0, nova.z_i, &nova.U_i, proof).unwrap();
assert!(verified);
}
}
14 changes: 7 additions & 7 deletions src/folding/nova/decider_eth_circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ where

/// Circuit that implements the in-circuit checks needed for the onchain (Ethereum's EVM)
/// verification.
#[derive(Clone, Debug)]
pub struct DeciderEthCircuit<C1, GC1, C2, GC2, CP1, CP2>
where
C1: CurveGroup,
Expand Down Expand Up @@ -281,13 +282,12 @@ where
Ok(self.r1cs.clone())
})?;

let i = FpVar::<CF1<C1>>::new_witness(cs.clone(), || {
Ok(self.i.unwrap_or_else(CF1::<C1>::zero))
})?;
let z_0 = Vec::<FpVar<CF1<C1>>>::new_witness(cs.clone(), || {
let i =
FpVar::<CF1<C1>>::new_input(cs.clone(), || Ok(self.i.unwrap_or_else(CF1::<C1>::zero)))?;
let z_0 = Vec::<FpVar<CF1<C1>>>::new_input(cs.clone(), || {
Ok(self.z_0.unwrap_or(vec![CF1::<C1>::zero()]))
})?;
let z_i = Vec::<FpVar<CF1<C1>>>::new_witness(cs.clone(), || {
let z_i = Vec::<FpVar<CF1<C1>>>::new_input(cs.clone(), || {
Ok(self.z_i.unwrap_or(vec![CF1::<C1>::zero()]))
})?;

Expand All @@ -303,7 +303,7 @@ where
let w_i = WitnessVar::<C1>::new_witness(cs.clone(), || {
Ok(self.w_i.unwrap_or(w_dummy_native.clone()))
})?;
let U_i = CommittedInstanceVar::<C1>::new_witness(cs.clone(), || {
let U_i = CommittedInstanceVar::<C1>::new_input(cs.clone(), || {
Ok(self.U_i.unwrap_or(u_dummy_native.clone()))
})?;
let W_i = WitnessVar::<C1>::new_witness(cs.clone(), || {
Expand Down Expand Up @@ -367,7 +367,7 @@ where
#[cfg(not(test))]
{
// imports here instead of at the top of the file, so we avoid having multiple
// `#[cfg(not(test))]
// `#[cfg(not(test))]`
use crate::commitment::pedersen::PedersenGadget;
use crate::folding::nova::cyclefold::{CycleFoldCommittedInstanceVar, CF_IO_LEN};
use ark_r1cs_std::ToBitsGadget;
Expand Down
6 changes: 3 additions & 3 deletions src/folding/nova/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/// Implements the scheme described in [Nova](https://eprint.iacr.org/2021/370.pdf)
/// Implements the scheme described in [Nova](https://eprint.iacr.org/2021/370.pdf) and
/// [CycleFold](https://eprint.iacr.org/2023/1192.pdf).
use ark_crypto_primitives::{
crh::{poseidon::CRH, CRHScheme},
sponge::{poseidon::PoseidonConfig, Absorb},
Expand All @@ -22,6 +23,7 @@ use crate::FoldingScheme;

pub mod circuits;
pub mod cyclefold;
pub mod decider_eth;
pub mod decider_eth_circuit;
pub mod nifs;
pub mod traits;
Expand Down Expand Up @@ -216,10 +218,8 @@ where
type PreprocessorParam = (Self::ProverParam, FC);
type ProverParam = ProverParams<C1, C2, CP1, CP2>;
type VerifierParam = VerifierParams<C1, C2>;
type Witness = Witness<C1>;
type CommittedInstanceWithWitness = (CommittedInstance<C1>, Witness<C1>);
type CFCommittedInstanceWithWitness = (CommittedInstance<C2>, Witness<C2>);
type CommittedInstance = CommittedInstance<C1>;

fn preprocess(
prep_param: &Self::PreprocessorParam,
Expand Down
Loading
Loading