From c7d89e3d561e594fc4a2efab8130f10cc8d67d08 Mon Sep 17 00:00:00 2001 From: Federico Kunze <31522760+fedekunze@users.noreply.github.com> Date: Tue, 12 May 2020 15:12:52 -0400 Subject: [PATCH] rpc: fix eth_getProof (#286) --- Makefile | 2 +- rpc/eth_api.go | 49 +++++++++++++++++++++++++++++++++++++++-------- tests/rpc_test.go | 24 +++++++++++++++++++++-- 3 files changed, 64 insertions(+), 11 deletions(-) diff --git a/Makefile b/Makefile index 9cc9a8dd..9ac1b3dd 100644 --- a/Makefile +++ b/Makefile @@ -160,7 +160,7 @@ test-import: # TODO: remove tmp directory after test run to avoid subsequent errors test-rpc: - @${GO_MOD} go test -v --vet=off ./rpc/tester + @${GO_MOD} go test -v --vet=off ./tests/rpc_test it-tests: ./scripts/integration-test-all.sh -q 1 -z 1 -s 10 diff --git a/rpc/eth_api.go b/rpc/eth_api.go index 5f598c5a..369fef96 100644 --- a/rpc/eth_api.go +++ b/rpc/eth_api.go @@ -21,6 +21,8 @@ import ( "github.com/cosmos/ethermint/x/evm" "github.com/cosmos/ethermint/x/evm/types" + abci "github.com/tendermint/tendermint/abci/types" + "github.com/tendermint/tendermint/crypto/merkle" "github.com/tendermint/tendermint/rpc/client" tmtypes "github.com/tendermint/tendermint/types" @@ -36,6 +38,7 @@ import ( "github.com/cosmos/cosmos-sdk/crypto/keyring" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/x/auth" authclient "github.com/cosmos/cosmos-sdk/x/auth/client" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" ) @@ -748,6 +751,9 @@ func (e *PublicEthAPI) GetUncleByBlockNumberAndIndex(number hexutil.Uint, idx he return nil } +// Copied the Account and StorageResult types since they are registered under an +// internal pkg on geth. + // AccountResult struct for account proof type AccountResult struct { Address common.Address `json:"address"` @@ -768,20 +774,20 @@ type StorageResult struct { // GetProof returns an account object with proof and any storage proofs func (e *PublicEthAPI) GetProof(address common.Address, storageKeys []string, block BlockNumber) (*AccountResult, error) { - opts := client.ABCIQueryOptions{Height: int64(block), Prove: true} + e.cliCtx = e.cliCtx.WithHeight(int64(block)) path := fmt.Sprintf("custom/%s/%s/%s", types.ModuleName, evm.QueryAccount, address.Hex()) - pRes, err := e.cliCtx.Client.ABCIQueryWithOptions(path, nil, opts) + + // query eth account at block height + resBz, _, err := e.cliCtx.Query(path) if err != nil { return nil, err } - // TODO: convert TM merkle proof to []string if needed in future - // proof := pRes.Response.GetProof() - var account types.QueryResAccount - e.cliCtx.Codec.MustUnmarshalJSON(pRes.Response.GetValue(), &account) + e.cliCtx.Codec.MustUnmarshalJSON(resBz, &account) storageProofs := make([]StorageResult, len(storageKeys)) + opts := client.ABCIQueryOptions{Height: int64(block), Prove: true} for i, k := range storageKeys { // Get value for key vPath := fmt.Sprintf("custom/%s/%s/%s/%s", types.ModuleName, evm.QueryStorage, address, k) @@ -789,19 +795,46 @@ func (e *PublicEthAPI) GetProof(address common.Address, storageKeys []string, bl if err != nil { return nil, err } + var value types.QueryResStorage e.cliCtx.Codec.MustUnmarshalJSON(vRes.Response.GetValue(), &value) + // check for proof + proof := vRes.Response.GetProof() + proofStr := new(merkle.Proof).String() + if proof != nil { + proofStr = proof.String() + } + storageProofs[i] = StorageResult{ Key: k, Value: (*hexutil.Big)(common.BytesToHash(value.Value).Big()), - Proof: []string{""}, + Proof: []string{proofStr}, } } + req := abci.RequestQuery{ + Path: fmt.Sprintf("store/%s/key", auth.StoreKey), + Data: auth.AddressStoreKey(sdk.AccAddress(address.Bytes())), + Height: int64(block), + Prove: true, + } + + res, err := e.cliCtx.QueryABCI(req) + if err != nil { + return nil, err + } + + // check for proof + accountProof := res.GetProof() + accProofStr := new(merkle.Proof).String() + if accountProof != nil { + accProofStr = accountProof.String() + } + return &AccountResult{ Address: address, - AccountProof: []string{""}, // This shouldn't be necessary (different proof formats) + AccountProof: []string{accProofStr}, Balance: (*hexutil.Big)(utils.MustUnmarshalBigInt(account.Balance)), CodeHash: common.BytesToHash(account.CodeHash), Nonce: hexutil.Uint64(account.Nonce), diff --git a/tests/rpc_test.go b/tests/rpc_test.go index e87c9f5d..9f9c94bd 100644 --- a/tests/rpc_test.go +++ b/tests/rpc_test.go @@ -19,11 +19,14 @@ import ( "testing" "time" - "github.com/cosmos/ethermint/version" + "github.com/stretchr/testify/require" + ethcmn "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" ethtypes "github.com/ethereum/go-ethereum/core/types" - "github.com/stretchr/testify/require" + + "github.com/cosmos/ethermint/rpc" + "github.com/cosmos/ethermint/version" ) const ( @@ -155,6 +158,23 @@ func TestEth_GetStorageAt(t *testing.T) { require.True(t, bytes.Equal(storage, expectedRes), "expected: %d (%d bytes) got: %d (%d bytes)", expectedRes, len(expectedRes), storage, len(storage)) } +func TestEth_GetProof(t *testing.T) { + params := make([]interface{}, 3) + params[0] = addrA + params[1] = []string{string(addrAStoreKey)} + params[2] = "latest" + rpcRes := call(t, "eth_getProof", params) + require.NotNil(t, rpcRes) + + var accRes rpc.AccountResult + err := json.Unmarshal(rpcRes.Result, &accRes) + require.NoError(t, err) + require.NotEmpty(t, accRes.AccountProof) + require.NotEmpty(t, accRes.StorageProof) + + t.Logf("Got AccountResult %s", rpcRes.Result) +} + func TestEth_GetCode(t *testing.T) { expectedRes := hexutil.Bytes{} rpcRes := call(t, "eth_getCode", []string{addrA, zeroString})