impl zero check (#15) and vp operations (#17)

This commit is contained in:
zhenfei
2022-05-16 11:48:19 -04:00
committed by GitHub
parent 0b251a38e8
commit 0d77c3a2b1
10 changed files with 511 additions and 462 deletions

View File

@@ -4,7 +4,7 @@
use crate::{
errors::PolyIOPErrors,
structs::{DomainInfo, IOPProof, SubClaim},
structs::{DomainInfo, IOPProof, IOPProverState, IOPVerifierState, SubClaim},
transcript::IOPTranscript,
virtual_poly::VirtualPolynomial,
PolyIOP,
@@ -15,9 +15,7 @@ use ark_std::{end_timer, start_timer};
mod prover;
mod verifier;
pub use prover::ProverState;
pub use verifier::VerifierState;
/// Trait for doing sum check protocols.
pub trait SumCheck<F: PrimeField> {
type Proof;
type PolyList;
@@ -36,27 +34,15 @@ pub trait SumCheck<F: PrimeField> {
/// SumCheck prover/verifier.
fn init_transcript() -> Self::Transcript;
/// generate proof of the sum of polynomial over {0,1}^`num_vars`
/// Generate proof of the sum of polynomial over {0,1}^`num_vars`
///
/// The polynomial is represented by a list of products of polynomials along
/// with its coefficient that is meant to be added together.
///
/// This data structure of the polynomial is a list of list of
/// `(coefficient, DenseMultilinearExtension)`.
/// * Number of products n = `polynomial.products.len()`,
/// * Number of multiplicands of ith product m_i =
/// `polynomial.products[i].1.len()`,
/// * Coefficient of ith product c_i = `polynomial.products[i].0`
///
/// The resulting polynomial is
///
/// $$\sum_{i=0}^{n}C_i\cdot\prod_{j=0}^{m_i}P_{ij}$$
/// The polynomial is represented in the form of a VirtualPolynomial.
fn prove(
poly: &Self::PolyList,
transcript: &mut Self::Transcript,
) -> Result<Self::Proof, PolyIOPErrors>;
/// verify the claimed sum using the proof
/// Verify the claimed sum using the proof
fn verify(
sum: F,
proof: &Self::Proof,
@@ -65,6 +51,7 @@ pub trait SumCheck<F: PrimeField> {
) -> Result<Self::SubClaim, PolyIOPErrors>;
}
/// Trait for sum check protocol prover side APIs.
pub trait SumCheckProver<F: PrimeField>
where
Self: Sized,
@@ -72,22 +59,10 @@ where
type PolyList;
type ProverMessage;
/// initialize the prover to argue for the sum of polynomial over
/// Initialize the prover to argue for the sum of polynomial over
/// {0,1}^`num_vars`
///
/// The polynomial is represented by a list of products of polynomials along
/// with its coefficient that is meant to be added together.
///
/// This data structure of the polynomial is a list of list of
/// `(coefficient, DenseMultilinearExtension)`.
/// * Number of products n = `polynomial.products.len()`,
/// * Number of multiplicands of ith product m_i =
/// `polynomial.products[i].1.len()`,
/// * Coefficient of ith product c_i = `polynomial.products[i].0`
///
/// The resulting polynomial is
///
/// $$\sum_{i=0}^{n}C_i\cdot\prod_{j=0}^{m_i}P_{ij}$$
/// The polynomial is represented in the form of a VirtualPolynomial.
fn prover_init(polynomial: &Self::PolyList) -> Result<Self, PolyIOPErrors>;
/// receive message from verifier, generate prover message, and proceed to
@@ -100,6 +75,7 @@ where
) -> Result<Self::ProverMessage, PolyIOPErrors>;
}
/// Trait for sum check protocol verifier side APIs.
pub trait SumCheckVerifier<F: PrimeField> {
type DomainInfo;
type ProverMessage;
@@ -165,21 +141,9 @@ impl<F: PrimeField> SumCheck<F> for PolyIOP<F> {
res
}
/// generate proof of the sum of polynomial over {0,1}^`num_vars`
/// Generate proof of the sum of polynomial over {0,1}^`num_vars`
///
/// The polynomial is represented by a list of products of polynomials along
/// with its coefficient that is meant to be added together.
///
/// This data structure of the polynomial is a list of list of
/// `(coefficient, DenseMultilinearExtension)`.
/// * Number of products n = `polynomial.products.len()`,
/// * Number of multiplicands of ith product m_i =
/// `polynomial.products[i].1.len()`,
/// * Coefficient of ith product c_i = `polynomial.products[i].0`
///
/// The resulting polynomial is
///
/// $$\sum_{i=0}^{n}C_i\cdot\prod_{j=0}^{m_i}P_{ij}$$
/// The polynomial is represented in the form of a VirtualPolynomial.
fn prove(
poly: &Self::PolyList,
transcript: &mut Self::Transcript,
@@ -188,12 +152,12 @@ impl<F: PrimeField> SumCheck<F> for PolyIOP<F> {
transcript.append_domain_info(&poly.domain_info)?;
let mut prover_state = ProverState::prover_init(poly)?;
let mut prover_state = IOPProverState::prover_init(poly)?;
let mut challenge = None;
let mut prover_msgs = Vec::with_capacity(poly.domain_info.num_variables);
for _ in 0..poly.domain_info.num_variables {
let prover_msg =
ProverState::prove_round_and_update_state(&mut prover_state, &challenge)?;
IOPProverState::prove_round_and_update_state(&mut prover_state, &challenge)?;
transcript.append_prover_message(&prover_msg)?;
prover_msgs.push(prover_msg);
challenge = Some(transcript.get_and_append_challenge(b"Internal round")?);
@@ -215,18 +179,18 @@ impl<F: PrimeField> SumCheck<F> for PolyIOP<F> {
let start = start_timer!(|| "sum check verify");
transcript.append_domain_info(domain_info)?;
let mut verifier_state = VerifierState::verifier_init(domain_info);
let mut verifier_state = IOPVerifierState::verifier_init(domain_info);
for i in 0..domain_info.num_variables {
let prover_msg = proof.proofs.get(i).expect("proof is incomplete");
transcript.append_prover_message(prover_msg)?;
VerifierState::verify_round_and_update_state(
IOPVerifierState::verify_round_and_update_state(
&mut verifier_state,
prover_msg,
transcript,
)?;
}
let res = VerifierState::check_and_generate_subclaim(&verifier_state, &claimed_sum);
let res = IOPVerifierState::check_and_generate_subclaim(&verifier_state, &claimed_sum);
end_timer!(start);
res
@@ -237,41 +201,49 @@ impl<F: PrimeField> SumCheck<F> for PolyIOP<F> {
mod test {
use super::*;
use crate::virtual_poly::test::random_list_of_products;
use ark_bls12_381::Fr;
use ark_ff::UniformRand;
use ark_poly::{DenseMultilinearExtension, MultilinearExtension};
use ark_std::test_rng;
use std::rc::Rc;
fn test_sumcheck(nv: usize, num_multiplicands_range: (usize, usize), num_products: usize) {
fn test_sumcheck(
nv: usize,
num_multiplicands_range: (usize, usize),
num_products: usize,
) -> Result<(), PolyIOPErrors> {
let mut rng = test_rng();
let mut transcript = PolyIOP::init_transcript();
let mut transcript = <PolyIOP<Fr> as SumCheck<Fr>>::init_transcript();
let (poly, asserted_sum) =
random_list_of_products::<Fr, _>(nv, num_multiplicands_range, num_products, &mut rng);
let proof = PolyIOP::prove(&poly, &mut transcript).expect("fail to prove");
VirtualPolynomial::rand(nv, num_multiplicands_range, num_products, &mut rng)?;
let proof = <PolyIOP<Fr> as SumCheck<Fr>>::prove(&poly, &mut transcript)?;
let poly_info = poly.domain_info.clone();
let mut transcript = PolyIOP::init_transcript();
let subclaim = PolyIOP::verify(asserted_sum, &proof, &poly_info, &mut transcript)
.expect("fail to verify");
let mut transcript = <PolyIOP<Fr> as SumCheck<Fr>>::init_transcript();
let subclaim = <PolyIOP<Fr> as SumCheck<Fr>>::verify(
asserted_sum,
&proof,
&poly_info,
&mut transcript,
)?;
assert!(
poly.evaluate(&subclaim.point).unwrap() == subclaim.expected_evaluation,
"wrong subclaim"
);
Ok(())
}
fn test_sumcheck_internal(
nv: usize,
num_multiplicands_range: (usize, usize),
num_products: usize,
) {
) -> Result<(), PolyIOPErrors> {
let mut rng = test_rng();
let (poly, asserted_sum) =
random_list_of_products::<Fr, _>(nv, num_multiplicands_range, num_products, &mut rng);
VirtualPolynomial::<Fr>::rand(nv, num_multiplicands_range, num_products, &mut rng)?;
let poly_info = poly.domain_info.clone();
let mut prover_state = ProverState::prover_init(&poly).unwrap();
let mut verifier_state = VerifierState::verifier_init(&poly_info);
let mut prover_state = IOPProverState::prover_init(&poly)?;
let mut verifier_state = IOPVerifierState::verifier_init(&poly_info);
let mut challenge = None;
let mut transcript = IOPTranscript::new(b"a test transcript");
transcript
@@ -279,10 +251,11 @@ mod test {
.unwrap();
for _ in 0..poly.domain_info.num_variables {
let prover_message =
ProverState::prove_round_and_update_state(&mut prover_state, &challenge).unwrap();
IOPProverState::prove_round_and_update_state(&mut prover_state, &challenge)
.unwrap();
challenge = Some(
VerifierState::verify_round_and_update_state(
IOPVerifierState::verify_round_and_update_state(
&mut verifier_state,
&prover_message,
&mut transcript,
@@ -290,115 +263,120 @@ mod test {
.unwrap(),
);
}
let subclaim = VerifierState::check_and_generate_subclaim(&verifier_state, &asserted_sum)
.expect("fail to generate subclaim");
let subclaim =
IOPVerifierState::check_and_generate_subclaim(&verifier_state, &asserted_sum)
.expect("fail to generate subclaim");
assert!(
poly.evaluate(&subclaim.point).unwrap() == subclaim.expected_evaluation,
"wrong subclaim"
);
Ok(())
}
#[test]
fn test_trivial_polynomial() {
fn test_trivial_polynomial() -> Result<(), PolyIOPErrors> {
let nv = 1;
let num_multiplicands_range = (4, 13);
let num_products = 5;
test_sumcheck(nv, num_multiplicands_range, num_products);
test_sumcheck_internal(nv, num_multiplicands_range, num_products);
test_sumcheck(nv, num_multiplicands_range, num_products)?;
test_sumcheck_internal(nv, num_multiplicands_range, num_products)
}
#[test]
fn test_normal_polynomial() {
fn test_normal_polynomial() -> Result<(), PolyIOPErrors> {
let nv = 12;
let num_multiplicands_range = (4, 9);
let num_products = 5;
test_sumcheck(nv, num_multiplicands_range, num_products);
test_sumcheck_internal(nv, num_multiplicands_range, num_products);
test_sumcheck(nv, num_multiplicands_range, num_products)?;
test_sumcheck_internal(nv, num_multiplicands_range, num_products)
}
#[test]
#[should_panic]
fn zero_polynomial_should_error() {
let nv = 0;
let num_multiplicands_range = (4, 13);
let num_products = 5;
test_sumcheck(nv, num_multiplicands_range, num_products);
test_sumcheck_internal(nv, num_multiplicands_range, num_products);
assert!(test_sumcheck(nv, num_multiplicands_range, num_products).is_err());
assert!(test_sumcheck_internal(nv, num_multiplicands_range, num_products).is_err());
}
#[test]
fn test_extract_sum() {
fn test_extract_sum() -> Result<(), PolyIOPErrors> {
let mut rng = test_rng();
let mut transcript = PolyIOP::init_transcript();
let (poly, asserted_sum) = random_list_of_products::<Fr, _>(8, (3, 4), 3, &mut rng);
let mut transcript = <PolyIOP<Fr> as SumCheck<Fr>>::init_transcript();
let (poly, asserted_sum) = VirtualPolynomial::<Fr>::rand(8, (3, 4), 3, &mut rng)?;
let proof = PolyIOP::prove(&poly, &mut transcript).expect("fail to prove");
assert_eq!(PolyIOP::extract_sum(&proof), asserted_sum);
let proof = <PolyIOP<Fr> as SumCheck<Fr>>::prove(&poly, &mut transcript)?;
assert_eq!(
<PolyIOP<Fr> as SumCheck<Fr>>::extract_sum(&proof),
asserted_sum
);
Ok(())
}
#[test]
/// Test that the memory usage of shared-reference is linear to number of
/// unique MLExtensions instead of total number of multiplicands.
fn test_shared_reference() {
fn test_shared_reference() -> Result<(), PolyIOPErrors> {
let mut rng = test_rng();
let ml_extensions: Vec<_> = (0..5)
.map(|_| Rc::new(DenseMultilinearExtension::<Fr>::rand(8, &mut rng)))
.collect();
let mut poly = VirtualPolynomial::new(8);
poly.add_product(
poly.add_mle_list(
vec![
ml_extensions[2].clone(),
ml_extensions[3].clone(),
ml_extensions[0].clone(),
],
Fr::rand(&mut rng),
)
.unwrap();
poly.add_product(
)?;
poly.add_mle_list(
vec![
ml_extensions[1].clone(),
ml_extensions[4].clone(),
ml_extensions[4].clone(),
],
Fr::rand(&mut rng),
)
.unwrap();
poly.add_product(
)?;
poly.add_mle_list(
vec![
ml_extensions[3].clone(),
ml_extensions[2].clone(),
ml_extensions[1].clone(),
],
Fr::rand(&mut rng),
)
.unwrap();
poly.add_product(
)?;
poly.add_mle_list(
vec![ml_extensions[0].clone(), ml_extensions[0].clone()],
Fr::rand(&mut rng),
)
.unwrap();
poly.add_product(vec![ml_extensions[4].clone()], Fr::rand(&mut rng))
.unwrap();
)?;
poly.add_mle_list(vec![ml_extensions[4].clone()], Fr::rand(&mut rng))?;
assert_eq!(poly.flattened_ml_extensions.len(), 5);
// test memory usage for prover
let prover = ProverState::prover_init(&poly).unwrap();
let prover = IOPProverState::prover_init(&poly).unwrap();
assert_eq!(prover.poly.flattened_ml_extensions.len(), 5);
drop(prover);
let mut transcript = PolyIOP::init_transcript();
let mut transcript = <PolyIOP<Fr> as SumCheck<Fr>>::init_transcript();
let poly_info = poly.domain_info.clone();
let proof = PolyIOP::prove(&poly, &mut transcript).expect("fail to prove");
let asserted_sum = PolyIOP::extract_sum(&proof);
let proof = <PolyIOP<Fr> as SumCheck<Fr>>::prove(&poly, &mut transcript)?;
let asserted_sum = <PolyIOP<Fr> as SumCheck<Fr>>::extract_sum(&proof);
let mut transcript = PolyIOP::init_transcript();
let subclaim = PolyIOP::verify(asserted_sum, &proof, &poly_info, &mut transcript)
.expect("fail to verify");
let mut transcript = <PolyIOP<Fr> as SumCheck<Fr>>::init_transcript();
let subclaim = <PolyIOP<Fr> as SumCheck<Fr>>::verify(
asserted_sum,
&proof,
&poly_info,
&mut transcript,
)?;
assert!(
poly.evaluate(&subclaim.point).unwrap() == subclaim.expected_evaluation,
poly.evaluate(&subclaim.point)? == subclaim.expected_evaluation,
"wrong subclaim"
);
Ok(())
}
}

View File

@@ -1,49 +1,27 @@
//! Prover
use std::rc::Rc;
// TODO: some of the struct is generic for Sum Checks and Zero Checks.
// If so move them to src/structs.rs
use super::SumCheckProver;
use crate::{errors::PolyIOPErrors, structs::IOPProverMessage, virtual_poly::VirtualPolynomial};
use crate::{
errors::PolyIOPErrors,
structs::{IOPProverMessage, IOPProverState},
virtual_poly::VirtualPolynomial,
};
use ark_ff::PrimeField;
use ark_poly::{DenseMultilinearExtension, MultilinearExtension};
use ark_std::{end_timer, start_timer, vec::Vec};
use std::rc::Rc;
#[cfg(feature = "parallel")]
use rayon::iter::{IndexedParallelIterator, IntoParallelRefMutIterator, ParallelIterator};
/// Prover State
pub struct ProverState<F: PrimeField> {
/// sampled randomness given by the verifier
pub challenges: Vec<F>,
/// the current round number
pub(crate) round: usize,
/// pointer to the virtual polynomial
pub(crate) poly: VirtualPolynomial<F>,
}
impl<F: PrimeField> SumCheckProver<F> for ProverState<F> {
impl<F: PrimeField> SumCheckProver<F> for IOPProverState<F> {
type PolyList = VirtualPolynomial<F>;
type ProverMessage = IOPProverMessage<F>;
/// initialize the prover to argue for the sum of polynomial over
/// Initialize the prover to argue for the sum of polynomial over
/// {0,1}^`num_vars`
///
/// The polynomial is represented by a list of products of polynomials along
/// with its coefficient that is meant to be added together.
///
/// This data structure of the polynomial is a list of list of
/// `(coefficient, DenseMultilinearExtension)`.
/// * Number of products n = `polynomial.products.len()`,
/// * Number of multiplicands of ith product m_i =
/// `polynomial.products[i].1.len()`,
/// * Coefficient of ith product c_i = `polynomial.products[i].0`
///
/// The resulting polynomial is
///
/// $$\sum_{i=0}^{n}C_i\cdot\prod_{j=0}^{m_i}P_{ij}$$
fn prover_init(polynomial: &Self::PolyList) -> Result<Self, PolyIOPErrors> {
let start = start_timer!(|| "prover init");
let start = start_timer!(|| "sum check prover init");
if polynomial.domain_info.num_variables == 0 {
return Err(PolyIOPErrors::InvalidParameters(
"Attempt to prove a constant.".to_string(),
@@ -51,14 +29,14 @@ impl<F: PrimeField> SumCheckProver<F> for ProverState<F> {
}
end_timer!(start);
Ok(ProverState {
Ok(Self {
challenges: Vec::with_capacity(polynomial.domain_info.num_variables),
round: 0,
poly: polynomial.clone(),
})
}
/// receive message from verifier, generate prover message, and proceed to
/// Receive message from verifier, generate prover message, and proceed to
/// next round
///
/// Main algorithm used is from section 3.2 of [XZZPS19](https://eprint.iacr.org/2019/317.pdf#subsection.3.2).
@@ -66,7 +44,8 @@ impl<F: PrimeField> SumCheckProver<F> for ProverState<F> {
&mut self,
challenge: &Option<F>,
) -> Result<Self::ProverMessage, PolyIOPErrors> {
let start = start_timer!(|| format!("prove {}-th round and update state", self.round));
let start =
start_timer!(|| format!("sum check prove {}-th round and update state", self.round));
let fix_argument = start_timer!(|| "fix argument");

View File

@@ -4,7 +4,7 @@
use super::SumCheckVerifier;
use crate::{
errors::PolyIOPErrors,
structs::{DomainInfo, IOPProverMessage, SubClaim},
structs::{DomainInfo, IOPProverMessage, IOPVerifierState, SubClaim},
transcript::IOPTranscript,
};
use ark_ff::PrimeField;
@@ -13,20 +13,7 @@ use ark_std::{end_timer, start_timer};
#[cfg(feature = "parallel")]
use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator};
/// Verifier State
pub struct VerifierState<F: PrimeField> {
round: usize,
num_vars: usize,
max_degree: usize,
finished: bool,
/// a list storing the univariate polynomial in evaluation form sent by the
/// prover at each round
polynomials_received: Vec<Vec<F>>,
/// a list storing the randomness sampled by the verifier at each round
challenges: Vec<F>,
}
impl<F: PrimeField> SumCheckVerifier<F> for VerifierState<F> {
impl<F: PrimeField> SumCheckVerifier<F> for IOPVerifierState<F> {
type DomainInfo = DomainInfo<F>;
type ProverMessage = IOPProverMessage<F>;
type Challenge = F;
@@ -35,8 +22,8 @@ impl<F: PrimeField> SumCheckVerifier<F> for VerifierState<F> {
/// initialize the verifier
fn verifier_init(index_info: &Self::DomainInfo) -> Self {
let start = start_timer!(|| "verifier init");
let res = VerifierState {
let start = start_timer!(|| "sum check verifier init");
let res = Self {
round: 1,
num_vars: index_info.num_variables,
max_degree: index_info.max_degree,
@@ -59,7 +46,8 @@ impl<F: PrimeField> SumCheckVerifier<F> for VerifierState<F> {
prover_msg: &Self::ProverMessage,
transcript: &mut Self::Transcript,
) -> Result<Self::Challenge, PolyIOPErrors> {
let start = start_timer!(|| format!("verify {}-th round and update state", self.round));
let start =
start_timer!(|| format!("sum check verify {}-th round and update state", self.round));
if self.finished {
return Err(PolyIOPErrors::InvalidVerifier(
@@ -101,7 +89,7 @@ impl<F: PrimeField> SumCheckVerifier<F> for VerifierState<F> {
&self,
asserted_sum: &F,
) -> Result<Self::SubClaim, PolyIOPErrors> {
let start = start_timer!(|| "check_and_generate_subclaim");
let start = start_timer!(|| "sum check check and generate subclaim");
if !self.finished {
return Err(PolyIOPErrors::InvalidVerifier(
"Incorrect verifier state: Verifier has not finished.".to_string(),
@@ -173,10 +161,10 @@ impl<F: PrimeField> SumCheckVerifier<F> for VerifierState<F> {
}
}
/// interpolate a uni-variate degree-`p_i.len()-1` polynomial and evaluate this
/// Interpolate a uni-variate degree-`p_i.len()-1` polynomial and evaluate this
/// polynomial at `eval_at`.
pub(crate) fn interpolate_uni_poly<F: PrimeField>(p_i: &[F], eval_at: F) -> F {
let start = start_timer!(|| "interpolate_uni_poly");
let start = start_timer!(|| "sum check interpolate uni poly");
let mut result = F::zero();
let mut i = F::zero();
for term in p_i.iter() {