From b9527f8e3748bd5be51fc5990d64730c79bebc36 Mon Sep 17 00:00:00 2001 From: zhenfei Date: Thu, 19 May 2022 16:23:44 -0400 Subject: [PATCH] impl KZG based multilinear pcs (#22) --- pcs/Cargo.toml | 29 ++++ pcs/benches/bench.rs | 81 +++++++++++ pcs/src/commit.rs | 257 ++++++++++++++++++++++++++++++++ pcs/src/errors.rs | 25 ++++ pcs/src/lib.rs | 59 +++++++- pcs/src/param.rs | 259 +++++++++++++++++++++++++++++++++ poly-iop/Cargo.toml | 4 +- poly-iop/benches/bench.rs | 42 +++--- poly-iop/readme.md | 4 +- poly-iop/src/errors.rs | 12 +- poly-iop/src/lib.rs | 2 +- poly-iop/src/zero_check/mod.rs | 4 +- 12 files changed, 741 insertions(+), 37 deletions(-) create mode 100644 pcs/benches/bench.rs create mode 100644 pcs/src/commit.rs create mode 100644 pcs/src/errors.rs create mode 100644 pcs/src/param.rs diff --git a/pcs/Cargo.toml b/pcs/Cargo.toml index 3880fee..30ce8aa 100644 --- a/pcs/Cargo.toml +++ b/pcs/Cargo.toml @@ -6,3 +6,32 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] + +ark-std = { version = "^0.3.0", default-features = false } +ark-serialize = { version = "^0.3.0", default-features = false, features = [ "derive" ] } +ark-ff = { version = "^0.3.0", default-features = false } +ark-ec = { version = "^0.3.0", default-features = false } +ark-poly = {version = "^0.3.0", default-features = false } +ark-sponge = {version = "^0.3.0", default-features = false} +ark-bls12-381 = { version = "0.3.0", default-features = false, features = [ "curve" ] } + +displaydoc = { version = "0.2.3", default-features = false } + +# Benchmarks +[[bench]] +name = "pcs-benches" +path = "benches/bench.rs" +harness = false + +[features] +default = [ "parallel", "print-trace" ] +# default = [ "parallel" ] +parallel = [ + "ark-std/parallel", + "ark-ff/parallel", + "ark-poly/parallel", + "ark-ec/parallel", + ] +print-trace = [ + "ark-std/print-trace" + ] \ No newline at end of file diff --git a/pcs/benches/bench.rs b/pcs/benches/bench.rs new file mode 100644 index 0000000..b27e1b1 --- /dev/null +++ b/pcs/benches/bench.rs @@ -0,0 +1,81 @@ +use ark_bls12_381::{Bls12_381, Fr}; +use ark_ff::UniformRand; +use ark_poly::{DenseMultilinearExtension, MultilinearExtension}; +use ark_std::test_rng; +use pcs::{KZGMultilinearPC, MultilinearCommitmentScheme, PCSErrors}; +use std::time::Instant; + +fn main() -> Result<(), PCSErrors> { + bench_pcs() +} + +fn bench_pcs() -> Result<(), PCSErrors> { + let mut rng = test_rng(); + + // normal polynomials + let uni_params = KZGMultilinearPC::::setup(&mut rng, 18)?; + + for nv in 4..19 { + let repetition = if nv < 10 { + 100 + } else if nv < 20 { + 50 + } else { + 10 + }; + + let poly = DenseMultilinearExtension::rand(nv, &mut rng); + let (ck, vk) = uni_params.trim(nv)?; + let point: Vec<_> = (0..nv).map(|_| Fr::rand(&mut rng)).collect(); + + // commit + let com = { + let start = Instant::now(); + for _ in 0..repetition { + let _commit = KZGMultilinearPC::commit(&ck, &poly)?; + } + + println!( + "KZG commit for {} variables: {} ns", + nv, + start.elapsed().as_nanos() / repetition as u128 + ); + + KZGMultilinearPC::commit(&ck, &poly)? + }; + + // open + let proof = { + let start = Instant::now(); + for _ in 0..repetition { + let _open = KZGMultilinearPC::open(&ck, &poly, &point)?; + } + + println!( + "KZG open for {} variables: {} ns", + nv, + start.elapsed().as_nanos() / repetition as u128 + ); + KZGMultilinearPC::open(&ck, &poly, &point)? + }; + let value = poly.evaluate(&point).unwrap(); + + // verify + + { + let start = Instant::now(); + for _ in 0..repetition { + assert!(KZGMultilinearPC::verify(&vk, &com, &point, value, &proof)?); + } + println!( + "KZG verify for {} variables: {} ns", + nv, + start.elapsed().as_nanos() / repetition as u128 + ); + } + + println!("===================================="); + } + + Ok(()) +} diff --git a/pcs/src/commit.rs b/pcs/src/commit.rs new file mode 100644 index 0000000..150e4c1 --- /dev/null +++ b/pcs/src/commit.rs @@ -0,0 +1,257 @@ +use ark_ec::{ + msm::{FixedBaseMSM, VariableBaseMSM}, + AffineCurve, PairingEngine, ProjectiveCurve, +}; +use ark_ff::PrimeField; +use ark_poly::MultilinearExtension; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Read, SerializationError, Write}; +use ark_std::{end_timer, rand::RngCore, start_timer, vec::Vec, One, Zero}; + +use crate::{ + KZGMultilinearPC, MultilinearCommitmentScheme, PCSErrors, ProverParam, UniversalParams, + VerifierParam, +}; + +#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug)] +/// commitment +pub struct Commitment { + /// number of variables + pub nv: usize, + /// product of g as described by the vRAM paper + pub g_product: E::G1Affine, +} + +#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug)] +/// proof of opening +pub struct Proof { + /// Evaluation of quotients + pub proofs: Vec, +} + +impl MultilinearCommitmentScheme for KZGMultilinearPC { + type ProverParam = ProverParam; + type VerifierParam = VerifierParam; + type SRS = UniversalParams; + type Commitment = Commitment; + type Proof = Proof; + + /// Generate SRS from RNG. + /// WARNING: THIS FUNCTION IS FOR TESTING PURPOSE ONLY. + /// THE OUTPUT SRS SHOULD NOT BE USED IN PRODUCTION. + fn setup(rng: &mut R, num_vars: usize) -> Result { + let setup_timer = start_timer!(|| format!("SRS setup for dim {}", num_vars)); + let res = Self::SRS::gen_srs_for_testing(rng, num_vars); + end_timer!(setup_timer); + res + } + + /// Generate a commitment for a polynomial. + /// + /// This function takes `2^num_vars` number of scalar multiplications over + /// G1. + fn commit( + prover_param: &Self::ProverParam, + poly: &impl MultilinearExtension, + ) -> Result { + let commit_timer = start_timer!(|| "commit"); + + let nv = poly.num_vars(); + let scalars: Vec<_> = poly + .to_evaluations() + .into_iter() + .map(|x| x.into_repr()) + .collect(); + let g_product = VariableBaseMSM::multi_scalar_mul( + &prover_param.powers_of_g[0].evals, + scalars.as_slice(), + ) + .into_affine(); + + end_timer!(commit_timer); + Ok(Commitment { nv, g_product }) + } + + /// On input a polynomial `p` and a point `point`, outputs a proof for the + /// same. This function does not need to take the evaluation value as an + /// input. + /// + /// This function takes 2^{num_var +1} number of scalar multiplications over + /// G2: + /// - it proceeds with `num_var` number of rounds, + /// - at round i, we compute an MSM for `2^{num_var - i + 1}` number of G2 + /// elements. + fn open( + prover_param: &Self::ProverParam, + polynomial: &impl MultilinearExtension, + point: &[E::Fr], + ) -> Result { + let open_timer = start_timer!(|| "open"); + + assert_eq!( + polynomial.num_vars(), + prover_param.num_vars, + "Invalid size of polynomial" + ); + let nv = polynomial.num_vars(); + let mut r: Vec> = (0..nv + 1).map(|_| Vec::new()).collect(); + let mut q: Vec> = (0..nv + 1).map(|_| Vec::new()).collect(); + + r[nv] = polynomial.to_evaluations(); + + let mut proofs = Vec::new(); + + for (i, (&point_at_k, hi)) in point + .iter() + .zip(prover_param.powers_of_h.iter()) + .take(nv) + .enumerate() + { + let ith_round = start_timer!(|| format!("{}-th round", i)); + + let k = nv - i; + let cur_dim = 1 << (k - 1); + let mut cur_q = vec![E::Fr::zero(); cur_dim]; + let mut cur_r = vec![E::Fr::zero(); cur_dim]; + + for b in 0..(1 << (k - 1)) { + // q_b = pre_r [2^b + 1] - pre_r [2^b] + cur_q[b] = r[k][(b << 1) + 1] - r[k][b << 1]; + + // r_b = pre_r [2^b]*(1-p) + pre_r [2^b + 1] * p + cur_r[b] = + r[k][b << 1] * (E::Fr::one() - point_at_k) + (r[k][(b << 1) + 1] * point_at_k); + } + + let scalars: Vec<_> = (0..(1 << k)).map(|x| cur_q[x >> 1].into_repr()).collect(); + + q[k] = cur_q; + r[k - 1] = cur_r; + + // this is a MSM over G2 and is likely to be the bottleneck + proofs.push(VariableBaseMSM::multi_scalar_mul(&hi.evals, &scalars).into_affine()); + end_timer!(ith_round); + } + + end_timer!(open_timer); + Ok(Proof { proofs }) + } + + /// Verifies that `value` is the evaluation at `x` of the polynomial + /// committed inside `comm`. + /// + /// This function takes + /// - num_var number of pairing product. + /// - num_var number of MSM + fn verify( + verifier_param: &Self::VerifierParam, + commitment: &Self::Commitment, + point: &[E::Fr], + value: E::Fr, + proof: &Self::Proof, + ) -> Result { + let verify_timer = start_timer!(|| "verify"); + let prepare_inputs_timer = start_timer!(|| "prepare pairing inputs"); + + let scalar_size = E::Fr::size_in_bits(); + let window_size = FixedBaseMSM::get_mul_window_size(verifier_param.num_vars); + + let g_table = FixedBaseMSM::get_window_table( + scalar_size, + window_size, + verifier_param.g.into_projective(), + ); + let g_mul: Vec = + FixedBaseMSM::multi_scalar_mul(scalar_size, window_size, &g_table, point); + + let mut g1_vec: Vec<_> = (0..verifier_param.num_vars) + .map(|i| verifier_param.g_mask[i].into_projective() - g_mul[i]) + .collect(); + g1_vec.push(verifier_param.g.mul(value) - commitment.g_product.into_projective()); + + let g1_vec: Vec = E::G1Projective::batch_normalization_into_affine(&g1_vec); + let tmp = g1_vec[verifier_param.num_vars]; + end_timer!(prepare_inputs_timer); + + let pairing_product_timer = start_timer!(|| "pairing product"); + + let mut pairings: Vec<_> = g1_vec + .into_iter() + .take(verifier_param.num_vars) + .map(E::G1Prepared::from) + .zip(proof.proofs.iter().map(|&x| E::G2Prepared::from(x))) + .collect(); + + pairings.push(( + E::G1Prepared::from(tmp), + E::G2Prepared::from(verifier_param.h), + )); + + let res = E::product_of_pairings(pairings.iter()) == E::Fqk::one(); + + end_timer!(pairing_product_timer); + end_timer!(verify_timer); + Ok(res) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use ark_bls12_381::Bls12_381; + use ark_ec::PairingEngine; + use ark_poly::{DenseMultilinearExtension, MultilinearExtension, SparseMultilinearExtension}; + use ark_std::{rand::RngCore, test_rng, vec::Vec, UniformRand}; + type E = Bls12_381; + type Fr = ::Fr; + + fn test_kzg_mlpc_helper( + uni_params: &UniversalParams, + poly: &impl MultilinearExtension, + rng: &mut R, + ) -> Result<(), PCSErrors> { + let nv = poly.num_vars(); + assert_ne!(nv, 0); + let (ck, vk) = uni_params.trim(nv)?; + let point: Vec<_> = (0..nv).map(|_| Fr::rand(rng)).collect(); + let com = KZGMultilinearPC::commit(&ck, poly)?; + let proof = KZGMultilinearPC::open(&ck, poly, &point)?; + + let value = poly.evaluate(&point).unwrap(); + assert!(KZGMultilinearPC::verify(&vk, &com, &point, value, &proof)?); + + let value = Fr::rand(rng); + assert!(!KZGMultilinearPC::verify(&vk, &com, &point, value, &proof)?); + + Ok(()) + } + + #[test] + fn setup_commit_verify_correct_polynomials() -> Result<(), PCSErrors> { + let mut rng = test_rng(); + + let uni_params = KZGMultilinearPC::::setup(&mut rng, 10)?; + + // normal polynomials + let poly1 = DenseMultilinearExtension::rand(8, &mut rng); + test_kzg_mlpc_helper(&uni_params, &poly1, &mut rng)?; + + let poly2 = SparseMultilinearExtension::rand_with_config(9, 1 << 5, &mut rng); + test_kzg_mlpc_helper(&uni_params, &poly2, &mut rng)?; + + // single-variate polynomials + let poly3 = DenseMultilinearExtension::rand(1, &mut rng); + test_kzg_mlpc_helper(&uni_params, &poly3, &mut rng)?; + + let poly4 = SparseMultilinearExtension::rand_with_config(1, 1 << 1, &mut rng); + test_kzg_mlpc_helper(&uni_params, &poly4, &mut rng)?; + Ok(()) + } + + #[test] + fn setup_commit_verify_constant_polynomial() { + let mut rng = test_rng(); + + // normal polynomials + assert!(KZGMultilinearPC::::setup(&mut rng, 0).is_err()); + } +} diff --git a/pcs/src/errors.rs b/pcs/src/errors.rs new file mode 100644 index 0000000..bf4c61f --- /dev/null +++ b/pcs/src/errors.rs @@ -0,0 +1,25 @@ +//! Error module. + +use ark_std::string::String; +use displaydoc::Display; + +/// A `enum` specifying the possible failure modes of the PCS. +#[derive(Display, Debug)] +pub enum PCSErrors { + /// Invalid Prover: {0} + InvalidProver(String), + /// Invalid Verifier: {0} + InvalidVerifier(String), + /// Invalid Proof: {0} + InvalidProof(String), + /// Invalid parameters: {0} + InvalidParameters(String), + /// An error during (de)serialization: {0} + SerializationError(ark_serialize::SerializationError), +} + +impl From for PCSErrors { + fn from(e: ark_serialize::SerializationError) -> Self { + Self::SerializationError(e) + } +} diff --git a/pcs/src/lib.rs b/pcs/src/lib.rs index 1b4a90c..52c247b 100644 --- a/pcs/src/lib.rs +++ b/pcs/src/lib.rs @@ -1,8 +1,53 @@ -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - let result = 2 + 2; - assert_eq!(result, 4); - } +mod commit; +mod errors; +mod param; + +use ark_ec::PairingEngine; +use ark_poly::MultilinearExtension; +use ark_std::rand::RngCore; +use std::marker::PhantomData; + +pub use errors::PCSErrors; +pub use param::{ProverParam, UniversalParams, VerifierParam}; + +/// KZG Polynomial Commitment Scheme on multilinear extensions. +pub struct KZGMultilinearPC { + phantom: PhantomData, +} + +pub trait MultilinearCommitmentScheme { + type ProverParam; + type VerifierParam; + type SRS; + type Commitment; + type Proof; + + /// Generate SRS from RNG. + /// WARNING: THIS FUNCTION IS FOR TESTING PURPOSE ONLY. + /// THE OUTPUT SRS SHOULD NOT BE USED IN PRODUCTION. + fn setup(rng: &mut R, num_vars: usize) -> Result; + + /// Generate a commitment for a polynomial + fn commit( + prover_param: &Self::ProverParam, + poly: &impl MultilinearExtension, + ) -> Result; + + /// On input a polynomial `p` and a point `point`, outputs a proof for the + /// same. + fn open( + prover_param: &Self::ProverParam, + polynomial: &impl MultilinearExtension, + point: &[E::Fr], + ) -> Result; + + /// Verifies that `value` is the evaluation at `x` of the polynomial + /// committed inside `comm`. + fn verify( + verifier_param: &Self::VerifierParam, + commitment: &Self::Commitment, + point: &[E::Fr], + value: E::Fr, + proof: &Self::Proof, + ) -> Result; } diff --git a/pcs/src/param.rs b/pcs/src/param.rs new file mode 100644 index 0000000..316c2f8 --- /dev/null +++ b/pcs/src/param.rs @@ -0,0 +1,259 @@ +use std::collections::LinkedList; + +use crate::PCSErrors; +use ark_ec::{msm::FixedBaseMSM, AffineCurve, PairingEngine, ProjectiveCurve}; +use ark_ff::{Field, PrimeField}; +use ark_poly::DenseMultilinearExtension; +use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Read, SerializationError, Write}; +use ark_std::{end_timer, rand::RngCore, start_timer, vec::Vec, UniformRand}; + +/// Evaluations over {0,1}^n for G1 or G2 +#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug)] +pub struct Evaluations { + pub evals: Vec, +} + +/// Universal Parameter +#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug)] +pub struct UniversalParams { + /// prover parameters + pub prover_param: ProverParam, + /// g^randomness: g^t1, g^t2, ... + pub g_mask: Vec, +} + +/// Prover Parameters +#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug)] +pub struct ProverParam { + /// number of variables + pub num_vars: usize, + /// `pp_{num_vars}`, `pp_{num_vars - 1}`, `pp_{num_vars - 2}`, ..., defined + /// by XZZPD19 + pub powers_of_g: Vec>, + /// `pp_{num_vars}`, `pp_{num_vars - 1}`, `pp_{num_vars - 2}`, ..., defined + /// by XZZPD19 + pub powers_of_h: Vec>, + /// generator for G1 + pub g: E::G1Affine, + /// generator for G2 + pub h: E::G2Affine, +} + +/// Verifier Parameters +#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug)] +pub struct VerifierParam { + /// number of variables + pub num_vars: usize, + /// generator of G1 + pub g: E::G1Affine, + /// generator of G2 + pub h: E::G2Affine, + /// g^t1, g^t2, ... + pub g_mask: Vec, +} + +impl UniversalParams { + /// Extract the prover parameters from the public parameters. + pub fn extract_prover_param(&self) -> ProverParam { + self.prover_param.clone() + } + + /// Extract the verifier parameters from the public parameters. + pub fn extract_verifier_param(&self) -> VerifierParam { + VerifierParam { + num_vars: self.prover_param.num_vars, + g: self.prover_param.g, + h: self.prover_param.h, + g_mask: self.g_mask.clone(), + } + } + + /// Trim the universal parameters to specialize the public parameters + /// for multilinear polynomials to the given `supported_num_vars`, and + /// returns committer key and verifier key. `supported_num_vars` should + /// be in range `1..=params.num_vars` + pub fn trim( + &self, + supported_num_vars: usize, + ) -> Result<(ProverParam, VerifierParam), PCSErrors> { + if supported_num_vars > self.prover_param.num_vars { + return Err(PCSErrors::InvalidParameters(format!( + "SRS does not support target number of vars {}", + supported_num_vars + ))); + } + + let to_reduce = self.prover_param.num_vars - supported_num_vars; + let ck = ProverParam { + powers_of_h: self.prover_param.powers_of_h[to_reduce..].to_vec(), + powers_of_g: self.prover_param.powers_of_g[to_reduce..].to_vec(), + g: self.prover_param.g, + h: self.prover_param.h, + num_vars: supported_num_vars, + }; + let vk = VerifierParam { + num_vars: supported_num_vars, + g: self.prover_param.g, + h: self.prover_param.h, + g_mask: self.g_mask[to_reduce..].to_vec(), + }; + Ok((ck, vk)) + } + + /// Build SRS for testing. + /// WARNING: THIS FUNCTION IS FOR TESTING PURPOSE ONLY. + /// THE OUTPUT SRS SHOULD NOT BE USED IN PRODUCTION. + pub fn gen_srs_for_testing( + rng: &mut R, + num_vars: usize, + ) -> Result { + if num_vars == 0 { + return Err(PCSErrors::InvalidParameters( + "constant polynomial not supported".to_string(), + )); + } + + let total_timer = start_timer!(|| "SRS generation"); + + let pp_generation_timer = start_timer!(|| "Prover Param generation"); + let g = E::G1Projective::rand(rng); + let h = E::G2Projective::rand(rng); + + let mut powers_of_g = Vec::new(); + let mut powers_of_h = Vec::new(); + let t: Vec<_> = (0..num_vars).map(|_| E::Fr::rand(rng)).collect(); + let scalar_bits = E::Fr::size_in_bits(); + + let mut eq: LinkedList> = + LinkedList::from_iter(eq_extension(&t).into_iter()); + let mut eq_arr = LinkedList::new(); + let mut base = eq.pop_back().unwrap().evaluations; + + for i in (0..num_vars).rev() { + eq_arr.push_front(remove_dummy_variable(&base, i)?); + if i != 0 { + let mul = eq.pop_back().unwrap().evaluations; + base = base + .into_iter() + .zip(mul.into_iter()) + .map(|(a, b)| a * b) + .collect(); + } + } + + let mut pp_powers = Vec::new(); + let mut total_scalars = 0; + for i in 0..num_vars { + let eq = eq_arr.pop_front().unwrap(); + let pp_k_powers = (0..(1 << (num_vars - i))).map(|x| eq[x]); + pp_powers.extend(pp_k_powers); + total_scalars += 1 << (num_vars - i); + } + let window_size = FixedBaseMSM::get_mul_window_size(total_scalars); + let g_table = FixedBaseMSM::get_window_table(scalar_bits, window_size, g); + let h_table = FixedBaseMSM::get_window_table(scalar_bits, window_size, h); + + let pp_g = E::G1Projective::batch_normalization_into_affine( + &FixedBaseMSM::multi_scalar_mul(scalar_bits, window_size, &g_table, &pp_powers), + ); + let pp_h = E::G2Projective::batch_normalization_into_affine( + &FixedBaseMSM::multi_scalar_mul(scalar_bits, window_size, &h_table, &pp_powers), + ); + let mut start = 0; + for i in 0..num_vars { + let size = 1 << (num_vars - i); + let pp_k_g = Evaluations { + evals: pp_g[start..(start + size)].to_vec(), + }; + let pp_k_h = Evaluations { + evals: pp_h[start..(start + size)].to_vec(), + }; + powers_of_g.push(pp_k_g); + powers_of_h.push(pp_k_h); + start += size; + } + + let pp = ProverParam { + num_vars, + g: g.into_affine(), + h: h.into_affine(), + powers_of_g, + powers_of_h, + }; + + end_timer!(pp_generation_timer); + + let vp_generation_timer = start_timer!(|| "VP generation"); + let g_mask = { + let window_size = FixedBaseMSM::get_mul_window_size(num_vars); + let g_table = FixedBaseMSM::get_window_table(scalar_bits, window_size, g); + E::G1Projective::batch_normalization_into_affine(&FixedBaseMSM::multi_scalar_mul( + scalar_bits, + window_size, + &g_table, + &t, + )) + }; + end_timer!(vp_generation_timer); + end_timer!(total_timer); + Ok(Self { + prover_param: pp, + g_mask, + }) + } +} + +/// fix first `pad` variables of `poly` represented in evaluation form to zero +fn remove_dummy_variable(poly: &[F], pad: usize) -> Result, PCSErrors> { + if pad == 0 { + return Ok(poly.to_vec()); + } + if !poly.len().is_power_of_two() { + return Err(PCSErrors::InvalidParameters( + "Size of polynomial should be power of two.".to_string(), + )); + } + let nv = ark_std::log2(poly.len()) as usize - pad; + + Ok((0..(1 << nv)).map(|x| poly[x << pad]).collect()) +} + +/// Generate eq(t,x), a product of multilinear polynomials with fixed t. +/// eq(a,b) is takes extensions of a,b in {0,1}^num_vars such that if a and b in +/// {0,1}^num_vars are equal then this polynomial evaluates to 1. +fn eq_extension(t: &[F]) -> Vec> { + let start = start_timer!(|| "eq extension"); + + let dim = t.len(); + let mut result = Vec::new(); + for (i, &ti) in t.iter().enumerate().take(dim) { + let mut poly = Vec::with_capacity(1 << dim); + for x in 0..(1 << dim) { + let xi = if x >> i & 1 == 1 { F::one() } else { F::zero() }; + let ti_xi = ti * xi; + poly.push(ti_xi + ti_xi - xi - ti + F::one()); + } + result.push(DenseMultilinearExtension::from_evaluations_vec(dim, poly)); + } + + end_timer!(start); + result +} + +#[cfg(test)] +mod tests { + use super::*; + use ark_bls12_381::Bls12_381; + use ark_std::test_rng; + type E = Bls12_381; + + #[test] + fn test_srs_gen() -> Result<(), PCSErrors> { + let mut rng = test_rng(); + for nv in 4..10 { + let _ = UniversalParams::::gen_srs_for_testing(&mut rng, nv)?; + } + + Ok(()) + } +} diff --git a/poly-iop/Cargo.toml b/poly-iop/Cargo.toml index 1ee3835..3fcf4bd 100644 --- a/poly-iop/Cargo.toml +++ b/poly-iop/Cargo.toml @@ -26,8 +26,8 @@ path = "benches/bench.rs" harness = false [features] -default = [ "parallel", "print-trace" ] -# default = [ "parallel" ] +# default = [ "parallel", "print-trace" ] +default = [ "parallel" ] parallel = [ "rayon", "ark-std/parallel", diff --git a/poly-iop/benches/bench.rs b/poly-iop/benches/bench.rs index 7f79d5f..1886138 100644 --- a/poly-iop/benches/bench.rs +++ b/poly-iop/benches/bench.rs @@ -1,8 +1,7 @@ -use std::time::Instant; - use ark_bls12_381::Fr; use ark_std::test_rng; use poly_iop::{PolyIOP, PolyIOPErrors, SumCheck, VirtualPolynomial, ZeroCheck}; +use std::time::Instant; fn main() -> Result<(), PolyIOPErrors> { bench_sum_check()?; @@ -27,8 +26,10 @@ fn bench_sum_check() -> Result<(), PolyIOPErrors> { let poly_info = poly.domain_info.clone(); let proof = { let start = Instant::now(); - let mut transcript = as SumCheck>::init_transcript(); - let proof = as SumCheck>::prove(&poly, &mut transcript)?; + for _ in 0..repetition { + let mut transcript = as SumCheck>::init_transcript(); + let _proof = as SumCheck>::prove(&poly, &mut transcript)?; + } println!( "sum check proving time for {} variables and {} degree: {} ns", @@ -36,26 +37,30 @@ fn bench_sum_check() -> Result<(), PolyIOPErrors> { degree, start.elapsed().as_nanos() / repetition as u128 ); - proof + let mut transcript = as SumCheck>::init_transcript(); + as SumCheck>::prove(&poly, &mut transcript)? }; { let start = Instant::now(); - let mut transcript = as SumCheck>::init_transcript(); - let subclaim = as SumCheck>::verify( - asserted_sum, - &proof, - &poly_info, - &mut transcript, - )?; - assert!( - poly.evaluate(&subclaim.point).unwrap() == subclaim.expected_evaluation, - "wrong subclaim" - ); + for _ in 0..repetition { + let mut transcript = as SumCheck>::init_transcript(); + let subclaim = as SumCheck>::verify( + asserted_sum, + &proof, + &poly_info, + &mut transcript, + )?; + assert!( + poly.evaluate(&subclaim.point).unwrap() == subclaim.expected_evaluation, + "wrong subclaim" + ); + } println!( - "sum check verification time for {} variables: {} ns", + "sum check verification time for {} variables and {} degree: {} ns", nv, + degree, start.elapsed().as_nanos() / repetition as u128 ); } @@ -106,8 +111,9 @@ fn bench_zero_check() -> Result<(), PolyIOPErrors> { "wrong subclaim" ); println!( - "zero check verification time for {} variables: {} ns", + "zero check verification time for {} variables and {} degree:: {} ns", nv, + degree, start.elapsed().as_nanos() / repetition as u128 ); } diff --git a/poly-iop/readme.md b/poly-iop/readme.md index 0881499..65ec136 100644 --- a/poly-iop/readme.md +++ b/poly-iop/readme.md @@ -3,5 +3,5 @@ Poly IOP Implements the following protocols -- [ ] sum checks -- [ ] zero checks \ No newline at end of file +- [x] sum checks +- [x] zero checks \ No newline at end of file diff --git a/poly-iop/src/errors.rs b/poly-iop/src/errors.rs index 580345a..e30fb88 100644 --- a/poly-iop/src/errors.rs +++ b/poly-iop/src/errors.rs @@ -6,17 +6,17 @@ use displaydoc::Display; /// A `enum` specifying the possible failure modes of the PolyIOP. #[derive(Display, Debug)] pub enum PolyIOPErrors { - /// Invalid Prover + /// Invalid Prover: {0} InvalidProver(String), - /// Invalid Verifier + /// Invalid Verifier: {0} InvalidVerifier(String), - /// Invalid Proof + /// Invalid Proof: {0} InvalidProof(String), - /// Invalid parameters + /// Invalid parameters: {0} InvalidParameters(String), - /// Invalid Transcript + /// Invalid Transcript: {0} InvalidTranscript(String), - /// An error during (de)serialization + /// An error during (de)serialization: {0} SerializationError(ark_serialize::SerializationError), } diff --git a/poly-iop/src/lib.rs b/poly-iop/src/lib.rs index f89f26f..06882a9 100644 --- a/poly-iop/src/lib.rs +++ b/poly-iop/src/lib.rs @@ -12,7 +12,7 @@ mod zero_check; pub use errors::PolyIOPErrors; pub use sum_check::SumCheck; pub use virtual_poly::VirtualPolynomial; -pub use zero_check::ZeroCheck; +pub use zero_check::{build_eq_x_r, ZeroCheck}; /// Struct for PolyIOP protocol. /// It is instantiated with diff --git a/poly-iop/src/zero_check/mod.rs b/poly-iop/src/zero_check/mod.rs index e28da37..adb9d92 100644 --- a/poly-iop/src/zero_check/mod.rs +++ b/poly-iop/src/zero_check/mod.rs @@ -137,7 +137,9 @@ fn build_f_hat( // eq(x,y) = \prod_i=1^num_var (x_i * y_i + (1-x_i)*(1-y_i)) // over r, which is // eq(x,y) = \prod_i=1^num_var (x_i * r_i + (1-x_i)*(1-r_i)) -fn build_eq_x_r(r: &[F]) -> Result>, PolyIOPErrors> { +pub fn build_eq_x_r( + r: &[F], +) -> Result>, PolyIOPErrors> { let start = start_timer!(|| "zero check build eq_x_r"); // we build eq(x,r) from its evaluations