mirror of
https://github.com/arnaucube/hyperplonk.git
synced 2026-01-10 16:11:29 +01:00
refactoring building block PIOPs (#71)
This commit is contained in:
@@ -680,7 +680,7 @@ where
|
||||
&mut transcript,
|
||||
)?;
|
||||
|
||||
let zero_check_point = &zero_check_sub_claim.sum_check_sub_claim.point;
|
||||
let zero_check_point = &zero_check_sub_claim.point;
|
||||
|
||||
// check zero check subclaim
|
||||
let f_eval = eval_f(
|
||||
@@ -718,10 +718,9 @@ where
|
||||
let perm_check_point = &perm_check_sub_claim
|
||||
.product_check_sub_claim
|
||||
.zero_check_sub_claim
|
||||
.sum_check_sub_claim
|
||||
.point;
|
||||
|
||||
let alpha = perm_check_sub_claim.product_check_sub_claim.challenge;
|
||||
let alpha = perm_check_sub_claim.product_check_sub_claim.alpha;
|
||||
let (beta, gamma) = perm_check_sub_claim.challenges;
|
||||
|
||||
// check perm check subclaim:
|
||||
|
||||
@@ -4,7 +4,8 @@ use ark_poly::{DenseMultilinearExtension, MultilinearExtension};
|
||||
use ark_std::test_rng;
|
||||
use pcs::{prelude::KZGMultilinearPCS, PolynomialCommitmentScheme};
|
||||
use poly_iop::prelude::{
|
||||
identity_permutation_mle, PermutationCheck, PolyIOP, PolyIOPErrors, SumCheck, ZeroCheck,
|
||||
identity_permutation_mle, PermutationCheck, PolyIOP, PolyIOPErrors, ProductCheck, SumCheck,
|
||||
ZeroCheck,
|
||||
};
|
||||
use std::{marker::PhantomData, rc::Rc, time::Instant};
|
||||
|
||||
@@ -15,6 +16,8 @@ fn main() -> Result<(), PolyIOPErrors> {
|
||||
println!("\n\n");
|
||||
bench_sum_check()?;
|
||||
println!("\n\n");
|
||||
bench_prod_check()?;
|
||||
println!("\n\n");
|
||||
bench_zero_check()
|
||||
}
|
||||
|
||||
@@ -113,11 +116,10 @@ fn bench_zero_check() -> Result<(), PolyIOPErrors> {
|
||||
let start = Instant::now();
|
||||
let mut transcript = <PolyIOP<Fr> as ZeroCheck<Fr>>::init_transcript();
|
||||
transcript.append_message(b"testing", b"initializing transcript for testing")?;
|
||||
let subclaim =
|
||||
<PolyIOP<Fr> as ZeroCheck<Fr>>::verify(&proof, &poly_info, &mut transcript)?
|
||||
.sum_check_sub_claim;
|
||||
let zero_subclaim =
|
||||
<PolyIOP<Fr> as ZeroCheck<Fr>>::verify(&proof, &poly_info, &mut transcript)?;
|
||||
assert!(
|
||||
poly.evaluate(&subclaim.point)? == subclaim.expected_evaluation,
|
||||
poly.evaluate(&zero_subclaim.point)? == zero_subclaim.expected_evaluation,
|
||||
"wrong subclaim"
|
||||
);
|
||||
println!(
|
||||
@@ -204,3 +206,72 @@ fn bench_permutation_check() -> Result<(), PolyIOPErrors> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn bench_prod_check() -> Result<(), PolyIOPErrors> {
|
||||
let mut rng = test_rng();
|
||||
|
||||
for nv in 4..20 {
|
||||
let srs = KZG::gen_srs_for_testing(&mut rng, nv + 1)?;
|
||||
let (pcs_param, _) = KZG::trim(&srs, nv + 1, Some(nv + 1))?;
|
||||
|
||||
let repetition = if nv < 10 {
|
||||
100
|
||||
} else if nv < 20 {
|
||||
50
|
||||
} else {
|
||||
10
|
||||
};
|
||||
|
||||
let f: DenseMultilinearExtension<Fr> = DenseMultilinearExtension::rand(nv, &mut rng);
|
||||
let mut g = f.clone();
|
||||
g.evaluations.reverse();
|
||||
let f = Rc::new(f);
|
||||
let g = Rc::new(g);
|
||||
|
||||
let proof = {
|
||||
let start = Instant::now();
|
||||
let mut transcript = <PolyIOP<Fr> as ProductCheck<Bls12_381, KZG>>::init_transcript();
|
||||
transcript.append_message(b"testing", b"initializing transcript for testing")?;
|
||||
|
||||
let (proof, _prod_x) = <PolyIOP<Fr> as ProductCheck<Bls12_381, KZG>>::prove(
|
||||
&pcs_param,
|
||||
&f,
|
||||
&g,
|
||||
&mut transcript,
|
||||
)?;
|
||||
|
||||
println!(
|
||||
"product check proving time for {} variables: {} ns",
|
||||
nv,
|
||||
start.elapsed().as_nanos() / repetition as u128
|
||||
);
|
||||
proof
|
||||
};
|
||||
|
||||
{
|
||||
let poly_info = VPAuxInfo {
|
||||
max_degree: 2,
|
||||
num_variables: nv,
|
||||
phantom: PhantomData::default(),
|
||||
};
|
||||
|
||||
let start = Instant::now();
|
||||
let mut transcript = <PolyIOP<Fr> as ProductCheck<Bls12_381, KZG>>::init_transcript();
|
||||
transcript.append_message(b"testing", b"initializing transcript for testing")?;
|
||||
let _perm_check_sum_claim = <PolyIOP<Fr> as ProductCheck<Bls12_381, KZG>>::verify(
|
||||
&proof,
|
||||
&poly_info,
|
||||
&mut transcript,
|
||||
)?;
|
||||
println!(
|
||||
"product check verification time for {} variables: {} ns",
|
||||
nv,
|
||||
start.elapsed().as_nanos() / repetition as u128
|
||||
);
|
||||
}
|
||||
|
||||
println!("====================================");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -27,7 +27,10 @@ where
|
||||
|
||||
pub mod util;
|
||||
|
||||
/// A PermutationCheck is derived from ZeroCheck.
|
||||
/// A PermutationCheck w.r.t. `(f, g, perm)`
|
||||
/// proves that g is a permutation of f under
|
||||
/// permutation `perm`
|
||||
/// It is derived from ProductCheck.
|
||||
///
|
||||
/// A Permutation Check IOP takes the following steps:
|
||||
///
|
||||
@@ -58,7 +61,7 @@ where
|
||||
/// Outputs:
|
||||
/// - a permutation check proof proving that g is a permutation of f under
|
||||
/// s_perm
|
||||
/// - the Q(x) polynomial build during product check
|
||||
/// - the product polynomial build during product check
|
||||
///
|
||||
/// Cost: O(N)
|
||||
fn prove(
|
||||
@@ -78,53 +81,18 @@ where
|
||||
) -> Result<Self::PermutationCheckSubClaim, PolyIOPErrors>;
|
||||
}
|
||||
|
||||
/// A PermutationCheck is derived from ZeroCheck.
|
||||
///
|
||||
/// A Permutation Check IOP takes the following steps:
|
||||
///
|
||||
/// Inputs:
|
||||
/// - f(x)
|
||||
/// - g(x)
|
||||
/// - permutation s_perm(x)
|
||||
///
|
||||
/// Steps:
|
||||
/// 1. `generate_challenge` from current transcript (generate beta, gamma)
|
||||
/// 2. `compute_product` to build `prod(x)` etc. from f, g and s_perm
|
||||
/// 3. push a commitment of `prod(x)` to the transcript (done by the snark
|
||||
/// caller)
|
||||
/// 4. `update_challenge` with the updated transcript (generate alpha)
|
||||
/// 5. `prove` to generate the proof
|
||||
impl<E, PCS> PermutationCheck<E, PCS> for PolyIOP<E::Fr>
|
||||
where
|
||||
E: PairingEngine,
|
||||
PCS: PolynomialCommitmentScheme<E, Polynomial = Rc<DenseMultilinearExtension<E::Fr>>>,
|
||||
{
|
||||
/// A Permutation SubClaim is indeed a ZeroCheck SubClaim that consists of
|
||||
/// - the SubClaim from the SumCheck
|
||||
/// - the initial challenge r which is used to build eq(x, r)
|
||||
type PermutationCheckSubClaim = PermutationCheckSubClaim<E, PCS, Self>;
|
||||
type PermutationProof = Self::ProductCheckProof;
|
||||
|
||||
/// Initialize the system with a transcript
|
||||
///
|
||||
/// This function is optional -- in the case where a PermutationCheck is
|
||||
/// an building block for a more complex protocol, the transcript
|
||||
/// may be initialized by this complex protocol, and passed to the
|
||||
/// PermutationCheck prover/verifier.
|
||||
fn init_transcript() -> Self::Transcript {
|
||||
IOPTranscript::<E::Fr>::new(b"Initializing PermutationCheck transcript")
|
||||
}
|
||||
|
||||
/// Inputs:
|
||||
/// - f(x)
|
||||
/// - g(x)
|
||||
/// - permutation s_perm(x)
|
||||
/// Outputs:
|
||||
/// - a permutation check proof proving that g is a permutation of f under
|
||||
/// s_perm
|
||||
/// - the Q(x) polynomial build during product check
|
||||
///
|
||||
/// Cost: O(N)
|
||||
fn prove(
|
||||
pcs_param: &PCS::ProverParam,
|
||||
fx: &Self::MultilinearExtension,
|
||||
@@ -151,11 +119,11 @@ where
|
||||
let (numerator, denominator) = computer_num_and_denom(&beta, &gamma, fx, gx, s_perm)?;
|
||||
|
||||
// invoke product check on numerator and denominator
|
||||
let (proof, poly) =
|
||||
let (proof, prod_poly) =
|
||||
<Self as ProductCheck<E, PCS>>::prove(pcs_param, &numerator, &denominator, transcript)?;
|
||||
|
||||
end_timer!(start);
|
||||
Ok((proof, poly))
|
||||
Ok((proof, prod_poly))
|
||||
}
|
||||
|
||||
/// Verify that an MLE g(x) is a permutation of an
|
||||
@@ -184,18 +152,15 @@ where
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::{util::build_prod_partial_eval, PermutationCheck};
|
||||
use super::PermutationCheck;
|
||||
use crate::{
|
||||
errors::PolyIOPErrors,
|
||||
perm_check::util::computer_num_and_denom,
|
||||
prelude::{identity_permutation_mle, random_permutation_mle},
|
||||
utils::bit_decompose,
|
||||
PolyIOP,
|
||||
};
|
||||
use arithmetic::{VPAuxInfo, VirtualPolynomial};
|
||||
use arithmetic::VPAuxInfo;
|
||||
use ark_bls12_381::Bls12_381;
|
||||
use ark_ec::PairingEngine;
|
||||
use ark_ff::{One, Zero};
|
||||
use ark_poly::{DenseMultilinearExtension, MultilinearExtension};
|
||||
use ark_std::test_rng;
|
||||
use pcs::{prelude::KZGMultilinearPCS, PolynomialCommitmentScheme};
|
||||
@@ -223,7 +188,7 @@ mod test {
|
||||
// prover
|
||||
let mut transcript = <PolyIOP<E::Fr> as PermutationCheck<E, PCS>>::init_transcript();
|
||||
transcript.append_message(b"testing", b"initializing transcript for testing")?;
|
||||
let (proof, q_x) = <PolyIOP<E::Fr> as PermutationCheck<E, PCS>>::prove(
|
||||
let (proof, prod_x) = <PolyIOP<E::Fr> as PermutationCheck<E, PCS>>::prove(
|
||||
pcs_param,
|
||||
fx,
|
||||
gx,
|
||||
@@ -234,76 +199,21 @@ mod test {
|
||||
// verifier
|
||||
let mut transcript = <PolyIOP<E::Fr> as PermutationCheck<E, PCS>>::init_transcript();
|
||||
transcript.append_message(b"testing", b"initializing transcript for testing")?;
|
||||
let perm_check_sum_claim = <PolyIOP<E::Fr> as PermutationCheck<E, PCS>>::verify(
|
||||
let perm_check_sub_claim = <PolyIOP<E::Fr> as PermutationCheck<E, PCS>>::verify(
|
||||
&proof,
|
||||
&poly_info,
|
||||
&mut transcript,
|
||||
)?;
|
||||
|
||||
let prod_partial_evals = build_prod_partial_eval(&q_x)?;
|
||||
let prod_0x = prod_partial_evals[0].clone();
|
||||
let prod_1x = prod_partial_evals[1].clone();
|
||||
let prod_x0 = prod_partial_evals[2].clone();
|
||||
let prod_x1 = prod_partial_evals[3].clone();
|
||||
|
||||
let (numerator, denominator) = computer_num_and_denom(
|
||||
&perm_check_sum_claim.challenges.0,
|
||||
&perm_check_sum_claim.challenges.1,
|
||||
fx,
|
||||
gx,
|
||||
&s_perm,
|
||||
)?;
|
||||
|
||||
// compute (g(x) + beta * s_perm(x) + gamma) * prod(0, x) * alpha
|
||||
// which is prods[6] * prod[1] * alpha
|
||||
let mut q_x = VirtualPolynomial::new_from_mle(&denominator, E::Fr::one());
|
||||
q_x.mul_by_mle(
|
||||
prod_0x,
|
||||
perm_check_sum_claim.product_check_sub_claim.challenge,
|
||||
)?;
|
||||
|
||||
// (g(x) + beta * s_perm(x) + gamma) * prod(0, x) * alpha
|
||||
// - (f(x) + beta * s_id(x) + gamma) * alpha
|
||||
q_x.add_mle_list(
|
||||
[numerator],
|
||||
-perm_check_sum_claim.product_check_sub_claim.challenge,
|
||||
)?;
|
||||
|
||||
// Q(x) := prod(1,x) - prod(x, 0) * prod(x, 1)
|
||||
// + alpha * (
|
||||
// (g(x) + beta * s_perm(x) + gamma) * prod(0, x)
|
||||
// - (f(x) + beta * s_id(x) + gamma))
|
||||
q_x.add_mle_list([prod_x0, prod_x1], -E::Fr::one())?;
|
||||
q_x.add_mle_list([prod_1x], E::Fr::one())?;
|
||||
|
||||
if q_x
|
||||
.evaluate(
|
||||
&perm_check_sum_claim
|
||||
.product_check_sub_claim
|
||||
.zero_check_sub_claim
|
||||
.sum_check_sub_claim
|
||||
.point,
|
||||
)
|
||||
// check product subclaim
|
||||
if prod_x
|
||||
.evaluate(&perm_check_sub_claim.product_check_sub_claim.final_query.0)
|
||||
.unwrap()
|
||||
!= perm_check_sum_claim
|
||||
.product_check_sub_claim
|
||||
.zero_check_sub_claim
|
||||
.sum_check_sub_claim
|
||||
.expected_evaluation
|
||||
!= perm_check_sub_claim.product_check_sub_claim.final_query.1
|
||||
{
|
||||
return Err(PolyIOPErrors::InvalidVerifier("wrong subclaim".to_string()));
|
||||
};
|
||||
|
||||
// test q_x is a 0 over boolean hypercube
|
||||
for i in 0..1 << nv {
|
||||
let bit_sequence = bit_decompose(i, nv);
|
||||
let eval: Vec<E::Fr> = bit_sequence
|
||||
.iter()
|
||||
.map(|x| E::Fr::from(*x as u64))
|
||||
.collect();
|
||||
let res = q_x.evaluate(&eval).unwrap();
|
||||
if !res.is_zero() {}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -6,60 +6,6 @@ use ark_poly::DenseMultilinearExtension;
|
||||
use ark_std::{end_timer, rand::RngCore, start_timer};
|
||||
use std::rc::Rc;
|
||||
|
||||
/// Returns the evaluations of three MLEs:
|
||||
/// - prod(0,x)
|
||||
/// - numerator
|
||||
/// - denominator
|
||||
///
|
||||
/// where
|
||||
/// - `prod(0,x) := prod(0, x1, …, xn)` which is the MLE over the
|
||||
/// evaluations of the following polynomial on the boolean hypercube {0,1}^n:
|
||||
///
|
||||
/// (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
|
||||
///
|
||||
/// - numerator is the MLE for `f(x) + \beta s_id(x) + \gamma`
|
||||
/// - denominator is the MLE for `g(x) + \beta s_perm(x) + \gamma`
|
||||
///
|
||||
/// The caller needs to check num_vars matches in f/g/s_id/s_perm
|
||||
/// Cost: linear in N.
|
||||
#[cfg(test)]
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub(super) fn compute_prod_0<F: PrimeField>(
|
||||
beta: &F,
|
||||
gamma: &F,
|
||||
fx: &DenseMultilinearExtension<F>,
|
||||
gx: &DenseMultilinearExtension<F>,
|
||||
s_perm: &DenseMultilinearExtension<F>,
|
||||
) -> Result<(Vec<F>, Vec<F>, Vec<F>), PolyIOPErrors> {
|
||||
let start = start_timer!(|| "compute prod(1,x)");
|
||||
|
||||
let num_vars = fx.num_vars;
|
||||
let mut prod_0x_evals = vec![];
|
||||
let mut numerator_evals = vec![];
|
||||
let mut denominator_evals = vec![];
|
||||
|
||||
// TODO: remove this line after replacing `s_perm` with `s`.
|
||||
let s_id = identity_permutation_mle::<F>(num_vars);
|
||||
|
||||
for (&fi, (&gi, (&s_id_i, &s_perm_i))) in
|
||||
fx.iter().zip(gx.iter().zip(s_id.iter().zip(s_perm.iter())))
|
||||
{
|
||||
let numerator = fi + *beta * s_id_i + gamma;
|
||||
let denominator = gi + *beta * s_perm_i + gamma;
|
||||
|
||||
prod_0x_evals.push(numerator / denominator);
|
||||
numerator_evals.push(numerator);
|
||||
denominator_evals.push(denominator);
|
||||
}
|
||||
|
||||
end_timer!(start);
|
||||
Ok((prod_0x_evals, numerator_evals, denominator_evals))
|
||||
}
|
||||
|
||||
/// Returns the evaluations of two MLEs:
|
||||
/// - numerator
|
||||
/// - denominator
|
||||
@@ -139,118 +85,3 @@ pub fn random_permutation_mle<F: PrimeField, R: RngCore>(
|
||||
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)
|
||||
#[cfg(test)]
|
||||
pub(super) fn build_prod_partial_eval<F: PrimeField>(
|
||||
prod_x: &Rc<DenseMultilinearExtension<F>>,
|
||||
) -> Result<[Rc<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 = Rc::new(DenseMultilinearExtension::from_evaluations_slice(
|
||||
num_vars,
|
||||
&prod_x_eval[0..1 << num_vars],
|
||||
));
|
||||
// prod(1, x)
|
||||
let prod_1_x = Rc::new(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 = Rc::new(DenseMultilinearExtension::from_evaluations_vec(
|
||||
num_vars, eval_x0,
|
||||
));
|
||||
let prod_x_1 = Rc::new(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::*;
|
||||
use crate::utils::bit_decompose;
|
||||
use ark_bls12_381::Fr;
|
||||
use ark_ff::UniformRand;
|
||||
use ark_poly::MultilinearExtension;
|
||||
use ark_std::test_rng;
|
||||
|
||||
#[test]
|
||||
fn test_compute_prod_0() -> Result<(), PolyIOPErrors> {
|
||||
let mut rng = test_rng();
|
||||
|
||||
for num_vars in 2..6 {
|
||||
let f = DenseMultilinearExtension::rand(num_vars, &mut rng);
|
||||
let g = DenseMultilinearExtension::rand(num_vars, &mut rng);
|
||||
|
||||
let s_id = identity_permutation_mle::<Fr>(num_vars);
|
||||
let s_perm = random_permutation_mle(num_vars, &mut rng);
|
||||
|
||||
let beta = Fr::rand(&mut rng);
|
||||
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)
|
||||
.iter()
|
||||
.map(|&x| Fr::from(x))
|
||||
.collect();
|
||||
|
||||
let prod_0_eval = prod_0.evaluate(&r).unwrap();
|
||||
let numerator_eval = numerator.evaluate(&r).unwrap();
|
||||
let denominator_eval = denominator.evaluate(&r).unwrap();
|
||||
|
||||
let f_eval = f.evaluate(&r).unwrap();
|
||||
let g_eval = g.evaluate(&r).unwrap();
|
||||
let s_id_eval = s_id.evaluate(&r).unwrap();
|
||||
let s_perm_eval = s_perm.evaluate(&r).unwrap();
|
||||
|
||||
let numerator_eval_rec = f_eval + beta * s_id_eval + gamma;
|
||||
let denominator_eval_rec = g_eval + beta * s_perm_eval + gamma;
|
||||
let prod_0_eval_rec = numerator_eval_rec / denominator_eval_rec;
|
||||
|
||||
assert_eq!(numerator_eval, numerator_eval_rec);
|
||||
assert_eq!(denominator_eval, denominator_eval_rec);
|
||||
assert_eq!(prod_0_eval, prod_0_eval_rec);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,12 +86,10 @@ where
|
||||
|
||||
/// A product check subclaim consists of
|
||||
/// - A zero check IOP subclaim for
|
||||
/// `Q(x) = prod(1, x) - prod(x, 0) * prod(x, 1) + challenge * (f(x) - prod(0,
|
||||
/// x) * g(x))` is 0, consists of the following:
|
||||
/// - the SubClaim from the SumCheck
|
||||
/// - 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`.
|
||||
/// `Q(x) = prod(1, x) - prod(x, 0) * prod(x, 1) + alpha * (f(x) - prod(0,
|
||||
/// x) * g(x)) = 0`
|
||||
/// - The random challenge `alpha`
|
||||
/// - A final query for `prod(1, ..., 1, 0) = 1`.
|
||||
// Note that this final query is in fact a constant that
|
||||
// is independent from the proof. So we should avoid
|
||||
// (de)serialize it.
|
||||
@@ -103,8 +101,8 @@ pub struct ProductCheckSubClaim<F: PrimeField, ZC: ZeroCheck<F>> {
|
||||
// - the vector `(1, ..., 1, 0)` (needs to be reversed because Arkwork's MLE uses big-endian
|
||||
// format for points)
|
||||
// The expected final query evaluation is 1
|
||||
final_query: (Vec<F>, F),
|
||||
pub challenge: F,
|
||||
pub final_query: (Vec<F>, F),
|
||||
pub alpha: F,
|
||||
}
|
||||
|
||||
/// A product check proof consists of
|
||||
@@ -195,7 +193,7 @@ where
|
||||
Ok(ProductCheckSubClaim {
|
||||
zero_check_sub_claim,
|
||||
final_query: (final_query, final_eval),
|
||||
challenge: alpha,
|
||||
alpha,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -240,11 +238,11 @@ mod test {
|
||||
num_variables: f.num_vars,
|
||||
phantom: PhantomData::default(),
|
||||
};
|
||||
let subclaim =
|
||||
let prod_subclaim =
|
||||
<PolyIOP<E::Fr> as ProductCheck<E, PCS>>::verify(&proof, &aux_info, &mut transcript)?;
|
||||
assert_eq!(
|
||||
prod_x.evaluate(&subclaim.final_query.0).unwrap(),
|
||||
subclaim.final_query.1,
|
||||
prod_x.evaluate(&prod_subclaim.final_query.0).unwrap(),
|
||||
prod_subclaim.final_query.1,
|
||||
"different product"
|
||||
);
|
||||
|
||||
|
||||
@@ -131,7 +131,6 @@ impl<F: PrimeField> SumCheck<F> for PolyIOP<F> {
|
||||
type SumCheckSubClaim = SumCheckSubClaim<F>;
|
||||
type Transcript = IOPTranscript<F>;
|
||||
|
||||
/// Extract sum from the proof
|
||||
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];
|
||||
@@ -139,12 +138,6 @@ impl<F: PrimeField> SumCheck<F> for PolyIOP<F> {
|
||||
res
|
||||
}
|
||||
|
||||
/// Initialize the system with a transcript
|
||||
///
|
||||
/// This function is optional -- in the case where a SumCheck is
|
||||
/// an building block for a more complex protocol, the transcript
|
||||
/// may be initialized by this complex protocol, and passed to the
|
||||
/// SumCheck prover/verifier.
|
||||
fn init_transcript() -> Self::Transcript {
|
||||
let start = start_timer!(|| "init transcript");
|
||||
let res = IOPTranscript::<F>::new(b"Initializing SumCheck transcript");
|
||||
@@ -152,9 +145,6 @@ impl<F: PrimeField> SumCheck<F> for PolyIOP<F> {
|
||||
res
|
||||
}
|
||||
|
||||
/// Generate proof of the sum of polynomial over {0,1}^`num_vars`
|
||||
///
|
||||
/// The polynomial is represented in the form of a VirtualPolynomial.
|
||||
fn prove(
|
||||
poly: &Self::VirtualPolynomial,
|
||||
transcript: &mut Self::Transcript,
|
||||
@@ -185,7 +175,6 @@ impl<F: PrimeField> SumCheck<F> for PolyIOP<F> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Verify the claimed sum using the proof
|
||||
fn verify(
|
||||
claimed_sum: F,
|
||||
proof: &Self::SumCheckProof,
|
||||
|
||||
@@ -9,20 +9,23 @@ use ark_poly::MultilinearExtension;
|
||||
use ark_std::{end_timer, start_timer};
|
||||
use transcript::IOPTranscript;
|
||||
|
||||
/// A zero check IOP subclaim for \hat f(x) is 0, consists of the following:
|
||||
/// - the SubClaim from the SumCheck
|
||||
/// - the initial challenge r which is used to build eq(x, r) in ZeroCheck
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
pub struct ZeroCheckSubClaim<F: PrimeField, SC: SumCheck<F>> {
|
||||
// the SubClaim from the SumCheck
|
||||
pub sum_check_sub_claim: SC::SumCheckSubClaim,
|
||||
/// A zero check IOP subclaim for `f(x)` consists of the following:
|
||||
/// - the initial challenge vector r which is used to build eq(x, r) in
|
||||
/// SumCheck
|
||||
/// - the random vector `v` to be evaluated
|
||||
/// - the claimed evaluation of `f(v)`
|
||||
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||
pub struct ZeroCheckSubClaim<F: PrimeField> {
|
||||
// the evaluation point
|
||||
pub point: Vec<F>,
|
||||
/// the expected evaluation
|
||||
pub expected_evaluation: F,
|
||||
// the initial challenge r which is used to build eq(x, r)
|
||||
pub init_challenge: Vec<F>,
|
||||
}
|
||||
|
||||
/// A ZeroCheck is derived from SumCheck.
|
||||
/// A ZeroCheck for `f(x)` proves that `f(x) = 0` for all `x \in {0,1}^num_vars`
|
||||
/// It is derived from SumCheck.
|
||||
pub trait ZeroCheck<F: PrimeField>: SumCheck<F> {
|
||||
type ZeroCheckSubClaim: Clone + Debug + Default + PartialEq;
|
||||
type ZeroCheckProof: Clone + Debug + Default + PartialEq;
|
||||
@@ -51,31 +54,13 @@ pub trait ZeroCheck<F: PrimeField>: SumCheck<F> {
|
||||
}
|
||||
|
||||
impl<F: PrimeField> ZeroCheck<F> for PolyIOP<F> {
|
||||
/// A ZeroCheck SubClaim consists of
|
||||
/// - 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 ZeroCheckSubClaim = ZeroCheckSubClaim<F>;
|
||||
type ZeroCheckProof = Self::SumCheckProof;
|
||||
|
||||
/// Initialize the system with a transcript
|
||||
///
|
||||
/// This function is optional -- in the case where a ZeroCheck is
|
||||
/// an building block for a more complex protocol, the transcript
|
||||
/// may be initialized by this complex protocol, and passed to the
|
||||
/// ZeroCheck prover/verifier.
|
||||
fn init_transcript() -> Self::Transcript {
|
||||
IOPTranscript::<F>::new(b"Initializing ZeroCheck transcript")
|
||||
}
|
||||
|
||||
/// Initialize the prover to argue for the sum of polynomial f(x) over
|
||||
/// {0,1}^`num_vars` is zero.
|
||||
///
|
||||
/// f(x) is zero if \hat f(x) := f(x) * eq(x,r) is also a zero polynomial
|
||||
/// for a random r sampled from transcript.
|
||||
///
|
||||
/// This function will build the \hat f(x) and then invoke the sumcheck
|
||||
/// protocol to generate a proof for which the sum of \hat f(x) is zero
|
||||
fn prove(
|
||||
poly: &Self::VirtualPolynomial,
|
||||
transcript: &mut Self::Transcript,
|
||||
@@ -91,15 +76,6 @@ impl<F: PrimeField> ZeroCheck<F> for PolyIOP<F> {
|
||||
res
|
||||
}
|
||||
|
||||
/// Verify that the polynomial's sum is zero using the proof.
|
||||
/// Return a Self::Subclaim that consists of the
|
||||
///
|
||||
/// - a Subclaim that the sum is zero
|
||||
/// - the initial challenge `r` that is used to build `eq(x, r)`
|
||||
///
|
||||
/// This function will check that \hat f(x)'s sum is zero. It does not check
|
||||
/// `\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::ZeroCheckProof,
|
||||
fx_aux_info: &Self::VPAuxInfo,
|
||||
@@ -122,20 +98,20 @@ impl<F: PrimeField> ZeroCheck<F> for PolyIOP<F> {
|
||||
// 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();
|
||||
hat_fx_aux_info.max_degree += 1;
|
||||
let subclaim =
|
||||
let sum_subclaim =
|
||||
<Self as SumCheck<F>>::verify(F::zero(), proof, &hat_fx_aux_info, transcript)?;
|
||||
|
||||
// expected_eval = sumcheck.expect_eval/eq(x, r)
|
||||
// where x = sum_check_sub_claim.point
|
||||
// expected_eval = sumcheck.expect_eval/eq(v, r)
|
||||
// where v = sum_check_sub_claim.point
|
||||
let eq_x_r = build_eq_x_r(&r)?;
|
||||
let expected_evaluation = subclaim.expected_evaluation
|
||||
/ eq_x_r.evaluate(&subclaim.point).ok_or_else(|| {
|
||||
let expected_evaluation = sum_subclaim.expected_evaluation
|
||||
/ eq_x_r.evaluate(&sum_subclaim.point).ok_or_else(|| {
|
||||
PolyIOPErrors::InvalidParameters("evaluation dimension does not match".to_string())
|
||||
})?;
|
||||
|
||||
end_timer!(start);
|
||||
Ok(ZeroCheckSubClaim {
|
||||
sum_check_sub_claim: subclaim,
|
||||
point: sum_subclaim.point,
|
||||
expected_evaluation,
|
||||
init_challenge: r,
|
||||
})
|
||||
@@ -170,11 +146,10 @@ mod test {
|
||||
let poly_info = poly.aux_info.clone();
|
||||
let mut transcript = <PolyIOP<Fr> as ZeroCheck<Fr>>::init_transcript();
|
||||
transcript.append_message(b"testing", b"initializing transcript for testing")?;
|
||||
let subclaim =
|
||||
<PolyIOP<Fr> as ZeroCheck<Fr>>::verify(&proof, &poly_info, &mut transcript)?
|
||||
.sum_check_sub_claim;
|
||||
let zero_subclaim =
|
||||
<PolyIOP<Fr> as ZeroCheck<Fr>>::verify(&proof, &poly_info, &mut transcript)?;
|
||||
assert!(
|
||||
poly.evaluate(&subclaim.point)? == subclaim.expected_evaluation,
|
||||
poly.evaluate(&zero_subclaim.point)? == zero_subclaim.expected_evaluation,
|
||||
"wrong subclaim"
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user