Skip to content

Commit

Permalink
Absorb pp_hash when initializing sponge or transcript so we no long…
Browse files Browse the repository at this point in the history
…er need to absorb it later
  • Loading branch information
winderica committed Jan 16, 2025
1 parent 1604ddc commit b66d42a
Show file tree
Hide file tree
Showing 26 changed files with 310 additions and 362 deletions.
59 changes: 19 additions & 40 deletions folding-schemes/src/folding/circuits/cyclefold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,13 +167,8 @@ impl<C: Curve> CycleFoldCommittedInstance<C> {
/// 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<T: Transcript<C::BaseField>>(
&self,
sponge: &T,
pp_hash: C::BaseField, // public params hash
) -> C::BaseField {
pub fn hash_cyclefold<T: Transcript<C::BaseField>>(&self, sponge: &T) -> C::BaseField {
let mut sponge = sponge.clone();
sponge.absorb(&pp_hash);
sponge.absorb_nonnative(self);
sponge.squeeze_field_elements(1)[0]
}
Expand All @@ -191,11 +186,9 @@ impl<C: Curve> CycleFoldCommittedInstanceVar<C> {
pub fn hash<S: CryptographicSponge, T: TranscriptVar<CF2<C>, S>>(
&self,
sponge: &T,
pp_hash: FpVar<CF2<C>>, // public params hash
) -> Result<(FpVar<CF2<C>>, Vec<FpVar<CF2<C>>>), 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
Expand Down Expand Up @@ -325,12 +318,10 @@ pub struct CycleFoldChallengeGadget<C: Curve> {
impl<C: Curve> CycleFoldChallengeGadget<C> {
pub fn get_challenge_native<T: Transcript<C::BaseField>>(
transcript: &mut T,
pp_hash: C::BaseField, // public params hash
U_i: &CycleFoldCommittedInstance<C>,
u_i: &CycleFoldCommittedInstance<C>,
cmT: C,
) -> Vec<bool> {
transcript.absorb(&pp_hash);
transcript.absorb_nonnative(U_i);
transcript.absorb_nonnative(u_i);
transcript.absorb_point(&cmT);
Expand All @@ -340,12 +331,10 @@ impl<C: Curve> CycleFoldChallengeGadget<C> {
// compatible with the native get_challenge_native
pub fn get_challenge_gadget<S: CryptographicSponge, T: TranscriptVar<C::BaseField, S>>(
transcript: &mut T,
pp_hash: &FpVar<C::BaseField>, // public params hash
U_i_vec: &[FpVar<C::BaseField>],
u_i: &CycleFoldCommittedInstanceVar<C>,
cmT: &C::Var,
) -> Result<Vec<Boolean<C::BaseField>>, SynthesisError> {
transcript.absorb(pp_hash)?;
transcript.absorb(&U_i_vec)?;
transcript.absorb_nonnative(u_i)?;
transcript.absorb_point(cmT)?;
Expand Down Expand Up @@ -596,10 +585,9 @@ impl CycleFoldAugmentationGadget {
transcript: &mut impl Transcript<CF2<C>>,
cf_r1cs: &R1CS<C::ScalarField>,
cf_cs_params: &CS::ProverParams,
pp_hash: CF2<C>, // public params hash
mut cf_W: CycleFoldWitness<C>, // witness of the running instance
mut cf_U: CycleFoldCommittedInstance<C>, // running instance
cf_ws: Vec<CycleFoldWitness<C>>, // witnesses of the incoming instances
mut cf_W: CycleFoldWitness<C>, // witness of the running instance
mut cf_U: CycleFoldCommittedInstance<C>, // running instance
cf_ws: Vec<CycleFoldWitness<C>>, // witnesses of the incoming instances
cf_us: Vec<CycleFoldCommittedInstance<C>>, // incoming instances
) -> Result<
(
Expand All @@ -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::<C>::from(<CF1<C> as PrimeField>::BigInt::from_bits_le(&cf_r_bits));

(cf_W, cf_U) = CycleFoldNIFS::<C, CS, H>::prove(
Expand All @@ -647,7 +634,6 @@ impl CycleFoldAugmentationGadget {

pub fn fold_gadget<C2: Curve, S: CryptographicSponge>(
transcript: &mut impl TranscriptVar<CF2<C2>, S>,
pp_hash: &FpVar<CF2<C2>>,
mut cf_U: CycleFoldCommittedInstanceVar<C2>,
cf_us: Vec<CycleFoldCommittedInstanceVar<C2>>,
cf_cmTs: Vec<C2::Var>,
Expand All @@ -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,
Expand Down Expand Up @@ -721,10 +706,7 @@ impl<C2: Curve, CS2: CommitmentScheme<C2, H>, const H: bool> CycleFoldNIFS<C2, C
#[cfg(test)]
pub mod tests {
use ark_bn254::{constraints::GVar, Fq, Fr, G1Projective as Projective};
use ark_crypto_primitives::sponge::{
constraints::CryptographicSpongeVar,
poseidon::{constraints::PoseidonSpongeVar, PoseidonSponge},
};
use ark_crypto_primitives::sponge::poseidon::{constraints::PoseidonSpongeVar, PoseidonSponge};
use ark_r1cs_std::R1CSVar;
use ark_std::{One, UniformRand, Zero};

Expand Down Expand Up @@ -824,8 +806,8 @@ pub mod tests {
let mut rng = ark_std::test_rng();

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

// prepare the committed instances to test in-circuit
let ci: Vec<CommittedInstance<Projective>> = (0..2)
Expand All @@ -845,7 +827,6 @@ pub mod tests {
let cmT = Projective::rand(&mut rng); // random only for testing
let (ci3, r_bits) = NIFS::<Projective, Pedersen<Projective>, PoseidonSponge<Fr>>::verify(
&mut transcript_v,
pp_hash,
&ci1,
&ci2,
&cmT,
Expand Down Expand Up @@ -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::<Fq>();
let mut transcript = PoseidonSponge::<Fq>::new(&poseidon_config);
let pp_hash = Fq::from(42u32); // only for test
let mut transcript = PoseidonSponge::<Fq>::new_with_pp_hash(&poseidon_config, pp_hash);

let u_i = CycleFoldCommittedInstance::<Projective> {
cmE: Projective::zero(), // zero on purpose, so we test also the zero point case
Expand All @@ -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::<Projective>::get_challenge_native(
&mut transcript,
pp_hash,
&U_i,
&u_i,
cmT,
Expand All @@ -911,13 +891,12 @@ pub mod tests {
Ok(U_i.clone())
})?;
let cmTVar = GVar::new_witness(cs.clone(), || Ok(cmT))?;
let pp_hashVar = FpVar::<Fq>::new_witness(cs.clone(), || Ok(pp_hash))?;
let mut transcript_var =
PoseidonSpongeVar::<Fq>::new(ConstraintSystem::<Fq>::new_ref(), &poseidon_config);
PoseidonSpongeVar::<Fq>::new_with_pp_hash(&poseidon_config, &pp_hashVar)?;

let pp_hashVar = FpVar::<Fq>::new_witness(cs.clone(), || Ok(pp_hash))?;
let r_bitsVar = CycleFoldChallengeGadget::<Projective>::get_challenge_gadget(
&mut transcript_var,
&pp_hashVar,
&U_iVar.to_native_sponge_field_elements()?,
&u_iVar,
&cmTVar,
Expand All @@ -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::<Fq>();
let sponge = PoseidonSponge::<Fq>::new(&poseidon_config);
let pp_hash = Fq::from(42u32); // only for test
let sponge = PoseidonSponge::<Fq>::new_with_pp_hash(&poseidon_config, pp_hash);

let U_i = CycleFoldCommittedInstance::<Projective> {
cmE: Projective::rand(&mut rng),
Expand All @@ -946,18 +926,17 @@ pub mod tests {
.take(TestCycleFoldConfig::<Projective, 2>::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::<Fq>::new_ref();
let U_iVar = CycleFoldCommittedInstanceVar::<Projective>::new_witness(cs.clone(), || {
Ok(U_i.clone())
})?;
let pp_hashVar = FpVar::<Fq>::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(())
Expand Down
1 change: 0 additions & 1 deletion folding-schemes/src/folding/circuits/decider/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,6 @@ pub trait DeciderEnabledNIFS<
fn fold_field_elements_gadget(
arith: &A,
transcript: &mut PoseidonSpongeVar<CF1<C>>,
pp_hash: FpVar<CF1<C>>,
U: RU::Var,
U_vec: Vec<FpVar<CF1<C>>>,
u: IU::Var,
Expand Down
15 changes: 7 additions & 8 deletions folding-schemes/src/folding/circuits/decider/off_chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -27,6 +27,7 @@ use crate::{
nova::{decider_eth_circuit::WitnessVar, nifs::nova_circuits::CommittedInstanceVar},
traits::{CommittedInstanceOps, CommittedInstanceVarOps, Dummy, WitnessOps, WitnessVarOps},
},
transcript::TranscriptVar,
Curve,
};

Expand Down Expand Up @@ -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)?;
Expand All @@ -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,
Expand Down Expand Up @@ -281,8 +281,7 @@ impl<C2: Curve> ConstraintSynthesizer<CF1<C2>> 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)?;
Expand Down
10 changes: 5 additions & 5 deletions folding-schemes/src/folding/circuits/decider/on_chain.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand All @@ -22,6 +22,7 @@ use crate::{
},
traits::{CommittedInstanceOps, CommittedInstanceVarOps, Dummy, WitnessOps, WitnessVarOps},
},
transcript::TranscriptVar,
Curve,
};

Expand Down Expand Up @@ -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();

Expand All @@ -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")]
Expand Down Expand Up @@ -286,7 +287,6 @@ where
D::fold_field_elements_gadget(
&self.arith,
&mut transcript,
pp_hash,
U_i,
U_i_vec,
u_i,
Expand Down
Loading

0 comments on commit b66d42a

Please sign in to comment.