From 8850835f6b1b5c8468c54dbedd1ebc39c3f0fbb1 Mon Sep 17 00:00:00 2001 From: Daniel Liu <139250065@qq.com> Date: Wed, 11 Mar 2026 11:20:56 +0800 Subject: [PATCH] perf(core/txpool,miner): speed up pending transaction ordering with uint256 #29008 (#2159) Switch LazyTransaction gas caps from *big.Int to *uint256.Int and convert once at pending retrieval time. In miner ordering, keep fee comparisons on uint256 and precompute TRC21 gas price in uint256 form to avoid repeated big-int conversions in heap comparisons. Also update affected tests and call sites in legacypool/worker/helper paths. Compatibility: LazyTransaction exported field types changed (GasFeeCap/GasTipCap). --- core/txpool/legacypool/legacypool.go | 4 ++-- core/txpool/subpool.go | 6 +++--- eth/helper_test.go | 4 ++-- miner/ordering.go | 30 ++++++++++++++++++---------- miner/ordering_test.go | 13 ++++++------ miner/worker.go | 12 +++++------ miner/worker_test.go | 11 +++++----- 7 files changed, 45 insertions(+), 35 deletions(-) diff --git a/core/txpool/legacypool/legacypool.go b/core/txpool/legacypool/legacypool.go index af20848b6c..1464cd60a1 100644 --- a/core/txpool/legacypool/legacypool.go +++ b/core/txpool/legacypool/legacypool.go @@ -618,8 +618,8 @@ func (pool *LegacyPool) Pending(minTip *uint256.Int, baseFee *uint256.Int) map[c Hash: txs[i].Hash(), Tx: txs[i], Time: txs[i].Time(), - GasFeeCap: txs[i].GasFeeCap(), - GasTipCap: txs[i].GasTipCap(), + GasFeeCap: uint256.MustFromBig(txs[i].GasFeeCap()), + GasTipCap: uint256.MustFromBig(txs[i].GasTipCap()), Gas: txs[i].Gas(), } } diff --git a/core/txpool/subpool.go b/core/txpool/subpool.go index 4ebd72fd25..714d54200f 100644 --- a/core/txpool/subpool.go +++ b/core/txpool/subpool.go @@ -35,9 +35,9 @@ type LazyTransaction struct { Hash common.Hash // Transaction hash to pull up if needed Tx *types.Transaction // Transaction if already resolved - Time time.Time // Time when the transaction was first seen - GasFeeCap *big.Int // Maximum fee per gas the transaction may consume - GasTipCap *big.Int // Maximum miner tip per gas the transaction can pay + Time time.Time // Time when the transaction was first seen + GasFeeCap *uint256.Int // Maximum fee per gas the transaction may consume + GasTipCap *uint256.Int // Maximum miner tip per gas the transaction can pay Gas uint64 // Amount of gas required by the transaction } diff --git a/eth/helper_test.go b/eth/helper_test.go index 04f6b0494a..9602260844 100644 --- a/eth/helper_test.go +++ b/eth/helper_test.go @@ -166,8 +166,8 @@ func (p *testTxPool) Pending(minTip *uint256.Int, baseFee *uint256.Int) map[comm Hash: tx.Hash(), Tx: tx, Time: tx.Time(), - GasFeeCap: tx.GasFeeCap(), - GasTipCap: tx.GasTipCap(), + GasFeeCap: uint256.MustFromBig(tx.GasFeeCap()), + GasTipCap: uint256.MustFromBig(tx.GasTipCap()), }) } } diff --git a/miner/ordering.go b/miner/ordering.go index d8b3dc7dd1..bfb708e39f 100644 --- a/miner/ordering.go +++ b/miner/ordering.go @@ -23,27 +23,30 @@ import ( "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/core/txpool" "github.com/XinFinOrg/XDPoSChain/core/types" + "github.com/holiman/uint256" ) +var trc21GasPriceUint = uint256.MustFromBig(common.TRC21GasPrice) + // txWithMinerFee wraps a transaction with its gas price or effective miner gasTipCap type txWithMinerFee struct { tx *txpool.LazyTransaction from common.Address - fees *big.Int + fees *uint256.Int } // newTxWithMinerFee creates a wrapped transaction, calculating the effective // miner gasTipCap if a base fee is provided. // Returns error in case of a negative effective miner gasTipCap. -func newTxWithMinerFee(tx *txpool.LazyTransaction, from common.Address, baseFee *big.Int) (*txWithMinerFee, error) { - tip := new(big.Int).Set(tx.GasTipCap) +func newTxWithMinerFee(tx *txpool.LazyTransaction, from common.Address, baseFee *uint256.Int) (*txWithMinerFee, error) { + tip := new(uint256.Int).Set(tx.GasTipCap) if baseFee != nil { if tx.GasFeeCap.Cmp(baseFee) < 0 { return nil, types.ErrGasFeeCapTooLow } - effectiveTip := new(big.Int).Sub(tx.GasFeeCap, baseFee) - if tip.Cmp(effectiveTip) > 0 { - tip = effectiveTip + tip = new(uint256.Int).Sub(tx.GasFeeCap, baseFee) + if tip.Gt(tx.GasTipCap) { + tip = tx.GasTipCap } } return &txWithMinerFee{ @@ -68,14 +71,14 @@ func (s txByPriceAndTime) Less(i, j int) bool { i_price := s.txs[i].fees if tx := s.txs[i].tx.Resolve(); tx != nil && tx.To() != nil { if _, ok := s.payersSwap[*tx.To()]; ok { - i_price = common.TRC21GasPrice + i_price = trc21GasPriceUint } } j_price := s.txs[j].fees if tx := s.txs[j].tx.Resolve(); tx != nil && tx.To() != nil { if _, ok := s.payersSwap[*tx.To()]; ok { - j_price = common.TRC21GasPrice + j_price = trc21GasPriceUint } } @@ -112,7 +115,7 @@ type transactionsByPriceAndNonce struct { txs map[common.Address][]*txpool.LazyTransaction // Per account nonce-sorted list of transactions heads txByPriceAndTime // Next transaction for each unique account (price heap) signer types.Signer // Signer for the set of transactions - baseFee *big.Int // Current base fee + baseFee *uint256.Int // Current base fee } // newTransactionsByPriceAndNonce creates a transaction set that can retrieve @@ -121,6 +124,11 @@ type transactionsByPriceAndNonce struct { // Note, the input map is reowned so the caller should not interact any more with // if after providing it to the constructor. func newTransactionsByPriceAndNonce(signer types.Signer, txs map[common.Address][]*txpool.LazyTransaction, payersSwap map[common.Address]*big.Int, baseFee *big.Int) (*transactionsByPriceAndNonce, types.Transactions) { + // Convert the basefee from header format to uint256 format + var baseFeeUint *uint256.Int + if baseFee != nil { + baseFeeUint = uint256.MustFromBig(baseFee) + } // Initialize a price and received time based heap with the head transactions heads := txByPriceAndTime{ txs: make([]*txWithMinerFee, 0, len(txs)), @@ -137,7 +145,7 @@ func newTransactionsByPriceAndNonce(signer types.Signer, txs map[common.Address] } } if len(normalTxs) > 0 { - wrapped, err := newTxWithMinerFee(normalTxs[0], from, baseFee) + wrapped, err := newTxWithMinerFee(normalTxs[0], from, baseFeeUint) if err != nil { delete(txs, from) continue @@ -157,7 +165,7 @@ func newTransactionsByPriceAndNonce(signer types.Signer, txs map[common.Address] txs: txs, heads: heads, signer: signer, - baseFee: baseFee, + baseFee: baseFeeUint, }, specialTxs } diff --git a/miner/ordering_test.go b/miner/ordering_test.go index 18a88df863..d1a7d96329 100644 --- a/miner/ordering_test.go +++ b/miner/ordering_test.go @@ -27,6 +27,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/core/txpool" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/crypto" + "github.com/holiman/uint256" ) func TestTransactionPriceNonceSortLegacy(t *testing.T) { @@ -90,8 +91,8 @@ func testTransactionPriceNonceSort(t *testing.T, baseFee *big.Int) { Hash: tx.Hash(), Tx: tx, Time: tx.Time(), - GasFeeCap: tx.GasFeeCap(), - GasTipCap: tx.GasTipCap(), + GasFeeCap: uint256.MustFromBig(tx.GasFeeCap()), + GasTipCap: uint256.MustFromBig(tx.GasTipCap()), }) } expectedCount += count @@ -155,8 +156,8 @@ func TestTransactionTimeSort(t *testing.T) { Hash: tx.Hash(), Tx: tx, Time: tx.Time(), - GasFeeCap: tx.GasFeeCap(), - GasTipCap: tx.GasTipCap(), + GasFeeCap: uint256.MustFromBig(tx.GasFeeCap()), + GasTipCap: uint256.MustFromBig(tx.GasTipCap()), }) } // Sort the transactions and cross check the nonce ordering @@ -193,11 +194,11 @@ func TestNewTransactionsByPriceAndNonce_SpecialSeparation(t *testing.T) { genNormalTx := func(nonce uint64, key *ecdsa.PrivateKey) *txpool.LazyTransaction { tx, _ := types.SignTx(types.NewTransaction(nonce, common.HexToAddress("0x1234567890123456789012345678901234567890"), big.NewInt(1), 21000, big.NewInt(1), nil), signer, key) - return &txpool.LazyTransaction{Tx: tx, Hash: tx.Hash(), Time: tx.Time(), GasFeeCap: tx.GasFeeCap(), GasTipCap: tx.GasTipCap()} + return &txpool.LazyTransaction{Tx: tx, Hash: tx.Hash(), Time: tx.Time(), GasFeeCap: uint256.MustFromBig(tx.GasFeeCap()), GasTipCap: uint256.MustFromBig(tx.GasTipCap())} } genSpecialTx := func(nonce uint64, key *ecdsa.PrivateKey) *txpool.LazyTransaction { tx, _ := types.SignTx(types.NewTransaction(nonce, common.BlockSignersBinary, big.NewInt(1), 21000, big.NewInt(1), nil), signer, key) - return &txpool.LazyTransaction{Tx: tx, Hash: tx.Hash(), Time: tx.Time(), GasFeeCap: tx.GasFeeCap(), GasTipCap: tx.GasTipCap()} + return &txpool.LazyTransaction{Tx: tx, Hash: tx.Hash(), Time: tx.Time(), GasFeeCap: uint256.MustFromBig(tx.GasFeeCap()), GasTipCap: uint256.MustFromBig(tx.GasTipCap())} } testCases := []struct { diff --git a/miner/worker.go b/miner/worker.go index 6cfb6167e1..47a849e52f 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -145,7 +145,7 @@ type worker struct { mu sync.Mutex coinbase common.Address extra []byte - tip *big.Int // Minimum tip needed for non-local transaction to include them + tip *uint256.Int // Minimum tip needed for non-local transaction to include them snapshotMu sync.RWMutex // The lock used to protect the block snapshot and state snapshot snapshotBlock *types.Block @@ -176,7 +176,7 @@ func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus mux: mux, coinbase: config.Etherbase, extra: config.ExtraData, - tip: config.GasPrice, + tip: uint256.MustFromBig(config.GasPrice), txsCh: make(chan core.NewTxsEvent, txChanSize), chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize), chainSideCh: make(chan core.ChainSideEvent, chainSideChanSize), @@ -233,7 +233,7 @@ func (w *worker) setGasTip(tip *big.Int) error { } // Copy the value to avoid external mutation through shared pointers. - w.tip = new(big.Int).Set(tip) + w.tip = uint256.MustFromBig(tip) log.Info("Worker tip updated", "tip", w.tip) return nil } @@ -404,8 +404,8 @@ func (w *worker) update() { Hash: tx.Hash(), Tx: tx, Time: tx.Time(), - GasFeeCap: tx.GasFeeCap(), - GasTipCap: tx.GasTipCap(), + GasFeeCap: uint256.MustFromBig(tx.GasFeeCap()), + GasTipCap: uint256.MustFromBig(tx.GasTipCap()), }) } feeCapacity := w.current.state.GetTRC21FeeCapacityFromState() @@ -866,7 +866,7 @@ func (w *worker) commitNewWork() { if header.BaseFee != nil { baseFee = uint256.MustFromBig(header.BaseFee) } - pending := w.eth.TxPool().Pending(uint256.MustFromBig(w.tip), baseFee) + pending := w.eth.TxPool().Pending(w.tip, baseFee) txs, specialTxs = newTransactionsByPriceAndNonce(w.current.signer, pending, feeCapacity, header.BaseFee) } } diff --git a/miner/worker_test.go b/miner/worker_test.go index 7df6920d30..9b174c04d3 100644 --- a/miner/worker_test.go +++ b/miner/worker_test.go @@ -30,6 +30,7 @@ import ( "github.com/XinFinOrg/XDPoSChain/core/vm" "github.com/XinFinOrg/XDPoSChain/event" "github.com/XinFinOrg/XDPoSChain/params" + "github.com/holiman/uint256" ) func newBlockingSubscription() event.Subscription { @@ -132,8 +133,8 @@ func TestWorkerCheckPreCommitXDPoSMismatch(t *testing.T) { } func TestWorkerSetGasTipValidation(t *testing.T) { - w := &worker{tip: big.NewInt(1)} - old := new(big.Int).Set(w.tip) + w := &worker{tip: uint256.NewInt(1)} + old := new(uint256.Int).Set(w.tip) tests := []struct { name string @@ -163,12 +164,12 @@ func TestWorkerSetGasTipCopiesValue(t *testing.T) { if err := w.setGasTip(input); err != nil { t.Fatalf("setGasTip failed: %v", err) } - if w.tip == input { - t.Fatal("worker tip shares pointer with input") + if w.tip == nil { + t.Fatal("worker tip was not set") } input.Add(input, big.NewInt(1)) - if w.tip.Cmp(big.NewInt(2*params.GWei)) != 0 { + if w.tip.Cmp(uint256.NewInt(2*params.GWei)) != 0 { t.Fatalf("worker tip mutated via input pointer: have %v", w.tip) } }