Browse Source

folding witness & crossterms works, add Ve helpers

ivc-proofs
arnaucube 1 year ago
parent
commit
ab2b047402
3 changed files with 133 additions and 42 deletions
  1. +1
    -1
      README.md
  2. +77
    -29
      src/nifs.rs
  3. +55
    -12
      src/utils.rs

+ 1
- 1
README.md

@ -4,6 +4,6 @@ Implementation of [Nova](https://eprint.iacr.org/2021/370.pdf) using [arkworks-r
> Warning: Implementation from scratch to learn the internals of Nova. Do not use in production.
This repo is an ongoing implementation, the code will be dirty for a while and not optimized and not to be used but just to understand and experiment with the internals of the scheme and try experimental combinations.
This repo is an ongoing implementation, the code will be dirty for a while and not optimized but just to understand and experiment with the internals of the scheme and try experimental combinations.
Thanks to [levs57](https://twitter.com/levs57) for clarifications on the Nova folding.

+ 77
- 29
src/nifs.rs

@ -1,17 +1,24 @@
use ark_ec::AffineRepr;
use ark_std::ops::Add;
use ark_std::One;
use std::marker::PhantomData;
use crate::pedersen::{Commitment, CommitmentVec};
use crate::pedersen::{Commitment, CommitmentVec, Params as PedersenParams, Pedersen};
use crate::r1cs::*;
use crate::transcript::Transcript;
use crate::utils::*;
use ark_std::{
rand::{Rng, RngCore},
UniformRand,
};
// Phi: φ in the paper (later 𝖴), a folded instance
pub struct Phi<C: AffineRepr> {
cmE: Commitment<C>, // TODO not Commitment but directly C (without rE)
// cmE: CommitmentVec<C>, // TODO not Commitment but directly C (without rE)
cmE: C,
u: C::ScalarField,
cmW: Commitment<C>, // TODO not Commitment but directly C (without rW)
// cmW: CommitmentVec<C>, // TODO not Commitment but directly C (without rW)
cmW: C,
x: Vec<C::ScalarField>,
}
@ -24,8 +31,21 @@ pub struct FWit {
}
impl<C: AffineRepr> FWit<C> {
pub fn commit(&self) -> Phi<C> {
unimplemented!();
pub fn commit<R: Rng>(
&self,
rng: &mut R,
params: &PedersenParams<C>,
x: Vec<C::ScalarField>,
) -> Phi<C> {
// TODO instead of rand r, use self.rE and self.rW for the commit_vec
let cmE = Pedersen::commit_vec(rng, &params, &self.E);
let cmW = Pedersen::commit_vec(rng, &params, &self.W);
Phi {
cmE: cmE.cm,
u: C::ScalarField::one(),
cmW: cmW.cm,
x,
}
}
}
@ -46,17 +66,19 @@ impl NIFS {
// this is parallelizable (for the future)
let Az1 = matrix_vector_product(&A, &z1);
let Bz1 = matrix_vector_product(&B, &z1);
let Az1_Bz1 = hadamard_product(Az1, Bz1);
let Cz1 = matrix_vector_product(&C, &z1);
let Az2 = matrix_vector_product(&A, &z2);
let Bz2 = matrix_vector_product(&B, &z2);
let Az2_Bz2 = hadamard_product(Az2, Bz2);
let Cz2 = matrix_vector_product(&C, &z2);
let Cz1 = matrix_vector_product(&C, &z1);
let Az1_Bz2 = hadamard_product(Az1, Bz2);
let Az2_Bz1 = hadamard_product(Az2, Bz1);
let u1Cz2 = vector_elem_product(&Cz2, &cs1.u);
let u2Cz1 = vector_elem_product(&Cz1, &cs2.u);
// this will get simplifyied with future operators from Add trait
let T = vec_sub(vec_sub(vec_add(Az1_Bz1, Az2_Bz2), u1Cz2), u2Cz1);
T
// let T = vec_sub(vec_sub(vec_add(Az1_Bz2, Az2_Bz1), u1Cz2), u2Cz1);
let T = ((Ve(Az1_Bz2) + Ve(Az2_Bz1)) - Ve(u1Cz2)) - Ve(u2Cz1);
T.0
}
pub fn fold_witness(
@ -67,12 +89,15 @@ impl NIFS {
) -> FWit<C> {
let r2 = r * r;
let E: Vec<C::ScalarField> = vec_add(
// TODO this syntax will be simplified with future operators impl
// this syntax will be simplified with future operators impl (or at least a method
// for r-lin)
vec_add(fw1.E.clone(), vector_elem_product(&T, &r)),
// rlin(fw1.E.clone(), T, r),
vector_elem_product(&fw2.E, &r2),
);
let rE = fw1.rE + r * fw2.rE;
let rE = fw1.rE + r2 * fw2.rE; // TODO rT
let W = vec_add(fw1.W.clone(), vector_elem_product(&fw2.W, &r));
// let W = rlin(fw1.W.clone(), fw2.W.clone(), r);
let rW = fw1.rW + r * fw2.rW;
FWit::<C> {
E: E.into(),
@ -90,21 +115,24 @@ impl NIFS {
) -> Phi<C> {
let r2 = r * r;
let cmE = phi1.cmE.cm + cmT.cm.mul(r) + phi2.cmE.cm.mul(r2);
let cmE = phi1.cmE + cmT.cm.mul(r) + phi2.cmE.mul(r2);
let u = phi1.u + r * phi2.u;
let cmW = phi1.cmW.cm + phi2.cmW.cm.mul(r);
let cmW = phi1.cmW + phi2.cmW.mul(r);
let x = vec_add(phi1.x, vector_elem_product(&phi2.x, &r));
// let x = rlin(phi1.x, phi2.x, r);
Phi::<C> {
cmE: Commitment::<C> {
cm: cmE.into(),
r: phi1.cmE.r,
},
// cmE: Commitment::<C> {
// cm: cmE.into(),
// r: phi1.cmE.r,
// },
cmE: cmE.into(),
u,
cmW: Commitment::<C> {
cm: cmW.into(),
r: phi1.cmW.r,
},
// cmW: Commitment::<C> {
// cm: cmW.into(),
// r: phi1.cmW.r,
// },
cmW: cmW.into(),
x,
}
}
@ -127,7 +155,8 @@ mod tests {
fn test_simple_folding() {
let mut rng = ark_std::test_rng();
// R1CS for: x^3 + x + 5 = y
// R1CS for: x^3 + x + 5 = y (example from article
// https://www.vitalik.ca/general/2016/12/10/qap.html )
let A = to_F_matrix::<Fr>(vec![
vec![0, 1, 0, 0, 0, 0],
vec![0, 0, 0, 1, 0, 0],
@ -147,7 +176,9 @@ mod tests {
vec![0, 0, 1, 0, 0, 0],
]);
let z1 = to_F_vec::<Fr>(vec![1, 3, 35, 9, 27, 30]);
let x1 = to_F_vec::<Fr>(vec![35]);
let z2 = to_F_vec::<Fr>(vec![1, 4, 73, 16, 64, 68]);
let x2 = to_F_vec::<Fr>(vec![73]);
let relaxed_r1cs_1 = R1CS::<Fr> {
A: A.clone(),
@ -155,7 +186,12 @@ mod tests {
C: C.clone(),
}
.relax();
let relaxed_r1cs_2 = R1CS::<Fr> { A, B, C }.relax();
let relaxed_r1cs_2 = R1CS::<Fr> {
A: A.clone(),
B: B.clone(),
C: C.clone(),
}
.relax();
let T = NIFS::<G1Affine>::comp_T(relaxed_r1cs_1, relaxed_r1cs_2, &z1, &z2);
let params = Pedersen::<G1Affine>::new_params(&mut rng);
@ -179,11 +215,23 @@ mod tests {
// fold witness
let folded_witness = NIFS::<G1Affine>::fold_witness(r, &fw1, &fw2, T);
let phi1 = fw1.commit(); // <- unimplemented
let phi2 = fw2.commit();
let pedersen_params = Pedersen::<G1Affine>::new_params(&mut rng);
let phi1 = fw1.commit(&mut rng, &pedersen_params, x1); // wip
let phi2 = fw2.commit(&mut rng, &pedersen_params, x2);
// fold instance
let folded_instance = NIFS::<G1Affine>::fold_instance(r, phi1, phi2, cmT);
// naive check r1cs of the folded witness
// assert_eq!(hadamard_product(Az, Bz), vec_add(vector_elem_product(Cz, u), E));
// naive check that the folded witness satisfies the relaxed r1cs
let Az = matrix_vector_product(&A, &folded_witness.W);
let Bz = matrix_vector_product(&B, &folded_witness.W);
let Cz = matrix_vector_product(&C, &folded_witness.W);
assert_eq!(
hadamard_product(Az, Bz),
vec_add(
vector_elem_product(&Cz, &folded_instance.u),
folded_witness.E
)
);
}
}

+ 55
- 12
src/utils.rs

@ -1,6 +1,7 @@
use ark_ec::AffineRepr;
use ark_ff::fields::PrimeField;
use core::ops::Add;
use core::ops::{Add, Sub};
use std::fmt;
pub fn vector_elem_product<F: PrimeField>(a: &Vec<F>, e: &F) -> Vec<F> {
// maybe move this method to operator a * e
@ -30,6 +31,11 @@ pub fn hadamard_product(a: Vec, b: Vec) -> Vec {
r
}
// rlin: random linear combination
// pub fn rlin<F: PrimeField>(a: Vec<F>, b: Vec<F>, r: F) -> Vec<F> {
// vec_add(a, vector_elem_product(&b, &r)) // WIP probably group loops
// }
pub fn naive_msm<C: AffineRepr>(s: &Vec<C::ScalarField>, p: &Vec<C>) -> C {
// check lengths
@ -47,17 +53,6 @@ pub fn vec_add(a: Vec, b: Vec) -> Vec {
}
r
}
// TODO instead of vec_add do:
// impl<'a, 'b, F> Add<&'b Vec<F>> for &'a Vec<F> {
// type Output = Vec<F>;
// fn add(self, rhs: &'b Vec<F>) -> Vec<F> {
// let mut r: Vec<F> = vec![F::zero(); self.len()];
// for i in 0..self.len() {
// r[i] = self[i] + rhs[i];
// }
// r
// }
// }
pub fn vec_sub<F: PrimeField>(a: Vec<F>, b: Vec<F>) -> Vec<F> {
let mut r: Vec<F> = vec![F::zero(); a.len()];
for i in 0..a.len() {
@ -66,6 +61,48 @@ pub fn vec_sub(a: Vec, b: Vec) -> Vec {
r
}
// instead of vec_{add/sub} can use Ve wrapper which has '+', '-' operators
#[derive(Debug, Clone, PartialEq)]
pub struct Ve<F: PrimeField>(pub Vec<F>);
impl<F: PrimeField> fmt::Display for Ve<F> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for (i, e) in self.0.iter().enumerate() {
if i == self.0.len() - 1 {
write!(f, "{}", e)?;
break;
}
write!(f, "{}, ", e)?;
}
Ok(())
}
}
impl<F: PrimeField> Add<Ve<F>> for Ve<F> {
type Output = Ve<F>;
fn add(self, rhs_vec: Self) -> Ve<F> {
let lhs = self.0.clone();
let rhs = rhs_vec.0.clone();
let mut r: Vec<F> = vec![F::zero(); lhs.len()];
for i in 0..self.0.len() {
r[i] = lhs[i] + rhs[i];
}
Ve(r)
}
}
impl<F: PrimeField> Sub<Ve<F>> for Ve<F> {
type Output = Ve<F>;
fn sub(self, rhs_vec: Self) -> Ve<F> {
let lhs = self.0.clone();
let rhs = rhs_vec.0.clone();
let mut r: Vec<F> = vec![F::zero(); lhs.len()];
for i in 0..self.0.len() {
r[i] = lhs[i] - rhs[i];
}
Ve(r)
}
}
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() {
@ -114,6 +151,12 @@ mod tests {
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.clone(), b.clone()), (Ve(a) + Ve(b)).0);
}
#[test]
fn test_ABC_hadamard() {

Loading…
Cancel
Save