core/types: reduce allocations for transaction comparison

This commit is contained in:
MariusVanDerWijden 2025-05-28 13:23:34 +02:00
parent bf8f63dcd2
commit c42b31a1f6
4 changed files with 63 additions and 37 deletions

View file

@ -514,26 +514,15 @@ func (pool *LegacyPool) Pending(filter txpool.PendingFilter) map[common.Address]
pool.mu.Lock() pool.mu.Lock()
defer pool.mu.Unlock() defer pool.mu.Unlock()
// Convert the new uint256.Int types to the old big.Int ones used by the legacy pool
var (
minTipBig *big.Int
baseFeeBig *big.Int
)
if filter.MinTip != nil {
minTipBig = filter.MinTip.ToBig()
}
if filter.BaseFee != nil {
baseFeeBig = filter.BaseFee.ToBig()
}
pending := make(map[common.Address][]*txpool.LazyTransaction, len(pool.pending)) pending := make(map[common.Address][]*txpool.LazyTransaction, len(pool.pending))
for addr, list := range pool.pending { for addr, list := range pool.pending {
txs := list.Flatten() txs := list.Flatten()
// If the miner requests tip enforcement, cap the lists now // If the miner requests tip enforcement, cap the lists now
if minTipBig != nil || filter.GasLimitCap != 0 { if filter.MinTip != nil || filter.GasLimitCap != 0 {
for i, tx := range txs { for i, tx := range txs {
if minTipBig != nil { if filter.MinTip != nil {
if tx.EffectiveGasTipIntCmp(minTipBig, baseFeeBig) < 0 { if tx.EffectiveGasTipIntCmp(filter.MinTip, filter.BaseFee) < 0 {
txs = txs[:i] txs = txs[:i]
break break
} }

View file

@ -475,7 +475,7 @@ func (l *list) subTotalCost(txs []*types.Transaction) {
// then the heap is sorted based on the effective tip based on the given base fee. // then the heap is sorted based on the effective tip based on the given base fee.
// If baseFee is nil then the sorting is based on gasFeeCap. // If baseFee is nil then the sorting is based on gasFeeCap.
type priceHeap struct { type priceHeap struct {
baseFee *big.Int // heap should always be re-sorted after baseFee is changed baseFee *uint256.Int // heap should always be re-sorted after baseFee is changed
list []*types.Transaction list []*types.Transaction
} }
@ -677,6 +677,10 @@ func (l *pricedList) Reheap() {
// SetBaseFee updates the base fee and triggers a re-heap. Note that Removed is not // SetBaseFee updates the base fee and triggers a re-heap. Note that Removed is not
// necessary to call right before SetBaseFee when processing a new block. // necessary to call right before SetBaseFee when processing a new block.
func (l *pricedList) SetBaseFee(baseFee *big.Int) { func (l *pricedList) SetBaseFee(baseFee *big.Int) {
l.urgent.baseFee = baseFee base := new(uint256.Int)
if baseFee != nil {
base.SetFromBig(baseFee)
}
l.urgent.baseFee = base
l.Reheap() l.Reheap()
} }

View file

@ -28,6 +28,7 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/holiman/uint256"
) )
var ( var (
@ -355,51 +356,55 @@ func (tx *Transaction) GasTipCapIntCmp(other *big.Int) int {
// Note: if the effective gasTipCap is negative, this method returns both error // Note: if the effective gasTipCap is negative, this method returns both error
// the actual negative value, _and_ ErrGasFeeCapTooLow // the actual negative value, _and_ ErrGasFeeCapTooLow
func (tx *Transaction) EffectiveGasTip(baseFee *big.Int) (*big.Int, error) { func (tx *Transaction) EffectiveGasTip(baseFee *big.Int) (*big.Int, error) {
dst := new(big.Int) dst := new(uint256.Int)
err := tx.calcEffectiveGasTip(dst, baseFee) base := new(uint256.Int)
return dst, err if baseFee != nil {
base.SetFromBig(baseFee)
}
err := tx.calcEffectiveGasTip(dst, base)
return dst.ToBig(), err
} }
// calcEffectiveGasTip calculates the effective gas tip of the transaction and // calcEffectiveGasTip calculates the effective gas tip of the transaction and
// saves the result to dst. // saves the result to dst.
func (tx *Transaction) calcEffectiveGasTip(dst *big.Int, baseFee *big.Int) error { func (tx *Transaction) calcEffectiveGasTip(dst *uint256.Int, baseFee *uint256.Int) error {
if baseFee == nil { if baseFee == nil {
dst.Set(tx.inner.gasTipCap()) dst.SetFromBig(tx.inner.gasTipCap())
return nil return nil
} }
var err error var err error
gasFeeCap := tx.inner.gasFeeCap() dst.SetFromBig(tx.inner.gasFeeCap())
if gasFeeCap.Cmp(baseFee) < 0 { if dst.Cmp(baseFee) < 0 {
err = ErrGasFeeCapTooLow err = ErrGasFeeCapTooLow
} }
dst.Sub(gasFeeCap, baseFee) dst.Sub(dst, baseFee)
gasTipCap := tx.inner.gasTipCap() gasTipCap := new(uint256.Int)
gasTipCap.SetFromBig(tx.inner.gasTipCap())
if gasTipCap.Cmp(dst) < 0 { if gasTipCap.Cmp(dst) < 0 {
dst.Set(gasTipCap) dst.Set(gasTipCap)
} }
return err return err
} }
// EffectiveGasTipCmp compares the effective gasTipCap of two transactions assuming the given base fee. func (tx *Transaction) EffectiveGasTipCmp(other *Transaction, baseFee *uint256.Int) int {
func (tx *Transaction) EffectiveGasTipCmp(other *Transaction, baseFee *big.Int) int {
if baseFee == nil { if baseFee == nil {
return tx.GasTipCapCmp(other) return tx.GasTipCapCmp(other)
} }
// Use more efficient internal method. // Use more efficient internal method.
txTip, otherTip := new(big.Int), new(big.Int) txTip, otherTip := new(uint256.Int), new(uint256.Int)
tx.calcEffectiveGasTip(txTip, baseFee) tx.calcEffectiveGasTip(txTip, baseFee)
other.calcEffectiveGasTip(otherTip, baseFee) other.calcEffectiveGasTip(otherTip, baseFee)
return txTip.Cmp(otherTip) return txTip.Cmp(otherTip)
} }
// EffectiveGasTipIntCmp compares the effective gasTipCap of a transaction to the given gasTipCap. // EffectiveGasTipIntCmp compares the effective gasTipCap of a transaction to the given gasTipCap.
func (tx *Transaction) EffectiveGasTipIntCmp(other *big.Int, baseFee *big.Int) int { func (tx *Transaction) EffectiveGasTipIntCmp(other *uint256.Int, baseFee *uint256.Int) int {
if baseFee == nil { if baseFee == nil {
return tx.GasTipCapIntCmp(other) return tx.GasTipCapIntCmp(other.ToBig())
} }
txTip := new(big.Int) txTip := new(uint256.Int)
tx.calcEffectiveGasTip(txTip, baseFee) tx.calcEffectiveGasTip(txTip, baseFee)
return txTip.Cmp(other) return txTip.Cmp(other)
} }

View file

@ -31,6 +31,7 @@ import (
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/holiman/uint256"
) )
// The values in those tests are from the Transaction Tests // The values in those tests are from the Transaction Tests
@ -609,12 +610,12 @@ func BenchmarkEffectiveGasTip(b *testing.B) {
Data: nil, Data: nil,
} }
tx, _ := SignNewTx(key, signer, txdata) tx, _ := SignNewTx(key, signer, txdata)
baseFee := big.NewInt(1000000000) // 1 gwei baseFee := uint256.NewInt(1000000000) // 1 gwei
b.Run("Original", func(b *testing.B) { b.Run("Original", func(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
_, err := tx.EffectiveGasTip(baseFee) _, err := tx.EffectiveGasTip(baseFee.ToBig())
if err != nil { if err != nil {
b.Fatal(err) b.Fatal(err)
} }
@ -623,7 +624,7 @@ func BenchmarkEffectiveGasTip(b *testing.B) {
b.Run("IntoMethod", func(b *testing.B) { b.Run("IntoMethod", func(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
dst := new(big.Int) dst := new(uint256.Int)
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
err := tx.calcEffectiveGasTip(dst, baseFee) err := tx.calcEffectiveGasTip(dst, baseFee)
if err != nil { if err != nil {
@ -666,23 +667,25 @@ func TestEffectiveGasTipInto(t *testing.T) {
tx, _ := SignNewTx(key, signer, txdata) tx, _ := SignNewTx(key, signer, txdata)
var baseFee *big.Int var baseFee *big.Int
var baseFee2 *uint256.Int
if tc.baseFee != nil { if tc.baseFee != nil {
baseFee = big.NewInt(*tc.baseFee) baseFee = big.NewInt(*tc.baseFee)
baseFee2 = uint256.NewInt(uint64(*tc.baseFee))
} }
// Get result from original method // Get result from original method
orig, origErr := tx.EffectiveGasTip(baseFee) orig, origErr := tx.EffectiveGasTip(baseFee)
// Get result from new method // Get result from new method
dst := new(big.Int) dst := new(uint256.Int)
newErr := tx.calcEffectiveGasTip(dst, baseFee) newErr := tx.calcEffectiveGasTip(dst, baseFee2)
// Compare results // Compare results
if (origErr != nil) != (newErr != nil) { if (origErr != nil) != (newErr != nil) {
t.Fatalf("case %d: error mismatch: orig %v, new %v", i, origErr, newErr) t.Fatalf("case %d: error mismatch: orig %v, new %v", i, origErr, newErr)
} }
if orig.Cmp(dst) != 0 { if origErr == nil && orig.Cmp(dst.ToBig()) != 0 {
t.Fatalf("case %d: result mismatch: orig %v, new %v", i, orig, dst) t.Fatalf("case %d: result mismatch: orig %v, new %v", i, orig, dst)
} }
} }
@ -692,3 +695,28 @@ func TestEffectiveGasTipInto(t *testing.T) {
func intPtr(i int64) *int64 { func intPtr(i int64) *int64 {
return &i return &i
} }
func BenchmarkEffectiveGasTipCmp(b *testing.B) {
signer := LatestSigner(params.TestChainConfig)
key, _ := crypto.GenerateKey()
txdata := &DynamicFeeTx{
ChainID: big.NewInt(1),
Nonce: 0,
GasTipCap: big.NewInt(2000000000),
GasFeeCap: big.NewInt(3000000000),
Gas: 21000,
To: &common.Address{},
Value: big.NewInt(0),
Data: nil,
}
tx, _ := SignNewTx(key, signer, txdata)
other, _ := SignNewTx(key, signer, txdata)
baseFee := uint256.NewInt(1000000000) // 1 gwei
b.Run("Original", func(b *testing.B) {
b.ReportAllocs()
for i := 0; i < b.N; i++ {
tx.EffectiveGasTipCmp(other, baseFee)
}
})
}