Skip to content

Commit

Permalink
Add support to eth_blobBaseFee, eth_baseFee and EIP4844 support t…
Browse files Browse the repository at this point in the history
  • Loading branch information
Giulio2002 authored Sep 18, 2024
1 parent 72f749b commit 1dd4a55
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 23 deletions.
63 changes: 46 additions & 17 deletions eth/gasprice/feehistory.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,12 @@ type blockFees struct {
block *types.Block // only set if reward percentiles are requested
receipts types.Receipts
// filled by processBlock
reward []*big.Int
baseFee, nextBaseFee *big.Int
gasUsedRatio float64
err error
reward []*big.Int
baseFee, nextBaseFee *big.Int
blobBaseFee, nextBlobBaseFee *big.Int
gasUsedRatio float64
blobGasUsedRatio float64
err error
}

// txGasAndReward is sorted in ascending order based on reward
Expand Down Expand Up @@ -90,11 +92,35 @@ func (oracle *Oracle) processBlock(bf *blockFees, percentiles []float64) {
} else {
bf.nextBaseFee = new(big.Int)
}

// Fill in blob base fee and next blob base fee.
if excessBlobGas := bf.header.ExcessBlobGas; excessBlobGas != nil {
blobBaseFee256, err := misc.GetBlobGasPrice(chainconfig, *excessBlobGas)
if err != nil {
bf.err = err
return
}
nextBlobBaseFee256, err := misc.GetBlobGasPrice(chainconfig, misc.CalcExcessBlobGas(chainconfig, bf.header))
if err != nil {
bf.err = err
return
}
bf.blobBaseFee = blobBaseFee256.ToBig()
bf.nextBlobBaseFee = nextBlobBaseFee256.ToBig()

} else {
bf.blobBaseFee = new(big.Int)
bf.nextBlobBaseFee = new(big.Int)
}
bf.gasUsedRatio = float64(bf.header.GasUsed) / float64(bf.header.GasLimit)
if len(percentiles) == 0 {
// rewards were not requested, return null
return
}

if blobGasUsed := bf.header.BlobGasUsed; blobGasUsed != nil && chainconfig.MaxBlobGasPerBlock != nil {
bf.blobGasUsedRatio = float64(*blobGasUsed) / float64(*chainconfig.MaxBlobGasPerBlock)
}
if bf.block == nil || (bf.receipts == nil && len(bf.block.Transactions()) != 0) {
oracle.log.Error("Block or receipts are missing while reward percentiles are requested")
return
Expand Down Expand Up @@ -203,20 +229,20 @@ func (oracle *Oracle) resolveBlockRange(ctx context.Context, lastBlock rpc.Block
//
// Note: baseFee includes the next block after the newest of the returned range, because this
// value can be derived from the newest block.
func (oracle *Oracle) FeeHistory(ctx context.Context, blocks int, unresolvedLastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, error) {
func (oracle *Oracle) FeeHistory(ctx context.Context, blocks int, unresolvedLastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, []*big.Int, []float64, error) {
if blocks < 1 {
return libcommon.Big0, nil, nil, nil, nil // returning with no data and no error means there are no retrievable blocks
return libcommon.Big0, nil, nil, nil, nil, nil, nil // returning with no data and no error means there are no retrievable blocks
}
if blocks > maxFeeHistory {
oracle.log.Warn("Sanitizing fee history length", "requested", blocks, "truncated", maxFeeHistory)
blocks = maxFeeHistory
}
for i, p := range rewardPercentiles {
if p < 0 || p > 100 {
return libcommon.Big0, nil, nil, nil, fmt.Errorf("%w: %f", ErrInvalidPercentile, p)
return libcommon.Big0, nil, nil, nil, nil, nil, fmt.Errorf("%w: %f", ErrInvalidPercentile, p)
}
if i > 0 && p < rewardPercentiles[i-1] {
return libcommon.Big0, nil, nil, nil, fmt.Errorf("%w: #%d:%f > #%d:%f", ErrInvalidPercentile, i-1, rewardPercentiles[i-1], i, p)
return libcommon.Big0, nil, nil, nil, nil, nil, fmt.Errorf("%w: #%d:%f > #%d:%f", ErrInvalidPercentile, i-1, rewardPercentiles[i-1], i, p)
}
}
// Only process blocks if reward percentiles were requested
Expand All @@ -231,22 +257,24 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks int, unresolvedLast
)
pendingBlock, pendingReceipts, lastBlock, blocks, err := oracle.resolveBlockRange(ctx, unresolvedLastBlock, blocks, maxHistory)
if err != nil || blocks == 0 {
return libcommon.Big0, nil, nil, nil, err
return libcommon.Big0, nil, nil, nil, nil, nil, err
}
oldestBlock := lastBlock + 1 - uint64(blocks)

var (
next = oldestBlock
)
var (
reward = make([][]*big.Int, blocks)
baseFee = make([]*big.Int, blocks+1)
gasUsedRatio = make([]float64, blocks)
firstMissing = blocks
reward = make([][]*big.Int, blocks)
baseFee = make([]*big.Int, blocks+1)
gasUsedRatio = make([]float64, blocks)
blobGasUsedRatio = make([]float64, blocks)
blobBaseFee = make([]*big.Int, blocks+1)
firstMissing = blocks
)
for ; blocks > 0; blocks-- {
if err = libcommon.Stopped(ctx.Done()); err != nil {
return libcommon.Big0, nil, nil, nil, err
return libcommon.Big0, nil, nil, nil, nil, nil, err
}
// Retrieve the next block number to fetch with this goroutine
blockNumber := atomic.AddUint64(&next, 1) - 1
Expand Down Expand Up @@ -275,11 +303,12 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks int, unresolvedLast
}

if fees.err != nil {
return libcommon.Big0, nil, nil, nil, fees.err
return libcommon.Big0, nil, nil, nil, nil, nil, fees.err
}
i := int(fees.blockNumber - oldestBlock)
if fees.header != nil {
reward[i], baseFee[i], baseFee[i+1], gasUsedRatio[i] = fees.reward, fees.baseFee, fees.nextBaseFee, fees.gasUsedRatio
blobGasUsedRatio[i], blobBaseFee[i], blobBaseFee[i+1] = fees.blobGasUsedRatio, fees.blobBaseFee, fees.nextBlobBaseFee
} else {
// getting no block and no error means we are requesting into the future (might happen because of a reorg)
if i < firstMissing {
Expand All @@ -288,13 +317,13 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks int, unresolvedLast
}
}
if firstMissing == 0 {
return libcommon.Big0, nil, nil, nil, nil
return libcommon.Big0, nil, nil, nil, nil, nil, nil
}
if len(rewardPercentiles) != 0 {
reward = reward[:firstMissing]
} else {
reward = nil
}
baseFee, gasUsedRatio = baseFee[:firstMissing+1], gasUsedRatio[:firstMissing]
return new(big.Int).SetUint64(oldestBlock), reward, baseFee, gasUsedRatio, nil
return new(big.Int).SetUint64(oldestBlock), reward, baseFee, gasUsedRatio, blobBaseFee, blobGasUsedRatio, nil
}
8 changes: 7 additions & 1 deletion eth/gasprice/feehistory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ func TestFeeHistory(t *testing.T) {
cache := jsonrpc.NewGasPriceCache()
oracle := gasprice.NewOracle(jsonrpc.NewGasPriceOracleBackend(tx, baseApi), config, cache, log.New())

first, reward, baseFee, ratio, err := oracle.FeeHistory(context.Background(), c.count, c.last, c.percent)
first, reward, baseFee, ratio, blobBaseFee, blobBaseFeeRatio, err := oracle.FeeHistory(context.Background(), c.count, c.last, c.percent)

expReward := c.expCount
if len(c.percent) == 0 {
Expand All @@ -100,6 +100,12 @@ func TestFeeHistory(t *testing.T) {
if len(ratio) != c.expCount {
t.Fatalf("Test case %d: gasUsedRatio array length mismatch, want %d, got %d", i, c.expCount, len(ratio))
}
if c.expCount != 0 && len(blobBaseFee) != c.expCount+1 {
t.Fatalf("Test case %d: blobBaseFee array length mismatch, want %d, got %d", i, c.expCount+1, len(blobBaseFee))
}
if len(blobBaseFeeRatio) != c.expCount {
t.Fatalf("Test case %d: blobBaseFeeRatio array length mismatch, want %d, got %d", i, c.expCount, len(blobBaseFeeRatio))
}
if err != c.expErr && !errors.Is(err, c.expErr) {
t.Fatalf("Test case %d: error mismatch, want %v, got %v", i, c.expErr, err)
}
Expand Down
74 changes: 69 additions & 5 deletions turbo/jsonrpc/eth_system.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ import (
"math"
"math/big"

"github.com/erigontech/erigon-lib/common"
"github.com/erigontech/erigon-lib/common/hexutil"

"github.com/erigontech/erigon-lib/chain"
"github.com/erigontech/erigon-lib/kv"

"github.com/erigontech/erigon/consensus/misc"
"github.com/erigontech/erigon/core/rawdb"
"github.com/erigontech/erigon/core/types"
"github.com/erigontech/erigon/eth/ethconfig"
Expand Down Expand Up @@ -177,10 +179,12 @@ func (api *APIImpl) MaxPriorityFeePerGas(ctx context.Context) (*hexutil.Big, err
}

type feeHistoryResult struct {
OldestBlock *hexutil.Big `json:"oldestBlock"`
Reward [][]*hexutil.Big `json:"reward,omitempty"`
BaseFee []*hexutil.Big `json:"baseFeePerGas,omitempty"`
GasUsedRatio []float64 `json:"gasUsedRatio"`
OldestBlock *hexutil.Big `json:"oldestBlock"`
Reward [][]*hexutil.Big `json:"reward,omitempty"`
BaseFee []*hexutil.Big `json:"baseFeePerGas,omitempty"`
GasUsedRatio []float64 `json:"gasUsedRatio"`
BlobBaseFee []*hexutil.Big `json:"baseFeePerBlobGas,omitempty"`
BlobGasUsedRatio []float64 `json:"blobGasUsedRatio,omitempty"`
}

func (api *APIImpl) FeeHistory(ctx context.Context, blockCount rpc.DecimalOrHex, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*feeHistoryResult, error) {
Expand All @@ -191,7 +195,7 @@ func (api *APIImpl) FeeHistory(ctx context.Context, blockCount rpc.DecimalOrHex,
defer tx.Rollback()
oracle := gasprice.NewOracle(NewGasPriceOracleBackend(tx, api.BaseAPI), ethconfig.Defaults.GPO, api.gasCache, api.logger.New("app", "gasPriceOracle"))

oldest, reward, baseFee, gasUsed, err := oracle.FeeHistory(ctx, int(blockCount), lastBlock, rewardPercentiles)
oldest, reward, baseFee, gasUsed, blobBaseFee, blobGasUsedRatio, err := oracle.FeeHistory(ctx, int(blockCount), lastBlock, rewardPercentiles)
if err != nil {
return nil, err
}
Expand All @@ -214,9 +218,69 @@ func (api *APIImpl) FeeHistory(ctx context.Context, blockCount rpc.DecimalOrHex,
results.BaseFee[i] = (*hexutil.Big)(v)
}
}
if blobBaseFee != nil {
results.BlobBaseFee = make([]*hexutil.Big, len(blobBaseFee))
for i, v := range blobBaseFee {
results.BlobBaseFee[i] = (*hexutil.Big)(v)
}
}
if blobGasUsedRatio != nil {
results.BlobGasUsedRatio = blobGasUsedRatio
}
return results, nil
}

// BlobBaseFee returns the base fee for blob gas at the current head.
func (api *APIImpl) BlobBaseFee(ctx context.Context) (*hexutil.Big, error) {
// read current header
tx, err := api.db.BeginRo(ctx)
if err != nil {
return nil, err
}
defer tx.Rollback()
header := rawdb.ReadCurrentHeader(tx)
if header == nil || header.BlobGasUsed == nil {
return (*hexutil.Big)(common.Big0), nil
}
config, err := api.BaseAPI.chainConfig(ctx, tx)
if err != nil {
return nil, err
}
if config == nil {
return (*hexutil.Big)(common.Big0), nil
}
ret256, err := misc.GetBlobGasPrice(config, misc.CalcExcessBlobGas(config, header))
if err != nil {
return nil, err
}
return (*hexutil.Big)(ret256.ToBig()), nil
}

// BaseFee returns the base fee at the current head.
func (api *APIImpl) BaseFee(ctx context.Context) (*hexutil.Big, error) {
// read current header
tx, err := api.db.BeginRo(ctx)
if err != nil {
return nil, err
}
defer tx.Rollback()
header := rawdb.ReadCurrentHeader(tx)
if header == nil {
return (*hexutil.Big)(common.Big0), nil
}
config, err := api.BaseAPI.chainConfig(ctx, tx)
if err != nil {
return nil, err
}
if config == nil {
return (*hexutil.Big)(common.Big0), nil
}
if !config.IsLondon(header.Number.Uint64() + 1) {
return (*hexutil.Big)(common.Big0), nil
}
return (*hexutil.Big)(misc.CalcBaseFee(config, header)), nil
}

type GasPriceOracleBackend struct {
tx kv.Tx
baseApi *BaseAPI
Expand Down

0 comments on commit 1dd4a55

Please sign in to comment.