From 7096ff26d4bd18955ff293a03131c6d1da695707 Mon Sep 17 00:00:00 2001 From: winderica Date: Sun, 8 Sep 2024 07:35:12 +0800 Subject: [PATCH 1/8] Add traits for witness and committed instance --- folding-schemes/src/folding/mod.rs | 1 + folding-schemes/src/folding/traits.rs | 121 ++++++++++++++++++++++++++ folding-schemes/src/lib.rs | 2 + 3 files changed, 124 insertions(+) create mode 100644 folding-schemes/src/folding/traits.rs diff --git a/folding-schemes/src/folding/mod.rs b/folding-schemes/src/folding/mod.rs index 8f76a714..0f3a66fd 100644 --- a/folding-schemes/src/folding/mod.rs +++ b/folding-schemes/src/folding/mod.rs @@ -2,3 +2,4 @@ pub mod circuits; pub mod hypernova; pub mod nova; pub mod protogalaxy; +pub mod traits; diff --git a/folding-schemes/src/folding/traits.rs b/folding-schemes/src/folding/traits.rs new file mode 100644 index 00000000..d780a920 --- /dev/null +++ b/folding-schemes/src/folding/traits.rs @@ -0,0 +1,121 @@ +use ark_crypto_primitives::sponge::{ + constraints::{AbsorbGadget, CryptographicSpongeVar}, + poseidon::constraints::PoseidonSpongeVar, + Absorb, +}; +use ark_ec::CurveGroup; +use ark_ff::PrimeField; +use ark_r1cs_std::{alloc::AllocVar, fields::fp::FpVar, ToConstraintFieldGadget}; +use ark_relations::r1cs::SynthesisError; + +use crate::{transcript::Transcript, Error}; + +use super::circuits::CF1; + +pub trait CommittedInstanceExt { + /// The in-circuit representation of the committed instance. + type Var: AllocVar> + CommittedInstanceVarExt; + /// `hash` implements the committed instance hash compatible with the + /// in-circuit implementation from `CommittedInstanceVarOps::hash`. + /// + /// Returns `H(i, z_0, z_i, U_i)`, where `i` can be `i` but also `i+1`, and + /// `U_i` is the committed instance `self`. + fn hash>>( + &self, + sponge: &T, + pp_hash: CF1, // public params hash + i: CF1, + z_0: &[CF1], + z_i: &[CF1], + ) -> CF1 + where + CF1: Absorb, + Self: Sized + Absorb, + { + 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] + } + + /// Returns the commitments contained in the committed instance. + fn get_commitments(&self) -> Vec; + + /// Returns `true` if the committed instance is an incoming instance, and + /// `false` if it is a running instance. + fn is_incoming(&self) -> bool; + + /// Checks if the committed instance is an incoming instance. + fn check_incoming(&self) -> Result<(), Error> { + self.is_incoming() + .then_some(()) + .ok_or(Error::NotIncomingCommittedInstance) + } +} + +pub trait CommittedInstanceVarExt { + type PointVar: ToConstraintFieldGadget>; + /// `hash` implements the in-circuit committed instance hash compatible with + /// the native implementation from `CommittedInstanceExt::hash`. + /// Returns `H(i, z_0, z_i, U_i)`, where `i` can be `i` but also `i+1`, and + /// `U_i` is the committed instance `self`. + /// + /// Additionally it returns the in-circuit representation of the committed + /// instance `self` as a vector of field elements, so they can be reused in + /// other gadgets avoiding recalculating (reconstraining) them. + #[allow(clippy::type_complexity)] + fn hash( + &self, + sponge: &PoseidonSpongeVar>, + pp_hash: &FpVar>, + i: &FpVar>, + z_0: &[FpVar>], + z_i: &[FpVar>], + ) -> Result<(FpVar>, Vec>>), SynthesisError> + where + Self: AbsorbGadget>, + { + 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)) + } + + /// Returns the commitments contained in the committed instance. + fn get_commitments(&self) -> Vec; + + /// Returns the public inputs contained in the committed instance. + fn get_public_inputs(&self) -> &[FpVar>]; + + /// Generates constraints to enforce that the committed instance is an + /// incoming instance. + fn enforce_incoming(&self) -> Result<(), SynthesisError>; + + /// Generates constraints to enforce that the committed instance `self` is + /// partially equal to another committed instance `other`. + /// Here, only field elements are compared, while commitments (points) are + /// not. + fn enforce_partial_equal(&self, other: &Self) -> Result<(), SynthesisError>; +} + +pub trait WitnessExt { + /// The in-circuit representation of the witness. + type Var: AllocVar + WitnessVarExt; + + /// Returns the openings (i.e., values being committed to) contained in the + /// witness. + fn get_openings(&self) -> Vec<(&[F], F)>; +} + +pub trait WitnessVarExt { + /// Returns the openings (i.e., values being committed to) contained in the + /// witness. + fn get_openings(&self) -> Vec<(&[FpVar], FpVar)>; +} diff --git a/folding-schemes/src/lib.rs b/folding-schemes/src/lib.rs index e4b22ef7..d072a162 100644 --- a/folding-schemes/src/lib.rs +++ b/folding-schemes/src/lib.rs @@ -43,6 +43,8 @@ pub enum Error { IVCVerificationFail, #[error("zkIVC verification failed")] zkIVCVerificationFail, + #[error("Committed instance is expected to an incoming (fresh) instance")] + NotIncomingCommittedInstance, #[error("R1CS instance is expected to not be relaxed")] R1CSUnrelaxedFail, #[error("Could not find the inner ConstraintSystem")] From 2a30a4dc7fe1c7e6e3008c34b0832de447e07480 Mon Sep 17 00:00:00 2001 From: winderica Date: Sun, 8 Sep 2024 07:43:07 +0800 Subject: [PATCH 2/8] Implement witness and committed instance traits for Nova and HyperNova --- folding-schemes/src/folding/hypernova/cccs.rs | 14 ++ .../src/folding/hypernova/circuits.rs | 157 +++++++++--------- .../folding/hypernova/decider_eth_circuit.rs | 18 +- .../src/folding/hypernova/lcccs.rs | 38 ++--- folding-schemes/src/folding/hypernova/mod.rs | 36 ++-- folding-schemes/src/folding/nova/circuits.rs | 87 ++++------ .../src/folding/nova/decider_eth_circuit.rs | 22 +-- folding-schemes/src/folding/nova/mod.rs | 52 +++--- folding-schemes/src/folding/nova/zk.rs | 3 +- 9 files changed, 210 insertions(+), 217 deletions(-) diff --git a/folding-schemes/src/folding/hypernova/cccs.rs b/folding-schemes/src/folding/hypernova/cccs.rs index de5c281c..d592f8c8 100644 --- a/folding-schemes/src/folding/hypernova/cccs.rs +++ b/folding-schemes/src/folding/hypernova/cccs.rs @@ -7,9 +7,11 @@ use std::sync::Arc; use ark_std::rand::Rng; +use super::circuits::CCCSVar; use super::Witness; use crate::arith::{ccs::CCS, Arith}; use crate::commitment::CommitmentScheme; +use crate::folding::traits::CommittedInstanceExt; use crate::transcript::AbsorbNonNative; use crate::utils::mle::dense_vec_to_dense_mle; use crate::utils::vec::mat_vec_mul; @@ -140,6 +142,18 @@ where } } +impl CommittedInstanceExt for CCCS { + type Var = CCCSVar; + + fn get_commitments(&self) -> Vec { + vec![self.C] + } + + fn is_incoming(&self) -> bool { + true + } +} + #[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 1c2d7863..e9802718 100644 --- a/folding-schemes/src/folding/hypernova/circuits.rs +++ b/folding-schemes/src/folding/hypernova/circuits.rs @@ -1,6 +1,6 @@ /// Implementation of [HyperNova](https://eprint.iacr.org/2023/573.pdf) circuits use ark_crypto_primitives::sponge::{ - constraints::CryptographicSpongeVar, + constraints::{AbsorbGadget, CryptographicSpongeVar}, poseidon::{constraints::PoseidonSpongeVar, PoseidonSponge}, CryptographicSponge, }; @@ -14,6 +14,7 @@ use ark_r1cs_std::{ fields::{fp::FpVar, FieldVar}, groups::GroupOpsBounds, prelude::CurveVar, + uint8::UInt8, R1CSVar, ToConstraintFieldGadget, }; use ark_relations::r1cs::{ @@ -41,6 +42,7 @@ use crate::folding::{ CF1, CF2, }, nova::get_r1cs_from_cs, + traits::CommittedInstanceVarExt, }; use crate::frontend::FCircuit; use crate::utils::virtual_polynomial::VPAuxInfo; @@ -52,19 +54,16 @@ use crate::{ /// Committed CCS instance #[derive(Debug, Clone)] -pub struct CCCSVar -where - ::BaseField: PrimeField, -{ +pub struct CCCSVar { // Commitment to witness pub C: NonNativeAffineVar, // Public io pub x: Vec>>, } + impl AllocVar, CF1> for CCCSVar where C: CurveGroup, - ::BaseField: PrimeField, { fn new_variable>>( cs: impl Into>>, @@ -83,12 +82,30 @@ where } } +impl CommittedInstanceVarExt for CCCSVar { + type PointVar = NonNativeAffineVar; + + fn get_commitments(&self) -> Vec { + vec![self.C.clone()] + } + + fn get_public_inputs(&self) -> &[FpVar>] { + &self.x + } + + fn enforce_incoming(&self) -> Result<(), SynthesisError> { + // `CCCSVar` is always the incoming instance + Ok(()) + } + + fn enforce_partial_equal(&self, other: &Self) -> Result<(), SynthesisError> { + self.x.enforce_equal(&other.x) + } +} + /// Linearized Committed CCS instance #[derive(Debug, Clone)] -pub struct LCCCSVar -where - ::BaseField: PrimeField, -{ +pub struct LCCCSVar { // Commitment to witness pub C: NonNativeAffineVar, // Relaxation factor of z for folded LCCCS @@ -100,10 +117,10 @@ where // Vector of v_i pub v: Vec>>, } + impl AllocVar, CF1> for LCCCSVar where C: CurveGroup, - ::BaseField: PrimeField, { fn new_variable>>( cs: impl Into>>, @@ -127,41 +144,44 @@ where } } -impl LCCCSVar -where - C: CurveGroup, - ::ScalarField: Absorb, - ::BaseField: ark_ff::PrimeField, -{ - /// [`LCCCSVar`].hash implements the LCCCS instance hash compatible with the native - /// implementation from LCCCS.hash. - /// Returns `H(i, z_0, z_i, U_i)`, where `i` can be `i` but also `i+1`, and `U` is the LCCCS. - /// 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( - self, - 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], - self.x, - self.r_x, - self.v, +impl AbsorbGadget for LCCCSVar { + fn to_sponge_bytes(&self) -> Result>, SynthesisError> { + unimplemented!() + } + + fn to_sponge_field_elements(&self) -> Result>, SynthesisError> { + Ok([ + &self.C.to_constraint_field()?, + &[self.u.clone()][..], + &self.x, + &self.r_x, + &self.v, ] - .concat(); - 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)) + .concat()) + } +} + +impl CommittedInstanceVarExt for LCCCSVar { + type PointVar = NonNativeAffineVar; + + fn get_commitments(&self) -> Vec { + vec![self.C.clone()] + } + + fn get_public_inputs(&self) -> &[FpVar>] { + &self.x + } + + fn enforce_incoming(&self) -> Result<(), SynthesisError> { + // `LCCCSVar` is always the running instance + Err(SynthesisError::Unsatisfiable) + } + + fn enforce_partial_equal(&self, other: &Self) -> Result<(), SynthesisError> { + self.u.enforce_equal(&other.u)?; + self.x.enforce_equal(&other.x)?; + self.r_x.enforce_equal(&other.r_x)?; + self.v.enforce_equal(&other.v) } } @@ -754,13 +774,7 @@ where // Primary Part // P.1. Compute u_i.x // u_i.x[0] = H(i, z_0, z_i, U_i) - let (u_i_x, _) = U_i.clone().hash( - &sponge, - pp_hash.clone(), - i.clone(), - z_0.clone(), - z_i.clone(), - )?; + let (u_i_x, _) = U_i.clone().hash(&sponge, &pp_hash, &i, &z_0, &z_i)?; // u_i.x[1] = H(cf_U_i) let (cf_u_i_x, cf_U_i_vec) = cf_U_i.clone().hash(&sponge, pp_hash.clone())?; @@ -797,17 +811,17 @@ where // P.4.a compute and check the first output of F' let (u_i1_x, _) = U_i1.clone().hash( &sponge, - pp_hash.clone(), - i + FpVar::>::one(), - z_0.clone(), - z_i1.clone(), + &pp_hash, + &(i + FpVar::>::one()), + &z_0, + &z_i1, )?; let (u_i1_x_base, _) = LCCCSVar::new_constant(cs.clone(), U_dummy)?.hash( &sponge, - pp_hash.clone(), - FpVar::>::one(), - z_0.clone(), - z_i1.clone(), + &pp_hash, + &FpVar::>::one(), + &z_0, + &z_i1, )?; let x = FpVar::new_input(cs.clone(), || Ok(self.x.unwrap_or(u_i1_x_base.value()?)))?; x.enforce_equal(&is_basecase.select(&u_i1_x_base, &u_i1_x)?)?; @@ -900,6 +914,7 @@ mod tests { utils::{compute_c, compute_sigmas_thetas}, HyperNovaCycleFoldCircuit, }, + traits::CommittedInstanceExt, }, frontend::utils::CubicFCircuit, transcript::poseidon::poseidon_canonical_config, @@ -1133,9 +1148,7 @@ mod tests { let (lcccs, _) = ccs .to_lcccs::<_, _, Pedersen, true>(&mut rng, &pedersen_params, &z1) .unwrap(); - let h = lcccs - .clone() - .hash(&sponge, pp_hash, i, z_0.clone(), z_i.clone()); + let h = lcccs.clone().hash(&sponge, pp_hash, i, &z_0, &z_i); let cs = ConstraintSystem::::new_ref(); @@ -1147,13 +1160,7 @@ mod tests { let lcccsVar = LCCCSVar::::new_witness(cs.clone(), || Ok(lcccs)).unwrap(); let (hVar, _) = lcccsVar .clone() - .hash( - &spongeVar, - pp_hashVar, - iVar.clone(), - z_0Var.clone(), - z_iVar.clone(), - ) + .hash(&spongeVar, &pp_hashVar, &iVar, &z_0Var, &z_iVar) .unwrap(); assert!(cs.is_satisfied().unwrap()); @@ -1225,7 +1232,7 @@ mod tests { let mut cf_W_i = cf_W_dummy.clone(); let mut cf_U_i = cf_U_dummy.clone(); u_i.x = vec![ - U_i.hash(&sponge, pp_hash, Fr::zero(), z_0.clone(), z_i.clone()), + U_i.hash(&sponge, pp_hash, Fr::zero(), &z_0, &z_i), cf_U_i.hash_cyclefold(&sponge, pp_hash), ]; @@ -1252,7 +1259,7 @@ mod tests { W_i1 = Witness::::dummy(&ccs); U_i1 = LCCCS::dummy(ccs.l, ccs.t, ccs.s); - let u_i1_x = U_i1.hash(&sponge, pp_hash, Fr::one(), z_0.clone(), z_i1.clone()); + let u_i1_x = U_i1.hash(&sponge, pp_hash, Fr::one(), &z_0, &z_i1); // hash the initial (dummy) CycleFold instance, which is used as the 2nd public // input in the AugmentedFCircuit @@ -1309,8 +1316,7 @@ mod tests { // sanity check: check the folded instance relation U_i1.check_relation(&ccs, &W_i1).unwrap(); - let u_i1_x = - U_i1.hash(&sponge, pp_hash, iFr + Fr::one(), z_0.clone(), z_i1.clone()); + let u_i1_x = U_i1.hash(&sponge, pp_hash, iFr + Fr::one(), &z_0, &z_i1); let rho_bits = rho.into_bigint().to_bits_le()[..NOVA_N_BITS_RO].to_vec(); let rho_Fq = Fq::from_bigint(BigInteger::from_bits_le(&rho_bits)).unwrap(); @@ -1434,8 +1440,7 @@ 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(&sponge, pp_hash, iFr + Fr::one(), z_0.clone(), z_i1.clone()); + let expected_u_i1_x = U_i1.hash(&sponge, pp_hash, iFr + Fr::one(), &z_0, &z_i1); let expected_cf_U_i1_x = cf_U_i.hash_cyclefold(&sponge, pp_hash); // 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); diff --git a/folding-schemes/src/folding/hypernova/decider_eth_circuit.rs b/folding-schemes/src/folding/hypernova/decider_eth_circuit.rs index b4e2cf85..fc45fea5 100644 --- a/folding-schemes/src/folding/hypernova/decider_eth_circuit.rs +++ b/folding-schemes/src/folding/hypernova/decider_eth_circuit.rs @@ -26,8 +26,6 @@ use super::{ nimfs::{NIMFSProof, NIMFS}, HyperNova, Witness, CCCS, LCCCS, }; -use crate::arith::ccs::CCS; -use crate::arith::r1cs::R1CS; use crate::commitment::{pedersen::Params as PedersenParams, CommitmentScheme}; use crate::folding::circuits::{ cyclefold::{CycleFoldCommittedInstance, CycleFoldWitness}, @@ -40,6 +38,8 @@ use crate::utils::{ vec::poly_from_vec, }; use crate::Error; +use crate::{arith::ccs::CCS, folding::traits::CommittedInstanceVarExt}; +use crate::{arith::r1cs::R1CS, folding::traits::WitnessVarExt}; /// In-circuit representation of the Witness associated to the CommittedInstance. #[derive(Debug, Clone)] @@ -66,6 +66,12 @@ impl AllocVar, F> for WitnessVar { } } +impl WitnessVarExt for WitnessVar { + fn get_openings(&self) -> Vec<(&[FpVar], FpVar)> { + vec![(&self.w, self.r_w.clone())] + } +} + /// CCSMatricesVar contains the matrices 'M' of the CCS without the rest of CCS parameters. #[derive(Debug, Clone)] pub struct CCSMatricesVar { @@ -340,13 +346,7 @@ where )?; // 3.a u_i.x[0] == H(i, z_0, z_i, U_i) - let (u_i_x, _) = U_i.clone().hash( - &sponge, - pp_hash.clone(), - i.clone(), - z_0.clone(), - z_i.clone(), - )?; + let (u_i_x, _) = U_i.clone().hash(&sponge, &pp_hash, &i, &z_0, &z_i)?; (u_i.x[0]).enforce_equal(&u_i_x)?; #[cfg(feature = "light-test")] diff --git a/folding-schemes/src/folding/hypernova/lcccs.rs b/folding-schemes/src/folding/hypernova/lcccs.rs index 8cca4bc9..e36eb413 100644 --- a/folding-schemes/src/folding/hypernova/lcccs.rs +++ b/folding-schemes/src/folding/hypernova/lcccs.rs @@ -1,15 +1,17 @@ use ark_crypto_primitives::sponge::Absorb; -use ark_ec::{CurveGroup, Group}; +use ark_ec::CurveGroup; use ark_ff::PrimeField; use ark_poly::DenseMultilinearExtension; use ark_poly::MultilinearExtension; use ark_std::rand::Rng; use ark_std::Zero; +use super::circuits::LCCCSVar; use super::Witness; use crate::arith::ccs::CCS; use crate::commitment::CommitmentScheme; -use crate::transcript::{AbsorbNonNative, Transcript}; +use crate::folding::traits::CommittedInstanceExt; +use crate::transcript::AbsorbNonNative; use crate::utils::mle::dense_vec_to_dense_mle; use crate::utils::vec::mat_vec_mul; use crate::Error; @@ -138,29 +140,15 @@ where } } -impl LCCCS -where - ::ScalarField: Absorb, - ::BaseField: ark_ff::PrimeField, -{ - /// [`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>( - &self, - sponge: &T, - pp_hash: C::ScalarField, - i: C::ScalarField, - z_0: Vec, - z_i: Vec, - ) -> 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] +impl CommittedInstanceExt for LCCCS { + type Var = LCCCSVar; + + fn get_commitments(&self) -> Vec { + vec![self.C] + } + + fn is_incoming(&self) -> bool { + false } } diff --git a/folding-schemes/src/folding/hypernova/mod.rs b/folding-schemes/src/folding/hypernova/mod.rs index f3c90ea4..c85ae075 100644 --- a/folding-schemes/src/folding/hypernova/mod.rs +++ b/folding-schemes/src/folding/hypernova/mod.rs @@ -18,6 +18,7 @@ pub mod utils; use cccs::CCCS; use circuits::AugmentedFCircuit; +use decider_eth_circuit::WitnessVar; use lcccs::LCCCS; use nimfs::NIMFS; @@ -30,6 +31,7 @@ use crate::folding::circuits::{ CF2, }; use crate::folding::nova::{get_r1cs_from_cs, PreprocessorParam}; +use crate::folding::traits::{CommittedInstanceExt, WitnessExt}; use crate::frontend::FCircuit; use crate::utils::{get_cm_coordinates, pp_hash}; use crate::Error; @@ -79,6 +81,14 @@ impl Witness { } } +impl WitnessExt for Witness { + type Var = WitnessVar; + + fn get_openings(&self) -> Vec<(&[F], F)> { + vec![(&self.w, self.r_w)] + } +} + /// Proving parameters for HyperNova-based IVC #[derive(Debug, Clone)] pub struct ProverParams @@ -305,8 +315,8 @@ where &sponge, self.pp_hash, C1::ScalarField::zero(), // i - self.z_0.clone(), - state.clone(), + &self.z_0, + &state, ), cf_U_i.hash_cyclefold(&sponge, self.pp_hash), ]; @@ -322,8 +332,8 @@ where &sponge, self.pp_hash, C1::ScalarField::one(), // i+1, where i=0 - self.z_0.clone(), - z_i1.clone(), + &self.z_0, + &z_i1, ); let cf_u_i1_x = cf_U_i.hash_cyclefold(&sponge, self.pp_hash); @@ -492,13 +502,7 @@ where let (cf_W_dummy, cf_U_dummy): (CycleFoldWitness, CycleFoldCommittedInstance) = cf_r1cs.dummy_running_instance(); u_dummy.x = vec![ - U_dummy.hash( - &sponge, - pp_hash, - C1::ScalarField::zero(), - z_0.clone(), - z_0.clone(), - ), + U_dummy.hash(&sponge, pp_hash, C1::ScalarField::zero(), &z_0, &z_0), cf_U_dummy.hash_cyclefold(&sponge, pp_hash), ]; @@ -641,8 +645,8 @@ where &sponge, self.pp_hash, C1::ScalarField::one(), - self.z_0.clone(), - z_i1.clone(), + &self.z_0, + &z_i1, ); // hash the initial (dummy) CycleFold instance, which is used as the 2nd public @@ -697,8 +701,8 @@ where &sponge, self.pp_hash, self.i + C1::ScalarField::one(), - self.z_0.clone(), - z_i1.clone(), + &self.z_0, + &z_i1, ); let rho_bits = rho.into_bigint().to_bits_le()[..NOVA_N_BITS_RO].to_vec(); @@ -882,7 +886,7 @@ 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(&sponge, 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); if expected_u_i_x != u_i.x[0] { return Err(Error::IVCVerificationFail); } diff --git a/folding-schemes/src/folding/nova/circuits.rs b/folding-schemes/src/folding/nova/circuits.rs index a506c8c6..142e662d 100644 --- a/folding-schemes/src/folding/nova/circuits.rs +++ b/folding-schemes/src/folding/nova/circuits.rs @@ -21,7 +21,6 @@ use ark_std::{fmt::Debug, One, Zero}; use core::{borrow::Borrow, marker::PhantomData}; use super::{CommittedInstance, NovaCycleFoldConfig}; -use crate::constants::NOVA_N_BITS_RO; use crate::folding::circuits::{ cyclefold::{ CycleFoldChallengeGadget, CycleFoldCommittedInstance, CycleFoldCommittedInstanceVar, @@ -32,15 +31,13 @@ use crate::folding::circuits::{ }; use crate::frontend::FCircuit; use crate::transcript::{AbsorbNonNativeGadget, Transcript, TranscriptVar}; +use crate::{constants::NOVA_N_BITS_RO, folding::traits::CommittedInstanceVarExt}; /// 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 /// represented non-natively over the constraint field. #[derive(Debug, Clone)] -pub struct CommittedInstanceVar -where - ::BaseField: ark_ff::PrimeField, -{ +pub struct CommittedInstanceVar { pub u: FpVar, pub x: Vec>, pub cmE: NonNativeAffineVar, @@ -50,7 +47,6 @@ where impl AllocVar, CF1> for CommittedInstanceVar where C: CurveGroup, - ::BaseField: PrimeField, { fn new_variable>>( cs: impl Into>>, @@ -94,35 +90,27 @@ where } } -impl CommittedInstanceVar -where - C: CurveGroup, - ::ScalarField: Absorb, - ::BaseField: ark_ff::PrimeField, -{ - /// hash implements the committed instance hash compatible with the native implementation from - /// CommittedInstance.hash. - /// Returns `H(i, z_0, z_i, U_i)`, where `i` can be `i` but also `i+1`, and `U` is the - /// `CommittedInstance`. - /// 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, S>>( - self, - sponge: &T, - pp_hash: FpVar>, - i: FpVar>, - z_0: Vec>>, - z_i: Vec>>, - ) -> Result<(FpVar>, Vec>>), SynthesisError> { - 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)) +impl CommittedInstanceVarExt for CommittedInstanceVar { + type PointVar = NonNativeAffineVar; + + fn get_commitments(&self) -> Vec { + vec![self.cmW.clone(), self.cmE.clone()] + } + + fn get_public_inputs(&self) -> &[FpVar>] { + &self.x + } + + fn enforce_incoming(&self) -> Result<(), SynthesisError> { + let zero = NonNativeUintVar::new_constant(ConstraintSystemRef::None, CF2::::zero())?; + self.cmE.x.enforce_equal_unaligned(&zero)?; + self.cmE.y.enforce_equal_unaligned(&zero)?; + self.u.enforce_equal(&FpVar::one()) + } + + fn enforce_partial_equal(&self, other: &Self) -> Result<(), SynthesisError> { + self.u.enforce_equal(&other.u)?; + self.x.enforce_equal(&other.x) } } @@ -370,13 +358,7 @@ where // Primary Part // P.1. Compute u_i.x // u_i.x[0] = H(i, z_0, z_i, U_i) - let (u_i_x, U_i_vec) = U_i.clone().hash( - &sponge, - pp_hash.clone(), - i.clone(), - z_0.clone(), - z_i.clone(), - )?; + let (u_i_x, U_i_vec) = U_i.clone().hash(&sponge, &pp_hash, &i, &z_0, &z_i)?; // u_i.x[1] = H(cf_U_i) let (cf_u_i_x, cf_U_i_vec) = cf_U_i.clone().hash(&sponge, pp_hash.clone())?; @@ -425,17 +407,17 @@ where // Non-base case: u_{i+1}.x[0] == H((i+1, z_0, z_{i+1}, U_{i+1}) let (u_i1_x, _) = U_i1.clone().hash( &sponge, - pp_hash.clone(), - i + FpVar::>::one(), - z_0.clone(), - z_i1.clone(), + &pp_hash, + &(i + FpVar::>::one()), + &z_0, + &z_i1, )?; let (u_i1_x_base, _) = CommittedInstanceVar::new_constant(cs.clone(), u_dummy)?.hash( &sponge, - pp_hash.clone(), - FpVar::>::one(), - z_0.clone(), - z_i1.clone(), + &pp_hash, + &FpVar::>::one(), + &z_0, + &z_i1, )?; let x = FpVar::new_input(cs.clone(), || Ok(self.x.unwrap_or(u_i1_x_base.value()?)))?; x.enforce_equal(&is_basecase.select(&u_i1_x_base, &u_i1_x)?)?; @@ -538,6 +520,7 @@ pub mod tests { use crate::commitment::pedersen::Pedersen; use crate::folding::nova::nifs::tests::prepare_simple_fold_inputs; use crate::folding::nova::nifs::NIFS; + use crate::folding::traits::CommittedInstanceExt; use crate::transcript::poseidon::poseidon_canonical_config; #[test] @@ -609,7 +592,7 @@ pub mod tests { }; // compute the CommittedInstance hash natively - let h = ci.hash(&sponge, pp_hash, i, z_0.clone(), z_i.clone()); + let h = ci.hash(&sponge, pp_hash, i, &z_0, &z_i); let cs = ConstraintSystem::::new_ref(); @@ -624,7 +607,7 @@ pub mod tests { // compute the CommittedInstance hash in-circuit let (hVar, _) = ciVar - .hash(&sponge, pp_hashVar, iVar, z_0Var, z_iVar) + .hash(&sponge, &pp_hashVar, &iVar, &z_0Var, &z_iVar) .unwrap(); assert!(cs.is_satisfied().unwrap()); diff --git a/folding-schemes/src/folding/nova/decider_eth_circuit.rs b/folding-schemes/src/folding/nova/decider_eth_circuit.rs index 25564a13..53be6a9c 100644 --- a/folding-schemes/src/folding/nova/decider_eth_circuit.rs +++ b/folding-schemes/src/folding/nova/decider_eth_circuit.rs @@ -27,8 +27,6 @@ use super::{ nifs::NIFS, CommittedInstance, Nova, Witness, }; -use crate::arith::r1cs::R1CS; -use crate::commitment::{pedersen::Params as PedersenParams, CommitmentScheme}; use crate::folding::circuits::{ cyclefold::{CycleFoldCommittedInstance, CycleFoldWitness}, nonnative::{affine::NonNativeAffineVar, uint::NonNativeUintVar}, @@ -41,6 +39,11 @@ use crate::utils::{ vec::poly_from_vec, }; use crate::Error; +use crate::{arith::r1cs::R1CS, folding::traits::WitnessVarExt}; +use crate::{ + commitment::{pedersen::Params as PedersenParams, CommitmentScheme}, + folding::traits::CommittedInstanceVarExt, +}; #[derive(Debug, Clone)] pub struct RelaxedR1CSGadget {} @@ -135,7 +138,6 @@ pub struct WitnessVar { impl AllocVar, CF1> for WitnessVar where C: CurveGroup, - ::BaseField: PrimeField, { fn new_variable>>( cs: impl Into>>, @@ -160,6 +162,12 @@ where } } +impl WitnessVarExt for WitnessVar { + fn get_openings(&self) -> Vec<(&[FpVar], FpVar)> { + vec![(&self.E, self.rE.clone()), (&self.W, self.rW.clone())] + } +} + /// Circuit that implements the in-circuit checks needed for the onchain (Ethereum's EVM) /// verification. #[derive(Clone, Debug)] @@ -398,13 +406,7 @@ where (u_i.u.is_one()?).enforce_equal(&Boolean::TRUE)?; // 3.a u_i.x[0] == H(i, z_0, z_i, U_i) - let (u_i_x, U_i_vec) = U_i.clone().hash( - &sponge, - pp_hash.clone(), - i.clone(), - z_0.clone(), - z_i.clone(), - )?; + let (u_i_x, U_i_vec) = U_i.clone().hash(&sponge, &pp_hash, &i, &z_0, &z_i)?; (u_i.x[0]).enforce_equal(&u_i_x)?; #[cfg(feature = "light-test")] diff --git a/folding-schemes/src/folding/nova/mod.rs b/folding-schemes/src/folding/nova/mod.rs index 02e6d3b0..5e499405 100644 --- a/folding-schemes/src/folding/nova/mod.rs +++ b/folding-schemes/src/folding/nova/mod.rs @@ -13,6 +13,7 @@ use ark_std::fmt::Debug; use ark_std::rand::RngCore; use ark_std::{One, UniformRand, Zero}; use core::marker::PhantomData; +use decider_eth_circuit::WitnessVar; use crate::folding::circuits::cyclefold::{ fold_cyclefold_circuit, CycleFoldCircuit, CycleFoldCommittedInstance, CycleFoldConfig, @@ -38,9 +39,11 @@ pub mod nifs; pub mod serialize; pub mod traits; pub mod zk; -use circuits::{AugmentedFCircuit, ChallengeGadget}; +use circuits::{AugmentedFCircuit, ChallengeGadget, CommittedInstanceVar}; use nifs::NIFS; +use super::traits::{CommittedInstanceExt, WitnessExt}; + /// Configuration for Nova's CycleFold circuit pub struct NovaCycleFoldConfig { _c: PhantomData, @@ -103,30 +106,15 @@ where } } -impl CommittedInstance -where - ::ScalarField: Absorb, - ::BaseField: ark_ff::PrimeField, -{ - /// 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 - /// `CommittedInstance`. - pub fn hash>( - &self, - sponge: &T, - pp_hash: C::ScalarField, // public params hash - i: C::ScalarField, - z_0: Vec, - z_i: Vec, - ) -> 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] +impl CommittedInstanceExt for CommittedInstance { + type Var = CommittedInstanceVar; + + fn get_commitments(&self) -> Vec { + vec![self.cmW, self.cmE] + } + + fn is_incoming(&self) -> bool { + self.cmE == C::zero() && self.u == One::one() } } @@ -188,6 +176,14 @@ impl Witness { } } +impl WitnessExt for Witness { + type Var = WitnessVar; + + fn get_openings(&self) -> Vec<(&[C::ScalarField], C::ScalarField)> { + vec![(&self.W, self.rW), (&self.E, self.rE)] + } +} + #[derive(Debug, Clone)] pub struct PreprocessorParam where @@ -696,8 +692,8 @@ where &sponge, self.pp_hash, self.i + C1::ScalarField::one(), - self.z_0.clone(), - z_i1.clone(), + &self.z_0, + &z_i1, ); // u_{i+1}.x[1] = H(cf_U_{i+1}) let cf_u_i1_x: C1::ScalarField; @@ -907,7 +903,7 @@ 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(&sponge, 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); if expected_u_i_x != u_i.x[0] { return Err(Error::IVCVerificationFail); } diff --git a/folding-schemes/src/folding/nova/zk.rs b/folding-schemes/src/folding/nova/zk.rs index 25a216a2..84e9ca48 100644 --- a/folding-schemes/src/folding/nova/zk.rs +++ b/folding-schemes/src/folding/nova/zk.rs @@ -36,6 +36,7 @@ use ark_std::{One, Zero}; use crate::{ arith::r1cs::{RelaxedR1CS, R1CS}, + folding::traits::CommittedInstanceExt, RngCore, }; use ark_crypto_primitives::sponge::{ @@ -226,7 +227,7 @@ where // b. Check computed hashes are correct let mut sponge = PoseidonSponge::::new(poseidon_config); - let expected_u_i_x = proof.U_i.hash(&sponge, pp_hash, i, z_0, z_i); + let expected_u_i_x = proof.U_i.hash(&sponge, pp_hash, i, &z_0, &z_i); if expected_u_i_x != proof.u_i.x[0] { return Err(Error::zkIVCVerificationFail); } From efdbc5d1a36357427986c678ce22ef479461533c Mon Sep 17 00:00:00 2001 From: winderica Date: Sun, 8 Sep 2024 08:10:29 +0800 Subject: [PATCH 3/8] Implement witness and committed instance traits for ProtoGalaxy --- .../src/folding/protogalaxy/circuits.rs | 39 +++-- .../src/folding/protogalaxy/mod.rs | 140 ++++++++++-------- 2 files changed, 98 insertions(+), 81 deletions(-) diff --git a/folding-schemes/src/folding/protogalaxy/circuits.rs b/folding-schemes/src/folding/protogalaxy/circuits.rs index 74a73327..52ee2099 100644 --- a/folding-schemes/src/folding/protogalaxy/circuits.rs +++ b/folding-schemes/src/folding/protogalaxy/circuits.rs @@ -24,13 +24,16 @@ use super::{ CommittedInstance, CommittedInstanceVar, ProtoGalaxyCycleFoldConfig, }; use crate::{ - folding::circuits::{ - cyclefold::{ - CycleFoldChallengeGadget, CycleFoldCommittedInstance, CycleFoldCommittedInstanceVar, - CycleFoldConfig, NIFSFullGadget, + folding::{ + circuits::{ + cyclefold::{ + CycleFoldChallengeGadget, CycleFoldCommittedInstance, + CycleFoldCommittedInstanceVar, CycleFoldConfig, NIFSFullGadget, + }, + nonnative::{affine::NonNativeAffineVar, uint::NonNativeUintVar}, + CF1, CF2, }, - nonnative::{affine::NonNativeAffineVar, uint::NonNativeUintVar}, - CF1, CF2, + traits::CommittedInstanceVarExt, }, frontend::FCircuit, transcript::{AbsorbNonNativeGadget, TranscriptVar}, @@ -357,13 +360,7 @@ where // Primary Part // P.1. Compute u_i.x // u_i.x[0] = H(i, z_0, z_i, U_i) - let (u_i_x, _) = U_i.clone().hash( - &sponge, - pp_hash.clone(), - i.clone(), - z_0.clone(), - z_i.clone(), - )?; + let (u_i_x, _) = U_i.clone().hash(&sponge, &pp_hash, &i, &z_0, &z_i)?; // u_i.x[1] = H(cf_U_i) let (cf_u_i_x, _) = cf_U_i.clone().hash(&sponge, pp_hash.clone())?; @@ -384,17 +381,17 @@ where // Non-base case: u_{i+1}.x[0] == H((i+1, z_0, z_{i+1}, U_{i+1}) let (u_i1_x, _) = U_i1.clone().hash( &sponge, - pp_hash.clone(), - i + FpVar::>::one(), - z_0.clone(), - z_i1.clone(), + &pp_hash, + &(i + FpVar::>::one()), + &z_0, + &z_i1, )?; let (u_i1_x_base, _) = CommittedInstanceVar::new_constant(cs.clone(), u_dummy)?.hash( &sponge, - pp_hash.clone(), - FpVar::>::one(), - z_0.clone(), - z_i1.clone(), + &pp_hash, + &FpVar::>::one(), + &z_0, + &z_i1, )?; let x = FpVar::new_input(cs.clone(), || Ok(self.x.unwrap_or(u_i1_x_base.value()?)))?; x.enforce_equal(&is_basecase.select(&u_i1_x_base, &u_i1_x)?)?; diff --git a/folding-schemes/src/folding/protogalaxy/mod.rs b/folding-schemes/src/folding/protogalaxy/mod.rs index 9ee44de4..c22b06b5 100644 --- a/folding-schemes/src/folding/protogalaxy/mod.rs +++ b/folding-schemes/src/folding/protogalaxy/mod.rs @@ -1,14 +1,14 @@ /// Implements the scheme described in [ProtoGalaxy](https://eprint.iacr.org/2023/1106.pdf) use ark_crypto_primitives::sponge::{ - constraints::{AbsorbGadget, CryptographicSpongeVar}, - poseidon::{constraints::PoseidonSpongeVar, PoseidonConfig, PoseidonSponge}, + poseidon::{PoseidonConfig, PoseidonSponge}, Absorb, CryptographicSponge, }; use ark_ec::{CurveGroup, Group}; use ark_ff::{BigInteger, PrimeField}; use ark_r1cs_std::{ alloc::{AllocVar, AllocationMode}, - fields::fp::FpVar, + eq::EqGadget, + fields::{fp::FpVar, FieldVar}, groups::{CurveVar, GroupOpsBounds}, R1CSVar, ToConstraintFieldGadget, }; @@ -44,6 +44,8 @@ pub(crate) mod utils; use circuits::AugmentedFCircuit; use folding::Folding; +use super::traits::{CommittedInstanceExt, CommittedInstanceVarExt, WitnessExt, WitnessVarExt}; + /// Configuration for ProtoGalaxy's CycleFold circuit pub struct ProtoGalaxyCycleFoldConfig { _c: PhantomData, @@ -83,30 +85,15 @@ impl CommittedInstance { } } -impl CommittedInstance -where - C::ScalarField: Absorb, - C::BaseField: PrimeField, -{ - /// hash implements the committed instance hash compatible with the gadget implemented in - /// 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( - &self, - sponge: &PoseidonSponge, - pp_hash: C::ScalarField, - i: C::ScalarField, - z_0: Vec, - z_i: Vec, - ) -> 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] +impl CommittedInstanceExt for CommittedInstance { + type Var = CommittedInstanceVar; + + fn get_commitments(&self) -> Vec { + vec![self.phi] + } + + fn is_incoming(&self) -> bool { + self.e == Zero::zero() && self.betas.is_empty() } } @@ -164,34 +151,29 @@ impl R1CSVar for CommittedInstanceVar { } } -impl CommittedInstanceVar -where - C::ScalarField: Absorb, - C::BaseField: PrimeField, -{ - /// hash implements the committed instance hash compatible with the native implementation from - /// CommittedInstance.hash. - /// Returns `H(i, z_0, z_i, U_i)`, where `i` can be `i` but also `i+1`, and `U` is the - /// `CommittedInstance`. - /// 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( - self, - 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.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)) +impl CommittedInstanceVarExt for CommittedInstanceVar { + type PointVar = NonNativeAffineVar; + + fn get_commitments(&self) -> Vec { + vec![self.phi.clone()] + } + + fn get_public_inputs(&self) -> &[FpVar>] { + &self.x + } + + fn enforce_incoming(&self) -> Result<(), SynthesisError> { + if self.betas.is_empty() { + self.e.enforce_equal(&FpVar::zero()) + } else { + Err(SynthesisError::Unsatisfiable) + } + } + + fn enforce_partial_equal(&self, other: &Self) -> Result<(), SynthesisError> { + self.betas.enforce_equal(&other.betas)?; + self.e.enforce_equal(&other.e)?; + self.x.enforce_equal(&other.x) } } @@ -224,6 +206,44 @@ impl Witness { } } +impl WitnessExt for Witness { + type Var = WitnessVar; + + fn get_openings(&self) -> Vec<(&[F], F)> { + vec![(&self.w, self.r_w)] + } +} + +/// In-circuit representation of the Witness associated to the CommittedInstance. +#[derive(Debug, Clone)] +pub struct WitnessVar { + pub W: Vec>, + pub rW: FpVar, +} + +impl AllocVar, F> for WitnessVar { + fn new_variable>>( + cs: impl Into>, + f: impl FnOnce() -> Result, + mode: AllocationMode, + ) -> Result { + f().and_then(|val| { + let cs = cs.into(); + + let W = Vec::new_variable(cs.clone(), || Ok(val.borrow().w.to_vec()), mode)?; + let rW = FpVar::new_variable(cs.clone(), || Ok(val.borrow().r_w), mode)?; + + Ok(Self { W, rW }) + }) + } +} + +impl WitnessVarExt for WitnessVar { + fn get_openings(&self) -> Vec<(&[FpVar], FpVar)> { + vec![(&self.W, self.rW.clone())] + } +} + #[derive(Debug, thiserror::Error, PartialEq)] pub enum ProtoGalaxyError { #[error("The remainder from G(X)-F(α)*L_0(X)) / Z(X) should be zero")] @@ -636,8 +656,8 @@ where &sponge, self.pp_hash, self.i + C1::ScalarField::one(), - self.z_0.clone(), - z_i1.clone(), + &self.z_0, + &z_i1, ); // `cf_U_{i+1}` (i.e., `cf_U_1`) is fixed to `cf_U_dummy`, so we // just use `self.cf_U_i = cf_U_0 = cf_U_dummy`. @@ -744,8 +764,8 @@ where &sponge, self.pp_hash, self.i + C1::ScalarField::one(), - self.z_0.clone(), - z_i1.clone(), + &self.z_0, + &z_i1, ); cf_u_i1_x = cf_U_i1.hash_cyclefold(&sponge, self.pp_hash); @@ -874,7 +894,7 @@ 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(&sponge, 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); if expected_u_i_x != u_i.x[0] { return Err(Error::IVCVerificationFail); } From bcd0b26a3110847168ea5c7ce91cab3f608dad03 Mon Sep 17 00:00:00 2001 From: winderica Date: Sun, 8 Sep 2024 08:15:46 +0800 Subject: [PATCH 4/8] Improve the clarity of docs for `Witness{Var}Ext::get_openings` --- folding-schemes/src/folding/traits.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/folding-schemes/src/folding/traits.rs b/folding-schemes/src/folding/traits.rs index d780a920..a96ba72f 100644 --- a/folding-schemes/src/folding/traits.rs +++ b/folding-schemes/src/folding/traits.rs @@ -109,13 +109,13 @@ pub trait WitnessExt { /// The in-circuit representation of the witness. type Var: AllocVar + WitnessVarExt; - /// Returns the openings (i.e., values being committed to) contained in the - /// witness. + /// Returns the openings (i.e., the values being committed to and the + /// randomness) contained in the witness. fn get_openings(&self) -> Vec<(&[F], F)>; } pub trait WitnessVarExt { - /// Returns the openings (i.e., values being committed to) contained in the - /// witness. + /// Returns the openings (i.e., the values being committed to and the + /// randomness) contained in the witness. fn get_openings(&self) -> Vec<(&[FpVar], FpVar)>; } From 4096fb2f79a75fcdb4bff1fc46f0800e49e5c9ca Mon Sep 17 00:00:00 2001 From: winderica Date: Tue, 17 Sep 2024 20:51:45 +0800 Subject: [PATCH 5/8] Avoid cloning `z_i` --- folding-schemes/src/folding/hypernova/circuits.rs | 13 +++++++------ folding-schemes/src/folding/nova/circuits.rs | 13 +++++++------ folding-schemes/src/folding/protogalaxy/circuits.rs | 12 ++++++------ 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/folding-schemes/src/folding/hypernova/circuits.rs b/folding-schemes/src/folding/hypernova/circuits.rs index e9802718..8dc4bd51 100644 --- a/folding-schemes/src/folding/hypernova/circuits.rs +++ b/folding-schemes/src/folding/hypernova/circuits.rs @@ -762,12 +762,6 @@ where 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); - let z_i1 = - self.F - .generate_step_constraints(cs.clone(), i_usize, z_i.clone(), external_inputs)?; - let is_basecase = i.is_zero()?; let is_not_basecase = is_basecase.not(); @@ -809,6 +803,13 @@ where U_i1.C = U_i1_C; // P.4.a compute and check the first output of F' + + // get z_{i+1} from the F circuit + let i_usize = self.i_usize.unwrap_or(0); + let z_i1 = self + .F + .generate_step_constraints(cs.clone(), i_usize, z_i, external_inputs)?; + let (u_i1_x, _) = U_i1.clone().hash( &sponge, &pp_hash, diff --git a/folding-schemes/src/folding/nova/circuits.rs b/folding-schemes/src/folding/nova/circuits.rs index 142e662d..bcf75a6b 100644 --- a/folding-schemes/src/folding/nova/circuits.rs +++ b/folding-schemes/src/folding/nova/circuits.rs @@ -347,12 +347,6 @@ where // `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); - let z_i1 = - self.F - .generate_step_constraints(cs.clone(), i_usize, z_i.clone(), external_inputs)?; - let is_basecase = i.is_zero()?; // Primary Part @@ -403,6 +397,13 @@ where U_i1.cmW = U_i1_cmW; // P.4.a compute and check the first output of F' + + // get z_{i+1} from the F circuit + let i_usize = self.i_usize.unwrap_or(0); + let z_i1 = self + .F + .generate_step_constraints(cs.clone(), i_usize, z_i, external_inputs)?; + // 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( diff --git a/folding-schemes/src/folding/protogalaxy/circuits.rs b/folding-schemes/src/folding/protogalaxy/circuits.rs index 52ee2099..aef62509 100644 --- a/folding-schemes/src/folding/protogalaxy/circuits.rs +++ b/folding-schemes/src/folding/protogalaxy/circuits.rs @@ -349,12 +349,6 @@ where // `transcript` is for challenge generation. let mut transcript = sponge.clone(); - // get z_{i+1} from the F circuit - let i_usize = self.i_usize; - let z_i1 = - self.F - .generate_step_constraints(cs.clone(), i_usize, z_i.clone(), external_inputs)?; - let is_basecase = i.is_zero()?; // Primary Part @@ -377,6 +371,12 @@ where )?; // P.4.a compute and check the first output of F' + + // get z_{i+1} from the F circuit + let z_i1 = + self.F + .generate_step_constraints(cs.clone(), self.i_usize, z_i, external_inputs)?; + // 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( From 8047ccf79586630e09d3de69af75cb3be2ea9910 Mon Sep 17 00:00:00 2001 From: winderica Date: Wed, 18 Sep 2024 00:49:44 +0800 Subject: [PATCH 6/8] Fix grammar issues --- folding-schemes/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/folding-schemes/src/lib.rs b/folding-schemes/src/lib.rs index d072a162..6ed08054 100644 --- a/folding-schemes/src/lib.rs +++ b/folding-schemes/src/lib.rs @@ -43,7 +43,7 @@ pub enum Error { IVCVerificationFail, #[error("zkIVC verification failed")] zkIVCVerificationFail, - #[error("Committed instance is expected to an incoming (fresh) instance")] + #[error("Committed instance is expected to be an incoming (fresh) instance")] NotIncomingCommittedInstance, #[error("R1CS instance is expected to not be relaxed")] R1CSUnrelaxedFail, From dbdc4bf0724253bd430ac7d56c0f8e03b6fa9532 Mon Sep 17 00:00:00 2001 From: winderica Date: Wed, 18 Sep 2024 00:53:56 +0800 Subject: [PATCH 7/8] Rename `Ext` traits for committed instances and witnesses to `Ops` --- folding-schemes/src/folding/hypernova/cccs.rs | 4 ++-- folding-schemes/src/folding/hypernova/circuits.rs | 8 ++++---- .../src/folding/hypernova/decider_eth_circuit.rs | 6 +++--- folding-schemes/src/folding/hypernova/lcccs.rs | 4 ++-- folding-schemes/src/folding/hypernova/mod.rs | 4 ++-- folding-schemes/src/folding/nova/circuits.rs | 6 +++--- .../src/folding/nova/decider_eth_circuit.rs | 6 +++--- folding-schemes/src/folding/nova/mod.rs | 6 +++--- folding-schemes/src/folding/nova/zk.rs | 2 +- .../src/folding/protogalaxy/circuits.rs | 2 +- folding-schemes/src/folding/protogalaxy/mod.rs | 10 +++++----- folding-schemes/src/folding/traits.rs | 14 +++++++------- 12 files changed, 36 insertions(+), 36 deletions(-) diff --git a/folding-schemes/src/folding/hypernova/cccs.rs b/folding-schemes/src/folding/hypernova/cccs.rs index d592f8c8..fb819a3a 100644 --- a/folding-schemes/src/folding/hypernova/cccs.rs +++ b/folding-schemes/src/folding/hypernova/cccs.rs @@ -11,7 +11,7 @@ use super::circuits::CCCSVar; use super::Witness; use crate::arith::{ccs::CCS, Arith}; use crate::commitment::CommitmentScheme; -use crate::folding::traits::CommittedInstanceExt; +use crate::folding::traits::CommittedInstanceOps; use crate::transcript::AbsorbNonNative; use crate::utils::mle::dense_vec_to_dense_mle; use crate::utils::vec::mat_vec_mul; @@ -142,7 +142,7 @@ where } } -impl CommittedInstanceExt for CCCS { +impl CommittedInstanceOps for CCCS { type Var = CCCSVar; fn get_commitments(&self) -> Vec { diff --git a/folding-schemes/src/folding/hypernova/circuits.rs b/folding-schemes/src/folding/hypernova/circuits.rs index 8dc4bd51..0b610d68 100644 --- a/folding-schemes/src/folding/hypernova/circuits.rs +++ b/folding-schemes/src/folding/hypernova/circuits.rs @@ -42,7 +42,7 @@ use crate::folding::{ CF1, CF2, }, nova::get_r1cs_from_cs, - traits::CommittedInstanceVarExt, + traits::CommittedInstanceVarOps, }; use crate::frontend::FCircuit; use crate::utils::virtual_polynomial::VPAuxInfo; @@ -82,7 +82,7 @@ where } } -impl CommittedInstanceVarExt for CCCSVar { +impl CommittedInstanceVarOps for CCCSVar { type PointVar = NonNativeAffineVar; fn get_commitments(&self) -> Vec { @@ -161,7 +161,7 @@ impl AbsorbGadget for LCCCSVar { } } -impl CommittedInstanceVarExt for LCCCSVar { +impl CommittedInstanceVarOps for LCCCSVar { type PointVar = NonNativeAffineVar; fn get_commitments(&self) -> Vec { @@ -915,7 +915,7 @@ mod tests { utils::{compute_c, compute_sigmas_thetas}, HyperNovaCycleFoldCircuit, }, - traits::CommittedInstanceExt, + traits::CommittedInstanceOps, }, frontend::utils::CubicFCircuit, transcript::poseidon::poseidon_canonical_config, diff --git a/folding-schemes/src/folding/hypernova/decider_eth_circuit.rs b/folding-schemes/src/folding/hypernova/decider_eth_circuit.rs index fc45fea5..5f367fbe 100644 --- a/folding-schemes/src/folding/hypernova/decider_eth_circuit.rs +++ b/folding-schemes/src/folding/hypernova/decider_eth_circuit.rs @@ -38,8 +38,8 @@ use crate::utils::{ vec::poly_from_vec, }; use crate::Error; -use crate::{arith::ccs::CCS, folding::traits::CommittedInstanceVarExt}; -use crate::{arith::r1cs::R1CS, folding::traits::WitnessVarExt}; +use crate::{arith::ccs::CCS, folding::traits::CommittedInstanceVarOps}; +use crate::{arith::r1cs::R1CS, folding::traits::WitnessVarOps}; /// In-circuit representation of the Witness associated to the CommittedInstance. #[derive(Debug, Clone)] @@ -66,7 +66,7 @@ impl AllocVar, F> for WitnessVar { } } -impl WitnessVarExt for WitnessVar { +impl WitnessVarOps for WitnessVar { fn get_openings(&self) -> Vec<(&[FpVar], FpVar)> { vec![(&self.w, self.r_w.clone())] } diff --git a/folding-schemes/src/folding/hypernova/lcccs.rs b/folding-schemes/src/folding/hypernova/lcccs.rs index e36eb413..b930d8d3 100644 --- a/folding-schemes/src/folding/hypernova/lcccs.rs +++ b/folding-schemes/src/folding/hypernova/lcccs.rs @@ -10,7 +10,7 @@ use super::circuits::LCCCSVar; use super::Witness; use crate::arith::ccs::CCS; use crate::commitment::CommitmentScheme; -use crate::folding::traits::CommittedInstanceExt; +use crate::folding::traits::CommittedInstanceOps; use crate::transcript::AbsorbNonNative; use crate::utils::mle::dense_vec_to_dense_mle; use crate::utils::vec::mat_vec_mul; @@ -140,7 +140,7 @@ where } } -impl CommittedInstanceExt for LCCCS { +impl CommittedInstanceOps for LCCCS { type Var = LCCCSVar; fn get_commitments(&self) -> Vec { diff --git a/folding-schemes/src/folding/hypernova/mod.rs b/folding-schemes/src/folding/hypernova/mod.rs index c85ae075..b79bb822 100644 --- a/folding-schemes/src/folding/hypernova/mod.rs +++ b/folding-schemes/src/folding/hypernova/mod.rs @@ -31,7 +31,7 @@ use crate::folding::circuits::{ CF2, }; use crate::folding::nova::{get_r1cs_from_cs, PreprocessorParam}; -use crate::folding::traits::{CommittedInstanceExt, WitnessExt}; +use crate::folding::traits::{CommittedInstanceOps, WitnessOps}; use crate::frontend::FCircuit; use crate::utils::{get_cm_coordinates, pp_hash}; use crate::Error; @@ -81,7 +81,7 @@ impl Witness { } } -impl WitnessExt for Witness { +impl WitnessOps for Witness { type Var = WitnessVar; fn get_openings(&self) -> Vec<(&[F], F)> { diff --git a/folding-schemes/src/folding/nova/circuits.rs b/folding-schemes/src/folding/nova/circuits.rs index bcf75a6b..c1162ce7 100644 --- a/folding-schemes/src/folding/nova/circuits.rs +++ b/folding-schemes/src/folding/nova/circuits.rs @@ -31,7 +31,7 @@ use crate::folding::circuits::{ }; use crate::frontend::FCircuit; use crate::transcript::{AbsorbNonNativeGadget, Transcript, TranscriptVar}; -use crate::{constants::NOVA_N_BITS_RO, folding::traits::CommittedInstanceVarExt}; +use crate::{constants::NOVA_N_BITS_RO, folding::traits::CommittedInstanceVarOps}; /// 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 @@ -90,7 +90,7 @@ where } } -impl CommittedInstanceVarExt for CommittedInstanceVar { +impl CommittedInstanceVarOps for CommittedInstanceVar { type PointVar = NonNativeAffineVar; fn get_commitments(&self) -> Vec { @@ -521,7 +521,7 @@ pub mod tests { use crate::commitment::pedersen::Pedersen; use crate::folding::nova::nifs::tests::prepare_simple_fold_inputs; use crate::folding::nova::nifs::NIFS; - use crate::folding::traits::CommittedInstanceExt; + use crate::folding::traits::CommittedInstanceOps; use crate::transcript::poseidon::poseidon_canonical_config; #[test] diff --git a/folding-schemes/src/folding/nova/decider_eth_circuit.rs b/folding-schemes/src/folding/nova/decider_eth_circuit.rs index 53be6a9c..27d83f1f 100644 --- a/folding-schemes/src/folding/nova/decider_eth_circuit.rs +++ b/folding-schemes/src/folding/nova/decider_eth_circuit.rs @@ -39,10 +39,10 @@ use crate::utils::{ vec::poly_from_vec, }; use crate::Error; -use crate::{arith::r1cs::R1CS, folding::traits::WitnessVarExt}; +use crate::{arith::r1cs::R1CS, folding::traits::WitnessVarOps}; use crate::{ commitment::{pedersen::Params as PedersenParams, CommitmentScheme}, - folding::traits::CommittedInstanceVarExt, + folding::traits::CommittedInstanceVarOps, }; #[derive(Debug, Clone)] @@ -162,7 +162,7 @@ where } } -impl WitnessVarExt for WitnessVar { +impl WitnessVarOps for WitnessVar { fn get_openings(&self) -> Vec<(&[FpVar], FpVar)> { vec![(&self.E, self.rE.clone()), (&self.W, self.rW.clone())] } diff --git a/folding-schemes/src/folding/nova/mod.rs b/folding-schemes/src/folding/nova/mod.rs index 5e499405..f47cf038 100644 --- a/folding-schemes/src/folding/nova/mod.rs +++ b/folding-schemes/src/folding/nova/mod.rs @@ -42,7 +42,7 @@ pub mod zk; use circuits::{AugmentedFCircuit, ChallengeGadget, CommittedInstanceVar}; use nifs::NIFS; -use super::traits::{CommittedInstanceExt, WitnessExt}; +use super::traits::{CommittedInstanceOps, WitnessOps}; /// Configuration for Nova's CycleFold circuit pub struct NovaCycleFoldConfig { @@ -106,7 +106,7 @@ where } } -impl CommittedInstanceExt for CommittedInstance { +impl CommittedInstanceOps for CommittedInstance { type Var = CommittedInstanceVar; fn get_commitments(&self) -> Vec { @@ -176,7 +176,7 @@ impl Witness { } } -impl WitnessExt for Witness { +impl WitnessOps for Witness { type Var = WitnessVar; fn get_openings(&self) -> Vec<(&[C::ScalarField], C::ScalarField)> { diff --git a/folding-schemes/src/folding/nova/zk.rs b/folding-schemes/src/folding/nova/zk.rs index 84e9ca48..8b5ff149 100644 --- a/folding-schemes/src/folding/nova/zk.rs +++ b/folding-schemes/src/folding/nova/zk.rs @@ -36,7 +36,7 @@ use ark_std::{One, Zero}; use crate::{ arith::r1cs::{RelaxedR1CS, R1CS}, - folding::traits::CommittedInstanceExt, + folding::traits::CommittedInstanceOps, RngCore, }; use ark_crypto_primitives::sponge::{ diff --git a/folding-schemes/src/folding/protogalaxy/circuits.rs b/folding-schemes/src/folding/protogalaxy/circuits.rs index aef62509..4ec2fadf 100644 --- a/folding-schemes/src/folding/protogalaxy/circuits.rs +++ b/folding-schemes/src/folding/protogalaxy/circuits.rs @@ -33,7 +33,7 @@ use crate::{ nonnative::{affine::NonNativeAffineVar, uint::NonNativeUintVar}, CF1, CF2, }, - traits::CommittedInstanceVarExt, + traits::CommittedInstanceVarOps, }, frontend::FCircuit, transcript::{AbsorbNonNativeGadget, TranscriptVar}, diff --git a/folding-schemes/src/folding/protogalaxy/mod.rs b/folding-schemes/src/folding/protogalaxy/mod.rs index c22b06b5..37b2836b 100644 --- a/folding-schemes/src/folding/protogalaxy/mod.rs +++ b/folding-schemes/src/folding/protogalaxy/mod.rs @@ -44,7 +44,7 @@ pub(crate) mod utils; use circuits::AugmentedFCircuit; use folding::Folding; -use super::traits::{CommittedInstanceExt, CommittedInstanceVarExt, WitnessExt, WitnessVarExt}; +use super::traits::{CommittedInstanceOps, CommittedInstanceVarOps, WitnessOps, WitnessVarOps}; /// Configuration for ProtoGalaxy's CycleFold circuit pub struct ProtoGalaxyCycleFoldConfig { @@ -85,7 +85,7 @@ impl CommittedInstance { } } -impl CommittedInstanceExt for CommittedInstance { +impl CommittedInstanceOps for CommittedInstance { type Var = CommittedInstanceVar; fn get_commitments(&self) -> Vec { @@ -151,7 +151,7 @@ impl R1CSVar for CommittedInstanceVar { } } -impl CommittedInstanceVarExt for CommittedInstanceVar { +impl CommittedInstanceVarOps for CommittedInstanceVar { type PointVar = NonNativeAffineVar; fn get_commitments(&self) -> Vec { @@ -206,7 +206,7 @@ impl Witness { } } -impl WitnessExt for Witness { +impl WitnessOps for Witness { type Var = WitnessVar; fn get_openings(&self) -> Vec<(&[F], F)> { @@ -238,7 +238,7 @@ impl AllocVar, F> for WitnessVar { } } -impl WitnessVarExt for WitnessVar { +impl WitnessVarOps for WitnessVar { fn get_openings(&self) -> Vec<(&[FpVar], FpVar)> { vec![(&self.W, self.rW.clone())] } diff --git a/folding-schemes/src/folding/traits.rs b/folding-schemes/src/folding/traits.rs index a96ba72f..5815397d 100644 --- a/folding-schemes/src/folding/traits.rs +++ b/folding-schemes/src/folding/traits.rs @@ -12,9 +12,9 @@ use crate::{transcript::Transcript, Error}; use super::circuits::CF1; -pub trait CommittedInstanceExt { +pub trait CommittedInstanceOps { /// The in-circuit representation of the committed instance. - type Var: AllocVar> + CommittedInstanceVarExt; + type Var: AllocVar> + CommittedInstanceVarOps; /// `hash` implements the committed instance hash compatible with the /// in-circuit implementation from `CommittedInstanceVarOps::hash`. /// @@ -56,10 +56,10 @@ pub trait CommittedInstanceExt { } } -pub trait CommittedInstanceVarExt { +pub trait CommittedInstanceVarOps { type PointVar: ToConstraintFieldGadget>; /// `hash` implements the in-circuit committed instance hash compatible with - /// the native implementation from `CommittedInstanceExt::hash`. + /// the native implementation from `CommittedInstanceOps::hash`. /// Returns `H(i, z_0, z_i, U_i)`, where `i` can be `i` but also `i+1`, and /// `U_i` is the committed instance `self`. /// @@ -105,16 +105,16 @@ pub trait CommittedInstanceVarExt { fn enforce_partial_equal(&self, other: &Self) -> Result<(), SynthesisError>; } -pub trait WitnessExt { +pub trait WitnessOps { /// The in-circuit representation of the witness. - type Var: AllocVar + WitnessVarExt; + type Var: AllocVar + WitnessVarOps; /// Returns the openings (i.e., the values being committed to and the /// randomness) contained in the witness. fn get_openings(&self) -> Vec<(&[F], F)>; } -pub trait WitnessVarExt { +pub trait WitnessVarOps { /// Returns the openings (i.e., the values being committed to and the /// randomness) contained in the witness. fn get_openings(&self) -> Vec<(&[FpVar], FpVar)>; From 2c9eb4c232944fedf974c88fd7d54b2b49557545 Mon Sep 17 00:00:00 2001 From: winderica Date: Fri, 20 Sep 2024 00:24:05 +0800 Subject: [PATCH 8/8] Implement `to_sponge_bytes` --- folding-schemes/src/folding/hypernova/cccs.rs | 5 +- .../src/folding/hypernova/circuits.rs | 33 ++++++++++++- .../src/folding/hypernova/lcccs.rs | 5 +- folding-schemes/src/folding/nova/circuits.rs | 32 +++++++++++- folding-schemes/src/folding/nova/mod.rs | 5 +- .../src/folding/protogalaxy/traits.rs | 49 +++++++++++++++++-- 6 files changed, 115 insertions(+), 14 deletions(-) diff --git a/folding-schemes/src/folding/hypernova/cccs.rs b/folding-schemes/src/folding/hypernova/cccs.rs index fb819a3a..2c25524f 100644 --- a/folding-schemes/src/folding/hypernova/cccs.rs +++ b/folding-schemes/src/folding/hypernova/cccs.rs @@ -126,9 +126,8 @@ impl Absorb for CCCS where C::ScalarField: Absorb, { - fn to_sponge_bytes(&self, _dest: &mut Vec) { - // This is never called - unimplemented!() + fn to_sponge_bytes(&self, dest: &mut Vec) { + C::ScalarField::batch_to_sponge_bytes(&self.to_sponge_field_elements_as_vec(), dest); } fn to_sponge_field_elements(&self, dest: &mut Vec) { diff --git a/folding-schemes/src/folding/hypernova/circuits.rs b/folding-schemes/src/folding/hypernova/circuits.rs index 0b610d68..1fb4f096 100644 --- a/folding-schemes/src/folding/hypernova/circuits.rs +++ b/folding-schemes/src/folding/hypernova/circuits.rs @@ -146,7 +146,7 @@ where impl AbsorbGadget for LCCCSVar { fn to_sponge_bytes(&self) -> Result>, SynthesisError> { - unimplemented!() + FpVar::batch_to_sponge_bytes(&self.to_sponge_field_elements()?) } fn to_sponge_field_elements(&self) -> Result>, SynthesisError> { @@ -1129,6 +1129,37 @@ mod tests { assert_eq!(folded_lcccsVar.u.value().unwrap(), folded_lcccs.u); } + /// test that checks the native LCCCS.to_sponge_{bytes,field_elements} vs + /// the R1CS constraints version + #[test] + pub fn test_lcccs_to_sponge_preimage() { + let mut rng = test_rng(); + + let ccs = get_test_ccs(); + let z1 = get_test_z::(3); + + let (pedersen_params, _) = + Pedersen::::setup(&mut rng, ccs.n - ccs.l - 1).unwrap(); + + let (lcccs, _) = ccs + .to_lcccs::<_, _, Pedersen, true>(&mut rng, &pedersen_params, &z1) + .unwrap(); + let bytes = lcccs.to_sponge_bytes_as_vec(); + let field_elements = lcccs.to_sponge_field_elements_as_vec(); + + let cs = ConstraintSystem::::new_ref(); + + let lcccsVar = LCCCSVar::::new_witness(cs.clone(), || Ok(lcccs)).unwrap(); + let bytes_var = lcccsVar.to_sponge_bytes().unwrap(); + let field_elements_var = lcccsVar.to_sponge_field_elements().unwrap(); + + assert!(cs.is_satisfied().unwrap()); + + // check that the natively computed and in-circuit computed hashes match + assert_eq!(bytes_var.value().unwrap(), bytes); + assert_eq!(field_elements_var.value().unwrap(), field_elements); + } + /// test that checks the native LCCCS.hash vs the R1CS constraints version #[test] pub fn test_lcccs_hash() { diff --git a/folding-schemes/src/folding/hypernova/lcccs.rs b/folding-schemes/src/folding/hypernova/lcccs.rs index b930d8d3..d95d21fa 100644 --- a/folding-schemes/src/folding/hypernova/lcccs.rs +++ b/folding-schemes/src/folding/hypernova/lcccs.rs @@ -121,9 +121,8 @@ impl Absorb for LCCCS where C::ScalarField: Absorb, { - fn to_sponge_bytes(&self, _dest: &mut Vec) { - // This is never called - unimplemented!() + fn to_sponge_bytes(&self, dest: &mut Vec) { + C::ScalarField::batch_to_sponge_bytes(&self.to_sponge_field_elements_as_vec(), dest); } fn to_sponge_field_elements(&self, dest: &mut Vec) { diff --git a/folding-schemes/src/folding/nova/circuits.rs b/folding-schemes/src/folding/nova/circuits.rs index c1162ce7..6e90460b 100644 --- a/folding-schemes/src/folding/nova/circuits.rs +++ b/folding-schemes/src/folding/nova/circuits.rs @@ -76,7 +76,7 @@ where ::BaseField: ark_ff::PrimeField, { fn to_sponge_bytes(&self) -> Result>, SynthesisError> { - unimplemented!() + FpVar::batch_to_sponge_bytes(&self.to_sponge_field_elements()?) } fn to_sponge_field_elements(&self) -> Result>, SynthesisError> { @@ -575,6 +575,36 @@ pub mod tests { assert!(cs.is_satisfied().unwrap()); } + /// test that checks the native CommittedInstance.to_sponge_{bytes,field_elements} + /// vs the R1CS constraints version + #[test] + pub fn test_committed_instance_to_sponge_preimage() { + let mut rng = ark_std::test_rng(); + + let ci = CommittedInstance:: { + cmE: Projective::rand(&mut rng), + u: Fr::rand(&mut rng), + cmW: Projective::rand(&mut rng), + x: vec![Fr::rand(&mut rng); 1], + }; + + let bytes = ci.to_sponge_bytes_as_vec(); + let field_elements = ci.to_sponge_field_elements_as_vec(); + + let cs = ConstraintSystem::::new_ref(); + + let ciVar = + CommittedInstanceVar::::new_witness(cs.clone(), || Ok(ci.clone())).unwrap(); + let bytes_var = ciVar.to_sponge_bytes().unwrap(); + let field_elements_var = ciVar.to_sponge_field_elements().unwrap(); + + assert!(cs.is_satisfied().unwrap()); + + // check that the natively computed and in-circuit computed hashes match + assert_eq!(bytes_var.value().unwrap(), bytes); + assert_eq!(field_elements_var.value().unwrap(), field_elements); + } + #[test] fn test_committed_instance_hash() { let mut rng = ark_std::test_rng(); diff --git a/folding-schemes/src/folding/nova/mod.rs b/folding-schemes/src/folding/nova/mod.rs index f47cf038..97150a01 100644 --- a/folding-schemes/src/folding/nova/mod.rs +++ b/folding-schemes/src/folding/nova/mod.rs @@ -86,9 +86,8 @@ impl Absorb for CommittedInstance where C::ScalarField: Absorb, { - fn to_sponge_bytes(&self, _dest: &mut Vec) { - // This is never called - unimplemented!() + fn to_sponge_bytes(&self, dest: &mut Vec) { + C::ScalarField::batch_to_sponge_bytes(&self.to_sponge_field_elements_as_vec(), dest); } fn to_sponge_field_elements(&self, dest: &mut Vec) { diff --git a/folding-schemes/src/folding/protogalaxy/traits.rs b/folding-schemes/src/folding/protogalaxy/traits.rs index d735f6eb..d67f79ee 100644 --- a/folding-schemes/src/folding/protogalaxy/traits.rs +++ b/folding-schemes/src/folding/protogalaxy/traits.rs @@ -18,8 +18,8 @@ impl Absorb for CommittedInstance where C::ScalarField: Absorb, { - fn to_sponge_bytes(&self, _dest: &mut Vec) { - unimplemented!() + fn to_sponge_bytes(&self, dest: &mut Vec) { + C::ScalarField::batch_to_sponge_bytes(&self.to_sponge_field_elements_as_vec(), dest); } fn to_sponge_field_elements(&self, dest: &mut Vec) { @@ -35,7 +35,7 @@ where // Implements the trait for absorbing ProtoGalaxy's CommittedInstanceVar in-circuit. impl AbsorbGadget for CommittedInstanceVar { fn to_sponge_bytes(&self) -> Result>, SynthesisError> { - unimplemented!() + FpVar::batch_to_sponge_bytes(&self.to_sponge_field_elements()?) } fn to_sponge_field_elements(&self) -> Result>, SynthesisError> { @@ -114,3 +114,46 @@ impl RelaxedR1CS, CommittedInstance unimplemented!() } } + +#[cfg(test)] +pub mod tests { + use super::*; + use ark_bn254::{Fr, G1Projective as Projective}; + use ark_r1cs_std::{alloc::AllocVar, R1CSVar}; + use ark_relations::r1cs::ConstraintSystem; + use ark_std::UniformRand; + use rand::Rng; + + /// test that checks the native CommittedInstance.to_sponge_{bytes,field_elements} + /// vs the R1CS constraints version + #[test] + pub fn test_committed_instance_to_sponge_preimage() { + let mut rng = ark_std::test_rng(); + + let t = rng.gen::() as usize; + let io_len = rng.gen::() as usize; + + let ci = CommittedInstance:: { + phi: Projective::rand(&mut rng), + betas: (0..t).map(|_| Fr::rand(&mut rng)).collect(), + e: Fr::rand(&mut rng), + x: (0..io_len).map(|_| Fr::rand(&mut rng)).collect(), + }; + + let bytes = ci.to_sponge_bytes_as_vec(); + let field_elements = ci.to_sponge_field_elements_as_vec(); + + let cs = ConstraintSystem::::new_ref(); + + let ciVar = + CommittedInstanceVar::::new_witness(cs.clone(), || Ok(ci.clone())).unwrap(); + let bytes_var = ciVar.to_sponge_bytes().unwrap(); + let field_elements_var = ciVar.to_sponge_field_elements().unwrap(); + + assert!(cs.is_satisfied().unwrap()); + + // check that the natively computed and in-circuit computed hashes match + assert_eq!(bytes_var.value().unwrap(), bytes); + assert_eq!(field_elements_var.value().unwrap(), field_elements); + } +}