Skip to content

Commit

Permalink
Nova vairants (Ova) NIFS abstraction (#165)
Browse files Browse the repository at this point in the history
* add NIFS trait abstraction (based on the needs for Nova, Mova, Ova), defining a common interface between the three Nova variants

The recent Ova NIFS PR #163 (#163)
and Mova NIFS PR #161 (#161)
PRs add Nova NIFS variants implementations which differ from Nova in the
logic done for the `E` error terms of the instances.

The current Ova implementation (#163)
is based on the existing Nova NIFS code base and adds the modifications
to the `E` logic on top of it, and thus duplicating the code. Similarly
for the Mova NIFS impl.

The rest of the Mova & Ova schemes logic that is not yet implemented is
pretty similar to Nova one (ie. the IVC logic, the circuits and the
Decider), so ideally that can be done reusing most of the already
existing Nova code without duplicating it. This PR is a first step in
that direction for the existing Ova NIFS code.

This commit adds the NIFS trait abstraction with the idea of allowing to
reduce the amount of duplicated code for the Ova's NIFS impl on top of
the Nova's code.

* add Ova variant on top of the new NIFS trait abstraction

This is done from the existing Ova implementation at
`folding/ova/{mod.rs,nofs.rs}`, but removing when possible code that is not
needed or duplicated from the Nova logic.

* rm old Ova duplicated code

This commit combined with the other ones (add nifs abstraction & port
Ova to the nifs abstraction) allows to effectively get rid of ~400 lines
of code that were duplicated in the Ova NIFS impl from the Nova impl.

* small polishing & rebase to latest `main` branch updates
  • Loading branch information
arnaucube authored Oct 3, 2024
1 parent edcef6c commit a07e17e
Show file tree
Hide file tree
Showing 14 changed files with 640 additions and 1,075 deletions.
36 changes: 27 additions & 9 deletions folding-schemes/src/folding/circuits/cyclefold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ 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;
use crate::folding::nova::{nifs::NIFS, traits::NIFSTrait};
use crate::transcript::{AbsorbNonNative, AbsorbNonNativeGadget, Transcript, TranscriptVar};
use crate::Error;

Expand Down Expand Up @@ -570,9 +570,8 @@ 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>::fold_instances(
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) =
NIFS::<C2, CS2, H>::prove(cf_r_Fq, &cf_W_i, &cf_U_i, &cf_w_i, &cf_u_i, &cf_T, &cf_cmT)?;
Ok((cf_w_i, cf_u_i, cf_W_i1, cf_U_i1, cf_cmT, cf_r_Fq))
}

Expand All @@ -584,10 +583,11 @@ pub mod tests {
poseidon::{constraints::PoseidonSpongeVar, PoseidonSponge},
};
use ark_r1cs_std::R1CSVar;
use ark_std::UniformRand;
use ark_std::{One, UniformRand};

use super::*;
use crate::folding::nova::nifs::tests::prepare_simple_fold_inputs;
use crate::commitment::pedersen::Pedersen;
use crate::folding::nova::CommittedInstance;
use crate::transcript::poseidon::poseidon_canonical_config;
use crate::utils::get_cm_coordinates;

Expand Down Expand Up @@ -669,12 +669,30 @@ pub mod tests {

#[test]
fn test_nifs_full_gadget() {
let (_, _, _, _, ci1, _, ci2, _, ci3, _, cmT, r_bits, _) = prepare_simple_fold_inputs();
let mut rng = ark_std::test_rng();

let cs = ConstraintSystem::<Fq>::new_ref();
// prepare the committed instances to test in-circuit
let ci: Vec<CommittedInstance<Projective>> = (0..2)
.into_iter()
.map(|_| CommittedInstance::<Projective> {
cmE: Projective::rand(&mut rng),
u: Fr::rand(&mut rng),
cmW: Projective::rand(&mut rng),
x: vec![Fr::rand(&mut rng); 1],
})
.collect();
let (ci1, mut ci2) = (ci[0].clone(), ci[1].clone());
// 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 cs = ConstraintSystem::<Fq>::new_ref();
let r_bitsVar = Vec::<Boolean<Fq>>::new_witness(cs.clone(), || Ok(r_bits)).unwrap();

let ci1Var =
CycleFoldCommittedInstanceVar::<Projective, GVar>::new_witness(cs.clone(), || {
Ok(ci1.clone())
Expand Down
1 change: 0 additions & 1 deletion folding-schemes/src/folding/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
pub mod circuits;
pub mod hypernova;
pub mod nova;
pub mod ova;
pub mod protogalaxy;
pub mod traits;
79 changes: 50 additions & 29 deletions folding-schemes/src/folding/nova/circuits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,10 +168,11 @@ where

/// 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> {
pub struct ChallengeGadget<C: CurveGroup, CI: Absorb> {
_c: PhantomData<C>,
_ci: PhantomData<CI>,
}
impl<C: CurveGroup> ChallengeGadget<C>
impl<C: CurveGroup, CI: Absorb> ChallengeGadget<C, CI>
where
C: CurveGroup,
<C as CurveGroup>::BaseField: PrimeField,
Expand All @@ -180,14 +181,17 @@ where
pub fn get_challenge_native<T: Transcript<C::ScalarField>>(
transcript: &mut T,
pp_hash: C::ScalarField, // public params hash
U_i: CommittedInstance<C>,
u_i: CommittedInstance<C>,
cmT: C,
U_i: &CI,
u_i: &CI,
cmT: Option<&C>,
) -> Vec<bool> {
transcript.absorb(&pp_hash);
transcript.absorb(&U_i);
transcript.absorb(&u_i);
transcript.absorb_nonnative(&cmT);
// 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)
}

Expand All @@ -197,12 +201,15 @@ where
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: CommittedInstanceVar<C>,
cmT: NonNativeAffineVar<C>,
cmT: Option<NonNativeAffineVar<C>>,
) -> Result<Vec<Boolean<C::ScalarField>>, SynthesisError> {
transcript.absorb(&pp_hash)?;
transcript.absorb(&U_i_vec)?;
transcript.absorb(&u_i)?;
transcript.absorb_nonnative(&cmT)?;
// 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)
}
}
Expand Down Expand Up @@ -376,12 +383,12 @@ where
// P.3. nifs.verify, obtains U_{i+1} by folding u_i & U_i .

// compute r = H(u_i, U_i, cmT)
let r_bits = ChallengeGadget::<C1>::get_challenge_gadget(
let r_bits = ChallengeGadget::<C1, CommittedInstance<C1>>::get_challenge_gadget(
&mut transcript,
pp_hash.clone(),
U_i_vec,
u_i.clone(),
cmT.clone(),
Some(cmT.clone()),
)?;
let r = Boolean::le_bits_to_fp_var(&r_bits)?;
// Also convert r_bits to a `NonNativeFieldVar`
Expand Down Expand Up @@ -522,8 +529,8 @@ pub mod tests {
use ark_std::UniformRand;

use crate::commitment::pedersen::Pedersen;
use crate::folding::nova::nifs::tests::prepare_simple_fold_inputs;
use crate::folding::nova::nifs::NIFS;
use crate::folding::nova::traits::NIFSTrait;
use crate::folding::traits::CommittedInstanceOps;
use crate::transcript::poseidon::poseidon_canonical_config;

Expand All @@ -550,10 +557,22 @@ pub mod tests {

#[test]
fn test_nifs_gadget() {
let (_, _, _, _, ci1, _, ci2, _, ci3, _, cmT, _, r_Fr) = prepare_simple_fold_inputs();
let mut rng = ark_std::test_rng();

let ci3_verifier = NIFS::<Projective, Pedersen<Projective>>::verify(r_Fr, &ci1, &ci2, &cmT);
assert_eq!(ci3_verifier, ci3);
// prepare the committed instances to test in-circuit
let ci: Vec<CommittedInstance<Projective>> = (0..2)
.into_iter()
.map(|_| CommittedInstance::<Projective> {
cmE: Projective::rand(&mut rng),
u: Fr::rand(&mut rng),
cmW: Projective::rand(&mut rng),
x: vec![Fr::rand(&mut rng); 1],
})
.collect();
let (ci1, ci2) = (ci[0].clone(), ci[1].clone());
let r_Fr = Fr::rand(&mut rng);
let cmT = Projective::rand(&mut rng);
let ci3 = NIFS::<Projective, Pedersen<Projective>>::verify(r_Fr, &ci1, &ci2, &cmT);

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

Expand Down Expand Up @@ -673,13 +692,14 @@ pub mod tests {
let pp_hash = Fr::from(42u32); // only for testing

// compute the challenge natively
let r_bits = ChallengeGadget::<Projective>::get_challenge_native(
&mut transcript,
pp_hash,
U_i.clone(),
u_i.clone(),
cmT,
);
let r_bits =
ChallengeGadget::<Projective, CommittedInstance<Projective>>::get_challenge_native(
&mut transcript,
pp_hash,
&U_i,
&u_i,
Some(&cmT),
);
let r = Fr::from_bigint(BigInteger::from_bits_le(&r_bits)).unwrap();

let cs = ConstraintSystem::<Fr>::new_ref();
Expand All @@ -701,14 +721,15 @@ pub mod tests {
U_iVar.cmW.to_constraint_field().unwrap(),
]
.concat();
let r_bitsVar = ChallengeGadget::<Projective>::get_challenge_gadget(
&mut transcriptVar,
pp_hashVar,
U_iVar_vec,
u_iVar,
cmTVar,
)
.unwrap();
let r_bitsVar =
ChallengeGadget::<Projective, CommittedInstance<Projective>>::get_challenge_gadget(
&mut transcriptVar,
pp_hashVar,
U_iVar_vec,
u_iVar,
Some(cmTVar),
)
.unwrap();
assert!(cs.is_satisfied().unwrap());

// check that the natively computed and in-circuit computed hashes match
Expand Down
2 changes: 1 addition & 1 deletion folding-schemes/src/folding/nova/decider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use ark_std::{One, Zero};
use core::marker::PhantomData;

use super::decider_circuits::{DeciderCircuit1, DeciderCircuit2};
use super::{nifs::NIFS, CommittedInstance, Nova};
use super::{nifs::NIFS, traits::NIFSTrait, CommittedInstance, Nova};
use crate::commitment::CommitmentScheme;
use crate::folding::circuits::{
cyclefold::CycleFoldCommittedInstance,
Expand Down
18 changes: 9 additions & 9 deletions folding-schemes/src/folding/nova/decider_circuits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ use super::{
circuits::{ChallengeGadget, CommittedInstanceVar},
decider_eth_circuit::{KZGChallengesGadget, R1CSVar, RelaxedR1CSGadget, WitnessVar},
nifs::NIFS,
traits::NIFSTrait,
CommittedInstance, Nova, Witness,
};
use crate::arith::r1cs::R1CS;
Expand Down Expand Up @@ -122,18 +123,17 @@ where
&nova.W_i.clone(),
&nova.U_i.clone(),
)?;
let r_bits = ChallengeGadget::<C1>::get_challenge_native(
let r_bits = NIFS::<C1, CS1, H>::get_challenge(
&mut transcript,
nova.pp_hash,
nova.U_i.clone(),
nova.u_i.clone(),
cmT,
&nova.U_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>::fold_instances(
r_Fr, &nova.W_i, &nova.U_i, &nova.w_i, &nova.u_i, &T, cmT,
)?;
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 Expand Up @@ -283,12 +283,12 @@ where
// do the actual checks later.
let cmT =
NonNativeAffineVar::new_input(cs.clone(), || Ok(self.cmT.unwrap_or_else(C1::zero)))?;
let r_bits = ChallengeGadget::<C1>::get_challenge_gadget(
let r_bits = ChallengeGadget::<C1, CommittedInstance<C1>>::get_challenge_gadget(
&mut transcript,
pp_hash,
U_i_vec,
u_i.clone(),
cmT.clone(),
Some(cmT.clone()),
)?;
// 5.1.
let (incircuit_c_W, incircuit_c_E) =
Expand Down
1 change: 1 addition & 0 deletions folding-schemes/src/folding/nova/decider_eth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use ark_std::{One, Zero};
use core::marker::PhantomData;

pub use super::decider_eth_circuit::DeciderEthCircuit;
use super::traits::NIFSTrait;
use super::{nifs::NIFS, CommittedInstance, Nova};
use crate::commitment::{
kzg::{Proof as KZGProof, KZG},
Expand Down
20 changes: 11 additions & 9 deletions folding-schemes/src/folding/nova/decider_eth_circuit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ use core::{borrow::Borrow, marker::PhantomData};
use super::{
circuits::{ChallengeGadget, CommittedInstanceVar},
nifs::NIFS,
traits::NIFSTrait,
CommittedInstance, Nova, Witness,
};
use crate::commitment::{pedersen::Params as PedersenParams, CommitmentScheme};
Expand Down Expand Up @@ -245,25 +246,26 @@ where
let mut transcript = PoseidonSponge::<C1::ScalarField>::new(&nova.poseidon_config);

// compute the U_{i+1}, W_{i+1}
let (T, cmT) = NIFS::<C1, CS1, H>::compute_cmT(
let (aux_p, aux_v) = NIFS::<C1, CS1, H>::compute_aux(
&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 = ChallengeGadget::<C1>::get_challenge_native(
let cmT = aux_v;
let r_bits = ChallengeGadget::<C1, CommittedInstance<C1>>::get_challenge_native(
&mut transcript,
nova.pp_hash,
nova.U_i.clone(),
nova.u_i.clone(),
cmT,
&nova.U_i,
&nova.u_i,
Some(&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>::fold_instances(
r_Fr, &nova.W_i, &nova.U_i, &nova.w_i, &nova.u_i, &T, cmT,
let (W_i1, U_i1) = NIFS::<C1, CS1, H>::prove(
r_Fr, &nova.W_i, &nova.U_i, &nova.w_i, &nova.u_i, &aux_p, &aux_v,
)?;

// compute the KZG challenges used as inputs in the circuit
Expand Down Expand Up @@ -483,12 +485,12 @@ where
let cmT =
NonNativeAffineVar::new_input(cs.clone(), || Ok(self.cmT.unwrap_or_else(C1::zero)))?;
// 1.1.a
let r_bits = ChallengeGadget::<C1>::get_challenge_gadget(
let r_bits = ChallengeGadget::<C1, CommittedInstance<C1>>::get_challenge_gadget(
&mut transcript,
pp_hash,
U_i_vec,
u_i.clone(),
cmT.clone(),
Some(cmT),
)?;
// 5.1.
let (incircuit_c_W, incircuit_c_E) =
Expand Down
Loading

0 comments on commit a07e17e

Please sign in to comment.