diff --git a/folding-schemes/src/folding/circuits/cyclefold.rs b/folding-schemes/src/folding/circuits/cyclefold.rs index 43ce70cb..e09dd1b1 100644 --- a/folding-schemes/src/folding/circuits/cyclefold.rs +++ b/folding-schemes/src/folding/circuits/cyclefold.rs @@ -167,13 +167,8 @@ impl CycleFoldCommittedInstance { /// hash_cyclefold implements the committed instance hash compatible with the /// in-circuit implementation `CycleFoldCommittedInstanceVar::hash`. /// Returns `H(U_i)`, where `U_i` is a `CycleFoldCommittedInstance`. - pub fn hash_cyclefold>( - &self, - sponge: &T, - pp_hash: C::BaseField, // public params hash - ) -> C::BaseField { + pub fn hash_cyclefold>(&self, sponge: &T) -> C::BaseField { let mut sponge = sponge.clone(); - sponge.absorb(&pp_hash); sponge.absorb_nonnative(self); sponge.squeeze_field_elements(1)[0] } @@ -191,11 +186,9 @@ impl CycleFoldCommittedInstanceVar { pub fn hash, S>>( &self, sponge: &T, - pp_hash: FpVar>, // public params hash ) -> Result<(FpVar>, Vec>>), SynthesisError> { let mut sponge = sponge.clone(); let U_vec = self.to_native_sponge_field_elements()?; - sponge.absorb(&pp_hash)?; sponge.absorb(&U_vec)?; Ok(( // `unwrap` is safe because the sponge is guaranteed to return a single element @@ -325,12 +318,10 @@ pub struct CycleFoldChallengeGadget { impl CycleFoldChallengeGadget { pub fn get_challenge_native>( transcript: &mut T, - pp_hash: C::BaseField, // public params hash U_i: &CycleFoldCommittedInstance, u_i: &CycleFoldCommittedInstance, cmT: C, ) -> Vec { - transcript.absorb(&pp_hash); transcript.absorb_nonnative(U_i); transcript.absorb_nonnative(u_i); transcript.absorb_point(&cmT); @@ -340,12 +331,10 @@ impl CycleFoldChallengeGadget { // compatible with the native get_challenge_native pub fn get_challenge_gadget>( transcript: &mut T, - pp_hash: &FpVar, // public params hash U_i_vec: &[FpVar], u_i: &CycleFoldCommittedInstanceVar, cmT: &C::Var, ) -> Result>, SynthesisError> { - transcript.absorb(pp_hash)?; transcript.absorb(&U_i_vec)?; transcript.absorb_nonnative(u_i)?; transcript.absorb_point(cmT)?; @@ -596,10 +585,9 @@ impl CycleFoldAugmentationGadget { transcript: &mut impl Transcript>, cf_r1cs: &R1CS, cf_cs_params: &CS::ProverParams, - pp_hash: CF2, // public params hash - mut cf_W: CycleFoldWitness, // witness of the running instance - mut cf_U: CycleFoldCommittedInstance, // running instance - cf_ws: Vec>, // witnesses of the incoming instances + mut cf_W: CycleFoldWitness, // witness of the running instance + mut cf_U: CycleFoldCommittedInstance, // running instance + cf_ws: Vec>, // witnesses of the incoming instances cf_us: Vec>, // incoming instances ) -> Result< ( @@ -624,9 +612,8 @@ impl CycleFoldAugmentationGadget { )?; cf_cmTs.push(cf_cmT); - let cf_r_bits = CycleFoldChallengeGadget::get_challenge_native( - transcript, pp_hash, &cf_U, &cf_u, cf_cmT, - ); + let cf_r_bits = + CycleFoldChallengeGadget::get_challenge_native(transcript, &cf_U, &cf_u, cf_cmT); let cf_r_Fq = CF1::::from( as PrimeField>::BigInt::from_bits_le(&cf_r_bits)); (cf_W, cf_U) = CycleFoldNIFS::::prove( @@ -647,7 +634,6 @@ impl CycleFoldAugmentationGadget { pub fn fold_gadget( transcript: &mut impl TranscriptVar, S>, - pp_hash: &FpVar>, mut cf_U: CycleFoldCommittedInstanceVar, cf_us: Vec>, cf_cmTs: Vec, @@ -660,7 +646,6 @@ impl CycleFoldAugmentationGadget { for (cf_u, cmT) in cf_us.into_iter().zip(cf_cmTs) { let cf_r_bits = CycleFoldChallengeGadget::get_challenge_gadget( transcript, - pp_hash, &cf_U.to_native_sponge_field_elements()?, &cf_u, &cmT, @@ -721,10 +706,7 @@ impl, const H: bool> CycleFoldNIFS(); - let mut transcript_v = PoseidonSponge::::new(&poseidon_config); let pp_hash = Fr::rand(&mut rng); + let mut transcript_v = PoseidonSponge::::new_with_pp_hash(&poseidon_config, pp_hash); // prepare the committed instances to test in-circuit let ci: Vec> = (0..2) @@ -845,7 +827,6 @@ pub mod tests { let cmT = Projective::rand(&mut rng); // random only for testing let (ci3, r_bits) = NIFS::, PoseidonSponge>::verify( &mut transcript_v, - pp_hash, &ci1, &ci2, &cmT, @@ -873,7 +854,8 @@ pub mod tests { fn test_cyclefold_challenge_gadget() -> Result<(), Error> { let mut rng = ark_std::test_rng(); let poseidon_config = poseidon_canonical_config::(); - let mut transcript = PoseidonSponge::::new(&poseidon_config); + let pp_hash = Fq::from(42u32); // only for test + let mut transcript = PoseidonSponge::::new_with_pp_hash(&poseidon_config, pp_hash); let u_i = CycleFoldCommittedInstance:: { cmE: Projective::zero(), // zero on purpose, so we test also the zero point case @@ -894,10 +876,8 @@ pub mod tests { let cmT = Projective::rand(&mut rng); // random only for testing // compute the challenge natively - let pp_hash = Fq::from(42u32); // only for test let r_bits = CycleFoldChallengeGadget::::get_challenge_native( &mut transcript, - pp_hash, &U_i, &u_i, cmT, @@ -911,13 +891,12 @@ pub mod tests { Ok(U_i.clone()) })?; let cmTVar = GVar::new_witness(cs.clone(), || Ok(cmT))?; + let pp_hashVar = FpVar::::new_witness(cs.clone(), || Ok(pp_hash))?; let mut transcript_var = - PoseidonSpongeVar::::new(ConstraintSystem::::new_ref(), &poseidon_config); + PoseidonSpongeVar::::new_with_pp_hash(&poseidon_config, &pp_hashVar)?; - let pp_hashVar = FpVar::::new_witness(cs.clone(), || Ok(pp_hash))?; let r_bitsVar = CycleFoldChallengeGadget::::get_challenge_gadget( &mut transcript_var, - &pp_hashVar, &U_iVar.to_native_sponge_field_elements()?, &u_iVar, &cmTVar, @@ -936,7 +915,8 @@ pub mod tests { fn test_cyclefold_hash_gadget() -> Result<(), Error> { let mut rng = ark_std::test_rng(); let poseidon_config = poseidon_canonical_config::(); - let sponge = PoseidonSponge::::new(&poseidon_config); + let pp_hash = Fq::from(42u32); // only for test + let sponge = PoseidonSponge::::new_with_pp_hash(&poseidon_config, pp_hash); let U_i = CycleFoldCommittedInstance:: { cmE: Projective::rand(&mut rng), @@ -946,18 +926,17 @@ pub mod tests { .take(TestCycleFoldConfig::::IO_LEN) .collect(), }; - let pp_hash = Fq::from(42u32); // only for test - let h = U_i.hash_cyclefold(&sponge, pp_hash); + let h = U_i.hash_cyclefold(&sponge); let cs = ConstraintSystem::::new_ref(); let U_iVar = CycleFoldCommittedInstanceVar::::new_witness(cs.clone(), || { Ok(U_i.clone()) })?; let pp_hashVar = FpVar::::new_witness(cs.clone(), || Ok(pp_hash))?; - let (hVar, _) = U_iVar.hash( - &PoseidonSpongeVar::new(cs.clone(), &poseidon_config), - pp_hashVar, - )?; + let (hVar, _) = U_iVar.hash(&PoseidonSpongeVar::new_with_pp_hash( + &poseidon_config, + &pp_hashVar, + )?)?; hVar.enforce_equal(&FpVar::new_witness(cs.clone(), || Ok(h))?)?; assert!(cs.is_satisfied()?); Ok(()) diff --git a/folding-schemes/src/folding/circuits/decider/mod.rs b/folding-schemes/src/folding/circuits/decider/mod.rs index fbfda6fb..ad603970 100644 --- a/folding-schemes/src/folding/circuits/decider/mod.rs +++ b/folding-schemes/src/folding/circuits/decider/mod.rs @@ -115,7 +115,6 @@ pub trait DeciderEnabledNIFS< fn fold_field_elements_gadget( arith: &A, transcript: &mut PoseidonSpongeVar>, - pp_hash: FpVar>, U: RU::Var, U_vec: Vec>>, u: IU::Var, diff --git a/folding-schemes/src/folding/circuits/decider/off_chain.rs b/folding-schemes/src/folding/circuits/decider/off_chain.rs index 1ea8c71a..b92ba9bb 100644 --- a/folding-schemes/src/folding/circuits/decider/off_chain.rs +++ b/folding-schemes/src/folding/circuits/decider/off_chain.rs @@ -3,7 +3,7 @@ /// More details can be found at the documentation page: /// https://privacy-scaling-explorations.github.io/sonobe-docs/design/nova-decider-offchain.html use ark_crypto_primitives::sponge::{ - constraints::{AbsorbGadget, CryptographicSpongeVar}, + constraints::AbsorbGadget, poseidon::{constraints::PoseidonSpongeVar, PoseidonConfig}, }; use ark_r1cs_std::{alloc::AllocVar, eq::EqGadget, fields::fp::FpVar}; @@ -27,6 +27,7 @@ use crate::{ nova::{decider_eth_circuit::WitnessVar, nifs::nova_circuits::CommittedInstanceVar}, traits::{CommittedInstanceOps, CommittedInstanceVarOps, Dummy, WitnessOps, WitnessVarOps}, }, + transcript::TranscriptVar, Curve, }; @@ -177,10 +178,10 @@ where let kzg_evaluations = Vec::new_input(cs.clone(), || Ok(self.kzg_evaluations))?; // `sponge` is for digest computation. - let sponge = PoseidonSpongeVar::new(cs.clone(), &self.poseidon_config); + // notice that `pp_hash` has already been absorbed during init. + let sponge = PoseidonSpongeVar::new_with_pp_hash(&self.poseidon_config, &pp_hash)?; // `transcript` is for challenge generation. let mut transcript = sponge.clone(); - // notice that the `pp_hash` is absorbed inside the ChallengeGadget::get_challenge_gadget call // 1. enforce `U_{i+1}` and `W_{i+1}` satisfy `arith` arith.enforce_relation(&W_i1, &U_i1)?; @@ -189,15 +190,14 @@ where u_i.enforce_incoming()?; // 3. u_i.x[0] == H(i, z_0, z_i, U_i), u_i.x[1] == H(cf_U_i) - let (u_i_x, U_i_vec) = U_i.hash(&sponge, &pp_hash, &i, &z_0, &z_i)?; - let (cf_u_i_x, _) = cf_U_i.hash(&sponge, pp_hash.clone())?; + let (u_i_x, U_i_vec) = U_i.hash(&sponge, &i, &z_0, &z_i)?; + let (cf_u_i_x, _) = cf_U_i.hash(&sponge)?; u_i.get_public_inputs().enforce_equal(&[u_i_x, cf_u_i_x])?; // 6.1. partially enforce `NIFS.V(U_i, u_i) = U_{i+1}`. D::fold_field_elements_gadget( &self.arith, &mut transcript, - pp_hash, U_i, U_i_vec, u_i, @@ -281,8 +281,7 @@ impl ConstraintSynthesizer> for GenericOffchainDeciderCircuit let kzg_evaluations = Vec::new_input(cs.clone(), || Ok(self.kzg_evaluations))?; // `transcript` is for challenge generation. - let mut transcript = PoseidonSpongeVar::new(cs.clone(), &self.poseidon_config); - transcript.absorb(&pp_hash)?; + let mut transcript = PoseidonSpongeVar::new_with_pp_hash(&self.poseidon_config, &pp_hash)?; // 5. enforce `cf_U_i` and `cf_W_i` satisfy `cf_r1cs` cf_r1cs.enforce_relation(&cf_W_i, &cf_U_i)?; diff --git a/folding-schemes/src/folding/circuits/decider/on_chain.rs b/folding-schemes/src/folding/circuits/decider/on_chain.rs index 8c692229..a756ad42 100644 --- a/folding-schemes/src/folding/circuits/decider/on_chain.rs +++ b/folding-schemes/src/folding/circuits/decider/on_chain.rs @@ -1,7 +1,7 @@ /// This file implements the onchain (Ethereum's EVM) decider circuit. For non-ethereum use cases, /// other more efficient approaches can be used. use ark_crypto_primitives::sponge::{ - constraints::{AbsorbGadget, CryptographicSpongeVar}, + constraints::AbsorbGadget, poseidon::{constraints::PoseidonSpongeVar, PoseidonConfig}, }; use ark_r1cs_std::{alloc::AllocVar, eq::EqGadget, fields::fp::FpVar}; @@ -22,6 +22,7 @@ use crate::{ }, traits::{CommittedInstanceOps, CommittedInstanceVarOps, Dummy, WitnessOps, WitnessVarOps}, }, + transcript::TranscriptVar, Curve, }; @@ -211,7 +212,7 @@ where let kzg_evaluations = Vec::new_input(cs.clone(), || Ok(self.kzg_evaluations))?; // `sponge` is for digest computation. - let sponge = PoseidonSpongeVar::new(cs.clone(), &self.poseidon_config); + let sponge = PoseidonSpongeVar::new_with_pp_hash(&self.poseidon_config, &pp_hash)?; // `transcript` is for challenge generation. let mut transcript = sponge.clone(); @@ -226,8 +227,8 @@ where u_i.enforce_incoming()?; // 3. u_i.x[0] == H(i, z_0, z_i, U_i), u_i.x[1] == H(cf_U_i) - let (u_i_x, U_i_vec) = U_i.hash(&sponge, &pp_hash, &i, &z_0, &z_i)?; - let (cf_u_i_x, _) = cf_U_i.hash(&sponge, pp_hash.clone())?; + let (u_i_x, U_i_vec) = U_i.hash(&sponge, &i, &z_0, &z_i)?; + let (cf_u_i_x, _) = cf_U_i.hash(&sponge)?; u_i.get_public_inputs().enforce_equal(&[u_i_x, cf_u_i_x])?; #[cfg(feature = "light-test")] @@ -286,7 +287,6 @@ where D::fold_field_elements_gadget( &self.arith, &mut transcript, - pp_hash, U_i, U_i_vec, u_i, diff --git a/folding-schemes/src/folding/hypernova/circuits.rs b/folding-schemes/src/folding/hypernova/circuits.rs index 1fd4d256..14791258 100644 --- a/folding-schemes/src/folding/hypernova/circuits.rs +++ b/folding-schemes/src/folding/hypernova/circuits.rs @@ -1,6 +1,6 @@ /// Implementation of [HyperNova](https://eprint.iacr.org/2023/573.pdf) circuits use ark_crypto_primitives::sponge::{ - constraints::{AbsorbGadget, CryptographicSpongeVar}, + constraints::AbsorbGadget, poseidon::{constraints::PoseidonSpongeVar, PoseidonConfig, PoseidonSponge}, CryptographicSponge, }; @@ -25,7 +25,6 @@ use super::{ nimfs::{NIMFSProof, NIMFS}, HyperNovaCycleFoldConfig, Witness, }; -use crate::constants::NOVA_N_BITS_RO; use crate::folding::{ circuits::{ cyclefold::{ @@ -46,6 +45,7 @@ use crate::{ arith::{ccs::CCS, r1cs::extract_r1cs}, transcript::{AbsorbNonNativeGadget, TranscriptVar}, }; +use crate::{constants::NOVA_N_BITS_RO, transcript::Transcript}; use crate::{Curve, Error}; /// Committed CCS instance @@ -241,7 +241,6 @@ impl NIMFSGadget { // only used the CCS params, not the matrices ccs: &CCS, transcript: &mut T, - running_instances: &[LCCCSVar], // U new_instances: &[CCCSVar], // u proof: ProofVar, @@ -581,9 +580,10 @@ where let all_Ws = [vec![W_i.clone()], Ws].concat(); let all_ws = [vec![w_i.clone()], ws].concat(); - let mut transcript_p: PoseidonSponge = - PoseidonSponge::::new(&self.poseidon_config.clone()); - // since this is only for the number of constraints, no need to absorb the pp_hash here + let mut transcript_p = PoseidonSponge::new_with_pp_hash( + &self.poseidon_config.clone(), + C1::ScalarField::zero(), + ); let (nimfs_proof, U_i1, _, _) = NIMFS::>::prove( &mut transcript_p, &ccs, @@ -710,7 +710,11 @@ where })?; let cf_cmT = C2::Var::new_witness(cs.clone(), || Ok(self.cf_cmT.unwrap_or_else(C2::zero)))?; - let sponge = PoseidonSpongeVar::::new(cs.clone(), &self.poseidon_config); + let sponge = PoseidonSpongeVar::::new_with_pp_hash( + &self.poseidon_config, + &pp_hash, + )?; + let mut transcript = sponge.clone(); let is_basecase = i.is_zero()?; let is_not_basecase = !&is_basecase; @@ -718,9 +722,9 @@ 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(&sponge, &pp_hash, &i, &z_0, &z_i)?; + let (u_i_x, _) = U_i.clone().hash(&sponge, &i, &z_0, &z_i)?; // u_i.x[1] = H(cf_U_i) - let (cf_u_i_x, _) = cf_U_i.clone().hash(&sponge, pp_hash.clone())?; + let (cf_u_i_x, _) = cf_U_i.clone().hash(&sponge)?; // P.2. Construct u_i let u_i = CCCSVar:: { @@ -739,8 +743,6 @@ 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 mut transcript = PoseidonSpongeVar::new(cs.clone(), &self.poseidon_config); - transcript.absorb(&pp_hash)?; let (mut U_i1, rho_bits) = NIMFSGadget::::verify( cs.clone(), &self.ccs.clone(), @@ -760,16 +762,11 @@ where .F .generate_step_constraints(cs.clone(), i_usize, z_i, external_inputs)?; - let (u_i1_x, _) = U_i1.clone().hash( - &sponge, - &pp_hash, - &(i + FpVar::>::one()), - &z_0, - &z_i1, - )?; + let (u_i1_x, _) = + U_i1.clone() + .hash(&sponge, &(i + FpVar::>::one()), &z_0, &z_i1)?; let (u_i1_x_base, _) = LCCCSVar::new_constant(cs.clone(), U_dummy)?.hash( &sponge, - &pp_hash, &FpVar::>::one(), &z_0, &z_i1, @@ -807,7 +804,6 @@ where // C.3. nifs.verify (fold_committed_instance), obtains cf_U_{i+1} by folding cf_u_i & cf_U_i. let cf_U_i1 = CycleFoldAugmentationGadget::fold_gadget( &mut transcript, - &pp_hash, cf_U_i, vec![cf_u_i], vec![cf_cmT], @@ -817,10 +813,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(&sponge, pp_hash.clone())?; + let (cf_u_i1_x, _) = cf_U_i1.clone().hash(&sponge)?; let (cf_u_i1_x_base, _) = CycleFoldCommittedInstanceVar::::new_constant(cs.clone(), cf_u_dummy)? - .hash(&sponge, pp_hash)?; + .hash(&sponge)?; let cf_x = is_basecase.select(&cf_u_i1_x_base, &cf_u_i1_x)?; // This line "converts" `cf_x` from a witness to a public input. // Instead of directly modifying the constraint system, we explicitly @@ -871,7 +867,7 @@ mod tests { traits::CommittedInstanceOps, }, frontend::utils::{cubic_step_native, CubicFCircuit}, - transcript::poseidon::poseidon_canonical_config, + transcript::{poseidon::poseidon_canonical_config, Transcript}, }; #[test] @@ -1017,7 +1013,11 @@ mod tests { // Prover's transcript let poseidon_config = poseidon_canonical_config::(); - let mut transcript_p: PoseidonSponge = PoseidonSponge::::new(&poseidon_config); + let pp_hash = Fr::from(42u32); // only for test + let mut transcript_p: PoseidonSponge = + PoseidonSponge::::new_with_pp_hash(&poseidon_config, pp_hash); + // Verifier's transcript + let mut transcript_v: PoseidonSponge = transcript_p.clone(); // Run the prover side of the multifolding let (proof, folded_lcccs, folded_witness, _) = @@ -1030,9 +1030,6 @@ mod tests { &w_cccs, )?; - // Verifier's transcript - let mut transcript_v: PoseidonSponge = PoseidonSponge::::new(&poseidon_config); - // Run the verifier side of the multifolding let folded_lcccs_v = NIMFS::>::verify( &mut transcript_v, @@ -1053,7 +1050,9 @@ mod tests { let cccs_instancesVar = Vec::>::new_witness(cs.clone(), || Ok(cccs_instances.clone()))?; let proofVar = ProofVar::::new_witness(cs.clone(), || Ok(proof.clone()))?; - let mut transcriptVar = PoseidonSpongeVar::::new(cs.clone(), &poseidon_config); + let pp_hashVar = FpVar::::new_witness(cs.clone(), || Ok(pp_hash))?; + let mut transcriptVar = + PoseidonSpongeVar::::new_with_pp_hash(&poseidon_config, &pp_hashVar)?; let enabled = Boolean::::new_witness(cs.clone(), || Ok(true))?; let (folded_lcccsVar, _) = NIMFSGadget::::verify( @@ -1108,13 +1107,13 @@ mod tests { pub fn test_lcccs_hash() -> Result<(), Error> { let mut rng = test_rng(); let poseidon_config = poseidon_canonical_config::(); - let sponge = PoseidonSponge::::new(&poseidon_config); + let pp_hash = Fr::from(42u32); // only for test + let sponge = PoseidonSponge::::new_with_pp_hash(&poseidon_config, pp_hash); let ccs = get_test_ccs(); let z1 = get_test_z::(3); let (pedersen_params, _) = Pedersen::::setup(&mut rng, ccs.n - ccs.l - 1)?; - let pp_hash = Fr::from(42u32); // only for test let i = Fr::from(3_u32); let z_0 = vec![Fr::from(3_u32)]; @@ -1124,19 +1123,17 @@ mod tests { &pedersen_params, &z1, )?; - let h = lcccs.clone().hash(&sponge, pp_hash, i, &z_0, &z_i); + let h = lcccs.clone().hash(&sponge, i, &z_0, &z_i); let cs = ConstraintSystem::::new_ref(); - let spongeVar = PoseidonSpongeVar::::new(cs.clone(), &poseidon_config); let pp_hashVar = FpVar::::new_witness(cs.clone(), || Ok(pp_hash))?; + let spongeVar = PoseidonSpongeVar::::new_with_pp_hash(&poseidon_config, &pp_hashVar)?; let iVar = FpVar::::new_witness(cs.clone(), || Ok(i))?; let z_0Var = Vec::>::new_witness(cs.clone(), || Ok(z_0.clone()))?; let z_iVar = Vec::>::new_witness(cs.clone(), || Ok(z_i.clone()))?; let lcccsVar = LCCCSVar::::new_witness(cs.clone(), || Ok(lcccs))?; - let (hVar, _) = lcccsVar - .clone() - .hash(&spongeVar, &pp_hashVar, &iVar, &z_0Var, &z_iVar)?; + let (hVar, _) = lcccsVar.clone().hash(&spongeVar, &iVar, &z_0Var, &z_iVar)?; assert!(cs.is_satisfied()?); // check that the natively computed and in-circuit computed hashes match @@ -1148,7 +1145,9 @@ mod tests { pub fn test_augmented_f_circuit() -> Result<(), Error> { let mut rng = test_rng(); let poseidon_config = poseidon_canonical_config::(); - let sponge = PoseidonSponge::::new(&poseidon_config); + // public params hash + let pp_hash = Fr::from(42u32); // only for test + let sponge = PoseidonSponge::::new_with_pp_hash(&poseidon_config, pp_hash); const MU: usize = 3; const NU: usize = 3; @@ -1179,9 +1178,6 @@ mod tests { let (cf_pedersen_params, _) = Pedersen::::setup(&mut rng, cf_r1cs.A.n_cols - cf_r1cs.l - 1)?; - // 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(); @@ -1204,8 +1200,8 @@ 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(&sponge, pp_hash, Fr::zero(), &z_0, &z_i), - cf_U_i.hash_cyclefold(&sponge, pp_hash), + U_i.hash(&sponge, Fr::zero(), &z_0, &z_i), + cf_U_i.hash_cyclefold(&sponge), ]; let n_steps: usize = 4; @@ -1234,11 +1230,11 @@ mod tests { W_i1 = Witness::::dummy(&ccs); U_i1 = LCCCS::dummy(&ccs); - u_i1_x = U_i1.hash(&sponge, pp_hash, Fr::one(), &z_0, &z_i1); + u_i1_x = U_i1.hash(&sponge, Fr::one(), &z_0, &z_i1); // hash the initial (dummy) CycleFold instance, which is used as the 2nd public // input in the AugmentedFCircuit - cf_u_i1_x = cf_U_i.hash_cyclefold(&sponge, pp_hash); + cf_u_i1_x = cf_U_i.hash_cyclefold(&sponge); augmented_f_circuit = AugmentedFCircuit::, MU, NU> { @@ -1264,9 +1260,7 @@ mod tests { cf_cmT: None, }; } else { - let mut transcript_p: PoseidonSponge = - PoseidonSponge::::new(&poseidon_config.clone()); - transcript_p.absorb(&pp_hash); + let mut transcript_p: PoseidonSponge = sponge.clone(); let (rho, nimfs_proof); (nimfs_proof, U_i1, W_i1, rho) = NIMFS::>::prove( &mut transcript_p, @@ -1280,7 +1274,7 @@ mod tests { // sanity check: check the folded instance relation ccs.check_relation(&W_i1, &U_i1)?; - u_i1_x = U_i1.hash(&sponge, pp_hash, iFr + Fr::one(), &z_0, &z_i1); + u_i1_x = U_i1.hash(&sponge, iFr + Fr::one(), &z_0, &z_i1); // CycleFold part: let cf_config = HyperNovaCycleFoldConfig:: { @@ -1311,7 +1305,6 @@ mod tests { &mut transcript_p, &cf_r1cs, &cf_pedersen_params, - pp_hash, cf_W_i, cf_U_i.clone(), vec![cf_w_i], @@ -1320,7 +1313,7 @@ mod tests { // hash the CycleFold folded instance, which is used as the 2nd public input in the // AugmentedFCircuit - cf_u_i1_x = cf_U_i1.hash_cyclefold(&sponge, pp_hash); + cf_u_i1_x = cf_U_i1.hash_cyclefold(&sponge); augmented_f_circuit = AugmentedFCircuit::, MU, NU> { @@ -1375,8 +1368,8 @@ mod tests { assert_eq!(u_i.x, r1cs_x_i1); assert_eq!(u_i.x[0], u_i1_x); assert_eq!(u_i.x[1], cf_u_i1_x); - let expected_u_i1_x = U_i1.hash(&sponge, pp_hash, iFr + Fr::one(), &z_0, &z_i1); - let expected_cf_U_i1_x = cf_U_i.hash_cyclefold(&sponge, pp_hash); + let expected_u_i1_x = U_i1.hash(&sponge, iFr + Fr::one(), &z_0, &z_i1); + let expected_cf_U_i1_x = cf_U_i.hash_cyclefold(&sponge); // 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/decider_eth_circuit.rs b/folding-schemes/src/folding/hypernova/decider_eth_circuit.rs index 099e2146..ec76815e 100644 --- a/folding-schemes/src/folding/hypernova/decider_eth_circuit.rs +++ b/folding-schemes/src/folding/hypernova/decider_eth_circuit.rs @@ -1,16 +1,13 @@ /// This file implements the onchain (Ethereum's EVM) decider circuit. For non-ethereum use cases, /// other more efficient approaches can be used. -use ark_crypto_primitives::sponge::{ - constraints::CryptographicSpongeVar, - poseidon::{constraints::PoseidonSpongeVar, PoseidonSponge}, - CryptographicSponge, -}; +use ark_crypto_primitives::sponge::poseidon::{constraints::PoseidonSpongeVar, PoseidonSponge}; use ark_ff::PrimeField; use ark_r1cs_std::{ alloc::{AllocVar, AllocationMode}, boolean::Boolean, eq::EqGadget, fields::fp::FpVar, + R1CSVar, }; use ark_relations::r1cs::{Namespace, SynthesisError}; use ark_std::{borrow::Borrow, log2, marker::PhantomData}; @@ -20,7 +17,6 @@ use super::{ nimfs::{NIMFSProof, NIMFS}, HyperNova, Witness, CCCS, LCCCS, }; -use crate::folding::circuits::{decider::on_chain::GenericOnchainDeciderCircuit, CF1}; use crate::folding::traits::{WitnessOps, WitnessVarOps}; use crate::frontend::FCircuit; use crate::utils::gadgets::{eval_mle, MatrixGadget}; @@ -37,6 +33,10 @@ use crate::{ folding::circuits::decider::DeciderEnabledNIFS, Curve, }; +use crate::{ + folding::circuits::{decider::on_chain::GenericOnchainDeciderCircuit, CF1}, + transcript::Transcript, +}; impl ArithGadget>, LCCCSVar> for CCSMatricesVar> { type Evaluation = Vec>>; @@ -125,8 +125,7 @@ impl< fn try_from(hn: HyperNova) -> Result { // compute the U_{i+1}, W_{i+1}, by folding the last running & incoming instances - let mut transcript = PoseidonSponge::::new(&hn.poseidon_config); - transcript.absorb(&hn.pp_hash); + let mut transcript = PoseidonSponge::new_with_pp_hash(&hn.poseidon_config, hn.pp_hash); let (nimfs_proof, U_i1, W_i1, rho) = NIMFS::>::prove( &mut transcript, &hn.ccs, @@ -186,15 +185,13 @@ impl DeciderEnabledNIFS, CCCS, Witness, fn fold_field_elements_gadget( arith: &CCS>, transcript: &mut PoseidonSpongeVar>, - pp_hash: FpVar>, U: LCCCSVar, _U_vec: Vec>>, u: CCCSVar, proof: Self::Proof, randomness: Self::Randomness, ) -> Result, SynthesisError> { - let cs = transcript.cs(); - transcript.absorb(&pp_hash)?; + let cs = U.u.cs(); let nimfs_proof = NIMFSProofVar::::new_witness(cs.clone(), || Ok(proof))?; let rho = FpVar::>::new_input(cs.clone(), || Ok(randomness))?; let (computed_U_i1, rho_bits) = NIMFSGadget::::verify( diff --git a/folding-schemes/src/folding/hypernova/mod.rs b/folding-schemes/src/folding/hypernova/mod.rs index fcbf5849..587a5b74 100644 --- a/folding-schemes/src/folding/hypernova/mod.rs +++ b/folding-schemes/src/folding/hypernova/mod.rs @@ -1,8 +1,5 @@ /// Implements the scheme described in [HyperNova](https://eprint.iacr.org/2023/573.pdf) -use ark_crypto_primitives::sponge::{ - poseidon::{PoseidonConfig, PoseidonSponge}, - CryptographicSponge, -}; +use ark_crypto_primitives::sponge::poseidon::{PoseidonConfig, PoseidonSponge}; use ark_ff::{BigInteger, PrimeField}; use ark_r1cs_std::{alloc::AllocVar, boolean::Boolean, R1CSVar}; use ark_relations::r1cs::{ @@ -25,7 +22,6 @@ use decider_eth_circuit::WitnessVar; use lcccs::LCCCS; use nimfs::NIMFS; -use crate::commitment::CommitmentScheme; use crate::constants::NOVA_N_BITS_RO; use crate::folding::{ circuits::{ @@ -49,6 +45,7 @@ use crate::{ }, Curve, Error, FoldingScheme, MultiFolding, }; +use crate::{commitment::CommitmentScheme, transcript::Transcript}; /// Configuration for HyperNova's CycleFold circuit pub struct HyperNovaCycleFoldConfig { @@ -369,17 +366,19 @@ where let (_, cf_U_i): (CycleFoldWitness, CycleFoldCommittedInstance) = self.cf_r1cs.dummy_witness_instance(); - let sponge = PoseidonSponge::::new(&self.poseidon_config); + let sponge = PoseidonSponge::::new_with_pp_hash( + &self.poseidon_config, + self.pp_hash, + ); u_i.x = vec![ U_i.hash( &sponge, - self.pp_hash, C1::ScalarField::zero(), // i &self.z_0, &state, ), - cf_U_i.hash_cyclefold(&sponge, self.pp_hash), + cf_U_i.hash_cyclefold(&sponge), ]; let us = vec![u_i.clone(); NU - 1]; @@ -571,8 +570,12 @@ where return Err(Error::CantBeZero("mu,nu".to_string())); } + // compute the public params hash + let pp_hash = vp.pp_hash()?; + // `sponge` is for digest computation. - let sponge = PoseidonSponge::::new(&pp.poseidon_config); + let sponge = + PoseidonSponge::::new_with_pp_hash(&pp.poseidon_config, pp_hash); // prepare the HyperNova's AugmentedFCircuit and CycleFold's circuits and obtain its CCS // and R1CS respectively @@ -586,9 +589,6 @@ where let cf_circuit = CycleFoldCircuit::<_, HyperNovaCycleFoldConfig>::default(); 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); @@ -597,8 +597,8 @@ where let (cf_W_dummy, cf_U_dummy): (CycleFoldWitness, CycleFoldCommittedInstance) = cf_r1cs.dummy_witness_instance(); u_dummy.x = vec![ - U_dummy.hash(&sponge, pp_hash, C1::ScalarField::zero(), &z_0, &z_0), - cf_U_dummy.hash_cyclefold(&sponge, pp_hash), + U_dummy.hash(&sponge, C1::ScalarField::zero(), &z_0, &z_0), + cf_U_dummy.hash_cyclefold(&sponge), ]; // W_dummy=W_0 is a 'dummy witness', all zeroes, but with the size corresponding to the @@ -746,8 +746,10 @@ where }; } else { let mut transcript_p: PoseidonSponge = - PoseidonSponge::::new(&self.poseidon_config); - transcript_p.absorb(&self.pp_hash); + PoseidonSponge::::new_with_pp_hash( + &self.poseidon_config, + self.pp_hash, + ); let (all_Us, all_us, all_Ws, all_ws) = ( [&[self.U_i.clone()][..], &Us].concat(), @@ -786,7 +788,6 @@ where &mut transcript_p, &self.cf_r1cs, &self.cf_cs_pp, - self.pp_hash, self.cf_W_i.clone(), self.cf_U_i.clone(), vec![cf_w_i], @@ -953,22 +954,21 @@ where return Ok(()); } // `sponge` is for digest computation. - let sponge = PoseidonSponge::::new(&vp.poseidon_config); + let sponge = + PoseidonSponge::::new_with_pp_hash(&vp.poseidon_config, vp.pp_hash()?); if u_i.x.len() != 2 || U_i.x.len() != 2 { 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(&sponge, pp_hash, num_steps, &z_0, &z_i); + let expected_u_i_x = U_i.hash(&sponge, num_steps, &z_0, &z_i); 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(&sponge, pp_hash); + let expected_cf_u_i_x = cf_U_i.hash_cyclefold(&sponge); if expected_cf_u_i_x != u_i.x[1] { return Err(Error::IVCVerificationFail); } diff --git a/folding-schemes/src/folding/hypernova/nimfs.rs b/folding-schemes/src/folding/hypernova/nimfs.rs index c126d18d..4680fb38 100644 --- a/folding-schemes/src/folding/hypernova/nimfs.rs +++ b/folding-schemes/src/folding/hypernova/nimfs.rs @@ -396,18 +396,16 @@ impl> NIMFS { #[cfg(test)] pub mod tests { use super::*; + use ark_crypto_primitives::sponge::poseidon::PoseidonSponge; + use ark_pallas::{Fr, Projective}; + use ark_std::{test_rng, UniformRand}; + use crate::arith::{ ccs::tests::{get_test_ccs, get_test_z}, Arith, }; - use crate::transcript::poseidon::poseidon_canonical_config; - use ark_crypto_primitives::sponge::poseidon::PoseidonSponge; - use ark_crypto_primitives::sponge::CryptographicSponge; - use ark_std::test_rng; - use ark_std::UniformRand; - use crate::commitment::{pedersen::Pedersen, CommitmentScheme}; - use ark_pallas::{Fr, Projective}; + use crate::transcript::poseidon::poseidon_canonical_config; #[test] fn test_fold() -> Result<(), Error> { @@ -481,8 +479,10 @@ pub mod tests { // Prover's transcript let poseidon_config = poseidon_canonical_config::(); - let mut transcript_p: PoseidonSponge = PoseidonSponge::::new(&poseidon_config); - transcript_p.absorb(&Fr::from_le_bytes_mod_order(b"init init")); + let pp_hash = Fr::from_le_bytes_mod_order(b"init init"); + let mut transcript_p = PoseidonSponge::new_with_pp_hash(&poseidon_config, pp_hash); + // Verifier's transcript + let mut transcript_v = transcript_p.clone(); // Run the prover side of the multifolding let (proof, folded_lcccs, folded_witness, _) = @@ -495,10 +495,6 @@ pub mod tests { &[w2], )?; - // Verifier's transcript - let mut transcript_v: PoseidonSponge = PoseidonSponge::::new(&poseidon_config); - transcript_v.absorb(&Fr::from_le_bytes_mod_order(b"init init")); - // Run the verifier side of the multifolding let folded_lcccs_v = NIMFS::>::verify( &mut transcript_v, @@ -529,12 +525,10 @@ pub mod tests { ccs.to_lcccs::<_, _, Pedersen, false>(&mut rng, &pedersen_params, &z_1)?; let poseidon_config = poseidon_canonical_config::(); - - let mut transcript_p: PoseidonSponge = PoseidonSponge::::new(&poseidon_config); - transcript_p.absorb(&Fr::from_le_bytes_mod_order(b"init init")); - - let mut transcript_v: PoseidonSponge = PoseidonSponge::::new(&poseidon_config); - transcript_v.absorb(&Fr::from_le_bytes_mod_order(b"init init")); + let pp_hash = Fr::from_le_bytes_mod_order(b"init init"); + let mut transcript_p = PoseidonSponge::new_with_pp_hash(&poseidon_config, pp_hash); + // Verifier's transcript + let mut transcript_v = transcript_p.clone(); let n: usize = 10; for i in 3..n { @@ -619,8 +613,10 @@ pub mod tests { // Prover's transcript let poseidon_config = poseidon_canonical_config::(); - let mut transcript_p: PoseidonSponge = PoseidonSponge::::new(&poseidon_config); - transcript_p.absorb(&Fr::from_le_bytes_mod_order(b"init init")); + let pp_hash = Fr::from_le_bytes_mod_order(b"init init"); + let mut transcript_p = PoseidonSponge::new_with_pp_hash(&poseidon_config, pp_hash); + // Verifier's transcript + let mut transcript_v = transcript_p.clone(); // Run the prover side of the multifolding let (proof, folded_lcccs, folded_witness, _) = @@ -633,10 +629,6 @@ pub mod tests { &w_cccs, )?; - // Verifier's transcript - let mut transcript_v: PoseidonSponge = PoseidonSponge::::new(&poseidon_config); - transcript_v.absorb(&Fr::from_le_bytes_mod_order(b"init init")); - // Run the verifier side of the multifolding let folded_lcccs_v = NIMFS::>::verify( &mut transcript_v, @@ -663,13 +655,11 @@ pub mod tests { let (pedersen_params, _) = Pedersen::::setup(&mut rng, ccs.n - ccs.l - 1)?; let poseidon_config = poseidon_canonical_config::(); + let pp_hash = Fr::from_le_bytes_mod_order(b"init init"); // Prover's transcript - let mut transcript_p: PoseidonSponge = PoseidonSponge::::new(&poseidon_config); - transcript_p.absorb(&Fr::from_le_bytes_mod_order(b"init init")); - + let mut transcript_p = PoseidonSponge::new_with_pp_hash(&poseidon_config, pp_hash); // Verifier's transcript - let mut transcript_v: PoseidonSponge = PoseidonSponge::::new(&poseidon_config); - transcript_v.absorb(&Fr::from_le_bytes_mod_order(b"init init")); + let mut transcript_v = transcript_p.clone(); let n_steps = 3; diff --git a/folding-schemes/src/folding/nova/circuits.rs b/folding-schemes/src/folding/nova/circuits.rs index d7715837..250ba28d 100644 --- a/folding-schemes/src/folding/nova/circuits.rs +++ b/folding-schemes/src/folding/nova/circuits.rs @@ -1,7 +1,6 @@ /// contains [Nova](https://eprint.iacr.org/2021/370.pdf) related circuits -use ark_crypto_primitives::sponge::{ - constraints::CryptographicSpongeVar, - poseidon::{constraints::PoseidonSpongeVar, PoseidonConfig, PoseidonSponge}, +use ark_crypto_primitives::sponge::poseidon::{ + constraints::PoseidonSpongeVar, PoseidonConfig, PoseidonSponge, }; use ark_r1cs_std::{ alloc::AllocVar, @@ -19,17 +18,20 @@ use super::{ }, CommittedInstance, NovaCycleFoldConfig, }; -use crate::folding::circuits::{ - cyclefold::{ - CycleFoldAugmentationGadget, CycleFoldCommittedInstance, CycleFoldCommittedInstanceVar, - CycleFoldConfig, - }, - nonnative::affine::NonNativeAffineVar, - CF1, -}; use crate::folding::traits::{CommittedInstanceVarOps, Dummy}; use crate::frontend::FCircuit; use crate::Curve; +use crate::{ + folding::circuits::{ + cyclefold::{ + CycleFoldAugmentationGadget, CycleFoldCommittedInstance, CycleFoldCommittedInstanceVar, + CycleFoldConfig, + }, + nonnative::affine::NonNativeAffineVar, + CF1, + }, + transcript::TranscriptVar, +}; /// `AugmentedFCircuit` enhances the original step function `F`, so that it can /// be used in recursive arguments such as IVC. @@ -149,7 +151,10 @@ where C2::Var::new_witness(cs.clone(), || Ok(self.cf2_cmT.unwrap_or_else(C2::zero)))?; // `sponge` is for digest computation. - let sponge = PoseidonSpongeVar::::new(cs.clone(), &self.poseidon_config); + let sponge = PoseidonSpongeVar::::new_with_pp_hash( + &self.poseidon_config, + &pp_hash, + )?; // `transcript` is for challenge generation. let mut transcript = sponge.clone(); @@ -158,9 +163,9 @@ 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(&sponge, &pp_hash, &i, &z_0, &z_i)?; + let (u_i_x, U_i_vec) = U_i.clone().hash(&sponge, &i, &z_0, &z_i)?; // u_i.x[1] = H(cf_U_i) - let (cf_u_i_x, _) = cf_U_i.clone().hash(&sponge, pp_hash.clone())?; + let (cf_u_i_x, _) = cf_U_i.clone().hash(&sponge)?; // P.2. Construct u_i let u_i = CommittedInstanceVar { @@ -187,7 +192,6 @@ where PoseidonSpongeVar, >::verify( &mut transcript, - pp_hash.clone(), U_i.clone(), U_i_vec, u_i.clone(), @@ -206,16 +210,11 @@ where // Base case: u_{i+1}.x[0] == H((i+1, z_0, z_{i+1}, U_{\bot}) // 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( - &sponge, - &pp_hash, - &(i + FpVar::>::one()), - &z_0, - &z_i1, - )?; + let (u_i1_x, _) = + U_i1.clone() + .hash(&sponge, &(i + FpVar::>::one()), &z_0, &z_i1)?; let (u_i1_x_base, _) = CommittedInstanceVar::new_constant(cs.clone(), u_dummy)?.hash( &sponge, - &pp_hash, &FpVar::>::one(), &z_0, &z_i1, @@ -257,7 +256,6 @@ where // C.3. nifs.verify, obtains cf_U_{i+1} by folding cf1_u_i and cf2_u_i into cf_U. let cf_U_i1 = CycleFoldAugmentationGadget::fold_gadget( &mut transcript, - &pp_hash, cf_U_i, vec![cf1_u_i, cf2_u_i], vec![cf1_cmT, cf2_cmT], @@ -267,10 +265,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(&sponge, pp_hash.clone())?; + let (cf_u_i1_x, _) = cf_U_i1.clone().hash(&sponge)?; let (cf_u_i1_x_base, _) = CycleFoldCommittedInstanceVar::::new_constant(cs.clone(), cf_u_dummy)? - .hash(&sponge, pp_hash)?; + .hash(&sponge)?; let cf_x = is_basecase.select(&cf_u_i1_x_base, &cf_u_i1_x)?; // This line "converts" `cf_x` from a witness to a public input. // Instead of directly modifying the constraint system, we explicitly @@ -302,9 +300,7 @@ where pub mod tests { use super::*; use ark_bn254::{Fr, G1Projective as Projective}; - use ark_crypto_primitives::sponge::{ - constraints::AbsorbGadget, poseidon::PoseidonSponge, CryptographicSponge, - }; + use ark_crypto_primitives::sponge::{constraints::AbsorbGadget, poseidon::PoseidonSponge}; use ark_ff::{BigInteger, PrimeField}; use ark_r1cs_std::prelude::Boolean; @@ -312,7 +308,7 @@ pub mod tests { use ark_std::UniformRand; use crate::folding::nova::nifs::nova::ChallengeGadget; - use crate::transcript::poseidon::poseidon_canonical_config; + use crate::transcript::{poseidon::poseidon_canonical_config, Transcript}; use crate::Error; // checks that the gadget and native implementations of the challenge computation match @@ -320,7 +316,8 @@ pub mod tests { fn test_challenge_gadget() -> Result<(), Error> { let mut rng = ark_std::test_rng(); let poseidon_config = poseidon_canonical_config::(); - let mut transcript = PoseidonSponge::::new(&poseidon_config); + let pp_hash = Fr::from(42u32); // only for testing + let mut transcript = PoseidonSponge::::new_with_pp_hash(&poseidon_config, pp_hash); let u_i = CommittedInstance:: { cmE: Projective::rand(&mut rng), @@ -336,13 +333,10 @@ 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( &mut transcript, - pp_hash, &U_i, &u_i, Some(&cmT), @@ -356,13 +350,13 @@ pub mod tests { let U_iVar = CommittedInstanceVar::::new_witness(cs.clone(), || Ok(U_i.clone()))?; let cmTVar = NonNativeAffineVar::::new_witness(cs.clone(), || Ok(cmT))?; - let mut transcriptVar = PoseidonSpongeVar::::new(cs.clone(), &poseidon_config); + let mut transcriptVar = + PoseidonSpongeVar::::new_with_pp_hash(&poseidon_config, &pp_hashVar)?; // compute the challenge in-circuit let r_bitsVar = ChallengeGadget::>::get_challenge_gadget( &mut transcriptVar, - pp_hashVar, U_iVar.to_sponge_field_elements()?, u_iVar, Some(cmTVar), diff --git a/folding-schemes/src/folding/nova/decider_circuits.rs b/folding-schemes/src/folding/nova/decider_circuits.rs index db9a686d..f7ec1014 100644 --- a/folding-schemes/src/folding/nova/decider_circuits.rs +++ b/folding-schemes/src/folding/nova/decider_circuits.rs @@ -2,7 +2,7 @@ /// DeciderEthCircuit. /// More details can be found at the documentation page: /// https://privacy-scaling-explorations.github.io/sonobe-docs/design/nova-decider-offchain.html -use ark_crypto_primitives::sponge::{poseidon::PoseidonSponge, CryptographicSponge}; +use ark_crypto_primitives::sponge::poseidon::PoseidonSponge; use ark_ff::{BigInteger, PrimeField}; use ark_r1cs_std::fields::fp::FpVar; use core::marker::PhantomData; @@ -12,17 +12,23 @@ use super::{ nifs::{nova::NIFS, NIFSTrait}, CommittedInstance, Nova, Witness, }; -use crate::folding::{circuits::CF1, traits::WitnessOps}; -use crate::frontend::FCircuit; use crate::{ arith::r1cs::{circuits::R1CSMatricesVar, R1CS}, - folding::circuits::decider::{ - off_chain::{GenericOffchainDeciderCircuit1, GenericOffchainDeciderCircuit2}, - EvalGadget, KZGChallengesGadget, + commitment::CommitmentScheme, + folding::{ + circuits::{ + decider::{ + off_chain::{GenericOffchainDeciderCircuit1, GenericOffchainDeciderCircuit2}, + EvalGadget, KZGChallengesGadget, + }, + CF1, + }, + traits::WitnessOps, }, + frontend::FCircuit, + transcript::{poseidon::poseidon_canonical_config, Transcript}, + Curve, Error, }; -use crate::{commitment::CommitmentScheme, transcript::poseidon::poseidon_canonical_config}; -use crate::{Curve, Error}; /// Circuit that implements part of the in-circuit checks needed for the offchain verification over /// the Curve2's BaseField (=Curve1's ScalarField). @@ -50,7 +56,7 @@ impl< type Error = Error; fn try_from(nova: Nova) -> Result { - let mut transcript = PoseidonSponge::::new(&nova.poseidon_config); + let mut transcript = PoseidonSponge::new_with_pp_hash(&nova.poseidon_config, nova.pp_hash); // pp_hash is absorbed to transcript at the NIFS::prove call // compute the U_{i+1}, W_{i+1} @@ -58,7 +64,6 @@ impl< &nova.cs_pp, &nova.r1cs.clone(), &mut transcript, - nova.pp_hash, &nova.W_i, &nova.U_i, &nova.w_i, @@ -120,10 +125,10 @@ impl< // compute the Commitment Scheme challenges of the CycleFold instance commitments, used as // inputs in the circuit let poseidon_config = poseidon_canonical_config::(); - let mut transcript = PoseidonSponge::::new(&poseidon_config); let pp_hash_Fq = C2::ScalarField::from_le_bytes_mod_order(&nova.pp_hash.into_bigint().to_bytes_le()); - transcript.absorb(&pp_hash_Fq); + let mut transcript = + PoseidonSponge::::new_with_pp_hash(&poseidon_config, pp_hash_Fq); // compute the KZG challenges used as inputs in the circuit let kzg_challenges = diff --git a/folding-schemes/src/folding/nova/decider_eth_circuit.rs b/folding-schemes/src/folding/nova/decider_eth_circuit.rs index e3f7fdba..f5098758 100644 --- a/folding-schemes/src/folding/nova/decider_eth_circuit.rs +++ b/folding-schemes/src/folding/nova/decider_eth_circuit.rs @@ -2,15 +2,12 @@ /// other more efficient approaches can be used. /// More details can be found at the documentation page: /// https://privacy-scaling-explorations.github.io/sonobe-docs/design/nova-decider-onchain.html -use ark_crypto_primitives::sponge::{ - constraints::CryptographicSpongeVar, - poseidon::{constraints::PoseidonSpongeVar, PoseidonSponge}, - CryptographicSponge, -}; +use ark_crypto_primitives::sponge::poseidon::{constraints::PoseidonSpongeVar, PoseidonSponge}; use ark_ff::{BigInteger, PrimeField}; use ark_r1cs_std::{ alloc::{AllocVar, AllocationMode}, fields::fp::FpVar, + R1CSVar, }; use ark_relations::r1cs::{Namespace, SynthesisError}; use ark_std::{borrow::Borrow, marker::PhantomData}; @@ -20,19 +17,24 @@ use super::{ nifs::{nova::NIFS, NIFSGadgetTrait, NIFSTrait}, CommittedInstance, Nova, Witness, }; -use crate::commitment::{pedersen::Params as PedersenParams, CommitmentScheme}; -use crate::folding::{ - circuits::{ - decider::on_chain::GenericOnchainDeciderCircuit, nonnative::affine::NonNativeAffineVar, CF1, - }, - traits::{WitnessOps, WitnessVarOps}, -}; -use crate::frontend::FCircuit; use crate::{ arith::r1cs::{circuits::R1CSMatricesVar, R1CS}, - folding::circuits::decider::{DeciderEnabledNIFS, EvalGadget, KZGChallengesGadget}, + commitment::{pedersen::Params as PedersenParams, CommitmentScheme}, + folding::{ + circuits::{ + decider::{ + on_chain::GenericOnchainDeciderCircuit, DeciderEnabledNIFS, EvalGadget, + KZGChallengesGadget, + }, + nonnative::affine::NonNativeAffineVar, + CF1, + }, + traits::{WitnessOps, WitnessVarOps}, + }, + frontend::FCircuit, + transcript::Transcript, + Curve, Error, }; -use crate::{Curve, Error}; /// In-circuit representation of the Witness associated to the CommittedInstance. #[derive(Debug, Clone)] @@ -98,14 +100,13 @@ impl< type Error = Error; fn try_from(nova: Nova) -> Result { - let mut transcript = PoseidonSponge::::new(&nova.poseidon_config); + let mut transcript = PoseidonSponge::new_with_pp_hash(&nova.poseidon_config, nova.pp_hash); // compute the U_{i+1}, W_{i+1} let (W_i1, U_i1, cmT, r_bits) = NIFS::, H>::prove( &nova.cs_pp, &nova.r1cs.clone(), &mut transcript, - nova.pp_hash, &nova.W_i, &nova.U_i, &nova.w_i, @@ -165,16 +166,15 @@ impl fn fold_field_elements_gadget( _arith: &R1CS>, transcript: &mut PoseidonSpongeVar>, - pp_hash: FpVar>, U: CommittedInstanceVar, U_vec: Vec>>, u: CommittedInstanceVar, proof: C, _randomness: CF1, ) -> Result, SynthesisError> { - let cs = transcript.cs(); + let cs = U.u.cs(); let cmT = NonNativeAffineVar::new_input(cs.clone(), || Ok(proof))?; - let (new_U, _) = NIFSGadget::verify(transcript, pp_hash, U, U_vec, u, Some(cmT))?; + let (new_U, _) = NIFSGadget::verify(transcript, U, U_vec, u, Some(cmT))?; Ok(new_U) } diff --git a/folding-schemes/src/folding/nova/mod.rs b/folding-schemes/src/folding/nova/mod.rs index 1958488c..a5be8f26 100644 --- a/folding-schemes/src/folding/nova/mod.rs +++ b/folding-schemes/src/folding/nova/mod.rs @@ -6,7 +6,7 @@ /// - IVC and the Decider (offchain Decider & onchain Decider) implementations for Nova use ark_crypto_primitives::sponge::{ poseidon::{PoseidonConfig, PoseidonSponge}, - Absorb, CryptographicSponge, + Absorb, }; use ark_ff::{BigInteger, PrimeField}; use ark_r1cs_std::{alloc::AllocVar, prelude::Boolean, R1CSVar}; @@ -14,25 +14,30 @@ use ark_relations::r1cs::{ ConstraintSynthesizer, ConstraintSystem, ConstraintSystemRef, SynthesisError, }; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Valid}; -use ark_std::fmt::Debug; -use ark_std::rand::RngCore; -use ark_std::{One, UniformRand, Zero}; +use ark_std::{fmt::Debug, rand::RngCore, One, UniformRand, Zero}; -use crate::folding::circuits::cyclefold::{ - CycleFoldAugmentationGadget, CycleFoldCommittedInstance, CycleFoldConfig, CycleFoldWitness, -}; -use crate::folding::{circuits::CF1, traits::Dummy}; -use crate::frontend::FCircuit; -use crate::transcript::poseidon::poseidon_canonical_config; -use crate::utils::vec::is_zero_vec; -use crate::FoldingScheme; use crate::{ - arith::r1cs::{extract_r1cs, extract_w_x, R1CS}, + arith::{ + r1cs::{extract_r1cs, extract_w_x, R1CS}, + Arith, + }, + commitment::CommitmentScheme, constants::NOVA_N_BITS_RO, - utils::pp_hash, + folding::{ + circuits::{ + cyclefold::{ + CycleFoldAugmentationGadget, CycleFoldCommittedInstance, CycleFoldConfig, + CycleFoldWitness, + }, + CF1, + }, + traits::Dummy, + }, + frontend::FCircuit, + transcript::{poseidon::poseidon_canonical_config, Transcript}, + utils::{pp_hash, vec::is_zero_vec}, + Curve, Error, FoldingScheme, }; -use crate::{arith::Arith, commitment::CommitmentScheme}; -use crate::{Curve, Error}; use decider_eth_circuit::WitnessVar; pub mod circuits; @@ -668,7 +673,10 @@ where } } // `sponge` is for digest computation. - let sponge = PoseidonSponge::::new(&self.poseidon_config); + let sponge = PoseidonSponge::::new_with_pp_hash( + &self.poseidon_config, + self.pp_hash, + ); // `transcript` is for challenge generation. let mut transcript = sponge.clone(); @@ -714,7 +722,6 @@ where &self.cs_pp, &self.r1cs, &mut transcript, - self.pp_hash, &self.W_i, &self.U_i, &self.w_i, @@ -773,7 +780,6 @@ where &mut transcript, &self.cf_r1cs, &self.cf_cs_pp, - self.pp_hash, self.cf_W_i.clone(), self.cf_U_i.clone(), vec![cfW_w_i, cfE_w_i], @@ -929,7 +935,8 @@ where cf_U_i, } = ivc_proof; - let sponge = PoseidonSponge::::new(&vp.poseidon_config); + let sponge = + PoseidonSponge::::new_with_pp_hash(&vp.poseidon_config, vp.pp_hash()?); if num_steps == C1::ScalarField::zero() { if z_0 != z_i { @@ -942,16 +949,14 @@ 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(&sponge, pp_hash, num_steps, &z_0, &z_i); + let expected_u_i_x = U_i.hash(&sponge, num_steps, &z_0, &z_i); 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(&sponge, pp_hash); + let expected_cf_u_i_x = cf_U_i.hash_cyclefold(&sponge); if expected_cf_u_i_x != u_i.x[1] { return Err(Error::IVCVerificationFail); } diff --git a/folding-schemes/src/folding/nova/nifs/mod.rs b/folding-schemes/src/folding/nova/nifs/mod.rs index 9b9fe3b1..b65744f6 100644 --- a/folding-schemes/src/folding/nova/nifs/mod.rs +++ b/folding-schemes/src/folding/nova/nifs/mod.rs @@ -68,7 +68,6 @@ pub trait NIFSTrait< cs_prover_params: &CS::ProverParams, r1cs: &R1CS, transcript: &mut T, - pp_hash: C::ScalarField, W_i: &Self::Witness, // running witness U_i: &Self::CommittedInstance, // running committed instance w_i: &Self::Witness, // incoming witness @@ -87,7 +86,6 @@ pub trait NIFSTrait< /// bits, so that it can be reused in other methods. fn verify( transcript: &mut T, - pp_hash: C::ScalarField, U_i: &Self::CommittedInstance, u_i: &Self::CommittedInstance, proof: &Self::Proof, @@ -113,7 +111,6 @@ pub trait NIFSGadgetTrait>, U_i: Self::CommittedInstanceVar, // U_i_vec is passed to reuse the already computed U_i_vec from previous methods U_i_vec: Vec>>, @@ -128,11 +125,10 @@ pub trait NIFSGadgetTrait::setup(&mut rng, r1cs.A.n_cols)?; let poseidon_config = poseidon_canonical_config::(); - let mut transcript_p = PoseidonSponge::::new(&poseidon_config); - let mut transcript_v = PoseidonSponge::::new(&poseidon_config); let pp_hash = Fr::rand(&mut rng); + let mut transcript_p = PoseidonSponge::::new_with_pp_hash(&poseidon_config, pp_hash); + let mut transcript_v = transcript_p.clone(); // prepare the running instance let z = get_test_z(3); @@ -180,7 +176,6 @@ pub mod tests { &pedersen_params, &r1cs, &mut transcript_p, - pp_hash, &W_i, &U_i, &w_i, @@ -188,8 +183,7 @@ pub mod tests { )?; // NIFS.V - let (folded_committed_instance, _) = - N::verify(&mut transcript_v, pp_hash, &U_i, &u_i, &proof)?; + let (folded_committed_instance, _) = N::verify(&mut transcript_v, &U_i, &u_i, &proof)?; // set running_instance for next loop iteration W_i = folded_witness; @@ -221,13 +215,14 @@ pub mod tests { let (U_i, u_i) = (ci[0].clone(), ci[1].clone()); let pp_hash = Fr::rand(&mut rng); let poseidon_config = poseidon_canonical_config::(); - let mut transcript = PoseidonSponge::::new(&poseidon_config); - let (ci3, _) = N::verify(&mut transcript, pp_hash, &U_i, &u_i, &proof)?; + let mut transcript = PoseidonSponge::::new_with_pp_hash(&poseidon_config, pp_hash); + let (ci3, _) = N::verify(&mut transcript, &U_i, &u_i, &proof)?; let cs = ConstraintSystem::::new_ref(); - let mut transcriptVar = PoseidonSpongeVar::::new(cs.clone(), &poseidon_config); let pp_hashVar = FpVar::::new_witness(cs.clone(), || Ok(pp_hash))?; + let mut transcriptVar = + PoseidonSpongeVar::::new_with_pp_hash(&poseidon_config, &pp_hashVar)?; let ci1Var = NG::CommittedInstanceVar::new_witness(cs.clone(), || Ok(U_i.clone()))?; let ci2Var = NG::CommittedInstanceVar::new_witness(cs.clone(), || Ok(u_i.clone()))?; let proofVar = NG::ProofVar::new_witness(cs.clone(), || Ok(proof))?; @@ -235,7 +230,6 @@ pub mod tests { let ci1Var_vec = ci1Var.to_sponge_field_elements()?; let (out, _) = NG::verify( &mut transcriptVar, - pp_hashVar, ci1Var.clone(), ci1Var_vec, ci2Var.clone(), @@ -293,15 +287,15 @@ pub mod tests { N::CommittedInstance: CommittedInstanceOps, { let poseidon_config = poseidon_canonical_config::(); - let sponge = PoseidonSponge::::new(&poseidon_config); let pp_hash = Fr::from(42u32); // only for test + let sponge = PoseidonSponge::::new_with_pp_hash(&poseidon_config, pp_hash); let i = Fr::from(3_u32); let z_0 = vec![Fr::from(3_u32)]; let z_i = vec![Fr::from(3_u32)]; // compute the CommittedInstance hash natively - let h = ci.hash(&sponge, pp_hash, i, &z_0, &z_i); + let h = ci.hash(&sponge, i, &z_0, &z_i); let cs = ConstraintSystem::::new_ref(); @@ -311,10 +305,10 @@ pub mod tests { let z_iVar = Vec::>::new_witness(cs.clone(), || Ok(z_i.clone()))?; let ciVar = NG::CommittedInstanceVar::new_witness(cs.clone(), || Ok(ci.clone()))?; - let sponge = PoseidonSpongeVar::::new(cs.clone(), &poseidon_config); + let sponge = PoseidonSpongeVar::::new_with_pp_hash(&poseidon_config, &pp_hashVar)?; // compute the CommittedInstance hash in-circuit - let (hVar, _) = ciVar.hash(&sponge, &pp_hashVar, &iVar, &z_0Var, &z_iVar)?; + let (hVar, _) = ciVar.hash(&sponge, &iVar, &z_0Var, &z_iVar)?; assert!(cs.is_satisfied()?); // check that the natively computed and in-circuit computed hashes match diff --git a/folding-schemes/src/folding/nova/nifs/mova.rs b/folding-schemes/src/folding/nova/nifs/mova.rs index da3bdf7a..2ef209b0 100644 --- a/folding-schemes/src/folding/nova/nifs/mova.rs +++ b/folding-schemes/src/folding/nova/nifs/mova.rs @@ -200,7 +200,6 @@ impl, T: Transcript, const _cs_prover_params: &CS::ProverParams, // not used in Mova since we don't commit to T r1cs: &R1CS, transcript: &mut T, - pp_hash: C::ScalarField, W_i: &Witness, U_i: &CommittedInstance, w_i: &Witness, @@ -214,7 +213,6 @@ impl, T: Transcript, const ), Error, > { - transcript.absorb(&pp_hash); // Protocol 5 is pre-processing transcript.absorb(U_i); transcript.absorb(u_i); @@ -282,12 +280,10 @@ impl, T: Transcript, const /// returns the folded committed instance fn verify( transcript: &mut T, - pp_hash: C::ScalarField, U_i: &CommittedInstance, u_i: &CommittedInstance, proof: &Proof, ) -> Result<(Self::CommittedInstance, Vec), Error> { - transcript.absorb(&pp_hash); transcript.absorb(U_i); transcript.absorb(u_i); let rE_prime = PointVsLine::::verify( diff --git a/folding-schemes/src/folding/nova/nifs/nova.rs b/folding-schemes/src/folding/nova/nifs/nova.rs index da5c13bf..8c8c1395 100644 --- a/folding-schemes/src/folding/nova/nifs/nova.rs +++ b/folding-schemes/src/folding/nova/nifs/nova.rs @@ -31,12 +31,10 @@ pub struct ChallengeGadget { impl ChallengeGadget { pub fn get_challenge_native>( transcript: &mut T, - pp_hash: C::ScalarField, // public params hash U_i: &CI, u_i: &CI, cmT: Option<&C>, ) -> Vec { - transcript.absorb(&pp_hash); transcript.absorb(&U_i); transcript.absorb(&u_i); // in the Nova case we absorb the cmT, in Ova case we don't since it is not used. @@ -53,12 +51,10 @@ impl ChallengeGadget { CIVar: AbsorbGadget>, >( transcript: &mut T, - pp_hash: FpVar>, // public params hash U_i_vec: Vec>>, // apready processed input, so we don't have to recompute these values u_i: CIVar, cmT: Option>, ) -> Result>, SynthesisError> { - transcript.absorb(&pp_hash)?; transcript.absorb(&U_i_vec)?; transcript.absorb(&u_i)?; // in the Nova case we absorb the cmT, in Ova case we don't since it is not used. @@ -134,7 +130,6 @@ impl, T: Transcript, const cs_prover_params: &CS::ProverParams, r1cs: &R1CS, transcript: &mut T, - pp_hash: C::ScalarField, W_i: &Self::Witness, U_i: &Self::CommittedInstance, w_i: &Self::Witness, @@ -158,7 +153,6 @@ impl, T: Transcript, const let r_bits = ChallengeGadget::::get_challenge_native( transcript, - pp_hash, U_i, u_i, Some(&cmT), @@ -175,14 +169,12 @@ impl, T: Transcript, const fn verify( transcript: &mut T, - pp_hash: C::ScalarField, U_i: &Self::CommittedInstance, u_i: &Self::CommittedInstance, cmT: &C, // Proof ) -> Result<(Self::CommittedInstance, Vec), Error> { let r_bits = ChallengeGadget::::get_challenge_native( transcript, - pp_hash, U_i, u_i, Some(cmT), diff --git a/folding-schemes/src/folding/nova/nifs/nova_circuits.rs b/folding-schemes/src/folding/nova/nifs/nova_circuits.rs index 2487c165..7f7302ca 100644 --- a/folding-schemes/src/folding/nova/nifs/nova_circuits.rs +++ b/folding-schemes/src/folding/nova/nifs/nova_circuits.rs @@ -121,7 +121,6 @@ where fn verify( transcript: &mut T, - pp_hash: FpVar>, U_i: Self::CommittedInstanceVar, // U_i_vec is passed to reuse the already computed U_i_vec from previous methods U_i_vec: Vec>>, @@ -130,7 +129,6 @@ where ) -> Result<(Self::CommittedInstanceVar, Vec>>), SynthesisError> { let r_bits = ChallengeGadget::>::get_challenge_gadget( transcript, - pp_hash.clone(), U_i_vec, u_i.clone(), cmT.clone(), diff --git a/folding-schemes/src/folding/nova/nifs/ova.rs b/folding-schemes/src/folding/nova/nifs/ova.rs index 7e6757b8..7395e344 100644 --- a/folding-schemes/src/folding/nova/nifs/ova.rs +++ b/folding-schemes/src/folding/nova/nifs/ova.rs @@ -170,7 +170,6 @@ impl, T: Transcript, const _cs_prover_params: &CS::ProverParams, _r1cs: &R1CS, transcript: &mut T, - pp_hash: C::ScalarField, W_i: &Self::Witness, U_i: &Self::CommittedInstance, w_i: &Self::Witness, @@ -187,7 +186,7 @@ impl, T: Transcript, const let mut transcript_v = transcript.clone(); let r_bits = ChallengeGadget::::get_challenge_native( - transcript, pp_hash, U_i, u_i, None, // cmT not used in Ova + transcript, U_i, u_i, None, // cmT not used in Ova ); let r_Fr = C::ScalarField::from_bigint(BigInteger::from_bits_le(&r_bits)) .ok_or(Error::OutOfBounds)?; @@ -195,7 +194,7 @@ impl, T: Transcript, const let w = Self::fold_witness(r_Fr, W_i, w_i, &())?; let proof = C::ScalarField::zero(); - let (ci, _r_bits_v) = Self::verify(&mut transcript_v, pp_hash, U_i, u_i, &proof)?; + let (ci, _r_bits_v) = Self::verify(&mut transcript_v, U_i, u_i, &proof)?; #[cfg(test)] assert_eq!(_r_bits_v, r_bits); @@ -204,13 +203,12 @@ impl, T: Transcript, const fn verify( transcript: &mut T, - pp_hash: C::ScalarField, U_i: &Self::CommittedInstance, u_i: &Self::CommittedInstance, _proof: &Self::Proof, // unused in Ova ) -> Result<(Self::CommittedInstance, Vec), Error> { let r_bits = ChallengeGadget::::get_challenge_native( - transcript, pp_hash, U_i, u_i, None, // cmT not used in Ova + transcript, U_i, u_i, None, // cmT not used in Ova ); let r = C::ScalarField::from_bigint(BigInteger::from_bits_le(&r_bits)) .ok_or(Error::OutOfBounds)?; diff --git a/folding-schemes/src/folding/nova/nifs/ova_circuits.rs b/folding-schemes/src/folding/nova/nifs/ova_circuits.rs index 75c3eff7..a4396cd6 100644 --- a/folding-schemes/src/folding/nova/nifs/ova_circuits.rs +++ b/folding-schemes/src/folding/nova/nifs/ova_circuits.rs @@ -108,7 +108,6 @@ where fn verify( transcript: &mut T, - pp_hash: FpVar>, U_i: Self::CommittedInstanceVar, // U_i_vec is passed to reuse the already computed U_i_vec from previous methods U_i_vec: Vec>>, @@ -117,7 +116,6 @@ where ) -> Result<(Self::CommittedInstanceVar, Vec>>), SynthesisError> { let r_bits = ChallengeGadget::>::get_challenge_gadget( transcript, - pp_hash.clone(), U_i_vec, u_i.clone(), None, diff --git a/folding-schemes/src/folding/nova/zk.rs b/folding-schemes/src/folding/nova/zk.rs index 2f809973..89452ffa 100644 --- a/folding-schemes/src/folding/nova/zk.rs +++ b/folding-schemes/src/folding/nova/zk.rs @@ -30,10 +30,7 @@ /// paper). /// And the Use-case-2 would require a modified version of the Decider circuits. /// -use ark_crypto_primitives::sponge::{ - poseidon::{PoseidonConfig, PoseidonSponge}, - CryptographicSponge, -}; +use ark_crypto_primitives::sponge::poseidon::{PoseidonConfig, PoseidonSponge}; use ark_std::{rand::RngCore, One, Zero}; use super::{ @@ -45,6 +42,7 @@ use crate::{ commitment::CommitmentScheme, folding::traits::CommittedInstanceOps, frontend::FCircuit, + transcript::Transcript, Curve, Error, }; @@ -71,7 +69,10 @@ impl RandomizedIVCProof { nova: &Nova, mut rng: impl RngCore, ) -> Result, Error> { - let mut transcript = PoseidonSponge::::new(&nova.poseidon_config); + let mut transcript = PoseidonSponge::::new_with_pp_hash( + &nova.poseidon_config, + nova.pp_hash, + ); // I. Compute proof for 'regular' instances // 1. Fold the instance-witness pairs (U_i, W_i) with (u_i, w_i) @@ -79,7 +80,6 @@ impl RandomizedIVCProof { &nova.cs_pp, &nova.r1cs, &mut transcript, - nova.pp_hash, &nova.w_i, &nova.u_i, &nova.W_i, @@ -97,7 +97,6 @@ impl RandomizedIVCProof { &nova.cs_pp, &nova.r1cs, &mut transcript, - nova.pp_hash, &W_f, &U_f, &W_r, @@ -148,13 +147,14 @@ impl RandomizedIVCProof { } // b. Check computed hashes are correct - let sponge = PoseidonSponge::::new(poseidon_config); - let expected_u_i_x = proof.U_i.hash(&sponge, pp_hash, i, &z_0, &z_i); + let sponge = PoseidonSponge::::new_with_pp_hash(poseidon_config, pp_hash); + let mut transcript = sponge.clone(); + let expected_u_i_x = proof.U_i.hash(&sponge, i, &z_0, &z_i); if expected_u_i_x != proof.u_i.x[0] { return Err(Error::zkIVCVerificationFail); } - let expected_cf_u_i_x = proof.cf_U_i.hash_cyclefold(&sponge, pp_hash); + let expected_cf_u_i_x = proof.cf_U_i.hash_cyclefold(&sponge); if expected_cf_u_i_x != proof.u_i.x[1] { return Err(Error::IVCVerificationFail); } @@ -164,11 +164,9 @@ impl RandomizedIVCProof { return Err(Error::zkIVCVerificationFail); } - let mut transcript = PoseidonSponge::::new(poseidon_config); // 3. Obtain the U_f folded instance let (U_f, _) = NIFS::, true>::verify( &mut transcript, - pp_hash, &proof.u_i, &proof.U_i, &proof.pi, @@ -177,7 +175,6 @@ impl RandomizedIVCProof { // 4. Obtain the U^{\prime}_i folded instance let (U_i_prime, _) = NIFS::, true>::verify( &mut transcript, - pp_hash, &U_f, &proof.U_r, &proof.pi_prime, diff --git a/folding-schemes/src/folding/protogalaxy/circuits.rs b/folding-schemes/src/folding/protogalaxy/circuits.rs index 06ab34f8..6801d9ae 100644 --- a/folding-schemes/src/folding/protogalaxy/circuits.rs +++ b/folding-schemes/src/folding/protogalaxy/circuits.rs @@ -1,5 +1,4 @@ use ark_crypto_primitives::sponge::{ - constraints::CryptographicSpongeVar, poseidon::{constraints::PoseidonSpongeVar, PoseidonConfig}, CryptographicSponge, }; @@ -269,7 +268,7 @@ where let K_coeffs = Vec::new_witness(cs.clone(), || Ok(self.K_coeffs))?; // `sponge` is for digest computation. - let sponge = PoseidonSpongeVar::::new(cs.clone(), &self.poseidon_config); + let sponge = PoseidonSpongeVar::new_with_pp_hash(&self.poseidon_config, &pp_hash)?; // `transcript` is for challenge generation. let mut transcript = sponge.clone(); @@ -278,9 +277,9 @@ 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(&sponge, &pp_hash, &i, &z_0, &z_i)?; + let (u_i_x, _) = U_i.clone().hash(&sponge, &i, &z_0, &z_i)?; // u_i.x[1] = H(cf_U_i) - let (cf_u_i_x, _) = cf_U_i.clone().hash(&sponge, pp_hash.clone())?; + let (cf_u_i_x, _) = cf_U_i.clone().hash(&sponge)?; // P.2. Prepare incoming primary instances // P.3. Fold incoming primary instances into the running instance @@ -303,16 +302,11 @@ where // Base case: u_{i+1}.x[0] == H((i+1, z_0, z_{i+1}, U_{\bot}) // 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( - &sponge, - &pp_hash, - &(i + FpVar::>::one()), - &z_0, - &z_i1, - )?; + let (u_i1_x, _) = + U_i1.clone() + .hash(&sponge, &(i + FpVar::>::one()), &z_0, &z_i1)?; let (u_i1_x_base, _) = CommittedInstanceVar::new_constant(cs.clone(), u_dummy)?.hash( &sponge, - &pp_hash, &FpVar::>::one(), &z_0, &z_i1, @@ -353,7 +347,6 @@ where // C.3. Fold incoming CycleFold instances into the running instance let cf_U_i1 = CycleFoldAugmentationGadget::fold_gadget( &mut transcript, - &pp_hash, cf_U_i, vec![cf_u_i], vec![cf_cmT], @@ -363,10 +356,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(&sponge, pp_hash.clone())?; + let (cf_u_i1_x, _) = cf_U_i1.clone().hash(&sponge)?; let (cf_u_i1_x_base, _) = CycleFoldCommittedInstanceVar::::new_constant(cs.clone(), cf_u_dummy)? - .hash(&sponge, pp_hash.clone())?; + .hash(&sponge)?; let cf_x = is_basecase.select(&cf_u_i1_x_base, &cf_u_i1_x)?; // This line "converts" `cf_x` from a witness to a public input. // Instead of directly modifying the constraint system, we explicitly @@ -401,7 +394,7 @@ mod tests { use crate::{ arith::r1cs::tests::get_test_r1cs, folding::protogalaxy::folding::{tests::prepare_inputs, Folding}, - transcript::poseidon::poseidon_canonical_config, + transcript::{poseidon::poseidon_canonical_config, Transcript}, Error, }; @@ -417,8 +410,9 @@ mod tests { // init Prover & Verifier's transcript let poseidon_config = poseidon_canonical_config::(); - let mut transcript_p = PoseidonSponge::new(&poseidon_config); - let mut transcript_v = PoseidonSponge::new(&poseidon_config); + let pp_hash = Fr::from(42u32); // only for testing + let mut transcript_p = PoseidonSponge::new_with_pp_hash(&poseidon_config, pp_hash); + let mut transcript_v = transcript_p.clone(); let (_, _, proof, _) = Folding::::prove( &mut transcript_p, @@ -433,7 +427,9 @@ mod tests { Folding::::verify(&mut transcript_v, &instance, &instances, proof.clone())?; let cs = ConstraintSystem::new_ref(); - let mut transcript_var = PoseidonSpongeVar::new(cs.clone(), &poseidon_config); + let pp_hash_var = FpVar::new_witness(cs.clone(), || Ok(pp_hash))?; + let mut transcript_var = + PoseidonSpongeVar::new_with_pp_hash(&poseidon_config, &pp_hash_var)?; let instance_var = CommittedInstanceVar::new_witness(cs.clone(), || Ok(instance))?; let instances_var = Vec::new_witness(cs.clone(), || Ok(instances))?; let F_coeffs_var = Vec::new_witness(cs.clone(), || Ok(proof.F_coeffs))?; diff --git a/folding-schemes/src/folding/protogalaxy/decider_eth_circuit.rs b/folding-schemes/src/folding/protogalaxy/decider_eth_circuit.rs index da9ce4e3..0984453d 100644 --- a/folding-schemes/src/folding/protogalaxy/decider_eth_circuit.rs +++ b/folding-schemes/src/folding/protogalaxy/decider_eth_circuit.rs @@ -1,15 +1,12 @@ /// This file implements the onchain (Ethereum's EVM) decider circuit. For non-ethereum use cases, /// other more efficient approaches can be used. -use ark_crypto_primitives::sponge::{ - constraints::CryptographicSpongeVar, - poseidon::{constraints::PoseidonSpongeVar, PoseidonSponge}, - CryptographicSponge, -}; +use ark_crypto_primitives::sponge::poseidon::{constraints::PoseidonSpongeVar, PoseidonSponge}; use ark_ff::PrimeField; use ark_r1cs_std::{ alloc::{AllocVar, AllocationMode}, eq::EqGadget, fields::fp::FpVar, + R1CSVar, }; use ark_relations::r1cs::{Namespace, SynthesisError}; use ark_std::{borrow::Borrow, marker::PhantomData}; @@ -28,6 +25,7 @@ use crate::{ traits::{WitnessOps, WitnessVarOps}, }, frontend::FCircuit, + transcript::Transcript, Curve, Error, }; @@ -92,7 +90,8 @@ impl< type Error = Error; fn try_from(protogalaxy: ProtoGalaxy) -> Result { - let mut transcript = PoseidonSponge::::new(&protogalaxy.poseidon_config); + let mut transcript = + PoseidonSponge::new_with_pp_hash(&protogalaxy.poseidon_config, protogalaxy.pp_hash); let (U_i1, W_i1, proof, aux) = Folding::prove( &mut transcript, @@ -159,14 +158,13 @@ impl fn fold_field_elements_gadget( _arith: &R1CS>, transcript: &mut PoseidonSpongeVar>, - _pp_hash: FpVar>, U: CommittedInstanceVar, _U_vec: Vec>>, u: CommittedInstanceVar, proof: Self::Proof, randomness: Self::Randomness, ) -> Result, SynthesisError> { - let cs = transcript.cs(); + let cs = U.e.cs(); let F_coeffs = Vec::new_witness(cs.clone(), || Ok(&proof.F_coeffs[..]))?; let K_coeffs = Vec::new_witness(cs.clone(), || Ok(&proof.K_coeffs[..]))?; let randomness = Vec::new_input(cs.clone(), || Ok(randomness))?; diff --git a/folding-schemes/src/folding/protogalaxy/folding.rs b/folding-schemes/src/folding/protogalaxy/folding.rs index 4fc40594..2d3a220a 100644 --- a/folding-schemes/src/folding/protogalaxy/folding.rs +++ b/folding-schemes/src/folding/protogalaxy/folding.rs @@ -403,7 +403,6 @@ pub fn lagrange_polys( pub mod tests { use super::*; use ark_crypto_primitives::sponge::poseidon::PoseidonSponge; - use ark_crypto_primitives::sponge::CryptographicSponge; use ark_pallas::{Fr, Projective}; use ark_std::{rand::Rng, UniformRand}; @@ -494,8 +493,9 @@ pub mod tests { // init Prover & Verifier's transcript let poseidon_config = poseidon_canonical_config::(); - let mut transcript_p = PoseidonSponge::::new(&poseidon_config); - let mut transcript_v = PoseidonSponge::::new(&poseidon_config); + let pp_hash = Fr::from(42u32); // only for testing + let mut transcript_p = PoseidonSponge::new_with_pp_hash(&poseidon_config, pp_hash); + let mut transcript_v = transcript_p.clone(); let (folded_instance, folded_witness, proof, _) = Folding::::prove( &mut transcript_p, @@ -527,8 +527,9 @@ pub mod tests { // init Prover & Verifier's transcript let poseidon_config = poseidon_canonical_config::(); - let mut transcript_p = PoseidonSponge::::new(&poseidon_config); - let mut transcript_v = PoseidonSponge::::new(&poseidon_config); + let pp_hash = Fr::from(42u32); // only for testing + let mut transcript_p = PoseidonSponge::new_with_pp_hash(&poseidon_config, pp_hash); + let mut transcript_v = transcript_p.clone(); let (mut running_witness, mut running_instance, _, _) = prepare_inputs(0)?; diff --git a/folding-schemes/src/folding/protogalaxy/mod.rs b/folding-schemes/src/folding/protogalaxy/mod.rs index 1089e320..dd2bd662 100644 --- a/folding-schemes/src/folding/protogalaxy/mod.rs +++ b/folding-schemes/src/folding/protogalaxy/mod.rs @@ -1,8 +1,5 @@ /// Implements the scheme described in [ProtoGalaxy](https://eprint.iacr.org/2023/1106.pdf) -use ark_crypto_primitives::sponge::{ - poseidon::{PoseidonConfig, PoseidonSponge}, - CryptographicSponge, -}; +use ark_crypto_primitives::sponge::poseidon::{PoseidonConfig, PoseidonSponge}; use ark_ff::{BigInteger, PrimeField}; use ark_r1cs_std::{ alloc::{AllocVar, AllocationMode}, @@ -34,7 +31,7 @@ use crate::{ CF1, }, frontend::{utils::DummyCircuit, FCircuit}, - transcript::poseidon::poseidon_canonical_config, + transcript::{poseidon::poseidon_canonical_config, Transcript}, utils::pp_hash, Curve, Error, FoldingScheme, }; @@ -836,7 +833,10 @@ where let d = 2; // `sponge` is for digest computation. - let sponge = PoseidonSponge::::new(&self.poseidon_config); + let sponge = PoseidonSponge::::new_with_pp_hash( + &self.poseidon_config, + self.pp_hash, + ); // `transcript` is for challenge generation. let mut transcript_prover = sponge.clone(); @@ -903,7 +903,6 @@ where &mut transcript_prover, &self.cf_r1cs, &self.cf_cs_params, - self.pp_hash, self.cf_W_i.clone(), self.cf_U_i.clone(), vec![cf_w_i], @@ -1055,22 +1054,20 @@ where cf_U_i, } = ivc_proof; - let sponge = PoseidonSponge::::new(&vp.poseidon_config); + let sponge = PoseidonSponge::new_with_pp_hash(&vp.poseidon_config, vp.pp_hash()?); if u_i.x.len() != 2 || U_i.x.len() != 2 { 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(&sponge, pp_hash, num_steps, &z_0, &z_i); + let expected_u_i_x = U_i.hash(&sponge, num_steps, &z_0, &z_i); 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(&sponge, pp_hash); + let expected_cf_u_i_x = cf_U_i.hash_cyclefold(&sponge); if expected_cf_u_i_x != u_i.x[1] { return Err(Error::IVCVerificationFail); } diff --git a/folding-schemes/src/folding/traits.rs b/folding-schemes/src/folding/traits.rs index 74ab3b28..cdd3b050 100644 --- a/folding-schemes/src/folding/traits.rs +++ b/folding-schemes/src/folding/traits.rs @@ -25,7 +25,6 @@ pub trait CommittedInstanceOps: Inputize> { fn hash>>( &self, sponge: &T, - pp_hash: CF1, // public params hash i: CF1, z_0: &[CF1], z_i: &[CF1], @@ -34,7 +33,6 @@ pub trait CommittedInstanceOps: Inputize> { Self: Sized + Absorb, { let mut sponge = sponge.clone(); - sponge.absorb(&pp_hash); sponge.absorb(&i); sponge.absorb(&z_0); sponge.absorb(&z_i); @@ -71,7 +69,6 @@ pub trait CommittedInstanceVarOps { fn hash( &self, sponge: &PoseidonSpongeVar>, - pp_hash: &FpVar>, i: &FpVar>, z_0: &[FpVar>], z_i: &[FpVar>], @@ -81,7 +78,6 @@ pub trait CommittedInstanceVarOps { { let mut sponge = sponge.clone(); let U_vec = self.to_sponge_field_elements()?; - sponge.absorb(&pp_hash)?; sponge.absorb(&i)?; sponge.absorb(&z_0)?; sponge.absorb(&z_i)?; diff --git a/folding-schemes/src/transcript/mod.rs b/folding-schemes/src/transcript/mod.rs index de3a0572..e79603eb 100644 --- a/folding-schemes/src/transcript/mod.rs +++ b/folding-schemes/src/transcript/mod.rs @@ -57,6 +57,10 @@ impl> AbsorbNonNativeGadget for [T } pub trait Transcript: CryptographicSponge { + /// `new_with_pp_hash` creates a new transcript / sponge with the given + /// hash of the public parameters. + fn new_with_pp_hash(config: &Self::Config, pp_hash: F) -> Self; + /// `absorb_point` is for absorbing points whose `BaseField` is the field of /// the sponge, i.e., the type `C` of these points should satisfy /// `C::BaseField = F`. @@ -90,6 +94,13 @@ pub trait Transcript: CryptographicSponge { pub trait TranscriptVar: CryptographicSpongeVar { + /// `new_with_pp_hash` creates a new transcript / sponge with the given + /// hash of the public parameters. + fn new_with_pp_hash( + config: &Self::Parameters, + pp_hash: &FpVar, + ) -> Result; + /// `absorb_point` is for absorbing points whose `BaseField` is the field of /// the sponge, i.e., the type `C` of these points should satisfy /// `C::BaseField = F`. diff --git a/folding-schemes/src/transcript/poseidon.rs b/folding-schemes/src/transcript/poseidon.rs index 90709757..15a7f49b 100644 --- a/folding-schemes/src/transcript/poseidon.rs +++ b/folding-schemes/src/transcript/poseidon.rs @@ -6,11 +6,17 @@ use ark_crypto_primitives::sponge::{ use ark_ec::{AffineRepr, CurveGroup}; use ark_ff::{BigInteger, PrimeField}; use ark_r1cs_std::{boolean::Boolean, fields::fp::FpVar, groups::CurveVar}; -use ark_relations::r1cs::SynthesisError; +use ark_relations::r1cs::{ConstraintSystemRef, SynthesisError}; use super::{AbsorbNonNative, AbsorbNonNativeGadget, Transcript, TranscriptVar}; impl Transcript for PoseidonSponge { + fn new_with_pp_hash(config: &Self::Config, pp_hash: F) -> Self { + let mut sponge = Self::new(config); + sponge.absorb(&pp_hash); + sponge + } + // Compatible with the in-circuit `TranscriptVar::absorb_point` fn absorb_point>(&mut self, p: &C) { let (x, y) = p.into_affine().xy().unwrap_or_default(); @@ -38,6 +44,15 @@ impl Transcript for PoseidonSponge { } impl TranscriptVar> for PoseidonSpongeVar { + fn new_with_pp_hash( + config: &Self::Parameters, + pp_hash: &FpVar, + ) -> Result { + let mut sponge = Self::new(ConstraintSystemRef::None, config); + sponge.absorb(&pp_hash)?; + Ok(sponge) + } + fn absorb_point, GC: CurveVar>( &mut self, v: &GC,