Browse Source

add compute T, start test of simple folding

ivc-proofs
arnaucube 1 year ago
parent
commit
5a43ecd268
4 changed files with 187 additions and 41 deletions
  1. +1
    -1
      README.md
  2. +117
    -8
      src/nifs.rs
  3. +40
    -14
      src/pedersen.rs
  4. +29
    -18
      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, 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 and not to be used 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.

+ 117
- 8
src/nifs.rs

@ -2,16 +2,16 @@ use ark_ec::AffineRepr;
use ark_std::ops::Add;
use std::marker::PhantomData;
use crate::pedersen::Commitment;
use crate::pedersen::{Commitment, CommitmentVec};
use crate::r1cs::*;
use crate::transcript::Transcript;
use crate::utils::*;
// Phi: φ in the paper (later 𝖴), a folded instance
pub struct Phi<C: AffineRepr> {
cmE: Commitment<C>,
cmE: Commitment<C>, // TODO not Commitment but directly C (without rE)
u: C::ScalarField,
cmW: Commitment<C>,
cmW: Commitment<C>, // TODO not Commitment but directly C (without rW)
x: Vec<C::ScalarField>,
}
@ -23,25 +23,56 @@ pub struct FWit {
rW: C::ScalarField,
}
impl<C: AffineRepr> FWit<C> {
pub fn commit(&self) -> Phi<C> {
unimplemented!();
}
}
pub struct NIFS<C: AffineRepr> {
_phantom: PhantomData<C>,
}
impl<C: AffineRepr> NIFS<C> {
pub fn comp_T(
cs1: RelaxedR1CS<C::ScalarField>,
cs2: RelaxedR1CS<C::ScalarField>,
z1: &Vec<C::ScalarField>,
z2: &Vec<C::ScalarField>,
) -> Vec<C::ScalarField> {
// assuming cs1.R1CS == cs2.R1CS
let (A, B, C) = (cs1.ABC.A, cs1.ABC.B, cs1.ABC.C);
// 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 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 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
}
pub fn fold_witness(
r: C::ScalarField,
fw1: FWit<C>,
fw2: FWit<C>,
fw1: &FWit<C>,
fw2: &FWit<C>,
T: Vec<C::ScalarField>,
) -> FWit<C> {
let r2 = r * r;
let E: Vec<C::ScalarField> = vec_add(
// TODO this syntax will be simplified with future operators impl
vec_add(fw1.E, vector_elem_product(&T, &r)),
vec_add(fw1.E.clone(), vector_elem_product(&T, &r)),
vector_elem_product(&fw2.E, &r2),
);
let rE = fw1.rE + r * fw2.rE;
let W = vec_add(fw1.W, vector_elem_product(&fw2.W, &r));
let W = vec_add(fw1.W.clone(), vector_elem_product(&fw2.W, &r));
let rW = fw1.rW + r * fw2.rW;
FWit::<C> {
E: E.into(),
@ -55,7 +86,7 @@ impl NIFS {
r: C::ScalarField,
phi1: Phi<C>,
phi2: Phi<C>,
cmT: Commitment<C>,
cmT: CommitmentVec<C>,
) -> Phi<C> {
let r2 = r * r;
@ -78,3 +109,81 @@ impl NIFS {
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::pedersen::Pedersen;
use ark_bn254::{g1::G1Affine, Fr};
use ark_ec::CurveGroup;
use ark_std::{
rand::{Rng, RngCore},
UniformRand,
};
use ark_std::{One, Zero};
use std::ops::Mul;
#[test]
fn test_simple_folding() {
let mut rng = ark_std::test_rng();
// R1CS for: x^3 + x + 5 = y
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 B = to_F_matrix::<Fr>(vec![
vec![0, 1, 0, 0, 0, 0],
vec![0, 1, 0, 0, 0, 0],
vec![1, 0, 0, 0, 0, 0],
vec![1, 0, 0, 0, 0, 0],
]);
let C = to_F_matrix::<Fr>(vec![
vec![0, 0, 0, 1, 0, 0],
vec![0, 0, 0, 0, 1, 0],
vec![0, 0, 0, 0, 0, 1],
vec![0, 0, 1, 0, 0, 0],
]);
let z1 = to_F_vec::<Fr>(vec![1, 3, 35, 9, 27, 30]);
let z2 = to_F_vec::<Fr>(vec![1, 4, 73, 16, 64, 68]);
let relaxed_r1cs_1 = R1CS::<Fr> {
A: A.clone(),
B: B.clone(),
C: C.clone(),
}
.relax();
let relaxed_r1cs_2 = R1CS::<Fr> { A, B, C }.relax();
let T = NIFS::<G1Affine>::comp_T(relaxed_r1cs_1, relaxed_r1cs_2, &z1, &z2);
let params = Pedersen::<G1Affine>::new_params(&mut rng);
let cmT = Pedersen::commit_vec(&mut rng, &params, &T);
let r = Fr::rand(&mut rng); // this would come from the transcript
// WIP TMP
let fw1 = FWit::<G1Affine> {
E: vec![Fr::zero(); T.len()],
rE: Fr::zero(),
W: z1,
rW: Fr::zero(),
};
let fw2 = FWit::<G1Affine> {
E: vec![Fr::zero(); T.len()],
rE: Fr::zero(),
W: z2,
rW: Fr::zero(),
};
// fold witness
let folded_witness = NIFS::<G1Affine>::fold_witness(r, &fw1, &fw2, T);
let phi1 = fw1.commit(); // <- unimplemented
let phi2 = fw2.commit();
// 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));
}
}

+ 40
- 14
src/pedersen.rs

@ -1,8 +1,12 @@
use ark_ec::AffineRepr;
use ark_std::{rand::RngCore, UniformRand};
use ark_std::{
rand::{Rng, RngCore},
UniformRand,
};
use std::marker::PhantomData;
use crate::transcript::Transcript;
use crate::utils::naive_msm;
pub struct Proof<C: AffineRepr> {
R: C,
@ -20,15 +24,35 @@ pub struct Pedersen {
}
impl<C: AffineRepr> Pedersen<C> {
pub fn commit<R: RngCore>(
pub fn new_params<R: Rng>(rng: &mut R) -> Params<C> {
let h_scalar = C::ScalarField::rand(rng);
let g: C = C::generator();
let params: Params<C> = Params::<C> {
g,
h: g.mul(h_scalar).into(),
};
params
}
pub fn commit_elem<R: RngCore>(
rng: &mut R,
params: &Params<C>,
v: &C::ScalarField,
) -> (C, C::ScalarField) {
) -> Commitment<C> {
let r = C::ScalarField::rand(rng);
let cm: C = (params.g.mul(v) + params.h.mul(r)).into();
(cm, r)
Commitment::<C> { cm, r }
}
pub fn commit_vec<R: RngCore>(
rng: &mut R,
params: &Params<C>,
v: &Vec<C::ScalarField>,
) -> CommitmentVec<C> {
let r: Vec<C> = vec![C::rand(rng); v.len()]; // wip
let cm = naive_msm(v, &r);
CommitmentVec::<C> { cm, r }
}
pub fn prove(
params: &Params<C>,
transcript: &mut Transcript<C::ScalarField>,
@ -73,13 +97,18 @@ impl Pedersen {
}
}
pub struct CommitmentVec<C: AffineRepr> {
// WIP
pub cm: C,
pub r: Vec<C>,
}
pub struct Commitment<C: AffineRepr> {
pub cm: C,
pub r: C::ScalarField,
}
impl<C: AffineRepr> Commitment<C> {
pub fn prove(
self,
&self,
params: &Params<C>,
transcript: &mut Transcript<C::ScalarField>,
v: C::ScalarField,
@ -100,12 +129,7 @@ mod tests {
let mut rng = ark_std::test_rng();
// setup params
let h_scalar = Fr::rand(&mut rng);
let g: G1Affine = G1Affine::generator();
let params: Params<G1Affine> = Params::<G1Affine> {
g,
h: g.mul(h_scalar).into_affine(),
};
let params = Pedersen::<G1Affine>::new_params(&mut rng);
// init Prover's transcript
let mut transcript_p: Transcript<Fr> = Transcript::<Fr>::new();
@ -114,9 +138,11 @@ mod tests {
let v = Fr::rand(&mut rng);
let (cm, r) = Pedersen::commit(&mut rng, &params, &v);
let proof = Pedersen::prove(&params, &mut transcript_p, cm, v, r);
let v = Pedersen::verify(&params, &mut transcript_v, cm, proof);
let cm = Pedersen::commit_elem(&mut rng, &params, &v);
let proof = cm.prove(&params, &mut transcript_p, v);
// also can use:
// let proof = Pedersen::prove(&params, &mut transcript_p, cm.cm, v, cm.r);
let v = Pedersen::verify(&params, &mut transcript_v, cm.cm, proof);
assert!(v);
}
}

+ 29
- 18
src/utils.rs

@ -1,3 +1,4 @@
use ark_ec::AffineRepr;
use ark_ff::fields::PrimeField;
use core::ops::Add;
@ -29,6 +30,16 @@ pub fn hadamard_product(a: Vec, b: Vec) -> Vec {
r
}
pub fn naive_msm<C: AffineRepr>(s: &Vec<C::ScalarField>, p: &Vec<C>) -> C {
// check lengths
let mut r = p[0].mul(s[0]);
for i in 1..s.len() {
r = p[i].mul(s[i]);
}
r.into()
}
pub fn vec_add<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() {
@ -55,6 +66,24 @@ pub fn vec_sub(a: Vec, b: Vec) -> Vec {
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() {
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
}
#[cfg(test)]
mod tests {
use super::*;
@ -63,24 +92,6 @@ mod tests {
use ark_std::{One, Zero};
use std::ops::Mul;
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
}
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_matrix_vector_product() {
let A = to_F_matrix::<Fr>(vec![

Loading…
Cancel
Save