diff --git a/eth/gasprice/feehistory.go b/eth/gasprice/feehistory.go index cd1e1cef3eb..fc35eb737c2 100644 --- a/eth/gasprice/feehistory.go +++ b/eth/gasprice/feehistory.go @@ -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 @@ -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 @@ -203,9 +229,9 @@ 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) @@ -213,10 +239,10 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks int, unresolvedLast } 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 @@ -231,7 +257,7 @@ 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) @@ -239,14 +265,16 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks int, unresolvedLast 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 @@ -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 { @@ -288,7 +317,7 @@ 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] @@ -296,5 +325,5 @@ func (oracle *Oracle) FeeHistory(ctx context.Context, blocks int, unresolvedLast 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 } diff --git a/eth/gasprice/feehistory_test.go b/eth/gasprice/feehistory_test.go index eab55261fa1..8207a89140d 100644 --- a/eth/gasprice/feehistory_test.go +++ b/eth/gasprice/feehistory_test.go @@ -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 { @@ -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) } diff --git a/turbo/jsonrpc/eth_system.go b/turbo/jsonrpc/eth_system.go index fa1f10302fa..034d34b84e9 100644 --- a/turbo/jsonrpc/eth_system.go +++ b/turbo/jsonrpc/eth_system.go @@ -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" @@ -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) { @@ -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 } @@ -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