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()
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))
for addr, list := range pool.pending {
txs := list.Flatten()
// 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 {
if minTipBig != nil {
if tx.EffectiveGasTipIntCmp(minTipBig, baseFeeBig) < 0 {
if filter.MinTip != nil {
if tx.EffectiveGasTipIntCmp(filter.MinTip, filter.BaseFee) < 0 {
txs = txs[:i]
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.
// If baseFee is nil then the sorting is based on gasFeeCap.
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
}
@ -677,6 +677,10 @@ func (l *pricedList) Reheap() {
// 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.
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()
}

View file

@ -28,6 +28,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp"
"github.com/holiman/uint256"
)
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
// the actual negative value, _and_ ErrGasFeeCapTooLow
func (tx *Transaction) EffectiveGasTip(baseFee *big.Int) (*big.Int, error) {
dst := new(big.Int)
err := tx.calcEffectiveGasTip(dst, baseFee)
return dst, err
dst := new(uint256.Int)
base := new(uint256.Int)
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
// 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 {
dst.Set(tx.inner.gasTipCap())
dst.SetFromBig(tx.inner.gasTipCap())
return nil
}
var err error
gasFeeCap := tx.inner.gasFeeCap()
if gasFeeCap.Cmp(baseFee) < 0 {
dst.SetFromBig(tx.inner.gasFeeCap())
if dst.Cmp(baseFee) < 0 {
err = ErrGasFeeCapTooLow
}
dst.Sub(gasFeeCap, baseFee)
gasTipCap := tx.inner.gasTipCap()
dst.Sub(dst, baseFee)
gasTipCap := new(uint256.Int)
gasTipCap.SetFromBig(tx.inner.gasTipCap())
if gasTipCap.Cmp(dst) < 0 {
dst.Set(gasTipCap)
}
return err
}
// EffectiveGasTipCmp compares the effective gasTipCap of two transactions assuming the given base fee.
func (tx *Transaction) EffectiveGasTipCmp(other *Transaction, baseFee *big.Int) int {
func (tx *Transaction) EffectiveGasTipCmp(other *Transaction, baseFee *uint256.Int) int {
if baseFee == nil {
return tx.GasTipCapCmp(other)
}
// 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)
other.calcEffectiveGasTip(otherTip, baseFee)
return txTip.Cmp(otherTip)
}
// 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 {
return tx.GasTipCapIntCmp(other)
return tx.GasTipCapIntCmp(other.ToBig())
}
txTip := new(big.Int)
txTip := new(uint256.Int)
tx.calcEffectiveGasTip(txTip, baseFee)
return txTip.Cmp(other)
}

View file

@ -31,6 +31,7 @@ import (
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/holiman/uint256"
)
// The values in those tests are from the Transaction Tests
@ -609,12 +610,12 @@ func BenchmarkEffectiveGasTip(b *testing.B) {
Data: nil,
}
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.ReportAllocs()
for i := 0; i < b.N; i++ {
_, err := tx.EffectiveGasTip(baseFee)
_, err := tx.EffectiveGasTip(baseFee.ToBig())
if err != nil {
b.Fatal(err)
}
@ -623,7 +624,7 @@ func BenchmarkEffectiveGasTip(b *testing.B) {
b.Run("IntoMethod", func(b *testing.B) {
b.ReportAllocs()
dst := new(big.Int)
dst := new(uint256.Int)
for i := 0; i < b.N; i++ {
err := tx.calcEffectiveGasTip(dst, baseFee)
if err != nil {
@ -666,23 +667,25 @@ func TestEffectiveGasTipInto(t *testing.T) {
tx, _ := SignNewTx(key, signer, txdata)
var baseFee *big.Int
var baseFee2 *uint256.Int
if tc.baseFee != nil {
baseFee = big.NewInt(*tc.baseFee)
baseFee2 = uint256.NewInt(uint64(*tc.baseFee))
}
// Get result from original method
orig, origErr := tx.EffectiveGasTip(baseFee)
// Get result from new method
dst := new(big.Int)
newErr := tx.calcEffectiveGasTip(dst, baseFee)
dst := new(uint256.Int)
newErr := tx.calcEffectiveGasTip(dst, baseFee2)
// Compare results
if (origErr != nil) != (newErr != nil) {
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)
}
}
@ -692,3 +695,28 @@ func TestEffectiveGasTipInto(t *testing.T) {
func intPtr(i int64) *int64 {
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)
}
})
}