Browse Source

add hash of public params for Nova & HyperNova (#118)

- implement hash of public params for Nova & HyperNova
- abstract pp_hash computation for folding schemes
- add pp_hash to solidity contract generator to verify the decider proof
main
arnaucube 10 months ago
committed by GitHub
parent
commit
c17fcf56c6
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
33 changed files with 666 additions and 407 deletions
  1. +3
    -3
      examples/circom_full_flow.rs
  2. +4
    -3
      examples/external_inputs.rs
  3. +3
    -3
      examples/full_flow.rs
  4. +4
    -3
      examples/multi_inputs.rs
  5. +4
    -3
      examples/sha256.rs
  6. +1
    -0
      folding-schemes/Cargo.toml
  7. +16
    -5
      folding-schemes/src/arith/ccs.rs
  8. +15
    -0
      folding-schemes/src/arith/mod.rs
  9. +25
    -14
      folding-schemes/src/arith/r1cs.rs
  10. +2
    -1
      folding-schemes/src/commitment/mod.rs
  11. +25
    -7
      folding-schemes/src/folding/circuits/cyclefold.rs
  12. +2
    -2
      folding-schemes/src/folding/hypernova/cccs.rs
  13. +80
    -23
      folding-schemes/src/folding/hypernova/circuits.rs
  14. +6
    -4
      folding-schemes/src/folding/hypernova/lcccs.rs
  15. +86
    -47
      folding-schemes/src/folding/hypernova/mod.rs
  16. +5
    -2
      folding-schemes/src/folding/hypernova/nimfs.rs
  17. +5
    -2
      folding-schemes/src/folding/hypernova/utils.rs
  18. +37
    -9
      folding-schemes/src/folding/nova/circuits.rs
  19. +14
    -42
      folding-schemes/src/folding/nova/decider_eth.rs
  20. +39
    -37
      folding-schemes/src/folding/nova/decider_eth_circuit.rs
  21. +65
    -20
      folding-schemes/src/folding/nova/mod.rs
  22. +4
    -2
      folding-schemes/src/folding/nova/nifs.rs
  23. +16
    -30
      folding-schemes/src/folding/nova/serialize.rs
  24. +1
    -1
      folding-schemes/src/folding/nova/traits.rs
  25. +2
    -2
      folding-schemes/src/folding/protogalaxy/folding.rs
  26. +2
    -2
      folding-schemes/src/lib.rs
  27. +1
    -1
      folding-schemes/src/utils/mle.rs
  28. +68
    -0
      folding-schemes/src/utils/mod.rs
  29. +6
    -6
      solidity-verifiers/src/verifiers/g16.rs
  30. +3
    -3
      solidity-verifiers/src/verifiers/kzg.rs
  31. +3
    -1
      solidity-verifiers/src/verifiers/mod.rs
  32. +103
    -114
      solidity-verifiers/src/verifiers/nova_cyclefold.rs
  33. +16
    -15
      solidity-verifiers/templates/nova_cyclefold_decider.askama.sol

+ 3
- 3
examples/circom_full_flow.rs

@ -82,13 +82,13 @@ fn main() {
// prepare the Nova prover & verifier params // prepare the Nova prover & verifier params
let nova_preprocess_params = PreprocessorParam::new(poseidon_config, f_circuit.clone()); let nova_preprocess_params = PreprocessorParam::new(poseidon_config, f_circuit.clone());
let (fs_pp, fs_vp) = N::preprocess(&mut rng, &nova_preprocess_params).unwrap();
let nova_params = N::preprocess(&mut rng, &nova_preprocess_params).unwrap();
// initialize the folding scheme engine, in our case we use Nova // initialize the folding scheme engine, in our case we use Nova
let mut nova = N::init(&fs_pp, f_circuit.clone(), z_0).unwrap();
let mut nova = N::init(nova_params.clone(), f_circuit.clone(), z_0).unwrap();
// prepare the Decider prover & verifier params // prepare the Decider prover & verifier params
let (decider_pp, decider_vp) = D::preprocess(&mut rng, &(fs_pp, fs_vp), nova.clone()).unwrap();
let (decider_pp, decider_vp) = D::preprocess(&mut rng, &nova_params, nova.clone()).unwrap();
// run n steps of the folding iteration // run n steps of the folding iteration
for (i, external_inputs_at_step) in external_inputs.iter().enumerate() { for (i, external_inputs_at_step) in external_inputs.iter().enumerate() {

+ 4
- 3
examples/external_inputs.rs

@ -187,10 +187,11 @@ fn main() {
println!("Prepare Nova's ProverParams & VerifierParams"); println!("Prepare Nova's ProverParams & VerifierParams");
let nova_preprocess_params = PreprocessorParam::new(poseidon_config, F_circuit.clone()); let nova_preprocess_params = PreprocessorParam::new(poseidon_config, F_circuit.clone());
let (nova_pp, nova_vp) = N::preprocess(&mut rng, &nova_preprocess_params).unwrap();
let nova_params = N::preprocess(&mut rng, &nova_preprocess_params).unwrap();
println!("Initialize FoldingScheme"); println!("Initialize FoldingScheme");
let mut folding_scheme = N::init(&nova_pp, F_circuit, initial_state.clone()).unwrap();
let mut folding_scheme =
N::init(nova_params.clone(), F_circuit, initial_state.clone()).unwrap();
// compute a step of the IVC // compute a step of the IVC
for (i, external_inputs_at_step) in external_inputs.iter().enumerate() { for (i, external_inputs_at_step) in external_inputs.iter().enumerate() {
@ -210,7 +211,7 @@ fn main() {
println!("Run the Nova's IVC verifier"); println!("Run the Nova's IVC verifier");
N::verify( N::verify(
nova_vp,
nova_params.1,
initial_state.clone(), initial_state.clone(),
folding_scheme.state(), // latest state folding_scheme.state(), // latest state
Fr::from(num_steps as u32), Fr::from(num_steps as u32),

+ 3
- 3
examples/full_flow.rs

@ -99,13 +99,13 @@ fn main() {
// prepare the Nova prover & verifier params // prepare the Nova prover & verifier params
let nova_preprocess_params = PreprocessorParam::new(poseidon_config.clone(), f_circuit); let nova_preprocess_params = PreprocessorParam::new(poseidon_config.clone(), f_circuit);
let (fs_pp, fs_vp) = N::preprocess(&mut rng, &nova_preprocess_params).unwrap();
let nova_params = N::preprocess(&mut rng, &nova_preprocess_params).unwrap();
// initialize the folding scheme engine, in our case we use Nova // initialize the folding scheme engine, in our case we use Nova
let mut nova = N::init(&fs_pp, f_circuit, z_0).unwrap();
let mut nova = N::init(nova_params.clone(), f_circuit, z_0).unwrap();
// prepare the Decider prover & verifier params // prepare the Decider prover & verifier params
let (decider_pp, decider_vp) = D::preprocess(&mut rng, &(fs_pp, fs_vp), nova.clone()).unwrap();
let (decider_pp, decider_vp) = D::preprocess(&mut rng, &nova_params, nova.clone()).unwrap();
// run n steps of the folding iteration // run n steps of the folding iteration
for i in 0..n_steps { for i in 0..n_steps {

+ 4
- 3
examples/multi_inputs.rs

@ -141,10 +141,11 @@ fn main() {
println!("Prepare Nova ProverParams & VerifierParams"); println!("Prepare Nova ProverParams & VerifierParams");
let nova_preprocess_params = PreprocessorParam::new(poseidon_config, F_circuit); let nova_preprocess_params = PreprocessorParam::new(poseidon_config, F_circuit);
let (nova_pp, nova_vp) = N::preprocess(&mut rng, &nova_preprocess_params).unwrap();
let nova_params = N::preprocess(&mut rng, &nova_preprocess_params).unwrap();
println!("Initialize FoldingScheme"); println!("Initialize FoldingScheme");
let mut folding_scheme = N::init(&nova_pp, F_circuit, initial_state.clone()).unwrap();
let mut folding_scheme =
N::init(nova_params.clone(), F_circuit, initial_state.clone()).unwrap();
// compute a step of the IVC // compute a step of the IVC
for i in 0..num_steps { for i in 0..num_steps {
@ -157,7 +158,7 @@ fn main() {
println!("Run the Nova's IVC verifier"); println!("Run the Nova's IVC verifier");
N::verify( N::verify(
nova_vp,
nova_params.1,
initial_state.clone(), initial_state.clone(),
folding_scheme.state(), // latest state folding_scheme.state(), // latest state
Fr::from(num_steps as u32), Fr::from(num_steps as u32),

+ 4
- 3
examples/sha256.rs

@ -126,10 +126,11 @@ fn main() {
println!("Prepare Nova ProverParams & VerifierParams"); println!("Prepare Nova ProverParams & VerifierParams");
let nova_preprocess_params = PreprocessorParam::new(poseidon_config, F_circuit); let nova_preprocess_params = PreprocessorParam::new(poseidon_config, F_circuit);
let (nova_pp, nova_vp) = N::preprocess(&mut rng, &nova_preprocess_params).unwrap();
let nova_params = N::preprocess(&mut rng, &nova_preprocess_params).unwrap();
println!("Initialize FoldingScheme"); println!("Initialize FoldingScheme");
let mut folding_scheme = N::init(&nova_pp, F_circuit, initial_state.clone()).unwrap();
let mut folding_scheme =
N::init(nova_params.clone(), F_circuit, initial_state.clone()).unwrap();
// compute a step of the IVC // compute a step of the IVC
for i in 0..num_steps { for i in 0..num_steps {
let start = Instant::now(); let start = Instant::now();
@ -141,7 +142,7 @@ fn main() {
println!("Run the Nova's IVC verifier"); println!("Run the Nova's IVC verifier");
N::verify( N::verify(
nova_vp,
nova_params.1,
initial_state, initial_state,
folding_scheme.state(), // latest state folding_scheme.state(), // latest state
Fr::from(num_steps as u32), Fr::from(num_steps as u32),

+ 1
- 0
folding-schemes/Cargo.toml

@ -23,6 +23,7 @@ num-integer = "0.1"
color-eyre = "=0.6.2" color-eyre = "=0.6.2"
ark-bn254 = {version="0.4.0"} ark-bn254 = {version="0.4.0"}
ark-groth16 = { version = "^0.4.0" } ark-groth16 = { version = "^0.4.0" }
sha3 = "0.10"
# tmp imports for espresso's sumcheck # tmp imports for espresso's sumcheck
espresso_subroutines = {git="https://github.com/EspressoSystems/hyperplonk", package="subroutines"} espresso_subroutines = {git="https://github.com/EspressoSystems/hyperplonk", package="subroutines"}

folding-schemes/src/ccs/mod.rs → folding-schemes/src/arith/ccs.rs

@ -4,8 +4,7 @@ use ark_std::log2;
use crate::utils::vec::{hadamard, mat_vec_mul, vec_add, vec_scalar_mul, SparseMatrix}; use crate::utils::vec::{hadamard, mat_vec_mul, vec_add, vec_scalar_mul, SparseMatrix};
use crate::Error; use crate::Error;
pub mod r1cs;
use r1cs::R1CS;
use super::{r1cs::R1CS, Arith};
/// CCS represents the Customizable Constraint Systems structure defined in /// CCS represents the Customizable Constraint Systems structure defined in
/// the [CCS paper](https://eprint.iacr.org/2023/552) /// the [CCS paper](https://eprint.iacr.org/2023/552)
@ -36,9 +35,9 @@ pub struct CCS {
pub c: Vec<F>, pub c: Vec<F>,
} }
impl<F: PrimeField> CCS<F> {
impl<F: PrimeField> Arith<F> for CCS<F> {
/// check that a CCS structure is satisfied by a z vector. Only for testing. /// check that a CCS structure is satisfied by a z vector. Only for testing.
pub fn check_relation(&self, z: &[F]) -> Result<(), Error> {
fn check_relation(&self, z: &[F]) -> Result<(), Error> {
let mut result = vec![F::zero(); self.m]; let mut result = vec![F::zero(); self.m];
for i in 0..self.q { for i in 0..self.q {
@ -67,6 +66,18 @@ impl CCS {
Ok(()) Ok(())
} }
fn params_to_bytes(&self) -> Vec<u8> {
[
self.l.to_le_bytes(),
self.m.to_le_bytes(),
self.n.to_le_bytes(),
self.t.to_le_bytes(),
self.q.to_le_bytes(),
self.d.to_le_bytes(),
]
.concat()
}
} }
impl<F: PrimeField> CCS<F> { impl<F: PrimeField> CCS<F> {
@ -102,7 +113,7 @@ impl CCS {
#[cfg(test)] #[cfg(test)]
pub mod tests { pub mod tests {
use super::*; use super::*;
use crate::ccs::r1cs::tests::{get_test_r1cs, get_test_z as r1cs_get_test_z};
use crate::arith::r1cs::tests::{get_test_r1cs, get_test_z as r1cs_get_test_z};
use ark_ff::PrimeField; use ark_ff::PrimeField;
use ark_pallas::Fr; use ark_pallas::Fr;

+ 15
- 0
folding-schemes/src/arith/mod.rs

@ -0,0 +1,15 @@
use ark_ff::PrimeField;
use crate::Error;
pub mod ccs;
pub mod r1cs;
pub trait Arith<F: PrimeField> {
/// Checks that the given Arith structure is satisfied by a z vector. Used only for testing.
fn check_relation(&self, z: &[F]) -> Result<(), Error>;
/// Returns the bytes that represent the parameters, that is, the matrices sizes, the amount of
/// public inputs, etc, without the matrices/polynomials values.
fn params_to_bytes(&self) -> Vec<u8>;
}

folding-schemes/src/ccs/r1cs.rs → folding-schemes/src/arith/r1cs.rs

@ -1,10 +1,11 @@
use ark_ff::PrimeField; use ark_ff::PrimeField;
use ark_relations::r1cs::ConstraintSystem; use ark_relations::r1cs::ConstraintSystem;
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
use ark_std::rand::Rng; use ark_std::rand::Rng;
use super::Arith;
use crate::utils::vec::{hadamard, mat_vec_mul, vec_add, vec_scalar_mul, SparseMatrix}; use crate::utils::vec::{hadamard, mat_vec_mul, vec_add, vec_scalar_mul, SparseMatrix};
use crate::Error; use crate::Error;
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
#[derive(Debug, Clone, Eq, PartialEq, CanonicalSerialize, CanonicalDeserialize)] #[derive(Debug, Clone, Eq, PartialEq, CanonicalSerialize, CanonicalDeserialize)]
pub struct R1CS<F: PrimeField> { pub struct R1CS<F: PrimeField> {
@ -14,6 +15,29 @@ pub struct R1CS {
pub C: SparseMatrix<F>, pub C: SparseMatrix<F>,
} }
impl<F: PrimeField> Arith<F> for R1CS<F> {
/// check that a R1CS structure is satisfied by a z vector. Only for testing.
fn check_relation(&self, z: &[F]) -> Result<(), Error> {
let Az = mat_vec_mul(&self.A, z)?;
let Bz = mat_vec_mul(&self.B, z)?;
let Cz = mat_vec_mul(&self.C, z)?;
let AzBz = hadamard(&Az, &Bz)?;
if AzBz != Cz {
return Err(Error::NotSatisfied);
}
Ok(())
}
fn params_to_bytes(&self) -> Vec<u8> {
[
self.l.to_le_bytes(),
self.A.n_rows.to_le_bytes(),
self.A.n_cols.to_le_bytes(),
]
.concat()
}
}
impl<F: PrimeField> R1CS<F> { impl<F: PrimeField> R1CS<F> {
pub fn rand<R: Rng>(rng: &mut R, n_rows: usize, n_cols: usize) -> Self { pub fn rand<R: Rng>(rng: &mut R, n_rows: usize, n_cols: usize) -> Self {
Self { Self {
@ -29,19 +53,6 @@ impl R1CS {
(z[self.l + 1..].to_vec(), z[1..self.l + 1].to_vec()) (z[self.l + 1..].to_vec(), z[1..self.l + 1].to_vec())
} }
/// check that a R1CS structure is satisfied by a z vector. Only for testing.
pub fn check_relation(&self, z: &[F]) -> Result<(), Error> {
let Az = mat_vec_mul(&self.A, z)?;
let Bz = mat_vec_mul(&self.B, z)?;
let Cz = mat_vec_mul(&self.C, z)?;
let AzBz = hadamard(&Az, &Bz)?;
if AzBz != Cz {
return Err(Error::NotSatisfied);
}
Ok(())
}
/// converts the R1CS instance into a RelaxedR1CS as described in /// converts the R1CS instance into a RelaxedR1CS as described in
/// [Nova](https://eprint.iacr.org/2021/370.pdf) section 4.1. /// [Nova](https://eprint.iacr.org/2021/370.pdf) section 4.1.
pub fn relax(self) -> RelaxedR1CS<F> { pub fn relax(self) -> RelaxedR1CS<F> {

+ 2
- 1
folding-schemes/src/commitment/mod.rs

@ -1,4 +1,5 @@
use ark_ec::CurveGroup; use ark_ec::CurveGroup;
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
use ark_std::fmt::Debug; use ark_std::fmt::Debug;
use ark_std::rand::RngCore; use ark_std::rand::RngCore;
@ -13,7 +14,7 @@ pub mod pedersen;
/// commitment in hiding mode or not. /// commitment in hiding mode or not.
pub trait CommitmentScheme<C: CurveGroup, const H: bool = false>: Clone + Debug { pub trait CommitmentScheme<C: CurveGroup, const H: bool = false>: Clone + Debug {
type ProverParams: Clone + Debug; type ProverParams: Clone + Debug;
type VerifierParams: Clone + Debug;
type VerifierParams: Clone + Debug + CanonicalSerialize + CanonicalDeserialize;
type Proof: Clone + Debug; type Proof: Clone + Debug;
type ProverChallenge: Clone + Debug; type ProverChallenge: Clone + Debug;
type Challenge: Clone + Debug; type Challenge: Clone + Debug;

+ 25
- 7
folding-schemes/src/folding/circuits/cyclefold.rs

@ -30,7 +30,7 @@ use ark_std::{One, Zero};
use core::{borrow::Borrow, marker::PhantomData}; use core::{borrow::Borrow, marker::PhantomData};
use super::{nonnative::uint::NonNativeUintVar, CF2}; use super::{nonnative::uint::NonNativeUintVar, CF2};
use crate::ccs::r1cs::{extract_w_x, R1CS};
use crate::arith::r1cs::{extract_w_x, R1CS};
use crate::commitment::CommitmentScheme; use crate::commitment::CommitmentScheme;
use crate::constants::N_BITS_RO; use crate::constants::N_BITS_RO;
use crate::folding::nova::{nifs::NIFS, CommittedInstance, Witness}; use crate::folding::nova::{nifs::NIFS, CommittedInstance, Witness};
@ -127,9 +127,13 @@ where
pub fn hash( pub fn hash(
self, self,
crh_params: &CRHParametersVar<CF2<C>>, crh_params: &CRHParametersVar<CF2<C>>,
pp_hash: FpVar<CF2<C>>, // public params hash
) -> Result<(FpVar<CF2<C>>, Vec<FpVar<CF2<C>>>), SynthesisError> { ) -> Result<(FpVar<CF2<C>>, Vec<FpVar<CF2<C>>>), SynthesisError> {
let U_vec = self.to_constraint_field()?; let U_vec = self.to_constraint_field()?;
Ok((CRHGadget::evaluate(crh_params, &U_vec)?, U_vec))
Ok((
CRHGadget::evaluate(crh_params, &[vec![pp_hash], U_vec.clone()].concat())?,
U_vec,
))
} }
} }
@ -252,6 +256,7 @@ where
{ {
pub fn get_challenge_native( pub fn get_challenge_native(
poseidon_config: &PoseidonConfig<C::BaseField>, poseidon_config: &PoseidonConfig<C::BaseField>,
pp_hash: C::BaseField, // public params hash
U_i: CommittedInstance<C>, U_i: CommittedInstance<C>,
u_i: CommittedInstance<C>, u_i: CommittedInstance<C>,
cmT: C, cmT: C,
@ -276,7 +281,7 @@ where
// to save constraints for sponge.squeeze_bits in the corresponding circuit // to save constraints for sponge.squeeze_bits in the corresponding circuit
let is_inf = U_cm_is_inf * CF2::<C>::from(8u8) + u_cm_is_inf.double() + cmT_is_inf; let is_inf = U_cm_is_inf * CF2::<C>::from(8u8) + u_cm_is_inf.double() + cmT_is_inf;
let input = [U_vec, u_vec, vec![cmT_x, cmT_y, is_inf]].concat();
let input = [vec![pp_hash], U_vec, u_vec, vec![cmT_x, cmT_y, is_inf]].concat();
sponge.absorb(&input); sponge.absorb(&input);
let bits = sponge.squeeze_bits(N_BITS_RO); let bits = sponge.squeeze_bits(N_BITS_RO);
Ok(bits) Ok(bits)
@ -286,6 +291,7 @@ where
pub fn get_challenge_gadget( pub fn get_challenge_gadget(
cs: ConstraintSystemRef<C::BaseField>, cs: ConstraintSystemRef<C::BaseField>,
poseidon_config: &PoseidonConfig<C::BaseField>, poseidon_config: &PoseidonConfig<C::BaseField>,
pp_hash: FpVar<C::BaseField>, // public params hash
mut U_i_vec: Vec<FpVar<C::BaseField>>, mut U_i_vec: Vec<FpVar<C::BaseField>>,
u_i: CycleFoldCommittedInstanceVar<C, GC>, u_i: CycleFoldCommittedInstanceVar<C, GC>,
cmT: GC, cmT: GC,
@ -303,7 +309,7 @@ where
// to save constraints for sponge.squeeze_bits // to save constraints for sponge.squeeze_bits
let is_inf = U_cm_is_inf * CF2::<C>::from(8u8) + u_cm_is_inf.double()? + cmT_is_inf; let is_inf = U_cm_is_inf * CF2::<C>::from(8u8) + u_cm_is_inf.double()? + cmT_is_inf;
let input = [U_i_vec, u_i_vec, cmT_vec, vec![is_inf]].concat();
let input = [vec![pp_hash], U_i_vec, u_i_vec, cmT_vec, vec![is_inf]].concat();
sponge.absorb(&input)?; sponge.absorb(&input)?;
let bits = sponge.squeeze_bits(N_BITS_RO)?; let bits = sponge.squeeze_bits(N_BITS_RO)?;
Ok(bits) Ok(bits)
@ -372,13 +378,15 @@ where
} }
} }
/// Folds the given cyclefold circuit and its instances. This method is isolated from any folding
/// 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. /// scheme struct because it is used both by Nova & HyperNova's CycleFold.
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
#[allow(clippy::too_many_arguments)]
pub fn fold_cyclefold_circuit<C1, GC1, C2, GC2, FC, CS1, CS2>( pub fn fold_cyclefold_circuit<C1, GC1, C2, GC2, FC, CS1, CS2>(
poseidon_config: &PoseidonConfig<C1::ScalarField>, poseidon_config: &PoseidonConfig<C1::ScalarField>,
cf_r1cs: R1CS<C2::ScalarField>, cf_r1cs: R1CS<C2::ScalarField>,
cf_cs_params: CS2::ProverParams, cf_cs_params: CS2::ProverParams,
pp_hash: C1::ScalarField, // public params hash
cf_W_i: Witness<C2>, // witness of the running instance cf_W_i: Witness<C2>, // witness of the running instance
cf_U_i: CommittedInstance<C2>, // running instance cf_U_i: CommittedInstance<C2>, // running instance
cf_u_i_x: Vec<C2::ScalarField>, cf_u_i_x: Vec<C2::ScalarField>,
@ -438,6 +446,7 @@ where
let cf_r_bits = CycleFoldChallengeGadget::<C2, GC2>::get_challenge_native( let cf_r_bits = CycleFoldChallengeGadget::<C2, GC2>::get_challenge_native(
poseidon_config, poseidon_config,
pp_hash,
cf_U_i.clone(), cf_U_i.clone(),
cf_u_i.clone(), cf_u_i.clone(),
cf_cmT, cf_cmT,
@ -594,8 +603,10 @@ pub mod tests {
let cmT = Projective::rand(&mut rng); let cmT = Projective::rand(&mut rng);
// compute the challenge natively // compute the challenge natively
let pp_hash = Fq::from(42u32); // only for test
let r_bits = CycleFoldChallengeGadget::<Projective, GVar>::get_challenge_native( let r_bits = CycleFoldChallengeGadget::<Projective, GVar>::get_challenge_native(
&poseidon_config, &poseidon_config,
pp_hash,
U_i.clone(), U_i.clone(),
u_i.clone(), u_i.clone(),
cmT, cmT,
@ -615,9 +626,11 @@ pub mod tests {
.unwrap(); .unwrap();
let cmTVar = GVar::new_witness(cs.clone(), || Ok(cmT)).unwrap(); let cmTVar = GVar::new_witness(cs.clone(), || Ok(cmT)).unwrap();
let pp_hashVar = FpVar::<Fq>::new_witness(cs.clone(), || Ok(pp_hash)).unwrap();
let r_bitsVar = CycleFoldChallengeGadget::<Projective, GVar>::get_challenge_gadget( let r_bitsVar = CycleFoldChallengeGadget::<Projective, GVar>::get_challenge_gadget(
cs.clone(), cs.clone(),
&poseidon_config, &poseidon_config,
pp_hashVar,
U_iVar.to_constraint_field().unwrap(), U_iVar.to_constraint_field().unwrap(),
u_iVar, u_iVar,
cmTVar, cmTVar,
@ -645,7 +658,8 @@ pub mod tests {
.take(CF_IO_LEN) .take(CF_IO_LEN)
.collect(), .collect(),
}; };
let h = U_i.hash_cyclefold(&poseidon_config).unwrap();
let pp_hash = Fq::from(42u32); // only for test
let h = U_i.hash_cyclefold(&poseidon_config, pp_hash).unwrap();
let cs = ConstraintSystem::<Fq>::new_ref(); let cs = ConstraintSystem::<Fq>::new_ref();
let U_iVar = let U_iVar =
@ -653,8 +667,12 @@ pub mod tests {
Ok(U_i.clone()) Ok(U_i.clone())
}) })
.unwrap(); .unwrap();
let pp_hashVar = FpVar::<Fq>::new_witness(cs.clone(), || Ok(pp_hash)).unwrap();
let (hVar, _) = U_iVar let (hVar, _) = U_iVar
.hash(&CRHParametersVar::new_constant(cs.clone(), poseidon_config).unwrap())
.hash(
&CRHParametersVar::new_constant(cs.clone(), poseidon_config).unwrap(),
pp_hashVar,
)
.unwrap(); .unwrap();
hVar.enforce_equal(&FpVar::new_witness(cs.clone(), || Ok(h)).unwrap()) hVar.enforce_equal(&FpVar::new_witness(cs.clone(), || Ok(h)).unwrap())
.unwrap(); .unwrap();

+ 2
- 2
folding-schemes/src/folding/hypernova/cccs.rs

@ -7,7 +7,7 @@ use std::sync::Arc;
use ark_std::rand::Rng; use ark_std::rand::Rng;
use super::Witness; use super::Witness;
use crate::ccs::CCS;
use crate::arith::{ccs::CCS, Arith};
use crate::commitment::CommitmentScheme; use crate::commitment::CommitmentScheme;
use crate::utils::mle::dense_vec_to_dense_mle; use crate::utils::mle::dense_vec_to_dense_mle;
use crate::utils::vec::mat_vec_mul; use crate::utils::vec::mat_vec_mul;
@ -125,7 +125,7 @@ pub mod tests {
use ark_std::UniformRand; use ark_std::UniformRand;
use super::*; use super::*;
use crate::ccs::tests::{get_test_ccs, get_test_z};
use crate::arith::ccs::tests::{get_test_ccs, get_test_z};
use crate::utils::hypercube::BooleanHypercube; use crate::utils::hypercube::BooleanHypercube;
/// Do some sanity checks on q(x). It's a multivariable polynomial and it should evaluate to zero inside the /// Do some sanity checks on q(x). It's a multivariable polynomial and it should evaluate to zero inside the

+ 80
- 23
folding-schemes/src/folding/hypernova/circuits.rs

@ -44,7 +44,7 @@ use crate::frontend::FCircuit;
use crate::utils::virtual_polynomial::VPAuxInfo; use crate::utils::virtual_polynomial::VPAuxInfo;
use crate::Error; use crate::Error;
use crate::{ use crate::{
ccs::{r1cs::extract_r1cs, CCS},
arith::{ccs::CCS, r1cs::extract_r1cs},
transcript::{ transcript::{
poseidon::{PoseidonTranscript, PoseidonTranscriptVar}, poseidon::{PoseidonTranscript, PoseidonTranscriptVar},
Transcript, TranscriptVar, Transcript, TranscriptVar,
@ -143,6 +143,7 @@ where
pub fn hash( pub fn hash(
self, self,
crh_params: &CRHParametersVar<CF1<C>>, crh_params: &CRHParametersVar<CF1<C>>,
pp_hash: FpVar<CF1<C>>,
i: FpVar<CF1<C>>, i: FpVar<CF1<C>>,
z_0: Vec<FpVar<CF1<C>>>, z_0: Vec<FpVar<CF1<C>>>,
z_i: Vec<FpVar<CF1<C>>>, z_i: Vec<FpVar<CF1<C>>>,
@ -155,7 +156,7 @@ where
self.v, self.v,
] ]
.concat(); .concat();
let input = [vec![i], z_0, z_i, U_vec.clone()].concat();
let input = [vec![pp_hash, i], z_0, z_i, U_vec.clone()].concat();
Ok(( Ok((
CRHGadget::<C::ScalarField>::evaluate(crh_params, &input)?, CRHGadget::<C::ScalarField>::evaluate(crh_params, &input)?,
U_vec, U_vec,
@ -455,6 +456,7 @@ pub struct AugmentedFCircuit<
pub _gc2: PhantomData<GC2>, pub _gc2: PhantomData<GC2>,
pub poseidon_config: PoseidonConfig<CF1<C1>>, pub poseidon_config: PoseidonConfig<CF1<C1>>,
pub ccs: CCS<C1::ScalarField>, // CCS of the AugmentedFCircuit pub ccs: CCS<C1::ScalarField>, // CCS of the AugmentedFCircuit
pub pp_hash: Option<CF1<C1>>,
pub i: Option<CF1<C1>>, pub i: Option<CF1<C1>>,
pub i_usize: Option<usize>, pub i_usize: Option<usize>,
pub z_0: Option<Vec<C1::ScalarField>>, pub z_0: Option<Vec<C1::ScalarField>>,
@ -497,6 +499,7 @@ where
_gc2: PhantomData, _gc2: PhantomData,
poseidon_config: poseidon_config.clone(), poseidon_config: poseidon_config.clone(),
ccs, ccs,
pp_hash: None,
i: None, i: None,
i_usize: None, i_usize: None,
z_0: None, z_0: None,
@ -559,6 +562,7 @@ where
let mut transcript_p: PoseidonTranscript<C1> = let mut transcript_p: PoseidonTranscript<C1> =
PoseidonTranscript::<C1>::new(&self.poseidon_config.clone()); PoseidonTranscript::<C1>::new(&self.poseidon_config.clone());
// since this is only for the number of constraints, no need to absorb the pp_hash here
let (nimfs_proof, U_i1, _, _) = NIMFS::<C1, PoseidonTranscript<C1>>::prove( let (nimfs_proof, U_i1, _, _) = NIMFS::<C1, PoseidonTranscript<C1>>::prove(
&mut transcript_p, &mut transcript_p,
&ccs, &ccs,
@ -573,6 +577,7 @@ where
_gc2: PhantomData, _gc2: PhantomData,
poseidon_config: self.poseidon_config.clone(), poseidon_config: self.poseidon_config.clone(),
ccs: ccs.clone(), ccs: ccs.clone(),
pp_hash: Some(C1::ScalarField::zero()),
i: Some(C1::ScalarField::zero()), i: Some(C1::ScalarField::zero()),
i_usize: Some(0), i_usize: Some(0),
z_0: Some(z_0.clone()), z_0: Some(z_0.clone()),
@ -624,6 +629,9 @@ where
for<'a> &'a GC2: GroupOpsBounds<'a, C2, GC2>, for<'a> &'a GC2: GroupOpsBounds<'a, C2, GC2>,
{ {
fn generate_constraints(self, cs: ConstraintSystemRef<CF1<C1>>) -> Result<(), SynthesisError> { fn generate_constraints(self, cs: ConstraintSystemRef<CF1<C1>>) -> Result<(), SynthesisError> {
let pp_hash = FpVar::<CF1<C1>>::new_witness(cs.clone(), || {
Ok(self.pp_hash.unwrap_or_else(CF1::<C1>::zero))
})?;
let i = FpVar::<CF1<C1>>::new_witness(cs.clone(), || { let i = FpVar::<CF1<C1>>::new_witness(cs.clone(), || {
Ok(self.i.unwrap_or_else(CF1::<C1>::zero)) Ok(self.i.unwrap_or_else(CF1::<C1>::zero))
})?; })?;
@ -680,11 +688,15 @@ where
// Primary Part // Primary Part
// P.1. Compute u_i.x // P.1. Compute u_i.x
// u_i.x[0] = H(i, z_0, z_i, U_i) // u_i.x[0] = H(i, z_0, z_i, U_i)
let (u_i_x, _) = U_i
.clone()
.hash(&crh_params, i.clone(), z_0.clone(), z_i.clone())?;
let (u_i_x, _) = U_i.clone().hash(
&crh_params,
pp_hash.clone(),
i.clone(),
z_0.clone(),
z_i.clone(),
)?;
// u_i.x[1] = H(cf_U_i) // u_i.x[1] = H(cf_U_i)
let (cf_u_i_x, cf_U_i_vec) = cf_U_i.clone().hash(&crh_params)?;
let (cf_u_i_x, cf_U_i_vec) = cf_U_i.clone().hash(&crh_params, pp_hash.clone())?;
// P.2. Construct u_i // P.2. Construct u_i
let u_i = CCCSVar::<C1> { let u_i = CCCSVar::<C1> {
@ -700,8 +712,9 @@ where
// Notice that NIMFSGadget::fold_committed_instance does not fold C. We set `U_i1.C` to // Notice that NIMFSGadget::fold_committed_instance does not fold C. We set `U_i1.C` to
// unconstrained witnesses `U_i1_C` respectively. Its correctness will be checked on the // unconstrained witnesses `U_i1_C` respectively. Its correctness will be checked on the
// other curve. // other curve.
let transcript =
let mut transcript =
PoseidonTranscriptVar::<C1::ScalarField>::new(cs.clone(), &self.poseidon_config); PoseidonTranscriptVar::<C1::ScalarField>::new(cs.clone(), &self.poseidon_config);
transcript.absorb(pp_hash.clone())?;
let (mut U_i1, rho_bits) = NIMFSGadget::<C1>::verify( let (mut U_i1, rho_bits) = NIMFSGadget::<C1>::verify(
cs.clone(), cs.clone(),
&self.ccs.clone(), &self.ccs.clone(),
@ -716,12 +729,14 @@ where
// P.4.a compute and check the first output of F' // P.4.a compute and check the first output of F'
let (u_i1_x, _) = U_i1.clone().hash( let (u_i1_x, _) = U_i1.clone().hash(
&crh_params, &crh_params,
pp_hash.clone(),
i + FpVar::<CF1<C1>>::one(), i + FpVar::<CF1<C1>>::one(),
z_0.clone(), z_0.clone(),
z_i1.clone(), z_i1.clone(),
)?; )?;
let (u_i1_x_base, _) = LCCCSVar::new_constant(cs.clone(), U_dummy)?.hash( let (u_i1_x_base, _) = LCCCSVar::new_constant(cs.clone(), U_dummy)?.hash(
&crh_params, &crh_params,
pp_hash.clone(),
FpVar::<CF1<C1>>::one(), FpVar::<CF1<C1>>::one(),
z_0.clone(), z_0.clone(),
z_i1.clone(), z_i1.clone(),
@ -763,6 +778,7 @@ where
let cf_r_bits = CycleFoldChallengeGadget::<C2, GC2>::get_challenge_gadget( let cf_r_bits = CycleFoldChallengeGadget::<C2, GC2>::get_challenge_gadget(
cs.clone(), cs.clone(),
&self.poseidon_config, &self.poseidon_config,
pp_hash.clone(),
cf_U_i_vec, cf_U_i_vec,
cf_u_i.clone(), cf_u_i.clone(),
cf_cmT.clone(), cf_cmT.clone(),
@ -786,10 +802,10 @@ where
// P.4.b compute and check the second output of F' // P.4.b compute and check the second output of F'
// Base case: u_{i+1}.x[1] == H(cf_U_{\bot}) // Base case: u_{i+1}.x[1] == H(cf_U_{\bot})
// Non-base case: u_{i+1}.x[1] == H(cf_U_{i+1}) // Non-base case: u_{i+1}.x[1] == H(cf_U_{i+1})
let (cf_u_i1_x, _) = cf_U_i1.clone().hash(&crh_params)?;
let (cf_u_i1_x, _) = cf_U_i1.clone().hash(&crh_params, pp_hash.clone())?;
let (cf_u_i1_x_base, _) = let (cf_u_i1_x_base, _) =
CycleFoldCommittedInstanceVar::new_constant(cs.clone(), cf_u_dummy)? CycleFoldCommittedInstanceVar::new_constant(cs.clone(), cf_u_dummy)?
.hash(&crh_params)?;
.hash(&crh_params, pp_hash)?;
let cf_x = FpVar::new_input(cs.clone(), || { let cf_x = FpVar::new_input(cs.clone(), || {
Ok(self.cf_x.unwrap_or(cf_u_i1_x_base.value()?)) Ok(self.cf_x.unwrap_or(cf_u_i1_x_base.value()?))
})?; })?;
@ -810,10 +826,12 @@ mod tests {
use super::*; use super::*;
use crate::{ use crate::{
ccs::{
arith::{
ccs::{
tests::{get_test_ccs, get_test_z},
CCS,
},
r1cs::extract_w_x, r1cs::extract_w_x,
tests::{get_test_ccs, get_test_z},
CCS,
}, },
commitment::{pedersen::Pedersen, CommitmentScheme}, commitment::{pedersen::Pedersen, CommitmentScheme},
folding::{ folding::{
@ -1049,6 +1067,7 @@ mod tests {
let (pedersen_params, _) = let (pedersen_params, _) =
Pedersen::<Projective>::setup(&mut rng, ccs.n - ccs.l - 1).unwrap(); Pedersen::<Projective>::setup(&mut rng, ccs.n - ccs.l - 1).unwrap();
let pp_hash = Fr::from(42u32); // only for test
let i = Fr::from(3_u32); let i = Fr::from(3_u32);
let z_0 = vec![Fr::from(3_u32)]; let z_0 = vec![Fr::from(3_u32)];
@ -1058,19 +1077,26 @@ mod tests {
.unwrap(); .unwrap();
let h = lcccs let h = lcccs
.clone() .clone()
.hash(&poseidon_config, i, z_0.clone(), z_i.clone())
.hash(&poseidon_config, pp_hash, i, z_0.clone(), z_i.clone())
.unwrap(); .unwrap();
let cs = ConstraintSystem::<Fr>::new_ref(); let cs = ConstraintSystem::<Fr>::new_ref();
let crh_params = CRHParametersVar::<Fr>::new_constant(cs.clone(), poseidon_config).unwrap(); let crh_params = CRHParametersVar::<Fr>::new_constant(cs.clone(), poseidon_config).unwrap();
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 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_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 z_iVar = Vec::<FpVar<Fr>>::new_witness(cs.clone(), || Ok(z_i.clone())).unwrap();
let lcccsVar = LCCCSVar::<Projective>::new_witness(cs.clone(), || Ok(lcccs)).unwrap(); let lcccsVar = LCCCSVar::<Projective>::new_witness(cs.clone(), || Ok(lcccs)).unwrap();
let (hVar, _) = lcccsVar let (hVar, _) = lcccsVar
.clone() .clone()
.hash(&crh_params, iVar.clone(), z_0Var.clone(), z_iVar.clone())
.hash(
&crh_params,
pp_hashVar,
iVar.clone(),
z_0Var.clone(),
z_iVar.clone(),
)
.unwrap(); .unwrap();
assert!(cs.is_satisfied().unwrap()); assert!(cs.is_satisfied().unwrap());
@ -1112,6 +1138,9 @@ mod tests {
let (cf_pedersen_params, _) = let (cf_pedersen_params, _) =
Pedersen::<Projective2>::setup(&mut rng, cf_r1cs.A.n_cols - cf_r1cs.l - 1).unwrap(); Pedersen::<Projective2>::setup(&mut rng, cf_r1cs.A.n_cols - cf_r1cs.l - 1).unwrap();
// public params hash
let pp_hash = Fr::from(42u32); // only for test
// first step // first step
let z_0 = vec![Fr::from(3_u32)]; let z_0 = vec![Fr::from(3_u32)];
let mut z_i = z_0.clone(); let mut z_i = z_0.clone();
@ -1132,9 +1161,15 @@ mod tests {
let mut cf_W_i = cf_W_dummy.clone(); let mut cf_W_i = cf_W_dummy.clone();
let mut cf_U_i = cf_U_dummy.clone(); let mut cf_U_i = cf_U_dummy.clone();
u_i.x = vec![ u_i.x = vec![
U_i.hash(&poseidon_config, Fr::zero(), z_0.clone(), z_i.clone())
.unwrap(),
cf_U_i.hash_cyclefold(&poseidon_config).unwrap(),
U_i.hash(
&poseidon_config,
pp_hash,
Fr::zero(),
z_0.clone(),
z_i.clone(),
)
.unwrap(),
cf_U_i.hash_cyclefold(&poseidon_config, pp_hash).unwrap(),
]; ];
let n_steps: usize = 4; let n_steps: usize = 4;
@ -1151,12 +1186,18 @@ mod tests {
U_i1 = LCCCS::dummy(ccs.l, ccs.t, ccs.s); U_i1 = LCCCS::dummy(ccs.l, ccs.t, ccs.s);
let u_i1_x = U_i1 let u_i1_x = U_i1
.hash(&poseidon_config, Fr::one(), z_0.clone(), z_i1.clone())
.hash(
&poseidon_config,
pp_hash,
Fr::one(),
z_0.clone(),
z_i1.clone(),
)
.unwrap(); .unwrap();
// hash the initial (dummy) CycleFold instance, which is used as the 2nd public // hash the initial (dummy) CycleFold instance, which is used as the 2nd public
// input in the AugmentedFCircuit // input in the AugmentedFCircuit
let cf_u_i1_x = cf_U_i.hash_cyclefold(&poseidon_config).unwrap();
let cf_u_i1_x = cf_U_i.hash_cyclefold(&poseidon_config, pp_hash).unwrap();
augmented_f_circuit = augmented_f_circuit =
AugmentedFCircuit::<Projective, Projective2, GVar2, CubicFCircuit<Fr>> { AugmentedFCircuit::<Projective, Projective2, GVar2, CubicFCircuit<Fr>> {
@ -1164,6 +1205,7 @@ mod tests {
_gc2: PhantomData, _gc2: PhantomData,
poseidon_config: poseidon_config.clone(), poseidon_config: poseidon_config.clone(),
ccs: ccs.clone(), ccs: ccs.clone(),
pp_hash: Some(pp_hash),
i: Some(Fr::zero()), i: Some(Fr::zero()),
i_usize: Some(0), i_usize: Some(0),
z_0: Some(z_0.clone()), z_0: Some(z_0.clone()),
@ -1185,6 +1227,7 @@ mod tests {
} else { } else {
let mut transcript_p: PoseidonTranscript<Projective> = let mut transcript_p: PoseidonTranscript<Projective> =
PoseidonTranscript::<Projective>::new(&poseidon_config.clone()); PoseidonTranscript::<Projective>::new(&poseidon_config.clone());
transcript_p.absorb(&pp_hash);
let (rho_bits, nimfs_proof); let (rho_bits, nimfs_proof);
(nimfs_proof, U_i1, W_i1, rho_bits) = (nimfs_proof, U_i1, W_i1, rho_bits) =
NIMFS::<Projective, PoseidonTranscript<Projective>>::prove( NIMFS::<Projective, PoseidonTranscript<Projective>>::prove(
@ -1201,7 +1244,13 @@ mod tests {
U_i1.check_relation(&ccs, &W_i1).unwrap(); U_i1.check_relation(&ccs, &W_i1).unwrap();
let u_i1_x = U_i1 let u_i1_x = U_i1
.hash(&poseidon_config, iFr + Fr::one(), z_0.clone(), z_i1.clone())
.hash(
&poseidon_config,
pp_hash,
iFr + Fr::one(),
z_0.clone(),
z_i1.clone(),
)
.unwrap(); .unwrap();
let rho_Fq = Fq::from_bigint(BigInteger::from_bits_le(&rho_bits)).unwrap(); let rho_Fq = Fq::from_bigint(BigInteger::from_bits_le(&rho_bits)).unwrap();
@ -1236,6 +1285,7 @@ mod tests {
&poseidon_config, &poseidon_config,
cf_r1cs.clone(), cf_r1cs.clone(),
cf_pedersen_params.clone(), cf_pedersen_params.clone(),
pp_hash,
cf_W_i.clone(), // CycleFold running instance witness cf_W_i.clone(), // CycleFold running instance witness
cf_U_i.clone(), // CycleFold running instance cf_U_i.clone(), // CycleFold running instance
cf_u_i_x, // CycleFold incoming instance cf_u_i_x, // CycleFold incoming instance
@ -1245,7 +1295,7 @@ mod tests {
// hash the CycleFold folded instance, which is used as the 2nd public input in the // hash the CycleFold folded instance, which is used as the 2nd public input in the
// AugmentedFCircuit // AugmentedFCircuit
let cf_u_i1_x = cf_U_i1.hash_cyclefold(&poseidon_config).unwrap();
let cf_u_i1_x = cf_U_i1.hash_cyclefold(&poseidon_config, pp_hash).unwrap();
augmented_f_circuit = augmented_f_circuit =
AugmentedFCircuit::<Projective, Projective2, GVar2, CubicFCircuit<Fr>> { AugmentedFCircuit::<Projective, Projective2, GVar2, CubicFCircuit<Fr>> {
@ -1253,6 +1303,7 @@ mod tests {
_gc2: PhantomData, _gc2: PhantomData,
poseidon_config: poseidon_config.clone(), poseidon_config: poseidon_config.clone(),
ccs: ccs.clone(), ccs: ccs.clone(),
pp_hash: Some(pp_hash),
i: Some(iFr), i: Some(iFr),
i_usize: Some(i), i_usize: Some(i),
z_0: Some(z_0.clone()), z_0: Some(z_0.clone()),
@ -1296,9 +1347,15 @@ mod tests {
assert_eq!(u_i.x[0], augmented_f_circuit.x.unwrap()); assert_eq!(u_i.x[0], augmented_f_circuit.x.unwrap());
assert_eq!(u_i.x[1], augmented_f_circuit.cf_x.unwrap()); assert_eq!(u_i.x[1], augmented_f_circuit.cf_x.unwrap());
let expected_u_i1_x = U_i1 let expected_u_i1_x = U_i1
.hash(&poseidon_config, iFr + Fr::one(), z_0.clone(), z_i1.clone())
.hash(
&poseidon_config,
pp_hash,
iFr + Fr::one(),
z_0.clone(),
z_i1.clone(),
)
.unwrap(); .unwrap();
let expected_cf_U_i1_x = cf_U_i.hash_cyclefold(&poseidon_config).unwrap();
let expected_cf_U_i1_x = cf_U_i.hash_cyclefold(&poseidon_config, pp_hash).unwrap();
// u_i is already u_i1 at this point, check that has the expected value at x[0] // u_i is already u_i1 at this point, check that has the expected value at x[0]
assert_eq!(u_i.x[0], expected_u_i1_x); assert_eq!(u_i.x[0], expected_u_i1_x);
assert_eq!(u_i.x[1], expected_cf_U_i1_x); assert_eq!(u_i.x[1], expected_cf_U_i1_x);

+ 6
- 4
folding-schemes/src/folding/hypernova/lcccs.rs

@ -10,7 +10,7 @@ use ark_std::rand::Rng;
use ark_std::Zero; use ark_std::Zero;
use super::Witness; use super::Witness;
use crate::ccs::CCS;
use crate::arith::ccs::CCS;
use crate::commitment::CommitmentScheme; use crate::commitment::CommitmentScheme;
use crate::folding::circuits::nonnative::affine::nonnative_affine_to_field_elements; use crate::folding::circuits::nonnative::affine::nonnative_affine_to_field_elements;
use crate::utils::mle::dense_vec_to_dense_mle; use crate::utils::mle::dense_vec_to_dense_mle;
@ -129,6 +129,7 @@ where
pub fn hash( pub fn hash(
&self, &self,
poseidon_config: &PoseidonConfig<C::ScalarField>, poseidon_config: &PoseidonConfig<C::ScalarField>,
pp_hash: C::ScalarField,
i: C::ScalarField, i: C::ScalarField,
z_0: Vec<C::ScalarField>, z_0: Vec<C::ScalarField>,
z_i: Vec<C::ScalarField>, z_i: Vec<C::ScalarField>,
@ -138,7 +139,7 @@ where
CRH::<C::ScalarField>::evaluate( CRH::<C::ScalarField>::evaluate(
poseidon_config, poseidon_config,
vec![ vec![
vec![i],
vec![pp_hash, i],
z_0, z_0,
z_i, z_i,
C_x, C_x,
@ -164,9 +165,10 @@ pub mod tests {
use std::sync::Arc; use std::sync::Arc;
use super::*; use super::*;
use crate::ccs::{
use crate::arith::{
ccs::tests::{get_test_ccs, get_test_z},
r1cs::R1CS, r1cs::R1CS,
tests::{get_test_ccs, get_test_z},
Arith,
}; };
use crate::commitment::pedersen::Pedersen; use crate::commitment::pedersen::Pedersen;
use crate::utils::hypercube::BooleanHypercube; use crate::utils::hypercube::BooleanHypercube;

+ 86
- 47
folding-schemes/src/folding/hypernova/mod.rs

@ -24,16 +24,17 @@ use crate::folding::circuits::{
CF2, CF2,
}; };
use crate::folding::nova::{ use crate::folding::nova::{
get_r1cs_from_cs, traits::NovaR1CS, CommittedInstance, Witness as NovaWitness,
get_r1cs_from_cs, traits::NovaR1CS, CommittedInstance, PreprocessorParam,
Witness as NovaWitness,
}; };
use crate::frontend::FCircuit; use crate::frontend::FCircuit;
use crate::utils::get_cm_coordinates;
use crate::utils::{get_cm_coordinates, pp_hash};
use crate::Error; use crate::Error;
use crate::FoldingScheme; use crate::FoldingScheme;
use crate::{ use crate::{
ccs::{
arith::{
ccs::CCS,
r1cs::{extract_w_x, R1CS}, r1cs::{extract_w_x, R1CS},
CCS,
}, },
transcript::{poseidon::PoseidonTranscript, Transcript}, transcript::{poseidon::PoseidonTranscript, Transcript},
}; };
@ -56,22 +57,6 @@ impl Witness {
} }
} }
#[derive(Debug, Clone)]
pub struct PreprocessorParam<C1, C2, FC, CS1, CS2>
where
C1: CurveGroup,
C2: CurveGroup,
FC: FCircuit<C1::ScalarField>,
CS1: CommitmentScheme<C1>,
CS2: CommitmentScheme<C2>,
{
pub poseidon_config: PoseidonConfig<C1::ScalarField>,
pub F: FC,
// cs_params & cf_cs_params: if not provided, will be generated at the preprocess method
pub cs_params: Option<CS1::ProverParams>,
pub cf_cs_params: Option<CS2::ProverParams>,
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct ProverParams<C1, C2, CS1, CS2> pub struct ProverParams<C1, C2, CS1, CS2>
where where
@ -97,8 +82,27 @@ pub struct VerifierParams<
pub poseidon_config: PoseidonConfig<C1::ScalarField>, pub poseidon_config: PoseidonConfig<C1::ScalarField>,
pub ccs: CCS<C1::ScalarField>, pub ccs: CCS<C1::ScalarField>,
pub cf_r1cs: R1CS<C2::ScalarField>, pub cf_r1cs: R1CS<C2::ScalarField>,
pub cs_params: CS1::ProverParams,
pub cf_cs_params: CS2::ProverParams,
pub cs_vp: CS1::VerifierParams,
pub cf_cs_vp: CS2::VerifierParams,
}
impl<C1, C2, CS1, CS2> VerifierParams<C1, C2, CS1, CS2>
where
C1: CurveGroup,
C2: CurveGroup,
CS1: CommitmentScheme<C1>,
CS2: CommitmentScheme<C2>,
{
/// returns the hash of the public parameters of HyperNova
pub fn pp_hash(&self) -> Result<C1::ScalarField, Error> {
pp_hash::<C1, C2, CS1, CS2>(
&self.ccs,
&self.cf_r1cs,
&self.cs_vp,
&self.cf_cs_vp,
&self.poseidon_config,
)
}
} }
/// Implements HyperNova+CycleFold's IVC, described in /// Implements HyperNova+CycleFold's IVC, described in
@ -130,6 +134,8 @@ where
pub cf_cs_params: CS2::ProverParams, pub cf_cs_params: CS2::ProverParams,
/// F circuit, the circuit that is being folded /// F circuit, the circuit that is being folded
pub F: FC, pub F: FC,
/// public params hash
pub pp_hash: C1::ScalarField,
pub i: C1::ScalarField, pub i: C1::ScalarField,
/// initial state /// initial state
pub z_0: Vec<C1::ScalarField>, pub z_0: Vec<C1::ScalarField>,
@ -185,35 +191,49 @@ where
let cf_circuit = CycleFoldCircuit::<C1, GC1>::empty(); let cf_circuit = CycleFoldCircuit::<C1, GC1>::empty();
let cf_r1cs = get_r1cs_from_cs::<C2::ScalarField>(cf_circuit)?; let cf_r1cs = get_r1cs_from_cs::<C2::ScalarField>(cf_circuit)?;
// if cs_params & cf_cs_params exist, use them, if not, generate new ones
let cs_params: CS1::ProverParams;
let cf_cs_params: CS2::ProverParams;
if prep_param.cs_params.is_some() && prep_param.cf_cs_params.is_some() {
cs_params = prep_param.clone().cs_params.unwrap();
cf_cs_params = prep_param.clone().cf_cs_params.unwrap();
// if cs params exist, use them, if not, generate new ones
let cs_pp: CS1::ProverParams;
let cs_vp: CS1::VerifierParams;
let cf_cs_pp: CS2::ProverParams;
let cf_cs_vp: CS2::VerifierParams;
if prep_param.cs_pp.is_some()
&& prep_param.cf_cs_pp.is_some()
&& prep_param.cs_vp.is_some()
&& prep_param.cf_cs_vp.is_some()
{
cs_pp = prep_param.clone().cs_pp.unwrap();
cs_vp = prep_param.clone().cs_vp.unwrap();
cf_cs_pp = prep_param.clone().cf_cs_pp.unwrap();
cf_cs_vp = prep_param.clone().cf_cs_vp.unwrap();
} else { } else {
(cs_params, _) = CS1::setup(&mut rng, ccs.n - ccs.l - 1).unwrap();
(cf_cs_params, _) = CS2::setup(&mut rng, cf_r1cs.A.n_cols - cf_r1cs.l - 1).unwrap();
(cs_pp, cs_vp) = CS1::setup(&mut rng, ccs.n - ccs.l - 1)?;
(cf_cs_pp, cf_cs_vp) = CS2::setup(&mut rng, cf_r1cs.A.n_cols - cf_r1cs.l - 1)?;
} }
let pp = ProverParams::<C1, C2, CS1, CS2> { let pp = ProverParams::<C1, C2, CS1, CS2> {
poseidon_config: prep_param.poseidon_config.clone(), poseidon_config: prep_param.poseidon_config.clone(),
cs_params: cs_params.clone(),
cf_cs_params: cf_cs_params.clone(),
cs_params: cs_pp.clone(),
cf_cs_params: cf_cs_pp.clone(),
ccs: Some(ccs.clone()), ccs: Some(ccs.clone()),
}; };
let vp = VerifierParams::<C1, C2, CS1, CS2> { let vp = VerifierParams::<C1, C2, CS1, CS2> {
poseidon_config: prep_param.poseidon_config.clone(), poseidon_config: prep_param.poseidon_config.clone(),
ccs, ccs,
cf_r1cs, cf_r1cs,
cs_params: cs_params.clone(),
cf_cs_params: cf_cs_params.clone(),
cs_vp: cs_vp.clone(),
cf_cs_vp: cf_cs_vp.clone(),
}; };
Ok((pp, vp)) Ok((pp, vp))
} }
/// Initializes the HyperNova+CycleFold's IVC for the given parameters and initial state `z_0`. /// Initializes the HyperNova+CycleFold's IVC for the given parameters and initial state `z_0`.
fn init(pp: &Self::ProverParam, F: FC, z_0: Vec<C1::ScalarField>) -> Result<Self, Error> {
fn init(
params: (Self::ProverParam, Self::VerifierParam),
F: FC,
z_0: Vec<C1::ScalarField>,
) -> Result<Self, Error> {
let (pp, vp) = params;
// prepare the HyperNova's AugmentedFCircuit and CycleFold's circuits and obtain its CCS // prepare the HyperNova's AugmentedFCircuit and CycleFold's circuits and obtain its CCS
// and R1CS respectively // and R1CS respectively
let augmented_f_circuit = AugmentedFCircuit::<C1, C2, GC2, FC>::empty( let augmented_f_circuit = AugmentedFCircuit::<C1, C2, GC2, FC>::empty(
@ -226,6 +246,9 @@ where
let cf_circuit = CycleFoldCircuit::<C1, GC1>::empty(); let cf_circuit = CycleFoldCircuit::<C1, GC1>::empty();
let cf_r1cs = get_r1cs_from_cs::<C2::ScalarField>(cf_circuit)?; let cf_r1cs = get_r1cs_from_cs::<C2::ScalarField>(cf_circuit)?;
// compute the public params hash
let pp_hash = vp.pp_hash()?;
// setup the dummy instances // setup the dummy instances
let W_dummy = Witness::<C1::ScalarField>::dummy(&ccs); let W_dummy = Witness::<C1::ScalarField>::dummy(&ccs);
let U_dummy = LCCCS::<C1>::dummy(ccs.l, ccs.t, ccs.s); let U_dummy = LCCCS::<C1>::dummy(ccs.l, ccs.t, ccs.s);
@ -236,11 +259,12 @@ where
u_dummy.x = vec![ u_dummy.x = vec![
U_dummy.hash( U_dummy.hash(
&pp.poseidon_config, &pp.poseidon_config,
pp_hash,
C1::ScalarField::zero(), C1::ScalarField::zero(),
z_0.clone(), z_0.clone(),
z_0.clone(), z_0.clone(),
)?, )?,
cf_U_dummy.hash_cyclefold(&pp.poseidon_config)?,
cf_U_dummy.hash_cyclefold(&pp.poseidon_config, pp_hash)?,
]; ];
// W_dummy=W_0 is a 'dummy witness', all zeroes, but with the size corresponding to the // W_dummy=W_0 is a 'dummy witness', all zeroes, but with the size corresponding to the
@ -255,6 +279,7 @@ where
cs_params: pp.cs_params.clone(), cs_params: pp.cs_params.clone(),
cf_cs_params: pp.cf_cs_params.clone(), cf_cs_params: pp.cf_cs_params.clone(),
F, F,
pp_hash,
i: C1::ScalarField::zero(), i: C1::ScalarField::zero(),
z_0: z_0.clone(), z_0: z_0.clone(),
z_i: z_0, z_i: z_0,
@ -315,6 +340,7 @@ where
let u_i1_x = U_i1.hash( let u_i1_x = U_i1.hash(
&self.poseidon_config, &self.poseidon_config,
self.pp_hash,
C1::ScalarField::one(), C1::ScalarField::one(),
self.z_0.clone(), self.z_0.clone(),
z_i1.clone(), z_i1.clone(),
@ -322,13 +348,16 @@ where
// hash the initial (dummy) CycleFold instance, which is used as the 2nd public // hash the initial (dummy) CycleFold instance, which is used as the 2nd public
// input in the AugmentedFCircuit // input in the AugmentedFCircuit
cf_u_i1_x = self.cf_U_i.hash_cyclefold(&self.poseidon_config)?;
cf_u_i1_x = self
.cf_U_i
.hash_cyclefold(&self.poseidon_config, self.pp_hash)?;
augmented_f_circuit = AugmentedFCircuit::<C1, C2, GC2, FC> { augmented_f_circuit = AugmentedFCircuit::<C1, C2, GC2, FC> {
_c2: PhantomData, _c2: PhantomData,
_gc2: PhantomData, _gc2: PhantomData,
poseidon_config: self.poseidon_config.clone(), poseidon_config: self.poseidon_config.clone(),
ccs: self.ccs.clone(), ccs: self.ccs.clone(),
pp_hash: Some(self.pp_hash),
i: Some(C1::ScalarField::zero()), i: Some(C1::ScalarField::zero()),
i_usize: Some(0), i_usize: Some(0),
z_0: Some(self.z_0.clone()), z_0: Some(self.z_0.clone()),
@ -350,6 +379,7 @@ where
} else { } else {
let mut transcript_p: PoseidonTranscript<C1> = let mut transcript_p: PoseidonTranscript<C1> =
PoseidonTranscript::<C1>::new(&self.poseidon_config); PoseidonTranscript::<C1>::new(&self.poseidon_config);
transcript_p.absorb(&self.pp_hash);
let (rho_bits, nimfs_proof); let (rho_bits, nimfs_proof);
(nimfs_proof, U_i1, W_i1, rho_bits) = NIMFS::<C1, PoseidonTranscript<C1>>::prove( (nimfs_proof, U_i1, W_i1, rho_bits) = NIMFS::<C1, PoseidonTranscript<C1>>::prove(
&mut transcript_p, &mut transcript_p,
@ -366,6 +396,7 @@ where
let u_i1_x = U_i1.hash( let u_i1_x = U_i1.hash(
&self.poseidon_config, &self.poseidon_config,
self.pp_hash,
self.i + C1::ScalarField::one(), self.i + C1::ScalarField::one(),
self.z_0.clone(), self.z_0.clone(),
z_i1.clone(), z_i1.clone(),
@ -397,19 +428,21 @@ where
&self.poseidon_config, &self.poseidon_config,
self.cf_r1cs.clone(), self.cf_r1cs.clone(),
self.cf_cs_params.clone(), self.cf_cs_params.clone(),
self.pp_hash,
self.cf_W_i.clone(), // CycleFold running instance witness self.cf_W_i.clone(), // CycleFold running instance witness
self.cf_U_i.clone(), // CycleFold running instance self.cf_U_i.clone(), // CycleFold running instance
cf_u_i_x, cf_u_i_x,
cf_circuit, cf_circuit,
)?; )?;
cf_u_i1_x = cf_U_i1.hash_cyclefold(&self.poseidon_config)?;
cf_u_i1_x = cf_U_i1.hash_cyclefold(&self.poseidon_config, self.pp_hash)?;
augmented_f_circuit = AugmentedFCircuit::<C1, C2, GC2, FC> { augmented_f_circuit = AugmentedFCircuit::<C1, C2, GC2, FC> {
_c2: PhantomData, _c2: PhantomData,
_gc2: PhantomData, _gc2: PhantomData,
poseidon_config: self.poseidon_config.clone(), poseidon_config: self.poseidon_config.clone(),
ccs: self.ccs.clone(), ccs: self.ccs.clone(),
pp_hash: Some(self.pp_hash),
i: Some(self.i), i: Some(self.i),
i_usize: Some(i_usize), i_usize: Some(i_usize),
z_0: Some(self.z_0.clone()), z_0: Some(self.z_0.clone()),
@ -516,14 +549,16 @@ where
return Err(Error::IVCVerificationFail); return Err(Error::IVCVerificationFail);
} }
let pp_hash = vp.pp_hash()?;
// check that u_i's output points to the running instance // check that u_i's output points to the running instance
// u_i.X[0] == H(i, z_0, z_i, U_i) // u_i.X[0] == H(i, z_0, z_i, U_i)
let expected_u_i_x = U_i.hash(&vp.poseidon_config, num_steps, z_0, z_i.clone())?;
let expected_u_i_x = U_i.hash(&vp.poseidon_config, pp_hash, num_steps, z_0, z_i.clone())?;
if expected_u_i_x != u_i.x[0] { if expected_u_i_x != u_i.x[0] {
return Err(Error::IVCVerificationFail); return Err(Error::IVCVerificationFail);
} }
// u_i.X[1] == H(cf_U_i) // u_i.X[1] == H(cf_U_i)
let expected_cf_u_i_x = cf_U_i.hash_cyclefold(&vp.poseidon_config)?;
let expected_cf_u_i_x = cf_U_i.hash_cyclefold(&vp.poseidon_config, pp_hash)?;
if expected_cf_u_i_x != u_i.x[1] { if expected_cf_u_i_x != u_i.x[1] {
return Err(Error::IVCVerificationFail); return Err(Error::IVCVerificationFail);
} }
@ -578,16 +613,20 @@ mod tests {
type HN<CS1, CS2> = type HN<CS1, CS2> =
HyperNova<Projective, GVar, Projective2, GVar2, CubicFCircuit<Fr>, CS1, CS2>; HyperNova<Projective, GVar, Projective2, GVar2, CubicFCircuit<Fr>, CS1, CS2>;
let prep_param = PreprocessorParam::<Projective, Projective2, CubicFCircuit<Fr>, CS1, CS2> {
poseidon_config,
F: F_circuit,
cs_params: None,
cf_cs_params: None,
};
let prep_param =
PreprocessorParam::<Projective, Projective2, CubicFCircuit<Fr>, CS1, CS2>::new(
poseidon_config.clone(),
F_circuit,
);
let (prover_params, verifier_params) = HN::preprocess(&mut rng, &prep_param).unwrap(); let (prover_params, verifier_params) = HN::preprocess(&mut rng, &prep_param).unwrap();
let z_0 = vec![Fr::from(3_u32)]; let z_0 = vec![Fr::from(3_u32)];
let mut hypernova = HN::init(&prover_params, F_circuit, z_0.clone()).unwrap();
let mut hypernova = HN::init(
(prover_params, verifier_params.clone()),
F_circuit,
z_0.clone(),
)
.unwrap();
let num_steps: usize = 3; let num_steps: usize = 3;
for _ in 0..num_steps { for _ in 0..num_steps {

+ 5
- 2
folding-schemes/src/folding/hypernova/nimfs.rs

@ -11,7 +11,7 @@ use super::{
utils::{compute_c, compute_g, compute_sigmas_thetas}, utils::{compute_c, compute_g, compute_sigmas_thetas},
Witness, Witness,
}; };
use crate::ccs::CCS;
use crate::arith::ccs::CCS;
use crate::constants::N_BITS_RO; use crate::constants::N_BITS_RO;
use crate::folding::circuits::nonnative::affine::nonnative_affine_to_field_elements; use crate::folding::circuits::nonnative::affine::nonnative_affine_to_field_elements;
use crate::transcript::Transcript; use crate::transcript::Transcript;
@ -408,7 +408,10 @@ where
#[cfg(test)] #[cfg(test)]
pub mod tests { pub mod tests {
use super::*; use super::*;
use crate::ccs::tests::{get_test_ccs, get_test_z};
use crate::arith::{
ccs::tests::{get_test_ccs, get_test_z},
Arith,
};
use crate::transcript::poseidon::poseidon_canonical_config; use crate::transcript::poseidon::poseidon_canonical_config;
use crate::transcript::poseidon::PoseidonTranscript; use crate::transcript::poseidon::PoseidonTranscript;
use ark_std::test_rng; use ark_std::test_rng;

+ 5
- 2
folding-schemes/src/folding/hypernova/utils.rs

@ -7,7 +7,7 @@ use std::sync::Arc;
use super::lcccs::LCCCS; use super::lcccs::LCCCS;
use super::nimfs::SigmasThetas; use super::nimfs::SigmasThetas;
use crate::ccs::CCS;
use crate::arith::ccs::CCS;
use crate::utils::mle::dense_vec_to_dense_mle; use crate::utils::mle::dense_vec_to_dense_mle;
use crate::utils::vec::mat_vec_mul; use crate::utils::vec::mat_vec_mul;
use crate::utils::virtual_polynomial::{build_eq_x_r_vec, eq_eval, VirtualPolynomial}; use crate::utils::virtual_polynomial::{build_eq_x_r_vec, eq_eval, VirtualPolynomial};
@ -167,7 +167,10 @@ pub mod tests {
use ark_std::Zero; use ark_std::Zero;
use super::*; use super::*;
use crate::ccs::tests::{get_test_ccs, get_test_z};
use crate::arith::{
ccs::tests::{get_test_ccs, get_test_z},
Arith,
};
use crate::commitment::{pedersen::Pedersen, CommitmentScheme}; use crate::commitment::{pedersen::Pedersen, CommitmentScheme};
use crate::folding::hypernova::lcccs::tests::compute_Ls; use crate::folding::hypernova::lcccs::tests::compute_Ls;
use crate::utils::hypercube::BooleanHypercube; use crate::utils::hypercube::BooleanHypercube;

+ 37
- 9
folding-schemes/src/folding/nova/circuits.rs

@ -94,6 +94,7 @@ where
pub fn hash( pub fn hash(
self, self,
crh_params: &CRHParametersVar<CF1<C>>, crh_params: &CRHParametersVar<CF1<C>>,
pp_hash: FpVar<CF1<C>>,
i: FpVar<CF1<C>>, i: FpVar<CF1<C>>,
z_0: Vec<FpVar<CF1<C>>>, z_0: Vec<FpVar<CF1<C>>>,
z_i: Vec<FpVar<CF1<C>>>, z_i: Vec<FpVar<CF1<C>>>,
@ -105,7 +106,7 @@ where
self.cmW.to_constraint_field()?, self.cmW.to_constraint_field()?,
] ]
.concat(); .concat();
let input = [vec![i], z_0, z_i, U_vec.clone()].concat();
let input = [vec![pp_hash, i], z_0, z_i, U_vec.clone()].concat();
Ok(( Ok((
CRHGadget::<C::ScalarField>::evaluate(crh_params, &input)?, CRHGadget::<C::ScalarField>::evaluate(crh_params, &input)?,
U_vec, U_vec,
@ -175,6 +176,7 @@ where
{ {
pub fn get_challenge_native( pub fn get_challenge_native(
poseidon_config: &PoseidonConfig<C::ScalarField>, poseidon_config: &PoseidonConfig<C::ScalarField>,
pp_hash: C::ScalarField, // public params hash
U_i: CommittedInstance<C>, U_i: CommittedInstance<C>,
u_i: CommittedInstance<C>, u_i: CommittedInstance<C>,
cmT: C, cmT: C,
@ -187,6 +189,7 @@ where
let mut sponge = PoseidonSponge::<C::ScalarField>::new(poseidon_config); let mut sponge = PoseidonSponge::<C::ScalarField>::new(poseidon_config);
let input = vec![ let input = vec![
vec![pp_hash],
vec![U_i.u], vec![U_i.u],
U_i.x.clone(), U_i.x.clone(),
U_cmE_x, U_cmE_x,
@ -212,6 +215,7 @@ where
pub fn get_challenge_gadget( pub fn get_challenge_gadget(
cs: ConstraintSystemRef<C::ScalarField>, cs: ConstraintSystemRef<C::ScalarField>,
poseidon_config: &PoseidonConfig<C::ScalarField>, poseidon_config: &PoseidonConfig<C::ScalarField>,
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_vec: Vec<FpVar<CF1<C>>>, // apready processed input, so we don't have to recompute these values
u_i: CommittedInstanceVar<C>, u_i: CommittedInstanceVar<C>,
cmT: NonNativeAffineVar<C>, cmT: NonNativeAffineVar<C>,
@ -219,6 +223,7 @@ where
let mut sponge = PoseidonSpongeVar::<C::ScalarField>::new(cs, poseidon_config); let mut sponge = PoseidonSpongeVar::<C::ScalarField>::new(cs, poseidon_config);
let input: Vec<FpVar<C::ScalarField>> = [ let input: Vec<FpVar<C::ScalarField>> = [
vec![pp_hash],
U_i_vec, U_i_vec,
vec![u_i.u.clone()], vec![u_i.u.clone()],
u_i.x.clone(), u_i.x.clone(),
@ -247,6 +252,7 @@ pub struct AugmentedFCircuit<
{ {
pub _gc2: PhantomData<GC2>, pub _gc2: PhantomData<GC2>,
pub poseidon_config: PoseidonConfig<CF1<C1>>, pub poseidon_config: PoseidonConfig<CF1<C1>>,
pub pp_hash: Option<CF1<C1>>,
pub i: Option<CF1<C1>>, pub i: Option<CF1<C1>>,
pub i_usize: Option<usize>, pub i_usize: Option<usize>,
pub z_0: Option<Vec<C1::ScalarField>>, pub z_0: Option<Vec<C1::ScalarField>>,
@ -280,6 +286,7 @@ where
Self { Self {
_gc2: PhantomData, _gc2: PhantomData,
poseidon_config: poseidon_config.clone(), poseidon_config: poseidon_config.clone(),
pp_hash: None,
i: None, i: None,
i_usize: None, i_usize: None,
z_0: None, z_0: None,
@ -317,6 +324,9 @@ where
for<'a> &'a GC2: GroupOpsBounds<'a, C2, GC2>, for<'a> &'a GC2: GroupOpsBounds<'a, C2, GC2>,
{ {
fn generate_constraints(self, cs: ConstraintSystemRef<CF1<C1>>) -> Result<(), SynthesisError> { fn generate_constraints(self, cs: ConstraintSystemRef<CF1<C1>>) -> Result<(), SynthesisError> {
let pp_hash = FpVar::<CF1<C1>>::new_witness(cs.clone(), || {
Ok(self.pp_hash.unwrap_or_else(CF1::<C1>::zero))
})?;
let i = FpVar::<CF1<C1>>::new_witness(cs.clone(), || { let i = FpVar::<CF1<C1>>::new_witness(cs.clone(), || {
Ok(self.i.unwrap_or_else(CF1::<C1>::zero)) Ok(self.i.unwrap_or_else(CF1::<C1>::zero))
})?; })?;
@ -373,11 +383,15 @@ where
// Primary Part // Primary Part
// P.1. Compute u_i.x // P.1. Compute u_i.x
// u_i.x[0] = H(i, z_0, z_i, U_i) // u_i.x[0] = H(i, z_0, z_i, U_i)
let (u_i_x, U_i_vec) =
U_i.clone()
.hash(&crh_params, i.clone(), z_0.clone(), z_i.clone())?;
let (u_i_x, U_i_vec) = U_i.clone().hash(
&crh_params,
pp_hash.clone(),
i.clone(),
z_0.clone(),
z_i.clone(),
)?;
// u_i.x[1] = H(cf_U_i) // u_i.x[1] = H(cf_U_i)
let (cf_u_i_x, cf_U_i_vec) = cf_U_i.clone().hash(&crh_params)?;
let (cf_u_i_x, cf_U_i_vec) = cf_U_i.clone().hash(&crh_params, pp_hash.clone())?;
// P.2. Construct u_i // P.2. Construct u_i
let u_i = CommittedInstanceVar { let u_i = CommittedInstanceVar {
@ -399,6 +413,7 @@ where
let r_bits = ChallengeGadget::<C1>::get_challenge_gadget( let r_bits = ChallengeGadget::<C1>::get_challenge_gadget(
cs.clone(), cs.clone(),
&self.poseidon_config, &self.poseidon_config,
pp_hash.clone(),
U_i_vec, U_i_vec,
u_i.clone(), u_i.clone(),
cmT.clone(), cmT.clone(),
@ -424,12 +439,14 @@ where
// Non-base case: u_{i+1}.x[0] == H((i+1, z_0, z_{i+1}, U_{i+1}) // Non-base case: u_{i+1}.x[0] == H((i+1, z_0, z_{i+1}, U_{i+1})
let (u_i1_x, _) = U_i1.clone().hash( let (u_i1_x, _) = U_i1.clone().hash(
&crh_params, &crh_params,
pp_hash.clone(),
i + FpVar::<CF1<C1>>::one(), i + FpVar::<CF1<C1>>::one(),
z_0.clone(), z_0.clone(),
z_i1.clone(), z_i1.clone(),
)?; )?;
let (u_i1_x_base, _) = CommittedInstanceVar::new_constant(cs.clone(), u_dummy)?.hash( let (u_i1_x_base, _) = CommittedInstanceVar::new_constant(cs.clone(), u_dummy)?.hash(
&crh_params, &crh_params,
pp_hash.clone(),
FpVar::<CF1<C1>>::one(), FpVar::<CF1<C1>>::one(),
z_0.clone(), z_0.clone(),
z_i1.clone(), z_i1.clone(),
@ -484,6 +501,7 @@ where
let cf1_r_bits = CycleFoldChallengeGadget::<C2, GC2>::get_challenge_gadget( let cf1_r_bits = CycleFoldChallengeGadget::<C2, GC2>::get_challenge_gadget(
cs.clone(), cs.clone(),
&self.poseidon_config, &self.poseidon_config,
pp_hash.clone(),
cf_U_i_vec, cf_U_i_vec,
cf1_u_i.clone(), cf1_u_i.clone(),
cf1_cmT.clone(), cf1_cmT.clone(),
@ -507,6 +525,7 @@ where
let cf2_r_bits = CycleFoldChallengeGadget::<C2, GC2>::get_challenge_gadget( let cf2_r_bits = CycleFoldChallengeGadget::<C2, GC2>::get_challenge_gadget(
cs.clone(), cs.clone(),
&self.poseidon_config, &self.poseidon_config,
pp_hash.clone(),
cf1_U_i1.to_constraint_field()?, cf1_U_i1.to_constraint_field()?,
cf2_u_i.clone(), cf2_u_i.clone(),
cf2_cmT.clone(), cf2_cmT.clone(),
@ -528,10 +547,10 @@ where
// P.4.b compute and check the second output of F' // P.4.b compute and check the second output of F'
// Base case: u_{i+1}.x[1] == H(cf_U_{\bot}) // Base case: u_{i+1}.x[1] == H(cf_U_{\bot})
// Non-base case: u_{i+1}.x[1] == H(cf_U_{i+1}) // Non-base case: u_{i+1}.x[1] == H(cf_U_{i+1})
let (cf_u_i1_x, _) = cf_U_i1.clone().hash(&crh_params)?;
let (cf_u_i1_x, _) = cf_U_i1.clone().hash(&crh_params, pp_hash.clone())?;
let (cf_u_i1_x_base, _) = let (cf_u_i1_x_base, _) =
CycleFoldCommittedInstanceVar::new_constant(cs.clone(), cf_u_dummy)? CycleFoldCommittedInstanceVar::new_constant(cs.clone(), cf_u_dummy)?
.hash(&crh_params)?;
.hash(&crh_params, pp_hash)?;
let cf_x = FpVar::new_input(cs.clone(), || { let cf_x = FpVar::new_input(cs.clone(), || {
Ok(self.cf_x.unwrap_or(cf_u_i1_x_base.value()?)) Ok(self.cf_x.unwrap_or(cf_u_i1_x_base.value()?))
})?; })?;
@ -609,6 +628,7 @@ pub mod tests {
fn test_committed_instance_hash() { fn test_committed_instance_hash() {
let mut rng = ark_std::test_rng(); let mut rng = ark_std::test_rng();
let poseidon_config = poseidon_canonical_config::<Fr>(); let poseidon_config = poseidon_canonical_config::<Fr>();
let pp_hash = Fr::from(42u32); // only for test
let i = Fr::from(3_u32); let i = Fr::from(3_u32);
let z_0 = vec![Fr::from(3_u32)]; let z_0 = vec![Fr::from(3_u32)];
@ -622,11 +642,12 @@ pub mod tests {
// compute the CommittedInstance hash natively // compute the CommittedInstance hash natively
let h = ci let h = ci
.hash(&poseidon_config, i, z_0.clone(), z_i.clone())
.hash(&poseidon_config, pp_hash, i, z_0.clone(), z_i.clone())
.unwrap(); .unwrap();
let cs = ConstraintSystem::<Fr>::new_ref(); 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 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_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 z_iVar = Vec::<FpVar<Fr>>::new_witness(cs.clone(), || Ok(z_i.clone())).unwrap();
@ -636,7 +657,9 @@ pub mod tests {
let crh_params = CRHParametersVar::<Fr>::new_constant(cs.clone(), poseidon_config).unwrap(); let crh_params = CRHParametersVar::<Fr>::new_constant(cs.clone(), poseidon_config).unwrap();
// compute the CommittedInstance hash in-circuit // compute the CommittedInstance hash in-circuit
let (hVar, _) = ciVar.hash(&crh_params, iVar, z_0Var, z_iVar).unwrap();
let (hVar, _) = ciVar
.hash(&crh_params, pp_hashVar, iVar, z_0Var, z_iVar)
.unwrap();
assert!(cs.is_satisfied().unwrap()); assert!(cs.is_satisfied().unwrap());
// check that the natively computed and in-circuit computed hashes match // check that the natively computed and in-circuit computed hashes match
@ -663,9 +686,12 @@ pub mod tests {
}; };
let cmT = Projective::rand(&mut rng); let cmT = Projective::rand(&mut rng);
let pp_hash = Fr::from(42u32); // only for testing
// compute the challenge natively // compute the challenge natively
let r_bits = ChallengeGadget::<Projective>::get_challenge_native( let r_bits = ChallengeGadget::<Projective>::get_challenge_native(
&poseidon_config, &poseidon_config,
pp_hash,
U_i.clone(), U_i.clone(),
u_i.clone(), u_i.clone(),
cmT, cmT,
@ -674,6 +700,7 @@ pub mod tests {
let r = Fr::from_bigint(BigInteger::from_bits_le(&r_bits)).unwrap(); let r = Fr::from_bigint(BigInteger::from_bits_le(&r_bits)).unwrap();
let cs = ConstraintSystem::<Fr>::new_ref(); let cs = ConstraintSystem::<Fr>::new_ref();
let pp_hashVar = FpVar::<Fr>::new_witness(cs.clone(), || Ok(pp_hash)).unwrap();
let u_iVar = let u_iVar =
CommittedInstanceVar::<Projective>::new_witness(cs.clone(), || Ok(u_i.clone())) CommittedInstanceVar::<Projective>::new_witness(cs.clone(), || Ok(u_i.clone()))
.unwrap(); .unwrap();
@ -693,6 +720,7 @@ pub mod tests {
let r_bitsVar = ChallengeGadget::<Projective>::get_challenge_gadget( let r_bitsVar = ChallengeGadget::<Projective>::get_challenge_gadget(
cs.clone(), cs.clone(),
&poseidon_config, &poseidon_config,
pp_hashVar,
U_iVar_vec, U_iVar_vec,
u_iVar, u_iVar,
cmTVar, cmTVar,

+ 14
- 42
folding-schemes/src/folding/nova/decider_eth.rs

@ -90,7 +90,8 @@ where
type PreprocessorParam = (FS::ProverParam, FS::VerifierParam); type PreprocessorParam = (FS::ProverParam, FS::VerifierParam);
type ProverParam = (S::ProvingKey, CS1::ProverParams); type ProverParam = (S::ProvingKey, CS1::ProverParams);
type Proof = Proof<C1, CS1, S>; type Proof = Proof<C1, CS1, S>;
type VerifierParam = (S::VerifyingKey, CS1::VerifierParams);
/// VerifierParam = (pp_hash, snark::vk, commitment_scheme::vk)
type VerifierParam = (C1::ScalarField, S::VerifyingKey, CS1::VerifierParams);
type PublicInput = Vec<C1::ScalarField>; type PublicInput = Vec<C1::ScalarField>;
type CommittedInstance = CommittedInstance<C1>; type CommittedInstance = CommittedInstance<C1>;
@ -115,9 +116,10 @@ where
let nova_vp: let nova_vp:
<Nova<C1, GC1, C2, GC2, FC, CS1, CS2> as FoldingScheme<C1, C2, FC>>::VerifierParam = <Nova<C1, GC1, C2, GC2, FC, CS1, CS2> as FoldingScheme<C1, C2, FC>>::VerifierParam =
prep_param.1.clone().into(); prep_param.1.clone().into();
let pp_hash = nova_vp.pp_hash()?;
let pp = (g16_pk, nova_pp.cs_pp); let pp = (g16_pk, nova_pp.cs_pp);
let vp = (g16_vk, nova_vp.cs_vp);
let vp = (pp_hash, g16_vk, nova_vp.cs_vp);
Ok((pp, vp)) Ok((pp, vp))
} }
@ -186,7 +188,8 @@ where
return Err(Error::NotEnoughSteps); return Err(Error::NotEnoughSteps);
} }
let (snark_vk, cs_vk): (S::VerifyingKey, CS1::VerifierParams) = vp;
let (pp_hash, snark_vk, cs_vk): (C1::ScalarField, S::VerifyingKey, CS1::VerifierParams) =
vp;
// compute U = U_{d+1}= NIFS.V(U_d, u_d, cmT) // 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 U = NIFS::<C1, CS1>::verify(proof.r, running_instance, incoming_instance, &proof.cmT);
@ -196,7 +199,7 @@ where
let (cmT_x, cmT_y) = NonNativeAffineVar::inputize(proof.cmT)?; let (cmT_x, cmT_y) = NonNativeAffineVar::inputize(proof.cmT)?;
let public_input: Vec<C1::ScalarField> = vec![ let public_input: Vec<C1::ScalarField> = vec![
vec![i],
vec![pp_hash, i],
z_0, z_0,
z_i, z_i,
vec![U.u], vec![U.u],
@ -317,13 +320,12 @@ pub mod tests {
use ark_bn254::{constraints::GVar, Bn254, Fr, G1Projective as Projective}; use ark_bn254::{constraints::GVar, Bn254, Fr, G1Projective as Projective};
use ark_groth16::Groth16; use ark_groth16::Groth16;
use ark_grumpkin::{constraints::GVar as GVar2, Projective as Projective2}; use ark_grumpkin::{constraints::GVar as GVar2, Projective as Projective2};
use ark_poly_commit::kzg10::VerifierKey as KZGVerifierKey;
use std::time::Instant; use std::time::Instant;
use super::*; use super::*;
use crate::commitment::kzg::{ProverKey as KZGProverKey, KZG};
use crate::commitment::kzg::KZG;
use crate::commitment::pedersen::Pedersen; use crate::commitment::pedersen::Pedersen;
use crate::folding::nova::{get_cs_params_len, ProverParams};
use crate::folding::nova::PreprocessorParam;
use crate::frontend::tests::CubicFCircuit; use crate::frontend::tests::CubicFCircuit;
use crate::transcript::poseidon::poseidon_canonical_config; use crate::transcript::poseidon::poseidon_canonical_config;
@ -357,59 +359,29 @@ pub mod tests {
let F_circuit = CubicFCircuit::<Fr>::new(()).unwrap(); let F_circuit = CubicFCircuit::<Fr>::new(()).unwrap();
let z_0 = vec![Fr::from(3_u32)]; let z_0 = vec![Fr::from(3_u32)];
let (cs_len, cf_cs_len) =
get_cs_params_len::<Projective, GVar, Projective2, GVar2, CubicFCircuit<Fr>>(
&poseidon_config,
F_circuit,
)
.unwrap();
let start = Instant::now();
let (kzg_pk, kzg_vk): (KZGProverKey<Projective>, KZGVerifierKey<Bn254>) =
KZG::<Bn254>::setup(&mut rng, cs_len).unwrap();
let (cf_pedersen_params, _) = Pedersen::<Projective2>::setup(&mut rng, cf_cs_len).unwrap();
println!("generated KZG params, {:?}", start.elapsed());
let prover_params =
ProverParams::<Projective, Projective2, KZG<Bn254>, Pedersen<Projective2>> {
poseidon_config: poseidon_config.clone(),
cs_pp: kzg_pk.clone(),
cf_cs_pp: cf_pedersen_params,
};
let prep_param = PreprocessorParam::new(poseidon_config, F_circuit);
let nova_params = N::preprocess(&mut rng, &prep_param).unwrap();
let start = Instant::now(); let start = Instant::now();
let mut nova = N::init(&prover_params, F_circuit, z_0.clone()).unwrap();
let mut nova = N::init(nova_params.clone(), F_circuit, z_0.clone()).unwrap();
println!("Nova initialized, {:?}", start.elapsed()); println!("Nova initialized, {:?}", start.elapsed());
let start = Instant::now(); let start = Instant::now();
nova.prove_step(&mut rng, vec![]).unwrap(); nova.prove_step(&mut rng, vec![]).unwrap();
println!("prove_step, {:?}", start.elapsed()); println!("prove_step, {:?}", start.elapsed());
nova.prove_step(&mut rng, vec![]).unwrap(); // do a 2nd step nova.prove_step(&mut rng, vec![]).unwrap(); // do a 2nd step
// generate Groth16 setup
let circuit = DeciderEthCircuit::<
Projective,
GVar,
Projective2,
GVar2,
KZG<Bn254>,
Pedersen<Projective2>,
>::from_nova::<CubicFCircuit<Fr>>(nova.clone())
.unwrap();
let mut rng = rand::rngs::OsRng; let mut rng = rand::rngs::OsRng;
let start = Instant::now();
let (g16_pk, g16_vk) =
Groth16::<Bn254>::circuit_specific_setup(circuit.clone(), &mut rng).unwrap();
println!("Groth16 setup, {:?}", start.elapsed());
// prepare the Decider prover & verifier params
let (decider_pp, decider_vp) = D::preprocess(&mut rng, &nova_params, nova.clone()).unwrap();
// decider proof generation // decider proof generation
let start = Instant::now(); let start = Instant::now();
let decider_pp = (g16_pk, kzg_pk);
let proof = D::prove(rng, decider_pp, nova.clone()).unwrap(); let proof = D::prove(rng, decider_pp, nova.clone()).unwrap();
println!("Decider prove, {:?}", start.elapsed()); println!("Decider prove, {:?}", start.elapsed());
// decider proof verification // decider proof verification
let start = Instant::now(); let start = Instant::now();
let decider_vp = (g16_vk, kzg_vk);
let verified = D::verify( let verified = D::verify(
decider_vp, nova.i, nova.z_0, nova.z_i, &nova.U_i, &nova.u_i, &proof, decider_vp, nova.i, nova.z_0, nova.z_i, &nova.U_i, &nova.u_i, &proof,
) )

+ 39
- 37
folding-schemes/src/folding/nova/decider_eth_circuit.rs

@ -20,7 +20,7 @@ use ark_std::{log2, Zero};
use core::{borrow::Borrow, marker::PhantomData}; use core::{borrow::Borrow, marker::PhantomData};
use super::{circuits::ChallengeGadget, nifs::NIFS}; use super::{circuits::ChallengeGadget, nifs::NIFS};
use crate::ccs::r1cs::R1CS;
use crate::arith::r1cs::R1CS;
use crate::commitment::{pedersen::Params as PedersenParams, CommitmentScheme}; use crate::commitment::{pedersen::Params as PedersenParams, CommitmentScheme};
use crate::folding::circuits::{ use crate::folding::circuits::{
nonnative::{ nonnative::{
@ -223,6 +223,8 @@ where
/// CycleFold PedersenParams over C2 /// CycleFold PedersenParams over C2
pub cf_pedersen_params: PedersenParams<C2>, pub cf_pedersen_params: PedersenParams<C2>,
pub poseidon_config: PoseidonConfig<CF1<C1>>, pub poseidon_config: PoseidonConfig<CF1<C1>>,
/// public params hash
pub pp_hash: Option<C1::ScalarField>,
pub i: Option<CF1<C1>>, pub i: Option<CF1<C1>>,
/// initial state /// initial state
pub z_0: Option<Vec<C1::ScalarField>>, pub z_0: Option<Vec<C1::ScalarField>>,
@ -273,6 +275,7 @@ where
)?; )?;
let r_bits = ChallengeGadget::<C1>::get_challenge_native( let r_bits = ChallengeGadget::<C1>::get_challenge_native(
&nova.poseidon_config, &nova.poseidon_config,
nova.pp_hash,
nova.U_i.clone(), nova.U_i.clone(),
nova.u_i.clone(), nova.u_i.clone(),
cmT, cmT,
@ -317,6 +320,7 @@ where
cf_r1cs: nova.cf_r1cs, cf_r1cs: nova.cf_r1cs,
cf_pedersen_params: nova.cf_cs_pp, cf_pedersen_params: nova.cf_cs_pp,
poseidon_config: nova.poseidon_config, poseidon_config: nova.poseidon_config,
pp_hash: Some(nova.pp_hash),
i: Some(nova.i), i: Some(nova.i),
z_0: Some(nova.z_0), z_0: Some(nova.z_0),
z_i: Some(nova.z_i), z_i: Some(nova.z_i),
@ -360,6 +364,9 @@ where
Ok(self.r1cs.clone()) Ok(self.r1cs.clone())
})?; })?;
let pp_hash = FpVar::<CF1<C1>>::new_input(cs.clone(), || {
Ok(self.pp_hash.unwrap_or_else(CF1::<C1>::zero))
})?;
let i = let i =
FpVar::<CF1<C1>>::new_input(cs.clone(), || Ok(self.i.unwrap_or_else(CF1::<C1>::zero)))?; 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(), || { let z_0 = Vec::<FpVar<CF1<C1>>>::new_input(cs.clone(), || {
@ -421,9 +428,13 @@ where
(u_i.u.is_one()?).enforce_equal(&Boolean::TRUE)?; (u_i.u.is_one()?).enforce_equal(&Boolean::TRUE)?;
// 3.a u_i.x[0] == H(i, z_0, z_i, U_i) // 3.a u_i.x[0] == H(i, z_0, z_i, U_i)
let (u_i_x, U_i_vec) =
U_i.clone()
.hash(&crh_params, i.clone(), z_0.clone(), z_i.clone())?;
let (u_i_x, U_i_vec) = U_i.clone().hash(
&crh_params,
pp_hash.clone(),
i.clone(),
z_0.clone(),
z_i.clone(),
)?;
(u_i.x[0]).enforce_equal(&u_i_x)?; (u_i.x[0]).enforce_equal(&u_i_x)?;
#[cfg(feature = "light-test")] #[cfg(feature = "light-test")]
@ -454,7 +465,7 @@ where
})?; })?;
// 3.b u_i.x[1] == H(cf_U_i) // 3.b u_i.x[1] == H(cf_U_i)
let (cf_u_i_x, _) = cf_U_i.clone().hash(&crh_params)?;
let (cf_u_i_x, _) = cf_U_i.clone().hash(&crh_params, pp_hash.clone())?;
(u_i.x[1]).enforce_equal(&cf_u_i_x)?; (u_i.x[1]).enforce_equal(&cf_u_i_x)?;
// 4. check Pedersen commitments of cf_U_i.{cmE, cmW} // 4. check Pedersen commitments of cf_U_i.{cmE, cmW}
@ -512,6 +523,7 @@ where
let r_bits = ChallengeGadget::<C1>::get_challenge_gadget( let r_bits = ChallengeGadget::<C1>::get_challenge_gadget(
cs.clone(), cs.clone(),
&self.poseidon_config, &self.poseidon_config,
pp_hash,
U_i_vec, U_i_vec,
u_i.clone(), u_i.clone(),
cmT.clone(), cmT.clone(),
@ -611,10 +623,15 @@ pub mod tests {
use ark_vesta::{constraints::GVar as GVar2, Projective as Projective2}; use ark_vesta::{constraints::GVar as GVar2, Projective as Projective2};
use super::*; use super::*;
use crate::ccs::r1cs::tests::{get_test_r1cs, get_test_z};
use crate::ccs::r1cs::{extract_r1cs, extract_w_x};
use crate::arith::{
r1cs::{
tests::{get_test_r1cs, get_test_z},
{extract_r1cs, extract_w_x},
},
Arith,
};
use crate::commitment::pedersen::Pedersen; use crate::commitment::pedersen::Pedersen;
use crate::folding::nova::{get_cs_params_len, ProverParams, VerifierParams};
use crate::folding::nova::PreprocessorParam;
use crate::frontend::tests::{CubicFCircuit, CustomFCircuit, WrapperCircuit}; use crate::frontend::tests::{CubicFCircuit, CustomFCircuit, WrapperCircuit};
use crate::transcript::poseidon::poseidon_canonical_config; use crate::transcript::poseidon::poseidon_canonical_config;
use crate::FoldingScheme; use crate::FoldingScheme;
@ -772,23 +789,6 @@ pub mod tests {
let F_circuit = CubicFCircuit::<Fr>::new(()).unwrap(); let F_circuit = CubicFCircuit::<Fr>::new(()).unwrap();
let z_0 = vec![Fr::from(3_u32)]; let z_0 = vec![Fr::from(3_u32)];
// get the CS & CF_CS len
let (cs_len, cf_cs_len) =
get_cs_params_len::<Projective, GVar, Projective2, GVar2, CubicFCircuit<Fr>>(
&poseidon_config,
F_circuit,
)
.unwrap();
let (pedersen_params, _) = Pedersen::<Projective>::setup(&mut rng, cs_len).unwrap();
let (cf_pedersen_params, _) = Pedersen::<Projective2>::setup(&mut rng, cf_cs_len).unwrap();
let prover_params =
ProverParams::<Projective, Projective2, Pedersen<Projective>, Pedersen<Projective2>> {
poseidon_config: poseidon_config.clone(),
cs_pp: pedersen_params.clone(),
cf_cs_pp: cf_pedersen_params.clone(),
};
type N = Nova< type N = Nova<
Projective, Projective,
GVar, GVar,
@ -799,22 +799,24 @@ pub mod tests {
Pedersen<Projective2>, Pedersen<Projective2>,
>; >;
// generate a Nova instance and do a step of it
let mut nova = N::init(&prover_params, F_circuit, z_0.clone()).unwrap();
nova.prove_step(&mut rng, vec![]).unwrap();
let ivc_v = nova.clone();
let verifier_params = VerifierParams::<
let prep_param = PreprocessorParam::<
Projective, Projective,
Projective2, Projective2,
CubicFCircuit<Fr>,
Pedersen<Projective>, Pedersen<Projective>,
Pedersen<Projective2>, Pedersen<Projective2>,
> {
poseidon_config: poseidon_config.clone(),
r1cs: ivc_v.clone().r1cs,
cf_r1cs: ivc_v.clone().cf_r1cs,
cs_vp: pedersen_params,
cf_cs_vp: cf_pedersen_params,
};
>::new(poseidon_config, F_circuit);
let (prover_params, verifier_params) = N::preprocess(&mut rng, &prep_param).unwrap();
// generate a Nova instance and do a step of it
let mut nova = N::init(
(prover_params, verifier_params.clone()),
F_circuit,
z_0.clone(),
)
.unwrap();
nova.prove_step(&mut rng, vec![]).unwrap();
let ivc_v = nova.clone();
let (running_instance, incoming_instance, cyclefold_instance) = ivc_v.instances(); let (running_instance, incoming_instance, cyclefold_instance) = ivc_v.instances();
N::verify( N::verify(
verifier_params, verifier_params,

+ 65
- 20
folding-schemes/src/folding/nova/mod.rs

@ -13,19 +13,18 @@ use ark_std::fmt::Debug;
use ark_std::rand::RngCore; use ark_std::rand::RngCore;
use ark_std::{One, Zero}; use ark_std::{One, Zero};
use core::marker::PhantomData; use core::marker::PhantomData;
use std::usize;
use crate::ccs::r1cs::{extract_r1cs, extract_w_x, R1CS};
use crate::arith::r1cs::{extract_r1cs, extract_w_x, R1CS};
use crate::commitment::CommitmentScheme; use crate::commitment::CommitmentScheme;
use crate::folding::circuits::cyclefold::{fold_cyclefold_circuit, CycleFoldCircuit};
use crate::folding::circuits::{ use crate::folding::circuits::{
cyclefold::{fold_cyclefold_circuit, CycleFoldCircuit},
nonnative::{ nonnative::{
affine::nonnative_affine_to_field_elements, uint::nonnative_field_to_field_elements, affine::nonnative_affine_to_field_elements, uint::nonnative_field_to_field_elements,
}, },
CF2, CF2,
}; };
use crate::frontend::FCircuit; use crate::frontend::FCircuit;
use crate::utils::{get_cm_coordinates, vec::is_zero_vec};
use crate::utils::{get_cm_coordinates, pp_hash, vec::is_zero_vec};
use crate::Error; use crate::Error;
use crate::FoldingScheme; use crate::FoldingScheme;
@ -70,6 +69,7 @@ where
pub fn hash( pub fn hash(
&self, &self,
poseidon_config: &PoseidonConfig<C::ScalarField>, poseidon_config: &PoseidonConfig<C::ScalarField>,
pp_hash: C::ScalarField, // public params hash
i: C::ScalarField, i: C::ScalarField,
z_0: Vec<C::ScalarField>, z_0: Vec<C::ScalarField>,
z_i: Vec<C::ScalarField>, z_i: Vec<C::ScalarField>,
@ -80,7 +80,7 @@ where
CRH::<C::ScalarField>::evaluate( CRH::<C::ScalarField>::evaluate(
poseidon_config, poseidon_config,
vec![ vec![
vec![i],
vec![pp_hash, i],
z_0, z_0,
z_i, z_i,
vec![self.u], vec![self.u],
@ -140,9 +140,13 @@ where
pub fn hash_cyclefold( pub fn hash_cyclefold(
&self, &self,
poseidon_config: &PoseidonConfig<C::BaseField>, poseidon_config: &PoseidonConfig<C::BaseField>,
pp_hash: C::BaseField, // public params hash
) -> Result<C::BaseField, Error> { ) -> Result<C::BaseField, Error> {
CRH::<C::BaseField>::evaluate(poseidon_config, self.to_field_elements().unwrap())
.map_err(|e| Error::Other(e.to_string()))
CRH::<C::BaseField>::evaluate(
poseidon_config,
[vec![pp_hash], self.to_field_elements().unwrap()].concat(),
)
.map_err(|e| Error::Other(e.to_string()))
} }
} }
@ -253,6 +257,25 @@ where
pub cf_cs_vp: CS2::VerifierParams, pub cf_cs_vp: CS2::VerifierParams,
} }
impl<C1, C2, CS1, CS2> VerifierParams<C1, C2, CS1, CS2>
where
C1: CurveGroup,
C2: CurveGroup,
CS1: CommitmentScheme<C1>,
CS2: CommitmentScheme<C2>,
{
/// returns the hash of the public parameters of Nova
pub fn pp_hash(&self) -> Result<C1::ScalarField, Error> {
pp_hash::<C1, C2, CS1, CS2>(
&self.r1cs,
&self.cf_r1cs,
&self.cs_vp,
&self.cf_cs_vp,
&self.poseidon_config,
)
}
}
/// Implements Nova+CycleFold's IVC, described in [Nova](https://eprint.iacr.org/2021/370.pdf) and /// Implements Nova+CycleFold's IVC, described in [Nova](https://eprint.iacr.org/2021/370.pdf) and
/// [CycleFold](https://eprint.iacr.org/2023/1192.pdf), following the FoldingScheme trait /// [CycleFold](https://eprint.iacr.org/2023/1192.pdf), following the FoldingScheme trait
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -280,6 +303,8 @@ where
pub cf_cs_pp: CS2::ProverParams, pub cf_cs_pp: CS2::ProverParams,
/// F circuit, the circuit that is being folded /// F circuit, the circuit that is being folded
pub F: FC, pub F: FC,
/// public params hash
pub pp_hash: C1::ScalarField,
pub i: C1::ScalarField, pub i: C1::ScalarField,
/// initial state /// initial state
pub z_0: Vec<C1::ScalarField>, pub z_0: Vec<C1::ScalarField>,
@ -343,8 +368,8 @@ where
cf_cs_pp = prep_param.clone().cf_cs_pp.unwrap(); cf_cs_pp = prep_param.clone().cf_cs_pp.unwrap();
cf_cs_vp = prep_param.clone().cf_cs_vp.unwrap(); cf_cs_vp = prep_param.clone().cf_cs_vp.unwrap();
} else { } else {
(cs_pp, cs_vp) = CS1::setup(&mut rng, r1cs.A.n_rows).unwrap();
(cf_cs_pp, cf_cs_vp) = CS2::setup(&mut rng, cf_r1cs.A.n_rows).unwrap();
(cs_pp, cs_vp) = CS1::setup(&mut rng, r1cs.A.n_rows)?;
(cf_cs_pp, cf_cs_vp) = CS2::setup(&mut rng, cf_r1cs.A.n_rows)?;
} }
let prover_params = ProverParams::<C1, C2, CS1, CS2> { let prover_params = ProverParams::<C1, C2, CS1, CS2> {
@ -356,14 +381,21 @@ where
poseidon_config: prep_param.poseidon_config.clone(), poseidon_config: prep_param.poseidon_config.clone(),
r1cs, r1cs,
cf_r1cs, cf_r1cs,
cs_vp: cs_vp.clone(),
cf_cs_vp: cf_cs_vp.clone(),
cs_vp,
cf_cs_vp,
}; };
Ok((prover_params.clone(), verifier_params))
Ok((prover_params, verifier_params))
} }
/// Initializes the Nova+CycleFold's IVC for the given parameters and initial state `z_0`. /// Initializes the Nova+CycleFold's IVC for the given parameters and initial state `z_0`.
fn init(pp: &Self::ProverParam, F: FC, z_0: Vec<C1::ScalarField>) -> Result<Self, Error> {
fn init(
params: (Self::ProverParam, Self::VerifierParam),
F: FC,
z_0: Vec<C1::ScalarField>,
) -> Result<Self, Error> {
let (pp, vp) = params;
// prepare the circuit to obtain its R1CS // prepare the circuit to obtain its R1CS
let cs = ConstraintSystem::<C1::ScalarField>::new_ref(); let cs = ConstraintSystem::<C1::ScalarField>::new_ref();
let cs2 = ConstraintSystem::<C1::BaseField>::new_ref(); let cs2 = ConstraintSystem::<C1::BaseField>::new_ref();
@ -382,6 +414,9 @@ where
let cs2 = cs2.into_inner().ok_or(Error::NoInnerConstraintSystem)?; let cs2 = cs2.into_inner().ok_or(Error::NoInnerConstraintSystem)?;
let cf_r1cs = extract_r1cs::<C1::BaseField>(&cs2); let cf_r1cs = extract_r1cs::<C1::BaseField>(&cs2);
// compute the public params hash
let pp_hash = vp.pp_hash()?;
// setup the dummy instances // setup the dummy instances
let (w_dummy, u_dummy) = r1cs.dummy_instance(); let (w_dummy, u_dummy) = r1cs.dummy_instance();
let (cf_w_dummy, cf_u_dummy) = cf_r1cs.dummy_instance(); let (cf_w_dummy, cf_u_dummy) = cf_r1cs.dummy_instance();
@ -398,6 +433,7 @@ where
cs_pp: pp.cs_pp.clone(), cs_pp: pp.cs_pp.clone(),
cf_cs_pp: pp.cf_cs_pp.clone(), cf_cs_pp: pp.cf_cs_pp.clone(),
F, F,
pp_hash,
i: C1::ScalarField::zero(), i: C1::ScalarField::zero(),
z_0: z_0.clone(), z_0: z_0.clone(),
z_i: z_0, z_i: z_0,
@ -453,6 +489,7 @@ where
// r_bits is the r used to the RLC of the F' instances // r_bits is the r used to the RLC of the F' instances
let r_bits = ChallengeGadget::<C1>::get_challenge_native( let r_bits = ChallengeGadget::<C1>::get_challenge_native(
&self.poseidon_config, &self.poseidon_config,
self.pp_hash,
self.U_i.clone(), self.U_i.clone(),
self.u_i.clone(), self.u_i.clone(),
cmT, cmT,
@ -471,6 +508,7 @@ where
// u_{i+1}.x[0] = H(i+1, z_0, z_{i+1}, U_{i+1}) // u_{i+1}.x[0] = H(i+1, z_0, z_{i+1}, U_{i+1})
let u_i1_x = U_i1.hash( let u_i1_x = U_i1.hash(
&self.poseidon_config, &self.poseidon_config,
self.pp_hash,
self.i + C1::ScalarField::one(), self.i + C1::ScalarField::one(),
self.z_0.clone(), self.z_0.clone(),
z_i1.clone(), z_i1.clone(),
@ -479,11 +517,14 @@ where
let cf_u_i1_x: C1::ScalarField; let cf_u_i1_x: C1::ScalarField;
if self.i == C1::ScalarField::zero() { if self.i == C1::ScalarField::zero() {
cf_u_i1_x = self.cf_U_i.hash_cyclefold(&self.poseidon_config)?;
cf_u_i1_x = self
.cf_U_i
.hash_cyclefold(&self.poseidon_config, self.pp_hash)?;
// base case // base case
augmented_F_circuit = AugmentedFCircuit::<C1, C2, GC2, FC> { augmented_F_circuit = AugmentedFCircuit::<C1, C2, GC2, FC> {
_gc2: PhantomData, _gc2: PhantomData,
poseidon_config: self.poseidon_config.clone(), poseidon_config: self.poseidon_config.clone(),
pp_hash: Some(self.pp_hash),
i: Some(C1::ScalarField::zero()), // = i=0 i: Some(C1::ScalarField::zero()), // = i=0
i_usize: Some(0), i_usize: Some(0),
z_0: Some(self.z_0.clone()), // = z_i z_0: Some(self.z_0.clone()), // = z_i
@ -552,11 +593,12 @@ where
let (_cfE_w_i, cfE_u_i, cf_W_i1, cf_U_i1, cf_cmT, _) = let (_cfE_w_i, cfE_u_i, cf_W_i1, cf_U_i1, cf_cmT, _) =
self.fold_cyclefold_circuit(cfW_W_i1, cfW_U_i1.clone(), cfE_u_i_x, cfE_circuit)?; self.fold_cyclefold_circuit(cfW_W_i1, cfW_U_i1.clone(), cfE_u_i_x, cfE_circuit)?;
cf_u_i1_x = cf_U_i1.hash_cyclefold(&self.poseidon_config)?;
cf_u_i1_x = cf_U_i1.hash_cyclefold(&self.poseidon_config, self.pp_hash)?;
augmented_F_circuit = AugmentedFCircuit::<C1, C2, GC2, FC> { augmented_F_circuit = AugmentedFCircuit::<C1, C2, GC2, FC> {
_gc2: PhantomData, _gc2: PhantomData,
poseidon_config: self.poseidon_config.clone(), poseidon_config: self.poseidon_config.clone(),
pp_hash: Some(self.pp_hash),
i: Some(self.i), i: Some(self.i),
i_usize: Some(i_usize), i_usize: Some(i_usize),
z_0: Some(self.z_0.clone()), z_0: Some(self.z_0.clone()),
@ -670,14 +712,16 @@ where
return Err(Error::IVCVerificationFail); return Err(Error::IVCVerificationFail);
} }
let pp_hash = vp.pp_hash()?;
// check that u_i's output points to the running instance // check that u_i's output points to the running instance
// u_i.X[0] == H(i, z_0, z_i, U_i) // u_i.X[0] == H(i, z_0, z_i, U_i)
let expected_u_i_x = U_i.hash(&vp.poseidon_config, num_steps, z_0, z_i.clone())?;
let expected_u_i_x = U_i.hash(&vp.poseidon_config, pp_hash, num_steps, z_0, z_i.clone())?;
if expected_u_i_x != u_i.x[0] { if expected_u_i_x != u_i.x[0] {
return Err(Error::IVCVerificationFail); return Err(Error::IVCVerificationFail);
} }
// u_i.X[1] == H(cf_U_i) // u_i.X[1] == H(cf_U_i)
let expected_cf_u_i_x = cf_U_i.hash_cyclefold(&vp.poseidon_config)?;
let expected_cf_u_i_x = cf_U_i.hash_cyclefold(&vp.poseidon_config, pp_hash)?;
if expected_cf_u_i_x != u_i.x[1] { if expected_cf_u_i_x != u_i.x[1] {
return Err(Error::IVCVerificationFail); return Err(Error::IVCVerificationFail);
} }
@ -767,6 +811,7 @@ where
&self.poseidon_config, &self.poseidon_config,
self.cf_r1cs.clone(), self.cf_r1cs.clone(),
self.cf_cs_pp.clone(), self.cf_cs_pp.clone(),
self.pp_hash,
cf_W_i, cf_W_i,
cf_U_i, cf_U_i,
cf_u_i_x, cf_u_i_x,
@ -884,10 +929,10 @@ pub mod tests {
cf_cs_pp: None, cf_cs_pp: None,
cf_cs_vp: None, cf_cs_vp: None,
}; };
let (prover_params, verifier_params) = N::preprocess(&mut rng, &prep_param).unwrap();
let nova_params = N::preprocess(&mut rng, &prep_param).unwrap();
let z_0 = vec![Fr::from(3_u32)]; let z_0 = vec![Fr::from(3_u32)];
let mut nova = N::init(&prover_params, F_circuit, z_0.clone()).unwrap();
let mut nova = N::init(nova_params.clone(), F_circuit, z_0.clone()).unwrap();
let num_steps: usize = 3; let num_steps: usize = 3;
for _ in 0..num_steps { for _ in 0..num_steps {
@ -897,7 +942,7 @@ pub mod tests {
let (running_instance, incoming_instance, cyclefold_instance) = nova.instances(); let (running_instance, incoming_instance, cyclefold_instance) = nova.instances();
N::<CS1, CS2>::verify( N::<CS1, CS2>::verify(
verifier_params,
nova_params.1, // Nova's verifier params
z_0, z_0,
nova.z_i, nova.z_i,
nova.i, nova.i,

+ 4
- 2
folding-schemes/src/folding/nova/nifs.rs

@ -4,7 +4,7 @@ use ark_std::Zero;
use std::marker::PhantomData; use std::marker::PhantomData;
use super::{CommittedInstance, Witness}; use super::{CommittedInstance, Witness};
use crate::ccs::r1cs::R1CS;
use crate::arith::r1cs::R1CS;
use crate::commitment::CommitmentScheme; use crate::commitment::CommitmentScheme;
use crate::transcript::Transcript; use crate::transcript::Transcript;
use crate::utils::vec::{hadamard, mat_vec_mul, vec_add, vec_scalar_mul, vec_sub}; use crate::utils::vec::{hadamard, mat_vec_mul, vec_add, vec_scalar_mul, vec_sub};
@ -205,7 +205,7 @@ pub mod tests {
use ark_pallas::{Fr, Projective}; use ark_pallas::{Fr, Projective};
use ark_std::{ops::Mul, UniformRand}; use ark_std::{ops::Mul, UniformRand};
use crate::ccs::r1cs::tests::{get_test_r1cs, get_test_z};
use crate::arith::r1cs::tests::{get_test_r1cs, get_test_z};
use crate::commitment::pedersen::{Params as PedersenParams, Pedersen}; use crate::commitment::pedersen::{Params as PedersenParams, Pedersen};
use crate::folding::nova::circuits::ChallengeGadget; use crate::folding::nova::circuits::ChallengeGadget;
use crate::folding::nova::traits::NovaR1CS; use crate::folding::nova::traits::NovaR1CS;
@ -259,8 +259,10 @@ pub mod tests {
let poseidon_config = poseidon_canonical_config::<C::ScalarField>(); let poseidon_config = poseidon_canonical_config::<C::ScalarField>();
let pp_hash = C::ScalarField::from(42u32); // only for test
let r_bits = ChallengeGadget::<C>::get_challenge_native( let r_bits = ChallengeGadget::<C>::get_challenge_native(
&poseidon_config, &poseidon_config,
pp_hash,
ci1.clone(), ci1.clone(),
ci2.clone(), ci2.clone(),
cmT, cmT,

+ 16
- 30
folding-schemes/src/folding/nova/serialize.rs

@ -14,7 +14,7 @@ use super::{circuits::AugmentedFCircuit, Nova, ProverParams};
use super::{CommittedInstance, Witness}; use super::{CommittedInstance, Witness};
use crate::folding::circuits::{cyclefold::CycleFoldCircuit, CF2}; use crate::folding::circuits::{cyclefold::CycleFoldCircuit, CF2};
use crate::{ use crate::{
ccs::r1cs::extract_r1cs, commitment::CommitmentScheme, folding::circuits::CF1,
arith::r1cs::extract_r1cs, commitment::CommitmentScheme, folding::circuits::CF1,
frontend::FCircuit, frontend::FCircuit,
}; };
@ -41,6 +41,7 @@ where
mut writer: W, mut writer: W,
compress: ark_serialize::Compress, compress: ark_serialize::Compress,
) -> Result<(), ark_serialize::SerializationError> { ) -> Result<(), ark_serialize::SerializationError> {
self.pp_hash.serialize_with_mode(&mut writer, compress)?;
self.i.serialize_with_mode(&mut writer, compress)?; self.i.serialize_with_mode(&mut writer, compress)?;
self.z_0.serialize_with_mode(&mut writer, compress)?; self.z_0.serialize_with_mode(&mut writer, compress)?;
self.z_i.serialize_with_mode(&mut writer, compress)?; self.z_i.serialize_with_mode(&mut writer, compress)?;
@ -53,7 +54,8 @@ where
} }
fn serialized_size(&self, compress: ark_serialize::Compress) -> usize { fn serialized_size(&self, compress: ark_serialize::Compress) -> usize {
self.i.serialized_size(compress)
self.pp_hash.serialized_size(compress)
+ self.i.serialized_size(compress)
+ self.z_0.serialized_size(compress) + self.z_0.serialized_size(compress)
+ self.z_i.serialized_size(compress) + self.z_i.serialized_size(compress)
+ self.w_i.serialized_size(compress) + self.w_i.serialized_size(compress)
@ -115,6 +117,7 @@ where
prover_params: ProverParams<C1, C2, CS1, CS2>, prover_params: ProverParams<C1, C2, CS1, CS2>,
poseidon_config: PoseidonConfig<C1::ScalarField>, poseidon_config: PoseidonConfig<C1::ScalarField>,
) -> Result<Self, ark_serialize::SerializationError> { ) -> Result<Self, ark_serialize::SerializationError> {
let pp_hash = C1::ScalarField::deserialize_with_mode(&mut reader, compress, validate)?;
let i = C1::ScalarField::deserialize_with_mode(&mut reader, compress, validate)?; let i = C1::ScalarField::deserialize_with_mode(&mut reader, compress, validate)?;
let z_0 = Vec::<C1::ScalarField>::deserialize_with_mode(&mut reader, compress, validate)?; let z_0 = Vec::<C1::ScalarField>::deserialize_with_mode(&mut reader, compress, validate)?;
let z_i = Vec::<C1::ScalarField>::deserialize_with_mode(&mut reader, compress, validate)?; let z_i = Vec::<C1::ScalarField>::deserialize_with_mode(&mut reader, compress, validate)?;
@ -151,8 +154,13 @@ where
_gc1: PhantomData, _gc1: PhantomData,
_c2: PhantomData, _c2: PhantomData,
_gc2: PhantomData, _gc2: PhantomData,
r1cs,
cf_r1cs,
poseidon_config,
cs_pp: prover_params.cs_pp, cs_pp: prover_params.cs_pp,
cf_cs_pp: prover_params.cf_cs_pp, cf_cs_pp: prover_params.cf_cs_pp,
F: f_circuit,
pp_hash,
i, i,
z_0, z_0,
z_i, z_i,
@ -162,10 +170,6 @@ where
U_i, U_i,
cf_W_i, cf_W_i,
cf_U_i, cf_U_i,
r1cs,
cf_r1cs,
poseidon_config,
F: f_circuit,
}) })
} }
} }
@ -174,17 +178,12 @@ where
pub mod tests { pub mod tests {
use ark_bn254::{constraints::GVar, Bn254, Fr, G1Projective as Projective}; use ark_bn254::{constraints::GVar, Bn254, Fr, G1Projective as Projective};
use ark_grumpkin::{constraints::GVar as GVar2, Projective as Projective2}; use ark_grumpkin::{constraints::GVar as GVar2, Projective as Projective2};
use ark_poly_commit::kzg10::VerifierKey as KZGVerifierKey;
use ark_serialize::{CanonicalSerialize, Compress, Validate}; use ark_serialize::{CanonicalSerialize, Compress, Validate};
use std::{fs, io::Write}; use std::{fs, io::Write};
use crate::{ use crate::{
commitment::{
kzg::{ProverKey as KZGProverKey, KZG},
pedersen::Pedersen,
CommitmentScheme,
},
folding::nova::{get_cs_params_len, Nova, ProverParams},
commitment::{kzg::KZG, pedersen::Pedersen},
folding::nova::{Nova, PreprocessorParam},
frontend::{tests::CubicFCircuit, FCircuit}, frontend::{tests::CubicFCircuit, FCircuit},
transcript::poseidon::poseidon_canonical_config, transcript::poseidon::poseidon_canonical_config,
FoldingScheme, FoldingScheme,
@ -195,15 +194,6 @@ pub mod tests {
let mut rng = ark_std::test_rng(); let mut rng = ark_std::test_rng();
let poseidon_config = poseidon_canonical_config::<Fr>(); let poseidon_config = poseidon_canonical_config::<Fr>();
let F_circuit = CubicFCircuit::<Fr>::new(()).unwrap(); let F_circuit = CubicFCircuit::<Fr>::new(()).unwrap();
let (cs_len, cf_cs_len) =
get_cs_params_len::<Projective, GVar, Projective2, GVar2, CubicFCircuit<Fr>>(
&poseidon_config,
F_circuit,
)
.unwrap();
let (kzg_pk, _): (KZGProverKey<Projective>, KZGVerifierKey<Bn254>) =
KZG::<Bn254>::setup(&mut rng, cs_len).unwrap();
let (cf_pedersen_params, _) = Pedersen::<Projective2>::setup(&mut rng, cf_cs_len).unwrap();
// Initialize nova and make multiple `prove_step()` // Initialize nova and make multiple `prove_step()`
type N = Nova< type N = Nova<
@ -215,15 +205,11 @@ pub mod tests {
KZG<'static, Bn254>, KZG<'static, Bn254>,
Pedersen<Projective2>, Pedersen<Projective2>,
>; >;
let prover_params =
ProverParams::<Projective, Projective2, KZG<Bn254>, Pedersen<Projective2>> {
poseidon_config: poseidon_config.clone(),
cs_pp: kzg_pk.clone(),
cf_cs_pp: cf_pedersen_params.clone(),
};
let prep_param = PreprocessorParam::new(poseidon_config.clone(), F_circuit);
let nova_params = N::preprocess(&mut rng, &prep_param).unwrap();
let z_0 = vec![Fr::from(3_u32)]; let z_0 = vec![Fr::from(3_u32)];
let mut nova = N::init(&prover_params, F_circuit, z_0.clone()).unwrap();
let mut nova = N::init(nova_params.clone(), F_circuit, z_0.clone()).unwrap();
let num_steps: usize = 3; let num_steps: usize = 3;
for _ in 0..num_steps { for _ in 0..num_steps {
@ -257,7 +243,7 @@ pub mod tests {
bytes.as_slice(), bytes.as_slice(),
Compress::No, Compress::No,
Validate::No, Validate::No,
prover_params,
nova_params.0, // Nova's prover params
poseidon_config, poseidon_config,
) )
.unwrap(); .unwrap();

+ 1
- 1
folding-schemes/src/folding/nova/traits.rs

@ -3,7 +3,7 @@ use ark_ec::{CurveGroup, Group};
use ark_std::{One, Zero}; use ark_std::{One, Zero};
use super::{CommittedInstance, Witness}; use super::{CommittedInstance, Witness};
use crate::ccs::r1cs::R1CS;
use crate::arith::{r1cs::R1CS, Arith};
use crate::Error; use crate::Error;
/// NovaR1CS extends R1CS methods with Nova specific methods /// NovaR1CS extends R1CS methods with Nova specific methods

+ 2
- 2
folding-schemes/src/folding/protogalaxy/folding.rs

@ -16,7 +16,7 @@ use super::utils::{all_powers, betas_star, exponential_powers};
use super::ProtoGalaxyError; use super::ProtoGalaxyError;
use super::{CommittedInstance, Witness}; use super::{CommittedInstance, Witness};
use crate::ccs::r1cs::R1CS;
use crate::arith::r1cs::R1CS;
use crate::transcript::Transcript; use crate::transcript::Transcript;
use crate::utils::vec::*; use crate::utils::vec::*;
use crate::utils::virtual_polynomial::bit_decompose; use crate::utils::virtual_polynomial::bit_decompose;
@ -383,7 +383,7 @@ mod tests {
use ark_pallas::{Fr, Projective}; use ark_pallas::{Fr, Projective};
use ark_std::UniformRand; use ark_std::UniformRand;
use crate::ccs::r1cs::tests::{get_test_r1cs, get_test_z};
use crate::arith::r1cs::tests::{get_test_r1cs, get_test_z};
use crate::commitment::{pedersen::Pedersen, CommitmentScheme}; use crate::commitment::{pedersen::Pedersen, CommitmentScheme};
use crate::transcript::poseidon::{poseidon_canonical_config, PoseidonTranscript}; use crate::transcript::poseidon::{poseidon_canonical_config, PoseidonTranscript};

+ 2
- 2
folding-schemes/src/lib.rs

@ -10,7 +10,7 @@ use thiserror::Error;
use crate::frontend::FCircuit; use crate::frontend::FCircuit;
pub mod ccs;
pub mod arith;
pub mod commitment; pub mod commitment;
pub mod constants; pub mod constants;
pub mod folding; pub mod folding;
@ -122,7 +122,7 @@ where
) -> Result<(Self::ProverParam, Self::VerifierParam), Error>; ) -> Result<(Self::ProverParam, Self::VerifierParam), Error>;
fn init( fn init(
pp: &Self::ProverParam,
params: (Self::ProverParam, Self::VerifierParam),
step_circuit: FC, step_circuit: FC,
z_0: Vec<C1::ScalarField>, // initial state z_0: Vec<C1::ScalarField>, // initial state
) -> Result<Self, Error>; ) -> Result<Self, Error>;

+ 1
- 1
folding-schemes/src/utils/mle.rs

@ -104,7 +104,7 @@ pub fn dense_vec_to_mle(n_vars: usize, v: &[F]) -> SparseMultilin
mod tests { mod tests {
use super::*; use super::*;
use crate::{ use crate::{
ccs::tests::get_test_z,
arith::ccs::tests::get_test_z,
utils::multilinear_polynomial::fix_variables, utils::multilinear_polynomial::fix_variables,
utils::multilinear_polynomial::tests::fix_last_variables, utils::multilinear_polynomial::tests::fix_last_variables,
utils::{hypercube::BooleanHypercube, vec::tests::to_F_matrix}, utils::{hypercube::BooleanHypercube, vec::tests::to_F_matrix},

+ 68
- 0
folding-schemes/src/utils/mod.rs

@ -1,6 +1,13 @@
use ark_crypto_primitives::sponge::poseidon::PoseidonConfig;
use ark_ec::{AffineRepr, CurveGroup}; use ark_ec::{AffineRepr, CurveGroup};
use ark_ff::PrimeField; use ark_ff::PrimeField;
use ark_serialize::CanonicalSerialize;
use ark_std::Zero; use ark_std::Zero;
use sha3::{Digest, Sha3_256};
use crate::arith::Arith;
use crate::commitment::CommitmentScheme;
use crate::Error;
pub mod gadgets; pub mod gadgets;
pub mod hypercube; pub mod hypercube;
@ -32,3 +39,64 @@ pub fn get_cm_coordinates(cm: &C) -> Vec {
let (cm_x, cm_y) = cm.xy().unwrap_or(zero); let (cm_x, cm_y) = cm.xy().unwrap_or(zero);
vec![*cm_x, *cm_y] vec![*cm_x, *cm_y]
} }
/// returns the hash of the given public parameters of the Folding Scheme
pub fn pp_hash<C1, C2, CS1, CS2>(
arith: &impl Arith<C1::ScalarField>,
cf_arith: &impl Arith<C2::ScalarField>,
cs_vp: &CS1::VerifierParams,
cf_cs_vp: &CS2::VerifierParams,
poseidon_config: &PoseidonConfig<C1::ScalarField>,
) -> Result<C1::ScalarField, Error>
where
C1: CurveGroup,
C2: CurveGroup,
CS1: CommitmentScheme<C1>,
CS2: CommitmentScheme<C2>,
{
let mut hasher = Sha3_256::new();
// Fr & Fq modulus bit size
hasher.update(C1::ScalarField::MODULUS_BIT_SIZE.to_le_bytes());
hasher.update(C2::ScalarField::MODULUS_BIT_SIZE.to_le_bytes());
// AugmentedFCircuit Arith params
hasher.update(arith.params_to_bytes());
// CycleFold Circuit Arith params
hasher.update(cf_arith.params_to_bytes());
// cs_vp & cf_cs_vp (commitments setup)
let mut cs_vp_bytes = Vec::new();
cs_vp.serialize_uncompressed(&mut cs_vp_bytes)?;
hasher.update(cs_vp_bytes);
let mut cf_cs_vp_bytes = Vec::new();
cf_cs_vp.serialize_uncompressed(&mut cf_cs_vp_bytes)?;
hasher.update(cf_cs_vp_bytes);
// poseidon params
let mut poseidon_config_bytes = Vec::new();
poseidon_config
.full_rounds
.serialize_uncompressed(&mut poseidon_config_bytes)?;
poseidon_config
.partial_rounds
.serialize_uncompressed(&mut poseidon_config_bytes)?;
poseidon_config
.alpha
.serialize_uncompressed(&mut poseidon_config_bytes)?;
poseidon_config
.ark
.serialize_uncompressed(&mut poseidon_config_bytes)?;
poseidon_config
.mds
.serialize_uncompressed(&mut poseidon_config_bytes)?;
poseidon_config
.rate
.serialize_uncompressed(&mut poseidon_config_bytes)?;
poseidon_config
.capacity
.serialize_uncompressed(&mut poseidon_config_bytes)?;
hasher.update(poseidon_config_bytes);
let public_params_hash = hasher.finalize();
Ok(C1::ScalarField::from_le_bytes_mod_order(
&public_params_hash,
))
}

+ 6
- 6
solidity-verifiers/src/verifiers/g16.rs

@ -3,7 +3,7 @@ use crate::utils::encoding::{G1Repr, G2Repr};
use crate::utils::HeaderInclusion; use crate::utils::HeaderInclusion;
use crate::{ProtocolVerifierKey, GPL3_SDPX_IDENTIFIER}; use crate::{ProtocolVerifierKey, GPL3_SDPX_IDENTIFIER};
use ark_bn254::Bn254; use ark_bn254::Bn254;
use ark_groth16::VerifyingKey;
use ark_groth16::VerifyingKey as ArkVerifyingKey;
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
use askama::Template; use askama::Template;
@ -48,10 +48,10 @@ impl From for Groth16Verifier {
// Ideally this would be linked to the `Decider` trait in FoldingSchemes. // Ideally this would be linked to the `Decider` trait in FoldingSchemes.
// For now, this is the easiest as NovaCycleFold isn't clear target from where we can get all it's needed arguments. // For now, this is the easiest as NovaCycleFold isn't clear target from where we can get all it's needed arguments.
#[derive(CanonicalDeserialize, CanonicalSerialize, Clone, PartialEq, Debug)] #[derive(CanonicalDeserialize, CanonicalSerialize, Clone, PartialEq, Debug)]
pub struct Groth16VerifierKey(pub(crate) VerifyingKey<Bn254>);
pub struct Groth16VerifierKey(pub(crate) ArkVerifyingKey<Bn254>);
impl From<VerifyingKey<Bn254>> for Groth16VerifierKey {
fn from(value: VerifyingKey<Bn254>) -> Self {
impl From<ArkVerifyingKey<Bn254>> for Groth16VerifierKey {
fn from(value: ArkVerifyingKey<Bn254>) -> Self {
Self(value) Self(value)
} }
} }
@ -95,7 +95,7 @@ mod tests {
#[test] #[test]
fn groth16_vk_serde_roundtrip() { fn groth16_vk_serde_roundtrip() {
let (_, _, _, vk, _) = setup(DEFAULT_SETUP_LEN);
let (_, _, _, _, vk, _) = setup(DEFAULT_SETUP_LEN);
let g16_vk = Groth16VerifierKey::from(vk); let g16_vk = Groth16VerifierKey::from(vk);
let mut bytes = vec![]; let mut bytes = vec![];
@ -109,7 +109,7 @@ mod tests {
#[test] #[test]
fn test_groth16_verifier_accepts_and_rejects_proofs() { fn test_groth16_verifier_accepts_and_rejects_proofs() {
let mut rng = ark_std::rand::rngs::StdRng::seed_from_u64(test_rng().next_u64()); let mut rng = ark_std::rand::rngs::StdRng::seed_from_u64(test_rng().next_u64());
let (_, _, g16_pk, g16_vk, circuit) = setup(DEFAULT_SETUP_LEN);
let (_, _, _, g16_pk, g16_vk, circuit) = setup(DEFAULT_SETUP_LEN);
let g16_vk = Groth16VerifierKey::from(g16_vk); let g16_vk = Groth16VerifierKey::from(g16_vk);
let proof = Groth16::<Bn254>::prove(&g16_pk, circuit, &mut rng).unwrap(); let proof = Groth16::<Bn254>::prove(&g16_pk, circuit, &mut rng).unwrap();

+ 3
- 3
solidity-verifiers/src/verifiers/kzg.rs

@ -102,7 +102,7 @@ mod tests {
#[test] #[test]
fn kzg_vk_serde_roundtrip() { fn kzg_vk_serde_roundtrip() {
let (pk, vk, _, _, _) = setup(DEFAULT_SETUP_LEN);
let (_, pk, vk, _, _, _) = setup(DEFAULT_SETUP_LEN);
let kzg_vk = KZG10VerifierKey::from((vk, pk.powers_of_g[0..3].to_vec())); let kzg_vk = KZG10VerifierKey::from((vk, pk.powers_of_g[0..3].to_vec()));
let mut bytes = vec![]; let mut bytes = vec![];
@ -115,7 +115,7 @@ mod tests {
#[test] #[test]
fn kzg_verifier_compiles() { fn kzg_verifier_compiles() {
let (kzg_pk, kzg_vk, _, _, _) = setup(DEFAULT_SETUP_LEN);
let (_, kzg_pk, kzg_vk, _, _, _) = setup(DEFAULT_SETUP_LEN);
let kzg_vk = KZG10VerifierKey::from((kzg_vk.clone(), kzg_pk.powers_of_g[0..3].to_vec())); let kzg_vk = KZG10VerifierKey::from((kzg_vk.clone(), kzg_pk.powers_of_g[0..3].to_vec()));
let res = HeaderInclusion::<KZG10Verifier>::builder() let res = HeaderInclusion::<KZG10Verifier>::builder()
@ -136,7 +136,7 @@ mod tests {
let transcript_p = &mut PoseidonTranscript::<G1>::new(&poseidon_config); let transcript_p = &mut PoseidonTranscript::<G1>::new(&poseidon_config);
let transcript_v = &mut PoseidonTranscript::<G1>::new(&poseidon_config); let transcript_v = &mut PoseidonTranscript::<G1>::new(&poseidon_config);
let (kzg_pk, kzg_vk, _, _, _) = setup(DEFAULT_SETUP_LEN);
let (_, kzg_pk, kzg_vk, _, _, _) = setup(DEFAULT_SETUP_LEN);
let kzg_vk = KZG10VerifierKey::from((kzg_vk.clone(), kzg_pk.powers_of_g[0..3].to_vec())); let kzg_vk = KZG10VerifierKey::from((kzg_vk.clone(), kzg_pk.powers_of_g[0..3].to_vec()));
let v: Vec<Fr> = std::iter::repeat_with(|| Fr::rand(&mut rng)) let v: Vec<Fr> = std::iter::repeat_with(|| Fr::rand(&mut rng))

+ 3
- 1
solidity-verifiers/src/verifiers/mod.rs

@ -97,6 +97,7 @@ pub mod tests {
pub fn setup<'a>( pub fn setup<'a>(
n: usize, n: usize,
) -> ( ) -> (
Fr, // public params hash
KZGProverKey<'a, G1>, KZGProverKey<'a, G1>,
KZGVerifierKey<Bn254>, KZGVerifierKey<Bn254>,
ark_groth16::ProvingKey<Bn254>, ark_groth16::ProvingKey<Bn254>,
@ -115,6 +116,7 @@ pub mod tests {
let (kzg_pk, kzg_vk): (KZGProverKey<G1>, KZGVerifierKey<Bn254>) = let (kzg_pk, kzg_vk): (KZGProverKey<G1>, KZGVerifierKey<Bn254>) =
KZG::<Bn254>::setup(&mut rng, n).unwrap(); KZG::<Bn254>::setup(&mut rng, n).unwrap();
(kzg_pk, kzg_vk, g16_pk, g16_vk, circuit)
let pp_hash = Fr::from(42u32); // only for test
(pp_hash, kzg_pk, kzg_vk, g16_pk, g16_vk, circuit)
} }
} }

+ 103
- 114
solidity-verifiers/src/verifiers/nova_cyclefold.rs

@ -1,9 +1,10 @@
#![allow(non_snake_case)] #![allow(non_snake_case)]
#![allow(non_camel_case_types)] #![allow(non_camel_case_types)]
#![allow(clippy::upper_case_acronyms)]
use ark_bn254::{Bn254, Fq, G1Affine};
use ark_groth16::VerifyingKey;
use ark_poly_commit::kzg10::VerifierKey;
use ark_bn254::{Bn254, Fq, Fr, G1Affine};
use ark_groth16::VerifyingKey as ArkG16VerifierKey;
use ark_poly_commit::kzg10::VerifierKey as ArkKZG10VerifierKey;
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
use askama::Template; use askama::Template;
@ -27,6 +28,7 @@ pub fn get_decider_template_for_cyclefold_decider(
#[derive(Template, Default)] #[derive(Template, Default)]
#[template(path = "nova_cyclefold_decider.askama.sol", ext = "sol")] #[template(path = "nova_cyclefold_decider.askama.sol", ext = "sol")]
pub struct NovaCycleFoldDecider { pub struct NovaCycleFoldDecider {
pp_hash: Fr, // public params hash
groth16_verifier: Groth16Verifier, groth16_verifier: Groth16Verifier,
kzg10_verifier: KZG10Verifier, kzg10_verifier: KZG10Verifier,
// z_len denotes the FCircuit state (z_i) length // z_len denotes the FCircuit state (z_i) length
@ -42,6 +44,7 @@ impl From for NovaCycleFoldDecider {
let public_inputs_len = groth16_verifier.gamma_abc_len; let public_inputs_len = groth16_verifier.gamma_abc_len;
let bits_per_limb = NonNativeUintVar::<Fq>::bits_per_limb(); let bits_per_limb = NonNativeUintVar::<Fq>::bits_per_limb();
Self { Self {
pp_hash: value.pp_hash,
groth16_verifier, groth16_verifier,
kzg10_verifier: KZG10Verifier::from(value.kzg_vk), kzg10_verifier: KZG10Verifier::from(value.kzg_vk),
z_len: value.z_len, z_len: value.z_len,
@ -54,6 +57,7 @@ impl From for NovaCycleFoldDecider {
#[derive(CanonicalDeserialize, CanonicalSerialize, PartialEq, Debug, Clone)] #[derive(CanonicalDeserialize, CanonicalSerialize, PartialEq, Debug, Clone)]
pub struct NovaCycleFoldVerifierKey { pub struct NovaCycleFoldVerifierKey {
pp_hash: Fr,
g16_vk: Groth16VerifierKey, g16_vk: Groth16VerifierKey,
kzg_vk: KZG10VerifierKey, kzg_vk: KZG10VerifierKey,
z_len: usize, z_len: usize,
@ -73,25 +77,37 @@ impl ProtocolVerifierKey for NovaCycleFoldVerifierKey {
} }
} }
impl From<(Groth16VerifierKey, KZG10VerifierKey, usize)> for NovaCycleFoldVerifierKey {
fn from(value: (Groth16VerifierKey, KZG10VerifierKey, usize)) -> Self {
impl From<(Fr, Groth16VerifierKey, KZG10VerifierKey, usize)> for NovaCycleFoldVerifierKey {
fn from(value: (Fr, Groth16VerifierKey, KZG10VerifierKey, usize)) -> Self {
Self { Self {
g16_vk: value.0,
kzg_vk: value.1,
z_len: value.2,
pp_hash: value.0,
g16_vk: value.1,
kzg_vk: value.2,
z_len: value.3,
} }
} }
} }
// implements From assuming that the 'batchCheck' method from the KZG10 template will not be used // implements From assuming that the 'batchCheck' method from the KZG10 template will not be used
// in the NovaCycleFoldDecider verifier contract // in the NovaCycleFoldDecider verifier contract
impl From<((VerifyingKey<Bn254>, VerifierKey<Bn254>), usize)> for NovaCycleFoldVerifierKey {
fn from(value: ((VerifyingKey<Bn254>, VerifierKey<Bn254>), usize)) -> Self {
impl
From<(
(Fr, ArkG16VerifierKey<Bn254>, ArkKZG10VerifierKey<Bn254>),
usize,
)> for NovaCycleFoldVerifierKey
{
fn from(
value: (
(Fr, ArkG16VerifierKey<Bn254>, ArkKZG10VerifierKey<Bn254>),
usize,
),
) -> Self {
let decider_vp = value.0; let decider_vp = value.0;
let g16_vk = Groth16VerifierKey::from(decider_vp.0);
let g16_vk = Groth16VerifierKey::from(decider_vp.1);
// pass `Vec::new()` since batchCheck will not be used // pass `Vec::new()` since batchCheck will not be used
let kzg_vk = KZG10VerifierKey::from((decider_vp.1, Vec::new()));
let kzg_vk = KZG10VerifierKey::from((decider_vp.2, Vec::new()));
Self { Self {
pp_hash: decider_vp.0,
g16_vk, g16_vk,
kzg_vk, kzg_vk,
z_len: value.1, z_len: value.1,
@ -101,12 +117,14 @@ impl From<((VerifyingKey, VerifierKey), usize)> for NovaCycleFoldV
impl NovaCycleFoldVerifierKey { impl NovaCycleFoldVerifierKey {
pub fn new( pub fn new(
vkey_g16: VerifyingKey<Bn254>,
vkey_kzg: VerifierKey<Bn254>,
pp_hash: Fr,
vkey_g16: ArkG16VerifierKey<Bn254>,
vkey_kzg: ArkKZG10VerifierKey<Bn254>,
crs_points: Vec<G1Affine>, crs_points: Vec<G1Affine>,
z_len: usize, z_len: usize,
) -> Self { ) -> Self {
Self { Self {
pp_hash,
g16_vk: Groth16VerifierKey::from(vkey_g16), g16_vk: Groth16VerifierKey::from(vkey_g16),
kzg_vk: KZG10VerifierKey::from((vkey_kzg, crs_points)), kzg_vk: KZG10VerifierKey::from((vkey_kzg, crs_points)),
z_len, z_len,
@ -117,12 +135,9 @@ impl NovaCycleFoldVerifierKey {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use ark_bn254::{constraints::GVar, Bn254, Fr, G1Projective as G1}; use ark_bn254::{constraints::GVar, Bn254, Fr, G1Projective as G1};
use ark_crypto_primitives::snark::SNARK;
use ark_ff::PrimeField; use ark_ff::PrimeField;
use ark_groth16::VerifyingKey as G16VerifierKey;
use ark_groth16::{Groth16, ProvingKey};
use ark_groth16::Groth16;
use ark_grumpkin::{constraints::GVar as GVar2, Projective as G2}; use ark_grumpkin::{constraints::GVar as GVar2, Projective as G2};
use ark_poly_commit::kzg10::VerifierKey as KZGVerifierKey;
use ark_r1cs_std::alloc::AllocVar; use ark_r1cs_std::alloc::AllocVar;
use ark_r1cs_std::fields::fp::FpVar; use ark_r1cs_std::fields::fp::FpVar;
use ark_relations::r1cs::{ConstraintSystemRef, SynthesisError}; use ark_relations::r1cs::{ConstraintSystemRef, SynthesisError};
@ -132,15 +147,10 @@ mod tests {
use std::time::Instant; use std::time::Instant;
use folding_schemes::{ use folding_schemes::{
commitment::{
kzg::{ProverKey as KZGProverKey, KZG},
pedersen::Pedersen,
CommitmentScheme,
},
commitment::{kzg::KZG, pedersen::Pedersen},
folding::nova::{ folding::nova::{
decider_eth::{prepare_calldata, Decider as DeciderEth}, decider_eth::{prepare_calldata, Decider as DeciderEth},
decider_eth_circuit::DeciderEthCircuit,
get_cs_params_len, Nova, ProverParams,
Nova, PreprocessorParam,
}, },
frontend::FCircuit, frontend::FCircuit,
transcript::poseidon::poseidon_canonical_config, transcript::poseidon::poseidon_canonical_config,
@ -156,6 +166,24 @@ mod tests {
NovaCycleFoldVerifierKey, ProtocolVerifierKey, NovaCycleFoldVerifierKey, ProtocolVerifierKey,
}; };
type NOVA<FC> = Nova<G1, GVar, G2, GVar2, FC, KZG<'static, Bn254>, Pedersen<G2>>;
type DECIDER<FC> = DeciderEth<
G1,
GVar,
G2,
GVar2,
FC,
KZG<'static, Bn254>,
Pedersen<G2>,
Groth16<Bn254>,
NOVA<FC>,
>;
type FS_PP<FC> = <NOVA<FC> as FoldingScheme<G1, G2, FC>>::ProverParam;
type FS_VP<FC> = <NOVA<FC> as FoldingScheme<G1, G2, FC>>::VerifierParam;
type DECIDER_PP<FC> = <DECIDER<FC> as Decider<G1, G2, FC, NOVA<FC>>>::ProverParam;
type DECIDER_VP<FC> = <DECIDER<FC> as Decider<G1, G2, FC, NOVA<FC>>>::VerifierParam;
/// Test circuit to be folded /// Test circuit to be folded
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct CubicFCircuit<F: PrimeField> { pub struct CubicFCircuit<F: PrimeField> {
@ -256,10 +284,10 @@ mod tests {
#[test] #[test]
fn nova_cyclefold_vk_serde_roundtrip() { fn nova_cyclefold_vk_serde_roundtrip() {
let (_, kzg_vk, _, g16_vk, _) = setup(DEFAULT_SETUP_LEN);
let (pp_hash, _, kzg_vk, _, g16_vk, _) = setup(DEFAULT_SETUP_LEN);
let mut bytes = vec![]; let mut bytes = vec![];
let nova_cyclefold_vk = NovaCycleFoldVerifierKey::from(((g16_vk, kzg_vk), 1));
let nova_cyclefold_vk = NovaCycleFoldVerifierKey::from(((pp_hash, g16_vk, kzg_vk), 1));
nova_cyclefold_vk nova_cyclefold_vk
.serialize_protocol_verifier_key(&mut bytes) .serialize_protocol_verifier_key(&mut bytes)
@ -272,8 +300,8 @@ mod tests {
#[test] #[test]
fn nova_cyclefold_decider_template_renders() { fn nova_cyclefold_decider_template_renders() {
let (_, kzg_vk, _, g16_vk, _) = setup(DEFAULT_SETUP_LEN);
let nova_cyclefold_vk = NovaCycleFoldVerifierKey::from(((g16_vk, kzg_vk), 1));
let (pp_hash, _, kzg_vk, _, g16_vk, _) = setup(DEFAULT_SETUP_LEN);
let nova_cyclefold_vk = NovaCycleFoldVerifierKey::from(((pp_hash, g16_vk, kzg_vk), 1));
let decider_solidity_code = HeaderInclusion::<NovaCycleFoldDecider>::builder() let decider_solidity_code = HeaderInclusion::<NovaCycleFoldDecider>::builder()
.template(nova_cyclefold_vk) .template(nova_cyclefold_vk)
@ -282,59 +310,29 @@ mod tests {
save_solidity("NovaDecider.sol", &decider_solidity_code.render().unwrap()); save_solidity("NovaDecider.sol", &decider_solidity_code.render().unwrap());
} }
#[allow(clippy::type_complexity)]
fn init_test_prover_params<FC: FCircuit<Fr, Params = ()>>() -> (
ProverParams<G1, G2, KZG<'static, Bn254>, Pedersen<G2>>,
KZGVerifierKey<Bn254>,
) {
let mut rng = ark_std::test_rng();
let poseidon_config = poseidon_canonical_config::<Fr>();
let f_circuit = FC::new(()).unwrap();
let (cs_len, cf_cs_len) =
get_cs_params_len::<G1, GVar, G2, GVar2, FC>(&poseidon_config, f_circuit).unwrap();
let (kzg_pk, kzg_vk): (KZGProverKey<G1>, KZGVerifierKey<Bn254>) =
KZG::<Bn254>::setup(&mut rng, cs_len).unwrap();
let (cf_pedersen_params, _) = Pedersen::<G2>::setup(&mut rng, cf_cs_len).unwrap();
let fs_prover_params = ProverParams::<G1, G2, KZG<Bn254>, Pedersen<G2>> {
poseidon_config: poseidon_config.clone(),
cs_pp: kzg_pk.clone(),
cf_cs_pp: cf_pedersen_params,
};
(fs_prover_params, kzg_vk)
}
/// Initializes Nova parameters and DeciderEth parameters. Only for test purposes. /// Initializes Nova parameters and DeciderEth parameters. Only for test purposes.
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
fn init_params<FC: FCircuit<Fr, Params = ()>>() -> (
ProverParams<G1, G2, KZG<'static, Bn254>, Pedersen<G2>>,
KZGVerifierKey<Bn254>,
ProvingKey<Bn254>,
G16VerifierKey<Bn254>,
) {
fn init_params<FC: FCircuit<Fr, Params = ()>>(
) -> ((FS_PP<FC>, FS_VP<FC>), (DECIDER_PP<FC>, DECIDER_VP<FC>)) {
let mut rng = rand::rngs::OsRng; let mut rng = rand::rngs::OsRng;
let start = Instant::now();
let (fs_prover_params, kzg_vk) = init_test_prover_params::<FC>();
println!("generated Nova folding params: {:?}", start.elapsed());
let f_circuit = FC::new(()).unwrap();
pub type NOVA_FCircuit<FC> =
Nova<G1, GVar, G2, GVar2, FC, KZG<'static, Bn254>, Pedersen<G2>>;
let z_0 = vec![Fr::zero(); f_circuit.state_len()];
let nova = NOVA_FCircuit::init(&fs_prover_params, f_circuit, z_0.clone()).unwrap();
let poseidon_config = poseidon_canonical_config::<Fr>();
let decider_circuit =
DeciderEthCircuit::<G1, GVar, G2, GVar2, KZG<Bn254>, Pedersen<G2>>::from_nova::<FC>(
nova.clone(),
)
.unwrap();
let start = Instant::now();
let (g16_pk, g16_vk) =
Groth16::<Bn254>::circuit_specific_setup(decider_circuit.clone(), &mut rng).unwrap();
println!(
"generated G16 (Decider circuit) params: {:?}",
start.elapsed()
let f_circuit = FC::new(()).unwrap();
let prep_param = PreprocessorParam::<G1, G2, FC, KZG<'static, Bn254>, Pedersen<G2>>::new(
poseidon_config,
f_circuit.clone(),
); );
(fs_prover_params, kzg_vk, g16_pk, g16_vk)
let nova_params = NOVA::preprocess(&mut rng, &prep_param).unwrap();
let nova = NOVA::init(
nova_params.clone(),
f_circuit.clone(),
vec![Fr::zero(); f_circuit.state_len()].clone(),
)
.unwrap();
let decider_params =
DECIDER::preprocess(&mut rng, &nova_params.clone(), nova.clone()).unwrap();
(nova_params, decider_params)
} }
/// This function allows to define which FCircuit to use for the test, and how many prove_step /// This function allows to define which FCircuit to use for the test, and how many prove_step
@ -346,52 +344,31 @@ mod tests {
/// - modifies the z_0 and checks that it does not pass the EVM check /// - modifies the z_0 and checks that it does not pass the EVM check
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
fn nova_cyclefold_solidity_verifier_opt<FC: FCircuit<Fr, Params = ()>>( fn nova_cyclefold_solidity_verifier_opt<FC: FCircuit<Fr, Params = ()>>(
params: (
ProverParams<G1, G2, KZG<'static, Bn254>, Pedersen<G2>>,
KZGVerifierKey<Bn254>,
ProvingKey<Bn254>,
G16VerifierKey<Bn254>,
),
fs_params: (FS_PP<FC>, FS_VP<FC>),
decider_params: (DECIDER_PP<FC>, DECIDER_VP<FC>),
z_0: Vec<Fr>, z_0: Vec<Fr>,
n_steps: usize, n_steps: usize,
) { ) {
let (fs_prover_params, kzg_vk, g16_pk, g16_vk) = params.clone();
pub type NOVA_FCircuit<FC> =
Nova<G1, GVar, G2, GVar2, FC, KZG<'static, Bn254>, Pedersen<G2>>;
pub type DECIDERETH_FCircuit<FC> = DeciderEth<
G1,
GVar,
G2,
GVar2,
FC,
KZG<'static, Bn254>,
Pedersen<G2>,
Groth16<Bn254>,
NOVA_FCircuit<FC>,
>;
let (decider_pp, decider_vp) = decider_params;
let f_circuit = FC::new(()).unwrap(); let f_circuit = FC::new(()).unwrap();
let nova_cyclefold_vk = NovaCycleFoldVerifierKey::from((
(g16_vk.clone(), kzg_vk.clone()),
f_circuit.state_len(),
));
let nova_cyclefold_vk =
NovaCycleFoldVerifierKey::from((decider_vp.clone(), f_circuit.state_len()));
let mut rng = rand::rngs::OsRng; let mut rng = rand::rngs::OsRng;
let mut nova = NOVA_FCircuit::init(&fs_prover_params, f_circuit, z_0).unwrap();
let mut nova = NOVA::<FC>::init(fs_params, f_circuit, z_0).unwrap();
for _ in 0..n_steps { for _ in 0..n_steps {
nova.prove_step(&mut rng, vec![]).unwrap(); nova.prove_step(&mut rng, vec![]).unwrap();
} }
let start = Instant::now(); let start = Instant::now();
let proof =
DECIDERETH_FCircuit::prove(rng, (g16_pk, fs_prover_params.cs_pp.clone()), nova.clone())
.unwrap();
let proof = DECIDER::<FC>::prove(rng, decider_pp, nova.clone()).unwrap();
println!("generated Decider proof: {:?}", start.elapsed()); println!("generated Decider proof: {:?}", start.elapsed());
let verified = DECIDERETH_FCircuit::<FC>::verify(
(g16_vk, kzg_vk),
let verified = DECIDER::<FC>::verify(
decider_vp,
nova.i, nova.i,
nova.z_0.clone(), nova.z_0.clone(),
nova.z_i.clone(), nova.z_i.clone(),
@ -448,12 +425,22 @@ mod tests {
#[test] #[test]
fn nova_cyclefold_solidity_verifier() { fn nova_cyclefold_solidity_verifier() {
let params = init_params::<CubicFCircuit<Fr>>();
let (nova_params, decider_params) = init_params::<CubicFCircuit<Fr>>();
let z_0 = vec![Fr::from(3_u32)]; let z_0 = vec![Fr::from(3_u32)];
nova_cyclefold_solidity_verifier_opt::<CubicFCircuit<Fr>>(params.clone(), z_0.clone(), 2);
nova_cyclefold_solidity_verifier_opt::<CubicFCircuit<Fr>>(params.clone(), z_0.clone(), 3);
nova_cyclefold_solidity_verifier_opt::<CubicFCircuit<Fr>>(
nova_params.clone(),
decider_params.clone(),
z_0.clone(),
2,
);
nova_cyclefold_solidity_verifier_opt::<CubicFCircuit<Fr>>(
nova_params,
decider_params,
z_0,
3,
);
let params = init_params::<MultiInputsFCircuit<Fr>>();
let (nova_params, decider_params) = init_params::<MultiInputsFCircuit<Fr>>();
let z_0 = vec![ let z_0 = vec![
Fr::from(1_u32), Fr::from(1_u32),
Fr::from(1_u32), Fr::from(1_u32),
@ -462,12 +449,14 @@ mod tests {
Fr::from(1_u32), Fr::from(1_u32),
]; ];
nova_cyclefold_solidity_verifier_opt::<MultiInputsFCircuit<Fr>>( nova_cyclefold_solidity_verifier_opt::<MultiInputsFCircuit<Fr>>(
params.clone(),
nova_params.clone(),
decider_params.clone(),
z_0.clone(), z_0.clone(),
2, 2,
); );
nova_cyclefold_solidity_verifier_opt::<MultiInputsFCircuit<Fr>>( nova_cyclefold_solidity_verifier_opt::<MultiInputsFCircuit<Fr>>(
params.clone(),
nova_params,
decider_params,
z_0.clone(), z_0.clone(),
3, 3,
); );

+ 16
- 15
solidity-verifiers/templates/nova_cyclefold_decider.askama.sol

@ -78,10 +78,11 @@ contract NovaDecider is Groth16Verifier, KZG10Verifier {
// from gamma_abc_len, we subtract 1. // from gamma_abc_len, we subtract 1.
uint256[{{ public_inputs_len - 1 }}] memory public_inputs; uint256[{{ public_inputs_len - 1 }}] memory public_inputs;
public_inputs[0] = i_z0_zi[0];
public_inputs[0] = {{pp_hash}};
public_inputs[1] = i_z0_zi[0];
for (uint i = 0; i < {{ z_len * 2 }}; i++) { for (uint i = 0; i < {{ z_len * 2 }}; i++) {
public_inputs[1 + i] = i_z0_zi[1 + i];
public_inputs[2 + i] = i_z0_zi[1 + i];
} }
{ {
@ -91,9 +92,9 @@ contract NovaDecider is Groth16Verifier, KZG10Verifier {
uint256 x0 = rlc(U_i_x_u_i_cmW[0], U_i_u_u_i_u_r[2], u_i_x_cmT[0]); uint256 x0 = rlc(U_i_x_u_i_cmW[0], U_i_u_u_i_u_r[2], u_i_x_cmT[0]);
uint256 x1 = rlc(U_i_x_u_i_cmW[1], U_i_u_u_i_u_r[2], u_i_x_cmT[1]); uint256 x1 = rlc(U_i_x_u_i_cmW[1], U_i_u_u_i_u_r[2], u_i_x_cmT[1]);
public_inputs[{{ z_len * 2 + 1 }}] = u;
public_inputs[{{ z_len * 2 + 2 }}] = x0;
public_inputs[{{ z_len * 2 + 3 }}] = x1;
public_inputs[{{ z_len * 2 + 2 }}] = u;
public_inputs[{{ z_len * 2 + 3 }}] = x0;
public_inputs[{{ z_len * 2 + 4 }}] = x1;
} }
{ {
@ -106,8 +107,8 @@ contract NovaDecider is Groth16Verifier, KZG10Verifier {
uint256[{{num_limbs}}] memory cmE_y_limbs = LimbsDecomposition.decompose(cmE[1]); uint256[{{num_limbs}}] memory cmE_y_limbs = LimbsDecomposition.decompose(cmE[1]);
for (uint8 k = 0; k < {{num_limbs}}; k++) { for (uint8 k = 0; k < {{num_limbs}}; k++) {
public_inputs[{{ z_len * 2 + 4 }} + k] = cmE_x_limbs[k];
public_inputs[{{ z_len * 2 + 4 + num_limbs }} + k] = cmE_y_limbs[k];
public_inputs[{{ z_len * 2 + 5 }} + k] = cmE_x_limbs[k];
public_inputs[{{ z_len * 2 + 5 + num_limbs }} + k] = cmE_y_limbs[k];
} }
} }
@ -124,8 +125,8 @@ contract NovaDecider is Groth16Verifier, KZG10Verifier {
uint256[{{num_limbs}}] memory cmW_y_limbs = LimbsDecomposition.decompose(cmW[1]); uint256[{{num_limbs}}] memory cmW_y_limbs = LimbsDecomposition.decompose(cmW[1]);
for (uint8 k = 0; k < {{num_limbs}}; k++) { for (uint8 k = 0; k < {{num_limbs}}; k++) {
public_inputs[{{ z_len * 2 + 4 + num_limbs * 2 }} + k] = cmW_x_limbs[k];
public_inputs[{{ z_len * 2 + 4 + num_limbs * 3 }} + k] = cmW_y_limbs[k];
public_inputs[{{ z_len * 2 + 5 + num_limbs * 2 }} + k] = cmW_x_limbs[k];
public_inputs[{{ z_len * 2 + 5 + num_limbs * 3 }} + k] = cmW_y_limbs[k];
} }
} }
@ -134,10 +135,10 @@ contract NovaDecider is Groth16Verifier, KZG10Verifier {
{ {
// add challenges // add challenges
public_inputs[{{ z_len * 2 + 4 + num_limbs * 4 }}] = challenge_W_challenge_E_kzg_evals[0];
public_inputs[{{ z_len * 2 + 4 + num_limbs * 4 + 1 }}] = challenge_W_challenge_E_kzg_evals[1];
public_inputs[{{ z_len * 2 + 4 + num_limbs * 4 + 2 }}] = challenge_W_challenge_E_kzg_evals[2];
public_inputs[{{ z_len * 2 + 4 + num_limbs * 4 + 3 }}] = challenge_W_challenge_E_kzg_evals[3];
public_inputs[{{ z_len * 2 + 5 + num_limbs * 4 }}] = challenge_W_challenge_E_kzg_evals[0];
public_inputs[{{ z_len * 2 + 5 + num_limbs * 4 + 1 }}] = challenge_W_challenge_E_kzg_evals[1];
public_inputs[{{ z_len * 2 + 5 + num_limbs * 4 + 2 }}] = challenge_W_challenge_E_kzg_evals[2];
public_inputs[{{ z_len * 2 + 5 + num_limbs * 4 + 3 }}] = challenge_W_challenge_E_kzg_evals[3];
uint256[{{num_limbs}}] memory cmT_x_limbs; uint256[{{num_limbs}}] memory cmT_x_limbs;
uint256[{{num_limbs}}] memory cmT_y_limbs; uint256[{{num_limbs}}] memory cmT_y_limbs;
@ -146,8 +147,8 @@ contract NovaDecider is Groth16Verifier, KZG10Verifier {
cmT_y_limbs = LimbsDecomposition.decompose(u_i_x_cmT[3]); cmT_y_limbs = LimbsDecomposition.decompose(u_i_x_cmT[3]);
for (uint8 k = 0; k < {{num_limbs}}; k++) { for (uint8 k = 0; k < {{num_limbs}}; k++) {
public_inputs[{{ z_len * 2 + 4 + num_limbs * 4 }} + 4 + k] = cmT_x_limbs[k];
public_inputs[{{ z_len * 2 + 4 + num_limbs * 5}} + 4 + k] = cmT_y_limbs[k];
public_inputs[{{ z_len * 2 + 5 + num_limbs * 4 }} + 4 + k] = cmT_x_limbs[k];
public_inputs[{{ z_len * 2 + 5 + num_limbs * 5}} + 4 + k] = cmT_y_limbs[k];
} }
// last element of the groth16 proof's public inputs is `r` // last element of the groth16 proof's public inputs is `r`

Loading…
Cancel
Save