mirror of
https://github.com/arnaucube/sonobe.git
synced 2026-01-11 08:21:37 +01:00
Change CycleFold circuit approach (#77)
* Change CycleFold approach: Instead of having a single CycleFold circuit that checks the 2 forign scalarmul of the main circuit instances, now there are 2 separated CycleFold circuits each of them checking a single foreign scalarmul. Increasing the number of constraints of the AugmentedFCircuit, but reducing the number of constraints in the CycleFold circuit, which will translate into reducing the number of constraints in the Decider circuit. * CycleFold circuits checks in AugmentedFCircuit: - update NonNativeAffineVar to work with NonNativeFieldVar directly instead of FpVar comming from NonNativeFieldVar.to_constraint_field() - include in AugmentedFCircuit intermediate steps inbetween CycleFold circuits, and update the internal checks of the CycleFold circuits Pending to document the new CycleFold circuits approach and better variable namings, rm unwraps, etc * matrix_vec_mul_sparse gadget: skip value * v[col_i] mul when value==1 Saves a notable amount of constraints since there is a notable amount of 1 values in R1CS matrices. * Reuse computed vector of U_i Reuse computed vector of U_i, saving 4k constraints in AugmentedFCircuit. * fixes post last rebase to main * rm test_augmentedfcircuit since it is already tested in test_ivc (and is a slow computation) * rm dbg!() * small fixes after last main rebase
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
use ark_ec::CurveGroup;
|
use ark_ec::CurveGroup;
|
||||||
use ark_ff::Field;
|
use ark_ff::Field;
|
||||||
use ark_r1cs_std::{groups::GroupOpsBounds, prelude::CurveVar};
|
use ark_r1cs_std::{boolean::Boolean, groups::GroupOpsBounds, prelude::CurveVar};
|
||||||
use ark_relations::r1cs::SynthesisError;
|
use ark_relations::r1cs::SynthesisError;
|
||||||
use ark_std::Zero;
|
use ark_std::Zero;
|
||||||
use ark_std::{
|
use ark_std::{
|
||||||
@@ -148,7 +148,7 @@ where
|
|||||||
_gc: PhantomData<GC>,
|
_gc: PhantomData<GC>,
|
||||||
}
|
}
|
||||||
|
|
||||||
use ark_r1cs_std::{fields::nonnative::NonNativeFieldVar, ToBitsGadget};
|
use ark_r1cs_std::ToBitsGadget;
|
||||||
impl<C, GC, const H: bool> PedersenGadget<C, GC, H>
|
impl<C, GC, const H: bool> PedersenGadget<C, GC, H>
|
||||||
where
|
where
|
||||||
C: CurveGroup,
|
C: CurveGroup,
|
||||||
@@ -160,12 +160,12 @@ where
|
|||||||
pub fn commit(
|
pub fn commit(
|
||||||
h: GC,
|
h: GC,
|
||||||
g: Vec<GC>,
|
g: Vec<GC>,
|
||||||
v: Vec<NonNativeFieldVar<C::ScalarField, CF<C>>>,
|
v: Vec<Vec<Boolean<CF<C>>>>,
|
||||||
r: NonNativeFieldVar<C::ScalarField, CF<C>>,
|
r: Vec<Boolean<CF<C>>>,
|
||||||
) -> Result<GC, SynthesisError> {
|
) -> Result<GC, SynthesisError> {
|
||||||
let mut res = GC::zero();
|
let mut res = GC::zero();
|
||||||
if H {
|
if H {
|
||||||
res += h.scalar_mul_le(r.to_bits_le()?.iter())?;
|
res += h.scalar_mul_le(r.iter())?;
|
||||||
}
|
}
|
||||||
for (i, v_i) in v.iter().enumerate() {
|
for (i, v_i) in v.iter().enumerate() {
|
||||||
res += g[i].scalar_mul_le(v_i.to_bits_le()?.iter())?;
|
res += g[i].scalar_mul_le(v_i.to_bits_le()?.iter())?;
|
||||||
@@ -176,6 +176,7 @@ where
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use ark_ff::{BigInteger, PrimeField};
|
||||||
use ark_pallas::{constraints::GVar, Fq, Fr, Projective};
|
use ark_pallas::{constraints::GVar, Fq, Fr, Projective};
|
||||||
use ark_r1cs_std::{alloc::AllocVar, eq::EqGadget};
|
use ark_r1cs_std::{alloc::AllocVar, eq::EqGadget};
|
||||||
use ark_relations::r1cs::ConstraintSystem;
|
use ark_relations::r1cs::ConstraintSystem;
|
||||||
@@ -226,12 +227,20 @@ mod tests {
|
|||||||
let r: Fr = Fr::rand(&mut rng);
|
let r: Fr = Fr::rand(&mut rng);
|
||||||
let cm = Pedersen::<Projective>::commit(¶ms, &v, &r).unwrap();
|
let cm = Pedersen::<Projective>::commit(¶ms, &v, &r).unwrap();
|
||||||
|
|
||||||
|
let v_bits: Vec<Vec<bool>> = v.iter().map(|val| val.into_bigint().to_bits_le()).collect();
|
||||||
|
let r_bits: Vec<bool> = r.into_bigint().to_bits_le();
|
||||||
|
|
||||||
// circuit
|
// circuit
|
||||||
let cs = ConstraintSystem::<Fq>::new_ref();
|
let cs = ConstraintSystem::<Fq>::new_ref();
|
||||||
|
|
||||||
// prepare inputs
|
// prepare inputs
|
||||||
let vVar = Vec::<NonNativeFieldVar<Fr, Fq>>::new_witness(cs.clone(), || Ok(v)).unwrap();
|
let vVar: Vec<Vec<Boolean<Fq>>> = v_bits
|
||||||
let rVar = NonNativeFieldVar::<Fr, Fq>::new_witness(cs.clone(), || Ok(r)).unwrap();
|
.iter()
|
||||||
|
.map(|val_bits| {
|
||||||
|
Vec::<Boolean<Fq>>::new_witness(cs.clone(), || Ok(val_bits.clone())).unwrap()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let rVar = Vec::<Boolean<Fq>>::new_witness(cs.clone(), || Ok(r_bits)).unwrap();
|
||||||
let gVar = Vec::<GVar>::new_witness(cs.clone(), || Ok(params.generators)).unwrap();
|
let gVar = Vec::<GVar>::new_witness(cs.clone(), || Ok(params.generators)).unwrap();
|
||||||
let hVar = GVar::new_witness(cs.clone(), || Ok(params.h)).unwrap();
|
let hVar = GVar::new_witness(cs.clone(), || Ok(params.h)).unwrap();
|
||||||
let expected_cmVar = GVar::new_witness(cs.clone(), || Ok(cm)).unwrap();
|
let expected_cmVar = GVar::new_witness(cs.clone(), || Ok(cm)).unwrap();
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
use ark_ec::{AffineRepr, CurveGroup};
|
use ark_ec::{AffineRepr, CurveGroup};
|
||||||
use ark_ff::PrimeField;
|
|
||||||
use ark_r1cs_std::fields::nonnative::{params::OptimizationType, AllocatedNonNativeFieldVar};
|
use ark_r1cs_std::fields::nonnative::{params::OptimizationType, AllocatedNonNativeFieldVar};
|
||||||
use ark_r1cs_std::{
|
use ark_r1cs_std::{
|
||||||
alloc::{AllocVar, AllocationMode},
|
alloc::{AllocVar, AllocationMode},
|
||||||
fields::{fp::FpVar, nonnative::NonNativeFieldVar},
|
fields::nonnative::NonNativeFieldVar,
|
||||||
ToConstraintFieldGadget,
|
|
||||||
};
|
};
|
||||||
use ark_relations::r1cs::{Namespace, SynthesisError};
|
use ark_relations::r1cs::{Namespace, SynthesisError};
|
||||||
use ark_std::{One, Zero};
|
use ark_std::{One, Zero};
|
||||||
@@ -14,12 +12,15 @@ use core::borrow::Borrow;
|
|||||||
/// field, over the constraint field. It is not intended to perform operations, but just to contain
|
/// 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.
|
/// the affine coordinates in order to perform hash operations of the point.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct NonNativeAffineVar<F: PrimeField> {
|
pub struct NonNativeAffineVar<C: CurveGroup>
|
||||||
pub x: Vec<FpVar<F>>,
|
where
|
||||||
pub y: Vec<FpVar<F>>,
|
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
|
||||||
|
{
|
||||||
|
pub x: NonNativeFieldVar<C::BaseField, C::ScalarField>,
|
||||||
|
pub y: NonNativeFieldVar<C::BaseField, C::ScalarField>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C> AllocVar<C, C::ScalarField> for NonNativeAffineVar<C::ScalarField>
|
impl<C> AllocVar<C, C::ScalarField> for NonNativeAffineVar<C>
|
||||||
where
|
where
|
||||||
C: CurveGroup,
|
C: CurveGroup,
|
||||||
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
|
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
|
||||||
@@ -40,14 +41,12 @@ where
|
|||||||
cs.clone(),
|
cs.clone(),
|
||||||
|| Ok(xy.0),
|
|| Ok(xy.0),
|
||||||
mode,
|
mode,
|
||||||
)?
|
)?;
|
||||||
.to_constraint_field()?;
|
|
||||||
let y = NonNativeFieldVar::<C::BaseField, C::ScalarField>::new_variable(
|
let y = NonNativeFieldVar::<C::BaseField, C::ScalarField>::new_variable(
|
||||||
cs.clone(),
|
cs.clone(),
|
||||||
|| Ok(xy.1),
|
|| Ok(xy.1),
|
||||||
mode,
|
mode,
|
||||||
)?
|
)?;
|
||||||
.to_constraint_field()?;
|
|
||||||
|
|
||||||
Ok(Self { x, y })
|
Ok(Self { x, y })
|
||||||
})
|
})
|
||||||
@@ -108,7 +107,7 @@ where
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use ark_pallas::{Fr, Projective};
|
use ark_pallas::{Fr, Projective};
|
||||||
use ark_r1cs_std::{alloc::AllocVar, R1CSVar};
|
use ark_r1cs_std::{alloc::AllocVar, R1CSVar, ToConstraintFieldGadget};
|
||||||
use ark_relations::r1cs::ConstraintSystem;
|
use ark_relations::r1cs::ConstraintSystem;
|
||||||
use ark_std::{UniformRand, Zero};
|
use ark_std::{UniformRand, Zero};
|
||||||
|
|
||||||
@@ -118,14 +117,14 @@ mod tests {
|
|||||||
|
|
||||||
// dealing with the 'zero' point should not panic when doing the unwrap
|
// dealing with the 'zero' point should not panic when doing the unwrap
|
||||||
let p = Projective::zero();
|
let p = Projective::zero();
|
||||||
NonNativeAffineVar::<Fr>::new_witness(cs.clone(), || Ok(p)).unwrap();
|
NonNativeAffineVar::<Projective>::new_witness(cs.clone(), || Ok(p)).unwrap();
|
||||||
|
|
||||||
// check that point_to_nonnative_limbs returns the expected values
|
// check that point_to_nonnative_limbs returns the expected values
|
||||||
let mut rng = ark_std::test_rng();
|
let mut rng = ark_std::test_rng();
|
||||||
let p = Projective::rand(&mut rng);
|
let p = Projective::rand(&mut rng);
|
||||||
let pVar = NonNativeAffineVar::<Fr>::new_witness(cs.clone(), || Ok(p)).unwrap();
|
let pVar = NonNativeAffineVar::<Projective>::new_witness(cs.clone(), || Ok(p)).unwrap();
|
||||||
let (x, y) = point_to_nonnative_limbs(p).unwrap();
|
let (x, y) = point_to_nonnative_limbs(p).unwrap();
|
||||||
assert_eq!(pVar.x.value().unwrap(), x);
|
assert_eq!(pVar.x.to_constraint_field().unwrap().value().unwrap(), x);
|
||||||
assert_eq!(pVar.y.value().unwrap(), y);
|
assert_eq!(pVar.y.to_constraint_field().unwrap().value().unwrap(), y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,11 +46,14 @@ pub type CF2<C> = <<C as CurveGroup>::BaseField as Field>::BasePrimeField;
|
|||||||
/// constraints field (E1::Fr, where E1 is the main curve). The peculiarity is that cmE and cmW are
|
/// constraints field (E1::Fr, where E1 is the main curve). The peculiarity is that cmE and cmW are
|
||||||
/// represented non-natively over the constraint field.
|
/// represented non-natively over the constraint field.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct CommittedInstanceVar<C: CurveGroup> {
|
pub struct CommittedInstanceVar<C: CurveGroup>
|
||||||
|
where
|
||||||
|
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
|
||||||
|
{
|
||||||
pub u: FpVar<C::ScalarField>,
|
pub u: FpVar<C::ScalarField>,
|
||||||
pub x: Vec<FpVar<C::ScalarField>>,
|
pub x: Vec<FpVar<C::ScalarField>>,
|
||||||
pub cmE: NonNativeAffineVar<C::ScalarField>,
|
pub cmE: NonNativeAffineVar<C>,
|
||||||
pub cmW: NonNativeAffineVar<C::ScalarField>,
|
pub cmW: NonNativeAffineVar<C>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C> AllocVar<CommittedInstance<C>, CF1<C>> for CommittedInstanceVar<C>
|
impl<C> AllocVar<CommittedInstance<C>, CF1<C>> for CommittedInstanceVar<C>
|
||||||
@@ -70,16 +73,10 @@ where
|
|||||||
let x: Vec<FpVar<C::ScalarField>> =
|
let x: Vec<FpVar<C::ScalarField>> =
|
||||||
Vec::new_variable(cs.clone(), || Ok(val.borrow().x.clone()), mode)?;
|
Vec::new_variable(cs.clone(), || Ok(val.borrow().x.clone()), mode)?;
|
||||||
|
|
||||||
let cmE = NonNativeAffineVar::<C::ScalarField>::new_variable(
|
let cmE =
|
||||||
cs.clone(),
|
NonNativeAffineVar::<C>::new_variable(cs.clone(), || Ok(val.borrow().cmE), mode)?;
|
||||||
|| Ok(val.borrow().cmE),
|
let cmW =
|
||||||
mode,
|
NonNativeAffineVar::<C>::new_variable(cs.clone(), || Ok(val.borrow().cmW), mode)?;
|
||||||
)?;
|
|
||||||
let cmW = NonNativeAffineVar::<C::ScalarField>::new_variable(
|
|
||||||
cs.clone(),
|
|
||||||
|| Ok(val.borrow().cmW),
|
|
||||||
mode,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(Self { u, x, cmE, cmW })
|
Ok(Self { u, x, cmE, cmW })
|
||||||
})
|
})
|
||||||
@@ -90,31 +87,36 @@ impl<C> CommittedInstanceVar<C>
|
|||||||
where
|
where
|
||||||
C: CurveGroup,
|
C: CurveGroup,
|
||||||
<C as Group>::ScalarField: Absorb,
|
<C as Group>::ScalarField: Absorb,
|
||||||
|
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
|
||||||
{
|
{
|
||||||
/// hash implements the committed instance hash compatible with the native implementation from
|
/// hash implements the committed instance hash compatible with the native implementation from
|
||||||
/// CommittedInstance.hash.
|
/// CommittedInstance.hash.
|
||||||
/// Returns `H(i, z_0, z_i, U_i)`, where `i` can be `i` but also `i+1`, and `U` is the
|
/// Returns `H(i, z_0, z_i, U_i)`, where `i` can be `i` but also `i+1`, and `U` is the
|
||||||
/// `CommittedInstance`.
|
/// `CommittedInstance`.
|
||||||
|
/// Additionally it returns the vector of the field elements from the self parameters, so they
|
||||||
|
/// can be reused in other gadgets avoiding recalculating (reconstraining) them.
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
pub fn hash(
|
pub fn hash(
|
||||||
self,
|
self,
|
||||||
crh_params: &CRHParametersVar<CF1<C>>,
|
crh_params: &CRHParametersVar<CF1<C>>,
|
||||||
i: FpVar<CF1<C>>,
|
i: FpVar<CF1<C>>,
|
||||||
z_0: Vec<FpVar<CF1<C>>>,
|
z_0: Vec<FpVar<CF1<C>>>,
|
||||||
z_i: Vec<FpVar<CF1<C>>>,
|
z_i: Vec<FpVar<CF1<C>>>,
|
||||||
) -> Result<FpVar<CF1<C>>, SynthesisError> {
|
) -> Result<(FpVar<CF1<C>>, Vec<FpVar<CF1<C>>>), SynthesisError> {
|
||||||
let input = vec![
|
let U_vec = [
|
||||||
vec![i],
|
|
||||||
z_0,
|
|
||||||
z_i,
|
|
||||||
vec![self.u],
|
vec![self.u],
|
||||||
self.x,
|
self.x,
|
||||||
self.cmE.x,
|
self.cmE.x.to_constraint_field()?,
|
||||||
self.cmE.y,
|
self.cmE.y.to_constraint_field()?,
|
||||||
self.cmW.x,
|
self.cmW.x.to_constraint_field()?,
|
||||||
self.cmW.y,
|
self.cmW.y.to_constraint_field()?,
|
||||||
]
|
]
|
||||||
.concat();
|
.concat();
|
||||||
CRHGadget::<C::ScalarField>::evaluate(crh_params, &input)
|
let input = [vec![i], z_0, z_i, U_vec.clone()].concat();
|
||||||
|
Ok((
|
||||||
|
CRHGadget::<C::ScalarField>::evaluate(crh_params, &input)?,
|
||||||
|
U_vec,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,14 +130,15 @@ pub struct NIFSGadget<C: CurveGroup> {
|
|||||||
impl<C: CurveGroup> NIFSGadget<C>
|
impl<C: CurveGroup> NIFSGadget<C>
|
||||||
where
|
where
|
||||||
C: CurveGroup,
|
C: CurveGroup,
|
||||||
|
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
|
||||||
{
|
{
|
||||||
/// Implements the constraints for NIFS.V for u and x, since cm(E) and cm(W) are delegated to
|
/// Implements the constraints for NIFS.V for u and x, since cm(E) and cm(W) are delegated to
|
||||||
/// the CycleFold circuit.
|
/// the CycleFold circuit.
|
||||||
pub fn verify(
|
pub fn verify(
|
||||||
r: FpVar<CF1<C>>,
|
r: FpVar<CF1<C>>,
|
||||||
ci1: CommittedInstanceVar<C>,
|
ci1: CommittedInstanceVar<C>, // U_i
|
||||||
ci2: CommittedInstanceVar<C>,
|
ci2: CommittedInstanceVar<C>, // u_i
|
||||||
ci3: CommittedInstanceVar<C>,
|
ci3: CommittedInstanceVar<C>, // U_{i+1}
|
||||||
) -> Result<Boolean<CF1<C>>, SynthesisError> {
|
) -> Result<Boolean<CF1<C>>, SynthesisError> {
|
||||||
// ensure that: ci3.u == ci1.u + r * ci2.u
|
// ensure that: ci3.u == ci1.u + r * ci2.u
|
||||||
let first_check = ci3.u.is_eq(&(ci1.u + r.clone() * ci2.u))?;
|
let first_check = ci3.u.is_eq(&(ci1.u + r.clone() * ci2.u))?;
|
||||||
@@ -166,30 +169,30 @@ where
|
|||||||
{
|
{
|
||||||
pub fn get_challenge_native(
|
pub fn get_challenge_native(
|
||||||
poseidon_config: &PoseidonConfig<C::ScalarField>,
|
poseidon_config: &PoseidonConfig<C::ScalarField>,
|
||||||
u_i: CommittedInstance<C>,
|
|
||||||
U_i: CommittedInstance<C>,
|
U_i: CommittedInstance<C>,
|
||||||
|
u_i: CommittedInstance<C>,
|
||||||
cmT: C,
|
cmT: C,
|
||||||
) -> Result<Vec<bool>, SynthesisError> {
|
) -> Result<Vec<bool>, SynthesisError> {
|
||||||
let (u_cmE_x, u_cmE_y) = point_to_nonnative_limbs::<C>(u_i.cmE)?;
|
|
||||||
let (u_cmW_x, u_cmW_y) = point_to_nonnative_limbs::<C>(u_i.cmW)?;
|
|
||||||
let (U_cmE_x, U_cmE_y) = point_to_nonnative_limbs::<C>(U_i.cmE)?;
|
let (U_cmE_x, U_cmE_y) = point_to_nonnative_limbs::<C>(U_i.cmE)?;
|
||||||
let (U_cmW_x, U_cmW_y) = point_to_nonnative_limbs::<C>(U_i.cmW)?;
|
let (U_cmW_x, U_cmW_y) = point_to_nonnative_limbs::<C>(U_i.cmW)?;
|
||||||
|
let (u_cmE_x, u_cmE_y) = point_to_nonnative_limbs::<C>(u_i.cmE)?;
|
||||||
|
let (u_cmW_x, u_cmW_y) = point_to_nonnative_limbs::<C>(u_i.cmW)?;
|
||||||
let (cmT_x, cmT_y) = point_to_nonnative_limbs::<C>(cmT)?;
|
let (cmT_x, cmT_y) = point_to_nonnative_limbs::<C>(cmT)?;
|
||||||
|
|
||||||
let mut sponge = PoseidonSponge::<C::ScalarField>::new(poseidon_config);
|
let mut sponge = PoseidonSponge::<C::ScalarField>::new(poseidon_config);
|
||||||
let input = vec![
|
let input = vec![
|
||||||
vec![u_i.u],
|
|
||||||
u_i.x.clone(),
|
|
||||||
u_cmE_x,
|
|
||||||
u_cmE_y,
|
|
||||||
u_cmW_x,
|
|
||||||
u_cmW_y,
|
|
||||||
vec![U_i.u],
|
vec![U_i.u],
|
||||||
U_i.x.clone(),
|
U_i.x.clone(),
|
||||||
U_cmE_x,
|
U_cmE_x,
|
||||||
U_cmE_y,
|
U_cmE_y,
|
||||||
U_cmW_x,
|
U_cmW_x,
|
||||||
U_cmW_y,
|
U_cmW_y,
|
||||||
|
vec![u_i.u],
|
||||||
|
u_i.x.clone(),
|
||||||
|
u_cmE_x,
|
||||||
|
u_cmE_y,
|
||||||
|
u_cmW_x,
|
||||||
|
u_cmW_y,
|
||||||
cmT_x,
|
cmT_x,
|
||||||
cmT_y,
|
cmT_y,
|
||||||
]
|
]
|
||||||
@@ -203,27 +206,22 @@ where
|
|||||||
pub fn get_challenge_gadget(
|
pub fn get_challenge_gadget(
|
||||||
cs: ConstraintSystemRef<C::ScalarField>,
|
cs: ConstraintSystemRef<C::ScalarField>,
|
||||||
poseidon_config: &PoseidonConfig<C::ScalarField>,
|
poseidon_config: &PoseidonConfig<C::ScalarField>,
|
||||||
|
U_i_vec: Vec<FpVar<CF1<C>>>, // apready processed input, so we don't have to recompute these values
|
||||||
u_i: CommittedInstanceVar<C>,
|
u_i: CommittedInstanceVar<C>,
|
||||||
U_i: CommittedInstanceVar<C>,
|
cmT: NonNativeAffineVar<C>,
|
||||||
cmT: NonNativeAffineVar<C::ScalarField>,
|
|
||||||
) -> Result<Vec<Boolean<C::ScalarField>>, SynthesisError> {
|
) -> Result<Vec<Boolean<C::ScalarField>>, SynthesisError> {
|
||||||
let mut sponge = PoseidonSpongeVar::<C::ScalarField>::new(cs, poseidon_config);
|
let mut sponge = PoseidonSpongeVar::<C::ScalarField>::new(cs, poseidon_config);
|
||||||
|
|
||||||
let input: Vec<FpVar<C::ScalarField>> = vec![
|
let input: Vec<FpVar<C::ScalarField>> = vec![
|
||||||
|
U_i_vec,
|
||||||
vec![u_i.u.clone()],
|
vec![u_i.u.clone()],
|
||||||
u_i.x.clone(),
|
u_i.x.clone(),
|
||||||
u_i.cmE.x,
|
u_i.cmE.x.to_constraint_field()?,
|
||||||
u_i.cmE.y,
|
u_i.cmE.y.to_constraint_field()?,
|
||||||
u_i.cmW.x,
|
u_i.cmW.x.to_constraint_field()?,
|
||||||
u_i.cmW.y,
|
u_i.cmW.y.to_constraint_field()?,
|
||||||
vec![U_i.u.clone()],
|
cmT.x.to_constraint_field()?,
|
||||||
U_i.x.clone(),
|
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();
|
.concat();
|
||||||
sponge.absorb(&input)?;
|
sponge.absorb(&input)?;
|
||||||
@@ -257,11 +255,17 @@ pub struct AugmentedFCircuit<
|
|||||||
pub x: Option<CF1<C1>>, // public inputs (u_{i+1}.x)
|
pub x: Option<CF1<C1>>, // public inputs (u_{i+1}.x)
|
||||||
|
|
||||||
// cyclefold verifier on C1
|
// cyclefold verifier on C1
|
||||||
pub cf_u_i: Option<CommittedInstance<C2>>,
|
// Here 'cf1, cf2' are for each of the CycleFold circuits, corresponding to the fold of cmW and
|
||||||
pub cf_U_i: Option<CommittedInstance<C2>>,
|
// cmE respectively
|
||||||
pub cf_U_i1: Option<CommittedInstance<C2>>,
|
pub cf1_u_i: Option<CommittedInstance<C2>>, // input
|
||||||
pub cf_cmT: Option<C2>,
|
pub cf2_u_i: Option<CommittedInstance<C2>>, // input
|
||||||
pub cf_r_nonnat: Option<C2::ScalarField>,
|
pub cf_U_i: Option<CommittedInstance<C2>>, // input
|
||||||
|
pub cf1_U_i1: Option<CommittedInstance<C2>>, // intermediate
|
||||||
|
pub cf_U_i1: Option<CommittedInstance<C2>>, // output
|
||||||
|
pub cf1_cmT: Option<C2>,
|
||||||
|
pub cf2_cmT: Option<C2>,
|
||||||
|
pub cf1_r_nonnat: Option<C2::ScalarField>,
|
||||||
|
pub cf2_r_nonnat: Option<C2::ScalarField>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C1: CurveGroup, C2: CurveGroup, GC2: CurveVar<C2, CF2<C2>>, FC: FCircuit<CF1<C1>>>
|
impl<C1: CurveGroup, C2: CurveGroup, GC2: CurveVar<C2, CF2<C2>>, FC: FCircuit<CF1<C1>>>
|
||||||
@@ -283,11 +287,15 @@ where
|
|||||||
F: F_circuit,
|
F: F_circuit,
|
||||||
x: None,
|
x: None,
|
||||||
// cyclefold values
|
// cyclefold values
|
||||||
cf_u_i: None,
|
cf1_u_i: None,
|
||||||
|
cf2_u_i: None,
|
||||||
cf_U_i: None,
|
cf_U_i: None,
|
||||||
|
cf1_U_i1: None,
|
||||||
cf_U_i1: None,
|
cf_U_i1: None,
|
||||||
cf_cmT: None,
|
cf1_cmT: None,
|
||||||
cf_r_nonnat: None,
|
cf2_cmT: None,
|
||||||
|
cf1_r_nonnat: None,
|
||||||
|
cf2_r_nonnat: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -347,9 +355,9 @@ where
|
|||||||
let is_not_basecase = i.is_neq(&zero)?;
|
let is_not_basecase = i.is_neq(&zero)?;
|
||||||
|
|
||||||
// 1. u_i.x == H(i, z_0, z_i, U_i)
|
// 1. u_i.x == H(i, z_0, z_i, U_i)
|
||||||
let u_i_x = U_i
|
let (u_i_x, U_i_vec) =
|
||||||
.clone()
|
U_i.clone()
|
||||||
.hash(&crh_params, i.clone(), z_0.clone(), z_i.clone())?;
|
.hash(&crh_params, i.clone(), z_0.clone(), z_i.clone())?;
|
||||||
|
|
||||||
// check that h == u_i.x
|
// check that h == u_i.x
|
||||||
(u_i.x[0]).conditional_enforce_equal(&u_i_x, &is_not_basecase)?;
|
(u_i.x[0]).conditional_enforce_equal(&u_i_x, &is_not_basecase)?;
|
||||||
@@ -358,13 +366,11 @@ where
|
|||||||
let zero_x = NonNativeFieldVar::<C1::BaseField, C1::ScalarField>::new_constant(
|
let zero_x = NonNativeFieldVar::<C1::BaseField, C1::ScalarField>::new_constant(
|
||||||
cs.clone(),
|
cs.clone(),
|
||||||
C1::BaseField::zero(),
|
C1::BaseField::zero(),
|
||||||
)?
|
)?;
|
||||||
.to_constraint_field()?;
|
|
||||||
let zero_y = NonNativeFieldVar::<C1::BaseField, C1::ScalarField>::new_constant(
|
let zero_y = NonNativeFieldVar::<C1::BaseField, C1::ScalarField>::new_constant(
|
||||||
cs.clone(),
|
cs.clone(),
|
||||||
C1::BaseField::one(),
|
C1::BaseField::one(),
|
||||||
)?
|
)?;
|
||||||
.to_constraint_field()?;
|
|
||||||
(u_i.cmE.x.is_eq(&zero_x)?).conditional_enforce_equal(&Boolean::TRUE, &is_not_basecase)?;
|
(u_i.cmE.x.is_eq(&zero_x)?).conditional_enforce_equal(&Boolean::TRUE, &is_not_basecase)?;
|
||||||
(u_i.cmE.y.is_eq(&zero_y)?).conditional_enforce_equal(&Boolean::TRUE, &is_not_basecase)?;
|
(u_i.cmE.y.is_eq(&zero_y)?).conditional_enforce_equal(&Boolean::TRUE, &is_not_basecase)?;
|
||||||
(u_i.u.is_one()?).conditional_enforce_equal(&Boolean::TRUE, &is_not_basecase)?;
|
(u_i.u.is_one()?).conditional_enforce_equal(&Boolean::TRUE, &is_not_basecase)?;
|
||||||
@@ -374,19 +380,19 @@ where
|
|||||||
let r_bits = ChallengeGadget::<C1>::get_challenge_gadget(
|
let r_bits = ChallengeGadget::<C1>::get_challenge_gadget(
|
||||||
cs.clone(),
|
cs.clone(),
|
||||||
&self.poseidon_config,
|
&self.poseidon_config,
|
||||||
|
U_i_vec,
|
||||||
u_i.clone(),
|
u_i.clone(),
|
||||||
U_i.clone(),
|
|
||||||
cmT.clone(),
|
cmT.clone(),
|
||||||
)?;
|
)?;
|
||||||
let r = Boolean::le_bits_to_fp_var(&r_bits)?;
|
let r = Boolean::le_bits_to_fp_var(&r_bits)?;
|
||||||
|
|
||||||
// Notice that NIFSGadget::verify is not checking the folding of cmE & cmW, since it will
|
// Notice that NIFSGadget::verify is not checking the folding of cmE & cmW, since it will
|
||||||
// be done on the other curve.
|
// be done on the other curve.
|
||||||
let nifs_check = NIFSGadget::<C1>::verify(r, u_i.clone(), U_i.clone(), U_i1.clone())?;
|
let nifs_check = NIFSGadget::<C1>::verify(r, U_i.clone(), u_i.clone(), U_i1.clone())?;
|
||||||
nifs_check.conditional_enforce_equal(&Boolean::TRUE, &is_not_basecase)?;
|
nifs_check.conditional_enforce_equal(&Boolean::TRUE, &is_not_basecase)?;
|
||||||
|
|
||||||
// 4. u_{i+1}.x = H(i+1, z_0, z_i+1, U_{i+1}), this is the output of F'
|
// 4. u_{i+1}.x = H(i+1, z_0, z_i+1, U_{i+1}), this is the output of F'
|
||||||
let u_i1_x = U_i1.clone().hash(
|
let (u_i1_x, _) = U_i1.clone().hash(
|
||||||
&crh_params,
|
&crh_params,
|
||||||
i + FpVar::<CF1<C1>>::one(),
|
i + FpVar::<CF1<C1>>::one(),
|
||||||
z_0.clone(),
|
z_0.clone(),
|
||||||
@@ -397,65 +403,99 @@ where
|
|||||||
|
|
||||||
// CycleFold part
|
// CycleFold part
|
||||||
let cf_u_dummy_native = CommittedInstance::<C2>::dummy(CF_IO_LEN);
|
let cf_u_dummy_native = CommittedInstance::<C2>::dummy(CF_IO_LEN);
|
||||||
let cf_u_i = CycleFoldCommittedInstanceVar::<C2, GC2>::new_witness(cs.clone(), || {
|
// cf W circuit data
|
||||||
Ok(self.cf_u_i.unwrap_or_else(|| cf_u_dummy_native.clone()))
|
let cf1_u_i = CycleFoldCommittedInstanceVar::<C2, GC2>::new_witness(cs.clone(), || {
|
||||||
|
Ok(self.cf1_u_i.unwrap_or_else(|| cf_u_dummy_native.clone()))
|
||||||
|
})?;
|
||||||
|
let cf2_u_i = CycleFoldCommittedInstanceVar::<C2, GC2>::new_witness(cs.clone(), || {
|
||||||
|
Ok(self.cf2_u_i.unwrap_or_else(|| cf_u_dummy_native.clone()))
|
||||||
})?;
|
})?;
|
||||||
let cf_U_i = CycleFoldCommittedInstanceVar::<C2, GC2>::new_witness(cs.clone(), || {
|
let cf_U_i = CycleFoldCommittedInstanceVar::<C2, GC2>::new_witness(cs.clone(), || {
|
||||||
Ok(self.cf_U_i.unwrap_or_else(|| cf_u_dummy_native.clone()))
|
Ok(self.cf_U_i.unwrap_or_else(|| cf_u_dummy_native.clone()))
|
||||||
})?;
|
})?;
|
||||||
|
let cf1_U_i1 = CycleFoldCommittedInstanceVar::<C2, GC2>::new_witness(cs.clone(), || {
|
||||||
|
Ok(self.cf1_U_i1.unwrap_or_else(|| cf_u_dummy_native.clone()))
|
||||||
|
})?;
|
||||||
let cf_U_i1 = CycleFoldCommittedInstanceVar::<C2, GC2>::new_witness(cs.clone(), || {
|
let cf_U_i1 = CycleFoldCommittedInstanceVar::<C2, GC2>::new_witness(cs.clone(), || {
|
||||||
Ok(self.cf_U_i1.unwrap_or_else(|| cf_u_dummy_native.clone()))
|
Ok(self.cf_U_i1.unwrap_or_else(|| cf_u_dummy_native.clone()))
|
||||||
})?;
|
})?;
|
||||||
let cf_cmT = GC2::new_witness(cs.clone(), || Ok(self.cf_cmT.unwrap_or_else(C2::zero)))?;
|
let cf1_cmT = GC2::new_witness(cs.clone(), || Ok(self.cf1_cmT.unwrap_or_else(C2::zero)))?;
|
||||||
// cf_r_nonnat is an auxiliary input
|
let cf2_cmT = GC2::new_witness(cs.clone(), || Ok(self.cf2_cmT.unwrap_or_else(C2::zero)))?;
|
||||||
let cf_r_nonnat =
|
let cf1_r_nonnat =
|
||||||
NonNativeFieldVar::<C2::ScalarField, CF2<C2>>::new_witness(cs.clone(), || {
|
NonNativeFieldVar::<C1::BaseField, C1::ScalarField>::new_witness(cs.clone(), || {
|
||||||
Ok(self.cf_r_nonnat.unwrap_or_else(C2::ScalarField::zero))
|
Ok(self.cf1_r_nonnat.unwrap_or_else(C2::ScalarField::zero))
|
||||||
|
})?;
|
||||||
|
let cf2_r_nonnat =
|
||||||
|
NonNativeFieldVar::<C1::BaseField, C1::ScalarField>::new_witness(cs.clone(), || {
|
||||||
|
Ok(self.cf2_r_nonnat.unwrap_or_else(C2::ScalarField::zero))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// check that cf_u_i.x == [u_i, U_i, U_{i+1}]
|
let cfW_x: Vec<NonNativeFieldVar<C1::BaseField, C1::ScalarField>> = vec![
|
||||||
let incircuit_x = 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,
|
||||||
u_i.cmE.x, u_i.cmE.y, u_i.cmW.x, u_i.cmW.y, U_i.cmE.x, U_i.cmE.y, U_i.cmW.x, U_i.cmW.y,
|
];
|
||||||
U_i1.cmE.x, U_i1.cmE.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,
|
||||||
.concat();
|
];
|
||||||
|
|
||||||
let mut cf_u_i_x: Vec<FpVar<CF2<C2>>> = vec![];
|
// ensure that cf1_u & cf2_u have as public inputs the cmW & cmE from main instances U_i,
|
||||||
for x_i in cf_u_i.x.iter() {
|
// u_i, U_i+1 coordinates of the commitments
|
||||||
let mut x_fpvar = x_i.to_constraint_field()?;
|
cf1_u_i
|
||||||
cf_u_i_x.append(&mut x_fpvar);
|
.x
|
||||||
}
|
.conditional_enforce_equal(&cfW_x, &is_not_basecase)?;
|
||||||
cf_u_i_x.conditional_enforce_equal(&incircuit_x, &is_not_basecase)?;
|
cf2_u_i
|
||||||
|
.x
|
||||||
|
.conditional_enforce_equal(&cfE_x, &is_not_basecase)?;
|
||||||
|
|
||||||
// cf_r_bits is denoted by rho* in the paper
|
// cf_r_bits is denoted by rho* in the paper.
|
||||||
let cf_r_bits = CycleFoldChallengeGadget::<C2, GC2>::get_challenge_gadget(
|
|
||||||
cs.clone(),
|
|
||||||
&self.poseidon_config,
|
|
||||||
cf_u_i.clone(),
|
|
||||||
cf_U_i.clone(),
|
|
||||||
cf_cmT.clone(),
|
|
||||||
)?;
|
|
||||||
// assert that cf_r_bits == cf_r_nonnat converted to bits. cf_r_nonnat is just an auxiliary
|
// 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
|
// value used to compute RLC of NonNativeFieldVar values, since we can convert
|
||||||
// NonNativeFieldVar into Vec<Boolean>, but not in the other direction.
|
// NonNativeFieldVar into Vec<Boolean>, but not in the other direction.
|
||||||
let cf_r_nonnat_bits = cf_r_nonnat.to_bits_le()?;
|
let cf1_r_bits = CycleFoldChallengeGadget::<C2, GC2>::get_challenge_gadget(
|
||||||
cf_r_bits.conditional_enforce_equal(&cf_r_nonnat_bits[..N_BITS_RO], &is_not_basecase)?;
|
cs.clone(),
|
||||||
|
&self.poseidon_config,
|
||||||
|
cf_U_i.clone(),
|
||||||
|
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)?;
|
||||||
|
// same for cf2_r:
|
||||||
|
let cf2_r_bits = CycleFoldChallengeGadget::<C2, GC2>::get_challenge_gadget(
|
||||||
|
cs.clone(),
|
||||||
|
&self.poseidon_config,
|
||||||
|
cf1_U_i1.clone(),
|
||||||
|
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
|
// check cf_u_i.cmE=0, cf_u_i.u=1
|
||||||
(cf_u_i.cmE.is_zero()?).conditional_enforce_equal(&Boolean::TRUE, &is_not_basecase)?;
|
(cf1_u_i.cmE.is_zero()?).conditional_enforce_equal(&Boolean::TRUE, &is_not_basecase)?;
|
||||||
(cf_u_i.u.is_one()?).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
|
// 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)
|
// curve points relations are checked natively in Curve1 circuit (this one)
|
||||||
let v = NIFSFullGadget::<C2, GC2>::verify(
|
let v1 = NIFSFullGadget::<C2, GC2>::verify(
|
||||||
cf_r_bits,
|
cf1_r_bits,
|
||||||
cf_r_nonnat,
|
cf1_r_nonnat,
|
||||||
cf_cmT,
|
cf1_cmT,
|
||||||
cf_U_i,
|
cf_U_i,
|
||||||
cf_u_i,
|
cf1_u_i,
|
||||||
|
cf1_U_i1.clone(),
|
||||||
|
)?;
|
||||||
|
v1.conditional_enforce_equal(&Boolean::TRUE, &is_not_basecase)?;
|
||||||
|
let v2 = NIFSFullGadget::<C2, GC2>::verify(
|
||||||
|
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,
|
cf_U_i1,
|
||||||
)?;
|
)?;
|
||||||
v.conditional_enforce_equal(&Boolean::TRUE, &is_not_basecase)?;
|
v2.conditional_enforce_equal(&Boolean::TRUE, &is_not_basecase)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -465,21 +505,14 @@ where
|
|||||||
pub mod tests {
|
pub mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use ark_ff::BigInteger;
|
use ark_ff::BigInteger;
|
||||||
use ark_pallas::{Fq, Fr, Projective};
|
use ark_pallas::{Fr, Projective};
|
||||||
use ark_r1cs_std::{alloc::AllocVar, R1CSVar};
|
use ark_r1cs_std::{alloc::AllocVar, R1CSVar};
|
||||||
use ark_relations::r1cs::{ConstraintLayer, ConstraintSystem, TracingMode};
|
use ark_relations::r1cs::ConstraintSystem;
|
||||||
use ark_std::One;
|
|
||||||
use ark_std::UniformRand;
|
use ark_std::UniformRand;
|
||||||
use ark_vesta::{constraints::GVar as GVar2, Projective as Projective2};
|
|
||||||
use tracing_subscriber::layer::SubscriberExt;
|
|
||||||
|
|
||||||
use crate::ccs::r1cs::{extract_r1cs, extract_w_x};
|
|
||||||
use crate::commitment::pedersen::Pedersen;
|
use crate::commitment::pedersen::Pedersen;
|
||||||
use crate::folding::nova::nifs::tests::prepare_simple_fold_inputs;
|
use crate::folding::nova::nifs::tests::prepare_simple_fold_inputs;
|
||||||
use crate::folding::nova::{
|
use crate::folding::nova::nifs::NIFS;
|
||||||
get_committed_instance_coordinates, nifs::NIFS, traits::NovaR1CS, Witness,
|
|
||||||
};
|
|
||||||
use crate::frontend::tests::CubicFCircuit;
|
|
||||||
use crate::transcript::poseidon::poseidon_test_config;
|
use crate::transcript::poseidon::poseidon_test_config;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -565,7 +598,7 @@ pub mod tests {
|
|||||||
let crh_params = CRHParametersVar::<Fr>::new_constant(cs.clone(), poseidon_config).unwrap();
|
let crh_params = CRHParametersVar::<Fr>::new_constant(cs.clone(), poseidon_config).unwrap();
|
||||||
|
|
||||||
// compute the CommittedInstance hash in-circuit
|
// compute the CommittedInstance hash in-circuit
|
||||||
let hVar = ciVar.hash(&crh_params, iVar, z_0Var, z_iVar).unwrap();
|
let (hVar, _) = ciVar.hash(&crh_params, iVar, z_0Var, z_iVar).unwrap();
|
||||||
assert!(cs.is_satisfied().unwrap());
|
assert!(cs.is_satisfied().unwrap());
|
||||||
|
|
||||||
// check that the natively computed and in-circuit computed hashes match
|
// check that the natively computed and in-circuit computed hashes match
|
||||||
@@ -595,8 +628,8 @@ pub mod tests {
|
|||||||
// compute the challenge natively
|
// compute the challenge natively
|
||||||
let r_bits = ChallengeGadget::<Projective>::get_challenge_native(
|
let r_bits = ChallengeGadget::<Projective>::get_challenge_native(
|
||||||
&poseidon_config,
|
&poseidon_config,
|
||||||
u_i.clone(),
|
|
||||||
U_i.clone(),
|
U_i.clone(),
|
||||||
|
u_i.clone(),
|
||||||
cmT,
|
cmT,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -609,14 +642,23 @@ pub mod tests {
|
|||||||
let U_iVar =
|
let U_iVar =
|
||||||
CommittedInstanceVar::<Projective>::new_witness(cs.clone(), || Ok(U_i.clone()))
|
CommittedInstanceVar::<Projective>::new_witness(cs.clone(), || Ok(U_i.clone()))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let cmTVar = NonNativeAffineVar::<Fr>::new_witness(cs.clone(), || Ok(cmT)).unwrap();
|
let cmTVar = NonNativeAffineVar::<Projective>::new_witness(cs.clone(), || Ok(cmT)).unwrap();
|
||||||
|
|
||||||
// compute the challenge in-circuit
|
// compute the challenge in-circuit
|
||||||
|
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(),
|
||||||
|
]
|
||||||
|
.concat();
|
||||||
let r_bitsVar = ChallengeGadget::<Projective>::get_challenge_gadget(
|
let r_bitsVar = ChallengeGadget::<Projective>::get_challenge_gadget(
|
||||||
cs.clone(),
|
cs.clone(),
|
||||||
&poseidon_config,
|
&poseidon_config,
|
||||||
|
U_iVar_vec,
|
||||||
u_iVar,
|
u_iVar,
|
||||||
U_iVar,
|
|
||||||
cmTVar,
|
cmTVar,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -627,225 +669,4 @@ pub mod tests {
|
|||||||
assert_eq!(rVar.value().unwrap(), r);
|
assert_eq!(rVar.value().unwrap(), r);
|
||||||
assert_eq!(r_bitsVar.value().unwrap(), r_bits);
|
assert_eq!(r_bitsVar.value().unwrap(), r_bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
/// test_augmented_f_circuit folds the CubicFCircuit circuit in multiple iterations, feeding the
|
|
||||||
/// values into the AugmentedFCircuit.
|
|
||||||
fn test_augmented_f_circuit() {
|
|
||||||
let mut layer = ConstraintLayer::default();
|
|
||||||
layer.mode = TracingMode::OnlyConstraints;
|
|
||||||
let subscriber = tracing_subscriber::Registry::default().with(layer);
|
|
||||||
let _guard = tracing::subscriber::set_default(subscriber);
|
|
||||||
|
|
||||||
let mut rng = ark_std::test_rng();
|
|
||||||
let poseidon_config = poseidon_test_config::<Fr>();
|
|
||||||
|
|
||||||
// compute z vector for the initial instance
|
|
||||||
let cs = ConstraintSystem::<Fr>::new_ref();
|
|
||||||
|
|
||||||
// prepare the circuit to obtain its R1CS
|
|
||||||
let F_circuit = CubicFCircuit::<Fr>::new(());
|
|
||||||
let mut augmented_F_circuit =
|
|
||||||
AugmentedFCircuit::<Projective, Projective2, GVar2, CubicFCircuit<Fr>>::empty(
|
|
||||||
&poseidon_config,
|
|
||||||
F_circuit,
|
|
||||||
);
|
|
||||||
augmented_F_circuit
|
|
||||||
.generate_constraints(cs.clone())
|
|
||||||
.unwrap();
|
|
||||||
cs.finalize();
|
|
||||||
let cs = cs.into_inner().unwrap();
|
|
||||||
let r1cs = extract_r1cs::<Fr>(&cs);
|
|
||||||
let (w, x) = extract_w_x::<Fr>(&cs);
|
|
||||||
assert_eq!(1 + x.len() + w.len(), r1cs.A.n_cols);
|
|
||||||
assert_eq!(r1cs.l, x.len());
|
|
||||||
|
|
||||||
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)
|
|
||||||
let z_0 = vec![Fr::from(3_u32)];
|
|
||||||
let mut z_i = z_0.clone();
|
|
||||||
let mut z_i1 = vec![Fr::from(35_u32)];
|
|
||||||
|
|
||||||
let w_dummy = Witness::<Projective>::new(vec![Fr::zero(); w.len()], r1cs.A.n_rows);
|
|
||||||
let u_dummy = CommittedInstance::<Projective>::dummy(x.len());
|
|
||||||
|
|
||||||
// W_i is a 'dummy witness', all zeroes, but with the size corresponding to the R1CS that
|
|
||||||
// we're working with.
|
|
||||||
// set U_i <-- dummy instance
|
|
||||||
let mut W_i = w_dummy.clone();
|
|
||||||
let mut U_i = u_dummy.clone();
|
|
||||||
r1cs.check_relaxed_instance_relation(&W_i, &U_i).unwrap();
|
|
||||||
|
|
||||||
let mut w_i = w_dummy.clone();
|
|
||||||
let mut u_i = u_dummy.clone();
|
|
||||||
let (mut W_i1, mut U_i1, mut cmT): (
|
|
||||||
Witness<Projective>,
|
|
||||||
CommittedInstance<Projective>,
|
|
||||||
Projective,
|
|
||||||
) = (w_dummy.clone(), u_dummy.clone(), Projective::generator());
|
|
||||||
// as expected, dummy instances pass the relaxed_r1cs check
|
|
||||||
r1cs.check_relaxed_instance_relation(&W_i1, &U_i1).unwrap();
|
|
||||||
|
|
||||||
let mut i = Fr::zero();
|
|
||||||
let mut u_i1_x: Fr;
|
|
||||||
for _ in 0..4 {
|
|
||||||
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)
|
|
||||||
u_i1_x = U_i
|
|
||||||
.hash(&poseidon_config, Fr::one(), z_0.clone(), z_i1.clone())
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// base case
|
|
||||||
augmented_F_circuit =
|
|
||||||
AugmentedFCircuit::<Projective, Projective2, GVar2, CubicFCircuit<Fr>> {
|
|
||||||
_gc2: PhantomData,
|
|
||||||
poseidon_config: poseidon_config.clone(),
|
|
||||||
i: Some(i), // = 0
|
|
||||||
z_0: Some(z_0.clone()), // = z_i=3
|
|
||||||
z_i: Some(z_i.clone()),
|
|
||||||
u_i: Some(u_i.clone()), // = dummy
|
|
||||||
U_i: Some(U_i.clone()), // = dummy
|
|
||||||
U_i1: Some(U_i1.clone()), // = dummy
|
|
||||||
cmT: Some(cmT),
|
|
||||||
F: F_circuit,
|
|
||||||
x: Some(u_i1_x),
|
|
||||||
// cyclefold instances (not tested in this test)
|
|
||||||
cf_u_i: None,
|
|
||||||
cf_U_i: None,
|
|
||||||
cf_U_i1: None,
|
|
||||||
cf_cmT: None,
|
|
||||||
cf_r_nonnat: None,
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
r1cs.check_relaxed_instance_relation(&w_i, &u_i).unwrap();
|
|
||||||
r1cs.check_relaxed_instance_relation(&W_i, &U_i).unwrap();
|
|
||||||
|
|
||||||
// U_{i+1}
|
|
||||||
let T: Vec<Fr>;
|
|
||||||
(T, cmT) = NIFS::<Projective, Pedersen<Projective>>::compute_cmT(
|
|
||||||
&pedersen_params,
|
|
||||||
&r1cs,
|
|
||||||
&w_i,
|
|
||||||
&u_i,
|
|
||||||
&W_i,
|
|
||||||
&U_i,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// get challenge r
|
|
||||||
let r_bits = ChallengeGadget::<Projective>::get_challenge_native(
|
|
||||||
&poseidon_config,
|
|
||||||
u_i.clone(),
|
|
||||||
U_i.clone(),
|
|
||||||
cmT,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
let r_Fr = Fr::from_bigint(BigInteger::from_bits_le(&r_bits)).unwrap();
|
|
||||||
|
|
||||||
(W_i1, U_i1) = NIFS::<Projective, Pedersen<Projective>>::fold_instances(
|
|
||||||
r_Fr, &w_i, &u_i, &W_i, &U_i, &T, cmT,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
r1cs.check_relaxed_instance_relation(&W_i1, &U_i1).unwrap();
|
|
||||||
|
|
||||||
// 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(&poseidon_config, i + Fr::one(), z_0.clone(), z_i1.clone())
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// set up dummy cyclefold instances just for the sake of this test. Warning, this
|
|
||||||
// is only because we are in a test were we're not testing the cyclefold side of
|
|
||||||
// things.
|
|
||||||
let cf_W_i = Witness::<Projective2>::new(vec![Fq::zero(); 1], 1);
|
|
||||||
let cf_U_i = CommittedInstance::<Projective2>::dummy(CF_IO_LEN);
|
|
||||||
let cf_u_i_x = [
|
|
||||||
get_committed_instance_coordinates(&u_i),
|
|
||||||
get_committed_instance_coordinates(&U_i),
|
|
||||||
get_committed_instance_coordinates(&U_i1),
|
|
||||||
]
|
|
||||||
.concat();
|
|
||||||
let cf_u_i = CommittedInstance::<Projective2> {
|
|
||||||
cmE: cf_U_i.cmE,
|
|
||||||
u: Fq::one(),
|
|
||||||
cmW: cf_U_i.cmW,
|
|
||||||
x: cf_u_i_x,
|
|
||||||
};
|
|
||||||
let cf_w_i = cf_W_i.clone();
|
|
||||||
let (cf_T, cf_cmT): (Vec<Fq>, Projective2) =
|
|
||||||
(vec![Fq::zero(); cf_W_i.E.len()], Projective2::zero());
|
|
||||||
let cf_r_bits =
|
|
||||||
CycleFoldChallengeGadget::<Projective2, GVar2>::get_challenge_native(
|
|
||||||
&poseidon_config,
|
|
||||||
cf_u_i.clone(),
|
|
||||||
cf_U_i.clone(),
|
|
||||||
cf_cmT,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
let cf_r_Fq = Fq::from_bigint(BigInteger::from_bits_le(&cf_r_bits)).unwrap();
|
|
||||||
let (_, cf_U_i1) = NIFS::<Projective2, Pedersen<Projective2>>::fold_instances(
|
|
||||||
cf_r_Fq, &cf_W_i, &cf_U_i, &cf_w_i, &cf_u_i, &cf_T, cf_cmT,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
augmented_F_circuit =
|
|
||||||
AugmentedFCircuit::<Projective, Projective2, GVar2, CubicFCircuit<Fr>> {
|
|
||||||
_gc2: PhantomData,
|
|
||||||
poseidon_config: poseidon_config.clone(),
|
|
||||||
i: Some(i),
|
|
||||||
z_0: Some(z_0.clone()),
|
|
||||||
z_i: Some(z_i.clone()),
|
|
||||||
u_i: Some(u_i),
|
|
||||||
U_i: Some(U_i.clone()),
|
|
||||||
U_i1: Some(U_i1.clone()),
|
|
||||||
cmT: Some(cmT),
|
|
||||||
F: F_circuit,
|
|
||||||
x: Some(u_i1_x),
|
|
||||||
cf_u_i: Some(cf_u_i),
|
|
||||||
cf_U_i: Some(cf_U_i),
|
|
||||||
cf_U_i1: Some(cf_U_i1),
|
|
||||||
cf_cmT: Some(cf_cmT),
|
|
||||||
cf_r_nonnat: Some(cf_r_Fq),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let cs = ConstraintSystem::<Fr>::new_ref();
|
|
||||||
|
|
||||||
augmented_F_circuit
|
|
||||||
.generate_constraints(cs.clone())
|
|
||||||
.unwrap();
|
|
||||||
let is_satisfied = cs.is_satisfied().unwrap();
|
|
||||||
if !is_satisfied {
|
|
||||||
dbg!(cs.which_is_unsatisfied().unwrap());
|
|
||||||
}
|
|
||||||
assert!(is_satisfied);
|
|
||||||
|
|
||||||
cs.finalize();
|
|
||||||
let cs = cs.into_inner().unwrap();
|
|
||||||
let (w_i1, x_i1) = extract_w_x::<Fr>(&cs);
|
|
||||||
assert_eq!(x_i1.len(), 1);
|
|
||||||
assert_eq!(x_i1[0], u_i1_x);
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
w_i = Witness::<Projective>::new(w_i1.clone(), r1cs.A.n_rows);
|
|
||||||
u_i = w_i
|
|
||||||
.commit::<Pedersen<Projective>>(&pedersen_params, vec![u_i1_x])
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
r1cs.check_relaxed_instance_relation(&w_i, &u_i).unwrap();
|
|
||||||
r1cs.check_relaxed_instance_relation(&W_i1, &U_i1).unwrap();
|
|
||||||
|
|
||||||
// set values for next iteration
|
|
||||||
i += Fr::one();
|
|
||||||
// advance the F circuit state
|
|
||||||
z_i = z_i1.clone();
|
|
||||||
z_i1 = F_circuit.step_native(z_i.clone()).unwrap();
|
|
||||||
U_i = U_i1.clone();
|
|
||||||
W_i = W_i1.clone();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ use super::CommittedInstance;
|
|||||||
use crate::constants::N_BITS_RO;
|
use crate::constants::N_BITS_RO;
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
|
|
||||||
// publi inputs length for the CycleFoldCircuit, |[u_i, U_i, U_{i+1}]|
|
// publi inputs length for the CycleFoldCircuit, |[p1.x,y, p2.x,y, p3.x,y]|
|
||||||
pub const CF_IO_LEN: usize = 12;
|
pub const CF_IO_LEN: usize = 6;
|
||||||
|
|
||||||
/// CycleFoldCommittedInstanceVar is the CycleFold CommittedInstance representation in the Nova
|
/// CycleFoldCommittedInstanceVar is the CycleFold CommittedInstance representation in the Nova
|
||||||
/// circuit.
|
/// circuit.
|
||||||
@@ -122,40 +122,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// NIFSinCycleFoldGadget performs the Nova NIFS.V elliptic curve points relation checks in the other
|
|
||||||
/// curve (natively) following [CycleFold](https://eprint.iacr.org/2023/1192.pdf).
|
|
||||||
pub struct NIFSinCycleFoldGadget<C: CurveGroup, GC: CurveVar<C, CF2<C>>> {
|
|
||||||
_c: PhantomData<C>,
|
|
||||||
_gc: PhantomData<GC>,
|
|
||||||
}
|
|
||||||
impl<C: CurveGroup, GC: CurveVar<C, CF2<C>>> NIFSinCycleFoldGadget<C, GC>
|
|
||||||
where
|
|
||||||
C: CurveGroup,
|
|
||||||
GC: CurveVar<C, CF2<C>>,
|
|
||||||
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
|
|
||||||
for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
|
|
||||||
{
|
|
||||||
pub fn verify(
|
|
||||||
r_bits: Vec<Boolean<CF2<C>>>,
|
|
||||||
cmT: GC,
|
|
||||||
ci1: CommittedInstanceInCycleFoldVar<C, GC>,
|
|
||||||
ci2: CommittedInstanceInCycleFoldVar<C, GC>,
|
|
||||||
ci3: CommittedInstanceInCycleFoldVar<C, GC>,
|
|
||||||
) -> Result<Boolean<CF2<C>>, SynthesisError> {
|
|
||||||
// cm(E) check: ci3.cmE == ci1.cmE + r * cmT + r^2 * ci2.cmE
|
|
||||||
let first_check = ci3.cmE.is_eq(
|
|
||||||
&((ci2.cmE.scalar_mul_le(r_bits.iter())? + 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())?))?;
|
|
||||||
|
|
||||||
first_check.and(&second_check)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This is the gadget used in the AugmentedFCircuit to verify the CycleFold instances folding,
|
/// This is the gadget used in the AugmentedFCircuit to verify the CycleFold instances folding,
|
||||||
/// which checks the correct RLC of u,x,cmE,cmW (hence the name containing 'Full', since it checks
|
/// which checks the correct RLC of u,x,cmE,cmW (hence the name containing 'Full', since it checks
|
||||||
/// all the RLC values, not only the native ones). It assumes that ci2.cmE=0, ci2.u=1.
|
/// all the RLC values, not only the native ones). It assumes that ci2.cmE=0, ci2.u=1.
|
||||||
@@ -171,6 +137,7 @@ where
|
|||||||
for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
|
for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
|
||||||
{
|
{
|
||||||
pub fn verify(
|
pub fn verify(
|
||||||
|
// assumes that r_bits is equal to r_nonnat just that in a different format
|
||||||
r_bits: Vec<Boolean<CF2<C>>>,
|
r_bits: Vec<Boolean<CF2<C>>>,
|
||||||
r_nonnat: NonNativeFieldVar<C::ScalarField, CF2<C>>,
|
r_nonnat: NonNativeFieldVar<C::ScalarField, CF2<C>>,
|
||||||
cmT: GC,
|
cmT: GC,
|
||||||
@@ -224,38 +191,38 @@ where
|
|||||||
{
|
{
|
||||||
pub fn get_challenge_native(
|
pub fn get_challenge_native(
|
||||||
poseidon_config: &PoseidonConfig<C::BaseField>,
|
poseidon_config: &PoseidonConfig<C::BaseField>,
|
||||||
u_i: CommittedInstance<C>,
|
|
||||||
U_i: CommittedInstance<C>,
|
U_i: CommittedInstance<C>,
|
||||||
|
u_i: CommittedInstance<C>,
|
||||||
cmT: C,
|
cmT: C,
|
||||||
) -> Result<Vec<bool>, Error> {
|
) -> Result<Vec<bool>, Error> {
|
||||||
let mut sponge = PoseidonSponge::<C::BaseField>::new(poseidon_config);
|
let mut sponge = PoseidonSponge::<C::BaseField>::new(poseidon_config);
|
||||||
|
|
||||||
let u_i_cmE_bytes = point_to_bytes(u_i.cmE);
|
let U_i_cmE_bytes = point_to_bytes(U_i.cmE)?;
|
||||||
let u_i_cmW_bytes = point_to_bytes(u_i.cmW);
|
let U_i_cmW_bytes = point_to_bytes(U_i.cmW)?;
|
||||||
let U_i_cmE_bytes = point_to_bytes(U_i.cmE);
|
let u_i_cmE_bytes = point_to_bytes(u_i.cmE)?;
|
||||||
let U_i_cmW_bytes = point_to_bytes(U_i.cmW);
|
let u_i_cmW_bytes = point_to_bytes(u_i.cmW)?;
|
||||||
let cmT_bytes = point_to_bytes(cmT);
|
let cmT_bytes = point_to_bytes(cmT)?;
|
||||||
|
|
||||||
let mut u_i_u_bytes = Vec::new();
|
|
||||||
u_i.u.serialize_uncompressed(&mut u_i_u_bytes)?;
|
|
||||||
let mut u_i_x_bytes = Vec::new();
|
|
||||||
u_i.x.serialize_uncompressed(&mut u_i_x_bytes)?;
|
|
||||||
u_i_x_bytes = u_i_x_bytes[8..].to_vec();
|
|
||||||
let mut U_i_u_bytes = Vec::new();
|
let mut U_i_u_bytes = Vec::new();
|
||||||
U_i.u.serialize_uncompressed(&mut U_i_u_bytes)?;
|
U_i.u.serialize_uncompressed(&mut U_i_u_bytes)?;
|
||||||
let mut U_i_x_bytes = Vec::new();
|
let mut U_i_x_bytes = Vec::new();
|
||||||
U_i.x.serialize_uncompressed(&mut U_i_x_bytes)?;
|
U_i.x.serialize_uncompressed(&mut U_i_x_bytes)?;
|
||||||
U_i_x_bytes = U_i_x_bytes[8..].to_vec();
|
U_i_x_bytes = U_i_x_bytes[8..].to_vec();
|
||||||
|
let mut u_i_u_bytes = Vec::new();
|
||||||
|
u_i.u.serialize_uncompressed(&mut u_i_u_bytes)?;
|
||||||
|
let mut u_i_x_bytes = Vec::new();
|
||||||
|
u_i.x.serialize_uncompressed(&mut u_i_x_bytes)?;
|
||||||
|
u_i_x_bytes = u_i_x_bytes[8..].to_vec();
|
||||||
|
|
||||||
let input: Vec<u8> = [
|
let input: Vec<u8> = [
|
||||||
u_i_cmE_bytes,
|
|
||||||
u_i_u_bytes,
|
|
||||||
u_i_cmW_bytes,
|
|
||||||
u_i_x_bytes,
|
|
||||||
U_i_cmE_bytes,
|
U_i_cmE_bytes,
|
||||||
U_i_u_bytes,
|
U_i_u_bytes,
|
||||||
U_i_cmW_bytes,
|
U_i_cmW_bytes,
|
||||||
U_i_x_bytes,
|
U_i_x_bytes,
|
||||||
|
u_i_cmE_bytes,
|
||||||
|
u_i_u_bytes,
|
||||||
|
u_i_cmW_bytes,
|
||||||
|
u_i_x_bytes,
|
||||||
cmT_bytes,
|
cmT_bytes,
|
||||||
]
|
]
|
||||||
.concat();
|
.concat();
|
||||||
@@ -267,32 +234,32 @@ where
|
|||||||
pub fn get_challenge_gadget(
|
pub fn get_challenge_gadget(
|
||||||
cs: ConstraintSystemRef<C::BaseField>,
|
cs: ConstraintSystemRef<C::BaseField>,
|
||||||
poseidon_config: &PoseidonConfig<C::BaseField>,
|
poseidon_config: &PoseidonConfig<C::BaseField>,
|
||||||
u_i: CycleFoldCommittedInstanceVar<C, GC>,
|
|
||||||
U_i: CycleFoldCommittedInstanceVar<C, GC>,
|
U_i: CycleFoldCommittedInstanceVar<C, GC>,
|
||||||
|
u_i: CycleFoldCommittedInstanceVar<C, GC>,
|
||||||
cmT: GC,
|
cmT: GC,
|
||||||
) -> Result<Vec<Boolean<C::BaseField>>, SynthesisError> {
|
) -> Result<Vec<Boolean<C::BaseField>>, SynthesisError> {
|
||||||
let mut sponge = PoseidonSpongeVar::<C::BaseField>::new(cs, poseidon_config);
|
let mut sponge = PoseidonSpongeVar::<C::BaseField>::new(cs, poseidon_config);
|
||||||
|
|
||||||
let u_i_x_bytes: Vec<UInt8<CF2<C>>> = u_i
|
let U_i_x_bytes: Vec<UInt8<CF2<C>>> = U_i
|
||||||
.x
|
.x
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|e| e.to_bytes().unwrap_or(vec![]))
|
.flat_map(|e| e.to_bytes().unwrap_or(vec![]))
|
||||||
.collect::<Vec<UInt8<CF2<C>>>>();
|
.collect::<Vec<UInt8<CF2<C>>>>();
|
||||||
let U_i_x_bytes: Vec<UInt8<CF2<C>>> = U_i
|
let u_i_x_bytes: Vec<UInt8<CF2<C>>> = u_i
|
||||||
.x
|
.x
|
||||||
.iter()
|
.iter()
|
||||||
.flat_map(|e| e.to_bytes().unwrap_or(vec![]))
|
.flat_map(|e| e.to_bytes().unwrap_or(vec![]))
|
||||||
.collect::<Vec<UInt8<CF2<C>>>>();
|
.collect::<Vec<UInt8<CF2<C>>>>();
|
||||||
|
|
||||||
let input: Vec<UInt8<CF2<C>>> = [
|
let input: Vec<UInt8<CF2<C>>> = [
|
||||||
u_i.cmE.to_bytes()?,
|
|
||||||
u_i.u.to_bytes()?,
|
|
||||||
u_i.cmW.to_bytes()?,
|
|
||||||
u_i_x_bytes,
|
|
||||||
U_i.cmE.to_bytes()?,
|
U_i.cmE.to_bytes()?,
|
||||||
U_i.u.to_bytes()?,
|
U_i.u.to_bytes()?,
|
||||||
U_i.cmW.to_bytes()?,
|
U_i.cmW.to_bytes()?,
|
||||||
U_i_x_bytes,
|
U_i_x_bytes,
|
||||||
|
u_i.cmE.to_bytes()?,
|
||||||
|
u_i.u.to_bytes()?,
|
||||||
|
u_i.cmW.to_bytes()?,
|
||||||
|
u_i_x_bytes,
|
||||||
cmT.to_bytes()?,
|
cmT.to_bytes()?,
|
||||||
// TODO instead of bytes, use field elements, but needs x,y coordinates from
|
// TODO instead of bytes, use field elements, but needs x,y coordinates from
|
||||||
// u_i.{cmE,cmW}, U_i.{cmE,cmW}, cmT. Depends exposing x,y coordinates of GC. Issue to
|
// u_i.{cmE,cmW}, U_i.{cmE,cmW}, cmT. Depends exposing x,y coordinates of GC. Issue to
|
||||||
@@ -307,16 +274,16 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// returns the bytes being compatible with the ark_r1cs_std `.to_bytes` approach
|
/// returns the bytes being compatible with the ark_r1cs_std `.to_bytes` approach
|
||||||
fn point_to_bytes<C: CurveGroup>(p: C) -> Vec<u8> {
|
fn point_to_bytes<C: CurveGroup>(p: C) -> Result<Vec<u8>, Error> {
|
||||||
let l = p.uncompressed_size();
|
let l = p.uncompressed_size();
|
||||||
let mut b = Vec::new();
|
let mut b = Vec::new();
|
||||||
p.serialize_uncompressed(&mut b).unwrap();
|
p.serialize_uncompressed(&mut b)?;
|
||||||
b[l - 1] = 0;
|
b[l - 1] = 0;
|
||||||
if p.is_zero() {
|
if p.is_zero() {
|
||||||
b[l / 2] = 1;
|
b[l / 2] = 1;
|
||||||
b[l - 1] = 1;
|
b[l - 1] = 1;
|
||||||
}
|
}
|
||||||
b
|
Ok(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// CycleFoldCircuit contains the constraints that check the correct fold of the committed
|
/// CycleFoldCircuit contains the constraints that check the correct fold of the committed
|
||||||
@@ -326,12 +293,9 @@ fn point_to_bytes<C: CurveGroup>(p: C) -> Vec<u8> {
|
|||||||
pub struct CycleFoldCircuit<C: CurveGroup, GC: CurveVar<C, CF2<C>>> {
|
pub struct CycleFoldCircuit<C: CurveGroup, GC: CurveVar<C, CF2<C>>> {
|
||||||
pub _gc: PhantomData<GC>,
|
pub _gc: PhantomData<GC>,
|
||||||
pub r_bits: Option<Vec<bool>>,
|
pub r_bits: Option<Vec<bool>>,
|
||||||
pub cmT: Option<C>,
|
pub p1: Option<C>,
|
||||||
// u_i,U_i,U_i1 are the nova instances from AugmentedFCircuit which will be (their elliptic
|
pub p2: Option<C>,
|
||||||
// curve points) checked natively in CycleFoldCircuit
|
pub p3: Option<C>,
|
||||||
pub u_i: Option<CommittedInstance<C>>,
|
|
||||||
pub U_i: Option<CommittedInstance<C>>,
|
|
||||||
pub U_i1: Option<CommittedInstance<C>>,
|
|
||||||
pub x: Option<Vec<CF2<C>>>, // public inputs (cf_u_{i+1}.x)
|
pub x: Option<Vec<CF2<C>>>, // public inputs (cf_u_{i+1}.x)
|
||||||
}
|
}
|
||||||
impl<C: CurveGroup, GC: CurveVar<C, CF2<C>>> CycleFoldCircuit<C, GC> {
|
impl<C: CurveGroup, GC: CurveVar<C, CF2<C>>> CycleFoldCircuit<C, GC> {
|
||||||
@@ -339,10 +303,9 @@ impl<C: CurveGroup, GC: CurveVar<C, CF2<C>>> CycleFoldCircuit<C, GC> {
|
|||||||
Self {
|
Self {
|
||||||
_gc: PhantomData,
|
_gc: PhantomData,
|
||||||
r_bits: None,
|
r_bits: None,
|
||||||
cmT: None,
|
p1: None,
|
||||||
u_i: None,
|
p2: None,
|
||||||
U_i: None,
|
p3: None,
|
||||||
U_i1: None,
|
|
||||||
x: None,
|
x: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -358,29 +321,21 @@ where
|
|||||||
let r_bits: Vec<Boolean<CF2<C>>> = Vec::new_witness(cs.clone(), || {
|
let r_bits: Vec<Boolean<CF2<C>>> = Vec::new_witness(cs.clone(), || {
|
||||||
Ok(self.r_bits.unwrap_or(vec![false; N_BITS_RO]))
|
Ok(self.r_bits.unwrap_or(vec![false; N_BITS_RO]))
|
||||||
})?;
|
})?;
|
||||||
let cmT = GC::new_witness(cs.clone(), || Ok(self.cmT.unwrap_or(C::zero())))?;
|
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 u_dummy_native = CommittedInstance::<C>::dummy(1);
|
|
||||||
|
|
||||||
let u_i = CommittedInstanceInCycleFoldVar::<C, GC>::new_witness(cs.clone(), || {
|
|
||||||
Ok(self.u_i.unwrap_or(u_dummy_native.clone()))
|
|
||||||
})?;
|
|
||||||
let U_i = CommittedInstanceInCycleFoldVar::<C, GC>::new_witness(cs.clone(), || {
|
|
||||||
Ok(self.U_i.unwrap_or(u_dummy_native.clone()))
|
|
||||||
})?;
|
|
||||||
let U_i1 = CommittedInstanceInCycleFoldVar::<C, GC>::new_witness(cs.clone(), || {
|
|
||||||
Ok(self.U_i1.unwrap_or(u_dummy_native.clone()))
|
|
||||||
})?;
|
|
||||||
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]))
|
Ok(self.x.unwrap_or(vec![CF2::<C>::zero(); CF_IO_LEN]))
|
||||||
})?;
|
})?;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
assert_eq!(_x.len(), CF_IO_LEN); // non-constrained sanity check
|
assert_eq!(_x.len(), CF_IO_LEN); // non-constrained sanity check
|
||||||
|
|
||||||
// fold the original Nova instances natively in CycleFold
|
// Fold the original Nova instances natively in CycleFold
|
||||||
let v =
|
// For the cmW we're checking: U_i1.cmW == U_i.cmW + r * u_i.cmW
|
||||||
NIFSinCycleFoldGadget::<C, GC>::verify(r_bits.clone(), cmT, u_i.clone(), U_i, U_i1)?;
|
// For the cmE we're checking: U_i1.cmE == U_i.cmE + r * cmT + r^2 * u_i.cmE, where u_i.cmE
|
||||||
v.enforce_equal(&Boolean::TRUE)?;
|
// 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
|
// 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
|
// the CycleFoldCircuit are the sames used in the public inputs 'x', which come from the
|
||||||
@@ -401,6 +356,7 @@ pub mod tests {
|
|||||||
use ark_relations::r1cs::ConstraintSystem;
|
use ark_relations::r1cs::ConstraintSystem;
|
||||||
use ark_std::UniformRand;
|
use ark_std::UniformRand;
|
||||||
|
|
||||||
|
use crate::folding::nova::get_cm_coordinates;
|
||||||
use crate::folding::nova::nifs::tests::prepare_simple_fold_inputs;
|
use crate::folding::nova::nifs::tests::prepare_simple_fold_inputs;
|
||||||
use crate::transcript::poseidon::poseidon_test_config;
|
use crate::transcript::poseidon::poseidon_test_config;
|
||||||
|
|
||||||
@@ -427,37 +383,48 @@ pub mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_nifs_gadget_cyclefold() {
|
fn test_CycleFoldCircuit_constraints() {
|
||||||
let (_, _, _, _, ci1, _, ci2, _, ci3, _, cmT, r_bits, _) = prepare_simple_fold_inputs();
|
let (_, _, _, _, ci1, _, ci2, _, ci3, _, cmT, r_bits, _) = prepare_simple_fold_inputs();
|
||||||
|
|
||||||
// cs is the Constraint System on the Curve Cycle auxiliary curve constraints field
|
// cs is the Constraint System on the Curve Cycle auxiliary curve constraints field
|
||||||
// (E2::Fr)
|
// (E1::Fq=E2::Fr)
|
||||||
let cs = ConstraintSystem::<Fq>::new_ref();
|
let cs = ConstraintSystem::<Fq>::new_ref();
|
||||||
|
|
||||||
let r_bitsVar = Vec::<Boolean<Fq>>::new_witness(cs.clone(), || Ok(r_bits)).unwrap();
|
let cfW_u_i_x = [
|
||||||
|
get_cm_coordinates(&ci1.cmW),
|
||||||
|
get_cm_coordinates(&ci2.cmW),
|
||||||
|
get_cm_coordinates(&ci3.cmW),
|
||||||
|
]
|
||||||
|
.concat();
|
||||||
|
let cfW_circuit = CycleFoldCircuit::<Projective, GVar> {
|
||||||
|
_gc: PhantomData,
|
||||||
|
r_bits: Some(r_bits.clone()),
|
||||||
|
p1: Some(ci1.clone().cmW),
|
||||||
|
p2: Some(ci2.clone().cmW),
|
||||||
|
p3: Some(ci3.clone().cmW),
|
||||||
|
x: Some(cfW_u_i_x.clone()),
|
||||||
|
};
|
||||||
|
cfW_circuit.generate_constraints(cs.clone()).unwrap();
|
||||||
|
assert!(cs.is_satisfied().unwrap());
|
||||||
|
dbg!(cs.num_constraints());
|
||||||
|
|
||||||
let cmTVar = GVar::new_witness(cs.clone(), || Ok(cmT)).unwrap();
|
// same for E:
|
||||||
let ci1Var =
|
let cs = ConstraintSystem::<Fq>::new_ref();
|
||||||
CommittedInstanceInCycleFoldVar::<Projective, GVar>::new_witness(cs.clone(), || {
|
let cfE_u_i_x = [
|
||||||
Ok(ci1.clone())
|
get_cm_coordinates(&ci1.cmE),
|
||||||
})
|
get_cm_coordinates(&ci2.cmE),
|
||||||
.unwrap();
|
get_cm_coordinates(&ci3.cmE),
|
||||||
let ci2Var =
|
]
|
||||||
CommittedInstanceInCycleFoldVar::<Projective, GVar>::new_witness(cs.clone(), || {
|
.concat();
|
||||||
Ok(ci2.clone())
|
let cfE_circuit = CycleFoldCircuit::<Projective, GVar> {
|
||||||
})
|
_gc: PhantomData,
|
||||||
.unwrap();
|
r_bits: Some(r_bits.clone()),
|
||||||
let ci3Var =
|
p1: Some(ci1.clone().cmE),
|
||||||
CommittedInstanceInCycleFoldVar::<Projective, GVar>::new_witness(cs.clone(), || {
|
p2: Some(cmT),
|
||||||
Ok(ci3.clone())
|
p3: Some(ci3.clone().cmE),
|
||||||
})
|
x: Some(cfE_u_i_x.clone()),
|
||||||
.unwrap();
|
};
|
||||||
|
cfE_circuit.generate_constraints(cs.clone()).unwrap();
|
||||||
let nifs_cf_check = NIFSinCycleFoldGadget::<Projective, GVar>::verify(
|
|
||||||
r_bitsVar, cmTVar, ci1Var, ci2Var, ci3Var,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
nifs_cf_check.enforce_equal(&Boolean::<Fq>::TRUE).unwrap();
|
|
||||||
assert!(cs.is_satisfied().unwrap());
|
assert!(cs.is_satisfied().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -527,8 +494,8 @@ pub mod tests {
|
|||||||
// compute the challenge natively
|
// compute the challenge natively
|
||||||
let r_bits = CycleFoldChallengeGadget::<Projective, GVar>::get_challenge_native(
|
let r_bits = CycleFoldChallengeGadget::<Projective, GVar>::get_challenge_native(
|
||||||
&poseidon_config,
|
&poseidon_config,
|
||||||
u_i.clone(),
|
|
||||||
U_i.clone(),
|
U_i.clone(),
|
||||||
|
u_i.clone(),
|
||||||
cmT,
|
cmT,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@@ -549,8 +516,8 @@ pub mod tests {
|
|||||||
let r_bitsVar = CycleFoldChallengeGadget::<Projective, GVar>::get_challenge_gadget(
|
let r_bitsVar = CycleFoldChallengeGadget::<Projective, GVar>::get_challenge_gadget(
|
||||||
cs.clone(),
|
cs.clone(),
|
||||||
&poseidon_config,
|
&poseidon_config,
|
||||||
u_iVar,
|
|
||||||
U_iVar,
|
U_iVar,
|
||||||
|
u_iVar,
|
||||||
cmTVar,
|
cmTVar,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|||||||
@@ -68,9 +68,7 @@ where
|
|||||||
let circuit =
|
let circuit =
|
||||||
DeciderEthCircuit::<C1, GC1, C2, GC2, CP1, CP2>::from_nova::<FC>(folding_scheme.into());
|
DeciderEthCircuit::<C1, GC1, C2, GC2, CP1, CP2>::from_nova::<FC>(folding_scheme.into());
|
||||||
|
|
||||||
let proof = S::prove(pp, circuit.clone(), &mut rng).unwrap();
|
S::prove(pp, circuit.clone(), &mut rng).map_err(|e| Error::Other(e.to_string()))
|
||||||
|
|
||||||
Ok(proof)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verify(
|
fn verify(
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ use ark_r1cs_std::{
|
|||||||
fields::{fp::FpVar, nonnative::NonNativeFieldVar, FieldVar},
|
fields::{fp::FpVar, nonnative::NonNativeFieldVar, FieldVar},
|
||||||
groups::GroupOpsBounds,
|
groups::GroupOpsBounds,
|
||||||
prelude::CurveVar,
|
prelude::CurveVar,
|
||||||
ToConstraintFieldGadget,
|
|
||||||
};
|
};
|
||||||
use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, Namespace, SynthesisError};
|
use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, Namespace, SynthesisError};
|
||||||
use ark_std::{One, Zero};
|
use ark_std::{One, Zero};
|
||||||
@@ -344,19 +343,17 @@ where
|
|||||||
let zero_x = NonNativeFieldVar::<C1::BaseField, C1::ScalarField>::new_constant(
|
let zero_x = NonNativeFieldVar::<C1::BaseField, C1::ScalarField>::new_constant(
|
||||||
cs.clone(),
|
cs.clone(),
|
||||||
C1::BaseField::zero(),
|
C1::BaseField::zero(),
|
||||||
)?
|
)?;
|
||||||
.to_constraint_field()?;
|
|
||||||
let zero_y = NonNativeFieldVar::<C1::BaseField, C1::ScalarField>::new_constant(
|
let zero_y = NonNativeFieldVar::<C1::BaseField, C1::ScalarField>::new_constant(
|
||||||
cs.clone(),
|
cs.clone(),
|
||||||
C1::BaseField::one(),
|
C1::BaseField::one(),
|
||||||
)?
|
)?;
|
||||||
.to_constraint_field()?;
|
|
||||||
(u_i.cmE.x.is_eq(&zero_x)?).enforce_equal(&Boolean::TRUE)?;
|
(u_i.cmE.x.is_eq(&zero_x)?).enforce_equal(&Boolean::TRUE)?;
|
||||||
(u_i.cmE.y.is_eq(&zero_y)?).enforce_equal(&Boolean::TRUE)?;
|
(u_i.cmE.y.is_eq(&zero_y)?).enforce_equal(&Boolean::TRUE)?;
|
||||||
(u_i.u.is_one()?).enforce_equal(&Boolean::TRUE)?;
|
(u_i.u.is_one()?).enforce_equal(&Boolean::TRUE)?;
|
||||||
|
|
||||||
// 4. u_i.x == H(i, z_0, z_i, U_i)
|
// 4. u_i.x == H(i, z_0, z_i, U_i)
|
||||||
let u_i_x = U_i
|
let (u_i_x, _) = U_i
|
||||||
.clone()
|
.clone()
|
||||||
.hash(&crh_params, i.clone(), z_0.clone(), z_i.clone())?;
|
.hash(&crh_params, i.clone(), z_0.clone(), z_i.clone())?;
|
||||||
(u_i.x[0]).enforce_equal(&u_i_x)?;
|
(u_i.x[0]).enforce_equal(&u_i_x)?;
|
||||||
@@ -370,6 +367,7 @@ where
|
|||||||
// `#[cfg(not(test))]`
|
// `#[cfg(not(test))]`
|
||||||
use crate::commitment::pedersen::PedersenGadget;
|
use crate::commitment::pedersen::PedersenGadget;
|
||||||
use crate::folding::nova::cyclefold::{CycleFoldCommittedInstanceVar, CF_IO_LEN};
|
use crate::folding::nova::cyclefold::{CycleFoldCommittedInstanceVar, CF_IO_LEN};
|
||||||
|
use ark_r1cs_std::ToBitsGadget;
|
||||||
|
|
||||||
let cf_u_dummy_native = CommittedInstance::<C2>::dummy(CF_IO_LEN);
|
let cf_u_dummy_native = CommittedInstance::<C2>::dummy(CF_IO_LEN);
|
||||||
let w_dummy_native = Witness::<C2>::new(
|
let w_dummy_native = Witness::<C2>::new(
|
||||||
@@ -386,16 +384,20 @@ where
|
|||||||
// 5. check Pedersen commitments of cf_U_i.{cmE, cmW}
|
// 5. check Pedersen commitments of cf_U_i.{cmE, cmW}
|
||||||
let H = GC2::new_constant(cs.clone(), self.cf_pedersen_params.h)?;
|
let H = GC2::new_constant(cs.clone(), self.cf_pedersen_params.h)?;
|
||||||
let G = Vec::<GC2>::new_constant(cs.clone(), self.cf_pedersen_params.generators)?;
|
let G = Vec::<GC2>::new_constant(cs.clone(), self.cf_pedersen_params.generators)?;
|
||||||
|
let cf_W_i_E_bits: Result<Vec<Vec<Boolean<CF1<C1>>>>, SynthesisError> =
|
||||||
|
cf_W_i.E.iter().map(|E_i| E_i.to_bits_le()).collect();
|
||||||
|
let cf_W_i_W_bits: Result<Vec<Vec<Boolean<CF1<C1>>>>, SynthesisError> =
|
||||||
|
cf_W_i.W.iter().map(|W_i| W_i.to_bits_le()).collect();
|
||||||
|
|
||||||
let computed_cmE = PedersenGadget::<C2, GC2>::commit(
|
let computed_cmE = PedersenGadget::<C2, GC2>::commit(
|
||||||
H.clone(),
|
H.clone(),
|
||||||
G.clone(),
|
G.clone(),
|
||||||
cf_W_i.E.clone(),
|
cf_W_i_E_bits?,
|
||||||
cf_W_i.rE,
|
cf_W_i.rE.to_bits_le()?,
|
||||||
)?;
|
)?;
|
||||||
cf_U_i.cmE.enforce_equal(&computed_cmE)?;
|
cf_U_i.cmE.enforce_equal(&computed_cmE)?;
|
||||||
let computed_cmW =
|
let computed_cmW =
|
||||||
PedersenGadget::<C2, GC2>::commit(H, G, cf_W_i.W.clone(), cf_W_i.rW)?;
|
PedersenGadget::<C2, GC2>::commit(H, G, cf_W_i_W_bits?, cf_W_i.rW.to_bits_le()?)?;
|
||||||
cf_U_i.cmW.enforce_equal(&computed_cmW)?;
|
cf_U_i.cmW.enforce_equal(&computed_cmW)?;
|
||||||
|
|
||||||
let cf_r1cs = R1CSVar::<
|
let cf_r1cs = R1CSVar::<
|
||||||
@@ -641,17 +643,18 @@ pub mod tests {
|
|||||||
let ivc_v = nova.clone();
|
let ivc_v = nova.clone();
|
||||||
let verifier_params = VerifierParams::<Projective, Projective2> {
|
let verifier_params = VerifierParams::<Projective, Projective2> {
|
||||||
poseidon_config: poseidon_config.clone(),
|
poseidon_config: poseidon_config.clone(),
|
||||||
r1cs: ivc_v.r1cs,
|
r1cs: ivc_v.clone().r1cs,
|
||||||
cf_r1cs: ivc_v.cf_r1cs,
|
cf_r1cs: ivc_v.clone().cf_r1cs,
|
||||||
};
|
};
|
||||||
|
let (running_instance, incoming_instance, cyclefold_instance) = ivc_v.instances();
|
||||||
NOVA::verify(
|
NOVA::verify(
|
||||||
verifier_params,
|
verifier_params,
|
||||||
z_0,
|
z_0,
|
||||||
ivc_v.z_i,
|
ivc_v.z_i,
|
||||||
Fr::one(),
|
Fr::one(),
|
||||||
(ivc_v.U_i, ivc_v.W_i),
|
running_instance,
|
||||||
(ivc_v.u_i, ivc_v.w_i),
|
incoming_instance,
|
||||||
(ivc_v.cf_U_i, ivc_v.cf_W_i),
|
cyclefold_instance,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
|||||||
@@ -289,17 +289,19 @@ where
|
|||||||
/// Implements IVC.P of Nova+CycleFold
|
/// Implements IVC.P of Nova+CycleFold
|
||||||
fn prove_step(&mut self) -> Result<(), Error> {
|
fn prove_step(&mut self) -> Result<(), Error> {
|
||||||
let augmented_F_circuit: AugmentedFCircuit<C1, C2, GC2, FC>;
|
let augmented_F_circuit: AugmentedFCircuit<C1, C2, GC2, FC>;
|
||||||
let cf_circuit: CycleFoldCircuit<C1, GC1>;
|
let cfW_circuit: CycleFoldCircuit<C1, GC1>;
|
||||||
|
let cfE_circuit: CycleFoldCircuit<C1, GC1>;
|
||||||
|
|
||||||
let z_i1 = self.F.step_native(self.z_i.clone())?;
|
let z_i1 = self.F.step_native(self.z_i.clone())?;
|
||||||
|
|
||||||
// compute T and cmT for AugmentedFCircuit
|
// compute T and cmT for AugmentedFCircuit
|
||||||
let (T, cmT) = self.compute_cmT()?;
|
let (T, cmT) = self.compute_cmT()?;
|
||||||
|
|
||||||
|
// r_bits is the r used to the RLC of the F' instances
|
||||||
let r_bits = ChallengeGadget::<C1>::get_challenge_native(
|
let r_bits = ChallengeGadget::<C1>::get_challenge_native(
|
||||||
&self.poseidon_config,
|
&self.poseidon_config,
|
||||||
self.u_i.clone(),
|
|
||||||
self.U_i.clone(),
|
self.U_i.clone(),
|
||||||
|
self.u_i.clone(),
|
||||||
cmT,
|
cmT,
|
||||||
)?;
|
)?;
|
||||||
let r_Fr = C1::ScalarField::from_bigint(BigInteger::from_bits_le(&r_bits))
|
let r_Fr = C1::ScalarField::from_bigint(BigInteger::from_bits_le(&r_bits))
|
||||||
@@ -307,7 +309,7 @@ where
|
|||||||
|
|
||||||
// fold Nova instances
|
// fold Nova instances
|
||||||
let (W_i1, U_i1): (Witness<C1>, CommittedInstance<C1>) = NIFS::<C1, CP1>::fold_instances(
|
let (W_i1, U_i1): (Witness<C1>, CommittedInstance<C1>) = NIFS::<C1, CP1>::fold_instances(
|
||||||
r_Fr, &self.w_i, &self.u_i, &self.W_i, &self.U_i, &T, cmT,
|
r_Fr, &self.W_i, &self.U_i, &self.w_i, &self.u_i, &T, cmT,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// folded instance output (public input, x)
|
// folded instance output (public input, x)
|
||||||
@@ -333,75 +335,65 @@ where
|
|||||||
cmT: Some(cmT),
|
cmT: Some(cmT),
|
||||||
F: self.F,
|
F: self.F,
|
||||||
x: Some(u_i1_x),
|
x: Some(u_i1_x),
|
||||||
cf_u_i: None,
|
cf1_u_i: None,
|
||||||
|
cf2_u_i: None,
|
||||||
cf_U_i: None,
|
cf_U_i: None,
|
||||||
|
cf1_U_i1: None,
|
||||||
cf_U_i1: None,
|
cf_U_i1: None,
|
||||||
cf_cmT: None,
|
cf1_cmT: None,
|
||||||
cf_r_nonnat: None,
|
cf2_cmT: None,
|
||||||
|
cf1_r_nonnat: None,
|
||||||
|
cf2_r_nonnat: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
NIFS::<C1, CP1>::verify_folded_instance(r_Fr, &self.u_i, &self.U_i, &U_i1, &cmT)?;
|
NIFS::<C1, CP1>::verify_folded_instance(r_Fr, &self.U_i, &self.u_i, &U_i1, &cmT)?;
|
||||||
} else {
|
} else {
|
||||||
// CycleFold part:
|
// CycleFold part:
|
||||||
// get the vector used as public inputs 'x' in the CycleFold circuit
|
// get the vector used as public inputs 'x' in the CycleFold circuit
|
||||||
let cf_u_i_x = [
|
// cyclefold circuit for cmW
|
||||||
get_committed_instance_coordinates(&self.u_i),
|
let cfW_u_i_x = [
|
||||||
get_committed_instance_coordinates(&self.U_i),
|
get_cm_coordinates(&self.U_i.cmW),
|
||||||
get_committed_instance_coordinates(&U_i1),
|
get_cm_coordinates(&self.u_i.cmW),
|
||||||
|
get_cm_coordinates(&U_i1.cmW),
|
||||||
|
]
|
||||||
|
.concat();
|
||||||
|
// cyclefold circuit for cmE
|
||||||
|
let cfE_u_i_x = [
|
||||||
|
get_cm_coordinates(&self.U_i.cmE),
|
||||||
|
get_cm_coordinates(&self.u_i.cmE),
|
||||||
|
get_cm_coordinates(&U_i1.cmE),
|
||||||
]
|
]
|
||||||
.concat();
|
.concat();
|
||||||
|
|
||||||
cf_circuit = CycleFoldCircuit::<C1, GC1> {
|
cfW_circuit = CycleFoldCircuit::<C1, GC1> {
|
||||||
_gc: PhantomData,
|
_gc: PhantomData,
|
||||||
r_bits: Some(r_bits.clone()),
|
r_bits: Some(r_bits.clone()),
|
||||||
cmT: Some(cmT),
|
p1: Some(self.U_i.clone().cmW),
|
||||||
u_i: Some(self.u_i.clone()),
|
p2: Some(self.u_i.clone().cmW),
|
||||||
U_i: Some(self.U_i.clone()),
|
p3: Some(U_i1.clone().cmW),
|
||||||
U_i1: Some(U_i1.clone()),
|
x: Some(cfW_u_i_x.clone()),
|
||||||
x: Some(cf_u_i_x.clone()),
|
};
|
||||||
|
cfE_circuit = CycleFoldCircuit::<C1, GC1> {
|
||||||
|
_gc: PhantomData,
|
||||||
|
r_bits: Some(r_bits.clone()),
|
||||||
|
p1: Some(self.U_i.clone().cmE),
|
||||||
|
p2: Some(cmT),
|
||||||
|
p3: Some(U_i1.clone().cmE),
|
||||||
|
x: Some(cfE_u_i_x.clone()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let cs2 = ConstraintSystem::<C1::BaseField>::new_ref();
|
// fold self.cf_U_i + cfW_U -> folded running with cfW
|
||||||
cf_circuit.generate_constraints(cs2.clone())?;
|
let (_cfW_w_i, cfW_u_i, cfW_W_i1, cfW_U_i1, cfW_cmT, cfW_r1_Fq) = self
|
||||||
|
.fold_cyclefold_circuit(
|
||||||
let cs2 = cs2.into_inner().ok_or(Error::NoInnerConstraintSystem)?;
|
self.cf_W_i.clone(), // CycleFold running instance witness
|
||||||
let (cf_w_i, cf_x_i) = extract_w_x::<C1::BaseField>(&cs2);
|
self.cf_U_i.clone(), // CycleFold running instance
|
||||||
if cf_x_i != cf_u_i_x {
|
cfW_u_i_x,
|
||||||
return Err(Error::NotEqual);
|
cfW_circuit,
|
||||||
}
|
)?;
|
||||||
|
// fold [the output from folding self.cf_U_i + cfW_U] + cfE_U = folded_running_with_cfW + cfE
|
||||||
#[cfg(test)]
|
let (_cfE_w_i, cfE_u_i, cf_W_i1, cf_U_i1, cf_cmT, cf_r2_Fq) =
|
||||||
if cf_x_i.len() != CF_IO_LEN {
|
self.fold_cyclefold_circuit(cfW_W_i1, cfW_U_i1.clone(), cfE_u_i_x, cfE_circuit)?;
|
||||||
return Err(Error::NotExpectedLength(cf_x_i.len(), CF_IO_LEN));
|
|
||||||
}
|
|
||||||
|
|
||||||
// fold cyclefold instances
|
|
||||||
let cf_w_i = Witness::<C2>::new(cf_w_i.clone(), self.cf_r1cs.A.n_rows);
|
|
||||||
let cf_u_i: CommittedInstance<C2> =
|
|
||||||
cf_w_i.commit::<CP2>(&self.cf_cm_params, cf_x_i.clone())?;
|
|
||||||
|
|
||||||
// compute T* and cmT* for CycleFoldCircuit
|
|
||||||
let (cf_T, cf_cmT) = self.compute_cf_cmT(&cf_w_i, &cf_u_i)?;
|
|
||||||
|
|
||||||
let cf_r_bits = CycleFoldChallengeGadget::<C2, GC2>::get_challenge_native(
|
|
||||||
&self.poseidon_config,
|
|
||||||
cf_u_i.clone(),
|
|
||||||
self.cf_U_i.clone(),
|
|
||||||
cf_cmT,
|
|
||||||
)?;
|
|
||||||
let cf_r_Fq = C1::BaseField::from_bigint(BigInteger::from_bits_le(&cf_r_bits))
|
|
||||||
.ok_or(Error::OutOfBounds)?;
|
|
||||||
|
|
||||||
let (cf_W_i1, cf_U_i1) = NIFS::<C2, CP2>::fold_instances(
|
|
||||||
cf_r_Fq,
|
|
||||||
&self.cf_W_i,
|
|
||||||
&self.cf_U_i,
|
|
||||||
&cf_w_i,
|
|
||||||
&cf_u_i,
|
|
||||||
&cf_T,
|
|
||||||
cf_cmT,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
augmented_F_circuit = AugmentedFCircuit::<C1, C2, GC2, FC> {
|
augmented_F_circuit = AugmentedFCircuit::<C1, C2, GC2, FC> {
|
||||||
_gc2: PhantomData,
|
_gc2: PhantomData,
|
||||||
@@ -416,11 +408,15 @@ where
|
|||||||
F: self.F,
|
F: self.F,
|
||||||
x: Some(u_i1_x),
|
x: Some(u_i1_x),
|
||||||
// cyclefold values
|
// cyclefold values
|
||||||
cf_u_i: Some(cf_u_i.clone()),
|
cf1_u_i: Some(cfW_u_i.clone()),
|
||||||
|
cf2_u_i: Some(cfE_u_i.clone()),
|
||||||
cf_U_i: Some(self.cf_U_i.clone()),
|
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()),
|
cf_U_i1: Some(cf_U_i1.clone()),
|
||||||
cf_cmT: Some(cf_cmT),
|
cf1_cmT: Some(cfW_cmT),
|
||||||
cf_r_nonnat: Some(cf_r_Fq),
|
cf2_cmT: Some(cf_cmT),
|
||||||
|
cf1_r_nonnat: Some(cfW_r1_Fq),
|
||||||
|
cf2_r_nonnat: Some(cf_r2_Fq),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.cf_W_i = cf_W_i1.clone();
|
self.cf_W_i = cf_W_i1.clone();
|
||||||
@@ -428,7 +424,8 @@ where
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
{
|
{
|
||||||
self.cf_r1cs.check_instance_relation(&cf_w_i, &cf_u_i)?;
|
self.cf_r1cs.check_instance_relation(&_cfW_w_i, &cfW_u_i)?;
|
||||||
|
self.cf_r1cs.check_instance_relation(&_cfE_w_i, &cfE_u_i)?;
|
||||||
self.cf_r1cs
|
self.cf_r1cs
|
||||||
.check_relaxed_instance_relation(&self.cf_W_i, &self.cf_U_i)?;
|
.check_relaxed_instance_relation(&self.cf_W_i, &self.cf_U_i)?;
|
||||||
}
|
}
|
||||||
@@ -557,18 +554,94 @@ where
|
|||||||
&self,
|
&self,
|
||||||
cf_w_i: &Witness<C2>,
|
cf_w_i: &Witness<C2>,
|
||||||
cf_u_i: &CommittedInstance<C2>,
|
cf_u_i: &CommittedInstance<C2>,
|
||||||
|
cf_W_i: &Witness<C2>,
|
||||||
|
cf_U_i: &CommittedInstance<C2>,
|
||||||
) -> Result<(Vec<C2::ScalarField>, C2), Error> {
|
) -> Result<(Vec<C2::ScalarField>, C2), Error> {
|
||||||
NIFS::<C2, CP2>::compute_cyclefold_cmT(
|
NIFS::<C2, CP2>::compute_cyclefold_cmT(
|
||||||
&self.cf_cm_params,
|
&self.cf_cm_params,
|
||||||
&self.cf_r1cs,
|
&self.cf_r1cs,
|
||||||
cf_w_i,
|
cf_w_i,
|
||||||
cf_u_i,
|
cf_u_i,
|
||||||
&self.cf_W_i,
|
cf_W_i,
|
||||||
&self.cf_U_i,
|
cf_U_i,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<C1, GC1, C2, GC2, FC, CP1, CP2> Nova<C1, GC1, C2, GC2, FC, CP1, CP2>
|
||||||
|
where
|
||||||
|
C1: CurveGroup,
|
||||||
|
GC1: CurveVar<C1, CF2<C1>>,
|
||||||
|
C2: CurveGroup,
|
||||||
|
GC2: CurveVar<C2, CF2<C2>>,
|
||||||
|
FC: FCircuit<C1::ScalarField>,
|
||||||
|
CP1: CommitmentProver<C1>,
|
||||||
|
CP2: CommitmentProver<C2>,
|
||||||
|
<C1 as CurveGroup>::BaseField: PrimeField,
|
||||||
|
<C2 as CurveGroup>::BaseField: PrimeField,
|
||||||
|
<C1 as Group>::ScalarField: Absorb,
|
||||||
|
<C2 as Group>::ScalarField: Absorb,
|
||||||
|
C1: CurveGroup<BaseField = C2::ScalarField, ScalarField = C2::BaseField>,
|
||||||
|
for<'a> &'a GC1: GroupOpsBounds<'a, C1, GC1>,
|
||||||
|
for<'a> &'a GC2: GroupOpsBounds<'a, C2, GC2>,
|
||||||
|
{
|
||||||
|
// folds the given cyclefold circuit and its instances
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
|
fn fold_cyclefold_circuit(
|
||||||
|
&self,
|
||||||
|
cf_W_i: Witness<C2>, // witness of the running instance
|
||||||
|
cf_U_i: CommittedInstance<C2>, // running instance
|
||||||
|
cf_u_i_x: Vec<C2::ScalarField>,
|
||||||
|
cf_circuit: CycleFoldCircuit<C1, GC1>,
|
||||||
|
) -> Result<
|
||||||
|
(
|
||||||
|
Witness<C2>,
|
||||||
|
CommittedInstance<C2>, // u_i
|
||||||
|
Witness<C2>, // W_i1
|
||||||
|
CommittedInstance<C2>, // U_i1
|
||||||
|
C2, // cmT
|
||||||
|
C2::ScalarField, // r_Fq
|
||||||
|
),
|
||||||
|
Error,
|
||||||
|
> {
|
||||||
|
let cs2 = ConstraintSystem::<C1::BaseField>::new_ref();
|
||||||
|
cf_circuit.generate_constraints(cs2.clone())?;
|
||||||
|
|
||||||
|
let cs2 = cs2.into_inner().ok_or(Error::NoInnerConstraintSystem)?;
|
||||||
|
let (cf_w_i, cf_x_i) = extract_w_x::<C1::BaseField>(&cs2);
|
||||||
|
if cf_x_i != cf_u_i_x {
|
||||||
|
return Err(Error::NotEqual);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
if cf_x_i.len() != CF_IO_LEN {
|
||||||
|
return Err(Error::NotExpectedLength(cf_x_i.len(), CF_IO_LEN));
|
||||||
|
}
|
||||||
|
|
||||||
|
// fold cyclefold instances
|
||||||
|
let cf_w_i = Witness::<C2>::new(cf_w_i.clone(), self.cf_r1cs.A.n_rows);
|
||||||
|
let cf_u_i: CommittedInstance<C2> =
|
||||||
|
cf_w_i.commit::<CP2>(&self.cf_cm_params, cf_x_i.clone())?;
|
||||||
|
|
||||||
|
// compute T* and cmT* for CycleFoldCircuit
|
||||||
|
let (cf_T, cf_cmT) = self.compute_cf_cmT(&cf_w_i, &cf_u_i, &cf_W_i, &cf_U_i)?;
|
||||||
|
|
||||||
|
let cf_r_bits = CycleFoldChallengeGadget::<C2, GC2>::get_challenge_native(
|
||||||
|
&self.poseidon_config,
|
||||||
|
cf_U_i.clone(),
|
||||||
|
cf_u_i.clone(),
|
||||||
|
cf_cmT,
|
||||||
|
)?;
|
||||||
|
let cf_r_Fq = C1::BaseField::from_bigint(BigInteger::from_bits_le(&cf_r_bits))
|
||||||
|
.ok_or(Error::OutOfBounds)?;
|
||||||
|
|
||||||
|
let (cf_W_i1, cf_U_i1) = NIFS::<C2, CP2>::fold_instances(
|
||||||
|
cf_r_Fq, &cf_W_i, &cf_U_i, &cf_w_i, &cf_u_i, &cf_T, cf_cmT,
|
||||||
|
)?;
|
||||||
|
Ok((cf_w_i, cf_u_i, cf_W_i1, cf_U_i1, cf_cmT, cf_r_Fq))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// helper method to get the r1cs from the ConstraintSynthesizer
|
/// helper method to get the r1cs from the ConstraintSynthesizer
|
||||||
pub fn get_r1cs_from_cs<F: PrimeField>(
|
pub fn get_r1cs_from_cs<F: PrimeField>(
|
||||||
circuit: impl ConstraintSynthesizer<F>,
|
circuit: impl ConstraintSynthesizer<F>,
|
||||||
@@ -633,17 +706,11 @@ where
|
|||||||
Ok((r1cs.A.n_rows, cf_r1cs.A.n_rows))
|
Ok((r1cs.A.n_rows, cf_r1cs.A.n_rows))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_committed_instance_coordinates<C: CurveGroup>(
|
pub(crate) fn get_cm_coordinates<C: CurveGroup>(cm: &C) -> Vec<C::BaseField> {
|
||||||
u: &CommittedInstance<C>,
|
|
||||||
) -> Vec<C::BaseField> {
|
|
||||||
let zero = (&C::BaseField::zero(), &C::BaseField::one());
|
let zero = (&C::BaseField::zero(), &C::BaseField::one());
|
||||||
|
let cm = cm.into_affine();
|
||||||
let cmE = u.cmE.into_affine();
|
let (cm_x, cm_y) = cm.xy().unwrap_or(zero);
|
||||||
let (cmE_x, cmE_y) = cmE.xy().unwrap_or(zero);
|
vec![*cm_x, *cm_y]
|
||||||
|
|
||||||
let cmW = u.cmW.into_affine();
|
|
||||||
let (cmW_x, cmW_y) = cmW.xy().unwrap_or(zero);
|
|
||||||
vec![*cmE_x, *cmE_y, *cmW_x, *cmW_y]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -656,6 +723,8 @@ pub mod tests {
|
|||||||
use crate::frontend::tests::CubicFCircuit;
|
use crate::frontend::tests::CubicFCircuit;
|
||||||
use crate::transcript::poseidon::poseidon_test_config;
|
use crate::transcript::poseidon::poseidon_test_config;
|
||||||
|
|
||||||
|
/// This test tests the Nova+CycleFold IVC, and by consequence it is also testing the
|
||||||
|
/// AugmentedFCircuit
|
||||||
#[test]
|
#[test]
|
||||||
fn test_ivc() {
|
fn test_ivc() {
|
||||||
type NOVA = Nova<
|
type NOVA = Nova<
|
||||||
@@ -700,17 +769,18 @@ pub mod tests {
|
|||||||
|
|
||||||
let verifier_params = VerifierParams::<Projective, Projective2> {
|
let verifier_params = VerifierParams::<Projective, Projective2> {
|
||||||
poseidon_config,
|
poseidon_config,
|
||||||
r1cs: nova.r1cs,
|
r1cs: nova.clone().r1cs,
|
||||||
cf_r1cs: nova.cf_r1cs,
|
cf_r1cs: nova.clone().cf_r1cs,
|
||||||
};
|
};
|
||||||
|
let (running_instance, incoming_instance, cyclefold_instance) = nova.instances();
|
||||||
NOVA::verify(
|
NOVA::verify(
|
||||||
verifier_params,
|
verifier_params,
|
||||||
z_0,
|
z_0,
|
||||||
nova.z_i,
|
nova.z_i,
|
||||||
nova.i,
|
nova.i,
|
||||||
(nova.U_i, nova.W_i),
|
running_instance,
|
||||||
(nova.u_i, nova.w_i),
|
incoming_instance,
|
||||||
(nova.cf_U_i, nova.cf_W_i),
|
cyclefold_instance,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,8 +68,8 @@ where
|
|||||||
|
|
||||||
pub fn fold_committed_instance(
|
pub fn fold_committed_instance(
|
||||||
r: C::ScalarField,
|
r: C::ScalarField,
|
||||||
ci1: &CommittedInstance<C>,
|
ci1: &CommittedInstance<C>, // U_i
|
||||||
ci2: &CommittedInstance<C>,
|
ci2: &CommittedInstance<C>, // u_i
|
||||||
cmT: &C,
|
cmT: &C,
|
||||||
) -> CommittedInstance<C> {
|
) -> CommittedInstance<C> {
|
||||||
let r2 = r * r;
|
let r2 = r * r;
|
||||||
|
|||||||
@@ -104,7 +104,8 @@ where
|
|||||||
// returns the state at the current step
|
// returns the state at the current step
|
||||||
fn state(&self) -> Vec<C1::ScalarField>;
|
fn state(&self) -> Vec<C1::ScalarField>;
|
||||||
|
|
||||||
// returns the instances at the current step
|
// returns the instances at the current step, in the following order:
|
||||||
|
// (running_instance, incoming_instance, cyclefold_instance)
|
||||||
fn instances(
|
fn instances(
|
||||||
&self,
|
&self,
|
||||||
) -> (
|
) -> (
|
||||||
|
|||||||
@@ -15,6 +15,11 @@ pub fn mat_vec_mul_sparse<F: PrimeField, CF: PrimeField, FV: FieldVar<F, CF>>(
|
|||||||
let mut res = vec![FV::zero(); m.n_rows];
|
let mut res = vec![FV::zero(); m.n_rows];
|
||||||
for (row_i, row) in m.coeffs.iter().enumerate() {
|
for (row_i, row) in m.coeffs.iter().enumerate() {
|
||||||
for (value, col_i) in row.iter() {
|
for (value, col_i) in row.iter() {
|
||||||
|
if value.value().unwrap() == F::one() {
|
||||||
|
// no need to multiply by 1
|
||||||
|
res[row_i] += v[*col_i].clone();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
res[row_i] += value.clone().mul(&v[*col_i].clone());
|
res[row_i] += value.clone().mul(&v[*col_i].clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user