Skip to content

Add batching commit support#188

Merged
WizardOfMenlo merged 29 commits into
mainfrom
veljko/batching
Aug 25, 2025
Merged

Add batching commit support#188
WizardOfMenlo merged 29 commits into
mainfrom
veljko/batching

Conversation

@veljkovranic

Copy link
Copy Markdown
Collaborator

This PR has been in the works for months now.

The CI should be passing as well.

The batch committing to the multiple polynomials is a requirement for ZK WHIR

yswami-tfh and others added 18 commits April 2, 2025 15:24
Changes:

1. New set of API:
	DomainSeparator::batch_commit_statement
	Committer::batch_commit
	Prover::batch_prove
	Verifier::verify_batched
2. New cargo feature flag "batching" to enable disable this API
3. New traits to make the verifier generic
Merging batching branch to main
Moved some code to follow the new CommitmentReader and CommitmentWriter structure.
Main changes:

1. Unified batched and non-batched structs. Refactored `Witness`
   `WhirProof` and `ParsedCommitment` to work with both batched
   oracles as well as single oracle.
2. Removed seprate APIs for batched proving and batched verifying.
   The only seperate function is `CommitmentWriter::batched_commit`
   which takes a list of polynomials while `CommitmentWriter::commit`
   works with only a single polynomial.
3. Removed index based access to vectors as much as possible. Chaching
   friendly/sequential access to vectors still WIP.
4. Removed conjunctions within assert statements.
5. Updated tests to test different batch sizes
Fix batching test case that was taking too much time.
* Added OOD for batching oracles.
* Removed index based array access.
* Removed separate functions for batching and non-batching
  as much as possible.
Moved code from batching to the Prover side of things.
@veljkovranic

Copy link
Copy Markdown
Collaborator Author

Moving the review conversation from Slack to Github for visibility:

@WizardOfMenlo comment:

We are doing something weird?
Why are we first committing to rt_1, ..., rt_n and then (after the batching randomness) doing another commitment to the RLC?
We should instead commit (before the batching randomness) to a single MT rt with leaves of size n instead.

@yswami-tfh response:

I think Giacomo has a more general model in mind for batching. The current code is sufficient for ZK. (Supporting the more general model will require substantial refactoring.)
The current implementation assumes that the user has f_1, f_2, …, f_n polynomials and the weight for each polynomial is the same, say w(x1, … , x_n).
The code works as follows:
Computes NTT of each f1, …, fn and commits the Merkle root R1, …, Rn separately.
It then samples batching_randomness (let’s call it B.)
It then computes the random linear combination (RLC) of f_i’s as h := f_1 + B*f_2 + B^2*f_3 + …
It then runs the normal WHIR sumcheck over h(x_1,…,x_m)*w(x_1,…,x_m)
On the verifier side, since each polynomial is committed individually in the first round, the verifier checks the consistency by using individual Merkle tree.
Supporting the more general use case where weight are different for different f_i will requires some changes to last round of WHIR and requires major refactoring efforts.

Lets continue the review in this PR, for the sake of visibilitiy

@WizardOfMenlo

WizardOfMenlo commented Aug 20, 2025

Copy link
Copy Markdown
Collaborator

Supporting the more general use case where weight are different for different f_i will requires some changes to last round of WHIR and requires major refactoring efforts.

I don't really care about the case where the f_i have different weights, I am happy with testing the RLC at the same weight. My objection is in how f_1, ..., f_n are committed.
Roughly speaking, there are two approaches that make sense (and there is a tradeoff):

  • commit f_1, ..., f_n as a single Merkle tree with leaves of size n, absorb the root and squeeze batching randomness and commit to the RLC f^* of f_1, ..., f_n as a Merkle tree with leaves of size 2^k (note you could also domain shift this). Then sample x_1, ..., x_t and add as claims when testing f^* that f^*(x_i) = RLC((f_1, ..., f_n), x_i). In terms of queries to the initial oracles, you read something like t * (n + 2^k) field elements and 2t authentication paths (one for each tree).
  • commit f_1, ..., f_n as a single Merkle tree with leaves of size n * 2^k, absorb the root and squeeze batching randomness, and whenever you query this tree you first RLC and the do the other operation. This requires reading t *n * 2^k field elements and t authentication paths.

Since in our case n = 2, probably the second option is the most beneficial. The one currently implement commits to (n + 1) Merkle trees with leaves 2^k which leads to reading t * n * 2^k field elements and (n+1) * t authentication paths, which is strictly worse than either of the above.

@yswami-tfh

Copy link
Copy Markdown
Contributor

I agree, option 2 is far more efficient, but not sure if anyone has free cycles to implement it.

@veljkovranic The high level idea is that right now commit_batch calls commit_single which computes both the NTT as well as separate Merkle trees for each batching polynomial. Instead, it will be better for commit_single to just compute the NTTs and then commit_batch can concatenate all the NTT from all the polynomials to create a single Merkle tree. (Most likely, it will also require changes to the transcript and verifier's consistency checks in the Verifier code.)

@veljkovranic

Copy link
Copy Markdown
Collaborator Author

@recmo what is your opinion here, how should we proceed from here?

Should I implement this on top of the codebase that we currently have, or do we want to leave it for the planned rewrite of WHIR?

@WizardOfMenlo WizardOfMenlo left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left a few comments, happy to merge after some cleanpu

Comment thread src/fs_utils.rs Outdated
/// OOD response. If num_samples == 0 or batch_size == 0, nothing is added
///
#[must_use]
fn add_committed_ood(self, num_samples: usize, batch_size: usize) -> Self;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about just changing the add_ood to take as input batch size?

Comment thread src/whir/committer/writer.rs Outdated
#[cfg_attr(feature = "tracing", instrument(skip_all, fields(size = polynomial.num_coeffs())))]
pub fn commit<ProverState>(
#[cfg_attr(feature = "tracing", instrument(skip_all, fields(size = polynomials.first().unwrap().num_coeffs())))]
pub fn commit_batch<ProverFSState>(

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why change to ProverFSState? Keep as ProverState for consistency

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, good catch, this is just too old of a branch :D

Comment thread src/whir/committer/writer.rs
Comment thread src/whir/committer/writer.rs
@WizardOfMenlo
WizardOfMenlo merged commit 6f01823 into main Aug 25, 2025
5 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants