eth/gasprice: fix blob base fee ordering, simplify tests

This commit is contained in:
lightclient 2024-03-06 07:29:05 -07:00 committed by Felix Lange
parent 9ec859b73c
commit 7305023635
3 changed files with 64 additions and 103 deletions

View file

@ -51,11 +51,10 @@ const (
// blockFees represents a single block for processing
type blockFees struct {
// set by the caller
blockNumber uint64
header *types.Header
parentHeader *types.Header
block *types.Block // only set if reward percentiles are requested
receipts types.Receipts
blockNumber uint64
header *types.Header
block *types.Block // only set if reward percentiles are requested
receipts types.Receipts
// filled by processBlock
results processedFees
err error
@ -85,34 +84,31 @@ type txGasAndReward struct {
// the block field filled in, retrieves the block from the backend if not present yet and
// fills in the rest of the fields.
func (oracle *Oracle) processBlock(bf *blockFees, percentiles []float64) {
chainconfig := oracle.backend.ChainConfig()
config := oracle.backend.ChainConfig()
// Fill in base fee and next base fee.
if bf.results.baseFee = bf.header.BaseFee; bf.results.baseFee == nil {
bf.results.baseFee = new(big.Int)
}
if chainconfig.IsLondon(big.NewInt(int64(bf.blockNumber + 1))) {
bf.results.nextBaseFee = eip1559.CalcBaseFee(chainconfig, bf.header)
if config.IsLondon(big.NewInt(int64(bf.blockNumber + 1))) {
bf.results.nextBaseFee = eip1559.CalcBaseFee(config, bf.header)
} else {
bf.results.nextBaseFee = new(big.Int)
}
// Fill in blob base fee and next blob base fee.
if excessBlobGas := bf.header.ExcessBlobGas; excessBlobGas != nil {
bf.results.blobBaseFee = eip4844.CalcBlobFee(*excessBlobGas)
bf.results.nextBlobBaseFee = eip4844.CalcBlobFee(eip4844.CalcExcessBlobGas(*excessBlobGas, *bf.header.BlobGasUsed))
} else {
bf.results.blobBaseFee = new(big.Int)
bf.results.nextBlobBaseFee = new(big.Int)
}
// Compute gas used ratio for normal and blob gas.
bf.results.gasUsedRatio = float64(bf.header.GasUsed) / float64(bf.header.GasLimit)
bf.results.blobGasUsedRatio = 0.0
bf.results.blobBaseFee = new(big.Int)
if blobGasUsed := bf.header.BlobGasUsed; blobGasUsed != nil {
bf.results.blobGasUsedRatio = float64(*blobGasUsed) / params.MaxBlobGasPerBlock
}
getBlobBaseFee := func(h *types.Header) *big.Int {
if h != nil && chainconfig.IsCancun(h.Number, h.Time) {
if excessBlobGas := h.ExcessBlobGas; excessBlobGas != nil {
return eip4844.CalcBlobFee(*excessBlobGas)
}
}
return new(big.Int)
}
bf.results.blobBaseFee = getBlobBaseFee(bf.parentHeader)
bf.results.nextBlobBaseFee = getBlobBaseFee(bf.header)
if bf.header != nil && chainconfig.IsCancun(bf.header.Number, bf.header.Time) {
if blobGasUsed := bf.header.BlobGasUsed; blobGasUsed != nil {
bf.results.blobGasUsedRatio = float64(*blobGasUsed) / params.MaxBlobGasPerBlock
}
}
if len(percentiles) == 0 {
// rewards were not requested, return null
return
@ -153,6 +149,15 @@ func (oracle *Oracle) processBlock(bf *blockFees, percentiles []float64) {
}
}
// getBlobBaseFee is a helper function which computes and returns the blob base
// fee if excessBlobGas exists.
func getBlobBaseFee(h *types.Header) *big.Int {
if excessBlobGas := h.ExcessBlobGas; excessBlobGas != nil {
return eip4844.CalcBlobFee(*excessBlobGas)
}
return new(big.Int)
}
// resolveBlockRange resolves the specified block range to absolute block numbers while also
// enforcing backend specific limitations. The pending block and corresponding receipts are
// also returned if requested and available.
@ -290,7 +295,6 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks uint64, unresolvedL
if pendingBlock != nil && blockNumber >= pendingBlock.NumberU64() {
fees.block, fees.receipts = pendingBlock, pendingReceipts
fees.header = fees.block.Header()
fees.parentHeader, fees.err = oracle.backend.HeaderByNumber(ctx, rpc.BlockNumber(pendingBlock.NumberU64()-1))
oracle.processBlock(fees, rewardPercentiles)
results <- fees
} else {
@ -310,9 +314,6 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks uint64, unresolvedL
fees.header, fees.err = oracle.backend.HeaderByNumber(ctx, rpc.BlockNumber(blockNumber))
}
if fees.header != nil && fees.err == nil {
if blockNumber > 0 {
fees.parentHeader, fees.err = oracle.backend.HeaderByNumber(ctx, rpc.BlockNumber(blockNumber-1))
}
oracle.processBlock(fees, rewardPercentiles)
if fees.err == nil {
oracle.historyCache.Add(cacheKey, fees.results)

View file

@ -58,7 +58,7 @@ func TestFeeHistory(t *testing.T) {
MaxHeaderHistory: c.maxHeader,
MaxBlockHistory: c.maxBlock,
}
backend := newTestBackend(t, big.NewInt(16), c.pending, true)
backend := newTestBackend(t, big.NewInt(16), big.NewInt(28), c.pending)
oracle := NewOracle(backend, config)
first, reward, baseFee, ratio, blobBaseFee, blobRatio, err := oracle.FeeHistory(context.Background(), c.count, c.last, c.percent)

View file

@ -18,7 +18,6 @@ package gasprice
import (
"context"
"crypto/ecdsa"
"crypto/sha256"
"fmt"
"math"
@ -32,7 +31,6 @@ import (
"github.com/ethereum/go-ethereum/consensus/beacon"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
@ -129,7 +127,10 @@ func (b *testBackend) teardown() {
// newTestBackend creates a test backend. OBS: don't forget to invoke tearDown
// after use, otherwise the blockchain instance will mem-leak via goroutines.
func newTestBackend(t *testing.T, londonBlock *big.Int, pending bool, addCancunBlocks bool) *testBackend {
func newTestBackend(t *testing.T, londonBlock *big.Int, cancunBlock *big.Int, pending bool) *testBackend {
if londonBlock != nil && cancunBlock != nil && londonBlock.Cmp(cancunBlock) == 1 {
panic("cannot define test backend with cancun before london")
}
var (
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
addr = crypto.PubkeyToAddress(key.PublicKey)
@ -138,32 +139,29 @@ func newTestBackend(t *testing.T, londonBlock *big.Int, pending bool, addCancunB
Config: &config,
Alloc: types.GenesisAlloc{addr: {Balance: big.NewInt(math.MaxInt64)}},
}
signer = types.LatestSigner(gspec.Config)
blobSigners = [6]struct {
key *ecdsa.PrivateKey
addr common.Address
}{}
signer = types.LatestSigner(gspec.Config)
// Compute empty blob hash.
emptyBlob = kzg4844.Blob{}
emptyBlobCommit, _ = kzg4844.BlobToCommitment(emptyBlob)
emptyBlobVHash = kzg4844.CalcBlobHashV1(sha256.New(), &emptyBlobCommit)
)
config.LondonBlock = londonBlock
config.ArrowGlacierBlock = londonBlock
config.GrayGlacierBlock = londonBlock
var engine consensus.Engine = beacon.New(ethash.NewFaker())
td := int(params.GenesisDifficulty.Uint64())
td := params.GenesisDifficulty.Uint64()
if addCancunBlocks {
for i := range blobSigners {
key, _ := crypto.GenerateKey()
addr := crypto.PubkeyToAddress(key.PublicKey)
blobSigners[i].key = key
blobSigners[i].addr = addr
gspec.Alloc[addr] = types.Account{Balance: big.NewInt(math.MaxInt64)}
}
if cancunBlock != nil {
ts := gspec.Timestamp + cancunBlock.Uint64()*10 // fixed 10 sec block time in blockgen
config.ShanghaiTime = &ts
config.CancunTime = &ts
signer = types.LatestSigner(gspec.Config)
}
// Generate testing blocks
genDb, blocks, _ := core.GenerateChainWithGenesis(gspec, engine, testHead+1, func(i int, b *core.BlockGen) {
db, blocks, _ := core.GenerateChainWithGenesis(gspec, engine, testHead+1, func(i int, b *core.BlockGen) {
b.SetCoinbase(common.Address{1})
td += int(b.Difficulty().Uint64())
var txdata types.TxData
if londonBlock != nil && b.Number().Cmp(londonBlock) >= 0 {
@ -187,61 +185,15 @@ func newTestBackend(t *testing.T, londonBlock *big.Int, pending bool, addCancunB
}
}
b.AddTx(types.MustSignNewTx(key, signer, txdata))
})
// Construct testing chain
db := rawdb.NewMemoryDatabase()
merger := consensus.NewMerger(db)
chain, err := core.NewBlockChain(db, &core.CacheConfig{TrieCleanNoPrefetch: true}, gspec, nil, engine, vm.Config{}, nil, nil)
if err != nil {
t.Fatalf("Failed to create local chain, %v", err)
}
if i, err := chain.InsertChain(blocks); err != nil {
panic(fmt.Errorf("error inserting block %d: %w", i, err))
}
chain.SetFinalized(chain.GetBlockByNumber(25).Header())
chain.SetSafe(chain.GetBlockByNumber(25).Header())
if addCancunBlocks {
if err := chain.SetHead(26); err != nil {
panic(err)
}
head := chain.GetBlockByNumber(26)
ts := head.Time()
config.ShanghaiTime = &ts
config.CancunTime = &ts
ttd := chain.GetTd(head.Hash(), head.NumberU64())
config.TerminalTotalDifficulty = ttd
signer = types.LatestSigner(gspec.Config)
merger.ReachTTD()
merger.FinalizePoS()
var (
emptyBlob = kzg4844.Blob{}
emptyBlobCommit, _ = kzg4844.BlobToCommitment(emptyBlob)
emptyBlobVHash = kzg4844.CalcBlobHashV1(sha256.New(), &emptyBlobCommit)
)
postBlocks, _ := core.GenerateChain(gspec.Config, head, engine, genDb, 6+1, func(i int, b *core.BlockGen) {
if cancunBlock != nil && b.Number().Cmp(cancunBlock) >= 0 {
b.SetPoS()
txdata := &types.DynamicFeeTx{
ChainID: gspec.Config.ChainID,
Nonce: b.TxNonce(addr),
To: &common.Address{},
Gas: 30000,
GasFeeCap: big.NewInt(100 * params.GWei),
GasTipCap: big.NewInt(int64(i+1) * params.GWei),
Data: []byte{},
}
b.AddTx(types.MustSignNewTx(key, signer, txdata))
// put more blobs in each new block
for j := 0; j < i && j < 6; j++ {
blobTx := &types.BlobTx{
ChainID: uint256.MustFromBig(gspec.Config.ChainID),
Nonce: b.TxNonce(blobSigners[j].addr),
Nonce: b.TxNonce(addr),
To: common.Address{},
Gas: 30000,
GasFeeCap: uint256.NewInt(100 * params.GWei),
@ -252,14 +204,22 @@ func newTestBackend(t *testing.T, londonBlock *big.Int, pending bool, addCancunB
Value: uint256.NewInt(100),
Sidecar: nil,
}
b.AddTx(types.MustSignNewTx(blobSigners[j].key, signer, blobTx))
b.AddTx(types.MustSignNewTx(key, signer, blobTx))
}
})
if i, err := chain.InsertChain(postBlocks); err != nil {
panic(fmt.Errorf("error inserting block %d: %w", i, err))
}
td += b.Difficulty().Uint64()
})
// Construct testing chain
gspec.Config.TerminalTotalDifficulty = new(big.Int).SetUint64(td)
chain, err := core.NewBlockChain(db, &core.CacheConfig{TrieCleanNoPrefetch: true}, gspec, nil, engine, vm.Config{}, nil, nil)
if err != nil {
t.Fatalf("Failed to create local chain, %v", err)
}
if i, err := chain.InsertChain(blocks); err != nil {
panic(fmt.Errorf("error inserting block %d: %w", i, err))
}
chain.SetFinalized(chain.GetBlockByNumber(25).Header())
chain.SetSafe(chain.GetBlockByNumber(25).Header())
return &testBackend{chain: chain, pending: pending}
}
@ -289,7 +249,7 @@ func TestSuggestTipCap(t *testing.T) {
{big.NewInt(33), big.NewInt(params.GWei * int64(30))}, // Fork point in the future
}
for _, c := range cases {
backend := newTestBackend(t, c.fork, false, false)
backend := newTestBackend(t, c.fork, nil, false)
oracle := NewOracle(backend, config)
// The gas price sampled is: 32G, 31G, 30G, 29G, 28G, 27G