From 7d67a4ead41514d724d9fd98be0d4b69de448cd0 Mon Sep 17 00:00:00 2001 From: Daniel Liu <139250065@qq.com> Date: Wed, 11 Mar 2026 11:28:07 +0800 Subject: [PATCH] refactor(txpool,eth,miner): use pending filter struct #29026 (#2160) Introduce txpool.PendingFilter and migrate Pending(...) signatures from positional params to a typed struct. - Add core/txpool.PendingFilter with MinTip/BaseFee fields for cheap call-site filtering - Update txpool subpool interface and all call sites in eth/miner to pass the filter struct - Keep behavior unchanged for empty filter (equivalent to previous nil,nil usage) - Refresh legacypool tests to use the new API shape This is a refactor-only change intended to improve API clarity and future extensibility without changing consensus or RPC semantics. --- core/txpool/legacypool/legacypool.go | 10 +++++----- core/txpool/legacypool/legacypool_test.go | 14 +++++++------- core/txpool/subpool.go | 13 ++++++++++++- core/txpool/txpool.go | 5 ++--- eth/api_backend.go | 2 +- eth/helper_test.go | 2 +- eth/protocol.go | 3 +-- eth/sync.go | 3 ++- miner/worker.go | 10 ++++++---- 9 files changed, 37 insertions(+), 25 deletions(-) diff --git a/core/txpool/legacypool/legacypool.go b/core/txpool/legacypool/legacypool.go index 1464cd60a1..f827f5d3cc 100644 --- a/core/txpool/legacypool/legacypool.go +++ b/core/txpool/legacypool/legacypool.go @@ -581,7 +581,7 @@ func (pool *LegacyPool) ContentFrom(addr common.Address) ([]*types.Transaction, // // The transactions can also be pre-filtered by the dynamic fee components to // reduce allocations and load on downstream subsystems. -func (pool *LegacyPool) Pending(minTip *uint256.Int, baseFee *uint256.Int) map[common.Address][]*txpool.LazyTransaction { +func (pool *LegacyPool) Pending(filter txpool.PendingFilter) map[common.Address][]*txpool.LazyTransaction { pool.mu.Lock() defer pool.mu.Unlock() @@ -590,11 +590,11 @@ func (pool *LegacyPool) Pending(minTip *uint256.Int, baseFee *uint256.Int) map[c minTipBig *big.Int baseFeeBig *big.Int ) - if minTip != nil { - minTipBig = minTip.ToBig() + if filter.MinTip != nil { + minTipBig = filter.MinTip.ToBig() } - if baseFee != nil { - baseFeeBig = baseFee.ToBig() + if filter.BaseFee != nil { + baseFeeBig = filter.BaseFee.ToBig() } pending := make(map[common.Address][]*txpool.LazyTransaction, len(pool.pending)) diff --git a/core/txpool/legacypool/legacypool_test.go b/core/txpool/legacypool/legacypool_test.go index 8402ea9301..c80e2f305d 100644 --- a/core/txpool/legacypool/legacypool_test.go +++ b/core/txpool/legacypool/legacypool_test.go @@ -3085,7 +3085,7 @@ func TestPendingMinTipThreshold(t *testing.T) { t.Fatalf("failed to add tx nonce 1: %v", err) } - filtered := pool.Pending(uint256.MustFromBig(threshold), nil) + filtered := pool.Pending(txpool.PendingFilter{MinTip: uint256.MustFromBig(threshold)}) txs := filtered[addr] if len(txs) != 1 { t.Fatalf("unexpected filtered tx count: have %d, want %d", len(txs), 1) @@ -3099,7 +3099,7 @@ func TestPendingMinTipThreshold(t *testing.T) { t.Fatalf("unexpected surviving tx after tip filter: got nonce %d", have) } - all := pool.Pending(nil, nil) + all := pool.Pending(txpool.PendingFilter{}) if len(all[addr]) != 2 { t.Fatalf("unexpected unfiltered tx count: have %d, want %d", len(all[addr]), 2) } @@ -3132,7 +3132,7 @@ func TestPendingMinTipWithBaseFee(t *testing.T) { minTip := uint256.MustFromBig(minTipBig) baseFee := uint256.MustFromBig(baseFeeBig) - withBaseFee := pool.Pending(minTip, baseFee) + withBaseFee := pool.Pending(txpool.PendingFilter{MinTip: minTip, BaseFee: baseFee}) txs := withBaseFee[addr] if len(txs) != 1 { t.Fatalf("unexpected tx count with base fee filter: have %d, want %d", len(txs), 1) @@ -3146,7 +3146,7 @@ func TestPendingMinTipWithBaseFee(t *testing.T) { t.Fatalf("unexpected surviving tx with base fee filter: got nonce %d", have) } - withoutBaseFee := pool.Pending(minTip, nil) + withoutBaseFee := pool.Pending(txpool.PendingFilter{MinTip: minTip}) if len(withoutBaseFee[addr]) != 3 { t.Fatalf("unexpected tx count without base fee filter: have %d, want %d", len(withoutBaseFee[addr]), 3) } @@ -3180,7 +3180,7 @@ func TestPendingKeepsLocalAndSpecialTransactions(t *testing.T) { t.Fatalf("failed to add local tx: %v", err) } - filtered := pool.Pending(uint256.MustFromBig(filterTip), nil) + filtered := pool.Pending(txpool.PendingFilter{MinTip: uint256.MustFromBig(filterTip)}) specialPending := filtered[specialAddr] if len(specialPending) != 2 { @@ -3225,7 +3225,7 @@ func TestPendingDynamicFeeThresholdWithoutBaseFee(t *testing.T) { t.Fatalf("failed to add tx nonce 1: %v", err) } - filtered := pool.Pending(uint256.MustFromBig(minTipBig), nil) + filtered := pool.Pending(txpool.PendingFilter{MinTip: uint256.MustFromBig(minTipBig)}) txs := filtered[addr] if len(txs) != 1 { t.Fatalf("unexpected filtered tx count: have %d, want %d", len(txs), 1) @@ -3239,7 +3239,7 @@ func TestPendingDynamicFeeThresholdWithoutBaseFee(t *testing.T) { t.Fatalf("unexpected surviving tx without base fee: got nonce %d", have) } - all := pool.Pending(nil, nil) + all := pool.Pending(txpool.PendingFilter{}) if len(all[addr]) != 2 { t.Fatalf("unexpected unfiltered tx count: have %d, want %d", len(all[addr]), 2) } diff --git a/core/txpool/subpool.go b/core/txpool/subpool.go index 714d54200f..8ad40718c3 100644 --- a/core/txpool/subpool.go +++ b/core/txpool/subpool.go @@ -65,6 +65,17 @@ type LazyResolver interface { Get(hash common.Hash) *types.Transaction } +// PendingFilter is a collection of filter rules to allow retrieving a subset +// of transactions for announcement or mining. +// +// Note, the entries here are not arbitrary useful filters, rather each one has +// a very specific call site in mind and each one can be evaluated very cheaply +// by the pool implementations. Only add new ones that satisfy those constraints. +type PendingFilter struct { + MinTip *uint256.Int // Minimum miner tip required to include a transaction + BaseFee *uint256.Int // Minimum 1559 basefee needed to include a transaction +} + // SubPool represents a specialized transaction pool that lives on its own (e.g. // blob pool). Since independent of how many specialized pools we have, they do // need to be updated in lockstep and assemble into one coherent view for block @@ -113,7 +124,7 @@ type SubPool interface { // // The transactions can also be pre-filtered by the dynamic fee components to // reduce allocations and load on downstream subsystems. - Pending(minTip *uint256.Int, baseFee *uint256.Int) map[common.Address][]*LazyTransaction + Pending(filter PendingFilter) map[common.Address][]*LazyTransaction // SubscribeTransactions subscribes to new transaction events. The subscriber // can decide whether to receive notifications only for newly seen transactions diff --git a/core/txpool/txpool.go b/core/txpool/txpool.go index 26d5af1ca3..4b6a5dc835 100644 --- a/core/txpool/txpool.go +++ b/core/txpool/txpool.go @@ -25,7 +25,6 @@ import ( "github.com/XinFinOrg/XDPoSChain/core" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/event" - "github.com/holiman/uint256" ) // TxStatus is the current status of a transaction as seen by the pool. @@ -254,10 +253,10 @@ func (p *TxPool) Add(txs []*types.Transaction, local bool, sync bool) []error { // // The transactions can also be pre-filtered by the dynamic fee components to // reduce allocations and load on downstream subsystems. -func (p *TxPool) Pending(minTip *uint256.Int, baseFee *uint256.Int) map[common.Address][]*LazyTransaction { +func (p *TxPool) Pending(filter PendingFilter) map[common.Address][]*LazyTransaction { txs := make(map[common.Address][]*LazyTransaction) for _, subpool := range p.subpools { - maps.Copy(txs, subpool.Pending(minTip, baseFee)) + maps.Copy(txs, subpool.Pending(filter)) } return txs } diff --git a/eth/api_backend.go b/eth/api_backend.go index cd906846e1..ecfcd18a3d 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -305,7 +305,7 @@ func (b *EthAPIBackend) SendTx(ctx context.Context, signedTx *types.Transaction) } func (b *EthAPIBackend) GetPoolTransactions() (types.Transactions, error) { - pending := b.eth.txPool.Pending(nil, nil) + pending := b.eth.txPool.Pending(txpool.PendingFilter{}) var txs types.Transactions for _, batch := range pending { for _, lazy := range batch { diff --git a/eth/helper_test.go b/eth/helper_test.go index 9602260844..3db3579eb9 100644 --- a/eth/helper_test.go +++ b/eth/helper_test.go @@ -147,7 +147,7 @@ func (p *testTxPool) Add(txs []*types.Transaction, local bool, sync bool) []erro } // Pending returns all the transactions known to the pool -func (p *testTxPool) Pending(minTip *uint256.Int, baseFee *uint256.Int) map[common.Address][]*txpool.LazyTransaction { +func (p *testTxPool) Pending(filter txpool.PendingFilter) map[common.Address][]*txpool.LazyTransaction { p.lock.RLock() defer p.lock.RUnlock() diff --git a/eth/protocol.go b/eth/protocol.go index 23db4e5165..ef3c74bb85 100644 --- a/eth/protocol.go +++ b/eth/protocol.go @@ -27,7 +27,6 @@ import ( "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/event" "github.com/XinFinOrg/XDPoSChain/rlp" - "github.com/holiman/uint256" ) // Constants to match up protocol versions and messages @@ -110,7 +109,7 @@ type txPool interface { // Pending should return pending transactions. // The slice should be modifiable by the caller. - Pending(minTip *uint256.Int, baseFee *uint256.Int) map[common.Address][]*txpool.LazyTransaction + Pending(filter txpool.PendingFilter) map[common.Address][]*txpool.LazyTransaction // SubscribeTransactions subscribes to new transaction events. The subscriber // can decide whether to receive notifications only for newly seen transactions diff --git a/eth/sync.go b/eth/sync.go index 0da6bec830..627434933a 100644 --- a/eth/sync.go +++ b/eth/sync.go @@ -22,6 +22,7 @@ import ( "time" "github.com/XinFinOrg/XDPoSChain/common" + "github.com/XinFinOrg/XDPoSChain/core/txpool" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/eth/downloader" "github.com/XinFinOrg/XDPoSChain/log" @@ -45,7 +46,7 @@ type txsync struct { // syncTransactions starts sending all currently pending transactions to the given peer. func (pm *ProtocolManager) syncTransactions(p *peer) { var txs types.Transactions - pending := pm.txpool.Pending(nil, nil) + pending := pm.txpool.Pending(txpool.PendingFilter{}) for _, batch := range pending { for _, lazy := range batch { if tx := lazy.Resolve(); tx != nil { diff --git a/miner/worker.go b/miner/worker.go index 47a849e52f..fd15fbf517 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -862,11 +862,13 @@ func (w *worker) commitNewWork() { } if !isEpochSwitchBlock { // Retrieve the pending transactions pre-filtered by the 1559 dynamic fees - var baseFee *uint256.Int - if header.BaseFee != nil { - baseFee = uint256.MustFromBig(header.BaseFee) + filter := txpool.PendingFilter{ + MinTip: w.tip, } - pending := w.eth.TxPool().Pending(w.tip, baseFee) + if header.BaseFee != nil { + filter.BaseFee = uint256.MustFromBig(header.BaseFee) + } + pending := w.eth.TxPool().Pending(filter) txs, specialTxs = newTransactionsByPriceAndNonce(w.current.signer, pending, feeCapacity, header.BaseFee) } }