From f2b104553ce67c346df6d0de3e298da292beb45e Mon Sep 17 00:00:00 2001 From: PayneJoe Date: Fri, 15 Dec 2023 01:23:55 +0800 Subject: [PATCH 1/2] refactor commit&ro trait --- .vscode/settings.json | 3 + Cargo.toml | 2 +- src/lib.rs | 3 + src/nifs.rs | 0 src/poseidon/mod.rs | 4 +- ...idon_constant.rs => poseidon_constants.rs} | 0 src/primary/bn254_field.rs | 15 +-- src/primary/nifs.rs | 2 +- src/provider/bn254.rs | 24 +++++ src/provider/keccak.rs | 0 src/provider/kzg.rs | 100 ++++++++++++++++++ src/provider/mod.rs | 4 + src/provider/poseidon.rs | 44 ++++++++ src/traits.rs | 43 ++++++++ 14 files changed, 226 insertions(+), 18 deletions(-) create mode 100644 src/nifs.rs rename src/poseidon/{poseidon_constant.rs => poseidon_constants.rs} (100%) create mode 100644 src/provider/bn254.rs create mode 100644 src/provider/keccak.rs create mode 100644 src/provider/kzg.rs create mode 100644 src/provider/mod.rs create mode 100644 src/provider/poseidon.rs create mode 100644 src/traits.rs diff --git a/.vscode/settings.json b/.vscode/settings.json index d652a6f..c7b842a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -11,6 +11,9 @@ "./Cargo.toml", "./Cargo.toml", "./Cargo.toml", + "./Cargo.toml", + "./Cargo.toml", + "./Cargo.toml", "./Cargo.toml" ] } \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 4bddabf..33e86bc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ rayon = "1.7" num-traits = "0.2" digest = "0.10" generic-array = "1.0.0" +ark-bn254 = "0.4.0" ark-crypto-primitives = { version = "0.4.0", default-features = false, features = [ "sponge", @@ -22,6 +23,5 @@ ark-std = { version = "0.4.0", default-features = false } ark-ec = {version = "0.4.0", default-features = false} ark-ff = {version= "0.4.0", default-features = false} ark-poly = {version = "0.4.0", default-features = false} -ark-bn254 = "0.4.0" jf_primitives = {git = "https://github.com/EspressoSystems/jellyfish", package = "jf-primitives"} jf_utils = {git = "https://github.com/EspressoSystems/jellyfish", package = "jf-utils"} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index ce61752..832835f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,7 @@ pub mod error; +pub mod nifs; pub mod poseidon; pub mod primary; +pub mod provider; pub mod secondary; +pub mod traits; diff --git a/src/nifs.rs b/src/nifs.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/poseidon/mod.rs b/src/poseidon/mod.rs index edfe7cf..bf4329e 100644 --- a/src/poseidon/mod.rs +++ b/src/poseidon/mod.rs @@ -1,2 +1,2 @@ -pub mod grain_lfsr; -pub mod poseidon_constant; +mod grain_lfsr; +pub mod poseidon_constants; diff --git a/src/poseidon/poseidon_constant.rs b/src/poseidon/poseidon_constants.rs similarity index 100% rename from src/poseidon/poseidon_constant.rs rename to src/poseidon/poseidon_constants.rs diff --git a/src/primary/bn254_field.rs b/src/primary/bn254_field.rs index 0fdb0f3..d92a768 100644 --- a/src/primary/bn254_field.rs +++ b/src/primary/bn254_field.rs @@ -1,5 +1,5 @@ //// configurations for bn254 curve/field -use crate::poseidon::poseidon_constant::{PoseidonDefaultConfig, PoseidonDefaultConfigEntry}; +use crate::poseidon::poseidon_constants::{PoseidonDefaultConfig, PoseidonDefaultConfigEntry}; use ark_crypto_primitives::sponge::Absorb; use ark_ff::{fields::models::*, MontBackend, MontConfig, PrimeField}; @@ -12,19 +12,6 @@ pub struct FqBackend; type FqConfig = MontBackend; pub type Fq = Fp256; -// pub struct BaseField(Fq); - -// impl Absorb for BaseField { -// // convert BaseField into bytes -// fn to_sponge_bytes(&self, dest: &mut Vec) { -// todo!() -// } -// // convert BaseField into ScalarField -// fn to_sponge_field_elements(&self, dest: &mut Vec) { -// todo!() -// } -// } - impl PoseidonDefaultConfig<4> for FqConfig { const PARAMS_OPT_FOR_CONSTRAINTS: [PoseidonDefaultConfigEntry; 7] = [ PoseidonDefaultConfigEntry::new(2, 17, 8, 31, 0), diff --git a/src/primary/nifs.rs b/src/primary/nifs.rs index c500611..760eac3 100644 --- a/src/primary/nifs.rs +++ b/src/primary/nifs.rs @@ -16,7 +16,7 @@ use super::plonk::{ RelaxedPLONKWitness, }; use crate::error::MyError; -use crate::poseidon::poseidon_constant::PoseidonDefaultConfigField; +use crate::poseidon::poseidon_constants::PoseidonDefaultConfigField; pub struct NIFS { pub(crate) comm_T: Commitment, diff --git a/src/provider/bn254.rs b/src/provider/bn254.rs new file mode 100644 index 0000000..2105dee --- /dev/null +++ b/src/provider/bn254.rs @@ -0,0 +1,24 @@ +use crate::poseidon::poseidon_constants::{PoseidonDefaultConfig, PoseidonDefaultConfigEntry}; +use ark_bn254::{Bn254, Fq, FqConfig}; +use ark_ff::fields::MontBackend; + +impl PoseidonDefaultConfig<4> for MontBackend { + const PARAMS_OPT_FOR_CONSTRAINTS: [PoseidonDefaultConfigEntry; 7] = [ + PoseidonDefaultConfigEntry::new(2, 17, 8, 31, 0), + PoseidonDefaultConfigEntry::new(3, 5, 8, 56, 0), + PoseidonDefaultConfigEntry::new(4, 5, 8, 56, 0), + PoseidonDefaultConfigEntry::new(5, 5, 8, 57, 0), + PoseidonDefaultConfigEntry::new(6, 5, 8, 57, 0), + PoseidonDefaultConfigEntry::new(7, 5, 8, 57, 0), + PoseidonDefaultConfigEntry::new(8, 5, 8, 57, 0), + ]; + const PARAMS_OPT_FOR_WEIGHTS: [PoseidonDefaultConfigEntry; 7] = [ + PoseidonDefaultConfigEntry::new(2, 257, 8, 13, 0), + PoseidonDefaultConfigEntry::new(3, 257, 8, 13, 0), + PoseidonDefaultConfigEntry::new(4, 257, 8, 13, 0), + PoseidonDefaultConfigEntry::new(5, 257, 8, 13, 0), + PoseidonDefaultConfigEntry::new(6, 257, 8, 13, 0), + PoseidonDefaultConfigEntry::new(7, 257, 8, 13, 0), + PoseidonDefaultConfigEntry::new(8, 257, 8, 13, 0), + ]; +} diff --git a/src/provider/keccak.rs b/src/provider/keccak.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/provider/kzg.rs b/src/provider/kzg.rs new file mode 100644 index 0000000..f69a217 --- /dev/null +++ b/src/provider/kzg.rs @@ -0,0 +1,100 @@ +use rand::rngs::StdRng; +use std::marker::PhantomData; + +use crate::traits::{CommitmentEngineTrait, CommitmentTrait}; + +use ark_ec::{pairing::Pairing, scalar_mul::fixed_base::FixedBase, CurveGroup}; +use ark_ff::PrimeField; +use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial}; +use ark_std::{ + end_timer, + rand::{CryptoRng, RngCore}, + start_timer, vec, One, UniformRand, +}; + +use jf_primitives::pcs::prelude::Commitment; +use jf_primitives::pcs::{ + prelude::{PCSError, UnivariateKzgPCS, UnivariateProverParam, UnivariateUniversalParams}, + PolynomialCommitmentScheme, StructuredReferenceString, +}; + +pub fn gen_srs_for_testing( + rng: &mut R, + prover_degree: usize, + verifier_degree: usize, +) -> Result, PCSError> { + let setup_time = start_timer!(|| ark_std::format!( + "KZG10::Setup with prover degree {} and verifier degree {}", + prover_degree, + verifier_degree + )); + let beta = E::ScalarField::rand(rng); + let g = E::G1::rand(rng); + let h = E::G2::rand(rng); + + let mut powers_of_beta = vec![E::ScalarField::one()]; + + let mut cur = beta; + let max_degree = ark_std::cmp::max(prover_degree, verifier_degree); + for _ in 0..max_degree { + powers_of_beta.push(cur); + cur *= β + } + + let window_size = FixedBase::get_mul_window_size(prover_degree + 1); + + let scalar_bits = E::ScalarField::MODULUS_BIT_SIZE as usize; + let g_time = start_timer!(|| "Generating powers of G"); + // TODO: parallelization + let g_table = FixedBase::get_window_table(scalar_bits, window_size, g); + let powers_of_g = FixedBase::msm::(scalar_bits, window_size, &g_table, &powers_of_beta); + end_timer!(g_time); + + let powers_of_g = E::G1::normalize_batch(&powers_of_g); + + let h = h.into_affine(); + let beta_h = (h * beta).into_affine(); + + let powers_of_h = powers_of_beta + .iter() + .take(verifier_degree + 1) + .map(|x| (h * x).into_affine()) + .collect(); + + let pp = UnivariateUniversalParams { + powers_of_g, + h, + beta_h, + powers_of_h, + }; + end_timer!(setup_time); + Ok(pp) +} + +/// implement LOCAL trait CommitmentEngineTrait for it +/// +pub struct KZG { + _p: PhantomData, +} + +impl CommitmentTrait for Commitment where E: Pairing {} + +impl CommitmentEngineTrait for KZG +where + E: Pairing, +{ + type CommitmentKey = UnivariateProverParam; + type Commitment = Commitment; + + fn setup(rng: &mut StdRng, degree: usize) -> Self::CommitmentKey { + let pp: UnivariateUniversalParams = gen_srs_for_testing(rng, degree, 1).unwrap(); + let (ck, _) = pp.trim(degree).unwrap(); + ck + } + + fn commit(ck: &Self::CommitmentKey, v: &[E::ScalarField]) -> Self::Commitment { + let poly = + as DenseUVPolynomial>::from_coefficients_vec(v.to_vec()); + UnivariateKzgPCS::::commit(ck, &poly).unwrap() + } +} diff --git a/src/provider/mod.rs b/src/provider/mod.rs new file mode 100644 index 0000000..1e36192 --- /dev/null +++ b/src/provider/mod.rs @@ -0,0 +1,4 @@ +pub mod bn254; +pub mod keccak; +pub mod kzg; +pub mod poseidon; diff --git a/src/provider/poseidon.rs b/src/provider/poseidon.rs new file mode 100644 index 0000000..74d6df4 --- /dev/null +++ b/src/provider/poseidon.rs @@ -0,0 +1,44 @@ +use ark_crypto_primitives::sponge::poseidon::{PoseidonConfig, PoseidonSponge}; +use ark_crypto_primitives::sponge::{Absorb, CryptographicSponge}; +use ark_ff::PrimeField; + +use crate::poseidon::poseidon_constants::PoseidonDefaultConfigField; +use crate::traits::{ROConstantsTrait, ROTrait}; + +/// wrap a EXTERNAL object PoseidonConfig, and implement LOCAL trait 'ROConstantsTrait' +pub struct PoseidonConstants(PoseidonConfig); + +impl ROConstantsTrait for PoseidonConstants +where + BaseField: PoseidonDefaultConfigField, +{ + fn new(rate: usize) -> Self { + Self( + ::get_default_poseidon_parameters(rate, false) + .unwrap(), + ) + } +} + +/// wrap a EXTERNAL object PoseidonSponge, impl LOCAL trait ROTrait +pub struct PoseidonRO(PoseidonSponge); + +impl ROTrait for PoseidonRO +where + BaseField: PrimeField + PoseidonDefaultConfigField + Absorb, + ScalarField: PrimeField, +{ + type Constants = PoseidonConstants; + + fn new(constants: Self::Constants) -> Self { + Self(PoseidonSponge::::new(&constants.0)) + } + + fn absorb(&mut self, e: BaseField) { + self.0.absorb(&e); + } + + fn squeeze(&mut self) -> ScalarField { + self.0.squeeze_field_elements::(1 as usize)[0] + } +} diff --git a/src/traits.rs b/src/traits.rs new file mode 100644 index 0000000..f634cd3 --- /dev/null +++ b/src/traits.rs @@ -0,0 +1,43 @@ +use ark_ec::pairing::Pairing; +use rand::rngs::StdRng; +// use ark_ff::PrimeField; + +// pub trait Group: Sized { +// type BaseField: PrimeField; +// type ScalarField: PrimeField; +// type RO: ROTrait; +// type CE: CommitmentEngineTrait; +// } +pub trait ROConstantsTrait { + /// produces constants/parameters associated with the hash function + fn new(rate: usize) -> Self; +} + +pub trait ROTrait { + type Constants: ROConstantsTrait; + + /// Initializes the hash function + fn new(constants: Self::Constants) -> Self; + + /// Adds a scalar to the internal state + fn absorb(&mut self, e: BaseField); + + /// Returns a challenge of `num_bits` by hashing the internal state + fn squeeze(&mut self) -> ScalarField; +} + +pub trait CommitmentEngineTrait { + /// Holds the type of the commitment key + type CommitmentKey; + + /// Holds the type of the commitment + type Commitment: CommitmentTrait; + + /// Samples a new commitment key of a specified size + fn setup(rng: &mut StdRng, degree: usize) -> Self::CommitmentKey; + + /// Commits to the provided vector using the provided generators + fn commit(ck: &Self::CommitmentKey, v: &[E::ScalarField]) -> Self::Commitment; +} + +pub trait CommitmentTrait {} From 63b26bfa1d3f71719df3130bfb1180fe864c9036 Mon Sep 17 00:00:00 2001 From: PayneJoe Date: Sat, 16 Dec 2023 14:12:41 +0800 Subject: [PATCH 2/2] scalar not compatible --- src/lib.rs | 5 + src/nifs.rs | 42 +++ src/plonk.rs | 635 +++++++++++++++++++++++++++++++++++++++ src/provider/bn254.rs | 12 +- src/provider/kzg.rs | 39 ++- src/provider/poseidon.rs | 19 +- src/traits.rs | 30 +- 7 files changed, 747 insertions(+), 35 deletions(-) create mode 100644 src/plonk.rs diff --git a/src/lib.rs b/src/lib.rs index 832835f..64967c3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,12 @@ pub mod error; pub mod nifs; +pub mod plonk; pub mod poseidon; pub mod primary; pub mod provider; pub mod secondary; pub mod traits; + +type Commitment = <::CE as traits::CommitmentEngineTrait>::Commitment; +type CommitmentKey = + <::CE as traits::CommitmentEngineTrait>::CommitmentKey; diff --git a/src/nifs.rs b/src/nifs.rs index e69de29..ba0ba55 100644 --- a/src/nifs.rs +++ b/src/nifs.rs @@ -0,0 +1,42 @@ +use crate::{ + error::MyError, + plonk::*, + traits::{Group, ROConstantsTrait, ROTrait}, + Commitment, CommitmentKey, +}; +use std::marker::PhantomData; + +pub struct NIFS { + pub(crate) comm_T: Commitment, + _p: PhantomData, +} + +impl NIFS { + pub fn prove( + ck: &CommitmentKey, + pp_digest: &::ScalarField, + S: &PLONKShape, + U1: &RelaxedPLONKInstance, + W1: &RelaxedPLONKWitness, + U2: &PLONKInstance, + W2: &PLONKWitness, + ) -> Result<(NIFS, (RelaxedPLONKInstance, RelaxedPLONKWitness)), MyError> { + let constants = <::RO as ROTrait< + ::BaseField, + ::ScalarField, + >>::Constants::new(3); + let ro = <::RO as ROTrait< + ::BaseField, + ::ScalarField, + >>::new(constants.clone()); + todo!() + } + pub fn verifiy( + &self, + pp_digest: &::ScalarField, + U1: &RelaxedPLONKInstance, + U2: &PLONKInstance, + ) -> Result, MyError> { + todo!() + } +} diff --git a/src/plonk.rs b/src/plonk.rs new file mode 100644 index 0000000..dd2d433 --- /dev/null +++ b/src/plonk.rs @@ -0,0 +1,635 @@ +/// plonk instances for primary circuit over BN254 curve +/// +/// computation of cross terms followed from chapter 3.4 of protostar: https://eprint.iacr.org/2023/620.pdf +/// +// use ark_ec::pairing::Pairing; +use ark_ff::{Field, PrimeField}; +use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial}; +// use jf_primitives::pcs::prelude::Commitment; +// use jf_primitives::pcs::{ +// prelude::{PCSError, UnivariateKzgPCS, UnivariateProverParam, UnivariateUniversalParams}, +// PolynomialCommitmentScheme, StructuredReferenceString, +// }; +use rand::rngs::StdRng; +use rayon::prelude::*; + +use crate::error::MyError; +// use crate::primary::kzg::gen_srs_for_testing; +use crate::{ + traits::{CommitmentEngineTrait, Group}, + Commitment, CommitmentKey, +}; + +use std::marker::PhantomData; + +// pub(crate) type CommitmentKey = UnivariateProverParam; + +/// Public parameters for a given PLONK +#[derive(Clone)] +pub struct PLONK { + _p: PhantomData, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct PLONKShape { + pub(crate) num_cons: usize, + pub(crate) num_wire_types: usize, + pub(crate) num_public_input: usize, + + pub(crate) q_lc: Vec::ScalarField>>, + pub(crate) q_mul: Vec::ScalarField>>, + pub(crate) q_hash: Vec::ScalarField>>, + pub(crate) q_ecc: Vec<::ScalarField>, + pub(crate) q_o: Vec<::ScalarField>, + pub(crate) q_c: Vec<::ScalarField>, +} + +/// A type that holds a witness for a given Plonk instance +/// w_0, w_1, w_2, w_3, w_o +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct PLONKWitness { + pub(crate) W: Vec::ScalarField>>, +} + +/// A type that holds a commitment vector and public io vector +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct PLONKInstance { + pub(crate) comm_W: Vec>, + pub(crate) X: Vec<::ScalarField>, +} + +/// relaxed witness +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct RelaxedPLONKWitness { + pub(crate) W: Vec::ScalarField>>, + pub(crate) E: Vec::ScalarField>>, +} + +/// relaxed instance +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct RelaxedPLONKInstance { + pub(crate) comm_W: Vec>, + pub(crate) comm_E: Vec>, + pub(crate) X: Vec<::ScalarField>, + pub(crate) u: ::ScalarField, +} + +impl PLONK { + pub fn commitment_key(rng: &mut StdRng, degree: usize) -> CommitmentKey { + G::CE::setup(rng, degree) + } +} + +impl PLONKShape {} + +impl PLONKWitness { + /// A method to create a witness object using a vector of scalars + pub fn new( + S: &PLONKShape, + W: &[Vec<::ScalarField>], + ) -> Result, MyError> { + if S.num_wire_types != W.len() { + Err(MyError::WitnessError) + } else { + Ok(PLONKWitness { W: W.to_owned() }) + } + } + + /// Commits to the witness using the supplied generators + pub fn commit(&self, ck: &CommitmentKey) -> Vec> { + let com_W = self + .W + .iter() + .map(|w| { + G::CE::commit(ck, w.as_slice()).unwarp(); + }) + .collect::>>(); + com_W + } +} + +impl PLONKInstance { + /// A method to create an instance object using consitituent elements + pub fn new( + S: &PLONKShape, + comm_W: &Vec>, + X: &[::ScalarField], + ) -> Result, MyError> { + if S.num_public_input != X.len() { + Err(MyError::PublicIntputError) + } else { + Ok(PLONKInstance { + comm_W: comm_W.to_owned(), + X: X.to_owned(), + }) + } + } +} + +impl RelaxedPLONKWitness { + /// Produces a default RelaxedPLONKWitness given an PLONKShape + pub fn default(S: &PLONKShape) -> RelaxedPLONKWitness { + RelaxedPLONKWitness { + W: (0..S.num_wire_types) + .map(|_| vec![<::ScalarField as Field>::ZERO; S.num_cons]) + .collect::::ScalarField>>>(), + E: (0..S.num_wire_types - 1) + .map(|_| vec![<::ScalarField as Field>::ZERO; S.num_cons]) + .collect::::ScalarField>>>(), + } + } + + /// Initializes a new RelaxedPLONKWitness from an R1CSWitness + pub fn from_plonk_witness( + S: &PLONKShape, + witness: &PLONKWitness, + ) -> RelaxedPLONKWitness { + RelaxedPLONKWitness { + W: witness.W.clone(), + E: (0..S.num_wire_types - 1) + .map(|_| vec![<::ScalarField as Field>::ZERO; S.num_cons]) + .collect::::ScalarField>>>(), + } + } + + /// Commits to the witness using the supplied generators + pub fn commit(&self, ck: &CommitmentKey) -> (Vec>, Vec>) { + let com_func = |vecs: &Vec::ScalarField>>| { + vecs.iter() + .map(|v| { + let p = ::ScalarField> as DenseUVPolynomial< + ::ScalarField, + >>::from_coefficients_vec(v.to_vec()); + UnivariateKzgPCS::::commit(ck, &p).unwrap() + }) + .collect::>>() + }; + + let comm_W = com_func(&self.W); + let comm_E = com_func(&self.E); + + (comm_W, comm_E) + } + + pub fn fold( + &self, + W2: &PLONKWitness, + T: &Vec::ScalarField>>, + r: &::ScalarField, + ) -> Result, MyError> { + let (W1, E1) = (&self.W, &self.E); + let W2 = &W2.W; + + if W1.len() != W2.len() { + return Err(MyError::WitnessError); + } + + let fold_scalar_func = + |a_vecs: &Vec::ScalarField>>, + b_vecs: &Vec::ScalarField>>| { + a_vecs + .par_iter() + .zip(b_vecs) + .map(|(a_col, b_col)| { + a_col + .par_iter() + .zip(b_col) + .map(|(a, b)| *a + *r * *b) + .collect::::ScalarField>>() + }) + .collect::::ScalarField>>>() + }; + let W = fold_scalar_func(&W1, &W2); + let E = fold_scalar_func(&E1, T); + + Ok(RelaxedPLONKWitness { W, E }) + } +} + +impl RelaxedPLONKInstance { + pub fn default(_ck: &CommitmentKey, S: &PLONKShape) -> RelaxedPLONKInstance { + let (comm_W, comm_E) = ( + (0..S.num_wire_types) + .map(|_| Commitment::::default()) + .collect::>>(), + (0..S.num_wire_types - 1) + .map(|_| Commitment::::default()) + .collect::>>(), + ); + RelaxedPLONKInstance { + comm_W, + comm_E, + u: <::ScalarField as Field>::ZERO, + X: vec![<::ScalarField as Field>::ZERO; S.num_public_input], + } + } + + /// Initializes a new RelaxedPLONKInstance from an PLONKInstance + pub fn from_plonk_instance( + ck: &CommitmentKey, + S: &PLONKShape, + instance: &PLONKInstance, + ) -> RelaxedPLONKInstance { + let mut r_instance = RelaxedPLONKInstance::default(ck, S); + r_instance.comm_W = instance.comm_W.clone(); + r_instance.u = <::ScalarField as Field>::ONE; + r_instance.X = instance.X.clone(); + r_instance + } + + /// Initializes a new RelaxedPLONKInstance from an PLONKInstance + pub fn from_plonk_instance_unchecked( + comm_W: &Vec>, + X: &[::ScalarField], + ) -> RelaxedPLONKInstance { + let comm_E = (0..comm_W.len() - 1) + .map(|_| Commitment::::default()) + .collect::>>(); + RelaxedPLONKInstance { + comm_W: comm_W.to_owned(), + comm_E: comm_E, + u: ::ScalarField::ONE, + X: X.to_vec(), + } + } + + /// Folds an incoming RelaxedPLONKInstance into the current one + pub fn fold( + &self, + U2: &PLONKInstance, + comm_T: &Vec>, + r: &::ScalarField, + ) -> Result, MyError> { + let (X1, u1, comm_W_1, comm_E_1) = + (&self.X, &self.u, &self.comm_W.clone(), &self.comm_E.clone()); + let (X2, comm_W_2) = (&U2.X, &U2.comm_W); + + // weighted sum of X, comm_W, comm_E, and u + let X = X1 + .par_iter() + .zip(X2) + .map(|(a, b)| *a + *r * *b) + .collect::::ScalarField>>(); + + let fold_comm_func = |comm_1: &Vec>, comm_2: &Vec>| { + comm_1 + .par_iter() + .zip(comm_2) + .map(|(a, b)| { + let a_affine: &E::G1Affine = a.as_ref(); + let b_affine: &E::G1Affine = b.as_ref(); + Commitment((*a_affine + *b_affine * *r).into_affine()) + }) + .collect::>>() + }; + + let comm_W = fold_comm_func(&self.comm_W, &U2.comm_W); + let comm_E = fold_comm_func(&self.comm_E, comm_T); + + let u = *u1 + *r; + + Ok(RelaxedPLONKInstance { + comm_W, + comm_E, + X, + u, + }) + } +} + +impl PLONKShape { + pub fn new( + num_cons: usize, + num_wire_types: usize, + num_public_input: usize, + q_c: &Vec<::ScalarField>, + q_lc: &Vec::ScalarField>>, + q_mul: &Vec::ScalarField>>, + q_ecc: &Vec<::ScalarField>, + q_hash: &Vec::ScalarField>>, + q_o: &Vec<::ScalarField>, + ) -> Result, MyError> { + assert!(q_lc.len() == num_wire_types - 1); + assert!(q_mul.len() == 2); + let is_valid = + |num_cons: usize, q: &Vec<::ScalarField>| -> Result<(), MyError> { + if (q.len() == num_cons) { + Ok(()) + } else { + Err(MyError::SelectorError) + } + }; + + let invalid_num: i32 = vec![ + vec![q_c, q_ecc, q_o], + q_lc.into_iter() + .collect::::ScalarField>>>(), + q_mul + .into_iter() + .collect::::ScalarField>>>(), + q_hash + .into_iter() + .collect::::ScalarField>>>(), + ] + .concat() + .iter() + .map(|q| { + if (is_valid(num_cons, q).is_err()) { + 1 as i32 + } else { + 0 as i32 + } + }) + .collect::>() + .iter() + .sum(); + + if (invalid_num > 0) { + return Err(MyError::SelectorError); + } + + Ok(PLONKShape { + num_cons: num_cons, + num_wire_types: num_wire_types, + num_public_input: num_public_input, + q_c: q_c.to_owned(), + q_lc: q_lc.to_owned(), + q_mul: q_mul.to_owned(), + q_ecc: q_ecc.to_owned(), + q_hash: q_hash.to_owned(), + q_o: q_o.to_owned(), + }) + } + + fn grand_product(n: usize, vec: Vec<&::ScalarField>) -> ::ScalarField { + let first: ::ScalarField = *vec[0]; + if n == 1 { + first + } else { + vec[1..].iter().fold(first, |acc, cur| acc * *cur) + } + } + + fn compute_cross_terms( + degree: usize, + u1: ::ScalarField, + u2: ::ScalarField, + inst1: &Vec::ScalarField>>, + inst2: &Vec::ScalarField>>, + ) -> Vec::ScalarField>> { + assert!(inst1.len() == inst2.len(), "compute cross term"); + + let transpose_matrix = |mat: Vec::ScalarField>>| { + let num_row = mat[0].len(); + let mut mut_cols: Vec<_> = mat.into_iter().map(|col| col.into_iter()).collect(); + (0..num_row) + .map(|_| { + mut_cols + .iter_mut() + .map(|n| n.next().unwrap()) + .collect::::ScalarField>>() + }) + .collect::::ScalarField>>>() + }; + let trans_inst1 = transpose_matrix(inst1.clone()); + let trans_inst2 = transpose_matrix(inst2.clone()); + + let max_degree = 5 as usize; + (1..max_degree) + .rev() + .map(|r_degree| { + let l_degree = max_degree - r_degree; + + trans_inst1 + .par_iter() + .zip(&trans_inst2) + .map(|(row_a, row_b)| { + let l_vars = vec![ + vec![&u1; degree], + row_a + .into_iter() + .map(|a| a) + .collect::::ScalarField>>(), + ] + .concat(); + let r_vars = vec![ + vec![&u2; degree], + row_b + .into_iter() + .map(|a| a) + .collect::::ScalarField>>(), + ] + .concat(); + // let l_vars = vec![vec![u1; degree], row_a].concat(); + // let r_vars = vec![vec![u2; degree], row_b].concat(); + Self::grand_product(l_degree, l_vars) + * Self::grand_product(r_degree, r_vars) + }) + .collect::::ScalarField>>() + }) + .rev() + .collect::::ScalarField>>>() + } + + fn compute_cross_terms_five_exp( + inst1: &Vec<::ScalarField>, + inst2: &Vec<::ScalarField>, + ) -> Vec::ScalarField>> { + let count_combination = |n: usize, r: usize| { + if r > n { + 0 + } else { + (1..=r).fold(1, |acc, val| acc * (n - val + 1) / val) + } + }; + let vec_pow = |n: usize, vec: &Vec<::ScalarField>| { + vec.par_iter() + .map(|v| { + let first = *v; + if n == 1 { + first + } else { + vec![v; n - 1].iter().fold(first, |a, b| a * *b) + } + }) + .collect::::ScalarField>>() + }; + + let max_degree: usize = 5; + (1..max_degree) + .rev() + .map(|r_degree| { + let l_degree = max_degree - r_degree; + let const_var = count_combination(max_degree, r_degree); + let const_scalar = <::ScalarField as PrimeField>::from_bigint( + <::ScalarField as PrimeField>::BigInt::from(const_var as u32), + ) + .unwrap(); + let ref_const_scalar = &const_scalar; + let l_pow = vec_pow(l_degree, inst1); + let r_pow = vec_pow(r_degree, inst2); + l_pow + .iter() + .zip(r_pow) + .map(|(a, b)| *ref_const_scalar * a * b) + .collect::::ScalarField>>() + }) + .rev() + .collect::::ScalarField>>>() + } + + //// compute cross terms and their commitments + /// 1. length of cross term vector equals max_degree - 1 + pub fn commit_T( + &self, + ck: &CommitmentKey, + U1: &RelaxedPLONKInstance, + W1: &RelaxedPLONKWitness, + U2: &PLONKInstance, + W2: &PLONKWitness, + ) -> Result<(Vec::ScalarField>>, Vec>), MyError> { + assert!(W1.W.len() == self.num_wire_types - 1, "wrong wires"); + // q_ecc operation, u^0 * q_ecc * w_0 * w_1 * w_2 * w_3 * w_o + let ecc_T: Vec::ScalarField>> = Self::compute_cross_terms( + 0 as usize, + U1.u, + <::ScalarField as Field>::ONE, + &W1.W, + &W2.W, + ); + + // q_lc operation, u^4 * (q_lc_0 * w_0 + q_lc_1 * w_1 + q_lc_2 * w_2 + q_lc_3 * w_3) + let lc_T = (0..self.num_wire_types - 1) + .map(|i| { + Self::compute_cross_terms( + 4, + U1.u, + <::ScalarField as Field>::ONE, + &W1.W[i..i + 1].to_vec(), + &W2.W[i..i + 1].to_vec(), + ) + }) + .collect::::ScalarField>>>>(); + + // q_mul operation, u^3 * (q_mul_0 * w_0 * w_1 + q_mul_1 * w_2 * w_3) + let mul_T = (0..self.num_wire_types - 1) + .step_by(2) + .map(|i| { + Self::compute_cross_terms( + 3, + U1.u, + <::ScalarField as Field>::ONE, + &W1.W[i..i + 2].to_vec(), + &W2.W[i..i + 2].to_vec(), + ) + }) + .collect::::ScalarField>>>>(); + + // q_out operation, u^4 * (q_o * w_o) + let out_T = Self::compute_cross_terms( + 4, + U1.u, + <::ScalarField as Field>::ONE, + &W1.W[self.num_wire_types - 1..].to_vec(), + &W2.W[self.num_wire_types - 1..].to_vec(), + ); + + // q_c operation, u^5 * q_c + let u1_vec = vec![U1.u; self.num_cons]; + let u2_vec = vec![<::ScalarField as Field>::ONE; self.num_cons]; + let const_T = Self::compute_cross_terms_five_exp(&u1_vec, &u2_vec); + + // q_hash operation, u^0 * (q_hash_0 * w_0^5 + q_hash_1 * w_1^5 + q_hash_2 * w_2^5 + q_hash_3 * w_3^5) + let hash_T = (0..self.num_wire_types - 1) + .map(|i| Self::compute_cross_terms_five_exp(&W1.W[i], &W2.W[i])) + .collect::::ScalarField>>>>(); + + //////////////////////////////// apply selectors on cross terms + let apply_selector = + |T: &Vec::ScalarField>>, selector: &Vec<::ScalarField>| { + (0..self.num_wire_types - 1) + .map(|i| { + let ref_T = &T[i]; + ref_T + .par_iter() + .zip(selector) + .map(|(a, b)| *a * *b) + .collect::::ScalarField>>() + }) + .collect::::ScalarField>>>() + }; + + let (ref_ecc_T, ref_out_T, ref_const_T, ref_q_ecc, ref_q_out, ref_q_const) = + (&ecc_T, &out_T, &const_T, &self.q_ecc, &self.q_o, &self.q_c); + let ecc_result = apply_selector(ref_ecc_T, ref_q_ecc); + let out_result = apply_selector(ref_out_T, ref_q_out); + let const_result = apply_selector(ref_const_T, ref_q_const); + + let lc_result = (0..self.num_wire_types - 1) + .map(|i| { + let (ref_lc_T, ref_q_lc) = (&lc_T[i], &self.q_lc[i]); + apply_selector(ref_lc_T, ref_q_lc) + }) + .collect::::ScalarField>>>>(); + + let hash_result = (0..self.num_wire_types - 1) + .map(|i| { + let (ref_hash_T, ref_q_hash) = (&hash_T[i], &self.q_hash[i]); + apply_selector(ref_hash_T, ref_q_hash) + }) + .collect::::ScalarField>>>>(); + + let mul_result = (0..2) + .map(|i| { + let (ref_mul_T, ref_q_mul) = (&mul_T[i], &self.q_mul[i]); + apply_selector(ref_mul_T, ref_q_mul) + }) + .collect::::ScalarField>>>>(); + + ////////////////////////////////////////// add-on all cross terms + let apply_mat_element_add = + |acc: &Vec::ScalarField>>, + cur: &Vec::ScalarField>>| { + acc.into_iter() + .zip(cur) + .map(|(a_col, b_col)| { + a_col + .iter() + .zip(b_col) + .map(|(a, b)| *a + *b) + .collect::::ScalarField>>() + }) + .collect::::ScalarField>>>() + }; + + let stack_T = vec![ + vec![&ecc_result, &out_result, &const_result], + lc_result + .iter() + .collect::::ScalarField>>>>(), + hash_result + .iter() + .collect::::ScalarField>>>>(), + mul_result + .iter() + .collect::::ScalarField>>>>(), + ] + .concat(); + let T = stack_T[1..].iter().fold(stack_T[0].clone(), |acc, cur| { + apply_mat_element_add(&acc, cur) + }); + + ////////////////////////////////////////// commit T + let com_T = T + .iter() + .map(|coefficients| { + let poly = ::ScalarField> as DenseUVPolynomial< + ::ScalarField, + >>::from_coefficients_vec(coefficients.clone()); + UnivariateKzgPCS::::commit(ck, &poly).unwrap() + }) + .collect::>>(); + + Ok((T, com_T)) + } +} diff --git a/src/provider/bn254.rs b/src/provider/bn254.rs index 2105dee..5c45472 100644 --- a/src/provider/bn254.rs +++ b/src/provider/bn254.rs @@ -1,5 +1,7 @@ use crate::poseidon::poseidon_constants::{PoseidonDefaultConfig, PoseidonDefaultConfigEntry}; -use ark_bn254::{Bn254, Fq, FqConfig}; +use crate::provider::{kzg::CommitmentEngine, poseidon::PoseidonRO}; +use crate::traits::Group; +use ark_bn254::{Bn254, Fq, FqConfig, Fr, G1Affine}; use ark_ff::fields::MontBackend; impl PoseidonDefaultConfig<4> for MontBackend { @@ -22,3 +24,11 @@ impl PoseidonDefaultConfig<4> for MontBackend { PoseidonDefaultConfigEntry::new(8, 257, 8, 13, 0), ]; } + +impl Group for Bn254 { + type BaseField = Fq; + type ScalarField = Fr; + type PreprocessedGroupElement = G1Affine; + type RO = PoseidonRO; + type CE = CommitmentEngine; +} diff --git a/src/provider/kzg.rs b/src/provider/kzg.rs index f69a217..7844241 100644 --- a/src/provider/kzg.rs +++ b/src/provider/kzg.rs @@ -1,10 +1,10 @@ use rand::rngs::StdRng; use std::marker::PhantomData; -use crate::traits::{CommitmentEngineTrait, CommitmentTrait}; +use crate::traits::CommitmentEngineTrait; use ark_ec::{pairing::Pairing, scalar_mul::fixed_base::FixedBase, CurveGroup}; -use ark_ff::PrimeField; +use ark_ff::{Field, PrimeField}; use ark_poly::{univariate::DensePolynomial, DenseUVPolynomial}; use ark_std::{ end_timer, @@ -18,6 +18,8 @@ use jf_primitives::pcs::{ PolynomialCommitmentScheme, StructuredReferenceString, }; +use crate::traits::Group; + pub fn gen_srs_for_testing( rng: &mut R, prover_degree: usize, @@ -73,28 +75,35 @@ pub fn gen_srs_for_testing( /// implement LOCAL trait CommitmentEngineTrait for it /// -pub struct KZG { - _p: PhantomData, +pub struct CommitmentEngine { + _p: PhantomData, } -impl CommitmentTrait for Commitment where E: Pairing {} +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct KZGCommitmentKey(UnivariateProverParam); + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct KZGCommitment(Commitment); + +// impl CommitmentTrait for KZGCommitment where G: Group {} -impl CommitmentEngineTrait for KZG +impl CommitmentEngineTrait for CommitmentEngine where - E: Pairing, + G: Group, { - type CommitmentKey = UnivariateProverParam; - type Commitment = Commitment; + type CommitmentKey = KZGCommitmentKey; + type Commitment = KZGCommitment; fn setup(rng: &mut StdRng, degree: usize) -> Self::CommitmentKey { - let pp: UnivariateUniversalParams = gen_srs_for_testing(rng, degree, 1).unwrap(); + let pp: UnivariateUniversalParams = gen_srs_for_testing(rng, degree, 1).unwrap(); let (ck, _) = pp.trim(degree).unwrap(); - ck + KZGCommitmentKey(ck) } - fn commit(ck: &Self::CommitmentKey, v: &[E::ScalarField]) -> Self::Commitment { - let poly = - as DenseUVPolynomial>::from_coefficients_vec(v.to_vec()); - UnivariateKzgPCS::::commit(ck, &poly).unwrap() + fn commit(ck: &Self::CommitmentKey, v: &[::ScalarField]) -> Self::Commitment { + let poly = ::ScalarField> as DenseUVPolynomial< + ::ScalarField, + >>::from_coefficients_vec(v.to_vec()); + KZGCommitment(UnivariateKzgPCS::::commit(&ck.0, &poly).unwrap()) } } diff --git a/src/provider/poseidon.rs b/src/provider/poseidon.rs index 74d6df4..3aa103e 100644 --- a/src/provider/poseidon.rs +++ b/src/provider/poseidon.rs @@ -2,10 +2,13 @@ use ark_crypto_primitives::sponge::poseidon::{PoseidonConfig, PoseidonSponge}; use ark_crypto_primitives::sponge::{Absorb, CryptographicSponge}; use ark_ff::PrimeField; +use std::marker::PhantomData; + use crate::poseidon::poseidon_constants::PoseidonDefaultConfigField; use crate::traits::{ROConstantsTrait, ROTrait}; /// wrap a EXTERNAL object PoseidonConfig, and implement LOCAL trait 'ROConstantsTrait' +#[derive(Clone)] pub struct PoseidonConstants(PoseidonConfig); impl ROConstantsTrait for PoseidonConstants @@ -21,9 +24,12 @@ where } /// wrap a EXTERNAL object PoseidonSponge, impl LOCAL trait ROTrait -pub struct PoseidonRO(PoseidonSponge); +pub struct PoseidonRO { + ro: PoseidonSponge, + _p: PhantomData, +} -impl ROTrait for PoseidonRO +impl ROTrait for PoseidonRO where BaseField: PrimeField + PoseidonDefaultConfigField + Absorb, ScalarField: PrimeField, @@ -31,14 +37,17 @@ where type Constants = PoseidonConstants; fn new(constants: Self::Constants) -> Self { - Self(PoseidonSponge::::new(&constants.0)) + Self { + ro: PoseidonSponge::::new(&constants.0), + _p: PhantomData, + } } fn absorb(&mut self, e: BaseField) { - self.0.absorb(&e); + self.ro.absorb(&e); } fn squeeze(&mut self) -> ScalarField { - self.0.squeeze_field_elements::(1 as usize)[0] + self.ro.squeeze_field_elements::(1 as usize)[0] } } diff --git a/src/traits.rs b/src/traits.rs index f634cd3..1291a32 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -1,20 +1,22 @@ use ark_ec::pairing::Pairing; +use ark_ff::PrimeField; +use core::fmt::Debug; use rand::rngs::StdRng; -// use ark_ff::PrimeField; - -// pub trait Group: Sized { -// type BaseField: PrimeField; -// type ScalarField: PrimeField; -// type RO: ROTrait; -// type CE: CommitmentEngineTrait; -// } + +pub trait Group: Sized + Pairing { + type BaseField: PrimeField; + type ScalarField: PrimeField; + type PreprocessedGroupElement: Clone + Debug; + type RO: ROTrait<::BaseField, ::ScalarField>; + type CE: CommitmentEngineTrait; +} pub trait ROConstantsTrait { /// produces constants/parameters associated with the hash function fn new(rate: usize) -> Self; } pub trait ROTrait { - type Constants: ROConstantsTrait; + type Constants: ROConstantsTrait + Clone; /// Initializes the hash function fn new(constants: Self::Constants) -> Self; @@ -26,18 +28,18 @@ pub trait ROTrait { fn squeeze(&mut self) -> ScalarField; } -pub trait CommitmentEngineTrait { +pub trait CommitmentEngineTrait { /// Holds the type of the commitment key - type CommitmentKey; + type CommitmentKey: Clone + Debug; /// Holds the type of the commitment - type Commitment: CommitmentTrait; + type Commitment: Clone + Debug + PartialEq + Eq; /// Samples a new commitment key of a specified size fn setup(rng: &mut StdRng, degree: usize) -> Self::CommitmentKey; /// Commits to the provided vector using the provided generators - fn commit(ck: &Self::CommitmentKey, v: &[E::ScalarField]) -> Self::Commitment; + fn commit(ck: &Self::CommitmentKey, v: &[::ScalarField]) -> Self::Commitment; } -pub trait CommitmentTrait {} +// pub trait CommitmentTrait: Clone + Debug + PartialEq + Eq {}