mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-19 11:20:45 +00:00
eth/gasprice: fix fee history percentile weighting under eip-8037
Weight reward percentiles by cumulative receipt gas instead of block header gas used, which reflects max(regular, state) and can exceed the sum of per-tx receipt gas.
This commit is contained in:
parent
7e625dd548
commit
fbe9b59279
2 changed files with 48 additions and 3 deletions
|
|
@ -140,17 +140,29 @@ func (oracle *Oracle) processBlock(bf *blockFees, percentiles []float64) {
|
||||||
return a.reward.Cmp(b.reward)
|
return a.reward.Cmp(b.reward)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Percentile rewards are weighted by per-tx receipt gas. Under EIP-8037
|
||||||
|
// block gas used is max(cumulative regular, cumulative state) and can exceed
|
||||||
|
// the sum of receipt gas, so base thresholds on the receipt total.
|
||||||
|
totalGasUsed := bf.receipts[len(bf.receipts)-1].CumulativeGasUsed
|
||||||
|
bf.results.reward = percentileRewards(sorter, totalGasUsed, percentiles)
|
||||||
|
}
|
||||||
|
|
||||||
|
// percentileRewards returns the effective priority-fee reward at each requested
|
||||||
|
// percentile, with transactions sorted by ascending tip and weighted by gas used.
|
||||||
|
func percentileRewards(sorter []txGasAndReward, totalGasUsed uint64, percentiles []float64) []*big.Int {
|
||||||
|
reward := make([]*big.Int, len(percentiles))
|
||||||
var txIndex int
|
var txIndex int
|
||||||
sumGasUsed := sorter[0].gasUsed
|
sumGasUsed := sorter[0].gasUsed
|
||||||
|
|
||||||
for i, p := range percentiles {
|
for i, p := range percentiles {
|
||||||
thresholdGasUsed := uint64(float64(bf.block.GasUsed()) * p / 100)
|
thresholdGasUsed := uint64(float64(totalGasUsed) * p / 100)
|
||||||
for sumGasUsed < thresholdGasUsed && txIndex < len(bf.block.Transactions())-1 {
|
for sumGasUsed < thresholdGasUsed && txIndex < len(sorter)-1 {
|
||||||
txIndex++
|
txIndex++
|
||||||
sumGasUsed += sorter[txIndex].gasUsed
|
sumGasUsed += sorter[txIndex].gasUsed
|
||||||
}
|
}
|
||||||
bf.results.reward[i] = sorter[txIndex].reward
|
reward[i] = sorter[txIndex].reward
|
||||||
}
|
}
|
||||||
|
return reward
|
||||||
}
|
}
|
||||||
|
|
||||||
// resolveBlockRange resolves the specified block range to absolute block numbers while also
|
// resolveBlockRange resolves the specified block range to absolute block numbers while also
|
||||||
|
|
|
||||||
|
|
@ -22,9 +22,42 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TestPercentileRewardsReceiptGasEIP8037 checks that reward percentiles are
|
||||||
|
// weighted by the sum of per-tx receipt gas, not the block header gas used.
|
||||||
|
// Under EIP-8037 the header reflects max(cumulative regular, cumulative state)
|
||||||
|
// and can exceed receipt totals; using it as the threshold skews percentiles.
|
||||||
|
func TestPercentileRewardsReceiptGasEIP8037(t *testing.T) {
|
||||||
|
lowTip := big.NewInt(1)
|
||||||
|
highTip := big.NewInt(100 * params.GWei)
|
||||||
|
|
||||||
|
const (
|
||||||
|
lowGas = 100_000
|
||||||
|
highGas = 21_000
|
||||||
|
receiptTotal = lowGas + highGas
|
||||||
|
headerGasUsed = 500_000 // state bottleneck, well above receiptTotal
|
||||||
|
)
|
||||||
|
sorter := []txGasAndReward{
|
||||||
|
{gasUsed: lowGas, reward: lowTip},
|
||||||
|
{gasUsed: highGas, reward: highTip},
|
||||||
|
}
|
||||||
|
|
||||||
|
got := percentileRewards(sorter, receiptTotal, []float64{50})
|
||||||
|
if got[0].Cmp(lowTip) != 0 {
|
||||||
|
t.Fatalf("50th percentile reward = %s, want low-tip %s (receipt gas %d)",
|
||||||
|
got[0], lowTip, receiptTotal)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Using block header gas used as the weight base selects the wrong tx.
|
||||||
|
gotBroken := percentileRewards(sorter, headerGasUsed, []float64{50})
|
||||||
|
if gotBroken[0].Cmp(highTip) != 0 {
|
||||||
|
t.Fatalf("sanity check: header-based weighting = %s, want high-tip %s", gotBroken[0], highTip)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestFeeHistory(t *testing.T) {
|
func TestFeeHistory(t *testing.T) {
|
||||||
var cases = []struct {
|
var cases = []struct {
|
||||||
pending bool
|
pending bool
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue