implement eth_exportAccount (#331)
This commit is contained in:
parent
94f6acc651
commit
9f573690a6
@ -264,6 +264,19 @@ func (e *PublicEthAPI) GetTransactionLogs(txHash common.Hash) ([]*ethtypes.Log,
|
|||||||
return e.backend.GetTransactionLogs(txHash)
|
return e.backend.GetTransactionLogs(txHash)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ExportAccount exports an account's balance, code, and storage at the given block number
|
||||||
|
// TODO: deprecate this once the export genesis command works
|
||||||
|
func (e *PublicEthAPI) ExportAccount(address common.Address, blockNumber BlockNumber) (string, error) {
|
||||||
|
ctx := e.cliCtx.WithHeight(blockNumber.Int64())
|
||||||
|
|
||||||
|
res, _, err := ctx.QueryWithData(fmt.Sprintf("custom/%s/%s/%s", types.ModuleName, evm.QueryExportAccount, address.Hex()), nil)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(res), nil
|
||||||
|
}
|
||||||
|
|
||||||
// Sign signs the provided data using the private key of address via Geth's signature standard.
|
// Sign signs the provided data using the private key of address via Geth's signature standard.
|
||||||
func (e *PublicEthAPI) Sign(address common.Address, data hexutil.Bytes) (hexutil.Bytes, error) {
|
func (e *PublicEthAPI) Sign(address common.Address, data hexutil.Bytes) (hexutil.Bytes, error) {
|
||||||
// TODO: Change this functionality to find an unlocked account by address
|
// TODO: Change this functionality to find an unlocked account by address
|
||||||
|
@ -17,6 +17,7 @@ import (
|
|||||||
"math/big"
|
"math/big"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -28,6 +29,7 @@ import (
|
|||||||
|
|
||||||
"github.com/cosmos/ethermint/rpc"
|
"github.com/cosmos/ethermint/rpc"
|
||||||
"github.com/cosmos/ethermint/version"
|
"github.com/cosmos/ethermint/version"
|
||||||
|
"github.com/cosmos/ethermint/x/evm/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -452,18 +454,21 @@ func deployTestContractWithFunction(t *testing.T) hexutil.Bytes {
|
|||||||
|
|
||||||
// contract Test {
|
// contract Test {
|
||||||
// event Hello(uint256 indexed world);
|
// event Hello(uint256 indexed world);
|
||||||
// event Test(uint256 indexed a, uint256 indexed b);
|
// event TestEvent(uint256 indexed a, uint256 indexed b);
|
||||||
|
|
||||||
|
// uint256 myStorage;
|
||||||
|
|
||||||
// constructor() public {
|
// constructor() public {
|
||||||
// emit Hello(17);
|
// emit Hello(17);
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// function test(uint256 a, uint256 b) public {
|
// function test(uint256 a, uint256 b) public {
|
||||||
// emit Test(a, b);
|
// myStorage = a;
|
||||||
|
// emit TestEvent(a, b);
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
bytecode := "0x608060405234801561001057600080fd5b5060117f775a94827b8fd9b519d36cd827093c664f93347070a554f65e4a6f56cd73889860405160405180910390a260c98061004d6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063eb8ac92114602d575b600080fd5b606060048036036040811015604157600080fd5b8101908080359060200190929190803590602001909291905050506062565b005b80827f91916a5e2c96453ddf6b585497262675140eb9f7a774095fb003d93e6dc6921660405160405180910390a3505056fea265627a7a72315820ef746422e676b3ed22147cd771a6f689e7c33ef17bf5cd91921793b5dd01e3e064736f6c63430005110032"
|
bytecode := "0x608060405234801561001057600080fd5b5060117f775a94827b8fd9b519d36cd827093c664f93347070a554f65e4a6f56cd73889860405160405180910390a260d08061004d6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063eb8ac92114602d575b600080fd5b606060048036036040811015604157600080fd5b8101908080359060200190929190803590602001909291905050506062565b005b8160008190555080827ff3ca124a697ba07e8c5e80bebcfcc48991fc16a63170e8a9206e30508960d00360405160405180910390a3505056fea265627a7a723158201d94d2187aaf3a6790527b615fcc40970febf0385fa6d72a2344848ebd0df3e964736f6c63430005110032"
|
||||||
|
|
||||||
from := getAddress(t)
|
from := getAddress(t)
|
||||||
|
|
||||||
@ -686,6 +691,70 @@ func TestEth_EstimateGas(t *testing.T) {
|
|||||||
require.Equal(t, hexutil.Bytes{0xf7, 0xa6}, gas)
|
require.Equal(t, hexutil.Bytes{0xf7, 0xa6}, gas)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestEth_ExportAccount(t *testing.T) {
|
||||||
|
param := []string{}
|
||||||
|
param = append(param, "0x1122334455667788990011223344556677889900")
|
||||||
|
param = append(param, "latest")
|
||||||
|
rpcRes := call(t, "eth_exportAccount", param)
|
||||||
|
|
||||||
|
var res string
|
||||||
|
err := json.Unmarshal(rpcRes.Result, &res)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
var account types.GenesisAccount
|
||||||
|
err = json.Unmarshal([]byte(res), &account)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.Equal(t, "0x1122334455667788990011223344556677889900", account.Address.Hex())
|
||||||
|
require.Equal(t, big.NewInt(0), account.Balance)
|
||||||
|
require.Equal(t, hexutil.Bytes(nil), account.Code)
|
||||||
|
require.Equal(t, []types.GenesisStorage(nil), account.Storage)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEth_ExportAccount_WithStorage(t *testing.T) {
|
||||||
|
hash := deployTestContractWithFunction(t)
|
||||||
|
receipt := waitForReceipt(t, hash)
|
||||||
|
addr := receipt["contractAddress"].(string)
|
||||||
|
|
||||||
|
// call function to set storage
|
||||||
|
calldata := "0xeb8ac92100000000000000000000000000000000000000000000000000000000000000630000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
|
||||||
|
from := getAddress(t)
|
||||||
|
param := make([]map[string]string, 1)
|
||||||
|
param[0] = make(map[string]string)
|
||||||
|
param[0]["from"] = "0x" + fmt.Sprintf("%x", from)
|
||||||
|
param[0]["to"] = addr
|
||||||
|
//param[0]["value"] = "0x1"
|
||||||
|
param[0]["data"] = calldata
|
||||||
|
rpcRes := call(t, "eth_sendTransaction", param)
|
||||||
|
|
||||||
|
var txhash hexutil.Bytes
|
||||||
|
err := json.Unmarshal(rpcRes.Result, &txhash)
|
||||||
|
require.NoError(t, err)
|
||||||
|
waitForReceipt(t, txhash)
|
||||||
|
|
||||||
|
// get exported account
|
||||||
|
eap := []string{}
|
||||||
|
eap = append(eap, addr)
|
||||||
|
eap = append(eap, "latest")
|
||||||
|
rpcRes = call(t, "eth_exportAccount", eap)
|
||||||
|
|
||||||
|
var res string
|
||||||
|
err = json.Unmarshal(rpcRes.Result, &res)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
var account types.GenesisAccount
|
||||||
|
err = json.Unmarshal([]byte(res), &account)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// deployed bytecode
|
||||||
|
bytecode := ethcmn.FromHex("0x6080604052348015600f57600080fd5b506004361060285760003560e01c8063eb8ac92114602d575b600080fd5b606060048036036040811015604157600080fd5b8101908080359060200190929190803590602001909291905050506062565b005b8160008190555080827ff3ca124a697ba07e8c5e80bebcfcc48991fc16a63170e8a9206e30508960d00360405160405180910390a3505056fea265627a7a723158201d94d2187aaf3a6790527b615fcc40970febf0385fa6d72a2344848ebd0df3e964736f6c63430005110032")
|
||||||
|
require.Equal(t, addr, strings.ToLower(account.Address.Hex()))
|
||||||
|
require.Equal(t, big.NewInt(0), account.Balance)
|
||||||
|
require.Equal(t, hexutil.Bytes(bytecode), account.Code)
|
||||||
|
require.NotEqual(t, []types.GenesisStorage(nil), account.Storage)
|
||||||
|
}
|
||||||
|
|
||||||
func TestEth_GetBlockByNumber(t *testing.T) {
|
func TestEth_GetBlockByNumber(t *testing.T) {
|
||||||
param := []interface{}{"0x1", false}
|
param := []interface{}{"0x1", false}
|
||||||
rpcRes := call(t, "eth_getBlockByNumber", param)
|
rpcRes := call(t, "eth_getBlockByNumber", param)
|
||||||
|
@ -21,6 +21,7 @@ const (
|
|||||||
QueryBloom = types.QueryBloom
|
QueryBloom = types.QueryBloom
|
||||||
QueryLogs = types.QueryLogs
|
QueryLogs = types.QueryLogs
|
||||||
QueryAccount = types.QueryAccount
|
QueryAccount = types.QueryAccount
|
||||||
|
QueryExportAccount = types.QueryExportAccount
|
||||||
)
|
)
|
||||||
|
|
||||||
// nolint
|
// nolint
|
||||||
|
@ -42,6 +42,8 @@ func NewQuerier(keeper Keeper) sdk.Querier {
|
|||||||
return queryLogs(ctx, keeper)
|
return queryLogs(ctx, keeper)
|
||||||
case types.QueryAccount:
|
case types.QueryAccount:
|
||||||
return queryAccount(ctx, path, keeper)
|
return queryAccount(ctx, path, keeper)
|
||||||
|
case types.QueryExportAccount:
|
||||||
|
return queryExportAccount(ctx, path, keeper)
|
||||||
default:
|
default:
|
||||||
return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "unknown query endpoint")
|
return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "unknown query endpoint")
|
||||||
}
|
}
|
||||||
@ -195,3 +197,30 @@ func queryAccount(ctx sdk.Context, path []string, keeper Keeper) ([]byte, error)
|
|||||||
}
|
}
|
||||||
return bz, nil
|
return bz, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func queryExportAccount(ctx sdk.Context, path []string, keeper Keeper) ([]byte, error) {
|
||||||
|
addr := ethcmn.HexToAddress(path[1])
|
||||||
|
|
||||||
|
var storage []types.GenesisStorage
|
||||||
|
err := keeper.CommitStateDB.ForEachStorage(addr, func(key, value ethcmn.Hash) bool {
|
||||||
|
storage = append(storage, types.NewGenesisStorage(key, value))
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
res := types.GenesisAccount{
|
||||||
|
Address: addr,
|
||||||
|
Balance: keeper.GetBalance(ctx, addr),
|
||||||
|
Code: keeper.GetCode(ctx, addr),
|
||||||
|
Storage: storage,
|
||||||
|
}
|
||||||
|
|
||||||
|
bz, err := codec.MarshalJSONIndent(keeper.cdc, res)
|
||||||
|
if err != nil {
|
||||||
|
return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return bz, nil
|
||||||
|
}
|
||||||
|
@ -29,6 +29,7 @@ func (suite *KeeperTestSuite) TestQuerier() {
|
|||||||
// {"logs bloom", []string{types.QueryLogsBloom, "0x0"}, func() {}, true},
|
// {"logs bloom", []string{types.QueryLogsBloom, "0x0"}, func() {}, true},
|
||||||
{"logs", []string{types.QueryLogs, "0x0"}, func() {}, true},
|
{"logs", []string{types.QueryLogs, "0x0"}, func() {}, true},
|
||||||
{"account", []string{types.QueryAccount, "0x0"}, func() {}, true},
|
{"account", []string{types.QueryAccount, "0x0"}, func() {}, true},
|
||||||
|
{"exportAccount", []string{types.QueryExportAccount, "0x0"}, func() {}, true},
|
||||||
{"unknown request", []string{"other"}, func() {}, false},
|
{"unknown request", []string{"other"}, func() {}, false},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ const (
|
|||||||
QueryBloom = "bloom"
|
QueryBloom = "bloom"
|
||||||
QueryLogs = "logs"
|
QueryLogs = "logs"
|
||||||
QueryAccount = "account"
|
QueryAccount = "account"
|
||||||
|
QueryExportAccount = "exportAccount"
|
||||||
)
|
)
|
||||||
|
|
||||||
// QueryResProtocolVersion is response type for protocol version query
|
// QueryResProtocolVersion is response type for protocol version query
|
||||||
@ -99,3 +100,5 @@ type QueryResAccount struct {
|
|||||||
CodeHash []byte `json:"codeHash"`
|
CodeHash []byte `json:"codeHash"`
|
||||||
Nonce uint64 `json:"nonce"`
|
Nonce uint64 `json:"nonce"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type QueryResExportAccount = GenesisAccount
|
||||||
|
@ -221,7 +221,7 @@ func (so *stateObject) markSuicided() {
|
|||||||
// commitState commits all dirty storage to a KVStore.
|
// commitState commits all dirty storage to a KVStore.
|
||||||
func (so *stateObject) commitState() {
|
func (so *stateObject) commitState() {
|
||||||
ctx := so.stateDB.ctx
|
ctx := so.stateDB.ctx
|
||||||
store := prefix.NewStore(ctx.KVStore(so.stateDB.storeKey), KeyPrefixStorage)
|
store := prefix.NewStore(ctx.KVStore(so.stateDB.storeKey), AddressStoragePrefix(so.Address()))
|
||||||
|
|
||||||
for key, value := range so.dirtyStorage {
|
for key, value := range so.dirtyStorage {
|
||||||
delete(so.dirtyStorage, key)
|
delete(so.dirtyStorage, key)
|
||||||
@ -333,7 +333,7 @@ func (so *stateObject) GetCommittedState(_ ethstate.Database, key ethcmn.Hash) e
|
|||||||
|
|
||||||
// otherwise load the value from the KVStore
|
// otherwise load the value from the KVStore
|
||||||
ctx := so.stateDB.ctx
|
ctx := so.stateDB.ctx
|
||||||
store := prefix.NewStore(ctx.KVStore(so.stateDB.storeKey), KeyPrefixStorage)
|
store := prefix.NewStore(ctx.KVStore(so.stateDB.storeKey), AddressStoragePrefix(so.Address()))
|
||||||
rawValue := store.Get(prefixKey.Bytes())
|
rawValue := store.Get(prefixKey.Bytes())
|
||||||
|
|
||||||
if len(rawValue) > 0 {
|
if len(rawValue) > 0 {
|
||||||
|
@ -717,7 +717,8 @@ func (csdb *CommitStateDB) ForEachStorage(addr ethcmn.Address, cb func(key, valu
|
|||||||
}
|
}
|
||||||
|
|
||||||
store := csdb.ctx.KVStore(csdb.storeKey)
|
store := csdb.ctx.KVStore(csdb.storeKey)
|
||||||
iterator := sdk.KVStorePrefixIterator(store, AddressStoragePrefix(so.Address()))
|
prefix := AddressStoragePrefix(so.Address())
|
||||||
|
iterator := sdk.KVStorePrefixIterator(store, prefix)
|
||||||
defer iterator.Close()
|
defer iterator.Close()
|
||||||
|
|
||||||
for ; iterator.Valid(); iterator.Next() {
|
for ; iterator.Valid(); iterator.Next() {
|
||||||
|
Loading…
Reference in New Issue
Block a user