Skip to content

Commit 344382f

Browse files
committed
Sync TransactionImporter changes to the mithril-signer
1 parent 9492050 commit 344382f

File tree

2 files changed

+112
-48
lines changed

2 files changed

+112
-48
lines changed

mithril-signer/src/cardano_transactions_importer.rs

Lines changed: 107 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,12 @@ pub trait TransactionStore: Send + Sync {
2222
/// Get the highest known transaction beacon
2323
async fn get_highest_beacon(&self) -> StdResult<Option<ChainPoint>>;
2424

25+
/// Get the highest stored block range root bounds
26+
async fn get_highest_block_range(&self) -> StdResult<Option<BlockRange>>;
27+
2528
/// Store list of transactions
2629
async fn store_transactions(&self, transactions: Vec<CardanoTransaction>) -> StdResult<()>;
2730

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-
3331
/// Get transactions in an interval of blocks
3432
async fn get_transactions_in_range(
3533
&self,
@@ -129,15 +127,16 @@ impl CardanoTransactionsImporter {
129127
Ok(())
130128
}
131129

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)),
141140
// Not enough block to form at least one block range
142141
Some(ranges) if ranges.is_empty() => return Ok(()),
143142
Some(ranges) => ranges,
@@ -181,7 +180,7 @@ impl CardanoTransactionsImporter {
181180
up_to_beacon: BlockNumber,
182181
) -> StdResult<()> {
183182
self.import_transactions(up_to_beacon).await?;
184-
self.import_block_ranges().await
183+
self.import_block_ranges(up_to_beacon).await
185184
}
186185
}
187186

@@ -315,7 +314,8 @@ mod tests {
315314
SqliteConnectionPool::build_from_connection(connection),
316315
)));
317316

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);
319319
let transactions = into_transactions(&blocks);
320320
repository.store_transactions(transactions).await.unwrap();
321321

@@ -325,7 +325,7 @@ mod tests {
325325
);
326326

327327
importer
328-
.import_block_ranges()
328+
.import_block_ranges(up_to_block_number)
329329
.await
330330
.expect("Transactions Importer should succeed");
331331

@@ -352,6 +352,7 @@ mod tests {
352352
SqliteConnectionPool::build_from_connection(connection),
353353
)));
354354

355+
let up_to_block_number = BlockRange::LENGTH * 4;
355356
// Two block ranges with a gap
356357
let blocks: Vec<ScannedBlock> = [
357358
build_blocks(0, BlockRange::LENGTH),
@@ -367,7 +368,7 @@ mod tests {
367368
);
368369

369370
importer
370-
.import_block_ranges()
371+
.import_block_ranges(up_to_block_number)
371372
.await
372373
.expect("Transactions Importer should succeed");
373374

@@ -397,7 +398,7 @@ mod tests {
397398
);
398399

399400
importer
400-
.import_block_ranges()
401+
.import_block_ranges(10_000)
401402
.await
402403
.expect("Transactions Importer should succeed");
403404

@@ -504,7 +505,8 @@ mod tests {
504505
SqliteConnectionPool::build_from_connection(connection),
505506
)));
506507

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);
508510
let transactions = into_transactions(&blocks);
509511
repository.store_transactions(transactions).await.unwrap();
510512
repository
@@ -540,7 +542,7 @@ mod tests {
540542
);
541543

542544
importer
543-
.import_block_ranges()
545+
.import_block_ranges(up_to_block_number)
544546
.await
545547
.expect("Transactions Importer should succeed");
546548

@@ -559,35 +561,97 @@ mod tests {
559561
);
560562
}
561563

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+
562628
#[tokio::test]
563629
async fn block_range_root_retrieves_only_strictly_required_transactions() {
564630
fn transactions_for_block(range: Range<BlockNumber>) -> StdResult<Vec<CardanoTransaction>> {
565631
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())
568634
.collect())
569635
}
636+
const HIGHEST_BLOCK_RANGE_START: BlockNumber = BlockRange::LENGTH;
637+
const UP_TO_BLOCK_NUMBER: BlockNumber = BlockRange::LENGTH * 5;
570638

571639
let importer = {
572640
let mut store_mock = MockTransactionStore::new();
573641
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+
})
579648
.once();
580649
store_mock
581650
.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`
589653
.withf(|range| {
590-
BlockRangesSequence::new(BlockRange::LENGTH..(BlockRange::LENGTH * 5))
654+
BlockRangesSequence::new(HIGHEST_BLOCK_RANGE_START..=UP_TO_BLOCK_NUMBER)
591655
.contains(range)
592656
})
593657
.returning(transactions_for_block);
@@ -602,7 +666,7 @@ mod tests {
602666
};
603667

604668
importer
605-
.import_block_ranges()
669+
.import_block_ranges(UP_TO_BLOCK_NUMBER)
606670
.await
607671
.expect("Transactions Importer should succeed");
608672
}
@@ -615,7 +679,8 @@ mod tests {
615679
)));
616680

617681
// 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);
619684
let transactions = into_transactions(&blocks);
620685
let expected_block_range_roots = vec![
621686
(
@@ -638,7 +703,7 @@ mod tests {
638703
);
639704

640705
importer
641-
.import_block_ranges()
706+
.import_block_ranges(up_to_block_number)
642707
.await
643708
.expect("Transactions Importer should succeed");
644709

@@ -717,14 +782,14 @@ mod tests {
717782
.await
718783
.unwrap();
719784

720-
let chain_point = ChainPoint::new(5, 130, "block_hash-131");
785+
let chain_point = ChainPoint::new(5, 130, "block_hash-130");
721786
let scanner = DumbBlockScanner::new().backward(chain_point);
722787

723788
let importer =
724789
CardanoTransactionsImporter::new_for_test(Arc::new(scanner), repository.clone());
725790

726791
importer
727-
.import(3000)
792+
.import_transactions(3000)
728793
.await
729794
.expect("Transactions Importer should succeed");
730795

@@ -791,7 +856,7 @@ mod tests {
791856
CardanoTransactionsImporter::new_for_test(Arc::new(scanner), repository.clone());
792857

793858
importer
794-
.import(3000)
859+
.import_transactions(3000)
795860
.await
796861
.expect("Transactions Importer should succeed");
797862

mithril-signer/src/database/repository/cardano_transaction_repository.rs

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,13 @@ impl TransactionStore for CardanoTransactionRepository {
1717
self.get_transaction_highest_chain_point().await
1818
}
1919

20-
async fn store_transactions(&self, transactions: Vec<CardanoTransaction>) -> StdResult<()> {
21-
self.store_transactions(transactions).await
20+
async fn get_highest_block_range(&self) -> StdResult<Option<BlockRange>> {
21+
let record = self.retrieve_highest_block_range_root().await?;
22+
Ok(record.map(|record| record.range))
2223
}
2324

24-
async fn get_block_interval_without_block_range_root(
25-
&self,
26-
) -> StdResult<Option<Range<BlockNumber>>> {
27-
self.get_block_interval_without_block_range_root().await
25+
async fn store_transactions(&self, transactions: Vec<CardanoTransaction>) -> StdResult<()> {
26+
self.store_transactions(transactions).await
2827
}
2928

3029
async fn get_transactions_in_range(

0 commit comments

Comments
 (0)