Browse Source

Implement poseidon based Transcript & integrate it

- remove Merlin based transcript
- implement poseidon based Transcript
- implement TranscriptVar gadget
- update transcript usage in the other modules to new Transcript
  interface
ivc-proofs
arnaucube 1 year ago
parent
commit
7cf729f00a
6 changed files with 236 additions and 106 deletions
  1. +1
    -1
      README.md
  2. +6
    -2
      src/circuits.rs
  3. +26
    -13
      src/nifs.rs
  4. +53
    -35
      src/pedersen.rs
  5. +36
    -25
      src/sumcheck.rs
  6. +114
    -30
      src/transcript.rs

+ 1
- 1
README.md

@ -9,4 +9,4 @@ This repo is an ongoing implementation, the code will be dirty for a while and n
Thanks to [Levs57](https://twitter.com/levs57), [Nalin Bhardwaj](https://twitter.com/nibnalin) and [Carlos Pérez](https://twitter.com/cperezz19) for clarifications on the Nova paper. Thanks to [Levs57](https://twitter.com/levs57), [Nalin Bhardwaj](https://twitter.com/nibnalin) and [Carlos Pérez](https://twitter.com/cperezz19) for clarifications on the Nova paper.
### Details ### Details
Initial impl using a cycle of pairing-friendly curves with Groth16 for the folding proofs (as an example, the tests use MNT4, MNT6 curves), once the full scheme works, will see how many constraints the folding circuits need and might change one of the sides to a non-pairing curve with a non-pairing proof instead of Groth16. Eventually would like to explore also using BN254 with Grumpkin for ending up verifying the proofs in Ethereum.
Current implementation uses a cycle of pairing-friendly curves with Groth16 for the folding proofs (as an example, the tests use MNT4, MNT6 curves), once the full scheme works, will see how many constraints the folding circuits need and might change one of the sides to a non-pairing curve with a non-pairing proof instead of Groth16. Eventually would like to explore also using BN254 with Grumpkin for ending up verifying the proofs in Ethereum.

+ 6
- 2
src/circuits.rs

@ -239,6 +239,7 @@ mod test {
use crate::nifs; use crate::nifs;
use crate::pedersen; use crate::pedersen;
use crate::transcript::poseidon_test_config;
use ark_ec::CurveGroup; use ark_ec::CurveGroup;
// use ark_ed_on_mnt4_298::{constraints::EdwardsVar, EdwardsProjective}; // use ark_ed_on_mnt4_298::{constraints::EdwardsVar, EdwardsProjective};
use crate::pedersen::Commitment; use crate::pedersen::Commitment;
@ -270,6 +271,7 @@ mod test {
fn test_nifs_gadget() { fn test_nifs_gadget() {
let mut rng = ark_std::test_rng(); let mut rng = ark_std::test_rng();
let pedersen_params = pedersen::Pedersen::<MNT6G1Projective>::new_params(&mut rng, 100); // 100 is wip, will get it from actual vec let pedersen_params = pedersen::Pedersen::<MNT6G1Projective>::new_params(&mut rng, 100); // 100 is wip, will get it from actual vec
let poseidon_config = poseidon_test_config::<Fq>();
let cs = ConstraintSystem::<Fr>::new_ref(); let cs = ConstraintSystem::<Fr>::new_ref();
@ -280,7 +282,9 @@ mod test {
let fw1 = nifs::FWit::<MNT6G1Projective>::new(w1.clone(), A.len()); let fw1 = nifs::FWit::<MNT6G1Projective>::new(w1.clone(), A.len());
let fw2 = nifs::FWit::<MNT6G1Projective>::new(w2.clone(), A.len()); let fw2 = nifs::FWit::<MNT6G1Projective>::new(w2.clone(), A.len());
let mut transcript_p = Transcript::<Fq>::new();
let mut transcript_p = Transcript::<Fq, MNT6G1Projective>::new(&poseidon_config);
let (fw3, phi1, phi2, T, cmT) = nifs::NIFS::<MNT6G1Projective>::P( let (fw3, phi1, phi2, T, cmT) = nifs::NIFS::<MNT6G1Projective>::P(
&mut transcript_p, &mut transcript_p,
&pedersen_params, &pedersen_params,
@ -304,6 +308,6 @@ mod test {
let valid = NIFSGadget::<MNT6G1Projective, MNT6G1Var>::verify( let valid = NIFSGadget::<MNT6G1Projective, MNT6G1Var>::verify(
rVar, cmTVar, phi1Var, phi2Var, phi3Var, rVar, cmTVar, phi1Var, phi2Var, phi3Var,
); );
println!("num_constraints={:?}", cs.num_constraints());
// println!("num_constraints={:?}", cs.num_constraints());
} }
} }

+ 26
- 13
src/nifs.rs

@ -1,5 +1,5 @@
// use ark_ec::AffineRepr; // use ark_ec::AffineRepr;
use ark_ec::CurveGroup;
use ark_ec::{CurveGroup, Group};
use ark_ff::fields::PrimeField; use ark_ff::fields::PrimeField;
use ark_std::{ use ark_std::{
rand::{Rng, RngCore}, rand::{Rng, RngCore},
@ -11,6 +11,7 @@ use std::marker::PhantomData;
use crate::pedersen::{Commitment, Params as PedersenParams, Pedersen, Proof as PedersenProof}; use crate::pedersen::{Commitment, Params as PedersenParams, Pedersen, Proof as PedersenProof};
use crate::transcript::Transcript; use crate::transcript::Transcript;
use crate::utils::*; use crate::utils::*;
use ark_crypto_primitives::sponge::Absorb;
pub struct R1CS<F: PrimeField> { pub struct R1CS<F: PrimeField> {
pub A: Vec<Vec<F>>, pub A: Vec<Vec<F>>,
@ -35,7 +36,11 @@ pub struct FWit {
rW: C::ScalarField, rW: C::ScalarField,
} }
impl<C: CurveGroup> FWit<C> {
impl<C: CurveGroup> FWit<C>
where
<C as Group>::ScalarField: Absorb,
<C as CurveGroup>::BaseField: Absorb,
{
pub fn new(z: Vec<C::ScalarField>, e_len: usize) -> Self { pub fn new(z: Vec<C::ScalarField>, e_len: usize) -> Self {
FWit::<C> { FWit::<C> {
E: vec![C::ScalarField::zero(); e_len], E: vec![C::ScalarField::zero(); e_len],
@ -60,7 +65,11 @@ pub struct NIFS {
_phantom: PhantomData<C>, _phantom: PhantomData<C>,
} }
impl<C: CurveGroup> NIFS<C> {
impl<C: CurveGroup> NIFS<C>
where
<C as Group>::ScalarField: Absorb,
<C as CurveGroup>::BaseField: Absorb,
{
// comp_T: compute cross-terms T // comp_T: compute cross-terms T
pub fn comp_T( pub fn comp_T(
r1cs: &R1CS<C::ScalarField>, r1cs: &R1CS<C::ScalarField>,
@ -138,7 +147,7 @@ impl NIFS {
// NIFS.P // NIFS.P
pub fn P( pub fn P(
tr: &mut Transcript<C::ScalarField>,
tr: &mut Transcript<C::ScalarField, C>,
pedersen_params: &PedersenParams<C>, pedersen_params: &PedersenParams<C>,
r: C::ScalarField, r: C::ScalarField,
r1cs: &R1CS<C::ScalarField>, r1cs: &R1CS<C::ScalarField>,
@ -151,7 +160,7 @@ impl NIFS {
// compute cross terms // compute cross terms
let T = Self::comp_T(&r1cs, phi1.u, phi2.u, &fw1.W, &fw2.W); let T = Self::comp_T(&r1cs, phi1.u, phi2.u, &fw1.W, &fw2.W);
let rT = tr.get_challenge(b"rT");
let rT = tr.get_challenge(); // r_T
let cmT = Pedersen::commit(&pedersen_params, &T, &rT); let cmT = Pedersen::commit(&pedersen_params, &T, &rT);
// fold witness // fold witness
@ -193,7 +202,7 @@ impl NIFS {
} }
pub fn open_commitments( pub fn open_commitments(
tr: &mut Transcript<C::ScalarField>,
tr: &mut Transcript<C::ScalarField, C>,
pedersen_params: &PedersenParams<C>, pedersen_params: &PedersenParams<C>,
fw: &FWit<C>, fw: &FWit<C>,
phi: &Phi<C>, phi: &Phi<C>,
@ -207,7 +216,7 @@ impl NIFS {
(cmE_proof, cmW_proof, cmT_proof) (cmE_proof, cmW_proof, cmT_proof)
} }
pub fn verify_commitments( pub fn verify_commitments(
tr: &mut Transcript<C::ScalarField>,
tr: &mut Transcript<C::ScalarField, C>,
pedersen_params: &PedersenParams<C>, pedersen_params: &PedersenParams<C>,
phi: Phi<C>, phi: Phi<C>,
cmT: Commitment<C>, cmT: Commitment<C>,
@ -273,6 +282,7 @@ pub fn gen_test_values(
mod tests { mod tests {
use super::*; use super::*;
use crate::pedersen::Pedersen; use crate::pedersen::Pedersen;
use crate::transcript::poseidon_test_config;
use ark_ec::CurveGroup; use ark_ec::CurveGroup;
use ark_mnt4_298::{Fr, G1Projective}; use ark_mnt4_298::{Fr, G1Projective};
use ark_std::{ use ark_std::{
@ -287,6 +297,7 @@ mod tests {
fn test_one_fold() { fn test_one_fold() {
let mut rng = ark_std::test_rng(); let mut rng = ark_std::test_rng();
let pedersen_params = Pedersen::<G1Projective>::new_params(&mut rng, 100); // 100 is wip, will get it from actual vec let pedersen_params = Pedersen::<G1Projective>::new_params(&mut rng, 100); // 100 is wip, will get it from actual vec
let poseidon_config = poseidon_test_config::<Fr>();
let (r1cs, w1, w2, _, x1, x2, _) = gen_test_values(&mut rng); let (r1cs, w1, w2, _, x1, x2, _) = gen_test_values(&mut rng);
let (A, B, C) = (r1cs.A.clone(), r1cs.B.clone(), r1cs.C.clone()); let (A, B, C) = (r1cs.A.clone(), r1cs.B.clone(), r1cs.C.clone());
@ -329,9 +340,9 @@ mod tests {
assert!(NIFS::<G1Projective>::verify(r, &phi1, &phi2, &phi3, &cmT)); assert!(NIFS::<G1Projective>::verify(r, &phi1, &phi2, &phi3, &cmT));
// init Prover's transcript // init Prover's transcript
let mut transcript_p: Transcript<Fr> = Transcript::<Fr>::new();
let mut transcript_p = Transcript::<Fr, G1Projective>::new(&poseidon_config);
// init Verifier's transcript // init Verifier's transcript
let mut transcript_v: Transcript<Fr> = Transcript::<Fr>::new();
let mut transcript_v = Transcript::<Fr, G1Projective>::new(&poseidon_config);
// check openings of phi3.cmE, phi3.cmW and cmT // check openings of phi3.cmE, phi3.cmW and cmT
let (cmE_proof, cmW_proof, cmT_proof) = NIFS::<G1Projective>::open_commitments( let (cmE_proof, cmW_proof, cmT_proof) = NIFS::<G1Projective>::open_commitments(
@ -359,6 +370,7 @@ mod tests {
fn test_two_fold() { fn test_two_fold() {
let mut rng = ark_std::test_rng(); let mut rng = ark_std::test_rng();
let pedersen_params = Pedersen::<G1Projective>::new_params(&mut rng, 6); let pedersen_params = Pedersen::<G1Projective>::new_params(&mut rng, 6);
let poseidon_config = poseidon_test_config::<Fr>();
let (r1cs, w1, w2, w3, x1, x2, x3) = gen_test_values(&mut rng); let (r1cs, w1, w2, w3, x1, x2, x3) = gen_test_values(&mut rng);
@ -433,9 +445,9 @@ mod tests {
assert_eq!(phi_123_expected.cmW.0, phi_123.cmW.0); assert_eq!(phi_123_expected.cmW.0, phi_123.cmW.0);
// init Prover's transcript // init Prover's transcript
let mut transcript_p: Transcript<Fr> = Transcript::<Fr>::new();
let mut transcript_p = Transcript::<Fr, G1Projective>::new(&poseidon_config);
// init Verifier's transcript // init Verifier's transcript
let mut transcript_v: Transcript<Fr> = Transcript::<Fr>::new();
let mut transcript_v = Transcript::<Fr, G1Projective>::new(&poseidon_config);
// check openings of phi_123.cmE, phi_123.cmW and cmT_123 // check openings of phi_123.cmE, phi_123.cmW and cmT_123
let (cmE_proof, cmW_proof, cmT_proof) = NIFS::<G1Projective>::open_commitments( let (cmE_proof, cmW_proof, cmT_proof) = NIFS::<G1Projective>::open_commitments(
@ -463,6 +475,7 @@ mod tests {
fn test_nifs_interface() { fn test_nifs_interface() {
let mut rng = ark_std::test_rng(); let mut rng = ark_std::test_rng();
let pedersen_params = Pedersen::<G1Projective>::new_params(&mut rng, 100); // 100 is wip, will get it from actual vec let pedersen_params = Pedersen::<G1Projective>::new_params(&mut rng, 100); // 100 is wip, will get it from actual vec
let poseidon_config = poseidon_test_config::<Fr>();
let (r1cs, w1, w2, _, x1, x2, _) = gen_test_values(&mut rng); let (r1cs, w1, w2, _, x1, x2, _) = gen_test_values(&mut rng);
let (A, B, C) = (r1cs.A.clone(), r1cs.B.clone(), r1cs.C.clone()); let (A, B, C) = (r1cs.A.clone(), r1cs.B.clone(), r1cs.C.clone());
@ -473,14 +486,14 @@ mod tests {
let fw2 = FWit::<G1Projective>::new(w2.clone(), A.len()); let fw2 = FWit::<G1Projective>::new(w2.clone(), A.len());
// init Prover's transcript // init Prover's transcript
let mut transcript_p: Transcript<Fr> = Transcript::<Fr>::new();
let mut transcript_p = Transcript::<Fr, G1Projective>::new(&poseidon_config);
// NIFS.P // NIFS.P
let (fw3, phi1, phi2, T, cmT) = let (fw3, phi1, phi2, T, cmT) =
NIFS::<G1Projective>::P(&mut transcript_p, &pedersen_params, r, &r1cs, fw1, fw2); NIFS::<G1Projective>::P(&mut transcript_p, &pedersen_params, r, &r1cs, fw1, fw2);
// init Verifier's transcript // init Verifier's transcript
let mut transcript_v: Transcript<Fr> = Transcript::<Fr>::new();
let mut transcript_v = Transcript::<Fr, G1Projective>::new(&poseidon_config);
// NIFS.V // NIFS.V
let phi3 = NIFS::<G1Projective>::V(r, &phi1, &phi2, &cmT); let phi3 = NIFS::<G1Projective>::V(r, &phi1, &phi2, &cmT);

+ 53
- 35
src/pedersen.rs

@ -1,14 +1,17 @@
use ark_ec::AffineRepr; use ark_ec::AffineRepr;
use ark_ec::CurveGroup;
use ark_ec::{CurveGroup, Group};
use ark_std::{ use ark_std::{
rand::{Rng, RngCore}, rand::{Rng, RngCore},
UniformRand, UniformRand,
}; };
use std::marker::PhantomData; use std::marker::PhantomData;
use crate::transcript::Transcript;
use crate::utils::{naive_msm, vec_add, vector_elem_product}; use crate::utils::{naive_msm, vec_add, vector_elem_product};
use crate::transcript::Transcript;
use ark_crypto_primitives::sponge::Absorb;
use ark_ff::PrimeField;
pub struct Proof_elem<C: CurveGroup> { pub struct Proof_elem<C: CurveGroup> {
R: C, R: C,
t1: C::ScalarField, t1: C::ScalarField,
@ -26,11 +29,19 @@ pub struct Params {
pub generators: Vec<C>, pub generators: Vec<C>,
} }
pub struct Pedersen<C: CurveGroup> {
_phantom: PhantomData<C>,
pub struct Pedersen<C: CurveGroup>
where
<C as Group>::ScalarField: Absorb,
<C as CurveGroup>::BaseField: Absorb,
{
_c: PhantomData<C>,
} }
impl<C: CurveGroup> Pedersen<C> {
impl<C: CurveGroup> Pedersen<C>
where
<C as Group>::ScalarField: Absorb,
<C as CurveGroup>::BaseField: Absorb,
{
pub fn new_params<R: Rng>(rng: &mut R, max: usize) -> Params<C> { pub fn new_params<R: Rng>(rng: &mut R, max: usize) -> Params<C> {
let h_scalar = C::ScalarField::rand(rng); let h_scalar = C::ScalarField::rand(rng);
let g: C = C::generator(); let g: C = C::generator();
@ -63,19 +74,19 @@ impl Pedersen {
pub fn prove_elem( pub fn prove_elem(
params: &Params<C>, params: &Params<C>,
transcript: &mut Transcript<C::ScalarField>,
transcript: &mut Transcript<C::ScalarField, C>,
cm: C, cm: C,
v: C::ScalarField, v: C::ScalarField,
r: C::ScalarField, r: C::ScalarField,
) -> Proof_elem<C> { ) -> Proof_elem<C> {
let r1 = transcript.get_challenge(b"r_1");
let r2 = transcript.get_challenge(b"r_2");
let r1 = transcript.get_challenge();
let r2 = transcript.get_challenge();
let R: C = (params.g.mul(r1) + params.h.mul(r2)); let R: C = (params.g.mul(r1) + params.h.mul(r2));
transcript.add(b"cm", &cm);
transcript.add(b"R", &R);
let e = transcript.get_challenge(b"e");
transcript.add_point(&cm);
transcript.add_point(&R);
let e = transcript.get_challenge();
let t1 = r1 + v * e; let t1 = r1 + v * e;
let t2 = r2 + r * e; let t2 = r2 + r * e;
@ -84,19 +95,19 @@ impl Pedersen {
} }
pub fn prove( pub fn prove(
params: &Params<C>, params: &Params<C>,
transcript: &mut Transcript<C::ScalarField>,
transcript: &mut Transcript<C::ScalarField, C>,
cm: &Commitment<C>, // TODO maybe it makes sense to not have a type wrapper and use directly C cm: &Commitment<C>, // TODO maybe it makes sense to not have a type wrapper and use directly C
v: &Vec<C::ScalarField>, v: &Vec<C::ScalarField>,
r: &C::ScalarField, r: &C::ScalarField,
) -> Proof<C> { ) -> Proof<C> {
let r1 = transcript.get_challenge(b"r_1");
let d = transcript.get_challenge_vec(b"d", v.len());
let r1 = transcript.get_challenge();
let d = transcript.get_challenge_vec(v.len());
let R: C = params.h.mul(r1) + naive_msm(&d, &params.generators); let R: C = params.h.mul(r1) + naive_msm(&d, &params.generators);
transcript.add(b"cm", &cm.0);
transcript.add(b"R", &R);
let e = transcript.get_challenge(b"e");
transcript.add_point(&cm.0);
transcript.add_point(&R);
let e = transcript.get_challenge();
let u_ = vec_add(&vector_elem_product(&v, &e), &d); let u_ = vec_add(&vector_elem_product(&v, &e), &d);
let ru_ = e * r + r1; let ru_ = e * r + r1;
@ -105,17 +116,17 @@ impl Pedersen {
} }
pub fn verify( pub fn verify(
params: &Params<C>, params: &Params<C>,
transcript: &mut Transcript<C::ScalarField>,
transcript: &mut Transcript<C::ScalarField, C>,
cm: Commitment<C>, cm: Commitment<C>,
proof: Proof<C>, proof: Proof<C>,
) -> bool { ) -> bool {
// r1, d just to match Prover's transcript // r1, d just to match Prover's transcript
transcript.get_challenge(b"r_1");
transcript.get_challenge_vec(b"d", proof.u_.len());
transcript.get_challenge(); // r_1
transcript.get_challenge_vec(proof.u_.len()); // d
transcript.add(b"cm", &cm.0);
transcript.add(b"R", &proof.R);
let e = transcript.get_challenge(b"e");
transcript.add_point(&cm.0);
transcript.add_point(&proof.R);
let e = transcript.get_challenge();
let lhs = proof.R + cm.0.mul(e); let lhs = proof.R + cm.0.mul(e);
let rhs = params.h.mul(proof.ru_) + naive_msm(&proof.u_, &params.generators); let rhs = params.h.mul(proof.ru_) + naive_msm(&proof.u_, &params.generators);
if lhs != rhs { if lhs != rhs {
@ -126,17 +137,17 @@ impl Pedersen {
pub fn verify_elem( pub fn verify_elem(
params: &Params<C>, params: &Params<C>,
transcript: &mut Transcript<C::ScalarField>,
transcript: &mut Transcript<C::ScalarField, C>,
cm: C, cm: C,
proof: Proof_elem<C>, proof: Proof_elem<C>,
) -> bool { ) -> bool {
// s1, s2 just to match Prover's transcript // s1, s2 just to match Prover's transcript
transcript.get_challenge(b"r_1");
transcript.get_challenge(b"r_2");
transcript.get_challenge(); // r_1
transcript.get_challenge(); // r_2
transcript.add(b"cm", &cm);
transcript.add(b"R", &proof.R);
let e = transcript.get_challenge(b"e");
transcript.add_point(&cm);
transcript.add_point(&proof.R);
let e = transcript.get_challenge();
let lhs = proof.R + cm.mul(e); let lhs = proof.R + cm.mul(e);
let rhs = params.g.mul(proof.t1) + params.h.mul(proof.t2); let rhs = params.g.mul(proof.t1) + params.h.mul(proof.t2);
if lhs != rhs { if lhs != rhs {
@ -153,11 +164,15 @@ pub struct CommitmentElem {
pub cm: C, pub cm: C,
pub r: C::ScalarField, pub r: C::ScalarField,
} }
impl<C: CurveGroup> CommitmentElem<C> {
impl<C: CurveGroup> CommitmentElem<C>
where
<C as Group>::ScalarField: Absorb,
<C as CurveGroup>::BaseField: Absorb,
{
pub fn prove( pub fn prove(
&self, &self,
params: &Params<C>, params: &Params<C>,
transcript: &mut Transcript<C::ScalarField>,
transcript: &mut Transcript<C::ScalarField, C>,
v: C::ScalarField, v: C::ScalarField,
) -> Proof_elem<C> { ) -> Proof_elem<C> {
Pedersen::<C>::prove_elem(params, transcript, self.cm, v, self.r) Pedersen::<C>::prove_elem(params, transcript, self.cm, v, self.r)
@ -167,6 +182,7 @@ impl CommitmentElem {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::transcript::poseidon_test_config;
use ark_ec::CurveGroup; use ark_ec::CurveGroup;
use ark_mnt4_298::{Fr, G1Projective}; use ark_mnt4_298::{Fr, G1Projective};
use std::ops::Mul; use std::ops::Mul;
@ -179,11 +195,12 @@ mod tests {
let params = Pedersen::<G1Projective>::new_params( let params = Pedersen::<G1Projective>::new_params(
&mut rng, 0, /* 0, as here we don't use commit_vec */ &mut rng, 0, /* 0, as here we don't use commit_vec */
); );
let poseidon_config = poseidon_test_config::<Fr>();
// init Prover's transcript // init Prover's transcript
let mut transcript_p: Transcript<Fr> = Transcript::<Fr>::new();
let mut transcript_p = Transcript::<Fr, G1Projective>::new(&poseidon_config);
// init Verifier's transcript // init Verifier's transcript
let mut transcript_v: Transcript<Fr> = Transcript::<Fr>::new();
let mut transcript_v = Transcript::<Fr, G1Projective>::new(&poseidon_config);
let v = Fr::rand(&mut rng); let v = Fr::rand(&mut rng);
@ -201,11 +218,12 @@ 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::<G1Projective>::new_params(&mut rng, n);
let poseidon_config = poseidon_test_config::<Fr>();
// init Prover's transcript // init Prover's transcript
let mut transcript_p: Transcript<Fr> = Transcript::<Fr>::new();
let mut transcript_p = Transcript::<Fr, G1Projective>::new(&poseidon_config);
// init Verifier's transcript // init Verifier's transcript
let mut transcript_v: Transcript<Fr> = Transcript::<Fr>::new();
let mut transcript_v = Transcript::<Fr, G1Projective>::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);

+ 36
- 25
src/sumcheck.rs

@ -1,6 +1,7 @@
// this file contains a sum-check protocol initial implementation, not used by the rest of the repo // this file contains a sum-check protocol initial implementation, not used by the rest of the repo
// but implemented as an exercise and it will probably be used in the future. // but implemented as an exercise and it will probably be used in the future.
use ark_ec::{CurveGroup, Group};
use ark_ff::{BigInteger, PrimeField}; use ark_ff::{BigInteger, PrimeField};
use ark_poly::{ use ark_poly::{
multivariate::{SparsePolynomial, SparseTerm, Term}, multivariate::{SparsePolynomial, SparseTerm, Term},
@ -14,23 +15,30 @@ use ark_std::marker::PhantomData;
use ark_std::ops::Mul; use ark_std::ops::Mul;
use ark_std::{rand::Rng, UniformRand}; use ark_std::{rand::Rng, UniformRand};
use ark_crypto_primitives::sponge::{poseidon::PoseidonConfig, Absorb};
use crate::transcript::Transcript; use crate::transcript::Transcript;
pub struct SumCheck< pub struct SumCheck<
F: PrimeField,
F: PrimeField + Absorb,
C: CurveGroup,
UV: Polynomial<F> + DenseUVPolynomial<F>, UV: Polynomial<F> + DenseUVPolynomial<F>,
MV: Polynomial<F> + DenseMVPolynomial<F>, MV: Polynomial<F> + DenseMVPolynomial<F>,
> { > {
_f: PhantomData<F>, _f: PhantomData<F>,
_c: PhantomData<C>,
_uv: PhantomData<UV>, _uv: PhantomData<UV>,
_mv: PhantomData<MV>, _mv: PhantomData<MV>,
} }
impl< impl<
F: PrimeField,
F: PrimeField + Absorb,
C: CurveGroup,
UV: Polynomial<F> + DenseUVPolynomial<F>, UV: Polynomial<F> + DenseUVPolynomial<F>,
MV: Polynomial<F> + DenseMVPolynomial<F>, MV: Polynomial<F> + DenseMVPolynomial<F>,
> SumCheck<F, UV, MV>
> SumCheck<F, C, UV, MV>
where
<C as CurveGroup>::BaseField: Absorb,
{ {
fn partial_evaluate(g: &MV, point: &[Option<F>]) -> UV { fn partial_evaluate(g: &MV, point: &[Option<F>]) -> UV {
assert!(point.len() >= g.num_vars(), "Invalid evaluation domain"); assert!(point.len() >= g.num_vars(), "Invalid evaluation domain");
@ -121,12 +129,12 @@ impl<
p p
} }
pub fn prove(g: MV) -> (F, Vec<UV>, F)
pub fn prove(poseidon_config: &PoseidonConfig<F>, g: MV) -> (F, Vec<UV>, F)
where where
<MV as Polynomial<F>>::Point: From<Vec<F>>, <MV as Polynomial<F>>::Point: From<Vec<F>>,
{ {
// init transcript // init transcript
let mut transcript: Transcript<F> = Transcript::<F>::new();
let mut transcript = Transcript::<F, C>::new(poseidon_config);
let v = g.num_vars(); let v = g.num_vars();
@ -137,12 +145,12 @@ impl<
H = H + g.evaluate(&p.into()); H = H + g.evaluate(&p.into());
} }
transcript.add(b"H", &H);
transcript.add(&H);
let mut ss: Vec<UV> = Vec::new(); let mut ss: Vec<UV> = Vec::new();
let mut r: Vec<F> = vec![]; let mut r: Vec<F> = vec![];
for i in 0..v { for i in 0..v {
let r_i = transcript.get_challenge(b"r_i");
let r_i = transcript.get_challenge();
r.push(r_i); r.push(r_i);
let var_slots = v - 1 - i; let var_slots = v - 1 - i;
@ -153,7 +161,7 @@ impl<
let point = Self::point(r[..i].to_vec(), true, v, j); let point = Self::point(r[..i].to_vec(), true, v, j);
s_i = s_i + Self::partial_evaluate(&g, &point); s_i = s_i + Self::partial_evaluate(&g, &point);
} }
transcript.add(b"s_i", &s_i);
transcript.add_vec(s_i.coeffs());
ss.push(s_i); ss.push(s_i);
} }
@ -161,10 +169,10 @@ impl<
(H, ss, last_g_eval) (H, ss, last_g_eval)
} }
pub fn verify(proof: (F, Vec<UV>, F)) -> bool {
pub fn verify(poseidon_config: &PoseidonConfig<F>, proof: (F, Vec<UV>, F)) -> bool {
// init transcript // init transcript
let mut transcript: Transcript<F> = Transcript::<F>::new();
transcript.add(b"H", &proof.0);
let mut transcript = Transcript::<F, C>::new(poseidon_config);
transcript.add(&proof.0);
let (c, ss, last_g_eval) = proof; let (c, ss, last_g_eval) = proof;
@ -175,18 +183,18 @@ impl<
if c != s.evaluate(&F::zero()) + s.evaluate(&F::one()) { if c != s.evaluate(&F::zero()) + s.evaluate(&F::one()) {
return false; return false;
} }
let r_i = transcript.get_challenge(b"r_i");
let r_i = transcript.get_challenge();
r.push(r_i); r.push(r_i);
transcript.add(b"s_i", s);
transcript.add_vec(s.coeffs());
continue; continue;
} }
let r_i = transcript.get_challenge(b"r_i");
let r_i = transcript.get_challenge();
r.push(r_i); r.push(r_i);
if ss[i - 1].evaluate(&r[i - 1]) != s.evaluate(&F::zero()) + s.evaluate(&F::one()) { if ss[i - 1].evaluate(&r[i - 1]) != s.evaluate(&F::zero()) + s.evaluate(&F::one()) {
return false; return false;
} }
transcript.add(b"s_i", s);
transcript.add_vec(s.coeffs());
} }
// last round // last round
if ss[ss.len() - 1].evaluate(&r[r.len() - 1]) != last_g_eval { if ss[ss.len() - 1].evaluate(&r[r.len() - 1]) != last_g_eval {
@ -200,14 +208,15 @@ impl<
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use ark_mnt4_298::Fr; // scalar field
use crate::transcript::poseidon_test_config;
use ark_mnt4_298::{Fr, G1Projective}; // scalar field
#[test] #[test]
fn test_new_point() { fn test_new_point() {
let f4 = Fr::from(4_u32); let f4 = Fr::from(4_u32);
let f1 = Fr::from(1); let f1 = Fr::from(1);
let f0 = Fr::from(0); let f0 = Fr::from(0);
type SC = SumCheck<Fr, DensePolynomial<Fr>, SparsePolynomial<Fr, SparseTerm>>;
type SC = SumCheck<Fr, G1Projective, DensePolynomial<Fr>, SparsePolynomial<Fr, SparseTerm>>;
let p = SC::point(vec![Fr::from(4_u32)], true, 5, 0); let p = SC::point(vec![Fr::from(4_u32)], true, 5, 0);
assert_eq!(vec![Some(f4), None, Some(f0), Some(f0), Some(f0),], p); assert_eq!(vec![Some(f4), None, Some(f0), Some(f0), Some(f0),], p);
@ -265,7 +274,7 @@ mod tests {
]; ];
let p = SparsePolynomial::from_coefficients_slice(3, &terms); let p = SparsePolynomial::from_coefficients_slice(3, &terms);
type SC = SumCheck<Fr, DensePolynomial<Fr>, SparsePolynomial<Fr, SparseTerm>>;
type SC = SumCheck<Fr, G1Projective, DensePolynomial<Fr>, SparsePolynomial<Fr, SparseTerm>>;
let e0 = SC::partial_evaluate(&p, &[Some(Fr::from(2_u32)), None, Some(Fr::from(0_u32))]); let e0 = SC::partial_evaluate(&p, &[Some(Fr::from(2_u32)), None, Some(Fr::from(0_u32))]);
assert_eq!(e0.coeffs(), vec![Fr::from(16_u32)]); assert_eq!(e0.coeffs(), vec![Fr::from(16_u32)]);
@ -293,13 +302,14 @@ mod tests {
let p = SparsePolynomial::from_coefficients_slice(3, &terms); let p = SparsePolynomial::from_coefficients_slice(3, &terms);
// println!("p {:?}", p); // println!("p {:?}", p);
type SC = SumCheck<Fr, DensePolynomial<Fr>, SparsePolynomial<Fr, SparseTerm>>;
let poseidon_config = poseidon_test_config::<Fr>();
type SC = SumCheck<Fr, G1Projective, DensePolynomial<Fr>, SparsePolynomial<Fr, SparseTerm>>;
let proof = SC::prove(p);
let proof = SC::prove(&poseidon_config, p);
assert_eq!(proof.0, Fr::from(12_u32)); assert_eq!(proof.0, Fr::from(12_u32));
// println!("proof {:?}", proof); // println!("proof {:?}", proof);
let v = SC::verify(proof);
let v = SC::verify(&poseidon_config, proof);
assert!(v); assert!(v);
} }
@ -335,12 +345,13 @@ mod tests {
let p = rand_poly(3, 3, &mut rng); let p = rand_poly(3, 3, &mut rng);
// println!("p {:?}", p); // println!("p {:?}", p);
type SC = SumCheck<Fr, DensePolynomial<Fr>, SparsePolynomial<Fr, SparseTerm>>;
let poseidon_config = poseidon_test_config::<Fr>();
type SC = SumCheck<Fr, G1Projective, DensePolynomial<Fr>, SparsePolynomial<Fr, SparseTerm>>;
let proof = SC::prove(p);
println!("proof.s len {:?}", proof.1.len());
let proof = SC::prove(&poseidon_config, p);
// println!("proof.s len {:?}", proof.1.len());
let v = SC::verify(proof);
let v = SC::verify(&poseidon_config, proof);
assert!(v); assert!(v);
} }
} }

+ 114
- 30
src/transcript.rs

@ -1,41 +1,125 @@
use ark_ec::{AffineRepr, CurveGroup, Group};
use ark_ff::PrimeField; use ark_ff::PrimeField;
use ark_serialize::CanonicalSerialize; use ark_serialize::CanonicalSerialize;
use merlin::Transcript as MerlinTranscript;
use std::marker::PhantomData; use std::marker::PhantomData;
// TODO poseidon transcript (not keccak)
use ark_r1cs_std::fields::fp::FpVar;
// This Transcript approach is a modified version from https://github.com/arkworks-rs/gemini ,
// using Merlin transcript (https://merlin.cool).
pub struct Transcript<F: PrimeField> {
phantom: PhantomData<F>,
transcript: MerlinTranscript,
}
use ark_crypto_primitives::sponge::poseidon::{
constraints::PoseidonSpongeVar, PoseidonConfig, PoseidonSponge,
};
use ark_crypto_primitives::sponge::{
constraints::CryptographicSpongeVar, Absorb, CryptographicSponge,
};
use ark_relations::r1cs::{ConstraintSystemRef, SynthesisError};
use ark_poly::DenseUVPolynomial;
impl<F: PrimeField> Transcript<F> {
pub fn new() -> Self {
Self {
phantom: PhantomData::default(),
transcript: MerlinTranscript::new(b"transcript"),
pub struct Transcript<F: PrimeField + Absorb, C: CurveGroup>
where
<C as CurveGroup>::BaseField: Absorb,
{
// where F is the Constraint Field (eq. C::ScalarField)
sponge: PoseidonSponge<F>,
_c: PhantomData<C>,
}
impl<F: PrimeField + Absorb, C: CurveGroup> Transcript<F, C>
where
<C as CurveGroup>::BaseField: Absorb,
{
pub fn new(poseidon_config: &PoseidonConfig<F>) -> Self {
let mut sponge = PoseidonSponge::<F>::new(&poseidon_config);
Transcript {
sponge,
_c: PhantomData,
} }
} }
pub fn add<S: CanonicalSerialize>(&mut self, label: &'static [u8], r: &S) {
let mut msg = Vec::new();
r.serialize_uncompressed(&mut msg).unwrap();
self.transcript.append_message(label, &msg);
}
pub fn get_challenge(&mut self, label: &'static [u8]) -> F {
let mut bytes = [0u8; 64];
self.transcript.challenge_bytes(label, &mut bytes);
let challenge = F::from_le_bytes_mod_order(bytes.as_ref());
self.add(b"get challenge", &challenge);
challenge
}
pub fn get_challenge_vec(&mut self, label: &'static [u8], n: usize) -> Vec<F> {
let mut c: Vec<F> = vec![F::zero(); n];
for i in 0..n {
c[i] = self.get_challenge(label);
}
pub fn add(&mut self, v: &F) {
self.sponge.absorb(&v);
}
pub fn add_vec(&mut self, v: &[F]) {
self.sponge.absorb(&v);
}
pub fn add_point(&mut self, v: &C) {
let v_affine = v.into_affine();
let xy = v_affine.xy().unwrap(); // WIP
self.sponge.absorb(&vec![xy.0, xy.1]);
}
pub fn get_challenge(&mut self) -> F {
let c = self.sponge.squeeze_field_elements(1);
self.add(&c[0]);
c[0]
}
pub fn get_challenge_vec(&mut self, n: usize) -> Vec<F> {
let c = self.sponge.squeeze_field_elements(n);
self.sponge.absorb(&c);
c c
} }
} }
pub struct TranscriptVar<F: PrimeField> {
// where F is the Constraint Field
sponge: PoseidonSpongeVar<F>,
}
impl<F: PrimeField> TranscriptVar<F> {
pub fn new(cs: ConstraintSystemRef<F>, poseidon_config: PoseidonConfig<F>) -> Self {
let mut sponge = PoseidonSpongeVar::<F>::new(cs.clone(), &poseidon_config);
Self { sponge }
}
pub fn add(&mut self, v: FpVar<F>) -> Result<(), SynthesisError> {
self.sponge.absorb(&v)
}
pub fn get_challenge(&mut self) -> Result<FpVar<F>, SynthesisError> {
let c = self.sponge.squeeze_field_elements(1)?;
self.sponge.absorb(&c[0]);
Ok(c[0].clone())
}
pub fn get_challenge_vec(&mut self, n: usize) -> Result<Vec<FpVar<F>>, SynthesisError> {
let c = self.sponge.squeeze_field_elements(n)?;
self.sponge.absorb(&c);
Ok(c)
}
}
use ark_crypto_primitives::sponge::poseidon::find_poseidon_ark_and_mds;
// WARNING this is for test only
pub fn poseidon_test_config<F: PrimeField>() -> PoseidonConfig<F> {
let full_rounds = 8;
let partial_rounds = 31;
let alpha = 5;
let rate = 2;
let (ark, mds) = find_poseidon_ark_and_mds::<F>(
F::MODULUS_BIT_SIZE as u64,
rate,
full_rounds,
partial_rounds,
0,
);
PoseidonConfig::new(
full_rounds as usize,
partial_rounds as usize,
alpha,
mds,
ark,
rate,
1,
)
}
// #[cfg(test)]
// mod tests {
// use super::*;
// use ark_mnt4_298::{Fr, G1Projective}; // scalar field
// use ark_std::{One, Zero};
//
// #[test]
// fn test_poseidon() {
// let config = poseidon_test_config::<Fr>();
// let mut tr = Transcript::<Fr, G1Projective>::new(&config);
// tr.add(&Fr::one());
// println!("c {:?}", tr.get_challenge().to_string());
// }
// }

Loading…
Cancel
Save