@@ -2150,9 +2150,22 @@ func containsHashInAnnounces(slice []announce, hash common.Hash) bool {
2150
2150
return false
2151
2151
}
2152
2152
2153
- // Tests that a transaction is forgotten after the timeout.
2153
+ // TestTransactionForgotten verifies that underpriced transactions are properly
2154
+ // forgotten after the timeout period, testing both the exact timeout boundary
2155
+ // and the cleanup of the underpriced cache.
2154
2156
func TestTransactionForgotten (t * testing.T ) {
2155
- fetcher := NewTxFetcher (
2157
+ // Test ensures that underpriced transactions are properly forgotten after a timeout period,
2158
+ // including checks for timeout boundary and cache cleanup.
2159
+ t .Parallel ()
2160
+
2161
+ // Create a mock clock for deterministic time control
2162
+ mockClock := new (mclock.Simulated )
2163
+ mockTime := func () time.Time {
2164
+ nanoTime := int64 (mockClock .Now ())
2165
+ return time .Unix (nanoTime / 1000000000 , nanoTime % 1000000000 )
2166
+ }
2167
+
2168
+ fetcher := NewTxFetcherForTests (
2156
2169
func (common.Hash ) bool { return false },
2157
2170
func (txs []* types.Transaction ) []error {
2158
2171
errs := make ([]error , len (txs ))
@@ -2163,24 +2176,83 @@ func TestTransactionForgotten(t *testing.T) {
2163
2176
},
2164
2177
func (string , []common.Hash ) error { return nil },
2165
2178
func (string ) {},
2179
+ mockClock ,
2180
+ mockTime ,
2181
+ rand .New (rand .NewSource (0 )), // Use fixed seed for deterministic behavior
2166
2182
)
2167
2183
fetcher .Start ()
2168
2184
defer fetcher .Stop ()
2169
- // Create one TX which is 5 minutes old, and one which is recent
2170
- tx1 := types .NewTx (& types.LegacyTx {Nonce : 0 })
2171
- tx1 .SetTime (time .Now ().Add (- maxTxUnderpricedTimeout - 1 * time .Second ))
2172
- tx2 := types .NewTx (& types.LegacyTx {Nonce : 1 })
2173
2185
2174
- // Enqueue both in the fetcher. They will be immediately tagged as underpriced
2175
- if err := fetcher .Enqueue ("asdf" , []* types.Transaction {tx1 , tx2 }, false ); err != nil {
2186
+ // Create two test transactions with the same timestamp
2187
+ tx1 := types .NewTransaction (0 , common.Address {}, big .NewInt (100 ), 21000 , big .NewInt (1 ), nil )
2188
+ tx2 := types .NewTransaction (1 , common.Address {}, big .NewInt (100 ), 21000 , big .NewInt (1 ), nil )
2189
+
2190
+ now := mockTime ()
2191
+ tx1 .SetTime (now )
2192
+ tx2 .SetTime (now )
2193
+
2194
+ // Initial state: both transactions should be marked as underpriced
2195
+ if err := fetcher .Enqueue ("peer" , []* types.Transaction {tx1 , tx2 }, false ); err != nil {
2176
2196
t .Fatal (err )
2177
2197
}
2178
- // isKnownUnderpriced should trigger removal of the first tx (no longer be known underpriced)
2179
- if fetcher .isKnownUnderpriced (tx1 .Hash ()) {
2180
- t .Fatal ("transaction should be forgotten by now" )
2198
+ if ! fetcher .isKnownUnderpriced (tx1 .Hash ()) {
2199
+ t .Error ("tx1 should be underpriced" )
2200
+ }
2201
+ if ! fetcher .isKnownUnderpriced (tx2 .Hash ()) {
2202
+ t .Error ("tx2 should be underpriced" )
2203
+ }
2204
+
2205
+ // Verify cache size
2206
+ if size := fetcher .underpriced .Len (); size != 2 {
2207
+ t .Errorf ("wrong underpriced cache size: got %d, want %d" , size , 2 )
2208
+ }
2209
+
2210
+ // Just before timeout: transactions should still be underpriced
2211
+ mockClock .Run (maxTxUnderpricedTimeout - time .Second )
2212
+ if ! fetcher .isKnownUnderpriced (tx1 .Hash ()) {
2213
+ t .Error ("tx1 should still be underpriced before timeout" )
2214
+ }
2215
+ if ! fetcher .isKnownUnderpriced (tx2 .Hash ()) {
2216
+ t .Error ("tx2 should still be underpriced before timeout" )
2217
+ }
2218
+
2219
+ // Exactly at timeout boundary: transactions should still be present
2220
+ mockClock .Run (time .Second )
2221
+ if ! fetcher .isKnownUnderpriced (tx1 .Hash ()) {
2222
+ t .Error ("tx1 should be present exactly at timeout" )
2181
2223
}
2182
- // isKnownUnderpriced should not trigger removal of the second
2183
2224
if ! fetcher .isKnownUnderpriced (tx2 .Hash ()) {
2184
- t .Fatal ("transaction should be known underpriced" )
2225
+ t .Error ("tx2 should be present exactly at timeout" )
2226
+ }
2227
+
2228
+ // After timeout: transactions should be forgotten
2229
+ mockClock .Run (time .Second )
2230
+ if fetcher .isKnownUnderpriced (tx1 .Hash ()) {
2231
+ t .Error ("tx1 should be forgotten after timeout" )
2232
+ }
2233
+ if fetcher .isKnownUnderpriced (tx2 .Hash ()) {
2234
+ t .Error ("tx2 should be forgotten after timeout" )
2235
+ }
2236
+
2237
+ // Verify cache is empty
2238
+ if size := fetcher .underpriced .Len (); size != 0 {
2239
+ t .Errorf ("wrong underpriced cache size after timeout: got %d, want 0" , size )
2240
+ }
2241
+
2242
+ // Re-enqueue tx1 with updated timestamp
2243
+ tx1 .SetTime (mockTime ())
2244
+ if err := fetcher .Enqueue ("peer" , []* types.Transaction {tx1 }, false ); err != nil {
2245
+ t .Fatal (err )
2246
+ }
2247
+ if ! fetcher .isKnownUnderpriced (tx1 .Hash ()) {
2248
+ t .Error ("tx1 should be underpriced after re-enqueueing with new timestamp" )
2249
+ }
2250
+ if fetcher .isKnownUnderpriced (tx2 .Hash ()) {
2251
+ t .Error ("tx2 should remain forgotten" )
2252
+ }
2253
+
2254
+ // Verify final cache state
2255
+ if size := fetcher .underpriced .Len (); size != 1 {
2256
+ t .Errorf ("wrong final underpriced cache size: got %d, want 1" , size )
2185
2257
}
2186
2258
}
0 commit comments