mirror of
https://github.com/arnaucube/sonobe.git
synced 2026-01-09 23:41:30 +01:00
Implement Nova's NIFS.Verify circuits (with CycleFold) (#11)
* Implement Nova's NIFS.Verify circuits (with CycleFold) - Add circuit for NIFS.Verify on the main curve to check the folded `u` & `x` - Add circuit for NIFS.Verify on the CycleFold's auxiliary curve to check the folded `cm(E)` & `cm(W)` - Add transcript.get_challenge_nbits - Add tests for utils::vec.rs * replace bls12-377 & bw6-761 by pallas & vesta curves (only affects tests) We will use pallas & vesta curves (for tests only, the non-tests code uses generics) for the logic that does not require pairings, and while Grumpkin is not available (https://github.com/privacy-scaling-explorations/folding-schemes/issues/12). * update links to papers to markdown style
This commit is contained in:
@@ -21,8 +21,8 @@ espresso_transcript = {git="https://github.com/EspressoSystems/hyperplonk", pack
|
|||||||
|
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
ark-bls12-377 = {version="0.4.0", features=["r1cs"]}
|
ark-pallas = {version="0.4.0", features=["r1cs"]}
|
||||||
ark-bw6-761 = {version="0.4.0"}
|
ark-vesta = {version="0.4.0"}
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["parallel"]
|
default = ["parallel"]
|
||||||
|
|||||||
@@ -10,12 +10,12 @@ pub mod r1cs;
|
|||||||
use r1cs::R1CS;
|
use r1cs::R1CS;
|
||||||
|
|
||||||
/// CCS represents the Customizable Constraint Systems structure defined in
|
/// CCS represents the Customizable Constraint Systems structure defined in
|
||||||
/// https://eprint.iacr.org/2023/552
|
/// the [CCS paper](https://eprint.iacr.org/2023/552)
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
pub struct CCS<C: CurveGroup> {
|
pub struct CCS<C: CurveGroup> {
|
||||||
/// m: number of columns in M_i (such that M_i \in F^{m, n})
|
/// m: number of rows in M_i (such that M_i \in F^{m, n})
|
||||||
pub m: usize,
|
pub m: usize,
|
||||||
/// n = |z|, number of rows in M_i
|
/// n = |z|, number of cols in M_i
|
||||||
pub n: usize,
|
pub n: usize,
|
||||||
/// l = |io|, size of public input/output
|
/// l = |io|, size of public input/output
|
||||||
pub l: usize,
|
pub l: usize,
|
||||||
@@ -73,13 +73,13 @@ impl<C: CurveGroup> CCS<C> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<C: CurveGroup> CCS<C> {
|
impl<C: CurveGroup> CCS<C> {
|
||||||
pub fn from_r1cs(r1cs: R1CS<C::ScalarField>, io_len: usize) -> Self {
|
pub fn from_r1cs(r1cs: R1CS<C::ScalarField>) -> Self {
|
||||||
let m = r1cs.A.n_cols;
|
let m = r1cs.A.n_rows;
|
||||||
let n = r1cs.A.n_rows;
|
let n = r1cs.A.n_cols;
|
||||||
CCS {
|
CCS {
|
||||||
m,
|
m,
|
||||||
n,
|
n,
|
||||||
l: io_len,
|
l: r1cs.l,
|
||||||
s: log2(m) as usize,
|
s: log2(m) as usize,
|
||||||
s_prime: log2(n) as usize,
|
s_prime: log2(n) as usize,
|
||||||
t: 3,
|
t: 3,
|
||||||
@@ -105,17 +105,17 @@ impl<C: CurveGroup> CCS<C> {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::ccs::r1cs::tests::{get_test_r1cs, get_test_z};
|
use crate::ccs::r1cs::tests::{get_test_r1cs, get_test_z};
|
||||||
use ark_bls12_377::G1Projective;
|
use ark_pallas::Projective;
|
||||||
|
|
||||||
pub fn get_test_ccs<C: CurveGroup>() -> CCS<C> {
|
pub fn get_test_ccs<C: CurveGroup>() -> CCS<C> {
|
||||||
let r1cs = get_test_r1cs::<C::ScalarField>();
|
let r1cs = get_test_r1cs::<C::ScalarField>();
|
||||||
CCS::<C>::from_r1cs(r1cs, 1)
|
CCS::<C>::from_r1cs(r1cs)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Test that a basic CCS relation can be satisfied
|
/// Test that a basic CCS relation can be satisfied
|
||||||
#[test]
|
#[test]
|
||||||
fn test_ccs_relation() {
|
fn test_ccs_relation() {
|
||||||
let ccs = get_test_ccs::<G1Projective>();
|
let ccs = get_test_ccs::<Projective>();
|
||||||
let z = get_test_z(3);
|
let z = get_test_z(3);
|
||||||
|
|
||||||
ccs.check_relation(&z).unwrap();
|
ccs.check_relation(&z).unwrap();
|
||||||
|
|||||||
@@ -19,24 +19,7 @@ impl<F: PrimeField> R1CS<F> {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod tests {
|
pub mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
use crate::utils::vec::tests::{to_F_matrix, to_F_vec};
|
||||||
pub fn to_F_matrix<F: PrimeField>(M: Vec<Vec<usize>>) -> Vec<Vec<F>> {
|
|
||||||
let mut R: Vec<Vec<F>> = vec![Vec::new(); M.len()];
|
|
||||||
for i in 0..M.len() {
|
|
||||||
R[i] = vec![F::zero(); M[i].len()];
|
|
||||||
for j in 0..M[i].len() {
|
|
||||||
R[i][j] = F::from(M[i][j] as u64);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
R
|
|
||||||
}
|
|
||||||
pub fn to_F_vec<F: PrimeField>(z: Vec<usize>) -> Vec<F> {
|
|
||||||
let mut r: Vec<F> = vec![F::zero(); z.len()];
|
|
||||||
for i in 0..z.len() {
|
|
||||||
r[i] = F::from(z[i] as u64);
|
|
||||||
}
|
|
||||||
r
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_test_r1cs<F: PrimeField>() -> R1CS<F> {
|
pub fn get_test_r1cs<F: PrimeField>() -> R1CS<F> {
|
||||||
// R1CS for: x^3 + x + 5 = y (example from article
|
// R1CS for: x^3 + x + 5 = y (example from article
|
||||||
@@ -72,8 +55,6 @@ pub mod tests {
|
|||||||
input * input, // x^2
|
input * input, // x^2
|
||||||
input * input * input, // x^2 * x
|
input * input * input, // x^2 * x
|
||||||
input * input * input + input, // x^3 + x
|
input * input * input + input, // x^3 + x
|
||||||
0, // pad to pow of 2
|
|
||||||
0,
|
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1
src/constants.rs
Normal file
1
src/constants.rs
Normal file
@@ -0,0 +1 @@
|
|||||||
|
pub const N_BITS_CHALLENGE: usize = 250;
|
||||||
@@ -1,26 +1,28 @@
|
|||||||
/// Implements the C_{EC} circuit described in CycleFold paper https://eprint.iacr.org/2023/1192.pdf
|
/// Implements the C_{EC} circuit described in [CycleFold paper](https://eprint.iacr.org/2023/1192.pdf)
|
||||||
use ark_ec::CurveGroup;
|
use ark_ec::CurveGroup;
|
||||||
use ark_r1cs_std::{fields::nonnative::NonNativeFieldVar, prelude::CurveVar, ToBitsGadget};
|
use ark_r1cs_std::{boolean::Boolean, prelude::CurveVar};
|
||||||
use ark_relations::r1cs::SynthesisError;
|
use ark_relations::r1cs::SynthesisError;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
use super::ConstraintF;
|
use super::CF;
|
||||||
|
|
||||||
/// ECRLC implements gadget that checks the Elliptic Curve points RandomLinearCombination described
|
/// ECRLC implements gadget that checks the Elliptic Curve points RandomLinearCombination described
|
||||||
/// in CycleFold (https://eprint.iacr.org/2023/1192.pdf).
|
/// in [CycleFold](https://eprint.iacr.org/2023/1192.pdf).
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ECRLC<C: CurveGroup, GC: CurveVar<C, ConstraintF<C>>> {
|
pub struct ECRLC<C: CurveGroup, GC: CurveVar<C, CF<C>>> {
|
||||||
_c: PhantomData<C>,
|
_c: PhantomData<C>,
|
||||||
_gc: PhantomData<GC>,
|
_gc: PhantomData<GC>,
|
||||||
}
|
}
|
||||||
impl<C: CurveGroup, GC: CurveVar<C, ConstraintF<C>>> ECRLC<C, GC> {
|
impl<C: CurveGroup, GC: CurveVar<C, CF<C>>> ECRLC<C, GC> {
|
||||||
pub fn check(
|
pub fn check(
|
||||||
r: NonNativeFieldVar<C::ScalarField, ConstraintF<C>>,
|
// get r in bits format, so it can be reused across many instances of ECRLC gadget,
|
||||||
|
// reducing the number of constraints needed
|
||||||
|
r_bits: Vec<Boolean<CF<C>>>,
|
||||||
p1: GC,
|
p1: GC,
|
||||||
p2: GC,
|
p2: GC,
|
||||||
p3: GC,
|
p3: GC,
|
||||||
) -> Result<(), SynthesisError> {
|
) -> Result<(), SynthesisError> {
|
||||||
p3.enforce_equal(&(p1 + p2.scalar_mul_le(r.to_bits_le()?.iter())?))?;
|
p3.enforce_equal(&(p1 + p2.scalar_mul_le(r_bits.iter())?))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -28,35 +30,36 @@ impl<C: CurveGroup, GC: CurveVar<C, ConstraintF<C>>> ECRLC<C, GC> {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use ark_bls12_377::{constraints::G1Var, Fq, Fr, G1Projective};
|
use ark_ff::{BigInteger, PrimeField};
|
||||||
|
use ark_pallas::{constraints::GVar, Fq, Fr, Projective};
|
||||||
use ark_r1cs_std::alloc::AllocVar;
|
use ark_r1cs_std::alloc::AllocVar;
|
||||||
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;
|
||||||
|
|
||||||
/// Let Curve1=bls12-377::G1 and Curve2=bw6-761::G1. Here we have our constraint system will
|
/// Let Curve1=pallas and Curve2=vesta. Here our constraints system will work over Curve2::Fr =
|
||||||
/// work over Curve2::Fr = bw6-761::Fr (=bls12-377::Fq), thus our points are P_i \in Curve1
|
/// vesta::Fr (=pallas::Fq), thus our points are P_i \in Curve1 (=pasta).
|
||||||
/// (=bls12-377).
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_ecrlc_check() {
|
fn test_ecrlc_check() {
|
||||||
let mut rng = ark_std::test_rng();
|
let mut rng = ark_std::test_rng();
|
||||||
|
|
||||||
let r = Fr::rand(&mut rng);
|
let r = Fr::rand(&mut rng);
|
||||||
let p1 = G1Projective::rand(&mut rng);
|
let p1 = Projective::rand(&mut rng);
|
||||||
let p2 = G1Projective::rand(&mut rng);
|
let p2 = Projective::rand(&mut rng);
|
||||||
let p3 = p1 + p2.mul(r);
|
let p3 = p1 + p2.mul(r);
|
||||||
|
|
||||||
let cs = ConstraintSystem::<Fq>::new_ref(); // CS over Curve2::Fr = Curve1::Fq
|
let cs = ConstraintSystem::<Fq>::new_ref(); // CS over Curve2::Fr = Curve1::Fq
|
||||||
|
|
||||||
// prepare circuit inputs
|
// prepare circuit inputs
|
||||||
let rVar = NonNativeFieldVar::<Fr, Fq>::new_witness(cs.clone(), || Ok(r)).unwrap();
|
let rbitsVar: Vec<Boolean<Fq>> =
|
||||||
let p1Var = G1Var::new_witness(cs.clone(), || Ok(p1)).unwrap();
|
Vec::new_witness(cs.clone(), || Ok(r.into_bigint().to_bits_le())).unwrap();
|
||||||
let p2Var = G1Var::new_witness(cs.clone(), || Ok(p2)).unwrap();
|
|
||||||
let p3Var = G1Var::new_witness(cs.clone(), || Ok(p3)).unwrap();
|
let p1Var = GVar::new_witness(cs.clone(), || Ok(p1)).unwrap();
|
||||||
|
let p2Var = GVar::new_witness(cs.clone(), || Ok(p2)).unwrap();
|
||||||
|
let p3Var = GVar::new_witness(cs.clone(), || Ok(p3)).unwrap();
|
||||||
|
|
||||||
// check ECRLC circuit
|
// check ECRLC circuit
|
||||||
ECRLC::<G1Projective, G1Var>::check(rVar, p1Var, p2Var, p3Var).unwrap();
|
ECRLC::<Projective, GVar>::check(rbitsVar, p1Var, p2Var, p3Var).unwrap();
|
||||||
assert!(cs.is_satisfied().unwrap());
|
assert!(cs.is_satisfied().unwrap());
|
||||||
// dbg!(cs.num_constraints());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,4 +4,5 @@ use ark_ff::Field;
|
|||||||
|
|
||||||
pub mod cyclefold;
|
pub mod cyclefold;
|
||||||
|
|
||||||
pub type ConstraintF<C> = <<C as CurveGroup>::BaseField as Field>::BasePrimeField;
|
// CF represents the constraints field
|
||||||
|
pub type CF<C> = <<C as CurveGroup>::BaseField as Field>::BasePrimeField;
|
||||||
|
|||||||
273
src/folding/nova/circuits.rs
Normal file
273
src/folding/nova/circuits.rs
Normal file
@@ -0,0 +1,273 @@
|
|||||||
|
use ark_ec::{AffineRepr, CurveGroup};
|
||||||
|
use ark_ff::Field;
|
||||||
|
use ark_r1cs_std::{
|
||||||
|
alloc::{AllocVar, AllocationMode},
|
||||||
|
boolean::Boolean,
|
||||||
|
eq::EqGadget,
|
||||||
|
fields::fp::FpVar,
|
||||||
|
groups::GroupOpsBounds,
|
||||||
|
prelude::CurveVar,
|
||||||
|
};
|
||||||
|
use ark_relations::r1cs::{Namespace, SynthesisError};
|
||||||
|
use core::{borrow::Borrow, marker::PhantomData};
|
||||||
|
|
||||||
|
use super::CommittedInstance;
|
||||||
|
use crate::folding::circuits::cyclefold::ECRLC;
|
||||||
|
|
||||||
|
/// CF1 represents the ConstraintField used for the main Nova circuit which is over E1::Fr.
|
||||||
|
pub type CF1<C> = <<C as CurveGroup>::Affine as AffineRepr>::ScalarField;
|
||||||
|
/// CF2 represents the ConstraintField used for the CycleFold circuit which is over E2::Fr=E1::Fq.
|
||||||
|
pub type CF2<C> = <<C as CurveGroup>::BaseField as Field>::BasePrimeField;
|
||||||
|
|
||||||
|
/// CommittedInstance on E1 contains the u and x values which are folded on the main Nova
|
||||||
|
/// constraints field (E1::Fr).
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct CommittedInstanceE1Var<C: CurveGroup> {
|
||||||
|
_c: PhantomData<C>,
|
||||||
|
u: FpVar<C::ScalarField>,
|
||||||
|
x: Vec<FpVar<C::ScalarField>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C> AllocVar<CommittedInstance<C>, CF1<C>> for CommittedInstanceE1Var<C>
|
||||||
|
where
|
||||||
|
C: CurveGroup,
|
||||||
|
{
|
||||||
|
fn new_variable<T: Borrow<CommittedInstance<C>>>(
|
||||||
|
cs: impl Into<Namespace<CF1<C>>>,
|
||||||
|
f: impl FnOnce() -> Result<T, SynthesisError>,
|
||||||
|
mode: AllocationMode,
|
||||||
|
) -> Result<Self, SynthesisError> {
|
||||||
|
f().and_then(|val| {
|
||||||
|
let cs = cs.into();
|
||||||
|
|
||||||
|
let u = FpVar::<C::ScalarField>::new_variable(cs.clone(), || Ok(val.borrow().u), mode)?;
|
||||||
|
let x: Vec<FpVar<C::ScalarField>> =
|
||||||
|
Vec::new_variable(cs, || Ok(val.borrow().x.clone()), mode)?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
_c: PhantomData,
|
||||||
|
u,
|
||||||
|
x,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// CommittedInstance on E2 contains the commitments to E and W, which are folded on the auxiliary
|
||||||
|
/// curve constraints field (E2::Fr = E1::Fq).
|
||||||
|
pub struct CommittedInstanceE2Var<C: CurveGroup, GC: CurveVar<C, CF2<C>>>
|
||||||
|
where
|
||||||
|
for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
|
||||||
|
{
|
||||||
|
_c: PhantomData<C>,
|
||||||
|
cmE: GC,
|
||||||
|
cmW: GC,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C, GC> AllocVar<CommittedInstance<C>, CF2<C>> for CommittedInstanceE2Var<C, GC>
|
||||||
|
where
|
||||||
|
C: CurveGroup,
|
||||||
|
GC: CurveVar<C, CF2<C>>,
|
||||||
|
for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
|
||||||
|
{
|
||||||
|
fn new_variable<T: Borrow<CommittedInstance<C>>>(
|
||||||
|
cs: impl Into<Namespace<CF2<C>>>,
|
||||||
|
f: impl FnOnce() -> Result<T, SynthesisError>,
|
||||||
|
mode: AllocationMode,
|
||||||
|
) -> Result<Self, SynthesisError> {
|
||||||
|
f().and_then(|val| {
|
||||||
|
let cs = cs.into();
|
||||||
|
|
||||||
|
let cmE = GC::new_variable(cs.clone(), || Ok(val.borrow().cmE), mode)?;
|
||||||
|
let cmW = GC::new_variable(cs.clone(), || Ok(val.borrow().cmW), mode)?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
_c: PhantomData,
|
||||||
|
cmE,
|
||||||
|
cmW,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implements the circuit that does the checks of the Non-Interactive Folding Scheme Verifier
|
||||||
|
/// described in section 4 of [Nova](https://eprint.iacr.org/2021/370.pdf), where the cmE & cmW checks are
|
||||||
|
/// delegated to the NIFSCycleFoldGadget.
|
||||||
|
pub struct NIFSGadget<C: CurveGroup> {
|
||||||
|
_c: PhantomData<C>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<C: CurveGroup> NIFSGadget<C>
|
||||||
|
where
|
||||||
|
C: CurveGroup,
|
||||||
|
{
|
||||||
|
/// Implements the constraints for NIFS.V for u and x, since cm(E) and cm(W) are delegated to
|
||||||
|
/// the CycleFold circuit.
|
||||||
|
pub fn verify(
|
||||||
|
r: FpVar<CF1<C>>,
|
||||||
|
ci1: CommittedInstanceE1Var<C>,
|
||||||
|
ci2: CommittedInstanceE1Var<C>,
|
||||||
|
ci3: CommittedInstanceE1Var<C>,
|
||||||
|
) -> Result<(), SynthesisError> {
|
||||||
|
// ensure that: ci3.u == ci1.u + r * ci2.u
|
||||||
|
ci3.u.enforce_equal(&(ci1.u + r.clone() * ci2.u))?;
|
||||||
|
|
||||||
|
// ensure that: ci3.x == ci1.x + r * ci2.x
|
||||||
|
let x_rlc = ci1
|
||||||
|
.x
|
||||||
|
.iter()
|
||||||
|
.zip(ci2.x)
|
||||||
|
.map(|(a, b)| a + &r * &b)
|
||||||
|
.collect::<Vec<FpVar<CF1<C>>>>();
|
||||||
|
x_rlc.enforce_equal(&ci3.x)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// NIFSCycleFoldGadget performs the Nova NIFS.V elliptic curve points relation checks in the other
|
||||||
|
/// curve following [CycleFold](https://eprint.iacr.org/2023/1192.pdf).
|
||||||
|
pub struct NIFSCycleFoldGadget<C: CurveGroup, GC: CurveVar<C, CF2<C>>> {
|
||||||
|
_c: PhantomData<C>,
|
||||||
|
_gc: PhantomData<GC>,
|
||||||
|
}
|
||||||
|
impl<C: CurveGroup, GC: CurveVar<C, CF2<C>>> NIFSCycleFoldGadget<C, GC>
|
||||||
|
where
|
||||||
|
C: CurveGroup,
|
||||||
|
GC: CurveVar<C, CF2<C>>,
|
||||||
|
for<'a> &'a GC: GroupOpsBounds<'a, C, GC>,
|
||||||
|
{
|
||||||
|
pub fn verify(
|
||||||
|
r_bits: Vec<Boolean<CF2<C>>>,
|
||||||
|
cmT: GC,
|
||||||
|
ci1: CommittedInstanceE2Var<C, GC>,
|
||||||
|
ci2: CommittedInstanceE2Var<C, GC>,
|
||||||
|
ci3: CommittedInstanceE2Var<C, GC>,
|
||||||
|
) -> Result<(), SynthesisError> {
|
||||||
|
// cm(E) check: ci3.cmE == ci1.cmE + r * cmT + r^2 * ci2.cmE
|
||||||
|
ci3.cmE.enforce_equal(
|
||||||
|
&((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
|
||||||
|
ECRLC::<C, GC>::check(r_bits, ci1.cmW, ci2.cmW, ci3.cmW)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use ark_ff::{BigInteger, PrimeField};
|
||||||
|
use ark_pallas::{constraints::GVar, Fq, Fr, Projective};
|
||||||
|
use ark_r1cs_std::{alloc::AllocVar, R1CSVar};
|
||||||
|
use ark_relations::r1cs::ConstraintSystem;
|
||||||
|
use ark_std::UniformRand;
|
||||||
|
|
||||||
|
use crate::ccs::r1cs::tests::{get_test_r1cs, get_test_z};
|
||||||
|
use crate::folding::nova::{nifs::NIFS, Witness};
|
||||||
|
use crate::pedersen::Pedersen;
|
||||||
|
use crate::transcript::poseidon::{tests::poseidon_test_config, PoseidonTranscript};
|
||||||
|
use crate::transcript::Transcript;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_committed_instance_var() {
|
||||||
|
let mut rng = ark_std::test_rng();
|
||||||
|
|
||||||
|
let ci = CommittedInstance::<Projective> {
|
||||||
|
cmE: Projective::rand(&mut rng),
|
||||||
|
u: Fr::rand(&mut rng),
|
||||||
|
cmW: Projective::rand(&mut rng),
|
||||||
|
x: vec![Fr::rand(&mut rng); 1],
|
||||||
|
};
|
||||||
|
|
||||||
|
let cs = ConstraintSystem::<Fr>::new_ref();
|
||||||
|
let ciVar =
|
||||||
|
CommittedInstanceE1Var::<Projective>::new_witness(cs.clone(), || Ok(ci.clone()))
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(ciVar.u.value().unwrap(), ci.u);
|
||||||
|
assert_eq!(ciVar.x.value().unwrap(), ci.x);
|
||||||
|
|
||||||
|
// check the instantiation of the CycleFold side:
|
||||||
|
let cs = ConstraintSystem::<Fq>::new_ref();
|
||||||
|
let ciVar =
|
||||||
|
CommittedInstanceE2Var::<Projective, GVar>::new_witness(cs.clone(), || Ok(ci.clone()))
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(ciVar.cmE.value().unwrap(), ci.cmE);
|
||||||
|
assert_eq!(ciVar.cmW.value().unwrap(), ci.cmW);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_nifs_gadget() {
|
||||||
|
let r1cs = get_test_r1cs();
|
||||||
|
let z1 = get_test_z(3);
|
||||||
|
let z2 = get_test_z(4);
|
||||||
|
let (w1, x1) = r1cs.split_z(&z1);
|
||||||
|
let (w2, x2) = r1cs.split_z(&z2);
|
||||||
|
|
||||||
|
let w1 = Witness::<Projective>::new(w1.clone(), r1cs.A.n_rows);
|
||||||
|
let w2 = Witness::<Projective>::new(w2.clone(), r1cs.A.n_rows);
|
||||||
|
|
||||||
|
let mut rng = ark_std::test_rng();
|
||||||
|
let pedersen_params = Pedersen::<Projective>::new_params(&mut rng, r1cs.A.n_cols);
|
||||||
|
|
||||||
|
// compute committed instances
|
||||||
|
let ci1 = w1.commit(&pedersen_params, x1.clone());
|
||||||
|
let ci2 = w2.commit(&pedersen_params, x2.clone());
|
||||||
|
|
||||||
|
// get challenge from transcript
|
||||||
|
let config = poseidon_test_config::<Fr>();
|
||||||
|
let mut tr = PoseidonTranscript::<Projective>::new(&config);
|
||||||
|
let r_bits = tr.get_challenge_nbits(128);
|
||||||
|
let r_Fr = Fr::from_bigint(BigInteger::from_bits_le(&r_bits)).unwrap();
|
||||||
|
|
||||||
|
let (_w3, ci3, _T, cmT) =
|
||||||
|
NIFS::<Projective>::prove(&pedersen_params, r_Fr, &r1cs, &w1, &ci1, &w2, &ci2);
|
||||||
|
|
||||||
|
let cs = ConstraintSystem::<Fr>::new_ref();
|
||||||
|
|
||||||
|
let rVar = FpVar::<Fr>::new_witness(cs.clone(), || Ok(r_Fr)).unwrap();
|
||||||
|
let ci1Var =
|
||||||
|
CommittedInstanceE1Var::<Projective>::new_witness(cs.clone(), || Ok(ci1.clone()))
|
||||||
|
.unwrap();
|
||||||
|
let ci2Var =
|
||||||
|
CommittedInstanceE1Var::<Projective>::new_witness(cs.clone(), || Ok(ci2.clone()))
|
||||||
|
.unwrap();
|
||||||
|
let ci3Var =
|
||||||
|
CommittedInstanceE1Var::<Projective>::new_witness(cs.clone(), || Ok(ci3.clone()))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
NIFSGadget::<Projective>::verify(
|
||||||
|
rVar.clone(),
|
||||||
|
ci1Var.clone(),
|
||||||
|
ci2Var.clone(),
|
||||||
|
ci3Var.clone(),
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
assert!(cs.is_satisfied().unwrap());
|
||||||
|
|
||||||
|
// cs_CC is the Constraint System on the Curve Cycle auxiliary curve constraints field
|
||||||
|
// (E2::Fr)
|
||||||
|
let cs_CC = ConstraintSystem::<Fq>::new_ref();
|
||||||
|
|
||||||
|
let r_bitsVar = Vec::<Boolean<Fq>>::new_witness(cs_CC.clone(), || Ok(r_bits)).unwrap();
|
||||||
|
|
||||||
|
let cmTVar = GVar::new_witness(cs_CC.clone(), || Ok(cmT)).unwrap();
|
||||||
|
let ci1Var = CommittedInstanceE2Var::<Projective, GVar>::new_witness(cs_CC.clone(), || {
|
||||||
|
Ok(ci1.clone())
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
let ci2Var = CommittedInstanceE2Var::<Projective, GVar>::new_witness(cs_CC.clone(), || {
|
||||||
|
Ok(ci2.clone())
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
let ci3Var = CommittedInstanceE2Var::<Projective, GVar>::new_witness(cs_CC.clone(), || {
|
||||||
|
Ok(ci3.clone())
|
||||||
|
})
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
NIFSCycleFoldGadget::<Projective, GVar>::verify(r_bitsVar, cmTVar, ci1Var, ci2Var, ci3Var)
|
||||||
|
.unwrap();
|
||||||
|
assert!(cs_CC.is_satisfied().unwrap());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ use ark_std::{One, Zero};
|
|||||||
|
|
||||||
use crate::pedersen::{Params as PedersenParams, Pedersen};
|
use crate::pedersen::{Params as PedersenParams, Pedersen};
|
||||||
|
|
||||||
|
pub mod circuits;
|
||||||
pub mod nifs;
|
pub mod nifs;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
@@ -14,6 +15,7 @@ pub struct CommittedInstance<C: CurveGroup> {
|
|||||||
pub cmW: C,
|
pub cmW: C,
|
||||||
pub x: Vec<C::ScalarField>,
|
pub x: Vec<C::ScalarField>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C: CurveGroup> CommittedInstance<C> {
|
impl<C: CurveGroup> CommittedInstance<C> {
|
||||||
pub fn empty() -> Self {
|
pub fn empty() -> Self {
|
||||||
CommittedInstance {
|
CommittedInstance {
|
||||||
|
|||||||
@@ -56,7 +56,8 @@ where
|
|||||||
&vec_scalar_mul(&w2.E, &r2),
|
&vec_scalar_mul(&w2.E, &r2),
|
||||||
);
|
);
|
||||||
let rE = w1.rE + r * rT + r2 * w2.rE;
|
let rE = w1.rE + r * rT + r2 * w2.rE;
|
||||||
let W = vec_add(&w1.W, &vec_scalar_mul(&w2.W, &r));
|
let W: Vec<C::ScalarField> = w1.W.iter().zip(&w2.W).map(|(a, b)| *a + (r * b)).collect();
|
||||||
|
|
||||||
let rW = w1.rW + r * w2.rW;
|
let rW = w1.rW + r * w2.rW;
|
||||||
Witness::<C> { E, rE, W, rW }
|
Witness::<C> { E, rE, W, rW }
|
||||||
}
|
}
|
||||||
@@ -68,11 +69,15 @@ where
|
|||||||
cmT: &C,
|
cmT: &C,
|
||||||
) -> CommittedInstance<C> {
|
) -> CommittedInstance<C> {
|
||||||
let r2 = r * r;
|
let r2 = r * r;
|
||||||
|
|
||||||
let cmE = ci1.cmE + cmT.mul(r) + ci2.cmE.mul(r2);
|
let cmE = ci1.cmE + cmT.mul(r) + ci2.cmE.mul(r2);
|
||||||
let u = ci1.u + r * ci2.u;
|
let u = ci1.u + r * ci2.u;
|
||||||
let cmW = ci1.cmW + ci2.cmW.mul(r);
|
let cmW = ci1.cmW + ci2.cmW.mul(r);
|
||||||
let x = vec_add(&ci1.x, &vec_scalar_mul(&ci2.x, &r));
|
let x = ci1
|
||||||
|
.x
|
||||||
|
.iter()
|
||||||
|
.zip(&ci2.x)
|
||||||
|
.map(|(a, b)| *a + (r * b))
|
||||||
|
.collect::<Vec<C::ScalarField>>();
|
||||||
|
|
||||||
CommittedInstance::<C> { cmE, u, cmW, x }
|
CommittedInstance::<C> { cmE, u, cmW, x }
|
||||||
}
|
}
|
||||||
@@ -81,6 +86,7 @@ where
|
|||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
pub fn prove(
|
pub fn prove(
|
||||||
pedersen_params: &PedersenParams<C>,
|
pedersen_params: &PedersenParams<C>,
|
||||||
|
// r comes from the transcript, and is a n-bit (N_BITS_CHALLENGE) element
|
||||||
r: C::ScalarField,
|
r: C::ScalarField,
|
||||||
r1cs: &R1CS<C::ScalarField>,
|
r1cs: &R1CS<C::ScalarField>,
|
||||||
w1: &Witness<C>,
|
w1: &Witness<C>,
|
||||||
@@ -107,6 +113,7 @@ where
|
|||||||
|
|
||||||
// NIFS.V
|
// NIFS.V
|
||||||
pub fn verify(
|
pub fn verify(
|
||||||
|
// r comes from the transcript, and is a n-bit (N_BITS_CHALLENGE) element
|
||||||
r: C::ScalarField,
|
r: C::ScalarField,
|
||||||
ci1: &CommittedInstance<C>,
|
ci1: &CommittedInstance<C>,
|
||||||
ci2: &CommittedInstance<C>,
|
ci2: &CommittedInstance<C>,
|
||||||
@@ -179,8 +186,8 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::ccs::r1cs::tests::{get_test_r1cs, get_test_z};
|
use crate::ccs::r1cs::tests::{get_test_r1cs, get_test_z};
|
||||||
use crate::transcript::poseidon::{tests::poseidon_test_config, PoseidonTranscript};
|
use crate::transcript::poseidon::{tests::poseidon_test_config, PoseidonTranscript};
|
||||||
use ark_bls12_377::{Fr, G1Projective};
|
|
||||||
use ark_ff::PrimeField;
|
use ark_ff::PrimeField;
|
||||||
|
use ark_pallas::{Fr, Projective};
|
||||||
use ark_std::UniformRand;
|
use ark_std::UniformRand;
|
||||||
|
|
||||||
pub fn check_relaxed_r1cs<F: PrimeField>(r1cs: &R1CS<F>, z: Vec<F>, u: F, E: &[F]) {
|
pub fn check_relaxed_r1cs<F: PrimeField>(r1cs: &R1CS<F>, z: Vec<F>, u: F, E: &[F]) {
|
||||||
@@ -199,23 +206,24 @@ mod tests {
|
|||||||
let (w1, x1) = r1cs.split_z(&z1);
|
let (w1, x1) = r1cs.split_z(&z1);
|
||||||
let (w2, x2) = r1cs.split_z(&z2);
|
let (w2, x2) = r1cs.split_z(&z2);
|
||||||
|
|
||||||
let w1 = Witness::<G1Projective>::new(w1.clone(), r1cs.A.n_cols);
|
let w1 = Witness::<Projective>::new(w1.clone(), r1cs.A.n_rows);
|
||||||
let w2 = Witness::<G1Projective>::new(w2.clone(), r1cs.A.n_cols);
|
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::<G1Projective>::new_params(&mut rng, r1cs.A.n_cols);
|
let pedersen_params = Pedersen::<Projective>::new_params(&mut rng, r1cs.A.n_cols);
|
||||||
|
|
||||||
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
|
||||||
|
|
||||||
// compute committed instances
|
// compute committed instances
|
||||||
let ci1 = w1.commit(&pedersen_params, x1.clone());
|
let ci1 = w1.commit(&pedersen_params, x1.clone());
|
||||||
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, _, T, cmT) =
|
||||||
NIFS::<G1Projective>::prove(&pedersen_params, r, &r1cs, &w1, &ci1, &w2, &ci2);
|
NIFS::<Projective>::prove(&pedersen_params, r, &r1cs, &w1, &ci1, &w2, &ci2);
|
||||||
|
|
||||||
// NIFS.V
|
// NIFS.V
|
||||||
let ci3 = NIFS::<G1Projective>::verify(r, &ci1, &ci2, &cmT);
|
let ci3 = NIFS::<Projective>::verify(r, &ci1, &ci2, &cmT);
|
||||||
|
|
||||||
// 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();
|
||||||
@@ -234,18 +242,18 @@ mod tests {
|
|||||||
assert_eq!(ci3_expected.cmW, ci3.cmW);
|
assert_eq!(ci3_expected.cmW, ci3.cmW);
|
||||||
|
|
||||||
// NIFS.Verify_Folded_Instance:
|
// NIFS.Verify_Folded_Instance:
|
||||||
assert!(NIFS::<G1Projective>::verify_folded_instance(
|
assert!(NIFS::<Projective>::verify_folded_instance(
|
||||||
r, &ci1, &ci2, &ci3, &cmT
|
r, &ci1, &ci2, &ci3, &cmT
|
||||||
));
|
));
|
||||||
|
|
||||||
let poseidon_config = poseidon_test_config::<Fr>();
|
let poseidon_config = poseidon_test_config::<Fr>();
|
||||||
// init Prover's transcript
|
// init Prover's transcript
|
||||||
let mut transcript_p = PoseidonTranscript::<G1Projective>::new(&poseidon_config);
|
let mut transcript_p = PoseidonTranscript::<Projective>::new(&poseidon_config);
|
||||||
// init Verifier's transcript
|
// init Verifier's transcript
|
||||||
let mut transcript_v = PoseidonTranscript::<G1Projective>::new(&poseidon_config);
|
let mut transcript_v = PoseidonTranscript::<Projective>::new(&poseidon_config);
|
||||||
|
|
||||||
// check openings of ci3.cmE, ci3.cmW and cmT
|
// check openings of ci3.cmE, ci3.cmW and cmT
|
||||||
let (cmE_proof, cmW_proof, cmT_proof) = NIFS::<G1Projective>::open_commitments(
|
let (cmE_proof, cmW_proof, cmT_proof) = NIFS::<Projective>::open_commitments(
|
||||||
&mut transcript_p,
|
&mut transcript_p,
|
||||||
&pedersen_params,
|
&pedersen_params,
|
||||||
&w3,
|
&w3,
|
||||||
@@ -253,7 +261,7 @@ mod tests {
|
|||||||
T,
|
T,
|
||||||
&cmT,
|
&cmT,
|
||||||
);
|
);
|
||||||
let v = NIFS::<G1Projective>::verify_commitments(
|
let v = NIFS::<Projective>::verify_commitments(
|
||||||
&mut transcript_v,
|
&mut transcript_v,
|
||||||
&pedersen_params,
|
&pedersen_params,
|
||||||
ci3,
|
ci3,
|
||||||
@@ -272,10 +280,10 @@ mod tests {
|
|||||||
let (w, x) = r1cs.split_z(&z);
|
let (w, x) = r1cs.split_z(&z);
|
||||||
|
|
||||||
let mut rng = ark_std::test_rng();
|
let mut rng = ark_std::test_rng();
|
||||||
let pedersen_params = Pedersen::<G1Projective>::new_params(&mut rng, r1cs.A.n_cols);
|
let pedersen_params = Pedersen::<Projective>::new_params(&mut rng, r1cs.A.n_cols);
|
||||||
|
|
||||||
// prepare the running instance
|
// prepare the running instance
|
||||||
let mut running_instance_w = Witness::<G1Projective>::new(w.clone(), r1cs.A.n_cols);
|
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(
|
check_relaxed_r1cs(
|
||||||
&r1cs,
|
&r1cs,
|
||||||
@@ -289,7 +297,7 @@ mod tests {
|
|||||||
// prepare the incomming instance
|
// prepare the incomming instance
|
||||||
let incomming_instance_z = get_test_z(i + 4);
|
let incomming_instance_z = get_test_z(i + 4);
|
||||||
let (w, x) = r1cs.split_z(&incomming_instance_z);
|
let (w, x) = r1cs.split_z(&incomming_instance_z);
|
||||||
let incomming_instance_w = Witness::<G1Projective>::new(w.clone(), r1cs.A.n_cols);
|
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(
|
check_relaxed_r1cs(
|
||||||
&r1cs,
|
&r1cs,
|
||||||
@@ -301,7 +309,7 @@ mod tests {
|
|||||||
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
|
||||||
|
|
||||||
// NIFS.P
|
// NIFS.P
|
||||||
let (folded_w, _, _, cmT) = NIFS::<G1Projective>::prove(
|
let (folded_w, _, _, cmT) = NIFS::<Projective>::prove(
|
||||||
&pedersen_params,
|
&pedersen_params,
|
||||||
r,
|
r,
|
||||||
&r1cs,
|
&r1cs,
|
||||||
@@ -312,7 +320,7 @@ mod tests {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// NIFS.V
|
// NIFS.V
|
||||||
let folded_committed_instance = NIFS::<G1Projective>::verify(
|
let folded_committed_instance = NIFS::<Projective>::verify(
|
||||||
r,
|
r,
|
||||||
&running_committed_instance,
|
&running_committed_instance,
|
||||||
&incomming_committed_instance,
|
&incomming_committed_instance,
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ use thiserror::Error;
|
|||||||
pub mod transcript;
|
pub mod transcript;
|
||||||
use transcript::Transcript;
|
use transcript::Transcript;
|
||||||
pub mod ccs;
|
pub mod ccs;
|
||||||
|
pub mod constants;
|
||||||
pub mod folding;
|
pub mod folding;
|
||||||
pub mod pedersen;
|
pub mod pedersen;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use crate::transcript::poseidon::{tests::poseidon_test_config, PoseidonTranscript};
|
use crate::transcript::poseidon::{tests::poseidon_test_config, PoseidonTranscript};
|
||||||
|
|
||||||
use ark_bls12_377::{Fr, G1Projective};
|
use ark_pallas::{Fr, Projective};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_pedersen_vector() {
|
fn test_pedersen_vector() {
|
||||||
@@ -109,19 +109,19 @@ mod tests {
|
|||||||
|
|
||||||
const n: usize = 10;
|
const n: usize = 10;
|
||||||
// setup params
|
// setup params
|
||||||
let params = Pedersen::<G1Projective>::new_params(&mut rng, n);
|
let params = Pedersen::<Projective>::new_params(&mut rng, n);
|
||||||
let poseidon_config = poseidon_test_config::<Fr>();
|
let poseidon_config = poseidon_test_config::<Fr>();
|
||||||
|
|
||||||
// init Prover's transcript
|
// init Prover's transcript
|
||||||
let mut transcript_p = PoseidonTranscript::<G1Projective>::new(&poseidon_config);
|
let mut transcript_p = PoseidonTranscript::<Projective>::new(&poseidon_config);
|
||||||
// init Verifier's transcript
|
// init Verifier's transcript
|
||||||
let mut transcript_v = PoseidonTranscript::<G1Projective>::new(&poseidon_config);
|
let mut transcript_v = PoseidonTranscript::<Projective>::new(&poseidon_config);
|
||||||
|
|
||||||
let v: Vec<Fr> = vec![Fr::rand(&mut rng); n];
|
let v: Vec<Fr> = vec![Fr::rand(&mut rng); n];
|
||||||
let r: Fr = Fr::rand(&mut rng);
|
let r: Fr = Fr::rand(&mut rng);
|
||||||
let cm = Pedersen::<G1Projective>::commit(¶ms, &v, &r);
|
let cm = Pedersen::<Projective>::commit(¶ms, &v, &r);
|
||||||
let proof = Pedersen::<G1Projective>::prove(¶ms, &mut transcript_p, &cm, &v, &r);
|
let proof = Pedersen::<Projective>::prove(¶ms, &mut transcript_p, &cm, &v, &r);
|
||||||
let v = Pedersen::<G1Projective>::verify(¶ms, &mut transcript_v, cm, proof);
|
let v = Pedersen::<Projective>::verify(¶ms, &mut transcript_v, cm, proof);
|
||||||
assert!(v);
|
assert!(v);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,5 +11,7 @@ pub trait Transcript<C: CurveGroup> {
|
|||||||
fn absorb_vec(&mut self, v: &[C::ScalarField]);
|
fn absorb_vec(&mut self, v: &[C::ScalarField]);
|
||||||
fn absorb_point(&mut self, v: &C);
|
fn absorb_point(&mut self, v: &C);
|
||||||
fn get_challenge(&mut self) -> C::ScalarField;
|
fn get_challenge(&mut self) -> C::ScalarField;
|
||||||
|
/// get_challenge_nbits returns a field element of size nbits
|
||||||
|
fn get_challenge_nbits(&mut self, nbits: usize) -> Vec<bool>;
|
||||||
fn get_challenges(&mut self, n: usize) -> Vec<C::ScalarField>;
|
fn get_challenges(&mut self, n: usize) -> Vec<C::ScalarField>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use ark_crypto_primitives::sponge::{
|
|||||||
};
|
};
|
||||||
use ark_ec::{AffineRepr, CurveGroup, Group};
|
use ark_ec::{AffineRepr, CurveGroup, Group};
|
||||||
use ark_ff::{BigInteger, Field, PrimeField};
|
use ark_ff::{BigInteger, Field, PrimeField};
|
||||||
use ark_r1cs_std::fields::fp::FpVar;
|
use ark_r1cs_std::{boolean::Boolean, fields::fp::FpVar};
|
||||||
use ark_relations::r1cs::{ConstraintSystemRef, SynthesisError};
|
use ark_relations::r1cs::{ConstraintSystemRef, SynthesisError};
|
||||||
|
|
||||||
use crate::transcript::Transcript;
|
use crate::transcript::Transcript;
|
||||||
@@ -42,6 +42,9 @@ where
|
|||||||
self.sponge.absorb(&c[0]);
|
self.sponge.absorb(&c[0]);
|
||||||
c[0]
|
c[0]
|
||||||
}
|
}
|
||||||
|
fn get_challenge_nbits(&mut self, nbits: usize) -> Vec<bool> {
|
||||||
|
self.sponge.squeeze_bits(nbits)
|
||||||
|
}
|
||||||
fn get_challenges(&mut self, n: usize) -> Vec<C::ScalarField> {
|
fn get_challenges(&mut self, n: usize) -> Vec<C::ScalarField> {
|
||||||
let c = self.sponge.squeeze_field_elements(n);
|
let c = self.sponge.squeeze_field_elements(n);
|
||||||
self.sponge.absorb(&c);
|
self.sponge.absorb(&c);
|
||||||
@@ -92,6 +95,12 @@ impl<F: PrimeField> PoseidonTranscriptVar<F> {
|
|||||||
self.sponge.absorb(&c[0])?;
|
self.sponge.absorb(&c[0])?;
|
||||||
Ok(c[0].clone())
|
Ok(c[0].clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// returns the bit representation of the challenge, we use its output in-circuit for the
|
||||||
|
/// `GC.scalar_mul_le` method.
|
||||||
|
pub fn get_challenge_nbits(&mut self, nbits: usize) -> Result<Vec<Boolean<F>>, SynthesisError> {
|
||||||
|
self.sponge.squeeze_bits(nbits)
|
||||||
|
}
|
||||||
pub fn get_challenges(&mut self, n: usize) -> Result<Vec<FpVar<F>>, SynthesisError> {
|
pub fn get_challenges(&mut self, n: usize) -> Result<Vec<FpVar<F>>, SynthesisError> {
|
||||||
let c = self.sponge.squeeze_field_elements(n)?;
|
let c = self.sponge.squeeze_field_elements(n)?;
|
||||||
self.sponge.absorb(&c)?;
|
self.sponge.absorb(&c)?;
|
||||||
@@ -102,10 +111,12 @@ impl<F: PrimeField> PoseidonTranscriptVar<F> {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod tests {
|
pub mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use ark_bls12_377::{Fr, G1Projective};
|
|
||||||
use ark_crypto_primitives::sponge::poseidon::find_poseidon_ark_and_mds;
|
use ark_crypto_primitives::sponge::poseidon::find_poseidon_ark_and_mds;
|
||||||
use ark_r1cs_std::{alloc::AllocVar, fields::fp::FpVar, R1CSVar};
|
use ark_pallas::{constraints::GVar, Fq, Fr, Projective};
|
||||||
|
use ark_r1cs_std::{alloc::AllocVar, fields::fp::FpVar, groups::CurveVar, R1CSVar};
|
||||||
use ark_relations::r1cs::ConstraintSystem;
|
use ark_relations::r1cs::ConstraintSystem;
|
||||||
|
use ark_vesta::Projective as E2Projective;
|
||||||
|
use std::ops::Mul;
|
||||||
|
|
||||||
/// WARNING the method poseidon_test_config is for tests only
|
/// WARNING the method poseidon_test_config is for tests only
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@@ -135,10 +146,10 @@ pub mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_transcript_and_transcriptvar() {
|
fn test_transcript_and_transcriptvar_get_challenge() {
|
||||||
// use 'native' transcript
|
// use 'native' transcript
|
||||||
let config = poseidon_test_config::<Fr>();
|
let config = poseidon_test_config::<Fr>();
|
||||||
let mut tr = PoseidonTranscript::<G1Projective>::new(&config);
|
let mut tr = PoseidonTranscript::<Projective>::new(&config);
|
||||||
tr.absorb(&Fr::from(42_u32));
|
tr.absorb(&Fr::from(42_u32));
|
||||||
let c = tr.get_challenge();
|
let c = tr.get_challenge();
|
||||||
|
|
||||||
@@ -152,4 +163,52 @@ pub mod tests {
|
|||||||
// assert that native & gadget transcripts return the same challenge
|
// assert that native & gadget transcripts return the same challenge
|
||||||
assert_eq!(c, c_var.value().unwrap());
|
assert_eq!(c, c_var.value().unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_transcript_and_transcriptvar_nbits() {
|
||||||
|
let nbits = crate::constants::N_BITS_CHALLENGE;
|
||||||
|
|
||||||
|
// use 'native' transcript
|
||||||
|
let config = poseidon_test_config::<Fq>();
|
||||||
|
let mut tr = PoseidonTranscript::<E2Projective>::new(&config);
|
||||||
|
tr.absorb(&Fq::from(42_u32));
|
||||||
|
|
||||||
|
// get challenge from native transcript
|
||||||
|
let c_bits = tr.get_challenge_nbits(nbits);
|
||||||
|
|
||||||
|
// use 'gadget' transcript
|
||||||
|
let cs = ConstraintSystem::<Fq>::new_ref();
|
||||||
|
let mut tr_var = PoseidonTranscriptVar::<Fq>::new(cs.clone(), &config);
|
||||||
|
let v = FpVar::<Fq>::new_witness(cs.clone(), || Ok(Fq::from(42_u32))).unwrap();
|
||||||
|
tr_var.absorb(v).unwrap();
|
||||||
|
|
||||||
|
// get challenge from circuit transcript
|
||||||
|
let c_var = tr_var.get_challenge_nbits(nbits).unwrap();
|
||||||
|
|
||||||
|
let P = Projective::generator();
|
||||||
|
let PVar = GVar::new_witness(cs.clone(), || Ok(P)).unwrap();
|
||||||
|
|
||||||
|
// multiply point P by the challenge in different formats, to ensure that we get the same
|
||||||
|
// result natively and in-circuit
|
||||||
|
|
||||||
|
// native c*P
|
||||||
|
let c_Fr = Fr::from_bigint(BigInteger::from_bits_le(&c_bits)).unwrap();
|
||||||
|
let cP_native = P.mul(c_Fr);
|
||||||
|
|
||||||
|
// native c*P using mul_bits_be (notice the .rev to convert the LE to BE)
|
||||||
|
let cP_native_bits = P.mul_bits_be(c_bits.into_iter().rev());
|
||||||
|
|
||||||
|
// in-circuit c*P using scalar_mul_le
|
||||||
|
let cPVar = PVar.scalar_mul_le(c_var.iter()).unwrap();
|
||||||
|
|
||||||
|
// check that they are equal
|
||||||
|
assert_eq!(
|
||||||
|
cP_native.into_affine(),
|
||||||
|
cPVar.value().unwrap().into_affine()
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
cP_native_bits.into_affine(),
|
||||||
|
cPVar.value().unwrap().into_affine()
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -321,7 +321,7 @@ fn u64_factorial(a: usize) -> u64 {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::interpolate_uni_poly;
|
use super::interpolate_uni_poly;
|
||||||
use ark_bls12_377::Fr;
|
use ark_pallas::Fr;
|
||||||
use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial, Polynomial};
|
use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial, Polynomial};
|
||||||
use ark_std::{vec::Vec, UniformRand};
|
use ark_std::{vec::Vec, UniformRand};
|
||||||
use espresso_subroutines::poly_iop::prelude::PolyIOPErrors;
|
use espresso_subroutines::poly_iop::prelude::PolyIOPErrors;
|
||||||
|
|||||||
@@ -412,8 +412,8 @@ pub fn bit_decompose(input: u64, num_var: usize) -> Vec<bool> {
|
|||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::utils::multilinear_polynomial::tests::random_mle_list;
|
use crate::utils::multilinear_polynomial::tests::random_mle_list;
|
||||||
use ark_bls12_377::Fr;
|
|
||||||
use ark_ff::UniformRand;
|
use ark_ff::UniformRand;
|
||||||
|
use ark_pallas::Fr;
|
||||||
use ark_std::{
|
use ark_std::{
|
||||||
rand::{Rng, RngCore},
|
rand::{Rng, RngCore},
|
||||||
test_rng,
|
test_rng,
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ pub fn mat_vec_mul<F: PrimeField>(M: &Vec<Vec<F>>, z: &[F]) -> Vec<F> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn mat_vec_mul_sparse<F: PrimeField>(matrix: &SparseMatrix<F>, vector: &[F]) -> Vec<F> {
|
pub fn mat_vec_mul_sparse<F: PrimeField>(matrix: &SparseMatrix<F>, vector: &[F]) -> Vec<F> {
|
||||||
let mut res = vec![F::zero(); matrix.n_cols];
|
let mut res = vec![F::zero(); matrix.n_rows];
|
||||||
for &(row, col, value) in matrix.coeffs.iter() {
|
for &(row, col, value) in matrix.coeffs.iter() {
|
||||||
res[row] += value * vector[col];
|
res[row] += value * vector[col];
|
||||||
}
|
}
|
||||||
@@ -85,3 +85,65 @@ pub fn hadamard<F: PrimeField>(a: &[F], b: &[F]) -> Vec<F> {
|
|||||||
assert_eq!(a.len(), b.len());
|
assert_eq!(a.len(), b.len());
|
||||||
cfg_iter!(a).zip(b).map(|(a, b)| *a * b).collect()
|
cfg_iter!(a).zip(b).map(|(a, b)| *a * b).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub mod tests {
|
||||||
|
use super::*;
|
||||||
|
use ark_pallas::Fr;
|
||||||
|
|
||||||
|
pub fn to_F_matrix<F: PrimeField>(M: Vec<Vec<usize>>) -> Vec<Vec<F>> {
|
||||||
|
let mut R: Vec<Vec<F>> = vec![Vec::new(); M.len()];
|
||||||
|
for i in 0..M.len() {
|
||||||
|
R[i] = vec![F::zero(); M[i].len()];
|
||||||
|
for j in 0..M[i].len() {
|
||||||
|
R[i][j] = F::from(M[i][j] as u64);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
R
|
||||||
|
}
|
||||||
|
pub fn to_F_vec<F: PrimeField>(z: Vec<usize>) -> Vec<F> {
|
||||||
|
let mut r: Vec<F> = vec![F::zero(); z.len()];
|
||||||
|
for i in 0..z.len() {
|
||||||
|
r[i] = F::from(z[i] as u64);
|
||||||
|
}
|
||||||
|
r
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mat_vec_mul() {
|
||||||
|
let A = to_F_matrix::<Fr>(vec![
|
||||||
|
vec![0, 1, 0, 0, 0, 0],
|
||||||
|
vec![0, 0, 0, 1, 0, 0],
|
||||||
|
vec![0, 1, 0, 0, 1, 0],
|
||||||
|
vec![5, 0, 0, 0, 0, 1],
|
||||||
|
]);
|
||||||
|
let z = to_F_vec(vec![1, 3, 35, 9, 27, 30]);
|
||||||
|
assert_eq!(mat_vec_mul(&A, &z), to_F_vec(vec![3, 9, 30, 35]));
|
||||||
|
assert_eq!(
|
||||||
|
mat_vec_mul_sparse(&dense_matrix_to_sparse(A), &z),
|
||||||
|
to_F_vec(vec![3, 9, 30, 35])
|
||||||
|
);
|
||||||
|
|
||||||
|
let A = to_F_matrix::<Fr>(vec![vec![2, 3, 4, 5], vec![4, 8, 12, 14], vec![9, 8, 7, 6]]);
|
||||||
|
let v = to_F_vec(vec![19, 55, 50, 3]);
|
||||||
|
|
||||||
|
assert_eq!(mat_vec_mul(&A, &v), to_F_vec(vec![418, 1158, 979]));
|
||||||
|
assert_eq!(
|
||||||
|
mat_vec_mul_sparse(&dense_matrix_to_sparse(A), &v),
|
||||||
|
to_F_vec(vec![418, 1158, 979])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hadamard_product() {
|
||||||
|
let a = to_F_vec::<Fr>(vec![1, 2, 3, 4, 5, 6]);
|
||||||
|
let b = to_F_vec(vec![7, 8, 9, 10, 11, 12]);
|
||||||
|
assert_eq!(hadamard(&a, &b), to_F_vec(vec![7, 16, 27, 40, 55, 72]));
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_vec_add() {
|
||||||
|
let a: Vec<Fr> = to_F_vec::<Fr>(vec![1, 2, 3, 4, 5, 6]);
|
||||||
|
let b: Vec<Fr> = to_F_vec(vec![7, 8, 9, 10, 11, 12]);
|
||||||
|
assert_eq!(vec_add(&a, &b), to_F_vec(vec![8, 10, 12, 14, 16, 18]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user