From 27665b163e97903d5f5ac42ba9675787136fc839 Mon Sep 17 00:00:00 2001 From: arnaucube Date: Thu, 26 Oct 2023 12:42:08 +0200 Subject: [PATCH] Update cmE of E=0-vec to work as zero point Update cmE of E=0-vec to work as zero point instead of as cm(0-vec) --- src/folding/circuits/nonnative.rs | 46 +++++++++++++++++++++++++++++++ src/folding/nova/circuits.rs | 41 ++++++++++++++------------- src/folding/nova/mod.rs | 17 ++++++++++-- src/folding/nova/nifs.rs | 13 +++++++-- 4 files changed, 93 insertions(+), 24 deletions(-) diff --git a/src/folding/circuits/nonnative.rs b/src/folding/circuits/nonnative.rs index f5a37a90..08e15450 100644 --- a/src/folding/circuits/nonnative.rs +++ b/src/folding/circuits/nonnative.rs @@ -6,6 +6,7 @@ use ark_r1cs_std::{ fields::nonnative::NonNativeFieldVar, }; use ark_relations::r1cs::{Namespace, SynthesisError}; +use ark_std::{One, Zero}; use core::borrow::Borrow; /// NonNativeAffineVar represents an elliptic curve point in Affine represenation in the non-native @@ -31,6 +32,19 @@ where let cs = cs.into(); let affine = val.borrow().into_affine(); + if affine.is_zero() { + let x = NonNativeFieldVar::::new_variable( + cs.clone(), + || Ok(C::BaseField::zero()), + mode, + )?; + let y = NonNativeFieldVar::::new_variable( + cs.clone(), + || Ok(C::BaseField::one()), + mode, + )?; + return Ok(Self { x, y }); + } let xy = affine.xy().unwrap(); let x = NonNativeFieldVar::::new_variable( cs.clone(), @@ -58,6 +72,20 @@ where ::BaseField: ark_ff::PrimeField, { let affine = p.into_affine(); + if affine.is_zero() { + let x = + AllocatedNonNativeFieldVar::::get_limbs_representations( + &C::BaseField::zero(), + OptimizationType::Weight, + )?; + let y = + AllocatedNonNativeFieldVar::::get_limbs_representations( + &C::BaseField::one(), + OptimizationType::Weight, + )?; + return Ok((x, y)); + } + let (x, y) = affine.xy().unwrap(); let x = AllocatedNonNativeFieldVar::::get_limbs_representations( x, @@ -69,3 +97,21 @@ where )?; Ok((x, y)) } + +#[cfg(test)] +mod tests { + use super::*; + use ark_pallas::{Fq, Fr, Projective}; + use ark_r1cs_std::alloc::AllocVar; + use ark_relations::r1cs::ConstraintSystem; + use ark_std::Zero; + + #[test] + fn test_alloc_nonnativeaffinevar_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(); + } +} diff --git a/src/folding/nova/circuits.rs b/src/folding/nova/circuits.rs index 8fd591f0..aaf349dc 100644 --- a/src/folding/nova/circuits.rs +++ b/src/folding/nova/circuits.rs @@ -257,23 +257,26 @@ where let z_i = FpVar::>::new_witness(cs.clone(), || Ok(z_i_native))?; let z_i1 = FpVar::>::new_witness(cs.clone(), || Ok(z_i1_native))?; - let u_dummy = CommittedInstance { - cmE: C::generator(), + let u_dummy_native = CommittedInstance { + cmE: C::zero(), u: C::ScalarField::zero(), - cmW: C::generator(), + cmW: C::zero(), x: vec![CF1::::zero()], }; + let u_dummy = + CommittedInstanceVar::::new_witness(cs.clone(), || Ok(u_dummy_native.clone()))?; let u_i = CommittedInstanceVar::::new_witness(cs.clone(), || { - Ok(self.u_i.unwrap_or_else(|| u_dummy.clone())) + Ok(self.u_i.unwrap_or_else(|| u_dummy_native.clone())) })?; let U_i = CommittedInstanceVar::::new_witness(cs.clone(), || { - Ok(self.U_i.unwrap_or_else(|| u_dummy.clone())) + Ok(self.U_i.unwrap_or_else(|| u_dummy_native.clone())) })?; let U_i1 = CommittedInstanceVar::::new_witness(cs.clone(), || { - Ok(self.U_i1.unwrap_or_else(|| u_dummy.clone())) + Ok(self.U_i1.unwrap_or_else(|| u_dummy_native.clone())) })?; - // let cmT = NonNativeAffineVar::new_witness(cs.clone(), || Ok(self.cmT.unwrap()))?; // TODO TMP + let _cmT = + NonNativeAffineVar::new_witness(cs.clone(), || Ok(self.cmT.unwrap_or_else(C::zero)))?; let r = FpVar::>::new_witness(cs.clone(), || Ok(self.r.unwrap_or_else(CF1::::zero)))?; // r will come from higher level transcript let x = @@ -295,7 +298,10 @@ where (u_i.x[0]).conditional_enforce_equal(&u_i_x, &is_not_basecase)?; // 2. u_i.cmE==cm(0), u_i.u==1 - // (u_i.cmE.is_zero()?).enforce_equal(&Boolean::TRUE)?; // TODO not cmE=0, but check that cmE = cm(0) + (u_i.cmE.x.is_eq(&u_dummy.cmE.x)?) + .conditional_enforce_equal(&Boolean::TRUE, &is_not_basecase)?; + (u_i.cmE.y.is_eq(&u_dummy.cmE.y)?) + .conditional_enforce_equal(&Boolean::TRUE, &is_not_basecase)?; (u_i.u.is_one()?).conditional_enforce_equal(&Boolean::TRUE, &is_not_basecase)?; // 3. nifs.verify, checks that folding u_i & U_i obtains U_{i+1}. @@ -420,7 +426,7 @@ mod tests { let w2 = Witness::::new(w2.clone(), r1cs.A.n_rows); let mut rng = ark_std::test_rng(); - let pedersen_params = Pedersen::::new_params(&mut rng, r1cs.A.n_cols); + let pedersen_params = Pedersen::::new_params(&mut rng, r1cs.A.n_rows); // compute committed instances let ci1 = w1.commit(&pedersen_params, x1.clone()); @@ -435,6 +441,9 @@ mod tests { let (_w3, ci3, _T, cmT) = NIFS::::prove(&pedersen_params, r_Fr, &r1cs, &w1, &ci1, &w2, &ci2).unwrap(); + let ci3_verifier = NIFS::::verify(r_Fr, &ci1, &ci2, &cmT); + assert_eq!(ci3_verifier, ci3); + let cs = ConstraintSystem::::new_ref(); let rVar = FpVar::::new_witness(cs.clone(), || Ok(r_Fr)).unwrap(); @@ -570,19 +579,18 @@ mod tests { let mut tr = PoseidonTranscript::::new(&poseidon_config); - let pedersen_params = Pedersen::::new_params(&mut rng, r1cs.A.n_cols); + let pedersen_params = Pedersen::::new_params(&mut rng, r1cs.A.n_rows); // first step let z_0 = Fr::from(3_u32); - let mut z_i = z_0; + let z_i = z_0; let mut z_i1 = Fr::from(35_u32); // set the circuit to be folded with z_i=z_0=3 and z_{i+1}=35 (initial values) let mut test_F_circuit = TestFCircuit:: { z_i, z_i1 }; let w_dummy = Witness::::new(vec![Fr::zero(); F_witness_len], r1cs.A.n_rows); - let mut u_dummy = w_dummy.commit(&pedersen_params, vec![Fr::zero(); x.len()]); - u_dummy.u = Fr::zero(); + let u_dummy = CommittedInstance::::dummy(x.len()); // Wi is a 'dummy witness', all zeroes, but with the size corresponding to the R1CS that // we're working with. @@ -606,10 +614,6 @@ mod tests { let mut u_i1_x: Fr; let n_steps: usize = 4; for _ in 0..n_steps { - dbg!(&i); - dbg!(&z_i); - dbg!(&z_i1); - if i == Fr::zero() { // base case: i=0, z_i=z_0, U_i = U_d := dummy instance // u_1.x = H(1, z_0, z_i, U_i) @@ -684,7 +688,6 @@ mod tests { } assert!(is_satisfied); - println!("num_constraints={:?}", cs.num_constraints()); cs.finalize(); let cs = cs.into_inner().unwrap(); // notice that here we use 'Z' (uppercase) to denote the 'z-vector' as in the paper, @@ -705,7 +708,7 @@ mod tests { // set values for next iteration i += Fr::one(); test_F_circuit.step(); // advance the F circuit state - (z_i, z_i1) = test_F_circuit.public(); + (_, z_i1) = test_F_circuit.public(); U_i = U_i1.clone(); W_i = W_i1.clone(); } diff --git a/src/folding/nova/mod.rs b/src/folding/nova/mod.rs index b8806810..b196a699 100644 --- a/src/folding/nova/mod.rs +++ b/src/folding/nova/mod.rs @@ -10,6 +10,7 @@ use ark_std::{One, Zero}; use crate::ccs::r1cs::R1CS; use crate::folding::circuits::nonnative::point_to_nonnative_limbs; use crate::pedersen::{Params as PedersenParams, Pedersen}; +use crate::utils::vec::is_zero_vec; use crate::Error; pub mod circuits; @@ -28,6 +29,15 @@ where ::ScalarField: Absorb, ::BaseField: ark_ff::PrimeField, { + pub fn dummy(io_len: usize) -> Self { + Self { + cmE: C::zero(), + u: C::ScalarField::zero(), + cmW: C::zero(), + x: vec![C::ScalarField::zero(); io_len], + } + } + /// hash implements the committed instance hash compatible with the gadget implemented in /// nova/circuits.rs::CommittedInstanceVar.hash. /// Returns `H(i, z_0, z_i, U_i)`, where `i` can be `i` but also `i+1`, and `U` is the @@ -73,7 +83,7 @@ where pub fn new(w: Vec, e_len: usize) -> Self { Self { E: vec![C::ScalarField::zero(); e_len], - rE: C::ScalarField::one(), + rE: C::ScalarField::zero(), // because we use C::zero() as cmE W: w, rW: C::ScalarField::one(), } @@ -83,7 +93,10 @@ where params: &PedersenParams, x: Vec, ) -> CommittedInstance { - let cmE = Pedersen::commit(params, &self.E, &self.rE); + let mut cmE = C::zero(); + if !is_zero_vec::(&self.E) { + cmE = Pedersen::commit(params, &self.E, &self.rE); + } let cmW = Pedersen::commit(params, &self.W, &self.rW); CommittedInstance { cmE, diff --git a/src/folding/nova/nifs.rs b/src/folding/nova/nifs.rs index 560acd1c..e3d76091 100644 --- a/src/folding/nova/nifs.rs +++ b/src/folding/nova/nifs.rs @@ -183,11 +183,13 @@ where #[cfg(test)] pub mod tests { use super::*; - use crate::ccs::r1cs::tests::{get_test_r1cs, get_test_z}; - use crate::transcript::poseidon::{tests::poseidon_test_config, PoseidonTranscript}; use ark_ff::PrimeField; use ark_pallas::{Fr, Projective}; - use ark_std::{UniformRand, Zero}; + use ark_std::{ops::Mul, UniformRand, Zero}; + + use crate::ccs::r1cs::tests::{get_test_r1cs, get_test_z}; + use crate::transcript::poseidon::{tests::poseidon_test_config, PoseidonTranscript}; + use crate::utils::vec::vec_scalar_mul; pub fn check_relaxed_r1cs(r1cs: &R1CS, z: &[F], u: &F, E: &[F]) -> bool { let Az = mat_vec_mul_sparse(&r1cs.A, z); @@ -275,6 +277,10 @@ pub mod tests { assert_eq!(ci3_expected.cmE, ci3.cmE); assert_eq!(ci3_expected.cmW, ci3.cmW); + // next equalities should hold since we started from two cmE of zero-vector E's + assert_eq!(ci3.cmE, cmT.mul(r)); + assert_eq!(w3.E, vec_scalar_mul(&T, &r)); + // NIFS.Verify_Folded_Instance: NIFS::::verify_folded_instance(r, &ci1, &ci2, &ci3, &cmT).unwrap(); @@ -294,6 +300,7 @@ pub mod tests { &cmT, ) .unwrap(); + NIFS::::verify_commitments( &mut transcript_v, &pedersen_params,