forked from cerc-io/laconicd-deprecated
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:
co-authored by
Federico Kunze Küllmer
parent
c0ca43fbf1
commit
4ea3cc190e
@@ -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)
|
||||
|
||||
@@ -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(),
|
||||
@@ -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(),
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
@@ -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())
|
||||
@@ -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
|
||||
}
|
||||
@@ -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 {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user