1use std::ops::Range;
2use std::sync::Arc;
3
4use anyhow::Context;
5
6use mithril_common::StdResult;
7use mithril_common::crypto_helper::MKTreeNode;
8use mithril_common::entities::{
9 BlockHash, BlockNumber, BlockRange, CardanoBlockWithTransactions, CardanoTransaction,
10 ChainPoint, SlotNumber, TransactionHash,
11};
12
13use crate::database::query::{
14 DeleteBlockRangeRootQuery, DeleteCardanoBlockAndTransactionQuery,
15 DeleteLegacyBlockRangeRootQuery, GetBlockRangeRootQuery, GetCardanoBlockQuery,
16 GetCardanoBlockTransactionsQuery, GetCardanoTransactionQuery, GetLegacyBlockRangeRootQuery,
17 InsertBlockRangeRootQuery, InsertCardanoBlockQuery, InsertCardanoTransactionQuery,
18 InsertLegacyBlockRangeRootQuery,
19};
20use crate::database::record::{
21 BlockRangeRootRecord, CardanoBlockRecord, CardanoBlockTransactionsRecord,
22 CardanoTransactionRecord, IntoRecords,
23};
24use crate::sqlite::{ConnectionExtensions, SqliteConnection, SqliteConnectionPool};
25
26pub struct CardanoTransactionRepository {
31 connection_pool: Arc<SqliteConnectionPool>,
32}
33
34impl CardanoTransactionRepository {
35 pub fn new(connection_pool: Arc<SqliteConnectionPool>) -> Self {
37 Self { connection_pool }
38 }
39
40 pub async fn get_all_transactions(&self) -> StdResult<Vec<CardanoTransactionRecord>> {
42 self.connection_pool
43 .connection()?
44 .fetch_collect(GetCardanoTransactionQuery::all())
45 }
46
47 pub async fn get_all_blocks(&self) -> StdResult<Vec<CardanoBlockRecord>> {
49 self.connection_pool
50 .connection()?
51 .fetch_collect(GetCardanoBlockQuery::all())
52 }
53
54 pub async fn get_transactions_in_range_blocks(
57 &self,
58 range: Range<BlockNumber>,
59 ) -> StdResult<Vec<CardanoTransactionRecord>> {
60 self.connection_pool
61 .connection()?
62 .fetch_collect(GetCardanoTransactionQuery::between_blocks(range))
63 }
64
65 pub async fn get_blocks_with_transactions_in_range_blocks(
68 &self,
69 range: Range<BlockNumber>,
70 ) -> StdResult<Vec<CardanoBlockTransactionsRecord>> {
71 self.connection_pool
72 .connection()?
73 .fetch_collect(GetCardanoBlockTransactionsQuery::between_blocks(range))
74 }
75
76 pub async fn get_block<T: Into<BlockHash>>(
78 &self,
79 block_hash: T,
80 ) -> StdResult<Option<CardanoBlockRecord>> {
81 self.connection_pool
82 .connection()?
83 .fetch_first(GetCardanoBlockQuery::by_block_hash(&block_hash.into()))
84 }
85
86 pub async fn get_transaction<T: Into<TransactionHash>>(
88 &self,
89 transaction_hash: T,
90 ) -> StdResult<Option<CardanoTransactionRecord>> {
91 self.connection_pool.connection()?.fetch_first(
92 GetCardanoTransactionQuery::by_transaction_hash(transaction_hash),
93 )
94 }
95
96 pub async fn create_block_and_transactions(
98 &self,
99 blocks_with_transactions: Vec<CardanoBlockWithTransactions>,
100 ) -> StdResult<()> {
101 let connection = self.connection_pool.connection()?;
102
103 self.create_block_and_transactions_with_connection(&connection, blocks_with_transactions)
104 .await
105 }
106
107 async fn create_block_and_transactions_with_connection(
109 &self,
110 connection: &SqliteConnection,
111 blocks_with_transactions: Vec<CardanoBlockWithTransactions>,
112 ) -> StdResult<()> {
113 if blocks_with_transactions.is_empty() {
114 return Ok(());
115 }
116 let (blocks_records, transactions_records) = blocks_with_transactions.into_records();
117
118 connection.apply(InsertCardanoBlockQuery::insert_many(blocks_records)?)?;
119 if !transactions_records.is_empty() {
120 connection.apply(InsertCardanoTransactionQuery::insert_many(
121 transactions_records,
122 )?)?;
123 }
124
125 Ok(())
126 }
127
128 pub async fn create_block_range_roots<T: Into<BlockRangeRootRecord>>(
130 &self,
131 block_ranges: Vec<T>,
132 ) -> StdResult<Vec<BlockRangeRootRecord>> {
133 let records: Vec<BlockRangeRootRecord> =
134 block_ranges.into_iter().map(|tx| tx.into()).collect();
135 let connection = self.connection_pool.connection()?;
136
137 connection.fetch_collect(InsertBlockRangeRootQuery::insert_many(records)?)
138 }
139
140 pub async fn create_legacy_block_range_roots<T: Into<BlockRangeRootRecord>>(
142 &self,
143 block_ranges: Vec<T>,
144 ) -> StdResult<Vec<BlockRangeRootRecord>> {
145 let records: Vec<BlockRangeRootRecord> =
146 block_ranges.into_iter().map(|tx| tx.into()).collect();
147 let connection = self.connection_pool.connection()?;
148
149 connection.fetch_collect(InsertLegacyBlockRangeRootQuery::insert_many(records)?)
150 }
151
152 pub async fn get_transaction_highest_chain_point(&self) -> StdResult<Option<ChainPoint>> {
154 let first_transaction_with_highest_block_number = self
155 .connection_pool
156 .connection()?
157 .fetch_first(GetCardanoBlockQuery::with_highest_block_number())?;
158
159 Ok(first_transaction_with_highest_block_number.map(|record| {
160 ChainPoint::new(record.slot_number, record.block_number, record.block_hash)
161 }))
162 }
163
164 pub async fn get_prune_blocks_threshold(&self) -> StdResult<Option<BlockNumber>> {
170 let highest: Option<i64> = self.connection_pool.connection()?.query_single_cell(
171 r#"
172select coalesce(min(max_new.highest, max_legacy.highest), max_new.highest, max_legacy.highest)
173from (select max(start) as highest from block_range_root) max_new,
174 (select max(start) as highest from block_range_root_legacy) max_legacy;"#,
175 &[],
176 )?;
177 highest
178 .map(u64::try_from)
179 .transpose()
180 .map(|num| num.map(BlockNumber))
181 .with_context(||
182 format!("Integer field max(start) (value={highest:?}) is incompatible with u64 representation.")
183 )
184 }
185
186 pub async fn retrieve_block_range_roots_up_to(
189 &self,
190 block_number: BlockNumber,
191 ) -> StdResult<Box<dyn Iterator<Item = (BlockRange, MKTreeNode)> + '_>> {
192 let block_range_roots = self
193 .connection_pool
194 .connection()?
195 .fetch(GetBlockRangeRootQuery::contains_or_below_block_number(
196 block_number,
197 ))?
198 .map(|record| -> (BlockRange, MKTreeNode) { record.into() })
199 .collect::<Vec<_>>(); Ok(Box::new(block_range_roots.into_iter()))
202 }
203
204 pub async fn retrieve_legacy_block_range_roots_up_to(
207 &self,
208 block_number: BlockNumber,
209 ) -> StdResult<Box<dyn Iterator<Item = (BlockRange, MKTreeNode)> + '_>> {
210 let block_range_roots = self
211 .connection_pool
212 .connection()?
213 .fetch(GetLegacyBlockRangeRootQuery::contains_or_below_block_number(block_number))?
214 .map(|record| -> (BlockRange, MKTreeNode) { record.into() })
215 .collect::<Vec<_>>(); Ok(Box::new(block_range_roots.into_iter()))
218 }
219
220 pub async fn retrieve_highest_block_range_root(
222 &self,
223 ) -> StdResult<Option<BlockRangeRootRecord>> {
224 self.connection_pool
225 .connection()?
226 .fetch_first(GetBlockRangeRootQuery::highest())
227 }
228
229 pub async fn retrieve_highest_legacy_block_range_root(
231 &self,
232 ) -> StdResult<Option<BlockRangeRootRecord>> {
233 self.connection_pool
234 .connection()?
235 .fetch_first(GetLegacyBlockRangeRootQuery::highest())
236 }
237
238 pub async fn get_all(&self) -> StdResult<Vec<CardanoTransaction>> {
240 let records = self
241 .connection_pool
242 .connection()?
243 .fetch(GetCardanoTransactionQuery::all())?
244 .map(|record| record.into())
245 .collect();
246
247 Ok(records)
248 }
249
250 pub fn get_all_block_range_root(&self) -> StdResult<Vec<BlockRangeRootRecord>> {
252 self.connection_pool
253 .connection()?
254 .fetch_collect(GetBlockRangeRootQuery::all())
255 }
256
257 pub fn get_all_legacy_block_range_root(&self) -> StdResult<Vec<BlockRangeRootRecord>> {
259 self.connection_pool
260 .connection()?
261 .fetch_collect(GetLegacyBlockRangeRootQuery::all())
262 }
263
264 pub async fn store_blocks_and_transactions(
268 &self,
269 blocks_with_transactions: Vec<CardanoBlockWithTransactions>,
270 ) -> StdResult<()> {
271 const DB_TRANSACTION_SIZE: usize = 100000;
272
273 for transaction_chunk in blocks_with_transactions.chunks(DB_TRANSACTION_SIZE) {
275 let connection = self.connection_pool.connection()?;
276 let transaction = connection.begin_transaction()?;
277
278 for inner_chunk in transaction_chunk.chunks(100) {
280 self.create_block_and_transactions_with_connection(
281 &connection,
282 inner_chunk.to_vec(),
283 )
284 .await
285 .with_context(
286 || "CardanoTransactionRepository can not store blocks and transactions",
287 )?;
288 }
289
290 transaction.commit()?;
291 }
292 Ok(())
293 }
294
295 pub async fn get_closest_block_number_above_slot_number(
297 &self,
298 slot_number: SlotNumber,
299 ) -> StdResult<Option<BlockNumber>> {
300 let query = GetCardanoBlockQuery::with_highest_block_number_below_slot_number(slot_number);
301 let record = self.connection_pool.connection()?.fetch_first(query)?;
302
303 Ok(record.map(|r| r.block_number))
304 }
305
306 pub async fn get_transaction_by_hashes<T: Into<TransactionHash>>(
308 &self,
309 hashes: Vec<T>,
310 up_to: BlockNumber,
311 ) -> StdResult<Vec<CardanoTransactionRecord>> {
312 let query = GetCardanoTransactionQuery::by_transaction_hashes(
313 hashes.into_iter().map(Into::into).collect(),
314 up_to,
315 );
316 self.connection_pool.connection()?.fetch_collect(query)
317 }
318
319 pub async fn get_transaction_by_block_ranges(
321 &self,
322 block_ranges: Vec<BlockRange>,
323 ) -> StdResult<Vec<CardanoTransactionRecord>> {
324 let mut transactions = vec![];
325 for block_range in block_ranges {
328 let block_range_transactions: Vec<CardanoTransactionRecord> =
329 self.connection_pool.connection()?.fetch_collect(
330 GetCardanoTransactionQuery::by_block_ranges(vec![block_range]),
331 )?;
332 transactions.extend(block_range_transactions);
333 }
334
335 Ok(transactions)
336 }
337
338 pub async fn get_blocks_with_transactions_by_block_ranges(
340 &self,
341 block_ranges: Vec<BlockRange>,
342 ) -> StdResult<Vec<CardanoBlockTransactionsRecord>> {
343 let mut blocks_with_transactions = vec![];
344 for block_range in block_ranges {
347 let block_range_transactions: Vec<CardanoBlockTransactionsRecord> =
348 self.connection_pool.connection()?.fetch_collect(
349 GetCardanoBlockTransactionsQuery::by_block_ranges(vec![block_range]),
350 )?;
351 blocks_with_transactions.extend(block_range_transactions);
352 }
353
354 Ok(blocks_with_transactions)
355 }
356
357 pub async fn prune_transaction(&self, number_of_blocks_to_keep: BlockNumber) -> StdResult<()> {
360 if let Some(highest_block_range_start) = self.get_prune_blocks_threshold().await? {
361 let threshold = highest_block_range_start - number_of_blocks_to_keep;
362 let query =
363 DeleteCardanoBlockAndTransactionQuery::below_block_number_threshold(threshold)?;
364
365 let connection = self.connection_pool.connection()?;
366 connection.fetch_first(query)?;
367 }
368
369 Ok(())
370 }
371
372 pub async fn remove_rolled_back_transactions_and_block_range_by_block_number(
377 &self,
378 block_number: BlockNumber,
379 ) -> StdResult<()> {
380 let connection = self.connection_pool.connection()?;
381 let transaction = connection.begin_transaction()?;
382
383 connection.fetch_first(
384 DeleteCardanoBlockAndTransactionQuery::above_block_number_threshold(block_number)?,
385 )?;
386 connection.fetch_first(
387 DeleteBlockRangeRootQuery::contains_or_above_block_number_threshold(block_number)?,
388 )?;
389 connection.fetch_first(
390 DeleteLegacyBlockRangeRootQuery::contains_or_above_block_number_threshold(
391 block_number,
392 )?,
393 )?;
394
395 transaction.commit()?;
396 Ok(())
397 }
398
399 pub async fn remove_rolled_back_blocks_transactions_and_block_range_by_slot_number(
404 &self,
405 slot_number: SlotNumber,
406 ) -> StdResult<()> {
407 if let Some(block_number) =
408 self.get_closest_block_number_above_slot_number(slot_number).await?
409 {
410 self.remove_rolled_back_transactions_and_block_range_by_block_number(block_number)
411 .await?;
412 }
413
414 Ok(())
415 }
416}
417
418#[cfg(test)]
419mod tests {
420 use crate::database::query::GetLegacyBlockRangeRootQuery;
421 use crate::database::test_helper::cardano_tx_db_connection;
422
423 use super::*;
424
425 #[tokio::test]
426 async fn repository_create_and_get_blocks_and_transactions() {
427 let repository = CardanoTransactionRepository::new(Arc::new(
428 SqliteConnectionPool::build(1, cardano_tx_db_connection).unwrap(),
429 ));
430
431 repository
432 .create_block_and_transactions(vec![
433 CardanoBlockWithTransactions::new(
434 "block_hash-123",
435 BlockNumber(10),
436 SlotNumber(50),
437 vec!["tx_hash-123", "tx_hash-456"],
438 ),
439 CardanoBlockWithTransactions::new(
440 "block_hash-789",
441 BlockNumber(11),
442 SlotNumber(51),
443 vec!["tx_hash-789"],
444 ),
445 ])
446 .await
447 .unwrap();
448
449 {
450 let block_result = repository.get_block("block_hash-123").await.unwrap();
451 assert_eq!(
452 Some(CardanoBlockRecord {
453 block_hash: "block_hash-123".to_string(),
454 block_number: BlockNumber(10),
455 slot_number: SlotNumber(50),
456 }),
457 block_result
458 );
459
460 let transaction_result = repository.get_transaction("tx_hash-123").await.unwrap();
461 assert_eq!(
462 Some(CardanoTransactionRecord {
463 transaction_hash: "tx_hash-123".to_string(),
464 block_number: BlockNumber(10),
465 slot_number: SlotNumber(50),
466 block_hash: "block_hash-123".to_string(),
467 }),
468 transaction_result
469 );
470 }
471 {
472 let transaction_result = repository.get_transaction("not-exist").await.unwrap();
473 assert_eq!(None, transaction_result);
474 }
475 }
476
477 #[tokio::test]
478 async fn repository_create_and_get_blocks_and_transactions_dont_fail_if_empty_or_no_transactions()
479 {
480 let repository = CardanoTransactionRepository::new(Arc::new(
481 SqliteConnectionPool::build(1, cardano_tx_db_connection).unwrap(),
482 ));
483
484 repository.create_block_and_transactions(vec![]).await.unwrap();
485 repository
486 .create_block_and_transactions(vec![CardanoBlockWithTransactions::new(
487 "block_hash-10",
488 BlockNumber(10),
489 SlotNumber(50),
490 Vec::<String>::new(),
491 )])
492 .await
493 .unwrap();
494 }
495
496 #[tokio::test]
497 async fn repository_get_transaction_by_hashes() {
498 let repository = CardanoTransactionRepository::new(Arc::new(
499 SqliteConnectionPool::build(1, cardano_tx_db_connection).unwrap(),
500 ));
501
502 repository
503 .create_block_and_transactions(vec![
504 CardanoBlockWithTransactions::new(
505 "block_hash-10",
506 BlockNumber(10),
507 SlotNumber(50),
508 vec!["tx_hash-123", "tx_hash-456", "tx_hash-789"],
509 ),
510 CardanoBlockWithTransactions::new(
511 "block_hash-101",
512 BlockNumber(101),
513 SlotNumber(100),
514 vec!["tx_hash-000"],
515 ),
516 ])
517 .await
518 .unwrap();
519
520 {
521 let transactions = repository
522 .get_transaction_by_hashes(vec!["tx_hash-123", "tx_hash-789"], BlockNumber(100))
523 .await
524 .unwrap();
525
526 assert_eq!(
527 vec![
528 CardanoTransactionRecord::new(
529 "tx_hash-123",
530 BlockNumber(10),
531 SlotNumber(50),
532 "block_hash-10"
533 ),
534 CardanoTransactionRecord::new(
535 "tx_hash-789",
536 BlockNumber(10),
537 SlotNumber(50),
538 "block_hash-10"
539 ),
540 ],
541 transactions
542 );
543 }
544 {
545 let transactions = repository
546 .get_transaction_by_hashes(
547 vec!["tx_hash-123", "tx_hash-789", "tx_hash-000"],
548 BlockNumber(100),
549 )
550 .await
551 .unwrap();
552
553 assert_eq!(
554 vec![
555 CardanoTransactionRecord::new(
556 "tx_hash-123",
557 BlockNumber(10),
558 SlotNumber(50),
559 "block_hash-10"
560 ),
561 CardanoTransactionRecord::new(
562 "tx_hash-789",
563 BlockNumber(10),
564 SlotNumber(50),
565 "block_hash-10"
566 ),
567 ],
568 transactions
569 );
570 }
571 {
572 let transactions = repository
573 .get_transaction_by_hashes(
574 vec!["tx_hash-123", "tx_hash-789", "tx_hash-000"],
575 BlockNumber(101),
576 )
577 .await
578 .unwrap();
579
580 assert_eq!(
581 vec![
582 CardanoTransactionRecord::new(
583 "tx_hash-123",
584 BlockNumber(10),
585 SlotNumber(50),
586 "block_hash-10"
587 ),
588 CardanoTransactionRecord::new(
589 "tx_hash-789",
590 BlockNumber(10),
591 SlotNumber(50),
592 "block_hash-10"
593 ),
594 CardanoTransactionRecord::new(
595 "tx_hash-000",
596 BlockNumber(101),
597 SlotNumber(100),
598 "block_hash-101"
599 ),
600 ],
601 transactions
602 );
603 }
604 {
605 let transactions = repository
606 .get_transaction_by_hashes(vec!["not-exist".to_string()], BlockNumber(100))
607 .await
608 .unwrap();
609
610 assert_eq!(Vec::<CardanoTransactionRecord>::new(), transactions);
611 }
612 }
613
614 #[tokio::test]
615 async fn repository_create_ignore_further_blocks_when_exists() {
616 let connection = cardano_tx_db_connection().unwrap();
617 let repository = CardanoTransactionRepository::new(Arc::new(
618 SqliteConnectionPool::build_from_connection(connection),
619 ));
620 let base_block = CardanoBlockWithTransactions::new(
621 "block_hash-1",
622 BlockNumber(10),
623 SlotNumber(50),
624 vec![""],
625 );
626 let expected_record: CardanoBlockRecord = base_block.clone().into();
627
628 repository
629 .create_block_and_transactions(vec![base_block.clone()])
630 .await
631 .unwrap();
632
633 {
635 repository
636 .create_block_and_transactions(vec![CardanoBlockWithTransactions {
637 block_hash: "block_hash-1-new".to_string(),
638 slot_number: base_block.slot_number + 10,
639 ..base_block.clone()
640 }])
641 .await
642 .unwrap();
643 let block_result = repository.get_block("block_hash-1").await.unwrap();
644
645 assert_eq!(Some(expected_record.clone()), block_result);
646 }
647 {
649 repository
650 .create_block_and_transactions(vec![CardanoBlockWithTransactions {
651 block_hash: "block_hash-1-new".to_string(),
652 block_number: base_block.block_number + 10,
653 ..base_block.clone()
654 }])
655 .await
656 .unwrap();
657 let block_result = repository.get_block("block_hash-1").await.unwrap();
658
659 assert_eq!(Some(expected_record.clone()), block_result);
660 }
661 {
663 repository
664 .create_block_and_transactions(vec![CardanoBlockWithTransactions {
665 block_number: base_block.block_number + 10,
666 slot_number: base_block.slot_number,
667 ..base_block.clone()
668 }])
669 .await
670 .unwrap();
671 let block_result = repository.get_block("block_hash-1").await.unwrap();
672
673 assert_eq!(Some(expected_record.clone()), block_result);
674 }
675 }
676
677 #[tokio::test]
678 async fn repository_create_ignore_further_transactions_when_exists() {
679 let connection = cardano_tx_db_connection().unwrap();
680 let repository = CardanoTransactionRepository::new(Arc::new(
681 SqliteConnectionPool::build_from_connection(connection),
682 ));
683 let base_block_with_txs = CardanoBlockWithTransactions::new(
684 "block_hash-1",
685 BlockNumber(10),
686 SlotNumber(50),
687 vec!["tx_hash-1"],
688 );
689
690 repository
691 .create_block_and_transactions(vec![base_block_with_txs.clone()])
692 .await
693 .unwrap();
694 repository
695 .create_block_and_transactions(vec![base_block_with_txs.clone()])
696 .await
697 .unwrap();
698 let transactions_result = repository.get_all().await.unwrap();
699
700 assert_eq!(
701 vec![CardanoTransaction::new(
702 "tx_hash-1".to_string(),
703 BlockNumber(10),
704 SlotNumber(50),
705 "block_hash-1".to_string(),
706 )],
707 transactions_result
708 );
709 }
710
711 #[tokio::test]
712 async fn repository_store_blocks_and_transactions_and_get_stored_them_individually() {
713 let connection = cardano_tx_db_connection().unwrap();
714 let repository = CardanoTransactionRepository::new(Arc::new(
715 SqliteConnectionPool::build_from_connection(connection),
716 ));
717
718 repository
719 .create_block_and_transactions(vec![CardanoBlockWithTransactions::new(
720 "block_hash-123",
721 BlockNumber(10),
722 SlotNumber(50),
723 vec!["tx_hash-123", "tx_hash-456"],
724 )])
725 .await
726 .unwrap();
727
728 let block_result = repository.get_block("block_hash-123").await.unwrap();
729 assert_eq!(
730 Some(CardanoBlockRecord {
731 block_hash: "block_hash-123".to_string(),
732 block_number: BlockNumber(10),
733 slot_number: SlotNumber(50),
734 }),
735 block_result
736 );
737
738 let transaction_result = repository.get_transaction("tx_hash-123").await.unwrap();
739 assert_eq!(
740 Some(CardanoTransactionRecord {
741 transaction_hash: "tx_hash-123".to_string(),
742 block_number: BlockNumber(10),
743 slot_number: SlotNumber(50),
744 block_hash: "block_hash-123".to_string(),
745 }),
746 transaction_result
747 );
748
749 let transaction_result = repository.get_transaction("tx_hash-456").await.unwrap();
750 assert_eq!(
751 Some(CardanoTransactionRecord {
752 transaction_hash: "tx_hash-456".to_string(),
753 block_number: BlockNumber(10),
754 slot_number: SlotNumber(50),
755 block_hash: "block_hash-123".to_string(),
756 }),
757 transaction_result
758 );
759 }
760
761 #[tokio::test]
762 async fn repository_get_all_stored_blocks() {
763 let connection = cardano_tx_db_connection().unwrap();
764 let repository = CardanoTransactionRepository::new(Arc::new(
765 SqliteConnectionPool::build_from_connection(connection),
766 ));
767
768 repository
769 .create_block_and_transactions(vec![
770 CardanoBlockWithTransactions::new(
771 "block_hash-1",
772 BlockNumber(10),
773 SlotNumber(50),
774 Vec::<String>::new(),
775 ),
776 CardanoBlockWithTransactions::new(
777 "block_hash-2",
778 BlockNumber(11),
779 SlotNumber(51),
780 Vec::<String>::new(),
781 ),
782 ])
783 .await
784 .unwrap();
785
786 let transactions_result = repository.get_all_blocks().await.unwrap();
787 assert_eq!(
788 vec![
789 CardanoBlockRecord::new(
790 "block_hash-1".to_string(),
791 BlockNumber(10),
792 SlotNumber(50),
793 ),
794 CardanoBlockRecord::new(
795 "block_hash-2".to_string(),
796 BlockNumber(11),
797 SlotNumber(51),
798 )
799 ],
800 transactions_result
801 )
802 }
803
804 #[tokio::test]
805 async fn repository_get_all_stored_transactions() {
806 let connection = cardano_tx_db_connection().unwrap();
807 let repository = CardanoTransactionRepository::new(Arc::new(
808 SqliteConnectionPool::build_from_connection(connection),
809 ));
810
811 repository
812 .create_block_and_transactions(vec![CardanoBlockWithTransactions::new(
813 "block_hash-123",
814 BlockNumber(10),
815 SlotNumber(50),
816 vec!["tx_hash-123", "tx_hash-456"],
817 )])
818 .await
819 .unwrap();
820
821 let transactions_result = repository.get_all_transactions().await.unwrap();
822 assert_eq!(
823 vec![
824 CardanoTransactionRecord::new(
825 "tx_hash-123".to_string(),
826 BlockNumber(10),
827 SlotNumber(50),
828 "block_hash-123".to_string(),
829 ),
830 CardanoTransactionRecord::new(
831 "tx_hash-456".to_string(),
832 BlockNumber(10),
833 SlotNumber(50),
834 "block_hash-123".to_string(),
835 )
836 ],
837 transactions_result
838 )
839 }
840
841 #[tokio::test]
842 async fn repository_get_highest_chain_point_without_blocks_in_db() {
843 let connection = cardano_tx_db_connection().unwrap();
844 let repository = CardanoTransactionRepository::new(Arc::new(
845 SqliteConnectionPool::build_from_connection(connection),
846 ));
847
848 let highest_beacon = repository.get_transaction_highest_chain_point().await.unwrap();
849 assert_eq!(None, highest_beacon);
850 }
851
852 #[tokio::test]
853 async fn repository_get_highest_chain_point_with_blocks_in_db() {
854 let connection = cardano_tx_db_connection().unwrap();
855 let repository = CardanoTransactionRepository::new(Arc::new(
856 SqliteConnectionPool::build_from_connection(connection),
857 ));
858
859 repository
860 .create_block_and_transactions(vec![
861 CardanoBlockWithTransactions::new(
862 "block_hash-1",
863 BlockNumber(10),
864 SlotNumber(50),
865 Vec::<String>::new(),
866 ),
867 CardanoBlockWithTransactions::new(
868 "block_hash-2",
869 BlockNumber(100),
870 SlotNumber(150),
871 Vec::<String>::new(),
872 ),
873 ])
874 .await
875 .unwrap();
876
877 let highest_beacon = repository.get_transaction_highest_chain_point().await.unwrap();
878 assert_eq!(
879 Some(ChainPoint::new(
880 SlotNumber(150),
881 BlockNumber(100),
882 "block_hash-2"
883 )),
884 highest_beacon
885 );
886 }
887
888 #[tokio::test]
889 async fn repository_get_transactions_in_range_blocks() {
890 let connection = cardano_tx_db_connection().unwrap();
891 let repository = CardanoTransactionRepository::new(Arc::new(
892 SqliteConnectionPool::build_from_connection(connection),
893 ));
894
895 let blocks = vec![
896 CardanoBlockWithTransactions::new(
897 "block_hash-1",
898 BlockNumber(10),
899 SlotNumber(50),
900 vec!["tx_hash-1"],
901 ),
902 CardanoBlockWithTransactions::new(
903 "block_hash-2",
904 BlockNumber(11),
905 SlotNumber(51),
906 vec!["tx_hash-2"],
907 ),
908 CardanoBlockWithTransactions::new(
909 "block_hash-3",
910 BlockNumber(12),
911 SlotNumber(52),
912 vec!["tx_hash-3"],
913 ),
914 ];
915 repository
916 .create_block_and_transactions(blocks.clone())
917 .await
918 .unwrap();
919 let expected_transactions: Vec<CardanoTransactionRecord> = blocks
920 .into_iter()
921 .flat_map(|b| b.into_transactions())
922 .map(Into::into)
923 .collect();
924
925 {
926 let transaction_result = repository
927 .get_transactions_in_range_blocks(BlockNumber(0)..BlockNumber(10))
928 .await
929 .unwrap();
930 assert_eq!(Vec::<CardanoTransactionRecord>::new(), transaction_result);
931 }
932 {
933 let transaction_result = repository
934 .get_transactions_in_range_blocks(BlockNumber(13)..BlockNumber(21))
935 .await
936 .unwrap();
937 assert_eq!(Vec::<CardanoTransactionRecord>::new(), transaction_result);
938 }
939 {
940 let transaction_result = repository
941 .get_transactions_in_range_blocks(BlockNumber(9)..BlockNumber(12))
942 .await
943 .unwrap();
944 assert_eq!(expected_transactions[0..=1].to_vec(), transaction_result);
945 }
946 {
947 let transaction_result = repository
948 .get_transactions_in_range_blocks(BlockNumber(10)..BlockNumber(13))
949 .await
950 .unwrap();
951 assert_eq!(expected_transactions.clone(), transaction_result);
952 }
953 {
954 let transaction_result = repository
955 .get_transactions_in_range_blocks(BlockNumber(11)..BlockNumber(14))
956 .await
957 .unwrap();
958 assert_eq!(expected_transactions[1..=2].to_vec(), transaction_result);
959 }
960 }
961
962 #[tokio::test]
963 async fn repository_get_blocks_with_transactions_in_range_blocks() {
964 let connection = cardano_tx_db_connection().unwrap();
965 let repository = CardanoTransactionRepository::new(Arc::new(
966 SqliteConnectionPool::build_from_connection(connection),
967 ));
968
969 let blocks = vec![
970 CardanoBlockWithTransactions::new(
971 "block_hash-1",
972 BlockNumber(10),
973 SlotNumber(50),
974 vec!["tx_hash-1"],
975 ),
976 CardanoBlockWithTransactions::new(
977 "block_hash-2",
978 BlockNumber(11),
979 SlotNumber(51),
980 vec!["tx_hash-2"],
981 ),
982 CardanoBlockWithTransactions::new(
983 "block_hash-3",
984 BlockNumber(12),
985 SlotNumber(52),
986 vec!["tx_hash-3"],
987 ),
988 ];
989 repository
990 .create_block_and_transactions(blocks.clone())
991 .await
992 .unwrap();
993 let expected_blocks: Vec<CardanoBlockTransactionsRecord> =
994 blocks.into_iter().map(Into::into).collect();
995
996 {
997 let blocks = repository
998 .get_blocks_with_transactions_in_range_blocks(BlockNumber(0)..BlockNumber(10))
999 .await
1000 .unwrap();
1001 assert_eq!(Vec::<CardanoBlockTransactionsRecord>::new(), blocks);
1002 }
1003 {
1004 let blocks = repository
1005 .get_blocks_with_transactions_in_range_blocks(BlockNumber(13)..BlockNumber(21))
1006 .await
1007 .unwrap();
1008 assert_eq!(Vec::<CardanoBlockTransactionsRecord>::new(), blocks);
1009 }
1010 {
1011 let blocks = repository
1012 .get_blocks_with_transactions_in_range_blocks(BlockNumber(9)..BlockNumber(12))
1013 .await
1014 .unwrap();
1015 assert_eq!(expected_blocks[0..=1].to_vec(), blocks);
1016 }
1017 {
1018 let blocks = repository
1019 .get_blocks_with_transactions_in_range_blocks(BlockNumber(10)..BlockNumber(13))
1020 .await
1021 .unwrap();
1022 assert_eq!(expected_blocks.clone(), blocks);
1023 }
1024 {
1025 let blocks = repository
1026 .get_blocks_with_transactions_in_range_blocks(BlockNumber(11)..BlockNumber(14))
1027 .await
1028 .unwrap();
1029 assert_eq!(expected_blocks[1..=2].to_vec(), blocks);
1030 }
1031 }
1032
1033 #[tokio::test]
1034 async fn repository_get_transactions_by_block_ranges() {
1035 let connection = cardano_tx_db_connection().unwrap();
1036 let repository = CardanoTransactionRepository::new(Arc::new(
1037 SqliteConnectionPool::build_from_connection(connection),
1038 ));
1039
1040 let blocks = vec![
1041 CardanoBlockWithTransactions::new(
1042 "block_hash-1",
1043 BlockNumber(10),
1044 SlotNumber(50),
1045 vec!["tx_hash-1"],
1046 ),
1047 CardanoBlockWithTransactions::new(
1048 "block_hash-2",
1049 BlockNumber(11),
1050 SlotNumber(51),
1051 vec!["tx_hash-2"],
1052 ),
1053 CardanoBlockWithTransactions::new(
1054 "block_hash-3",
1055 BlockNumber(20),
1056 SlotNumber(52),
1057 vec!["tx_hash-3"],
1058 ),
1059 CardanoBlockWithTransactions::new(
1060 "block_hash-4",
1061 BlockNumber(31),
1062 SlotNumber(53),
1063 vec!["tx_hash-4"],
1064 ),
1065 CardanoBlockWithTransactions::new(
1066 "block_hash-5",
1067 BlockNumber(35),
1068 SlotNumber(54),
1069 vec!["tx_hash-5"],
1070 ),
1071 CardanoBlockWithTransactions::new(
1072 "block_hash-6",
1073 BlockNumber(46),
1074 SlotNumber(55),
1075 vec!["tx_hash-6"],
1076 ),
1077 ];
1078 repository
1079 .create_block_and_transactions(blocks.clone())
1080 .await
1081 .unwrap();
1082 let expected_transactions: Vec<CardanoTransactionRecord> = blocks
1083 .into_iter()
1084 .flat_map(|b| b.into_transactions())
1085 .map(Into::into)
1086 .collect();
1087
1088 {
1089 let transaction_result = repository
1090 .get_transaction_by_block_ranges(vec![BlockRange::from_block_number(BlockNumber(
1091 100,
1092 ))])
1093 .await
1094 .unwrap();
1095 assert_eq!(Vec::<CardanoTransactionRecord>::new(), transaction_result);
1096 }
1097 {
1098 let transaction_result = repository
1099 .get_transaction_by_block_ranges(vec![BlockRange::from_block_number(BlockNumber(
1100 0,
1101 ))])
1102 .await
1103 .unwrap();
1104 assert_eq!(expected_transactions[0..=1].to_vec(), transaction_result);
1105 }
1106 {
1107 let transaction_result = repository
1108 .get_transaction_by_block_ranges(vec![
1109 BlockRange::from_block_number(BlockNumber(0)),
1110 BlockRange::from_block_number(BlockNumber(15)),
1111 ])
1112 .await
1113 .unwrap();
1114 assert_eq!(expected_transactions[0..=2].to_vec(), transaction_result);
1115 }
1116 {
1117 let transaction_result = repository
1118 .get_transaction_by_block_ranges(vec![
1119 BlockRange::from_block_number(BlockNumber(0)),
1120 BlockRange::from_block_number(BlockNumber(30)),
1121 ])
1122 .await
1123 .unwrap();
1124 assert_eq!(
1125 [
1126 expected_transactions[0..=1].to_vec(),
1127 expected_transactions[3..=4].to_vec()
1128 ]
1129 .concat(),
1130 transaction_result
1131 );
1132 }
1133 }
1134
1135 #[tokio::test]
1136 async fn repository_get_blocks_with_transactions_by_block_ranges() {
1137 let connection = cardano_tx_db_connection().unwrap();
1138 let repository = CardanoTransactionRepository::new(Arc::new(
1139 SqliteConnectionPool::build_from_connection(connection),
1140 ));
1141
1142 let blocks = vec![
1143 CardanoBlockWithTransactions::new(
1144 "block_hash-1",
1145 BlockNumber(10),
1146 SlotNumber(50),
1147 vec!["tx_hash-1"],
1148 ),
1149 CardanoBlockWithTransactions::new(
1150 "block_hash-2",
1151 BlockNumber(11),
1152 SlotNumber(51),
1153 vec!["tx_hash-2"],
1154 ),
1155 CardanoBlockWithTransactions::new(
1156 "block_hash-3",
1157 BlockNumber(20),
1158 SlotNumber(52),
1159 vec!["tx_hash-3"],
1160 ),
1161 CardanoBlockWithTransactions::new(
1162 "block_hash-4",
1163 BlockNumber(31),
1164 SlotNumber(53),
1165 vec!["tx_hash-4"],
1166 ),
1167 CardanoBlockWithTransactions::new(
1168 "block_hash-5",
1169 BlockNumber(35),
1170 SlotNumber(54),
1171 vec!["tx_hash-5"],
1172 ),
1173 CardanoBlockWithTransactions::new(
1174 "block_hash-6",
1175 BlockNumber(46),
1176 SlotNumber(55),
1177 vec!["tx_hash-6"],
1178 ),
1179 ];
1180 repository
1181 .create_block_and_transactions(blocks.clone())
1182 .await
1183 .unwrap();
1184 let expected_blocks: Vec<CardanoBlockTransactionsRecord> =
1185 blocks.into_iter().map(Into::into).collect();
1186
1187 {
1188 let transaction_result = repository
1189 .get_blocks_with_transactions_by_block_ranges(vec![BlockRange::from_block_number(
1190 BlockNumber(100),
1191 )])
1192 .await
1193 .unwrap();
1194 assert_eq!(
1195 Vec::<CardanoBlockTransactionsRecord>::new(),
1196 transaction_result
1197 );
1198 }
1199 {
1200 let transaction_result = repository
1201 .get_blocks_with_transactions_by_block_ranges(vec![BlockRange::from_block_number(
1202 BlockNumber(0),
1203 )])
1204 .await
1205 .unwrap();
1206 assert_eq!(expected_blocks[0..=1].to_vec(), transaction_result);
1207 }
1208 {
1209 let transaction_result = repository
1210 .get_blocks_with_transactions_by_block_ranges(vec![
1211 BlockRange::from_block_number(BlockNumber(0)),
1212 BlockRange::from_block_number(BlockNumber(15)),
1213 ])
1214 .await
1215 .unwrap();
1216 assert_eq!(expected_blocks[0..=2].to_vec(), transaction_result);
1217 }
1218 {
1219 let transaction_result = repository
1220 .get_blocks_with_transactions_by_block_ranges(vec![
1221 BlockRange::from_block_number(BlockNumber(0)),
1222 BlockRange::from_block_number(BlockNumber(30)),
1223 ])
1224 .await
1225 .unwrap();
1226 assert_eq!(
1227 [expected_blocks[0..=1].to_vec(), expected_blocks[3..=4].to_vec()].concat(),
1228 transaction_result
1229 );
1230 }
1231 }
1232
1233 #[tokio::test]
1234 async fn repository_get_closest_block_number_by_slot_number() {
1235 let connection = cardano_tx_db_connection().unwrap();
1236 let repository = CardanoTransactionRepository::new(Arc::new(
1237 SqliteConnectionPool::build_from_connection(connection),
1238 ));
1239
1240 let blocks = vec![
1241 CardanoBlockWithTransactions::new(
1242 "block_hash-1",
1243 BlockNumber(100),
1244 SlotNumber(500),
1245 Vec::<String>::new(),
1246 ),
1247 CardanoBlockWithTransactions::new(
1248 "block_hash-2",
1249 BlockNumber(101),
1250 SlotNumber(501),
1251 Vec::<String>::new(),
1252 ),
1253 ];
1254 repository
1255 .create_block_and_transactions(blocks.clone())
1256 .await
1257 .unwrap();
1258
1259 let transaction_block_number_retrieved = repository
1260 .get_closest_block_number_above_slot_number(SlotNumber(500))
1261 .await
1262 .unwrap();
1263
1264 assert_eq!(transaction_block_number_retrieved, Some(BlockNumber(100)));
1265 }
1266
1267 #[tokio::test]
1268 async fn repository_store_legacy_block_range() {
1269 let repository = CardanoTransactionRepository::new(Arc::new(
1270 SqliteConnectionPool::build_from_connection(cardano_tx_db_connection().unwrap()),
1271 ));
1272
1273 repository
1274 .create_legacy_block_range_roots(vec![
1275 (
1276 BlockRange::from_block_number(BlockNumber(0)),
1277 MKTreeNode::from_hex("AAAA").unwrap(),
1278 ),
1279 (
1280 BlockRange::from_block_number(BlockRange::LENGTH),
1281 MKTreeNode::from_hex("BBBB").unwrap(),
1282 ),
1283 ])
1284 .await
1285 .unwrap();
1286
1287 let connection = repository.connection_pool.connection().unwrap();
1288 let records: Vec<BlockRangeRootRecord> =
1289 connection.fetch_collect(GetLegacyBlockRangeRootQuery::all()).unwrap();
1290 assert_eq!(
1291 vec![
1292 BlockRangeRootRecord {
1293 range: BlockRange::from_block_number(BlockNumber(0)),
1294 merkle_root: MKTreeNode::from_hex("AAAA").unwrap(),
1295 },
1296 BlockRangeRootRecord {
1297 range: BlockRange::from_block_number(BlockRange::LENGTH),
1298 merkle_root: MKTreeNode::from_hex("BBBB").unwrap(),
1299 }
1300 ],
1301 records
1302 );
1303 }
1304
1305 #[tokio::test]
1306 async fn repository_store_legacy_block_range_with_existing_hash_doesnt_erase_existing_data() {
1307 let connection = cardano_tx_db_connection().unwrap();
1308 let repository = CardanoTransactionRepository::new(Arc::new(
1309 SqliteConnectionPool::build_from_connection(connection),
1310 ));
1311 let range = BlockRange::from_block_number(BlockNumber(0));
1312
1313 repository
1314 .create_legacy_block_range_roots(vec![(
1315 range.clone(),
1316 MKTreeNode::from_hex("AAAA").unwrap(),
1317 )])
1318 .await
1319 .unwrap();
1320 repository
1321 .create_legacy_block_range_roots(vec![(
1322 range.clone(),
1323 MKTreeNode::from_hex("BBBB").unwrap(),
1324 )])
1325 .await
1326 .unwrap();
1327
1328 let record: Vec<BlockRangeRootRecord> = repository
1329 .connection_pool
1330 .connection()
1331 .unwrap()
1332 .fetch_collect(GetLegacyBlockRangeRootQuery::all())
1333 .unwrap();
1334 assert_eq!(
1335 vec![BlockRangeRootRecord {
1336 range,
1337 merkle_root: MKTreeNode::from_hex("AAAA").unwrap()
1338 }],
1339 record
1340 );
1341 }
1342
1343 #[tokio::test]
1344 async fn repository_retrieve_legacy_block_range_roots_up_to() {
1345 let connection = cardano_tx_db_connection().unwrap();
1346 let repository = CardanoTransactionRepository::new(Arc::new(
1347 SqliteConnectionPool::build_from_connection(connection),
1348 ));
1349 let block_range_roots = vec![
1350 (
1351 BlockRange::from_block_number(BlockNumber(15)),
1352 MKTreeNode::from_hex("AAAA").unwrap(),
1353 ),
1354 (
1355 BlockRange::from_block_number(BlockNumber(30)),
1356 MKTreeNode::from_hex("BBBB").unwrap(),
1357 ),
1358 (
1359 BlockRange::from_block_number(BlockNumber(45)),
1360 MKTreeNode::from_hex("CCCC").unwrap(),
1361 ),
1362 ];
1363 repository
1364 .create_legacy_block_range_roots(block_range_roots.clone())
1365 .await
1366 .unwrap();
1367
1368 let retrieved_block_ranges = repository
1369 .retrieve_legacy_block_range_roots_up_to(BlockNumber(45))
1370 .await
1371 .unwrap();
1372 assert_eq!(
1373 block_range_roots[0..2].to_vec(),
1374 retrieved_block_ranges.collect::<Vec<_>>()
1375 );
1376 }
1377
1378 #[tokio::test]
1379 async fn repository_retrieve_highest_legacy_block_range_roots() {
1380 let connection = cardano_tx_db_connection().unwrap();
1381 let repository = CardanoTransactionRepository::new(Arc::new(
1382 SqliteConnectionPool::build_from_connection(connection),
1383 ));
1384 let block_range_roots = vec![
1385 BlockRangeRootRecord {
1386 range: BlockRange::from_block_number(BlockNumber(15)),
1387 merkle_root: MKTreeNode::from_hex("AAAA").unwrap(),
1388 },
1389 BlockRangeRootRecord {
1390 range: BlockRange::from_block_number(BlockNumber(30)),
1391 merkle_root: MKTreeNode::from_hex("BBBB").unwrap(),
1392 },
1393 BlockRangeRootRecord {
1394 range: BlockRange::from_block_number(BlockNumber(45)),
1395 merkle_root: MKTreeNode::from_hex("CCCC").unwrap(),
1396 },
1397 ];
1398 repository
1399 .create_legacy_block_range_roots(block_range_roots.clone())
1400 .await
1401 .unwrap();
1402
1403 let retrieved_block_range =
1404 repository.retrieve_highest_legacy_block_range_root().await.unwrap();
1405 assert_eq!(block_range_roots.last().cloned(), retrieved_block_range);
1406 }
1407
1408 #[tokio::test]
1409 async fn repository_store_block_range() {
1410 let repository = CardanoTransactionRepository::new(Arc::new(
1411 SqliteConnectionPool::build_from_connection(cardano_tx_db_connection().unwrap()),
1412 ));
1413
1414 repository
1415 .create_block_range_roots(vec![
1416 (
1417 BlockRange::from_block_number(BlockNumber(0)),
1418 MKTreeNode::from_hex("AAAA").unwrap(),
1419 ),
1420 (
1421 BlockRange::from_block_number(BlockRange::LENGTH),
1422 MKTreeNode::from_hex("BBBB").unwrap(),
1423 ),
1424 ])
1425 .await
1426 .unwrap();
1427
1428 let connection = repository.connection_pool.connection().unwrap();
1429 let records: Vec<BlockRangeRootRecord> =
1430 connection.fetch_collect(GetBlockRangeRootQuery::all()).unwrap();
1431 assert_eq!(
1432 vec![
1433 BlockRangeRootRecord {
1434 range: BlockRange::from_block_number(BlockNumber(0)),
1435 merkle_root: MKTreeNode::from_hex("AAAA").unwrap(),
1436 },
1437 BlockRangeRootRecord {
1438 range: BlockRange::from_block_number(BlockRange::LENGTH),
1439 merkle_root: MKTreeNode::from_hex("BBBB").unwrap(),
1440 }
1441 ],
1442 records
1443 );
1444 }
1445
1446 #[tokio::test]
1447 async fn repository_store_block_range_with_existing_hash_doesnt_erase_existing_data() {
1448 let connection = cardano_tx_db_connection().unwrap();
1449 let repository = CardanoTransactionRepository::new(Arc::new(
1450 SqliteConnectionPool::build_from_connection(connection),
1451 ));
1452 let range = BlockRange::from_block_number(BlockNumber(0));
1453
1454 repository
1455 .create_block_range_roots(vec![(range.clone(), MKTreeNode::from_hex("AAAA").unwrap())])
1456 .await
1457 .unwrap();
1458 repository
1459 .create_block_range_roots(vec![(range.clone(), MKTreeNode::from_hex("BBBB").unwrap())])
1460 .await
1461 .unwrap();
1462
1463 let record: Vec<BlockRangeRootRecord> = repository
1464 .connection_pool
1465 .connection()
1466 .unwrap()
1467 .fetch_collect(GetBlockRangeRootQuery::all())
1468 .unwrap();
1469 assert_eq!(
1470 vec![BlockRangeRootRecord {
1471 range,
1472 merkle_root: MKTreeNode::from_hex("AAAA").unwrap()
1473 }],
1474 record
1475 );
1476 }
1477
1478 #[tokio::test]
1479 async fn repository_retrieve_block_range_roots_up_to() {
1480 let connection = cardano_tx_db_connection().unwrap();
1481 let repository = CardanoTransactionRepository::new(Arc::new(
1482 SqliteConnectionPool::build_from_connection(connection),
1483 ));
1484 let block_range_roots = vec![
1485 (
1486 BlockRange::from_block_number(BlockNumber(15)),
1487 MKTreeNode::from_hex("AAAA").unwrap(),
1488 ),
1489 (
1490 BlockRange::from_block_number(BlockNumber(30)),
1491 MKTreeNode::from_hex("BBBB").unwrap(),
1492 ),
1493 (
1494 BlockRange::from_block_number(BlockNumber(45)),
1495 MKTreeNode::from_hex("CCCC").unwrap(),
1496 ),
1497 ];
1498 repository
1499 .create_block_range_roots(block_range_roots.clone())
1500 .await
1501 .unwrap();
1502
1503 let retrieved_block_ranges = repository
1504 .retrieve_block_range_roots_up_to(BlockNumber(45))
1505 .await
1506 .unwrap();
1507 assert_eq!(
1508 block_range_roots[0..2].to_vec(),
1509 retrieved_block_ranges.collect::<Vec<_>>()
1510 );
1511 }
1512
1513 #[tokio::test]
1514 async fn repository_retrieve_highest_block_range_roots() {
1515 let connection = cardano_tx_db_connection().unwrap();
1516 let repository = CardanoTransactionRepository::new(Arc::new(
1517 SqliteConnectionPool::build_from_connection(connection),
1518 ));
1519 let block_range_roots = vec![
1520 BlockRangeRootRecord {
1521 range: BlockRange::from_block_number(BlockNumber(15)),
1522 merkle_root: MKTreeNode::from_hex("AAAA").unwrap(),
1523 },
1524 BlockRangeRootRecord {
1525 range: BlockRange::from_block_number(BlockNumber(30)),
1526 merkle_root: MKTreeNode::from_hex("BBBB").unwrap(),
1527 },
1528 BlockRangeRootRecord {
1529 range: BlockRange::from_block_number(BlockNumber(45)),
1530 merkle_root: MKTreeNode::from_hex("CCCC").unwrap(),
1531 },
1532 ];
1533 repository
1534 .create_block_range_roots(block_range_roots.clone())
1535 .await
1536 .unwrap();
1537
1538 let retrieved_block_range = repository.retrieve_highest_block_range_root().await.unwrap();
1539 assert_eq!(block_range_roots.last().cloned(), retrieved_block_range);
1540 }
1541
1542 #[tokio::test]
1543 async fn repository_prune_blocks_and_transactions() {
1544 let connection = cardano_tx_db_connection().unwrap();
1545 let repository = CardanoTransactionRepository::new(Arc::new(
1546 SqliteConnectionPool::build_from_connection(connection),
1547 ));
1548
1549 let blocks = vec![
1550 CardanoBlockWithTransactions::new(
1551 "block_hash-1",
1552 BlockNumber(24),
1553 SlotNumber(50),
1554 vec!["tx_hash-1"],
1555 ),
1556 CardanoBlockWithTransactions::new(
1557 "block_hash-2",
1558 BlockNumber(25),
1559 SlotNumber(51),
1560 vec!["tx_hash-2"],
1561 ),
1562 CardanoBlockWithTransactions::new(
1563 "block_hash-3",
1564 BlockNumber(26),
1565 SlotNumber(52),
1566 vec!["tx_hash-3", "tx_hash-4"],
1567 ),
1568 ];
1569 repository.create_block_and_transactions(blocks).await.unwrap();
1570 repository
1572 .create_legacy_block_range_roots(vec![(
1573 BlockRange::from_block_number(BlockNumber(45)),
1574 MKTreeNode::from_hex("BBBB").unwrap(),
1575 )])
1576 .await
1577 .unwrap();
1578
1579 let stored_transactions = repository.get_all_transactions().await.unwrap();
1580 assert_eq!(4, stored_transactions.len());
1581 let stored_blocks = repository.get_all_blocks().await.unwrap();
1582 assert_eq!(3, stored_blocks.len());
1583
1584 repository.prune_transaction(BlockNumber(10_000_000)).await.unwrap();
1587 let stored_transactions = repository.get_all_transactions().await.unwrap();
1588 assert_eq!(4, stored_transactions.len());
1589 let stored_blocks = repository.get_all_blocks().await.unwrap();
1590 assert_eq!(3, stored_blocks.len());
1591
1592 repository.prune_transaction(BlockNumber(20)).await.unwrap();
1595 let transaction_result = repository
1596 .get_transactions_in_range_blocks(BlockNumber(0)..BlockNumber(25))
1597 .await
1598 .unwrap();
1599 assert_eq!(Vec::<CardanoTransactionRecord>::new(), transaction_result);
1600
1601 let transaction_result = repository
1602 .get_transactions_in_range_blocks(BlockNumber(25)..BlockNumber(1000))
1603 .await
1604 .unwrap();
1605 assert_eq!(3, transaction_result.len());
1606
1607 let stored_blocks = repository.get_all_blocks().await.unwrap();
1608 assert_eq!(2, stored_blocks.len());
1609 }
1610
1611 #[tokio::test]
1612 async fn get_prune_blocks_threshold() {
1613 let connection = cardano_tx_db_connection().unwrap();
1614 let repository = CardanoTransactionRepository::new(Arc::new(
1615 SqliteConnectionPool::build_from_connection(connection),
1616 ));
1617
1618 {
1620 let highest = repository.get_prune_blocks_threshold().await.unwrap();
1621 assert_eq!(None, highest);
1622 }
1623 {
1625 repository
1626 .create_block_range_roots(vec![
1627 (
1628 BlockRange::from_block_number(BlockNumber(45)),
1629 MKTreeNode::from_hex("AAAA").unwrap(),
1630 ),
1631 (
1632 BlockRange::from_block_number(BlockNumber(60)),
1633 MKTreeNode::from_hex("BBBB").unwrap(),
1634 ),
1635 ])
1636 .await
1637 .unwrap();
1638 let highest = repository.get_prune_blocks_threshold().await.unwrap();
1639 assert_eq!(Some(BlockNumber(60)), highest);
1640 }
1641 {
1643 repository
1644 .create_legacy_block_range_roots(vec![(
1645 BlockRange::from_block_number(BlockNumber(30)),
1646 MKTreeNode::from_hex("DDDD").unwrap(),
1647 )])
1648 .await
1649 .unwrap();
1650
1651 let highest = repository.get_prune_blocks_threshold().await.unwrap();
1652 assert_eq!(Some(BlockNumber(30)), highest);
1653 }
1654 {
1656 repository
1657 .create_legacy_block_range_roots(vec![(
1658 BlockRange::from_block_number(BlockNumber(75)),
1659 MKTreeNode::from_hex("CCCC").unwrap(),
1660 )])
1661 .await
1662 .unwrap();
1663
1664 let highest = repository.get_prune_blocks_threshold().await.unwrap();
1665 assert_eq!(Some(BlockNumber(60)), highest);
1666 }
1667 }
1668
1669 #[tokio::test]
1670 async fn remove_blocks_transactions_and_block_ranges_greater_than_given_block_number() {
1671 let connection = cardano_tx_db_connection().unwrap();
1672 let repository = CardanoTransactionRepository::new(Arc::new(
1673 SqliteConnectionPool::build_from_connection(connection),
1674 ));
1675
1676 let blocks = vec![
1677 CardanoBlockWithTransactions::new(
1678 "block_hash-1",
1679 BlockRange::LENGTH,
1680 SlotNumber(50),
1681 vec!["tx_hash-1", "tx_hash-2"],
1682 ),
1683 CardanoBlockWithTransactions::new(
1684 "block_hash-2",
1685 BlockRange::LENGTH * 3,
1686 SlotNumber(51),
1687 vec!["tx_hash-3", "tx_hash-4"],
1688 ),
1689 CardanoBlockWithTransactions::new(
1690 "block_hash-3",
1691 BlockRange::LENGTH * 3 + 1,
1692 SlotNumber(52),
1693 vec!["tx_hash-5", "tx_hash-6"],
1694 ),
1695 ];
1696 repository.create_block_and_transactions(blocks).await.unwrap();
1697 repository
1698 .create_legacy_block_range_roots(vec![
1699 (
1700 BlockRange::from_block_number(BlockRange::LENGTH),
1701 MKTreeNode::from_hex("AAAA").unwrap(),
1702 ),
1703 (
1704 BlockRange::from_block_number(BlockRange::LENGTH * 2),
1705 MKTreeNode::from_hex("AAAA").unwrap(),
1706 ),
1707 (
1708 BlockRange::from_block_number(BlockRange::LENGTH * 3),
1709 MKTreeNode::from_hex("AAAA").unwrap(),
1710 ),
1711 ])
1712 .await
1713 .unwrap();
1714 repository
1715 .create_block_range_roots(vec![
1716 (
1717 BlockRange::from_block_number(BlockNumber(0)),
1718 MKTreeNode::from_hex("9999").unwrap(),
1719 ),
1720 (
1721 BlockRange::from_block_number(BlockRange::LENGTH),
1722 MKTreeNode::from_hex("AAAA").unwrap(),
1723 ),
1724 (
1725 BlockRange::from_block_number(BlockRange::LENGTH * 2),
1726 MKTreeNode::from_hex("AAAA").unwrap(),
1727 ),
1728 (
1729 BlockRange::from_block_number(BlockRange::LENGTH * 3),
1730 MKTreeNode::from_hex("AAAA").unwrap(),
1731 ),
1732 ])
1733 .await
1734 .unwrap();
1735
1736 repository
1737 .remove_rolled_back_transactions_and_block_range_by_block_number(BlockRange::LENGTH * 3)
1738 .await
1739 .unwrap();
1740 assert_eq!(4, repository.get_all_transactions().await.unwrap().len());
1741 assert_eq!(
1742 2,
1743 repository.get_all_legacy_block_range_root().unwrap().len()
1744 );
1745 assert_eq!(3, repository.get_all_block_range_root().unwrap().len());
1746 assert_eq!(2, repository.get_all_blocks().await.unwrap().len());
1747 }
1748
1749 #[tokio::test]
1750 async fn remove_rolled_back_blocks_transactions_and_block_range_by_slot_number() {
1751 fn transaction_record(
1752 block_number: BlockNumber,
1753 slot_number: SlotNumber,
1754 tx_hash: &str,
1755 ) -> CardanoTransactionRecord {
1756 CardanoTransactionRecord::new(
1757 tx_hash,
1758 block_number,
1759 slot_number,
1760 format!("block_hash-{block_number}"),
1761 )
1762 }
1763
1764 let repository = CardanoTransactionRepository::new(Arc::new(
1765 SqliteConnectionPool::build(1, cardano_tx_db_connection).unwrap(),
1766 ));
1767
1768 let blocks = vec![
1769 CardanoBlockWithTransactions::new(
1770 "block_hash-10",
1771 BlockNumber(10),
1772 SlotNumber(50),
1773 vec!["tx_hash-1"],
1774 ),
1775 CardanoBlockWithTransactions::new(
1776 "block_hash-13",
1777 BlockNumber(13),
1778 SlotNumber(52),
1779 vec!["tx_hash-2"],
1780 ),
1781 CardanoBlockWithTransactions::new(
1782 "block_hash-101",
1783 BlockNumber(101),
1784 SlotNumber(100),
1785 vec!["tx_hash-3", "tx_hash-4"],
1786 ),
1787 ];
1788 repository.create_block_and_transactions(blocks).await.unwrap();
1789
1790 {
1791 repository
1792 .remove_rolled_back_blocks_transactions_and_block_range_by_slot_number(SlotNumber(
1793 110,
1794 ))
1795 .await
1796 .expect("Failed to remove rolled back transactions");
1797
1798 let transactions = repository.get_all_transactions().await.unwrap();
1799 assert_eq!(
1800 vec![
1801 transaction_record(BlockNumber(10), SlotNumber(50), "tx_hash-1"),
1802 transaction_record(BlockNumber(13), SlotNumber(52), "tx_hash-2"),
1803 transaction_record(BlockNumber(101), SlotNumber(100), "tx_hash-3"),
1804 transaction_record(BlockNumber(101), SlotNumber(100), "tx_hash-4"),
1805 ],
1806 transactions
1807 );
1808 }
1809
1810 {
1811 repository
1812 .remove_rolled_back_blocks_transactions_and_block_range_by_slot_number(SlotNumber(
1813 53,
1814 ))
1815 .await
1816 .expect("Failed to remove rolled back transactions");
1817
1818 let transactions = repository.get_all_transactions().await.unwrap();
1819 assert_eq!(
1820 vec![
1821 transaction_record(BlockNumber(10), SlotNumber(50), "tx_hash-1"),
1822 transaction_record(BlockNumber(13), SlotNumber(52), "tx_hash-2"),
1823 ],
1824 transactions
1825 );
1826 }
1827
1828 {
1829 repository
1830 .remove_rolled_back_blocks_transactions_and_block_range_by_slot_number(SlotNumber(
1831 51,
1832 ))
1833 .await
1834 .expect("Failed to remove rolled back transactions");
1835
1836 let transactions = repository.get_all_transactions().await.unwrap();
1837 assert_eq!(
1838 vec![transaction_record(BlockNumber(10), SlotNumber(50), "tx_hash-1")],
1839 transactions
1840 );
1841 }
1842 }
1843}