arnaucube
arnaucube/nova-studyPublic Self-hosted repository
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
arnaucube root@arnaucube.com
Sat, 29 Apr 2023 14:15:25 +0200
commit

7cf729f00a6680f516ba6f3c6d583c68dcc30f57

parent

eb289313b0066d56aca8a7ba4a9b7cd7083a5423

6 files changed, 234 insertions(+), 104 deletions(-)

jump to
M README.md → README.md

@@ -9,4 +9,4 @@

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 -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.
M src/circuits.rs → src/circuits.rs

@@ -239,6 +239,7 @@ };

use crate::nifs; use crate::pedersen; + use crate::transcript::poseidon_test_config; use ark_ec::CurveGroup; // use ark_ed_on_mnt4_298::{constraints::EdwardsVar, EdwardsProjective}; use crate::pedersen::Commitment;

@@ -270,6 +271,7 @@ #[test]

fn test_nifs_gadget() { 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 poseidon_config = poseidon_test_config::<Fq>(); let cs = ConstraintSystem::<Fr>::new_ref();

@@ -280,7 +282,9 @@ let r = Fq::rand(&mut rng); // this would come from the transcript

let fw1 = nifs::FWit::<MNT6G1Projective>::new(w1.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( &mut transcript_p, &pedersen_params,

@@ -304,6 +308,6 @@

let valid = NIFSGadget::<MNT6G1Projective, MNT6G1Var>::verify( rVar, cmTVar, phi1Var, phi2Var, phi3Var, ); - println!("num_constraints={:?}", cs.num_constraints()); + // println!("num_constraints={:?}", cs.num_constraints()); } }
M src/nifs.rs → src/nifs.rs

@@ -1,5 +1,5 @@

// use ark_ec::AffineRepr; -use ark_ec::CurveGroup; +use ark_ec::{CurveGroup, Group}; use ark_ff::fields::PrimeField; use ark_std::{ rand::{Rng, RngCore},

@@ -11,6 +11,7 @@

use crate::pedersen::{Commitment, Params as PedersenParams, Pedersen, Proof as PedersenProof}; use crate::transcript::Transcript; use crate::utils::*; +use ark_crypto_primitives::sponge::Absorb; pub struct R1CS<F: PrimeField> { pub A: Vec<Vec<F>>,

@@ -35,7 +36,11 @@ W: Vec<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 { FWit::<C> { E: vec![C::ScalarField::zero(); e_len],

@@ -60,7 +65,11 @@ pub struct NIFS<C: CurveGroup> {

_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 pub fn comp_T( r1cs: &R1CS<C::ScalarField>,

@@ -138,7 +147,7 @@ }

// NIFS.P pub fn P( - tr: &mut Transcript<C::ScalarField>, + tr: &mut Transcript<C::ScalarField, C>, pedersen_params: &PedersenParams<C>, r: C::ScalarField, r1cs: &R1CS<C::ScalarField>,

@@ -151,7 +160,7 @@ let phi2 = fw2.commit(&pedersen_params);

// compute cross terms 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); // fold witness

@@ -193,7 +202,7 @@ true

} pub fn open_commitments( - tr: &mut Transcript<C::ScalarField>, + tr: &mut Transcript<C::ScalarField, C>, pedersen_params: &PedersenParams<C>, fw: &FWit<C>, phi: &Phi<C>,

@@ -207,7 +216,7 @@ let cmT_proof = Pedersen::prove(&pedersen_params, tr, &cmT, &T, &rT);

(cmE_proof, cmW_proof, cmT_proof) } pub fn verify_commitments( - tr: &mut Transcript<C::ScalarField>, + tr: &mut Transcript<C::ScalarField, C>, pedersen_params: &PedersenParams<C>, phi: Phi<C>, cmT: Commitment<C>,

@@ -273,6 +282,7 @@ #[cfg(test)]

mod tests { use super::*; use crate::pedersen::Pedersen; + use crate::transcript::poseidon_test_config; use ark_ec::CurveGroup; use ark_mnt4_298::{Fr, G1Projective}; use ark_std::{

@@ -287,6 +297,7 @@ #[test]

fn test_one_fold() { 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 poseidon_config = poseidon_test_config::<Fr>(); let (r1cs, w1, w2, _, x1, x2, _) = gen_test_values(&mut rng); let (A, B, C) = (r1cs.A.clone(), r1cs.B.clone(), r1cs.C.clone());

@@ -329,9 +340,9 @@ // NIFS.Verify:

assert!(NIFS::<G1Projective>::verify(r, &phi1, &phi2, &phi3, &cmT)); // 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 - 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 let (cmE_proof, cmW_proof, cmT_proof) = NIFS::<G1Projective>::open_commitments(

@@ -359,6 +370,7 @@ #[test]

fn test_two_fold() { let mut rng = ark_std::test_rng(); 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);

@@ -433,9 +445,9 @@ assert_eq!(phi_123_expected.cmE.0, phi_123.cmE.0);

assert_eq!(phi_123_expected.cmW.0, phi_123.cmW.0); // 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 - 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 let (cmE_proof, cmW_proof, cmT_proof) = NIFS::<G1Projective>::open_commitments(

@@ -463,6 +475,7 @@ #[test]

fn test_nifs_interface() { 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 poseidon_config = poseidon_test_config::<Fr>(); let (r1cs, w1, w2, _, x1, x2, _) = gen_test_values(&mut rng); let (A, B, C) = (r1cs.A.clone(), r1cs.B.clone(), r1cs.C.clone());

@@ -473,14 +486,14 @@ let fw1 = FWit::<G1Projective>::new(w1.clone(), A.len());

let fw2 = FWit::<G1Projective>::new(w2.clone(), A.len()); // 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 let (fw3, phi1, phi2, T, cmT) = NIFS::<G1Projective>::P(&mut transcript_p, &pedersen_params, r, &r1cs, fw1, fw2); // 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 let phi3 = NIFS::<G1Projective>::V(r, &phi1, &phi2, &cmT);
M src/pedersen.rs → src/pedersen.rs

@@ -1,13 +1,16 @@

use ark_ec::AffineRepr; -use ark_ec::CurveGroup; +use ark_ec::{CurveGroup, Group}; use ark_std::{ rand::{Rng, RngCore}, UniformRand, }; use std::marker::PhantomData; +use crate::utils::{naive_msm, vec_add, vector_elem_product}; + use crate::transcript::Transcript; -use crate::utils::{naive_msm, vec_add, vector_elem_product}; +use ark_crypto_primitives::sponge::Absorb; +use ark_ff::PrimeField; pub struct Proof_elem<C: CurveGroup> { R: C,

@@ -26,11 +29,19 @@ h: 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> { let h_scalar = C::ScalarField::rand(rng); let g: C = C::generator();

@@ -63,19 +74,19 @@ }

pub fn prove_elem( params: &Params<C>, - transcript: &mut Transcript<C::ScalarField>, + transcript: &mut Transcript<C::ScalarField, C>, cm: C, v: C::ScalarField, r: C::ScalarField, ) -> 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)); - 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 t2 = r2 + r * e;

@@ -84,19 +95,19 @@ Proof_elem::<C> { R, t1, t2 }

} pub fn prove( 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 v: &Vec<C::ScalarField>, r: &C::ScalarField, ) -> 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); - 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 ru_ = e * r + r1;

@@ -105,17 +116,17 @@ Proof::<C> { R, u_, ru_ }

} pub fn verify( params: &Params<C>, - transcript: &mut Transcript<C::ScalarField>, + transcript: &mut Transcript<C::ScalarField, C>, cm: Commitment<C>, proof: Proof<C>, ) -> bool { // 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 rhs = params.h.mul(proof.ru_) + naive_msm(&proof.u_, &params.generators); if lhs != rhs {

@@ -126,17 +137,17 @@ }

pub fn verify_elem( params: &Params<C>, - transcript: &mut Transcript<C::ScalarField>, + transcript: &mut Transcript<C::ScalarField, C>, cm: C, proof: Proof_elem<C>, ) -> bool { // 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 rhs = params.g.mul(proof.t1) + params.h.mul(proof.t2); if lhs != rhs {

@@ -153,11 +164,15 @@ pub struct CommitmentElem<C: CurveGroup> {

pub cm: C, 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( &self, params: &Params<C>, - transcript: &mut Transcript<C::ScalarField>, + transcript: &mut Transcript<C::ScalarField, C>, v: C::ScalarField, ) -> Proof_elem<C> { Pedersen::<C>::prove_elem(params, transcript, self.cm, v, self.r)

@@ -167,6 +182,7 @@

#[cfg(test)] mod tests { use super::*; + use crate::transcript::poseidon_test_config; use ark_ec::CurveGroup; use ark_mnt4_298::{Fr, G1Projective}; use std::ops::Mul;

@@ -179,11 +195,12 @@ // setup params

let params = Pedersen::<G1Projective>::new_params( &mut rng, 0, /* 0, as here we don't use commit_vec */ ); + let poseidon_config = poseidon_test_config::<Fr>(); // 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 - 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);

@@ -201,11 +218,12 @@

const n: usize = 10; // setup params let params = Pedersen::<G1Projective>::new_params(&mut rng, n); + let poseidon_config = poseidon_test_config::<Fr>(); // 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 - 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 r: Fr = Fr::rand(&mut rng);
M src/sumcheck.rs → src/sumcheck.rs

@@ -1,6 +1,7 @@

// 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. +use ark_ec::{CurveGroup, Group}; use ark_ff::{BigInteger, PrimeField}; use ark_poly::{ multivariate::{SparsePolynomial, SparseTerm, Term},

@@ -14,23 +15,30 @@ use ark_std::marker::PhantomData;

use ark_std::ops::Mul; use ark_std::{rand::Rng, UniformRand}; +use ark_crypto_primitives::sponge::{poseidon::PoseidonConfig, Absorb}; + use crate::transcript::Transcript; pub struct SumCheck< - F: PrimeField, + F: PrimeField + Absorb, + C: CurveGroup, UV: Polynomial<F> + DenseUVPolynomial<F>, MV: Polynomial<F> + DenseMVPolynomial<F>, > { _f: PhantomData<F>, + _c: PhantomData<C>, _uv: PhantomData<UV>, _mv: PhantomData<MV>, } impl< - F: PrimeField, + F: PrimeField + Absorb, + C: CurveGroup, UV: Polynomial<F> + DenseUVPolynomial<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 { assert!(point.len() >= g.num_vars(), "Invalid evaluation domain");

@@ -121,12 +129,12 @@ }

p } - pub fn prove(g: MV) -> (F, Vec<UV>, F) + pub fn prove(poseidon_config: &PoseidonConfig<F>, g: MV) -> (F, Vec<UV>, F) where <MV as Polynomial<F>>::Point: From<Vec<F>>, { // init transcript - let mut transcript: Transcript<F> = Transcript::<F>::new(); + let mut transcript = Transcript::<F, C>::new(poseidon_config); let v = g.num_vars();

@@ -137,12 +145,12 @@ let p = Self::point_complete(vec![], v, i);

H = H + g.evaluate(&p.into()); } - transcript.add(b"H", &H); + transcript.add(&H); let mut ss: Vec<UV> = Vec::new(); let mut r: Vec<F> = vec![]; for i in 0..v { - let r_i = transcript.get_challenge(b"r_i"); + let r_i = transcript.get_challenge(); r.push(r_i); let var_slots = v - 1 - i;

@@ -153,7 +161,7 @@ for j in 0..n_points {

let point = Self::point(r[..i].to_vec(), true, v, j); 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); }

@@ -161,10 +169,10 @@ let last_g_eval = g.evaluate(&r.into());

(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 - 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;

@@ -175,18 +183,18 @@ if i == 0 {

if c != s.evaluate(&F::zero()) + s.evaluate(&F::one()) { return false; } - let r_i = transcript.get_challenge(b"r_i"); + let r_i = transcript.get_challenge(); r.push(r_i); - transcript.add(b"s_i", s); + transcript.add_vec(s.coeffs()); continue; } - let r_i = transcript.get_challenge(b"r_i"); + let r_i = transcript.get_challenge(); r.push(r_i); if ss[i - 1].evaluate(&r[i - 1]) != s.evaluate(&F::zero()) + s.evaluate(&F::one()) { return false; } - transcript.add(b"s_i", s); + transcript.add_vec(s.coeffs()); } // last round if ss[ss.len() - 1].evaluate(&r[r.len() - 1]) != last_g_eval {

@@ -200,14 +208,15 @@

#[cfg(test)] mod tests { use super::*; - use ark_mnt4_298::Fr; // scalar field + use crate::transcript::poseidon_test_config; + use ark_mnt4_298::{Fr, G1Projective}; // scalar field #[test] fn test_new_point() { let f4 = Fr::from(4_u32); let f1 = Fr::from(1); 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); assert_eq!(vec![Some(f4), None, Some(f0), Some(f0), Some(f0),], p);

@@ -265,7 +274,7 @@ ),

]; 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))]); assert_eq!(e0.coeffs(), vec![Fr::from(16_u32)]);

@@ -293,13 +302,14 @@ ];

let p = SparsePolynomial::from_coefficients_slice(3, &terms); // 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)); // println!("proof {:?}", proof); - let v = SC::verify(proof); + let v = SC::verify(&poseidon_config, proof); assert!(v); }

@@ -335,12 +345,13 @@ // let p = SparsePolynomial::<Fr, SparseTerm>::rand(3, 3, &mut rng);

let p = rand_poly(3, 3, &mut rng); // 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); } }
M src/transcript.rs → src/transcript.rs

@@ -1,41 +1,125 @@

+use ark_ec::{AffineRepr, CurveGroup, Group}; use ark_ff::PrimeField; use ark_serialize::CanonicalSerialize; -use merlin::Transcript as MerlinTranscript; 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; + +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> Transcript<F> { - pub fn new() -> Self { - Self { - phantom: PhantomData::default(), - transcript: MerlinTranscript::new(b"transcript"), +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 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, 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(&mut self) -> F { + let c = self.sponge.squeeze_field_elements(1); + self.add(&c[0]); + c[0] } - 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 get_challenge_vec(&mut self, n: usize) -> Vec<F> { + let c = self.sponge.squeeze_field_elements(n); + self.sponge.absorb(&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()); +// } +// }