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

Implemented Mova folding scheme #161

Merged
merged 13 commits into from
Oct 23, 2024
Merged
2 changes: 2 additions & 0 deletions examples/circom_full_flow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ fn main() {
// prepare the Nova prover & verifier params
let nova_preprocess_params = PreprocessorParam::new(poseidon_config, f_circuit.clone());
let nova_params = N::preprocess(&mut rng, &nova_preprocess_params).unwrap();
let pp_hash = nova_params.1.pp_hash().unwrap();

// initialize the folding scheme engine, in our case we use Nova
let mut nova = N::init(&nova_params, f_circuit.clone(), z_0).unwrap();
Expand Down Expand Up @@ -131,6 +132,7 @@ fn main() {

let calldata: Vec<u8> = prepare_calldata(
function_selector,
pp_hash,
nova.i,
nova.z_0,
nova.z_i,
Expand Down
2 changes: 2 additions & 0 deletions examples/full_flow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ fn main() {
// prepare the Nova prover & verifier params
let nova_preprocess_params = PreprocessorParam::new(poseidon_config.clone(), f_circuit);
let nova_params = N::preprocess(&mut rng, &nova_preprocess_params).unwrap();
let pp_hash = nova_params.1.pp_hash().unwrap();

// initialize the folding scheme engine, in our case we use Nova
let mut nova = N::init(&nova_params, f_circuit, z_0).unwrap();
Expand Down Expand Up @@ -138,6 +139,7 @@ fn main() {

let calldata: Vec<u8> = prepare_calldata(
function_selector,
pp_hash,
nova.i,
nova.z_0,
nova.z_i,
Expand Down
2 changes: 2 additions & 0 deletions examples/noir_full_flow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ fn main() {
// prepare the Nova prover & verifier params
let nova_preprocess_params = PreprocessorParam::new(poseidon_config, f_circuit.clone());
let nova_params = N::preprocess(&mut rng, &nova_preprocess_params).unwrap();
let pp_hash = nova_params.1.pp_hash().unwrap();

// initialize the folding scheme engine, in our case we use Nova
let mut nova = N::init(&nova_params, f_circuit.clone(), z_0).unwrap();
Expand Down Expand Up @@ -119,6 +120,7 @@ fn main() {

let calldata: Vec<u8> = prepare_calldata(
function_selector,
pp_hash,
nova.i,
nova.z_0,
nova.z_i,
Expand Down
2 changes: 2 additions & 0 deletions examples/noname_full_flow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ fn main() {
// prepare the Nova prover & verifier params
let nova_preprocess_params = PreprocessorParam::new(poseidon_config, f_circuit.clone());
let nova_params = N::preprocess(&mut rng, &nova_preprocess_params).unwrap();
let pp_hash = nova_params.1.pp_hash().unwrap();

// initialize the folding scheme engine, in our case we use Nova
let mut nova = N::init(&nova_params, f_circuit.clone(), z_0).unwrap();
Expand Down Expand Up @@ -131,6 +132,7 @@ fn main() {

let calldata: Vec<u8> = prepare_calldata(
function_selector,
pp_hash,
nova.i,
nova.z_0,
nova.z_i,
Expand Down
114 changes: 97 additions & 17 deletions folding-schemes/src/folding/circuits/cyclefold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@ use super::{nonnative::uint::NonNativeUintVar, CF1, CF2};
use crate::arith::r1cs::{extract_w_x, R1CS};
use crate::commitment::CommitmentScheme;
use crate::constants::NOVA_N_BITS_RO;
use crate::folding::nova::{nifs::NIFS, traits::NIFSTrait};
use crate::folding::nova::nifs::{nova::NIFS, NIFSTrait};
use crate::transcript::{AbsorbNonNative, AbsorbNonNativeGadget, Transcript, TranscriptVar};
use crate::Error;
use ark_crypto_primitives::sponge::poseidon::PoseidonSponge;

/// Re-export the Nova committed instance as `CycleFoldCommittedInstance` and
/// witness as `CycleFoldWitness`, for clarity and consistency
Expand Down Expand Up @@ -493,6 +494,72 @@ where
}
}

/// CycleFoldNIFS is a wrapper on top of Nova's NIFS, which just replaces the `prove` and `verify`
/// methods to use a different ChallengeGadget, but internally reuses the other Nova's NIFS
/// methods.
/// It is a custom implementation that does not follow the NIFSTrait because it needs to work over
/// different fields than the main NIFS impls (Nova, Mova, Ova). Could be abstracted, but it's a
/// tradeoff between overcomplexity at the NIFSTrait and the (not much) need of generalization at
/// the CycleFoldNIFS.
pub struct CycleFoldNIFS<
C1: CurveGroup,
C2: CurveGroup,
GC2: CurveVar<C2, CF2<C2>> + ToConstraintFieldGadget<CF2<C2>>,
CS2: CommitmentScheme<C2, H>,
const H: bool = false,
> where
<C1 as CurveGroup>::BaseField: PrimeField,
<C2 as CurveGroup>::BaseField: PrimeField,
for<'a> &'a GC2: GroupOpsBounds<'a, C2, GC2>,
{
_c1: PhantomData<C1>,
_c2: PhantomData<C2>,
_gc2: PhantomData<GC2>,
_cs: PhantomData<CS2>,
}
impl<C1: CurveGroup, C2: CurveGroup, GC2, CS2: CommitmentScheme<C2, H>, const H: bool>
CycleFoldNIFS<C1, C2, GC2, CS2, H>
where
<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>,
GC2: CurveVar<C2, CF2<C2>> + ToConstraintFieldGadget<CF2<C2>>,
for<'a> &'a GC2: GroupOpsBounds<'a, C2, GC2>,
{
fn prove(
cf_r_Fq: C2::ScalarField, // C2::Fr==C1::Fq
cf_W_i: &CycleFoldWitness<C2>,
cf_U_i: &CycleFoldCommittedInstance<C2>,
cf_w_i: &CycleFoldWitness<C2>,
cf_u_i: &CycleFoldCommittedInstance<C2>,
aux_p: &[C2::ScalarField], // = cf_T
aux_v: C2, // = cf_cmT
) -> Result<(CycleFoldWitness<C2>, CycleFoldCommittedInstance<C2>), Error> {
let w = NIFS::<C2, CS2, PoseidonSponge<C2::ScalarField>, H>::fold_witness(
cf_r_Fq,
cf_W_i,
cf_w_i,
&aux_p.to_vec(),
)?;
let ci = Self::verify(cf_r_Fq, cf_U_i, cf_u_i, &aux_v)?;
Ok((w, ci))
}
fn verify(
r: C2::ScalarField,
U_i: &CycleFoldCommittedInstance<C2>,
u_i: &CycleFoldCommittedInstance<C2>,
cmT: &C2, // VerifierAux
) -> Result<CycleFoldCommittedInstance<C2>, Error> {
Ok(
NIFS::<C2, CS2, PoseidonSponge<C2::ScalarField>, H>::fold_committed_instances(
r, U_i, u_i, cmT,
),
)
}
}

/// Folds the given cyclefold circuit and its instances. This method is abstracted from any folding
/// scheme struct because it is used both by Nova & HyperNova's CycleFold.
#[allow(clippy::type_complexity)]
Expand Down Expand Up @@ -551,14 +618,15 @@ where
cf_w_i.commit::<CS2, H>(&cf_cs_params, cf_x_i.clone())?;

// compute T* and cmT* for CycleFoldCircuit
let (cf_T, cf_cmT) = NIFS::<C2, CS2, H>::compute_cyclefold_cmT(
&cf_cs_params,
&cf_r1cs,
&cf_w_i,
&cf_u_i,
&cf_W_i,
&cf_U_i,
)?;
let (cf_T, cf_cmT) =
NIFS::<C2, CS2, PoseidonSponge<C2::ScalarField>, H>::compute_cyclefold_cmT(
&cf_cs_params,
&cf_r1cs,
&cf_w_i,
&cf_u_i,
&cf_W_i,
&cf_U_i,
)?;

let cf_r_bits = CycleFoldChallengeGadget::<C2, GC2>::get_challenge_native(
transcript,
Expand All @@ -570,8 +638,11 @@ where
let cf_r_Fq = C1::BaseField::from_bigint(BigInteger::from_bits_le(&cf_r_bits))
.expect("cf_r_bits out of bounds");

let (cf_W_i1, cf_U_i1) =
NIFS::<C2, CS2, H>::prove(cf_r_Fq, &cf_W_i, &cf_U_i, &cf_w_i, &cf_u_i, &cf_T, &cf_cmT)?;
let (cf_W_i1, cf_U_i1) = CycleFoldNIFS::<C1, C2, GC2, CS2, H>::prove(
cf_r_Fq, &cf_W_i, &cf_U_i, &cf_w_i, &cf_u_i, &cf_T, cf_cmT,
)?;
let cf_r_Fq = C1::BaseField::from_bigint(BigInteger::from_bits_le(&cf_r_bits))
.expect("cf_r_bits out of bounds");
Ok((cf_w_i, cf_u_i, cf_W_i1, cf_U_i1, cf_cmT, cf_r_Fq))
}

Expand Down Expand Up @@ -671,6 +742,10 @@ pub mod tests {
fn test_nifs_full_gadget() {
let mut rng = ark_std::test_rng();

let poseidon_config = poseidon_canonical_config::<Fr>();
let mut transcript_v = PoseidonSponge::<Fr>::new(&poseidon_config);
let pp_hash = Fr::rand(&mut rng);

// prepare the committed instances to test in-circuit
let ci: Vec<CommittedInstance<Projective>> = (0..2)
.into_iter()
Expand All @@ -685,11 +760,16 @@ pub mod tests {
// make the 2nd instance a 'fresh' instance (ie. cmE=0, u=1)
ci2.cmE = Projective::zero();
ci2.u = Fr::one();
let r_bits: Vec<bool> =
Fr::rand(&mut rng).into_bigint().to_bits_le()[..NOVA_N_BITS_RO].to_vec();
let r_Fr = Fr::from_bigint(BigInteger::from_bits_le(&r_bits)).unwrap();
let cmT = Projective::rand(&mut rng);
let ci3 = NIFS::<Projective, Pedersen<Projective>>::verify(r_Fr, &ci1, &ci2, &cmT);

let cmT = Projective::rand(&mut rng); // random only for testing
let (ci3, r_bits) = NIFS::<Projective, Pedersen<Projective>, PoseidonSponge<Fr>>::verify(
&mut transcript_v,
pp_hash,
&ci1,
&ci2,
&cmT,
)
.unwrap();

let cs = ConstraintSystem::<Fq>::new_ref();
let r_bitsVar = Vec::<Boolean<Fq>>::new_witness(cs.clone(), || Ok(r_bits)).unwrap();
Expand Down Expand Up @@ -737,7 +817,7 @@ pub mod tests {
.take(TestCycleFoldConfig::<Projective, 2>::IO_LEN)
.collect(),
};
let cmT = Projective::rand(&mut rng);
let cmT = Projective::rand(&mut rng); // random only for testing

// compute the challenge natively
let pp_hash = Fq::from(42u32); // only for test
Expand Down
17 changes: 13 additions & 4 deletions folding-schemes/src/folding/nova/circuits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -529,8 +529,7 @@ pub mod tests {
use ark_std::UniformRand;

use crate::commitment::pedersen::Pedersen;
use crate::folding::nova::nifs::NIFS;
use crate::folding::nova::traits::NIFSTrait;
use crate::folding::nova::nifs::{nova::NIFS, NIFSTrait};
use crate::folding::traits::CommittedInstanceOps;
use crate::transcript::poseidon::poseidon_canonical_config;

Expand Down Expand Up @@ -570,9 +569,19 @@ pub mod tests {
})
.collect();
let (ci1, ci2) = (ci[0].clone(), ci[1].clone());
let r_Fr = Fr::rand(&mut rng);
let pp_hash = Fr::rand(&mut rng);
let cmT = Projective::rand(&mut rng);
let ci3 = NIFS::<Projective, Pedersen<Projective>>::verify(r_Fr, &ci1, &ci2, &cmT);
let poseidon_config = poseidon_canonical_config::<Fr>();
let mut transcript = PoseidonSponge::<Fr>::new(&poseidon_config);
let (ci3, r_bits) = NIFS::<Projective, Pedersen<Projective>, PoseidonSponge<Fr>>::verify(
&mut transcript,
pp_hash,
&ci1,
&ci2,
&cmT,
)
.unwrap();
let r_Fr = Fr::from_bigint(BigInteger::from_bits_le(&r_bits)).unwrap();

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

Expand Down
25 changes: 18 additions & 7 deletions folding-schemes/src/folding/nova/decider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/// DeciderEth from decider_eth.rs file.
/// More details can be found at the documentation page:
/// https://privacy-scaling-explorations.github.io/sonobe-docs/design/nova-decider-offchain.html
use ark_crypto_primitives::sponge::Absorb;
use ark_crypto_primitives::sponge::{poseidon::PoseidonSponge, Absorb, CryptographicSponge};
use ark_ec::{AffineRepr, CurveGroup, Group};
use ark_ff::{BigInteger, PrimeField};
use ark_r1cs_std::{groups::GroupOpsBounds, prelude::CurveVar, ToConstraintFieldGadget};
Expand All @@ -13,14 +13,18 @@ use ark_std::{One, Zero};
use core::marker::PhantomData;

use super::decider_circuits::{DeciderCircuit1, DeciderCircuit2};
use super::{nifs::NIFS, traits::NIFSTrait, CommittedInstance, Nova};
use super::{
nifs::{nova::NIFS, NIFSTrait},
CommittedInstance, Nova,
};
use crate::commitment::CommitmentScheme;
use crate::folding::circuits::{
cyclefold::CycleFoldCommittedInstance,
nonnative::{affine::NonNativeAffineVar, uint::NonNativeUintVar},
CF2,
};
use crate::frontend::FCircuit;
use crate::transcript::poseidon::poseidon_canonical_config;
use crate::Error;
use crate::{Decider as DeciderTrait, FoldingScheme};

Expand All @@ -41,7 +45,6 @@ where
// cmT and r are values for the last fold, U_{i+1}=NIFS.V(r, U_i, u_i, cmT), and they are
// checked in-circuit
cmT: C1,
r: C1::ScalarField,
// cyclefold committed instance
cf_U_i: CycleFoldCommittedInstance<C2>,
// the CS challenges are provided by the prover, but in-circuit they are checked to match the
Expand Down Expand Up @@ -209,7 +212,6 @@ where
.map_err(|e| Error::Other(e.to_string()))?;

let cmT = circuit1.cmT.unwrap();
let r_Fr = circuit1.r.unwrap();
let W_i1 = circuit1.W_i1.unwrap();
let cf_W_i = circuit2.cf_W_i.unwrap();

Expand Down Expand Up @@ -265,7 +267,6 @@ where
cs1_proofs: [U_cmW_proof, U_cmE_proof],
cs2_proofs: [cf_cmW_proof, cf_cmE_proof],
cmT,
r: r_Fr,
cf_U_i: circuit1.cf_U_i.unwrap(),
cs1_challenges: [challenge_W, challenge_E],
cs2_challenges: [c2_challenge_W, c2_challenge_E],
Expand All @@ -286,7 +287,17 @@ where
}

// compute U = U_{d+1}= NIFS.V(U_d, u_d, cmT)
let U = NIFS::<C1, CS1>::verify(proof.r, running_instance, incoming_instance, &proof.cmT);
let poseidon_config = poseidon_canonical_config::<C1::ScalarField>();
let mut transcript = PoseidonSponge::<C1::ScalarField>::new(&poseidon_config);
let (U, r_bits) = NIFS::<C1, CS1, PoseidonSponge<C1::ScalarField>>::verify(
&mut transcript,
vp.pp_hash,
running_instance,
incoming_instance,
&proof.cmT,
)?;
let r = C1::ScalarField::from_bigint(BigInteger::from_bits_le(&r_bits))
.ok_or(Error::OutOfBounds)?;

let (cmE_x, cmE_y) = NonNativeAffineVar::inputize(U.cmE)?;
let (cmW_x, cmW_y) = NonNativeAffineVar::inputize(U.cmW)?;
Expand Down Expand Up @@ -332,7 +343,7 @@ where
// NIFS values:
cmT_x,
cmT_y,
vec![proof.r],
vec![r],
]
.concat();

Expand Down
20 changes: 6 additions & 14 deletions folding-schemes/src/folding/nova/decider_circuits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ use super::{
decider_eth_circuit::{
evaluate_gadget, KZGChallengesGadget, R1CSVar, RelaxedR1CSGadget, WitnessVar,
},
nifs::NIFS,
traits::NIFSTrait,
nifs::{nova::NIFS, NIFSTrait},
CommittedInstance, Nova, Witness,
};
use crate::arith::r1cs::R1CS;
Expand Down Expand Up @@ -114,28 +113,21 @@ where
CS2: CommitmentScheme<C2, H>,
{
let mut transcript = PoseidonSponge::<C1::ScalarField>::new(&nova.poseidon_config);
// pp_hash is absorbed to transcript at the ChallengeGadget::get_challenge_native call
// pp_hash is absorbed to transcript at the NIFS::prove call

// compute the U_{i+1}, W_{i+1}
let (T, cmT) = NIFS::<C1, CS1, H>::compute_cmT(
let (W_i1, U_i1, cmT, r_bits) = NIFS::<C1, CS1, PoseidonSponge<C1::ScalarField>, H>::prove(
&nova.cs_pp,
&nova.r1cs.clone(),
&nova.w_i.clone(),
&nova.u_i.clone(),
&nova.W_i.clone(),
&nova.U_i.clone(),
)?;
let r_bits = NIFS::<C1, CS1, H>::get_challenge(
&mut transcript,
nova.pp_hash,
&nova.W_i,
&nova.U_i,
&nova.w_i,
&nova.u_i,
&cmT,
);
)?;
let r_Fr = C1::ScalarField::from_bigint(BigInteger::from_bits_le(&r_bits))
.ok_or(Error::OutOfBounds)?;
let (W_i1, U_i1) =
NIFS::<C1, CS1, H>::prove(r_Fr, &nova.W_i, &nova.U_i, &nova.w_i, &nova.u_i, &T, &cmT)?;

// compute the commitment scheme challenges used as inputs in the circuit
let (cs_challenge_W, cs_challenge_E) =
Expand Down
Loading
Loading