Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ ark-poly = "0.5.0"
ark-serialize = "0.5.0"
ark-std ="0.5.0"
memmap2 = "0.9.5"
nohash-hasher = "0.2.0"
rayon = { version = "1.10", optional = true }
spongefish = { git = "https://github.com/arkworks-rs/spongefish", rev="fce4bfed23b2deb9a9f174178b6c9f59f580cc81" }
spongefish-poseidon = { git = "https://github.com/arkworks-rs/spongefish", package = "spongefish-poseidon", features = ["bls12-381"], rev = "fce4bfed23b2deb9a9f174178b6c9f59f580cc81" }

[dev-dependencies]
criterion = "0.7"
Expand Down
126 changes: 70 additions & 56 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,81 +1,95 @@
<h1 align="center">Efficient Sumcheck</h1>

<p align="center">
<a href="https://github.com/compsec-epfl/space-efficient-sumcheck/blob/main/LICENSE-APACHE"><img src="https://img.shields.io/badge/license-APACHE-blue.svg"></a>
<a href="https://github.com/compsec-epfl/space-efficient-sumcheck/blob/main/LICENSE-MIT"><img src="https://img.shields.io/badge/license-MIT-blue.svg"></a>
</p>
Efficient, streaming capable, sumcheck with **Fiat–Shamir** support via [SpongeFish](https://github.com/arkworks-rs/spongefish).

This library was developed using [arkworks](https://arkworks.rs) to accompany:
**DISCLAIMER:** This library has not undergone a formal security audit. If you’d like to coordinate an audit, please contact.

- [Time-Space Trade-Offs for Sumcheck](https://eprint.iacr.org/2025/1473)<br>
[Anubhav Baweja](https://dblp.org/pid/192/1642), [Alessandro Chiesa](https://ic-people.epfl.ch/~achiesa/), [Elisabetta Fedele](https://elisabettafedele.github.io), [Giacomo Fenzi](https://gfenzi.io), [Pratyush Mishra](https://pratyushmishra.com/), [Tushar Mopuri](https://tmopuri.com/) and [Andrew Zitek-Estrada](https://andrewzitek.xyz)
## General Use

- [A Time-Space Tradeoff for the Sumcheck Prover](https://eprint.iacr.org/2024/524.pdf)<br>
[Alessandro Chiesa](https://ic-people.epfl.ch/~achiesa/), [Elisabetta Fedele](https://elisabettafedele.github.io), [Giacomo Fenzi](https://gfenzi.io), and [Andrew Zitek-Estrada](https://andrewzitek.xyz)
This library exposes two high-level functions:
1) [`multilinear_sumcheck`](multilinear_sumcheck) and
2) [`inner_product_sumcheck`](inner_product_sumcheck).

It is a repository of algorithms and abstractions including but not limited to Blendy 🍹.
Using [SpongeFish](https://github.com/arkworks-rs/spongefish) (or similar Fiat-Shamir interface) simply call the functions with the prover state:

**DISCLAIMER:** This library has not received security review and is NOT recommended for production use.
### Multilinear Sumcheck
$claim = \sum_{x \in \{0,1\}^n} p(x)$
```rust
use efficient_sumcheck::{multilinear_sumcheck, Sumcheck};
use efficient_sumcheck::transcript::SanityTranscript;

## Overview
The library provides implementation of sumcheck [[LFKN92](#references)] including product sumcheck. For adaptability to different contexts, it implements three proving algorithms:

- The quasi-linear time and logarithmic space algorithm of [[CTY11](#references)]
- [SpaceProver](https://github.com/compsec-epfl/efficient-sumcheck/blob/main/src/multilinear/provers/space/space.rs#L8)
- [SpaceProductProver](https://github.com/compsec-epfl/efficient-sumcheck/blob/main/src/multilinear_product/provers/space/space.rs#L11)

- The linear time and linear space algorithm of [[VSBW13](#references)]
- [TimeProver](https://github.com/compsec-epfl/efficient-sumcheck/blob/main/src/multilinear/provers/time/time.rs#L13)
- [TimeProductProver](https://github.com/compsec-epfl/efficient-sumcheck/blob/main/src/multilinear_product/provers/time/time.rs#L10)
let mut evals_p_01n: Vec<F> = /* ... */;
let mut prover_state = SanityTranscript::new(&mut rng);
let sumcheck_transcript: Sumcheck<F> = multilinear_sumcheck(
&mut evals_p_01n,
&mut prover_state
);
```

- The linear time and sublinear space algorithm Blendy🍹
- [BlendyProver](https://github.com/compsec-epfl/efficient-sumcheck/blob/main/src/multilinear/provers/blendy/blendy.rs#L9)
- [BlendyProductProver](https://github.com/compsec-epfl/efficient-sumcheck/blob/main/src/multilinear_product/provers/blendy/blendy.rs#L13)
### Inner Product Sumcheck
$claim = \sum_{x \in \{0,1\}^n} f(x) \cdot g(x)$

## Usage
The library can be used to obtain a sumcheck transcript over any implementation of [Stream](https://github.com/compsec-epfl/efficient-sumcheck/blob/main/src/streams/stream.rs#L41), which could be backed by an evaluations table held in memory or read from disk. For example, if $f = 4x_1x_2 + 7x_2x_3 + 2x_1 + 13x_2$ like in the test [here](https://github.com/compsec-epfl/efficient-sumcheck/blob/main/src/tests/polynomials.rs#L15), then:
```rust
use efficient_sumcheck::{inner_product_sumcheck, ProductSumcheck};
use efficient_sumcheck::transcript::SanityTranscript;

```
let f_stream: MemoryStream<F> = MemoryStream::<F>::new(f.to_evaluations());
let mut multivariate_prover = TimeProver::<F, MemoryStream<F>>::new(
<TimeProver<F, MemoryStream<F>> as Prover<F>>::ProverConfig::default(
f_stream.claimed_sum,
3,
p_stream,
),
let mut evals_f_01n: Vec<F> = /* ... */;
let mut evals_g_01n: Vec<F> = /* ... */;
let mut prover_state = SanityTranscript::new(&mut rng);
let sumcheck_transcript: ProductSumcheck<F> = inner_product_sumcheck(
&mut evals_f_01n,
&mut evals_g_01n,
&mut prover_state
);
let transcript = Sumcheck::<F>::prove::<
MemoryStream<F>,
TimeProver<F, MemoryStream<F>>,
>(&mut multivariate_prover, &mut ark_std::test_rng()));
```

Or for the sum of $f * g$, then:
```
let f_stream: MemoryStream<F> = MemoryStream::<F>::new(f.to_evaluations());
let g_stream: MemoryStream<F> = MemoryStream::<F>::new(g.to_evaluations());
let streams: Vec<MemoryStream<F>> = vec![f_stream, g_stream];
let multivariate_product_prover = TimeProductProver::<F, MemoryStream<F>>::new(ProductProverConfig::default(
multivariate_product_claim(streams.clone()),
num_vars,
streams,
));
## Examples

### 1) WARP - Multilinear Constraint Batching

WARP batches $r$ multilinear evaluation claims into a single inner product sumcheck:

$$\sigma = \sum_{x \in \{0,1\}^n} \hat{f}(x) \cdot \underbrace{\sum_{i=1}^{r} \xi_i \cdot \widetilde{eq}(\zeta_i,\, x)}_{g(x)}$$

Before integration, [WARP](https://github.com/compsec-epfl/warp) used 200+ lines of sumcheck related code including calls to SpongeFish, pair- and table-wise reductions, as well as sparse-map foldings ([PR #14](https://github.com/compsec-epfl/warp/pull/14), [PR #12](https://github.com/compsec-epfl/warp/pull/12/changes#diff-904f410986c619441fb8554f4840cb36613f2de354b41ca991d381dec78959b0L34)).

Using Efficient Sumcheck this reduces to six lines of code and with zero user effort benefits from parallelization (and soon vectorization):

```rust
use efficient_sumcheck::{inner_product_sumcheck, batched_constraint_poly};

let alpha = inner_product_sumcheck(
&mut f,
&mut batched_constraint_poly(&dense_evals, &sparse_evals),
&mut transcript,
).verifier_messages;
```

## Evaluation
In addition to the reference papers, to help selection of prover algorithm we give a brief evaluation. The asymptotic improvement of BlendyProver translates to significantly lower memory consumption than TimeProver across all configurations tested. TimeProver and BlendyProver have similar runtimes and are orders of magnitude faster than SpaceProver.
Here, `batched_constraint_poly` builds $g(x)$ by merging **dense** evaluation vectors (out-of-domain points $\zeta_i \notin \{0,1\}^n$, where $\widetilde{eq}(\zeta_i, \cdot)$ is nonzero everywhere) with **sparse** index-keyed corrections (in-domain shift queries $\zeta_j \in \{0,1\}^n$, where $\widetilde{eq}(\zeta_j, \cdot)$ has a single nonzero entry, optimized via [[CBBZ23](#references)]).

## Advanced Usage

<p align="center">
<img src="assets/evaluation_graphs.png#gh-light-mode-only" alt="Line graph showing runtime and memory consumption of provers for inputs ranging from 15 to 30 variables" style="max-width: 800px;" />
<img src="assets/evaluation_graphs_inverted.png#gh-dark-mode-only" alt="Line graph showing runtime and memory consumption of provers for inputs ranging from 15 to 30 variables" style="max-width: 800px;" />
</p>
Supporting the high-level interfaces are raw implementations of sumcheck [[LFKN92](#references)] using three proving algorithms:

## Contribution
Contributions in the form of PRs and issues/ suggestions are welcome.
- The quasi-linear time and logarithmic space algorithm of [[CTY11](#references)]
- [SpaceProver](https://github.com/compsec-epfl/efficient-sumcheck/blob/main/src/multilinear/provers/space/core.rs#L8)
- [SpaceProductProver](https://github.com/compsec-epfl/efficient-sumcheck/blob/main/src/multilinear_product/provers/space/core.rs#L11)

- The linear time and linear space algorithm of [[VSBW13](#references)]
- [TimeProver](https://github.com/compsec-epfl/efficient-sumcheck/blob/main/src/multilinear/provers/time/core.rs#L7)
- [TimeProductProver](https://github.com/compsec-epfl/efficient-sumcheck/blob/main/src/multilinear_product/provers/time/core.rs#L16)

- The linear time and sublinear space algorithms of [[CFFZ24](#references)] and [[BCFFMMZ25](#references)] respectively
- [BlendyProver](https://github.com/compsec-epfl/efficient-sumcheck/blob/main/src/multilinear/provers/blendy/core.rs#L14)
- [BlendyProductProver](https://github.com/compsec-epfl/efficient-sumcheck/blob/main/src/multilinear_product/provers/blendy/core.rs#L13)

## References
[[LFNK92](https://dl.acm.org/doi/pdf/10.1145/146585.146605)]: Carsten Lund, Lance Fortnow, Howard J. Karloff, and Noam Nisan. “Algebraic Methods for Interactive Proof Systems”. In: Journal of the ACM 39.4 (1992).

[[CTY11](https://arxiv.org/pdf/1109.6882.pdf)]: Graham Cormode, Justin Thaler, and Ke Yi. “Verifying computations with streaming interactive proofs”. In: Proceedings of the VLDB Endowment 5.1 (2011), pp. 25–36.

[[VSBW13](https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=6547112)]: Victor Vu, Srinath Setty, Andrew J. Blumberg, and Michael Walfish. “A hybrid architecture for interactive verifiable computation”. In: Proceedings of the 34th IEEE Symposium on Security and Privacy. Oakland ’13. 2013, pp. 223–237.

[[CFFZ24](https://eprint.iacr.org/2024/524.pdf)]: Alessandro Chiesa, Elisabetta Fedele, Giacomo Fenzi, Andrew Zitek-Estrada. "A time-space tradeoff for the sumcheck prover". In: Cryptology ePrint Archive.

[[BCFFMMZ25](https://eprint.iacr.org/2025/1473.pdf)]: Anubhav Bawejal, Alessandro Chiesa, Elisabetta Fedele, Giacomo Fenzi, Pratyush Mishra, Tushar Mopuri, and Andrew Zitek-Estrada. "Time-Space Trade-Offs for Sumcheck". In: TCC Theory of Cryptography: 23rd International Conference, pp. 37.
171 changes: 171 additions & 0 deletions src/inner_product_sumcheck.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
//! Inner product sumcheck protocol.
//!
//! Given two evaluation vectors `f` and `g` representing multilinear polynomials on
//! the boolean hypercube `{0,1}^n`, the [`inner_product_sumcheck`] function executes
//! `n` rounds of the product sumcheck protocol computing `∑_x f(x)·g(x)`, and returns
//! the resulting [`ProductSumcheck`] transcript.
//!
//! # Example
//!
//! ```ignore
//! use efficient_sumcheck::{inner_product_sumcheck, ProductSumcheck};
//! use efficient_sumcheck::transcript::SanityTranscript;
//!
//! let mut f = vec![F::from(1), F::from(2), F::from(3), F::from(4)];
//! let mut g = vec![F::from(5), F::from(6), F::from(7), F::from(8)];
//! let mut transcript = SanityTranscript::new(&mut rng);
//! let result: ProductSumcheck<F> = inner_product_sumcheck(&mut f, &mut g, &mut transcript);
//! ```

use ark_std::collections::HashMap;
use nohash_hasher::BuildNoHashHasher;

use ark_ff::Field;

use crate::{
multilinear::{reductions::pairwise, ReduceMode},
multilinear_product::{TimeProductProver, TimeProductProverConfig},
prover::Prover,
streams::MemoryStream,
};

use crate::transcript::Transcript;

pub use crate::multilinear_product::ProductSumcheck;

pub type FastMap<V> = HashMap<usize, V, BuildNoHashHasher<usize>>;

pub fn batched_constraint_poly<F: Field>(
dense_polys: &Vec<Vec<F>>,
sparse_polys: &FastMap<F>,
) -> Vec<F> {
fn sum_columns<F: Field>(matrix: &Vec<Vec<F>>) -> Vec<F> {
if matrix.is_empty() {
return vec![];
}
let mut result = vec![F::ZERO; matrix[0].len()];
for row in matrix {
for (i, &val) in row.iter().enumerate() {
result[i] += val;
}
}
result
}
let mut res = sum_columns(dense_polys);
for (k, v) in sparse_polys.iter() {
res[*k] += v;
}
res
}

/// Run the inner product sumcheck protocol over two evaluation vectors,
/// using a generic [`Transcript`] for Fiat-Shamir (or sanity/random challenges).
///
/// Each round:
/// 1. Computes the round polynomial evaluations `(s(0), s(1), s(2))` via the product prover.
/// 2. Writes them to the transcript (3 field elements).
/// 3. Reads the verifier's challenge from the transcript (1 field element).
/// 4. Reduces both evaluation vectors by folding with the challenge.
pub fn inner_product_sumcheck<F: Field>(
f: &mut Vec<F>,
g: &mut Vec<F>,
transcript: &mut impl Transcript<F>,
) -> ProductSumcheck<F> {
// checks
assert_eq!(f.len(), g.len());
assert!(f.len().count_ones() == 1);

// initialize
let num_rounds = f.len().trailing_zeros() as usize;
let mut prover_messages: Vec<(F, F, F)> = vec![];
let mut verifier_messages: Vec<F> = vec![];

// all rounds
for _ in 0..num_rounds {
let mut prover = TimeProductProver::new(TimeProductProverConfig::new(
f.len().trailing_zeros() as usize,
vec![MemoryStream::new(f.to_vec()), MemoryStream::new(g.to_vec())],
ReduceMode::Pairwise,
));

// call the prover
let msg = prover.next_message(None).unwrap();

// write transcript
prover_messages.push(msg);
transcript.write(msg.0);
transcript.write(msg.1);
transcript.write(msg.2);

// read the transcript
let chg = transcript.read();
verifier_messages.push(chg);

// reduce
pairwise::reduce_evaluations(f, chg);
pairwise::reduce_evaluations(g, chg);
}

ProductSumcheck {
verifier_messages,
prover_messages,
}
}

#[cfg(test)]
mod tests {
use super::*;
use ark_ff::UniformRand;
use ark_std::test_rng;

use crate::tests::F64;

const NUM_VARS: usize = 4; // vectors of length 2^4 = 16

#[test]
fn test_inner_product_sumcheck_sanity() {
use crate::transcript::SanityTranscript;

let mut rng = test_rng();

let n = 1 << NUM_VARS;
let mut f: Vec<F64> = (0..n).map(|_| F64::rand(&mut rng)).collect();
let mut g: Vec<F64> = (0..n).map(|_| F64::rand(&mut rng)).collect();

let mut transcript = SanityTranscript::new(&mut rng);
let result = inner_product_sumcheck(&mut f, &mut g, &mut transcript);

assert_eq!(result.prover_messages.len(), NUM_VARS);
assert_eq!(result.verifier_messages.len(), NUM_VARS);
}

#[test]
fn test_inner_product_sumcheck_spongefish() {
use crate::transcript::SpongefishTranscript;
use spongefish::codecs::arkworks_algebra::FieldDomainSeparator;
use spongefish::DomainSeparator;

let mut rng = test_rng();

let n = 1 << NUM_VARS;
let mut f: Vec<F64> = (0..n).map(|_| F64::rand(&mut rng)).collect();
let mut g: Vec<F64> = (0..n).map(|_| F64::rand(&mut rng)).collect();

// Build the IO pattern: each round absorbs 3 scalars and squeezes 1 challenge
let mut domsep = DomainSeparator::new("test-inner-product-sumcheck");
for _ in 0..NUM_VARS {
domsep =
<DomainSeparator as FieldDomainSeparator<F64>>::add_scalars(domsep, 3, "prover");
domsep = <DomainSeparator as FieldDomainSeparator<F64>>::challenge_scalars(
domsep, 1, "verifier",
);
}

let prover_state = domsep.to_prover_state();
let mut transcript = SpongefishTranscript::new(prover_state);
let result = inner_product_sumcheck(&mut f, &mut g, &mut transcript);

assert_eq!(result.prover_messages.len(), NUM_VARS);
assert_eq!(result.verifier_messages.len(), NUM_VARS);
}
}
55 changes: 47 additions & 8 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,53 @@
#[doc(hidden)]
pub mod tests;
//! # efficient-sumcheck
//!
//! Space-efficient implementations of the sumcheck protocol with Fiat-Shamir support.
//!
//! ## Quick Start
//!
//! For most use cases, you need just two functions and a transcript:
//!
//! ```ignore
//! use efficient_sumcheck::{multilinear_sumcheck, inner_product_sumcheck};
//! use efficient_sumcheck::transcript::{Transcript, SpongefishTranscript, SanityTranscript};
//! ```
//!
//! - [`multilinear_sumcheck()`] — standard multilinear sumcheck: `∑_x p(x)`
//! - [`inner_product_sumcheck()`] — inner product sumcheck: `∑_x f(x)·g(x)`
//!
//! Both accept any [`Transcript`] implementation — either
//! [`SpongefishTranscript`](transcript::SpongefishTranscript) for real Fiat-Shamir, or
//! [`SanityTranscript`](transcript::SanityTranscript) for testing with random challenges.
//!
//! ## Advanced Usage
//!
//! For custom prover implementations, streaming evaluation access,
//! or specialized reduction strategies, the internal modules expose the full
//! prover machinery: [`multilinear`], [`multilinear_product`], [`prover`], [`streams`].

// ─── Primary API ─────────────────────────────────────────────────────────────

/// Transcript trait and backends (Spongefish, Sanity).
pub mod transcript;

mod inner_product_sumcheck;
mod multilinear_sumcheck;

pub use inner_product_sumcheck::{
batched_constraint_poly, inner_product_sumcheck, ProductSumcheck,
};
pub use multilinear_sumcheck::{multilinear_sumcheck, Sumcheck};

// ─── Internal / Advanced ─────────────────────────────────────────────────────

pub mod hypercube;
pub mod interpolation;
pub mod messages;
pub mod multilinear;
pub mod multilinear_product;
pub mod order_strategy;
pub mod prover;
pub mod streams;

pub use crate::multilinear::Sumcheck;
pub use crate::multilinear_product::ProductSumcheck;
pub mod hypercube;
pub mod interpolation;
pub mod messages;
pub mod order_strategy;

#[doc(hidden)]
pub mod tests;
Loading
Loading