From 93d15db4d086d65eb0a7bee744b453e5ae416ac0 Mon Sep 17 00:00:00 2001 From: yihuang Date: Wed, 4 May 2022 01:51:02 +0800 Subject: [PATCH] ante: check `EnableCreate`/`EnableCall` in ante handler (#1060) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Check EnableCreate/EnableCall in ante handler WIP: #1045 Reject tx early in ante handler, avoid deduct user fee for vain. * add unit tests * update changelog Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> Co-authored-by: Federico Kunze Küllmer --- CHANGELOG.md | 4 ++ app/ante/ante_test.go | 137 ++++++++++++++++++++++++++++++++++++++++- app/ante/eth.go | 17 +++-- app/ante/utils_test.go | 8 ++- 4 files changed, 159 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b55c4a2a..f0cca4b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,10 @@ Ref: https://keepachangelog.com/en/1.0.0/ ## Unreleased +### State Machine Breaking + +* (ante) [tharsis#1060](https://github.com/tharsis/ethermint/pull/1060) Check `EnableCreate`/`EnableCall` in `AnteHandler` to short-circuit EVM transactions. + ### API Breaking * (rpc) [tharsis#1070](https://github.com/tharsis/ethermint/pull/1070) Refactor `rpc/` package: diff --git a/app/ante/ante_test.go b/app/ante/ante_test.go index 2548ba30..e3812f59 100644 --- a/app/ante/ante_test.go +++ b/app/ante/ante_test.go @@ -1,10 +1,12 @@ package ante_test import ( - "github.com/cosmos/cosmos-sdk/types/tx/signing" + "errors" "math/big" "strings" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + sdk "github.com/cosmos/cosmos-sdk/types" "github.com/ethereum/go-ethereum/core/types" @@ -685,3 +687,136 @@ func (suite AnteTestSuite) TestAnteHandlerWithDynamicTxFee() { suite.enableFeemarket = false suite.enableLondonHF = true } + +func (suite AnteTestSuite) TestAnteHandlerWithParams() { + addr, privKey := tests.NewAddrKey() + to := tests.GenerateAddress() + + testCases := []struct { + name string + txFn func() sdk.Tx + enableCall bool + enableCreate bool + expErr error + }{ + { + "fail - Contract Creation Disabled", + func() sdk.Tx { + signedContractTx := + evmtypes.NewTxContract( + suite.app.EvmKeeper.ChainID(), + 1, + big.NewInt(10), + 100000, + nil, + big.NewInt(ethparams.InitialBaseFee+1), + big.NewInt(1), + nil, + &types.AccessList{}, + ) + signedContractTx.From = addr.Hex() + + tx := suite.CreateTestTx(signedContractTx, privKey, 1, false) + return tx + }, + true, false, + evmtypes.ErrCreateDisabled, + }, + { + "success - Contract Creation Enabled", + func() sdk.Tx { + signedContractTx := + evmtypes.NewTxContract( + suite.app.EvmKeeper.ChainID(), + 1, + big.NewInt(10), + 100000, + nil, + big.NewInt(ethparams.InitialBaseFee+1), + big.NewInt(1), + nil, + &types.AccessList{}, + ) + signedContractTx.From = addr.Hex() + + tx := suite.CreateTestTx(signedContractTx, privKey, 1, false) + return tx + }, + true, true, + nil, + }, + { + "fail - EVM Call Disabled", + func() sdk.Tx { + signedTx := + evmtypes.NewTx( + suite.app.EvmKeeper.ChainID(), + 1, + &to, + big.NewInt(10), + 100000, + nil, + big.NewInt(ethparams.InitialBaseFee+1), + big.NewInt(1), + nil, + &types.AccessList{}, + ) + signedTx.From = addr.Hex() + + tx := suite.CreateTestTx(signedTx, privKey, 1, false) + return tx + }, + false, true, + evmtypes.ErrCallDisabled, + }, + { + "success - EVM Call Enabled", + func() sdk.Tx { + signedTx := + evmtypes.NewTx( + suite.app.EvmKeeper.ChainID(), + 1, + &to, + big.NewInt(10), + 100000, + nil, + big.NewInt(ethparams.InitialBaseFee+1), + big.NewInt(1), + nil, + &types.AccessList{}, + ) + signedTx.From = addr.Hex() + + tx := suite.CreateTestTx(signedTx, privKey, 1, false) + return tx + }, + true, true, + nil, + }, + } + + for _, tc := range testCases { + suite.Run(tc.name, func() { + suite.evmParamsOption = func(params *evmtypes.Params) { + params.EnableCall = tc.enableCall + params.EnableCreate = tc.enableCreate + } + suite.SetupTest() // reset + + acc := suite.app.AccountKeeper.NewAccountWithAddress(suite.ctx, addr.Bytes()) + suite.Require().NoError(acc.SetSequence(1)) + suite.app.AccountKeeper.SetAccount(suite.ctx, acc) + + suite.ctx = suite.ctx.WithIsCheckTx(true) + suite.app.EvmKeeper.SetBalance(suite.ctx, addr, big.NewInt((ethparams.InitialBaseFee+10)*100000)) + _, err := suite.anteHandler(suite.ctx, tc.txFn(), false) + if tc.expErr == nil { + suite.Require().NoError(err) + } else { + suite.Require().Error(err) + suite.Require().True(errors.Is(err, tc.expErr)) + } + }) + } + suite.evmParamsOption = nil +} diff --git a/app/ante/eth.go b/app/ante/eth.go index a0cc9608..b0b39639 100644 --- a/app/ante/eth.go +++ b/app/ante/eth.go @@ -410,11 +410,17 @@ func (vbd EthValidateBasicDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simu txFee := sdk.Coins{} txGasLimit := uint64(0) + params := vbd.evmKeeper.GetParams(ctx) + chainID := vbd.evmKeeper.ChainID() + ethCfg := params.ChainConfig.EthereumConfig(chainID) + baseFee := vbd.evmKeeper.GetBaseFee(ctx, ethCfg) + for _, msg := range protoTx.GetMsgs() { msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx) if !ok { return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid message type %T, expected %T", msg, (*evmtypes.MsgEthereumTx)(nil)) } + txGasLimit += msgEthTx.GetGas() txData, err := evmtypes.UnpackTxData(msgEthTx.Data) @@ -422,10 +428,13 @@ func (vbd EthValidateBasicDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simu return ctx, sdkerrors.Wrap(err, "failed to unpack MsgEthereumTx Data") } - params := vbd.evmKeeper.GetParams(ctx) - chainID := vbd.evmKeeper.ChainID() - ethCfg := params.ChainConfig.EthereumConfig(chainID) - baseFee := vbd.evmKeeper.GetBaseFee(ctx, ethCfg) + // return error if contract creation or call are disabled through governance + if !params.EnableCreate && txData.GetTo() == nil { + return ctx, sdkerrors.Wrap(evmtypes.ErrCreateDisabled, "failed to create new contract") + } else if !params.EnableCall && txData.GetTo() != nil { + return ctx, sdkerrors.Wrap(evmtypes.ErrCallDisabled, "failed to call contract") + } + if baseFee == nil && txData.TxType() == ethtypes.DynamicFeeTxType { return ctx, sdkerrors.Wrap(ethtypes.ErrTxTypeNotSupported, "dynamic fee tx not supported") } diff --git a/app/ante/utils_test.go b/app/ante/utils_test.go index 1375bde8..83f30378 100644 --- a/app/ante/utils_test.go +++ b/app/ante/utils_test.go @@ -51,6 +51,7 @@ type AnteTestSuite struct { ethSigner ethtypes.Signer enableFeemarket bool enableLondonHF bool + evmParamsOption func(*evmtypes.Params) } func (suite *AnteTestSuite) StateDB() *statedb.StateDB { @@ -71,14 +72,17 @@ func (suite *AnteTestSuite) SetupTest() { suite.Require().NoError(err) genesis[feemarkettypes.ModuleName] = app.AppCodec().MustMarshalJSON(feemarketGenesis) } + evmGenesis := evmtypes.DefaultGenesisState() if !suite.enableLondonHF { - evmGenesis := evmtypes.DefaultGenesisState() maxInt := sdk.NewInt(math.MaxInt64) evmGenesis.Params.ChainConfig.LondonBlock = &maxInt evmGenesis.Params.ChainConfig.ArrowGlacierBlock = &maxInt evmGenesis.Params.ChainConfig.MergeForkBlock = &maxInt - genesis[evmtypes.ModuleName] = app.AppCodec().MustMarshalJSON(evmGenesis) } + if suite.evmParamsOption != nil { + suite.evmParamsOption(&evmGenesis.Params) + } + genesis[evmtypes.ModuleName] = app.AppCodec().MustMarshalJSON(evmGenesis) return genesis })