mirror of
https://github.com/arnaucube/hyperplonk.git
synced 2026-01-11 16:41:28 +01:00
Prod check (#61)
This commit is contained in:
@@ -52,7 +52,7 @@ pub struct VirtualPolynomial<F: PrimeField> {
|
|||||||
raw_pointers_lookup_table: HashMap<*const DenseMultilinearExtension<F>, usize>,
|
raw_pointers_lookup_table: HashMap<*const DenseMultilinearExtension<F>, usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, PartialEq, CanonicalSerialize)]
|
#[derive(Clone, Debug, Default, PartialEq, Eq, CanonicalSerialize)]
|
||||||
/// Auxiliary information about the multilinear polynomial
|
/// Auxiliary information about the multilinear polynomial
|
||||||
pub struct VPAuxInfo<F: PrimeField> {
|
pub struct VPAuxInfo<F: PrimeField> {
|
||||||
/// max number of multiplicands in each product
|
/// max number of multiplicands in each product
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ pub struct HyperPlonkVerifyingKey<E: PairingEngine, PCS: PolynomialCommitmentSch
|
|||||||
/// id_w2 = 1 // second witness
|
/// id_w2 = 1 // second witness
|
||||||
///
|
///
|
||||||
/// NOTE: here coeff is a signed integer, instead of a field element
|
/// NOTE: here coeff is a signed integer, instead of a field element
|
||||||
#[derive(Clone, Debug, Default, PartialEq)]
|
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||||
pub struct CustomizedGates {
|
pub struct CustomizedGates {
|
||||||
pub(crate) gates: Vec<(i64, Option<usize>, Vec<usize>)>,
|
pub(crate) gates: Vec<(i64, Option<usize>, Vec<usize>)>,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use ark_ec::PairingEngine;
|
use ark_ec::PairingEngine;
|
||||||
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Read, SerializationError, Write};
|
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize, Read, SerializationError, Write};
|
||||||
|
|
||||||
#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug, Default, PartialEq)]
|
#[derive(CanonicalSerialize, CanonicalDeserialize, Clone, Debug, Default, PartialEq, Eq)]
|
||||||
/// A commitment is an Affine point.
|
/// A commitment is an Affine point.
|
||||||
pub struct Commitment<E: PairingEngine> {
|
pub struct Commitment<E: PairingEngine> {
|
||||||
/// the actual commitment is an affine point.
|
/// the actual commitment is an affine point.
|
||||||
|
|||||||
@@ -6,12 +6,14 @@ edition = "2021"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
pcs = { path = "../pcs" }
|
||||||
|
|
||||||
ark-ff = { version = "^0.3.0", default-features = false }
|
ark-ff = { version = "^0.3.0", default-features = false }
|
||||||
ark-std = { 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-poly = { version = "^0.3.0", default-features = false }
|
||||||
ark-serialize = { 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" ] }
|
ark-bls12-381 = { version = "0.3.0", default-features = false, features = [ "curve" ] }
|
||||||
|
ark-ec = { version = "^0.3.0", default-features = false }
|
||||||
|
|
||||||
rand_chacha = { version = "0.3.0", default-features = false }
|
rand_chacha = { version = "0.3.0", default-features = false }
|
||||||
displaydoc = { version = "0.2.3", default-features = false }
|
displaydoc = { version = "0.2.3", default-features = false }
|
||||||
@@ -37,7 +39,8 @@ parallel = [
|
|||||||
"arithmetic/parallel",
|
"arithmetic/parallel",
|
||||||
"ark-std/parallel",
|
"ark-std/parallel",
|
||||||
"ark-ff/parallel",
|
"ark-ff/parallel",
|
||||||
"ark-poly/parallel"
|
"ark-poly/parallel",
|
||||||
|
"pcs/parallel",
|
||||||
]
|
]
|
||||||
print-trace = [
|
print-trace = [
|
||||||
"arithmetic/print-trace",
|
"arithmetic/print-trace",
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
use arithmetic::ArithErrors;
|
use arithmetic::ArithErrors;
|
||||||
use ark_std::string::String;
|
use ark_std::string::String;
|
||||||
use displaydoc::Display;
|
use displaydoc::Display;
|
||||||
|
use pcs::prelude::PCSErrors;
|
||||||
use transcript::TranscriptErrors;
|
use transcript::TranscriptErrors;
|
||||||
|
|
||||||
/// A `enum` specifying the possible failure modes of the PolyIOP.
|
/// A `enum` specifying the possible failure modes of the PolyIOP.
|
||||||
@@ -26,6 +27,8 @@ pub enum PolyIOPErrors {
|
|||||||
TranscriptErrors(TranscriptErrors),
|
TranscriptErrors(TranscriptErrors),
|
||||||
/// Arithmetic Error: {0}
|
/// Arithmetic Error: {0}
|
||||||
ArithmeticErrors(ArithErrors),
|
ArithmeticErrors(ArithErrors),
|
||||||
|
/// PCS error {0}
|
||||||
|
PCSErrors(PCSErrors),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ark_serialize::SerializationError> for PolyIOPErrors {
|
impl From<ark_serialize::SerializationError> for PolyIOPErrors {
|
||||||
@@ -45,3 +48,9 @@ impl From<ArithErrors> for PolyIOPErrors {
|
|||||||
Self::ArithmeticErrors(e)
|
Self::ArithmeticErrors(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<PCSErrors> for PolyIOPErrors {
|
||||||
|
fn from(e: PCSErrors) -> Self {
|
||||||
|
Self::PCSErrors(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,37 +1,49 @@
|
|||||||
//! Main module for the Permutation Check protocol
|
//! Main module for the Product Check protocol
|
||||||
|
|
||||||
use crate::{errors::PolyIOPErrors, ZeroCheck};
|
use crate::{
|
||||||
use arithmetic::VirtualPolynomial;
|
errors::PolyIOPErrors,
|
||||||
use ark_ff::PrimeField;
|
prod_check::util::{compute_product_poly, prove_zero_check},
|
||||||
|
PolyIOP, ZeroCheck,
|
||||||
|
};
|
||||||
|
use arithmetic::VPAuxInfo;
|
||||||
|
use ark_ec::PairingEngine;
|
||||||
|
use ark_ff::{One, PrimeField, Zero};
|
||||||
use ark_poly::DenseMultilinearExtension;
|
use ark_poly::DenseMultilinearExtension;
|
||||||
|
use ark_std::{end_timer, start_timer};
|
||||||
|
use pcs::prelude::PolynomialCommitmentScheme;
|
||||||
|
use std::{marker::PhantomData, rc::Rc};
|
||||||
use transcript::IOPTranscript;
|
use transcript::IOPTranscript;
|
||||||
|
|
||||||
|
mod util;
|
||||||
|
|
||||||
|
/// A product-check proves that two n-variate multilinear polynomials `f(x),
|
||||||
|
/// g(x)` satisfy:
|
||||||
|
/// \prod_{x \in {0,1}^n} f(x) = \prod_{x \in {0,1}^n} g(x)
|
||||||
|
///
|
||||||
/// A ProductCheck is derived from ZeroCheck.
|
/// A ProductCheck is derived from ZeroCheck.
|
||||||
///
|
///
|
||||||
/// A ProductCheck IOP takes the following steps:
|
|
||||||
///
|
|
||||||
/// Inputs:
|
|
||||||
/// - f(x)
|
|
||||||
///
|
|
||||||
/// Prover steps:
|
/// Prover steps:
|
||||||
/// 1. `compute_product_poly` to build `prod(x0, ..., x_n)` from virtual
|
/// 1. build `prod(x0, ..., x_n)` from f and g,
|
||||||
/// polynomial f
|
/// such that `prod(0, x1, ..., xn)` equals `f/g` over domain {0,1}^n
|
||||||
/// 2. push commitments of `f(x)`, `prod(x)` to the transcript
|
/// 2. push commitments of `prod(x)` to the transcript,
|
||||||
/// (done by the snark caller)
|
/// and `generate_challenge` from current transcript (generate alpha)
|
||||||
/// 3. `generate_challenge` from current transcript (generate alpha)
|
/// 3. generate the zerocheck proof for the virtual polynomial
|
||||||
/// 4. `prove` to generate the zerocheck proof for the virtual polynomial
|
/// prod(1, x) - prod(x, 0) * prod(x, 1) + alpha * (f(x) - prod(0, x) * g(x))
|
||||||
/// prod(1, x) - prod(x, 0) * prod(x, 1) + alpha * (f(x) - prod(0, x))
|
|
||||||
///
|
///
|
||||||
/// Verifier steps:
|
/// Verifier steps:
|
||||||
/// 1. Extract commitments of `f(x)`, `prod(x)` from the proof, push them to the
|
/// 1. Extract commitments of `prod(x)` from the proof, push
|
||||||
/// transcript (done by the snark caller)
|
/// them to the transcript
|
||||||
/// 2. `generate_challenge` from current transcript (generate alpha)
|
/// 2. `generate_challenge` from current transcript (generate alpha)
|
||||||
/// 3. `verify` to verify the zerocheck proof and generate the subclaim for
|
/// 3. `verify` to verify the zerocheck proof and generate the subclaim for
|
||||||
/// polynomial evaluations
|
/// polynomial evaluations
|
||||||
pub trait ProductCheck<F: PrimeField>: ZeroCheck<F> {
|
pub trait ProductCheck<E, PCS>: ZeroCheck<E::Fr>
|
||||||
|
where
|
||||||
|
E: PairingEngine,
|
||||||
|
PCS: PolynomialCommitmentScheme<E>,
|
||||||
|
{
|
||||||
type ProductCheckSubClaim;
|
type ProductCheckSubClaim;
|
||||||
type ProductCheckChallenge;
|
|
||||||
type ProductProof;
|
type ProductProof;
|
||||||
|
type Polynomial;
|
||||||
|
|
||||||
/// Initialize the system with a transcript
|
/// Initialize the system with a transcript
|
||||||
///
|
///
|
||||||
@@ -41,58 +53,44 @@ pub trait ProductCheck<F: PrimeField>: ZeroCheck<F> {
|
|||||||
/// ProductCheck prover/verifier.
|
/// ProductCheck prover/verifier.
|
||||||
fn init_transcript() -> Self::Transcript;
|
fn init_transcript() -> Self::Transcript;
|
||||||
|
|
||||||
/// Generate random challenge `alpha` from a transcript.
|
/// Generate a proof for product check, showing that witness multilinear
|
||||||
fn generate_challenge(
|
/// polynomials f(x), g(x) satisfy `\prod_{x \in {0,1}^n} f(x) =
|
||||||
transcript: &mut Self::Transcript,
|
/// \prod_{x \in {0,1}^n} g(x)`
|
||||||
) -> Result<Self::ProductCheckChallenge, PolyIOPErrors>;
|
|
||||||
|
|
||||||
/// Compute the product polynomial `prod(x)` where
|
|
||||||
///
|
|
||||||
/// - `prod(0,x) := prod(0, x1, …, xn)` is the MLE over the
|
|
||||||
/// evaluations of `f(x)` on the boolean hypercube {0,1}^n
|
|
||||||
///
|
|
||||||
/// - `prod(1,x)` is a MLE over the evaluations of `prod(x, 0) * prod(x, 1)`
|
|
||||||
/// on the boolean hypercube {0,1}^n
|
|
||||||
///
|
|
||||||
/// The caller needs to check num_vars matches in f
|
|
||||||
/// Cost: linear in N.
|
|
||||||
fn compute_product_poly(
|
|
||||||
fx: &VirtualPolynomial<F>,
|
|
||||||
) -> Result<DenseMultilinearExtension<F>, PolyIOPErrors>;
|
|
||||||
|
|
||||||
/// Initialize the prover to argue that for a virtual polynomial f(x),
|
|
||||||
/// it holds that `s = \prod_{x \in {0,1}^n} f(x)`
|
|
||||||
///
|
///
|
||||||
/// Inputs:
|
/// Inputs:
|
||||||
/// - fx: the virtual polynomial
|
/// - fx: the numerator multilinear polynomial
|
||||||
/// - prod_x: the product polynomial
|
/// - gx: the denominator multilinear polynomial
|
||||||
/// - transcript: a transcript that is used to generate the challenges alpha
|
/// - transcript: the IOP transcript
|
||||||
/// - claimed_product: the claimed product value
|
/// - pk: PCS committing key
|
||||||
|
///
|
||||||
|
/// Outputs
|
||||||
|
/// - the product check proof
|
||||||
|
/// - the product polynomial (used for testing)
|
||||||
///
|
///
|
||||||
/// Cost: O(N)
|
/// Cost: O(N)
|
||||||
fn prove(
|
fn prove(
|
||||||
fx: &VirtualPolynomial<F>,
|
fx: &Self::Polynomial,
|
||||||
prod_x: &DenseMultilinearExtension<F>,
|
gx: &Self::Polynomial,
|
||||||
transcript: &mut IOPTranscript<F>,
|
transcript: &mut IOPTranscript<E::Fr>,
|
||||||
claimed_product: F,
|
pk: &PCS::ProverParam,
|
||||||
) -> Result<Self::ProductProof, PolyIOPErrors>;
|
) -> Result<(Self::ProductProof, Self::Polynomial), PolyIOPErrors>;
|
||||||
|
|
||||||
/// Verify that for a witness virtual polynomial f(x),
|
/// Verify that for witness multilinear polynomials f(x), g(x)
|
||||||
/// it holds that `s = \prod_{x \in {0,1}^n} f(x)`
|
/// it holds that `\prod_{x \in {0,1}^n} f(x) = \prod_{x \in {0,1}^n} g(x)`
|
||||||
fn verify(
|
fn verify(
|
||||||
proof: &Self::ProductProof,
|
proof: &Self::ProductProof,
|
||||||
aux_info: &Self::VPAuxInfo,
|
num_vars: usize,
|
||||||
transcript: &mut Self::Transcript,
|
transcript: &mut Self::Transcript,
|
||||||
claimed_product: F,
|
|
||||||
) -> Result<Self::ProductCheckSubClaim, PolyIOPErrors>;
|
) -> Result<Self::ProductCheckSubClaim, PolyIOPErrors>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A product check subclaim consists of
|
/// A product check subclaim consists of
|
||||||
/// - A zero check IOP subclaim for
|
/// - A zero check IOP subclaim for
|
||||||
/// `Q(x) = prod(1, x) - prod(x, 0) * prod(x, 1) + alpha * (f(x) - prod(0, x)`
|
/// `Q(x) = prod(1, x) - prod(x, 0) * prod(x, 1) + challenge * (f(x) - prod(0,
|
||||||
/// is 0, consists of the following:
|
/// x) * g(x))` is 0, consists of the following:
|
||||||
/// - the SubClaim from the SumCheck
|
/// - the SubClaim from the SumCheck
|
||||||
/// - the initial challenge r which is used to build eq(x, r) in ZeroCheck
|
/// - the initial challenge r which is used to build eq(x, r) in ZeroCheck
|
||||||
|
/// - The challenge `challenge`
|
||||||
/// - A final query for `prod(1, ..., 1, 0) = claimed_product`.
|
/// - A final query for `prod(1, ..., 1, 0) = claimed_product`.
|
||||||
// Note that this final query is in fact a constant that
|
// Note that this final query is in fact a constant that
|
||||||
// is independent from the proof. So we should avoid
|
// is independent from the proof. So we should avoid
|
||||||
@@ -102,16 +100,200 @@ pub struct ProductCheckSubClaim<F: PrimeField, ZC: ZeroCheck<F>> {
|
|||||||
// the SubClaim from the ZeroCheck
|
// the SubClaim from the ZeroCheck
|
||||||
zero_check_sub_claim: ZC::ZeroCheckSubClaim,
|
zero_check_sub_claim: ZC::ZeroCheckSubClaim,
|
||||||
// final query which consists of
|
// final query which consists of
|
||||||
// - the vector `(1, ..., 1, 0)`
|
// - the vector `(1, ..., 1, 0)` (needs to be reversed because Arkwork's MLE uses big-endian
|
||||||
// - the evaluation `claimed_product`
|
// format for points)
|
||||||
|
// The expected final query evaluation is 1
|
||||||
final_query: (Vec<F>, F),
|
final_query: (Vec<F>, F),
|
||||||
|
challenge: F,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The random challenges in a product check protocol
|
/// A product check proof consists of
|
||||||
#[allow(dead_code)]
|
/// - a zerocheck proof
|
||||||
pub struct ProductCheckChallenge<F: PrimeField> {
|
/// - a product polynomial commitment
|
||||||
alpha: F,
|
#[derive(Clone, Debug, Default, PartialEq)]
|
||||||
|
pub struct ProductProof<E: PairingEngine, PCS: PolynomialCommitmentScheme<E>, ZC: ZeroCheck<E::Fr>>
|
||||||
|
{
|
||||||
|
zero_check_proof: ZC::ZeroCheckProof,
|
||||||
|
prod_x_comm: PCS::Commitment,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E, PCS> ProductCheck<E, PCS> for PolyIOP<E::Fr>
|
||||||
|
where
|
||||||
|
E: PairingEngine,
|
||||||
|
PCS: PolynomialCommitmentScheme<E, Polynomial = Rc<DenseMultilinearExtension<E::Fr>>>,
|
||||||
|
{
|
||||||
|
type ProductCheckSubClaim = ProductCheckSubClaim<E::Fr, Self>;
|
||||||
|
type ProductProof = ProductProof<E, PCS, Self>;
|
||||||
|
type Polynomial = Rc<DenseMultilinearExtension<E::Fr>>;
|
||||||
|
|
||||||
|
fn init_transcript() -> Self::Transcript {
|
||||||
|
IOPTranscript::<E::Fr>::new(b"Initializing ProductCheck transcript")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn prove(
|
||||||
|
fx: &Self::Polynomial,
|
||||||
|
gx: &Self::Polynomial,
|
||||||
|
transcript: &mut IOPTranscript<E::Fr>,
|
||||||
|
pk: &PCS::ProverParam,
|
||||||
|
) -> Result<(Self::ProductProof, Self::Polynomial), PolyIOPErrors> {
|
||||||
|
let start = start_timer!(|| "prod_check prove");
|
||||||
|
|
||||||
|
if fx.num_vars != gx.num_vars {
|
||||||
|
return Err(PolyIOPErrors::InvalidParameters(
|
||||||
|
"fx and gx have different number of variables".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// compute the product polynomial
|
||||||
|
let prod_x = compute_product_poly(fx, gx)?;
|
||||||
|
|
||||||
|
// generate challenge
|
||||||
|
let prod_x_comm = PCS::commit(pk, &Rc::new(prod_x.clone()))?;
|
||||||
|
transcript.append_serializable_element(b"prod(x)", &prod_x_comm)?;
|
||||||
|
let alpha = transcript.get_and_append_challenge(b"alpha")?;
|
||||||
|
|
||||||
|
// build the zero-check proof
|
||||||
|
let (zero_check_proof, _) = prove_zero_check(fx, gx, &prod_x, &alpha, transcript)?;
|
||||||
|
|
||||||
|
end_timer!(start);
|
||||||
|
|
||||||
|
Ok((
|
||||||
|
ProductProof {
|
||||||
|
zero_check_proof,
|
||||||
|
prod_x_comm,
|
||||||
|
},
|
||||||
|
Rc::new(prod_x.clone()),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify(
|
||||||
|
proof: &Self::ProductProof,
|
||||||
|
num_vars: usize,
|
||||||
|
transcript: &mut Self::Transcript,
|
||||||
|
) -> Result<Self::ProductCheckSubClaim, PolyIOPErrors> {
|
||||||
|
let start = start_timer!(|| "prod_check verify");
|
||||||
|
|
||||||
|
// update transcript and generate challenge
|
||||||
|
transcript.append_serializable_element(b"prod(x)", &proof.prod_x_comm)?;
|
||||||
|
let alpha = transcript.get_and_append_challenge(b"alpha")?;
|
||||||
|
|
||||||
|
// invoke the zero check on the iop_proof
|
||||||
|
// the virtual poly info for Q(x)
|
||||||
|
let aux_info = VPAuxInfo {
|
||||||
|
max_degree: 2,
|
||||||
|
num_variables: num_vars,
|
||||||
|
phantom: PhantomData::default(),
|
||||||
|
};
|
||||||
|
let zero_check_sub_claim =
|
||||||
|
<Self as ZeroCheck<E::Fr>>::verify(&proof.zero_check_proof, &aux_info, transcript)?;
|
||||||
|
|
||||||
|
// the final query is on prod_x, hence has length `num_vars` + 1
|
||||||
|
let mut final_query = vec![E::Fr::one(); aux_info.num_variables + 1];
|
||||||
|
// the point has to be reversed because Arkworks uses big-endian.
|
||||||
|
final_query[0] = E::Fr::zero();
|
||||||
|
let final_eval = E::Fr::one();
|
||||||
|
|
||||||
|
end_timer!(start);
|
||||||
|
|
||||||
|
Ok(ProductCheckSubClaim {
|
||||||
|
zero_check_sub_claim,
|
||||||
|
final_query: (final_query, final_eval),
|
||||||
|
challenge: alpha,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {}
|
mod test {
|
||||||
|
use super::ProductCheck;
|
||||||
|
use crate::{errors::PolyIOPErrors, PolyIOP};
|
||||||
|
use ark_bls12_381::{Bls12_381, Fr};
|
||||||
|
use ark_ec::PairingEngine;
|
||||||
|
use ark_poly::{DenseMultilinearExtension, MultilinearExtension};
|
||||||
|
use ark_std::test_rng;
|
||||||
|
use pcs::{prelude::KZGMultilinearPCS, PolynomialCommitmentScheme};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
// f and g are guaranteed to have the same product
|
||||||
|
fn test_product_check_helper<E, PCS>(
|
||||||
|
f: &DenseMultilinearExtension<E::Fr>,
|
||||||
|
g: &DenseMultilinearExtension<E::Fr>,
|
||||||
|
pk: &PCS::ProverParam,
|
||||||
|
) -> Result<(), PolyIOPErrors>
|
||||||
|
where
|
||||||
|
E: PairingEngine,
|
||||||
|
PCS: PolynomialCommitmentScheme<E, Polynomial = Rc<DenseMultilinearExtension<E::Fr>>>,
|
||||||
|
{
|
||||||
|
let mut transcript = <PolyIOP<E::Fr> as ProductCheck<E, PCS>>::init_transcript();
|
||||||
|
transcript.append_message(b"testing", b"initializing transcript for testing")?;
|
||||||
|
|
||||||
|
let (proof, prod_x) = <PolyIOP<E::Fr> as ProductCheck<E, PCS>>::prove(
|
||||||
|
&Rc::new(f.clone()),
|
||||||
|
&Rc::new(g.clone()),
|
||||||
|
&mut transcript,
|
||||||
|
pk,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let mut transcript = <PolyIOP<E::Fr> as ProductCheck<E, PCS>>::init_transcript();
|
||||||
|
transcript.append_message(b"testing", b"initializing transcript for testing")?;
|
||||||
|
|
||||||
|
let subclaim =
|
||||||
|
<PolyIOP<E::Fr> as ProductCheck<E, PCS>>::verify(&proof, f.num_vars, &mut transcript)?;
|
||||||
|
assert_eq!(
|
||||||
|
prod_x.evaluate(&subclaim.final_query.0).unwrap(),
|
||||||
|
subclaim.final_query.1,
|
||||||
|
"different product"
|
||||||
|
);
|
||||||
|
|
||||||
|
// bad path
|
||||||
|
let mut transcript = <PolyIOP<E::Fr> as ProductCheck<E, PCS>>::init_transcript();
|
||||||
|
transcript.append_message(b"testing", b"initializing transcript for testing")?;
|
||||||
|
|
||||||
|
let h = f + g;
|
||||||
|
let (bad_proof, prod_x_bad) = <PolyIOP<E::Fr> as ProductCheck<E, PCS>>::prove(
|
||||||
|
&Rc::new(f.clone()),
|
||||||
|
&Rc::new(h.clone()),
|
||||||
|
&mut transcript,
|
||||||
|
pk,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let mut transcript = <PolyIOP<E::Fr> as ProductCheck<E, PCS>>::init_transcript();
|
||||||
|
transcript.append_message(b"testing", b"initializing transcript for testing")?;
|
||||||
|
let bad_subclaim = <PolyIOP<E::Fr> as ProductCheck<E, PCS>>::verify(
|
||||||
|
&bad_proof,
|
||||||
|
f.num_vars,
|
||||||
|
&mut transcript,
|
||||||
|
)?;
|
||||||
|
assert_ne!(
|
||||||
|
prod_x_bad.evaluate(&bad_subclaim.final_query.0).unwrap(),
|
||||||
|
bad_subclaim.final_query.1,
|
||||||
|
"can't detect wrong proof"
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_product_check(nv: usize) -> Result<(), PolyIOPErrors> {
|
||||||
|
let mut rng = test_rng();
|
||||||
|
|
||||||
|
let f: DenseMultilinearExtension<Fr> = DenseMultilinearExtension::rand(nv, &mut rng);
|
||||||
|
let mut g = f.clone();
|
||||||
|
g.evaluations.reverse();
|
||||||
|
|
||||||
|
let srs = KZGMultilinearPCS::<Bls12_381>::gen_srs_for_testing(&mut rng, nv + 1)?;
|
||||||
|
let (pk, _) = KZGMultilinearPCS::<Bls12_381>::trim(&srs, nv + 1, Some(nv + 1))?;
|
||||||
|
|
||||||
|
test_product_check_helper::<Bls12_381, KZGMultilinearPCS<Bls12_381>>(&f, &g, &pk)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_trivial_polynomial() -> Result<(), PolyIOPErrors> {
|
||||||
|
test_product_check(1)
|
||||||
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_normal_polynomial() -> Result<(), PolyIOPErrors> {
|
||||||
|
test_product_check(10)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
192
poly-iop/src/prod_check/util.rs
Normal file
192
poly-iop/src/prod_check/util.rs
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
//! This module implements useful functions for the product check protocol.
|
||||||
|
|
||||||
|
use crate::{errors::PolyIOPErrors, structs::IOPProof, utils::get_index, PolyIOP, ZeroCheck};
|
||||||
|
use arithmetic::VirtualPolynomial;
|
||||||
|
use ark_ff::PrimeField;
|
||||||
|
use ark_poly::DenseMultilinearExtension;
|
||||||
|
use ark_std::{end_timer, start_timer};
|
||||||
|
use std::rc::Rc;
|
||||||
|
use transcript::IOPTranscript;
|
||||||
|
|
||||||
|
/// Compute the product polynomial `prod(x)` where
|
||||||
|
///
|
||||||
|
/// - `prod(0,x) := prod(0, x1, …, xn)` is the MLE over the
|
||||||
|
/// evaluations of `f(x)/g(x)` on the boolean hypercube {0,1}^n
|
||||||
|
///
|
||||||
|
/// - `prod(1,x)` is a MLE over the evaluations of `prod(x, 0) * prod(x, 1)`
|
||||||
|
/// on the boolean hypercube {0,1}^n
|
||||||
|
///
|
||||||
|
/// The caller needs to check num_vars matches in f and g
|
||||||
|
/// Cost: linear in N.
|
||||||
|
pub(super) fn compute_product_poly<F: PrimeField>(
|
||||||
|
fx: &DenseMultilinearExtension<F>,
|
||||||
|
gx: &DenseMultilinearExtension<F>,
|
||||||
|
) -> Result<DenseMultilinearExtension<F>, PolyIOPErrors> {
|
||||||
|
let start = start_timer!(|| "compute evaluations of prod polynomial");
|
||||||
|
let num_vars = fx.num_vars;
|
||||||
|
|
||||||
|
// ===================================
|
||||||
|
// prod(0, x)
|
||||||
|
// ===================================
|
||||||
|
let prod_0x_eval = compute_prod_0(fx, gx)?;
|
||||||
|
|
||||||
|
// ===================================
|
||||||
|
// prod(1, x)
|
||||||
|
// ===================================
|
||||||
|
//
|
||||||
|
// `prod(1, x)` can be computed via recursing the following formula for 2^n-1
|
||||||
|
// times
|
||||||
|
//
|
||||||
|
// `prod(1, x_1, ..., x_n) :=
|
||||||
|
// prod(x_1, x_2, ..., x_n, 0) * prod(x_1, x_2, ..., x_n, 1)`
|
||||||
|
//
|
||||||
|
// 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 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 {
|
||||||
|
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 >= prod_1x_eval.len() || x_one_index >= prod_1x_eval.len() {
|
||||||
|
return Err(PolyIOPErrors::ShouldNotArrive);
|
||||||
|
}
|
||||||
|
prod_1x_eval.push(prod_1x_eval[x_zero_index] * prod_1x_eval[x_one_index]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// prod(1, 1, ..., 1) := 0
|
||||||
|
prod_1x_eval.push(F::zero());
|
||||||
|
|
||||||
|
// ===================================
|
||||||
|
// prod(x)
|
||||||
|
// ===================================
|
||||||
|
// prod(x)'s evaluation is indeed `e := [eval_0x[..], eval_1x[..]].concat()`
|
||||||
|
let eval = [prod_0x_eval.as_slice(), prod_1x_eval.as_slice()].concat();
|
||||||
|
|
||||||
|
let prod_x = DenseMultilinearExtension::from_evaluations_vec(num_vars + 1, eval);
|
||||||
|
|
||||||
|
end_timer!(start);
|
||||||
|
Ok(prod_x)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// generate the zerocheck proof for the virtual polynomial
|
||||||
|
/// prod(1, x) - prod(x, 0) * prod(x, 1) + alpha * (f(x) - prod(0, x) * g(x))
|
||||||
|
///
|
||||||
|
/// Returns proof and Q(x) for testing purpose.
|
||||||
|
///
|
||||||
|
/// Cost: O(N)
|
||||||
|
pub(super) fn prove_zero_check<F: PrimeField>(
|
||||||
|
fx: &DenseMultilinearExtension<F>,
|
||||||
|
gx: &DenseMultilinearExtension<F>,
|
||||||
|
prod_x: &DenseMultilinearExtension<F>,
|
||||||
|
alpha: &F,
|
||||||
|
transcript: &mut IOPTranscript<F>,
|
||||||
|
) -> Result<(IOPProof<F>, VirtualPolynomial<F>), PolyIOPErrors> {
|
||||||
|
let start = start_timer!(|| "zerocheck in product check");
|
||||||
|
|
||||||
|
let prod_partial_evals = build_prod_partial_eval(prod_x)?;
|
||||||
|
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 fx = Rc::new(fx.clone());
|
||||||
|
let gx = Rc::new(gx.clone());
|
||||||
|
|
||||||
|
// compute g(x) * prod(0, x) * alpha
|
||||||
|
let mut q_x = VirtualPolynomial::new_from_mle(gx, F::one());
|
||||||
|
q_x.mul_by_mle(prod_0x, *alpha)?;
|
||||||
|
|
||||||
|
// g(x) * prod(0, x) * alpha
|
||||||
|
// - f(x) * alpha
|
||||||
|
q_x.add_mle_list([fx], -*alpha)?;
|
||||||
|
|
||||||
|
// Q(x) := prod(1,x) - prod(x, 0) * prod(x, 1)
|
||||||
|
// + alpha * (
|
||||||
|
// g(x) * prod(0, x)
|
||||||
|
// - f(x))
|
||||||
|
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);
|
||||||
|
Ok((iop_proof, q_x))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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)
|
||||||
|
fn build_prod_partial_eval<F: PrimeField>(
|
||||||
|
prod_x: &DenseMultilinearExtension<F>,
|
||||||
|
) -> Result<[DenseMultilinearExtension<F>; 4], PolyIOPErrors> {
|
||||||
|
let start = start_timer!(|| "build partial 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])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the evaluations of
|
||||||
|
/// - `prod(0,x) := prod(0, x1, …, xn)` which is the MLE over the
|
||||||
|
/// evaluations of f(x)/g(x) on the boolean hypercube {0,1}^n:
|
||||||
|
///
|
||||||
|
/// The caller needs to check num_vars matches in f/g
|
||||||
|
/// Cost: linear in N.
|
||||||
|
fn compute_prod_0<F: PrimeField>(
|
||||||
|
fx: &DenseMultilinearExtension<F>,
|
||||||
|
gx: &DenseMultilinearExtension<F>,
|
||||||
|
) -> Result<Vec<F>, PolyIOPErrors> {
|
||||||
|
let start = start_timer!(|| "compute prod(0,x)");
|
||||||
|
|
||||||
|
let mut prod_0x_evals = vec![];
|
||||||
|
for (&fi, &gi) in fx.iter().zip(gx.iter()) {
|
||||||
|
prod_0x_evals.push(fi / gi);
|
||||||
|
}
|
||||||
|
|
||||||
|
end_timer!(start);
|
||||||
|
Ok(prod_0x_evals)
|
||||||
|
}
|
||||||
@@ -16,7 +16,7 @@ pub struct IOPProof<F: PrimeField> {
|
|||||||
|
|
||||||
/// A message from the prover to the verifier at a given round
|
/// A message from the prover to the verifier at a given round
|
||||||
/// is a list of evaluations.
|
/// is a list of evaluations.
|
||||||
#[derive(Clone, Debug, Default, PartialEq, CanonicalSerialize)]
|
#[derive(Clone, Debug, Default, PartialEq, Eq, CanonicalSerialize)]
|
||||||
pub struct IOPProverMessage<F: PrimeField> {
|
pub struct IOPProverMessage<F: PrimeField> {
|
||||||
pub(crate) evaluations: Vec<F>,
|
pub(crate) evaluations: Vec<F>,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -113,7 +113,7 @@ pub trait SumCheckVerifier<F: PrimeField> {
|
|||||||
|
|
||||||
/// A SumCheckSubClaim is a claim generated by the verifier at the end of
|
/// A SumCheckSubClaim is a claim generated by the verifier at the end of
|
||||||
/// verification when it is convinced.
|
/// verification when it is convinced.
|
||||||
#[derive(Clone, Debug, Default, PartialEq)]
|
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||||
pub struct SumCheckSubClaim<F: PrimeField> {
|
pub struct SumCheckSubClaim<F: PrimeField> {
|
||||||
/// the multi-dimensional point that this multilinear extension is evaluated
|
/// the multi-dimensional point that this multilinear extension is evaluated
|
||||||
/// to
|
/// to
|
||||||
|
|||||||
Reference in New Issue
Block a user