1+ use std:: collections:: HashSet ;
2+
13use hyperlane_core:: H512 ;
24
35use crate :: {
@@ -11,11 +13,147 @@ pub trait Update {
1113
1214impl Update for Transaction {
1315 fn update_after_submission ( & mut self , hash : H512 , precursor : SealevelTxPrecursor ) -> & mut Self {
14- self . tx_hashes . push ( hash) ;
16+ // only push hash if it doesn't exist in tx_hashes
17+ if !self . tx_hashes . contains ( & hash) {
18+ self . tx_hashes . push ( hash) ;
19+ }
1520
1621 // Data is updated since transaction is re-estimated before submission
1722 self . vm_specific_data = VmSpecificTxData :: Svm ( Box :: new ( precursor) ) ;
1823
1924 self
2025 }
2126}
27+
28+ #[ cfg( test) ]
29+ mod tests {
30+ use super :: * ;
31+ use crate :: {
32+ adapter:: chains:: sealevel:: SealevelTxPrecursor ,
33+ payload:: PayloadDetails ,
34+ transaction:: { Transaction , VmSpecificTxData } ,
35+ } ;
36+ use hyperlane_core:: { H256 , H512 } ;
37+ use hyperlane_sealevel:: SealevelTxCostEstimate ;
38+ use solana_sdk:: { instruction:: Instruction as SealevelInstruction , pubkey:: Pubkey } ;
39+
40+ fn create_test_precursor ( ) -> SealevelTxPrecursor {
41+ let instruction = SealevelInstruction :: new_with_bytes ( Pubkey :: new_unique ( ) , & [ ] , vec ! [ ] ) ;
42+ let estimate = SealevelTxCostEstimate {
43+ compute_units : 200_000 ,
44+ compute_unit_price_micro_lamports : 1000 ,
45+ } ;
46+ SealevelTxPrecursor {
47+ instruction,
48+ estimate,
49+ }
50+ }
51+
52+ fn create_test_transaction ( ) -> Transaction {
53+ let precursor = create_test_precursor ( ) ;
54+ let payload_details = vec ! [ PayloadDetails {
55+ uuid: hyperlane_core:: identifiers:: UniqueIdentifier :: random( ) ,
56+ metadata: "test-payload" . to_string( ) ,
57+ success_criteria: None ,
58+ } ] ;
59+ Transaction :: new ( precursor, payload_details)
60+ }
61+
62+ #[ test]
63+ fn test_update_after_submission_adds_hash ( ) {
64+ let mut tx = create_test_transaction ( ) ;
65+ assert_eq ! ( tx. tx_hashes. len( ) , 0 ) ;
66+
67+ let hash1 = H512 :: random ( ) ;
68+ let precursor = create_test_precursor ( ) ;
69+ tx. update_after_submission ( hash1, precursor) ;
70+
71+ assert_eq ! ( tx. tx_hashes. len( ) , 1 ) ;
72+ assert_eq ! ( tx. tx_hashes[ 0 ] , hash1) ;
73+ }
74+
75+ #[ test]
76+ fn test_update_after_submission_deduplicates_hashes ( ) {
77+ let mut tx = create_test_transaction ( ) ;
78+
79+ let hash1 = H512 :: random ( ) ;
80+ let hash2 = H512 :: random ( ) ;
81+
82+ // Add first hash
83+ let precursor1 = create_test_precursor ( ) ;
84+ tx. update_after_submission ( hash1, precursor1) ;
85+ assert_eq ! ( tx. tx_hashes. len( ) , 1 ) ;
86+
87+ // Add second hash
88+ let precursor2 = create_test_precursor ( ) ;
89+ tx. update_after_submission ( hash2, precursor2) ;
90+ assert_eq ! ( tx. tx_hashes. len( ) , 2 ) ;
91+
92+ // Add duplicate of first hash - should deduplicate
93+ let precursor3 = create_test_precursor ( ) ;
94+ tx. update_after_submission ( hash1, precursor3) ;
95+
96+ // Should still have 2 unique hashes, not 3
97+ assert_eq ! ( tx. tx_hashes. len( ) , 2 ) ;
98+ assert ! ( tx. tx_hashes. contains( & hash1) ) ;
99+ assert ! ( tx. tx_hashes. contains( & hash2) ) ;
100+ }
101+
102+ #[ test]
103+ fn test_update_after_submission_deduplicates_multiple_duplicates ( ) {
104+ let mut tx = create_test_transaction ( ) ;
105+
106+ let hash1 = H512 :: random ( ) ;
107+ let hash2 = H512 :: random ( ) ;
108+ let hash3 = H512 :: random ( ) ;
109+
110+ // Add multiple hashes
111+ tx. update_after_submission ( hash1, create_test_precursor ( ) ) ;
112+ tx. update_after_submission ( hash2, create_test_precursor ( ) ) ;
113+ tx. update_after_submission ( hash3, create_test_precursor ( ) ) ;
114+ assert_eq ! ( tx. tx_hashes. len( ) , 3 ) ;
115+
116+ // Add duplicates
117+ tx. update_after_submission ( hash1, create_test_precursor ( ) ) ;
118+ tx. update_after_submission ( hash2, create_test_precursor ( ) ) ;
119+ tx. update_after_submission ( hash1, create_test_precursor ( ) ) ;
120+
121+ // Should still have 3 unique hashes
122+ assert_eq ! ( tx. tx_hashes. len( ) , 3 ) ;
123+ assert ! ( tx. tx_hashes. contains( & hash1) ) ;
124+ assert ! ( tx. tx_hashes. contains( & hash2) ) ;
125+ assert ! ( tx. tx_hashes. contains( & hash3) ) ;
126+ }
127+
128+ #[ test]
129+ fn test_update_after_submission_updates_vm_specific_data ( ) {
130+ let mut tx = create_test_transaction ( ) ;
131+ let _original_precursor = match & tx. vm_specific_data {
132+ VmSpecificTxData :: Svm ( p) => p. clone ( ) ,
133+ _ => panic ! ( "Expected Svm variant" ) ,
134+ } ;
135+
136+ let hash = H512 :: random ( ) ;
137+ let new_precursor = SealevelTxPrecursor {
138+ instruction : SealevelInstruction :: new_with_bytes (
139+ Pubkey :: new_unique ( ) ,
140+ & [ 1 , 2 , 3 ] ,
141+ vec ! [ ] ,
142+ ) ,
143+ estimate : SealevelTxCostEstimate {
144+ compute_units : 300_000 ,
145+ compute_unit_price_micro_lamports : 2000 ,
146+ } ,
147+ } ;
148+
149+ tx. update_after_submission ( hash, new_precursor. clone ( ) ) ;
150+
151+ match & tx. vm_specific_data {
152+ VmSpecificTxData :: Svm ( p) => {
153+ assert_eq ! ( p. estimate. compute_units, 300_000 ) ;
154+ assert_eq ! ( p. estimate. compute_unit_price_micro_lamports, 2000 ) ;
155+ }
156+ _ => panic ! ( "Expected Svm variant" ) ,
157+ }
158+ }
159+ }
0 commit comments