diff --git a/arithmetic/src/lib.rs b/arithmetic/src/lib.rs index 43576ece..5fa31db7 100644 --- a/arithmetic/src/lib.rs +++ b/arithmetic/src/lib.rs @@ -6,10 +6,11 @@ mod virtual_polynomial; pub use errors::ArithErrors; pub use multilinear_polynomial::{ - evaluate_no_par, evaluate_opt, fix_first_variable, fix_variables, identity_permutation_mle, + evaluate_no_par, evaluate_opt, fix_first_variable, fix_last_variable, fix_last_variable_no_par, + fix_last_variables, fix_last_variables_no_par, fix_variables, identity_permutation_mle, merge_polynomials, random_mle_list, random_permutation_mle, 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}; -pub use virtual_polynomial::{build_eq_x_r, VPAuxInfo, VirtualPolynomial}; +pub use virtual_polynomial::{build_eq_x_r, build_eq_x_r_vec, VPAuxInfo, VirtualPolynomial}; diff --git a/arithmetic/src/multilinear_polynomial.rs b/arithmetic/src/multilinear_polynomial.rs index 1bcef2ba..a037d5ec 100644 --- a/arithmetic/src/multilinear_polynomial.rs +++ b/arithmetic/src/multilinear_polynomial.rs @@ -210,3 +210,84 @@ pub fn merge_polynomials( merged_nv, scalars, ))) } + +pub fn fix_last_variables_no_par( + poly: &DenseMultilinearExtension, + partial_point: &[F], +) -> DenseMultilinearExtension { + let mut res = fix_last_variable_no_par(poly, &partial_point.last().unwrap()); + for p in partial_point.iter().rev().skip(1) { + res = fix_last_variable_no_par(&res, p); + } + res +} + +pub fn fix_last_variable_no_par( + poly: &DenseMultilinearExtension, + partial_point: &F, +) -> DenseMultilinearExtension { + let nv = poly.num_vars(); + let half_len = 1 << (nv - 1); + let mut res = vec![F::zero(); half_len]; + let one_minus_p = F::one() - partial_point; + for i in 0..half_len { + res[i] = + one_minus_p * poly.evaluations[i] + *partial_point * poly.evaluations[i + half_len]; + } + DenseMultilinearExtension::from_evaluations_vec(nv - 1, res) +} +pub fn fix_last_variables( + poly: &DenseMultilinearExtension, + partial_point: &[F], +) -> DenseMultilinearExtension { + assert!( + partial_point.len() <= poly.num_vars, + "invalid size of partial point" + ); + let nv = poly.num_vars; + let mut poly = poly.evaluations.to_vec(); + let dim = partial_point.len(); + // evaluate single variable of partial point from left to right + for (i, point) in partial_point.iter().rev().enumerate().take(dim) { + poly = fix_last_variable_helper(&poly, nv - i, point); + } + + DenseMultilinearExtension::::from_evaluations_slice(nv - dim, &poly[..(1 << (nv - dim))]) +} + +pub fn fix_last_variable( + poly: &DenseMultilinearExtension, + partial_point: &F, +) -> DenseMultilinearExtension { + assert!(poly.num_vars != 0, "invalid size of partial point"); + + let nv = poly.num_vars; + let res = fix_last_variable_helper(&poly.evaluations, nv, partial_point); + DenseMultilinearExtension::::from_evaluations_slice(nv - 1, &res) +} + +fn fix_last_variable_helper(data: &[F], nv: usize, point: &F) -> Vec { + let one_minus_p = F::one() - point; + let half_len = 1 << (nv - 1); + let mut res = vec![F::zero(); half_len]; + + // evaluate single variable of partial point from left to right + #[cfg(not(feature = "parallel"))] + for b in 0..half_len { + res[b] = data[b] * one_minus_p + data[b + half_len] * point; + } + + #[cfg(feature = "parallel")] + if nv >= 13 { + // on my computer we parallelization doesn't help till nv >= 13 + res.par_iter_mut().enumerate().for_each(|(i, x)| { + *x = data[i] * one_minus_p + data[i + half_len] * point; + }); + } else { + for b in 0..(1 << (nv - 1)) { + res[b] = data[b] * one_minus_p + data[b + half_len] * point; + } + } + + res +} diff --git a/arithmetic/src/virtual_polynomial.rs b/arithmetic/src/virtual_polynomial.rs index bb6b44cd..6e535755 100644 --- a/arithmetic/src/virtual_polynomial.rs +++ b/arithmetic/src/virtual_polynomial.rs @@ -333,7 +333,19 @@ impl VirtualPolynomial { pub fn build_eq_x_r( r: &[F], ) -> Result>, ArithErrors> { - let start = start_timer!(|| "zero check build eq_x_r"); + let evals = build_eq_x_r_vec(r)?; + let mle = DenseMultilinearExtension::from_evaluations_vec(r.len(), evals); + + Ok(Rc::new(mle)) +} +// This function build the eq(x, r) polynomial for any given r. +// +// Evaluate +// eq(x,y) = \prod_i=1^num_var (x_i * y_i + (1-x_i)*(1-y_i)) +// over r, which is +// eq(x,y) = \prod_i=1^num_var (x_i * r_i + (1-x_i)*(1-r_i)) +pub fn build_eq_x_r_vec(r: &[F]) -> Result, ArithErrors> { + let start = start_timer!(|| format!("build eq_x_r of size {}", r.len())); // we build eq(x,r) from its evaluations // we want to evaluate eq(x,r) over x \in {0, 1}^num_vars @@ -349,11 +361,8 @@ pub fn build_eq_x_r( let mut eval = Vec::new(); build_eq_x_r_helper(r, &mut eval)?; - let mle = DenseMultilinearExtension::from_evaluations_vec(r.len(), eval); - - let res = Rc::new(mle); end_timer!(start); - Ok(res) + Ok(eval) } /// A helper function to build eq(x, r) recursively. diff --git a/hyperplonk/Cargo.toml b/hyperplonk/Cargo.toml index 042f99e3..f9273c7a 100644 --- a/hyperplonk/Cargo.toml +++ b/hyperplonk/Cargo.toml @@ -34,9 +34,9 @@ harness = false [features] # default = [ ] -default = [ "parallel" ] +# default = [ "parallel" ] # default = [ "parallel", "print-trace" ] -# default = [ "parallel", "extensive_sanity_checks" ] +default = [ "parallel", "extensive_sanity_checks" ] bench = [ "parallel" ] # extensive sanity checks that are useful for debugging extensive_sanity_checks = [ @@ -59,4 +59,5 @@ print-trace = [ "ark-std/print-trace", "poly-iop/print-trace", "arithmetic/print-trace", + "pcs/print-trace" ] \ No newline at end of file diff --git a/hyperplonk/src/batching.rs b/hyperplonk/src/batching.rs new file mode 100644 index 00000000..83434b2e --- /dev/null +++ b/hyperplonk/src/batching.rs @@ -0,0 +1,337 @@ +//! Sumcheck based batch opening and verify commitment. +// TODO: refactoring this code to somewhere else +// currently IOP depends on PCS because perm check requires commitment. +// The sumcheck based batch opening therefore cannot stay in the PCS repo -- +// which creates a cyclic dependency. + +use arithmetic::{ + build_eq_x_r_vec, fix_last_variables, DenseMultilinearExtension, VPAuxInfo, VirtualPolynomial, +}; +use ark_ec::{AffineCurve, PairingEngine, ProjectiveCurve}; +use ark_poly::MultilinearExtension; +use ark_std::{end_timer, log2, start_timer, One, Zero}; +use pcs::{prelude::Commitment, PolynomialCommitmentScheme}; +use poly_iop::{ + prelude::{IOPProof, SumCheck}, + PolyIOP, +}; +use std::{marker::PhantomData, rc::Rc}; +use transcript::IOPTranscript; + +use crate::prelude::HyperPlonkErrors; + +#[derive(Clone, Debug, Default, PartialEq)] +pub(crate) struct NewBatchProof +where + E: PairingEngine, + PCS: PolynomialCommitmentScheme, +{ + /// A sum check proof proving tilde g's sum + pub(crate) sum_check_proof: IOPProof, + /// \tilde g(a1, a2) + pub(crate) tilde_g_eval: E::Fr, + /// f_i(a2) + pub(crate) f_i_eval_at_a2: Vec, + /// f_i(point_i) + pub(crate) f_i_eval_at_point_i: Vec, + /// proof for g'(a_2) + pub(crate) g_prime_proof: PCS::Proof, +} + +/// Steps: +/// 1. todo... +pub(crate) fn multi_open_internal( + prover_param: &PCS::ProverParam, + polynomials: &[PCS::Polynomial], + points: &[PCS::Point], + transcript: &mut IOPTranscript, +) -> Result, HyperPlonkErrors> +where + E: PairingEngine, + PCS: PolynomialCommitmentScheme< + E, + Polynomial = Rc>, + Point = Vec, + Evaluation = E::Fr, + >, +{ + let open_timer = start_timer!(|| format!("multi open {} points", points.len())); + + // TODO: sanity checks + + let num_var = polynomials[0].num_vars; + let k = polynomials.len(); + let ell = log2(k) as usize; + let merged_num_var = num_var + ell; + + // println!("ell {}, num_var {}", ell, num_var); + + // challenge point t + let t = transcript.get_and_append_challenge_vectors("t".as_ref(), ell)?; + + // eq(t, i) for i in [0..k] + let eq_t_i_list = build_eq_x_r_vec(t.as_ref())?; + + // \tilde g(i, b) = eq(t, i) * f_i(b) + let timer = start_timer!(|| format!("compute tilde g for {} points", points.len())); + let mut tilde_g_eval = vec![]; + for (index, f_i) in polynomials.iter().enumerate() { + for &f_i_eval in f_i.iter() { + tilde_g_eval.push(f_i_eval * eq_t_i_list[index]) + } + } + tilde_g_eval.resize(1 << (ell + num_var), E::Fr::zero()); + let tilde_g = Rc::new(DenseMultilinearExtension::from_evaluations_vec( + merged_num_var, + tilde_g_eval, + )); + end_timer!(timer); + // evaluate eq(b, z_i) at boolean hypercube + // merge all evals into a nv + ell mle + + let timer = start_timer!(|| format!("compute tilde eq for {} points", points.len())); + let mut tilde_eq_eval = vec![]; + for point in points.iter() { + let eq_b_zi = build_eq_x_r_vec(&point)?; + tilde_eq_eval.extend_from_slice(eq_b_zi.as_slice()); + } + tilde_eq_eval.resize(1 << (ell + num_var), E::Fr::zero()); + let tilde_eq = Rc::new(DenseMultilinearExtension::from_evaluations_vec( + merged_num_var, + tilde_eq_eval, + )); + end_timer!(timer); + + // built the virtual polynomial for SumCheck + let timer = start_timer!(|| format!("sum check prove")); + + let step = start_timer!(|| "add mle"); + let mut sum_check_vp = VirtualPolynomial::new(num_var + ell); + sum_check_vp.add_mle_list([tilde_g.clone(), tilde_eq], E::Fr::one())?; + end_timer!(step); + + let proof = as SumCheck>::prove(&sum_check_vp, transcript)?; + let tilde_g_eval = tilde_g.evaluate(&proof.point).unwrap(); + end_timer!(timer); + + // (a1, a2) := sumcheck's point + let step = start_timer!(|| "open at a2"); + let a1 = &proof.point[num_var..]; + let a2 = &proof.point[..num_var]; + let f_i_eval_at_a2 = polynomials + .iter() + .map(|p| p.evaluate(a2).unwrap()) + .collect::>(); + end_timer!(step); + + // build g'(a2) + let step = start_timer!(|| "evaluate at a2"); + let g_prime = Rc::new(fix_last_variables(&tilde_g, a1)); + end_timer!(step); + + let step = start_timer!(|| "pcs open"); + let (g_prime_proof, g_prime_eval) = PCS::open(prover_param, &g_prime, a2.to_vec().as_ref())?; + assert_eq!(g_prime_eval, tilde_g_eval); + end_timer!(step); + + let step = start_timer!(|| "evaluate fi(pi)"); + let f_i_eval_at_point_i = polynomials + .iter() + .zip(points.iter()) + .map(|(f, p)| f.evaluate(p).unwrap()) + .collect(); + end_timer!(step); + end_timer!(open_timer); + Ok(NewBatchProof { + sum_check_proof: proof, + tilde_g_eval, + f_i_eval_at_a2, + f_i_eval_at_point_i, + g_prime_proof, + }) +} + +/// Steps: +/// 1. todo... +pub(crate) fn batch_verify_internal( + verifier_param: &PCS::VerifierParam, + f_i_commitments: &[Commitment], + proof: &NewBatchProof, + transcript: &mut IOPTranscript, +) -> Result +where + E: PairingEngine, + PCS: PolynomialCommitmentScheme< + E, + Polynomial = Rc>, + Point = Vec, + Evaluation = E::Fr, + Commitment = Commitment, + >, +{ + let open_timer = start_timer!(|| "batch verification"); + + // TODO: sanity checks + + let k = f_i_commitments.len(); + let ell = log2(k) as usize; + let num_var = proof.sum_check_proof.point.len() - ell; + // println!("ell {}, num_var {}", ell, num_var); + + // challenge point t + let t = transcript.get_and_append_challenge_vectors("t".as_ref(), ell)?; + + // sum check point (a1, a2) + let a1 = &proof.sum_check_proof.point[num_var..]; + let a2 = &proof.sum_check_proof.point[..num_var]; + + // build g' commitment + let eq_a1_list = build_eq_x_r_vec(a1)?; + let eq_t_list = build_eq_x_r_vec(t.as_ref())?; + + let mut g_prime_eval = E::Fr::zero(); + let mut g_prime_commit = E::G1Affine::zero().into_projective(); + for i in 0..k { + let tmp = eq_a1_list[i] * eq_t_list[i]; + g_prime_eval += tmp * proof.f_i_eval_at_a2[i]; + g_prime_commit += &f_i_commitments[i].0.mul(tmp); + } + + // ensure g'(a_2) == \tilde g(a1, a2) + if proof.tilde_g_eval != g_prime_eval { + // println!("eval not match"); + return Ok(false); + } + + // ensure \sum_i eq(t, ) * f_i_evals matches the sum via SumCheck + // verification + let mut sum = E::Fr::zero(); + for i in 0..k { + sum += eq_t_list[i] * proof.f_i_eval_at_point_i[i]; + } + let aux_info = VPAuxInfo { + max_degree: 2, + num_variables: num_var + ell, + phantom: PhantomData, + }; + let _subclaim = as SumCheck>::verify( + sum, + &proof.sum_check_proof, + &aux_info, + transcript, + )?; + + // verify commitment + let res = PCS::verify( + verifier_param, + &Commitment(g_prime_commit.into_affine()), + a2.to_vec().as_ref(), + &g_prime_eval, + &proof.g_prime_proof, + )?; + + // println!("res {}", res); + end_timer!(open_timer); + Ok(res) +} + +#[cfg(test)] +mod tests { + use super::*; + use arithmetic::get_batched_nv; + use ark_bls12_381::Bls12_381 as E; + use ark_ec::PairingEngine; + use ark_poly::{DenseMultilinearExtension, MultilinearExtension}; + use ark_std::{ + log2, + rand::{CryptoRng, RngCore}, + test_rng, + vec::Vec, + UniformRand, + }; + use pcs::{ + prelude::{ + compute_qx_degree, MultilinearKzgPCS, MultilinearUniversalParams, + UnivariateUniversalParams, + }, + StructuredReferenceString, + }; + + type Fr = ::Fr; + + fn test_multi_open_helper( + uni_params: &UnivariateUniversalParams, + ml_params: &MultilinearUniversalParams, + polys: &[Rc>], + rng: &mut R, + ) -> Result<(), HyperPlonkErrors> { + let merged_nv = get_batched_nv(polys[0].num_vars(), polys.len()); + let qx_degree = compute_qx_degree(merged_nv, polys.len()); + let padded_qx_degree = 1usize << log2(qx_degree); + + let (uni_ck, uni_vk) = uni_params.trim(padded_qx_degree)?; + let (ml_ck, ml_vk) = ml_params.trim(merged_nv)?; + + let mut points = Vec::new(); + for poly in polys.iter() { + let point = (0..poly.num_vars()) + .map(|_| Fr::rand(rng)) + .collect::>(); + points.push(point); + } + + // let evals = polys + // .iter() + // .zip(points.iter()) + // .map(|(f, p)| f.evaluate(p).unwrap()) + // .collect::>(); + + let commitments = polys + .iter() + .map(|poly| MultilinearKzgPCS::commit(&(ml_ck.clone(), uni_ck.clone()), poly).unwrap()) + .collect::>(); + + let mut transcript = IOPTranscript::new("test transcript".as_ref()); + transcript.append_field_element("init".as_ref(), &Fr::zero())?; + + println!("prove"); + let batch_proof = multi_open_internal::>( + &(ml_ck.clone(), uni_ck.clone()), + polys, + &points, + &mut transcript, + )?; + + // good path + println!("verify"); + let mut transcript = IOPTranscript::new("test transcript".as_ref()); + transcript.append_field_element("init".as_ref(), &Fr::zero())?; + assert!(batch_verify_internal::>( + &(ml_vk, uni_vk), + &commitments, + &batch_proof, + &mut transcript + )?); + + Ok(()) + } + + #[test] + fn test_multi_open_internal() -> Result<(), HyperPlonkErrors> { + let mut rng = test_rng(); + + let uni_params = + UnivariateUniversalParams::::gen_srs_for_testing(&mut rng, 1usize << 10)?; + let ml_params = MultilinearUniversalParams::::gen_srs_for_testing(&mut rng, 20)?; + for num_poly in 5..6 { + for nv in 15..16 { + let polys1: Vec<_> = (0..num_poly) + .map(|_| Rc::new(DenseMultilinearExtension::rand(nv, &mut rng))) + .collect(); + test_multi_open_helper(&uni_params, &ml_params, &polys1, &mut rng)?; + } + } + + Ok(()) + } +} diff --git a/hyperplonk/src/lib.rs b/hyperplonk/src/lib.rs index 4df019eb..7dd1aaff 100644 --- a/hyperplonk/src/lib.rs +++ b/hyperplonk/src/lib.rs @@ -6,6 +6,7 @@ use pcs::prelude::PolynomialCommitmentScheme; use poly_iop::prelude::PermutationCheck; use witness::WitnessColumn; +mod batching; mod custom_gate; mod errors; mod mock; diff --git a/hyperplonk/src/mock.rs b/hyperplonk/src/mock.rs index 75399635..43a9f1db 100644 --- a/hyperplonk/src/mock.rs +++ b/hyperplonk/src/mock.rs @@ -224,7 +224,7 @@ mod test { #[test] fn test_mock_circuit_e2e() -> Result<(), HyperPlonkErrors> { let mut rng = test_rng(); - let pcs_srs = MultilinearKzgPCS::::gen_srs_for_testing(&mut rng, 23)?; + let pcs_srs = MultilinearKzgPCS::::gen_srs_for_testing(&mut rng, 21)?; let nv = 18; let vanilla_gate = CustomizedGates::vanilla_plonk_gate(); diff --git a/hyperplonk/src/snark.rs b/hyperplonk/src/snark.rs index ff980a89..a36ee0ba 100644 --- a/hyperplonk/src/snark.rs +++ b/hyperplonk/src/snark.rs @@ -1,4 +1,5 @@ use crate::{ + batching::batch_verify_internal, errors::HyperPlonkErrors, structs::{HyperPlonkIndex, HyperPlonkProof, HyperPlonkProvingKey, HyperPlonkVerifyingKey}, utils::{build_f, eval_f, prover_sanity_check, PcsAccumulator}, @@ -11,7 +12,7 @@ use arithmetic::{ use ark_ec::PairingEngine; use ark_poly::DenseMultilinearExtension; use ark_std::{end_timer, log2, start_timer, One, Zero}; -use pcs::prelude::{compute_qx_degree, PolynomialCommitmentScheme}; +use pcs::prelude::{compute_qx_degree, Commitment, PolynomialCommitmentScheme}; use poly_iop::{ prelude::{PermutationCheck, ZeroCheck}, PolyIOP, @@ -30,6 +31,7 @@ where Polynomial = Rc>, Point = Vec, Evaluation = E::Fr, + Commitment = Commitment, >, { type Index = HyperPlonkIndex; @@ -201,8 +203,8 @@ where ))); } let w_merged_com = PCS::commit(&pk.pcs_param, &w_merged)?; - w_merged_pcs_acc.init_poly(w_merged.clone(), w_merged_com.clone())?; transcript.append_serializable_element(b"w", &w_merged_com)?; + end_timer!(step); // ======================================================================= // 2 Run ZeroCheck on @@ -266,7 +268,6 @@ where // 4.1 (deferred) open prod(0,x), prod(1, x), prod(x, 0), prod(x, 1) // perm_check_point - prod_pcs_acc.init_poly(prod_x, perm_check_proof.prod_x_comm.clone())?; // prod(0, x) let tmp_point1 = [perm_check_point.as_slice(), &[E::Fr::zero()]].concat(); // prod(1, x) @@ -278,49 +279,33 @@ where // prod(1, ..., 1, 0) let tmp_point5 = [vec![E::Fr::zero()], vec![E::Fr::one(); merged_nv]].concat(); - prod_pcs_acc.insert_point(&tmp_point1); - prod_pcs_acc.insert_point(&tmp_point2); - prod_pcs_acc.insert_point(&tmp_point3); - prod_pcs_acc.insert_point(&tmp_point4); - prod_pcs_acc.insert_point(&tmp_point5); + prod_pcs_acc.insert_poly_and_points(&prod_x, &perm_check_proof.prod_x_comm, &tmp_point1); + prod_pcs_acc.insert_poly_and_points(&prod_x, &perm_check_proof.prod_x_comm, &tmp_point2); + prod_pcs_acc.insert_poly_and_points(&prod_x, &perm_check_proof.prod_x_comm, &tmp_point3); + prod_pcs_acc.insert_poly_and_points(&prod_x, &perm_check_proof.prod_x_comm, &tmp_point4); + prod_pcs_acc.insert_poly_and_points(&prod_x, &perm_check_proof.prod_x_comm, &tmp_point5); // 4.2 permutation check // - 4.2.1. (deferred) wi_poly(perm_check_point) - w_merged_pcs_acc.insert_point(perm_check_point); - - #[cfg(feature = "extensive_sanity_checks")] - { - // sanity check - let eval = pk - .permutation_oracle - .evaluate(&perm_check_proof.zero_check_proof.point) - .ok_or_else(|| { - HyperPlonkErrors::InvalidParameters( - "perm_oracle evaluation dimension does not match".to_string(), - ) - })?; - if eval != perm_oracle_eval { - return Err(HyperPlonkErrors::InvalidProver( - "perm_oracle evaluation is different from PCS opening".to_string(), - )); - } - } + w_merged_pcs_acc.insert_poly_and_points(&w_merged, &w_merged_com, 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() { let tmp_point = gen_eval_point(i, log_num_witness_polys, &zero_check_proof.point); // Deferred opening zero check proof - w_merged_pcs_acc.insert_point(&tmp_point); + // w_merged_pcs_acc.insert_point(&tmp_point); + + w_merged_pcs_acc.insert_poly_and_points(&w_merged, &w_merged_com, &tmp_point); } // - 4.3.2. (deferred) selector_poly(zero_check_point) let selector_merged = merge_polynomials(&pk.selector_oracles)?; - selector_pcs_acc.init_poly(selector_merged, pk.selector_com.clone())?; + // selector_pcs_acc.init_poly(selector_merged, pk.selector_com.clone())?; for i in 0..pk.selector_oracles.len() { let tmp_point = gen_eval_point(i, log_num_selector_polys, &zero_check_proof.point); // Deferred opening zero check proof - selector_pcs_acc.insert_point(&tmp_point); + selector_pcs_acc.insert_poly_and_points(&selector_merged, &pk.selector_com, &tmp_point); } // - 4.4. public input consistency checks @@ -332,26 +317,8 @@ where vec![E::Fr::zero(); log_num_witness_polys], ] .concat(); - w_merged_pcs_acc.insert_point(&tmp_point); - #[cfg(feature = "extensive_sanity_checks")] - { - // sanity check - let pi_poly = Rc::new(DenseMultilinearExtension::from_evaluations_slice( - ell, pub_input, - )); - - let eval = pi_poly.evaluate(&r_pi).ok_or_else(|| { - HyperPlonkErrors::InvalidParameters( - "public input evaluation dimension does not match".to_string(), - ) - })?; - if eval != pi_eval { - return Err(HyperPlonkErrors::InvalidProver( - "public input evaluation is different from PCS opening".to_string(), - )); - } - } + w_merged_pcs_acc.insert_poly_and_points(&w_merged, &w_merged_com, &tmp_point); end_timer!(step); // ======================================================================= @@ -359,18 +326,21 @@ where // ======================================================================= let step = start_timer!(|| "deferred batch openings"); let sub_step = start_timer!(|| "open witness"); - let (w_merged_batch_opening, w_merged_batch_evals) = - w_merged_pcs_acc.batch_open(&pk.pcs_param)?; + let w_merged_batch_opening = w_merged_pcs_acc.multi_open(&pk.pcs_param, &mut transcript)?; end_timer!(sub_step); let sub_step = start_timer!(|| "open prod(x)"); - let (prod_batch_openings, prod_batch_evals) = prod_pcs_acc.batch_open(&pk.pcs_param)?; + // let (prod_batch_openings, prod_batch_evals) = + // prod_pcs_acc.batch_open(&pk.pcs_param)?; + let prod_batch_openings = prod_pcs_acc.multi_open(&pk.pcs_param, &mut transcript)?; end_timer!(sub_step); let sub_step = start_timer!(|| "open selector"); - - let (selector_batch_opening, selector_batch_evals) = selector_pcs_acc - .batch_open_overlapped_points(&pk.pcs_param, zero_check_proof.point.len())?; + let selector_batch_openings = + selector_pcs_acc.multi_open(&pk.pcs_param, &mut transcript)?; + // let (selector_batch_opening, selector_batch_evals) = selector_pcs_acc + // .batch_open_overlapped_points(&pk.pcs_param, + // zero_check_proof.point.len())?; end_timer!(sub_step); end_timer!(step); end_timer!(start); @@ -380,17 +350,18 @@ where // witness related // ======================================================================= /// PCS commit for witnesses + w_merged_batch_opening, w_merged_com, // Batch opening for witness commitment // - PermCheck eval: 1 point // - ZeroCheck evals: #witness points // - public input eval: 1 point - w_merged_batch_opening, + // w_merged_batch_opening, // Evaluations of Witness // - PermCheck eval: 1 point // - ZeroCheck evals: #witness points // - public input eval: 1 point - w_merged_batch_evals, + // w_merged_batch_evals, // ======================================================================= // prod(x) related // ======================================================================= @@ -407,14 +378,14 @@ where // - prod(x, 0), // - prod(x, 1), // - prod(1, ..., 1,0) - prod_batch_evals, + // prod_batch_evals, // ======================================================================= // selectors related // ======================================================================= // PCS openings for selectors on zero check point - selector_batch_opening, + selector_batch_openings, // Evaluates of selectors on zero check point - selector_batch_evals, + // selector_batch_evals, // ======================================================================= // IOP proofs // ======================================================================= @@ -483,24 +454,28 @@ where 1 << ell ))); } - if proof.selector_batch_evals.len() - 1 != vk.params.num_selector_columns() { + if proof.selector_batch_openings.f_i_eval_at_point_i.len() + != vk.params.num_selector_columns() + { return Err(HyperPlonkErrors::InvalidVerifier(format!( "Selector length is not correct: got {}, expect {}", - proof.selector_batch_evals.len() - 1, + proof.selector_batch_openings.f_i_eval_at_point_i.len(), 1 << vk.params.num_selector_columns() ))); } - if proof.w_merged_batch_evals.len() != vk.params.num_witness_columns() + 3 { + if proof.w_merged_batch_opening.f_i_eval_at_point_i.len() + != vk.params.num_witness_columns() + 2 + { return Err(HyperPlonkErrors::InvalidVerifier(format!( "Witness length is not correct: got {}, expect {}", - proof.w_merged_batch_evals.len() - 3, + proof.w_merged_batch_opening.f_i_eval_at_point_i.len() - 2, vk.params.num_witness_columns() ))); } - if proof.prod_batch_evals.len() - 1 != 5 { + if proof.prod_batch_openings.f_i_eval_at_point_i.len() != 5 { return Err(HyperPlonkErrors::InvalidVerifier(format!( "the number of product polynomial evaluations is not correct: got {}, expect {}", - proof.prod_batch_evals.len() - 1, + proof.prod_batch_openings.f_i_eval_at_point_i.len(), 5 ))); } @@ -536,8 +511,8 @@ where // check zero check subclaim let f_eval = eval_f( &vk.params.gate_func, - &proof.selector_batch_evals[..vk.params.num_selector_columns()], - &proof.w_merged_batch_evals[1..], + &&proof.selector_batch_openings.f_i_eval_at_point_i[..vk.params.num_selector_columns()], + &proof.w_merged_batch_opening.f_i_eval_at_point_i[1..], )?; if f_eval != zero_check_sub_claim.expected_evaluation { return Err(HyperPlonkErrors::InvalidProof( @@ -593,12 +568,17 @@ where let s_id_eval = evaluate_opt(&s_id, perm_check_point); let s_perm_eval = evaluate_opt(&vk.permutation_oracle, perm_check_point); - let q_x_rec = proof.prod_batch_evals[1] - - proof.prod_batch_evals[2] * proof.prod_batch_evals[3] + let q_x_rec = proof.prod_batch_openings.f_i_eval_at_point_i[1] + - proof.prod_batch_openings.f_i_eval_at_point_i[2] + * proof.prod_batch_openings.f_i_eval_at_point_i[3] + alpha - * ((proof.w_merged_batch_evals[0] + beta * s_perm_eval + gamma) - * proof.prod_batch_evals[0] - - (proof.w_merged_batch_evals[0] + beta * s_id_eval + gamma)); + * ((proof.w_merged_batch_opening.f_i_eval_at_point_i[0] + + beta * s_perm_eval + + gamma) + * proof.prod_batch_openings.f_i_eval_at_point_i[0] + - (proof.w_merged_batch_opening.f_i_eval_at_point_i[0] + + beta * s_id_eval + + gamma)); if q_x_rec != perm_check_sub_claim @@ -629,17 +609,17 @@ where prod_final_query.0, ]; - if !PCS::batch_verify_single_poly( - &vk.pcs_param, - &proof.perm_check_proof.prod_x_comm, - &points, - &proof.prod_batch_evals, - &proof.prod_batch_openings, - )? { - return Err(HyperPlonkErrors::InvalidProof( - "prod(0, x) pcs verification failed".to_string(), - )); - } + // if !PCS::batch_verify_single_poly( + // &vk.pcs_param, + // &proof.perm_check_proof.prod_x_comm, + // &points, + // &proof.prod_batch_evals, + // &proof.prod_batch_openings, + // )? { + // return Err(HyperPlonkErrors::InvalidProof( + // "prod(0, x) pcs verification failed".to_string(), + // )); + // } // ======================================================================= // 3.2 open selectors' evaluations @@ -652,30 +632,30 @@ where points.push(tmp_point); } - if proof.selector_batch_evals.len() == 2 { - if !PCS::batch_verify_single_poly( - &vk.pcs_param, - &vk.selector_com, - &points, - &proof.selector_batch_evals, - &proof.selector_batch_opening, - )? { - return Err(HyperPlonkErrors::InvalidProof( - "selector pcs verification failed".to_string(), - )); - } - } else if !PCS::batch_verify_single_poly_overlap_points( - &vk.pcs_param, - &vk.selector_com, - &points, - &proof.selector_batch_evals, - &proof.selector_batch_opening, - proof.zero_check_proof.point.len(), - )? { - return Err(HyperPlonkErrors::InvalidProof( - "selector pcs verification failed".to_string(), - )); - } + // if proof.se.len() == 2 { + // if !PCS::batch_verify_single_poly( + // &vk.pcs_param, + // &vk.selector_com, + // &points, + // &proof.selector_batch_evals, + // &proof.selector_batch_opening, + // )? { + // return Err(HyperPlonkErrors::InvalidProof( + // "selector pcs verification failed".to_string(), + // )); + // } + // } else if !PCS::batch_verify_single_poly_overlap_points( + // &vk.pcs_param, + // &vk.selector_com, + // &points, + // &proof.selector_batch_evals, + // &proof.selector_batch_opening, + // proof.zero_check_proof.point.len(), + // )? { + // return Err(HyperPlonkErrors::InvalidProof( + // "selector pcs verification failed".to_string(), + // )); + // } // ======================================================================= // 3.2 open witnesses' evaluations @@ -684,7 +664,8 @@ where let pi_eval = evaluate_opt(&pi_poly, &r_pi); assert_eq!( pi_eval, - proof.w_merged_batch_evals[proof.w_merged_batch_evals.len() - 2] + proof.w_merged_batch_opening.f_i_eval_at_point_i + [proof.w_merged_batch_opening.f_i_eval_at_point_i.len() - 1] ); r_pi = [ @@ -694,24 +675,46 @@ where ] .concat(); - let mut points = vec![perm_check_point.clone()]; - - for i in 0..proof.w_merged_batch_evals.len() - 3 { - points.push(gen_eval_point(i, log_num_witness_polys, zero_check_point)) - } - points.push(r_pi); - if !PCS::batch_verify_single_poly( + // let mut points = vec![perm_check_point.clone()]; + + // for i in 0..proof.w_merged_batch_evals.len() - 3 { + // points.push(gen_eval_point(i, log_num_witness_polys, zero_check_point)) + // } + // points.push(r_pi); + // if !PCS::batch_verify_single_poly( + // &vk.pcs_param, + // &proof.w_merged_com, + // &points, + // &proof.w_merged_batch_evals, + // &proof.w_merged_batch_opening, + // )? { + // return Err(HyperPlonkErrors::InvalidProof( + // "witness for permutation check pcs verification failed".to_string(), + // )); + // } + + let res = batch_verify_internal( &vk.pcs_param, - &proof.w_merged_com, - &points, - &proof.w_merged_batch_evals, + vec![proof.w_merged_com; vk.params.num_witness_columns() + 2].as_ref(), &proof.w_merged_batch_opening, - )? { - return Err(HyperPlonkErrors::InvalidProof( - "witness for permutation check pcs verification failed".to_string(), - )); - } + &mut transcript, + )?; + assert!(res); + let res = batch_verify_internal( + &vk.pcs_param, + vec![proof.perm_check_proof.prod_x_comm; 5].as_ref(), + &proof.prod_batch_openings, + &mut transcript, + )?; + assert!(res); + let res = batch_verify_internal( + &vk.pcs_param, + vec![vk.selector_com; vk.params.num_selector_columns()].as_ref(), + &proof.selector_batch_openings, + &mut transcript, + )?; + assert!(res); end_timer!(step); end_timer!(start); Ok(true) diff --git a/hyperplonk/src/structs.rs b/hyperplonk/src/structs.rs index b63c9472..f7a06c5d 100644 --- a/hyperplonk/src/structs.rs +++ b/hyperplonk/src/structs.rs @@ -1,6 +1,6 @@ //! Main module for the HyperPlonk PolyIOP. -use crate::{custom_gate::CustomizedGates, selectors::SelectorColumn}; +use crate::{batching::NewBatchProof, custom_gate::CustomizedGates, selectors::SelectorColumn}; use ark_ec::PairingEngine; use ark_ff::PrimeField; use ark_poly::DenseMultilinearExtension; @@ -14,7 +14,7 @@ use std::rc::Rc; /// - 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)] +#[derive(Clone, Debug, PartialEq)] pub struct HyperPlonkProof where E: PairingEngine, @@ -25,17 +25,19 @@ where // witness related // ======================================================================= // PCS commit for witnesses + pub(crate) w_merged_batch_opening: NewBatchProof, + pub w_merged_com: PCS::Commitment, // Batch opening for witness commitment // - PermCheck eval: 1 point // - ZeroCheck evals: #witness points // - public input eval: 1 point - pub w_merged_batch_opening: PCS::BatchProof, + // pub w_merged_batch_opening: PCS::BatchProof, // Evaluations of Witness // - PermCheck eval: 1 point // - ZeroCheck evals: #witness points // - public input eval: 1 point - pub w_merged_batch_evals: Vec, + // pub w_merged_batch_evals: Vec, // ======================================================================= // prod(x) related // ======================================================================= @@ -45,21 +47,21 @@ where // - prod(x, 0), // - prod(x, 1), // - prod(1, ..., 1,0) - pub prod_batch_openings: PCS::BatchProof, + pub(crate) prod_batch_openings: NewBatchProof, // prod(x)'s evaluations // - prod(0, x), // - prod(1, x), // - prod(x, 0), // - prod(x, 1), // - prod(1, ..., 1,0) - pub prod_batch_evals: Vec, + // pub prod_batch_evals: Vec, // ======================================================================= // selectors related // ======================================================================= // PCS openings for selectors on zero check point - pub selector_batch_opening: PCS::BatchProof, + pub(crate) selector_batch_openings: NewBatchProof, // Evaluates of selectors on zero check point - pub selector_batch_evals: Vec, + // pub selector_batch_evals: Vec, // ======================================================================= // IOP proofs // ======================================================================= diff --git a/hyperplonk/src/utils.rs b/hyperplonk/src/utils.rs index 9b8ea1b1..27288526 100644 --- a/hyperplonk/src/utils.rs +++ b/hyperplonk/src/utils.rs @@ -1,122 +1,72 @@ use crate::{ - custom_gate::CustomizedGates, errors::HyperPlonkErrors, structs::HyperPlonkParams, + batching::{multi_open_internal, NewBatchProof}, + custom_gate::CustomizedGates, + errors::HyperPlonkErrors, + structs::HyperPlonkParams, witness::WitnessColumn, }; use arithmetic::VirtualPolynomial; use ark_ec::PairingEngine; use ark_ff::PrimeField; use ark_poly::DenseMultilinearExtension; -use pcs::PolynomialCommitmentScheme; +use pcs::{prelude::Commitment, PolynomialCommitmentScheme}; use std::{borrow::Borrow, rc::Rc}; +use transcript::IOPTranscript; /// An accumulator structure that holds a polynomial and /// its opening points #[derive(Debug)] pub(super) struct PcsAccumulator> { - pub(crate) polynomial: Option, - pub(crate) poly_commit: Option, + pub(crate) polynomials: Vec, + pub(crate) commitments: Vec, pub(crate) points: Vec, } -impl> PcsAccumulator { +impl PcsAccumulator +where + E: PairingEngine, + PCS: PolynomialCommitmentScheme< + E, + Polynomial = Rc>, + Point = Vec, + Evaluation = E::Fr, + Commitment = Commitment, + >, +{ /// Create an empty accumulator. pub(super) fn new() -> Self { Self { - polynomial: None, - poly_commit: None, + polynomials: vec![], + commitments: vec![], points: vec![], } } - /// Initialize the polynomial; requires both the polynomial - /// and its commitment. - pub(super) fn init_poly( - &mut self, - polynomial: PCS::Polynomial, - commitment: PCS::Commitment, - ) -> Result<(), HyperPlonkErrors> { - if self.polynomial.is_some() || self.poly_commit.is_some() { - return Err(HyperPlonkErrors::InvalidProver( - "poly already set for accumulator".to_string(), - )); - } - - self.polynomial = Some(polynomial); - self.poly_commit = Some(commitment); - Ok(()) - } - /// Push a new evaluation point into the accumulator - pub(super) fn insert_point(&mut self, point: &PCS::Point) { + pub(super) fn insert_poly_and_points( + &mut self, + poly: &PCS::Polynomial, + commit: &PCS::Commitment, + point: &PCS::Point, + ) { + self.polynomials.push(poly.clone()); + self.commitments.push(commit.clone()); self.points.push(point.clone()) } /// Batch open all the points over a merged polynomial. /// A simple wrapper of PCS::multi_open - pub(super) fn batch_open( - &self, - prover_param: impl Borrow, - ) -> Result<(PCS::BatchProof, Vec), HyperPlonkErrors> { - let poly = match &self.polynomial { - Some(p) => p, - None => { - return Err(HyperPlonkErrors::InvalidProver( - "poly is set for accumulator".to_string(), - )) - }, - }; - - let commitment = match &self.poly_commit { - Some(p) => p, - None => { - return Err(HyperPlonkErrors::InvalidProver( - "poly is set for accumulator".to_string(), - )) - }, - }; - Ok(PCS::multi_open_single_poly( - prover_param.borrow(), - commitment, - poly, - &self.points, - )?) - } - - /// Batch open all the points over a merged polynomial. - /// A simple wrapper of PCS::multi_open - pub(super) fn batch_open_overlapped_points( + pub(super) fn multi_open( &self, prover_param: impl Borrow, - overlap_len: usize, - ) -> Result<(PCS::BatchProof, Vec), HyperPlonkErrors> { - if self.points.len() == 1 { - return self.batch_open(prover_param); - } - - let poly = match &self.polynomial { - Some(p) => p, - None => { - return Err(HyperPlonkErrors::InvalidProver( - "poly is set for accumulator".to_string(), - )) - }, - }; - - let commitment = match &self.poly_commit { - Some(p) => p, - None => { - return Err(HyperPlonkErrors::InvalidProver( - "poly is set for accumulator".to_string(), - )) - }, - }; - Ok(PCS::multi_open_single_poly_overlap_points( + transcript: &mut IOPTranscript, + ) -> Result, HyperPlonkErrors> { + multi_open_internal( prover_param.borrow(), - commitment, - poly, - &self.points, - overlap_len, - )?) + self.polynomials.as_ref(), + self.points.as_ref(), + transcript, + ) } } diff --git a/pcs/src/multilinear_kzg/srs.rs b/pcs/src/multilinear_kzg/srs.rs index c24fbdd1..eaf82895 100644 --- a/pcs/src/multilinear_kzg/srs.rs +++ b/pcs/src/multilinear_kzg/srs.rs @@ -5,7 +5,7 @@ // along with the Jellyfish library. If not, see . //! Implementing Structured Reference Strings for multilinear polynomial KZG -use crate::{prelude::PCSError, StructuredReferenceString}; +use crate::{multilinear_kzg::util::eq_extension, prelude::PCSError, StructuredReferenceString}; use ark_ec::{msm::FixedBaseMSM, AffineCurve, PairingEngine, ProjectiveCurve}; use ark_ff::{Field, PrimeField}; use ark_poly::DenseMultilinearExtension; @@ -232,28 +232,6 @@ fn remove_dummy_variable(poly: &[F], pad: usize) -> Result, PCS Ok((0..(1 << nv)).map(|x| poly[x << pad]).collect()) } -/// Generate eq(t,x), a product of multilinear polynomials with fixed t. -/// eq(a,b) is takes extensions of a,b in {0,1}^num_vars such that if a and b in -/// {0,1}^num_vars are equal then this polynomial evaluates to 1. -fn eq_extension(t: &[F]) -> Vec> { - let start = start_timer!(|| "eq extension"); - - let dim = t.len(); - let mut result = Vec::new(); - for (i, &ti) in t.iter().enumerate().take(dim) { - let mut poly = Vec::with_capacity(1 << dim); - for x in 0..(1 << dim) { - let xi = if x >> i & 1 == 1 { F::one() } else { F::zero() }; - let ti_xi = ti * xi; - poly.push(ti_xi + ti_xi - xi - ti + F::one()); - } - result.push(DenseMultilinearExtension::from_evaluations_vec(dim, poly)); - } - - end_timer!(start); - result -} - #[cfg(test)] mod tests { use super::*; diff --git a/pcs/src/multilinear_kzg/util.rs b/pcs/src/multilinear_kzg/util.rs index c2cd9b7c..5f5869c0 100644 --- a/pcs/src/multilinear_kzg/util.rs +++ b/pcs/src/multilinear_kzg/util.rs @@ -203,6 +203,28 @@ pub(crate) fn generate_evaluations_single_poly( Ok(mle_values) } +/// Generate eq(t,x), a product of multilinear polynomials with fixed t. +/// eq(a,b) is takes extensions of a,b in {0,1}^num_vars such that if a and b in +/// {0,1}^num_vars are equal then this polynomial evaluates to 1. +pub(crate) fn eq_extension(t: &[F]) -> Vec> { + let start = start_timer!(|| "eq extension"); + + let dim = t.len(); + let mut result = Vec::new(); + for (i, &ti) in t.iter().enumerate().take(dim) { + let mut poly = Vec::with_capacity(1 << dim); + for x in 0..(1 << dim) { + let xi = if x >> i & 1 == 1 { F::one() } else { F::zero() }; + let ti_xi = ti * xi; + poly.push(ti_xi + ti_xi - xi - ti + F::one()); + } + result.push(DenseMultilinearExtension::from_evaluations_vec(dim, poly)); + } + + end_timer!(start); + result +} + #[cfg(test)] mod test { use super::*; diff --git a/poly-iop/benches/bench.rs b/poly-iop/benches/bench.rs index e7e6061e..886c51bc 100644 --- a/poly-iop/benches/bench.rs +++ b/poly-iop/benches/bench.rs @@ -63,10 +63,11 @@ fn bench_sum_check() -> Result<(), PolyIOPErrors> { &poly_info, &mut transcript, )?; - assert!( - poly.evaluate(&subclaim.point).unwrap() == subclaim.expected_evaluation, - "wrong subclaim" - ); + // assert!( + // poly.evaluate(&subclaim.point).unwrap() == + // subclaim.expected_evaluation, + // "wrong subclaim" + // ); } println!( "sum check verification time for {} variables and {} degree: {} ns", @@ -117,10 +118,10 @@ fn bench_zero_check() -> Result<(), PolyIOPErrors> { transcript.append_message(b"testing", b"initializing transcript for testing")?; let zero_subclaim = as ZeroCheck>::verify(&proof, &poly_info, &mut transcript)?; - assert!( - poly.evaluate(&zero_subclaim.point)? == zero_subclaim.expected_evaluation, - "wrong subclaim" - ); + // assert!( + // poly.evaluate(&zero_subclaim.point)? == + // zero_subclaim.expected_evaluation, "wrong subclaim" + // ); println!( "zero check verification time for {} variables and {} degree: {} ns", nv, diff --git a/poly-iop/src/prelude.rs b/poly-iop/src/prelude.rs index 36ebba17..a67e92e8 100644 --- a/poly-iop/src/prelude.rs +++ b/poly-iop/src/prelude.rs @@ -1,4 +1,4 @@ pub use crate::{ errors::PolyIOPErrors, perm_check::PermutationCheck, prod_check::ProductCheck, - sum_check::SumCheck, utils::*, zero_check::ZeroCheck, PolyIOP, + structs::IOPProof, sum_check::SumCheck, utils::*, zero_check::ZeroCheck, PolyIOP, };