From 16d51d757b2c80a9f1cc4ac29930235895064ff4 Mon Sep 17 00:00:00 2001 From: winderica Date: Mon, 8 Jul 2024 09:25:08 +0100 Subject: [PATCH] Unify the computation of digests and challenges in different folding schemes (#94) * Remove the trait bound `C::BaseField: PrimeField` for better DX * Methods in `TranscriptVar` now exactly matches the ones in `Transcript` * Add `ProtoGalaxyTranscriptVar` and `CommittedInstanceVar` for protogalaxy * betas are unnecessary in "plain" (incoming) instances * Absorb the result of `get_challenge_nbits` as well * `ProtoGalaxyTranscript` now allows absorbing mulitple instances * Always return `Result<(), SynthesisError>` in `ProtoGalaxyTranscriptVar` * Impl `Transcript{Var}` for `PoseidonSponge{Var}` directly and remove `PoseidonTranscript{Var}` * `Transcript::absorb_point` doesn't need to return `Error` * Add `AbsorbNonNative` trait for hashing non-native values Note that now `absorb_point` only supports hashing points whose BaseField is equal to the sponge's field * More efficient `TranscriptVar::absorb_point` by securely removing `is_inf` * Use `sponge` and `transcript` consistently * Clarify the usage of `AbsorbNonNative{Gadget}` * Generic `sponge` and `transcript` params * Avoid unstable `associated_type_bounds` * Reuse `sponge` in hypernova * Clean up redundant imports * Remove unstable code * Clarify the usage of `absorb_point` and `absorb_nonnative` --- folding-schemes/src/arith/ccs.rs | 1 - folding-schemes/src/arith/r1cs.rs | 1 - folding-schemes/src/commitment/ipa.rs | 38 +-- folding-schemes/src/commitment/kzg.rs | 15 +- folding-schemes/src/commitment/mod.rs | 18 +- folding-schemes/src/commitment/pedersen.rs | 20 +- .../src/folding/circuits/cyclefold.rs | 146 +++++------- .../src/folding/circuits/nonnative/affine.rs | 74 +++--- .../src/folding/circuits/nonnative/uint.rs | 53 ++++- .../src/folding/circuits/sum_check.rs | 116 ++++------ folding-schemes/src/folding/hypernova/cccs.rs | 22 ++ .../src/folding/hypernova/circuits.rs | 181 ++++++--------- .../src/folding/hypernova/lcccs.rs | 62 ++--- folding-schemes/src/folding/hypernova/mod.rs | 70 +++--- .../src/folding/hypernova/nimfs.rs | 109 +++------ .../src/folding/hypernova/utils.rs | 1 - folding-schemes/src/folding/nova/circuits.rs | 174 ++++++-------- .../src/folding/nova/decider_eth.rs | 4 +- .../src/folding/nova/decider_eth_circuit.rs | 113 ++++----- folding-schemes/src/folding/nova/mod.rs | 194 ++++++++-------- folding-schemes/src/folding/nova/nifs.rs | 19 +- .../src/folding/protogalaxy/folding.rs | 42 ++-- .../src/folding/protogalaxy/mod.rs | 10 + .../src/folding/protogalaxy/traits.rs | 53 +++-- folding-schemes/src/frontend/circom/mod.rs | 3 +- folding-schemes/src/frontend/circom/utils.rs | 1 - folding-schemes/src/frontend/mod.rs | 4 +- folding-schemes/src/transcript/mod.rs | 106 +++++++-- folding-schemes/src/transcript/poseidon.rs | 219 ++++++++++-------- .../src/utils/espresso/sum_check/mod.rs | 99 ++++---- .../src/utils/espresso/sum_check/prover.rs | 34 ++- .../src/utils/espresso/sum_check/structs.rs | 15 +- .../src/utils/espresso/sum_check/verifier.rs | 24 +- .../src/utils/espresso/virtual_polynomial.rs | 2 - folding-schemes/src/utils/lagrange_poly.rs | 2 +- solidity-verifiers/src/verifiers/kzg.rs | 14 +- 36 files changed, 1028 insertions(+), 1031 deletions(-) diff --git a/folding-schemes/src/arith/ccs.rs b/folding-schemes/src/arith/ccs.rs index 2c55fed..80b812f 100644 --- a/folding-schemes/src/arith/ccs.rs +++ b/folding-schemes/src/arith/ccs.rs @@ -114,7 +114,6 @@ impl CCS { pub mod tests { use super::*; use crate::arith::r1cs::tests::{get_test_r1cs, get_test_z as r1cs_get_test_z}; - use ark_ff::PrimeField; use ark_pallas::Fr; pub fn get_test_ccs() -> CCS { diff --git a/folding-schemes/src/arith/r1cs.rs b/folding-schemes/src/arith/r1cs.rs index d66754f..ef0c5ba 100644 --- a/folding-schemes/src/arith/r1cs.rs +++ b/folding-schemes/src/arith/r1cs.rs @@ -140,7 +140,6 @@ pub mod tests { use super::*; use crate::utils::vec::tests::{to_F_matrix, to_F_vec}; - use ark_ff::PrimeField; use ark_pallas::Fr; pub fn get_test_r1cs() -> R1CS { diff --git a/folding-schemes/src/commitment/ipa.rs b/folding-schemes/src/commitment/ipa.rs index 5645d43..4607834 100644 --- a/folding-schemes/src/commitment/ipa.rs +++ b/folding-schemes/src/commitment/ipa.rs @@ -97,7 +97,7 @@ impl CommitmentScheme for IPA { fn prove( params: &Self::ProverParams, - transcript: &mut impl Transcript, + transcript: &mut impl Transcript, P: &C, // commitment a: &[C::ScalarField], // vector blind: &C::ScalarField, @@ -131,7 +131,7 @@ impl CommitmentScheme for IPA { r = vec![]; } - transcript.absorb_point(P)?; + transcript.absorb_nonnative(P); let x = transcript.get_challenge(); // challenge value at which we evaluate let s = transcript.get_challenge(); let U = C::generator().mul(s); @@ -162,8 +162,8 @@ impl CommitmentScheme for IPA { R[j] = C::msm_unchecked(&G[..m], &a[m..]) + U.mul(inner_prod(&a[m..], &b[..m])?); } // get challenge for the j-th round - transcript.absorb_point(&L[j])?; - transcript.absorb_point(&R[j])?; + transcript.absorb_nonnative(&L[j]); + transcript.absorb_nonnative(&R[j]); u[j] = transcript.get_challenge(); let uj = u[j]; @@ -225,21 +225,21 @@ impl CommitmentScheme for IPA { fn verify( params: &Self::VerifierParams, - transcript: &mut impl Transcript, + transcript: &mut impl Transcript, P: &C, // commitment proof: &Self::Proof, ) -> Result<(), Error> { let (p, _r) = (proof.0.clone(), proof.1); let k = p.L.len(); - transcript.absorb_point(P)?; + transcript.absorb_nonnative(P); let x = transcript.get_challenge(); // challenge value at which we evaluate let s = transcript.get_challenge(); let U = C::generator().mul(s); let mut u: Vec = vec![C::ScalarField::zero(); k]; for i in (0..k).rev() { - transcript.absorb_point(&p.L[i])?; - transcript.absorb_point(&p.R[i])?; + transcript.absorb_nonnative(&p.L[i]); + transcript.absorb_nonnative(&p.R[i]); u[i] = transcript.get_challenge(); } let challenge = (x, U, u); @@ -566,15 +566,15 @@ where #[cfg(test)] mod tests { + use ark_crypto_primitives::sponge::{poseidon::PoseidonSponge, CryptographicSponge}; use ark_ec::Group; use ark_pallas::{constraints::GVar, Fq, Fr, Projective}; - use ark_r1cs_std::{alloc::AllocVar, bits::boolean::Boolean, eq::EqGadget}; + use ark_r1cs_std::eq::EqGadget; use ark_relations::r1cs::ConstraintSystem; - use ark_std::UniformRand; use std::ops::Mul; use super::*; - use crate::transcript::poseidon::{poseidon_canonical_config, PoseidonTranscript}; + use crate::transcript::poseidon::poseidon_canonical_config; #[test] fn test_ipa() { @@ -592,9 +592,9 @@ mod tests { let poseidon_config = poseidon_canonical_config::(); // init Prover's transcript - let mut transcript_p = PoseidonTranscript::::new(&poseidon_config); + let mut transcript_p = PoseidonSponge::::new(&poseidon_config); // init Verifier's transcript - let mut transcript_v = PoseidonTranscript::::new(&poseidon_config); + let mut transcript_v = PoseidonSponge::::new(&poseidon_config); // a is the vector that we're committing let a: Vec = std::iter::repeat_with(|| Fr::rand(&mut rng)) @@ -636,9 +636,9 @@ mod tests { let poseidon_config = poseidon_canonical_config::(); // init Prover's transcript - let mut transcript_p = PoseidonTranscript::::new(&poseidon_config); + let mut transcript_p = PoseidonSponge::::new(&poseidon_config); // init Verifier's transcript - let mut transcript_v = PoseidonTranscript::::new(&poseidon_config); + let mut transcript_v = PoseidonSponge::::new(&poseidon_config); let mut a: Vec = std::iter::repeat_with(|| Fr::rand(&mut rng)) .take(d / 2) @@ -666,15 +666,15 @@ mod tests { // circuit let cs = ConstraintSystem::::new_ref(); - let mut transcript_v = PoseidonTranscript::::new(&poseidon_config); - transcript_v.absorb_point(&cm).unwrap(); + let mut transcript_v = PoseidonSponge::::new(&poseidon_config); + transcript_v.absorb_nonnative(&cm); let challenge = transcript_v.get_challenge(); // challenge value at which we evaluate let s = transcript_v.get_challenge(); let U = Projective::generator().mul(s); let mut u: Vec = vec![Fr::zero(); k]; for i in (0..k).rev() { - transcript_v.absorb_point(&proof.0.L[i]).unwrap(); - transcript_v.absorb_point(&proof.0.R[i]).unwrap(); + transcript_v.absorb_nonnative(&proof.0.L[i]); + transcript_v.absorb_nonnative(&proof.0.R[i]); u[i] = transcript_v.get_challenge(); } diff --git a/folding-schemes/src/commitment/kzg.rs b/folding-schemes/src/commitment/kzg.rs index 47a4cbd..d034428 100644 --- a/folding-schemes/src/commitment/kzg.rs +++ b/folding-schemes/src/commitment/kzg.rs @@ -156,13 +156,13 @@ where /// the Pairing trait. fn prove( params: &Self::ProverParams, - transcript: &mut impl Transcript, + transcript: &mut impl Transcript, cm: &E::G1, v: &[E::ScalarField], _blind: &E::ScalarField, _rng: Option<&mut dyn RngCore>, ) -> Result { - transcript.absorb_point(cm)?; + transcript.absorb_nonnative(cm); let challenge = transcript.get_challenge(); Self::prove_with_challenge(params, challenge, v, _blind, _rng) } @@ -214,11 +214,11 @@ where fn verify( params: &Self::VerifierParams, - transcript: &mut impl Transcript, + transcript: &mut impl Transcript, cm: &E::G1, proof: &Self::Proof, ) -> Result<(), Error> { - transcript.absorb_point(cm)?; + transcript.absorb_nonnative(cm); let challenge = transcript.get_challenge(); Self::verify_with_challenge(params, challenge, cm, proof) } @@ -286,17 +286,18 @@ fn convert_to_bigints(p: &[F]) -> Vec { #[cfg(test)] mod tests { use ark_bn254::{Bn254, Fr, G1Projective as G1}; + use ark_crypto_primitives::sponge::{poseidon::PoseidonSponge, CryptographicSponge}; use ark_std::{test_rng, UniformRand}; use super::*; - use crate::transcript::poseidon::{poseidon_canonical_config, PoseidonTranscript}; + use crate::transcript::poseidon::poseidon_canonical_config; #[test] fn test_kzg_commitment_scheme() { let mut rng = &mut test_rng(); let poseidon_config = poseidon_canonical_config::(); - let transcript_p = &mut PoseidonTranscript::::new(&poseidon_config); - let transcript_v = &mut PoseidonTranscript::::new(&poseidon_config); + let transcript_p = &mut PoseidonSponge::::new(&poseidon_config); + let transcript_v = &mut PoseidonSponge::::new(&poseidon_config); let n = 10; let (pk, vk): (ProverKey, VerifierKey) = diff --git a/folding-schemes/src/commitment/mod.rs b/folding-schemes/src/commitment/mod.rs index 1c64457..58ecadd 100644 --- a/folding-schemes/src/commitment/mod.rs +++ b/folding-schemes/src/commitment/mod.rs @@ -34,7 +34,7 @@ pub trait CommitmentScheme: Clone + Debug fn prove( params: &Self::ProverParams, - transcript: &mut impl Transcript, + transcript: &mut impl Transcript, cm: &C, v: &[C::ScalarField], blind: &C::ScalarField, @@ -53,7 +53,7 @@ pub trait CommitmentScheme: Clone + Debug fn verify( params: &Self::VerifierParams, - transcript: &mut impl Transcript, + transcript: &mut impl Transcript, cm: &C, proof: &Self::Proof, ) -> Result<(), Error>; @@ -72,7 +72,10 @@ pub trait CommitmentScheme: Clone + Debug mod tests { use super::*; use ark_bn254::{Bn254, Fr, G1Projective as G1}; - use ark_crypto_primitives::sponge::{poseidon::PoseidonConfig, Absorb}; + use ark_crypto_primitives::sponge::{ + poseidon::{PoseidonConfig, PoseidonSponge}, + Absorb, CryptographicSponge, + }; use ark_poly_commit::kzg10::VerifierKey; use ark_std::Zero; use ark_std::{test_rng, UniformRand}; @@ -80,10 +83,7 @@ mod tests { use super::ipa::IPA; use super::kzg::{ProverKey, KZG}; use super::pedersen::Pedersen; - use crate::transcript::{ - poseidon::{poseidon_canonical_config, PoseidonTranscript}, - Transcript, - }; + use crate::transcript::poseidon::poseidon_canonical_config; #[test] fn test_homomorphic_property_using_Commitment_trait() { @@ -153,7 +153,7 @@ mod tests { let v_3: Vec = v_1.iter().zip(v_2).map(|(a, b)| *a + (r * b)).collect(); // compute the proof of the cm_3 - let transcript_p = &mut PoseidonTranscript::::new(poseidon_config); + let transcript_p = &mut PoseidonSponge::::new(poseidon_config); let proof = CS::prove( prover_params, transcript_p, @@ -165,7 +165,7 @@ mod tests { .unwrap(); // verify the opening proof - let transcript_v = &mut PoseidonTranscript::::new(poseidon_config); + let transcript_v = &mut PoseidonSponge::::new(poseidon_config); CS::verify(verifier_params, transcript_v, &cm_3, &proof).unwrap(); } } diff --git a/folding-schemes/src/commitment/pedersen.rs b/folding-schemes/src/commitment/pedersen.rs index a906f21..4d225ce 100644 --- a/folding-schemes/src/commitment/pedersen.rs +++ b/folding-schemes/src/commitment/pedersen.rs @@ -81,13 +81,13 @@ impl CommitmentScheme for Pedersen { fn prove( params: &Self::ProverParams, - transcript: &mut impl Transcript, + transcript: &mut impl Transcript, cm: &C, v: &[C::ScalarField], r: &C::ScalarField, // blinding factor _rng: Option<&mut dyn RngCore>, ) -> Result { - transcript.absorb_point(cm)?; + transcript.absorb_nonnative(cm); let r1 = transcript.get_challenge(); let d = transcript.get_challenges(v.len()); @@ -98,7 +98,7 @@ impl CommitmentScheme for Pedersen { R += params.h.mul(r1); } - transcript.absorb_point(&R)?; + transcript.absorb_nonnative(&R); let e = transcript.get_challenge(); let challenge = (r1, d, R, e); @@ -133,14 +133,14 @@ impl CommitmentScheme for Pedersen { fn verify( params: &Self::VerifierParams, - transcript: &mut impl Transcript, + transcript: &mut impl Transcript, cm: &C, proof: &Proof, ) -> Result<(), Error> { - transcript.absorb_point(cm)?; + transcript.absorb_nonnative(cm); transcript.get_challenge(); // r_1 transcript.get_challenges(proof.u.len()); // d - transcript.absorb_point(&proof.R)?; + transcript.absorb_nonnative(&proof.R); let e = transcript.get_challenge(); Self::verify_with_challenge(params, e, cm, proof) } @@ -217,14 +217,14 @@ where #[cfg(test)] mod tests { + use ark_crypto_primitives::sponge::{poseidon::PoseidonSponge, CryptographicSponge}; use ark_ff::{BigInteger, PrimeField}; use ark_pallas::{constraints::GVar, Fq, Fr, Projective}; use ark_r1cs_std::{alloc::AllocVar, eq::EqGadget}; use ark_relations::r1cs::ConstraintSystem; - use ark_std::UniformRand; use super::*; - use crate::transcript::poseidon::{poseidon_canonical_config, PoseidonTranscript}; + use crate::transcript::poseidon::poseidon_canonical_config; #[test] fn test_pedersen() { @@ -240,9 +240,9 @@ mod tests { let poseidon_config = poseidon_canonical_config::(); // init Prover's transcript - let mut transcript_p = PoseidonTranscript::::new(&poseidon_config); + let mut transcript_p = PoseidonSponge::::new(&poseidon_config); // init Verifier's transcript - let mut transcript_v = PoseidonTranscript::::new(&poseidon_config); + let mut transcript_v = PoseidonSponge::::new(&poseidon_config); let v: Vec = std::iter::repeat_with(|| Fr::rand(&mut rng)) .take(n) diff --git a/folding-schemes/src/folding/circuits/cyclefold.rs b/folding-schemes/src/folding/circuits/cyclefold.rs index 3d0e5f8..522971e 100644 --- a/folding-schemes/src/folding/circuits/cyclefold.rs +++ b/folding-schemes/src/folding/circuits/cyclefold.rs @@ -1,23 +1,13 @@ /// Contains [CycleFold](https://eprint.iacr.org/2023/1192.pdf) related circuits and functions that /// are shared across the different folding schemes -use ark_crypto_primitives::{ - crh::{ - poseidon::constraints::{CRHGadget, CRHParametersVar}, - CRHSchemeGadget, - }, - sponge::{ - constraints::CryptographicSpongeVar, - poseidon::{constraints::PoseidonSpongeVar, PoseidonConfig, PoseidonSponge}, - Absorb, CryptographicSponge, - }, -}; -use ark_ec::{AffineRepr, CurveGroup, Group}; -use ark_ff::{BigInteger, Field, PrimeField, ToConstraintField}; +use ark_crypto_primitives::sponge::{Absorb, CryptographicSponge}; +use ark_ec::{CurveGroup, Group}; +use ark_ff::{BigInteger, PrimeField}; use ark_r1cs_std::{ alloc::{AllocVar, AllocationMode}, boolean::Boolean, eq::EqGadget, - fields::{fp::FpVar, FieldVar}, + fields::fp::FpVar, groups::GroupOpsBounds, prelude::CurveVar, ToConstraintFieldGadget, @@ -26,7 +16,7 @@ use ark_relations::r1cs::{ ConstraintSynthesizer, ConstraintSystem, ConstraintSystemRef, Namespace, SynthesisError, }; use ark_std::fmt::Debug; -use ark_std::{One, Zero}; +use ark_std::Zero; use core::{borrow::Borrow, marker::PhantomData}; use super::{nonnative::uint::NonNativeUintVar, CF2}; @@ -35,6 +25,7 @@ use crate::commitment::CommitmentScheme; use crate::constants::N_BITS_RO; use crate::folding::nova::{nifs::NIFS, CommittedInstance, Witness}; use crate::frontend::FCircuit; +use crate::transcript::{AbsorbNonNativeGadget, Transcript, TranscriptVar}; use crate::Error; /// Public inputs length for the CycleFoldCircuit: |[r, p1.x,y, p2.x,y, p3.x,y]| @@ -77,7 +68,7 @@ where } } -impl ToConstraintFieldGadget> for CycleFoldCommittedInstanceVar +impl AbsorbNonNativeGadget for CycleFoldCommittedInstanceVar where C: CurveGroup, GC: CurveVar> + ToConstraintFieldGadget>, @@ -87,25 +78,24 @@ where /// Extracts the underlying field elements from `CycleFoldCommittedInstanceVar`, in the order /// of `u`, `x`, `cmE.x`, `cmE.y`, `cmW.x`, `cmW.y`, `cmE.is_inf || cmW.is_inf` (|| is for /// concat). - fn to_constraint_field(&self) -> Result>>, SynthesisError> { + fn to_native_sponge_field_elements(&self) -> Result>>, SynthesisError> { let mut cmE_elems = self.cmE.to_constraint_field()?; let mut cmW_elems = self.cmW.to_constraint_field()?; - let cmE_is_inf = cmE_elems.pop().unwrap(); - let cmW_is_inf = cmW_elems.pop().unwrap(); - // Concatenate `cmE_is_inf` and `cmW_is_inf` to save constraints for CRHGadget::evaluate - let is_inf = cmE_is_inf.double()? + cmW_is_inf; + // See `transcript/poseidon.rs: TranscriptVar::absorb_point` for details + // why the last element is unnecessary. + cmE_elems.pop(); + cmW_elems.pop(); Ok([ - self.u.to_constraint_field()?, + self.u.to_native_sponge_field_elements()?, self.x .iter() - .map(|i| i.to_constraint_field()) + .map(|i| i.to_native_sponge_field_elements()) .collect::, _>>()? .concat(), cmE_elems, cmW_elems, - vec![is_inf], ] .concat()) } @@ -124,16 +114,16 @@ where /// parameters, so they can be reused in other gadgets avoiding recalculating (reconstraining) /// them. #[allow(clippy::type_complexity)] - pub fn hash( + pub fn hash, S>>( self, - crh_params: &CRHParametersVar>, + sponge: &T, pp_hash: FpVar>, // public params hash ) -> Result<(FpVar>, Vec>>), SynthesisError> { - let U_vec = self.to_constraint_field()?; - Ok(( - CRHGadget::evaluate(crh_params, &[vec![pp_hash], U_vec.clone()].concat())?, - U_vec, - )) + let mut sponge = sponge.clone(); + let U_vec = self.to_native_sponge_field_elements()?; + sponge.absorb(&pp_hash)?; + sponge.absorb(&U_vec)?; + Ok((sponge.squeeze_field_elements(1)?.pop().unwrap(), U_vec)) } } @@ -254,65 +244,33 @@ where ::BaseField: Absorb, for<'a> &'a GC: GroupOpsBounds<'a, C, GC>, { - pub fn get_challenge_native( - poseidon_config: &PoseidonConfig, + pub fn get_challenge_native>( + transcript: &mut T, pp_hash: C::BaseField, // public params hash U_i: CommittedInstance, u_i: CommittedInstance, cmT: C, - ) -> Result, Error> { - let mut sponge = PoseidonSponge::::new(poseidon_config); - - let mut U_vec = U_i.to_field_elements().unwrap(); - let mut u_vec = u_i.to_field_elements().unwrap(); - let (cmT_x, cmT_y, cmT_is_inf) = match cmT.into_affine().xy() { - Some((&x, &y)) => (x, y, C::BaseField::zero()), - None => ( - C::BaseField::zero(), - C::BaseField::zero(), - C::BaseField::one(), - ), - }; - - let U_cm_is_inf = U_vec.pop().unwrap(); - let u_cm_is_inf = u_vec.pop().unwrap(); - - // Concatenate `U_i.cmE_is_inf`, `U_i.cmW_is_inf`, `u_i.cmE_is_inf`, `u_i.cmW_is_inf`, `cmT_is_inf` - // 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 = [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) + ) -> Vec { + transcript.absorb(&pp_hash); + transcript.absorb_nonnative(&U_i); + transcript.absorb_nonnative(&u_i); + transcript.absorb_point(&cmT); + transcript.squeeze_bits(N_BITS_RO) } // compatible with the native get_challenge_native - pub fn get_challenge_gadget( - cs: ConstraintSystemRef, - poseidon_config: &PoseidonConfig, + pub fn get_challenge_gadget>( + transcript: &mut T, pp_hash: FpVar, // public params hash - mut U_i_vec: Vec>, + U_i_vec: Vec>, u_i: CycleFoldCommittedInstanceVar, cmT: GC, ) -> Result>, SynthesisError> { - let mut sponge = PoseidonSpongeVar::::new(cs, poseidon_config); - - let mut u_i_vec = u_i.to_constraint_field()?; - let mut cmT_vec = cmT.to_constraint_field()?; - - let U_cm_is_inf = U_i_vec.pop().unwrap(); - let u_cm_is_inf = u_i_vec.pop().unwrap(); - let cmT_is_inf = cmT_vec.pop().unwrap(); - - // Concatenate `U_i.cmE_is_inf`, `U_i.cmW_is_inf`, `u_i.cmE_is_inf`, `u_i.cmW_is_inf`, `cmT_is_inf` - // 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 = [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) + transcript.absorb(&pp_hash)?; + transcript.absorb(&U_i_vec)?; + transcript.absorb_nonnative(&u_i)?; + transcript.absorb_point(&cmT)?; + transcript.squeeze_bits(N_BITS_RO) } } @@ -383,7 +341,7 @@ where #[allow(clippy::type_complexity)] #[allow(clippy::too_many_arguments)] pub fn fold_cyclefold_circuit( - poseidon_config: &PoseidonConfig, + transcript: &mut impl Transcript, cf_r1cs: R1CS, cf_cs_params: CS2::ProverParams, pp_hash: C1::ScalarField, // public params hash @@ -445,12 +403,12 @@ where )?; let cf_r_bits = CycleFoldChallengeGadget::::get_challenge_native( - poseidon_config, + transcript, pp_hash, cf_U_i.clone(), cf_u_i.clone(), cf_cmT, - )?; + ); let cf_r_Fq = C1::BaseField::from_bigint(BigInteger::from_bits_le(&cf_r_bits)) .expect("cf_r_bits out of bounds"); @@ -463,9 +421,11 @@ where #[cfg(test)] pub mod tests { use ark_bn254::{constraints::GVar, Fq, Fr, G1Projective as Projective}; - use ark_ff::BigInteger; + use ark_crypto_primitives::sponge::{ + constraints::CryptographicSpongeVar, + poseidon::{constraints::PoseidonSpongeVar, PoseidonSponge}, + }; use ark_r1cs_std::R1CSVar; - use ark_relations::r1cs::ConstraintSystem; use ark_std::UniformRand; use super::*; @@ -583,6 +543,7 @@ pub mod tests { fn test_cyclefold_challenge_gadget() { let mut rng = ark_std::test_rng(); let poseidon_config = poseidon_canonical_config::(); + let mut transcript = PoseidonSponge::::new(&poseidon_config); let u_i = CommittedInstance:: { cmE: Projective::zero(), // zero on purpose, so we test also the zero point case @@ -605,13 +566,12 @@ pub mod tests { // compute the challenge natively let pp_hash = Fq::from(42u32); // only for test let r_bits = CycleFoldChallengeGadget::::get_challenge_native( - &poseidon_config, + &mut transcript, pp_hash, U_i.clone(), u_i.clone(), cmT, - ) - .unwrap(); + ); let cs = ConstraintSystem::::new_ref(); let u_iVar = @@ -625,13 +585,14 @@ pub mod tests { }) .unwrap(); let cmTVar = GVar::new_witness(cs.clone(), || Ok(cmT)).unwrap(); + let mut transcript_var = + PoseidonSpongeVar::::new(ConstraintSystem::::new_ref(), &poseidon_config); let pp_hashVar = FpVar::::new_witness(cs.clone(), || Ok(pp_hash)).unwrap(); let r_bitsVar = CycleFoldChallengeGadget::::get_challenge_gadget( - cs.clone(), - &poseidon_config, + &mut transcript_var, pp_hashVar, - U_iVar.to_constraint_field().unwrap(), + U_iVar.to_native_sponge_field_elements().unwrap(), u_iVar, cmTVar, ) @@ -649,6 +610,7 @@ pub mod tests { fn test_cyclefold_hash_gadget() { let mut rng = ark_std::test_rng(); let poseidon_config = poseidon_canonical_config::(); + let sponge = PoseidonSponge::::new(&poseidon_config); let U_i = CommittedInstance:: { cmE: Projective::rand(&mut rng), @@ -659,7 +621,7 @@ pub mod tests { .collect(), }; let pp_hash = Fq::from(42u32); // only for test - let h = U_i.hash_cyclefold(&poseidon_config, pp_hash).unwrap(); + let h = U_i.hash_cyclefold(&sponge, pp_hash); let cs = ConstraintSystem::::new_ref(); let U_iVar = @@ -670,7 +632,7 @@ pub mod tests { 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(), + &PoseidonSpongeVar::new(cs.clone(), &poseidon_config), pp_hashVar, ) .unwrap(); diff --git a/folding-schemes/src/folding/circuits/nonnative/affine.rs b/folding-schemes/src/folding/circuits/nonnative/affine.rs index 05ee6b1..f4ec661 100644 --- a/folding-schemes/src/folding/circuits/nonnative/affine.rs +++ b/folding-schemes/src/folding/circuits/nonnative/affine.rs @@ -1,5 +1,4 @@ use ark_ec::{AffineRepr, CurveGroup}; -use ark_ff::PrimeField; use ark_r1cs_std::{ alloc::{AllocVar, AllocationMode}, fields::fp::FpVar, @@ -9,16 +8,15 @@ use ark_relations::r1cs::{Namespace, SynthesisError}; use ark_std::Zero; use core::borrow::Borrow; +use crate::transcript::{AbsorbNonNative, AbsorbNonNativeGadget}; + use super::uint::{nonnative_field_to_field_elements, NonNativeUintVar}; /// NonNativeAffineVar represents an elliptic curve point in Affine representation in the non-native /// field, over the constraint field. It is not intended to perform operations, but just to contain /// the affine coordinates in order to perform hash operations of the point. #[derive(Debug, Clone)] -pub struct NonNativeAffineVar -where - ::BaseField: PrimeField, -{ +pub struct NonNativeAffineVar { pub x: NonNativeUintVar, pub y: NonNativeUintVar, } @@ -26,7 +24,6 @@ where impl AllocVar for NonNativeAffineVar where C: CurveGroup, - ::BaseField: PrimeField, { fn new_variable>( cs: impl Into>, @@ -37,21 +34,18 @@ where let cs = cs.into(); let affine = val.borrow().into_affine(); - let zero_point = (&C::BaseField::zero(), &C::BaseField::zero()); - let xy = affine.xy().unwrap_or(zero_point); + let zero = (&C::BaseField::zero(), &C::BaseField::zero()); + let (x, y) = affine.xy().unwrap_or(zero); - let x = NonNativeUintVar::new_variable(cs.clone(), || Ok(*xy.0), mode)?; - let y = NonNativeUintVar::new_variable(cs.clone(), || Ok(*xy.1), mode)?; + let x = NonNativeUintVar::new_variable(cs.clone(), || Ok(*x), mode)?; + let y = NonNativeUintVar::new_variable(cs.clone(), || Ok(*y), mode)?; Ok(Self { x, y }) }) } } -impl ToConstraintFieldGadget for NonNativeAffineVar -where - ::BaseField: PrimeField, -{ +impl ToConstraintFieldGadget for NonNativeAffineVar { // Used for converting `NonNativeAffineVar` to a vector of `FpVar` with minimum length in // the circuit. fn to_constraint_field(&self) -> Result>, SynthesisError> { @@ -63,50 +57,50 @@ where /// The out-circuit counterpart of `NonNativeAffineVar::to_constraint_field` #[allow(clippy::type_complexity)] -pub fn nonnative_affine_to_field_elements( +fn nonnative_affine_to_field_elements( p: C, -) -> Result<(Vec, Vec), SynthesisError> -where - ::BaseField: PrimeField, -{ +) -> (Vec, Vec) { let affine = p.into_affine(); - if affine.is_zero() { - let x = nonnative_field_to_field_elements(&C::BaseField::zero()); - let y = nonnative_field_to_field_elements(&C::BaseField::zero()); - return Ok((x, y)); - } + let zero = (&C::BaseField::zero(), &C::BaseField::zero()); + let (x, y) = affine.xy().unwrap_or(zero); - let (x, y) = affine.xy().unwrap(); let x = nonnative_field_to_field_elements(x); let y = nonnative_field_to_field_elements(y); - Ok((x, y)) + (x, y) } -impl NonNativeAffineVar -where - ::BaseField: PrimeField, -{ - // A wrapper of `point_to_nonnative_limbs_custom_opt` with constraints-focused optimization - // type (which is the default optimization type for arkworks' Groth16). - // Used for extracting a list of field elements of type `C::ScalarField` from the public input +impl NonNativeAffineVar { + // Extracts a list of field elements of type `C::ScalarField` from the public input // `p`, in exactly the same way as how `NonNativeAffineVar` is represented as limbs of type // `FpVar` in-circuit. #[allow(clippy::type_complexity)] pub fn inputize(p: C) -> Result<(Vec, Vec), SynthesisError> { let affine = p.into_affine(); - if affine.is_zero() { - let x = NonNativeUintVar::inputize(C::BaseField::zero()); - let y = NonNativeUintVar::inputize(C::BaseField::zero()); - return Ok((x, y)); - } + let zero = (&C::BaseField::zero(), &C::BaseField::zero()); + let (x, y) = affine.xy().unwrap_or(zero); - let (x, y) = affine.xy().unwrap(); let x = NonNativeUintVar::inputize(*x); let y = NonNativeUintVar::inputize(*y); Ok((x, y)) } } +impl AbsorbNonNative for C { + fn to_native_sponge_field_elements(&self, dest: &mut Vec) { + let (x, y) = nonnative_affine_to_field_elements(*self); + dest.extend(x); + dest.extend(y); + } +} + +impl AbsorbNonNativeGadget for NonNativeAffineVar { + fn to_native_sponge_field_elements( + &self, + ) -> Result>, SynthesisError> { + self.to_constraint_field() + } +} + #[cfg(test)] mod tests { use super::*; @@ -132,7 +126,7 @@ mod tests { let mut rng = ark_std::test_rng(); let p = Projective::rand(&mut rng); let pVar = NonNativeAffineVar::::new_witness(cs.clone(), || Ok(p)).unwrap(); - let (x, y) = nonnative_affine_to_field_elements(p).unwrap(); + let (x, y) = nonnative_affine_to_field_elements(p); assert_eq!( pVar.to_constraint_field().unwrap().value().unwrap(), [x, y].concat() diff --git a/folding-schemes/src/folding/circuits/nonnative/uint.rs b/folding-schemes/src/folding/circuits/nonnative/uint.rs index 151ed2c..2b32177 100644 --- a/folding-schemes/src/folding/circuits/nonnative/uint.rs +++ b/folding-schemes/src/folding/circuits/nonnative/uint.rs @@ -3,7 +3,7 @@ use std::{ cmp::{max, min}, }; -use ark_ff::{BigInteger, One, PrimeField, Zero}; +use ark_ff::{BigInteger, Field, One, PrimeField, Zero}; use ark_r1cs_std::{ alloc::{AllocVar, AllocationMode}, boolean::Boolean, @@ -16,7 +16,10 @@ use ark_relations::r1cs::{ConstraintSystemRef, Namespace, SynthesisError}; use num_bigint::BigUint; use num_integer::Integer; -use crate::utils::gadgets::{MatrixGadget, SparseMatrixVar, VectorGadget}; +use crate::{ + transcript::{AbsorbNonNative, AbsorbNonNativeGadget}, + utils::gadgets::{MatrixGadget, SparseMatrixVar, VectorGadget}, +}; /// `LimbVar` represents a single limb of a non-native unsigned integer in the /// circuit. @@ -229,7 +232,7 @@ impl AllocVar for NonNativeUintVar { } } -impl AllocVar for NonNativeUintVar { +impl AllocVar for NonNativeUintVar { fn new_variable>( cs: impl Into>, f: impl FnOnce() -> Result, @@ -237,7 +240,8 @@ impl AllocVar for NonNativeUintVar { ) -> Result { let cs = cs.into().cs(); let v = f()?; - let v = v.borrow(); + assert_eq!(G::extension_degree(), 1); + let v = v.borrow().to_base_prime_field_elements().next().unwrap(); let mut limbs = vec![]; @@ -256,8 +260,12 @@ impl AllocVar for NonNativeUintVar { } impl NonNativeUintVar { - pub fn inputize(x: T) -> Vec { - x.into_bigint() + pub fn inputize(x: T) -> Vec { + assert_eq!(T::extension_degree(), 1); + x.to_base_prime_field_elements() + .next() + .unwrap() + .into_bigint() .to_bits_le() .chunks(Self::bits_per_limb()) .map(|chunk| F::from_bigint(F::BigInt::from_bits_le(chunk)).unwrap()) @@ -802,14 +810,40 @@ impl]>> From for NonNativeUintVar { } } +// If we impl `AbsorbNonNative` directly for `PrimeField`, rustc will complain +// that this impl conflicts with the impl for `CurveGroup`. +// Therefore, we instead impl `AbsorbNonNative` for a slice of `PrimeField` as a +// workaround. +impl AbsorbNonNative + for [TargetField] +{ + fn to_native_sponge_field_elements(&self, dest: &mut Vec) { + self.iter() + .for_each(|x| dest.extend(&nonnative_field_to_field_elements(x))); + } +} + +impl AbsorbNonNativeGadget for NonNativeUintVar { + fn to_native_sponge_field_elements(&self) -> Result>, SynthesisError> { + self.to_constraint_field() + } +} + /// The out-circuit counterpart of `NonNativeUintVar::to_constraint_field` -pub fn nonnative_field_to_field_elements( +pub(super) fn nonnative_field_to_field_elements( f: &TargetField, ) -> Vec { - let bits = f.into_bigint().to_bits_le(); + assert_eq!(TargetField::extension_degree(), 1); + let bits = f + .to_base_prime_field_elements() + .next() + .unwrap() + .into_bigint() + .to_bits_le(); let bits_per_limb = BaseField::MODULUS_BIT_SIZE as usize - 1; - let num_limbs = (TargetField::MODULUS_BIT_SIZE as usize).div_ceil(bits_per_limb); + let num_limbs = + (TargetField::BasePrimeField::MODULUS_BIT_SIZE as usize).div_ceil(bits_per_limb); let mut limbs = bits .chunks(bits_per_limb) @@ -897,7 +931,6 @@ mod tests { use std::error::Error; use super::*; - use ark_ff::Field; use ark_pallas::{Fq, Fr}; use ark_relations::r1cs::ConstraintSystem; use ark_std::{test_rng, UniformRand}; diff --git a/folding-schemes/src/folding/circuits/sum_check.rs b/folding-schemes/src/folding/circuits/sum_check.rs index f06b4f9..75c7b43 100644 --- a/folding-schemes/src/folding/circuits/sum_check.rs +++ b/folding-schemes/src/folding/circuits/sum_check.rs @@ -3,8 +3,7 @@ /// - Typings to better stick to ark_poly's API /// - Uses `folding-schemes`' own `TranscriptVar` trait and `PoseidonTranscriptVar` struct /// - API made closer to gadgets found in `folding-schemes` -use ark_crypto_primitives::sponge::Absorb; -use ark_ec::{CurveGroup, Group}; +use ark_crypto_primitives::sponge::{poseidon::PoseidonSponge, Absorb, CryptographicSponge}; use ark_ff::PrimeField; use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial}; use ark_r1cs_std::{ @@ -19,7 +18,7 @@ use std::{borrow::Borrow, marker::PhantomData}; use crate::utils::espresso::sum_check::SumCheck; use crate::utils::virtual_polynomial::VPAuxInfo; use crate::{ - transcript::{poseidon::PoseidonTranscript, TranscriptVar}, + transcript::TranscriptVar, utils::sum_check::{structs::IOPProof, IOPSumCheck}, }; @@ -82,35 +81,27 @@ impl DensePolynomialVar { } #[derive(Clone, Debug)] -pub struct IOPProofVar { +pub struct IOPProofVar { // We have to be generic over a CurveGroup because instantiating a IOPProofVar will call IOPSumCheck which requires a CurveGroup - pub proofs: Vec>, // = IOPProof.proofs - pub claim: FpVar, + pub proofs: Vec>, + pub claim: FpVar, } -impl AllocVar, C::ScalarField> for IOPProofVar -where - ::ScalarField: Absorb, -{ - fn new_variable>>( - cs: impl Into>, +impl AllocVar, F> for IOPProofVar { + fn new_variable>>( + cs: impl Into>, f: impl FnOnce() -> Result, mode: AllocationMode, ) -> Result { f().and_then(|c| { let cs = cs.into(); - let cp: &IOPProof = c.borrow(); - let claim = IOPSumCheck::>::extract_sum(cp); - let claim = FpVar::::new_variable(cs.clone(), || Ok(claim), mode)?; - let mut proofs = - Vec::>::with_capacity(cp.proofs.len()); + let cp: &IOPProof = c.borrow(); + let claim = IOPSumCheck::>::extract_sum(cp); + let claim = FpVar::::new_variable(cs.clone(), || Ok(claim), mode)?; + let mut proofs = Vec::>::with_capacity(cp.proofs.len()); for proof in cp.proofs.iter() { let poly = DensePolynomial::from_coefficients_slice(&proof.coeffs); - let proof = DensePolynomialVar::::new_variable( - cs.clone(), - || Ok(poly), - mode, - )?; + let proof = DensePolynomialVar::::new_variable(cs.clone(), || Ok(poly), mode)?; proofs.push(proof); } Ok(Self { proofs, claim }) @@ -149,28 +140,28 @@ impl AllocVar, F> for VPAuxInfoVar { } #[derive(Debug, Clone)] -pub struct SumCheckVerifierGadget { - _f: PhantomData, +pub struct SumCheckVerifierGadget { + _f: PhantomData, } -impl SumCheckVerifierGadget { +impl SumCheckVerifierGadget { #[allow(clippy::type_complexity)] - pub fn verify( - iop_proof_var: &IOPProofVar, - poly_aux_info_var: &VPAuxInfoVar, - transcript_var: &mut impl TranscriptVar, - enabled: Boolean, - ) -> Result<(Vec>, Vec>), SynthesisError> { + pub fn verify>( + iop_proof_var: &IOPProofVar, + poly_aux_info_var: &VPAuxInfoVar, + transcript_var: &mut T, + enabled: Boolean, + ) -> Result<(Vec>, Vec>), SynthesisError> { let mut e_vars = vec![iop_proof_var.claim.clone()]; - let mut r_vars: Vec> = Vec::new(); - transcript_var.absorb(poly_aux_info_var.num_variables.clone())?; - transcript_var.absorb(poly_aux_info_var.max_degree.clone())?; + let mut r_vars: Vec> = Vec::new(); + transcript_var.absorb(&poly_aux_info_var.num_variables)?; + transcript_var.absorb(&poly_aux_info_var.max_degree)?; for poly_var in iop_proof_var.proofs.iter() { let res = poly_var.eval_at_one() + poly_var.eval_at_zero(); let e_var = e_vars.last().ok_or(SynthesisError::Unsatisfiable)?; res.conditional_enforce_equal(e_var, &enabled)?; - transcript_var.absorb_vec(&poly_var.coeffs)?; + transcript_var.absorb(&poly_var.coeffs)?; let r_i_var = transcript_var.get_challenge()?; e_vars.push(poly_var.evaluate(&r_i_var)); r_vars.push(r_i_var); @@ -182,49 +173,35 @@ impl SumCheckVerifierGadget { #[cfg(test)] mod tests { - use ark_crypto_primitives::sponge::{poseidon::PoseidonConfig, Absorb}; - use ark_ec::CurveGroup; - use ark_ff::Field; - use ark_pallas::{Fr, Projective}; - use ark_poly::{ - univariate::DensePolynomial, DenseMultilinearExtension, DenseUVPolynomial, - MultilinearExtension, Polynomial, + use ark_crypto_primitives::sponge::{ + constraints::CryptographicSpongeVar, + poseidon::{constraints::PoseidonSpongeVar, PoseidonConfig}, }; - use ark_r1cs_std::{alloc::AllocVar, R1CSVar}; + use ark_pallas::Fr; + use ark_poly::{DenseMultilinearExtension, MultilinearExtension, Polynomial}; + use ark_r1cs_std::R1CSVar; use ark_relations::r1cs::ConstraintSystem; use std::sync::Arc; use super::*; use crate::{ - folding::circuits::sum_check::{IOPProofVar, VPAuxInfoVar}, - transcript::{ - poseidon::{poseidon_canonical_config, PoseidonTranscript, PoseidonTranscriptVar}, - Transcript, TranscriptVar, - }, - utils::{ - sum_check::{structs::IOPProof, IOPSumCheck, SumCheck}, - virtual_polynomial::VirtualPolynomial, - }, + transcript::poseidon::poseidon_canonical_config, + utils::virtual_polynomial::VirtualPolynomial, }; pub type TestSumCheckProof = (VirtualPolynomial, PoseidonConfig, IOPProof); /// Primarily used for testing the sumcheck gadget /// Returns a random virtual polynomial, the poseidon config used and the associated sumcheck proof - pub fn get_test_sumcheck_proof( + pub fn get_test_sumcheck_proof( num_vars: usize, - ) -> TestSumCheckProof - where - ::ScalarField: Absorb, - { + ) -> TestSumCheckProof { let mut rng = ark_std::test_rng(); - let poseidon_config: PoseidonConfig = - poseidon_canonical_config::(); - let mut poseidon_transcript_prove = PoseidonTranscript::::new(&poseidon_config); + let poseidon_config: PoseidonConfig = poseidon_canonical_config::(); + let mut poseidon_transcript_prove = PoseidonSponge::::new(&poseidon_config); let poly_mle = DenseMultilinearExtension::rand(num_vars, &mut rng); - let virtual_poly = - VirtualPolynomial::new_from_mle(&Arc::new(poly_mle), C::ScalarField::ONE); - let sum_check: IOPProof = IOPSumCheck::>::prove( + let virtual_poly = VirtualPolynomial::new_from_mle(&Arc::new(poly_mle), F::ONE); + let sum_check: IOPProof = IOPSumCheck::>::prove( &virtual_poly, &mut poseidon_transcript_prove, ) @@ -237,15 +214,15 @@ mod tests { for num_vars in 1..15 { let cs = ConstraintSystem::::new_ref(); let (virtual_poly, poseidon_config, sum_check) = - get_test_sumcheck_proof::(num_vars); - let mut poseidon_var: PoseidonTranscriptVar = - PoseidonTranscriptVar::new(cs.clone(), &poseidon_config); + get_test_sumcheck_proof::(num_vars); + let mut poseidon_var: PoseidonSpongeVar = + PoseidonSpongeVar::new(cs.clone(), &poseidon_config); let iop_proof_var = - IOPProofVar::::new_witness(cs.clone(), || Ok(&sum_check)).unwrap(); + IOPProofVar::::new_witness(cs.clone(), || Ok(&sum_check)).unwrap(); let poly_aux_info_var = VPAuxInfoVar::::new_witness(cs.clone(), || Ok(virtual_poly.aux_info)).unwrap(); let enabled = Boolean::::new_witness(cs.clone(), || Ok(true)).unwrap(); - let res = SumCheckVerifierGadget::::verify( + let res = SumCheckVerifierGadget::::verify( &iop_proof_var, &poly_aux_info_var, &mut poseidon_var, @@ -256,8 +233,7 @@ mod tests { let (circuit_evals, r_challenges) = res.unwrap(); // 1. assert claim from circuit is equal to the one from the sum-check - let claim: Fr = - IOPSumCheck::>::extract_sum(&sum_check); + let claim: Fr = IOPSumCheck::>::extract_sum(&sum_check); assert_eq!(circuit_evals[0].value().unwrap(), claim); // 2. assert that all in-circuit evaluations are equal to the ones from the sum-check diff --git a/folding-schemes/src/folding/hypernova/cccs.rs b/folding-schemes/src/folding/hypernova/cccs.rs index c81a189..6be8ab6 100644 --- a/folding-schemes/src/folding/hypernova/cccs.rs +++ b/folding-schemes/src/folding/hypernova/cccs.rs @@ -1,3 +1,4 @@ +use ark_crypto_primitives::sponge::Absorb; use ark_ec::CurveGroup; use ark_ff::PrimeField; use ark_std::One; @@ -9,6 +10,7 @@ use ark_std::rand::Rng; use super::Witness; use crate::arith::{ccs::CCS, Arith}; use crate::commitment::CommitmentScheme; +use crate::transcript::AbsorbNonNative; 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, VirtualPolynomial}; @@ -118,6 +120,26 @@ impl CCCS { } } +impl Absorb for CCCS +where + C::ScalarField: Absorb, +{ + fn to_sponge_bytes(&self, _dest: &mut Vec) { + // This is never called + unimplemented!() + } + + fn to_sponge_field_elements(&self, dest: &mut Vec) { + // We cannot call `to_native_sponge_field_elements(dest)` directly, as + // `to_native_sponge_field_elements` needs `F` to be `C::ScalarField`, + // but here `F` is a generic `PrimeField`. + self.C + .to_native_sponge_field_elements_as_vec() + .to_sponge_field_elements(dest); + self.x.to_sponge_field_elements(dest); + } +} + #[cfg(test)] pub mod tests { use ark_pallas::Fr; diff --git a/folding-schemes/src/folding/hypernova/circuits.rs b/folding-schemes/src/folding/hypernova/circuits.rs index bdc9bd3..17b0442 100644 --- a/folding-schemes/src/folding/hypernova/circuits.rs +++ b/folding-schemes/src/folding/hypernova/circuits.rs @@ -1,7 +1,8 @@ /// Implementation of [HyperNova](https://eprint.iacr.org/2023/573.pdf) circuits -use ark_crypto_primitives::crh::{ - poseidon::constraints::{CRHGadget, CRHParametersVar}, - CRHSchemeGadget, +use ark_crypto_primitives::sponge::{ + constraints::CryptographicSpongeVar, + poseidon::{constraints::PoseidonSpongeVar, PoseidonSponge}, + CryptographicSponge, }; use ark_crypto_primitives::sponge::{poseidon::PoseidonConfig, Absorb}; use ark_ec::{CurveGroup, Group}; @@ -45,10 +46,7 @@ use crate::utils::virtual_polynomial::VPAuxInfo; use crate::Error; use crate::{ arith::{ccs::CCS, r1cs::extract_r1cs}, - transcript::{ - poseidon::{PoseidonTranscript, PoseidonTranscriptVar}, - Transcript, TranscriptVar, - }, + transcript::TranscriptVar, }; /// Committed CCS instance @@ -142,12 +140,13 @@ where #[allow(clippy::type_complexity)] pub fn hash( self, - crh_params: &CRHParametersVar>, + sponge: &PoseidonSpongeVar>, pp_hash: FpVar>, i: FpVar>, z_0: Vec>>, z_i: Vec>>, ) -> Result<(FpVar>, Vec>>), SynthesisError> { + let mut sponge = sponge.clone(); let U_vec = [ self.C.to_constraint_field()?, vec![self.u], @@ -156,18 +155,19 @@ where self.v, ] .concat(); - let input = [vec![pp_hash, i], z_0, z_i, U_vec.clone()].concat(); - Ok(( - CRHGadget::::evaluate(crh_params, &input)?, - U_vec, - )) + sponge.absorb(&pp_hash)?; + sponge.absorb(&i)?; + sponge.absorb(&z_0)?; + sponge.absorb(&z_i)?; + sponge.absorb(&U_vec)?; + Ok((sponge.squeeze_field_elements(1)?.pop().unwrap(), U_vec)) } } /// ProofVar defines a multifolding proof #[derive(Debug)] pub struct ProofVar { - pub sc_proof: IOPProofVar, + pub sc_proof: IOPProofVar, #[allow(clippy::type_complexity)] pub sigmas_thetas: (Vec>>>, Vec>>>), } @@ -185,7 +185,7 @@ where f().and_then(|val| { let cs = cs.into(); - let sc_proof = IOPProofVar::::new_variable( + let sc_proof = IOPProofVar::::new_variable( cs.clone(), || Ok(val.borrow().sc_proof.clone()), mode, @@ -223,12 +223,11 @@ where /// Runs (in-circuit) the NIMFS.V, which outputs the new folded LCCCS instance together with /// the rho_bits, which will be used in other parts of the AugmentedFCircuit #[allow(clippy::type_complexity)] - pub fn verify( + pub fn verify>( cs: ConstraintSystemRef>, // only used the CCS params, not the matrices ccs: &CCS, - mut transcript: impl TranscriptVar, - + transcript: &mut T, running_instances: &[LCCCSVar], new_instances: &[CCCSVar], proof: ProofVar, @@ -244,24 +243,24 @@ where U_i.v.clone(), ] .concat(); - transcript.absorb_vec(&v)?; + transcript.absorb(&v)?; } for u_i in new_instances { let v = [u_i.C.to_constraint_field()?, u_i.x.clone()].concat(); - transcript.absorb_vec(&v)?; + transcript.absorb(&v)?; } // get the challenges let gamma_scalar_raw = C::ScalarField::from_le_bytes_mod_order(b"gamma"); let gamma_scalar: FpVar> = FpVar::>::new_constant(cs.clone(), gamma_scalar_raw)?; - transcript.absorb(gamma_scalar)?; + transcript.absorb(&gamma_scalar)?; let gamma: FpVar> = transcript.get_challenge()?; let beta_scalar_raw = C::ScalarField::from_le_bytes_mod_order(b"beta"); let beta_scalar: FpVar> = FpVar::>::new_constant(cs.clone(), beta_scalar_raw)?; - transcript.absorb(beta_scalar)?; + transcript.absorb(&beta_scalar)?; let beta: Vec>> = transcript.get_challenges(ccs.s)?; let vp_aux_info_raw = VPAuxInfo:: { @@ -283,10 +282,10 @@ where } // verify the interactive part of the sumcheck - let (e_vars, r_vars) = SumCheckVerifierGadget::::verify( + let (e_vars, r_vars) = SumCheckVerifierGadget::::verify( &proof.sc_proof, &vp_aux_info, - &mut transcript, + transcript, enabled.clone(), )?; @@ -312,7 +311,7 @@ where // get the folding challenge let rho_scalar_raw = C::ScalarField::from_le_bytes_mod_order(b"rho"); let rho_scalar: FpVar> = FpVar::>::new_constant(cs.clone(), rho_scalar_raw)?; - transcript.absorb(rho_scalar)?; + transcript.absorb(&rho_scalar)?; let rho_bits: Vec>> = transcript.get_challenge_nbits(N_BITS_RO)?; let rho = Boolean::le_bits_to_fp_var(&rho_bits)?; @@ -560,10 +559,10 @@ where let w_i = W_i.clone(); let u_i = CCCS::::dummy(ccs.l); - let mut transcript_p: PoseidonTranscript = - PoseidonTranscript::::new(&self.poseidon_config.clone()); + 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 (nimfs_proof, U_i1, _, _) = NIMFS::>::prove( + let (nimfs_proof, U_i1, _, _) = NIMFS::>::prove( &mut transcript_p, &ccs, &[U_i.clone()], @@ -671,10 +670,7 @@ where })?; let cf_cmT = GC2::new_witness(cs.clone(), || Ok(self.cf_cmT.unwrap_or_else(C2::zero)))?; - let crh_params = CRHParametersVar::::new_constant( - cs.clone(), - self.poseidon_config.clone(), - )?; + let sponge = PoseidonSpongeVar::::new(cs.clone(), &self.poseidon_config); // get z_{i+1} from the F circuit let i_usize = self.i_usize.unwrap_or(0); @@ -689,14 +685,14 @@ where // 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, + &sponge, 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, pp_hash.clone())?; + let (cf_u_i_x, cf_U_i_vec) = cf_U_i.clone().hash(&sponge, pp_hash.clone())?; // P.2. Construct u_i let u_i = CCCSVar:: { @@ -712,13 +708,12 @@ 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 = - PoseidonTranscriptVar::::new(cs.clone(), &self.poseidon_config); - transcript.absorb(pp_hash.clone())?; + 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(), - transcript, + &mut transcript, &[U_i.clone()], &[u_i.clone()], nimfs_proof, @@ -728,14 +723,14 @@ where // P.4.a compute and check the first output of F' let (u_i1_x, _) = U_i1.clone().hash( - &crh_params, + &sponge, 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, + &sponge, pp_hash.clone(), FpVar::>::one(), z_0.clone(), @@ -776,8 +771,7 @@ where // compute cf_r = H(cf_u_i, cf_U_i, cf_cmT) // cf_r_bits is denoted by rho* in the paper. let cf_r_bits = CycleFoldChallengeGadget::::get_challenge_gadget( - cs.clone(), - &self.poseidon_config, + &mut transcript, pp_hash.clone(), cf_U_i_vec, cf_u_i.clone(), @@ -802,10 +796,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, pp_hash.clone())?; + let (cf_u_i1_x, _) = cf_U_i1.clone().hash(&sponge, pp_hash.clone())?; let (cf_u_i1_x_base, _) = CycleFoldCommittedInstanceVar::new_constant(cs.clone(), cf_u_dummy)? - .hash(&crh_params, pp_hash)?; + .hash(&sponge, pp_hash)?; let cf_x = FpVar::new_input(cs.clone(), || { Ok(self.cf_x.unwrap_or(cf_u_i1_x_base.value()?)) })?; @@ -820,34 +814,23 @@ mod tests { use ark_bn254::{constraints::GVar, Fq, Fr, G1Projective as Projective}; use ark_ff::BigInteger; use ark_grumpkin::{constraints::GVar as GVar2, Projective as Projective2}; - use ark_r1cs_std::{alloc::AllocVar, fields::fp::FpVar, R1CSVar}; use ark_std::{test_rng, UniformRand}; use std::time::Instant; use super::*; use crate::{ arith::{ - ccs::{ - tests::{get_test_ccs, get_test_z}, - CCS, - }, + ccs::tests::{get_test_ccs, get_test_z}, r1cs::extract_w_x, }, commitment::{pedersen::Pedersen, CommitmentScheme}, folding::{ circuits::cyclefold::{fold_cyclefold_circuit, CycleFoldCircuit}, - hypernova::{ - nimfs::NIMFS, - utils::{compute_c, compute_sigmas_thetas}, - Witness, - }, - nova::{traits::NovaR1CS, CommittedInstance, Witness as NovaWitness}, + hypernova::utils::{compute_c, compute_sigmas_thetas}, + nova::{traits::NovaR1CS, Witness as NovaWitness}, }, frontend::tests::CubicFCircuit, - transcript::{ - poseidon::{poseidon_canonical_config, PoseidonTranscript, PoseidonTranscriptVar}, - Transcript, - }, + transcript::poseidon::poseidon_canonical_config, utils::get_cm_coordinates, }; @@ -996,12 +979,11 @@ mod tests { // Prover's transcript let poseidon_config = poseidon_canonical_config::(); - let mut transcript_p: PoseidonTranscript = - PoseidonTranscript::::new(&poseidon_config); + let mut transcript_p: PoseidonSponge = PoseidonSponge::::new(&poseidon_config); // Run the prover side of the multifolding let (proof, folded_lcccs, folded_witness, _) = - NIMFS::>::prove( + NIMFS::>::prove( &mut transcript_p, &ccs, &lcccs_instances, @@ -1012,11 +994,10 @@ mod tests { .unwrap(); // Verifier's transcript - let mut transcript_v: PoseidonTranscript = - PoseidonTranscript::::new(&poseidon_config); + let mut transcript_v: PoseidonSponge = PoseidonSponge::::new(&poseidon_config); // Run the verifier side of the multifolding - let folded_lcccs_v = NIMFS::>::verify( + let folded_lcccs_v = NIMFS::>::verify( &mut transcript_v, &ccs, &lcccs_instances, @@ -1039,13 +1020,13 @@ mod tests { .unwrap(); let proofVar = ProofVar::::new_witness(cs.clone(), || Ok(proof.clone())).unwrap(); - let transcriptVar = PoseidonTranscriptVar::::new(cs.clone(), &poseidon_config); + let mut transcriptVar = PoseidonSpongeVar::::new(cs.clone(), &poseidon_config); let enabled = Boolean::::new_witness(cs.clone(), || Ok(true)).unwrap(); let (folded_lcccsVar, _) = NIMFSGadget::::verify( cs.clone(), &ccs, - transcriptVar, + &mut transcriptVar, &lcccs_instancesVar, &cccs_instancesVar, proofVar, @@ -1061,6 +1042,7 @@ mod tests { pub fn test_lcccs_hash() { let mut rng = test_rng(); let poseidon_config = poseidon_canonical_config::(); + let sponge = PoseidonSponge::::new(&poseidon_config); let ccs = get_test_ccs(); let z1 = get_test_z::(3); @@ -1077,12 +1059,11 @@ mod tests { .unwrap(); let h = lcccs .clone() - .hash(&poseidon_config, pp_hash, i, z_0.clone(), z_i.clone()) - .unwrap(); + .hash(&sponge, pp_hash, i, z_0.clone(), z_i.clone()); let cs = ConstraintSystem::::new_ref(); - let crh_params = CRHParametersVar::::new_constant(cs.clone(), poseidon_config).unwrap(); + let spongeVar = PoseidonSpongeVar::::new(cs.clone(), &poseidon_config); 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(); @@ -1091,7 +1072,7 @@ mod tests { let (hVar, _) = lcccsVar .clone() .hash( - &crh_params, + &spongeVar, pp_hashVar, iVar.clone(), z_0Var.clone(), @@ -1108,6 +1089,7 @@ mod tests { pub fn test_augmented_f_circuit() { let mut rng = test_rng(); let poseidon_config = poseidon_canonical_config::(); + let sponge = PoseidonSponge::::new(&poseidon_config); let start = Instant::now(); let F_circuit = CubicFCircuit::::new(()).unwrap(); @@ -1161,15 +1143,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( - &poseidon_config, - pp_hash, - Fr::zero(), - z_0.clone(), - z_i.clone(), - ) - .unwrap(), - cf_U_i.hash_cyclefold(&poseidon_config, pp_hash).unwrap(), + U_i.hash(&sponge, pp_hash, Fr::zero(), z_0.clone(), z_i.clone()), + cf_U_i.hash_cyclefold(&sponge, pp_hash), ]; let n_steps: usize = 4; @@ -1185,19 +1160,11 @@ mod tests { W_i1 = Witness::::dummy(&ccs); U_i1 = LCCCS::dummy(ccs.l, ccs.t, ccs.s); - let u_i1_x = U_i1 - .hash( - &poseidon_config, - pp_hash, - Fr::one(), - z_0.clone(), - z_i1.clone(), - ) - .unwrap(); + let u_i1_x = U_i1.hash(&sponge, pp_hash, Fr::one(), z_0.clone(), z_i1.clone()); // 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, pp_hash).unwrap(); + let cf_u_i1_x = cf_U_i.hash_cyclefold(&sponge, pp_hash); augmented_f_circuit = AugmentedFCircuit::> { @@ -1225,12 +1192,12 @@ mod tests { cf_cmT: None, }; } else { - let mut transcript_p: PoseidonTranscript = - PoseidonTranscript::::new(&poseidon_config.clone()); + let mut transcript_p: PoseidonSponge = + PoseidonSponge::::new(&poseidon_config.clone()); transcript_p.absorb(&pp_hash); let (rho_bits, nimfs_proof); (nimfs_proof, U_i1, W_i1, rho_bits) = - NIMFS::>::prove( + NIMFS::>::prove( &mut transcript_p, &ccs, &[U_i.clone()], @@ -1243,15 +1210,8 @@ mod tests { // sanity check: check the folded instance relation U_i1.check_relation(&ccs, &W_i1).unwrap(); - let u_i1_x = U_i1 - .hash( - &poseidon_config, - pp_hash, - iFr + Fr::one(), - z_0.clone(), - z_i1.clone(), - ) - .unwrap(); + let u_i1_x = + U_i1.hash(&sponge, pp_hash, iFr + Fr::one(), z_0.clone(), z_i1.clone()); let rho_Fq = Fq::from_bigint(BigInteger::from_bits_le(&rho_bits)).unwrap(); // CycleFold part: @@ -1282,7 +1242,7 @@ mod tests { Pedersen, Pedersen, >( - &poseidon_config, + &mut transcript_p, cf_r1cs.clone(), cf_pedersen_params.clone(), pp_hash, @@ -1295,7 +1255,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, pp_hash).unwrap(); + let cf_u_i1_x = cf_U_i1.hash_cyclefold(&sponge, pp_hash); augmented_f_circuit = AugmentedFCircuit::> { @@ -1346,16 +1306,9 @@ mod tests { assert_eq!(u_i.x, r1cs_x_i1); 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, - 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, pp_hash).unwrap(); + let expected_u_i1_x = + U_i1.hash(&sponge, pp_hash, iFr + Fr::one(), z_0.clone(), z_i1.clone()); + let expected_cf_U_i1_x = cf_U_i.hash_cyclefold(&sponge, pp_hash); // 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 95fd937..801ec4e 100644 --- a/folding-schemes/src/folding/hypernova/lcccs.rs +++ b/folding-schemes/src/folding/hypernova/lcccs.rs @@ -1,7 +1,4 @@ -use ark_crypto_primitives::{ - crh::{poseidon::CRH, CRHScheme}, - sponge::{poseidon::PoseidonConfig, Absorb}, -}; +use ark_crypto_primitives::sponge::Absorb; use ark_ec::{CurveGroup, Group}; use ark_ff::PrimeField; use ark_poly::DenseMultilinearExtension; @@ -12,7 +9,7 @@ use ark_std::Zero; use super::Witness; use crate::arith::ccs::CCS; use crate::commitment::CommitmentScheme; -use crate::folding::circuits::nonnative::affine::nonnative_affine_to_field_elements; +use crate::transcript::{AbsorbNonNative, Transcript}; use crate::utils::mle::dense_vec_to_dense_mle; use crate::utils::vec::mat_vec_mul; use crate::Error; @@ -118,6 +115,29 @@ impl LCCCS { } } +impl Absorb for LCCCS +where + C::ScalarField: Absorb, +{ + fn to_sponge_bytes(&self, _dest: &mut Vec) { + // This is never called + unimplemented!() + } + + fn to_sponge_field_elements(&self, dest: &mut Vec) { + // We cannot call `to_native_sponge_field_elements(dest)` directly, as + // `to_native_sponge_field_elements` needs `F` to be `C::ScalarField`, + // but here `F` is a generic `PrimeField`. + self.C + .to_native_sponge_field_elements_as_vec() + .to_sponge_field_elements(dest); + self.u.to_sponge_field_elements(dest); + self.x.to_sponge_field_elements(dest); + self.r_x.to_sponge_field_elements(dest); + self.v.to_sponge_field_elements(dest); + } +} + impl LCCCS where ::ScalarField: Absorb, @@ -126,32 +146,21 @@ where /// [`LCCCS`].hash implements the committed instance hash compatible with the gadget /// implemented in nova/circuits.rs::CommittedInstanceVar.hash. /// Returns `H(i, z_0, z_i, U_i)`, where `i` can be `i` but also `i+1`, and `U_i` is the LCCCS. - pub fn hash( + pub fn hash>( &self, - poseidon_config: &PoseidonConfig, + sponge: &T, pp_hash: C::ScalarField, i: C::ScalarField, z_0: Vec, z_i: Vec, - ) -> Result { - let (C_x, C_y) = nonnative_affine_to_field_elements::(self.C)?; - - CRH::::evaluate( - poseidon_config, - vec![ - vec![pp_hash, i], - z_0, - z_i, - C_x, - C_y, - vec![self.u], - self.x.clone(), - self.r_x.clone(), - self.v.clone(), - ] - .concat(), - ) - .map_err(|e| Error::Other(e.to_string())) + ) -> C::ScalarField { + let mut sponge = sponge.clone(); + sponge.absorb(&pp_hash); + sponge.absorb(&i); + sponge.absorb(&z_0); + sponge.absorb(&z_i); + sponge.absorb(&self); + sponge.squeeze_field_elements(1)[0] } } @@ -161,7 +170,6 @@ pub mod tests { use ark_std::test_rng; use ark_std::One; use ark_std::UniformRand; - use ark_std::Zero; use std::sync::Arc; use super::*; diff --git a/folding-schemes/src/folding/hypernova/mod.rs b/folding-schemes/src/folding/hypernova/mod.rs index c1b5939..0a3db8c 100644 --- a/folding-schemes/src/folding/hypernova/mod.rs +++ b/folding-schemes/src/folding/hypernova/mod.rs @@ -1,5 +1,8 @@ /// Implements the scheme described in [HyperNova](https://eprint.iacr.org/2023/573.pdf) -use ark_crypto_primitives::sponge::{poseidon::PoseidonConfig, Absorb}; +use ark_crypto_primitives::sponge::{ + poseidon::{PoseidonConfig, PoseidonSponge}, + Absorb, CryptographicSponge, +}; use ark_ec::{CurveGroup, Group}; use ark_ff::{BigInteger, PrimeField}; use ark_r1cs_std::{groups::GroupOpsBounds, prelude::CurveVar, ToConstraintFieldGadget}; @@ -18,6 +21,10 @@ use cccs::CCCS; use lcccs::LCCCS; use nimfs::NIMFS; +use crate::arith::{ + ccs::CCS, + r1cs::{extract_w_x, R1CS}, +}; use crate::commitment::CommitmentScheme; use crate::folding::circuits::{ cyclefold::{fold_cyclefold_circuit, CycleFoldCircuit}, @@ -31,13 +38,6 @@ use crate::frontend::FCircuit; use crate::utils::{get_cm_coordinates, pp_hash}; use crate::Error; use crate::FoldingScheme; -use crate::{ - arith::{ - ccs::CCS, - r1cs::{extract_w_x, R1CS}, - }, - transcript::{poseidon::PoseidonTranscript, Transcript}, -}; /// Witness for the LCCCS & CCCS, containing the w vector, and the r_w used as randomness in the Pedersen commitment. #[derive(Debug, Clone, Eq, PartialEq)] @@ -233,6 +233,8 @@ where z_0: Vec, ) -> Result { let (pp, vp) = params; + // `sponge` is for digest computation. + let sponge = PoseidonSponge::::new(&pp.poseidon_config); // prepare the HyperNova's AugmentedFCircuit and CycleFold's circuits and obtain its CCS // and R1CS respectively @@ -258,13 +260,13 @@ where cf_r1cs.dummy_instance(); u_dummy.x = vec![ U_dummy.hash( - &pp.poseidon_config, + &sponge, pp_hash, C1::ScalarField::zero(), z_0.clone(), z_0.clone(), - )?, - cf_U_dummy.hash_cyclefold(&pp.poseidon_config, pp_hash)?, + ), + cf_U_dummy.hash_cyclefold(&sponge, pp_hash), ]; // W_dummy=W_0 is a 'dummy witness', all zeroes, but with the size corresponding to the @@ -299,6 +301,9 @@ where mut rng: impl RngCore, external_inputs: Vec, ) -> Result<(), Error> { + // `sponge` is for digest computation. + let sponge = PoseidonSponge::::new(&self.poseidon_config); + let augmented_f_circuit: AugmentedFCircuit; if self.z_i.len() != self.F.state_len() { @@ -339,18 +344,16 @@ where U_i1 = LCCCS::dummy(self.ccs.l, self.ccs.t, self.ccs.s); let u_i1_x = U_i1.hash( - &self.poseidon_config, + &sponge, self.pp_hash, C1::ScalarField::one(), self.z_0.clone(), z_i1.clone(), - )?; + ); // 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, self.pp_hash)?; + cf_u_i1_x = self.cf_U_i.hash_cyclefold(&sponge, self.pp_hash); augmented_f_circuit = AugmentedFCircuit:: { _c2: PhantomData, @@ -377,30 +380,31 @@ where cf_cmT: None, }; } else { - let mut transcript_p: PoseidonTranscript = - PoseidonTranscript::::new(&self.poseidon_config); + let mut transcript_p: PoseidonSponge = + PoseidonSponge::::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, - &self.ccs, - &[self.U_i.clone()], - &[self.u_i.clone()], - &[self.W_i.clone()], - &[self.w_i.clone()], - )?; + (nimfs_proof, U_i1, W_i1, rho_bits) = + NIMFS::>::prove( + &mut transcript_p, + &self.ccs, + &[self.U_i.clone()], + &[self.u_i.clone()], + &[self.W_i.clone()], + &[self.w_i.clone()], + )?; // sanity check: check the folded instance relation #[cfg(test)] U_i1.check_relation(&self.ccs, &W_i1)?; let u_i1_x = U_i1.hash( - &self.poseidon_config, + &sponge, self.pp_hash, self.i + C1::ScalarField::one(), self.z_0.clone(), z_i1.clone(), - )?; + ); let rho_Fq = C2::ScalarField::from_bigint(BigInteger::from_bits_le(&rho_bits)) .ok_or(Error::OutOfBounds)?; @@ -425,7 +429,7 @@ where let (_cf_w_i, cf_u_i, cf_W_i1, cf_U_i1, cf_cmT, _) = fold_cyclefold_circuit::( - &self.poseidon_config, + &mut transcript_p, self.cf_r1cs.clone(), self.cf_cs_params.clone(), self.pp_hash, @@ -435,7 +439,7 @@ where cf_circuit, )?; - cf_u_i1_x = cf_U_i1.hash_cyclefold(&self.poseidon_config, self.pp_hash)?; + cf_u_i1_x = cf_U_i1.hash_cyclefold(&sponge, self.pp_hash); augmented_f_circuit = AugmentedFCircuit:: { _c2: PhantomData, @@ -541,6 +545,8 @@ where } return Ok(()); } + // `sponge` is for digest computation. + let sponge = PoseidonSponge::::new(&vp.poseidon_config); let (U_i, W_i) = running_instance; let (u_i, w_i) = incoming_instance; @@ -553,12 +559,12 @@ where // 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, pp_hash, num_steps, z_0, z_i.clone())?; + let expected_u_i_x = U_i.hash(&sponge, 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, pp_hash)?; + let expected_cf_u_i_x = cf_U_i.hash_cyclefold(&sponge, pp_hash); 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 5e32d52..ab3bb52 100644 --- a/folding-schemes/src/folding/hypernova/nimfs.rs +++ b/folding-schemes/src/folding/hypernova/nimfs.rs @@ -13,7 +13,6 @@ use super::{ }; 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; use crate::utils::sum_check::structs::{IOPProof as SumCheckProof, IOPProverMessage}; use crate::utils::sum_check::{IOPSumCheck, SumCheck}; @@ -58,12 +57,12 @@ pub struct SigmasThetas(pub Vec>, pub Vec>); #[derive(Debug)] /// Implements the Non-Interactive Multi Folding Scheme described in section 5 of /// [HyperNova](https://eprint.iacr.org/2023/573.pdf) -pub struct NIMFS> { +pub struct NIMFS> { pub _c: PhantomData, pub _t: PhantomData, } -impl> NIMFS +impl> NIMFS where ::ScalarField: Absorb, C::BaseField: PrimeField, @@ -178,7 +177,7 @@ where /// contains the sumcheck proof and the helper sumcheck claim sigmas and thetas. #[allow(clippy::type_complexity)] pub fn prove( - transcript: &mut impl Transcript, + transcript: &mut impl Transcript, ccs: &CCS, running_instances: &[LCCCS], new_instances: &[CCCS], @@ -186,24 +185,8 @@ where w_cccs: &[Witness], ) -> Result<(NIMFSProof, LCCCS, Witness, Vec), Error> { // absorb instances to transcript - for U_i in running_instances { - let (C_x, C_y) = nonnative_affine_to_field_elements::(U_i.C)?; - let v = [ - C_x, - C_y, - vec![U_i.u], - U_i.x.clone(), - U_i.r_x.clone(), - U_i.v.clone(), - ] - .concat(); - transcript.absorb_vec(&v); - } - for u_i in new_instances { - let (C_x, C_y) = nonnative_affine_to_field_elements::(u_i.C)?; - let v = [C_x, C_y, u_i.x.clone()].concat(); - transcript.absorb_vec(&v); - } + transcript.absorb(&running_instances); + transcript.absorb(&new_instances); if running_instances.is_empty() { return Err(Error::Empty); @@ -247,7 +230,7 @@ where let g = compute_g(ccs, running_instances, &z_lcccs, &z_cccs, gamma, &beta)?; // Step 3: Run the sumcheck prover - let sumcheck_proof = IOPSumCheck::::prove(&g, transcript) + let sumcheck_proof = IOPSumCheck::::prove(&g, transcript) .map_err(|err| Error::SumCheckProveError(err.to_string()))?; // Step 2: dig into the sumcheck and extract r_x_prime @@ -290,31 +273,15 @@ where /// into a single LCCCS instance. /// Returns the folded LCCCS instance. pub fn verify( - transcript: &mut impl Transcript, + transcript: &mut impl Transcript, ccs: &CCS, running_instances: &[LCCCS], new_instances: &[CCCS], proof: NIMFSProof, ) -> Result, Error> { // absorb instances to transcript - for U_i in running_instances { - let (C_x, C_y) = nonnative_affine_to_field_elements::(U_i.C)?; - let v = [ - C_x, - C_y, - vec![U_i.u], - U_i.x.clone(), - U_i.r_x.clone(), - U_i.v.clone(), - ] - .concat(); - transcript.absorb_vec(&v); - } - for u_i in new_instances { - let (C_x, C_y) = nonnative_affine_to_field_elements::(u_i.C)?; - let v = [C_x, C_y, u_i.x.clone()].concat(); - transcript.absorb_vec(&v); - } + transcript.absorb(&running_instances); + transcript.absorb(&new_instances); if running_instances.is_empty() { return Err(Error::Empty); @@ -349,9 +316,13 @@ where } // Verify the interactive part of the sumcheck - let sumcheck_subclaim = - IOPSumCheck::::verify(sum_v_j_gamma, &proof.sc_proof, &vp_aux_info, transcript) - .map_err(|err| Error::SumCheckVerifyError(err.to_string()))?; + let sumcheck_subclaim = IOPSumCheck::::verify( + sum_v_j_gamma, + &proof.sc_proof, + &vp_aux_info, + transcript, + ) + .map_err(|err| Error::SumCheckVerifyError(err.to_string()))?; // Step 2: Dig into the sumcheck claim and extract the randomness used let r_x_prime = sumcheck_subclaim.point.clone(); @@ -413,7 +384,8 @@ pub mod tests { Arith, }; use crate::transcript::poseidon::poseidon_canonical_config; - use crate::transcript::poseidon::PoseidonTranscript; + use ark_crypto_primitives::sponge::poseidon::PoseidonSponge; + use ark_crypto_primitives::sponge::CryptographicSponge; use ark_std::test_rng; use ark_std::UniformRand; @@ -450,7 +422,7 @@ pub mod tests { let mut rng = test_rng(); let rho = Fr::rand(&mut rng); - let folded = NIMFS::>::fold( + let folded = NIMFS::>::fold( &[lcccs], &[cccs], &sigmas_thetas, @@ -458,8 +430,7 @@ pub mod tests { rho, ); - let w_folded = - NIMFS::>::fold_witness(&[w1], &[w2], rho); + let w_folded = NIMFS::>::fold_witness(&[w1], &[w2], rho); // check lcccs relation folded.check_relation(&ccs, &w_folded).unwrap(); @@ -491,13 +462,12 @@ pub mod tests { // Prover's transcript let poseidon_config = poseidon_canonical_config::(); - let mut transcript_p: PoseidonTranscript = - PoseidonTranscript::::new(&poseidon_config); + let mut transcript_p: PoseidonSponge = PoseidonSponge::::new(&poseidon_config); transcript_p.absorb(&Fr::from_le_bytes_mod_order(b"init init")); // Run the prover side of the multifolding let (proof, folded_lcccs, folded_witness, _) = - NIMFS::>::prove( + NIMFS::>::prove( &mut transcript_p, &ccs, &[running_instance.clone()], @@ -508,12 +478,11 @@ pub mod tests { .unwrap(); // Verifier's transcript - let mut transcript_v: PoseidonTranscript = - PoseidonTranscript::::new(&poseidon_config); + 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( + let folded_lcccs_v = NIMFS::>::verify( &mut transcript_v, &ccs, &[running_instance.clone()], @@ -545,12 +514,10 @@ pub mod tests { let poseidon_config = poseidon_canonical_config::(); - let mut transcript_p: PoseidonTranscript = - PoseidonTranscript::::new(&poseidon_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: PoseidonTranscript = - PoseidonTranscript::::new(&poseidon_config); + let mut transcript_v: PoseidonSponge = PoseidonSponge::::new(&poseidon_config); transcript_v.absorb(&Fr::from_le_bytes_mod_order(b"init init")); let n: usize = 10; @@ -564,7 +531,7 @@ pub mod tests { // run the prover side of the multifolding let (proof, folded_lcccs, folded_witness, _) = - NIMFS::>::prove( + NIMFS::>::prove( &mut transcript_p, &ccs, &[running_instance.clone()], @@ -575,7 +542,7 @@ pub mod tests { .unwrap(); // run the verifier side of the multifolding - let folded_lcccs_v = NIMFS::>::verify( + let folded_lcccs_v = NIMFS::>::verify( &mut transcript_v, &ccs, &[running_instance.clone()], @@ -641,13 +608,12 @@ pub mod tests { // Prover's transcript let poseidon_config = poseidon_canonical_config::(); - let mut transcript_p: PoseidonTranscript = - PoseidonTranscript::::new(&poseidon_config); + let mut transcript_p: PoseidonSponge = PoseidonSponge::::new(&poseidon_config); transcript_p.absorb(&Fr::from_le_bytes_mod_order(b"init init")); // Run the prover side of the multifolding let (proof, folded_lcccs, folded_witness, _) = - NIMFS::>::prove( + NIMFS::>::prove( &mut transcript_p, &ccs, &lcccs_instances, @@ -658,12 +624,11 @@ pub mod tests { .unwrap(); // Verifier's transcript - let mut transcript_v: PoseidonTranscript = - PoseidonTranscript::::new(&poseidon_config); + 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( + let folded_lcccs_v = NIMFS::>::verify( &mut transcript_v, &ccs, &lcccs_instances, @@ -690,13 +655,11 @@ pub mod tests { let poseidon_config = poseidon_canonical_config::(); // Prover's transcript - let mut transcript_p: PoseidonTranscript = - PoseidonTranscript::::new(&poseidon_config); + let mut transcript_p: PoseidonSponge = PoseidonSponge::::new(&poseidon_config); transcript_p.absorb(&Fr::from_le_bytes_mod_order(b"init init")); // Verifier's transcript - let mut transcript_v: PoseidonTranscript = - PoseidonTranscript::::new(&poseidon_config); + let mut transcript_v: PoseidonSponge = PoseidonSponge::::new(&poseidon_config); transcript_v.absorb(&Fr::from_le_bytes_mod_order(b"init init")); let n_steps = 3; @@ -741,7 +704,7 @@ pub mod tests { // Run the prover side of the multifolding let (proof, folded_lcccs, folded_witness, _) = - NIMFS::>::prove( + NIMFS::>::prove( &mut transcript_p, &ccs, &lcccs_instances, @@ -752,7 +715,7 @@ pub mod tests { .unwrap(); // Run the verifier side of the multifolding - let folded_lcccs_v = NIMFS::>::verify( + let folded_lcccs_v = NIMFS::>::verify( &mut transcript_v, &ccs, &lcccs_instances, diff --git a/folding-schemes/src/folding/hypernova/utils.rs b/folding-schemes/src/folding/hypernova/utils.rs index 8b247c9..160ae5a 100644 --- a/folding-schemes/src/folding/hypernova/utils.rs +++ b/folding-schemes/src/folding/hypernova/utils.rs @@ -176,7 +176,6 @@ pub mod tests { use crate::utils::hypercube::BooleanHypercube; use crate::utils::mle::matrix_to_dense_mle; use crate::utils::multilinear_polynomial::tests::fix_last_variables; - use crate::utils::virtual_polynomial::eq_eval; /// Given M(x,y) matrix and a random field element `r`, test that ~M(r,y) is is an s'-variable polynomial which /// compresses every column j of the M(x,y) matrix by performing a random linear combination between the elements diff --git a/folding-schemes/src/folding/nova/circuits.rs b/folding-schemes/src/folding/nova/circuits.rs index 28721c6..3ad2269 100644 --- a/folding-schemes/src/folding/nova/circuits.rs +++ b/folding-schemes/src/folding/nova/circuits.rs @@ -1,11 +1,7 @@ /// contains [Nova](https://eprint.iacr.org/2021/370.pdf) related circuits -use ark_crypto_primitives::crh::{ - poseidon::constraints::{CRHGadget, CRHParametersVar}, - CRHSchemeGadget, -}; use ark_crypto_primitives::sponge::{ - constraints::CryptographicSpongeVar, - poseidon::{constraints::PoseidonSpongeVar, PoseidonConfig, PoseidonSponge}, + constraints::{AbsorbGadget, CryptographicSpongeVar}, + poseidon::{constraints::PoseidonSpongeVar, PoseidonConfig}, Absorb, CryptographicSponge, }; use ark_ec::{CurveGroup, Group}; @@ -17,6 +13,7 @@ use ark_r1cs_std::{ fields::{fp::FpVar, FieldVar}, groups::GroupOpsBounds, prelude::CurveVar, + uint8::UInt8, R1CSVar, ToConstraintFieldGadget, }; use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, Namespace, SynthesisError}; @@ -29,13 +26,11 @@ use crate::folding::circuits::{ cyclefold::{ CycleFoldChallengeGadget, CycleFoldCommittedInstanceVar, NIFSFullGadget, CF_IO_LEN, }, - nonnative::{ - affine::{nonnative_affine_to_field_elements, NonNativeAffineVar}, - uint::NonNativeUintVar, - }, + nonnative::{affine::NonNativeAffineVar, uint::NonNativeUintVar}, CF1, CF2, }; use crate::frontend::FCircuit; +use crate::transcript::{AbsorbNonNativeGadget, Transcript, TranscriptVar}; /// CommittedInstanceVar contains the u, x, cmE and cmW values which are folded on the main Nova /// constraints field (E1::Fr, where E1 is the main curve). The peculiarity is that cmE and cmW are @@ -78,6 +73,26 @@ where } } +impl AbsorbGadget for CommittedInstanceVar +where + C: CurveGroup, + ::BaseField: ark_ff::PrimeField, +{ + fn to_sponge_bytes(&self) -> Result>, SynthesisError> { + unimplemented!() + } + + fn to_sponge_field_elements(&self) -> Result>, SynthesisError> { + Ok([ + vec![self.u.clone()], + self.x.clone(), + self.cmE.to_constraint_field()?, + self.cmW.to_constraint_field()?, + ] + .concat()) + } +} + impl CommittedInstanceVar where C: CurveGroup, @@ -91,26 +106,22 @@ where /// Additionally it returns the vector of the field elements from the self parameters, so they /// can be reused in other gadgets avoiding recalculating (reconstraining) them. #[allow(clippy::type_complexity)] - pub fn hash( + pub fn hash, S>>( self, - crh_params: &CRHParametersVar>, + sponge: &T, pp_hash: FpVar>, i: FpVar>, z_0: Vec>>, z_i: Vec>>, ) -> Result<(FpVar>, Vec>>), SynthesisError> { - let U_vec = [ - vec![self.u], - self.x, - self.cmE.to_constraint_field()?, - self.cmW.to_constraint_field()?, - ] - .concat(); - let input = [vec![pp_hash, i], z_0, z_i, U_vec.clone()].concat(); - Ok(( - CRHGadget::::evaluate(crh_params, &input)?, - U_vec, - )) + 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)?; + sponge.absorb(&U_vec)?; + Ok((sponge.squeeze_field_elements(1)?.pop().unwrap(), U_vec)) } } @@ -174,67 +185,33 @@ where ::BaseField: PrimeField, ::ScalarField: Absorb, { - pub fn get_challenge_native( - poseidon_config: &PoseidonConfig, + pub fn get_challenge_native>( + transcript: &mut T, pp_hash: C::ScalarField, // public params hash U_i: CommittedInstance, u_i: CommittedInstance, cmT: C, - ) -> Result, SynthesisError> { - let (U_cmE_x, U_cmE_y) = nonnative_affine_to_field_elements::(U_i.cmE)?; - let (U_cmW_x, U_cmW_y) = nonnative_affine_to_field_elements::(U_i.cmW)?; - let (u_cmE_x, u_cmE_y) = nonnative_affine_to_field_elements::(u_i.cmE)?; - let (u_cmW_x, u_cmW_y) = nonnative_affine_to_field_elements::(u_i.cmW)?; - let (cmT_x, cmT_y) = nonnative_affine_to_field_elements::(cmT)?; - - let mut sponge = PoseidonSponge::::new(poseidon_config); - let input = vec![ - vec![pp_hash], - vec![U_i.u], - U_i.x.clone(), - U_cmE_x, - U_cmE_y, - U_cmW_x, - U_cmW_y, - vec![u_i.u], - u_i.x.clone(), - u_cmE_x, - u_cmE_y, - u_cmW_x, - u_cmW_y, - cmT_x, - cmT_y, - ] - .concat(); - sponge.absorb(&input); - let bits = sponge.squeeze_bits(N_BITS_RO); - Ok(bits) + ) -> Vec { + transcript.absorb(&pp_hash); + transcript.absorb(&U_i); + transcript.absorb(&u_i); + transcript.absorb_nonnative(&cmT); + transcript.squeeze_bits(N_BITS_RO) } // compatible with the native get_challenge_native - pub fn get_challenge_gadget( - cs: ConstraintSystemRef, - poseidon_config: &PoseidonConfig, + pub fn get_challenge_gadget, S>>( + 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: CommittedInstanceVar, cmT: NonNativeAffineVar, ) -> Result>, SynthesisError> { - 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(), - u_i.cmE.to_constraint_field()?, - u_i.cmW.to_constraint_field()?, - cmT.to_constraint_field()?, - ] - .concat(); - sponge.absorb(&input)?; - let bits = sponge.squeeze_bits(N_BITS_RO)?; - Ok(bits) + transcript.absorb(&pp_hash)?; + transcript.absorb(&U_i_vec)?; + transcript.absorb(&u_i)?; + transcript.absorb_nonnative(&cmT)?; + transcript.squeeze_bits(N_BITS_RO) } } @@ -367,10 +344,10 @@ where let cf1_cmT = GC2::new_witness(cs.clone(), || Ok(self.cf1_cmT.unwrap_or_else(C2::zero)))?; let cf2_cmT = GC2::new_witness(cs.clone(), || Ok(self.cf2_cmT.unwrap_or_else(C2::zero)))?; - let crh_params = CRHParametersVar::::new_constant( - cs.clone(), - self.poseidon_config.clone(), - )?; + // `sponge` is for digest computation. + let sponge = PoseidonSpongeVar::::new(cs.clone(), &self.poseidon_config); + // `transcript` is for challenge generation. + let mut transcript = sponge.clone(); // get z_{i+1} from the F circuit let i_usize = self.i_usize.unwrap_or(0); @@ -384,14 +361,14 @@ where // 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, + &sponge, 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, pp_hash.clone())?; + let (cf_u_i_x, cf_U_i_vec) = cf_U_i.clone().hash(&sponge, pp_hash.clone())?; // P.2. Construct u_i let u_i = CommittedInstanceVar { @@ -411,8 +388,7 @@ where // compute r = H(u_i, U_i, cmT) let r_bits = ChallengeGadget::::get_challenge_gadget( - cs.clone(), - &self.poseidon_config, + &mut transcript, pp_hash.clone(), U_i_vec, u_i.clone(), @@ -438,14 +414,14 @@ 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( - &crh_params, + &sponge, 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, + &sponge, pp_hash.clone(), FpVar::>::one(), z_0.clone(), @@ -499,8 +475,7 @@ where // compute cf1_r = H(cf1_u_i, cf_U_i, cf1_cmT) // cf_r_bits is denoted by rho* in the paper. let cf1_r_bits = CycleFoldChallengeGadget::::get_challenge_gadget( - cs.clone(), - &self.poseidon_config, + &mut transcript, pp_hash.clone(), cf_U_i_vec, cf1_u_i.clone(), @@ -523,10 +498,9 @@ where // same for cf2_r: let cf2_r_bits = CycleFoldChallengeGadget::::get_challenge_gadget( - cs.clone(), - &self.poseidon_config, + &mut transcript, pp_hash.clone(), - cf1_U_i1.to_constraint_field()?, + cf1_U_i1.to_native_sponge_field_elements()?, cf2_u_i.clone(), cf2_cmT.clone(), )?; @@ -547,10 +521,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, pp_hash.clone())?; + let (cf_u_i1_x, _) = cf_U_i1.clone().hash(&sponge, pp_hash.clone())?; let (cf_u_i1_x_base, _) = CycleFoldCommittedInstanceVar::new_constant(cs.clone(), cf_u_dummy)? - .hash(&crh_params, pp_hash)?; + .hash(&sponge, pp_hash)?; let cf_x = FpVar::new_input(cs.clone(), || { Ok(self.cf_x.unwrap_or(cf_u_i1_x_base.value()?)) })?; @@ -564,6 +538,7 @@ where pub mod tests { use super::*; use ark_bn254::{Fr, G1Projective as Projective}; + use ark_crypto_primitives::sponge::poseidon::PoseidonSponge; use ark_ff::BigInteger; use ark_relations::r1cs::ConstraintSystem; use ark_std::UniformRand; @@ -628,6 +603,7 @@ pub mod tests { fn test_committed_instance_hash() { let mut rng = ark_std::test_rng(); let poseidon_config = poseidon_canonical_config::(); + let sponge = PoseidonSponge::::new(&poseidon_config); let pp_hash = Fr::from(42u32); // only for test let i = Fr::from(3_u32); @@ -641,9 +617,7 @@ pub mod tests { }; // compute the CommittedInstance hash natively - let h = ci - .hash(&poseidon_config, pp_hash, i, z_0.clone(), z_i.clone()) - .unwrap(); + let h = ci.hash(&sponge, pp_hash, i, z_0.clone(), z_i.clone()); let cs = ConstraintSystem::::new_ref(); @@ -654,11 +628,11 @@ pub mod tests { let ciVar = CommittedInstanceVar::::new_witness(cs.clone(), || Ok(ci.clone())).unwrap(); - let crh_params = CRHParametersVar::::new_constant(cs.clone(), poseidon_config).unwrap(); + let sponge = PoseidonSpongeVar::::new(cs.clone(), &poseidon_config); // compute the CommittedInstance hash in-circuit let (hVar, _) = ciVar - .hash(&crh_params, pp_hashVar, iVar, z_0Var, z_iVar) + .hash(&sponge, pp_hashVar, iVar, z_0Var, z_iVar) .unwrap(); assert!(cs.is_satisfied().unwrap()); @@ -671,6 +645,7 @@ pub mod tests { fn test_challenge_gadget() { let mut rng = ark_std::test_rng(); let poseidon_config = poseidon_canonical_config::(); + let mut transcript = PoseidonSponge::::new(&poseidon_config); let u_i = CommittedInstance:: { cmE: Projective::rand(&mut rng), @@ -690,13 +665,12 @@ pub mod tests { // compute the challenge natively let r_bits = ChallengeGadget::::get_challenge_native( - &poseidon_config, + &mut transcript, pp_hash, U_i.clone(), u_i.clone(), cmT, - ) - .unwrap(); + ); let r = Fr::from_bigint(BigInteger::from_bits_le(&r_bits)).unwrap(); let cs = ConstraintSystem::::new_ref(); @@ -708,6 +682,7 @@ pub mod tests { CommittedInstanceVar::::new_witness(cs.clone(), || Ok(U_i.clone())) .unwrap(); let cmTVar = NonNativeAffineVar::::new_witness(cs.clone(), || Ok(cmT)).unwrap(); + let mut transcriptVar = PoseidonSpongeVar::::new(cs.clone(), &poseidon_config); // compute the challenge in-circuit let U_iVar_vec = [ @@ -718,8 +693,7 @@ pub mod tests { ] .concat(); let r_bitsVar = ChallengeGadget::::get_challenge_gadget( - cs.clone(), - &poseidon_config, + &mut transcriptVar, pp_hashVar, U_iVar_vec, u_iVar, diff --git a/folding-schemes/src/folding/nova/decider_eth.rs b/folding-schemes/src/folding/nova/decider_eth.rs index c0727d4..4b011e8 100644 --- a/folding-schemes/src/folding/nova/decider_eth.rs +++ b/folding-schemes/src/folding/nova/decider_eth.rs @@ -317,13 +317,11 @@ fn point2_to_eth_format(p: ark_bn254::G2Affine) -> Result, Error> { #[cfg(test)] pub mod tests { - use ark_bn254::{constraints::GVar, Bn254, Fr, G1Projective as Projective}; - use ark_groth16::Groth16; + use ark_bn254::{constraints::GVar, Fr, G1Projective as Projective}; use ark_grumpkin::{constraints::GVar as GVar2, Projective as Projective2}; use std::time::Instant; use super::*; - use crate::commitment::kzg::KZG; use crate::commitment::pedersen::Pedersen; use crate::folding::nova::PreprocessorParam; use crate::frontend::tests::CubicFCircuit; diff --git a/folding-schemes/src/folding/nova/decider_eth_circuit.rs b/folding-schemes/src/folding/nova/decider_eth_circuit.rs index 8e399c9..2a0ab0b 100644 --- a/folding-schemes/src/folding/nova/decider_eth_circuit.rs +++ b/folding-schemes/src/folding/nova/decider_eth_circuit.rs @@ -1,7 +1,10 @@ /// 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::crh::poseidon::constraints::CRHParametersVar; -use ark_crypto_primitives::sponge::{poseidon::PoseidonConfig, Absorb}; +use ark_crypto_primitives::sponge::{ + constraints::CryptographicSpongeVar, + poseidon::{constraints::PoseidonSpongeVar, PoseidonConfig, PoseidonSponge}, + Absorb, CryptographicSponge, +}; use ark_ec::{CurveGroup, Group}; use ark_ff::{BigInteger, PrimeField}; use ark_poly::Polynomial; @@ -23,18 +26,12 @@ use super::{circuits::ChallengeGadget, nifs::NIFS}; use crate::arith::r1cs::R1CS; use crate::commitment::{pedersen::Params as PedersenParams, CommitmentScheme}; use crate::folding::circuits::{ - nonnative::{ - affine::{nonnative_affine_to_field_elements, NonNativeAffineVar}, - uint::NonNativeUintVar, - }, + nonnative::{affine::NonNativeAffineVar, uint::NonNativeUintVar}, CF1, CF2, }; use crate::folding::nova::{circuits::CommittedInstanceVar, CommittedInstance, Nova, Witness}; use crate::frontend::FCircuit; -use crate::transcript::{ - poseidon::{PoseidonTranscript, PoseidonTranscriptVar}, - Transcript, TranscriptVar, -}; +use crate::transcript::{Transcript, TranscriptVar}; use crate::utils::{ gadgets::{MatrixGadget, SparseMatrixVar, VectorGadget}, vec::poly_from_vec, @@ -264,6 +261,8 @@ where pub fn from_nova>( nova: Nova, ) -> Result { + let mut transcript = PoseidonSponge::::new(&nova.poseidon_config); + // compute the U_{i+1}, W_{i+1} let (T, cmT) = NIFS::::compute_cmT( &nova.cs_pp, @@ -274,12 +273,12 @@ where &nova.U_i.clone(), )?; let r_bits = ChallengeGadget::::get_challenge_native( - &nova.poseidon_config, + &mut transcript, nova.pp_hash, nova.U_i.clone(), nova.u_i.clone(), cmT, - )?; + ); let r_Fr = C1::ScalarField::from_bigint(BigInteger::from_bits_le(&r_bits)) .ok_or(Error::OutOfBounds)?; let (W_i1, U_i1) = NIFS::::fold_instances( @@ -288,7 +287,7 @@ where // compute the KZG challenges used as inputs in the circuit let (kzg_challenge_W, kzg_challenge_E) = - KZGChallengesGadget::::get_challenges_native(&nova.poseidon_config, U_i1.clone())?; + KZGChallengesGadget::::get_challenges_native(&mut transcript, U_i1.clone()); // get KZG evals let mut W = W_i1.W.clone(); @@ -410,10 +409,10 @@ where Ok(self.eval_E.unwrap_or_else(CF1::::zero)) })?; - let crh_params = CRHParametersVar::::new_constant( - cs.clone(), - self.poseidon_config.clone(), - )?; + // `sponge` is for digest computation. + let sponge = PoseidonSpongeVar::::new(cs.clone(), &self.poseidon_config); + // `transcript` is for challenge generation. + let mut transcript = sponge.clone(); // 1. check RelaxedR1CS of U_{i+1} let z_U1: Vec>> = @@ -429,7 +428,7 @@ where // 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, + &sponge, pp_hash.clone(), i.clone(), z_0.clone(), @@ -465,7 +464,7 @@ where })?; // 3.b u_i.x[1] == H(cf_U_i) - let (cf_u_i_x, _) = cf_U_i.clone().hash(&crh_params, pp_hash.clone())?; + let (cf_u_i_x, _) = cf_U_i.clone().hash(&sponge, pp_hash.clone())?; (u_i.x[1]).enforce_equal(&cf_u_i_x)?; // 4. check Pedersen commitments of cf_U_i.{cmE, cmW} @@ -498,12 +497,23 @@ where RelaxedR1CSGadget::check_nonnative(cf_r1cs, cf_W_i.E, cf_U_i.u.clone(), cf_z_U)?; } - // 6. check KZG challenges - let (incircuit_c_W, incircuit_c_E) = KZGChallengesGadget::::get_challenges_gadget( - cs.clone(), - &self.poseidon_config, - U_i1.clone(), + // 8.a, 6.a compute NIFS.V and KZG challenges. + // We need to ensure the order of challenge generation is the same as + // the native counterpart, so we first compute the challenges here and + // do the actual checks later. + let cmT = + NonNativeAffineVar::new_input(cs.clone(), || Ok(self.cmT.unwrap_or_else(C1::zero)))?; + let r_bits = ChallengeGadget::::get_challenge_gadget( + &mut transcript, + pp_hash, + U_i_vec, + u_i.clone(), + cmT.clone(), )?; + let (incircuit_c_W, incircuit_c_E) = + KZGChallengesGadget::::get_challenges_gadget(&mut transcript, U_i1.clone())?; + + // 6.b check KZG challenges incircuit_c_W.enforce_equal(&kzg_c_W)?; incircuit_c_E.enforce_equal(&kzg_c_E)?; @@ -516,18 +526,8 @@ where // incircuit_eval_W.enforce_equal(&eval_W)?; // incircuit_eval_E.enforce_equal(&eval_E)?; - // 8. compute the NIFS.V challenge and check that matches the one from the public input (so we + // 8.b check the NIFS.V challenge matches the one from the public input (so we // avoid the verifier computing it) - let cmT = - NonNativeAffineVar::new_input(cs.clone(), || Ok(self.cmT.unwrap_or_else(C1::zero)))?; - let r_bits = ChallengeGadget::::get_challenge_gadget( - cs.clone(), - &self.poseidon_config, - pp_hash, - U_i_vec, - u_i.clone(), - cmT.clone(), - )?; let r_Fr = Boolean::le_bits_to_fp_var(&r_bits)?; // check that the in-circuit computed r is equal to the inputted r let r = @@ -569,38 +569,28 @@ where ::BaseField: PrimeField, C::ScalarField: Absorb, { - pub fn get_challenges_native( - poseidon_config: &PoseidonConfig, + pub fn get_challenges_native>( + transcript: &mut T, U_i: CommittedInstance, - ) -> Result<(C::ScalarField, C::ScalarField), Error> { - let (cmE_x_limbs, cmE_y_limbs) = nonnative_affine_to_field_elements(U_i.cmE)?; - let (cmW_x_limbs, cmW_y_limbs) = nonnative_affine_to_field_elements(U_i.cmW)?; - - let transcript = &mut PoseidonTranscript::::new(poseidon_config); + ) -> (C::ScalarField, C::ScalarField) { // compute the KZG challenges, which are computed in-circuit and checked that it matches // the inputted one - transcript.absorb_vec(&cmW_x_limbs); - transcript.absorb_vec(&cmW_y_limbs); + transcript.absorb_nonnative(&U_i.cmW); let challenge_W = transcript.get_challenge(); - transcript.absorb_vec(&cmE_x_limbs); - transcript.absorb_vec(&cmE_y_limbs); + transcript.absorb_nonnative(&U_i.cmE); let challenge_E = transcript.get_challenge(); - Ok((challenge_W, challenge_E)) + (challenge_W, challenge_E) } // compatible with the native get_challenges_native - pub fn get_challenges_gadget( - cs: ConstraintSystemRef, - poseidon_config: &PoseidonConfig, + pub fn get_challenges_gadget, S>>( + transcript: &mut T, U_i: CommittedInstanceVar, ) -> Result<(FpVar, FpVar), SynthesisError> { - let mut transcript = - PoseidonTranscriptVar::>::new(cs.clone(), &poseidon_config.clone()); - - transcript.absorb_vec(&U_i.cmW.to_constraint_field()?[..])?; + transcript.absorb(&U_i.cmW.to_constraint_field()?)?; let challenge_W = transcript.get_challenge()?; - transcript.absorb_vec(&U_i.cmE.to_constraint_field()?[..])?; + transcript.absorb(&U_i.cmE.to_constraint_field()?)?; let challenge_E = transcript.get_challenge()?; Ok((challenge_W, challenge_E)) @@ -852,6 +842,7 @@ pub mod tests { fn test_kzg_challenge_gadget() { let mut rng = ark_std::test_rng(); let poseidon_config = poseidon_canonical_config::(); + let mut transcript = PoseidonSponge::::new(&poseidon_config); let U_i = CommittedInstance:: { cmE: Projective::rand(&mut rng), @@ -862,21 +853,17 @@ pub mod tests { // compute the challenge natively let (challenge_W, challenge_E) = - KZGChallengesGadget::::get_challenges_native(&poseidon_config, U_i.clone()) - .unwrap(); + KZGChallengesGadget::::get_challenges_native(&mut transcript, U_i.clone()); let cs = ConstraintSystem::::new_ref(); let U_iVar = CommittedInstanceVar::::new_witness(cs.clone(), || Ok(U_i.clone())) .unwrap(); + let mut transcript_var = PoseidonSpongeVar::::new(cs.clone(), &poseidon_config); let (challenge_W_Var, challenge_E_Var) = - KZGChallengesGadget::::get_challenges_gadget( - cs.clone(), - &poseidon_config, - U_iVar, - ) - .unwrap(); + KZGChallengesGadget::::get_challenges_gadget(&mut transcript_var, U_iVar) + .unwrap(); assert!(cs.is_satisfied().unwrap()); // check that the natively computed and in-circuit computed hashes match diff --git a/folding-schemes/src/folding/nova/mod.rs b/folding-schemes/src/folding/nova/mod.rs index 071959c..4847314 100644 --- a/folding-schemes/src/folding/nova/mod.rs +++ b/folding-schemes/src/folding/nova/mod.rs @@ -1,11 +1,11 @@ /// Implements the scheme described in [Nova](https://eprint.iacr.org/2021/370.pdf) and /// [CycleFold](https://eprint.iacr.org/2023/1192.pdf). -use ark_crypto_primitives::{ - crh::{poseidon::CRH, CRHScheme}, - sponge::{poseidon::PoseidonConfig, Absorb}, +use ark_crypto_primitives::sponge::{ + poseidon::{PoseidonConfig, PoseidonSponge}, + Absorb, CryptographicSponge, }; use ark_ec::{AffineRepr, CurveGroup, Group}; -use ark_ff::{BigInteger, Field, PrimeField, ToConstraintField}; +use ark_ff::{BigInteger, PrimeField}; use ark_r1cs_std::{groups::GroupOpsBounds, prelude::CurveVar, ToConstraintFieldGadget}; use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystem}; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; @@ -14,19 +14,18 @@ use ark_std::rand::RngCore; use ark_std::{One, Zero}; use core::marker::PhantomData; -use crate::arith::r1cs::{extract_r1cs, extract_w_x, R1CS}; use crate::commitment::CommitmentScheme; -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::folding::circuits::cyclefold::{fold_cyclefold_circuit, CycleFoldCircuit}; +use crate::folding::circuits::CF2; use crate::frontend::FCircuit; -use crate::utils::{get_cm_coordinates, pp_hash, vec::is_zero_vec}; +use crate::transcript::{AbsorbNonNative, Transcript}; +use crate::utils::vec::is_zero_vec; use crate::Error; use crate::FoldingScheme; +use crate::{ + arith::r1cs::{extract_r1cs, extract_w_x, R1CS}, + utils::{get_cm_coordinates, pp_hash}, +}; pub mod circuits; pub mod decider_eth; @@ -57,6 +56,54 @@ impl CommittedInstance { } } +impl Absorb for CommittedInstance +where + C::ScalarField: Absorb, +{ + fn to_sponge_bytes(&self, _dest: &mut Vec) { + // This is never called + unimplemented!() + } + + fn to_sponge_field_elements(&self, dest: &mut Vec) { + self.u.to_sponge_field_elements(dest); + self.x.to_sponge_field_elements(dest); + // We cannot call `to_native_sponge_field_elements(dest)` directly, as + // `to_native_sponge_field_elements` needs `F` to be `C::ScalarField`, + // but here `F` is a generic `PrimeField`. + self.cmE + .to_native_sponge_field_elements_as_vec() + .to_sponge_field_elements(dest); + self.cmW + .to_native_sponge_field_elements_as_vec() + .to_sponge_field_elements(dest); + } +} + +impl AbsorbNonNative for CommittedInstance +where + ::BaseField: ark_ff::PrimeField + Absorb, +{ + // Compatible with the in-circuit `CycleFoldCommittedInstanceVar::to_native_sponge_field_elements` + // in `cyclefold.rs`. + fn to_native_sponge_field_elements(&self, dest: &mut Vec) { + [self.u].to_native_sponge_field_elements(dest); + self.x.to_native_sponge_field_elements(dest); + let (cmE_x, cmE_y) = match self.cmE.into_affine().xy() { + Some((&x, &y)) => (x, y), + None => (C::BaseField::zero(), C::BaseField::zero()), + }; + let (cmW_x, cmW_y) = match self.cmW.into_affine().xy() { + Some((&x, &y)) => (x, y), + None => (C::BaseField::zero(), C::BaseField::zero()), + }; + cmE_x.to_sponge_field_elements(dest); + cmE_y.to_sponge_field_elements(dest); + cmW_x.to_sponge_field_elements(dest); + cmW_y.to_sponge_field_elements(dest); + } +} + impl CommittedInstance where ::ScalarField: Absorb, @@ -66,67 +113,21 @@ where /// nova/circuits.rs::CommittedInstanceVar.hash. /// Returns `H(i, z_0, z_i, U_i)`, where `i` can be `i` but also `i+1`, and `U_i` is the /// `CommittedInstance`. - pub fn hash( + pub fn hash>( &self, - poseidon_config: &PoseidonConfig, + sponge: &T, pp_hash: C::ScalarField, // public params hash i: C::ScalarField, z_0: Vec, z_i: Vec, - ) -> Result { - let (cmE_x, cmE_y) = nonnative_affine_to_field_elements::(self.cmE)?; - let (cmW_x, cmW_y) = nonnative_affine_to_field_elements::(self.cmW)?; - - CRH::::evaluate( - poseidon_config, - vec![ - vec![pp_hash, i], - z_0, - z_i, - vec![self.u], - self.x.clone(), - cmE_x, - cmE_y, - cmW_x, - cmW_y, - ] - .concat(), - ) - .map_err(|e| Error::Other(e.to_string())) - } -} - -impl ToConstraintField for CommittedInstance -where - ::BaseField: ark_ff::PrimeField + Absorb, -{ - fn to_field_elements(&self) -> Option> { - let u = nonnative_field_to_field_elements(&self.u); - let x = self - .x - .iter() - .flat_map(nonnative_field_to_field_elements) - .collect::>(); - let (cmE_x, cmE_y, cmE_is_inf) = match self.cmE.into_affine().xy() { - Some((&x, &y)) => (x, y, C::BaseField::zero()), - None => ( - C::BaseField::zero(), - C::BaseField::zero(), - C::BaseField::one(), - ), - }; - let (cmW_x, cmW_y, cmW_is_inf) = match self.cmW.into_affine().xy() { - Some((&x, &y)) => (x, y, C::BaseField::zero()), - None => ( - C::BaseField::zero(), - C::BaseField::zero(), - C::BaseField::one(), - ), - }; - // Concatenate `cmE_is_inf` and `cmW_is_inf` to save constraints for CRHGadget::evaluate in the corresponding circuit - let is_inf = cmE_is_inf.double() + cmW_is_inf; - - Some([u, x, vec![cmE_x, cmE_y, cmW_x, cmW_y, is_inf]].concat()) + ) -> C::ScalarField { + let mut sponge = sponge.clone(); + sponge.absorb(&pp_hash); + sponge.absorb(&i); + sponge.absorb(&z_0); + sponge.absorb(&z_i); + sponge.absorb(&self); + sponge.squeeze_field_elements(1)[0] } } @@ -137,16 +138,15 @@ where /// hash_cyclefold implements the committed instance hash compatible with the gadget implemented in /// nova/cyclefold.rs::CycleFoldCommittedInstanceVar.hash. /// Returns `H(U_i)`, where `U_i` is the `CommittedInstance` for CycleFold. - pub fn hash_cyclefold( + pub fn hash_cyclefold>( &self, - poseidon_config: &PoseidonConfig, + sponge: &T, pp_hash: C::BaseField, // public params hash - ) -> Result { - CRH::::evaluate( - poseidon_config, - [vec![pp_hash], self.to_field_elements().unwrap()].concat(), - ) - .map_err(|e| Error::Other(e.to_string())) + ) -> C::BaseField { + let mut sponge = sponge.clone(); + sponge.absorb(&pp_hash); + sponge.absorb_nonnative(self); + sponge.squeeze_field_elements(1)[0] } } @@ -453,6 +453,11 @@ where _rng: impl RngCore, external_inputs: Vec, ) -> Result<(), Error> { + // `sponge` is for digest computation. + let sponge = PoseidonSponge::::new(&self.poseidon_config); + // `transcript` is for challenge generation. + let mut transcript = sponge.clone(); + let augmented_F_circuit: AugmentedFCircuit; if self.z_i.len() != self.F.state_len() { @@ -488,12 +493,12 @@ where // r_bits is the r used to the RLC of the F' instances let r_bits = ChallengeGadget::::get_challenge_native( - &self.poseidon_config, + &mut transcript, self.pp_hash, self.U_i.clone(), self.u_i.clone(), cmT, - )?; + ); let r_Fr = C1::ScalarField::from_bigint(BigInteger::from_bits_le(&r_bits)) .ok_or(Error::OutOfBounds)?; let r_Fq = C1::BaseField::from_bigint(BigInteger::from_bits_le(&r_bits)) @@ -507,19 +512,17 @@ where // folded instance output (public input, x) // 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, + &sponge, self.pp_hash, self.i + C1::ScalarField::one(), self.z_0.clone(), z_i1.clone(), - )?; + ); // u_{i+1}.x[1] = H(cf_U_{i+1}) 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, self.pp_hash)?; + cf_u_i1_x = self.cf_U_i.hash_cyclefold(&sponge, self.pp_hash); // base case augmented_F_circuit = AugmentedFCircuit:: { _gc2: PhantomData, @@ -584,16 +587,22 @@ where // fold self.cf_U_i + cfW_U -> folded running with cfW let (_cfW_w_i, cfW_u_i, cfW_W_i1, cfW_U_i1, cfW_cmT, _) = self.fold_cyclefold_circuit( + &mut transcript, self.cf_W_i.clone(), // CycleFold running instance witness self.cf_U_i.clone(), // CycleFold running instance cfW_u_i_x, cfW_circuit, )?; // fold [the output from folding self.cf_U_i + cfW_U] + cfE_U = folded_running_with_cfW + cfE - 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)?; + let (_cfE_w_i, cfE_u_i, cf_W_i1, cf_U_i1, cf_cmT, _) = self.fold_cyclefold_circuit( + &mut transcript, + 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, self.pp_hash)?; + cf_u_i1_x = cf_U_i1.hash_cyclefold(&sponge, self.pp_hash); augmented_F_circuit = AugmentedFCircuit:: { _gc2: PhantomData, @@ -697,6 +706,8 @@ where incoming_instance: Self::IncomingInstance, cyclefold_instance: Self::CFInstance, ) -> Result<(), Error> { + let sponge = PoseidonSponge::::new(&vp.poseidon_config); + if num_steps == C1::ScalarField::zero() { if z_0 != z_i { return Err(Error::IVCVerificationFail); @@ -716,12 +727,12 @@ where // 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, pp_hash, num_steps, z_0, z_i.clone())?; + let expected_u_i_x = U_i.hash(&sponge, 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, pp_hash)?; + let expected_cf_u_i_x = cf_U_i.hash_cyclefold(&sponge, pp_hash); if expected_cf_u_i_x != u_i.x[1] { return Err(Error::IVCVerificationFail); } @@ -790,8 +801,9 @@ where { // folds the given cyclefold circuit and its instances #[allow(clippy::type_complexity)] - fn fold_cyclefold_circuit( + fn fold_cyclefold_circuit>( &self, + transcript: &mut T, cf_W_i: Witness, // witness of the running instance cf_U_i: CommittedInstance, // running instance cf_u_i_x: Vec, @@ -808,7 +820,7 @@ where Error, > { fold_cyclefold_circuit::( - &self.poseidon_config, + transcript, self.cf_r1cs.clone(), self.cf_cs_pp.clone(), self.pp_hash, diff --git a/folding-schemes/src/folding/nova/nifs.rs b/folding-schemes/src/folding/nova/nifs.rs index 9e5a1be..725247e 100644 --- a/folding-schemes/src/folding/nova/nifs.rs +++ b/folding-schemes/src/folding/nova/nifs.rs @@ -183,7 +183,7 @@ where } pub fn prove_commitments( - tr: &mut impl Transcript, + tr: &mut impl Transcript, cs_prover_params: &CS::ProverParams, w: &Witness, ci: &CommittedInstance, @@ -200,7 +200,10 @@ where #[cfg(test)] pub mod tests { use super::*; - use ark_crypto_primitives::sponge::poseidon::PoseidonConfig; + use ark_crypto_primitives::sponge::{ + poseidon::{PoseidonConfig, PoseidonSponge}, + CryptographicSponge, + }; use ark_ff::{BigInteger, PrimeField}; use ark_pallas::{Fr, Projective}; use ark_std::{ops::Mul, UniformRand}; @@ -209,7 +212,7 @@ pub mod tests { use crate::commitment::pedersen::{Params as PedersenParams, Pedersen}; use crate::folding::nova::circuits::ChallengeGadget; use crate::folding::nova::traits::NovaR1CS; - use crate::transcript::poseidon::{poseidon_canonical_config, PoseidonTranscript}; + use crate::transcript::poseidon::poseidon_canonical_config; #[allow(clippy::type_complexity)] pub(crate) fn prepare_simple_fold_inputs() -> ( @@ -258,16 +261,16 @@ pub mod tests { .unwrap(); let poseidon_config = poseidon_canonical_config::(); + let mut transcript = PoseidonSponge::::new(&poseidon_config); let pp_hash = C::ScalarField::from(42u32); // only for test let r_bits = ChallengeGadget::::get_challenge_native( - &poseidon_config, + &mut transcript, pp_hash, ci1.clone(), ci2.clone(), cmT, - ) - .unwrap(); + ); let r_Fr = C::ScalarField::from_bigint(BigInteger::from_bits_le(&r_bits)).unwrap(); let (w3, ci3) = @@ -364,9 +367,9 @@ pub mod tests { .unwrap(); // init Prover's transcript - let mut transcript_p = PoseidonTranscript::::new(&poseidon_config); + let mut transcript_p = PoseidonSponge::::new(&poseidon_config); // init Verifier's transcript - let mut transcript_v = PoseidonTranscript::::new(&poseidon_config); + let mut transcript_v = PoseidonSponge::::new(&poseidon_config); // prove the ci3.cmE, ci3.cmW, cmT commitments let cm_proofs = NIFS::>::prove_commitments( diff --git a/folding-schemes/src/folding/protogalaxy/folding.rs b/folding-schemes/src/folding/protogalaxy/folding.rs index f12d07b..e9b78da 100644 --- a/folding-schemes/src/folding/protogalaxy/folding.rs +++ b/folding-schemes/src/folding/protogalaxy/folding.rs @@ -6,12 +6,10 @@ use ark_poly::{ univariate::{DensePolynomial, SparsePolynomial}, DenseUVPolynomial, EvaluationDomain, Evaluations, GeneralEvaluationDomain, Polynomial, }; -use ark_std::log2; -use ark_std::{cfg_into_iter, Zero}; +use ark_std::{cfg_into_iter, log2, Zero}; use rayon::iter::{IntoParallelIterator, ParallelIterator}; use std::marker::PhantomData; -use super::traits::ProtoGalaxyTranscript; use super::utils::{all_powers, betas_star, exponential_powers}; use super::ProtoGalaxyError; use super::{CommittedInstance, Witness}; @@ -36,7 +34,7 @@ where #![allow(clippy::type_complexity)] /// implements the non-interactive Prover from the folding scheme described in section 4 pub fn prove( - transcript: &mut (impl Transcript + ProtoGalaxyTranscript), + transcript: &mut impl Transcript, r1cs: &R1CS, // running instance instance: &CommittedInstance, @@ -81,10 +79,8 @@ where } // absorb the committed instances - transcript.absorb_committed_instance(instance)?; - for ci in vec_instances.iter() { - transcript.absorb_committed_instance(ci)?; - } + transcript.absorb(instance); + transcript.absorb(&vec_instances); let delta = transcript.get_challenge(); let deltas = exponential_powers(delta, t); @@ -95,7 +91,7 @@ where let F_X: SparsePolynomial = calc_f_from_btree(&f_w, &instance.betas, &deltas).expect("Error calculating F[x]"); let F_X_dense = DensePolynomial::from(F_X.clone()); - transcript.absorb_vec(&F_X_dense.coeffs); + transcript.absorb(&F_X_dense.coeffs); let alpha = transcript.get_challenge(); @@ -187,7 +183,7 @@ where return Err(Error::ProtoGalaxy(ProtoGalaxyError::RemainderNotZero)); } - transcript.absorb_vec(&K_X.coeffs); + transcript.absorb(&K_X.coeffs); let gamma = transcript.get_challenge(); @@ -223,7 +219,7 @@ where /// implements the non-interactive Verifier from the folding scheme described in section 4 pub fn verify( - transcript: &mut (impl Transcript + ProtoGalaxyTranscript), + transcript: &mut impl Transcript, r1cs: &R1CS, // running instance instance: &CommittedInstance, @@ -237,15 +233,13 @@ where let n = r1cs.A.n_cols; // absorb the committed instances - transcript.absorb_committed_instance(instance)?; - for ci in vec_instances.iter() { - transcript.absorb_committed_instance(ci)?; - } + transcript.absorb(instance); + transcript.absorb(&vec_instances); let delta = transcript.get_challenge(); let deltas = exponential_powers(delta, t); - transcript.absorb_vec(&F_coeffs); + transcript.absorb(&F_coeffs); let alpha = transcript.get_challenge(); let alphas = all_powers(alpha, n); @@ -266,7 +260,7 @@ where let K_X: DensePolynomial = DensePolynomial::::from_coefficients_vec(K_coeffs); - transcript.absorb_vec(&K_X.coeffs); + transcript.absorb(&K_X.coeffs); let gamma = transcript.get_challenge(); @@ -380,12 +374,14 @@ fn eval_f(r1cs: &R1CS, w: &[F]) -> Result, Error> { #[cfg(test)] 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::UniformRand; 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}; + use crate::transcript::poseidon::poseidon_canonical_config; pub(crate) fn check_instance( r1cs: &R1CS, @@ -495,7 +491,7 @@ mod tests { .unwrap(); let instance_i = CommittedInstance:: { phi: phi_i, - betas: betas.clone(), + betas: vec![], e: Fr::zero(), }; witnesses.push(witness_i); @@ -513,8 +509,8 @@ mod tests { // init Prover & Verifier's transcript let poseidon_config = poseidon_canonical_config::(); - let mut transcript_p = PoseidonTranscript::::new(&poseidon_config); - let mut transcript_v = PoseidonTranscript::::new(&poseidon_config); + let mut transcript_p = PoseidonSponge::::new(&poseidon_config); + let mut transcript_v = PoseidonSponge::::new(&poseidon_config); let (folded_instance, folded_witness, F_coeffs, K_coeffs) = Folding::::prove( &mut transcript_p, @@ -553,8 +549,8 @@ mod tests { // init Prover & Verifier's transcript let poseidon_config = poseidon_canonical_config::(); - let mut transcript_p = PoseidonTranscript::::new(&poseidon_config); - let mut transcript_v = PoseidonTranscript::::new(&poseidon_config); + let mut transcript_p = PoseidonSponge::::new(&poseidon_config); + let mut transcript_v = PoseidonSponge::::new(&poseidon_config); 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 f2f244f..1d8ab6f 100644 --- a/folding-schemes/src/folding/protogalaxy/mod.rs +++ b/folding-schemes/src/folding/protogalaxy/mod.rs @@ -1,8 +1,11 @@ /// Implements the scheme described in [ProtoGalaxy](https://eprint.iacr.org/2023/1106.pdf) use ark_ec::CurveGroup; use ark_ff::PrimeField; +use ark_r1cs_std::fields::fp::FpVar; use thiserror::Error; +use super::circuits::nonnative::affine::NonNativeAffineVar; + pub mod folding; pub mod traits; pub(crate) mod utils; @@ -14,6 +17,13 @@ pub struct CommittedInstance { e: C::ScalarField, } +#[derive(Clone, Debug)] +pub struct CommittedInstanceVar { + phi: NonNativeAffineVar, + betas: Vec>, + e: FpVar, +} + #[derive(Clone, Debug)] pub struct Witness { w: Vec, diff --git a/folding-schemes/src/folding/protogalaxy/traits.rs b/folding-schemes/src/folding/protogalaxy/traits.rs index ff943e1..db96638 100644 --- a/folding-schemes/src/folding/protogalaxy/traits.rs +++ b/folding-schemes/src/folding/protogalaxy/traits.rs @@ -1,23 +1,42 @@ -use ark_crypto_primitives::sponge::Absorb; -use ark_ec::{CurveGroup, Group}; +use ark_crypto_primitives::sponge::{constraints::AbsorbGadget, Absorb}; +use ark_ec::CurveGroup; +use ark_ff::PrimeField; +use ark_r1cs_std::{fields::fp::FpVar, uint8::UInt8, ToConstraintFieldGadget}; +use ark_relations::r1cs::SynthesisError; -use super::CommittedInstance; -use crate::transcript::{poseidon::PoseidonTranscript, Transcript}; -use crate::Error; +use super::{CommittedInstance, CommittedInstanceVar}; +use crate::transcript::AbsorbNonNative; -/// ProtoGalaxyTranscript extends [`Transcript`] with the method to absorb ProtoGalaxy's -/// CommittedInstance. -pub trait ProtoGalaxyTranscript: Transcript { - fn absorb_committed_instance(&mut self, ci: &CommittedInstance) -> Result<(), Error> { - self.absorb_point(&ci.phi)?; - self.absorb_vec(&ci.betas); - self.absorb(&ci.e); - Ok(()) +// Implements the trait for absorbing ProtoGalaxy's CommittedInstance. +impl Absorb for CommittedInstance +where + C::ScalarField: Absorb, +{ + fn to_sponge_bytes(&self, _dest: &mut Vec) { + unimplemented!() + } + + fn to_sponge_field_elements(&self, dest: &mut Vec) { + self.phi + .to_native_sponge_field_elements_as_vec() + .to_sponge_field_elements(dest); + self.betas.to_sponge_field_elements(dest); + self.e.to_sponge_field_elements(dest); } } -// Implements ProtoGalaxyTranscript for PoseidonTranscript -impl ProtoGalaxyTranscript for PoseidonTranscript where - ::ScalarField: Absorb -{ +// Implements the trait for absorbing ProtoGalaxy's CommittedInstanceVar in-circuit. +impl AbsorbGadget for CommittedInstanceVar { + fn to_sponge_bytes(&self) -> Result>, SynthesisError> { + unimplemented!() + } + + fn to_sponge_field_elements(&self) -> Result>, SynthesisError> { + Ok([ + self.phi.to_constraint_field()?, + self.betas.to_sponge_field_elements()?, + self.e.to_sponge_field_elements()?, + ] + .concat()) + } } diff --git a/folding-schemes/src/frontend/circom/mod.rs b/folding-schemes/src/frontend/circom/mod.rs index 9259a94..303a5de 100644 --- a/folding-schemes/src/frontend/circom/mod.rs +++ b/folding-schemes/src/frontend/circom/mod.rs @@ -207,8 +207,7 @@ impl CircomFCircuit { pub mod tests { use super::*; use ark_bn254::Fr; - use ark_r1cs_std::alloc::AllocVar; - use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystem}; + use ark_relations::r1cs::ConstraintSystem; // Tests the step_native function of CircomFCircuit. #[test] diff --git a/folding-schemes/src/frontend/circom/utils.rs b/folding-schemes/src/frontend/circom/utils.rs index 4bdab8b..9ee7dfb 100644 --- a/folding-schemes/src/frontend/circom/utils.rs +++ b/folding-schemes/src/frontend/circom/utils.rs @@ -110,7 +110,6 @@ mod tests { use ark_circom::circom::{CircomBuilder, CircomConfig}; use ark_circom::CircomCircuit; use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystem}; - use std::path::PathBuf; //To generate .r1cs and .wasm files, run the below command in the terminal. //bash ./folding-schemes/src/frontend/circom/test_folder/compile.sh diff --git a/folding-schemes/src/frontend/mod.rs b/folding-schemes/src/frontend/mod.rs index 59f18eb..15fcc74 100644 --- a/folding-schemes/src/frontend/mod.rs +++ b/folding-schemes/src/frontend/mod.rs @@ -52,9 +52,7 @@ pub mod tests { use super::*; use ark_bn254::Fr; use ark_r1cs_std::{alloc::AllocVar, eq::EqGadget}; - use ark_relations::r1cs::{ - ConstraintSynthesizer, ConstraintSystem, ConstraintSystemRef, SynthesisError, - }; + use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystem}; use core::marker::PhantomData; /// CubicFCircuit is a struct that implements the FCircuit trait, for the R1CS example circuit diff --git a/folding-schemes/src/transcript/mod.rs b/folding-schemes/src/transcript/mod.rs index 0e22a11..2c728d4 100644 --- a/folding-schemes/src/transcript/mod.rs +++ b/folding-schemes/src/transcript/mod.rs @@ -1,31 +1,103 @@ -use crate::Error; +use ark_crypto_primitives::sponge::{constraints::CryptographicSpongeVar, CryptographicSponge}; use ark_ec::CurveGroup; use ark_ff::PrimeField; -use ark_r1cs_std::{boolean::Boolean, fields::fp::FpVar}; -use ark_relations::r1cs::{ConstraintSystemRef, SynthesisError}; -use ark_std::fmt::Debug; +use ark_r1cs_std::{ + boolean::Boolean, fields::fp::FpVar, groups::CurveVar, ToConstraintFieldGadget, +}; +use ark_relations::r1cs::SynthesisError; pub mod poseidon; -pub trait Transcript { - type TranscriptConfig: Debug; +/// An interface for objects that can be absorbed by a `Transcript`. +/// +/// Matches `Absorb` in `ark-crypto-primitives`. +pub trait AbsorbNonNative { + /// Converts the object into field elements that can be absorbed by a `Transcript`. + /// Append the list to `dest` + fn to_native_sponge_field_elements(&self, dest: &mut Vec); - fn new(config: &Self::TranscriptConfig) -> Self; - fn absorb(&mut self, v: &C::ScalarField); - fn absorb_vec(&mut self, v: &[C::ScalarField]); - fn absorb_point(&mut self, v: &C) -> Result<(), Error>; - fn get_challenge(&mut self) -> C::ScalarField; + /// Converts the object into field elements that can be absorbed by a `Transcript`. + /// Return the list as `Vec` + fn to_native_sponge_field_elements_as_vec(&self) -> Vec { + let mut result = Vec::new(); + self.to_native_sponge_field_elements(&mut result); + result + } +} + +/// An interface for objects that can be absorbed by a `TranscriptVar` whose constraint field +/// is `F`. +/// +/// Matches `AbsorbGadget` in `ark-crypto-primitives`. +pub trait AbsorbNonNativeGadget { + /// Converts the object into field elements that can be absorbed by a `TranscriptVar`. + fn to_native_sponge_field_elements(&self) -> Result>, SynthesisError>; +} + +pub trait Transcript: CryptographicSponge { + /// `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`. + /// + /// If the sponge field `F` is `C::ScalarField`, call `absorb_nonnative` + /// instead. + fn absorb_point>(&mut self, v: &C); + /// `absorb_nonnative` is for structs that contain non-native (field or + /// group) elements, including: + /// + /// - A field element of type `T: PrimeField` that will be absorbed into a + /// sponge that operates in another field `F != T`. + /// - A group element of type `C: CurveGroup` that will be absorbed into a + /// sponge that operates in another field `F != C::BaseField`, e.g., + /// `F = C::ScalarField`. + /// - A `CommittedInstance` on the secondary curve (used for CycleFold) that + /// will be absorbed into a sponge that operates in the (scalar field of + /// the) primary curve. + /// + /// Note that although a `CommittedInstance` for `AugmentedFCircuit` on + /// the primary curve also contains non-native elements, we still regard + /// it as native, because the sponge is on the same curve. + fn absorb_nonnative>(&mut self, v: &V); + + fn get_challenge(&mut self) -> F; /// get_challenge_nbits returns a field element of size nbits fn get_challenge_nbits(&mut self, nbits: usize) -> Vec; - fn get_challenges(&mut self, n: usize) -> Vec; + fn get_challenges(&mut self, n: usize) -> Vec; } -pub trait TranscriptVar { - type TranscriptVarConfig: Debug; +pub trait TranscriptVar: + CryptographicSpongeVar +{ + /// `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`. + /// + /// If the sponge field `F` is `C::ScalarField`, call `absorb_nonnative` + /// instead. + fn absorb_point, GC: CurveVar + ToConstraintFieldGadget>( + &mut self, + v: &GC, + ) -> Result<(), SynthesisError>; + /// `absorb_nonnative` is for structs that contain non-native (field or + /// group) elements, including: + /// + /// - A field element of type `T: PrimeField` that will be absorbed into a + /// sponge that operates in another field `F != T`. + /// - A group element of type `C: CurveGroup` that will be absorbed into a + /// sponge that operates in another field `F != C::BaseField`, e.g., + /// `F = C::ScalarField`. + /// - A `CommittedInstance` on the secondary curve (used for CycleFold) that + /// will be absorbed into a sponge that operates in the (scalar field of + /// the) primary curve. + /// + /// Note that although a `CommittedInstance` for `AugmentedFCircuit` on + /// the primary curve also contains non-native elements, we still regard + /// it as native, because the sponge is on the same curve. + fn absorb_nonnative>( + &mut self, + v: &V, + ) -> Result<(), SynthesisError>; - fn new(cs: ConstraintSystemRef, poseidon_config: &Self::TranscriptVarConfig) -> Self; - fn absorb(&mut self, v: FpVar) -> Result<(), SynthesisError>; - fn absorb_vec(&mut self, v: &[FpVar]) -> Result<(), SynthesisError>; fn get_challenge(&mut self) -> Result, SynthesisError>; /// returns the bit representation of the challenge, we use its output in-circuit for the /// `GC.scalar_mul_le` method. diff --git a/folding-schemes/src/transcript/poseidon.rs b/folding-schemes/src/transcript/poseidon.rs index 55e7f4f..0496a17 100644 --- a/folding-schemes/src/transcript/poseidon.rs +++ b/folding-schemes/src/transcript/poseidon.rs @@ -3,114 +3,86 @@ use ark_crypto_primitives::sponge::{ poseidon::{constraints::PoseidonSpongeVar, PoseidonConfig, PoseidonSponge}, Absorb, CryptographicSponge, }; -use ark_ec::{AffineRepr, CurveGroup, Group}; -use ark_ff::{BigInteger, Field, PrimeField}; -use ark_r1cs_std::{boolean::Boolean, fields::fp::FpVar}; -use ark_relations::r1cs::{ConstraintSystemRef, SynthesisError}; -use ark_std::Zero; - -use crate::transcript::Transcript; -use crate::Error; - -use super::TranscriptVar; - -/// PoseidonTranscript implements the Transcript trait using the Poseidon hash -pub struct PoseidonTranscript -where - ::ScalarField: Absorb, -{ - sponge: PoseidonSponge, -} +use ark_ec::{AffineRepr, CurveGroup}; +use ark_ff::{BigInteger, PrimeField}; +use ark_r1cs_std::{ + boolean::Boolean, fields::fp::FpVar, groups::CurveVar, ToConstraintFieldGadget, +}; +use ark_relations::r1cs::SynthesisError; -impl Transcript for PoseidonTranscript -where - ::ScalarField: Absorb, -{ - type TranscriptConfig = PoseidonConfig; +use super::{AbsorbNonNative, AbsorbNonNativeGadget, Transcript, TranscriptVar}; - fn new(poseidon_config: &Self::TranscriptConfig) -> Self { - let sponge = PoseidonSponge::::new(poseidon_config); - Self { sponge } - } - fn absorb(&mut self, v: &C::ScalarField) { - self.sponge.absorb(&v); +impl Transcript for PoseidonSponge { + // Compatible with the in-circuit `TranscriptVar::absorb_point` + fn absorb_point>(&mut self, p: &C) { + let (x, y) = match p.into_affine().xy() { + Some((&x, &y)) => (x, y), + None => (C::BaseField::zero(), C::BaseField::zero()), + }; + self.absorb(&x); + self.absorb(&y); } - fn absorb_vec(&mut self, v: &[C::ScalarField]) { - self.sponge.absorb(&v); + fn absorb_nonnative>(&mut self, v: &V) { + self.absorb(&v.to_native_sponge_field_elements_as_vec()); } - fn absorb_point(&mut self, p: &C) -> Result<(), Error> { - self.sponge.absorb(&prepare_point(p)?); - Ok(()) - } - fn get_challenge(&mut self) -> C::ScalarField { - let c = self.sponge.squeeze_field_elements(1); - self.sponge.absorb(&c[0]); + fn get_challenge(&mut self) -> F { + let c = self.squeeze_field_elements(1); + self.absorb(&c[0]); c[0] } fn get_challenge_nbits(&mut self, nbits: usize) -> Vec { - self.sponge.squeeze_bits(nbits) + let bits = self.squeeze_bits(nbits); + self.absorb(&F::from(F::BigInt::from_bits_le(&bits))); + bits } - fn get_challenges(&mut self, n: usize) -> Vec { - let c = self.sponge.squeeze_field_elements(n); - self.sponge.absorb(&c); + fn get_challenges(&mut self, n: usize) -> Vec { + let c = self.squeeze_field_elements(n); + self.absorb(&c); c } } -// Returns the point coordinates in Fr, so it can be absorbed by the transcript. It does not work -// over bytes in order to have a logic that can be reproduced in-circuit. -fn prepare_point(p: &C) -> Result, Error> { - let affine = p.into_affine(); - let zero_point = (&C::BaseField::zero(), &C::BaseField::zero()); - let xy = affine.xy().unwrap_or(zero_point); - - let x_bi = - xy.0.to_base_prime_field_elements() - .next() - .expect("a") - .into_bigint(); - let y_bi = - xy.1.to_base_prime_field_elements() - .next() - .expect("a") - .into_bigint(); - Ok(vec![ - C::ScalarField::from_le_bytes_mod_order(x_bi.to_bytes_le().as_ref()), - C::ScalarField::from_le_bytes_mod_order(y_bi.to_bytes_le().as_ref()), - ]) -} - -/// PoseidonTranscriptVar implements the gadget compatible with PoseidonTranscript -pub struct PoseidonTranscriptVar { - sponge: PoseidonSpongeVar, -} -impl TranscriptVar for PoseidonTranscriptVar { - type TranscriptVarConfig = PoseidonConfig; - - fn new(cs: ConstraintSystemRef, poseidon_config: &Self::TranscriptVarConfig) -> Self { - let sponge = PoseidonSpongeVar::::new(cs, poseidon_config); - Self { sponge } +impl TranscriptVar> for PoseidonSpongeVar { + fn absorb_point< + C: CurveGroup, + GC: CurveVar + ToConstraintFieldGadget, + >( + &mut self, + v: &GC, + ) -> Result<(), SynthesisError> { + let mut vec = v.to_constraint_field()?; + // The last element in the vector tells whether the point is infinity, + // but we can in fact avoid absorbing it without loss of soundness. + // This is because the `to_constraint_field` method internally invokes + // [`ProjectiveVar::to_afine`](https://github.com/arkworks-rs/r1cs-std/blob/4020fbc22625621baa8125ede87abaeac3c1ca26/src/groups/curves/short_weierstrass/mod.rs#L160-L195), + // which guarantees that an infinity point is represented as `(0, 0)`, + // but the y-coordinate of a non-infinity point is never 0 (for why, see + // https://crypto.stackexchange.com/a/108242 ). + vec.pop(); + self.absorb(&vec) } - fn absorb(&mut self, v: FpVar) -> Result<(), SynthesisError> { - self.sponge.absorb(&v) - } - fn absorb_vec(&mut self, v: &[FpVar]) -> Result<(), SynthesisError> { - self.sponge.absorb(&v) + fn absorb_nonnative>( + &mut self, + v: &V, + ) -> Result<(), SynthesisError> { + self.absorb(&v.to_native_sponge_field_elements()?) } fn get_challenge(&mut self) -> Result, SynthesisError> { - let c = self.sponge.squeeze_field_elements(1)?; - self.sponge.absorb(&c[0])?; + let c = self.squeeze_field_elements(1)?; + self.absorb(&c[0])?; Ok(c[0].clone()) } /// returns the bit representation of the challenge, we use its output in-circuit for the /// `GC.scalar_mul_le` method. fn get_challenge_nbits(&mut self, nbits: usize) -> Result>, SynthesisError> { - self.sponge.squeeze_bits(nbits) + let bits = self.squeeze_bits(nbits)?; + self.absorb(&Boolean::le_bits_to_fp_var(&bits)?)?; + Ok(bits) } fn get_challenges(&mut self, n: usize) -> Result>, SynthesisError> { - let c = self.sponge.squeeze_field_elements(n)?; - self.sponge.absorb(&c)?; + let c = self.squeeze_field_elements(n)?; + self.absorb(&c)?; Ok(c) } } @@ -147,12 +119,17 @@ pub fn poseidon_canonical_config() -> PoseidonConfig { #[cfg(test)] pub mod tests { + use crate::folding::circuits::nonnative::affine::NonNativeAffineVar; + use super::*; - use ark_bn254::{constraints::GVar, Fq, Fr, G1Projective as G1}; - use ark_grumpkin::Projective; - use ark_r1cs_std::{alloc::AllocVar, groups::CurveVar, R1CSVar}; + use ark_bn254::{constraints::GVar, g1::Config, Fq, Fr, G1Projective as G1}; + use ark_ec::Group; + use ark_ff::UniformRand; + use ark_r1cs_std::{ + alloc::AllocVar, groups::curves::short_weierstrass::ProjectiveVar, R1CSVar, + }; use ark_relations::r1cs::ConstraintSystem; - use std::ops::Mul; + use ark_std::test_rng; // Test with value taken from https://github.com/iden3/circomlibjs/blob/43cc582b100fc3459cf78d903a6f538e5d7f38ee/test/poseidon.js#L32 #[test] @@ -178,19 +155,69 @@ pub mod tests { ); } + #[test] + fn test_transcript_and_transcriptvar_absorb_native_point() { + // use 'native' transcript + let config = poseidon_canonical_config::(); + let mut tr = PoseidonSponge::::new(&config); + let rng = &mut test_rng(); + + let p = G1::rand(rng); + tr.absorb_point(&p); + let c = tr.get_challenge(); + + // use 'gadget' transcript + let cs = ConstraintSystem::::new_ref(); + let mut tr_var = PoseidonSpongeVar::::new(cs.clone(), &config); + let p_var = ProjectiveVar::>::new_witness( + ConstraintSystem::::new_ref(), + || Ok(p), + ) + .unwrap(); + tr_var.absorb_point(&p_var).unwrap(); + let c_var = tr_var.get_challenge().unwrap(); + + // assert that native & gadget transcripts return the same challenge + assert_eq!(c, c_var.value().unwrap()); + } + + #[test] + fn test_transcript_and_transcriptvar_absorb_nonnative_point() { + // use 'native' transcript + let config = poseidon_canonical_config::(); + let mut tr = PoseidonSponge::::new(&config); + let rng = &mut test_rng(); + + let p = G1::rand(rng); + tr.absorb_nonnative(&p); + let c = tr.get_challenge(); + + // use 'gadget' transcript + let cs = ConstraintSystem::::new_ref(); + let mut tr_var = PoseidonSpongeVar::::new(cs.clone(), &config); + let p_var = + NonNativeAffineVar::::new_witness(ConstraintSystem::::new_ref(), || Ok(p)) + .unwrap(); + tr_var.absorb_nonnative(&p_var).unwrap(); + let c_var = tr_var.get_challenge().unwrap(); + + // assert that native & gadget transcripts return the same challenge + assert_eq!(c, c_var.value().unwrap()); + } + #[test] fn test_transcript_and_transcriptvar_get_challenge() { // use 'native' transcript let config = poseidon_canonical_config::(); - let mut tr = PoseidonTranscript::::new(&config); + let mut tr = PoseidonSponge::::new(&config); tr.absorb(&Fr::from(42_u32)); let c = tr.get_challenge(); // use 'gadget' transcript let cs = ConstraintSystem::::new_ref(); - let mut tr_var = PoseidonTranscriptVar::::new(cs.clone(), &config); + let mut tr_var = PoseidonSpongeVar::::new(cs.clone(), &config); let v = FpVar::::new_witness(cs.clone(), || Ok(Fr::from(42_u32))).unwrap(); - tr_var.absorb(v).unwrap(); + tr_var.absorb(&v).unwrap(); let c_var = tr_var.get_challenge().unwrap(); // assert that native & gadget transcripts return the same challenge @@ -203,7 +230,7 @@ pub mod tests { // use 'native' transcript let config = poseidon_canonical_config::(); - let mut tr = PoseidonTranscript::::new(&config); + let mut tr = PoseidonSponge::::new(&config); tr.absorb(&Fq::from(42_u32)); // get challenge from native transcript @@ -211,9 +238,9 @@ pub mod tests { // use 'gadget' transcript let cs = ConstraintSystem::::new_ref(); - let mut tr_var = PoseidonTranscriptVar::::new(cs.clone(), &config); + let mut tr_var = PoseidonSpongeVar::::new(cs.clone(), &config); let v = FpVar::::new_witness(cs.clone(), || Ok(Fq::from(42_u32))).unwrap(); - tr_var.absorb(v).unwrap(); + tr_var.absorb(&v).unwrap(); // get challenge from circuit transcript let c_var = tr_var.get_challenge_nbits(nbits).unwrap(); @@ -226,7 +253,7 @@ pub mod tests { // native c*P let c_Fr = Fr::from_bigint(BigInteger::from_bits_le(&c_bits)).unwrap(); - let cP_native = P.mul(c_Fr); + let cP_native = P * c_Fr; // native c*P using mul_bits_be (notice the .rev to convert the LE to BE) let cP_native_bits = P.mul_bits_be(c_bits.into_iter().rev()); diff --git a/folding-schemes/src/utils/espresso/sum_check/mod.rs b/folding-schemes/src/utils/espresso/sum_check/mod.rs index 86842c4..638719d 100644 --- a/folding-schemes/src/utils/espresso/sum_check/mod.rs +++ b/folding-schemes/src/utils/espresso/sum_check/mod.rs @@ -13,7 +13,7 @@ use crate::{ transcript::Transcript, utils::virtual_polynomial::{VPAuxInfo, VirtualPolynomial}, }; -use ark_ec::CurveGroup; +use ark_crypto_primitives::sponge::Absorb; use ark_ff::PrimeField; use ark_poly::univariate::DensePolynomial; use ark_poly::{DenseMultilinearExtension, DenseUVPolynomial, Polynomial}; @@ -22,7 +22,6 @@ use std::{fmt::Debug, marker::PhantomData, sync::Arc}; use crate::utils::sum_check::structs::IOPProverMessage; use crate::utils::sum_check::structs::IOPVerifierState; -use ark_ff::Field; use espresso_subroutines::poly_iop::prelude::PolyIOPErrors; use structs::{IOPProof, IOPProverState}; @@ -31,7 +30,7 @@ pub mod structs; pub mod verifier; /// A generic sum-check trait over a curve group -pub trait SumCheck { +pub trait SumCheck { type VirtualPolynomial; type VPAuxInfo; type MultilinearExtension; @@ -40,27 +39,27 @@ pub trait SumCheck { type SumCheckSubClaim: Clone + Debug + Default + PartialEq; /// Extract sum from the proof - fn extract_sum(proof: &Self::SumCheckProof) -> C::ScalarField; + fn extract_sum(proof: &Self::SumCheckProof) -> F; /// Generate proof of the sum of polynomial over {0,1}^`num_vars` /// /// The polynomial is represented in the form of a VirtualPolynomial. fn prove( poly: &Self::VirtualPolynomial, - transcript: &mut impl Transcript, + transcript: &mut impl Transcript, ) -> Result; /// Verify the claimed sum using the proof fn verify( - sum: C::ScalarField, + sum: F, proof: &Self::SumCheckProof, aux_info: &Self::VPAuxInfo, - transcript: &mut impl Transcript, + transcript: &mut impl Transcript, ) -> Result; } /// Trait for sum check protocol prover side APIs. -pub trait SumCheckProver +pub trait SumCheckProver where Self: Sized, { @@ -77,12 +76,12 @@ where /// Main algorithm used is from section 3.2 of [XZZPS19](https://eprint.iacr.org/2019/317.pdf#subsection.3.2). fn prove_round_and_update_state( &mut self, - challenge: &Option, + challenge: &Option, ) -> Result; } /// Trait for sum check protocol verifier side APIs. -pub trait SumCheckVerifier { +pub trait SumCheckVerifier { type VPAuxInfo; type ProverMessage; type Challenge; @@ -100,7 +99,7 @@ pub trait SumCheckVerifier { fn verify_round_and_update_state( &mut self, prover_msg: &Self::ProverMessage, - transcript: &mut impl Transcript, + transcript: &mut impl Transcript, ) -> Result; /// This function verifies the deferred checks in the interactive version of @@ -113,7 +112,7 @@ pub trait SumCheckVerifier { /// Larger field size guarantees smaller soundness error. fn check_and_generate_subclaim( &self, - asserted_sum: &C::ScalarField, + asserted_sum: &F, ) -> Result; } @@ -129,42 +128,42 @@ pub struct SumCheckSubClaim { } #[derive(Clone, Debug, Default, Copy, PartialEq, Eq)] -pub struct IOPSumCheck> { +pub struct IOPSumCheck> { #[doc(hidden)] - phantom: PhantomData, + phantom: PhantomData, #[doc(hidden)] phantom2: PhantomData, } -impl> SumCheck for IOPSumCheck { - type SumCheckProof = IOPProof; - type VirtualPolynomial = VirtualPolynomial; - type VPAuxInfo = VPAuxInfo; - type MultilinearExtension = Arc>; - type SumCheckSubClaim = SumCheckSubClaim; +impl> SumCheck for IOPSumCheck { + type SumCheckProof = IOPProof; + type VirtualPolynomial = VirtualPolynomial; + type VPAuxInfo = VPAuxInfo; + type MultilinearExtension = Arc>; + type SumCheckSubClaim = SumCheckSubClaim; - fn extract_sum(proof: &Self::SumCheckProof) -> C::ScalarField { + fn extract_sum(proof: &Self::SumCheckProof) -> F { let start = start_timer!(|| "extract sum"); let poly = DensePolynomial::from_coefficients_vec(proof.proofs[0].coeffs.clone()); - let res = poly.evaluate(&C::ScalarField::ONE) + poly.evaluate(&C::ScalarField::ZERO); + let res = poly.evaluate(&F::ONE) + poly.evaluate(&F::ZERO); end_timer!(start); res } fn prove( - poly: &VirtualPolynomial, - transcript: &mut impl Transcript, - ) -> Result, PolyIOPErrors> { - transcript.absorb(&C::ScalarField::from(poly.aux_info.num_variables as u64)); - transcript.absorb(&C::ScalarField::from(poly.aux_info.max_degree as u64)); - let mut prover_state: IOPProverState = IOPProverState::prover_init(poly)?; - let mut challenge: Option = None; - let mut prover_msgs: Vec> = + poly: &VirtualPolynomial, + transcript: &mut impl Transcript, + ) -> Result, PolyIOPErrors> { + transcript.absorb(&F::from(poly.aux_info.num_variables as u64)); + transcript.absorb(&F::from(poly.aux_info.max_degree as u64)); + let mut prover_state: IOPProverState = IOPProverState::prover_init(poly)?; + let mut challenge: Option = None; + let mut prover_msgs: Vec> = Vec::with_capacity(poly.aux_info.num_variables); for _ in 0..poly.aux_info.num_variables { - let prover_msg: IOPProverMessage = + let prover_msg: IOPProverMessage = IOPProverState::prove_round_and_update_state(&mut prover_state, &challenge)?; - transcript.absorb_vec(&prover_msg.coeffs); + transcript.absorb(&prover_msg.coeffs); prover_msgs.push(prover_msg); challenge = Some(transcript.get_challenge()); } @@ -178,17 +177,17 @@ impl> SumCheck for IOPSumCheck { } fn verify( - claimed_sum: C::ScalarField, - proof: &IOPProof, - aux_info: &VPAuxInfo, - transcript: &mut impl Transcript, - ) -> Result, PolyIOPErrors> { - transcript.absorb(&C::ScalarField::from(aux_info.num_variables as u64)); - transcript.absorb(&C::ScalarField::from(aux_info.max_degree as u64)); + claimed_sum: F, + proof: &IOPProof, + aux_info: &VPAuxInfo, + transcript: &mut impl Transcript, + ) -> Result, PolyIOPErrors> { + transcript.absorb(&F::from(aux_info.num_variables as u64)); + transcript.absorb(&F::from(aux_info.max_degree as u64)); let mut verifier_state = IOPVerifierState::verifier_init(aux_info); for i in 0..aux_info.num_variables { let prover_msg = proof.proofs.get(i).expect("proof is incomplete"); - transcript.absorb_vec(&prover_msg.coeffs); + transcript.absorb(&prover_msg.coeffs); IOPVerifierState::verify_round_and_update_state( &mut verifier_state, prover_msg, @@ -204,16 +203,15 @@ impl> SumCheck for IOPSumCheck { pub mod tests { use std::sync::Arc; + use ark_crypto_primitives::sponge::poseidon::PoseidonSponge; + use ark_crypto_primitives::sponge::CryptographicSponge; use ark_ff::Field; use ark_pallas::Fr; - use ark_pallas::Projective; use ark_poly::DenseMultilinearExtension; use ark_poly::MultilinearExtension; use ark_std::test_rng; use crate::transcript::poseidon::poseidon_canonical_config; - use crate::transcript::poseidon::PoseidonTranscript; - use crate::transcript::Transcript; use crate::utils::sum_check::SumCheck; use crate::utils::virtual_polynomial::VirtualPolynomial; @@ -227,20 +225,19 @@ pub mod tests { let poseidon_config = poseidon_canonical_config::(); // sum-check prove - let mut poseidon_transcript_prove: PoseidonTranscript = - PoseidonTranscript::::new(&poseidon_config); - let sum_check = IOPSumCheck::>::prove( + let mut poseidon_transcript_prove: PoseidonSponge = + PoseidonSponge::::new(&poseidon_config); + let sum_check = IOPSumCheck::>::prove( &virtual_poly, &mut poseidon_transcript_prove, ) .unwrap(); // sum-check verify - let claimed_sum = - IOPSumCheck::>::extract_sum(&sum_check); - let mut poseidon_transcript_verify: PoseidonTranscript = - PoseidonTranscript::::new(&poseidon_config); - let res_verify = IOPSumCheck::>::verify( + let claimed_sum = IOPSumCheck::>::extract_sum(&sum_check); + let mut poseidon_transcript_verify: PoseidonSponge = + PoseidonSponge::::new(&poseidon_config); + let res_verify = IOPSumCheck::>::verify( claimed_sum, &sum_check, &virtual_poly.aux_info, diff --git a/folding-schemes/src/utils/espresso/sum_check/prover.rs b/folding-schemes/src/utils/espresso/sum_check/prover.rs index 46d2e0c..24440d2 100644 --- a/folding-schemes/src/utils/espresso/sum_check/prover.rs +++ b/folding-schemes/src/utils/espresso/sum_check/prover.rs @@ -14,11 +14,9 @@ use crate::utils::{ lagrange_poly::compute_lagrange_interpolated_poly, multilinear_polynomial::fix_variables, virtual_polynomial::VirtualPolynomial, }; -use ark_ec::CurveGroup; -use ark_ff::Field; use ark_ff::{batch_inversion, PrimeField}; use ark_poly::DenseMultilinearExtension; -use ark_std::{cfg_into_iter, end_timer, start_timer, vec::Vec}; +use ark_std::{cfg_into_iter, end_timer, start_timer}; use rayon::prelude::{IntoParallelIterator, IntoParallelRefIterator}; use std::sync::Arc; @@ -28,9 +26,9 @@ use espresso_subroutines::poly_iop::prelude::PolyIOPErrors; // #[cfg(feature = "parallel")] use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator}; -impl SumCheckProver for IOPProverState { - type VirtualPolynomial = VirtualPolynomial; - type ProverMessage = IOPProverMessage; +impl SumCheckProver for IOPProverState { + type VirtualPolynomial = VirtualPolynomial; + type ProverMessage = IOPProverMessage; /// Initialize the prover state to argue for the sum of the input polynomial /// over {0,1}^`num_vars`. @@ -49,9 +47,7 @@ impl SumCheckProver for IOPProverState { poly: polynomial.clone(), extrapolation_aux: (1..polynomial.aux_info.max_degree) .map(|degree| { - let points = (0..1 + degree as u64) - .map(C::ScalarField::from) - .collect::>(); + let points = (0..1 + degree as u64).map(F::from).collect::>(); let weights = barycentric_weights(&points); (points, weights) }) @@ -65,7 +61,7 @@ impl SumCheckProver for IOPProverState { /// Main algorithm used is from section 3.2 of [XZZPS19](https://eprint.iacr.org/2019/317.pdf#subsection.3.2). fn prove_round_and_update_state( &mut self, - challenge: &Option, + challenge: &Option, ) -> Result { // let start = // start_timer!(|| format!("sum check prove {}-th round and update state", @@ -90,7 +86,7 @@ impl SumCheckProver for IOPProverState { // g(r_1, ..., r_{m-1}, x_m ... x_n) // // eval g over r_m, and mutate g to g(r_1, ... r_m,, x_{m+1}... x_n) - let mut flattened_ml_extensions: Vec> = self + let mut flattened_ml_extensions: Vec> = self .poly .flattened_ml_extensions .par_iter() @@ -124,7 +120,7 @@ impl SumCheckProver for IOPProverState { self.round += 1; let products_list = self.poly.products.clone(); - let mut products_sum = vec![C::ScalarField::ZERO; self.poly.aux_info.max_degree + 1]; + let mut products_sum = vec![F::ZERO; self.poly.aux_info.max_degree + 1]; // Step 2: generate sum for the partial evaluated polynomial: // f(r_1, ... r_m,, x_{m+1}... x_n) @@ -134,8 +130,8 @@ impl SumCheckProver for IOPProverState { .fold( || { ( - vec![(C::ScalarField::ZERO, C::ScalarField::ZERO); products.len()], - vec![C::ScalarField::ZERO; products.len() + 1], + vec![(F::ZERO, F::ZERO); products.len()], + vec![F::ZERO; products.len() + 1], ) }, |(mut buf, mut acc), b| { @@ -146,17 +142,17 @@ impl SumCheckProver for IOPProverState { *eval = table[b << 1]; *step = table[(b << 1) + 1] - table[b << 1]; }); - acc[0] += buf.iter().map(|(eval, _)| eval).product::(); + acc[0] += buf.iter().map(|(eval, _)| eval).product::(); acc[1..].iter_mut().for_each(|acc| { buf.iter_mut().for_each(|(eval, step)| *eval += step as &_); - *acc += buf.iter().map(|(eval, _)| eval).product::(); + *acc += buf.iter().map(|(eval, _)| eval).product::(); }); (buf, acc) }, ) .map(|(_, partial)| partial) .reduce( - || vec![C::ScalarField::ZERO; products.len() + 1], + || vec![F::ZERO; products.len() + 1], |mut sum, partial| { sum.iter_mut() .zip(partial.iter()) @@ -168,7 +164,7 @@ impl SumCheckProver for IOPProverState { let extraploation = cfg_into_iter!(0..self.poly.aux_info.max_degree - products.len()) .map(|i| { let (points, weights) = &self.extrapolation_aux[products.len() - 1]; - let at = C::ScalarField::from((products.len() + 1 + i) as u64); + let at = F::from((products.len() + 1 + i) as u64); extrapolate(points, weights, &sum, &at) }) .collect::>(); @@ -184,7 +180,7 @@ impl SumCheckProver for IOPProverState { .map(|x| Arc::new(x.clone())) .collect(); - let prover_poly = compute_lagrange_interpolated_poly::(&products_sum); + let prover_poly = compute_lagrange_interpolated_poly::(&products_sum); Ok(IOPProverMessage { coeffs: prover_poly.coeffs, }) diff --git a/folding-schemes/src/utils/espresso/sum_check/structs.rs b/folding-schemes/src/utils/espresso/sum_check/structs.rs index b34ed81..de487d9 100644 --- a/folding-schemes/src/utils/espresso/sum_check/structs.rs +++ b/folding-schemes/src/utils/espresso/sum_check/structs.rs @@ -10,7 +10,6 @@ //! This module defines structs that are shared by all sub protocols. use crate::utils::virtual_polynomial::VirtualPolynomial; -use ark_ec::CurveGroup; use ark_ff::PrimeField; use ark_serialize::CanonicalSerialize; @@ -33,28 +32,28 @@ pub struct IOPProverMessage { /// Prover State of a PolyIOP. #[derive(Debug)] -pub struct IOPProverState { +pub struct IOPProverState { /// sampled randomness given by the verifier - pub challenges: Vec, + pub challenges: Vec, /// the current round number pub(crate) round: usize, /// pointer to the virtual polynomial - pub(crate) poly: VirtualPolynomial, + pub(crate) poly: VirtualPolynomial, /// points with precomputed barycentric weights for extrapolating smaller /// degree uni-polys to `max_degree + 1` evaluations. #[allow(clippy::type_complexity)] - pub(crate) extrapolation_aux: Vec<(Vec, Vec)>, + pub(crate) extrapolation_aux: Vec<(Vec, Vec)>, } /// Verifier State of a PolyIOP, generic over a curve group #[derive(Debug)] -pub struct IOPVerifierState { +pub struct IOPVerifierState { pub(crate) round: usize, pub(crate) num_vars: usize, pub(crate) finished: bool, /// a list storing the univariate polynomial in evaluation form sent by the /// prover at each round - pub(crate) polynomials_received: Vec>, + pub(crate) polynomials_received: Vec>, /// a list storing the randomness sampled by the verifier at each round - pub(crate) challenges: Vec, + pub(crate) challenges: Vec, } diff --git a/folding-schemes/src/utils/espresso/sum_check/verifier.rs b/folding-schemes/src/utils/espresso/sum_check/verifier.rs index 55fafb2..8ff83b3 100644 --- a/folding-schemes/src/utils/espresso/sum_check/verifier.rs +++ b/folding-schemes/src/utils/espresso/sum_check/verifier.rs @@ -14,7 +14,7 @@ use super::{ SumCheckSubClaim, SumCheckVerifier, }; use crate::{transcript::Transcript, utils::virtual_polynomial::VPAuxInfo}; -use ark_ec::CurveGroup; +use ark_crypto_primitives::sponge::Absorb; use ark_ff::PrimeField; use ark_poly::Polynomial; use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial}; @@ -24,11 +24,11 @@ use espresso_subroutines::poly_iop::prelude::PolyIOPErrors; #[cfg(feature = "parallel")] use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator}; -impl SumCheckVerifier for IOPVerifierState { - type VPAuxInfo = VPAuxInfo; - type ProverMessage = IOPProverMessage; - type Challenge = C::ScalarField; - type SumCheckSubClaim = SumCheckSubClaim; +impl SumCheckVerifier for IOPVerifierState { + type VPAuxInfo = VPAuxInfo; + type ProverMessage = IOPProverMessage; + type Challenge = F; + type SumCheckSubClaim = SumCheckSubClaim; /// Initialize the verifier's state. fn verifier_init(index_info: &Self::VPAuxInfo) -> Self { @@ -46,9 +46,9 @@ impl SumCheckVerifier for IOPVerifierState { fn verify_round_and_update_state( &mut self, - prover_msg: & as SumCheckVerifier>::ProverMessage, - transcript: &mut impl Transcript, - ) -> Result< as SumCheckVerifier>::Challenge, PolyIOPErrors> { + prover_msg: & as SumCheckVerifier>::ProverMessage, + transcript: &mut impl Transcript, + ) -> Result< as SumCheckVerifier>::Challenge, PolyIOPErrors> { let start = start_timer!(|| format!("sum check verify {}-th round and update state", self.round)); @@ -83,7 +83,7 @@ impl SumCheckVerifier for IOPVerifierState { fn check_and_generate_subclaim( &self, - asserted_sum: &C::ScalarField, + asserted_sum: &F, ) -> Result { let start = start_timer!(|| "sum check check and generate subclaim"); if !self.finished { @@ -136,8 +136,8 @@ impl SumCheckVerifier for IOPVerifierState { .take(self.num_vars) { let poly = DensePolynomial::from_coefficients_slice(coeffs); - let eval_at_one: C::ScalarField = poly.iter().sum(); - let eval_at_zero: C::ScalarField = poly.coeffs[0]; + let eval_at_one: F = poly.iter().sum(); + let eval_at_zero: F = poly.coeffs[0]; let eval = eval_at_one + eval_at_zero; // the deferred check during the interactive phase: diff --git a/folding-schemes/src/utils/espresso/virtual_polynomial.rs b/folding-schemes/src/utils/espresso/virtual_polynomial.rs index 24f0b02..acc843b 100644 --- a/folding-schemes/src/utils/espresso/virtual_polynomial.rs +++ b/folding-schemes/src/utils/espresso/virtual_polynomial.rs @@ -18,8 +18,6 @@ use rayon::prelude::*; use std::{cmp::max, collections::HashMap, marker::PhantomData, ops::Add, sync::Arc}; use thiserror::Error; -use ark_std::string::String; - //-- aritherrors /// A `enum` specifying the possible failure modes of the arithmetics. #[derive(Error, Debug)] diff --git a/folding-schemes/src/utils/lagrange_poly.rs b/folding-schemes/src/utils/lagrange_poly.rs index f3dfaee..22a38e1 100644 --- a/folding-schemes/src/utils/lagrange_poly.rs +++ b/folding-schemes/src/utils/lagrange_poly.rs @@ -52,7 +52,7 @@ mod tests { use crate::utils::lagrange_poly::compute_lagrange_interpolated_poly; use ark_pallas::Fr; use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial, Polynomial}; - use ark_std::{vec::Vec, UniformRand}; + use ark_std::UniformRand; use espresso_subroutines::poly_iop::prelude::PolyIOPErrors; #[test] diff --git a/solidity-verifiers/src/verifiers/kzg.rs b/solidity-verifiers/src/verifiers/kzg.rs index dab3be7..9dc156d 100644 --- a/solidity-verifiers/src/verifiers/kzg.rs +++ b/solidity-verifiers/src/verifiers/kzg.rs @@ -78,7 +78,8 @@ mod tests { utils::HeaderInclusion, ProtocolVerifierKey, }; - use ark_bn254::{Bn254, Fr, G1Projective as G1}; + use ark_bn254::{Bn254, Fr}; + use ark_crypto_primitives::sponge::{poseidon::PoseidonSponge, CryptographicSponge}; use ark_ec::{AffineRepr, CurveGroup}; use ark_ff::{BigInteger, PrimeField}; use ark_std::rand::{RngCore, SeedableRng}; @@ -89,10 +90,7 @@ mod tests { use folding_schemes::{ commitment::{kzg::KZG, CommitmentScheme}, - transcript::{ - poseidon::{poseidon_canonical_config, PoseidonTranscript}, - Transcript, - }, + transcript::{poseidon::poseidon_canonical_config, Transcript}, }; use super::KZG10Verifier; @@ -133,8 +131,8 @@ mod tests { fn kzg_verifier_accepts_and_rejects_proofs() { let mut rng = ark_std::rand::rngs::StdRng::seed_from_u64(test_rng().next_u64()); let poseidon_config = poseidon_canonical_config::(); - let transcript_p = &mut PoseidonTranscript::::new(&poseidon_config); - let transcript_v = &mut PoseidonTranscript::::new(&poseidon_config); + let transcript_p = &mut PoseidonSponge::::new(&poseidon_config); + let transcript_v = &mut PoseidonSponge::::new(&poseidon_config); 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())); @@ -159,7 +157,7 @@ mod tests { let (x_proof, y_proof) = proof_affine.xy().unwrap(); let y = proof.eval.into_bigint().to_bytes_be(); - transcript_v.absorb_point(&cm).unwrap(); + transcript_v.absorb_nonnative(&cm); let x = transcript_v.get_challenge(); let x = x.into_bigint().to_bytes_be();