From c17fcf56c61777226ac5e54564d5a38b2ebe4e10 Mon Sep 17 00:00:00 2001 From: arnaucube Date: Fri, 5 Jul 2024 11:47:18 +0200 Subject: [PATCH] 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 --- examples/circom_full_flow.rs | 6 +- examples/external_inputs.rs | 7 +- examples/full_flow.rs | 6 +- examples/multi_inputs.rs | 7 +- examples/sha256.rs | 7 +- folding-schemes/Cargo.toml | 1 + .../src/{ccs/mod.rs => arith/ccs.rs} | 21 +- folding-schemes/src/arith/mod.rs | 15 ++ folding-schemes/src/{ccs => arith}/r1cs.rs | 39 ++-- folding-schemes/src/commitment/mod.rs | 3 +- .../src/folding/circuits/cyclefold.rs | 32 ++- folding-schemes/src/folding/hypernova/cccs.rs | 4 +- .../src/folding/hypernova/circuits.rs | 103 +++++++-- .../src/folding/hypernova/lcccs.rs | 10 +- folding-schemes/src/folding/hypernova/mod.rs | 133 +++++++---- .../src/folding/hypernova/nimfs.rs | 7 +- .../src/folding/hypernova/utils.rs | 7 +- folding-schemes/src/folding/nova/circuits.rs | 46 +++- .../src/folding/nova/decider_eth.rs | 56 ++--- .../src/folding/nova/decider_eth_circuit.rs | 76 +++--- folding-schemes/src/folding/nova/mod.rs | 85 +++++-- folding-schemes/src/folding/nova/nifs.rs | 6 +- folding-schemes/src/folding/nova/serialize.rs | 46 ++-- folding-schemes/src/folding/nova/traits.rs | 2 +- .../src/folding/protogalaxy/folding.rs | 4 +- folding-schemes/src/lib.rs | 4 +- folding-schemes/src/utils/mle.rs | 2 +- folding-schemes/src/utils/mod.rs | 68 ++++++ solidity-verifiers/src/verifiers/g16.rs | 12 +- solidity-verifiers/src/verifiers/kzg.rs | 6 +- solidity-verifiers/src/verifiers/mod.rs | 4 +- .../src/verifiers/nova_cyclefold.rs | 217 +++++++++--------- .../nova_cyclefold_decider.askama.sol | 31 +-- 33 files changed, 666 insertions(+), 407 deletions(-) rename folding-schemes/src/{ccs/mod.rs => arith/ccs.rs} (86%) create mode 100644 folding-schemes/src/arith/mod.rs rename folding-schemes/src/{ccs => arith}/r1cs.rs (94%) diff --git a/examples/circom_full_flow.rs b/examples/circom_full_flow.rs index 65d38e8..69d902f 100644 --- a/examples/circom_full_flow.rs +++ b/examples/circom_full_flow.rs @@ -82,13 +82,13 @@ fn main() { // prepare the Nova prover & verifier params 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 - 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 - 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 for (i, external_inputs_at_step) in external_inputs.iter().enumerate() { diff --git a/examples/external_inputs.rs b/examples/external_inputs.rs index 5e7b57e..726e0e9 100644 --- a/examples/external_inputs.rs +++ b/examples/external_inputs.rs @@ -187,10 +187,11 @@ fn main() { println!("Prepare Nova's ProverParams & VerifierParams"); 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"); - 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 for (i, external_inputs_at_step) in external_inputs.iter().enumerate() { @@ -210,7 +211,7 @@ fn main() { println!("Run the Nova's IVC verifier"); N::verify( - nova_vp, + nova_params.1, initial_state.clone(), folding_scheme.state(), // latest state Fr::from(num_steps as u32), diff --git a/examples/full_flow.rs b/examples/full_flow.rs index e592d3d..2304b59 100644 --- a/examples/full_flow.rs +++ b/examples/full_flow.rs @@ -99,13 +99,13 @@ fn main() { // prepare the Nova prover & verifier params 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 - 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 - 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 for i in 0..n_steps { diff --git a/examples/multi_inputs.rs b/examples/multi_inputs.rs index 5856459..a78b98b 100644 --- a/examples/multi_inputs.rs +++ b/examples/multi_inputs.rs @@ -141,10 +141,11 @@ fn main() { println!("Prepare Nova ProverParams & VerifierParams"); 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"); - 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 for i in 0..num_steps { @@ -157,7 +158,7 @@ fn main() { println!("Run the Nova's IVC verifier"); N::verify( - nova_vp, + nova_params.1, initial_state.clone(), folding_scheme.state(), // latest state Fr::from(num_steps as u32), diff --git a/examples/sha256.rs b/examples/sha256.rs index 03c3c67..045967b 100644 --- a/examples/sha256.rs +++ b/examples/sha256.rs @@ -126,10 +126,11 @@ fn main() { println!("Prepare Nova ProverParams & VerifierParams"); 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"); - 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 for i in 0..num_steps { let start = Instant::now(); @@ -141,7 +142,7 @@ fn main() { println!("Run the Nova's IVC verifier"); N::verify( - nova_vp, + nova_params.1, initial_state, folding_scheme.state(), // latest state Fr::from(num_steps as u32), diff --git a/folding-schemes/Cargo.toml b/folding-schemes/Cargo.toml index c173f8a..a309293 100644 --- a/folding-schemes/Cargo.toml +++ b/folding-schemes/Cargo.toml @@ -23,6 +23,7 @@ num-integer = "0.1" color-eyre = "=0.6.2" ark-bn254 = {version="0.4.0"} ark-groth16 = { version = "^0.4.0" } +sha3 = "0.10" # tmp imports for espresso's sumcheck espresso_subroutines = {git="https://github.com/EspressoSystems/hyperplonk", package="subroutines"} diff --git a/folding-schemes/src/ccs/mod.rs b/folding-schemes/src/arith/ccs.rs similarity index 86% rename from folding-schemes/src/ccs/mod.rs rename to folding-schemes/src/arith/ccs.rs index 4637ad9..2c55fed 100644 --- a/folding-schemes/src/ccs/mod.rs +++ b/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::Error; -pub mod r1cs; -use r1cs::R1CS; +use super::{r1cs::R1CS, Arith}; /// CCS represents the Customizable Constraint Systems structure defined in /// the [CCS paper](https://eprint.iacr.org/2023/552) @@ -36,9 +35,9 @@ pub struct CCS { pub c: Vec, } -impl CCS { +impl Arith for CCS { /// 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]; for i in 0..self.q { @@ -67,6 +66,18 @@ impl CCS { Ok(()) } + + fn params_to_bytes(&self) -> Vec { + [ + 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 CCS { @@ -102,7 +113,7 @@ impl CCS { #[cfg(test)] pub mod tests { 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_pallas::Fr; diff --git a/folding-schemes/src/arith/mod.rs b/folding-schemes/src/arith/mod.rs new file mode 100644 index 0000000..f53b565 --- /dev/null +++ b/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 { + /// 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; +} diff --git a/folding-schemes/src/ccs/r1cs.rs b/folding-schemes/src/arith/r1cs.rs similarity index 94% rename from folding-schemes/src/ccs/r1cs.rs rename to folding-schemes/src/arith/r1cs.rs index 6af5fd3..d66754f 100644 --- a/folding-schemes/src/ccs/r1cs.rs +++ b/folding-schemes/src/arith/r1cs.rs @@ -1,10 +1,11 @@ use ark_ff::PrimeField; use ark_relations::r1cs::ConstraintSystem; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use ark_std::rand::Rng; +use super::Arith; use crate::utils::vec::{hadamard, mat_vec_mul, vec_add, vec_scalar_mul, SparseMatrix}; use crate::Error; -use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; #[derive(Debug, Clone, Eq, PartialEq, CanonicalSerialize, CanonicalDeserialize)] pub struct R1CS { @@ -14,6 +15,29 @@ pub struct R1CS { pub C: SparseMatrix, } +impl Arith for R1CS { + /// 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 { + [ + self.l.to_le_bytes(), + self.A.n_rows.to_le_bytes(), + self.A.n_cols.to_le_bytes(), + ] + .concat() + } +} + impl R1CS { pub fn rand(rng: &mut R, n_rows: usize, n_cols: usize) -> Self { Self { @@ -29,19 +53,6 @@ impl R1CS { (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 /// [Nova](https://eprint.iacr.org/2021/370.pdf) section 4.1. pub fn relax(self) -> RelaxedR1CS { diff --git a/folding-schemes/src/commitment/mod.rs b/folding-schemes/src/commitment/mod.rs index 9fa9a13..1c64457 100644 --- a/folding-schemes/src/commitment/mod.rs +++ b/folding-schemes/src/commitment/mod.rs @@ -1,4 +1,5 @@ use ark_ec::CurveGroup; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use ark_std::fmt::Debug; use ark_std::rand::RngCore; @@ -13,7 +14,7 @@ pub mod pedersen; /// commitment in hiding mode or not. pub trait CommitmentScheme: Clone + Debug { type ProverParams: Clone + Debug; - type VerifierParams: Clone + Debug; + type VerifierParams: Clone + Debug + CanonicalSerialize + CanonicalDeserialize; type Proof: Clone + Debug; type ProverChallenge: Clone + Debug; type Challenge: Clone + Debug; diff --git a/folding-schemes/src/folding/circuits/cyclefold.rs b/folding-schemes/src/folding/circuits/cyclefold.rs index 920e24d..3d0e5f8 100644 --- a/folding-schemes/src/folding/circuits/cyclefold.rs +++ b/folding-schemes/src/folding/circuits/cyclefold.rs @@ -30,7 +30,7 @@ use ark_std::{One, Zero}; use core::{borrow::Borrow, marker::PhantomData}; 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::constants::N_BITS_RO; use crate::folding::nova::{nifs::NIFS, CommittedInstance, Witness}; @@ -127,9 +127,13 @@ where pub fn hash( self, crh_params: &CRHParametersVar>, + pp_hash: FpVar>, // public params hash ) -> Result<(FpVar>, Vec>>), SynthesisError> { 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( poseidon_config: &PoseidonConfig, + pp_hash: C::BaseField, // public params hash U_i: CommittedInstance, u_i: CommittedInstance, cmT: C, @@ -276,7 +281,7 @@ where // to save constraints for sponge.squeeze_bits in the corresponding circuit let is_inf = U_cm_is_inf * CF2::::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); let bits = sponge.squeeze_bits(N_BITS_RO); Ok(bits) @@ -286,6 +291,7 @@ where pub fn get_challenge_gadget( cs: ConstraintSystemRef, poseidon_config: &PoseidonConfig, + pp_hash: FpVar, // public params hash mut U_i_vec: Vec>, u_i: CycleFoldCommittedInstanceVar, cmT: GC, @@ -303,7 +309,7 @@ where // to save constraints for sponge.squeeze_bits let is_inf = U_cm_is_inf * CF2::::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)?; let bits = sponge.squeeze_bits(N_BITS_RO)?; 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. #[allow(clippy::type_complexity)] +#[allow(clippy::too_many_arguments)] pub fn fold_cyclefold_circuit( poseidon_config: &PoseidonConfig, cf_r1cs: R1CS, cf_cs_params: CS2::ProverParams, + pp_hash: C1::ScalarField, // public params hash cf_W_i: Witness, // witness of the running instance cf_U_i: CommittedInstance, // running instance cf_u_i_x: Vec, @@ -438,6 +446,7 @@ where let cf_r_bits = CycleFoldChallengeGadget::::get_challenge_native( poseidon_config, + pp_hash, cf_U_i.clone(), cf_u_i.clone(), cf_cmT, @@ -594,8 +603,10 @@ pub mod tests { let cmT = Projective::rand(&mut rng); // compute the challenge natively + let pp_hash = Fq::from(42u32); // only for test let r_bits = CycleFoldChallengeGadget::::get_challenge_native( &poseidon_config, + pp_hash, U_i.clone(), u_i.clone(), cmT, @@ -615,9 +626,11 @@ pub mod tests { .unwrap(); let cmTVar = GVar::new_witness(cs.clone(), || Ok(cmT)).unwrap(); + let pp_hashVar = FpVar::::new_witness(cs.clone(), || Ok(pp_hash)).unwrap(); let r_bitsVar = CycleFoldChallengeGadget::::get_challenge_gadget( cs.clone(), &poseidon_config, + pp_hashVar, U_iVar.to_constraint_field().unwrap(), u_iVar, cmTVar, @@ -645,7 +658,8 @@ pub mod tests { .take(CF_IO_LEN) .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::::new_ref(); let U_iVar = @@ -653,8 +667,12 @@ pub mod tests { Ok(U_i.clone()) }) .unwrap(); + let pp_hashVar = FpVar::::new_witness(cs.clone(), || Ok(pp_hash)).unwrap(); 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(); hVar.enforce_equal(&FpVar::new_witness(cs.clone(), || Ok(h)).unwrap()) .unwrap(); diff --git a/folding-schemes/src/folding/hypernova/cccs.rs b/folding-schemes/src/folding/hypernova/cccs.rs index 0f4c11c..c81a189 100644 --- a/folding-schemes/src/folding/hypernova/cccs.rs +++ b/folding-schemes/src/folding/hypernova/cccs.rs @@ -7,7 +7,7 @@ use std::sync::Arc; use ark_std::rand::Rng; use super::Witness; -use crate::ccs::CCS; +use crate::arith::{ccs::CCS, Arith}; use crate::commitment::CommitmentScheme; use crate::utils::mle::dense_vec_to_dense_mle; use crate::utils::vec::mat_vec_mul; @@ -125,7 +125,7 @@ pub mod tests { use ark_std::UniformRand; 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; /// Do some sanity checks on q(x). It's a multivariable polynomial and it should evaluate to zero inside the diff --git a/folding-schemes/src/folding/hypernova/circuits.rs b/folding-schemes/src/folding/hypernova/circuits.rs index 36116f8..bdc9bd3 100644 --- a/folding-schemes/src/folding/hypernova/circuits.rs +++ b/folding-schemes/src/folding/hypernova/circuits.rs @@ -44,7 +44,7 @@ use crate::frontend::FCircuit; use crate::utils::virtual_polynomial::VPAuxInfo; use crate::Error; use crate::{ - ccs::{r1cs::extract_r1cs, CCS}, + arith::{ccs::CCS, r1cs::extract_r1cs}, transcript::{ poseidon::{PoseidonTranscript, PoseidonTranscriptVar}, Transcript, TranscriptVar, @@ -143,6 +143,7 @@ where pub fn hash( self, crh_params: &CRHParametersVar>, + pp_hash: FpVar>, i: FpVar>, z_0: Vec>>, z_i: Vec>>, @@ -155,7 +156,7 @@ where self.v, ] .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(( CRHGadget::::evaluate(crh_params, &input)?, U_vec, @@ -455,6 +456,7 @@ pub struct AugmentedFCircuit< pub _gc2: PhantomData, pub poseidon_config: PoseidonConfig>, pub ccs: CCS, // CCS of the AugmentedFCircuit + pub pp_hash: Option>, pub i: Option>, pub i_usize: Option, pub z_0: Option>, @@ -497,6 +499,7 @@ where _gc2: PhantomData, poseidon_config: poseidon_config.clone(), ccs, + pp_hash: None, i: None, i_usize: None, z_0: None, @@ -559,6 +562,7 @@ where let mut transcript_p: PoseidonTranscript = PoseidonTranscript::::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::>::prove( &mut transcript_p, &ccs, @@ -573,6 +577,7 @@ where _gc2: PhantomData, poseidon_config: self.poseidon_config.clone(), ccs: ccs.clone(), + pp_hash: Some(C1::ScalarField::zero()), i: Some(C1::ScalarField::zero()), i_usize: Some(0), z_0: Some(z_0.clone()), @@ -624,6 +629,9 @@ where for<'a> &'a GC2: GroupOpsBounds<'a, C2, GC2>, { fn generate_constraints(self, cs: ConstraintSystemRef>) -> Result<(), SynthesisError> { + let pp_hash = FpVar::>::new_witness(cs.clone(), || { + Ok(self.pp_hash.unwrap_or_else(CF1::::zero)) + })?; let i = FpVar::>::new_witness(cs.clone(), || { Ok(self.i.unwrap_or_else(CF1::::zero)) })?; @@ -680,11 +688,15 @@ where // Primary Part // P.1. Compute u_i.x // 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) - 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 let u_i = CCCSVar:: { @@ -700,8 +712,9 @@ where // 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 // other curve. - let transcript = + let mut transcript = PoseidonTranscriptVar::::new(cs.clone(), &self.poseidon_config); + transcript.absorb(pp_hash.clone())?; let (mut U_i1, rho_bits) = NIMFSGadget::::verify( cs.clone(), &self.ccs.clone(), @@ -716,12 +729,14 @@ where // P.4.a compute and check the first output of F' let (u_i1_x, _) = U_i1.clone().hash( &crh_params, + pp_hash.clone(), i + FpVar::>::one(), z_0.clone(), z_i1.clone(), )?; let (u_i1_x_base, _) = LCCCSVar::new_constant(cs.clone(), U_dummy)?.hash( &crh_params, + pp_hash.clone(), FpVar::>::one(), z_0.clone(), z_i1.clone(), @@ -763,6 +778,7 @@ where let cf_r_bits = CycleFoldChallengeGadget::::get_challenge_gadget( cs.clone(), &self.poseidon_config, + pp_hash.clone(), cf_U_i_vec, cf_u_i.clone(), cf_cmT.clone(), @@ -786,10 +802,10 @@ where // P.4.b compute and check the second output of F' // Base case: u_{i+1}.x[1] == H(cf_U_{\bot}) // 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, _) = CycleFoldCommittedInstanceVar::new_constant(cs.clone(), cf_u_dummy)? - .hash(&crh_params)?; + .hash(&crh_params, pp_hash)?; let cf_x = FpVar::new_input(cs.clone(), || { Ok(self.cf_x.unwrap_or(cf_u_i1_x_base.value()?)) })?; @@ -810,10 +826,12 @@ mod tests { use super::*; use crate::{ - ccs::{ + arith::{ + ccs::{ + tests::{get_test_ccs, get_test_z}, + CCS, + }, r1cs::extract_w_x, - tests::{get_test_ccs, get_test_z}, - CCS, }, commitment::{pedersen::Pedersen, CommitmentScheme}, folding::{ @@ -1049,6 +1067,7 @@ mod tests { let (pedersen_params, _) = Pedersen::::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 z_0 = vec![Fr::from(3_u32)]; @@ -1058,19 +1077,26 @@ mod tests { .unwrap(); let h = lcccs .clone() - .hash(&poseidon_config, i, z_0.clone(), z_i.clone()) + .hash(&poseidon_config, pp_hash, i, z_0.clone(), z_i.clone()) .unwrap(); let cs = ConstraintSystem::::new_ref(); let crh_params = CRHParametersVar::::new_constant(cs.clone(), poseidon_config).unwrap(); + let pp_hashVar = FpVar::::new_witness(cs.clone(), || Ok(pp_hash)).unwrap(); let iVar = FpVar::::new_witness(cs.clone(), || Ok(i)).unwrap(); let z_0Var = Vec::>::new_witness(cs.clone(), || Ok(z_0.clone())).unwrap(); let z_iVar = Vec::>::new_witness(cs.clone(), || Ok(z_i.clone())).unwrap(); let lcccsVar = LCCCSVar::::new_witness(cs.clone(), || Ok(lcccs)).unwrap(); let (hVar, _) = lcccsVar .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(); assert!(cs.is_satisfied().unwrap()); @@ -1112,6 +1138,9 @@ mod tests { let (cf_pedersen_params, _) = Pedersen::::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 let z_0 = vec![Fr::from(3_u32)]; 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_U_i = cf_U_dummy.clone(); 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; @@ -1151,12 +1186,18 @@ mod tests { U_i1 = LCCCS::dummy(ccs.l, ccs.t, ccs.s); 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(); // hash the initial (dummy) CycleFold instance, which is used as the 2nd public // 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 = AugmentedFCircuit::> { @@ -1164,6 +1205,7 @@ mod tests { _gc2: PhantomData, poseidon_config: poseidon_config.clone(), ccs: ccs.clone(), + pp_hash: Some(pp_hash), i: Some(Fr::zero()), i_usize: Some(0), z_0: Some(z_0.clone()), @@ -1185,6 +1227,7 @@ mod tests { } else { let mut transcript_p: PoseidonTranscript = PoseidonTranscript::::new(&poseidon_config.clone()); + transcript_p.absorb(&pp_hash); let (rho_bits, nimfs_proof); (nimfs_proof, U_i1, W_i1, rho_bits) = NIMFS::>::prove( @@ -1201,7 +1244,13 @@ mod tests { U_i1.check_relation(&ccs, &W_i1).unwrap(); 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(); let rho_Fq = Fq::from_bigint(BigInteger::from_bits_le(&rho_bits)).unwrap(); @@ -1236,6 +1285,7 @@ mod tests { &poseidon_config, cf_r1cs.clone(), cf_pedersen_params.clone(), + pp_hash, cf_W_i.clone(), // CycleFold running instance witness cf_U_i.clone(), // CycleFold running 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 // 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 = AugmentedFCircuit::> { @@ -1253,6 +1303,7 @@ mod tests { _gc2: PhantomData, poseidon_config: poseidon_config.clone(), ccs: ccs.clone(), + pp_hash: Some(pp_hash), i: Some(iFr), i_usize: Some(i), 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[1], augmented_f_circuit.cf_x.unwrap()); 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(); - 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] assert_eq!(u_i.x[0], expected_u_i1_x); assert_eq!(u_i.x[1], expected_cf_U_i1_x); diff --git a/folding-schemes/src/folding/hypernova/lcccs.rs b/folding-schemes/src/folding/hypernova/lcccs.rs index 76b169a..95fd937 100644 --- a/folding-schemes/src/folding/hypernova/lcccs.rs +++ b/folding-schemes/src/folding/hypernova/lcccs.rs @@ -10,7 +10,7 @@ use ark_std::rand::Rng; use ark_std::Zero; use super::Witness; -use crate::ccs::CCS; +use crate::arith::ccs::CCS; use crate::commitment::CommitmentScheme; use crate::folding::circuits::nonnative::affine::nonnative_affine_to_field_elements; use crate::utils::mle::dense_vec_to_dense_mle; @@ -129,6 +129,7 @@ where pub fn hash( &self, poseidon_config: &PoseidonConfig, + pp_hash: C::ScalarField, i: C::ScalarField, z_0: Vec, z_i: Vec, @@ -138,7 +139,7 @@ where CRH::::evaluate( poseidon_config, vec![ - vec![i], + vec![pp_hash, i], z_0, z_i, C_x, @@ -164,9 +165,10 @@ pub mod tests { use std::sync::Arc; use super::*; - use crate::ccs::{ + use crate::arith::{ + ccs::tests::{get_test_ccs, get_test_z}, r1cs::R1CS, - tests::{get_test_ccs, get_test_z}, + Arith, }; use crate::commitment::pedersen::Pedersen; use crate::utils::hypercube::BooleanHypercube; diff --git a/folding-schemes/src/folding/hypernova/mod.rs b/folding-schemes/src/folding/hypernova/mod.rs index 56aae65..c1b5939 100644 --- a/folding-schemes/src/folding/hypernova/mod.rs +++ b/folding-schemes/src/folding/hypernova/mod.rs @@ -24,16 +24,17 @@ use crate::folding::circuits::{ CF2, }; 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::utils::get_cm_coordinates; +use crate::utils::{get_cm_coordinates, pp_hash}; use crate::Error; use crate::FoldingScheme; use crate::{ - ccs::{ + arith::{ + ccs::CCS, r1cs::{extract_w_x, R1CS}, - CCS, }, transcript::{poseidon::PoseidonTranscript, Transcript}, }; @@ -56,22 +57,6 @@ impl Witness { } } -#[derive(Debug, Clone)] -pub struct PreprocessorParam -where - C1: CurveGroup, - C2: CurveGroup, - FC: FCircuit, - CS1: CommitmentScheme, - CS2: CommitmentScheme, -{ - pub poseidon_config: PoseidonConfig, - pub F: FC, - // cs_params & cf_cs_params: if not provided, will be generated at the preprocess method - pub cs_params: Option, - pub cf_cs_params: Option, -} - #[derive(Debug, Clone)] pub struct ProverParams where @@ -97,8 +82,27 @@ pub struct VerifierParams< pub poseidon_config: PoseidonConfig, pub ccs: CCS, pub cf_r1cs: R1CS, - pub cs_params: CS1::ProverParams, - pub cf_cs_params: CS2::ProverParams, + pub cs_vp: CS1::VerifierParams, + pub cf_cs_vp: CS2::VerifierParams, +} + +impl VerifierParams +where + C1: CurveGroup, + C2: CurveGroup, + CS1: CommitmentScheme, + CS2: CommitmentScheme, +{ + /// returns the hash of the public parameters of HyperNova + pub fn pp_hash(&self) -> Result { + pp_hash::( + &self.ccs, + &self.cf_r1cs, + &self.cs_vp, + &self.cf_cs_vp, + &self.poseidon_config, + ) + } } /// Implements HyperNova+CycleFold's IVC, described in @@ -130,6 +134,8 @@ where pub cf_cs_params: CS2::ProverParams, /// F circuit, the circuit that is being folded pub F: FC, + /// public params hash + pub pp_hash: C1::ScalarField, pub i: C1::ScalarField, /// initial state pub z_0: Vec, @@ -185,35 +191,49 @@ where let cf_circuit = CycleFoldCircuit::::empty(); let cf_r1cs = get_r1cs_from_cs::(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 { - (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:: { 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()), }; let vp = VerifierParams:: { poseidon_config: prep_param.poseidon_config.clone(), ccs, 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)) } /// 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) -> Result { + fn init( + params: (Self::ProverParam, Self::VerifierParam), + F: FC, + z_0: Vec, + ) -> Result { + let (pp, vp) = params; + // prepare the HyperNova's AugmentedFCircuit and CycleFold's circuits and obtain its CCS // and R1CS respectively let augmented_f_circuit = AugmentedFCircuit::::empty( @@ -226,6 +246,9 @@ where let cf_circuit = CycleFoldCircuit::::empty(); let cf_r1cs = get_r1cs_from_cs::(cf_circuit)?; + // compute the public params hash + let pp_hash = vp.pp_hash()?; + // setup the dummy instances let W_dummy = Witness::::dummy(&ccs); let U_dummy = LCCCS::::dummy(ccs.l, ccs.t, ccs.s); @@ -236,11 +259,12 @@ where u_dummy.x = vec![ U_dummy.hash( &pp.poseidon_config, + pp_hash, C1::ScalarField::zero(), 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 @@ -255,6 +279,7 @@ where cs_params: pp.cs_params.clone(), cf_cs_params: pp.cf_cs_params.clone(), F, + pp_hash, i: C1::ScalarField::zero(), z_0: z_0.clone(), z_i: z_0, @@ -315,6 +340,7 @@ where let u_i1_x = U_i1.hash( &self.poseidon_config, + self.pp_hash, C1::ScalarField::one(), self.z_0.clone(), z_i1.clone(), @@ -322,13 +348,16 @@ where // hash the initial (dummy) CycleFold instance, which is used as the 2nd public // 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:: { _c2: PhantomData, _gc2: PhantomData, poseidon_config: self.poseidon_config.clone(), ccs: self.ccs.clone(), + pp_hash: Some(self.pp_hash), i: Some(C1::ScalarField::zero()), i_usize: Some(0), z_0: Some(self.z_0.clone()), @@ -350,6 +379,7 @@ where } else { let mut transcript_p: PoseidonTranscript = PoseidonTranscript::::new(&self.poseidon_config); + transcript_p.absorb(&self.pp_hash); let (rho_bits, nimfs_proof); (nimfs_proof, U_i1, W_i1, rho_bits) = NIMFS::>::prove( &mut transcript_p, @@ -366,6 +396,7 @@ where let u_i1_x = U_i1.hash( &self.poseidon_config, + self.pp_hash, self.i + C1::ScalarField::one(), self.z_0.clone(), z_i1.clone(), @@ -397,19 +428,21 @@ where &self.poseidon_config, self.cf_r1cs.clone(), self.cf_cs_params.clone(), + self.pp_hash, self.cf_W_i.clone(), // CycleFold running instance witness self.cf_U_i.clone(), // CycleFold running instance cf_u_i_x, 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:: { _c2: PhantomData, _gc2: PhantomData, poseidon_config: self.poseidon_config.clone(), ccs: self.ccs.clone(), + pp_hash: Some(self.pp_hash), i: Some(self.i), i_usize: Some(i_usize), z_0: Some(self.z_0.clone()), @@ -516,14 +549,16 @@ where return Err(Error::IVCVerificationFail); } + let pp_hash = vp.pp_hash()?; + // check that u_i's output points to the running instance // 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] { return Err(Error::IVCVerificationFail); } // 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] { return Err(Error::IVCVerificationFail); } @@ -578,16 +613,20 @@ mod tests { type HN = HyperNova, CS1, CS2>; - let prep_param = PreprocessorParam::, CS1, CS2> { - poseidon_config, - F: F_circuit, - cs_params: None, - cf_cs_params: None, - }; + let prep_param = + PreprocessorParam::, CS1, CS2>::new( + poseidon_config.clone(), + F_circuit, + ); let (prover_params, verifier_params) = HN::preprocess(&mut rng, &prep_param).unwrap(); 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; for _ in 0..num_steps { diff --git a/folding-schemes/src/folding/hypernova/nimfs.rs b/folding-schemes/src/folding/hypernova/nimfs.rs index f919c5d..5e32d52 100644 --- a/folding-schemes/src/folding/hypernova/nimfs.rs +++ b/folding-schemes/src/folding/hypernova/nimfs.rs @@ -11,7 +11,7 @@ use super::{ utils::{compute_c, compute_g, compute_sigmas_thetas}, Witness, }; -use crate::ccs::CCS; +use crate::arith::ccs::CCS; use crate::constants::N_BITS_RO; use crate::folding::circuits::nonnative::affine::nonnative_affine_to_field_elements; use crate::transcript::Transcript; @@ -408,7 +408,10 @@ where #[cfg(test)] pub mod tests { 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::PoseidonTranscript; use ark_std::test_rng; diff --git a/folding-schemes/src/folding/hypernova/utils.rs b/folding-schemes/src/folding/hypernova/utils.rs index d4cf3a6..8b247c9 100644 --- a/folding-schemes/src/folding/hypernova/utils.rs +++ b/folding-schemes/src/folding/hypernova/utils.rs @@ -7,7 +7,7 @@ use std::sync::Arc; use super::lcccs::LCCCS; 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::vec::mat_vec_mul; 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 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::folding::hypernova::lcccs::tests::compute_Ls; use crate::utils::hypercube::BooleanHypercube; diff --git a/folding-schemes/src/folding/nova/circuits.rs b/folding-schemes/src/folding/nova/circuits.rs index 6d23322..28721c6 100644 --- a/folding-schemes/src/folding/nova/circuits.rs +++ b/folding-schemes/src/folding/nova/circuits.rs @@ -94,6 +94,7 @@ where pub fn hash( self, crh_params: &CRHParametersVar>, + pp_hash: FpVar>, i: FpVar>, z_0: Vec>>, z_i: Vec>>, @@ -105,7 +106,7 @@ where self.cmW.to_constraint_field()?, ] .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(( CRHGadget::::evaluate(crh_params, &input)?, U_vec, @@ -175,6 +176,7 @@ where { pub fn get_challenge_native( poseidon_config: &PoseidonConfig, + pp_hash: C::ScalarField, // public params hash U_i: CommittedInstance, u_i: CommittedInstance, cmT: C, @@ -187,6 +189,7 @@ where let mut sponge = PoseidonSponge::::new(poseidon_config); let input = vec![ + vec![pp_hash], vec![U_i.u], U_i.x.clone(), U_cmE_x, @@ -212,6 +215,7 @@ where pub fn get_challenge_gadget( cs: ConstraintSystemRef, poseidon_config: &PoseidonConfig, + pp_hash: FpVar>, // public params hash U_i_vec: Vec>>, // apready processed input, so we don't have to recompute these values u_i: CommittedInstanceVar, cmT: NonNativeAffineVar, @@ -219,6 +223,7 @@ where let mut sponge = PoseidonSpongeVar::::new(cs, poseidon_config); let input: Vec> = [ + vec![pp_hash], U_i_vec, vec![u_i.u.clone()], u_i.x.clone(), @@ -247,6 +252,7 @@ pub struct AugmentedFCircuit< { pub _gc2: PhantomData, pub poseidon_config: PoseidonConfig>, + pub pp_hash: Option>, pub i: Option>, pub i_usize: Option, pub z_0: Option>, @@ -280,6 +286,7 @@ where Self { _gc2: PhantomData, poseidon_config: poseidon_config.clone(), + pp_hash: None, i: None, i_usize: None, z_0: None, @@ -317,6 +324,9 @@ where for<'a> &'a GC2: GroupOpsBounds<'a, C2, GC2>, { fn generate_constraints(self, cs: ConstraintSystemRef>) -> Result<(), SynthesisError> { + let pp_hash = FpVar::>::new_witness(cs.clone(), || { + Ok(self.pp_hash.unwrap_or_else(CF1::::zero)) + })?; let i = FpVar::>::new_witness(cs.clone(), || { Ok(self.i.unwrap_or_else(CF1::::zero)) })?; @@ -373,11 +383,15 @@ where // Primary Part // P.1. Compute u_i.x // 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) - 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 let u_i = CommittedInstanceVar { @@ -399,6 +413,7 @@ where let r_bits = ChallengeGadget::::get_challenge_gadget( cs.clone(), &self.poseidon_config, + pp_hash.clone(), U_i_vec, u_i.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}) let (u_i1_x, _) = U_i1.clone().hash( &crh_params, + pp_hash.clone(), i + FpVar::>::one(), z_0.clone(), z_i1.clone(), )?; let (u_i1_x_base, _) = CommittedInstanceVar::new_constant(cs.clone(), u_dummy)?.hash( &crh_params, + pp_hash.clone(), FpVar::>::one(), z_0.clone(), z_i1.clone(), @@ -484,6 +501,7 @@ where let cf1_r_bits = CycleFoldChallengeGadget::::get_challenge_gadget( cs.clone(), &self.poseidon_config, + pp_hash.clone(), cf_U_i_vec, cf1_u_i.clone(), cf1_cmT.clone(), @@ -507,6 +525,7 @@ where let cf2_r_bits = CycleFoldChallengeGadget::::get_challenge_gadget( cs.clone(), &self.poseidon_config, + pp_hash.clone(), cf1_U_i1.to_constraint_field()?, cf2_u_i.clone(), cf2_cmT.clone(), @@ -528,10 +547,10 @@ where // P.4.b compute and check the second output of F' // Base case: u_{i+1}.x[1] == H(cf_U_{\bot}) // 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, _) = CycleFoldCommittedInstanceVar::new_constant(cs.clone(), cf_u_dummy)? - .hash(&crh_params)?; + .hash(&crh_params, pp_hash)?; let cf_x = FpVar::new_input(cs.clone(), || { Ok(self.cf_x.unwrap_or(cf_u_i1_x_base.value()?)) })?; @@ -609,6 +628,7 @@ pub mod tests { fn test_committed_instance_hash() { let mut rng = ark_std::test_rng(); let poseidon_config = poseidon_canonical_config::(); + let pp_hash = Fr::from(42u32); // only for test let i = Fr::from(3_u32); let z_0 = vec![Fr::from(3_u32)]; @@ -622,11 +642,12 @@ pub mod tests { // compute the CommittedInstance hash natively 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(); let cs = ConstraintSystem::::new_ref(); + let pp_hashVar = FpVar::::new_witness(cs.clone(), || Ok(pp_hash)).unwrap(); let iVar = FpVar::::new_witness(cs.clone(), || Ok(i)).unwrap(); let z_0Var = Vec::>::new_witness(cs.clone(), || Ok(z_0.clone())).unwrap(); let z_iVar = Vec::>::new_witness(cs.clone(), || Ok(z_i.clone())).unwrap(); @@ -636,7 +657,9 @@ pub mod tests { let crh_params = CRHParametersVar::::new_constant(cs.clone(), poseidon_config).unwrap(); // 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()); // 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 pp_hash = Fr::from(42u32); // only for testing + // compute the challenge natively let r_bits = ChallengeGadget::::get_challenge_native( &poseidon_config, + pp_hash, U_i.clone(), u_i.clone(), cmT, @@ -674,6 +700,7 @@ pub mod tests { let r = Fr::from_bigint(BigInteger::from_bits_le(&r_bits)).unwrap(); let cs = ConstraintSystem::::new_ref(); + let pp_hashVar = FpVar::::new_witness(cs.clone(), || Ok(pp_hash)).unwrap(); let u_iVar = CommittedInstanceVar::::new_witness(cs.clone(), || Ok(u_i.clone())) .unwrap(); @@ -693,6 +720,7 @@ pub mod tests { let r_bitsVar = ChallengeGadget::::get_challenge_gadget( cs.clone(), &poseidon_config, + pp_hashVar, U_iVar_vec, u_iVar, cmTVar, diff --git a/folding-schemes/src/folding/nova/decider_eth.rs b/folding-schemes/src/folding/nova/decider_eth.rs index 2e669bb..c0727d4 100644 --- a/folding-schemes/src/folding/nova/decider_eth.rs +++ b/folding-schemes/src/folding/nova/decider_eth.rs @@ -90,7 +90,8 @@ where type PreprocessorParam = (FS::ProverParam, FS::VerifierParam); type ProverParam = (S::ProvingKey, CS1::ProverParams); type Proof = Proof; - 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; type CommittedInstance = CommittedInstance; @@ -115,9 +116,10 @@ where let nova_vp: as FoldingScheme>::VerifierParam = prep_param.1.clone().into(); + let pp_hash = nova_vp.pp_hash()?; 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)) } @@ -186,7 +188,8 @@ where 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) let U = NIFS::::verify(proof.r, running_instance, incoming_instance, &proof.cmT); @@ -196,7 +199,7 @@ where let (cmT_x, cmT_y) = NonNativeAffineVar::inputize(proof.cmT)?; let public_input: Vec = vec![ - vec![i], + vec![pp_hash, i], z_0, z_i, vec![U.u], @@ -317,13 +320,12 @@ pub mod tests { use ark_bn254::{constraints::GVar, Bn254, Fr, G1Projective as Projective}; use ark_groth16::Groth16; use ark_grumpkin::{constraints::GVar as GVar2, Projective as Projective2}; - use ark_poly_commit::kzg10::VerifierKey as KZGVerifierKey; use std::time::Instant; use super::*; - use crate::commitment::kzg::{ProverKey as KZGProverKey, KZG}; + use crate::commitment::kzg::KZG; 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::transcript::poseidon::poseidon_canonical_config; @@ -357,59 +359,29 @@ pub mod tests { let F_circuit = CubicFCircuit::::new(()).unwrap(); let z_0 = vec![Fr::from(3_u32)]; - let (cs_len, cf_cs_len) = - get_cs_params_len::>( - &poseidon_config, - F_circuit, - ) - .unwrap(); - let start = Instant::now(); - let (kzg_pk, kzg_vk): (KZGProverKey, KZGVerifierKey) = - KZG::::setup(&mut rng, cs_len).unwrap(); - let (cf_pedersen_params, _) = Pedersen::::setup(&mut rng, cf_cs_len).unwrap(); - println!("generated KZG params, {:?}", start.elapsed()); - - let prover_params = - ProverParams::, Pedersen> { - 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 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()); let start = Instant::now(); nova.prove_step(&mut rng, vec![]).unwrap(); println!("prove_step, {:?}", start.elapsed()); nova.prove_step(&mut rng, vec![]).unwrap(); // do a 2nd step - // generate Groth16 setup - let circuit = DeciderEthCircuit::< - Projective, - GVar, - Projective2, - GVar2, - KZG, - Pedersen, - >::from_nova::>(nova.clone()) - .unwrap(); let mut rng = rand::rngs::OsRng; - let start = Instant::now(); - let (g16_pk, g16_vk) = - Groth16::::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 let start = Instant::now(); - let decider_pp = (g16_pk, kzg_pk); let proof = D::prove(rng, decider_pp, nova.clone()).unwrap(); println!("Decider prove, {:?}", start.elapsed()); // decider proof verification let start = Instant::now(); - let decider_vp = (g16_vk, kzg_vk); let verified = D::verify( decider_vp, nova.i, nova.z_0, nova.z_i, &nova.U_i, &nova.u_i, &proof, ) diff --git a/folding-schemes/src/folding/nova/decider_eth_circuit.rs b/folding-schemes/src/folding/nova/decider_eth_circuit.rs index de58e63..8e399c9 100644 --- a/folding-schemes/src/folding/nova/decider_eth_circuit.rs +++ b/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 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::folding::circuits::{ nonnative::{ @@ -223,6 +223,8 @@ where /// CycleFold PedersenParams over C2 pub cf_pedersen_params: PedersenParams, pub poseidon_config: PoseidonConfig>, + /// public params hash + pub pp_hash: Option, pub i: Option>, /// initial state pub z_0: Option>, @@ -273,6 +275,7 @@ where )?; let r_bits = ChallengeGadget::::get_challenge_native( &nova.poseidon_config, + nova.pp_hash, nova.U_i.clone(), nova.u_i.clone(), cmT, @@ -317,6 +320,7 @@ where cf_r1cs: nova.cf_r1cs, cf_pedersen_params: nova.cf_cs_pp, poseidon_config: nova.poseidon_config, + pp_hash: Some(nova.pp_hash), i: Some(nova.i), z_0: Some(nova.z_0), z_i: Some(nova.z_i), @@ -360,6 +364,9 @@ where Ok(self.r1cs.clone()) })?; + let pp_hash = FpVar::>::new_input(cs.clone(), || { + Ok(self.pp_hash.unwrap_or_else(CF1::::zero)) + })?; let i = FpVar::>::new_input(cs.clone(), || Ok(self.i.unwrap_or_else(CF1::::zero)))?; let z_0 = Vec::>>::new_input(cs.clone(), || { @@ -421,9 +428,13 @@ where (u_i.u.is_one()?).enforce_equal(&Boolean::TRUE)?; // 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)?; #[cfg(feature = "light-test")] @@ -454,7 +465,7 @@ where })?; // 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)?; // 4. check Pedersen commitments of cf_U_i.{cmE, cmW} @@ -512,6 +523,7 @@ where let r_bits = ChallengeGadget::::get_challenge_gadget( cs.clone(), &self.poseidon_config, + pp_hash, U_i_vec, u_i.clone(), cmT.clone(), @@ -611,10 +623,15 @@ pub mod tests { use ark_vesta::{constraints::GVar as GVar2, Projective as Projective2}; 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::folding::nova::{get_cs_params_len, ProverParams, VerifierParams}; + use crate::folding::nova::PreprocessorParam; use crate::frontend::tests::{CubicFCircuit, CustomFCircuit, WrapperCircuit}; use crate::transcript::poseidon::poseidon_canonical_config; use crate::FoldingScheme; @@ -772,23 +789,6 @@ pub mod tests { let F_circuit = CubicFCircuit::::new(()).unwrap(); let z_0 = vec![Fr::from(3_u32)]; - // get the CS & CF_CS len - let (cs_len, cf_cs_len) = - get_cs_params_len::>( - &poseidon_config, - F_circuit, - ) - .unwrap(); - let (pedersen_params, _) = Pedersen::::setup(&mut rng, cs_len).unwrap(); - let (cf_pedersen_params, _) = Pedersen::::setup(&mut rng, cf_cs_len).unwrap(); - - let prover_params = - ProverParams::, Pedersen> { - poseidon_config: poseidon_config.clone(), - cs_pp: pedersen_params.clone(), - cf_cs_pp: cf_pedersen_params.clone(), - }; - type N = Nova< Projective, GVar, @@ -799,22 +799,24 @@ pub mod tests { Pedersen, >; - // 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, Projective2, + CubicFCircuit, Pedersen, Pedersen, - > { - 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(); N::verify( verifier_params, diff --git a/folding-schemes/src/folding/nova/mod.rs b/folding-schemes/src/folding/nova/mod.rs index bcc1564..071959c 100644 --- a/folding-schemes/src/folding/nova/mod.rs +++ b/folding-schemes/src/folding/nova/mod.rs @@ -13,19 +13,18 @@ use ark_std::fmt::Debug; use ark_std::rand::RngCore; use ark_std::{One, Zero}; 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::folding::circuits::cyclefold::{fold_cyclefold_circuit, CycleFoldCircuit}; use crate::folding::circuits::{ + cyclefold::{fold_cyclefold_circuit, CycleFoldCircuit}, nonnative::{ affine::nonnative_affine_to_field_elements, uint::nonnative_field_to_field_elements, }, CF2, }; 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::FoldingScheme; @@ -70,6 +69,7 @@ where pub fn hash( &self, poseidon_config: &PoseidonConfig, + pp_hash: C::ScalarField, // public params hash i: C::ScalarField, z_0: Vec, z_i: Vec, @@ -80,7 +80,7 @@ where CRH::::evaluate( poseidon_config, vec![ - vec![i], + vec![pp_hash, i], z_0, z_i, vec![self.u], @@ -140,9 +140,13 @@ where pub fn hash_cyclefold( &self, poseidon_config: &PoseidonConfig, + pp_hash: C::BaseField, // public params hash ) -> Result { - CRH::::evaluate(poseidon_config, self.to_field_elements().unwrap()) - .map_err(|e| Error::Other(e.to_string())) + CRH::::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, } +impl VerifierParams +where + C1: CurveGroup, + C2: CurveGroup, + CS1: CommitmentScheme, + CS2: CommitmentScheme, +{ + /// returns the hash of the public parameters of Nova + pub fn pp_hash(&self) -> Result { + pp_hash::( + &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 /// [CycleFold](https://eprint.iacr.org/2023/1192.pdf), following the FoldingScheme trait #[derive(Clone, Debug)] @@ -280,6 +303,8 @@ where pub cf_cs_pp: CS2::ProverParams, /// F circuit, the circuit that is being folded pub F: FC, + /// public params hash + pub pp_hash: C1::ScalarField, pub i: C1::ScalarField, /// initial state pub z_0: Vec, @@ -343,8 +368,8 @@ where cf_cs_pp = prep_param.clone().cf_cs_pp.unwrap(); cf_cs_vp = prep_param.clone().cf_cs_vp.unwrap(); } 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:: { @@ -356,14 +381,21 @@ where poseidon_config: prep_param.poseidon_config.clone(), 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`. - fn init(pp: &Self::ProverParam, F: FC, z_0: Vec) -> Result { + fn init( + params: (Self::ProverParam, Self::VerifierParam), + F: FC, + z_0: Vec, + ) -> Result { + let (pp, vp) = params; + // prepare the circuit to obtain its R1CS let cs = ConstraintSystem::::new_ref(); let cs2 = ConstraintSystem::::new_ref(); @@ -382,6 +414,9 @@ where let cs2 = cs2.into_inner().ok_or(Error::NoInnerConstraintSystem)?; let cf_r1cs = extract_r1cs::(&cs2); + // compute the public params hash + let pp_hash = vp.pp_hash()?; + // setup the dummy instances let (w_dummy, u_dummy) = 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(), cf_cs_pp: pp.cf_cs_pp.clone(), F, + pp_hash, i: C1::ScalarField::zero(), z_0: z_0.clone(), z_i: z_0, @@ -453,6 +489,7 @@ where // r_bits is the r used to the RLC of the F' instances let r_bits = ChallengeGadget::::get_challenge_native( &self.poseidon_config, + self.pp_hash, self.U_i.clone(), self.u_i.clone(), cmT, @@ -471,6 +508,7 @@ where // u_{i+1}.x[0] = H(i+1, z_0, z_{i+1}, U_{i+1}) let u_i1_x = U_i1.hash( &self.poseidon_config, + self.pp_hash, self.i + C1::ScalarField::one(), self.z_0.clone(), z_i1.clone(), @@ -479,11 +517,14 @@ where let cf_u_i1_x: C1::ScalarField; 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 augmented_F_circuit = AugmentedFCircuit:: { _gc2: PhantomData, poseidon_config: self.poseidon_config.clone(), + pp_hash: Some(self.pp_hash), i: Some(C1::ScalarField::zero()), // = i=0 i_usize: Some(0), 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, _) = 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:: { _gc2: PhantomData, poseidon_config: self.poseidon_config.clone(), + pp_hash: Some(self.pp_hash), i: Some(self.i), i_usize: Some(i_usize), z_0: Some(self.z_0.clone()), @@ -670,14 +712,16 @@ where return Err(Error::IVCVerificationFail); } + let pp_hash = vp.pp_hash()?; + // check that u_i's output points to the running instance // 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] { return Err(Error::IVCVerificationFail); } // 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] { return Err(Error::IVCVerificationFail); } @@ -767,6 +811,7 @@ where &self.poseidon_config, self.cf_r1cs.clone(), self.cf_cs_pp.clone(), + self.pp_hash, cf_W_i, cf_U_i, cf_u_i_x, @@ -884,10 +929,10 @@ pub mod tests { cf_cs_pp: 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 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; for _ in 0..num_steps { @@ -897,7 +942,7 @@ pub mod tests { let (running_instance, incoming_instance, cyclefold_instance) = nova.instances(); N::::verify( - verifier_params, + nova_params.1, // Nova's verifier params z_0, nova.z_i, nova.i, diff --git a/folding-schemes/src/folding/nova/nifs.rs b/folding-schemes/src/folding/nova/nifs.rs index 8325802..9e5a1be 100644 --- a/folding-schemes/src/folding/nova/nifs.rs +++ b/folding-schemes/src/folding/nova/nifs.rs @@ -4,7 +4,7 @@ use ark_std::Zero; use std::marker::PhantomData; use super::{CommittedInstance, Witness}; -use crate::ccs::r1cs::R1CS; +use crate::arith::r1cs::R1CS; use crate::commitment::CommitmentScheme; use crate::transcript::Transcript; 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_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::folding::nova::circuits::ChallengeGadget; use crate::folding::nova::traits::NovaR1CS; @@ -259,8 +259,10 @@ pub mod tests { let poseidon_config = poseidon_canonical_config::(); + let pp_hash = C::ScalarField::from(42u32); // only for test let r_bits = ChallengeGadget::::get_challenge_native( &poseidon_config, + pp_hash, ci1.clone(), ci2.clone(), cmT, diff --git a/folding-schemes/src/folding/nova/serialize.rs b/folding-schemes/src/folding/nova/serialize.rs index d4682fd..6506adb 100644 --- a/folding-schemes/src/folding/nova/serialize.rs +++ b/folding-schemes/src/folding/nova/serialize.rs @@ -14,7 +14,7 @@ use super::{circuits::AugmentedFCircuit, Nova, ProverParams}; use super::{CommittedInstance, Witness}; use crate::folding::circuits::{cyclefold::CycleFoldCircuit, CF2}; use crate::{ - ccs::r1cs::extract_r1cs, commitment::CommitmentScheme, folding::circuits::CF1, + arith::r1cs::extract_r1cs, commitment::CommitmentScheme, folding::circuits::CF1, frontend::FCircuit, }; @@ -41,6 +41,7 @@ where mut writer: W, compress: ark_serialize::Compress, ) -> Result<(), ark_serialize::SerializationError> { + self.pp_hash.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_i.serialize_with_mode(&mut writer, compress)?; @@ -53,7 +54,8 @@ where } 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_i.serialized_size(compress) + self.w_i.serialized_size(compress) @@ -115,6 +117,7 @@ where prover_params: ProverParams, poseidon_config: PoseidonConfig, ) -> Result { + let pp_hash = C1::ScalarField::deserialize_with_mode(&mut reader, compress, validate)?; let i = C1::ScalarField::deserialize_with_mode(&mut reader, compress, validate)?; let z_0 = Vec::::deserialize_with_mode(&mut reader, compress, validate)?; let z_i = Vec::::deserialize_with_mode(&mut reader, compress, validate)?; @@ -151,8 +154,13 @@ where _gc1: PhantomData, _c2: PhantomData, _gc2: PhantomData, + r1cs, + cf_r1cs, + poseidon_config, cs_pp: prover_params.cs_pp, cf_cs_pp: prover_params.cf_cs_pp, + F: f_circuit, + pp_hash, i, z_0, z_i, @@ -162,10 +170,6 @@ where U_i, cf_W_i, cf_U_i, - r1cs, - cf_r1cs, - poseidon_config, - F: f_circuit, }) } } @@ -174,17 +178,12 @@ where pub mod tests { use ark_bn254::{constraints::GVar, Bn254, Fr, G1Projective as Projective}; 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 std::{fs, io::Write}; 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}, transcript::poseidon::poseidon_canonical_config, FoldingScheme, @@ -195,15 +194,6 @@ pub mod tests { let mut rng = ark_std::test_rng(); let poseidon_config = poseidon_canonical_config::(); let F_circuit = CubicFCircuit::::new(()).unwrap(); - let (cs_len, cf_cs_len) = - get_cs_params_len::>( - &poseidon_config, - F_circuit, - ) - .unwrap(); - let (kzg_pk, _): (KZGProverKey, KZGVerifierKey) = - KZG::::setup(&mut rng, cs_len).unwrap(); - let (cf_pedersen_params, _) = Pedersen::::setup(&mut rng, cf_cs_len).unwrap(); // Initialize nova and make multiple `prove_step()` type N = Nova< @@ -215,15 +205,11 @@ pub mod tests { KZG<'static, Bn254>, Pedersen, >; - let prover_params = - ProverParams::, Pedersen> { - 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 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; for _ in 0..num_steps { @@ -257,7 +243,7 @@ pub mod tests { bytes.as_slice(), Compress::No, Validate::No, - prover_params, + nova_params.0, // Nova's prover params poseidon_config, ) .unwrap(); diff --git a/folding-schemes/src/folding/nova/traits.rs b/folding-schemes/src/folding/nova/traits.rs index 2ff3f83..29a4ede 100644 --- a/folding-schemes/src/folding/nova/traits.rs +++ b/folding-schemes/src/folding/nova/traits.rs @@ -3,7 +3,7 @@ use ark_ec::{CurveGroup, Group}; use ark_std::{One, Zero}; use super::{CommittedInstance, Witness}; -use crate::ccs::r1cs::R1CS; +use crate::arith::{r1cs::R1CS, Arith}; use crate::Error; /// NovaR1CS extends R1CS methods with Nova specific methods diff --git a/folding-schemes/src/folding/protogalaxy/folding.rs b/folding-schemes/src/folding/protogalaxy/folding.rs index d60ee42..f12d07b 100644 --- a/folding-schemes/src/folding/protogalaxy/folding.rs +++ b/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::{CommittedInstance, Witness}; -use crate::ccs::r1cs::R1CS; +use crate::arith::r1cs::R1CS; use crate::transcript::Transcript; use crate::utils::vec::*; use crate::utils::virtual_polynomial::bit_decompose; @@ -383,7 +383,7 @@ mod tests { use ark_pallas::{Fr, Projective}; 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::transcript::poseidon::{poseidon_canonical_config, PoseidonTranscript}; diff --git a/folding-schemes/src/lib.rs b/folding-schemes/src/lib.rs index cb70231..fd5280b 100644 --- a/folding-schemes/src/lib.rs +++ b/folding-schemes/src/lib.rs @@ -10,7 +10,7 @@ use thiserror::Error; use crate::frontend::FCircuit; -pub mod ccs; +pub mod arith; pub mod commitment; pub mod constants; pub mod folding; @@ -122,7 +122,7 @@ where ) -> Result<(Self::ProverParam, Self::VerifierParam), Error>; fn init( - pp: &Self::ProverParam, + params: (Self::ProverParam, Self::VerifierParam), step_circuit: FC, z_0: Vec, // initial state ) -> Result; diff --git a/folding-schemes/src/utils/mle.rs b/folding-schemes/src/utils/mle.rs index a044eca..68c4aea 100644 --- a/folding-schemes/src/utils/mle.rs +++ b/folding-schemes/src/utils/mle.rs @@ -104,7 +104,7 @@ pub fn dense_vec_to_mle(n_vars: usize, v: &[F]) -> SparseMultilin mod tests { use super::*; use crate::{ - ccs::tests::get_test_z, + arith::ccs::tests::get_test_z, utils::multilinear_polynomial::fix_variables, utils::multilinear_polynomial::tests::fix_last_variables, utils::{hypercube::BooleanHypercube, vec::tests::to_F_matrix}, diff --git a/folding-schemes/src/utils/mod.rs b/folding-schemes/src/utils/mod.rs index 72aff2d..764dc18 100644 --- a/folding-schemes/src/utils/mod.rs +++ b/folding-schemes/src/utils/mod.rs @@ -1,6 +1,13 @@ +use ark_crypto_primitives::sponge::poseidon::PoseidonConfig; use ark_ec::{AffineRepr, CurveGroup}; use ark_ff::PrimeField; +use ark_serialize::CanonicalSerialize; 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 hypercube; @@ -32,3 +39,64 @@ pub fn get_cm_coordinates(cm: &C) -> Vec { let (cm_x, cm_y) = cm.xy().unwrap_or(zero); vec![*cm_x, *cm_y] } + +/// returns the hash of the given public parameters of the Folding Scheme +pub fn pp_hash( + arith: &impl Arith, + cf_arith: &impl Arith, + cs_vp: &CS1::VerifierParams, + cf_cs_vp: &CS2::VerifierParams, + poseidon_config: &PoseidonConfig, +) -> Result +where + C1: CurveGroup, + C2: CurveGroup, + CS1: CommitmentScheme, + CS2: CommitmentScheme, +{ + 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, + )) +} diff --git a/solidity-verifiers/src/verifiers/g16.rs b/solidity-verifiers/src/verifiers/g16.rs index 0063e14..eda66fa 100644 --- a/solidity-verifiers/src/verifiers/g16.rs +++ b/solidity-verifiers/src/verifiers/g16.rs @@ -3,7 +3,7 @@ use crate::utils::encoding::{G1Repr, G2Repr}; use crate::utils::HeaderInclusion; use crate::{ProtocolVerifierKey, GPL3_SDPX_IDENTIFIER}; use ark_bn254::Bn254; -use ark_groth16::VerifyingKey; +use ark_groth16::VerifyingKey as ArkVerifyingKey; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; use askama::Template; @@ -48,10 +48,10 @@ impl From for Groth16Verifier { // 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. #[derive(CanonicalDeserialize, CanonicalSerialize, Clone, PartialEq, Debug)] -pub struct Groth16VerifierKey(pub(crate) VerifyingKey); +pub struct Groth16VerifierKey(pub(crate) ArkVerifyingKey); -impl From> for Groth16VerifierKey { - fn from(value: VerifyingKey) -> Self { +impl From> for Groth16VerifierKey { + fn from(value: ArkVerifyingKey) -> Self { Self(value) } } @@ -95,7 +95,7 @@ mod tests { #[test] fn groth16_vk_serde_roundtrip() { - let (_, _, _, vk, _) = setup(DEFAULT_SETUP_LEN); + let (_, _, _, _, vk, _) = setup(DEFAULT_SETUP_LEN); let g16_vk = Groth16VerifierKey::from(vk); let mut bytes = vec![]; @@ -109,7 +109,7 @@ mod tests { #[test] fn test_groth16_verifier_accepts_and_rejects_proofs() { 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 proof = Groth16::::prove(&g16_pk, circuit, &mut rng).unwrap(); diff --git a/solidity-verifiers/src/verifiers/kzg.rs b/solidity-verifiers/src/verifiers/kzg.rs index a630235..dab3be7 100644 --- a/solidity-verifiers/src/verifiers/kzg.rs +++ b/solidity-verifiers/src/verifiers/kzg.rs @@ -102,7 +102,7 @@ mod tests { #[test] 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 mut bytes = vec![]; @@ -115,7 +115,7 @@ mod tests { #[test] 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 res = HeaderInclusion::::builder() @@ -136,7 +136,7 @@ mod tests { let transcript_p = &mut PoseidonTranscript::::new(&poseidon_config); let transcript_v = &mut PoseidonTranscript::::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 v: Vec = std::iter::repeat_with(|| Fr::rand(&mut rng)) diff --git a/solidity-verifiers/src/verifiers/mod.rs b/solidity-verifiers/src/verifiers/mod.rs index db283a4..8a3bf0d 100644 --- a/solidity-verifiers/src/verifiers/mod.rs +++ b/solidity-verifiers/src/verifiers/mod.rs @@ -97,6 +97,7 @@ pub mod tests { pub fn setup<'a>( n: usize, ) -> ( + Fr, // public params hash KZGProverKey<'a, G1>, KZGVerifierKey, ark_groth16::ProvingKey, @@ -115,6 +116,7 @@ pub mod tests { let (kzg_pk, kzg_vk): (KZGProverKey, KZGVerifierKey) = KZG::::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) } } diff --git a/solidity-verifiers/src/verifiers/nova_cyclefold.rs b/solidity-verifiers/src/verifiers/nova_cyclefold.rs index 236a15d..9328460 100644 --- a/solidity-verifiers/src/verifiers/nova_cyclefold.rs +++ b/solidity-verifiers/src/verifiers/nova_cyclefold.rs @@ -1,9 +1,10 @@ #![allow(non_snake_case)] #![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 askama::Template; @@ -27,6 +28,7 @@ pub fn get_decider_template_for_cyclefold_decider( #[derive(Template, Default)] #[template(path = "nova_cyclefold_decider.askama.sol", ext = "sol")] pub struct NovaCycleFoldDecider { + pp_hash: Fr, // public params hash groth16_verifier: Groth16Verifier, kzg10_verifier: KZG10Verifier, // 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 bits_per_limb = NonNativeUintVar::::bits_per_limb(); Self { + pp_hash: value.pp_hash, groth16_verifier, kzg10_verifier: KZG10Verifier::from(value.kzg_vk), z_len: value.z_len, @@ -54,6 +57,7 @@ impl From for NovaCycleFoldDecider { #[derive(CanonicalDeserialize, CanonicalSerialize, PartialEq, Debug, Clone)] pub struct NovaCycleFoldVerifierKey { + pp_hash: Fr, g16_vk: Groth16VerifierKey, kzg_vk: KZG10VerifierKey, 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 { - 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 // in the NovaCycleFoldDecider verifier contract -impl From<((VerifyingKey, VerifierKey), usize)> for NovaCycleFoldVerifierKey { - fn from(value: ((VerifyingKey, VerifierKey), usize)) -> Self { +impl + From<( + (Fr, ArkG16VerifierKey, ArkKZG10VerifierKey), + usize, + )> for NovaCycleFoldVerifierKey +{ + fn from( + value: ( + (Fr, ArkG16VerifierKey, ArkKZG10VerifierKey), + usize, + ), + ) -> Self { 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 - let kzg_vk = KZG10VerifierKey::from((decider_vp.1, Vec::new())); + let kzg_vk = KZG10VerifierKey::from((decider_vp.2, Vec::new())); Self { + pp_hash: decider_vp.0, g16_vk, kzg_vk, z_len: value.1, @@ -101,12 +117,14 @@ impl From<((VerifyingKey, VerifierKey), usize)> for NovaCycleFoldV impl NovaCycleFoldVerifierKey { pub fn new( - vkey_g16: VerifyingKey, - vkey_kzg: VerifierKey, + pp_hash: Fr, + vkey_g16: ArkG16VerifierKey, + vkey_kzg: ArkKZG10VerifierKey, crs_points: Vec, z_len: usize, ) -> Self { Self { + pp_hash, g16_vk: Groth16VerifierKey::from(vkey_g16), kzg_vk: KZG10VerifierKey::from((vkey_kzg, crs_points)), z_len, @@ -117,12 +135,9 @@ impl NovaCycleFoldVerifierKey { #[cfg(test)] mod tests { use ark_bn254::{constraints::GVar, Bn254, Fr, G1Projective as G1}; - use ark_crypto_primitives::snark::SNARK; 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_poly_commit::kzg10::VerifierKey as KZGVerifierKey; use ark_r1cs_std::alloc::AllocVar; use ark_r1cs_std::fields::fp::FpVar; use ark_relations::r1cs::{ConstraintSystemRef, SynthesisError}; @@ -132,15 +147,10 @@ mod tests { use std::time::Instant; use folding_schemes::{ - commitment::{ - kzg::{ProverKey as KZGProverKey, KZG}, - pedersen::Pedersen, - CommitmentScheme, - }, + commitment::{kzg::KZG, pedersen::Pedersen}, folding::nova::{ decider_eth::{prepare_calldata, Decider as DeciderEth}, - decider_eth_circuit::DeciderEthCircuit, - get_cs_params_len, Nova, ProverParams, + Nova, PreprocessorParam, }, frontend::FCircuit, transcript::poseidon::poseidon_canonical_config, @@ -156,6 +166,24 @@ mod tests { NovaCycleFoldVerifierKey, ProtocolVerifierKey, }; + type NOVA = Nova, Pedersen>; + type DECIDER = DeciderEth< + G1, + GVar, + G2, + GVar2, + FC, + KZG<'static, Bn254>, + Pedersen, + Groth16, + NOVA, + >; + + type FS_PP = as FoldingScheme>::ProverParam; + type FS_VP = as FoldingScheme>::VerifierParam; + type DECIDER_PP = as Decider>>::ProverParam; + type DECIDER_VP = as Decider>>::VerifierParam; + /// Test circuit to be folded #[derive(Clone, Copy, Debug)] pub struct CubicFCircuit { @@ -256,10 +284,10 @@ mod tests { #[test] 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 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 .serialize_protocol_verifier_key(&mut bytes) @@ -272,8 +300,8 @@ mod tests { #[test] 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::::builder() .template(nova_cyclefold_vk) @@ -282,59 +310,29 @@ mod tests { save_solidity("NovaDecider.sol", &decider_solidity_code.render().unwrap()); } - #[allow(clippy::type_complexity)] - fn init_test_prover_params>() -> ( - ProverParams, Pedersen>, - KZGVerifierKey, - ) { - let mut rng = ark_std::test_rng(); - let poseidon_config = poseidon_canonical_config::(); - let f_circuit = FC::new(()).unwrap(); - let (cs_len, cf_cs_len) = - get_cs_params_len::(&poseidon_config, f_circuit).unwrap(); - let (kzg_pk, kzg_vk): (KZGProverKey, KZGVerifierKey) = - KZG::::setup(&mut rng, cs_len).unwrap(); - let (cf_pedersen_params, _) = Pedersen::::setup(&mut rng, cf_cs_len).unwrap(); - let fs_prover_params = ProverParams::, Pedersen> { - 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. #[allow(clippy::type_complexity)] - fn init_params>() -> ( - ProverParams, Pedersen>, - KZGVerifierKey, - ProvingKey, - G16VerifierKey, - ) { + fn init_params>( + ) -> ((FS_PP, FS_VP), (DECIDER_PP, DECIDER_VP)) { let mut rng = rand::rngs::OsRng; - let start = Instant::now(); - let (fs_prover_params, kzg_vk) = init_test_prover_params::(); - println!("generated Nova folding params: {:?}", start.elapsed()); - let f_circuit = FC::new(()).unwrap(); - - pub type NOVA_FCircuit = - Nova, Pedersen>; - 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::(); - let decider_circuit = - DeciderEthCircuit::, Pedersen>::from_nova::( - nova.clone(), - ) - .unwrap(); - let start = Instant::now(); - let (g16_pk, g16_vk) = - Groth16::::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::, Pedersen>::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 @@ -346,52 +344,31 @@ mod tests { /// - modifies the z_0 and checks that it does not pass the EVM check #[allow(clippy::type_complexity)] fn nova_cyclefold_solidity_verifier_opt>( - params: ( - ProverParams, Pedersen>, - KZGVerifierKey, - ProvingKey, - G16VerifierKey, - ), + fs_params: (FS_PP, FS_VP), + decider_params: (DECIDER_PP, DECIDER_VP), z_0: Vec, n_steps: usize, ) { - let (fs_prover_params, kzg_vk, g16_pk, g16_vk) = params.clone(); - - pub type NOVA_FCircuit = - Nova, Pedersen>; - pub type DECIDERETH_FCircuit = DeciderEth< - G1, - GVar, - G2, - GVar2, - FC, - KZG<'static, Bn254>, - Pedersen, - Groth16, - NOVA_FCircuit, - >; + let (decider_pp, decider_vp) = decider_params; + 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 nova = NOVA_FCircuit::init(&fs_prover_params, f_circuit, z_0).unwrap(); + let mut nova = NOVA::::init(fs_params, f_circuit, z_0).unwrap(); for _ in 0..n_steps { nova.prove_step(&mut rng, vec![]).unwrap(); } let start = Instant::now(); - let proof = - DECIDERETH_FCircuit::prove(rng, (g16_pk, fs_prover_params.cs_pp.clone()), nova.clone()) - .unwrap(); + let proof = DECIDER::::prove(rng, decider_pp, nova.clone()).unwrap(); println!("generated Decider proof: {:?}", start.elapsed()); - let verified = DECIDERETH_FCircuit::::verify( - (g16_vk, kzg_vk), + let verified = DECIDER::::verify( + decider_vp, nova.i, nova.z_0.clone(), nova.z_i.clone(), @@ -448,12 +425,22 @@ mod tests { #[test] fn nova_cyclefold_solidity_verifier() { - let params = init_params::>(); + let (nova_params, decider_params) = init_params::>(); let z_0 = vec![Fr::from(3_u32)]; - nova_cyclefold_solidity_verifier_opt::>(params.clone(), z_0.clone(), 2); - nova_cyclefold_solidity_verifier_opt::>(params.clone(), z_0.clone(), 3); + nova_cyclefold_solidity_verifier_opt::>( + nova_params.clone(), + decider_params.clone(), + z_0.clone(), + 2, + ); + nova_cyclefold_solidity_verifier_opt::>( + nova_params, + decider_params, + z_0, + 3, + ); - let params = init_params::>(); + let (nova_params, decider_params) = init_params::>(); let z_0 = vec![ Fr::from(1_u32), Fr::from(1_u32), @@ -462,12 +449,14 @@ mod tests { Fr::from(1_u32), ]; nova_cyclefold_solidity_verifier_opt::>( - params.clone(), + nova_params.clone(), + decider_params.clone(), z_0.clone(), 2, ); nova_cyclefold_solidity_verifier_opt::>( - params.clone(), + nova_params, + decider_params, z_0.clone(), 3, ); diff --git a/solidity-verifiers/templates/nova_cyclefold_decider.askama.sol b/solidity-verifiers/templates/nova_cyclefold_decider.askama.sol index 7f15957..a82893a 100644 --- a/solidity-verifiers/templates/nova_cyclefold_decider.askama.sol +++ b/solidity-verifiers/templates/nova_cyclefold_decider.askama.sol @@ -78,10 +78,11 @@ contract NovaDecider is Groth16Verifier, KZG10Verifier { // from gamma_abc_len, we subtract 1. 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++) { - 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 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]); 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]); 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 - 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_y_limbs; @@ -146,8 +147,8 @@ contract NovaDecider is Groth16Verifier, KZG10Verifier { cmT_y_limbs = LimbsDecomposition.decompose(u_i_x_cmT[3]); 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`