core/types: fix transaction pool price-heap comparison (#33923)

Fixes priceheap comparison in some edge cases.

---------

Signed-off-by: Csaba Kiraly <csaba.kiraly@gmail.com>
This commit is contained in:
Csaba Kiraly 2026-03-02 16:42:39 -06:00 committed by GitHub
parent 2726c9ef9e
commit 1eead2ec33
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 62 additions and 2 deletions

View file

@ -68,6 +68,43 @@ func TestListAddVeryExpensive(t *testing.T) {
}
}
// TestPriceHeapCmp tests that the price heap comparison function works as intended.
// It also tests combinations where the basefee is higher than the gas fee cap, which
// are useful to sort in the mempool to support basefee changes.
func TestPriceHeapCmp(t *testing.T) {
key, _ := crypto.GenerateKey()
txs := []*types.Transaction{
// nonce, gaslimit, gasfee, gastip
dynamicFeeTx(0, 1000, big.NewInt(2), big.NewInt(1), key),
dynamicFeeTx(0, 1000, big.NewInt(1), big.NewInt(2), key),
dynamicFeeTx(0, 1000, big.NewInt(1), big.NewInt(1), key),
dynamicFeeTx(0, 1000, big.NewInt(1), big.NewInt(0), key),
}
// create priceHeap
ph := &priceHeap{}
// now set the basefee on the heap
for _, basefee := range []uint64{0, 1, 2, 3} {
ph.baseFee = uint256.NewInt(basefee)
for i := 0; i < len(txs); i++ {
for j := 0; j < len(txs); j++ {
switch {
case i == j:
if c := ph.cmp(txs[i], txs[j]); c != 0 {
t.Errorf("tx %d should be equal priority to tx %d with basefee %d (cmp=%d)", i, j, basefee, c)
}
case i < j:
if c := ph.cmp(txs[i], txs[j]); c != 1 {
t.Errorf("tx %d vs tx %d comparison inconsistent with basefee %d (cmp=%d)", i, j, basefee, c)
}
}
}
}
}
}
func BenchmarkListAdd(b *testing.B) {
// Generate a list of transactions to insert
key, _ := crypto.GenerateKey()

View file

@ -396,14 +396,37 @@ func (tx *Transaction) calcEffectiveGasTip(dst *uint256.Int, baseFee *uint256.In
return err
}
// EffectiveGasTipValue returns the effective gasTip value for the given base fee,
// even if it would be negative. This can be used for sorting purposes.
func (tx *Transaction) EffectiveGasTipValue(baseFee *big.Int) *big.Int {
// min(gasTipCap, gasFeeCap - baseFee)
dst := new(big.Int)
if baseFee == nil {
dst.Set(tx.inner.gasTipCap())
return dst
}
dst.Sub(tx.inner.gasFeeCap(), baseFee) // gasFeeCap - baseFee
gasTipCap := tx.inner.gasTipCap()
if gasTipCap.Cmp(dst) < 0 { // gasTipCap < (gasFeeCap - baseFee)
dst.Set(gasTipCap)
}
return dst
}
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(uint256.Int), new(uint256.Int)
tx.calcEffectiveGasTip(txTip, baseFee)
other.calcEffectiveGasTip(otherTip, baseFee)
err1 := tx.calcEffectiveGasTip(txTip, baseFee)
err2 := other.calcEffectiveGasTip(otherTip, baseFee)
if err1 != nil || err2 != nil {
// fall back to big int comparison in case of error
base := baseFee.ToBig()
return tx.EffectiveGasTipValue(base).Cmp(other.EffectiveGasTipValue(base))
}
return txTip.Cmp(otherTip)
}