Skip to content

Commit

Permalink
reuse computed points with coordinates over CF (non-native) to save c…
Browse files Browse the repository at this point in the history
…onstraints in AugmentedFCircuit

(constraint count went down ~6k)
  • Loading branch information
arnaucube committed Nov 9, 2023
1 parent 4f9667e commit fc116a7
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 92 deletions.
2 changes: 1 addition & 1 deletion src/constants.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// used for r, so when computing r^2 it does not overflow the field
// used for challenge r, so when computing r^2 it does not overflow the field
pub const N_BITS_CHALLENGE: usize = 128;
// used for committed instances hash, so when going to the other curve of the cycle it does not
// overflow the scalar field
Expand Down
25 changes: 14 additions & 11 deletions src/folding/circuits/nonnative.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,23 @@ use ark_ff::PrimeField;
use ark_r1cs_std::fields::nonnative::{params::OptimizationType, AllocatedNonNativeFieldVar};
use ark_r1cs_std::{
alloc::{AllocVar, AllocationMode},
fields::nonnative::NonNativeFieldVar,
fields::{fp::FpVar, nonnative::NonNativeFieldVar},
ToConstraintFieldGadget,
};
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
/// field. It is not intended to perform operations, but just to contain the affine coordinates in
/// order to perform hash operations of the point.
/// field, over the constraint field. It is not intended to perform operations, but just to contain
/// the affine coordinates in order to perform hash operations of the point.
#[derive(Debug, Clone)]
pub struct NonNativeAffineVar<F: PrimeField, CF: PrimeField> {
pub x: NonNativeFieldVar<F, CF>,
pub y: NonNativeFieldVar<F, CF>,
pub struct NonNativeAffineVar<F: PrimeField> {
pub x: Vec<FpVar<F>>,
pub y: Vec<FpVar<F>>,
}

impl<C> AllocVar<C, C::ScalarField> for NonNativeAffineVar<C::BaseField, C::ScalarField>
impl<C> AllocVar<C, C::ScalarField> for NonNativeAffineVar<C::ScalarField>
where
C: CurveGroup,
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
Expand All @@ -41,12 +42,14 @@ where
cs.clone(),
|| Ok(xy.0),
mode,
)?;
)?
.to_constraint_field()?;
let y = NonNativeFieldVar::<C::BaseField, C::ScalarField>::new_variable(
cs.clone(),
|| Ok(xy.1),
mode,
)?;
)?
.to_constraint_field()?;

Ok(Self { x, y })
})
Expand Down Expand Up @@ -92,7 +95,7 @@ where
#[cfg(test)]
mod tests {
use super::*;
use ark_pallas::{Fq, Fr, Projective};
use ark_pallas::{Fr, Projective};
use ark_r1cs_std::alloc::AllocVar;
use ark_relations::r1cs::ConstraintSystem;
use ark_std::Zero;
Expand All @@ -103,6 +106,6 @@ mod tests {

// dealing with the 'zero' point should not panic when doing the unwrap
let p = Projective::zero();
NonNativeAffineVar::<Fq, Fr>::new_witness(cs.clone(), || Ok(p)).unwrap();
NonNativeAffineVar::<Fr>::new_witness(cs.clone(), || Ok(p)).unwrap();
}
}
56 changes: 25 additions & 31 deletions src/folding/nova/circuits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ use ark_r1cs_std::{
fields::{fp::FpVar, FieldVar},
groups::GroupOpsBounds,
prelude::CurveVar,
ToConstraintFieldGadget,
};
use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, Namespace, SynthesisError};
use ark_std::fmt::Debug;
Expand All @@ -40,8 +39,8 @@ pub type CF2<C> = <<C as CurveGroup>::BaseField as Field>::BasePrimeField;
pub struct CommittedInstanceVar<C: CurveGroup> {
u: FpVar<C::ScalarField>,
x: Vec<FpVar<C::ScalarField>>,
cmE: NonNativeAffineVar<CF2<C>, C::ScalarField>,
cmW: NonNativeAffineVar<CF2<C>, C::ScalarField>,
cmE: NonNativeAffineVar<C::ScalarField>,
cmW: NonNativeAffineVar<C::ScalarField>,
}

impl<C> AllocVar<CommittedInstance<C>, CF1<C>> for CommittedInstanceVar<C>
Expand All @@ -61,12 +60,12 @@ where
let x: Vec<FpVar<C::ScalarField>> =
Vec::new_variable(cs.clone(), || Ok(val.borrow().x.clone()), mode)?;

let cmE = NonNativeAffineVar::<CF2<C>, C::ScalarField>::new_variable(
let cmE = NonNativeAffineVar::<C::ScalarField>::new_variable(
cs.clone(),
|| Ok(val.borrow().cmE),
mode,
)?;
let cmW = NonNativeAffineVar::<CF2<C>, C::ScalarField>::new_variable(
let cmW = NonNativeAffineVar::<C::ScalarField>::new_variable(
cs.clone(),
|| Ok(val.borrow().cmW),
mode,
Expand Down Expand Up @@ -99,10 +98,10 @@ where
z_i,
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.x,
self.cmE.y,
self.cmW.x,
self.cmW.y,
]
.concat();
CRHGadget::<C::ScalarField>::evaluate(crh_params, &input)
Expand Down Expand Up @@ -286,13 +285,13 @@ where
let (cmT_x, cmT_y) = point_to_nonnative_limbs::<C>(cmT)?;

let input = vec![
vec![u_i.u.clone()],
vec![u_i.u],
u_i.x.clone(),
u_cmE_x,
u_cmE_y,
u_cmW_x,
u_cmW_y,
vec![U_i.u.clone()],
vec![U_i.u],
U_i.x.clone(),
U_cmE_x,
U_cmE_y,
Expand All @@ -302,33 +301,33 @@ where
cmT_y,
]
.concat();
Ok(CRH::<C::ScalarField>::evaluate(&poseidon_config, input).unwrap()) // TODO rm unwrap
Ok(CRH::<C::ScalarField>::evaluate(poseidon_config, input).unwrap())
}

pub fn get_challenge(
crh_params: &CRHParametersVar<C::ScalarField>,
u_i: CommittedInstanceVar<C>,
U_i: CommittedInstanceVar<C>,
cmT: NonNativeAffineVar<CF2<C>, C::ScalarField>,
cmT: NonNativeAffineVar<C::ScalarField>,
) -> Result<FpVar<C::ScalarField>, SynthesisError> {
let input = 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()?,
u_i.cmE.x,
u_i.cmE.y,
u_i.cmW.x,
u_i.cmW.y,
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.x,
U_i.cmE.y,
U_i.cmW.x,
U_i.cmW.y,
cmT.x,
cmT.y,
]
.concat();
CRHGadget::<C::ScalarField>::evaluate(&crh_params, &input)
CRHGadget::<C::ScalarField>::evaluate(crh_params, &input)
}
}

Expand Down Expand Up @@ -659,9 +658,7 @@ pub mod tests {
let U_iVar =
CommittedInstanceVar::<Projective>::new_witness(cs.clone(), || Ok(U_i.clone()))
.unwrap();
let cmTVar =
NonNativeAffineVar::<CF2<Projective>, Fr>::new_witness(cs.clone(), || Ok(cmT.clone()))
.unwrap();
let cmTVar = NonNativeAffineVar::<Fr>::new_witness(cs.clone(), || Ok(cmT)).unwrap();

let crh_params = CRHParametersVar::<Fr>::new_constant(cs.clone(), poseidon_config).unwrap();

Expand Down Expand Up @@ -711,9 +708,6 @@ pub mod tests {
assert_eq!(1 + x.len() + w.len(), r1cs.A.n_cols);
assert_eq!(r1cs.l, x.len());

// TODO rm
// let mut tr = PoseidonTranscript::<Projective>::new(&poseidon_config);

let pedersen_params = Pedersen::<Projective>::new_params(&mut rng, r1cs.A.n_rows);

// first step, set z_i=z_0=3 and z_{i+1}=35 (initial values)
Expand Down Expand Up @@ -785,7 +779,7 @@ pub mod tests {
&poseidon_config,
u_i.clone(),
U_i.clone(),
cmT.clone(),
cmT,
)
.unwrap();

Expand Down
76 changes: 27 additions & 49 deletions src/folding/nova/ivc.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use ark_crypto_primitives::sponge::{poseidon::PoseidonConfig, Absorb};
use ark_ec::{CurveGroup, Group};
use ark_ff::{BigInteger, PrimeField};
use ark_ff::PrimeField;
use ark_relations::r1cs::ConstraintSynthesizer;
use ark_relations::r1cs::ConstraintSystem;
use ark_std::rand::Rng;
Expand All @@ -14,11 +14,10 @@ use super::{
CommittedInstance, Witness,
};
use crate::ccs::r1cs::R1CS;
use crate::constants::N_BITS_CHALLENGE;
use crate::frontend::arkworks::{extract_r1cs, extract_z}; // TODO once Frontend trait is ready, use that
use crate::pedersen::{Params as PedersenParams, Pedersen};
use crate::transcript::Transcript;
use crate::Error;
use crate::{unwrap_or_return_err, Error};

pub struct IVC<C1, C2, FC, Tr>
where
Expand Down Expand Up @@ -57,17 +56,15 @@ where
poseidon_config: PoseidonConfig<C1::ScalarField>,
F: FC,
z_0: Vec<C1::ScalarField>,
) -> Self {
) -> Result<Self, Error> {
// prepare the circuit to obtain its R1CS
let cs = ConstraintSystem::<C1::ScalarField>::new_ref();

let augmented_F_circuit = AugmentedFCircuit::<C1, FC>::empty(&poseidon_config, F);

augmented_F_circuit
.generate_constraints(cs.clone())
.unwrap();
augmented_F_circuit.generate_constraints(cs.clone())?;
cs.finalize();
let cs = cs.into_inner().unwrap();
let cs = unwrap_or_return_err!(cs.into_inner(), Err(Error::NoInnerConstraintSystem));
let r1cs = extract_r1cs::<C1::ScalarField>(&cs);

let transcript = Tr::new(&transcript_config);
Expand All @@ -80,7 +77,7 @@ where
// W_i=W_0 is a 'dummy witness', all zeroes, but with the size corresponding to the R1CS that
// we're working with.
// Set U_i to be dummy instance
Self {
Ok(Self {
_c2: PhantomData,
r1cs,
poseidon_config,
Expand All @@ -94,7 +91,7 @@ where
u_i: u_dummy.clone(),
W_i: w_dummy.clone(),
U_i: u_dummy.clone(),
}
})
}

/// Implements IVC.P
Expand All @@ -108,15 +105,12 @@ where
if self.i == C1::ScalarField::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)
u_i1_x = self
.U_i
.hash(
&self.poseidon_config,
C1::ScalarField::one(),
self.z_0.clone(),
z_i1.clone(),
)
.unwrap();
u_i1_x = self.U_i.hash(
&self.poseidon_config,
C1::ScalarField::one(),
self.z_0.clone(),
z_i1.clone(),
)?;

(W_i1, U_i1, cmT) = (self.w_i.clone(), self.u_i.clone(), C1::generator());

Expand Down Expand Up @@ -148,7 +142,7 @@ where
&self.poseidon_config,
self.u_i.clone(),
self.U_i.clone(),
cmT.clone(),
cmT,
)?;

// compute W_{i+1} and U_{i+1}
Expand All @@ -160,14 +154,12 @@ where

// folded instance output (public input, x)
// u_{i+1}.x = H(i+1, z_0, z_{i+1}, U_{i+1})
u_i1_x = U_i1
.hash(
&self.poseidon_config,
self.i + C1::ScalarField::one(),
self.z_0.clone(),
z_i1.clone(),
)
.unwrap();
u_i1_x = U_i1.hash(
&self.poseidon_config,
self.i + C1::ScalarField::one(),
self.z_0.clone(),
z_i1.clone(),
)?;

augmented_F_circuit = AugmentedFCircuit::<C1, FC> {
poseidon_config: self.poseidon_config.clone(),
Expand All @@ -185,11 +177,9 @@ where

let cs = ConstraintSystem::<C1::ScalarField>::new_ref();

augmented_F_circuit
.generate_constraints(cs.clone())
.unwrap();
augmented_F_circuit.generate_constraints(cs.clone())?;

let cs = cs.into_inner().unwrap();
let cs = unwrap_or_return_err!(cs.into_inner(), Err(Error::NoInnerConstraintSystem));
// notice that here we use 'Z' (uppercase) to denote the 'z-vector' as in the paper, not
// the value 'z' (lowercase) which is the state
let Z_i1 = extract_z::<C1::ScalarField>(&cs);
Expand All @@ -200,10 +190,7 @@ where
// compute committed instances, w_{i+1}, u_{i+1}, which will be used as w_i, u_i, so we
// assign them directly to w_i, u_i.
self.w_i = Witness::<C1>::new(w_i1.clone(), self.r1cs.A.n_rows);
self.u_i = self
.w_i
.commit(&self.pedersen_params, vec![u_i1_x])
.unwrap();
self.u_i = self.w_i.commit(&self.pedersen_params, vec![u_i1_x])?;

// set values for next iteration
self.i += C1::ScalarField::one();
Expand Down Expand Up @@ -239,13 +226,10 @@ where
}

// check R1CS satisfiability
self.r1cs
.check_instance_relation(&self.w_i, &self.u_i)
.unwrap();
self.r1cs.check_instance_relation(&self.w_i, &self.u_i)?;
// check RelaxedR1CS satisfiability
self.r1cs
.check_relaxed_instance_relation(&self.W_i, &self.U_i)
.unwrap();
.check_relaxed_instance_relation(&self.W_i, &self.U_i)?;

Ok(())
}
Expand Down Expand Up @@ -275,20 +259,14 @@ mod tests {
poseidon_config, // poseidon config
F_circuit,
z_0.clone(),
);
)
.unwrap();

let num_steps: usize = 3;
for _ in 0..num_steps {
ivc.prove_step().unwrap();
}

ivc.r1cs
.check_relaxed_instance_relation(&ivc.w_i, &ivc.u_i)
.unwrap();
ivc.r1cs
.check_relaxed_instance_relation(&ivc.W_i, &ivc.U_i)
.unwrap();

ivc.verify(z_0, num_steps as u32).unwrap();
}
}
Loading

0 comments on commit fc116a7

Please sign in to comment.