From 9cbb4dcf6daf36bf4d367aa696a87df317ab6bba Mon Sep 17 00:00:00 2001 From: Federico Kunze <31522760+fedekunze@users.noreply.github.com> Date: Wed, 6 Jan 2021 17:56:40 -0300 Subject: [PATCH] stargate: migrate types (#670) * changelog v0.4.0 * stargate: types changes * msg and handler changes * validation * fixes * more fixes * more test fixes * changelog * changelog * lint * rm comment * lint * redundant if condition --- CHANGELOG.md | 11 +- app/ante/eth.go | 2 +- rpc/types/utils.go | 10 +- types/types.proto | 23 ---- types/validation.go | 17 +++ types/validation_test.go | 55 ++++++++++ x/evm/genesis.go | 20 ++-- x/evm/genesis_test.go | 2 +- x/evm/handler.go | 87 +-------------- x/evm/keeper/keeper.go | 12 +-- x/evm/keeper/keeper_test.go | 4 +- x/evm/keeper/msg_server.go | 104 ++++++++++++++++++ x/evm/keeper/querier.go | 31 ------ x/evm/keeper/querier_test.go | 1 - x/evm/keeper/statedb_test.go | 20 ++-- x/evm/types/chain_config.go | 6 -- x/evm/types/genesis.go | 23 ++-- x/evm/types/genesis_test.go | 110 ++++++++++--------- x/evm/types/logs.go | 22 ++-- x/evm/types/logs_test.go | 60 ++++------- x/evm/types/msg.go | 146 +++++++++++++++++++++----- x/evm/types/msg_test.go | 5 +- x/evm/types/params.go | 10 +- x/evm/types/params_test.go | 4 +- x/evm/types/querier.go | 1 - x/evm/types/state_object.go | 39 ++++--- x/evm/types/state_transition.go | 9 +- x/evm/types/statedb.go | 2 +- x/evm/types/statedb_test.go | 20 ++-- x/evm/types/storage.go | 23 ++-- x/evm/types/storage_test.go | 11 +- x/evm/types/tx_data.go | 180 +++----------------------------- x/evm/types/tx_data_test.go | 56 ---------- 33 files changed, 540 insertions(+), 586 deletions(-) delete mode 100644 types/types.proto create mode 100644 types/validation.go create mode 100644 types/validation_test.go create mode 100644 x/evm/keeper/msg_server.go delete mode 100644 x/evm/types/tx_data_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index a1f146e7..2b1222d5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,16 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### API Breaking +* (evm) [\#670](https://github.com/cosmos/ethermint/pull/670) Migrate types to the ones defined by the protobuf messages, which are required for the stargate release. + +### Bug Fixes + +* (evm) [\#674](https://github.com/cosmos/ethermint/issues/674) Reset all cache after account data has been committed in `EndBlock` to make sure every node state consistent + +## [v0.4.0] - 2020-12-15 + +### API Breaking + * (evm) [\#661](https://github.com/cosmos/ethermint/pull/661) `Balance` field has been removed from the evm module's `GenesisState`. ### Features @@ -60,7 +70,6 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Bug Fixes -* (evm) [\#674](https://github.com/cosmos/ethermint/issues/674) Reset all cache after account data has been committed in `EndBlock` to make sure every node state consistent * (evm) [\#661](https://github.com/cosmos/ethermint/pull/661) Set nonce to the EVM account on genesis initialization. * (rpc) [\#648](https://github.com/cosmos/ethermint/issues/648) Fix block cumulative gas used value. * (evm) [\#621](https://github.com/cosmos/ethermint/issues/621) EVM `GenesisAccount` fields now share the same format as the auth module `Account`. diff --git a/app/ante/eth.go b/app/ante/eth.go index 2075235d..6f4f6a1e 100644 --- a/app/ante/eth.go +++ b/app/ante/eth.go @@ -330,7 +330,7 @@ func (egcd EthGasConsumeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simula // Charge sender for gas up to limit if gasLimit != 0 { // Cost calculates the fees paid to validators based on gas limit and price - cost := new(big.Int).Mul(msgEthTx.Data.Price, new(big.Int).SetUint64(gasLimit)) + cost := new(big.Int).Mul(msgEthTx.Data.Price.BigInt(), new(big.Int).SetUint64(gasLimit)) evmDenom := egcd.evmKeeper.GetParams(ctx).EvmDenom diff --git a/rpc/types/utils.go b/rpc/types/utils.go index 94b0fc15..a0e591e1 100644 --- a/rpc/types/utils.go +++ b/rpc/types/utils.go @@ -48,15 +48,15 @@ func NewTransaction(tx *evmtypes.MsgEthereumTx, txHash, blockHash common.Hash, b rpcTx := &Transaction{ From: from, Gas: hexutil.Uint64(tx.Data.GasLimit), - GasPrice: (*hexutil.Big)(tx.Data.Price), + GasPrice: (*hexutil.Big)(tx.Data.Price.BigInt()), Hash: txHash, Input: hexutil.Bytes(tx.Data.Payload), Nonce: hexutil.Uint64(tx.Data.AccountNonce), To: tx.To(), - Value: (*hexutil.Big)(tx.Data.Amount), - V: (*hexutil.Big)(tx.Data.V), - R: (*hexutil.Big)(tx.Data.R), - S: (*hexutil.Big)(tx.Data.S), + Value: (*hexutil.Big)(tx.Data.Amount.BigInt()), + V: (*hexutil.Big)(new(big.Int).SetBytes(tx.Data.V)), + R: (*hexutil.Big)(new(big.Int).SetBytes(tx.Data.R)), + S: (*hexutil.Big)(new(big.Int).SetBytes(tx.Data.S)), } if blockHash != (common.Hash{}) { diff --git a/types/types.proto b/types/types.proto deleted file mode 100644 index a0a82c0c..00000000 --- a/types/types.proto +++ /dev/null @@ -1,23 +0,0 @@ -syntax = "proto3"; -package ethermint.v1; - -import "third_party/proto/gogoproto/gogo.proto"; -import "third_party/proto/cosmos-sdk/x/auth/types/types.proto"; - -option go_package = "github.com/cosmos/ethermint/types"; - - -// EthAccount implements the auth.Account interface and embeds an -// auth.BaseAccount type. It is compatible with the auth.AccountKeeper. -message EthAccount { - option (gogoproto.goproto_getters) = false; - option (gogoproto.goproto_stringer) = false; - - cosmos_sdk.x.auth.v1.BaseAccount base_account = 1 [ - (gogoproto.embed) = true, - (gogoproto.moretags) = "yaml:\"base_account\"" - ]; - bytes code_hash = 2 [ - (gogoproto.moretags) = "yaml:\"code_hash\"" - ]; -} \ No newline at end of file diff --git a/types/validation.go b/types/validation.go new file mode 100644 index 00000000..11b8c917 --- /dev/null +++ b/types/validation.go @@ -0,0 +1,17 @@ +package types + +import ( + "bytes" + + ethcmn "github.com/ethereum/go-ethereum/common" +) + +// IsEmptyHash returns true if the hash corresponds to an empty ethereum hex hash. +func IsEmptyHash(hash string) bool { + return bytes.Equal(ethcmn.HexToHash(hash).Bytes(), ethcmn.Hash{}.Bytes()) +} + +// IsZeroAddress returns true if the address corresponds to an empty ethereum hex address. +func IsZeroAddress(address string) bool { + return bytes.Equal(ethcmn.HexToAddress(address).Bytes(), ethcmn.Address{}.Bytes()) +} diff --git a/types/validation_test.go b/types/validation_test.go new file mode 100644 index 00000000..7b04d272 --- /dev/null +++ b/types/validation_test.go @@ -0,0 +1,55 @@ +package types + +import ( + "testing" + + "github.com/stretchr/testify/require" + + ethcmn "github.com/ethereum/go-ethereum/common" +) + +func TestIsEmptyHash(t *testing.T) { + testCases := []struct { + name string + hash string + expEmpty bool + }{ + { + "empty string", "", true, + }, + { + "zero hash", ethcmn.Hash{}.String(), true, + }, + + { + "non-empty hash", ethcmn.BytesToHash([]byte{1, 2, 3, 4}).String(), false, + }, + } + + for _, tc := range testCases { + require.Equal(t, tc.expEmpty, IsEmptyHash(tc.hash), tc.name) + } +} + +func TestIsZeroAddress(t *testing.T) { + testCases := []struct { + name string + address string + expEmpty bool + }{ + { + "empty string", "", true, + }, + { + "zero address", ethcmn.Address{}.String(), true, + }, + + { + "non-empty address", ethcmn.BytesToAddress([]byte{1, 2, 3, 4}).String(), false, + }, + } + + for _, tc := range testCases { + require.Equal(t, tc.expEmpty, IsZeroAddress(tc.address), tc.name) + } +} diff --git a/x/evm/genesis.go b/x/evm/genesis.go index 02189426..b736712a 100644 --- a/x/evm/genesis.go +++ b/x/evm/genesis.go @@ -9,15 +9,21 @@ import ( ethcmn "github.com/ethereum/go-ethereum/common" ethermint "github.com/cosmos/ethermint/types" + "github.com/cosmos/ethermint/x/evm/keeper" "github.com/cosmos/ethermint/x/evm/types" abci "github.com/tendermint/tendermint/abci/types" ) // InitGenesis initializes genesis state based on exported genesis -func InitGenesis(ctx sdk.Context, k Keeper, accountKeeper types.AccountKeeper, data GenesisState) []abci.ValidatorUpdate { // nolint: interfacer - k.SetParams(ctx, data.Params) +func InitGenesis( + ctx sdk.Context, + k keeper.Keeper, + accountKeeper types.AccountKeeper, // nolint: interfacer + data GenesisState, +) []abci.ValidatorUpdate { + k.SetParams(ctx, data.Params) evmDenom := data.Params.EvmDenom for _, account := range data.Accounts { @@ -42,16 +48,16 @@ func InitGenesis(ctx sdk.Context, k Keeper, accountKeeper types.AccountKeeper, d k.SetNonce(ctx, address, acc.GetSequence()) k.SetBalance(ctx, address, evmBalance.BigInt()) - k.SetCode(ctx, address, account.Code) + k.SetCode(ctx, address, ethcmn.Hex2Bytes(account.Code)) for _, storage := range account.Storage { - k.SetState(ctx, address, storage.Key, storage.Value) + k.SetState(ctx, address, ethcmn.HexToHash(storage.Key), ethcmn.HexToHash(storage.Value)) } } var err error for _, txLog := range data.TxsLogs { - if err = k.SetLogs(ctx, txLog.Hash, txLog.Logs); err != nil { + if err = k.SetLogs(ctx, ethcmn.HexToHash(txLog.Hash), txLog.Logs); err != nil { panic(err) } } @@ -75,7 +81,7 @@ func InitGenesis(ctx sdk.Context, k Keeper, accountKeeper types.AccountKeeper, d } // ExportGenesis exports genesis state of the EVM module -func ExportGenesis(ctx sdk.Context, k Keeper, ak types.AccountKeeper) GenesisState { +func ExportGenesis(ctx sdk.Context, k keeper.Keeper, ak types.AccountKeeper) GenesisState { // nolint: prealloc var ethGenAccounts []types.GenesisAccount ak.IterateAccounts(ctx, func(account authexported.Account) bool { @@ -94,7 +100,7 @@ func ExportGenesis(ctx sdk.Context, k Keeper, ak types.AccountKeeper) GenesisSta genAccount := types.GenesisAccount{ Address: addr.String(), - Code: k.GetCode(ctx, addr), + Code: ethcmn.Bytes2Hex(k.GetCode(ctx, addr)), Storage: storage, } diff --git a/x/evm/genesis_test.go b/x/evm/genesis_test.go index 5cdd6f98..01b99f20 100644 --- a/x/evm/genesis_test.go +++ b/x/evm/genesis_test.go @@ -55,7 +55,7 @@ func (suite *EvmTestSuite) TestInitGenesis() { { Address: address.String(), Storage: types.Storage{ - {Key: common.BytesToHash([]byte("key")), Value: common.BytesToHash([]byte("value"))}, + {Key: common.BytesToHash([]byte("key")).String(), Value: common.BytesToHash([]byte("value")).String()}, }, }, }, diff --git a/x/evm/handler.go b/x/evm/handler.go index 9b99e2bf..82d27189 100644 --- a/x/evm/handler.go +++ b/x/evm/handler.go @@ -29,93 +29,16 @@ func NewHandler(k Keeper) sdk.Handler { // handleMsgEthereumTx handles an Ethereum specific tx func handleMsgEthereumTx(ctx sdk.Context, k Keeper, msg types.MsgEthereumTx) (*sdk.Result, error) { - // parse the chainID from a string to a base-10 integer - chainIDEpoch, err := ethermint.ParseChainID(ctx.ChainID()) + // execute state transition + res, err := k.EthereumTx(ctx, msg) if err != nil { return nil, err } - // Verify signature and retrieve sender address - sender, err := msg.VerifySig(chainIDEpoch) - if err != nil { - return nil, err - } + // log state transition result + k.Logger(ctx).Info(res.Log) - txHash := tmtypes.Tx(ctx.TxBytes()).Hash() - ethHash := common.BytesToHash(txHash) - - st := types.StateTransition{ - AccountNonce: msg.Data.AccountNonce, - Price: msg.Data.Price, - GasLimit: msg.Data.GasLimit, - Recipient: msg.Data.Recipient, - Amount: msg.Data.Amount, - Payload: msg.Data.Payload, - Csdb: k.CommitStateDB.WithContext(ctx), - ChainID: chainIDEpoch, - TxHash: ðHash, - Sender: sender, - Simulate: ctx.IsCheckTx(), - } - - // since the txCount is used by the stateDB, and a simulated tx is run only on the node it's submitted to, - // then this will cause the txCount/stateDB of the node that ran the simulated tx to be different than the - // other nodes, causing a consensus error - if !st.Simulate { - // Prepare db for logs - blockHash := types.HashFromContext(ctx) - k.CommitStateDB.Prepare(ethHash, blockHash, k.TxCount) - k.TxCount++ - } - - config, found := k.GetChainConfig(ctx) - if !found { - return nil, types.ErrChainConfigNotFound - } - - executionResult, err := st.TransitionDb(ctx, config) - if err != nil { - return nil, err - } - - if !st.Simulate { - // update block bloom filter - k.Bloom.Or(k.Bloom, executionResult.Bloom) - - // update transaction logs in KVStore - err = k.SetLogs(ctx, common.BytesToHash(txHash), executionResult.Logs) - if err != nil { - panic(err) - } - } - - // log successful execution - k.Logger(ctx).Info(executionResult.Result.Log) - - ctx.EventManager().EmitEvents(sdk.Events{ - sdk.NewEvent( - types.EventTypeEthereumTx, - sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Data.Amount.String()), - ), - sdk.NewEvent( - sdk.EventTypeMessage, - sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), - sdk.NewAttribute(sdk.AttributeKeySender, sender.String()), - ), - }) - - if msg.Data.Recipient != nil { - ctx.EventManager().EmitEvent( - sdk.NewEvent( - types.EventTypeEthereumTx, - sdk.NewAttribute(types.AttributeKeyRecipient, msg.Data.Recipient.String()), - ), - ) - } - - // set the events to the result - executionResult.Result.Events = ctx.EventManager().Events() - return executionResult.Result, nil + return res, nil } // handleMsgEthermint handles an sdk.StdTx for an Ethereum state transition diff --git a/x/evm/keeper/keeper.go b/x/evm/keeper/keeper.go index 4a29fd4d..43763c51 100644 --- a/x/evm/keeper/keeper.go +++ b/x/evm/keeper/keeper.go @@ -151,6 +151,7 @@ func (k Keeper) GetAllTxLogs(ctx sdk.Context) []types.TransactionLogs { // GetAccountStorage return state storage associated with an account func (k Keeper) GetAccountStorage(ctx sdk.Context, address common.Address) (types.Storage, error) { storage := types.Storage{} + err := k.ForEachStorage(ctx, address, func(key, value common.Hash) bool { storage = append(storage, types.NewState(key, value)) return false @@ -164,9 +165,9 @@ func (k Keeper) GetAccountStorage(ctx sdk.Context, address common.Address) (type // GetChainConfig gets block height from block consensus hash func (k Keeper) GetChainConfig(ctx sdk.Context) (types.ChainConfig, bool) { - store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixChainConfig) - // get from an empty key that's already prefixed by KeyPrefixChainConfig - bz := store.Get([]byte{}) + store := ctx.KVStore(k.storeKey) + + bz := store.Get(types.KeyPrefixChainConfig) if len(bz) == 0 { return types.ChainConfig{}, false } @@ -178,8 +179,7 @@ func (k Keeper) GetChainConfig(ctx sdk.Context) (types.ChainConfig, bool) { // SetChainConfig sets the mapping from block consensus hash to block height func (k Keeper) SetChainConfig(ctx sdk.Context, config types.ChainConfig) { - store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixChainConfig) + store := ctx.KVStore(k.storeKey) bz := k.cdc.MustMarshalBinaryBare(config) - // get to an empty key that's already prefixed by KeyPrefixChainConfig - store.Set([]byte{}, bz) + store.Set(types.KeyPrefixChainConfig, bz) } diff --git a/x/evm/keeper/keeper_test.go b/x/evm/keeper/keeper_test.go index b9ff9b3c..df7796e0 100644 --- a/x/evm/keeper/keeper_test.go +++ b/x/evm/keeper/keeper_test.go @@ -98,10 +98,10 @@ func (suite *KeeperTestSuite) TestTransactionLogs() { txLogs := suite.app.EvmKeeper.GetAllTxLogs(suite.ctx) suite.Require().Equal(2, len(txLogs)) - suite.Require().Equal(ethcmn.Hash{}.String(), txLogs[0].Hash.String()) + suite.Require().Equal(ethcmn.Hash{}.String(), txLogs[0].Hash) suite.Require().Equal([]*ethtypes.Log{log2, log3}, txLogs[0].Logs) - suite.Require().Equal(ethHash.String(), txLogs[1].Hash.String()) + suite.Require().Equal(ethHash.String(), txLogs[1].Hash) suite.Require().Equal([]*ethtypes.Log{log}, txLogs[1].Logs) } diff --git a/x/evm/keeper/msg_server.go b/x/evm/keeper/msg_server.go new file mode 100644 index 00000000..94273202 --- /dev/null +++ b/x/evm/keeper/msg_server.go @@ -0,0 +1,104 @@ +package keeper + +import ( + "github.com/ethereum/go-ethereum/common" + tmtypes "github.com/tendermint/tendermint/types" + + sdk "github.com/cosmos/cosmos-sdk/types" + + ethermint "github.com/cosmos/ethermint/types" + "github.com/cosmos/ethermint/x/evm/types" +) + +// EthereumTx implements the Msg/EthereumTx gRPC method. +func (k Keeper) EthereumTx(ctx sdk.Context, msg types.MsgEthereumTx) (*sdk.Result, error) { + // parse the chainID from a string to a base-10 integer + chainIDEpoch, err := ethermint.ParseChainID(ctx.ChainID()) + if err != nil { + return nil, err + } + + // Verify signature and retrieve sender address + sender, err := msg.VerifySig(chainIDEpoch) + if err != nil { + return nil, err + } + + var recipient *common.Address + if msg.Data.Recipient != nil { + addr := common.HexToAddress(msg.Data.Recipient.Address) + recipient = &addr + } + + txHash := tmtypes.Tx(ctx.TxBytes()).Hash() + ethHash := common.BytesToHash(txHash) + + st := types.StateTransition{ + AccountNonce: msg.Data.AccountNonce, + Price: msg.Data.Price.BigInt(), + GasLimit: msg.Data.GasLimit, + Recipient: recipient, + Amount: msg.Data.Amount.BigInt(), + Payload: msg.Data.Payload, + Csdb: k.CommitStateDB.WithContext(ctx), + ChainID: chainIDEpoch, + TxHash: ðHash, + Sender: sender, + Simulate: ctx.IsCheckTx(), + } + + // since the txCount is used by the stateDB, and a simulated tx is run only on the node it's submitted to, + // then this will cause the txCount/stateDB of the node that ran the simulated tx to be different than the + // other nodes, causing a consensus error + if !st.Simulate { + // Prepare db for logs + blockHash := types.HashFromContext(ctx) + k.CommitStateDB.Prepare(ethHash, blockHash, k.TxCount) + k.TxCount++ + } + + config, found := k.GetChainConfig(ctx) + if !found { + return nil, types.ErrChainConfigNotFound + } + + executionResult, err := st.TransitionDb(ctx, config) + if err != nil { + return nil, err + } + + if !st.Simulate { + // update block bloom filter + k.Bloom.Or(k.Bloom, executionResult.Bloom) + + // update transaction logs in KVStore + err = k.SetLogs(ctx, common.BytesToHash(txHash), executionResult.Logs) + if err != nil { + panic(err) + } + } + + ctx.EventManager().EmitEvents(sdk.Events{ + sdk.NewEvent( + types.EventTypeEthereumTx, + sdk.NewAttribute(sdk.AttributeKeyAmount, msg.Data.Amount.String()), + ), + sdk.NewEvent( + sdk.EventTypeMessage, + sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), + sdk.NewAttribute(sdk.AttributeKeySender, sender.String()), + ), + }) + + if msg.Data.Recipient != nil { + ctx.EventManager().EmitEvent( + sdk.NewEvent( + types.EventTypeEthereumTx, + sdk.NewAttribute(types.AttributeKeyRecipient, msg.Data.Recipient.Address), + ), + ) + } + + executionResult.Result.Events = ctx.EventManager().Events() + return executionResult.Result, nil +} diff --git a/x/evm/keeper/querier.go b/x/evm/keeper/querier.go index ca749e09..ebfe39bd 100644 --- a/x/evm/keeper/querier.go +++ b/x/evm/keeper/querier.go @@ -1,7 +1,6 @@ package keeper import ( - "encoding/json" "fmt" "strconv" @@ -39,8 +38,6 @@ func NewQuerier(keeper Keeper) sdk.Querier { return queryLogs(ctx, keeper) case types.QueryAccount: return queryAccount(ctx, path, keeper) - case types.QueryExportAccount: - return queryExportAccount(ctx, path, keeper) default: return nil, sdkerrors.Wrap(sdkerrors.ErrUnknownRequest, "unknown query endpoint") } @@ -183,31 +180,3 @@ func queryAccount(ctx sdk.Context, path []string, keeper Keeper) ([]byte, error) } return bz, nil } - -func queryExportAccount(ctx sdk.Context, path []string, keeper Keeper) ([]byte, error) { - hexAddress := path[1] - addr := ethcmn.HexToAddress(hexAddress) - - var storage types.Storage - err := keeper.ForEachStorage(ctx, addr, func(key, value ethcmn.Hash) bool { - storage = append(storage, types.NewState(key, value)) - return false - }) - if err != nil { - return nil, err - } - - res := types.GenesisAccount{ - Address: hexAddress, - Code: keeper.GetCode(ctx, addr), - Storage: storage, - } - - // TODO: codec.MarshalJSONIndent doesn't call the String() method of types properly - bz, err := json.MarshalIndent(res, "", "\t") - if err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrJSONMarshal, err.Error()) - } - - return bz, nil -} diff --git a/x/evm/keeper/querier_test.go b/x/evm/keeper/querier_test.go index 3b2f6c17..d79e2123 100644 --- a/x/evm/keeper/querier_test.go +++ b/x/evm/keeper/querier_test.go @@ -34,7 +34,6 @@ func (suite *KeeperTestSuite) TestQuerier() { }, true}, {"logs", []string{types.QueryLogs, "0x0"}, func() {}, true}, {"account", []string{types.QueryAccount, "0x0"}, func() {}, true}, - {"exportAccount", []string{types.QueryExportAccount, "0x0"}, func() {}, true}, {"unknown request", []string{"other"}, func() {}, false}, } diff --git a/x/evm/keeper/statedb_test.go b/x/evm/keeper/statedb_test.go index ed2fbf5f..d67d71ad 100644 --- a/x/evm/keeper/statedb_test.go +++ b/x/evm/keeper/statedb_test.go @@ -582,7 +582,7 @@ func (suite *KeeperTestSuite) TestCommitStateDB_ForEachStorage() { name string malleate func() callback func(key, value ethcmn.Hash) (stop bool) - expValues []ethcmn.Hash + expValues []string }{ { "aggregate state", @@ -595,12 +595,12 @@ func (suite *KeeperTestSuite) TestCommitStateDB_ForEachStorage() { storage = append(storage, types.NewState(key, value)) return false }, - []ethcmn.Hash{ - ethcmn.BytesToHash([]byte("value0")), - ethcmn.BytesToHash([]byte("value1")), - ethcmn.BytesToHash([]byte("value2")), - ethcmn.BytesToHash([]byte("value3")), - ethcmn.BytesToHash([]byte("value4")), + []string{ + ethcmn.BytesToHash([]byte("value0")).String(), + ethcmn.BytesToHash([]byte("value1")).String(), + ethcmn.BytesToHash([]byte("value2")).String(), + ethcmn.BytesToHash([]byte("value3")).String(), + ethcmn.BytesToHash([]byte("value4")).String(), }, }, { @@ -616,8 +616,8 @@ func (suite *KeeperTestSuite) TestCommitStateDB_ForEachStorage() { } return false }, - []ethcmn.Hash{ - ethcmn.BytesToHash([]byte("filtervalue")), + []string{ + ethcmn.BytesToHash([]byte("filtervalue")).String(), }, }, } @@ -632,7 +632,7 @@ func (suite *KeeperTestSuite) TestCommitStateDB_ForEachStorage() { suite.Require().NoError(err) suite.Require().Equal(len(tc.expValues), len(storage), fmt.Sprintf("Expected values:\n%v\nStorage Values\n%v", tc.expValues, storage)) - vals := make([]ethcmn.Hash, len(storage)) + vals := make([]string, len(storage)) for i := range storage { vals[i] = storage[i].Value } diff --git a/x/evm/types/chain_config.go b/x/evm/types/chain_config.go index 1f9184ad..c3725c5a 100644 --- a/x/evm/types/chain_config.go +++ b/x/evm/types/chain_config.go @@ -151,12 +151,6 @@ func validateHash(hex string) error { return sdkerrors.Wrapf(ErrInvalidChainConfig, "hash cannot be blank") } - bz := common.FromHex(hex) - lenHex := len(bz) - if lenHex > 0 && lenHex != common.HashLength { - return sdkerrors.Wrapf(ErrInvalidChainConfig, "invalid hash length, expected %d, got %d", common.HashLength, lenHex) - } - return nil } diff --git a/x/evm/types/genesis.go b/x/evm/types/genesis.go index 1871103b..d53e7586 100644 --- a/x/evm/types/genesis.go +++ b/x/evm/types/genesis.go @@ -4,8 +4,9 @@ import ( "errors" "fmt" + ethermint "github.com/cosmos/ethermint/types" + ethcmn "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" ) type ( @@ -22,19 +23,19 @@ type ( // storage type and that it doesn't contain the private key field. // NOTE: balance is omitted as it is imported from the auth account balance. GenesisAccount struct { - Address string `json:"address"` - Code hexutil.Bytes `json:"code,omitempty"` - Storage Storage `json:"storage,omitempty"` + Address string `json:"address"` + Code string `json:"code,omitempty"` + Storage Storage `json:"storage,omitempty"` } ) // Validate performs a basic validation of a GenesisAccount fields. func (ga GenesisAccount) Validate() error { - if ga.Address == (ethcmn.Address{}.String()) { + if ethermint.IsZeroAddress(ga.Address) { return fmt.Errorf("address cannot be the zero address %s", ga.Address) } - if ga.Code != nil && len(ga.Code) == 0 { - return errors.New("code bytes cannot be empty") + if len(ethcmn.Hex2Bytes(ga.Code)) == 0 { + return errors.New("code cannot be empty") } return ga.Storage.Validate() @@ -67,15 +68,15 @@ func (gs GenesisState) Validate() error { } for _, tx := range gs.TxsLogs { - if seenTxs[tx.Hash.String()] { - return fmt.Errorf("duplicated logs from transaction %s", tx.Hash.String()) + if seenTxs[tx.Hash] { + return fmt.Errorf("duplicated logs from transaction %s", tx.Hash) } if err := tx.Validate(); err != nil { - return fmt.Errorf("invalid logs from transaction %s: %w", tx.Hash.String(), err) + return fmt.Errorf("invalid logs from transaction %s: %w", tx.Hash, err) } - seenTxs[tx.Hash.String()] = true + seenTxs[tx.Hash] = true } if err := gs.ChainConfig.Validate(); err != nil { diff --git a/x/evm/types/genesis_test.go b/x/evm/types/genesis_test.go index 1413ae07..3b1041d4 100644 --- a/x/evm/types/genesis_test.go +++ b/x/evm/types/genesis_test.go @@ -3,18 +3,36 @@ package types import ( "testing" - "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" ethcmn "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" - ethcrypto "github.com/ethereum/go-ethereum/crypto" "github.com/cosmos/ethermint/crypto/ethsecp256k1" ) -var address = ethcmn.BytesToAddress([]byte{1, 2, 3, 4, 5}) +type GenesisTestSuite struct { + suite.Suite -func TestValidateGenesisAccount(t *testing.T) { + address ethcmn.Address + hash ethcmn.Hash + code string +} + +func (suite *GenesisTestSuite) SetupTest() { + priv, err := ethsecp256k1.GenerateKey() + suite.Require().NoError(err) + + suite.address = ethcmn.BytesToAddress(priv.PubKey().Address().Bytes()) + suite.hash = ethcmn.BytesToHash([]byte("hash")) + suite.code = ethcmn.Bytes2Hex([]byte{1, 2, 3}) +} + +func TestGenesisTestSuite(t *testing.T) { + suite.Run(t, new(GenesisTestSuite)) +} + +func (suite *GenesisTestSuite) TestValidateGenesisAccount() { testCases := []struct { name string genesisAccount GenesisAccount @@ -23,10 +41,10 @@ func TestValidateGenesisAccount(t *testing.T) { { "valid genesis account", GenesisAccount{ - Address: address.String(), - Code: []byte{1, 2, 3}, + Address: suite.address.String(), + Code: suite.code, Storage: Storage{ - NewState(ethcmn.BytesToHash([]byte{1, 2, 3}), ethcmn.BytesToHash([]byte{1, 2, 3})), + NewState(suite.hash, suite.hash), }, }, true, @@ -41,8 +59,8 @@ func TestValidateGenesisAccount(t *testing.T) { { "empty code bytes", GenesisAccount{ - Address: address.String(), - Code: []byte{}, + Address: suite.address.String(), + Code: "", }, false, }, @@ -52,18 +70,14 @@ func TestValidateGenesisAccount(t *testing.T) { tc := tc err := tc.genesisAccount.Validate() if tc.expPass { - require.NoError(t, err, tc.name) + suite.Require().NoError(err, tc.name) } else { - require.Error(t, err, tc.name) + suite.Require().Error(err, tc.name) } } } -func TestValidateGenesis(t *testing.T) { - priv, err := ethsecp256k1.GenerateKey() - require.NoError(t, err) - addr := ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey) - +func (suite *GenesisTestSuite) TestValidateGenesis() { testCases := []struct { name string genState GenesisState @@ -79,25 +93,25 @@ func TestValidateGenesis(t *testing.T) { genState: GenesisState{ Accounts: []GenesisAccount{ { - Address: address.String(), - Code: []byte{1, 2, 3}, + Address: suite.address.String(), + Code: suite.code, Storage: Storage{ - {Key: ethcmn.BytesToHash([]byte{1, 2, 3})}, + {Key: suite.hash.String()}, }, }, }, TxsLogs: []TransactionLogs{ { - Hash: ethcmn.BytesToHash([]byte("tx_hash")), + Hash: suite.hash.String(), Logs: []*ethtypes.Log{ { - Address: addr, - Topics: []ethcmn.Hash{ethcmn.BytesToHash([]byte("topic"))}, + Address: suite.address, + Topics: []ethcmn.Hash{suite.hash}, Data: []byte("data"), BlockNumber: 1, - TxHash: ethcmn.BytesToHash([]byte("tx_hash")), + TxHash: suite.hash, TxIndex: 1, - BlockHash: ethcmn.BytesToHash([]byte("block_hash")), + BlockHash: suite.hash, Index: 1, Removed: false, }, @@ -130,17 +144,17 @@ func TestValidateGenesis(t *testing.T) { genState: GenesisState{ Accounts: []GenesisAccount{ { - Address: address.String(), - Code: []byte{1, 2, 3}, + Address: suite.address.String(), + Code: suite.code, Storage: Storage{ - NewState(ethcmn.BytesToHash([]byte{1, 2, 3}), ethcmn.BytesToHash([]byte{1, 2, 3})), + NewState(suite.hash, suite.hash), }, }, { - Address: address.String(), - Code: []byte{1, 2, 3}, + Address: suite.address.String(), + Code: suite.code, Storage: Storage{ - NewState(ethcmn.BytesToHash([]byte{1, 2, 3}), ethcmn.BytesToHash([]byte{1, 2, 3})), + NewState(suite.hash, suite.hash), }, }, }, @@ -152,41 +166,41 @@ func TestValidateGenesis(t *testing.T) { genState: GenesisState{ Accounts: []GenesisAccount{ { - Address: address.String(), - Code: []byte{1, 2, 3}, + Address: suite.address.String(), + Code: suite.code, Storage: Storage{ - {Key: ethcmn.BytesToHash([]byte{1, 2, 3})}, + {Key: suite.hash.String()}, }, }, }, TxsLogs: []TransactionLogs{ { - Hash: ethcmn.BytesToHash([]byte("tx_hash")), + Hash: suite.hash.String(), Logs: []*ethtypes.Log{ { - Address: addr, - Topics: []ethcmn.Hash{ethcmn.BytesToHash([]byte("topic"))}, + Address: suite.address, + Topics: []ethcmn.Hash{suite.hash}, Data: []byte("data"), BlockNumber: 1, - TxHash: ethcmn.BytesToHash([]byte("tx_hash")), + TxHash: suite.hash, TxIndex: 1, - BlockHash: ethcmn.BytesToHash([]byte("block_hash")), + BlockHash: suite.hash, Index: 1, Removed: false, }, }, }, { - Hash: ethcmn.BytesToHash([]byte("tx_hash")), + Hash: suite.hash.String(), Logs: []*ethtypes.Log{ { - Address: addr, - Topics: []ethcmn.Hash{ethcmn.BytesToHash([]byte("topic"))}, + Address: suite.address, + Topics: []ethcmn.Hash{suite.hash}, Data: []byte("data"), BlockNumber: 1, - TxHash: ethcmn.BytesToHash([]byte("tx_hash")), + TxHash: suite.hash, TxIndex: 1, - BlockHash: ethcmn.BytesToHash([]byte("block_hash")), + BlockHash: suite.hash, Index: 1, Removed: false, }, @@ -201,10 +215,10 @@ func TestValidateGenesis(t *testing.T) { genState: GenesisState{ Accounts: []GenesisAccount{ { - Address: address.String(), - Code: []byte{1, 2, 3}, + Address: suite.address.String(), + Code: suite.code, Storage: Storage{ - {Key: ethcmn.BytesToHash([]byte{1, 2, 3})}, + {Key: suite.hash.String()}, }, }, }, @@ -234,9 +248,9 @@ func TestValidateGenesis(t *testing.T) { tc := tc err := tc.genState.Validate() if tc.expPass { - require.NoError(t, err, tc.name) + suite.Require().NoError(err, tc.name) } else { - require.Error(t, err, tc.name) + suite.Require().Error(err, tc.name) } } } diff --git a/x/evm/types/logs.go b/x/evm/types/logs.go index 9694ddbe..aae545d3 100644 --- a/x/evm/types/logs.go +++ b/x/evm/types/logs.go @@ -1,10 +1,10 @@ package types import ( - "bytes" "errors" "fmt" + ethermint "github.com/cosmos/ethermint/types" ethcmn "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" ) @@ -13,14 +13,14 @@ import ( // with a given hash. It it used for import/export data as transactions are not persisted // on blockchain state after an upgrade. type TransactionLogs struct { - Hash ethcmn.Hash `json:"hash"` + Hash string `json:"hash"` Logs []*ethtypes.Log `json:"logs"` } // NewTransactionLogs creates a new NewTransactionLogs instance. -func NewTransactionLogs(hash ethcmn.Hash, logs []*ethtypes.Log) TransactionLogs { +func NewTransactionLogs(hash ethcmn.Hash, logs []*ethtypes.Log) TransactionLogs { // nolint: interfacer return TransactionLogs{ - Hash: hash, + Hash: hash.String(), Logs: logs, } } @@ -39,16 +39,16 @@ func UnmarshalLogs(in []byte) ([]*ethtypes.Log, error) { // Validate performs a basic validation of a GenesisAccount fields. func (tx TransactionLogs) Validate() error { - if bytes.Equal(tx.Hash.Bytes(), ethcmn.Hash{}.Bytes()) { - return fmt.Errorf("hash cannot be the empty %s", tx.Hash.String()) + if ethermint.IsEmptyHash(tx.Hash) { + return fmt.Errorf("hash cannot be the empty %s", tx.Hash) } for i, log := range tx.Logs { if err := ValidateLog(log); err != nil { return fmt.Errorf("invalid log %d: %w", i, err) } - if !bytes.Equal(log.TxHash.Bytes(), tx.Hash.Bytes()) { - return fmt.Errorf("log tx hash mismatch (%s ≠ %s)", log.TxHash.String(), tx.Hash.String()) + if log.TxHash.String() != tx.Hash { + return fmt.Errorf("log tx hash mismatch (%s ≠ %s)", log.TxHash.String(), tx.Hash) } } return nil @@ -59,16 +59,16 @@ func ValidateLog(log *ethtypes.Log) error { if log == nil { return errors.New("log cannot be nil") } - if bytes.Equal(log.Address.Bytes(), ethcmn.Address{}.Bytes()) { + if ethermint.IsZeroAddress(log.Address.String()) { return fmt.Errorf("log address cannot be empty %s", log.Address.String()) } - if bytes.Equal(log.BlockHash.Bytes(), ethcmn.Hash{}.Bytes()) { + if ethermint.IsEmptyHash(log.BlockHash.String()) { return fmt.Errorf("block hash cannot be the empty %s", log.BlockHash.String()) } if log.BlockNumber == 0 { return errors.New("block number cannot be zero") } - if bytes.Equal(log.TxHash.Bytes(), ethcmn.Hash{}.Bytes()) { + if ethermint.IsEmptyHash(log.TxHash.String()) { return fmt.Errorf("tx hash cannot be the empty %s", log.TxHash.String()) } return nil diff --git a/x/evm/types/logs_test.go b/x/evm/types/logs_test.go index 6fd38b24..314f8dac 100644 --- a/x/evm/types/logs_test.go +++ b/x/evm/types/logs_test.go @@ -1,21 +1,11 @@ package types import ( - "testing" - - "github.com/cosmos/ethermint/crypto/ethsecp256k1" - "github.com/stretchr/testify/require" - ethcmn "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" - ethcrypto "github.com/ethereum/go-ethereum/crypto" ) -func TestTransactionLogsValidate(t *testing.T) { - priv, err := ethsecp256k1.GenerateKey() - require.NoError(t, err) - addr := ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey) - +func (suite *GenesisTestSuite) TestTransactionLogsValidate() { testCases := []struct { name string txLogs TransactionLogs @@ -24,16 +14,16 @@ func TestTransactionLogsValidate(t *testing.T) { { "valid log", TransactionLogs{ - Hash: ethcmn.BytesToHash([]byte("tx_hash")), + Hash: suite.hash.String(), Logs: []*ethtypes.Log{ { - Address: addr, + Address: suite.address, Topics: []ethcmn.Hash{ethcmn.BytesToHash([]byte("topic"))}, Data: []byte("data"), BlockNumber: 1, - TxHash: ethcmn.BytesToHash([]byte("tx_hash")), + TxHash: suite.hash, TxIndex: 1, - BlockHash: ethcmn.BytesToHash([]byte("block_hash")), + BlockHash: suite.hash, Index: 1, Removed: false, }, @@ -44,14 +34,14 @@ func TestTransactionLogsValidate(t *testing.T) { { "empty hash", TransactionLogs{ - Hash: ethcmn.Hash{}, + Hash: ethcmn.Hash{}.String(), }, false, }, { "invalid log", TransactionLogs{ - Hash: ethcmn.BytesToHash([]byte("tx_hash")), + Hash: suite.hash.String(), Logs: []*ethtypes.Log{nil}, }, false, @@ -59,16 +49,16 @@ func TestTransactionLogsValidate(t *testing.T) { { "hash mismatch log", TransactionLogs{ - Hash: ethcmn.BytesToHash([]byte("tx_hash")), + Hash: suite.hash.String(), Logs: []*ethtypes.Log{ { - Address: addr, + Address: suite.address, Topics: []ethcmn.Hash{ethcmn.BytesToHash([]byte("topic"))}, Data: []byte("data"), BlockNumber: 1, TxHash: ethcmn.BytesToHash([]byte("other_hash")), TxIndex: 1, - BlockHash: ethcmn.BytesToHash([]byte("block_hash")), + BlockHash: suite.hash, Index: 1, Removed: false, }, @@ -82,18 +72,14 @@ func TestTransactionLogsValidate(t *testing.T) { tc := tc err := tc.txLogs.Validate() if tc.expPass { - require.NoError(t, err, tc.name) + suite.Require().NoError(err, tc.name) } else { - require.Error(t, err, tc.name) + suite.Require().Error(err, tc.name) } } } -func TestValidateLog(t *testing.T) { - priv, err := ethsecp256k1.GenerateKey() - require.NoError(t, err) - addr := ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey) - +func (suite *GenesisTestSuite) TestValidateLog() { testCases := []struct { name string log *ethtypes.Log @@ -102,13 +88,13 @@ func TestValidateLog(t *testing.T) { { "valid log", ðtypes.Log{ - Address: addr, + Address: suite.address, Topics: []ethcmn.Hash{ethcmn.BytesToHash([]byte("topic"))}, Data: []byte("data"), BlockNumber: 1, - TxHash: ethcmn.BytesToHash([]byte("tx_hash")), + TxHash: suite.hash, TxIndex: 1, - BlockHash: ethcmn.BytesToHash([]byte("block_hash")), + BlockHash: suite.hash, Index: 1, Removed: false, }, @@ -127,7 +113,7 @@ func TestValidateLog(t *testing.T) { { "empty block hash", ðtypes.Log{ - Address: addr, + Address: suite.address, BlockHash: ethcmn.Hash{}, }, false, @@ -135,8 +121,8 @@ func TestValidateLog(t *testing.T) { { "zero block number", ðtypes.Log{ - Address: addr, - BlockHash: ethcmn.BytesToHash([]byte("block_hash")), + Address: suite.address, + BlockHash: suite.hash, BlockNumber: 0, }, false, @@ -144,8 +130,8 @@ func TestValidateLog(t *testing.T) { { "empty tx hash", ðtypes.Log{ - Address: addr, - BlockHash: ethcmn.BytesToHash([]byte("block_hash")), + Address: suite.address, + BlockHash: suite.hash, BlockNumber: 1, TxHash: ethcmn.Hash{}, }, @@ -157,9 +143,9 @@ func TestValidateLog(t *testing.T) { tc := tc err := ValidateLog(tc.log) if tc.expPass { - require.NoError(t, err, tc.name) + suite.Require().NoError(err, tc.name) } else { - require.Error(t, err, tc.name) + suite.Require().Error(err, tc.name) } } } diff --git a/x/evm/types/msg.go b/x/evm/types/msg.go index a35d63d0..d927c48d 100644 --- a/x/evm/types/msg.go +++ b/x/evm/types/msg.go @@ -9,6 +9,7 @@ import ( "sync/atomic" "github.com/cosmos/ethermint/types" + "gopkg.in/yaml.v2" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" @@ -147,37 +148,43 @@ func NewMsgEthereumTxContract( } func newMsgEthereumTx( - nonce uint64, to *ethcmn.Address, amount *big.Int, + nonce uint64, to *ethcmn.Address, amount *big.Int, // nolint: interfacer gasLimit uint64, gasPrice *big.Int, payload []byte, ) MsgEthereumTx { if len(payload) > 0 { payload = ethcmn.CopyBytes(payload) } + var recipient *Recipient + if to != nil { + recipient = &Recipient{Address: to.String()} + } + txData := TxData{ AccountNonce: nonce, - Recipient: to, + Recipient: recipient, Payload: payload, GasLimit: gasLimit, - Amount: new(big.Int), - Price: new(big.Int), - V: new(big.Int), - R: new(big.Int), - S: new(big.Int), + Amount: sdk.ZeroInt(), + Price: sdk.ZeroInt(), + V: []byte{}, + R: []byte{}, + S: []byte{}, } if amount != nil { - txData.Amount.Set(amount) + txData.Amount = sdk.NewIntFromBigInt(amount) } if gasPrice != nil { - txData.Price.Set(gasPrice) + txData.Price = sdk.NewIntFromBigInt(gasPrice) } return MsgEthereumTx{Data: txData} } func (msg MsgEthereumTx) String() string { - return msg.Data.String() + out, _ := yaml.Marshal(msg.Data) + return string(out) } // Route returns the route value of an MsgEthereumTx. @@ -189,16 +196,16 @@ func (msg MsgEthereumTx) Type() string { return TypeMsgEthereumTx } // ValidateBasic implements the sdk.Msg interface. It performs basic validation // checks of a Transaction. If returns an error if validation fails. func (msg MsgEthereumTx) ValidateBasic() error { - if msg.Data.Price.Cmp(big.NewInt(0)) == 0 { + if msg.Data.Price.IsZero() { return sdkerrors.Wrapf(types.ErrInvalidValue, "gas price cannot be 0") } - if msg.Data.Price.Sign() == -1 { + if msg.Data.Price.IsNegative() { return sdkerrors.Wrapf(types.ErrInvalidValue, "gas price cannot be negative %s", msg.Data.Price) } // Amount can be 0 - if msg.Data.Amount.Sign() == -1 { + if msg.Data.Amount.IsNegative() { return sdkerrors.Wrapf(types.ErrInvalidValue, "amount cannot be negative %s", msg.Data.Amount) } @@ -208,7 +215,12 @@ func (msg MsgEthereumTx) ValidateBasic() error { // To returns the recipient address of the transaction. It returns nil if the // transaction is a contract creation. func (msg MsgEthereumTx) To() *ethcmn.Address { - return msg.Data.Recipient + if msg.Data.Recipient == nil { + return nil + } + + recipient := ethcmn.HexToAddress(msg.Data.Recipient.Address) + return &recipient } // GetMsgs returns a single MsgEthereumTx as an sdk.Msg. @@ -242,10 +254,10 @@ func (msg MsgEthereumTx) GetSignBytes() []byte { func (msg MsgEthereumTx) RLPSignBytes(chainID *big.Int) ethcmn.Hash { return rlpHash([]interface{}{ msg.Data.AccountNonce, - msg.Data.Price, + msg.Data.Price.BigInt(), msg.Data.GasLimit, - msg.Data.Recipient, - msg.Data.Amount, + msg.To(), + msg.Data.Amount.BigInt(), msg.Data.Payload, chainID, uint(0), uint(0), }) @@ -253,7 +265,39 @@ func (msg MsgEthereumTx) RLPSignBytes(chainID *big.Int) ethcmn.Hash { // EncodeRLP implements the rlp.Encoder interface. func (msg *MsgEthereumTx) EncodeRLP(w io.Writer) error { - return rlp.Encode(w, &msg.Data) + var hash ethcmn.Hash + if len(msg.Data.Hash) > 0 { + hash = ethcmn.HexToHash(msg.Data.Hash) + } + + data := struct { + AccountNonce uint64 + Price *big.Int `json:"gasPrice"` + GasLimit uint64 `json:"gas"` + Recipient *ethcmn.Address `json:"to" rlp:"nil"` // nil means contract creation + Amount *big.Int `json:"value"` + Payload []byte `json:"input"` + + // signature values + V *big.Int `json:"v"` + R *big.Int `json:"r"` + S *big.Int `json:"s"` + + // hash is only used when marshaling to JSON + Hash *ethcmn.Hash `json:"hash" rlp:"-"` + }{ + AccountNonce: msg.Data.AccountNonce, + Price: msg.Data.Price.BigInt(), + GasLimit: msg.Data.GasLimit, + Recipient: msg.To(), + Amount: msg.Data.Amount.BigInt(), + Payload: msg.Data.Payload, + V: new(big.Int).SetBytes(msg.Data.V), + R: new(big.Int).SetBytes(msg.Data.R), + S: new(big.Int).SetBytes(msg.Data.S), + Hash: &hash, + } + return rlp.Encode(w, data) } // DecodeRLP implements the rlp.Decoder interface. @@ -264,10 +308,50 @@ func (msg *MsgEthereumTx) DecodeRLP(s *rlp.Stream) error { return err } - if err := s.Decode(&msg.Data); err != nil { + var data struct { + AccountNonce uint64 + Price *big.Int `json:"gasPrice"` + GasLimit uint64 `json:"gas"` + Recipient *ethcmn.Address `json:"to" rlp:"nil"` // nil means contract creation + Amount *big.Int `json:"value"` + Payload []byte `json:"input"` + + // signature values + V *big.Int `json:"v"` + R *big.Int `json:"r"` + S *big.Int `json:"s"` + + // hash is only used when marshaling to JSON + Hash *ethcmn.Hash `json:"hash" rlp:"-"` + } + + if err := s.Decode(&data); err != nil { return err } + var hash string + if data.Hash != nil { + hash = data.Hash.String() + } + + var recipient *Recipient + if data.Recipient != nil { + recipient = &Recipient{Address: data.Recipient.String()} + } + + msg.Data = TxData{ + AccountNonce: data.AccountNonce, + Price: sdk.NewIntFromBigInt(data.Price), + GasLimit: data.GasLimit, + Recipient: recipient, + Amount: sdk.NewIntFromBigInt(data.Amount), + Payload: data.Payload, + V: data.V.Bytes(), + R: data.R.Bytes(), + S: data.S.Bytes(), + Hash: hash, + } + msg.size.Store(ethcmn.StorageSize(rlp.ListSize(size))) return nil } @@ -302,15 +386,16 @@ func (msg *MsgEthereumTx) Sign(chainID *big.Int, priv *ecdsa.PrivateKey) error { v.Add(v, chainIDMul) } - msg.Data.V = v - msg.Data.R = r - msg.Data.S = s + msg.Data.V = v.Bytes() + msg.Data.R = r.Bytes() + msg.Data.S = s.Bytes() return nil } // VerifySig attempts to verify a Transaction's signature for a given chainID. // A derived address is returned upon success or an error if recovery fails. func (msg *MsgEthereumTx) VerifySig(chainID *big.Int) (ethcmn.Address, error) { + v, r, s := msg.RawSignatureValues() signer := ethtypes.NewEIP155Signer(chainID) if sc := msg.from.Load(); sc != nil { @@ -328,11 +413,11 @@ func (msg *MsgEthereumTx) VerifySig(chainID *big.Int) (ethcmn.Address, error) { } chainIDMul := new(big.Int).Mul(chainID, big.NewInt(2)) - V := new(big.Int).Sub(msg.Data.V, chainIDMul) + V := new(big.Int).Sub(v, chainIDMul) V.Sub(V, big8) sigHash := msg.RLPSignBytes(chainID) - sender, err := recoverEthSig(msg.Data.R, msg.Data.S, V, sigHash) + sender, err := recoverEthSig(r, s, V, sigHash) if err != nil { return ethcmn.Address{}, err } @@ -348,25 +433,30 @@ func (msg MsgEthereumTx) GetGas() uint64 { // Fee returns gasprice * gaslimit. func (msg MsgEthereumTx) Fee() *big.Int { - return new(big.Int).Mul(msg.Data.Price, new(big.Int).SetUint64(msg.Data.GasLimit)) + gasPrice := msg.Data.Price.BigInt() + gasLimit := new(big.Int).SetUint64(msg.Data.GasLimit) + return new(big.Int).Mul(gasPrice, gasLimit) } // ChainID returns which chain id this transaction was signed for (if at all) func (msg *MsgEthereumTx) ChainID() *big.Int { - return deriveChainID(msg.Data.V) + v := new(big.Int).SetBytes(msg.Data.V) + return deriveChainID(v) } // Cost returns amount + gasprice * gaslimit. func (msg MsgEthereumTx) Cost() *big.Int { total := msg.Fee() - total.Add(total, msg.Data.Amount) + total.Add(total, msg.Data.Amount.BigInt()) return total } // RawSignatureValues returns the V, R, S signature values of the transaction. // The return values should not be modified by the caller. func (msg MsgEthereumTx) RawSignatureValues() (v, r, s *big.Int) { - return msg.Data.V, msg.Data.R, msg.Data.S + return new(big.Int).SetBytes(msg.Data.V), + new(big.Int).SetBytes(msg.Data.R), + new(big.Int).SetBytes(msg.Data.S) } // From loads the ethereum sender address from the sigcache and returns an diff --git a/x/evm/types/msg_test.go b/x/evm/types/msg_test.go index 5f9d0296..6b48247c 100644 --- a/x/evm/types/msg_test.go +++ b/x/evm/types/msg_test.go @@ -92,7 +92,8 @@ func TestMsgEthereumTx(t *testing.T) { msg := NewMsgEthereumTx(0, &addr, nil, 100000, nil, []byte("test")) require.NotNil(t, msg) - require.Equal(t, *msg.Data.Recipient, addr) + require.NotNil(t, msg.Data.Recipient) + require.Equal(t, msg.Data.Recipient.Address, addr.String()) require.Equal(t, msg.Route(), RouterKey) require.Equal(t, msg.Type(), TypeMsgEthereumTx) require.NotNil(t, msg.To()) @@ -102,7 +103,7 @@ func TestMsgEthereumTx(t *testing.T) { msg = NewMsgEthereumTxContract(0, nil, 100000, nil, []byte("test")) require.NotNil(t, msg) - require.Nil(t, msg.Data.Recipient) + require.Empty(t, msg.Data.Recipient) require.Nil(t, msg.To()) } diff --git a/x/evm/types/params.go b/x/evm/types/params.go index 7024a4a1..2cae0625 100644 --- a/x/evm/types/params.go +++ b/x/evm/types/params.go @@ -41,11 +41,11 @@ type Params struct { // EnableCall toggles state transitions that use the vm.Call function EnableCall bool `json:"enable_call" yaml:"enable_call"` // ExtraEIPs defines the additional EIPs for the vm.Config - ExtraEIPs []int `json:"extra_eips" yaml:"extra_eips"` + ExtraEIPs []int64 `json:"extra_eips" yaml:"extra_eips"` } // NewParams creates a new Params instance -func NewParams(evmDenom string, enableCreate, enableCall bool, extraEIPs ...int) Params { +func NewParams(evmDenom string, enableCreate, enableCall bool, extraEIPs ...int64) Params { return Params{ EvmDenom: evmDenom, EnableCreate: enableCreate, @@ -60,7 +60,7 @@ func DefaultParams() Params { EvmDenom: ethermint.AttoPhoton, EnableCreate: true, EnableCall: true, - ExtraEIPs: []int(nil), // TODO: define default values + ExtraEIPs: []int64(nil), // TODO: define default values } } @@ -107,13 +107,13 @@ func validateBool(i interface{}) error { } func validateEIPs(i interface{}) error { - eips, ok := i.([]int) + eips, ok := i.([]int64) if !ok { return fmt.Errorf("invalid EIP slice type: %T", i) } for _, eip := range eips { - if !vm.ValidEip(eip) { + if !vm.ValidEip(int(eip)) { return fmt.Errorf("EIP %d is not activateable", eip) } } diff --git a/x/evm/types/params_test.go b/x/evm/types/params_test.go index 83332fd0..aa7cf48c 100644 --- a/x/evm/types/params_test.go +++ b/x/evm/types/params_test.go @@ -34,7 +34,7 @@ func TestParamsValidate(t *testing.T) { "invalid eip", Params{ EvmDenom: "stake", - ExtraEIPs: []int{1}, + ExtraEIPs: []int64{1}, }, true, }, @@ -57,7 +57,7 @@ func TestParamsValidatePriv(t *testing.T) { require.Error(t, validateBool("")) require.NoError(t, validateBool(true)) require.Error(t, validateEIPs("")) - require.NoError(t, validateEIPs([]int{1884})) + require.NoError(t, validateEIPs([]int64{1884})) } func TestParams_String(t *testing.T) { diff --git a/x/evm/types/querier.go b/x/evm/types/querier.go index df0dd506..5fdeb7c5 100644 --- a/x/evm/types/querier.go +++ b/x/evm/types/querier.go @@ -18,7 +18,6 @@ const ( QueryBloom = "bloom" QueryLogs = "logs" QueryAccount = "account" - QueryExportAccount = "exportAccount" ) // QueryResBalance is response type for balance query diff --git a/x/evm/types/state_object.go b/x/evm/types/state_object.go index d9c4d7e1..a2dbce62 100644 --- a/x/evm/types/state_object.go +++ b/x/evm/types/state_object.go @@ -10,7 +10,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" authexported "github.com/cosmos/cosmos-sdk/x/auth/exported" - "github.com/cosmos/ethermint/types" + ethermint "github.com/cosmos/ethermint/types" ethcmn "github.com/ethereum/go-ethereum/common" ethstate "github.com/ethereum/go-ethereum/core/state" @@ -53,7 +53,7 @@ type StateObject interface { // Account values can be accessed and modified through the object. // Finally, call CommitTrie to write the modified storage trie into a database. type stateObject struct { - code types.Code // contract bytecode, which gets set when code is loaded + code ethermint.Code // contract bytecode, which gets set when code is loaded // State objects are used by the consensus core and VM which are // unable to deal with database-level errors. Any error that occurs // during a database read is memoized here and will eventually be returned @@ -64,7 +64,7 @@ type stateObject struct { // DB error dbErr error stateDB *CommitStateDB - account *types.EthAccount + account *ethermint.EthAccount keyToOriginStorageIndex map[ethcmn.Hash]int keyToDirtyStorageIndex map[ethcmn.Hash]int @@ -82,7 +82,7 @@ type stateObject struct { func newStateObject(db *CommitStateDB, accProto authexported.Account) *stateObject { // func newStateObject(db *CommitStateDB, accProto authexported.Account, balance sdk.Int) *stateObject { - ethermintAccount, ok := accProto.(*types.EthAccount) + ethermintAccount, ok := accProto.(*ethermint.EthAccount) if !ok { panic(fmt.Sprintf("invalid account type for state object: %T", accProto)) } @@ -132,7 +132,7 @@ func (so *stateObject) SetState(db ethstate.Database, key, value ethcmn.Hash) { func (so *stateObject) setState(key, value ethcmn.Hash) { idx, ok := so.keyToDirtyStorageIndex[key] if ok { - so.dirtyStorage[idx].Value = value + so.dirtyStorage[idx].Value = value.String() return } @@ -248,21 +248,24 @@ func (so *stateObject) commitState() { for _, state := range so.dirtyStorage { // NOTE: key is already prefixed from GetStorageByAddressKey + key := ethcmn.HexToHash(state.Key) + value := ethcmn.HexToHash(state.Value) + // delete empty values from the store - if (state.Value == ethcmn.Hash{}) { - store.Delete(state.Key.Bytes()) + if ethermint.IsEmptyHash(state.Value) { + store.Delete(key.Bytes()) } - delete(so.keyToDirtyStorageIndex, state.Key) + delete(so.keyToDirtyStorageIndex, key) // skip no-op changes, persist actual changes - idx, ok := so.keyToOriginStorageIndex[state.Key] + idx, ok := so.keyToOriginStorageIndex[key] if !ok { continue } - if (state.Value == ethcmn.Hash{}) { - delete(so.keyToOriginStorageIndex, state.Key) + if ethermint.IsEmptyHash(state.Value) { + delete(so.keyToOriginStorageIndex, key) continue } @@ -271,7 +274,7 @@ func (so *stateObject) commitState() { } so.originStorage[idx].Value = state.Value - store.Set(state.Key.Bytes(), state.Value.Bytes()) + store.Set(key.Bytes(), value.Bytes()) } // clean storage as all entries are dirty so.dirtyStorage = Storage{} @@ -348,7 +351,8 @@ func (so *stateObject) GetState(db ethstate.Database, key ethcmn.Hash) ethcmn.Ha // if we have a dirty value for this state entry, return it idx, dirty := so.keyToDirtyStorageIndex[prefixKey] if dirty { - return so.dirtyStorage[idx].Value + value := ethcmn.HexToHash(so.dirtyStorage[idx].Value) + return value } // otherwise return the entry's original value @@ -365,23 +369,26 @@ func (so *stateObject) GetCommittedState(_ ethstate.Database, key ethcmn.Hash) e // if we have the original value cached, return that idx, cached := so.keyToOriginStorageIndex[prefixKey] if cached { - return so.originStorage[idx].Value + value := ethcmn.HexToHash(so.originStorage[idx].Value) + return value } // otherwise load the value from the KVStore state := NewState(prefixKey, ethcmn.Hash{}) + value := ethcmn.Hash{} ctx := so.stateDB.ctx store := prefix.NewStore(ctx.KVStore(so.stateDB.storeKey), AddressStoragePrefix(so.Address())) rawValue := store.Get(prefixKey.Bytes()) if len(rawValue) > 0 { - state.Value.SetBytes(rawValue) + value.SetBytes(rawValue) + state.Value = value.String() } so.originStorage = append(so.originStorage, state) so.keyToOriginStorageIndex[prefixKey] = len(so.originStorage) - 1 - return state.Value + return value } // ---------------------------------------------------------------------------- diff --git a/x/evm/types/state_transition.go b/x/evm/types/state_transition.go index d2993012..79332069 100644 --- a/x/evm/types/state_transition.go +++ b/x/evm/types/state_transition.go @@ -77,7 +77,7 @@ func (st StateTransition) newEVM( gasLimit uint64, gasPrice *big.Int, config ChainConfig, - extraEIPs []int, + extraEIPs []int64, ) *vm.EVM { // Create contexts for evm @@ -97,8 +97,13 @@ func (st StateTransition) newEVM( GasPrice: gasPrice, } + eips := make([]int, len(extraEIPs)) + for i, eip := range extraEIPs { + eips[i] = int(eip) + } + vmConfig := vm.Config{ - ExtraEips: extraEIPs, + ExtraEips: eips, } return vm.NewEVM(blockCtx, txCtx, csdb, config.EthereumConfig(st.ChainID), vmConfig) diff --git a/x/evm/types/statedb.go b/x/evm/types/statedb.go index b3fcd588..2c6de2fe 100644 --- a/x/evm/types/statedb.go +++ b/x/evm/types/statedb.go @@ -834,7 +834,7 @@ func (csdb *CommitStateDB) ForEachStorage(addr ethcmn.Address, cb func(key, valu if idx, dirty := so.keyToDirtyStorageIndex[key]; dirty { // check if iteration stops - if cb(key, so.dirtyStorage[idx].Value) { + if cb(key, ethcmn.HexToHash(so.dirtyStorage[idx].Value)) { break } diff --git a/x/evm/types/statedb_test.go b/x/evm/types/statedb_test.go index 9c9eaadd..cce16082 100644 --- a/x/evm/types/statedb_test.go +++ b/x/evm/types/statedb_test.go @@ -646,7 +646,7 @@ func (suite *StateDBTestSuite) TestCommitStateDB_ForEachStorage() { name string malleate func() callback func(key, value ethcmn.Hash) (stop bool) - expValues []ethcmn.Hash + expValues []string }{ { "aggregate state", @@ -659,12 +659,12 @@ func (suite *StateDBTestSuite) TestCommitStateDB_ForEachStorage() { storage = append(storage, types.NewState(key, value)) return false }, - []ethcmn.Hash{ - ethcmn.BytesToHash([]byte("value0")), - ethcmn.BytesToHash([]byte("value1")), - ethcmn.BytesToHash([]byte("value2")), - ethcmn.BytesToHash([]byte("value3")), - ethcmn.BytesToHash([]byte("value4")), + []string{ + ethcmn.BytesToHash([]byte("value0")).String(), + ethcmn.BytesToHash([]byte("value1")).String(), + ethcmn.BytesToHash([]byte("value2")).String(), + ethcmn.BytesToHash([]byte("value3")).String(), + ethcmn.BytesToHash([]byte("value4")).String(), }, }, { @@ -680,8 +680,8 @@ func (suite *StateDBTestSuite) TestCommitStateDB_ForEachStorage() { } return false }, - []ethcmn.Hash{ - ethcmn.BytesToHash([]byte("filtervalue")), + []string{ + ethcmn.BytesToHash([]byte("filtervalue")).String(), }, }, } @@ -696,7 +696,7 @@ func (suite *StateDBTestSuite) TestCommitStateDB_ForEachStorage() { suite.Require().NoError(err) suite.Require().Equal(len(tc.expValues), len(storage), fmt.Sprintf("Expected values:\n%v\nStorage Values\n%v", tc.expValues, storage)) - vals := make([]ethcmn.Hash, len(storage)) + vals := make([]string, len(storage)) for i := range storage { vals[i] = storage[i].Value } diff --git a/x/evm/types/storage.go b/x/evm/types/storage.go index cf7afaf2..dbf353e8 100644 --- a/x/evm/types/storage.go +++ b/x/evm/types/storage.go @@ -1,11 +1,12 @@ package types import ( - "bytes" "fmt" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + ethermint "github.com/cosmos/ethermint/types" + ethcmn "github.com/ethereum/go-ethereum/common" ) @@ -17,15 +18,15 @@ type Storage []State func (s Storage) Validate() error { seenStorage := make(map[string]bool) for i, state := range s { - if seenStorage[state.Key.String()] { - return sdkerrors.Wrapf(ErrInvalidState, "duplicate state key %d", i) + if seenStorage[state.Key] { + return sdkerrors.Wrapf(ErrInvalidState, "duplicate state key %d: %s", i, state.Key) } if err := state.Validate(); err != nil { return err } - seenStorage[state.Key.String()] = true + seenStorage[state.Key] = true } return nil } @@ -34,7 +35,7 @@ func (s Storage) Validate() error { func (s Storage) String() string { var str string for _, state := range s { - str += fmt.Sprintf("%s: %s\n", state.Key.String(), state.Value.String()) + str += fmt.Sprintf("%s: %s\n", state.Key, state.Value) } return str @@ -50,13 +51,13 @@ func (s Storage) Copy() Storage { // State represents a single Storage key value pair item. type State struct { - Key ethcmn.Hash `json:"key"` - Value ethcmn.Hash `json:"value"` + Key string `json:"key"` + Value string `json:"value"` } // Validate performs a basic validation of the State fields. func (s State) Validate() error { - if bytes.Equal(s.Key.Bytes(), ethcmn.Hash{}.Bytes()) { + if ethermint.IsEmptyHash(s.Key) { return sdkerrors.Wrap(ErrInvalidState, "state key hash cannot be empty") } // NOTE: state value can be empty @@ -64,9 +65,9 @@ func (s State) Validate() error { } // NewState creates a new State instance -func NewState(key, value ethcmn.Hash) State { +func NewState(key, value ethcmn.Hash) State { // nolint: interfacer return State{ - Key: key, - Value: value, + Key: key.String(), + Value: value.String(), } } diff --git a/x/evm/types/storage_test.go b/x/evm/types/storage_test.go index b2300267..1d404994 100644 --- a/x/evm/types/storage_test.go +++ b/x/evm/types/storage_test.go @@ -3,8 +3,9 @@ package types import ( "testing" - ethcmn "github.com/ethereum/go-ethereum/common" "github.com/stretchr/testify/require" + + ethcmn "github.com/ethereum/go-ethereum/common" ) func TestStorageValidate(t *testing.T) { @@ -23,15 +24,15 @@ func TestStorageValidate(t *testing.T) { { "empty storage key bytes", Storage{ - {Key: ethcmn.Hash{}}, + {Key: ethcmn.Hash{}.String()}, }, false, }, { "duplicated storage key", Storage{ - {Key: ethcmn.BytesToHash([]byte{1, 2, 3})}, - {Key: ethcmn.BytesToHash([]byte{1, 2, 3})}, + {Key: ethcmn.BytesToHash([]byte{1, 2, 3}).String()}, + {Key: ethcmn.BytesToHash([]byte{1, 2, 3}).String()}, }, false, }, @@ -62,7 +63,7 @@ func TestStorageCopy(t *testing.T) { { "empty storage key value bytes", Storage{ - {Key: ethcmn.Hash{}, Value: ethcmn.Hash{}}, + {Key: ethcmn.Hash{}.String(), Value: ethcmn.Hash{}.String()}, }, }, { diff --git a/x/evm/types/tx_data.go b/x/evm/types/tx_data.go index 93f0b055..2381a41a 100644 --- a/x/evm/types/tx_data.go +++ b/x/evm/types/tx_data.go @@ -1,177 +1,29 @@ package types import ( - "fmt" - "math/big" - - "github.com/cosmos/ethermint/utils" - - ethcmn "github.com/ethereum/go-ethereum/common" + sdk "github.com/cosmos/cosmos-sdk/types" ) +// Recipient is a wrapper of the +type Recipient struct { + Address string +} + // TxData implements the Ethereum transaction data structure. It is used // solely as intended in Ethereum abiding by the protocol. type TxData struct { - AccountNonce uint64 `json:"nonce"` - Price *big.Int `json:"gasPrice"` - GasLimit uint64 `json:"gas"` - Recipient *ethcmn.Address `json:"to" rlp:"nil"` // nil means contract creation - Amount *big.Int `json:"value"` - Payload []byte `json:"input"` + AccountNonce uint64 `json:"nonce"` + Price sdk.Int `json:"gasPrice"` + GasLimit uint64 `json:"gas"` + Recipient *Recipient `json:"to" rlp:"nil"` // nil means contract creation + Amount sdk.Int `json:"value"` + Payload []byte `json:"input"` // signature values - V *big.Int `json:"v"` - R *big.Int `json:"r"` - S *big.Int `json:"s"` + V []byte `json:"v"` + R []byte `json:"r"` + S []byte `json:"s"` // hash is only used when marshaling to JSON - Hash *ethcmn.Hash `json:"hash" rlp:"-"` + Hash string `json:"hash" rlp:"-"` } - -// encodableTxData implements the Ethereum transaction data structure. It is used -// solely as intended in Ethereum abiding by the protocol. -type encodableTxData struct { - AccountNonce uint64 `json:"nonce"` - Price string `json:"gasPrice"` - GasLimit uint64 `json:"gas"` - Recipient *ethcmn.Address `json:"to" rlp:"nil"` // nil means contract creation - Amount string `json:"value"` - Payload []byte `json:"input"` - - // signature values - V string `json:"v"` - R string `json:"r"` - S string `json:"s"` - - // hash is only used when marshaling to JSON - Hash *ethcmn.Hash `json:"hash" rlp:"-"` -} - -func (td TxData) String() string { - if td.Recipient != nil { - return fmt.Sprintf("nonce=%d price=%s gasLimit=%d recipient=%s amount=%s data=0x%x v=%s r=%s s=%s", - td.AccountNonce, td.Price, td.GasLimit, td.Recipient.Hex(), td.Amount, td.Payload, td.V, td.R, td.S) - } - - return fmt.Sprintf("nonce=%d price=%s gasLimit=%d recipient=nil amount=%s data=0x%x v=%s r=%s s=%s", - td.AccountNonce, td.Price, td.GasLimit, td.Amount, td.Payload, td.V, td.R, td.S) -} - -// MarshalAmino defines custom encoding scheme for TxData -func (td TxData) MarshalAmino() ([]byte, error) { - gasPrice, err := utils.MarshalBigInt(td.Price) - if err != nil { - return nil, err - } - - amount, err := utils.MarshalBigInt(td.Amount) - if err != nil { - return nil, err - } - - v, err := utils.MarshalBigInt(td.V) - if err != nil { - return nil, err - } - - r, err := utils.MarshalBigInt(td.R) - if err != nil { - return nil, err - } - - s, err := utils.MarshalBigInt(td.S) - if err != nil { - return nil, err - } - - e := encodableTxData{ - AccountNonce: td.AccountNonce, - Price: gasPrice, - GasLimit: td.GasLimit, - Recipient: td.Recipient, - Amount: amount, - Payload: td.Payload, - V: v, - R: r, - S: s, - Hash: td.Hash, - } - - return ModuleCdc.MarshalBinaryBare(e) -} - -// UnmarshalAmino defines custom decoding scheme for TxData -func (td *TxData) UnmarshalAmino(data []byte) error { - var e encodableTxData - err := ModuleCdc.UnmarshalBinaryBare(data, &e) - if err != nil { - return err - } - - td.AccountNonce = e.AccountNonce - td.GasLimit = e.GasLimit - td.Recipient = e.Recipient - td.Payload = e.Payload - td.Hash = e.Hash - - price, err := utils.UnmarshalBigInt(e.Price) - if err != nil { - return err - } - - if td.Price != nil { - td.Price.Set(price) - } else { - td.Price = price - } - - amt, err := utils.UnmarshalBigInt(e.Amount) - if err != nil { - return err - } - - if td.Amount != nil { - td.Amount.Set(amt) - } else { - td.Amount = amt - } - - v, err := utils.UnmarshalBigInt(e.V) - if err != nil { - return err - } - - if td.V != nil { - td.V.Set(v) - } else { - td.V = v - } - - r, err := utils.UnmarshalBigInt(e.R) - if err != nil { - return err - } - - if td.R != nil { - td.R.Set(r) - } else { - td.R = r - } - - s, err := utils.UnmarshalBigInt(e.S) - if err != nil { - return err - } - - if td.S != nil { - td.S.Set(s) - } else { - td.S = s - } - - return nil -} - -// TODO: Implement JSON marshaling/ unmarshaling for this type - -// TODO: Implement YAML marshaling/ unmarshaling for this type diff --git a/x/evm/types/tx_data_test.go b/x/evm/types/tx_data_test.go deleted file mode 100644 index 4e938439..00000000 --- a/x/evm/types/tx_data_test.go +++ /dev/null @@ -1,56 +0,0 @@ -package types - -import ( - "math/big" - "testing" - - "github.com/stretchr/testify/require" - - ethcmn "github.com/ethereum/go-ethereum/common" -) - -func TestMarshalAndUnmarshalData(t *testing.T) { - addr := GenerateEthAddress() - hash := ethcmn.BigToHash(big.NewInt(2)) - - txData := TxData{ - AccountNonce: 2, - Price: big.NewInt(3), - GasLimit: 1, - Recipient: &addr, - Amount: big.NewInt(4), - Payload: []byte("test"), - V: big.NewInt(5), - R: big.NewInt(6), - S: big.NewInt(7), - Hash: &hash, - } - - bz, err := txData.MarshalAmino() - require.NoError(t, err) - require.NotNil(t, bz) - - var txData2 TxData - err = txData2.UnmarshalAmino(bz) - require.NoError(t, err) - - require.Equal(t, txData, txData2) -} - -func TestMsgEthereumTxAmino(t *testing.T) { - addr := GenerateEthAddress() - msg := NewMsgEthereumTx(5, &addr, big.NewInt(1), 100000, big.NewInt(3), []byte("test")) - - msg.Data.V = big.NewInt(1) - msg.Data.R = big.NewInt(2) - msg.Data.S = big.NewInt(3) - - raw, err := ModuleCdc.MarshalBinaryBare(msg) - require.NoError(t, err) - - var msg2 MsgEthereumTx - - err = ModuleCdc.UnmarshalBinaryBare(raw, &msg2) - require.NoError(t, err) - require.Equal(t, msg, msg2) -}