core/txpool/blobpool: adopt log calculation to new max decrease

EIP-7892 (BPO) changed the maximum blobfee decrease in a slot
from 1.125 to 1.17 . Since we want priorties to approximate time,
we should change our log calculation.

Signed-off-by: Csaba Kiraly <csaba.kiraly@gmail.com>
This commit is contained in:
Csaba Kiraly 2026-02-26 01:19:27 +01:00
parent a19f1ff11b
commit 27994a7c09
No known key found for this signature in database
GPG key ID: 0FE274EE8C95166E
6 changed files with 41 additions and 24 deletions

View file

@ -171,7 +171,7 @@ func newBlobTxMeta(id uint64, size uint64, storageSize uint32, tx *types.Transac
blobGas: tx.BlobGas(),
}
meta.basefeeJumps = dynamicFeeJumps(meta.execFeeCap)
meta.blobfeeJumps = dynamicFeeJumps(meta.blobFeeCap)
meta.blobfeeJumps = dynamicBlobFeeJumps(meta.blobFeeCap)
return meta
}
@ -317,14 +317,20 @@ func newBlobTxMeta(id uint64, size uint64, storageSize uint32, tx *types.Transac
// the current fee to the transaction's cap:
//
// jumps = floor(log1.125(txfee) - log1.125(basefee))
//
// For blob fees, EIP-7892 changed the ratio of target to max blobs, and
// with that also the maximum blob fee decrease in a slot from 1.125 to
// approx 1.17. therefore, we use:
//
// blobfeeJumps = floor(log1.17(txBlobfee) - log1.17(blobfee))
//
// - The second observation is that when ranking executable blob txs, it
// does not make sense to grant a later eviction priority to txs with high
// fee caps since it could enable pool wars. As such, any positive priority
// will be grouped together.
//
// priority = min(jumps, 0)
//
// - The third observation is that the basefee and blobfee move independently,
// so there's no way to split mixed txs on their own (A has higher base fee,
// B has higher blob fee).

View file

@ -830,8 +830,8 @@ func TestOpenIndex(t *testing.T) {
//blobfeeJumps = []float64{34.023, 35.570, 36.879, 29.686, 26.243, 20.358} // log 1.125 (blob fee cap)
evictExecTipCaps = []uint64{10, 10, 5, 5, 1, 1}
evictExecFeeJumps = []float64{39.098, 38.204, 38.204, 19.549, 19.549, 19.549} // min(log 1.125 (exec fee cap))
evictBlobFeeJumps = []float64{34.023, 34.023, 34.023, 29.686, 26.243, 20.358} // min(log 1.125 (blob fee cap))
evictExecFeeJumps = []float64{39.098, 38.204, 38.204, 19.549, 19.549, 19.549} // min(log 1.125 (exec fee cap))
evictBlobFeeJumps = []float64{25.517256, 25.517256, 25.517256, 22.264502, 19.682646, 15.268934} // min(log 1.17 (blob fee cap))
totalSpent = uint256.NewInt(21000*(100+90+200+10+80+300) + blobSize*(55+66+77+33+22+11) + 100*6) // 21000 gas x price + 128KB x blobprice + value
)

View file

@ -67,7 +67,7 @@ func newPriceHeap(basefee *uint256.Int, blobfee *uint256.Int, index map[common.A
func (h *evictHeap) reinit(basefee *uint256.Int, blobfee *uint256.Int, force bool) {
// If the update is mostly the same as the old, don't sort pointlessly
basefeeJumps := dynamicFeeJumps(basefee)
blobfeeJumps := dynamicFeeJumps(blobfee)
blobfeeJumps := dynamicBlobFeeJumps(blobfee)
if !force && math.Abs(h.basefeeJumps-basefeeJumps) < 0.01 && math.Abs(h.blobfeeJumps-blobfeeJumps) < 0.01 { // TODO(karalabe): 0.01 enough, maybe should be smaller? Maybe this optimization is moot?
return

View file

@ -109,22 +109,22 @@ func TestPriceHeapSorting(t *testing.T) {
order: []int{3, 2, 1, 0, 4, 5, 6},
},
// If both basefee and blobfee is specified, sort by the larger distance
// of the two from the current network conditions, splitting same (loglog)
// of the two from the current network conditions, splitting same
// ones via the tip.
//
// Basefee: 1000
// Blobfee: 100
// Basefee: 1000 , jumps: 888, 790, 702, 624
// Blobfee: 100 , jumps: 85, 73, 62, 53
//
// Tx #0: (800, 80) - 2 jumps below both => priority -1
// Tx #1: (630, 63) - 4 jumps below both => priority -2
// Tx #2: (800, 63) - 2 jumps below basefee, 4 jumps below blobfee => priority -2 (blob penalty dominates)
// Tx #3: (630, 80) - 4 jumps below basefee, 2 jumps below blobfee => priority -2 (base penalty dominates)
// Tx #0: (800, 80) - 2 jumps below both => priority -2
// Tx #1: (630, 55) - 4 jumps below both => priority -4
// Tx #2: (800, 55) - 2 jumps below basefee, 4 jumps below blobfee => priority -4 (blob penalty dominates)
// Tx #3: (630, 80) - 4 jumps below basefee, 2 jumps below blobfee => priority -4 (base penalty dominates)
//
// Txs 1, 2, 3 share the same priority, split via tip, prefer 0 as the best
{
execTips: []uint64{1, 2, 3, 4},
execFees: []uint64{800, 630, 800, 630},
blobFees: []uint64{80, 63, 63, 80},
blobFees: []uint64{80, 55, 55, 80},
basefee: 1000,
blobfee: 100,
order: []int{1, 2, 3, 0},
@ -142,7 +142,7 @@ func TestPriceHeapSorting(t *testing.T) {
blobFee = uint256.NewInt(tt.blobFees[j])
basefeeJumps = dynamicFeeJumps(execFee)
blobfeeJumps = dynamicFeeJumps(blobFee)
blobfeeJumps = dynamicBlobFeeJumps(blobFee)
)
index[addr] = []*blobTxMeta{{
id: uint64(j),
@ -201,7 +201,7 @@ func benchmarkPriceHeapReinit(b *testing.B, datacap uint64) {
blobFee = uint256.NewInt(rnd.Uint64())
basefeeJumps = dynamicFeeJumps(execFee)
blobfeeJumps = dynamicFeeJumps(blobFee)
blobfeeJumps = dynamicBlobFeeJumps(blobFee)
)
index[addr] = []*blobTxMeta{{
id: uint64(i),
@ -277,7 +277,7 @@ func benchmarkPriceHeapOverflow(b *testing.B, datacap uint64) {
blobFee = uint256.NewInt(rnd.Uint64())
basefeeJumps = dynamicFeeJumps(execFee)
blobfeeJumps = dynamicFeeJumps(blobFee)
blobfeeJumps = dynamicBlobFeeJumps(blobFee)
)
index[addr] = []*blobTxMeta{{
id: uint64(i),
@ -308,7 +308,7 @@ func benchmarkPriceHeapOverflow(b *testing.B, datacap uint64) {
blobFee = uint256.NewInt(rnd.Uint64())
basefeeJumps = dynamicFeeJumps(execFee)
blobfeeJumps = dynamicFeeJumps(blobFee)
blobfeeJumps = dynamicBlobFeeJumps(blobFee)
)
metas[i] = &blobTxMeta{
id: uint64(int(blobs) + i),

View file

@ -25,6 +25,13 @@ import (
// log1_125 is used in the eviction priority calculation.
var log1_125 = math.Log(1.125)
// log1_17 is used in the eviction priority calculation for blob fees.
// EIP-7892 (BPO) changed the ratio of target to max blobs, and with that
// also the maximum blob fee decrease in a slot from 1.125 to approx 1.17 .
// Since we want priorities to approximate time, we should change our log
// calculation for blob fees.
var log1_17 = log1_125 * 4 / 3
// evictionPriority calculates the eviction priority based on the algorithm
// described in the BlobPool docs for both fee components.
//
@ -66,5 +73,9 @@ func dynamicFeeJumps(fee *uint256.Int) float64 {
return math.Log(fee.Float64()) / log1_125
}
func dynamicBlobFeeJumps(fee *uint256.Int) float64 {
if fee.IsZero() {
return 0 // can't log2 zero, should never happen outside tests, but don't choke
}
return math.Log(fee.Float64()) / log1_17
}

View file

@ -30,12 +30,12 @@ func TestPriorityCalculation(t *testing.T) {
txfee uint64
result int
}{
{basefee: 7, txfee: 10, result: 2}, // 3.02 jumps, 4 ceil, 2 log2
{basefee: 17_200_000_000, txfee: 17_200_000_000, result: 0}, // 0 jumps, special case 0 log2
{basefee: 9_853_941_692, txfee: 11_085_092_510, result: 0}, // 0.99 jumps, 1 ceil, 0 log2
{basefee: 7, txfee: 10, result: 4}, // 3.02 jumps, 4 ceil
{basefee: 17_200_000_000, txfee: 17_200_000_000, result: 0}, // 0 jumps, special case 0
{basefee: 9_853_941_692, txfee: 11_085_092_510, result: 1}, // 0.99 jumps, 1 ceil
{basefee: 11_544_106_391, txfee: 10_356_781_100, result: -1}, // -0.92 jumps, -1 floor
{basefee: 17_200_000_000, txfee: 7, result: -184}, // -183.57 jumps, -184 floor
{basefee: 7, txfee: 17_200_000_000, result: 7}, // 183.57 jumps, 184 ceil, 7 log2
{basefee: 7, txfee: 17_200_000_000, result: 184}, // 183.57 jumps, 184 ceil
}
for i, tt := range tests {
var (
@ -69,7 +69,7 @@ func BenchmarkPriorityCalculation(b *testing.B) {
blobfee := uint256.NewInt(123_456_789_000) // Completely random, no idea what this will be
basefeeJumps := dynamicFeeJumps(basefee)
blobfeeJumps := dynamicFeeJumps(blobfee)
blobfeeJumps := dynamicBlobFeeJumps(blobfee)
// The transaction's fee cap and blob fee cap are constant across the life
// of the transaction, so we can pre-calculate and cache them.
@ -77,7 +77,7 @@ func BenchmarkPriorityCalculation(b *testing.B) {
txBlobfeeJumps := make([]float64, b.N)
for i := 0; i < b.N; i++ {
txBasefeeJumps[i] = dynamicFeeJumps(uint256.NewInt(rnd.Uint64()))
txBlobfeeJumps[i] = dynamicFeeJumps(uint256.NewInt(rnd.Uint64()))
txBlobfeeJumps[i] = dynamicBlobFeeJumps(uint256.NewInt(rnd.Uint64()))
}
b.ResetTimer()
b.ReportAllocs()