diff --git a/x/evm/handler.go b/x/evm/handler.go index f0b36554..8eefba3e 100644 --- a/x/evm/handler.go +++ b/x/evm/handler.go @@ -98,12 +98,6 @@ func handleETHTxMsg(ctx sdk.Context, keeper Keeper, msg types.EthereumTxMsg) sdk keeper.csdb.Finalise(true) // Change to depend on config - // TODO: Remove commit from tx handler (should be done at end of block) - _, err = keeper.csdb.Commit(true) - if err != nil { - return sdk.ErrUnknownRequest("Failed to write data to kv store").Result() - } - // TODO: Consume gas from sender return sdk.Result{Data: addr.Bytes(), GasUsed: msg.Data.GasLimit - leftOverGas} diff --git a/x/evm/keeper_test.go b/x/evm/keeper_test.go new file mode 100644 index 00000000..ea016394 --- /dev/null +++ b/x/evm/keeper_test.go @@ -0,0 +1,95 @@ +package evm + +import ( + "math/big" + "testing" + + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/store" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/cosmos/cosmos-sdk/x/auth" + "github.com/cosmos/cosmos-sdk/x/params" + + "github.com/cosmos/ethermint/types" + evmtypes "github.com/cosmos/ethermint/x/evm/types" + + ethcmn "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" + + abci "github.com/tendermint/tendermint/abci/types" + tmlog "github.com/tendermint/tendermint/libs/log" + dbm "github.com/tendermint/tm-db" +) + +var ( + address = ethcmn.HexToAddress("0x756F45E3FA69347A9A973A725E3C98bC4db0b4c1") + + accKey = sdk.NewKVStoreKey("acc") + storageKey = sdk.NewKVStoreKey(evmtypes.EvmStoreKey) + codeKey = sdk.NewKVStoreKey(evmtypes.EvmCodeKey) + + logger = tmlog.NewNopLogger() +) + +func newTestCodec() *codec.Codec { + cdc := codec.New() + + evmtypes.RegisterCodec(cdc) + types.RegisterCodec(cdc) + auth.RegisterCodec(cdc) + sdk.RegisterCodec(cdc) + codec.RegisterCrypto(cdc) + + return cdc +} + +func TestDBStorage(t *testing.T) { + // create logger, codec and root multi-store + cdc := newTestCodec() + + // The ParamsKeeper handles parameter storage for the application + keyParams := sdk.NewKVStoreKey(params.StoreKey) + tkeyParams := sdk.NewTransientStoreKey(params.TStoreKey) + paramsKeeper := params.NewKeeper(cdc, keyParams, tkeyParams, params.DefaultCodespace) + // Set specific supspaces + authSubspace := paramsKeeper.Subspace(auth.DefaultParamspace) + ak := auth.NewAccountKeeper(cdc, accKey, authSubspace, types.ProtoBaseAccount) + ek := NewKeeper(ak, storageKey, codeKey, cdc) + + db := dbm.NewMemDB() + cms := store.NewCommitMultiStore(db) + // mount stores + keys := []*sdk.KVStoreKey{accKey, storageKey, codeKey} + for _, key := range keys { + cms.MountStoreWithDB(key, sdk.StoreTypeIAVL, nil) + } + + // load latest version (root) + err := cms.LoadLatestVersion() + require.NoError(t, err) + + // First execution + ms := cms.CacheMultiStore() + ctx := sdk.NewContext(ms, abci.Header{}, false, logger) + ctx = ctx.WithBlockHeight(1) + + // Perform state transitions + ek.SetBalance(ctx, address, big.NewInt(5)) + ek.SetNonce(ctx, address, 4) + ek.SetState(ctx, address, ethcmn.HexToHash("0x2"), ethcmn.HexToHash("0x3")) + ek.SetCode(ctx, address, []byte{0x1}) + + // Get those state transitions + require.Equal(t, ek.GetBalance(ctx, address).Cmp(big.NewInt(5)), 0) + require.Equal(t, ek.GetNonce(ctx, address), uint64(4)) + require.Equal(t, ek.GetState(ctx, address, ethcmn.HexToHash("0x2")), ethcmn.HexToHash("0x3")) + require.Equal(t, ek.GetCode(ctx, address), []byte{0x1}) + + // commit stateDB + _, err = ek.Commit(ctx, false) + require.NoError(t, err, "failed to commit StateDB") + + // simulate BaseApp EndBlocker commitment + ms.Write() + cms.Commit() +} diff --git a/x/evm/module.go b/x/evm/module.go index b20d3006..d09715b5 100644 --- a/x/evm/module.go +++ b/x/evm/module.go @@ -17,22 +17,25 @@ import ( var _ module.AppModuleBasic = AppModuleBasic{} var _ module.AppModule = AppModule{} -// app module Basics object +// AppModuleBasic struct type AppModuleBasic struct{} +// Name for app module basic func (AppModuleBasic) Name() string { return types.ModuleName } +// RegisterCodec registers types for module func (AppModuleBasic) RegisterCodec(cdc *codec.Codec) { types.RegisterCodec(cdc) } +// DefaultGenesis is json default structure func (AppModuleBasic) DefaultGenesis() json.RawMessage { return types.ModuleCdc.MustMarshalJSON(DefaultGenesisState()) } -// Validation check of the Genesis +// ValidateGenesis is the validation check of the Genesis func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { var data GenesisState err := types.ModuleCdc.UnmarshalJSON(bz, &data) @@ -43,21 +46,22 @@ func (AppModuleBasic) ValidateGenesis(bz json.RawMessage) error { return ValidateGenesis(data) } -// Register rest routes +// RegisterRESTRoutes Registers rest routes func (AppModuleBasic) RegisterRESTRoutes(ctx context.CLIContext, rtr *mux.Router) { //rpc.RegisterRoutes(ctx, rtr, StoreKey) } -// Get the root query command of this module +// GetQueryCmd Gets the root query command of this module func (AppModuleBasic) GetQueryCmd(cdc *codec.Codec) *cobra.Command { return cli.GetQueryCmd(types.ModuleName, cdc) } -// Get the root tx command of this module +// GetTxCmd Gets the root tx command of this module func (AppModuleBasic) GetTxCmd(cdc *codec.Codec) *cobra.Command { return cli.GetTxCmd(types.ModuleName, cdc) } +// AppModule is struct that defines variables used within module type AppModule struct { AppModuleBasic keeper Keeper @@ -71,40 +75,55 @@ func NewAppModule(keeper Keeper) AppModule { } } +// Name is module name func (AppModule) Name() string { return types.ModuleName } +// RegisterInvariants interface for registering invariants func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) {} +// Route specifies path for transactions func (am AppModule) Route() string { return types.RouterKey } +// NewHandler sets up a new handler for module func (am AppModule) NewHandler() sdk.Handler { return NewHandler(am.keeper) } + +// QuerierRoute sets up path for queries func (am AppModule) QuerierRoute() string { return types.ModuleName } +// NewQuerierHandler sets up new querier handler for module func (am AppModule) NewQuerierHandler() sdk.Querier { return NewQuerier(am.keeper) } +// BeginBlock function for module at start of each block func (am AppModule) BeginBlock(_ sdk.Context, _ abci.RequestBeginBlock) {} +// EndBlock function for module at end of block func (am AppModule) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { - // TODO: Commit database here ? + _, err := am.keeper.csdb.Commit(true) + if err != nil { + panic(err) + } + return []abci.ValidatorUpdate{} } +// InitGenesis instantiates the genesis state func (am AppModule) InitGenesis(ctx sdk.Context, data json.RawMessage) []abci.ValidatorUpdate { var genesisState GenesisState types.ModuleCdc.MustUnmarshalJSON(data, &genesisState) return InitGenesis(ctx, am.keeper, genesisState) } +// ExportGenesis exports the genesis state to be used by daemon func (am AppModule) ExportGenesis(ctx sdk.Context) json.RawMessage { gs := ExportGenesis(ctx, am.keeper) return types.ModuleCdc.MustMarshalJSON(gs)