1
1
use crate :: {
2
+ crypto_helper:: { MKTree , MKTreeStoreInMemory } ,
2
3
digesters:: {
3
4
cache:: ImmutableFileDigestCacheProvider , ImmutableDigester , ImmutableDigesterError ,
4
5
ImmutableFile ,
@@ -16,7 +17,7 @@ use std::{collections::BTreeMap, io, path::Path, sync::Arc};
16
17
type ComputedImmutablesDigestsResult = Result < ComputedImmutablesDigests , io:: Error > ;
17
18
18
19
struct ComputedImmutablesDigests {
19
- digests : Vec < HexEncodedDigest > ,
20
+ entries : BTreeMap < ImmutableFile , HexEncodedDigest > ,
20
21
new_cached_entries : Vec < ( ImmutableFileName , HexEncodedDigest ) > ,
21
22
}
22
23
@@ -91,7 +92,7 @@ impl ImmutableDigester for CardanoImmutableDigester {
91
92
let digest = {
92
93
let mut hasher = Sha256 :: new ( ) ;
93
94
hasher. update ( compute_beacon_hash ( & self . cardano_network , beacon) . as_bytes ( ) ) ;
94
- for digest in computed_immutables_digests. digests {
95
+ for ( _ , digest) in computed_immutables_digests. entries {
95
96
hasher. update ( digest) ;
96
97
}
97
98
let hash: [ u8 ; 32 ] = hasher. finalize ( ) . into ( ) ;
@@ -115,6 +116,40 @@ impl ImmutableDigester for CardanoImmutableDigester {
115
116
116
117
Ok ( digest)
117
118
}
119
+
120
+ async fn compute_merkle_tree (
121
+ & self ,
122
+ dirpath : & Path ,
123
+ beacon : & CardanoDbBeacon ,
124
+ ) -> Result < MKTree < MKTreeStoreInMemory > , ImmutableDigesterError > {
125
+ let immutables_to_process =
126
+ list_immutable_files_to_process ( dirpath, beacon. immutable_file_number ) ?;
127
+ info ! ( self . logger, ">> compute_merkle_tree" ; "beacon" => #?beacon, "nb_of_immutables" => immutables_to_process. len( ) ) ;
128
+ let computed_immutables_digests = self . process_immutables ( immutables_to_process) . await ?;
129
+
130
+ let digests: Vec < HexEncodedDigest > =
131
+ computed_immutables_digests. entries . into_values ( ) . collect ( ) ;
132
+ let mktree =
133
+ MKTree :: new ( & digests) . map_err ( ImmutableDigesterError :: MerkleTreeComputationError ) ?;
134
+
135
+ debug ! (
136
+ self . logger,
137
+ "Successfully computed Merkle tree for Cardano database" ; "beacon" => #?beacon) ;
138
+
139
+ if let Some ( cache_provider) = self . cache_provider . as_ref ( ) {
140
+ if let Err ( error) = cache_provider
141
+ . store ( computed_immutables_digests. new_cached_entries )
142
+ . await
143
+ {
144
+ warn ! (
145
+ self . logger, "Error while storing new immutable files digests to cache" ;
146
+ "error" => ?error
147
+ ) ;
148
+ }
149
+ }
150
+
151
+ Ok ( mktree)
152
+ }
118
153
}
119
154
120
155
fn list_immutable_files_to_process (
@@ -153,17 +188,17 @@ fn compute_immutables_digests(
153
188
total : entries. len ( ) ,
154
189
} ;
155
190
156
- let mut digests = Vec :: with_capacity ( entries . len ( ) ) ;
191
+ let mut digests = BTreeMap :: new ( ) ;
157
192
158
193
for ( ix, ( entry, cache) ) in entries. iter ( ) . enumerate ( ) {
159
194
match cache {
160
195
None => {
161
196
let data = hex:: encode ( entry. compute_raw_hash :: < Sha256 > ( ) ?) ;
162
- digests. push ( data. clone ( ) ) ;
197
+ digests. insert ( entry . clone ( ) , data. clone ( ) ) ;
163
198
new_cached_entries. push ( ( entry. filename . clone ( ) , data) ) ;
164
199
}
165
200
Some ( digest) => {
166
- digests. push ( digest. to_string ( ) ) ;
201
+ digests. insert ( entry . clone ( ) , digest. to_string ( ) ) ;
167
202
}
168
203
} ;
169
204
@@ -173,7 +208,7 @@ fn compute_immutables_digests(
173
208
}
174
209
175
210
Ok ( ComputedImmutablesDigests {
176
- digests,
211
+ entries : digests,
177
212
new_cached_entries,
178
213
} )
179
214
}
@@ -359,6 +394,33 @@ mod tests {
359
394
) ;
360
395
}
361
396
397
+ #[ tokio:: test]
398
+ async fn can_compute_merkle_tree_of_a_hundred_immutable_file_trio ( ) {
399
+ let immutable_db = db_builder ( "can_compute_merkle_tree_of_a_hundred_immutable_file_trio" )
400
+ . with_immutables ( & ( 1 ..=100 ) . collect :: < Vec < ImmutableFileNumber > > ( ) )
401
+ . append_immutable_trio ( )
402
+ . build ( ) ;
403
+ let logger = TestLogger :: stdout ( ) ;
404
+ let digester = CardanoImmutableDigester :: new (
405
+ "devnet" . to_string ( ) ,
406
+ Some ( Arc :: new ( MemoryImmutableFileDigestCacheProvider :: default ( ) ) ) ,
407
+ logger. clone ( ) ,
408
+ ) ;
409
+ let beacon = CardanoDbBeacon :: new ( 1 , 100 ) ;
410
+
411
+ let result = digester
412
+ . compute_merkle_tree ( & immutable_db. dir , & beacon)
413
+ . await
414
+ . expect ( "compute_merkle_tree must not fail" ) ;
415
+
416
+ let expected_merkle_root = result. compute_root ( ) . unwrap ( ) . to_hex ( ) ;
417
+
418
+ assert_eq ! (
419
+ "8552f75838176c967a33eb6da1fe5f3c9940b706d75a9c2352c0acd8439f3d84" . to_string( ) ,
420
+ expected_merkle_root
421
+ )
422
+ }
423
+
362
424
#[ tokio:: test]
363
425
async fn can_compute_hash_of_a_hundred_immutable_file_trio ( ) {
364
426
let immutable_db = db_builder ( "can_compute_hash_of_a_hundred_immutable_file_trio" )
@@ -385,8 +447,8 @@ mod tests {
385
447
}
386
448
387
449
#[ tokio:: test]
388
- async fn digests_are_stored_into_cache_provider ( ) {
389
- let immutable_db = db_builder ( "digests_are_stored_into_cache_provider " )
450
+ async fn compute_digest_store_digests_into_cache_provider ( ) {
451
+ let immutable_db = db_builder ( "compute_digest_store_digests_into_cache_provider " )
390
452
. with_immutables ( & [ 1 , 2 ] )
391
453
. append_immutable_trio ( )
392
454
. build ( ) ;
@@ -420,6 +482,42 @@ mod tests {
420
482
assert_eq ! ( expected, cached_entries) ;
421
483
}
422
484
485
+ #[ tokio:: test]
486
+ async fn compute_merkle_tree_store_digests_into_cache_provider ( ) {
487
+ let immutable_db = db_builder ( "compute_merkle_tree_store_digests_into_cache_provider" )
488
+ . with_immutables ( & [ 1 , 2 ] )
489
+ . append_immutable_trio ( )
490
+ . build ( ) ;
491
+ let immutables = immutable_db. immutables_files ;
492
+ let cache = Arc :: new ( MemoryImmutableFileDigestCacheProvider :: default ( ) ) ;
493
+ let logger = TestLogger :: stdout ( ) ;
494
+ let digester = CardanoImmutableDigester :: new (
495
+ "devnet" . to_string ( ) ,
496
+ Some ( cache. clone ( ) ) ,
497
+ logger. clone ( ) ,
498
+ ) ;
499
+ let beacon = CardanoDbBeacon :: new ( 1 , 2 ) ;
500
+
501
+ digester
502
+ . compute_merkle_tree ( & immutable_db. dir , & beacon)
503
+ . await
504
+ . expect ( "compute_digest must not fail" ) ;
505
+
506
+ let cached_entries = cache
507
+ . get ( immutables. clone ( ) )
508
+ . await
509
+ . expect ( "Cache read should not fail" ) ;
510
+ let expected: BTreeMap < _ , _ > = immutables
511
+ . into_iter ( )
512
+ . map ( |i| {
513
+ let digest = hex:: encode ( i. compute_raw_hash :: < Sha256 > ( ) . unwrap ( ) ) ;
514
+ ( i, Some ( digest) )
515
+ } )
516
+ . collect ( ) ;
517
+
518
+ assert_eq ! ( expected, cached_entries) ;
519
+ }
520
+
423
521
#[ tokio:: test]
424
522
async fn computed_digest_with_cold_or_hot_or_without_any_cache_are_equals ( ) {
425
523
let immutable_db = DummyImmutablesDbBuilder :: new (
@@ -464,6 +562,53 @@ mod tests {
464
562
) ;
465
563
}
466
564
565
+ #[ tokio:: test]
566
+ async fn computed_merkle_tree_with_cold_or_hot_or_without_any_cache_are_equals ( ) {
567
+ let immutable_db = DummyImmutablesDbBuilder :: new (
568
+ "computed_merkle_tree_with_cold_or_hot_or_without_any_cache_are_equals" ,
569
+ )
570
+ . with_immutables ( & [ 1 , 2 , 3 ] )
571
+ . append_immutable_trio ( )
572
+ . build ( ) ;
573
+ let logger = TestLogger :: stdout ( ) ;
574
+ let no_cache_digester =
575
+ CardanoImmutableDigester :: new ( "devnet" . to_string ( ) , None , logger. clone ( ) ) ;
576
+ let cache_digester = CardanoImmutableDigester :: new (
577
+ "devnet" . to_string ( ) ,
578
+ Some ( Arc :: new ( MemoryImmutableFileDigestCacheProvider :: default ( ) ) ) ,
579
+ logger. clone ( ) ,
580
+ ) ;
581
+ let beacon = CardanoDbBeacon :: new ( 1 , 3 ) ;
582
+
583
+ let without_cache_digest = no_cache_digester
584
+ . compute_merkle_tree ( & immutable_db. dir , & beacon)
585
+ . await
586
+ . expect ( "compute_merkle_tree must not fail" ) ;
587
+
588
+ let cold_cache_digest = cache_digester
589
+ . compute_merkle_tree ( & immutable_db. dir , & beacon)
590
+ . await
591
+ . expect ( "compute_merkle_tree must not fail" ) ;
592
+
593
+ let full_cache_digest = cache_digester
594
+ . compute_merkle_tree ( & immutable_db. dir , & beacon)
595
+ . await
596
+ . expect ( "compute_merkle_tree must not fail" ) ;
597
+
598
+ let without_cache_merkle_root = without_cache_digest. compute_root ( ) . unwrap ( ) ;
599
+ let cold_cache_merkle_root = cold_cache_digest. compute_root ( ) . unwrap ( ) ;
600
+ let full_cache_merkle_root = full_cache_digest. compute_root ( ) . unwrap ( ) ;
601
+ assert_eq ! (
602
+ without_cache_merkle_root, full_cache_merkle_root,
603
+ "Merkle roots with or without cache should be the same"
604
+ ) ;
605
+
606
+ assert_eq ! (
607
+ cold_cache_merkle_root, full_cache_merkle_root,
608
+ "Merkle roots with cold or with hot cache should be the same"
609
+ ) ;
610
+ }
611
+
467
612
#[ tokio:: test]
468
613
async fn hash_computation_is_quicker_with_a_full_cache ( ) {
469
614
let immutable_db = db_builder ( "hash_computation_is_quicker_with_a_full_cache" )
@@ -506,7 +651,7 @@ mod tests {
506
651
}
507
652
508
653
#[ tokio:: test]
509
- async fn cache_read_failure_dont_block_computation ( ) {
654
+ async fn cache_read_failure_dont_block_computations ( ) {
510
655
let immutable_db = db_builder ( "cache_read_failure_dont_block_computation" )
511
656
. with_immutables ( & [ 1 , 2 , 3 ] )
512
657
. append_immutable_trio ( )
@@ -530,6 +675,11 @@ mod tests {
530
675
. compute_digest ( & immutable_db. dir , & beacon)
531
676
. await
532
677
. expect ( "compute_digest must not fail even with cache write failure" ) ;
678
+
679
+ digester
680
+ . compute_merkle_tree ( & immutable_db. dir , & beacon)
681
+ . await
682
+ . expect ( "compute_merkle_tree must not fail even with cache write failure" ) ;
533
683
}
534
684
535
685
#[ tokio:: test]
@@ -557,5 +707,10 @@ mod tests {
557
707
. compute_digest ( & immutable_db. dir , & beacon)
558
708
. await
559
709
. expect ( "compute_digest must not fail even with cache read failure" ) ;
710
+
711
+ digester
712
+ . compute_merkle_tree ( & immutable_db. dir , & beacon)
713
+ . await
714
+ . expect ( "compute_merkle_tree must not fail even with cache read failure" ) ;
560
715
}
561
716
}
0 commit comments