From 3c0cb70109f900a1af7bd5a6930060df14e74630 Mon Sep 17 00:00:00 2001 From: zhenfei Date: Tue, 30 Aug 2022 09:38:35 -0400 Subject: [PATCH] perm check (#62) Co-authored-by: Charles Chen --- arithmetic/src/virtual_polynomial.rs | 8 +- hyperplonk/src/lib.rs | 131 ++--- hyperplonk/src/structs.rs | 30 +- pcs/src/multilinear_kzg/util.rs | 10 +- poly-iop/Cargo.toml | 9 +- poly-iop/benches/bench.rs | 55 +-- poly-iop/src/lib.rs | 12 +- poly-iop/src/perm_check/mod.rs | 711 ++++++++------------------- poly-iop/src/perm_check/util.rs | 96 +++- poly-iop/src/prelude.rs | 1 + poly-iop/src/prod_check/mod.rs | 85 ++-- poly-iop/src/prod_check/util.rs | 55 ++- poly-iop/src/structs.rs | 2 +- poly-iop/src/sum_check/mod.rs | 7 +- poly-iop/src/zero_check/mod.rs | 6 +- 15 files changed, 471 insertions(+), 747 deletions(-) diff --git a/arithmetic/src/virtual_polynomial.rs b/arithmetic/src/virtual_polynomial.rs index 50ab6db..e44d9b2 100644 --- a/arithmetic/src/virtual_polynomial.rs +++ b/arithmetic/src/virtual_polynomial.rs @@ -101,8 +101,8 @@ impl VirtualPolynomial { } /// Creates an new virtual polynomial from a MLE and its coefficient. - pub fn new_from_mle(mle: Rc>, coefficient: F) -> Self { - let mle_ptr: *const DenseMultilinearExtension = Rc::as_ptr(&mle); + pub fn new_from_mle(mle: &Rc>, coefficient: F) -> Self { + let mle_ptr: *const DenseMultilinearExtension = Rc::as_ptr(mle); let mut hm = HashMap::new(); hm.insert(mle_ptr, 0); @@ -115,7 +115,7 @@ impl VirtualPolynomial { }, // here `0` points to the first polynomial of `flattened_ml_extensions` products: vec![(coefficient, vec![0])], - flattened_ml_extensions: vec![mle], + flattened_ml_extensions: vec![mle.clone()], raw_pointers_lookup_table: hm, } } @@ -475,7 +475,7 @@ mod test { let (b, _b_sum) = random_mle_list(nv, 1, &mut rng); let b_mle = b[0].clone(); let coeff = Fr::rand(&mut rng); - let b_vp = VirtualPolynomial::new_from_mle(b_mle.clone(), coeff); + let b_vp = VirtualPolynomial::new_from_mle(&b_mle, coeff); let mut c = a.clone(); diff --git a/hyperplonk/src/lib.rs b/hyperplonk/src/lib.rs index a0ec86a..d7669ab 100644 --- a/hyperplonk/src/lib.rs +++ b/hyperplonk/src/lib.rs @@ -8,8 +8,7 @@ use ark_std::{end_timer, log2, start_timer, One, Zero}; use errors::HyperPlonkErrors; use pcs::prelude::{compute_qx_degree, merge_polynomials, PCSErrors, PolynomialCommitmentScheme}; use poly_iop::{ - identity_permutation_mle, - prelude::{PermutationCheck, SumCheck, ZeroCheck}, + prelude::{identity_permutation_mle, PermutationCheck, ZeroCheck}, PolyIOP, }; use selectors::SelectorColumn; @@ -25,10 +24,9 @@ mod structs; mod utils; mod witness; -/// A trait for HyperPlonk Poly-IOPs. +/// A trait for HyperPlonk SNARKs. /// A HyperPlonk is derived from SumChecks, ZeroChecks and PermutationChecks. -pub trait HyperPlonkSNARK: - SumCheck + ZeroCheck + PermutationCheck +pub trait HyperPlonkSNARK: PermutationCheck where E: PairingEngine, PCS: PolynomialCommitmentScheme, @@ -99,7 +97,7 @@ where type Parameters = HyperPlonkParams; type ProvingKey = HyperPlonkProvingKey; type VerifyingKey = HyperPlonkVerifyingKey; - type Proof = HyperPlonkProof; + type Proof = HyperPlonkProof; /// Generate the preprocessed polynomials output by the indexer. /// @@ -277,7 +275,7 @@ where w_merged.num_vars, merged_nv ))); } - let w_merged_com = PCS::commit(&pk.pcs_param, &Rc::new(w_merged.clone()))?; + let w_merged_com = PCS::commit(&pk.pcs_param, &w_merged)?; transcript.append_serializable_element(b"w", &w_merged_com)?; end_timer!(step); @@ -309,51 +307,24 @@ where // ======================================================================= // 3. Run permutation check on `\{w_i(x)\}` and `permutation_oracles`, and // obtain a PermCheckSubClaim. - // - // 3.1. `generate_challenge` from current transcript (generate beta, gamma) - // 3.2. `compute_product` to build `prod(x)` etc. from f, g and s_perm - // 3.3. push a commitment of `prod(x)` to the transcript - // 3.4. `update_challenge` with the updated transcript - // 3.5. `prove` to generate the proof - // 3.6. open `prod(0,x)`, `prod(1, x)`, `prod(x, 0)`, `prod(x, 1)` at - // zero_check.point // ======================================================================= let step = start_timer!(|| "Permutation check on w_i(x)"); - // 3.1 `generate_challenge` from current transcript (generate beta, gamma) - let mut permutation_challenge = Self::generate_challenge(&mut transcript)?; - - // 3.2. `compute_product` to build `prod(x)` etc. from f, g and s_perm - - // This function returns 3 MLEs: - // - prod(x) - // - numerator - // - denominator - // See function signature for details. - let prod_x_and_aux_info = Self::compute_prod_evals( - &permutation_challenge, + let (perm_check_proof, prod_x) = >::prove( + &pk.pcs_param, &w_merged, &w_merged, &pk.permutation_oracles, - )?; - let prod_x = Rc::new(prod_x_and_aux_info[0].clone()); - - // 3.3 push a commitment of `prod(x)` to the transcript - let prod_com = PCS::commit(&pk.pcs_param, &prod_x)?; - - // 3.4. `update_challenge` with the updated transcript - Self::update_challenge(&mut permutation_challenge, &mut transcript, &prod_com)?; - - // 3.5. `prove` to generate the proof - let perm_check_proof = >::prove( - &prod_x_and_aux_info, - &permutation_challenge, &mut transcript, )?; - // 3.6 open prod(0,x), prod(1, x), prod(x, 0), prod(x, 1) at zero_check.point + // open prod(0,x), prod(1, x), prod(x, 0), prod(x, 1) at zero_check.point // prod(0, x) - let tmp_point = [perm_check_proof.point.as_slice(), &[E::Fr::zero()]].concat(); + let tmp_point = [ + perm_check_proof.zero_check_proof.point.as_slice(), + &[E::Fr::zero()], + ] + .concat(); let (prod_0_x_opening, prod_0_x_eval) = PCS::open(&pk.pcs_param, &prod_x, &tmp_point)?; #[cfg(feature = "extensive_sanity_checks")] { @@ -370,7 +341,11 @@ where } } // prod(1, x) - let tmp_point = [perm_check_proof.point.as_slice(), &[E::Fr::one()]].concat(); + let tmp_point = [ + perm_check_proof.zero_check_proof.point.as_slice(), + &[E::Fr::one()], + ] + .concat(); let (prod_1_x_opening, prod_1_x_eval) = PCS::open(&pk.pcs_param, &prod_x, &tmp_point)?; #[cfg(feature = "extensive_sanity_checks")] { @@ -387,7 +362,11 @@ where } } // prod(x, 0) - let tmp_point = [&[E::Fr::zero()], perm_check_proof.point.as_slice()].concat(); + let tmp_point = [ + &[E::Fr::zero()], + perm_check_proof.zero_check_proof.point.as_slice(), + ] + .concat(); let (prod_x_0_opening, prod_x_0_eval) = PCS::open(&pk.pcs_param, &prod_x, &tmp_point)?; #[cfg(feature = "extensive_sanity_checks")] { @@ -405,7 +384,11 @@ where } } // prod(x, 1) - let tmp_point = [&[E::Fr::one()], perm_check_proof.point.as_slice()].concat(); + let tmp_point = [ + &[E::Fr::one()], + perm_check_proof.zero_check_proof.point.as_slice(), + ] + .concat(); let (prod_x_1_opening, prod_x_1_eval) = PCS::open(&pk.pcs_param, &prod_x, &tmp_point)?; #[cfg(feature = "extensive_sanity_checks")] { @@ -447,18 +430,20 @@ where // open permutation check proof let (witness_perm_check_opening, witness_perm_check_eval) = PCS::open( &pk.pcs_param, - &Rc::new(w_merged.clone()), - &perm_check_proof.point, + &w_merged, + &perm_check_proof.zero_check_proof.point, )?; #[cfg(feature = "extensive_sanity_checks")] { // sanity checks - let eval = w_merged.evaluate(&perm_check_proof.point).ok_or_else(|| { - HyperPlonkErrors::InvalidParameters( - "evaluation dimension does not match".to_string(), - ) - })?; + let eval = w_merged + .evaluate(&perm_check_proof.zero_check_proof.point) + .ok_or_else(|| { + HyperPlonkErrors::InvalidParameters( + "evaluation dimension does not match".to_string(), + ) + })?; if eval != witness_perm_check_eval { return Err(HyperPlonkErrors::InvalidProver( "Evaluation is different from PCS opening".to_string(), @@ -492,7 +477,7 @@ where let (s_perm_opening, s_perm_eval) = PCS::open( &pk.pcs_param, &pk.permutation_oracles, - &perm_check_proof.point, + &perm_check_proof.zero_check_proof.point, )?; #[cfg(feature = "extensive_sanity_checks")] @@ -500,7 +485,7 @@ where // sanity check let eval = pk .permutation_oracles - .evaluate(&perm_check_proof.point) + .evaluate(&perm_check_proof.zero_check_proof.point) .ok_or_else(|| { HyperPlonkErrors::InvalidParameters( "evaluation dimension does not match".to_string(), @@ -576,7 +561,6 @@ where // PCS components: permutation check // ======================================================================= // We do not validate prod(x), this is checked by subclaim - prod_commit: prod_com, prod_evals: vec![prod_0_x_eval, prod_1_x_eval, prod_x_0_eval, prod_x_1_eval], prod_openings: vec![ prod_0_x_opening, @@ -715,6 +699,7 @@ where // 2. Verify perm_check_proof on `\{w_i(x)\}` and `permutation_oracles` // ======================================================================= let step = start_timer!(|| "verify permutation check"); + // Zero check and sum check have different AuxInfo because `w_merged` and // `Prod(x)` have degree and num_vars let perm_check_aux_info = VPAuxInfo:: { @@ -724,23 +709,21 @@ where num_variables: merged_nv, phantom: PhantomData::default(), }; - let mut challenge = >::generate_challenge(&mut transcript)?; - >::update_challenge( - &mut challenge, - &mut transcript, - &proof.prod_commit, - )?; - - let perm_check_sub_claim = >::verify( + let perm_check_sub_claim = >::verify( &proof.perm_check_proof, &perm_check_aux_info, &mut transcript, )?; + let perm_check_point = &perm_check_sub_claim + .product_check_sub_claim .zero_check_sub_claim .sum_check_sub_claim .point; + let alpha = perm_check_sub_claim.product_check_sub_claim.challenge; + let (beta, gamma) = perm_check_sub_claim.challenges; + // check perm check subclaim: // proof.witness_perm_check_eval ?= perm_check_sub_claim.expected_eval // @@ -754,9 +737,6 @@ where // - g(x), f(x) are both w_merged over (zero_point) // - s_perm(x) and s_id(x) from vk_param.perm_oracle // - alpha, beta, gamma from challenge - let alpha = challenge - .alpha - .ok_or_else(|| HyperPlonkErrors::InvalidVerifier("alpha is not set".to_string()))?; let s_id = identity_permutation_mle::(perm_check_point.len()); let s_id_eval = s_id.evaluate(perm_check_point).ok_or_else(|| { @@ -765,16 +745,13 @@ where let q_x_rec = proof.prod_evals[1] - proof.prod_evals[2] * proof.prod_evals[3] + alpha - * ((proof.witness_perm_check_eval - + challenge.beta * proof.perm_oracle_eval - + challenge.gamma) + * ((proof.witness_perm_check_eval + beta * proof.perm_oracle_eval + gamma) * proof.prod_evals[0] - - (proof.witness_perm_check_eval - + challenge.beta * s_id_eval - + challenge.gamma)); + - (proof.witness_perm_check_eval + beta * s_id_eval + gamma)); if q_x_rec != perm_check_sub_claim + .product_check_sub_claim .zero_check_sub_claim .expected_evaluation { @@ -823,7 +800,7 @@ where // prod(0, x) if !PCS::verify( &vk.pcs_param, - &proof.prod_commit, + &proof.perm_check_proof.prod_x_comm, &[perm_check_point.as_slice(), &[E::Fr::zero()]].concat(), &proof.prod_evals[0], &proof.prod_openings[0], @@ -835,7 +812,7 @@ where // prod(1, x) if !PCS::verify( &vk.pcs_param, - &proof.prod_commit, + &proof.perm_check_proof.prod_x_comm, &[perm_check_point.as_slice(), &[E::Fr::one()]].concat(), &proof.prod_evals[1], &proof.prod_openings[1], @@ -847,7 +824,7 @@ where // prod(x, 0) if !PCS::verify( &vk.pcs_param, - &proof.prod_commit, + &proof.perm_check_proof.prod_x_comm, &[&[E::Fr::zero()], perm_check_point.as_slice()].concat(), &proof.prod_evals[2], &proof.prod_openings[2], @@ -859,7 +836,7 @@ where // prod(x, 1) if !PCS::verify( &vk.pcs_param, - &proof.prod_commit, + &proof.perm_check_proof.prod_x_comm, &[&[E::Fr::one()], perm_check_point.as_slice()].concat(), &proof.prod_evals[3], &proof.prod_openings[3], @@ -940,7 +917,7 @@ mod tests { use ark_bls12_381::Bls12_381; use ark_std::test_rng; use pcs::prelude::KZGMultilinearPCS; - use poly_iop::random_permutation_mle; + use poly_iop::prelude::random_permutation_mle; #[test] fn test_hyperplonk_e2e() -> Result<(), HyperPlonkErrors> { diff --git a/hyperplonk/src/structs.rs b/hyperplonk/src/structs.rs index 3f6e3f5..68ccca3 100644 --- a/hyperplonk/src/structs.rs +++ b/hyperplonk/src/structs.rs @@ -1,38 +1,23 @@ //! Main module for the HyperPlonk PolyIOP. use ark_ec::PairingEngine; -use ark_ff::PrimeField; use ark_poly::DenseMultilinearExtension; use pcs::PolynomialCommitmentScheme; use poly_iop::prelude::{PermutationCheck, ZeroCheck}; use std::rc::Rc; -/// The sub-claim for the HyperPlonk PolyIOP, consists of the following: -/// - the SubClaim for the zero-check PIOP -/// - the SubClaim for the permutation-check PIOP -/// - the SubClaim for public input consistency -#[derive(Clone, Debug, Default, PartialEq)] -pub struct HyperPlonkSubClaim, PC: PermutationCheck> { - /// the SubClaim for the custom gate zerocheck - pub zero_check_sub_claim: ZC::ZeroCheckSubClaim, - /// the SubClaim for the permutation check - pub perm_check_sub_claim: PC::PermutationCheckSubClaim, - /// the public input consistency check - pub pub_input_sub_claim: (Vec, F), // (point, expected_eval) -} - /// The proof for the HyperPlonk PolyIOP, consists of the following: /// - a batch commitment to all the witness MLEs /// - a batch opening to all the MLEs at certain index /// - the zero-check proof for checking custom gate-satisfiability /// - the permutation-check proof for checking the copy constraints #[derive(Clone, Debug, Default, PartialEq)] -pub struct HyperPlonkProof< +pub struct HyperPlonkProof +where E: PairingEngine, + PC: PermutationCheck, PCS: PolynomialCommitmentScheme, - ZC: ZeroCheck, - PC: PermutationCheck, -> { +{ // ======================================================================= // PCS components: common // ======================================================================= @@ -43,9 +28,6 @@ pub struct HyperPlonkProof< // ======================================================================= // PCS components: permutation check // ======================================================================= - /// PCS commit for prod(x) - // TODO: replace me with a batch commitment - pub prod_commit: PCS::Commitment, /// prod(x)'s evaluations /// sequence: prod(0,x), prod(1, x), prod(x, 0), prod(x, 1) pub prod_evals: Vec, @@ -86,7 +68,7 @@ pub struct HyperPlonkProof< // IOP components // ======================================================================= /// the custom gate zerocheck proof - pub zero_check_proof: ZC::ZeroCheckProof, + pub zero_check_proof: >::ZeroCheckProof, /// the permutation check proof for copy constraints pub perm_check_proof: PC::PermutationProof, } @@ -97,7 +79,7 @@ pub struct HyperPlonkProof< /// - binary log of the number of selectors /// - binary log of the number of witness wires /// - the customized gate function -#[derive(Clone, Debug, Default, PartialEq)] +#[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct HyperPlonkParams { /// the number of variables in polys pub nv: usize, diff --git a/pcs/src/multilinear_kzg/util.rs b/pcs/src/multilinear_kzg/util.rs index 83dfe64..3c5425e 100644 --- a/pcs/src/multilinear_kzg/util.rs +++ b/pcs/src/multilinear_kzg/util.rs @@ -107,7 +107,7 @@ pub fn get_batched_nv(num_var: usize, polynomials_len: usize) -> usize { /// polynomials do not share a same number of nvs. pub fn merge_polynomials( polynomials: &[Rc>], -) -> Result, PCSErrors> { +) -> Result>, PCSErrors> { let nv = polynomials[0].num_vars(); for poly in polynomials.iter() { if nv != poly.num_vars() { @@ -123,9 +123,9 @@ pub fn merge_polynomials( scalars.extend_from_slice(poly.to_evaluations().as_slice()); } scalars.extend_from_slice(vec![F::zero(); (1 << merged_nv) - scalars.len()].as_ref()); - Ok(DenseMultilinearExtension::from_evaluations_vec( + Ok(Rc::new(DenseMultilinearExtension::from_evaluations_vec( merged_nv, scalars, - )) + ))) } /// Given a list of points, build `l(points)` which is a list of univariate @@ -337,7 +337,7 @@ mod test { F::from(1u64), F::from(2u64), ]; - let w_rec = DenseMultilinearExtension::from_evaluations_vec(3, w_eval); + let w_rec = Rc::new(DenseMultilinearExtension::from_evaluations_vec(3, w_eval)); assert_eq!(w, w_rec); } @@ -387,7 +387,7 @@ mod test { F::zero(), F::zero(), ]; - let w_rec = DenseMultilinearExtension::from_evaluations_vec(4, w_eval); + let w_rec = Rc::new(DenseMultilinearExtension::from_evaluations_vec(4, w_eval)); assert_eq!(w, w_rec); } diff --git a/poly-iop/Cargo.toml b/poly-iop/Cargo.toml index e299ff2..e058c96 100644 --- a/poly-iop/Cargo.toml +++ b/poly-iop/Cargo.toml @@ -6,14 +6,13 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -pcs = { path = "../pcs" } ark-ff = { version = "^0.3.0", default-features = false } ark-std = { 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-serialize = { version = "^0.3.0", default-features = false } ark-bls12-381 = { version = "0.3.0", default-features = false, features = [ "curve" ] } -ark-ec = { version = "^0.3.0", default-features = false } rand_chacha = { version = "0.3.0", default-features = false } displaydoc = { version = "0.2.3", default-features = false } @@ -21,6 +20,7 @@ rayon = { version = "1.5.2", default-features = false, optional = true } transcript = { path = "../transcript" } arithmetic = { path = "../arithmetic" } +pcs = { path = "../pcs" } [dev-dependencies] ark-ec = { version = "^0.3.0", default-features = false } @@ -39,10 +39,11 @@ parallel = [ "arithmetic/parallel", "ark-std/parallel", "ark-ff/parallel", - "ark-poly/parallel", + "ark-poly/parallel", "pcs/parallel", ] print-trace = [ "arithmetic/print-trace", - "ark-std/print-trace" + "ark-std/print-trace", + "pcs/print-trace", ] \ No newline at end of file diff --git a/poly-iop/benches/bench.rs b/poly-iop/benches/bench.rs index e0c487d..0e9a332 100644 --- a/poly-iop/benches/bench.rs +++ b/poly-iop/benches/bench.rs @@ -1,11 +1,14 @@ use arithmetic::{VPAuxInfo, VirtualPolynomial}; -use ark_bls12_381::Fr; +use ark_bls12_381::{Bls12_381, Fr}; use ark_poly::{DenseMultilinearExtension, MultilinearExtension}; -use ark_std::{test_rng, UniformRand}; +use ark_std::test_rng; +use pcs::{prelude::KZGMultilinearPCS, PolynomialCommitmentScheme}; use poly_iop::prelude::{ identity_permutation_mle, PermutationCheck, PolyIOP, PolyIOPErrors, SumCheck, ZeroCheck, }; -use std::{marker::PhantomData, time::Instant}; +use std::{marker::PhantomData, rc::Rc, time::Instant}; + +type KZG = KZGMultilinearPCS; fn main() -> Result<(), PolyIOPErrors> { bench_permutation_check()?; @@ -135,6 +138,9 @@ 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 repetition = if nv < 10 { 100 } else if nv < 20 { @@ -143,34 +149,22 @@ fn bench_permutation_check() -> Result<(), PolyIOPErrors> { 10 }; - let w = DenseMultilinearExtension::rand(nv, &mut rng); + let w = Rc::new(DenseMultilinearExtension::rand(nv, &mut rng)); // s_perm is the identity map let s_perm = identity_permutation_mle(nv); let proof = { let start = Instant::now(); - let mut transcript = as PermutationCheck>::init_transcript(); + let mut transcript = + as PermutationCheck>::init_transcript(); transcript.append_message(b"testing", b"initializing transcript for testing")?; - let mut challenge = - as PermutationCheck>::generate_challenge(&mut transcript)?; - - let prod_x_and_aux = as PermutationCheck>::compute_prod_evals( - &challenge, &w, &w, &s_perm, - )?; - - let prod_x_binding = mock_commit(&prod_x_and_aux[0]); - - as PermutationCheck>::update_challenge( - &mut challenge, - &mut transcript, - &prod_x_binding, - )?; - - let proof = as PermutationCheck>::prove( - &prod_x_and_aux, - &challenge, + let (proof, _q_x) = as PermutationCheck>::prove( + &pcs_param, + &w, + &w, + &s_perm, &mut transcript, )?; @@ -190,10 +184,14 @@ fn bench_permutation_check() -> Result<(), PolyIOPErrors> { }; let start = Instant::now(); - let mut transcript = as PermutationCheck>::init_transcript(); + let mut transcript = + as PermutationCheck>::init_transcript(); transcript.append_message(b"testing", b"initializing transcript for testing")?; - let _subclaim = - as PermutationCheck>::verify(&proof, &poly_info, &mut transcript)?; + let _perm_check_sum_claim = as PermutationCheck>::verify( + &proof, + &poly_info, + &mut transcript, + )?; println!( "permutation check verification time for {} variables: {} ns", nv, @@ -206,8 +204,3 @@ fn bench_permutation_check() -> Result<(), PolyIOPErrors> { Ok(()) } - -fn mock_commit(_f: &DenseMultilinearExtension) -> Fr { - let mut rng = test_rng(); - Fr::rand(&mut rng) -} diff --git a/poly-iop/src/lib.rs b/poly-iop/src/lib.rs index 1331f65..6e1102f 100644 --- a/poly-iop/src/lib.rs +++ b/poly-iop/src/lib.rs @@ -10,17 +10,7 @@ mod sum_check; mod utils; mod zero_check; -pub use errors::PolyIOPErrors; -pub use perm_check::{ - util::{identity_permutation_mle, random_permutation_mle}, - PermutationCheck, -}; -pub use prod_check::ProductCheck; -pub use sum_check::SumCheck; -pub use utils::*; -pub use zero_check::ZeroCheck; - -#[derive(Clone, Debug, Default, Copy)] +#[derive(Clone, Debug, Default, Copy, PartialEq, Eq)] /// Struct for PolyIOP protocol. /// It has an associated type `F` that defines the prime field the multi-variate /// polynomial operates on. diff --git a/poly-iop/src/perm_check/mod.rs b/poly-iop/src/perm_check/mod.rs index f65531c..b250294 100644 --- a/poly-iop/src/perm_check/mod.rs +++ b/poly-iop/src/perm_check/mod.rs @@ -1,20 +1,30 @@ //! Main module for the Permutation Check protocol -use crate::{ - errors::PolyIOPErrors, - perm_check::util::{build_prod_partial_eval, compute_prod_0}, - structs::IOPProof, - utils::get_index, - PolyIOP, ZeroCheck, -}; -use arithmetic::VirtualPolynomial; -use ark_ff::PrimeField; +use self::util::computer_num_and_denom; +use crate::{errors::PolyIOPErrors, prelude::ProductCheck, PolyIOP}; +use ark_ec::PairingEngine; use ark_poly::DenseMultilinearExtension; -use ark_serialize::CanonicalSerialize; use ark_std::{end_timer, start_timer}; +use pcs::PolynomialCommitmentScheme; use std::rc::Rc; use transcript::IOPTranscript; +/// A permutation subclaim consists of +/// - the SubClaim from the ProductCheck +/// - Challenges beta and gamma +#[derive(Clone, Debug, Default, PartialEq)] +pub struct PermutationCheckSubClaim +where + E: PairingEngine, + PC: ProductCheck, + PCS: PolynomialCommitmentScheme, +{ + /// the SubClaim from the ProductCheck + pub product_check_sub_claim: PC::ProductCheckSubClaim, + /// Challenges beta and gamma + pub challenges: (E::Fr, E::Fr), +} + pub mod util; /// A PermutationCheck is derived from ZeroCheck. @@ -25,31 +35,14 @@ pub mod util; /// - f(x) /// - g(x) /// - permutation s_perm(x) -/// -/// Steps: -/// 1. `generate_challenge` from current transcript (generate beta, gamma) -/// 2. `compute_product` to build `prod(x)` etc. from f, g and s_perm -/// 3. push a commitment of `prod(x)` to the transcript (done by the snark -/// caller) -/// 4. `update_challenge` with the updated transcript (generate alpha) -/// 5. `prove` to generate the proof -pub trait PermutationCheck: ZeroCheck { +pub trait PermutationCheck: ProductCheck +where + E: PairingEngine, + PCS: PolynomialCommitmentScheme, +{ type PermutationCheckSubClaim; - type PermutationChallenge; type PermutationProof; - /// Generate the preprocessed polynomial for the permutation check. - /// - /// The algorithm takes as input a permutation and outputs a merged - /// multilinear polynomial s(X0, X1, ..., Xn) such that - /// - s(0, X1, ..., Xn) = s_id(X1, ..., Xn) (identity permutation - /// polynomial) - /// - s(1, X1, ..., Xn) = s_perm(X1, ..., Xn) (permutation polynomial) - fn preprocess( - permutation: &[F], - aux_info: &Self::VPAuxInfo, - ) -> Result, PolyIOPErrors>; - /// Initialize the system with a transcript /// /// This function is optional -- in the case where a PermutationCheck is @@ -58,77 +51,23 @@ pub trait PermutationCheck: ZeroCheck { /// PermutationCheck prover/verifier. fn init_transcript() -> Self::Transcript; - /// Step 1 of the IOP. - /// Generate challenge beta and gamma from a transcript. - fn generate_challenge( - transcript: &mut Self::Transcript, - ) -> Result; - - /// Step 2 of the IOP. - /// - /// Input: - /// - f(x), g(x), s_perm(x) are mle-s - /// - challenges, consists of beta and gamma - /// - /// Output: the evaluations for the following 3 polynomials - /// - prod(x) - /// - numerator - /// - denominator - /// - /// where - /// - `prod(0,x) := prod(0, x0, x1, …, xn)` which is the MLE over the - /// evaluations of the following polynomial on the boolean hypercube - /// {0,1}^n: - /// - /// (f(x) + \beta s_id(x) + \gamma)/(g(x) + \beta s_perm(x) + \gamma) - /// - /// where s_id(x) is an identity permutation - /// - /// - numerator is the MLE for `f(x) + \beta s_id(x) + \gamma` - /// - denominator is the MLE for `g(x) + \beta s_perm(x) + \gamma` - /// - /// The caller needs to check num_vars matches in f/g/s_id/s_perm - /// Cost: linear in N. - /// - /// TODO: replace argument `s_perm` with the merged polynomial `s`. - fn compute_prod_evals( - challenge: &Self::PermutationChallenge, - fx: &DenseMultilinearExtension, - gx: &DenseMultilinearExtension, - s_perm: &DenseMultilinearExtension, - ) -> Result<[DenseMultilinearExtension; 3], PolyIOPErrors>; - - /// Step 3 of the IOP. - /// push a commitment of `prod(x)` to the transcript - /// IMPORTANT: this step is done by the snark caller - fn commit_prod_x() { - unimplemented!() - } - - /// Step 4 of the IOP. - /// Update the challenge with alpha; returns an error if - /// alpha already exists. - fn update_challenge( - challenge: &mut Self::PermutationChallenge, - transcript: &mut Self::Transcript, - prod_x_binding: &impl CanonicalSerialize, - ) -> Result<(), PolyIOPErrors>; - - /// Step 5 of the IOP. - /// - /// Initialize the prover to argue that an MLE g(x) is a permutation of - /// MLE f(x) over a permutation given by s_perm. /// Inputs: - /// - 3 MLEs from the second step - /// - challenge: `Self::Challenge` that has been updated - /// - transcript: a transcript that is used to generate the challenges beta - /// and gamma + /// - f(x) + /// - g(x) + /// - permutation s_perm(x) + /// Outputs: + /// - a permutation check proof proving that g is a permutation of f under + /// s_perm + /// - the Q(x) polynomial build during product check + /// /// Cost: O(N) fn prove( - prod_x_and_aux_info: &[DenseMultilinearExtension; 3], - challenge: &Self::PermutationChallenge, - transcript: &mut IOPTranscript, - ) -> Result; + pcs_param: &PCS::ProverParam, + fx: &Self::MultilinearExtension, + gx: &Self::MultilinearExtension, + s_perm: &Self::MultilinearExtension, + transcript: &mut IOPTranscript, + ) -> Result<(Self::PermutationProof, Self::MultilinearExtension), PolyIOPErrors>; /// Verify that an MLE g(x) is a permutation of /// MLE f(x) over a permutation given by s_perm. @@ -139,32 +78,6 @@ pub trait PermutationCheck: ZeroCheck { ) -> Result; } -/// A permutation subclaim consists of -/// - A zero check IOP subclaim for Q(x) is 0, consists of the following: -/// (See `build_qx` for definition of Q(x).) -/// - the SubClaim from the SumCheck -/// - the initial challenge r which is used to build eq(x, r) in ZeroCheck -/// - A final query for `prod(1, ..., 1, 0) = 1`. -// Note that this final query is in fact a constant that -// is independent from the proof. So we should avoid -// (de)serialize it. -#[derive(Clone, Debug, Default, PartialEq)] -pub struct PermutationCheckSubClaim> { - // the SubClaim from the ZeroCheck - pub zero_check_sub_claim: ZC::ZeroCheckSubClaim, - // final query which consists of - // - the vector `(1, ..., 1, 0)` - // - the evaluation `1` - final_query: (Vec, F), -} - -#[derive(Debug, Clone)] -pub struct PermutationChallenge { - pub alpha: Option, - pub beta: F, - pub gamma: F, -} - /// A PermutationCheck is derived from ZeroCheck. /// /// A Permutation Check IOP takes the following steps: @@ -181,28 +94,16 @@ pub struct PermutationChallenge { /// caller) /// 4. `update_challenge` with the updated transcript (generate alpha) /// 5. `prove` to generate the proof -impl PermutationCheck for PolyIOP { +impl PermutationCheck for PolyIOP +where + E: PairingEngine, + PCS: PolynomialCommitmentScheme>>, +{ /// A Permutation SubClaim is indeed a ZeroCheck SubClaim that consists of /// - the SubClaim from the SumCheck /// - the initial challenge r which is used to build eq(x, r) - type PermutationCheckSubClaim = PermutationCheckSubClaim; - - type PermutationProof = Self::SumCheckProof; - type PermutationChallenge = PermutationChallenge; - - /// Generate the preprocessed polynomial for the permutation check. - /// - /// The algorithm takes as input a permutation and outputs a merged - /// multilinear polynomial s(X0, X1, ..., Xn) such that - /// - s(0, X1, ..., Xn) = s_id(X1, ..., Xn) (identity permutation - /// polynomial) - /// - s(1, X1, ..., Xn) = s_perm(X1, ..., Xn) (permutation polynomial) - fn preprocess( - _permutation: &[F], - _aux_info: &Self::VPAuxInfo, - ) -> Result, PolyIOPErrors> { - unimplemented!(); - } + type PermutationCheckSubClaim = PermutationCheckSubClaim; + type PermutationProof = Self::ProductCheckProof; /// Initialize the system with a transcript /// @@ -211,162 +112,50 @@ impl PermutationCheck for PolyIOP { /// may be initialized by this complex protocol, and passed to the /// PermutationCheck prover/verifier. fn init_transcript() -> Self::Transcript { - IOPTranscript::::new(b"Initializing PermutationCheck transcript") - } - - /// Step 1 of the IOP. - /// Generate challenge beta and gamma from a transcript. - fn generate_challenge( - transcript: &mut Self::Transcript, - ) -> Result { - Ok(Self::PermutationChallenge { - beta: transcript.get_and_append_challenge(b"beta")?, - gamma: transcript.get_and_append_challenge(b"gamma")?, - alpha: None, - }) + IOPTranscript::::new(b"Initializing PermutationCheck transcript") } - /// Step 2 of the IOP. - /// - /// Input: - /// - f(x), g(x), s_perm(x) are mle-s - /// - challenges, consists of beta and gamma - /// - /// Output: the evaluations for the following 3 polynomials - /// - prod(x) - /// - numerator - /// - denominator - /// - /// where - /// - `prod(0,x) := prod(0, x0, x1, …, xn)` which is the MLE over the - /// evaluations of the following polynomial on the boolean hypercube - /// {0,1}^n: - /// - /// (f(x) + \beta s_id(x) + \gamma)/(g(x) + \beta s_perm(x) + \gamma) - /// - /// where s_id(x) is an identity permutation - /// - /// - numerator is the MLE for `f(x) + \beta s_id(x) + \gamma` - /// - denominator is the MLE for `g(x) + \beta s_perm(x) + \gamma` - /// - /// The caller needs to check num_vars matches in f/g/s_id/s_perm - /// Cost: linear in N. + /// Inputs: + /// - f(x) + /// - g(x) + /// - permutation s_perm(x) + /// Outputs: + /// - a permutation check proof proving that g is a permutation of f under + /// s_perm + /// - the Q(x) polynomial build during product check /// - /// TODO: replace argument `s_perm` with the merged polynomial `s`. - fn compute_prod_evals( - challenge: &Self::PermutationChallenge, - fx: &DenseMultilinearExtension, - gx: &DenseMultilinearExtension, - s_perm: &DenseMultilinearExtension, - ) -> Result<[DenseMultilinearExtension; 3], PolyIOPErrors> { - let start = start_timer!(|| "compute evaluations of prod polynomial"); - - if challenge.alpha.is_some() { - return Err(PolyIOPErrors::InvalidChallenge( - "alpha is already sampled".to_string(), + /// Cost: O(N) + fn prove( + pcs_param: &PCS::ProverParam, + fx: &Self::MultilinearExtension, + gx: &Self::MultilinearExtension, + s_perm: &Self::MultilinearExtension, + transcript: &mut IOPTranscript, + ) -> Result<(Self::PermutationProof, Self::MultilinearExtension), PolyIOPErrors> { + let start = start_timer!(|| "Permutation check prove"); + if fx.num_vars != gx.num_vars { + return Err(PolyIOPErrors::InvalidParameters( + "fx and gx have different number of variables".to_string(), )); } - let num_vars = fx.num_vars; - - // =================================== - // prod(0, x) - // =================================== - let (prod_0x_eval, numerator_eval, denominator_eval) = - compute_prod_0(&challenge.beta, &challenge.gamma, fx, gx, s_perm)?; - - // =================================== - // prod(1, x) - // =================================== - // - // `prod(1, x)` can be computed via recursing the following formula for 2^n-1 - // times - // - // `prod(1, x_1, ..., x_n) := - // prod(x_1, x_2, ..., x_n, 0) * prod(x_1, x_2, ..., x_n, 1)` - // - // At any given step, the right hand side of the equation - // is available via either eval_0x or the current view of eval_1x - let mut prod_1x_eval = vec![]; - for x in 0..(1 << num_vars) - 1 { - // sign will decide if the evaluation should be looked up from eval_0x or - // eval_1x; x_zero_index is the index for the evaluation (x_2, ..., x_n, - // 0); x_one_index is the index for the evaluation (x_2, ..., x_n, 1); - let (x_zero_index, x_one_index, sign) = get_index(x, num_vars); - if !sign { - prod_1x_eval.push(prod_0x_eval[x_zero_index] * prod_0x_eval[x_one_index]); - } else { - // sanity check: if we are trying to look up from the eval_1x table, - // then the target index must already exist - if x_zero_index >= prod_1x_eval.len() || x_one_index >= prod_1x_eval.len() { - return Err(PolyIOPErrors::ShouldNotArrive); - } - prod_1x_eval.push(prod_1x_eval[x_zero_index] * prod_1x_eval[x_one_index]); - } + if fx.num_vars != s_perm.num_vars { + return Err(PolyIOPErrors::InvalidParameters( + "fx and s_perm have different number of variables".to_string(), + )); } - // prod(1, 1, ..., 1) := 0 - prod_1x_eval.push(F::zero()); - - // =================================== - // prod(x) - // =================================== - // prod(x)'s evaluation is indeed `e := [eval_0x[..], eval_1x[..]].concat()` - let eval = [prod_0x_eval.as_slice(), prod_1x_eval.as_slice()].concat(); + // generate challenge `beta` and `gamma` from current transcript + let beta = transcript.get_and_append_challenge(b"beta")?; + let gamma = transcript.get_and_append_challenge(b"gamma")?; + let (numerator, denominator) = computer_num_and_denom(&beta, &gamma, fx, gx, s_perm)?; - let fx = DenseMultilinearExtension::from_evaluations_vec(num_vars + 1, eval); - let numerator = DenseMultilinearExtension::from_evaluations_vec(num_vars, numerator_eval); - let denominator = - DenseMultilinearExtension::from_evaluations_vec(num_vars, denominator_eval); + // invoke product check on numerator and denominator + let (proof, poly) = + >::prove(pcs_param, &numerator, &denominator, transcript)?; end_timer!(start); - Ok([fx, numerator, denominator]) - } - - /// Step 4 of the IOP. - /// Update the challenge with alpha; returns an error if - /// alpha already exists. - fn update_challenge( - challenge: &mut Self::PermutationChallenge, - transcript: &mut Self::Transcript, - prod_x_binding: &impl CanonicalSerialize, - ) -> Result<(), PolyIOPErrors> { - if challenge.alpha.is_some() { - return Err(PolyIOPErrors::InvalidChallenge( - "alpha should not be sampled at the current stage".to_string(), - )); - } - transcript.append_serializable_element(b"prod(x)", prod_x_binding)?; - challenge.alpha = Some(transcript.get_and_append_challenge(b"alpha")?); - Ok(()) - } - - /// Step 5 of the IOP. - /// - /// Initialize the prover to argue that an MLE g(x) is a permutation of - /// MLE f(x) over a permutation given by s_perm. - /// Inputs: - /// - 3 MLEs from the second step - /// - challenge: `Self::Challenge` that has been updated - /// - transcript: a transcript that is used to generate the challenges beta - /// and gamma - /// Cost: O(N) - fn prove( - prod_x_and_aux_info: &[DenseMultilinearExtension; 3], - challenge: &Self::PermutationChallenge, - transcript: &mut IOPTranscript, - ) -> Result { - let alpha = match challenge.alpha { - Some(p) => p, - None => { - return Err(PolyIOPErrors::InvalidChallenge( - "alpha is not sampled yet".to_string(), - )) - }, - }; - - let (proof, _q_x) = prove_internal(prod_x_and_aux_info, &alpha, transcript)?; - Ok(proof) + Ok((proof, poly)) } /// Verify that an MLE g(x) is a permutation of an @@ -378,235 +167,187 @@ impl PermutationCheck for PolyIOP { ) -> Result { let start = start_timer!(|| "Permutation check verify"); + let beta = transcript.get_and_append_challenge(b"beta")?; + let gamma = transcript.get_and_append_challenge(b"gamma")?; + // invoke the zero check on the iop_proof - let zero_check_sub_claim = >::verify(proof, aux_info, transcript)?; - let mut final_query = vec![F::one(); aux_info.num_variables]; - final_query[aux_info.num_variables - 1] = F::zero(); - let final_eval = F::one(); + let product_check_sub_claim = + >::verify(proof, aux_info, transcript)?; end_timer!(start); - Ok(PermutationCheckSubClaim { - zero_check_sub_claim, - final_query: (final_query, final_eval), + product_check_sub_claim, + challenges: (beta, gamma), }) } } -/// Step 5 of the IOP. -/// -/// Generate a proof to argue that an MLE g(x) is a permutation of -/// MLE f(x) over a permutation given by s_perm. -/// Inputs: -/// - 3 MLEs from the second step -/// - challenge alpha -/// - transcript: a transcript that is used to generate the challenges beta and -/// gamma -/// -/// Returns proof and Q(x) for testing purpose. -/// -/// Cost: O(N) -fn prove_internal( - prod_x_and_aux_info: &[DenseMultilinearExtension; 3], - alpha: &F, - transcript: &mut IOPTranscript, -) -> Result<(IOPProof, VirtualPolynomial), PolyIOPErrors> { - let start = start_timer!(|| "Permutation check prove"); - - let prod_partial_evals = build_prod_partial_eval(&prod_x_and_aux_info[0])?; - let prod_0x = Rc::new(prod_partial_evals[0].clone()); - let prod_1x = Rc::new(prod_partial_evals[1].clone()); - let prod_x0 = Rc::new(prod_partial_evals[2].clone()); - let prod_x1 = Rc::new(prod_partial_evals[3].clone()); - let numerator = Rc::new(prod_x_and_aux_info[1].clone()); - let denominator = Rc::new(prod_x_and_aux_info[2].clone()); - - // compute (g(x) + beta * s_perm(x) + gamma) * prod(0, x) * alpha - // which is prods[6] * prod[1] * alpha - let mut q_x = VirtualPolynomial::new_from_mle(denominator, F::one()); - q_x.mul_by_mle(prod_0x, *alpha)?; - - // (g(x) + beta * s_perm(x) + gamma) * prod(0, x) * alpha - // - (f(x) + beta * s_id(x) + gamma) * alpha - q_x.add_mle_list([numerator], -*alpha)?; - - // Q(x) := prod(1,x) - prod(x, 0) * prod(x, 1) - // + alpha * ( - // (g(x) + beta * s_perm(x) + gamma) * prod(0, x) - // - (f(x) + beta * s_id(x) + gamma)) - q_x.add_mle_list([prod_x0, prod_x1], -F::one())?; - q_x.add_mle_list([prod_1x], F::one())?; - - let iop_proof = as ZeroCheck>::prove(&q_x, transcript)?; - - end_timer!(start); - Ok((iop_proof, q_x)) -} - #[cfg(test)] mod test { use super::{util::build_prod_partial_eval, PermutationCheck}; use crate::{ errors::PolyIOPErrors, - perm_check::prove_internal, + perm_check::util::computer_num_and_denom, prelude::{identity_permutation_mle, random_permutation_mle}, - structs::IOPProof, utils::bit_decompose, PolyIOP, }; use arithmetic::{VPAuxInfo, VirtualPolynomial}; - use ark_bls12_381::{Fr, G1Affine}; - use ark_ec::{AffineCurve, ProjectiveCurve}; - use ark_ff::{UniformRand, Zero}; + use ark_bls12_381::Bls12_381; + use ark_ec::PairingEngine; + use ark_ff::{One, Zero}; use ark_poly::{DenseMultilinearExtension, MultilinearExtension}; use ark_std::test_rng; - use std::marker::PhantomData; - - /// This is a mock function to generate some commitment element for testing. - fn mock_commit(_f: &DenseMultilinearExtension) -> G { - let mut rng = test_rng(); - G::Projective::rand(&mut rng).into_affine() - } + use pcs::{prelude::KZGMultilinearPCS, PolynomialCommitmentScheme}; + use std::{marker::PhantomData, rc::Rc}; + + type KZG = KZGMultilinearPCS; + + fn test_permutation_check_helper( + pcs_param: &PCS::ProverParam, + fx: &Rc>, + gx: &Rc>, + s_perm: &Rc>, + ) -> Result<(), PolyIOPErrors> + where + E: PairingEngine, + PCS: PolynomialCommitmentScheme>>, + { + let nv = fx.num_vars; + let poly_info = VPAuxInfo { + max_degree: 2, + num_variables: nv, + phantom: PhantomData::default(), + }; - fn test_permutation_check_helper( - f: &DenseMultilinearExtension, - g: &DenseMultilinearExtension, - s_perm: &DenseMultilinearExtension, - ) -> Result<(IOPProof, VirtualPolynomial), PolyIOPErrors> { - let mut transcript = as PermutationCheck>::init_transcript(); + // prover + let mut transcript = as PermutationCheck>::init_transcript(); transcript.append_message(b"testing", b"initializing transcript for testing")?; + let (proof, q_x) = as PermutationCheck>::prove( + pcs_param, + fx, + gx, + s_perm, + &mut transcript, + )?; - let mut challenge = - as PermutationCheck>::generate_challenge(&mut transcript)?; + // verifier + let mut transcript = as PermutationCheck>::init_transcript(); + transcript.append_message(b"testing", b"initializing transcript for testing")?; + let perm_check_sum_claim = as PermutationCheck>::verify( + &proof, + &poly_info, + &mut transcript, + )?; - let prod_x_and_aux = - as PermutationCheck>::compute_prod_evals(&challenge, f, g, s_perm)?; + let prod_partial_evals = build_prod_partial_eval(&q_x)?; + let prod_0x = prod_partial_evals[0].clone(); + let prod_1x = prod_partial_evals[1].clone(); + let prod_x0 = prod_partial_evals[2].clone(); + let prod_x1 = prod_partial_evals[3].clone(); + + let (numerator, denominator) = computer_num_and_denom( + &perm_check_sum_claim.challenges.0, + &perm_check_sum_claim.challenges.1, + fx, + gx, + &s_perm, + )?; - let prod_x_binding: G1Affine = mock_commit(&prod_x_and_aux[0]); + // compute (g(x) + beta * s_perm(x) + gamma) * prod(0, x) * alpha + // which is prods[6] * prod[1] * alpha + let mut q_x = VirtualPolynomial::new_from_mle(&denominator, E::Fr::one()); + q_x.mul_by_mle( + prod_0x, + perm_check_sum_claim.product_check_sub_claim.challenge, + )?; - as PermutationCheck>::update_challenge( - &mut challenge, - &mut transcript, - &prod_x_binding, + // (g(x) + beta * s_perm(x) + gamma) * prod(0, x) * alpha + // - (f(x) + beta * s_id(x) + gamma) * alpha + q_x.add_mle_list( + [numerator], + -perm_check_sum_claim.product_check_sub_claim.challenge, )?; - let alpha = challenge.alpha.unwrap(); - prove_internal(&prod_x_and_aux, &alpha, &mut transcript) + // Q(x) := prod(1,x) - prod(x, 0) * prod(x, 1) + // + alpha * ( + // (g(x) + beta * s_perm(x) + gamma) * prod(0, x) + // - (f(x) + beta * s_id(x) + gamma)) + q_x.add_mle_list([prod_x0, prod_x1], -E::Fr::one())?; + q_x.add_mle_list([prod_1x], E::Fr::one())?; + + if q_x + .evaluate( + &perm_check_sum_claim + .product_check_sub_claim + .zero_check_sub_claim + .sum_check_sub_claim + .point, + ) + .unwrap() + != perm_check_sum_claim + .product_check_sub_claim + .zero_check_sub_claim + .sum_check_sub_claim + .expected_evaluation + { + return Err(PolyIOPErrors::InvalidVerifier("wrong subclaim".to_string())); + }; + + // test q_x is a 0 over boolean hypercube + for i in 0..1 << nv { + let bit_sequence = bit_decompose(i, nv); + let eval: Vec = bit_sequence + .iter() + .map(|x| E::Fr::from(*x as u64)) + .collect(); + let res = q_x.evaluate(&eval).unwrap(); + if !res.is_zero() {} + } + Ok(()) } fn test_permutation_check(nv: usize) -> Result<(), PolyIOPErrors> { let mut rng = test_rng(); - let poly_info = VPAuxInfo { - max_degree: 2, - num_variables: nv, - phantom: PhantomData::default(), - }; + let srs = KZGMultilinearPCS::::gen_srs_for_testing(&mut rng, nv + 1)?; + let (pcs_param, _) = KZGMultilinearPCS::::trim(&srs, nv + 1, Some(nv + 1))?; { // good path: w is a permutation of w itself under the identify map - let w = DenseMultilinearExtension::rand(nv, &mut rng); - + let w = Rc::new(DenseMultilinearExtension::rand(nv, &mut rng)); // s_perm is the identity map let s_perm = identity_permutation_mle(nv); - - let (proof, q_x) = test_permutation_check_helper(&w, &w, &s_perm)?; - - let mut transcript = as PermutationCheck>::init_transcript(); - transcript.append_message(b"testing", b"initializing transcript for testing")?; - let subclaim = - as PermutationCheck>::verify(&proof, &poly_info, &mut transcript)? - .zero_check_sub_claim; - assert_eq!( - q_x.evaluate(&subclaim.sum_check_sub_claim.point).unwrap(), - subclaim.sum_check_sub_claim.expected_evaluation, - "wrong subclaim" - ); - - // test q_x is a 0 over boolean hypercube - for i in 0..1 << nv { - let bit_sequence = bit_decompose(i, nv); - let eval: Vec = bit_sequence.iter().map(|x| Fr::from(*x as u64)).collect(); - let res = q_x.evaluate(&eval)?; - assert!(res.is_zero()) - } + test_permutation_check_helper::(&pcs_param, &w, &w, &s_perm)?; } { // bad path 1: w is a not permutation of w itself under a random map - let w = DenseMultilinearExtension::rand(nv, &mut rng); - + let w = Rc::new(DenseMultilinearExtension::rand(nv, &mut rng)); // s_perm is a random map let s_perm = random_permutation_mle(nv, &mut rng); - let (proof, q_x) = test_permutation_check_helper(&w, &w, &s_perm)?; - - let mut transcript = as PermutationCheck>::init_transcript(); - transcript.append_message(b"testing", b"initializing transcript for testing")?; - if nv != 1 { - assert!( as PermutationCheck>::verify( - &proof, - &poly_info, - &mut transcript + if nv == 1 { + test_permutation_check_helper::(&pcs_param, &w, &w, &s_perm)?; + } else { + assert!(test_permutation_check_helper::( + &pcs_param, &w, &w, &s_perm ) .is_err()); - } else { - // a trivial poly is always a permutation of itself, so the zero check should - // pass - let subclaim = as PermutationCheck>::verify( - &proof, - &poly_info, - &mut transcript, - )? - .zero_check_sub_claim; - - // the evaluation should fail because a different s_perm is used for proof and - // for w |-> w mapping - assert_ne!( - q_x.evaluate(&subclaim.sum_check_sub_claim.point).unwrap(), - subclaim.sum_check_sub_claim.expected_evaluation, - "wrong subclaim" - ); } } { // bad path 2: f is a not permutation of g under a identity map - let f = DenseMultilinearExtension::rand(nv, &mut rng); - let g = DenseMultilinearExtension::rand(nv, &mut rng); - + let f = Rc::new(DenseMultilinearExtension::rand(nv, &mut rng)); + let g = Rc::new(DenseMultilinearExtension::rand(nv, &mut rng)); // s_perm is the identity map let s_perm = identity_permutation_mle(nv); - let (proof, q_x) = test_permutation_check_helper(&f, &g, &s_perm)?; - - let mut transcript = as PermutationCheck>::init_transcript(); - transcript.append_message(b"testing", b"initializing transcript for testing")?; - if nv != 1 { - assert!( as PermutationCheck>::verify( - &proof, - &poly_info, - &mut transcript - ) - .is_err()); - } else { - // a trivial poly is always a permutation of itself, so the zero check should - // pass - let subclaim = as PermutationCheck>::verify( - &proof, - &poly_info, - &mut transcript, - )? - .zero_check_sub_claim; - - // the evaluation should fail because a different s_perm is used for proof and - // for f |-> g mapping - assert_ne!( - q_x.evaluate(&subclaim.sum_check_sub_claim.point).unwrap(), - subclaim.sum_check_sub_claim.expected_evaluation, - "wrong subclaim" - ); - } + assert!( + test_permutation_check_helper::(&pcs_param, &f, &g, &s_perm) + .is_err() + ); } Ok(()) @@ -626,46 +367,4 @@ mod test { assert!(test_permutation_check(0).is_err()); Ok(()) } - - #[test] - fn test_compute_prod() -> Result<(), PolyIOPErrors> { - let mut rng = test_rng(); - - for num_vars in 2..6 { - let f = DenseMultilinearExtension::rand(num_vars, &mut rng); - let g = DenseMultilinearExtension::rand(num_vars, &mut rng); - - let s_id = identity_permutation_mle::(num_vars); - let s_perm = random_permutation_mle(num_vars, &mut rng); - - let mut transcript = as PermutationCheck>::init_transcript(); - transcript.append_message(b"testing", b"initializing transcript for testing")?; - let challenge = - as PermutationCheck>::generate_challenge(&mut transcript)?; - - let prod_and_aux = as PermutationCheck>::compute_prod_evals( - &challenge, &f, &g, &s_perm, - )?; - let prod_partial_eval = build_prod_partial_eval(&prod_and_aux[0])?; - - for i in 0..1 << num_vars { - let r: Vec = bit_decompose(i, num_vars) - .iter() - .map(|&x| Fr::from(x)) - .collect(); - - let eval = prod_partial_eval[0].evaluate(&r).unwrap(); - - let f_eval = f.evaluate(&r).unwrap(); - let g_eval = g.evaluate(&r).unwrap(); - let s_id_eval = s_id.evaluate(&r).unwrap(); - let s_perm_eval = s_perm.evaluate(&r).unwrap(); - let eval_rec = (f_eval + challenge.beta * s_id_eval + challenge.gamma) - / (g_eval + challenge.beta * s_perm_eval + challenge.gamma); - - assert_eq!(eval, eval_rec); - } - } - Ok(()) - } } diff --git a/poly-iop/src/perm_check/util.rs b/poly-iop/src/perm_check/util.rs index d9265c8..35ccc65 100644 --- a/poly-iop/src/perm_check/util.rs +++ b/poly-iop/src/perm_check/util.rs @@ -1,9 +1,10 @@ //! This module implements useful functions for the permutation check protocol. -use crate::PolyIOPErrors; +use crate::errors::PolyIOPErrors; use ark_ff::PrimeField; use ark_poly::DenseMultilinearExtension; use ark_std::{end_timer, rand::RngCore, start_timer}; +use std::rc::Rc; /// Returns the evaluations of three MLEs: /// - prod(0,x) @@ -25,8 +26,7 @@ use ark_std::{end_timer, rand::RngCore, start_timer}; /// /// The caller needs to check num_vars matches in f/g/s_id/s_perm /// Cost: linear in N. -/// -/// TODO: replace `s_perm` with the merged poly `s`. +#[cfg(test)] #[allow(clippy::type_complexity)] pub(super) fn compute_prod_0( beta: &F, @@ -60,17 +60,74 @@ pub(super) fn compute_prod_0( Ok((prod_0x_evals, numerator_evals, denominator_evals)) } +/// Returns the evaluations of two MLEs: +/// - numerator +/// - denominator +/// +/// where +/// - beta and gamma are challenges +/// - f(x), g(x), s_id(x), s_perm(x) are mle-s +/// +/// - numerator is the MLE for `f(x) + \beta s_id(x) + \gamma` +/// - denominator is the MLE for `g(x) + \beta s_perm(x) + \gamma` +#[allow(clippy::type_complexity)] +pub(super) fn computer_num_and_denom( + beta: &F, + gamma: &F, + fx: &DenseMultilinearExtension, + gx: &DenseMultilinearExtension, + s_perm: &DenseMultilinearExtension, +) -> Result< + ( + Rc>, + Rc>, + ), + PolyIOPErrors, +> { + let start = start_timer!(|| "compute numerator and denominator"); + + let num_vars = fx.num_vars; + let mut numerator_evals = vec![]; + let mut denominator_evals = vec![]; + let s_id = identity_permutation_mle::(num_vars); + + for (&fi, (&gi, (&s_id_i, &s_perm_i))) in + fx.iter().zip(gx.iter().zip(s_id.iter().zip(s_perm.iter()))) + { + let numerator = fi + *beta * s_id_i + gamma; + let denominator = gi + *beta * s_perm_i + gamma; + + numerator_evals.push(numerator); + denominator_evals.push(denominator); + } + let numerator = Rc::new(DenseMultilinearExtension::from_evaluations_vec( + num_vars, + numerator_evals, + )); + let denominator = Rc::new(DenseMultilinearExtension::from_evaluations_vec( + num_vars, + denominator_evals, + )); + + end_timer!(start); + Ok((numerator, denominator)) +} + /// An MLE that represent an identity permutation: `f(index) \mapto index` -pub fn identity_permutation_mle(num_vars: usize) -> DenseMultilinearExtension { +pub fn identity_permutation_mle( + num_vars: usize, +) -> Rc> { let s_id_vec = (0..1u64 << num_vars).map(F::from).collect(); - DenseMultilinearExtension::from_evaluations_vec(num_vars, s_id_vec) + Rc::new(DenseMultilinearExtension::from_evaluations_vec( + num_vars, s_id_vec, + )) } /// An MLE that represent a random permutation pub fn random_permutation_mle( num_vars: usize, rng: &mut R, -) -> DenseMultilinearExtension { +) -> Rc> { let len = 1u64 << num_vars; let mut s_id_vec: Vec = (0..len).map(F::from).collect(); let mut s_perm_vec = vec![]; @@ -78,7 +135,9 @@ pub fn random_permutation_mle( let index = rng.next_u64() as usize % s_id_vec.len(); s_perm_vec.push(s_id_vec.remove(index)); } - DenseMultilinearExtension::from_evaluations_vec(num_vars, s_perm_vec) + Rc::new(DenseMultilinearExtension::from_evaluations_vec( + num_vars, s_perm_vec, + )) } /// Helper function of the IOP. @@ -91,22 +150,25 @@ pub fn random_permutation_mle( /// - prod(1, x) /// - prod(x, 0) /// - prod(x, 1) +#[cfg(test)] pub(super) fn build_prod_partial_eval( - prod_x: &DenseMultilinearExtension, -) -> Result<[DenseMultilinearExtension; 4], PolyIOPErrors> { + prod_x: &Rc>, +) -> Result<[Rc>; 4], PolyIOPErrors> { let start = start_timer!(|| "build prod polynomial"); let prod_x_eval = &prod_x.evaluations; let num_vars = prod_x.num_vars - 1; // prod(0, x) - let prod_0_x = - DenseMultilinearExtension::from_evaluations_slice(num_vars, &prod_x_eval[0..1 << num_vars]); + let prod_0_x = Rc::new(DenseMultilinearExtension::from_evaluations_slice( + num_vars, + &prod_x_eval[0..1 << num_vars], + )); // prod(1, x) - let prod_1_x = DenseMultilinearExtension::from_evaluations_slice( + let prod_1_x = Rc::new(DenseMultilinearExtension::from_evaluations_slice( num_vars, &prod_x_eval[1 << num_vars..1 << (num_vars + 1)], - ); + )); // =================================== // prod(x, 0) and prod(x, 1) @@ -124,8 +186,12 @@ pub(super) fn build_prod_partial_eval( eval_x1.push(prod_x); } } - let prod_x_0 = DenseMultilinearExtension::from_evaluations_vec(num_vars, eval_x0); - let prod_x_1 = DenseMultilinearExtension::from_evaluations_vec(num_vars, eval_x1); + let prod_x_0 = Rc::new(DenseMultilinearExtension::from_evaluations_vec( + num_vars, eval_x0, + )); + let prod_x_1 = Rc::new(DenseMultilinearExtension::from_evaluations_vec( + num_vars, eval_x1, + )); end_timer!(start); diff --git a/poly-iop/src/prelude.rs b/poly-iop/src/prelude.rs index 0d13d05..634a196 100644 --- a/poly-iop/src/prelude.rs +++ b/poly-iop/src/prelude.rs @@ -4,6 +4,7 @@ pub use crate::{ util::{identity_permutation_mle, random_permutation_mle}, PermutationCheck, }, + prod_check::ProductCheck, sum_check::SumCheck, utils::*, zero_check::ZeroCheck, diff --git a/poly-iop/src/prod_check/mod.rs b/poly-iop/src/prod_check/mod.rs index 9fab5ba..8d9952e 100644 --- a/poly-iop/src/prod_check/mod.rs +++ b/poly-iop/src/prod_check/mod.rs @@ -3,7 +3,8 @@ use crate::{ errors::PolyIOPErrors, prod_check::util::{compute_product_poly, prove_zero_check}, - PolyIOP, ZeroCheck, + zero_check::ZeroCheck, + PolyIOP, }; use arithmetic::VPAuxInfo; use ark_ec::PairingEngine; @@ -11,7 +12,7 @@ use ark_ff::{One, PrimeField, Zero}; use ark_poly::DenseMultilinearExtension; use ark_std::{end_timer, start_timer}; use pcs::prelude::PolynomialCommitmentScheme; -use std::{marker::PhantomData, rc::Rc}; +use std::rc::Rc; use transcript::IOPTranscript; mod util; @@ -42,8 +43,7 @@ where PCS: PolynomialCommitmentScheme, { type ProductCheckSubClaim; - type ProductProof; - type Polynomial; + type ProductCheckProof; /// Initialize the system with a transcript /// @@ -69,17 +69,17 @@ where /// /// Cost: O(N) fn prove( - fx: &Self::Polynomial, - gx: &Self::Polynomial, + pcs_param: &PCS::ProverParam, + fx: &Self::MultilinearExtension, + gx: &Self::MultilinearExtension, transcript: &mut IOPTranscript, - pk: &PCS::ProverParam, - ) -> Result<(Self::ProductProof, Self::Polynomial), PolyIOPErrors>; + ) -> Result<(Self::ProductCheckProof, Self::MultilinearExtension), PolyIOPErrors>; /// Verify that for witness multilinear polynomials f(x), g(x) /// it holds that `\prod_{x \in {0,1}^n} f(x) = \prod_{x \in {0,1}^n} g(x)` fn verify( - proof: &Self::ProductProof, - num_vars: usize, + proof: &Self::ProductCheckProof, + aux_info: &VPAuxInfo, transcript: &mut Self::Transcript, ) -> Result; } @@ -98,23 +98,26 @@ where #[derive(Clone, Debug, Default, PartialEq)] pub struct ProductCheckSubClaim> { // the SubClaim from the ZeroCheck - zero_check_sub_claim: ZC::ZeroCheckSubClaim, + pub zero_check_sub_claim: ZC::ZeroCheckSubClaim, // final query which consists of // - the vector `(1, ..., 1, 0)` (needs to be reversed because Arkwork's MLE uses big-endian // format for points) // The expected final query evaluation is 1 final_query: (Vec, F), - challenge: F, + pub challenge: F, } /// A product check proof consists of /// - a zerocheck proof /// - a product polynomial commitment #[derive(Clone, Debug, Default, PartialEq)] -pub struct ProductProof, ZC: ZeroCheck> -{ - zero_check_proof: ZC::ZeroCheckProof, - prod_x_comm: PCS::Commitment, +pub struct ProductCheckProof< + E: PairingEngine, + PCS: PolynomialCommitmentScheme, + ZC: ZeroCheck, +> { + pub zero_check_proof: ZC::ZeroCheckProof, + pub prod_x_comm: PCS::Commitment, } impl ProductCheck for PolyIOP @@ -123,19 +126,18 @@ where PCS: PolynomialCommitmentScheme>>, { type ProductCheckSubClaim = ProductCheckSubClaim; - type ProductProof = ProductProof; - type Polynomial = Rc>; + type ProductCheckProof = ProductCheckProof; fn init_transcript() -> Self::Transcript { IOPTranscript::::new(b"Initializing ProductCheck transcript") } fn prove( - fx: &Self::Polynomial, - gx: &Self::Polynomial, + pcs_param: &PCS::ProverParam, + fx: &Self::MultilinearExtension, + gx: &Self::MultilinearExtension, transcript: &mut IOPTranscript, - pk: &PCS::ProverParam, - ) -> Result<(Self::ProductProof, Self::Polynomial), PolyIOPErrors> { + ) -> Result<(Self::ProductCheckProof, Self::MultilinearExtension), PolyIOPErrors> { let start = start_timer!(|| "prod_check prove"); if fx.num_vars != gx.num_vars { @@ -148,7 +150,7 @@ where let prod_x = compute_product_poly(fx, gx)?; // generate challenge - let prod_x_comm = PCS::commit(pk, &Rc::new(prod_x.clone()))?; + let prod_x_comm = PCS::commit(pcs_param, &prod_x)?; transcript.append_serializable_element(b"prod(x)", &prod_x_comm)?; let alpha = transcript.get_and_append_challenge(b"alpha")?; @@ -158,17 +160,17 @@ where end_timer!(start); Ok(( - ProductProof { + ProductCheckProof { zero_check_proof, prod_x_comm, }, - Rc::new(prod_x.clone()), + prod_x, )) } fn verify( - proof: &Self::ProductProof, - num_vars: usize, + proof: &Self::ProductCheckProof, + aux_info: &VPAuxInfo, transcript: &mut Self::Transcript, ) -> Result { let start = start_timer!(|| "prod_check verify"); @@ -179,13 +181,8 @@ where // invoke the zero check on the iop_proof // the virtual poly info for Q(x) - let aux_info = VPAuxInfo { - max_degree: 2, - num_variables: num_vars, - phantom: PhantomData::default(), - }; let zero_check_sub_claim = - >::verify(&proof.zero_check_proof, &aux_info, transcript)?; + >::verify(&proof.zero_check_proof, aux_info, transcript)?; // the final query is on prod_x, hence has length `num_vars` + 1 let mut final_query = vec![E::Fr::one(); aux_info.num_variables + 1]; @@ -207,18 +204,19 @@ where mod test { use super::ProductCheck; use crate::{errors::PolyIOPErrors, PolyIOP}; + use arithmetic::VPAuxInfo; use ark_bls12_381::{Bls12_381, Fr}; use ark_ec::PairingEngine; use ark_poly::{DenseMultilinearExtension, MultilinearExtension}; use ark_std::test_rng; use pcs::{prelude::KZGMultilinearPCS, PolynomialCommitmentScheme}; - use std::rc::Rc; + use std::{marker::PhantomData, rc::Rc}; // f and g are guaranteed to have the same product fn test_product_check_helper( f: &DenseMultilinearExtension, g: &DenseMultilinearExtension, - pk: &PCS::ProverParam, + pcs_param: &PCS::ProverParam, ) -> Result<(), PolyIOPErrors> where E: PairingEngine, @@ -228,17 +226,22 @@ mod test { transcript.append_message(b"testing", b"initializing transcript for testing")?; let (proof, prod_x) = as ProductCheck>::prove( + pcs_param, &Rc::new(f.clone()), &Rc::new(g.clone()), &mut transcript, - pk, )?; let mut transcript = as ProductCheck>::init_transcript(); transcript.append_message(b"testing", b"initializing transcript for testing")?; + let aux_info = VPAuxInfo { + max_degree: 2, + num_variables: f.num_vars, + phantom: PhantomData::default(), + }; let subclaim = - as ProductCheck>::verify(&proof, f.num_vars, &mut transcript)?; + as ProductCheck>::verify(&proof, &aux_info, &mut transcript)?; assert_eq!( prod_x.evaluate(&subclaim.final_query.0).unwrap(), subclaim.final_query.1, @@ -251,17 +254,17 @@ mod test { let h = f + g; let (bad_proof, prod_x_bad) = as ProductCheck>::prove( + pcs_param, &Rc::new(f.clone()), &Rc::new(h.clone()), &mut transcript, - pk, )?; let mut transcript = as ProductCheck>::init_transcript(); transcript.append_message(b"testing", b"initializing transcript for testing")?; let bad_subclaim = as ProductCheck>::verify( &bad_proof, - f.num_vars, + &aux_info, &mut transcript, )?; assert_ne!( @@ -281,9 +284,9 @@ mod test { g.evaluations.reverse(); let srs = KZGMultilinearPCS::::gen_srs_for_testing(&mut rng, nv + 1)?; - let (pk, _) = KZGMultilinearPCS::::trim(&srs, nv + 1, Some(nv + 1))?; + let (pcs_param, _) = KZGMultilinearPCS::::trim(&srs, nv + 1, Some(nv + 1))?; - test_product_check_helper::>(&f, &g, &pk)?; + test_product_check_helper::>(&f, &g, &pcs_param)?; Ok(()) } diff --git a/poly-iop/src/prod_check/util.rs b/poly-iop/src/prod_check/util.rs index 79ca129..cb645f7 100644 --- a/poly-iop/src/prod_check/util.rs +++ b/poly-iop/src/prod_check/util.rs @@ -1,6 +1,8 @@ //! This module implements useful functions for the product check protocol. -use crate::{errors::PolyIOPErrors, structs::IOPProof, utils::get_index, PolyIOP, ZeroCheck}; +use crate::{ + errors::PolyIOPErrors, structs::IOPProof, utils::get_index, zero_check::ZeroCheck, PolyIOP, +}; use arithmetic::VirtualPolynomial; use ark_ff::PrimeField; use ark_poly::DenseMultilinearExtension; @@ -19,9 +21,9 @@ use transcript::IOPTranscript; /// The caller needs to check num_vars matches in f and g /// Cost: linear in N. pub(super) fn compute_product_poly( - fx: &DenseMultilinearExtension, - gx: &DenseMultilinearExtension, -) -> Result, PolyIOPErrors> { + fx: &Rc>, + gx: &Rc>, +) -> Result>, PolyIOPErrors> { let start = start_timer!(|| "compute evaluations of prod polynomial"); let num_vars = fx.num_vars; @@ -69,7 +71,10 @@ pub(super) fn compute_product_poly( // prod(x)'s evaluation is indeed `e := [eval_0x[..], eval_1x[..]].concat()` let eval = [prod_0x_eval.as_slice(), prod_1x_eval.as_slice()].concat(); - let prod_x = DenseMultilinearExtension::from_evaluations_vec(num_vars + 1, eval); + let prod_x = Rc::new(DenseMultilinearExtension::from_evaluations_vec( + num_vars + 1, + eval, + )); end_timer!(start); Ok(prod_x) @@ -82,21 +87,19 @@ pub(super) fn compute_product_poly( /// /// Cost: O(N) pub(super) fn prove_zero_check( - fx: &DenseMultilinearExtension, - gx: &DenseMultilinearExtension, - prod_x: &DenseMultilinearExtension, + fx: &Rc>, + gx: &Rc>, + prod_x: &Rc>, alpha: &F, transcript: &mut IOPTranscript, ) -> Result<(IOPProof, VirtualPolynomial), PolyIOPErrors> { let start = start_timer!(|| "zerocheck in product check"); let prod_partial_evals = build_prod_partial_eval(prod_x)?; - let prod_0x = Rc::new(prod_partial_evals[0].clone()); - let prod_1x = Rc::new(prod_partial_evals[1].clone()); - let prod_x0 = Rc::new(prod_partial_evals[2].clone()); - let prod_x1 = Rc::new(prod_partial_evals[3].clone()); - let fx = Rc::new(fx.clone()); - let gx = Rc::new(gx.clone()); + let prod_0x = prod_partial_evals[0].clone(); + let prod_1x = prod_partial_evals[1].clone(); + let prod_x0 = prod_partial_evals[2].clone(); + let prod_x1 = prod_partial_evals[3].clone(); // compute g(x) * prod(0, x) * alpha let mut q_x = VirtualPolynomial::new_from_mle(gx, F::one()); @@ -104,7 +107,7 @@ pub(super) fn prove_zero_check( // g(x) * prod(0, x) * alpha // - f(x) * alpha - q_x.add_mle_list([fx], -*alpha)?; + q_x.add_mle_list([fx.clone()], -*alpha)?; // Q(x) := prod(1,x) - prod(x, 0) * prod(x, 1) // + alpha * ( @@ -130,21 +133,23 @@ pub(super) fn prove_zero_check( /// - prod(x, 0) /// - prod(x, 1) fn build_prod_partial_eval( - prod_x: &DenseMultilinearExtension, -) -> Result<[DenseMultilinearExtension; 4], PolyIOPErrors> { + prod_x: &Rc>, +) -> Result<[Rc>; 4], PolyIOPErrors> { let start = start_timer!(|| "build partial prod polynomial"); let prod_x_eval = &prod_x.evaluations; let num_vars = prod_x.num_vars - 1; // prod(0, x) - let prod_0_x = - DenseMultilinearExtension::from_evaluations_slice(num_vars, &prod_x_eval[0..1 << num_vars]); + let prod_0_x = Rc::new(DenseMultilinearExtension::from_evaluations_slice( + num_vars, + &prod_x_eval[0..1 << num_vars], + )); // prod(1, x) - let prod_1_x = DenseMultilinearExtension::from_evaluations_slice( + let prod_1_x = Rc::new(DenseMultilinearExtension::from_evaluations_slice( num_vars, &prod_x_eval[1 << num_vars..1 << (num_vars + 1)], - ); + )); // =================================== // prod(x, 0) and prod(x, 1) @@ -162,8 +167,12 @@ fn build_prod_partial_eval( eval_x1.push(prod_x); } } - let prod_x_0 = DenseMultilinearExtension::from_evaluations_vec(num_vars, eval_x0); - let prod_x_1 = DenseMultilinearExtension::from_evaluations_vec(num_vars, eval_x1); + let prod_x_0 = Rc::new(DenseMultilinearExtension::from_evaluations_vec( + num_vars, eval_x0, + )); + let prod_x_1 = Rc::new(DenseMultilinearExtension::from_evaluations_vec( + num_vars, eval_x1, + )); end_timer!(start); diff --git a/poly-iop/src/structs.rs b/poly-iop/src/structs.rs index 741e177..9990c06 100644 --- a/poly-iop/src/structs.rs +++ b/poly-iop/src/structs.rs @@ -8,7 +8,7 @@ use ark_serialize::{CanonicalSerialize, SerializationError, Write}; /// - messages from prover to verifier at each round through the interactive /// protocol. /// - a point that is generated by the transcript for evaluation -#[derive(Clone, Debug, Default, PartialEq)] +#[derive(Clone, Debug, Default, PartialEq, Eq)] pub struct IOPProof { pub point: Vec, pub proofs: Vec>, diff --git a/poly-iop/src/sum_check/mod.rs b/poly-iop/src/sum_check/mod.rs index b132b12..f164b7e 100644 --- a/poly-iop/src/sum_check/mod.rs +++ b/poly-iop/src/sum_check/mod.rs @@ -9,6 +9,7 @@ use arithmetic::{VPAuxInfo, VirtualPolynomial}; use ark_ff::PrimeField; use ark_poly::DenseMultilinearExtension; use ark_std::{end_timer, start_timer}; +use std::{fmt::Debug, rc::Rc}; use transcript::IOPTranscript; mod prover; @@ -20,9 +21,9 @@ pub trait SumCheck { type VPAuxInfo; type MultilinearExtension; - type SumCheckProof; + type SumCheckProof: Clone + Debug + Default + PartialEq; type Transcript; - type SumCheckSubClaim; + type SumCheckSubClaim: Clone + Debug + Default + PartialEq; /// Extract sum from the proof fn extract_sum(proof: &Self::SumCheckProof) -> F; @@ -126,7 +127,7 @@ impl SumCheck for PolyIOP { type SumCheckProof = IOPProof; type VirtualPolynomial = VirtualPolynomial; type VPAuxInfo = VPAuxInfo; - type MultilinearExtension = DenseMultilinearExtension; + type MultilinearExtension = Rc>; type SumCheckSubClaim = SumCheckSubClaim; type Transcript = IOPTranscript; diff --git a/poly-iop/src/zero_check/mod.rs b/poly-iop/src/zero_check/mod.rs index 290831c..a6eaa0a 100644 --- a/poly-iop/src/zero_check/mod.rs +++ b/poly-iop/src/zero_check/mod.rs @@ -1,5 +1,7 @@ //! Main module for the ZeroCheck protocol. +use std::fmt::Debug; + use crate::{errors::PolyIOPErrors, sum_check::SumCheck, PolyIOP}; use arithmetic::build_eq_x_r; use ark_ff::PrimeField; @@ -22,8 +24,8 @@ pub struct ZeroCheckSubClaim> { /// A ZeroCheck is derived from SumCheck. pub trait ZeroCheck: SumCheck { - type ZeroCheckSubClaim; - type ZeroCheckProof; + type ZeroCheckSubClaim: Clone + Debug + Default + PartialEq; + type ZeroCheckProof: Clone + Debug + Default + PartialEq; /// Initialize the system with a transcript ///