@@ -22,14 +22,12 @@ pub trait TransactionStore: Send + Sync {
22
22
/// Get the highest known transaction beacon
23
23
async fn get_highest_beacon ( & self ) -> StdResult < Option < ChainPoint > > ;
24
24
25
+ /// Get the highest stored block range root bounds
26
+ async fn get_highest_block_range ( & self ) -> StdResult < Option < BlockRange > > ;
27
+
25
28
/// Store list of transactions
26
29
async fn store_transactions ( & self , transactions : Vec < CardanoTransaction > ) -> StdResult < ( ) > ;
27
30
28
- /// Get the interval of blocks whose merkle root has yet to be computed
29
- async fn get_block_interval_without_block_range_root (
30
- & self ,
31
- ) -> StdResult < Option < Range < BlockNumber > > > ;
32
-
33
31
/// Get transactions in an interval of blocks
34
32
async fn get_transactions_in_range (
35
33
& self ,
@@ -129,15 +127,16 @@ impl CardanoTransactionsImporter {
129
127
Ok ( ( ) )
130
128
}
131
129
132
- async fn import_block_ranges ( & self ) -> StdResult < ( ) > {
133
- let block_ranges = match self
134
- . transaction_store
135
- . get_block_interval_without_block_range_root ( )
136
- . await ?
137
- . map ( |range| BlockRange :: all_block_ranges_in ( BlockRange :: start ( range. start ) ..range. end ) )
138
- {
139
- // Everything is already computed
140
- None => return Ok ( ( ) ) ,
130
+ async fn import_block_ranges ( & self , until : BlockNumber ) -> StdResult < ( ) > {
131
+ let block_ranges = match self . transaction_store . get_highest_block_range ( ) . await ?. map (
132
+ |highest_stored_block_range| {
133
+ BlockRange :: all_block_ranges_in (
134
+ BlockRange :: start ( highest_stored_block_range. end ) ..=( until) ,
135
+ )
136
+ } ,
137
+ ) {
138
+ // No block range root stored yet, start from the beginning
139
+ None => BlockRange :: all_block_ranges_in ( 0 ..=( until) ) ,
141
140
// Not enough block to form at least one block range
142
141
Some ( ranges) if ranges. is_empty ( ) => return Ok ( ( ) ) ,
143
142
Some ( ranges) => ranges,
@@ -181,7 +180,7 @@ impl CardanoTransactionsImporter {
181
180
up_to_beacon : BlockNumber ,
182
181
) -> StdResult < ( ) > {
183
182
self . import_transactions ( up_to_beacon) . await ?;
184
- self . import_block_ranges ( ) . await
183
+ self . import_block_ranges ( up_to_beacon ) . await
185
184
}
186
185
}
187
186
@@ -315,7 +314,8 @@ mod tests {
315
314
SqliteConnectionPool :: build_from_connection ( connection) ,
316
315
) ) ) ;
317
316
318
- let blocks = build_blocks ( 0 , BlockRange :: LENGTH * 5 + 1 ) ;
317
+ let up_to_block_number = BlockRange :: LENGTH * 5 ;
318
+ let blocks = build_blocks ( 0 , up_to_block_number + 1 ) ;
319
319
let transactions = into_transactions ( & blocks) ;
320
320
repository. store_transactions ( transactions) . await . unwrap ( ) ;
321
321
@@ -325,7 +325,7 @@ mod tests {
325
325
) ;
326
326
327
327
importer
328
- . import_block_ranges ( )
328
+ . import_block_ranges ( up_to_block_number )
329
329
. await
330
330
. expect ( "Transactions Importer should succeed" ) ;
331
331
@@ -352,6 +352,7 @@ mod tests {
352
352
SqliteConnectionPool :: build_from_connection ( connection) ,
353
353
) ) ) ;
354
354
355
+ let up_to_block_number = BlockRange :: LENGTH * 4 ;
355
356
// Two block ranges with a gap
356
357
let blocks: Vec < ScannedBlock > = [
357
358
build_blocks ( 0 , BlockRange :: LENGTH ) ,
@@ -367,7 +368,7 @@ mod tests {
367
368
) ;
368
369
369
370
importer
370
- . import_block_ranges ( )
371
+ . import_block_ranges ( up_to_block_number )
371
372
. await
372
373
. expect ( "Transactions Importer should succeed" ) ;
373
374
@@ -397,7 +398,7 @@ mod tests {
397
398
) ;
398
399
399
400
importer
400
- . import_block_ranges ( )
401
+ . import_block_ranges ( 10_000 )
401
402
. await
402
403
. expect ( "Transactions Importer should succeed" ) ;
403
404
@@ -504,7 +505,8 @@ mod tests {
504
505
SqliteConnectionPool :: build_from_connection ( connection) ,
505
506
) ) ) ;
506
507
507
- let blocks = build_blocks ( 0 , BlockRange :: LENGTH * 4 + 1 ) ;
508
+ let up_to_block_number = BlockRange :: LENGTH * 4 ;
509
+ let blocks = build_blocks ( 0 , up_to_block_number + 1 ) ;
508
510
let transactions = into_transactions ( & blocks) ;
509
511
repository. store_transactions ( transactions) . await . unwrap ( ) ;
510
512
repository
@@ -540,7 +542,7 @@ mod tests {
540
542
) ;
541
543
542
544
importer
543
- . import_block_ranges ( )
545
+ . import_block_ranges ( up_to_block_number )
544
546
. await
545
547
. expect ( "Transactions Importer should succeed" ) ;
546
548
@@ -559,35 +561,97 @@ mod tests {
559
561
) ;
560
562
}
561
563
564
+ #[ tokio:: test]
565
+ async fn can_compute_block_ranges_up_to_the_strict_end_of_a_block_range ( ) {
566
+ let connection = cardano_tx_db_connection ( ) . unwrap ( ) ;
567
+ let repository = Arc :: new ( CardanoTransactionRepository :: new ( Arc :: new (
568
+ SqliteConnectionPool :: build_from_connection ( connection) ,
569
+ ) ) ) ;
570
+
571
+ // Transactions for all blocks in the (15..=29) interval
572
+ let blocks = build_blocks ( BlockRange :: LENGTH , BlockRange :: LENGTH - 1 ) ;
573
+ let transactions = into_transactions ( & blocks) ;
574
+ repository. store_transactions ( transactions) . await . unwrap ( ) ;
575
+
576
+ let importer = CardanoTransactionsImporter :: new_for_test (
577
+ Arc :: new ( MockBlockScannerImpl :: new ( ) ) ,
578
+ repository. clone ( ) ,
579
+ ) ;
580
+
581
+ importer
582
+ . import_block_ranges ( BlockRange :: LENGTH * 2 - 1 )
583
+ . await
584
+ . expect ( "Transactions Importer should succeed" ) ;
585
+
586
+ let block_range_roots = repository. get_all_block_range_root ( ) . unwrap ( ) ;
587
+ assert_eq ! (
588
+ vec![ BlockRange :: from_block_number( BlockRange :: LENGTH ) ] ,
589
+ block_range_roots
590
+ . into_iter( )
591
+ . map( |r| r. range)
592
+ . collect:: <Vec <_>>( )
593
+ ) ;
594
+ }
595
+
596
+ #[ tokio:: test]
597
+ async fn can_compute_block_ranges_even_if_last_blocks_in_range_dont_have_transactions ( ) {
598
+ let connection = cardano_tx_db_connection ( ) . unwrap ( ) ;
599
+ let repository = Arc :: new ( CardanoTransactionRepository :: new ( Arc :: new (
600
+ SqliteConnectionPool :: build_from_connection ( connection) ,
601
+ ) ) ) ;
602
+
603
+ // For the block range (15..=29) we only have transactions in the 10 first blocks (15..=24)
604
+ let blocks = build_blocks ( BlockRange :: LENGTH , 10 ) ;
605
+ let transactions = into_transactions ( & blocks) ;
606
+ repository. store_transactions ( transactions) . await . unwrap ( ) ;
607
+
608
+ let importer = CardanoTransactionsImporter :: new_for_test (
609
+ Arc :: new ( MockBlockScannerImpl :: new ( ) ) ,
610
+ repository. clone ( ) ,
611
+ ) ;
612
+
613
+ importer
614
+ . import_block_ranges ( BlockRange :: LENGTH * 2 )
615
+ . await
616
+ . expect ( "Transactions Importer should succeed" ) ;
617
+
618
+ let block_range_roots = repository. get_all_block_range_root ( ) . unwrap ( ) ;
619
+ assert_eq ! (
620
+ vec![ BlockRange :: from_block_number( BlockRange :: LENGTH ) ] ,
621
+ block_range_roots
622
+ . into_iter( )
623
+ . map( |r| r. range)
624
+ . collect:: <Vec <_>>( )
625
+ ) ;
626
+ }
627
+
562
628
#[ tokio:: test]
563
629
async fn block_range_root_retrieves_only_strictly_required_transactions ( ) {
564
630
fn transactions_for_block ( range : Range < BlockNumber > ) -> StdResult < Vec < CardanoTransaction > > {
565
631
Ok ( build_blocks ( range. start , range. count ( ) as BlockNumber )
566
- . iter ( )
567
- . flat_map ( |b| b. clone ( ) . into_transactions ( ) )
632
+ . into_iter ( )
633
+ . flat_map ( |b| b. into_transactions ( ) )
568
634
. collect ( ) )
569
635
}
636
+ const HIGHEST_BLOCK_RANGE_START : BlockNumber = BlockRange :: LENGTH ;
637
+ const UP_TO_BLOCK_NUMBER : BlockNumber = BlockRange :: LENGTH * 5 ;
570
638
571
639
let importer = {
572
640
let mut store_mock = MockTransactionStore :: new ( ) ;
573
641
store_mock
574
- . expect_get_block_interval_without_block_range_root ( )
575
- // Specification of the interval without block range root
576
- // Note: in reality the lower bound will always be a multiple of BlockRange::LENGTH
577
- // since it's computed from the `block_range_root` table
578
- . returning ( || Ok ( Some ( ( BlockRange :: LENGTH + 2 ) ..( BlockRange :: LENGTH * 5 ) ) ) )
642
+ . expect_get_highest_block_range ( )
643
+ . returning ( || {
644
+ Ok ( Some ( BlockRange :: from_block_number (
645
+ HIGHEST_BLOCK_RANGE_START ,
646
+ ) ) )
647
+ } )
579
648
. once ( ) ;
580
649
store_mock
581
650
. expect_get_transactions_in_range ( )
582
- // Lower bound should be the block number that start after the last known block range end
583
- //
584
- // if it's not a multiple of BlockRange::LENGTH, it should be the start block number
585
- // of the block range that contains the end of the last known block range.
586
- //
587
- // Upper bound should be the block number of the highest transaction in a db that can be
588
- // included in a block range
651
+ // Lower bound should be the end block number of the last known block range
652
+ // Upper bound should be the block number provided to `import_block_ranges`
589
653
. withf ( |range| {
590
- BlockRangesSequence :: new ( BlockRange :: LENGTH .. ( BlockRange :: LENGTH * 5 ) )
654
+ BlockRangesSequence :: new ( HIGHEST_BLOCK_RANGE_START ..= UP_TO_BLOCK_NUMBER )
591
655
. contains ( range)
592
656
} )
593
657
. returning ( transactions_for_block) ;
@@ -602,7 +666,7 @@ mod tests {
602
666
} ;
603
667
604
668
importer
605
- . import_block_ranges ( )
669
+ . import_block_ranges ( UP_TO_BLOCK_NUMBER )
606
670
. await
607
671
. expect ( "Transactions Importer should succeed" ) ;
608
672
}
@@ -615,7 +679,8 @@ mod tests {
615
679
) ) ) ;
616
680
617
681
// 2 block ranges worth of blocks with one more block that should be ignored for merkle root computation
618
- let blocks = build_blocks ( 0 , BlockRange :: LENGTH * 2 + 1 ) ;
682
+ let up_to_block_number = BlockRange :: LENGTH * 2 ;
683
+ let blocks = build_blocks ( 0 , up_to_block_number + 1 ) ;
619
684
let transactions = into_transactions ( & blocks) ;
620
685
let expected_block_range_roots = vec ! [
621
686
(
@@ -638,7 +703,7 @@ mod tests {
638
703
) ;
639
704
640
705
importer
641
- . import_block_ranges ( )
706
+ . import_block_ranges ( up_to_block_number )
642
707
. await
643
708
. expect ( "Transactions Importer should succeed" ) ;
644
709
@@ -717,14 +782,14 @@ mod tests {
717
782
. await
718
783
. unwrap ( ) ;
719
784
720
- let chain_point = ChainPoint :: new ( 5 , 130 , "block_hash-131 " ) ;
785
+ let chain_point = ChainPoint :: new ( 5 , 130 , "block_hash-130 " ) ;
721
786
let scanner = DumbBlockScanner :: new ( ) . backward ( chain_point) ;
722
787
723
788
let importer =
724
789
CardanoTransactionsImporter :: new_for_test ( Arc :: new ( scanner) , repository. clone ( ) ) ;
725
790
726
791
importer
727
- . import ( 3000 )
792
+ . import_transactions ( 3000 )
728
793
. await
729
794
. expect ( "Transactions Importer should succeed" ) ;
730
795
@@ -791,7 +856,7 @@ mod tests {
791
856
CardanoTransactionsImporter :: new_for_test ( Arc :: new ( scanner) , repository. clone ( ) ) ;
792
857
793
858
importer
794
- . import ( 3000 )
859
+ . import_transactions ( 3000 )
795
860
. await
796
861
. expect ( "Transactions Importer should succeed" ) ;
797
862
0 commit comments