diff --git a/folding-schemes/src/folding/circuits/cyclefold.rs b/folding-schemes/src/folding/circuits/cyclefold.rs index 27bb75e..36ba7d0 100644 --- a/folding-schemes/src/folding/circuits/cyclefold.rs +++ b/folding-schemes/src/folding/circuits/cyclefold.rs @@ -376,14 +376,12 @@ pub trait CycleFoldConfig { /// Public inputs length for the CycleFoldCircuit. /// * For Nova this is: `|[r, p1.x,y, p2.x,y, p3.x,y]|` - /// * In general, `|[r * (n_points-1), (p_i.x,y)*n_points, p_folded.x,y]|`. + /// * In general, `|[r, (p_i.x,y)*n_points, p_folded.x,y]|`. /// /// Thus, `IO_LEN` is: - /// `RANDOMNESS_BIT_LENGTH / FIELD_CAPACITY * (N_INPUT_POINTS - 1) + 2 * N_INPUT_POINTS + 2` + /// `RANDOMNESS_BIT_LENGTH / FIELD_CAPACITY + 2 * N_INPUT_POINTS + 2` const IO_LEN: usize = { - Self::RANDOMNESS_BIT_LENGTH.div_ceil(Self::FIELD_CAPACITY) * (Self::N_INPUT_POINTS - 1) - + 2 * Self::N_INPUT_POINTS - + 2 + Self::RANDOMNESS_BIT_LENGTH.div_ceil(Self::FIELD_CAPACITY) + 2 * Self::N_INPUT_POINTS + 2 }; type F: Field; @@ -396,10 +394,9 @@ pub trait CycleFoldConfig { #[derive(Debug, Clone)] pub struct CycleFoldCircuit> { pub _gc: PhantomData, - /// r_bits is a vector containing the r_bits, one for each point except for the first one. They - /// are used for the scalar multiplication of the points. The r_bits are the bit - /// representation of each power of r (in Fr, while the CycleFoldCircuit is in Fq). - pub r_bits: Option>>, + /// r_bits is the bit representation of the r whose powers are used in the + /// random-linear-combination inside the CycleFoldCircuit + pub r_bits: Option>, /// points to be folded in the CycleFoldCircuit pub points: Option>, /// public inputs (cf_u_{i+1}.x) @@ -426,18 +423,11 @@ where for<'a> &'a GC: GroupOpsBounds<'a, CFG::C, GC>, { fn generate_constraints(self, cs: ConstraintSystemRef) -> Result<(), SynthesisError> { - let r_bits: Vec>> = self - .r_bits - // n_points-1, bcs is one for each point except for the first one - .unwrap_or(vec![ - vec![false; CFG::RANDOMNESS_BIT_LENGTH]; - CFG::N_INPUT_POINTS - 1 - ]) - .iter() - .map(|r_bits_i| { - Vec::>::new_witness(cs.clone(), || Ok(r_bits_i.clone())) - }) - .collect::>()?; + let r_bits = Vec::>::new_witness(cs.clone(), || { + Ok(self + .r_bits + .unwrap_or(vec![false; CFG::RANDOMNESS_BIT_LENGTH])) + })?; let points = Vec::::new_witness(cs.clone(), || { Ok(self .points @@ -447,10 +437,7 @@ where #[cfg(test)] { assert_eq!(CFG::N_INPUT_POINTS, points.len()); - assert_eq!(CFG::N_INPUT_POINTS - 1, r_bits.len()); - for r_bits_i in &r_bits { - assert_eq!(r_bits_i.len(), CFG::RANDOMNESS_BIT_LENGTH); - } + assert_eq!(CFG::RANDOMNESS_BIT_LENGTH, r_bits.len()); } // Fold the original points of the instances natively in CycleFold. @@ -458,11 +445,13 @@ where // - for the cmW we're computing: U_i1.cmW = U_i.cmW + r * u_i.cmW // - for the cmE we're computing: 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 - let mut p_folded: GC = points[0].clone(); - // iter over n_points-1 because the first point is not multiplied by r^i (it is multiplied - // by r^0=1) - for i in 0..CFG::N_INPUT_POINTS - 1 { - p_folded += points[i + 1].scalar_mul_le(r_bits[i].iter())?; + // We want to compute + // P_folded = p_0 + r * P_1 + r^2 * P_2 + r^3 * P_3 + ... + r^{n-2} * P_{n-2} + r^{n-1} * P_{n-1} + // so in order to do it more efficiently (less constraints) we do + // P_folded = (((P_{n-1} * r + P_{n-2}) * r + P_{n-3})... ) * r + P_0 + let mut p_folded: GC = points[CFG::N_INPUT_POINTS - 1].clone(); + for i in (0..CFG::N_INPUT_POINTS - 1).rev() { + p_folded = p_folded.scalar_mul_le(r_bits.iter())? + points[i].clone(); } let x = Vec::>::new_input(cs.clone(), || { @@ -474,24 +463,23 @@ where // Check that the points coordinates are placed as the public input x: // In Nova, this is: x == [r, p1, p2, p3] (wheere p3 is the p_folded). // In multifolding schemes such as HyperNova, this is: - // computed_x = [r_0, r_1, r_2, ..., r_n, p_0, p_1, p_2, ..., p_n, p_folded], + // computed_x = [r, p_0, p_1, p_2, ..., p_n, p_folded], // where each p_i is in fact p_i.to_constraint_field() - let computed_x: Vec> = r_bits + let r_fp = Boolean::le_bits_to_fp_var(&r_bits)?; + let points_aux: Vec> = points .iter() - .map(|r_bits_i| { - r_bits_i - .chunks(CFG::FIELD_CAPACITY) - .map(Boolean::le_bits_to_fp_var) - .collect::, _>>() - }) - .chain( - points - .iter() - .chain(&[p_folded]) - .map(|p_i| Ok(p_i.to_constraint_field()?[..2].to_vec())), - ) - .collect::, _>>()? - .concat(); + .map(|p_i| Ok(p_i.to_constraint_field()?[..2].to_vec())) + .collect::, SynthesisError>>()? + .into_iter() + .flatten() + .collect(); + + let computed_x: Vec> = [ + vec![r_fp], + points_aux, + p_folded.to_constraint_field()?[..2].to_vec(), + ] + .concat(); computed_x.enforce_equal(&x)?; Ok(()) @@ -596,13 +584,13 @@ pub mod tests { use crate::transcript::poseidon::poseidon_canonical_config; use crate::utils::get_cm_coordinates; - struct TestCycleFoldConfig { + struct TestCycleFoldConfig { _c: PhantomData, } - impl CycleFoldConfig for TestCycleFoldConfig { + impl CycleFoldConfig for TestCycleFoldConfig { const RANDOMNESS_BIT_LENGTH: usize = NOVA_N_BITS_RO; - const N_INPUT_POINTS: usize = 2; + const N_INPUT_POINTS: usize = N; type C = C; type F = C::BaseField; } @@ -630,46 +618,45 @@ 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(); + fn test_CycleFoldCircuit_n_points_constraints() { + const n: usize = 16; + let mut rng = ark_std::test_rng(); + + // points to random-linear-combine + let points: Vec = std::iter::repeat_with(|| Projective::rand(&mut rng)) + .take(n) + .collect(); + + use std::ops::Mul; + let rho_raw = Fq::rand(&mut rng); + let rho_bits = rho_raw.into_bigint().to_bits_le()[..NOVA_N_BITS_RO].to_vec(); + let rho_Fq = Fq::from_bigint(BigInteger::from_bits_le(&rho_bits)).unwrap(); + let rho_Fr = Fr::from_bigint(BigInteger::from_bits_le(&rho_bits)).unwrap(); + let mut res = Projective::zero(); + use ark_std::One; + let mut rho_i = Fr::one(); + for point_i in points.iter() { + res += point_i.mul(rho_i); + rho_i *= rho_Fr; + } // 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: Vec = [ - vec![r_Fq], - get_cm_coordinates(&ci1.cmW), - get_cm_coordinates(&ci2.cmW), - get_cm_coordinates(&ci3.cmW), - ] - .concat(); - let cfW_circuit = CycleFoldCircuit::, GVar> { - _gc: PhantomData, - r_bits: Some(vec![r_bits.clone()]), - points: Some(vec![ci1.clone().cmW, ci2.clone().cmW]), - x: Some(cfW_u_i_x.clone()), - }; - cfW_circuit.generate_constraints(cs.clone()).unwrap(); - assert!(cs.is_satisfied().unwrap()); - - // same for E: - let cs = ConstraintSystem::::new_ref(); - let cfE_u_i_x = [ - vec![r_Fq], - get_cm_coordinates(&ci1.cmE), - get_cm_coordinates(&cmT), - get_cm_coordinates(&ci3.cmE), + let x: Vec = [ + vec![rho_Fq], + points.iter().flat_map(get_cm_coordinates).collect(), + get_cm_coordinates(&res), ] .concat(); - let cfE_circuit = CycleFoldCircuit::, GVar> { + let cf_circuit = CycleFoldCircuit::, GVar> { _gc: PhantomData, - r_bits: Some(vec![r_bits.clone()]), - points: Some(vec![ci1.clone().cmE, cmT]), - x: Some(cfE_u_i_x.clone()), + r_bits: Some(rho_bits), + points: Some(points), + x: Some(x.clone()), }; - cfE_circuit.generate_constraints(cs.clone()).unwrap(); + cf_circuit.generate_constraints(cs.clone()).unwrap(); assert!(cs.is_satisfied().unwrap()); } @@ -714,7 +701,7 @@ pub mod tests { u: Fr::zero(), cmW: Projective::rand(&mut rng), x: std::iter::repeat_with(|| Fr::rand(&mut rng)) - .take(TestCycleFoldConfig::::IO_LEN) + .take(TestCycleFoldConfig::::IO_LEN) .collect(), }; let U_i = CycleFoldCommittedInstance:: { @@ -722,7 +709,7 @@ pub mod tests { u: Fr::rand(&mut rng), cmW: Projective::rand(&mut rng), x: std::iter::repeat_with(|| Fr::rand(&mut rng)) - .take(TestCycleFoldConfig::::IO_LEN) + .take(TestCycleFoldConfig::::IO_LEN) .collect(), }; let cmT = Projective::rand(&mut rng); @@ -781,7 +768,7 @@ pub mod tests { u: Fr::rand(&mut rng), cmW: Projective::rand(&mut rng), x: std::iter::repeat_with(|| Fr::rand(&mut rng)) - .take(TestCycleFoldConfig::::IO_LEN) + .take(TestCycleFoldConfig::::IO_LEN) .collect(), }; let pp_hash = Fq::from(42u32); // only for test diff --git a/folding-schemes/src/folding/hypernova/circuits.rs b/folding-schemes/src/folding/hypernova/circuits.rs index 107a089..f4bb388 100644 --- a/folding-schemes/src/folding/hypernova/circuits.rs +++ b/folding-schemes/src/folding/hypernova/circuits.rs @@ -14,7 +14,7 @@ use ark_r1cs_std::{ fields::{fp::FpVar, FieldVar}, groups::GroupOpsBounds, prelude::CurveVar, - R1CSVar, ToBitsGadget, ToConstraintFieldGadget, + R1CSVar, ToConstraintFieldGadget, }; use ark_relations::r1cs::{ ConstraintSynthesizer, ConstraintSystem, ConstraintSystemRef, Namespace, SynthesisError, @@ -234,7 +234,7 @@ where new_instances: &[CCCSVar], // u proof: ProofVar, enabled: Boolean, - ) -> Result<(LCCCSVar, Vec>>>), SynthesisError> { + ) -> Result<(LCCCSVar, Vec>>), SynthesisError> { // absorb instances to transcript for U_i in running_instances { let v = [ @@ -317,15 +317,16 @@ where let rho_bits: Vec>> = transcript.get_challenge_nbits(NOVA_N_BITS_RO)?; let rho = Boolean::le_bits_to_fp_var(&rho_bits)?; - // Self::fold will return the folded instance, together with the rho's powers vector so - // they can be used in other parts of the AugmentedFCircuit - Self::fold( + // Self::fold will return the folded instance + let folded_lcccs = Self::fold( running_instances, new_instances, proof.sigmas_thetas, r_x_prime, rho, - ) + )?; + // return the rho_bits so it can be used in other parts of the AugmentedFCircuit + Ok((folded_lcccs, rho_bits)) } /// Runs (in-circuit) the verifier side of the fold, computing the new folded LCCCS instance @@ -336,14 +337,12 @@ where sigmas_thetas: (Vec>>>, Vec>>>), r_x_prime: Vec>>, rho: FpVar>, - ) -> Result<(LCCCSVar, Vec>>>), SynthesisError> { + ) -> Result, SynthesisError> { let (sigmas, thetas) = (sigmas_thetas.0.clone(), sigmas_thetas.1.clone()); let mut u_folded: FpVar> = FpVar::zero(); let mut x_folded: Vec>> = vec![FpVar::zero(); lcccs[0].x.len()]; let mut v_folded: Vec>> = vec![FpVar::zero(); sigmas[0].len()]; - let mut rho_vec: Vec>>> = - vec![vec![Boolean::FALSE; NOVA_N_BITS_RO]; lcccs.len() + cccs.len() - 1]; let mut rho_i = FpVar::one(); for i in 0..(lcccs.len() + cccs.len()) { let u: FpVar>; @@ -382,28 +381,18 @@ where // compute the next power of rho rho_i *= rho.clone(); - // crop the size of rho_i to NOVA_N_BITS_RO - let rho_i_bits = rho_i.to_bits_le()?; - rho_i = Boolean::le_bits_to_fp_var(&rho_i_bits[..NOVA_N_BITS_RO])?; - if i < lcccs.len() + cccs.len() - 1 { - // store the cropped rho_i into the rho_vec - rho_vec[i] = rho_i_bits[..NOVA_N_BITS_RO].to_vec(); - } } // return the folded instance, together with the rho's powers vector so they can be used in // other parts of the AugmentedFCircuit - Ok(( - LCCCSVar:: { - // C this is later overwritten by the U_{i+1}.C value checked by the cyclefold circuit - C: lcccs[0].C.clone(), - u: u_folded, - x: x_folded, - r_x: r_x_prime, - v: v_folded, - }, - rho_vec, - )) + Ok(LCCCSVar:: { + // C this is later overwritten by the U_{i+1}.C value checked by the cyclefold circuit + C: lcccs[0].C.clone(), + u: u_folded, + x: x_folded, + r_x: r_x_prime, + v: v_folded, + }) } } @@ -734,7 +723,7 @@ where Ok(self.Us.unwrap_or(vec![U_dummy.clone(); MU - 1])) })?; let us = Vec::>::new_witness(cs.clone(), || { - Ok(self.us.unwrap_or(vec![u_dummy.clone(); MU - 1])) + Ok(self.us.unwrap_or(vec![u_dummy.clone(); NU - 1])) })?; let U_i1_C = NonNativeAffineVar::new_witness(cs.clone(), || { Ok(self.U_i1_C.unwrap_or_else(C1::zero)) @@ -794,7 +783,7 @@ where // other curve. let mut transcript = PoseidonSpongeVar::new(cs.clone(), &self.poseidon_config); transcript.absorb(&pp_hash)?; - let (mut U_i1, rho_vec) = NIMFSGadget::::verify( + let (mut U_i1, rho_bits) = NIMFSGadget::::verify( cs.clone(), &self.ccs.clone(), &mut transcript, @@ -824,19 +813,14 @@ where x.enforce_equal(&is_basecase.select(&u_i1_x_base, &u_i1_x)?)?; // convert rho_bits of the rho_vec to a `NonNativeFieldVar` - let rho_vec_nonnat = rho_vec - .iter() - .map(|rho_i| { - let mut bits = rho_i.clone(); - bits.resize(C1::BaseField::MODULUS_BIT_SIZE as usize, Boolean::FALSE); - NonNativeUintVar::from(&bits) - }) - .collect(); + let mut rho_bits_resized = rho_bits.clone(); + rho_bits_resized.resize(C1::BaseField::MODULUS_BIT_SIZE as usize, Boolean::FALSE); + let rho_nonnat = NonNativeUintVar::from(&rho_bits_resized); // CycleFold part // C.1. Compute cf1_u_i.x and cf2_u_i.x let cf_x: Vec>> = [ - rho_vec_nonnat, + vec![rho_nonnat], all_Us .iter() .flat_map(|U| vec![U.C.x.clone(), U.C.y.clone()]) @@ -1312,17 +1296,16 @@ mod tests { let mut transcript_p: PoseidonSponge = PoseidonSponge::::new(&poseidon_config.clone()); transcript_p.absorb(&pp_hash); - let (rho_powers, nimfs_proof); - (nimfs_proof, U_i1, W_i1, rho_powers) = - NIMFS::>::prove( - &mut transcript_p, - &ccs, - &all_Us, - &all_us, - &all_Ws, - &all_ws, - ) - .unwrap(); + let (rho, nimfs_proof); + (nimfs_proof, U_i1, W_i1, rho) = NIMFS::>::prove( + &mut transcript_p, + &ccs, + &all_Us, + &all_us, + &all_Ws, + &all_ws, + ) + .unwrap(); // sanity check: check the folded instance relation U_i1.check_relation(&ccs, &W_i1).unwrap(); @@ -1330,23 +1313,13 @@ mod tests { let u_i1_x = U_i1.hash(&sponge, pp_hash, iFr + Fr::one(), z_0.clone(), z_i1.clone()); - let rho_powers_Fq: Vec = rho_powers - .iter() - .map(|rho_i| { - Fq::from_bigint(BigInteger::from_bits_le(&rho_i.into_bigint().to_bits_le())) - .unwrap() - }) - .collect(); - let rho_powers_bits: Vec> = rho_powers - .iter() - .map(|rho_i| rho_i.into_bigint().to_bits_le()[..NOVA_N_BITS_RO].to_vec()) - .collect(); + 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(); // CycleFold part: // get the vector used as public inputs 'x' in the CycleFold circuit let cf_u_i_x = [ - // all values for multiple instances - rho_powers_Fq, + vec![rho_Fq], get_cm_coordinates(&U_i.C), Us.iter() .flat_map(|Us_i| get_cm_coordinates(&Us_i.C)) @@ -1361,7 +1334,7 @@ mod tests { let cf_circuit = HyperNovaCycleFoldCircuit:: { _gc: PhantomData, - r_bits: Some(rho_powers_bits.clone()), + r_bits: Some(rho_bits.clone()), points: Some( [ vec![U_i.clone().C], @@ -1375,10 +1348,6 @@ mod tests { }; // ensure that the CycleFoldCircuit is well defined - assert_eq!( - cf_circuit.r_bits.clone().unwrap().len(), - HyperNovaCycleFoldConfig::::N_INPUT_POINTS - 1 - ); assert_eq!( cf_circuit.points.clone().unwrap().len(), HyperNovaCycleFoldConfig::::N_INPUT_POINTS diff --git a/folding-schemes/src/folding/hypernova/decider_eth_circuit.rs b/folding-schemes/src/folding/hypernova/decider_eth_circuit.rs index 93ba6bb..ac95cbb 100644 --- a/folding-schemes/src/folding/hypernova/decider_eth_circuit.rs +++ b/folding-schemes/src/folding/hypernova/decider_eth_circuit.rs @@ -168,9 +168,8 @@ where pub U_i1: Option>, pub W_i1: Option>, pub nimfs_proof: Option>, - // rho_0 is the first and only rho in the 'rho_powers' array, since it comes from NIMFS-folding - // only 2 instances. - pub rho_0: Option, + // rho is the 'random' value used for the fold of the last 2 instances + pub rho: Option, /// CycleFold running instance pub cf_U_i: Option>, pub cf_W_i: Option>, @@ -199,15 +198,14 @@ where // compute the U_{i+1}, W_{i+1}, by folding the last running & incoming instances let mut transcript = PoseidonSponge::::new(&hn.poseidon_config); transcript.absorb(&hn.pp_hash); - let (nimfs_proof, U_i1, W_i1, rho_powers) = - NIMFS::>::prove( - &mut transcript, - &hn.ccs, - &[hn.U_i.clone()], - &[hn.u_i.clone()], - &[hn.W_i.clone()], - &[hn.w_i.clone()], - )?; + let (nimfs_proof, U_i1, W_i1, rho) = NIMFS::>::prove( + &mut transcript, + &hn.ccs, + &[hn.U_i.clone()], + &[hn.u_i.clone()], + &[hn.W_i.clone()], + &[hn.w_i.clone()], + )?; // compute the KZG challenges used as inputs in the circuit let kzg_challenge = @@ -222,11 +220,6 @@ where let p_W = poly_from_vec(W.to_vec())?; let eval_W = p_W.evaluate(&kzg_challenge); - // ensure that we only have 1 element in rho_powers, since we only NIMFS-fold 2 instances - if rho_powers.len() != 1 { - return Err(Error::NotExpectedLength(rho_powers.len(), 1)); - } - Ok(Self { _c1: PhantomData, _gc1: PhantomData, @@ -251,7 +244,7 @@ where U_i1: Some(U_i1), W_i1: Some(W_i1), nimfs_proof: Some(nimfs_proof), - rho_0: Some(rho_powers[0]), + rho: Some(rho), cf_U_i: Some(hn.cf_U_i), cf_W_i: Some(hn.cf_W_i), kzg_challenge: Some(kzg_challenge), @@ -428,7 +421,7 @@ where // The following steps are in non-increasing order because the `computed_U_i1` is computed // at step 8, and later used at step 6. Notice that in Nova, we compute U_i1 outside of the // circuit, in the smart contract, but here we're computing it in-circuit, and we reuse the - // `rho_vec` computed along the way of computing `computed_U_i1` for the later `rho_powers` + // `rho_bits` computed along the way of computing `computed_U_i1` for the later `rho_powers` // check (6.b). // Check 7 is temporary disabled due @@ -445,7 +438,7 @@ where // Notice that the NIMFSGadget performs all the logic except of checking the fold of the // instances C parameter, which would require non-native arithmetic, henceforth we perform // that check outside the circuit. - let (computed_U_i1, rho_vec) = NIMFSGadget::::verify( + let (computed_U_i1, rho_bits) = NIMFSGadget::::verify( cs.clone(), &self.ccs.clone(), &mut transcript, @@ -471,20 +464,11 @@ where computed_U_i1.v.enforce_equal(&U_i1.v)?; // 8.b check that the in-circuit computed r is equal to the inputted r. - // Notice that rho_vec only contains one element, since at the final fold we are only - // folding 2-to-1 instances. - - // Ensure that rho_vec is of length 1, note that this is not enforced at the constraint - // level but more as a check for the prover. - if rho_vec.len() != 1 { - return Err(SynthesisError::Unsatisfiable); - } - let rho_0 = Boolean::le_bits_to_fp_var(&rho_vec[0])?; - let external_rho_0 = FpVar::>::new_input(cs.clone(), || { - Ok(self.rho_0.unwrap_or(CF1::::zero())) - })?; - rho_0.enforce_equal(&external_rho_0)?; + let rho = Boolean::le_bits_to_fp_var(&rho_bits)?; + let external_rho = + FpVar::>::new_input(cs.clone(), || Ok(self.rho.unwrap_or(CF1::::zero())))?; + rho.enforce_equal(&external_rho)?; Ok(()) } diff --git a/folding-schemes/src/folding/hypernova/mod.rs b/folding-schemes/src/folding/hypernova/mod.rs index 38d1ead..c2656a0 100644 --- a/folding-schemes/src/folding/hypernova/mod.rs +++ b/folding-schemes/src/folding/hypernova/mod.rs @@ -664,16 +664,15 @@ where let mut transcript_p: PoseidonSponge = PoseidonSponge::::new(&self.poseidon_config); transcript_p.absorb(&self.pp_hash); - let (rho_powers, nimfs_proof); - (nimfs_proof, U_i1, W_i1, rho_powers) = - NIMFS::>::prove( - &mut transcript_p, - &self.ccs, - &[vec![self.U_i.clone()], Us.clone()].concat(), - &[vec![self.u_i.clone()], us.clone()].concat(), - &[vec![self.W_i.clone()], Ws].concat(), - &[vec![self.w_i.clone()], ws].concat(), - )?; + let (rho, nimfs_proof); + (nimfs_proof, U_i1, W_i1, rho) = NIMFS::>::prove( + &mut transcript_p, + &self.ccs, + &[vec![self.U_i.clone()], Us.clone()].concat(), + &[vec![self.u_i.clone()], us.clone()].concat(), + &[vec![self.W_i.clone()], Ws].concat(), + &[vec![self.w_i.clone()], ws].concat(), + )?; // sanity check: check the folded instance relation #[cfg(test)] @@ -687,29 +686,18 @@ where z_i1.clone(), ); - let rho_powers_Fq: Vec = rho_powers - .iter() - .map(|rho_i| { - C1::BaseField::from_bigint(BigInteger::from_bits_le( - &rho_i.into_bigint().to_bits_le(), - )) - .unwrap() - }) - .collect(); - let rho_powers_bits: Vec> = rho_powers - .iter() - .map(|rho_i| rho_i.into_bigint().to_bits_le()[..NOVA_N_BITS_RO].to_vec()) - .collect(); + let rho_bits = rho.into_bigint().to_bits_le()[..NOVA_N_BITS_RO].to_vec(); + let rho_Fq = C1::BaseField::from_bigint(BigInteger::from_bits_le(&rho_bits)).unwrap(); // CycleFold part: // get the vector used as public inputs 'x' in the CycleFold circuit. // Place the random values and the points coordinates as the public input x: // In Nova, this is: x == [r, p1, p2, p3]. // In multifolding schemes such as HyperNova, this is: - // computed_x = [r_0, r_1, r_2, ..., r_n, p_0, p_1, p_2, ..., p_n], + // computed_x = [r, p_0, p_1, p_2, ..., p_n], // where each p_i is in fact p_i.to_constraint_field() let cf_u_i_x = [ - rho_powers_Fq, + vec![rho_Fq], get_cm_coordinates(&self.U_i.C), Us.iter() .flat_map(|Us_i| get_cm_coordinates(&Us_i.C)) @@ -724,7 +712,7 @@ where let cf_circuit = HyperNovaCycleFoldCircuit:: { _gc: PhantomData, - r_bits: Some(rho_powers_bits.clone()), + r_bits: Some(rho_bits.clone()), points: Some( [ vec![self.U_i.clone().C], @@ -991,7 +979,6 @@ mod tests { cccs.push((u, w)); } - dbg!(&hypernova.i); hypernova .prove_step(&mut rng, vec![], Some((lcccs, cccs))) .unwrap(); diff --git a/folding-schemes/src/folding/hypernova/nimfs.rs b/folding-schemes/src/folding/hypernova/nimfs.rs index 37eb647..2a70b28 100644 --- a/folding-schemes/src/folding/hypernova/nimfs.rs +++ b/folding-schemes/src/folding/hypernova/nimfs.rs @@ -73,7 +73,7 @@ where sigmas_thetas: &SigmasThetas, r_x_prime: Vec, rho: C::ScalarField, - ) -> (LCCCS, Vec) { + ) -> LCCCS { let (sigmas, thetas) = (sigmas_thetas.0.clone(), sigmas_thetas.1.clone()); let mut C_folded = C::zero(); let mut u_folded = C::ScalarField::zero(); @@ -81,7 +81,6 @@ where let mut v_folded: Vec = vec![C::ScalarField::zero(); sigmas[0].len()]; let mut rho_i = C::ScalarField::one(); - let mut rho_powers = vec![C::ScalarField::zero(); lcccs.len() + cccs.len() - 1]; for i in 0..(lcccs.len() + cccs.len()) { let c: C; let u: C::ScalarField; @@ -123,28 +122,15 @@ where // compute the next power of rho rho_i *= rho; - // crop the size of rho_i to NOVA_N_BITS_RO - let rho_i_bits = rho_i.into_bigint().to_bits_le(); - rho_i = C::ScalarField::from_bigint(BigInteger::from_bits_le( - &rho_i_bits[..NOVA_N_BITS_RO], - )) - .unwrap(); - if i < lcccs.len() + cccs.len() - 1 { - // store the cropped rho_i into the rho_powers vector - rho_powers[i] = rho_i; - } } - ( - LCCCS:: { - C: C_folded, - u: u_folded, - x: x_folded, - r_x: r_x_prime, - v: v_folded, - }, - rho_powers, - ) + LCCCS:: { + C: C_folded, + u: u_folded, + x: x_folded, + r_x: r_x_prime, + v: v_folded, + } } pub fn fold_witness( @@ -183,12 +169,6 @@ where // compute the next power of rho rho_i *= rho; - // crop the size of rho_i to NOVA_N_BITS_RO - let rho_i_bits = rho_i.into_bigint().to_bits_le(); - rho_i = C::ScalarField::from_bigint(BigInteger::from_bits_le( - &rho_i_bits[..NOVA_N_BITS_RO], - )) - .unwrap(); } Witness { w: w_folded, @@ -213,8 +193,7 @@ where NIMFSProof, LCCCS, Witness, - // Vec, - Vec, + C::ScalarField, // rho ), Error, > { @@ -281,7 +260,7 @@ where C::ScalarField::from_bigint(BigInteger::from_bits_le(&rho_bits)).unwrap(); // Step 7: Create the folded instance - let (folded_lcccs, rho_powers) = Self::fold( + let folded_lcccs = Self::fold( running_instances, new_instances, &sigmas_thetas, @@ -299,7 +278,7 @@ where }, folded_lcccs, folded_witness, - rho_powers, + rho, )) } @@ -406,8 +385,7 @@ where &proof.sigmas_thetas, r_x_prime, rho, - ) - .0) + )) } } @@ -457,7 +435,7 @@ pub mod tests { let mut rng = test_rng(); let rho = Fr::rand(&mut rng); - let (folded, _) = NIMFS::>::fold( + let folded = NIMFS::>::fold( &[lcccs], &[cccs], &sigmas_thetas, diff --git a/folding-schemes/src/folding/nova/mod.rs b/folding-schemes/src/folding/nova/mod.rs index 961a534..f0c07b1 100644 --- a/folding-schemes/src/folding/nova/mod.rs +++ b/folding-schemes/src/folding/nova/mod.rs @@ -608,13 +608,13 @@ where let cfW_circuit = NovaCycleFoldCircuit:: { _gc: PhantomData, - r_bits: Some(vec![r_bits.clone()]), + r_bits: Some(r_bits.clone()), points: Some(vec![self.U_i.clone().cmW, self.u_i.clone().cmW]), x: Some(cfW_u_i_x.clone()), }; let cfE_circuit = NovaCycleFoldCircuit:: { _gc: PhantomData, - r_bits: Some(vec![r_bits.clone()]), + r_bits: Some(r_bits.clone()), points: Some(vec![self.U_i.clone().cmE, cmT]), x: Some(cfE_u_i_x.clone()), };