From bd1fc9897ecf3a709d33a235810c625bf83fe411 Mon Sep 17 00:00:00 2001 From: Charles Chen Date: Wed, 9 Nov 2022 21:03:25 -0500 Subject: [PATCH] refactor snark --- arithmetic/src/lib.rs | 4 +- arithmetic/src/multilinear_polynomial.rs | 20 +- hyperplonk/src/mock.rs | 5 +- hyperplonk/src/snark.rs | 474 ++++++++++----------- hyperplonk/src/structs.rs | 28 +- hyperplonk/src/utils.rs | 39 ++ subroutines/benches/iop_bench.rs | 38 +- subroutines/src/poly_iop/perm_check/mod.rs | 28 +- 8 files changed, 343 insertions(+), 293 deletions(-) diff --git a/arithmetic/src/lib.rs b/arithmetic/src/lib.rs index a05acaa..2fc2b5f 100644 --- a/arithmetic/src/lib.rs +++ b/arithmetic/src/lib.rs @@ -7,8 +7,8 @@ mod virtual_polynomial; pub use errors::ArithErrors; pub use multilinear_polynomial::{ evaluate_no_par, evaluate_opt, fix_last_variables, fix_last_variables_no_par, fix_variables, - identity_permutation_mles, merge_polynomials, random_mle_list, random_permutation_mles, - random_zero_mle_list, DenseMultilinearExtension, + identity_permutation, identity_permutation_mles, merge_polynomials, random_mle_list, + random_permutation, random_permutation_mles, random_zero_mle_list, DenseMultilinearExtension, }; pub use univariate_polynomial::{build_l, get_uni_domain}; pub use util::{bit_decompose, gen_eval_point, get_batched_nv, get_index}; diff --git a/arithmetic/src/multilinear_polynomial.rs b/arithmetic/src/multilinear_polynomial.rs index cda1247..3b7a4be 100644 --- a/arithmetic/src/multilinear_polynomial.rs +++ b/arithmetic/src/multilinear_polynomial.rs @@ -72,6 +72,11 @@ pub fn random_zero_mle_list( list } +pub fn identity_permutation(num_vars: usize, num_chunks: usize) -> Vec { + let len = (num_chunks as u64) * (1u64 << num_vars); + (0..len).map(F::from).collect() +} + /// A list of MLEs that represents an identity permutation pub fn identity_permutation_mles( num_vars: usize, @@ -88,12 +93,11 @@ pub fn identity_permutation_mles( res } -/// A list of MLEs that represent a random permutation -pub fn random_permutation_mles( +pub fn random_permutation( num_vars: usize, num_chunks: usize, rng: &mut R, -) -> Vec>> { +) -> Vec { let len = (num_chunks as u64) * (1u64 << num_vars); let mut s_id_vec: Vec = (0..len).map(F::from).collect(); let mut s_perm_vec = vec![]; @@ -101,6 +105,16 @@ pub fn random_permutation_mles( let index = rng.next_u64() as usize % s_id_vec.len(); s_perm_vec.push(s_id_vec.remove(index)); } + s_perm_vec +} + +/// A list of MLEs that represent a random permutation +pub fn random_permutation_mles( + num_vars: usize, + num_chunks: usize, + rng: &mut R, +) -> Vec>> { + let s_perm_vec = random_permutation(num_vars, num_chunks, rng); let mut res = vec![]; let n = 1 << num_vars; for i in 0..num_chunks { diff --git a/hyperplonk/src/mock.rs b/hyperplonk/src/mock.rs index c605224..6af98a2 100644 --- a/hyperplonk/src/mock.rs +++ b/hyperplonk/src/mock.rs @@ -1,6 +1,5 @@ -use arithmetic::identity_permutation_mles; +use arithmetic::identity_permutation; use ark_ff::PrimeField; -use ark_poly::MultilinearExtension; use ark_std::{log2, test_rng}; use crate::{ @@ -93,7 +92,7 @@ impl MockCircuit { gate_func: gate.clone(), }; - let permutation = identity_permutation_mles(merged_nv as usize, 1)[0].to_evaluations(); + let permutation = identity_permutation(merged_nv as usize, 1); let index = HyperPlonkIndex { params, permutation, diff --git a/hyperplonk/src/snark.rs b/hyperplonk/src/snark.rs index a2581aa..3e5fd85 100644 --- a/hyperplonk/src/snark.rs +++ b/hyperplonk/src/snark.rs @@ -1,14 +1,14 @@ use crate::{ errors::HyperPlonkErrors, structs::{HyperPlonkIndex, HyperPlonkProof, HyperPlonkProvingKey, HyperPlonkVerifyingKey}, - utils::{build_f, eval_f, prover_sanity_check, PcsAccumulator}, + utils::{build_f, eval_f, eval_perm_gate, prover_sanity_check, PcsAccumulator}, witness::WitnessColumn, HyperPlonkSNARK, }; -use arithmetic::{evaluate_opt, identity_permutation_mles, merge_polynomials, VPAuxInfo}; +use arithmetic::{evaluate_opt, identity_permutation_mles, VPAuxInfo}; use ark_ec::PairingEngine; use ark_poly::DenseMultilinearExtension; -use ark_std::{end_timer, log2, start_timer, One, Zero}; +use ark_std::{end_timer, log2, println, start_timer, One, Zero}; use std::{marker::PhantomData, rc::Rc}; use subroutines::{ pcs::prelude::{Commitment, PolynomialCommitmentScheme}, @@ -45,25 +45,32 @@ where pcs_srs: &PCS::SRS, ) -> Result<(Self::ProvingKey, Self::VerifyingKey), HyperPlonkErrors> { let num_vars = index.num_variables(); - - let log_num_witness_polys = log2(index.num_witness_columns()) as usize; - let witness_merged_nv = num_vars + log_num_witness_polys; - - let log_chunk_size = log_num_witness_polys + 1; - let prod_x_nv = num_vars + log_chunk_size; - - let supported_ml_degree = prod_x_nv; + let supported_ml_degree = num_vars; // extract PCS prover and verifier keys from SRS let (pcs_prover_param, pcs_verifier_param) = PCS::trim(pcs_srs, None, Some(supported_ml_degree))?; + // build identity oracles + let id_oracles = identity_permutation_mles(num_vars, index.num_witness_columns()); + let mut id_comms = vec![]; + for id_oracle in id_oracles.iter() { + id_comms.push(PCS::commit(&pcs_prover_param, id_oracle)?); + } + // build permutation oracles - let permutation_oracle = Rc::new(DenseMultilinearExtension::from_evaluations_slice( - witness_merged_nv, - &index.permutation, - )); - let perm_com = PCS::commit(&pcs_prover_param, &permutation_oracle)?; + let mut permutation_oracles = vec![]; + let mut perm_comms = vec![]; + let chunk_size = 1 << num_vars; + for i in 0..index.num_witness_columns() { + let perm_oracle = Rc::new(DenseMultilinearExtension::from_evaluations_slice( + num_vars, + &index.permutation[i * chunk_size..(i + 1) * chunk_size], + )); + let perm_comm = PCS::commit(&pcs_prover_param, &perm_oracle)?; + permutation_oracles.push(perm_oracle); + perm_comms.push(perm_comm); + } // build selector oracles and commit to it let selector_oracles: Vec>> = index @@ -77,23 +84,23 @@ where .map(|poly| PCS::commit(&pcs_prover_param, poly)) .collect::, _>>()?; - // let selector_merged = merge_polynomials(&selector_oracles)?; - // let selector_com = PCS::commit(&pcs_prover_param, &selector_merged)?; - Ok(( Self::ProvingKey { params: index.params.clone(), - permutation_oracle: permutation_oracle.clone(), + id_oracles, + permutation_oracles, selector_oracles, selector_commitments: selector_commitments.clone(), + permutation_commitments: perm_comms.clone(), + id_commitments: id_comms.clone(), pcs_param: pcs_prover_param, }, Self::VerifyingKey { params: index.params.clone(), - permutation_oracle, pcs_param: pcs_verifier_param, selector_commitments, - perm_com, + perm_commitments: perm_comms, + id_commitments: id_comms, }, )) } @@ -157,30 +164,13 @@ where // witness assignment of length 2^n let num_vars = pk.params.num_variables(); - let log_num_witness_polys = log2(pk.params.num_witness_columns()) as usize; - let merged_nv = num_vars + log_num_witness_polys; - - // number of nv in prod(x) which is supposed to be the cap - // so each chunk we we store maximum 1 << (prod_x_nv - num_var) selectors - let log_chunk_size = log_num_witness_polys + 1; - let prod_x_nv = num_vars + log_chunk_size; // online public input of length 2^\ell let ell = log2(pk.params.num_pub_input) as usize; // We use accumulators to store the polynomials and their eval points. // They are batch opened at a later stage. - // This includes - // - witnesses - // - prod(x) - // - selectors - // - // Accumulator's nv is bounded by prod(x) that means - // we need to split the selectors into multiple chunks if - // #selectors > chunk_size - // let mut pcs_acc = PcsAccumulator::::new(prod_x_nv); - let mut prod_x_pcs_acc = PcsAccumulator::::new(prod_x_nv); - let mut witness_and_selector_x_pcs_acc = PcsAccumulator::::new(num_vars); + let mut pcs_acc = PcsAccumulator::::new(num_vars); // ======================================================================= // 1. Commit Witness polynomials `w_i(x)` and append commitment to @@ -197,21 +187,10 @@ where .iter() .map(|x| PCS::commit(&pk.pcs_param, x).unwrap()) .collect::>(); - - // merge all witness into a single MLE - we will run perm check on it - // to obtain prod(x) - let w_merged = 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 ({})", - w_merged.num_vars, merged_nv - ))); + for w_com in witness_commits.iter() { + transcript.append_serializable_element(b"w", w_com)?; } - // TODO: we'll remove one of witness_merged_commit and witness_commits later. - let witness_merged_commit = PCS::commit(&pk.pcs_param, &w_merged)?; - transcript.append_serializable_element(b"w", &witness_merged_commit)?; - end_timer!(step); // ======================================================================= // 2 Run ZeroCheck on @@ -242,11 +221,11 @@ where // ======================================================================= let step = start_timer!(|| "Permutation check on w_i(x)"); - let (perm_check_proof, prod_x) = >::prove( + let (perm_check_proof, prod_x, frac_poly) = >::prove( &pk.pcs_param, - &[w_merged.clone()], - &[w_merged.clone()], - &[pk.permutation_oracle.clone()], + &witness_polys, + &witness_polys, + &pk.permutation_oracles, &mut transcript, )?; let perm_check_point = &perm_check_proof.zero_check_proof.point; @@ -254,17 +233,24 @@ where end_timer!(step); // ======================================================================= // 4. Generate evaluations and corresponding proofs - // - 4.1. (deferred) batch opening prod(x) at - // - [0, perm_check_point] - // - [1, perm_check_point] - // - [perm_check_point, 0] - // - [perm_check_point, 1] + // - permcheck + // 1. (deferred) batch opening prod(x) at + // - [perm_check_point] + // - [perm_check_point[2..n], 0] + // - [perm_check_point[2..n], 1] // - [1,...1, 0] + // 2. (deferred) batch opening frac(x) at + // - [perm_check_point] + // - [perm_check_point[2..n], 0] + // - [perm_check_point[2..n], 1] + // 3. (deferred) batch opening s_id(x) at + // - [perm_check_point] + // 4. (deferred) batch opening perms(x) at + // - [perm_check_point] + // 5. (deferred) batch opening witness_i(x) at + // - [perm_check_point] // - // - 4.2. permutation check evaluations and proofs - // - 4.2.1. (deferred) wi_poly(perm_check_point) - // - // - 4.3. zero check evaluations and proofs + // - zero check evaluations and proofs // - 4.3.1. (deferred) wi_poly(zero_check_point) // - 4.3.2. (deferred) selector_poly(zero_check_point) // @@ -273,38 +259,58 @@ where // ======================================================================= let step = start_timer!(|| "opening and evaluations"); - // 4.1 (deferred) open prod(0,x), prod(1, x), prod(x, 0), prod(x, 1) - // perm_check_point - // prod(0, x) - let tmp_point1 = [perm_check_point.as_slice(), &[E::Fr::zero()]].concat(); - // prod(1, x) - let tmp_point2 = [perm_check_point.as_slice(), &[E::Fr::one()]].concat(); - // prod(x, 0) - let tmp_point3 = [&[E::Fr::zero()], perm_check_point.as_slice()].concat(); - // prod(x, 1) - let tmp_point4 = [&[E::Fr::one()], perm_check_point.as_slice()].concat(); - // // prod(1, ..., 1, 0) - // let tmp_point5 = [vec![E::Fr::zero()], vec![E::Fr::one(); - // merged_nv]].concat(); - - prod_x_pcs_acc.insert_poly_and_points(&prod_x, &perm_check_proof.prod_x_comm, &tmp_point1); - prod_x_pcs_acc.insert_poly_and_points(&prod_x, &perm_check_proof.prod_x_comm, &tmp_point2); - prod_x_pcs_acc.insert_poly_and_points(&prod_x, &perm_check_proof.prod_x_comm, &tmp_point3); - prod_x_pcs_acc.insert_poly_and_points(&prod_x, &perm_check_proof.prod_x_comm, &tmp_point4); - - // 4.2 permutation check - // - 4.2.1. wi_poly(perm_check_point) - let (perm_check_opening, perm_check_eval) = - PCS::open(&pk.pcs_param, &w_merged, perm_check_point)?; - - // - 4.3. zero check evaluations and proofs - // - 4.3.1 (deferred) wi_poly(zero_check_point) - for i in 0..witness_polys.len() { - witness_and_selector_x_pcs_acc.insert_poly_and_points( - &witness_polys[i], - &witness_commits[i], - &zero_check_proof.point, - ); + // (perm_check_point[2..n], 0) + let perm_check_point_0 = [&[E::Fr::zero()], &perm_check_point[0..num_vars - 1]].concat(); + // (perm_check_point[2..n], 1) + let perm_check_point_1 = [&[E::Fr::one()], &perm_check_point[0..num_vars - 1]].concat(); + // (1, ..., 1, 0) + let prod_final_query_point = + [vec![E::Fr::zero()], vec![E::Fr::one(); num_vars - 1]].concat(); + + // prod(x)'s points + pcs_acc.insert_poly_and_points(&prod_x, &perm_check_proof.prod_x_comm, perm_check_point); + pcs_acc.insert_poly_and_points(&prod_x, &perm_check_proof.prod_x_comm, &perm_check_point_0); + pcs_acc.insert_poly_and_points(&prod_x, &perm_check_proof.prod_x_comm, &perm_check_point_1); + pcs_acc.insert_poly_and_points( + &prod_x, + &perm_check_proof.prod_x_comm, + &prod_final_query_point, + ); + + // frac(x)'s points + pcs_acc.insert_poly_and_points(&frac_poly, &perm_check_proof.frac_comm, perm_check_point); + pcs_acc.insert_poly_and_points( + &frac_poly, + &perm_check_proof.frac_comm, + &perm_check_point_0, + ); + pcs_acc.insert_poly_and_points( + &frac_poly, + &perm_check_proof.frac_comm, + &perm_check_point_1, + ); + + // s_id(x)'s points + for (s_id, s_com) in pk.id_oracles.iter().zip(pk.id_commitments.iter()) { + pcs_acc.insert_poly_and_points(s_id, s_com, perm_check_point); + } + + // perms(x)'s points + for (perm, pcom) in pk + .permutation_oracles + .iter() + .zip(pk.permutation_commitments.iter()) + { + pcs_acc.insert_poly_and_points(perm, pcom, perm_check_point); + } + + // witnesses' points + // TODO: refactor so it remains correct even if the order changed + for (wpoly, wcom) in witness_polys.iter().zip(witness_commits.iter()) { + pcs_acc.insert_poly_and_points(wpoly, wcom, perm_check_point); + } + for (wpoly, wcom) in witness_polys.iter().zip(witness_commits.iter()) { + pcs_acc.insert_poly_and_points(wpoly, wcom, &zero_check_proof.point); } // - 4.3.2. (deferred) selector_poly(zero_check_point) @@ -312,48 +318,30 @@ where .iter() .zip(pk.selector_commitments.iter()) .for_each(|(poly, com)| { - witness_and_selector_x_pcs_acc.insert_poly_and_points( - poly, - com, - &zero_check_proof.point, - ) + pcs_acc.insert_poly_and_points(poly, com, &zero_check_proof.point) }); // - 4.4. public input consistency checks // - pi_poly(r_pi) where r_pi is sampled from transcript let r_pi = transcript.get_and_append_challenge_vectors(b"r_pi", ell)?; let tmp_point = [vec![E::Fr::zero(); num_vars - ell], r_pi].concat(); - witness_and_selector_x_pcs_acc.insert_poly_and_points( - &witness_polys[0], - &witness_commits[0], - &tmp_point, - ); + pcs_acc.insert_poly_and_points(&witness_polys[0], &witness_commits[0], &tmp_point); end_timer!(step); // ======================================================================= // 5. deferred batch opening // ======================================================================= let step = start_timer!(|| "deferred batch openings prod(x)"); - let batch_prod_x_openings = prod_x_pcs_acc.multi_open(&pk.pcs_param, &mut transcript)?; - end_timer!(step); - - let step = start_timer!(|| "deferred batch openings witness and selectors"); - let batch_witness_and_selector_openings = - witness_and_selector_x_pcs_acc.multi_open(&pk.pcs_param, &mut transcript)?; + let batch_openings = pcs_acc.multi_open(&pk.pcs_param, &mut transcript)?; end_timer!(step); end_timer!(start); Ok(HyperPlonkProof { // PCS commit for witnesses - witness_merged_commit, witness_commits, // batch_openings, - batch_prod_x_openings, - batch_witness_and_selector_openings, - // perm check openings - perm_check_opening, - perm_check_eval, + batch_openings, // ======================================================================= // IOP proofs // ======================================================================= @@ -384,7 +372,7 @@ where /// ``` /// in vanilla plonk, and obtain a ZeroCheckSubClaim /// - /// 2. Verify perm_check_proof on `\{w_i(x)\}` and `permutation_oracle` + /// 2. Verify perm_check_proof on `\{w_i(x)\}` and `permutation_oracles` /// /// 3. check subclaim validity /// @@ -403,37 +391,11 @@ where let num_selectors = vk.params.num_selector_columns(); let num_witnesses = vk.params.num_witness_columns(); - - // witness assignment of length 2^n - let log_num_witness_polys = log2(num_witnesses) as usize; let num_vars = vk.params.num_variables(); - // number of variables in merged polynomial for Multilinear-KZG - let merged_nv = num_vars + log_num_witness_polys; // online public input of length 2^\ell let ell = log2(vk.params.num_pub_input) as usize; - // sequence: - // - prod(x) at 5 points - // - w_merged at perm check point - // - w_merged at zero check points (#witness points) - // - selector_merged at zero check points (#selector points) - // - w[0] at r_pi - let selector_evals = &proof - .batch_witness_and_selector_openings - .f_i_eval_at_point_i[num_witnesses..num_witnesses + num_selectors]; - let witness_evals = &proof - .batch_witness_and_selector_openings - .f_i_eval_at_point_i[..num_witnesses]; - let prod_evals = &proof.batch_prod_x_openings.f_i_eval_at_point_i[0..4]; - let pi_eval = proof - .batch_witness_and_selector_openings - .f_i_eval_at_point_i - .last() - .unwrap(); - - let pi_poly = DenseMultilinearExtension::from_evaluations_slice(ell as usize, pub_input); - // ======================================================================= // 0. sanity checks // ======================================================================= @@ -446,6 +408,20 @@ where ))); } + // Extract evaluations from openings + let prod_evals = &proof.batch_openings.f_i_eval_at_point_i[0..4]; + let frac_evals = &proof.batch_openings.f_i_eval_at_point_i[4..7]; + let id_evals = &proof.batch_openings.f_i_eval_at_point_i[7..7 + num_witnesses]; + let perm_evals = + &proof.batch_openings.f_i_eval_at_point_i[7 + num_witnesses..7 + 2 * num_witnesses]; + let witness_perm_evals = + &proof.batch_openings.f_i_eval_at_point_i[7 + 2 * num_witnesses..7 + 3 * num_witnesses]; + let witness_gate_evals = + &proof.batch_openings.f_i_eval_at_point_i[7 + 3 * num_witnesses..7 + 4 * num_witnesses]; + let selector_evals = &proof.batch_openings.f_i_eval_at_point_i + [7 + 4 * num_witnesses..7 + 4 * num_witnesses + num_selectors]; + let pi_eval = proof.batch_openings.f_i_eval_at_point_i.last().unwrap(); + // ======================================================================= // 1. Verify zero_check_proof on // `f(q_0(x),...q_l(x), w_0(x),...w_d(x))` @@ -464,7 +440,9 @@ where phantom: PhantomData::default(), }; // push witness to transcript - transcript.append_serializable_element(b"w", &proof.witness_merged_commit)?; + for w_com in proof.witness_commits.iter() { + transcript.append_serializable_element(b"w", w_com)?; + } let zero_check_sub_claim = >::verify( &proof.zero_check_proof, @@ -472,10 +450,10 @@ where &mut transcript, )?; - let zero_check_point = &zero_check_sub_claim.point; + let zero_check_point = zero_check_sub_claim.point.clone(); // check zero check subclaim - let f_eval = eval_f(&vk.params.gate_func, selector_evals, witness_evals)?; + let f_eval = eval_f(&vk.params.gate_func, selector_evals, witness_gate_evals)?; if f_eval != zero_check_sub_claim.expected_evaluation { return Err(HyperPlonkErrors::InvalidProof( "zero check evaluation failed".to_string(), @@ -490,10 +468,9 @@ where // Zero check and perm check have different AuxInfo let perm_check_aux_info = VPAuxInfo:: { - // Prod(x) has a max degree of 2 - max_degree: 2, - // degree of merged poly - num_variables: merged_nv, + // Prod(x) has a max degree of witnesses.len() + 1 + max_degree: proof.witness_commits.len() + 1, + num_variables: num_vars, phantom: PhantomData::default(), }; let perm_check_sub_claim = >::verify( @@ -502,40 +479,28 @@ where &mut transcript, )?; - let perm_check_point = &perm_check_sub_claim + let perm_check_point = perm_check_sub_claim .product_check_sub_claim .zero_check_sub_claim - .point; + .point + .clone(); let alpha = perm_check_sub_claim.product_check_sub_claim.alpha; let (beta, gamma) = perm_check_sub_claim.challenges; - // check perm check subclaim: - // proof.witness_perm_check_eval ?= perm_check_sub_claim.expected_eval - // - // 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)) - // where - // - Q(x) is perm_check_sub_claim.zero_check.exp_eval - // - prod(1, x) ... from prod(x) evaluated over (1, zero_point) - // - 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 - - // we evaluate MLE directly instead of using s_id/s_perm PCS verify - // Verification takes n pairings while evaluate takes 2^n field ops. - let s_ids = identity_permutation_mles::(perm_check_point.len(), 1); - let s_id_eval = evaluate_opt(&s_ids[0], perm_check_point); - let s_perm_eval = evaluate_opt(&vk.permutation_oracle, perm_check_point); - - let q_x_rec = prod_evals[1] - prod_evals[2] * prod_evals[3] - + alpha - * ((prod_evals[0] + beta * s_perm_eval + gamma) * prod_evals[0] - - (prod_evals[0] + beta * s_id_eval + gamma)); - - if q_x_rec + // check evaluation subclaim + let perm_gate_eval = eval_perm_gate( + prod_evals, + frac_evals, + witness_perm_evals, + id_evals, + perm_evals, + alpha, + beta, + gamma, + *perm_check_point.last().unwrap(), + )?; + if perm_gate_eval != perm_check_sub_claim .product_check_sub_claim .zero_check_sub_claim @@ -552,73 +517,92 @@ where // ======================================================================= let step = start_timer!(|| "verify commitments"); - // ======================================================================= - // 3.1 open prod(x)' evaluations - // ======================================================================= - // TODO: Check prod(x) at (1,...,1,0) - let _prod_final_query = perm_check_sub_claim.product_check_sub_claim.final_query; - let prod_points = [ - [perm_check_point.as_slice(), &[E::Fr::zero()]].concat(), - [perm_check_point.as_slice(), &[E::Fr::one()]].concat(), - [&[E::Fr::zero()], perm_check_point.as_slice()].concat(), - [&[E::Fr::one()], perm_check_point.as_slice()].concat(), - // prod_final_query.0, - ]; - - let mut r_pi = transcript.get_and_append_challenge_vectors(b"r_pi", ell)?; - - let res = PCS::batch_verify( - &vk.pcs_param, - [proof.perm_check_proof.prod_x_comm; 4].as_ref(), - prod_points.as_ref(), - &proof.batch_prod_x_openings, - &mut transcript, - )?; - assert!(res); + // generate evaluation points and commitments + let mut comms = vec![]; + let mut points = vec![]; + + let perm_check_point_0 = [&[E::Fr::zero()], &perm_check_point[0..num_vars - 1]].concat(); + let perm_check_point_1 = [&[E::Fr::one()], &perm_check_point[0..num_vars - 1]].concat(); + let prod_final_query_point = + [vec![E::Fr::zero()], vec![E::Fr::one(); num_vars - 1]].concat(); + + // prod(x)'s points + comms.push(proof.perm_check_proof.prod_x_comm); + comms.push(proof.perm_check_proof.prod_x_comm); + comms.push(proof.perm_check_proof.prod_x_comm); + comms.push(proof.perm_check_proof.prod_x_comm); + points.push(perm_check_point.clone()); + points.push(perm_check_point_0.clone()); + points.push(perm_check_point_1.clone()); + points.push(prod_final_query_point); + + // frac(x)'s points + comms.push(proof.perm_check_proof.frac_comm); + comms.push(proof.perm_check_proof.frac_comm); + comms.push(proof.perm_check_proof.frac_comm); + points.push(perm_check_point.clone()); + points.push(perm_check_point_0); + points.push(perm_check_point_1); + + // s_id's points + for &id_com in vk.id_commitments.iter() { + comms.push(id_com); + points.push(perm_check_point.clone()); + } - // ======================================================================= - // 3.3 open witnesses' and selectors evaluations - // ======================================================================= + // perms' points + for &pcom in vk.perm_commitments.iter() { + comms.push(pcom); + points.push(perm_check_point.clone()); + } - let res = PCS::verify( - &vk.pcs_param, - &proof.witness_merged_commit, - perm_check_point, - &proof.perm_check_eval, - &proof.perm_check_opening, - )?; - assert!(res); + // witnesses' points + for &wcom in proof.witness_commits.iter() { + comms.push(wcom); + points.push(perm_check_point.clone()); + } + for &wcom in proof.witness_commits.iter() { + comms.push(wcom); + points.push(zero_check_point.clone()); + } - let pi_eval_rec = evaluate_opt(&pi_poly, &r_pi); - assert_eq!(&pi_eval_rec, pi_eval); + // selector_poly(zero_check_point) + for &com in vk.selector_commitments.iter() { + comms.push(com); + points.push(zero_check_point.clone()); + } - r_pi = [vec![E::Fr::zero(); num_vars - ell], r_pi].concat(); - let commitments = [ - proof.witness_commits.as_slice(), - vk.selector_commitments.as_slice(), - &[proof.witness_commits[0]], - ] - .concat(); + // - 4.4. public input consistency checks + // - pi_poly(r_pi) where r_pi is sampled from transcript + let r_pi = transcript.get_and_append_challenge_vectors(b"r_pi", ell)?; + let tmp_point = [vec![E::Fr::zero(); num_vars - ell], r_pi].concat(); + // check public evaluation + let pi_poly = DenseMultilinearExtension::from_evaluations_slice(ell as usize, pub_input); + let expect_pi_eval = evaluate_opt(&pi_poly, &tmp_point[..]); + if expect_pi_eval != *pi_eval { + return Err(HyperPlonkErrors::InvalidProver(format!( + "Public input eval mismatch: got {}, expect {}", + pi_eval, expect_pi_eval, + ))); + } + comms.push(proof.witness_commits[0]); + points.push(tmp_point); - let points = [ - vec![zero_check_point.clone(); num_witnesses + num_selectors].as_slice(), - &[r_pi], - ] - .concat(); + assert_eq!(comms.len(), proof.batch_openings.f_i_eval_at_point_i.len()); + // check proof + println!("last step!!!"); let res = PCS::batch_verify( &vk.pcs_param, - commitments.as_ref(), - points.as_ref(), - &proof.batch_witness_and_selector_openings, + &comms, + &points, + &proof.batch_openings, &mut transcript, )?; - assert!(res); - end_timer!(step); end_timer!(start); - Ok(true) + Ok(res) } } @@ -629,7 +613,7 @@ mod tests { custom_gate::CustomizedGates, selectors::SelectorColumn, structs::HyperPlonkParams, witness::WitnessColumn, }; - use arithmetic::random_permutation_mles; + use arithmetic::{identity_permutation, random_permutation}; use ark_bls12_381::Bls12_381; use ark_std::test_rng; use subroutines::pcs::prelude::MultilinearKzgPCS; @@ -664,7 +648,7 @@ mod tests { let num_constraints = 4; let num_pub_input = 4; let nv = log2(num_constraints) as usize; - let merged_nv = nv + log2(gate_func.num_witness_columns()) as usize; + let num_witnesses = 2; // generate index let params = HyperPlonkParams { @@ -672,9 +656,7 @@ mod tests { num_pub_input, gate_func, }; - let permutation = identity_permutation_mles(merged_nv, 1)[0] - .evaluations - .clone(); + let permutation = identity_permutation(nv, num_witnesses); let q1 = SelectorColumn(vec![E::Fr::one(), E::Fr::one(), E::Fr::one(), E::Fr::one()]); let index = HyperPlonkIndex { params, @@ -716,20 +698,18 @@ mod tests { )?; // bad path 1: wrong permutation - let rand_perm: Vec = random_permutation_mles(merged_nv, 1, &mut rng)[0] - .evaluations - .clone(); + let rand_perm: Vec = random_permutation(nv, num_witnesses, &mut rng); let mut bad_index = index.clone(); bad_index.permutation = rand_perm; // generate pk and vks let (_, bad_vk) = as HyperPlonkSNARK>>::preprocess( &bad_index, &pcs_srs, )?; - assert!( + assert_eq!( as HyperPlonkSNARK>>::verify( &bad_vk, &pi.0, &proof, - ) - .is_err() + )?, + false ); // bad path 2: wrong witness diff --git a/hyperplonk/src/structs.rs b/hyperplonk/src/structs.rs index 8fa10ce..c381a16 100644 --- a/hyperplonk/src/structs.rs +++ b/hyperplonk/src/structs.rs @@ -12,7 +12,7 @@ use subroutines::{ }; /// The proof for the HyperPlonk PolyIOP, consists of the following: -/// - a batch commitment to all the witness MLEs +/// - the commitments to all 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 @@ -24,12 +24,8 @@ where PCS: PolynomialCommitmentScheme, { // PCS commit for witnesses - pub witness_merged_commit: PCS::Commitment, pub witness_commits: Vec, - pub batch_prod_x_openings: PCS::BatchProof, - pub batch_witness_and_selector_openings: PCS::BatchProof, - pub perm_check_opening: PCS::Proof, - pub perm_check_eval: PCS::Evaluation, + pub batch_openings: PCS::BatchProof, // ======================================================================= // IOP proofs // ======================================================================= @@ -103,18 +99,24 @@ impl HyperPlonkIndex { /// The HyperPlonk proving key, consists of the following: /// - the hyperplonk instance parameters /// - the preprocessed polynomials output by the indexer -/// - the commitment to the selectors +/// - the commitment to the selectors and permutations /// - the parameters for polynomial commitment #[derive(Clone, Debug, Default, PartialEq)] pub struct HyperPlonkProvingKey> { /// Hyperplonk instance parameters pub params: HyperPlonkParams, /// The preprocessed permutation polynomials - pub permutation_oracle: Rc>, + pub permutation_oracles: Vec>>, + /// The preprocessed identity polynomials + pub id_oracles: Vec>>, /// The preprocessed selector polynomials pub selector_oracles: Vec>>, - /// A commitment to the preprocessed selector polynomials + /// Commitments to the preprocessed selector polynomials pub selector_commitments: Vec, + /// Commitments to the preprocessed permutation polynomials + pub permutation_commitments: Vec, + /// Commitments to the preprocessed identity polynomials + pub id_commitments: Vec, /// The parameters for PCS commitment pub pcs_param: PCS::ProverParam, } @@ -127,12 +129,12 @@ pub struct HyperPlonkProvingKey> { /// Hyperplonk instance parameters pub params: HyperPlonkParams, - /// The preprocessed permutation polynomials - pub permutation_oracle: Rc>, /// The parameters for PCS commitment pub pcs_param: PCS::VerifierParam, /// A commitment to the preprocessed selector polynomials pub selector_commitments: Vec, - /// Permutation oracle's commitment - pub perm_com: PCS::Commitment, + /// Permutation oracles' commitments + pub perm_commitments: Vec, + /// Commitments to the preprocessed identity polynomials + pub id_commitments: Vec, } diff --git a/hyperplonk/src/utils.rs b/hyperplonk/src/utils.rs index 2dcdf73..da11f13 100644 --- a/hyperplonk/src/utils.rs +++ b/hyperplonk/src/utils.rs @@ -240,6 +240,45 @@ pub(crate) fn eval_f( Ok(res) } +// check perm check subclaim: +// proof.witness_perm_check_eval ?= perm_check_sub_claim.expected_eval +// Q(x) := prod(x) - p1(x) * p2(x) +// + alpha * frac(x) * g1(x) * ... * gk(x) +// - alpha * f1(x) * ... * fk(x) +// +// where p1(x) = (1-x1) * frac(x2, ..., xn, 0) +// + x1 * prod(x2, ..., xn, 0), +// and p2(x) = (1-x1) * frac(x2, ..., xn, 1) +// + x1 * prod(x2, ..., xn, 1) +// and gi(x) = (wi(x) + beta * perms_i(x) + gamma) +// and fi(x) = (wi(x) + beta * s_id_i(x) + gamma) +#[allow(clippy::too_many_arguments)] +pub(crate) fn eval_perm_gate( + prod_evals: &[F], + frac_evals: &[F], + witness_perm_evals: &[F], + id_evals: &[F], + perm_evals: &[F], + alpha: F, + beta: F, + gamma: F, + x1: F, +) -> Result { + let p1_eval = frac_evals[1] + x1 * (prod_evals[1] - frac_evals[1]); + let p2_eval = frac_evals[2] + x1 * (prod_evals[2] - frac_evals[2]); + let mut f_prod_eval = F::one(); + for (&w_eval, &id_eval) in witness_perm_evals.iter().zip(id_evals.iter()) { + f_prod_eval *= w_eval + beta * id_eval + gamma; + } + let mut g_prod_eval = F::one(); + for (&w_eval, &p_eval) in witness_perm_evals.iter().zip(perm_evals.iter()) { + g_prod_eval *= w_eval + beta * p_eval + gamma; + } + let res = + prod_evals[0] - p1_eval * p2_eval + alpha * (frac_evals[0] * g_prod_eval - f_prod_eval); + Ok(res) +} + #[cfg(test)] mod test { use super::*; diff --git a/subroutines/benches/iop_bench.rs b/subroutines/benches/iop_bench.rs index cfd9cbc..0f70477 100644 --- a/subroutines/benches/iop_bench.rs +++ b/subroutines/benches/iop_bench.rs @@ -149,27 +149,27 @@ fn bench_permutation_check() -> Result<(), PolyIOPErrors> { // identity map let perms = identity_permutation_mles(nv, 1); - let proof = { - let start = Instant::now(); - let mut transcript = - as PermutationCheck>::init_transcript(); - transcript.append_message(b"testing", b"initializing transcript for testing")?; + let proof = + { + let start = Instant::now(); + 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, - &ws, - &ws, - &perms, - &mut transcript, - )?; + let (proof, _q_x, _frac_poly) = as PermutationCheck< + Bls12_381, + KZG, + >>::prove( + &pcs_param, &ws, &ws, &perms, &mut transcript + )?; - println!( - "permutation check proving time for {} variables: {} ns", - nv, - start.elapsed().as_nanos() / repetition as u128 - ); - proof - }; + println!( + "permutation check proving time for {} variables: {} ns", + nv, + start.elapsed().as_nanos() / repetition as u128 + ); + proof + }; { let poly_info = VPAuxInfo { diff --git a/subroutines/src/poly_iop/perm_check/mod.rs b/subroutines/src/poly_iop/perm_check/mod.rs index de97d79..a26e029 100644 --- a/subroutines/src/poly_iop/perm_check/mod.rs +++ b/subroutines/src/poly_iop/perm_check/mod.rs @@ -63,16 +63,25 @@ where /// Outputs: /// - a permutation check proof proving that gs is a permutation of fs under /// permutation - /// - the product polynomial build during product check (for testing) + /// - the product polynomial built during product check + /// - the fractional polynomial built during product check /// /// Cost: O(N) + #[allow(clippy::type_complexity)] fn prove( pcs_param: &PCS::ProverParam, fxs: &[Self::MultilinearExtension], gxs: &[Self::MultilinearExtension], perms: &[Self::MultilinearExtension], transcript: &mut IOPTranscript, - ) -> Result<(Self::PermutationProof, Self::MultilinearExtension), PolyIOPErrors>; + ) -> Result< + ( + Self::PermutationProof, + Self::MultilinearExtension, + Self::MultilinearExtension, + ), + PolyIOPErrors, + >; /// Verify that (g1, ..., gk) is a permutation of /// (f1, ..., fk) over the permutation oracles (perm1, ..., permk) @@ -101,7 +110,14 @@ where gxs: &[Self::MultilinearExtension], perms: &[Self::MultilinearExtension], transcript: &mut IOPTranscript, - ) -> Result<(Self::PermutationProof, Self::MultilinearExtension), PolyIOPErrors> { + ) -> Result< + ( + Self::PermutationProof, + Self::MultilinearExtension, + Self::MultilinearExtension, + ), + PolyIOPErrors, + > { let start = start_timer!(|| "Permutation check prove"); if fxs.is_empty() { return Err(PolyIOPErrors::InvalidParameters("fxs is empty".to_string())); @@ -131,7 +147,7 @@ where let (numerators, denominators) = computer_nums_and_denoms(&beta, &gamma, fxs, gxs, perms)?; // invoke product check on numerator and denominator - let (proof, prod_poly, _) = >::prove( + let (proof, prod_poly, frac_poly) = >::prove( pcs_param, &numerators, &denominators, @@ -139,7 +155,7 @@ where )?; end_timer!(start); - Ok((proof, prod_poly)) + Ok((proof, prod_poly, frac_poly)) } fn verify( @@ -201,7 +217,7 @@ mod test { // prover let mut transcript = as PermutationCheck>::init_transcript(); transcript.append_message(b"testing", b"initializing transcript for testing")?; - let (proof, prod_x) = as PermutationCheck>::prove( + let (proof, prod_x, _frac_poly) = as PermutationCheck>::prove( pcs_param, fxs, gxs,