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
|
||||
EthProtocolVersion(ctx context.Context) (EthUint64, 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
|
||||
EthEstimateGas(ctx context.Context, tx EthCall) (EthUint64, error) //perm:read
|
||||
|
@ -372,6 +372,8 @@ func init() {
|
||||
ethhash, _ := api.EthHashFromCid(c)
|
||||
addExample(ðhash)
|
||||
|
||||
ethFeeHistoryReward := [][]api.EthBigInt{}
|
||||
addExample(ðFeeHistoryReward)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
// 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.
|
||||
func (m *MockFullNode) EthGasPrice(arg0 context.Context) (api.EthBigInt, error) {
|
||||
m.ctrl.T.Helper()
|
||||
|
@ -230,6 +230,8 @@ type FullNodeStruct struct {
|
||||
|
||||
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"`
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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) {
|
||||
if s.Internal.EthGasPrice == nil {
|
||||
return *new(EthBigInt), ErrNotSupported
|
||||
|
@ -71,6 +71,7 @@
|
||||
* [EthCall](#EthCall)
|
||||
* [EthChainId](#EthChainId)
|
||||
* [EthEstimateGas](#EthEstimateGas)
|
||||
* [EthFeeHistory](#EthFeeHistory)
|
||||
* [EthGasPrice](#EthGasPrice)
|
||||
* [EthGetBalance](#EthGetBalance)
|
||||
* [EthGetBlockByHash](#EthGetBlockByHash)
|
||||
@ -2228,6 +2229,33 @@ Inputs:
|
||||
|
||||
Response: `"0x5"`
|
||||
|
||||
### EthFeeHistory
|
||||
|
||||
|
||||
Perms: read
|
||||
|
||||
Inputs:
|
||||
```json
|
||||
[
|
||||
42,
|
||||
"string value"
|
||||
]
|
||||
```
|
||||
|
||||
Response:
|
||||
```json
|
||||
{
|
||||
"oldestBlock": 42,
|
||||
"baseFeePerGas": [
|
||||
"0x0"
|
||||
],
|
||||
"gasUsedRatio": [
|
||||
12.3
|
||||
],
|
||||
"reward": []
|
||||
}
|
||||
```
|
||||
|
||||
### EthGasPrice
|
||||
|
||||
|
||||
|
@ -45,6 +45,7 @@ type EthModuleAPI interface {
|
||||
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)
|
||||
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)
|
||||
NetVersion(ctx context.Context) (string, 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
|
||||
}
|
||||
|
||||
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) {
|
||||
// Note that networkId is not encoded in hex
|
||||
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_getBalance", "Filecoin.EthGetBalance")
|
||||
rpcServer.AliasMethod("eth_chainId", "Filecoin.EthChainId")
|
||||
rpcServer.AliasMethod("eth_feeHistory", "Filecoin.EthFeeHistory")
|
||||
rpcServer.AliasMethod("eth_protocolVersion", "Filecoin.EthProtocolVersion")
|
||||
rpcServer.AliasMethod("eth_maxPriorityFeePerGas", "Filecoin.EthMaxPriorityFeePerGas")
|
||||
rpcServer.AliasMethod("eth_gasPrice", "Filecoin.EthGasPrice")
|
||||
|
Loading…
Reference in New Issue
Block a user