Skip to content

Commit

Permalink
Add NIFSGadgetTrait, implement Mova's NIFSGadget, adapt Nova NIFSGadg…
Browse files Browse the repository at this point in the history
…et into NIFSGadgetTrait

* add new NIFSGadgetTrait

* implement Mova's NIFSGadget

* refactor Nova NIFSGadget to fit into the new NIFSGadgetTrait

* abstract NIFSGadget related tests for all implementors of
  NIFSGadgetTrait to avoid duplicated code in the tests between the
  different Nova variants gadget tests
  • Loading branch information
arnaucube committed Oct 27, 2024
1 parent 6d8f297 commit 5e849f9
Show file tree
Hide file tree
Showing 10 changed files with 767 additions and 378 deletions.
385 changes: 32 additions & 353 deletions folding-schemes/src/folding/nova/circuits.rs

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion folding-schemes/src/folding/nova/decider_circuits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,11 @@ use ark_std::Zero;
use core::marker::PhantomData;

use super::{
circuits::{ChallengeGadget, CommittedInstanceVar},
decider_eth_circuit::{
evaluate_gadget, KZGChallengesGadget, R1CSVar, RelaxedR1CSGadget, WitnessVar,
},
nifs::nova::ChallengeGadget,
nifs::nova_circuits::CommittedInstanceVar,
nifs::{nova::NIFS, NIFSTrait},
CommittedInstance, Nova, Witness,
};
Expand Down
2 changes: 1 addition & 1 deletion folding-schemes/src/folding/nova/decider_eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use crate::commitment::{
CommitmentScheme,
};
use crate::folding::circuits::{nonnative::affine::NonNativeAffineVar, CF2};
use crate::folding::nova::circuits::ChallengeGadget;
use crate::folding::nova::nifs::nova::ChallengeGadget;
use crate::frontend::FCircuit;
use crate::transcript::poseidon::poseidon_canonical_config;
use crate::Error;
Expand Down
3 changes: 2 additions & 1 deletion folding-schemes/src/folding/nova/decider_eth_circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ use ark_std::{log2, Zero};
use core::{borrow::Borrow, marker::PhantomData};

use super::{
circuits::{ChallengeGadget, CommittedInstanceVar},
nifs::nova::ChallengeGadget,
nifs::nova_circuits::CommittedInstanceVar,
nifs::{nova::NIFS, NIFSTrait},
CommittedInstance, Nova, Witness,
};
Expand Down
6 changes: 3 additions & 3 deletions folding-schemes/src/folding/nova/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ use ark_std::fmt::Debug;
use ark_std::rand::RngCore;
use ark_std::{One, UniformRand, Zero};
use core::marker::PhantomData;
use decider_eth_circuit::WitnessVar;

use crate::folding::circuits::cyclefold::{
fold_cyclefold_circuit, CycleFoldCircuit, CycleFoldCommittedInstance, CycleFoldConfig,
Expand All @@ -38,6 +37,7 @@ use crate::{
utils::{get_cm_coordinates, pp_hash},
};
use crate::{arith::Arith, commitment::CommitmentScheme};
use decider_eth_circuit::WitnessVar;

pub mod circuits;
pub mod traits;
Expand All @@ -46,8 +46,8 @@ pub mod zk;
// NIFS related:
pub mod nifs;

use circuits::{AugmentedFCircuit, CommittedInstanceVar};
use nifs::{nova::NIFS, NIFSTrait};
use circuits::AugmentedFCircuit;
use nifs::{nova::NIFS, nova_circuits::CommittedInstanceVar, NIFSTrait};

// offchain decider
pub mod decider;
Expand Down
187 changes: 177 additions & 10 deletions folding-schemes/src/folding/nova/nifs/mod.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,28 @@
/// This module defines the NIFSTrait, which is set to implement the NIFS (Non-Interactive Folding
/// Scheme) by the various schemes (Nova, Mova, Ova).
use ark_crypto_primitives::sponge::Absorb;
/// This module defines the traits related to the NIFS (Non-Interactive Folding Scheme).
/// - NIFSTrait, which implements the NIFS interface
/// - NIFSGadget, which implements the NIFS in-circuit
/// both traits implemented by the various Nova variants schemes; ie.
/// [Nova](https://eprint.iacr.org/2021/370.pdf), [Ova](https://hackmd.io/V4838nnlRKal9ZiTHiGYzw),
/// [Mova](https://eprint.iacr.org/2024/1220.pdf).
use ark_crypto_primitives::sponge::{constraints::AbsorbGadget, Absorb, CryptographicSponge};
use ark_ec::CurveGroup;
use ark_r1cs_std::{alloc::AllocVar, boolean::Boolean, fields::fp::FpVar};
use ark_relations::r1cs::SynthesisError;
use ark_std::fmt::Debug;
use ark_std::rand::RngCore;

use crate::arith::r1cs::R1CS;
use crate::commitment::CommitmentScheme;
use crate::transcript::Transcript;
use crate::folding::circuits::CF1;
use crate::folding::traits::{CommittedInstanceOps, CommittedInstanceVarOps};
use crate::transcript::{Transcript, TranscriptVar};
use crate::Error;

pub mod mova;
pub mod nova;
pub mod nova_circuits;
pub mod ova;
pub mod ova_circuits;
pub mod pointvsline;

/// Defines the NIFS (Non-Interactive Folding Scheme) trait, initially defined in
Expand All @@ -27,7 +37,7 @@ pub trait NIFSTrait<
const H: bool = false,
>
{
type CommittedInstance: Debug + Clone + Absorb;
type CommittedInstance: Debug + Clone + Absorb; // + CommittedInstanceOps<C>;
type Witness: Debug + Clone;
type ProverAux: Debug + Clone; // Prover's aux params. eg. in Nova is T
type Proof: Debug + Clone; // proof. eg. in Nova is cmT
Expand Down Expand Up @@ -83,27 +93,63 @@ pub trait NIFSTrait<
) -> Result<(Self::CommittedInstance, Vec<bool>), Error>;
}

/// Defines the NIFS (Non-Interactive Folding Scheme) Gadget trait, which specifies the in-circuit
/// logic of the NIFS.Verify defined in [Nova](https://eprint.iacr.org/2021/370.pdf) and it's
/// variants [Ova](https://hackmd.io/V4838nnlRKal9ZiTHiGYzw) and
/// [Mova](https://eprint.iacr.org/2024/1220.pdf).
pub trait NIFSGadgetTrait<C: CurveGroup, S: CryptographicSponge, T: TranscriptVar<CF1<C>, S>> {
type CommittedInstance: Debug + Clone + Absorb + CommittedInstanceOps<C>;
type CommittedInstanceVar: Debug
+ Clone
+ AbsorbGadget<C::ScalarField>
+ AllocVar<Self::CommittedInstance, CF1<C>>
+ CommittedInstanceVarOps<C>;
type Proof: Debug + Clone;
type ProofVar: Debug + Clone + AllocVar<Self::Proof, CF1<C>>;

/// Implements the constraints for NIFS.V for u and x, since cm(E) and cm(W) are delegated to
/// the CycleFold circuit.
fn verify(
transcript: &mut T,
pp_hash: FpVar<CF1<C>>,
U_i: Self::CommittedInstanceVar,
// U_i_vec is passed to reuse the already computed U_i_vec from previous methods
U_i_vec: Vec<FpVar<CF1<C>>>,
u_i: Self::CommittedInstanceVar,
proof: Option<Self::ProofVar>,
) -> Result<(Self::CommittedInstanceVar, Vec<Boolean<CF1<C>>>), SynthesisError>;
}

/// These tests are the generic tests so that in the tests ofNova, Mova, Ova, we just need to
/// instantiate these tests to test both the NIFSTrait and NIFSGadgetTrait implementations for each
/// of the schemes.
#[cfg(test)]
pub mod tests {
use super::*;
use crate::transcript::poseidon::poseidon_canonical_config;
use ark_crypto_primitives::sponge::{
constraints::{AbsorbGadget, CryptographicSpongeVar},
poseidon::constraints::PoseidonSpongeVar,
Absorb,
};
use ark_crypto_primitives::sponge::{poseidon::PoseidonSponge, CryptographicSponge};
use ark_pallas::{Fr, Projective};
use ark_r1cs_std::{alloc::AllocVar, fields::fp::FpVar, R1CSVar};
use ark_relations::r1cs::ConstraintSystem;
use ark_std::{test_rng, UniformRand};

use super::NIFSTrait;
use super::*;
use crate::arith::r1cs::tests::{get_test_r1cs, get_test_z};
use crate::commitment::pedersen::Pedersen;
use crate::folding::traits::{CommittedInstanceOps, CommittedInstanceVarOps};
use crate::transcript::poseidon::poseidon_canonical_config;

/// Test method used to test the different implementations of the NIFSTrait (ie. Nova, Mova,
/// Ova). Runs a loop using the NIFS trait, and returns the last Witness and CommittedInstance
/// so that their relation can be checked.
pub(crate) fn test_nifs_opt<
N: NIFSTrait<Projective, Pedersen<Projective>, PoseidonSponge<Fr>>,
>() -> (N::Witness, N::CommittedInstance) {
let r1cs = get_test_r1cs();
let z = get_test_z(3);
let (w, x) = r1cs.split_z(&z);
let r1cs: R1CS<Fr> = get_test_r1cs();

let mut rng = ark_std::test_rng();
let (pedersen_params, _) = Pedersen::<Projective>::setup(&mut rng, r1cs.A.n_cols).unwrap();
Expand All @@ -114,6 +160,8 @@ pub mod tests {
let pp_hash = Fr::rand(&mut rng);

// prepare the running instance
let z = get_test_z(3);
let (w, x) = r1cs.split_z(&z);
let mut W_i = N::new_witness(w.clone(), r1cs.A.n_rows, test_rng());
let mut U_i = N::new_instance(&mut rng, &pedersen_params, &W_i, x, vec![]).unwrap();

Expand Down Expand Up @@ -149,4 +197,123 @@ pub mod tests {

(W_i, U_i)
}

/// Test method used to test the different implementations of the NIFSGadgetTrait (ie. Nova,
/// Mova, Ova). It returns the last Witness and CommittedInstance so that it can be checked at
/// the parent test that their values match.
pub(crate) fn test_nifs_gadget_opt<N, NG>(
ci: Vec<NG::CommittedInstance>,
proof: NG::Proof,
) -> Result<(NG::CommittedInstance, NG::CommittedInstanceVar), Error>
where
N: NIFSTrait<Projective, Pedersen<Projective>, PoseidonSponge<Fr>>,
NG: NIFSGadgetTrait<
Projective,
PoseidonSponge<Fr>,
PoseidonSpongeVar<Fr>,
CommittedInstance = N::CommittedInstance, // constrain that N::CI==NG::CI
Proof = N::Proof, // constrain that N::Proof==NG::Proof
>,
{
let mut rng = ark_std::test_rng();

let (U_i, u_i) = (ci[0].clone(), ci[1].clone());
let pp_hash = Fr::rand(&mut rng);
let poseidon_config = poseidon_canonical_config::<Fr>();
let mut transcript = PoseidonSponge::<Fr>::new(&poseidon_config);
let (ci3, _) = N::verify(&mut transcript, pp_hash, &U_i, &u_i, &proof)?;

let cs = ConstraintSystem::<Fr>::new_ref();

let mut transcriptVar = PoseidonSpongeVar::<Fr>::new(cs.clone(), &poseidon_config);
let pp_hashVar = FpVar::<Fr>::new_witness(cs.clone(), || Ok(pp_hash))?;
let ci1Var = NG::CommittedInstanceVar::new_witness(cs.clone(), || Ok(U_i.clone()))?;
let ci2Var = NG::CommittedInstanceVar::new_witness(cs.clone(), || Ok(u_i.clone()))?;
let proofVar = NG::ProofVar::new_witness(cs.clone(), || Ok(proof))?;

let ci1Var_vec = ci1Var.to_sponge_field_elements()?;
let (out, _) = NG::verify(
&mut transcriptVar,
pp_hashVar,
ci1Var.clone(),
ci1Var_vec,
ci2Var.clone(),
Some(proofVar.clone()),
)?;
assert!(cs.is_satisfied()?);

// return the NIFS.V and the NIFSGadget.V obtained values, so that they are checked at the
// parent test
Ok((ci3, out))
}

/// test that checks the native CommittedInstance.to_sponge_{bytes,field_elements}
/// vs the R1CS constraints version
pub(crate) fn test_committed_instance_to_sponge_preimage_opt<N, NG>(ci: N::CommittedInstance)
where
N: NIFSTrait<Projective, Pedersen<Projective>, PoseidonSponge<Fr>>,
NG: NIFSGadgetTrait<
Projective,
PoseidonSponge<Fr>,
PoseidonSpongeVar<Fr>,
CommittedInstance = N::CommittedInstance, // constrain that N::CI==NG::CI
>,
{
let bytes = ci.to_sponge_bytes_as_vec();
let field_elements = ci.to_sponge_field_elements_as_vec();

let cs = ConstraintSystem::<Fr>::new_ref();

let ciVar = NG::CommittedInstanceVar::new_witness(cs.clone(), || Ok(ci.clone())).unwrap();
let bytes_var = ciVar.to_sponge_bytes().unwrap();
let field_elements_var = ciVar.to_sponge_field_elements().unwrap();

assert!(cs.is_satisfied().unwrap());

// check that the natively computed and in-circuit computed hashes match
assert_eq!(bytes_var.value().unwrap(), bytes);
assert_eq!(field_elements_var.value().unwrap(), field_elements);
}

pub(crate) fn test_committed_instance_hash_opt<N, NG>(ci: NG::CommittedInstance)
where
N: NIFSTrait<Projective, Pedersen<Projective>, PoseidonSponge<Fr>>,
NG: NIFSGadgetTrait<
Projective,
PoseidonSponge<Fr>,
PoseidonSpongeVar<Fr>,
CommittedInstance = N::CommittedInstance, // constrain that N::CI==NG::CI
>,
N::CommittedInstance: CommittedInstanceOps<Projective>,
{
let poseidon_config = poseidon_canonical_config::<Fr>();
let sponge = PoseidonSponge::<Fr>::new(&poseidon_config);
let pp_hash = Fr::from(42u32); // only for test

let i = Fr::from(3_u32);
let z_0 = vec![Fr::from(3_u32)];
let z_i = vec![Fr::from(3_u32)];

// compute the CommittedInstance hash natively
let h = ci.hash(&sponge, pp_hash, i, &z_0, &z_i);

let cs = ConstraintSystem::<Fr>::new_ref();

let pp_hashVar = FpVar::<Fr>::new_witness(cs.clone(), || Ok(pp_hash)).unwrap();
let iVar = FpVar::<Fr>::new_witness(cs.clone(), || Ok(i)).unwrap();
let z_0Var = Vec::<FpVar<Fr>>::new_witness(cs.clone(), || Ok(z_0.clone())).unwrap();
let z_iVar = Vec::<FpVar<Fr>>::new_witness(cs.clone(), || Ok(z_i.clone())).unwrap();
let ciVar = NG::CommittedInstanceVar::new_witness(cs.clone(), || Ok(ci.clone())).unwrap();

let sponge = PoseidonSpongeVar::<Fr>::new(cs.clone(), &poseidon_config);

// compute the CommittedInstance hash in-circuit
let (hVar, _) = ciVar
.hash(&sponge, &pp_hashVar, &iVar, &z_0Var, &z_iVar)
.unwrap();
assert!(cs.is_satisfied().unwrap());

// check that the natively computed and in-circuit computed hashes match
assert_eq!(hVar.value().unwrap(), h);
}
}
66 changes: 62 additions & 4 deletions folding-schemes/src/folding/nova/nifs/nova.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,80 @@
/// This module contains the implementation the NIFSTrait for the
/// [Nova](https://eprint.iacr.org/2021/370.pdf) NIFS (Non-Interactive Folding Scheme).
use ark_crypto_primitives::sponge::Absorb;
use ark_crypto_primitives::sponge::{constraints::AbsorbGadget, Absorb, CryptographicSponge};
use ark_ec::{CurveGroup, Group};
use ark_ff::{BigInteger, PrimeField};
use ark_r1cs_std::{boolean::Boolean, fields::fp::FpVar};
use ark_relations::r1cs::SynthesisError;
use ark_std::rand::RngCore;
use ark_std::Zero;
use std::marker::PhantomData;

use super::NIFSTrait;
use crate::arith::r1cs::R1CS;
use crate::commitment::CommitmentScheme;
use crate::folding::circuits::cyclefold::{CycleFoldCommittedInstance, CycleFoldWitness};
use crate::folding::nova::circuits::ChallengeGadget;
use crate::constants::NOVA_N_BITS_RO;
use crate::folding::circuits::{
cyclefold::{CycleFoldCommittedInstance, CycleFoldWitness},
nonnative::affine::NonNativeAffineVar,
CF1,
};
use crate::folding::nova::{CommittedInstance, Witness};
use crate::transcript::Transcript;
use crate::transcript::{Transcript, TranscriptVar};
use crate::utils::vec::{hadamard, mat_vec_mul, vec_add, vec_scalar_mul, vec_sub};
use crate::Error;

/// ChallengeGadget computes the RO challenge used for the Nova instances NIFS, it contains a
/// rust-native and a in-circuit compatible versions.
pub struct ChallengeGadget<C: CurveGroup, CI: Absorb> {
_c: PhantomData<C>,
_ci: PhantomData<CI>,
}
impl<C: CurveGroup, CI: Absorb> ChallengeGadget<C, CI>
where
C: CurveGroup,
<C as CurveGroup>::BaseField: PrimeField,
<C as Group>::ScalarField: Absorb,
{
pub fn get_challenge_native<T: Transcript<C::ScalarField>>(
transcript: &mut T,
pp_hash: C::ScalarField, // public params hash
U_i: &CI,
u_i: &CI,
cmT: Option<&C>,
) -> Vec<bool> {
transcript.absorb(&pp_hash);
transcript.absorb(&U_i);
transcript.absorb(&u_i);
// in the Nova case we absorb the cmT, in Ova case we don't since it is not used.
if let Some(cmT_value) = cmT {
transcript.absorb_nonnative(cmT_value);
}
transcript.squeeze_bits(NOVA_N_BITS_RO)
}

// compatible with the native get_challenge_native
pub fn get_challenge_gadget<
S: CryptographicSponge,
T: TranscriptVar<CF1<C>, S>,
CIVar: AbsorbGadget<CF1<C>>,
>(
transcript: &mut T,
pp_hash: FpVar<CF1<C>>, // public params hash
U_i_vec: Vec<FpVar<CF1<C>>>, // apready processed input, so we don't have to recompute these values
u_i: CIVar,
cmT: Option<NonNativeAffineVar<C>>,
) -> Result<Vec<Boolean<C::ScalarField>>, SynthesisError> {
transcript.absorb(&pp_hash)?;
transcript.absorb(&U_i_vec)?;
transcript.absorb(&u_i)?;
// in the Nova case we absorb the cmT, in Ova case we don't since it is not used.
if let Some(cmT_value) = cmT {
transcript.absorb_nonnative(&cmT_value)?;
}
transcript.squeeze_bits(NOVA_N_BITS_RO)
}
}

/// Implements the Non-Interactive Folding Scheme described in section 4 of
/// [Nova](https://eprint.iacr.org/2021/370.pdf).
/// `H` specifies whether the NIFS will use a blinding factor
Expand Down
Loading

0 comments on commit 5e849f9

Please sign in to comment.