Skip to content
This repository was archived by the owner on Apr 4, 2024. It is now read-only.

Add eip1898 support #462

Merged
merged 18 commits into from
Aug 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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) [#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`.
* (eth) [tharsis#460](https://github.com/tharsis/ethermint/issues/460) Add support for EIP-1898.

### Bug Fixes

Expand Down
31 changes: 26 additions & 5 deletions docs/api/json-rpc/endpoints.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ Check the JSON-RPC methods supported on Ethermint. {synopsis}
| `eth_getUncleCountByBlockNumber` | 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_getProof`](#eth-getProof) | Eth | ✔ | |
| [`eth_subscribe`](#eth-subscribe) | Websocket | ✔ | |
| [`eth_unsubscribe`](#eth-unsubscribe) | Websocket | ✔ | |
| [`personal_importRawKey`](#personal-importrawkey) | Personal | ✔ | |
Expand Down Expand Up @@ -299,7 +300,7 @@ Returns the account balance for a given account address and Block Number.

- Account Address

- Block Number
- Block Number or Block Hash [(eip-1898)](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1898.md)

```json
// Request
Expand All @@ -319,7 +320,7 @@ Returns the storage address for a given account address.

- 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
// Request
Expand All @@ -337,7 +338,7 @@ Returns the total transaction for a given account address and Block Number.

- Account Address

- Block Number
- Block Number or Block Hash [(eip-1898)](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1898.md)

```json
// Request
Expand Down Expand Up @@ -387,7 +388,7 @@ Returns the code for a given account address and Block Number.

- Account Address

- Block Number
- Block Number or Block Hash [(eip-1898)](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1898.md)

```json
// Request
Expand Down Expand Up @@ -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

- Block number
- Block number or Block Hash [(eip-1898)](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-1898.md)

```json
// Request
Expand Down Expand Up @@ -778,6 +779,26 @@ curl -X POST --data '{"jsonrpc":"2.0","method":"eth_coinbase","params":[],"id":1
{"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

Read about websockets in [events](./../quickstart/events.md) {hide}
Expand Down
4 changes: 4 additions & 0 deletions ethereum/rpc/backend/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,10 @@ func (e *EVMBackend) HeaderByHash(blockHash common.Hash) (*ethtypes.Header, erro
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}

blockBloomResp, err := e.queryClient.BlockBloom(types.ContextWithHeight(resBlock.Block.Height), req)
Expand Down
77 changes: 62 additions & 15 deletions ethereum/rpc/namespaces/eth/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,8 +197,13 @@ func (e *PublicAPI) BlockNumber() (hexutil.Uint64, error) {
}

// 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
e.logger.Debug("eth_getBalance", "address", address.String(), "block number", blockNum)
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 or hash", blockNrOrHash)

blockNum, err := e.getBlockNumber(blockNrOrHash)
if err != nil {
return nil, err
}

req := &evmtypes.QueryBalanceRequest{
Address: address.String(),
Expand All @@ -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.
func (e *PublicAPI) GetStorageAt(address common.Address, key string, blockNum rpctypes.BlockNumber) (hexutil.Bytes, error) { // nolint: interfacer
e.logger.Debug("eth_getStorageAt", "address", address.Hex(), "key", key, "block number", blockNum)
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 or hash", blockNrOrHash)

blockNum, err := e.getBlockNumber(blockNrOrHash)
if err != nil {
return nil, err
}

req := &evmtypes.QueryStorageRequest{
Address: address.String(),
Expand All @@ -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.
func (e *PublicAPI) GetTransactionCount(address common.Address, blockNum rpctypes.BlockNumber) (*hexutil.Uint64, error) {
e.logger.Debug("eth_getTransactionCount", "address", address.Hex(), "block number", blockNum)
func (e *PublicAPI) GetTransactionCount(address common.Address, blockNrOrHash rpctypes.BlockNumberOrHash) (*hexutil.Uint64, error) {
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)
}

Expand Down Expand Up @@ -289,14 +303,19 @@ func (e *PublicAPI) GetUncleCountByBlockNumber(blockNum rpctypes.BlockNumber) he
}

// 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
e.logger.Debug("eth_getCode", "address", address.Hex(), "block number", blockNumber)
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 or hash", blockNrOrHash)

blockNum, err := e.getBlockNumber(blockNrOrHash)
if err != nil {
return nil, err
}

req := &evmtypes.QueryCodeRequest{
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 {
return nil, err
}
Expand Down Expand Up @@ -418,9 +437,14 @@ func (e *PublicAPI) SendRawTransaction(data hexutil.Bytes) (common.Hash, error)
}

// Call performs a raw contract call.
func (e *PublicAPI) Call(args evmtypes.CallArgs, blockNr rpctypes.BlockNumber, _ *rpctypes.StateOverride) (hexutil.Bytes, error) {
e.logger.Debug("eth_call", "args", args.String(), "block number", blockNr)
data, err := e.doCall(args, blockNr)
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 or hash", blockNrOrHash)

blockNum, err := e.getBlockNumber(blockNrOrHash)
if err != nil {
return nil, err
}
data, err := e.doCall(args, blockNum)
if err != nil {
return []byte{}, err
}
Expand Down Expand Up @@ -791,9 +815,14 @@ func (e *PublicAPI) GetUncleByBlockNumberAndIndex(number hexutil.Uint, idx hexut
}

// 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) {
height := blockNumber.Int64()
e.logger.Debug("eth_getProof", "address", address.Hex(), "keys", storageKeys, "number", height)
func (e *PublicAPI) GetProof(address common.Address, storageKeys []string, blockNrOrHash rpctypes.BlockNumberOrHash) (*rpctypes.AccountResult, error) {
e.logger.Debug("eth_getProof", "address", address.Hex(), "keys", storageKeys, "block number or hash", blockNrOrHash)

blockNum, err := e.getBlockNumber(blockNrOrHash)
if err != nil {
return nil, err
}
height := blockNum.Int64()

ctx := rpctypes.ContextWithHeight(height)
clientCtx := e.clientCtx.WithHeight(height)
Expand Down Expand Up @@ -859,3 +888,21 @@ func (e *PublicAPI) GetProof(address common.Address, storageKeys []string, block
StorageProof: storageProofs,
}, 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
}
}
86 changes: 83 additions & 3 deletions ethereum/rpc/types/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@ package types

import (
"context"
"encoding/json"
"fmt"
"math"
"math/big"
"strings"

"github.com/ethereum/go-ethereum/common"

grpctypes "github.com/cosmos/cosmos-sdk/types/grpc"
"github.com/spf13/cast"
"google.golang.org/grpc/metadata"
Expand All @@ -23,6 +26,12 @@ const (
EthEarliestBlockNumber = BlockNumber(0)
)

const (
BlockParamEarliest = "earliest"
BlockParamLatest = "latest"
BlockParamPending = "pending"
)

// NewBlockNumber creates a new BlockNumber instance.
func NewBlockNumber(n *big.Int) BlockNumber {
return BlockNumber(n.Int64())
Expand Down Expand Up @@ -52,13 +61,13 @@ func (bn *BlockNumber) UnmarshalJSON(data []byte) error {
}

switch input {
case "earliest":
case BlockParamEarliest:
*bn = EthEarliestBlockNumber
return nil
case "latest":
case BlockParamLatest:
*bn = EthLatestBlockNumber
return nil
case "pending":
case BlockParamPending:
*bn = EthPendingBlockNumber
return nil
}
Expand Down Expand Up @@ -103,3 +112,74 @@ func (bn BlockNumber) TmHeight() *int64 {
height := bn.Int64()
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 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why 66 length?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is the size of the hash string in ethereum ( "0x" + 2 * 32)

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
}
Loading