Skip to content

Commit

Permalink
pull ark-linear-sumcheck (#29)
Browse files Browse the repository at this point in the history
* pull ark-linear-sumcheck

* clippy

* absorb challenges

* make sumcheck::prove infallible

* add list len to verifier key

* comment
  • Loading branch information
slumber committed Nov 7, 2023
1 parent 9749dd2 commit c8fe9df
Show file tree
Hide file tree
Showing 8 changed files with 1,056 additions and 0 deletions.
1 change: 1 addition & 0 deletions supernova/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ ark-crypto-primitives = { version = "0.4.0", features = ["r1cs", "sponge"] }
ark-relations = { version = "0.4.0" }
ark-r1cs-std = { version = "0.4.0" }
ark-serialize = { version = "0.4.0", features = ["derive"] }
ark-poly = "0.4.0"

sha3 = { version = "0.10", default-features = false }

Expand Down
1 change: 1 addition & 0 deletions supernova/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#![allow(clippy::large_enum_variant)]

mod absorb;
mod ml_sumcheck;
mod multifold;
mod nifs;
mod provider;
Expand Down
126 changes: 126 additions & 0 deletions supernova/src/ml_sumcheck/data_structures.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
//! Defines the data structures used by the `MLSumcheck` protocol.

use std::collections::HashMap;

use ark_crypto_primitives::sponge::Absorb;
use ark_ff::{Field, PrimeField};
use ark_poly::{DenseMultilinearExtension, MultilinearExtension};
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
use ark_std::{cmp::max, rc::Rc};

/// Stores a list of products of `DenseMultilinearExtension` that is meant to be added together.
///
/// 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 = `self.products.len()`,
/// * Number of multiplicands of ith product m_i = `self.products[i].1.len()`,
/// * Coefficient of ith product c_i = `self.products[i].0`
///
/// The resulting polynomial is
///
/// $$\sum_{i=0}^{n}C_i\cdot\prod_{j=0}^{m_i}P_{ij}$$
///
/// The result polynomial is used as the prover key.
#[derive(Clone)]
pub struct ListOfProductsOfPolynomials<F: Field> {
/// max number of multiplicands in each product
pub max_multiplicands: usize,
/// number of variables of the polynomial
pub num_variables: usize,
/// list of reference to products (as usize) of multilinear extension
pub products: Vec<(F, Vec<usize>)>,
/// Stores multilinear extensions in which product multiplicand can refer to.
pub flattened_ml_extensions: Vec<Rc<DenseMultilinearExtension<F>>>,
raw_pointers_lookup_table: HashMap<*const DenseMultilinearExtension<F>, usize>,
}

impl<F: Field> ListOfProductsOfPolynomials<F> {
/// Extract the max number of multiplicands and number of variables of the list of products.
pub fn info(&self) -> PolynomialInfo {
PolynomialInfo {
max_multiplicands: self.max_multiplicands,
num_variables: self.num_variables,
num_terms: self.products.len(),
}
}
}

/// Stores the number of variables and max number of multiplicands of the added polynomial used by the prover.
/// This data structures will is used as the verifier key.
#[derive(CanonicalSerialize, CanonicalDeserialize, Clone)]
pub struct PolynomialInfo {
/// max number of multiplicands in each product
pub max_multiplicands: usize,
/// number of variables of the polynomial
pub num_variables: usize,
/// number of terms in the sum of multilinear products.
pub num_terms: usize,
}

impl Absorb for PolynomialInfo {
fn to_sponge_bytes(&self, dest: &mut Vec<u8>) {
unreachable!()
}

fn to_sponge_field_elements<F: PrimeField>(&self, dest: &mut Vec<F>) {
self.max_multiplicands.to_sponge_field_elements(dest);
self.num_variables.to_sponge_field_elements(dest);
self.num_terms.to_sponge_field_elements(dest);
}
}

impl<F: Field> ListOfProductsOfPolynomials<F> {
/// Returns an empty polynomial
pub fn new(num_variables: usize) -> Self {
ListOfProductsOfPolynomials {
max_multiplicands: 0,
num_variables,
products: Vec::new(),
flattened_ml_extensions: Vec::new(),
raw_pointers_lookup_table: HashMap::new(),
}
}

/// Add a list of multilinear extensions that is meant to be multiplied together.
/// The resulting polynomial will be multiplied by the scalar `coefficient`.
pub fn add_product(
&mut self,
product: impl IntoIterator<Item = Rc<DenseMultilinearExtension<F>>>,
coefficient: F,
) {
let product: Vec<Rc<DenseMultilinearExtension<F>>> = product.into_iter().collect();
let mut indexed_product = Vec::with_capacity(product.len());
assert!(!product.is_empty());
self.max_multiplicands = max(self.max_multiplicands, product.len());
for m in product {
assert_eq!(
m.num_vars, self.num_variables,
"product has a multiplicand with wrong number of variables"
);
let m_ptr: *const DenseMultilinearExtension<F> = Rc::as_ptr(&m);
if let Some(index) = self.raw_pointers_lookup_table.get(&m_ptr) {
indexed_product.push(*index)
} else {
let curr_index = self.flattened_ml_extensions.len();
self.flattened_ml_extensions.push(m.clone());
self.raw_pointers_lookup_table.insert(m_ptr, curr_index);
indexed_product.push(curr_index);
}
}
self.products.push((coefficient, indexed_product));
}

/// Evaluate the polynomial at point `point`
pub fn evaluate(&self, point: &[F]) -> F {
self.products
.iter()
.map(|(c, p)| {
*c * p
.iter()
.map(|&i| self.flattened_ml_extensions[i].evaluate(point).unwrap())
.product::<F>()
})
.sum()
}
}
122 changes: 122 additions & 0 deletions supernova/src/ml_sumcheck/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
//! Sumcheck Protocol for multilinear extension

// This implementation is pulled from `ark-linear-sumcheck` and adapted to use cryptographic
// sponge interface instead of blake2 -- [https://github.com/arkworks-rs/sumcheck]

#![forbid(unsafe_code)]
#![allow(unused)]

use ark_crypto_primitives::sponge::{Absorb, CryptographicSponge};
use ark_ff::PrimeField;
use ark_std::marker::PhantomData;

mod data_structures;
mod protocol;

#[cfg(test)]
mod tests;

pub use data_structures::{ListOfProductsOfPolynomials, PolynomialInfo};
use protocol::{
prover::{ProverMsg, ProverState},
verifier::SubClaim,
IPForMLSumcheck,
};

#[derive(Debug)]
pub enum Error {
/// protocol rejects this proof
Reject,
}

/// Sumcheck for products of multilinear polynomial
pub struct MLSumcheck<F, RO>(#[doc(hidden)] PhantomData<(F, RO)>);

/// proof generated by prover
pub type Proof<F> = Vec<ProverMsg<F>>;

impl<F: PrimeField + Absorb, RO: CryptographicSponge> MLSumcheck<F, RO> {
/// extract sum from the proof
pub fn extract_sum(proof: &Proof<F>) -> F {
proof[0].evaluations[0] + proof[0].evaluations[1]
}

/// 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}$$
pub fn prove(config: &RO::Config, polynomial: &ListOfProductsOfPolynomials<F>) -> Proof<F> {
let mut random_oracle = RO::new(config);
Self::prove_as_subprotocol(&mut random_oracle, polynomial).0
}

/// This function does the same thing as `prove`, but it uses cryptographic sponge as the transcript/to generate the
/// verifier challenges. Additionally, it returns the prover's state in addition to the proof.
/// Both of these allow this sumcheck to be better used as a part of a larger protocol.
pub fn prove_as_subprotocol(
random_oracle: &mut RO,
polynomial: &ListOfProductsOfPolynomials<F>,
) -> (Proof<F>, ProverState<F>) {
random_oracle.absorb(&polynomial.info());

let mut prover_state = IPForMLSumcheck::<F, RO>::prover_init(polynomial);
let mut verifier_msg = None;
let mut prover_msgs = Vec::with_capacity(polynomial.num_variables);
for _ in 0..polynomial.num_variables {
let prover_msg =
IPForMLSumcheck::<F, RO>::prove_round(&mut prover_state, &verifier_msg);
random_oracle.absorb(&prover_msg);
prover_msgs.push(prover_msg);
let next_verifier_msg = IPForMLSumcheck::<F, RO>::sample_round(random_oracle);
random_oracle.absorb(&next_verifier_msg.randomness);

verifier_msg = Some(next_verifier_msg);
}

(prover_msgs, prover_state)
}

/// verify the claimed sum using the proof
pub fn verify(
config: &RO::Config,
polynomial_info: &PolynomialInfo,
claimed_sum: F,
proof: &Proof<F>,
) -> Result<SubClaim<F>, Error> {
let mut random_oracle = RO::new(config);
Self::verify_as_subprotocol(&mut random_oracle, polynomial_info, claimed_sum, proof)
}

/// This function does the same thing as `prove`, but it uses a cryptographic sponge as the transcript/to generate the
/// verifier challenges. This allows this sumcheck to be used as a part of a larger protocol.
pub fn verify_as_subprotocol(
random_oracle: &mut RO,
polynomial_info: &PolynomialInfo,
claimed_sum: F,
proof: &Proof<F>,
) -> Result<SubClaim<F>, Error> {
random_oracle.absorb(polynomial_info);

let mut verifier_state = IPForMLSumcheck::<F, RO>::verifier_init(polynomial_info);
for i in 0..polynomial_info.num_variables {
let prover_msg = proof.get(i).expect("proof is incomplete");
random_oracle.absorb(prover_msg);
let verifier_msg = IPForMLSumcheck::verify_round(
(*prover_msg).clone(),
&mut verifier_state,
random_oracle,
);
random_oracle.absorb(&verifier_msg.randomness);
}

IPForMLSumcheck::<F, RO>::check_and_generate_subclaim(verifier_state, claimed_sum)
}
}
13 changes: 13 additions & 0 deletions supernova/src/ml_sumcheck/protocol/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//! Interactive Proof Protocol used for Multilinear Sumcheck
use ark_std::marker::PhantomData;

pub mod prover;
pub mod verifier;

pub use super::data_structures::{ListOfProductsOfPolynomials, PolynomialInfo};

/// Interactive Proof for Multilinear Sumcheck
pub struct IPForMLSumcheck<F, RO> {
#[doc(hidden)]
_marker: PhantomData<(F, RO)>,
}

0 comments on commit c8fe9df

Please sign in to comment.