From 4dcb981dd4d2644d3671c4e81080b87e750caa79 Mon Sep 17 00:00:00 2001 From: winderica Date: Fri, 12 Apr 2024 14:01:27 +0100 Subject: [PATCH] Reduce the number of constraints in `AugmentedFCircuit` for Nova (#86) * Reduce the number of constraints in `AugmentedFCircuit` For the test `folding::nova::tests::test_ivc` Before: 138240 After: 86756 (1.6x improvement) Two notable optimization techniques: 1. Instead of allocating two witness variables `a, b` and enforce their equality by calling `a.conditional_enforce_equal(&b, &cond)`, we can avoid the allocation of `b` and directly set `b = a`. The former might be costly due to the checks in allocation and `conditional_enforce_equal`. See `nova/circuits.rs` for details. 2. Before this commit, `NonNativeFieldVar::to_constraint_field` was majorly called for generating the inputs (preimage) to hash functions. However, it turns out that the underlying conversion strategy (optimized for weight) is not optimal for reducing the length of hash preimage. We can go further by maximizing the number of bits per limb, thereby minimizing the preimage length. See `circuits/nonnative.rs` for details. * Format * Fix clippy warnings * Move the comments to the right position * Cleanup unnecessary code --- .../src/folding/circuits/nonnative.rs | 149 +++++++- folding-schemes/src/folding/nova/circuits.rs | 346 +++++++++--------- folding-schemes/src/folding/nova/cyclefold.rs | 86 +++-- .../src/folding/nova/decider_eth.rs | 12 +- .../src/folding/nova/decider_eth_circuit.rs | 18 +- folding-schemes/src/folding/nova/mod.rs | 57 ++- folding-schemes/src/folding/nova/nifs.rs | 3 +- 7 files changed, 378 insertions(+), 293 deletions(-) diff --git a/folding-schemes/src/folding/circuits/nonnative.rs b/folding-schemes/src/folding/circuits/nonnative.rs index 943a243..67ca365 100644 --- a/folding-schemes/src/folding/circuits/nonnative.rs +++ b/folding-schemes/src/folding/circuits/nonnative.rs @@ -2,16 +2,54 @@ use ark_ec::{AffineRepr, CurveGroup}; use ark_ff::{BigInteger, PrimeField}; use ark_r1cs_std::{ alloc::{AllocVar, AllocationMode}, + boolean::Boolean, fields::{ fp::FpVar, - nonnative::{params::OptimizationType, AllocatedNonNativeFieldVar, NonNativeFieldVar}, + nonnative::{ + params::{get_params, OptimizationType}, + AllocatedNonNativeFieldVar, NonNativeFieldVar, + }, FieldVar, }, - ToBitsGadget, + ToBitsGadget, ToConstraintFieldGadget, }; -use ark_relations::r1cs::{Namespace, SynthesisError}; +use ark_relations::r1cs::{ConstraintSystemRef, Namespace, OptimizationGoal, SynthesisError}; use ark_std::Zero; use core::borrow::Borrow; +use std::marker::PhantomData; + +/// Compose a vector boolean into a `NonNativeFieldVar` +pub fn nonnative_field_var_from_le_bits( + cs: ConstraintSystemRef, + bits: &[Boolean], +) -> Result, SynthesisError> { + let params = get_params( + TargetField::MODULUS_BIT_SIZE as usize, + BaseField::MODULUS_BIT_SIZE as usize, + match cs.optimization_goal() { + OptimizationGoal::None => OptimizationType::Constraints, + OptimizationGoal::Constraints => OptimizationType::Constraints, + OptimizationGoal::Weight => OptimizationType::Weight, + }, + ); + + // push the lower limbs first + let mut limbs = bits + .chunks(params.bits_per_limb) + .map(Boolean::le_bits_to_fp_var) + .collect::, _>>()?; + limbs.resize(params.num_limbs, FpVar::zero()); + limbs.reverse(); + + Ok(AllocatedNonNativeFieldVar { + cs, + limbs, + num_of_additions_over_normal_form: BaseField::one(), + is_in_the_normal_form: false, + target_phantom: PhantomData, + } + .into()) +} /// A more efficient version of `NonNativeFieldVar::to_constraint_field` pub fn nonnative_field_var_to_constraint_field( @@ -112,23 +150,63 @@ where } } -/// Wrapper on top of [`point_to_nonnative_limbs_custom_opt`] which always uses -/// [`OptimizationType::Weight`]. +impl ToConstraintFieldGadget for NonNativeAffineVar +where + ::BaseField: ark_ff::PrimeField, +{ + // A more efficient version of `point_to_nonnative_limbs_custom_opt`. + // Used for converting `NonNativeAffineVar` to a vector of `FpVar` with minimum length in + // the circuit. + fn to_constraint_field(&self) -> Result>, SynthesisError> { + let x = nonnative_field_var_to_constraint_field(&self.x)?; + let y = nonnative_field_var_to_constraint_field(&self.y)?; + Ok([x, y].concat()) + } +} + +/// The out-circuit counterpart of `NonNativeAffineVar::to_constraint_field` #[allow(clippy::type_complexity)] -pub fn point_to_nonnative_limbs( +pub fn nonnative_affine_to_field_elements( p: C, ) -> Result<(Vec, Vec), SynthesisError> where ::BaseField: ark_ff::PrimeField, { - point_to_nonnative_limbs_custom_opt(p, OptimizationType::Weight) + let affine = p.into_affine(); + if affine.is_zero() { + let x = nonnative_field_to_field_elements(&C::BaseField::zero()); + let y = nonnative_field_to_field_elements(&C::BaseField::zero()); + return Ok((x, y)); + } + + let (x, y) = affine.xy().unwrap(); + let x = nonnative_field_to_field_elements(x); + let y = nonnative_field_to_field_elements(y); + Ok((x, y)) +} + +impl NonNativeAffineVar +where + ::BaseField: ark_ff::PrimeField, +{ + // A wrapper of `point_to_nonnative_limbs_custom_opt` with constraints-focused optimization + // type (which is the default optimization type for arkworks' Groth16). + // Used for extracting a list of field elements of type `C::ScalarField` from the public input + // `p`, in exactly the same way as how `NonNativeAffineVar` is represented as limbs of type + // `FpVar` in-circuit. + #[allow(clippy::type_complexity)] + pub fn inputize(p: C) -> Result<(Vec, Vec), SynthesisError> { + point_to_nonnative_limbs_custom_opt(p, OptimizationType::Constraints) + } } -/// Used to compute (outside the circuit) the limbs representation of a point that matches the one -/// used in-circuit, and in particular this method allows to specify which [`OptimizationType`] to -/// use. +// Used to compute (outside the circuit) the limbs representation of a point. +// For `OptimizationType::Constraints`, the result matches the one used in-circuit. +// For `OptimizationType::Weight`, the result vector is more dense and is suitable for hashing. +// It is possible to further optimize the length of the result vector (see +// `nonnative_affine_to_field_elements`) #[allow(clippy::type_complexity)] -pub fn point_to_nonnative_limbs_custom_opt( +fn point_to_nonnative_limbs_custom_opt( p: C, optimization_type: OptimizationType, ) -> Result<(Vec, Vec), SynthesisError> @@ -166,24 +244,63 @@ where mod tests { use super::*; use ark_pallas::{Fr, Projective}; - use ark_r1cs_std::{alloc::AllocVar, R1CSVar, ToConstraintFieldGadget}; + use ark_r1cs_std::R1CSVar; use ark_relations::r1cs::ConstraintSystem; - use ark_std::{UniformRand, Zero}; + use ark_std::UniformRand; #[test] - fn test_alloc_nonnativeaffinevar() { + fn test_alloc_zero() { let cs = ConstraintSystem::::new_ref(); // dealing with the 'zero' point should not panic when doing the unwrap let p = Projective::zero(); - NonNativeAffineVar::::new_witness(cs.clone(), || Ok(p)).unwrap(); + assert!(NonNativeAffineVar::::new_witness(cs.clone(), || Ok(p)).is_ok()); + } + + #[test] + fn test_arkworks_to_constraint_field() { + let cs = ConstraintSystem::::new_ref(); // check that point_to_nonnative_limbs returns the expected values let mut rng = ark_std::test_rng(); let p = Projective::rand(&mut rng); let pVar = NonNativeAffineVar::::new_witness(cs.clone(), || Ok(p)).unwrap(); - let (x, y) = point_to_nonnative_limbs(p).unwrap(); + let (x, y) = point_to_nonnative_limbs_custom_opt(p, OptimizationType::Weight).unwrap(); assert_eq!(pVar.x.to_constraint_field().unwrap().value().unwrap(), x); assert_eq!(pVar.y.to_constraint_field().unwrap().value().unwrap(), y); } + + #[test] + fn test_improved_to_constraint_field() { + let cs = ConstraintSystem::::new_ref(); + + // check that point_to_nonnative_limbs returns the expected values + let mut rng = ark_std::test_rng(); + let p = Projective::rand(&mut rng); + let pVar = NonNativeAffineVar::::new_witness(cs.clone(), || Ok(p)).unwrap(); + let (x, y) = nonnative_affine_to_field_elements(p).unwrap(); + assert_eq!( + pVar.to_constraint_field().unwrap().value().unwrap(), + [x, y].concat() + ); + } + + #[test] + fn test_inputize() { + let cs = ConstraintSystem::::new_ref(); + + // check that point_to_nonnative_limbs returns the expected values + let mut rng = ark_std::test_rng(); + let p = Projective::rand(&mut rng); + let pVar = NonNativeAffineVar::::new_witness(cs.clone(), || Ok(p)).unwrap(); + let (x, y) = NonNativeAffineVar::inputize(p).unwrap(); + + match (pVar.x, pVar.y) { + (NonNativeFieldVar::Var(p_x), NonNativeFieldVar::Var(p_y)) => { + assert_eq!(p_x.limbs.value().unwrap(), x); + assert_eq!(p_y.limbs.value().unwrap(), y); + } + _ => unreachable!(), + } + } } diff --git a/folding-schemes/src/folding/nova/circuits.rs b/folding-schemes/src/folding/nova/circuits.rs index 3c35532..b80093f 100644 --- a/folding-schemes/src/folding/nova/circuits.rs +++ b/folding-schemes/src/folding/nova/circuits.rs @@ -17,11 +17,10 @@ use ark_r1cs_std::{ fields::{fp::FpVar, nonnative::NonNativeFieldVar, FieldVar}, groups::GroupOpsBounds, prelude::CurveVar, - ToBitsGadget, ToConstraintFieldGadget, + R1CSVar, ToConstraintFieldGadget, }; use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, Namespace, SynthesisError}; -use ark_std::fmt::Debug; -use ark_std::Zero; +use ark_std::{fmt::Debug, Zero}; use core::{borrow::Borrow, marker::PhantomData}; use super::{ @@ -30,9 +29,9 @@ use super::{ }, CommittedInstance, }; -use crate::constants::N_BITS_RO; -use crate::folding::circuits::nonnative::{point_to_nonnative_limbs, NonNativeAffineVar}; +use crate::folding::circuits::nonnative::{nonnative_affine_to_field_elements, NonNativeAffineVar}; use crate::frontend::FCircuit; +use crate::{constants::N_BITS_RO, folding::circuits::nonnative::nonnative_field_var_from_le_bits}; /// CF1 represents the ConstraintField used for the main Nova circuit which is over E1::Fr, where /// E1 is the main curve where we do the folding. @@ -106,10 +105,8 @@ where let U_vec = [ vec![self.u], self.x, - self.cmE.x.to_constraint_field()?, - self.cmE.y.to_constraint_field()?, - self.cmW.x.to_constraint_field()?, - self.cmW.y.to_constraint_field()?, + self.cmE.to_constraint_field()?, + self.cmW.to_constraint_field()?, ] .concat(); let input = [vec![i], z_0, z_i, U_vec.clone()].concat(); @@ -132,6 +129,26 @@ where C: CurveGroup, ::BaseField: ark_ff::PrimeField, { + pub fn fold_committed_instance( + r: FpVar>, + ci1: CommittedInstanceVar, // U_i + ci2: CommittedInstanceVar, // u_i + ) -> Result, SynthesisError> { + Ok(CommittedInstanceVar { + cmE: NonNativeAffineVar::new_constant(ConstraintSystemRef::None, C::zero())?, + cmW: NonNativeAffineVar::new_constant(ConstraintSystemRef::None, C::zero())?, + // ci3.u = ci1.u + r * ci2.u + u: ci1.u + &r * ci2.u, + // ci3.x = ci1.x + r * ci2.x + x: ci1 + .x + .iter() + .zip(ci2.x) + .map(|(a, b)| a + &r * &b) + .collect::>>>(), + }) + } + /// Implements the constraints for NIFS.V for u and x, since cm(E) and cm(W) are delegated to /// the CycleFold circuit. pub fn verify( @@ -139,20 +156,13 @@ where ci1: CommittedInstanceVar, // U_i ci2: CommittedInstanceVar, // u_i ci3: CommittedInstanceVar, // U_{i+1} - ) -> Result>, SynthesisError> { - // ensure that: ci3.u == ci1.u + r * ci2.u - let first_check = ci3.u.is_eq(&(ci1.u + r.clone() * ci2.u))?; - - // ensure that: ci3.x == ci1.x + r * ci2.x - let x_rlc = ci1 - .x - .iter() - .zip(ci2.x) - .map(|(a, b)| a + &r * &b) - .collect::>>>(); - let second_check = x_rlc.is_eq(&ci3.x)?; - - first_check.and(&second_check) + ) -> Result<(), SynthesisError> { + let ci = Self::fold_committed_instance(r, ci1, ci2)?; + + ci.u.enforce_equal(&ci3.u)?; + ci.x.enforce_equal(&ci3.x)?; + + Ok(()) } } @@ -173,11 +183,11 @@ where u_i: CommittedInstance, cmT: C, ) -> Result, SynthesisError> { - let (U_cmE_x, U_cmE_y) = point_to_nonnative_limbs::(U_i.cmE)?; - let (U_cmW_x, U_cmW_y) = point_to_nonnative_limbs::(U_i.cmW)?; - let (u_cmE_x, u_cmE_y) = point_to_nonnative_limbs::(u_i.cmE)?; - let (u_cmW_x, u_cmW_y) = point_to_nonnative_limbs::(u_i.cmW)?; - let (cmT_x, cmT_y) = point_to_nonnative_limbs::(cmT)?; + let (U_cmE_x, U_cmE_y) = nonnative_affine_to_field_elements::(U_i.cmE)?; + let (U_cmW_x, U_cmW_y) = nonnative_affine_to_field_elements::(U_i.cmW)?; + let (u_cmE_x, u_cmE_y) = nonnative_affine_to_field_elements::(u_i.cmE)?; + let (u_cmW_x, u_cmW_y) = nonnative_affine_to_field_elements::(u_i.cmW)?; + let (cmT_x, cmT_y) = nonnative_affine_to_field_elements::(cmT)?; let mut sponge = PoseidonSponge::::new(poseidon_config); let input = vec![ @@ -212,16 +222,13 @@ where ) -> Result>, SynthesisError> { let mut sponge = PoseidonSpongeVar::::new(cs, poseidon_config); - let input: Vec> = vec![ + let input: Vec> = [ U_i_vec, vec![u_i.u.clone()], u_i.x.clone(), - u_i.cmE.x.to_constraint_field()?, - u_i.cmE.y.to_constraint_field()?, - u_i.cmW.x.to_constraint_field()?, - u_i.cmW.y.to_constraint_field()?, - cmT.x.to_constraint_field()?, - cmT.y.to_constraint_field()?, + u_i.cmE.to_constraint_field()?, + u_i.cmW.to_constraint_field()?, + cmT.to_constraint_field()?, ] .concat(); sponge.absorb(&input)?; @@ -248,10 +255,10 @@ pub struct AugmentedFCircuit< pub i_usize: Option, pub z_0: Option>, pub z_i: Option>, - pub u_i: Option>, + pub u_i_cmW: Option, pub U_i: Option>, - pub U_i1: Option>, - pub r_nonnat: Option>, + pub U_i1_cmE: Option, + pub U_i1_cmW: Option, pub cmT: Option, pub F: FC, // F circuit pub x: Option>, // public input (u_{i+1}.x[0]) @@ -259,15 +266,11 @@ pub struct AugmentedFCircuit< // cyclefold verifier on C1 // Here 'cf1, cf2' are for each of the CycleFold circuits, corresponding to the fold of cmW and // cmE respectively - pub cf1_u_i: Option>, // input - pub cf2_u_i: Option>, // input - pub cf_U_i: Option>, // input - pub cf1_U_i1: Option>, // intermediate - pub cf_U_i1: Option>, // output + pub cf1_u_i_cmW: Option, // input + pub cf2_u_i_cmW: Option, // input + pub cf_U_i: Option>, // input pub cf1_cmT: Option, pub cf2_cmT: Option, - pub cf1_r_nonnat: Option, - pub cf2_r_nonnat: Option, pub cf_x: Option>, // public input (u_{i+1}.x[1]) } @@ -284,23 +287,19 @@ where i_usize: None, z_0: None, z_i: None, - u_i: None, + u_i_cmW: None, U_i: None, - U_i1: None, - r_nonnat: None, + U_i1_cmE: None, + U_i1_cmW: None, cmT: None, F: F_circuit, x: None, // cyclefold values - cf1_u_i: None, - cf2_u_i: None, + cf1_u_i_cmW: None, + cf2_u_i_cmW: None, cf_U_i: None, - cf1_U_i1: None, - cf_U_i1: None, cf1_cmT: None, cf2_cmT: None, - cf1_r_nonnat: None, - cf2_r_nonnat: None, cf_x: None, } } @@ -334,24 +333,26 @@ where .unwrap_or(vec![CF1::::zero(); self.F.state_len()])) })?; - let u_dummy_native = CommittedInstance::::dummy(2); - let u_i = CommittedInstanceVar::::new_witness(cs.clone(), || { - Ok(self.u_i.unwrap_or(u_dummy_native.clone())) - })?; + let u_dummy = CommittedInstance::dummy(2); let U_i = CommittedInstanceVar::::new_witness(cs.clone(), || { - Ok(self.U_i.unwrap_or(u_dummy_native.clone())) + Ok(self.U_i.unwrap_or(u_dummy.clone())) })?; - let U_i1 = CommittedInstanceVar::::new_witness(cs.clone(), || { - Ok(self.U_i1.unwrap_or(u_dummy_native.clone())) + let U_i1_cmE = NonNativeAffineVar::new_witness(cs.clone(), || { + Ok(self.U_i1_cmE.unwrap_or_else(C1::zero)) })?; - let r_nonnat = - NonNativeFieldVar::::new_witness(cs.clone(), || { - Ok(self.r_nonnat.unwrap_or_else(CF2::::zero)) - })?; + let U_i1_cmW = NonNativeAffineVar::new_witness(cs.clone(), || { + Ok(self.U_i1_cmW.unwrap_or_else(C1::zero)) + })?; + let cmT = NonNativeAffineVar::new_witness(cs.clone(), || Ok(self.cmT.unwrap_or_else(C1::zero)))?; - let x = - FpVar::>::new_input(cs.clone(), || Ok(self.x.unwrap_or_else(CF1::::zero)))?; + + let cf_u_dummy = CommittedInstance::dummy(CF_IO_LEN); + let cf_U_i = CycleFoldCommittedInstanceVar::::new_witness(cs.clone(), || { + Ok(self.cf_U_i.unwrap_or(cf_u_dummy.clone())) + })?; + let cf1_cmT = GC2::new_witness(cs.clone(), || Ok(self.cf1_cmT.unwrap_or_else(C2::zero)))?; + let cf2_cmT = GC2::new_witness(cs.clone(), || Ok(self.cf2_cmT.unwrap_or_else(C2::zero)))?; let crh_params = CRHParametersVar::::new_constant( cs.clone(), @@ -364,22 +365,33 @@ where .F .generate_step_constraints(cs.clone(), i_usize, z_i.clone())?; - let zero = FpVar::>::new_constant(cs.clone(), CF1::::zero())?; - let is_not_basecase = i.is_neq(&zero)?; + let is_basecase = i.is_zero()?; - // 1.a u_i.x[0] == H(i, z_0, z_i, U_i) + // 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(&crh_params, i.clone(), z_0.clone(), z_i.clone())?; - // check that h == u_i.x[0] - (u_i.x[0]).conditional_enforce_equal(&u_i_x, &is_not_basecase)?; + // u_i.x[1] = H(cf_U_i) + let (cf_u_i_x, cf_U_i_vec) = cf_U_i.clone().hash(&crh_params)?; + + // P.2. Construct u_i + let u_i = CommittedInstanceVar { + // u_i.cmE = cm(0) + cmE: NonNativeAffineVar::new_constant(cs.clone(), C1::zero())?, + // u_i.u = 1 + u: FpVar::one(), + // u_i.cmW is provided by the prover as witness + cmW: NonNativeAffineVar::new_witness(cs.clone(), || { + Ok(self.u_i_cmW.unwrap_or(C1::zero())) + })?, + // u_i.x is computed in step 1 + x: vec![u_i_x, cf_u_i_x], + }; - // 2. u_i.cmE==cm(0), u_i.u==1 - (u_i.cmE.x.is_zero()?).conditional_enforce_equal(&Boolean::TRUE, &is_not_basecase)?; - (u_i.cmE.y.is_zero()?).conditional_enforce_equal(&Boolean::TRUE, &is_not_basecase)?; - (u_i.u.is_one()?).conditional_enforce_equal(&Boolean::TRUE, &is_not_basecase)?; + // P.3. nifs.verify, obtains U_{i+1} by folding u_i & U_i . - // 3. nifs.verify, checks that folding u_i & U_i obtains U_{i+1}. // compute r = H(u_i, U_i, cmT) let r_bits = ChallengeGadget::::get_challenge_gadget( cs.clone(), @@ -389,60 +401,38 @@ where 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> = - 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())?; - nifs_check.conditional_enforce_equal(&Boolean::TRUE, &is_not_basecase)?; - - // 4.a u_{i+1}.x[0] = H(i+1, z_0, z_i+1, U_{i+1}), this is the first output of F' + // Also convert r_bits to a `NonNativeFieldVar` + let r_nonnat = nonnative_field_var_from_le_bits(cs.clone(), &r_bits)?; + + // Notice that NIFSGadget::fold_committed_instance does not fold cmE & cmW. + // We set `U_i1.cmE` and `U_i1.cmW` to unconstrained witnesses `U_i1_cmE` and `U_i1_cmW` + // respectively. + // The correctness of them will be checked on the other curve. + let mut U_i1 = NIFSGadget::::fold_committed_instance(r, U_i.clone(), u_i.clone())?; + U_i1.cmE = U_i1_cmE; + U_i1.cmW = U_i1_cmW; + + // P.4.a compute and check the first output of F' + // Base case: u_{i+1}.x[0] == H((i+1, z_0, z_{i+1}, U_{\bot}) + // Non-base case: u_{i+1}.x[0] == H((i+1, z_0, z_{i+1}, U_{i+1}) let (u_i1_x, _) = U_i1.clone().hash( &crh_params, i + FpVar::>::one(), z_0.clone(), z_i1.clone(), )?; - - u_i1_x.enforce_equal(&x)?; + let (u_i1_x_base, _) = CommittedInstanceVar::new_constant(cs.clone(), u_dummy)?.hash( + &crh_params, + FpVar::>::one(), + z_0.clone(), + z_i1.clone(), + )?; + 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)?)?; // CycleFold part - let cf_u_dummy_native = CommittedInstance::::dummy(CF_IO_LEN); - // cf W circuit data - let cf1_u_i = CycleFoldCommittedInstanceVar::::new_witness(cs.clone(), || { - Ok(self.cf1_u_i.unwrap_or_else(|| cf_u_dummy_native.clone())) - })?; - let cf2_u_i = CycleFoldCommittedInstanceVar::::new_witness(cs.clone(), || { - Ok(self.cf2_u_i.unwrap_or_else(|| cf_u_dummy_native.clone())) - })?; - let cf_U_i = CycleFoldCommittedInstanceVar::::new_witness(cs.clone(), || { - Ok(self.cf_U_i.unwrap_or_else(|| cf_u_dummy_native.clone())) - })?; - let cf1_U_i1 = CycleFoldCommittedInstanceVar::::new_witness(cs.clone(), || { - Ok(self.cf1_U_i1.unwrap_or_else(|| cf_u_dummy_native.clone())) - })?; - let cf_U_i1 = CycleFoldCommittedInstanceVar::::new_witness(cs.clone(), || { - Ok(self.cf_U_i1.unwrap_or_else(|| cf_u_dummy_native.clone())) - })?; - let cf1_cmT = GC2::new_witness(cs.clone(), || Ok(self.cf1_cmT.unwrap_or_else(C2::zero)))?; - let cf2_cmT = GC2::new_witness(cs.clone(), || Ok(self.cf2_cmT.unwrap_or_else(C2::zero)))?; - let cf1_r_nonnat = - NonNativeFieldVar::::new_witness(cs.clone(), || { - Ok(self.cf1_r_nonnat.unwrap_or_else(C2::ScalarField::zero)) - })?; - let cf2_r_nonnat = - NonNativeFieldVar::::new_witness(cs.clone(), || { - Ok(self.cf2_r_nonnat.unwrap_or_else(C2::ScalarField::zero)) - })?; - let cf_x = FpVar::>::new_input(cs.clone(), || { - Ok(self.cf_x.unwrap_or_else(C1::ScalarField::zero)) - })?; - - let cfW_x: Vec> = vec![ + // C.1. Compute cf1_u_i.x and cf2_u_i.x + let cfW_x = vec![ r_nonnat.clone(), U_i.cmW.x, U_i.cmW.y, @@ -451,78 +441,86 @@ where U_i1.cmW.x, U_i1.cmW.y, ]; - let cfE_x: Vec> = vec![ + let cfE_x = vec![ r_nonnat, U_i.cmE.x, U_i.cmE.y, cmT.x, cmT.y, U_i1.cmE.x, U_i1.cmE.y, ]; - // 1.b u_i.x[1] == H(cf_U_i) - let (cf_u_i_x, _) = cf_U_i.clone().hash(&crh_params)?; - // check that h == u_i.x[1] - (u_i.x[1]).conditional_enforce_equal(&cf_u_i_x, &is_not_basecase)?; - - // 4.b u_{i+1}.x[1] = H(cf_U_{i+1}), this is the second output of F' - let (cf_u_i1_x, _) = cf_U_i1.clone().hash(&crh_params)?; - cf_u_i1_x.enforce_equal(&cf_x)?; - // ensure that cf1_u & cf2_u have as public inputs the cmW & cmE from main instances U_i, // u_i, U_i+1 coordinates of the commitments - cf1_u_i - .x - .conditional_enforce_equal(&cfW_x, &is_not_basecase)?; - cf2_u_i - .x - .conditional_enforce_equal(&cfE_x, &is_not_basecase)?; + // C.2. Construct `cf1_u_i` and `cf2_u_i` + let cf1_u_i = CycleFoldCommittedInstanceVar { + // cf1_u_i.cmE = 0 + cmE: GC2::zero(), + // cf1_u_i.u = 1 + u: NonNativeFieldVar::one(), + // cf1_u_i.cmW is provided by the prover as witness + cmW: GC2::new_witness(cs.clone(), || Ok(self.cf1_u_i_cmW.unwrap_or(C2::zero())))?, + // cf1_u_i.x is computed in step 1 + x: cfW_x, + }; + let cf2_u_i = CycleFoldCommittedInstanceVar { + // cf2_u_i.cmE = 0 + cmE: GC2::zero(), + // cf2_u_i.u = 1 + u: NonNativeFieldVar::one(), + // cf2_u_i.cmW is provided by the prover as witness + cmW: GC2::new_witness(cs.clone(), || Ok(self.cf2_u_i_cmW.unwrap_or(C2::zero())))?, + // cf2_u_i.x is computed in step 1 + x: cfE_x, + }; + // C.3. nifs.verify, obtains cf1_U_{i+1} by folding cf1_u_i & cf_U_i, and then cf_U_{i+1} + // by folding cf2_u_i & cf1_U_{i+1}. + + // compute cf1_r = H(cf1_u_i, cf_U_i, cf1_cmT) // cf_r_bits is denoted by rho* in the paper. - // assert that cf_r_bits == cf_r_nonnat converted to bits. cf_r_nonnat is just an auxiliary - // value used to compute RLC of NonNativeFieldVar values, since we can convert - // NonNativeFieldVar into Vec, but not in the other direction. let cf1_r_bits = CycleFoldChallengeGadget::::get_challenge_gadget( cs.clone(), &self.poseidon_config, - cf_U_i.clone(), + cf_U_i_vec, cf1_u_i.clone(), cf1_cmT.clone(), )?; - let cf1_r_nonnat_bits = cf1_r_nonnat.to_bits_le()?; - cf1_r_bits.conditional_enforce_equal(&cf1_r_nonnat_bits[..N_BITS_RO], &is_not_basecase)?; + // Convert cf1_r_bits to a `NonNativeFieldVar` + let cf1_r_nonnat = nonnative_field_var_from_le_bits(cs.clone(), &cf1_r_bits)?; + // Fold cf1_u_i & cf_U_i into cf1_U_{i+1} + let cf1_U_i1 = NIFSFullGadget::::fold_committed_instance( + cf1_r_bits, + cf1_r_nonnat, + cf1_cmT, + cf_U_i, + cf1_u_i, + )?; + // same for cf2_r: let cf2_r_bits = CycleFoldChallengeGadget::::get_challenge_gadget( cs.clone(), &self.poseidon_config, - cf1_U_i1.clone(), + cf1_U_i1.to_constraint_field()?, cf2_u_i.clone(), cf2_cmT.clone(), )?; - let cf2_r_nonnat_bits = cf2_r_nonnat.to_bits_le()?; - cf2_r_bits.conditional_enforce_equal(&cf2_r_nonnat_bits[..N_BITS_RO], &is_not_basecase)?; - - // check cf_u_i.cmE=0, cf_u_i.u=1 - (cf1_u_i.cmE.is_zero()?).conditional_enforce_equal(&Boolean::TRUE, &is_not_basecase)?; - (cf1_u_i.u.is_one()?).conditional_enforce_equal(&Boolean::TRUE, &is_not_basecase)?; - (cf2_u_i.cmE.is_zero()?).conditional_enforce_equal(&Boolean::TRUE, &is_not_basecase)?; - (cf2_u_i.u.is_one()?).conditional_enforce_equal(&Boolean::TRUE, &is_not_basecase)?; - - // check the fold of all the parameters of the CycleFold instances, where the elliptic - // curve points relations are checked natively in Curve1 circuit (this one) - let v1 = NIFSFullGadget::::verify( - cf1_r_bits, - cf1_r_nonnat, - cf1_cmT, - cf_U_i, - cf1_u_i, - cf1_U_i1.clone(), - )?; - v1.conditional_enforce_equal(&Boolean::TRUE, &is_not_basecase)?; - let v2 = NIFSFullGadget::::verify( + let cf2_r_nonnat = nonnative_field_var_from_le_bits(cs.clone(), &cf2_r_bits)?; + let cf_U_i1 = NIFSFullGadget::::fold_committed_instance( cf2_r_bits, cf2_r_nonnat, cf2_cmT, cf1_U_i1, // the output from NIFS.V(cf1_r, cf_U, cfE_u) cf2_u_i, - cf_U_i1, )?; - v2.conditional_enforce_equal(&Boolean::TRUE, &is_not_basecase)?; + + // Back to Primary Part + // P.4.b compute and check the second output of F' + // Base case: u_{i+1}.x[1] == H(cf_U_{\bot}) + // Non-base case: u_{i+1}.x[1] == H(cf_U_{i+1}) + let (cf_u_i1_x, _) = cf_U_i1.clone().hash(&crh_params)?; + let (cf_u_i1_x_base, _) = + CycleFoldCommittedInstanceVar::new_constant(cs.clone(), cf_u_dummy)? + .hash(&crh_params)?; + let cf_x = FpVar::new_input(cs.clone(), || { + Ok(self.cf_x.unwrap_or(cf_u_i1_x_base.value()?)) + })?; + cf_x.enforce_equal(&is_basecase.select(&cf_u_i1_x_base, &cf_u_i1_x)?)?; Ok(()) } @@ -533,7 +531,6 @@ pub mod tests { use super::*; use ark_bn254::{Fr, G1Projective as Projective}; use ark_ff::BigInteger; - use ark_r1cs_std::R1CSVar; use ark_relations::r1cs::ConstraintSystem; use ark_std::UniformRand; @@ -583,14 +580,13 @@ pub mod tests { CommittedInstanceVar::::new_witness(cs.clone(), || Ok(ci3.clone())) .unwrap(); - let nifs_check = NIFSGadget::::verify( + NIFSGadget::::verify( rVar.clone(), ci1Var.clone(), ci2Var.clone(), ci3Var.clone(), ) .unwrap(); - nifs_check.enforce_equal(&Boolean::::TRUE).unwrap(); assert!(cs.is_satisfied().unwrap()); } @@ -675,10 +671,8 @@ pub mod tests { let U_iVar_vec = [ vec![U_iVar.u.clone()], U_iVar.x.clone(), - U_iVar.cmE.x.to_constraint_field().unwrap(), - U_iVar.cmE.y.to_constraint_field().unwrap(), - U_iVar.cmW.x.to_constraint_field().unwrap(), - U_iVar.cmW.y.to_constraint_field().unwrap(), + U_iVar.cmE.to_constraint_field().unwrap(), + U_iVar.cmW.to_constraint_field().unwrap(), ] .concat(); let r_bitsVar = ChallengeGadget::::get_challenge_gadget( diff --git a/folding-schemes/src/folding/nova/cyclefold.rs b/folding-schemes/src/folding/nova/cyclefold.rs index e04e234..7bc87bb 100644 --- a/folding-schemes/src/folding/nova/cyclefold.rs +++ b/folding-schemes/src/folding/nova/cyclefold.rs @@ -42,7 +42,6 @@ pub struct CycleFoldCommittedInstanceVar>> where for<'a> &'a GC: GroupOpsBounds<'a, C, GC>, { - _c: PhantomData, pub cmE: GC, pub u: NonNativeFieldVar>, pub cmW: GC, @@ -76,13 +75,7 @@ where mode, )?; - Ok(Self { - _c: PhantomData, - cmE, - u, - cmW, - x, - }) + Ok(Self { cmE, u, cmW, x }) }) } } @@ -189,6 +182,7 @@ pub struct NIFSFullGadget>> { _c: PhantomData, _gc: PhantomData, } + impl>> NIFSFullGadget where C: CurveGroup, @@ -196,42 +190,46 @@ where ::BaseField: ark_ff::PrimeField, for<'a> &'a GC: GroupOpsBounds<'a, C, GC>, { + pub fn fold_committed_instance( + // assumes that r_bits is equal to r_nonnat just that in a different format + r_bits: Vec>>, + r_nonnat: NonNativeFieldVar>, + cmT: GC, + ci1: CycleFoldCommittedInstanceVar, + // ci2 is assumed to be always with cmE=0, u=1 (checks done previous to this method) + ci2: CycleFoldCommittedInstanceVar, + ) -> Result, SynthesisError> { + Ok(CycleFoldCommittedInstanceVar { + cmE: cmT.scalar_mul_le(r_bits.iter())? + ci1.cmE, + cmW: ci1.cmW + ci2.cmW.scalar_mul_le(r_bits.iter())?, + u: ci1.u + r_nonnat.clone(), + x: ci1 + .x + .iter() + .zip(ci2.x) + .map(|(a, b)| a + &r_nonnat * &b) + .collect::>(), + }) + } + pub fn verify( // assumes that r_bits is equal to r_nonnat just that in a different format r_bits: Vec>>, r_nonnat: NonNativeFieldVar>, cmT: GC, - // ci1 is assumed to be always with cmE=0, u=1 (checks done previous to this method) ci1: CycleFoldCommittedInstanceVar, + // ci2 is assumed to be always with cmE=0, u=1 (checks done previous to this method) ci2: CycleFoldCommittedInstanceVar, ci3: CycleFoldCommittedInstanceVar, - ) -> Result>, SynthesisError> { - // cm(E) check: ci3.cmE == ci1.cmE + r * cmT (ci2.cmE=0) - let first_check = ci3 - .cmE - .is_eq(&(cmT.scalar_mul_le(r_bits.iter())? + ci1.cmE))?; - - // cm(W) check: ci3.cmW == ci1.cmW + r * ci2.cmW - let second_check = ci3 - .cmW - .is_eq(&(ci1.cmW + ci2.cmW.scalar_mul_le(r_bits.iter())?))?; - - let u_rlc: NonNativeFieldVar> = ci1.u + r_nonnat.clone(); - let third_check = u_rlc.is_eq(&ci3.u)?; - - // ensure that: ci3.x == ci1.x + r * ci2.x - let x_rlc: Vec>> = ci1 - .x - .iter() - .zip(ci2.x) - .map(|(a, b)| a + &r_nonnat * &b) - .collect::>>>(); - let fourth_check = x_rlc.is_eq(&ci3.x)?; - - first_check - .and(&second_check)? - .and(&third_check)? - .and(&fourth_check) + ) -> Result<(), SynthesisError> { + let ci = Self::fold_committed_instance(r_bits, r_nonnat, cmT, ci1, ci2)?; + + ci.cmE.enforce_equal(&ci3.cmE)?; + ci.u.enforce_equal(&ci3.u)?; + ci.cmW.enforce_equal(&ci3.cmW)?; + ci.x.enforce_equal(&ci3.x)?; + + Ok(()) } } @@ -285,25 +283,24 @@ where pub fn get_challenge_gadget( cs: ConstraintSystemRef, poseidon_config: &PoseidonConfig, - U_i: CycleFoldCommittedInstanceVar, + mut U_i_vec: Vec>, u_i: CycleFoldCommittedInstanceVar, cmT: GC, ) -> Result>, SynthesisError> { let mut sponge = PoseidonSpongeVar::::new(cs, poseidon_config); - let mut U_vec = U_i.to_constraint_field()?; - let mut u_vec = u_i.to_constraint_field()?; + let mut u_i_vec = u_i.to_constraint_field()?; let mut cmT_vec = cmT.to_constraint_field()?; - let U_cm_is_inf = U_vec.pop().unwrap(); - let u_cm_is_inf = u_vec.pop().unwrap(); + let U_cm_is_inf = U_i_vec.pop().unwrap(); + let u_cm_is_inf = u_i_vec.pop().unwrap(); let cmT_is_inf = cmT_vec.pop().unwrap(); // Concatenate `U_i.cmE_is_inf`, `U_i.cmW_is_inf`, `u_i.cmE_is_inf`, `u_i.cmW_is_inf`, `cmT_is_inf` // to save constraints for sponge.squeeze_bits let is_inf = U_cm_is_inf * CF2::::from(8u8) + u_cm_is_inf.double()? + cmT_is_inf; - let input = [U_vec, u_vec, cmT_vec, vec![is_inf]].concat(); + let input = [U_i_vec, u_i_vec, cmT_vec, vec![is_inf]].concat(); sponge.absorb(&input)?; let bits = sponge.squeeze_bits(N_BITS_RO)?; Ok(bits) @@ -486,7 +483,7 @@ pub mod tests { .unwrap(); let cmTVar = GVar::new_witness(cs.clone(), || Ok(cmT)).unwrap(); - let nifs_check = NIFSFullGadget::::verify( + NIFSFullGadget::::verify( r_bitsVar, r_nonnatVar, cmTVar, @@ -495,7 +492,6 @@ pub mod tests { ci3Var, ) .unwrap(); - nifs_check.enforce_equal(&Boolean::::TRUE).unwrap(); assert!(cs.is_satisfied().unwrap()); } @@ -547,7 +543,7 @@ pub mod tests { let r_bitsVar = CycleFoldChallengeGadget::::get_challenge_gadget( cs.clone(), &poseidon_config, - U_iVar, + U_iVar.to_constraint_field().unwrap(), u_iVar, cmTVar, ) diff --git a/folding-schemes/src/folding/nova/decider_eth.rs b/folding-schemes/src/folding/nova/decider_eth.rs index 5f8cb00..1ae68c5 100644 --- a/folding-schemes/src/folding/nova/decider_eth.rs +++ b/folding-schemes/src/folding/nova/decider_eth.rs @@ -2,7 +2,6 @@ use ark_crypto_primitives::sponge::Absorb; use ark_ec::{CurveGroup, Group}; use ark_ff::PrimeField; -use ark_r1cs_std::fields::nonnative::params::OptimizationType; use ark_r1cs_std::{groups::GroupOpsBounds, prelude::CurveVar, ToConstraintFieldGadget}; use ark_snark::SNARK; use ark_std::rand::{CryptoRng, RngCore}; @@ -14,7 +13,7 @@ use super::{circuits::CF2, nifs::NIFS, CommittedInstance, Nova}; use crate::commitment::{ kzg::Proof as KZGProof, pedersen::Params as PedersenParams, CommitmentScheme, }; -use crate::folding::circuits::nonnative::point_to_nonnative_limbs_custom_opt; +use crate::folding::circuits::nonnative::NonNativeAffineVar; use crate::frontend::FCircuit; use crate::Error; use crate::{Decider as DeciderTrait, FoldingScheme}; @@ -151,12 +150,9 @@ where // compute U = U_{d+1}= NIFS.V(U_d, u_d, cmT) let U = NIFS::::verify(proof.r, running_instance, incoming_instance, &proof.cmT); - let (cmE_x, cmE_y) = - point_to_nonnative_limbs_custom_opt::(U.cmE, OptimizationType::Constraints)?; - let (cmW_x, cmW_y) = - point_to_nonnative_limbs_custom_opt::(U.cmW, OptimizationType::Constraints)?; - let (cmT_x, cmT_y) = - point_to_nonnative_limbs_custom_opt::(proof.cmT, OptimizationType::Constraints)?; + let (cmE_x, cmE_y) = NonNativeAffineVar::inputize(U.cmE)?; + let (cmW_x, cmW_y) = NonNativeAffineVar::inputize(U.cmW)?; + let (cmT_x, cmT_y) = NonNativeAffineVar::inputize(proof.cmT)?; let public_input: Vec = vec![ vec![i], diff --git a/folding-schemes/src/folding/nova/decider_eth_circuit.rs b/folding-schemes/src/folding/nova/decider_eth_circuit.rs index 0a00e05..335eb28 100644 --- a/folding-schemes/src/folding/nova/decider_eth_circuit.rs +++ b/folding-schemes/src/folding/nova/decider_eth_circuit.rs @@ -22,7 +22,7 @@ use core::{borrow::Borrow, marker::PhantomData}; use super::{circuits::ChallengeGadget, nifs::NIFS}; use crate::ccs::r1cs::R1CS; use crate::commitment::{pedersen::Params as PedersenParams, CommitmentScheme}; -use crate::folding::circuits::nonnative::{point_to_nonnative_limbs, NonNativeAffineVar}; +use crate::folding::circuits::nonnative::{nonnative_affine_to_field_elements, NonNativeAffineVar}; use crate::folding::nova::{ circuits::{CommittedInstanceVar, CF1, CF2}, CommittedInstance, Nova, Witness, @@ -560,10 +560,8 @@ where poseidon_config: &PoseidonConfig, U_i: CommittedInstance, ) -> Result<(C::ScalarField, C::ScalarField), Error> { - let (cmE_x_limbs, cmE_y_limbs): (Vec, Vec) = - point_to_nonnative_limbs::(U_i.cmE)?; - let (cmW_x_limbs, cmW_y_limbs): (Vec, Vec) = - point_to_nonnative_limbs::(U_i.cmW)?; + let (cmE_x_limbs, cmE_y_limbs) = nonnative_affine_to_field_elements(U_i.cmE)?; + let (cmW_x_limbs, cmW_y_limbs) = nonnative_affine_to_field_elements(U_i.cmW)?; let transcript = &mut PoseidonTranscript::::new(poseidon_config); // compute the KZG challenges, which are computed in-circuit and checked that it matches @@ -586,16 +584,10 @@ where let mut transcript = PoseidonTranscriptVar::>::new(cs.clone(), &poseidon_config.clone()); - let cmW_x_limbs = U_i.cmW.x.to_constraint_field()?; - let cmW_y_limbs = U_i.cmW.y.to_constraint_field()?; - transcript.absorb_vec(&cmW_x_limbs)?; - transcript.absorb_vec(&cmW_y_limbs)?; + transcript.absorb_vec(&U_i.cmW.to_constraint_field()?[..])?; let challenge_W = transcript.get_challenge()?; - let cmE_x_limbs = U_i.cmE.x.to_constraint_field()?; - let cmE_y_limbs = U_i.cmE.y.to_constraint_field()?; - transcript.absorb_vec(&cmE_x_limbs)?; - transcript.absorb_vec(&cmE_y_limbs)?; + transcript.absorb_vec(&U_i.cmE.to_constraint_field()?[..])?; let challenge_E = transcript.get_challenge()?; Ok((challenge_W, challenge_E)) diff --git a/folding-schemes/src/folding/nova/mod.rs b/folding-schemes/src/folding/nova/mod.rs index dab6632..b6cc2d7 100644 --- a/folding-schemes/src/folding/nova/mod.rs +++ b/folding-schemes/src/folding/nova/mod.rs @@ -16,7 +16,7 @@ use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystem}; use crate::ccs::r1cs::{extract_r1cs, extract_w_x, R1CS}; use crate::commitment::CommitmentScheme; use crate::folding::circuits::nonnative::{ - nonnative_field_to_field_elements, point_to_nonnative_limbs, + nonnative_affine_to_field_elements, nonnative_field_to_field_elements, }; use crate::frontend::FCircuit; use crate::utils::vec::is_zero_vec; @@ -73,8 +73,8 @@ where z_0: Vec, z_i: Vec, ) -> Result { - let (cmE_x, cmE_y) = point_to_nonnative_limbs::(self.cmE)?; - let (cmW_x, cmW_y) = point_to_nonnative_limbs::(self.cmW)?; + let (cmE_x, cmE_y) = nonnative_affine_to_field_elements::(self.cmE)?; + let (cmW_x, cmW_y) = nonnative_affine_to_field_elements::(self.cmW)?; CRH::::evaluate( poseidon_config, @@ -342,7 +342,7 @@ where fn prove_step(&mut self) -> Result<(), Error> { let augmented_F_circuit: AugmentedFCircuit; - if self.i > C1::ScalarField::from_le_bytes_mod_order(&std::usize::MAX.to_le_bytes()) { + if self.i > C1::ScalarField::from_le_bytes_mod_order(&usize::MAX.to_le_bytes()) { return Err(Error::MaxStep); } let mut i_bytes: [u8; 8] = [0; 8]; @@ -392,22 +392,18 @@ where i_usize: Some(0), z_0: Some(self.z_0.clone()), // = z_i z_i: Some(self.z_i.clone()), - u_i: Some(self.u_i.clone()), // = dummy + u_i_cmW: Some(self.u_i.cmW), // = dummy U_i: Some(self.U_i.clone()), // = dummy - U_i1: Some(U_i1.clone()), - r_nonnat: Some(r_Fq), + U_i1_cmE: Some(U_i1.cmE), + U_i1_cmW: Some(U_i1.cmW), cmT: Some(cmT), F: self.F.clone(), x: Some(u_i1_x), - cf1_u_i: None, - cf2_u_i: None, + cf1_u_i_cmW: None, + cf2_u_i_cmW: None, cf_U_i: None, - cf1_U_i1: None, - cf_U_i1: None, cf1_cmT: None, cf2_cmT: None, - cf1_r_nonnat: None, - cf2_r_nonnat: None, cf_x: Some(cf_u_i1_x), }; @@ -451,15 +447,14 @@ where }; // fold self.cf_U_i + cfW_U -> folded running with cfW - let (_cfW_w_i, cfW_u_i, cfW_W_i1, cfW_U_i1, cfW_cmT, cfW_r1_Fq) = self - .fold_cyclefold_circuit( - self.cf_W_i.clone(), // CycleFold running instance witness - self.cf_U_i.clone(), // CycleFold running instance - cfW_u_i_x, - cfW_circuit, - )?; + let (_cfW_w_i, cfW_u_i, cfW_W_i1, cfW_U_i1, cfW_cmT, _) = self.fold_cyclefold_circuit( + self.cf_W_i.clone(), // CycleFold running instance witness + self.cf_U_i.clone(), // CycleFold running instance + cfW_u_i_x, + cfW_circuit, + )?; // fold [the output from folding self.cf_U_i + cfW_U] + cfE_U = folded_running_with_cfW + cfE - let (_cfE_w_i, cfE_u_i, cf_W_i1, cf_U_i1, cf_cmT, cf_r2_Fq) = + let (_cfE_w_i, cfE_u_i, cf_W_i1, cf_U_i1, cf_cmT, _) = self.fold_cyclefold_circuit(cfW_W_i1, cfW_U_i1.clone(), cfE_u_i_x, cfE_circuit)?; cf_u_i1_x = cf_U_i1.hash_cyclefold(&self.poseidon_config)?; @@ -471,23 +466,19 @@ where i_usize: Some(i_usize), z_0: Some(self.z_0.clone()), z_i: Some(self.z_i.clone()), - u_i: Some(self.u_i.clone()), + u_i_cmW: Some(self.u_i.cmW), U_i: Some(self.U_i.clone()), - U_i1: Some(U_i1.clone()), - r_nonnat: Some(r_Fq), + U_i1_cmE: Some(U_i1.cmE), + U_i1_cmW: Some(U_i1.cmW), cmT: Some(cmT), F: self.F.clone(), x: Some(u_i1_x), // cyclefold values - cf1_u_i: Some(cfW_u_i.clone()), - cf2_u_i: Some(cfE_u_i.clone()), + cf1_u_i_cmW: Some(cfW_u_i.cmW), + cf2_u_i_cmW: Some(cfE_u_i.cmW), cf_U_i: Some(self.cf_U_i.clone()), - cf1_U_i1: Some(cfW_U_i1.clone()), - cf_U_i1: Some(cf_U_i1.clone()), cf1_cmT: Some(cfW_cmT), cf2_cmT: Some(cf_cmT), - cf1_r_nonnat: Some(cfW_r1_Fq), - cf2_r_nonnat: Some(cf_r2_Fq), cf_x: Some(cf_u_i1_x), }; @@ -523,11 +514,11 @@ where // set values for next iteration self.i += C1::ScalarField::one(); - self.z_i = z_i1.clone(); + self.z_i = z_i1; self.w_i = Witness::::new(w_i1, self.r1cs.A.n_rows); self.u_i = self.w_i.commit::(&self.cs_params, x_i1)?; - self.W_i = W_i1.clone(); - self.U_i = U_i1.clone(); + self.W_i = W_i1; + self.U_i = U_i1; #[cfg(test)] { diff --git a/folding-schemes/src/folding/nova/nifs.rs b/folding-schemes/src/folding/nova/nifs.rs index d2bcc65..20d4f58 100644 --- a/folding-schemes/src/folding/nova/nifs.rs +++ b/folding-schemes/src/folding/nova/nifs.rs @@ -203,14 +203,13 @@ pub mod tests { use ark_crypto_primitives::sponge::poseidon::PoseidonConfig; use ark_ff::{BigInteger, PrimeField}; use ark_pallas::{Fr, Projective}; - use ark_std::{ops::Mul, UniformRand, Zero}; + use ark_std::{ops::Mul, UniformRand}; use crate::ccs::r1cs::tests::{get_test_r1cs, get_test_z}; use crate::commitment::pedersen::{Params as PedersenParams, Pedersen}; use crate::folding::nova::circuits::ChallengeGadget; use crate::folding::nova::traits::NovaR1CS; use crate::transcript::poseidon::{poseidon_test_config, PoseidonTranscript}; - use crate::utils::vec::vec_scalar_mul; #[allow(clippy::type_complexity)] pub(crate) fn prepare_simple_fold_inputs() -> (