use crate::{ errors::HyperPlonkErrors, structs::{HyperPlonkIndex, HyperPlonkProof, HyperPlonkProvingKey, HyperPlonkVerifyingKey}, utils::{build_f, eval_f, prover_sanity_check, PcsAccumulator}, witness::WitnessColumn, HyperPlonkSNARK, }; use arithmetic::{ evaluate_opt, gen_eval_point, identity_permutation_mle, merge_polynomials, VPAuxInfo, }; 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 poly_iop::{ prelude::{PermutationCheck, ZeroCheck}, PolyIOP, }; use std::{cmp::max, marker::PhantomData, rc::Rc}; use transcript::IOPTranscript; impl HyperPlonkSNARK for PolyIOP where E: PairingEngine, // Ideally we want to access polynomial as PCS::Polynomial, instead of instantiating it here. // But since PCS::Polynomial can be both univariate or multivariate in our implementation // we cannot bound PCS::Polynomial with a property trait bound. PCS: PolynomialCommitmentScheme< E, Polynomial = Rc>, Point = Vec, Evaluation = E::Fr, >, { type Index = HyperPlonkIndex; type ProvingKey = HyperPlonkProvingKey; type VerifyingKey = HyperPlonkVerifyingKey; type Proof = HyperPlonkProof; fn preprocess( 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 log_num_selector_polys = log2(index.num_selector_columns()) as usize; let witness_merged_nv = num_vars + log_num_witness_polys; let selector_merged_nv = num_vars + log_num_selector_polys; let max_nv = max(witness_merged_nv + 1, selector_merged_nv); let max_points = max( // prod(x) has 5 points 5, max( // selector points index.num_selector_columns(), // witness points + public input point + perm point index.num_witness_columns() + 2, ), ); let supported_uni_degree = compute_qx_degree(max_nv, max_points); let supported_ml_degree = max_nv; // extract PCS prover and verifier keys from SRS let (pcs_prover_param, pcs_verifier_param) = PCS::trim(pcs_srs, supported_uni_degree, Some(supported_ml_degree))?; // 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)?; // build selector oracles and commit to it let selector_oracles: Vec>> = index .selectors .iter() .map(|s| Rc::new(DenseMultilinearExtension::from(s))) .collect(); let selector_merged = merge_polynomials(&selector_oracles)?; let selector_com = PCS::commit(&pcs_prover_param, &selector_merged)?; Ok(( Self::ProvingKey { params: index.params.clone(), permutation_oracle: permutation_oracle.clone(), selector_oracles, selector_com: selector_com.clone(), pcs_param: pcs_prover_param, }, Self::VerifyingKey { params: index.params.clone(), permutation_oracle, pcs_param: pcs_verifier_param, selector_com, perm_com, }, )) } /// Generate HyperPlonk SNARK proof. /// /// Inputs: /// - `pk`: circuit proving key /// - `pub_input`: online public input of length 2^\ell /// - `witness`: witness assignment of length 2^n /// Outputs: /// - The HyperPlonk SNARK proof. /// /// Steps: /// /// 1. Commit Witness polynomials `w_i(x)` and append commitment to /// transcript /// /// 2. Run ZeroCheck on /// /// `f(q_0(x),...q_l(x), w_0(x),...w_d(x))` /// /// where `f` is the constraint polynomial i.e., /// ```ignore /// f(q_l, q_r, q_m, q_o, w_a, w_b, w_c) /// = 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 /// /// 3. Run permutation check on `\{w_i(x)\}` and `permutation_oracle`, and /// obtain a PermCheckSubClaim. /// /// 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] /// - [1,...1, 0] /// /// - 4.2. permutation check evaluations and proofs /// - 4.2.1. (deferred) wi_poly(perm_check_point) /// /// - 4.3. zero check evaluations and proofs /// - 4.3.1. (deferred) wi_poly(zero_check_point) /// - 4.3.2. (deferred) selector_poly(zero_check_point) /// /// - 4.4. public input consistency checks /// - pi_poly(r_pi) where r_pi is sampled from transcript /// /// - 5. deferred batch opening fn prove( pk: &Self::ProvingKey, pub_input: &[E::Fr], witnesses: &[WitnessColumn], ) -> Result { let start = start_timer!(|| "hyperplonk proving"); let mut transcript = IOPTranscript::::new(b"hyperplonk"); 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 log_num_selector_polys = log2(pk.params.num_selector_columns()) as usize; // 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(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 for w_merged and its points let mut w_merged_pcs_acc = PcsAccumulator::::new(); // Accumulator for prod(x) and its points let mut prod_pcs_acc = PcsAccumulator::::new(); // Accumulator for prod(x) and its points let mut selector_pcs_acc = PcsAccumulator::::new(); let witness_polys: Vec>> = witnesses .iter() .map(|w| Rc::new(DenseMultilinearExtension::from(w))) .collect(); // ======================================================================= // 1. Commit Witness polynomials `w_i(x)` and append commitment to // transcript // ======================================================================= let step = start_timer!(|| "commit witnesses"); 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 ))); } 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 // // `f(q_0(x),...q_l(x), w_0(x),...w_d(x))` // // where `f` is the constraint polynomial i.e., // // f(q_l, q_r, q_m, q_o, w_a, w_b, w_c) // = 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 // ======================================================================= let step = start_timer!(|| "ZeroCheck on f"); let fx = build_f( &pk.params.gate_func, pk.params.num_variables(), &pk.selector_oracles, &witness_polys, )?; let zero_check_proof = >::prove(&fx, &mut transcript)?; end_timer!(step); // ======================================================================= // 3. Run permutation check on `\{w_i(x)\}` and `permutation_oracle`, and // obtain a PermCheckSubClaim. // ======================================================================= let step = start_timer!(|| "Permutation check on w_i(x)"); let (perm_check_proof, prod_x) = >::prove( &pk.pcs_param, &w_merged, &w_merged, &pk.permutation_oracle, &mut transcript, )?; let perm_check_point = &perm_check_proof.zero_check_proof.point; 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] // - [1,...1, 0] // // - 4.2. permutation check evaluations and proofs // - 4.2.1. (deferred) wi_poly(perm_check_point) // // - 4.3. zero check evaluations and proofs // - 4.3.1. (deferred) wi_poly(zero_check_point) // - 4.3.2. (deferred) selector_poly(zero_check_point) // // - 4.4. (deferred) public input consistency checks // - 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_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) let tmp_point2 = [perm_check_point.as_slice(), &[E::Fr::one()]].concat(); // prod(x, 0) let tmp_point3 = [&[E::Fr::zero()], perm_check_point.as_slice()].concat(); // prod(x, 1) let tmp_point4 = [&[E::Fr::one()], perm_check_point.as_slice()].concat(); // prod(1, ..., 1, 0) let tmp_point5 = [vec![E::Fr::zero()], vec![E::Fr::one(); merged_nv]].concat(); prod_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); // 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(), )); } } // - 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); } // - 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())?; 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); } // - 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, 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(), )); } } end_timer!(step); // ======================================================================= // 5. deferred batch opening // ======================================================================= 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)?; 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)?; end_timer!(sub_step); let sub_step = start_timer!(|| "open selector"); let (selector_batch_opening, selector_batch_evals) = selector_pcs_acc.batch_open(&pk.pcs_param)?; end_timer!(sub_step); end_timer!(step); end_timer!(start); Ok(HyperPlonkProof { // ======================================================================= // witness related // ======================================================================= /// PCS commit for witnesses 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, // Evaluations of Witness // - PermCheck eval: 1 point // - ZeroCheck evals: #witness points // - public input eval: 1 point w_merged_batch_evals, // ======================================================================= // prod(x) related // ======================================================================= // prod(x)'s openings // - prod(0, x), // - prod(1, x), // - prod(x, 0), // - prod(x, 1), // - prod(1, ..., 1,0) prod_batch_openings, // prod(x)'s evaluations // - prod(0, x), // - prod(1, x), // - prod(x, 0), // - prod(x, 1), // - prod(1, ..., 1,0) prod_batch_evals, // ======================================================================= // selectors related // ======================================================================= // PCS openings for selectors on zero check point selector_batch_opening, // Evaluates of selectors on zero check point selector_batch_evals, // ======================================================================= // IOP proofs // ======================================================================= // the custom gate zerocheck proof zero_check_proof, // the permutation check proof for copy constraints perm_check_proof, }) } /// Verify the HyperPlonk proof. /// /// Inputs: /// - `vk`: verification key /// - `pub_input`: online public input /// - `proof`: HyperPlonk SNARK proof /// Outputs: /// - Return a boolean on whether the verification is successful /// /// 1. Verify zero_check_proof on /// /// `f(q_0(x),...q_l(x), w_0(x),...w_d(x))` /// /// where `f` is the constraint polynomial i.e., /// ```ignore /// f(q_l, q_r, q_m, q_o, w_a, w_b, w_c) /// = 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` /// /// 3. check subclaim validity /// /// 4. Verify the opening against the commitment: /// - check permutation check evaluations /// - check zero check evaluations /// - public input consistency checks fn verify( vk: &Self::VerifyingKey, pub_input: &[E::Fr], proof: &Self::Proof, ) -> Result { let start = start_timer!(|| "hyperplonk verification"); let mut transcript = IOPTranscript::::new(b"hyperplonk"); // witness assignment of length 2^n let num_vars = vk.params.num_variables(); let log_num_witness_polys = log2(vk.params.num_witness_columns()) as usize; // 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; let pi_poly = DenseMultilinearExtension::from_evaluations_slice(ell as usize, pub_input); // ======================================================================= // 0. sanity checks // ======================================================================= // public input length if pub_input.len() != vk.params.num_pub_input { return Err(HyperPlonkErrors::InvalidProver(format!( "Public input length is not correct: got {}, expect {}", pub_input.len(), 1 << ell ))); } if proof.selector_batch_evals.len() - 1 != vk.params.num_selector_columns() { return Err(HyperPlonkErrors::InvalidVerifier(format!( "Selector length is not correct: got {}, expect {}", proof.selector_batch_evals.len() - 1, 1 << vk.params.num_selector_columns() ))); } if proof.w_merged_batch_evals.len() != vk.params.num_witness_columns() + 3 { return Err(HyperPlonkErrors::InvalidVerifier(format!( "Witness length is not correct: got {}, expect {}", proof.w_merged_batch_evals.len() - 3, vk.params.num_witness_columns() ))); } if proof.prod_batch_evals.len() - 1 != 5 { return Err(HyperPlonkErrors::InvalidVerifier(format!( "the number of product polynomial evaluations is not correct: got {}, expect {}", proof.prod_batch_evals.len() - 1, 5 ))); } // ======================================================================= // 1. Verify zero_check_proof on // `f(q_0(x),...q_l(x), w_0(x),...w_d(x))` // // where `f` is the constraint polynomial i.e., // // f(q_l, q_r, q_m, q_o, w_a, w_b, w_c) // = q_l w_a(x) + q_r w_b(x) + q_m w_a(x)w_b(x) - q_o w_c(x) // // ======================================================================= let step = start_timer!(|| "verify zero check"); // Zero check and perm check have different AuxInfo let zero_check_aux_info = VPAuxInfo:: { max_degree: vk.params.gate_func.degree(), num_variables: num_vars, phantom: PhantomData::default(), }; // push witness to transcript transcript.append_serializable_element(b"w", &proof.w_merged_com)?; let zero_check_sub_claim = >::verify( &proof.zero_check_proof, &zero_check_aux_info, &mut transcript, )?; let zero_check_point = &zero_check_sub_claim.point; // 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..], )?; if f_eval != zero_check_sub_claim.expected_evaluation { return Err(HyperPlonkErrors::InvalidProof( "zero check evaluation failed".to_string(), )); } end_timer!(step); // ======================================================================= // 2. Verify perm_check_proof on `\{w_i(x)\}` and `permutation_oracle` // ======================================================================= let step = start_timer!(|| "verify permutation check"); // Zero check and perm check have different AuxInfo let perm_check_aux_info = VPAuxInfo:: { // Prod(x) has a max degree of 2 max_degree: 2, // degree of merged poly num_variables: merged_nv, phantom: PhantomData::default(), }; let perm_check_sub_claim = >::verify( &proof.perm_check_proof, &perm_check_aux_info, &mut transcript, )?; let perm_check_point = &perm_check_sub_claim .product_check_sub_claim .zero_check_sub_claim .point; 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_id = identity_permutation_mle::(perm_check_point.len()); 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] + 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)); if q_x_rec != perm_check_sub_claim .product_check_sub_claim .zero_check_sub_claim .expected_evaluation { return Err(HyperPlonkErrors::InvalidVerifier( "evaluation failed".to_string(), )); } end_timer!(step); // ======================================================================= // 3. Verify the opening against the commitment // ======================================================================= let step = start_timer!(|| "verify commitments"); // ======================================================================= // 3.1 open prod(x)' evaluations // ======================================================================= let prod_final_query = perm_check_sub_claim.product_check_sub_claim.final_query; let 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, ]; 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 // ======================================================================= let log_num_selector_polys = log2(vk.params.num_selector_columns()) as usize; let mut points = vec![]; for i in 0..vk.params.num_selector_columns() { let tmp_point = gen_eval_point(i, log_num_selector_polys, &proof.zero_check_proof.point); points.push(tmp_point); } 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(), )); } // ======================================================================= // 3.2 open witnesses' evaluations // ======================================================================= let mut r_pi = transcript.get_and_append_challenge_vectors(b"r_pi", ell)?; 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] ); r_pi = [ vec![E::Fr::zero(); num_vars - ell], r_pi, vec![E::Fr::zero(); log_num_witness_polys], ] .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( &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(), )); } end_timer!(step); end_timer!(start); Ok(true) } } #[cfg(test)] mod tests { use super::*; use crate::{ custom_gate::CustomizedGates, selectors::SelectorColumn, structs::HyperPlonkParams, witness::WitnessColumn, }; use arithmetic::random_permutation_mle; use ark_bls12_381::Bls12_381; use ark_std::test_rng; use pcs::prelude::MultilinearKzgPCS; #[test] fn test_hyperplonk_e2e() -> Result<(), HyperPlonkErrors> { // Example: // q_L(X) * W_1(X)^5 - W_2(X) = 0 // is represented as // vec![ // ( 1, Some(id_qL), vec![id_W1, id_W1, id_W1, id_W1, id_W1]), // (-1, None, vec![id_W2]) // ] // // 4 public input // 1 selector, // 2 witnesses, // 2 variables for MLE, // 4 wires, let gates = CustomizedGates { gates: vec![(1, Some(0), vec![0, 0, 0, 0, 0]), (-1, None, vec![1])], }; test_hyperplonk_helper::(gates) } fn test_hyperplonk_helper( gate_func: CustomizedGates, ) -> Result<(), HyperPlonkErrors> { let mut rng = test_rng(); let pcs_srs = MultilinearKzgPCS::::gen_srs_for_testing(&mut rng, 16)?; 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; // generate index let params = HyperPlonkParams { num_constraints, num_pub_input, gate_func, }; let permutation = identity_permutation_mle(merged_nv).evaluations.clone(); let q1 = SelectorColumn(vec![E::Fr::one(), E::Fr::one(), E::Fr::one(), E::Fr::one()]); let index = HyperPlonkIndex { params, permutation, selectors: vec![q1], }; // generate pk and vks let (pk, vk) = as HyperPlonkSNARK>>::preprocess( &index, &pcs_srs, )?; // w1 := [0, 1, 2, 3] let w1 = WitnessColumn(vec![ E::Fr::zero(), E::Fr::one(), E::Fr::from(2u64), E::Fr::from(3u64), ]); // w2 := [0^5, 1^5, 2^5, 3^5] let w2 = WitnessColumn(vec![ E::Fr::zero(), E::Fr::one(), E::Fr::from(32u64), E::Fr::from(243u64), ]); // public input = w1 let pi = w1.clone(); // generate a proof and verify let proof = as HyperPlonkSNARK>>::prove( &pk, &pi.0, &[w1.clone(), w2.clone()], )?; let _verify = as HyperPlonkSNARK>>::verify( &vk, &pi.0, &proof, )?; // bad path 1: wrong permutation let rand_perm: Vec = random_permutation_mle(merged_nv, &mut rng) .evaluations .clone(); let mut bad_index = index.clone(); bad_index.permutation = rand_perm; // generate pk and vks let (_, bad_vk) = as HyperPlonkSNARK>>::preprocess( &bad_index, &pcs_srs, )?; assert!( as HyperPlonkSNARK>>::verify( &bad_vk, &pi.0, &proof, ) .is_err() ); // bad path 2: wrong witness let mut w1_bad = w1.clone(); w1_bad.0[0] = E::Fr::one(); assert!( as HyperPlonkSNARK>>::prove( &pk, &pi.0, &[w1_bad, w2], ) .is_err() ); Ok(()) } }