mirror of
https://github.com/arnaucube/hyperplonk.git
synced 2026-01-12 00:51:27 +01:00
initial integration of hyperplonk snark(#39)
This commit is contained in:
@@ -15,10 +15,13 @@ ark-bls12-381 = { version = "0.3.0", default-features = false, features = [ "cur
|
||||
|
||||
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 }
|
||||
|
||||
transcript = { path = "../transcript" }
|
||||
arithmetic = { path = "../arithmetic" }
|
||||
|
||||
[dev-dependencies]
|
||||
ark-ec = { version = "^0.3.0", default-features = false }
|
||||
|
||||
# Benchmarks
|
||||
[[bench]]
|
||||
@@ -31,10 +34,12 @@ harness = false
|
||||
default = [ "parallel" ]
|
||||
parallel = [
|
||||
"rayon",
|
||||
"arithmetic/parallel",
|
||||
"ark-std/parallel",
|
||||
"ark-ff/parallel",
|
||||
"ark-poly/parallel"
|
||||
]
|
||||
print-trace = [
|
||||
"arithmetic/print-trace",
|
||||
"ark-std/print-trace"
|
||||
]
|
||||
@@ -1,9 +1,9 @@
|
||||
use arithmetic::{VPAuxInfo, VirtualPolynomial};
|
||||
use ark_bls12_381::Fr;
|
||||
use ark_poly::{DenseMultilinearExtension, MultilinearExtension};
|
||||
use ark_std::{test_rng, UniformRand};
|
||||
use poly_iop::{
|
||||
identity_permutation_mle, PermutationCheck, PolyIOP, PolyIOPErrors, SumCheck, VPAuxInfo,
|
||||
VirtualPolynomial, ZeroCheck,
|
||||
use poly_iop::prelude::{
|
||||
identity_permutation_mle, PermutationCheck, PolyIOP, PolyIOPErrors, SumCheck, ZeroCheck,
|
||||
};
|
||||
use std::{marker::PhantomData, time::Instant};
|
||||
|
||||
@@ -156,7 +156,7 @@ fn bench_permutation_check() -> Result<(), PolyIOPErrors> {
|
||||
let mut challenge =
|
||||
<PolyIOP<Fr> as PermutationCheck<Fr>>::generate_challenge(&mut transcript)?;
|
||||
|
||||
let prod_x_and_aux = <PolyIOP<Fr> as PermutationCheck<Fr>>::compute_products(
|
||||
let prod_x_and_aux = <PolyIOP<Fr> as PermutationCheck<Fr>>::compute_prod_evals(
|
||||
&challenge, &w, &w, &s_perm,
|
||||
)?;
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
//! Error module.
|
||||
|
||||
use arithmetic::ArithErrors;
|
||||
use ark_std::string::String;
|
||||
use displaydoc::Display;
|
||||
use transcript::TranscriptErrors;
|
||||
@@ -20,19 +21,27 @@ pub enum PolyIOPErrors {
|
||||
/// Should not arrive to this point
|
||||
ShouldNotArrive,
|
||||
/// An error during (de)serialization: {0}
|
||||
SerializationError(ark_serialize::SerializationError),
|
||||
SerializationErrors(ark_serialize::SerializationError),
|
||||
/// Transcript Error: {0}
|
||||
TranscriptError(TranscriptErrors),
|
||||
TranscriptErrors(TranscriptErrors),
|
||||
/// Arithmetic Error: {0}
|
||||
ArithmeticErrors(ArithErrors),
|
||||
}
|
||||
|
||||
impl From<ark_serialize::SerializationError> for PolyIOPErrors {
|
||||
fn from(e: ark_serialize::SerializationError) -> Self {
|
||||
Self::SerializationError(e)
|
||||
Self::SerializationErrors(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TranscriptErrors> for PolyIOPErrors {
|
||||
fn from(e: TranscriptErrors) -> Self {
|
||||
Self::TranscriptError(e)
|
||||
Self::TranscriptErrors(e)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ArithErrors> for PolyIOPErrors {
|
||||
fn from(e: ArithErrors) -> Self {
|
||||
Self::ArithmeticErrors(e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {}
|
||||
@@ -2,17 +2,15 @@ use ark_ff::PrimeField;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
mod errors;
|
||||
mod hyperplonk;
|
||||
mod perm_check;
|
||||
pub mod prelude;
|
||||
mod prod_check;
|
||||
mod structs;
|
||||
mod sum_check;
|
||||
mod utils;
|
||||
mod virtual_poly;
|
||||
mod zero_check;
|
||||
|
||||
pub use errors::PolyIOPErrors;
|
||||
pub use hyperplonk::HyperPlonkPIOP;
|
||||
pub use perm_check::{
|
||||
util::{identity_permutation_mle, random_permutation_mle},
|
||||
PermutationCheck,
|
||||
@@ -20,7 +18,6 @@ pub use perm_check::{
|
||||
pub use prod_check::ProductCheck;
|
||||
pub use sum_check::SumCheck;
|
||||
pub use utils::*;
|
||||
pub use virtual_poly::{VPAuxInfo, VirtualPolynomial};
|
||||
pub use zero_check::ZeroCheck;
|
||||
|
||||
#[derive(Clone, Debug, Default, Copy)]
|
||||
|
||||
@@ -1,11 +1,16 @@
|
||||
//! Main module for the Permutation Check protocol
|
||||
|
||||
use crate::{
|
||||
errors::PolyIOPErrors, perm_check::util::compute_prod_0, structs::IOPProof, utils::get_index,
|
||||
PolyIOP, VirtualPolynomial, ZeroCheck,
|
||||
errors::PolyIOPErrors,
|
||||
perm_check::util::{build_prod_partial_eval, compute_prod_0},
|
||||
structs::IOPProof,
|
||||
utils::get_index,
|
||||
PolyIOP, ZeroCheck,
|
||||
};
|
||||
use arithmetic::VirtualPolynomial;
|
||||
use ark_ff::PrimeField;
|
||||
use ark_poly::DenseMultilinearExtension;
|
||||
use ark_serialize::CanonicalSerialize;
|
||||
use ark_std::{end_timer, start_timer};
|
||||
use std::rc::Rc;
|
||||
use transcript::IOPTranscript;
|
||||
@@ -31,6 +36,7 @@ pub mod util;
|
||||
pub trait PermutationCheck<F: PrimeField>: ZeroCheck<F> {
|
||||
type PermutationCheckSubClaim;
|
||||
type PermutationChallenge;
|
||||
type PermutationProof;
|
||||
|
||||
/// Generate the preprocessed polynomial for the permutation check.
|
||||
///
|
||||
@@ -58,22 +64,14 @@ pub trait PermutationCheck<F: PrimeField>: ZeroCheck<F> {
|
||||
transcript: &mut Self::Transcript,
|
||||
) -> Result<Self::PermutationChallenge, PolyIOPErrors>;
|
||||
|
||||
/// Step 4 of the IOP.
|
||||
/// Update the challenge with alpha; returns an error if
|
||||
/// alpha already exists.
|
||||
fn update_challenge(
|
||||
challenge: &mut Self::PermutationChallenge,
|
||||
transcript: &mut Self::Transcript,
|
||||
prod_x_binding: &F,
|
||||
) -> Result<(), PolyIOPErrors>;
|
||||
|
||||
/// Step 2 of the IOP.
|
||||
/// Compute the following 7 polynomials
|
||||
///
|
||||
/// Input:
|
||||
/// - f(x), g(x), s_perm(x) are mle-s
|
||||
/// - challenges, consists of beta and gamma
|
||||
///
|
||||
/// Output: the evaluations for the following 3 polynomials
|
||||
/// - prod(x)
|
||||
/// - prod(0, x)
|
||||
/// - prod(1, x)
|
||||
/// - prod(x, 0)
|
||||
/// - prod(x, 1)
|
||||
/// - numerator
|
||||
/// - denominator
|
||||
///
|
||||
@@ -84,11 +82,8 @@ pub trait PermutationCheck<F: PrimeField>: ZeroCheck<F> {
|
||||
///
|
||||
/// (f(x) + \beta s_id(x) + \gamma)/(g(x) + \beta s_perm(x) + \gamma)
|
||||
///
|
||||
/// where
|
||||
/// - beta and gamma are challenges
|
||||
/// - f(x), g(x), s_id(x), s_perm(x) are mle-s
|
||||
/// where s_id(x) is an identity permutation
|
||||
///
|
||||
/// - `prod(1,x) := prod(x, 0) * prod(x, 1)`
|
||||
/// - numerator is the MLE for `f(x) + \beta s_id(x) + \gamma`
|
||||
/// - denominator is the MLE for `g(x) + \beta s_perm(x) + \gamma`
|
||||
///
|
||||
@@ -96,33 +91,49 @@ pub trait PermutationCheck<F: PrimeField>: ZeroCheck<F> {
|
||||
/// Cost: linear in N.
|
||||
///
|
||||
/// TODO: replace argument `s_perm` with the merged polynomial `s`.
|
||||
fn compute_products(
|
||||
fn compute_prod_evals(
|
||||
challenge: &Self::PermutationChallenge,
|
||||
fx: &DenseMultilinearExtension<F>,
|
||||
gx: &DenseMultilinearExtension<F>,
|
||||
s_perm: &DenseMultilinearExtension<F>,
|
||||
) -> Result<[DenseMultilinearExtension<F>; 7], PolyIOPErrors>;
|
||||
) -> Result<[DenseMultilinearExtension<F>; 3], PolyIOPErrors>;
|
||||
|
||||
/// Step 3 of the IOP.
|
||||
/// push a commitment of `prod(x)` to the transcript
|
||||
/// IMPORTANT: this step is done by the snark caller
|
||||
fn commit_prod_x() {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
/// Step 4 of the IOP.
|
||||
/// Update the challenge with alpha; returns an error if
|
||||
/// alpha already exists.
|
||||
fn update_challenge(
|
||||
challenge: &mut Self::PermutationChallenge,
|
||||
transcript: &mut Self::Transcript,
|
||||
prod_x_binding: &impl CanonicalSerialize,
|
||||
) -> Result<(), PolyIOPErrors>;
|
||||
|
||||
/// Step 5 of the IOP.
|
||||
///
|
||||
/// Initialize the prover to argue that an MLE g(x) is a permutation of
|
||||
/// MLE f(x) over a permutation given by s_perm.
|
||||
/// Inputs:
|
||||
/// - 7 MLEs from `Self::compute_products`
|
||||
/// - 3 MLEs from the second step
|
||||
/// - challenge: `Self::Challenge` that has been updated
|
||||
/// - transcript: a transcript that is used to generate the challenges beta
|
||||
/// and gamma
|
||||
/// Cost: O(N)
|
||||
fn prove(
|
||||
prod_x_and_aux_info: &[DenseMultilinearExtension<F>; 7],
|
||||
prod_x_and_aux_info: &[DenseMultilinearExtension<F>; 3],
|
||||
challenge: &Self::PermutationChallenge,
|
||||
transcript: &mut IOPTranscript<F>,
|
||||
) -> Result<Self::Proof, PolyIOPErrors>;
|
||||
) -> Result<Self::PermutationProof, PolyIOPErrors>;
|
||||
|
||||
/// Verify that an MLE g(x) is a permutation of
|
||||
/// MLE f(x) over a permutation given by s_perm.
|
||||
fn verify(
|
||||
proof: &Self::Proof,
|
||||
proof: &Self::PermutationProof,
|
||||
aux_info: &Self::VPAuxInfo,
|
||||
transcript: &mut Self::Transcript,
|
||||
) -> Result<Self::PermutationCheckSubClaim, PolyIOPErrors>;
|
||||
@@ -140,13 +151,14 @@ pub trait PermutationCheck<F: PrimeField>: ZeroCheck<F> {
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub struct PermutationCheckSubClaim<F: PrimeField, ZC: ZeroCheck<F>> {
|
||||
// the SubClaim from the ZeroCheck
|
||||
zero_check_sub_claim: ZC::ZeroCheckSubClaim,
|
||||
pub zero_check_sub_claim: ZC::ZeroCheckSubClaim,
|
||||
// final query which consists of
|
||||
// - the vector `(1, ..., 1, 0)`
|
||||
// - the evaluation `1`
|
||||
final_query: (Vec<F>, F),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PermutationChallenge<F: PrimeField> {
|
||||
alpha: Option<F>,
|
||||
beta: F,
|
||||
@@ -175,6 +187,7 @@ impl<F: PrimeField> PermutationCheck<F> for PolyIOP<F> {
|
||||
/// - the initial challenge r which is used to build eq(x, r)
|
||||
type PermutationCheckSubClaim = PermutationCheckSubClaim<F, Self>;
|
||||
|
||||
type PermutationProof = Self::SumCheckProof;
|
||||
type PermutationChallenge = PermutationChallenge<F>;
|
||||
|
||||
/// Generate the preprocessed polynomial for the permutation check.
|
||||
@@ -213,31 +226,14 @@ impl<F: PrimeField> PermutationCheck<F> for PolyIOP<F> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Step 4 of the IOP.
|
||||
/// Update the challenge with alpha; returns an error if
|
||||
/// alpha already exists.
|
||||
fn update_challenge(
|
||||
challenge: &mut Self::PermutationChallenge,
|
||||
transcript: &mut Self::Transcript,
|
||||
prod_x_binding: &F,
|
||||
) -> Result<(), PolyIOPErrors> {
|
||||
if challenge.alpha.is_some() {
|
||||
return Err(PolyIOPErrors::InvalidChallenge(
|
||||
"alpha should not be sampled at the current stage".to_string(),
|
||||
));
|
||||
}
|
||||
transcript.append_field_element(b"prod(x)", prod_x_binding)?;
|
||||
challenge.alpha = Some(transcript.get_and_append_challenge(b"alpha")?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Step 2 of the IOP.
|
||||
/// Compute the following 7 polynomials
|
||||
///
|
||||
/// Input:
|
||||
/// - f(x), g(x), s_perm(x) are mle-s
|
||||
/// - challenges, consists of beta and gamma
|
||||
///
|
||||
/// Output: the evaluations for the following 3 polynomials
|
||||
/// - prod(x)
|
||||
/// - prod(0, x)
|
||||
/// - prod(1, x)
|
||||
/// - prod(x, 0)
|
||||
/// - prod(x, 1)
|
||||
/// - numerator
|
||||
/// - denominator
|
||||
///
|
||||
@@ -248,11 +244,8 @@ impl<F: PrimeField> PermutationCheck<F> for PolyIOP<F> {
|
||||
///
|
||||
/// (f(x) + \beta s_id(x) + \gamma)/(g(x) + \beta s_perm(x) + \gamma)
|
||||
///
|
||||
/// where
|
||||
/// - beta and gamma are challenges
|
||||
/// - f(x), g(x), s_id(x), s_perm(x) are mle-s
|
||||
/// where s_id(x) is an identity permutation
|
||||
///
|
||||
/// - `prod(1,x) := prod(x, 0) * prod(x, 1)`
|
||||
/// - numerator is the MLE for `f(x) + \beta s_id(x) + \gamma`
|
||||
/// - denominator is the MLE for `g(x) + \beta s_perm(x) + \gamma`
|
||||
///
|
||||
@@ -260,13 +253,13 @@ impl<F: PrimeField> PermutationCheck<F> for PolyIOP<F> {
|
||||
/// Cost: linear in N.
|
||||
///
|
||||
/// TODO: replace argument `s_perm` with the merged polynomial `s`.
|
||||
fn compute_products(
|
||||
fn compute_prod_evals(
|
||||
challenge: &Self::PermutationChallenge,
|
||||
fx: &DenseMultilinearExtension<F>,
|
||||
gx: &DenseMultilinearExtension<F>,
|
||||
s_perm: &DenseMultilinearExtension<F>,
|
||||
) -> Result<[DenseMultilinearExtension<F>; 7], PolyIOPErrors> {
|
||||
let start = start_timer!(|| "compute all prod polynomial");
|
||||
) -> Result<[DenseMultilinearExtension<F>; 3], PolyIOPErrors> {
|
||||
let start = start_timer!(|| "compute evaluations of prod polynomial");
|
||||
|
||||
if challenge.alpha.is_some() {
|
||||
return Err(PolyIOPErrors::InvalidChallenge(
|
||||
@@ -279,7 +272,7 @@ impl<F: PrimeField> PermutationCheck<F> for PolyIOP<F> {
|
||||
// ===================================
|
||||
// prod(0, x)
|
||||
// ===================================
|
||||
let (prod_0x, numerator, denominator) =
|
||||
let (prod_0x_eval, numerator_eval, denominator_eval) =
|
||||
compute_prod_0(&challenge.beta, &challenge.gamma, fx, gx, s_perm)?;
|
||||
|
||||
// ===================================
|
||||
@@ -294,82 +287,75 @@ impl<F: PrimeField> PermutationCheck<F> for PolyIOP<F> {
|
||||
//
|
||||
// At any given step, the right hand side of the equation
|
||||
// is available via either eval_0x or the current view of eval_1x
|
||||
let eval_0x = &prod_0x.evaluations;
|
||||
let mut eval_1x = vec![];
|
||||
let mut prod_1x_eval = vec![];
|
||||
for x in 0..(1 << num_vars) - 1 {
|
||||
// sign will decide if the evaluation should be looked up from eval_0x or
|
||||
// eval_1x; x_zero_index is the index for the evaluation (x_2, ..., x_n,
|
||||
// 0); x_one_index is the index for the evaluation (x_2, ..., x_n, 1);
|
||||
let (x_zero_index, x_one_index, sign) = get_index(x, num_vars);
|
||||
if !sign {
|
||||
eval_1x.push(eval_0x[x_zero_index] * eval_0x[x_one_index]);
|
||||
prod_1x_eval.push(prod_0x_eval[x_zero_index] * prod_0x_eval[x_one_index]);
|
||||
} else {
|
||||
// sanity check: if we are trying to look up from the eval_1x table,
|
||||
// then the target index must already exist
|
||||
if x_zero_index >= eval_1x.len() || x_one_index >= eval_1x.len() {
|
||||
if x_zero_index >= prod_1x_eval.len() || x_one_index >= prod_1x_eval.len() {
|
||||
return Err(PolyIOPErrors::ShouldNotArrive);
|
||||
}
|
||||
eval_1x.push(eval_1x[x_zero_index] * eval_1x[x_one_index]);
|
||||
prod_1x_eval.push(prod_1x_eval[x_zero_index] * prod_1x_eval[x_one_index]);
|
||||
}
|
||||
}
|
||||
|
||||
// prod(1, 1, ..., 1) := 0
|
||||
eval_1x.push(F::zero());
|
||||
prod_1x_eval.push(F::zero());
|
||||
|
||||
// ===================================
|
||||
// prod(x)
|
||||
// ===================================
|
||||
// prod(x)'s evaluation is indeed `e := [eval_0x[..], eval_1x[..]].concat()`
|
||||
let eval = [eval_0x.as_slice(), eval_1x.as_slice()].concat();
|
||||
let eval = [prod_0x_eval.as_slice(), prod_1x_eval.as_slice()].concat();
|
||||
|
||||
// ===================================
|
||||
// prod(x, 0) and prod(x, 1)
|
||||
// ===================================
|
||||
//
|
||||
// now we compute eval_x0 and eval_x1
|
||||
// eval_0x will be the odd coefficients of eval
|
||||
// and eval_1x will be the even coefficients of eval
|
||||
let mut eval_x0 = vec![];
|
||||
let mut eval_x1 = vec![];
|
||||
for (x, &prod_x) in eval.iter().enumerate() {
|
||||
if x & 1 == 0 {
|
||||
eval_x0.push(prod_x);
|
||||
} else {
|
||||
eval_x1.push(prod_x);
|
||||
}
|
||||
}
|
||||
|
||||
let prod_1x = DenseMultilinearExtension::from_evaluations_vec(num_vars, eval_1x);
|
||||
let prod_x0 = DenseMultilinearExtension::from_evaluations_vec(num_vars, eval_x0);
|
||||
let prod_x1 = DenseMultilinearExtension::from_evaluations_vec(num_vars, eval_x1);
|
||||
let prod = DenseMultilinearExtension::from_evaluations_vec(num_vars + 1, eval);
|
||||
let fx = DenseMultilinearExtension::from_evaluations_vec(num_vars + 1, eval);
|
||||
let numerator = DenseMultilinearExtension::from_evaluations_vec(num_vars, numerator_eval);
|
||||
let denominator =
|
||||
DenseMultilinearExtension::from_evaluations_vec(num_vars, denominator_eval);
|
||||
|
||||
end_timer!(start);
|
||||
Ok([
|
||||
prod,
|
||||
prod_0x,
|
||||
prod_1x,
|
||||
prod_x0,
|
||||
prod_x1,
|
||||
numerator,
|
||||
denominator,
|
||||
])
|
||||
Ok([fx, numerator, denominator])
|
||||
}
|
||||
|
||||
/// Step 4 of the IOP.
|
||||
/// Update the challenge with alpha; returns an error if
|
||||
/// alpha already exists.
|
||||
fn update_challenge(
|
||||
challenge: &mut Self::PermutationChallenge,
|
||||
transcript: &mut Self::Transcript,
|
||||
prod_x_binding: &impl CanonicalSerialize,
|
||||
) -> Result<(), PolyIOPErrors> {
|
||||
if challenge.alpha.is_some() {
|
||||
return Err(PolyIOPErrors::InvalidChallenge(
|
||||
"alpha should not be sampled at the current stage".to_string(),
|
||||
));
|
||||
}
|
||||
transcript.append_serializable_element(b"prod(x)", prod_x_binding)?;
|
||||
challenge.alpha = Some(transcript.get_and_append_challenge(b"alpha")?);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Step 5 of the IOP.
|
||||
///
|
||||
/// Generate a proof to argue that an MLE g(x) is a permutation of
|
||||
/// Initialize the prover to argue that an MLE g(x) is a permutation of
|
||||
/// MLE f(x) over a permutation given by s_perm.
|
||||
/// Inputs:
|
||||
/// - 7 MLEs from `Self::compute_products(*, f, g, s_perm)`
|
||||
/// - 3 MLEs from the second step
|
||||
/// - challenge: `Self::Challenge` that has been updated
|
||||
/// - transcript: a transcript that is used to generate the challenges beta
|
||||
/// and gamma
|
||||
/// Cost: O(N)
|
||||
fn prove(
|
||||
prod_x_and_aux_info: &[DenseMultilinearExtension<F>; 7],
|
||||
prod_x_and_aux_info: &[DenseMultilinearExtension<F>; 3],
|
||||
challenge: &Self::PermutationChallenge,
|
||||
transcript: &mut IOPTranscript<F>,
|
||||
) -> Result<Self::Proof, PolyIOPErrors> {
|
||||
) -> Result<Self::PermutationProof, PolyIOPErrors> {
|
||||
let alpha = match challenge.alpha {
|
||||
Some(p) => p,
|
||||
None => {
|
||||
@@ -386,7 +372,7 @@ impl<F: PrimeField> PermutationCheck<F> for PolyIOP<F> {
|
||||
/// Verify that an MLE g(x) is a permutation of an
|
||||
/// MLE f(x) over a permutation given by s_perm.
|
||||
fn verify(
|
||||
proof: &Self::Proof,
|
||||
proof: &Self::PermutationProof,
|
||||
aux_info: &Self::VPAuxInfo,
|
||||
transcript: &mut Self::Transcript,
|
||||
) -> Result<Self::PermutationCheckSubClaim, PolyIOPErrors> {
|
||||
@@ -394,7 +380,6 @@ impl<F: PrimeField> PermutationCheck<F> for PolyIOP<F> {
|
||||
|
||||
// invoke the zero check on the iop_proof
|
||||
let zero_check_sub_claim = <Self as ZeroCheck<F>>::verify(proof, aux_info, transcript)?;
|
||||
|
||||
let mut final_query = vec![F::one(); aux_info.num_variables];
|
||||
final_query[aux_info.num_variables - 1] = F::zero();
|
||||
let final_eval = F::one();
|
||||
@@ -413,8 +398,8 @@ impl<F: PrimeField> PermutationCheck<F> for PolyIOP<F> {
|
||||
/// Generate a proof to argue that an MLE g(x) is a permutation of
|
||||
/// MLE f(x) over a permutation given by s_perm.
|
||||
/// Inputs:
|
||||
/// - 7 MLEs from `Self::compute_products(*, f, g, s_perm)`
|
||||
/// - challenge: `Self::Challenge` that has been updated
|
||||
/// - 3 MLEs from the second step
|
||||
/// - challenge alpha
|
||||
/// - transcript: a transcript that is used to generate the challenges beta and
|
||||
/// gamma
|
||||
///
|
||||
@@ -422,26 +407,19 @@ impl<F: PrimeField> PermutationCheck<F> for PolyIOP<F> {
|
||||
///
|
||||
/// Cost: O(N)
|
||||
fn prove_internal<F: PrimeField>(
|
||||
prod_x_and_aux_info: &[DenseMultilinearExtension<F>; 7],
|
||||
prod_x_and_aux_info: &[DenseMultilinearExtension<F>; 3],
|
||||
alpha: &F,
|
||||
transcript: &mut IOPTranscript<F>,
|
||||
) -> Result<(IOPProof<F>, VirtualPolynomial<F>), PolyIOPErrors> {
|
||||
let start = start_timer!(|| "Permutation check prove");
|
||||
|
||||
// prods consists of the following:
|
||||
// - prod(x)
|
||||
// - prod(0, x)
|
||||
// - prod(1, x)
|
||||
// - prod(x, 0)
|
||||
// - prod(x, 1)
|
||||
// - numerator
|
||||
// - denominator
|
||||
let prod_0x = Rc::new(prod_x_and_aux_info[1].clone());
|
||||
let prod_1x = Rc::new(prod_x_and_aux_info[2].clone());
|
||||
let prod_x1 = Rc::new(prod_x_and_aux_info[3].clone());
|
||||
let prod_x0 = Rc::new(prod_x_and_aux_info[4].clone());
|
||||
let numerator = Rc::new(prod_x_and_aux_info[5].clone());
|
||||
let denominator = Rc::new(prod_x_and_aux_info[6].clone());
|
||||
let prod_partial_evals = build_prod_partial_eval(&prod_x_and_aux_info[0])?;
|
||||
let prod_0x = Rc::new(prod_partial_evals[0].clone());
|
||||
let prod_1x = Rc::new(prod_partial_evals[1].clone());
|
||||
let prod_x0 = Rc::new(prod_partial_evals[2].clone());
|
||||
let prod_x1 = Rc::new(prod_partial_evals[3].clone());
|
||||
let numerator = Rc::new(prod_x_and_aux_info[1].clone());
|
||||
let denominator = Rc::new(prod_x_and_aux_info[2].clone());
|
||||
|
||||
// compute (g(x) + beta * s_perm(x) + gamma) * prod(0, x) * alpha
|
||||
// which is prods[6] * prod[1] * alpha
|
||||
@@ -458,6 +436,7 @@ fn prove_internal<F: PrimeField>(
|
||||
// - (f(x) + beta * s_id(x) + gamma))
|
||||
q_x.add_mle_list([prod_x0, prod_x1], -F::one())?;
|
||||
q_x.add_mle_list([prod_1x], F::one())?;
|
||||
|
||||
let iop_proof = <PolyIOP<F> as ZeroCheck<F>>::prove(&q_x, transcript)?;
|
||||
|
||||
end_timer!(start);
|
||||
@@ -466,26 +445,27 @@ fn prove_internal<F: PrimeField>(
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
use super::PermutationCheck;
|
||||
use super::{util::build_prod_partial_eval, PermutationCheck};
|
||||
use crate::{
|
||||
errors::PolyIOPErrors,
|
||||
perm_check::{prove_internal, util::identity_permutation_mle},
|
||||
random_permutation_mle,
|
||||
perm_check::prove_internal,
|
||||
prelude::{identity_permutation_mle, random_permutation_mle},
|
||||
structs::IOPProof,
|
||||
utils::bit_decompose,
|
||||
virtual_poly::VPAuxInfo,
|
||||
PolyIOP, VirtualPolynomial,
|
||||
PolyIOP,
|
||||
};
|
||||
use ark_bls12_381::Fr;
|
||||
use ark_ff::{PrimeField, Zero};
|
||||
use arithmetic::{VPAuxInfo, VirtualPolynomial};
|
||||
use ark_bls12_381::{Fr, G1Affine};
|
||||
use ark_ec::{AffineCurve, ProjectiveCurve};
|
||||
use ark_ff::{UniformRand, Zero};
|
||||
use ark_poly::{DenseMultilinearExtension, MultilinearExtension};
|
||||
use ark_std::test_rng;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
fn mock_commit<F: PrimeField>(_f: &DenseMultilinearExtension<F>) -> F {
|
||||
/// This is a mock function to generate some commitment element for testing.
|
||||
fn mock_commit<G: AffineCurve>(_f: &DenseMultilinearExtension<G::ScalarField>) -> G {
|
||||
let mut rng = test_rng();
|
||||
F::rand(&mut rng)
|
||||
G::Projective::rand(&mut rng).into_affine()
|
||||
}
|
||||
|
||||
fn test_permutation_check_helper(
|
||||
@@ -500,9 +480,9 @@ mod test {
|
||||
<PolyIOP<Fr> as PermutationCheck<Fr>>::generate_challenge(&mut transcript)?;
|
||||
|
||||
let prod_x_and_aux =
|
||||
<PolyIOP<Fr> as PermutationCheck<Fr>>::compute_products(&challenge, f, g, s_perm)?;
|
||||
<PolyIOP<Fr> as PermutationCheck<Fr>>::compute_prod_evals(&challenge, f, g, s_perm)?;
|
||||
|
||||
let prod_x_binding = mock_commit(&prod_x_and_aux[0]);
|
||||
let prod_x_binding: G1Affine = mock_commit(&prod_x_and_aux[0]);
|
||||
|
||||
<PolyIOP<Fr> as PermutationCheck<Fr>>::update_challenge(
|
||||
&mut challenge,
|
||||
@@ -663,9 +643,10 @@ mod test {
|
||||
let challenge =
|
||||
<PolyIOP<Fr> as PermutationCheck<Fr>>::generate_challenge(&mut transcript)?;
|
||||
|
||||
let res = <PolyIOP<Fr> as PermutationCheck<Fr>>::compute_products(
|
||||
let prod_and_aux = <PolyIOP<Fr> as PermutationCheck<Fr>>::compute_prod_evals(
|
||||
&challenge, &f, &g, &s_perm,
|
||||
)?;
|
||||
let prod_partial_eval = build_prod_partial_eval(&prod_and_aux[0])?;
|
||||
|
||||
for i in 0..1 << num_vars {
|
||||
let r: Vec<Fr> = bit_decompose(i, num_vars)
|
||||
@@ -673,7 +654,7 @@ mod test {
|
||||
.map(|&x| Fr::from(x))
|
||||
.collect();
|
||||
|
||||
let eval = res[1].evaluate(&r).unwrap();
|
||||
let eval = prod_partial_eval[0].evaluate(&r).unwrap();
|
||||
|
||||
let f_eval = f.evaluate(&r).unwrap();
|
||||
let g_eval = g.evaluate(&r).unwrap();
|
||||
|
||||
@@ -5,7 +5,7 @@ use ark_ff::PrimeField;
|
||||
use ark_poly::DenseMultilinearExtension;
|
||||
use ark_std::{end_timer, rand::RngCore, start_timer};
|
||||
|
||||
/// Returns three MLEs:
|
||||
/// Returns the evaluations of three MLEs:
|
||||
/// - prod(0,x)
|
||||
/// - numerator
|
||||
/// - denominator
|
||||
@@ -34,14 +34,7 @@ pub(super) fn compute_prod_0<F: PrimeField>(
|
||||
fx: &DenseMultilinearExtension<F>,
|
||||
gx: &DenseMultilinearExtension<F>,
|
||||
s_perm: &DenseMultilinearExtension<F>,
|
||||
) -> Result<
|
||||
(
|
||||
DenseMultilinearExtension<F>,
|
||||
DenseMultilinearExtension<F>,
|
||||
DenseMultilinearExtension<F>,
|
||||
),
|
||||
PolyIOPErrors,
|
||||
> {
|
||||
) -> Result<(Vec<F>, Vec<F>, Vec<F>), PolyIOPErrors> {
|
||||
let start = start_timer!(|| "compute prod(1,x)");
|
||||
|
||||
let num_vars = fx.num_vars;
|
||||
@@ -63,12 +56,8 @@ pub(super) fn compute_prod_0<F: PrimeField>(
|
||||
denominator_evals.push(denominator);
|
||||
}
|
||||
|
||||
let prod_0x = DenseMultilinearExtension::from_evaluations_vec(num_vars, prod_0x_evals);
|
||||
let numerator = DenseMultilinearExtension::from_evaluations_vec(num_vars, numerator_evals);
|
||||
let denominator = DenseMultilinearExtension::from_evaluations_vec(num_vars, denominator_evals);
|
||||
|
||||
end_timer!(start);
|
||||
Ok((prod_0x, numerator, denominator))
|
||||
Ok((prod_0x_evals, numerator_evals, denominator_evals))
|
||||
}
|
||||
|
||||
/// An MLE that represent an identity permutation: `f(index) \mapto index`
|
||||
@@ -92,6 +81,57 @@ pub fn random_permutation_mle<F: PrimeField, R: RngCore>(
|
||||
DenseMultilinearExtension::from_evaluations_vec(num_vars, s_perm_vec)
|
||||
}
|
||||
|
||||
/// Helper function of the IOP.
|
||||
///
|
||||
/// Input:
|
||||
/// - prod(x)
|
||||
///
|
||||
/// Output: the following 4 polynomials
|
||||
/// - prod(0, x)
|
||||
/// - prod(1, x)
|
||||
/// - prod(x, 0)
|
||||
/// - prod(x, 1)
|
||||
pub(super) fn build_prod_partial_eval<F: PrimeField>(
|
||||
prod_x: &DenseMultilinearExtension<F>,
|
||||
) -> Result<[DenseMultilinearExtension<F>; 4], PolyIOPErrors> {
|
||||
let start = start_timer!(|| "build prod polynomial");
|
||||
|
||||
let prod_x_eval = &prod_x.evaluations;
|
||||
let num_vars = prod_x.num_vars - 1;
|
||||
|
||||
// prod(0, x)
|
||||
let prod_0_x =
|
||||
DenseMultilinearExtension::from_evaluations_slice(num_vars, &prod_x_eval[0..1 << num_vars]);
|
||||
// prod(1, x)
|
||||
let prod_1_x = DenseMultilinearExtension::from_evaluations_slice(
|
||||
num_vars,
|
||||
&prod_x_eval[1 << num_vars..1 << (num_vars + 1)],
|
||||
);
|
||||
|
||||
// ===================================
|
||||
// prod(x, 0) and prod(x, 1)
|
||||
// ===================================
|
||||
//
|
||||
// now we compute eval_x0 and eval_x1
|
||||
// eval_0x will be the odd coefficients of eval
|
||||
// and eval_1x will be the even coefficients of eval
|
||||
let mut eval_x0 = vec![];
|
||||
let mut eval_x1 = vec![];
|
||||
for (x, &prod_x) in prod_x_eval.iter().enumerate() {
|
||||
if x & 1 == 0 {
|
||||
eval_x0.push(prod_x);
|
||||
} else {
|
||||
eval_x1.push(prod_x);
|
||||
}
|
||||
}
|
||||
let prod_x_0 = DenseMultilinearExtension::from_evaluations_vec(num_vars, eval_x0);
|
||||
let prod_x_1 = DenseMultilinearExtension::from_evaluations_vec(num_vars, eval_x1);
|
||||
|
||||
end_timer!(start);
|
||||
|
||||
Ok([prod_0_x, prod_1_x, prod_x_0, prod_x_1])
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
@@ -116,6 +156,10 @@ mod test {
|
||||
let gamma = Fr::rand(&mut rng);
|
||||
|
||||
let (prod_0, numerator, denominator) = compute_prod_0(&beta, &gamma, &f, &g, &s_perm)?;
|
||||
let prod_0 = DenseMultilinearExtension::from_evaluations_vec(num_vars, prod_0);
|
||||
let numerator = DenseMultilinearExtension::from_evaluations_vec(num_vars, numerator);
|
||||
let denominator =
|
||||
DenseMultilinearExtension::from_evaluations_vec(num_vars, denominator);
|
||||
|
||||
for i in 0..1 << num_vars {
|
||||
let r: Vec<Fr> = bit_decompose(i, num_vars)
|
||||
|
||||
11
poly-iop/src/prelude.rs
Normal file
11
poly-iop/src/prelude.rs
Normal file
@@ -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,
|
||||
};
|
||||
@@ -1,6 +1,7 @@
|
||||
//! Main module for the Permutation Check protocol
|
||||
|
||||
use crate::{errors::PolyIOPErrors, VirtualPolynomial, ZeroCheck};
|
||||
use crate::{errors::PolyIOPErrors, ZeroCheck};
|
||||
use arithmetic::VirtualPolynomial;
|
||||
use ark_ff::PrimeField;
|
||||
use ark_poly::DenseMultilinearExtension;
|
||||
use transcript::IOPTranscript;
|
||||
@@ -30,6 +31,7 @@ use transcript::IOPTranscript;
|
||||
pub trait ProductCheck<F: PrimeField>: ZeroCheck<F> {
|
||||
type ProductCheckSubClaim;
|
||||
type ProductCheckChallenge;
|
||||
type ProductProof;
|
||||
|
||||
/// Initialize the system with a transcript
|
||||
///
|
||||
@@ -73,12 +75,12 @@ pub trait ProductCheck<F: PrimeField>: ZeroCheck<F> {
|
||||
prod_x: &DenseMultilinearExtension<F>,
|
||||
transcript: &mut IOPTranscript<F>,
|
||||
claimed_product: F,
|
||||
) -> Result<Self::Proof, PolyIOPErrors>;
|
||||
) -> Result<Self::ProductProof, PolyIOPErrors>;
|
||||
|
||||
/// Verify that for a witness virtual polynomial f(x),
|
||||
/// it holds that `s = \prod_{x \in {0,1}^n} f(x)`
|
||||
fn verify(
|
||||
proof: &Self::Proof,
|
||||
proof: &Self::ProductProof,
|
||||
aux_info: &Self::VPAuxInfo,
|
||||
transcript: &mut Self::Transcript,
|
||||
claimed_product: F,
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
//! This module defines structs that are shared by all sub protocols.
|
||||
|
||||
use crate::VirtualPolynomial;
|
||||
use arithmetic::VirtualPolynomial;
|
||||
use ark_ff::PrimeField;
|
||||
use ark_serialize::{CanonicalSerialize, SerializationError, Write};
|
||||
|
||||
/// An IOP proof is a collections of messages from prover to verifier at each
|
||||
/// round through the interactive protocol.
|
||||
#[derive(Clone, Debug, Default, PartialEq, CanonicalSerialize)]
|
||||
/// An IOP proof is a collections of
|
||||
/// - messages from prover to verifier at each round through the interactive
|
||||
/// protocol.
|
||||
/// - a point that is generated by the transcript for evaluation
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub struct IOPProof<F: PrimeField> {
|
||||
pub point: Vec<F>,
|
||||
pub proofs: Vec<IOPProverMessage<F>>,
|
||||
}
|
||||
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
use crate::{
|
||||
errors::PolyIOPErrors,
|
||||
structs::{IOPProof, IOPProverState, IOPVerifierState},
|
||||
virtual_poly::{VPAuxInfo, VirtualPolynomial},
|
||||
PolyIOP,
|
||||
};
|
||||
use arithmetic::{VPAuxInfo, VirtualPolynomial};
|
||||
use ark_ff::PrimeField;
|
||||
use ark_poly::DenseMultilinearExtension;
|
||||
use ark_std::{end_timer, start_timer};
|
||||
@@ -20,12 +20,12 @@ pub trait SumCheck<F: PrimeField> {
|
||||
type VPAuxInfo;
|
||||
type MultilinearExtension;
|
||||
|
||||
type Proof;
|
||||
type SumCheckProof;
|
||||
type Transcript;
|
||||
type SumCheckSubClaim;
|
||||
|
||||
/// Extract sum from the proof
|
||||
fn extract_sum(proof: &Self::Proof) -> F;
|
||||
fn extract_sum(proof: &Self::SumCheckProof) -> F;
|
||||
|
||||
/// Initialize the system with a transcript
|
||||
///
|
||||
@@ -41,12 +41,12 @@ pub trait SumCheck<F: PrimeField> {
|
||||
fn prove(
|
||||
poly: &Self::VirtualPolynomial,
|
||||
transcript: &mut Self::Transcript,
|
||||
) -> Result<Self::Proof, PolyIOPErrors>;
|
||||
) -> Result<Self::SumCheckProof, PolyIOPErrors>;
|
||||
|
||||
/// Verify the claimed sum using the proof
|
||||
fn verify(
|
||||
sum: F,
|
||||
proof: &Self::Proof,
|
||||
proof: &Self::SumCheckProof,
|
||||
aux_info: &Self::VPAuxInfo,
|
||||
transcript: &mut Self::Transcript,
|
||||
) -> Result<Self::SumCheckSubClaim, PolyIOPErrors>;
|
||||
@@ -123,7 +123,7 @@ pub struct SumCheckSubClaim<F: PrimeField> {
|
||||
}
|
||||
|
||||
impl<F: PrimeField> SumCheck<F> for PolyIOP<F> {
|
||||
type Proof = IOPProof<F>;
|
||||
type SumCheckProof = IOPProof<F>;
|
||||
type VirtualPolynomial = VirtualPolynomial<F>;
|
||||
type VPAuxInfo = VPAuxInfo<F>;
|
||||
type MultilinearExtension = DenseMultilinearExtension<F>;
|
||||
@@ -131,7 +131,7 @@ impl<F: PrimeField> SumCheck<F> for PolyIOP<F> {
|
||||
type Transcript = IOPTranscript<F>;
|
||||
|
||||
/// Extract sum from the proof
|
||||
fn extract_sum(proof: &Self::Proof) -> F {
|
||||
fn extract_sum(proof: &Self::SumCheckProof) -> F {
|
||||
let start = start_timer!(|| "extract sum");
|
||||
let res = proof.proofs[0].evaluations[0] + proof.proofs[0].evaluations[1];
|
||||
end_timer!(start);
|
||||
@@ -157,7 +157,7 @@ impl<F: PrimeField> SumCheck<F> for PolyIOP<F> {
|
||||
fn prove(
|
||||
poly: &Self::VirtualPolynomial,
|
||||
transcript: &mut Self::Transcript,
|
||||
) -> Result<Self::Proof, PolyIOPErrors> {
|
||||
) -> Result<Self::SumCheckProof, PolyIOPErrors> {
|
||||
let start = start_timer!(|| "sum check prove");
|
||||
|
||||
transcript.append_serializable_element(b"aux info", &poly.aux_info)?;
|
||||
@@ -172,9 +172,15 @@ impl<F: PrimeField> SumCheck<F> for PolyIOP<F> {
|
||||
prover_msgs.push(prover_msg);
|
||||
challenge = Some(transcript.get_and_append_challenge(b"Internal round")?);
|
||||
}
|
||||
// pushing the last challenge point to the state
|
||||
match challenge {
|
||||
Some(p) => prover_state.challenges.push(p),
|
||||
None => (),
|
||||
};
|
||||
|
||||
end_timer!(start);
|
||||
Ok(IOPProof {
|
||||
point: prover_state.challenges,
|
||||
proofs: prover_msgs,
|
||||
})
|
||||
}
|
||||
@@ -182,7 +188,7 @@ impl<F: PrimeField> SumCheck<F> for PolyIOP<F> {
|
||||
/// Verify the claimed sum using the proof
|
||||
fn verify(
|
||||
claimed_sum: F,
|
||||
proof: &Self::Proof,
|
||||
proof: &Self::SumCheckProof,
|
||||
aux_info: &Self::VPAuxInfo,
|
||||
transcript: &mut Self::Transcript,
|
||||
) -> Result<Self::SumCheckSubClaim, PolyIOPErrors> {
|
||||
@@ -367,7 +373,7 @@ mod test {
|
||||
assert_eq!(poly.flattened_ml_extensions.len(), 5);
|
||||
|
||||
// test memory usage for prover
|
||||
let prover = IOPProverState::prover_init(&poly).unwrap();
|
||||
let prover = IOPProverState::<Fr>::prover_init(&poly).unwrap();
|
||||
assert_eq!(prover.poly.flattened_ml_extensions.len(), 5);
|
||||
drop(prover);
|
||||
|
||||
|
||||
@@ -4,8 +4,8 @@ use super::SumCheckProver;
|
||||
use crate::{
|
||||
errors::PolyIOPErrors,
|
||||
structs::{IOPProverMessage, IOPProverState},
|
||||
virtual_poly::VirtualPolynomial,
|
||||
};
|
||||
use arithmetic::VirtualPolynomial;
|
||||
use ark_ff::PrimeField;
|
||||
use ark_poly::{DenseMultilinearExtension, MultilinearExtension};
|
||||
use ark_std::{end_timer, start_timer, vec::Vec};
|
||||
@@ -60,7 +60,7 @@ impl<F: PrimeField> SumCheckProver<F> for IOPProverState<F> {
|
||||
// for the current round, and m is the round number, indexed from 1
|
||||
//
|
||||
// i.e.:
|
||||
// at round m <=n, for each mle g(x_1, ... x_n) within the flattened_mle
|
||||
// at round m <= n, for each mle g(x_1, ... x_n) within the flattened_mle
|
||||
// which has already been evaluated to
|
||||
//
|
||||
// g(r_1, ..., r_{m-1}, x_m ... x_n)
|
||||
|
||||
@@ -4,8 +4,8 @@ use super::{SumCheckSubClaim, SumCheckVerifier};
|
||||
use crate::{
|
||||
errors::PolyIOPErrors,
|
||||
structs::{IOPProverMessage, IOPVerifierState},
|
||||
virtual_poly::VPAuxInfo,
|
||||
};
|
||||
use arithmetic::VPAuxInfo;
|
||||
use ark_ff::PrimeField;
|
||||
use ark_std::{end_timer, start_timer};
|
||||
use transcript::IOPTranscript;
|
||||
@@ -105,6 +105,12 @@ 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,553 +0,0 @@
|
||||
//! This module defines our main mathematical object `VirtualPolynomial`; and
|
||||
//! various functions associated with it.
|
||||
|
||||
use crate::errors::PolyIOPErrors;
|
||||
use ark_ff::PrimeField;
|
||||
use ark_poly::{DenseMultilinearExtension, MultilinearExtension};
|
||||
use ark_serialize::{CanonicalSerialize, SerializationError, Write};
|
||||
use ark_std::{
|
||||
end_timer,
|
||||
rand::{Rng, RngCore},
|
||||
start_timer,
|
||||
};
|
||||
use std::{cmp::max, collections::HashMap, marker::PhantomData, ops::Add, rc::Rc};
|
||||
|
||||
#[rustfmt::skip]
|
||||
/// A virtual polynomial is a sum of products of multilinear polynomials;
|
||||
/// where the multilinear polynomials are stored via their multilinear
|
||||
/// extensions: `(coefficient, DenseMultilinearExtension)`
|
||||
///
|
||||
/// * Number of products n = `polynomial.products.len()`,
|
||||
/// * Number of multiplicands of ith product m_i =
|
||||
/// `polynomial.products[i].1.len()`,
|
||||
/// * Coefficient of ith product c_i = `polynomial.products[i].0`
|
||||
///
|
||||
/// The resulting polynomial is
|
||||
///
|
||||
/// $$ \sum_{i=0}^{n} c_i \cdot \prod_{j=0}^{m_i} P_{ij} $$
|
||||
///
|
||||
/// Example:
|
||||
/// f = c0 * f0 * f1 * f2 + c1 * f3 * f4
|
||||
/// where f0 ... f4 are multilinear polynomials
|
||||
///
|
||||
/// - flattened_ml_extensions stores the multilinear extension representation of
|
||||
/// f0, f1, f2, f3 and f4
|
||||
/// - products is
|
||||
/// \[
|
||||
/// (c0, \[0, 1, 2\]),
|
||||
/// (c1, \[3, 4\])
|
||||
/// \]
|
||||
/// - raw_pointers_lookup_table maps fi to i
|
||||
///
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub struct VirtualPolynomial<F: PrimeField> {
|
||||
/// Aux information about the multilinear polynomial
|
||||
pub aux_info: VPAuxInfo<F>,
|
||||
/// list of reference to products (as usize) of multilinear extension
|
||||
pub products: Vec<(F, Vec<usize>)>,
|
||||
/// Stores multilinear extensions in which product multiplicand can refer
|
||||
/// to.
|
||||
pub flattened_ml_extensions: Vec<Rc<DenseMultilinearExtension<F>>>,
|
||||
/// Pointers to the above poly extensions
|
||||
raw_pointers_lookup_table: HashMap<*const DenseMultilinearExtension<F>, usize>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, PartialEq, CanonicalSerialize)]
|
||||
/// Auxiliary information about the multilinear polynomial
|
||||
pub struct VPAuxInfo<F: PrimeField> {
|
||||
/// max number of multiplicands in each product
|
||||
pub max_degree: usize,
|
||||
/// number of variables of the polynomial
|
||||
pub num_variables: usize,
|
||||
/// Associated field
|
||||
#[doc(hidden)]
|
||||
pub phantom: PhantomData<F>,
|
||||
}
|
||||
|
||||
impl<F: PrimeField> Add for &VirtualPolynomial<F> {
|
||||
type Output = VirtualPolynomial<F>;
|
||||
fn add(self, other: &VirtualPolynomial<F>) -> Self::Output {
|
||||
let start = start_timer!(|| "virtual poly add");
|
||||
let mut res = self.clone();
|
||||
for products in other.products.iter() {
|
||||
let cur: Vec<Rc<DenseMultilinearExtension<F>>> = products
|
||||
.1
|
||||
.iter()
|
||||
.map(|&x| other.flattened_ml_extensions[x].clone())
|
||||
.collect();
|
||||
|
||||
res.add_mle_list(cur, products.0)
|
||||
.expect("add product failed");
|
||||
}
|
||||
end_timer!(start);
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: PrimeField> VirtualPolynomial<F> {
|
||||
/// Creates an empty virtual polynomial with `num_variables`.
|
||||
pub fn new(num_variables: usize) -> Self {
|
||||
VirtualPolynomial {
|
||||
aux_info: VPAuxInfo {
|
||||
max_degree: 0,
|
||||
num_variables,
|
||||
phantom: PhantomData::default(),
|
||||
},
|
||||
products: Vec::new(),
|
||||
flattened_ml_extensions: Vec::new(),
|
||||
raw_pointers_lookup_table: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates an new virtual polynomial from a MLE and its coefficient.
|
||||
pub fn new_from_mle(mle: Rc<DenseMultilinearExtension<F>>, coefficient: F) -> Self {
|
||||
let mle_ptr: *const DenseMultilinearExtension<F> = Rc::as_ptr(&mle);
|
||||
let mut hm = HashMap::new();
|
||||
hm.insert(mle_ptr, 0);
|
||||
|
||||
VirtualPolynomial {
|
||||
aux_info: VPAuxInfo {
|
||||
// The max degree is the max degree of any individual variable
|
||||
max_degree: 1,
|
||||
num_variables: mle.num_vars,
|
||||
phantom: PhantomData::default(),
|
||||
},
|
||||
// here `0` points to the first polynomial of `flattened_ml_extensions`
|
||||
products: vec![(coefficient, vec![0])],
|
||||
flattened_ml_extensions: vec![mle],
|
||||
raw_pointers_lookup_table: hm,
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a product of list of multilinear extensions to self
|
||||
/// Returns an error if the list is empty, or the MLE has a different
|
||||
/// `num_vars` from self.
|
||||
///
|
||||
/// The MLEs will be multiplied together, and then multiplied by the scalar
|
||||
/// `coefficient`.
|
||||
pub fn add_mle_list(
|
||||
&mut self,
|
||||
mle_list: impl IntoIterator<Item = Rc<DenseMultilinearExtension<F>>>,
|
||||
coefficient: F,
|
||||
) -> Result<(), PolyIOPErrors> {
|
||||
let mle_list: Vec<Rc<DenseMultilinearExtension<F>>> = mle_list.into_iter().collect();
|
||||
let mut indexed_product = Vec::with_capacity(mle_list.len());
|
||||
|
||||
if mle_list.is_empty() {
|
||||
return Err(PolyIOPErrors::InvalidParameters(
|
||||
"input mle_list is empty".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
self.aux_info.max_degree = max(self.aux_info.max_degree, mle_list.len());
|
||||
|
||||
for mle in mle_list {
|
||||
if mle.num_vars != self.aux_info.num_variables {
|
||||
return Err(PolyIOPErrors::InvalidParameters(format!(
|
||||
"product has a multiplicand with wrong number of variables {} vs {}",
|
||||
mle.num_vars, self.aux_info.num_variables
|
||||
)));
|
||||
}
|
||||
|
||||
let mle_ptr: *const DenseMultilinearExtension<F> = Rc::as_ptr(&mle);
|
||||
if let Some(index) = self.raw_pointers_lookup_table.get(&mle_ptr) {
|
||||
indexed_product.push(*index)
|
||||
} else {
|
||||
let curr_index = self.flattened_ml_extensions.len();
|
||||
self.flattened_ml_extensions.push(mle.clone());
|
||||
self.raw_pointers_lookup_table.insert(mle_ptr, curr_index);
|
||||
indexed_product.push(curr_index);
|
||||
}
|
||||
}
|
||||
self.products.push((coefficient, indexed_product));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Multiple the current VirtualPolynomial by an MLE:
|
||||
/// - add the MLE to the MLE list;
|
||||
/// - multiple each product by MLE and its coefficient.
|
||||
/// Returns an error if the MLE has a different `num_vars` from self.
|
||||
pub fn mul_by_mle(
|
||||
&mut self,
|
||||
mle: Rc<DenseMultilinearExtension<F>>,
|
||||
coefficient: F,
|
||||
) -> Result<(), PolyIOPErrors> {
|
||||
let start = start_timer!(|| "mul by mle");
|
||||
|
||||
if mle.num_vars != self.aux_info.num_variables {
|
||||
return Err(PolyIOPErrors::InvalidParameters(format!(
|
||||
"product has a multiplicand with wrong number of variables {} vs {}",
|
||||
mle.num_vars, self.aux_info.num_variables
|
||||
)));
|
||||
}
|
||||
|
||||
let mle_ptr: *const DenseMultilinearExtension<F> = Rc::as_ptr(&mle);
|
||||
|
||||
// check if this mle already exists in the virtual polynomial
|
||||
let mle_index = match self.raw_pointers_lookup_table.get(&mle_ptr) {
|
||||
Some(&p) => p,
|
||||
None => {
|
||||
self.raw_pointers_lookup_table
|
||||
.insert(mle_ptr, self.flattened_ml_extensions.len());
|
||||
self.flattened_ml_extensions.push(mle);
|
||||
self.flattened_ml_extensions.len() - 1
|
||||
},
|
||||
};
|
||||
|
||||
for (prod_coef, indices) in self.products.iter_mut() {
|
||||
// - add the MLE to the MLE list;
|
||||
// - multiple each product by MLE and its coefficient.
|
||||
indices.push(mle_index);
|
||||
*prod_coef *= coefficient;
|
||||
}
|
||||
|
||||
// increase the max degree by one as the MLE has degree 1.
|
||||
self.aux_info.max_degree += 1;
|
||||
end_timer!(start);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Evaluate the virtual polynomial at point `point`.
|
||||
/// Returns an error is point.len() does not match `num_variables`.
|
||||
pub fn evaluate(&self, point: &[F]) -> Result<F, PolyIOPErrors> {
|
||||
let start = start_timer!(|| "evaluation");
|
||||
|
||||
if self.aux_info.num_variables != point.len() {
|
||||
return Err(PolyIOPErrors::InvalidParameters(format!(
|
||||
"wrong number of variables {} vs {}",
|
||||
self.aux_info.num_variables,
|
||||
point.len()
|
||||
)));
|
||||
}
|
||||
|
||||
let evals: Vec<F> = self
|
||||
.flattened_ml_extensions
|
||||
.iter()
|
||||
.map(|x| {
|
||||
x.evaluate(point).unwrap() // safe unwrap here since we have
|
||||
// already checked that num_var
|
||||
// matches
|
||||
})
|
||||
.collect();
|
||||
|
||||
let res = self
|
||||
.products
|
||||
.iter()
|
||||
.map(|(c, p)| *c * p.iter().map(|&i| evals[i]).product::<F>())
|
||||
.sum();
|
||||
|
||||
end_timer!(start);
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// Sample a random virtual polynomial, return the polynomial and its sum.
|
||||
pub fn rand<R: RngCore>(
|
||||
nv: usize,
|
||||
num_multiplicands_range: (usize, usize),
|
||||
num_products: usize,
|
||||
rng: &mut R,
|
||||
) -> Result<(Self, F), PolyIOPErrors> {
|
||||
let start = start_timer!(|| "sample random virtual polynomial");
|
||||
|
||||
let mut sum = F::zero();
|
||||
let mut poly = VirtualPolynomial::new(nv);
|
||||
for _ in 0..num_products {
|
||||
let num_multiplicands =
|
||||
rng.gen_range(num_multiplicands_range.0..num_multiplicands_range.1);
|
||||
let (product, product_sum) = random_mle_list(nv, num_multiplicands, rng);
|
||||
let coefficient = F::rand(rng);
|
||||
poly.add_mle_list(product.into_iter(), coefficient)?;
|
||||
sum += product_sum * coefficient;
|
||||
}
|
||||
|
||||
end_timer!(start);
|
||||
Ok((poly, sum))
|
||||
}
|
||||
|
||||
/// Sample a random virtual polynomial that evaluates to zero everywhere
|
||||
/// over the boolean hypercube.
|
||||
pub fn rand_zero<R: RngCore>(
|
||||
nv: usize,
|
||||
num_multiplicands_range: (usize, usize),
|
||||
num_products: usize,
|
||||
rng: &mut R,
|
||||
) -> Result<Self, PolyIOPErrors> {
|
||||
let mut poly = VirtualPolynomial::new(nv);
|
||||
for _ in 0..num_products {
|
||||
let num_multiplicands =
|
||||
rng.gen_range(num_multiplicands_range.0..num_multiplicands_range.1);
|
||||
let product = random_zero_mle_list(nv, num_multiplicands, rng);
|
||||
let coefficient = F::rand(rng);
|
||||
poly.add_mle_list(product.into_iter(), coefficient)?;
|
||||
}
|
||||
|
||||
Ok(poly)
|
||||
}
|
||||
|
||||
// Input poly f(x) and a random vector r, output
|
||||
// \hat f(x) = \sum_{x_i \in eval_x} f(x_i) eq(x, r)
|
||||
// where
|
||||
// eq(x,y) = \prod_i=1^num_var (x_i * y_i + (1-x_i)*(1-y_i))
|
||||
//
|
||||
// This function is used in ZeroCheck.
|
||||
pub(crate) fn build_f_hat(&self, r: &[F]) -> Result<Self, PolyIOPErrors> {
|
||||
let start = start_timer!(|| "zero check build hat f");
|
||||
|
||||
if self.aux_info.num_variables != r.len() {
|
||||
return Err(PolyIOPErrors::InvalidParameters(format!(
|
||||
"r.len() is different from number of variables: {} vs {}",
|
||||
r.len(),
|
||||
self.aux_info.num_variables
|
||||
)));
|
||||
}
|
||||
|
||||
let eq_x_r = build_eq_x_r(r)?;
|
||||
let mut res = self.clone();
|
||||
res.mul_by_mle(eq_x_r, F::one())?;
|
||||
|
||||
end_timer!(start);
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
/// Sample a random list of multilinear polynomials.
|
||||
/// Returns
|
||||
/// - the list of polynomials,
|
||||
/// - its sum of polynomial evaluations over the boolean hypercube.
|
||||
fn random_mle_list<F: PrimeField, R: RngCore>(
|
||||
nv: usize,
|
||||
degree: usize,
|
||||
rng: &mut R,
|
||||
) -> (Vec<Rc<DenseMultilinearExtension<F>>>, F) {
|
||||
let start = start_timer!(|| "sample random mle list");
|
||||
let mut multiplicands = Vec::with_capacity(degree);
|
||||
for _ in 0..degree {
|
||||
multiplicands.push(Vec::with_capacity(1 << nv))
|
||||
}
|
||||
let mut sum = F::zero();
|
||||
|
||||
for _ in 0..(1 << nv) {
|
||||
let mut product = F::one();
|
||||
|
||||
for e in multiplicands.iter_mut() {
|
||||
let val = F::rand(rng);
|
||||
e.push(val);
|
||||
product *= val;
|
||||
}
|
||||
sum += product;
|
||||
}
|
||||
|
||||
let list = multiplicands
|
||||
.into_iter()
|
||||
.map(|x| Rc::new(DenseMultilinearExtension::from_evaluations_vec(nv, x)))
|
||||
.collect();
|
||||
|
||||
end_timer!(start);
|
||||
(list, sum)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// This function build the eq(x, r) polynomial for any given r.
|
||||
//
|
||||
// Evaluate
|
||||
// eq(x,y) = \prod_i=1^num_var (x_i * y_i + (1-x_i)*(1-y_i))
|
||||
// over r, which is
|
||||
// eq(x,y) = \prod_i=1^num_var (x_i * r_i + (1-x_i)*(1-r_i))
|
||||
fn build_eq_x_r<F: PrimeField>(r: &[F]) -> Result<Rc<DenseMultilinearExtension<F>>, PolyIOPErrors> {
|
||||
let start = start_timer!(|| "zero check build eq_x_r");
|
||||
|
||||
// we build eq(x,r) from its evaluations
|
||||
// we want to evaluate eq(x,r) over x \in {0, 1}^num_vars
|
||||
// for example, with num_vars = 4, x is a binary vector of 4, then
|
||||
// 0 0 0 0 -> (1-r0) * (1-r1) * (1-r2) * (1-r3)
|
||||
// 1 0 0 0 -> r0 * (1-r1) * (1-r2) * (1-r3)
|
||||
// 0 1 0 0 -> (1-r0) * r1 * (1-r2) * (1-r3)
|
||||
// 1 1 0 0 -> r0 * r1 * (1-r2) * (1-r3)
|
||||
// ....
|
||||
// 1 1 1 1 -> r0 * r1 * r2 * r3
|
||||
// we will need 2^num_var evaluations
|
||||
|
||||
let mut eval = Vec::new();
|
||||
build_eq_x_r_helper(r, &mut eval)?;
|
||||
|
||||
let mle = DenseMultilinearExtension::from_evaluations_vec(r.len(), eval);
|
||||
|
||||
let res = Rc::new(mle);
|
||||
end_timer!(start);
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
/// A helper function to build eq(x, r) recursively.
|
||||
/// This function takes `r.len()` steps, and for each step it requires a maximum
|
||||
/// `r.len()-1` multiplications.
|
||||
fn build_eq_x_r_helper<F: PrimeField>(r: &[F], buf: &mut Vec<F>) -> Result<(), PolyIOPErrors> {
|
||||
if r.is_empty() {
|
||||
return Err(PolyIOPErrors::InvalidParameters(
|
||||
"r length is 0".to_string(),
|
||||
));
|
||||
} else if r.len() == 1 {
|
||||
// initializing the buffer with [1-r_0, r_0]
|
||||
buf.push(F::one() - r[0]);
|
||||
buf.push(r[0]);
|
||||
} else {
|
||||
build_eq_x_r_helper(&r[1..], buf)?;
|
||||
|
||||
// suppose at the previous step we received [b_1, ..., b_k]
|
||||
// for the current step we will need
|
||||
// if x_0 = 0: (1-r0) * [b_1, ..., b_k]
|
||||
// if x_0 = 1: r0 * [b_1, ..., b_k]
|
||||
|
||||
let mut res = vec![];
|
||||
for &b_i in buf.iter() {
|
||||
let tmp = r[0] * b_i;
|
||||
res.push(b_i - tmp);
|
||||
res.push(tmp);
|
||||
}
|
||||
*buf = res;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::utils::bit_decompose;
|
||||
use ark_bls12_381::Fr;
|
||||
use ark_ff::UniformRand;
|
||||
use ark_std::test_rng;
|
||||
|
||||
#[test]
|
||||
fn test_virtual_polynomial_additions() -> Result<(), PolyIOPErrors> {
|
||||
let mut rng = test_rng();
|
||||
for nv in 2..5 {
|
||||
for num_products in 2..5 {
|
||||
let base: Vec<Fr> = (0..nv).map(|_| Fr::rand(&mut rng)).collect();
|
||||
|
||||
let (a, _a_sum) =
|
||||
VirtualPolynomial::<Fr>::rand(nv, (2, 3), num_products, &mut rng)?;
|
||||
let (b, _b_sum) =
|
||||
VirtualPolynomial::<Fr>::rand(nv, (2, 3), num_products, &mut rng)?;
|
||||
let c = &a + &b;
|
||||
|
||||
assert_eq!(
|
||||
a.evaluate(base.as_ref())? + b.evaluate(base.as_ref())?,
|
||||
c.evaluate(base.as_ref())?
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_virtual_polynomial_mul_by_mle() -> Result<(), PolyIOPErrors> {
|
||||
let mut rng = test_rng();
|
||||
for nv in 2..5 {
|
||||
for num_products in 2..5 {
|
||||
let base: Vec<Fr> = (0..nv).map(|_| Fr::rand(&mut rng)).collect();
|
||||
|
||||
let (a, _a_sum) =
|
||||
VirtualPolynomial::<Fr>::rand(nv, (2, 3), num_products, &mut rng)?;
|
||||
let (b, _b_sum) = random_mle_list(nv, 1, &mut rng);
|
||||
let b_mle = b[0].clone();
|
||||
let coeff = Fr::rand(&mut rng);
|
||||
let b_vp = VirtualPolynomial::new_from_mle(b_mle.clone(), coeff);
|
||||
|
||||
let mut c = a.clone();
|
||||
|
||||
c.mul_by_mle(b_mle, coeff)?;
|
||||
|
||||
assert_eq!(
|
||||
a.evaluate(base.as_ref())? * b_vp.evaluate(base.as_ref())?,
|
||||
c.evaluate(base.as_ref())?
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_eq_xr() {
|
||||
let mut rng = test_rng();
|
||||
for nv in 4..10 {
|
||||
let r: Vec<Fr> = (0..nv).map(|_| Fr::rand(&mut rng)).collect();
|
||||
let eq_x_r = build_eq_x_r(r.as_ref()).unwrap();
|
||||
let eq_x_r2 = build_eq_x_r_for_test(r.as_ref());
|
||||
assert_eq!(eq_x_r, eq_x_r2);
|
||||
}
|
||||
}
|
||||
|
||||
/// Naive method to build eq(x, r).
|
||||
/// Only used for testing purpose.
|
||||
// Evaluate
|
||||
// eq(x,y) = \prod_i=1^num_var (x_i * y_i + (1-x_i)*(1-y_i))
|
||||
// over r, which is
|
||||
// eq(x,y) = \prod_i=1^num_var (x_i * r_i + (1-x_i)*(1-r_i))
|
||||
fn build_eq_x_r_for_test<F: PrimeField>(r: &[F]) -> Rc<DenseMultilinearExtension<F>> {
|
||||
let start = start_timer!(|| "zero check naive build eq_x_r");
|
||||
|
||||
// we build eq(x,r) from its evaluations
|
||||
// we want to evaluate eq(x,r) over x \in {0, 1}^num_vars
|
||||
// for example, with num_vars = 4, x is a binary vector of 4, then
|
||||
// 0 0 0 0 -> (1-r0) * (1-r1) * (1-r2) * (1-r3)
|
||||
// 1 0 0 0 -> r0 * (1-r1) * (1-r2) * (1-r3)
|
||||
// 0 1 0 0 -> (1-r0) * r1 * (1-r2) * (1-r3)
|
||||
// 1 1 0 0 -> r0 * r1 * (1-r2) * (1-r3)
|
||||
// ....
|
||||
// 1 1 1 1 -> r0 * r1 * r2 * r3
|
||||
// we will need 2^num_var evaluations
|
||||
|
||||
// First, we build array for {1 - r_i}
|
||||
let one_minus_r: Vec<F> = r.iter().map(|ri| F::one() - ri).collect();
|
||||
|
||||
let num_var = r.len();
|
||||
let mut eval = vec![];
|
||||
|
||||
for i in 0..1 << num_var {
|
||||
let mut current_eval = F::one();
|
||||
let bit_sequence = bit_decompose(i, num_var);
|
||||
|
||||
for (&bit, (ri, one_minus_ri)) in
|
||||
bit_sequence.iter().zip(r.iter().zip(one_minus_r.iter()))
|
||||
{
|
||||
current_eval *= if bit { *ri } else { *one_minus_ri };
|
||||
}
|
||||
eval.push(current_eval);
|
||||
}
|
||||
|
||||
let mle = DenseMultilinearExtension::from_evaluations_vec(num_var, eval);
|
||||
|
||||
let res = Rc::new(mle);
|
||||
end_timer!(start);
|
||||
res
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ pub struct ZeroCheckSubClaim<F: PrimeField, SC: SumCheck<F>> {
|
||||
/// A ZeroCheck is derived from SumCheck.
|
||||
pub trait ZeroCheck<F: PrimeField>: SumCheck<F> {
|
||||
type ZeroCheckSubClaim;
|
||||
type ZeroCheckProof;
|
||||
|
||||
/// Initialize the system with a transcript
|
||||
///
|
||||
@@ -33,11 +34,11 @@ pub trait ZeroCheck<F: PrimeField>: SumCheck<F> {
|
||||
fn prove(
|
||||
poly: &Self::VirtualPolynomial,
|
||||
transcript: &mut Self::Transcript,
|
||||
) -> Result<Self::Proof, PolyIOPErrors>;
|
||||
) -> Result<Self::ZeroCheckProof, PolyIOPErrors>;
|
||||
|
||||
/// verify the claimed sum using the proof
|
||||
fn verify(
|
||||
proof: &Self::Proof,
|
||||
proof: &Self::ZeroCheckProof,
|
||||
aux_info: &Self::VPAuxInfo,
|
||||
transcript: &mut Self::Transcript,
|
||||
) -> Result<Self::ZeroCheckSubClaim, PolyIOPErrors>;
|
||||
@@ -48,6 +49,8 @@ impl<F: PrimeField> ZeroCheck<F> for PolyIOP<F> {
|
||||
/// - the SubClaim from the SumCheck
|
||||
/// - the initial challenge r which is used to build eq(x, r)
|
||||
type ZeroCheckSubClaim = ZeroCheckSubClaim<F, Self>;
|
||||
/// A ZeroCheckProof is a SumCheckProof
|
||||
type ZeroCheckProof = Self::SumCheckProof;
|
||||
|
||||
/// Initialize the system with a transcript
|
||||
///
|
||||
@@ -70,11 +73,11 @@ impl<F: PrimeField> ZeroCheck<F> for PolyIOP<F> {
|
||||
fn prove(
|
||||
poly: &Self::VirtualPolynomial,
|
||||
transcript: &mut Self::Transcript,
|
||||
) -> Result<Self::Proof, PolyIOPErrors> {
|
||||
) -> Result<Self::ZeroCheckProof, PolyIOPErrors> {
|
||||
let start = start_timer!(|| "zero check prove");
|
||||
|
||||
let length = poly.aux_info.num_variables;
|
||||
let r = transcript.get_and_append_challenge_vectors(b"vector r", length)?;
|
||||
let r = transcript.get_and_append_challenge_vectors(b"0check r", length)?;
|
||||
let f_hat = poly.build_f_hat(r.as_ref())?;
|
||||
let res = <Self as SumCheck<F>>::prove(&f_hat, transcript);
|
||||
|
||||
@@ -92,7 +95,7 @@ impl<F: PrimeField> ZeroCheck<F> for PolyIOP<F> {
|
||||
/// `\hat f(x)` is build correctly. The caller needs to makes sure that
|
||||
/// `\hat f(x) = f(x) * eq(x, r)`
|
||||
fn verify(
|
||||
proof: &Self::Proof,
|
||||
proof: &Self::ZeroCheckProof,
|
||||
fx_aux_info: &Self::VPAuxInfo,
|
||||
transcript: &mut Self::Transcript,
|
||||
) -> Result<Self::ZeroCheckSubClaim, PolyIOPErrors> {
|
||||
@@ -108,7 +111,7 @@ impl<F: PrimeField> ZeroCheck<F> for PolyIOP<F> {
|
||||
|
||||
// generate `r` and pass it to the caller for correctness check
|
||||
let length = fx_aux_info.num_variables;
|
||||
let r = transcript.get_and_append_challenge_vectors(b"vector r", length)?;
|
||||
let r = transcript.get_and_append_challenge_vectors(b"0check r", length)?;
|
||||
|
||||
// hat_fx's max degree is increased by eq(x, r).degree() which is 1
|
||||
let mut hat_fx_aux_info = fx_aux_info.clone();
|
||||
@@ -128,7 +131,8 @@ impl<F: PrimeField> ZeroCheck<F> for PolyIOP<F> {
|
||||
mod test {
|
||||
|
||||
use super::ZeroCheck;
|
||||
use crate::{errors::PolyIOPErrors, PolyIOP, VirtualPolynomial};
|
||||
use crate::{errors::PolyIOPErrors, PolyIOP};
|
||||
use arithmetic::VirtualPolynomial;
|
||||
use ark_bls12_381::Fr;
|
||||
use ark_std::test_rng;
|
||||
|
||||
@@ -163,7 +167,7 @@ mod test {
|
||||
{
|
||||
// bad path: random virtual poly whose sum is not zero
|
||||
let (poly, _sum) =
|
||||
VirtualPolynomial::rand(nv, num_multiplicands_range, num_products, &mut rng)?;
|
||||
VirtualPolynomial::<Fr>::rand(nv, num_multiplicands_range, num_products, &mut rng)?;
|
||||
|
||||
let mut transcript = <PolyIOP<Fr> as ZeroCheck<Fr>>::init_transcript();
|
||||
transcript.append_message(b"testing", b"initializing transcript for testing")?;
|
||||
|
||||
Reference in New Issue
Block a user