Skip to content

Commit

Permalink
chore(rpc): restructure JSON-RPC APIs (evmos#1218)
Browse files Browse the repository at this point in the history
* move non api methods from eth/api.go to evm_backend: ClientCtx, QueryClient, Ctx, getBlockNumber, getTransactionByBlockAndIndex, doCall

* organize eth/api.go into sections and move backend logic to dedicated files

* remove unnecesary comment

* move resend to the backend

* refractor eth api

* refractor debug namespace

* refactor miner namespace

* refactor personal namespace

* update transactionReceipt from upstream

* update getBlockByNumber from upstream

* update getBalance from upstream

* update getProof from upstream

* update getBalance from upstream

* fix linter

* remove duplicated import

* remove duplicated import

* fix backend tests

* fix lint

* fix duplicated imports

* fix linter

* reorganize blocks

* backend folder refractor

* remove unnecessary file

* remove duplicate import

Co-authored-by: Freddy Caceres <[email protected]>
(cherry picked from commit d43386d)
  • Loading branch information
danburck authored and devon-chain committed Nov 15, 2022
1 parent fd3f4d1 commit e79488a
Show file tree
Hide file tree
Showing 22 changed files with 2,893 additions and 2,563 deletions.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/evmos/ethermint
go 1.18

require (
cosmossdk.io/math v1.0.0-beta.3
github.com/armon/go-metrics v0.4.0
github.com/btcsuite/btcd v0.22.1
github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce
Expand Down
3 changes: 3 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE=
cosmossdk.io/math v1.0.0-beta.3 h1:TbZxSopz2LqjJ7aXYfn7nJSb8vNaBklW6BLpcei1qwM=
cosmossdk.io/math v1.0.0-beta.3/go.mod h1:3LYasri3Zna4XpbrTNdKsWmD5fHHkaNAod/mNT9XdE4=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
filippo.io/edwards25519 v1.0.0-beta.2 h1:/BZRNzm8N4K4eWfK28dL4yescorxtO7YG1yun8fy+pI=
filippo.io/edwards25519 v1.0.0-beta.2/go.mod h1:X+pm78QAUPtFLi1z9PYIlS/bdDnvbCOGKtZ+ACWEf7o=
Expand Down Expand Up @@ -1391,4 +1393,5 @@ rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU=
10 changes: 4 additions & 6 deletions rpc/apis.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import (
"github.com/evmos/ethermint/rpc/namespaces/ethereum/personal"
"github.com/evmos/ethermint/rpc/namespaces/ethereum/txpool"
"github.com/evmos/ethermint/rpc/namespaces/ethereum/web3"
"github.com/evmos/ethermint/rpc/types"

rpcclient "github.com/tendermint/tendermint/rpc/jsonrpc/client"
)
Expand Down Expand Up @@ -57,13 +56,12 @@ var apiCreators map[string]APICreator
func init() {
apiCreators = map[string]APICreator{
EthNamespace: func(ctx *server.Context, clientCtx client.Context, tmWSClient *rpcclient.WSClient, allowUnprotectedTxs bool) []rpc.API {
nonceLock := new(types.AddrLocker)
evmBackend := backend.NewBackend(ctx, ctx.Logger, clientCtx, allowUnprotectedTxs)
return []rpc.API{
{
Namespace: EthNamespace,
Version: apiVersion,
Service: eth.NewPublicAPI(ctx.Logger, clientCtx, evmBackend, nonceLock),
Service: eth.NewPublicAPI(ctx.Logger, evmBackend),
Public: true,
},
{
Expand Down Expand Up @@ -100,7 +98,7 @@ func init() {
{
Namespace: PersonalNamespace,
Version: apiVersion,
Service: personal.NewAPI(ctx.Logger, clientCtx, evmBackend),
Service: personal.NewAPI(ctx.Logger, evmBackend),
Public: false,
},
}
Expand All @@ -121,7 +119,7 @@ func init() {
{
Namespace: DebugNamespace,
Version: apiVersion,
Service: debug.NewAPI(ctx, evmBackend, clientCtx),
Service: debug.NewAPI(ctx, evmBackend),
Public: true,
},
}
Expand All @@ -132,7 +130,7 @@ func init() {
{
Namespace: MinerNamespace,
Version: apiVersion,
Service: miner.NewPrivateAPI(ctx, clientCtx, evmBackend),
Service: miner.NewPrivateAPI(ctx, evmBackend),
Public: false,
},
}
Expand Down
207 changes: 207 additions & 0 deletions rpc/backend/account_info.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
package backend

import (
"fmt"
"math"
"math/big"

sdkmath "cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
rpctypes "github.com/evmos/ethermint/rpc/types"
evmtypes "github.com/evmos/ethermint/x/evm/types"
"github.com/pkg/errors"
)

// GetCode returns the contract code at the given address and block number.
func (b *Backend) GetCode(address common.Address, blockNrOrHash rpctypes.BlockNumberOrHash) (hexutil.Bytes, error) {
blockNum, err := b.GetBlockNumber(blockNrOrHash)
if err != nil {
return nil, err
}

req := &evmtypes.QueryCodeRequest{
Address: address.String(),
}

res, err := b.queryClient.Code(rpctypes.ContextWithHeight(blockNum.Int64()), req)
if err != nil {
return nil, err
}

return res.Code, nil
}

// GetProof returns an account object with proof and any storage proofs
func (b *Backend) GetProof(address common.Address, storageKeys []string, blockNrOrHash rpctypes.BlockNumberOrHash) (*rpctypes.AccountResult, error) {
blockNum, err := b.GetBlockNumber(blockNrOrHash)
if err != nil {
return nil, err
}

height := blockNum.Int64()
_, err = b.GetTendermintBlockByNumber(blockNum)
if err != nil {
// Get 'latest' proof if query is in the future
// this imitates geth behavior
height = 0
}
ctx := rpctypes.ContextWithHeight(height)

// if the height is equal to zero, meaning the query condition of the block is either "pending" or "latest"
if height == 0 {
bn, err := b.BlockNumber()
if err != nil {
return nil, err
}

if bn > math.MaxInt64 {
return nil, fmt.Errorf("not able to query block number greater than MaxInt64")
}

height = int64(bn)
}

clientCtx := b.clientCtx.WithHeight(height)

// query storage proofs
storageProofs := make([]rpctypes.StorageResult, len(storageKeys))

for i, key := range storageKeys {
hexKey := common.HexToHash(key)
valueBz, proof, err := b.queryClient.GetProof(clientCtx, evmtypes.StoreKey, evmtypes.StateKey(address, hexKey.Bytes()))
if err != nil {
return nil, err
}

// check for proof
var proofStr string
if proof != nil {
proofStr = proof.String()
}

storageProofs[i] = rpctypes.StorageResult{
Key: key,
Value: (*hexutil.Big)(new(big.Int).SetBytes(valueBz)),
Proof: []string{proofStr},
}
}

// query EVM account
req := &evmtypes.QueryAccountRequest{
Address: address.String(),
}

res, err := b.queryClient.Account(ctx, req)
if err != nil {
return nil, err
}

// query account proofs
accountKey := authtypes.AddressStoreKey(sdk.AccAddress(address.Bytes()))
_, proof, err := b.queryClient.GetProof(clientCtx, authtypes.StoreKey, accountKey)
if err != nil {
return nil, err
}

// check for proof
var accProofStr string
if proof != nil {
accProofStr = proof.String()
}

balance, ok := sdkmath.NewIntFromString(res.Balance)
if !ok {
return nil, errors.New("invalid balance")
}

return &rpctypes.AccountResult{
Address: address,
AccountProof: []string{accProofStr},
Balance: (*hexutil.Big)(balance.BigInt()),
CodeHash: common.HexToHash(res.CodeHash),
Nonce: hexutil.Uint64(res.Nonce),
StorageHash: common.Hash{}, // NOTE: Ethermint doesn't have a storage hash. TODO: implement?
StorageProof: storageProofs,
}, nil
}

// GetStorageAt returns the contract storage at the given address, block number, and key.
func (b *Backend) GetStorageAt(address common.Address, key string, blockNrOrHash rpctypes.BlockNumberOrHash) (hexutil.Bytes, error) {
blockNum, err := b.GetBlockNumber(blockNrOrHash)
if err != nil {
return nil, err
}

req := &evmtypes.QueryStorageRequest{
Address: address.String(),
Key: key,
}

res, err := b.queryClient.Storage(rpctypes.ContextWithHeight(blockNum.Int64()), req)
if err != nil {
return nil, err
}

value := common.HexToHash(res.Value)
return value.Bytes(), nil
}

// GetBalance returns the provided account's balance up to the provided block number.
func (b *Backend) GetBalance(address common.Address, blockNrOrHash rpctypes.BlockNumberOrHash) (*hexutil.Big, error) {
blockNum, err := b.GetBlockNumber(blockNrOrHash)
if err != nil {
return nil, err
}

req := &evmtypes.QueryBalanceRequest{
Address: address.String(),
}

_, err = b.GetTendermintBlockByNumber(blockNum)
if err != nil {
return nil, err
}

res, err := b.queryClient.Balance(rpctypes.ContextWithHeight(blockNum.Int64()), req)
if err != nil {
return nil, err
}

val, ok := sdkmath.NewIntFromString(res.Balance)
if !ok {
return nil, errors.New("invalid balance")
}

// balance can only be negative in case of pruned node
if val.IsNegative() {
return nil, errors.New("couldn't fetch balance. Node state is pruned")
}

return (*hexutil.Big)(val.BigInt()), nil
}

// GetTransactionCount returns the number of transactions at the given address up to the given block number.
func (b *Backend) GetTransactionCount(address common.Address, blockNum rpctypes.BlockNumber) (*hexutil.Uint64, error) {
// Get nonce (sequence) from account
from := sdk.AccAddress(address.Bytes())
accRet := b.clientCtx.AccountRetriever

err := accRet.EnsureExists(b.clientCtx, from)
if err != nil {
// account doesn't exist yet, return 0
n := hexutil.Uint64(0)
return &n, nil
}

includePending := blockNum == rpctypes.EthPendingBlockNumber
nonce, err := b.getAccountNonce(address, includePending, blockNum.Int64(), b.logger)
if err != nil {
return nil, err
}

n := hexutil.Uint64(nonce)
return &n, nil
}
Loading

0 comments on commit e79488a

Please sign in to comment.