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
This commit is contained in:
parent
64ada18b01
commit
9cbb4dcf6d
11
CHANGELOG.md
11
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`.
|
||||
|
@ -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
|
||||
|
||||
|
@ -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{}) {
|
||||
|
@ -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\""
|
||||
];
|
||||
}
|
17
types/validation.go
Normal file
17
types/validation.go
Normal file
@ -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())
|
||||
}
|
55
types/validation_test.go
Normal file
55
types/validation_test.go
Normal file
@ -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)
|
||||
}
|
||||
}
|
@ -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,
|
||||
}
|
||||
|
||||
|
@ -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()},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
104
x/evm/keeper/msg_server.go
Normal file
104
x/evm/keeper/msg_server.go
Normal file
@ -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
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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},
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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 (
|
||||
@ -23,18 +24,18 @@ type (
|
||||
// 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"`
|
||||
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 {
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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())
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -18,7 +18,6 @@ const (
|
||||
QueryBloom = "bloom"
|
||||
QueryLogs = "logs"
|
||||
QueryAccount = "account"
|
||||
QueryExportAccount = "exportAccount"
|
||||
)
|
||||
|
||||
// QueryResBalance is response type for balance query
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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(),
|
||||
}
|
||||
}
|
||||
|
@ -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()},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -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"`
|
||||
Price sdk.Int `json:"gasPrice"`
|
||||
GasLimit uint64 `json:"gas"`
|
||||
Recipient *ethcmn.Address `json:"to" rlp:"nil"` // nil means contract creation
|
||||
Amount *big.Int `json:"value"`
|
||||
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
|
||||
|
@ -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)
|
||||
}
|
Loading…
Reference in New Issue
Block a user