@@ -2150,9 +2150,22 @@ func containsHashInAnnounces(slice []announce, hash common.Hash) bool {
21502150 return false
21512151}
21522152
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.
21542156func 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 (
21562169 func (common.Hash ) bool { return false },
21572170 func (txs []* types.Transaction ) []error {
21582171 errs := make ([]error , len (txs ))
@@ -2163,24 +2176,83 @@ func TestTransactionForgotten(t *testing.T) {
21632176 },
21642177 func (string , []common.Hash ) error { return nil },
21652178 func (string ) {},
2179+ mockClock ,
2180+ mockTime ,
2181+ rand .New (rand .NewSource (0 )), // Use fixed seed for deterministic behavior
21662182 )
21672183 fetcher .Start ()
21682184 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 })
21732185
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 {
21762196 t .Fatal (err )
21772197 }
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" )
21812223 }
2182- // isKnownUnderpriced should not trigger removal of the second
21832224 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 )
21852257 }
21862258}
0 commit comments