api: ethrpc: implement eth_feeHistory (#9539)
Co-authored-by: Raúl Kripalani <raul@protocol.ai>
This commit is contained in:
parent
c0cbcda1c2
commit
64afdfc642
@ -786,6 +786,7 @@ type FullNode interface {
|
|||||||
NetListening(ctx context.Context) (bool, error) //perm:read
|
NetListening(ctx context.Context) (bool, error) //perm:read
|
||||||
EthProtocolVersion(ctx context.Context) (EthUint64, error) //perm:read
|
EthProtocolVersion(ctx context.Context) (EthUint64, error) //perm:read
|
||||||
EthGasPrice(ctx context.Context) (EthBigInt, error) //perm:read
|
EthGasPrice(ctx context.Context) (EthBigInt, error) //perm:read
|
||||||
|
EthFeeHistory(ctx context.Context, blkCount uint64, newestBlk string) (EthFeeHistory, error) //perm:read
|
||||||
|
|
||||||
EthMaxPriorityFeePerGas(ctx context.Context) (EthBigInt, error) //perm:read
|
EthMaxPriorityFeePerGas(ctx context.Context) (EthBigInt, error) //perm:read
|
||||||
EthEstimateGas(ctx context.Context, tx EthCall) (EthUint64, error) //perm:read
|
EthEstimateGas(ctx context.Context, tx EthCall) (EthUint64, error) //perm:read
|
||||||
|
@ -372,6 +372,8 @@ func init() {
|
|||||||
ethhash, _ := api.EthHashFromCid(c)
|
ethhash, _ := api.EthHashFromCid(c)
|
||||||
addExample(ðhash)
|
addExample(ðhash)
|
||||||
|
|
||||||
|
ethFeeHistoryReward := [][]api.EthBigInt{}
|
||||||
|
addExample(ðFeeHistoryReward)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetAPIType(name, pkg string) (i interface{}, t reflect.Type, permStruct []reflect.Type) {
|
func GetAPIType(name, pkg string) (i interface{}, t reflect.Type, permStruct []reflect.Type) {
|
||||||
|
@ -417,3 +417,10 @@ func (h EthHash) ToCid() cid.Cid {
|
|||||||
|
|
||||||
return cid.NewCidV1(cid.DagCBOR, mh)
|
return cid.NewCidV1(cid.DagCBOR, mh)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type EthFeeHistory struct {
|
||||||
|
OldestBlock uint64 `json:"oldestBlock"`
|
||||||
|
BaseFeePerGas []EthBigInt `json:"baseFeePerGas"`
|
||||||
|
GasUsedRatio []float64 `json:"gasUsedRatio"`
|
||||||
|
Reward *[][]EthBigInt `json:"reward,omitempty"`
|
||||||
|
}
|
||||||
|
@ -996,6 +996,21 @@ func (mr *MockFullNodeMockRecorder) EthEstimateGas(arg0, arg1 interface{}) *gomo
|
|||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EthEstimateGas", reflect.TypeOf((*MockFullNode)(nil).EthEstimateGas), arg0, arg1)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EthEstimateGas", reflect.TypeOf((*MockFullNode)(nil).EthEstimateGas), arg0, arg1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EthFeeHistory mocks base method.
|
||||||
|
func (m *MockFullNode) EthFeeHistory(arg0 context.Context, arg1 uint64, arg2 string) (api.EthFeeHistory, error) {
|
||||||
|
m.ctrl.T.Helper()
|
||||||
|
ret := m.ctrl.Call(m, "EthFeeHistory", arg0, arg1, arg2)
|
||||||
|
ret0, _ := ret[0].(api.EthFeeHistory)
|
||||||
|
ret1, _ := ret[1].(error)
|
||||||
|
return ret0, ret1
|
||||||
|
}
|
||||||
|
|
||||||
|
// EthFeeHistory indicates an expected call of EthFeeHistory.
|
||||||
|
func (mr *MockFullNodeMockRecorder) EthFeeHistory(arg0, arg1, arg2 interface{}) *gomock.Call {
|
||||||
|
mr.mock.ctrl.T.Helper()
|
||||||
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EthFeeHistory", reflect.TypeOf((*MockFullNode)(nil).EthFeeHistory), arg0, arg1, arg2)
|
||||||
|
}
|
||||||
|
|
||||||
// EthGasPrice mocks base method.
|
// EthGasPrice mocks base method.
|
||||||
func (m *MockFullNode) EthGasPrice(arg0 context.Context) (api.EthBigInt, error) {
|
func (m *MockFullNode) EthGasPrice(arg0 context.Context) (api.EthBigInt, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
|
@ -230,6 +230,8 @@ type FullNodeStruct struct {
|
|||||||
|
|
||||||
EthEstimateGas func(p0 context.Context, p1 EthCall) (EthUint64, error) `perm:"read"`
|
EthEstimateGas func(p0 context.Context, p1 EthCall) (EthUint64, error) `perm:"read"`
|
||||||
|
|
||||||
|
EthFeeHistory func(p0 context.Context, p1 uint64, p2 string) (EthFeeHistory, error) `perm:"read"`
|
||||||
|
|
||||||
EthGasPrice func(p0 context.Context) (EthBigInt, error) `perm:"read"`
|
EthGasPrice func(p0 context.Context) (EthBigInt, error) `perm:"read"`
|
||||||
|
|
||||||
EthGetBalance func(p0 context.Context, p1 EthAddress, p2 string) (EthBigInt, error) `perm:"read"`
|
EthGetBalance func(p0 context.Context, p1 EthAddress, p2 string) (EthBigInt, error) `perm:"read"`
|
||||||
@ -1894,6 +1896,17 @@ func (s *FullNodeStub) EthEstimateGas(p0 context.Context, p1 EthCall) (EthUint64
|
|||||||
return *new(EthUint64), ErrNotSupported
|
return *new(EthUint64), ErrNotSupported
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *FullNodeStruct) EthFeeHistory(p0 context.Context, p1 uint64, p2 string) (EthFeeHistory, error) {
|
||||||
|
if s.Internal.EthFeeHistory == nil {
|
||||||
|
return *new(EthFeeHistory), ErrNotSupported
|
||||||
|
}
|
||||||
|
return s.Internal.EthFeeHistory(p0, p1, p2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *FullNodeStub) EthFeeHistory(p0 context.Context, p1 uint64, p2 string) (EthFeeHistory, error) {
|
||||||
|
return *new(EthFeeHistory), ErrNotSupported
|
||||||
|
}
|
||||||
|
|
||||||
func (s *FullNodeStruct) EthGasPrice(p0 context.Context) (EthBigInt, error) {
|
func (s *FullNodeStruct) EthGasPrice(p0 context.Context) (EthBigInt, error) {
|
||||||
if s.Internal.EthGasPrice == nil {
|
if s.Internal.EthGasPrice == nil {
|
||||||
return *new(EthBigInt), ErrNotSupported
|
return *new(EthBigInt), ErrNotSupported
|
||||||
|
@ -71,6 +71,7 @@
|
|||||||
* [EthCall](#EthCall)
|
* [EthCall](#EthCall)
|
||||||
* [EthChainId](#EthChainId)
|
* [EthChainId](#EthChainId)
|
||||||
* [EthEstimateGas](#EthEstimateGas)
|
* [EthEstimateGas](#EthEstimateGas)
|
||||||
|
* [EthFeeHistory](#EthFeeHistory)
|
||||||
* [EthGasPrice](#EthGasPrice)
|
* [EthGasPrice](#EthGasPrice)
|
||||||
* [EthGetBalance](#EthGetBalance)
|
* [EthGetBalance](#EthGetBalance)
|
||||||
* [EthGetBlockByHash](#EthGetBlockByHash)
|
* [EthGetBlockByHash](#EthGetBlockByHash)
|
||||||
@ -2228,6 +2229,33 @@ Inputs:
|
|||||||
|
|
||||||
Response: `"0x5"`
|
Response: `"0x5"`
|
||||||
|
|
||||||
|
### EthFeeHistory
|
||||||
|
|
||||||
|
|
||||||
|
Perms: read
|
||||||
|
|
||||||
|
Inputs:
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
42,
|
||||||
|
"string value"
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
Response:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"oldestBlock": 42,
|
||||||
|
"baseFeePerGas": [
|
||||||
|
"0x0"
|
||||||
|
],
|
||||||
|
"gasUsedRatio": [
|
||||||
|
12.3
|
||||||
|
],
|
||||||
|
"reward": []
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### EthGasPrice
|
### EthGasPrice
|
||||||
|
|
||||||
|
|
||||||
|
@ -45,6 +45,7 @@ type EthModuleAPI interface {
|
|||||||
EthGetCode(ctx context.Context, address api.EthAddress, blkOpt string) (api.EthBytes, error)
|
EthGetCode(ctx context.Context, address api.EthAddress, blkOpt string) (api.EthBytes, error)
|
||||||
EthGetStorageAt(ctx context.Context, address api.EthAddress, position api.EthBytes, blkParam string) (api.EthBytes, error)
|
EthGetStorageAt(ctx context.Context, address api.EthAddress, position api.EthBytes, blkParam string) (api.EthBytes, error)
|
||||||
EthGetBalance(ctx context.Context, address api.EthAddress, blkParam string) (api.EthBigInt, error)
|
EthGetBalance(ctx context.Context, address api.EthAddress, blkParam string) (api.EthBigInt, error)
|
||||||
|
EthFeeHistory(ctx context.Context, blkCount uint64, newestBlk string) (api.EthFeeHistory, error)
|
||||||
EthChainId(ctx context.Context) (api.EthUint64, error)
|
EthChainId(ctx context.Context) (api.EthUint64, error)
|
||||||
NetVersion(ctx context.Context) (string, error)
|
NetVersion(ctx context.Context) (string, error)
|
||||||
NetListening(ctx context.Context) (bool, error)
|
NetListening(ctx context.Context) (bool, error)
|
||||||
@ -375,6 +376,73 @@ func (a *EthModule) EthChainId(ctx context.Context) (api.EthUint64, error) {
|
|||||||
return api.EthUint64(build.Eip155ChainId), nil
|
return api.EthUint64(build.Eip155ChainId), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *EthModule) EthFeeHistory(ctx context.Context, blkCount uint64, newestBlkNum string) (api.EthFeeHistory, error) {
|
||||||
|
if blkCount > 1024 {
|
||||||
|
return api.EthFeeHistory{}, fmt.Errorf("block count should be smaller than 1024")
|
||||||
|
}
|
||||||
|
|
||||||
|
newestBlkHeight := uint64(a.Chain.GetHeaviestTipSet().Height())
|
||||||
|
|
||||||
|
// TODO https://github.com/filecoin-project/ref-fvm/issues/1016
|
||||||
|
var blkNum api.EthUint64
|
||||||
|
err := blkNum.UnmarshalJSON([]byte(`"` + newestBlkNum + `"`))
|
||||||
|
if err == nil && uint64(blkNum) < newestBlkHeight {
|
||||||
|
newestBlkHeight = uint64(blkNum)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deal with the case that the chain is shorter than the number of
|
||||||
|
// requested blocks.
|
||||||
|
oldestBlkHeight := uint64(1)
|
||||||
|
if blkCount <= newestBlkHeight {
|
||||||
|
oldestBlkHeight = newestBlkHeight - blkCount + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
ts, err := a.Chain.GetTipsetByHeight(ctx, abi.ChainEpoch(newestBlkHeight), nil, false)
|
||||||
|
if err != nil {
|
||||||
|
return api.EthFeeHistory{}, fmt.Errorf("cannot load find block height: %v", newestBlkHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: baseFeePerGas should include the next block after the newest of the returned range, because this
|
||||||
|
// can be inferred from the newest block. we use the newest block's baseFeePerGas for now but need to fix it
|
||||||
|
// In other words, due to deferred execution, we might not be returning the most useful value here for the client.
|
||||||
|
baseFeeArray := []api.EthBigInt{api.EthBigInt(ts.Blocks()[0].ParentBaseFee)}
|
||||||
|
gasUsedRatioArray := []float64{}
|
||||||
|
|
||||||
|
for ts.Height() >= abi.ChainEpoch(oldestBlkHeight) {
|
||||||
|
// Unfortunately we need to rebuild the full message view so we can
|
||||||
|
// totalize gas used in the tipset.
|
||||||
|
block, err := a.ethBlockFromFilecoinTipSet(ctx, ts, false)
|
||||||
|
if err != nil {
|
||||||
|
return api.EthFeeHistory{}, fmt.Errorf("cannot create eth block: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// both arrays should be reversed at the end
|
||||||
|
baseFeeArray = append(baseFeeArray, api.EthBigInt(ts.Blocks()[0].ParentBaseFee))
|
||||||
|
gasUsedRatioArray = append(gasUsedRatioArray, float64(block.GasUsed)/float64(build.BlockGasLimit))
|
||||||
|
|
||||||
|
parentTsKey := ts.Parents()
|
||||||
|
ts, err = a.Chain.LoadTipSet(ctx, parentTsKey)
|
||||||
|
if err != nil {
|
||||||
|
return api.EthFeeHistory{}, fmt.Errorf("cannot load tipset key: %v", parentTsKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reverse the arrays; we collected them newest to oldest; the client expects oldest to newest.
|
||||||
|
|
||||||
|
for i, j := 0, len(baseFeeArray)-1; i < j; i, j = i+1, j-1 {
|
||||||
|
baseFeeArray[i], baseFeeArray[j] = baseFeeArray[j], baseFeeArray[i]
|
||||||
|
}
|
||||||
|
for i, j := 0, len(gasUsedRatioArray)-1; i < j; i, j = i+1, j-1 {
|
||||||
|
gasUsedRatioArray[i], gasUsedRatioArray[j] = gasUsedRatioArray[j], gasUsedRatioArray[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
return api.EthFeeHistory{
|
||||||
|
OldestBlock: oldestBlkHeight,
|
||||||
|
BaseFeePerGas: baseFeeArray,
|
||||||
|
GasUsedRatio: gasUsedRatioArray,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (a *EthModule) NetVersion(ctx context.Context) (string, error) {
|
func (a *EthModule) NetVersion(ctx context.Context) (string, error) {
|
||||||
// Note that networkId is not encoded in hex
|
// Note that networkId is not encoded in hex
|
||||||
nv, err := a.StateNetworkVersion(ctx, types.EmptyTSK)
|
nv, err := a.StateNetworkVersion(ctx, types.EmptyTSK)
|
||||||
|
@ -91,6 +91,7 @@ func FullNodeHandler(a v1api.FullNode, permissioned bool, opts ...jsonrpc.Server
|
|||||||
rpcServer.AliasMethod("eth_getStorageAt", "Filecoin.EthGetStorageAt")
|
rpcServer.AliasMethod("eth_getStorageAt", "Filecoin.EthGetStorageAt")
|
||||||
rpcServer.AliasMethod("eth_getBalance", "Filecoin.EthGetBalance")
|
rpcServer.AliasMethod("eth_getBalance", "Filecoin.EthGetBalance")
|
||||||
rpcServer.AliasMethod("eth_chainId", "Filecoin.EthChainId")
|
rpcServer.AliasMethod("eth_chainId", "Filecoin.EthChainId")
|
||||||
|
rpcServer.AliasMethod("eth_feeHistory", "Filecoin.EthFeeHistory")
|
||||||
rpcServer.AliasMethod("eth_protocolVersion", "Filecoin.EthProtocolVersion")
|
rpcServer.AliasMethod("eth_protocolVersion", "Filecoin.EthProtocolVersion")
|
||||||
rpcServer.AliasMethod("eth_maxPriorityFeePerGas", "Filecoin.EthMaxPriorityFeePerGas")
|
rpcServer.AliasMethod("eth_maxPriorityFeePerGas", "Filecoin.EthMaxPriorityFeePerGas")
|
||||||
rpcServer.AliasMethod("eth_gasPrice", "Filecoin.EthGasPrice")
|
rpcServer.AliasMethod("eth_gasPrice", "Filecoin.EthGasPrice")
|
||||||
|
Loading…
Reference in New Issue
Block a user