mirror of
https://github.com/arnaucube/hyperplonk.git
synced 2026-01-10 16:11:29 +01:00
49 verify validity of subclaims in plonk verification (#51)
This commit is contained in:
@@ -25,8 +25,11 @@ ark-bls12-381 = { version = "0.3.0", default-features = false, features = [ "cur
|
||||
|
||||
|
||||
[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",
|
||||
|
||||
@@ -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(())
|
||||
|
||||
@@ -34,35 +34,50 @@ pub struct HyperPlonkProof<
|
||||
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
|
||||
|
||||
@@ -74,10 +74,9 @@ pub(crate) fn build_f<F: PrimeField>(
|
||||
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())
|
||||
}
|
||||
|
||||
@@ -242,7 +242,7 @@ pub(super) fn batch_verify_internal<E: PairingEngine>(
|
||||
uni_verifier_param,
|
||||
&batch_proof.q_x_commit,
|
||||
&domain.element(i),
|
||||
&value,
|
||||
value,
|
||||
&batch_proof.q_x_opens[i],
|
||||
)? {
|
||||
#[cfg(debug_assertion)]
|
||||
|
||||
@@ -160,9 +160,9 @@ pub struct PermutationCheckSubClaim<F: PrimeField, ZC: ZeroCheck<F>> {
|
||||
|
||||
#[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.
|
||||
|
||||
@@ -173,9 +173,8 @@ impl<F: PrimeField> SumCheck<F> for PolyIOP<F> {
|
||||
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);
|
||||
|
||||
@@ -105,12 +105,6 @@ impl<F: PrimeField> SumCheckVerifier<F> for IOPVerifierState<F> {
|
||||
));
|
||||
}
|
||||
|
||||
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")]
|
||||
|
||||
@@ -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 @@ use transcript::IOPTranscript;
|
||||
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 @@ impl<F: PrimeField> ZeroCheck<F> for PolyIOP<F> {
|
||||
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,
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user