From 95078deca2d4217676bc64bf5c7af92405895359 Mon Sep 17 00:00:00 2001 From: arnaucube Date: Thu, 27 Jul 2023 23:17:27 +0200 Subject: [PATCH] port pedersen.rs & transcript.rs from https://github.com/arnaucube/nova-study --- Cargo.toml | 11 +++ src/lib.rs | 20 ++-- src/pedersen.rs | 232 ++++++++++++++++++++++++++++++++++++++++++++++ src/transcript.rs | 77 +++++++++++++++ src/utils.rs | 17 ++++ 5 files changed, 344 insertions(+), 13 deletions(-) create mode 100644 src/pedersen.rs create mode 100644 src/transcript.rs create mode 100644 src/utils.rs diff --git a/Cargo.toml b/Cargo.toml index 4047bef..13189cf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,3 +6,14 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +ark-std = "0.4.0" +ark-ff = "0.4.0" +ark-ec = { version = "0.4.0", default-features = false } +ark-poly = "0.4.0" +ark-serialize = { version = "0.4.0", default-features = false, features = [ "derive" ] } +rand = { version = "0.8", features = [ "std", "std_rng" ] } +merlin = { version = "3.0.0" } +ark-crypto-primitives = { version = "^0.4.0", default-features = false, features = [ "r1cs", "snark", "sponge", "crh" ] } + +[dev-dependencies] +ark-bls12-381 = "0.4.0" diff --git a/src/lib.rs b/src/lib.rs index 7d12d9a..609383e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,14 +1,8 @@ -pub fn add(left: usize, right: usize) -> usize { - left + right -} +#![allow(non_snake_case)] +#![allow(non_upper_case_globals)] +#![allow(unused)] // TMP +#![allow(dead_code)] // TMP -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } -} +pub mod pedersen; +pub mod transcript; +pub mod utils; diff --git a/src/pedersen.rs b/src/pedersen.rs new file mode 100644 index 0000000..e6e61dd --- /dev/null +++ b/src/pedersen.rs @@ -0,0 +1,232 @@ +/// pedersen.rs file and adapted from https://github.com/arnaucube/nova-study +use ark_ec::{CurveGroup, Group}; +use ark_std::{ + rand::{Rng, RngCore}, + UniformRand, +}; +use std::marker::PhantomData; + +use crate::utils::{vec_add, vec_scalar_mul}; + +use crate::transcript::Transcript; +use ark_crypto_primitives::sponge::Absorb; + +pub struct ProofElem { + R: C, + t1: C::ScalarField, + t2: C::ScalarField, +} +pub struct Proof { + R: C, + u_: Vec, + ru_: C::ScalarField, +} + +pub struct Params { + g: C, + h: C, + pub generators: Vec, +} + +pub struct Pedersen +where + ::ScalarField: Absorb, + ::BaseField: Absorb, +{ + _c: PhantomData, +} + +impl Pedersen +where + ::ScalarField: Absorb, + ::BaseField: Absorb, +{ + pub fn new_params(rng: &mut R, max: usize) -> Params { + let h_scalar = C::ScalarField::rand(rng); + let g: C = C::generator(); + let generators: Vec = vec![C::Affine::rand(rng); max]; + let params: Params = Params:: { + g, + h: g.mul(h_scalar), + generators, + }; + params + } + + pub fn commit_elem( + rng: &mut R, + params: &Params, + v: &C::ScalarField, + ) -> CommitmentElem { + let r = C::ScalarField::rand(rng); + let cm: C = params.g.mul(v) + params.h.mul(r); + CommitmentElem:: { cm, r } + } + pub fn commit( + params: &Params, + v: &Vec, + r: &C::ScalarField, // random value is provided, in order to be choosen by other parts of the protocol + ) -> Commitment { + let cm = params.h.mul(r) + C::msm(¶ms.generators, v).unwrap(); + Commitment::(cm) + } + + pub fn prove_elem( + params: &Params, + transcript: &mut Transcript, + cm: C, + v: C::ScalarField, + r: C::ScalarField, + ) -> ProofElem { + let r1 = transcript.get_challenge(); + let r2 = transcript.get_challenge(); + + let R: C = params.g.mul(r1) + params.h.mul(r2); + + transcript.add_point(&cm); + transcript.add_point(&R); + let e = transcript.get_challenge(); + + let t1 = r1 + v * e; + let t2 = r2 + r * e; + + ProofElem:: { R, t1, t2 } + } + pub fn prove( + params: &Params, + transcript: &mut Transcript, + cm: &Commitment, // TODO maybe it makes sense to not have a type wrapper and use directly C + v: &Vec, + r: &C::ScalarField, + ) -> Proof { + let r1 = transcript.get_challenge(); + let d = transcript.get_challenge_vec(v.len()); + + let R: C = params.h.mul(r1) + C::msm(¶ms.generators, &d).unwrap(); + + transcript.add_point(&cm.0); + transcript.add_point(&R); + let e = transcript.get_challenge(); + + let u_ = vec_add(&vec_scalar_mul(v, &e), &d); + let ru_ = e * r + r1; + + Proof:: { R, u_, ru_ } + } + pub fn verify( + params: &Params, + transcript: &mut Transcript, + cm: Commitment, + proof: Proof, + ) -> bool { + // r1, d just to match Prover's transcript + transcript.get_challenge(); // r_1 + transcript.get_challenge_vec(proof.u_.len()); // d + + 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_) + C::msm(¶ms.generators, &proof.u_).unwrap(); + if lhs != rhs { + return false; + } + true + } + + pub fn verify_elem( + params: &Params, + transcript: &mut Transcript, + cm: C, + proof: ProofElem, + ) -> bool { + // s1, s2 just to match Prover's transcript + transcript.get_challenge(); // r_1 + transcript.get_challenge(); // r_2 + + 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 { + return false; + } + true + } +} + +#[derive(Clone, Debug)] +pub struct Commitment(pub C); + +pub struct CommitmentElem { + pub cm: C, + pub r: C::ScalarField, +} +impl CommitmentElem +where + ::ScalarField: Absorb, + ::BaseField: Absorb, +{ + pub fn prove( + &self, + params: &Params, + transcript: &mut Transcript, + v: C::ScalarField, + ) -> ProofElem { + Pedersen::::prove_elem(params, transcript, self.cm, v, self.r) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::transcript::poseidon_test_config; + use ark_bls12_381::{Fr, G1Projective}; + + #[test] + fn test_pedersen_single_element() { + let mut rng = ark_std::test_rng(); + + // setup params + let params = Pedersen::::new_params( + &mut rng, 0, /* 0, as here we don't use commit_vec */ + ); + let poseidon_config = poseidon_test_config::(); + + // init Prover's transcript + let mut transcript_p = Transcript::::new(&poseidon_config); + // init Verifier's transcript + let mut transcript_v = Transcript::::new(&poseidon_config); + + let v = Fr::rand(&mut rng); + + let cm = Pedersen::commit_elem(&mut rng, ¶ms, &v); + let proof = cm.prove(¶ms, &mut transcript_p, v); + // also can use: + // let proof = Pedersen::prove_elem(¶ms, &mut transcript_p, cm.cm, v, cm.r); + let v = Pedersen::verify_elem(¶ms, &mut transcript_v, cm.cm, proof); + assert!(v); + } + #[test] + fn test_pedersen_vector() { + let mut rng = ark_std::test_rng(); + + const n: usize = 10; + // setup params + let params = Pedersen::::new_params(&mut rng, n); + let poseidon_config = poseidon_test_config::(); + + // init Prover's transcript + let mut transcript_p = Transcript::::new(&poseidon_config); + // init Verifier's transcript + let mut transcript_v = Transcript::::new(&poseidon_config); + + let v: Vec = vec![Fr::rand(&mut rng); n]; + let r: Fr = Fr::rand(&mut rng); + let cm = Pedersen::commit(¶ms, &v, &r); + let proof = Pedersen::prove(¶ms, &mut transcript_p, &cm, &v, &r); + let v = Pedersen::verify(¶ms, &mut transcript_v, cm, proof); + assert!(v); + } +} diff --git a/src/transcript.rs b/src/transcript.rs new file mode 100644 index 0000000..c750472 --- /dev/null +++ b/src/transcript.rs @@ -0,0 +1,77 @@ +/// transcript.rs file and adapted from https://github.com/arnaucube/nova-study +use ark_ec::{AffineRepr, CurveGroup}; +use ark_ff::PrimeField; +use std::marker::PhantomData; + +use ark_crypto_primitives::sponge::poseidon::{PoseidonConfig, PoseidonSponge}; +use ark_crypto_primitives::sponge::{Absorb, CryptographicSponge}; + +pub struct Transcript +where + ::BaseField: Absorb, +{ + // where F is the Constraint Field (eq. C::ScalarField) + sponge: PoseidonSponge, + _c: PhantomData, +} +impl Transcript +where + ::BaseField: Absorb, +{ + pub fn new(poseidon_config: &PoseidonConfig) -> Self { + let sponge = PoseidonSponge::::new(poseidon_config); + Transcript { + sponge, + _c: PhantomData, + } + } + 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 { + let c = self.sponge.squeeze_field_elements(n); + self.sponge.absorb(&c); + c + } +} + +use ark_crypto_primitives::sponge::poseidon::find_poseidon_ark_and_mds; + +// WARNING this is for test only +pub fn poseidon_test_config() -> PoseidonConfig { + let full_rounds = 8; + let partial_rounds = 31; + let alpha = 5; + let rate = 2; + + let (ark, mds) = find_poseidon_ark_and_mds::( + 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, + ) +} diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..23444a0 --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,17 @@ +use ark_ff::fields::PrimeField; + +pub fn vec_add(a: &Vec, b: &[F]) -> Vec { + let mut r: Vec = vec![F::zero(); a.len()]; + for i in 0..a.len() { + r[i] = a[i] + b[i]; + } + r +} + +pub fn vec_scalar_mul(vec: &[F], c: &F) -> Vec { + let mut result = vec![F::zero(); vec.len()]; + for (i, a) in vec.iter().enumerate() { + result[i] = *a * c; + } + result +}