diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a500b008c0..e0a6bc4645 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,6 +3,7 @@ on: pull_request: branches: - main + - kava/release/** jobs: cleanup-runs: diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index a897fd5d16..4dc39dd5af 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -9,6 +9,6 @@ jobs: triage: runs-on: ubuntu-latest steps: - - uses: actions/labeler@v4 - with: - repo-token: "${{ secrets.GITHUB_TOKEN }}" + - uses: actions/labeler@v4 + with: + repo-token: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index bf7bb9977e..b18a695289 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -7,6 +7,7 @@ on: push: branches: - main + - kava/release/** jobs: golangci: name: Run golangci-lint @@ -25,7 +26,7 @@ jobs: **/**.go go.mod go.sum - - uses: golangci/golangci-lint-action@v3.3.1 + - uses: golangci/golangci-lint-action@v3.7.0 with: # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version. version: latest diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index c0541dd72a..856520c133 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -4,6 +4,7 @@ on: push: branches: - main + - kava/release/** jobs: Gosec: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 181d755b1d..71eb005789 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -5,6 +5,7 @@ on: branches: - main - release/** + - kava/release/** jobs: cleanup-runs: diff --git a/.golangci.yml b/.golangci.yml index 3f76d081a9..69a3a4ceff 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -63,17 +63,16 @@ linters-settings: locale: US nolintlint: allow-unused: false - allow-leading-space: true require-explanation: false require-specific: false gofumpt: lang-version: "1.19" gomodguard: blocked: - versions: # List of blocked module version constraints - - https://github.com/etcd-io/etcd: # Blocked module with version constraint - version: ">= 3.4.10 || ~3.3.23" # Version constraint, see https://github.com/Masterminds/semver#basic-comparisons - reason: "CVE-2020-15114; CVE-2020-15136; CVE-2020-15115" # Reason why the version constraint exists. (Optional) - - https://github.com/dgrijalva/jwt-go: # Blocked module with version constraint - version: ">= 4.0.0-preview1" # Version constraint, see https://github.com/Masterminds/semver#basic-comparisons - reason: "CVE-2020-26160" # Reason why the version constraint exists. (Optional) + versions: # List of blocked module version constraints + - https://github.com/etcd-io/etcd: # Blocked module with version constraint + version: ">= 3.4.10 || ~3.3.23" # Version constraint, see https://github.com/Masterminds/semver#basic-comparisons + reason: "CVE-2020-15114; CVE-2020-15136; CVE-2020-15115" # Reason why the version constraint exists. (Optional) + - https://github.com/dgrijalva/jwt-go: # Blocked module with version constraint + version: ">= 4.0.0-preview1" # Version constraint, see https://github.com/Masterminds/semver#basic-comparisons + reason: "CVE-2020-26160" # Reason why the version constraint exists. (Optional) diff --git a/Makefile b/Makefile index 65f866a7a1..2f3d6be63f 100644 --- a/Makefile +++ b/Makefile @@ -235,7 +235,7 @@ endif ifeq (, $(shell which go-bindata)) @echo "Installing go-bindata..." - @go get github.com/kevinburke/go-bindata/v4/go-bindata + @go get github.com/kevinburke/go-bindata/v4 else @echo "go-bindata already installed; skipping..." endif diff --git a/app/ante/eth.go b/app/ante/eth.go index cf949eb672..691629ba41 100644 --- a/app/ante/eth.go +++ b/app/ante/eth.go @@ -88,13 +88,14 @@ func (avd EthAccountVerificationDecorator) AnteHandle( if acct == nil { acc := avd.ak.NewAccountWithAddress(ctx, from) avd.ak.SetAccount(ctx, acc) - acct = statedb.NewEmptyAccount() } else if acct.IsContract() { return ctx, errorsmod.Wrapf(errortypes.ErrInvalidType, "the sender is not EOA: address %s, codeHash <%s>", fromAddr, acct.CodeHash) } - if err := keeper.CheckSenderBalance(sdkmath.NewIntFromBigInt(acct.Balance), txData); err != nil { + bal := avd.evmKeeper.GetBalance(ctx, fromAddr) + + if err := keeper.CheckSenderBalance(sdkmath.NewIntFromBigInt(bal), txData); err != nil { return ctx, errorsmod.Wrap(err, "failed to check sender balance") } } diff --git a/app/ante/sigs_test.go b/app/ante/sigs_test.go index 0c7b0539c8..76efa5396b 100644 --- a/app/ante/sigs_test.go +++ b/app/ante/sigs_test.go @@ -17,9 +17,10 @@ func (suite AnteTestSuite) TestSignatures() { acc := statedb.NewEmptyAccount() acc.Nonce = 1 - acc.Balance = big.NewInt(10000000000) suite.app.EvmKeeper.SetAccount(suite.ctx, addr, *acc) + suite.app.EvmKeeper.SetBalance(suite.ctx, addr, big.NewInt(10000000000)) + msgEthereumTx := evmtypes.NewTx(suite.app.EvmKeeper.ChainID(), 1, &to, big.NewInt(10), 100000, big.NewInt(1), nil, nil, nil, nil) msgEthereumTx.From = addr.Hex() diff --git a/app/app.go b/app/app.go index 1df647ea7a..f589b9f553 100644 --- a/app/app.go +++ b/app/app.go @@ -423,7 +423,8 @@ func NewEthermintApp( // Set authority to x/gov module account to only expect the module account to update params evmSs := app.GetSubspace(evmtypes.ModuleName) app.EvmKeeper = evmkeeper.NewKeeper( - appCodec, keys[evmtypes.StoreKey], tkeys[evmtypes.TransientKey], + appCodec, + keys[evmtypes.StoreKey], tkeys[evmtypes.TransientKey], authtypes.NewModuleAddress(govtypes.ModuleName), app.AccountKeeper, app.BankKeeper, stakingKeeper, app.FeeMarketKeeper, vm.NewEVM, tracer, evmSs, @@ -766,6 +767,13 @@ func (app *EthermintApp) InterfaceRegistry() types.InterfaceRegistry { return app.interfaceRegistry } +// GetKeys returns all KVStoreKeys +// +// NOTE: This is solely to be used for testing purposes. +func (app *EthermintApp) GetKeys() map[string]*storetypes.KVStoreKey { + return app.keys +} + // GetKey returns the KVStoreKey for the provided store key. // // NOTE: This is solely to be used for testing purposes. diff --git a/app/utils.go b/app/utils.go index 3c81aa18ec..d2e33d7ee5 100644 --- a/app/utils.go +++ b/app/utils.go @@ -24,12 +24,14 @@ import ( "github.com/cosmos/cosmos-sdk/codec" codectypes "github.com/cosmos/cosmos-sdk/codec/types" cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec" + "github.com/cosmos/cosmos-sdk/crypto/keys/ed25519" "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" "github.com/cosmos/cosmos-sdk/testutil/mock" simutils "github.com/cosmos/cosmos-sdk/testutil/sims" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/ethereum/go-ethereum/common" dbm "github.com/cometbft/cometbft-db" abci "github.com/cometbft/cometbft/abci/types" @@ -105,7 +107,9 @@ func SetupWithDB(isCheckTx bool, patchGenesis func(*EthermintApp, simapp.Genesis // NewTestGenesisState generate genesis state with single validator func NewTestGenesisState(codec codec.Codec) simapp.GenesisState { - privVal := mock.NewPV() + key := ed25519.GenPrivKeyFromSecret([]byte("constant seed")) + privVal := mock.PV{PrivKey: key} + pubKey, err := privVal.GetPubKey() if err != nil { panic(err) @@ -115,7 +119,9 @@ func NewTestGenesisState(codec codec.Codec) simapp.GenesisState { valSet := tmtypes.NewValidatorSet([]*tmtypes.Validator{validator}) // generate genesis account - senderPrivKey := secp256k1.GenPrivKey() + senderPrivKey := secp256k1.PrivKey{ + Key: common.Hex2Bytes("6b65793a225c745c3337355c33363627365c333735476c5c3331375f5c323530305c3031375c303237375c3337365c3234315c3336355c323136247d475c3237316a3c5c3336365f785c3337353b5c3336305c3031362220"), //nolint:lll + } acc := authtypes.NewBaseAccount(senderPrivKey.PubKey().Address().Bytes(), senderPrivKey.PubKey(), 0, 0) balance := banktypes.Balance{ Address: acc.GetAddress().String(), diff --git a/x/evm/genesis.go b/x/evm/genesis.go index 5df8066d8d..185d450b7e 100644 --- a/x/evm/genesis.go +++ b/x/evm/genesis.go @@ -79,7 +79,7 @@ func InitGenesis( k.SetCode(ctx, codeHash.Bytes(), code) for _, storage := range account.Storage { - k.SetState(ctx, address, common.HexToHash(storage.Key), common.HexToHash(storage.Value).Bytes()) + k.SetState(ctx, address, common.HexToHash(storage.Key), common.HexToHash(storage.Value)) } } diff --git a/x/evm/keeper/abci_test.go b/x/evm/keeper/abci_test.go index cb37ec08a1..e5d7e7af13 100644 --- a/x/evm/keeper/abci_test.go +++ b/x/evm/keeper/abci_test.go @@ -6,10 +6,10 @@ import ( ) func (suite *KeeperTestSuite) TestEndBlock() { - em := suite.ctx.EventManager() + em := suite.Ctx.EventManager() suite.Require().Equal(0, len(em.Events())) - res := suite.app.EvmKeeper.EndBlock(suite.ctx, types.RequestEndBlock{}) + res := suite.App.EvmKeeper.EndBlock(suite.Ctx, types.RequestEndBlock{}) suite.Require().Equal([]types.ValidatorUpdate{}, res) // should emit 1 EventTypeBlockBloom event on EndBlock diff --git a/x/evm/keeper/benchmark_test.go b/x/evm/keeper/benchmark_test.go index 9264629bcf..40c39ac2a5 100644 --- a/x/evm/keeper/benchmark_test.go +++ b/x/evm/keeper/benchmark_test.go @@ -21,12 +21,12 @@ func SetupContract(b *testing.B) (*KeeperTestSuite, common.Address) { suite.SetupTestWithT(b) amt := sdk.Coins{ethermint.NewPhotonCoinInt64(1000000000000000000)} - err := suite.app.BankKeeper.MintCoins(suite.ctx, types.ModuleName, amt) + err := suite.App.BankKeeper.MintCoins(suite.Ctx, types.ModuleName, amt) require.NoError(b, err) - err = suite.app.BankKeeper.SendCoinsFromModuleToAccount(suite.ctx, types.ModuleName, suite.address.Bytes(), amt) + err = suite.App.BankKeeper.SendCoinsFromModuleToAccount(suite.Ctx, types.ModuleName, suite.Address.Bytes(), amt) require.NoError(b, err) - contractAddr := suite.DeployTestContract(b, suite.address, sdkmath.NewIntWithDecimal(1000, 18).BigInt()) + contractAddr := suite.DeployTestContract(b, suite.Address, sdkmath.NewIntWithDecimal(1000, 18).BigInt()) suite.Commit() return &suite, contractAddr @@ -37,9 +37,9 @@ func SetupTestMessageCall(b *testing.B) (*KeeperTestSuite, common.Address) { suite.SetupTestWithT(b) amt := sdk.Coins{ethermint.NewPhotonCoinInt64(1000000000000000000)} - err := suite.app.BankKeeper.MintCoins(suite.ctx, types.ModuleName, amt) + err := suite.App.BankKeeper.MintCoins(suite.Ctx, types.ModuleName, amt) require.NoError(b, err) - err = suite.app.BankKeeper.SendCoinsFromModuleToAccount(suite.ctx, types.ModuleName, suite.address.Bytes(), amt) + err = suite.App.BankKeeper.SendCoinsFromModuleToAccount(suite.Ctx, types.ModuleName, suite.Address.Bytes(), amt) require.NoError(b, err) contractAddr := suite.DeployTestMessageCall(b) @@ -54,24 +54,24 @@ func DoBenchmark(b *testing.B, txBuilder TxBuilder) { suite, contractAddr := SetupContract(b) msg := txBuilder(suite, contractAddr) - msg.From = suite.address.Hex() - err := msg.Sign(ethtypes.LatestSignerForChainID(suite.app.EvmKeeper.ChainID()), suite.signer) + msg.From = suite.Address.Hex() + err := msg.Sign(ethtypes.LatestSignerForChainID(suite.App.EvmKeeper.ChainID()), suite.Signer) require.NoError(b, err) b.ResetTimer() b.StartTimer() for i := 0; i < b.N; i++ { - ctx, _ := suite.ctx.CacheContext() + ctx, _ := suite.Ctx.CacheContext() // deduct fee first txData, err := types.UnpackTxData(msg.Data) require.NoError(b, err) fees := sdk.Coins{sdk.NewCoin(suite.EvmDenom(), sdkmath.NewIntFromBigInt(txData.Fee()))} - err = authante.DeductFees(suite.app.BankKeeper, suite.ctx, suite.app.AccountKeeper.GetAccount(ctx, msg.GetFrom()), fees) + err = authante.DeductFees(suite.App.BankKeeper, suite.Ctx, suite.App.AccountKeeper.GetAccount(ctx, msg.GetFrom()), fees) require.NoError(b, err) - rsp, err := suite.app.EvmKeeper.EthereumTx(sdk.WrapSDKContext(ctx), msg) + rsp, err := suite.App.EvmKeeper.EthereumTx(sdk.WrapSDKContext(ctx), msg) require.NoError(b, err) require.False(b, rsp.Failed()) } @@ -81,8 +81,8 @@ func BenchmarkTokenTransfer(b *testing.B) { DoBenchmark(b, func(suite *KeeperTestSuite, contract common.Address) *types.MsgEthereumTx { input, err := types.ERC20Contract.ABI.Pack("transfer", common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), big.NewInt(1000)) require.NoError(b, err) - nonce := suite.app.EvmKeeper.GetNonce(suite.ctx, suite.address) - return types.NewTx(suite.app.EvmKeeper.ChainID(), nonce, &contract, big.NewInt(0), 410000, big.NewInt(1), nil, nil, input, nil) + nonce := suite.App.EvmKeeper.GetNonce(suite.Ctx, suite.Address) + return types.NewTx(suite.App.EvmKeeper.ChainID(), nonce, &contract, big.NewInt(0), 410000, big.NewInt(1), nil, nil, input, nil) }) } @@ -90,17 +90,17 @@ func BenchmarkEmitLogs(b *testing.B) { DoBenchmark(b, func(suite *KeeperTestSuite, contract common.Address) *types.MsgEthereumTx { input, err := types.ERC20Contract.ABI.Pack("benchmarkLogs", big.NewInt(1000)) require.NoError(b, err) - nonce := suite.app.EvmKeeper.GetNonce(suite.ctx, suite.address) - return types.NewTx(suite.app.EvmKeeper.ChainID(), nonce, &contract, big.NewInt(0), 4100000, big.NewInt(1), nil, nil, input, nil) + nonce := suite.App.EvmKeeper.GetNonce(suite.Ctx, suite.Address) + return types.NewTx(suite.App.EvmKeeper.ChainID(), nonce, &contract, big.NewInt(0), 4100000, big.NewInt(1), nil, nil, input, nil) }) } func BenchmarkTokenTransferFrom(b *testing.B) { DoBenchmark(b, func(suite *KeeperTestSuite, contract common.Address) *types.MsgEthereumTx { - input, err := types.ERC20Contract.ABI.Pack("transferFrom", suite.address, common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), big.NewInt(0)) + input, err := types.ERC20Contract.ABI.Pack("transferFrom", suite.Address, common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), big.NewInt(0)) require.NoError(b, err) - nonce := suite.app.EvmKeeper.GetNonce(suite.ctx, suite.address) - return types.NewTx(suite.app.EvmKeeper.ChainID(), nonce, &contract, big.NewInt(0), 410000, big.NewInt(1), nil, nil, input, nil) + nonce := suite.App.EvmKeeper.GetNonce(suite.Ctx, suite.Address) + return types.NewTx(suite.App.EvmKeeper.ChainID(), nonce, &contract, big.NewInt(0), 410000, big.NewInt(1), nil, nil, input, nil) }) } @@ -108,8 +108,8 @@ func BenchmarkTokenMint(b *testing.B) { DoBenchmark(b, func(suite *KeeperTestSuite, contract common.Address) *types.MsgEthereumTx { input, err := types.ERC20Contract.ABI.Pack("mint", common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), big.NewInt(1000)) require.NoError(b, err) - nonce := suite.app.EvmKeeper.GetNonce(suite.ctx, suite.address) - return types.NewTx(suite.app.EvmKeeper.ChainID(), nonce, &contract, big.NewInt(0), 410000, big.NewInt(1), nil, nil, input, nil) + nonce := suite.App.EvmKeeper.GetNonce(suite.Ctx, suite.Address) + return types.NewTx(suite.App.EvmKeeper.ChainID(), nonce, &contract, big.NewInt(0), 410000, big.NewInt(1), nil, nil, input, nil) }) } @@ -118,27 +118,27 @@ func BenchmarkMessageCall(b *testing.B) { input, err := types.TestMessageCall.ABI.Pack("benchmarkMessageCall", big.NewInt(10000)) require.NoError(b, err) - nonce := suite.app.EvmKeeper.GetNonce(suite.ctx, suite.address) - msg := types.NewTx(suite.app.EvmKeeper.ChainID(), nonce, &contract, big.NewInt(0), 25000000, big.NewInt(1), nil, nil, input, nil) + nonce := suite.App.EvmKeeper.GetNonce(suite.Ctx, suite.Address) + msg := types.NewTx(suite.App.EvmKeeper.ChainID(), nonce, &contract, big.NewInt(0), 25000000, big.NewInt(1), nil, nil, input, nil) - msg.From = suite.address.Hex() - err = msg.Sign(ethtypes.LatestSignerForChainID(suite.app.EvmKeeper.ChainID()), suite.signer) + msg.From = suite.Address.Hex() + err = msg.Sign(ethtypes.LatestSignerForChainID(suite.App.EvmKeeper.ChainID()), suite.Signer) require.NoError(b, err) b.ResetTimer() b.StartTimer() for i := 0; i < b.N; i++ { - ctx, _ := suite.ctx.CacheContext() + ctx, _ := suite.Ctx.CacheContext() // deduct fee first txData, err := types.UnpackTxData(msg.Data) require.NoError(b, err) fees := sdk.Coins{sdk.NewCoin(suite.EvmDenom(), sdkmath.NewIntFromBigInt(txData.Fee()))} - err = authante.DeductFees(suite.app.BankKeeper, suite.ctx, suite.app.AccountKeeper.GetAccount(ctx, msg.GetFrom()), fees) + err = authante.DeductFees(suite.App.BankKeeper, suite.Ctx, suite.App.AccountKeeper.GetAccount(ctx, msg.GetFrom()), fees) require.NoError(b, err) - rsp, err := suite.app.EvmKeeper.EthereumTx(sdk.WrapSDKContext(ctx), msg) + rsp, err := suite.App.EvmKeeper.EthereumTx(sdk.WrapSDKContext(ctx), msg) require.NoError(b, err) require.False(b, rsp.Failed()) } diff --git a/x/evm/keeper/grpc_query.go b/x/evm/keeper/grpc_query.go index 5e09153b59..a775128014 100644 --- a/x/evm/keeper/grpc_query.go +++ b/x/evm/keeper/grpc_query.go @@ -67,8 +67,10 @@ func (k Keeper) Account(c context.Context, req *types.QueryAccountRequest) (*typ ctx := sdk.UnwrapSDKContext(c) acct := k.GetAccountOrEmpty(ctx, addr) + bal := k.GetBalance(ctx, addr) + return &types.QueryAccountResponse{ - Balance: acct.Balance.String(), + Balance: bal.String(), CodeHash: common.BytesToHash(acct.CodeHash).Hex(), Nonce: acct.Nonce, }, nil diff --git a/x/evm/keeper/grpc_query_test.go b/x/evm/keeper/grpc_query_test.go index 4149aa47f3..53230928c9 100644 --- a/x/evm/keeper/grpc_query_test.go +++ b/x/evm/keeper/grpc_query_test.go @@ -9,7 +9,6 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" ethlogger "github.com/ethereum/go-ethereum/eth/tracers/logger" ethparams "github.com/ethereum/go-ethereum/params" @@ -55,9 +54,9 @@ func (suite *KeeperTestSuite) TestQueryAccount() { "success", func() { amt := sdk.Coins{ethermint.NewPhotonCoinInt64(100)} - err := suite.app.BankKeeper.MintCoins(suite.ctx, types.ModuleName, amt) + err := suite.App.BankKeeper.MintCoins(suite.Ctx, types.ModuleName, amt) suite.Require().NoError(err) - err = suite.app.BankKeeper.SendCoinsFromModuleToAccount(suite.ctx, types.ModuleName, suite.address.Bytes(), amt) + err = suite.App.BankKeeper.SendCoinsFromModuleToAccount(suite.Ctx, types.ModuleName, suite.Address.Bytes(), amt) suite.Require().NoError(err) expAccount = &types.QueryAccountResponse{ @@ -66,7 +65,7 @@ func (suite *KeeperTestSuite) TestQueryAccount() { Nonce: 0, } req = &types.QueryAccountRequest{ - Address: suite.address.String(), + Address: suite.Address.String(), } }, true, @@ -78,8 +77,8 @@ func (suite *KeeperTestSuite) TestQueryAccount() { suite.SetupTest() // reset tc.malleate() - ctx := sdk.WrapSDKContext(suite.ctx) - res, err := suite.queryClient.Account(ctx, req) + ctx := sdk.WrapSDKContext(suite.Ctx) + res, err := suite.QueryClient.Account(ctx, req) if tc.expPass { suite.Require().NoError(err) @@ -120,12 +119,12 @@ func (suite *KeeperTestSuite) TestQueryCosmosAccount() { "success", func() { expAccount = &types.QueryCosmosAccountResponse{ - CosmosAddress: sdk.AccAddress(suite.address.Bytes()).String(), + CosmosAddress: sdk.AccAddress(suite.Address.Bytes()).String(), Sequence: 0, AccountNumber: 0, } req = &types.QueryCosmosAccountRequest{ - Address: suite.address.String(), + Address: suite.Address.String(), } }, true, @@ -133,18 +132,18 @@ func (suite *KeeperTestSuite) TestQueryCosmosAccount() { { "success with seq and account number", func() { - acc := suite.app.AccountKeeper.GetAccount(suite.ctx, suite.address.Bytes()) + acc := suite.App.AccountKeeper.GetAccount(suite.Ctx, suite.Address.Bytes()) suite.Require().NoError(acc.SetSequence(10)) suite.Require().NoError(acc.SetAccountNumber(1)) - suite.app.AccountKeeper.SetAccount(suite.ctx, acc) + suite.App.AccountKeeper.SetAccount(suite.Ctx, acc) expAccount = &types.QueryCosmosAccountResponse{ - CosmosAddress: sdk.AccAddress(suite.address.Bytes()).String(), + CosmosAddress: sdk.AccAddress(suite.Address.Bytes()).String(), Sequence: 10, AccountNumber: 1, } req = &types.QueryCosmosAccountRequest{ - Address: suite.address.String(), + Address: suite.Address.String(), } }, true, @@ -156,8 +155,8 @@ func (suite *KeeperTestSuite) TestQueryCosmosAccount() { suite.SetupTest() // reset tc.malleate() - ctx := sdk.WrapSDKContext(suite.ctx) - res, err := suite.queryClient.CosmosAccount(ctx, req) + ctx := sdk.WrapSDKContext(suite.Ctx) + res, err := suite.QueryClient.CosmosAccount(ctx, req) if tc.expPass { suite.Require().NoError(err) @@ -196,14 +195,14 @@ func (suite *KeeperTestSuite) TestQueryBalance() { "success", func() { amt := sdk.Coins{ethermint.NewPhotonCoinInt64(100)} - err := suite.app.BankKeeper.MintCoins(suite.ctx, types.ModuleName, amt) + err := suite.App.BankKeeper.MintCoins(suite.Ctx, types.ModuleName, amt) suite.Require().NoError(err) - err = suite.app.BankKeeper.SendCoinsFromModuleToAccount(suite.ctx, types.ModuleName, suite.address.Bytes(), amt) + err = suite.App.BankKeeper.SendCoinsFromModuleToAccount(suite.Ctx, types.ModuleName, suite.Address.Bytes(), amt) suite.Require().NoError(err) expBalance = "100" req = &types.QueryBalanceRequest{ - Address: suite.address.String(), + Address: suite.Address.String(), } }, true, @@ -215,8 +214,8 @@ func (suite *KeeperTestSuite) TestQueryBalance() { suite.SetupTest() // reset tc.malleate() - ctx := sdk.WrapSDKContext(suite.ctx) - res, err := suite.queryClient.Balance(ctx, req) + ctx := sdk.WrapSDKContext(suite.Ctx) + res, err := suite.QueryClient.Balance(ctx, req) if tc.expPass { suite.Require().NoError(err) @@ -238,12 +237,12 @@ func (suite *KeeperTestSuite) TestQueryStorage() { testCases := []struct { msg string - malleate func(vm.StateDB) + malleate func(*statedb.StateDB) expPass bool }{ { "invalid address", - func(vm.StateDB) { + func(*statedb.StateDB) { req = &types.QueryStorageRequest{ Address: invalidAddress, } @@ -252,13 +251,13 @@ func (suite *KeeperTestSuite) TestQueryStorage() { }, { "success", - func(vmdb vm.StateDB) { + func(vmdb *statedb.StateDB) { key := common.BytesToHash([]byte("key")) value := common.BytesToHash([]byte("value")) expValue = value.String() - vmdb.SetState(suite.address, key, value) + vmdb.SetState(suite.Address, key, value) req = &types.QueryStorageRequest{ - Address: suite.address.String(), + Address: suite.Address.String(), Key: key.String(), } }, @@ -274,8 +273,8 @@ func (suite *KeeperTestSuite) TestQueryStorage() { tc.malleate(vmdb) suite.Require().NoError(vmdb.Commit()) - ctx := sdk.WrapSDKContext(suite.ctx) - res, err := suite.queryClient.Storage(ctx, req) + ctx := sdk.WrapSDKContext(suite.Ctx) + res, err := suite.QueryClient.Storage(ctx, req) if tc.expPass { suite.Require().NoError(err) @@ -297,12 +296,12 @@ func (suite *KeeperTestSuite) TestQueryCode() { testCases := []struct { msg string - malleate func(vm.StateDB) + malleate func(*statedb.StateDB) expPass bool }{ { "invalid address", - func(vm.StateDB) { + func(*statedb.StateDB) { req = &types.QueryCodeRequest{ Address: invalidAddress, } @@ -313,12 +312,12 @@ func (suite *KeeperTestSuite) TestQueryCode() { }, { "success", - func(vmdb vm.StateDB) { + func(vmdb *statedb.StateDB) { expCode = []byte("code") - vmdb.SetCode(suite.address, expCode) + vmdb.SetCode(suite.Address, expCode) req = &types.QueryCodeRequest{ - Address: suite.address.String(), + Address: suite.Address.String(), } }, true, @@ -333,8 +332,8 @@ func (suite *KeeperTestSuite) TestQueryCode() { tc.malleate(vmdb) suite.Require().NoError(vmdb.Commit()) - ctx := sdk.WrapSDKContext(suite.ctx) - res, err := suite.queryClient.Code(ctx, req) + ctx := sdk.WrapSDKContext(suite.Ctx) + res, err := suite.QueryClient.Code(ctx, req) if tc.expPass { suite.Require().NoError(err) @@ -356,26 +355,26 @@ func (suite *KeeperTestSuite) TestQueryTxLogs() { testCases := []struct { msg string - malleate func(vm.StateDB) + malleate func(*statedb.StateDB) }{ { "empty logs", - func(vm.StateDB) { + func(*statedb.StateDB) { expLogs = nil }, }, { "success", - func(vmdb vm.StateDB) { + func(vmdb *statedb.StateDB) { expLogs = []*types.Log{ { - Address: suite.address.String(), + Address: suite.Address.String(), Topics: []string{common.BytesToHash([]byte("topic")).String()}, Data: []byte("data"), BlockNumber: 1, TxHash: txHash.String(), TxIndex: uint64(txIndex), - BlockHash: common.BytesToHash(suite.ctx.HeaderHash()).Hex(), + BlockHash: common.BytesToHash(suite.Ctx.HeaderHash()).Hex(), Index: uint64(logIndex), Removed: false, }, @@ -392,7 +391,7 @@ func (suite *KeeperTestSuite) TestQueryTxLogs() { suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { suite.SetupTest() // reset - vmdb := statedb.New(suite.ctx, suite.app.EvmKeeper, statedb.NewTxConfig(common.BytesToHash(suite.ctx.HeaderHash().Bytes()), txHash, txIndex, logIndex)) + vmdb := statedb.New(suite.Ctx, suite.App.EvmKeeper, statedb.NewTxConfig(common.BytesToHash(suite.Ctx.HeaderHash().Bytes()), txHash, txIndex, logIndex)) tc.malleate(vmdb) suite.Require().NoError(vmdb.Commit()) @@ -403,11 +402,11 @@ func (suite *KeeperTestSuite) TestQueryTxLogs() { } func (suite *KeeperTestSuite) TestQueryParams() { - ctx := sdk.WrapSDKContext(suite.ctx) + ctx := sdk.WrapSDKContext(suite.Ctx) expParams := types.DefaultParams() expParams.EIP712AllowedMsgs = nil - res, err := suite.queryClient.Params(ctx, &types.QueryParamsRequest{}) + res, err := suite.QueryClient.Params(ctx, &types.QueryParamsRequest{}) suite.Require().NoError(err) suite.Require().Equal(expParams, res.Params) } @@ -439,12 +438,12 @@ func (suite *KeeperTestSuite) TestQueryValidatorAccount() { "success", func() { expAccount = &types.QueryValidatorAccountResponse{ - AccountAddress: sdk.AccAddress(suite.address.Bytes()).String(), + AccountAddress: sdk.AccAddress(suite.Address.Bytes()).String(), Sequence: 0, AccountNumber: 0, } req = &types.QueryValidatorAccountRequest{ - ConsAddress: suite.consAddress.String(), + ConsAddress: suite.ConsAddress.String(), } }, true, @@ -452,18 +451,18 @@ func (suite *KeeperTestSuite) TestQueryValidatorAccount() { { "success with seq and account number", func() { - acc := suite.app.AccountKeeper.GetAccount(suite.ctx, suite.address.Bytes()) + acc := suite.App.AccountKeeper.GetAccount(suite.Ctx, suite.Address.Bytes()) suite.Require().NoError(acc.SetSequence(10)) suite.Require().NoError(acc.SetAccountNumber(1)) - suite.app.AccountKeeper.SetAccount(suite.ctx, acc) + suite.App.AccountKeeper.SetAccount(suite.Ctx, acc) expAccount = &types.QueryValidatorAccountResponse{ - AccountAddress: sdk.AccAddress(suite.address.Bytes()).String(), + AccountAddress: sdk.AccAddress(suite.Address.Bytes()).String(), Sequence: 10, AccountNumber: 1, } req = &types.QueryValidatorAccountRequest{ - ConsAddress: suite.consAddress.String(), + ConsAddress: suite.ConsAddress.String(), } }, true, @@ -475,8 +474,8 @@ func (suite *KeeperTestSuite) TestQueryValidatorAccount() { suite.SetupTest() // reset tc.malleate() - ctx := sdk.WrapSDKContext(suite.ctx) - res, err := suite.queryClient.ValidatorAccount(ctx, req) + ctx := sdk.WrapSDKContext(suite.Ctx) + res, err := suite.QueryClient.ValidatorAccount(ctx, req) if tc.expPass { suite.Require().NoError(err) @@ -540,7 +539,7 @@ func (suite *KeeperTestSuite) TestEstimateGas() { { "enough balance", func() { - args = types.TransactionArgs{To: &common.Address{}, From: &suite.address, Value: (*hexutil.Big)(big.NewInt(100))} + args = types.TransactionArgs{To: &common.Address{}, From: &suite.Address, Value: (*hexutil.Big)(big.NewInt(100))} }, false, 0, false, }, // should success, because gas limit lower than 21000 is ignored @@ -568,11 +567,11 @@ func (suite *KeeperTestSuite) TestEstimateGas() { { "contract deployment", func() { - ctorArgs, err := types.ERC20Contract.ABI.Pack("", &suite.address, sdkmath.NewIntWithDecimal(1000, 18).BigInt()) + ctorArgs, err := types.ERC20Contract.ABI.Pack("", &suite.Address, sdkmath.NewIntWithDecimal(1000, 18).BigInt()) suite.Require().NoError(err) data := append(types.ERC20Contract.Bin, ctorArgs...) args = types.TransactionArgs{ - From: &suite.address, + From: &suite.Address, Data: (*hexutil.Bytes)(&data), } }, @@ -584,11 +583,11 @@ func (suite *KeeperTestSuite) TestEstimateGas() { { "erc20 transfer", func() { - contractAddr := suite.DeployTestContract(suite.T(), suite.address, sdkmath.NewIntWithDecimal(1000, 18).BigInt()) + contractAddr := suite.DeployTestContract(suite.T(), suite.Address, sdkmath.NewIntWithDecimal(1000, 18).BigInt()) suite.Commit() transferData, err := types.ERC20Contract.ABI.Pack("transfer", common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), big.NewInt(1000)) suite.Require().NoError(err) - args = types.TransactionArgs{To: &contractAddr, From: &suite.address, Data: (*hexutil.Bytes)(&transferData)} + args = types.TransactionArgs{To: &contractAddr, From: &suite.Address, Data: (*hexutil.Bytes)(&transferData)} }, true, 51880, @@ -616,7 +615,7 @@ func (suite *KeeperTestSuite) TestEstimateGas() { { "enough balance w/ enableFeemarket", func() { - args = types.TransactionArgs{To: &common.Address{}, From: &suite.address, Value: (*hexutil.Big)(big.NewInt(100))} + args = types.TransactionArgs{To: &common.Address{}, From: &suite.Address, Value: (*hexutil.Big)(big.NewInt(100))} }, false, 0, @@ -644,11 +643,11 @@ func (suite *KeeperTestSuite) TestEstimateGas() { { "contract deployment w/ enableFeemarket", func() { - ctorArgs, err := types.ERC20Contract.ABI.Pack("", &suite.address, sdkmath.NewIntWithDecimal(1000, 18).BigInt()) + ctorArgs, err := types.ERC20Contract.ABI.Pack("", &suite.Address, sdkmath.NewIntWithDecimal(1000, 18).BigInt()) suite.Require().NoError(err) data := append(types.ERC20Contract.Bin, ctorArgs...) args = types.TransactionArgs{ - From: &suite.address, + From: &suite.Address, Data: (*hexutil.Bytes)(&data), } }, @@ -659,11 +658,11 @@ func (suite *KeeperTestSuite) TestEstimateGas() { { "erc20 transfer w/ enableFeemarket", func() { - contractAddr := suite.DeployTestContract(suite.T(), suite.address, sdkmath.NewIntWithDecimal(1000, 18).BigInt()) + contractAddr := suite.DeployTestContract(suite.T(), suite.Address, sdkmath.NewIntWithDecimal(1000, 18).BigInt()) suite.Commit() transferData, err := types.ERC20Contract.ABI.Pack("transfer", common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), big.NewInt(1000)) suite.Require().NoError(err) - args = types.TransactionArgs{To: &contractAddr, From: &suite.address, Data: (*hexutil.Bytes)(&transferData)} + args = types.TransactionArgs{To: &contractAddr, From: &suite.Address, Data: (*hexutil.Bytes)(&transferData)} }, true, 51880, @@ -672,16 +671,16 @@ func (suite *KeeperTestSuite) TestEstimateGas() { { "contract creation but 'create' param disabled", func() { - ctorArgs, err := types.ERC20Contract.ABI.Pack("", &suite.address, sdkmath.NewIntWithDecimal(1000, 18).BigInt()) + ctorArgs, err := types.ERC20Contract.ABI.Pack("", &suite.Address, sdkmath.NewIntWithDecimal(1000, 18).BigInt()) suite.Require().NoError(err) data := append(types.ERC20Contract.Bin, ctorArgs...) args = types.TransactionArgs{ - From: &suite.address, + From: &suite.Address, Data: (*hexutil.Bytes)(&data), } - params := suite.app.EvmKeeper.GetParams(suite.ctx) + params := suite.App.EvmKeeper.GetParams(suite.Ctx) params.EnableCreate = false - suite.app.EvmKeeper.SetParams(suite.ctx, params) + suite.App.EvmKeeper.SetParams(suite.Ctx, params) }, false, 0, @@ -729,7 +728,7 @@ func (suite *KeeperTestSuite) TestEstimateGas() { for _, tc := range testCases { suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { - suite.enableFeemarket = tc.enableFeemarket + suite.EnableFeemarket = tc.enableFeemarket suite.SetupTest() gasCap = 25_000_000 tc.malleate() @@ -739,10 +738,10 @@ func (suite *KeeperTestSuite) TestEstimateGas() { req := types.EthCallRequest{ Args: args, GasCap: gasCap, - ProposerAddress: suite.ctx.BlockHeader().ProposerAddress, + ProposerAddress: suite.Ctx.BlockHeader().ProposerAddress, } - rsp, err := suite.queryClient.EstimateGas(sdk.WrapSDKContext(suite.ctx), &req) + rsp, err := suite.QueryClient.EstimateGas(sdk.WrapSDKContext(suite.Ctx), &req) if tc.expPass { suite.Require().NoError(err) suite.Require().Equal(int64(tc.expGas), int64(rsp.Gas)) @@ -751,7 +750,7 @@ func (suite *KeeperTestSuite) TestEstimateGas() { } }) } - suite.enableFeemarket = false // reset flag + suite.EnableFeemarket = false // reset flag } func (suite *KeeperTestSuite) TestTraceTx() { @@ -837,14 +836,14 @@ func (suite *KeeperTestSuite) TestTraceTx() { // increase nonce to avoid address collision vmdb := suite.StateDB() - vmdb.SetNonce(suite.address, vmdb.GetNonce(suite.address)+1) + vmdb.SetNonce(suite.Address, vmdb.GetNonce(suite.Address)+1) suite.Require().NoError(vmdb.Commit()) - contractAddr := suite.DeployTestContract(suite.T(), suite.address, sdkmath.NewIntWithDecimal(1000, 18).BigInt()) + contractAddr := suite.DeployTestContract(suite.T(), suite.Address, sdkmath.NewIntWithDecimal(1000, 18).BigInt()) suite.Commit() // Generate token transfer transaction - firstTx := suite.TransferERC20Token(suite.T(), contractAddr, suite.address, common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), sdkmath.NewIntWithDecimal(1, 18).BigInt()) - txMsg = suite.TransferERC20Token(suite.T(), contractAddr, suite.address, common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), sdkmath.NewIntWithDecimal(1, 18).BigInt()) + firstTx := suite.MustTransferERC20Token(suite.T(), contractAddr, suite.Address, common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), sdkmath.NewIntWithDecimal(1, 18).BigInt()) + txMsg = suite.MustTransferERC20Token(suite.T(), contractAddr, suite.Address, common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), sdkmath.NewIntWithDecimal(1, 18).BigInt()) suite.Commit() predecessors = append(predecessors, firstTx) @@ -896,11 +895,11 @@ func (suite *KeeperTestSuite) TestTraceTx() { // increase nonce to avoid address collision vmdb := suite.StateDB() - vmdb.SetNonce(suite.address, vmdb.GetNonce(suite.address)+1) + vmdb.SetNonce(suite.Address, vmdb.GetNonce(suite.Address)+1) suite.Require().NoError(vmdb.Commit()) - chainID := suite.app.EvmKeeper.ChainID() - nonce := suite.app.EvmKeeper.GetNonce(suite.ctx, suite.address) + chainID := suite.App.EvmKeeper.ChainID() + nonce := suite.App.EvmKeeper.GetNonce(suite.Ctx, suite.Address) data := types.ERC20Contract.Bin contractTx := types.NewTxContract( chainID, @@ -916,9 +915,9 @@ func (suite *KeeperTestSuite) TestTraceTx() { predecessors = append(predecessors, contractTx) suite.Commit() - params := suite.app.EvmKeeper.GetParams(suite.ctx) + params := suite.App.EvmKeeper.GetParams(suite.Ctx) params.EnableCreate = false - suite.app.EvmKeeper.SetParams(suite.ctx, params) + suite.App.EvmKeeper.SetParams(suite.Ctx, params) }, expPass: true, traceResponse: "{\"gas\":34828,\"failed\":false,\"returnValue\":\"0000000000000000000000000000000000000000000000000000000000000001\",\"structLogs\":[{\"pc\":0,\"op\":\"PUSH1\",\"gas\":", @@ -937,13 +936,13 @@ func (suite *KeeperTestSuite) TestTraceTx() { for _, tc := range testCases { suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { - suite.enableFeemarket = tc.enableFeemarket + suite.EnableFeemarket = tc.enableFeemarket suite.SetupTest() // Deploy contract - contractAddr := suite.DeployTestContract(suite.T(), suite.address, sdkmath.NewIntWithDecimal(1000, 18).BigInt()) + contractAddr := suite.DeployTestContract(suite.T(), suite.Address, sdkmath.NewIntWithDecimal(1000, 18).BigInt()) suite.Commit() // Generate token transfer transaction - txMsg = suite.TransferERC20Token(suite.T(), contractAddr, suite.address, common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), sdkmath.NewIntWithDecimal(1, 18).BigInt()) + txMsg = suite.MustTransferERC20Token(suite.T(), contractAddr, suite.Address, common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), sdkmath.NewIntWithDecimal(1, 18).BigInt()) suite.Commit() tc.malleate() @@ -956,7 +955,7 @@ func (suite *KeeperTestSuite) TestTraceTx() { if chainID != nil { traceReq.ChainId = chainID.Int64() } - res, err := suite.queryClient.TraceTx(sdk.WrapSDKContext(suite.ctx), &traceReq) + res, err := suite.QueryClient.TraceTx(sdk.WrapSDKContext(suite.Ctx), &traceReq) if tc.expPass { suite.Require().NoError(err) @@ -979,7 +978,7 @@ func (suite *KeeperTestSuite) TestTraceTx() { }) } - suite.enableFeemarket = false // reset flag + suite.EnableFeemarket = false // reset flag } func (suite *KeeperTestSuite) TestTraceBlock() { @@ -1057,14 +1056,14 @@ func (suite *KeeperTestSuite) TestTraceBlock() { // increase nonce to avoid address collision vmdb := suite.StateDB() - vmdb.SetNonce(suite.address, vmdb.GetNonce(suite.address)+1) + vmdb.SetNonce(suite.Address, vmdb.GetNonce(suite.Address)+1) suite.Require().NoError(vmdb.Commit()) - contractAddr := suite.DeployTestContract(suite.T(), suite.address, sdkmath.NewIntWithDecimal(1000, 18).BigInt()) + contractAddr := suite.DeployTestContract(suite.T(), suite.Address, sdkmath.NewIntWithDecimal(1000, 18).BigInt()) suite.Commit() // create multiple transactions in the same block - firstTx := suite.TransferERC20Token(suite.T(), contractAddr, suite.address, common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), sdkmath.NewIntWithDecimal(1, 18).BigInt()) - secondTx := suite.TransferERC20Token(suite.T(), contractAddr, suite.address, common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), sdkmath.NewIntWithDecimal(1, 18).BigInt()) + firstTx := suite.MustTransferERC20Token(suite.T(), contractAddr, suite.Address, common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), sdkmath.NewIntWithDecimal(1, 18).BigInt()) + secondTx := suite.MustTransferERC20Token(suite.T(), contractAddr, suite.Address, common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), sdkmath.NewIntWithDecimal(1, 18).BigInt()) suite.Commit() // overwrite txs to include only the ones on new block txs = append([]*types.MsgEthereumTx{}, firstTx, secondTx) @@ -1113,13 +1112,13 @@ func (suite *KeeperTestSuite) TestTraceBlock() { for _, tc := range testCases { suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { txs = []*types.MsgEthereumTx{} - suite.enableFeemarket = tc.enableFeemarket + suite.EnableFeemarket = tc.enableFeemarket suite.SetupTest() // Deploy contract - contractAddr := suite.DeployTestContract(suite.T(), suite.address, sdkmath.NewIntWithDecimal(1000, 18).BigInt()) + contractAddr := suite.DeployTestContract(suite.T(), suite.Address, sdkmath.NewIntWithDecimal(1000, 18).BigInt()) suite.Commit() // Generate token transfer transaction - txMsg := suite.TransferERC20Token(suite.T(), contractAddr, suite.address, common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), sdkmath.NewIntWithDecimal(1, 18).BigInt()) + txMsg := suite.MustTransferERC20Token(suite.T(), contractAddr, suite.Address, common.HexToAddress("0x378c50D9264C63F3F92B806d4ee56E9D86FfB3Ec"), sdkmath.NewIntWithDecimal(1, 18).BigInt()) suite.Commit() txs = append(txs, txMsg) @@ -1134,7 +1133,7 @@ func (suite *KeeperTestSuite) TestTraceBlock() { traceReq.ChainId = chainID.Int64() } - res, err := suite.queryClient.TraceBlock(sdk.WrapSDKContext(suite.ctx), &traceReq) + res, err := suite.QueryClient.TraceBlock(sdk.WrapSDKContext(suite.Ctx), &traceReq) if tc.expPass { suite.Require().NoError(err) @@ -1152,12 +1151,12 @@ func (suite *KeeperTestSuite) TestTraceBlock() { }) } - suite.enableFeemarket = false // reset flag + suite.EnableFeemarket = false // reset flag } func (suite *KeeperTestSuite) TestNonceInQuery() { address := tests.GenerateAddress() - suite.Require().Equal(uint64(0), suite.app.EvmKeeper.GetNonce(suite.ctx, address)) + suite.Require().Equal(uint64(0), suite.App.EvmKeeper.GetNonce(suite.Ctx, address)) supply := sdkmath.NewIntWithDecimal(1000, 18).BigInt() // accupy nonce 0 @@ -1173,15 +1172,15 @@ func (suite *KeeperTestSuite) TestNonceInQuery() { Data: (*hexutil.Bytes)(&data), }) suite.Require().NoError(err) - proposerAddress := suite.ctx.BlockHeader().ProposerAddress - _, err = suite.queryClient.EstimateGas(sdk.WrapSDKContext(suite.ctx), &types.EthCallRequest{ + proposerAddress := suite.Ctx.BlockHeader().ProposerAddress + _, err = suite.QueryClient.EstimateGas(sdk.WrapSDKContext(suite.Ctx), &types.EthCallRequest{ Args: args, GasCap: uint64(config.DefaultGasCap), ProposerAddress: proposerAddress, }) suite.Require().NoError(err) - _, err = suite.queryClient.EthCall(sdk.WrapSDKContext(suite.ctx), &types.EthCallRequest{ + _, err = suite.QueryClient.EthCall(sdk.WrapSDKContext(suite.Ctx), &types.EthCallRequest{ Args: args, GasCap: uint64(config.DefaultGasCap), ProposerAddress: proposerAddress, @@ -1214,7 +1213,7 @@ func (suite *KeeperTestSuite) TestQueryBaseFee() { "pass - non-nil Base Fee", func() { baseFee := sdk.OneInt().BigInt() - suite.app.FeeMarketKeeper.SetBaseFee(suite.ctx, baseFee) + suite.App.FeeMarketKeeper.SetBaseFee(suite.Ctx, baseFee) aux = sdkmath.NewIntFromBigInt(baseFee) expRes = &types.QueryBaseFeeResponse{BaseFee: &aux} @@ -1225,7 +1224,7 @@ func (suite *KeeperTestSuite) TestQueryBaseFee() { "pass - nil Base Fee when london hardfork not activated", func() { baseFee := sdk.OneInt().BigInt() - suite.app.FeeMarketKeeper.SetBaseFee(suite.ctx, baseFee) + suite.App.FeeMarketKeeper.SetBaseFee(suite.Ctx, baseFee) expRes = &types.QueryBaseFeeResponse{} }, @@ -1242,13 +1241,13 @@ func (suite *KeeperTestSuite) TestQueryBaseFee() { } for _, tc := range testCases { suite.Run(tc.name, func() { - suite.enableFeemarket = tc.enableFeemarket - suite.enableLondonHF = tc.enableLondonHF + suite.EnableFeemarket = tc.enableFeemarket + suite.EnableLondonHF = tc.enableLondonHF suite.SetupTest() tc.malleate() - res, err := suite.queryClient.BaseFee(suite.ctx.Context(), &types.QueryBaseFeeRequest{}) + res, err := suite.QueryClient.BaseFee(suite.Ctx.Context(), &types.QueryBaseFeeRequest{}) if tc.expPass { suite.Require().NotNil(res) suite.Require().Equal(expRes, res, tc.name) @@ -1258,15 +1257,15 @@ func (suite *KeeperTestSuite) TestQueryBaseFee() { } }) } - suite.enableFeemarket = false - suite.enableLondonHF = true + suite.EnableFeemarket = false + suite.EnableLondonHF = true } func (suite *KeeperTestSuite) TestEthCall() { var req *types.EthCallRequest address := tests.GenerateAddress() - suite.Require().Equal(uint64(0), suite.app.EvmKeeper.GetNonce(suite.ctx, address)) + suite.Require().Equal(uint64(0), suite.App.EvmKeeper.GetNonce(suite.Ctx, address)) supply := sdkmath.NewIntWithDecimal(1000, 18).BigInt() hexBigInt := hexutil.Big(*big.NewInt(1)) @@ -1313,9 +1312,9 @@ func (suite *KeeperTestSuite) TestEthCall() { suite.Require().NoError(err) req = &types.EthCallRequest{Args: args, GasCap: uint64(config.DefaultGasCap)} - params := suite.app.EvmKeeper.GetParams(suite.ctx) + params := suite.App.EvmKeeper.GetParams(suite.Ctx) params.EnableCreate = false - suite.app.EvmKeeper.SetParams(suite.ctx, params) + suite.App.EvmKeeper.SetParams(suite.Ctx, params) }, false, }, @@ -1325,7 +1324,7 @@ func (suite *KeeperTestSuite) TestEthCall() { suite.SetupTest() tc.malleate() - res, err := suite.queryClient.EthCall(suite.ctx, req) + res, err := suite.QueryClient.EthCall(suite.Ctx, req) if tc.expPass { suite.Require().NotNil(res) suite.Require().NoError(err) @@ -1337,7 +1336,7 @@ func (suite *KeeperTestSuite) TestEthCall() { } func (suite *KeeperTestSuite) TestEmptyRequest() { - k := suite.app.EvmKeeper + k := suite.App.EvmKeeper testCases := []struct { name string @@ -1346,61 +1345,61 @@ func (suite *KeeperTestSuite) TestEmptyRequest() { { "Account method", func() (interface{}, error) { - return k.Account(suite.ctx, nil) + return k.Account(suite.Ctx, nil) }, }, { "CosmosAccount method", func() (interface{}, error) { - return k.CosmosAccount(suite.ctx, nil) + return k.CosmosAccount(suite.Ctx, nil) }, }, { "ValidatorAccount method", func() (interface{}, error) { - return k.ValidatorAccount(suite.ctx, nil) + return k.ValidatorAccount(suite.Ctx, nil) }, }, { "Balance method", func() (interface{}, error) { - return k.Balance(suite.ctx, nil) + return k.Balance(suite.Ctx, nil) }, }, { "Storage method", func() (interface{}, error) { - return k.Storage(suite.ctx, nil) + return k.Storage(suite.Ctx, nil) }, }, { "Code method", func() (interface{}, error) { - return k.Code(suite.ctx, nil) + return k.Code(suite.Ctx, nil) }, }, { "EthCall method", func() (interface{}, error) { - return k.EthCall(suite.ctx, nil) + return k.EthCall(suite.Ctx, nil) }, }, { "EstimateGas method", func() (interface{}, error) { - return k.EstimateGas(suite.ctx, nil) + return k.EstimateGas(suite.Ctx, nil) }, }, { "TraceTx method", func() (interface{}, error) { - return k.TraceTx(suite.ctx, nil) + return k.TraceTx(suite.Ctx, nil) }, }, { "TraceBlock method", func() (interface{}, error) { - return k.TraceBlock(suite.ctx, nil) + return k.TraceBlock(suite.Ctx, nil) }, }, } diff --git a/x/evm/keeper/hooks_test.go b/x/evm/keeper/hooks_test.go index b635cf4cd4..d8d0dafd35 100644 --- a/x/evm/keeper/hooks_test.go +++ b/x/evm/keeper/hooks_test.go @@ -61,10 +61,10 @@ func (suite *KeeperTestSuite) TestEvmHooks() { for _, tc := range testCases { suite.SetupTest() hook := tc.setupHook() - suite.app.EvmKeeper.SetHooks(keeper.NewMultiEvmHooks(hook)) + suite.App.EvmKeeper.SetHooks(keeper.NewMultiEvmHooks(hook)) - k := suite.app.EvmKeeper - ctx := suite.ctx + k := suite.App.EvmKeeper + ctx := suite.Ctx txHash := common.BigToHash(big.NewInt(1)) vmdb := statedb.New(ctx, k, statedb.NewTxConfig( common.BytesToHash(ctx.HeaderHash().Bytes()), @@ -75,7 +75,7 @@ func (suite *KeeperTestSuite) TestEvmHooks() { vmdb.AddLog(ðtypes.Log{ Topics: []common.Hash{}, - Address: suite.address, + Address: suite.Address, }) logs := vmdb.Logs() receipt := ðtypes.Receipt{ diff --git a/x/evm/keeper/integration_test.go b/x/evm/keeper/integration_test.go index 77a9220717..bdb02fc968 100644 --- a/x/evm/keeper/integration_test.go +++ b/x/evm/keeper/integration_test.go @@ -130,11 +130,11 @@ var _ = Describe("Feemarket", func() { // setupTestWithContext sets up a test chain with an example Cosmos send msg, // given a local (validator config) and a gloabl (feemarket param) minGasPrice func setupTestWithContext(valMinGasPrice string, minGasPrice sdk.Dec, baseFee sdk.Int) (*ethsecp256k1.PrivKey, banktypes.MsgSend) { - privKey, msg := setupTest(valMinGasPrice + s.denom) + privKey, msg := setupTest(valMinGasPrice + s.Denom) params := types.DefaultParams() params.MinGasPrice = minGasPrice - s.app.FeeMarketKeeper.SetParams(s.ctx, params) - s.app.FeeMarketKeeper.SetBaseFee(s.ctx, baseFee.BigInt()) + s.App.FeeMarketKeeper.SetParams(s.Ctx, params) + s.App.FeeMarketKeeper.SetBaseFee(s.Ctx, baseFee.BigInt()) s.Commit() return privKey, msg @@ -147,16 +147,16 @@ func setupTest(localMinGasPrices string) (*ethsecp256k1.PrivKey, banktypes.MsgSe amount, ok := sdk.NewIntFromString("10000000000000000000") s.Require().True(ok) initBalance := sdk.Coins{sdk.Coin{ - Denom: s.denom, + Denom: s.Denom, Amount: amount, }} - testutil.FundAccount(s.app.BankKeeper, s.ctx, address, initBalance) + testutil.FundAccount(s.App.BankKeeper, s.Ctx, address, initBalance) msg := banktypes.MsgSend{ FromAddress: address.String(), ToAddress: address.String(), Amount: sdk.Coins{sdk.Coin{ - Denom: s.denom, + Denom: s.Denom, Amount: sdk.NewInt(10000), }}, } @@ -198,7 +198,7 @@ func setupChain(localMinGasPricesStr string) { }, ) - s.app = newapp + s.App = newapp s.SetupApp(false) } @@ -208,8 +208,8 @@ func generateKey() (*ethsecp256k1.PrivKey, sdk.AccAddress) { } func getNonce(addressBytes []byte) uint64 { - return s.app.EvmKeeper.GetNonce( - s.ctx, + return s.App.EvmKeeper.GetNonce( + s.Ctx, common.BytesToAddress(addressBytes), ) } @@ -223,7 +223,7 @@ func buildEthTx( gasTipCap *big.Int, accesses *ethtypes.AccessList, ) *evmtypes.MsgEthereumTx { - chainID := s.app.EvmKeeper.ChainID() + chainID := s.App.EvmKeeper.ChainID() from := common.BytesToAddress(priv.PubKey().Address().Bytes()) nonce := getNonce(from.Bytes()) data := make([]byte, 0) @@ -253,7 +253,7 @@ func prepareEthTx(priv *ethsecp256k1.PrivKey, msgEthereumTx *evmtypes.MsgEthereu s.Require().True(ok) builder.SetExtensionOptions(option) - err = msgEthereumTx.Sign(s.ethSigner, tests.NewSigner(priv)) + err = msgEthereumTx.Sign(s.EthSigner, tests.NewSigner(priv)) s.Require().NoError(err) // A valid msg should have empty `From` @@ -264,7 +264,7 @@ func prepareEthTx(priv *ethsecp256k1.PrivKey, msgEthereumTx *evmtypes.MsgEthereu txData, err := evmtypes.UnpackTxData(msgEthereumTx.Data) s.Require().NoError(err) - evmDenom := s.app.EvmKeeper.GetParams(s.ctx).EvmDenom + evmDenom := s.App.EvmKeeper.GetParams(s.Ctx).EvmDenom fees := sdk.Coins{{Denom: evmDenom, Amount: sdk.NewIntFromBigInt(txData.Fee())}} builder.SetFeeAmount(fees) builder.SetGasLimit(msgEthereumTx.GetGas()) @@ -279,13 +279,13 @@ func prepareEthTx(priv *ethsecp256k1.PrivKey, msgEthereumTx *evmtypes.MsgEthereu func checkEthTx(priv *ethsecp256k1.PrivKey, msgEthereumTx *evmtypes.MsgEthereumTx) abci.ResponseCheckTx { bz := prepareEthTx(priv, msgEthereumTx) req := abci.RequestCheckTx{Tx: bz} - res := s.app.BaseApp.CheckTx(req) + res := s.App.BaseApp.CheckTx(req) return res } func deliverEthTx(priv *ethsecp256k1.PrivKey, msgEthereumTx *evmtypes.MsgEthereumTx) abci.ResponseDeliverTx { bz := prepareEthTx(priv, msgEthereumTx) req := abci.RequestDeliverTx{Tx: bz} - res := s.app.BaseApp.DeliverTx(req) + res := s.App.BaseApp.DeliverTx(req) return res } diff --git a/x/evm/keeper/keeper.go b/x/evm/keeper/keeper.go index 90ec6038b4..3014438fa5 100644 --- a/x/evm/keeper/keeper.go +++ b/x/evm/keeper/keeper.go @@ -289,6 +289,7 @@ func (k *Keeper) GetAccountWithoutBalance(ctx sdk.Context, addr common.Address) return &statedb.Account{ Nonce: acct.GetSequence(), CodeHash: codeHash, + // Balance: nil, } } @@ -301,7 +302,7 @@ func (k *Keeper) GetAccountOrEmpty(ctx sdk.Context, addr common.Address) statedb // empty account return statedb.Account{ - Balance: new(big.Int), + // Balance: new(big.Int), CodeHash: types.EmptyCodeHash, } } diff --git a/x/evm/keeper/keeper_test.go b/x/evm/keeper/keeper_test.go index 6478c7d8b3..b6db464d86 100644 --- a/x/evm/keeper/keeper_test.go +++ b/x/evm/keeper/keeper_test.go @@ -2,77 +2,32 @@ package keeper_test import ( _ "embed" - "encoding/json" - "math" "math/big" "os" "testing" - "time" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + "github.com/stretchr/testify/suite" sdkmath "cosmossdk.io/math" - "github.com/stretchr/testify/require" - "github.com/stretchr/testify/suite" - "cosmossdk.io/simapp" - tmjson "github.com/cometbft/cometbft/libs/json" - "github.com/cosmos/cosmos-sdk/baseapp" - "github.com/cosmos/cosmos-sdk/client" - "github.com/cosmos/cosmos-sdk/codec" - "github.com/cosmos/cosmos-sdk/crypto/keyring" - sdk "github.com/cosmos/cosmos-sdk/types" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" - feemarkettypes "github.com/evmos/ethermint/x/feemarket/types" + "github.com/evmos/ethermint/x/evm/statedb" + "github.com/evmos/ethermint/x/evm/testutil" - "github.com/evmos/ethermint/app" - "github.com/evmos/ethermint/crypto/ethsecp256k1" - "github.com/evmos/ethermint/encoding" - "github.com/evmos/ethermint/server/config" - "github.com/evmos/ethermint/tests" ethermint "github.com/evmos/ethermint/types" - "github.com/evmos/ethermint/x/evm/statedb" "github.com/evmos/ethermint/x/evm/types" - evmtypes "github.com/evmos/ethermint/x/evm/types" "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - ethtypes "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/params" abci "github.com/cometbft/cometbft/abci/types" - "github.com/cometbft/cometbft/crypto/tmhash" - tmproto "github.com/cometbft/cometbft/proto/tendermint/types" - tmversion "github.com/cometbft/cometbft/proto/tendermint/version" - "github.com/cometbft/cometbft/version" ) var testTokens = sdkmath.NewIntWithDecimal(1000, 18) type KeeperTestSuite struct { - suite.Suite - - ctx sdk.Context - app *app.EthermintApp - queryClient types.QueryClient - address common.Address - consAddress sdk.ConsAddress - - // for generate test tx - clientCtx client.Context - ethSigner ethtypes.Signer - - appCodec codec.Codec - signer keyring.Signer - - enableFeemarket bool - enableLondonHF bool - mintFeeCollector bool - denom string + testutil.TestSuite } var s *KeeperTestSuite @@ -82,8 +37,8 @@ func TestKeeperTestSuite(t *testing.T) { t.Skip("Skipping Gingko Test") } s = new(KeeperTestSuite) - s.enableFeemarket = false - s.enableLondonHF = true + s.EnableFeemarket = false + s.EnableLondonHF = true suite.Run(t, s) // Run Ginkgo integration tests @@ -91,339 +46,6 @@ func TestKeeperTestSuite(t *testing.T) { RunSpecs(t, "Keeper Suite") } -func (suite *KeeperTestSuite) SetupTest() { - checkTx := false - suite.app = app.Setup(checkTx, nil) - suite.SetupApp(checkTx) -} - -func (suite *KeeperTestSuite) SetupTestWithT(t require.TestingT) { - checkTx := false - suite.app = app.Setup(checkTx, nil) - suite.SetupAppWithT(checkTx, t) -} - -func (suite *KeeperTestSuite) SetupApp(checkTx bool) { - suite.SetupAppWithT(checkTx, suite.T()) -} - -// SetupApp setup test environment, it uses`require.TestingT` to support both `testing.T` and `testing.B`. -func (suite *KeeperTestSuite) SetupAppWithT(checkTx bool, t require.TestingT) { - // account key, use a constant account to keep unit test deterministic. - ecdsaPriv, err := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") - require.NoError(t, err) - priv := ðsecp256k1.PrivKey{ - Key: crypto.FromECDSA(ecdsaPriv), - } - suite.address = common.BytesToAddress(priv.PubKey().Address().Bytes()) - suite.signer = tests.NewSigner(priv) - - // consensus key - priv, err = ethsecp256k1.GenerateKey() - require.NoError(t, err) - suite.consAddress = sdk.ConsAddress(priv.PubKey().Address()) - - suite.app = app.Setup(checkTx, func(app *app.EthermintApp, genesis simapp.GenesisState) simapp.GenesisState { - feemarketGenesis := feemarkettypes.DefaultGenesisState() - if suite.enableFeemarket { - feemarketGenesis.Params.EnableHeight = 1 - feemarketGenesis.Params.NoBaseFee = false - } else { - feemarketGenesis.Params.NoBaseFee = true - } - genesis[feemarkettypes.ModuleName] = app.AppCodec().MustMarshalJSON(feemarketGenesis) - if !suite.enableLondonHF { - evmGenesis := types.DefaultGenesisState() - maxInt := sdkmath.NewInt(math.MaxInt64) - evmGenesis.Params.ChainConfig.LondonBlock = &maxInt - evmGenesis.Params.ChainConfig.ArrowGlacierBlock = &maxInt - evmGenesis.Params.ChainConfig.GrayGlacierBlock = &maxInt - evmGenesis.Params.ChainConfig.MergeNetsplitBlock = &maxInt - evmGenesis.Params.ChainConfig.ShanghaiBlock = &maxInt - evmGenesis.Params.ChainConfig.CancunBlock = &maxInt - genesis[types.ModuleName] = app.AppCodec().MustMarshalJSON(evmGenesis) - } - return genesis - }) - - if suite.mintFeeCollector { - // mint some coin to fee collector - coins := sdk.NewCoins(sdk.NewCoin(types.DefaultEVMDenom, sdkmath.NewInt(int64(params.TxGas)-1))) - genesisState := app.NewTestGenesisState(suite.app.AppCodec()) - balances := []banktypes.Balance{ - { - Address: suite.app.AccountKeeper.GetModuleAddress(authtypes.FeeCollectorName).String(), - Coins: coins, - }, - } - var bankGenesis banktypes.GenesisState - suite.app.AppCodec().MustUnmarshalJSON(genesisState[banktypes.ModuleName], &bankGenesis) - // Update balances and total supply - bankGenesis.Balances = append(bankGenesis.Balances, balances...) - bankGenesis.Supply = bankGenesis.Supply.Add(coins...) - genesisState[banktypes.ModuleName] = suite.app.AppCodec().MustMarshalJSON(&bankGenesis) - - // we marshal the genesisState of all module to a byte array - stateBytes, err := tmjson.MarshalIndent(genesisState, "", " ") - require.NoError(t, err) - - // Initialize the chain - suite.app.InitChain( - abci.RequestInitChain{ - ChainId: "ethermint_9000-1", - Validators: []abci.ValidatorUpdate{}, - ConsensusParams: app.DefaultConsensusParams, - AppStateBytes: stateBytes, - }, - ) - } - - suite.ctx = suite.app.BaseApp.NewContext(checkTx, tmproto.Header{ - Height: 1, - ChainID: "ethermint_9000-1", - Time: time.Now().UTC(), - ProposerAddress: suite.consAddress.Bytes(), - Version: tmversion.Consensus{ - Block: version.BlockProtocol, - }, - LastBlockId: tmproto.BlockID{ - Hash: tmhash.Sum([]byte("block_id")), - PartSetHeader: tmproto.PartSetHeader{ - Total: 11, - Hash: tmhash.Sum([]byte("partset_header")), - }, - }, - AppHash: tmhash.Sum([]byte("app")), - DataHash: tmhash.Sum([]byte("data")), - EvidenceHash: tmhash.Sum([]byte("evidence")), - ValidatorsHash: tmhash.Sum([]byte("validators")), - NextValidatorsHash: tmhash.Sum([]byte("next_validators")), - ConsensusHash: tmhash.Sum([]byte("consensus")), - LastResultsHash: tmhash.Sum([]byte("last_result")), - }) - - queryHelper := baseapp.NewQueryServerTestHelper(suite.ctx, suite.app.InterfaceRegistry()) - types.RegisterQueryServer(queryHelper, suite.app.EvmKeeper) - suite.queryClient = types.NewQueryClient(queryHelper) - - acc := ðermint.EthAccount{ - BaseAccount: authtypes.NewBaseAccount(sdk.AccAddress(suite.address.Bytes()), nil, 0, 0), - CodeHash: common.BytesToHash(crypto.Keccak256(nil)).String(), - } - - suite.app.AccountKeeper.SetAccount(suite.ctx, acc) - - valAddr := sdk.ValAddress(suite.address.Bytes()) - validator, err := stakingtypes.NewValidator(valAddr, priv.PubKey(), stakingtypes.Description{}) - require.NoError(t, err) - err = suite.app.StakingKeeper.SetValidatorByConsAddr(suite.ctx, validator) - require.NoError(t, err) - err = suite.app.StakingKeeper.SetValidatorByConsAddr(suite.ctx, validator) - require.NoError(t, err) - suite.app.StakingKeeper.SetValidator(suite.ctx, validator) - - encodingConfig := encoding.MakeConfig(app.ModuleBasics) - suite.clientCtx = client.Context{}.WithTxConfig(encodingConfig.TxConfig) - suite.ethSigner = ethtypes.LatestSignerForChainID(suite.app.EvmKeeper.ChainID()) - suite.appCodec = encodingConfig.Codec - suite.denom = evmtypes.DefaultEVMDenom -} - -func (suite *KeeperTestSuite) EvmDenom() string { - ctx := sdk.WrapSDKContext(suite.ctx) - rsp, _ := suite.queryClient.Params(ctx, &types.QueryParamsRequest{}) - return rsp.Params.EvmDenom -} - -// Commit and begin new block -func (suite *KeeperTestSuite) Commit() { - _ = suite.app.Commit() - header := suite.ctx.BlockHeader() - header.Height += 1 - suite.app.BeginBlock(abci.RequestBeginBlock{ - Header: header, - }) - - // update ctx - suite.ctx = suite.app.BaseApp.NewContext(false, header) - - queryHelper := baseapp.NewQueryServerTestHelper(suite.ctx, suite.app.InterfaceRegistry()) - types.RegisterQueryServer(queryHelper, suite.app.EvmKeeper) - suite.queryClient = types.NewQueryClient(queryHelper) -} - -func (suite *KeeperTestSuite) StateDB() *statedb.StateDB { - return statedb.New(suite.ctx, suite.app.EvmKeeper, statedb.NewEmptyTxConfig(common.BytesToHash(suite.ctx.HeaderHash().Bytes()))) -} - -// DeployTestContract deploy a test erc20 contract and returns the contract address -func (suite *KeeperTestSuite) DeployTestContract(t require.TestingT, owner common.Address, supply *big.Int) common.Address { - ctx := sdk.WrapSDKContext(suite.ctx) - chainID := suite.app.EvmKeeper.ChainID() - - ctorArgs, err := types.ERC20Contract.ABI.Pack("", owner, supply) - require.NoError(t, err) - - nonce := suite.app.EvmKeeper.GetNonce(suite.ctx, suite.address) - - data := append(types.ERC20Contract.Bin, ctorArgs...) - args, err := json.Marshal(&types.TransactionArgs{ - From: &suite.address, - Data: (*hexutil.Bytes)(&data), - }) - require.NoError(t, err) - res, err := suite.queryClient.EstimateGas(ctx, &types.EthCallRequest{ - Args: args, - GasCap: uint64(config.DefaultGasCap), - ProposerAddress: suite.ctx.BlockHeader().ProposerAddress, - }) - require.NoError(t, err) - - var erc20DeployTx *types.MsgEthereumTx - if suite.enableFeemarket { - erc20DeployTx = types.NewTxContract( - chainID, - nonce, - nil, // amount - res.Gas, // gasLimit - nil, // gasPrice - suite.app.FeeMarketKeeper.GetBaseFee(suite.ctx), - big.NewInt(1), - data, // input - ðtypes.AccessList{}, // accesses - ) - } else { - erc20DeployTx = types.NewTxContract( - chainID, - nonce, - nil, // amount - res.Gas, // gasLimit - nil, // gasPrice - nil, nil, - data, // input - nil, // accesses - ) - } - - erc20DeployTx.From = suite.address.Hex() - err = erc20DeployTx.Sign(ethtypes.LatestSignerForChainID(chainID), suite.signer) - require.NoError(t, err) - rsp, err := suite.app.EvmKeeper.EthereumTx(ctx, erc20DeployTx) - require.NoError(t, err) - require.Empty(t, rsp.VmError) - return crypto.CreateAddress(suite.address, nonce) -} - -func (suite *KeeperTestSuite) TransferERC20Token(t require.TestingT, contractAddr, from, to common.Address, amount *big.Int) *types.MsgEthereumTx { - ctx := sdk.WrapSDKContext(suite.ctx) - chainID := suite.app.EvmKeeper.ChainID() - - transferData, err := types.ERC20Contract.ABI.Pack("transfer", to, amount) - require.NoError(t, err) - args, err := json.Marshal(&types.TransactionArgs{To: &contractAddr, From: &from, Data: (*hexutil.Bytes)(&transferData)}) - require.NoError(t, err) - res, err := suite.queryClient.EstimateGas(ctx, &types.EthCallRequest{ - Args: args, - GasCap: 25_000_000, - ProposerAddress: suite.ctx.BlockHeader().ProposerAddress, - }) - require.NoError(t, err) - - nonce := suite.app.EvmKeeper.GetNonce(suite.ctx, suite.address) - - var ercTransferTx *types.MsgEthereumTx - if suite.enableFeemarket { - ercTransferTx = types.NewTx( - chainID, - nonce, - &contractAddr, - nil, - res.Gas, - nil, - suite.app.FeeMarketKeeper.GetBaseFee(suite.ctx), - big.NewInt(1), - transferData, - ðtypes.AccessList{}, // accesses - ) - } else { - ercTransferTx = types.NewTx( - chainID, - nonce, - &contractAddr, - nil, - res.Gas, - nil, - nil, nil, - transferData, - nil, - ) - } - - ercTransferTx.From = suite.address.Hex() - err = ercTransferTx.Sign(ethtypes.LatestSignerForChainID(chainID), suite.signer) - require.NoError(t, err) - rsp, err := suite.app.EvmKeeper.EthereumTx(ctx, ercTransferTx) - require.NoError(t, err) - require.Empty(t, rsp.VmError) - return ercTransferTx -} - -// DeployTestMessageCall deploy a test erc20 contract and returns the contract address -func (suite *KeeperTestSuite) DeployTestMessageCall(t require.TestingT) common.Address { - ctx := sdk.WrapSDKContext(suite.ctx) - chainID := suite.app.EvmKeeper.ChainID() - - data := types.TestMessageCall.Bin - args, err := json.Marshal(&types.TransactionArgs{ - From: &suite.address, - Data: (*hexutil.Bytes)(&data), - }) - require.NoError(t, err) - - res, err := suite.queryClient.EstimateGas(ctx, &types.EthCallRequest{ - Args: args, - GasCap: uint64(config.DefaultGasCap), - ProposerAddress: suite.ctx.BlockHeader().ProposerAddress, - }) - require.NoError(t, err) - - nonce := suite.app.EvmKeeper.GetNonce(suite.ctx, suite.address) - - var erc20DeployTx *types.MsgEthereumTx - if suite.enableFeemarket { - erc20DeployTx = types.NewTxContract( - chainID, - nonce, - nil, // amount - res.Gas, // gasLimit - nil, // gasPrice - suite.app.FeeMarketKeeper.GetBaseFee(suite.ctx), - big.NewInt(1), - data, // input - ðtypes.AccessList{}, // accesses - ) - } else { - erc20DeployTx = types.NewTxContract( - chainID, - nonce, - nil, // amount - res.Gas, // gasLimit - nil, // gasPrice - nil, nil, - data, // input - nil, // accesses - ) - } - - erc20DeployTx.From = suite.address.Hex() - err = erc20DeployTx.Sign(ethtypes.LatestSignerForChainID(chainID), suite.signer) - require.NoError(t, err) - rsp, err := suite.app.EvmKeeper.EthereumTx(ctx, erc20DeployTx) - require.NoError(t, err) - require.Empty(t, rsp.VmError) - return crypto.CreateAddress(suite.address, nonce) -} - func (suite *KeeperTestSuite) TestBaseFee() { testCases := []struct { name string @@ -439,18 +61,18 @@ func (suite *KeeperTestSuite) TestBaseFee() { for _, tc := range testCases { suite.Run(tc.name, func() { - suite.enableFeemarket = tc.enableFeemarket - suite.enableLondonHF = tc.enableLondonHF + suite.EnableFeemarket = tc.enableFeemarket + suite.EnableLondonHF = tc.enableLondonHF suite.SetupTest() - suite.app.EvmKeeper.BeginBlock(suite.ctx, abci.RequestBeginBlock{}) - params := suite.app.EvmKeeper.GetParams(suite.ctx) - ethCfg := params.ChainConfig.EthereumConfig(suite.app.EvmKeeper.ChainID()) - baseFee := suite.app.EvmKeeper.GetBaseFee(suite.ctx, ethCfg) + suite.App.EvmKeeper.BeginBlock(suite.Ctx, abci.RequestBeginBlock{}) + params := suite.App.EvmKeeper.GetParams(suite.Ctx) + ethCfg := params.ChainConfig.EthereumConfig(suite.App.EvmKeeper.ChainID()) + baseFee := suite.App.EvmKeeper.GetBaseFee(suite.Ctx, ethCfg) suite.Require().Equal(tc.expectBaseFee, baseFee) }) } - suite.enableFeemarket = false - suite.enableLondonHF = true + suite.EnableFeemarket = false + suite.EnableLondonHF = true } func (suite *KeeperTestSuite) TestGetAccountStorage() { @@ -468,7 +90,7 @@ func (suite *KeeperTestSuite) TestGetAccountStorage() { "Two accounts - one contract (with storage), one wallet", func() { supply := big.NewInt(100) - suite.DeployTestContract(suite.T(), suite.address, supply) + suite.DeployTestContract(suite.T(), suite.Address, supply) }, []int{2, 0}, }, @@ -479,7 +101,7 @@ func (suite *KeeperTestSuite) TestGetAccountStorage() { suite.SetupTest() tc.malleate() i := 0 - suite.app.AccountKeeper.IterateAccounts(suite.ctx, func(account authtypes.AccountI) bool { + suite.App.AccountKeeper.IterateAccounts(suite.Ctx, func(account authtypes.AccountI) bool { ethAccount, ok := account.(ethermint.EthAccountI) if !ok { // ignore non EthAccounts @@ -487,7 +109,7 @@ func (suite *KeeperTestSuite) TestGetAccountStorage() { } addr := ethAccount.EthAddress() - storage := suite.app.EvmKeeper.GetAccountStorage(suite.ctx, addr) + storage := suite.App.EvmKeeper.GetAccountStorage(suite.Ctx, addr) suite.Require().Equal(tc.expRes[i], len(storage)) i++ @@ -499,12 +121,12 @@ func (suite *KeeperTestSuite) TestGetAccountStorage() { func (suite *KeeperTestSuite) TestGetAccountOrEmpty() { empty := statedb.Account{ - Balance: new(big.Int), + // Balance: new(big.Int), CodeHash: types.EmptyCodeHash, } supply := big.NewInt(100) - contractAddr := suite.DeployTestContract(suite.T(), suite.address, supply) + contractAddr := suite.DeployTestContract(suite.T(), suite.Address, supply) testCases := []struct { name string @@ -525,7 +147,7 @@ func (suite *KeeperTestSuite) TestGetAccountOrEmpty() { for _, tc := range testCases { suite.Run(tc.name, func() { - res := suite.app.EvmKeeper.GetAccountOrEmpty(suite.ctx, tc.addr) + res := suite.App.EvmKeeper.GetAccountOrEmpty(suite.Ctx, tc.addr) if tc.expEmpty { suite.Require().Equal(empty, res) } else { diff --git a/x/evm/keeper/msg_server_test.go b/x/evm/keeper/msg_server_test.go index 4a8c86cefa..756426442b 100644 --- a/x/evm/keeper/msg_server_test.go +++ b/x/evm/keeper/msg_server_test.go @@ -31,7 +31,7 @@ func (suite *KeeperTestSuite) TestEthereumTx() { "Deploy contract tx - insufficient gas", func() { msg, err = suite.createContractMsgTx( - vmdb.GetNonce(suite.address), + vmdb.GetNonce(suite.Address), signer, chainCfg, big.NewInt(1), @@ -44,11 +44,11 @@ func (suite *KeeperTestSuite) TestEthereumTx() { "Transfer funds tx", func() { msg, _, err = newEthMsgTx( - vmdb.GetNonce(suite.address), - suite.ctx.BlockHeight(), - suite.address, + vmdb.GetNonce(suite.Address), + suite.Ctx.BlockHeight(), + suite.Address, chainCfg, - suite.signer, + suite.Signer, signer, ethtypes.AccessListTxType, nil, @@ -64,13 +64,13 @@ func (suite *KeeperTestSuite) TestEthereumTx() { for _, tc := range testCases { suite.Run(tc.name, func() { suite.SetupTest() - keeperParams := suite.app.EvmKeeper.GetParams(suite.ctx) - chainCfg = keeperParams.ChainConfig.EthereumConfig(suite.app.EvmKeeper.ChainID()) - signer = ethtypes.LatestSignerForChainID(suite.app.EvmKeeper.ChainID()) + keeperParams := suite.App.EvmKeeper.GetParams(suite.Ctx) + chainCfg = keeperParams.ChainConfig.EthereumConfig(suite.App.EvmKeeper.ChainID()) + signer = ethtypes.LatestSignerForChainID(suite.App.EvmKeeper.ChainID()) vmdb = suite.StateDB() tc.malleate() - res, err := suite.app.EvmKeeper.EthereumTx(suite.ctx, msg) + res, err := suite.App.EvmKeeper.EthereumTx(suite.Ctx, msg) if tc.expErr { suite.Require().Error(err) return @@ -106,7 +106,7 @@ func (suite *KeeperTestSuite) TestUpdateParams() { for _, tc := range testCases { tc := tc suite.Run("MsgUpdateParams", func() { - _, err := suite.app.EvmKeeper.UpdateParams(suite.ctx, tc.request) + _, err := suite.App.EvmKeeper.UpdateParams(suite.Ctx, tc.request) if tc.expectErr { suite.Require().Error(err) } else { diff --git a/x/evm/keeper/params_benchmark_test.go b/x/evm/keeper/params_benchmark_test.go index b42529e482..8100eb9669 100644 --- a/x/evm/keeper/params_benchmark_test.go +++ b/x/evm/keeper/params_benchmark_test.go @@ -14,7 +14,7 @@ func BenchmarkSetParams(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - _ = suite.app.EvmKeeper.SetParams(suite.ctx, params) + _ = suite.App.EvmKeeper.SetParams(suite.Ctx, params) } } @@ -25,6 +25,6 @@ func BenchmarkGetParams(b *testing.B) { b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { - _ = suite.app.EvmKeeper.GetParams(suite.ctx) + _ = suite.App.EvmKeeper.GetParams(suite.Ctx) } } diff --git a/x/evm/keeper/params_test.go b/x/evm/keeper/params_test.go index 38fc687f10..796434084d 100644 --- a/x/evm/keeper/params_test.go +++ b/x/evm/keeper/params_test.go @@ -18,8 +18,8 @@ import ( ) func (suite *KeeperTestSuite) TestParams() { - params := suite.app.EvmKeeper.GetParams(suite.ctx) - suite.app.EvmKeeper.SetParams(suite.ctx, params) + params := suite.App.EvmKeeper.GetParams(suite.Ctx) + suite.App.EvmKeeper.SetParams(suite.Ctx, params) testCases := []struct { name string paramsFun func() interface{} @@ -32,7 +32,7 @@ func (suite *KeeperTestSuite) TestParams() { return types.DefaultParams() }, func() interface{} { - return suite.app.EvmKeeper.GetParams(suite.ctx) + return suite.App.EvmKeeper.GetParams(suite.Ctx) }, true, }, @@ -40,11 +40,11 @@ func (suite *KeeperTestSuite) TestParams() { "success - EvmDenom param is set to \"inj\" and can be retrieved correctly", func() interface{} { params.EvmDenom = "inj" - suite.app.EvmKeeper.SetParams(suite.ctx, params) + suite.App.EvmKeeper.SetParams(suite.Ctx, params) return params.EvmDenom }, func() interface{} { - evmParams := suite.app.EvmKeeper.GetParams(suite.ctx) + evmParams := suite.App.EvmKeeper.GetParams(suite.Ctx) return evmParams.GetEvmDenom() }, true, @@ -53,11 +53,11 @@ func (suite *KeeperTestSuite) TestParams() { "success - Check EnableCreate param is set to false and can be retrieved correctly", func() interface{} { params.EnableCreate = false - suite.app.EvmKeeper.SetParams(suite.ctx, params) + suite.App.EvmKeeper.SetParams(suite.Ctx, params) return params.EnableCreate }, func() interface{} { - evmParams := suite.app.EvmKeeper.GetParams(suite.ctx) + evmParams := suite.App.EvmKeeper.GetParams(suite.Ctx) return evmParams.GetEnableCreate() }, true, @@ -66,11 +66,11 @@ func (suite *KeeperTestSuite) TestParams() { "success - Check EnableCall param is set to false and can be retrieved correctly", func() interface{} { params.EnableCall = false - suite.app.EvmKeeper.SetParams(suite.ctx, params) + suite.App.EvmKeeper.SetParams(suite.Ctx, params) return params.EnableCall }, func() interface{} { - evmParams := suite.app.EvmKeeper.GetParams(suite.ctx) + evmParams := suite.App.EvmKeeper.GetParams(suite.Ctx) return evmParams.GetEnableCall() }, true, @@ -79,11 +79,11 @@ func (suite *KeeperTestSuite) TestParams() { "success - Check AllowUnprotectedTxs param is set to false and can be retrieved correctly", func() interface{} { params.AllowUnprotectedTxs = false - suite.app.EvmKeeper.SetParams(suite.ctx, params) + suite.App.EvmKeeper.SetParams(suite.Ctx, params) return params.AllowUnprotectedTxs }, func() interface{} { - evmParams := suite.app.EvmKeeper.GetParams(suite.ctx) + evmParams := suite.App.EvmKeeper.GetParams(suite.Ctx) return evmParams.GetAllowUnprotectedTxs() }, true, @@ -92,11 +92,11 @@ func (suite *KeeperTestSuite) TestParams() { "success - Check ChainConfig param is set to the default value and can be retrieved correctly", func() interface{} { params.ChainConfig = types.DefaultChainConfig() - suite.app.EvmKeeper.SetParams(suite.ctx, params) + suite.App.EvmKeeper.SetParams(suite.Ctx, params) return params.ChainConfig }, func() interface{} { - evmParams := suite.app.EvmKeeper.GetParams(suite.ctx) + evmParams := suite.App.EvmKeeper.GetParams(suite.Ctx) return evmParams.GetChainConfig() }, true, @@ -118,7 +118,7 @@ func (suite *KeeperTestSuite) TestLegacyParamsKeyTableRegistration() { paramStoreKey := sdk.NewKVStoreKey(paramtypes.ModuleName) paramStoreTKey := sdk.NewTransientStoreKey(paramtypes.TStoreKey) ctx := legacytestutil.NewDBContext([]storetypes.StoreKey{storeKey, paramStoreKey}, []storetypes.StoreKey{tKey, paramStoreTKey}) - ak := suite.app.AccountKeeper + ak := suite.App.AccountKeeper // paramspace used only for setting legacy parameters (not given to keeper) setParamSpace := paramtypes.NewSubspace( @@ -178,7 +178,7 @@ func (suite *KeeperTestSuite) TestRenamedFieldReturnsProperValueForLegacyParams( paramStoreKey := sdk.NewKVStoreKey(paramtypes.ModuleName) paramStoreTKey := sdk.NewTransientStoreKey(paramtypes.TStoreKey) ctx := legacytestutil.NewDBContext([]storetypes.StoreKey{storeKey, paramStoreKey}, []storetypes.StoreKey{tKey, paramStoreTKey}) - ak := suite.app.AccountKeeper + ak := suite.App.AccountKeeper // paramspace used only for setting legacy parameters (not given to keeper) legacyParamstore := paramtypes.NewSubspace( @@ -223,10 +223,11 @@ func (suite *KeeperTestSuite) TestNilLegacyParamsDoNotPanic() { cdc := encCfg.Codec storeKey := sdk.NewKVStoreKey(types.ModuleName) tKey := sdk.NewTransientStoreKey(types.TransientKey) + paramStoreKey := sdk.NewKVStoreKey(paramtypes.ModuleName) paramStoreTKey := sdk.NewTransientStoreKey(paramtypes.TStoreKey) ctx := legacytestutil.NewDBContext([]storetypes.StoreKey{storeKey, paramStoreKey}, []storetypes.StoreKey{tKey, paramStoreTKey}) - ak := suite.app.AccountKeeper + ak := suite.App.AccountKeeper subspace := paramtypes.NewSubspace( cdc, diff --git a/x/evm/keeper/state_transition_benchmark_test.go b/x/evm/keeper/state_transition_benchmark_test.go index 77df3af9ef..2fae5d4c35 100644 --- a/x/evm/keeper/state_transition_benchmark_test.go +++ b/x/evm/keeper/state_transition_benchmark_test.go @@ -11,6 +11,7 @@ import ( "github.com/ethereum/go-ethereum/core" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/params" + "github.com/evmos/ethermint/x/evm/testutil" evmtypes "github.com/evmos/ethermint/x/evm/types" "github.com/stretchr/testify/require" ) @@ -156,25 +157,29 @@ func newNativeMessage( } func BenchmarkApplyTransaction(b *testing.B) { - suite := KeeperTestSuite{enableLondonHF: true} + suite := KeeperTestSuite{ + TestSuite: testutil.TestSuite{ + EnableLondonHF: true, + }, + } suite.SetupTestWithT(b) - ethSigner := ethtypes.LatestSignerForChainID(suite.app.EvmKeeper.ChainID()) + ethSigner := ethtypes.LatestSignerForChainID(suite.App.EvmKeeper.ChainID()) b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { b.StopTimer() tx, err := newSignedEthTx(templateAccessListTx, - suite.app.EvmKeeper.GetNonce(suite.ctx, suite.address), - sdk.AccAddress(suite.address.Bytes()), - suite.signer, + suite.App.EvmKeeper.GetNonce(suite.Ctx, suite.Address), + sdk.AccAddress(suite.Address.Bytes()), + suite.Signer, ethSigner, ) require.NoError(b, err) b.StartTimer() - resp, err := suite.app.EvmKeeper.ApplyTransaction(suite.ctx, tx) + resp, err := suite.App.EvmKeeper.ApplyTransaction(suite.Ctx, tx) b.StopTimer() require.NoError(b, err) @@ -183,25 +188,29 @@ func BenchmarkApplyTransaction(b *testing.B) { } func BenchmarkApplyTransactionWithLegacyTx(b *testing.B) { - suite := KeeperTestSuite{enableLondonHF: true} + suite := KeeperTestSuite{ + TestSuite: testutil.TestSuite{ + EnableLondonHF: true, + }, + } suite.SetupTestWithT(b) - ethSigner := ethtypes.LatestSignerForChainID(suite.app.EvmKeeper.ChainID()) + ethSigner := ethtypes.LatestSignerForChainID(suite.App.EvmKeeper.ChainID()) b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { b.StopTimer() tx, err := newSignedEthTx(templateLegacyTx, - suite.app.EvmKeeper.GetNonce(suite.ctx, suite.address), - sdk.AccAddress(suite.address.Bytes()), - suite.signer, + suite.App.EvmKeeper.GetNonce(suite.Ctx, suite.Address), + sdk.AccAddress(suite.Address.Bytes()), + suite.Signer, ethSigner, ) require.NoError(b, err) b.StartTimer() - resp, err := suite.app.EvmKeeper.ApplyTransaction(suite.ctx, tx) + resp, err := suite.App.EvmKeeper.ApplyTransaction(suite.Ctx, tx) b.StopTimer() require.NoError(b, err) @@ -210,25 +219,30 @@ func BenchmarkApplyTransactionWithLegacyTx(b *testing.B) { } func BenchmarkApplyTransactionWithDynamicFeeTx(b *testing.B) { - suite := KeeperTestSuite{enableFeemarket: true, enableLondonHF: true} + suite := KeeperTestSuite{ + TestSuite: testutil.TestSuite{ + EnableFeemarket: true, + EnableLondonHF: true, + }, + } suite.SetupTestWithT(b) - ethSigner := ethtypes.LatestSignerForChainID(suite.app.EvmKeeper.ChainID()) + ethSigner := ethtypes.LatestSignerForChainID(suite.App.EvmKeeper.ChainID()) b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { b.StopTimer() tx, err := newSignedEthTx(templateDynamicFeeTx, - suite.app.EvmKeeper.GetNonce(suite.ctx, suite.address), - sdk.AccAddress(suite.address.Bytes()), - suite.signer, + suite.App.EvmKeeper.GetNonce(suite.Ctx, suite.Address), + sdk.AccAddress(suite.Address.Bytes()), + suite.Signer, ethSigner, ) require.NoError(b, err) b.StartTimer() - resp, err := suite.app.EvmKeeper.ApplyTransaction(suite.ctx, tx) + resp, err := suite.App.EvmKeeper.ApplyTransaction(suite.Ctx, tx) b.StopTimer() require.NoError(b, err) @@ -237,12 +251,16 @@ func BenchmarkApplyTransactionWithDynamicFeeTx(b *testing.B) { } func BenchmarkApplyMessage(b *testing.B) { - suite := KeeperTestSuite{enableLondonHF: true} + suite := KeeperTestSuite{ + TestSuite: testutil.TestSuite{ + EnableLondonHF: true, + }, + } suite.SetupTestWithT(b) - params := suite.app.EvmKeeper.GetParams(suite.ctx) - ethCfg := params.ChainConfig.EthereumConfig(suite.app.EvmKeeper.ChainID()) - signer := ethtypes.LatestSignerForChainID(suite.app.EvmKeeper.ChainID()) + params := suite.App.EvmKeeper.GetParams(suite.Ctx) + ethCfg := params.ChainConfig.EthereumConfig(suite.App.EvmKeeper.ChainID()) + signer := ethtypes.LatestSignerForChainID(suite.App.EvmKeeper.ChainID()) b.ResetTimer() b.ReportAllocs() @@ -250,11 +268,11 @@ func BenchmarkApplyMessage(b *testing.B) { b.StopTimer() m, err := newNativeMessage( - suite.app.EvmKeeper.GetNonce(suite.ctx, suite.address), - suite.ctx.BlockHeight(), - suite.address, + suite.App.EvmKeeper.GetNonce(suite.Ctx, suite.Address), + suite.Ctx.BlockHeight(), + suite.Address, ethCfg, - suite.signer, + suite.Signer, signer, ethtypes.AccessListTxType, nil, @@ -263,7 +281,7 @@ func BenchmarkApplyMessage(b *testing.B) { require.NoError(b, err) b.StartTimer() - resp, err := suite.app.EvmKeeper.ApplyMessage(suite.ctx, m, nil, true) + resp, err := suite.App.EvmKeeper.ApplyMessage(suite.Ctx, m, nil, true) b.StopTimer() require.NoError(b, err) @@ -272,12 +290,16 @@ func BenchmarkApplyMessage(b *testing.B) { } func BenchmarkApplyMessageWithLegacyTx(b *testing.B) { - suite := KeeperTestSuite{enableLondonHF: true} + suite := KeeperTestSuite{ + TestSuite: testutil.TestSuite{ + EnableLondonHF: true, + }, + } suite.SetupTestWithT(b) - params := suite.app.EvmKeeper.GetParams(suite.ctx) - ethCfg := params.ChainConfig.EthereumConfig(suite.app.EvmKeeper.ChainID()) - signer := ethtypes.LatestSignerForChainID(suite.app.EvmKeeper.ChainID()) + params := suite.App.EvmKeeper.GetParams(suite.Ctx) + ethCfg := params.ChainConfig.EthereumConfig(suite.App.EvmKeeper.ChainID()) + signer := ethtypes.LatestSignerForChainID(suite.App.EvmKeeper.ChainID()) b.ResetTimer() b.ReportAllocs() @@ -285,11 +307,11 @@ func BenchmarkApplyMessageWithLegacyTx(b *testing.B) { b.StopTimer() m, err := newNativeMessage( - suite.app.EvmKeeper.GetNonce(suite.ctx, suite.address), - suite.ctx.BlockHeight(), - suite.address, + suite.App.EvmKeeper.GetNonce(suite.Ctx, suite.Address), + suite.Ctx.BlockHeight(), + suite.Address, ethCfg, - suite.signer, + suite.Signer, signer, ethtypes.LegacyTxType, nil, @@ -298,7 +320,7 @@ func BenchmarkApplyMessageWithLegacyTx(b *testing.B) { require.NoError(b, err) b.StartTimer() - resp, err := suite.app.EvmKeeper.ApplyMessage(suite.ctx, m, nil, true) + resp, err := suite.App.EvmKeeper.ApplyMessage(suite.Ctx, m, nil, true) b.StopTimer() require.NoError(b, err) @@ -307,12 +329,17 @@ func BenchmarkApplyMessageWithLegacyTx(b *testing.B) { } func BenchmarkApplyMessageWithDynamicFeeTx(b *testing.B) { - suite := KeeperTestSuite{enableFeemarket: true, enableLondonHF: true} + suite := KeeperTestSuite{ + TestSuite: testutil.TestSuite{ + EnableFeemarket: true, + EnableLondonHF: true, + }, + } suite.SetupTestWithT(b) - params := suite.app.EvmKeeper.GetParams(suite.ctx) - ethCfg := params.ChainConfig.EthereumConfig(suite.app.EvmKeeper.ChainID()) - signer := ethtypes.LatestSignerForChainID(suite.app.EvmKeeper.ChainID()) + params := suite.App.EvmKeeper.GetParams(suite.Ctx) + ethCfg := params.ChainConfig.EthereumConfig(suite.App.EvmKeeper.ChainID()) + signer := ethtypes.LatestSignerForChainID(suite.App.EvmKeeper.ChainID()) b.ResetTimer() b.ReportAllocs() @@ -320,11 +347,11 @@ func BenchmarkApplyMessageWithDynamicFeeTx(b *testing.B) { b.StopTimer() m, err := newNativeMessage( - suite.app.EvmKeeper.GetNonce(suite.ctx, suite.address), - suite.ctx.BlockHeight(), - suite.address, + suite.App.EvmKeeper.GetNonce(suite.Ctx, suite.Address), + suite.Ctx.BlockHeight(), + suite.Address, ethCfg, - suite.signer, + suite.Signer, signer, ethtypes.DynamicFeeTxType, nil, @@ -333,7 +360,7 @@ func BenchmarkApplyMessageWithDynamicFeeTx(b *testing.B) { require.NoError(b, err) b.StartTimer() - resp, err := suite.app.EvmKeeper.ApplyMessage(suite.ctx, m, nil, true) + resp, err := suite.App.EvmKeeper.ApplyMessage(suite.Ctx, m, nil, true) b.StopTimer() require.NoError(b, err) diff --git a/x/evm/keeper/state_transition_test.go b/x/evm/keeper/state_transition_test.go index 3f45146718..b259494353 100644 --- a/x/evm/keeper/state_transition_test.go +++ b/x/evm/keeper/state_transition_test.go @@ -1,6 +1,7 @@ package keeper_test import ( + "encoding/hex" "fmt" "math" "math/big" @@ -9,11 +10,14 @@ import ( tmproto "github.com/cometbft/cometbft/proto/tendermint/types" tmtypes "github.com/cometbft/cometbft/types" codectypes "github.com/cosmos/cosmos-sdk/codec/types" + "github.com/cosmos/cosmos-sdk/store/iavl" sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" "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" "github.com/evmos/ethermint/tests" "github.com/evmos/ethermint/x/evm/keeper" @@ -22,7 +26,7 @@ import ( ) func (suite *KeeperTestSuite) TestGetHashFn() { - header := suite.ctx.BlockHeader() + header := suite.Ctx.BlockHeader() h, _ := tmtypes.HeaderFromProto(&header) hash := h.Hash() @@ -34,27 +38,27 @@ func (suite *KeeperTestSuite) TestGetHashFn() { }{ { "case 1.1: context hash cached", - uint64(suite.ctx.BlockHeight()), + uint64(suite.Ctx.BlockHeight()), func() { - suite.ctx = suite.ctx.WithHeaderHash(tmhash.Sum([]byte("header"))) + suite.Ctx = suite.Ctx.WithHeaderHash(tmhash.Sum([]byte("header"))) }, common.BytesToHash(tmhash.Sum([]byte("header"))), }, { "case 1.2: failed to cast Tendermint header", - uint64(suite.ctx.BlockHeight()), + uint64(suite.Ctx.BlockHeight()), func() { header := tmproto.Header{} - header.Height = suite.ctx.BlockHeight() - suite.ctx = suite.ctx.WithBlockHeader(header) + header.Height = suite.Ctx.BlockHeight() + suite.Ctx = suite.Ctx.WithBlockHeader(header) }, common.Hash{}, }, { "case 1.3: hash calculated from Tendermint header", - uint64(suite.ctx.BlockHeight()), + uint64(suite.Ctx.BlockHeight()), func() { - suite.ctx = suite.ctx.WithBlockHeader(header) + suite.Ctx = suite.Ctx.WithBlockHeader(header) }, common.BytesToHash(hash), }, @@ -62,7 +66,7 @@ func (suite *KeeperTestSuite) TestGetHashFn() { "case 2.1: height lower than current one, hist info not found", 1, func() { - suite.ctx = suite.ctx.WithBlockHeight(10) + suite.Ctx = suite.Ctx.WithBlockHeight(10) }, common.Hash{}, }, @@ -70,8 +74,8 @@ func (suite *KeeperTestSuite) TestGetHashFn() { "case 2.2: height lower than current one, invalid hist info header", 1, func() { - suite.app.StakingKeeper.SetHistoricalInfo(suite.ctx, 1, &stakingtypes.HistoricalInfo{}) - suite.ctx = suite.ctx.WithBlockHeight(10) + suite.App.StakingKeeper.SetHistoricalInfo(suite.Ctx, 1, &stakingtypes.HistoricalInfo{}) + suite.Ctx = suite.Ctx.WithBlockHeight(10) }, common.Hash{}, }, @@ -82,8 +86,8 @@ func (suite *KeeperTestSuite) TestGetHashFn() { histInfo := &stakingtypes.HistoricalInfo{ Header: header, } - suite.app.StakingKeeper.SetHistoricalInfo(suite.ctx, 1, histInfo) - suite.ctx = suite.ctx.WithBlockHeight(10) + suite.App.StakingKeeper.SetHistoricalInfo(suite.Ctx, 1, histInfo) + suite.Ctx = suite.Ctx.WithBlockHeight(10) }, common.BytesToHash(hash), }, @@ -101,7 +105,7 @@ func (suite *KeeperTestSuite) TestGetHashFn() { tc.malleate() - hash := suite.app.EvmKeeper.GetHashFn(suite.ctx)(tc.height) + hash := suite.App.EvmKeeper.GetHashFn(suite.Ctx)(tc.height) suite.Require().Equal(tc.expHash, hash) }) } @@ -118,9 +122,9 @@ func (suite *KeeperTestSuite) TestGetCoinbaseAddress() { { "validator not found", func() { - header := suite.ctx.BlockHeader() + header := suite.Ctx.BlockHeader() header.ProposerAddress = []byte{} - suite.ctx = suite.ctx.WithBlockHeader(header) + suite.Ctx = suite.Ctx.WithBlockHeader(header) }, false, }, @@ -137,18 +141,18 @@ func (suite *KeeperTestSuite) TestGetCoinbaseAddress() { ConsensusPubkey: pkAny, } - suite.app.StakingKeeper.SetValidator(suite.ctx, validator) - err = suite.app.StakingKeeper.SetValidatorByConsAddr(suite.ctx, validator) + suite.App.StakingKeeper.SetValidator(suite.Ctx, validator) + err = suite.App.StakingKeeper.SetValidatorByConsAddr(suite.Ctx, validator) suite.Require().NoError(err) - header := suite.ctx.BlockHeader() + header := suite.Ctx.BlockHeader() header.ProposerAddress = valConsAddr.Bytes() - suite.ctx = suite.ctx.WithBlockHeader(header) + suite.Ctx = suite.Ctx.WithBlockHeader(header) - _, found := suite.app.StakingKeeper.GetValidatorByConsAddr(suite.ctx, valConsAddr.Bytes()) + _, found := suite.App.StakingKeeper.GetValidatorByConsAddr(suite.Ctx, valConsAddr.Bytes()) suite.Require().True(found) - suite.Require().NotEmpty(suite.ctx.BlockHeader().ProposerAddress) + suite.Require().NotEmpty(suite.Ctx.BlockHeader().ProposerAddress) }, true, }, @@ -159,8 +163,8 @@ func (suite *KeeperTestSuite) TestGetCoinbaseAddress() { suite.SetupTest() // reset tc.malleate() - proposerAddress := suite.ctx.BlockHeader().ProposerAddress - coinbase, err := suite.app.EvmKeeper.GetCoinbaseAddress(suite.ctx, sdk.ConsAddress(proposerAddress)) + proposerAddress := suite.Ctx.BlockHeader().ProposerAddress + coinbase, err := suite.App.EvmKeeper.GetCoinbaseAddress(suite.Ctx, sdk.ConsAddress(proposerAddress)) if tc.expPass { suite.Require().NoError(err) suite.Require().Equal(valOpAddr, coinbase) @@ -254,21 +258,21 @@ func (suite *KeeperTestSuite) TestGetEthIntrinsicGas() { suite.Run(fmt.Sprintf("Case %s", tc.name), func() { suite.SetupTest() // reset - params := suite.app.EvmKeeper.GetParams(suite.ctx) - ethCfg := params.ChainConfig.EthereumConfig(suite.app.EvmKeeper.ChainID()) + params := suite.App.EvmKeeper.GetParams(suite.Ctx) + ethCfg := params.ChainConfig.EthereumConfig(suite.App.EvmKeeper.ChainID()) ethCfg.HomesteadBlock = big.NewInt(2) ethCfg.IstanbulBlock = big.NewInt(3) - signer := ethtypes.LatestSignerForChainID(suite.app.EvmKeeper.ChainID()) + signer := ethtypes.LatestSignerForChainID(suite.App.EvmKeeper.ChainID()) - suite.ctx = suite.ctx.WithBlockHeight(tc.height) + suite.Ctx = suite.Ctx.WithBlockHeight(tc.height) - nonce := suite.app.EvmKeeper.GetNonce(suite.ctx, suite.address) + nonce := suite.App.EvmKeeper.GetNonce(suite.Ctx, suite.Address) m, err := newNativeMessage( nonce, - suite.ctx.BlockHeight(), - suite.address, + suite.Ctx.BlockHeight(), + suite.Address, ethCfg, - suite.signer, + suite.Signer, signer, ethtypes.AccessListTxType, tc.data, @@ -276,7 +280,7 @@ func (suite *KeeperTestSuite) TestGetEthIntrinsicGas() { ) suite.Require().NoError(err) - gas, err := suite.app.EvmKeeper.GetEthIntrinsicGas(suite.ctx, m, ethCfg, tc.isContractCreation) + gas, err := suite.App.EvmKeeper.GetEthIntrinsicGas(suite.Ctx, m, ethCfg, tc.isContractCreation) if tc.noError { suite.Require().NoError(err) } else { @@ -328,7 +332,7 @@ func (suite *KeeperTestSuite) TestGasToRefund() { for _, tc := range testCases { suite.Run(fmt.Sprintf("Case %s", tc.name), func() { - suite.mintFeeCollector = true + suite.MintFeeCollector = true suite.SetupTest() // reset vmdb := suite.StateDB() vmdb.AddRefund(10) @@ -344,7 +348,7 @@ func (suite *KeeperTestSuite) TestGasToRefund() { } }) } - suite.mintFeeCollector = false + suite.MintFeeCollector = false } func (suite *KeeperTestSuite) TestRefundGas() { @@ -396,11 +400,11 @@ func (suite *KeeperTestSuite) TestRefundGas() { noError: false, expGasRefund: params.TxGas, malleate: func() { - keeperParams := suite.app.EvmKeeper.GetParams(suite.ctx) + keeperParams := suite.App.EvmKeeper.GetParams(suite.Ctx) m, err = suite.createContractGethMsg( - suite.StateDB().GetNonce(suite.address), - ethtypes.LatestSignerForChainID(suite.app.EvmKeeper.ChainID()), - keeperParams.ChainConfig.EthereumConfig(suite.app.EvmKeeper.ChainID()), + suite.StateDB().GetNonce(suite.Address), + ethtypes.LatestSignerForChainID(suite.App.EvmKeeper.ChainID()), + keeperParams.ChainConfig.EthereumConfig(suite.App.EvmKeeper.ChainID()), big.NewInt(-100), ) suite.Require().NoError(err) @@ -410,20 +414,20 @@ func (suite *KeeperTestSuite) TestRefundGas() { for _, tc := range testCases { suite.Run(fmt.Sprintf("Case %s", tc.name), func() { - suite.mintFeeCollector = true + suite.MintFeeCollector = true suite.SetupTest() // reset - keeperParams := suite.app.EvmKeeper.GetParams(suite.ctx) - ethCfg := keeperParams.ChainConfig.EthereumConfig(suite.app.EvmKeeper.ChainID()) - signer := ethtypes.LatestSignerForChainID(suite.app.EvmKeeper.ChainID()) + keeperParams := suite.App.EvmKeeper.GetParams(suite.Ctx) + ethCfg := keeperParams.ChainConfig.EthereumConfig(suite.App.EvmKeeper.ChainID()) + signer := ethtypes.LatestSignerForChainID(suite.App.EvmKeeper.ChainID()) vmdb := suite.StateDB() m, err = newNativeMessage( - vmdb.GetNonce(suite.address), - suite.ctx.BlockHeight(), - suite.address, + vmdb.GetNonce(suite.Address), + suite.Ctx.BlockHeight(), + suite.Address, ethCfg, - suite.signer, + suite.Signer, signer, ethtypes.AccessListTxType, nil, @@ -445,7 +449,7 @@ func (suite *KeeperTestSuite) TestRefundGas() { refund := keeper.GasToRefund(vmdb.GetRefund(), gasUsed, tc.refundQuotient) suite.Require().Equal(tc.expGasRefund, refund) - err = suite.app.EvmKeeper.RefundGas(suite.ctx, m, refund, "aphoton") + err = suite.App.EvmKeeper.RefundGas(suite.Ctx, m, refund, "aphoton") if tc.noError { suite.Require().NoError(err) } else { @@ -453,7 +457,7 @@ func (suite *KeeperTestSuite) TestRefundGas() { } }) } - suite.mintFeeCollector = false + suite.MintFeeCollector = false } func (suite *KeeperTestSuite) TestResetGasMeterAndConsumeGas() { @@ -502,8 +506,8 @@ func (suite *KeeperTestSuite) TestResetGasMeterAndConsumeGas() { panicF := func() { gm := sdk.NewGasMeter(10) gm.ConsumeGas(tc.gasConsumed, "") - ctx := suite.ctx.WithGasMeter(gm) - suite.app.EvmKeeper.ResetGasMeterAndConsumeGas(ctx, tc.gasUsed) + ctx := suite.Ctx.WithGasMeter(gm) + suite.App.EvmKeeper.ResetGasMeterAndConsumeGas(ctx, tc.gasUsed) } if tc.expPanic { @@ -516,18 +520,18 @@ func (suite *KeeperTestSuite) TestResetGasMeterAndConsumeGas() { } func (suite *KeeperTestSuite) TestEVMConfig() { - proposerAddress := suite.ctx.BlockHeader().ProposerAddress - cfg, err := suite.app.EvmKeeper.EVMConfig(suite.ctx, proposerAddress, big.NewInt(9000)) + proposerAddress := suite.Ctx.BlockHeader().ProposerAddress + cfg, err := suite.App.EvmKeeper.EVMConfig(suite.Ctx, proposerAddress, big.NewInt(9000)) suite.Require().NoError(err) suite.Require().Equal(types.DefaultParams(), cfg.Params) // london hardfork is enabled by default suite.Require().Equal(big.NewInt(0), cfg.BaseFee) - suite.Require().Equal(suite.address, cfg.CoinBase) + suite.Require().Equal(suite.Address, cfg.CoinBase) suite.Require().Equal(types.DefaultParams().ChainConfig.EthereumConfig(big.NewInt(9000)), cfg.ChainConfig) } func (suite *KeeperTestSuite) TestContractDeployment() { - contractAddress := suite.DeployTestContract(suite.T(), suite.address, big.NewInt(10000000000000)) + contractAddress := suite.DeployTestContract(suite.T(), suite.Address, big.NewInt(10000000000000)) db := suite.StateDB() suite.Require().Greater(db.GetCodeSize(contractAddress), 0) } @@ -536,22 +540,22 @@ func (suite *KeeperTestSuite) TestApplyMessage() { expectedGasUsed := params.TxGas var msg core.Message - proposerAddress := suite.ctx.BlockHeader().ProposerAddress - config, err := suite.app.EvmKeeper.EVMConfig(suite.ctx, proposerAddress, big.NewInt(9000)) + proposerAddress := suite.Ctx.BlockHeader().ProposerAddress + config, err := suite.App.EvmKeeper.EVMConfig(suite.Ctx, proposerAddress, big.NewInt(9000)) suite.Require().NoError(err) - keeperParams := suite.app.EvmKeeper.GetParams(suite.ctx) - chainCfg := keeperParams.ChainConfig.EthereumConfig(suite.app.EvmKeeper.ChainID()) - signer := ethtypes.LatestSignerForChainID(suite.app.EvmKeeper.ChainID()) - tracer := suite.app.EvmKeeper.Tracer(suite.ctx, msg, config.ChainConfig) + keeperParams := suite.App.EvmKeeper.GetParams(suite.Ctx) + chainCfg := keeperParams.ChainConfig.EthereumConfig(suite.App.EvmKeeper.ChainID()) + signer := ethtypes.LatestSignerForChainID(suite.App.EvmKeeper.ChainID()) + tracer := suite.App.EvmKeeper.Tracer(suite.Ctx, msg, config.ChainConfig) vmdb := suite.StateDB() msg, err = newNativeMessage( - vmdb.GetNonce(suite.address), - suite.ctx.BlockHeight(), - suite.address, + vmdb.GetNonce(suite.Address), + suite.Ctx.BlockHeight(), + suite.Address, chainCfg, - suite.signer, + suite.Signer, signer, ethtypes.AccessListTxType, nil, @@ -559,7 +563,7 @@ func (suite *KeeperTestSuite) TestApplyMessage() { ) suite.Require().NoError(err) - res, err := suite.app.EvmKeeper.ApplyMessage(suite.ctx, msg, tracer, true) + res, err := suite.App.EvmKeeper.ApplyMessage(suite.Ctx, msg, tracer, true) suite.Require().NoError(err) suite.Require().Equal(expectedGasUsed, res.GasUsed) @@ -588,11 +592,11 @@ func (suite *KeeperTestSuite) TestApplyMessageWithConfig() { "messsage applied ok", func() { msg, err = newNativeMessage( - vmdb.GetNonce(suite.address), - suite.ctx.BlockHeight(), - suite.address, + vmdb.GetNonce(suite.Address), + suite.Ctx.BlockHeight(), + suite.Address, chainCfg, - suite.signer, + suite.Signer, signer, ethtypes.AccessListTxType, nil, @@ -607,11 +611,11 @@ func (suite *KeeperTestSuite) TestApplyMessageWithConfig() { func() { config.Params.EnableCall = false msg, err = newNativeMessage( - vmdb.GetNonce(suite.address), - suite.ctx.BlockHeight(), - suite.address, + vmdb.GetNonce(suite.Address), + suite.Ctx.BlockHeight(), + suite.Address, chainCfg, - suite.signer, + suite.Signer, signer, ethtypes.AccessListTxType, nil, @@ -624,7 +628,7 @@ func (suite *KeeperTestSuite) TestApplyMessageWithConfig() { { "create contract tx with config param EnableCreate = false", func() { - msg, err = suite.createContractGethMsg(vmdb.GetNonce(suite.address), signer, chainCfg, big.NewInt(1)) + msg, err = suite.createContractGethMsg(vmdb.GetNonce(suite.Address), signer, chainCfg, big.NewInt(1)) suite.Require().NoError(err) config.Params.EnableCreate = false }, @@ -637,18 +641,18 @@ func (suite *KeeperTestSuite) TestApplyMessageWithConfig() { suite.SetupTest() expectedGasUsed = params.TxGas - proposerAddress := suite.ctx.BlockHeader().ProposerAddress - config, err = suite.app.EvmKeeper.EVMConfig(suite.ctx, proposerAddress, big.NewInt(9000)) + proposerAddress := suite.Ctx.BlockHeader().ProposerAddress + config, err = suite.App.EvmKeeper.EVMConfig(suite.Ctx, proposerAddress, big.NewInt(9000)) suite.Require().NoError(err) - keeperParams = suite.app.EvmKeeper.GetParams(suite.ctx) - chainCfg = keeperParams.ChainConfig.EthereumConfig(suite.app.EvmKeeper.ChainID()) - signer = ethtypes.LatestSignerForChainID(suite.app.EvmKeeper.ChainID()) + keeperParams = suite.App.EvmKeeper.GetParams(suite.Ctx) + chainCfg = keeperParams.ChainConfig.EthereumConfig(suite.App.EvmKeeper.ChainID()) + signer = ethtypes.LatestSignerForChainID(suite.App.EvmKeeper.ChainID()) vmdb = suite.StateDB() - txConfig = suite.app.EvmKeeper.TxConfig(suite.ctx, common.Hash{}) + txConfig = suite.App.EvmKeeper.TxConfig(suite.Ctx, common.Hash{}) tc.malleate() - res, err := suite.app.EvmKeeper.ApplyMessageWithConfig(suite.ctx, msg, nil, true, config, txConfig) + res, err := suite.App.EvmKeeper.ApplyMessageWithConfig(suite.Ctx, msg, nil, true, config, txConfig) if tc.expErr { suite.Require().Error(err) @@ -668,30 +672,34 @@ func (suite *KeeperTestSuite) createContractGethMsg(nonce uint64, signer ethtype return nil, err } - msgSigner := ethtypes.MakeSigner(cfg, big.NewInt(suite.ctx.BlockHeight())) + msgSigner := ethtypes.MakeSigner(cfg, big.NewInt(suite.Ctx.BlockHeight())) return ethMsg.AsMessage(msgSigner, nil) } func (suite *KeeperTestSuite) createContractMsgTx(nonce uint64, signer ethtypes.Signer, cfg *params.ChainConfig, gasPrice *big.Int) (*types.MsgEthereumTx, error) { contractCreateTx := ðtypes.AccessListTx{ GasPrice: gasPrice, - Gas: params.TxGasContractCreation, + Gas: params.TxGasContractCreation * 2, To: nil, - Data: []byte("contract_data"), - Nonce: nonce, + // Minimal contract data + // https://ethereum.stackexchange.com/questions/40757/what-is-the-shortest-bytecode-that-will-publish-a-contract-with-non-zero-bytecod + // Using the previous string "contract_data" as contract code may cause + // an error as it includes 0x5f which is PUSH0, only on Shanghai and later + Data: common.Hex2Bytes("0x3859818153F3"), + Nonce: nonce, } ethTx := ethtypes.NewTx(contractCreateTx) ethMsg := &types.MsgEthereumTx{} ethMsg.FromEthereumTx(ethTx) - ethMsg.From = suite.address.Hex() + ethMsg.From = suite.Address.Hex() - return ethMsg, ethMsg.Sign(signer, suite.signer) + return ethMsg, ethMsg.Sign(signer, suite.Signer) } func (suite *KeeperTestSuite) TestGetProposerAddress() { var a sdk.ConsAddress - address := sdk.ConsAddress(suite.address.Bytes()) - proposerAddress := sdk.ConsAddress(suite.ctx.BlockHeader().ProposerAddress) + address := sdk.ConsAddress(suite.Address.Bytes()) + proposerAddress := sdk.ConsAddress(suite.Ctx.BlockHeader().ProposerAddress) testCases := []struct { msg string adr sdk.ConsAddress @@ -715,7 +723,228 @@ func (suite *KeeperTestSuite) TestGetProposerAddress() { } for _, tc := range testCases { suite.Run(fmt.Sprintf("Case %s", tc.msg), func() { - suite.Require().Equal(tc.expAdr, keeper.GetProposerAddress(suite.ctx, tc.adr)) + suite.Require().Equal(tc.expAdr, keeper.GetProposerAddress(suite.Ctx, tc.adr)) + }) + } +} + +var ( + blockHash common.Hash = common.BigToHash(big.NewInt(9999)) + emptyTxConfig statedb.TxConfig = statedb.NewEmptyTxConfig(blockHash) +) + +type StateDBCommit interface { + vm.StateDB + Commit() error +} + +func (suite *KeeperTestSuite) GetStoreCommitHashes( + storeKeys []string, +) map[string]string { + hashes := make(map[string]string) + + for _, storeKey := range storeKeys { + key := suite.App.GetKey(storeKey) + store := suite.App.CommitMultiStore().GetStore(key) + iavlStore := store.(*iavl.Store) + commitID := iavlStore.LastCommitID() + + hashes[storeKey] = hex.EncodeToString(commitID.Hash) + } + + return hashes +} + +func (suite *KeeperTestSuite) TestNoopStateChange_UnmodifiedIAVLTree() { + // On StateDB.Commit(), if there is a dirty state change that matches the + // committed state, it should be skipped. Only state changes that are + // different from committed state should be applied to the underlying store. + // Corresponding journal based StateDB code: + // https://github.com/ethereum/go-ethereum/blob/e31709db6570e302557a9bccd681034ea0dcc246/core/state/state_object.go#L302-L305 + // https://github.com/Kava-Labs/ethermint/blob/877e8fd1bd140c37ad05ed613f31e28f0130c0c4/x/evm/statedb/statedb.go#L469-L472 + + // Even with store.Set() on the same pre-existing key and value, it will + // update the underlying iavl.Node version and thus the node hash, parent + // hashes, and the commitID + + // E.g. SetState(A, B) -> xxx -> SetState(A, B) should not be applied to + // the underlying store. + // xxx could be 0 or more state changes to the same key. It can be different + // values since the only value that actually matters is the last one when + // Commit() is called. + + addr := common.BigToAddress(big.NewInt(1)) + key := common.BigToHash(big.NewInt(10)) + value := common.BigToHash(big.NewInt(20)) + + tests := []struct { + name string + affectedStoreKeys []string + initializeState func(vmdb vm.StateDB) + maleate func(vmdb vm.StateDB) + }{ + { + "SetState - no extra snapshots", + []string{types.StoreKey}, + func(vmdb vm.StateDB) { + vmdb.SetState(addr, key, value) + }, + func(vmdb vm.StateDB) { + vmdb.SetState(addr, key, value) + }, + }, + { + "SetState - 2nd snapshot, same value", + []string{types.StoreKey}, + func(vmdb vm.StateDB) { + vmdb.SetState(addr, key, value) + }, + func(vmdb vm.StateDB) { + // A -> A + vmdb.SetState(addr, key, value) + + // Same value, just different snapshot. Should be skipped in all + // snapshots. + _ = vmdb.Snapshot() + vmdb.SetState(addr, key, value) + }, + }, + { + "SetState - 2nd snapshot, different value", + []string{types.StoreKey}, + func(vmdb vm.StateDB) { + vmdb.SetState(addr, key, value) + }, + func(vmdb vm.StateDB) { + // A -> B -> A + + // Different value in 1st snapshot + value2 := common.BigToHash(big.NewInt(30)) + vmdb.SetState(addr, key, value2) + + // Back to original value in 2nd snapshot + _ = vmdb.Snapshot() + vmdb.SetState(addr, key, value) + }, + }, + { + "SetState - multiple snapshots, different value", + []string{types.StoreKey}, + func(vmdb vm.StateDB) { + vmdb.SetState(addr, key, value) + }, + func(vmdb vm.StateDB) { + // A -> B -> C -> A + + // Different value in 1st snapshot + value2 := common.BigToHash(big.NewInt(30)) + value3 := common.BigToHash(big.NewInt(40)) + _ = vmdb.Snapshot() + vmdb.SetState(addr, key, value2) + + // Extra empty snapshot for good measure + _ = vmdb.Snapshot() + + _ = vmdb.Snapshot() + vmdb.SetState(addr, key, value3) + + // Back to original value in last snapshot + _ = vmdb.Snapshot() + vmdb.SetState(addr, key, value) + }, + }, + { + "no-op balance change", + []string{types.StoreKey, banktypes.ModuleName}, + func(vmdb vm.StateDB) { + // Start with some non-zero balance + vmdb.AddBalance(addr, big.NewInt(10)) + }, + func(vmdb vm.StateDB) { + // No-op balance change + vmdb.AddBalance(addr, big.NewInt(50)) + vmdb.SubBalance(addr, big.NewInt(40)) + vmdb.SubBalance(addr, big.NewInt(10)) + }, + }, + { + "no-op balance change to zero", + []string{types.StoreKey, banktypes.ModuleName}, + func(vmdb vm.StateDB) { + // Start with some non-zero balance + vmdb.AddBalance(addr, big.NewInt(10)) + }, + func(vmdb vm.StateDB) { + // No-op balance change + vmdb.AddBalance(addr, big.NewInt(50)) + + // Down to zero + vmdb.SubBalance(addr, big.NewInt(60)) + suite.Require().Equal(int64(0), vmdb.GetBalance(addr).Int64()) + + // Back to a non-zero balance same as before + vmdb.AddBalance(addr, big.NewInt(10)) + }, + }, + { + "no-op balance change with snapshots", + []string{types.StoreKey, banktypes.ModuleName}, + func(vmdb vm.StateDB) { + // Start with some non-zero balance + vmdb.AddBalance(addr, big.NewInt(10)) + }, + func(vmdb vm.StateDB) { + // No-op balance change + vmdb.AddBalance(addr, big.NewInt(50)) + + _ = vmdb.Snapshot() + vmdb.SubBalance(addr, big.NewInt(40)) + + _ = vmdb.Snapshot() + vmdb.SubBalance(addr, big.NewInt(10)) + }, + }, + } + + for _, tt := range tests { + suite.Run(tt.name, func() { + // reset + suite.SetupTest() + + db := statedb.New(suite.Ctx, suite.App.EvmKeeper, emptyTxConfig) + tt.initializeState(db) + + suite.Require().NoError(db.Commit()) + suite.Commit() + + store := suite.App.CommitMultiStore().GetStore(suite.App.GetKey(types.StoreKey)) + iavlStore := store.(*iavl.Store) + commitID1 := iavlStore.LastCommitID() + + iavlHashes1 := s.GetStoreCommitHashes(tt.affectedStoreKeys) + + // New statedb that should not modify the underlying store + db = statedb.New(suite.Ctx, suite.App.EvmKeeper, emptyTxConfig) + tt.maleate(db) + + suite.Require().NoError(db.Commit()) + // suite.Commit() + + commitID2 := iavlStore.LastCommitID() + iavlHashes2 := s.GetStoreCommitHashes(tt.affectedStoreKeys) + + // We can compare the commitIDs since this is *only* the x/evm store which + // doesn't change between blocks without state changes. Any version change, + // e.g. no-op change that was written when it shouldn't, will modify the + // hash. + suite.Require().Equal( + common.Bytes2Hex(commitID1.Hash), + common.Bytes2Hex(commitID2.Hash), + "evm store should be unchanged", + ) + + // Check all affected stores + suite.Require().Equal(iavlHashes1, iavlHashes2) }) } } diff --git a/x/evm/keeper/statedb.go b/x/evm/keeper/statedb.go index c2cd893c5e..51e4ddec73 100644 --- a/x/evm/keeper/statedb.go +++ b/x/evm/keeper/statedb.go @@ -44,7 +44,7 @@ func (k *Keeper) GetAccount(ctx sdk.Context, addr common.Address) *statedb.Accou return nil } - acct.Balance = k.GetBalance(ctx, addr) + // acct.Balance = k.GetBalance(ctx, addr) return acct } @@ -153,29 +153,27 @@ func (k *Keeper) SetAccount(ctx sdk.Context, addr common.Address, account stated k.accountKeeper.SetAccount(ctx, acct) - if err := k.SetBalance(ctx, addr, account.Balance); err != nil { - return err - } - k.Logger(ctx).Debug( "account updated", "ethereum-address", addr.Hex(), "nonce", account.Nonce, "codeHash", codeHash.Hex(), - "balance", account.Balance, + // "balance", account.Balance, ) return nil } // SetState update contract storage, delete if value is empty. -func (k *Keeper) SetState(ctx sdk.Context, addr common.Address, key common.Hash, value []byte) { +func (k *Keeper) SetState(ctx sdk.Context, addr common.Address, key, value common.Hash) { store := prefix.NewStore(ctx.KVStore(k.storeKey), types.AddressStoragePrefix(addr)) action := "updated" - if len(value) == 0 { + + // Value is always HashLength long, so check if empty by comparing to zero hash + if (value == common.Hash{}) { store.Delete(key.Bytes()) action = "deleted" } else { - store.Set(key.Bytes(), value) + store.Set(key.Bytes(), value.Bytes()) } k.Logger(ctx).Debug( fmt.Sprintf("state %s", action), @@ -227,7 +225,8 @@ func (k *Keeper) DeleteAccount(ctx sdk.Context, addr common.Address) error { // clear storage k.ForEachStorage(ctx, addr, func(key, _ common.Hash) bool { - k.SetState(ctx, addr, key, nil) + // Empty Hash to clear + k.SetState(ctx, addr, key, common.Hash{}) return true }) diff --git a/x/evm/keeper/statedb_benchmark_test.go b/x/evm/keeper/statedb_benchmark_test.go index bf141052aa..962e3411fd 100644 --- a/x/evm/keeper/statedb_benchmark_test.go +++ b/x/evm/keeper/statedb_benchmark_test.go @@ -38,7 +38,7 @@ func BenchmarkCreateAccountExisting(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { - vmdb.CreateAccount(suite.address) + vmdb.CreateAccount(suite.Address) } } @@ -53,7 +53,7 @@ func BenchmarkAddBalance(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { - vmdb.AddBalance(suite.address, amt) + vmdb.AddBalance(suite.Address, amt) } } @@ -68,7 +68,7 @@ func BenchmarkSetCode(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { - vmdb.SetCode(suite.address, hash) + vmdb.SetCode(suite.Address, hash) } } @@ -83,7 +83,7 @@ func BenchmarkSetState(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { - vmdb.SetCode(suite.address, hash) + vmdb.SetCode(suite.Address, hash) } } @@ -101,7 +101,7 @@ func BenchmarkAddLog(b *testing.B) { for i := 0; i < b.N; i++ { vmdb.AddLog(ðtypes.Log{ - Address: suite.address, + Address: suite.Address, Topics: []common.Hash{topic}, Data: []byte("data"), BlockNumber: 1, @@ -141,11 +141,14 @@ func BenchmarkSubBalance(b *testing.B) { amt := big.NewInt(10) + // Add enough balance to cover all subtractions + vmdb.AddBalance(suite.Address, new(big.Int).Mul(amt, big.NewInt(int64(b.N)))) + b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { - vmdb.SubBalance(suite.address, amt) + vmdb.SubBalance(suite.Address, amt) } } @@ -158,7 +161,7 @@ func BenchmarkSetNonce(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { - vmdb.SetNonce(suite.address, 1) + vmdb.SetNonce(suite.Address, 1) } } diff --git a/x/evm/keeper/statedb_test.go b/x/evm/keeper/statedb_test.go index fecb8392f5..a6af5dac4c 100644 --- a/x/evm/keeper/statedb_test.go +++ b/x/evm/keeper/statedb_test.go @@ -7,16 +7,17 @@ import ( codectypes "github.com/cosmos/cosmos-sdk/codec/types" cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types" + "github.com/cosmos/cosmos-sdk/store/iavl" "github.com/cosmos/cosmos-sdk/store/prefix" sdk "github.com/cosmos/cosmos-sdk/types" authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" authtx "github.com/cosmos/cosmos-sdk/x/auth/tx" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" vestingtypes "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" "github.com/evmos/ethermint/crypto/ethsecp256k1" "github.com/evmos/ethermint/tests" @@ -29,27 +30,27 @@ func (suite *KeeperTestSuite) TestCreateAccount() { testCases := []struct { name string addr common.Address - malleate func(vm.StateDB, common.Address) - callback func(vm.StateDB, common.Address) + malleate func(*statedb.StateDB, common.Address) + callback func(*statedb.StateDB, common.Address) }{ { "reset account (keep balance)", - suite.address, - func(vmdb vm.StateDB, addr common.Address) { + suite.Address, + func(vmdb *statedb.StateDB, addr common.Address) { vmdb.AddBalance(addr, big.NewInt(100)) suite.Require().NotZero(vmdb.GetBalance(addr).Int64()) }, - func(vmdb vm.StateDB, addr common.Address) { + func(vmdb *statedb.StateDB, addr common.Address) { suite.Require().Equal(vmdb.GetBalance(addr).Int64(), int64(100)) }, }, { "create account", tests.GenerateAddress(), - func(vmdb vm.StateDB, addr common.Address) { + func(vmdb *statedb.StateDB, addr common.Address) { suite.Require().False(vmdb.Exist(addr)) }, - func(vmdb vm.StateDB, addr common.Address) { + func(vmdb *statedb.StateDB, addr common.Address) { suite.Require().True(vmdb.Exist(addr)) }, }, @@ -84,16 +85,18 @@ func (suite *KeeperTestSuite) TestAddBalance() { { "negative amount", big.NewInt(-1), - false, // seems to be consistent with go-ethereum's implementation + // Pre-cache-ctx implementation allowed negative amounts, which + // seems to be consistent with go-ethereum's implementation + true, }, } for _, tc := range testCases { suite.Run(tc.name, func() { vmdb := suite.StateDB() - prev := vmdb.GetBalance(suite.address) - vmdb.AddBalance(suite.address, tc.amount) - post := vmdb.GetBalance(suite.address) + prev := vmdb.GetBalance(suite.Address) + vmdb.AddBalance(suite.Address, tc.amount) + post := vmdb.GetBalance(suite.Address) if tc.isNoOp { suite.Require().Equal(prev.Int64(), post.Int64()) @@ -108,33 +111,33 @@ func (suite *KeeperTestSuite) TestSubBalance() { testCases := []struct { name string amount *big.Int - malleate func(vm.StateDB) + malleate func(*statedb.StateDB) isNoOp bool }{ { "positive amount, below zero", big.NewInt(100), - func(vm.StateDB) {}, - false, + func(*statedb.StateDB) {}, + true, }, { "positive amount, above zero", big.NewInt(50), - func(vmdb vm.StateDB) { - vmdb.AddBalance(suite.address, big.NewInt(100)) + func(vmdb *statedb.StateDB) { + vmdb.AddBalance(suite.Address, big.NewInt(100)) }, false, }, { "zero amount", big.NewInt(0), - func(vm.StateDB) {}, + func(*statedb.StateDB) {}, true, }, { "negative amount", big.NewInt(-1), - func(vm.StateDB) {}, + func(*statedb.StateDB) {}, false, }, } @@ -144,9 +147,9 @@ func (suite *KeeperTestSuite) TestSubBalance() { vmdb := suite.StateDB() tc.malleate(vmdb) - prev := vmdb.GetBalance(suite.address) - vmdb.SubBalance(suite.address, tc.amount) - post := vmdb.GetBalance(suite.address) + prev := vmdb.GetBalance(suite.Address) + vmdb.SubBalance(suite.Address, tc.amount) + post := vmdb.GetBalance(suite.Address) if tc.isNoOp { suite.Require().Equal(prev.Int64(), post.Int64()) @@ -162,20 +165,20 @@ func (suite *KeeperTestSuite) TestGetNonce() { name string address common.Address expectedNonce uint64 - malleate func(vm.StateDB) + malleate func(*statedb.StateDB) }{ { "account not found", tests.GenerateAddress(), 0, - func(vm.StateDB) {}, + func(*statedb.StateDB) {}, }, { "existing account", - suite.address, + suite.Address, 1, - func(vmdb vm.StateDB) { - vmdb.SetNonce(suite.address, 1) + func(vmdb *statedb.StateDB) { + vmdb.SetNonce(suite.Address, 1) }, }, } @@ -206,7 +209,7 @@ func (suite *KeeperTestSuite) TestSetNonce() { }, { "existing account", - suite.address, + suite.Address, 99, func() {}, }, @@ -239,49 +242,81 @@ func (suite *KeeperTestSuite) TestSetAccount() { { "new account, non-contract account", tests.GenerateAddress(), - statedb.Account{10, big.NewInt(100), types.EmptyCodeHash}, + statedb.Account{ + Nonce: 10, + // Balance:, + CodeHash: types.EmptyCodeHash, + }, nil, }, { "new account, contract account", tests.GenerateAddress(), - statedb.Account{10, big.NewInt(100), crypto.Keccak256Hash([]byte("some code hash")).Bytes()}, + statedb.Account{ + Nonce: 10, + // Balance: big.NewInt(100) + CodeHash: crypto.Keccak256Hash([]byte("some code hash")).Bytes(), + }, nil, }, { "existing eth account, non-contract account", ethAddr, - statedb.Account{10, big.NewInt(1), types.EmptyCodeHash}, + statedb.Account{ + Nonce: 10, + // Balance: big.NewInt(1), + CodeHash: types.EmptyCodeHash, + }, nil, }, { "existing eth account, contract account", ethAddr, - statedb.Account{10, big.NewInt(0), crypto.Keccak256Hash([]byte("some code hash")).Bytes()}, + statedb.Account{ + Nonce: 10, + // Balance: /* big.NewInt(0),*/, + CodeHash: crypto.Keccak256Hash([]byte("some code hash")).Bytes(), + }, nil, }, { "existing base account, non-contract account", baseAddr, - statedb.Account{10, big.NewInt(10), types.EmptyCodeHash}, + statedb.Account{ + Nonce: 10, + // Balance: /* big.NewInt(10),*/, + CodeHash: types.EmptyCodeHash, + }, nil, }, { "existing base account, contract account", baseAddr, - statedb.Account{10, big.NewInt(99), crypto.Keccak256Hash([]byte("some code hash")).Bytes()}, + statedb.Account{ + Nonce: 10, + // Balance: /* big.NewInt(99),*/, + CodeHash: crypto.Keccak256Hash([]byte("some code hash")).Bytes(), + }, nil, }, { "existing vesting account, non-contract account", vestingAddr, - statedb.Account{10, big.NewInt(1000), types.EmptyCodeHash}, + statedb.Account{ + Nonce: 10, + // Balance: /* big.NewInt(1000),*/, + CodeHash: types.EmptyCodeHash, + }, nil, }, { "existing vesting account, contract account", vestingAddr, - statedb.Account{10, big.NewInt(1001), crypto.Keccak256Hash([]byte("some code hash")).Bytes()}, + statedb.Account{ + Nonce: 10, + // Balance: /* big.NewInt(1001),*/, + CodeHash: crypto.Keccak256Hash([]byte("some code hash")).Bytes(), + }, types.ErrInvalidAccount, }, } @@ -289,17 +324,17 @@ func (suite *KeeperTestSuite) TestSetAccount() { for _, tc := range testCases { suite.Run(tc.name, func() { if tc.address == baseAddr { - suite.app.AccountKeeper.SetAccount(suite.ctx, baseAcc) + suite.App.AccountKeeper.SetAccount(suite.Ctx, baseAcc) } if tc.address == ethAddr { - suite.app.AccountKeeper.SetAccount(suite.ctx, ethAcc) + suite.App.AccountKeeper.SetAccount(suite.Ctx, ethAcc) } if tc.address == vestingAddr { - suite.app.AccountKeeper.SetAccount(suite.ctx, vestingAcc) + suite.App.AccountKeeper.SetAccount(suite.Ctx, vestingAcc) } vmdb := suite.StateDB() - err := vmdb.Keeper().SetAccount(suite.ctx, tc.address, tc.account) + err := vmdb.Keeper().SetAccount(suite.Ctx, tc.address, tc.account) if tc.expectedErr == nil { suite.Require().NoError(err) @@ -315,7 +350,7 @@ func (suite *KeeperTestSuite) TestSetAccount() { suite.Equal(common.BytesToHash(tc.account.CodeHash), hash, "expected code hash to be set") balance := vmdb.GetBalance(tc.address) - suite.Equal(balance, tc.account.Balance, "expected balance to be set") + suite.Equal(big.NewInt(0), balance, "balance is not set from SetAccount") }) } } @@ -323,32 +358,32 @@ func (suite *KeeperTestSuite) TestSetAccount() { func (suite *KeeperTestSuite) TestGetCodeHash() { addr := tests.GenerateAddress() baseAcc := &authtypes.BaseAccount{Address: sdk.AccAddress(addr.Bytes()).String()} - suite.app.AccountKeeper.SetAccount(suite.ctx, baseAcc) + suite.App.AccountKeeper.SetAccount(suite.Ctx, baseAcc) testCases := []struct { name string address common.Address expHash common.Hash - malleate func(vm.StateDB) + malleate func(*statedb.StateDB) }{ { "account not found", tests.GenerateAddress(), common.Hash{}, - func(vm.StateDB) {}, + func(*statedb.StateDB) {}, }, { "account not EthAccount type, EmptyCodeHash", addr, common.BytesToHash(types.EmptyCodeHash), - func(vm.StateDB) {}, + func(*statedb.StateDB) {}, }, { "existing account", - suite.address, + suite.Address, crypto.Keccak256Hash([]byte("codeHash")), - func(vmdb vm.StateDB) { - vmdb.SetCode(suite.address, []byte("codeHash")) + func(vmdb *statedb.StateDB) { + vmdb.SetCode(suite.Address, []byte("codeHash")) }, }, } @@ -367,7 +402,7 @@ func (suite *KeeperTestSuite) TestGetCodeHash() { func (suite *KeeperTestSuite) TestSetCode() { addr := tests.GenerateAddress() baseAcc := &authtypes.BaseAccount{Address: sdk.AccAddress(addr.Bytes()).String()} - suite.app.AccountKeeper.SetAccount(suite.ctx, baseAcc) + suite.App.AccountKeeper.SetAccount(suite.Ctx, baseAcc) testCases := []struct { name string @@ -389,13 +424,13 @@ func (suite *KeeperTestSuite) TestSetCode() { }, { "existing account", - suite.address, + suite.Address, []byte("code"), false, }, { "existing account, code deleted from store", - suite.address, + suite.Address, nil, false, }, @@ -422,7 +457,7 @@ func (suite *KeeperTestSuite) TestSetCode() { func (suite *KeeperTestSuite) TestKeeperSetCode() { addr := tests.GenerateAddress() baseAcc := &authtypes.BaseAccount{Address: sdk.AccAddress(addr.Bytes()).String()} - suite.app.AccountKeeper.SetAccount(suite.ctx, baseAcc) + suite.App.AccountKeeper.SetAccount(suite.Ctx, baseAcc) testCases := []struct { name string @@ -443,9 +478,9 @@ func (suite *KeeperTestSuite) TestKeeperSetCode() { for _, tc := range testCases { suite.Run(tc.name, func() { - suite.app.EvmKeeper.SetCode(suite.ctx, tc.codeHash, tc.code) - key := suite.app.GetKey(types.StoreKey) - store := prefix.NewStore(suite.ctx.KVStore(key), types.KeyPrefixCode) + suite.App.EvmKeeper.SetCode(suite.Ctx, tc.codeHash, tc.code) + key := suite.App.GetKey(types.StoreKey) + store := prefix.NewStore(suite.Ctx.KVStore(key), types.KeyPrefixCode) code := store.Get(tc.codeHash) suite.Require().Equal(tc.code, code) @@ -456,13 +491,13 @@ func (suite *KeeperTestSuite) TestKeeperSetCode() { func (suite *KeeperTestSuite) TestRefund() { testCases := []struct { name string - malleate func(vm.StateDB) + malleate func(*statedb.StateDB) expRefund uint64 expPanic bool }{ { "success - add and subtract refund", - func(vmdb vm.StateDB) { + func(vmdb *statedb.StateDB) { vmdb.AddRefund(11) }, 1, @@ -470,7 +505,7 @@ func (suite *KeeperTestSuite) TestRefund() { }, { "fail - subtract amount > current refund", - func(vm.StateDB) { + func(*statedb.StateDB) { }, 0, true, @@ -512,32 +547,51 @@ func (suite *KeeperTestSuite) TestState() { for _, tc := range testCases { suite.Run(tc.name, func() { vmdb := suite.StateDB() - vmdb.SetState(suite.address, tc.key, tc.value) - value := vmdb.GetState(suite.address, tc.key) + vmdb.SetState(suite.Address, tc.key, tc.value) + value := vmdb.GetState(suite.Address, tc.key) suite.Require().Equal(tc.value, value) }) } } +func (suite *KeeperTestSuite) TestSetState_Delete() { + // Set state + key := common.BytesToHash([]byte("key")) + value := common.BytesToHash([]byte("value")) + suite.App.EvmKeeper.SetState(suite.Ctx, suite.Address, key, value) + + // Check store if exists + storeKey := suite.App.GetKey(types.StoreKey) + store := prefix.NewStore(suite.Ctx.KVStore(storeKey), types.AddressStoragePrefix(suite.Address)) + + suite.Require().True(store.Has(key.Bytes()), "key/value should be set in store") + + // Set state with empty value to delete + suite.App.EvmKeeper.SetState(suite.Ctx, suite.Address, key, common.Hash{}) + + // Check store if deleted + suite.Require().False(store.Has(key.Bytes()), "key/value should be deleted from store") +} + func (suite *KeeperTestSuite) TestCommittedState() { key := common.BytesToHash([]byte("key")) value1 := common.BytesToHash([]byte("value1")) value2 := common.BytesToHash([]byte("value2")) vmdb := suite.StateDB() - vmdb.SetState(suite.address, key, value1) + vmdb.SetState(suite.Address, key, value1) vmdb.Commit() vmdb = suite.StateDB() - vmdb.SetState(suite.address, key, value2) - tmp := vmdb.GetState(suite.address, key) + vmdb.SetState(suite.Address, key, value2) + tmp := vmdb.GetState(suite.Address, key) suite.Require().Equal(value2, tmp) - tmp = vmdb.GetCommittedState(suite.address, key) + tmp = vmdb.GetCommittedState(suite.Address, key) suite.Require().Equal(value1, tmp) vmdb.Commit() vmdb = suite.StateDB() - tmp = vmdb.GetCommittedState(suite.address, key) + tmp = vmdb.GetCommittedState(suite.Address, key) suite.Require().Equal(value2, tmp) } @@ -545,11 +599,11 @@ func (suite *KeeperTestSuite) TestSuicide() { code := []byte("code") db := suite.StateDB() // Add code to account - db.SetCode(suite.address, code) - suite.Require().Equal(code, db.GetCode(suite.address)) + db.SetCode(suite.Address, code) + suite.Require().Equal(code, db.GetCode(suite.Address)) // Add state to account for i := 0; i < 5; i++ { - db.SetState(suite.address, common.BytesToHash([]byte(fmt.Sprintf("key%d", i))), common.BytesToHash([]byte(fmt.Sprintf("value%d", i)))) + db.SetState(suite.Address, common.BytesToHash([]byte(fmt.Sprintf("key%d", i))), common.BytesToHash([]byte(fmt.Sprintf("value%d", i)))) } suite.Require().NoError(db.Commit()) @@ -569,27 +623,27 @@ func (suite *KeeperTestSuite) TestSuicide() { } // Call Suicide - suite.Require().Equal(true, db.Suicide(suite.address)) + suite.Require().Equal(true, db.Suicide(suite.Address)) // Check suicided is marked - suite.Require().Equal(true, db.HasSuicided(suite.address)) + suite.Require().Equal(true, db.HasSuicided(suite.Address)) // Commit state suite.Require().NoError(db.Commit()) db = suite.StateDB() // Check code is deleted - suite.Require().Nil(db.GetCode(suite.address)) + suite.Require().Nil(db.GetCode(suite.Address)) // Check state is deleted var storage types.Storage - suite.app.EvmKeeper.ForEachStorage(suite.ctx, suite.address, func(key, value common.Hash) bool { + suite.App.EvmKeeper.ForEachStorage(suite.Ctx, suite.Address, func(key, value common.Hash) bool { storage = append(storage, types.NewState(key, value)) return true }) suite.Require().Equal(0, len(storage)) // Check account is deleted - suite.Require().Equal(common.Hash{}, db.GetCodeHash(suite.address)) + suite.Require().Equal(common.Hash{}, db.GetCodeHash(suite.Address)) // Check code is still present in addr2 and suicided is false suite.Require().NotNil(db.GetCode(addr2)) @@ -600,14 +654,14 @@ func (suite *KeeperTestSuite) TestExist() { testCases := []struct { name string address common.Address - malleate func(vm.StateDB) + malleate func(*statedb.StateDB) exists bool }{ - {"success, account exists", suite.address, func(vm.StateDB) {}, true}, - {"success, has suicided", suite.address, func(vmdb vm.StateDB) { - vmdb.Suicide(suite.address) + {"success, account exists", suite.Address, func(*statedb.StateDB) {}, true}, + {"success, has suicided", suite.Address, func(vmdb *statedb.StateDB) { + vmdb.Suicide(suite.Address) }, true}, - {"success, account doesn't exist", tests.GenerateAddress(), func(vm.StateDB) {}, false}, + {"success, account doesn't exist", tests.GenerateAddress(), func(*statedb.StateDB) {}, false}, } for _, tc := range testCases { @@ -624,17 +678,17 @@ func (suite *KeeperTestSuite) TestEmpty() { testCases := []struct { name string address common.Address - malleate func(vm.StateDB) + malleate func(*statedb.StateDB) empty bool }{ - {"empty, account exists", suite.address, func(vm.StateDB) {}, true}, + {"empty, account exists", suite.Address, func(*statedb.StateDB) {}, true}, { "not empty, positive balance", - suite.address, - func(vmdb vm.StateDB) { vmdb.AddBalance(suite.address, big.NewInt(100)) }, + suite.Address, + func(vmdb *statedb.StateDB) { vmdb.AddBalance(suite.Address, big.NewInt(100)) }, false, }, - {"empty, account doesn't exist", tests.GenerateAddress(), func(vm.StateDB) {}, true}, + {"empty, account doesn't exist", tests.GenerateAddress(), func(*statedb.StateDB) {}, true}, } for _, tc := range testCases { @@ -655,44 +709,44 @@ func (suite *KeeperTestSuite) TestSnapshot() { testCases := []struct { name string - malleate func(vm.StateDB) + malleate func(*statedb.StateDB) }{ - {"simple revert", func(vmdb vm.StateDB) { + {"simple revert", func(vmdb *statedb.StateDB) { revision := vmdb.Snapshot() suite.Require().Zero(revision) - vmdb.SetState(suite.address, key, value1) - suite.Require().Equal(value1, vmdb.GetState(suite.address, key)) + vmdb.SetState(suite.Address, key, value1) + suite.Require().Equal(value1, vmdb.GetState(suite.Address, key)) vmdb.RevertToSnapshot(revision) // reverted - suite.Require().Equal(common.Hash{}, vmdb.GetState(suite.address, key)) + suite.Require().Equal(common.Hash{}, vmdb.GetState(suite.Address, key)) }}, - {"nested snapshot/revert", func(vmdb vm.StateDB) { + {"nested snapshot/revert", func(vmdb *statedb.StateDB) { revision1 := vmdb.Snapshot() suite.Require().Zero(revision1) - vmdb.SetState(suite.address, key, value1) + vmdb.SetState(suite.Address, key, value1) revision2 := vmdb.Snapshot() - vmdb.SetState(suite.address, key, value2) - suite.Require().Equal(value2, vmdb.GetState(suite.address, key)) + vmdb.SetState(suite.Address, key, value2) + suite.Require().Equal(value2, vmdb.GetState(suite.Address, key)) vmdb.RevertToSnapshot(revision2) - suite.Require().Equal(value1, vmdb.GetState(suite.address, key)) + suite.Require().Equal(value1, vmdb.GetState(suite.Address, key)) vmdb.RevertToSnapshot(revision1) - suite.Require().Equal(common.Hash{}, vmdb.GetState(suite.address, key)) + suite.Require().Equal(common.Hash{}, vmdb.GetState(suite.Address, key)) }}, - {"jump revert", func(vmdb vm.StateDB) { + {"jump revert", func(vmdb *statedb.StateDB) { revision1 := vmdb.Snapshot() - vmdb.SetState(suite.address, key, value1) + vmdb.SetState(suite.Address, key, value1) vmdb.Snapshot() - vmdb.SetState(suite.address, key, value2) + vmdb.SetState(suite.Address, key, value2) vmdb.RevertToSnapshot(revision1) - suite.Require().Equal(common.Hash{}, vmdb.GetState(suite.address, key)) + suite.Require().Equal(common.Hash{}, vmdb.GetState(suite.Address, key)) }}, } @@ -709,13 +763,13 @@ func (suite *KeeperTestSuite) CreateTestTx(msg *types.MsgEthereumTx, priv crypto option, err := codectypes.NewAnyWithValue(&types.ExtensionOptionsEthereumTx{}) suite.Require().NoError(err) - txBuilder := suite.clientCtx.TxConfig.NewTxBuilder() + txBuilder := suite.ClientCtx.TxConfig.NewTxBuilder() builder, ok := txBuilder.(authtx.ExtensionOptionsTxBuilder) suite.Require().True(ok) builder.SetExtensionOptions(option) - err = msg.Sign(suite.ethSigner, tests.NewSigner(priv)) + err = msg.Sign(suite.EthSigner, tests.NewSigner(priv)) suite.Require().NoError(err) err = txBuilder.SetMsgs(msg) @@ -726,27 +780,27 @@ func (suite *KeeperTestSuite) CreateTestTx(msg *types.MsgEthereumTx, priv crypto func (suite *KeeperTestSuite) TestAddLog() { addr, privKey := tests.NewAddrKey() - msg := types.NewTx(big.NewInt(1), 0, &suite.address, big.NewInt(1), 100000, big.NewInt(1), nil, nil, []byte("test"), nil) + msg := types.NewTx(big.NewInt(1), 0, &suite.Address, big.NewInt(1), 100000, big.NewInt(1), nil, nil, []byte("test"), nil) msg.From = addr.Hex() tx := suite.CreateTestTx(msg, privKey) msg, _ = tx.GetMsgs()[0].(*types.MsgEthereumTx) txHash := msg.AsTransaction().Hash() - msg2 := types.NewTx(big.NewInt(1), 1, &suite.address, big.NewInt(1), 100000, big.NewInt(1), nil, nil, []byte("test"), nil) + msg2 := types.NewTx(big.NewInt(1), 1, &suite.Address, big.NewInt(1), 100000, big.NewInt(1), nil, nil, []byte("test"), nil) msg2.From = addr.Hex() tx2 := suite.CreateTestTx(msg2, privKey) msg2, _ = tx2.GetMsgs()[0].(*types.MsgEthereumTx) - msg3 := types.NewTx(big.NewInt(1), 0, &suite.address, big.NewInt(1), 100000, nil, big.NewInt(1), big.NewInt(1), []byte("test"), nil) + msg3 := types.NewTx(big.NewInt(1), 0, &suite.Address, big.NewInt(1), 100000, nil, big.NewInt(1), big.NewInt(1), []byte("test"), nil) msg3.From = addr.Hex() tx3 := suite.CreateTestTx(msg3, privKey) msg3, _ = tx3.GetMsgs()[0].(*types.MsgEthereumTx) txHash3 := msg3.AsTransaction().Hash() - msg4 := types.NewTx(big.NewInt(1), 1, &suite.address, big.NewInt(1), 100000, nil, big.NewInt(1), big.NewInt(1), []byte("test"), nil) + msg4 := types.NewTx(big.NewInt(1), 1, &suite.Address, big.NewInt(1), 100000, nil, big.NewInt(1), big.NewInt(1), []byte("test"), nil) msg4.From = addr.Hex() tx4 := suite.CreateTestTx(msg4, privKey) @@ -756,7 +810,7 @@ func (suite *KeeperTestSuite) TestAddLog() { name string hash common.Hash log, expLog *ethtypes.Log // pre and post populating log fields - malleate func(vm.StateDB) + malleate func(*statedb.StateDB) }{ { "tx hash from message", @@ -770,7 +824,7 @@ func (suite *KeeperTestSuite) TestAddLog() { TxHash: txHash, Topics: make([]common.Hash, 0), }, - func(vm.StateDB) {}, + func(*statedb.StateDB) {}, }, { "dynamicfee tx hash from message", @@ -784,15 +838,15 @@ func (suite *KeeperTestSuite) TestAddLog() { TxHash: txHash3, Topics: make([]common.Hash, 0), }, - func(vm.StateDB) {}, + func(*statedb.StateDB) {}, }, } for _, tc := range testCases { suite.Run(tc.name, func() { suite.SetupTest() - vmdb := statedb.New(suite.ctx, suite.app.EvmKeeper, statedb.NewTxConfig( - common.BytesToHash(suite.ctx.HeaderHash().Bytes()), + vmdb := statedb.New(suite.Ctx, suite.App.EvmKeeper, statedb.NewTxConfig( + common.BytesToHash(suite.Ctx.HeaderHash().Bytes()), tc.hash, 0, 0, )) @@ -815,9 +869,9 @@ func (suite *KeeperTestSuite) TestPrepareAccessList() { } vmdb := suite.StateDB() - vmdb.PrepareAccessList(suite.address, &dest, precompiles, accesses) + vmdb.PrepareAccessList(suite.Address, &dest, precompiles, accesses) - suite.Require().True(vmdb.AddressInAccessList(suite.address)) + suite.Require().True(vmdb.AddressInAccessList(suite.Address)) suite.Require().True(vmdb.AddressInAccessList(dest)) for _, precompile := range precompiles { @@ -838,8 +892,8 @@ func (suite *KeeperTestSuite) TestAddAddressToAccessList() { name string addr common.Address }{ - {"new address", suite.address}, - {"existing address", suite.address}, + {"new address", suite.Address}, + {"existing address", suite.Address}, } for _, tc := range testCases { @@ -859,9 +913,9 @@ func (suite *KeeperTestSuite) AddSlotToAccessList() { slot common.Hash }{ {"new address and slot (1)", tests.GenerateAddress(), common.BytesToHash([]byte("hash"))}, - {"new address and slot (2)", suite.address, common.Hash{}}, - {"existing address and slot", suite.address, common.Hash{}}, - {"existing address, new slot", suite.address, common.BytesToHash([]byte("hash"))}, + {"new address and slot (2)", suite.Address, common.Hash{}}, + {"existing address and slot", suite.Address, common.Hash{}}, + {"existing address, new slot", suite.Address, common.BytesToHash([]byte("hash"))}, } for _, tc := range testCases { @@ -881,15 +935,15 @@ func (suite *KeeperTestSuite) _TestForEachStorage() { testCase := []struct { name string - malleate func(vm.StateDB) + malleate func(*statedb.StateDB) callback func(key, value common.Hash) (stop bool) expValues []common.Hash }{ { "aggregate state", - func(vmdb vm.StateDB) { + func(vmdb *statedb.StateDB) { for i := 0; i < 5; i++ { - vmdb.SetState(suite.address, common.BytesToHash([]byte(fmt.Sprintf("key%d", i))), common.BytesToHash([]byte(fmt.Sprintf("value%d", i)))) + vmdb.SetState(suite.Address, common.BytesToHash([]byte(fmt.Sprintf("key%d", i))), common.BytesToHash([]byte(fmt.Sprintf("value%d", i)))) } }, func(key, value common.Hash) bool { @@ -906,9 +960,9 @@ func (suite *KeeperTestSuite) _TestForEachStorage() { }, { "filter state", - func(vmdb vm.StateDB) { - vmdb.SetState(suite.address, common.BytesToHash([]byte("key")), common.BytesToHash([]byte("value"))) - vmdb.SetState(suite.address, common.BytesToHash([]byte("filterkey")), common.BytesToHash([]byte("filtervalue"))) + func(vmdb *statedb.StateDB) { + vmdb.SetState(suite.Address, common.BytesToHash([]byte("key")), common.BytesToHash([]byte("value"))) + vmdb.SetState(suite.Address, common.BytesToHash([]byte("filterkey")), common.BytesToHash([]byte("filtervalue"))) }, func(key, value common.Hash) bool { if value == common.BytesToHash([]byte("filtervalue")) { @@ -929,7 +983,7 @@ func (suite *KeeperTestSuite) _TestForEachStorage() { vmdb := suite.StateDB() tc.malleate(vmdb) - err := vmdb.ForEachStorage(suite.address, tc.callback) + err := vmdb.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)) @@ -956,13 +1010,13 @@ func (suite *KeeperTestSuite) TestSetBalance() { }{ { "address without funds - invalid amount", - suite.address, + suite.Address, func() {}, true, }, { "mint to address", - suite.address, + suite.Address, func() { amount = big.NewInt(100) }, @@ -970,7 +1024,7 @@ func (suite *KeeperTestSuite) TestSetBalance() { }, { "burn from address", - suite.address, + suite.Address, func() { amount = big.NewInt(60) }, @@ -978,7 +1032,7 @@ func (suite *KeeperTestSuite) TestSetBalance() { }, { "address with funds - invalid amount", - suite.address, + suite.Address, func() { amount = big.NewInt(-10) }, @@ -990,11 +1044,11 @@ func (suite *KeeperTestSuite) TestSetBalance() { suite.Run(tc.name, func() { suite.SetupTest() tc.malleate() - err := suite.app.EvmKeeper.SetBalance(suite.ctx, tc.addr, amount) + err := suite.App.EvmKeeper.SetBalance(suite.Ctx, tc.addr, amount) if tc.expErr { suite.Require().Error(err) } else { - balance := suite.app.EvmKeeper.GetBalance(suite.ctx, tc.addr) + balance := suite.App.EvmKeeper.GetBalance(suite.Ctx, tc.addr) suite.Require().NoError(err) suite.Require().Equal(amount, balance) } @@ -1004,7 +1058,7 @@ func (suite *KeeperTestSuite) TestSetBalance() { func (suite *KeeperTestSuite) TestDeleteAccount() { supply := big.NewInt(100) - contractAddr := suite.DeployTestContract(suite.T(), suite.address, supply) + contractAddr := suite.DeployTestContract(suite.T(), suite.Address, supply) testCases := []struct { name string @@ -1013,7 +1067,7 @@ func (suite *KeeperTestSuite) TestDeleteAccount() { }{ { "remove address", - suite.address, + suite.Address, false, }, { @@ -1031,14 +1085,46 @@ func (suite *KeeperTestSuite) TestDeleteAccount() { for _, tc := range testCases { suite.Run(tc.name, func() { suite.SetupTest() - err := suite.app.EvmKeeper.DeleteAccount(suite.ctx, tc.addr) + err := suite.App.EvmKeeper.DeleteAccount(suite.Ctx, tc.addr) if tc.expErr { suite.Require().Error(err) } else { suite.Require().NoError(err) - balance := suite.app.EvmKeeper.GetBalance(suite.ctx, tc.addr) + balance := suite.App.EvmKeeper.GetBalance(suite.Ctx, tc.addr) suite.Require().Equal(new(big.Int), balance) } }) } } + +func (suite *KeeperTestSuite) TestUnsetBalanceChange() { + // init non-zero balance + db := statedb.New(suite.Ctx, suite.App.EvmKeeper, emptyTxConfig) + db.AddBalance(suite.Address, big.NewInt(100)) + suite.Require().NoError(db.Commit()) + + suite.Commit() + + // No-op change + db = statedb.New(suite.Ctx, suite.App.EvmKeeper, emptyTxConfig) + + db.AddBalance(suite.Address, big.NewInt(100)) + suite.Require().NotZero(db.GetBalance(suite.Address).Int64()) + + db.SubBalance(suite.Address, big.NewInt(10)) + db.SubBalance(suite.Address, big.NewInt(90)) + + suite.Require().Equal(int64(100), db.GetBalance(suite.Address).Int64()) + + // Currently doesn't actually test if the state was removed, but just if it + // doesn't error + suite.Require().NoError(db.Commit()) + + suite.Commit() + + store := suite.App.CommitMultiStore().GetStore(suite.App.GetKey(banktypes.StoreKey)) + iavlStore := store.(*iavl.Store) + + commitID1 := iavlStore.LastCommitID() + suite.T().Logf("commitID: %x", commitID1.Hash) +} diff --git a/x/evm/keeper/utils_test.go b/x/evm/keeper/utils_test.go index f14e119f29..6b61012c8b 100644 --- a/x/evm/keeper/utils_test.go +++ b/x/evm/keeper/utils_test.go @@ -35,168 +35,168 @@ func (suite *KeeperTestSuite) TestCheckSenderBalance() { }{ { name: "Enough balance", - to: suite.address.String(), + to: suite.Address.String(), gasLimit: 10, gasPrice: &oneInt, cost: &oneInt, - from: suite.address.String(), + from: suite.Address.String(), accessList: ðtypes.AccessList{}, expectPass: true, }, { name: "Equal balance", - to: suite.address.String(), + to: suite.Address.String(), gasLimit: 99, gasPrice: &oneInt, cost: &oneInt, - from: suite.address.String(), + from: suite.Address.String(), accessList: ðtypes.AccessList{}, expectPass: true, }, { name: "negative cost", - to: suite.address.String(), + to: suite.Address.String(), gasLimit: 1, gasPrice: &oneInt, cost: &negInt, - from: suite.address.String(), + from: suite.Address.String(), accessList: ðtypes.AccessList{}, expectPass: false, }, { name: "Higher gas limit, not enough balance", - to: suite.address.String(), + to: suite.Address.String(), gasLimit: 100, gasPrice: &oneInt, cost: &oneInt, - from: suite.address.String(), + from: suite.Address.String(), accessList: ðtypes.AccessList{}, expectPass: false, }, { name: "Higher gas price, enough balance", - to: suite.address.String(), + to: suite.Address.String(), gasLimit: 10, gasPrice: &fiveInt, cost: &oneInt, - from: suite.address.String(), + from: suite.Address.String(), accessList: ðtypes.AccessList{}, expectPass: true, }, { name: "Higher gas price, not enough balance", - to: suite.address.String(), + to: suite.Address.String(), gasLimit: 20, gasPrice: &fiveInt, cost: &oneInt, - from: suite.address.String(), + from: suite.Address.String(), accessList: ðtypes.AccessList{}, expectPass: false, }, { name: "Higher cost, enough balance", - to: suite.address.String(), + to: suite.Address.String(), gasLimit: 10, gasPrice: &fiveInt, cost: &fiftyInt, - from: suite.address.String(), + from: suite.Address.String(), accessList: ðtypes.AccessList{}, expectPass: true, }, { name: "Higher cost, not enough balance", - to: suite.address.String(), + to: suite.Address.String(), gasLimit: 10, gasPrice: &fiveInt, cost: &hundredInt, - from: suite.address.String(), + from: suite.Address.String(), accessList: ðtypes.AccessList{}, expectPass: false, }, { name: "Enough balance w/ enableFeemarket", - to: suite.address.String(), + to: suite.Address.String(), gasLimit: 10, gasFeeCap: big.NewInt(1), cost: &oneInt, - from: suite.address.String(), + from: suite.Address.String(), accessList: ðtypes.AccessList{}, expectPass: true, enableFeemarket: true, }, { name: "Equal balance w/ enableFeemarket", - to: suite.address.String(), + to: suite.Address.String(), gasLimit: 99, gasFeeCap: big.NewInt(1), cost: &oneInt, - from: suite.address.String(), + from: suite.Address.String(), accessList: ðtypes.AccessList{}, expectPass: true, enableFeemarket: true, }, { name: "negative cost w/ enableFeemarket", - to: suite.address.String(), + to: suite.Address.String(), gasLimit: 1, gasFeeCap: big.NewInt(1), cost: &negInt, - from: suite.address.String(), + from: suite.Address.String(), accessList: ðtypes.AccessList{}, expectPass: false, enableFeemarket: true, }, { name: "Higher gas limit, not enough balance w/ enableFeemarket", - to: suite.address.String(), + to: suite.Address.String(), gasLimit: 100, gasFeeCap: big.NewInt(1), cost: &oneInt, - from: suite.address.String(), + from: suite.Address.String(), accessList: ðtypes.AccessList{}, expectPass: false, enableFeemarket: true, }, { name: "Higher gas price, enough balance w/ enableFeemarket", - to: suite.address.String(), + to: suite.Address.String(), gasLimit: 10, gasFeeCap: big.NewInt(5), cost: &oneInt, - from: suite.address.String(), + from: suite.Address.String(), accessList: ðtypes.AccessList{}, expectPass: true, enableFeemarket: true, }, { name: "Higher gas price, not enough balance w/ enableFeemarket", - to: suite.address.String(), + to: suite.Address.String(), gasLimit: 20, gasFeeCap: big.NewInt(5), cost: &oneInt, - from: suite.address.String(), + from: suite.Address.String(), accessList: ðtypes.AccessList{}, expectPass: false, enableFeemarket: true, }, { name: "Higher cost, enough balance w/ enableFeemarket", - to: suite.address.String(), + to: suite.Address.String(), gasLimit: 10, gasFeeCap: big.NewInt(5), cost: &fiftyInt, - from: suite.address.String(), + from: suite.Address.String(), accessList: ðtypes.AccessList{}, expectPass: true, enableFeemarket: true, }, { name: "Higher cost, not enough balance w/ enableFeemarket", - to: suite.address.String(), + to: suite.Address.String(), gasLimit: 10, gasFeeCap: big.NewInt(5), cost: &hundredInt, - from: suite.address.String(), + from: suite.Address.String(), accessList: ðtypes.AccessList{}, expectPass: false, enableFeemarket: true, @@ -204,8 +204,8 @@ func (suite *KeeperTestSuite) TestCheckSenderBalance() { } vmdb := suite.StateDB() - vmdb.AddBalance(suite.address, hundredInt.BigInt()) - balance := vmdb.GetBalance(suite.address) + vmdb.AddBalance(suite.Address, hundredInt.BigInt()) + balance := vmdb.GetBalance(suite.Address) suite.Require().Equal(balance, hundredInt.BigInt()) err := vmdb.Commit() suite.Require().NoError(err, "Unexpected error while committing to vmdb: %d", err) @@ -237,9 +237,9 @@ func (suite *KeeperTestSuite) TestCheckSenderBalance() { txData, _ := evmtypes.UnpackTxData(tx.Data) - acct := suite.app.EvmKeeper.GetAccountOrEmpty(suite.ctx, suite.address) + bal := suite.App.EvmKeeper.GetBalance(suite.Ctx, suite.Address) err := keeper.CheckSenderBalance( - sdkmath.NewIntFromBigInt(acct.Balance), + sdkmath.NewIntFromBigInt(bal), txData, ) @@ -290,7 +290,7 @@ func (suite *KeeperTestSuite) TestVerifyFeeAndDeductTxCostsFromUserBalance() { accessList: ðtypes.AccessList{}, expectPassVerify: true, expectPassDeduct: true, - from: suite.address.String(), + from: suite.Address.String(), }, { name: "Equal balance", @@ -300,7 +300,7 @@ func (suite *KeeperTestSuite) TestVerifyFeeAndDeductTxCostsFromUserBalance() { accessList: ðtypes.AccessList{}, expectPassVerify: true, expectPassDeduct: true, - from: suite.address.String(), + from: suite.Address.String(), }, { name: "Higher gas limit, not enough balance", @@ -310,7 +310,7 @@ func (suite *KeeperTestSuite) TestVerifyFeeAndDeductTxCostsFromUserBalance() { accessList: ðtypes.AccessList{}, expectPassVerify: true, expectPassDeduct: false, - from: suite.address.String(), + from: suite.Address.String(), }, { name: "Higher gas price, enough balance", @@ -320,7 +320,7 @@ func (suite *KeeperTestSuite) TestVerifyFeeAndDeductTxCostsFromUserBalance() { accessList: ðtypes.AccessList{}, expectPassVerify: true, expectPassDeduct: true, - from: suite.address.String(), + from: suite.Address.String(), }, { name: "Higher gas price, not enough balance", @@ -330,7 +330,7 @@ func (suite *KeeperTestSuite) TestVerifyFeeAndDeductTxCostsFromUserBalance() { accessList: ðtypes.AccessList{}, expectPassVerify: true, expectPassDeduct: false, - from: suite.address.String(), + from: suite.Address.String(), }, // This case is expected to be true because the fees can be deducted, but the tx // execution is going to fail because there is no more balance to pay the cost @@ -342,7 +342,7 @@ func (suite *KeeperTestSuite) TestVerifyFeeAndDeductTxCostsFromUserBalance() { accessList: ðtypes.AccessList{}, expectPassVerify: true, expectPassDeduct: true, - from: suite.address.String(), + from: suite.Address.String(), }, // testcases with enableFeemarket enabled. { @@ -355,7 +355,7 @@ func (suite *KeeperTestSuite) TestVerifyFeeAndDeductTxCostsFromUserBalance() { expectPassVerify: false, expectPassDeduct: true, enableFeemarket: true, - from: suite.address.String(), + from: suite.Address.String(), }, { name: "empty tip fee is valid to deduct", @@ -367,7 +367,7 @@ func (suite *KeeperTestSuite) TestVerifyFeeAndDeductTxCostsFromUserBalance() { expectPassVerify: true, expectPassDeduct: true, enableFeemarket: true, - from: suite.address.String(), + from: suite.Address.String(), }, { name: "effectiveTip equal to gasTipCap", @@ -378,7 +378,7 @@ func (suite *KeeperTestSuite) TestVerifyFeeAndDeductTxCostsFromUserBalance() { expectPassVerify: true, expectPassDeduct: true, enableFeemarket: true, - from: suite.address.String(), + from: suite.Address.String(), }, { name: "effectiveTip equal to (gasFeeCap - baseFee)", @@ -390,7 +390,7 @@ func (suite *KeeperTestSuite) TestVerifyFeeAndDeductTxCostsFromUserBalance() { expectPassVerify: true, expectPassDeduct: true, enableFeemarket: true, - from: suite.address.String(), + from: suite.Address.String(), }, { name: "Invalid from address", @@ -409,13 +409,13 @@ func (suite *KeeperTestSuite) TestVerifyFeeAndDeductTxCostsFromUserBalance() { cost: &oneInt, accessList: ðtypes.AccessList{ ethtypes.AccessTuple{ - Address: suite.address, + Address: suite.Address, StorageKeys: []common.Hash{}, }, }, expectPassVerify: true, expectPassDeduct: true, - from: suite.address.String(), + from: suite.Address.String(), }, { name: "gasLimit < intrinsicGas during IsCheckTx", @@ -425,16 +425,16 @@ func (suite *KeeperTestSuite) TestVerifyFeeAndDeductTxCostsFromUserBalance() { accessList: ðtypes.AccessList{}, expectPassVerify: false, expectPassDeduct: true, - from: suite.address.String(), + from: suite.Address.String(), malleate: func() { - suite.ctx = suite.ctx.WithIsCheckTx(true) + suite.Ctx = suite.Ctx.WithIsCheckTx(true) }, }, } for i, tc := range testCases { suite.Run(tc.name, func() { - suite.enableFeemarket = tc.enableFeemarket + suite.EnableFeemarket = tc.enableFeemarket suite.SetupTest() vmdb := suite.StateDB() @@ -446,7 +446,7 @@ func (suite *KeeperTestSuite) TestVerifyFeeAndDeductTxCostsFromUserBalance() { amount = tc.cost.BigInt() } - if suite.enableFeemarket { + if suite.EnableFeemarket { if tc.gasFeeCap != nil { gasFeeCap = tc.gasFeeCap } @@ -455,36 +455,36 @@ func (suite *KeeperTestSuite) TestVerifyFeeAndDeductTxCostsFromUserBalance() { } else { gasTipCap = tc.gasTipCap } - vmdb.AddBalance(suite.address, initBalance.BigInt()) - balance := vmdb.GetBalance(suite.address) + vmdb.AddBalance(suite.Address, initBalance.BigInt()) + balance := vmdb.GetBalance(suite.Address) suite.Require().Equal(balance, initBalance.BigInt()) } else { if tc.gasPrice != nil { gasPrice = tc.gasPrice.BigInt() } - vmdb.AddBalance(suite.address, hundredInt.BigInt()) - balance := vmdb.GetBalance(suite.address) + vmdb.AddBalance(suite.Address, hundredInt.BigInt()) + balance := vmdb.GetBalance(suite.Address) suite.Require().Equal(balance, hundredInt.BigInt()) } err := vmdb.Commit() suite.Require().NoError(err, "Unexpected error while committing to vmdb: %d", err) - tx := evmtypes.NewTx(zeroInt.BigInt(), 1, &suite.address, amount, tc.gasLimit, gasPrice, gasFeeCap, gasTipCap, nil, tc.accessList) + tx := evmtypes.NewTx(zeroInt.BigInt(), 1, &suite.Address, amount, tc.gasLimit, gasPrice, gasFeeCap, gasTipCap, nil, tc.accessList) tx.From = tc.from txData, _ := evmtypes.UnpackTxData(tx.Data) - evmParams := suite.app.EvmKeeper.GetParams(suite.ctx) + evmParams := suite.App.EvmKeeper.GetParams(suite.Ctx) ethCfg := evmParams.GetChainConfig().EthereumConfig(nil) - baseFee := suite.app.EvmKeeper.GetBaseFee(suite.ctx, ethCfg) + baseFee := suite.App.EvmKeeper.GetBaseFee(suite.Ctx, ethCfg) priority := evmtypes.GetTxPriority(txData, baseFee) - fees, err := keeper.VerifyFee(txData, evmtypes.DefaultEVMDenom, baseFee, false, false, suite.ctx.IsCheckTx()) + fees, err := keeper.VerifyFee(txData, evmtypes.DefaultEVMDenom, baseFee, false, false, suite.Ctx.IsCheckTx()) if tc.expectPassVerify { suite.Require().NoError(err, "valid test %d failed - '%s'", i, tc.name) if tc.enableFeemarket { - baseFee := suite.app.FeeMarketKeeper.GetBaseFee(suite.ctx) + baseFee := suite.App.FeeMarketKeeper.GetBaseFee(suite.Ctx) suite.Require().Equal( fees, sdk.NewCoins( @@ -507,7 +507,7 @@ func (suite *KeeperTestSuite) TestVerifyFeeAndDeductTxCostsFromUserBalance() { suite.Require().Nil(fees, "invalid test %d passed. fees value must be nil - '%s'", i, tc.name) } - err = suite.app.EvmKeeper.DeductTxCostsFromUserBalance(suite.ctx, fees, common.HexToAddress(tx.From)) + err = suite.App.EvmKeeper.DeductTxCostsFromUserBalance(suite.Ctx, fees, common.HexToAddress(tx.From)) if tc.expectPassDeduct { suite.Require().NoError(err, "valid test %d failed - '%s'", i, tc.name) } else { @@ -515,5 +515,5 @@ func (suite *KeeperTestSuite) TestVerifyFeeAndDeductTxCostsFromUserBalance() { } }) } - suite.enableFeemarket = false // reset flag + suite.EnableFeemarket = false // reset flag } diff --git a/x/evm/statedb/ctx.go b/x/evm/statedb/ctx.go new file mode 100644 index 0000000000..0b8c5179e0 --- /dev/null +++ b/x/evm/statedb/ctx.go @@ -0,0 +1,112 @@ +package statedb + +import ( + "fmt" + "sort" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// SnapshotCommitCtx provides a way to create snapshots of branched contexts and +// only write state to the initial context when Commit() is called. +type SnapshotCommitCtx struct { + initialCtx sdk.Context + snapshots []CtxSnapshot +} + +// NewSnapshotCtx creates a new SnapshotCtx from the initial context. +func NewSnapshotCtx(initialCtx sdk.Context) *SnapshotCommitCtx { + sCtx := &SnapshotCommitCtx{ + initialCtx: initialCtx, + snapshots: nil, + } + + // Create an initial snapshot of the initialCtx so no state is written until + // Commit() is called. The ID is -1 but disregarded along with the + // StoreRevertKey indices as this is only to branch the ctx. + sCtx.Snapshot(-1) + + return sCtx +} + +// InitialCtx returns the initial context prior to any snapshots. +func (c *SnapshotCommitCtx) InitialCtx() sdk.Context { + return c.initialCtx +} + +// CurrentCtx returns the current ctx, either the latest branched ctx, or the +// initial ctx if there are no snapshots. +func (c *SnapshotCommitCtx) CurrentCtx() sdk.Context { + if len(c.snapshots) == 0 { + return c.initialCtx + } + + return c.snapshots[len(c.snapshots)-1].ctx +} + +// CurrentSnapshot returns the current snapshot and true if there is one, or +// false if there are no snapshots. +func (c *SnapshotCommitCtx) CurrentSnapshot() (CtxSnapshot, bool) { + if len(c.snapshots) == 0 { + return CtxSnapshot{}, false + } + + return c.snapshots[len(c.snapshots)-1], true +} + +// Snapshot creates a new branched context with the specified snapshot ID. +func (c *SnapshotCommitCtx) Snapshot(snapshotID int) { + // Branch off a new CacheMultiStore + write function + newCtx, newWrite := c.CurrentCtx().CacheContext() + + // Disable tracing for the branched context + ms := newCtx.MultiStore().SetTracer(nil) + newCtx = newCtx.WithMultiStore(ms) + + // Save the new snapshot to the list + c.snapshots = append(c.snapshots, CtxSnapshot{ + id: snapshotID, + ctx: newCtx, + write: newWrite, + }) +} + +// Revert reverts the state to the given revision id. +func (c *SnapshotCommitCtx) Revert(revid int) { + // Find the snapshot in the stack of valid snapshots. + idx := sort.Search(len(c.snapshots), func(i int) bool { + return c.snapshots[i].id >= revid + }) + + if idx == -1 { + panic(fmt.Errorf("revision id %v does not exist", revid)) + } + + // Index is invalid or the revision id is not the same somehow + if idx >= len(c.snapshots) || c.snapshots[idx].id != revid { + panic(fmt.Errorf("revision id %v is invalid", revid)) + } + + // Remove invalidated snapshots + c.snapshots = c.snapshots[:idx] +} + +// Commit writes all the branched contexts to the initialContext. +func (c *SnapshotCommitCtx) Commit() { + // Write snapshots from newest to oldest. + // Each store.Write() applies the state changes to its parent / previous snapshot + for i := len(c.snapshots) - 1; i >= 0; i-- { + snapshot := c.snapshots[i] + snapshot.write() + + // initialCtx should not be considered as a snapshot, so we don't need + // to call Write() on it to apply the changes. + } +} + +// CtxSnapshot is a single snapshot with a branched context. +type CtxSnapshot struct { + id int + ctx sdk.Context + write func() +} diff --git a/x/evm/statedb/interfaces.go b/x/evm/statedb/interfaces.go index e4e83e09c3..64ad6b3890 100644 --- a/x/evm/statedb/interfaces.go +++ b/x/evm/statedb/interfaces.go @@ -16,6 +16,8 @@ package statedb import ( + "math/big" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/vm" @@ -35,6 +37,7 @@ type ExtStateDB interface { type Keeper interface { // Read methods GetAccount(ctx sdk.Context, addr common.Address) *Account + GetBalance(ctx sdk.Context, addr common.Address) *big.Int GetState(ctx sdk.Context, addr common.Address, key common.Hash) common.Hash GetCode(ctx sdk.Context, codeHash common.Hash) []byte // the callback returns false to break early @@ -42,7 +45,8 @@ type Keeper interface { // Write methods, only called by `StateDB.Commit()` SetAccount(ctx sdk.Context, addr common.Address, account Account) error - SetState(ctx sdk.Context, addr common.Address, key common.Hash, value []byte) + SetState(ctx sdk.Context, addr common.Address, key, value common.Hash) SetCode(ctx sdk.Context, codeHash []byte, code []byte) + SetBalance(ctx sdk.Context, addr common.Address, amount *big.Int) error DeleteAccount(ctx sdk.Context, addr common.Address) error } diff --git a/x/evm/statedb/journal.go b/x/evm/statedb/journal.go index 49f61c0040..31c4b36f49 100644 --- a/x/evm/statedb/journal.go +++ b/x/evm/statedb/journal.go @@ -110,7 +110,6 @@ type ( // Changes to individual accounts. balanceChange struct { account *common.Address - prev *big.Int } nonceChange struct { account *common.Address @@ -169,8 +168,8 @@ func (ch suicideChange) Dirtied() *common.Address { return ch.account } -func (ch balanceChange) Revert(s *StateDB) { - s.getStateObject(*ch.account).setBalance(ch.prev) +func (ch balanceChange) Revert(_ *StateDB) { + // Do nothing, balance is reverted by SnapshotCommitCtx } func (ch balanceChange) Dirtied() *common.Address { diff --git a/x/evm/statedb/mock_test.go b/x/evm/statedb/mock_test.go deleted file mode 100644 index 544cbfa1b4..0000000000 --- a/x/evm/statedb/mock_test.go +++ /dev/null @@ -1,114 +0,0 @@ -package statedb_test - -import ( - "bytes" - "errors" - "math/big" - - sdk "github.com/cosmos/cosmos-sdk/types" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" - "github.com/evmos/ethermint/x/evm/statedb" -) - -var ( - _ statedb.Keeper = &MockKeeper{} - errAddress common.Address = common.BigToAddress(big.NewInt(100)) - emptyCodeHash = crypto.Keccak256(nil) -) - -type MockAcount struct { - account statedb.Account - states statedb.Storage -} - -type MockKeeper struct { - accounts map[common.Address]MockAcount - codes map[common.Hash][]byte -} - -func NewMockKeeper() *MockKeeper { - return &MockKeeper{ - accounts: make(map[common.Address]MockAcount), - codes: make(map[common.Hash][]byte), - } -} - -func (k MockKeeper) GetAccount(ctx sdk.Context, addr common.Address) *statedb.Account { - acct, ok := k.accounts[addr] - if !ok { - return nil - } - return &acct.account -} - -func (k MockKeeper) GetState(ctx sdk.Context, addr common.Address, key common.Hash) common.Hash { - return k.accounts[addr].states[key] -} - -func (k MockKeeper) GetCode(ctx sdk.Context, codeHash common.Hash) []byte { - return k.codes[codeHash] -} - -func (k MockKeeper) ForEachStorage(ctx sdk.Context, addr common.Address, cb func(key, value common.Hash) bool) { - if acct, ok := k.accounts[addr]; ok { - for k, v := range acct.states { - if !cb(k, v) { - return - } - } - } -} - -func (k MockKeeper) SetAccount(ctx sdk.Context, addr common.Address, account statedb.Account) error { - if addr == errAddress { - return errors.New("mock db error") - } - acct, exists := k.accounts[addr] - if exists { - // update - acct.account = account - k.accounts[addr] = acct - } else { - k.accounts[addr] = MockAcount{account: account, states: make(statedb.Storage)} - } - return nil -} - -func (k MockKeeper) SetState(ctx sdk.Context, addr common.Address, key common.Hash, value []byte) { - if acct, ok := k.accounts[addr]; ok { - if len(value) == 0 { - delete(acct.states, key) - } else { - acct.states[key] = common.BytesToHash(value) - } - } -} - -func (k MockKeeper) SetCode(ctx sdk.Context, codeHash []byte, code []byte) { - k.codes[common.BytesToHash(codeHash)] = code -} - -func (k MockKeeper) DeleteAccount(ctx sdk.Context, addr common.Address) error { - if addr == errAddress { - return errors.New("mock db error") - } - old := k.accounts[addr] - delete(k.accounts, addr) - if !bytes.Equal(old.account.CodeHash, emptyCodeHash) { - delete(k.codes, common.BytesToHash(old.account.CodeHash)) - } - return nil -} - -func (k MockKeeper) Clone() *MockKeeper { - accounts := make(map[common.Address]MockAcount, len(k.accounts)) - for k, v := range k.accounts { - accounts[k] = v - } - codes := make(map[common.Hash][]byte, len(k.codes)) - for k, v := range k.codes { - codes[k] = v - } - return &MockKeeper{accounts, codes} -} diff --git a/x/evm/statedb/state_object.go b/x/evm/statedb/state_object.go index dd344dfadb..fd4a5a50cb 100644 --- a/x/evm/statedb/state_object.go +++ b/x/evm/statedb/state_object.go @@ -29,15 +29,14 @@ var emptyCodeHash = crypto.Keccak256(nil) // Account is the Ethereum consensus representation of accounts. // These objects are stored in the storage of auth module. type Account struct { + // Balance is *not* included as it is managed by bank Nonce uint64 - Balance *big.Int CodeHash []byte } // NewEmptyAccount returns an empty account. func NewEmptyAccount() *Account { return &Account{ - Balance: new(big.Int), CodeHash: emptyCodeHash, } } @@ -83,10 +82,11 @@ type stateObject struct { } // newObject creates a state object. -func newObject(db *StateDB, address common.Address, account Account) *stateObject { - if account.Balance == nil { - account.Balance = new(big.Int) - } +func newObject( + db *StateDB, + address common.Address, + account Account, +) *stateObject { if account.CodeHash == nil { account.CodeHash = emptyCodeHash } @@ -101,7 +101,9 @@ func newObject(db *StateDB, address common.Address, account Account) *stateObjec // empty returns whether the account is considered empty. func (s *stateObject) empty() bool { - return s.account.Nonce == 0 && s.account.Balance.Sign() == 0 && bytes.Equal(s.account.CodeHash, emptyCodeHash) + return s.account.Nonce == 0 && + s.Balance().Sign() == 0 && + bytes.Equal(s.account.CodeHash, emptyCodeHash) } func (s *stateObject) markSuicided() { @@ -128,15 +130,19 @@ func (s *stateObject) SubBalance(amount *big.Int) { // SetBalance update account balance. func (s *stateObject) SetBalance(amount *big.Int) { + // Create a journal entry that only contains the address to track dirties. + // The prev value is not used as snapshots/rollbacks for balance state is + // managed by SnapshotCommitCtx s.db.journal.append(balanceChange{ account: &s.address, - prev: new(big.Int).Set(s.account.Balance), }) s.setBalance(amount) } func (s *stateObject) setBalance(amount *big.Int) { - s.account.Balance = amount + if err := s.db.keeper.SetBalance(s.db.ctx.CurrentCtx(), s.address, amount); err != nil { + s.db.SetError(err) + } } // @@ -156,7 +162,7 @@ func (s *stateObject) Code() []byte { if bytes.Equal(s.CodeHash(), emptyCodeHash) { return nil } - code := s.db.keeper.GetCode(s.db.ctx, common.BytesToHash(s.CodeHash())) + code := s.db.keeper.GetCode(s.db.ctx.InitialCtx(), common.BytesToHash(s.CodeHash())) s.code = code return code } @@ -204,7 +210,14 @@ func (s *stateObject) CodeHash() []byte { // Balance returns the balance of account func (s *stateObject) Balance() *big.Int { - return s.account.Balance + // Balance tracking uses the current ctx state, NOT journal state as + // precompiles can modify balances directly. + return s.db.keeper.GetBalance(s.db.ctx.CurrentCtx(), s.address) +} + +// CommittedBalance returns the committed balance of account +func (s *stateObject) CommittedBalance() *big.Int { + return s.db.keeper.GetBalance(s.db.ctx.InitialCtx(), s.address) } // Nonce returns the nonce of account @@ -218,7 +231,7 @@ func (s *stateObject) GetCommittedState(key common.Hash) common.Hash { return value } // If no live objects are available, load it from keeper - value := s.db.keeper.GetState(s.db.ctx, s.Address(), key) + value := s.db.keeper.GetState(s.db.ctx.InitialCtx(), s.Address(), key) s.originStorage[key] = value return value } diff --git a/x/evm/statedb/statedb.go b/x/evm/statedb/statedb.go index 941cb0c21b..969e9d2d3e 100644 --- a/x/evm/statedb/statedb.go +++ b/x/evm/statedb/statedb.go @@ -45,7 +45,9 @@ var _ vm.StateDB = &StateDB{} // * Accounts type StateDB struct { keeper Keeper - ctx sdk.Context + + ctx *SnapshotCommitCtx + sdkError error // Journal of state modifications. This is the backbone of // Snapshot and RevertToSnapshot. @@ -71,7 +73,8 @@ type StateDB struct { func New(ctx sdk.Context, keeper Keeper, txConfig TxConfig) *StateDB { return &StateDB{ keeper: keeper, - ctx: ctx, + ctx: NewSnapshotCtx(ctx), + sdkError: nil, stateObjects: make(map[common.Address]*stateObject), journal: newJournal(), accessList: newAccessList(), @@ -85,6 +88,13 @@ func (s *StateDB) Keeper() Keeper { return s.keeper } +// Context returns the current sdk.Context of the latest snapshot for any +// stateful cosmos operations. +// NOTE: This should not be used for querying or modifying evm state. +func (s *StateDB) Context() sdk.Context { + return s.ctx.CurrentCtx() +} + // AddLog adds a log, called by evm. func (s *StateDB) AddLog(log *ethtypes.Log) { s.journal.append(addLogChange{}) @@ -132,6 +142,8 @@ func (s *StateDB) Empty(addr common.Address) bool { // GetBalance retrieves the balance from the given address or 0 if object not found func (s *StateDB) GetBalance(addr common.Address) *big.Int { + // TODO: This should use the active ctx balance + stateObject := s.getStateObject(addr) if stateObject != nil { return stateObject.Balance() @@ -221,8 +233,10 @@ func (s *StateDB) getStateObject(addr common.Address) *stateObject { if obj := s.stateObjects[addr]; obj != nil { return obj } - // If no live objects are available, load it from keeper - account := s.keeper.GetAccount(s.ctx, addr) + // If no live objects are available, load it from keeper. + // Use the current context to load the account as it may have been modified + // by a precompile. + account := s.keeper.GetAccount(s.ctx.CurrentCtx(), addr) if account == nil { return nil } @@ -272,7 +286,7 @@ func (s *StateDB) createObject(addr common.Address) (newobj, prev *stateObject) func (s *StateDB) CreateAccount(addr common.Address) { newObj, prev := s.createObject(addr) if prev != nil { - newObj.setBalance(prev.account.Balance) + newObj.setBalance(prev.Balance()) } } @@ -282,7 +296,9 @@ func (s *StateDB) ForEachStorage(addr common.Address, cb func(key, value common. if so == nil { return nil } - s.keeper.ForEachStorage(s.ctx, addr, func(key, value common.Hash) bool { + + // TODO: InitialCtx or CurrentCtx? Neither will include new keys + s.keeper.ForEachStorage(s.ctx.CurrentCtx(), addr, func(key, value common.Hash) bool { if value, dirty := so.dirtyStorage[key]; dirty { return cb(key, value) } @@ -291,6 +307,7 @@ func (s *StateDB) ForEachStorage(addr common.Address, cb func(key, value common. } return true }) + return nil } @@ -358,7 +375,7 @@ func (s *StateDB) Suicide(addr common.Address) bool { prevbalance: new(big.Int).Set(stateObject.Balance()), }) stateObject.markSuicided() - stateObject.account.Balance = new(big.Int) + stateObject.SetBalance(new(big.Int)) return true } @@ -429,6 +446,9 @@ func (s *StateDB) Snapshot() int { id := s.nextRevisionID s.nextRevisionID++ s.validRevisions = append(s.validRevisions, revision{id, s.journal.length()}) + + s.ctx.Snapshot(id) + return id } @@ -446,33 +466,56 @@ func (s *StateDB) RevertToSnapshot(revid int) { // Replay the journal to undo changes and remove invalidated snapshots s.journal.Revert(s, snapshot) s.validRevisions = s.validRevisions[:idx] + + s.ctx.Revert(revid) +} + +// SetError sets the error in the StateDB which will be returned on Commit. This +// only sets the first error that occurs. Subsequent calls to SetError will be +// ignored as the initial error is the most important. Any errors that occur +// after the first error may be due to invalid state caused by the first error. +func (s *StateDB) SetError(err error) { + if s.sdkError != nil { + return + } + + s.sdkError = err } // Commit writes the dirty states to keeper // the StateDB object should be discarded after committed. func (s *StateDB) Commit() error { + if s.sdkError != nil { + return s.sdkError + } + for _, addr := range s.journal.sortedDirties() { obj := s.stateObjects[addr] if obj.suicided { - if err := s.keeper.DeleteAccount(s.ctx, obj.Address()); err != nil { + if err := s.keeper.DeleteAccount(s.ctx.CurrentCtx(), obj.Address()); err != nil { return errorsmod.Wrap(err, "failed to delete account") } } else { if obj.code != nil && obj.dirtyCode { - s.keeper.SetCode(s.ctx, obj.CodeHash(), obj.code) + s.keeper.SetCode(s.ctx.CurrentCtx(), obj.CodeHash(), obj.code) } - if err := s.keeper.SetAccount(s.ctx, obj.Address(), obj.account); err != nil { + + if err := s.keeper.SetAccount(s.ctx.CurrentCtx(), obj.Address(), obj.account); err != nil { return errorsmod.Wrap(err, "failed to set account") } + for _, key := range obj.dirtyStorage.SortedKeys() { value := obj.dirtyStorage[key] // Skip noop changes, persist actual changes if value == obj.originStorage[key] { continue } - s.keeper.SetState(s.ctx, obj.Address(), key, value.Bytes()) + s.keeper.SetState(s.ctx.CurrentCtx(), obj.Address(), key, value) } } } + + s.ctx.Commit() + return nil } diff --git a/x/evm/statedb/statedb_test.go b/x/evm/statedb/statedb_test.go index 3a491aa8cc..64d6f699e0 100644 --- a/x/evm/statedb/statedb_test.go +++ b/x/evm/statedb/statedb_test.go @@ -5,27 +5,173 @@ import ( "testing" sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/stretchr/testify/suite" + "github.com/ethereum/go-ethereum/common" ethtypes "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" + xevm "github.com/evmos/ethermint/x/evm" + "github.com/evmos/ethermint/x/evm/keeper" "github.com/evmos/ethermint/x/evm/statedb" - "github.com/stretchr/testify/suite" + "github.com/evmos/ethermint/x/evm/testutil" + "github.com/evmos/ethermint/x/evm/types" ) var ( - address common.Address = common.BigToAddress(big.NewInt(101)) - address2 common.Address = common.BigToAddress(big.NewInt(102)) - address3 common.Address = common.BigToAddress(big.NewInt(103)) + address common.Address = common.BigToAddress(big.NewInt(101)) + address2 common.Address = common.BigToAddress(big.NewInt(102)) + address3 common.Address = common.BigToAddress(big.NewInt(103)) + blockHash common.Hash = common.BigToHash(big.NewInt(9999)) emptyTxConfig statedb.TxConfig = statedb.NewEmptyTxConfig(blockHash) + emptyCodeHash = crypto.Keccak256(nil) ) -type StateDBTestSuite struct { - suite.Suite +type HybridStateDBTestSuite struct { + testutil.TestSuite +} + +func TestHybridStateDBTestSuite(t *testing.T) { + suite.Run(t, &HybridStateDBTestSuite{}) +} + +func (suite *HybridStateDBTestSuite) TestGetBalance_External() { + // GetBalance() should account for balance changes from sdk.Context and + // external factors - e.g. precompiles that modify user account balances. + keeper := suite.App.EvmKeeper + params := keeper.GetParams(suite.Ctx) + + tests := []struct { + name string + maleate func(db *statedb.StateDB) + expected *big.Int + }{ + { + "no external balance changes", + func(db *statedb.StateDB) { + amount := big.NewInt(100) + db.AddBalance(address, amount) + }, + big.NewInt(100), + }, + { + "bank balance add", + func(db *statedb.StateDB) { + amount := big.NewInt(100) + db.AddBalance(address, amount) + + // Add some external balance changes, this could be a call to a precompile + // that transfers funds. + externalTransferAmount := big.NewInt(50) + suite.MintCoinsForAccount( + db.Context(), + sdk.AccAddress(address.Bytes()), + sdk.NewCoins(sdk.NewCoin(params.EvmDenom, sdk.NewIntFromBigInt(externalTransferAmount))), + ) + }, + big.NewInt(150), + }, + { + "bank balance sub", + func(db *statedb.StateDB) { + amount := big.NewInt(100) + db.AddBalance(address, amount) + + externalTransferAmount := big.NewInt(50) + err := suite.App.BankKeeper.SendCoins( + db.Context(), + sdk.AccAddress(address.Bytes()), + sdk.AccAddress(address2.Bytes()), + sdk.NewCoins(sdk.NewCoin(params.EvmDenom, sdk.NewIntFromBigInt(externalTransferAmount))), + ) + suite.Require().NoError(err) + }, + big.NewInt(50), + }, + } + + for _, tt := range tests { + suite.Run(tt.name, func() { + suite.SetupTest() + keeper := suite.App.EvmKeeper + + db := statedb.New(suite.Ctx, keeper, emptyTxConfig) + tt.maleate(db) + + suite.Require().NoError(db.Commit()) + + // New db after Commit() + db = statedb.New(suite.Ctx, keeper, emptyTxConfig) + + suite.Require().Equal( + tt.expected, + db.GetBalance(address), + "GetBalance should account for both internal and external balance changes", + ) + }) + } +} + +func (suite *HybridStateDBTestSuite) TestExist() { + // When precompiles create accounts, it should be visible to StateDB. + db := statedb.New(suite.Ctx, suite.App.EvmKeeper, emptyTxConfig) + + suite.Require().False(db.Exist(address), "account shouldn't be created or found") + + // Create account externally + suite.MintCoinsForAccount( + db.Context(), // Use the statedb context! + sdk.AccAddress(address.Bytes()), + sdk.NewCoins(sdk.NewCoin(types.DefaultEVMDenom, sdk.NewInt(100))), + ) + + // Account should be visible to StateDB - ensures the correct ctx is used + // in the StateDB + suite.Require().True(db.Exist(address), "account should be created and found") +} + +func (suite *HybridStateDBTestSuite) TestForEachStorage_Committed() { + db := statedb.New(suite.Ctx, suite.App.EvmKeeper, emptyTxConfig) + // Set some storage + db.SetState(address, common.BytesToHash([]byte("key")), common.BytesToHash([]byte("value"))) + + // Commit changes + suite.Require().NoError(db.Commit()) + + // Create a new StateDB + db = statedb.New(suite.Ctx, suite.App.EvmKeeper, emptyTxConfig) + + // Iterate over storage + var keys []common.Hash + db.ForEachStorage(address, func(key, value common.Hash) bool { + keys = append(keys, key) + return false + }) + + suite.Require().Len(keys, 1, "expected 1 key") + suite.Require().Equal(common.BytesToHash([]byte("key")), keys[0], "expected key to be found") } -func (suite *StateDBTestSuite) TestAccount() { +func (suite *HybridStateDBTestSuite) TestForEachStorage_Dirty() { + suite.T().Skip("TODO: identify if ForEachStorage should return new keys") + + db := statedb.New(suite.Ctx, suite.App.EvmKeeper, emptyTxConfig) + // Set some storage + db.SetState(address, common.BytesToHash([]byte("key")), common.BytesToHash([]byte("value"))) + + // Iterate over storage + var keys []common.Hash + db.ForEachStorage(address, func(key, value common.Hash) bool { + keys = append(keys, key) + return false + }) + + suite.Require().Len(keys, 1, "expected 1 key") + suite.Require().Equal(common.BytesToHash([]byte("key")), keys[0], "expected key to be found") +} + +func (suite *HybridStateDBTestSuite) TestAccount() { key1 := common.BigToHash(big.NewInt(1)) value1 := common.BigToHash(big.NewInt(2)) key2 := common.BigToHash(big.NewInt(3)) @@ -46,13 +192,15 @@ func (suite *StateDBTestSuite) TestAccount() { db.CreateAccount(address) suite.Require().NoError(db.Commit()) - keeper := db.Keeper().(*MockKeeper) - acct := keeper.accounts[address] - suite.Require().Equal(statedb.NewEmptyAccount(), &acct.account) - suite.Require().Empty(acct.states) - suite.Require().False(acct.account.IsContract()) + keeper := db.Keeper().(*keeper.Keeper) + acct := keeper.GetAccount(suite.Ctx, address) + states := suite.GetAllAccountStorage(suite.Ctx, address) + + suite.Require().Equal(statedb.NewEmptyAccount(), acct) + suite.Require().Empty(states) + suite.Require().False(acct.IsContract()) - db = statedb.New(sdk.Context{}, keeper, emptyTxConfig) + db = statedb.New(suite.Ctx, keeper, emptyTxConfig) suite.Require().Equal(true, db.Exist(address)) suite.Require().Equal(true, db.Empty(address)) suite.Require().Equal(big.NewInt(0), db.GetBalance(address)) @@ -74,7 +222,7 @@ func (suite *StateDBTestSuite) TestAccount() { suite.Require().NoError(db.Commit()) // suicide - db = statedb.New(sdk.Context{}, db.Keeper(), emptyTxConfig) + db = statedb.New(suite.Ctx, db.Keeper(), emptyTxConfig) suite.Require().False(db.HasSuicided(address)) suite.Require().True(db.Suicide(address)) @@ -89,27 +237,32 @@ func (suite *StateDBTestSuite) TestAccount() { suite.Require().NoError(db.Commit()) // not accessible from StateDB anymore - db = statedb.New(sdk.Context{}, db.Keeper(), emptyTxConfig) + db = statedb.New(suite.Ctx, db.Keeper(), emptyTxConfig) suite.Require().False(db.Exist(address)) // and cleared in keeper too - keeper := db.Keeper().(*MockKeeper) - suite.Require().Empty(keeper.accounts) - suite.Require().Empty(keeper.codes) + keeper := db.Keeper().(*keeper.Keeper) + acc := keeper.GetAccount(suite.Ctx, address) + states := suite.GetAllAccountStorage(suite.Ctx, address) + + suite.Require().Empty(acc) + suite.Require().Empty(states) }}, } for _, tc := range testCases { suite.Run(tc.name, func() { - keeper := NewMockKeeper() - db := statedb.New(sdk.Context{}, keeper, emptyTxConfig) + suite.SetupTest() + + keeper := suite.App.EvmKeeper + db := statedb.New(suite.Ctx, keeper, emptyTxConfig) tc.malleate(db) }) } } -func (suite *StateDBTestSuite) TestAccountOverride() { - keeper := NewMockKeeper() - db := statedb.New(sdk.Context{}, keeper, emptyTxConfig) +func (suite *HybridStateDBTestSuite) TestAccountOverride() { + keeper := suite.App.EvmKeeper + db := statedb.New(suite.Ctx, keeper, emptyTxConfig) // test balance carry over when overwritten amount := big.NewInt(1) @@ -126,27 +279,43 @@ func (suite *StateDBTestSuite) TestAccountOverride() { suite.Require().Equal(uint64(0), db.GetNonce(address)) } -func (suite *StateDBTestSuite) TestDBError() { +func (suite *HybridStateDBTestSuite) TestDBError() { testCases := []struct { - name string - malleate func(vm.StateDB) + name string + malleate func(*statedb.StateDB) + errContains string }{ - {"set account", func(db vm.StateDB) { - db.SetNonce(errAddress, 1) - }}, - {"delete account", func(db vm.StateDB) { - db.SetNonce(errAddress, 1) - suite.Require().True(db.Suicide(errAddress)) - }}, + { + "negative balance", + func(db *statedb.StateDB) { + db.SubBalance(address, big.NewInt(1)) + }, + "insufficient funds", + }, + { + "multiple errors persist first error", + func(db *statedb.StateDB) { + db.SubBalance(address, big.NewInt(200)) + db.SubBalance(address2, big.NewInt(500)) + }, + "insufficient funds", + }, } for _, tc := range testCases { - db := statedb.New(sdk.Context{}, NewMockKeeper(), emptyTxConfig) - tc.malleate(db) - suite.Require().Error(db.Commit()) + suite.Run(tc.name, func() { + suite.SetupTest() + db := statedb.New(suite.Ctx, suite.App.EvmKeeper, emptyTxConfig) + tc.malleate(db) + + err := db.Commit() + + suite.Require().Error(err) + suite.Require().ErrorContains(err, tc.errContains) + }) } } -func (suite *StateDBTestSuite) TestBalance() { +func (suite *HybridStateDBTestSuite) TestBalance() { // NOTE: no need to test overflow/underflow, that is guaranteed by evm implementation. testCases := []struct { name string @@ -172,36 +341,53 @@ func (suite *StateDBTestSuite) TestBalance() { for _, tc := range testCases { suite.Run(tc.name, func() { - keeper := NewMockKeeper() - db := statedb.New(sdk.Context{}, keeper, emptyTxConfig) + suite.SetupTest() + + keeper := suite.App.EvmKeeper + db := statedb.New(suite.Ctx, keeper, emptyTxConfig) tc.malleate(db) // check dirty state suite.Require().Equal(tc.expBalance, db.GetBalance(address)) suite.Require().NoError(db.Commit()) // check committed balance too - suite.Require().Equal(tc.expBalance, keeper.accounts[address].account.Balance) + suite.Require().Equal(tc.expBalance, keeper.GetBalance(suite.Ctx, address)) }) } } -func (suite *StateDBTestSuite) TestState() { +func (suite *HybridStateDBTestSuite) TestState() { key1 := common.BigToHash(big.NewInt(1)) value1 := common.BigToHash(big.NewInt(1)) testCases := []struct { name string malleate func(*statedb.StateDB) - expStates statedb.Storage + expStates map[common.Hash]common.Hash }{ {"empty state", func(db *statedb.StateDB) { }, nil}, - {"set empty value", func(db *statedb.StateDB) { + + {"set empty value deletes", func(db *statedb.StateDB) { db.SetState(address, key1, common.Hash{}) - }, statedb.Storage{}}, - {"noop state change", func(db *statedb.StateDB) { + }, map[common.Hash]common.Hash{}}, + + {"noop state change - empty", func(db *statedb.StateDB) { db.SetState(address, key1, value1) db.SetState(address, key1, common.Hash{}) - }, statedb.Storage{}}, + }, map[common.Hash]common.Hash{}}, + + {"noop state change - non-empty", func(db *statedb.StateDB) { + // Start with non-empty committed state + db.SetState(address, key1, value1) + suite.Require().NoError(db.Commit()) + + db.SetState(address, key1, common.Hash{}) + db.SetState(address, key1, value1) + }, map[common.Hash]common.Hash{ + // Shouldn't be modified - Commit() may still write it again though + key1: value1, + }}, + {"set state", func(db *statedb.StateDB) { // check empty initial state suite.Require().Equal(common.Hash{}, db.GetState(address, key1)) @@ -217,23 +403,30 @@ func (suite *StateDBTestSuite) TestState() { // set same value again, should be noop db.SetState(address, key1, value1) suite.Require().Equal(value1, db.GetState(address, key1)) - }, statedb.Storage{ + }, map[common.Hash]common.Hash{ key1: value1, }}, } for _, tc := range testCases { suite.Run(tc.name, func() { - keeper := NewMockKeeper() - db := statedb.New(sdk.Context{}, keeper, emptyTxConfig) + suite.SetupTest() + + keeper := suite.App.EvmKeeper + db := statedb.New(suite.Ctx, keeper, emptyTxConfig) tc.malleate(db) suite.Require().NoError(db.Commit()) // check committed states in keeper - suite.Require().Equal(tc.expStates, keeper.accounts[address].states) + states := suite.GetAllAccountStorage(suite.Ctx, address) + if len(tc.expStates) > 0 { + suite.Require().Equal(tc.expStates, states) + } else { + suite.Require().Empty(states) + } // check ForEachStorage - db = statedb.New(sdk.Context{}, keeper, emptyTxConfig) + db = statedb.New(suite.Ctx, keeper, emptyTxConfig) collected := CollectContractStorage(db) if len(tc.expStates) > 0 { suite.Require().Equal(tc.expStates, collected) @@ -244,29 +437,29 @@ func (suite *StateDBTestSuite) TestState() { } } -func (suite *StateDBTestSuite) TestCode() { +func (suite *HybridStateDBTestSuite) TestCode() { code := []byte("hello world") codeHash := crypto.Keccak256Hash(code) testCases := []struct { name string - malleate func(vm.StateDB) + malleate func(*statedb.StateDB) expCode []byte expCodeHash common.Hash }{ - {"non-exist account", func(vm.StateDB) {}, nil, common.Hash{}}, - {"empty account", func(db vm.StateDB) { + {"non-exist account", func(*statedb.StateDB) {}, nil, common.Hash{}}, + {"empty account", func(db *statedb.StateDB) { db.CreateAccount(address) }, nil, common.BytesToHash(emptyCodeHash)}, - {"set code", func(db vm.StateDB) { + {"set code", func(db *statedb.StateDB) { db.SetCode(address, code) }, code, codeHash}, } for _, tc := range testCases { suite.Run(tc.name, func() { - keeper := NewMockKeeper() - db := statedb.New(sdk.Context{}, keeper, emptyTxConfig) + keeper := suite.App.EvmKeeper + db := statedb.New(suite.Ctx, keeper, emptyTxConfig) tc.malleate(db) // check dirty state @@ -277,7 +470,7 @@ func (suite *StateDBTestSuite) TestCode() { suite.Require().NoError(db.Commit()) // check again - db = statedb.New(sdk.Context{}, keeper, emptyTxConfig) + db = statedb.New(suite.Ctx, keeper, emptyTxConfig) suite.Require().Equal(tc.expCode, db.GetCode(address)) suite.Require().Equal(len(tc.expCode), db.GetCodeSize(address)) suite.Require().Equal(tc.expCodeHash, db.GetCodeHash(address)) @@ -285,53 +478,56 @@ func (suite *StateDBTestSuite) TestCode() { } } -func (suite *StateDBTestSuite) TestRevertSnapshot() { +func (suite *HybridStateDBTestSuite) TestRevertSnapshot() { v1 := common.BigToHash(big.NewInt(1)) v2 := common.BigToHash(big.NewInt(2)) v3 := common.BigToHash(big.NewInt(3)) testCases := []struct { name string - malleate func(vm.StateDB) + malleate func(*statedb.StateDB) }{ - {"set state", func(db vm.StateDB) { + {"set state", func(db *statedb.StateDB) { db.SetState(address, v1, v3) }}, - {"set nonce", func(db vm.StateDB) { + {"set nonce", func(db *statedb.StateDB) { db.SetNonce(address, 10) }}, - {"change balance", func(db vm.StateDB) { + {"change balance", func(db *statedb.StateDB) { db.AddBalance(address, big.NewInt(10)) db.SubBalance(address, big.NewInt(5)) }}, - {"override account", func(db vm.StateDB) { + {"override account", func(db *statedb.StateDB) { db.CreateAccount(address) }}, - {"set code", func(db vm.StateDB) { + {"set code", func(db *statedb.StateDB) { db.SetCode(address, []byte("hello world")) }}, - {"suicide", func(db vm.StateDB) { + {"suicide", func(db *statedb.StateDB) { db.SetState(address, v1, v2) db.SetCode(address, []byte("hello world")) suite.Require().True(db.Suicide(address)) }}, - {"add log", func(db vm.StateDB) { + {"add log", func(db *statedb.StateDB) { db.AddLog(ðtypes.Log{ Address: address, }) }}, - {"add refund", func(db vm.StateDB) { + {"add refund", func(db *statedb.StateDB) { db.AddRefund(10) db.SubRefund(5) }}, - {"access list", func(db vm.StateDB) { + {"access list", func(db *statedb.StateDB) { db.AddAddressToAccessList(address) db.AddSlotToAccessList(address, v1) }}, } for _, tc := range testCases { suite.Run(tc.name, func() { - ctx := sdk.Context{} - keeper := NewMockKeeper() + suite.SetupTest() + suite.App.AccountKeeper.GetModuleAccount(suite.Ctx, types.ModuleName) + + ctx := suite.Ctx + keeper := suite.App.EvmKeeper { // do some arbitrary changes to the storage @@ -344,7 +540,7 @@ func (suite *StateDBTestSuite) TestRevertSnapshot() { suite.Require().NoError(db.Commit()) } - originalKeeper := keeper.Clone() + originalState := xevm.ExportGenesis(suite.Ctx, keeper, suite.App.AccountKeeper) // run test db := statedb.New(ctx, keeper, emptyTxConfig) @@ -358,18 +554,20 @@ func (suite *StateDBTestSuite) TestRevertSnapshot() { suite.Require().NoError(db.Commit()) - // check keeper should stay the same - suite.Require().Equal(originalKeeper, keeper) + revertState := xevm.ExportGenesis(suite.Ctx, keeper, suite.App.AccountKeeper) + + // check keeper state should stay the same + suite.Require().Equal(originalState, revertState) }) } } -func (suite *StateDBTestSuite) TestNestedSnapshot() { +func (suite *HybridStateDBTestSuite) TestNestedSnapshot() { key := common.BigToHash(big.NewInt(1)) value1 := common.BigToHash(big.NewInt(1)) value2 := common.BigToHash(big.NewInt(2)) - db := statedb.New(sdk.Context{}, NewMockKeeper(), emptyTxConfig) + db := statedb.New(suite.Ctx, suite.App.EvmKeeper, emptyTxConfig) rev1 := db.Snapshot() db.SetState(address, key, value1) @@ -385,22 +583,48 @@ func (suite *StateDBTestSuite) TestNestedSnapshot() { suite.Require().Equal(common.Hash{}, db.GetState(address, key)) } -func (suite *StateDBTestSuite) TestInvalidSnapshotId() { - db := statedb.New(sdk.Context{}, NewMockKeeper(), emptyTxConfig) +func (suite *HybridStateDBTestSuite) TestBalanceSnapshots() { + db := statedb.New(suite.Ctx, suite.App.EvmKeeper, emptyTxConfig) + // take a snapshot + snapshot := db.Snapshot() + + // add balance + db.AddBalance(address, big.NewInt(10)) + suite.Require().Equal(big.NewInt(10), db.GetBalance(address)) + + // revert to snapshot + db.RevertToSnapshot(snapshot) + // balance should be reverted + suite.Require().Equal(big.NewInt(0), db.GetBalance(address)) + + // add balance again + db.AddBalance(address, big.NewInt(10)) + suite.Require().Equal(big.NewInt(10), db.GetBalance(address)) + + // commit + suite.Require().NoError(db.Commit()) + + db = statedb.New(suite.Ctx, suite.App.EvmKeeper, emptyTxConfig) + // balance should be committed + suite.Require().Equal(big.NewInt(10), db.GetBalance(address)) +} + +func (suite *HybridStateDBTestSuite) TestInvalidSnapshotId() { + db := statedb.New(suite.Ctx, suite.App.EvmKeeper, emptyTxConfig) suite.Require().Panics(func() { db.RevertToSnapshot(1) }) } -func (suite *StateDBTestSuite) TestAccessList() { +func (suite *HybridStateDBTestSuite) TestAccessList() { value1 := common.BigToHash(big.NewInt(1)) value2 := common.BigToHash(big.NewInt(2)) testCases := []struct { name string - malleate func(vm.StateDB) + malleate func(*statedb.StateDB) }{ - {"add address", func(db vm.StateDB) { + {"add address", func(db *statedb.StateDB) { suite.Require().False(db.AddressInAccessList(address)) db.AddAddressToAccessList(address) suite.Require().True(db.AddressInAccessList(address)) @@ -413,7 +637,7 @@ func (suite *StateDBTestSuite) TestAccessList() { db.AddAddressToAccessList(address) suite.Require().True(db.AddressInAccessList(address)) }}, - {"add slot", func(db vm.StateDB) { + {"add slot", func(db *statedb.StateDB) { addrPresent, slotPresent := db.SlotInAccessList(address, value1) suite.Require().False(addrPresent) suite.Require().False(slotPresent) @@ -434,7 +658,9 @@ func (suite *StateDBTestSuite) TestAccessList() { suite.Require().True(addrPresent) suite.Require().True(slotPresent) }}, - {"prepare access list", func(db vm.StateDB) { + {"prepare access list", func(db *statedb.StateDB) { + suite.SetupTest() + al := ethtypes.AccessList{{ Address: address3, StorageKeys: []common.Hash{value1}, @@ -458,12 +684,12 @@ func (suite *StateDBTestSuite) TestAccessList() { } for _, tc := range testCases { - db := statedb.New(sdk.Context{}, NewMockKeeper(), emptyTxConfig) + db := statedb.New(suite.Ctx, suite.App.EvmKeeper, emptyTxConfig) tc.malleate(db) } } -func (suite *StateDBTestSuite) TestLog() { +func (suite *HybridStateDBTestSuite) TestLog() { txHash := common.BytesToHash([]byte("tx")) // use a non-default tx config txConfig := statedb.NewTxConfig( @@ -471,7 +697,7 @@ func (suite *StateDBTestSuite) TestLog() { txHash, 1, 1, ) - db := statedb.New(sdk.Context{}, NewMockKeeper(), txConfig) + db := statedb.New(suite.Ctx, suite.App.EvmKeeper, txConfig) data := []byte("hello world") db.AddLog(ðtypes.Log{ Address: address, @@ -480,7 +706,7 @@ func (suite *StateDBTestSuite) TestLog() { BlockNumber: 1, }) suite.Require().Equal(1, len(db.Logs())) - expecedLog := ðtypes.Log{ + expectedLog := ðtypes.Log{ Address: address, Topics: []common.Hash{}, Data: data, @@ -490,7 +716,7 @@ func (suite *StateDBTestSuite) TestLog() { TxIndex: 1, Index: 1, } - suite.Require().Equal(expecedLog, db.Logs()[0]) + suite.Require().Equal(expectedLog, db.Logs()[0]) db.AddLog(ðtypes.Log{ Address: address, @@ -499,31 +725,33 @@ func (suite *StateDBTestSuite) TestLog() { BlockNumber: 1, }) suite.Require().Equal(2, len(db.Logs())) - expecedLog.Index++ - suite.Require().Equal(expecedLog, db.Logs()[1]) + expectedLog.Index++ + suite.Require().Equal(expectedLog, db.Logs()[1]) } -func (suite *StateDBTestSuite) TestRefund() { +func (suite *HybridStateDBTestSuite) TestRefund() { testCases := []struct { name string - malleate func(vm.StateDB) + malleate func(*statedb.StateDB) expRefund uint64 expPanic bool }{ - {"add refund", func(db vm.StateDB) { + {"add refund", func(db *statedb.StateDB) { db.AddRefund(uint64(10)) }, 10, false}, - {"sub refund", func(db vm.StateDB) { + {"sub refund", func(db *statedb.StateDB) { db.AddRefund(uint64(10)) db.SubRefund(uint64(5)) }, 5, false}, - {"negative refund counter", func(db vm.StateDB) { + {"negative refund counter", func(db *statedb.StateDB) { db.AddRefund(uint64(5)) db.SubRefund(uint64(10)) }, 0, true}, } for _, tc := range testCases { - db := statedb.New(sdk.Context{}, NewMockKeeper(), emptyTxConfig) + suite.SetupTest() + + db := statedb.New(suite.Ctx, suite.App.EvmKeeper, emptyTxConfig) if !tc.expPanic { tc.malleate(db) suite.Require().Equal(tc.expRefund, db.GetRefund()) @@ -535,14 +763,14 @@ func (suite *StateDBTestSuite) TestRefund() { } } -func (suite *StateDBTestSuite) TestIterateStorage() { +func (suite *HybridStateDBTestSuite) TestIterateStorage() { key1 := common.BigToHash(big.NewInt(1)) value1 := common.BigToHash(big.NewInt(2)) key2 := common.BigToHash(big.NewInt(3)) value2 := common.BigToHash(big.NewInt(4)) - keeper := NewMockKeeper() - db := statedb.New(sdk.Context{}, keeper, emptyTxConfig) + keeper := suite.App.EvmKeeper + db := statedb.New(suite.Ctx, keeper, emptyTxConfig) db.SetState(address, key1, value1) db.SetState(address, key2, value2) @@ -553,7 +781,9 @@ func (suite *StateDBTestSuite) TestIterateStorage() { storage := CollectContractStorage(db) suite.Require().Equal(2, len(storage)) - suite.Require().Equal(keeper.accounts[address].states, storage) + + accStorages := suite.GetAllAccountStorage(suite.Ctx, address) + suite.Require().Equal(accStorages, storage) // break early iteration storage = make(statedb.Storage) @@ -565,15 +795,11 @@ func (suite *StateDBTestSuite) TestIterateStorage() { suite.Require().Equal(1, len(storage)) } -func CollectContractStorage(db vm.StateDB) statedb.Storage { - storage := make(statedb.Storage) +func CollectContractStorage(db *statedb.StateDB) map[common.Hash]common.Hash { + storage := make(map[common.Hash]common.Hash) db.ForEachStorage(address, func(k, v common.Hash) bool { storage[k] = v return true }) return storage } - -func TestStateDBTestSuite(t *testing.T) { - suite.Run(t, &StateDBTestSuite{}) -} diff --git a/x/evm/testutil/suite.go b/x/evm/testutil/suite.go new file mode 100644 index 0000000000..fdd182851b --- /dev/null +++ b/x/evm/testutil/suite.go @@ -0,0 +1,469 @@ +package testutil + +import ( + "encoding/json" + "math" + "math/big" + "time" + + sdkmath "cosmossdk.io/math" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + + "cosmossdk.io/simapp" + tmjson "github.com/cometbft/cometbft/libs/json" + "github.com/cosmos/cosmos-sdk/baseapp" + "github.com/cosmos/cosmos-sdk/client" + "github.com/cosmos/cosmos-sdk/codec" + "github.com/cosmos/cosmos-sdk/crypto/keyring" + sdk "github.com/cosmos/cosmos-sdk/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + minttypes "github.com/cosmos/cosmos-sdk/x/mint/types" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + feemarkettypes "github.com/evmos/ethermint/x/feemarket/types" + + "github.com/evmos/ethermint/app" + "github.com/evmos/ethermint/crypto/ethsecp256k1" + "github.com/evmos/ethermint/encoding" + "github.com/evmos/ethermint/server/config" + "github.com/evmos/ethermint/tests" + ethermint "github.com/evmos/ethermint/types" + "github.com/evmos/ethermint/x/evm/statedb" + "github.com/evmos/ethermint/x/evm/types" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + ethtypes "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" + + abci "github.com/cometbft/cometbft/abci/types" + "github.com/cometbft/cometbft/crypto/tmhash" + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + tmversion "github.com/cometbft/cometbft/proto/tendermint/version" + "github.com/cometbft/cometbft/version" +) + +type TestSuite struct { + suite.Suite + + Ctx sdk.Context + App *app.EthermintApp + QueryClient types.QueryClient + Address common.Address + ConsAddress sdk.ConsAddress + + // for generate test tx + ClientCtx client.Context + EthSigner ethtypes.Signer + + AppCodec codec.Codec + Signer keyring.Signer + + EnableFeemarket bool + EnableLondonHF bool + MintFeeCollector bool + Denom string +} + +func (suite *TestSuite) SetupTest() { + checkTx := false + suite.App = app.Setup(checkTx, nil) + suite.SetupApp(checkTx) +} + +func (suite *TestSuite) SetupTestWithT(t require.TestingT) { + checkTx := false + suite.App = app.Setup(checkTx, nil) + suite.SetupAppWithT(checkTx, t) +} + +func (suite *TestSuite) SetupApp(checkTx bool) { + suite.SetupAppWithT(checkTx, suite.T()) +} + +// SetupApp setup test environment, it uses`require.TestingT` to support both `testing.T` and `testing.B`. +func (suite *TestSuite) SetupAppWithT(checkTx bool, t require.TestingT) { + // account key, use a constant account to keep unit test deterministic. + ecdsaPriv, err := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + require.NoError(t, err) + priv := ðsecp256k1.PrivKey{ + Key: crypto.FromECDSA(ecdsaPriv), + } + suite.Address = common.BytesToAddress(priv.PubKey().Address().Bytes()) + suite.Signer = tests.NewSigner(priv) + + suite.Require().Equal("0x71562b71999873DB5b286dF957af199Ec94617F7", suite.Address.Hex()) + + // consensus key + priv = ðsecp256k1.PrivKey{ + Key: common.Hex2Bytes("a249d5fbd4516fde5765dbd763b93c3542bf2748b6cd512eb96e0862b3583261"), + } + require.NoError(t, err) + suite.ConsAddress = sdk.ConsAddress(priv.PubKey().Address()) + + suite.Require().Equal("cosmosvalcons1505eel9r7tnacxyzsqysysudwgucvhl53t7p70", suite.ConsAddress.String()) + + suite.App = app.Setup(checkTx, func(app *app.EthermintApp, genesis simapp.GenesisState) simapp.GenesisState { + feemarketGenesis := feemarkettypes.DefaultGenesisState() + if suite.EnableFeemarket { + feemarketGenesis.Params.EnableHeight = 1 + feemarketGenesis.Params.NoBaseFee = false + } else { + feemarketGenesis.Params.NoBaseFee = true + } + genesis[feemarkettypes.ModuleName] = app.AppCodec().MustMarshalJSON(feemarketGenesis) + if !suite.EnableLondonHF { + evmGenesis := types.DefaultGenesisState() + maxInt := sdkmath.NewInt(math.MaxInt64) + evmGenesis.Params.ChainConfig.LondonBlock = &maxInt + evmGenesis.Params.ChainConfig.ArrowGlacierBlock = &maxInt + evmGenesis.Params.ChainConfig.GrayGlacierBlock = &maxInt + evmGenesis.Params.ChainConfig.MergeNetsplitBlock = &maxInt + evmGenesis.Params.ChainConfig.ShanghaiBlock = &maxInt + evmGenesis.Params.ChainConfig.CancunBlock = &maxInt + genesis[types.ModuleName] = app.AppCodec().MustMarshalJSON(evmGenesis) + } + return genesis + }) + + if suite.MintFeeCollector { + // mint some coin to fee collector + coins := sdk.NewCoins(sdk.NewCoin(types.DefaultEVMDenom, sdkmath.NewInt(int64(params.TxGas)-1))) + genesisState := app.NewTestGenesisState(suite.App.AppCodec()) + balances := []banktypes.Balance{ + { + Address: suite.App.AccountKeeper.GetModuleAddress(authtypes.FeeCollectorName).String(), + Coins: coins, + }, + } + var bankGenesis banktypes.GenesisState + suite.App.AppCodec().MustUnmarshalJSON(genesisState[banktypes.ModuleName], &bankGenesis) + // Update balances and total supply + bankGenesis.Balances = append(bankGenesis.Balances, balances...) + bankGenesis.Supply = bankGenesis.Supply.Add(coins...) + genesisState[banktypes.ModuleName] = suite.App.AppCodec().MustMarshalJSON(&bankGenesis) + + // we marshal the genesisState of all module to a byte array + stateBytes, err := tmjson.MarshalIndent(genesisState, "", " ") + require.NoError(t, err) + + // Initialize the chain + suite.App.InitChain( + abci.RequestInitChain{ + ChainId: "ethermint_9000-1", + Validators: []abci.ValidatorUpdate{}, + ConsensusParams: app.DefaultConsensusParams, + AppStateBytes: stateBytes, + }, + ) + } + + suite.Ctx = suite.App.BaseApp.NewContext(checkTx, tmproto.Header{ + Height: 1, + ChainID: "ethermint_9000-1", + // Fixed date to have deterministic tests + Time: time.Date(2020, 1, 2, 0, 0, 0, 0, time.UTC), + ProposerAddress: suite.ConsAddress.Bytes(), + Version: tmversion.Consensus{ + Block: version.BlockProtocol, + }, + LastBlockId: tmproto.BlockID{ + Hash: tmhash.Sum([]byte("block_id")), + PartSetHeader: tmproto.PartSetHeader{ + Total: 11, + Hash: tmhash.Sum([]byte("partset_header")), + }, + }, + AppHash: tmhash.Sum([]byte("app")), + DataHash: tmhash.Sum([]byte("data")), + EvidenceHash: tmhash.Sum([]byte("evidence")), + ValidatorsHash: tmhash.Sum([]byte("validators")), + NextValidatorsHash: tmhash.Sum([]byte("next_validators")), + ConsensusHash: tmhash.Sum([]byte("consensus")), + LastResultsHash: tmhash.Sum([]byte("last_result")), + }) + + queryHelper := baseapp.NewQueryServerTestHelper(suite.Ctx, suite.App.InterfaceRegistry()) + types.RegisterQueryServer(queryHelper, suite.App.EvmKeeper) + suite.QueryClient = types.NewQueryClient(queryHelper) + + acc := ðermint.EthAccount{ + BaseAccount: authtypes.NewBaseAccount(sdk.AccAddress(suite.Address.Bytes()), nil, 0, 0), + CodeHash: common.BytesToHash(crypto.Keccak256(nil)).String(), + } + + suite.App.AccountKeeper.SetAccount(suite.Ctx, acc) + + valAddr := sdk.ValAddress(suite.Address.Bytes()) + + suite.Equal("cosmosvaloper1w9tzkuvenpeakkegdhu40tcenmy5v9lhc9rt8k", valAddr.String()) + + validator, err := stakingtypes.NewValidator(valAddr, priv.PubKey(), stakingtypes.Description{}) + require.NoError(t, err) + err = suite.App.StakingKeeper.SetValidatorByConsAddr(suite.Ctx, validator) + require.NoError(t, err) + err = suite.App.StakingKeeper.SetValidatorByConsAddr(suite.Ctx, validator) + require.NoError(t, err) + suite.App.StakingKeeper.SetValidator(suite.Ctx, validator) + + encodingConfig := encoding.MakeConfig(app.ModuleBasics) + suite.ClientCtx = client.Context{}.WithTxConfig(encodingConfig.TxConfig) + suite.EthSigner = ethtypes.LatestSignerForChainID(suite.App.EvmKeeper.ChainID()) + suite.AppCodec = encodingConfig.Codec + suite.Denom = types.DefaultEVMDenom +} + +func (suite *TestSuite) EvmDenom() string { + ctx := sdk.WrapSDKContext(suite.Ctx) + rsp, _ := suite.QueryClient.Params(ctx, &types.QueryParamsRequest{}) + return rsp.Params.EvmDenom +} + +// Commit and begin new block +func (suite *TestSuite) Commit() abci.ResponseCommit { + res := suite.App.Commit() + header := suite.Ctx.BlockHeader() + header.Height++ + suite.App.BeginBlock(abci.RequestBeginBlock{ + Header: header, + }) + + // update ctx + suite.Ctx = suite.App.BaseApp.NewContext(false, header) + + queryHelper := baseapp.NewQueryServerTestHelper(suite.Ctx, suite.App.InterfaceRegistry()) + types.RegisterQueryServer(queryHelper, suite.App.EvmKeeper) + suite.QueryClient = types.NewQueryClient(queryHelper) + + return res +} + +func (suite *TestSuite) StateDB() *statedb.StateDB { + return statedb.New( + suite.Ctx, + suite.App.EvmKeeper, + statedb.NewEmptyTxConfig(common.BytesToHash(suite.Ctx.HeaderHash().Bytes())), + ) +} + +// DeployTestContract deploy a test erc20 contract and returns the contract address +func (suite *TestSuite) DeployTestContract(t require.TestingT, owner common.Address, supply *big.Int) common.Address { + ctx := sdk.WrapSDKContext(suite.Ctx) + chainID := suite.App.EvmKeeper.ChainID() + + ctorArgs, err := types.ERC20Contract.ABI.Pack("", owner, supply) + require.NoError(t, err) + + nonce := suite.App.EvmKeeper.GetNonce(suite.Ctx, suite.Address) + + data := types.ERC20Contract.Bin + data = append(data, ctorArgs...) + args, err := json.Marshal(&types.TransactionArgs{ + From: &suite.Address, + Data: (*hexutil.Bytes)(&data), + }) + require.NoError(t, err) + res, err := suite.QueryClient.EstimateGas(ctx, &types.EthCallRequest{ + Args: args, + GasCap: config.DefaultGasCap, + ProposerAddress: suite.Ctx.BlockHeader().ProposerAddress, + }) + require.NoError(t, err) + + var erc20DeployTx *types.MsgEthereumTx + if suite.EnableFeemarket { + erc20DeployTx = types.NewTxContract( + chainID, + nonce, + nil, // amount + res.Gas, // gasLimit + nil, // gasPrice + suite.App.FeeMarketKeeper.GetBaseFee(suite.Ctx), + big.NewInt(1), + data, // input + ðtypes.AccessList{}, // accesses + ) + } else { + erc20DeployTx = types.NewTxContract( + chainID, + nonce, + nil, // amount + res.Gas, // gasLimit + nil, // gasPrice + nil, nil, + data, // input + nil, // accesses + ) + } + + erc20DeployTx.From = suite.Address.Hex() + err = erc20DeployTx.Sign(ethtypes.LatestSignerForChainID(chainID), suite.Signer) + require.NoError(t, err) + rsp, err := suite.App.EvmKeeper.EthereumTx(ctx, erc20DeployTx) + require.NoError(t, err) + require.Empty(t, rsp.VmError) + return crypto.CreateAddress(suite.Address, nonce) +} + +func (suite *TestSuite) MustTransferERC20Token(t require.TestingT, contractAddr, from, to common.Address, amount *big.Int) *types.MsgEthereumTx { + ercTransferTx, rsp, err := suite.TransferERC20Token(contractAddr, from, to, amount) + require.NoError(t, err) + require.Empty(t, rsp.VmError) + return ercTransferTx +} + +func (suite *TestSuite) TransferERC20Token( + contractAddr, from, to common.Address, + amount *big.Int, +) (*types.MsgEthereumTx, *types.MsgEthereumTxResponse, error) { + ctx := sdk.WrapSDKContext(suite.Ctx) + chainID := suite.App.EvmKeeper.ChainID() + + transferData, err := types.ERC20Contract.ABI.Pack("transfer", to, amount) + if err != nil { + return nil, nil, err + } + args, err := json.Marshal(&types.TransactionArgs{To: &contractAddr, From: &from, Data: (*hexutil.Bytes)(&transferData)}) + if err != nil { + return nil, nil, err + } + res, err := suite.QueryClient.EstimateGas(ctx, &types.EthCallRequest{ + Args: args, + GasCap: 25_000_000, + ProposerAddress: suite.Ctx.BlockHeader().ProposerAddress, + }) + if err != nil { + return nil, nil, err + } + + nonce := suite.App.EvmKeeper.GetNonce(suite.Ctx, suite.Address) + + var ercTransferTx *types.MsgEthereumTx + if suite.EnableFeemarket { + ercTransferTx = types.NewTx( + chainID, + nonce, + &contractAddr, + nil, + res.Gas, + nil, + suite.App.FeeMarketKeeper.GetBaseFee(suite.Ctx), + big.NewInt(1), + transferData, + ðtypes.AccessList{}, // accesses + ) + } else { + ercTransferTx = types.NewTx( + chainID, + nonce, + &contractAddr, + nil, + res.Gas, + nil, + nil, nil, + transferData, + nil, + ) + } + + ercTransferTx.From = suite.Address.Hex() + err = ercTransferTx.Sign(ethtypes.LatestSignerForChainID(chainID), suite.Signer) + if err != nil { + return nil, nil, err + } + rsp, err := suite.App.EvmKeeper.EthereumTx(ctx, ercTransferTx) + if err != nil { + return nil, rsp, err + } + + return ercTransferTx, rsp, nil +} + +// DeployTestMessageCall deploy a test erc20 contract and returns the contract address +func (suite *TestSuite) DeployTestMessageCall(t require.TestingT) common.Address { + ctx := sdk.WrapSDKContext(suite.Ctx) + chainID := suite.App.EvmKeeper.ChainID() + + data := types.TestMessageCall.Bin + args, err := json.Marshal(&types.TransactionArgs{ + From: &suite.Address, + Data: (*hexutil.Bytes)(&data), + }) + require.NoError(t, err) + + res, err := suite.QueryClient.EstimateGas(ctx, &types.EthCallRequest{ + Args: args, + GasCap: config.DefaultGasCap, + ProposerAddress: suite.Ctx.BlockHeader().ProposerAddress, + }) + require.NoError(t, err) + + nonce := suite.App.EvmKeeper.GetNonce(suite.Ctx, suite.Address) + + var erc20DeployTx *types.MsgEthereumTx + if suite.EnableFeemarket { + erc20DeployTx = types.NewTxContract( + chainID, + nonce, + nil, // amount + res.Gas, // gasLimit + nil, // gasPrice + suite.App.FeeMarketKeeper.GetBaseFee(suite.Ctx), + big.NewInt(1), + data, // input + ðtypes.AccessList{}, // accesses + ) + } else { + erc20DeployTx = types.NewTxContract( + chainID, + nonce, + nil, // amount + res.Gas, // gasLimit + nil, // gasPrice + nil, nil, + data, // input + nil, // accesses + ) + } + + erc20DeployTx.From = suite.Address.Hex() + err = erc20DeployTx.Sign(ethtypes.LatestSignerForChainID(chainID), suite.Signer) + require.NoError(t, err) + rsp, err := suite.App.EvmKeeper.EthereumTx(ctx, erc20DeployTx) + require.NoError(t, err) + require.Empty(t, rsp.VmError) + return crypto.CreateAddress(suite.Address, nonce) +} + +func (suite *TestSuite) GetAllAccountStorage( + ctx sdk.Context, + addr common.Address, +) map[common.Hash]common.Hash { + states := make(map[common.Hash]common.Hash) + suite.App.EvmKeeper.ForEachStorage(ctx, addr, func(key, value common.Hash) bool { + states[key] = value + // Iterate all + return true + }) + + return states +} + +func (suite *TestSuite) MintCoinsForAccount( + ctx sdk.Context, + addr sdk.AccAddress, + amount sdk.Coins, +) { + err := suite.App.BankKeeper.MintCoins(ctx, minttypes.ModuleName, amount) + suite.Require().NoError(err) + + err = suite.App.BankKeeper.SendCoinsFromModuleToAccount( + ctx, + minttypes.ModuleName, + addr, + amount, + ) + suite.Require().NoError(err) +} diff --git a/x/evm/vm/interface.go b/x/evm/vm/interface.go new file mode 100644 index 0000000000..f1a6f4f707 --- /dev/null +++ b/x/evm/vm/interface.go @@ -0,0 +1,81 @@ +// Copyright 2021 Evmos Foundation +// This file is part of Evmos' Ethermint library. +// +// The Ethermint library is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// The Ethermint library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License +// along with the Ethermint library. If not, see https://github.com/evmos/ethermint/blob/main/LICENSE +package vm + +import ( + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/params" + "github.com/holiman/uint256" +) + +// PrecompiledContracts defines a map of address -> precompiled contract +type PrecompiledContracts map[common.Address]vm.PrecompiledContract + +type StatefulPrecompiledContract interface { + vm.PrecompiledContract + RunStateful(evm EVM, addr common.Address, input []byte, value *big.Int) (ret []byte, err error) +} + +// EVM defines the interface for the Ethereum Virtual Machine used by the EVM module. +type EVM interface { + Config() vm.Config + Context() vm.BlockContext + TxContext() vm.TxContext + + Reset(txCtx vm.TxContext, statedb vm.StateDB) + Cancel() + Cancelled() bool //nolint + Interpreter() *vm.EVMInterpreter + Call(caller vm.ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) + CallCode(caller vm.ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) + DelegateCall(caller vm.ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) + StaticCall(caller vm.ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) + Create(caller vm.ContractRef, code []byte, gas uint64, value *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) + Create2( + caller vm.ContractRef, + code []byte, + gas uint64, + endowment *big.Int, + salt *uint256.Int) ( + ret []byte, contractAddr common.Address, leftOverGas uint64, err error, + ) + ChainConfig() *params.ChainConfig + + ActivePrecompiles(rules params.Rules) []common.Address + Precompile(addr common.Address) (vm.PrecompiledContract, bool) + RunPrecompiledContract( + p StatefulPrecompiledContract, + addr common.Address, + input []byte, + suppliedGas uint64, + value *big.Int) ( + ret []byte, remainingGas uint64, err error, + ) +} + +// Constructor defines the function used to instantiate the EVM on +// each state transition. +type Constructor func( + blockCtx vm.BlockContext, + txCtx vm.TxContext, + stateDB vm.StateDB, + chainConfig *params.ChainConfig, + config vm.Config, + customPrecompiles PrecompiledContracts, +) EVM