diff --git a/Cargo.toml b/Cargo.toml index caf62a3..9467f58 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,6 @@ members = [ "arithmetic", "hyperplonk", - "pcs", "poly-iop", "transcript", ] diff --git a/hyperplonk/Cargo.toml b/hyperplonk/Cargo.toml index af09f7e..4a93241 100644 --- a/hyperplonk/Cargo.toml +++ b/hyperplonk/Cargo.toml @@ -7,7 +7,6 @@ edition = "2021" [dependencies] poly-iop = { path = "../poly-iop" } -pcs = { path = "../pcs" } ark-std = { version = "^0.3.0", default-features = false } ark-ec = { version = "^0.3.0", default-features = false } @@ -19,6 +18,7 @@ displaydoc = { version = "0.2.3", default-features = false } transcript = { path = "../transcript" } arithmetic = { path = "../arithmetic" } +jf-primitives = { git = "https://github.com/EspressoSystems/jellyfish" } [dev-dependencies] ark-bls12-381 = { version = "0.3.0", default-features = false, features = [ "curve" ] } @@ -36,12 +36,12 @@ parallel = [ "ark-poly/parallel", "ark-ec/parallel", "poly-iop/parallel", - "pcs/parallel", "arithmetic/parallel", + "jf-primitives/parallel", ] print-trace = [ "ark-std/print-trace", "poly-iop/print-trace", - "pcs/print-trace", "arithmetic/print-trace", + "jf-primitives/print-trace", ] \ No newline at end of file diff --git a/hyperplonk/src/errors.rs b/hyperplonk/src/errors.rs index 168d6f6..20caa25 100644 --- a/hyperplonk/src/errors.rs +++ b/hyperplonk/src/errors.rs @@ -4,7 +4,7 @@ use arithmetic::ArithErrors; use ark_serialize::SerializationError; use ark_std::string::String; use displaydoc::Display; -use pcs::prelude::PCSErrors; +use jf_primitives::pcs::prelude::PCSError; use poly_iop::prelude::PolyIOPErrors; use transcript::TranscriptErrors; @@ -24,7 +24,7 @@ pub enum HyperPlonkErrors { /// PolyIOP error {0} PolyIOPErrors(PolyIOPErrors), /// PCS error {0} - PCSErrors(PCSErrors), + PCSError(PCSError), /// Transcript error {0} TranscriptError(TranscriptErrors), /// Arithmetic Error: {0} @@ -43,9 +43,9 @@ impl From for HyperPlonkErrors { } } -impl From for HyperPlonkErrors { - fn from(e: PCSErrors) -> Self { - Self::PCSErrors(e) +impl From for HyperPlonkErrors { + fn from(e: PCSError) -> Self { + Self::PCSError(e) } } diff --git a/hyperplonk/src/lib.rs b/hyperplonk/src/lib.rs index f031a4a..fdd55ed 100644 --- a/hyperplonk/src/lib.rs +++ b/hyperplonk/src/lib.rs @@ -4,14 +4,17 @@ use crate::utils::{eval_f, prove_sanity_check}; use arithmetic::VPAuxInfo; use ark_ec::PairingEngine; use ark_poly::{DenseMultilinearExtension, MultilinearExtension}; -use ark_std::{end_timer, log2, start_timer, One, Zero}; +use ark_std::{ + borrow::Borrow, end_timer, log2, marker::PhantomData, rc::Rc, start_timer, One, Zero, +}; use errors::HyperPlonkErrors; -use pcs::prelude::{compute_qx_degree, merge_polynomials, PCSErrors, PolynomialCommitmentScheme}; +use jf_primitives::pcs::prelude::{ + compute_qx_degree, merge_polynomials, PCSError, PolynomialCommitmentScheme, +}; use poly_iop::{ prelude::{identity_permutation_mle, PermutationCheck, ZeroCheck}, PolyIOP, }; -use std::{marker::PhantomData, rc::Rc}; use structs::{HyperPlonkIndex, HyperPlonkProof, HyperPlonkProvingKey, HyperPlonkVerifyingKey}; use transcript::IOPTranscript; use utils::{build_f, gen_eval_point}; @@ -47,7 +50,7 @@ where /// polynomial commitments fn preprocess( index: &Self::Index, - pcs_srs: &PCS::SRS, + pcs_srs: impl Borrow, ) -> Result<(Self::ProvingKey, Self::VerifyingKey), HyperPlonkErrors>; /// Generate HyperPlonk SNARK proof. @@ -59,7 +62,7 @@ where /// Outputs: /// - The HyperPlonk SNARK proof. fn prove( - pk: &Self::ProvingKey, + pk: impl Borrow, pub_input: &[E::Fr], witnesses: &[WitnessColumn], ) -> Result; @@ -73,7 +76,7 @@ where /// Outputs: /// - Return a boolean on whether the verification is successful fn verify( - vk: &Self::VerifyingKey, + vk: impl Borrow, pub_input: &[E::Fr], proof: &Self::Proof, ) -> Result; @@ -99,7 +102,7 @@ where fn preprocess( index: &Self::Index, - pcs_srs: &PCS::SRS, + pcs_srs: impl Borrow, ) -> Result<(Self::ProvingKey, Self::VerifyingKey), HyperPlonkErrors> { let num_vars = index.params.nv; let log_num_witness_polys = index.params.log_n_wires; @@ -133,7 +136,7 @@ where let selector_com = selector_oracles .iter() .map(|poly| PCS::commit(&pcs_prover_param, poly)) - .collect::, PCSErrors>>()?; + .collect::, PCSError>>()?; Ok(( Self::ProvingKey { @@ -186,10 +189,11 @@ where /// /// TODO: this function is gigantic -- refactor it to smaller ones fn prove( - pk: &Self::ProvingKey, + pk: impl Borrow, pub_input: &[E::Fr], witnesses: &[WitnessColumn], ) -> Result { + let pk = pk.borrow(); let start = start_timer!(|| "hyperplonk proving"); let mut transcript = IOPTranscript::::new(b"hyperplonk"); @@ -219,7 +223,7 @@ where // transcript // ======================================================================= let step = start_timer!(|| "commit witnesses"); - let w_merged = merge_polynomials(&witness_polys)?; + let w_merged = Rc::new(merge_polynomials(&witness_polys)?); if w_merged.num_vars != merged_nv { return Err(HyperPlonkErrors::InvalidParameters(format!( "merged witness poly has a different num_vars ({}) from expected ({})", @@ -590,10 +594,11 @@ where /// - check zero check evaluations /// - public input consistency checks fn verify( - vk: &Self::VerifyingKey, + vk: impl Borrow, pub_input: &[E::Fr], proof: &Self::Proof, ) -> Result { + let vk = vk.borrow(); let start = start_timer!(|| "hyperplonk verification"); let mut transcript = IOPTranscript::::new(b"hyperplonk"); @@ -925,7 +930,7 @@ mod tests { }; use ark_bls12_381::Bls12_381; use ark_std::test_rng; - use pcs::prelude::KZGMultilinearPCS; + use jf_primitives::pcs::prelude::MultilinearKzgPCS; use poly_iop::prelude::random_permutation_mle; #[test] @@ -957,7 +962,7 @@ mod tests { gate_func: CustomizedGates, ) -> Result<(), HyperPlonkErrors> { let mut rng = test_rng(); - let pcs_srs = KZGMultilinearPCS::::gen_srs_for_testing(&mut rng, 15)?; + let pcs_srs = MultilinearKzgPCS::::gen_srs_for_testing(&mut rng, 15)?; let merged_nv = nv + log_n_wires; // generate index @@ -977,7 +982,7 @@ mod tests { }; // generate pk and vks - let (pk, vk) = as HyperPlonkSNARK>>::preprocess( + let (pk, vk) = as HyperPlonkSNARK>>::preprocess( &index, &pcs_srs, )?; @@ -999,13 +1004,13 @@ mod tests { let pi = w1.clone(); // generate a proof and verify - let proof = as HyperPlonkSNARK>>::prove( + let proof = as HyperPlonkSNARK>>::prove( &pk, &pi.0, &[w1.clone(), w2.clone()], )?; - let _verify = as HyperPlonkSNARK>>::verify( + let _verify = as HyperPlonkSNARK>>::verify( &vk, &pi.0, &proof, )?; @@ -1013,24 +1018,24 @@ mod tests { let rand_perm: Vec = random_permutation_mle(merged_nv, &mut rng) .evaluations .clone(); - let mut bad_index = index.clone(); + let mut bad_index = index; bad_index.permutation = rand_perm; // generate pk and vks - let (_, bad_vk) = as HyperPlonkSNARK>>::preprocess( + let (_, bad_vk) = as HyperPlonkSNARK>>::preprocess( &bad_index, &pcs_srs, )?; assert!( - as HyperPlonkSNARK>>::verify( + as HyperPlonkSNARK>>::verify( &bad_vk, &pi.0, &proof, ) .is_err() ); // bad path 2: wrong witness - let mut w1_bad = w1.clone(); + let mut w1_bad = w1; w1_bad.0[0] = E::Fr::one(); assert!( - as HyperPlonkSNARK>>::prove( + as HyperPlonkSNARK>>::prove( &pk, &pi.0, &[w1_bad, w2], diff --git a/hyperplonk/src/structs.rs b/hyperplonk/src/structs.rs index f22e48b..7b2ffb3 100644 --- a/hyperplonk/src/structs.rs +++ b/hyperplonk/src/structs.rs @@ -5,7 +5,7 @@ use ark_ec::PairingEngine; use ark_ff::PrimeField; use ark_poly::DenseMultilinearExtension; use ark_std::cmp::max; -use pcs::PolynomialCommitmentScheme; +use jf_primitives::pcs::PolynomialCommitmentScheme; use poly_iop::prelude::{PermutationCheck, ZeroCheck}; use std::rc::Rc; diff --git a/pcs/Cargo.toml b/pcs/Cargo.toml deleted file mode 100644 index 0347620..0000000 --- a/pcs/Cargo.toml +++ /dev/null @@ -1,40 +0,0 @@ -[package] -name = "pcs" -version = "0.1.0" -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 } -derivative = { version = "2", features = ["use_core"] } - -transcript = { path = "../transcript" } - -# 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 deleted file mode 100644 index 4642765..0000000 --- a/pcs/benches/bench.rs +++ /dev/null @@ -1,88 +0,0 @@ -use ark_bls12_381::{Bls12_381, Fr}; -use ark_ff::UniformRand; -use ark_poly::{DenseMultilinearExtension, MultilinearExtension}; -use ark_std::{rc::Rc, test_rng}; -use pcs::{ - prelude::{KZGMultilinearPCS, PCSErrors, PolynomialCommitmentScheme}, - StructuredReferenceString, -}; -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 = KZGMultilinearPCS::::gen_srs_for_testing(&mut rng, 18)?; - - for nv in 4..19 { - let repetition = if nv < 10 { - 100 - } else if nv < 20 { - 50 - } else { - 10 - }; - - let poly = Rc::new(DenseMultilinearExtension::rand(nv, &mut rng)); - let (ml_ck, ml_vk) = uni_params.0.trim(nv)?; - let (uni_ck, uni_vk) = uni_params.1.trim(nv)?; - let ck = (ml_ck, uni_ck); - let vk = (ml_vk, uni_vk); - - let point: Vec<_> = (0..nv).map(|_| Fr::rand(&mut rng)).collect(); - - // commit - let com = { - let start = Instant::now(); - for _ in 0..repetition { - let _commit = KZGMultilinearPCS::commit(&ck, &poly)?; - } - - println!( - "KZG commit for {} variables: {} ns", - nv, - start.elapsed().as_nanos() / repetition as u128 - ); - - KZGMultilinearPCS::commit(&ck, &poly)? - }; - - // open - let (proof, value) = { - let start = Instant::now(); - for _ in 0..repetition { - let _open = KZGMultilinearPCS::open(&ck, &poly, &point)?; - } - - println!( - "KZG open for {} variables: {} ns", - nv, - start.elapsed().as_nanos() / repetition as u128 - ); - KZGMultilinearPCS::open(&ck, &poly, &point)? - }; - - // verify - { - let start = Instant::now(); - for _ in 0..repetition { - assert!(KZGMultilinearPCS::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/readme.md b/pcs/readme.md deleted file mode 100644 index 8b461e7..0000000 --- a/pcs/readme.md +++ /dev/null @@ -1,6 +0,0 @@ -KZG based multilinear polynomial commitment ------ - -# Compiling features: -- `parallel`: use multi-threading when possible. -- `print-trace`: print out user friendly information about the running time for each micro component. \ No newline at end of file diff --git a/pcs/src/errors.rs b/pcs/src/errors.rs deleted file mode 100644 index b30d8c4..0000000 --- a/pcs/src/errors.rs +++ /dev/null @@ -1,35 +0,0 @@ -//! Error module. - -use ark_serialize::SerializationError; -use ark_std::string::String; -use displaydoc::Display; -use transcript::TranscriptErrors; - -/// 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} - SerializationErrors(SerializationError), - /// Transcript error {0} - TranscriptErrors(TranscriptErrors), -} - -impl From for PCSErrors { - fn from(e: ark_serialize::SerializationError) -> Self { - Self::SerializationErrors(e) - } -} - -impl From for PCSErrors { - fn from(e: TranscriptErrors) -> Self { - Self::TranscriptErrors(e) - } -} diff --git a/pcs/src/lib.rs b/pcs/src/lib.rs deleted file mode 100644 index 3c817ea..0000000 --- a/pcs/src/lib.rs +++ /dev/null @@ -1,135 +0,0 @@ -mod errors; -mod multilinear_kzg; -mod structs; -mod univariate_kzg; - -pub mod prelude; - -use ark_ec::PairingEngine; -use ark_serialize::CanonicalSerialize; -use ark_std::rand::RngCore; -use errors::PCSErrors; - -/// This trait defines APIs for polynomial commitment schemes. -/// Note that for our usage of PCS, we do not require the hiding property. -pub trait PolynomialCommitmentScheme { - // Parameters - type ProverParam; - type VerifierParam; - type SRS; - // Polynomial and its associated types - type Polynomial; - type Point; - type Evaluation; - // Commitments and proofs - type Commitment: CanonicalSerialize; - type BatchCommitment: CanonicalSerialize; - type Proof; - type BatchProof; - - /// Build SRS for testing. - /// - /// - For univariate polynomials, `log_size` is the log of maximum degree. - /// - For multilinear polynomials, `log_size` is the number of variables. - /// - /// WARNING: THIS FUNCTION IS FOR TESTING PURPOSE ONLY. - /// THE OUTPUT SRS SHOULD NOT BE USED IN PRODUCTION. - fn gen_srs_for_testing( - rng: &mut R, - log_size: usize, - ) -> Result; - - /// Trim the universal parameters to specialize the public parameters. - /// Input both `supported_log_degree` for univariate and - /// `supported_num_vars` for multilinear. - fn trim( - srs: &Self::SRS, - supported_log_degree: usize, - supported_num_vars: Option, - ) -> Result<(Self::ProverParam, Self::VerifierParam), PCSErrors>; - - /// Generate a commitment for a polynomial - fn commit( - prover_param: &Self::ProverParam, - poly: &Self::Polynomial, - ) -> Result; - - /// Generate a commitment for a list of polynomials - fn multi_commit( - prover_param: &Self::ProverParam, - polys: &[Self::Polynomial], - ) -> Result; - - /// On input a polynomial `p` and a point `point`, outputs a proof for the - /// same. - fn open( - prover_param: &Self::ProverParam, - polynomial: &Self::Polynomial, - point: &Self::Point, - ) -> Result<(Self::Proof, Self::Evaluation), PCSErrors>; - - /// Input a list of MLEs, and a same number of points, and a transcript, - /// compute a multi-opening for all the polynomials. - fn multi_open( - prover_param: &Self::ProverParam, - multi_commitment: &Self::Commitment, - polynomials: &[Self::Polynomial], - points: &[Self::Point], - ) -> Result<(Self::BatchProof, Vec), PCSErrors>; - - /// Verifies that `value` is the evaluation at `x` of the polynomial - /// committed inside `comm`. - fn verify( - verifier_param: &Self::VerifierParam, - commitment: &Self::Commitment, - point: &Self::Point, - 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::BatchCommitment, - points: &[Self::Point], - values: &[E::Fr], - batch_proof: &Self::BatchProof, - rng: &mut R, - ) -> Result; -} - -/// API definitions for structured reference string -pub trait StructuredReferenceString: Sized { - type ProverParam; - type VerifierParam; - - /// Extract the prover parameters from the public parameters. - fn extract_prover_param(&self, supported_log_size: usize) -> Self::ProverParam; - /// Extract the verifier parameters from the public parameters. - fn extract_verifier_param(&self, supported_log_size: usize) -> Self::VerifierParam; - - /// Trim the universal parameters to specialize the public parameters - /// for polynomials to the given `supported_log_size`, and - /// returns committer key and verifier key. - /// - /// - For univariate polynomials, `supported_log_size` is the log of maximum - /// degree. - /// - For multilinear polynomials, `supported_log_size` is the number of - /// variables. - /// - /// `supported_log_size` should be in range `1..=params.log_size` - fn trim( - &self, - supported_log_size: usize, - ) -> Result<(Self::ProverParam, Self::VerifierParam), PCSErrors>; - - /// Build SRS for testing. - /// - /// - For univariate polynomials, `log_size` is the log of maximum degree. - /// - For multilinear polynomials, `log_size` is the number of variables. - /// - /// WARNING: THIS FUNCTION IS FOR TESTING PURPOSE ONLY. - /// THE OUTPUT SRS SHOULD NOT BE USED IN PRODUCTION. - fn gen_srs_for_testing(rng: &mut R, log_size: usize) -> Result; -} diff --git a/pcs/src/multilinear_kzg/batching.rs b/pcs/src/multilinear_kzg/batching.rs deleted file mode 100644 index 8d459c9..0000000 --- a/pcs/src/multilinear_kzg/batching.rs +++ /dev/null @@ -1,433 +0,0 @@ -use super::{ - open_internal, - srs::{MultilinearProverParam, MultilinearVerifierParam}, - util::{build_l, compute_w_circ_l, merge_polynomials}, - verify_internal, BatchProof, -}; -use crate::{ - multilinear_kzg::util::get_uni_domain, - prelude::{Commitment, UnivariateProverParam, UnivariateVerifierParam}, - univariate_kzg::KZGUnivariatePCS, - PCSErrors, PolynomialCommitmentScheme, -}; -use ark_ec::PairingEngine; -use ark_poly::{DenseMultilinearExtension, EvaluationDomain, MultilinearExtension, Polynomial}; -use ark_std::{end_timer, rc::Rc, start_timer, vec::Vec}; -use transcript::IOPTranscript; - -/// Input -/// - the prover parameters for univariate KZG, -/// - the prover parameters for multilinear KZG, -/// - a list of MLEs, -/// - a commitment to all MLEs -/// - and a same number of points, -/// 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 the proof, consists of -/// - the multilinear KZG opening -/// - the univariate KZG commitment to q(x) -/// - the openings and evaluations of q(x) at omega^i and r -/// -/// 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. commit to q(x) and sample r from transcript -/// transcript contains: w commitment, points, q(x)'s commitment -/// 5. build q(omega^i) and their openings -/// 6. build q(r) and its opening -/// 7. get a point `p := l(r)` -/// 8. output an opening of `w` over point `p` -/// 9. output `w(p)` -pub(super) fn multi_open_internal( - uni_prover_param: &UnivariateProverParam, - ml_prover_param: &MultilinearProverParam, - polynomials: &[Rc>], - multi_commitment: &Commitment, - points: &[Vec], -) -> Result<(BatchProof, Vec), PCSErrors> { - let open_timer = start_timer!(|| "multi open"); - - // =================================== - // Sanity checks on inputs - // =================================== - let points_len = points.len(); - if points_len == 0 { - return Err(PCSErrors::InvalidParameters("points is empty".to_string())); - } - - if points_len != polynomials.len() { - return Err(PCSErrors::InvalidParameters( - "polynomial length does not match point length".to_string(), - )); - } - - 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 domain = get_uni_domain::(points_len)?; - - // 1. build `l(points)` which is a list of univariate polynomials that goes - // through the points - let uni_polys = build_l(num_var, points, &domain)?; - - // 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. commit to q(x) and sample r from transcript - // transcript contains: w commitment, points, q(x)'s commitment - let mut transcript = IOPTranscript::new(b"ml kzg"); - transcript.append_serializable_element(b"w", multi_commitment)?; - for point in points { - transcript.append_serializable_element(b"w", point)?; - } - - let q_x_commit = KZGUnivariatePCS::::commit(uni_prover_param, &q_x)?; - transcript.append_serializable_element(b"q(x)", &q_x_commit)?; - let r = transcript.get_and_append_challenge(b"r")?; - - // 5. build q(omega^i) and their openings - let mut q_x_opens = vec![]; - let mut q_x_evals = vec![]; - for i in 0..points_len { - let (q_x_open, q_x_eval) = - KZGUnivariatePCS::::open(uni_prover_param, &q_x, &domain.element(i))?; - q_x_opens.push(q_x_open); - q_x_evals.push(q_x_eval); - - // sanity check - let point: Vec = uni_polys - .iter() - .rev() - .map(|poly| poly.evaluate(&domain.element(i))) - .collect(); - let mle_eval = merge_poly.evaluate(&point).unwrap(); - if mle_eval != q_x_eval { - return Err(PCSErrors::InvalidProver( - "Q(omega) does not match W(l(omega))".to_string(), - )); - } - } - - // 6. build q(r) and its opening - let (q_x_open, q_r_value) = KZGUnivariatePCS::::open(uni_prover_param, &q_x, &r)?; - q_x_opens.push(q_x_open); - q_x_evals.push(q_r_value); - - // 7. get a point `p := l(r)` - let point: Vec = uni_polys - .iter() - .rev() - .map(|poly| poly.evaluate(&r)) - .collect(); - - // 8. output an opening of `w` over point `p` - let (mle_opening, mle_eval) = open_internal(ml_prover_param, &merge_poly, &point)?; - - // 9. output value that is `w` evaluated at `p` (which should match `q(r)`) - if mle_eval != q_r_value { - return Err(PCSErrors::InvalidProver( - "Q(r) does not match W(l(r))".to_string(), - )); - } - end_timer!(open_timer); - - Ok(( - BatchProof { - proof: mle_opening, - q_x_commit, - q_x_opens, - }, - q_x_evals, - )) -} - -/// Verifies that the `multi_commitment` is a valid commitment -/// to a list of MLEs for the given openings and evaluations in -/// the batch_proof. -/// -/// steps: -/// -/// 1. push w, points and q_com into transcript -/// 2. sample `r` from transcript -/// 3. check `q(r) == batch_proof.q_x_value.last` and -/// `q(omega^i) == batch_proof.q_x_value[i]` -/// 4. build `l(points)` which is a list of univariate -/// polynomials that goes through the points -/// 5. get a point `p := l(r)` -/// 6. verifies `p` is valid against multilinear KZG proof -pub(super) fn batch_verify_internal( - uni_verifier_param: &UnivariateVerifierParam, - ml_verifier_param: &MultilinearVerifierParam, - multi_commitment: &Commitment, - points: &[Vec], - values: &[E::Fr], - batch_proof: &BatchProof, -) -> Result { - let verify_timer = start_timer!(|| "batch verify"); - - // =================================== - // Sanity checks on inputs - // =================================== - let points_len = points.len(); - if points_len == 0 { - return Err(PCSErrors::InvalidParameters("points is empty".to_string())); - } - - // add one here because we also have q(r) and its opening - if points_len + 1 != batch_proof.q_x_opens.len() { - return Err(PCSErrors::InvalidParameters( - "openings length does not match point length".to_string(), - )); - } - - if points_len + 1 != values.len() { - return Err(PCSErrors::InvalidParameters( - "values length does not match point length".to_string(), - )); - } - - let num_var = points[0].len(); - 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, - ))); - } - } - - let domain = get_uni_domain::(points_len)?; - - // 1. push w, points and q_com into transcript - let mut transcript = IOPTranscript::new(b"ml kzg"); - transcript.append_serializable_element(b"w", multi_commitment)?; - for point in points { - transcript.append_serializable_element(b"w", point)?; - } - - transcript.append_serializable_element(b"q(x)", &batch_proof.q_x_commit)?; - - // 2. sample `r` from transcript - let r = transcript.get_and_append_challenge(b"r")?; - - // 3. check `q(r) == batch_proof.q_x_value.last` and `q(omega^i) = - // batch_proof.q_x_value[i]` - for (i, value) in values.iter().enumerate().take(points_len) { - if !KZGUnivariatePCS::verify( - uni_verifier_param, - &batch_proof.q_x_commit, - &domain.element(i), - value, - &batch_proof.q_x_opens[i], - )? { - #[cfg(debug_assertion)] - println!("q(omega^{}) verification failed", i); - return Ok(false); - } - } - - if !KZGUnivariatePCS::verify( - uni_verifier_param, - &batch_proof.q_x_commit, - &r, - &values[points_len], - &batch_proof.q_x_opens[points_len], - )? { - #[cfg(debug_assertion)] - println!("q(r) verification 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, &domain)?; - - // 5. get a point `p := l(r)` - let point: Vec = uni_polys.iter().rev().map(|x| x.evaluate(&r)).collect(); - - // 6. verifies `p` is valid against multilinear KZG proof - let res = verify_internal( - ml_verifier_param, - multi_commitment, - &point, - &values[points_len], - &batch_proof.proof, - )?; - - #[cfg(debug_assertion)] - if !res { - println!("multilinear KZG verification failed"); - } - - end_timer!(verify_timer); - - Ok(res) -} - -#[cfg(test)] -mod tests { - use super::{ - super::{util::get_batched_nv, *}, - *, - }; - use crate::{ - multilinear_kzg::util::{compute_qx_degree, generate_evaluations}, - prelude::UnivariateUniversalParams, - StructuredReferenceString, - }; - use ark_bls12_381::Bls12_381 as E; - use ark_ec::PairingEngine; - use ark_poly::{DenseMultilinearExtension, MultilinearExtension}; - use ark_std::{log2, rand::RngCore, test_rng, vec::Vec, UniformRand}; - type Fr = ::Fr; - - fn test_multi_commit_helper( - uni_params: &UnivariateUniversalParams, - ml_params: &MultilinearUniversalParams, - polys: &[Rc>], - rng: &mut R, - ) -> Result<(), PCSErrors> { - let merged_nv = get_batched_nv(polys[0].num_vars(), polys.len()); - let qx_degree = compute_qx_degree(merged_nv, polys.len()); - let log_qx_degree = log2(qx_degree) as usize; - - let (uni_ck, uni_vk) = uni_params.trim(log_qx_degree)?; - let (ml_ck, ml_vk) = ml_params.trim(merged_nv)?; - - let mut points = Vec::new(); - for poly in polys.iter() { - let point = (0..poly.num_vars()) - .map(|_| Fr::rand(rng)) - .collect::>(); - points.push(point); - } - - let evals = generate_evaluations(polys, &points)?; - - let com = KZGMultilinearPCS::multi_commit(&(ml_ck.clone(), uni_ck.clone()), polys)?; - let (batch_proof, evaluations) = - multi_open_internal(&uni_ck, &ml_ck, polys, &com, &points)?; - - for (a, b) in evals.iter().zip(evaluations.iter()) { - assert_eq!(a, b) - } - - // good path - assert!(batch_verify_internal( - &uni_vk, - &ml_vk, - &com, - &points, - &evaluations, - &batch_proof, - )?); - - // bad commitment - assert!(!batch_verify_internal( - &uni_vk, - &ml_vk, - &Commitment { - commitment: ::G1Affine::default() - }, - &points, - &evaluations, - &batch_proof, - )?); - - // bad points - assert!( - batch_verify_internal(&uni_vk, &ml_vk, &com, &points[1..], &[], &batch_proof,).is_err() - ); - - // bad proof - assert!(batch_verify_internal( - &uni_vk, - &ml_vk, - &com, - &points, - &evaluations, - &BatchProof { - proof: Proof { proofs: Vec::new() }, - q_x_commit: Commitment { - commitment: ::G1Affine::default() - }, - q_x_opens: vec![], - }, - ) - .is_err()); - - // bad value - let mut wrong_evals = evaluations.clone(); - wrong_evals[0] = Fr::default(); - assert!(!batch_verify_internal( - &uni_vk, - &ml_vk, - &com, - &points, - &wrong_evals, - &batch_proof - )?); - - // bad q(x) commit - let mut wrong_proof = batch_proof.clone(); - wrong_proof.q_x_commit = Commitment { - commitment: ::G1Affine::default(), - }; - assert!(!batch_verify_internal( - &uni_vk, - &ml_vk, - &com, - &points, - &evaluations, - &wrong_proof, - )?); - Ok(()) - } - - #[test] - fn test_multi_commit_internal() -> Result<(), PCSErrors> { - let mut rng = test_rng(); - - let uni_params = UnivariateUniversalParams::::gen_srs_for_testing(&mut rng, 15)?; - let ml_params = MultilinearUniversalParams::::gen_srs_for_testing(&mut rng, 15)?; - - // normal polynomials - let polys1: Vec<_> = (0..5) - .map(|_| Rc::new(DenseMultilinearExtension::rand(4, &mut rng))) - .collect(); - test_multi_commit_helper(&uni_params, &ml_params, &polys1, &mut rng)?; - - // single-variate polynomials - let polys1: Vec<_> = (0..5) - .map(|_| Rc::new(DenseMultilinearExtension::rand(1, &mut rng))) - .collect(); - test_multi_commit_helper(&uni_params, &ml_params, &polys1, &mut rng)?; - - Ok(()) - } -} diff --git a/pcs/src/multilinear_kzg/mod.rs b/pcs/src/multilinear_kzg/mod.rs deleted file mode 100644 index 4a54a00..0000000 --- a/pcs/src/multilinear_kzg/mod.rs +++ /dev/null @@ -1,480 +0,0 @@ -//! Main module for multilinear KZG commitment scheme - -mod batching; -pub(crate) mod srs; -pub(crate) mod util; - -use crate::{ - prelude::{ - Commitment, UnivariateProverParam, UnivariateUniversalParams, UnivariateVerifierParam, - }, - univariate_kzg::KZGUnivariateOpening, - PCSErrors, PolynomialCommitmentScheme, StructuredReferenceString, -}; -use ark_ec::{ - msm::{FixedBaseMSM, VariableBaseMSM}, - AffineCurve, PairingEngine, ProjectiveCurve, -}; -use ark_ff::PrimeField; -use ark_poly::{DenseMultilinearExtension, MultilinearExtension}; -use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Read, SerializationError, Write}; -use ark_std::{end_timer, rand::RngCore, rc::Rc, start_timer, vec::Vec, One, Zero}; -use batching::{batch_verify_internal, multi_open_internal}; -use srs::{MultilinearProverParam, MultilinearUniversalParams, MultilinearVerifierParam}; -use std::marker::PhantomData; -use util::merge_polynomials; - -/// KZG Polynomial Commitment Scheme on multilinear polynomials. -pub struct KZGMultilinearPCS { - #[doc(hidden)] - phantom: PhantomData, -} - -#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug)] -/// proof of opening -pub struct Proof { - /// Evaluation of quotients - pub proofs: Vec, -} - -#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug)] -/// proof of batch opening -pub struct BatchProof { - /// The actual proof - pub proof: Proof, - /// Commitment to q(x):= w(l(x)) where - /// - `w` is the merged MLE - /// - `l` is the list of univariate polys that goes through all points - pub q_x_commit: Commitment, - /// openings of q(x) at 1, omega, ..., and r - pub q_x_opens: Vec>, -} - -impl PolynomialCommitmentScheme for KZGMultilinearPCS { - // Parameters - type ProverParam = ( - MultilinearProverParam, - UnivariateProverParam, - ); - type VerifierParam = (MultilinearVerifierParam, UnivariateVerifierParam); - type SRS = (MultilinearUniversalParams, UnivariateUniversalParams); - // Polynomial and its associated types - type Polynomial = Rc>; - type Point = Vec; - type Evaluation = E::Fr; - // Commitments and proofs - type Commitment = Commitment; - type BatchCommitment = Commitment; - type Proof = Proof; - type BatchProof = BatchProof; - - /// Build SRS for testing. - /// - /// - For univariate polynomials, `log_size` is the log of maximum degree. - /// - For multilinear polynomials, `log_size` is the number of variables. - /// - /// WARNING: THIS FUNCTION IS FOR TESTING PURPOSE ONLY. - /// THE OUTPUT SRS SHOULD NOT BE USED IN PRODUCTION. - fn gen_srs_for_testing( - rng: &mut R, - log_size: usize, - ) -> Result { - Ok(( - MultilinearUniversalParams::::gen_srs_for_testing(rng, log_size)?, - UnivariateUniversalParams::::gen_srs_for_testing(rng, log_size)?, - )) - } - - /// Trim the universal parameters to specialize the public parameters. - /// Input both `supported_log_degree` for univariate and - /// `supported_num_vars` for multilinear. - fn trim( - srs: &Self::SRS, - supported_log_degree: usize, - supported_num_vars: Option, - ) -> Result<(Self::ProverParam, Self::VerifierParam), PCSErrors> { - let supported_num_vars = match supported_num_vars { - Some(p) => p, - None => { - return Err(PCSErrors::InvalidParameters( - "multilinear should receive a num_var param".to_string(), - )) - }, - }; - let (uni_ck, uni_vk) = srs.1.trim(supported_log_degree)?; - let (ml_ck, ml_vk) = srs.0.trim(supported_num_vars)?; - - Ok(((ml_ck, uni_ck), (ml_vk, uni_vk))) - } - - /// 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: &Self::Polynomial, - ) -> Result { - let commit_timer = start_timer!(|| "commit"); - if prover_param.0.num_vars < poly.num_vars { - return Err(PCSErrors::InvalidParameters(format!( - "Poly length ({}) exceeds param limit ({})", - poly.num_vars, prover_param.0.num_vars - ))); - } - let ignored = prover_param.0.num_vars - poly.num_vars; - let scalars: Vec<_> = poly - .to_evaluations() - .into_iter() - .map(|x| x.into_repr()) - .collect(); - let commitment = VariableBaseMSM::multi_scalar_mul( - &prover_param.0.powers_of_g[ignored].evals, - scalars.as_slice(), - ) - .into_affine(); - - end_timer!(commit_timer); - Ok(Commitment { commitment }) - } - - /// Generate a commitment for a list of polynomials. - /// - /// This function takes `2^(num_vars + log(polys.len())` number of scalar - /// multiplications over G1. - fn multi_commit( - prover_param: &Self::ProverParam, - polys: &[Self::Polynomial], - ) -> Result { - let commit_timer = start_timer!(|| "multi commit"); - let poly = merge_polynomials(polys)?; - - let scalars: Vec<_> = poly - .to_evaluations() - .iter() - .map(|x| x.into_repr()) - .collect(); - - let commitment = VariableBaseMSM::multi_scalar_mul( - &prover_param.0.powers_of_g[0].evals, - scalars.as_slice(), - ) - .into_affine(); - - end_timer!(commit_timer); - Ok(Commitment { commitment }) - } - - /// 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 - /// G1: - /// - it prodceeds 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: &Self::Polynomial, - point: &Self::Point, - ) -> Result<(Self::Proof, Self::Evaluation), PCSErrors> { - open_internal(&prover_param.0, polynomial, point) - } - - /// Input - /// - the prover parameters for univariate KZG, - /// - the prover parameters for multilinear KZG, - /// - a list of MLEs, - /// - a commitment to all MLEs - /// - and a same number of points, - /// 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 the proof, consists of - /// - the multilinear KZG opening - /// - the univariate KZG commitment to q(x) - /// - the openings and evaluations of q(x) at omega^i and r - /// - /// 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. commit to q(x) and sample r from transcript - /// transcript contains: w commitment, points, q(x)'s commitment - /// 5. build q(omega^i) and their openings - /// 6. build q(r) and its opening - /// 7. get a point `p := l(r)` - /// 8. output an opening of `w` over point `p` - /// 9. output `w(p)` - fn multi_open( - prover_param: &Self::ProverParam, - multi_commitment: &Self::Commitment, - polynomials: &[Self::Polynomial], - points: &[Self::Point], - ) -> Result<(Self::BatchProof, Vec), PCSErrors> { - multi_open_internal::( - &prover_param.1, - &prover_param.0, - polynomials, - multi_commitment, - points, - ) - } - - /// 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: &Self::Point, - value: &E::Fr, - proof: &Self::Proof, - ) -> Result { - verify_internal(&verifier_param.0, commitment, point, value, proof) - } - - /// 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. check `q(r) == value` - /// 4. build `l(points)` which is a list of univariate polynomials that goes - /// through the points - /// 5. get a point `p := l(r)` - /// 6. verifies `p` is verifies against proof - fn batch_verify( - verifier_param: &Self::VerifierParam, - multi_commitment: &Self::BatchCommitment, - points: &[Self::Point], - values: &[E::Fr], - batch_proof: &Self::BatchProof, - _rng: &mut R, - ) -> Result { - batch_verify_internal( - &verifier_param.1, - &verifier_param.0, - multi_commitment, - points, - values, - batch_proof, - ) - } -} - -/// 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 -/// 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. -fn open_internal( - prover_param: &MultilinearProverParam, - polynomial: &DenseMultilinearExtension, - point: &[E::Fr], -) -> Result<(Proof, E::Fr), PCSErrors> { - let open_timer = start_timer!(|| format!("open mle with {} variable", polynomial.num_vars)); - - if polynomial.num_vars() > prover_param.num_vars { - return Err(PCSErrors::InvalidParameters(format!( - "Polynomial num_vars {} exceed the limit {}", - polynomial.num_vars, prover_param.num_vars - ))); - } - - if polynomial.num_vars() != point.len() { - return Err(PCSErrors::InvalidParameters(format!( - "Polynomial num_vars {} does not match point len {}", - polynomial.num_vars, - point.len() - ))); - } - - let nv = polynomial.num_vars(); - let ignored = prover_param.num_vars - nv; - 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, gi)) in point - .iter() - .zip(prover_param.powers_of_g[ignored..].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 G1 and is likely to be the bottleneck - proofs.push(VariableBaseMSM::multi_scalar_mul(&gi.evals, &scalars).into_affine()); - end_timer!(ith_round); - } - let eval = polynomial.evaluate(point).ok_or_else(|| { - PCSErrors::InvalidParameters("fail to evaluate the polynomial".to_string()) - })?; - end_timer!(open_timer); - Ok((Proof { proofs }, eval)) -} - -/// 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_internal( - verifier_param: &MultilinearVerifierParam, - commitment: &Commitment, - point: &[E::Fr], - value: &E::Fr, - proof: &Proof, -) -> Result { - let verify_timer = start_timer!(|| "verify"); - let num_var = point.len(); - - if num_var > verifier_param.num_vars { - return Err(PCSErrors::InvalidParameters(format!( - "point length ({}) exceeds param limit ({})", - num_var, verifier_param.num_vars - ))); - } - - let ignored = verifier_param.num_vars - num_var; - 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(num_var); - - let h_table = FixedBaseMSM::get_window_table( - scalar_size, - window_size, - verifier_param.h.into_projective(), - ); - let h_mul: Vec = - FixedBaseMSM::multi_scalar_mul(scalar_size, window_size, &h_table, point); - - let h_vec: Vec<_> = (0..num_var) - .map(|i| verifier_param.h_mask[ignored + i].into_projective() - h_mul[i]) - .collect(); - let h_vec: Vec = E::G2Projective::batch_normalization_into_affine(&h_vec); - end_timer!(prepare_inputs_timer); - - let pairing_product_timer = start_timer!(|| "pairing product"); - - let mut pairings: Vec<_> = proof - .proofs - .iter() - .map(|&x| E::G1Prepared::from(x)) - .zip(h_vec.into_iter().take(num_var).map(E::G2Prepared::from)) - .collect(); - - pairings.push(( - E::G1Prepared::from( - (verifier_param.g.mul(*value) - commitment.commitment.into_projective()).into_affine(), - ), - 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}; - use ark_std::{rand::RngCore, test_rng, vec::Vec, UniformRand}; - type E = Bls12_381; - type Fr = ::Fr; - - fn test_single_helper( - params: &(MultilinearUniversalParams, UnivariateUniversalParams), - poly: &Rc>, - rng: &mut R, - ) -> Result<(), PCSErrors> { - let nv = poly.num_vars(); - assert_ne!(nv, 0); - let uni_degree = 1; - let (ck, vk) = KZGMultilinearPCS::trim(params, uni_degree, Some(nv + 1))?; - let point: Vec<_> = (0..nv).map(|_| Fr::rand(rng)).collect(); - let com = KZGMultilinearPCS::commit(&ck, poly)?; - let (proof, value) = KZGMultilinearPCS::open(&ck, poly, &point)?; - - assert!(KZGMultilinearPCS::verify( - &vk, &com, &point, &value, &proof - )?); - - let value = Fr::rand(rng); - assert!(!KZGMultilinearPCS::verify( - &vk, &com, &point, &value, &proof - )?); - - Ok(()) - } - - #[test] - fn test_single_commit() -> Result<(), PCSErrors> { - let mut rng = test_rng(); - - let params = KZGMultilinearPCS::::gen_srs_for_testing(&mut rng, 10)?; - - // normal polynomials - let poly1 = Rc::new(DenseMultilinearExtension::rand(8, &mut rng)); - test_single_helper(¶ms, &poly1, &mut rng)?; - - // single-variate polynomials - let poly2 = Rc::new(DenseMultilinearExtension::rand(1, &mut rng)); - test_single_helper(¶ms, &poly2, &mut rng)?; - - Ok(()) - } - - #[test] - fn setup_commit_verify_constant_polynomial() { - let mut rng = test_rng(); - - // normal polynomials - assert!(KZGMultilinearPCS::::gen_srs_for_testing(&mut rng, 0).is_err()); - } -} diff --git a/pcs/src/multilinear_kzg/srs.rs b/pcs/src/multilinear_kzg/srs.rs deleted file mode 100644 index 7e7a93c..0000000 --- a/pcs/src/multilinear_kzg/srs.rs +++ /dev/null @@ -1,258 +0,0 @@ -//! Implementing Structured Reference Strings for multilinear polynomial KZG - -use crate::{prelude::PCSErrors, StructuredReferenceString}; -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}; -use std::collections::LinkedList; - -/// Evaluations over {0,1}^n for G1 or G2 -#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug)] -pub struct Evaluations { - /// The evaluations. - pub evals: Vec, -} - -/// Universal Parameter -#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug)] -pub struct MultilinearUniversalParams { - /// prover parameters - pub prover_param: MultilinearProverParam, - /// h^randomness: h^t1, h^t2, ..., **h^{t_nv}** - pub h_mask: Vec, -} - -/// Prover Parameters -#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug)] -pub struct MultilinearProverParam { - /// 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>, - /// generator for G1 - pub g: E::G1Affine, - /// generator for G2 - pub h: E::G2Affine, -} - -/// Verifier Parameters -#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug)] -pub struct MultilinearVerifierParam { - /// number of variables - pub num_vars: usize, - /// generator of G1 - pub g: E::G1Affine, - /// generator of G2 - pub h: E::G2Affine, - /// h^randomness: h^t1, h^t2, ..., **h^{t_nv}** - pub h_mask: Vec, -} - -impl StructuredReferenceString for MultilinearUniversalParams { - type ProverParam = MultilinearProverParam; - type VerifierParam = MultilinearVerifierParam; - - /// Extract the prover parameters from the public parameters. - fn extract_prover_param(&self, supported_num_vars: usize) -> Self::ProverParam { - let to_reduce = self.prover_param.num_vars - supported_num_vars; - - Self::ProverParam { - 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, - } - } - - /// Extract the verifier parameters from the public parameters. - fn extract_verifier_param(&self, supported_num_vars: usize) -> Self::VerifierParam { - let to_reduce = self.prover_param.num_vars - supported_num_vars; - Self::VerifierParam { - num_vars: supported_num_vars, - g: self.prover_param.g, - h: self.prover_param.h, - h_mask: self.h_mask[to_reduce..].to_vec(), - } - } - - /// 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` - fn trim( - &self, - supported_num_vars: usize, - ) -> Result<(Self::ProverParam, Self::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 = Self::ProverParam { - 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 = Self::VerifierParam { - num_vars: supported_num_vars, - g: self.prover_param.g, - h: self.prover_param.h, - h_mask: self.h_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. - 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 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 pp_g = E::G1Projective::batch_normalization_into_affine( - &FixedBaseMSM::multi_scalar_mul(scalar_bits, window_size, &g_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(), - }; - powers_of_g.push(pp_k_g); - start += size; - } - - let pp = Self::ProverParam { - num_vars, - g: g.into_affine(), - h: h.into_affine(), - powers_of_g, - }; - - end_timer!(pp_generation_timer); - - let vp_generation_timer = start_timer!(|| "VP generation"); - let h_mask = { - let window_size = FixedBaseMSM::get_mul_window_size(num_vars); - let h_table = FixedBaseMSM::get_window_table(scalar_bits, window_size, h); - E::G2Projective::batch_normalization_into_affine(&FixedBaseMSM::multi_scalar_mul( - scalar_bits, - window_size, - &h_table, - &t, - )) - }; - end_timer!(vp_generation_timer); - end_timer!(total_timer); - Ok(Self { - prover_param: pp, - h_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 _ = MultilinearUniversalParams::::gen_srs_for_testing(&mut rng, nv)?; - } - - Ok(()) - } -} diff --git a/pcs/src/multilinear_kzg/util.rs b/pcs/src/multilinear_kzg/util.rs deleted file mode 100644 index 3c5425e..0000000 --- a/pcs/src/multilinear_kzg/util.rs +++ /dev/null @@ -1,674 +0,0 @@ -//! 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, log2, rc::Rc, start_timer}; - -/// Decompose an integer into a binary vector in little endian. -#[allow(dead_code)] -pub(crate) 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 { - res.push(i & 1 == 1); - i >>= 1; - } - res -} - -/// For an MLE w with `mle_num_vars` variables, and `point_len` number of -/// points, compute the degree of the univariate polynomial `q(x):= w(l(x))` -/// where l(x) is a list of polynomials that go through all points. -// uni_degree is computed as `mle_num_vars * point_len`: -// - each l(x) is of degree `point_len` -// - mle has degree one -// - worst case is `\prod_{i=0}^{mle_num_vars-1} l_i(x) < point_len * mle_num_vars` -#[inline] -pub fn compute_qx_degree(mle_num_vars: usize, point_len: usize) -> usize { - mle_num_vars * point_len -} - -/// get the domain for the univariate polynomial -#[inline] -pub(crate) fn get_uni_domain( - uni_poly_degree: usize, -) -> Result, PCSErrors> { - let domain = match Radix2EvaluationDomain::::new(uni_poly_degree) { - Some(p) => p, - None => { - return Err(PCSErrors::InvalidParameters( - "failed to build radix 2 domain".to_string(), - )) - }, - }; - Ok(domain) -} - -/// Compute W \circ 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( - w: &DenseMultilinearExtension, - l: &[DensePolynomial], -) -> Result, PCSErrors> { - let timer = start_timer!(|| "compute W \\circ l"); - - if w.num_vars != l.len() { - return Err(PCSErrors::InvalidParameters(format!( - "l's length ({}) does not match num_variables ({})", - l.len(), - w.num_vars(), - ))); - } - - let mut res_eval: Vec = vec![]; - - // 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, - None => { - return Err(PCSErrors::InvalidParameters( - "failed to build radix 2 domain".to_string(), - )) - }, - }; - for point in domain.elements() { - // 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); - let res = evaluation.interpolate(); - - end_timer!(timer); - Ok(res) -} - -/// Return the number of variables that one need for an MLE to -/// batch the list of MLEs -#[inline] -pub 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 fn merge_polynomials( - polynomials: &[Rc>], -) -> 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(Rc::new(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: &[Vec], - domain: &Radix2EvaluationDomain, -) -> Result>, PCSErrors> { - let prefix_len = log2(points.len()) as usize; - 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[prefix_len - i - 1])) - .collect(); - - uni_polys.push(Evaluations::from_vec_and_domain(eval, *domain).interpolate()); - } - - // 1.2 build the actual univariate polys that go through the points - for i in 0..num_var { - let mut eval: Vec = points.iter().map(|x| x[i]).collect(); - eval.extend_from_slice(vec![F::zero(); domain.size as usize - eval.len()].as_slice()); - uni_polys.push(Evaluations::from_vec_and_domain(eval, *domain).interpolate()) - } - - Ok(uni_polys) -} - -/// Input a list of multilinear polynomials and a list of points, -/// generate a list of evaluations. -// Note that this function is only used for testing verifications. -// In practice verifier does not see polynomials, and the `mle_values` -// are included in the `batch_proof`. -#[cfg(test)] -pub(crate) fn generate_evaluations( - polynomials: &[Rc>], - points: &[Vec], -) -> Result, PCSErrors> { - if polynomials.len() != points.len() { - return Err(PCSErrors::InvalidParameters( - "polynomial length does not match point length".to_string(), - )); - } - - let num_var = polynomials[0].num_vars; - let uni_poly_degree = points.len(); - let merge_poly = merge_polynomials(polynomials)?; - - let domain = get_uni_domain::(uni_poly_degree)?; - let uni_polys = build_l(num_var, points, &domain)?; - let mut mle_values = vec![]; - - for i in 0..uni_poly_degree { - let point: Vec = uni_polys - .iter() - .rev() - .map(|poly| poly.evaluate(&domain.element(i))) - .collect(); - - let mle_value = merge_poly.evaluate(&point).unwrap(); - mle_values.push(mle_value) - } - Ok(mle_values) -} - -#[cfg(test)] -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> { - 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, [l0, l1].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, [l0, l1, l2].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(()) - } - - #[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 = Rc::new(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 = Rc::new(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 = Rc::new(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 = Rc::new(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 = Rc::new(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 = vec![Fr::from(1u64), Fr::from(2u64)]; - - // point 2 is [3, 4] - let point2 = vec![Fr::from(3u64), Fr::from(4u64)]; - - // point 3 is [5, 6] - let point3 = vec![Fr::from(5u64), Fr::from(6u64)]; - - { - let domain = get_uni_domain::(2)?; - let l = build_l(2, &[point1.clone(), point2.clone()], &domain)?; - - // 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 domain = get_uni_domain::(3)?; - let l = build_l(2, &[point1, point2, point3], &domain)?; - - // 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 = Rc::new(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 = Rc::new(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 = Rc::new(DenseMultilinearExtension::from_evaluations_vec(2, w_eval)); - - let r = Fr::from(42u64); - - // point 1 is [1, 2] - let point1 = vec![Fr::from(1u64), Fr::from(2u64)]; - - // point 2 is [3, 4] - let point2 = vec![Fr::from(3u64), Fr::from(4u64)]; - - // point 3 is [5, 6] - let point3 = vec![Fr::from(5u64), Fr::from(6u64)]; - - { - let domain = get_uni_domain::(2)?; - // 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.clone(), point2.clone()], &domain)?; - - // 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))" - ); - } - - { - let domain = get_uni_domain::(3)?; - // 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], &domain)?; - - // 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(()) - } -} diff --git a/pcs/src/prelude.rs b/pcs/src/prelude.rs deleted file mode 100644 index 373b96d..0000000 --- a/pcs/src/prelude.rs +++ /dev/null @@ -1,13 +0,0 @@ -pub use crate::{ - errors::PCSErrors, - multilinear_kzg::{ - srs::{MultilinearProverParam, MultilinearUniversalParams, MultilinearVerifierParam}, - util::{compute_qx_degree, get_batched_nv, merge_polynomials}, - BatchProof, KZGMultilinearPCS, Proof, - }, - structs::Commitment, - univariate_kzg::srs::{ - UnivariateProverParam, UnivariateUniversalParams, UnivariateVerifierParam, - }, - PolynomialCommitmentScheme, StructuredReferenceString, -}; diff --git a/pcs/src/structs.rs b/pcs/src/structs.rs deleted file mode 100644 index bd0dfd9..0000000 --- a/pcs/src/structs.rs +++ /dev/null @@ -1,9 +0,0 @@ -use ark_ec::PairingEngine; -use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Read, SerializationError, Write}; - -#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug, Default, PartialEq, Eq)] -/// A commitment is an Affine point. -pub struct Commitment { - /// the actual commitment is an affine point. - pub commitment: E::G1Affine, -} diff --git a/pcs/src/univariate_kzg/mod.rs b/pcs/src/univariate_kzg/mod.rs deleted file mode 100644 index 321785b..0000000 --- a/pcs/src/univariate_kzg/mod.rs +++ /dev/null @@ -1,400 +0,0 @@ -//! Main module for univariate KZG commitment scheme - -use crate::{ - prelude::{Commitment, PCSErrors}, - PolynomialCommitmentScheme, StructuredReferenceString, -}; -use ark_ec::{msm::VariableBaseMSM, AffineCurve, PairingEngine, ProjectiveCurve}; -use ark_ff::PrimeField; -use ark_poly::{univariate::DensePolynomial, Polynomial, UVPolynomial}; -use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Read, SerializationError, Write}; -use ark_std::{end_timer, rand::RngCore, start_timer, One, UniformRand, Zero}; -use srs::{UnivariateProverParam, UnivariateUniversalParams, UnivariateVerifierParam}; -use std::marker::PhantomData; - -pub(crate) mod srs; - -/// KZG Polynomial Commitment Scheme on univariate polynomial. -pub struct KZGUnivariatePCS { - #[doc(hidden)] - phantom: PhantomData, -} - -#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug)] -/// proof of opening -pub struct KZGUnivariateOpening { - /// Evaluation of quotients - pub proof: E::G1Affine, -} - -impl PolynomialCommitmentScheme for KZGUnivariatePCS { - // Parameters - type ProverParam = UnivariateProverParam; - type VerifierParam = UnivariateVerifierParam; - type SRS = UnivariateUniversalParams; - // Polynomial and its associated types - type Polynomial = DensePolynomial; - type Point = E::Fr; - type Evaluation = E::Fr; - // Polynomial and its associated types - type Commitment = Commitment; - type BatchCommitment = Vec; - type Proof = KZGUnivariateOpening; - type BatchProof = Vec; - - /// Build SRS for testing. - /// - /// - For univariate polynomials, `log_size` is the log of maximum degree. - /// - For multilinear polynomials, `log_size` is the number of variables. - /// - /// WARNING: THIS FUNCTION IS FOR TESTING PURPOSE ONLY. - /// THE OUTPUT SRS SHOULD NOT BE USED IN PRODUCTION. - fn gen_srs_for_testing( - rng: &mut R, - log_size: usize, - ) -> Result { - Self::SRS::gen_srs_for_testing(rng, log_size) - } - - /// Trim the universal parameters to specialize the public parameters. - /// Input `supported_log_degree` for univariate. - /// `supported_num_vars` must be None or an error is returned. - fn trim( - srs: &Self::SRS, - supported_log_degree: usize, - supported_num_vars: Option, - ) -> Result<(Self::ProverParam, Self::VerifierParam), PCSErrors> { - if supported_num_vars.is_some() { - return Err(PCSErrors::InvalidParameters( - "univariate should not receive a num_var param".to_string(), - )); - } - srs.trim(supported_log_degree) - } - - /// Generate a commitment for a polynomial - /// Note that the scheme is not hidding - fn commit( - prover_param: &Self::ProverParam, - poly: &Self::Polynomial, - ) -> Result { - let commit_time = - start_timer!(|| format!("Committing to polynomial of degree {} ", poly.degree())); - - if poly.degree() > prover_param.powers_of_g.len() { - return Err(PCSErrors::InvalidParameters(format!( - "poly degree {} is larger than allowed {}", - poly.degree(), - prover_param.powers_of_g.len() - ))); - } - - let (num_leading_zeros, plain_coeffs) = skip_leading_zeros_and_convert_to_bigints(poly); - - let msm_time = start_timer!(|| "MSM to compute commitment to plaintext poly"); - let commitment = VariableBaseMSM::multi_scalar_mul( - &prover_param.powers_of_g[num_leading_zeros..], - &plain_coeffs, - ) - .into_affine(); - end_timer!(msm_time); - - end_timer!(commit_time); - Ok(Commitment { commitment }) - } - - /// Generate a commitment for a list of polynomials - fn multi_commit( - prover_param: &Self::ProverParam, - polys: &[Self::Polynomial], - ) -> Result { - let commit_time = start_timer!(|| format!("batch commit {} polynomials", polys.len())); - let res = polys - .iter() - .map(|poly| Self::commit(prover_param, poly)) - .collect::, PCSErrors>>()?; - - end_timer!(commit_time); - Ok(res) - } - - /// On input a polynomial `p` and a point `point`, outputs a proof for the - /// same. - fn open( - prover_param: &Self::ProverParam, - polynomial: &Self::Polynomial, - point: &Self::Point, - ) -> Result<(Self::Proof, Self::Evaluation), PCSErrors> { - let open_time = - start_timer!(|| format!("Opening polynomial of degree {}", polynomial.degree())); - let divisor = Self::Polynomial::from_coefficients_vec(vec![-*point, E::Fr::one()]); - - let witness_time = start_timer!(|| "Computing witness polynomial"); - let witness_polynomial = polynomial / &divisor; - end_timer!(witness_time); - - let (num_leading_zeros, witness_coeffs) = - skip_leading_zeros_and_convert_to_bigints(&witness_polynomial); - - let proof = VariableBaseMSM::multi_scalar_mul( - &prover_param.powers_of_g[num_leading_zeros..], - &witness_coeffs, - ) - .into_affine(); - - let eval = polynomial.evaluate(point); - - end_timer!(open_time); - Ok((Self::Proof { proof }, eval)) - } - - /// Input a list of polynomials, and a same number of points, - /// compute a multi-opening for all the polynomials. - // This is a naive approach - // TODO: to implement the more efficient batch opening algorithm - // (e.g., the appendix C.4 in https://eprint.iacr.org/2020/1536.pdf) - fn multi_open( - prover_param: &Self::ProverParam, - _multi_commitment: &Self::Commitment, - polynomials: &[Self::Polynomial], - points: &[Self::Point], - ) -> Result<(Self::BatchProof, Vec), PCSErrors> { - let open_time = start_timer!(|| format!("batch opening {} polynomials", polynomials.len())); - if polynomials.len() != points.len() { - return Err(PCSErrors::InvalidParameters(format!( - "poly length {} is different from points length {}", - polynomials.len(), - points.len() - ))); - } - let mut batch_proof = vec![]; - let mut evals = vec![]; - for (poly, point) in polynomials.iter().zip(points.iter()) { - let (proof, eval) = Self::open(prover_param, poly, point)?; - batch_proof.push(proof); - evals.push(eval); - } - - end_timer!(open_time); - Ok((batch_proof, evals)) - } - /// Verifies that `value` is the evaluation at `x` of the polynomial - /// committed inside `comm`. - fn verify( - verifier_param: &Self::VerifierParam, - commitment: &Self::Commitment, - point: &Self::Point, - value: &E::Fr, - proof: &Self::Proof, - ) -> Result { - let check_time = start_timer!(|| "Checking evaluation"); - let pairing_inputs: Vec<(E::G1Prepared, E::G2Prepared)> = vec![ - ( - (verifier_param.g.mul(value.into_repr()) - - proof.proof.mul(point.into_repr()) - - commitment.commitment.into_projective()) - .into_affine() - .into(), - verifier_param.h.into(), - ), - (proof.proof.into(), verifier_param.beta_h.into()), - ]; - - let res = E::product_of_pairings(pairing_inputs.iter()).is_one(); - - end_timer!(check_time, || format!("Result: {}", res)); - Ok(res) - } - - /// Verifies that `value_i` is the evaluation at `x_i` of the polynomial - /// `poly_i` committed inside `comm`. - // This is a naive approach - // TODO: to implement the more efficient batch verification algorithm - // (e.g., the appendix C.4 in https://eprint.iacr.org/2020/1536.pdf) - fn batch_verify( - verifier_param: &Self::VerifierParam, - multi_commitment: &Self::BatchCommitment, - points: &[Self::Point], - values: &[E::Fr], - batch_proof: &Self::BatchProof, - rng: &mut R, - ) -> Result { - let check_time = - start_timer!(|| format!("Checking {} evaluation proofs", multi_commitment.len())); - - let mut total_c = ::zero(); - let mut total_w = ::zero(); - - let combination_time = start_timer!(|| "Combining commitments and proofs"); - let mut randomizer = E::Fr::one(); - // Instead of multiplying g and gamma_g in each turn, we simply accumulate - // their coefficients and perform a final multiplication at the end. - let mut g_multiplier = E::Fr::zero(); - for (((c, z), v), proof) in multi_commitment - .iter() - .zip(points) - .zip(values) - .zip(batch_proof) - { - let w = proof.proof; - let mut temp = w.mul(*z); - temp.add_assign_mixed(&c.commitment); - let c = temp; - g_multiplier += &(randomizer * v); - total_c += &c.mul(randomizer.into_repr()); - total_w += &w.mul(randomizer.into_repr()); - // We don't need to sample randomizers from the full field, - // only from 128-bit strings. - randomizer = u128::rand(rng).into(); - } - total_c -= &verifier_param.g.mul(g_multiplier); - end_timer!(combination_time); - - let to_affine_time = start_timer!(|| "Converting results to affine for pairing"); - let affine_points = E::G1Projective::batch_normalization_into_affine(&[-total_w, total_c]); - let (total_w, total_c) = (affine_points[0], affine_points[1]); - end_timer!(to_affine_time); - - let pairing_time = start_timer!(|| "Performing product of pairings"); - let result = E::product_of_pairings(&[ - (total_w.into(), verifier_param.beta_h.into()), - (total_c.into(), verifier_param.h.into()), - ]) - .is_one(); - end_timer!(pairing_time); - end_timer!(check_time, || format!("Result: {}", result)); - Ok(result) - } -} - -fn skip_leading_zeros_and_convert_to_bigints>( - p: &P, -) -> (usize, Vec) { - let mut num_leading_zeros = 0; - while num_leading_zeros < p.coeffs().len() && p.coeffs()[num_leading_zeros].is_zero() { - num_leading_zeros += 1; - } - let coeffs = convert_to_bigints(&p.coeffs()[num_leading_zeros..]); - (num_leading_zeros, coeffs) -} - -fn convert_to_bigints(p: &[F]) -> Vec { - let to_bigint_time = start_timer!(|| "Converting polynomial coeffs to bigints"); - let coeffs = p.iter().map(|s| s.into_repr()).collect::>(); - end_timer!(to_bigint_time); - coeffs -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::StructuredReferenceString; - use ark_bls12_381::Bls12_381; - use ark_ec::PairingEngine; - use ark_poly::univariate::DensePolynomial; - use ark_std::{log2, test_rng, UniformRand}; - - fn end_to_end_test_template() -> Result<(), PCSErrors> - where - E: PairingEngine, - { - let rng = &mut test_rng(); - for _ in 0..100 { - let mut degree = 0; - while degree <= 1 { - degree = usize::rand(rng) % 20; - } - let log_degree = log2(degree) as usize; - let pp = KZGUnivariatePCS::::gen_srs_for_testing(rng, log_degree)?; - let (ck, vk) = pp.trim(log_degree)?; - let p = as UVPolynomial>::rand(degree, rng); - let comm = KZGUnivariatePCS::::commit(&ck, &p)?; - let point = E::Fr::rand(rng); - let (proof, value) = KZGUnivariatePCS::::open(&ck, &p, &point)?; - assert!( - KZGUnivariatePCS::::verify(&vk, &comm, &point, &value, &proof)?, - "proof was incorrect for max_degree = {}, polynomial_degree = {}", - degree, - p.degree(), - ); - } - Ok(()) - } - - fn linear_polynomial_test_template() -> Result<(), PCSErrors> - where - E: PairingEngine, - { - let rng = &mut test_rng(); - for _ in 0..100 { - let degree = 50; - let log_degree = log2(degree) as usize; - - let pp = KZGUnivariatePCS::::gen_srs_for_testing(rng, log_degree)?; - let (ck, vk) = pp.trim(log_degree)?; - let p = as UVPolynomial>::rand(degree, rng); - let comm = KZGUnivariatePCS::::commit(&ck, &p)?; - let point = E::Fr::rand(rng); - let (proof, value) = KZGUnivariatePCS::::open(&ck, &p, &point)?; - assert!( - KZGUnivariatePCS::::verify(&vk, &comm, &point, &value, &proof)?, - "proof was incorrect for max_degree = {}, polynomial_degree = {}", - degree, - p.degree(), - ); - } - Ok(()) - } - - fn batch_check_test_template() -> Result<(), PCSErrors> - where - E: PairingEngine, - { - let rng = &mut test_rng(); - for _ in 0..10 { - let mut degree = 0; - while degree <= 1 { - degree = usize::rand(rng) % 20; - } - let log_degree = log2(degree) as usize; - let pp = KZGUnivariatePCS::::gen_srs_for_testing(rng, log_degree)?; - let (ck, vk) = KZGUnivariatePCS::::trim(&pp, log_degree, None)?; - let mut comms = Vec::new(); - let mut values = Vec::new(); - let mut points = Vec::new(); - let mut proofs = Vec::new(); - for _ in 0..10 { - let p = as UVPolynomial>::rand(degree, rng); - let comm = KZGUnivariatePCS::::commit(&ck, &p)?; - let point = E::Fr::rand(rng); - let (proof, value) = KZGUnivariatePCS::::open(&ck, &p, &point)?; - - assert!(KZGUnivariatePCS::::verify( - &vk, &comm, &point, &value, &proof - )?); - comms.push(comm); - values.push(value); - points.push(point); - proofs.push(proof); - } - assert!(KZGUnivariatePCS::::batch_verify( - &vk, &comms, &points, &values, &proofs, rng - )?); - } - Ok(()) - } - - #[test] - fn end_to_end_test() { - end_to_end_test_template::().expect("test failed for bls12-381"); - } - - #[test] - fn linear_polynomial_test() { - linear_polynomial_test_template::().expect("test failed for bls12-381"); - } - #[test] - fn batch_check_test() { - batch_check_test_template::().expect("test failed for bls12-381"); - } -} diff --git a/pcs/src/univariate_kzg/srs.rs b/pcs/src/univariate_kzg/srs.rs deleted file mode 100644 index e2791e4..0000000 --- a/pcs/src/univariate_kzg/srs.rs +++ /dev/null @@ -1,127 +0,0 @@ -//! Implementing Structured Reference Strings for univariate polynomial KZG - -use crate::{prelude::PCSErrors, StructuredReferenceString}; -use ark_ec::{msm::FixedBaseMSM, AffineCurve, PairingEngine, ProjectiveCurve}; -use ark_ff::PrimeField; -use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Read, SerializationError, Write}; -use ark_std::{end_timer, rand::RngCore, start_timer, One, UniformRand}; - -/// `UniversalParams` are the universal parameters for the KZG10 scheme. -/// Adapted from -/// https://github.com/arkworks-rs/poly-commit/blob/master/src/kzg10/data_structures.rs#L20 -#[derive(Clone, Debug)] -pub struct UnivariateUniversalParams { - /// Group elements of the form `{ \beta^i G }`, where `i` ranges from 0 to - /// `degree`. - pub powers_of_g: Vec, - /// The generator of G2. - pub h: E::G2Affine, - /// \beta times the above generator of G2. - pub beta_h: E::G2Affine, -} - -/// Prover Parameters -#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug)] -pub struct UnivariateProverParam { - pub powers_of_g: Vec, -} - -/// `VerifierKey` is used to check evaluation proofs for a given commitment. -/// https://github.com/arkworks-rs/poly-commit/blob/master/src/kzg10/data_structures.rs#L236 -#[derive(Clone, Debug)] -pub struct UnivariateVerifierParam { - /// The generator of G1. - pub g: E::G1Affine, - /// The generator of G2. - pub h: E::G2Affine, - /// \beta times the above generator of G2. - pub beta_h: E::G2Affine, -} - -impl StructuredReferenceString for UnivariateUniversalParams { - type ProverParam = UnivariateProverParam; - type VerifierParam = UnivariateVerifierParam; - - /// Extract the prover parameters from the public parameters. - fn extract_prover_param(&self, supported_log_size: usize) -> Self::ProverParam { - let support_size = 1usize << supported_log_size; - let powers_of_g = self.powers_of_g[..=support_size].to_vec(); - - Self::ProverParam { powers_of_g } - } - - /// Extract the verifier parameters from the public parameters. - fn extract_verifier_param(&self, _supported_log_size: usize) -> Self::VerifierParam { - Self::VerifierParam { - g: self.powers_of_g[0], - h: self.h, - beta_h: self.beta_h, - } - } - - /// Trim the universal parameters to specialize the public parameters - /// for univariate polynomials to the given `supported_log_size`, and - /// returns committer key and verifier key. `supported_log_size` should - /// be in range `1..log(params.len())` - fn trim( - &self, - supported_log_size: usize, - ) -> Result<(Self::ProverParam, Self::VerifierParam), PCSErrors> { - let support_size = 1usize << supported_log_size; - let powers_of_g = self.powers_of_g[..=support_size].to_vec(); - - let pk = Self::ProverParam { powers_of_g }; - let vk = Self::VerifierParam { - g: self.powers_of_g[0], - h: self.h, - beta_h: self.beta_h, - }; - Ok((pk, vk)) - } - - /// Build SRS for testing. - /// WARNING: THIS FUNCTION IS FOR TESTING PURPOSE ONLY. - /// THE OUTPUT SRS SHOULD NOT BE USED IN PRODUCTION. - fn gen_srs_for_testing(rng: &mut R, log_degree: usize) -> Result { - let max_degree = 1usize << log_degree; - let setup_time = start_timer!(|| format!("KZG10::Setup with degree {}", max_degree)); - let beta = E::Fr::rand(rng); - let g = E::G1Projective::rand(rng); - let h = E::G2Projective::rand(rng); - - let mut powers_of_beta = vec![E::Fr::one()]; - - let mut cur = beta; - for _ in 0..max_degree { - powers_of_beta.push(cur); - cur *= β - } - - let window_size = FixedBaseMSM::get_mul_window_size(max_degree + 1); - - let scalar_bits = E::Fr::size_in_bits(); - let g_time = start_timer!(|| "Generating powers of G"); - // TODO: parallelization - let g_table = FixedBaseMSM::get_window_table(scalar_bits, window_size, g); - let powers_of_g = FixedBaseMSM::multi_scalar_mul::( - scalar_bits, - window_size, - &g_table, - &powers_of_beta, - ); - end_timer!(g_time); - - let powers_of_g = E::G1Projective::batch_normalization_into_affine(&powers_of_g); - - let h = h.into_affine(); - let beta_h = h.mul(beta).into_affine(); - - let pp = Self { - powers_of_g, - h, - beta_h, - }; - end_timer!(setup_time); - Ok(pp) - } -} diff --git a/poly-iop/Cargo.toml b/poly-iop/Cargo.toml index e058c96..8d9fbfa 100644 --- a/poly-iop/Cargo.toml +++ b/poly-iop/Cargo.toml @@ -20,7 +20,8 @@ rayon = { version = "1.5.2", default-features = false, optional = true } transcript = { path = "../transcript" } arithmetic = { path = "../arithmetic" } -pcs = { path = "../pcs" } + +jf-primitives = { git = "https://github.com/EspressoSystems/jellyfish" } [dev-dependencies] ark-ec = { version = "^0.3.0", default-features = false } @@ -40,10 +41,10 @@ parallel = [ "ark-std/parallel", "ark-ff/parallel", "ark-poly/parallel", - "pcs/parallel", + "jf-primitives/parallel", ] print-trace = [ "arithmetic/print-trace", "ark-std/print-trace", - "pcs/print-trace", + "jf-primitives/print-trace", ] \ No newline at end of file diff --git a/poly-iop/benches/bench.rs b/poly-iop/benches/bench.rs index 0fa2c61..abee327 100644 --- a/poly-iop/benches/bench.rs +++ b/poly-iop/benches/bench.rs @@ -2,14 +2,14 @@ use arithmetic::{VPAuxInfo, VirtualPolynomial}; use ark_bls12_381::{Bls12_381, Fr}; use ark_poly::{DenseMultilinearExtension, MultilinearExtension}; use ark_std::test_rng; -use pcs::{prelude::KZGMultilinearPCS, PolynomialCommitmentScheme}; +use jf_primitives::pcs::{prelude::MultilinearKzgPCS, PolynomialCommitmentScheme}; use poly_iop::prelude::{ identity_permutation_mle, PermutationCheck, PolyIOP, PolyIOPErrors, ProductCheck, SumCheck, ZeroCheck, }; use std::{marker::PhantomData, rc::Rc, time::Instant}; -type KZG = KZGMultilinearPCS; +type Kzg = MultilinearKzgPCS; fn main() -> Result<(), PolyIOPErrors> { bench_permutation_check()?; @@ -140,8 +140,8 @@ fn bench_permutation_check() -> Result<(), PolyIOPErrors> { let mut rng = test_rng(); for nv in 4..20 { - let srs = KZG::gen_srs_for_testing(&mut rng, nv + 1)?; - let (pcs_param, _) = KZG::trim(&srs, nv + 1, Some(nv + 1))?; + let srs = Kzg::gen_srs_for_testing(&mut rng, nv + 1)?; + let (pcs_param, _) = Kzg::trim(&srs, nv + 1, Some(nv + 1))?; let repetition = if nv < 10 { 100 @@ -159,10 +159,10 @@ fn bench_permutation_check() -> Result<(), PolyIOPErrors> { let proof = { let start = Instant::now(); let mut transcript = - as PermutationCheck>::init_transcript(); + as PermutationCheck>::init_transcript(); transcript.append_message(b"testing", b"initializing transcript for testing")?; - let (proof, _q_x) = as PermutationCheck>::prove( + let (proof, _q_x) = as PermutationCheck>::prove( &pcs_param, &w, &w, @@ -187,9 +187,9 @@ fn bench_permutation_check() -> Result<(), PolyIOPErrors> { let start = Instant::now(); let mut transcript = - as PermutationCheck>::init_transcript(); + as PermutationCheck>::init_transcript(); transcript.append_message(b"testing", b"initializing transcript for testing")?; - let _perm_check_sum_claim = as PermutationCheck>::verify( + let _perm_check_sum_claim = as PermutationCheck>::verify( &proof, &poly_info, &mut transcript, @@ -211,8 +211,8 @@ fn bench_prod_check() -> Result<(), PolyIOPErrors> { let mut rng = test_rng(); for nv in 4..20 { - let srs = KZG::gen_srs_for_testing(&mut rng, nv + 1)?; - let (pcs_param, _) = KZG::trim(&srs, nv + 1, Some(nv + 1))?; + let srs = Kzg::gen_srs_for_testing(&mut rng, nv + 1)?; + let (pcs_param, _) = Kzg::trim(&srs, nv + 1, Some(nv + 1))?; let repetition = if nv < 10 { 100 @@ -230,10 +230,10 @@ fn bench_prod_check() -> Result<(), PolyIOPErrors> { let proof = { let start = Instant::now(); - let mut transcript = as ProductCheck>::init_transcript(); + let mut transcript = as ProductCheck>::init_transcript(); transcript.append_message(b"testing", b"initializing transcript for testing")?; - let (proof, _prod_x) = as ProductCheck>::prove( + let (proof, _prod_x) = as ProductCheck>::prove( &pcs_param, &f, &g, @@ -256,9 +256,9 @@ fn bench_prod_check() -> Result<(), PolyIOPErrors> { }; let start = Instant::now(); - let mut transcript = as ProductCheck>::init_transcript(); + let mut transcript = as ProductCheck>::init_transcript(); transcript.append_message(b"testing", b"initializing transcript for testing")?; - let _perm_check_sum_claim = as ProductCheck>::verify( + let _perm_check_sum_claim = as ProductCheck>::verify( &proof, &poly_info, &mut transcript, diff --git a/poly-iop/src/errors.rs b/poly-iop/src/errors.rs index 916d4cc..8ac4d4f 100644 --- a/poly-iop/src/errors.rs +++ b/poly-iop/src/errors.rs @@ -3,7 +3,7 @@ use arithmetic::ArithErrors; use ark_std::string::String; use displaydoc::Display; -use pcs::prelude::PCSErrors; +use jf_primitives::pcs::errors::PCSError; use transcript::TranscriptErrors; /// A `enum` specifying the possible failure modes of the PolyIOP. @@ -28,7 +28,7 @@ pub enum PolyIOPErrors { /// Arithmetic Error: {0} ArithmeticErrors(ArithErrors), /// PCS error {0} - PCSErrors(PCSErrors), + PCSError(PCSError), } impl From for PolyIOPErrors { @@ -49,8 +49,8 @@ impl From for PolyIOPErrors { } } -impl From for PolyIOPErrors { - fn from(e: PCSErrors) -> Self { - Self::PCSErrors(e) +impl From for PolyIOPErrors { + fn from(e: PCSError) -> Self { + Self::PCSError(e) } } diff --git a/poly-iop/src/perm_check/mod.rs b/poly-iop/src/perm_check/mod.rs index 190a1e7..a9a7f78 100644 --- a/poly-iop/src/perm_check/mod.rs +++ b/poly-iop/src/perm_check/mod.rs @@ -5,7 +5,7 @@ use crate::{errors::PolyIOPErrors, prelude::ProductCheck, PolyIOP}; use ark_ec::PairingEngine; use ark_poly::DenseMultilinearExtension; use ark_std::{end_timer, start_timer}; -use pcs::PolynomialCommitmentScheme; +use jf_primitives::pcs::PolynomialCommitmentScheme; use std::rc::Rc; use transcript::IOPTranscript; @@ -163,10 +163,10 @@ mod test { use ark_ec::PairingEngine; use ark_poly::{DenseMultilinearExtension, MultilinearExtension}; use ark_std::test_rng; - use pcs::{prelude::KZGMultilinearPCS, PolynomialCommitmentScheme}; + use jf_primitives::pcs::{prelude::MultilinearKzgPCS, PolynomialCommitmentScheme}; use std::{marker::PhantomData, rc::Rc}; - type KZG = KZGMultilinearPCS; + type Kzg = MultilinearKzgPCS; fn test_permutation_check_helper( pcs_param: &PCS::ProverParam, @@ -220,15 +220,15 @@ mod test { fn test_permutation_check(nv: usize) -> Result<(), PolyIOPErrors> { let mut rng = test_rng(); - let srs = KZGMultilinearPCS::::gen_srs_for_testing(&mut rng, nv + 1)?; - let (pcs_param, _) = KZGMultilinearPCS::::trim(&srs, nv + 1, Some(nv + 1))?; + let srs = MultilinearKzgPCS::::gen_srs_for_testing(&mut rng, nv + 1)?; + let (pcs_param, _) = MultilinearKzgPCS::::trim(&srs, nv + 1, Some(nv + 1))?; { // good path: w is a permutation of w itself under the identify map let w = Rc::new(DenseMultilinearExtension::rand(nv, &mut rng)); // s_perm is the identity map let s_perm = identity_permutation_mle(nv); - test_permutation_check_helper::(&pcs_param, &w, &w, &s_perm)?; + test_permutation_check_helper::(&pcs_param, &w, &w, &s_perm)?; } { @@ -238,9 +238,9 @@ mod test { let s_perm = random_permutation_mle(nv, &mut rng); if nv == 1 { - test_permutation_check_helper::(&pcs_param, &w, &w, &s_perm)?; + test_permutation_check_helper::(&pcs_param, &w, &w, &s_perm)?; } else { - assert!(test_permutation_check_helper::( + assert!(test_permutation_check_helper::( &pcs_param, &w, &w, &s_perm ) .is_err()); @@ -255,7 +255,7 @@ mod test { let s_perm = identity_permutation_mle(nv); assert!( - test_permutation_check_helper::(&pcs_param, &f, &g, &s_perm) + test_permutation_check_helper::(&pcs_param, &f, &g, &s_perm) .is_err() ); } diff --git a/poly-iop/src/prod_check/mod.rs b/poly-iop/src/prod_check/mod.rs index c94d69f..2bd1279 100644 --- a/poly-iop/src/prod_check/mod.rs +++ b/poly-iop/src/prod_check/mod.rs @@ -11,7 +11,7 @@ use ark_ec::PairingEngine; use ark_ff::{One, PrimeField, Zero}; use ark_poly::DenseMultilinearExtension; use ark_std::{end_timer, start_timer}; -use pcs::prelude::PolynomialCommitmentScheme; +use jf_primitives::pcs::prelude::PolynomialCommitmentScheme; use std::rc::Rc; use transcript::IOPTranscript; @@ -207,7 +207,7 @@ mod test { use ark_ec::PairingEngine; use ark_poly::{DenseMultilinearExtension, MultilinearExtension}; use ark_std::test_rng; - use pcs::{prelude::KZGMultilinearPCS, PolynomialCommitmentScheme}; + use jf_primitives::pcs::{prelude::MultilinearKzgPCS, PolynomialCommitmentScheme}; use std::{marker::PhantomData, rc::Rc}; // f and g are guaranteed to have the same product @@ -254,7 +254,7 @@ mod test { let (bad_proof, prod_x_bad) = as ProductCheck>::prove( pcs_param, &Rc::new(f.clone()), - &Rc::new(h.clone()), + &Rc::new(h), &mut transcript, )?; @@ -281,10 +281,10 @@ mod test { let mut g = f.clone(); g.evaluations.reverse(); - let srs = KZGMultilinearPCS::::gen_srs_for_testing(&mut rng, nv + 1)?; - let (pcs_param, _) = KZGMultilinearPCS::::trim(&srs, nv + 1, Some(nv + 1))?; + let srs = MultilinearKzgPCS::::gen_srs_for_testing(&mut rng, nv + 1)?; + let (pcs_param, _) = MultilinearKzgPCS::::trim(&srs, nv + 1, Some(nv + 1))?; - test_product_check_helper::>(&f, &g, &pcs_param)?; + test_product_check_helper::>(&f, &g, &pcs_param)?; Ok(()) }