From 96ace270d8c27797a25c3d77b8f4091f53b465ce Mon Sep 17 00:00:00 2001 From: zhenfei Date: Thu, 16 Jun 2022 10:54:50 -0400 Subject: [PATCH 01/10] multi-commiting/opening --- pcs/src/commit.rs | 160 ++++++++++++++++++++++++++++++++++++++++------ pcs/src/lib.rs | 14 ++++ 2 files changed, 156 insertions(+), 18 deletions(-) diff --git a/pcs/src/commit.rs b/pcs/src/commit.rs index 7ad2ef67..44195645 100644 --- a/pcs/src/commit.rs +++ b/pcs/src/commit.rs @@ -1,16 +1,15 @@ +use crate::{ + KZGMultilinearPC, MultilinearCommitmentScheme, PCSErrors, ProverParam, UniversalParams, + VerifierParam, +}; use ark_ec::{ msm::{FixedBaseMSM, VariableBaseMSM}, AffineCurve, PairingEngine, ProjectiveCurve, }; use ark_ff::PrimeField; -use ark_poly::MultilinearExtension; +use ark_poly::{DenseMultilinearExtension, 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, -}; +use ark_std::{end_timer, log2, rand::RngCore, start_timer, vec::Vec, One, Zero}; #[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug)] /// commitment @@ -71,6 +70,36 @@ impl MultilinearCommitmentScheme for KZGMultilinearPC { Ok(Commitment { nv, g_product }) } + /// Generate a commitment for a list of polynomials. + /// + /// This function takes `\sum 2^num_vars[i]` number of scalar + /// multiplications over G1. + fn multi_commit( + prover_param: &Self::ProverParam, + polys: &[impl MultilinearExtension], + ) -> Result { + let commit_timer = start_timer!(|| "commit"); + let poly = merge_polynomials(polys); + + let scalars: Vec<_> = poly + .to_evaluations() + .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: poly.num_vars, + 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. @@ -136,6 +165,30 @@ impl MultilinearCommitmentScheme for KZGMultilinearPC { Ok(Proof { proofs }) } + /// 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 multi_open( + prover_param: &Self::ProverParam, + polynomials: &[impl MultilinearExtension], + point: &[E::Fr], + ) -> Result { + let open_timer = start_timer!(|| "open"); + + let poly = merge_polynomials(polynomials); + + let proof = Self::open(prover_param, &poly, point)?; + + end_timer!(open_timer); + Ok(proof) + } + /// Verifies that `value` is the evaluation at `x` of the polynomial /// committed inside `comm`. /// @@ -199,17 +252,50 @@ impl MultilinearCommitmentScheme for KZGMultilinearPC { } } +/// Return the number of variables that one need for an MLE to +/// batch the list of MLEs +#[inline] +fn get_nv(polynomials: &[impl MultilinearExtension]) -> usize { + polynomials.iter().map(|x| x.num_vars()).max().unwrap() + log2(polynomials.len()) as usize +} + +fn merge_polynomials( + polynomials: &[impl MultilinearExtension], +) -> DenseMultilinearExtension { + let individual_max = polynomials.iter().map(|x| x.num_vars()).max() + // safe unwrap since polynomials is not empty + .unwrap(); + let new_nv = get_nv(polynomials); + let mut scalars = vec![]; + for poly in polynomials.iter() { + let mut scalar = poly.to_evaluations(); + + if poly.num_vars() < individual_max { + scalar + .extend_from_slice(vec![F::zero(); (1 << individual_max) - scalar.len()].as_ref()); + } + scalars.extend_from_slice(scalar.as_slice()); + } + scalars.extend_from_slice(vec![F::zero(); (1 << new_nv) - scalars.len()].as_ref()); + DenseMultilinearExtension::from_evaluations_vec(new_nv, scalars) +} + #[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}; + use ark_poly::{DenseMultilinearExtension, MultilinearExtension}; + use ark_std::{ + rand::{Rng, RngCore}, + test_rng, + vec::Vec, + UniformRand, + }; type E = Bls12_381; type Fr = ::Fr; - fn test_kzg_mlpc_helper( + fn test_single_helper( uni_params: &UniversalParams, poly: &impl MultilinearExtension, rng: &mut R, @@ -231,24 +317,62 @@ mod tests { } #[test] - fn setup_commit_verify_correct_polynomials() -> Result<(), PCSErrors> { + fn test_single_commit() -> 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)?; + test_single_helper(&uni_params, &poly1, &mut rng)?; + + // single-variate polynomials + let poly2 = DenseMultilinearExtension::rand(1, &mut rng); + test_single_helper(&uni_params, &poly2, &mut rng)?; + + Ok(()) + } - let poly2 = SparseMultilinearExtension::rand_with_config(9, 1 << 5, &mut rng); - test_kzg_mlpc_helper(&uni_params, &poly2, &mut rng)?; + fn test_multi_commit_helper( + uni_params: &UniversalParams, + polys: &[impl MultilinearExtension], + rng: &mut R, + ) -> Result<(), PCSErrors> { + let poly = merge_polynomials(polys); + let nv = poly.num_vars; + + let (ck, vk) = uni_params.trim(nv)?; + let point: Vec<_> = (0..nv).map(|_| Fr::rand(rng)).collect(); + let com = KZGMultilinearPC::multi_commit(&ck, polys)?; + let proof = KZGMultilinearPC::multi_open(&ck, polys, &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 test_multi_commit() -> Result<(), PCSErrors> { + let mut rng = test_rng(); + + let uni_params = KZGMultilinearPC::::setup(&mut rng, 20)?; + + // normal polynomials + let polys1: Vec<_> = (0..5) + .map(|_| DenseMultilinearExtension::rand(rng.gen_range(5..9), &mut rng)) + .collect(); + test_multi_commit_helper(&uni_params, &polys1, &mut rng)?; // single-variate polynomials - let poly3 = DenseMultilinearExtension::rand(1, &mut rng); - test_kzg_mlpc_helper(&uni_params, &poly3, &mut rng)?; + let polys1: Vec<_> = (0..5) + .map(|_| DenseMultilinearExtension::rand(1, &mut rng)) + .collect(); + test_multi_commit_helper(&uni_params, &polys1, &mut rng)?; - let poly4 = SparseMultilinearExtension::rand_with_config(1, 1 << 1, &mut rng); - test_kzg_mlpc_helper(&uni_params, &poly4, &mut rng)?; Ok(()) } diff --git a/pcs/src/lib.rs b/pcs/src/lib.rs index 52c247b5..29feacd0 100644 --- a/pcs/src/lib.rs +++ b/pcs/src/lib.rs @@ -33,6 +33,12 @@ pub trait MultilinearCommitmentScheme { poly: &impl MultilinearExtension, ) -> Result; + /// Generate a commitment for a list of polynomials + fn multi_commit( + prover_param: &Self::ProverParam, + polys: &[impl MultilinearExtension], + ) -> Result; + /// On input a polynomial `p` and a point `point`, outputs a proof for the /// same. fn open( @@ -41,6 +47,14 @@ pub trait MultilinearCommitmentScheme { point: &[E::Fr], ) -> Result; + /// On input a polynomial `p` and a point `point`, outputs a proof for the + /// same. + fn multi_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( From 69113cc9f91e156687bdd131a0bd98eb03d45c34 Mon Sep 17 00:00:00 2001 From: zhenfei Date: Fri, 17 Jun 2022 16:25:40 -0400 Subject: [PATCH 02/10] w circ l function --- pcs/src/commit.rs | 17 ++---- pcs/src/lib.rs | 7 +-- pcs/src/util.rs | 130 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 137 insertions(+), 17 deletions(-) create mode 100644 pcs/src/util.rs diff --git a/pcs/src/commit.rs b/pcs/src/commit.rs index 44195645..e7fa00fb 100644 --- a/pcs/src/commit.rs +++ b/pcs/src/commit.rs @@ -165,15 +165,7 @@ impl MultilinearCommitmentScheme for KZGMultilinearPC { Ok(Proof { proofs }) } - /// 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. + /// TODO fn multi_open( prover_param: &Self::ProverParam, polynomials: &[impl MultilinearExtension], @@ -181,12 +173,9 @@ impl MultilinearCommitmentScheme for KZGMultilinearPC { ) -> Result { let open_timer = start_timer!(|| "open"); - let poly = merge_polynomials(polynomials); - - let proof = Self::open(prover_param, &poly, point)?; - end_timer!(open_timer); - Ok(proof) + + todo!() } /// Verifies that `value` is the evaluation at `x` of the polynomial diff --git a/pcs/src/lib.rs b/pcs/src/lib.rs index 29feacd0..f7b8aae0 100644 --- a/pcs/src/lib.rs +++ b/pcs/src/lib.rs @@ -1,6 +1,7 @@ mod commit; mod errors; mod param; +mod util; use ark_ec::PairingEngine; use ark_poly::MultilinearExtension; @@ -47,11 +48,11 @@ pub trait MultilinearCommitmentScheme { point: &[E::Fr], ) -> Result; - /// On input a polynomial `p` and a point `point`, outputs a proof for the - /// same. + /// On input a list of polynomials and a point `point`, outputs a proof for + /// the same. fn multi_open( prover_param: &Self::ProverParam, - polynomial: &[impl MultilinearExtension], + polynomials: &[impl MultilinearExtension], point: &[E::Fr], ) -> Result; diff --git a/pcs/src/util.rs b/pcs/src/util.rs new file mode 100644 index 00000000..1348c306 --- /dev/null +++ b/pcs/src/util.rs @@ -0,0 +1,130 @@ +//! Useful utilities for KZG PCS + +use crate::PCSErrors; +use ark_ff::PrimeField; +use ark_poly::{ + univariate::DensePolynomial, DenseMultilinearExtension, EvaluationDomain, Evaluations, + MultilinearExtension, Polynomial, Radix2EvaluationDomain, +}; +use ark_std::{end_timer, start_timer}; + +/// Compute W \circ l. +/// +/// Given an MLE W, and a list of univariate polynomials l, evaluate W at l. +/// +/// Returns an error is l's length does not matches number of variables in W. +pub(crate) fn compute_w_circ_l( + w: &DenseMultilinearExtension, + l: &[DensePolynomial], +) -> Result, PCSErrors> { + let timer = start_timer!(|| "compute W \\circ l"); + + if w.num_vars != l.len() { + return Err(PCSErrors::InvalidParameters( + "l's length does not match num_variables".to_string(), + )); + } + + let mut res_eval: Vec = vec![]; + let uni_degree = l.len() + 1; + + let domain = match Radix2EvaluationDomain::::new(uni_degree) { + Some(p) => p, + None => { + return Err(PCSErrors::InvalidParameters( + "failed to build radix 2 domain".to_string(), + )) + }, + }; + + for point in domain.elements() { + let l_eval: Vec = l.iter().map(|x| x.evaluate(&point)).collect(); + res_eval.push(w.evaluate(l_eval.as_ref()).unwrap()) + } + let evaluation = Evaluations::from_vec_and_domain(res_eval, domain); + let res = evaluation.interpolate(); + + end_timer!(timer); + Ok(res) +} + +#[cfg(test)] +mod test { + use super::*; + use ark_bls12_381::Fr; + use ark_poly::UVPolynomial; + + #[test] + fn test_w_circ_l() -> Result<(), PCSErrors> { + test_w_circ_l_helper::() + } + + fn test_w_circ_l_helper() -> Result<(), PCSErrors> { + { + // Example from page 53: + // W = 3x1x2 + 2x2 whose evaluations are + // 0, 0 |-> 0 + // 0, 1 |-> 2 + // 1, 0 |-> 0 + // 1, 1 |-> 5 + let w_eval = vec![F::zero(), F::from(2u64), F::zero(), F::from(5u64)]; + let w = DenseMultilinearExtension::from_evaluations_vec(2, w_eval); + + // l0 = t + 2 + // l1 = -2t + 4 + let l0 = DensePolynomial::from_coefficients_vec(vec![F::from(2u64), F::one()]); + let l1 = DensePolynomial::from_coefficients_vec(vec![F::from(4u64), -F::from(2u64)]); + + // res = -6t^2 - 4t + 32 + let res = compute_w_circ_l(&w, [l1, l0].as_ref())?; + let res_rec = DensePolynomial::from_coefficients_vec(vec![ + F::from(32u64), + -F::from(4u64), + -F::from(6u64), + ]); + assert_eq!(res, res_rec); + } + { + // A random example + // W = x1x2x3 - 2x1x2 + 3x2x3 - 4x1x3 + 5x1 - 6x2 + 7x3 + // 0, 0, 0 |-> 0 + // 0, 0, 1 |-> 7 + // 0, 1, 0 |-> -6 + // 0, 1, 1 |-> 4 + // 1, 0, 0 |-> 5 + // 1, 0, 1 |-> 8 + // 1, 1, 0 |-> -3 + // 1, 1, 1 |-> 4 + let w_eval = vec![ + F::zero(), + F::from(7u64), + -F::from(6u64), + F::from(4u64), + F::from(5u64), + F::from(8u64), + -F::from(3u64), + F::from(4u64), + ]; + let w = DenseMultilinearExtension::from_evaluations_vec(3, w_eval); + + // l0 = t + 2 + // l1 = 3t - 4 + // l2 = -5t + 6 + let l0 = DensePolynomial::from_coefficients_vec(vec![F::from(2u64), F::one()]); + let l1 = DensePolynomial::from_coefficients_vec(vec![-F::from(4u64), F::from(3u64)]); + let l2 = DensePolynomial::from_coefficients_vec(vec![F::from(6u64), -F::from(5u64)]); + let res = compute_w_circ_l(&w, [l2, l1, l0].as_ref())?; + + // res = -15t^3 - 23t^2 + 130t - 76 + let res_rec = DensePolynomial::from_coefficients_vec(vec![ + -F::from(76u64), + F::from(130u64), + -F::from(23u64), + -F::from(15u64), + ]); + + assert_eq!(res, res_rec); + } + Ok(()) + } +} From ae65439ed573a99c686293587110e7445e2cc53c Mon Sep 17 00:00:00 2001 From: zhenfei Date: Wed, 29 Jun 2022 12:44:43 -0400 Subject: [PATCH 03/10] wip --- pcs/Cargo.toml | 7 ++- pcs/src/commit.rs | 112 ++++++++++++++++++++++++++++++------- pcs/src/errors.rs | 14 ++++- pcs/src/lib.rs | 7 ++- pcs/src/param.rs | 1 + pcs/src/util.rs | 2 +- poly-iop/src/lib.rs | 1 + poly-iop/src/transcript.rs | 9 +-- 8 files changed, 122 insertions(+), 31 deletions(-) diff --git a/pcs/Cargo.toml b/pcs/Cargo.toml index 669ba65a..42929f58 100644 --- a/pcs/Cargo.toml +++ b/pcs/Cargo.toml @@ -17,6 +17,9 @@ ark-bls12-381 = { version = "0.3.0", default-features = false, features = [ "cur displaydoc = { version = "0.2.3", default-features = false } +poly-iop = { path = "../poly-iop" } + + # Benchmarks [[bench]] name = "pcs-benches" @@ -31,7 +34,9 @@ parallel = [ "ark-ff/parallel", "ark-poly/parallel", "ark-ec/parallel", + "poly-iop/parallel" ] print-trace = [ - "ark-std/print-trace" + "ark-std/print-trace", + "poly-iop/print-trace" ] \ No newline at end of file diff --git a/pcs/src/commit.rs b/pcs/src/commit.rs index e7fa00fb..9c761f50 100644 --- a/pcs/src/commit.rs +++ b/pcs/src/commit.rs @@ -1,15 +1,19 @@ use crate::{ - KZGMultilinearPC, MultilinearCommitmentScheme, PCSErrors, ProverParam, UniversalParams, - VerifierParam, + util::compute_w_circ_l, KZGMultilinearPC, MultilinearCommitmentScheme, PCSErrors, ProverParam, + UniversalParams, VerifierParam, }; use ark_ec::{ msm::{FixedBaseMSM, VariableBaseMSM}, AffineCurve, PairingEngine, ProjectiveCurve, }; use ark_ff::PrimeField; -use ark_poly::{DenseMultilinearExtension, MultilinearExtension}; +use ark_poly::{ + DenseMultilinearExtension, EvaluationDomain, Evaluations, MultilinearExtension, Polynomial, + Radix2EvaluationDomain, +}; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Read, SerializationError, Write}; use ark_std::{end_timer, log2, rand::RngCore, start_timer, vec::Vec, One, Zero}; +use poly_iop::IOPTranscript; #[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug)] /// commitment @@ -72,7 +76,7 @@ impl MultilinearCommitmentScheme for KZGMultilinearPC { /// Generate a commitment for a list of polynomials. /// - /// This function takes `\sum 2^num_vars[i]` number of scalar + /// This function takes `2^(num_vars + log(polys.len())` number of scalar /// multiplications over G1. fn multi_commit( prover_param: &Self::ProverParam, @@ -105,7 +109,7 @@ impl MultilinearCommitmentScheme for KZGMultilinearPC { /// input. /// /// This function takes 2^{num_var +1} number of scalar multiplications over - /// G2: + /// G1: /// - 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. @@ -165,17 +169,84 @@ impl MultilinearCommitmentScheme for KZGMultilinearPC { Ok(Proof { proofs }) } - /// TODO + /// Steps: + /// 1. build `l(points)` which is a list of univariate polynomials that goes + /// through the points + /// 2. build MLE `w` which is the merge of all MLEs. + /// 3. build `q(x)` which is a univariate polynomial `W circ l` + /// 4. output `q(x)`'s evaluations over `(1, omega,...)`, and put it into + /// transcript + /// 5. sample `r` from transcript + /// 6. get a point `p := l(r)` + /// 7. output an opening of `w` over point `p` fn multi_open( prover_param: &Self::ProverParam, polynomials: &[impl MultilinearExtension], - point: &[E::Fr], - ) -> Result { + points: &[&[E::Fr]], + transcript: &mut IOPTranscript, + ) -> Result<(Self::Proof, Vec), PCSErrors> { let open_timer = start_timer!(|| "open"); + if points.len() != polynomials.len() { + return Err(PCSErrors::InvalidParameters( + "polynomial length does not match point length".to_string(), + )); + } + + let uni_degree = points.len() + 1; + let small_domain = match Radix2EvaluationDomain::::new(uni_degree) { + Some(p) => p, + None => { + return Err(PCSErrors::InvalidParameters( + "failed to build radix 2 domain".to_string(), + )) + }, + }; + let num_var = polynomials[0].num_vars(); + let large_domain = match Radix2EvaluationDomain::::new(1 << get_nv(polynomials)) { + Some(p) => p, + None => { + return Err(PCSErrors::InvalidParameters( + "failed to build radix 2 domain".to_string(), + )) + }, + }; + + // 1. build `l(points)` which is a list of univariate polynomials that goes + // through the points + let mut uni_polys = Vec::new(); + for i in 0..1 << num_var { + let eval: Vec = points.iter().map(|x| x[i]).collect(); + uni_polys.push(Evaluations::from_vec_and_domain(eval, small_domain).interpolate()) + } + // 2. build MLE `w` which is the merge of all MLEs. + let merge_poly = merge_polynomials(polynomials); + + // 3. build `q(x)` which is a univariate polynomial `W circ l` + let q_x = compute_w_circ_l(&merge_poly, &uni_polys)?; + + // 4. output `q(x)`'s evaluations over `(1, omega,...)`, and put it into + // transcript + // + // TODO: use KZG commit for q(x) + let evals = q_x.evaluate_over_domain(large_domain); + evals + .evals + .iter() + .for_each(|x| transcript.append_field_element(b"q(x)", x).unwrap()); + + // 5. sample `r` from transcript + let r = transcript.get_and_append_challenge(b"r")?; + + // 6. get a point `p := l(r)` + let point: Vec = uni_polys.iter().map(|poly| poly.evaluate(&r)).collect(); + + // 7. output an opening of `w` over point `p` + let opening = Self::open(prover_param, &merge_poly, &point)?; + end_timer!(open_timer); - todo!() + Ok((opening, evals.evals)) } /// Verifies that `value` is the evaluation at `x` of the polynomial @@ -327,19 +398,22 @@ mod tests { polys: &[impl MultilinearExtension], rng: &mut R, ) -> Result<(), PCSErrors> { - let poly = merge_polynomials(polys); - let nv = poly.num_vars; + let mut transcript = IOPTranscript::new(b"test"); - let (ck, vk) = uni_params.trim(nv)?; - let point: Vec<_> = (0..nv).map(|_| Fr::rand(rng)).collect(); - let com = KZGMultilinearPC::multi_commit(&ck, polys)?; - let proof = KZGMultilinearPC::multi_open(&ck, polys, &point)?; + let nv = get_nv(polys); + let (ck, _vk) = uni_params.trim(nv)?; + let mut points = Vec::new(); - let value = poly.evaluate(&point).unwrap(); - assert!(KZGMultilinearPC::verify(&vk, &com, &point, value, &proof)?); + for poly in polys.iter() { + let point = (0..poly.num_vars()) + .map(|_| Fr::rand(rng)) + .collect::>(); + points.push(point); + } + let points_ref: Vec<&[Fr]> = points.iter().map(|x| x.as_ref()).collect(); - let value = Fr::rand(rng); - assert!(!KZGMultilinearPC::verify(&vk, &com, &point, value, &proof)?); + let _com = KZGMultilinearPC::multi_commit(&ck, polys)?; + let _proof = KZGMultilinearPC::multi_open(&ck, polys, &points_ref, &mut transcript)?; Ok(()) } diff --git a/pcs/src/errors.rs b/pcs/src/errors.rs index bf4c61f2..99d060d3 100644 --- a/pcs/src/errors.rs +++ b/pcs/src/errors.rs @@ -1,7 +1,9 @@ //! Error module. +use ark_serialize::SerializationError; use ark_std::string::String; use displaydoc::Display; +use poly_iop::PolyIOPErrors; /// A `enum` specifying the possible failure modes of the PCS. #[derive(Display, Debug)] @@ -15,11 +17,19 @@ pub enum PCSErrors { /// Invalid parameters: {0} InvalidParameters(String), /// An error during (de)serialization: {0} - SerializationError(ark_serialize::SerializationError), + SerializationError(SerializationError), + /// PolyIOP error {0} + PolyIOPErrors(PolyIOPErrors), } -impl From for PCSErrors { +impl From for PCSErrors { fn from(e: ark_serialize::SerializationError) -> Self { Self::SerializationError(e) } } + +impl From for PCSErrors { + fn from(e: PolyIOPErrors) -> Self { + Self::PolyIOPErrors(e) + } +} diff --git a/pcs/src/lib.rs b/pcs/src/lib.rs index f7b8aae0..b7c9ec87 100644 --- a/pcs/src/lib.rs +++ b/pcs/src/lib.rs @@ -6,6 +6,7 @@ mod util; use ark_ec::PairingEngine; use ark_poly::MultilinearExtension; use ark_std::rand::RngCore; +use poly_iop::IOPTranscript; use std::marker::PhantomData; pub use errors::PCSErrors; @@ -13,6 +14,7 @@ pub use param::{ProverParam, UniversalParams, VerifierParam}; /// KZG Polynomial Commitment Scheme on multilinear extensions. pub struct KZGMultilinearPC { + #[doc(hidden)] phantom: PhantomData, } @@ -53,8 +55,9 @@ pub trait MultilinearCommitmentScheme { fn multi_open( prover_param: &Self::ProverParam, polynomials: &[impl MultilinearExtension], - point: &[E::Fr], - ) -> Result; + point: &[&[E::Fr]], + transcript: &mut IOPTranscript, + ) -> Result<(Self::Proof, Vec), PCSErrors>; /// Verifies that `value` is the evaluation at `x` of the polynomial /// committed inside `comm`. diff --git a/pcs/src/param.rs b/pcs/src/param.rs index c70beb1d..f84a623b 100644 --- a/pcs/src/param.rs +++ b/pcs/src/param.rs @@ -10,6 +10,7 @@ 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 { + /// The evaluations. pub evals: Vec, } diff --git a/pcs/src/util.rs b/pcs/src/util.rs index 1348c306..478721c4 100644 --- a/pcs/src/util.rs +++ b/pcs/src/util.rs @@ -12,7 +12,7 @@ use ark_std::{end_timer, start_timer}; /// /// Given an MLE W, and a list of univariate polynomials l, evaluate W at l. /// -/// Returns an error is l's length does not matches number of variables in W. +/// Returns an error if l's length does not matches number of variables in W. pub(crate) fn compute_w_circ_l( w: &DenseMultilinearExtension, l: &[DensePolynomial], diff --git a/poly-iop/src/lib.rs b/poly-iop/src/lib.rs index 897a5828..1aa3224a 100644 --- a/poly-iop/src/lib.rs +++ b/poly-iop/src/lib.rs @@ -16,6 +16,7 @@ pub use perm_check::{ PermutationCheck, }; pub use sum_check::SumCheck; +pub use transcript::IOPTranscript; pub use virtual_poly::{VPAuxInfo, VirtualPolynomial}; pub use zero_check::ZeroCheck; diff --git a/poly-iop/src/transcript.rs b/poly-iop/src/transcript.rs index d14fb172..0a455d2a 100644 --- a/poly-iop/src/transcript.rs +++ b/poly-iop/src/transcript.rs @@ -28,7 +28,7 @@ pub struct IOPTranscript { impl IOPTranscript { /// Create a new IOP transcript. - pub(crate) fn new(label: &'static [u8]) -> Self { + pub fn new(label: &'static [u8]) -> Self { Self { transcript: Transcript::new(label), is_empty: true, @@ -59,7 +59,7 @@ impl IOPTranscript { } // Append the message to the transcript. - pub(crate) fn append_field_element( + pub fn append_field_element( &mut self, label: &'static [u8], field_elem: &F, @@ -83,10 +83,7 @@ impl IOPTranscript { // // The output field element is statistical uniform as long // as the field has a size less than 2^384. - pub(crate) fn get_and_append_challenge( - &mut self, - label: &'static [u8], - ) -> Result { + pub fn get_and_append_challenge(&mut self, label: &'static [u8]) -> Result { // we need to reject when transcript is empty if self.is_empty { return Err(PolyIOPErrors::InvalidTranscript( From 0bea82086a6ab95aa1dfcdbb84041b5f6aa2cf0a Mon Sep 17 00:00:00 2001 From: zhenfei Date: Wed, 29 Jun 2022 13:25:17 -0400 Subject: [PATCH 04/10] wip opening --- pcs/src/commit.rs | 48 ++++++++++++++++++++++++++++++++++--------- pcs/src/util.rs | 8 +++++--- poly-iop/src/lib.rs | 1 + poly-iop/src/utils.rs | 2 +- 4 files changed, 45 insertions(+), 14 deletions(-) diff --git a/pcs/src/commit.rs b/pcs/src/commit.rs index 9c761f50..74116fde 100644 --- a/pcs/src/commit.rs +++ b/pcs/src/commit.rs @@ -13,7 +13,7 @@ use ark_poly::{ }; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Read, SerializationError, Write}; use ark_std::{end_timer, log2, rand::RngCore, start_timer, vec::Vec, One, Zero}; -use poly_iop::IOPTranscript; +use poly_iop::{bit_decompose, IOPTranscript}; #[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug)] /// commitment @@ -169,6 +169,11 @@ impl MultilinearCommitmentScheme for KZGMultilinearPC { Ok(Proof { proofs }) } + /// Input a list of MLEs, and a same number of points, and a transcript, + /// compute a multi-opening for all the polynomials + /// + /// Returns an error if the lengths do not match. + /// /// Steps: /// 1. build `l(points)` which is a list of univariate polynomials that goes /// through the points @@ -193,6 +198,22 @@ impl MultilinearCommitmentScheme for KZGMultilinearPC { )); } + let num_var = polynomials[0].num_vars(); + for poly in polynomials.iter().skip(1) { + if poly.num_vars() != num_var { + return Err(PCSErrors::InvalidParameters( + "polynomials do not have same num_vars".to_string(), + )); + } + } + for &point in points.iter() { + if point.len() != num_var { + return Err(PCSErrors::InvalidParameters( + "points do not have same num_vars".to_string(), + )); + } + } + let uni_degree = points.len() + 1; let small_domain = match Radix2EvaluationDomain::::new(uni_degree) { Some(p) => p, @@ -202,7 +223,6 @@ impl MultilinearCommitmentScheme for KZGMultilinearPC { )) }, }; - let num_var = polynomials[0].num_vars(); let large_domain = match Radix2EvaluationDomain::::new(1 << get_nv(polynomials)) { Some(p) => p, None => { @@ -211,14 +231,27 @@ impl MultilinearCommitmentScheme for KZGMultilinearPC { )) }, }; + let prefix_len = log2(polynomials.len()) as usize; // 1. build `l(points)` which is a list of univariate polynomials that goes // through the points let mut uni_polys = Vec::new(); - for i in 0..1 << num_var { + + // 1.1 build the indexes and the univariate polys that go through the indexes + let indexes: Vec> = (0..num_var) + .map(|x| bit_decompose(x as u64, prefix_len)) + .collect(); + for i in 0..prefix_len { + let eval: Vec = indexes.iter().map(|x| E::Fr::from(x[i])).collect(); + uni_polys.push(Evaluations::from_vec_and_domain(eval, small_domain).interpolate()) + } + + // 1.2 build the actual univariate polys that go through the points + for i in 0..num_var { let eval: Vec = points.iter().map(|x| x[i]).collect(); uni_polys.push(Evaluations::from_vec_and_domain(eval, small_domain).interpolate()) } + // 2. build MLE `w` which is the merge of all MLEs. let merge_poly = merge_polynomials(polynomials); @@ -346,12 +379,7 @@ mod tests { use ark_bls12_381::Bls12_381; use ark_ec::PairingEngine; use ark_poly::{DenseMultilinearExtension, MultilinearExtension}; - use ark_std::{ - rand::{Rng, RngCore}, - test_rng, - vec::Vec, - UniformRand, - }; + use ark_std::{rand::RngCore, test_rng, vec::Vec, UniformRand}; type E = Bls12_381; type Fr = ::Fr; @@ -426,7 +454,7 @@ mod tests { // normal polynomials let polys1: Vec<_> = (0..5) - .map(|_| DenseMultilinearExtension::rand(rng.gen_range(5..9), &mut rng)) + .map(|_| DenseMultilinearExtension::rand(4, &mut rng)) .collect(); test_multi_commit_helper(&uni_params, &polys1, &mut rng)?; diff --git a/pcs/src/util.rs b/pcs/src/util.rs index 478721c4..3aea839f 100644 --- a/pcs/src/util.rs +++ b/pcs/src/util.rs @@ -20,9 +20,11 @@ pub(crate) fn compute_w_circ_l( let timer = start_timer!(|| "compute W \\circ l"); if w.num_vars != l.len() { - return Err(PCSErrors::InvalidParameters( - "l's length does not match num_variables".to_string(), - )); + return Err(PCSErrors::InvalidParameters(format!( + "l's length ({}) does not match num_variables ({})", + l.len(), + w.num_vars(), + ))); } let mut res_eval: Vec = vec![]; diff --git a/poly-iop/src/lib.rs b/poly-iop/src/lib.rs index 1aa3224a..326dff1f 100644 --- a/poly-iop/src/lib.rs +++ b/poly-iop/src/lib.rs @@ -17,6 +17,7 @@ pub use perm_check::{ }; pub use sum_check::SumCheck; pub use transcript::IOPTranscript; +pub use utils::*; pub use virtual_poly::{VPAuxInfo, VirtualPolynomial}; pub use zero_check::ZeroCheck; diff --git a/poly-iop/src/utils.rs b/poly-iop/src/utils.rs index 274ee518..8d43b8b4 100644 --- a/poly-iop/src/utils.rs +++ b/poly-iop/src/utils.rs @@ -13,7 +13,7 @@ macro_rules! to_bytes { /// Decompose an integer into a binary vector in little endian. #[allow(dead_code)] -pub(crate) fn bit_decompose(input: u64, num_var: usize) -> Vec { +pub fn bit_decompose(input: u64, num_var: usize) -> Vec { let mut res = Vec::with_capacity(num_var); let mut i = input; for _ in 0..num_var { From 7c2eb33c8325673094ba173ef5e7e1088a28dc34 Mon Sep 17 00:00:00 2001 From: zhenfei Date: Wed, 29 Jun 2022 14:59:36 -0400 Subject: [PATCH 05/10] e2e finished --- pcs/benches/bench.rs | 2 +- pcs/src/commit.rs | 191 ++++++++++++++++++++++++------------------- pcs/src/lib.rs | 22 ++++- pcs/src/util.rs | 71 +++++++++++++++- 4 files changed, 194 insertions(+), 92 deletions(-) diff --git a/pcs/benches/bench.rs b/pcs/benches/bench.rs index b27e1b13..eb2f2749 100644 --- a/pcs/benches/bench.rs +++ b/pcs/benches/bench.rs @@ -65,7 +65,7 @@ fn bench_pcs() -> Result<(), PCSErrors> { { let start = Instant::now(); for _ in 0..repetition { - assert!(KZGMultilinearPC::verify(&vk, &com, &point, value, &proof)?); + assert!(KZGMultilinearPC::verify(&vk, &com, &point, &value, &proof)?); } println!( "KZG verify for {} variables: {} ns", diff --git a/pcs/src/commit.rs b/pcs/src/commit.rs index 74116fde..6aacf079 100644 --- a/pcs/src/commit.rs +++ b/pcs/src/commit.rs @@ -1,19 +1,17 @@ use crate::{ - util::compute_w_circ_l, KZGMultilinearPC, MultilinearCommitmentScheme, PCSErrors, ProverParam, - UniversalParams, VerifierParam, + util::{build_l, compute_w_circ_l, merge_polynomials}, + KZGMultilinearPC, MultilinearCommitmentScheme, PCSErrors, ProverParam, UniversalParams, + VerifierParam, }; use ark_ec::{ msm::{FixedBaseMSM, VariableBaseMSM}, AffineCurve, PairingEngine, ProjectiveCurve, }; use ark_ff::PrimeField; -use ark_poly::{ - DenseMultilinearExtension, EvaluationDomain, Evaluations, MultilinearExtension, Polynomial, - Radix2EvaluationDomain, -}; +use ark_poly::{MultilinearExtension, Polynomial}; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Read, SerializationError, Write}; use ark_std::{end_timer, log2, rand::RngCore, start_timer, vec::Vec, One, Zero}; -use poly_iop::{bit_decompose, IOPTranscript}; +use poly_iop::IOPTranscript; #[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug)] /// commitment @@ -83,7 +81,7 @@ impl MultilinearCommitmentScheme for KZGMultilinearPC { polys: &[impl MultilinearExtension], ) -> Result { let commit_timer = start_timer!(|| "commit"); - let poly = merge_polynomials(polys); + let poly = merge_polynomials(polys)?; let scalars: Vec<_> = poly .to_evaluations() @@ -174,22 +172,29 @@ impl MultilinearCommitmentScheme for KZGMultilinearPC { /// /// Returns an error if the lengths do not match. /// + /// Returns + /// - the proof, + /// - q(x), + /// - and a value which is merged MLE evaluated at the consolidated point. + /// /// Steps: /// 1. build `l(points)` which is a list of univariate polynomials that goes /// through the points /// 2. build MLE `w` which is the merge of all MLEs. /// 3. build `q(x)` which is a univariate polynomial `W circ l` - /// 4. output `q(x)`'s evaluations over `(1, omega,...)`, and put it into - /// transcript + /// 4. output `q(x)`' and put it into transcript /// 5. sample `r` from transcript /// 6. get a point `p := l(r)` /// 7. output an opening of `w` over point `p` + /// 8. output `w(p)` + #[allow(clippy::type_complexity)] + // TODO: remove after we KZG-commit q(x) fn multi_open( prover_param: &Self::ProverParam, polynomials: &[impl MultilinearExtension], points: &[&[E::Fr]], transcript: &mut IOPTranscript, - ) -> Result<(Self::Proof, Vec), PCSErrors> { + ) -> Result<(Self::Proof, Vec, E::Fr), PCSErrors> { let open_timer = start_timer!(|| "open"); if points.len() != polynomials.len() { @@ -214,57 +219,21 @@ impl MultilinearCommitmentScheme for KZGMultilinearPC { } } - let uni_degree = points.len() + 1; - let small_domain = match Radix2EvaluationDomain::::new(uni_degree) { - Some(p) => p, - None => { - return Err(PCSErrors::InvalidParameters( - "failed to build radix 2 domain".to_string(), - )) - }, - }; - let large_domain = match Radix2EvaluationDomain::::new(1 << get_nv(polynomials)) { - Some(p) => p, - None => { - return Err(PCSErrors::InvalidParameters( - "failed to build radix 2 domain".to_string(), - )) - }, - }; - let prefix_len = log2(polynomials.len()) as usize; - // 1. build `l(points)` which is a list of univariate polynomials that goes // through the points - let mut uni_polys = Vec::new(); - - // 1.1 build the indexes and the univariate polys that go through the indexes - let indexes: Vec> = (0..num_var) - .map(|x| bit_decompose(x as u64, prefix_len)) - .collect(); - for i in 0..prefix_len { - let eval: Vec = indexes.iter().map(|x| E::Fr::from(x[i])).collect(); - uni_polys.push(Evaluations::from_vec_and_domain(eval, small_domain).interpolate()) - } - - // 1.2 build the actual univariate polys that go through the points - for i in 0..num_var { - let eval: Vec = points.iter().map(|x| x[i]).collect(); - uni_polys.push(Evaluations::from_vec_and_domain(eval, small_domain).interpolate()) - } + let uni_polys = build_l(num_var, points)?; // 2. build MLE `w` which is the merge of all MLEs. - let merge_poly = merge_polynomials(polynomials); + let merge_poly = merge_polynomials(polynomials)?; // 3. build `q(x)` which is a univariate polynomial `W circ l` let q_x = compute_w_circ_l(&merge_poly, &uni_polys)?; - // 4. output `q(x)`'s evaluations over `(1, omega,...)`, and put it into - // transcript + // 4. output `q(x)`' and put it into transcript // // TODO: use KZG commit for q(x) - let evals = q_x.evaluate_over_domain(large_domain); - evals - .evals + // TODO: unwrap + q_x.coeffs .iter() .for_each(|x| transcript.append_field_element(b"q(x)", x).unwrap()); @@ -277,9 +246,11 @@ impl MultilinearCommitmentScheme for KZGMultilinearPC { // 7. output an opening of `w` over point `p` let opening = Self::open(prover_param, &merge_poly, &point)?; + // 8. output value that is `w` evaluated at `p` + let value = merge_poly.evaluate(&point).unwrap(); end_timer!(open_timer); - Ok((opening, evals.evals)) + Ok((opening, q_x.coeffs, value)) } /// Verifies that `value` is the evaluation at `x` of the polynomial @@ -292,7 +263,7 @@ impl MultilinearCommitmentScheme for KZGMultilinearPC { verifier_param: &Self::VerifierParam, commitment: &Self::Commitment, point: &[E::Fr], - value: E::Fr, + value: &E::Fr, proof: &Self::Proof, ) -> Result { let verify_timer = start_timer!(|| "verify"); @@ -331,7 +302,7 @@ impl MultilinearCommitmentScheme for KZGMultilinearPC { pairings.push(( E::G1Prepared::from( - (verifier_param.g.mul(value) - commitment.g_product.into_projective()) + (verifier_param.g.mul(*value) - commitment.g_product.into_projective()) .into_affine(), ), E::G2Prepared::from(verifier_param.h), @@ -343,38 +314,72 @@ impl MultilinearCommitmentScheme for KZGMultilinearPC { end_timer!(verify_timer); Ok(res) } -} -/// Return the number of variables that one need for an MLE to -/// batch the list of MLEs -#[inline] -fn get_nv(polynomials: &[impl MultilinearExtension]) -> usize { - polynomials.iter().map(|x| x.num_vars()).max().unwrap() + log2(polynomials.len()) as usize -} + /// Verifies that `value` is the evaluation at `x_i` of the polynomial + /// `poly_i` committed inside `comm`. + /// steps: + /// + /// 1. put `q(x)`'s evaluations over `(1, omega,...)` into transcript + /// 2. sample `r` from transcript + /// 3. build `l(points)` which is a list of univariate polynomials that goes + /// through the points + /// 4. get a point `p := l(r)` + /// 5. verifies `p` is verifies against proof + fn batch_verify( + verifier_param: &Self::VerifierParam, + multi_commitment: &Self::Commitment, + points: &[&[E::Fr]], + value: &E::Fr, + proof: &Self::Proof, + q_x_coeff: &[E::Fr], + transcript: &mut IOPTranscript, + ) -> Result { + let num_var = points[0].len(); -fn merge_polynomials( - polynomials: &[impl MultilinearExtension], -) -> DenseMultilinearExtension { - let individual_max = polynomials.iter().map(|x| x.num_vars()).max() - // safe unwrap since polynomials is not empty - .unwrap(); - let new_nv = get_nv(polynomials); - let mut scalars = vec![]; - for poly in polynomials.iter() { - let mut scalar = poly.to_evaluations(); - - if poly.num_vars() < individual_max { - scalar - .extend_from_slice(vec![F::zero(); (1 << individual_max) - scalar.len()].as_ref()); + for &point in points.iter().skip(1) { + if point.len() != num_var { + return Err(PCSErrors::InvalidParameters(format!( + "points do not have same num_vars ({} vs {})", + point.len(), + num_var, + ))); + } } - scalars.extend_from_slice(scalar.as_slice()); + if num_var + log2(points.len()) as usize != multi_commitment.nv { + return Err(PCSErrors::InvalidParameters(format!( + "points and multi_commitment do not have same num_vars ({} vs {})", + num_var + log2(points.len()) as usize, + num_var, + ))); + } + + // TODO: verify commitment of `q(x)` instead of receiving full `q(x)` + + // 1. put `q(x)`'s evaluations over `(1, omega,...)` into transcript + // TODO: unwrap + q_x_coeff + .iter() + .for_each(|x| transcript.append_field_element(b"q(x)", x).unwrap()); + + // 2. sample `r` from transcript + let r = transcript.get_and_append_challenge(b"r")?; + + // 3. build `l(points)` which is a list of univariate polynomials that goes + // through the points + let uni_polys = build_l(num_var, points)?; + + // 4. get a point `p := l(r)` + let point: Vec = uni_polys.iter().map(|x| x.evaluate(&r)).collect(); + + // 5. verifies `p` is verifies against proof + Self::verify(verifier_param, multi_commitment, &point, value, proof) } - scalars.extend_from_slice(vec![F::zero(); (1 << new_nv) - scalars.len()].as_ref()); - DenseMultilinearExtension::from_evaluations_vec(new_nv, scalars) } #[cfg(test)] mod tests { + use crate::util::get_batched_nv; + use super::*; use ark_bls12_381::Bls12_381; use ark_ec::PairingEngine; @@ -396,10 +401,12 @@ mod tests { let proof = KZGMultilinearPC::open(&ck, poly, &point)?; let value = poly.evaluate(&point).unwrap(); - assert!(KZGMultilinearPC::verify(&vk, &com, &point, value, &proof)?); + assert!(KZGMultilinearPC::verify(&vk, &com, &point, &value, &proof)?); let value = Fr::rand(rng); - assert!(!KZGMultilinearPC::verify(&vk, &com, &point, value, &proof)?); + assert!(!KZGMultilinearPC::verify( + &vk, &com, &point, &value, &proof + )?); Ok(()) } @@ -428,8 +435,8 @@ mod tests { ) -> Result<(), PCSErrors> { let mut transcript = IOPTranscript::new(b"test"); - let nv = get_nv(polys); - let (ck, _vk) = uni_params.trim(nv)?; + let nv = get_batched_nv(polys[0].num_vars(), polys.len()); + let (ck, vk) = uni_params.trim(nv)?; let mut points = Vec::new(); for poly in polys.iter() { @@ -440,8 +447,20 @@ mod tests { } let points_ref: Vec<&[Fr]> = points.iter().map(|x| x.as_ref()).collect(); - let _com = KZGMultilinearPC::multi_commit(&ck, polys)?; - let _proof = KZGMultilinearPC::multi_open(&ck, polys, &points_ref, &mut transcript)?; + let com = KZGMultilinearPC::multi_commit(&ck, polys)?; + let (proof, q_x_coeff, value) = + KZGMultilinearPC::multi_open(&ck, polys, &points_ref, &mut transcript)?; + + let mut transcript = IOPTranscript::new(b"test"); + assert!(KZGMultilinearPC::batch_verify( + &vk, + &com, + &points_ref, + &value, + &proof, + &q_x_coeff, + &mut transcript + )?); Ok(()) } @@ -450,7 +469,7 @@ mod tests { fn test_multi_commit() -> Result<(), PCSErrors> { let mut rng = test_rng(); - let uni_params = KZGMultilinearPC::::setup(&mut rng, 20)?; + let uni_params = KZGMultilinearPC::::setup(&mut rng, 15)?; // normal polynomials let polys1: Vec<_> = (0..5) diff --git a/pcs/src/lib.rs b/pcs/src/lib.rs index b7c9ec87..6d13a6d1 100644 --- a/pcs/src/lib.rs +++ b/pcs/src/lib.rs @@ -50,14 +50,16 @@ pub trait MultilinearCommitmentScheme { point: &[E::Fr], ) -> Result; - /// On input a list of polynomials and a point `point`, outputs a proof for - /// the same. + /// Input a list of MLEs, and a same number of points, and a transcript, + /// compute a multi-opening for all the polynomials. + #[allow(clippy::type_complexity)] + // TODO: remove after we KZG-commit q(x) fn multi_open( prover_param: &Self::ProverParam, polynomials: &[impl MultilinearExtension], point: &[&[E::Fr]], transcript: &mut IOPTranscript, - ) -> Result<(Self::Proof, Vec), PCSErrors>; + ) -> Result<(Self::Proof, Vec, E::Fr), PCSErrors>; /// Verifies that `value` is the evaluation at `x` of the polynomial /// committed inside `comm`. @@ -65,7 +67,19 @@ pub trait MultilinearCommitmentScheme { verifier_param: &Self::VerifierParam, commitment: &Self::Commitment, point: &[E::Fr], - value: E::Fr, + value: &E::Fr, proof: &Self::Proof, ) -> Result; + + /// Verifies that `value_i` is the evaluation at `x_i` of the polynomial + /// `poly_i` committed inside `comm`. + fn batch_verify( + verifier_param: &Self::VerifierParam, + multi_commitment: &Self::Commitment, + points: &[&[E::Fr]], + values: &E::Fr, + proof: &Self::Proof, + q_x_coeff: &[E::Fr], + transcript: &mut IOPTranscript, + ) -> Result; } diff --git a/pcs/src/util.rs b/pcs/src/util.rs index 3aea839f..77d7b88e 100644 --- a/pcs/src/util.rs +++ b/pcs/src/util.rs @@ -6,7 +6,8 @@ use ark_poly::{ univariate::DensePolynomial, DenseMultilinearExtension, EvaluationDomain, Evaluations, MultilinearExtension, Polynomial, Radix2EvaluationDomain, }; -use ark_std::{end_timer, start_timer}; +use ark_std::{end_timer, log2, start_timer}; +use poly_iop::bit_decompose; /// Compute W \circ l. /// @@ -50,6 +51,74 @@ pub(crate) fn compute_w_circ_l( Ok(res) } +/// Return the number of variables that one need for an MLE to +/// batch the list of MLEs +#[inline] +pub(crate) fn get_batched_nv(num_var: usize, polynomials_len: usize) -> usize { + num_var + log2(polynomials_len) as usize +} + +/// merge a set of polynomials. Returns an error if the +/// polynomials do not share a same number of nvs. +pub(crate) fn merge_polynomials( + polynomials: &[impl MultilinearExtension], +) -> Result, PCSErrors> { + let nv = polynomials[0].num_vars(); + for poly in polynomials.iter() { + if nv != poly.num_vars() { + return Err(PCSErrors::InvalidParameters( + "num_vars do not match for polynomials".to_string(), + )); + } + } + + let merged_nv = get_batched_nv(nv, polynomials.len()); + let mut scalars = vec![]; + for poly in polynomials.iter() { + scalars.extend_from_slice(poly.to_evaluations().as_slice()); + } + scalars.extend_from_slice(vec![F::zero(); (1 << merged_nv) - scalars.len()].as_ref()); + Ok(DenseMultilinearExtension::from_evaluations_vec( + merged_nv, scalars, + )) +} + +/// Given a list of points, build `l(points)` which is a list of univariate +/// polynomials that goes through the points +pub(crate) fn build_l( + num_var: usize, + points: &[&[F]], +) -> Result>, PCSErrors> { + let prefix_len = log2(points.len()) as usize; + + let uni_degree = points.len(); + let small_domain = match Radix2EvaluationDomain::::new(uni_degree) { + Some(p) => p, + None => { + return Err(PCSErrors::InvalidParameters( + "failed to build radix 2 domain".to_string(), + )) + }, + }; + + let mut uni_polys = Vec::new(); + // 1.1 build the indexes and the univariate polys that go through the indexes + let indexes: Vec> = (0..num_var) + .map(|x| bit_decompose(x as u64, prefix_len)) + .collect(); + for i in 0..prefix_len { + let eval: Vec = indexes.iter().map(|x| F::from(x[i])).collect(); + uni_polys.push(Evaluations::from_vec_and_domain(eval, small_domain).interpolate()) + } + + // 1.2 build the actual univariate polys that go through the points + for i in 0..num_var { + let eval: Vec = points.iter().map(|x| x[i]).collect(); + uni_polys.push(Evaluations::from_vec_and_domain(eval, small_domain).interpolate()) + } + Ok(uni_polys) +} + #[cfg(test)] mod test { use super::*; From 65af56f385c315a0c6d370630d7a5ddee3927e00 Mon Sep 17 00:00:00 2001 From: zhenfei Date: Wed, 29 Jun 2022 18:24:33 -0400 Subject: [PATCH 06/10] partial address comments --- pcs/src/commit.rs | 102 +++++++++++++++++++++++++++++++++------------- pcs/src/lib.rs | 10 ++--- pcs/src/util.rs | 5 ++- 3 files changed, 82 insertions(+), 35 deletions(-) diff --git a/pcs/src/commit.rs b/pcs/src/commit.rs index 6aacf079..6c704c38 100644 --- a/pcs/src/commit.rs +++ b/pcs/src/commit.rs @@ -8,7 +8,7 @@ use ark_ec::{ AffineCurve, PairingEngine, ProjectiveCurve, }; use ark_ff::PrimeField; -use ark_poly::{MultilinearExtension, Polynomial}; +use ark_poly::{univariate::DensePolynomial, MultilinearExtension, Polynomial, UVPolynomial}; use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Read, SerializationError, Write}; use ark_std::{end_timer, log2, rand::RngCore, start_timer, vec::Vec, One, Zero}; use poly_iop::IOPTranscript; @@ -29,12 +29,30 @@ pub struct Proof { pub proofs: Vec, } +#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug)] +/// proof of batch opening +pub struct BatchProof { + /// The actual proof + pub proof: Proof, + /// The value which is `w` evaluated at `p:= l(r)`, where + /// - `w` is the merged MLE + /// - `l` is the list of univariate polys that goes through all points + /// - `r` is sampled from the transcript. + pub value: E::Fr, + /// Commitment to q(x) + // This is currently set to the entire coefficient list of q(x) + // TODO: replace me with a KZG commit + pub q_x_com: Vec, +} + impl MultilinearCommitmentScheme for KZGMultilinearPC { type ProverParam = ProverParam; type VerifierParam = VerifierParam; type SRS = UniversalParams; type Commitment = Commitment; type Proof = Proof; + type Transcript = IOPTranscript; + type BatchProof = BatchProof; /// Generate SRS from RNG. /// WARNING: THIS FUNCTION IS FOR TESTING PURPOSE ONLY. @@ -80,7 +98,7 @@ impl MultilinearCommitmentScheme for KZGMultilinearPC { prover_param: &Self::ProverParam, polys: &[impl MultilinearExtension], ) -> Result { - let commit_timer = start_timer!(|| "commit"); + let commit_timer = start_timer!(|| "multi commit"); let poly = merge_polynomials(polys)?; let scalars: Vec<_> = poly @@ -167,15 +185,26 @@ impl MultilinearCommitmentScheme for KZGMultilinearPC { Ok(Proof { proofs }) } - /// Input a list of MLEs, and a same number of points, and a transcript, - /// compute a multi-opening for all the polynomials + /// Input + /// - the prover parameters, + /// - a list of MLEs, + /// - and a same number of points, + /// - and a transcript, + /// compute a multi-opening for all the polynomials. + /// + /// For simplicity, this API requires each MLE to have only one point. If + /// the caller wish to use more than one points per MLE, it should be + /// handled at the caller layer. /// /// Returns an error if the lengths do not match. /// - /// Returns + /// Returns: /// - the proof, - /// - q(x), - /// - and a value which is merged MLE evaluated at the consolidated point. + /// - q(x), which is a univariate polynomial `w circ l` where `w` is the + /// merged MLE, and `l` is a list of polynomials that go through all the + /// points. TODO: change this field to a commitment to `q(x)` + /// - and a value which is `w` evaluated at `p:= l(r)` from some `r` from + /// the transcript. /// /// Steps: /// 1. build `l(points)` which is a list of univariate polynomials that goes @@ -187,15 +216,13 @@ impl MultilinearCommitmentScheme for KZGMultilinearPC { /// 6. get a point `p := l(r)` /// 7. output an opening of `w` over point `p` /// 8. output `w(p)` - #[allow(clippy::type_complexity)] - // TODO: remove after we KZG-commit q(x) fn multi_open( prover_param: &Self::ProverParam, polynomials: &[impl MultilinearExtension], points: &[&[E::Fr]], transcript: &mut IOPTranscript, - ) -> Result<(Self::Proof, Vec, E::Fr), PCSErrors> { - let open_timer = start_timer!(|| "open"); + ) -> Result { + let open_timer = start_timer!(|| "multi open"); if points.len() != polynomials.len() { return Err(PCSErrors::InvalidParameters( @@ -250,7 +277,11 @@ impl MultilinearCommitmentScheme for KZGMultilinearPC { let value = merge_poly.evaluate(&point).unwrap(); end_timer!(open_timer); - Ok((opening, q_x.coeffs, value)) + Ok(Self::BatchProof { + proof: opening, + q_x_com: q_x.coeffs, + value, + }) } /// Verifies that `value` is the evaluation at `x` of the polynomial @@ -321,19 +352,19 @@ impl MultilinearCommitmentScheme for KZGMultilinearPC { /// /// 1. put `q(x)`'s evaluations over `(1, omega,...)` into transcript /// 2. sample `r` from transcript - /// 3. build `l(points)` which is a list of univariate polynomials that goes + /// 3. check `q(r) == value` + /// 4. build `l(points)` which is a list of univariate polynomials that goes /// through the points - /// 4. get a point `p := l(r)` - /// 5. verifies `p` is verifies against proof + /// 5. get a point `p := l(r)` + /// 6. verifies `p` is verifies against proof fn batch_verify( verifier_param: &Self::VerifierParam, multi_commitment: &Self::Commitment, points: &[&[E::Fr]], - value: &E::Fr, - proof: &Self::Proof, - q_x_coeff: &[E::Fr], + batch_proof: &Self::BatchProof, transcript: &mut IOPTranscript, ) -> Result { + let verify_timer = start_timer!(|| "batch verify"); let num_var = points[0].len(); for &point in points.iter().skip(1) { @@ -357,22 +388,40 @@ impl MultilinearCommitmentScheme for KZGMultilinearPC { // 1. put `q(x)`'s evaluations over `(1, omega,...)` into transcript // TODO: unwrap - q_x_coeff + batch_proof + .q_x_com .iter() .for_each(|x| transcript.append_field_element(b"q(x)", x).unwrap()); // 2. sample `r` from transcript let r = transcript.get_and_append_challenge(b"r")?; - // 3. build `l(points)` which is a list of univariate polynomials that goes + // 3. check `q(r) == value` + let q_x = DensePolynomial::from_coefficients_slice(&batch_proof.q_x_com); + let q_r = q_x.evaluate(&r); + if q_r != batch_proof.value { + println!("univariate failed"); + return Ok(false); + } + + // 4. build `l(points)` which is a list of univariate polynomials that goes // through the points let uni_polys = build_l(num_var, points)?; - // 4. get a point `p := l(r)` + // 5. get a point `p := l(r)` let point: Vec = uni_polys.iter().map(|x| x.evaluate(&r)).collect(); - // 5. verifies `p` is verifies against proof - Self::verify(verifier_param, multi_commitment, &point, value, proof) + // 6. verifies `p` is verifies against proof + let res = Self::verify( + verifier_param, + multi_commitment, + &point, + &batch_proof.value, + &batch_proof.proof, + ); + end_timer!(verify_timer); + + res } } @@ -448,17 +497,14 @@ mod tests { let points_ref: Vec<&[Fr]> = points.iter().map(|x| x.as_ref()).collect(); let com = KZGMultilinearPC::multi_commit(&ck, polys)?; - let (proof, q_x_coeff, value) = - KZGMultilinearPC::multi_open(&ck, polys, &points_ref, &mut transcript)?; + let batch_proof = KZGMultilinearPC::multi_open(&ck, polys, &points_ref, &mut transcript)?; let mut transcript = IOPTranscript::new(b"test"); assert!(KZGMultilinearPC::batch_verify( &vk, &com, &points_ref, - &value, - &proof, - &q_x_coeff, + &batch_proof, &mut transcript )?); diff --git a/pcs/src/lib.rs b/pcs/src/lib.rs index 6d13a6d1..26634908 100644 --- a/pcs/src/lib.rs +++ b/pcs/src/lib.rs @@ -24,6 +24,8 @@ pub trait MultilinearCommitmentScheme { type SRS; type Commitment; type Proof; + type BatchProof; + type Transcript; /// Generate SRS from RNG. /// WARNING: THIS FUNCTION IS FOR TESTING PURPOSE ONLY. @@ -58,8 +60,8 @@ pub trait MultilinearCommitmentScheme { prover_param: &Self::ProverParam, polynomials: &[impl MultilinearExtension], point: &[&[E::Fr]], - transcript: &mut IOPTranscript, - ) -> Result<(Self::Proof, Vec, E::Fr), PCSErrors>; + transcript: &mut Self::Transcript, + ) -> Result; /// Verifies that `value` is the evaluation at `x` of the polynomial /// committed inside `comm`. @@ -77,9 +79,7 @@ pub trait MultilinearCommitmentScheme { verifier_param: &Self::VerifierParam, multi_commitment: &Self::Commitment, points: &[&[E::Fr]], - values: &E::Fr, - proof: &Self::Proof, - q_x_coeff: &[E::Fr], + batch_proof: &Self::BatchProof, transcript: &mut IOPTranscript, ) -> Result; } diff --git a/pcs/src/util.rs b/pcs/src/util.rs index 77d7b88e..11657e94 100644 --- a/pcs/src/util.rs +++ b/pcs/src/util.rs @@ -11,7 +11,8 @@ use poly_iop::bit_decompose; /// Compute W \circ l. /// -/// Given an MLE W, and a list of univariate polynomials l, evaluate W at l. +/// Given an MLE W, and a list of univariate polynomials l, generate the +/// univariate polynomial that composes W with l. /// /// Returns an error if l's length does not matches number of variables in W. pub(crate) fn compute_w_circ_l( @@ -103,7 +104,7 @@ pub(crate) fn build_l( let mut uni_polys = Vec::new(); // 1.1 build the indexes and the univariate polys that go through the indexes - let indexes: Vec> = (0..num_var) + let indexes: Vec> = (0..points.len()) .map(|x| bit_decompose(x as u64, prefix_len)) .collect(); for i in 0..prefix_len { From fafd5efe1f3a8ae43ee942f6d29b05ca72415a4e Mon Sep 17 00:00:00 2001 From: zhenfei Date: Thu, 30 Jun 2022 12:15:49 -0400 Subject: [PATCH 07/10] fix some bugs and add a bit more testing --- pcs/src/commit.rs | 19 ++- pcs/src/util.rs | 424 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 428 insertions(+), 15 deletions(-) diff --git a/pcs/src/commit.rs b/pcs/src/commit.rs index 6c704c38..8cbe13bb 100644 --- a/pcs/src/commit.rs +++ b/pcs/src/commit.rs @@ -273,8 +273,11 @@ impl MultilinearCommitmentScheme for KZGMultilinearPC { // 7. output an opening of `w` over point `p` let opening = Self::open(prover_param, &merge_poly, &point)?; - // 8. output value that is `w` evaluated at `p` + // 8. output value that is `w` evaluated at `p` (which should match `q(r)`) let value = merge_poly.evaluate(&point).unwrap(); + let value2 = q_x.evaluate(&r); + println!("{} {}", value, value2); + end_timer!(open_timer); Ok(Self::BatchProof { @@ -399,6 +402,8 @@ impl MultilinearCommitmentScheme for KZGMultilinearPC { // 3. check `q(r) == value` let q_x = DensePolynomial::from_coefficients_slice(&batch_proof.q_x_com); let q_r = q_x.evaluate(&r); + + println!("{} {}", batch_proof.value, q_r); if q_r != batch_proof.value { println!("univariate failed"); return Ok(false); @@ -518,16 +523,16 @@ mod tests { let uni_params = KZGMultilinearPC::::setup(&mut rng, 15)?; // normal polynomials - let polys1: Vec<_> = (0..5) + let polys1: Vec<_> = (0..2) .map(|_| DenseMultilinearExtension::rand(4, &mut rng)) .collect(); test_multi_commit_helper(&uni_params, &polys1, &mut rng)?; - // single-variate polynomials - let polys1: Vec<_> = (0..5) - .map(|_| DenseMultilinearExtension::rand(1, &mut rng)) - .collect(); - test_multi_commit_helper(&uni_params, &polys1, &mut rng)?; + // // single-variate polynomials + // let polys1: Vec<_> = (0..5) + // .map(|_| DenseMultilinearExtension::rand(1, &mut rng)) + // .collect(); + // test_multi_commit_helper(&uni_params, &polys1, &mut rng)?; Ok(()) } diff --git a/pcs/src/util.rs b/pcs/src/util.rs index 11657e94..c3b4f464 100644 --- a/pcs/src/util.rs +++ b/pcs/src/util.rs @@ -30,7 +30,11 @@ pub(crate) fn compute_w_circ_l( } let mut res_eval: Vec = vec![]; - let uni_degree = l.len() + 1; + + // TODO: consider to pass this in from caller + // uni_degree is (product of each prefix's) + (2 * MLEs) + // = (l.len() - (num_vars - log(l.len())) + 2) * l[0].degree + let uni_degree = (l.len() - w.num_vars + log2(l.len()) as usize + 2) * l[0].degree(); let domain = match Radix2EvaluationDomain::::new(uni_degree) { Some(p) => p, @@ -40,9 +44,10 @@ pub(crate) fn compute_w_circ_l( )) }, }; - for point in domain.elements() { - let l_eval: Vec = l.iter().map(|x| x.evaluate(&point)).collect(); + // we reverse the order here because the coefficient vec are stored in + // bit-reversed order + let l_eval: Vec = l.iter().rev().map(|x| x.evaluate(&point)).collect(); res_eval.push(w.evaluate(l_eval.as_ref()).unwrap()) } let evaluation = Evaluations::from_vec_and_domain(res_eval, domain); @@ -102,21 +107,34 @@ pub(crate) fn build_l( }, }; + // The following code print out the roots for testing + // println!("domain root0: {}", small_domain.element(0)); + // println!("domain root1: {}", small_domain.element(1)); + // println!("domain root2: {}", small_domain.element(2)); + // println!("domain root3: {}", small_domain.element(3)); + let mut uni_polys = Vec::new(); + // 1.1 build the indexes and the univariate polys that go through the indexes let indexes: Vec> = (0..points.len()) .map(|x| bit_decompose(x as u64, prefix_len)) .collect(); for i in 0..prefix_len { - let eval: Vec = indexes.iter().map(|x| F::from(x[i])).collect(); - uni_polys.push(Evaluations::from_vec_and_domain(eval, small_domain).interpolate()) + let eval: Vec = indexes + .iter() + .map(|x| F::from(x[prefix_len - i - 1])) + .collect(); + + uni_polys.push(Evaluations::from_vec_and_domain(eval.clone(), small_domain).interpolate()); } // 1.2 build the actual univariate polys that go through the points for i in 0..num_var { - let eval: Vec = points.iter().map(|x| x[i]).collect(); + let mut eval: Vec = points.iter().map(|x| x[i]).collect(); + eval.extend_from_slice(vec![F::zero(); small_domain.size as usize - eval.len()].as_slice()); uni_polys.push(Evaluations::from_vec_and_domain(eval, small_domain).interpolate()) } + Ok(uni_polys) } @@ -124,7 +142,9 @@ pub(crate) fn build_l( mod test { use super::*; use ark_bls12_381::Fr; + use ark_ff::field_new; use ark_poly::UVPolynomial; + use ark_std::{One, Zero}; #[test] fn test_w_circ_l() -> Result<(), PCSErrors> { @@ -148,7 +168,7 @@ mod test { let l1 = DensePolynomial::from_coefficients_vec(vec![F::from(4u64), -F::from(2u64)]); // res = -6t^2 - 4t + 32 - let res = compute_w_circ_l(&w, [l1, l0].as_ref())?; + let res = compute_w_circ_l(&w, [l0, l1].as_ref())?; let res_rec = DensePolynomial::from_coefficients_vec(vec![ F::from(32u64), -F::from(4u64), @@ -185,7 +205,7 @@ mod test { let l0 = DensePolynomial::from_coefficients_vec(vec![F::from(2u64), F::one()]); let l1 = DensePolynomial::from_coefficients_vec(vec![-F::from(4u64), F::from(3u64)]); let l2 = DensePolynomial::from_coefficients_vec(vec![F::from(6u64), -F::from(5u64)]); - let res = compute_w_circ_l(&w, [l2, l1, l0].as_ref())?; + let res = compute_w_circ_l(&w, [l0, l1, l2].as_ref())?; // res = -15t^3 - 23t^2 + 130t - 76 let res_rec = DensePolynomial::from_coefficients_vec(vec![ @@ -199,4 +219,392 @@ mod test { } Ok(()) } + + #[test] + fn test_merge_poly() -> Result<(), PCSErrors> { + test_merge_poly_helper::() + } + fn test_merge_poly_helper() -> Result<(), PCSErrors> { + // Example from page 53: + // W1 = 3x1x2 + 2x2 whose evaluations are + // 0, 0 |-> 0 + // 0, 1 |-> 2 + // 1, 0 |-> 0 + // 1, 1 |-> 5 + let w_eval = vec![F::zero(), F::from(2u64), F::zero(), F::from(5u64)]; + let w1 = DenseMultilinearExtension::from_evaluations_vec(2, w_eval); + + // W2 = x1x2 + x1 whose evaluations are + // 0, 0 |-> 0 + // 0, 1 |-> 0 + // 1, 0 |-> 1 + // 1, 1 |-> 2 + let w_eval = vec![F::zero(), F::zero(), F::from(1u64), F::from(2u64)]; + let w2 = DenseMultilinearExtension::from_evaluations_vec(2, w_eval); + + // W3 = x1 + x2 whose evaluations are + // 0, 0 |-> 0 + // 0, 1 |-> 1 + // 1, 0 |-> 1 + // 1, 1 |-> 2 + let w_eval = vec![F::zero(), F::one(), F::from(1u64), F::from(2u64)]; + let w3 = DenseMultilinearExtension::from_evaluations_vec(2, w_eval); + + { + // W = (3x1x2 + 2x2)(1-x0) + (x1x2 + x1)x0 + // = -2x0x1x2 + x0x1 - 2x0x2 + 3x1x2 + 2x2 + // with evaluation map + // + // x0 x1 x2 + // 0, 0, 0 |-> 0 + // 0, 0, 1 |-> 2 + // 0, 1, 0 |-> 0 + // 0, 1, 1 |-> 5 + // 1, 0, 0 |-> 0 + // 1, 0, 1 |-> 0 + // 1, 1, 0 |-> 1 + // 1, 1, 1 |-> 2 + // + let w = merge_polynomials(&[w1.clone(), w2.clone()])?; + // w is [0,2,0,5,0,0,1,2] + let w_eval = vec![ + F::zero(), + F::from(2u64), + F::zero(), + F::from(5u64), + F::zero(), + F::zero(), + F::from(1u64), + F::from(2u64), + ]; + let w_rec = DenseMultilinearExtension::from_evaluations_vec(3, w_eval); + + assert_eq!(w, w_rec); + } + + { + // W = (3x1x2 + 2x2) * (1-y1) * (1-y2) + // + (x1x2 + x1) * (1-y1) * y2 + // + (x1 + x2) * y1 * (1-y2) + // + // with evaluation map + // + // y1 y2 x1 x2 + // 0, 0, 0, 0 |-> 0 + // 0, 0, 0, 1 |-> 2 + // 0, 0, 1, 0 |-> 0 + // 0, 0, 1, 1 |-> 5 + // 0, 1, 0, 0 |-> 0 + // 0, 1, 0, 1 |-> 0 + // 0, 1, 1, 0 |-> 1 + // 0, 1, 1, 1 |-> 2 + // 1, 0, 0, 0 |-> 0 + // 1, 0, 0, 1 |-> 1 + // 1, 0, 1, 0 |-> 1 + // 1, 0, 1, 1 |-> 2 + // 1, 1, 0, 0 |-> 0 + // 1, 1, 0, 1 |-> 0 + // 1, 1, 1, 0 |-> 0 + // 1, 1, 1, 1 |-> 0 + // + let w = merge_polynomials(&[w1, w2, w3])?; + // w is [0,2,0,5,0,0,1,2, 0,1,1,2] + let w_eval = vec![ + F::zero(), + F::from(2u64), + F::zero(), + F::from(5u64), + F::zero(), + F::zero(), + F::from(1u64), + F::from(2u64), + F::zero(), + F::one(), + F::from(1u64), + F::from(2u64), + F::zero(), + F::zero(), + F::zero(), + F::zero(), + ]; + let w_rec = DenseMultilinearExtension::from_evaluations_vec(4, w_eval); + + assert_eq!(w, w_rec); + } + Ok(()) + } + + #[test] + fn test_build_l() -> Result<(), PCSErrors> { + test_build_l_helper::() + } + + fn test_build_l_helper() -> Result<(), PCSErrors> { + // point 1 is [1, 2] + let point1 = [Fr::from(1u64), Fr::from(2u64)]; + + // point 2 is [3, 4] + let point2 = [Fr::from(3u64), Fr::from(4u64)]; + + // point 3 is [5, 6] + let point3 = [Fr::from(5u64), Fr::from(6u64)]; + + { + let l = build_l(2, &[&point1, &point2])?; + + // roots: [1, -1] + // l0 = -1/2 * x + 1/2 + // l1 = -x + 2 + // l2 = -x + 3 + let l0 = DensePolynomial::from_coefficients_vec(vec![ + Fr::one() / Fr::from(2u64), + -Fr::one() / Fr::from(2u64), + ]); + let l1 = DensePolynomial::from_coefficients_vec(vec![Fr::from(2u64), -Fr::one()]); + let l2 = DensePolynomial::from_coefficients_vec(vec![Fr::from(3u64), -Fr::one()]); + + assert_eq!(l0, l[0], "l0 not equal"); + assert_eq!(l1, l[1], "l1 not equal"); + assert_eq!(l2, l[2], "l2 not equal"); + } + + { + let l = build_l(2, &[&point1, &point2, &point3])?; + + // sage: q = 52435875175126190479447740508185965837690552500527637822603658699938581184513 + // sage: P. = PolynomialRing(Zmod(q)) + // sage: root1 = 1 + // sage: root2 = 0x8D51CCCE760304D0EC030002760300000001000000000000 + // sage: root3 = -1 + // sage: root4 = -root2 + // Arkwork's code is a bit wired: it also interpolate (root4, 0) + // which returns a degree 3 polynomial, instead of degree 2 + + // ======================== + // l0: [0, 0, 1] + // ======================== + // sage: points = [(root1, 0), (root2, 0), (root3, 1), (root4, 0)] + // sage: P.lagrange_polynomial(points) + // 13108968793781547619861935127046491459422638125131909455650914674984645296128*x^3 + + // 39326906381344642859585805381139474378267914375395728366952744024953935888385*x^2 + + // 13108968793781547619861935127046491459422638125131909455650914674984645296128*x + + // 39326906381344642859585805381139474378267914375395728366952744024953935888385 + let l0 = DensePolynomial::from_coefficients_vec(vec![ + field_new!( + Fr, + "39326906381344642859585805381139474378267914375395728366952744024953935888385" + ), + field_new!( + Fr, + "13108968793781547619861935127046491459422638125131909455650914674984645296128" + ), + field_new!( + Fr, + "39326906381344642859585805381139474378267914375395728366952744024953935888385" + ), + field_new!( + Fr, + "13108968793781547619861935127046491459422638125131909455650914674984645296128" + ), + ]); + + // ======================== + // l1: [0, 1, 0] + // ======================== + // sage: points = [(root1, 0), (root2, 1), (root3, 0), (root4, 0)] + // sage: P.lagrange_polynomial(points) + // 866286206518413079694067382671935694567563117191340490752*x^3 + + // 13108968793781547619861935127046491459422638125131909455650914674984645296128*x^2 + + // 52435875175126190478581454301667552757996485117855702128036095582747240693761*x + + // 39326906381344642859585805381139474378267914375395728366952744024953935888385 + let l1 = DensePolynomial::from_coefficients_vec(vec![ + field_new!( + Fr, + "39326906381344642859585805381139474378267914375395728366952744024953935888385" + ), + field_new!( + Fr, + "52435875175126190478581454301667552757996485117855702128036095582747240693761" + ), + field_new!( + Fr, + "13108968793781547619861935127046491459422638125131909455650914674984645296128" + ), + field_new!( + Fr, + "866286206518413079694067382671935694567563117191340490752" + ), + ]); + + // ======================== + // l2: [1, 3, 5] + // ======================== + // sage: points = [(root1, 1), (root2, 3), (root3, 5), (root4, 0)] + // sage: P.lagrange_polynomial(points) + // 2598858619555239239082202148015807083702689351574021472255*x^3 + + // 13108968793781547619861935127046491459422638125131909455650914674984645296129*x^2 + + // 52435875175126190476848881888630726598608350352511830738900969348364559712256*x + + // 39326906381344642859585805381139474378267914375395728366952744024953935888387 + let l2 = DensePolynomial::from_coefficients_vec(vec![ + field_new!( + Fr, + "39326906381344642859585805381139474378267914375395728366952744024953935888387" + ), + field_new!( + Fr, + "52435875175126190476848881888630726598608350352511830738900969348364559712256" + ), + field_new!( + Fr, + "13108968793781547619861935127046491459422638125131909455650914674984645296129" + ), + field_new!( + Fr, + "2598858619555239239082202148015807083702689351574021472255" + ), + ]); + + // ======================== + // l3: [2, 4, 6] + // ======================== + // sage: points = [(root1, 2), (root2, 4), (root3, 6), (root4, 0)] + // sage: P.lagrange_polynomial(points) + // 3465144826073652318776269530687742778270252468765361963007*x^3 + + // x^2 + + // 52435875175126190475982595682112313518914282969839895044333406231173219221504*x + + // 3 + let l3 = DensePolynomial::from_coefficients_vec(vec![ + Fr::from(3u64), + field_new!( + Fr, + "52435875175126190475982595682112313518914282969839895044333406231173219221504" + ), + Fr::one(), + field_new!( + Fr, + "3465144826073652318776269530687742778270252468765361963007" + ), + ]); + + assert_eq!(l0, l[0], "l0 not equal"); + assert_eq!(l1, l[1], "l1 not equal"); + assert_eq!(l2, l[2], "l2 not equal"); + assert_eq!(l3, l[3], "l3 not equal"); + } + Ok(()) + } + + #[test] + fn test_qx() -> Result<(), PCSErrors> { + // Example from page 53: + // W1 = 3x1x2 + 2x2 + let w_eval = vec![Fr::zero(), Fr::from(2u64), Fr::zero(), Fr::from(5u64)]; + let w1 = DenseMultilinearExtension::from_evaluations_vec(2, w_eval); + + // W2 = x1x2 + x1 + let w_eval = vec![Fr::zero(), Fr::zero(), Fr::from(1u64), Fr::from(2u64)]; + let w2 = DenseMultilinearExtension::from_evaluations_vec(2, w_eval); + + // W3 = x1 + x2 + let w_eval = vec![Fr::zero(), Fr::one(), Fr::from(1u64), Fr::from(2u64)]; + let w3 = DenseMultilinearExtension::from_evaluations_vec(2, w_eval); + + let r = Fr::from(42u64); + + // point 1 is [1, 2] + let point1 = [Fr::from(1u64), Fr::from(2u64)]; + + // point 2 is [3, 4] + let point2 = [Fr::from(3u64), Fr::from(4u64)]; + + // point 3 is [5, 6] + let point3 = [Fr::from(5u64), Fr::from(6u64)]; + + { + // w = (3x1x2 + 2x2)(1-x0) + (x1x2 + x1)x0 + // with evaluations: [0,2,0,5,0,0,1,2] + let w = merge_polynomials(&[w1.clone(), w2.clone()])?; + + let l = build_l(2, &[&point1, &point2])?; + + // sage: P. = PolynomialRing(ZZ) + // sage: l0 = -1/2 * x + 1/2 + // sage: l1 = -x + 2 + // sage: l2 = -x + 3 + // sage: w = (3 * l1 * l2 + 2 * l2) * (1-l0) + (l1 * l2 + l1) * l0 + // sage: w + // x^3 - 7/2*x^2 - 7/2*x + 16 + // + // q(x) = x^3 - 7/2*x^2 - 7/2*x + 16 + let q_x = compute_w_circ_l(&w, &l)?; + + let point: Vec = l.iter().rev().map(|poly| poly.evaluate(&r)).collect(); + + assert_eq!( + q_x.evaluate(&r), + w.evaluate(&point).unwrap(), + "q(r) != w(l(r))" + ); + } + + { + // W = (3x1x2 + 2x2) * (1-y1) * (1-y2) + // + (x1x2 + x1) * (1-y1) * y2 + // + (x1 + x2) * y1 * (1-y2) + let w = merge_polynomials(&[w1, w2, w3])?; + + let l = build_l(2, &[&point1, &point2, &point3])?; + + // l0 = + // 13108968793781547619861935127046491459422638125131909455650914674984645296128*x^3 + + // 39326906381344642859585805381139474378267914375395728366952744024953935888385*x^2 + + // 13108968793781547619861935127046491459422638125131909455650914674984645296128*x + + // 39326906381344642859585805381139474378267914375395728366952744024953935888385 + // + // l1 = + // 866286206518413079694067382671935694567563117191340490752*x^3 + + // 13108968793781547619861935127046491459422638125131909455650914674984645296128*x^2 + + // 52435875175126190478581454301667552757996485117855702128036095582747240693761*x + + // 39326906381344642859585805381139474378267914375395728366952744024953935888385 + // + // l2 = + // 2598858619555239239082202148015807083702689351574021472255*x^3 + + // 13108968793781547619861935127046491459422638125131909455650914674984645296129*x^2 + + // 52435875175126190476848881888630726598608350352511830738900969348364559712256*x + + // 39326906381344642859585805381139474378267914375395728366952744024953935888387 + // + // l3 = + // 3465144826073652318776269530687742778270252468765361963007*x^3 + + // x^2 + + // 52435875175126190475982595682112313518914282969839895044333406231173219221504*x + + // 3 + // + // q_x = (3*l2*l3 + 2*l3) * (1-l0) *(1-l1) + // + (l2*l3+l2)*(1-l0)*l1 + // + (l2+l3)*l0*(1-l1) + // q_x(42) = 42675783400755005965526147011103024780845819057955866345013183657072368533932 + let q_x = compute_w_circ_l(&w, &l)?; + + let point: Vec = vec![ + l[3].evaluate(&r), + l[2].evaluate(&r), + l[1].evaluate(&r), + l[0].evaluate(&r), + ]; + + assert_eq!( + q_x.evaluate(&r), + field_new!( + Fr, + "42675783400755005965526147011103024780845819057955866345013183657072368533932" + ), + ); + assert_eq!( + q_x.evaluate(&r), + w.evaluate(&point).unwrap(), + "q(r) != w(l(r))" + ); + } + Ok(()) + } } From 7d9577950c56673a1aaedd01b099fb1aef060058 Mon Sep 17 00:00:00 2001 From: zhenfei Date: Thu, 30 Jun 2022 12:23:04 -0400 Subject: [PATCH 08/10] fix bugs in commitment --- pcs/src/commit.rs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/pcs/src/commit.rs b/pcs/src/commit.rs index 8cbe13bb..dc9b60dd 100644 --- a/pcs/src/commit.rs +++ b/pcs/src/commit.rs @@ -268,7 +268,11 @@ impl MultilinearCommitmentScheme for KZGMultilinearPC { let r = transcript.get_and_append_challenge(b"r")?; // 6. get a point `p := l(r)` - let point: Vec = uni_polys.iter().map(|poly| poly.evaluate(&r)).collect(); + let point: Vec = uni_polys + .iter() + .rev() + .map(|poly| poly.evaluate(&r)) + .collect(); // 7. output an opening of `w` over point `p` let opening = Self::open(prover_param, &merge_poly, &point)?; @@ -276,7 +280,12 @@ impl MultilinearCommitmentScheme for KZGMultilinearPC { // 8. output value that is `w` evaluated at `p` (which should match `q(r)`) let value = merge_poly.evaluate(&point).unwrap(); let value2 = q_x.evaluate(&r); - println!("{} {}", value, value2); + + if value != value2 { + return Err(PCSErrors::InvalidProver( + "Q(r) does not match W(l(r))".to_string(), + )); + } end_timer!(open_timer); @@ -402,10 +411,7 @@ impl MultilinearCommitmentScheme for KZGMultilinearPC { // 3. check `q(r) == value` let q_x = DensePolynomial::from_coefficients_slice(&batch_proof.q_x_com); let q_r = q_x.evaluate(&r); - - println!("{} {}", batch_proof.value, q_r); if q_r != batch_proof.value { - println!("univariate failed"); return Ok(false); } @@ -414,7 +420,7 @@ impl MultilinearCommitmentScheme for KZGMultilinearPC { let uni_polys = build_l(num_var, points)?; // 5. get a point `p := l(r)` - let point: Vec = uni_polys.iter().map(|x| x.evaluate(&r)).collect(); + let point: Vec = uni_polys.iter().rev().map(|x| x.evaluate(&r)).collect(); // 6. verifies `p` is verifies against proof let res = Self::verify( @@ -424,6 +430,7 @@ impl MultilinearCommitmentScheme for KZGMultilinearPC { &batch_proof.value, &batch_proof.proof, ); + println!("{:?}", res); end_timer!(verify_timer); res From 8f2cea08305635683866f108096d806f1f230c14 Mon Sep 17 00:00:00 2001 From: zhenfei Date: Thu, 30 Jun 2022 12:33:08 -0400 Subject: [PATCH 09/10] bad path tests --- pcs/src/commit.rs | 78 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 72 insertions(+), 6 deletions(-) diff --git a/pcs/src/commit.rs b/pcs/src/commit.rs index dc9b60dd..29e6c8a3 100644 --- a/pcs/src/commit.rs +++ b/pcs/src/commit.rs @@ -430,7 +430,6 @@ impl MultilinearCommitmentScheme for KZGMultilinearPC { &batch_proof.value, &batch_proof.proof, ); - println!("{:?}", res); end_timer!(verify_timer); res @@ -511,6 +510,7 @@ mod tests { let com = KZGMultilinearPC::multi_commit(&ck, polys)?; let batch_proof = KZGMultilinearPC::multi_open(&ck, polys, &points_ref, &mut transcript)?; + // good path let mut transcript = IOPTranscript::new(b"test"); assert!(KZGMultilinearPC::batch_verify( &vk, @@ -520,6 +520,72 @@ mod tests { &mut transcript )?); + // bad commitment + assert!(KZGMultilinearPC::batch_verify( + &vk, + &Commitment { + nv: 0, + g_product: ::G1Affine::default() + }, + &points_ref, + &batch_proof, + &mut transcript + ) + .is_err()); + + // bad points + let points_ref: Vec<&[Fr]> = points.iter().skip(1).map(|x| x.as_ref()).collect(); + assert!(KZGMultilinearPC::batch_verify( + &vk, + &com, + &points_ref, + &batch_proof, + &mut transcript + ) + .is_err()); + + // bad proof + assert!(KZGMultilinearPC::batch_verify( + &vk, + &com, + &points_ref, + &BatchProof { + proof: Proof { proofs: Vec::new() }, + value: batch_proof.value, + q_x_com: batch_proof.q_x_com.clone() + }, + &mut transcript + ) + .is_err()); + + // bad value + assert!(KZGMultilinearPC::batch_verify( + &vk, + &com, + &points_ref, + &BatchProof { + proof: batch_proof.proof.clone(), + value: Fr::one(), + q_x_com: batch_proof.q_x_com + }, + &mut transcript + ) + .is_err()); + + // bad q(x) commit + assert!(KZGMultilinearPC::batch_verify( + &vk, + &com, + &points_ref, + &BatchProof { + proof: batch_proof.proof, + value: batch_proof.value, + q_x_com: Vec::new() + }, + &mut transcript + ) + .is_err()); + Ok(()) } @@ -535,11 +601,11 @@ mod tests { .collect(); test_multi_commit_helper(&uni_params, &polys1, &mut rng)?; - // // single-variate polynomials - // let polys1: Vec<_> = (0..5) - // .map(|_| DenseMultilinearExtension::rand(1, &mut rng)) - // .collect(); - // test_multi_commit_helper(&uni_params, &polys1, &mut rng)?; + // single-variate polynomials + let polys1: Vec<_> = (0..5) + .map(|_| DenseMultilinearExtension::rand(1, &mut rng)) + .collect(); + test_multi_commit_helper(&uni_params, &polys1, &mut rng)?; Ok(()) } From eb6928460925e9533ad3cc22735afee99a1f19f1 Mon Sep 17 00:00:00 2001 From: zhenfei Date: Thu, 30 Jun 2022 12:36:36 -0400 Subject: [PATCH 10/10] minor clean up --- pcs/src/util.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pcs/src/util.rs b/pcs/src/util.rs index c3b4f464..39a74526 100644 --- a/pcs/src/util.rs +++ b/pcs/src/util.rs @@ -125,7 +125,7 @@ pub(crate) fn build_l( .map(|x| F::from(x[prefix_len - i - 1])) .collect(); - uni_polys.push(Evaluations::from_vec_and_domain(eval.clone(), small_domain).interpolate()); + uni_polys.push(Evaluations::from_vec_and_domain(eval, small_domain).interpolate()); } // 1.2 build the actual univariate polys that go through the points