arnaucube
arnaucube/hyperplonkPublic Self-hosted repository
refactor snark
Charles Chen chancharles92@gmail.com
Wed, 09 Nov 2022 21:03:25 -0500
commit

bd1fc9897ecf3a709d33a235810c625bf83fe411

parent

bee67686d07682185ee78bdd492f7f8391a817bb

M arithmetic/src/lib.rs → arithmetic/src/lib.rs

@@ -7,8 +7,8 @@

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};
M arithmetic/src/multilinear_polynomial.rs → arithmetic/src/multilinear_polynomial.rs

@@ -72,6 +72,11 @@ end_timer!(start);

list } +pub fn identity_permutation<F: PrimeField>(num_vars: usize, num_chunks: usize) -> Vec<F> { + 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<F: PrimeField>( num_vars: usize,

@@ -88,12 +93,11 @@ }

res } -/// A list of MLEs that represent a random permutation -pub fn random_permutation_mles<F: PrimeField, R: RngCore>( +pub fn random_permutation<F: PrimeField, R: RngCore>( num_vars: usize, num_chunks: usize, rng: &mut R, -) -> Vec<Rc<DenseMultilinearExtension<F>>> { +) -> Vec<F> { let len = (num_chunks as u64) * (1u64 << num_vars); let mut s_id_vec: Vec<F> = (0..len).map(F::from).collect(); let mut s_perm_vec = vec![];

@@ -101,6 +105,16 @@ for _ in 0..len {

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<F: PrimeField, R: RngCore>( + num_vars: usize, + num_chunks: usize, + rng: &mut R, +) -> Vec<Rc<DenseMultilinearExtension<F>>> { + 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 {
M hyperplonk/src/mock.rs → 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 @@ num_pub_input: num_constraints,

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,
M hyperplonk/src/snark.rs → 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 @@ index: &Self::Index,

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<Rc<DenseMultilinearExtension<E::Fr>>> = index

@@ -77,23 +84,23 @@ .iter()

.map(|poly| PCS::commit(&pcs_prover_param, poly)) .collect::<Result<Vec<_>, _>>()?; - // 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 @@ prover_sanity_check(&pk.params, pub_input, witnesses)?;

// 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::<E, PCS>::new(prod_x_nv); - let mut prod_x_pcs_acc = PcsAccumulator::<E, PCS>::new(prod_x_nv); - let mut witness_and_selector_x_pcs_acc = PcsAccumulator::<E, PCS>::new(num_vars); + let mut pcs_acc = PcsAccumulator::<E, PCS>::new(num_vars); // ======================================================================= // 1. Commit Witness polynomials `w_i(x)` and append commitment to

@@ -197,20 +187,9 @@ let witness_commits = witness_polys

.iter() .map(|x| PCS::commit(&pk.pcs_param, x).unwrap()) .collect::<Vec<_>>(); - - // 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); // =======================================================================

@@ -242,11 +221,11 @@ // obtain a PermCheckSubClaim.

// ======================================================================= let step = start_timer!(|| "Permutation check on w_i(x)"); - let (perm_check_proof, prod_x) = <Self as PermutationCheck<E, PCS>>::prove( + let (perm_check_proof, prod_x, frac_poly) = <Self as PermutationCheck<E, PCS>>::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 @@

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] - // - // - 4.2. permutation check evaluations and proofs - // - 4.2.1. (deferred) wi_poly(perm_check_point) + // 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.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 @@ // - pi_poly(r_pi) where r_pi is sampled from transcript

// ======================================================================= 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(); + // (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_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); + // 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, + ); - // 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)?; + // 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, + ); - // - 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, - ); + // 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 @@ pk.selector_oracles

.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 @@ /// = q_l w_a(x) + q_r w_b(x) + q_m w_a(x)w_b(x) - q_o w_c(x)

/// ``` /// 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 @@ let mut transcript = IOPTranscript::<E::Fr>::new(b"hyperplonk");

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 @@ 1 << ell

))); } + // 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 @@ num_variables: num_vars,

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 = <Self as ZeroCheck<E::Fr>>::verify( &proof.zero_check_proof,

@@ -472,10 +450,10 @@ &zero_check_aux_info,

&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 @@ let step = start_timer!(|| "verify permutation check");

// Zero check and perm check have different AuxInfo let perm_check_aux_info = VPAuxInfo::<E::Fr> { - // 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 = <Self as PermutationCheck<E, PCS>>::verify(

@@ -502,40 +479,28 @@ &perm_check_aux_info,

&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::<E::Fr>(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 @@ // 3. Verify the opening against the commitment

// ======================================================================= 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, - ]; + // generate evaluation points and commitments + let mut comms = vec![]; + let mut points = vec![]; - let mut r_pi = transcript.get_and_append_challenge_vectors(b"r_pi", ell)?; + 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(); - 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); + // 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); - // ======================================================================= - // 3.3 open witnesses' and selectors evaluations - // ======================================================================= + // 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); - let res = PCS::verify( - &vk.pcs_param, - &proof.witness_merged_commit, - perm_check_point, - &proof.perm_check_eval, - &proof.perm_check_opening, - )?; - assert!(res); + // s_id's points + for &id_com in vk.id_commitments.iter() { + comms.push(id_com); + points.push(perm_check_point.clone()); + } - let pi_eval_rec = evaluate_opt(&pi_poly, &r_pi); - assert_eq!(&pi_eval_rec, pi_eval); + // perms' points + for &pcom in vk.perm_commitments.iter() { + comms.push(pcom); + points.push(perm_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(); + // 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 points = [ - vec![zero_check_point.clone(); num_witnesses + num_selectors].as_slice(), - &[r_pi], - ] - .concat(); + // selector_poly(zero_check_point) + for &com in vk.selector_commitments.iter() { + comms.push(com); + points.push(zero_check_point.clone()); + } + + // - 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); + 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 @@ use crate::{

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 @@

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 @@ num_constraints,

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 @@ &vk, &pi.0, &proof,

)?; // bad path 1: wrong permutation - let rand_perm: Vec<E::Fr> = random_permutation_mles(merged_nv, 1, &mut rng)[0] - .evaluations - .clone(); + let rand_perm: Vec<E::Fr> = 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) = <PolyIOP<E::Fr> as HyperPlonkSNARK<E, MultilinearKzgPCS<E>>>::preprocess( &bad_index, &pcs_srs, )?; - assert!( + assert_eq!( <PolyIOP<E::Fr> as HyperPlonkSNARK<E, MultilinearKzgPCS<E>>>::verify( &bad_vk, &pi.0, &proof, - ) - .is_err() + )?, + false ); // bad path 2: wrong witness
M hyperplonk/src/structs.rs → hyperplonk/src/structs.rs

@@ -12,7 +12,7 @@ poly_iop::prelude::{PermutationCheck, ZeroCheck},

}; /// 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 @@ PC: PermutationCheck<E, PCS>,

PCS: PolynomialCommitmentScheme<E>, { // PCS commit for witnesses - pub witness_merged_commit: PCS::Commitment, pub witness_commits: Vec<PCS::Commitment>, - 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 @@

/// 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<E: PairingEngine, PCS: PolynomialCommitmentScheme<E>> { /// Hyperplonk instance parameters pub params: HyperPlonkParams, /// The preprocessed permutation polynomials - pub permutation_oracle: Rc<DenseMultilinearExtension<E::Fr>>, + pub permutation_oracles: Vec<Rc<DenseMultilinearExtension<E::Fr>>>, + /// The preprocessed identity polynomials + pub id_oracles: Vec<Rc<DenseMultilinearExtension<E::Fr>>>, /// The preprocessed selector polynomials pub selector_oracles: Vec<Rc<DenseMultilinearExtension<E::Fr>>>, - /// A commitment to the preprocessed selector polynomials + /// Commitments to the preprocessed selector polynomials pub selector_commitments: Vec<PCS::Commitment>, + /// Commitments to the preprocessed permutation polynomials + pub permutation_commitments: Vec<PCS::Commitment>, + /// Commitments to the preprocessed identity polynomials + pub id_commitments: Vec<PCS::Commitment>, /// The parameters for PCS commitment pub pcs_param: PCS::ProverParam, }

@@ -127,12 +129,12 @@ #[derive(Clone, Debug, Default, PartialEq)]

pub struct HyperPlonkVerifyingKey<E: PairingEngine, PCS: PolynomialCommitmentScheme<E>> { /// Hyperplonk instance parameters pub params: HyperPlonkParams, - /// The preprocessed permutation polynomials - pub permutation_oracle: Rc<DenseMultilinearExtension<E::Fr>>, /// The parameters for PCS commitment pub pcs_param: PCS::VerifierParam, /// A commitment to the preprocessed selector polynomials pub selector_commitments: Vec<PCS::Commitment>, - /// Permutation oracle's commitment - pub perm_com: PCS::Commitment, + /// Permutation oracles' commitments + pub perm_commitments: Vec<PCS::Commitment>, + /// Commitments to the preprocessed identity polynomials + pub id_commitments: Vec<PCS::Commitment>, }
M hyperplonk/src/utils.rs → hyperplonk/src/utils.rs

@@ -240,6 +240,45 @@ }

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<F: PrimeField>( + 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<F, HyperPlonkErrors> { + 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::*;
M subroutines/benches/iop_bench.rs → subroutines/benches/iop_bench.rs

@@ -149,27 +149,27 @@

// identity map let perms = identity_permutation_mles(nv, 1); - let proof = { - let start = Instant::now(); - let mut transcript = - <PolyIOP<Fr> as PermutationCheck<Bls12_381, KZG>>::init_transcript(); - transcript.append_message(b"testing", b"initializing transcript for testing")?; + let proof = + { + let start = Instant::now(); + let mut transcript = + <PolyIOP<Fr> as PermutationCheck<Bls12_381, KZG>>::init_transcript(); + transcript.append_message(b"testing", b"initializing transcript for testing")?; - let (proof, _q_x) = <PolyIOP<Fr> as PermutationCheck<Bls12_381, KZG>>::prove( - &pcs_param, - &ws, - &ws, - &perms, - &mut transcript, - )?; + let (proof, _q_x, _frac_poly) = <PolyIOP<Fr> 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 {
M subroutines/src/poly_iop/perm_check/mod.rs → subroutines/src/poly_iop/perm_check/mod.rs

@@ -63,16 +63,25 @@ /// - permutation oracles = (p1, ..., pk)

/// 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<E::Fr>, - ) -> 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 @@ fxs: &[Self::MultilinearExtension],

gxs: &[Self::MultilinearExtension], perms: &[Self::MultilinearExtension], transcript: &mut IOPTranscript<E::Fr>, - ) -> 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 @@ let gamma = transcript.get_and_append_challenge(b"gamma")?;

let (numerators, denominators) = computer_nums_and_denoms(&beta, &gamma, fxs, gxs, perms)?; // invoke product check on numerator and denominator - let (proof, prod_poly, _) = <Self as ProductCheck<E, PCS>>::prove( + let (proof, prod_poly, frac_poly) = <Self as ProductCheck<E, PCS>>::prove( pcs_param, &numerators, &denominators,

@@ -139,7 +155,7 @@ transcript,

)?; end_timer!(start); - Ok((proof, prod_poly)) + Ok((proof, prod_poly, frac_poly)) } fn verify(

@@ -201,7 +217,7 @@

// prover let mut transcript = <PolyIOP<E::Fr> as PermutationCheck<E, PCS>>::init_transcript(); transcript.append_message(b"testing", b"initializing transcript for testing")?; - let (proof, prod_x) = <PolyIOP<E::Fr> as PermutationCheck<E, PCS>>::prove( + let (proof, prod_x, _frac_poly) = <PolyIOP<E::Fr> as PermutationCheck<E, PCS>>::prove( pcs_param, fxs, gxs,