From 16df7725c5640060fef44ded538ee78d3f10de24 Mon Sep 17 00:00:00 2001 From: Federico Kunze <31522760+fedekunze@users.noreply.github.com> Date: Mon, 18 May 2020 15:21:12 -0400 Subject: [PATCH] evm: add missing genesis fields and export genesis state logic (#255) * evm: export genesis state * x/evm: split keeper.go * x/evm: retrieve storage from address * changelog * fixes * add check for nil logs * update validation func * fixes * fix non-determinism * stop storage iteration * remove error return value * update changelog * fix test * lint --- CHANGELOG.md | 3 +- app/ethermint.go | 2 +- x/evm/alias.go | 7 +- x/evm/genesis.go | 51 +++++- x/evm/genesis_test.go | 15 ++ x/evm/handler.go | 10 +- x/evm/handler_test.go | 3 +- x/evm/keeper/keeper.go | 273 ++------------------------------ x/evm/keeper/keeper_test.go | 3 +- x/evm/keeper/statedb.go | 239 ++++++++++++++++++++++++++++ x/evm/module.go | 14 +- x/evm/types/expected_keepers.go | 1 + x/evm/types/genesis.go | 52 ++++-- x/evm/types/genesis_test.go | 44 +++-- x/evm/types/statedb.go | 11 +- 15 files changed, 411 insertions(+), 317 deletions(-) create mode 100644 x/evm/genesis_test.go create mode 100644 x/evm/keeper/statedb.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 892d7be2..ffad07ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,7 +52,8 @@ Ref: https://keepachangelog.com/en/1.0.0/ * [\#236](https://github.com/ChainSafe/ethermint/pull/236) Changes from upgrade [@fedekunze](https://github.com/fedekunze) * (`app/ante`) Moved `AnteHandler` implementation to `app/ante` * (keys) Marked `ExportEthKeyCommand` as **UNSAFE** - * (`x/evm`) Moved `BeginBlock` and `EndBlock` to `x/evm/abci.go` + * (x/evm) Moved `BeginBlock` and `EndBlock` to `x/evm/abci.go` +* (`x/evm`) [\#255](https://github.com/ChainSafe/ethermint/pull/255) Add missing `GenesisState` fields and support `ExportGenesis` functionality. * [\#272](https://github.com/ChainSafe/ethermint/pull/272) Add `Logger` for evm module. ### Features diff --git a/app/ethermint.go b/app/ethermint.go index a0c3841d..8343d72d 100644 --- a/app/ethermint.go +++ b/app/ethermint.go @@ -241,7 +241,7 @@ func NewEthermintApp( distr.NewAppModule(app.DistrKeeper, app.AccountKeeper, app.BankKeeper, app.SupplyKeeper, app.StakingKeeper), staking.NewAppModule(app.StakingKeeper, app.AccountKeeper, app.BankKeeper, app.SupplyKeeper), evidence.NewAppModule(app.EvidenceKeeper), - evm.NewAppModule(app.EvmKeeper), + evm.NewAppModule(app.EvmKeeper, app.AccountKeeper), ) // During begin block slashing happens after distr.BeginBlocker so that diff --git a/x/evm/alias.go b/x/evm/alias.go index 03ad6792..d5f5ba9b 100644 --- a/x/evm/alias.go +++ b/x/evm/alias.go @@ -27,8 +27,9 @@ const ( // nolint var ( - NewKeeper = keeper.NewKeeper - TxDecoder = types.TxDecoder + NewKeeper = keeper.NewKeeper + TxDecoder = types.TxDecoder + NewGenesisStorage = types.NewGenesisStorage ) //nolint @@ -36,4 +37,6 @@ type ( Keeper = keeper.Keeper QueryResAccount = types.QueryResAccount GenesisState = types.GenesisState + GenesisAccount = types.GenesisAccount + GenesisStorage = types.GenesisStorage ) diff --git a/x/evm/genesis.go b/x/evm/genesis.go index c97916f5..9adb1825 100644 --- a/x/evm/genesis.go +++ b/x/evm/genesis.go @@ -1,20 +1,61 @@ package evm import ( + "github.com/ethereum/go-ethereum/common" + sdk "github.com/cosmos/cosmos-sdk/types" + + emint "github.com/cosmos/ethermint/types" + "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, data GenesisState) []abci.ValidatorUpdate { - for _, record := range data.Accounts { - k.SetCode(ctx, record.Address, record.Code) - k.CreateGenesisAccount(ctx, record) + for _, account := range data.Accounts { + csdb := k.CommitStateDB.WithContext(ctx) + csdb.SetBalance(account.Address, account.Balance) + csdb.SetCode(account.Address, account.Code) + for _, storage := range account.Storage { + csdb.SetState(account.Address, storage.Key, storage.Value) + } } return []abci.ValidatorUpdate{} } // ExportGenesis exports genesis state -func ExportGenesis(ctx sdk.Context, _ Keeper) GenesisState { - return GenesisState{Accounts: nil} +func ExportGenesis(ctx sdk.Context, k Keeper, ak types.AccountKeeper) GenesisState { + // nolint: prealloc + var ethGenAccounts []GenesisAccount + accounts := ak.GetAllAccounts(ctx) + + var err error + for _, account := range accounts { + ethAccount, ok := account.(emint.EthAccount) + if !ok { + continue + } + + addr := common.BytesToAddress(ethAccount.GetAddress().Bytes()) + + var storage []GenesisStorage + err = k.CommitStateDB.ForEachStorage(addr, func(key, value common.Hash) bool { + storage = append(storage, NewGenesisStorage(key, value)) + return false + }) + if err != nil { + panic(err) + } + + genAccount := GenesisAccount{ + Address: addr, + Balance: k.GetBalance(ctx, addr), + Code: k.GetCode(ctx, addr), + Storage: storage, + } + + ethGenAccounts = append(ethGenAccounts, genAccount) + } + + return GenesisState{Accounts: ethGenAccounts} } diff --git a/x/evm/genesis_test.go b/x/evm/genesis_test.go new file mode 100644 index 00000000..1a82bcac --- /dev/null +++ b/x/evm/genesis_test.go @@ -0,0 +1,15 @@ +package evm_test + +import ( + "github.com/cosmos/ethermint/x/evm" + "github.com/cosmos/ethermint/x/evm/types" +) + +func (suite *EvmTestSuite) TestExportImport() { + var genState types.GenesisState + suite.Require().NotPanics(func() { + genState = evm.ExportGenesis(suite.ctx, suite.app.EvmKeeper, suite.app.AccountKeeper) + }) + + _ = evm.InitGenesis(suite.ctx, suite.app.EvmKeeper, genState) +} diff --git a/x/evm/handler.go b/x/evm/handler.go index b6e5a589..d2c1f12c 100644 --- a/x/evm/handler.go +++ b/x/evm/handler.go @@ -74,10 +74,7 @@ func handleMsgEthereumTx(ctx sdk.Context, k Keeper, msg types.MsgEthereumTx) (*s k.Bloom.Or(k.Bloom, executionResult.Bloom) // update transaction logs in KVStore - err = k.SetTransactionLogs(ctx, executionResult.Logs, txHash) - if err != nil { - return nil, err - } + k.SetTransactionLogs(ctx, txHash, executionResult.Logs) // log successful execution k.Logger(ctx).Info(executionResult.Result.Log) @@ -150,10 +147,7 @@ func handleMsgEthermint(ctx sdk.Context, k Keeper, msg types.MsgEthermint) (*sdk k.Bloom.Or(k.Bloom, executionResult.Bloom) // update transaction logs in KVStore - err = k.SetTransactionLogs(ctx, executionResult.Logs, txHash) - if err != nil { - return nil, err - } + k.SetTransactionLogs(ctx, txHash, executionResult.Logs) // log successful execution k.Logger(ctx).Info(executionResult.Result.Log) diff --git a/x/evm/handler_test.go b/x/evm/handler_test.go index 54de8c0c..da8ddb17 100644 --- a/x/evm/handler_test.go +++ b/x/evm/handler_test.go @@ -236,8 +236,7 @@ func (suite *EvmTestSuite) TestHandlerLogs() { suite.Require().Equal(len(resultData.Logs[0].Topics), 2) hash := []byte{1} - err = suite.app.EvmKeeper.SetTransactionLogs(suite.ctx, resultData.Logs, hash) - suite.Require().NoError(err, "failed to set logs") + suite.app.EvmKeeper.SetTransactionLogs(suite.ctx, hash, resultData.Logs) logs, err := suite.app.EvmKeeper.GetTransactionLogs(suite.ctx, hash) suite.Require().NoError(err, "failed to get logs") diff --git a/x/evm/keeper/keeper.go b/x/evm/keeper/keeper.go index 06a5f70f..05bd7b57 100644 --- a/x/evm/keeper/keeper.go +++ b/x/evm/keeper/keeper.go @@ -8,12 +8,11 @@ import ( "github.com/tendermint/tendermint/libs/log" "github.com/cosmos/cosmos-sdk/codec" - ethcmn "github.com/ethereum/go-ethereum/common" - ethvm "github.com/ethereum/go-ethereum/core/vm" - sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/ethermint/x/evm/types" - ethstate "github.com/ethereum/go-ethereum/core/state" + + ethcmn "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" "math/big" @@ -28,8 +27,11 @@ type Keeper struct { // Web3 API blockKey sdk.StoreKey CommitStateDB *types.CommitStateDB - TxCount int - Bloom *big.Int + // Transaction counter in a block. Used on StateSB's Prepare function. + // It is reset to 0 every block on BeginBlock so there's no point in storing the counter + // on the KVStore or adding it as a field on the EVM genesis state. + TxCount int + Bloom *big.Int } // NewKeeper generates new evm module keeper @@ -100,264 +102,21 @@ func (k Keeper) SetBlockBloomMapping(ctx sdk.Context, bloom ethtypes.Bloom, heig } // SetTransactionLogs sets the transaction's logs in the KVStore -func (k *Keeper) SetTransactionLogs(ctx sdk.Context, logs []*ethtypes.Log, hash []byte) error { +func (k *Keeper) SetTransactionLogs(ctx sdk.Context, hash []byte, logs []*ethtypes.Log) { store := ctx.KVStore(k.blockKey) - encLogs, err := types.EncodeLogs(logs) - if err != nil { - return err - } - - store.Set(types.LogsKey(hash), encLogs) - return nil + bz := k.cdc.MustMarshalBinaryLengthPrefixed(logs) + store.Set(types.LogsKey(hash), bz) } // GetTransactionLogs gets the logs for a transaction from the KVStore func (k *Keeper) GetTransactionLogs(ctx sdk.Context, hash []byte) ([]*ethtypes.Log, error) { store := ctx.KVStore(k.blockKey) - encLogs := store.Get(types.LogsKey(hash)) - if len(encLogs) == 0 { + bz := store.Get(types.LogsKey(hash)) + if len(bz) == 0 { return nil, errors.New("cannot get transaction logs") } - return types.DecodeLogs(encLogs) -} - -// ---------------------------------------------------------------------------- -// Genesis -// ---------------------------------------------------------------------------- - -// CreateGenesisAccount initializes an account and its balance, code, and storage -func (k *Keeper) CreateGenesisAccount(ctx sdk.Context, account types.GenesisAccount) { - csdb := k.CommitStateDB.WithContext(ctx) - csdb.SetBalance(account.Address, account.Balance) - csdb.SetCode(account.Address, account.Code) - for _, key := range account.Storage { - csdb.SetState(account.Address, key, account.Storage[key]) - } -} - -// ---------------------------------------------------------------------------- -// Setters -// ---------------------------------------------------------------------------- - -// SetBalance calls CommitStateDB.SetBalance using the passed in context -func (k *Keeper) SetBalance(ctx sdk.Context, addr ethcmn.Address, amount *big.Int) { - k.CommitStateDB.WithContext(ctx).SetBalance(addr, amount) -} - -// AddBalance calls CommitStateDB.AddBalance using the passed in context -func (k *Keeper) AddBalance(ctx sdk.Context, addr ethcmn.Address, amount *big.Int) { - k.CommitStateDB.WithContext(ctx).AddBalance(addr, amount) -} - -// SubBalance calls CommitStateDB.SubBalance using the passed in context -func (k *Keeper) SubBalance(ctx sdk.Context, addr ethcmn.Address, amount *big.Int) { - k.CommitStateDB.WithContext(ctx).SubBalance(addr, amount) -} - -// SetNonce calls CommitStateDB.SetNonce using the passed in context -func (k *Keeper) SetNonce(ctx sdk.Context, addr ethcmn.Address, nonce uint64) { - k.CommitStateDB.WithContext(ctx).SetNonce(addr, nonce) -} - -// SetState calls CommitStateDB.SetState using the passed in context -func (k *Keeper) SetState(ctx sdk.Context, addr ethcmn.Address, key, value ethcmn.Hash) { - k.CommitStateDB.WithContext(ctx).SetState(addr, key, value) -} - -// SetCode calls CommitStateDB.SetCode using the passed in context -func (k *Keeper) SetCode(ctx sdk.Context, addr ethcmn.Address, code []byte) { - k.CommitStateDB.WithContext(ctx).SetCode(addr, code) -} - -// AddLog calls CommitStateDB.AddLog using the passed in context -func (k *Keeper) AddLog(ctx sdk.Context, log *ethtypes.Log) { - k.CommitStateDB.WithContext(ctx).AddLog(log) -} - -// AddPreimage calls CommitStateDB.AddPreimage using the passed in context -func (k *Keeper) AddPreimage(ctx sdk.Context, hash ethcmn.Hash, preimage []byte) { - k.CommitStateDB.WithContext(ctx).AddPreimage(hash, preimage) -} - -// AddRefund calls CommitStateDB.AddRefund using the passed in context -func (k *Keeper) AddRefund(ctx sdk.Context, gas uint64) { - k.CommitStateDB.WithContext(ctx).AddRefund(gas) -} - -// SubRefund calls CommitStateDB.SubRefund using the passed in context -func (k *Keeper) SubRefund(ctx sdk.Context, gas uint64) { - k.CommitStateDB.WithContext(ctx).SubRefund(gas) -} - -// ---------------------------------------------------------------------------- -// Getters -// ---------------------------------------------------------------------------- - -// GetBalance calls CommitStateDB.GetBalance using the passed in context -func (k *Keeper) GetBalance(ctx sdk.Context, addr ethcmn.Address) *big.Int { - return k.CommitStateDB.WithContext(ctx).GetBalance(addr) -} - -// GetNonce calls CommitStateDB.GetNonce using the passed in context -func (k *Keeper) GetNonce(ctx sdk.Context, addr ethcmn.Address) uint64 { - return k.CommitStateDB.WithContext(ctx).GetNonce(addr) -} - -// TxIndex calls CommitStateDB.TxIndex using the passed in context -func (k *Keeper) TxIndex(ctx sdk.Context) int { - return k.CommitStateDB.WithContext(ctx).TxIndex() -} - -// BlockHash calls CommitStateDB.BlockHash using the passed in context -func (k *Keeper) BlockHash(ctx sdk.Context) ethcmn.Hash { - return k.CommitStateDB.WithContext(ctx).BlockHash() -} - -// GetCode calls CommitStateDB.GetCode using the passed in context -func (k *Keeper) GetCode(ctx sdk.Context, addr ethcmn.Address) []byte { - return k.CommitStateDB.WithContext(ctx).GetCode(addr) -} - -// GetCodeSize calls CommitStateDB.GetCodeSize using the passed in context -func (k *Keeper) GetCodeSize(ctx sdk.Context, addr ethcmn.Address) int { - return k.CommitStateDB.WithContext(ctx).GetCodeSize(addr) -} - -// GetCodeHash calls CommitStateDB.GetCodeHash using the passed in context -func (k *Keeper) GetCodeHash(ctx sdk.Context, addr ethcmn.Address) ethcmn.Hash { - return k.CommitStateDB.WithContext(ctx).GetCodeHash(addr) -} - -// GetState calls CommitStateDB.GetState using the passed in context -func (k *Keeper) GetState(ctx sdk.Context, addr ethcmn.Address, hash ethcmn.Hash) ethcmn.Hash { - return k.CommitStateDB.WithContext(ctx).GetState(addr, hash) -} - -// GetCommittedState calls CommitStateDB.GetCommittedState using the passed in context -func (k *Keeper) GetCommittedState(ctx sdk.Context, addr ethcmn.Address, hash ethcmn.Hash) ethcmn.Hash { - return k.CommitStateDB.WithContext(ctx).GetCommittedState(addr, hash) -} - -// GetLogs calls CommitStateDB.GetLogs using the passed in context -func (k *Keeper) GetLogs(ctx sdk.Context, hash ethcmn.Hash) ([]*ethtypes.Log, error) { - return k.CommitStateDB.WithContext(ctx).GetLogs(hash) -} - -// AllLogs calls CommitStateDB.AllLogs using the passed in context -func (k *Keeper) AllLogs(ctx sdk.Context) []*ethtypes.Log { - return k.CommitStateDB.WithContext(ctx).AllLogs() -} - -// GetRefund calls CommitStateDB.GetRefund using the passed in context -func (k *Keeper) GetRefund(ctx sdk.Context) uint64 { - return k.CommitStateDB.WithContext(ctx).GetRefund() -} - -// Preimages calls CommitStateDB.Preimages using the passed in context -func (k *Keeper) Preimages(ctx sdk.Context) map[ethcmn.Hash][]byte { - return k.CommitStateDB.WithContext(ctx).Preimages() -} - -// HasSuicided calls CommitStateDB.HasSuicided using the passed in context -func (k *Keeper) HasSuicided(ctx sdk.Context, addr ethcmn.Address) bool { - return k.CommitStateDB.WithContext(ctx).HasSuicided(addr) -} - -// StorageTrie calls CommitStateDB.StorageTrie using the passed in context -func (k *Keeper) StorageTrie(ctx sdk.Context, addr ethcmn.Address) ethstate.Trie { - return k.CommitStateDB.WithContext(ctx).StorageTrie(addr) -} - -// ---------------------------------------------------------------------------- -// Persistence -// ---------------------------------------------------------------------------- - -// Commit calls CommitStateDB.Commit using the passed { in context -func (k *Keeper) Commit(ctx sdk.Context, deleteEmptyObjects bool) (root ethcmn.Hash, err error) { - return k.CommitStateDB.WithContext(ctx).Commit(deleteEmptyObjects) -} - -// Finalise calls CommitStateDB.Finalise using the passed in context -func (k *Keeper) Finalise(ctx sdk.Context, deleteEmptyObjects bool) error { - return k.CommitStateDB.WithContext(ctx).Finalise(deleteEmptyObjects) -} - -// IntermediateRoot calls CommitStateDB.IntermediateRoot using the passed in context -func (k *Keeper) IntermediateRoot(ctx sdk.Context, deleteEmptyObjects bool) error { - _, err := k.CommitStateDB.WithContext(ctx).IntermediateRoot(deleteEmptyObjects) - return err -} - -// ---------------------------------------------------------------------------- -// Snapshotting -// ---------------------------------------------------------------------------- - -// Snapshot calls CommitStateDB.Snapshot using the passed in context -func (k *Keeper) Snapshot(ctx sdk.Context) int { - return k.CommitStateDB.WithContext(ctx).Snapshot() -} - -// RevertToSnapshot calls CommitStateDB.RevertToSnapshot using the passed in context -func (k *Keeper) RevertToSnapshot(ctx sdk.Context, revID int) { - k.CommitStateDB.WithContext(ctx).RevertToSnapshot(revID) -} - -// ---------------------------------------------------------------------------- -// Auxiliary -// ---------------------------------------------------------------------------- - -// Database calls CommitStateDB.Database using the passed in context -func (k *Keeper) Database(ctx sdk.Context) ethstate.Database { - return k.CommitStateDB.WithContext(ctx).Database() -} - -// Empty calls CommitStateDB.Empty using the passed in context -func (k *Keeper) Empty(ctx sdk.Context, addr ethcmn.Address) bool { - return k.CommitStateDB.WithContext(ctx).Empty(addr) -} - -// Exist calls CommitStateDB.Exist using the passed in context -func (k *Keeper) Exist(ctx sdk.Context, addr ethcmn.Address) bool { - return k.CommitStateDB.WithContext(ctx).Exist(addr) -} - -// Error calls CommitStateDB.Error using the passed in context -func (k *Keeper) Error(ctx sdk.Context) error { - return k.CommitStateDB.WithContext(ctx).Error() -} - -// Suicide calls CommitStateDB.Suicide using the passed in context -func (k *Keeper) Suicide(ctx sdk.Context, addr ethcmn.Address) bool { - return k.CommitStateDB.WithContext(ctx).Suicide(addr) -} - -// Reset calls CommitStateDB.Reset using the passed in context -func (k *Keeper) Reset(ctx sdk.Context, root ethcmn.Hash) error { - return k.CommitStateDB.WithContext(ctx).Reset(root) -} - -// Prepare calls CommitStateDB.Prepare using the passed in context -func (k *Keeper) Prepare(ctx sdk.Context, thash, bhash ethcmn.Hash, txi int) { - k.CommitStateDB.WithContext(ctx).Prepare(thash, bhash, txi) -} - -// CreateAccount calls CommitStateDB.CreateAccount using the passed in context -func (k *Keeper) CreateAccount(ctx sdk.Context, addr ethcmn.Address) { - k.CommitStateDB.WithContext(ctx).CreateAccount(addr) -} - -// Copy calls CommitStateDB.Copy using the passed in context -func (k *Keeper) Copy(ctx sdk.Context) ethvm.StateDB { - return k.CommitStateDB.WithContext(ctx).Copy() -} - -// ForEachStorage calls CommitStateDB.ForEachStorage using passed in context -func (k *Keeper) ForEachStorage(ctx sdk.Context, addr ethcmn.Address, cb func(key, value ethcmn.Hash) bool) error { - return k.CommitStateDB.WithContext(ctx).ForEachStorage(addr, cb) -} - -// GetOrNewStateObject calls CommitStateDB.GetOrNetStateObject using the passed in context -func (k *Keeper) GetOrNewStateObject(ctx sdk.Context, addr ethcmn.Address) types.StateObject { - return k.CommitStateDB.WithContext(ctx).GetOrNewStateObject(addr) + var logs []*ethtypes.Log + k.cdc.MustUnmarshalBinaryLengthPrefixed(bz, &logs) + return logs, nil } diff --git a/x/evm/keeper/keeper_test.go b/x/evm/keeper/keeper_test.go index e296653e..c1b0c51e 100644 --- a/x/evm/keeper/keeper_test.go +++ b/x/evm/keeper/keeper_test.go @@ -53,8 +53,7 @@ func (suite *KeeperTestSuite) TestTransactionLogs() { } expLogs := []*ethtypes.Log{log} - err := suite.app.EvmKeeper.SetTransactionLogs(suite.ctx, expLogs, hash) - suite.Require().NoError(err) + suite.app.EvmKeeper.SetTransactionLogs(suite.ctx, hash, expLogs) suite.app.EvmKeeper.AddLog(suite.ctx, expLogs[0]) logs, err := suite.app.EvmKeeper.GetTransactionLogs(suite.ctx, hash) diff --git a/x/evm/keeper/statedb.go b/x/evm/keeper/statedb.go new file mode 100644 index 00000000..061a2142 --- /dev/null +++ b/x/evm/keeper/statedb.go @@ -0,0 +1,239 @@ +package keeper + +import ( + "math/big" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/ethermint/x/evm/types" + + ethcmn "github.com/ethereum/go-ethereum/common" + ethstate "github.com/ethereum/go-ethereum/core/state" + ethtypes "github.com/ethereum/go-ethereum/core/types" + ethvm "github.com/ethereum/go-ethereum/core/vm" +) + +// ---------------------------------------------------------------------------- +// Setters +// ---------------------------------------------------------------------------- + +// SetBalance calls CommitStateDB.SetBalance using the passed in context +func (k *Keeper) SetBalance(ctx sdk.Context, addr ethcmn.Address, amount *big.Int) { + k.CommitStateDB.WithContext(ctx).SetBalance(addr, amount) +} + +// AddBalance calls CommitStateDB.AddBalance using the passed in context +func (k *Keeper) AddBalance(ctx sdk.Context, addr ethcmn.Address, amount *big.Int) { + k.CommitStateDB.WithContext(ctx).AddBalance(addr, amount) +} + +// SubBalance calls CommitStateDB.SubBalance using the passed in context +func (k *Keeper) SubBalance(ctx sdk.Context, addr ethcmn.Address, amount *big.Int) { + k.CommitStateDB.WithContext(ctx).SubBalance(addr, amount) +} + +// SetNonce calls CommitStateDB.SetNonce using the passed in context +func (k *Keeper) SetNonce(ctx sdk.Context, addr ethcmn.Address, nonce uint64) { + k.CommitStateDB.WithContext(ctx).SetNonce(addr, nonce) +} + +// SetState calls CommitStateDB.SetState using the passed in context +func (k *Keeper) SetState(ctx sdk.Context, addr ethcmn.Address, key, value ethcmn.Hash) { + k.CommitStateDB.WithContext(ctx).SetState(addr, key, value) +} + +// SetCode calls CommitStateDB.SetCode using the passed in context +func (k *Keeper) SetCode(ctx sdk.Context, addr ethcmn.Address, code []byte) { + k.CommitStateDB.WithContext(ctx).SetCode(addr, code) +} + +// AddLog calls CommitStateDB.AddLog using the passed in context +func (k *Keeper) AddLog(ctx sdk.Context, log *ethtypes.Log) { + k.CommitStateDB.WithContext(ctx).AddLog(log) +} + +// AddPreimage calls CommitStateDB.AddPreimage using the passed in context +func (k *Keeper) AddPreimage(ctx sdk.Context, hash ethcmn.Hash, preimage []byte) { + k.CommitStateDB.WithContext(ctx).AddPreimage(hash, preimage) +} + +// AddRefund calls CommitStateDB.AddRefund using the passed in context +func (k *Keeper) AddRefund(ctx sdk.Context, gas uint64) { + k.CommitStateDB.WithContext(ctx).AddRefund(gas) +} + +// SubRefund calls CommitStateDB.SubRefund using the passed in context +func (k *Keeper) SubRefund(ctx sdk.Context, gas uint64) { + k.CommitStateDB.WithContext(ctx).SubRefund(gas) +} + +// ---------------------------------------------------------------------------- +// Getters +// ---------------------------------------------------------------------------- + +// GetBalance calls CommitStateDB.GetBalance using the passed in context +func (k *Keeper) GetBalance(ctx sdk.Context, addr ethcmn.Address) *big.Int { + return k.CommitStateDB.WithContext(ctx).GetBalance(addr) +} + +// GetNonce calls CommitStateDB.GetNonce using the passed in context +func (k *Keeper) GetNonce(ctx sdk.Context, addr ethcmn.Address) uint64 { + return k.CommitStateDB.WithContext(ctx).GetNonce(addr) +} + +// TxIndex calls CommitStateDB.TxIndex using the passed in context +func (k *Keeper) TxIndex(ctx sdk.Context) int { + return k.CommitStateDB.WithContext(ctx).TxIndex() +} + +// BlockHash calls CommitStateDB.BlockHash using the passed in context +func (k *Keeper) BlockHash(ctx sdk.Context) ethcmn.Hash { + return k.CommitStateDB.WithContext(ctx).BlockHash() +} + +// GetCode calls CommitStateDB.GetCode using the passed in context +func (k *Keeper) GetCode(ctx sdk.Context, addr ethcmn.Address) []byte { + return k.CommitStateDB.WithContext(ctx).GetCode(addr) +} + +// GetCodeSize calls CommitStateDB.GetCodeSize using the passed in context +func (k *Keeper) GetCodeSize(ctx sdk.Context, addr ethcmn.Address) int { + return k.CommitStateDB.WithContext(ctx).GetCodeSize(addr) +} + +// GetCodeHash calls CommitStateDB.GetCodeHash using the passed in context +func (k *Keeper) GetCodeHash(ctx sdk.Context, addr ethcmn.Address) ethcmn.Hash { + return k.CommitStateDB.WithContext(ctx).GetCodeHash(addr) +} + +// GetState calls CommitStateDB.GetState using the passed in context +func (k *Keeper) GetState(ctx sdk.Context, addr ethcmn.Address, hash ethcmn.Hash) ethcmn.Hash { + return k.CommitStateDB.WithContext(ctx).GetState(addr, hash) +} + +// GetCommittedState calls CommitStateDB.GetCommittedState using the passed in context +func (k *Keeper) GetCommittedState(ctx sdk.Context, addr ethcmn.Address, hash ethcmn.Hash) ethcmn.Hash { + return k.CommitStateDB.WithContext(ctx).GetCommittedState(addr, hash) +} + +// GetLogs calls CommitStateDB.GetLogs using the passed in context +func (k *Keeper) GetLogs(ctx sdk.Context, hash ethcmn.Hash) ([]*ethtypes.Log, error) { + return k.CommitStateDB.WithContext(ctx).GetLogs(hash) +} + +// AllLogs calls CommitStateDB.AllLogs using the passed in context +func (k *Keeper) AllLogs(ctx sdk.Context) []*ethtypes.Log { + return k.CommitStateDB.WithContext(ctx).AllLogs() +} + +// GetRefund calls CommitStateDB.GetRefund using the passed in context +func (k *Keeper) GetRefund(ctx sdk.Context) uint64 { + return k.CommitStateDB.WithContext(ctx).GetRefund() +} + +// Preimages calls CommitStateDB.Preimages using the passed in context +func (k *Keeper) Preimages(ctx sdk.Context) map[ethcmn.Hash][]byte { + return k.CommitStateDB.WithContext(ctx).Preimages() +} + +// HasSuicided calls CommitStateDB.HasSuicided using the passed in context +func (k *Keeper) HasSuicided(ctx sdk.Context, addr ethcmn.Address) bool { + return k.CommitStateDB.WithContext(ctx).HasSuicided(addr) +} + +// StorageTrie calls CommitStateDB.StorageTrie using the passed in context +func (k *Keeper) StorageTrie(ctx sdk.Context, addr ethcmn.Address) ethstate.Trie { + return k.CommitStateDB.WithContext(ctx).StorageTrie(addr) +} + +// ---------------------------------------------------------------------------- +// Persistence +// ---------------------------------------------------------------------------- + +// Commit calls CommitStateDB.Commit using the passed { in context +func (k *Keeper) Commit(ctx sdk.Context, deleteEmptyObjects bool) (root ethcmn.Hash, err error) { + return k.CommitStateDB.WithContext(ctx).Commit(deleteEmptyObjects) +} + +// Finalise calls CommitStateDB.Finalise using the passed in context +func (k *Keeper) Finalise(ctx sdk.Context, deleteEmptyObjects bool) error { + return k.CommitStateDB.WithContext(ctx).Finalise(deleteEmptyObjects) +} + +// IntermediateRoot calls CommitStateDB.IntermediateRoot using the passed in context +func (k *Keeper) IntermediateRoot(ctx sdk.Context, deleteEmptyObjects bool) error { + _, err := k.CommitStateDB.WithContext(ctx).IntermediateRoot(deleteEmptyObjects) + return err +} + +// ---------------------------------------------------------------------------- +// Snapshotting +// ---------------------------------------------------------------------------- + +// Snapshot calls CommitStateDB.Snapshot using the passed in context +func (k *Keeper) Snapshot(ctx sdk.Context) int { + return k.CommitStateDB.WithContext(ctx).Snapshot() +} + +// RevertToSnapshot calls CommitStateDB.RevertToSnapshot using the passed in context +func (k *Keeper) RevertToSnapshot(ctx sdk.Context, revID int) { + k.CommitStateDB.WithContext(ctx).RevertToSnapshot(revID) +} + +// ---------------------------------------------------------------------------- +// Auxiliary +// ---------------------------------------------------------------------------- + +// Database calls CommitStateDB.Database using the passed in context +func (k *Keeper) Database(ctx sdk.Context) ethstate.Database { + return k.CommitStateDB.WithContext(ctx).Database() +} + +// Empty calls CommitStateDB.Empty using the passed in context +func (k *Keeper) Empty(ctx sdk.Context, addr ethcmn.Address) bool { + return k.CommitStateDB.WithContext(ctx).Empty(addr) +} + +// Exist calls CommitStateDB.Exist using the passed in context +func (k *Keeper) Exist(ctx sdk.Context, addr ethcmn.Address) bool { + return k.CommitStateDB.WithContext(ctx).Exist(addr) +} + +// Error calls CommitStateDB.Error using the passed in context +func (k *Keeper) Error(ctx sdk.Context) error { + return k.CommitStateDB.WithContext(ctx).Error() +} + +// Suicide calls CommitStateDB.Suicide using the passed in context +func (k *Keeper) Suicide(ctx sdk.Context, addr ethcmn.Address) bool { + return k.CommitStateDB.WithContext(ctx).Suicide(addr) +} + +// Reset calls CommitStateDB.Reset using the passed in context +func (k *Keeper) Reset(ctx sdk.Context, root ethcmn.Hash) error { + return k.CommitStateDB.WithContext(ctx).Reset(root) +} + +// Prepare calls CommitStateDB.Prepare using the passed in context +func (k *Keeper) Prepare(ctx sdk.Context, thash, bhash ethcmn.Hash, txi int) { + k.CommitStateDB.WithContext(ctx).Prepare(thash, bhash, txi) +} + +// CreateAccount calls CommitStateDB.CreateAccount using the passed in context +func (k *Keeper) CreateAccount(ctx sdk.Context, addr ethcmn.Address) { + k.CommitStateDB.WithContext(ctx).CreateAccount(addr) +} + +// Copy calls CommitStateDB.Copy using the passed in context +func (k *Keeper) Copy(ctx sdk.Context) ethvm.StateDB { + return k.CommitStateDB.WithContext(ctx).Copy() +} + +// ForEachStorage calls CommitStateDB.ForEachStorage using passed in context +func (k *Keeper) ForEachStorage(ctx sdk.Context, addr ethcmn.Address, cb func(key, value ethcmn.Hash) bool) error { + return k.CommitStateDB.WithContext(ctx).ForEachStorage(addr, cb) +} + +// GetOrNewStateObject calls CommitStateDB.GetOrNetStateObject using the passed in context +func (k *Keeper) GetOrNewStateObject(ctx sdk.Context, addr ethcmn.Address) types.StateObject { + return k.CommitStateDB.WithContext(ctx).GetOrNewStateObject(addr) +} diff --git a/x/evm/module.go b/x/evm/module.go index cad7dbeb..5208c5f9 100644 --- a/x/evm/module.go +++ b/x/evm/module.go @@ -41,13 +41,13 @@ func (AppModuleBasic) DefaultGenesis(cdc codec.JSONMarshaler) json.RawMessage { // ValidateGenesis is the validation check of the Genesis func (AppModuleBasic) ValidateGenesis(cdc codec.JSONMarshaler, bz json.RawMessage) error { - var data types.GenesisState - err := cdc.UnmarshalJSON(bz, &data) + var genesisState types.GenesisState + err := cdc.UnmarshalJSON(bz, &genesisState) if err != nil { return err } - // Once json successfully marshalled, passes along to genesis.go - return types.ValidateGenesis(data) + + return genesisState.Validate() } // RegisterRESTRoutes Registers rest routes @@ -71,13 +71,15 @@ func (AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command { type AppModule struct { AppModuleBasic keeper Keeper + ak types.AccountKeeper } // NewAppModule creates a new AppModule Object -func NewAppModule(k Keeper) AppModule { +func NewAppModule(k Keeper, ak types.AccountKeeper) AppModule { return AppModule{ AppModuleBasic: AppModuleBasic{}, keeper: k, + ak: ak, } } @@ -128,6 +130,6 @@ func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONMarshaler, data j // ExportGenesis exports the genesis state to be used by daemon func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONMarshaler) json.RawMessage { - gs := ExportGenesis(ctx, am.keeper) + gs := ExportGenesis(ctx, am.keeper, am.ak) return cdc.MustMarshalJSON(gs) } diff --git a/x/evm/types/expected_keepers.go b/x/evm/types/expected_keepers.go index 28163b20..b6ce2a75 100644 --- a/x/evm/types/expected_keepers.go +++ b/x/evm/types/expected_keepers.go @@ -8,6 +8,7 @@ import ( // AccountKeeper defines the expected account keeper interface type AccountKeeper interface { NewAccountWithAddress(ctx sdk.Context, addr sdk.AccAddress) authexported.Account + GetAllAccounts(ctx sdk.Context) (accounts []authexported.Account) GetAccount(ctx sdk.Context, addr sdk.AccAddress) authexported.Account SetAccount(ctx sdk.Context, account authexported.Account) RemoveAccount(ctx sdk.Context, account authexported.Account) diff --git a/x/evm/types/genesis.go b/x/evm/types/genesis.go index d3da7068..066aab6b 100644 --- a/x/evm/types/genesis.go +++ b/x/evm/types/genesis.go @@ -1,13 +1,15 @@ package types import ( + "bytes" "errors" "math/big" - "github.com/cosmos/ethermint/types" ethcmn "github.com/ethereum/go-ethereum/common" ) +var zeroAddrBytes = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + type ( // GenesisState defines the application's genesis state. It contains all the // information required and accounts to initialize the blockchain. @@ -15,31 +17,49 @@ type ( Accounts []GenesisAccount `json:"accounts"` } + // GenesisStorage represents the GenesisAccount Storage map as single key value + // pairs. This is to prevent non determinism at genesis initialization or export. + GenesisStorage struct { + Key ethcmn.Hash `json:"key"` + Value ethcmn.Hash `json:"value"` + } + // GenesisAccount defines an account to be initialized in the genesis state. + // Its main difference between with Geth's GenesisAccount is that it uses a custom + // storage type and that it doesn't contain the private key field. GenesisAccount struct { - Address ethcmn.Address `json:"address"` - Balance *big.Int `json:"balance"` - Code []byte `json:"code,omitempty"` - Storage types.Storage `json:"storage,omitempty"` + Address ethcmn.Address `json:"address"` + Balance *big.Int `json:"balance"` + Code []byte `json:"code,omitempty"` + Storage []GenesisStorage `json:"storage,omitempty"` } ) -// ValidateGenesis validates evm genesis config -func ValidateGenesis(data GenesisState) error { - for _, acct := range data.Accounts { - if len(acct.Address.Bytes()) == 0 { - return errors.New("invalid GenesisAccount: address cannot be empty") - } - if acct.Balance == nil { - return errors.New("invalid GenesisAccount: balance cannot be empty") - } +// NewGenesisStorage creates a new GenesisStorage instance +func NewGenesisStorage(key, value ethcmn.Hash) GenesisStorage { + return GenesisStorage{ + Key: key, + Value: value, } - return nil } -// DefaultGenesisState sets default evm genesis config +// DefaultGenesisState sets default evm genesis state with empty accounts. func DefaultGenesisState() GenesisState { return GenesisState{ Accounts: []GenesisAccount{}, } } + +// Validate performs basic genesis state validation returning an error upon any +// failure. +func (gs GenesisState) Validate() error { + for _, acc := range gs.Accounts { + if bytes.Equal(acc.Address.Bytes(), zeroAddrBytes) { + return errors.New("invalid GenesisAccount: address cannot be empty") + } + if acc.Balance == nil { + return errors.New("invalid GenesisAccount: balance cannot be empty") + } + } + return nil +} diff --git a/x/evm/types/genesis_test.go b/x/evm/types/genesis_test.go index 503eea61..e1dfa0a3 100644 --- a/x/evm/types/genesis_test.go +++ b/x/evm/types/genesis_test.go @@ -1,45 +1,59 @@ package types import ( + "math/big" "testing" "github.com/stretchr/testify/require" + + ethcmn "github.com/ethereum/go-ethereum/common" ) func TestValidateGenesis(t *testing.T) { + testCases := []struct { - msg string - genstate GenesisState + name string + genState GenesisState expPass bool }{ { - msg: "pass with defaultState ", - genstate: DefaultGenesisState(), + name: "default", + genState: DefaultGenesisState(), expPass: true, }, { - msg: "empty address", - genstate: GenesisState{ - Accounts: []GenesisAccount{{}}, + name: "empty account address bytes", + genState: GenesisState{ + Accounts: []GenesisAccount{ + { + Address: ethcmn.Address{}, + Balance: big.NewInt(1), + }, + }, }, expPass: false, }, { - msg: "empty balance", - genstate: GenesisState{ - Accounts: []GenesisAccount{{Balance: nil}}, + name: "nil account balance", + genState: GenesisState{ + Accounts: []GenesisAccount{ + { + Address: ethcmn.BytesToAddress([]byte{1, 2, 3, 4, 5}), + Balance: nil, + }, + }, }, expPass: false, }, } - for i, tc := range testCases { - err := ValidateGenesis(tc.genstate) + for _, tc := range testCases { + tc := tc + err := tc.genState.Validate() if tc.expPass { - require.NoError(t, err, "test (%d) %s", i, tc.msg) + require.NoError(t, err, tc.name) } else { - require.Error(t, err, "test (%d): %s", i, tc.msg) + require.Error(t, err, tc.name) } } - } diff --git a/x/evm/types/statedb.go b/x/evm/types/statedb.go index a9575fb9..08255176 100644 --- a/x/evm/types/statedb.go +++ b/x/evm/types/statedb.go @@ -722,11 +722,18 @@ func (csdb *CommitStateDB) ForEachStorage(addr ethcmn.Address, cb func(key, valu value := iter.Value() if value, dirty := so.dirtyStorage[key]; dirty { - cb(key, value) + // check if iteration stops + if cb(key, value) { + break + } + continue } - cb(key, ethcmn.BytesToHash(value)) + // check if iteration stops + if cb(key, ethcmn.BytesToHash(value)) { + break + } } return nil