Browse Source

implement Nova's AugmentedFCircuit (#33)

* impl AugmentedFCircuit non-base case

* add multiple iterations to AugmentedFCircuit test

* implement base case on AugmentedFCircuit and test

* Update cmE of E=0-vec to work as zero point

Update cmE of E=0-vec to work as zero point instead of as cm(0-vec)

* patch r1cs-std dep to a cherry-picked version with the zero-scalar-mult fix

* refactor FCircuit to make it more suitable inside the AugmentedFCircuit
main
arnaucube 10 months ago
committed by GitHub
parent
commit
597ac27288
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 552 additions and 76 deletions
  1. +11
    -1
      Cargo.toml
  2. +5
    -5
      src/folding/circuits/cyclefold.rs
  3. +46
    -0
      src/folding/circuits/nonnative.rs
  4. +372
    -30
      src/folding/nova/circuits.rs
  5. +24
    -6
      src/folding/nova/mod.rs
  6. +69
    -22
      src/folding/nova/nifs.rs
  7. +25
    -12
      src/frontend/arkworks/mod.rs

+ 11
- 1
Cargo.toml

@ -10,7 +10,7 @@ ark-poly = "^0.4.0"
ark-std = "^0.4.0" ark-std = "^0.4.0"
ark-crypto-primitives = { version = "^0.4.0", default-features = false, features = ["r1cs", "sponge", "crh"] } ark-crypto-primitives = { version = "^0.4.0", default-features = false, features = ["r1cs", "sponge", "crh"] }
ark-relations = { version = "^0.4.0", default-features = false } ark-relations = { version = "^0.4.0", default-features = false }
ark-r1cs-std = { version = "^0.4.0", default-features = false }
ark-r1cs-std = { default-features = false } # use latest version from the patch
ark-circom = { git = "https://github.com/gakonst/ark-circom.git" } ark-circom = { git = "https://github.com/gakonst/ark-circom.git" }
thiserror = "1.0" thiserror = "1.0"
rayon = "1.7.0" rayon = "1.7.0"
@ -26,6 +26,8 @@ espresso_transcript = {git="https://github.com/EspressoSystems/hyperplonk", pack
ark-pallas = {version="0.4.0", features=["r1cs"]} ark-pallas = {version="0.4.0", features=["r1cs"]}
ark-vesta = {version="0.4.0"} ark-vesta = {version="0.4.0"}
ark-bn254 = "0.4.0" ark-bn254 = "0.4.0"
tracing = { version = "0.1", default-features = false, features = [ "attributes" ] }
tracing-subscriber = { version = "0.2" }
[features] [features]
default = ["parallel", "nova", "hypernova"] default = ["parallel", "nova", "hypernova"]
@ -37,3 +39,11 @@ parallel = [
"ark-ff/parallel", "ark-ff/parallel",
"ark-poly/parallel", "ark-poly/parallel",
] ]
# The following patch is to use a version of ark-r1cs-std compatible with
# v0.4.0 but that includes a cherry-picked commit from after v0.4.0 which fixes
# the in-circuit scalar multiplication of the zero point. The commit is from
# https://github.com/arkworks-rs/r1cs-std/pull/124, without including other
# changes done between v0.4.0 and this fix which would break compatibility.
[patch.crates-io]
ark-r1cs-std = { git = "https://github.com/arnaucube/ark-r1cs-std-cherry-picked/" }

+ 5
- 5
src/folding/circuits/cyclefold.rs

@ -21,9 +21,8 @@ impl>> ECRLC {
p1: GC, p1: GC,
p2: GC, p2: GC,
p3: GC, p3: GC,
) -> Result<(), SynthesisError> {
p3.enforce_equal(&(p1 + p2.scalar_mul_le(r_bits.iter())?))?;
Ok(())
) -> Result<Boolean<CF<C>>, SynthesisError> {
p3.is_eq(&(p1 + p2.scalar_mul_le(r_bits.iter())?))
} }
} }
@ -32,7 +31,7 @@ mod tests {
use super::*; use super::*;
use ark_ff::{BigInteger, PrimeField}; 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;
use ark_r1cs_std::{alloc::AllocVar, eq::EqGadget};
use ark_relations::r1cs::ConstraintSystem; use ark_relations::r1cs::ConstraintSystem;
use ark_std::UniformRand; use ark_std::UniformRand;
use std::ops::Mul; use std::ops::Mul;
@ -59,7 +58,8 @@ mod tests {
let p3Var = GVar::new_witness(cs.clone(), || Ok(p3)).unwrap(); let p3Var = GVar::new_witness(cs.clone(), || Ok(p3)).unwrap();
// check ECRLC circuit // check ECRLC circuit
ECRLC::<Projective, GVar>::check(rbitsVar, p1Var, p2Var, p3Var).unwrap();
let check_pass = ECRLC::<Projective, GVar>::check(rbitsVar, p1Var, p2Var, p3Var).unwrap();
check_pass.enforce_equal(&Boolean::<Fq>::TRUE).unwrap();
assert!(cs.is_satisfied().unwrap()); assert!(cs.is_satisfied().unwrap());
} }
} }

+ 46
- 0
src/folding/circuits/nonnative.rs

@ -6,6 +6,7 @@ use ark_r1cs_std::{
fields::nonnative::NonNativeFieldVar, fields::nonnative::NonNativeFieldVar,
}; };
use ark_relations::r1cs::{Namespace, SynthesisError}; use ark_relations::r1cs::{Namespace, SynthesisError};
use ark_std::{One, Zero};
use core::borrow::Borrow; use core::borrow::Borrow;
/// NonNativeAffineVar represents an elliptic curve point in Affine represenation in the non-native /// NonNativeAffineVar represents an elliptic curve point in Affine represenation in the non-native
@ -31,6 +32,19 @@ where
let cs = cs.into(); let cs = cs.into();
let affine = val.borrow().into_affine(); let affine = val.borrow().into_affine();
if affine.is_zero() {
let x = NonNativeFieldVar::<C::BaseField, C::ScalarField>::new_variable(
cs.clone(),
|| Ok(C::BaseField::zero()),
mode,
)?;
let y = NonNativeFieldVar::<C::BaseField, C::ScalarField>::new_variable(
cs.clone(),
|| Ok(C::BaseField::one()),
mode,
)?;
return Ok(Self { x, y });
}
let xy = affine.xy().unwrap(); let xy = affine.xy().unwrap();
let x = NonNativeFieldVar::<C::BaseField, C::ScalarField>::new_variable( let x = NonNativeFieldVar::<C::BaseField, C::ScalarField>::new_variable(
cs.clone(), cs.clone(),
@ -58,6 +72,20 @@ where
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField, <C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
{ {
let affine = p.into_affine(); let affine = p.into_affine();
if affine.is_zero() {
let x =
AllocatedNonNativeFieldVar::<C::BaseField, C::ScalarField>::get_limbs_representations(
&C::BaseField::zero(),
OptimizationType::Weight,
)?;
let y =
AllocatedNonNativeFieldVar::<C::BaseField, C::ScalarField>::get_limbs_representations(
&C::BaseField::one(),
OptimizationType::Weight,
)?;
return Ok((x, y));
}
let (x, y) = affine.xy().unwrap(); let (x, y) = affine.xy().unwrap();
let x = AllocatedNonNativeFieldVar::<C::BaseField, C::ScalarField>::get_limbs_representations( let x = AllocatedNonNativeFieldVar::<C::BaseField, C::ScalarField>::get_limbs_representations(
x, x,
@ -69,3 +97,21 @@ where
)?; )?;
Ok((x, y)) Ok((x, y))
} }
#[cfg(test)]
mod tests {
use super::*;
use ark_pallas::{Fq, Fr, Projective};
use ark_r1cs_std::alloc::AllocVar;
use ark_relations::r1cs::ConstraintSystem;
use ark_std::Zero;
#[test]
fn test_alloc_nonnativeaffinevar_zero() {
let cs = ConstraintSystem::<Fr>::new_ref();
// 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();
}
}

+ 372
- 30
src/folding/nova/circuits.rs

@ -2,19 +2,20 @@ use ark_crypto_primitives::crh::{
poseidon::constraints::{CRHGadget, CRHParametersVar}, poseidon::constraints::{CRHGadget, CRHParametersVar},
CRHSchemeGadget, CRHSchemeGadget,
}; };
use ark_crypto_primitives::sponge::Absorb;
use ark_crypto_primitives::sponge::{poseidon::PoseidonConfig, Absorb};
use ark_ec::{AffineRepr, CurveGroup, Group}; use ark_ec::{AffineRepr, CurveGroup, Group};
use ark_ff::Field;
use ark_ff::{Field, PrimeField};
use ark_r1cs_std::{ use ark_r1cs_std::{
alloc::{AllocVar, AllocationMode}, alloc::{AllocVar, AllocationMode},
boolean::Boolean, boolean::Boolean,
eq::EqGadget, eq::EqGadget,
fields::fp::FpVar,
fields::{fp::FpVar, FieldVar},
groups::GroupOpsBounds, groups::GroupOpsBounds,
prelude::CurveVar, prelude::CurveVar,
ToConstraintFieldGadget, ToConstraintFieldGadget,
}; };
use ark_relations::r1cs::{Namespace, SynthesisError};
use ark_relations::r1cs::{ConstraintSynthesizer, ConstraintSystemRef, Namespace, SynthesisError};
use ark_std::Zero;
use core::{borrow::Borrow, marker::PhantomData}; use core::{borrow::Borrow, marker::PhantomData};
use super::CommittedInstance; use super::CommittedInstance;
@ -32,10 +33,8 @@ pub type CF2 = <::BaseField as Field>::BasePrimeField;
/// constraints field (E1::Fr, where E1 is the main curve). /// constraints field (E1::Fr, where E1 is the main curve).
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct CommittedInstanceVar<C: CurveGroup> { pub struct CommittedInstanceVar<C: CurveGroup> {
_c: PhantomData<C>,
u: FpVar<C::ScalarField>, u: FpVar<C::ScalarField>,
x: Vec<FpVar<C::ScalarField>>, x: Vec<FpVar<C::ScalarField>>,
#[allow(dead_code)] // tmp while we don't have the code of the AugmentedFGadget
cmE: NonNativeAffineVar<CF2<C>, C::ScalarField>, cmE: NonNativeAffineVar<CF2<C>, C::ScalarField>,
cmW: NonNativeAffineVar<CF2<C>, C::ScalarField>, cmW: NonNativeAffineVar<CF2<C>, C::ScalarField>,
} }
@ -43,7 +42,7 @@ pub struct CommittedInstanceVar {
impl<C> AllocVar<CommittedInstance<C>, CF1<C>> for CommittedInstanceVar<C> impl<C> AllocVar<CommittedInstance<C>, CF1<C>> for CommittedInstanceVar<C>
where where
C: CurveGroup, C: CurveGroup,
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
<C as ark_ec::CurveGroup>::BaseField: PrimeField,
{ {
fn new_variable<T: Borrow<CommittedInstance<C>>>( fn new_variable<T: Borrow<CommittedInstance<C>>>(
cs: impl Into<Namespace<CF1<C>>>, cs: impl Into<Namespace<CF1<C>>>,
@ -68,13 +67,7 @@ where
mode, mode,
)?; )?;
Ok(Self {
_c: PhantomData,
u,
x,
cmE,
cmW,
})
Ok(Self { u, x, cmE, cmW })
}) })
} }
} }
@ -88,7 +81,6 @@ where
/// 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`.
#[allow(dead_code)] // tmp while we don't have the code of the AugmentedFGadget
fn hash( fn hash(
self, self,
crh_params: &CRHParametersVar<CF1<C>>, crh_params: &CRHParametersVar<CF1<C>>,
@ -164,9 +156,9 @@ where
ci1: CommittedInstanceVar<C>, ci1: CommittedInstanceVar<C>,
ci2: CommittedInstanceVar<C>, ci2: CommittedInstanceVar<C>,
ci3: CommittedInstanceVar<C>, ci3: CommittedInstanceVar<C>,
) -> Result<(), SynthesisError> {
) -> Result<Boolean<CF1<C>>, SynthesisError> {
// ensure that: ci3.u == ci1.u + r * ci2.u // ensure that: ci3.u == ci1.u + r * ci2.u
ci3.u.enforce_equal(&(ci1.u + r.clone() * ci2.u))?;
let first_check = ci3.u.is_eq(&(ci1.u + r.clone() * ci2.u))?;
// ensure that: ci3.x == ci1.x + r * ci2.x // ensure that: ci3.x == ci1.x + r * ci2.x
let x_rlc = ci1 let x_rlc = ci1
@ -175,9 +167,9 @@ where
.zip(ci2.x) .zip(ci2.x)
.map(|(a, b)| a + &r * &b) .map(|(a, b)| a + &r * &b)
.collect::<Vec<FpVar<CF1<C>>>>(); .collect::<Vec<FpVar<CF1<C>>>>();
x_rlc.enforce_equal(&ci3.x)?;
let second_check = x_rlc.is_eq(&ci3.x)?;
Ok(())
first_check.and(&second_check)
} }
} }
@ -199,14 +191,145 @@ where
ci1: CommittedInstanceCycleFoldVar<C, GC>, ci1: CommittedInstanceCycleFoldVar<C, GC>,
ci2: CommittedInstanceCycleFoldVar<C, GC>, ci2: CommittedInstanceCycleFoldVar<C, GC>,
ci3: CommittedInstanceCycleFoldVar<C, GC>, ci3: CommittedInstanceCycleFoldVar<C, GC>,
) -> Result<(), SynthesisError> {
) -> Result<Boolean<CF2<C>>, SynthesisError> {
// cm(E) check: ci3.cmE == ci1.cmE + r * cmT + r^2 * ci2.cmE // cm(E) check: ci3.cmE == ci1.cmE + r * cmT + r^2 * ci2.cmE
ci3.cmE.enforce_equal(
let first_check = ci3.cmE.is_eq(
&((ci2.cmE.scalar_mul_le(r_bits.iter())? + cmT).scalar_mul_le(r_bits.iter())? &((ci2.cmE.scalar_mul_le(r_bits.iter())? + cmT).scalar_mul_le(r_bits.iter())?
+ ci1.cmE), + ci1.cmE),
)?; )?;
// cm(W) check: ci3.cmW == ci1.cmW + r * ci2.cmW // cm(W) check: ci3.cmW == ci1.cmW + r * ci2.cmW
ECRLC::<C, GC>::check(r_bits, ci1.cmW, ci2.cmW, ci3.cmW)?;
let second_check = ECRLC::<C, GC>::check(r_bits, ci1.cmW, ci2.cmW, ci3.cmW)?;
first_check.and(&second_check)
}
}
/// FCircuit defines the trait of the circuit of the F function, which is the one being executed
/// inside the agmented F' function.
pub trait FCircuit<F: PrimeField>: Copy {
/// method that returns z_i (input), z_{i+1} (output)
fn public(self) -> (F, F);
/// method that computes the next state values in place, assigning z_{i+1} into z_i, and
/// computing the new z_i
fn step_native(&mut self);
fn step_circuit(
self,
cs: ConstraintSystemRef<F>,
z_i: FpVar<F>,
) -> Result<FpVar<F>, SynthesisError>;
}
/// AugmentedFCircuit implements the F' circuit (augmented F) defined in
/// [Nova](https://eprint.iacr.org/2021/370.pdf).
#[derive(Debug, Clone)]
pub struct AugmentedFCircuit<C: CurveGroup, FC: FCircuit<CF1<C>>> {
pub poseidon_config: PoseidonConfig<CF1<C>>,
pub i: Option<CF1<C>>,
pub z_0: Option<C::ScalarField>,
pub z_i: Option<C::ScalarField>,
pub u_i: Option<CommittedInstance<C>>,
pub U_i: Option<CommittedInstance<C>>,
pub U_i1: Option<CommittedInstance<C>>,
pub cmT: Option<C>,
pub r: Option<C::ScalarField>, // This will not be an input and derived from a hash internally in the circuit (poseidon transcript)
pub F: FC, // F circuit
pub x: Option<CF1<C>>, // public inputs (u_{i+1}.x)
}
impl<C: CurveGroup, FC: FCircuit<CF1<C>>> ConstraintSynthesizer<CF1<C>> for AugmentedFCircuit<C, FC>
where
C: CurveGroup,
<C as CurveGroup>::BaseField: PrimeField,
<C as Group>::ScalarField: Absorb,
{
fn generate_constraints(self, cs: ConstraintSystemRef<CF1<C>>) -> Result<(), SynthesisError> {
let i =
FpVar::<CF1<C>>::new_witness(cs.clone(), || Ok(self.i.unwrap_or_else(CF1::<C>::zero)))?;
let z_0 = FpVar::<CF1<C>>::new_witness(cs.clone(), || {
Ok(self.z_0.unwrap_or_else(CF1::<C>::zero))
})?;
let z_i = FpVar::<CF1<C>>::new_witness(cs.clone(), || {
Ok(self.z_i.unwrap_or_else(CF1::<C>::zero))
})?;
// get z_{i+1} from the F circuit
let z_i1 = self.F.step_circuit(cs.clone(), z_i.clone())?;
let u_dummy_native = CommittedInstance {
cmE: C::zero(),
u: C::ScalarField::zero(),
cmW: C::zero(),
x: vec![CF1::<C>::zero()],
};
let u_dummy =
CommittedInstanceVar::<C>::new_witness(cs.clone(), || Ok(u_dummy_native.clone()))?;
let u_i = CommittedInstanceVar::<C>::new_witness(cs.clone(), || {
Ok(self.u_i.unwrap_or_else(|| u_dummy_native.clone()))
})?;
let U_i = CommittedInstanceVar::<C>::new_witness(cs.clone(), || {
Ok(self.U_i.unwrap_or_else(|| u_dummy_native.clone()))
})?;
let U_i1 = CommittedInstanceVar::<C>::new_witness(cs.clone(), || {
Ok(self.U_i1.unwrap_or_else(|| u_dummy_native.clone()))
})?;
let _cmT =
NonNativeAffineVar::new_witness(cs.clone(), || Ok(self.cmT.unwrap_or_else(C::zero)))?;
let r =
FpVar::<CF1<C>>::new_witness(cs.clone(), || Ok(self.r.unwrap_or_else(CF1::<C>::zero)))?; // r will come from higher level transcript
let x =
FpVar::<CF1<C>>::new_input(cs.clone(), || Ok(self.x.unwrap_or_else(CF1::<C>::zero)))?;
let crh_params =
CRHParametersVar::<C::ScalarField>::new_constant(cs.clone(), self.poseidon_config)?;
let zero = FpVar::<CF1<C>>::new_constant(cs.clone(), CF1::<C>::zero())?;
let is_basecase = i.is_eq(&zero)?;
let is_not_basecase = i.is_neq(&zero)?;
// 1. h_{i+1} = u_i.X == H(i, z_0, z_i, U_i)
let u_i_x = U_i
.clone()
.hash(&crh_params, i.clone(), z_0.clone(), z_i.clone())?;
// check that h == u_i.x
(u_i.x[0]).conditional_enforce_equal(&u_i_x, &is_not_basecase)?;
// 2. u_i.cmE==cm(0), u_i.u==1
(u_i.cmE.x.is_eq(&u_dummy.cmE.x)?)
.conditional_enforce_equal(&Boolean::TRUE, &is_not_basecase)?;
(u_i.cmE.y.is_eq(&u_dummy.cmE.y)?)
.conditional_enforce_equal(&Boolean::TRUE, &is_not_basecase)?;
(u_i.u.is_one()?).conditional_enforce_equal(&Boolean::TRUE, &is_not_basecase)?;
// 3. nifs.verify, checks that folding u_i & U_i obtains U_{i+1}.
// Notice that NIFSGadget::verify is not checking the folding of cmE & cmW, since it will
// be done on the other curve.
let nifs_check = NIFSGadget::<C>::verify(r, u_i, U_i.clone(), U_i1.clone())?;
nifs_check.conditional_enforce_equal(&Boolean::TRUE, &is_not_basecase)?;
// 4. (base case) u_{i+1}.X == H(1, z_0, F(z_0)=F(z_i)=z_i1, U_i) (with U_i being dummy)
let u_i1_x_basecase = U_i.hash(
&crh_params,
FpVar::<CF1<C>>::one(),
z_0.clone(),
z_i1.clone(),
)?;
// 4. (non-base case). 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.hash(
&crh_params,
i + FpVar::<CF1<C>>::one(),
z_0.clone(),
z_i1.clone(),
)?;
// if i==0: check x==u_{i+1}.x_basecase
u_i1_x_basecase.conditional_enforce_equal(&x, &is_basecase)?;
// else: check x==u_{i+1}.x
u_i1_x.conditional_enforce_equal(&x, &is_not_basecase)?;
Ok(()) Ok(())
} }
} }
@ -214,18 +337,50 @@ where
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use ark_ff::{BigInteger, PrimeField};
use ark_ff::BigInteger;
use ark_pallas::{constraints::GVar, Fq, Fr, Projective}; use ark_pallas::{constraints::GVar, Fq, Fr, Projective};
use ark_r1cs_std::{alloc::AllocVar, R1CSVar}; use ark_r1cs_std::{alloc::AllocVar, R1CSVar};
use ark_relations::r1cs::ConstraintSystem;
use ark_relations::r1cs::{ConstraintLayer, ConstraintSystem, TracingMode};
use ark_std::One;
use ark_std::UniformRand; use ark_std::UniformRand;
use tracing_subscriber::layer::SubscriberExt;
use crate::ccs::r1cs::tests::{get_test_r1cs, get_test_z}; use crate::ccs::r1cs::tests::{get_test_r1cs, get_test_z};
use crate::folding::nova::{nifs::NIFS, Witness};
use crate::constants::N_BITS_CHALLENGE;
use crate::folding::nova::{check_instance_relation, nifs::NIFS, Witness};
use crate::frontend::arkworks::{extract_r1cs, extract_z};
use crate::pedersen::Pedersen; use crate::pedersen::Pedersen;
use crate::transcript::poseidon::{tests::poseidon_test_config, PoseidonTranscript}; use crate::transcript::poseidon::{tests::poseidon_test_config, PoseidonTranscript};
use crate::transcript::Transcript; use crate::transcript::Transcript;
#[derive(Clone, Copy, Debug)]
/// TestFCircuit is a variation of `x^3 + x + 5 = y` (as in
/// src/frontend/arkworks/mod.rs#tests::TestCircuit), adapted to have 2 public inputs which are
/// used as the state. `z_i` is used as `x`, and `z_{i+1}` is used as `y`, and at the next
/// step, `z_{i+1}` will be assigned to `z_i`, and a new `z+{i+1}` will be computted.
pub struct TestFCircuit<F: PrimeField> {
z_i: F, // z_i
z_i1: F, // z_{i+1}
}
impl<F: PrimeField> FCircuit<F> for TestFCircuit<F> {
fn public(self) -> (F, F) {
(self.z_i, self.z_i1)
}
fn step_native(&mut self) {
self.z_i = self.z_i1;
self.z_i1 = self.z_i * self.z_i * self.z_i + self.z_i + F::from(5_u32);
}
fn step_circuit(
self,
cs: ConstraintSystemRef<F>,
z_i: FpVar<F>,
) -> Result<FpVar<F>, SynthesisError> {
let five = FpVar::<F>::new_constant(cs.clone(), F::from(5u32))?;
Ok(&z_i * &z_i * &z_i + &z_i + &five)
}
}
#[test] #[test]
fn test_committed_instance_var() { fn test_committed_instance_var() {
let mut rng = ark_std::test_rng(); let mut rng = ark_std::test_rng();
@ -266,7 +421,7 @@ mod tests {
let w2 = Witness::<Projective>::new(w2.clone(), r1cs.A.n_rows); let w2 = Witness::<Projective>::new(w2.clone(), r1cs.A.n_rows);
let mut rng = ark_std::test_rng(); let mut rng = ark_std::test_rng();
let pedersen_params = Pedersen::<Projective>::new_params(&mut rng, r1cs.A.n_cols);
let pedersen_params = Pedersen::<Projective>::new_params(&mut rng, r1cs.A.n_rows);
// compute committed instances // compute committed instances
let ci1 = w1.commit(&pedersen_params, x1.clone()); let ci1 = w1.commit(&pedersen_params, x1.clone());
@ -275,12 +430,15 @@ mod tests {
// get challenge from transcript // get challenge from transcript
let poseidon_config = poseidon_test_config::<Fr>(); let poseidon_config = poseidon_test_config::<Fr>();
let mut tr = PoseidonTranscript::<Projective>::new(&poseidon_config); let mut tr = PoseidonTranscript::<Projective>::new(&poseidon_config);
let r_bits = tr.get_challenge_nbits(128);
let r_bits = tr.get_challenge_nbits(N_BITS_CHALLENGE);
let r_Fr = Fr::from_bigint(BigInteger::from_bits_le(&r_bits)).unwrap(); let r_Fr = Fr::from_bigint(BigInteger::from_bits_le(&r_bits)).unwrap();
let (_w3, ci3, _T, cmT) = let (_w3, ci3, _T, cmT) =
NIFS::<Projective>::prove(&pedersen_params, r_Fr, &r1cs, &w1, &ci1, &w2, &ci2).unwrap(); NIFS::<Projective>::prove(&pedersen_params, r_Fr, &r1cs, &w1, &ci1, &w2, &ci2).unwrap();
let ci3_verifier = NIFS::<Projective>::verify(r_Fr, &ci1, &ci2, &cmT);
assert_eq!(ci3_verifier, ci3);
let cs = ConstraintSystem::<Fr>::new_ref(); let cs = ConstraintSystem::<Fr>::new_ref();
let rVar = FpVar::<Fr>::new_witness(cs.clone(), || Ok(r_Fr)).unwrap(); let rVar = FpVar::<Fr>::new_witness(cs.clone(), || Ok(r_Fr)).unwrap();
@ -294,13 +452,14 @@ mod tests {
CommittedInstanceVar::<Projective>::new_witness(cs.clone(), || Ok(ci3.clone())) CommittedInstanceVar::<Projective>::new_witness(cs.clone(), || Ok(ci3.clone()))
.unwrap(); .unwrap();
NIFSGadget::<Projective>::verify(
let nifs_check = NIFSGadget::<Projective>::verify(
rVar.clone(), rVar.clone(),
ci1Var.clone(), ci1Var.clone(),
ci2Var.clone(), ci2Var.clone(),
ci3Var.clone(), ci3Var.clone(),
) )
.unwrap(); .unwrap();
nifs_check.enforce_equal(&Boolean::<Fr>::TRUE).unwrap();
assert!(cs.is_satisfied().unwrap()); assert!(cs.is_satisfied().unwrap());
// cs_CC is the Constraint System on the Curve Cycle auxiliary curve constraints field // cs_CC is the Constraint System on the Curve Cycle auxiliary curve constraints field
@ -326,8 +485,11 @@ mod tests {
}) })
.unwrap(); .unwrap();
NIFSCycleFoldGadget::<Projective, GVar>::verify(r_bitsVar, cmTVar, ci1Var, ci2Var, ci3Var)
.unwrap();
let nifs_cf_check = NIFSCycleFoldGadget::<Projective, GVar>::verify(
r_bitsVar, cmTVar, ci1Var, ci2Var, ci3Var,
)
.unwrap();
nifs_cf_check.enforce_equal(&Boolean::<Fq>::TRUE).unwrap();
assert!(cs_CC.is_satisfied().unwrap()); assert!(cs_CC.is_satisfied().unwrap());
} }
@ -366,4 +528,184 @@ mod tests {
// check that the natively computed and in-circuit computed hashes match // check that the natively computed and in-circuit computed hashes match
assert_eq!(hVar.value().unwrap(), h); assert_eq!(hVar.value().unwrap(), h);
} }
#[test]
/// test_augmented_f_circuit folds the TestFCircuit 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 test_F_circuit_dummy = TestFCircuit::<Fr> {
z_i: Fr::zero(),
z_i1: Fr::zero(),
};
let mut augmented_F_circuit = AugmentedFCircuit::<Projective, TestFCircuit<Fr>> {
poseidon_config: poseidon_config.clone(),
i: None,
z_0: None,
z_i: None,
u_i: None,
U_i: None,
U_i1: None,
cmT: None,
r: None,
F: test_F_circuit_dummy,
x: None,
};
augmented_F_circuit
.generate_constraints(cs.clone())
.unwrap();
cs.finalize();
let cs = cs.into_inner().unwrap();
let r1cs = extract_r1cs::<Fr>(&cs);
let z = extract_z::<Fr>(&cs); // includes 1 and public inputs
let (w, x) = r1cs.split_z(&z);
let F_witness_len = w.len();
let mut tr = PoseidonTranscript::<Projective>::new(&poseidon_config);
let pedersen_params = Pedersen::<Projective>::new_params(&mut rng, r1cs.A.n_rows);
// first step
let z_0 = Fr::from(3_u32);
let mut z_i = z_0;
let mut z_i1 = Fr::from(35_u32);
// set the circuit to be folded with z_i=z_0=3 and z_{i+1}=35 (initial values)
let mut test_F_circuit = TestFCircuit::<Fr> { z_i, z_i1 };
let w_dummy = Witness::<Projective>::new(vec![Fr::zero(); F_witness_len], r1cs.A.n_rows);
let u_dummy = CommittedInstance::<Projective>::dummy(x.len());
// Wi is a 'dummy witness', all zeroes, but with the size corresponding to the R1CS that
// we're working with.
// set U_i <-- dummay instance
let mut W_i = w_dummy.clone();
let mut U_i = u_dummy.clone();
check_instance_relation(&r1cs, &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 _T, mut cmT) = (
w_dummy.clone(),
u_dummy.clone(),
vec![],
Projective::generator(),
);
// as expected, dummy instances pass the relaxed_r1cs check
check_instance_relation(&r1cs, &W_i1, &U_i1).unwrap();
let mut i = Fr::zero();
let mut u_i1_x: Fr;
let n_steps: usize = 4;
for _ in 0..n_steps {
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, z_i1).unwrap();
// base case
augmented_F_circuit = AugmentedFCircuit::<Projective, TestFCircuit<Fr>> {
poseidon_config: poseidon_config.clone(),
i: Some(i), // = 0
z_0: Some(z_0), // = z_i=3
z_i: Some(z_i),
u_i: Some(u_i.clone()), // = dummy
U_i: Some(U_i.clone()), // = dummy
U_i1: Some(U_i1.clone()), // = dummy
cmT: Some(cmT),
r: Some(Fr::one()),
F: test_F_circuit,
x: Some(u_i1_x),
};
} else {
// get challenge from transcript (since this is a test, we skip absorbing values to
// the transcript for simplicity)
let r_bits = tr.get_challenge_nbits(N_BITS_CHALLENGE);
let r_Fr = Fr::from_bigint(BigInteger::from_bits_le(&r_bits)).unwrap();
check_instance_relation(&r1cs, &w_i, &u_i).unwrap();
check_instance_relation(&r1cs, &W_i, &U_i).unwrap();
// U_{i+1}
(W_i1, U_i1, _T, cmT) = NIFS::<Projective>::prove(
&pedersen_params,
r_Fr,
&r1cs,
&w_i,
&u_i,
&W_i,
&U_i,
)
.unwrap();
check_instance_relation(&r1cs, &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, z_i1)
.unwrap();
augmented_F_circuit = AugmentedFCircuit::<Projective, TestFCircuit<Fr>> {
poseidon_config: poseidon_config.clone(),
i: Some(i),
z_0: Some(z_0),
z_i: Some(z_i),
u_i: Some(u_i),
U_i: Some(U_i.clone()),
U_i1: Some(U_i1.clone()),
cmT: Some(cmT),
r: Some(r_Fr),
F: test_F_circuit,
x: Some(u_i1_x),
};
}
let cs = ConstraintSystem::<Fr>::new_ref();
augmented_F_circuit
.generate_constraints(cs.clone())
.unwrap();
let is_satisfied = cs.is_satisfied().unwrap();
if !is_satisfied {
println!("{:?}", cs.which_is_unsatisfied());
}
assert!(is_satisfied);
cs.finalize();
let cs = cs.into_inner().unwrap();
// notice that here we use 'Z' (uppercase) to denote the 'z-vector' as in the paper,
// not the value 'z_i' (lowercase) which is the next state
let Z_i1 = extract_z::<Fr>(&cs);
let (w_i1, x_i1) = r1cs.split_z(&Z_i1);
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_params, vec![u_i1_x]);
check_instance_relation(&r1cs, &w_i, &u_i).unwrap();
check_instance_relation(&r1cs, &W_i1, &U_i1).unwrap();
// set values for next iteration
i += Fr::one();
test_F_circuit.step_native(); // advance the F circuit state
(z_i, z_i1) = test_F_circuit.public();
U_i = U_i1.clone();
W_i = W_i1.clone();
}
}
} }

+ 24
- 6
src/folding/nova/mod.rs

@ -7,8 +7,10 @@ use ark_ec::{CurveGroup, Group};
use ark_std::fmt::Debug; use ark_std::fmt::Debug;
use ark_std::{One, Zero}; use ark_std::{One, Zero};
use crate::ccs::r1cs::R1CS;
use crate::folding::circuits::nonnative::point_to_nonnative_limbs; use crate::folding::circuits::nonnative::point_to_nonnative_limbs;
use crate::pedersen::{Params as PedersenParams, Pedersen}; use crate::pedersen::{Params as PedersenParams, Pedersen};
use crate::utils::vec::is_zero_vec;
use crate::Error; use crate::Error;
pub mod circuits; pub mod circuits;
@ -27,12 +29,12 @@ where
<C as Group>::ScalarField: Absorb, <C as Group>::ScalarField: Absorb,
<C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField, <C as ark_ec::CurveGroup>::BaseField: ark_ff::PrimeField,
{ {
pub fn empty() -> Self {
CommittedInstance {
pub fn dummy(io_len: usize) -> Self {
Self {
cmE: C::zero(), cmE: C::zero(),
u: C::ScalarField::one(),
u: C::ScalarField::zero(),
cmW: C::zero(), cmW: C::zero(),
x: Vec::new(),
x: vec![C::ScalarField::zero(); io_len],
} }
} }
@ -81,7 +83,7 @@ where
pub fn new(w: Vec<C::ScalarField>, e_len: usize) -> Self { pub fn new(w: Vec<C::ScalarField>, e_len: usize) -> Self {
Self { Self {
E: vec![C::ScalarField::zero(); e_len], E: vec![C::ScalarField::zero(); e_len],
rE: C::ScalarField::one(),
rE: C::ScalarField::zero(), // because we use C::zero() as cmE
W: w, W: w,
rW: C::ScalarField::one(), rW: C::ScalarField::one(),
} }
@ -91,7 +93,10 @@ where
params: &PedersenParams<C>, params: &PedersenParams<C>,
x: Vec<C::ScalarField>, x: Vec<C::ScalarField>,
) -> CommittedInstance<C> { ) -> CommittedInstance<C> {
let cmE = Pedersen::commit(params, &self.E, &self.rE);
let mut cmE = C::zero();
if !is_zero_vec::<C::ScalarField>(&self.E) {
cmE = Pedersen::commit(params, &self.E, &self.rE);
}
let cmW = Pedersen::commit(params, &self.W, &self.rW); let cmW = Pedersen::commit(params, &self.W, &self.rW);
CommittedInstance { CommittedInstance {
cmE, cmE,
@ -101,3 +106,16 @@ where
} }
} }
} }
pub fn check_instance_relation<C: CurveGroup>(
r1cs: &R1CS<C::ScalarField>,
W: &Witness<C>,
U: &CommittedInstance<C>,
) -> Result<(), Error> {
let mut rel_r1cs = r1cs.clone().relax();
rel_r1cs.u = U.u;
rel_r1cs.E = W.E.clone();
let Z: Vec<C::ScalarField> = [vec![U.u], U.x.to_vec(), W.W.to_vec()].concat();
rel_r1cs.check_relation(&Z)
}

+ 69
- 22
src/folding/nova/nifs.rs

@ -181,19 +181,55 @@ where
} }
#[cfg(test)] #[cfg(test)]
mod tests {
pub mod tests {
use super::*; use super::*;
use crate::ccs::r1cs::tests::{get_test_r1cs, get_test_z};
use crate::transcript::poseidon::{tests::poseidon_test_config, PoseidonTranscript};
use ark_ff::PrimeField; use ark_ff::PrimeField;
use ark_pallas::{Fr, Projective}; use ark_pallas::{Fr, Projective};
use ark_std::UniformRand;
use ark_std::{ops::Mul, UniformRand, Zero};
use crate::ccs::r1cs::tests::{get_test_r1cs, get_test_z};
use crate::transcript::poseidon::{tests::poseidon_test_config, PoseidonTranscript};
use crate::utils::vec::vec_scalar_mul;
pub fn check_relaxed_r1cs<F: PrimeField>(r1cs: &R1CS<F>, z: &[F], u: &F, E: &[F]) -> bool {
let Az = mat_vec_mul_sparse(&r1cs.A, z);
let Bz = mat_vec_mul_sparse(&r1cs.B, z);
let Cz = mat_vec_mul_sparse(&r1cs.C, z);
hadamard(&Az, &Bz).unwrap() == vec_add(&vec_scalar_mul(&Cz, u), E).unwrap()
}
// fold 2 dummy instances and check that the folded instance holds the relaxed R1CS relation
#[test]
fn test_nifs_fold_dummy() {
let r1cs = get_test_r1cs::<Fr>();
let z1 = get_test_z(3);
let (w1, x1) = r1cs.split_z(&z1);
pub fn check_relaxed_r1cs<F: PrimeField>(r1cs: &R1CS<F>, z: Vec<F>, u: F, E: &[F]) {
let Az = mat_vec_mul_sparse(&r1cs.A, &z);
let Bz = mat_vec_mul_sparse(&r1cs.B, &z);
let Cz = mat_vec_mul_sparse(&r1cs.C, &z);
assert_eq!(hadamard(&Az, &Bz), vec_add(&vec_scalar_mul(&Cz, &u), E));
let mut rng = ark_std::test_rng();
let pedersen_params = Pedersen::<Projective>::new_params(&mut rng, r1cs.A.n_cols);
// dummy instance, witness and public inputs zeroes
let w_dummy = Witness::<Projective>::new(vec![Fr::zero(); w1.len()], r1cs.A.n_rows);
let mut u_dummy = w_dummy.commit(&pedersen_params, vec![Fr::zero(); x1.len()]);
u_dummy.u = Fr::zero();
let w_i = w_dummy.clone();
let u_i = u_dummy.clone();
let W_i = w_dummy.clone();
let U_i = u_dummy.clone();
let z_dummy = vec![Fr::zero(); z1.len()];
assert!(check_relaxed_r1cs(&r1cs, &z_dummy, &u_i.u, &w_i.E));
assert!(check_relaxed_r1cs(&r1cs, &z_dummy, &U_i.u, &W_i.E));
let r_Fr = Fr::from(3_u32);
let (W_i1, U_i1, _, _) =
NIFS::<Projective>::prove(&pedersen_params, r_Fr, &r1cs, &w_i, &u_i, &W_i, &U_i)
.unwrap();
let z: Vec<Fr> = [vec![U_i1.u], U_i1.x.to_vec(), W_i1.W.to_vec()].concat();
assert_eq!(z.len(), z1.len());
assert!(check_relaxed_r1cs(&r1cs, &z, &U_i1.u, &W_i1.E));
} }
// fold 2 instances into one // fold 2 instances into one
@ -218,11 +254,12 @@ mod tests {
let ci2 = w2.commit(&pedersen_params, x2.clone()); let ci2 = w2.commit(&pedersen_params, x2.clone());
// NIFS.P // NIFS.P
let (w3, _, T, cmT) =
let (w3, ci3_aux, T, cmT) =
NIFS::<Projective>::prove(&pedersen_params, r, &r1cs, &w1, &ci1, &w2, &ci2).unwrap(); NIFS::<Projective>::prove(&pedersen_params, r, &r1cs, &w1, &ci1, &w2, &ci2).unwrap();
// NIFS.V // NIFS.V
let ci3 = NIFS::<Projective>::verify(r, &ci1, &ci2, &cmT); let ci3 = NIFS::<Projective>::verify(r, &ci1, &ci2, &cmT);
assert_eq!(ci3, ci3_aux);
// naive check that the folded witness satisfies the relaxed r1cs // naive check that the folded witness satisfies the relaxed r1cs
let z3: Vec<Fr> = [vec![ci3.u], ci3.x.to_vec(), w3.W.to_vec()].concat(); let z3: Vec<Fr> = [vec![ci3.u], ci3.x.to_vec(), w3.W.to_vec()].concat();
@ -230,9 +267,9 @@ mod tests {
let z3_aux = vec_add(&z1, &vec_scalar_mul(&z2, &r)).unwrap(); let z3_aux = vec_add(&z1, &vec_scalar_mul(&z2, &r)).unwrap();
assert_eq!(z3, z3_aux); assert_eq!(z3, z3_aux);
// check that relations hold for the 2 inputted instances and the folded one // check that relations hold for the 2 inputted instances and the folded one
check_relaxed_r1cs(&r1cs, z1, ci1.u, &w1.E);
check_relaxed_r1cs(&r1cs, z2, ci2.u, &w2.E);
check_relaxed_r1cs(&r1cs, z3, ci3.u, &w3.E);
assert!(check_relaxed_r1cs(&r1cs, &z1, &ci1.u, &w1.E));
assert!(check_relaxed_r1cs(&r1cs, &z2, &ci2.u, &w2.E));
assert!(check_relaxed_r1cs(&r1cs, &z3, &ci3.u, &w3.E));
// check that folded commitments from folded instance (ci) are equal to folding the // check that folded commitments from folded instance (ci) are equal to folding the
// use folded rE, rW to commit w3 // use folded rE, rW to commit w3
@ -240,6 +277,10 @@ mod tests {
assert_eq!(ci3_expected.cmE, ci3.cmE); assert_eq!(ci3_expected.cmE, ci3.cmE);
assert_eq!(ci3_expected.cmW, ci3.cmW); assert_eq!(ci3_expected.cmW, ci3.cmW);
// next equalities should hold since we started from two cmE of zero-vector E's
assert_eq!(ci3.cmE, cmT.mul(r));
assert_eq!(w3.E, vec_scalar_mul(&T, &r));
// NIFS.Verify_Folded_Instance: // NIFS.Verify_Folded_Instance:
NIFS::<Projective>::verify_folded_instance(r, &ci1, &ci2, &ci3, &cmT).unwrap(); NIFS::<Projective>::verify_folded_instance(r, &ci1, &ci2, &ci3, &cmT).unwrap();
@ -259,6 +300,7 @@ mod tests {
&cmT, &cmT,
) )
.unwrap(); .unwrap();
NIFS::<Projective>::verify_commitments( NIFS::<Projective>::verify_commitments(
&mut transcript_v, &mut transcript_v,
&pedersen_params, &pedersen_params,
@ -281,12 +323,12 @@ mod tests {
// prepare the running instance // prepare the running instance
let mut running_instance_w = Witness::<Projective>::new(w.clone(), r1cs.A.n_rows); let mut running_instance_w = Witness::<Projective>::new(w.clone(), r1cs.A.n_rows);
let mut running_committed_instance = running_instance_w.commit(&pedersen_params, x); let mut running_committed_instance = running_instance_w.commit(&pedersen_params, x);
check_relaxed_r1cs(
assert!(check_relaxed_r1cs(
&r1cs, &r1cs,
z,
running_committed_instance.u,
&z,
&running_committed_instance.u,
&running_instance_w.E, &running_instance_w.E,
);
));
let num_iters = 10; let num_iters = 10;
for i in 0..num_iters { for i in 0..num_iters {
@ -295,12 +337,12 @@ mod tests {
let (w, x) = r1cs.split_z(&incomming_instance_z); let (w, x) = r1cs.split_z(&incomming_instance_z);
let incomming_instance_w = Witness::<Projective>::new(w.clone(), r1cs.A.n_rows); let incomming_instance_w = Witness::<Projective>::new(w.clone(), r1cs.A.n_rows);
let incomming_committed_instance = incomming_instance_w.commit(&pedersen_params, x); let incomming_committed_instance = incomming_instance_w.commit(&pedersen_params, x);
check_relaxed_r1cs(
assert!(check_relaxed_r1cs(
&r1cs, &r1cs,
incomming_instance_z.clone(),
incomming_committed_instance.u,
&incomming_instance_z.clone(),
&incomming_committed_instance.u,
&incomming_instance_w.E, &incomming_instance_w.E,
);
));
let r = Fr::rand(&mut rng); // folding challenge would come from the transcript let r = Fr::rand(&mut rng); // folding challenge would come from the transcript
@ -331,7 +373,12 @@ mod tests {
] ]
.concat(); .concat();
check_relaxed_r1cs(&r1cs, folded_z, folded_committed_instance.u, &folded_w.E);
assert!(check_relaxed_r1cs(
&r1cs,
&folded_z,
&folded_committed_instance.u,
&folded_w.E
));
// set running_instance for next loop iteration // set running_instance for next loop iteration
running_instance_w = folded_w; running_instance_w = folded_w;

+ 25
- 12
src/frontend/arkworks/mod.rs

@ -8,6 +8,17 @@ use crate::utils::vec::SparseMatrix;
/// extracts arkworks ConstraintSystem matrices into crate::utils::vec::SparseMatrix format, and /// extracts arkworks ConstraintSystem matrices into crate::utils::vec::SparseMatrix format, and
/// extracts public inputs and witness into z vector. Returns a tuple containing (R1CS, z). /// extracts public inputs and witness into z vector. Returns a tuple containing (R1CS, z).
pub fn extract_r1cs_and_z<F: PrimeField>(cs: &ConstraintSystem<F>) -> (R1CS<F>, Vec<F>) { pub fn extract_r1cs_and_z<F: PrimeField>(cs: &ConstraintSystem<F>) -> (R1CS<F>, Vec<F>) {
let r1cs = extract_r1cs(cs);
// z = (1, x, w)
let z = extract_z(cs);
(r1cs, z)
}
/// extracts arkworks ConstraintSystem matrices into crate::utils::vec::SparseMatrix format as R1CS
/// struct.
pub fn extract_r1cs<F: PrimeField>(cs: &ConstraintSystem<F>) -> R1CS<F> {
let m = cs.to_matrices().unwrap(); let m = cs.to_matrices().unwrap();
let n_rows = cs.num_constraints; let n_rows = cs.num_constraints;
@ -29,23 +40,23 @@ pub fn extract_r1cs_and_z(cs: &ConstraintSystem) -> (R1CS,
coeffs: m.c, coeffs: m.c,
}; };
R1CS::<F> {
l: cs.num_instance_variables - 1, // -1 to substract the first '1'
A,
B,
C,
}
}
/// extracts public inputs and witness into z vector from arkworks ConstraintSystem.
pub fn extract_z<F: PrimeField>(cs: &ConstraintSystem<F>) -> Vec<F> {
// z = (1, x, w) // z = (1, x, w)
let z: Vec<F> = [
[
// 1 is already included in cs.instance_assignment // 1 is already included in cs.instance_assignment
cs.instance_assignment.clone(), cs.instance_assignment.clone(),
cs.witness_assignment.clone(), cs.witness_assignment.clone(),
] ]
.concat();
(
R1CS::<F> {
l: cs.num_instance_variables,
A,
B,
C,
},
z,
)
.concat()
} }
#[cfg(test)] #[cfg(test)]
@ -96,5 +107,7 @@ pub mod tests {
let (r1cs, z) = extract_r1cs_and_z::<Fr>(&cs); let (r1cs, z) = extract_r1cs_and_z::<Fr>(&cs);
r1cs.check_relation(&z).unwrap(); r1cs.check_relation(&z).unwrap();
// ensure that number of public inputs (l) is 1
assert_eq!(r1cs.l, 1);
} }
} }

Loading…
Cancel
Save