arnaucube
arnaucube/hyperplonkPublic Self-hosted repository
49 verify validity of subclaims in plonk verification (#51)

zhenfei zhenfei.zhang@hotmail.com
Tue, 09 Aug 2022 12:40:28 -0400
commit

066c370b3c086eb0255bd5d8a717defb0195b890

parent

a6ea6ac26bc4fde004ac360b1a49ffb531ef45fe

M hyperplonk/Cargo.toml → hyperplonk/Cargo.toml

@@ -25,8 +25,11 @@ ark-bls12-381 = { version = "0.3.0", default-features = false, features = [ "curve" ] }

[features] -default = [ "parallel", "print-trace" ] -# default = [ "parallel" ] +# default = [ "parallel", "print-trace", "extensive_sanity_checks" ] +default = [ "parallel", "extensive_sanity_checks" ] + +# extensive sanity checks that are useful for debugging +extensive_sanity_checks = [ ] parallel = [ "ark-std/parallel", "ark-ff/parallel",
M hyperplonk/src/lib.rs → hyperplonk/src/lib.rs

@@ -1,5 +1,6 @@

//! Main module for the HyperPlonk PolyIOP. +use crate::utils::eval_f; use arithmetic::VPAuxInfo; use ark_ec::PairingEngine; use ark_poly::{DenseMultilinearExtension, MultilinearExtension};

@@ -7,15 +8,14 @@ use ark_std::{end_timer, log2, start_timer, One, Zero};

use errors::HyperPlonkErrors; use pcs::prelude::{compute_qx_degree, merge_polynomials, PCSErrors, PolynomialCommitmentScheme}; use poly_iop::{ + identity_permutation_mle, prelude::{PermutationCheck, SumCheck, ZeroCheck}, PolyIOP, }; use selectors::SelectorColumn; use std::{marker::PhantomData, rc::Rc}; -use structs::{ - HyperPlonkParams, HyperPlonkProof, HyperPlonkProvingKey, HyperPlonkSubClaim, - HyperPlonkVerifyingKey, -}; +use structs::{HyperPlonkParams, HyperPlonkProof, HyperPlonkProvingKey, HyperPlonkVerifyingKey}; +use transcript::IOPTranscript; use utils::build_f; use witness::WitnessColumn;

@@ -37,7 +37,6 @@ type Parameters;

type ProvingKey; type VerifyingKey; type Proof; - type SubClaim; /// Generate the preprocessed polynomials output by the indexer. ///

@@ -61,35 +60,27 @@ /// Inputs:

/// - `pk`: circuit proving key /// - `pub_input`: online public input /// - `witness`: witness assignment - /// - `transcript`: the transcript used for generating pseudorandom - /// challenges /// Outputs: /// - The HyperPlonk SNARK proof. fn prove( pk: &Self::ProvingKey, pub_input: &[E::Fr], witnesses: &[WitnessColumn<E::Fr>], - transcript: &mut Self::Transcript, ) -> Result<Self::Proof, HyperPlonkErrors>; - /// Verify the HyperPlonk proof and generate the evaluation subclaims to be - /// checked later by the SNARK verifier. + /// Verify the HyperPlonk proof. /// /// Inputs: /// - `params`: instance parameter /// - `pub_input`: online public input - /// - `proof`: HyperPlonk SNARK proof - /// - `transcript`: the transcript used for generating pseudorandom - /// challenges + /// - `proof`: HyperPlonk SNARK proof challenges /// Outputs: - /// - Return error if the verification fails, otherwise return the - /// evaluation subclaim + /// - Return a boolean on whether the verification is successful fn verify( params: &Self::VerifyingKey, pub_input: &[E::Fr], proof: &Self::Proof, - transcript: &mut Self::Transcript, - ) -> Result<Self::SubClaim, HyperPlonkErrors>; + ) -> Result<bool, HyperPlonkErrors>; } impl<E, PCS> HyperPlonkSNARK<E, PCS> for PolyIOP<E::Fr>

@@ -109,7 +100,6 @@ type Parameters = HyperPlonkParams;

type ProvingKey = HyperPlonkProvingKey<E, PCS>; type VerifyingKey = HyperPlonkVerifyingKey<E, PCS>; type Proof = HyperPlonkProof<E, PCS, Self, Self>; - type SubClaim = HyperPlonkSubClaim<E::Fr, Self, Self>; /// Generate the preprocessed polynomials output by the indexer. ///

@@ -143,11 +133,9 @@ )?;

// build permutation oracles let permutation_oracles = Rc::new(DenseMultilinearExtension::from_evaluations_slice( - // num_vars = merged_nv + 1 because this oracle encodes both s_id and s_perm - merged_nv + 1, + merged_nv, permutation, )); - let perm_com = PCS::commit(&pcs_prover_param, &permutation_oracles)?; // build selector oracles and commit to it

@@ -183,8 +171,6 @@ /// Inputs:

/// - `pk`: circuit proving key /// - `pub_input`: online public input of length 2^\ell /// - `witness`: witness assignment of length 2^n - /// - `transcript`: the transcript used for generating pseudorandom - /// challenges /// Outputs: /// - The HyperPlonk SNARK proof. ///

@@ -211,13 +197,15 @@ /// 4. Generate evaluations and corresponding proofs

/// - permutation check evaluations and proofs /// - zero check evaluations and proofs /// - public input consistency checks + /// + /// TODO: this function is gigantic -- refactor it to smaller ones fn prove( pk: &Self::ProvingKey, pub_input: &[E::Fr], witnesses: &[WitnessColumn<E::Fr>], - transcript: &mut Self::Transcript, ) -> Result<Self::Proof, HyperPlonkErrors> { let start = start_timer!(|| "hyperplonk proving"); + let mut transcript = IOPTranscript::<E::Fr>::new(b"hyperplonk"); // witness assignment of length 2^n let num_vars = pk.params.nv;

@@ -315,7 +303,7 @@ &pk.selector_oracles,

&witness_polys, )?; - let zero_check_proof = <Self as ZeroCheck<E::Fr>>::prove(&fx, transcript)?; + let zero_check_proof = <Self as ZeroCheck<E::Fr>>::prove(&fx, &mut transcript)?; end_timer!(step); // =======================================================================

@@ -327,36 +315,112 @@ // 3.2. `compute_product` to build `prod(x)` etc. from f, g and s_perm

// 3.3. push a commitment of `prod(x)` to the transcript // 3.4. `update_challenge` with the updated transcript // 3.5. `prove` to generate the proof + // 3.6. open `prod(0,x)`, `prod(1, x)`, `prod(x, 0)`, `prod(x, 1)` at + // zero_check.point // ======================================================================= let step = start_timer!(|| "Permutation check on w_i(x)"); // 3.1 `generate_challenge` from current transcript (generate beta, gamma) - let mut permutation_challenge = Self::generate_challenge(transcript)?; + let mut permutation_challenge = Self::generate_challenge(&mut transcript)?; // 3.2. `compute_product` to build `prod(x)` etc. from f, g and s_perm - // s_perm is the second half of permutation oracle - let s_perm = pk.permutation_oracles.fix_variables(&[E::Fr::one()]); // This function returns 3 MLEs: // - prod(x) // - numerator // - denominator // See function signature for details. - let prod_x_and_aux_info = - Self::compute_prod_evals(&permutation_challenge, &w_merged, &w_merged, &s_perm)?; + let prod_x_and_aux_info = Self::compute_prod_evals( + &permutation_challenge, + &w_merged, + &w_merged, + &pk.permutation_oracles, + )?; + let prod_x = Rc::new(prod_x_and_aux_info[0].clone()); // 3.3 push a commitment of `prod(x)` to the transcript - let prod_com = PCS::commit(&pk.pcs_param, &Rc::new(prod_x_and_aux_info[0].clone()))?; + let prod_com = PCS::commit(&pk.pcs_param, &prod_x)?; // 3.4. `update_challenge` with the updated transcript - Self::update_challenge(&mut permutation_challenge, transcript, &prod_com)?; + Self::update_challenge(&mut permutation_challenge, &mut transcript, &prod_com)?; // 3.5. `prove` to generate the proof let perm_check_proof = <Self as PermutationCheck<E::Fr>>::prove( &prod_x_and_aux_info, &permutation_challenge, - transcript, + &mut transcript, )?; + + // 3.6 open prod(0,x), prod(1, x), prod(x, 0), prod(x, 1) at zero_check.point + // prod(0, x) + let tmp_point = [perm_check_proof.point.as_slice(), &[E::Fr::zero()]].concat(); + let (prod_0_x_opening, prod_0_x_eval) = PCS::open(&pk.pcs_param, &prod_x, &tmp_point)?; + #[cfg(feature = "extensive_sanity_checks")] + { + // sanity check + let eval = prod_x.evaluate(&tmp_point).ok_or_else(|| { + HyperPlonkErrors::InvalidParameters( + "evaluation dimension does not match".to_string(), + ) + })?; + if eval != prod_0_x_eval { + return Err(HyperPlonkErrors::InvalidProver( + "Evaluation is different from PCS opening".to_string(), + )); + } + } + // prod(1, x) + let tmp_point = [perm_check_proof.point.as_slice(), &[E::Fr::one()]].concat(); + let (prod_1_x_opening, prod_1_x_eval) = PCS::open(&pk.pcs_param, &prod_x, &tmp_point)?; + #[cfg(feature = "extensive_sanity_checks")] + { + // sanity check + let eval = prod_x.evaluate(&tmp_point).ok_or_else(|| { + HyperPlonkErrors::InvalidParameters( + "evaluation dimension does not match".to_string(), + ) + })?; + if eval != prod_1_x_eval { + return Err(HyperPlonkErrors::InvalidProver( + "Evaluation is different from PCS opening".to_string(), + )); + } + } + // prod(x, 0) + let tmp_point = [&[E::Fr::zero()], perm_check_proof.point.as_slice()].concat(); + let (prod_x_0_opening, prod_x_0_eval) = PCS::open(&pk.pcs_param, &prod_x, &tmp_point)?; + #[cfg(feature = "extensive_sanity_checks")] + { + // sanity check + let eval = prod_x.evaluate(&tmp_point).ok_or_else(|| { + HyperPlonkErrors::InvalidParameters( + "evaluation dimension does not match".to_string(), + ) + })?; + + if eval != prod_x_0_eval { + return Err(HyperPlonkErrors::InvalidProver( + "Evaluation is different from PCS opening".to_string(), + )); + } + } + // prod(x, 1) + let tmp_point = [&[E::Fr::one()], perm_check_proof.point.as_slice()].concat(); + let (prod_x_1_opening, prod_x_1_eval) = PCS::open(&pk.pcs_param, &prod_x, &tmp_point)?; + #[cfg(feature = "extensive_sanity_checks")] + { + // sanity check + let eval = prod_x.evaluate(&tmp_point).ok_or_else(|| { + HyperPlonkErrors::InvalidParameters( + "evaluation dimension does not match".to_string(), + ) + })?; + if eval != prod_x_1_eval { + return Err(HyperPlonkErrors::InvalidProver( + "Evaluation is different from PCS opening".to_string(), + )); + } + } end_timer!(step); // =======================================================================

@@ -386,14 +450,20 @@ &pk.pcs_param,

&Rc::new(w_merged.clone()), &perm_check_proof.point, )?; - // sanity checks - if w_merged.evaluate(&perm_check_proof.point).ok_or_else(|| { - HyperPlonkErrors::InvalidParameters("evaluation dimension does not match".to_string()) - })? != witness_perm_check_eval + + #[cfg(feature = "extensive_sanity_checks")] { - return Err(HyperPlonkErrors::InvalidProver( - "Evaluation is different from PCS opening".to_string(), - )); + // sanity checks + let eval = w_merged.evaluate(&perm_check_proof.point).ok_or_else(|| { + HyperPlonkErrors::InvalidParameters( + "evaluation dimension does not match".to_string(), + ) + })?; + if eval != witness_perm_check_eval { + return Err(HyperPlonkErrors::InvalidProver( + "Evaluation is different from PCS opening".to_string(), + )); + } } // 4.2 open zero check proof

@@ -403,12 +473,12 @@ // Open zero check proof

let (zero_proof, zero_eval) = PCS::open(&pk.pcs_param, &wire_poly, &zero_check_proof.point)?; { - if wire_poly.evaluate(&zero_check_proof.point).ok_or_else(|| { + let eval = wire_poly.evaluate(&zero_check_proof.point).ok_or_else(|| { HyperPlonkErrors::InvalidParameters( "evaluation dimension does not match".to_string(), ) - })? != zero_eval - { + })?; + if eval != zero_eval { return Err(HyperPlonkErrors::InvalidProver( "Evaluation is different from PCS opening".to_string(), ));

@@ -418,26 +488,32 @@ witness_zero_check_evals.push(zero_eval);

witness_zero_check_openings.push(zero_proof); } - // Open permutation check proof - let (perm_oracle_opening, perm_oracle_eval) = PCS::open( + // Open permutation polynomial at perm_check_point + let (s_perm_opening, s_perm_eval) = PCS::open( &pk.pcs_param, &pk.permutation_oracles, - &[&[E::Fr::one()], perm_check_proof.point.as_slice()].concat(), + &perm_check_proof.point, )?; + + #[cfg(feature = "extensive_sanity_checks")] { // sanity check - if s_perm.evaluate(&perm_check_proof.point).ok_or_else(|| { - HyperPlonkErrors::InvalidParameters( - "evaluation dimension does not match".to_string(), - ) - })? != perm_oracle_eval - { + let eval = pk + .permutation_oracles + .evaluate(&perm_check_proof.point) + .ok_or_else(|| { + HyperPlonkErrors::InvalidParameters( + "evaluation dimension does not match".to_string(), + ) + })?; + if eval != s_perm_eval { return Err(HyperPlonkErrors::InvalidProver( "Evaluation is different from PCS opening".to_string(), )); } } + // Open selector polynomial at zero_check_point let mut selector_oracle_openings = vec![]; let mut selector_oracle_evals = vec![];

@@ -447,20 +523,19 @@ // Open zero check proof

// during verification, use this eval against subclaim let (zero_proof, zero_eval) = PCS::open(&pk.pcs_param, selector_poly, &zero_check_proof.point)?; + + #[cfg(feature = "extensive_sanity_checks")] { - if selector_poly + let eval = selector_poly .evaluate(&zero_check_proof.point) .ok_or_else(|| { HyperPlonkErrors::InvalidParameters( "evaluation dimension does not match".to_string(), ) - })? - != zero_eval - { + })?; + if eval != zero_eval { return Err(HyperPlonkErrors::InvalidProver( - "Evaluation is different from PCS - opening" - .to_string(), + "Evaluation is different from PCS opening".to_string(), )); } }

@@ -472,14 +547,16 @@ // 4.3 public input consistency checks

let r_pi = transcript.get_and_append_challenge_vectors(b"r_pi", ell)?; let (pi_opening, pi_eval) = PCS::open(&pk.pcs_param, &pi_in_w0, &r_pi)?; + + #[cfg(feature = "extensive_sanity_checks")] { // sanity check - if pi_poly.evaluate(&r_pi).ok_or_else(|| { + let eval = pi_poly.evaluate(&r_pi).ok_or_else(|| { HyperPlonkErrors::InvalidParameters( "evaluation dimension does not match".to_string(), ) - })? != pi_eval - { + })?; + if eval != pi_eval { return Err(HyperPlonkErrors::InvalidProver( "Evaluation is different from PCS opening".to_string(), ));

@@ -490,39 +567,55 @@ end_timer!(step);

end_timer!(start); Ok(HyperPlonkProof { - // PCS components + // ======================================================================= + // PCS components: common + // ======================================================================= witness_commits, w_merged_com, + // ======================================================================= + // PCS components: permutation check + // ======================================================================= // We do not validate prod(x), this is checked by subclaim prod_commit: prod_com, + prod_evals: vec![prod_0_x_eval, prod_1_x_eval, prod_x_0_eval, prod_x_1_eval], + prod_openings: vec![ + prod_0_x_opening, + prod_1_x_opening, + prod_x_0_opening, + prod_x_1_opening, + ], witness_perm_check_opening, - witness_zero_check_openings, witness_perm_check_eval, + perm_oracle_opening: s_perm_opening, + perm_oracle_eval: s_perm_eval, + // ======================================================================= + // PCS components: zero check + // ======================================================================= + witness_zero_check_openings, witness_zero_check_evals, - perm_oracle_opening, - perm_oracle_eval, selector_oracle_openings, selector_oracle_evals, + // ======================================================================= + // PCS components: public inputs + // ======================================================================= pi_eval, pi_opening, + // ======================================================================= // IOP components + // ======================================================================= zero_check_proof, perm_check_proof, }) } - /// Verify the HyperPlonk proof and generate the evaluation subclaims to be - /// checked later by the SNARK verifier. + /// Verify the HyperPlonk proof. /// /// Inputs: - /// - `params`: instance parameter + /// - `vk`: verification key /// - `pub_input`: online public input /// - `proof`: HyperPlonk SNARK proof - /// - `transcript`: the transcript used for generating pseudorandom - /// challenges /// Outputs: - /// - Return error if the verification fails, otherwise return the - /// evaluation subclaim + /// - Return a boolean on whether the verification is successful /// /// 1. Verify zero_check_proof on ///

@@ -546,10 +639,10 @@ fn verify(

vk: &Self::VerifyingKey, pub_input: &[E::Fr], proof: &Self::Proof, - transcript: &mut Self::Transcript, - ) -> Result<Self::SubClaim, HyperPlonkErrors> { + ) -> Result<bool, HyperPlonkErrors> { let start = start_timer!(|| "hyperplonk verification"); + let mut transcript = IOPTranscript::<E::Fr>::new(b"hyperplonk"); // witness assignment of length 2^n let num_var = vk.params.nv; let log_num_witness_polys = vk.params.log_n_wires;

@@ -600,12 +693,28 @@

let zero_check_sub_claim = <Self as ZeroCheck<E::Fr>>::verify( &proof.zero_check_proof, &zero_check_aux_info, - transcript, + &mut transcript, )?; + + let zero_check_point = &zero_check_sub_claim.sum_check_sub_claim.point; + + // check zero check subclaim + let f_eval = eval_f( + &vk.params.gate_func, + &proof.selector_oracle_evals, + &proof.witness_zero_check_evals, + )?; + 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_oracles` // ======================================================================= + let step = start_timer!(|| "verify permutation check"); // Zero check and sum check have different AuxInfo because `w_merged` and // `Prod(x)` have degree and num_vars let perm_check_aux_info = VPAuxInfo::<E::Fr> {

@@ -615,18 +724,64 @@ // degree of merged poly

num_variables: merged_nv, phantom: PhantomData::default(), }; - let mut challenge = <Self as PermutationCheck<E::Fr>>::generate_challenge(transcript)?; + let mut challenge = <Self as PermutationCheck<E::Fr>>::generate_challenge(&mut transcript)?; <Self as PermutationCheck<E::Fr>>::update_challenge( &mut challenge, - transcript, + &mut transcript, &proof.prod_commit, )?; let perm_check_sub_claim = <Self as PermutationCheck<E::Fr>>::verify( &proof.perm_check_proof, &perm_check_aux_info, - transcript, + &mut transcript, )?; + let perm_check_point = &perm_check_sub_claim + .zero_check_sub_claim + .sum_check_sub_claim + .point; + + // 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 + let alpha = challenge + .alpha + .ok_or_else(|| HyperPlonkErrors::InvalidVerifier("alpha is not set".to_string()))?; + + let s_id = identity_permutation_mle::<E::Fr>(perm_check_point.len()); + let s_id_eval = s_id.evaluate(perm_check_point).ok_or_else(|| { + HyperPlonkErrors::InvalidVerifier("unable to evaluate s_id(x)".to_string()) + })?; + + let q_x_rec = proof.prod_evals[1] - proof.prod_evals[2] * proof.prod_evals[3] + + alpha + * ((proof.witness_perm_check_eval + + challenge.beta * proof.perm_oracle_eval + + challenge.gamma) + * proof.prod_evals[0] + - (proof.witness_perm_check_eval + + challenge.beta * s_id_eval + + challenge.gamma)); + + if q_x_rec + != perm_check_sub_claim + .zero_check_sub_claim + .expected_evaluation + { + return Err(HyperPlonkErrors::InvalidVerifier( + "evaluation failed".to_string(), + )); + } end_timer!(step); // =======================================================================

@@ -634,12 +789,6 @@ // 3. Verify the opening against the commitment

// ======================================================================= let step = start_timer!(|| "verify commitments"); - let perm_point = &perm_check_sub_claim - .zero_check_sub_claim - .sum_check_sub_claim - .point; - let zero_point = &zero_check_sub_claim.sum_check_sub_claim.point; - // ======================================================================= // 3.1 check permutation check evaluations // =======================================================================

@@ -647,7 +796,7 @@ // witness for permutation check

if !PCS::verify( &vk.pcs_param, &proof.w_merged_com, - perm_point, + perm_check_point, &proof.witness_perm_check_eval, &proof.witness_perm_check_opening, )? {

@@ -656,11 +805,10 @@ "pcs verification failed".to_string(),

)); } - // perm for permutation check if !PCS::verify( &vk.pcs_param, &vk.perm_com, - &[&[E::Fr::one()], perm_point.as_slice()].concat(), + perm_check_point, &proof.perm_oracle_eval, &proof.perm_oracle_opening, )? {

@@ -669,6 +817,58 @@ "pcs verification failed".to_string(),

)); } + // prod(x) for permutation check + // TODO: batch verification + + // prod(0, x) + if !PCS::verify( + &vk.pcs_param, + &proof.prod_commit, + &[perm_check_point.as_slice(), &[E::Fr::zero()]].concat(), + &proof.prod_evals[0], + &proof.prod_openings[0], + )? { + return Err(HyperPlonkErrors::InvalidProof( + "pcs verification failed".to_string(), + )); + } + // prod(1, x) + if !PCS::verify( + &vk.pcs_param, + &proof.prod_commit, + &[perm_check_point.as_slice(), &[E::Fr::one()]].concat(), + &proof.prod_evals[1], + &proof.prod_openings[1], + )? { + return Err(HyperPlonkErrors::InvalidProof( + "pcs verification failed".to_string(), + )); + } + // prod(x, 0) + if !PCS::verify( + &vk.pcs_param, + &proof.prod_commit, + &[&[E::Fr::zero()], perm_check_point.as_slice()].concat(), + &proof.prod_evals[2], + &proof.prod_openings[2], + )? { + return Err(HyperPlonkErrors::InvalidProof( + "pcs verification failed".to_string(), + )); + } + // prod(x, 1) + if !PCS::verify( + &vk.pcs_param, + &proof.prod_commit, + &[&[E::Fr::one()], perm_check_point.as_slice()].concat(), + &proof.prod_evals[3], + &proof.prod_openings[3], + )? { + return Err(HyperPlonkErrors::InvalidProof( + "pcs verification failed".to_string(), + )); + } + // ======================================================================= // 3.2 check zero check evaluations // =======================================================================

@@ -680,7 +880,7 @@ .witness_zero_check_openings

.iter() .zip(proof.witness_zero_check_evals.iter()), ) { - if !PCS::verify(&vk.pcs_param, commitment, zero_point, eval, opening)? { + if !PCS::verify(&vk.pcs_param, commitment, zero_check_point, eval, opening)? { return Err(HyperPlonkErrors::InvalidProof( "pcs verification failed".to_string(), ));

@@ -697,7 +897,7 @@ {

if !PCS::verify( &vk.pcs_param, &vk.selector_com[0], - perm_point, + perm_check_point, eval, opening, )? {

@@ -707,17 +907,6 @@ ));

} } - // let f_eval = eval_f( - // &vk.params.gate_func, - // &proof.selector_oracle_evals, - // &proof.witness_zero_check_evals, - // )?; - // if f_eval != zero_check_sub_claim.sum_check_sub_claim.expected_evaluation { - // return Err(HyperPlonkErrors::InvalidProof( - // "zero check evaluation failed".to_string(), - // )); - // } - // ======================================================================= // 3.3 public input consistency checks // =======================================================================

@@ -740,12 +929,7 @@ }

end_timer!(step); end_timer!(start); - // todo: verify the subclaim within snark - Ok(HyperPlonkSubClaim { - zero_check_sub_claim, - perm_check_sub_claim, - pub_input_sub_claim: (vec![], E::Fr::default()), // FIXME - }) + Ok(true) } }

@@ -756,8 +940,7 @@ use crate::{selectors::SelectorColumn, structs::CustomizedGates, witness::WitnessColumn};

use ark_bls12_381::Bls12_381; use ark_std::test_rng; use pcs::prelude::KZGMultilinearPCS; - use poly_iop::{identity_permutation_mle, random_permutation_mle}; - use transcript::IOPTranscript; + use poly_iop::random_permutation_mle; #[test] fn test_hyperplonk_e2e() -> Result<(), HyperPlonkErrors> {

@@ -800,8 +983,6 @@ let pcs_srs = KZGMultilinearPCS::<E>::gen_srs_for_testing(&mut rng, 15)?;

let merged_nv = nv + log_n_wires; let s_perm = random_permutation_mle(merged_nv, &mut rng); - let s_id = identity_permutation_mle(merged_nv); - let perm: Vec<E::Fr> = [s_id.evaluations, s_perm.evaluations].concat(); let q1 = SelectorColumn(vec![E::Fr::one(), E::Fr::one(), E::Fr::one(), E::Fr::one()]); // w1 := [0, 1, 2, 3]

@@ -825,25 +1006,19 @@ // generate pk and vks

let (pk, vk) = <PolyIOP<E::Fr> as HyperPlonkSNARK<E, KZGMultilinearPCS<E>>>::preprocess( &params, &pcs_srs, - &perm, + &s_perm.evaluations, &[q1], )?; // generate a proof and verify - let mut transcript = IOPTranscript::<E::Fr>::new(b"test hyperplonk"); let proof = <PolyIOP<E::Fr> as HyperPlonkSNARK<E, KZGMultilinearPCS<E>>>::prove( &pk, &pi.0, &[w1, w2], - &mut transcript, )?; - let mut transcript = IOPTranscript::<E::Fr>::new(b"test hyperplonk"); let _sub_claim = <PolyIOP<E::Fr> as HyperPlonkSNARK<E, KZGMultilinearPCS<E>>>::verify( - &vk, - &pi.0, - &proof, - &mut transcript, + &vk, &pi.0, &proof, )?; Ok(())
M hyperplonk/src/structs.rs → hyperplonk/src/structs.rs

@@ -34,35 +34,50 @@ ZC: ZeroCheck<E::Fr>,

PC: PermutationCheck<E::Fr>, > { // ======================================================================= - // PCS components + // PCS components: common // ======================================================================= /// PCS commit for witnesses // TODO: replace me with a batch commitment pub witness_commits: Vec<PCS::Commitment>, pub w_merged_com: PCS::Commitment, + // ======================================================================= + // PCS components: permutation check + // ======================================================================= /// PCS commit for prod(x) // TODO: replace me with a batch commitment pub prod_commit: PCS::Commitment, + /// prod(x)'s evaluations + /// sequence: prod(0,x), prod(1, x), prod(x, 0), prod(x, 1) + pub prod_evals: Vec<E::Fr>, + /// prod(x)'s openings + /// sequence: prod(0,x), prod(1, x), prod(x, 0), prod(x, 1) + pub prod_openings: Vec<PCS::Proof>, /// PCS openings for witness on permutation check point // TODO: replace me with a batch opening pub witness_perm_check_opening: PCS::Proof, - /// PCS openings for witness on zero check point - // TODO: replace me with a batch opening - pub witness_zero_check_openings: Vec<PCS::Proof>, /// Evaluates of witnesses on permutation check point pub witness_perm_check_eval: E::Fr, - /// Evaluates of witnesses on zero check point - pub witness_zero_check_evals: Vec<E::Fr>, /// PCS openings for selectors on permutation check point // TODO: replace me with a batch opening pub perm_oracle_opening: PCS::Proof, /// Evaluates of selectors on permutation check point pub perm_oracle_eval: E::Fr, + // ======================================================================= + // PCS components: zero check + // ======================================================================= + /// PCS openings for witness on zero check point + // TODO: replace me with a batch opening + pub witness_zero_check_openings: Vec<PCS::Proof>, + /// Evaluates of witnesses on zero check point + pub witness_zero_check_evals: Vec<E::Fr>, /// PCS openings for selectors on zero check point // TODO: replace me with a batch opening pub selector_oracle_openings: Vec<PCS::Proof>, /// Evaluates of selectors on zero check point pub selector_oracle_evals: Vec<E::Fr>, + // ======================================================================= + // PCS components: public inputs + // ======================================================================= /// Evaluates of public inputs on r_pi from transcript pub pi_eval: E::Fr, /// Opening of public inputs on r_pi from transcript
M hyperplonk/src/utils.rs → hyperplonk/src/utils.rs

@@ -74,10 +74,9 @@ } else {

F::from(*coeff as u64) }; let mut mle_list = vec![]; - match *selector { - Some(s) => mle_list.push(selector_mles[s].clone()), - None => (), - }; + if let Some(s) = *selector { + mle_list.push(selector_mles[s].clone()) + } for &witness in witnesses.iter() { mle_list.push(witness_mles[witness].clone()) }
M pcs/src/multilinear_kzg/batching.rs → pcs/src/multilinear_kzg/batching.rs

@@ -242,7 +242,7 @@ if !KZGUnivariatePCS::verify(

uni_verifier_param, &batch_proof.q_x_commit, &domain.element(i), - &value, + value, &batch_proof.q_x_opens[i], )? { #[cfg(debug_assertion)]
M poly-iop/src/perm_check/mod.rs → poly-iop/src/perm_check/mod.rs

@@ -160,9 +160,9 @@ }

#[derive(Debug, Clone)] pub struct PermutationChallenge<F: PrimeField> { - alpha: Option<F>, - beta: F, - gamma: F, + pub alpha: Option<F>, + pub beta: F, + pub gamma: F, } /// A PermutationCheck is derived from ZeroCheck.
M poly-iop/src/sum_check/mod.rs → poly-iop/src/sum_check/mod.rs

@@ -173,9 +173,8 @@ prover_msgs.push(prover_msg);

challenge = Some(transcript.get_and_append_challenge(b"Internal round")?); } // pushing the last challenge point to the state - match challenge { - Some(p) => prover_state.challenges.push(p), - None => (), + if let Some(p) = challenge { + prover_state.challenges.push(p) }; end_timer!(start);
M poly-iop/src/sum_check/verifier.rs → poly-iop/src/sum_check/verifier.rs

@@ -105,12 +105,6 @@ "insufficient rounds".to_string(),

)); } - println!( - "eval len {} max degree {}", - self.polynomials_received[0].len(), - self.max_degree + 1 - ); - // the deferred check during the interactive phase: // 2. set `expected` to P(r)` #[cfg(feature = "parallel")]
M poly-iop/src/zero_check/mod.rs → poly-iop/src/zero_check/mod.rs

@@ -1,7 +1,9 @@

//! Main module for the ZeroCheck protocol. use crate::{errors::PolyIOPErrors, sum_check::SumCheck, PolyIOP}; +use arithmetic::build_eq_x_r; use ark_ff::PrimeField; +use ark_poly::MultilinearExtension; use ark_std::{end_timer, start_timer}; use transcript::IOPTranscript;

@@ -12,6 +14,8 @@ #[derive(Clone, Debug, Default, PartialEq)]

pub struct ZeroCheckSubClaim<F: PrimeField, SC: SumCheck<F>> { // the SubClaim from the SumCheck pub sum_check_sub_claim: SC::SumCheckSubClaim, + /// the expected evaluation + pub expected_evaluation: F, // the initial challenge r which is used to build eq(x, r) pub init_challenge: Vec<F>, }

@@ -119,9 +123,18 @@ hat_fx_aux_info.max_degree += 1;

let subclaim = <Self as SumCheck<F>>::verify(F::zero(), proof, &hat_fx_aux_info, transcript)?; + // expected_eval = sumcheck.expect_eval/eq(x, r) + // where x = sum_check_sub_claim.point + let eq_x_r = build_eq_x_r(&r)?; + let expected_evaluation = subclaim.expected_evaluation + / eq_x_r.evaluate(&subclaim.point).ok_or_else(|| { + PolyIOPErrors::InvalidParameters("evaluation dimension does not match".to_string()) + })?; + end_timer!(start); Ok(ZeroCheckSubClaim { sum_check_sub_claim: subclaim, + expected_evaluation, init_challenge: r, }) }