From a92e781b0641c49687dfd290daa4f0754afe07d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benedikt=20Bu=CC=88nz?= Date: Fri, 18 Nov 2022 15:46:48 -0800 Subject: [PATCH 1/5] wip we need to be able to do batch opening for different poly sizes or pad poly with zeros --- arithmetic/src/lib.rs | 2 +- arithmetic/src/virtual_polynomial.rs | 18 ++++++++++++++++++ hyperplonk/Cargo.toml | 1 - hyperplonk/src/mock.rs | 13 +++++++------ hyperplonk/src/snark.rs | 16 ++++++++-------- subroutines/src/poly_iop/zero_check/mod.rs | 10 +++------- 6 files changed, 37 insertions(+), 23 deletions(-) diff --git a/arithmetic/src/lib.rs b/arithmetic/src/lib.rs index 2fc2b5f6..06161f50 100644 --- a/arithmetic/src/lib.rs +++ b/arithmetic/src/lib.rs @@ -12,4 +12,4 @@ pub use multilinear_polynomial::{ }; 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, build_eq_x_r_vec, VPAuxInfo, VirtualPolynomial}; +pub use virtual_polynomial::{build_eq_x_r, build_eq_x_r_vec,eq_eval, VPAuxInfo, VirtualPolynomial}; diff --git a/arithmetic/src/virtual_polynomial.rs b/arithmetic/src/virtual_polynomial.rs index a6f1eb35..c476d844 100644 --- a/arithmetic/src/virtual_polynomial.rs +++ b/arithmetic/src/virtual_polynomial.rs @@ -325,6 +325,24 @@ impl VirtualPolynomial { } } + +/// Evaluate eq polynomial. +pub fn eq_eval(x: &[F], y: &[F]) -> Result { + if x.len() != y.len() { + return Err(ArithErrors::InvalidParameters( + "x and y have different length".to_string(), + )); + } + let start = start_timer!(|| "eq_eval"); + let mut res = F::one(); + for (&xi, &yi) in x.iter().zip(y.iter()) { + let xi_yi = xi * yi; + res *= xi_yi + xi_yi - xi - yi + F::one(); + } + end_timer!(start); + Ok(res) +} + /// This function build the eq(x, r) polynomial for any given r. /// /// Evaluate diff --git a/hyperplonk/Cargo.toml b/hyperplonk/Cargo.toml index 5045d17e..d1dd5393 100644 --- a/hyperplonk/Cargo.toml +++ b/hyperplonk/Cargo.toml @@ -24,7 +24,6 @@ rayon = { version = "1.5.2", default-features = false, optional = true } [dev-dependencies] ark-bls12-381 = { version = "0.3.0", default-features = false, features = [ "curve" ] } - # Benchmarks [[bench]] name = "hyperplonk-benches" diff --git a/hyperplonk/src/mock.rs b/hyperplonk/src/mock.rs index 6af98a2c..778ed0e1 100644 --- a/hyperplonk/src/mock.rs +++ b/hyperplonk/src/mock.rs @@ -10,6 +10,7 @@ use crate::{ }; pub struct MockCircuit { + pub public_inputs: Vec, pub witnesses: Vec>, pub index: HyperPlonkIndex, } @@ -85,10 +86,11 @@ impl MockCircuit { witnesses[i].append(cur_witness[i]); } } + let public_inputs=witnesses[0].0[0..4].to_vec(); let params = HyperPlonkParams { num_constraints, - num_pub_input: num_constraints, + num_pub_input: public_inputs.len(), gate_func: gate.clone(), }; @@ -99,7 +101,7 @@ impl MockCircuit { selectors, }; - Self { witnesses, index } + Self { public_inputs,witnesses, index } } pub fn is_satisfied(&self) -> bool { @@ -144,7 +146,7 @@ mod test { const SUPPORTED_SIZE: usize = 20; const MIN_NUM_VARS: usize = 8; - const MAX_NUM_VARS: usize = 15; + const MAX_NUM_VARS: usize = 10; const CUSTOM_DEGREE: [usize; 6] = [1, 2, 4, 8, 16, 32]; #[test] @@ -177,7 +179,6 @@ mod test { assert!(circuit.is_satisfied()); let index = circuit.index; - // generate pk and vks let (pk, vk) = as HyperPlonkSNARK>>::preprocess( @@ -187,14 +188,14 @@ mod test { let proof = as HyperPlonkSNARK>>::prove( &pk, - &circuit.witnesses[0].0, + &circuit.public_inputs, &circuit.witnesses, )?; let verify = as HyperPlonkSNARK>>::verify( &vk, - &circuit.witnesses[0].0, + &circuit.public_inputs, &proof, )?; assert!(verify); diff --git a/hyperplonk/src/snark.rs b/hyperplonk/src/snark.rs index 4318d999..b0876245 100644 --- a/hyperplonk/src/snark.rs +++ b/hyperplonk/src/snark.rs @@ -324,8 +324,7 @@ where // - 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(); - 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], &r_pi); end_timer!(step); // ======================================================================= @@ -515,7 +514,7 @@ where // ======================================================================= // 3. Verify the opening against the commitment // ======================================================================= - let step = start_timer!(|| "verify commitments"); + let step = start_timer!(|| "assemble commitments"); // generate evaluation points and commitments let mut comms = vec![]; @@ -535,7 +534,6 @@ where 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); @@ -575,10 +573,11 @@ where // - 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[..]); + let expect_pi_eval = evaluate_opt(&pi_poly, &r_pi); if expect_pi_eval != *pi_eval { return Err(HyperPlonkErrors::InvalidProver(format!( "Public input eval mismatch: got {}, expect {}", @@ -586,10 +585,11 @@ where ))); } comms.push(proof.witness_commits[0]); - points.push(tmp_point); - + points.push(r_pi); assert_eq!(comms.len(), proof.batch_openings.f_i_eval_at_point_i.len()); + end_timer!(step); + let step=start_timer!(||"PCS batch verify"); // check proof let res = PCS::batch_verify( &vk.pcs_param, diff --git a/subroutines/src/poly_iop/zero_check/mod.rs b/subroutines/src/poly_iop/zero_check/mod.rs index 12d34247..144e8af2 100644 --- a/subroutines/src/poly_iop/zero_check/mod.rs +++ b/subroutines/src/poly_iop/zero_check/mod.rs @@ -3,9 +3,8 @@ use std::fmt::Debug; use crate::poly_iop::{errors::PolyIOPErrors, sum_check::SumCheck, PolyIOP}; -use arithmetic::build_eq_x_r; +use arithmetic::eq_eval; use ark_ff::PrimeField; -use ark_poly::MultilinearExtension; use ark_std::{end_timer, start_timer}; use transcript::IOPTranscript; @@ -103,11 +102,8 @@ impl ZeroCheck for PolyIOP { // expected_eval = sumcheck.expect_eval/eq(v, r) // where v = sum_check_sub_claim.point - let eq_x_r = build_eq_x_r(&r)?; - let expected_evaluation = sum_subclaim.expected_evaluation - / eq_x_r.evaluate(&sum_subclaim.point).ok_or_else(|| { - PolyIOPErrors::InvalidParameters("evaluation dimension does not match".to_string()) - })?; + let eq_x_r_eval = eq_eval(&sum_subclaim.point, &r)?; + let expected_evaluation = sum_subclaim.expected_evaluation / eq_x_r_eval; end_timer!(start); Ok(ZeroCheckSubClaim { From d92aad4b4be2147c1812c530a4dec4286aaba5bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benedikt=20Bu=CC=88nz?= Date: Fri, 18 Nov 2022 18:07:56 -0800 Subject: [PATCH 2/5] fix small public inputs. Only works for pow2 pubinput --- hyperplonk/src/mock.rs | 2 +- hyperplonk/src/snark.rs | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/hyperplonk/src/mock.rs b/hyperplonk/src/mock.rs index 778ed0e1..2ad7fded 100644 --- a/hyperplonk/src/mock.rs +++ b/hyperplonk/src/mock.rs @@ -146,7 +146,7 @@ mod test { const SUPPORTED_SIZE: usize = 20; const MIN_NUM_VARS: usize = 8; - const MAX_NUM_VARS: usize = 10; + const MAX_NUM_VARS: usize = 19; const CUSTOM_DEGREE: [usize; 6] = [1, 2, 4, 8, 16, 32]; #[test] diff --git a/hyperplonk/src/snark.rs b/hyperplonk/src/snark.rs index b0876245..81442c6c 100644 --- a/hyperplonk/src/snark.rs +++ b/hyperplonk/src/snark.rs @@ -7,7 +7,7 @@ use crate::{ }; use arithmetic::{evaluate_opt, identity_permutation_mles, VPAuxInfo}; use ark_ec::PairingEngine; -use ark_poly::DenseMultilinearExtension; +use ark_poly::{DenseMultilinearExtension}; use ark_std::{end_timer, log2, start_timer, One, Zero}; use std::{marker::PhantomData, rc::Rc}; use subroutines::{ @@ -324,7 +324,8 @@ where // - 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)?; - pcs_acc.insert_poly_and_points(&witness_polys[0], &witness_commits[0], &r_pi); + let r_pi_padded = [r_pi,vec![E::Fr::zero(); num_vars - ell]].concat(); + pcs_acc.insert_poly_and_points(&witness_polys[0], &witness_commits[0], &r_pi_padded); end_timer!(step); // ======================================================================= @@ -574,18 +575,19 @@ where // - pi_poly(r_pi) where r_pi is sampled from transcript let r_pi = transcript.get_and_append_challenge_vectors(b"r_pi", ell)?; - // check public evaluation let pi_poly = DenseMultilinearExtension::from_evaluations_slice(ell as usize, pub_input); - let expect_pi_eval = evaluate_opt(&pi_poly, &r_pi); + let expect_pi_eval = evaluate_opt(&pi_poly, &r_pi[..]); if expect_pi_eval != *pi_eval { return Err(HyperPlonkErrors::InvalidProver(format!( "Public input eval mismatch: got {}, expect {}", pi_eval, expect_pi_eval, ))); } + let r_pi_padded = [ r_pi,vec![E::Fr::zero(); num_vars - ell]].concat(); + comms.push(proof.witness_commits[0]); - points.push(r_pi); + points.push(r_pi_padded); assert_eq!(comms.len(), proof.batch_openings.f_i_eval_at_point_i.len()); end_timer!(step); From 7ef66fb865e0b791b331a08865e8472a68c3e9e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benedikt=20Bu=CC=88nz?= Date: Sat, 19 Nov 2022 10:48:11 -0800 Subject: [PATCH 3/5] comments --- hyperplonk/src/snark.rs | 4 ++++ hyperplonk/src/utils.rs | 7 +++++++ subroutines/src/poly_iop/sum_check/verifier.rs | 1 + 3 files changed, 12 insertions(+) diff --git a/hyperplonk/src/snark.rs b/hyperplonk/src/snark.rs index 81442c6c..b0591a64 100644 --- a/hyperplonk/src/snark.rs +++ b/hyperplonk/src/snark.rs @@ -324,7 +324,10 @@ where // - 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)?; + //padded with zeros let r_pi_padded = [r_pi,vec![E::Fr::zero(); num_vars - ell]].concat(); + //Evaluate witness_poly[0] at r_pi||0s which is equal to public_input evaluated at r_pi. + //Assumes that public_input is a power of 2 pcs_acc.insert_poly_and_points(&witness_polys[0], &witness_commits[0], &r_pi_padded); end_timer!(step); @@ -619,6 +622,7 @@ mod tests { use ark_std::test_rng; use subroutines::pcs::prelude::MultilinearKzgPCS; + #[test] fn test_hyperplonk_e2e() -> Result<(), HyperPlonkErrors> { // Example: diff --git a/hyperplonk/src/utils.rs b/hyperplonk/src/utils.rs index da11f13a..509ebfbc 100644 --- a/hyperplonk/src/utils.rs +++ b/hyperplonk/src/utils.rs @@ -137,6 +137,13 @@ pub(crate) fn prover_sanity_check( params.num_pub_input ))); } + if !pub_input.len().is_power_of_two(){ + return Err(HyperPlonkErrors::InvalidProver(format!( + "Public input length is not power of two: got {}", + pub_input.len(), + ))); + } + // witnesses length for (i, w) in witnesses.iter().enumerate() { if w.0.len() != params.num_constraints { diff --git a/subroutines/src/poly_iop/sum_check/verifier.rs b/subroutines/src/poly_iop/sum_check/verifier.rs index 90edb488..af0382f5 100644 --- a/subroutines/src/poly_iop/sum_check/verifier.rs +++ b/subroutines/src/poly_iop/sum_check/verifier.rs @@ -178,6 +178,7 @@ impl SumCheckVerifier for IOPVerifierState { /// This implementation is linear in number of inputs in terms of field /// operations. It also has a quadratic term in primitive operations which is /// negligible compared to field operations. +/// TODO: The quadratic term can be removed by precomputing the lagrange coefficients. fn interpolate_uni_poly(p_i: &[F], eval_at: F) -> Result { let start = start_timer!(|| "sum check interpolate uni poly opt"); From c9f751b4419747e1ad7d789f2339f2adafc17ddd Mon Sep 17 00:00:00 2001 From: Charles Chen Date: Sun, 20 Nov 2022 11:18:21 -0500 Subject: [PATCH 4/5] cargo fmt --- arithmetic/src/lib.rs | 4 +++- arithmetic/src/virtual_polynomial.rs | 1 - hyperplonk/src/mock.rs | 8 ++++++-- hyperplonk/src/snark.rs | 15 +++++++-------- hyperplonk/src/utils.rs | 2 +- subroutines/src/poly_iop/sum_check/verifier.rs | 3 ++- 6 files changed, 19 insertions(+), 14 deletions(-) diff --git a/arithmetic/src/lib.rs b/arithmetic/src/lib.rs index 06161f50..dfedeafd 100644 --- a/arithmetic/src/lib.rs +++ b/arithmetic/src/lib.rs @@ -12,4 +12,6 @@ pub use multilinear_polynomial::{ }; 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, build_eq_x_r_vec,eq_eval, VPAuxInfo, VirtualPolynomial}; +pub use virtual_polynomial::{ + build_eq_x_r, build_eq_x_r_vec, eq_eval, VPAuxInfo, VirtualPolynomial, +}; diff --git a/arithmetic/src/virtual_polynomial.rs b/arithmetic/src/virtual_polynomial.rs index c476d844..80943379 100644 --- a/arithmetic/src/virtual_polynomial.rs +++ b/arithmetic/src/virtual_polynomial.rs @@ -325,7 +325,6 @@ impl VirtualPolynomial { } } - /// Evaluate eq polynomial. pub fn eq_eval(x: &[F], y: &[F]) -> Result { if x.len() != y.len() { diff --git a/hyperplonk/src/mock.rs b/hyperplonk/src/mock.rs index 2ad7fded..04a3e82e 100644 --- a/hyperplonk/src/mock.rs +++ b/hyperplonk/src/mock.rs @@ -86,7 +86,7 @@ impl MockCircuit { witnesses[i].append(cur_witness[i]); } } - let public_inputs=witnesses[0].0[0..4].to_vec(); + let public_inputs = witnesses[0].0[0..4].to_vec(); let params = HyperPlonkParams { num_constraints, @@ -101,7 +101,11 @@ impl MockCircuit { selectors, }; - Self { public_inputs,witnesses, index } + Self { + public_inputs, + witnesses, + index, + } } pub fn is_satisfied(&self) -> bool { diff --git a/hyperplonk/src/snark.rs b/hyperplonk/src/snark.rs index b0591a64..e1615ac7 100644 --- a/hyperplonk/src/snark.rs +++ b/hyperplonk/src/snark.rs @@ -7,7 +7,7 @@ use crate::{ }; use arithmetic::{evaluate_opt, identity_permutation_mles, VPAuxInfo}; use ark_ec::PairingEngine; -use ark_poly::{DenseMultilinearExtension}; +use ark_poly::DenseMultilinearExtension; use ark_std::{end_timer, log2, start_timer, One, Zero}; use std::{marker::PhantomData, rc::Rc}; use subroutines::{ @@ -324,10 +324,10 @@ where // - 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)?; - //padded with zeros - let r_pi_padded = [r_pi,vec![E::Fr::zero(); num_vars - ell]].concat(); - //Evaluate witness_poly[0] at r_pi||0s which is equal to public_input evaluated at r_pi. - //Assumes that public_input is a power of 2 + // padded with zeros + let r_pi_padded = [r_pi, vec![E::Fr::zero(); num_vars - ell]].concat(); + // Evaluate witness_poly[0] at r_pi||0s which is equal to public_input evaluated + // at r_pi. Assumes that public_input is a power of 2 pcs_acc.insert_poly_and_points(&witness_polys[0], &witness_commits[0], &r_pi_padded); end_timer!(step); @@ -587,14 +587,14 @@ where pi_eval, expect_pi_eval, ))); } - let r_pi_padded = [ r_pi,vec![E::Fr::zero(); num_vars - ell]].concat(); + let r_pi_padded = [r_pi, vec![E::Fr::zero(); num_vars - ell]].concat(); comms.push(proof.witness_commits[0]); points.push(r_pi_padded); assert_eq!(comms.len(), proof.batch_openings.f_i_eval_at_point_i.len()); end_timer!(step); - let step=start_timer!(||"PCS batch verify"); + let step = start_timer!(|| "PCS batch verify"); // check proof let res = PCS::batch_verify( &vk.pcs_param, @@ -622,7 +622,6 @@ mod tests { use ark_std::test_rng; use subroutines::pcs::prelude::MultilinearKzgPCS; - #[test] fn test_hyperplonk_e2e() -> Result<(), HyperPlonkErrors> { // Example: diff --git a/hyperplonk/src/utils.rs b/hyperplonk/src/utils.rs index 509ebfbc..69a6d5a8 100644 --- a/hyperplonk/src/utils.rs +++ b/hyperplonk/src/utils.rs @@ -137,7 +137,7 @@ pub(crate) fn prover_sanity_check( params.num_pub_input ))); } - if !pub_input.len().is_power_of_two(){ + if !pub_input.len().is_power_of_two() { return Err(HyperPlonkErrors::InvalidProver(format!( "Public input length is not power of two: got {}", pub_input.len(), diff --git a/subroutines/src/poly_iop/sum_check/verifier.rs b/subroutines/src/poly_iop/sum_check/verifier.rs index af0382f5..99377da2 100644 --- a/subroutines/src/poly_iop/sum_check/verifier.rs +++ b/subroutines/src/poly_iop/sum_check/verifier.rs @@ -178,7 +178,8 @@ impl SumCheckVerifier for IOPVerifierState { /// This implementation is linear in number of inputs in terms of field /// operations. It also has a quadratic term in primitive operations which is /// negligible compared to field operations. -/// TODO: The quadratic term can be removed by precomputing the lagrange coefficients. +/// TODO: The quadratic term can be removed by precomputing the lagrange +/// coefficients. fn interpolate_uni_poly(p_i: &[F], eval_at: F) -> Result { let start = start_timer!(|| "sum check interpolate uni poly opt"); From acb484fdfd3856a9305b8d466e5711713b448c01 Mon Sep 17 00:00:00 2001 From: Charles Chen Date: Sun, 20 Nov 2022 11:42:47 -0500 Subject: [PATCH 5/5] minor fix --- hyperplonk/src/mock.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hyperplonk/src/mock.rs b/hyperplonk/src/mock.rs index 04a3e82e..7fa43727 100644 --- a/hyperplonk/src/mock.rs +++ b/hyperplonk/src/mock.rs @@ -86,7 +86,8 @@ impl MockCircuit { witnesses[i].append(cur_witness[i]); } } - let public_inputs = witnesses[0].0[0..4].to_vec(); + let pub_input_len = ark_std::cmp::min(4, num_constraints); + let public_inputs = witnesses[0].0[0..pub_input_len].to_vec(); let params = HyperPlonkParams { num_constraints, @@ -150,7 +151,7 @@ mod test { const SUPPORTED_SIZE: usize = 20; const MIN_NUM_VARS: usize = 8; - const MAX_NUM_VARS: usize = 19; + const MAX_NUM_VARS: usize = 15; const CUSTOM_DEGREE: [usize; 6] = [1, 2, 4, 8, 16, 32]; #[test]