rpc: add EIP1898 support (#462)
* support for eip1898 * update changelog * fix linter * fix linter * refactor code * add test * support for eip1898 * update changelog * fix linter * fix linter * refactor code * add test * cleanup code * add comment * create const for block param * change default to be earliest to be consistant with geth * correct comment * update doc * update doc Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com>
This commit is contained in:
parent
c0ca43fbf1
commit
4ea3cc190e
@ -99,6 +99,7 @@ the Tracer type used to collect execution traces from the EVM transaction execut
|
|||||||
* (rpc) [#313](https://github.com/tharsis/ethermint/pull/313) Implement internal debug namespace (Not including logger functions nor traces).
|
* (rpc) [#313](https://github.com/tharsis/ethermint/pull/313) Implement internal debug namespace (Not including logger functions nor traces).
|
||||||
* (rpc) [#349](https://github.com/tharsis/ethermint/pull/349) Implement configurable JSON-RPC APIs to manage enabled namespaces.
|
* (rpc) [#349](https://github.com/tharsis/ethermint/pull/349) Implement configurable JSON-RPC APIs to manage enabled namespaces.
|
||||||
* (rpc) [#377](https://github.com/tharsis/ethermint/pull/377) Implement `miner_` namespace. `miner_setEtherbase` and `miner_setGasPrice` are working as intended. All the other calls are not applicable and return `unsupported`.
|
* (rpc) [#377](https://github.com/tharsis/ethermint/pull/377) Implement `miner_` namespace. `miner_setEtherbase` and `miner_setGasPrice` are working as intended. All the other calls are not applicable and return `unsupported`.
|
||||||
|
* (eth) [tharsis#460](https://github.com/tharsis/ethermint/issues/460) Add support for EIP-1898.
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
|
@ -63,6 +63,7 @@ Check the JSON-RPC methods supported on Ethermint. {synopsis}
|
|||||||
| `eth_getUncleCountByBlockNumber` | Eth | N/A | Not relevant to Ethermint |
|
| `eth_getUncleCountByBlockNumber` | Eth | N/A | Not relevant to Ethermint |
|
||||||
| `eth_getUncleByBlockHashAndIndex` | Eth | N/A | Not relevant to Ethermint |
|
| `eth_getUncleByBlockHashAndIndex` | Eth | N/A | Not relevant to Ethermint |
|
||||||
| `eth_getUncleByBlockNumberAndIndex` | Eth | N/A | Not relevant to Ethermint |
|
| `eth_getUncleByBlockNumberAndIndex` | Eth | N/A | Not relevant to Ethermint |
|
||||||
|
| [`eth_getProof`](#eth-getProof) | Eth | ✔ | |
|
||||||
| [`eth_subscribe`](#eth-subscribe) | Websocket | ✔ | |
|
| [`eth_subscribe`](#eth-subscribe) | Websocket | ✔ | |
|
||||||
| [`eth_unsubscribe`](#eth-unsubscribe) | Websocket | ✔ | |
|
| [`eth_unsubscribe`](#eth-unsubscribe) | Websocket | ✔ | |
|
||||||
| [`personal_importRawKey`](#personal-importrawkey) | Personal | ✔ | |
|
| [`personal_importRawKey`](#personal-importrawkey) | Personal | ✔ | |
|
||||||
@ -299,7 +300,7 @@ Returns the account balance for a given account address and Block Number.
|
|||||||
|
|
||||||
- Account Address
|
- Account Address
|
||||||
|
|
||||||
- Block Number
|
- Block Number or Block Hash [(eip-1898)](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1898.md)
|
||||||
|
|
||||||
```json
|
```json
|
||||||
// Request
|
// Request
|
||||||
@ -319,7 +320,7 @@ Returns the storage address for a given account address.
|
|||||||
|
|
||||||
- Integer of the position in the storage
|
- Integer of the position in the storage
|
||||||
|
|
||||||
- Block Number
|
- Block Number or Block Hash [(eip-1898)](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1898.md)
|
||||||
|
|
||||||
```json
|
```json
|
||||||
// Request
|
// Request
|
||||||
@ -337,7 +338,7 @@ Returns the total transaction for a given account address and Block Number.
|
|||||||
|
|
||||||
- Account Address
|
- Account Address
|
||||||
|
|
||||||
- Block Number
|
- Block Number or Block Hash [(eip-1898)](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1898.md)
|
||||||
|
|
||||||
```json
|
```json
|
||||||
// Request
|
// Request
|
||||||
@ -387,7 +388,7 @@ Returns the code for a given account address and Block Number.
|
|||||||
|
|
||||||
- Account Address
|
- Account Address
|
||||||
|
|
||||||
- Block Number
|
- Block Number or Block Hash [(eip-1898)](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1898.md)
|
||||||
|
|
||||||
```json
|
```json
|
||||||
// Request
|
// Request
|
||||||
@ -489,7 +490,7 @@ Executes a new message call immediately without creating a transaction on the bl
|
|||||||
|
|
||||||
data: DATA - (optional) Hash of the method signature and encoded parameters. For details see Ethereum Contract ABI in the Solidity documentation
|
data: DATA - (optional) Hash of the method signature and encoded parameters. For details see Ethereum Contract ABI in the Solidity documentation
|
||||||
|
|
||||||
- Block number
|
- Block number or Block Hash [(eip-1898)](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1898.md)
|
||||||
|
|
||||||
```json
|
```json
|
||||||
// Request
|
// Request
|
||||||
@ -778,6 +779,26 @@ curl -X POST --data '{"jsonrpc":"2.0","method":"eth_coinbase","params":[],"id":1
|
|||||||
{"jsonrpc":"2.0","id":1,"result":"0x7cB61D4117AE31a12E393a1Cfa3BaC666481D02E"}
|
{"jsonrpc":"2.0","id":1,"result":"0x7cB61D4117AE31a12E393a1Cfa3BaC666481D02E"}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### eth_getProof
|
||||||
|
|
||||||
|
Returns the account- and storage-values of the specified account including the Merkle-proof.
|
||||||
|
|
||||||
|
#### Parameters
|
||||||
|
|
||||||
|
- Address of account or contract
|
||||||
|
|
||||||
|
- Integer of the position in the storage
|
||||||
|
|
||||||
|
- Block Number or Block Hash [(eip-1898)](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1898.md)
|
||||||
|
|
||||||
|
```json
|
||||||
|
// Request
|
||||||
|
curl -X POST --data '{"jsonrpc":"2.0","method":"eth_getProof","params":["0x1234567890123456789012345678901234567890",["0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000001"],"latest"],"id":1}' -H "Content-type:application/json" http://localhost:8545
|
||||||
|
|
||||||
|
// Result
|
||||||
|
{"jsonrpc": "2.0", "id": 1, "result": {"address": "0x1234567890123456789012345678901234567890", "accountProof": ["0xf90211a090dcaf88c40c7bbc95a912cbdde67c175767b31173df9ee4b0d733bfdd511c43a0babe369f6b12092f49181ae04ca173fb68d1a5456f18d20fa32cba73954052bda0473ecf8a7e36a829e75039a3b055e51b8332cbf03324ab4af2066bbd6fbf0021a0bbda34753d7aa6c38e603f360244e8f59611921d9e1f128372fec0d586d4f9e0a04e44caecff45c9891f74f6a2156735886eedf6f1a733628ebc802ec79d844648a0a5f3f2f7542148c973977c8a1e154c4300fec92f755f7846f1b734d3ab1d90e7a0e823850f50bf72baae9d1733a36a444ab65d0a6faaba404f0583ce0ca4dad92da0f7a00cbe7d4b30b11faea3ae61b7f1f2b315b61d9f6bd68bfe587ad0eeceb721a07117ef9fc932f1a88e908eaead8565c19b5645dc9e5b1b6e841c5edbdfd71681a069eb2de283f32c11f859d7bcf93da23990d3e662935ed4d6b39ce3673ec84472a0203d26456312bbc4da5cd293b75b840fc5045e493d6f904d180823ec22bfed8ea09287b5c21f2254af4e64fca76acc5cd87399c7f1ede818db4326c98ce2dc2208a06fc2d754e304c48ce6a517753c62b1a9c1d5925b89707486d7fc08919e0a94eca07b1c54f15e299bd58bdfef9741538c7828b5d7d11a489f9c20d052b3471df475a051f9dd3739a927c89e357580a4c97b40234aa01ed3d5e0390dc982a7975880a0a089d613f26159af43616fd9455bb461f4869bfede26f2130835ed067a8b967bfb80", "0xf90211a0395d87a95873cd98c21cf1df9421af03f7247880a2554e20738eec2c7507a494a0bcf6546339a1e7e14eb8fb572a968d217d2a0d1f3bc4257b22ef5333e9e4433ca012ae12498af8b2752c99efce07f3feef8ec910493be749acd63822c3558e6671a0dbf51303afdc36fc0c2d68a9bb05dab4f4917e7531e4a37ab0a153472d1b86e2a0ae90b50f067d9a2244e3d975233c0a0558c39ee152969f6678790abf773a9621a01d65cd682cc1be7c5e38d8da5c942e0a73eeaef10f387340a40a106699d494c3a06163b53d956c55544390c13634ea9aa75309f4fd866f312586942daf0f60fb37a058a52c1e858b1382a8893eb9c1f111f266eb9e21e6137aff0dddea243a567000a037b4b100761e02de63ea5f1fcfcf43e81a372dafb4419d126342136d329b7a7ba032472415864b08f808ba4374092003c8d7c40a9f7f9fe9cc8291f62538e1cc14a074e238ff5ec96b810364515551344100138916594d6af966170ff326a092fab0a0d31ac4eef14a79845200a496662e92186ca8b55e29ed0f9f59dbc6b521b116fea090607784fe738458b63c1942bba7c0321ae77e18df4961b2bc66727ea996464ea078f757653c1b63f72aff3dcc3f2a2e4c8cb4a9d36d1117c742833c84e20de994a0f78407de07f4b4cb4f899dfb95eedeb4049aeb5fc1635d65cf2f2f4dfd25d1d7a0862037513ba9d45354dd3e36264aceb2b862ac79d2050f14c95657e43a51b85c80", "0xf90171a04ad705ea7bf04339fa36b124fa221379bd5a38ffe9a6112cb2d94be3a437b879a08e45b5f72e8149c01efcb71429841d6a8879d4bbe27335604a5bff8dfdf85dcea00313d9b2f7c03733d6549ea3b810e5262ed844ea12f70993d87d3e0f04e3979ea0b59e3cdd6750fa8b15164612a5cb6567cdfb386d4e0137fccee5f35ab55d0efda0fe6db56e42f2057a071c980a778d9a0b61038f269dd74a0e90155b3f40f14364a08538587f2378a0849f9608942cf481da4120c360f8391bbcc225d811823c6432a026eac94e755534e16f9552e73025d6d9c30d1d7682a4cb5bd7741ddabfd48c50a041557da9a74ca68da793e743e81e2029b2835e1cc16e9e25bd0c1e89d4ccad6980a041dda0a40a21ade3a20fcd1a4abb2a42b74e9a32b02424ff8db4ea708a5e0fb9a09aaf8326a51f613607a8685f57458329b41e938bb761131a5747e066b81a0a16808080a022e6cef138e16d2272ef58434ddf49260dc1de1f8ad6dfca3da5d2a92aaaadc58080", "0xf851808080a009833150c367df138f1538689984b8a84fc55692d3d41fe4d1e5720ff5483a6980808080808080808080a0a319c1c415b271afc0adcb664e67738d103ac168e0bc0b7bd2da7966165cb9518080"], "balance": "0x0", "codeHash": "0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470", "nonce": "0x0", "storageHash": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", "storageProof": [{"key": "0x0000000000000000000000000000000000000000000000000000000000000000", "value": "0x0", "proof": []}, {"key": "0x0000000000000000000000000000000000000000000000000000000000000001", "value": "0x0", "proof": []}]}}
|
||||||
|
```
|
||||||
|
|
||||||
## WebSocket Methods
|
## WebSocket Methods
|
||||||
|
|
||||||
Read about websockets in [events](./../quickstart/events.md) {hide}
|
Read about websockets in [events](./../quickstart/events.md) {hide}
|
||||||
|
@ -318,6 +318,10 @@ func (e *EVMBackend) HeaderByHash(blockHash common.Hash) (*ethtypes.Header, erro
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if resBlock.Block == nil {
|
||||||
|
return nil, errors.Errorf("block not found for hash %s", blockHash.Hex())
|
||||||
|
}
|
||||||
|
|
||||||
req := &evmtypes.QueryBlockBloomRequest{Height: resBlock.Block.Height}
|
req := &evmtypes.QueryBlockBloomRequest{Height: resBlock.Block.Height}
|
||||||
|
|
||||||
blockBloomResp, err := e.queryClient.BlockBloom(types.ContextWithHeight(resBlock.Block.Height), req)
|
blockBloomResp, err := e.queryClient.BlockBloom(types.ContextWithHeight(resBlock.Block.Height), req)
|
||||||
|
@ -197,8 +197,13 @@ func (e *PublicAPI) BlockNumber() (hexutil.Uint64, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetBalance returns the provided account's balance up to the provided block number.
|
// GetBalance returns the provided account's balance up to the provided block number.
|
||||||
func (e *PublicAPI) GetBalance(address common.Address, blockNum rpctypes.BlockNumber) (*hexutil.Big, error) { // nolint: interfacer
|
func (e *PublicAPI) GetBalance(address common.Address, blockNrOrHash rpctypes.BlockNumberOrHash) (*hexutil.Big, error) { // nolint: interfacer
|
||||||
e.logger.Debug("eth_getBalance", "address", address.String(), "block number", blockNum)
|
e.logger.Debug("eth_getBalance", "address", address.String(), "block number or hash", blockNrOrHash)
|
||||||
|
|
||||||
|
blockNum, err := e.getBlockNumber(blockNrOrHash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
req := &evmtypes.QueryBalanceRequest{
|
req := &evmtypes.QueryBalanceRequest{
|
||||||
Address: address.String(),
|
Address: address.String(),
|
||||||
@ -218,8 +223,13 @@ func (e *PublicAPI) GetBalance(address common.Address, blockNum rpctypes.BlockNu
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetStorageAt returns the contract storage at the given address, block number, and key.
|
// GetStorageAt returns the contract storage at the given address, block number, and key.
|
||||||
func (e *PublicAPI) GetStorageAt(address common.Address, key string, blockNum rpctypes.BlockNumber) (hexutil.Bytes, error) { // nolint: interfacer
|
func (e *PublicAPI) GetStorageAt(address common.Address, key string, blockNrOrHash rpctypes.BlockNumberOrHash) (hexutil.Bytes, error) { // nolint: interfacer
|
||||||
e.logger.Debug("eth_getStorageAt", "address", address.Hex(), "key", key, "block number", blockNum)
|
e.logger.Debug("eth_getStorageAt", "address", address.Hex(), "key", key, "block number or hash", blockNrOrHash)
|
||||||
|
|
||||||
|
blockNum, err := e.getBlockNumber(blockNrOrHash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
req := &evmtypes.QueryStorageRequest{
|
req := &evmtypes.QueryStorageRequest{
|
||||||
Address: address.String(),
|
Address: address.String(),
|
||||||
@ -236,8 +246,12 @@ func (e *PublicAPI) GetStorageAt(address common.Address, key string, blockNum rp
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetTransactionCount returns the number of transactions at the given address up to the given block number.
|
// GetTransactionCount returns the number of transactions at the given address up to the given block number.
|
||||||
func (e *PublicAPI) GetTransactionCount(address common.Address, blockNum rpctypes.BlockNumber) (*hexutil.Uint64, error) {
|
func (e *PublicAPI) GetTransactionCount(address common.Address, blockNrOrHash rpctypes.BlockNumberOrHash) (*hexutil.Uint64, error) {
|
||||||
e.logger.Debug("eth_getTransactionCount", "address", address.Hex(), "block number", blockNum)
|
e.logger.Debug("eth_getTransactionCount", "address", address.Hex(), "block number or hash", blockNrOrHash)
|
||||||
|
blockNum, err := e.getBlockNumber(blockNrOrHash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
return e.backend.GetTransactionCount(address, blockNum)
|
return e.backend.GetTransactionCount(address, blockNum)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,14 +303,19 @@ func (e *PublicAPI) GetUncleCountByBlockNumber(blockNum rpctypes.BlockNumber) he
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetCode returns the contract code at the given address and block number.
|
// GetCode returns the contract code at the given address and block number.
|
||||||
func (e *PublicAPI) GetCode(address common.Address, blockNumber rpctypes.BlockNumber) (hexutil.Bytes, error) { // nolint: interfacer
|
func (e *PublicAPI) GetCode(address common.Address, blockNrOrHash rpctypes.BlockNumberOrHash) (hexutil.Bytes, error) { // nolint: interfacer
|
||||||
e.logger.Debug("eth_getCode", "address", address.Hex(), "block number", blockNumber)
|
e.logger.Debug("eth_getCode", "address", address.Hex(), "block number or hash", blockNrOrHash)
|
||||||
|
|
||||||
|
blockNum, err := e.getBlockNumber(blockNrOrHash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
req := &evmtypes.QueryCodeRequest{
|
req := &evmtypes.QueryCodeRequest{
|
||||||
Address: address.String(),
|
Address: address.String(),
|
||||||
}
|
}
|
||||||
|
|
||||||
res, err := e.queryClient.Code(rpctypes.ContextWithHeight(blockNumber.Int64()), req)
|
res, err := e.queryClient.Code(rpctypes.ContextWithHeight(blockNum.Int64()), req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -418,9 +437,14 @@ func (e *PublicAPI) SendRawTransaction(data hexutil.Bytes) (common.Hash, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Call performs a raw contract call.
|
// Call performs a raw contract call.
|
||||||
func (e *PublicAPI) Call(args evmtypes.CallArgs, blockNr rpctypes.BlockNumber, _ *rpctypes.StateOverride) (hexutil.Bytes, error) {
|
func (e *PublicAPI) Call(args evmtypes.CallArgs, blockNrOrHash rpctypes.BlockNumberOrHash, _ *rpctypes.StateOverride) (hexutil.Bytes, error) {
|
||||||
e.logger.Debug("eth_call", "args", args.String(), "block number", blockNr)
|
e.logger.Debug("eth_call", "args", args.String(), "block number or hash", blockNrOrHash)
|
||||||
data, err := e.doCall(args, blockNr)
|
|
||||||
|
blockNum, err := e.getBlockNumber(blockNrOrHash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
data, err := e.doCall(args, blockNum)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return []byte{}, err
|
return []byte{}, err
|
||||||
}
|
}
|
||||||
@ -791,9 +815,14 @@ func (e *PublicAPI) GetUncleByBlockNumberAndIndex(number hexutil.Uint, idx hexut
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetProof returns an account object with proof and any storage proofs
|
// GetProof returns an account object with proof and any storage proofs
|
||||||
func (e *PublicAPI) GetProof(address common.Address, storageKeys []string, blockNumber rpctypes.BlockNumber) (*rpctypes.AccountResult, error) {
|
func (e *PublicAPI) GetProof(address common.Address, storageKeys []string, blockNrOrHash rpctypes.BlockNumberOrHash) (*rpctypes.AccountResult, error) {
|
||||||
height := blockNumber.Int64()
|
e.logger.Debug("eth_getProof", "address", address.Hex(), "keys", storageKeys, "block number or hash", blockNrOrHash)
|
||||||
e.logger.Debug("eth_getProof", "address", address.Hex(), "keys", storageKeys, "number", height)
|
|
||||||
|
blockNum, err := e.getBlockNumber(blockNrOrHash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
height := blockNum.Int64()
|
||||||
|
|
||||||
ctx := rpctypes.ContextWithHeight(height)
|
ctx := rpctypes.ContextWithHeight(height)
|
||||||
clientCtx := e.clientCtx.WithHeight(height)
|
clientCtx := e.clientCtx.WithHeight(height)
|
||||||
@ -859,3 +888,21 @@ func (e *PublicAPI) GetProof(address common.Address, storageKeys []string, block
|
|||||||
StorageProof: storageProofs,
|
StorageProof: storageProofs,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getBlockNumber returns the BlockNumber from BlockNumberOrHash
|
||||||
|
func (e *PublicAPI) getBlockNumber(blockNrOrHash rpctypes.BlockNumberOrHash) (rpctypes.BlockNumber, error) {
|
||||||
|
switch {
|
||||||
|
case blockNrOrHash.BlockHash == nil && blockNrOrHash.BlockNumber == nil:
|
||||||
|
return rpctypes.EthEarliestBlockNumber, fmt.Errorf("BlockHash and BlockNumber cannot be both nil")
|
||||||
|
case blockNrOrHash.BlockHash != nil:
|
||||||
|
blockHeader, err := e.backend.HeaderByHash(*blockNrOrHash.BlockHash)
|
||||||
|
if err != nil {
|
||||||
|
return rpctypes.EthEarliestBlockNumber, err
|
||||||
|
}
|
||||||
|
return rpctypes.NewBlockNumber(blockHeader.Number), nil
|
||||||
|
case blockNrOrHash.BlockNumber != nil:
|
||||||
|
return *blockNrOrHash.BlockNumber, nil
|
||||||
|
default:
|
||||||
|
return rpctypes.EthEarliestBlockNumber, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2,11 +2,14 @@ package types
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
||||||
grpctypes "github.com/cosmos/cosmos-sdk/types/grpc"
|
grpctypes "github.com/cosmos/cosmos-sdk/types/grpc"
|
||||||
"github.com/spf13/cast"
|
"github.com/spf13/cast"
|
||||||
"google.golang.org/grpc/metadata"
|
"google.golang.org/grpc/metadata"
|
||||||
@ -23,6 +26,12 @@ const (
|
|||||||
EthEarliestBlockNumber = BlockNumber(0)
|
EthEarliestBlockNumber = BlockNumber(0)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
BlockParamEarliest = "earliest"
|
||||||
|
BlockParamLatest = "latest"
|
||||||
|
BlockParamPending = "pending"
|
||||||
|
)
|
||||||
|
|
||||||
// NewBlockNumber creates a new BlockNumber instance.
|
// NewBlockNumber creates a new BlockNumber instance.
|
||||||
func NewBlockNumber(n *big.Int) BlockNumber {
|
func NewBlockNumber(n *big.Int) BlockNumber {
|
||||||
return BlockNumber(n.Int64())
|
return BlockNumber(n.Int64())
|
||||||
@ -52,13 +61,13 @@ func (bn *BlockNumber) UnmarshalJSON(data []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch input {
|
switch input {
|
||||||
case "earliest":
|
case BlockParamEarliest:
|
||||||
*bn = EthEarliestBlockNumber
|
*bn = EthEarliestBlockNumber
|
||||||
return nil
|
return nil
|
||||||
case "latest":
|
case BlockParamLatest:
|
||||||
*bn = EthLatestBlockNumber
|
*bn = EthLatestBlockNumber
|
||||||
return nil
|
return nil
|
||||||
case "pending":
|
case BlockParamPending:
|
||||||
*bn = EthPendingBlockNumber
|
*bn = EthPendingBlockNumber
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -103,3 +112,74 @@ func (bn BlockNumber) TmHeight() *int64 {
|
|||||||
height := bn.Int64()
|
height := bn.Int64()
|
||||||
return &height
|
return &height
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BlockNumberOrHash represents a block number or a block hash.
|
||||||
|
type BlockNumberOrHash struct {
|
||||||
|
BlockNumber *BlockNumber `json:"blockNumber,omitempty"`
|
||||||
|
BlockHash *common.Hash `json:"blockHash,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bnh *BlockNumberOrHash) UnmarshalJSON(data []byte) error {
|
||||||
|
type erased BlockNumberOrHash
|
||||||
|
e := erased{}
|
||||||
|
err := json.Unmarshal(data, &e)
|
||||||
|
if err == nil {
|
||||||
|
return bnh.checkUnmarshal(BlockNumberOrHash(e))
|
||||||
|
}
|
||||||
|
var input string
|
||||||
|
err = json.Unmarshal(data, &input)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = bnh.decodeFromString(input)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bnh *BlockNumberOrHash) checkUnmarshal(e BlockNumberOrHash) error {
|
||||||
|
if e.BlockNumber != nil && e.BlockHash != nil {
|
||||||
|
return fmt.Errorf("cannot specify both BlockHash and BlockNumber, choose one or the other")
|
||||||
|
}
|
||||||
|
bnh.BlockNumber = e.BlockNumber
|
||||||
|
bnh.BlockHash = e.BlockHash
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bnh *BlockNumberOrHash) decodeFromString(input string) error {
|
||||||
|
switch input {
|
||||||
|
case BlockParamEarliest:
|
||||||
|
bn := EthEarliestBlockNumber
|
||||||
|
bnh.BlockNumber = &bn
|
||||||
|
case BlockParamLatest:
|
||||||
|
bn := EthLatestBlockNumber
|
||||||
|
bnh.BlockNumber = &bn
|
||||||
|
case BlockParamPending:
|
||||||
|
bn := EthPendingBlockNumber
|
||||||
|
bnh.BlockNumber = &bn
|
||||||
|
default:
|
||||||
|
// check if the input is a block hash
|
||||||
|
if len(input) == 66 {
|
||||||
|
hash := common.Hash{}
|
||||||
|
err := hash.UnmarshalText([]byte(input))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
bnh.BlockHash = &hash
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// otherwise take the hex string has int64 value
|
||||||
|
blockNumber, err := hexutil.DecodeUint64(input)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if blockNumber > math.MaxInt64 {
|
||||||
|
return fmt.Errorf("blocknumber %d is too high", blockNumber)
|
||||||
|
}
|
||||||
|
bn := BlockNumber(blockNumber)
|
||||||
|
bnh.BlockNumber = &bn
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
102
ethereum/rpc/types/block_test.go
Normal file
102
ethereum/rpc/types/block_test.go
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUnmarshalBlockNumberOrHash(t *testing.T) {
|
||||||
|
bnh := new(BlockNumberOrHash)
|
||||||
|
|
||||||
|
testCases := []struct {
|
||||||
|
msg string
|
||||||
|
input []byte
|
||||||
|
malleate func()
|
||||||
|
expPass bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"JSON input with block hash",
|
||||||
|
[]byte("{\"blockHash\": \"0x579917054e325746fda5c3ee431d73d26255bc4e10b51163862368629ae19739\"}"),
|
||||||
|
func() {
|
||||||
|
require.Equal(t, *bnh.BlockHash, common.HexToHash("0x579917054e325746fda5c3ee431d73d26255bc4e10b51163862368629ae19739"))
|
||||||
|
require.Nil(t, bnh.BlockNumber)
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"JSON input with block number",
|
||||||
|
[]byte("{\"blockNumber\": \"0x35\"}"),
|
||||||
|
func() {
|
||||||
|
require.Equal(t, *bnh.BlockNumber, BlockNumber(0x35))
|
||||||
|
require.Nil(t, bnh.BlockHash)
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"JSON input with block number latest",
|
||||||
|
[]byte("{\"blockNumber\": \"latest\"}"),
|
||||||
|
func() {
|
||||||
|
require.Equal(t, *bnh.BlockNumber, EthLatestBlockNumber)
|
||||||
|
require.Nil(t, bnh.BlockHash)
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"JSON input with both block hash and block number",
|
||||||
|
[]byte("{\"blockHash\": \"0x579917054e325746fda5c3ee431d73d26255bc4e10b51163862368629ae19739\", \"blockNumber\": \"0x35\"}"),
|
||||||
|
func() {
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"String input with block hash",
|
||||||
|
[]byte("\"0x579917054e325746fda5c3ee431d73d26255bc4e10b51163862368629ae19739\""),
|
||||||
|
func() {
|
||||||
|
require.Equal(t, *bnh.BlockHash, common.HexToHash("0x579917054e325746fda5c3ee431d73d26255bc4e10b51163862368629ae19739"))
|
||||||
|
require.Nil(t, bnh.BlockNumber)
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"String input with block number",
|
||||||
|
[]byte("\"0x35\""),
|
||||||
|
func() {
|
||||||
|
require.Equal(t, *bnh.BlockNumber, BlockNumber(0x35))
|
||||||
|
require.Nil(t, bnh.BlockHash)
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"String input with block number latest",
|
||||||
|
[]byte("\"latest\""),
|
||||||
|
func() {
|
||||||
|
require.Equal(t, *bnh.BlockNumber, EthLatestBlockNumber)
|
||||||
|
require.Nil(t, bnh.BlockHash)
|
||||||
|
},
|
||||||
|
true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"String input with block number overflow",
|
||||||
|
[]byte("\"0xffffffffffffffffffffffffffffffffffffff\""),
|
||||||
|
func() {
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
fmt.Sprintf("Case %s", tc.msg)
|
||||||
|
// reset input
|
||||||
|
bnh = new(BlockNumberOrHash)
|
||||||
|
err := bnh.UnmarshalJSON(tc.input)
|
||||||
|
tc.malleate()
|
||||||
|
if tc.expPass {
|
||||||
|
require.NoError(t, err)
|
||||||
|
} else {
|
||||||
|
require.Error(t, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user