@@ -124,8 +124,11 @@ use commonware_cryptography::{
124124 transcript:: { Summary , Transcript } ,
125125 Hasher ,
126126} ;
127- use commonware_storage:: bmt:: { Builder , Proof } ;
128- use commonware_utils:: rational:: BigRationalExt ;
127+ use commonware_storage:: mmr:: {
128+ mem:: Mmr , verification:: multi_proof, Error as MmrError , Location , Proof , StandardHasher ,
129+ } ;
130+ use commonware_utils:: BigRationalExt as _;
131+ use futures:: executor:: block_on;
129132use num_rational:: BigRational ;
130133use rand:: seq:: SliceRandom as _;
131134use std:: { marker:: PhantomData , sync:: Arc } ;
@@ -294,7 +297,7 @@ impl Topology {
294297pub struct Shard < H : Hasher > {
295298 data_bytes : usize ,
296299 root : H :: Digest ,
297- inclusion_proofs : Vec < Proof < H > > ,
300+ inclusion_proof : Proof < H :: Digest > ,
298301 rows : Matrix ,
299302 checksum : Arc < Matrix > ,
300303}
@@ -303,7 +306,7 @@ impl<H: Hasher> PartialEq for Shard<H> {
303306 fn eq ( & self , other : & Self ) -> bool {
304307 self . data_bytes == other. data_bytes
305308 && self . root == other. root
306- && self . inclusion_proofs == other. inclusion_proofs
309+ && self . inclusion_proof == other. inclusion_proof
307310 && self . rows == other. rows
308311 && self . checksum == other. checksum
309312 }
@@ -315,7 +318,7 @@ impl<H: Hasher> EncodeSize for Shard<H> {
315318 fn encode_size ( & self ) -> usize {
316319 self . data_bytes . encode_size ( )
317320 + self . root . encode_size ( )
318- + self . inclusion_proofs . encode_size ( )
321+ + self . inclusion_proof . encode_size ( )
319322 + self . rows . encode_size ( )
320323 + self . checksum . encode_size ( )
321324 }
@@ -325,7 +328,7 @@ impl<H: Hasher> Write for Shard<H> {
325328 fn write ( & self , buf : & mut impl BufMut ) {
326329 self . data_bytes . write ( buf) ;
327330 self . root . write ( buf) ;
328- self . inclusion_proofs . write ( buf) ;
331+ self . inclusion_proof . write ( buf) ;
329332 self . rows . write ( buf) ;
330333 self . checksum . write ( buf) ;
331334 }
@@ -343,7 +346,7 @@ impl<H: Hasher> Read for Shard<H> {
343346 Ok ( Self {
344347 data_bytes,
345348 root : ReadExt :: read ( buf) ?,
346- inclusion_proofs : Read :: read_cfg ( buf, & ( RangeCfg :: from ( ..= max_els) , ( ) ) ) ?,
349+ inclusion_proof : Read :: read_cfg ( buf, & max_els) ?,
347350 rows : Read :: read_cfg ( buf, & max_els) ?,
348351 checksum : Arc :: new ( Read :: read_cfg ( buf, & max_els) ?) ,
349352 } )
@@ -352,27 +355,27 @@ impl<H: Hasher> Read for Shard<H> {
352355
353356#[ derive( Clone , Debug ) ]
354357pub struct ReShard < H : Hasher > {
355- inclusion_proofs : Vec < Proof < H > > ,
358+ inclusion_proof : Proof < H :: Digest > ,
356359 shard : Matrix ,
357360}
358361
359362impl < H : Hasher > PartialEq for ReShard < H > {
360363 fn eq ( & self , other : & Self ) -> bool {
361- self . inclusion_proofs == other. inclusion_proofs && self . shard == other. shard
364+ self . inclusion_proof == other. inclusion_proof && self . shard == other. shard
362365 }
363366}
364367
365368impl < H : Hasher > Eq for ReShard < H > { }
366369
367370impl < H : Hasher > EncodeSize for ReShard < H > {
368371 fn encode_size ( & self ) -> usize {
369- self . inclusion_proofs . encode_size ( ) + self . shard . encode_size ( )
372+ self . inclusion_proof . encode_size ( ) + self . shard . encode_size ( )
370373 }
371374}
372375
373376impl < H : Hasher > Write for ReShard < H > {
374377 fn write ( & self , buf : & mut impl BufMut ) {
375- self . inclusion_proofs . write ( buf) ;
378+ self . inclusion_proof . write ( buf) ;
376379 self . shard . write ( buf) ;
377380 }
378381}
@@ -388,7 +391,7 @@ impl<H: Hasher> Read for ReShard<H> {
388391 let max_data_els = F :: bits_to_elements ( max_data_bits) . max ( 1 ) ;
389392 Ok ( Self {
390393 // Worst case: every row is one data element, and the sample size is all rows.
391- inclusion_proofs : Read :: read_cfg ( buf, & ( RangeCfg :: from ( ..= max_data_els) , ( ) ) ) ?,
394+ inclusion_proof : Read :: read_cfg ( buf, & max_data_els) ?,
392395 shard : Read :: read_cfg ( buf, & max_data_els) ?,
393396 } )
394397 }
@@ -403,8 +406,8 @@ pub struct CheckedShard {
403406/// Take indices up to `total`, and shuffle them.
404407///
405408/// The shuffle depends, deterministically, on the transcript.
406- fn shuffle_indices ( transcript : & Transcript , total : usize ) -> Vec < usize > {
407- let mut out = ( 0 ..total) . collect :: < Vec < _ > > ( ) ;
409+ fn shuffle_indices ( transcript : & Transcript , total : usize ) -> Vec < Location > {
410+ let mut out = ( 0 ..total as u64 ) . map ( Location :: from ) . collect :: < Vec < _ > > ( ) ;
408411 out. shuffle ( & mut transcript. noise ( b"shuffle" ) ) ;
409412 out
410413}
@@ -427,7 +430,7 @@ pub struct CheckingData<H: Hasher> {
427430 root : H :: Digest ,
428431 checking_matrix : Matrix ,
429432 encoded_checksum : Matrix ,
430- shuffled_indices : Vec < usize > ,
433+ shuffled_indices : Vec < Location > ,
431434}
432435
433436impl < H : Hasher > CheckingData < H > {
@@ -479,32 +482,30 @@ impl<H: Hasher> CheckingData<H> {
479482 self . topology . check_index ( index) ?;
480483 if reshard. shard . rows ( ) != self . topology . samples
481484 || reshard. shard . cols ( ) != self . topology . data_cols
482- || reshard. inclusion_proofs . len ( ) != reshard. shard . rows ( )
483485 {
484486 return Err ( Error :: InvalidReShard ) ;
485487 }
486488 let index = index as usize ;
487489 let these_shuffled_indices = & self . shuffled_indices
488490 [ index * self . topology . samples ..( index + 1 ) * self . topology . samples ] ;
489- // Check every inclusion proof.
490- for ( ( & i, row) , proof) in these_shuffled_indices
491- . iter ( )
492- . zip ( reshard. shard . iter ( ) )
493- . zip ( & reshard. inclusion_proofs )
494- {
495- proof
496- . verify (
497- & mut H :: new ( ) ,
498- & F :: slice_digest :: < H > ( row) ,
499- i as u32 ,
500- & self . root ,
501- )
502- . map_err ( |_| Error :: InvalidReShard ) ?;
491+ let proof_elements = {
492+ these_shuffled_indices
493+ . iter ( )
494+ . zip ( reshard. shard . iter ( ) )
495+ . map ( |( & i, row) | ( F :: slice_digest :: < H > ( row) , i) )
496+ . collect :: < Vec < _ > > ( )
497+ } ;
498+ if !reshard. inclusion_proof . verify_multi_inclusion (
499+ & mut StandardHasher :: < H > :: new ( ) ,
500+ & proof_elements,
501+ & self . root ,
502+ ) {
503+ return Err ( Error :: InvalidReShard ) ;
503504 }
504505 let shard_checksum = reshard. shard . mul ( & self . checking_matrix ) ;
505506 // Check that the shard checksum rows match the encoded checksums
506507 for ( row, & i) in shard_checksum. iter ( ) . zip ( these_shuffled_indices) {
507- if row != & self . encoded_checksum [ i ] {
508+ if row != & self . encoded_checksum [ u64 :: from ( i ) as usize ] {
508509 return Err ( Error :: InvalidReShard ) ;
509510 }
510511 }
@@ -527,6 +528,8 @@ pub enum Error {
527528 InsufficientShards ( usize , usize ) ,
528529 #[ error( "insufficient unique rows {0} < {1}" ) ]
529530 InsufficientUniqueRows ( usize , usize ) ,
531+ #[ error( "failed to create inclusion proof: {0}" ) ]
532+ FailedToCreateInclusionProof ( MmrError ) ,
530533}
531534
532535const NAMESPACE : & [ u8 ] = b"commonware-zoda" ;
@@ -567,19 +570,23 @@ impl<H: Hasher> Scheme for Zoda<H> {
567570 topology. data_cols ,
568571 F :: stream_from_u64s ( iter_u64_le ( data) ) ,
569572 ) ;
573+
570574 // Step 2: Encode the data.
571575 let encoded_data = data
572576 . as_polynomials ( topology. encoded_rows )
573577 . expect ( "data has too many rows" )
574578 . evaluate ( )
575579 . data ( ) ;
580+
576581 // Step 3: Commit to the rows of the data.
577- let mut builder = Builder :: < H > :: new ( encoded_data. rows ( ) ) ;
582+ let mut hasher = StandardHasher :: < H > :: new ( ) ;
583+ let mut mmr = Mmr :: new ( ) ;
578584 for row in encoded_data. iter ( ) {
579- builder . add ( & F :: slice_digest :: < H > ( row) ) ;
585+ mmr . add_batched ( & mut hasher , & F :: slice_digest :: < H > ( row) ) ;
580586 }
581- let tree = builder. build ( ) ;
582- let root = tree. root ( ) ;
587+ let mmr = mmr. merkleize ( & mut hasher) ;
588+ let root = mmr. root ( & mut hasher) ;
589+
583590 // Step 4: Commit to the root, and the size of the data.
584591 let mut transcript = Transcript :: new ( NAMESPACE ) ;
585592 transcript. commit ( ( topology. data_bytes as u64 ) . encode ( ) ) ;
@@ -604,24 +611,19 @@ impl<H: Hasher> Scheme for Zoda<H> {
604611 topology. data_cols ,
605612 indices
606613 . iter ( )
607- . flat_map ( |& i| encoded_data[ i ] . iter ( ) . copied ( ) ) ,
614+ . flat_map ( |& i| encoded_data[ u64 :: from ( i ) as usize ] . iter ( ) . copied ( ) ) ,
608615 ) ;
609- let inclusion_proofs = indices
610- . iter ( )
611- . map ( |& i| {
612- tree. proof ( i as u32 )
613- . expect ( "Impossible: ZODA should always have at least 1 row" )
614- } )
615- . collect :: < Vec < _ > > ( ) ;
616- Shard {
616+ let inclusion_proof = block_on ( multi_proof ( & mmr, indices) )
617+ . map_err ( Error :: FailedToCreateInclusionProof ) ?;
618+ Ok ( Shard {
617619 data_bytes,
618620 root,
619- inclusion_proofs ,
621+ inclusion_proof ,
620622 rows,
621623 checksum : checksum. clone ( ) ,
622- }
624+ } )
623625 } )
624- . collect :: < Vec < _ > > ( ) ;
626+ . collect :: < Result < Vec < _ > , Error > > ( ) ? ;
625627 Ok ( ( commitment, shards) )
626628 }
627629
@@ -632,7 +634,7 @@ impl<H: Hasher> Scheme for Zoda<H> {
632634 shard : Self :: Shard ,
633635 ) -> Result < ( Self :: CheckingData , Self :: CheckedShard , Self :: ReShard ) , Self :: Error > {
634636 let reshard = ReShard {
635- inclusion_proofs : shard. inclusion_proofs ,
637+ inclusion_proof : shard. inclusion_proof ,
636638 shard : shard. rows ,
637639 } ;
638640 let checking_data = CheckingData :: reckon (
@@ -679,7 +681,7 @@ impl<H: Hasher> Scheme for Zoda<H> {
679681 let indices =
680682 & checking_data. shuffled_indices [ shard. index * samples..( shard. index + 1 ) * samples] ;
681683 for ( & i, row) in indices. iter ( ) . zip ( shard. shard . iter ( ) ) {
682- evaluation. fill_row ( i , row) ;
684+ evaluation. fill_row ( u64 :: from ( i ) as usize , row) ;
683685 }
684686 }
685687 // This should never happen, because we check each shard, and the shards
0 commit comments