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.
This commit is contained in:
Daniel Liu 2026-03-11 11:28:07 +08:00 committed by GitHub
parent 8850835f6b
commit 7d67a4ead4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 37 additions and 25 deletions

View file

@ -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))

View file

@ -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)
}

View file

@ -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

View file

@ -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
}

View file

@ -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 {

View file

@ -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()

View file

@ -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

View file

@ -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 {

View file

@ -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)
}
}