Skip to content

Commit 48fc090

Browse files
Fix: Make Transcript Constant Length (#195)
* feat: added recursive feature for constant length transcript * fix: remove sorting of challenge indices * feat: enhance WHIR configuration with deduplication and proof strategies * chore: cargo clippy * refactor: move dedup & mt strategies into whir params * chore: cargo clippy * Refactor * clippy --------- Co-authored-by: Giacomo Fenzi <giacomofenzi@outlook.com>
1 parent 0077be2 commit 48fc090

13 files changed

Lines changed: 395 additions & 100 deletions

File tree

src/bin/benchmark.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ use whir::{
2626
},
2727
},
2828
parameters::{
29-
default_max_pow, FoldingFactor, MultivariateParameters, ProtocolParameters, SoundnessType,
29+
default_max_pow, DeduplicationStrategy, FoldingFactor, MerkleProofStrategy,
30+
MultivariateParameters, ProtocolParameters, SoundnessType,
3031
},
3132
poly_utils::{coeffs::CoefficientList, multilinear::MultilinearPoint},
3233
whir::{
@@ -257,6 +258,8 @@ fn run_whir<F, MerkleConfig>(
257258
_pow_parameters: Default::default(),
258259
starting_log_inv_rate: starting_rate,
259260
batch_size: 1,
261+
deduplication_strategy: DeduplicationStrategy::Enabled,
262+
merkle_proof_strategy: MerkleProofStrategy::Compressed,
260263
};
261264

262265
let polynomial = CoefficientList::new(
@@ -302,7 +305,7 @@ fn run_whir<F, MerkleConfig>(
302305
.commit(&mut prover_state, polynomial.clone())
303306
.unwrap();
304307

305-
let prover = Prover(params.clone());
308+
let prover = Prover::new(params.clone());
306309

307310
let statement_new = Statement::<F>::new(num_variables);
308311

@@ -384,7 +387,7 @@ fn run_whir<F, MerkleConfig>(
384387
let committer = CommitmentWriter::new(params.clone());
385388
let witness = committer.commit(&mut prover_state, polynomial).unwrap();
386389

387-
let prover = Prover(params.clone());
390+
let prover = Prover::new(params.clone());
388391

389392
prover
390393
.prove(&mut prover_state, statement.clone(), witness)

src/bin/main.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ use whir::{
2121
},
2222
},
2323
parameters::{
24-
default_max_pow, FoldingFactor, MultivariateParameters, ProtocolParameters, SoundnessType,
24+
default_max_pow, DeduplicationStrategy, FoldingFactor, MerkleProofStrategy,
25+
MultivariateParameters, ProtocolParameters, SoundnessType,
2526
},
2627
poly_utils::{coeffs::CoefficientList, evals::EvaluationsList, multilinear::MultilinearPoint},
2728
whir::{
@@ -260,6 +261,8 @@ fn run_whir_as_ldt<F, MerkleConfig>(
260261
_pow_parameters: Default::default(),
261262
starting_log_inv_rate: starting_rate,
262263
batch_size: 1,
264+
deduplication_strategy: DeduplicationStrategy::Enabled,
265+
merkle_proof_strategy: MerkleProofStrategy::Compressed,
263266
};
264267

265268
let params = WhirConfig::<F, MerkleConfig, PowStrategy>::new(mv_params, whir_params);
@@ -289,7 +292,7 @@ fn run_whir_as_ldt<F, MerkleConfig>(
289292
let committer = CommitmentWriter::new(params.clone());
290293
let witness = committer.commit(&mut prover_state, polynomial).unwrap();
291294

292-
let prover = Prover(params.clone());
295+
let prover = Prover::new(params.clone());
293296

294297
let statement = Statement::new(num_variables);
295298
prover
@@ -374,6 +377,8 @@ fn run_whir_pcs<F, MerkleConfig>(
374377
_pow_parameters: Default::default(),
375378
starting_log_inv_rate: starting_rate,
376379
batch_size: 1,
380+
deduplication_strategy: DeduplicationStrategy::Enabled,
381+
merkle_proof_strategy: MerkleProofStrategy::Compressed,
377382
};
378383

379384
let params = WhirConfig::<F, MerkleConfig, PowStrategy>::new(mv_params, whir_params);
@@ -429,7 +434,7 @@ fn run_whir_pcs<F, MerkleConfig>(
429434
statement.add_constraint(linear_claim_weight, sum);
430435
}
431436

432-
let prover = Prover(params.clone());
437+
let prover = Prover::new(params.clone());
433438

434439
prover
435440
.prove(&mut prover_state, statement.clone(), witness)

src/crypto/merkle_tree/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ pub mod blake3;
66
pub mod digest;
77
pub mod keccak;
88
pub mod parameters;
9+
pub mod proof;
910

1011
#[derive(Debug, Default)]
1112
pub struct HashCounter;

src/crypto/merkle_tree/proof.rs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
use std::borrow::Borrow;
2+
3+
use ark_crypto_primitives::{
4+
merkle_tree::{Config, LeafParam, Path, TwoToOneParam},
5+
Error,
6+
};
7+
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize};
8+
9+
#[derive(Clone, Default, CanonicalSerialize, CanonicalDeserialize)]
10+
pub struct FullMultiPath<P: Config> {
11+
pub proofs: Vec<Path<P>>,
12+
}
13+
14+
impl<P: Config> FullMultiPath<P> {
15+
pub fn indices(&self) -> Vec<usize> {
16+
self.proofs.iter().map(|p| p.leaf_index).collect()
17+
}
18+
19+
pub fn verify<L: Borrow<P::Leaf> + Clone>(
20+
&self,
21+
leaf_hash_params: &LeafParam<P>,
22+
two_to_one_params: &TwoToOneParam<P>,
23+
root_hash: &P::InnerDigest,
24+
leaves: impl IntoIterator<Item = L>,
25+
) -> Result<bool, Error> {
26+
let leaves_vec: Vec<L> = leaves.into_iter().collect();
27+
self.proofs
28+
.iter()
29+
.enumerate()
30+
.map(|(i, proof)| {
31+
proof.verify(
32+
leaf_hash_params,
33+
two_to_one_params,
34+
root_hash,
35+
leaves_vec[i].borrow(),
36+
)
37+
})
38+
.try_fold(true, |acc, res| res.map(|b| acc && b))
39+
}
40+
}
41+
42+
impl<P: Config> From<Vec<Path<P>>> for FullMultiPath<P> {
43+
fn from(proofs: Vec<Path<P>>) -> Self {
44+
Self { proofs }
45+
}
46+
}

src/parameters.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,18 @@ impl FoldingFactor {
206206
}
207207
}
208208

209+
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
210+
pub enum MerkleProofStrategy {
211+
Compressed,
212+
Uncompressed,
213+
}
214+
215+
#[derive(Debug, Clone, Serialize, Deserialize)]
216+
pub enum DeduplicationStrategy {
217+
Enabled, // Sort + dedup indices
218+
Disabled, // Preserve order/multiplicity
219+
}
220+
209221
/// Configuration parameters for WHIR proofs.
210222
#[derive(Clone, Serialize, Deserialize)]
211223
pub struct ProtocolParameters<MerkleConfig, PowStrategy>
@@ -239,6 +251,16 @@ where
239251

240252
/// Number of polynomials committed in the batch.
241253
pub batch_size: usize,
254+
255+
/// Strategy to decide on the deduplication of challenges
256+
/// Standard: Sort + dedup indices
257+
/// Recursive: Preserve order/multiplicity
258+
pub deduplication_strategy: DeduplicationStrategy,
259+
260+
/// Strategy to decide on compression of merkle proofs used in proving.
261+
/// Standard: Compressed proofs.
262+
/// Recursive: Full Proofs.
263+
pub merkle_proof_strategy: MerkleProofStrategy,
242264
}
243265

244266
impl<MerkleConfig, PowStrategy> Debug for ProtocolParameters<MerkleConfig, PowStrategy>

src/whir/batching.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,10 @@ mod batching_tests {
3737
parameters::default_config,
3838
},
3939
},
40-
parameters::{FoldingFactor, MultivariateParameters, ProtocolParameters, SoundnessType},
40+
parameters::{
41+
DeduplicationStrategy, FoldingFactor, MerkleProofStrategy, MultivariateParameters,
42+
ProtocolParameters, SoundnessType,
43+
},
4144
poly_utils::{
4245
coeffs::CoefficientList, evals::EvaluationsList, multilinear::MultilinearPoint,
4346
},
@@ -114,6 +117,8 @@ mod batching_tests {
114117
_pow_parameters: Default::default(),
115118
starting_log_inv_rate: 1,
116119
batch_size,
120+
deduplication_strategy: DeduplicationStrategy::Enabled,
121+
merkle_proof_strategy: MerkleProofStrategy::Compressed,
117122
};
118123

119124
// Build global configuration from multivariate + protocol parameters
@@ -172,7 +177,7 @@ mod batching_tests {
172177
statement.add_constraint(linear_claim_weight, sum);
173178

174179
// Instantiate the prover with the given parameters
175-
let prover = Prover(params.clone());
180+
let prover = Prover::new(params.clone());
176181

177182
// Extract verifier-side version of the statement (only public data)
178183

src/whir/committer/writer.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,10 @@ mod tests {
215215
parameters::default_config,
216216
},
217217
},
218-
parameters::{FoldingFactor, MultivariateParameters, ProtocolParameters, SoundnessType},
218+
parameters::{
219+
DeduplicationStrategy, FoldingFactor, MerkleProofStrategy, MultivariateParameters,
220+
ProtocolParameters, SoundnessType,
221+
},
219222
poly_utils::multilinear::MultilinearPoint,
220223
whir::domainsep::WhirDomainSeparator,
221224
};
@@ -254,6 +257,8 @@ mod tests {
254257
_pow_parameters: std::marker::PhantomData,
255258
starting_log_inv_rate: starting_rate,
256259
batch_size: 1,
260+
deduplication_strategy: DeduplicationStrategy::Enabled,
261+
merkle_proof_strategy: MerkleProofStrategy::Compressed,
257262
};
258263

259264
// Define multivariate parameters for the polynomial.
@@ -343,6 +348,8 @@ mod tests {
343348
_pow_parameters: Default::default(),
344349
starting_log_inv_rate: 1,
345350
batch_size: 1,
351+
deduplication_strategy: DeduplicationStrategy::Enabled,
352+
merkle_proof_strategy: MerkleProofStrategy::Compressed,
346353
},
347354
);
348355

@@ -383,6 +390,8 @@ mod tests {
383390
_pow_parameters: Default::default(),
384391
starting_log_inv_rate: 1,
385392
batch_size: 1,
393+
deduplication_strategy: DeduplicationStrategy::Enabled,
394+
merkle_proof_strategy: MerkleProofStrategy::Compressed,
386395
},
387396
);
388397

src/whir/merkle.rs

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
use ark_crypto_primitives::merkle_tree::{Config, LeafParam, MerkleTree, MultiPath, TwoToOneParam};
2+
use spongefish::{ProofError, ProofResult};
3+
4+
use crate::{
5+
crypto::merkle_tree::proof::FullMultiPath,
6+
parameters::MerkleProofStrategy,
7+
whir::utils::{HintDeserialize, HintSerialize},
8+
};
9+
10+
/// Prover-side state for emitting Merkle proofs using a fixed strategy.
11+
pub struct ProverMerkleState {
12+
strategy: MerkleProofStrategy,
13+
}
14+
15+
impl ProverMerkleState {
16+
pub const fn new(strategy: MerkleProofStrategy) -> Self {
17+
Self { strategy }
18+
}
19+
20+
pub fn write_proof_hint<ProverState, MerkleConfig>(
21+
&self,
22+
tree: &MerkleTree<MerkleConfig>,
23+
indices: &[usize],
24+
prover_state: &mut ProverState,
25+
) -> ProofResult<()>
26+
where
27+
MerkleConfig: Config,
28+
ProverState: HintSerialize,
29+
{
30+
match self.strategy {
31+
MerkleProofStrategy::Compressed => {
32+
let merkle_proof = tree
33+
.generate_multi_proof(indices.to_vec())
34+
.expect("indices sampled from transcript must be valid for the tree");
35+
prover_state.hint::<MultiPath<MerkleConfig>>(&merkle_proof)?;
36+
}
37+
MerkleProofStrategy::Uncompressed => {
38+
let proofs = indices
39+
.iter()
40+
.map(|&index| {
41+
tree.generate_proof(index)
42+
.expect("indices sampled from transcript must be valid for the tree")
43+
})
44+
.collect::<Vec<_>>();
45+
let merkle_proof: FullMultiPath<MerkleConfig> = proofs.into();
46+
47+
prover_state.hint::<FullMultiPath<MerkleConfig>>(&merkle_proof)?;
48+
}
49+
}
50+
51+
Ok(())
52+
}
53+
}
54+
55+
/// Verifier-side state for consuming Merkle proofs using a fixed strategy and hash parameters.
56+
pub struct VerifierMerkleState<'a, MerkleConfig>
57+
where
58+
MerkleConfig: Config,
59+
{
60+
strategy: MerkleProofStrategy,
61+
leaf_hash_params: &'a LeafParam<MerkleConfig>,
62+
two_to_one_params: &'a TwoToOneParam<MerkleConfig>,
63+
}
64+
65+
impl<'a, MerkleConfig> VerifierMerkleState<'a, MerkleConfig>
66+
where
67+
MerkleConfig: Config,
68+
{
69+
pub const fn new(
70+
strategy: MerkleProofStrategy,
71+
leaf_hash_params: &'a LeafParam<MerkleConfig>,
72+
two_to_one_params: &'a TwoToOneParam<MerkleConfig>,
73+
) -> Self {
74+
Self {
75+
strategy,
76+
leaf_hash_params,
77+
two_to_one_params,
78+
}
79+
}
80+
81+
pub fn read_and_verify_proof<VerifierState, L>(
82+
&self,
83+
verifier_state: &mut VerifierState,
84+
indices: &[usize],
85+
root: &MerkleConfig::InnerDigest,
86+
leaves: impl IntoIterator<Item = L>,
87+
) -> ProofResult<()>
88+
where
89+
VerifierState: HintDeserialize,
90+
L: Clone + std::borrow::Borrow<MerkleConfig::Leaf>,
91+
{
92+
let leaves: Vec<L> = leaves.into_iter().collect();
93+
94+
match self.strategy {
95+
MerkleProofStrategy::Compressed => {
96+
let merkle_proof: MultiPath<MerkleConfig> = verifier_state.hint()?;
97+
if merkle_proof.leaf_indexes != indices {
98+
return Err(ProofError::InvalidProof);
99+
}
100+
101+
let correct = merkle_proof
102+
.verify(
103+
self.leaf_hash_params,
104+
self.two_to_one_params,
105+
root,
106+
leaves.iter().cloned(),
107+
)
108+
.map_err(|_| ProofError::InvalidProof)?;
109+
if !correct {
110+
return Err(ProofError::InvalidProof);
111+
}
112+
}
113+
MerkleProofStrategy::Uncompressed => {
114+
let merkle_proof: FullMultiPath<MerkleConfig> = verifier_state.hint()?;
115+
if merkle_proof.indices() != indices {
116+
return Err(ProofError::InvalidProof);
117+
}
118+
119+
let correct = merkle_proof
120+
.verify(
121+
self.leaf_hash_params,
122+
self.two_to_one_params,
123+
root,
124+
leaves.iter().cloned(),
125+
)
126+
.map_err(|_| ProofError::InvalidProof)?;
127+
if !correct {
128+
return Err(ProofError::InvalidProof);
129+
}
130+
}
131+
}
132+
133+
Ok(())
134+
}
135+
}

0 commit comments

Comments
 (0)