@@ -3,7 +3,6 @@ use mithril_common::{
3
3
BlockHash , BlockNumber , CardanoDbBeacon , CardanoTransaction , ImmutableFileNumber ,
4
4
SlotNumber , TransactionHash ,
5
5
} ,
6
- signable_builder:: TransactionStore ,
7
6
StdResult ,
8
7
} ;
9
8
use mithril_persistence:: sqlite:: {
@@ -16,7 +15,7 @@ use async_trait::async_trait;
16
15
use sqlite:: { Row , Value } ;
17
16
use std:: { iter:: repeat, sync:: Arc } ;
18
17
19
- use crate :: services:: TransactionsRetriever ;
18
+ use crate :: services:: { TransactionStore , TransactionsRetriever } ;
20
19
21
20
/// Cardano Transaction record is the representation of a cardano transaction.
22
21
#[ derive( Debug , PartialEq , Clone ) ]
@@ -126,11 +125,11 @@ impl<'client> CardanoTransactionProvider<'client> {
126
125
127
126
pub ( crate ) fn get_transaction_up_to_beacon_condition (
128
127
& self ,
129
- beacon : & CardanoDbBeacon ,
128
+ beacon : ImmutableFileNumber ,
130
129
) -> WhereCondition {
131
130
WhereCondition :: new (
132
131
"immutable_file_number <= ?*" ,
133
- vec ! [ Value :: Integer ( beacon. immutable_file_number as i64 ) ] ,
132
+ vec ! [ Value :: Integer ( beacon as i64 ) ] ,
134
133
)
135
134
}
136
135
}
@@ -236,7 +235,7 @@ impl CardanoTransactionRepository {
236
235
/// chronological order.
237
236
pub async fn get_transactions_up_to (
238
237
& self ,
239
- beacon : & CardanoDbBeacon ,
238
+ beacon : ImmutableFileNumber ,
240
239
) -> StdResult < Vec < CardanoTransactionRecord > > {
241
240
let provider = CardanoTransactionProvider :: new ( & self . connection ) ;
242
241
let filters = provider. get_transaction_up_to_beacon_condition ( beacon) ;
@@ -280,12 +279,15 @@ impl CardanoTransactionRepository {
280
279
}
281
280
282
281
/// Create new [CardanoTransactionRecord]s in the database.
283
- pub async fn create_transactions (
282
+ pub async fn create_transactions < T : Into < CardanoTransactionRecord > > (
284
283
& self ,
285
- transactions : Vec < CardanoTransactionRecord > ,
284
+ transactions : Vec < T > ,
286
285
) -> StdResult < Vec < CardanoTransactionRecord > > {
286
+ let records: Vec < CardanoTransactionRecord > =
287
+ transactions. into_iter ( ) . map ( |tx| tx. into ( ) ) . collect ( ) ;
288
+
287
289
let provider = InsertCardanoTransactionProvider :: new ( & self . connection ) ;
288
- let filters = provider. get_insert_many_condition ( transactions ) ?;
290
+ let filters = provider. get_insert_many_condition ( records ) ?;
289
291
let cursor = provider. find ( filters) ?;
290
292
291
293
Ok ( cursor. collect ( ) )
@@ -294,25 +296,62 @@ impl CardanoTransactionRepository {
294
296
295
297
#[ async_trait]
296
298
impl TransactionStore for CardanoTransactionRepository {
297
- async fn store_transactions ( & self , transactions : & [ CardanoTransaction ] ) -> StdResult < ( ) > {
298
- let records: Vec < CardanoTransactionRecord > =
299
- transactions. iter ( ) . map ( |tx| tx. to_owned ( ) . into ( ) ) . collect ( ) ;
300
- self . create_transactions ( records)
301
- . await
302
- . with_context ( || "CardanoTransactionRepository can not store transactions" ) ?;
299
+ async fn get_highest_beacon ( & self ) -> StdResult < Option < ImmutableFileNumber > > {
300
+ let sql = "select max(immutable_file_number) as highest from cardano_tx;" ;
301
+ match self
302
+ . connection
303
+ . prepare ( sql)
304
+ . with_context ( || {
305
+ format ! (
306
+ "Prepare query error: SQL=`{}`" ,
307
+ & sql. replace( '\n' , " " ) . trim( )
308
+ )
309
+ } ) ?
310
+ . iter ( )
311
+ . next ( )
312
+ {
313
+ None => Ok ( None ) ,
314
+ Some ( row) => {
315
+ let highest = row?. read :: < Option < i64 > , _ > ( 0 ) ;
316
+ highest
317
+ . map ( u64:: try_from)
318
+ . transpose ( )
319
+ . with_context ( ||
320
+ format ! ( "Integer field max(immutable_file_number) (value={highest:?}) is incompatible with u64 representation." )
321
+ )
322
+ }
323
+ }
324
+ }
325
+
326
+ async fn get_up_to ( & self , beacon : ImmutableFileNumber ) -> StdResult < Vec < CardanoTransaction > > {
327
+ self . get_transactions_up_to ( beacon) . await . map ( |v| {
328
+ v. into_iter ( )
329
+ . map ( |record| record. into ( ) )
330
+ . collect :: < Vec < CardanoTransaction > > ( )
331
+ } )
332
+ }
303
333
334
+ async fn store_transactions ( & self , transactions : Vec < CardanoTransaction > ) -> StdResult < ( ) > {
335
+ // Chunk transactions to avoid an error when we exceed sqlite binding limitations
336
+ for transactions_in_chunk in transactions. chunks ( 100 ) {
337
+ self . create_transactions ( transactions_in_chunk. to_vec ( ) )
338
+ . await
339
+ . with_context ( || "CardanoTransactionRepository can not store transactions" ) ?;
340
+ }
304
341
Ok ( ( ) )
305
342
}
306
343
}
307
344
308
345
#[ async_trait]
309
346
impl TransactionsRetriever for CardanoTransactionRepository {
310
347
async fn get_up_to ( & self , beacon : & CardanoDbBeacon ) -> StdResult < Vec < CardanoTransaction > > {
311
- self . get_transactions_up_to ( beacon) . await . map ( |v| {
312
- v. into_iter ( )
313
- . map ( |record| record. into ( ) )
314
- . collect :: < Vec < CardanoTransaction > > ( )
315
- } )
348
+ self . get_transactions_up_to ( beacon. immutable_file_number )
349
+ . await
350
+ . map ( |v| {
351
+ v. into_iter ( )
352
+ . map ( |record| record. into ( ) )
353
+ . collect :: < Vec < CardanoTransaction > > ( )
354
+ } )
316
355
}
317
356
}
318
357
@@ -369,10 +408,7 @@ mod tests {
369
408
let connection = Connection :: open_thread_safe ( ":memory:" ) . unwrap ( ) ;
370
409
let provider = CardanoTransactionProvider :: new ( & connection) ;
371
410
let ( expr, params) = provider
372
- . get_transaction_up_to_beacon_condition ( & CardanoDbBeacon {
373
- immutable_file_number : 2309 ,
374
- ..CardanoDbBeacon :: default ( )
375
- } )
411
+ . get_transaction_up_to_beacon_condition ( 2309 )
376
412
. expand ( ) ;
377
413
378
414
assert_eq ! ( "immutable_file_number <= ?1" . to_string( ) , expr) ;
@@ -522,7 +558,7 @@ mod tests {
522
558
CardanoTransaction :: new( "tx-hash-456" , 11 , 51 , "block-hash-456" , 100 ) ,
523
559
] ;
524
560
repository
525
- . store_transactions ( & cardano_transactions)
561
+ . create_transactions ( cardano_transactions)
526
562
. await
527
563
. unwrap ( ) ;
528
564
@@ -558,8 +594,8 @@ mod tests {
558
594
let connection = get_connection ( ) . await ;
559
595
let repository = CardanoTransactionRepository :: new ( connection. clone ( ) ) ;
560
596
561
- let cardano_transactions: Vec < CardanoTransaction > = ( 20 ..=40 )
562
- . map ( |i| CardanoTransaction {
597
+ let cardano_transactions: Vec < CardanoTransactionRecord > = ( 20 ..=40 )
598
+ . map ( |i| CardanoTransactionRecord {
563
599
transaction_hash : format ! ( "tx-hash-{i}" ) ,
564
600
block_number : i % 10 ,
565
601
slot_number : i * 100 ,
@@ -568,33 +604,18 @@ mod tests {
568
604
} )
569
605
. collect ( ) ;
570
606
repository
571
- . store_transactions ( & cardano_transactions)
572
- . await
573
- . unwrap ( ) ;
574
-
575
- let transaction_result = repository
576
- . get_up_to ( & CardanoDbBeacon :: new ( "" . to_string ( ) , 1 , 34 ) )
607
+ . create_transactions ( cardano_transactions. clone ( ) )
577
608
. await
578
609
. unwrap ( ) ;
579
610
611
+ let transaction_result = repository. get_transactions_up_to ( 34 ) . await . unwrap ( ) ;
580
612
assert_eq ! ( cardano_transactions[ 0 ..=14 ] . to_vec( ) , transaction_result) ;
581
613
582
- let transaction_result = repository
583
- . get_up_to ( & CardanoDbBeacon :: new ( "" . to_string ( ) , 1 , 300 ) )
584
- . await
585
- . unwrap ( ) ;
586
-
587
- assert_eq ! (
588
- cardano_transactions. into_iter( ) . collect:: <Vec <_>>( ) ,
589
- transaction_result
590
- ) ;
591
-
592
- let transaction_result = repository
593
- . get_up_to ( & CardanoDbBeacon :: new ( "" . to_string ( ) , 1 , 19 ) )
594
- . await
595
- . unwrap ( ) ;
614
+ let transaction_result = repository. get_transactions_up_to ( 300 ) . await . unwrap ( ) ;
615
+ assert_eq ! ( cardano_transactions. clone( ) , transaction_result) ;
596
616
597
- assert_eq ! ( Vec :: <CardanoTransaction >:: new( ) , transaction_result) ;
617
+ let transaction_result = repository. get_transactions_up_to ( 19 ) . await . unwrap ( ) ;
618
+ assert_eq ! ( Vec :: <CardanoTransactionRecord >:: new( ) , transaction_result) ;
598
619
}
599
620
600
621
#[ tokio:: test]
@@ -607,7 +628,7 @@ mod tests {
607
628
CardanoTransaction :: new( "tx-hash-456" . to_string( ) , 11 , 51 , "block-hash-456" , 100 ) ,
608
629
] ;
609
630
repository
610
- . store_transactions ( & cardano_transactions)
631
+ . create_transactions ( cardano_transactions. clone ( ) )
611
632
. await
612
633
. unwrap ( ) ;
613
634
@@ -638,7 +659,7 @@ mod tests {
638
659
99 ,
639
660
) ] ;
640
661
repository
641
- . store_transactions ( & cardano_transactions)
662
+ . create_transactions ( cardano_transactions)
642
663
. await
643
664
. unwrap ( ) ;
644
665
@@ -655,4 +676,31 @@ mod tests {
655
676
transaction_result
656
677
) ;
657
678
}
679
+
680
+ #[ tokio:: test]
681
+ async fn repository_get_highest_beacon_without_transactions_in_db ( ) {
682
+ let connection = get_connection ( ) . await ;
683
+ let repository = CardanoTransactionRepository :: new ( connection. clone ( ) ) ;
684
+
685
+ let highest_beacon = repository. get_highest_beacon ( ) . await . unwrap ( ) ;
686
+ assert_eq ! ( None , highest_beacon) ;
687
+ }
688
+
689
+ #[ tokio:: test]
690
+ async fn repository_get_highest_beacon_with_transactions_in_db ( ) {
691
+ let connection = get_connection ( ) . await ;
692
+ let repository = CardanoTransactionRepository :: new ( connection. clone ( ) ) ;
693
+
694
+ let cardano_transactions = vec ! [
695
+ CardanoTransaction :: new( "tx-hash-123" . to_string( ) , 10 , 50 , "block-hash-123" , 50 ) ,
696
+ CardanoTransaction :: new( "tx-hash-456" . to_string( ) , 11 , 51 , "block-hash-456" , 100 ) ,
697
+ ] ;
698
+ repository
699
+ . create_transactions ( cardano_transactions)
700
+ . await
701
+ . unwrap ( ) ;
702
+
703
+ let highest_beacon = repository. get_highest_beacon ( ) . await . unwrap ( ) ;
704
+ assert_eq ! ( Some ( 100 ) , highest_beacon) ;
705
+ }
658
706
}
0 commit comments