From f358160cd5e1968b480c361db75592fe64dc422f Mon Sep 17 00:00:00 2001 From: Fridrik Asmundsson Date: Wed, 3 May 2023 17:24:36 +0000 Subject: [PATCH] Add EIP-1898 support needed for The Graph compatibility Fixes: #10814 This PR updates the following RPC methods according to EIP-1898 specs. The following RPC methods are affected: - eth_getBalance - eth_getStorageAt - eth_getTransactionCount - eth_getCode - eth_call Note that eth_getBlockByNumber was not included in this list in the spec although it seems it should be affected also? Currently these methods all accept a blkParam string which can be one of "latest", "earliest", "pending", or a block number (decimal or hex). The spec enables caller to additionally specify a json hash which can include the following fields: - blockNumber EthUint64: A block number (decimal or hex) which is similar to the original use of the blkParam string - blockHash EthHash: The block hash - requireCanonical bool) If true we should make sure the block is in the canonical chain Since the blkParam needs to support both being a number/string and a json hash then this to properly work we need to introduce a new struct with pointer fields to check if they exist. This is done in the EthBlockParamByNumberOrHash struct which first tries to unmarshal as a json hash (according to eip-1898) and then fallback to unmarshal as string/number. --- api/api_full.go | 48 +++++----- api/api_gateway.go | 10 +- api/docgen/docgen.go | 3 + api/mocks/mock_full.go | 10 +- api/proxy_gen.go | 60 ++++++------ build/openrpc/full.json.gz | Bin 33937 -> 34022 bytes build/openrpc/gateway.json.gz | Bin 10386 -> 10477 bytes chain/types/ethtypes/eth_types.go | 79 +++++++++++++++ cli/evm.go | 4 +- documentation/en/api-v1-unstable-methods.md | 40 +++++++- gateway/node.go | 10 +- gateway/proxy_eth.go | 56 ++++++++--- itests/eth_balance_test.go | 15 ++- itests/eth_bytecode_test.go | 16 ++-- itests/eth_conformance_test.go | 22 +++-- itests/eth_transactions_test.go | 2 +- itests/fevm_test.go | 8 +- itests/kit/evm.go | 2 +- node/impl/full/dummy.go | 10 +- node/impl/full/eth.go | 101 ++++++++++++++++---- 20 files changed, 355 insertions(+), 141 deletions(-) diff --git a/api/api_full.go b/api/api_full.go index af62c3b0c..cbf2b7435 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -796,32 +796,32 @@ type FullNode interface { // EthGetBlockTransactionCountByHash returns the number of messages in the TipSet EthGetBlockTransactionCountByHash(ctx context.Context, blkHash ethtypes.EthHash) (ethtypes.EthUint64, error) //perm:read - EthGetBlockByHash(ctx context.Context, blkHash ethtypes.EthHash, fullTxInfo bool) (ethtypes.EthBlock, error) //perm:read - EthGetBlockByNumber(ctx context.Context, blkNum string, fullTxInfo bool) (ethtypes.EthBlock, error) //perm:read - EthGetTransactionByHash(ctx context.Context, txHash *ethtypes.EthHash) (*ethtypes.EthTx, error) //perm:read - EthGetTransactionByHashLimited(ctx context.Context, txHash *ethtypes.EthHash, limit abi.ChainEpoch) (*ethtypes.EthTx, error) //perm:read - EthGetTransactionHashByCid(ctx context.Context, cid cid.Cid) (*ethtypes.EthHash, error) //perm:read - EthGetMessageCidByTransactionHash(ctx context.Context, txHash *ethtypes.EthHash) (*cid.Cid, error) //perm:read - EthGetTransactionCount(ctx context.Context, sender ethtypes.EthAddress, blkOpt string) (ethtypes.EthUint64, error) //perm:read - EthGetTransactionReceipt(ctx context.Context, txHash ethtypes.EthHash) (*EthTxReceipt, error) //perm:read - EthGetTransactionReceiptLimited(ctx context.Context, txHash ethtypes.EthHash, limit abi.ChainEpoch) (*EthTxReceipt, error) //perm:read - EthGetTransactionByBlockHashAndIndex(ctx context.Context, blkHash ethtypes.EthHash, txIndex ethtypes.EthUint64) (ethtypes.EthTx, error) //perm:read - EthGetTransactionByBlockNumberAndIndex(ctx context.Context, blkNum ethtypes.EthUint64, txIndex ethtypes.EthUint64) (ethtypes.EthTx, error) //perm:read + EthGetBlockByHash(ctx context.Context, blkHash ethtypes.EthHash, fullTxInfo bool) (ethtypes.EthBlock, error) //perm:read + EthGetBlockByNumber(ctx context.Context, blkNum string, fullTxInfo bool) (ethtypes.EthBlock, error) //perm:read + EthGetTransactionByHash(ctx context.Context, txHash *ethtypes.EthHash) (*ethtypes.EthTx, error) //perm:read + EthGetTransactionByHashLimited(ctx context.Context, txHash *ethtypes.EthHash, limit abi.ChainEpoch) (*ethtypes.EthTx, error) //perm:read + EthGetTransactionHashByCid(ctx context.Context, cid cid.Cid) (*ethtypes.EthHash, error) //perm:read + EthGetMessageCidByTransactionHash(ctx context.Context, txHash *ethtypes.EthHash) (*cid.Cid, error) //perm:read + EthGetTransactionCount(ctx context.Context, sender ethtypes.EthAddress, blkParam ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthUint64, error) //perm:read + EthGetTransactionReceipt(ctx context.Context, txHash ethtypes.EthHash) (*EthTxReceipt, error) //perm:read + EthGetTransactionReceiptLimited(ctx context.Context, txHash ethtypes.EthHash, limit abi.ChainEpoch) (*EthTxReceipt, error) //perm:read + EthGetTransactionByBlockHashAndIndex(ctx context.Context, blkHash ethtypes.EthHash, txIndex ethtypes.EthUint64) (ethtypes.EthTx, error) //perm:read + EthGetTransactionByBlockNumberAndIndex(ctx context.Context, blkNum ethtypes.EthUint64, txIndex ethtypes.EthUint64) (ethtypes.EthTx, error) //perm:read - EthGetCode(ctx context.Context, address ethtypes.EthAddress, blkOpt string) (ethtypes.EthBytes, error) //perm:read - EthGetStorageAt(ctx context.Context, address ethtypes.EthAddress, position ethtypes.EthBytes, blkParam string) (ethtypes.EthBytes, error) //perm:read - EthGetBalance(ctx context.Context, address ethtypes.EthAddress, blkParam string) (ethtypes.EthBigInt, error) //perm:read - EthChainId(ctx context.Context) (ethtypes.EthUint64, error) //perm:read - EthSyncing(ctx context.Context) (ethtypes.EthSyncingResult, error) //perm:read - NetVersion(ctx context.Context) (string, error) //perm:read - NetListening(ctx context.Context) (bool, error) //perm:read - EthProtocolVersion(ctx context.Context) (ethtypes.EthUint64, error) //perm:read - EthGasPrice(ctx context.Context) (ethtypes.EthBigInt, error) //perm:read - EthFeeHistory(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthFeeHistory, error) //perm:read + EthGetCode(ctx context.Context, address ethtypes.EthAddress, blkParam ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBytes, error) //perm:read + EthGetStorageAt(ctx context.Context, address ethtypes.EthAddress, position ethtypes.EthBytes, blkParam ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBytes, error) //perm:read + EthGetBalance(ctx context.Context, address ethtypes.EthAddress, blkParam ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBigInt, error) //perm:read + EthChainId(ctx context.Context) (ethtypes.EthUint64, error) //perm:read + EthSyncing(ctx context.Context) (ethtypes.EthSyncingResult, error) //perm:read + NetVersion(ctx context.Context) (string, error) //perm:read + NetListening(ctx context.Context) (bool, error) //perm:read + EthProtocolVersion(ctx context.Context) (ethtypes.EthUint64, error) //perm:read + EthGasPrice(ctx context.Context) (ethtypes.EthBigInt, error) //perm:read + EthFeeHistory(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthFeeHistory, error) //perm:read - EthMaxPriorityFeePerGas(ctx context.Context) (ethtypes.EthBigInt, error) //perm:read - EthEstimateGas(ctx context.Context, tx ethtypes.EthCall) (ethtypes.EthUint64, error) //perm:read - EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam string) (ethtypes.EthBytes, error) //perm:read + EthMaxPriorityFeePerGas(ctx context.Context) (ethtypes.EthBigInt, error) //perm:read + EthEstimateGas(ctx context.Context, tx ethtypes.EthCall) (ethtypes.EthUint64, error) //perm:read + EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBytes, error) //perm:read EthSendRawTransaction(ctx context.Context, rawTx ethtypes.EthBytes) (ethtypes.EthHash, error) //perm:read diff --git a/api/api_gateway.go b/api/api_gateway.go index 97116d345..767918b10 100644 --- a/api/api_gateway.go +++ b/api/api_gateway.go @@ -94,12 +94,12 @@ type Gateway interface { EthGetTransactionByHashLimited(ctx context.Context, txHash *ethtypes.EthHash, limit abi.ChainEpoch) (*ethtypes.EthTx, error) EthGetTransactionHashByCid(ctx context.Context, cid cid.Cid) (*ethtypes.EthHash, error) EthGetMessageCidByTransactionHash(ctx context.Context, txHash *ethtypes.EthHash) (*cid.Cid, error) - EthGetTransactionCount(ctx context.Context, sender ethtypes.EthAddress, blkOpt string) (ethtypes.EthUint64, error) + EthGetTransactionCount(ctx context.Context, sender ethtypes.EthAddress, blkParam ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthUint64, error) EthGetTransactionReceipt(ctx context.Context, txHash ethtypes.EthHash) (*EthTxReceipt, error) EthGetTransactionReceiptLimited(ctx context.Context, txHash ethtypes.EthHash, limit abi.ChainEpoch) (*EthTxReceipt, error) - EthGetCode(ctx context.Context, address ethtypes.EthAddress, blkOpt string) (ethtypes.EthBytes, error) - EthGetStorageAt(ctx context.Context, address ethtypes.EthAddress, position ethtypes.EthBytes, blkParam string) (ethtypes.EthBytes, error) - EthGetBalance(ctx context.Context, address ethtypes.EthAddress, blkParam string) (ethtypes.EthBigInt, error) + EthGetCode(ctx context.Context, address ethtypes.EthAddress, blkParam ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBytes, error) + EthGetStorageAt(ctx context.Context, address ethtypes.EthAddress, position ethtypes.EthBytes, blkParam ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBytes, error) + EthGetBalance(ctx context.Context, address ethtypes.EthAddress, blkParam ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBigInt, error) EthChainId(ctx context.Context) (ethtypes.EthUint64, error) EthSyncing(ctx context.Context) (ethtypes.EthSyncingResult, error) NetVersion(ctx context.Context) (string, error) @@ -109,7 +109,7 @@ type Gateway interface { EthFeeHistory(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthFeeHistory, error) EthMaxPriorityFeePerGas(ctx context.Context) (ethtypes.EthBigInt, error) EthEstimateGas(ctx context.Context, tx ethtypes.EthCall) (ethtypes.EthUint64, error) - EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam string) (ethtypes.EthBytes, error) + EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBytes, error) EthSendRawTransaction(ctx context.Context, rawTx ethtypes.EthBytes) (ethtypes.EthHash, error) EthGetLogs(ctx context.Context, filter *ethtypes.EthFilterSpec) (*ethtypes.EthFilterResult, error) EthGetFilterChanges(ctx context.Context, id ethtypes.EthFilterID) (*ethtypes.EthFilterResult, error) diff --git a/api/docgen/docgen.go b/api/docgen/docgen.go index 5c1d43129..018629600 100644 --- a/api/docgen/docgen.go +++ b/api/docgen/docgen.go @@ -480,6 +480,9 @@ func ExampleValue(method string, t, parent reflect.Type) interface{} { es := exampleStruct(method, t.Elem(), t) ExampleValues[t] = es return es + } else if t.Elem().Kind() == reflect.String { + str := "string value" + return &str } case reflect.Interface: return struct{}{} diff --git a/api/mocks/mock_full.go b/api/mocks/mock_full.go index 0b0e3ca4c..62ba66ccf 100644 --- a/api/mocks/mock_full.go +++ b/api/mocks/mock_full.go @@ -1012,7 +1012,7 @@ func (mr *MockFullNodeMockRecorder) EthBlockNumber(arg0 interface{}) *gomock.Cal } // EthCall mocks base method. -func (m *MockFullNode) EthCall(arg0 context.Context, arg1 ethtypes.EthCall, arg2 string) (ethtypes.EthBytes, error) { +func (m *MockFullNode) EthCall(arg0 context.Context, arg1 ethtypes.EthCall, arg2 ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBytes, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "EthCall", arg0, arg1, arg2) ret0, _ := ret[0].(ethtypes.EthBytes) @@ -1087,7 +1087,7 @@ func (mr *MockFullNodeMockRecorder) EthGasPrice(arg0 interface{}) *gomock.Call { } // EthGetBalance mocks base method. -func (m *MockFullNode) EthGetBalance(arg0 context.Context, arg1 ethtypes.EthAddress, arg2 string) (ethtypes.EthBigInt, error) { +func (m *MockFullNode) EthGetBalance(arg0 context.Context, arg1 ethtypes.EthAddress, arg2 ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBigInt, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "EthGetBalance", arg0, arg1, arg2) ret0, _ := ret[0].(ethtypes.EthBigInt) @@ -1162,7 +1162,7 @@ func (mr *MockFullNodeMockRecorder) EthGetBlockTransactionCountByNumber(arg0, ar } // EthGetCode mocks base method. -func (m *MockFullNode) EthGetCode(arg0 context.Context, arg1 ethtypes.EthAddress, arg2 string) (ethtypes.EthBytes, error) { +func (m *MockFullNode) EthGetCode(arg0 context.Context, arg1 ethtypes.EthAddress, arg2 ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBytes, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "EthGetCode", arg0, arg1, arg2) ret0, _ := ret[0].(ethtypes.EthBytes) @@ -1237,7 +1237,7 @@ func (mr *MockFullNodeMockRecorder) EthGetMessageCidByTransactionHash(arg0, arg1 } // EthGetStorageAt mocks base method. -func (m *MockFullNode) EthGetStorageAt(arg0 context.Context, arg1 ethtypes.EthAddress, arg2 ethtypes.EthBytes, arg3 string) (ethtypes.EthBytes, error) { +func (m *MockFullNode) EthGetStorageAt(arg0 context.Context, arg1 ethtypes.EthAddress, arg2 ethtypes.EthBytes, arg3 ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBytes, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "EthGetStorageAt", arg0, arg1, arg2, arg3) ret0, _ := ret[0].(ethtypes.EthBytes) @@ -1312,7 +1312,7 @@ func (mr *MockFullNodeMockRecorder) EthGetTransactionByHashLimited(arg0, arg1, a } // EthGetTransactionCount mocks base method. -func (m *MockFullNode) EthGetTransactionCount(arg0 context.Context, arg1 ethtypes.EthAddress, arg2 string) (ethtypes.EthUint64, error) { +func (m *MockFullNode) EthGetTransactionCount(arg0 context.Context, arg1 ethtypes.EthAddress, arg2 ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthUint64, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "EthGetTransactionCount", arg0, arg1, arg2) ret0, _ := ret[0].(ethtypes.EthUint64) diff --git a/api/proxy_gen.go b/api/proxy_gen.go index f2caab266..0f6c38b56 100644 --- a/api/proxy_gen.go +++ b/api/proxy_gen.go @@ -252,7 +252,7 @@ type FullNodeMethods struct { EthBlockNumber func(p0 context.Context) (ethtypes.EthUint64, error) `perm:"read"` - EthCall func(p0 context.Context, p1 ethtypes.EthCall, p2 string) (ethtypes.EthBytes, error) `perm:"read"` + EthCall func(p0 context.Context, p1 ethtypes.EthCall, p2 ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBytes, error) `perm:"read"` EthChainId func(p0 context.Context) (ethtypes.EthUint64, error) `perm:"read"` @@ -262,7 +262,7 @@ type FullNodeMethods struct { EthGasPrice func(p0 context.Context) (ethtypes.EthBigInt, error) `perm:"read"` - EthGetBalance func(p0 context.Context, p1 ethtypes.EthAddress, p2 string) (ethtypes.EthBigInt, error) `perm:"read"` + EthGetBalance func(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBigInt, error) `perm:"read"` EthGetBlockByHash func(p0 context.Context, p1 ethtypes.EthHash, p2 bool) (ethtypes.EthBlock, error) `perm:"read"` @@ -272,7 +272,7 @@ type FullNodeMethods struct { EthGetBlockTransactionCountByNumber func(p0 context.Context, p1 ethtypes.EthUint64) (ethtypes.EthUint64, error) `perm:"read"` - EthGetCode func(p0 context.Context, p1 ethtypes.EthAddress, p2 string) (ethtypes.EthBytes, error) `perm:"read"` + EthGetCode func(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBytes, error) `perm:"read"` EthGetFilterChanges func(p0 context.Context, p1 ethtypes.EthFilterID) (*ethtypes.EthFilterResult, error) `perm:"read"` @@ -282,7 +282,7 @@ type FullNodeMethods struct { EthGetMessageCidByTransactionHash func(p0 context.Context, p1 *ethtypes.EthHash) (*cid.Cid, error) `perm:"read"` - EthGetStorageAt func(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBytes, p3 string) (ethtypes.EthBytes, error) `perm:"read"` + EthGetStorageAt func(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBytes, p3 ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBytes, error) `perm:"read"` EthGetTransactionByBlockHashAndIndex func(p0 context.Context, p1 ethtypes.EthHash, p2 ethtypes.EthUint64) (ethtypes.EthTx, error) `perm:"read"` @@ -292,7 +292,7 @@ type FullNodeMethods struct { EthGetTransactionByHashLimited func(p0 context.Context, p1 *ethtypes.EthHash, p2 abi.ChainEpoch) (*ethtypes.EthTx, error) `perm:"read"` - EthGetTransactionCount func(p0 context.Context, p1 ethtypes.EthAddress, p2 string) (ethtypes.EthUint64, error) `perm:"read"` + EthGetTransactionCount func(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthUint64, error) `perm:"read"` EthGetTransactionHashByCid func(p0 context.Context, p1 cid.Cid) (*ethtypes.EthHash, error) `perm:"read"` @@ -668,7 +668,7 @@ type GatewayMethods struct { EthBlockNumber func(p0 context.Context) (ethtypes.EthUint64, error) `` - EthCall func(p0 context.Context, p1 ethtypes.EthCall, p2 string) (ethtypes.EthBytes, error) `` + EthCall func(p0 context.Context, p1 ethtypes.EthCall, p2 ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBytes, error) `` EthChainId func(p0 context.Context) (ethtypes.EthUint64, error) `` @@ -678,7 +678,7 @@ type GatewayMethods struct { EthGasPrice func(p0 context.Context) (ethtypes.EthBigInt, error) `` - EthGetBalance func(p0 context.Context, p1 ethtypes.EthAddress, p2 string) (ethtypes.EthBigInt, error) `` + EthGetBalance func(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBigInt, error) `` EthGetBlockByHash func(p0 context.Context, p1 ethtypes.EthHash, p2 bool) (ethtypes.EthBlock, error) `` @@ -688,7 +688,7 @@ type GatewayMethods struct { EthGetBlockTransactionCountByNumber func(p0 context.Context, p1 ethtypes.EthUint64) (ethtypes.EthUint64, error) `` - EthGetCode func(p0 context.Context, p1 ethtypes.EthAddress, p2 string) (ethtypes.EthBytes, error) `` + EthGetCode func(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBytes, error) `` EthGetFilterChanges func(p0 context.Context, p1 ethtypes.EthFilterID) (*ethtypes.EthFilterResult, error) `` @@ -698,13 +698,13 @@ type GatewayMethods struct { EthGetMessageCidByTransactionHash func(p0 context.Context, p1 *ethtypes.EthHash) (*cid.Cid, error) `` - EthGetStorageAt func(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBytes, p3 string) (ethtypes.EthBytes, error) `` + EthGetStorageAt func(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBytes, p3 ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBytes, error) `` EthGetTransactionByHash func(p0 context.Context, p1 *ethtypes.EthHash) (*ethtypes.EthTx, error) `` EthGetTransactionByHashLimited func(p0 context.Context, p1 *ethtypes.EthHash, p2 abi.ChainEpoch) (*ethtypes.EthTx, error) `` - EthGetTransactionCount func(p0 context.Context, p1 ethtypes.EthAddress, p2 string) (ethtypes.EthUint64, error) `` + EthGetTransactionCount func(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthUint64, error) `` EthGetTransactionHashByCid func(p0 context.Context, p1 cid.Cid) (*ethtypes.EthHash, error) `` @@ -2091,14 +2091,14 @@ func (s *FullNodeStub) EthBlockNumber(p0 context.Context) (ethtypes.EthUint64, e return *new(ethtypes.EthUint64), ErrNotSupported } -func (s *FullNodeStruct) EthCall(p0 context.Context, p1 ethtypes.EthCall, p2 string) (ethtypes.EthBytes, error) { +func (s *FullNodeStruct) EthCall(p0 context.Context, p1 ethtypes.EthCall, p2 ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBytes, error) { if s.Internal.EthCall == nil { return *new(ethtypes.EthBytes), ErrNotSupported } return s.Internal.EthCall(p0, p1, p2) } -func (s *FullNodeStub) EthCall(p0 context.Context, p1 ethtypes.EthCall, p2 string) (ethtypes.EthBytes, error) { +func (s *FullNodeStub) EthCall(p0 context.Context, p1 ethtypes.EthCall, p2 ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBytes, error) { return *new(ethtypes.EthBytes), ErrNotSupported } @@ -2146,14 +2146,14 @@ func (s *FullNodeStub) EthGasPrice(p0 context.Context) (ethtypes.EthBigInt, erro return *new(ethtypes.EthBigInt), ErrNotSupported } -func (s *FullNodeStruct) EthGetBalance(p0 context.Context, p1 ethtypes.EthAddress, p2 string) (ethtypes.EthBigInt, error) { +func (s *FullNodeStruct) EthGetBalance(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBigInt, error) { if s.Internal.EthGetBalance == nil { return *new(ethtypes.EthBigInt), ErrNotSupported } return s.Internal.EthGetBalance(p0, p1, p2) } -func (s *FullNodeStub) EthGetBalance(p0 context.Context, p1 ethtypes.EthAddress, p2 string) (ethtypes.EthBigInt, error) { +func (s *FullNodeStub) EthGetBalance(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBigInt, error) { return *new(ethtypes.EthBigInt), ErrNotSupported } @@ -2201,14 +2201,14 @@ func (s *FullNodeStub) EthGetBlockTransactionCountByNumber(p0 context.Context, p return *new(ethtypes.EthUint64), ErrNotSupported } -func (s *FullNodeStruct) EthGetCode(p0 context.Context, p1 ethtypes.EthAddress, p2 string) (ethtypes.EthBytes, error) { +func (s *FullNodeStruct) EthGetCode(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBytes, error) { if s.Internal.EthGetCode == nil { return *new(ethtypes.EthBytes), ErrNotSupported } return s.Internal.EthGetCode(p0, p1, p2) } -func (s *FullNodeStub) EthGetCode(p0 context.Context, p1 ethtypes.EthAddress, p2 string) (ethtypes.EthBytes, error) { +func (s *FullNodeStub) EthGetCode(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBytes, error) { return *new(ethtypes.EthBytes), ErrNotSupported } @@ -2256,14 +2256,14 @@ func (s *FullNodeStub) EthGetMessageCidByTransactionHash(p0 context.Context, p1 return nil, ErrNotSupported } -func (s *FullNodeStruct) EthGetStorageAt(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBytes, p3 string) (ethtypes.EthBytes, error) { +func (s *FullNodeStruct) EthGetStorageAt(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBytes, p3 ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBytes, error) { if s.Internal.EthGetStorageAt == nil { return *new(ethtypes.EthBytes), ErrNotSupported } return s.Internal.EthGetStorageAt(p0, p1, p2, p3) } -func (s *FullNodeStub) EthGetStorageAt(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBytes, p3 string) (ethtypes.EthBytes, error) { +func (s *FullNodeStub) EthGetStorageAt(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBytes, p3 ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBytes, error) { return *new(ethtypes.EthBytes), ErrNotSupported } @@ -2311,14 +2311,14 @@ func (s *FullNodeStub) EthGetTransactionByHashLimited(p0 context.Context, p1 *et return nil, ErrNotSupported } -func (s *FullNodeStruct) EthGetTransactionCount(p0 context.Context, p1 ethtypes.EthAddress, p2 string) (ethtypes.EthUint64, error) { +func (s *FullNodeStruct) EthGetTransactionCount(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthUint64, error) { if s.Internal.EthGetTransactionCount == nil { return *new(ethtypes.EthUint64), ErrNotSupported } return s.Internal.EthGetTransactionCount(p0, p1, p2) } -func (s *FullNodeStub) EthGetTransactionCount(p0 context.Context, p1 ethtypes.EthAddress, p2 string) (ethtypes.EthUint64, error) { +func (s *FullNodeStub) EthGetTransactionCount(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthUint64, error) { return *new(ethtypes.EthUint64), ErrNotSupported } @@ -4313,14 +4313,14 @@ func (s *GatewayStub) EthBlockNumber(p0 context.Context) (ethtypes.EthUint64, er return *new(ethtypes.EthUint64), ErrNotSupported } -func (s *GatewayStruct) EthCall(p0 context.Context, p1 ethtypes.EthCall, p2 string) (ethtypes.EthBytes, error) { +func (s *GatewayStruct) EthCall(p0 context.Context, p1 ethtypes.EthCall, p2 ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBytes, error) { if s.Internal.EthCall == nil { return *new(ethtypes.EthBytes), ErrNotSupported } return s.Internal.EthCall(p0, p1, p2) } -func (s *GatewayStub) EthCall(p0 context.Context, p1 ethtypes.EthCall, p2 string) (ethtypes.EthBytes, error) { +func (s *GatewayStub) EthCall(p0 context.Context, p1 ethtypes.EthCall, p2 ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBytes, error) { return *new(ethtypes.EthBytes), ErrNotSupported } @@ -4368,14 +4368,14 @@ func (s *GatewayStub) EthGasPrice(p0 context.Context) (ethtypes.EthBigInt, error return *new(ethtypes.EthBigInt), ErrNotSupported } -func (s *GatewayStruct) EthGetBalance(p0 context.Context, p1 ethtypes.EthAddress, p2 string) (ethtypes.EthBigInt, error) { +func (s *GatewayStruct) EthGetBalance(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBigInt, error) { if s.Internal.EthGetBalance == nil { return *new(ethtypes.EthBigInt), ErrNotSupported } return s.Internal.EthGetBalance(p0, p1, p2) } -func (s *GatewayStub) EthGetBalance(p0 context.Context, p1 ethtypes.EthAddress, p2 string) (ethtypes.EthBigInt, error) { +func (s *GatewayStub) EthGetBalance(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBigInt, error) { return *new(ethtypes.EthBigInt), ErrNotSupported } @@ -4423,14 +4423,14 @@ func (s *GatewayStub) EthGetBlockTransactionCountByNumber(p0 context.Context, p1 return *new(ethtypes.EthUint64), ErrNotSupported } -func (s *GatewayStruct) EthGetCode(p0 context.Context, p1 ethtypes.EthAddress, p2 string) (ethtypes.EthBytes, error) { +func (s *GatewayStruct) EthGetCode(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBytes, error) { if s.Internal.EthGetCode == nil { return *new(ethtypes.EthBytes), ErrNotSupported } return s.Internal.EthGetCode(p0, p1, p2) } -func (s *GatewayStub) EthGetCode(p0 context.Context, p1 ethtypes.EthAddress, p2 string) (ethtypes.EthBytes, error) { +func (s *GatewayStub) EthGetCode(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBytes, error) { return *new(ethtypes.EthBytes), ErrNotSupported } @@ -4478,14 +4478,14 @@ func (s *GatewayStub) EthGetMessageCidByTransactionHash(p0 context.Context, p1 * return nil, ErrNotSupported } -func (s *GatewayStruct) EthGetStorageAt(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBytes, p3 string) (ethtypes.EthBytes, error) { +func (s *GatewayStruct) EthGetStorageAt(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBytes, p3 ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBytes, error) { if s.Internal.EthGetStorageAt == nil { return *new(ethtypes.EthBytes), ErrNotSupported } return s.Internal.EthGetStorageAt(p0, p1, p2, p3) } -func (s *GatewayStub) EthGetStorageAt(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBytes, p3 string) (ethtypes.EthBytes, error) { +func (s *GatewayStub) EthGetStorageAt(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBytes, p3 ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBytes, error) { return *new(ethtypes.EthBytes), ErrNotSupported } @@ -4511,14 +4511,14 @@ func (s *GatewayStub) EthGetTransactionByHashLimited(p0 context.Context, p1 *eth return nil, ErrNotSupported } -func (s *GatewayStruct) EthGetTransactionCount(p0 context.Context, p1 ethtypes.EthAddress, p2 string) (ethtypes.EthUint64, error) { +func (s *GatewayStruct) EthGetTransactionCount(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthUint64, error) { if s.Internal.EthGetTransactionCount == nil { return *new(ethtypes.EthUint64), ErrNotSupported } return s.Internal.EthGetTransactionCount(p0, p1, p2) } -func (s *GatewayStub) EthGetTransactionCount(p0 context.Context, p1 ethtypes.EthAddress, p2 string) (ethtypes.EthUint64, error) { +func (s *GatewayStub) EthGetTransactionCount(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthUint64, error) { return *new(ethtypes.EthUint64), ErrNotSupported } diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index 57f88c609cd631277593979c36a0fc1dab41ca12..a3891144b50ecb5106ffab8f035c8fbcd4a80184 100644 GIT binary patch literal 34022 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 JZ}cEa0svt9>b(E} diff --git a/chain/types/ethtypes/eth_types.go b/chain/types/ethtypes/eth_types.go index 64f67f662..dcb85fffe 100644 --- a/chain/types/ethtypes/eth_types.go +++ b/chain/types/ethtypes/eth_types.go @@ -839,3 +839,82 @@ func (e EthFeeHistoryParams) MarshalJSON() ([]byte, error) { } return json.Marshal([]interface{}{e.BlkCount, e.NewestBlkNum}) } + +type EthBlockParamByNumberOrHash struct { + PredefinedBlock *string + Number *EthUint64 + + BlockNumber *EthUint64 `json:"blockNumber,omitempty"` + BlockHash *EthHash `json:"blockHash,omitempty"` + RequireCanonical bool `json:"requireCanonical,omitempty"` +} + +func NewEthBlockParamFromPredefined(predefined string) EthBlockParamByNumberOrHash { + return EthBlockParamByNumberOrHash{ + PredefinedBlock: &predefined, + Number: nil, + BlockHash: nil, + RequireCanonical: false, + } +} + +func NewEthBlockParamFromNumber(number EthUint64) EthBlockParamByNumberOrHash { + return EthBlockParamByNumberOrHash{ + PredefinedBlock: nil, + Number: &number, + BlockHash: nil, + RequireCanonical: false, + } +} + +func NewEthBlockParamFromHexString(str string) (EthBlockParamByNumberOrHash, error) { + // check if block param is a number (decimal or hex) + var num EthUint64 = 0 + err := num.UnmarshalJSON([]byte(str)) + if err != nil { + return NewEthBlockParamFromNumber(0), err + } + + return EthBlockParamByNumberOrHash{ + PredefinedBlock: nil, + Number: &num, + BlockHash: nil, + RequireCanonical: false, + }, nil +} + +func (e *EthBlockParamByNumberOrHash) UnmarshalJSON(b []byte) error { + // we first try to unmarshal into a EthBlockParamByNumberOrHash struct to check + // if the block param is a block hash or block number (see EIP-1898). We use + // a temporary struct to avoid infinite recursion. + type tmpStruct EthBlockParamByNumberOrHash + var tmp tmpStruct + if err := json.Unmarshal(b, &tmp); err == nil { + if tmp.BlockNumber != nil && tmp.BlockHash != nil { + return errors.New("cannot specify both blockNumber and blockHash") + } + + *e = EthBlockParamByNumberOrHash(tmp) + return nil + } + + // check if block param is once of the special strings + var str string + err := json.Unmarshal(b, &str) + if err != nil { + return err + } + if str == "earliest" || str == "pending" || str == "latest" { + e.PredefinedBlock = &str + return nil + } + + // check if block param is a number (decimal or hex) + var num EthUint64 + if err := num.UnmarshalJSON(b); err == nil { + e.Number = &num + return nil + } + + return errors.New("invalid block param") +} diff --git a/cli/evm.go b/cli/evm.go index 84cbf8c61..e3dda8647 100644 --- a/cli/evm.go +++ b/cli/evm.go @@ -130,7 +130,7 @@ var EvmCallSimulateCmd = &cli.Command{ From: &fromEthAddr, To: &toEthAddr, Data: params, - }, "") + }, ethtypes.NewEthBlockParamFromPredefined("latest")) if err != nil { fmt.Println("Eth call fails, return val: ", res) return err @@ -518,7 +518,7 @@ var EvmGetBytecode = &cli.Command{ defer closer() ctx := ReqContext(cctx) - code, err := api.EthGetCode(ctx, contractAddr, "latest") + code, err := api.EthGetCode(ctx, contractAddr, ethtypes.NewEthBlockParamFromPredefined("latest")) if err != nil { return err } diff --git a/documentation/en/api-v1-unstable-methods.md b/documentation/en/api-v1-unstable-methods.md index e3c97eecf..a72c57d61 100644 --- a/documentation/en/api-v1-unstable-methods.md +++ b/documentation/en/api-v1-unstable-methods.md @@ -2375,7 +2375,13 @@ Inputs: "value": "0x0", "data": "0x07" }, - "string value" + { + "PredefinedBlock": "string value", + "Number": "0x5", + "blockNumber": "0x5", + "blockHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "requireCanonical": true + } ] ``` @@ -2455,7 +2461,13 @@ Inputs: ```json [ "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", - "string value" + { + "PredefinedBlock": "string value", + "Number": "0x5", + "blockNumber": "0x5", + "blockHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "requireCanonical": true + } ] ``` @@ -2588,7 +2600,13 @@ Inputs: ```json [ "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", - "string value" + { + "PredefinedBlock": "string value", + "Number": "0x5", + "blockNumber": "0x5", + "blockHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "requireCanonical": true + } ] ``` @@ -2691,7 +2709,13 @@ Inputs: [ "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", "0x07", - "string value" + { + "PredefinedBlock": "string value", + "Number": "0x5", + "blockNumber": "0x5", + "blockHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "requireCanonical": true + } ] ``` @@ -2861,7 +2885,13 @@ Inputs: ```json [ "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", - "string value" + { + "PredefinedBlock": "string value", + "Number": "0x5", + "blockNumber": "0x5", + "blockHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", + "requireCanonical": true + } ] ``` diff --git a/gateway/node.go b/gateway/node.go index 6f1dac73a..b8eac8d72 100644 --- a/gateway/node.go +++ b/gateway/node.go @@ -110,13 +110,13 @@ type TargetAPI interface { EthGetTransactionByHashLimited(ctx context.Context, txHash *ethtypes.EthHash, limit abi.ChainEpoch) (*ethtypes.EthTx, error) EthGetTransactionHashByCid(ctx context.Context, cid cid.Cid) (*ethtypes.EthHash, error) EthGetMessageCidByTransactionHash(ctx context.Context, txHash *ethtypes.EthHash) (*cid.Cid, error) - EthGetTransactionCount(ctx context.Context, sender ethtypes.EthAddress, blkOpt string) (ethtypes.EthUint64, error) + EthGetTransactionCount(ctx context.Context, sender ethtypes.EthAddress, blkParam ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthUint64, error) EthGetTransactionReceiptLimited(ctx context.Context, txHash ethtypes.EthHash, limit abi.ChainEpoch) (*api.EthTxReceipt, error) EthGetTransactionByBlockHashAndIndex(ctx context.Context, blkHash ethtypes.EthHash, txIndex ethtypes.EthUint64) (ethtypes.EthTx, error) EthGetTransactionByBlockNumberAndIndex(ctx context.Context, blkNum ethtypes.EthUint64, txIndex ethtypes.EthUint64) (ethtypes.EthTx, error) - EthGetCode(ctx context.Context, address ethtypes.EthAddress, blkOpt string) (ethtypes.EthBytes, error) - EthGetStorageAt(ctx context.Context, address ethtypes.EthAddress, position ethtypes.EthBytes, blkParam string) (ethtypes.EthBytes, error) - EthGetBalance(ctx context.Context, address ethtypes.EthAddress, blkParam string) (ethtypes.EthBigInt, error) + EthGetCode(ctx context.Context, address ethtypes.EthAddress, blkParam ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBytes, error) + EthGetStorageAt(ctx context.Context, address ethtypes.EthAddress, position ethtypes.EthBytes, blkParam ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBytes, error) + EthGetBalance(ctx context.Context, address ethtypes.EthAddress, blkParam ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBigInt, error) EthChainId(ctx context.Context) (ethtypes.EthUint64, error) EthSyncing(ctx context.Context) (ethtypes.EthSyncingResult, error) NetVersion(ctx context.Context) (string, error) @@ -126,7 +126,7 @@ type TargetAPI interface { EthFeeHistory(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthFeeHistory, error) EthMaxPriorityFeePerGas(ctx context.Context) (ethtypes.EthBigInt, error) EthEstimateGas(ctx context.Context, tx ethtypes.EthCall) (ethtypes.EthUint64, error) - EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam string) (ethtypes.EthBytes, error) + EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBytes, error) EthSendRawTransaction(ctx context.Context, rawTx ethtypes.EthBytes) (ethtypes.EthHash, error) EthGetLogs(ctx context.Context, filter *ethtypes.EthFilterSpec) (*ethtypes.EthFilterResult, error) EthGetFilterChanges(ctx context.Context, id ethtypes.EthFilterID) (*ethtypes.EthFilterResult, error) diff --git a/gateway/proxy_eth.go b/gateway/proxy_eth.go index b992c9ea9..275baa935 100644 --- a/gateway/proxy_eth.go +++ b/gateway/proxy_eth.go @@ -80,6 +80,38 @@ func (gw *Node) checkBlkHash(ctx context.Context, blkHash ethtypes.EthHash) erro return gw.checkTipsetKey(ctx, tsk) } +func (gw *Node) checkEthBlockParamByNumberOrHash(ctx context.Context, blkParam ethtypes.EthBlockParamByNumberOrHash, lookback ethtypes.EthUint64) error { + head, err := gw.target.ChainHead(ctx) + if err != nil { + return err + } + + var num ethtypes.EthUint64 = 0 + if blkParam.PredefinedBlock != nil { + if *blkParam.PredefinedBlock == "earliest" { + return fmt.Errorf("block param \"earliest\" is not supported") + } else if *blkParam.PredefinedBlock == "pending" || *blkParam.PredefinedBlock == "latest" { + // Head is always ok. + if lookback == 0 { + return nil + } + + if lookback <= ethtypes.EthUint64(head.Height()) { + num = ethtypes.EthUint64(head.Height()) - lookback + } + } + } else if blkParam.Number != nil { + num = *blkParam.Number + } else if blkParam.BlockHash != nil || blkParam.BlockNumber != nil { + return fmt.Errorf("block hash and block number are not supported") + } else { + return fmt.Errorf("invalid block param") + } + + return gw.checkTipsetHeight(head, abi.ChainEpoch(num)) + +} + func (gw *Node) checkBlkParam(ctx context.Context, blkParam string, lookback ethtypes.EthUint64) error { if blkParam == "earliest" { // also not supported in node impl @@ -178,16 +210,16 @@ func (gw *Node) EthGetMessageCidByTransactionHash(ctx context.Context, txHash *e return gw.target.EthGetMessageCidByTransactionHash(ctx, txHash) } -func (gw *Node) EthGetTransactionCount(ctx context.Context, sender ethtypes.EthAddress, blkOpt string) (ethtypes.EthUint64, error) { +func (gw *Node) EthGetTransactionCount(ctx context.Context, sender ethtypes.EthAddress, blkParam ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthUint64, error) { if err := gw.limit(ctx, stateRateLimitTokens); err != nil { return 0, err } - if err := gw.checkBlkParam(ctx, blkOpt, 0); err != nil { + if err := gw.checkEthBlockParamByNumberOrHash(ctx, blkParam, 0); err != nil { return 0, err } - return gw.target.EthGetTransactionCount(ctx, sender, blkOpt) + return gw.target.EthGetTransactionCount(ctx, sender, blkParam) } func (gw *Node) EthGetTransactionReceipt(ctx context.Context, txHash ethtypes.EthHash) (*api.EthTxReceipt, error) { @@ -208,36 +240,36 @@ func (gw *Node) EthGetTransactionReceiptLimited(ctx context.Context, txHash etht return gw.target.EthGetTransactionReceiptLimited(ctx, txHash, limit) } -func (gw *Node) EthGetCode(ctx context.Context, address ethtypes.EthAddress, blkOpt string) (ethtypes.EthBytes, error) { +func (gw *Node) EthGetCode(ctx context.Context, address ethtypes.EthAddress, blkParam ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBytes, error) { if err := gw.limit(ctx, stateRateLimitTokens); err != nil { return nil, err } - if err := gw.checkBlkParam(ctx, blkOpt, 0); err != nil { + if err := gw.checkEthBlockParamByNumberOrHash(ctx, blkParam, 0); err != nil { return nil, err } - return gw.target.EthGetCode(ctx, address, blkOpt) + return gw.target.EthGetCode(ctx, address, blkParam) } -func (gw *Node) EthGetStorageAt(ctx context.Context, address ethtypes.EthAddress, position ethtypes.EthBytes, blkParam string) (ethtypes.EthBytes, error) { +func (gw *Node) EthGetStorageAt(ctx context.Context, address ethtypes.EthAddress, position ethtypes.EthBytes, blkParam ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBytes, error) { if err := gw.limit(ctx, stateRateLimitTokens); err != nil { return nil, err } - if err := gw.checkBlkParam(ctx, blkParam, 0); err != nil { + if err := gw.checkEthBlockParamByNumberOrHash(ctx, blkParam, 0); err != nil { return nil, err } return gw.target.EthGetStorageAt(ctx, address, position, blkParam) } -func (gw *Node) EthGetBalance(ctx context.Context, address ethtypes.EthAddress, blkParam string) (ethtypes.EthBigInt, error) { +func (gw *Node) EthGetBalance(ctx context.Context, address ethtypes.EthAddress, blkParam ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBigInt, error) { if err := gw.limit(ctx, stateRateLimitTokens); err != nil { return ethtypes.EthBigInt(big.Zero()), err } - if err := gw.checkBlkParam(ctx, blkParam, 0); err != nil { + if err := gw.checkEthBlockParamByNumberOrHash(ctx, blkParam, 0); err != nil { return ethtypes.EthBigInt(big.Zero()), err } @@ -332,12 +364,12 @@ func (gw *Node) EthEstimateGas(ctx context.Context, tx ethtypes.EthCall) (ethtyp return gw.target.EthEstimateGas(ctx, tx) } -func (gw *Node) EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam string) (ethtypes.EthBytes, error) { +func (gw *Node) EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBytes, error) { if err := gw.limit(ctx, stateRateLimitTokens); err != nil { return nil, err } - if err := gw.checkBlkParam(ctx, blkParam, 0); err != nil { + if err := gw.checkEthBlockParamByNumberOrHash(ctx, blkParam, 0); err != nil { return nil, err } diff --git a/itests/eth_balance_test.go b/itests/eth_balance_test.go index 116587902..cd6d1558b 100644 --- a/itests/eth_balance_test.go +++ b/itests/eth_balance_test.go @@ -2,7 +2,6 @@ package itests import ( "context" - "strconv" "testing" "time" @@ -32,7 +31,7 @@ func TestEthGetBalanceExistingF4address(t *testing.T) { // send some funds to the f410 address kit.SendFunds(ctx, t, client, deployer, fundAmount) - balance, err := client.EthGetBalance(ctx, ethAddr, "latest") + balance, err := client.EthGetBalance(ctx, ethAddr, ethtypes.NewEthBlockParamFromPredefined("latest")) require.NoError(t, err) require.Equal(t, balance, ethtypes.EthBigInt{Int: fundAmount.Int}) } @@ -47,7 +46,7 @@ func TestEthGetBalanceNonExistentF4address(t *testing.T) { _, ethAddr, _ := client.EVM().NewAccount() - balance, err := client.EthGetBalance(ctx, ethAddr, "latest") + balance, err := client.EthGetBalance(ctx, ethAddr, ethtypes.NewEthBlockParamFromPredefined("latest")) require.NoError(t, err) require.Equal(t, balance, ethtypes.EthBigIntZero) } @@ -71,7 +70,7 @@ func TestEthGetBalanceExistentIDMaskedAddr(t *testing.T) { balance, err := client.WalletBalance(ctx, fid) require.NoError(t, err) - ebal, err := client.EthGetBalance(ctx, ethAddr, "latest") + ebal, err := client.EthGetBalance(ctx, ethAddr, ethtypes.NewEthBlockParamFromPredefined("latest")) require.NoError(t, err) require.Equal(t, ebal, ethtypes.EthBigInt{Int: balance.Int}) } @@ -93,7 +92,7 @@ func TestEthGetBalanceBuiltinActor(t *testing.T) { ethAddr, err := ethtypes.EthAddressFromFilecoinAddress(fid) require.NoError(t, err) - ebal, err := client.EthGetBalance(ctx, ethAddr, "latest") + ebal, err := client.EthGetBalance(ctx, ethAddr, ethtypes.NewEthBlockParamFromPredefined("latest")) require.NoError(t, err) require.Equal(t, ethtypes.EthBigInt{Int: big.NewInt(10).Int}, ebal) } @@ -130,15 +129,15 @@ func TestEthBalanceCorrectLookup(t *testing.T) { inclTsParents, err := client.ChainGetTipSet(ctx, inclTs.Parents()) require.NoError(t, err) - bal, err := client.EVM().EthGetBalance(ctx, ethAddr, strconv.FormatInt(int64(inclTsParents.Height()), 10)) + bal, err := client.EVM().EthGetBalance(ctx, ethAddr, ethtypes.NewEthBlockParamFromNumber(inclTsParents.Height())) require.NoError(t, err) require.Equal(t, int64(0), bal.Int64()) - bal, err = client.EVM().EthGetBalance(ctx, ethAddr, strconv.FormatInt(int64(inclTs.Height()), 10)) + bal, err = client.EVM().EthGetBalance(ctx, ethAddr, ethtypes.NewEthBlockParamFromNumber(inclTs.Height())) require.NoError(t, err) require.Equal(t, val, bal.Int64()) - bal, err = client.EVM().EthGetBalance(ctx, ethAddr, strconv.FormatInt(int64(execTs.Height()), 10)) + bal, err = client.EVM().EthGetBalance(ctx, ethAddr, ethtypes.NewEthBlockParamFromNumber(execTs.Height())) require.NoError(t, err) require.Equal(t, val, bal.Int64()) } diff --git a/itests/eth_bytecode_test.go b/itests/eth_bytecode_test.go index a8a75e83f..86048ff91 100644 --- a/itests/eth_bytecode_test.go +++ b/itests/eth_bytecode_test.go @@ -33,12 +33,12 @@ func TestGetCodeAndNonce(t *testing.T) { { // A random eth address should have no code. _, ethAddr, filAddr := client.EVM().NewAccount() - bytecode, err := client.EVM().EthGetCode(ctx, ethAddr, "latest") + bytecode, err := client.EVM().EthGetCode(ctx, ethAddr, ethtypes.NewEthBlockParamFromPredefined("latest")) require.NoError(t, err) require.Empty(t, bytecode) // Nonce should also be zero - nonce, err := client.EVM().EthGetTransactionCount(ctx, ethAddr, "latest") + nonce, err := client.EVM().EthGetTransactionCount(ctx, ethAddr, ethtypes.NewEthBlockParamFromPredefined("latest")) require.NoError(t, err) require.Zero(t, nonce) @@ -46,12 +46,12 @@ func TestGetCodeAndNonce(t *testing.T) { kit.SendFunds(ctx, t, client, filAddr, types.FromFil(10)) // The code should still be empty, target is now a placeholder. - bytecode, err = client.EVM().EthGetCode(ctx, ethAddr, "latest") + bytecode, err = client.EVM().EthGetCode(ctx, ethAddr, ethtypes.NewEthBlockParamFromPredefined("latest")) require.NoError(t, err) require.Empty(t, bytecode) // Nonce should still be zero. - nonce, err = client.EVM().EthGetTransactionCount(ctx, ethAddr, "latest") + nonce, err = client.EVM().EthGetTransactionCount(ctx, ethAddr, ethtypes.NewEthBlockParamFromPredefined("latest")) require.NoError(t, err) require.Zero(t, nonce) } @@ -68,12 +68,12 @@ func TestGetCodeAndNonce(t *testing.T) { contractFilAddr := *createReturn.RobustAddress // The newly deployed contract should not be empty. - bytecode, err := client.EVM().EthGetCode(ctx, contractAddr, "latest") + bytecode, err := client.EVM().EthGetCode(ctx, contractAddr, ethtypes.NewEthBlockParamFromPredefined("latest")) require.NoError(t, err) require.NotEmpty(t, bytecode) // Nonce should be one. - nonce, err := client.EVM().EthGetTransactionCount(ctx, contractAddr, "latest") + nonce, err := client.EVM().EthGetTransactionCount(ctx, contractAddr, ethtypes.NewEthBlockParamFromPredefined("latest")) require.NoError(t, err) require.Equal(t, ethtypes.EthUint64(1), nonce) @@ -82,12 +82,12 @@ func TestGetCodeAndNonce(t *testing.T) { require.NoError(t, err) // The code should be empty again. - bytecode, err = client.EVM().EthGetCode(ctx, contractAddr, "latest") + bytecode, err = client.EVM().EthGetCode(ctx, contractAddr, ethtypes.NewEthBlockParamFromPredefined("latest")) require.NoError(t, err) require.Empty(t, bytecode) // Nonce should go back to zero - nonce, err = client.EVM().EthGetTransactionCount(ctx, contractAddr, "latest") + nonce, err = client.EVM().EthGetTransactionCount(ctx, contractAddr, ethtypes.NewEthBlockParamFromPredefined("latest")) require.NoError(t, err) require.Zero(t, nonce) } diff --git a/itests/eth_conformance_test.go b/itests/eth_conformance_test.go index 4d8f5c3dd..2a899e968 100644 --- a/itests/eth_conformance_test.go +++ b/itests/eth_conformance_test.go @@ -33,25 +33,25 @@ import ( type ethAPIRaw struct { EthAccounts func(context.Context) (json.RawMessage, error) EthBlockNumber func(context.Context) (json.RawMessage, error) - EthCall func(context.Context, ethtypes.EthCall, string) (json.RawMessage, error) + EthCall func(context.Context, ethtypes.EthCall, ethtypes.EthBlockParamByNumberOrHash) (json.RawMessage, error) EthChainId func(context.Context) (json.RawMessage, error) EthEstimateGas func(context.Context, ethtypes.EthCall) (json.RawMessage, error) EthFeeHistory func(context.Context, ethtypes.EthUint64, string, []float64) (json.RawMessage, error) EthGasPrice func(context.Context) (json.RawMessage, error) - EthGetBalance func(context.Context, ethtypes.EthAddress, string) (json.RawMessage, error) + EthGetBalance func(context.Context, ethtypes.EthAddress, ethtypes.EthBlockParamByNumberOrHash) (json.RawMessage, error) EthGetBlockByHash func(context.Context, ethtypes.EthHash, bool) (json.RawMessage, error) EthGetBlockByNumber func(context.Context, string, bool) (json.RawMessage, error) EthGetBlockTransactionCountByHash func(context.Context, ethtypes.EthHash) (json.RawMessage, error) EthGetBlockTransactionCountByNumber func(context.Context, ethtypes.EthUint64) (json.RawMessage, error) - EthGetCode func(context.Context, ethtypes.EthAddress, string) (json.RawMessage, error) + EthGetCode func(context.Context, ethtypes.EthAddress, ethtypes.EthBlockParamByNumberOrHash) (json.RawMessage, error) EthGetFilterChanges func(context.Context, ethtypes.EthFilterID) (json.RawMessage, error) EthGetFilterLogs func(context.Context, ethtypes.EthFilterID) (json.RawMessage, error) EthGetLogs func(context.Context, *ethtypes.EthFilterSpec) (json.RawMessage, error) - EthGetStorageAt func(context.Context, ethtypes.EthAddress, ethtypes.EthBytes, string) (json.RawMessage, error) + EthGetStorageAt func(context.Context, ethtypes.EthAddress, ethtypes.EthBytes, ethtypes.EthBlockParamByNumberOrHash) (json.RawMessage, error) EthGetTransactionByBlockHashAndIndex func(context.Context, ethtypes.EthHash, ethtypes.EthUint64) (json.RawMessage, error) EthGetTransactionByBlockNumberAndIndex func(context.Context, ethtypes.EthUint64, ethtypes.EthUint64) (json.RawMessage, error) EthGetTransactionByHash func(context.Context, *ethtypes.EthHash) (json.RawMessage, error) - EthGetTransactionCount func(context.Context, ethtypes.EthAddress, string) (json.RawMessage, error) + EthGetTransactionCount func(context.Context, ethtypes.EthAddress, ethtypes.EthBlockParamByNumberOrHash) (json.RawMessage, error) EthGetTransactionReceipt func(context.Context, ethtypes.EthHash) (json.RawMessage, error) EthMaxPriorityFeePerGas func(context.Context) (json.RawMessage, error) EthNewBlockFilter func(context.Context) (json.RawMessage, error) @@ -168,7 +168,7 @@ func TestEthOpenRPCConformance(t *testing.T) { return ethapi.EthCall(context.Background(), ethtypes.EthCall{ From: &senderEthAddr, Data: contractBin, - }, "latest") + }, ethtypes.NewEthBlockParamFromPredefined("latest")) }, }, @@ -207,7 +207,8 @@ func TestEthOpenRPCConformance(t *testing.T) { method: "eth_getBalance", variant: "blocknumber", call: func(a *ethAPIRaw) (json.RawMessage, error) { - return ethapi.EthGetBalance(context.Background(), contractEthAddr, "0x0") + blockParam, _ := ethtypes.NewEthBlockParamFromHexString("0x0") + return ethapi.EthGetBalance(context.Background(), contractEthAddr, blockParam) }, }, @@ -261,7 +262,7 @@ func TestEthOpenRPCConformance(t *testing.T) { method: "eth_getCode", variant: "blocknumber", call: func(a *ethAPIRaw) (json.RawMessage, error) { - return ethapi.EthGetCode(context.Background(), contractEthAddr, blockNumberWithMessage.Hex()) + return ethapi.EthGetCode(context.Background(), contractEthAddr, ethtypes.NewEthBlockParamFromNumber(blockNumberWithMessage)) }, }, @@ -307,7 +308,8 @@ func TestEthOpenRPCConformance(t *testing.T) { method: "eth_getStorageAt", variant: "blocknumber", call: func(a *ethAPIRaw) (json.RawMessage, error) { - return ethapi.EthGetStorageAt(context.Background(), contractEthAddr, ethtypes.EthBytes{0}, "0x0") + blockParam, _ := ethtypes.NewEthBlockParamFromHexString("0x0") + return ethapi.EthGetStorageAt(context.Background(), contractEthAddr, ethtypes.EthBytes{0}, blockParam) }, }, @@ -338,7 +340,7 @@ func TestEthOpenRPCConformance(t *testing.T) { method: "eth_getTransactionCount", variant: "blocknumber", call: func(a *ethAPIRaw) (json.RawMessage, error) { - return ethapi.EthGetTransactionCount(context.Background(), senderEthAddr, blockNumberWithMessage.Hex()) + return ethapi.EthGetTransactionCount(context.Background(), senderEthAddr, ethtypes.NewEthBlockParamFromNumber(blockNumberWithMessage)) }, }, diff --git a/itests/eth_transactions_test.go b/itests/eth_transactions_test.go index 8d0df0433..eb900c7fb 100644 --- a/itests/eth_transactions_test.go +++ b/itests/eth_transactions_test.go @@ -316,7 +316,7 @@ func TestGetBlockByNumber(t *testing.T) { // Fetch balance on a null round; should not fail and should return previous balance. // Should be lower than original balance. - bal, err := client.EthGetBalance(ctx, ethAddr, (ethtypes.EthUint64(afterNullHeight - 1)).Hex()) + bal, err := client.EthGetBalance(ctx, ethAddr, ethtypes.NewEthBlockParamFromNumber(ethtypes.EthUint64(afterNullHeight-1))) require.NoError(t, err) require.NotEqual(t, big.Zero(), bal) require.Equal(t, types.FromFil(10).Int, bal.Int) diff --git a/itests/fevm_test.go b/itests/fevm_test.go index 9b6dba92c..7051bb0df 100644 --- a/itests/fevm_test.go +++ b/itests/fevm_test.go @@ -268,14 +268,14 @@ func TestFEVMDelegateCall(t *testing.T) { // The implementation's storage should not have been updated. actorAddrEth, err := ethtypes.EthAddressFromFilecoinAddress(actorAddr) require.NoError(t, err) - value, err := client.EVM().EthGetStorageAt(ctx, actorAddrEth, nil, "latest") + value, err := client.EVM().EthGetStorageAt(ctx, actorAddrEth, nil, ethtypes.NewEthBlockParamFromPredefined("latest")) require.NoError(t, err) require.Equal(t, ethtypes.EthBytes(make([]byte, 32)), value) // The storage actor's storage _should_ have been updated storageAddrEth, err := ethtypes.EthAddressFromFilecoinAddress(storageAddr) require.NoError(t, err) - value, err = client.EVM().EthGetStorageAt(ctx, storageAddrEth, nil, "latest") + value, err = client.EVM().EthGetStorageAt(ctx, storageAddrEth, nil, ethtypes.NewEthBlockParamFromPredefined("latest")) require.NoError(t, err) require.Equal(t, ethtypes.EthBytes(expectedResult), value) } @@ -881,7 +881,7 @@ func TestFEVMTestDeployOnTransfer(t *testing.T) { require.NoError(t, err) require.True(t, ret.Receipt.ExitCode.IsSuccess()) - balance, err := client.EVM().EthGetBalance(ctx, randomAddr, "latest") + balance, err := client.EVM().EthGetBalance(ctx, randomAddr, ethtypes.NewEthBlockParamFromPredefined("latest")) require.NoError(t, err) require.Equal(t, value.Int, balance.Int) @@ -1030,7 +1030,7 @@ func TestFEVMErrorParsing(t *testing.T) { _, err := e.EthCall(ctx, ethtypes.EthCall{ To: &contractAddrEth, Data: entryPoint, - }, "latest") + }, ethtypes.NewEthBlockParamFromPredefined("latest")) require.ErrorContains(t, err, expected) }) t.Run("EthEstimateGas", func(t *testing.T) { diff --git a/itests/kit/evm.go b/itests/kit/evm.go index c8904ab15..9b9299980 100644 --- a/itests/kit/evm.go +++ b/itests/kit/evm.go @@ -199,7 +199,7 @@ func (e *EVM) AssertAddressBalanceConsistent(ctx context.Context, addr address.A ethAddr, err := ethtypes.EthAddressFromFilecoinAddress(addr) require.NoError(e.t, err) - ebal, err := e.EthGetBalance(ctx, ethAddr, "latest") + ebal, err := e.EthGetBalance(ctx, ethAddr, ethtypes.NewEthBlockParamFromPredefined("latest")) require.NoError(e.t, err) require.Equal(e.t, fbal, types.BigInt(ebal)) diff --git a/node/impl/full/dummy.go b/node/impl/full/dummy.go index 918e84d10..636732416 100644 --- a/node/impl/full/dummy.go +++ b/node/impl/full/dummy.go @@ -62,7 +62,7 @@ func (e *EthModuleDummy) EthGetTransactionByHashLimited(ctx context.Context, txH return nil, ErrModuleDisabled } -func (e *EthModuleDummy) EthGetTransactionCount(ctx context.Context, sender ethtypes.EthAddress, blkOpt string) (ethtypes.EthUint64, error) { +func (e *EthModuleDummy) EthGetTransactionCount(ctx context.Context, sender ethtypes.EthAddress, blkParam ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthUint64, error) { return 0, ErrModuleDisabled } @@ -82,15 +82,15 @@ func (e *EthModuleDummy) EthGetTransactionByBlockNumberAndIndex(ctx context.Cont return ethtypes.EthTx{}, ErrModuleDisabled } -func (e *EthModuleDummy) EthGetCode(ctx context.Context, address ethtypes.EthAddress, blkOpt string) (ethtypes.EthBytes, error) { +func (e *EthModuleDummy) EthGetCode(ctx context.Context, address ethtypes.EthAddress, blkParam ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBytes, error) { return nil, ErrModuleDisabled } -func (e *EthModuleDummy) EthGetStorageAt(ctx context.Context, address ethtypes.EthAddress, position ethtypes.EthBytes, blkParam string) (ethtypes.EthBytes, error) { +func (e *EthModuleDummy) EthGetStorageAt(ctx context.Context, address ethtypes.EthAddress, position ethtypes.EthBytes, blkParam ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBytes, error) { return nil, ErrModuleDisabled } -func (e *EthModuleDummy) EthGetBalance(ctx context.Context, address ethtypes.EthAddress, blkParam string) (ethtypes.EthBigInt, error) { +func (e *EthModuleDummy) EthGetBalance(ctx context.Context, address ethtypes.EthAddress, blkParam ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBigInt, error) { return ethtypes.EthBigIntZero, ErrModuleDisabled } @@ -126,7 +126,7 @@ func (e *EthModuleDummy) EthEstimateGas(ctx context.Context, tx ethtypes.EthCall return 0, ErrModuleDisabled } -func (e *EthModuleDummy) EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam string) (ethtypes.EthBytes, error) { +func (e *EthModuleDummy) EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBytes, error) { return nil, ErrModuleDisabled } diff --git a/node/impl/full/eth.go b/node/impl/full/eth.go index 38c3a88d1..cee222683 100644 --- a/node/impl/full/eth.go +++ b/node/impl/full/eth.go @@ -59,12 +59,12 @@ type EthModuleAPI interface { EthGetTransactionByHashLimited(ctx context.Context, txHash *ethtypes.EthHash, limit abi.ChainEpoch) (*ethtypes.EthTx, error) EthGetMessageCidByTransactionHash(ctx context.Context, txHash *ethtypes.EthHash) (*cid.Cid, error) EthGetTransactionHashByCid(ctx context.Context, cid cid.Cid) (*ethtypes.EthHash, error) - EthGetTransactionCount(ctx context.Context, sender ethtypes.EthAddress, blkOpt string) (ethtypes.EthUint64, error) + EthGetTransactionCount(ctx context.Context, sender ethtypes.EthAddress, blkParam ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthUint64, error) EthGetTransactionReceipt(ctx context.Context, txHash ethtypes.EthHash) (*api.EthTxReceipt, error) EthGetTransactionReceiptLimited(ctx context.Context, txHash ethtypes.EthHash, limit abi.ChainEpoch) (*api.EthTxReceipt, error) - EthGetCode(ctx context.Context, address ethtypes.EthAddress, blkOpt string) (ethtypes.EthBytes, error) - EthGetStorageAt(ctx context.Context, address ethtypes.EthAddress, position ethtypes.EthBytes, blkParam string) (ethtypes.EthBytes, error) - EthGetBalance(ctx context.Context, address ethtypes.EthAddress, blkParam string) (ethtypes.EthBigInt, error) + EthGetCode(ctx context.Context, address ethtypes.EthAddress, blkParam ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBytes, error) + EthGetStorageAt(ctx context.Context, address ethtypes.EthAddress, position ethtypes.EthBytes, blkParam ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBytes, error) + EthGetBalance(ctx context.Context, address ethtypes.EthAddress, blkParam ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBigInt, error) EthFeeHistory(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthFeeHistory, error) EthChainId(ctx context.Context) (ethtypes.EthUint64, error) EthSyncing(ctx context.Context) (ethtypes.EthSyncingResult, error) @@ -73,7 +73,7 @@ type EthModuleAPI interface { EthProtocolVersion(ctx context.Context) (ethtypes.EthUint64, error) EthGasPrice(ctx context.Context) (ethtypes.EthBigInt, error) EthEstimateGas(ctx context.Context, tx ethtypes.EthCall) (ethtypes.EthUint64, error) - EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam string) (ethtypes.EthBytes, error) + EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBytes, error) EthMaxPriorityFeePerGas(ctx context.Context) (ethtypes.EthBigInt, error) EthSendRawTransaction(ctx context.Context, rawTx ethtypes.EthBytes) (ethtypes.EthHash, error) Web3ClientVersion(ctx context.Context) (string, error) @@ -241,7 +241,76 @@ func (a *EthModule) EthGetBlockByHash(ctx context.Context, blkHash ethtypes.EthH return newEthBlockFromFilecoinTipSet(ctx, ts, fullTxInfo, a.Chain, a.StateAPI) } -func (a *EthModule) parseBlkParam(ctx context.Context, blkParam string, strict bool) (tipset *types.TipSet, err error) { +func (a *EthModule) getTipsetByEthBlockNumberOrHash(ctx context.Context, blkParam ethtypes.EthBlockParamByNumberOrHash) (*types.TipSet, error) { + head := a.Chain.GetHeaviestTipSet() + + predefined := blkParam.PredefinedBlock + if predefined != nil { + if *predefined == "earliest" { + return nil, fmt.Errorf("block param \"earliest\" is not supported") + } else if *predefined == "pending" { + return head, nil + } else if *predefined == "latest" { + parent, err := a.Chain.GetTipSetFromKey(ctx, head.Parents()) + if err != nil { + return nil, fmt.Errorf("cannot get parent tipset") + } + return parent, nil + } else { + return nil, fmt.Errorf("unknown predefined block %s", *predefined) + } + } + + // utility function to validate a tipset by height and return it + returnAndValidateTipsetFunc := func(height abi.ChainEpoch) (*types.TipSet, error) { + if height > head.Height()-1 { + return nil, fmt.Errorf("requested a future epoch (beyond 'latest')") + } + ts, err := a.ChainAPI.ChainGetTipSetByHeight(ctx, height, head.Key()) + if err != nil { + return nil, fmt.Errorf("cannot get tipset at height: %v", height) + } + return ts, nil + } + + if blkParam.Number != nil { + return returnAndValidateTipsetFunc(abi.ChainEpoch(*blkParam.Number)) + } + + if blkParam.BlockNumber != nil { + return returnAndValidateTipsetFunc(abi.ChainEpoch(*blkParam.BlockNumber)) + } + + if blkParam.BlockHash != nil { + ts, err := a.Chain.GetTipSetByCid(ctx, blkParam.BlockHash.ToCid()) + if err != nil { + return nil, fmt.Errorf("cannot get tipset by hash: %v", err) + } + + if blkParam.RequireCanonical { + // walk back the current chain (our head) until we reach targetHeight and validate the block hash + currTs := head + for { + if currTs.Equals(ts) { + return ts, nil + } else if currTs.Height() < ts.Height() { + return nil, fmt.Errorf("could not find block hash %s in canonical chain", blkParam.BlockHash.ToCid()) + } + + currTs, err = a.Chain.LoadTipSet(ctx, currTs.Parents()) + if err != nil { + return nil, fmt.Errorf("failed to load tipset: %v", err) + } + } + } + + return ts, nil + } + + return nil, errors.New("invalid block param") +} + +func (a *EthModule) parseBlkParam(ctx context.Context, blkParam string, strict bool) (*types.TipSet, error) { if blkParam == "earliest" { return nil, fmt.Errorf("block param \"earliest\" is not supported") } @@ -382,13 +451,13 @@ func (a *EthModule) EthGetTransactionHashByCid(ctx context.Context, cid cid.Cid) return &hash, err } -func (a *EthModule) EthGetTransactionCount(ctx context.Context, sender ethtypes.EthAddress, blkParam string) (ethtypes.EthUint64, error) { +func (a *EthModule) EthGetTransactionCount(ctx context.Context, sender ethtypes.EthAddress, blkParam ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthUint64, error) { addr, err := sender.ToFilecoinAddress() if err != nil { return ethtypes.EthUint64(0), nil } - ts, err := a.parseBlkParam(ctx, blkParam, false) + ts, err := a.getTipsetByEthBlockNumberOrHash(ctx, blkParam) if err != nil { return ethtypes.EthUint64(0), xerrors.Errorf("failed to process block param: %s; %w", blkParam, err) } @@ -470,13 +539,13 @@ func (a *EthAPI) EthGetTransactionByBlockNumberAndIndex(context.Context, ethtype } // EthGetCode returns string value of the compiled bytecode -func (a *EthModule) EthGetCode(ctx context.Context, ethAddr ethtypes.EthAddress, blkParam string) (ethtypes.EthBytes, error) { +func (a *EthModule) EthGetCode(ctx context.Context, ethAddr ethtypes.EthAddress, blkParam ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBytes, error) { to, err := ethAddr.ToFilecoinAddress() if err != nil { return nil, xerrors.Errorf("cannot get Filecoin address: %w", err) } - ts, err := a.parseBlkParam(ctx, blkParam, false) + ts, err := a.getTipsetByEthBlockNumberOrHash(ctx, blkParam) if err != nil { return nil, xerrors.Errorf("failed to process block param: %s; %w", blkParam, err) } @@ -554,8 +623,8 @@ func (a *EthModule) EthGetCode(ctx context.Context, ethAddr ethtypes.EthAddress, return blk.RawData(), nil } -func (a *EthModule) EthGetStorageAt(ctx context.Context, ethAddr ethtypes.EthAddress, position ethtypes.EthBytes, blkParam string) (ethtypes.EthBytes, error) { - ts, err := a.parseBlkParam(ctx, blkParam, false) +func (a *EthModule) EthGetStorageAt(ctx context.Context, ethAddr ethtypes.EthAddress, position ethtypes.EthBytes, blkParam ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBytes, error) { + ts, err := a.getTipsetByEthBlockNumberOrHash(ctx, blkParam) if err != nil { return nil, xerrors.Errorf("failed to process block param: %s; %w", blkParam, err) } @@ -645,13 +714,13 @@ func (a *EthModule) EthGetStorageAt(ctx context.Context, ethAddr ethtypes.EthAdd return ethtypes.EthBytes(ret), nil } -func (a *EthModule) EthGetBalance(ctx context.Context, address ethtypes.EthAddress, blkParam string) (ethtypes.EthBigInt, error) { +func (a *EthModule) EthGetBalance(ctx context.Context, address ethtypes.EthAddress, blkParam ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBigInt, error) { filAddr, err := address.ToFilecoinAddress() if err != nil { return ethtypes.EthBigInt{}, err } - ts, err := a.parseBlkParam(ctx, blkParam, false) + ts, err := a.getTipsetByEthBlockNumberOrHash(ctx, blkParam) if err != nil { return ethtypes.EthBigInt{}, xerrors.Errorf("failed to process block param: %s; %w", blkParam, err) } @@ -1112,13 +1181,13 @@ func ethGasSearch( return -1, xerrors.Errorf("message execution failed: exit %s, reason: %s", res.MsgRct.ExitCode, res.Error) } -func (a *EthModule) EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam string) (ethtypes.EthBytes, error) { +func (a *EthModule) EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam ethtypes.EthBlockParamByNumberOrHash) (ethtypes.EthBytes, error) { msg, err := a.ethCallToFilecoinMessage(ctx, tx) if err != nil { return nil, xerrors.Errorf("failed to convert ethcall to filecoin message: %w", err) } - ts, err := a.parseBlkParam(ctx, blkParam, false) + ts, err := a.getTipsetByEthBlockNumberOrHash(ctx, blkParam) if err != nil { return nil, xerrors.Errorf("failed to process block param: %s; %w", blkParam, err) }