@ -0,0 +1,34 @@ |
|||||
|
[package] |
||||
|
name = "arithmetic" |
||||
|
version = "0.1.0" |
||||
|
edition = "2021" |
||||
|
|
||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html |
||||
|
|
||||
|
[dependencies] |
||||
|
|
||||
|
ark-ff = { version = "^0.3.0", default-features = false } |
||||
|
ark-std = { version = "^0.3.0", default-features = false } |
||||
|
ark-poly = { version = "^0.3.0", default-features = false } |
||||
|
ark-serialize = { version = "^0.3.0", default-features = false } |
||||
|
ark-bls12-381 = { version = "0.3.0", default-features = false, features = [ "curve" ] } |
||||
|
|
||||
|
rand_chacha = { version = "0.3.0", default-features = false } |
||||
|
displaydoc = { version = "0.2.3", default-features = false } |
||||
|
rayon = { version = "1.5.2", default-features = false, optional = true } |
||||
|
|
||||
|
[dev-dependencies] |
||||
|
ark-ec = { version = "^0.3.0", default-features = false } |
||||
|
|
||||
|
[features] |
||||
|
# default = [ "parallel", "print-trace" ] |
||||
|
default = [ "parallel" ] |
||||
|
parallel = [ |
||||
|
"rayon", |
||||
|
"ark-std/parallel", |
||||
|
"ark-ff/parallel", |
||||
|
"ark-poly/parallel" |
||||
|
] |
||||
|
print-trace = [ |
||||
|
"ark-std/print-trace" |
||||
|
] |
@ -0,0 +1,21 @@ |
|||||
|
//! Error module.
|
||||
|
|
||||
|
use ark_std::string::String;
|
||||
|
use displaydoc::Display;
|
||||
|
|
||||
|
/// A `enum` specifying the possible failure modes of the arithmetics.
|
||||
|
#[derive(Display, Debug)]
|
||||
|
pub enum ArithErrors {
|
||||
|
/// Invalid parameters: {0}
|
||||
|
InvalidParameters(String),
|
||||
|
/// Should not arrive to this point
|
||||
|
ShouldNotArrive,
|
||||
|
/// An error during (de)serialization: {0}
|
||||
|
SerializationErrors(ark_serialize::SerializationError),
|
||||
|
}
|
||||
|
|
||||
|
impl From<ark_serialize::SerializationError> for ArithErrors {
|
||||
|
fn from(e: ark_serialize::SerializationError) -> Self {
|
||||
|
Self::SerializationErrors(e)
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,7 @@ |
|||||
|
mod errors;
|
||||
|
mod multilinear_polynomial;
|
||||
|
mod virtual_polynomial;
|
||||
|
|
||||
|
pub use errors::ArithErrors;
|
||||
|
pub use multilinear_polynomial::{random_zero_mle_list, DenseMultilinearExtension};
|
||||
|
pub use virtual_polynomial::{build_eq_x_r, VPAuxInfo, VirtualPolynomial};
|
@ -0,0 +1,33 @@ |
|||||
|
use ark_ff::PrimeField;
|
||||
|
use ark_std::{end_timer, rand::RngCore, start_timer};
|
||||
|
use std::rc::Rc;
|
||||
|
|
||||
|
pub use ark_poly::DenseMultilinearExtension;
|
||||
|
|
||||
|
// Build a randomize list of mle-s whose sum is zero.
|
||||
|
pub fn random_zero_mle_list<F: PrimeField, R: RngCore>(
|
||||
|
nv: usize,
|
||||
|
degree: usize,
|
||||
|
rng: &mut R,
|
||||
|
) -> Vec<Rc<DenseMultilinearExtension<F>>> {
|
||||
|
let start = start_timer!(|| "sample random zero mle list");
|
||||
|
|
||||
|
let mut multiplicands = Vec::with_capacity(degree);
|
||||
|
for _ in 0..degree {
|
||||
|
multiplicands.push(Vec::with_capacity(1 << nv))
|
||||
|
}
|
||||
|
for _ in 0..(1 << nv) {
|
||||
|
multiplicands[0].push(F::zero());
|
||||
|
for e in multiplicands.iter_mut().skip(1) {
|
||||
|
e.push(F::rand(rng));
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
let list = multiplicands
|
||||
|
.into_iter()
|
||||
|
.map(|x| Rc::new(DenseMultilinearExtension::from_evaluations_vec(nv, x)))
|
||||
|
.collect();
|
||||
|
|
||||
|
end_timer!(start);
|
||||
|
list
|
||||
|
}
|
@ -0,0 +1,64 @@ |
|||||
|
//! Error module.
|
||||
|
|
||||
|
use arithmetic::ArithErrors;
|
||||
|
use ark_serialize::SerializationError;
|
||||
|
use ark_std::string::String;
|
||||
|
use displaydoc::Display;
|
||||
|
use pcs::prelude::PCSErrors;
|
||||
|
use poly_iop::prelude::PolyIOPErrors;
|
||||
|
use transcript::TranscriptErrors;
|
||||
|
|
||||
|
/// A `enum` specifying the possible failure modes of hyperplonk.
|
||||
|
#[allow(dead_code)]
|
||||
|
// todo: REMOVE
|
||||
|
#[derive(Display, Debug)]
|
||||
|
pub enum HyperPlonkErrors {
|
||||
|
/// Invalid Prover: {0}
|
||||
|
InvalidProver(String),
|
||||
|
/// Invalid Verifier: {0}
|
||||
|
InvalidVerifier(String),
|
||||
|
/// Invalid Proof: {0}
|
||||
|
InvalidProof(String),
|
||||
|
/// Invalid parameters: {0}
|
||||
|
InvalidParameters(String),
|
||||
|
/// An error during (de)serialization: {0}
|
||||
|
SerializationError(SerializationError),
|
||||
|
/// PolyIOP error {0}
|
||||
|
PolyIOPErrors(PolyIOPErrors),
|
||||
|
/// PCS error {0}
|
||||
|
PCSErrors(PCSErrors),
|
||||
|
/// Transcript error {0}
|
||||
|
TranscriptError(TranscriptErrors),
|
||||
|
/// Arithmetic Error: {0}
|
||||
|
ArithmeticErrors(ArithErrors),
|
||||
|
}
|
||||
|
|
||||
|
impl From<SerializationError> for HyperPlonkErrors {
|
||||
|
fn from(e: ark_serialize::SerializationError) -> Self {
|
||||
|
Self::SerializationError(e)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl From<PolyIOPErrors> for HyperPlonkErrors {
|
||||
|
fn from(e: PolyIOPErrors) -> Self {
|
||||
|
Self::PolyIOPErrors(e)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl From<PCSErrors> for HyperPlonkErrors {
|
||||
|
fn from(e: PCSErrors) -> Self {
|
||||
|
Self::PCSErrors(e)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl From<TranscriptErrors> for HyperPlonkErrors {
|
||||
|
fn from(e: TranscriptErrors) -> Self {
|
||||
|
Self::TranscriptError(e)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl From<ArithErrors> for HyperPlonkErrors {
|
||||
|
fn from(e: ArithErrors) -> Self {
|
||||
|
Self::ArithmeticErrors(e)
|
||||
|
}
|
||||
|
}
|
@ -1,8 +1,851 @@ |
|||||
|
//! Main module for the HyperPlonk PolyIOP.
|
||||
|
|
||||
|
use arithmetic::VPAuxInfo;
|
||||
|
use ark_ec::PairingEngine;
|
||||
|
use ark_poly::{DenseMultilinearExtension, MultilinearExtension};
|
||||
|
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::{
|
||||
|
prelude::{PermutationCheck, SumCheck, ZeroCheck},
|
||||
|
PolyIOP,
|
||||
|
};
|
||||
|
use selectors::SelectorColumn;
|
||||
|
use std::{marker::PhantomData, rc::Rc};
|
||||
|
use structs::{
|
||||
|
HyperPlonkParams, HyperPlonkProof, HyperPlonkProvingKey, HyperPlonkSubClaim,
|
||||
|
HyperPlonkVerifyingKey,
|
||||
|
};
|
||||
|
use utils::build_f;
|
||||
|
use witness::WitnessColumn;
|
||||
|
|
||||
|
mod errors;
|
||||
|
mod selectors;
|
||||
|
mod structs;
|
||||
|
mod utils;
|
||||
|
mod witness;
|
||||
|
|
||||
|
/// A trait for HyperPlonk Poly-IOPs.
|
||||
|
/// A HyperPlonk is derived from SumChecks, ZeroChecks and PermutationChecks.
|
||||
|
pub trait HyperPlonkSNARK<E, PCS>: |
||||
|
SumCheck<E::Fr> + ZeroCheck<E::Fr> + PermutationCheck<E::Fr>
|
||||
|
where
|
||||
|
E: PairingEngine,
|
||||
|
PCS: PolynomialCommitmentScheme<E>,
|
||||
|
{
|
||||
|
type Parameters;
|
||||
|
type ProvingKey;
|
||||
|
type VerifyingKey;
|
||||
|
type Proof;
|
||||
|
type SubClaim;
|
||||
|
|
||||
|
/// Generate the preprocessed polynomials output by the indexer.
|
||||
|
///
|
||||
|
/// Inputs:
|
||||
|
/// - `params`: HyperPlonk instance parameters
|
||||
|
/// - `permutation`: the permutation for the copy constraints
|
||||
|
/// - `selectors`: the list of selector vectors for custom gates
|
||||
|
/// Outputs:
|
||||
|
/// - The HyperPlonk proving key, which includes the preprocessed
|
||||
|
/// polynomials.
|
||||
|
fn preprocess(
|
||||
|
params: &Self::Parameters,
|
||||
|
pcs_srs: &PCS::SRS,
|
||||
|
permutation: &[E::Fr],
|
||||
|
selectors: &[SelectorColumn<E::Fr>],
|
||||
|
) -> Result<(Self::ProvingKey, Self::VerifyingKey), HyperPlonkErrors>;
|
||||
|
|
||||
|
/// Generate HyperPlonk SNARK proof.
|
||||
|
///
|
||||
|
/// 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.
|
||||
|
///
|
||||
|
/// Inputs:
|
||||
|
/// - `params`: instance parameter
|
||||
|
/// - `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
|
||||
|
fn verify(
|
||||
|
params: &Self::VerifyingKey,
|
||||
|
pub_input: &[E::Fr],
|
||||
|
proof: &Self::Proof,
|
||||
|
transcript: &mut Self::Transcript,
|
||||
|
) -> Result<Self::SubClaim, HyperPlonkErrors>;
|
||||
|
}
|
||||
|
|
||||
|
impl<E, PCS> HyperPlonkSNARK<E, PCS> for PolyIOP<E::Fr>
|
||||
|
where
|
||||
|
E: PairingEngine,
|
||||
|
// Ideally we want to access polynomial as PCS::Polynomial, instead of instantiating it here.
|
||||
|
// But since PCS::Polynomial can be both univariate or multivariate in our implementation
|
||||
|
// we cannot bound PCS::Polynomial with a property trait bound.
|
||||
|
PCS: PolynomialCommitmentScheme<
|
||||
|
E,
|
||||
|
Polynomial = Rc<DenseMultilinearExtension<E::Fr>>,
|
||||
|
Point = Vec<E::Fr>,
|
||||
|
Evaluation = E::Fr,
|
||||
|
>,
|
||||
|
{
|
||||
|
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.
|
||||
|
///
|
||||
|
/// Inputs:
|
||||
|
/// - `params`: HyperPlonk instance parameters
|
||||
|
/// - `permutation`: the permutation for the copy constraints
|
||||
|
/// - `selectors`: the list of selector vectors for custom gates
|
||||
|
/// Outputs:
|
||||
|
/// - The HyperPlonk proving key, which includes the preprocessed
|
||||
|
/// polynomials.
|
||||
|
fn preprocess(
|
||||
|
params: &Self::Parameters,
|
||||
|
pcs_srs: &PCS::SRS,
|
||||
|
permutation: &[E::Fr],
|
||||
|
selectors: &[SelectorColumn<E::Fr>],
|
||||
|
) -> Result<(Self::ProvingKey, Self::VerifyingKey), HyperPlonkErrors> {
|
||||
|
let num_vars = params.nv;
|
||||
|
let log_num_witness_polys = params.log_n_wires;
|
||||
|
|
||||
|
// number of variables in merged polynomial for Multilinear-KZG
|
||||
|
let merged_nv = num_vars + log_num_witness_polys;
|
||||
|
// degree of q(x) for Univariate-KZG
|
||||
|
let supported_uni_degree = compute_qx_degree(num_vars, 1 << log_num_witness_polys);
|
||||
|
|
||||
|
// extract PCS prover and verifier keys from SRS
|
||||
|
let (pcs_prover_param, pcs_verifier_param) = PCS::trim(
|
||||
|
pcs_srs,
|
||||
|
log2(supported_uni_degree) as usize,
|
||||
|
Some(merged_nv + 1),
|
||||
|
)?;
|
||||
|
|
||||
|
// 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,
|
||||
|
permutation,
|
||||
|
));
|
||||
|
|
||||
|
let perm_com = PCS::commit(&pcs_prover_param, &permutation_oracles)?;
|
||||
|
|
||||
|
// build selector oracles and commit to it
|
||||
|
let selector_oracles: Vec<Rc<DenseMultilinearExtension<E::Fr>>> = selectors
|
||||
|
.iter()
|
||||
|
.map(|s| Rc::new(DenseMultilinearExtension::from(s)))
|
||||
|
.collect();
|
||||
|
|
||||
|
let selector_com = selector_oracles
|
||||
|
.iter()
|
||||
|
.map(|poly| PCS::commit(&pcs_prover_param, poly))
|
||||
|
.collect::<Result<Vec<PCS::Commitment>, PCSErrors>>()?;
|
||||
|
|
||||
|
Ok((
|
||||
|
Self::ProvingKey {
|
||||
|
params: params.clone(),
|
||||
|
permutation_oracles,
|
||||
|
selector_oracles,
|
||||
|
pcs_param: pcs_prover_param,
|
||||
|
},
|
||||
|
Self::VerifyingKey {
|
||||
|
params: params.clone(),
|
||||
|
pcs_param: pcs_verifier_param,
|
||||
|
selector_com,
|
||||
|
perm_com,
|
||||
|
},
|
||||
|
))
|
||||
|
}
|
||||
|
|
||||
|
/// Generate HyperPlonk SNARK proof.
|
||||
|
///
|
||||
|
/// Inputs:
|
||||
|
/// - `pk`: circuit proving key
|
||||
|
/// - `pub_input`: online public input of length 2^\ell
|
||||
|
/// - `witness`: witness assignment of length 2^n
|
||||
|
/// - `transcript`: the transcript used for generating pseudorandom
|
||||
|
/// challenges
|
||||
|
/// Outputs:
|
||||
|
/// - The HyperPlonk SNARK proof.
|
||||
|
///
|
||||
|
/// Steps:
|
||||
|
///
|
||||
|
/// 1. Commit Witness polynomials `w_i(x)` and append commitment to
|
||||
|
/// transcript
|
||||
|
///
|
||||
|
/// 2. Run ZeroCheck on
|
||||
|
///
|
||||
|
/// `f(q_0(x),...q_l(x), w_0(x),...w_d(x))`
|
||||
|
///
|
||||
|
/// where `f` is the constraint polynomial i.e.,
|
||||
|
/// ```ignore
|
||||
|
/// f(q_l, q_r, q_m, q_o, w_a, w_b, w_c)
|
||||
|
/// = q_l w_a(x) + q_r w_b(x) + q_m w_a(x)w_b(x) - q_o w_c(x)
|
||||
|
/// ```
|
||||
|
/// in vanilla plonk, and obtain a ZeroCheckSubClaim
|
||||
|
///
|
||||
|
/// 3. Run permutation check on `\{w_i(x)\}` and `permutation_oracles`, and
|
||||
|
/// obtain a PermCheckSubClaim.
|
||||
|
///
|
||||
|
/// 4. Generate evaluations and corresponding proofs
|
||||
|
/// - permutation check evaluations and proofs
|
||||
|
/// - zero check evaluations and proofs
|
||||
|
/// - public input consistency checks
|
||||
|
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");
|
||||
|
|
||||
|
// witness assignment of length 2^n
|
||||
|
let num_vars = pk.params.nv;
|
||||
|
let log_num_witness_polys = pk.params.log_n_wires;
|
||||
|
// number of variables in merged polynomial for Multilinear-KZG
|
||||
|
let merged_nv = num_vars + log_num_witness_polys;
|
||||
|
// degree of q(x) for Univariate-KZG
|
||||
|
let _supported_uni_degree = compute_qx_degree(num_vars, 1 << log_num_witness_polys);
|
||||
|
// online public input of length 2^\ell
|
||||
|
let ell = pk.params.log_pub_input_len;
|
||||
|
|
||||
|
let witness_polys: Vec<Rc<DenseMultilinearExtension<E::Fr>>> = witnesses
|
||||
|
.iter()
|
||||
|
.map(|w| Rc::new(DenseMultilinearExtension::from(w)))
|
||||
|
.collect();
|
||||
|
let pi_poly = Rc::new(DenseMultilinearExtension::from_evaluations_slice(
|
||||
|
ell as usize,
|
||||
|
pub_input,
|
||||
|
));
|
||||
|
|
||||
|
// =======================================================================
|
||||
|
// 0. sanity checks
|
||||
|
// =======================================================================
|
||||
|
// public input length
|
||||
|
if pub_input.len() != 1 << ell {
|
||||
|
return Err(HyperPlonkErrors::InvalidProver(format!(
|
||||
|
"Public input length is not correct: got {}, expect {}",
|
||||
|
pub_input.len(),
|
||||
|
1 << ell
|
||||
|
)));
|
||||
|
}
|
||||
|
// witnesses length
|
||||
|
for (i, w) in witnesses.iter().enumerate() {
|
||||
|
if w.0.len() != 1 << num_vars {
|
||||
|
return Err(HyperPlonkErrors::InvalidProver(format!(
|
||||
|
"{}-th witness length is not correct: got {}, expect {}",
|
||||
|
i,
|
||||
|
pub_input.len(),
|
||||
|
1 << ell
|
||||
|
)));
|
||||
|
}
|
||||
|
}
|
||||
|
// check public input matches witness[0]'s first 2^ell elements
|
||||
|
let pi_in_w0 =
|
||||
|
Rc::new(witness_polys[0].fix_variables(vec![E::Fr::zero(); num_vars - ell].as_ref()));
|
||||
|
|
||||
|
if pi_in_w0 != pi_poly {
|
||||
|
return Err(HyperPlonkErrors::InvalidProver(format!(
|
||||
|
"Public input {:?} does not match witness[0] {:?}",
|
||||
|
pi_poly, pi_in_w0,
|
||||
|
)));
|
||||
|
}
|
||||
|
// =======================================================================
|
||||
|
// 1. Commit Witness polynomials `w_i(x)` and append commitment to
|
||||
|
// transcript
|
||||
|
// =======================================================================
|
||||
|
let step = start_timer!(|| "commit witnesses");
|
||||
|
let mut witness_commits = vec![];
|
||||
|
// TODO: batch commit
|
||||
|
for wi_poly in witness_polys.iter() {
|
||||
|
let wi_com = PCS::commit(&pk.pcs_param, wi_poly)?;
|
||||
|
witness_commits.push(wi_com);
|
||||
|
}
|
||||
|
|
||||
|
let w_merged = merge_polynomials(&witness_polys)?;
|
||||
|
if w_merged.num_vars != merged_nv {
|
||||
|
return Err(HyperPlonkErrors::InvalidParameters(format!(
|
||||
|
"merged witness poly has a different num_var ({}) from expected ({})",
|
||||
|
w_merged.num_vars, merged_nv
|
||||
|
)));
|
||||
|
}
|
||||
|
let w_merged_com = PCS::commit(&pk.pcs_param, &Rc::new(w_merged.clone()))?;
|
||||
|
|
||||
|
transcript.append_serializable_element(b"w", &w_merged_com)?;
|
||||
|
end_timer!(step);
|
||||
|
|
||||
|
// =======================================================================
|
||||
|
// 2 Run ZeroCheck on
|
||||
|
//
|
||||
|
// `f(q_0(x),...q_l(x), w_0(x),...w_d(x))`
|
||||
|
//
|
||||
|
// where `f` is the constraint polynomial i.e.,
|
||||
|
//
|
||||
|
// f(q_l, q_r, q_m, q_o, w_a, w_b, w_c)
|
||||
|
// = q_l w_a(x) + q_r w_b(x) + q_m w_a(x)w_b(x) - q_o w_c(x)
|
||||
|
//
|
||||
|
// in vanilla plonk, and obtain a ZeroCheckSubClaim
|
||||
|
// =======================================================================
|
||||
|
let step = start_timer!(|| "ZeroCheck on f");
|
||||
|
|
||||
|
let fx = build_f(
|
||||
|
&pk.params.gate_func,
|
||||
|
pk.params.nv,
|
||||
|
&pk.selector_oracles,
|
||||
|
&witness_polys,
|
||||
|
)?;
|
||||
|
|
||||
|
let zero_check_proof = <Self as ZeroCheck<E::Fr>>::prove(&fx, transcript)?;
|
||||
|
end_timer!(step);
|
||||
|
|
||||
|
// =======================================================================
|
||||
|
// 3. Run permutation check on `\{w_i(x)\}` and `permutation_oracles`, and
|
||||
|
// obtain a PermCheckSubClaim.
|
||||
|
//
|
||||
|
// 3.1. `generate_challenge` from current transcript (generate beta, gamma)
|
||||
|
// 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
|
||||
|
// =======================================================================
|
||||
|
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)?;
|
||||
|
|
||||
|
// 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)?;
|
||||
|
|
||||
|
// 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()))?;
|
||||
|
|
||||
|
// 3.4. `update_challenge` with the updated transcript
|
||||
|
Self::update_challenge(&mut permutation_challenge, 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,
|
||||
|
)?;
|
||||
|
end_timer!(step);
|
||||
|
|
||||
|
// =======================================================================
|
||||
|
// 4. Generate evaluations and corresponding proofs
|
||||
|
// - permutation check evaluations and proofs
|
||||
|
// - wi_poly(r_perm_check) where r_perm_check is from perm_check_proof
|
||||
|
// - selector_poly(r_perm_check)
|
||||
|
//
|
||||
|
// - zero check evaluations and proofs
|
||||
|
// - wi_poly(r_zero_check) where r_zero_check is from zero_check_proof
|
||||
|
// - selector_poly(r_zero_check)
|
||||
|
//
|
||||
|
// - public input consistency checks
|
||||
|
// - pi_poly(r_pi) where r_pi is sampled from transcript
|
||||
|
// =======================================================================
|
||||
|
let step = start_timer!(|| "opening and evaluations");
|
||||
|
|
||||
|
// 4.1 permutation check
|
||||
|
let mut witness_zero_check_evals = vec![];
|
||||
|
let mut witness_zero_check_openings = vec![];
|
||||
|
// TODO: parallelization
|
||||
|
// TODO: Batch opening
|
||||
|
|
||||
|
// open permutation check proof
|
||||
|
let (witness_perm_check_opening, witness_perm_check_eval) = PCS::open(
|
||||
|
&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
|
||||
|
{
|
||||
|
return Err(HyperPlonkErrors::InvalidProver(
|
||||
|
"Evaluation is different from PCS opening".to_string(),
|
||||
|
));
|
||||
|
}
|
||||
|
|
||||
|
// 4.2 open zero check proof
|
||||
|
// TODO: batch opening
|
||||
|
for wire_poly in witness_polys {
|
||||
|
// 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(|| {
|
||||
|
HyperPlonkErrors::InvalidParameters(
|
||||
|
"evaluation dimension does not match".to_string(),
|
||||
|
)
|
||||
|
})? != zero_eval
|
||||
|
{
|
||||
|
return Err(HyperPlonkErrors::InvalidProver(
|
||||
|
"Evaluation is different from PCS opening".to_string(),
|
||||
|
));
|
||||
|
}
|
||||
|
}
|
||||
|
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(
|
||||
|
&pk.pcs_param,
|
||||
|
&pk.permutation_oracles,
|
||||
|
&[&[E::Fr::one()], perm_check_proof.point.as_slice()].concat(),
|
||||
|
)?;
|
||||
|
{
|
||||
|
// 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
|
||||
|
{
|
||||
|
return Err(HyperPlonkErrors::InvalidProver(
|
||||
|
"Evaluation is different from PCS opening".to_string(),
|
||||
|
));
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
let mut selector_oracle_openings = vec![];
|
||||
|
let mut selector_oracle_evals = vec![];
|
||||
|
|
||||
|
// TODO: parallelization
|
||||
|
for selector_poly in pk.selector_oracles.iter() {
|
||||
|
// 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)?;
|
||||
|
{
|
||||
|
if selector_poly
|
||||
|
.evaluate(&zero_check_proof.point)
|
||||
|
.ok_or_else(|| {
|
||||
|
HyperPlonkErrors::InvalidParameters(
|
||||
|
"evaluation dimension does not match".to_string(),
|
||||
|
)
|
||||
|
})?
|
||||
|
!= zero_eval
|
||||
|
{
|
||||
|
return Err(HyperPlonkErrors::InvalidProver(
|
||||
|
"Evaluation is different from PCS
|
||||
|
opening"
|
||||
|
.to_string(),
|
||||
|
));
|
||||
|
}
|
||||
|
}
|
||||
|
selector_oracle_openings.push(zero_proof);
|
||||
|
selector_oracle_evals.push(zero_eval);
|
||||
|
}
|
||||
|
|
||||
|
// 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)?;
|
||||
|
{
|
||||
|
// sanity check
|
||||
|
if pi_poly.evaluate(&r_pi).ok_or_else(|| {
|
||||
|
HyperPlonkErrors::InvalidParameters(
|
||||
|
"evaluation dimension does not match".to_string(),
|
||||
|
)
|
||||
|
})? != pi_eval
|
||||
|
{
|
||||
|
return Err(HyperPlonkErrors::InvalidProver(
|
||||
|
"Evaluation is different from PCS opening".to_string(),
|
||||
|
));
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
end_timer!(step);
|
||||
|
end_timer!(start);
|
||||
|
|
||||
|
Ok(HyperPlonkProof {
|
||||
|
// PCS components
|
||||
|
witness_commits,
|
||||
|
w_merged_com,
|
||||
|
// We do not validate prod(x), this is checked by subclaim
|
||||
|
prod_commit: prod_com,
|
||||
|
witness_perm_check_opening,
|
||||
|
witness_zero_check_openings,
|
||||
|
witness_perm_check_eval,
|
||||
|
witness_zero_check_evals,
|
||||
|
perm_oracle_opening,
|
||||
|
perm_oracle_eval,
|
||||
|
selector_oracle_openings,
|
||||
|
selector_oracle_evals,
|
||||
|
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.
|
||||
|
///
|
||||
|
/// Inputs:
|
||||
|
/// - `params`: instance parameter
|
||||
|
/// - `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
|
||||
|
///
|
||||
|
/// 1. Verify zero_check_proof on
|
||||
|
///
|
||||
|
/// `f(q_0(x),...q_l(x), w_0(x),...w_d(x))`
|
||||
|
///
|
||||
|
/// where `f` is the constraint polynomial i.e.,
|
||||
|
/// ```ignore
|
||||
|
/// f(q_l, q_r, q_m, q_o, w_a, w_b, w_c)
|
||||
|
/// = q_l w_a(x) + q_r w_b(x) + q_m w_a(x)w_b(x) - q_o w_c(x)
|
||||
|
/// ```
|
||||
|
/// in vanilla plonk, and obtain a ZeroCheckSubClaim
|
||||
|
///
|
||||
|
/// 2. Verify perm_check_proof on `\{w_i(x)\}` and `permutation_oracles`
|
||||
|
///
|
||||
|
/// 3. Verify the opening against the commitment:
|
||||
|
/// - check permutation check evaluations
|
||||
|
/// - check zero check evaluations
|
||||
|
/// - public input consistency checks
|
||||
|
/// 4. check subclaim validity // todo
|
||||
|
fn verify(
|
||||
|
vk: &Self::VerifyingKey,
|
||||
|
pub_input: &[E::Fr],
|
||||
|
proof: &Self::Proof,
|
||||
|
transcript: &mut Self::Transcript,
|
||||
|
) -> Result<Self::SubClaim, HyperPlonkErrors> {
|
||||
|
let start = start_timer!(|| "hyperplonk verification");
|
||||
|
|
||||
|
// witness assignment of length 2^n
|
||||
|
let num_var = vk.params.nv;
|
||||
|
let log_num_witness_polys = vk.params.log_n_wires;
|
||||
|
// number of variables in merged polynomial for Multilinear-KZG
|
||||
|
let merged_nv = num_var + log_num_witness_polys;
|
||||
|
|
||||
|
// online public input of length 2^\ell
|
||||
|
let ell = vk.params.log_pub_input_len;
|
||||
|
|
||||
|
let pi_poly = DenseMultilinearExtension::from_evaluations_slice(ell as usize, pub_input);
|
||||
|
|
||||
|
// =======================================================================
|
||||
|
// 0. sanity checks
|
||||
|
// =======================================================================
|
||||
|
// public input length
|
||||
|
if pub_input.len() != 1 << ell {
|
||||
|
return Err(HyperPlonkErrors::InvalidProver(format!(
|
||||
|
"Public input length is not correct: got {}, expect {}",
|
||||
|
pub_input.len(),
|
||||
|
1 << ell
|
||||
|
)));
|
||||
|
}
|
||||
|
|
||||
|
// =======================================================================
|
||||
|
// 1. Verify zero_check_proof on
|
||||
|
// `f(q_0(x),...q_l(x), w_0(x),...w_d(x))`
|
||||
|
//
|
||||
|
// where `f` is the constraint polynomial i.e.,
|
||||
|
//
|
||||
|
// f(q_l, q_r, q_m, q_o, w_a, w_b, w_c)
|
||||
|
// = q_l w_a(x) + q_r w_b(x) + q_m w_a(x)w_b(x) - q_o w_c(x)
|
||||
|
//
|
||||
|
// =======================================================================
|
||||
|
let step = start_timer!(|| "verify zero check");
|
||||
|
// Zero check and sum check have different AuxInfo because `w_merged` and
|
||||
|
// `Prod(x)` have degree and num_vars
|
||||
|
let zero_check_aux_info = VPAuxInfo::<E::Fr> {
|
||||
|
// TODO: get the real max degree from gate_func
|
||||
|
// Here we use 6 is because the test has q[0] * w[0]^5 which is degree 6
|
||||
|
max_degree: 6,
|
||||
|
num_variables: num_var,
|
||||
|
phantom: PhantomData::default(),
|
||||
|
};
|
||||
|
|
||||
|
// push witness to transcript
|
||||
|
transcript.append_serializable_element(b"w", &proof.w_merged_com)?;
|
||||
|
|
||||
|
let zero_check_sub_claim = <Self as ZeroCheck<E::Fr>>::verify(
|
||||
|
&proof.zero_check_proof,
|
||||
|
&zero_check_aux_info,
|
||||
|
transcript,
|
||||
|
)?;
|
||||
|
end_timer!(step);
|
||||
|
// =======================================================================
|
||||
|
// 2. Verify perm_check_proof on `\{w_i(x)\}` and `permutation_oracles`
|
||||
|
// =======================================================================
|
||||
|
// 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> {
|
||||
|
// Prod(x) has a max degree of 2
|
||||
|
max_degree: 2,
|
||||
|
// degree of merged poly
|
||||
|
num_variables: merged_nv,
|
||||
|
phantom: PhantomData::default(),
|
||||
|
};
|
||||
|
let mut challenge = <Self as PermutationCheck<E::Fr>>::generate_challenge(transcript)?;
|
||||
|
<Self as PermutationCheck<E::Fr>>::update_challenge(
|
||||
|
&mut challenge,
|
||||
|
transcript,
|
||||
|
&proof.prod_commit,
|
||||
|
)?;
|
||||
|
|
||||
|
let perm_check_sub_claim = <Self as PermutationCheck<E::Fr>>::verify(
|
||||
|
&proof.perm_check_proof,
|
||||
|
&perm_check_aux_info,
|
||||
|
transcript,
|
||||
|
)?;
|
||||
|
|
||||
|
end_timer!(step);
|
||||
|
// =======================================================================
|
||||
|
// 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
|
||||
|
// =======================================================================
|
||||
|
// witness for permutation check
|
||||
|
if !PCS::verify(
|
||||
|
&vk.pcs_param,
|
||||
|
&proof.w_merged_com,
|
||||
|
perm_point,
|
||||
|
&proof.witness_perm_check_eval,
|
||||
|
&proof.witness_perm_check_opening,
|
||||
|
)? {
|
||||
|
return Err(HyperPlonkErrors::InvalidProof(
|
||||
|
"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(),
|
||||
|
&proof.perm_oracle_eval,
|
||||
|
&proof.perm_oracle_opening,
|
||||
|
)? {
|
||||
|
return Err(HyperPlonkErrors::InvalidProof(
|
||||
|
"pcs verification failed".to_string(),
|
||||
|
));
|
||||
|
}
|
||||
|
|
||||
|
// =======================================================================
|
||||
|
// 3.2 check zero check evaluations
|
||||
|
// =======================================================================
|
||||
|
// witness for zero check
|
||||
|
// TODO: batch verification
|
||||
|
for (commitment, (opening, eval)) in proof.witness_commits.iter().zip(
|
||||
|
proof
|
||||
|
.witness_zero_check_openings
|
||||
|
.iter()
|
||||
|
.zip(proof.witness_zero_check_evals.iter()),
|
||||
|
) {
|
||||
|
if !PCS::verify(&vk.pcs_param, commitment, zero_point, eval, opening)? {
|
||||
|
return Err(HyperPlonkErrors::InvalidProof(
|
||||
|
"pcs verification failed".to_string(),
|
||||
|
));
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
// selector for zero check
|
||||
|
// TODO: for now we only support a single selector polynomial
|
||||
|
for (opening, eval) in proof
|
||||
|
.selector_oracle_openings
|
||||
|
.iter()
|
||||
|
.zip(proof.selector_oracle_evals.iter())
|
||||
|
{
|
||||
|
if !PCS::verify(
|
||||
|
&vk.pcs_param,
|
||||
|
&vk.selector_com[0],
|
||||
|
perm_point,
|
||||
|
eval,
|
||||
|
opening,
|
||||
|
)? {
|
||||
|
return Err(HyperPlonkErrors::InvalidProof(
|
||||
|
"pcs verification failed".to_string(),
|
||||
|
));
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
// 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
|
||||
|
// =======================================================================
|
||||
|
let mut r_pi = transcript.get_and_append_challenge_vectors(b"r_pi", ell)?;
|
||||
|
let pi_eval = pi_poly.evaluate(&r_pi).ok_or_else(|| {
|
||||
|
HyperPlonkErrors::InvalidParameters("evaluation dimension does not match".to_string())
|
||||
|
})?;
|
||||
|
r_pi = [vec![E::Fr::zero(); num_var - ell], r_pi].concat();
|
||||
|
if !PCS::verify(
|
||||
|
&vk.pcs_param,
|
||||
|
&proof.witness_commits[0],
|
||||
|
&r_pi,
|
||||
|
&pi_eval,
|
||||
|
&proof.pi_opening,
|
||||
|
)? {
|
||||
|
return Err(HyperPlonkErrors::InvalidProof(
|
||||
|
"pcs verification failed".to_string(),
|
||||
|
));
|
||||
|
}
|
||||
|
|
||||
|
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
|
||||
|
})
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
#[cfg(test)]
|
#[cfg(test)]
|
||||
mod tests {
|
mod tests {
|
||||
|
use super::*;
|
||||
|
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;
|
||||
|
|
||||
#[test]
|
#[test]
|
||||
fn it_works() {
|
|
||||
let result = 2 + 2;
|
|
||||
assert_eq!(result, 4);
|
|
||||
|
fn test_hyperplonk_e2e() -> Result<(), HyperPlonkErrors> {
|
||||
|
// Example:
|
||||
|
// q_L(X) * W_1(X)^5 - W_2(X) = 0
|
||||
|
// is represented as
|
||||
|
// vec![
|
||||
|
// ( 1, Some(id_qL), vec![id_W1, id_W1, id_W1, id_W1, id_W1]),
|
||||
|
// (-1, None, vec![id_W2])
|
||||
|
// ]
|
||||
|
//
|
||||
|
// 4 public input
|
||||
|
// 1 selector,
|
||||
|
// 2 witnesses,
|
||||
|
// 2 variables for MLE,
|
||||
|
// 4 wires,
|
||||
|
let gates = CustomizedGates {
|
||||
|
gates: vec![(1, Some(0), vec![0, 0, 0, 0, 0]), (-1, None, vec![1])],
|
||||
|
};
|
||||
|
test_hyperplonk_helper::<Bls12_381>(2, 2, 0, 1, gates)
|
||||
|
}
|
||||
|
|
||||
|
fn test_hyperplonk_helper<E: PairingEngine>(
|
||||
|
nv: usize,
|
||||
|
log_pub_input_len: usize,
|
||||
|
log_n_selectors: usize,
|
||||
|
log_n_wires: usize,
|
||||
|
gate_func: CustomizedGates,
|
||||
|
) -> Result<(), HyperPlonkErrors> {
|
||||
|
let mut rng = test_rng();
|
||||
|
// system parameters
|
||||
|
let params = HyperPlonkParams {
|
||||
|
nv,
|
||||
|
log_pub_input_len,
|
||||
|
log_n_selectors,
|
||||
|
log_n_wires,
|
||||
|
gate_func,
|
||||
|
};
|
||||
|
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]
|
||||
|
let w1 = WitnessColumn(vec![
|
||||
|
E::Fr::zero(),
|
||||
|
E::Fr::one(),
|
||||
|
E::Fr::from(2u64),
|
||||
|
E::Fr::from(3u64),
|
||||
|
]);
|
||||
|
// w2 := [0^5, 1^5, 2^5, 3^5]
|
||||
|
let w2 = WitnessColumn(vec![
|
||||
|
E::Fr::zero(),
|
||||
|
E::Fr::one(),
|
||||
|
E::Fr::from(32u64),
|
||||
|
E::Fr::from(243u64),
|
||||
|
]);
|
||||
|
// public input = w1
|
||||
|
let pi = w1.clone();
|
||||
|
|
||||
|
// generate pk and vks
|
||||
|
let (pk, vk) = <PolyIOP<E::Fr> as HyperPlonkSNARK<E, KZGMultilinearPCS<E>>>::preprocess(
|
||||
|
¶ms,
|
||||
|
&pcs_srs,
|
||||
|
&perm,
|
||||
|
&[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,
|
||||
|
)?;
|
||||
|
|
||||
|
Ok(())
|
||||
}
|
}
|
||||
}
|
}
|
@ -0,0 +1,46 @@ |
|||||
|
use crate::errors::HyperPlonkErrors;
|
||||
|
use ark_ff::PrimeField;
|
||||
|
use ark_poly::DenseMultilinearExtension;
|
||||
|
use ark_std::log2;
|
||||
|
|
||||
|
/// A column of selectors of length `#constraints`
|
||||
|
#[derive(Debug, Clone)]
|
||||
|
pub struct SelectorColumn<F: PrimeField>(pub(crate) Vec<F>);
|
||||
|
|
||||
|
impl<F: PrimeField> SelectorColumn<F> {
|
||||
|
/// the number of variables for MLE to present a column.
|
||||
|
pub fn get_nv(&self) -> usize {
|
||||
|
log2(self.0.len()) as usize
|
||||
|
}
|
||||
|
|
||||
|
/// Build selector columns from rows
|
||||
|
pub fn from_selector_rows(
|
||||
|
selector_rows: &[SelectorColumn<F>],
|
||||
|
) -> Result<Vec<Self>, HyperPlonkErrors> {
|
||||
|
if selector_rows.is_empty() {
|
||||
|
return Err(HyperPlonkErrors::InvalidParameters(
|
||||
|
"empty witness rows".to_string(),
|
||||
|
));
|
||||
|
}
|
||||
|
|
||||
|
let mut res = Vec::with_capacity(selector_rows.len());
|
||||
|
let num_wires = selector_rows[0].0.len();
|
||||
|
|
||||
|
for i in 0..num_wires {
|
||||
|
let mut cur_column = Vec::new();
|
||||
|
for row in selector_rows.iter() {
|
||||
|
cur_column.push(row.0[i])
|
||||
|
}
|
||||
|
res.push(Self(cur_column))
|
||||
|
}
|
||||
|
|
||||
|
Ok(res)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<F: PrimeField> From<&SelectorColumn<F>> for DenseMultilinearExtension<F> {
|
||||
|
fn from(witness: &SelectorColumn<F>) -> Self {
|
||||
|
let nv = witness.get_nv();
|
||||
|
Self::from_evaluations_slice(nv, witness.0.as_ref())
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,156 @@ |
|||||
|
//! Main module for the HyperPlonk PolyIOP.
|
||||
|
|
||||
|
use ark_ec::PairingEngine;
|
||||
|
use ark_ff::PrimeField;
|
||||
|
use ark_poly::DenseMultilinearExtension;
|
||||
|
use pcs::PolynomialCommitmentScheme;
|
||||
|
use poly_iop::prelude::{PermutationCheck, ZeroCheck};
|
||||
|
use std::rc::Rc;
|
||||
|
|
||||
|
/// The sub-claim for the HyperPlonk PolyIOP, consists of the following:
|
||||
|
/// - the SubClaim for the zero-check PIOP
|
||||
|
/// - the SubClaim for the permutation-check PIOP
|
||||
|
/// - the SubClaim for public input consistency
|
||||
|
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
|
pub struct HyperPlonkSubClaim<F: PrimeField, ZC: ZeroCheck<F>, PC: PermutationCheck<F>> {
|
||||
|
/// the SubClaim for the custom gate zerocheck
|
||||
|
pub zero_check_sub_claim: ZC::ZeroCheckSubClaim,
|
||||
|
/// the SubClaim for the permutation check
|
||||
|
pub perm_check_sub_claim: PC::PermutationCheckSubClaim,
|
||||
|
/// the public input consistency check
|
||||
|
pub pub_input_sub_claim: (Vec<F>, F), // (point, expected_eval)
|
||||
|
}
|
||||
|
|
||||
|
/// The proof for the HyperPlonk PolyIOP, consists of the following:
|
||||
|
/// - a batch commitment to all the witness MLEs
|
||||
|
/// - a batch opening to all the MLEs at certain index
|
||||
|
/// - the zero-check proof for checking custom gate-satisfiability
|
||||
|
/// - the permutation-check proof for checking the copy constraints
|
||||
|
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
|
pub struct HyperPlonkProof<
|
||||
|
E: PairingEngine,
|
||||
|
PCS: PolynomialCommitmentScheme<E>,
|
||||
|
ZC: ZeroCheck<E::Fr>,
|
||||
|
PC: PermutationCheck<E::Fr>,
|
||||
|
> {
|
||||
|
// =======================================================================
|
||||
|
// PCS components
|
||||
|
// =======================================================================
|
||||
|
/// PCS commit for witnesses
|
||||
|
// TODO: replace me with a batch commitment
|
||||
|
pub witness_commits: Vec<PCS::Commitment>,
|
||||
|
pub w_merged_com: PCS::Commitment,
|
||||
|
/// PCS commit for prod(x)
|
||||
|
// TODO: replace me with a batch commitment
|
||||
|
pub prod_commit: PCS::Commitment,
|
||||
|
/// 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 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>,
|
||||
|
/// Evaluates of public inputs on r_pi from transcript
|
||||
|
pub pi_eval: E::Fr,
|
||||
|
/// Opening of public inputs on r_pi from transcript
|
||||
|
pub pi_opening: PCS::Proof,
|
||||
|
// =======================================================================
|
||||
|
// IOP components
|
||||
|
// =======================================================================
|
||||
|
/// the custom gate zerocheck proof
|
||||
|
pub zero_check_proof: ZC::ZeroCheckProof,
|
||||
|
/// the permutation check proof for copy constraints
|
||||
|
pub perm_check_proof: PC::PermutationProof,
|
||||
|
}
|
||||
|
|
||||
|
/// The HyperPlonk instance parameters, consists of the following:
|
||||
|
/// - the number of variables in the poly-IOP
|
||||
|
/// - binary log of the number of public input variables
|
||||
|
/// - binary log of the number of selectors
|
||||
|
/// - binary log of the number of witness wires
|
||||
|
/// - the customized gate function
|
||||
|
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
|
pub struct HyperPlonkParams {
|
||||
|
/// the number of variables in polys
|
||||
|
pub nv: usize,
|
||||
|
/// binary log of the public input length
|
||||
|
pub log_pub_input_len: usize,
|
||||
|
// binary log of the number of selectors
|
||||
|
pub log_n_selectors: usize,
|
||||
|
/// binary log of the number of witness wires
|
||||
|
pub log_n_wires: usize,
|
||||
|
/// customized gate function
|
||||
|
pub gate_func: CustomizedGates,
|
||||
|
}
|
||||
|
|
||||
|
/// The HyperPlonk proving key, consists of the following:
|
||||
|
/// - the hyperplonk instance parameters
|
||||
|
/// - the preprocessed polynomials output by the indexer
|
||||
|
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
|
pub struct HyperPlonkProvingKey<E: PairingEngine, PCS: PolynomialCommitmentScheme<E>> {
|
||||
|
/// hyperplonk instance parameters
|
||||
|
pub params: HyperPlonkParams,
|
||||
|
/// the preprocessed permutation polynomials
|
||||
|
pub permutation_oracles: Rc<DenseMultilinearExtension<E::Fr>>,
|
||||
|
/// the preprocessed selector polynomials
|
||||
|
// TODO: merge the list into a single MLE
|
||||
|
pub selector_oracles: Vec<Rc<DenseMultilinearExtension<E::Fr>>>,
|
||||
|
/// the parameters for PCS commitment
|
||||
|
pub pcs_param: PCS::ProverParam,
|
||||
|
}
|
||||
|
|
||||
|
/// The HyperPlonk verifying key, consists of the following:
|
||||
|
/// - the hyperplonk instance parameters
|
||||
|
/// - the preprocessed polynomials output by the indexer
|
||||
|
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
|
pub struct HyperPlonkVerifyingKey<E: PairingEngine, PCS: PolynomialCommitmentScheme<E>> {
|
||||
|
/// hyperplonk instance parameters
|
||||
|
pub params: HyperPlonkParams,
|
||||
|
/// the parameters for PCS commitment
|
||||
|
pub pcs_param: PCS::VerifierParam,
|
||||
|
/// Selector's commitment
|
||||
|
// TODO: replace me with a batch commitment
|
||||
|
pub selector_com: Vec<PCS::Commitment>,
|
||||
|
/// Permutation oracle's commitment
|
||||
|
pub perm_com: PCS::Commitment,
|
||||
|
}
|
||||
|
|
||||
|
/// Customized gate is a list of tuples of
|
||||
|
/// (coefficient, selector_index, wire_indices)
|
||||
|
///
|
||||
|
/// Example:
|
||||
|
/// q_L(X) * W_1(X)^5 - W_2(X)
|
||||
|
/// is represented as
|
||||
|
/// vec![
|
||||
|
/// ( 1, Some(id_qL), vec![id_W1, id_W1, id_W1, id_W1, id_W1]),
|
||||
|
/// (-1, None, vec![id_W2])
|
||||
|
/// ]
|
||||
|
///
|
||||
|
/// CustomizedGates {
|
||||
|
/// gates: vec![
|
||||
|
/// (1, Some(0), vec![0, 0, 0, 0, 0]),
|
||||
|
/// (-1, None, vec![1])
|
||||
|
/// ],
|
||||
|
/// };
|
||||
|
/// where id_qL = 0 // first selector
|
||||
|
/// id_W1 = 0 // first witness
|
||||
|
/// id_w2 = 1 // second witness
|
||||
|
///
|
||||
|
/// NOTE: here coeff is a signed integer, instead of a field element
|
||||
|
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
|
pub struct CustomizedGates {
|
||||
|
pub(crate) gates: Vec<(i64, Option<usize>, Vec<usize>)>,
|
||||
|
}
|
@ -0,0 +1,210 @@ |
|||||
|
use std::rc::Rc;
|
||||
|
|
||||
|
use arithmetic::VirtualPolynomial;
|
||||
|
use ark_ff::PrimeField;
|
||||
|
use ark_poly::DenseMultilinearExtension;
|
||||
|
|
||||
|
use crate::{errors::HyperPlonkErrors, structs::CustomizedGates};
|
||||
|
|
||||
|
/// Build MLE from matrix of witnesses.
|
||||
|
///
|
||||
|
/// Given a matrix := [row1, row2, ...] where
|
||||
|
/// row1:= (a1, a2, ...)
|
||||
|
/// row2:= (b1, b2, ...)
|
||||
|
/// row3:= (c1, c2, ...)
|
||||
|
///
|
||||
|
/// output mle(a1,b1,c1, ...), mle(a2,b2,c2, ...), ...
|
||||
|
#[macro_export]
|
||||
|
macro_rules! build_mle {
|
||||
|
($rows:expr) => {{
|
||||
|
let mut res = Vec::with_capacity($rows.len());
|
||||
|
let num_vars = log2($rows.len()) as usize;
|
||||
|
let num_mles = $rows[0].0.len();
|
||||
|
|
||||
|
for i in 0..num_mles {
|
||||
|
let mut cur_coeffs = Vec::new();
|
||||
|
for row in $rows.iter() {
|
||||
|
cur_coeffs.push(row.0[i])
|
||||
|
}
|
||||
|
res.push(Rc::new(DenseMultilinearExtension::from_evaluations_vec(
|
||||
|
num_vars, cur_coeffs,
|
||||
|
)))
|
||||
|
}
|
||||
|
|
||||
|
Ok(res)
|
||||
|
}};
|
||||
|
}
|
||||
|
|
||||
|
/// build `f(w_0(x),...w_d(x))` where `f` is the constraint polynomial
|
||||
|
/// i.e., `f(a, b, c) = q_l a(x) + q_r b(x) + q_m a(x)b(x) - q_o c(x)` in
|
||||
|
/// vanilla plonk
|
||||
|
pub(crate) fn build_f<F: PrimeField>(
|
||||
|
gates: &CustomizedGates,
|
||||
|
num_vars: usize,
|
||||
|
selector_mles: &[Rc<DenseMultilinearExtension<F>>],
|
||||
|
witness_mles: &[Rc<DenseMultilinearExtension<F>>],
|
||||
|
) -> Result<VirtualPolynomial<F>, HyperPlonkErrors> {
|
||||
|
// TODO: check that selector and witness lengths match what is in
|
||||
|
// the gate definition
|
||||
|
|
||||
|
for selector_mle in selector_mles.iter() {
|
||||
|
if selector_mle.num_vars != num_vars {
|
||||
|
return Err(HyperPlonkErrors::InvalidParameters(format!(
|
||||
|
"selector has different number of vars: {} vs {}",
|
||||
|
selector_mle.num_vars, num_vars
|
||||
|
)));
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
for witness_mle in witness_mles.iter() {
|
||||
|
if witness_mle.num_vars != num_vars {
|
||||
|
return Err(HyperPlonkErrors::InvalidParameters(format!(
|
||||
|
"selector has different number of vars: {} vs {}",
|
||||
|
witness_mle.num_vars, num_vars
|
||||
|
)));
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
let mut res = VirtualPolynomial::<F>::new(num_vars);
|
||||
|
|
||||
|
for (coeff, selector, witnesses) in gates.gates.iter() {
|
||||
|
let coeff_fr = if *coeff < 0 {
|
||||
|
-F::from(-*coeff as u64)
|
||||
|
} else {
|
||||
|
F::from(*coeff as u64)
|
||||
|
};
|
||||
|
let mut mle_list = vec![];
|
||||
|
match *selector {
|
||||
|
Some(s) => mle_list.push(selector_mles[s].clone()),
|
||||
|
None => (),
|
||||
|
};
|
||||
|
for &witness in witnesses.iter() {
|
||||
|
mle_list.push(witness_mles[witness].clone())
|
||||
|
}
|
||||
|
res.add_mle_list(mle_list, coeff_fr)?;
|
||||
|
}
|
||||
|
|
||||
|
Ok(res)
|
||||
|
}
|
||||
|
|
||||
|
#[allow(dead_code)]
|
||||
|
pub(crate) fn eval_f<F: PrimeField>(
|
||||
|
gates: &CustomizedGates,
|
||||
|
selector_evals: &[F],
|
||||
|
witness_evals: &[F],
|
||||
|
) -> Result<F, HyperPlonkErrors> {
|
||||
|
let mut res = F::zero();
|
||||
|
for (coeff, selector, witnesses) in gates.gates.iter() {
|
||||
|
let mut cur_value = if *coeff < 0 {
|
||||
|
-F::from(-*coeff as u64)
|
||||
|
} else {
|
||||
|
F::from(*coeff as u64)
|
||||
|
};
|
||||
|
cur_value *= match selector {
|
||||
|
Some(s) => selector_evals[*s],
|
||||
|
None => F::one(),
|
||||
|
};
|
||||
|
for &witness in witnesses.iter() {
|
||||
|
cur_value *= witness_evals[witness]
|
||||
|
}
|
||||
|
res += cur_value;
|
||||
|
}
|
||||
|
Ok(res)
|
||||
|
}
|
||||
|
|
||||
|
#[cfg(test)]
|
||||
|
mod test {
|
||||
|
use super::*;
|
||||
|
use ark_bls12_381::Fr;
|
||||
|
use ark_ff::PrimeField;
|
||||
|
use ark_poly::MultilinearExtension;
|
||||
|
#[test]
|
||||
|
fn test_build_gate() -> Result<(), HyperPlonkErrors> {
|
||||
|
test_build_gate_helper::<Fr>()
|
||||
|
}
|
||||
|
|
||||
|
fn test_build_gate_helper<F: PrimeField>() -> Result<(), HyperPlonkErrors> {
|
||||
|
let num_vars = 2;
|
||||
|
|
||||
|
// ql = 3x1x2 + 2x2 whose evaluations are
|
||||
|
// 0, 0 |-> 0
|
||||
|
// 0, 1 |-> 2
|
||||
|
// 1, 0 |-> 0
|
||||
|
// 1, 1 |-> 5
|
||||
|
let ql_eval = vec![F::zero(), F::from(2u64), F::zero(), F::from(5u64)];
|
||||
|
let ql = Rc::new(DenseMultilinearExtension::from_evaluations_vec(2, ql_eval));
|
||||
|
|
||||
|
// W1 = x1x2 + x1 whose evaluations are
|
||||
|
// 0, 0 |-> 0
|
||||
|
// 0, 1 |-> 0
|
||||
|
// 1, 0 |-> 1
|
||||
|
// 1, 1 |-> 2
|
||||
|
let w_eval = vec![F::zero(), F::zero(), F::from(1u64), F::from(2u64)];
|
||||
|
let w1 = Rc::new(DenseMultilinearExtension::from_evaluations_vec(2, w_eval));
|
||||
|
|
||||
|
// W2 = x1 + x2 whose evaluations are
|
||||
|
// 0, 0 |-> 0
|
||||
|
// 0, 1 |-> 1
|
||||
|
// 1, 0 |-> 1
|
||||
|
// 1, 1 |-> 2
|
||||
|
let w_eval = vec![F::zero(), F::one(), F::from(1u64), F::from(2u64)];
|
||||
|
let w2 = Rc::new(DenseMultilinearExtension::from_evaluations_vec(2, w_eval));
|
||||
|
|
||||
|
// Example:
|
||||
|
// q_L(X) * W_1(X)^5 - W_2(X)
|
||||
|
// is represented as
|
||||
|
// vec![
|
||||
|
// ( 1, Some(id_qL), vec![id_W1, id_W1, id_W1, id_W1, id_W1]),
|
||||
|
// (-1, None, vec![id_W2])
|
||||
|
// ]
|
||||
|
let gates = CustomizedGates {
|
||||
|
gates: vec![(1, Some(0), vec![0, 0, 0, 0, 0]), (-1, None, vec![1])],
|
||||
|
};
|
||||
|
let f = build_f(&gates, num_vars, &[ql.clone()], &[w1.clone(), w2.clone()])?;
|
||||
|
|
||||
|
// Sanity check on build_f
|
||||
|
// f(0, 0) = 0
|
||||
|
assert_eq!(f.evaluate(&[F::zero(), F::zero()])?, F::zero());
|
||||
|
// f(0, 1) = 2 * 0^5 + (-1) * 1 = -1
|
||||
|
assert_eq!(f.evaluate(&[F::zero(), F::one()])?, -F::one());
|
||||
|
// f(1, 0) = 0 * 1^5 + (-1) * 1 = -1
|
||||
|
assert_eq!(f.evaluate(&[F::one(), F::zero()])?, -F::one());
|
||||
|
// f(1, 1) = 5 * 2^5 + (-1) * 2 = 158
|
||||
|
assert_eq!(f.evaluate(&[F::one(), F::one()])?, F::from(158u64));
|
||||
|
|
||||
|
// test eval_f
|
||||
|
{
|
||||
|
let point = [F::zero(), F::zero()];
|
||||
|
let selector_evals = ql.evaluate(&point).unwrap();
|
||||
|
let witness_evals = [w1.evaluate(&point).unwrap(), w2.evaluate(&point).unwrap()];
|
||||
|
let eval_f = eval_f(&gates, &[selector_evals], &witness_evals)?;
|
||||
|
// f(0, 0) = 0
|
||||
|
assert_eq!(eval_f, F::zero());
|
||||
|
}
|
||||
|
{
|
||||
|
let point = [F::zero(), F::one()];
|
||||
|
let selector_evals = ql.evaluate(&point).unwrap();
|
||||
|
let witness_evals = [w1.evaluate(&point).unwrap(), w2.evaluate(&point).unwrap()];
|
||||
|
let eval_f = eval_f(&gates, &[selector_evals], &witness_evals)?;
|
||||
|
// f(0, 1) = 2 * 0^5 + (-1) * 1 = -1
|
||||
|
assert_eq!(eval_f, -F::one());
|
||||
|
}
|
||||
|
{
|
||||
|
let point = [F::one(), F::zero()];
|
||||
|
let selector_evals = ql.evaluate(&point).unwrap();
|
||||
|
let witness_evals = [w1.evaluate(&point).unwrap(), w2.evaluate(&point).unwrap()];
|
||||
|
let eval_f = eval_f(&gates, &[selector_evals], &witness_evals)?;
|
||||
|
// f(1, 0) = 0 * 1^5 + (-1) * 1 = -1
|
||||
|
assert_eq!(eval_f, -F::one());
|
||||
|
}
|
||||
|
{
|
||||
|
let point = [F::one(), F::one()];
|
||||
|
let selector_evals = ql.evaluate(&point).unwrap();
|
||||
|
let witness_evals = [w1.evaluate(&point).unwrap(), w2.evaluate(&point).unwrap()];
|
||||
|
let eval_f = eval_f(&gates, &[selector_evals], &witness_evals)?;
|
||||
|
// f(1, 1) = 5 * 2^5 + (-1) * 2 = 158
|
||||
|
assert_eq!(eval_f, F::from(158u64));
|
||||
|
}
|
||||
|
Ok(())
|
||||
|
}
|
||||
|
}
|
@ -0,0 +1,67 @@ |
|||||
|
use crate::{build_mle, errors::HyperPlonkErrors};
|
||||
|
use ark_ff::PrimeField;
|
||||
|
use ark_poly::DenseMultilinearExtension;
|
||||
|
use ark_std::log2;
|
||||
|
use std::rc::Rc;
|
||||
|
|
||||
|
/// A row of witnesses of width `#wires`
|
||||
|
#[derive(Debug, Clone)]
|
||||
|
pub struct WitnessRow<F: PrimeField>(pub(crate) Vec<F>);
|
||||
|
|
||||
|
/// A column of witnesses of length `#constraints`
|
||||
|
#[derive(Debug, Clone)]
|
||||
|
pub struct WitnessColumn<F: PrimeField>(pub(crate) Vec<F>);
|
||||
|
|
||||
|
impl<F: PrimeField> WitnessColumn<F> {
|
||||
|
/// the number of variables for MLE to present a column.
|
||||
|
pub fn get_nv(&self) -> usize {
|
||||
|
log2(self.0.len()) as usize
|
||||
|
}
|
||||
|
|
||||
|
/// Build witness columns from rows
|
||||
|
pub fn from_witness_rows(
|
||||
|
witness_rows: &[WitnessRow<F>],
|
||||
|
) -> Result<Vec<Self>, HyperPlonkErrors> {
|
||||
|
if witness_rows.is_empty() {
|
||||
|
return Err(HyperPlonkErrors::InvalidParameters(
|
||||
|
"empty witness rows".to_string(),
|
||||
|
));
|
||||
|
}
|
||||
|
|
||||
|
let mut res = Vec::with_capacity(witness_rows.len());
|
||||
|
let num_wires = witness_rows[0].0.len();
|
||||
|
|
||||
|
for i in 0..num_wires {
|
||||
|
let mut cur_column = Vec::new();
|
||||
|
for row in witness_rows.iter() {
|
||||
|
cur_column.push(row.0[i])
|
||||
|
}
|
||||
|
res.push(Self(cur_column))
|
||||
|
}
|
||||
|
|
||||
|
Ok(res)
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<F: PrimeField> From<&WitnessColumn<F>> for DenseMultilinearExtension<F> {
|
||||
|
fn from(witness: &WitnessColumn<F>) -> Self {
|
||||
|
let nv = witness.get_nv();
|
||||
|
Self::from_evaluations_slice(nv, witness.0.as_ref())
|
||||
|
}
|
||||
|
}
|
||||
|
|
||||
|
impl<F: PrimeField> WitnessRow<F> {
|
||||
|
/// Build MLE from matrix of witnesses.
|
||||
|
///
|
||||
|
/// Given a matrix := [row1, row2, ...] where
|
||||
|
/// row1:= (a1, a2, ...)
|
||||
|
/// row2:= (b1, b2, ...)
|
||||
|
/// row3:= (c1, c2, ...)
|
||||
|
///
|
||||
|
/// output mle(a1,b1,c1, ...), mle(a2,b2,c2, ...), ...
|
||||
|
pub fn build_mles(
|
||||
|
matrix: &[Self],
|
||||
|
) -> Result<Vec<Rc<DenseMultilinearExtension<F>>>, HyperPlonkErrors> {
|
||||
|
build_mle!(matrix)
|
||||
|
}
|
||||
|
}
|
@ -1,3 +0,0 @@ |
|||||
use ark_ec::{AffineCurve, PairingEngine};
|
|
||||
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Read, SerializationError, Write};
|
|
||||
use ark_std::vec::Vec;
|
|
@ -1,126 +0,0 @@ |
|||||
//! Main module for the HyperPlonk PolyIOP.
|
|
||||
|
|
||||
use crate::{errors::PolyIOPErrors, perm_check::PermutationCheck, zero_check::ZeroCheck};
|
|
||||
use ark_ff::PrimeField;
|
|
||||
use ark_poly::DenseMultilinearExtension;
|
|
||||
use std::rc::Rc;
|
|
||||
use transcript::IOPTranscript;
|
|
||||
|
|
||||
/// A trait for HyperPlonk Poly-IOPs
|
|
||||
pub trait HyperPlonkPIOP<F: PrimeField> {
|
|
||||
type Parameters;
|
|
||||
type ProvingKey;
|
|
||||
type Proof;
|
|
||||
type SubClaim;
|
|
||||
|
|
||||
/// Generate the preprocessed polynomials output by the indexer.
|
|
||||
///
|
|
||||
/// Inputs:
|
|
||||
/// - `params`: HyperPlonk instance parameters
|
|
||||
/// - `permutation`: the permutation for the copy constraints
|
|
||||
/// - `selectors`: the list of selector vectors for custom gates
|
|
||||
/// Outputs:
|
|
||||
/// - The HyperPlonk proving key, which includes the preprocessed
|
|
||||
/// polynomials.
|
|
||||
fn preprocess(
|
|
||||
params: &Self::Parameters,
|
|
||||
permutation: &[F],
|
|
||||
selectors: &[&[F]],
|
|
||||
) -> Result<Self::ProvingKey, PolyIOPErrors>;
|
|
||||
|
|
||||
/// Generate HyperPlonk PIOP proof.
|
|
||||
///
|
|
||||
/// Inputs:
|
|
||||
/// - `pk`: circuit proving key
|
|
||||
/// - `pub_input`: online public input
|
|
||||
/// - `witness`: witness assignement
|
|
||||
/// - `transcript`: the transcript used for generating pseudorandom
|
|
||||
/// challenges
|
|
||||
/// Outputs:
|
|
||||
/// - The HyperPlonk PIOP proof.
|
|
||||
fn prove(
|
|
||||
pk: &Self::ProvingKey,
|
|
||||
pub_input: &[F],
|
|
||||
witness: &[&[F]],
|
|
||||
transcript: &mut IOPTranscript<F>,
|
|
||||
) -> Result<Self::Proof, PolyIOPErrors>;
|
|
||||
|
|
||||
/// Verify the HyperPlonk proof and generate the evaluation subclaims to be
|
|
||||
/// checked later by the SNARK verifier.
|
|
||||
///
|
|
||||
/// Inputs:
|
|
||||
/// - `params`: instance parameter
|
|
||||
/// - `pub_input`: online public input
|
|
||||
/// - `proof`: HyperPlonk PIOP proof
|
|
||||
/// - `transcript`: the transcript used for generating pseudorandom
|
|
||||
/// challenges
|
|
||||
/// Outputs:
|
|
||||
/// - Return error if the verification fails, otherwise return the
|
|
||||
/// evaluation subclaim
|
|
||||
fn verify(
|
|
||||
params: &Self::Parameters,
|
|
||||
pub_input: &[F],
|
|
||||
proof: &Self::Proof,
|
|
||||
transcript: &mut IOPTranscript<F>,
|
|
||||
) -> Result<Self::SubClaim, PolyIOPErrors>;
|
|
||||
}
|
|
||||
|
|
||||
/// The sub-claim for the HyperPlonk PolyIOP, consists of the following:
|
|
||||
/// - the SubClaim for the zero-check PIOP
|
|
||||
/// - the SubClaim for the permutation-check PIOP
|
|
||||
/// - the SubClaim for public input consistency
|
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
|
||||
pub struct HyperPlonkSubClaim<F: PrimeField, ZC: ZeroCheck<F>, PC: PermutationCheck<F>> {
|
|
||||
/// the SubClaim for the custom gate zerocheck
|
|
||||
pub zero_check_sub_claim: ZC::ZeroCheckSubClaim,
|
|
||||
/// the SubClaim for the permutation check
|
|
||||
pub perm_check_sub_claim: PC::PermutationCheckSubClaim,
|
|
||||
/// the public input consistency check
|
|
||||
pub pub_input_sub_claim: (Vec<F>, F), // (point, expected_eval)
|
|
||||
}
|
|
||||
|
|
||||
/// The proof for the HyperPlonk PolyIOP, consists of the following:
|
|
||||
/// - the zero-check proof for checking custom gate-satisfiability
|
|
||||
/// - the permutation-check proof for checking the copy constraints
|
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
|
||||
pub struct HyperPlonkProof<F: PrimeField, ZC: ZeroCheck<F>, PC: PermutationCheck<F>> {
|
|
||||
/// the custom gate zerocheck proof
|
|
||||
pub zero_check_proof: ZC::Proof,
|
|
||||
/// the permutation check proof for copy constraints
|
|
||||
pub perm_check_proof: PC::Proof,
|
|
||||
}
|
|
||||
|
|
||||
/// The HyperPlonk instance parameters, consists of the following:
|
|
||||
/// - the number of variables in the poly-IOP
|
|
||||
/// - binary log of the number of public input variables
|
|
||||
/// - binary log of the number of selectors
|
|
||||
/// - binary log of the number of witness wires
|
|
||||
/// - the customized gate function
|
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
|
||||
pub struct HyperPlonkParams {
|
|
||||
/// the number of variables in polys
|
|
||||
pub nv: usize,
|
|
||||
/// binary log of the public input length
|
|
||||
pub log_pub_input_len: usize,
|
|
||||
// binary log of the number of selectors
|
|
||||
pub log_n_selectors: usize,
|
|
||||
/// binary log of the number of witness wires
|
|
||||
pub log_n_wires: usize,
|
|
||||
/// customized gate function
|
|
||||
// TODO: define a struct for it.
|
|
||||
pub gate_func: Vec<Vec<usize>>,
|
|
||||
}
|
|
||||
|
|
||||
/// The HyperPlonk proving key, consists of the following:
|
|
||||
/// - the hyperplonk instance parameters
|
|
||||
/// - the preprocessed polynomials output by the indexer
|
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
|
||||
pub struct HyperPlonkProvingKey<F: PrimeField> {
|
|
||||
/// hyperplonk instance parameters
|
|
||||
pub params: HyperPlonkParams,
|
|
||||
/// the preprocessed index polynomials
|
|
||||
pub index_oracles: Vec<Rc<DenseMultilinearExtension<F>>>,
|
|
||||
}
|
|
||||
|
|
||||
#[cfg(test)]
|
|
||||
mod test {}
|
|
@ -0,0 +1,11 @@ |
|||||
|
pub use crate::{
|
||||
|
errors::PolyIOPErrors,
|
||||
|
perm_check::{
|
||||
|
util::{identity_permutation_mle, random_permutation_mle},
|
||||
|
PermutationCheck,
|
||||
|
},
|
||||
|
sum_check::SumCheck,
|
||||
|
utils::*,
|
||||
|
zero_check::ZeroCheck,
|
||||
|
PolyIOP,
|
||||
|
};
|