@@ -23,11 +23,24 @@ use super::mmr_accumulator::MmrAccumulator;
23
23
24
24
const ROOT_MT_INDEX : u64 = 1 ;
25
25
26
+ /// A witness to facilitate the proving of the authenticity of a Merkle
27
+ /// authentication struct.
28
+ #[ derive( Debug , Clone ) ]
26
29
pub struct MerkleAuthenticationStructAuthenticityWitness {
27
30
// All indices are Merkle tree node indices
28
- nd_auth_struct_indices : Vec < u64 > ,
29
- nd_sibling_indices : Vec < ( u64 , u64 ) > ,
30
- nd_siblings : Vec < ( Digest , Digest ) > ,
31
+ pub nd_auth_struct_indices : Vec < u64 > ,
32
+ pub nd_sibling_indices : Vec < ( u64 , u64 ) > ,
33
+ pub nd_siblings : Vec < ( Digest , Digest ) > ,
34
+ }
35
+
36
+ /// An authentication structure that can be used to prove membership of a list
37
+ /// of leaves in a Merkle tree, along with the indexed leaves in question, and
38
+ /// the witness necessary to prove membership in a ZK program.
39
+ #[ derive( Debug , Clone ) ]
40
+ pub struct AuthenticatedMerkleAuthStruct {
41
+ pub auth_struct : Vec < Digest > ,
42
+ pub indexed_leafs : Vec < ( u64 , Digest ) > ,
43
+ pub witness : MerkleAuthenticationStructAuthenticityWitness ,
31
44
}
32
45
33
46
impl MerkleAuthenticationStructAuthenticityWitness {
@@ -190,103 +203,120 @@ impl MerkleAuthenticationStructAuthenticityWitness {
190
203
pub fn new_from_mmr_membership_proofs (
191
204
mmra : & MmrAccumulator ,
192
205
indexed_mmr_mps : Vec < ( u64 , Digest , MmrMembershipProof ) > ,
193
- ) -> ( Self , Vec < Digest > , Vec < ( u64 , Digest ) > ) {
194
- // TODO: Consider rewriting this to return a list of authenticated
195
- // authentication structs, one for each peak in question.
206
+ ) -> HashMap < u32 , AuthenticatedMerkleAuthStruct > {
207
+ #[ derive( Clone , Debug ) ]
208
+ struct IndexedAuthenticatedMmrLeaf {
209
+ merkle_tree_node_index : u64 ,
210
+ merkle_tree_leaf_index : u64 ,
211
+ leaf_digest : Digest ,
212
+ membership_proof : MmrMembershipProof ,
213
+ }
196
214
197
- // Verify that MMR leaf indices belong to the same peak
215
+ // Split indexed MMR-mps into a hashmap with one entry for each
216
+ // referenced peak in the MMR.
198
217
let num_mmr_leafs = mmra. num_leafs ( ) ;
199
- let mt_and_peak_indices = indexed_mmr_mps
200
- . iter ( )
201
- . map ( |( mmr_leaf_index, _leaf, _mmr_mp) | {
202
- leaf_index_to_mt_index_and_peak_index ( * mmr_leaf_index, num_mmr_leafs)
203
- } )
204
- . collect_vec ( ) ;
218
+ let mut peak_index_to_indexed_mmr_mp: HashMap < u32 , Vec < IndexedAuthenticatedMmrLeaf > > =
219
+ HashMap :: default ( ) ;
220
+ let peak_heights = get_peak_heights ( num_mmr_leafs) ;
221
+ for ( mmr_leaf_index, leaf, mmr_mp) in indexed_mmr_mps {
222
+ let ( mt_index, peak_index) =
223
+ leaf_index_to_mt_index_and_peak_index ( mmr_leaf_index, num_mmr_leafs) ;
224
+ let peak_index_as_usize: usize = peak_index. try_into ( ) . unwrap ( ) ;
225
+ let num_leafs_local_mt = 1 << peak_heights[ peak_index_as_usize] ;
226
+ let mt_leaf_index = mt_index - num_leafs_local_mt;
227
+ peak_index_to_indexed_mmr_mp
228
+ . entry ( peak_index)
229
+ . or_default ( )
230
+ . push ( IndexedAuthenticatedMmrLeaf {
231
+ merkle_tree_node_index : mt_index,
232
+ merkle_tree_leaf_index : mt_leaf_index,
233
+ leaf_digest : leaf,
234
+ membership_proof : mmr_mp,
235
+ } ) ;
236
+ }
205
237
206
- assert ! (
207
- mt_and_peak_indices
238
+ // Loop over all peaks and collect an authentication witness struct
239
+ // for each peak.
240
+ let mut peak_index_to_authenticated_auth_struct = HashMap :: default ( ) ;
241
+ for ( peak_index, indexed_mmr_mp_structs) in peak_index_to_indexed_mmr_mp {
242
+ let peak_index_as_usize: usize = peak_index. try_into ( ) . unwrap ( ) ;
243
+ let num_leafs_in_local_mt = 1 << peak_heights[ peak_index_as_usize] ;
244
+ let local_mt_leaf_indices = indexed_mmr_mp_structs
208
245
. iter ( )
209
- . map( |( _mt_index, peak_index) | peak_index)
210
- . unique( )
211
- . count( )
212
- < 2
213
- ) ;
214
- assert ! ( !mt_and_peak_indices. is_empty( ) , "" ) ;
215
-
216
- let peak_index = mt_and_peak_indices[ 0 ] . 1 ;
217
- let mt_indices = mt_and_peak_indices
218
- . into_iter ( )
219
- . map ( |( mt_index, _peak_index) | mt_index)
220
- . collect_vec ( ) ;
221
-
222
- let peak_index: usize = peak_index. try_into ( ) . unwrap ( ) ;
223
- let height_of_local_mt = get_peak_heights ( num_mmr_leafs) [ peak_index] ;
224
- let num_leafs_in_local_mt = 1 << height_of_local_mt;
225
- let local_mt_leaf_indices = mt_indices
226
- . iter ( )
227
- . map ( |mt_index| mt_index - num_leafs_in_local_mt)
228
- . collect_vec ( ) ;
229
-
230
- let ( nd_auth_struct_indices, nd_sibling_indices) =
231
- Self :: auth_struct_and_nd_indices ( num_leafs_in_local_mt, & local_mt_leaf_indices) ;
232
-
233
- // Collect all node digests that can be calculated
234
- let peak = mmra. peaks ( ) [ peak_index] ;
235
- let mut node_digests: HashMap < u64 , Digest > = HashMap :: default ( ) ;
236
- node_digests. insert ( ROOT_MT_INDEX , peak) ;
237
- for ( ( _mmr_leaf_index, mut node, mmr_mp) , mut mt_index) in indexed_mmr_mps
238
- . clone ( )
239
- . into_iter ( )
240
- . zip_eq ( mt_indices. clone ( ) )
241
- {
242
- for ap_elem in mmr_mp. authentication_path . iter ( ) {
243
- node_digests. insert ( mt_index, node) ;
244
- node_digests. insert ( mt_index ^ 1 , * ap_elem) ;
245
- node = if mt_index & 1 == 0 {
246
- Tip5 :: hash_pair ( node, * ap_elem)
247
- } else {
248
- Tip5 :: hash_pair ( * ap_elem, node)
249
- } ;
250
-
251
- mt_index /= 2 ;
246
+ . map ( |x| x. merkle_tree_leaf_index )
247
+ . collect_vec ( ) ;
248
+
249
+ let ( nd_auth_struct_indices, nd_sibling_indices) =
250
+ Self :: auth_struct_and_nd_indices ( num_leafs_in_local_mt, & local_mt_leaf_indices) ;
251
+ let peak = mmra. peaks ( ) [ peak_index_as_usize] ;
252
+
253
+ let mut node_digests: HashMap < u64 , Digest > = HashMap :: default ( ) ;
254
+ node_digests. insert ( ROOT_MT_INDEX , peak) ;
255
+
256
+ // Loop over all indexed leafs for this peak
257
+ for indexed_mmr_mp in indexed_mmr_mp_structs. iter ( ) {
258
+ let mut mt_node_index = indexed_mmr_mp. merkle_tree_node_index ;
259
+ let mut node = indexed_mmr_mp. leaf_digest ;
260
+
261
+ // Loop over all authentication path elements for this indexed leaf
262
+ for ap_elem in indexed_mmr_mp. membership_proof . authentication_path . iter ( ) {
263
+ node_digests. insert ( mt_node_index, node) ;
264
+ node_digests. insert ( mt_node_index ^ 1 , * ap_elem) ;
265
+ node = if mt_node_index & 1 == 0 {
266
+ Tip5 :: hash_pair ( node, * ap_elem)
267
+ } else {
268
+ Tip5 :: hash_pair ( * ap_elem, node)
269
+ } ;
270
+
271
+ mt_node_index /= 2 ;
272
+ }
273
+
274
+ // Sanity check that MMR-MPs are valid
275
+ assert_eq ! ( peak, node, "Derived peak must match provided peak" ) ;
252
276
}
253
-
254
- // Sanity check that MMR-MPs are valid
255
- assert_eq ! ( peak, node, "Derived peak must match provided peak" ) ;
277
+ let nd_siblings = nd_sibling_indices
278
+ . iter ( )
279
+ . map ( |( left_idx, right_idx) | ( node_digests[ left_idx] , node_digests[ right_idx] ) )
280
+ . collect_vec ( ) ;
281
+ let auth_struct = nd_auth_struct_indices
282
+ . iter ( )
283
+ . map ( |idx| node_digests[ idx] )
284
+ . collect_vec ( ) ;
285
+ let indexed_leafs = indexed_mmr_mp_structs
286
+ . into_iter ( )
287
+ . map ( |indexed_mmr_mp| {
288
+ (
289
+ indexed_mmr_mp. merkle_tree_leaf_index ,
290
+ indexed_mmr_mp. leaf_digest ,
291
+ )
292
+ } )
293
+ . collect_vec ( ) ;
294
+
295
+ let witness = Self {
296
+ nd_auth_struct_indices,
297
+ nd_sibling_indices,
298
+ nd_siblings,
299
+ } ;
300
+
301
+ peak_index_to_authenticated_auth_struct. insert (
302
+ peak_index,
303
+ AuthenticatedMerkleAuthStruct {
304
+ auth_struct,
305
+ indexed_leafs,
306
+ witness,
307
+ } ,
308
+ ) ;
256
309
}
257
310
258
- let nd_siblings = nd_sibling_indices
259
- . iter ( )
260
- . map ( |( left_idx, right_idx) | ( node_digests[ left_idx] , node_digests[ right_idx] ) )
261
- . collect_vec ( ) ;
262
- let auth_struct = nd_auth_struct_indices
263
- . iter ( )
264
- . map ( |idx| node_digests[ idx] )
265
- . collect_vec ( ) ;
266
- let indexed_leafs = local_mt_leaf_indices
267
- . into_iter ( )
268
- . zip_eq (
269
- indexed_mmr_mps
270
- . into_iter ( )
271
- . map ( |( _mmr_leaf_index, leaf, _mmr_mp) | leaf) ,
272
- )
273
- . collect_vec ( ) ;
274
-
275
- let auth_struct_witness = Self {
276
- nd_auth_struct_indices,
277
- nd_sibling_indices,
278
- nd_siblings,
279
- } ;
280
-
281
- ( auth_struct_witness, auth_struct, indexed_leafs)
311
+ peak_index_to_authenticated_auth_struct
282
312
}
283
313
284
314
/// Return the authentication structure witness, authentication structure,
285
315
/// and the (leaf-index, leaf-digest) pairs.
286
316
pub fn new_from_merkle_tree (
287
317
tree : & MerkleTree < Tip5 > ,
288
318
mut revealed_leaf_indices : Vec < u64 > ,
289
- ) -> ( Self , Vec < Digest > , Vec < ( u64 , Digest ) > ) {
319
+ ) -> AuthenticatedMerkleAuthStruct {
290
320
revealed_leaf_indices. sort_unstable ( ) ;
291
321
revealed_leaf_indices. dedup ( ) ;
292
322
revealed_leaf_indices. reverse ( ) ;
@@ -322,13 +352,17 @@ impl MerkleAuthenticationStructAuthenticityWitness {
322
352
. map ( |node_index| tree. node ( * node_index as usize ) . unwrap ( ) )
323
353
. collect_vec ( ) ;
324
354
325
- let auth_struct_witness = Self {
355
+ let witness = Self {
326
356
nd_auth_struct_indices,
327
357
nd_sibling_indices,
328
358
nd_siblings,
329
359
} ;
330
360
331
- ( auth_struct_witness, auth_struct, indexed_leafs)
361
+ AuthenticatedMerkleAuthStruct {
362
+ auth_struct,
363
+ indexed_leafs,
364
+ witness,
365
+ }
332
366
}
333
367
}
334
368
@@ -345,6 +379,48 @@ mod tests {
345
379
346
380
use super :: * ;
347
381
382
+ #[ proptest( cases = 20 ) ]
383
+ fn root_from_authentication_struct_mmr_prop_test (
384
+ #[ strategy( 0 ..u64 :: MAX / 2 ) ] mmr_leaf_count : u64 ,
385
+ #[ strategy( 0usize ..20 ) ] _num_revealed_leafs : usize ,
386
+ #[ strategy( vec( 0u64 ..#mmr_leaf_count, #_num_revealed_leafs) ) ]
387
+ mmr_revealed_leaf_indices : Vec < u64 > ,
388
+ ) {
389
+ let indexed_leafs_input: Vec < ( u64 , Digest ) > = mmr_revealed_leaf_indices
390
+ . iter ( )
391
+ . map ( |idx| ( * idx, random ( ) ) )
392
+ . collect_vec ( ) ;
393
+ let ( mmra, mmr_mps) = mmra_with_mps ( mmr_leaf_count, indexed_leafs_input. clone ( ) ) ;
394
+ let indexed_mmr_mps = mmr_mps
395
+ . into_iter ( )
396
+ . zip_eq ( indexed_leafs_input)
397
+ . map ( |( mmr_mp, ( idx, leaf) ) | ( idx, leaf, mmr_mp) )
398
+ . collect_vec ( ) ;
399
+
400
+ let authenticated_auth_structs =
401
+ MerkleAuthenticationStructAuthenticityWitness :: new_from_mmr_membership_proofs (
402
+ & mmra,
403
+ indexed_mmr_mps,
404
+ ) ;
405
+
406
+ let peak_heights = get_peak_heights ( mmr_leaf_count) ;
407
+ for ( peak_index, authentication_auth_struct) in authenticated_auth_structs {
408
+ let AuthenticatedMerkleAuthStruct {
409
+ auth_struct,
410
+ indexed_leafs,
411
+ witness,
412
+ } = & authentication_auth_struct;
413
+ let tree_height: u32 = peak_heights[ peak_index as usize ] ;
414
+ let computed_root = witness. root_from_authentication_struct (
415
+ tree_height,
416
+ auth_struct. to_owned ( ) ,
417
+ indexed_leafs. to_owned ( ) ,
418
+ ) ;
419
+
420
+ prop_assert_eq ! ( mmra. peaks( ) [ peak_index as usize ] , computed_root) ;
421
+ }
422
+ }
423
+
348
424
#[ test]
349
425
fn auth_struct_from_mmr_mps_test_height_5_9_indices ( ) {
350
426
let local_tree_height = 5 ;
@@ -360,17 +436,26 @@ mod tests {
360
436
. map ( |( mmr_mp, ( idx, leaf) ) | ( idx, leaf, mmr_mp) )
361
437
. collect_vec ( ) ;
362
438
363
- let ( mmr_auth_struct , auth_struct , indexed_leafs ) =
439
+ let authenticity_witnesses =
364
440
MerkleAuthenticationStructAuthenticityWitness :: new_from_mmr_membership_proofs (
365
441
& mmra,
366
442
indexed_mmr_mps,
367
443
) ;
444
+ assert ! (
445
+ authenticity_witnesses. len( ) . is_one( ) ,
446
+ "All indices belong to first peak"
447
+ ) ;
448
+ let AuthenticatedMerkleAuthStruct {
449
+ auth_struct,
450
+ indexed_leafs,
451
+ witness,
452
+ } = & authenticity_witnesses[ & 0 ] ;
368
453
369
454
let tree_height: u32 = local_tree_height. try_into ( ) . unwrap ( ) ;
370
- let computed_root = mmr_auth_struct . root_from_authentication_struct (
455
+ let computed_root = witness . root_from_authentication_struct (
371
456
tree_height,
372
- auth_struct,
373
- indexed_leafs,
457
+ auth_struct. to_owned ( ) ,
458
+ indexed_leafs. to_owned ( ) ,
374
459
) ;
375
460
376
461
let peak_index = 0 ;
@@ -393,17 +478,26 @@ mod tests {
393
478
. map ( |( mmr_mp, ( idx, leaf) ) | ( idx, leaf, mmr_mp) )
394
479
. collect_vec ( ) ;
395
480
396
- let ( mmr_auth_struct , auth_struct , indexed_leafs ) =
481
+ let authenticity_witnesses =
397
482
MerkleAuthenticationStructAuthenticityWitness :: new_from_mmr_membership_proofs (
398
483
& mmra,
399
484
indexed_mmr_mps,
400
485
) ;
486
+ assert ! (
487
+ authenticity_witnesses. len( ) . is_one( ) ,
488
+ "All indices belong to first peak"
489
+ ) ;
490
+ let AuthenticatedMerkleAuthStruct {
491
+ auth_struct,
492
+ indexed_leafs,
493
+ witness,
494
+ } = & authenticity_witnesses[ & 0 ] ;
401
495
402
496
let tree_height: u32 = local_tree_height. try_into ( ) . unwrap ( ) ;
403
- let computed_root = mmr_auth_struct . root_from_authentication_struct (
497
+ let computed_root = witness . root_from_authentication_struct (
404
498
tree_height,
405
- auth_struct,
406
- indexed_leafs,
499
+ auth_struct. to_owned ( ) ,
500
+ indexed_leafs. to_owned ( ) ,
407
501
) ;
408
502
409
503
let peak_index = 0 ;
@@ -423,18 +517,20 @@ mod tests {
423
517
let leafs: Vec < Digest > = random_elements ( num_leafs. try_into ( ) . unwrap ( ) ) ;
424
518
let tree = MerkleTree :: < Tip5 > :: new :: < CpuParallel > ( & leafs) . unwrap ( ) ;
425
519
426
- let ( mmr_auth_struct , auth_struct , indexed_leafs ) =
520
+ let authenticated_auth_struct =
427
521
MerkleAuthenticationStructAuthenticityWitness :: new_from_merkle_tree (
428
522
& tree,
429
523
revealed_leaf_indices,
430
524
) ;
431
-
432
- let tree_height: u32 = tree_height. try_into ( ) . unwrap ( ) ;
433
- let computed_root = mmr_auth_struct. root_from_authentication_struct (
434
- tree_height,
525
+ let AuthenticatedMerkleAuthStruct {
435
526
auth_struct,
436
527
indexed_leafs,
437
- ) ;
528
+ witness,
529
+ } = authenticated_auth_struct;
530
+
531
+ let tree_height: u32 = tree_height. try_into ( ) . unwrap ( ) ;
532
+ let computed_root =
533
+ witness. root_from_authentication_struct ( tree_height, auth_struct, indexed_leafs) ;
438
534
let expected_root = tree. root ( ) ;
439
535
prop_assert_eq ! ( expected_root, computed_root) ;
440
536
}
0 commit comments