@@ -368,29 +368,37 @@ fn get_and_set_txs_and_receipts(
368
368
metadata : Metadata ,
369
369
) -> Result < Vec < OpReceipt > , Box < dyn std:: error:: Error > > {
370
370
let mut diff_receipts: Vec < OpReceipt > = vec ! [ ] ;
371
- // Store tx transaction signed
371
+ let mut tx_hashes: Vec < String > = vec ! [ ] ;
372
+
373
+ if let Some ( existing_hashes) = cache. get :: < Vec < String > > ( & format ! ( "tx_hashes:{}" , block_number) )
374
+ {
375
+ tx_hashes = existing_hashes;
376
+ }
377
+
372
378
for ( idx, transaction) in block. body . transactions . iter ( ) . enumerate ( ) {
373
- // check if exists, if not update
374
- let existing_tx = cache. get :: < OpTransactionSigned > ( & transaction. tx_hash ( ) . to_string ( ) ) ;
379
+ let tx_hash = transaction. tx_hash ( ) . to_string ( ) ;
380
+
381
+ // Add transaction hash to the ordered list if not already present
382
+ if !tx_hashes. contains ( & tx_hash) {
383
+ tx_hashes. push ( tx_hash. clone ( ) ) ;
384
+ }
385
+
386
+ let existing_tx = cache. get :: < OpTransactionSigned > ( & tx_hash) ;
375
387
if existing_tx. is_none ( ) {
376
- if let Err ( e) = cache. set ( & transaction . tx_hash ( ) . to_string ( ) , & transaction, Some ( 10 ) ) {
388
+ if let Err ( e) = cache. set ( & tx_hash, & transaction, Some ( 10 ) ) {
377
389
error ! ( "Failed to set transaction in cache: {}" , e) ;
378
390
continue ;
379
391
}
380
- // update tx index
381
392
if let Err ( e) = cache. set ( & format ! ( "tx_idx:{}" , transaction. tx_hash( ) ) , & idx, Some ( 10 ) )
382
393
{
383
394
error ! ( "Failed to set transaction index in cache: {}" , e) ;
384
395
continue ;
385
396
}
386
397
387
- // update tx count for each from address
388
398
if let Ok ( from) = transaction. recover_signer ( ) {
389
- // Get current tx count, default to 0 if not found
390
399
let current_count = cache
391
400
. get :: < u64 > ( & format ! ( "tx_count:{}:{}" , from, block_number) )
392
401
. unwrap_or ( 0 ) ;
393
- // Increment tx count by 1
394
402
if let Err ( e) = cache. set (
395
403
& format ! ( "tx_count:{}:{}" , from, block_number) ,
396
404
& ( current_count + 1 ) ,
@@ -399,7 +407,6 @@ fn get_and_set_txs_and_receipts(
399
407
error ! ( "Failed to set transaction count in cache: {}" , e) ;
400
408
}
401
409
402
- // also keep track of sender of each transaction
403
410
if let Err ( e) = cache. set (
404
411
& format ! ( "tx_sender:{}" , transaction. tx_hash( ) ) ,
405
412
& from,
@@ -408,7 +415,6 @@ fn get_and_set_txs_and_receipts(
408
415
error ! ( "Failed to set transaction sender in cache: {}" , e) ;
409
416
}
410
417
411
- // also keep track of the block number of each transaction
412
418
if let Err ( e) = cache. set (
413
419
& format ! ( "tx_block_number:{}" , transaction. tx_hash( ) ) ,
414
420
& block_number,
@@ -420,26 +426,16 @@ fn get_and_set_txs_and_receipts(
420
426
}
421
427
422
428
// TODO: move this into the transaction check
423
- if metadata
424
- . receipts
425
- . contains_key ( & transaction. tx_hash ( ) . to_string ( ) )
426
- {
429
+ if metadata. receipts . contains_key ( & tx_hash) {
427
430
// find receipt in metadata and set it in cache
428
- let receipt = metadata
429
- . receipts
430
- . get ( & transaction. tx_hash ( ) . to_string ( ) )
431
- . unwrap ( ) ;
432
- if let Err ( e) = cache. set (
433
- & format ! ( "receipt:{:?}" , transaction. tx_hash( ) . to_string( ) ) ,
434
- receipt,
435
- Some ( 10 ) ,
436
- ) {
431
+ let receipt = metadata. receipts . get ( & tx_hash) . unwrap ( ) ;
432
+ if let Err ( e) = cache. set ( & format ! ( "receipt:{:?}" , tx_hash) , receipt, Some ( 10 ) ) {
437
433
error ! ( "Failed to set receipt in cache: {}" , e) ;
438
434
continue ;
439
435
}
440
436
// map receipt's block number as well
441
437
if let Err ( e) = cache. set (
442
- & format ! ( "receipt_block:{:?}" , transaction . tx_hash( ) . to_string ( ) ) ,
438
+ & format ! ( "receipt_block:{:?}" , tx_hash) ,
443
439
& block_number,
444
440
Some ( 10 ) ,
445
441
) {
@@ -451,6 +447,10 @@ fn get_and_set_txs_and_receipts(
451
447
}
452
448
}
453
449
450
+ if let Err ( e) = cache. set ( & format ! ( "tx_hashes:{}" , block_number) , & tx_hashes, Some ( 10 ) ) {
451
+ error ! ( "Failed to update transaction hashes list in cache: {}" , e) ;
452
+ }
453
+
454
454
Ok ( diff_receipts)
455
455
}
456
456
@@ -635,7 +635,7 @@ mod tests {
635
635
payload_id : PayloadId :: new ( [ 0 ; 8 ] ) ,
636
636
base : None ,
637
637
diff : delta2,
638
- metadata : serde_json:: to_value ( metadata2) . unwrap ( ) ,
638
+ metadata : serde_json:: to_value ( metadata2. clone ( ) ) . unwrap ( ) ,
639
639
}
640
640
}
641
641
@@ -832,5 +832,164 @@ mod tests {
832
832
// Also verify metric would have been recorded (though we can't directly check the metric's value)
833
833
let highest = cache. get :: < u64 > ( "highest_payload_index" ) . unwrap ( ) ;
834
834
assert_eq ! ( highest, 0 ) ;
835
+
836
+ }
837
+
838
+ #[ test]
839
+ fn test_tx_hash_list_storage_and_deduplication ( ) {
840
+ let cache = Arc :: new ( Cache :: default ( ) ) ;
841
+ let block_number = 1 ;
842
+
843
+ let base = ExecutionPayloadBaseV1 {
844
+ parent_hash : Default :: default ( ) ,
845
+ parent_beacon_block_root : Default :: default ( ) ,
846
+ fee_recipient : Address :: from_str ( "0x1234567890123456789012345678901234567890" ) . unwrap ( ) ,
847
+ block_number,
848
+ gas_limit : 1000000 ,
849
+ timestamp : 1234567890 ,
850
+ prev_randao : Default :: default ( ) ,
851
+ extra_data : Default :: default ( ) ,
852
+ base_fee_per_gas : U256 :: from ( 1000 ) ,
853
+ } ;
854
+
855
+ let tx1 = Bytes :: from_str ( "0x02f87483014a3482017e8459682f0084596830a98301f1d094b01866f195533de16eb929b73f87280693ca0cb480844e71d92dc001a0a658c18bdba29dd4022ee6640fdd143691230c12b3c8c86cf5c1a1f1682cc1e2a0248a28763541ebed2b87ecea63a7024b5c2b7de58539fa64c887b08f5faf29c1" ) . unwrap ( ) ;
856
+
857
+ let delta1 = ExecutionPayloadFlashblockDeltaV1 {
858
+ transactions : vec ! [ tx1. clone( ) ] ,
859
+ withdrawals : vec ! [ ] ,
860
+ state_root : Default :: default ( ) ,
861
+ receipts_root : Default :: default ( ) ,
862
+ logs_bloom : Default :: default ( ) ,
863
+ gas_used : 21000 ,
864
+ block_hash : Default :: default ( ) ,
865
+ } ;
866
+
867
+ let tx1_hash =
868
+ "0x3cbbc9a6811ac5b2a2e5780bdb67baffc04246a59f39e398be048f1b2d05460c" . to_string ( ) ;
869
+
870
+ let metadata1 = Metadata {
871
+ block_number,
872
+ receipts : {
873
+ let mut receipts = HashMap :: default ( ) ;
874
+ receipts. insert (
875
+ tx1_hash. clone ( ) ,
876
+ OpReceipt :: Legacy ( Receipt {
877
+ status : true . into ( ) ,
878
+ cumulative_gas_used : 21000 ,
879
+ logs : vec ! [ ] ,
880
+ } ) ,
881
+ ) ;
882
+ receipts
883
+ } ,
884
+ new_account_balances : HashMap :: default ( ) ,
885
+ } ;
886
+
887
+ let payload1 = FlashblocksPayloadV1 {
888
+ index : 0 ,
889
+ payload_id : PayloadId :: new ( [ 0 ; 8 ] ) ,
890
+ base : Some ( base) ,
891
+ diff : delta1,
892
+ metadata : serde_json:: to_value ( metadata1) . unwrap ( ) ,
893
+ } ;
894
+
895
+ process_payload ( payload1, cache. clone ( ) ) ;
896
+
897
+ let tx_hashes1 = cache
898
+ . get :: < Vec < String > > ( & format ! ( "tx_hashes:{}" , block_number) )
899
+ . unwrap ( ) ;
900
+ assert_eq ! ( tx_hashes1. len( ) , 1 ) ;
901
+ assert_eq ! ( tx_hashes1[ 0 ] , tx1_hash) ;
902
+
903
+ let tx2 = Bytes :: from_str ( "0xf8cd82016d8316e5708302c01c94f39635f2adf40608255779ff742afe13de31f57780b8646e530e9700000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000001bc16d674ec8000000000000000000000000000000000000000000000000000156ddc81eed2a36d68302948ba0a608703e79b22164f74523d188a11f81c25a65dd59535bab1cd1d8b30d115f3ea07f4cfbbad77a139c9209d3bded89091867ff6b548dd714109c61d1f8e7a84d14" ) . unwrap ( ) ;
904
+
905
+ let tx2_hash =
906
+ "0xa6155b295085d3b87a3c86e342fe11c3b22f9952d0d85d9d34d223b7d6a17cd8" . to_string ( ) ;
907
+
908
+ let delta2 = ExecutionPayloadFlashblockDeltaV1 {
909
+ transactions : vec ! [ tx1. clone( ) , tx2. clone( ) ] , // Note tx1 is repeated
910
+ withdrawals : vec ! [ ] ,
911
+ state_root : B256 :: repeat_byte ( 0x1 ) ,
912
+ receipts_root : B256 :: repeat_byte ( 0x2 ) ,
913
+ logs_bloom : Default :: default ( ) ,
914
+ gas_used : 42000 ,
915
+ block_hash : B256 :: repeat_byte ( 0x3 ) ,
916
+ } ;
917
+
918
+ let metadata2 = Metadata {
919
+ block_number,
920
+ receipts : {
921
+ let mut receipts = HashMap :: default ( ) ;
922
+ receipts. insert (
923
+ tx1_hash. clone ( ) ,
924
+ OpReceipt :: Legacy ( Receipt {
925
+ status : true . into ( ) ,
926
+ cumulative_gas_used : 21000 ,
927
+ logs : vec ! [ ] ,
928
+ } ) ,
929
+ ) ;
930
+ receipts. insert (
931
+ tx2_hash. clone ( ) ,
932
+ OpReceipt :: Legacy ( Receipt {
933
+ status : true . into ( ) ,
934
+ cumulative_gas_used : 42000 ,
935
+ logs : vec ! [ ] ,
936
+ } ) ,
937
+ ) ;
938
+ receipts
939
+ } ,
940
+ new_account_balances : HashMap :: default ( ) ,
941
+ } ;
942
+
943
+ let payload2 = FlashblocksPayloadV1 {
944
+ index : 1 ,
945
+ payload_id : PayloadId :: new ( [ 0 ; 8 ] ) ,
946
+ base : None ,
947
+ diff : delta2,
948
+ metadata : serde_json:: to_value ( metadata2. clone ( ) ) . unwrap ( ) ,
949
+ } ;
950
+
951
+ process_payload ( payload2, cache. clone ( ) ) ;
952
+
953
+ let tx_hashes2 = cache
954
+ . get :: < Vec < String > > ( & format ! ( "tx_hashes:{}" , block_number) )
955
+ . unwrap ( ) ;
956
+ assert_eq ! (
957
+ tx_hashes2. len( ) ,
958
+ 2 ,
959
+ "Should have 2 unique transaction hashes"
960
+ ) ;
961
+ assert_eq ! ( tx_hashes2[ 0 ] , tx1_hash, "First hash should be tx1" ) ;
962
+ assert_eq ! ( tx_hashes2[ 1 ] , tx2_hash, "Second hash should be tx2" ) ;
963
+
964
+ let delta3 = ExecutionPayloadFlashblockDeltaV1 {
965
+ transactions : vec ! [ tx2. clone( ) , tx1. clone( ) ] , // Different order
966
+ withdrawals : vec ! [ ] ,
967
+ state_root : B256 :: repeat_byte ( 0x1 ) ,
968
+ receipts_root : B256 :: repeat_byte ( 0x2 ) ,
969
+ logs_bloom : Default :: default ( ) ,
970
+ gas_used : 42000 ,
971
+ block_hash : B256 :: repeat_byte ( 0x3 ) ,
972
+ } ;
973
+
974
+ let payload3 = FlashblocksPayloadV1 {
975
+ index : 2 ,
976
+ payload_id : PayloadId :: new ( [ 0 ; 8 ] ) ,
977
+ base : None ,
978
+ diff : delta3,
979
+ metadata : serde_json:: to_value ( metadata2) . unwrap ( ) , // Same metadata
980
+ } ;
981
+
982
+ process_payload ( payload3, cache. clone ( ) ) ;
983
+
984
+ let tx_hashes3 = cache
985
+ . get :: < Vec < String > > ( & format ! ( "tx_hashes:{}" , block_number) )
986
+ . unwrap ( ) ;
987
+ assert_eq ! (
988
+ tx_hashes3. len( ) ,
989
+ 2 ,
990
+ "Should still have 2 unique transaction hashes"
991
+ ) ;
992
+ assert_eq ! ( tx_hashes3[ 0 ] , tx1_hash, "First hash should be tx1" ) ;
993
+ assert_eq ! ( tx_hashes3[ 1 ] , tx2_hash, "Second hash should be tx2" ) ;
835
994
}
836
995
}
0 commit comments