Skip to content

Commit 2b7bc2c

Browse files
eth/fetcher: allow underpriced transactions in after timeout (#28097)
This PR will allow a previously underpriced transaction back in after a timeout of 5 minutes. This will block most transaction spam but allow for transactions to be re-broadcasted on networks with less transaction flow. --------- Co-authored-by: Felix Lange <[email protected]>
1 parent 4021910 commit 2b7bc2c

File tree

2 files changed

+24
-18
lines changed

2 files changed

+24
-18
lines changed

eth/fetcher/tx_fetcher.go

+22-16
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ import (
2424
"sort"
2525
"time"
2626

27-
mapset "github.com/deckarep/golang-set/v2"
2827
"github.com/ethereum/go-ethereum/common"
28+
"github.com/ethereum/go-ethereum/common/lru"
2929
"github.com/ethereum/go-ethereum/common/mclock"
3030
"github.com/ethereum/go-ethereum/core/txpool"
3131
"github.com/ethereum/go-ethereum/core/types"
@@ -53,6 +53,9 @@ const (
5353
// re-request them.
5454
maxTxUnderpricedSetSize = 32768
5555

56+
// maxTxUnderpricedTimeout is the max time a transaction should be stuck in the underpriced set.
57+
maxTxUnderpricedTimeout = int64(5 * time.Minute)
58+
5659
// txArriveTimeout is the time allowance before an announced transaction is
5760
// explicitly requested.
5861
txArriveTimeout = 500 * time.Millisecond
@@ -148,7 +151,7 @@ type TxFetcher struct {
148151
drop chan *txDrop
149152
quit chan struct{}
150153

151-
underpriced mapset.Set[common.Hash] // Transactions discarded as too cheap (don't re-fetch)
154+
underpriced *lru.Cache[common.Hash, int64] // Transactions discarded as too cheap (don't re-fetch)
152155

153156
// Stage 1: Waiting lists for newly discovered transactions that might be
154157
// broadcast without needing explicit request/reply round trips.
@@ -202,7 +205,7 @@ func NewTxFetcherForTests(
202205
fetching: make(map[common.Hash]string),
203206
requests: make(map[string]*txRequest),
204207
alternates: make(map[common.Hash]map[string]struct{}),
205-
underpriced: mapset.NewSet[common.Hash](),
208+
underpriced: lru.NewCache[common.Hash, int64](maxTxUnderpricedSetSize),
206209
hasTx: hasTx,
207210
addTxs: addTxs,
208211
fetchTxs: fetchTxs,
@@ -223,17 +226,16 @@ func (f *TxFetcher) Notify(peer string, hashes []common.Hash) error {
223226
// still valuable to check here because it runs concurrent to the internal
224227
// loop, so anything caught here is time saved internally.
225228
var (
226-
unknowns = make([]common.Hash, 0, len(hashes))
227-
duplicate, underpriced int64
229+
unknowns = make([]common.Hash, 0, len(hashes))
230+
duplicate int64
231+
underpriced int64
228232
)
229233
for _, hash := range hashes {
230234
switch {
231235
case f.hasTx(hash):
232236
duplicate++
233-
234-
case f.underpriced.Contains(hash):
237+
case f.isKnownUnderpriced(hash):
235238
underpriced++
236-
237239
default:
238240
unknowns = append(unknowns, hash)
239241
}
@@ -245,10 +247,7 @@ func (f *TxFetcher) Notify(peer string, hashes []common.Hash) error {
245247
if len(unknowns) == 0 {
246248
return nil
247249
}
248-
announce := &txAnnounce{
249-
origin: peer,
250-
hashes: unknowns,
251-
}
250+
announce := &txAnnounce{origin: peer, hashes: unknowns}
252251
select {
253252
case f.notify <- announce:
254253
return nil
@@ -257,6 +256,16 @@ func (f *TxFetcher) Notify(peer string, hashes []common.Hash) error {
257256
}
258257
}
259258

259+
// isKnownUnderpriced reports whether a transaction hash was recently found to be underpriced.
260+
func (f *TxFetcher) isKnownUnderpriced(hash common.Hash) bool {
261+
prevTime, ok := f.underpriced.Peek(hash)
262+
if ok && prevTime+maxTxUnderpricedTimeout < time.Now().Unix() {
263+
f.underpriced.Remove(hash)
264+
return false
265+
}
266+
return ok
267+
}
268+
260269
// Enqueue imports a batch of received transaction into the transaction pool
261270
// and the fetcher. This method may be called by both transaction broadcasts and
262271
// direct request replies. The differentiation is important so the fetcher can
@@ -300,10 +309,7 @@ func (f *TxFetcher) Enqueue(peer string, txs []*types.Transaction, direct bool)
300309
// Avoid re-request this transaction when we receive another
301310
// announcement.
302311
if errors.Is(err, txpool.ErrUnderpriced) || errors.Is(err, txpool.ErrReplaceUnderpriced) {
303-
for f.underpriced.Cardinality() >= maxTxUnderpricedSetSize {
304-
f.underpriced.Pop()
305-
}
306-
f.underpriced.Add(batch[j].Hash())
312+
f.underpriced.Add(batch[j].Hash(), batch[j].Time().Unix())
307313
}
308314
// Track a few interesting failure types
309315
switch {

eth/fetcher/tx_fetcher_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -1509,8 +1509,8 @@ func testTransactionFetcher(t *testing.T, tt txFetcherTest) {
15091509
}
15101510

15111511
case isUnderpriced:
1512-
if fetcher.underpriced.Cardinality() != int(step) {
1513-
t.Errorf("step %d: underpriced set size mismatch: have %d, want %d", i, fetcher.underpriced.Cardinality(), step)
1512+
if fetcher.underpriced.Len() != int(step) {
1513+
t.Errorf("step %d: underpriced set size mismatch: have %d, want %d", i, fetcher.underpriced.Len(), step)
15141514
}
15151515

15161516
default:

0 commit comments

Comments
 (0)