|
|
@ -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 @@ where |
|
|
|
type ProvingKey;
|
|
|
|
type VerifyingKey;
|
|
|
|
type Proof;
|
|
|
|
type SubClaim;
|
|
|
|
|
|
|
|
/// Generate the preprocessed polynomials output by the indexer.
|
|
|
|
///
|
|
|
@ -61,35 +60,27 @@ where |
|
|
|
/// - `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 @@ where |
|
|
|
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 @@ where |
|
|
|
|
|
|
|
// 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 @@ where |
|
|
|
/// - `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 @@ where |
|
|
|
/// - 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 @@ where |
|
|
|
&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 @@ where |
|
|
|
// 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 @@ where |
|
|
|
&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 @@ where |
|
|
|
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 @@ where |
|
|
|
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 @@ where |
|
|
|
// 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 @@ where |
|
|
|
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 @@ where |
|
|
|
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 @@ where |
|
|
|
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 @@ where |
|
|
|
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 @@ where |
|
|
|
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 @@ where |
|
|
|
// =======================================================================
|
|
|
|
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 @@ where |
|
|
|
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 @@ where |
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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 @@ where |
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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 @@ where |
|
|
|
.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 @@ where |
|
|
|
if !PCS::verify(
|
|
|
|
&vk.pcs_param,
|
|
|
|
&vk.selector_com[0],
|
|
|
|
perm_point,
|
|
|
|
perm_check_point,
|
|
|
|
eval,
|
|
|
|
opening,
|
|
|
|
)? {
|
|
|
@ -707,17 +907,6 @@ where |
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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 @@ where |
|
|
|
|
|
|
|
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 @@ mod tests { |
|
|
|
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 @@ mod tests { |
|
|
|
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 @@ mod tests { |
|
|
|
let (pk, vk) = <PolyIOP<E::Fr> as HyperPlonkSNARK<E, KZGMultilinearPCS<E>>>::preprocess(
|
|
|
|
¶ms,
|
|
|
|
&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(())
|
|
|
|