From b8db622a08ff7c7d11a0003f8bd9a3a8955649e8 Mon Sep 17 00:00:00 2001 From: arnaucube Date: Thu, 4 Apr 2024 09:58:26 +0200 Subject: [PATCH] Link committed instances and r to the public input x in cyclefold circuit (#81) * CycleFold circuit: link r,U_i,u_i point coords as inputs * DeciderEth::prove: rm repeated cmT, r, W_i1 computation --- .../src/folding/circuits/nonnative.rs | 3 +- folding-schemes/src/folding/nova/circuits.rs | 22 ++++++- folding-schemes/src/folding/nova/cyclefold.rs | 37 +++++++----- .../src/folding/nova/decider_eth.rs | 60 ++++--------------- .../src/folding/nova/decider_eth_circuit.rs | 5 +- folding-schemes/src/folding/nova/mod.rs | 31 ++++++---- folding-schemes/src/utils/mle.rs | 3 - 7 files changed, 73 insertions(+), 88 deletions(-) diff --git a/folding-schemes/src/folding/circuits/nonnative.rs b/folding-schemes/src/folding/circuits/nonnative.rs index 8211d7a..599d718 100644 --- a/folding-schemes/src/folding/circuits/nonnative.rs +++ b/folding-schemes/src/folding/circuits/nonnative.rs @@ -1,8 +1,7 @@ use ark_ec::{AffineRepr, CurveGroup}; -use ark_r1cs_std::fields::nonnative::{params::OptimizationType, AllocatedNonNativeFieldVar}; use ark_r1cs_std::{ alloc::{AllocVar, AllocationMode}, - fields::nonnative::NonNativeFieldVar, + fields::nonnative::{params::OptimizationType, AllocatedNonNativeFieldVar, NonNativeFieldVar}, }; use ark_relations::r1cs::{Namespace, SynthesisError}; use ark_std::{One, Zero}; diff --git a/folding-schemes/src/folding/nova/circuits.rs b/folding-schemes/src/folding/nova/circuits.rs index 764c269..767f65b 100644 --- a/folding-schemes/src/folding/nova/circuits.rs +++ b/folding-schemes/src/folding/nova/circuits.rs @@ -251,6 +251,7 @@ pub struct AugmentedFCircuit< pub u_i: Option>, pub U_i: Option>, pub U_i1: Option>, + pub r_nonnat: Option>, pub cmT: Option, pub F: FC, // F circuit pub x: Option>, // public inputs (u_{i+1}.x) @@ -285,6 +286,7 @@ where u_i: None, U_i: None, U_i1: None, + r_nonnat: None, cmT: None, F: F_circuit, x: None, @@ -340,6 +342,10 @@ where let U_i1 = CommittedInstanceVar::::new_witness(cs.clone(), || { Ok(self.U_i1.unwrap_or(u_dummy_native.clone())) })?; + let r_nonnat = + NonNativeFieldVar::::new_witness(cs.clone(), || { + Ok(self.r_nonnat.unwrap_or_else(CF2::::zero)) + })?; let cmT = NonNativeAffineVar::new_witness(cs.clone(), || Ok(self.cmT.unwrap_or_else(C1::zero)))?; let x = @@ -363,7 +369,6 @@ where let (u_i_x, U_i_vec) = U_i.clone() .hash(&crh_params, i.clone(), z_0.clone(), z_i.clone())?; - // check that h == u_i.x (u_i.x[0]).conditional_enforce_equal(&u_i_x, &is_not_basecase)?; @@ -391,6 +396,11 @@ where )?; let r = Boolean::le_bits_to_fp_var(&r_bits)?; + // enforce that the input r_nonnat as bits matches the in-circuit computed r_bits + let r_nonnat_bits: Vec> = + r_nonnat.to_bits_le()?.into_iter().take(N_BITS_RO).collect(); + r_nonnat_bits.enforce_equal(&r_bits)?; + // Notice that NIFSGadget::verify is not checking the folding of cmE & cmW, since it will // be done on the other curve. let nifs_check = NIFSGadget::::verify(r, U_i.clone(), u_i.clone(), U_i1.clone())?; @@ -436,10 +446,16 @@ where })?; let cfW_x: Vec> = vec![ - U_i.cmW.x, U_i.cmW.y, u_i.cmW.x, u_i.cmW.y, U_i1.cmW.x, U_i1.cmW.y, + r_nonnat.clone(), + U_i.cmW.x, + U_i.cmW.y, + u_i.cmW.x, + u_i.cmW.y, + U_i1.cmW.x, + U_i1.cmW.y, ]; let cfE_x: Vec> = vec![ - U_i.cmE.x, U_i.cmE.y, u_i.cmE.x, u_i.cmE.y, U_i1.cmE.x, U_i1.cmE.y, + r_nonnat, U_i.cmE.x, U_i.cmE.y, cmT.x, cmT.y, U_i1.cmE.x, U_i1.cmE.y, ]; // ensure that cf1_u & cf2_u have as public inputs the cmW & cmE from main instances U_i, diff --git a/folding-schemes/src/folding/nova/cyclefold.rs b/folding-schemes/src/folding/nova/cyclefold.rs index 2ec000e..202fffa 100644 --- a/folding-schemes/src/folding/nova/cyclefold.rs +++ b/folding-schemes/src/folding/nova/cyclefold.rs @@ -14,7 +14,7 @@ use ark_r1cs_std::{ fields::{fp::FpVar, nonnative::NonNativeFieldVar}, groups::GroupOpsBounds, prelude::CurveVar, - ToBytesGadget, + ToBytesGadget, ToConstraintFieldGadget, }; use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, Namespace, SynthesisError}; use ark_serialize::CanonicalSerialize; @@ -27,8 +27,8 @@ use super::CommittedInstance; use crate::constants::N_BITS_RO; use crate::Error; -// publi inputs length for the CycleFoldCircuit, |[p1.x,y, p2.x,y, p3.x,y]| -pub const CF_IO_LEN: usize = 6; +// public inputs length for the CycleFoldCircuit: |[r, p1.x,y, p2.x,y, p3.x,y]| +pub const CF_IO_LEN: usize = 7; /// CycleFoldCommittedInstanceVar is the CycleFold CommittedInstance representation in the Nova /// circuit. @@ -318,7 +318,7 @@ impl>> CycleFoldCircuit { impl ConstraintSynthesizer> for CycleFoldCircuit where C: CurveGroup, - GC: CurveVar>, + GC: CurveVar> + ToConstraintFieldGadget>, ::BaseField: ark_ff::PrimeField, for<'a> &'a GC: GroupOpsBounds<'a, C, GC>, { @@ -330,11 +330,22 @@ where let p2 = GC::new_witness(cs.clone(), || Ok(self.p2.unwrap_or(C::zero())))?; let p3 = GC::new_witness(cs.clone(), || Ok(self.p3.unwrap_or(C::zero())))?; - let _x = Vec::>>::new_input(cs.clone(), || { + let x = Vec::>>::new_input(cs.clone(), || { Ok(self.x.unwrap_or(vec![CF2::::zero(); CF_IO_LEN])) })?; #[cfg(test)] - assert_eq!(_x.len(), CF_IO_LEN); // non-constrained sanity check + assert_eq!(x.len(), CF_IO_LEN); // non-constrained sanity check + + // check that the points coordinates are placed as the public input x: x == [r, p1, p2, p3] + let r: FpVar> = Boolean::le_bits_to_fp_var(&r_bits)?; + let points_coords: Vec>> = [ + vec![r], + p1.clone().to_constraint_field()?[..2].to_vec(), + p2.clone().to_constraint_field()?[..2].to_vec(), + p3.clone().to_constraint_field()?[..2].to_vec(), + ] + .concat(); + points_coords.enforce_equal(&x)?; // Fold the original Nova instances natively in CycleFold // For the cmW we're checking: U_i1.cmW == U_i.cmW + r * u_i.cmW @@ -342,12 +353,6 @@ where // is assumed to be 0, so, U_i1.cmE == U_i.cmE + r * cmT p3.enforce_equal(&(p1 + p2.scalar_mul_le(r_bits.iter())?))?; - // check that x == [u_i, U_i, U_{i+1}], check that the cmW & cmW from u_i, U_i, U_{i+1} in - // the CycleFoldCircuit are the sames used in the public inputs 'x', which come from the - // AugmentedFCircuit. - // TODO: Issue to keep track of this: https://github.com/privacy-scaling-explorations/folding-schemes/issues/44 - // and https://github.com/privacy-scaling-explorations/folding-schemes/issues/48 - Ok(()) } } @@ -390,12 +395,14 @@ pub mod tests { #[test] fn test_CycleFoldCircuit_constraints() { let (_, _, _, _, ci1, _, ci2, _, ci3, _, cmT, r_bits, _) = prepare_simple_fold_inputs(); + let r_Fq = Fq::from_bigint(BigInteger::from_bits_le(&r_bits)).unwrap(); // cs is the Constraint System on the Curve Cycle auxiliary curve constraints field // (E1::Fq=E2::Fr) let cs = ConstraintSystem::::new_ref(); - let cfW_u_i_x = [ + let cfW_u_i_x: Vec = [ + vec![r_Fq], get_cm_coordinates(&ci1.cmW), get_cm_coordinates(&ci2.cmW), get_cm_coordinates(&ci3.cmW), @@ -411,13 +418,13 @@ pub mod tests { }; cfW_circuit.generate_constraints(cs.clone()).unwrap(); assert!(cs.is_satisfied().unwrap()); - dbg!(cs.num_constraints()); // same for E: let cs = ConstraintSystem::::new_ref(); let cfE_u_i_x = [ + vec![r_Fq], get_cm_coordinates(&ci1.cmE), - get_cm_coordinates(&ci2.cmE), + get_cm_coordinates(&cmT), get_cm_coordinates(&ci3.cmE), ] .concat(); diff --git a/folding-schemes/src/folding/nova/decider_eth.rs b/folding-schemes/src/folding/nova/decider_eth.rs index 1b8261a..1e3dcc5 100644 --- a/folding-schemes/src/folding/nova/decider_eth.rs +++ b/folding-schemes/src/folding/nova/decider_eth.rs @@ -1,20 +1,16 @@ /// This file implements the onchain (Ethereum's EVM) decider. -use ark_crypto_primitives::sponge::{poseidon::PoseidonConfig, Absorb}; +use ark_crypto_primitives::sponge::Absorb; use ark_ec::{CurveGroup, Group}; -use ark_ff::{BigInteger, PrimeField}; +use ark_ff::PrimeField; use ark_r1cs_std::fields::nonnative::params::OptimizationType; -use ark_r1cs_std::{groups::GroupOpsBounds, prelude::CurveVar}; +use ark_r1cs_std::{groups::GroupOpsBounds, prelude::CurveVar, ToConstraintFieldGadget}; use ark_snark::SNARK; use ark_std::rand::{CryptoRng, RngCore}; use ark_std::Zero; use core::marker::PhantomData; pub use super::decider_eth_circuit::{DeciderEthCircuit, KZGChallengesGadget}; -use super::{ - circuits::{ChallengeGadget, CF2}, - nifs::NIFS, - CommittedInstance, Nova, Witness, -}; +use super::{circuits::CF2, nifs::NIFS, CommittedInstance, Nova}; use crate::commitment::{ kzg::Proof as KZGProof, pedersen::Params as PedersenParams, CommitmentScheme, }; @@ -60,7 +56,7 @@ impl DeciderTrait where C1: CurveGroup, C2: CurveGroup, - GC1: CurveVar>, + GC1: CurveVar> + ToConstraintFieldGadget>, GC2: CurveVar>, FC: FCircuit, CS1: CommitmentScheme< @@ -82,11 +78,7 @@ where // constrain FS into Nova, since this is a Decider specifically for Nova Nova: From, { - type ProverParam = ( - PoseidonConfig, - S::ProvingKey, - CS1::ProverParams, - ); + type ProverParam = (S::ProvingKey, CS1::ProverParams); type Proof = Proof; type VerifierParam = (S::VerifyingKey, CS1::VerifierParams); type PublicInput = Vec; @@ -98,11 +90,7 @@ where mut rng: impl RngCore + CryptoRng, folding_scheme: FS, ) -> Result { - let (poseidon_config, snark_pk, cs_pk): ( - PoseidonConfig, - S::ProvingKey, - CS1::ProverParams, - ) = pp; + let (snark_pk, cs_pk): (S::ProvingKey, CS1::ProverParams) = pp; let circuit = DeciderEthCircuit::::from_nova::( folding_scheme.into(), @@ -111,35 +99,9 @@ where let snark_proof = S::prove(&snark_pk, circuit.clone(), &mut rng) .map_err(|e| Error::Other(e.to_string()))?; - let U_i = circuit - .U_i - .clone() - .ok_or(Error::MissingValue("U_i".to_string()))?; - let W_i = circuit - .W_i - .clone() - .ok_or(Error::MissingValue("W_i".to_string()))?; - let u_i = circuit - .u_i - .clone() - .ok_or(Error::MissingValue("u_i".to_string()))?; - let w_i = circuit - .w_i - .clone() - .ok_or(Error::MissingValue("w_i".to_string()))?; - - // compute NIFS.P((U_d, W_d), (u_d, w_d)) = (U_{d+1}, W_{d+1}, cmT) - let (T, cmT) = NIFS::::compute_cmT(&cs_pk, &circuit.r1cs, &w_i, &u_i, &W_i, &U_i)?; - let r_bits = ChallengeGadget::::get_challenge_native( - &poseidon_config, - U_i.clone(), - u_i.clone(), - cmT, - )?; - let r_Fr = C1::ScalarField::from_bigint(BigInteger::from_bits_le(&r_bits)) - .ok_or(Error::OutOfBounds)?; - let (W_i1, _): (Witness, CommittedInstance) = - NIFS::::fold_instances(r_Fr, &W_i, &U_i, &w_i, &u_i, &T, cmT)?; + let cmT = circuit.cmT.unwrap(); + let r_Fr = circuit.r.unwrap(); + let W_i1 = circuit.W_i1.unwrap(); // get the challenges that have been already computed when preparing the circuit inputs in // the above `from_nova` call @@ -332,7 +294,7 @@ pub mod tests { // decider proof generation let start = Instant::now(); - let decider_pp = (poseidon_config.clone(), g16_pk, kzg_pk); + let decider_pp = (g16_pk, kzg_pk); let proof = DECIDER::prove(decider_pp, rng, nova.clone()).unwrap(); println!("Decider prove, {:?}", start.elapsed()); diff --git a/folding-schemes/src/folding/nova/decider_eth_circuit.rs b/folding-schemes/src/folding/nova/decider_eth_circuit.rs index d199d50..db50ef6 100644 --- a/folding-schemes/src/folding/nova/decider_eth_circuit.rs +++ b/folding-schemes/src/folding/nova/decider_eth_circuit.rs @@ -244,7 +244,7 @@ impl DeciderEthCircuit where C1: CurveGroup, C2: CurveGroup, - GC1: CurveVar>, + GC1: CurveVar> + ToConstraintFieldGadget>, GC2: CurveVar>, CS1: CommitmentScheme, // enforce that the CS2 is Pedersen commitment scheme, since we're at Ethereum's EVM decider @@ -551,6 +551,7 @@ fn evaluate_gadget( pub struct KZGChallengesGadget { _c: PhantomData, } +#[allow(clippy::type_complexity)] impl KZGChallengesGadget where C: CurveGroup, @@ -580,7 +581,6 @@ where Ok((challenge_W, challenge_E)) } // compatible with the native get_challenges_native - #[allow(clippy::type_complexity)] pub fn get_challenges_gadget( cs: ConstraintSystemRef, poseidon_config: &PoseidonConfig, @@ -860,7 +860,6 @@ pub mod tests { // generate the constraints and check that are satisfied by the inputs decider_circuit.generate_constraints(cs.clone()).unwrap(); assert!(cs.is_satisfied().unwrap()); - dbg!(cs.num_constraints()); } // checks that the gadget and native implementations of the challenge computation match diff --git a/folding-schemes/src/folding/nova/mod.rs b/folding-schemes/src/folding/nova/mod.rs index 499ef25..fc8956c 100644 --- a/folding-schemes/src/folding/nova/mod.rs +++ b/folding-schemes/src/folding/nova/mod.rs @@ -6,7 +6,7 @@ use ark_crypto_primitives::{ }; use ark_ec::{AffineRepr, CurveGroup, Group}; use ark_ff::{BigInteger, PrimeField}; -use ark_r1cs_std::{groups::GroupOpsBounds, prelude::CurveVar}; +use ark_r1cs_std::{groups::GroupOpsBounds, prelude::CurveVar, ToConstraintFieldGadget}; use ark_std::fmt::Debug; use ark_std::{One, Zero}; use core::marker::PhantomData; @@ -160,7 +160,7 @@ pub struct VerifierParams { pub struct Nova where C1: CurveGroup, - GC1: CurveVar>, + GC1: CurveVar> + ToConstraintFieldGadget>, C2: CurveGroup, GC2: CurveVar>, FC: FCircuit, @@ -201,7 +201,7 @@ impl FoldingScheme for Nova where C1: CurveGroup, - GC1: CurveVar>, + GC1: CurveVar> + ToConstraintFieldGadget>, C2: CurveGroup, GC2: CurveVar>, FC: FCircuit, @@ -289,8 +289,6 @@ where /// Implements IVC.P of Nova+CycleFold fn prove_step(&mut self) -> Result<(), Error> { let augmented_F_circuit: AugmentedFCircuit; - let cfW_circuit: CycleFoldCircuit; - let cfE_circuit: CycleFoldCircuit; if self.i > C1::ScalarField::from_le_bytes_mod_order(&std::usize::MAX.to_le_bytes()) { return Err(Error::MaxStep); @@ -313,6 +311,8 @@ where )?; 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)) + .ok_or(Error::OutOfBounds)?; // fold Nova instances let (W_i1, U_i1): (Witness, CommittedInstance) = NIFS::::fold_instances( @@ -340,6 +340,7 @@ where u_i: Some(self.u_i.clone()), // = dummy U_i: Some(self.U_i.clone()), // = dummy U_i1: Some(U_i1.clone()), + r_nonnat: Some(r_Fq), cmT: Some(cmT), F: self.F.clone(), x: Some(u_i1_x), @@ -361,6 +362,7 @@ where // get the vector used as public inputs 'x' in the CycleFold circuit // cyclefold circuit for cmW let cfW_u_i_x = [ + vec![r_Fq], get_cm_coordinates(&self.U_i.cmW), get_cm_coordinates(&self.u_i.cmW), get_cm_coordinates(&U_i1.cmW), @@ -368,13 +370,14 @@ where .concat(); // cyclefold circuit for cmE let cfE_u_i_x = [ + vec![r_Fq], get_cm_coordinates(&self.U_i.cmE), - get_cm_coordinates(&self.u_i.cmE), + get_cm_coordinates(&cmT), get_cm_coordinates(&U_i1.cmE), ] .concat(); - cfW_circuit = CycleFoldCircuit:: { + let cfW_circuit = CycleFoldCircuit:: { _gc: PhantomData, r_bits: Some(r_bits.clone()), p1: Some(self.U_i.clone().cmW), @@ -382,7 +385,7 @@ where p3: Some(U_i1.clone().cmW), x: Some(cfW_u_i_x.clone()), }; - cfE_circuit = CycleFoldCircuit:: { + let cfE_circuit = CycleFoldCircuit:: { _gc: PhantomData, r_bits: Some(r_bits.clone()), p1: Some(self.U_i.clone().cmE), @@ -413,6 +416,7 @@ where u_i: Some(self.u_i.clone()), U_i: Some(self.U_i.clone()), U_i1: Some(U_i1.clone()), + r_nonnat: Some(r_Fq), cmT: Some(cmT), F: self.F.clone(), x: Some(u_i1_x), @@ -539,7 +543,7 @@ where impl Nova where C1: CurveGroup, - GC1: CurveVar>, + GC1: CurveVar> + ToConstraintFieldGadget>, C2: CurveGroup, GC2: CurveVar>, FC: FCircuit, @@ -583,7 +587,7 @@ where impl Nova where C1: CurveGroup, - GC1: CurveVar>, + GC1: CurveVar> + ToConstraintFieldGadget>, C2: CurveGroup, GC2: CurveVar>, FC: FCircuit, @@ -674,7 +678,7 @@ pub fn get_r1cs( ) -> Result<(R1CS, R1CS), Error> where C1: CurveGroup, - GC1: CurveVar>, + GC1: CurveVar> + ToConstraintFieldGadget>, C2: CurveGroup, GC2: CurveVar>, FC: FCircuit, @@ -702,7 +706,7 @@ pub fn get_cs_params_len( ) -> Result<(usize, usize), Error> where C1: CurveGroup, - GC1: CurveVar>, + GC1: CurveVar> + ToConstraintFieldGadget>, C2: CurveGroup, GC2: CurveVar>, FC: FCircuit, @@ -718,7 +722,8 @@ where Ok((r1cs.A.n_rows, cf_r1cs.A.n_rows)) } -/// returns the coordinates of a commitment point +/// returns the coordinates of a commitment point. This is compatible with the arkworks +/// GC.to_constraint_field()[..2] pub(crate) fn get_cm_coordinates(cm: &C) -> Vec { let zero = (&C::BaseField::zero(), &C::BaseField::one()); let cm = cm.into_affine(); diff --git a/folding-schemes/src/utils/mle.rs b/folding-schemes/src/utils/mle.rs index b7836f3..ceece42 100644 --- a/folding-schemes/src/utils/mle.rs +++ b/folding-schemes/src/utils/mle.rs @@ -51,8 +51,6 @@ pub fn vec_to_mle(n_vars: usize, v: &Vec) -> DenseMultilinearE } pub fn dense_vec_to_mle(n_vars: usize, v: &Vec) -> DenseMultilinearExtension { - dbg!(n_vars); - dbg!(v.len()); // Pad to 2^n_vars let v_padded: Vec = [ v.clone(), @@ -88,7 +86,6 @@ mod tests { ]); let A_mle = matrix_to_mle(A); - dbg!(&A_mle); assert_eq!(A_mle.evaluations.len(), 16); // 4x4 matrix, thus 2bit x 2bit, thus 2^4=16 evals let A = to_F_matrix::(vec![