arnaucube
arnaucube/sonobePublic Self-hosted repository
 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
arnaucube root@arnaucube.com
Thu, 04 Apr 2024 09:58:26 +0200
commit

b8db622a08ff7c7d11a0003f8bd9a3a8955649e8

parent

fe9a488f63fd8a131119cd3e40ec4d80dcf4690a

M folding-schemes/src/folding/circuits/nonnative.rs → 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};
M folding-schemes/src/folding/nova/circuits.rs → folding-schemes/src/folding/nova/circuits.rs

@@ -251,6 +251,7 @@ pub z_i: Option<Vec<C1::ScalarField>>,

pub u_i: Option<CommittedInstance<C1>>, pub U_i: Option<CommittedInstance<C1>>, pub U_i1: Option<CommittedInstance<C1>>, + pub r_nonnat: Option<CF2<C1>>, pub cmT: Option<C1>, pub F: FC, // F circuit pub x: Option<CF1<C1>>, // public inputs (u_{i+1}.x)

@@ -285,6 +286,7 @@ z_i: None,

u_i: None, U_i: None, U_i1: None, + r_nonnat: None, cmT: None, F: F_circuit, x: None,

@@ -340,6 +342,10 @@ })?;

let U_i1 = CommittedInstanceVar::<C1>::new_witness(cs.clone(), || { Ok(self.U_i1.unwrap_or(u_dummy_native.clone())) })?; + let r_nonnat = + NonNativeFieldVar::<C1::BaseField, C1::ScalarField>::new_witness(cs.clone(), || { + Ok(self.r_nonnat.unwrap_or_else(CF2::<C1>::zero)) + })?; let cmT = NonNativeAffineVar::new_witness(cs.clone(), || Ok(self.cmT.unwrap_or_else(C1::zero)))?; let x =

@@ -363,7 +369,6 @@ // 1. u_i.x == H(i, z_0, z_i, U_i)

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 @@ cmT.clone(),

)?; 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<Boolean<C1::ScalarField>> = + 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::<C1>::verify(r, U_i.clone(), u_i.clone(), U_i1.clone())?;

@@ -436,10 +446,16 @@ Ok(self.cf2_r_nonnat.unwrap_or_else(C2::ScalarField::zero))

})?; let cfW_x: Vec<NonNativeFieldVar<C1::BaseField, C1::ScalarField>> = 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<NonNativeFieldVar<C1::BaseField, C1::ScalarField>> = 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,
M folding-schemes/src/folding/nova/cyclefold.rs → folding-schemes/src/folding/nova/cyclefold.rs

@@ -14,7 +14,7 @@ eq::EqGadget,

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<C, GC> ConstraintSynthesizer<CF2<C>> for CycleFoldCircuit<C, GC> where C: CurveGroup, - GC: CurveVar<C, CF2<C>>, + GC: CurveVar<C, CF2<C>> + ToConstraintFieldGadget<CF2<C>>, <C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField, for<'a> &'a GC: GroupOpsBounds<'a, C, GC>, {

@@ -330,23 +330,28 @@ let p1 = GC::new_witness(cs.clone(), || Ok(self.p1.unwrap_or(C::zero())))?;

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::<FpVar<CF2<C>>>::new_input(cs.clone(), || { + let x = Vec::<FpVar<CF2<C>>>::new_input(cs.clone(), || { Ok(self.x.unwrap_or(vec![CF2::<C>::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<CF2<C>> = Boolean::le_bits_to_fp_var(&r_bits)?; + let points_coords: Vec<FpVar<CF2<C>>> = [ + 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 // For the cmE we're checking: U_i1.cmE == U_i.cmE + r * cmT + r^2 * u_i.cmE, where u_i.cmE // 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 @@

#[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::<Fq>::new_ref(); - let cfW_u_i_x = [ + let cfW_u_i_x: Vec<Fq> = [ + vec![r_Fq], get_cm_coordinates(&ci1.cmW), get_cm_coordinates(&ci2.cmW), get_cm_coordinates(&ci3.cmW),

@@ -411,13 +418,13 @@ x: Some(cfW_u_i_x.clone()),

}; cfW_circuit.generate_constraints(cs.clone()).unwrap(); assert!(cs.is_satisfied().unwrap()); - dbg!(cs.num_constraints()); // same for E: let cs = ConstraintSystem::<Fq>::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();
M folding-schemes/src/folding/nova/decider_eth.rs → 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 @@ for Decider<C1, GC1, C2, GC2, FC, CS1, CS2, S, FS>

where C1: CurveGroup, C2: CurveGroup, - GC1: CurveVar<C1, CF2<C1>>, + GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>, GC2: CurveVar<C2, CF2<C2>>, FC: FCircuit<C1::ScalarField>, CS1: CommitmentScheme<

@@ -82,11 +78,7 @@ for<'b> &'b GC2: GroupOpsBounds<'b, C2, GC2>,

// constrain FS into Nova, since this is a Decider specifically for Nova Nova<C1, GC1, C2, GC2, FC, CS1, CS2>: From<FS>, { - type ProverParam = ( - PoseidonConfig<C1::ScalarField>, - S::ProvingKey, - CS1::ProverParams, - ); + type ProverParam = (S::ProvingKey, CS1::ProverParams); type Proof = Proof<C1, CS1, S>; type VerifierParam = (S::VerifyingKey, CS1::VerifierParams); type PublicInput = Vec<C1::ScalarField>;

@@ -98,11 +90,7 @@ pp: Self::ProverParam,

mut rng: impl RngCore + CryptoRng, folding_scheme: FS, ) -> Result<Self::Proof, Error> { - let (poseidon_config, snark_pk, cs_pk): ( - PoseidonConfig<C1::ScalarField>, - S::ProvingKey, - CS1::ProverParams, - ) = pp; + let (snark_pk, cs_pk): (S::ProvingKey, CS1::ProverParams) = pp; let circuit = DeciderEthCircuit::<C1, GC1, C2, GC2, CS1, CS2>::from_nova::<FC>( folding_scheme.into(),

@@ -111,35 +99,9 @@

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::<C1, CS1>::compute_cmT(&cs_pk, &circuit.r1cs, &w_i, &u_i, &W_i, &U_i)?; - let r_bits = ChallengeGadget::<C1>::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<C1>, CommittedInstance<C1>) = - NIFS::<C1, CS1>::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 @@ println!("Groth16 setup, {:?}", start.elapsed());

// 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());
M folding-schemes/src/folding/nova/decider_eth_circuit.rs → folding-schemes/src/folding/nova/decider_eth_circuit.rs

@@ -244,7 +244,7 @@ impl<C1, GC1, C2, GC2, CS1, CS2> DeciderEthCircuit<C1, GC1, C2, GC2, CS1, CS2>

where C1: CurveGroup, C2: CurveGroup, - GC1: CurveVar<C1, CF2<C1>>, + GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>, GC2: CurveVar<C2, CF2<C2>>, CS1: CommitmentScheme<C1>, // enforce that the CS2 is Pedersen commitment scheme, since we're at Ethereum's EVM decider

@@ -551,6 +551,7 @@ /// with the gadget.

pub struct KZGChallengesGadget<C: CurveGroup> { _c: PhantomData<C>, } +#[allow(clippy::type_complexity)] impl<C> KZGChallengesGadget<C> where C: CurveGroup,

@@ -580,7 +581,6 @@

Ok((challenge_W, challenge_E)) } // compatible with the native get_challenges_native - #[allow(clippy::type_complexity)] pub fn get_challenges_gadget( cs: ConstraintSystemRef<C::ScalarField>, poseidon_config: &PoseidonConfig<C::ScalarField>,

@@ -860,7 +860,6 @@

// 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
M folding-schemes/src/folding/nova/mod.rs → folding-schemes/src/folding/nova/mod.rs

@@ -6,7 +6,7 @@ sponge::{poseidon::PoseidonConfig, Absorb},

}; 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 @@ #[derive(Clone, Debug)]

pub struct Nova<C1, GC1, C2, GC2, FC, CS1, CS2> where C1: CurveGroup, - GC1: CurveVar<C1, CF2<C1>>, + GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>, C2: CurveGroup, GC2: CurveVar<C2, CF2<C2>>, FC: FCircuit<C1::ScalarField>,

@@ -201,7 +201,7 @@ impl<C1, GC1, C2, GC2, FC, CS1, CS2> FoldingScheme<C1, C2, FC>

for Nova<C1, GC1, C2, GC2, FC, CS1, CS2> where C1: CurveGroup, - GC1: CurveVar<C1, CF2<C1>>, + GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>, C2: CurveGroup, GC2: CurveVar<C2, CF2<C2>>, FC: FCircuit<C1::ScalarField>,

@@ -289,8 +289,6 @@

/// Implements IVC.P of Nova+CycleFold fn prove_step(&mut self) -> Result<(), Error> { let augmented_F_circuit: AugmentedFCircuit<C1, C2, GC2, FC>; - let cfW_circuit: CycleFoldCircuit<C1, GC1>; - let cfE_circuit: CycleFoldCircuit<C1, GC1>; if self.i > C1::ScalarField::from_le_bytes_mod_order(&std::usize::MAX.to_le_bytes()) { return Err(Error::MaxStep);

@@ -313,6 +311,8 @@ 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)) + .ok_or(Error::OutOfBounds)?; // fold Nova instances let (W_i1, U_i1): (Witness<C1>, CommittedInstance<C1>) = NIFS::<C1, CS1>::fold_instances(

@@ -340,6 +340,7 @@ z_i: Some(self.z_i.clone()),

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 @@ // CycleFold part:

// 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 @@ ]

.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::<C1, GC1> { + let cfW_circuit = CycleFoldCircuit::<C1, GC1> { _gc: PhantomData, r_bits: Some(r_bits.clone()), p1: Some(self.U_i.clone().cmW),

@@ -382,7 +385,7 @@ p2: Some(self.u_i.clone().cmW),

p3: Some(U_i1.clone().cmW), x: Some(cfW_u_i_x.clone()), }; - cfE_circuit = CycleFoldCircuit::<C1, GC1> { + let cfE_circuit = CycleFoldCircuit::<C1, GC1> { _gc: PhantomData, r_bits: Some(r_bits.clone()), p1: Some(self.U_i.clone().cmE),

@@ -413,6 +416,7 @@ z_i: Some(self.z_i.clone()),

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 @@

impl<C1, GC1, C2, GC2, FC, CS1, CS2> Nova<C1, GC1, C2, GC2, FC, CS1, CS2> where C1: CurveGroup, - GC1: CurveVar<C1, CF2<C1>>, + GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>, C2: CurveGroup, GC2: CurveVar<C2, CF2<C2>>, FC: FCircuit<C1::ScalarField>,

@@ -583,7 +587,7 @@

impl<C1, GC1, C2, GC2, FC, CS1, CS2> Nova<C1, GC1, C2, GC2, FC, CS1, CS2> where C1: CurveGroup, - GC1: CurveVar<C1, CF2<C1>>, + GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>, C2: CurveGroup, GC2: CurveVar<C2, CF2<C2>>, FC: FCircuit<C1::ScalarField>,

@@ -674,7 +678,7 @@ F_circuit: FC,

) -> Result<(R1CS<C1::ScalarField>, R1CS<C2::ScalarField>), Error> where C1: CurveGroup, - GC1: CurveVar<C1, CF2<C1>>, + GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>, C2: CurveGroup, GC2: CurveVar<C2, CF2<C2>>, FC: FCircuit<C1::ScalarField>,

@@ -702,7 +706,7 @@ F_circuit: FC,

) -> Result<(usize, usize), Error> where C1: CurveGroup, - GC1: CurveVar<C1, CF2<C1>>, + GC1: CurveVar<C1, CF2<C1>> + ToConstraintFieldGadget<CF2<C1>>, C2: CurveGroup, GC2: CurveVar<C2, CF2<C2>>, FC: FCircuit<C1::ScalarField>,

@@ -718,7 +722,8 @@ let (r1cs, cf_r1cs) = get_r1cs::<C1, GC1, C2, GC2, FC>(poseidon_config, F_circuit)?;

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<C: CurveGroup>(cm: &C) -> Vec<C::BaseField> { let zero = (&C::BaseField::zero(), &C::BaseField::one()); let cm = cm.into_affine();
M folding-schemes/src/utils/mle.rs → folding-schemes/src/utils/mle.rs

@@ -51,8 +51,6 @@ DenseMultilinearExtension::<F>::from_evaluations_vec(n_vars, v_padded)

} pub fn dense_vec_to_mle<F: PrimeField>(n_vars: usize, v: &Vec<F>) -> DenseMultilinearExtension<F> { - dbg!(n_vars); - dbg!(v.len()); // Pad to 2^n_vars let v_padded: Vec<F> = [ v.clone(),

@@ -88,7 +86,6 @@ vec![420, 4, 2, 0],

]); 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::<Fr>(vec![