mirror of
https://github.com/arnaucube/hyperplonk.git
synced 2026-01-11 16:41:28 +01:00
refactoring building block PIOPs (#71)
This commit is contained in:
@@ -680,7 +680,7 @@ where
|
|||||||
&mut transcript,
|
&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
|
// check zero check subclaim
|
||||||
let f_eval = eval_f(
|
let f_eval = eval_f(
|
||||||
@@ -718,10 +718,9 @@ where
|
|||||||
let perm_check_point = &perm_check_sub_claim
|
let perm_check_point = &perm_check_sub_claim
|
||||||
.product_check_sub_claim
|
.product_check_sub_claim
|
||||||
.zero_check_sub_claim
|
.zero_check_sub_claim
|
||||||
.sum_check_sub_claim
|
|
||||||
.point;
|
.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;
|
let (beta, gamma) = perm_check_sub_claim.challenges;
|
||||||
|
|
||||||
// check perm check subclaim:
|
// check perm check subclaim:
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ use ark_poly::{DenseMultilinearExtension, MultilinearExtension};
|
|||||||
use ark_std::test_rng;
|
use ark_std::test_rng;
|
||||||
use pcs::{prelude::KZGMultilinearPCS, PolynomialCommitmentScheme};
|
use pcs::{prelude::KZGMultilinearPCS, PolynomialCommitmentScheme};
|
||||||
use poly_iop::prelude::{
|
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};
|
use std::{marker::PhantomData, rc::Rc, time::Instant};
|
||||||
|
|
||||||
@@ -15,6 +16,8 @@ fn main() -> Result<(), PolyIOPErrors> {
|
|||||||
println!("\n\n");
|
println!("\n\n");
|
||||||
bench_sum_check()?;
|
bench_sum_check()?;
|
||||||
println!("\n\n");
|
println!("\n\n");
|
||||||
|
bench_prod_check()?;
|
||||||
|
println!("\n\n");
|
||||||
bench_zero_check()
|
bench_zero_check()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,11 +116,10 @@ fn bench_zero_check() -> Result<(), PolyIOPErrors> {
|
|||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
let mut transcript = <PolyIOP<Fr> as ZeroCheck<Fr>>::init_transcript();
|
let mut transcript = <PolyIOP<Fr> as ZeroCheck<Fr>>::init_transcript();
|
||||||
transcript.append_message(b"testing", b"initializing transcript for testing")?;
|
transcript.append_message(b"testing", b"initializing transcript for testing")?;
|
||||||
let subclaim =
|
let zero_subclaim =
|
||||||
<PolyIOP<Fr> as ZeroCheck<Fr>>::verify(&proof, &poly_info, &mut transcript)?
|
<PolyIOP<Fr> as ZeroCheck<Fr>>::verify(&proof, &poly_info, &mut transcript)?;
|
||||||
.sum_check_sub_claim;
|
|
||||||
assert!(
|
assert!(
|
||||||
poly.evaluate(&subclaim.point)? == subclaim.expected_evaluation,
|
poly.evaluate(&zero_subclaim.point)? == zero_subclaim.expected_evaluation,
|
||||||
"wrong subclaim"
|
"wrong subclaim"
|
||||||
);
|
);
|
||||||
println!(
|
println!(
|
||||||
@@ -204,3 +206,72 @@ fn bench_permutation_check() -> Result<(), PolyIOPErrors> {
|
|||||||
|
|
||||||
Ok(())
|
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;
|
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:
|
/// A Permutation Check IOP takes the following steps:
|
||||||
///
|
///
|
||||||
@@ -58,7 +61,7 @@ where
|
|||||||
/// Outputs:
|
/// Outputs:
|
||||||
/// - a permutation check proof proving that g is a permutation of f under
|
/// - a permutation check proof proving that g is a permutation of f under
|
||||||
/// s_perm
|
/// s_perm
|
||||||
/// - the Q(x) polynomial build during product check
|
/// - the product polynomial build during product check
|
||||||
///
|
///
|
||||||
/// Cost: O(N)
|
/// Cost: O(N)
|
||||||
fn prove(
|
fn prove(
|
||||||
@@ -78,53 +81,18 @@ where
|
|||||||
) -> Result<Self::PermutationCheckSubClaim, PolyIOPErrors>;
|
) -> 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>
|
impl<E, PCS> PermutationCheck<E, PCS> for PolyIOP<E::Fr>
|
||||||
where
|
where
|
||||||
E: PairingEngine,
|
E: PairingEngine,
|
||||||
PCS: PolynomialCommitmentScheme<E, Polynomial = Rc<DenseMultilinearExtension<E::Fr>>>,
|
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 PermutationCheckSubClaim = PermutationCheckSubClaim<E, PCS, Self>;
|
||||||
type PermutationProof = Self::ProductCheckProof;
|
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 {
|
fn init_transcript() -> Self::Transcript {
|
||||||
IOPTranscript::<E::Fr>::new(b"Initializing PermutationCheck 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(
|
fn prove(
|
||||||
pcs_param: &PCS::ProverParam,
|
pcs_param: &PCS::ProverParam,
|
||||||
fx: &Self::MultilinearExtension,
|
fx: &Self::MultilinearExtension,
|
||||||
@@ -151,11 +119,11 @@ where
|
|||||||
let (numerator, denominator) = computer_num_and_denom(&beta, &gamma, fx, gx, s_perm)?;
|
let (numerator, denominator) = computer_num_and_denom(&beta, &gamma, fx, gx, s_perm)?;
|
||||||
|
|
||||||
// invoke product check on numerator and denominator
|
// 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)?;
|
<Self as ProductCheck<E, PCS>>::prove(pcs_param, &numerator, &denominator, transcript)?;
|
||||||
|
|
||||||
end_timer!(start);
|
end_timer!(start);
|
||||||
Ok((proof, poly))
|
Ok((proof, prod_poly))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verify that an MLE g(x) is a permutation of an
|
/// Verify that an MLE g(x) is a permutation of an
|
||||||
@@ -184,18 +152,15 @@ where
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::{util::build_prod_partial_eval, PermutationCheck};
|
use super::PermutationCheck;
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::PolyIOPErrors,
|
errors::PolyIOPErrors,
|
||||||
perm_check::util::computer_num_and_denom,
|
|
||||||
prelude::{identity_permutation_mle, random_permutation_mle},
|
prelude::{identity_permutation_mle, random_permutation_mle},
|
||||||
utils::bit_decompose,
|
|
||||||
PolyIOP,
|
PolyIOP,
|
||||||
};
|
};
|
||||||
use arithmetic::{VPAuxInfo, VirtualPolynomial};
|
use arithmetic::VPAuxInfo;
|
||||||
use ark_bls12_381::Bls12_381;
|
use ark_bls12_381::Bls12_381;
|
||||||
use ark_ec::PairingEngine;
|
use ark_ec::PairingEngine;
|
||||||
use ark_ff::{One, Zero};
|
|
||||||
use ark_poly::{DenseMultilinearExtension, MultilinearExtension};
|
use ark_poly::{DenseMultilinearExtension, MultilinearExtension};
|
||||||
use ark_std::test_rng;
|
use ark_std::test_rng;
|
||||||
use pcs::{prelude::KZGMultilinearPCS, PolynomialCommitmentScheme};
|
use pcs::{prelude::KZGMultilinearPCS, PolynomialCommitmentScheme};
|
||||||
@@ -223,7 +188,7 @@ mod test {
|
|||||||
// prover
|
// prover
|
||||||
let mut transcript = <PolyIOP<E::Fr> as PermutationCheck<E, PCS>>::init_transcript();
|
let mut transcript = <PolyIOP<E::Fr> as PermutationCheck<E, PCS>>::init_transcript();
|
||||||
transcript.append_message(b"testing", b"initializing transcript for testing")?;
|
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,
|
pcs_param,
|
||||||
fx,
|
fx,
|
||||||
gx,
|
gx,
|
||||||
@@ -234,76 +199,21 @@ mod test {
|
|||||||
// verifier
|
// verifier
|
||||||
let mut transcript = <PolyIOP<E::Fr> as PermutationCheck<E, PCS>>::init_transcript();
|
let mut transcript = <PolyIOP<E::Fr> as PermutationCheck<E, PCS>>::init_transcript();
|
||||||
transcript.append_message(b"testing", b"initializing transcript for testing")?;
|
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,
|
&proof,
|
||||||
&poly_info,
|
&poly_info,
|
||||||
&mut transcript,
|
&mut transcript,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let prod_partial_evals = build_prod_partial_eval(&q_x)?;
|
// check product subclaim
|
||||||
let prod_0x = prod_partial_evals[0].clone();
|
if prod_x
|
||||||
let prod_1x = prod_partial_evals[1].clone();
|
.evaluate(&perm_check_sub_claim.product_check_sub_claim.final_query.0)
|
||||||
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,
|
|
||||||
)
|
|
||||||
.unwrap()
|
.unwrap()
|
||||||
!= perm_check_sum_claim
|
!= perm_check_sub_claim.product_check_sub_claim.final_query.1
|
||||||
.product_check_sub_claim
|
|
||||||
.zero_check_sub_claim
|
|
||||||
.sum_check_sub_claim
|
|
||||||
.expected_evaluation
|
|
||||||
{
|
{
|
||||||
return Err(PolyIOPErrors::InvalidVerifier("wrong subclaim".to_string()));
|
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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,60 +6,6 @@ use ark_poly::DenseMultilinearExtension;
|
|||||||
use ark_std::{end_timer, rand::RngCore, start_timer};
|
use ark_std::{end_timer, rand::RngCore, start_timer};
|
||||||
use std::rc::Rc;
|
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:
|
/// Returns the evaluations of two MLEs:
|
||||||
/// - numerator
|
/// - numerator
|
||||||
/// - denominator
|
/// - denominator
|
||||||
@@ -139,118 +85,3 @@ pub fn random_permutation_mle<F: PrimeField, R: RngCore>(
|
|||||||
num_vars, s_perm_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)
|
|
||||||
#[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 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) + challenge * (f(x) - prod(0,
|
/// `Q(x) = prod(1, x) - prod(x, 0) * prod(x, 1) + alpha * (f(x) - prod(0,
|
||||||
/// x) * g(x))` is 0, consists of the following:
|
/// x) * g(x)) = 0`
|
||||||
/// - the SubClaim from the SumCheck
|
/// - The random challenge `alpha`
|
||||||
/// - the initial challenge r which is used to build eq(x, r) in ZeroCheck
|
/// - A final query for `prod(1, ..., 1, 0) = 1`.
|
||||||
/// - The challenge `challenge`
|
|
||||||
/// - 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
|
||||||
// (de)serialize it.
|
// (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
|
// - the vector `(1, ..., 1, 0)` (needs to be reversed because Arkwork's MLE uses big-endian
|
||||||
// format for points)
|
// format for points)
|
||||||
// The expected final query evaluation is 1
|
// The expected final query evaluation is 1
|
||||||
final_query: (Vec<F>, F),
|
pub final_query: (Vec<F>, F),
|
||||||
pub challenge: F,
|
pub alpha: F,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A product check proof consists of
|
/// A product check proof consists of
|
||||||
@@ -195,7 +193,7 @@ where
|
|||||||
Ok(ProductCheckSubClaim {
|
Ok(ProductCheckSubClaim {
|
||||||
zero_check_sub_claim,
|
zero_check_sub_claim,
|
||||||
final_query: (final_query, final_eval),
|
final_query: (final_query, final_eval),
|
||||||
challenge: alpha,
|
alpha,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -240,11 +238,11 @@ mod test {
|
|||||||
num_variables: f.num_vars,
|
num_variables: f.num_vars,
|
||||||
phantom: PhantomData::default(),
|
phantom: PhantomData::default(),
|
||||||
};
|
};
|
||||||
let subclaim =
|
let prod_subclaim =
|
||||||
<PolyIOP<E::Fr> as ProductCheck<E, PCS>>::verify(&proof, &aux_info, &mut transcript)?;
|
<PolyIOP<E::Fr> as ProductCheck<E, PCS>>::verify(&proof, &aux_info, &mut transcript)?;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
prod_x.evaluate(&subclaim.final_query.0).unwrap(),
|
prod_x.evaluate(&prod_subclaim.final_query.0).unwrap(),
|
||||||
subclaim.final_query.1,
|
prod_subclaim.final_query.1,
|
||||||
"different product"
|
"different product"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -131,7 +131,6 @@ impl<F: PrimeField> SumCheck<F> for PolyIOP<F> {
|
|||||||
type SumCheckSubClaim = SumCheckSubClaim<F>;
|
type SumCheckSubClaim = SumCheckSubClaim<F>;
|
||||||
type Transcript = IOPTranscript<F>;
|
type Transcript = IOPTranscript<F>;
|
||||||
|
|
||||||
/// Extract sum from the proof
|
|
||||||
fn extract_sum(proof: &Self::SumCheckProof) -> F {
|
fn extract_sum(proof: &Self::SumCheckProof) -> F {
|
||||||
let start = start_timer!(|| "extract sum");
|
let start = start_timer!(|| "extract sum");
|
||||||
let res = proof.proofs[0].evaluations[0] + proof.proofs[0].evaluations[1];
|
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
|
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 {
|
fn init_transcript() -> Self::Transcript {
|
||||||
let start = start_timer!(|| "init transcript");
|
let start = start_timer!(|| "init transcript");
|
||||||
let res = IOPTranscript::<F>::new(b"Initializing SumCheck transcript");
|
let res = IOPTranscript::<F>::new(b"Initializing SumCheck transcript");
|
||||||
@@ -152,9 +145,6 @@ impl<F: PrimeField> SumCheck<F> for PolyIOP<F> {
|
|||||||
res
|
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(
|
fn prove(
|
||||||
poly: &Self::VirtualPolynomial,
|
poly: &Self::VirtualPolynomial,
|
||||||
transcript: &mut Self::Transcript,
|
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(
|
fn verify(
|
||||||
claimed_sum: F,
|
claimed_sum: F,
|
||||||
proof: &Self::SumCheckProof,
|
proof: &Self::SumCheckProof,
|
||||||
|
|||||||
@@ -9,20 +9,23 @@ use ark_poly::MultilinearExtension;
|
|||||||
use ark_std::{end_timer, start_timer};
|
use ark_std::{end_timer, start_timer};
|
||||||
use transcript::IOPTranscript;
|
use transcript::IOPTranscript;
|
||||||
|
|
||||||
/// A zero check IOP subclaim for \hat f(x) is 0, consists of the following:
|
/// A zero check IOP subclaim for `f(x)` consists of the following:
|
||||||
/// - the SubClaim from the SumCheck
|
/// - the initial challenge vector r which is used to build eq(x, r) in
|
||||||
/// - the initial challenge r which is used to build eq(x, r) in ZeroCheck
|
/// SumCheck
|
||||||
#[derive(Clone, Debug, Default, PartialEq)]
|
/// - the random vector `v` to be evaluated
|
||||||
pub struct ZeroCheckSubClaim<F: PrimeField, SC: SumCheck<F>> {
|
/// - the claimed evaluation of `f(v)`
|
||||||
// the SubClaim from the SumCheck
|
#[derive(Clone, Debug, Default, PartialEq, Eq)]
|
||||||
pub sum_check_sub_claim: SC::SumCheckSubClaim,
|
pub struct ZeroCheckSubClaim<F: PrimeField> {
|
||||||
|
// the evaluation point
|
||||||
|
pub point: Vec<F>,
|
||||||
/// the expected evaluation
|
/// the expected evaluation
|
||||||
pub expected_evaluation: F,
|
pub expected_evaluation: F,
|
||||||
// the initial challenge r which is used to build eq(x, r)
|
// the initial challenge r which is used to build eq(x, r)
|
||||||
pub init_challenge: Vec<F>,
|
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> {
|
pub trait ZeroCheck<F: PrimeField>: SumCheck<F> {
|
||||||
type ZeroCheckSubClaim: Clone + Debug + Default + PartialEq;
|
type ZeroCheckSubClaim: Clone + Debug + Default + PartialEq;
|
||||||
type ZeroCheckProof: 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> {
|
impl<F: PrimeField> ZeroCheck<F> for PolyIOP<F> {
|
||||||
/// A ZeroCheck SubClaim consists of
|
type ZeroCheckSubClaim = ZeroCheckSubClaim<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;
|
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 {
|
fn init_transcript() -> Self::Transcript {
|
||||||
IOPTranscript::<F>::new(b"Initializing ZeroCheck 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(
|
fn prove(
|
||||||
poly: &Self::VirtualPolynomial,
|
poly: &Self::VirtualPolynomial,
|
||||||
transcript: &mut Self::Transcript,
|
transcript: &mut Self::Transcript,
|
||||||
@@ -91,15 +76,6 @@ impl<F: PrimeField> ZeroCheck<F> for PolyIOP<F> {
|
|||||||
res
|
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(
|
fn verify(
|
||||||
proof: &Self::ZeroCheckProof,
|
proof: &Self::ZeroCheckProof,
|
||||||
fx_aux_info: &Self::VPAuxInfo,
|
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
|
// 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();
|
let mut hat_fx_aux_info = fx_aux_info.clone();
|
||||||
hat_fx_aux_info.max_degree += 1;
|
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)?;
|
<Self as SumCheck<F>>::verify(F::zero(), proof, &hat_fx_aux_info, transcript)?;
|
||||||
|
|
||||||
// expected_eval = sumcheck.expect_eval/eq(x, r)
|
// expected_eval = sumcheck.expect_eval/eq(v, r)
|
||||||
// where x = sum_check_sub_claim.point
|
// where v = sum_check_sub_claim.point
|
||||||
let eq_x_r = build_eq_x_r(&r)?;
|
let eq_x_r = build_eq_x_r(&r)?;
|
||||||
let expected_evaluation = subclaim.expected_evaluation
|
let expected_evaluation = sum_subclaim.expected_evaluation
|
||||||
/ eq_x_r.evaluate(&subclaim.point).ok_or_else(|| {
|
/ eq_x_r.evaluate(&sum_subclaim.point).ok_or_else(|| {
|
||||||
PolyIOPErrors::InvalidParameters("evaluation dimension does not match".to_string())
|
PolyIOPErrors::InvalidParameters("evaluation dimension does not match".to_string())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
end_timer!(start);
|
end_timer!(start);
|
||||||
Ok(ZeroCheckSubClaim {
|
Ok(ZeroCheckSubClaim {
|
||||||
sum_check_sub_claim: subclaim,
|
point: sum_subclaim.point,
|
||||||
expected_evaluation,
|
expected_evaluation,
|
||||||
init_challenge: r,
|
init_challenge: r,
|
||||||
})
|
})
|
||||||
@@ -170,11 +146,10 @@ mod test {
|
|||||||
let poly_info = poly.aux_info.clone();
|
let poly_info = poly.aux_info.clone();
|
||||||
let mut transcript = <PolyIOP<Fr> as ZeroCheck<Fr>>::init_transcript();
|
let mut transcript = <PolyIOP<Fr> as ZeroCheck<Fr>>::init_transcript();
|
||||||
transcript.append_message(b"testing", b"initializing transcript for testing")?;
|
transcript.append_message(b"testing", b"initializing transcript for testing")?;
|
||||||
let subclaim =
|
let zero_subclaim =
|
||||||
<PolyIOP<Fr> as ZeroCheck<Fr>>::verify(&proof, &poly_info, &mut transcript)?
|
<PolyIOP<Fr> as ZeroCheck<Fr>>::verify(&proof, &poly_info, &mut transcript)?;
|
||||||
.sum_check_sub_claim;
|
|
||||||
assert!(
|
assert!(
|
||||||
poly.evaluate(&subclaim.point)? == subclaim.expected_evaluation,
|
poly.evaluate(&zero_subclaim.point)? == zero_subclaim.expected_evaluation,
|
||||||
"wrong subclaim"
|
"wrong subclaim"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user