erigon-pulse/cmd/rpcdaemon/commands/eth_system.go
Alex Sharov 6bf34b5cb8
RPC: eth_feeHistory (#2501)
* eth_FeeHistory

* eth_FeeHistory

* eth_FeeHistory

* lint

* lint
2021-08-08 14:27:47 +07:00

229 lines
6.4 KiB
Go

package commands
import (
"context"
"fmt"
"github.com/ledgerwatch/erigon-lib/kv"
"github.com/ledgerwatch/erigon/common"
"github.com/ledgerwatch/erigon/common/hexutil"
"github.com/ledgerwatch/erigon/core/rawdb"
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/eth/ethconfig"
"github.com/ledgerwatch/erigon/eth/gasprice"
"github.com/ledgerwatch/erigon/eth/stagedsync/stages"
"github.com/ledgerwatch/erigon/params"
"github.com/ledgerwatch/erigon/rpc"
)
// BlockNumber implements eth_blockNumber. Returns the block number of most recent block.
func (api *APIImpl) BlockNumber(ctx context.Context) (hexutil.Uint64, error) {
tx, err := api.db.BeginRo(ctx)
if err != nil {
return 0, err
}
defer tx.Rollback()
execution, err := stages.GetStageProgress(tx, stages.Finish)
if err != nil {
return 0, err
}
return hexutil.Uint64(execution), nil
}
// Syncing implements eth_syncing. Returns a data object detaling the status of the sync process or false if not syncing.
func (api *APIImpl) Syncing(ctx context.Context) (interface{}, error) {
tx, err := api.db.BeginRo(ctx)
if err != nil {
return nil, err
}
defer tx.Rollback()
highestBlock, err := stages.GetStageProgress(tx, stages.Headers)
if err != nil {
return false, err
}
currentBlock, err := stages.GetStageProgress(tx, stages.Finish)
if err != nil {
return false, err
}
if currentBlock > 0 && currentBlock >= highestBlock { // Return not syncing if the synchronisation already completed
return false, nil
}
// Otherwise gather the block sync stats
type S struct {
StageName string `json:"stage_name"`
BlockNumber hexutil.Uint64 `json:"block_number"`
}
stagesMap := make([]S, len(stages.AllStages))
for i, stage := range stages.AllStages {
progress, err := stages.GetStageProgress(tx, stage)
if err != nil {
return nil, err
}
stagesMap[i].StageName = string(stage)
stagesMap[i].BlockNumber = hexutil.Uint64(progress)
}
return map[string]interface{}{
"currentBlock": hexutil.Uint64(currentBlock),
"highestBlock": hexutil.Uint64(highestBlock),
"stages": stagesMap,
}, nil
}
// ChainId implements eth_chainId. Returns the current ethereum chainId.
func (api *APIImpl) ChainId(ctx context.Context) (hexutil.Uint64, error) {
tx, err := api.db.BeginRo(ctx)
if err != nil {
return 0, err
}
defer tx.Rollback()
chainConfig, err := api.chainConfig(tx)
if err != nil {
return 0, err
}
return hexutil.Uint64(chainConfig.ChainID.Uint64()), nil
}
// ChainID alias of ChainId - just for convenience
func (api *APIImpl) ChainID(ctx context.Context) (hexutil.Uint64, error) {
return api.ChainId(ctx)
}
// ProtocolVersion implements eth_protocolVersion. Returns the current ethereum protocol version.
func (api *APIImpl) ProtocolVersion(ctx context.Context) (hexutil.Uint, error) {
ver, err := api.ethBackend.ProtocolVersion(ctx)
if err != nil {
return 0, err
}
return hexutil.Uint(ver), nil
}
// GasPrice implements eth_gasPrice. Returns the current price per gas in wei.
func (api *APIImpl) GasPrice(ctx context.Context) (*hexutil.Big, error) {
tx, err := api.db.BeginRo(ctx)
if err != nil {
return nil, err
}
defer tx.Rollback()
cc, err := api.chainConfig(tx)
if err != nil {
return nil, err
}
oracle := gasprice.NewOracle(&GasPriceOracleBackend{tx: tx, cc: cc}, ethconfig.Defaults.GPO)
tipcap, err := oracle.SuggestTipCap(ctx)
if err != nil {
return nil, err
}
if head := rawdb.ReadCurrentHeader(tx); head != nil && head.BaseFee != nil {
tipcap.Add(tipcap, head.BaseFee)
}
return (*hexutil.Big)(tipcap), err
}
// MaxPriorityFeePerGas returns a suggestion for a gas tip cap for dynamic fee transactions.
func (api *APIImpl) MaxPriorityFeePerGas(ctx context.Context) (*hexutil.Big, error) {
tx, err := api.db.BeginRo(ctx)
if err != nil {
return nil, err
}
defer tx.Rollback()
cc, err := api.chainConfig(tx)
if err != nil {
return nil, err
}
oracle := gasprice.NewOracle(&GasPriceOracleBackend{tx: tx, cc: cc}, ethconfig.Defaults.GPO)
tipcap, err := oracle.SuggestTipCap(ctx)
if err != nil {
return nil, err
}
return (*hexutil.Big)(tipcap), err
}
type feeHistoryResult struct {
OldestBlock rpc.BlockNumber `json:"oldestBlock"`
Reward [][]*hexutil.Big `json:"reward,omitempty"`
BaseFee []*hexutil.Big `json:"baseFeePerGas,omitempty"`
GasUsedRatio []float64 `json:"gasUsedRatio"`
}
func (api *APIImpl) FeeHistory(ctx context.Context, blockCount int, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*feeHistoryResult, error) {
tx, err := api.db.BeginRo(ctx)
if err != nil {
return nil, err
}
defer tx.Rollback()
cc, err := api.chainConfig(tx)
if err != nil {
return nil, err
}
oracle := gasprice.NewOracle(&GasPriceOracleBackend{tx: tx, cc: cc}, ethconfig.Defaults.GPO)
oldest, reward, baseFee, gasUsed, err := oracle.FeeHistory(ctx, blockCount, lastBlock, rewardPercentiles)
if err != nil {
return nil, err
}
results := &feeHistoryResult{
OldestBlock: oldest,
GasUsedRatio: gasUsed,
}
if reward != nil {
results.Reward = make([][]*hexutil.Big, len(reward))
for i, w := range reward {
results.Reward[i] = make([]*hexutil.Big, len(w))
for j, v := range w {
results.Reward[i][j] = (*hexutil.Big)(v)
}
}
}
if baseFee != nil {
results.BaseFee = make([]*hexutil.Big, len(baseFee))
for i, v := range baseFee {
results.BaseFee[i] = (*hexutil.Big)(v)
}
}
return results, nil
}
type GasPriceOracleBackend struct {
tx kv.Tx
cc *params.ChainConfig
}
func (b *GasPriceOracleBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error) {
blockNum, err := getBlockNumber(number, b.tx)
if err != nil {
return nil, err
}
header := rawdb.ReadHeaderByNumber(b.tx, blockNum)
if header == nil {
return nil, fmt.Errorf("header not found: %d", blockNum)
}
return header, nil
}
func (b *GasPriceOracleBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Block, error) {
blockNum, err := getBlockNumber(number, b.tx)
if err != nil {
return nil, err
}
block, _, err := rawdb.ReadBlockByNumberWithSenders(b.tx, blockNum)
if err != nil {
return nil, err
}
return block, nil
}
func (b *GasPriceOracleBackend) ChainConfig() *params.ChainConfig {
return b.cc
}
func (b *GasPriceOracleBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
return rawdb.ReadReceiptsByHash(b.tx, hash)
}
func (b *GasPriceOracleBackend) PendingBlockAndReceipts() (*types.Block, types.Receipts) {
return nil, nil
}