From 0c6e44d3d329c03a642f2dd6b12b3d37b311d48c Mon Sep 17 00:00:00 2001 From: Federico Kunze <31522760+fedekunze@users.noreply.github.com> Date: Tue, 8 Jun 2021 13:10:29 -0400 Subject: [PATCH] evm: remove `CommitStateDB`, `journal` and `stateObject` (#84) * evm: remove CommitStateDB and stateObject * imported build fixes * lint * rm set nonce * update account response * changelog --- CHANGELOG.md | 1 + app/app.go | 2 +- docs/core/proto-docs.md | 2 +- go.mod | 2 +- proto/ethermint/evm/v1alpha1/query.proto | 4 +- tests/importer/importer_test.go | 57 +- x/evm/genesis.go | 31 +- x/evm/genesis_test.go | 4 +- x/evm/handler.go | 5 +- x/evm/handler_test.go | 18 +- x/evm/keeper/abci.go | 23 +- x/evm/keeper/grpc_query.go | 135 ++-- x/evm/keeper/grpc_query_test.go | 15 +- x/evm/keeper/invariants.go | 105 --- x/evm/keeper/invariants_test.go | 138 ---- x/evm/keeper/keeper.go | 5 - x/evm/keeper/msg_server.go | 88 +- x/evm/keeper/state_transition.go | 84 +- x/evm/module.go | 6 +- x/evm/types/journal.go | 348 -------- x/evm/types/journal_test.go | 354 -------- x/evm/types/logs.go | 2 +- x/evm/types/msg.go | 44 - x/evm/types/msg_test.go | 19 - x/evm/types/query.pb.go | 154 ++-- x/evm/types/state_object.go | 457 ----------- x/evm/types/state_object_test.go | 135 ---- x/evm/types/state_transition.go | 340 -------- x/evm/types/state_transition_test.go | 190 ----- x/evm/types/statedb.go | 990 ----------------------- x/evm/types/statedb_test.go | 739 ----------------- x/evm/types/utils.go | 13 +- 32 files changed, 260 insertions(+), 4250 deletions(-) delete mode 100644 x/evm/keeper/invariants.go delete mode 100644 x/evm/keeper/invariants_test.go delete mode 100644 x/evm/types/journal.go delete mode 100644 x/evm/types/journal_test.go delete mode 100644 x/evm/types/state_object.go delete mode 100644 x/evm/types/state_object_test.go delete mode 100644 x/evm/types/state_transition.go delete mode 100644 x/evm/types/state_transition_test.go delete mode 100644 x/evm/types/statedb.go delete mode 100644 x/evm/types/statedb_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 3488e317..83e8d7c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,6 +39,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### State Machine Breaking +* (evm) [tharsis#84](https://github.com/tharsis/ethermint/pull/84) Remove `journal`, `CommitStateDB` and `stateObjects`. * (rpc, evm) [tharsis#81](https://github.com/tharsis/ethermint/pull/81) Remove tx `Receipt` from store and replace it with fields obtained from the Tendermint RPC client. * (evm) [tharsis#72](https://github.com/tharsis/ethermint/issues/72) Update `AccessList` to use `TransientStore` instead of map. * (evm) [tharsis#68](https://github.com/tharsis/ethermint/issues/68) Replace block hash storage map to use staking `HistoricalInfo`. diff --git a/app/app.go b/app/app.go index 5b66492f..0699f893 100644 --- a/app/app.go +++ b/app/app.go @@ -385,7 +385,7 @@ func NewEthermintApp( params.NewAppModule(app.ParamsKeeper), transferModule, // Ethermint app modules - evm.NewAppModule(app.EvmKeeper, app.AccountKeeper, app.BankKeeper), + evm.NewAppModule(app.EvmKeeper, app.AccountKeeper), ) // During begin block slashing happens after distr.BeginBlocker so that diff --git a/docs/core/proto-docs.md b/docs/core/proto-docs.md index bfa6a20b..3fd19d9d 100644 --- a/docs/core/proto-docs.md +++ b/docs/core/proto-docs.md @@ -427,7 +427,7 @@ QueryAccountResponse is the response type for the Query/Account RPC method. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | `balance` | [string](#string) | | balance is the balance of the EVM denomination. | -| `code_hash` | [bytes](#bytes) | | code_hash is the code bytes from the EOA. | +| `code_hash` | [string](#string) | | code hash is the hex-formatted code bytes from the EOA. | | `nonce` | [uint64](#uint64) | | nonce is the account's sequence number. | diff --git a/go.mod b/go.mod index eca6284b..afb824e9 100644 --- a/go.mod +++ b/go.mod @@ -45,7 +45,7 @@ require ( github.com/tyler-smith/go-bip39 v1.1.0 github.com/xlab/closer v0.0.0-20190328110542-03326addb7c2 github.com/xlab/suplog v1.3.0 - golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a + golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a // indirect google.golang.org/genproto v0.0.0-20210607140030-00d4fb20b1ae google.golang.org/grpc v1.38.0 gopkg.in/yaml.v2 v2.4.0 diff --git a/proto/ethermint/evm/v1alpha1/query.proto b/proto/ethermint/evm/v1alpha1/query.proto index 55430926..d15fb9c7 100644 --- a/proto/ethermint/evm/v1alpha1/query.proto +++ b/proto/ethermint/evm/v1alpha1/query.proto @@ -75,8 +75,8 @@ message QueryAccountRequest { message QueryAccountResponse { // balance is the balance of the EVM denomination. string balance = 1; - // code_hash is the code bytes from the EOA. - bytes code_hash = 2; + // code hash is the hex-formatted code bytes from the EOA. + string code_hash = 2; // nonce is the account's sequence number. uint64 nonce = 3; } diff --git a/tests/importer/importer_test.go b/tests/importer/importer_test.go index 8065ec63..b2fa76ba 100644 --- a/tests/importer/importer_test.go +++ b/tests/importer/importer_test.go @@ -107,7 +107,7 @@ func createAndTestGenesis(t *testing.T, cms sdk.CommitMultiStore, ak authkeeper. genBlock := ethcore.DefaultGenesisBlock() ms := cms.CacheMultiStore() ctx := sdk.NewContext(ms, tmproto.Header{}, false, logger) - evmKeeper.CommitStateDB.WithContext(ctx) + evmKeeper.WithContext(ctx) // Set the default Ethermint parameters to the parameter keeper store evmKeeper.SetParams(ctx, evmtypes.DefaultParams()) @@ -126,25 +126,19 @@ func createAndTestGenesis(t *testing.T, cms sdk.CommitMultiStore, ak authkeeper. addr := ethcmn.HexToAddress(addrStr) acc := genBlock.Alloc[addr] - evmKeeper.CommitStateDB.AddBalance(addr, acc.Balance) - evmKeeper.CommitStateDB.SetCode(addr, acc.Code) - evmKeeper.CommitStateDB.SetNonce(addr, acc.Nonce) + evmKeeper.AddBalance(addr, acc.Balance) + evmKeeper.SetCode(addr, acc.Code) + evmKeeper.SetNonce(addr, acc.Nonce) for key, value := range acc.Storage { - evmKeeper.CommitStateDB.SetState(addr, key, value) + evmKeeper.SetState(addr, key, value) } } // get balance of one of the genesis account having 400 ETH - b := evmKeeper.CommitStateDB.GetBalance(genInvestor) + b := evmKeeper.GetBalance(genInvestor) require.Equal(t, "200000000000000000000", b.String()) - // commit the stateDB with 'false' to delete empty objects - // - // NOTE: Commit does not yet return the intra merkle root (version) - _, err := evmKeeper.CommitStateDB.Commit(false) - require.NoError(t, err) - // persist multi-store cache state ms.Write() @@ -267,15 +261,13 @@ func TestImportBlocks(t *testing.T) { ms := cms.CacheMultiStore() ctx := sdk.NewContext(ms, tmproto.Header{}, false, logger) ctx = ctx.WithBlockHeight(int64(block.NumberU64())) - evmKeeper.CommitStateDB.WithContext(ctx) + evmKeeper.WithContext(ctx) if chainConfig.DAOForkSupport && chainConfig.DAOForkBlock != nil && chainConfig.DAOForkBlock.Cmp(block.Number()) == 0 { applyDAOHardFork(evmKeeper) } - for i, tx := range block.Transactions() { - evmKeeper.CommitStateDB.Prepare(tx.Hash(), block.Hash(), i) - // evmKeeper.CommitStateDB.Set(block.Hash()) + for _, tx := range block.Transactions() { receipt, gas, err := applyTransaction( chainConfig, chainContext, nil, gp, evmKeeper, header, tx, usedGas, vmConfig, @@ -287,10 +279,6 @@ func TestImportBlocks(t *testing.T) { // apply mining rewards accumulateRewards(chainConfig, evmKeeper, header, block.Uncles()) - // commit stateDB - _, err := evmKeeper.CommitStateDB.Commit(chainConfig.IsEIP158(block.Number())) - require.NoError(t, err, "failed to commit StateDB") - // simulate BaseApp EndBlocker commitment ms.Write() cms.Commit() @@ -325,12 +313,12 @@ func accumulateRewards( r.Sub(r, header.Number) r.Mul(r, blockReward) r.Div(r, rewardBig8) - evmKeeper.CommitStateDB.AddBalance(uncle.Coinbase, r) + evmKeeper.AddBalance(uncle.Coinbase, r) r.Div(blockReward, rewardBig32) reward.Add(reward, r) } - evmKeeper.CommitStateDB.AddBalance(header.Coinbase, reward) + evmKeeper.AddBalance(header.Coinbase, reward) } // ApplyDAOHardFork modifies the state database according to the DAO hard-fork @@ -341,14 +329,13 @@ func accumulateRewards( // Ref: https://github.com/ethereum/go-ethereum/blob/52f2461774bcb8cdd310f86b4bc501df5b783852/consensus/misc/dao.go#L74 func applyDAOHardFork(evmKeeper *evmkeeper.Keeper) { // Retrieve the contract to refund balances into - if !evmKeeper.CommitStateDB.Exist(ethparams.DAORefundContract) { - evmKeeper.CommitStateDB.CreateAccount(ethparams.DAORefundContract) + if !evmKeeper.Exist(ethparams.DAORefundContract) { + evmKeeper.CreateAccount(ethparams.DAORefundContract) } // Move every DAO account and extra-balance account funds into the refund contract for _, addr := range ethparams.DAODrainList() { - evmKeeper.CommitStateDB.AddBalance(ethparams.DAORefundContract, evmKeeper.CommitStateDB.GetBalance(addr)) - evmKeeper.CommitStateDB.SetBalance(addr, new(big.Int)) + evmKeeper.AddBalance(ethparams.DAORefundContract, evmKeeper.GetBalance(addr)) } } @@ -374,7 +361,7 @@ func applyTransaction( // Create a new environment which holds all relevant information // about the transaction and calling mechanisms. - vmenv := ethvm.NewEVM(blockCtx, txCtx, evmKeeper.CommitStateDB, config, cfg) + vmenv := ethvm.NewEVM(blockCtx, txCtx, evmKeeper, config, cfg) // Apply the transaction to the current state (included in the env) execResult, err := ethcore.ApplyMessage(vmenv, msg, gp) @@ -383,19 +370,11 @@ func applyTransaction( return ðtypes.Receipt{}, 0, nil } - // Update the state with pending changes - var intRoot ethcmn.Hash - if config.IsByzantium(header.Number) { - err = evmKeeper.CommitStateDB.Finalise(true) - } else { - intRoot, err = evmKeeper.CommitStateDB.IntermediateRoot(config.IsEIP158(header.Number)) - } - if err != nil { return nil, execResult.UsedGas, err } - root := intRoot.Bytes() + root := ethcmn.Hash{}.Bytes() *usedGas += execResult.UsedGas // Create a new receipt for the transaction, storing the intermediate root and gas used by the tx @@ -410,11 +389,11 @@ func applyTransaction( } // Set the receipt logs and create a bloom for filtering - receipt.Logs, err = evmKeeper.CommitStateDB.GetLogs(tx.Hash()) + receipt.Logs = evmKeeper.GetTxLogs(tx.Hash()) receipt.Bloom = ethtypes.CreateBloom(ethtypes.Receipts{receipt}) - receipt.BlockHash = evmKeeper.CommitStateDB.BlockHash() + receipt.BlockHash = header.Hash() receipt.BlockNumber = header.Number - receipt.TransactionIndex = uint(evmKeeper.CommitStateDB.TxIndex()) + receipt.TransactionIndex = uint(evmKeeper.GetTxIndexTransient()) return receipt, execResult.UsedGas, err } diff --git a/x/evm/genesis.go b/x/evm/genesis.go index 3cf021b2..d03cabb7 100644 --- a/x/evm/genesis.go +++ b/x/evm/genesis.go @@ -18,16 +18,12 @@ func InitGenesis( ctx sdk.Context, k *keeper.Keeper, accountKeeper types.AccountKeeper, // nolint: interfacer - bankKeeper types.BankKeeper, data types.GenesisState, ) []abci.ValidatorUpdate { k.WithContext(ctx) k.WithChainID(ctx) - k.CommitStateDB.WithContext(ctx) - k.SetParams(ctx, data.Params) - evmDenom := data.Params.EvmDenom for _, account := range data.Accounts { address := ethcmn.HexToAddress(account.Address) @@ -47,46 +43,25 @@ func InitGenesis( ) } - evmBalance := bankKeeper.GetBalance(ctx, accAddress, evmDenom) - k.CommitStateDB.SetBalance(address, evmBalance.Amount.BigInt()) - k.CommitStateDB.SetNonce(address, acc.GetSequence()) - k.CommitStateDB.SetCode(address, ethcmn.Hex2Bytes(account.Code)) + k.SetCode(address, ethcmn.Hex2Bytes(account.Code)) for _, storage := range account.Storage { k.SetState(address, ethcmn.HexToHash(storage.Key), ethcmn.HexToHash(storage.Value)) } } - var err error for _, txLog := range data.TxsLogs { - err = k.CommitStateDB.SetLogs(ethcmn.HexToHash(txLog.Hash), txLog.EthLogs()) - if err != nil { - panic(err) - } + k.SetLogs(ethcmn.HexToHash(txLog.Hash), txLog.EthLogs()) } k.SetChainConfig(ctx, data.ChainConfig) - // set state objects and code to store - _, err = k.CommitStateDB.Commit(false) - if err != nil { - panic(err) - } - - // set storage to store - // NOTE: don't delete empty object to prevent import-export simulation failure - err = k.CommitStateDB.Finalise(false) - if err != nil { - panic(err) - } - return []abci.ValidatorUpdate{} } // ExportGenesis exports genesis state of the EVM module func ExportGenesis(ctx sdk.Context, k *keeper.Keeper, ak types.AccountKeeper) *types.GenesisState { k.WithContext(ctx) - k.CommitStateDB.WithContext(ctx) // nolint: prealloc var ethGenAccounts []types.GenesisAccount @@ -106,7 +81,7 @@ func ExportGenesis(ctx sdk.Context, k *keeper.Keeper, ak types.AccountKeeper) *t genAccount := types.GenesisAccount{ Address: addr.String(), - Code: ethcmn.Bytes2Hex(k.CommitStateDB.GetCode(addr)), + Code: ethcmn.Bytes2Hex(k.GetCode(addr)), Storage: storage, } diff --git a/x/evm/genesis_test.go b/x/evm/genesis_test.go index 9f42b2b4..a72c75ef 100644 --- a/x/evm/genesis_test.go +++ b/x/evm/genesis_test.go @@ -91,13 +91,13 @@ func (suite *EvmTestSuite) TestInitGenesis() { if tc.expPanic { suite.Require().Panics( func() { - _ = evm.InitGenesis(suite.ctx, suite.app.EvmKeeper, suite.app.AccountKeeper, suite.app.BankKeeper, *tc.genState) + _ = evm.InitGenesis(suite.ctx, suite.app.EvmKeeper, suite.app.AccountKeeper, *tc.genState) }, ) } else { suite.Require().NotPanics( func() { - _ = evm.InitGenesis(suite.ctx, suite.app.EvmKeeper, suite.app.AccountKeeper, suite.app.BankKeeper, *tc.genState) + _ = evm.InitGenesis(suite.ctx, suite.app.EvmKeeper, suite.app.AccountKeeper, *tc.genState) }, ) } diff --git a/x/evm/handler.go b/x/evm/handler.go index c820b993..d4e90556 100644 --- a/x/evm/handler.go +++ b/x/evm/handler.go @@ -8,12 +8,11 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - "github.com/cosmos/ethermint/x/evm/keeper" "github.com/cosmos/ethermint/x/evm/types" ) // NewHandler returns a handler for Ethermint type messages. -func NewHandler(k *keeper.Keeper) sdk.Handler { +func NewHandler(server types.MsgServer) sdk.Handler { return func(ctx sdk.Context, msg sdk.Msg) (result *sdk.Result, err error) { defer Recover(&err) @@ -22,7 +21,7 @@ func NewHandler(k *keeper.Keeper) sdk.Handler { switch msg := msg.(type) { case *types.MsgEthereumTx: // execute state transition - res, err := k.EthereumTx(sdk.WrapSDKContext(ctx), msg) + res, err := server.EthereumTx(sdk.WrapSDKContext(ctx), msg) return sdk.WrapServiceResult(ctx, res, err) default: diff --git a/x/evm/handler_test.go b/x/evm/handler_test.go index e6418025..480943cf 100644 --- a/x/evm/handler_test.go +++ b/x/evm/handler_test.go @@ -49,7 +49,7 @@ func (suite *EvmTestSuite) SetupTest() { suite.app = app.Setup(checkTx) suite.ctx = suite.app.BaseApp.NewContext(checkTx, tmproto.Header{Height: 1, ChainID: "ethermint-1", Time: time.Now().UTC()}) - suite.app.EvmKeeper.CommitStateDB.WithContext(suite.ctx) + suite.app.EvmKeeper.WithContext(suite.ctx) suite.handler = evm.NewHandler(suite.app.EvmKeeper) suite.codec = suite.app.AppCodec() suite.chainID = suite.app.EvmKeeper.ChainID() @@ -84,7 +84,6 @@ func (suite *EvmTestSuite) TestHandleMsgEthereumTx() { { "passed", func() { - suite.app.EvmKeeper.CommitStateDB.SetBalance(suite.from, big.NewInt(100)) to := ethcmn.BytesToAddress(suite.to) tx = types.NewMsgEthereumTx(suite.chainID, 0, &to, big.NewInt(100), 0, big.NewInt(10000), nil, nil) tx.From = suite.from.String() @@ -189,11 +188,10 @@ func (suite *EvmTestSuite) TestHandlerLogs() { suite.Require().Equal(len(txResponse.Logs[0].Topics), 2) hash := []byte{1} - err = suite.app.EvmKeeper.CommitStateDB.SetLogs(ethcmn.BytesToHash(hash), types.LogsToEthereum(txResponse.Logs)) + suite.app.EvmKeeper.SetLogs(ethcmn.BytesToHash(hash), types.LogsToEthereum(txResponse.Logs)) suite.Require().NoError(err) - logs, err := suite.app.EvmKeeper.CommitStateDB.GetLogs(ethcmn.BytesToHash(hash)) - suite.Require().NoError(err, "failed to get logs") + logs := suite.app.EvmKeeper.GetTxLogs(ethcmn.BytesToHash(hash)) suite.Require().Equal(logs, txResponse.Logs) } @@ -310,8 +308,6 @@ func (suite *EvmTestSuite) TestSendTransaction() { gasLimit := uint64(21000) gasPrice := big.NewInt(0x55ae82600) - suite.app.EvmKeeper.CommitStateDB.SetBalance(suite.from, big.NewInt(100)) - // send simple value transfer with gasLimit=21000 tx := types.NewMsgEthereumTx(suite.chainID, 1, ðcmn.Address{0x1}, big.NewInt(1), gasLimit, gasPrice, nil, nil) tx.From = suite.from.String() @@ -390,12 +386,12 @@ func (suite *EvmTestSuite) TestOutOfGasWhenDeployContract() { err := tx.Sign(suite.ethSigner, suite.signer) suite.Require().NoError(err) - snapshotCommitStateDBJson, err := json.Marshal(suite.app.EvmKeeper.CommitStateDB) + snapshotCommitStateDBJson, err := json.Marshal(suite.app.EvmKeeper) suite.Require().Nil(err) defer func() { if r := recover(); r != nil { - currentCommitStateDBJson, err := json.Marshal(suite.app.EvmKeeper.CommitStateDB) + currentCommitStateDBJson, err := json.Marshal(suite.app.EvmKeeper) suite.Require().Nil(err) suite.Require().Equal(snapshotCommitStateDBJson, currentCommitStateDBJson) } else { @@ -419,13 +415,13 @@ func (suite *EvmTestSuite) TestErrorWhenDeployContract() { err := tx.Sign(suite.ethSigner, suite.signer) suite.Require().NoError(err) - snapshotCommitStateDBJson, err := json.Marshal(suite.app.EvmKeeper.CommitStateDB) + snapshotCommitStateDBJson, err := json.Marshal(suite.app.EvmKeeper) suite.Require().Nil(err) _, sdkErr := suite.handler(suite.ctx, tx) suite.Require().NotNil(sdkErr) - currentCommitStateDBJson, err := json.Marshal(suite.app.EvmKeeper.CommitStateDB) + currentCommitStateDBJson, err := json.Marshal(suite.app.EvmKeeper) suite.Require().Nil(err) suite.Require().Equal(snapshotCommitStateDBJson, currentCommitStateDBJson) } diff --git a/x/evm/keeper/abci.go b/x/evm/keeper/abci.go index da0ad620..c286fc75 100644 --- a/x/evm/keeper/abci.go +++ b/x/evm/keeper/abci.go @@ -27,29 +27,13 @@ func (k *Keeper) BeginBlock(ctx sdk.Context, req abci.RequestBeginBlock) { // deleting the empty ones. It also sets the bloom filers for the request block to // the store. The EVM end block logic doesn't update the validator set, thus it returns // an empty slice. -func (k Keeper) EndBlock(ctx sdk.Context, req abci.RequestEndBlock) []abci.ValidatorUpdate { +func (k *Keeper) EndBlock(ctx sdk.Context, req abci.RequestEndBlock) []abci.ValidatorUpdate { defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), telemetry.MetricKeyEndBlocker) // Gas costs are handled within msg handler so costs should be ignored - ctx = ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) - k.CommitStateDB.WithContext(ctx) + infCtx := ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) k.WithContext(ctx) - // Update account balances before committing other parts of state - k.CommitStateDB.UpdateAccounts() - - root, err := k.CommitStateDB.Commit(true) - // Commit state objects to KV store - if err != nil { - k.Logger(ctx).Error("failed to commit state objects", "error", err, "height", ctx.BlockHeight()) - panic(err) - } - - // reset all cache after account data has been committed, that make sure node state consistent - if err = k.CommitStateDB.Reset(root); err != nil { - panic(err) - } - // get the block bloom bytes from the transient store and set it to the persistent storage bloomBig, found := k.GetBlockBloomTransient() if !found { @@ -57,7 +41,8 @@ func (k Keeper) EndBlock(ctx sdk.Context, req abci.RequestEndBlock) []abci.Valid } bloom := ethtypes.BytesToBloom(bloomBig.Bytes()) - k.SetBlockBloom(ctx, req.Height, bloom) + k.SetBlockBloom(infCtx, req.Height, bloom) + k.WithContext(ctx) return []abci.ValidatorUpdate{} } diff --git a/x/evm/keeper/grpc_query.go b/x/evm/keeper/grpc_query.go index 6e030c1c..2f96f8bc 100644 --- a/x/evm/keeper/grpc_query.go +++ b/x/evm/keeper/grpc_query.go @@ -2,13 +2,10 @@ package keeper import ( "context" - "math/big" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" - tmtypes "github.com/tendermint/tendermint/types" - "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" "github.com/cosmos/cosmos-sdk/types/query" @@ -33,19 +30,15 @@ func (k Keeper) Account(c context.Context, req *types.QueryAccountRequest) (*typ ) } - ctx := sdk.UnwrapSDKContext(c) - k.CommitStateDB.WithContext(ctx) + addr := ethcmn.HexToAddress(req.Address) - so := k.CommitStateDB.GetOrNewStateObject(ethcmn.HexToAddress(req.Address)) - balance, err := ethermint.MarshalBigInt(so.Balance()) - if err != nil { - return nil, err - } + ctx := sdk.UnwrapSDKContext(c) + k.WithContext(ctx) return &types.QueryAccountResponse{ - Balance: balance, - CodeHash: so.CodeHash(), - Nonce: so.Nonce(), + Balance: k.GetBalance(addr).String(), + CodeHash: k.GetCodeHash(addr).Hex(), + Nonce: k.GetNonce(addr), }, nil } @@ -61,7 +54,7 @@ func (k Keeper) CosmosAccount(c context.Context, req *types.QueryCosmosAccountRe } ctx := sdk.UnwrapSDKContext(c) - k.CommitStateDB.WithContext(ctx) + k.WithContext(ctx) ethAddr := ethcmn.HexToAddress(req.Address) cosmosAddr := sdk.AccAddress(ethAddr.Bytes()) @@ -93,9 +86,9 @@ func (k Keeper) Balance(c context.Context, req *types.QueryBalanceRequest) (*typ } ctx := sdk.UnwrapSDKContext(c) - k.CommitStateDB.WithContext(ctx) + k.WithContext(ctx) - balanceInt := k.CommitStateDB.GetBalance(ethcmn.HexToAddress(req.Address)) + balanceInt := k.GetBalance(ethcmn.HexToAddress(req.Address)) balance, err := ethermint.MarshalBigInt(balanceInt) if err != nil { return nil, status.Error( @@ -130,12 +123,12 @@ func (k Keeper) Storage(c context.Context, req *types.QueryStorageRequest) (*typ } ctx := sdk.UnwrapSDKContext(c) - k.CommitStateDB.WithContext(ctx) + k.WithContext(ctx) address := ethcmn.HexToAddress(req.Address) key := ethcmn.HexToHash(req.Key) - state := k.CommitStateDB.GetState(address, key) + state := k.GetState(address, key) stateHex := state.Hex() if ethermint.IsEmptyHash(stateHex) { @@ -163,10 +156,10 @@ func (k Keeper) Code(c context.Context, req *types.QueryCodeRequest) (*types.Que } ctx := sdk.UnwrapSDKContext(c) - k.CommitStateDB.WithContext(ctx) + k.WithContext(ctx) address := ethcmn.HexToAddress(req.Address) - code := k.CommitStateDB.GetCode(address) + code := k.GetCode(address) return &types.QueryCodeResponse{ Code: code, @@ -187,19 +180,13 @@ func (k Keeper) TxLogs(c context.Context, req *types.QueryTxLogsRequest) (*types } ctx := sdk.UnwrapSDKContext(c) - k.CommitStateDB.WithContext(ctx) + k.WithContext(ctx) hash := ethcmn.HexToHash(req.Hash) - logs, err := k.CommitStateDB.GetLogs(hash) - if err != nil { - return nil, status.Error( - codes.Internal, - err.Error(), - ) - } + logs := k.GetTxLogs(hash) return &types.QueryTxLogsResponse{ - Logs: types.NewTransactionLogsFromEth(hash, logs).Logs, + Logs: types.NewLogsFromEth(logs), }, nil } @@ -277,59 +264,61 @@ func (k Keeper) StaticCall(c context.Context, req *types.QueryStaticCallRequest) return nil, status.Error(codes.InvalidArgument, "empty request") } - ctx := sdk.UnwrapSDKContext(c) - k.CommitStateDB.WithContext(ctx) + // ctx := sdk.UnwrapSDKContext(c) + // k.WithContext(ctx) - // parse the chainID from a string to a base-10 integer - chainIDEpoch, err := ethermint.ParseChainID(ctx.ChainID()) - if err != nil { - return nil, status.Error(codes.Internal, err.Error()) - } + // // parse the chainID from a string to a base-10 integer + // chainIDEpoch, err := ethermint.ParseChainID(ctx.ChainID()) + // if err != nil { + // return nil, status.Error(codes.Internal, err.Error()) + // } - txHash := tmtypes.Tx(ctx.TxBytes()).Hash() - ethHash := ethcmn.BytesToHash(txHash) + // txHash := tmtypes.Tx(ctx.TxBytes()).Hash() + // ethHash := ethcmn.BytesToHash(txHash) - var recipient *ethcmn.Address - if len(req.Address) > 0 { - addr := ethcmn.HexToAddress(req.Address) - recipient = &addr - } + // var recipient *ethcmn.Address + // if len(req.Address) > 0 { + // addr := ethcmn.HexToAddress(req.Address) + // recipient = &addr + // } - so := k.CommitStateDB.GetOrNewStateObject(*recipient) - sender := ethcmn.HexToAddress("0xaDd00275E3d9d213654Ce5223f0FADE8b106b707") + // so := k.GetOrNewStateObject(*recipient) + // sender := ethcmn.HexToAddress("0xaDd00275E3d9d213654Ce5223f0FADE8b106b707") - msg := types.NewMsgEthereumTx( - chainIDEpoch, so.Nonce(), recipient, big.NewInt(0), 100000000, big.NewInt(0), req.Input, nil, - ) - msg.From = sender.Hex() + // msg := types.NewMsgEthereumTx( + // chainIDEpoch, so.Nonce(), recipient, big.NewInt(0), 100000000, big.NewInt(0), req.Input, nil, + // ) + // msg.From = sender.Hex() - if err := msg.ValidateBasic(); err != nil { - return nil, status.Error(codes.Internal, err.Error()) - } + // if err := msg.ValidateBasic(); err != nil { + // return nil, status.Error(codes.Internal, err.Error()) + // } - ethMsg, err := msg.AsMessage() - if err != nil { - return nil, status.Error(codes.Internal, err.Error()) - } + // ethMsg, err := msg.AsMessage() + // if err != nil { + // return nil, status.Error(codes.Internal, err.Error()) + // } - st := &types.StateTransition{ - Message: ethMsg, - Csdb: k.CommitStateDB.WithContext(ctx), - ChainID: chainIDEpoch, - TxHash: ðHash, - Simulate: ctx.IsCheckTx(), - Debug: false, - } + // st := &types.StateTransition{ + // Message: ethMsg, + // Csdb: k.WithContext(ctx), + // ChainID: chainIDEpoch, + // TxHash: ðHash, + // Simulate: ctx.IsCheckTx(), + // Debug: false, + // } - config, found := k.GetChainConfig(ctx) - if !found { - return nil, status.Error(codes.Internal, types.ErrChainConfigNotFound.Error()) - } + // config, found := k.GetChainConfig(ctx) + // if !found { + // return nil, status.Error(codes.Internal, types.ErrChainConfigNotFound.Error()) + // } - ret, err := st.StaticCall(ctx, config) - if err != nil { - return nil, status.Error(codes.Internal, err.Error()) - } + // ret, err := st.StaticCall(ctx, config) + // if err != nil { + // return nil, status.Error(codes.Internal, err.Error()) + // } - return &types.QueryStaticCallResponse{Data: ret}, nil + // return &types.QueryStaticCallResponse{Data: ret}, nil + + return nil, nil } diff --git a/x/evm/keeper/grpc_query_test.go b/x/evm/keeper/grpc_query_test.go index 3129b28f..2e5c9cc6 100644 --- a/x/evm/keeper/grpc_query_test.go +++ b/x/evm/keeper/grpc_query_test.go @@ -5,6 +5,7 @@ import ( "google.golang.org/grpc/metadata" + "github.com/ethereum/go-ethereum/common" ethcmn "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" ethcrypto "github.com/ethereum/go-ethereum/crypto" @@ -33,7 +34,7 @@ func (suite *KeeperTestSuite) TestQueryAccount() { suite.app.BankKeeper.SetBalance(suite.ctx, suite.address.Bytes(), ethermint.NewPhotonCoinInt64(0)) expAccount = &types.QueryAccountResponse{ Balance: "0", - CodeHash: ethcrypto.Keccak256(nil), + CodeHash: common.BytesToHash(ethcrypto.Keccak256(nil)).Hex(), Nonce: 0, } req = &types.QueryAccountRequest{ @@ -48,7 +49,7 @@ func (suite *KeeperTestSuite) TestQueryAccount() { suite.app.BankKeeper.SetBalance(suite.ctx, suite.address.Bytes(), ethermint.NewPhotonCoinInt64(100)) expAccount = &types.QueryAccountResponse{ Balance: "100", - CodeHash: ethcrypto.Keccak256(nil), + CodeHash: common.BytesToHash(ethcrypto.Keccak256(nil)).Hex(), Nonce: 0, } req = &types.QueryAccountRequest{ @@ -245,7 +246,7 @@ func (suite *KeeperTestSuite) TestQueryStorage() { key := ethcmn.BytesToHash([]byte("key")) value := ethcmn.BytesToHash([]byte("value")) expValue = value.String() - suite.app.EvmKeeper.CommitStateDB.SetState(suite.address, key, value) + suite.app.EvmKeeper.SetState(suite.address, key, value) req = &types.QueryStorageRequest{ Address: suite.address.String(), Key: key.String(), @@ -300,7 +301,7 @@ func (suite *KeeperTestSuite) TestQueryCode() { "success", func() { expCode = []byte("code") - suite.app.EvmKeeper.CommitStateDB.SetCode(suite.address, expCode) + suite.app.EvmKeeper.SetCode(suite.address, expCode) req = &types.QueryCodeRequest{ Address: suite.address.String(), @@ -377,7 +378,7 @@ func (suite *KeeperTestSuite) TestQueryTxLogs() { }, } - suite.app.EvmKeeper.CommitStateDB.SetLogs(hash, types.LogsToEthereum(expLogs)) + suite.app.EvmKeeper.SetLogs(hash, types.LogsToEthereum(expLogs)) req = &types.QueryTxLogsRequest{ Hash: hash.String(), @@ -486,8 +487,8 @@ func (suite *KeeperTestSuite) TestQueryBlockLogs() { }, } - suite.app.EvmKeeper.CommitStateDB.SetLogs(ethcmn.BytesToHash([]byte("tx_hash_0")), types.LogsToEthereum(expLogs[0].Logs)) - suite.app.EvmKeeper.CommitStateDB.SetLogs(ethcmn.BytesToHash([]byte("tx_hash_1")), types.LogsToEthereum(expLogs[1].Logs)) + suite.app.EvmKeeper.SetLogs(ethcmn.BytesToHash([]byte("tx_hash_0")), types.LogsToEthereum(expLogs[0].Logs)) + suite.app.EvmKeeper.SetLogs(ethcmn.BytesToHash([]byte("tx_hash_1")), types.LogsToEthereum(expLogs[1].Logs)) req = &types.QueryBlockLogsRequest{ Hash: hash.String(), diff --git a/x/evm/keeper/invariants.go b/x/evm/keeper/invariants.go deleted file mode 100644 index 17713e77..00000000 --- a/x/evm/keeper/invariants.go +++ /dev/null @@ -1,105 +0,0 @@ -package keeper - -import ( - "fmt" - - sdk "github.com/cosmos/cosmos-sdk/types" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - - ethermint "github.com/cosmos/ethermint/types" - "github.com/cosmos/ethermint/x/evm/types" -) - -const ( - balanceInvariant = "balance" - nonceInvariant = "nonce" -) - -// RegisterInvariants registers the evm module invariants -func RegisterInvariants(ir sdk.InvariantRegistry, k Keeper) { - ir.RegisterRoute(types.ModuleName, balanceInvariant, k.BalanceInvariant()) - ir.RegisterRoute(types.ModuleName, nonceInvariant, k.NonceInvariant()) -} - -// BalanceInvariant checks that all auth module's EthAccounts in the application have the same balance -// as the EVM one. -func (k Keeper) BalanceInvariant() sdk.Invariant { - return func(ctx sdk.Context) (string, bool) { - var ( - msg string - count int - ) - - k.CommitStateDB.WithContext(ctx) - - k.accountKeeper.IterateAccounts(ctx, func(account authtypes.AccountI) bool { - ethAccount, ok := account.(*ethermint.EthAccount) - if !ok { - // ignore non EthAccounts - return false - } - - evmDenom := k.GetParams(ctx).EvmDenom - - accountBalance := k.bankKeeper.GetBalance(ctx, ethAccount.GetAddress(), evmDenom) - evmBalance := k.CommitStateDB.GetBalance(ethAccount.EthAddress()) - - if evmBalance.Cmp(accountBalance.Amount.BigInt()) != 0 { - count++ - msg += fmt.Sprintf( - "\tbalance mismatch for address %s: account balance %s, evm balance %s\n", - account.GetAddress(), accountBalance.String(), evmBalance.String(), - ) - } - - return false - }) - - broken := count != 0 - - return sdk.FormatInvariant( - types.ModuleName, balanceInvariant, - fmt.Sprintf("account balances mismatches found %d\n%s", count, msg), - ), broken - } -} - -// NonceInvariant checks that all auth module's EthAccounts in the application have the same nonce -// sequence as the EVM. -func (k Keeper) NonceInvariant() sdk.Invariant { - return func(ctx sdk.Context) (string, bool) { - var ( - msg string - count int - ) - - k.CommitStateDB.WithContext(ctx) - - k.accountKeeper.IterateAccounts(ctx, func(account authtypes.AccountI) bool { - ethAccount, ok := account.(*ethermint.EthAccount) - if !ok { - // ignore non EthAccounts - return false - } - - evmNonce := k.CommitStateDB.GetNonce(ethAccount.EthAddress()) - - if evmNonce != ethAccount.Sequence { - count++ - msg += fmt.Sprintf( - "\nonce mismatch for address %s: account nonce %d, evm nonce %d\n", - account.GetAddress(), ethAccount.Sequence, evmNonce, - ) - } - - return false - }) - - broken := count != 0 - - return sdk.FormatInvariant( - types.ModuleName, nonceInvariant, - fmt.Sprintf("account nonces mismatches found %d\n%s", count, msg), - ), broken - } -} diff --git a/x/evm/keeper/invariants_test.go b/x/evm/keeper/invariants_test.go deleted file mode 100644 index b8bb167c..00000000 --- a/x/evm/keeper/invariants_test.go +++ /dev/null @@ -1,138 +0,0 @@ -package keeper_test - -import ( - "math/big" - - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - "github.com/cosmos/ethermint/crypto/ethsecp256k1" - ethermint "github.com/cosmos/ethermint/types" - ethcmn "github.com/ethereum/go-ethereum/common" -) - -func (suite *KeeperTestSuite) TestBalanceInvariant() { - privkey, err := ethsecp256k1.GenerateKey() - suite.Require().NoError(err) - - address := ethcmn.HexToAddress(privkey.PubKey().Address().String()) - - testCases := []struct { - name string - malleate func() - expBroken bool - }{ - { - "balance mismatch", - func() { - acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, address.Bytes()) - suite.Require().NotNil(acc) - suite.app.BankKeeper.SetBalance(suite.ctx, acc.GetAddress(), ethermint.NewPhotonCoinInt64(1)) - suite.Require().NoError(err) - suite.app.AccountKeeper.SetAccount(suite.ctx, acc) - - suite.app.EvmKeeper.CommitStateDB.SetBalance(address, big.NewInt(1000)) - }, - true, - }, - { - "balance ok", - func() { - acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, address.Bytes()) - suite.Require().NotNil(acc) - suite.app.BankKeeper.SetBalance(suite.ctx, acc.GetAddress(), ethermint.NewPhotonCoinInt64(1)) - suite.Require().NoError(err) - suite.app.AccountKeeper.SetAccount(suite.ctx, acc) - - suite.app.EvmKeeper.CommitStateDB.SetBalance(address, big.NewInt(1)) - }, - false, - }, - { - "invalid account type", - func() { - acc := authtypes.NewBaseAccountWithAddress(address.Bytes()) - suite.app.AccountKeeper.SetAccount(suite.ctx, acc) - }, - false, - }, - } - - for _, tc := range testCases { - suite.Run(tc.name, func() { - suite.SetupTest() // reset values - - suite.app.EvmKeeper.CommitStateDB.WithContext(suite.ctx) - tc.malleate() - - _, broken := suite.app.EvmKeeper.BalanceInvariant()(suite.ctx) - if tc.expBroken { - suite.Require().True(broken) - } else { - suite.Require().False(broken) - } - }) - } -} - -func (suite *KeeperTestSuite) TestNonceInvariant() { - privkey, err := ethsecp256k1.GenerateKey() - suite.Require().NoError(err) - - address := ethcmn.HexToAddress(privkey.PubKey().Address().String()) - - testCases := []struct { - name string - malleate func() - expBroken bool - }{ - { - "nonce mismatch", - func() { - acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, address.Bytes()) - suite.Require().NotNil(acc) - err := acc.SetSequence(1) - suite.Require().NoError(err) - suite.app.AccountKeeper.SetAccount(suite.ctx, acc) - - suite.app.EvmKeeper.CommitStateDB.SetNonce(address, 100) - }, - true, - }, - { - "nonce ok", - func() { - acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, address.Bytes()) - suite.Require().NotNil(acc) - err := acc.SetSequence(1) - suite.Require().NoError(err) - suite.app.AccountKeeper.SetAccount(suite.ctx, acc) - - suite.app.EvmKeeper.CommitStateDB.SetNonce(address, 1) - }, - false, - }, - { - "invalid account type", - func() { - acc := authtypes.NewBaseAccountWithAddress(address.Bytes()) - suite.app.AccountKeeper.SetAccount(suite.ctx, acc) - }, - false, - }, - } - - for _, tc := range testCases { - suite.Run(tc.name, func() { - suite.SetupTest() // reset values - - suite.app.EvmKeeper.CommitStateDB.WithContext(suite.ctx) - tc.malleate() - - _, broken := suite.app.EvmKeeper.NonceInvariant()(suite.ctx) - if tc.expBroken { - suite.Require().True(broken) - } else { - suite.Require().False(broken) - } - }) - } -} diff --git a/x/evm/keeper/keeper.go b/x/evm/keeper/keeper.go index a683bbfd..4aaae459 100644 --- a/x/evm/keeper/keeper.go +++ b/x/evm/keeper/keeper.go @@ -42,10 +42,6 @@ type Keeper struct { eip155ChainID *big.Int debug bool - // TODO: deprecate - // Ethermint concrete implementation on the EVM StateDB interface - CommitStateDB *types.CommitStateDB - // hash header for the current height. Reset during abci.RequestBeginBlock headerHash common.Hash } @@ -69,7 +65,6 @@ func NewKeeper( stakingKeeper: sk, storeKey: storeKey, transientKey: transientKey, - CommitStateDB: types.NewCommitStateDB(sdk.Context{}, storeKey, transientKey, paramSpace, ak, bankKeeper), } } diff --git a/x/evm/keeper/msg_server.go b/x/evm/keeper/msg_server.go index 00af47af..0af33f67 100644 --- a/x/evm/keeper/msg_server.go +++ b/x/evm/keeper/msg_server.go @@ -2,20 +2,15 @@ package keeper import ( "context" - "errors" - "math/big" "time" "github.com/armon/go-metrics" - ethcmn "github.com/ethereum/go-ethereum/common" - ethtypes "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/core/vm" + tmbytes "github.com/tendermint/tendermint/libs/bytes" tmtypes "github.com/tendermint/tendermint/types" "github.com/cosmos/cosmos-sdk/telemetry" sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/ethermint/x/evm/types" ) @@ -26,17 +21,7 @@ func (k *Keeper) EthereumTx(goCtx context.Context, msg *types.MsgEthereumTx) (*t defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), types.TypeMsgEthereumTx) ctx := sdk.UnwrapSDKContext(goCtx) - k.CommitStateDB.WithContext(ctx) - - ethMsg, err := msg.AsMessage() - if err != nil { - return nil, sdkerrors.Wrap(sdkerrors.ErrorInvalidSigner, err.Error()) - } - - config, found := k.GetChainConfig(ctx) - if !found { - return nil, types.ErrChainConfigNotFound - } + k.WithContext(ctx) var labels []metrics.Label if msg.To() == nil { @@ -46,62 +31,22 @@ func (k *Keeper) EthereumTx(goCtx context.Context, msg *types.MsgEthereumTx) (*t } else { labels = []metrics.Label{ telemetry.NewLabel("execution", "call"), - // add label to the called recipient address (contract or account) - telemetry.NewLabel("to", msg.Data.To), + telemetry.NewLabel("to", msg.Data.To), // recipient address (contract or account) } } - sender := ethMsg.From() + sender := msg.From + tx := msg.AsTransaction() - txHash := tmtypes.Tx(ctx.TxBytes()).Hash() - ethHash := ethcmn.BytesToHash(txHash) - - // Ethereum formatted tx hash - etherumTxHash := msg.AsTransaction().Hash() - - st := &types.StateTransition{ - Message: ethMsg, - Csdb: k.CommitStateDB.WithContext(ctx), - ChainID: msg.ChainID(), - TxHash: ðHash, - 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 - k.CommitStateDB.Prepare(ethHash, k.headerHash, int(k.GetTxIndexTransient())) - k.IncreaseTxIndexTransient() - } - - executionResult, err := st.TransitionDb(ctx, config) + response, err := k.ApplyTransaction(tx) if err != nil { - if errors.Is(err, vm.ErrExecutionReverted) && executionResult != nil { - // keep the execution result for revert reason - executionResult.Response.Reverted = true - return executionResult.Response, nil - } - return nil, err } - if !st.Simulate { - bloom, found := k.GetBlockBloomTransient() - if !found { - bloom = big.NewInt(0) - } - // update block bloom filter - logsBloom := ethtypes.LogsBloom(executionResult.Logs) - bloom = bloom.Or(bloom, new(big.Int).SetBytes(logsBloom)) - k.SetBlockBloomTransient(bloom) - } - defer func() { - if st.Message.Value().IsInt64() { + if tx.Value().IsInt64() { telemetry.SetGauge( - float32(st.Message.Value().Int64()), + float32(tx.Value().Int64()), "tx", "msg", "ethereum_tx", ) } @@ -114,9 +59,15 @@ func (k *Keeper) EthereumTx(goCtx context.Context, msg *types.MsgEthereumTx) (*t }() attrs := []sdk.Attribute{ - sdk.NewAttribute(sdk.AttributeKeyAmount, st.Message.Value().String()), - sdk.NewAttribute(types.AttributeKeyTxHash, ethcmn.BytesToHash(txHash).Hex()), - sdk.NewAttribute(types.AttributeKeyEthereumTxHash, etherumTxHash.Hex()), + sdk.NewAttribute(sdk.AttributeKeyAmount, tx.Value().String()), + // add event for ethereum transaction hash format + sdk.NewAttribute(types.AttributeKeyEthereumTxHash, response.Hash), + } + + if len(ctx.TxBytes()) > 0 { + // add event for tendermint transaction hash format + hash := tmbytes.HexBytes(tmtypes.Tx(ctx.TxBytes()).Hash()) + attrs = append(attrs, sdk.NewAttribute(types.AttributeKeyTxHash, hash.String())) } if len(msg.Data.To) > 0 { @@ -132,10 +83,9 @@ func (k *Keeper) EthereumTx(goCtx context.Context, msg *types.MsgEthereumTx) (*t sdk.NewEvent( sdk.EventTypeMessage, sdk.NewAttribute(sdk.AttributeKeyModule, types.AttributeValueCategory), - sdk.NewAttribute(sdk.AttributeKeySender, sender.String()), + sdk.NewAttribute(sdk.AttributeKeySender, sender), ), }) - executionResult.Response.Hash = etherumTxHash.Hex() - return executionResult.Response, nil + return response, nil } diff --git a/x/evm/keeper/state_transition.go b/x/evm/keeper/state_transition.go index 430b1ca4..88abe21c 100644 --- a/x/evm/keeper/state_transition.go +++ b/x/evm/keeper/state_transition.go @@ -18,6 +18,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" + ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/params" ) @@ -91,7 +92,7 @@ func (k Keeper) GetHashFn() vm.GetHashFunc { } } -// TransitionDb runs and attempts to perform a state transition with the given transaction (i.e Message), that will +// ApplyTransaction runs and attempts to perform a state transition with the given transaction (i.e Message), that will // only be persisted to the underlying KVStore if the transaction does not error. // // Gas tracking @@ -108,23 +109,39 @@ func (k Keeper) GetHashFn() vm.GetHashFunc { // returning. // // For relevant discussion see: https://github.com/cosmos/cosmos-sdk/discussions/9072 -func (k *Keeper) TransitionDb(msg core.Message) (*types.ExecutionResult, error) { +func (k *Keeper) ApplyTransaction(tx *ethtypes.Transaction) (*types.MsgEthereumTxResponse, error) { defer telemetry.ModuleMeasureSince(types.ModuleName, time.Now(), types.MetricKeyTransitionDB) - cfg, found := k.GetChainConfig(k.ctx) + gasMeter := k.ctx.GasMeter() // tx gas meter + infCtx := k.ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) + + cfg, found := k.GetChainConfig(infCtx) if !found { return nil, types.ErrChainConfigNotFound } + ethCfg := cfg.EthereumConfig(k.eip155ChainID) - evm := k.NewEVM(msg, cfg.EthereumConfig(k.eip155ChainID)) - - // create an ethereum StateTransition instance and run TransitionDb - result, err := k.ApplyMessage(evm, msg) + msg, err := tx.AsMessage(ethtypes.MakeSigner(ethCfg, big.NewInt(k.ctx.BlockHeight()))) if err != nil { return nil, err } - return result, nil + evm := k.NewEVM(msg, ethCfg) + + k.IncreaseTxIndexTransient() + + k.WithContext(k.ctx.WithGasMeter(gasMeter)) + // create an ethereum StateTransition instance and run TransitionDb + res, err := k.ApplyMessage(evm, msg, ethCfg) + if err != nil { + return nil, err + } + + txHash := tx.Hash() + res.Hash = txHash.Hex() + res.Logs = types.NewLogsFromEth(k.GetTxLogs(txHash)) + + return res, nil } // Gas consumption notes (write doc from this) @@ -143,10 +160,10 @@ func (k *Keeper) TransitionDb(msg core.Message) (*types.ExecutionResult, error) // TODO: (@fedekunze) currently we consume the entire gas limit in the ante handler, so if a transaction fails // the amount spent will be grater than the gas spent in an Ethereum tx (i.e here the leftover gas won't be refunded). -func (k *Keeper) ApplyMessage(evm *vm.EVM, msg core.Message) (*types.ExecutionResult, error) { +func (k *Keeper) ApplyMessage(evm *vm.EVM, msg core.Message, cfg *params.ChainConfig) (*types.MsgEthereumTxResponse, error) { var ( - ret []byte // return bytes from evm execution - vmErr, err error // vm errors do not effect consensus and are therefore not assigned to err + ret []byte // return bytes from evm execution + vmErr error // vm errors do not effect consensus and are therefore not assigned to err ) sender := vm.AccountRef(msg.From()) @@ -166,7 +183,7 @@ func (k *Keeper) ApplyMessage(evm *vm.EVM, msg core.Message) (*types.ExecutionRe // ensure gas is consistent during CheckTx if k.ctx.IsCheckTx() { - if err := k.checkGasConsumption(msg, gasConsumed, contractCreation); err != nil { + if err := k.CheckGasConsumption(msg, cfg, gasConsumed, contractCreation); err != nil { return nil, err } } @@ -178,8 +195,7 @@ func (k *Keeper) ApplyMessage(evm *vm.EVM, msg core.Message) (*types.ExecutionRe } // refund gas prior to handling the vm error in order to set the updated gas meter - gasConsumed, leftoverGas, err = k.refundGas(msg, leftoverGas) - if err != nil { + if err := k.RefundGas(msg, leftoverGas); err != nil { return nil, err } @@ -193,26 +209,17 @@ func (k *Keeper) ApplyMessage(evm *vm.EVM, msg core.Message) (*types.ExecutionRe return nil, sdkerrors.Wrap(types.ErrVMExecution, vmErr.Error()) } - return &types.ExecutionResult{ - Response: &types.MsgEthereumTxResponse{ - Ret: ret, - }, - GasInfo: types.GasInfo{ - GasLimit: k.ctx.GasMeter().Limit(), - GasConsumed: gasConsumed, - GasRefunded: leftoverGas, - }, + return &types.MsgEthereumTxResponse{ + Ret: ret, + Reverted: false, }, nil } -// checkGasConsumption verifies that the amount of gas consumed so far matches the intrinsic gas value. -func (k *Keeper) checkGasConsumption(msg core.Message, gasConsumed uint64, isContractCreation bool) error { - cfg, _ := k.GetChainConfig(k.ctx) - ethCfg := cfg.EthereumConfig(k.eip155ChainID) - +// CheckGasConsumption verifies that the amount of gas consumed so far matches the intrinsic gas value. +func (k *Keeper) CheckGasConsumption(msg core.Message, cfg *params.ChainConfig, gasConsumed uint64, isContractCreation bool) error { height := big.NewInt(k.ctx.BlockHeight()) - homestead := ethCfg.IsHomestead(height) - istanbul := ethCfg.IsIstanbul(height) + homestead := cfg.IsHomestead(height) + istanbul := cfg.IsIstanbul(height) intrinsicGas, err := core.IntrinsicGas(msg.Data(), msg.AccessList(), isContractCreation, homestead, istanbul) if err != nil { @@ -227,11 +234,11 @@ func (k *Keeper) checkGasConsumption(msg core.Message, gasConsumed uint64, isCon return nil } -// refundGas transfers the leftover gas to the sender of the message, caped to half of the total gas +// RefundGas transfers the leftover gas to the sender of the message, caped to half of the total gas // consumed in the transaction. Additionally, the function sets the total gas consumed to the value // returned by the EVM execution, thus ignoring the previous intrinsic gas inconsumed during in the // AnteHandler. -func (k *Keeper) refundGas(msg core.Message, leftoverGas uint64) (consumed, leftover uint64, err error) { +func (k *Keeper) RefundGas(msg core.Message, leftoverGas uint64) error { gasConsumed := msg.Gas() - leftoverGas // Apply refund counter, capped to half of the used gas. @@ -246,18 +253,21 @@ func (k *Keeper) refundGas(msg core.Message, leftoverGas uint64) (consumed, left // Return EVM tokens for remaining gas, exchanged at the original rate. remaining := new(big.Int).Mul(new(big.Int).SetUint64(leftoverGas), msg.GasPrice()) + // ignore gas consumption + infCtx := k.ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) + switch remaining.Sign() { case -1: // negative refund errors - return 0, 0, fmt.Errorf("refunded amount value cannot be negative %d", remaining.Int64()) + return fmt.Errorf("refunded amount value cannot be negative %d", remaining.Int64()) case 1: // positive amount refund - params := k.GetParams(k.ctx) + params := k.GetParams(infCtx) refundedCoins := sdk.Coins{sdk.NewCoin(params.EvmDenom, sdk.NewIntFromBigInt(remaining))} // refund to sender from the fee collector module account, which is the escrow account in charge of collecting tx fees - if err := k.bankKeeper.SendCoinsFromModuleToAccount(k.ctx, authtypes.FeeCollectorName, msg.From().Bytes(), refundedCoins); err != nil { - return 0, 0, sdkerrors.Wrapf(sdkerrors.ErrInsufficientFunds, "fee collector account failed to refund fees: %s", err.Error()) + if err := k.bankKeeper.SendCoinsFromModuleToAccount(infCtx, authtypes.FeeCollectorName, msg.From().Bytes(), refundedCoins); err != nil { + return sdkerrors.Wrapf(sdkerrors.ErrInsufficientFunds, "fee collector account failed to refund fees: %s", err.Error()) } default: // no refund, consume gas and update the tx gas meter @@ -270,5 +280,5 @@ func (k *Keeper) refundGas(msg core.Message, leftoverGas uint64) (consumed, left gasMeter.ConsumeGas(gasConsumed, "update gas consumption after refund") k.WithContext(k.ctx.WithGasMeter(gasMeter)) - return gasConsumed, leftoverGas, nil + return nil } diff --git a/x/evm/module.go b/x/evm/module.go index b15dac3f..af316b7f 100644 --- a/x/evm/module.go +++ b/x/evm/module.go @@ -88,16 +88,14 @@ type AppModule struct { AppModuleBasic keeper *keeper.Keeper ak types.AccountKeeper - bk types.BankKeeper } // NewAppModule creates a new AppModule object -func NewAppModule(k *keeper.Keeper, ak types.AccountKeeper, bk types.BankKeeper) AppModule { +func NewAppModule(k *keeper.Keeper, ak types.AccountKeeper) AppModule { return AppModule{ AppModuleBasic: AppModuleBasic{}, keeper: k, ak: ak, - bk: bk, } } @@ -151,7 +149,7 @@ func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONMarshaler, data j var genesisState types.GenesisState cdc.MustUnmarshalJSON(data, &genesisState) - InitGenesis(ctx, am.keeper, am.ak, am.bk, genesisState) + InitGenesis(ctx, am.keeper, am.ak, genesisState) return []abci.ValidatorUpdate{} } diff --git a/x/evm/types/journal.go b/x/evm/types/journal.go deleted file mode 100644 index 5952c841..00000000 --- a/x/evm/types/journal.go +++ /dev/null @@ -1,348 +0,0 @@ -package types - -import ( - sdk "github.com/cosmos/cosmos-sdk/types" - - ethcmn "github.com/ethereum/go-ethereum/common" -) - -var ripemd = ethcmn.HexToAddress("0000000000000000000000000000000000000003") - -// journalEntry is a modification entry in the state change journal that can be -// reverted on demand. -type journalEntry interface { - // revert undoes the changes introduced by this journal entry. - revert(*CommitStateDB) - - // dirtied returns the Ethereum address modified by this journal entry. - dirtied() *ethcmn.Address -} - -type revision struct { - id int - journalIndex int -} - -// journal contains the list of state modifications applied since the last state -// commit. These are tracked to be able to be reverted in case of an execution -// exception or revertal request. -type journal struct { - entries []journalEntry // Current changes tracked by the journal - dirties []dirty // Dirty accounts and the number of changes - addressToJournalIndex map[ethcmn.Address]int // map from address to the index of the dirties slice -} - -// dirty represents a single key value pair of the journal dirties, where the -// key correspons to the account address and the value to the number of -// changes for that account. -type dirty struct { - address ethcmn.Address - changes int -} - -// newJournal create a new initialized journal. -func newJournal() *journal { - return &journal{ - dirties: []dirty{}, - addressToJournalIndex: make(map[ethcmn.Address]int), - } -} - -// append inserts a new modification entry to the end of the change journal. -func (j *journal) append(entry journalEntry) { - j.entries = append(j.entries, entry) - if addr := entry.dirtied(); addr != nil { - j.addDirty(*addr) - } -} - -// revert undoes a batch of journalled modifications along with any reverted -// dirty handling too. -func (j *journal) revert(statedb *CommitStateDB, snapshot int) { - for i := len(j.entries) - 1; i >= snapshot; i-- { - // Undo the changes made by the operation - j.entries[i].revert(statedb) - - // Drop any dirty tracking induced by the change - if addr := j.entries[i].dirtied(); addr != nil { - j.substractDirty(*addr) - if j.getDirty(*addr) == 0 { - j.deleteDirty(*addr) - } - } - } - j.entries = j.entries[:snapshot] -} - -// dirty explicitly sets an address to dirty, even if the change entries would -// otherwise suggest it as clean. This method is an ugly hack to handle the RIPEMD -// precompile consensus exception. -func (j *journal) dirty(addr ethcmn.Address) { - j.addDirty(addr) -} - -// length returns the current number of entries in the journal. -func (j *journal) length() int { - return len(j.entries) -} - -// getDirty returns the dirty count for a given address. If the address is not -// found it returns 0. -func (j *journal) getDirty(addr ethcmn.Address) int { - idx, found := j.addressToJournalIndex[addr] - if !found { - return 0 - } - - return j.dirties[idx].changes -} - -// addDirty adds 1 to the dirty count of an address. If the dirty entry is not -// found it creates it. -func (j *journal) addDirty(addr ethcmn.Address) { - idx, found := j.addressToJournalIndex[addr] - if !found { - j.dirties = append(j.dirties, dirty{address: addr, changes: 0}) - idx = len(j.dirties) - 1 - j.addressToJournalIndex[addr] = idx - } - - j.dirties[idx].changes++ -} - -// substractDirty subtracts 1 to the dirty count of an address. It performs a -// no-op if the address is not found. -func (j *journal) substractDirty(addr ethcmn.Address) { - idx, found := j.addressToJournalIndex[addr] - if !found { - return - } - - if j.dirties[idx].changes == 0 { - return - } - j.dirties[idx].changes-- -} - -// deleteDirty deletes a dirty entry from the jounal's dirties slice. If the -// entry is not found it performs a no-op. -func (j *journal) deleteDirty(addr ethcmn.Address) { - idx, found := j.addressToJournalIndex[addr] - if !found { - return - } - - j.dirties = append(j.dirties[:idx], j.dirties[idx+1:]...) - delete(j.addressToJournalIndex, addr) -} - -type ( - // Changes to the account trie. - createObjectChange struct { - account *ethcmn.Address - } - - resetObjectChange struct { - prev *stateObject - } - - suicideChange struct { - account *ethcmn.Address - prev bool // whether account had already suicided - prevBalance sdk.Int - } - - // Changes to individual accounts. - balanceChange struct { - account *ethcmn.Address - prev sdk.Int - } - - nonceChange struct { - account *ethcmn.Address - prev uint64 - } - - storageChange struct { - account *ethcmn.Address - key, prevValue ethcmn.Hash - } - - codeChange struct { - account *ethcmn.Address - prevCode, prevHash []byte - } - - // Changes to other state values. - refundChange struct { - prev uint64 - } - - addLogChange struct { - txhash ethcmn.Hash - } - - addPreimageChange struct { - hash ethcmn.Hash - } - - touchChange struct { - account *ethcmn.Address - // prev bool - // prevDirty bool - } -) - -func (ch createObjectChange) revert(s *CommitStateDB) { - delete(s.stateObjectsDirty, *ch.account) - - idx, exists := s.addressToObjectIndex[*ch.account] - if !exists { - // perform no-op - return - } - - // remove from the slice - delete(s.addressToObjectIndex, *ch.account) - - // if the slice contains one element, delete it - if len(s.stateObjects) == 1 { - s.stateObjects = []stateEntry{} - return - } - - // move the elements one position left on the array - for i := idx + 1; i < len(s.stateObjects); i++ { - s.stateObjects[i-1] = s.stateObjects[i] - // the new index is i - 1 - s.addressToObjectIndex[s.stateObjects[i].address] = i - 1 - } - - // finally, delete the last element of the slice to account for the removed object - s.stateObjects = s.stateObjects[:len(s.stateObjects)-1] -} - -func (ch createObjectChange) dirtied() *ethcmn.Address { - return ch.account -} - -func (ch resetObjectChange) revert(s *CommitStateDB) { - s.setStateObject(ch.prev) -} - -func (ch resetObjectChange) dirtied() *ethcmn.Address { - return nil -} - -func (ch suicideChange) revert(s *CommitStateDB) { - so := s.getStateObject(*ch.account) - if so != nil { - so.suicided = ch.prev - so.setBalance(ch.prevBalance) - } -} - -func (ch suicideChange) dirtied() *ethcmn.Address { - return ch.account -} - -func (ch touchChange) revert(s *CommitStateDB) { -} - -func (ch touchChange) dirtied() *ethcmn.Address { - return ch.account -} - -func (ch balanceChange) revert(s *CommitStateDB) { - s.getStateObject(*ch.account).setBalance(ch.prev) -} - -func (ch balanceChange) dirtied() *ethcmn.Address { - return ch.account -} - -func (ch nonceChange) revert(s *CommitStateDB) { - s.getStateObject(*ch.account).setNonce(ch.prev) -} - -func (ch nonceChange) dirtied() *ethcmn.Address { - return ch.account -} - -func (ch codeChange) revert(s *CommitStateDB) { - s.getStateObject(*ch.account).setCode(ethcmn.BytesToHash(ch.prevHash), ch.prevCode) -} - -func (ch codeChange) dirtied() *ethcmn.Address { - return ch.account -} - -func (ch storageChange) revert(s *CommitStateDB) { - s.getStateObject(*ch.account).setState(ch.key, ch.prevValue) -} - -func (ch storageChange) dirtied() *ethcmn.Address { - return ch.account -} - -func (ch refundChange) revert(s *CommitStateDB) { - s.refund = ch.prev -} - -func (ch refundChange) dirtied() *ethcmn.Address { - return nil -} - -func (ch addLogChange) revert(s *CommitStateDB) { - logs, err := s.GetLogs(ch.txhash) - if err != nil { - // panic on unmarshal error - panic(err) - } - - // delete logs if entry is empty or has only one item - if len(logs) <= 1 { - s.DeleteLogs(ch.txhash) - } else if err := s.SetLogs(ch.txhash, logs[:len(logs)-1]); err != nil { - // panic on marshal error - panic(err) - } - - s.logSize-- -} - -func (ch addLogChange) dirtied() *ethcmn.Address { - return nil -} - -func (ch addPreimageChange) revert(s *CommitStateDB) { - idx, exists := s.hashToPreimageIndex[ch.hash] - if !exists { - // perform no-op - return - } - - // remove from the slice - delete(s.hashToPreimageIndex, ch.hash) - - // if the slice contains one element, delete it - if len(s.preimages) == 1 { - s.preimages = []preimageEntry{} - return - } - - // move the elements one position left on the array - for i := idx + 1; i < len(s.preimages); i++ { - s.preimages[i-1] = s.preimages[i] - // the new index is i - 1 - s.hashToPreimageIndex[s.preimages[i].hash] = i - 1 - } - - // finally, delete the last element - - s.preimages = s.preimages[:len(s.preimages)-1] -} - -func (ch addPreimageChange) dirtied() *ethcmn.Address { - return nil -} diff --git a/x/evm/types/journal_test.go b/x/evm/types/journal_test.go deleted file mode 100644 index 87d0961c..00000000 --- a/x/evm/types/journal_test.go +++ /dev/null @@ -1,354 +0,0 @@ -package types - -import ( - "fmt" - "os" - "testing" - - "github.com/cosmos/ethermint/crypto/ethsecp256k1" - enccodec "github.com/cosmos/ethermint/encoding/codec" - ethermint "github.com/cosmos/ethermint/types" - - "github.com/stretchr/testify/suite" - - tmlog "github.com/tendermint/tendermint/libs/log" - tmproto "github.com/tendermint/tendermint/proto/tendermint/types" - tmdb "github.com/tendermint/tm-db" - - "github.com/cosmos/cosmos-sdk/store" - sdk "github.com/cosmos/cosmos-sdk/types" - authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - paramkeeper "github.com/cosmos/cosmos-sdk/x/params/keeper" - paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" - - 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/cosmos-sdk/codec" - codectypes "github.com/cosmos/cosmos-sdk/codec/types" -) - -func newTestCodec() (codec.BinaryMarshaler, *codec.LegacyAmino) { - interfaceRegistry := codectypes.NewInterfaceRegistry() - cdc := codec.NewProtoCodec(interfaceRegistry) - amino := codec.NewLegacyAmino() - - sdk.RegisterLegacyAminoCodec(amino) - - enccodec.RegisterInterfaces(interfaceRegistry) - - return cdc, amino -} - -type JournalTestSuite struct { - suite.Suite - - address ethcmn.Address - journal *journal - ctx sdk.Context - stateDB *CommitStateDB -} - -func (suite *JournalTestSuite) SetupTest() { - suite.setup() - - privkey, err := ethsecp256k1.GenerateKey() - suite.Require().NoError(err) - - suite.address = ethcmn.BytesToAddress(privkey.PubKey().Address().Bytes()) - suite.journal = newJournal() - - balance := ethermint.NewPhotonCoin(sdk.NewInt(100)) - acc := ðermint.EthAccount{ - BaseAccount: authtypes.NewBaseAccount(sdk.AccAddress(suite.address.Bytes()), nil, 0, 0), - CodeHash: ethcrypto.Keccak256(nil), - } - - suite.stateDB.accountKeeper.SetAccount(suite.ctx, acc) - suite.stateDB.bankKeeper.SetBalance(suite.ctx, sdk.AccAddress(suite.address.Bytes()), balance) - suite.stateDB.SetLogs(ethcmn.BytesToHash([]byte("txhash")), []*ethtypes.Log{ - { - Address: suite.address, - Topics: []ethcmn.Hash{ethcmn.BytesToHash([]byte("topic_0"))}, - Data: []byte("data_0"), - BlockNumber: 1, - TxHash: ethcmn.BytesToHash([]byte("tx_hash")), - TxIndex: 1, - BlockHash: ethcmn.BytesToHash([]byte("block_hash")), - Index: 1, - Removed: false, - }, - { - Address: suite.address, - Topics: []ethcmn.Hash{ethcmn.BytesToHash([]byte("topic_1"))}, - Data: []byte("data_1"), - BlockNumber: 10, - TxHash: ethcmn.BytesToHash([]byte("tx_hash")), - TxIndex: 0, - BlockHash: ethcmn.BytesToHash([]byte("block_hash")), - Index: 0, - Removed: false, - }, - }) -} - -// setup performs a manual setup of the GoLevelDB and mounts the required IAVL stores. We use the manual -// setup here instead of the Ethermint app test setup because the journal methods are private and using -// the latter would result in a cycle dependency. We also want to avoid declaring the journal methods public -// to maintain consistency with the Geth implementation. -func (suite *JournalTestSuite) setup() { - authKey := sdk.NewKVStoreKey(authtypes.StoreKey) - paramsKey := sdk.NewKVStoreKey(paramtypes.StoreKey) - paramsTKey := sdk.NewTransientStoreKey(paramtypes.TStoreKey) - tKey := sdk.NewTransientStoreKey(TransientKey) - bankKey := sdk.NewKVStoreKey(banktypes.StoreKey) - storeKey := sdk.NewKVStoreKey(StoreKey) - - db, err := tmdb.NewDB("state", tmdb.GoLevelDBBackend, "temp") - suite.Require().NoError(err) - - defer func() { - os.RemoveAll("temp") - }() - - cms := store.NewCommitMultiStore(db) - cms.MountStoreWithDB(authKey, sdk.StoreTypeIAVL, db) - cms.MountStoreWithDB(bankKey, sdk.StoreTypeIAVL, db) - cms.MountStoreWithDB(paramsKey, sdk.StoreTypeIAVL, db) - cms.MountStoreWithDB(storeKey, sdk.StoreTypeIAVL, db) - cms.MountStoreWithDB(paramsTKey, sdk.StoreTypeTransient, db) - cms.MountStoreWithDB(tKey, sdk.StoreTypeTransient, db) - - err = cms.LoadLatestVersion() - suite.Require().NoError(err) - - cdc, amino := newTestCodec() - - paramsKeeper := paramkeeper.NewKeeper(cdc, amino, paramsKey, paramsTKey) - - authSubspace := paramsKeeper.Subspace(authtypes.ModuleName) - bankSubspace := paramsKeeper.Subspace(banktypes.ModuleName) - evmSubspace := paramsKeeper.Subspace(ModuleName).WithKeyTable(ParamKeyTable()) - - ak := authkeeper.NewAccountKeeper(cdc, authKey, authSubspace, ethermint.ProtoAccount, nil) - bk := bankkeeper.NewBaseKeeper(cdc, bankKey, ak, bankSubspace, nil) - suite.ctx = sdk.NewContext(cms, tmproto.Header{ChainID: "ethermint-8"}, false, tmlog.NewNopLogger()) - suite.stateDB = NewCommitStateDB(suite.ctx, storeKey, tKey, evmSubspace, ak, bk).WithContext(suite.ctx) - suite.stateDB.SetParams(DefaultParams()) -} - -func TestJournalTestSuite(t *testing.T) { - suite.Run(t, new(JournalTestSuite)) -} - -func (suite *JournalTestSuite) TestJournal_append_revert() { - testCases := []struct { - name string - entry journalEntry - }{ - { - "createObjectChange", - createObjectChange{ - account: &suite.address, - }, - }, - { - "resetObjectChange", - resetObjectChange{ - prev: &stateObject{ - address: suite.address, - balance: sdk.OneInt(), - }, - }, - }, - { - "suicideChange", - suicideChange{ - account: &suite.address, - prev: false, - prevBalance: sdk.OneInt(), - }, - }, - { - "balanceChange", - balanceChange{ - account: &suite.address, - prev: sdk.OneInt(), - }, - }, - { - "nonceChange", - nonceChange{ - account: &suite.address, - prev: 1, - }, - }, - { - "storageChange", - storageChange{ - account: &suite.address, - key: ethcmn.BytesToHash([]byte("key")), - prevValue: ethcmn.BytesToHash([]byte("value")), - }, - }, - { - "codeChange", - codeChange{ - account: &suite.address, - prevCode: []byte("code"), - prevHash: []byte("hash"), - }, - }, - { - "touchChange", - touchChange{ - account: &suite.address, - }, - }, - { - "refundChange", - refundChange{ - prev: 1, - }, - }, - { - "addPreimageChange", - addPreimageChange{ - hash: ethcmn.BytesToHash([]byte("hash")), - }, - }, - { - "addLogChange", - addLogChange{ - txhash: ethcmn.BytesToHash([]byte("hash")), - }, - }, - { - "addLogChange - 2 logs", - addLogChange{ - txhash: ethcmn.BytesToHash([]byte("txhash")), - }, - }, - { - "accessListAddAccountChange", - accessListAddAccountChange{ - address: &suite.address, - }, - }, - } - var dirtyCount int - for i, tc := range testCases { - suite.journal.append(tc.entry) - suite.Require().Equal(suite.journal.length(), i+1, tc.name) - if tc.entry.dirtied() != nil { - dirtyCount++ - - suite.Require().Equal(dirtyCount, suite.journal.getDirty(suite.address), tc.name) - } - } - - // revert to the initial journal state - suite.journal.revert(suite.stateDB, 0) - - // verify the dirty entry has been deleted - idx, ok := suite.journal.addressToJournalIndex[suite.address] - suite.Require().False(ok) - suite.Require().Zero(idx) -} - -func (suite *JournalTestSuite) TestJournal_preimage_revert() { - suite.stateDB.preimages = []preimageEntry{ - { - hash: ethcmn.BytesToHash([]byte("hash")), - preimage: []byte("preimage0"), - }, - { - hash: ethcmn.BytesToHash([]byte("hash1")), - preimage: []byte("preimage1"), - }, - { - hash: ethcmn.BytesToHash([]byte("hash2")), - preimage: []byte("preimage2"), - }, - } - - for i, preimage := range suite.stateDB.preimages { - suite.stateDB.hashToPreimageIndex[preimage.hash] = i - } - - change := addPreimageChange{ - hash: ethcmn.BytesToHash([]byte("hash")), - } - - // delete first entry - change.revert(suite.stateDB) - suite.Require().Len(suite.stateDB.preimages, 2) - suite.Require().Equal(len(suite.stateDB.preimages), len(suite.stateDB.hashToPreimageIndex)) - - for i, entry := range suite.stateDB.preimages { - suite.Require().Equal(fmt.Sprintf("preimage%d", i+1), string(entry.preimage), entry.hash.String()) - idx, found := suite.stateDB.hashToPreimageIndex[entry.hash] - suite.Require().True(found) - suite.Require().Equal(i, idx) - } -} - -func (suite *JournalTestSuite) TestJournal_createObjectChange_revert() { - addr := ethcmn.BytesToAddress([]byte("addr")) - - suite.stateDB.stateObjects = []stateEntry{ - { - address: addr, - stateObject: &stateObject{ - address: addr, - }, - }, - { - address: ethcmn.BytesToAddress([]byte("addr1")), - stateObject: &stateObject{ - address: ethcmn.BytesToAddress([]byte("addr1")), - }, - }, - { - address: ethcmn.BytesToAddress([]byte("addr2")), - stateObject: &stateObject{ - address: ethcmn.BytesToAddress([]byte("addr2")), - }, - }, - } - - for i, so := range suite.stateDB.stateObjects { - suite.stateDB.addressToObjectIndex[so.address] = i - } - - change := createObjectChange{ - account: &addr, - } - - // delete first entry - change.revert(suite.stateDB) - suite.Require().Len(suite.stateDB.stateObjects, 2) - suite.Require().Equal(len(suite.stateDB.stateObjects), len(suite.stateDB.addressToObjectIndex)) - - for i, entry := range suite.stateDB.stateObjects { - suite.Require().Equal(ethcmn.BytesToAddress([]byte(fmt.Sprintf("addr%d", i+1))).String(), entry.address.String()) - idx, found := suite.stateDB.addressToObjectIndex[entry.address] - suite.Require().True(found) - suite.Require().Equal(i, idx) - } -} - -func (suite *JournalTestSuite) TestJournal_dirty() { - // dirty entry hasn't been set - idx, ok := suite.journal.addressToJournalIndex[suite.address] - suite.Require().False(ok) - suite.Require().Zero(idx) - - // update dirty count - suite.journal.dirty(suite.address) - suite.Require().Equal(1, suite.journal.getDirty(suite.address)) -} diff --git a/x/evm/types/logs.go b/x/evm/types/logs.go index cf89817e..254a5bb3 100644 --- a/x/evm/types/logs.go +++ b/x/evm/types/logs.go @@ -89,7 +89,7 @@ func (log *Log) ToEthereum() *ethtypes.Log { } func NewLogsFromEth(ethlogs []*ethtypes.Log) []*Log { - var logs []*Log + var logs []*Log // nolint: prealloc for _, ethlog := range ethlogs { logs = append(logs, NewLogFromEth(ethlog)) } diff --git a/x/evm/types/msg.go b/x/evm/types/msg.go index e82b7df5..4efbff23 100644 --- a/x/evm/types/msg.go +++ b/x/evm/types/msg.go @@ -2,7 +2,6 @@ package types import ( "fmt" - "io" "math/big" "github.com/cosmos/cosmos-sdk/crypto/keyring" @@ -14,7 +13,6 @@ import ( ethcmn "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core" ethtypes "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/rlp" ) var ( @@ -191,48 +189,6 @@ func (msg MsgEthereumTx) GetSignBytes() []byte { panic("must use 'RLPSignBytes' with a chain ID to get the valid bytes to sign") } -// RLPSignBytes returns the RLP hash of an Ethereum transaction message with a -// given chainID used for signing. -func (msg MsgEthereumTx) RLPSignBytes(chainID *big.Int) ethcmn.Hash { - if msg.Data.ChainID != nil { - chainID = new(big.Int).SetBytes(msg.Data.ChainID) - } - - var accessList *ethtypes.AccessList - if msg.Data.Accesses != nil { - accessList = msg.Data.Accesses.ToEthAccessList() - } - - return rlpHash([]interface{}{ - chainID, - msg.Data.Nonce, - new(big.Int).SetBytes(msg.Data.GasPrice), - msg.Data.GasLimit, - msg.To(), - new(big.Int).SetBytes(msg.Data.Amount), - new(big.Int).SetBytes(msg.Data.Input), - accessList, - }) -} - -// EncodeRLP implements the rlp.Encoder interface. -func (msg *MsgEthereumTx) EncodeRLP(w io.Writer) error { - tx := msg.AsTransaction() - return tx.EncodeRLP(w) -} - -// DecodeRLP implements the rlp.Decoder interface. -func (msg *MsgEthereumTx) DecodeRLP(stream *rlp.Stream) error { - tx := ðtypes.Transaction{} - if err := tx.DecodeRLP(stream); err != nil { - return err - } - - msg.FromEthereumTx(tx) - - return nil -} - // Sign calculates a secp256k1 ECDSA signature and signs the transaction. It // takes a keyring signer and the chainID to sign an Ethereum transaction according to // EIP155 standard. diff --git a/x/evm/types/msg_test.go b/x/evm/types/msg_test.go index 795979a0..3fc9a023 100644 --- a/x/evm/types/msg_test.go +++ b/x/evm/types/msg_test.go @@ -1,7 +1,6 @@ package types import ( - "bytes" "math/big" "testing" @@ -15,7 +14,6 @@ import ( ethcmn "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/rlp" ) type MsgsTestSuite struct { @@ -91,23 +89,6 @@ func (suite *MsgsTestSuite) TestMsgEthereumTx_ValidateBasic() { } } -func (suite *MsgsTestSuite) TestMsgEthereumTx_EncodeRLP() { - expMsg := NewMsgEthereumTx(suite.chainID, 0, &suite.to, nil, 100000, nil, []byte("test"), nil) - - raw, err := rlp.EncodeToBytes(&expMsg) - suite.Require().NoError(err) - - msg := &MsgEthereumTx{} - err = rlp.Decode(bytes.NewReader(raw), &msg) - suite.Require().NoError(err) - suite.Require().Equal(expMsg.Data, msg.Data) -} - -func (suite *MsgsTestSuite) TestMsgEthereumTx_RLPSignBytes() { - msg := NewMsgEthereumTx(suite.chainID, 0, &suite.to, nil, 100000, nil, []byte("test"), nil) - suite.NotPanics(func() { _ = msg.RLPSignBytes(suite.chainID) }) -} - func (suite *MsgsTestSuite) TestMsgEthereumTx_Sign() { msg := NewMsgEthereumTx(suite.chainID, 0, &suite.to, nil, 100000, nil, []byte("test"), nil) diff --git a/x/evm/types/query.pb.go b/x/evm/types/query.pb.go index 69832304..2ee6a2fb 100644 --- a/x/evm/types/query.pb.go +++ b/x/evm/types/query.pb.go @@ -73,8 +73,8 @@ var xxx_messageInfo_QueryAccountRequest proto.InternalMessageInfo type QueryAccountResponse struct { // balance is the balance of the EVM denomination. Balance string `protobuf:"bytes,1,opt,name=balance,proto3" json:"balance,omitempty"` - // code_hash is the code bytes from the EOA. - CodeHash []byte `protobuf:"bytes,2,opt,name=code_hash,json=codeHash,proto3" json:"code_hash,omitempty"` + // code hash is the hex-formatted code bytes from the EOA. + CodeHash string `protobuf:"bytes,2,opt,name=code_hash,json=codeHash,proto3" json:"code_hash,omitempty"` // nonce is the account's sequence number. Nonce uint64 `protobuf:"varint,3,opt,name=nonce,proto3" json:"nonce,omitempty"` } @@ -119,11 +119,11 @@ func (m *QueryAccountResponse) GetBalance() string { return "" } -func (m *QueryAccountResponse) GetCodeHash() []byte { +func (m *QueryAccountResponse) GetCodeHash() string { if m != nil { return m.CodeHash } - return nil + return "" } func (m *QueryAccountResponse) GetNonce() uint64 { @@ -972,72 +972,72 @@ func init() { } var fileDescriptor_8bbc79ec2b6c5cb2 = []byte{ - // 1028 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x96, 0xcf, 0x6f, 0x1b, 0x45, - 0x14, 0xc7, 0xbd, 0x8d, 0x63, 0x27, 0x2f, 0x09, 0x2a, 0x83, 0x29, 0x61, 0x8b, 0x1c, 0x6b, 0x51, - 0x63, 0xe7, 0x47, 0x77, 0x6b, 0x23, 0xf1, 0x4b, 0x48, 0x28, 0xa9, 0x14, 0x2a, 0x51, 0xa1, 0xe2, - 0xf4, 0xc4, 0xc5, 0x1a, 0xaf, 0x47, 0x6b, 0x2b, 0xeb, 0x1d, 0xd7, 0xb3, 0xb6, 0x12, 0x45, 0xb9, - 0x70, 0x40, 0x20, 0x71, 0x00, 0x71, 0xa0, 0x42, 0x42, 0xea, 0x95, 0x1b, 0x7f, 0x46, 0x8f, 0x95, - 0xb8, 0x70, 0x42, 0x28, 0xe1, 0xc0, 0x9f, 0x81, 0x66, 0xe6, 0xad, 0xbd, 0x9b, 0x78, 0xb3, 0x0e, - 0xb7, 0x9d, 0xf1, 0xfb, 0xf1, 0x79, 0xef, 0xcd, 0x7c, 0xc7, 0x60, 0xb1, 0xb0, 0xcb, 0x86, 0xfd, - 0x5e, 0x10, 0x3a, 0x6c, 0xdc, 0x77, 0xc6, 0x75, 0xea, 0x0f, 0xba, 0xb4, 0xee, 0x3c, 0x1b, 0xb1, - 0xe1, 0x89, 0x3d, 0x18, 0xf2, 0x90, 0x93, 0x3b, 0x13, 0x1b, 0x9b, 0x8d, 0xfb, 0x76, 0x64, 0x63, - 0x96, 0x3c, 0xee, 0x71, 0x65, 0xe2, 0xc8, 0x2f, 0x6d, 0x6d, 0x6e, 0xbb, 0x5c, 0xf4, 0xb9, 0x70, - 0xda, 0x54, 0x30, 0x1d, 0xc6, 0x19, 0xd7, 0xdb, 0x2c, 0xa4, 0x75, 0x67, 0x40, 0xbd, 0x5e, 0x40, - 0xc3, 0x1e, 0x0f, 0xd0, 0xf6, 0x1d, 0x8f, 0x73, 0xcf, 0x67, 0x0e, 0x1d, 0xf4, 0x1c, 0x1a, 0x04, - 0x3c, 0x54, 0x3f, 0x0a, 0xfc, 0xb5, 0x92, 0xc2, 0x26, 0x21, 0x94, 0x85, 0xf5, 0x11, 0xbc, 0xf1, - 0xa5, 0xcc, 0xb0, 0xe7, 0xba, 0x7c, 0x14, 0x84, 0x4d, 0xf6, 0x6c, 0xc4, 0x44, 0x48, 0xd6, 0xa1, - 0x48, 0x3b, 0x9d, 0x21, 0x13, 0x62, 0xdd, 0xa8, 0x18, 0xb5, 0xe5, 0x66, 0xb4, 0xfc, 0x78, 0xe9, - 0xdb, 0x17, 0x1b, 0xb9, 0x7f, 0x5f, 0x6c, 0xe4, 0x2c, 0x17, 0x4a, 0x49, 0x57, 0x31, 0xe0, 0x81, - 0x60, 0xd2, 0xb7, 0x4d, 0x7d, 0x1a, 0xb8, 0x2c, 0xf2, 0xc5, 0x25, 0xb9, 0x0b, 0xcb, 0x2e, 0xef, - 0xb0, 0x56, 0x97, 0x8a, 0xee, 0xfa, 0xad, 0x8a, 0x51, 0x5b, 0x6d, 0x2e, 0xc9, 0x8d, 0x47, 0x54, - 0x74, 0x49, 0x09, 0x16, 0x03, 0x2e, 0x9d, 0x16, 0x2a, 0x46, 0x2d, 0xdf, 0xd4, 0x0b, 0xeb, 0x53, - 0x78, 0x5b, 0x25, 0x79, 0xa8, 0x5a, 0xf2, 0x3f, 0x28, 0xbf, 0x31, 0xc0, 0x9c, 0x15, 0x01, 0x61, - 0xef, 0xc1, 0x6b, 0xba, 0xdb, 0xad, 0x64, 0xa4, 0x35, 0xbd, 0xbb, 0xa7, 0x37, 0x89, 0x09, 0x4b, - 0x42, 0x26, 0x95, 0x7c, 0xb7, 0x14, 0xdf, 0x64, 0x2d, 0x43, 0x50, 0x1d, 0xb5, 0x15, 0x8c, 0xfa, - 0x6d, 0x36, 0xc4, 0x0a, 0xd6, 0x70, 0xf7, 0x0b, 0xb5, 0x39, 0xe9, 0xf4, 0xbe, 0x6e, 0xc6, 0x4d, - 0x6a, 0x78, 0x80, 0x9d, 0x9e, 0xb8, 0x66, 0x75, 0xda, 0xfa, 0x1c, 0x93, 0x1d, 0x86, 0x7c, 0x48, - 0xbd, 0xec, 0x64, 0xe4, 0x36, 0x2c, 0x1c, 0xb1, 0x13, 0x55, 0xdb, 0x72, 0x53, 0x7e, 0xc6, 0xd2, - 0xef, 0x62, 0xfa, 0x49, 0x30, 0x4c, 0x5f, 0x82, 0xc5, 0x31, 0xf5, 0x47, 0x51, 0x72, 0xbd, 0xb0, - 0xde, 0x87, 0xdb, 0xd8, 0xef, 0xce, 0x8d, 0x8a, 0xac, 0xc2, 0xeb, 0x31, 0x3f, 0x4c, 0x41, 0x20, - 0x2f, 0x0f, 0x88, 0xf2, 0x5a, 0x6d, 0xaa, 0x6f, 0xab, 0x01, 0x44, 0x19, 0x3e, 0x3d, 0x7e, 0xcc, - 0x3d, 0x11, 0xa5, 0x20, 0x90, 0x57, 0xc7, 0x4a, 0xc7, 0x57, 0xdf, 0xb1, 0xe0, 0x07, 0xd8, 0x8f, - 0xc8, 0x07, 0xc3, 0x3b, 0x90, 0xf7, 0xb9, 0x27, 0xa1, 0x16, 0x6a, 0x2b, 0x8d, 0xbb, 0xf6, 0xec, - 0x6b, 0x6a, 0x3f, 0xe6, 0x5e, 0x53, 0x19, 0x5a, 0x67, 0xf0, 0xa6, 0x9e, 0x84, 0xcf, 0xdd, 0xa3, - 0x8c, 0xf4, 0xe4, 0x00, 0x60, 0x7a, 0x5f, 0x55, 0x6b, 0x57, 0x1a, 0x9b, 0xb6, 0x3e, 0x58, 0xb6, - 0xbc, 0xdc, 0xb6, 0xd6, 0x08, 0xbc, 0xdc, 0xf6, 0x93, 0xe9, 0xa4, 0x9a, 0x31, 0xcf, 0x58, 0x19, - 0xbf, 0x19, 0x70, 0xe7, 0x72, 0x7e, 0x2c, 0xe5, 0x00, 0x8a, 0xe1, 0x71, 0x2b, 0x56, 0x4d, 0x35, - 0xad, 0x9a, 0xa7, 0x43, 0x1a, 0x08, 0xea, 0xca, 0xd0, 0x32, 0xc2, 0x7e, 0xfe, 0xe5, 0x5f, 0x1b, - 0xb9, 0x66, 0x21, 0x54, 0xad, 0x21, 0x9f, 0xcd, 0x80, 0xae, 0x66, 0x42, 0x6b, 0x88, 0x38, 0xb5, - 0xb5, 0x1e, 0x47, 0xdd, 0xf7, 0x39, 0xef, 0x63, 0x6d, 0x96, 0x03, 0x6f, 0x5d, 0xf9, 0x65, 0x7a, - 0xa4, 0xda, 0x72, 0x03, 0x07, 0xae, 0x17, 0x56, 0x09, 0x27, 0xfe, 0x84, 0x0e, 0x69, 0x3f, 0x6a, - 0xb9, 0x75, 0x88, 0x33, 0x8d, 0x76, 0x31, 0xc4, 0x27, 0x50, 0x18, 0xa8, 0x1d, 0x15, 0x63, 0xa5, - 0x51, 0x4e, 0xeb, 0x83, 0xf6, 0x8b, 0xca, 0xd7, 0x3e, 0xd6, 0x23, 0xa4, 0x3e, 0x94, 0x42, 0xea, - 0x3e, 0xa4, 0xbe, 0x9f, 0x7d, 0x77, 0x4a, 0xb0, 0xd8, 0x0b, 0x06, 0xa3, 0x10, 0x25, 0x4d, 0x2f, - 0xac, 0xfb, 0x58, 0x65, 0x3c, 0xd2, 0xf4, 0x54, 0x77, 0x68, 0x48, 0xa3, 0x53, 0x2d, 0xbf, 0x1b, - 0xcf, 0x57, 0x61, 0x51, 0xd9, 0x93, 0x9f, 0x0d, 0x28, 0xa2, 0x4c, 0x91, 0x9d, 0x34, 0xf8, 0x19, - 0xa2, 0x6d, 0xee, 0xce, 0x67, 0xac, 0x21, 0xac, 0xfa, 0xd7, 0x7f, 0xfc, 0xf3, 0xd3, 0xad, 0x1d, - 0xb2, 0xe5, 0xa4, 0x3c, 0x12, 0x28, 0x5f, 0xce, 0x29, 0xd6, 0x79, 0x46, 0x7e, 0x37, 0x60, 0x2d, - 0x21, 0xa3, 0xa4, 0x7e, 0x6d, 0xca, 0x59, 0xa2, 0x6d, 0x36, 0x6e, 0xe2, 0x82, 0xac, 0x1f, 0x2a, - 0xd6, 0x06, 0x79, 0x90, 0xc6, 0x1a, 0x69, 0xf8, 0x15, 0xe4, 0xe7, 0x06, 0x14, 0x51, 0x36, 0x33, - 0x9a, 0x99, 0xd4, 0xe5, 0x8c, 0x66, 0x5e, 0x52, 0x62, 0xab, 0xa1, 0x00, 0x77, 0xc9, 0x76, 0x1a, - 0x20, 0x0a, 0xb3, 0x88, 0xa1, 0xfd, 0x6a, 0x40, 0x11, 0x25, 0x35, 0x03, 0x2d, 0xa9, 0xe2, 0x19, - 0x68, 0x97, 0x54, 0xda, 0xfa, 0x40, 0xa1, 0xd5, 0x89, 0x93, 0x86, 0x26, 0xb4, 0xc3, 0x94, 0xcc, - 0x39, 0x3d, 0x62, 0x27, 0x67, 0xe4, 0x7b, 0x03, 0xf2, 0x52, 0x8c, 0x49, 0x2d, 0x63, 0x62, 0x13, - 0x9d, 0x37, 0xb7, 0xe6, 0xb0, 0x44, 0x2c, 0x47, 0x61, 0x6d, 0x91, 0x6a, 0xfa, 0x48, 0x3b, 0x89, - 0x76, 0xfd, 0x68, 0x40, 0x41, 0xcb, 0x37, 0xd9, 0xbe, 0x36, 0x4d, 0xe2, 0x5d, 0x30, 0x77, 0xe6, - 0xb2, 0x45, 0x28, 0x5b, 0x41, 0xd5, 0xc8, 0x66, 0x1a, 0x14, 0x4a, 0xac, 0x73, 0x2a, 0x05, 0x5e, - 0x8d, 0x70, 0x79, 0x22, 0xc5, 0xe4, 0xfe, 0xf5, 0x47, 0xe6, 0xd2, 0x93, 0x61, 0xda, 0xf3, 0x9a, - 0xcf, 0x7b, 0x61, 0xdb, 0xd2, 0x25, 0xc1, 0xf7, 0x8b, 0x01, 0x30, 0x55, 0x59, 0x32, 0x47, 0xc6, - 0xb8, 0x50, 0x9b, 0xce, 0xdc, 0xf6, 0x88, 0xb8, 0xa3, 0x10, 0xef, 0x91, 0x77, 0xaf, 0x47, 0x54, - 0xaa, 0x4e, 0xbe, 0x33, 0xa0, 0xa0, 0x35, 0x38, 0x63, 0xa0, 0x09, 0xd9, 0xcf, 0x18, 0x68, 0xf2, - 0x31, 0xb0, 0x36, 0x15, 0x50, 0x85, 0x94, 0xd3, 0x80, 0xb4, 0xec, 0xab, 0x46, 0x4d, 0x85, 0x3a, - 0xa3, 0x51, 0x57, 0xde, 0x86, 0x8c, 0x46, 0x5d, 0x7d, 0x01, 0xb2, 0x1b, 0x25, 0x94, 0x4f, 0xcb, - 0xa5, 0xbe, 0xbf, 0xbf, 0xf7, 0xf2, 0xbc, 0x6c, 0xbc, 0x3a, 0x2f, 0x1b, 0x7f, 0x9f, 0x97, 0x8d, - 0x1f, 0x2e, 0xca, 0xb9, 0x57, 0x17, 0xe5, 0xdc, 0x9f, 0x17, 0xe5, 0xdc, 0x57, 0x55, 0xaf, 0x17, - 0x76, 0x47, 0x6d, 0xdb, 0xe5, 0x7d, 0x94, 0xc0, 0x58, 0xbc, 0x63, 0x15, 0x31, 0x3c, 0x19, 0x30, - 0xd1, 0x2e, 0xa8, 0x7f, 0xfb, 0xef, 0xfd, 0x17, 0x00, 0x00, 0xff, 0xff, 0xc1, 0xa4, 0x09, 0x94, - 0xad, 0x0c, 0x00, 0x00, + // 1026 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x9c, 0x96, 0xcf, 0x6f, 0xe3, 0x44, + 0x14, 0xc7, 0xe3, 0x36, 0x4d, 0xda, 0xd7, 0x16, 0x2d, 0x43, 0x58, 0x8a, 0x17, 0xa5, 0x95, 0xd1, + 0x36, 0xe9, 0x8f, 0xb5, 0x37, 0x41, 0xe2, 0x97, 0x90, 0x50, 0xbb, 0x52, 0x59, 0x89, 0x15, 0x5a, + 0xd2, 0x3d, 0x71, 0x89, 0x26, 0xce, 0xc8, 0x89, 0xea, 0x78, 0xb2, 0x19, 0x27, 0x6a, 0x55, 0xf5, + 0xc2, 0x01, 0x81, 0xc4, 0x01, 0xc4, 0x81, 0x15, 0x12, 0xd2, 0x5e, 0xb9, 0xf1, 0x67, 0xec, 0xb1, + 0x12, 0x17, 0x4e, 0x08, 0xb5, 0x1c, 0xf8, 0x33, 0xd0, 0xcc, 0x3c, 0x27, 0x76, 0x1b, 0xd7, 0x29, + 0x37, 0xcf, 0xe4, 0xfd, 0xf8, 0xbc, 0xf7, 0x66, 0xbe, 0x13, 0xb0, 0x58, 0xd8, 0x61, 0x83, 0x5e, + 0x37, 0x08, 0x1d, 0x36, 0xea, 0x39, 0xa3, 0x1a, 0xf5, 0xfb, 0x1d, 0x5a, 0x73, 0x9e, 0x0f, 0xd9, + 0xe0, 0xc4, 0xee, 0x0f, 0x78, 0xc8, 0xc9, 0xdd, 0xb1, 0x8d, 0xcd, 0x46, 0x3d, 0x3b, 0xb2, 0x31, + 0x4b, 0x1e, 0xf7, 0xb8, 0x32, 0x71, 0xe4, 0x97, 0xb6, 0x36, 0xb7, 0x5d, 0x2e, 0x7a, 0x5c, 0x38, + 0x2d, 0x2a, 0x98, 0x0e, 0xe3, 0x8c, 0x6a, 0x2d, 0x16, 0xd2, 0x9a, 0xd3, 0xa7, 0x5e, 0x37, 0xa0, + 0x61, 0x97, 0x07, 0x68, 0xfb, 0x8e, 0xc7, 0xb9, 0xe7, 0x33, 0x87, 0xf6, 0xbb, 0x0e, 0x0d, 0x02, + 0x1e, 0xaa, 0x1f, 0x05, 0xfe, 0xba, 0x91, 0xc2, 0x26, 0x21, 0x94, 0x85, 0xf5, 0x11, 0xbc, 0xf1, + 0xa5, 0xcc, 0xb0, 0xe7, 0xba, 0x7c, 0x18, 0x84, 0x0d, 0xf6, 0x7c, 0xc8, 0x44, 0x48, 0xd6, 0xa0, + 0x48, 0xdb, 0xed, 0x01, 0x13, 0x62, 0xcd, 0xd8, 0x30, 0xaa, 0x4b, 0x8d, 0x68, 0xf9, 0xf1, 0xe2, + 0xb7, 0x2f, 0xd7, 0x73, 0xff, 0xbe, 0x5c, 0xcf, 0x59, 0x2e, 0x94, 0x92, 0xae, 0xa2, 0xcf, 0x03, + 0xc1, 0xa4, 0x6f, 0x8b, 0xfa, 0x34, 0x70, 0x59, 0xe4, 0x8b, 0x4b, 0x72, 0x0f, 0x96, 0x5c, 0xde, + 0x66, 0xcd, 0x0e, 0x15, 0x9d, 0xb5, 0x39, 0xf5, 0xdb, 0xa2, 0xdc, 0x78, 0x4c, 0x45, 0x87, 0x94, + 0x60, 0x21, 0xe0, 0xd2, 0x69, 0x7e, 0xc3, 0xa8, 0xe6, 0x1b, 0x7a, 0x61, 0x7d, 0x0a, 0x6f, 0xab, + 0x24, 0x8f, 0x54, 0x4b, 0xfe, 0x07, 0xe5, 0x37, 0x06, 0x98, 0xd3, 0x22, 0x20, 0xec, 0x7d, 0x78, + 0x4d, 0x77, 0xbb, 0x99, 0x8c, 0xb4, 0xaa, 0x77, 0xf7, 0xf4, 0x26, 0x31, 0x61, 0x51, 0xc8, 0xa4, + 0x92, 0x6f, 0x4e, 0xf1, 0x8d, 0xd7, 0x32, 0x04, 0xd5, 0x51, 0x9b, 0xc1, 0xb0, 0xd7, 0x62, 0x03, + 0xac, 0x60, 0x15, 0x77, 0xbf, 0x50, 0x9b, 0xe3, 0x4e, 0xef, 0xeb, 0x66, 0xdc, 0xa6, 0x86, 0x87, + 0xd8, 0xe9, 0xb1, 0x6b, 0x56, 0xa7, 0xad, 0xcf, 0x31, 0xd9, 0x61, 0xc8, 0x07, 0xd4, 0xcb, 0x4e, + 0x46, 0xee, 0xc0, 0xfc, 0x11, 0x3b, 0xc1, 0xa1, 0xc8, 0xcf, 0x58, 0xfa, 0x5d, 0x4c, 0x3f, 0x0e, + 0x86, 0xe9, 0x4b, 0xb0, 0x30, 0xa2, 0xfe, 0x30, 0x4a, 0xae, 0x17, 0xd6, 0xfb, 0x70, 0x07, 0xfb, + 0xdd, 0xbe, 0x55, 0x91, 0x15, 0x78, 0x3d, 0xe6, 0x87, 0x29, 0x08, 0xe4, 0xe5, 0x01, 0x51, 0x5e, + 0x2b, 0x0d, 0xf5, 0x6d, 0xd5, 0x81, 0x28, 0xc3, 0x67, 0xc7, 0x4f, 0xb8, 0x27, 0xa2, 0x14, 0x04, + 0xf2, 0xea, 0x58, 0xe9, 0xf8, 0xea, 0x3b, 0x16, 0xfc, 0x00, 0xfb, 0x11, 0xf9, 0x60, 0x78, 0x07, + 0xf2, 0x3e, 0xf7, 0x24, 0xd4, 0x7c, 0x75, 0xb9, 0x7e, 0xcf, 0x9e, 0x7e, 0x4d, 0xed, 0x27, 0xdc, + 0x6b, 0x28, 0x43, 0xeb, 0x0c, 0xde, 0xd4, 0x93, 0xf0, 0xb9, 0x7b, 0x94, 0x91, 0x9e, 0x1c, 0x00, + 0x4c, 0xee, 0xab, 0x6a, 0xed, 0x72, 0x7d, 0xd3, 0xd6, 0x07, 0xcb, 0x96, 0x97, 0xdb, 0xd6, 0x1a, + 0x81, 0x97, 0xdb, 0x7e, 0x3a, 0x99, 0x54, 0x23, 0xe6, 0x19, 0x2b, 0xe3, 0x37, 0x03, 0xee, 0x5e, + 0xcd, 0x8f, 0xa5, 0x1c, 0x40, 0x31, 0x3c, 0x6e, 0xc6, 0xaa, 0xa9, 0xa4, 0x55, 0xf3, 0x6c, 0x40, + 0x03, 0x41, 0x5d, 0x19, 0x5a, 0x46, 0xd8, 0xcf, 0xbf, 0xfa, 0x6b, 0x3d, 0xd7, 0x28, 0x84, 0xaa, + 0x35, 0xe4, 0xb3, 0x29, 0xd0, 0x95, 0x4c, 0x68, 0x0d, 0x11, 0xa7, 0xb6, 0xd6, 0xe2, 0xa8, 0xfb, + 0x3e, 0xe7, 0x3d, 0xac, 0xcd, 0x72, 0xe0, 0xad, 0x6b, 0xbf, 0x4c, 0x8e, 0x54, 0x4b, 0x6e, 0xe0, + 0xc0, 0xf5, 0xc2, 0x2a, 0xe1, 0xc4, 0x9f, 0xd2, 0x01, 0xed, 0x45, 0x2d, 0xb7, 0x0e, 0x71, 0xa6, + 0xd1, 0x2e, 0x86, 0xf8, 0x04, 0x0a, 0x7d, 0xb5, 0xa3, 0x62, 0x2c, 0xd7, 0xcb, 0x69, 0x7d, 0xd0, + 0x7e, 0x51, 0xf9, 0xda, 0xc7, 0x7a, 0x8c, 0xd4, 0x87, 0x52, 0x48, 0xdd, 0x47, 0xd4, 0xf7, 0xb3, + 0xef, 0x4e, 0x09, 0x16, 0xba, 0x41, 0x7f, 0x18, 0xaa, 0x6e, 0xad, 0x34, 0xf4, 0xc2, 0x7a, 0x80, + 0x55, 0xc6, 0x23, 0x4d, 0x4e, 0x75, 0x9b, 0x86, 0x34, 0x3a, 0xd5, 0xf2, 0xbb, 0xfe, 0x62, 0x05, + 0x16, 0x94, 0x3d, 0xf9, 0xd9, 0x80, 0x22, 0xca, 0x14, 0xd9, 0x49, 0x83, 0x9f, 0x22, 0xda, 0xe6, + 0xee, 0x6c, 0xc6, 0x1a, 0xc2, 0xaa, 0x7d, 0xfd, 0xc7, 0x3f, 0x3f, 0xcd, 0xed, 0x90, 0x2d, 0x27, + 0xe5, 0x91, 0x40, 0xf9, 0x72, 0x4e, 0xb1, 0xce, 0x33, 0xf2, 0xbb, 0x01, 0xab, 0x09, 0x19, 0x25, + 0xb5, 0x1b, 0x53, 0x4e, 0x13, 0x6d, 0xb3, 0x7e, 0x1b, 0x17, 0x64, 0xfd, 0x50, 0xb1, 0xd6, 0xc9, + 0xc3, 0x34, 0xd6, 0x48, 0xc3, 0xaf, 0x21, 0xbf, 0x30, 0xa0, 0x88, 0xb2, 0x99, 0xd1, 0xcc, 0xa4, + 0x2e, 0x67, 0x34, 0xf3, 0x8a, 0x12, 0x5b, 0x75, 0x05, 0xb8, 0x4b, 0xb6, 0xd3, 0x00, 0x51, 0x98, + 0x45, 0x0c, 0xed, 0x57, 0x03, 0x8a, 0x28, 0xa9, 0x19, 0x68, 0x49, 0x15, 0xcf, 0x40, 0xbb, 0xa2, + 0xd2, 0xd6, 0x07, 0x0a, 0xad, 0x46, 0x9c, 0x34, 0x34, 0xa1, 0x1d, 0x26, 0x64, 0xce, 0xe9, 0x11, + 0x3b, 0x39, 0x23, 0xdf, 0x1b, 0x90, 0x97, 0x62, 0x4c, 0xaa, 0x19, 0x13, 0x1b, 0xeb, 0xbc, 0xb9, + 0x35, 0x83, 0x25, 0x62, 0x39, 0x0a, 0x6b, 0x8b, 0x54, 0xd2, 0x47, 0xda, 0x4e, 0xb4, 0xeb, 0x47, + 0x03, 0x0a, 0x5a, 0xbe, 0xc9, 0xf6, 0x8d, 0x69, 0x12, 0xef, 0x82, 0xb9, 0x33, 0x93, 0x2d, 0x42, + 0xd9, 0x0a, 0xaa, 0x4a, 0x36, 0xd3, 0xa0, 0x50, 0x62, 0x9d, 0x53, 0x29, 0xf0, 0x6a, 0x84, 0x4b, + 0x63, 0x29, 0x26, 0x0f, 0x6e, 0x3e, 0x32, 0x57, 0x9e, 0x0c, 0xd3, 0x9e, 0xd5, 0x7c, 0xd6, 0x0b, + 0xdb, 0x92, 0x2e, 0x09, 0xbe, 0x5f, 0x0c, 0x80, 0x89, 0xca, 0x92, 0x19, 0x32, 0xc6, 0x85, 0xda, + 0x74, 0x66, 0xb6, 0x47, 0xc4, 0x1d, 0x85, 0x78, 0x9f, 0xbc, 0x7b, 0x33, 0xa2, 0x52, 0x75, 0xf2, + 0x9d, 0x01, 0x05, 0xad, 0xc1, 0x19, 0x03, 0x4d, 0xc8, 0x7e, 0xc6, 0x40, 0x93, 0x8f, 0x81, 0xb5, + 0xa9, 0x80, 0x36, 0x48, 0x39, 0x0d, 0x48, 0xcb, 0xbe, 0x6a, 0xd4, 0x44, 0xa8, 0x33, 0x1a, 0x75, + 0xed, 0x6d, 0xc8, 0x68, 0xd4, 0xf5, 0x17, 0x20, 0xbb, 0x51, 0x42, 0xf9, 0x34, 0x5d, 0xea, 0xfb, + 0xfb, 0x7b, 0xaf, 0x2e, 0xca, 0xc6, 0xf9, 0x45, 0xd9, 0xf8, 0xfb, 0xa2, 0x6c, 0xfc, 0x70, 0x59, + 0xce, 0x9d, 0x5f, 0x96, 0x73, 0x7f, 0x5e, 0x96, 0x73, 0x5f, 0x55, 0xbc, 0x6e, 0xd8, 0x19, 0xb6, + 0x6c, 0x97, 0xf7, 0x50, 0x02, 0x63, 0xf1, 0x8e, 0x55, 0xc4, 0xf0, 0xa4, 0xcf, 0x44, 0xab, 0xa0, + 0xfe, 0xed, 0xbf, 0xf7, 0x5f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x4b, 0xf0, 0x51, 0x91, 0xad, 0x0c, + 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -2579,7 +2579,7 @@ func (m *QueryAccountResponse) Unmarshal(dAtA []byte) error { if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field CodeHash", wireType) } - var byteLen int + var stringLen uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowQuery @@ -2589,25 +2589,23 @@ func (m *QueryAccountResponse) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - byteLen |= int(b&0x7F) << shift + stringLen |= uint64(b&0x7F) << shift if b < 0x80 { break } } - if byteLen < 0 { + intStringLen := int(stringLen) + if intStringLen < 0 { return ErrInvalidLengthQuery } - postIndex := iNdEx + byteLen + postIndex := iNdEx + intStringLen if postIndex < 0 { return ErrInvalidLengthQuery } if postIndex > l { return io.ErrUnexpectedEOF } - m.CodeHash = append(m.CodeHash[:0], dAtA[iNdEx:postIndex]...) - if m.CodeHash == nil { - m.CodeHash = []byte{} - } + m.CodeHash = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: if wireType != 0 { diff --git a/x/evm/types/state_object.go b/x/evm/types/state_object.go deleted file mode 100644 index 78599fde..00000000 --- a/x/evm/types/state_object.go +++ /dev/null @@ -1,457 +0,0 @@ -package types - -import ( - "bytes" - "fmt" - "io" - "math/big" - - "github.com/cosmos/cosmos-sdk/store/prefix" - sdk "github.com/cosmos/cosmos-sdk/types" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - - ethermint "github.com/cosmos/ethermint/types" - - ethcmn "github.com/ethereum/go-ethereum/common" - ethstate "github.com/ethereum/go-ethereum/core/state" - ethcrypto "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/rlp" -) - -var ( - _ StateObject = (*stateObject)(nil) - - EmptyCodeHash = ethcrypto.Keccak256(nil) -) - -// StateObject interface for interacting with state object -type StateObject interface { - GetCommittedState(db ethstate.Database, key ethcmn.Hash) ethcmn.Hash - GetState(db ethstate.Database, key ethcmn.Hash) ethcmn.Hash - SetState(db ethstate.Database, key, value ethcmn.Hash) - - Code(db ethstate.Database) []byte - SetCode(codeHash ethcmn.Hash, code []byte) - CodeHash() []byte - - AddBalance(amount *big.Int) - SubBalance(amount *big.Int) - SetBalance(amount *big.Int) - - Balance() *big.Int - ReturnGas(gas *big.Int) - Address() ethcmn.Address - - SetNonce(nonce uint64) - Nonce() uint64 -} - -// stateObject represents an Ethereum account which is being modified. -// -// The usage pattern is as follows: -// First you need to obtain a state object. -// 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 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 - // by StateDB.Commit. - originStorage Storage // Storage cache of original entries to dedup rewrites - dirtyStorage Storage // Storage entries that need to be flushed to disk - - // DB error - dbErr error - stateDB *CommitStateDB - account *ethermint.EthAccount - // balance represents the amount of the EVM denom token that an account holds - balance sdk.Int - - keyToOriginStorageIndex map[ethcmn.Hash]int - keyToDirtyStorageIndex map[ethcmn.Hash]int - - address ethcmn.Address - - // cache flags - // - // When an object is marked suicided it will be delete from the trie during - // the "update" phase of the state transition. - dirtyCode bool // true if the code was updated - suicided bool - deleted bool -} - -func newStateObject(db *CommitStateDB, accProto authtypes.AccountI, balance sdk.Int) *stateObject { - ethAccount, ok := accProto.(*ethermint.EthAccount) - if !ok { - panic(fmt.Sprintf("invalid account type for state object: %T", accProto)) - } - - // set empty code hash - if ethAccount.CodeHash == nil { - ethAccount.CodeHash = EmptyCodeHash - } - - return &stateObject{ - stateDB: db, - account: ethAccount, - balance: balance, - address: ethAccount.EthAddress(), - originStorage: Storage{}, - dirtyStorage: Storage{}, - keyToOriginStorageIndex: make(map[ethcmn.Hash]int), - keyToDirtyStorageIndex: make(map[ethcmn.Hash]int), - } -} - -// ---------------------------------------------------------------------------- -// Setters -// ---------------------------------------------------------------------------- - -// SetState updates a value in account storage. Note, the key will be prefixed -// with the address of the state object. -func (so *stateObject) SetState(db ethstate.Database, key, value ethcmn.Hash) { - // if the new value is the same as old, don't set - prev := so.GetState(db, key) - if prev == value { - return - } - - prefixKey := so.GetStorageByAddressKey(key.Bytes()) - - // since the new value is different, update and journal the change - so.stateDB.journal.append(storageChange{ - account: &so.address, - key: prefixKey, - prevValue: prev, - }) - - so.setState(prefixKey, value) -} - -// setState sets a state with a prefixed key and value to the dirty storage. -func (so *stateObject) setState(key, value ethcmn.Hash) { - idx, ok := so.keyToDirtyStorageIndex[key] - if ok { - so.dirtyStorage[idx].Value = value.String() - return - } - - // create new entry - so.dirtyStorage = append(so.dirtyStorage, NewState(key, value)) - idx = len(so.dirtyStorage) - 1 - so.keyToDirtyStorageIndex[key] = idx -} - -// SetCode sets the state object's code. -func (so *stateObject) SetCode(codeHash ethcmn.Hash, code []byte) { - prevCode := so.Code(nil) - - so.stateDB.journal.append(codeChange{ - account: &so.address, - prevHash: so.CodeHash(), - prevCode: prevCode, - }) - - so.setCode(codeHash, code) -} - -func (so *stateObject) setCode(codeHash ethcmn.Hash, code []byte) { - so.code = code - so.account.CodeHash = codeHash.Bytes() - so.dirtyCode = true -} - -// AddBalance adds an amount to a state object's balance. It is used to add -// funds to the destination account of a transfer. -func (so *stateObject) AddBalance(amount *big.Int) { - amt := sdk.NewIntFromBigInt(amount) - // EIP158: We must check emptiness for the objects such that the account - // clearing (0,0,0 objects) can take effect. - - // NOTE: this will panic if amount is nil - if amt.IsZero() { - if so.empty() { - so.touch() - } - return - } - - newBalance := so.balance.Add(amt) - so.SetBalance(newBalance.BigInt()) -} - -// SubBalance removes an amount from the stateObject's balance. It is used to -// remove funds from the origin account of a transfer. -func (so *stateObject) SubBalance(amount *big.Int) { - amt := sdk.NewIntFromBigInt(amount) - if amt.IsZero() { - return - } - - newBalance := so.balance.Sub(amt) - so.SetBalance(newBalance.BigInt()) -} - -// SetBalance sets the state object's balance. It doesn't perform any validation -// on the amount value. -func (so *stateObject) SetBalance(amount *big.Int) { - amt := sdk.NewIntFromBigInt(amount) - - so.stateDB.journal.append(balanceChange{ - account: &so.address, - prev: so.balance, - }) - - so.setBalance(amt) -} - -func (so *stateObject) setBalance(amount sdk.Int) { - so.balance = amount -} - -// SetNonce sets the state object's nonce (i.e sequence number of the account). -func (so *stateObject) SetNonce(nonce uint64) { - so.stateDB.journal.append(nonceChange{ - account: &so.address, - prev: so.account.Sequence, - }) - - so.setNonce(nonce) -} - -func (so *stateObject) setNonce(nonce uint64) { - if so.account == nil { - panic("state object account is empty") - } - so.account.Sequence = nonce -} - -// setError remembers the first non-nil error it is called with. -func (so *stateObject) setError(err error) { - if so.dbErr == nil { - so.dbErr = err - } -} - -func (so *stateObject) markSuicided() { - so.suicided = true -} - -// commitState commits all dirty storage to a KVStore and resets -// the dirty storage slice to the empty state. -func (so *stateObject) commitState() { - ctx := so.stateDB.ctx - store := prefix.NewStore(ctx.KVStore(so.stateDB.storeKey), AddressStoragePrefix(so.Address())) - - 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 ethermint.IsEmptyHash(state.Value) { - store.Delete(key.Bytes()) - } - - delete(so.keyToDirtyStorageIndex, key) - - // skip no-op changes, persist actual changes - idx, ok := so.keyToOriginStorageIndex[key] - if !ok { - continue - } - - if ethermint.IsEmptyHash(state.Value) { - delete(so.keyToOriginStorageIndex, key) - continue - } - - if state.Value == so.originStorage[idx].Value { - continue - } - - so.originStorage[idx].Value = state.Value - store.Set(key.Bytes(), value.Bytes()) - } - // clean storage as all entries are dirty - so.dirtyStorage = Storage{} -} - -// commitCode persists the state object's code to the KVStore. -func (so *stateObject) commitCode() { - ctx := so.stateDB.ctx - store := prefix.NewStore(ctx.KVStore(so.stateDB.storeKey), KeyPrefixCode) - store.Set(so.CodeHash(), so.code) -} - -// ---------------------------------------------------------------------------- -// Getters -// ---------------------------------------------------------------------------- - -// Address returns the address of the state object. -func (so stateObject) Address() ethcmn.Address { - return so.address -} - -// Balance returns the state object's current balance. -func (so *stateObject) Balance() *big.Int { - balance := so.balance.BigInt() - if balance == nil { - return zeroBalance - } - return balance -} - -// CodeHash returns the state object's code hash. -func (so *stateObject) CodeHash() []byte { - if so.account == nil || len(so.account.CodeHash) == 0 { - return EmptyCodeHash - } - return so.account.CodeHash -} - -// Nonce returns the state object's current nonce (sequence number). -func (so *stateObject) Nonce() uint64 { - if so.account == nil { - return 0 - } - return so.account.Sequence -} - -// Code returns the contract code associated with this object, if any. -func (so *stateObject) Code(_ ethstate.Database) []byte { - if len(so.code) > 0 { - return so.code - } - - if bytes.Equal(so.CodeHash(), EmptyCodeHash) { - return nil - } - - ctx := so.stateDB.ctx - store := prefix.NewStore(ctx.KVStore(so.stateDB.storeKey), KeyPrefixCode) - code := store.Get(so.CodeHash()) - - if len(code) == 0 { - so.setError(fmt.Errorf("failed to get code hash %x for address %s", so.CodeHash(), so.Address().String())) - } - - return code -} - -// GetState retrieves a value from the account storage trie. Note, the key will -// be prefixed with the address of the state object. -func (so *stateObject) GetState(db ethstate.Database, key ethcmn.Hash) ethcmn.Hash { - prefixKey := so.GetStorageByAddressKey(key.Bytes()) - - // if we have a dirty value for this state entry, return it - idx, dirty := so.keyToDirtyStorageIndex[prefixKey] - if dirty { - value := ethcmn.HexToHash(so.dirtyStorage[idx].Value) - return value - } - - // otherwise return the entry's original value - value := so.GetCommittedState(db, key) - return value -} - -// GetCommittedState retrieves a value from the committed account storage trie. -// -// NOTE: the key will be prefixed with the address of the state object. -func (so *stateObject) GetCommittedState(_ ethstate.Database, key ethcmn.Hash) ethcmn.Hash { - prefixKey := so.GetStorageByAddressKey(key.Bytes()) - - // if we have the original value cached, return that - idx, cached := so.keyToOriginStorageIndex[prefixKey] - if cached { - 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 { - value.SetBytes(rawValue) - state.Value = value.String() - } - - so.originStorage = append(so.originStorage, state) - so.keyToOriginStorageIndex[prefixKey] = len(so.originStorage) - 1 - return value -} - -// ---------------------------------------------------------------------------- -// Auxiliary -// ---------------------------------------------------------------------------- - -// ReturnGas returns the gas back to the origin. Used by the Virtual machine or -// Closures. It performs a no-op. -func (so *stateObject) ReturnGas(gas *big.Int) {} - -func (so *stateObject) deepCopy(db *CommitStateDB) *stateObject { - newStateObj := newStateObject(db, so.account, so.balance) - - newStateObj.code = so.code - newStateObj.dirtyStorage = so.dirtyStorage.Copy() - newStateObj.originStorage = so.originStorage.Copy() - newStateObj.suicided = so.suicided - newStateObj.dirtyCode = so.dirtyCode - newStateObj.deleted = so.deleted - - return newStateObj -} - -// empty returns whether the account is considered empty. -func (so *stateObject) empty() bool { - return so.account == nil || - (so.account != nil && - so.account.Sequence == 0 && - (so.balance.BigInt() == nil || so.balance.IsZero()) && - bytes.Equal(so.account.CodeHash, EmptyCodeHash)) -} - -// EncodeRLP implements rlp.Encoder. -func (so *stateObject) EncodeRLP(w io.Writer) error { - return rlp.Encode(w, so.account) -} - -func (so *stateObject) touch() { - so.stateDB.journal.append(touchChange{ - account: &so.address, - }) - - if so.address == ripemd { - // Explicitly put it in the dirty-cache, which is otherwise generated from - // flattened journals. - so.stateDB.journal.dirty(so.address) - } -} - -// GetStorageByAddressKey returns a hash of the composite key for a state -// object's storage prefixed with it's address. -func (so stateObject) GetStorageByAddressKey(key []byte) ethcmn.Hash { - prefix := so.Address().Bytes() - compositeKey := make([]byte, len(prefix)+len(key)) - - copy(compositeKey, prefix) - copy(compositeKey[len(prefix):], key) - - return ethcrypto.Keccak256Hash(compositeKey) -} - -// stateEntry represents a single key value pair from the StateDB's stateObject mappindg. -// This is to prevent non determinism at genesis initialization or export. -type stateEntry struct { - // address key of the state object - address ethcmn.Address - stateObject *stateObject -} diff --git a/x/evm/types/state_object_test.go b/x/evm/types/state_object_test.go deleted file mode 100644 index 6739a2ec..00000000 --- a/x/evm/types/state_object_test.go +++ /dev/null @@ -1,135 +0,0 @@ -package types_test - -import ( - "math/big" - - ethcmn "github.com/ethereum/go-ethereum/common" -) - -func (suite *StateDBTestSuite) TestStateObject_State() { - testCase := []struct { - name string - key ethcmn.Hash - expValue ethcmn.Hash - malleate func() - }{ - { - "no set value, load from KVStore", - ethcmn.BytesToHash([]byte("key")), - ethcmn.Hash{}, - func() {}, - }, - { - "no-op SetState", - ethcmn.BytesToHash([]byte("key")), - ethcmn.Hash{}, - func() { - suite.stateObject.SetState(nil, ethcmn.BytesToHash([]byte("key")), ethcmn.Hash{}) - }, - }, - { - "cached value", - ethcmn.BytesToHash([]byte("key1")), - ethcmn.BytesToHash([]byte("value1")), - func() { - suite.stateObject.SetState(nil, ethcmn.BytesToHash([]byte("key1")), ethcmn.BytesToHash([]byte("value1"))) - }, - }, - { - "update value", - ethcmn.BytesToHash([]byte("key1")), - ethcmn.BytesToHash([]byte("value2")), - func() { - suite.stateObject.SetState(nil, ethcmn.BytesToHash([]byte("key1")), ethcmn.BytesToHash([]byte("value2"))) - }, - }, - { - "update various keys", - ethcmn.BytesToHash([]byte("key1")), - ethcmn.BytesToHash([]byte("value1")), - func() { - suite.stateObject.SetState(nil, ethcmn.BytesToHash([]byte("key1")), ethcmn.BytesToHash([]byte("value1"))) - suite.stateObject.SetState(nil, ethcmn.BytesToHash([]byte("key2")), ethcmn.BytesToHash([]byte("value2"))) - suite.stateObject.SetState(nil, ethcmn.BytesToHash([]byte("key3")), ethcmn.BytesToHash([]byte("value3"))) - }, - }, - } - - for _, tc := range testCase { - tc.malleate() - - value := suite.stateObject.GetState(nil, tc.key) - suite.Require().Equal(tc.expValue, value, tc.name) - } -} - -func (suite *StateDBTestSuite) TestStateObject_AddBalance() { - testCase := []struct { - name string - amount *big.Int - expBalance *big.Int - }{ - {"zero amount", big.NewInt(0), big.NewInt(0)}, - {"positive amount", big.NewInt(10), big.NewInt(10)}, - {"negative amount", big.NewInt(-1), big.NewInt(9)}, - } - - for _, tc := range testCase { - suite.stateObject.AddBalance(tc.amount) - suite.Require().Equal(tc.expBalance, suite.stateObject.Balance(), tc.name) - } -} - -func (suite *StateDBTestSuite) TestStateObject_SubBalance() { - testCase := []struct { - name string - amount *big.Int - expBalance *big.Int - }{ - {"zero amount", big.NewInt(0), big.NewInt(0)}, - {"negative amount", big.NewInt(-10), big.NewInt(10)}, - {"positive amount", big.NewInt(1), big.NewInt(9)}, - } - - for _, tc := range testCase { - suite.stateObject.SubBalance(tc.amount) - suite.Require().Equal(tc.expBalance, suite.stateObject.Balance(), tc.name) - } -} - -func (suite *StateDBTestSuite) TestStateObject_Code() { - testCase := []struct { - name string - expCode []byte - malleate func() - }{ - { - "cached code", - []byte("code"), - func() { - suite.stateObject.SetCode(ethcmn.BytesToHash([]byte("code_hash")), []byte("code")) - }, - }, - { - "empty code hash", - nil, - func() { - suite.stateObject.SetCode(ethcmn.Hash{}, nil) - }, - }, - { - "empty code", - nil, - func() { - suite.stateObject.SetCode(ethcmn.BytesToHash([]byte("code_hash")), nil) - }, - }, - } - - for _, tc := range testCase { - tc.malleate() - - code := suite.stateObject.Code(nil) - suite.Require().Equal(tc.expCode, code, tc.name) - } -} diff --git a/x/evm/types/state_transition.go b/x/evm/types/state_transition.go deleted file mode 100644 index f3433cde..00000000 --- a/x/evm/types/state_transition.go +++ /dev/null @@ -1,340 +0,0 @@ -package types - -import ( - "math/big" - "os" - "time" - - "github.com/pkg/errors" - log "github.com/xlab/suplog" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core" - ethtypes "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/core/vm" - tmtypes "github.com/tendermint/tendermint/types" - - "github.com/cosmos/cosmos-sdk/telemetry" - sdk "github.com/cosmos/cosmos-sdk/types" - sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" -) - -// StateTransition defines data to transitionDB in evm -type StateTransition struct { - // TxData fields - Message core.Message - - ChainID *big.Int - Csdb *CommitStateDB - TxHash *common.Hash - Simulate bool // i.e CheckTx execution - Debug bool // enable EVM debugging -} - -// GasInfo returns the gas limit, gas consumed and gas refunded from the EVM transition -// execution -type GasInfo struct { - GasLimit uint64 - GasConsumed uint64 - GasRefunded uint64 -} - -// ExecutionResult represents what's returned from a transition -type ExecutionResult struct { - Logs []*ethtypes.Log - Bloom *big.Int - Response *MsgEthereumTxResponse - GasInfo GasInfo -} - -// GetHashFn implements vm.GetHashFunc for Ethermint. It handles 3 cases: -// 1. The requested height matches the current height from context (and thus same epoch number) -// 2. The requested height is from an previous height from the same chain epoch -// 3. The requested height is from a height greater than the latest one -func GetHashFn(ctx sdk.Context, csdb *CommitStateDB) vm.GetHashFunc { - return func(height uint64) common.Hash { - switch { - case ctx.BlockHeight() == int64(height): - // Case 1: The requested height matches the one from the context so we can retrieve the header - // hash directly from the context. - return csdb.bhash - - case ctx.BlockHeight() > int64(height): - // Case 2: if the chain is not the current height we need to retrieve the hash from the store for the - // current chain epoch. This only applies if the current height is greater than the requested height. - return csdb.WithContext(ctx).GetHeightHash(height) - - default: - // Case 3: heights greater than the current one returns an empty hash. - return common.Hash{} - } - } -} - -func (st *StateTransition) newEVM( - ctx sdk.Context, - csdb *CommitStateDB, - gasLimit uint64, - config ChainConfig, - extraEIPs []int64, -) *vm.EVM { - // Create context for evm - blockCtx := vm.BlockContext{ - CanTransfer: core.CanTransfer, - Transfer: core.Transfer, - GetHash: GetHashFn(ctx, csdb), - Coinbase: common.Address{}, // there's no beneficiary since we're not mining - BlockNumber: big.NewInt(ctx.BlockHeight()), - Time: big.NewInt(ctx.BlockHeader().Time.Unix()), - Difficulty: big.NewInt(0), // unused. Only required in PoW context - GasLimit: gasLimit, - } - - txCtx := core.NewEVMTxContext(st.Message) - - eips := make([]int, len(extraEIPs)) - for i, eip := range extraEIPs { - eips[i] = int(eip) - } - - vmConfig := vm.Config{ - EnablePreimageRecording: false, // no need for StateDB.AddPreimage - ExtraEips: eips, - } - - if st.Debug { - vmConfig.Tracer = vm.NewJSONLogger(&vm.LogConfig{ - Debug: true, - }, os.Stderr) - - vmConfig.Debug = true - } - - return vm.NewEVM(blockCtx, txCtx, csdb, config.EthereumConfig(st.ChainID), vmConfig) -} - -// TransitionDb will transition the state by applying the current transaction and -// returning the evm execution result. -// NOTE: State transition checks are run during AnteHandler execution. -func (st *StateTransition) TransitionDb(ctx sdk.Context, config ChainConfig) (resp *ExecutionResult, err error) { - defer telemetry.ModuleMeasureSince(ModuleName, time.Now(), MetricKeyTransitionDB) - - contractCreation := st.Message.To() == nil - - cost, err := core.IntrinsicGas(st.Message.Data(), st.Message.AccessList(), true, false, true) - if err != nil { - err = sdkerrors.Wrap(err, "invalid intrinsic gas for transaction") - return nil, err - } - - // This gas limit the the transaction gas limit with intrinsic gas subtracted - gasLimit := st.Message.Gas() - ctx.GasMeter().GasConsumed() - - csdb := st.Csdb.WithContext(ctx) - if st.Simulate { - // gasLimit is set here because stdTxs incur gaskv charges in the ante handler, but for eth_call - // the cost needs to be the same as an Ethereum transaction sent through the web3 API - consumedGas := ctx.GasMeter().GasConsumed() - gasLimit = st.Message.Gas() - cost - if consumedGas < cost { - // If Cosmos standard tx ante handler cost is less than EVM intrinsic cost - // gas must be consumed to match to accurately simulate an Ethereum transaction - ctx.GasMeter().ConsumeGas(cost-consumedGas, "Intrinsic gas match") - } - - csdb = st.Csdb.Copy() - } - - // This gas meter is set up to consume gas from gaskv during evm execution and be ignored - currentGasMeter := ctx.GasMeter() - evmGasMeter := sdk.NewInfiniteGasMeter() - csdb.WithContext(ctx.WithGasMeter(evmGasMeter)) - - // Clear cache of accounts to handle changes outside of the EVM - csdb.UpdateAccounts() - - params := csdb.GetParams() - - gasPrice := ctx.MinGasPrices().AmountOf(params.EvmDenom) - //gasPrice := sdk.ZeroDec() - if gasPrice.IsNil() { - return nil, errors.New("min gas price cannot be nil") - } - - evm := st.newEVM(ctx, csdb, gasLimit, config, params.ExtraEIPs) - - var ( - ret []byte - leftOverGas uint64 - contractAddress common.Address - senderRef = vm.AccountRef(st.Message.From()) - ) - - // Get nonce of account outside of the EVM - currentNonce := csdb.GetNonce(st.Message.From()) - // Set nonce of sender account before evm state transition for usage in generating Create address - csdb.SetNonce(st.Message.From(), st.Message.Nonce()) - - // create contract or execute call - switch contractCreation { - case true: - if !params.EnableCreate { - return nil, ErrCreateDisabled - } - - ret, contractAddress, leftOverGas, err = evm.Create(senderRef, st.Message.Data(), gasLimit, st.Message.Value()) - - if err != nil { - log.WithField("simulate", st.Simulate). - WithField("nonce", st.Message.Nonce()). - WithField("contract", contractAddress.String()). - WithError(err).Warningln("evm contract creation failed") - } - - gasConsumed := gasLimit - leftOverGas - resp = &ExecutionResult{ - Response: &MsgEthereumTxResponse{ - Ret: ret, - }, - GasInfo: GasInfo{ - GasConsumed: gasConsumed, - GasLimit: gasLimit, - GasRefunded: leftOverGas, - }, - } - default: - if !params.EnableCall { - return nil, ErrCallDisabled - } - - // Increment the nonce for the next transaction (just for evm state transition) - csdb.SetNonce(st.Message.From(), csdb.GetNonce(st.Message.From())+1) - - ret, leftOverGas, err = evm.Call(senderRef, *st.Message.To(), st.Message.Data(), gasLimit, st.Message.Value()) - - // fmt.Println("EVM CALL!!!", senderRef.Address().Hex(), (*st.Message.To()).Hex(), gasLimit) - // fmt.Println("EVM CALL RESULT", common.ToHex(ret), leftOverGas, err) - - if err != nil { - log.WithField("recipient", st.Message.To().String()). - WithError(err).Debugln("evm call failed") - } - - gasConsumed := gasLimit - leftOverGas - resp = &ExecutionResult{ - Response: &MsgEthereumTxResponse{ - Ret: ret, - }, - GasInfo: GasInfo{ - GasConsumed: gasConsumed, - GasLimit: gasLimit, - GasRefunded: leftOverGas, - }, - } - } - - if err != nil { - // Consume gas before returning - ctx.GasMeter().ConsumeGas(resp.GasInfo.GasConsumed, "evm execution consumption") - return resp, err - } - - // Resets nonce to value pre state transition - csdb.SetNonce(st.Message.From(), currentNonce) - - var ( - logs []*ethtypes.Log - ) - - if st.TxHash != nil && !st.Simulate { - logs, err = csdb.GetLogs(*st.TxHash) - if err != nil { - err = errors.Wrap(err, "failed to get logs") - return nil, err - } - } - - if !st.Simulate { - // Finalise state if not a simulated transaction - // TODO: change to depend on config - if err := csdb.Finalise(true); err != nil { - return nil, err - } - } - - resp.Logs = logs - resp.Response = &MsgEthereumTxResponse{ - Logs: NewLogsFromEth(logs), - Ret: ret, - } - - // TODO: Refund unused gas here, if intended in future - - // Consume gas from evm execution - // Out of gas check does not need to be done here since it is done within the EVM execution - - // TODO: @albert, @maxim, decide if can take this out, since InternalEthereumTx may want to continue execution afterwards - // which will use gas. - _ = currentGasMeter - //ctx.WithGasMeter(currentGasMeter).GasMeter().ConsumeGas(resp.GasInfo.GasConsumed, "EVM execution consumption") - - return resp, nil -} - -// StaticCall executes the contract associated with the addr with the given input -// as parameters while disallowing any modifications to the state during the call. -// Opcodes that attempt to perform such modifications will result in exceptions -// instead of performing the modifications. -func (st *StateTransition) StaticCall(ctx sdk.Context, config ChainConfig) ([]byte, error) { - defer telemetry.ModuleMeasureSince(ModuleName, time.Now(), MetricKeyStaticCall) - - // This gas limit the the transaction gas limit with intrinsic gas subtracted - gasLimit := st.Message.Gas() - ctx.GasMeter().GasConsumed() - csdb := st.Csdb.WithContext(ctx) - - // This gas meter is set up to consume gas from gaskv during evm execution and be ignored - evmGasMeter := sdk.NewInfiniteGasMeter() - csdb.WithContext(ctx.WithGasMeter(evmGasMeter)) - - // Clear cache of accounts to handle changes outside of the EVM - csdb.UpdateAccounts() - - params := csdb.GetParams() - - gasPrice := ctx.MinGasPrices().AmountOf(params.EvmDenom) - if gasPrice.IsNil() { - return []byte{}, errors.New("min gas price cannot be nil") - } - - evm := st.newEVM(ctx, csdb, gasLimit, config, params.ExtraEIPs) - senderRef := vm.AccountRef(st.Message.From()) - - ret, _, err := evm.StaticCall(senderRef, *st.Message.To(), st.Message.Data(), gasLimit) - - // fmt.Println("EVM STATIC CALL!!!", senderRef.Address().Hex(), (*st.Message.To()).Hex(), st.Message.Data(), gasLimit) - // fmt.Println("EVM STATIC CALL RESULT", common.ToHex(ret), leftOverGas, err) - - return ret, err -} - -// HashFromContext returns the Ethereum Header hash from the context's Tendermint -// block header. -func HashFromContext(ctx sdk.Context) common.Hash { - // cast the ABCI header to tendermint Header type - protoHeader := ctx.BlockHeader() - tmHeader, err := tmtypes.HeaderFromProto(&protoHeader) - if err != nil { - return common.Hash{} - } - - // get the Tendermint block hash from the current header - tmBlockHash := tmHeader.Hash() - - // NOTE: if the validator set hash is missing the hash will be returned as nil, - // so we need to check for this case to prevent a panic when calling Bytes() - if tmBlockHash == nil { - return common.Hash{} - } - - return common.BytesToHash(tmBlockHash.Bytes()) -} diff --git a/x/evm/types/state_transition_test.go b/x/evm/types/state_transition_test.go deleted file mode 100644 index dffe0fc3..00000000 --- a/x/evm/types/state_transition_test.go +++ /dev/null @@ -1,190 +0,0 @@ -package types_test - -import ( - "math/big" - - sdk "github.com/cosmos/cosmos-sdk/types" - - "github.com/cosmos/ethermint/crypto/ethsecp256k1" - ethermint "github.com/cosmos/ethermint/types" - "github.com/cosmos/ethermint/x/evm/types" - - ethcmn "github.com/ethereum/go-ethereum/common" - ethtypes "github.com/ethereum/go-ethereum/core/types" - ethcrypto "github.com/ethereum/go-ethereum/crypto" -) - -func (suite *StateDBTestSuite) TestTransitionDb() { - suite.stateDB.SetNonce(suite.address, 123) - - addr := sdk.AccAddress(suite.address.Bytes()) - balance := ethermint.NewPhotonCoin(sdk.NewInt(5000)) - acc := suite.app.AccountKeeper.GetAccount(suite.ctx, addr) - suite.app.AccountKeeper.SetAccount(suite.ctx, acc) - suite.app.BankKeeper.SetBalance(suite.ctx, addr, balance) - - priv, err := ethsecp256k1.GenerateKey() - suite.Require().NoError(err) - recipient := ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey) - - testCase := []struct { - name string - malleate func() - state types.StateTransition - expPass bool - }{ - { - "passing state transition", - func() {}, - types.StateTransition{ - Message: ethtypes.NewMessage( - suite.address, - &recipient, - 123, - big.NewInt(50), - 11, - big.NewInt(10), - []byte("data"), - nil, - true, - ), - ChainID: big.NewInt(1), - Csdb: suite.stateDB, - TxHash: ðcmn.Hash{}, - Simulate: suite.ctx.IsCheckTx(), - }, - true, - }, - { - "contract creation", - func() {}, - types.StateTransition{ - Message: ethtypes.NewMessage( - suite.address, - nil, - 123, - big.NewInt(50), - 11, - big.NewInt(10), - []byte("data"), - nil, - true, - ), - ChainID: big.NewInt(1), - Csdb: suite.stateDB, - TxHash: ðcmn.Hash{}, - Simulate: true, - }, - true, - }, - { - "state transition simulation", - func() {}, - types.StateTransition{ - Message: ethtypes.NewMessage( - suite.address, - &recipient, - 123, - big.NewInt(50), - 11, - big.NewInt(10), - []byte("data"), - nil, - true, - ), - ChainID: big.NewInt(1), - Csdb: suite.stateDB, - TxHash: ðcmn.Hash{}, - Simulate: true, - }, - true, - }, - { - "fail by sending more than balance", - func() {}, - types.StateTransition{ - Message: ethtypes.NewMessage( - suite.address, - &recipient, - 123, - big.NewInt(50000000), - 11, - big.NewInt(10), - []byte("data"), - nil, - true, - ), - ChainID: big.NewInt(1), - Csdb: suite.stateDB, - TxHash: ðcmn.Hash{}, - Simulate: suite.ctx.IsCheckTx(), - }, - false, - }, - { - "failed to Finalize", - func() {}, - types.StateTransition{ - Message: ethtypes.NewMessage( - suite.address, - &recipient, - 123, - big.NewInt(-5000), - 11, - big.NewInt(10), - []byte("data"), - nil, - true, - ), - ChainID: big.NewInt(1), - Csdb: suite.stateDB, - TxHash: ðcmn.Hash{}, - Simulate: false, - }, - false, - }, - { - "nil gas price", - func() { - invalidGas := sdk.DecCoins{ - {Denom: ethermint.AttoPhoton}, - } - suite.ctx = suite.ctx.WithMinGasPrices(invalidGas) - }, - types.StateTransition{ - Message: ethtypes.NewMessage( - suite.address, - &recipient, - 123, - big.NewInt(50), - 11, - nil, - []byte("data"), - nil, - true, - ), - ChainID: big.NewInt(1), - Csdb: suite.stateDB, - TxHash: ðcmn.Hash{}, - Simulate: suite.ctx.IsCheckTx(), - }, - false, - }, - } - - for _, tc := range testCase { - tc.malleate() - - _, err = tc.state.TransitionDb(suite.ctx, types.DefaultChainConfig()) - - if tc.expPass { - suite.Require().NoError(err, tc.name) - fromBalance := suite.app.EvmKeeper.CommitStateDB.GetBalance(suite.address) - toBalance := suite.app.EvmKeeper.CommitStateDB.GetBalance(recipient) - suite.Require().Equal(fromBalance, big.NewInt(4950), tc.name) - suite.Require().Equal(toBalance, big.NewInt(50), tc.name) - } else { - suite.Require().Error(err, tc.name) - } - } -} diff --git a/x/evm/types/statedb.go b/x/evm/types/statedb.go deleted file mode 100644 index d9eb44db..00000000 --- a/x/evm/types/statedb.go +++ /dev/null @@ -1,990 +0,0 @@ -package types - -import ( - "fmt" - "math/big" - "sort" - "sync" - - "github.com/cosmos/cosmos-sdk/store/prefix" - sdk "github.com/cosmos/cosmos-sdk/types" - paramtypes "github.com/cosmos/cosmos-sdk/x/params/types" - - ethermint "github.com/cosmos/ethermint/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" - ethcrypto "github.com/ethereum/go-ethereum/crypto" -) - -var ( - _ ethvm.StateDB = (*CommitStateDB)(nil) - - zeroBalance = sdk.ZeroInt().BigInt() -) - -// CommitStateDB implements the Geth state.StateDB interface. Instead of using -// a trie and database for querying and persistence, the Keeper uses KVStores -// and an AccountKeeper to facilitate state transitions. -// -// TODO: This implementation is subject to change in regards to its statefull -// manner. In otherwords, how this relates to the keeper in this module. -type CommitStateDB struct { - // TODO: We need to store the context as part of the structure itself opposed - // to being passed as a parameter (as it should be) in order to implement the - // StateDB interface. Perhaps there is a better way. - ctx sdk.Context - - storeKey sdk.StoreKey - transientKey sdk.StoreKey - paramSpace paramtypes.Subspace - accountKeeper AccountKeeper - bankKeeper BankKeeper - - // array that hold 'live' objects, which will get modified while processing a - // state transition - stateObjects []stateEntry - addressToObjectIndex map[ethcmn.Address]int // map from address to the index of the state objects slice - stateObjectsDirty map[ethcmn.Address]struct{} - - // The refund counter, also used by state transitioning. - refund uint64 - - thash, bhash ethcmn.Hash - txIndex int - logSize uint - - // TODO: Determine if we actually need this as we do not need preimages in - // the SDK, but it seems to be used elsewhere in Geth. - preimages []preimageEntry - hashToPreimageIndex map[ethcmn.Hash]int // map from hash to the index of the preimages slice - - // DB error. - // 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 memo-ized here and will eventually be returned - // by StateDB.Commit. - dbErr error - - // journal of state modifications. This is the backbone of - // Snapshot and RevertToSnapshot. - journal *journal - validRevisions []revision - nextRevisionID int - - // mutex for state deep copying - lock sync.Mutex -} - -// NewCommitStateDB returns a reference to a newly initialized CommitStateDB -// which implements Geth's state.StateDB interface. -// -// CONTRACT: Stores used for state must be cache-wrapped as the ordering of the -// key/value space matters in determining the merkle root. -func NewCommitStateDB( - ctx sdk.Context, storeKey, tKey sdk.StoreKey, paramSpace paramtypes.Subspace, - ak AccountKeeper, bankKeeper BankKeeper, -) *CommitStateDB { - return &CommitStateDB{ - ctx: ctx, - storeKey: storeKey, - transientKey: tKey, - paramSpace: paramSpace, - accountKeeper: ak, - bankKeeper: bankKeeper, - stateObjects: []stateEntry{}, - addressToObjectIndex: make(map[ethcmn.Address]int), - stateObjectsDirty: make(map[ethcmn.Address]struct{}), - preimages: []preimageEntry{}, - hashToPreimageIndex: make(map[ethcmn.Hash]int), - journal: newJournal(), - } -} - -// WithContext returns a Database with an updated SDK context -func (csdb *CommitStateDB) WithContext(ctx sdk.Context) *CommitStateDB { - csdb.ctx = ctx - return csdb -} - -// ---------------------------------------------------------------------------- -// Setters -// ---------------------------------------------------------------------------- - -// SetHeightHash sets the block header hash associated with a given height. -func (csdb *CommitStateDB) SetHeightHash(height uint64, hash ethcmn.Hash) { - store := prefix.NewStore(csdb.ctx.KVStore(csdb.storeKey), KeyPrefixHeightToHeaderHash) - store.Set(sdk.Uint64ToBigEndian(height), hash.Bytes()) -} - -// SetParams sets the evm parameters to the param space. -func (csdb *CommitStateDB) SetParams(params Params) { - csdb.paramSpace.SetParamSet(csdb.ctx, ¶ms) -} - -// SetBalance sets the balance of an account. -func (csdb *CommitStateDB) SetBalance(addr ethcmn.Address, amount *big.Int) { - so := csdb.GetOrNewStateObject(addr) - so.SetBalance(amount) -} - -// AddBalance adds amount to the account associated with addr. -func (csdb *CommitStateDB) AddBalance(addr ethcmn.Address, amount *big.Int) { - so := csdb.GetOrNewStateObject(addr) - so.AddBalance(amount) -} - -// SubBalance subtracts amount from the account associated with addr. -func (csdb *CommitStateDB) SubBalance(addr ethcmn.Address, amount *big.Int) { - so := csdb.GetOrNewStateObject(addr) - so.SubBalance(amount) -} - -// SetNonce sets the nonce (sequence number) of an account. -func (csdb *CommitStateDB) SetNonce(addr ethcmn.Address, nonce uint64) { - so := csdb.GetOrNewStateObject(addr) - so.SetNonce(nonce) -} - -// SetState sets the storage state with a key, value pair for an account. -func (csdb *CommitStateDB) SetState(addr ethcmn.Address, key, value ethcmn.Hash) { - so := csdb.GetOrNewStateObject(addr) - so.SetState(nil, key, value) -} - -// SetCode sets the code for a given account. -func (csdb *CommitStateDB) SetCode(addr ethcmn.Address, code []byte) { - so := csdb.GetOrNewStateObject(addr) - so.SetCode(ethcrypto.Keccak256Hash(code), code) -} - -// ---------------------------------------------------------------------------- -// Transaction logs -// Required for upgrade logic or ease of querying. -// NOTE: we use BinaryLengthPrefixed since the tx logs are also included on Result data, -// which can't use BinaryBare. -// ---------------------------------------------------------------------------- - -// SetLogs sets the logs for a transaction in the KVStore. -func (csdb *CommitStateDB) SetLogs(hash ethcmn.Hash, logs []*ethtypes.Log) error { - store := prefix.NewStore(csdb.ctx.KVStore(csdb.storeKey), KeyPrefixLogs) - - txLogs := NewTransactionLogsFromEth(hash, logs) - bz, err := ModuleCdc.MarshalBinaryBare(&txLogs) - if err != nil { - return err - } - - store.Set(hash.Bytes(), bz) - csdb.logSize = uint(len(logs)) - return nil -} - -// DeleteLogs removes the logs from the KVStore. It is used during journal.Revert. -func (csdb *CommitStateDB) DeleteLogs(hash ethcmn.Hash) { - store := prefix.NewStore(csdb.ctx.KVStore(csdb.storeKey), KeyPrefixLogs) - store.Delete(hash.Bytes()) -} - -// AddLog adds a new log to the state and sets the log metadata from the state. -func (csdb *CommitStateDB) AddLog(log *ethtypes.Log) { - csdb.journal.append(addLogChange{txhash: csdb.thash}) - - log.TxHash = csdb.thash - log.BlockHash = csdb.bhash - log.TxIndex = uint(csdb.txIndex) - log.Index = csdb.logSize - - logs, err := csdb.GetLogs(csdb.thash) - if err != nil { - // panic on unmarshal error - panic(err) - } - - if err = csdb.SetLogs(csdb.thash, append(logs, log)); err != nil { - // panic on marshal error - panic(err) - } -} - -// AddPreimage records a SHA3 preimage seen by the VM. -func (csdb *CommitStateDB) AddPreimage(hash ethcmn.Hash, preimage []byte) { - if _, ok := csdb.hashToPreimageIndex[hash]; !ok { - csdb.journal.append(addPreimageChange{hash: hash}) - - pi := make([]byte, len(preimage)) - copy(pi, preimage) - - csdb.preimages = append(csdb.preimages, preimageEntry{hash: hash, preimage: pi}) - csdb.hashToPreimageIndex[hash] = len(csdb.preimages) - 1 - } -} - -// AddRefund adds gas to the refund counter. -func (csdb *CommitStateDB) AddRefund(gas uint64) { - csdb.journal.append(refundChange{prev: csdb.refund}) - csdb.refund += gas -} - -// SubRefund removes gas from the refund counter. It will panic if the refund -// counter goes below zero. -func (csdb *CommitStateDB) SubRefund(gas uint64) { - csdb.journal.append(refundChange{prev: csdb.refund}) - if gas > csdb.refund { - panic("refund counter below zero") - } - - csdb.refund -= gas -} - -// ---------------------------------------------------------------------------- -// Access List // TODO: deprecate -// ---------------------------------------------------------------------------- - -// PrepareAccessList handles the preparatory steps for executing a state transition with -// regards to both EIP-2929 and EIP-2930: -// -// - Add sender to access list (2929) -// - Add destination to access list (2929) -// - Add precompiles to access list (2929) -// - Add the contents of the optional tx access list (2930) -// -// This method should only be called if Yolov3/Berlin/2929+2930 is applicable at the current number. -func (csdb *CommitStateDB) PrepareAccessList(sender ethcmn.Address, dest *ethcmn.Address, precompiles []ethcmn.Address, txAccesses ethtypes.AccessList) { - csdb.AddAddressToAccessList(sender) - if dest != nil { - csdb.AddAddressToAccessList(*dest) - // If it's a create-tx, the destination will be added inside evm.create - } - for _, addr := range precompiles { - csdb.AddAddressToAccessList(addr) - } - for _, tuple := range txAccesses { - csdb.AddAddressToAccessList(tuple.Address) - for _, key := range tuple.StorageKeys { - csdb.AddSlotToAccessList(tuple.Address, key) - } - } -} - -// AddressInAccessList returns true if the address is registered on the access list map. -func (csdb *CommitStateDB) AddressInAccessList(addr ethcmn.Address) bool { - ts := prefix.NewStore(csdb.ctx.TransientStore(csdb.transientKey), KeyPrefixTransientAccessListAddress) - return ts.Has(addr.Bytes()) -} - -func (csdb *CommitStateDB) SlotInAccessList(addr ethcmn.Address, slot ethcmn.Hash) (addressOk bool, slotOk bool) { - addressOk = csdb.AddressInAccessList(addr) - slotOk = csdb.addressSlotInAccessList(addr, slot) - return addressOk, slotOk -} - -func (csdb *CommitStateDB) addressSlotInAccessList(addr ethcmn.Address, slot ethcmn.Hash) bool { - ts := prefix.NewStore(csdb.ctx.TransientStore(csdb.transientKey), KeyPrefixTransientAccessListSlot) - key := append(addr.Bytes(), slot.Bytes()...) - return ts.Has(key) -} - -// AddAddressToAccessList adds the given address to the access list. This operation is safe to perform -// even if the feature/fork is not active yet -func (csdb *CommitStateDB) AddAddressToAccessList(addr ethcmn.Address) { - if csdb.AddressInAccessList(addr) { - return - } - - ts := prefix.NewStore(csdb.ctx.TransientStore(csdb.transientKey), KeyPrefixTransientAccessListAddress) - ts.Set(addr.Bytes(), []byte{0x1}) -} - -// AddSlotToAccessList adds the given (address,slot) to the access list. This operation is safe to perform -// even if the feature/fork is not active yet -func (csdb *CommitStateDB) AddSlotToAccessList(addr ethcmn.Address, slot ethcmn.Hash) { - csdb.AddAddressToAccessList(addr) - if csdb.addressSlotInAccessList(addr, slot) { - return - } - - ts := prefix.NewStore(csdb.ctx.TransientStore(csdb.transientKey), KeyPrefixTransientAccessListSlot) - key := append(addr.Bytes(), slot.Bytes()...) - ts.Set(key, []byte{0x1}) -} - -// ---------------------------------------------------------------------------- -// Getters -// ---------------------------------------------------------------------------- - -// GetHeightHash returns the block header hash associated with a given block height and chain epoch number. -func (csdb *CommitStateDB) GetHeightHash(height uint64) ethcmn.Hash { - store := prefix.NewStore(csdb.ctx.KVStore(csdb.storeKey), KeyPrefixHeightToHeaderHash) - key := sdk.Uint64ToBigEndian(height) - bz := store.Get(key) - if len(bz) == 0 { - return ethcmn.Hash{} - } - - return ethcmn.BytesToHash(bz) -} - -// GetParams returns the total set of evm parameters. -func (csdb *CommitStateDB) GetParams() (params Params) { - csdb.paramSpace.GetParamSet(csdb.ctx, ¶ms) - return params -} - -// GetBalance retrieves the balance from the given address or 0 if object not -// found. -func (csdb *CommitStateDB) GetBalance(addr ethcmn.Address) *big.Int { - so := csdb.getStateObject(addr) - if so != nil { - return so.Balance() - } - - return zeroBalance -} - -// GetNonce returns the nonce (sequence number) for a given account. -func (csdb *CommitStateDB) GetNonce(addr ethcmn.Address) uint64 { - so := csdb.getStateObject(addr) - if so != nil { - return so.Nonce() - } - - return 0 -} - -// TxIndex returns the current transaction index set by Prepare. -func (csdb *CommitStateDB) TxIndex() int { - return csdb.txIndex -} - -// BlockHash returns the current block hash set by Prepare. -func (csdb *CommitStateDB) BlockHash() ethcmn.Hash { - return csdb.bhash -} - -// GetCode returns the code for a given account. -func (csdb *CommitStateDB) GetCode(addr ethcmn.Address) []byte { - so := csdb.getStateObject(addr) - if so != nil { - return so.Code(nil) - } - - return nil -} - -// GetCodeSize returns the code size for a given account. -func (csdb *CommitStateDB) GetCodeSize(addr ethcmn.Address) int { - so := csdb.getStateObject(addr) - if so == nil { - return 0 - } - - if so.code != nil { - return len(so.code) - } - - return len(so.Code(nil)) -} - -// GetCodeHash returns the code hash for a given account. -func (csdb *CommitStateDB) GetCodeHash(addr ethcmn.Address) ethcmn.Hash { - so := csdb.getStateObject(addr) - if so == nil { - return ethcmn.Hash{} - } - - return ethcmn.BytesToHash(so.CodeHash()) -} - -// GetState retrieves a value from the given account's storage store. -func (csdb *CommitStateDB) GetState(addr ethcmn.Address, hash ethcmn.Hash) ethcmn.Hash { - so := csdb.getStateObject(addr) - if so != nil { - return so.GetState(nil, hash) - } - - return ethcmn.Hash{} -} - -// GetCommittedState retrieves a value from the given account's committed -// storage. -func (csdb *CommitStateDB) GetCommittedState(addr ethcmn.Address, hash ethcmn.Hash) ethcmn.Hash { - so := csdb.getStateObject(addr) - if so != nil { - return so.GetCommittedState(nil, hash) - } - - return ethcmn.Hash{} -} - -// GetLogs returns the current logs for a given transaction hash from the KVStore. -func (csdb *CommitStateDB) GetLogs(hash ethcmn.Hash) ([]*ethtypes.Log, error) { - store := prefix.NewStore(csdb.ctx.KVStore(csdb.storeKey), KeyPrefixLogs) - bz := store.Get(hash.Bytes()) - if len(bz) == 0 { - // return nil error if logs are not found - return []*ethtypes.Log{}, nil - } - - var txLogs TransactionLogs - if err := ModuleCdc.UnmarshalBinaryBare(bz, &txLogs); err != nil { - return []*ethtypes.Log{}, err - } - - allLogs := []*ethtypes.Log{} - for _, txLog := range txLogs.Logs { - allLogs = append(allLogs, txLog.ToEthereum()) - } - - return allLogs, nil -} - -// AllLogs returns all the current logs in the state. -func (csdb *CommitStateDB) AllLogs() []*ethtypes.Log { - store := csdb.ctx.KVStore(csdb.storeKey) - iterator := sdk.KVStorePrefixIterator(store, KeyPrefixLogs) - defer iterator.Close() - - allLogs := []*ethtypes.Log{} - for ; iterator.Valid(); iterator.Next() { - var txLogs TransactionLogs - ModuleCdc.MustUnmarshalBinaryBare(iterator.Value(), &txLogs) - - for _, txLog := range txLogs.Logs { - allLogs = append(allLogs, txLog.ToEthereum()) - } - } - - return allLogs -} - -// GetRefund returns the current value of the refund counter. -func (csdb *CommitStateDB) GetRefund() uint64 { - return csdb.refund -} - -// Preimages returns a list of SHA3 preimages that have been submitted. -func (csdb *CommitStateDB) Preimages() map[ethcmn.Hash][]byte { - preimages := map[ethcmn.Hash][]byte{} - - for _, pe := range csdb.preimages { - preimages[pe.hash] = pe.preimage - } - return preimages -} - -// HasSuicided returns if the given account for the specified address has been -// killed. -func (csdb *CommitStateDB) HasSuicided(addr ethcmn.Address) bool { - so := csdb.getStateObject(addr) - if so != nil { - return so.suicided - } - - return false -} - -// StorageTrie returns nil as the state in Ethermint does not use a direct -// storage trie. -func (csdb *CommitStateDB) StorageTrie(addr ethcmn.Address) ethstate.Trie { - return nil -} - -// ---------------------------------------------------------------------------- -// Persistence -// ---------------------------------------------------------------------------- - -// Commit writes the state to the appropriate KVStores. For each state object -// in the cache, it will either be removed, or have it's code set and/or it's -// state (storage) updated. In addition, the state object (account) itself will -// be written. Finally, the root hash (version) will be returned. -func (csdb *CommitStateDB) Commit(deleteEmptyObjects bool) (ethcmn.Hash, error) { - defer csdb.clearJournalAndRefund() - - // remove dirty state object entries based on the journal - for _, dirty := range csdb.journal.dirties { - csdb.stateObjectsDirty[dirty.address] = struct{}{} - } - - // set the state objects - for _, stateEntry := range csdb.stateObjects { - _, isDirty := csdb.stateObjectsDirty[stateEntry.address] - - switch { - case stateEntry.stateObject.suicided || (isDirty && deleteEmptyObjects && stateEntry.stateObject.empty()): - // If the state object has been removed, don't bother syncing it and just - // remove it from the store. - csdb.deleteStateObject(stateEntry.stateObject) - - case isDirty: - // write any contract code associated with the state object - if stateEntry.stateObject.code != nil && stateEntry.stateObject.dirtyCode { - stateEntry.stateObject.commitCode() - stateEntry.stateObject.dirtyCode = false - } - - // update the object in the KVStore - if err := csdb.updateStateObject(stateEntry.stateObject); err != nil { - return ethcmn.Hash{}, err - } - } - - delete(csdb.stateObjectsDirty, stateEntry.address) - } - - // NOTE: Ethereum returns the trie merkle root here, but as commitment - // actually happens in the BaseApp at EndBlocker, we do not know the root at - // this time. - return ethcmn.Hash{}, nil -} - -// Finalise finalizes the state objects (accounts) state by setting their state, -// removing the csdb destructed objects and clearing the journal as well as the -// refunds. -func (csdb *CommitStateDB) Finalise(deleteEmptyObjects bool) error { - for _, dirty := range csdb.journal.dirties { - idx, exist := csdb.addressToObjectIndex[dirty.address] - if !exist { - // ripeMD is 'touched' at block 1714175, in tx: - // 0x1237f737031e40bcde4a8b7e717b2d15e3ecadfe49bb1bbc71ee9deb09c6fcf2 - // - // That tx goes out of gas, and although the notion of 'touched' does not - // exist there, the touch-event will still be recorded in the journal. - // Since ripeMD is a special snowflake, it will persist in the journal even - // though the journal is reverted. In this special circumstance, it may - // exist in journal.dirties but not in stateObjects. Thus, we can safely - // ignore it here. - continue - } - - stateEntry := csdb.stateObjects[idx] - if stateEntry.stateObject.suicided || (deleteEmptyObjects && stateEntry.stateObject.empty()) { - csdb.deleteStateObject(stateEntry.stateObject) - } else { - // Set all the dirty state storage items for the state object in the - // KVStore and finally set the account in the account mapper. - stateEntry.stateObject.commitState() - if err := csdb.updateStateObject(stateEntry.stateObject); err != nil { - return err - } - } - - csdb.stateObjectsDirty[dirty.address] = struct{}{} - } - - // invalidate journal because reverting across transactions is not allowed - csdb.clearJournalAndRefund() - return nil -} - -// IntermediateRoot returns the current root hash of the state. It is called in -// between transactions to get the root hash that goes into transaction -// receipts. -// -// NOTE: The SDK has not concept or method of getting any intermediate merkle -// root as commitment of the merkle-ized tree doesn't happen until the -// BaseApps' EndBlocker. -func (csdb *CommitStateDB) IntermediateRoot(deleteEmptyObjects bool) (ethcmn.Hash, error) { - if err := csdb.Finalise(deleteEmptyObjects); err != nil { - return ethcmn.Hash{}, err - } - - return ethcmn.Hash{}, nil -} - -// updateStateObject writes the given state object to the store. -func (csdb *CommitStateDB) updateStateObject(so *stateObject) error { - evmDenom := csdb.GetParams().EvmDenom - // NOTE: we don't use sdk.NewCoin here to avoid panic on test importer's genesis - newBalance := sdk.Coin{Denom: evmDenom, Amount: sdk.NewIntFromBigInt(so.Balance())} - if !newBalance.IsValid() { - return fmt.Errorf("invalid balance %s", newBalance) - } - - err := csdb.bankKeeper.SetBalance(csdb.ctx, so.account.GetAddress(), newBalance) - if err != nil { - return err - } - csdb.accountKeeper.SetAccount(csdb.ctx, so.account) - return nil -} - -// deleteStateObject removes the given state object from the state store. -func (csdb *CommitStateDB) deleteStateObject(so *stateObject) { - so.deleted = true - csdb.accountKeeper.RemoveAccount(csdb.ctx, so.account) -} - -// ---------------------------------------------------------------------------- -// Snapshotting -// ---------------------------------------------------------------------------- - -// Snapshot returns an identifier for the current revision of the state. -func (csdb *CommitStateDB) Snapshot() int { - id := csdb.nextRevisionID - csdb.nextRevisionID++ - - csdb.validRevisions = append( - csdb.validRevisions, - revision{ - id: id, - journalIndex: csdb.journal.length(), - }, - ) - - return id -} - -// RevertToSnapshot reverts all state changes made since the given revision. -func (csdb *CommitStateDB) RevertToSnapshot(revID int) { - // find the snapshot in the stack of valid snapshots - idx := sort.Search(len(csdb.validRevisions), func(i int) bool { - return csdb.validRevisions[i].id >= revID - }) - - if idx == len(csdb.validRevisions) || csdb.validRevisions[idx].id != revID { - panic(fmt.Errorf("revision ID %v cannot be reverted", revID)) - } - - snapshot := csdb.validRevisions[idx].journalIndex - - // replay the journal to undo changes and remove invalidated snapshots - csdb.journal.revert(csdb, snapshot) - csdb.validRevisions = csdb.validRevisions[:idx] -} - -// ---------------------------------------------------------------------------- -// Auxiliary -// ---------------------------------------------------------------------------- - -// Database retrieves the low level database supporting the lower level trie -// ops. It is not used in Ethermint, so it returns nil. -func (csdb *CommitStateDB) Database() ethstate.Database { - return nil -} - -// Empty returns whether the state object is either non-existent or empty -// according to the EIP161 specification (balance = nonce = code = 0). -func (csdb *CommitStateDB) Empty(addr ethcmn.Address) bool { - so := csdb.getStateObject(addr) - return so == nil || so.empty() -} - -// Exist reports whether the given account address exists in the state. Notably, -// this also returns true for suicided accounts. -func (csdb *CommitStateDB) Exist(addr ethcmn.Address) bool { - return csdb.getStateObject(addr) != nil -} - -// Error returns the first non-nil error the StateDB encountered. -func (csdb *CommitStateDB) Error() error { - return csdb.dbErr -} - -// Suicide marks the given account as suicided and clears the account balance. -// -// The account's state object is still available until the state is committed, -// getStateObject will return a non-nil account after Suicide. -func (csdb *CommitStateDB) Suicide(addr ethcmn.Address) bool { - so := csdb.getStateObject(addr) - if so == nil { - return false - } - - csdb.journal.append(suicideChange{ - account: &addr, - prev: so.suicided, - prevBalance: sdk.NewIntFromBigInt(so.Balance()), - }) - - so.markSuicided() - so.SetBalance(new(big.Int)) - - return true -} - -// Reset clears out all ephemeral state objects from the state db, but keeps -// the underlying account mapper and store keys to avoid reloading data for the -// next operations. -func (csdb *CommitStateDB) Reset(_ ethcmn.Hash) error { - csdb.stateObjects = []stateEntry{} - csdb.addressToObjectIndex = make(map[ethcmn.Address]int) - csdb.stateObjectsDirty = make(map[ethcmn.Address]struct{}) - csdb.thash = ethcmn.Hash{} - csdb.bhash = ethcmn.Hash{} - csdb.txIndex = 0 - csdb.logSize = 0 - csdb.preimages = []preimageEntry{} - csdb.hashToPreimageIndex = make(map[ethcmn.Hash]int) - - csdb.clearJournalAndRefund() - return nil -} - -// UpdateAccounts updates the nonce and coin balances of accounts -func (csdb *CommitStateDB) UpdateAccounts() { - for _, stateEntry := range csdb.stateObjects { - address := sdk.AccAddress(stateEntry.address.Bytes()) - currAccount := csdb.accountKeeper.GetAccount(csdb.ctx, address) - ethAcc, ok := currAccount.(*ethermint.EthAccount) - if !ok { - continue - } - - evmDenom := csdb.GetParams().EvmDenom - balance := csdb.bankKeeper.GetBalance(csdb.ctx, address, evmDenom) - - if stateEntry.stateObject.Balance() != balance.Amount.BigInt() && balance.IsValid() { - stateEntry.stateObject.balance = balance.Amount - } - - if stateEntry.stateObject.Nonce() != ethAcc.GetSequence() { - stateEntry.stateObject.account = ethAcc - } - } -} - -// ClearStateObjects clears cache of state objects to handle account changes outside of the EVM -func (csdb *CommitStateDB) ClearStateObjects() { - csdb.stateObjects = []stateEntry{} - csdb.addressToObjectIndex = make(map[ethcmn.Address]int) - csdb.stateObjectsDirty = make(map[ethcmn.Address]struct{}) -} - -func (csdb *CommitStateDB) clearJournalAndRefund() { - csdb.journal = newJournal() - csdb.validRevisions = csdb.validRevisions[:0] - csdb.refund = 0 -} - -// Prepare sets the current transaction hash and index and block hash which is -// used when the EVM emits new state logs. -func (csdb *CommitStateDB) Prepare(thash, bhash ethcmn.Hash, txi int) { - csdb.thash = thash - csdb.bhash = bhash - csdb.txIndex = txi -} - -// CreateAccount explicitly creates a state object. If a state object with the -// address already exists the balance is carried over to the new account. -// -// CreateAccount is called during the EVM CREATE operation. The situation might -// arise that a contract does the following: -// -// 1. sends funds to sha(account ++ (nonce + 1)) -// 2. tx_create(sha(account ++ nonce)) (note that this gets the address of 1) -// -// Carrying over the balance ensures that Ether doesn't disappear. -func (csdb *CommitStateDB) CreateAccount(addr ethcmn.Address) { - newobj, prevobj := csdb.createObject(addr) - if prevobj != nil { - newobj.setBalance(sdk.NewIntFromBigInt(prevobj.Balance())) - } -} - -// Copy creates a deep, independent copy of the state. -// -// NOTE: Snapshots of the copied state cannot be applied to the copy. -func (csdb *CommitStateDB) Copy() *CommitStateDB { - - // copy all the basic fields, initialize the memory ones - state := &CommitStateDB{} - CopyCommitStateDB(csdb, state) - - return state -} - -func CopyCommitStateDB(from, to *CommitStateDB) { - from.lock.Lock() - defer from.lock.Unlock() - - to.ctx = from.ctx - to.storeKey = from.storeKey - to.paramSpace = from.paramSpace - to.accountKeeper = from.accountKeeper - to.bankKeeper = from.bankKeeper - to.stateObjects = []stateEntry{} - to.addressToObjectIndex = make(map[ethcmn.Address]int) - to.stateObjectsDirty = make(map[ethcmn.Address]struct{}) - to.refund = from.refund - to.logSize = from.logSize - to.preimages = make([]preimageEntry, len(from.preimages)) - to.hashToPreimageIndex = make(map[ethcmn.Hash]int, len(from.hashToPreimageIndex)) - to.journal = newJournal() - to.thash = from.thash - to.bhash = from.bhash - to.txIndex = from.txIndex - validRevisions := make([]revision, len(from.validRevisions)) - copy(validRevisions, from.validRevisions) - to.validRevisions = validRevisions - to.nextRevisionID = from.nextRevisionID - - // copy the dirty states, logs, and preimages - for _, dirty := range from.journal.dirties { - // There is a case where an object is in the journal but not in the - // stateObjects: OOG after touch on ripeMD prior to Byzantium. Thus, we - // need to check for nil. - // - // Ref: https://github.com/ethereum/go-ethereum/pull/16485#issuecomment-380438527 - if idx, exist := from.addressToObjectIndex[dirty.address]; exist { - to.stateObjects = append(to.stateObjects, stateEntry{ - address: dirty.address, - stateObject: from.stateObjects[idx].stateObject.deepCopy(to), - }) - to.addressToObjectIndex[dirty.address] = len(to.stateObjects) - 1 - to.stateObjectsDirty[dirty.address] = struct{}{} - } - } - - // Above, we don't copy the actual journal. This means that if the copy is - // copied, the loop above will be a no-op, since the copy's journal is empty. - // Thus, here we iterate over stateObjects, to enable copies of copies. - for addr := range from.stateObjectsDirty { - if idx, exist := to.addressToObjectIndex[addr]; !exist { - to.setStateObject(from.stateObjects[idx].stateObject.deepCopy(to)) - to.stateObjectsDirty[addr] = struct{}{} - } - } - - // copy pre-images - for i, preimageEntry := range from.preimages { - to.preimages[i] = preimageEntry - to.hashToPreimageIndex[preimageEntry.hash] = i - } -} - -// ForEachStorage iterates over each storage items, all invoke the provided -// callback on each key, value pair. -func (csdb *CommitStateDB) ForEachStorage(addr ethcmn.Address, cb func(key, value ethcmn.Hash) (stop bool)) error { - so := csdb.getStateObject(addr) - if so == nil { - return nil - } - - store := csdb.ctx.KVStore(csdb.storeKey) - prefix := AddressStoragePrefix(so.Address()) - iterator := sdk.KVStorePrefixIterator(store, prefix) - defer iterator.Close() - - for ; iterator.Valid(); iterator.Next() { - key := ethcmn.BytesToHash(iterator.Key()) - value := ethcmn.BytesToHash(iterator.Value()) - - if idx, dirty := so.keyToDirtyStorageIndex[key]; dirty { - // check if iteration stops - if cb(key, ethcmn.HexToHash(so.dirtyStorage[idx].Value)) { - break - } - - continue - } - - // check if iteration stops - if cb(key, value) { - return nil - } - } - - return nil -} - -// GetOrNewStateObject retrieves a state object or create a new state object if -// nil. -func (csdb *CommitStateDB) GetOrNewStateObject(addr ethcmn.Address) StateObject { - so := csdb.getStateObject(addr) - if so == nil || so.deleted { - so, _ = csdb.createObject(addr) - } - - return so -} - -// createObject creates a new state object. If there is an existing account with -// the given address, it is overwritten and returned as the second return value. -func (csdb *CommitStateDB) createObject(addr ethcmn.Address) (newObj, prevObj *stateObject) { - prevObj = csdb.getStateObject(addr) - - acc := csdb.accountKeeper.NewAccountWithAddress(csdb.ctx, sdk.AccAddress(addr.Bytes())) - - newObj = newStateObject(csdb, acc, sdk.ZeroInt()) - newObj.setNonce(0) // sets the object to dirty - - if prevObj == nil { - csdb.journal.append(createObjectChange{account: &addr}) - } else { - csdb.journal.append(resetObjectChange{prev: prevObj}) - } - - csdb.setStateObject(newObj) - return newObj, prevObj -} - -// setError remembers the first non-nil error it is called with. -func (csdb *CommitStateDB) setError(err error) { - if csdb.dbErr == nil { - csdb.dbErr = err - } -} - -// getStateObject attempts to retrieve a state object given by the address. -// Returns nil and sets an error if not found. -func (csdb *CommitStateDB) getStateObject(addr ethcmn.Address) (stateObject *stateObject) { - if idx, found := csdb.addressToObjectIndex[addr]; found { - // prefer 'live' (cached) objects - if so := csdb.stateObjects[idx].stateObject; so != nil { - if so.deleted { - return nil - } - - return so - } - } - - // otherwise, attempt to fetch the account from the account mapper - acc := csdb.accountKeeper.GetAccount(csdb.ctx, sdk.AccAddress(addr.Bytes())) - if acc == nil { - csdb.setError(fmt.Errorf("no account found for address: %s", addr.String())) - return nil - } - - evmDenom := csdb.GetParams().EvmDenom - balance := csdb.bankKeeper.GetBalance(csdb.ctx, acc.GetAddress(), evmDenom) - - // insert the state object into the live set - so := newStateObject(csdb, acc, balance.Amount) - csdb.setStateObject(so) - - return so -} - -func (csdb *CommitStateDB) setStateObject(so *stateObject) { - if idx, found := csdb.addressToObjectIndex[so.Address()]; found { - // update the existing object - csdb.stateObjects[idx].stateObject = so - return - } - - // append the new state object to the stateObjects slice - se := stateEntry{ - address: so.Address(), - stateObject: so, - } - - csdb.stateObjects = append(csdb.stateObjects, se) - csdb.addressToObjectIndex[se.address] = len(csdb.stateObjects) - 1 -} - -// RawDump returns a raw state dump. -// -// TODO: Implement if we need it, especially for the RPC API. -func (csdb *CommitStateDB) RawDump() ethstate.Dump { - return ethstate.Dump{} -} - -type preimageEntry struct { - // hash key of the preimage entry - hash ethcmn.Hash - preimage []byte -} diff --git a/x/evm/types/statedb_test.go b/x/evm/types/statedb_test.go deleted file mode 100644 index 41a250a3..00000000 --- a/x/evm/types/statedb_test.go +++ /dev/null @@ -1,739 +0,0 @@ -package types_test - -import ( - "fmt" - "math/big" - "testing" - - "github.com/stretchr/testify/suite" - - sdk "github.com/cosmos/cosmos-sdk/types" - authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - - 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/app" - "github.com/cosmos/ethermint/crypto/ethsecp256k1" - ethermint "github.com/cosmos/ethermint/types" - "github.com/cosmos/ethermint/x/evm/types" - - tmproto "github.com/tendermint/tendermint/proto/tendermint/types" -) - -type StateDBTestSuite struct { - suite.Suite - - ctx sdk.Context - app *app.EthermintApp - stateDB *types.CommitStateDB - address ethcmn.Address - stateObject types.StateObject -} - -func TestStateDBTestSuite(t *testing.T) { - suite.Run(t, new(StateDBTestSuite)) -} - -func (suite *StateDBTestSuite) SetupTest() { - checkTx := false - - suite.app = app.Setup(checkTx) - suite.ctx = suite.app.BaseApp.NewContext(checkTx, tmproto.Header{Height: 1}) - suite.stateDB = suite.app.EvmKeeper.CommitStateDB.WithContext(suite.ctx) - - privkey, err := ethsecp256k1.GenerateKey() - suite.Require().NoError(err) - - suite.address = ethcmn.BytesToAddress(privkey.PubKey().Address().Bytes()) - - balance := ethermint.NewPhotonCoin(sdk.ZeroInt()) - acc := ðermint.EthAccount{ - BaseAccount: authtypes.NewBaseAccount(sdk.AccAddress(suite.address.Bytes()), nil, 0, 0), - CodeHash: ethcrypto.Keccak256(nil), - } - - suite.app.AccountKeeper.SetAccount(suite.ctx, acc) - err = suite.app.BankKeeper.SetBalance(suite.ctx, acc.GetAddress(), balance) - suite.Require().NoError(err) - - suite.stateObject = suite.stateDB.GetOrNewStateObject(suite.address) -} - -func (suite *StateDBTestSuite) TestParams() { - params := suite.stateDB.GetParams() - suite.Require().Equal(types.DefaultParams(), params) - params.EvmDenom = "inj" - suite.stateDB.SetParams(params) - newParams := suite.stateDB.GetParams() - suite.Require().Equal(newParams, params) -} - -func (suite *StateDBTestSuite) TestBloomFilter() { - // Prepare db for logs - tHash := ethcmn.BytesToHash([]byte{0x1}) - suite.stateDB.Prepare(tHash, ethcmn.Hash{}, 0) - contractAddress := ethcmn.BigToAddress(big.NewInt(1)) - log := ethtypes.Log{Address: contractAddress} - - testCase := []struct { - name string - malleate func() - numLogs int - isBloom bool - }{ - { - "no logs", - func() {}, - 0, - false, - }, - { - "add log", - func() { - suite.stateDB.AddLog(&log) - }, - 1, - false, - }, - { - "bloom", - func() {}, - 0, - true, - }, - } - - for _, tc := range testCase { - tc.malleate() - logs, err := suite.stateDB.GetLogs(tHash) - if !tc.isBloom { - suite.Require().NoError(err, tc.name) - suite.Require().Len(logs, tc.numLogs, tc.name) - if len(logs) != 0 { - suite.Require().Equal(log, *logs[0], tc.name) - } - } else { - // get logs bloom from the log - bloomInt := ethtypes.LogsBloom(logs) - bloomFilter := ethtypes.BytesToBloom(bloomInt) - suite.Require().True(ethtypes.BloomLookup(bloomFilter, contractAddress), tc.name) - suite.Require().False(ethtypes.BloomLookup(bloomFilter, ethcmn.BigToAddress(big.NewInt(2))), tc.name) - } - } -} - -func (suite *StateDBTestSuite) TestStateDB_Balance() { - testCase := []struct { - name string - malleate func() - balance *big.Int - }{ - { - "set balance", - func() { - suite.stateDB.SetBalance(suite.address, big.NewInt(100)) - }, - big.NewInt(100), - }, - { - "sub balance", - func() { - suite.stateDB.SubBalance(suite.address, big.NewInt(100)) - }, - big.NewInt(0), - }, - { - "add balance", - func() { - suite.stateDB.AddBalance(suite.address, big.NewInt(200)) - }, - big.NewInt(200), - }, - { - "sub more than balance", - func() { - suite.stateDB.SubBalance(suite.address, big.NewInt(300)) - }, - big.NewInt(-100), - }, - } - - for _, tc := range testCase { - tc.malleate() - suite.Require().Equal(tc.balance, suite.stateDB.GetBalance(suite.address), tc.name) - } -} - -func (suite *StateDBTestSuite) TestStateDBNonce() { - nonce := uint64(123) - suite.stateDB.SetNonce(suite.address, nonce) - suite.Require().Equal(nonce, suite.stateDB.GetNonce(suite.address)) -} - -func (suite *StateDBTestSuite) TestStateDB_Error() { - nonce := suite.stateDB.GetNonce(ethcmn.Address{}) - suite.Require().Equal(0, int(nonce)) - suite.Require().Error(suite.stateDB.Error()) -} - -func (suite *StateDBTestSuite) TestStateDB_Database() { - suite.Require().Nil(suite.stateDB.Database()) -} - -func (suite *StateDBTestSuite) TestStateDB_State() { - key := ethcmn.BytesToHash([]byte("foo")) - val := ethcmn.BytesToHash([]byte("bar")) - suite.stateDB.SetState(suite.address, key, val) - - testCase := []struct { - name string - address ethcmn.Address - key ethcmn.Hash - value ethcmn.Hash - }{ - { - "found state", - suite.address, - ethcmn.BytesToHash([]byte("foo")), - ethcmn.BytesToHash([]byte("bar")), - }, - { - "state not found", - suite.address, - ethcmn.BytesToHash([]byte("key")), - ethcmn.Hash{}, - }, - { - "object not found", - ethcmn.Address{}, - ethcmn.BytesToHash([]byte("foo")), - ethcmn.Hash{}, - }, - } - for _, tc := range testCase { - value := suite.stateDB.GetState(tc.address, tc.key) - suite.Require().Equal(tc.value, value, tc.name) - } -} - -func (suite *StateDBTestSuite) TestStateDB_Code() { - testCase := []struct { - name string - address ethcmn.Address - code []byte - malleate func() - }{ - { - "no stored code for state object", - suite.address, - nil, - func() {}, - }, - { - "existing address", - suite.address, - []byte("code"), - func() { - suite.stateDB.SetCode(suite.address, []byte("code")) - }, - }, - { - "state object not found", - ethcmn.Address{}, - nil, - func() {}, - }, - } - - for _, tc := range testCase { - tc.malleate() - - suite.Require().Equal(tc.code, suite.stateDB.GetCode(tc.address), tc.name) - suite.Require().Equal(len(tc.code), suite.stateDB.GetCodeSize(tc.address), tc.name) - } -} - -func (suite *StateDBTestSuite) TestStateDB_Logs() { - testCase := []struct { - name string - log ethtypes.Log - }{ - { - "state db log", - ethtypes.Log{ - Address: suite.address, - Topics: []ethcmn.Hash{ethcmn.BytesToHash([]byte("topic"))}, - Data: []byte("data"), - BlockNumber: 1, - TxHash: ethcmn.Hash{}, - TxIndex: 1, - BlockHash: ethcmn.Hash{}, - Index: 0, - Removed: false, - }, - }, - } - - for _, tc := range testCase { - hash := ethcmn.BytesToHash([]byte("hash")) - logs := []*ethtypes.Log{&tc.log} - - err := suite.stateDB.SetLogs(hash, logs) - suite.Require().NoError(err, tc.name) - dbLogs, err := suite.stateDB.GetLogs(hash) - suite.Require().NoError(err, tc.name) - suite.Require().Equal(logs, dbLogs, tc.name) - - suite.stateDB.DeleteLogs(hash) - dbLogs, err = suite.stateDB.GetLogs(hash) - suite.Require().NoError(err, tc.name) - suite.Require().Empty(dbLogs, tc.name) - - suite.stateDB.AddLog(&tc.log) - suite.Require().Equal(logs, suite.stateDB.AllLogs(), tc.name) - - //resets state but checking to see if storekey still persists. - err = suite.stateDB.Reset(hash) - suite.Require().NoError(err, tc.name) - suite.Require().Equal(logs, suite.stateDB.AllLogs(), tc.name) - } -} - -func (suite *StateDBTestSuite) TestStateDB_Preimage() { - hash := ethcmn.BytesToHash([]byte("hash")) - preimage := []byte("preimage") - - suite.stateDB.AddPreimage(hash, preimage) - suite.Require().Equal(preimage, suite.stateDB.Preimages()[hash]) -} - -func (suite *StateDBTestSuite) TestStateDB_Refund() { - testCase := []struct { - name string - addAmount uint64 - subAmount uint64 - expRefund uint64 - expPanic bool - }{ - { - "refund 0", - 0, 0, 0, - false, - }, - { - "refund positive amount", - 100, 0, 100, - false, - }, - { - "refund panic", - 100, 200, 100, - true, - }, - } - - for _, tc := range testCase { - suite.Run(tc.name, func() { - suite.SetupTest() // reset - - suite.stateDB.AddRefund(tc.addAmount) - suite.Require().Equal(tc.addAmount, suite.stateDB.GetRefund()) - - if tc.expPanic { - suite.Panics(func() { - suite.stateDB.SubRefund(tc.subAmount) - }) - } else { - suite.stateDB.SubRefund(tc.subAmount) - suite.Require().Equal(tc.expRefund, suite.stateDB.GetRefund()) - } - }) - } -} - -func (suite *StateDBTestSuite) TestStateDB_CreateAccount() { - prevBalance := big.NewInt(12) - - testCase := []struct { - name string - address ethcmn.Address - malleate func() - }{ - { - "existing account", - suite.address, - func() { - suite.stateDB.AddBalance(suite.address, prevBalance) - }, - }, - { - "new account", - ethcmn.HexToAddress("0x756F45E3FA69347A9A973A725E3C98bC4db0b4c1"), - func() { - prevBalance = big.NewInt(0) - }, - }, - } - - for _, tc := range testCase { - tc.malleate() - - suite.stateDB.CreateAccount(tc.address) - suite.Require().True(suite.stateDB.Exist(tc.address), tc.name) - suite.Require().Equal(prevBalance, suite.stateDB.GetBalance(tc.address), tc.name) - } -} - -func (suite *StateDBTestSuite) TestStateDB_ClearStateObj() { - priv, err := ethsecp256k1.GenerateKey() - suite.Require().NoError(err) - - addr := ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey) - - suite.stateDB.CreateAccount(addr) - suite.Require().True(suite.stateDB.Exist(addr)) - - suite.stateDB.ClearStateObjects() - suite.Require().False(suite.stateDB.Exist(addr)) -} - -func (suite *StateDBTestSuite) TestStateDB_Reset() { - priv, err := ethsecp256k1.GenerateKey() - suite.Require().NoError(err) - - addr := ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey) - - suite.stateDB.CreateAccount(addr) - suite.Require().True(suite.stateDB.Exist(addr)) - - err = suite.stateDB.Reset(ethcmn.BytesToHash(nil)) - suite.Require().NoError(err) - suite.Require().False(suite.stateDB.Exist(addr)) -} - -func (suite *StateDBTestSuite) TestSuiteDB_Prepare() { - thash := ethcmn.BytesToHash([]byte("thash")) - bhash := ethcmn.BytesToHash([]byte("bhash")) - txi := 1 - - suite.stateDB.Prepare(thash, bhash, txi) - - suite.Require().Equal(txi, suite.stateDB.TxIndex()) - suite.Require().Equal(bhash, suite.stateDB.BlockHash()) -} - -func (suite *StateDBTestSuite) TestSuiteDB_CopyState() { - testCase := []struct { - name string - log ethtypes.Log - }{ - { - "copy state", - ethtypes.Log{ - Address: suite.address, - Topics: []ethcmn.Hash{ethcmn.BytesToHash([]byte("topic"))}, - Data: []byte("data"), - BlockNumber: 1, - TxHash: ethcmn.Hash{}, - TxIndex: 1, - BlockHash: ethcmn.Hash{}, - Index: 0, - Removed: false, - }, - }, - } - - for _, tc := range testCase { - hash := ethcmn.BytesToHash([]byte("hash")) - logs := []*ethtypes.Log{&tc.log} - - err := suite.stateDB.SetLogs(hash, logs) - suite.Require().NoError(err, tc.name) - - copyDB := suite.stateDB.Copy() - - copiedDBLogs, err := copyDB.GetLogs(hash) - suite.Require().NoError(err, tc.name) - suite.Require().Equal(logs, copiedDBLogs, tc.name) - suite.Require().Equal(suite.stateDB.Exist(suite.address), copyDB.Exist(suite.address), tc.name) - } -} - -func (suite *StateDBTestSuite) TestSuiteDB_Empty() { - suite.Require().True(suite.stateDB.Empty(suite.address)) - - suite.stateDB.SetBalance(suite.address, big.NewInt(100)) - suite.Require().False(suite.stateDB.Empty(suite.address)) -} - -func (suite *StateDBTestSuite) TestSuiteDB_Suicide() { - testCase := []struct { - name string - amount *big.Int - expPass bool - delete bool - }{ - { - "suicide zero balance", - big.NewInt(0), - false, false, - }, - { - "suicide with balance", - big.NewInt(100), - true, false, - }, - { - "delete", - big.NewInt(0), - true, true, - }, - } - - for _, tc := range testCase { - if tc.delete { - _, err := suite.stateDB.Commit(tc.delete) - suite.Require().NoError(err, tc.name) - suite.Require().False(suite.stateDB.Exist(suite.address), tc.name) - continue - } - - if tc.expPass { - suite.stateDB.SetBalance(suite.address, tc.amount) - suicide := suite.stateDB.Suicide(suite.address) - suite.Require().True(suicide, tc.name) - suite.Require().True(suite.stateDB.HasSuicided(suite.address), tc.name) - } else { - //Suicide only works for an account with non-zero balance/nonce - priv, err := ethsecp256k1.GenerateKey() - suite.Require().NoError(err) - - addr := ethcrypto.PubkeyToAddress(priv.ToECDSA().PublicKey) - suicide := suite.stateDB.Suicide(addr) - suite.Require().False(suicide, tc.name) - suite.Require().False(suite.stateDB.HasSuicided(addr), tc.name) - } - } -} - -func (suite *StateDBTestSuite) TestCommitStateDB_Commit() { - testCase := []struct { - name string - malleate func() - deleteObjs bool - expPass bool - }{ - { - "commit suicided", - func() { - ok := suite.stateDB.Suicide(suite.address) - suite.Require().True(ok) - }, - true, true, - }, - { - "commit with dirty value", - func() { - suite.stateDB.SetCode(suite.address, []byte("code")) - }, - false, true, - }, - { - "faled to update state object", - func() { - suite.stateDB.SubBalance(suite.address, big.NewInt(10)) - }, - false, false, - }, - } - - for _, tc := range testCase { - tc.malleate() - - hash, err := suite.stateDB.Commit(tc.deleteObjs) - suite.Require().Equal(ethcmn.Hash{}, hash) - - if !tc.expPass { - suite.Require().Error(err, tc.name) - continue - } - - suite.Require().NoError(err, tc.name) - acc := suite.app.AccountKeeper.GetAccount(suite.ctx, sdk.AccAddress(suite.address.Bytes())) - - if tc.deleteObjs { - suite.Require().Nil(acc, tc.name) - continue - } - - suite.Require().NotNil(acc, tc.name) - ethAcc, ok := acc.(*ethermint.EthAccount) - suite.Require().True(ok) - suite.Require().Equal(ethcrypto.Keccak256([]byte("code")), ethAcc.CodeHash) - } -} - -func (suite *StateDBTestSuite) TestCommitStateDB_Finalize() { - testCase := []struct { - name string - malleate func() - deleteObjs bool - expPass bool - }{ - { - "finalize suicided", - func() { - ok := suite.stateDB.Suicide(suite.address) - suite.Require().True(ok) - }, - true, true, - }, - { - "finalize, not suicided", - func() { - suite.stateDB.AddBalance(suite.address, big.NewInt(5)) - }, - false, true, - }, - { - "finalize, dirty storage", - func() { - suite.stateDB.SetState(suite.address, ethcmn.BytesToHash([]byte("key")), ethcmn.BytesToHash([]byte("value"))) - }, - false, true, - }, - { - "faled to update state object", - func() { - suite.stateDB.SubBalance(suite.address, big.NewInt(10)) - }, - false, false, - }, - } - - for _, tc := range testCase { - tc.malleate() - - err := suite.stateDB.Finalise(tc.deleteObjs) - - if !tc.expPass { - suite.Require().Error(err, tc.name) - hash := suite.stateDB.GetCommittedState(suite.address, ethcmn.BytesToHash([]byte("key"))) - suite.Require().NotEqual(ethcmn.Hash{}, hash, tc.name) - continue - } - - suite.Require().NoError(err, tc.name) - acc := suite.app.AccountKeeper.GetAccount(suite.ctx, sdk.AccAddress(suite.address.Bytes())) - - if tc.deleteObjs { - suite.Require().Nil(acc, tc.name) - continue - } - - suite.Require().NotNil(acc, tc.name) - } -} -func (suite *StateDBTestSuite) TestCommitStateDB_GetCommittedState() { - hash := suite.stateDB.GetCommittedState(ethcmn.Address{}, ethcmn.BytesToHash([]byte("key"))) - suite.Require().Equal(ethcmn.Hash{}, hash) -} - -func (suite *StateDBTestSuite) TestCommitStateDB_Snapshot() { - id := suite.stateDB.Snapshot() - suite.Require().NotPanics(func() { - suite.stateDB.RevertToSnapshot(id) - }) - - suite.Require().Panics(func() { - suite.stateDB.RevertToSnapshot(-1) - }, "invalid revision should panic") -} - -func (suite *StateDBTestSuite) TestCommitStateDB_ForEachStorage() { - var storage types.Storage - - testCase := []struct { - name string - malleate func() - callback func(key, value ethcmn.Hash) (stop bool) - expValues []ethcmn.Hash - }{ - { - "aggregate state", - func() { - for i := 0; i < 5; i++ { - suite.stateDB.SetState(suite.address, ethcmn.BytesToHash([]byte(fmt.Sprintf("key%d", i))), ethcmn.BytesToHash([]byte(fmt.Sprintf("value%d", i)))) - } - }, - func(key, value ethcmn.Hash) bool { - 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")), - }, - }, - { - "filter state", - func() { - suite.stateDB.SetState(suite.address, ethcmn.BytesToHash([]byte("key")), ethcmn.BytesToHash([]byte("value"))) - suite.stateDB.SetState(suite.address, ethcmn.BytesToHash([]byte("filterkey")), ethcmn.BytesToHash([]byte("filtervalue"))) - }, - func(key, value ethcmn.Hash) bool { - if value == ethcmn.BytesToHash([]byte("filtervalue")) { - storage = append(storage, types.NewState(key, value)) - return true - } - return false - }, - []ethcmn.Hash{ - ethcmn.BytesToHash([]byte("filtervalue")), - }, - }, - } - - for _, tc := range testCase { - suite.Run(tc.name, func() { - suite.SetupTest() // reset - tc.malleate() - suite.stateDB.Finalise(false) - - err := suite.stateDB.ForEachStorage(suite.address, tc.callback) - 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([]string, len(storage)) - for i := range storage { - vals[i] = storage[i].Value - } - - suite.Require().ElementsMatch(tc.expValues, vals) - }) - storage = types.Storage{} - } -} - -func (suite *StateDBTestSuite) TestCommitStateDB_AccessList() { - addr := ethcmn.Address([20]byte{77}) - hash := ethcmn.Hash([32]byte{99}) - - suite.Require().False(suite.stateDB.AddressInAccessList(addr)) - - suite.stateDB.AddAddressToAccessList(addr) - suite.Require().True(suite.stateDB.AddressInAccessList(addr)) - addrIn, slotIn := suite.stateDB.SlotInAccessList(addr, hash) - suite.Require().True(addrIn) - suite.Require().False(slotIn) - - suite.stateDB.AddSlotToAccessList(addr, hash) - addrIn, slotIn = suite.stateDB.SlotInAccessList(addr, hash) - suite.Require().True(addrIn) - suite.Require().True(slotIn) -} diff --git a/x/evm/types/utils.go b/x/evm/types/utils.go index dfd7fc8a..0679f7dc 100644 --- a/x/evm/types/utils.go +++ b/x/evm/types/utils.go @@ -5,20 +5,13 @@ import ( "github.com/gogo/protobuf/proto" "github.com/pkg/errors" - "golang.org/x/crypto/sha3" sdk "github.com/cosmos/cosmos-sdk/types" - ethcmn "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/rlp" + + "github.com/ethereum/go-ethereum/crypto" ) -func rlpHash(x interface{}) (hash ethcmn.Hash) { - hasher := sha3.NewLegacyKeccak256() - _ = rlp.Encode(hasher, x) - _ = hasher.Sum(hash[:0]) - - return hash -} +var EmptyCodeHash = crypto.Keccak256(nil) // EncodeTxResponse takes all of the necessary data from the EVM execution // and returns the data as a byte slice encoded with protobuf.