Skip to content

Commit 52f3dce

Browse files
committed
Proof size reduction for sumchecks (see https://eprint.iacr.org/2024/108.pdf, section 3)
1 parent 3135296 commit 52f3dce

23 files changed

Lines changed: 291 additions & 348 deletions

File tree

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ cargo run --release -- xmss --n-signatures 1400
3636

3737
| WHIR rate \ regime | Proven | Conjectured |
3838
| ------------------ | -------------------- | -------------------- |
39-
| 1/2 | 775 XMSS/s - 374 KiB | 775 XMSS/s - 204 KiB |
40-
| 1/4 | 650 XMSS/s - 246 KiB | 650 XMSS/s - 146 KiB |
39+
| 1/2 | 800 XMSS/s - 355 KiB | 800 XMSS/s - 188 KiB |
40+
| 1/4 | 700 XMSS/s - 229 KiB | 650 XMSS/s - 130 KiB |
4141

4242
(Proving throughput - proof size)
4343

@@ -52,7 +52,7 @@ cargo run --release -- recursion --n 2
5252

5353
| Proven | Conjectured |
5454
| --------------- | --------------- |
55-
| 0.77s - 223 KiB | 0.59s - 128 KiB |
55+
| 0.75s - 188 KiB | 0.57s - 116 KiB |
5656

5757

5858
### Bonus: unbounded recursive aggregation

crates/air/src/verify.rs

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,14 @@ where
2222
.unwrap_or_else(|| verifier_state.sample_vec(log_n_rows));
2323
assert_eq!(zerocheck_challenges.len(), log_n_rows);
2424

25-
let (sc_sum, outer_statement) = sumcheck_verify::<EF>(verifier_state, log_n_rows, air.degree_air() + 1)?;
26-
if sc_sum
27-
!= virtual_column_statement
28-
.as_ref()
29-
.map(|st| st.value)
30-
.unwrap_or_else(|| EF::ZERO)
31-
{
32-
return Err(ProofError::InvalidProof);
33-
}
25+
let expected_sum = virtual_column_statement.as_ref().map(|st| st.value).unwrap_or(EF::ZERO);
26+
let outer_statement = sumcheck_verify(
27+
verifier_state,
28+
log_n_rows,
29+
air.degree_air() + 1, // +1 for the eq factor
30+
expected_sum,
31+
Some(&zerocheck_challenges),
32+
)?;
3433

3534
let inner_evals = verifier_state.next_extension_scalars_vec(air.n_columns() + air.n_down_columns())?;
3635

@@ -74,11 +73,7 @@ fn open_columns<A: Air, EF: ExtensionField<PF<EF>>>(
7473

7574
let inner_sum: EF = dot_product(evals_down.into_iter(), batching_scalar_powers.iter().copied());
7675

77-
let (inner_sum_retrieved, inner_sumcheck_stement) = sumcheck_verify(verifier_state, log_n_rows, 2)?;
78-
79-
if inner_sum != inner_sum_retrieved {
80-
return Err(ProofError::InvalidProof);
81-
}
76+
let inner_sumcheck_stement = sumcheck_verify(verifier_state, log_n_rows, 2, inner_sum, None)?;
8277

8378
let matrix_down_sc_eval = next_mle(outer_sumcheck_challenge, &inner_sumcheck_stement.point);
8479

crates/backend/fiat-shamir/src/challenger.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,16 @@ impl<F: PrimeField64, P: Compression<[F; WIDTH]>> Challenger<F, P> {
3636
self.state = self.hash_state_with(&value);
3737
}
3838

39+
pub fn observe_scalars(&mut self, scalars: &[F]) {
40+
for chunk in scalars.chunks(RATE) {
41+
let mut buffer = [F::ZERO; RATE];
42+
for (i, val) in chunk.iter().enumerate() {
43+
buffer[i] = *val;
44+
}
45+
self.observe(buffer);
46+
}
47+
}
48+
3949
pub fn sample_many(&mut self, n: usize) -> Vec<[F; RATE]> {
4050
let mut sampled = Vec::with_capacity(n);
4151
for i in 0..n + 1 {

crates/backend/fiat-shamir/src/lib.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@ pub use errors::*;
44
mod prover;
55
pub use prover::*;
66

7-
mod verifier;
8-
pub use verifier::*;
9-
107
mod utils;
118
pub use utils::*;
129

@@ -16,7 +13,10 @@ mod traits;
1613
pub use traits::*;
1714

1815
mod transcript;
19-
pub use transcript::*;
16+
pub use transcript::{DIGEST_LEN_FE, MerkleOpening, MerklePath, MerklePaths, Proof, RawProof};
2017

2118
mod merkle_pruning;
2219
pub(crate) use merkle_pruning::*;
20+
21+
mod verifier;
22+
pub use verifier::*;

crates/backend/fiat-shamir/src/prover.rs

Lines changed: 33 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::{
2+
MerklePaths, PrunedMerklePaths,
23
challenger::{Challenger, RATE, WIDTH},
34
*,
45
};
@@ -14,7 +15,8 @@ use symetric::Compression;
1415
#[derive(Debug)]
1516
pub struct ProverState<EF: ExtensionField<PF<EF>>, P> {
1617
challenger: Challenger<PF<EF>, P>,
17-
transcript: Proof<PF<EF>>,
18+
transcript: Vec<PF<EF>>,
19+
merkle_paths: Vec<PrunedMerklePaths<PF<EF>, PF<EF>>>,
1820
}
1921

2022
impl<EF: ExtensionField<PF<EF>>, P: Compression<[PF<EF>; WIDTH]>> ProverState<EF, P>
@@ -26,20 +28,16 @@ where
2628
assert!(EF::DIMENSION <= RATE);
2729
Self {
2830
challenger: Challenger::new(compressor),
29-
transcript: Proof(Vec::new()),
31+
transcript: Vec::new(),
32+
merkle_paths: Vec::new(),
3033
}
3134
}
3235

33-
pub fn raw_proof(self) -> RawProof<PF<EF>> {
34-
self.transcript.into_raw_proof()
35-
}
36-
37-
pub fn pruned_proof(&self) -> PrunedProof<PF<EF>> {
38-
self.transcript.clone().prune()
39-
}
40-
41-
pub fn into_pruned_proof(self) -> PrunedProof<PF<EF>> {
42-
self.transcript.prune()
36+
pub fn into_proof(self) -> Proof<PF<EF>> {
37+
Proof {
38+
transcript: self.transcript,
39+
merkle_paths: self.merkle_paths,
40+
}
4341
}
4442
}
4543

@@ -62,14 +60,8 @@ where
6260
PF<EF>: PrimeField64,
6361
{
6462
fn add_base_scalars(&mut self, scalars: &[PF<EF>]) {
65-
self.transcript.0.push(TranscriptData::Interraction(scalars.to_vec()));
66-
for chunk in scalars.chunks(RATE) {
67-
let mut buffer = [PF::<EF>::ZERO; RATE];
68-
for (i, val) in chunk.iter().enumerate() {
69-
buffer[i] = *val;
70-
}
71-
self.challenger.observe(buffer);
72-
}
63+
self.challenger.observe_scalars(scalars);
64+
self.transcript.extend_from_slice(scalars);
7365
}
7466

7567
fn state(&self) -> String {
@@ -81,12 +73,28 @@ where
8173
.map(|f| f.to_string())
8274
.collect::<Vec<_>>()
8375
.join(", "),
84-
self.transcript.0.len()
76+
self.transcript.len()
8577
)
8678
}
8779

80+
fn add_sumcheck_polynomial(&mut self, coeffs: &[EF], eq_alpha: Option<EF>) {
81+
match eq_alpha {
82+
None => {
83+
let scalars = flatten_scalars_to_base(coeffs);
84+
self.challenger.observe_scalars(&scalars);
85+
self.transcript.extend_from_slice(&scalars[EF::DIMENSION..]); // c0 reconstructed by verifier from claimed_sum
86+
}
87+
Some(alpha) => {
88+
let bare_scalars = flatten_scalars_to_base(coeffs);
89+
let full_scalars = flatten_scalars_to_base(&expand_bare_to_full(coeffs, alpha));
90+
self.challenger.observe_scalars(&full_scalars);
91+
self.transcript.extend_from_slice(&bare_scalars[EF::DIMENSION..]); // h0 reconstructed by verifier from claimed_sum
92+
}
93+
}
94+
}
95+
8896
fn hint_merkle_paths_base(&mut self, paths: Vec<MerklePath<PF<EF>, PF<EF>>>) {
89-
self.transcript.0.push(TranscriptData::MerklePaths(MerklePaths(paths)));
97+
self.merkle_paths.push(MerklePaths(paths).prune());
9098
}
9199

92100
fn pow_grinding(&mut self, bits: usize) {
@@ -134,14 +142,10 @@ where
134142
})
135143
.expect("failed to find witness");
136144

137-
let witness_found = witness_found.lock().unwrap().unwrap();
145+
let witness = witness_found.lock().unwrap().unwrap();
138146

139-
self.challenger.observe({
140-
let mut value = [PF::<EF>::ZERO; RATE];
141-
value[0] = witness_found;
142-
value
143-
});
147+
self.challenger.observe_scalars(&[witness]);
144148
assert!(self.challenger.state[0].as_canonical_u64() & ((1 << bits) - 1) == 0);
145-
self.transcript.0.push(TranscriptData::GrindingWitness(witness_found));
149+
self.transcript.push(witness);
146150
}
147151
}

crates/backend/fiat-shamir/src/traits.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
use field::ExtensionField;
22

3-
use crate::{MerkleOpening, MerklePath, PF, ProofError, flatten_scalars_to_base, pack_scalars_to_extension};
3+
use crate::{
4+
MerkleOpening, MerklePath, PF, ProofError, ProofResult, flatten_scalars_to_base, pack_scalars_to_extension,
5+
};
46

57
pub trait ChallengeSampler<EF> {
68
fn sample_vec(&mut self, len: usize) -> Vec<EF>;
@@ -15,6 +17,7 @@ pub trait FSProver<EF: ExtensionField<PF<EF>>>: ChallengeSampler<EF> {
1517
fn add_base_scalars(&mut self, scalars: &[PF<EF>]);
1618
fn pow_grinding(&mut self, bits: usize);
1719
fn hint_merkle_paths_base(&mut self, paths: Vec<MerklePath<PF<EF>, PF<EF>>>);
20+
fn add_sumcheck_polynomial(&mut self, coeffs: &[EF], eq_alpha: Option<EF>);
1821

1922
fn add_extension_scalars(&mut self, scalars: &[EF]) {
2023
self.add_base_scalars(&flatten_scalars_to_base(scalars));
@@ -43,6 +46,12 @@ pub trait FSVerifier<EF: ExtensionField<PF<EF>>>: ChallengeSampler<EF> {
4346
fn next_base_scalars_vec(&mut self, n: usize) -> Result<Vec<PF<EF>>, ProofError>;
4447
fn next_merkle_opening(&mut self) -> Result<MerkleOpening<PF<EF>>, ProofError>;
4548
fn check_pow_grinding(&mut self, bits: usize) -> Result<(), ProofError>;
49+
fn next_sumcheck_polynomial(
50+
&mut self,
51+
n_coeffs: usize,
52+
claimed_sum: EF,
53+
eq_alpha: Option<EF>,
54+
) -> ProofResult<Vec<EF>>;
4655

4756
fn next_extension_scalars_vec(&mut self, n: usize) -> Result<Vec<EF>, ProofError> {
4857
Ok(pack_scalars_to_extension(

0 commit comments

Comments
 (0)