mirror of
https://github.com/arnaucube/hyperplonk.git
synced 2026-01-10 16:11:29 +01:00
@@ -20,6 +20,7 @@ displaydoc = { version = "0.2.3", default-features = false }
|
|||||||
rayon = { version = "1.5.2", default-features = false, optional = true }
|
rayon = { version = "1.5.2", default-features = false, optional = true }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
# default = [ "parallel", "print-trace" ]
|
||||||
default = [ "parallel" ]
|
default = [ "parallel" ]
|
||||||
parallel = [
|
parallel = [
|
||||||
"rayon",
|
"rayon",
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
#![allow(dead_code)]
|
|
||||||
|
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
use ark_ff::PrimeField;
|
use ark_ff::PrimeField;
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
mod errors;
|
mod errors;
|
||||||
mod structs;
|
mod structs;
|
||||||
@@ -10,14 +7,14 @@ mod sum_check;
|
|||||||
mod transcript;
|
mod transcript;
|
||||||
mod utils;
|
mod utils;
|
||||||
mod virtual_poly;
|
mod virtual_poly;
|
||||||
// mod zero_check;
|
mod zero_check;
|
||||||
|
|
||||||
pub use virtual_poly::VirtualPolynomial;
|
pub use virtual_poly::VirtualPolynomial;
|
||||||
|
|
||||||
/// Struct for PolyIOP protocol.
|
/// Struct for PolyIOP protocol.
|
||||||
/// It is instantiated with
|
/// It is instantiated with
|
||||||
/// - SumCheck protocol.
|
/// - SumCheck protocol.
|
||||||
/// - ZeroCheck protocol. (WIP)
|
/// - ZeroCheck protocol.
|
||||||
pub struct PolyIOP<F: PrimeField> {
|
pub struct PolyIOP<F: PrimeField> {
|
||||||
phantom: PhantomData<F>,
|
phantom: PhantomData<F>,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
//! Structs for polynomials and extensions.
|
//! Structs for polynomials and extensions.
|
||||||
|
|
||||||
|
use crate::VirtualPolynomial;
|
||||||
use ark_ff::PrimeField;
|
use ark_ff::PrimeField;
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
@@ -38,3 +39,26 @@ pub struct IOPProof<F: PrimeField> {
|
|||||||
pub struct IOPProverMessage<F: PrimeField> {
|
pub struct IOPProverMessage<F: PrimeField> {
|
||||||
pub(crate) evaluations: Vec<F>,
|
pub(crate) evaluations: Vec<F>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Prover State of a PolyIOP
|
||||||
|
pub struct IOPProverState<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>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Prover State of a PolyIOP
|
||||||
|
pub struct IOPVerifierState<F: PrimeField> {
|
||||||
|
pub(crate) round: usize,
|
||||||
|
pub(crate) num_vars: usize,
|
||||||
|
pub(crate) max_degree: usize,
|
||||||
|
pub(crate) finished: bool,
|
||||||
|
/// a list storing the univariate polynomial in evaluation form sent by the
|
||||||
|
/// prover at each round
|
||||||
|
pub(crate) polynomials_received: Vec<Vec<F>>,
|
||||||
|
/// a list storing the randomness sampled by the verifier at each round
|
||||||
|
pub(crate) challenges: Vec<F>,
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::PolyIOPErrors,
|
errors::PolyIOPErrors,
|
||||||
structs::{DomainInfo, IOPProof, SubClaim},
|
structs::{DomainInfo, IOPProof, IOPProverState, IOPVerifierState, SubClaim},
|
||||||
transcript::IOPTranscript,
|
transcript::IOPTranscript,
|
||||||
virtual_poly::VirtualPolynomial,
|
virtual_poly::VirtualPolynomial,
|
||||||
PolyIOP,
|
PolyIOP,
|
||||||
@@ -15,9 +15,7 @@ use ark_std::{end_timer, start_timer};
|
|||||||
mod prover;
|
mod prover;
|
||||||
mod verifier;
|
mod verifier;
|
||||||
|
|
||||||
pub use prover::ProverState;
|
/// Trait for doing sum check protocols.
|
||||||
pub use verifier::VerifierState;
|
|
||||||
|
|
||||||
pub trait SumCheck<F: PrimeField> {
|
pub trait SumCheck<F: PrimeField> {
|
||||||
type Proof;
|
type Proof;
|
||||||
type PolyList;
|
type PolyList;
|
||||||
@@ -36,27 +34,15 @@ pub trait SumCheck<F: PrimeField> {
|
|||||||
/// SumCheck prover/verifier.
|
/// SumCheck prover/verifier.
|
||||||
fn init_transcript() -> Self::Transcript;
|
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
|
/// The polynomial is represented in the form of a VirtualPolynomial.
|
||||||
/// 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 prove(
|
fn prove(
|
||||||
poly: &Self::PolyList,
|
poly: &Self::PolyList,
|
||||||
transcript: &mut Self::Transcript,
|
transcript: &mut Self::Transcript,
|
||||||
) -> Result<Self::Proof, PolyIOPErrors>;
|
) -> Result<Self::Proof, PolyIOPErrors>;
|
||||||
|
|
||||||
/// verify the claimed sum using the proof
|
/// Verify the claimed sum using the proof
|
||||||
fn verify(
|
fn verify(
|
||||||
sum: F,
|
sum: F,
|
||||||
proof: &Self::Proof,
|
proof: &Self::Proof,
|
||||||
@@ -65,6 +51,7 @@ pub trait SumCheck<F: PrimeField> {
|
|||||||
) -> Result<Self::SubClaim, PolyIOPErrors>;
|
) -> Result<Self::SubClaim, PolyIOPErrors>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Trait for sum check protocol prover side APIs.
|
||||||
pub trait SumCheckProver<F: PrimeField>
|
pub trait SumCheckProver<F: PrimeField>
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
@@ -72,22 +59,10 @@ where
|
|||||||
type PolyList;
|
type PolyList;
|
||||||
type ProverMessage;
|
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`
|
/// {0,1}^`num_vars`
|
||||||
///
|
///
|
||||||
/// The polynomial is represented by a list of products of polynomials along
|
/// The polynomial is represented in the form of a VirtualPolynomial.
|
||||||
/// 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>;
|
fn prover_init(polynomial: &Self::PolyList) -> Result<Self, PolyIOPErrors>;
|
||||||
|
|
||||||
/// receive message from verifier, generate prover message, and proceed to
|
/// receive message from verifier, generate prover message, and proceed to
|
||||||
@@ -100,6 +75,7 @@ where
|
|||||||
) -> Result<Self::ProverMessage, PolyIOPErrors>;
|
) -> Result<Self::ProverMessage, PolyIOPErrors>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Trait for sum check protocol verifier side APIs.
|
||||||
pub trait SumCheckVerifier<F: PrimeField> {
|
pub trait SumCheckVerifier<F: PrimeField> {
|
||||||
type DomainInfo;
|
type DomainInfo;
|
||||||
type ProverMessage;
|
type ProverMessage;
|
||||||
@@ -165,21 +141,9 @@ impl<F: PrimeField> SumCheck<F> for PolyIOP<F> {
|
|||||||
res
|
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
|
/// The polynomial is represented in the form of a VirtualPolynomial.
|
||||||
/// 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 prove(
|
fn prove(
|
||||||
poly: &Self::PolyList,
|
poly: &Self::PolyList,
|
||||||
transcript: &mut Self::Transcript,
|
transcript: &mut Self::Transcript,
|
||||||
@@ -188,12 +152,12 @@ impl<F: PrimeField> SumCheck<F> for PolyIOP<F> {
|
|||||||
|
|
||||||
transcript.append_domain_info(&poly.domain_info)?;
|
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 challenge = None;
|
||||||
let mut prover_msgs = Vec::with_capacity(poly.domain_info.num_variables);
|
let mut prover_msgs = Vec::with_capacity(poly.domain_info.num_variables);
|
||||||
for _ in 0..poly.domain_info.num_variables {
|
for _ in 0..poly.domain_info.num_variables {
|
||||||
let prover_msg =
|
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)?;
|
transcript.append_prover_message(&prover_msg)?;
|
||||||
prover_msgs.push(prover_msg);
|
prover_msgs.push(prover_msg);
|
||||||
challenge = Some(transcript.get_and_append_challenge(b"Internal round")?);
|
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");
|
let start = start_timer!(|| "sum check verify");
|
||||||
|
|
||||||
transcript.append_domain_info(domain_info)?;
|
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 {
|
for i in 0..domain_info.num_variables {
|
||||||
let prover_msg = proof.proofs.get(i).expect("proof is incomplete");
|
let prover_msg = proof.proofs.get(i).expect("proof is incomplete");
|
||||||
transcript.append_prover_message(prover_msg)?;
|
transcript.append_prover_message(prover_msg)?;
|
||||||
VerifierState::verify_round_and_update_state(
|
IOPVerifierState::verify_round_and_update_state(
|
||||||
&mut verifier_state,
|
&mut verifier_state,
|
||||||
prover_msg,
|
prover_msg,
|
||||||
transcript,
|
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);
|
end_timer!(start);
|
||||||
res
|
res
|
||||||
@@ -237,41 +201,49 @@ impl<F: PrimeField> SumCheck<F> for PolyIOP<F> {
|
|||||||
mod test {
|
mod test {
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::virtual_poly::test::random_list_of_products;
|
|
||||||
use ark_bls12_381::Fr;
|
use ark_bls12_381::Fr;
|
||||||
use ark_ff::UniformRand;
|
use ark_ff::UniformRand;
|
||||||
use ark_poly::{DenseMultilinearExtension, MultilinearExtension};
|
use ark_poly::{DenseMultilinearExtension, MultilinearExtension};
|
||||||
use ark_std::test_rng;
|
use ark_std::test_rng;
|
||||||
use std::rc::Rc;
|
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 rng = test_rng();
|
||||||
let mut transcript = PolyIOP::init_transcript();
|
let mut transcript = <PolyIOP<Fr> as SumCheck<Fr>>::init_transcript();
|
||||||
|
|
||||||
let (poly, asserted_sum) =
|
let (poly, asserted_sum) =
|
||||||
random_list_of_products::<Fr, _>(nv, num_multiplicands_range, num_products, &mut rng);
|
VirtualPolynomial::rand(nv, num_multiplicands_range, num_products, &mut rng)?;
|
||||||
let proof = PolyIOP::prove(&poly, &mut transcript).expect("fail to prove");
|
let proof = <PolyIOP<Fr> as SumCheck<Fr>>::prove(&poly, &mut transcript)?;
|
||||||
let poly_info = poly.domain_info.clone();
|
let poly_info = poly.domain_info.clone();
|
||||||
let mut transcript = PolyIOP::init_transcript();
|
let mut transcript = <PolyIOP<Fr> as SumCheck<Fr>>::init_transcript();
|
||||||
let subclaim = PolyIOP::verify(asserted_sum, &proof, &poly_info, &mut transcript)
|
let subclaim = <PolyIOP<Fr> as SumCheck<Fr>>::verify(
|
||||||
.expect("fail to verify");
|
asserted_sum,
|
||||||
|
&proof,
|
||||||
|
&poly_info,
|
||||||
|
&mut transcript,
|
||||||
|
)?;
|
||||||
assert!(
|
assert!(
|
||||||
poly.evaluate(&subclaim.point).unwrap() == subclaim.expected_evaluation,
|
poly.evaluate(&subclaim.point).unwrap() == subclaim.expected_evaluation,
|
||||||
"wrong subclaim"
|
"wrong subclaim"
|
||||||
);
|
);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn test_sumcheck_internal(
|
fn test_sumcheck_internal(
|
||||||
nv: usize,
|
nv: usize,
|
||||||
num_multiplicands_range: (usize, usize),
|
num_multiplicands_range: (usize, usize),
|
||||||
num_products: usize,
|
num_products: usize,
|
||||||
) {
|
) -> Result<(), PolyIOPErrors> {
|
||||||
let mut rng = test_rng();
|
let mut rng = test_rng();
|
||||||
let (poly, asserted_sum) =
|
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 poly_info = poly.domain_info.clone();
|
||||||
let mut prover_state = ProverState::prover_init(&poly).unwrap();
|
let mut prover_state = IOPProverState::prover_init(&poly)?;
|
||||||
let mut verifier_state = VerifierState::verifier_init(&poly_info);
|
let mut verifier_state = IOPVerifierState::verifier_init(&poly_info);
|
||||||
let mut challenge = None;
|
let mut challenge = None;
|
||||||
let mut transcript = IOPTranscript::new(b"a test transcript");
|
let mut transcript = IOPTranscript::new(b"a test transcript");
|
||||||
transcript
|
transcript
|
||||||
@@ -279,10 +251,11 @@ mod test {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
for _ in 0..poly.domain_info.num_variables {
|
for _ in 0..poly.domain_info.num_variables {
|
||||||
let prover_message =
|
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(
|
challenge = Some(
|
||||||
VerifierState::verify_round_and_update_state(
|
IOPVerifierState::verify_round_and_update_state(
|
||||||
&mut verifier_state,
|
&mut verifier_state,
|
||||||
&prover_message,
|
&prover_message,
|
||||||
&mut transcript,
|
&mut transcript,
|
||||||
@@ -290,115 +263,120 @@ mod test {
|
|||||||
.unwrap(),
|
.unwrap(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let subclaim = VerifierState::check_and_generate_subclaim(&verifier_state, &asserted_sum)
|
let subclaim =
|
||||||
.expect("fail to generate subclaim");
|
IOPVerifierState::check_and_generate_subclaim(&verifier_state, &asserted_sum)
|
||||||
|
.expect("fail to generate subclaim");
|
||||||
assert!(
|
assert!(
|
||||||
poly.evaluate(&subclaim.point).unwrap() == subclaim.expected_evaluation,
|
poly.evaluate(&subclaim.point).unwrap() == subclaim.expected_evaluation,
|
||||||
"wrong subclaim"
|
"wrong subclaim"
|
||||||
);
|
);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_trivial_polynomial() {
|
fn test_trivial_polynomial() -> Result<(), PolyIOPErrors> {
|
||||||
let nv = 1;
|
let nv = 1;
|
||||||
let num_multiplicands_range = (4, 13);
|
let num_multiplicands_range = (4, 13);
|
||||||
let num_products = 5;
|
let num_products = 5;
|
||||||
|
|
||||||
test_sumcheck(nv, num_multiplicands_range, num_products);
|
test_sumcheck(nv, num_multiplicands_range, num_products)?;
|
||||||
test_sumcheck_internal(nv, num_multiplicands_range, num_products);
|
test_sumcheck_internal(nv, num_multiplicands_range, num_products)
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_normal_polynomial() {
|
fn test_normal_polynomial() -> Result<(), PolyIOPErrors> {
|
||||||
let nv = 12;
|
let nv = 12;
|
||||||
let num_multiplicands_range = (4, 9);
|
let num_multiplicands_range = (4, 9);
|
||||||
let num_products = 5;
|
let num_products = 5;
|
||||||
|
|
||||||
test_sumcheck(nv, num_multiplicands_range, num_products);
|
test_sumcheck(nv, num_multiplicands_range, num_products)?;
|
||||||
test_sumcheck_internal(nv, num_multiplicands_range, num_products);
|
test_sumcheck_internal(nv, num_multiplicands_range, num_products)
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic]
|
|
||||||
fn zero_polynomial_should_error() {
|
fn zero_polynomial_should_error() {
|
||||||
let nv = 0;
|
let nv = 0;
|
||||||
let num_multiplicands_range = (4, 13);
|
let num_multiplicands_range = (4, 13);
|
||||||
let num_products = 5;
|
let num_products = 5;
|
||||||
|
|
||||||
test_sumcheck(nv, num_multiplicands_range, num_products);
|
assert!(test_sumcheck(nv, num_multiplicands_range, num_products).is_err());
|
||||||
test_sumcheck_internal(nv, num_multiplicands_range, num_products);
|
assert!(test_sumcheck_internal(nv, num_multiplicands_range, num_products).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_extract_sum() {
|
fn test_extract_sum() -> Result<(), PolyIOPErrors> {
|
||||||
let mut rng = test_rng();
|
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, _>(8, (3, 4), 3, &mut rng);
|
let (poly, asserted_sum) = VirtualPolynomial::<Fr>::rand(8, (3, 4), 3, &mut rng)?;
|
||||||
|
|
||||||
let proof = PolyIOP::prove(&poly, &mut transcript).expect("fail to prove");
|
let proof = <PolyIOP<Fr> as SumCheck<Fr>>::prove(&poly, &mut transcript)?;
|
||||||
assert_eq!(PolyIOP::extract_sum(&proof), asserted_sum);
|
assert_eq!(
|
||||||
|
<PolyIOP<Fr> as SumCheck<Fr>>::extract_sum(&proof),
|
||||||
|
asserted_sum
|
||||||
|
);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
/// Test that the memory usage of shared-reference is linear to number of
|
/// Test that the memory usage of shared-reference is linear to number of
|
||||||
/// unique MLExtensions instead of total number of multiplicands.
|
/// unique MLExtensions instead of total number of multiplicands.
|
||||||
fn test_shared_reference() {
|
fn test_shared_reference() -> Result<(), PolyIOPErrors> {
|
||||||
let mut rng = test_rng();
|
let mut rng = test_rng();
|
||||||
let ml_extensions: Vec<_> = (0..5)
|
let ml_extensions: Vec<_> = (0..5)
|
||||||
.map(|_| Rc::new(DenseMultilinearExtension::<Fr>::rand(8, &mut rng)))
|
.map(|_| Rc::new(DenseMultilinearExtension::<Fr>::rand(8, &mut rng)))
|
||||||
.collect();
|
.collect();
|
||||||
let mut poly = VirtualPolynomial::new(8);
|
let mut poly = VirtualPolynomial::new(8);
|
||||||
poly.add_product(
|
poly.add_mle_list(
|
||||||
vec![
|
vec![
|
||||||
ml_extensions[2].clone(),
|
ml_extensions[2].clone(),
|
||||||
ml_extensions[3].clone(),
|
ml_extensions[3].clone(),
|
||||||
ml_extensions[0].clone(),
|
ml_extensions[0].clone(),
|
||||||
],
|
],
|
||||||
Fr::rand(&mut rng),
|
Fr::rand(&mut rng),
|
||||||
)
|
)?;
|
||||||
.unwrap();
|
poly.add_mle_list(
|
||||||
poly.add_product(
|
|
||||||
vec![
|
vec![
|
||||||
ml_extensions[1].clone(),
|
ml_extensions[1].clone(),
|
||||||
ml_extensions[4].clone(),
|
ml_extensions[4].clone(),
|
||||||
ml_extensions[4].clone(),
|
ml_extensions[4].clone(),
|
||||||
],
|
],
|
||||||
Fr::rand(&mut rng),
|
Fr::rand(&mut rng),
|
||||||
)
|
)?;
|
||||||
.unwrap();
|
poly.add_mle_list(
|
||||||
poly.add_product(
|
|
||||||
vec![
|
vec![
|
||||||
ml_extensions[3].clone(),
|
ml_extensions[3].clone(),
|
||||||
ml_extensions[2].clone(),
|
ml_extensions[2].clone(),
|
||||||
ml_extensions[1].clone(),
|
ml_extensions[1].clone(),
|
||||||
],
|
],
|
||||||
Fr::rand(&mut rng),
|
Fr::rand(&mut rng),
|
||||||
)
|
)?;
|
||||||
.unwrap();
|
poly.add_mle_list(
|
||||||
poly.add_product(
|
|
||||||
vec![ml_extensions[0].clone(), ml_extensions[0].clone()],
|
vec![ml_extensions[0].clone(), ml_extensions[0].clone()],
|
||||||
Fr::rand(&mut rng),
|
Fr::rand(&mut rng),
|
||||||
)
|
)?;
|
||||||
.unwrap();
|
poly.add_mle_list(vec![ml_extensions[4].clone()], Fr::rand(&mut rng))?;
|
||||||
poly.add_product(vec![ml_extensions[4].clone()], Fr::rand(&mut rng))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
assert_eq!(poly.flattened_ml_extensions.len(), 5);
|
assert_eq!(poly.flattened_ml_extensions.len(), 5);
|
||||||
|
|
||||||
// test memory usage for prover
|
// 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);
|
assert_eq!(prover.poly.flattened_ml_extensions.len(), 5);
|
||||||
drop(prover);
|
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 poly_info = poly.domain_info.clone();
|
||||||
let proof = PolyIOP::prove(&poly, &mut transcript).expect("fail to prove");
|
let proof = <PolyIOP<Fr> as SumCheck<Fr>>::prove(&poly, &mut transcript)?;
|
||||||
let asserted_sum = PolyIOP::extract_sum(&proof);
|
let asserted_sum = <PolyIOP<Fr> as SumCheck<Fr>>::extract_sum(&proof);
|
||||||
|
|
||||||
let mut transcript = PolyIOP::init_transcript();
|
let mut transcript = <PolyIOP<Fr> as SumCheck<Fr>>::init_transcript();
|
||||||
let subclaim = PolyIOP::verify(asserted_sum, &proof, &poly_info, &mut transcript)
|
let subclaim = <PolyIOP<Fr> as SumCheck<Fr>>::verify(
|
||||||
.expect("fail to verify");
|
asserted_sum,
|
||||||
|
&proof,
|
||||||
|
&poly_info,
|
||||||
|
&mut transcript,
|
||||||
|
)?;
|
||||||
assert!(
|
assert!(
|
||||||
poly.evaluate(&subclaim.point).unwrap() == subclaim.expected_evaluation,
|
poly.evaluate(&subclaim.point)? == subclaim.expected_evaluation,
|
||||||
"wrong subclaim"
|
"wrong subclaim"
|
||||||
);
|
);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,49 +1,27 @@
|
|||||||
//! Prover
|
//! 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 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_ff::PrimeField;
|
||||||
use ark_poly::{DenseMultilinearExtension, MultilinearExtension};
|
use ark_poly::{DenseMultilinearExtension, MultilinearExtension};
|
||||||
use ark_std::{end_timer, start_timer, vec::Vec};
|
use ark_std::{end_timer, start_timer, vec::Vec};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
#[cfg(feature = "parallel")]
|
#[cfg(feature = "parallel")]
|
||||||
use rayon::iter::{IndexedParallelIterator, IntoParallelRefMutIterator, ParallelIterator};
|
use rayon::iter::{IndexedParallelIterator, IntoParallelRefMutIterator, ParallelIterator};
|
||||||
|
|
||||||
/// Prover State
|
impl<F: PrimeField> SumCheckProver<F> for IOPProverState<F> {
|
||||||
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> {
|
|
||||||
type PolyList = VirtualPolynomial<F>;
|
type PolyList = VirtualPolynomial<F>;
|
||||||
type ProverMessage = IOPProverMessage<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`
|
/// {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> {
|
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 {
|
if polynomial.domain_info.num_variables == 0 {
|
||||||
return Err(PolyIOPErrors::InvalidParameters(
|
return Err(PolyIOPErrors::InvalidParameters(
|
||||||
"Attempt to prove a constant.".to_string(),
|
"Attempt to prove a constant.".to_string(),
|
||||||
@@ -51,14 +29,14 @@ impl<F: PrimeField> SumCheckProver<F> for ProverState<F> {
|
|||||||
}
|
}
|
||||||
end_timer!(start);
|
end_timer!(start);
|
||||||
|
|
||||||
Ok(ProverState {
|
Ok(Self {
|
||||||
challenges: Vec::with_capacity(polynomial.domain_info.num_variables),
|
challenges: Vec::with_capacity(polynomial.domain_info.num_variables),
|
||||||
round: 0,
|
round: 0,
|
||||||
poly: polynomial.clone(),
|
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
|
/// next round
|
||||||
///
|
///
|
||||||
/// Main algorithm used is from section 3.2 of [XZZPS19](https://eprint.iacr.org/2019/317.pdf#subsection.3.2).
|
/// 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,
|
&mut self,
|
||||||
challenge: &Option<F>,
|
challenge: &Option<F>,
|
||||||
) -> Result<Self::ProverMessage, PolyIOPErrors> {
|
) -> 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");
|
let fix_argument = start_timer!(|| "fix argument");
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
use super::SumCheckVerifier;
|
use super::SumCheckVerifier;
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::PolyIOPErrors,
|
errors::PolyIOPErrors,
|
||||||
structs::{DomainInfo, IOPProverMessage, SubClaim},
|
structs::{DomainInfo, IOPProverMessage, IOPVerifierState, SubClaim},
|
||||||
transcript::IOPTranscript,
|
transcript::IOPTranscript,
|
||||||
};
|
};
|
||||||
use ark_ff::PrimeField;
|
use ark_ff::PrimeField;
|
||||||
@@ -13,20 +13,7 @@ use ark_std::{end_timer, start_timer};
|
|||||||
#[cfg(feature = "parallel")]
|
#[cfg(feature = "parallel")]
|
||||||
use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator};
|
use rayon::iter::{IndexedParallelIterator, IntoParallelIterator, ParallelIterator};
|
||||||
|
|
||||||
/// Verifier State
|
impl<F: PrimeField> SumCheckVerifier<F> for IOPVerifierState<F> {
|
||||||
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> {
|
|
||||||
type DomainInfo = DomainInfo<F>;
|
type DomainInfo = DomainInfo<F>;
|
||||||
type ProverMessage = IOPProverMessage<F>;
|
type ProverMessage = IOPProverMessage<F>;
|
||||||
type Challenge = F;
|
type Challenge = F;
|
||||||
@@ -35,8 +22,8 @@ impl<F: PrimeField> SumCheckVerifier<F> for VerifierState<F> {
|
|||||||
|
|
||||||
/// initialize the verifier
|
/// initialize the verifier
|
||||||
fn verifier_init(index_info: &Self::DomainInfo) -> Self {
|
fn verifier_init(index_info: &Self::DomainInfo) -> Self {
|
||||||
let start = start_timer!(|| "verifier init");
|
let start = start_timer!(|| "sum check verifier init");
|
||||||
let res = VerifierState {
|
let res = Self {
|
||||||
round: 1,
|
round: 1,
|
||||||
num_vars: index_info.num_variables,
|
num_vars: index_info.num_variables,
|
||||||
max_degree: index_info.max_degree,
|
max_degree: index_info.max_degree,
|
||||||
@@ -59,7 +46,8 @@ impl<F: PrimeField> SumCheckVerifier<F> for VerifierState<F> {
|
|||||||
prover_msg: &Self::ProverMessage,
|
prover_msg: &Self::ProverMessage,
|
||||||
transcript: &mut Self::Transcript,
|
transcript: &mut Self::Transcript,
|
||||||
) -> Result<Self::Challenge, PolyIOPErrors> {
|
) -> 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 {
|
if self.finished {
|
||||||
return Err(PolyIOPErrors::InvalidVerifier(
|
return Err(PolyIOPErrors::InvalidVerifier(
|
||||||
@@ -101,7 +89,7 @@ impl<F: PrimeField> SumCheckVerifier<F> for VerifierState<F> {
|
|||||||
&self,
|
&self,
|
||||||
asserted_sum: &F,
|
asserted_sum: &F,
|
||||||
) -> Result<Self::SubClaim, PolyIOPErrors> {
|
) -> 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 {
|
if !self.finished {
|
||||||
return Err(PolyIOPErrors::InvalidVerifier(
|
return Err(PolyIOPErrors::InvalidVerifier(
|
||||||
"Incorrect verifier state: Verifier has not finished.".to_string(),
|
"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`.
|
/// polynomial at `eval_at`.
|
||||||
pub(crate) fn interpolate_uni_poly<F: PrimeField>(p_i: &[F], eval_at: F) -> F {
|
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 result = F::zero();
|
||||||
let mut i = F::zero();
|
let mut i = F::zero();
|
||||||
for term in p_i.iter() {
|
for term in p_i.iter() {
|
||||||
|
|||||||
@@ -1,10 +1,40 @@
|
|||||||
use crate::{errors::PolyIOPErrors, structs::DomainInfo};
|
use crate::{errors::PolyIOPErrors, structs::DomainInfo};
|
||||||
use ark_ff::PrimeField;
|
use ark_ff::PrimeField;
|
||||||
use ark_poly::{DenseMultilinearExtension, MultilinearExtension};
|
use ark_poly::{DenseMultilinearExtension, MultilinearExtension};
|
||||||
use ark_std::{end_timer, start_timer};
|
use ark_std::{
|
||||||
use std::{cmp::max, collections::HashMap, marker::PhantomData, rc::Rc};
|
end_timer,
|
||||||
|
rand::{Rng, RngCore},
|
||||||
|
start_timer,
|
||||||
|
};
|
||||||
|
use std::{cmp::max, collections::HashMap, marker::PhantomData, ops::Add, rc::Rc};
|
||||||
|
|
||||||
/// A virtual polynomial is a list of multilinear polynomials
|
#[rustfmt::skip]
|
||||||
|
/// A virtual polynomial is a sum of products of multilinear polynomials;
|
||||||
|
/// where the multilinear polynomials are stored via their multilinear
|
||||||
|
/// extensions: `(coefficient, DenseMultilinearExtension)`
|
||||||
|
///
|
||||||
|
/// * Number of products n = `polynomial.products.len()`,
|
||||||
|
/// * Number of multiplicands of ith product m_i =
|
||||||
|
/// `polynomial.products[i].1.len()`,
|
||||||
|
/// * Coefficient of ith product c_i = `polynomial.products[i].0`
|
||||||
|
///
|
||||||
|
/// The resulting polynomial is
|
||||||
|
///
|
||||||
|
/// $$\sum_{i=0}^{n} c_i \cdot \prod_{j=0}^{m_i} P_{ij} $$
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// f = c0 * f0 * f1 * f2 + c1 * f3 * f4
|
||||||
|
/// where f0 ... f4 are multilinear polynomials
|
||||||
|
///
|
||||||
|
/// - flattened_ml_extensions stores the multilinear extension representation of
|
||||||
|
/// f0, f1, f2, f3 and f4
|
||||||
|
/// - products is
|
||||||
|
/// \[
|
||||||
|
/// (c0, \[0, 1, 2\]),
|
||||||
|
/// (c1, \[3, 4\])
|
||||||
|
/// \]
|
||||||
|
/// - raw_pointers_lookup_table maps fi to i
|
||||||
|
///
|
||||||
#[derive(Clone, Debug, Default, PartialEq)]
|
#[derive(Clone, Debug, Default, PartialEq)]
|
||||||
pub struct VirtualPolynomial<F: PrimeField> {
|
pub struct VirtualPolynomial<F: PrimeField> {
|
||||||
/// Aux information about the multilinear polynomial
|
/// Aux information about the multilinear polynomial
|
||||||
@@ -18,6 +48,26 @@ pub struct VirtualPolynomial<F: PrimeField> {
|
|||||||
raw_pointers_lookup_table: HashMap<*const DenseMultilinearExtension<F>, usize>,
|
raw_pointers_lookup_table: HashMap<*const DenseMultilinearExtension<F>, usize>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<F: PrimeField> Add for &VirtualPolynomial<F> {
|
||||||
|
type Output = VirtualPolynomial<F>;
|
||||||
|
fn add(self, other: &VirtualPolynomial<F>) -> Self::Output {
|
||||||
|
let start = start_timer!(|| "virtual poly add");
|
||||||
|
let mut res = self.clone();
|
||||||
|
for products in other.products.iter() {
|
||||||
|
let cur: Vec<Rc<DenseMultilinearExtension<F>>> = products
|
||||||
|
.1
|
||||||
|
.iter()
|
||||||
|
.map(|&x| other.flattened_ml_extensions[x].clone())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
res.add_mle_list(cur, products.0)
|
||||||
|
.expect("add product failed");
|
||||||
|
}
|
||||||
|
end_timer!(start);
|
||||||
|
res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<F: PrimeField> VirtualPolynomial<F> {
|
impl<F: PrimeField> VirtualPolynomial<F> {
|
||||||
/// Returns an empty polynomial
|
/// Returns an empty polynomial
|
||||||
pub fn new(num_variables: usize) -> Self {
|
pub fn new(num_variables: usize) -> Self {
|
||||||
@@ -33,33 +83,52 @@ impl<F: PrimeField> VirtualPolynomial<F> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns an new virtual polynomial from a MLE
|
||||||
|
pub fn new_from_mle(mle: Rc<DenseMultilinearExtension<F>>, coefficient: F) -> Self {
|
||||||
|
let mle_ptr: *const DenseMultilinearExtension<F> = Rc::as_ptr(&mle);
|
||||||
|
let mut hm = HashMap::new();
|
||||||
|
hm.insert(mle_ptr, 0);
|
||||||
|
|
||||||
|
VirtualPolynomial {
|
||||||
|
domain_info: DomainInfo {
|
||||||
|
// The max degree is the max degree of any individual variable
|
||||||
|
max_degree: 1,
|
||||||
|
num_variables: mle.num_vars,
|
||||||
|
phantom: PhantomData::default(),
|
||||||
|
},
|
||||||
|
products: vec![(coefficient, vec![0])],
|
||||||
|
flattened_ml_extensions: vec![mle],
|
||||||
|
raw_pointers_lookup_table: hm,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Add a list of multilinear extensions that is meant to be multiplied
|
/// Add a list of multilinear extensions that is meant to be multiplied
|
||||||
/// together. The resulting polynomial will be multiplied by the scalar
|
/// together. The resulting polynomial will be multiplied by the scalar
|
||||||
/// `coefficient`.
|
/// `coefficient`.
|
||||||
pub fn add_product(
|
pub fn add_mle_list(
|
||||||
&mut self,
|
&mut self,
|
||||||
product: impl IntoIterator<Item = Rc<DenseMultilinearExtension<F>>>,
|
mle_list: impl IntoIterator<Item = Rc<DenseMultilinearExtension<F>>>,
|
||||||
coefficient: F,
|
coefficient: F,
|
||||||
) -> Result<(), PolyIOPErrors> {
|
) -> Result<(), PolyIOPErrors> {
|
||||||
let product: Vec<Rc<DenseMultilinearExtension<F>>> = product.into_iter().collect();
|
let mle_list: Vec<Rc<DenseMultilinearExtension<F>>> = mle_list.into_iter().collect();
|
||||||
let mut indexed_product = Vec::with_capacity(product.len());
|
let mut indexed_product = Vec::with_capacity(mle_list.len());
|
||||||
assert!(!product.is_empty());
|
assert!(!mle_list.is_empty());
|
||||||
self.domain_info.max_degree = max(self.domain_info.max_degree, product.len());
|
self.domain_info.max_degree = max(self.domain_info.max_degree, mle_list.len());
|
||||||
for m in product {
|
for mle in mle_list {
|
||||||
if m.num_vars != self.domain_info.num_variables {
|
if mle.num_vars != self.domain_info.num_variables {
|
||||||
return Err(PolyIOPErrors::InvalidParameters(format!(
|
return Err(PolyIOPErrors::InvalidParameters(format!(
|
||||||
"product has a multiplicand with wrong number of variables {} vs {}",
|
"product has a multiplicand with wrong number of variables {} vs {}",
|
||||||
m.num_vars, self.domain_info.num_variables
|
mle.num_vars, self.domain_info.num_variables
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let m_ptr: *const DenseMultilinearExtension<F> = Rc::as_ptr(&m);
|
let mle_ptr: *const DenseMultilinearExtension<F> = Rc::as_ptr(&mle);
|
||||||
if let Some(index) = self.raw_pointers_lookup_table.get(&m_ptr) {
|
if let Some(index) = self.raw_pointers_lookup_table.get(&mle_ptr) {
|
||||||
indexed_product.push(*index)
|
indexed_product.push(*index)
|
||||||
} else {
|
} else {
|
||||||
let curr_index = self.flattened_ml_extensions.len();
|
let curr_index = self.flattened_ml_extensions.len();
|
||||||
self.flattened_ml_extensions.push(m.clone());
|
self.flattened_ml_extensions.push(mle.clone());
|
||||||
self.raw_pointers_lookup_table.insert(m_ptr, curr_index);
|
self.raw_pointers_lookup_table.insert(mle_ptr, curr_index);
|
||||||
indexed_product.push(curr_index);
|
indexed_product.push(curr_index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -67,9 +136,39 @@ impl<F: PrimeField> VirtualPolynomial<F> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Multiple the current VirtualPolynomial by an MLE:
|
||||||
|
/// - add the MLE to the MLE list
|
||||||
|
/// - multiple each product by MLE and its coefficient
|
||||||
|
pub fn mul_by_mle(
|
||||||
|
&mut self,
|
||||||
|
mle: Rc<DenseMultilinearExtension<F>>,
|
||||||
|
coefficient: F,
|
||||||
|
) -> Result<(), PolyIOPErrors> {
|
||||||
|
let start = start_timer!(|| "mul by mle");
|
||||||
|
let mle_ptr: *const DenseMultilinearExtension<F> = Rc::as_ptr(&mle);
|
||||||
|
|
||||||
|
let mle_index = match self.raw_pointers_lookup_table.get(&mle_ptr) {
|
||||||
|
Some(&p) => p,
|
||||||
|
None => {
|
||||||
|
self.raw_pointers_lookup_table
|
||||||
|
.insert(mle_ptr, self.flattened_ml_extensions.len());
|
||||||
|
self.flattened_ml_extensions.push(mle);
|
||||||
|
self.flattened_ml_extensions.len() - 1
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (prod_coef, indices) in self.products.iter_mut() {
|
||||||
|
indices.push(mle_index);
|
||||||
|
*prod_coef *= coefficient;
|
||||||
|
}
|
||||||
|
self.domain_info.max_degree += 1;
|
||||||
|
end_timer!(start);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
/// Evaluate the polynomial at point `point`
|
/// Evaluate the polynomial at point `point`
|
||||||
pub fn evaluate(&self, point: &[F]) -> Result<F, PolyIOPErrors> {
|
pub fn evaluate(&self, point: &[F]) -> Result<F, PolyIOPErrors> {
|
||||||
let start = start_timer!(|| "begin evaluation");
|
let start = start_timer!(|| "evaluation");
|
||||||
|
|
||||||
if self.domain_info.num_variables != point.len() {
|
if self.domain_info.num_variables != point.len() {
|
||||||
return Err(PolyIOPErrors::InvalidParameters(format!(
|
return Err(PolyIOPErrors::InvalidParameters(format!(
|
||||||
@@ -98,114 +197,171 @@ impl<F: PrimeField> VirtualPolynomial<F> {
|
|||||||
end_timer!(start);
|
end_timer!(start);
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
/// Sample a random virtual polynomial, return the polynomial and its sum.
|
||||||
pub(crate) mod test {
|
pub fn rand<R: RngCore>(
|
||||||
use super::*;
|
|
||||||
use ark_std::rand::{Rng, RngCore};
|
|
||||||
|
|
||||||
pub fn random_product<F: PrimeField, R: RngCore>(
|
|
||||||
nv: usize,
|
|
||||||
num_multiplicands: usize,
|
|
||||||
rng: &mut R,
|
|
||||||
) -> (Vec<Rc<DenseMultilinearExtension<F>>>, F) {
|
|
||||||
let mut multiplicands = Vec::with_capacity(num_multiplicands);
|
|
||||||
for _ in 0..num_multiplicands {
|
|
||||||
multiplicands.push(Vec::with_capacity(1 << nv))
|
|
||||||
}
|
|
||||||
let mut sum = F::zero();
|
|
||||||
|
|
||||||
for _ in 0..(1 << nv) {
|
|
||||||
let mut product = F::one();
|
|
||||||
for i in 0..num_multiplicands {
|
|
||||||
let val = F::rand(rng);
|
|
||||||
multiplicands[i].push(val);
|
|
||||||
product *= val;
|
|
||||||
}
|
|
||||||
sum += product;
|
|
||||||
}
|
|
||||||
|
|
||||||
(
|
|
||||||
multiplicands
|
|
||||||
.into_iter()
|
|
||||||
.map(|x| Rc::new(DenseMultilinearExtension::from_evaluations_vec(nv, x)))
|
|
||||||
.collect(),
|
|
||||||
sum,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn random_list_of_products<F: PrimeField, R: RngCore>(
|
|
||||||
nv: usize,
|
nv: usize,
|
||||||
num_multiplicands_range: (usize, usize),
|
num_multiplicands_range: (usize, usize),
|
||||||
num_products: usize,
|
num_products: usize,
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
) -> (VirtualPolynomial<F>, F) {
|
) -> Result<(Self, F), PolyIOPErrors> {
|
||||||
|
let start = start_timer!("sample random virtual polynomial");
|
||||||
|
|
||||||
let mut sum = F::zero();
|
let mut sum = F::zero();
|
||||||
let mut poly = VirtualPolynomial::new(nv);
|
let mut poly = VirtualPolynomial::new(nv);
|
||||||
for _ in 0..num_products {
|
for _ in 0..num_products {
|
||||||
let num_multiplicands =
|
let num_multiplicands =
|
||||||
rng.gen_range(num_multiplicands_range.0..num_multiplicands_range.1);
|
rng.gen_range(num_multiplicands_range.0..num_multiplicands_range.1);
|
||||||
let (product, product_sum) = random_product(nv, num_multiplicands, rng);
|
let (product, product_sum) = random_mle_list(nv, num_multiplicands, rng);
|
||||||
let coefficient = F::rand(rng);
|
let coefficient = F::rand(rng);
|
||||||
poly.add_product(product.into_iter(), coefficient).unwrap();
|
poly.add_mle_list(product.into_iter(), coefficient)?;
|
||||||
sum += product_sum * coefficient;
|
sum += product_sum * coefficient;
|
||||||
}
|
}
|
||||||
|
|
||||||
(poly, sum)
|
end_timer!(start);
|
||||||
|
Ok((poly, sum))
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub fn random_zero_product<F: PrimeField, R: RngCore>(
|
/// Sample a random virtual polynomial that evaluates to zero everywhere on
|
||||||
// nv: usize,
|
/// the boolean hypercube.
|
||||||
// num_multiplicands: usize,
|
pub fn rand_zero<R: RngCore>(
|
||||||
// rng: &mut R,
|
nv: usize,
|
||||||
// ) -> Vec<Rc<DenseMultilinearExtension<F>>> {
|
num_multiplicands_range: (usize, usize),
|
||||||
// let degree = 2;
|
num_products: usize,
|
||||||
// let mut multiplicands = Vec::with_capacity(degree);
|
rng: &mut R,
|
||||||
// for _ in 0..degree {
|
) -> Result<Self, PolyIOPErrors> {
|
||||||
// multiplicands.push(Vec::with_capacity(1 << nv))
|
let mut poly = VirtualPolynomial::new(nv);
|
||||||
// }
|
for _ in 0..num_products {
|
||||||
// let mut sum = F::zero();
|
let num_multiplicands =
|
||||||
|
rng.gen_range(num_multiplicands_range.0..num_multiplicands_range.1);
|
||||||
|
let product = random_zero_mle_list(nv, num_multiplicands, rng);
|
||||||
|
let coefficient = F::rand(rng);
|
||||||
|
poly.add_mle_list(product.into_iter(), coefficient).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
// for _ in 0..(1 << nv) {
|
Ok(poly)
|
||||||
// let mut product = F::one();
|
}
|
||||||
// for i in 0..degree {
|
}
|
||||||
// let val = F::zero(); // F::rand(rng);
|
|
||||||
// multiplicands[i].push(val);
|
/// Sample a random list of multilinear polynomials.
|
||||||
// product *= val;
|
/// Returns
|
||||||
// }
|
/// - the list of polynomials,
|
||||||
// sum += product;
|
/// - its sum of polynomial evaluations over the boolean hypercube.
|
||||||
// }
|
fn random_mle_list<F: PrimeField, R: RngCore>(
|
||||||
|
nv: usize,
|
||||||
// // // last nv offsets the poly to 0
|
degree: usize,
|
||||||
// // for i in 0..num_multiplicands - 1 {
|
rng: &mut R,
|
||||||
// // multiplicands[i].push(F::one());
|
) -> (Vec<Rc<DenseMultilinearExtension<F>>>, F) {
|
||||||
// // }
|
let start = start_timer!("sample random mle list");
|
||||||
// // multiplicands[num_multiplicands - 1].push(-sum);
|
let mut multiplicands = Vec::with_capacity(degree);
|
||||||
|
for _ in 0..degree {
|
||||||
// multiplicands
|
multiplicands.push(Vec::with_capacity(1 << nv))
|
||||||
// .into_iter()
|
}
|
||||||
// .map(|x|
|
let mut sum = F::zero();
|
||||||
// Rc::new(DenseMultilinearExtension::from_evaluations_vec(nv, x)))
|
|
||||||
// .collect()
|
for _ in 0..(1 << nv) {
|
||||||
// }
|
let mut product = F::one();
|
||||||
|
|
||||||
// pub(crate) fn random_zero_list_of_products<F: PrimeField, R: RngCore>(
|
for e in multiplicands.iter_mut() {
|
||||||
// nv: usize,
|
let val = F::rand(rng);
|
||||||
// num_multiplicands_range: (usize, usize),
|
e.push(val);
|
||||||
// num_products: usize,
|
product *= val;
|
||||||
// rng: &mut R,
|
}
|
||||||
// ) -> VirtualPolynomial<F> {
|
sum += product;
|
||||||
// let mut poly = VirtualPolynomial::new(nv);
|
}
|
||||||
// for _ in 0..num_products {
|
|
||||||
// let num_multiplicands =
|
let list = multiplicands
|
||||||
//
|
.into_iter()
|
||||||
// rng.gen_range(num_multiplicands_range.0..num_multiplicands_range.1);
|
.map(|x| Rc::new(DenseMultilinearExtension::from_evaluations_vec(nv, x)))
|
||||||
// let product = random_zero_product(nv, num_multiplicands, rng);
|
.collect();
|
||||||
// let coefficient = F::rand(rng);
|
|
||||||
// poly.add_product(product.into_iter(), coefficient);
|
end_timer!(start);
|
||||||
// }
|
(list, sum)
|
||||||
|
}
|
||||||
// poly
|
|
||||||
// }
|
// Build a randomize list of mle-s whose sum is zero.
|
||||||
|
pub fn random_zero_mle_list<F: PrimeField, R: RngCore>(
|
||||||
|
nv: usize,
|
||||||
|
degree: usize,
|
||||||
|
rng: &mut R,
|
||||||
|
) -> Vec<Rc<DenseMultilinearExtension<F>>> {
|
||||||
|
let start = start_timer!("sample random zero mle list");
|
||||||
|
|
||||||
|
let mut multiplicands = Vec::with_capacity(degree);
|
||||||
|
for _ in 0..degree {
|
||||||
|
multiplicands.push(Vec::with_capacity(1 << nv))
|
||||||
|
}
|
||||||
|
for _ in 0..(1 << nv) {
|
||||||
|
multiplicands[0].push(F::zero());
|
||||||
|
for e in multiplicands.iter_mut().skip(1) {
|
||||||
|
e.push(F::rand(rng));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let list = multiplicands
|
||||||
|
.into_iter()
|
||||||
|
.map(|x| Rc::new(DenseMultilinearExtension::from_evaluations_vec(nv, x)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
end_timer!(start);
|
||||||
|
list
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub(crate) mod test {
|
||||||
|
use super::*;
|
||||||
|
use ark_bls12_381::Fr;
|
||||||
|
use ark_ff::UniformRand;
|
||||||
|
use ark_std::test_rng;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_virtual_polynomial_additions() -> Result<(), PolyIOPErrors> {
|
||||||
|
let mut rng = test_rng();
|
||||||
|
for nv in 2..5 {
|
||||||
|
for num_products in 2..5 {
|
||||||
|
let base: Vec<Fr> = (0..nv).map(|_| Fr::rand(&mut rng)).collect();
|
||||||
|
|
||||||
|
let (a, _a_sum) =
|
||||||
|
VirtualPolynomial::<Fr>::rand(nv, (2, 3), num_products, &mut rng)?;
|
||||||
|
let (b, _b_sum) =
|
||||||
|
VirtualPolynomial::<Fr>::rand(nv, (2, 3), num_products, &mut rng)?;
|
||||||
|
let c = &a + &b;
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
a.evaluate(base.as_ref())? + b.evaluate(base.as_ref())?,
|
||||||
|
c.evaluate(base.as_ref())?
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_virtual_polynomial_mul_by_mle() -> Result<(), PolyIOPErrors> {
|
||||||
|
let mut rng = test_rng();
|
||||||
|
for nv in 2..5 {
|
||||||
|
for num_products in 2..5 {
|
||||||
|
let base: Vec<Fr> = (0..nv).map(|_| Fr::rand(&mut rng)).collect();
|
||||||
|
|
||||||
|
let (a, _a_sum) =
|
||||||
|
VirtualPolynomial::<Fr>::rand(nv, (2, 3), num_products, &mut rng)?;
|
||||||
|
let (b, _b_sum) = random_mle_list(nv, 1, &mut rng);
|
||||||
|
let b_mle = b[0].clone();
|
||||||
|
let coeff = Fr::rand(&mut rng);
|
||||||
|
let b_vp = VirtualPolynomial::new_from_mle(b_mle.clone(), coeff);
|
||||||
|
|
||||||
|
let mut c = a.clone();
|
||||||
|
|
||||||
|
c.mul_by_mle(b_mle, coeff)?;
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
a.evaluate(base.as_ref())? * b_vp.evaluate(base.as_ref())?,
|
||||||
|
c.evaluate(base.as_ref())?
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
mod prover;
|
|
||||||
mod verifier;
|
|
||||||
|
|
||||||
use ark_ff::PrimeField;
|
use ark_ff::PrimeField;
|
||||||
use ark_poly::DenseMultilinearExtension;
|
use ark_poly::DenseMultilinearExtension;
|
||||||
pub use prover::ProverState;
|
use ark_std::{end_timer, start_timer};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
pub use verifier::VerifierState;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::PolyIOPErrors,
|
errors::PolyIOPErrors,
|
||||||
@@ -31,6 +27,8 @@ pub trait ZeroCheck<F: PrimeField> {
|
|||||||
/// ZeroCheck prover/verifier.
|
/// ZeroCheck prover/verifier.
|
||||||
fn init_transcript() -> Self::Transcript;
|
fn init_transcript() -> Self::Transcript;
|
||||||
|
|
||||||
|
/// initialize the prover to argue for the sum of polynomial over
|
||||||
|
/// {0,1}^`num_vars` is zero.
|
||||||
fn prove(
|
fn prove(
|
||||||
poly: &Self::PolyList,
|
poly: &Self::PolyList,
|
||||||
transcript: &mut Self::Transcript,
|
transcript: &mut Self::Transcript,
|
||||||
@@ -48,7 +46,11 @@ impl<F: PrimeField> ZeroCheck<F> for PolyIOP<F> {
|
|||||||
type Proof = IOPProof<F>;
|
type Proof = IOPProof<F>;
|
||||||
type PolyList = VirtualPolynomial<F>;
|
type PolyList = VirtualPolynomial<F>;
|
||||||
type DomainInfo = DomainInfo<F>;
|
type DomainInfo = DomainInfo<F>;
|
||||||
type SubClaim = SubClaim<F>;
|
|
||||||
|
/// A ZeroCheck SubClaim consists of
|
||||||
|
/// - the SubClaim from the ZeroCheck
|
||||||
|
/// - the initial challenge r which is used to build eq(x, r)
|
||||||
|
type SubClaim = (SubClaim<F>, Vec<F>);
|
||||||
type Transcript = IOPTranscript<F>;
|
type Transcript = IOPTranscript<F>;
|
||||||
|
|
||||||
/// Initialize the system with a transcript
|
/// Initialize the system with a transcript
|
||||||
@@ -61,29 +63,53 @@ impl<F: PrimeField> ZeroCheck<F> for PolyIOP<F> {
|
|||||||
IOPTranscript::<F>::new(b"Initializing ZeroCheck transcript")
|
IOPTranscript::<F>::new(b"Initializing ZeroCheck transcript")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// initialize the prover to argue for the sum of polynomial over
|
||||||
|
/// {0,1}^`num_vars` is zero.
|
||||||
fn prove(
|
fn prove(
|
||||||
poly: &Self::PolyList,
|
poly: &Self::PolyList,
|
||||||
transcript: &mut Self::Transcript,
|
transcript: &mut Self::Transcript,
|
||||||
) -> Result<Self::Proof, PolyIOPErrors> {
|
) -> Result<Self::Proof, PolyIOPErrors> {
|
||||||
|
let start = start_timer!(|| "zero check prove");
|
||||||
|
|
||||||
let length = poly.domain_info.num_variables;
|
let length = poly.domain_info.num_variables;
|
||||||
let r = transcript.get_and_append_challenge_vectors(b"vector r", length)?;
|
let r = transcript.get_and_append_challenge_vectors(b"vector r", length)?;
|
||||||
let f_hat = build_f_hat(poly, r.as_ref());
|
let f_hat = build_f_hat(poly, r.as_ref())?;
|
||||||
<Self as SumCheck<F>>::prove(&f_hat, transcript)
|
let res = <Self as SumCheck<F>>::prove(&f_hat, transcript);
|
||||||
|
|
||||||
|
end_timer!(start);
|
||||||
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verify the claimed sum using the proof.
|
/// Verify the claimed sum using the proof.
|
||||||
/// Caller needs to makes sure that `\hat f = f * eq(x, r)`
|
/// the initial challenge `r` is also returned.
|
||||||
|
/// The caller needs to makes sure that `\hat f = f * eq(x, r)`
|
||||||
fn verify(
|
fn verify(
|
||||||
proof: &Self::Proof,
|
proof: &Self::Proof,
|
||||||
domain_info: &Self::DomainInfo,
|
fx_domain_info: &Self::DomainInfo,
|
||||||
transcript: &mut Self::Transcript,
|
transcript: &mut Self::Transcript,
|
||||||
) -> Result<Self::SubClaim, PolyIOPErrors> {
|
) -> Result<Self::SubClaim, PolyIOPErrors> {
|
||||||
println!(
|
let start = start_timer!(|| "zero check verify");
|
||||||
"sum: {}",
|
|
||||||
proof.proofs[0].evaluations[0] + proof.proofs[0].evaluations[1]
|
|
||||||
);
|
|
||||||
|
|
||||||
<Self as SumCheck<F>>::verify(F::zero(), proof, domain_info, transcript)
|
// check that the sum is zero
|
||||||
|
if proof.proofs[0].evaluations[0] + proof.proofs[0].evaluations[1] != F::zero() {
|
||||||
|
return Err(PolyIOPErrors::InvalidProof(format!(
|
||||||
|
"zero check: sum {} is not zero",
|
||||||
|
proof.proofs[0].evaluations[0] + proof.proofs[0].evaluations[1]
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate `r` and pass it to the caller for correctness check
|
||||||
|
let length = fx_domain_info.num_variables;
|
||||||
|
let r = transcript.get_and_append_challenge_vectors(b"vector r", length)?;
|
||||||
|
|
||||||
|
// hat_fx's max degree is increased by eq(x, r).degree() which is 1
|
||||||
|
let mut hat_fx_domain_info = fx_domain_info.clone();
|
||||||
|
hat_fx_domain_info.max_degree += 1;
|
||||||
|
let subclaim =
|
||||||
|
<Self as SumCheck<F>>::verify(F::zero(), proof, &hat_fx_domain_info, transcript)?;
|
||||||
|
|
||||||
|
end_timer!(start);
|
||||||
|
Ok((subclaim, r))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,39 +117,20 @@ impl<F: PrimeField> ZeroCheck<F> for PolyIOP<F> {
|
|||||||
// \hat f(x) = \sum_{x_i \in eval_x} f(x_i) eq(x, r)
|
// \hat f(x) = \sum_{x_i \in eval_x} f(x_i) eq(x, r)
|
||||||
// where
|
// where
|
||||||
// eq(x,y) = \prod_i=1^num_var (x_i * y_i + (1-x_i)*(1-y_i))
|
// eq(x,y) = \prod_i=1^num_var (x_i * y_i + (1-x_i)*(1-y_i))
|
||||||
fn build_f_hat<F: PrimeField>(poly: &VirtualPolynomial<F>, r: &[F]) -> VirtualPolynomial<F> {
|
fn build_f_hat<F: PrimeField>(
|
||||||
|
poly: &VirtualPolynomial<F>,
|
||||||
|
r: &[F],
|
||||||
|
) -> Result<VirtualPolynomial<F>, PolyIOPErrors> {
|
||||||
|
let start = start_timer!(|| "zero check build hat f");
|
||||||
|
|
||||||
assert_eq!(poly.domain_info.num_variables, r.len());
|
assert_eq!(poly.domain_info.num_variables, r.len());
|
||||||
let mut res = poly.clone();
|
|
||||||
let eq_x_r = build_eq_x_r(r);
|
let eq_x_r = build_eq_x_r(r);
|
||||||
res.add_product([eq_x_r; 1], F::one());
|
let mut res = poly.clone();
|
||||||
// // First, we build array for {1 - r_i}
|
res.mul_by_mle(eq_x_r, F::one())?;
|
||||||
// let one_minus_r: Vec<F> = r.iter().map(|ri| F::one() - ri).collect();
|
|
||||||
|
|
||||||
// let mut eval = vec![];
|
end_timer!(start);
|
||||||
// // let eq_x_r = build_eq_x_r(r);
|
Ok(res)
|
||||||
// let num_var = r.len();
|
|
||||||
// let mut res = VirtualPolynomial::new(num_var);
|
|
||||||
// // res.add_product([eq_x_r; 1], F::one());
|
|
||||||
|
|
||||||
// for i in 0..1 << num_var {
|
|
||||||
// let bit_sequence = bit_decompose(i, num_var);
|
|
||||||
// let bit_points: Vec<F> = bit_sequence.iter().map(|&x| F::from(x as
|
|
||||||
// u64)).collect(); let mut eq_eval = F::one();
|
|
||||||
// for (&bit, (ri, one_minus_ri)) in
|
|
||||||
// bit_sequence.iter().zip(r.iter().zip(one_minus_r.iter())) {
|
|
||||||
// if bit {
|
|
||||||
// eq_eval *= ri;
|
|
||||||
// } else {
|
|
||||||
// eq_eval *= one_minus_ri;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// eval.push(eq_eval * poly.evaluate(&bit_points))
|
|
||||||
// }
|
|
||||||
// let hat_f = Rc::new(DenseMultilinearExtension::from_evaluations_vec(
|
|
||||||
// num_var, eval,
|
|
||||||
// ));
|
|
||||||
// res.add_product([hat_f; 1], F::one());
|
|
||||||
res
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate
|
// Evaluate
|
||||||
@@ -131,6 +138,8 @@ fn build_f_hat<F: PrimeField>(poly: &VirtualPolynomial<F>, r: &[F]) -> VirtualPo
|
|||||||
// over r, which is
|
// over r, which is
|
||||||
// eq(x,y) = \prod_i=1^num_var (x_i * r_i + (1-x_i)*(1-r_i))
|
// eq(x,y) = \prod_i=1^num_var (x_i * r_i + (1-x_i)*(1-r_i))
|
||||||
fn build_eq_x_r<F: PrimeField>(r: &[F]) -> Rc<DenseMultilinearExtension<F>> {
|
fn build_eq_x_r<F: PrimeField>(r: &[F]) -> Rc<DenseMultilinearExtension<F>> {
|
||||||
|
let start = start_timer!(|| "zero check build build eq_x_r");
|
||||||
|
|
||||||
// we build eq(x,r) from its evaluations
|
// we build eq(x,r) from its evaluations
|
||||||
// we want to evaluate eq(x,r) over x \in {0, 1}^num_vars
|
// we want to evaluate eq(x,r) over x \in {0, 1}^num_vars
|
||||||
// for example, with num_vars = 4, x is a binary vector of 4, then
|
// for example, with num_vars = 4, x is a binary vector of 4, then
|
||||||
@@ -157,18 +166,14 @@ fn build_eq_x_r<F: PrimeField>(r: &[F]) -> Rc<DenseMultilinearExtension<F>> {
|
|||||||
|
|
||||||
for (&bit, (ri, one_minus_ri)) in bit_sequence.iter().zip(r.iter().zip(one_minus_r.iter()))
|
for (&bit, (ri, one_minus_ri)) in bit_sequence.iter().zip(r.iter().zip(one_minus_r.iter()))
|
||||||
{
|
{
|
||||||
if bit {
|
current_eval *= if bit { *ri } else { *one_minus_ri };
|
||||||
current_eval *= *ri;
|
|
||||||
} else {
|
|
||||||
current_eval *= *one_minus_ri;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
eval.push(current_eval);
|
eval.push(current_eval);
|
||||||
}
|
}
|
||||||
let res = Rc::new(DenseMultilinearExtension::from_evaluations_vec(
|
let mle = DenseMultilinearExtension::from_evaluations_vec(num_var, eval);
|
||||||
num_var, eval,
|
|
||||||
));
|
|
||||||
|
|
||||||
|
let res = Rc::new(mle);
|
||||||
|
end_timer!(start);
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,131 +191,83 @@ fn bit_decompose(input: u64, num_var: usize) -> Vec<bool> {
|
|||||||
mod test {
|
mod test {
|
||||||
|
|
||||||
use super::ZeroCheck;
|
use super::ZeroCheck;
|
||||||
use crate::{virtual_poly::test::random_zero_list_of_products, PolyIOP, VirtualPolynomial};
|
use crate::{errors::PolyIOPErrors, PolyIOP, VirtualPolynomial};
|
||||||
use ark_bls12_381::Fr;
|
use ark_bls12_381::Fr;
|
||||||
use ark_ff::UniformRand;
|
|
||||||
use ark_poly::{DenseMultilinearExtension, MultilinearExtension};
|
|
||||||
use ark_std::test_rng;
|
use ark_std::test_rng;
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
fn test_polynomial(nv: usize, num_multiplicands_range: (usize, usize), num_products: usize) {
|
fn test_zerocheck(
|
||||||
|
nv: usize,
|
||||||
|
num_multiplicands_range: (usize, usize),
|
||||||
|
num_products: usize,
|
||||||
|
) -> Result<(), PolyIOPErrors> {
|
||||||
let mut rng = test_rng();
|
let mut rng = test_rng();
|
||||||
let mut transcript = PolyIOP::init_transcript();
|
|
||||||
transcript
|
|
||||||
.append_message(b"testing", b"initializing transcript for testing")
|
|
||||||
.unwrap();
|
|
||||||
let poly = random_zero_list_of_products::<Fr, _>(
|
|
||||||
nv,
|
|
||||||
num_multiplicands_range,
|
|
||||||
num_products,
|
|
||||||
&mut rng,
|
|
||||||
);
|
|
||||||
// println!("{:?}", poly);
|
|
||||||
|
|
||||||
let proof = PolyIOP::prove(&poly, &mut transcript).expect("fail to prove");
|
{
|
||||||
println!(
|
// good path: zero virtual poly
|
||||||
"{}",
|
let poly =
|
||||||
proof.proofs[0].evaluations[0] + proof.proofs[0].evaluations[1]
|
VirtualPolynomial::rand_zero(nv, num_multiplicands_range, num_products, &mut rng)?;
|
||||||
);
|
|
||||||
|
|
||||||
let poly_info = poly.domain_info.clone();
|
let mut transcript = <PolyIOP<Fr> as ZeroCheck<Fr>>::init_transcript();
|
||||||
let mut transcript = PolyIOP::init_transcript();
|
transcript.append_message(b"testing", b"initializing transcript for testing")?;
|
||||||
transcript
|
let proof = <PolyIOP<Fr> as ZeroCheck<Fr>>::prove(&poly, &mut transcript)?;
|
||||||
.append_message(b"testing", b"initializing transcript for testing")
|
|
||||||
.unwrap();
|
let poly_info = poly.domain_info.clone();
|
||||||
let subclaim =
|
let mut transcript = <PolyIOP<Fr> as ZeroCheck<Fr>>::init_transcript();
|
||||||
PolyIOP::verify(&proof, &poly_info, &mut transcript).expect("fail to verify");
|
transcript.append_message(b"testing", b"initializing transcript for testing")?;
|
||||||
assert!(
|
let subclaim =
|
||||||
poly.evaluate(&subclaim.point) == subclaim.expected_evaluation,
|
<PolyIOP<Fr> as ZeroCheck<Fr>>::verify(&proof, &poly_info, &mut transcript)?.0;
|
||||||
"wrong subclaim"
|
assert!(
|
||||||
);
|
poly.evaluate(&subclaim.point)? == subclaim.expected_evaluation,
|
||||||
|
"wrong subclaim"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// bad path: random virtual poly whose sum is not zero
|
||||||
|
let (poly, _sum) =
|
||||||
|
VirtualPolynomial::rand(nv, num_multiplicands_range, num_products, &mut rng)?;
|
||||||
|
|
||||||
|
let mut transcript = <PolyIOP<Fr> as ZeroCheck<Fr>>::init_transcript();
|
||||||
|
transcript.append_message(b"testing", b"initializing transcript for testing")?;
|
||||||
|
let proof = <PolyIOP<Fr> as ZeroCheck<Fr>>::prove(&poly, &mut transcript)?;
|
||||||
|
|
||||||
|
let poly_info = poly.domain_info.clone();
|
||||||
|
let mut transcript = <PolyIOP<Fr> as ZeroCheck<Fr>>::init_transcript();
|
||||||
|
transcript.append_message(b"testing", b"initializing transcript for testing")?;
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
<PolyIOP<Fr> as ZeroCheck<Fr>>::verify(&proof, &poly_info, &mut transcript)
|
||||||
|
.is_err()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_trivial_polynomial() {
|
fn test_trivial_polynomial() -> Result<(), PolyIOPErrors> {
|
||||||
let nv = 1;
|
let nv = 1;
|
||||||
let num_multiplicands_range = (4, 5);
|
let num_multiplicands_range = (4, 5);
|
||||||
let num_products = 1;
|
let num_products = 1;
|
||||||
|
|
||||||
test_polynomial(nv, num_multiplicands_range, num_products);
|
test_zerocheck(nv, num_multiplicands_range, num_products)
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_normal_polynomial() {
|
fn test_normal_polynomial() -> Result<(), PolyIOPErrors> {
|
||||||
let nv = 16;
|
let nv = 5;
|
||||||
let num_multiplicands_range = (4, 9);
|
let num_multiplicands_range = (4, 9);
|
||||||
let num_products = 5;
|
let num_products = 5;
|
||||||
|
|
||||||
test_polynomial(nv, num_multiplicands_range, num_products);
|
test_zerocheck(nv, num_multiplicands_range, num_products)
|
||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic]
|
|
||||||
fn zero_polynomial_should_error() {
|
fn zero_polynomial_should_error() -> Result<(), PolyIOPErrors> {
|
||||||
let nv = 0;
|
let nv = 0;
|
||||||
let num_multiplicands_range = (4, 13);
|
let num_multiplicands_range = (4, 13);
|
||||||
let num_products = 5;
|
let num_products = 5;
|
||||||
|
|
||||||
test_polynomial(nv, num_multiplicands_range, num_products);
|
assert!(test_zerocheck(nv, num_multiplicands_range, num_products).is_err());
|
||||||
}
|
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() {
|
|
||||||
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(
|
|
||||||
vec![
|
|
||||||
ml_extensions[2].clone(),
|
|
||||||
ml_extensions[3].clone(),
|
|
||||||
ml_extensions[0].clone(),
|
|
||||||
],
|
|
||||||
Fr::rand(&mut rng),
|
|
||||||
);
|
|
||||||
poly.add_product(
|
|
||||||
vec![
|
|
||||||
ml_extensions[1].clone(),
|
|
||||||
ml_extensions[4].clone(),
|
|
||||||
ml_extensions[4].clone(),
|
|
||||||
],
|
|
||||||
Fr::rand(&mut rng),
|
|
||||||
);
|
|
||||||
poly.add_product(
|
|
||||||
vec![
|
|
||||||
ml_extensions[3].clone(),
|
|
||||||
ml_extensions[2].clone(),
|
|
||||||
ml_extensions[1].clone(),
|
|
||||||
],
|
|
||||||
Fr::rand(&mut rng),
|
|
||||||
);
|
|
||||||
poly.add_product(
|
|
||||||
vec![ml_extensions[0].clone(), ml_extensions[0].clone()],
|
|
||||||
Fr::rand(&mut rng),
|
|
||||||
);
|
|
||||||
poly.add_product(vec![ml_extensions[4].clone()], Fr::rand(&mut rng));
|
|
||||||
|
|
||||||
assert_eq!(poly.flattened_ml_extensions.len(), 5);
|
|
||||||
|
|
||||||
let mut transcript = PolyIOP::init_transcript();
|
|
||||||
|
|
||||||
transcript
|
|
||||||
.append_message(b"testing", b"initializing transcript for testing")
|
|
||||||
.unwrap();
|
|
||||||
let poly_info = poly.domain_info.clone();
|
|
||||||
let proof = PolyIOP::prove(&poly, &mut transcript).expect("fail to prove");
|
|
||||||
|
|
||||||
let mut transcript = PolyIOP::init_transcript();
|
|
||||||
|
|
||||||
transcript
|
|
||||||
.append_message(b"testing", b"initializing transcript for testing")
|
|
||||||
.unwrap();
|
|
||||||
let subclaim =
|
|
||||||
PolyIOP::verify(&proof, &poly_info, &mut transcript).expect("fail to verify");
|
|
||||||
assert!(
|
|
||||||
poly.evaluate(&subclaim.point) == subclaim.expected_evaluation,
|
|
||||||
"wrong subclaim"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
use ark_ff::PrimeField;
|
|
||||||
use ark_poly::DenseMultilinearExtension;
|
|
||||||
|
|
||||||
/// Prover State
|
|
||||||
pub struct ProverState<F: PrimeField> {
|
|
||||||
/// sampled randomness given by the verifier
|
|
||||||
pub challenges: Vec<F>,
|
|
||||||
/// Stores the list of products that is meant to be added together. Each
|
|
||||||
/// multiplicand is represented by the index in flattened_ml_extensions
|
|
||||||
pub list_of_products: Vec<(F, Vec<usize>)>,
|
|
||||||
/// Stores a list of multilinear extensions in which `self.list_of_products`
|
|
||||||
/// points to
|
|
||||||
pub flattened_ml_extensions: Vec<DenseMultilinearExtension<F>>,
|
|
||||||
pub(crate) num_vars: usize,
|
|
||||||
pub(crate) max_degree: usize,
|
|
||||||
pub(crate) round: usize,
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
use ark_ff::PrimeField;
|
|
||||||
|
|
||||||
/// Verifier State
|
|
||||||
pub struct VerifierState<F: PrimeField> {
|
|
||||||
round: usize,
|
|
||||||
nv: 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>,
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user