diff --git a/CHANGELOG.md b/CHANGELOG.md index 31622d3e..317403a0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,16 +37,20 @@ Ref: https://keepachangelog.com/en/1.0.0/ ## Unreleased +### API Breaking + +* (ante) [\#866](https://github.com/tharsis/ethermint/pull/866) `NewAnteHandler` constructor now receives a `HandlerOptions` field. + ### State Machine Breaking -- (evm) [tharsis#840](https://github.com/tharsis/ethermint/pull/840) Store empty topics as empty array rather than nil. -- (feemarket) [tharsis#822](https://github.com/tharsis/ethermint/pull/822) Update EIP1559 base fee in `BeginBlock`. -- (evm) [tharsis#817](https://github.com/tharsis/ethermint/pull/817) Use `effectiveGasPrice` in ante handler, add `effectiveGasPrice` to tx receipt. -- (evm) [tharsis#808](https://github.com/tharsis/ethermint/issues/808) increase nonce in ante handler for contract creation transaction. -- (evm) [tharsis#851](https://github.com/tharsis/ethermint/pull/851) fix contract address used in EVM, this issue is caused by [tharsis#808](https://github.com/tharsis/ethermint/issues/808). -- (evm) [tharsis#N/A]() reject invalid `MsgEthereumTx` wrapping tx -- (evm) [tharsis#N/A]() Fix SelfDestruct opcode by deleting account code and state -- (feemarket) [tharsis#855](https://github.com/tharsis/ethermint/pull/855) consistent baseFee check logic +* (evm) [tharsis#840](https://github.com/tharsis/ethermint/pull/840) Store empty topics as empty array rather than nil. +* (feemarket) [tharsis#822](https://github.com/tharsis/ethermint/pull/822) Update EIP1559 base fee in `BeginBlock`. +* (evm) [tharsis#817](https://github.com/tharsis/ethermint/pull/817) Use `effectiveGasPrice` in ante handler, add `effectiveGasPrice` to tx receipt. +* (evm) [tharsis#808](https://github.com/tharsis/ethermint/issues/808) increase nonce in ante handler for contract creation transaction. +* (evm) [tharsis#851](https://github.com/tharsis/ethermint/pull/851) fix contract address used in EVM, this issue is caused by [tharsis#808](https://github.com/tharsis/ethermint/issues/808). +* (evm) Reject invalid `MsgEthereumTx` wrapping tx +* (evm) Fix `SelfDestruct` opcode by deleting account code and state. +* (feemarket) [tharsis#855](https://github.com/tharsis/ethermint/pull/855) consistent `BaseFee` check logic. ### Improvements diff --git a/app/ante/ante.go b/app/ante/ante.go index 8c274a04..1c6ab912 100644 --- a/app/ante/ante.go +++ b/app/ante/ante.go @@ -10,14 +10,9 @@ import ( sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" "github.com/cosmos/cosmos-sdk/types/tx/signing" authante "github.com/cosmos/cosmos-sdk/x/auth/ante" - authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" - channelkeeper "github.com/cosmos/ibc-go/v2/modules/core/04-channel/keeper" - ibcante "github.com/cosmos/ibc-go/v2/modules/core/ante" - "github.com/tharsis/ethermint/crypto/ethsecp256k1" - evmtypes "github.com/tharsis/ethermint/x/evm/types" ) const ( @@ -28,15 +23,7 @@ const ( // Ethereum or SDK transaction to an internal ante handler for performing // transaction-level processing (e.g. fee payment, signature verification) before // being passed onto it's respective handler. -func NewAnteHandler( - ak evmtypes.AccountKeeper, - bankKeeper evmtypes.BankKeeper, - evmKeeper EVMKeeper, - feeGrantKeeper authante.FeegrantKeeper, - channelKeeper channelkeeper.Keeper, - feeMarketKeeper evmtypes.FeeMarketKeeper, - signModeHandler authsigning.SignModeHandler, -) sdk.AnteHandler { +func NewAnteHandler(options HandlerOptions) sdk.AnteHandler { return func( ctx sdk.Context, tx sdk.Tx, sim bool, ) (newCtx sdk.Context, err error) { @@ -51,24 +38,11 @@ func NewAnteHandler( switch typeURL := opts[0].GetTypeUrl(); typeURL { case "/ethermint.evm.v1.ExtensionOptionsEthereumTx": // handle as *evmtypes.MsgEthereumTx - - anteHandler = sdk.ChainAnteDecorators( - NewEthSetUpContextDecorator(), // outermost AnteDecorator. SetUpContext must be called first - NewEthMempoolFeeDecorator(evmKeeper, feeMarketKeeper), // Check eth effective gas price against minimal-gas-prices - NewEthValidateBasicDecorator(evmKeeper), - NewEthSigVerificationDecorator(evmKeeper), - NewEthAccountVerificationDecorator(ak, bankKeeper, evmKeeper), - NewEthNonceVerificationDecorator(ak), - NewEthGasConsumeDecorator(evmKeeper), - NewCanTransferDecorator(evmKeeper, feeMarketKeeper), - NewEthIncrementSenderSequenceDecorator(ak), // innermost AnteDecorator. - ) - + anteHandler = newEthAnteHandler(options) default: return ctx, sdkerrors.Wrapf( sdkerrors.ErrUnknownExtensionOptions, - "rejecting tx with unsupported extension option: %s", - typeURL, + "rejecting tx with unsupported extension option: %s", typeURL, ) } @@ -76,37 +50,10 @@ func NewAnteHandler( } } - // Reject messages that requires specific authentication here. - // For example `MsgEthereumTx` requires fee to be deducted in the antehandler in order to perform the refund. - for _, msg := range tx.GetMsgs() { - if _, ok := msg.(*evmtypes.MsgEthereumTx); ok { - return ctx, sdkerrors.Wrapf( - sdkerrors.ErrInvalidType, - "MsgEthereumTx needs to be contained within a tx with ExtensionOptionsEthereumTx option", - ) - } - } - // handle as totally normal Cosmos SDK tx - switch tx.(type) { case sdk.Tx: - anteHandler = sdk.ChainAnteDecorators( - authante.NewSetUpContextDecorator(), // outermost AnteDecorator. SetUpContext must be called first - authante.NewRejectExtensionOptionsDecorator(), - authante.NewMempoolFeeDecorator(), - authante.NewValidateBasicDecorator(), - authante.NewTxTimeoutHeightDecorator(), - authante.NewValidateMemoDecorator(ak), - ibcante.NewAnteDecorator(channelKeeper), - authante.NewConsumeGasForTxSizeDecorator(ak), - authante.NewSetPubKeyDecorator(ak), // SetPubKeyDecorator must be called before all signature verification decorators - authante.NewValidateSigCountDecorator(ak), - authante.NewDeductFeeDecorator(ak, bankKeeper, feeGrantKeeper), - authante.NewSigGasConsumeDecorator(ak, DefaultSigVerificationGasConsumer), - authante.NewSigVerificationDecorator(ak, signModeHandler), - authante.NewIncrementSequenceDecorator(ak), // innermost AnteDecorator - ) + anteHandler = newCosmosAnteHandler(options) default: return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type: %T", tx) } diff --git a/app/ante/eth.go b/app/ante/eth.go index 65ede030..75ea445f 100644 --- a/app/ante/eth.go +++ b/app/ante/eth.go @@ -6,7 +6,6 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" - tx "github.com/cosmos/cosmos-sdk/types/tx" authante "github.com/cosmos/cosmos-sdk/x/auth/ante" ethermint "github.com/tharsis/ethermint/types" @@ -14,32 +13,9 @@ import ( evmtypes "github.com/tharsis/ethermint/x/evm/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" ) -// EVMKeeper defines the expected keeper interface used on the Eth AnteHandler -type EVMKeeper interface { - vm.StateDB - - ChainID() *big.Int - GetParams(ctx sdk.Context) evmtypes.Params - WithContext(ctx sdk.Context) - ResetRefundTransient(ctx sdk.Context) - NewEVM(msg core.Message, cfg *evmtypes.EVMConfig, tracer vm.Tracer) *vm.EVM - GetCodeHash(addr common.Address) common.Hash - DeductTxCostsFromUserBalance( - ctx sdk.Context, msgEthTx evmtypes.MsgEthereumTx, txData evmtypes.TxData, denom string, homestead, istanbul, london bool, - ) (sdk.Coins, error) - BaseFee(ctx sdk.Context, ethCfg *params.ChainConfig) *big.Int -} - -type protoTxProvider interface { - GetProtoTx() *tx.Tx -} - // EthSigVerificationDecorator validates an ethereum signatures type EthSigVerificationDecorator struct { evmKeeper EVMKeeper diff --git a/app/ante/handler_options.go b/app/ante/handler_options.go new file mode 100644 index 00000000..cf637b80 --- /dev/null +++ b/app/ante/handler_options.go @@ -0,0 +1,82 @@ +package ante + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + "github.com/cosmos/cosmos-sdk/types/tx/signing" + "github.com/cosmos/cosmos-sdk/x/auth/ante" + authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" + + channelkeeper "github.com/cosmos/ibc-go/v2/modules/core/04-channel/keeper" + ibcante "github.com/cosmos/ibc-go/v2/modules/core/ante" + + evmtypes "github.com/tharsis/ethermint/x/evm/types" +) + +// HandlerOptions extend the SDK's AnteHandler options by requiring the IBC +// channel keeper, EVM Keeper and Fee Market Keeper. +type HandlerOptions struct { + AccountKeeper evmtypes.AccountKeeper + BankKeeper evmtypes.BankKeeper + IBCChannelKeeper channelkeeper.Keeper + FeeMarketKeeper evmtypes.FeeMarketKeeper + EvmKeeper EVMKeeper + FeegrantKeeper ante.FeegrantKeeper + SignModeHandler authsigning.SignModeHandler + SigGasConsumer func(meter sdk.GasMeter, sig signing.SignatureV2, params authtypes.Params) error +} + +func (options HandlerOptions) Validate() error { + if options.AccountKeeper == nil { + return sdkerrors.Wrap(sdkerrors.ErrLogic, "account keeper is required for AnteHandler") + } + if options.BankKeeper == nil { + return sdkerrors.Wrap(sdkerrors.ErrLogic, "bank keeper is required for AnteHandler") + } + if options.SignModeHandler == nil { + return sdkerrors.Wrap(sdkerrors.ErrLogic, "sign mode handler is required for ante builder") + } + if options.FeeMarketKeeper == nil { + return sdkerrors.Wrap(sdkerrors.ErrLogic, "fee market keeper is required for AnteHandler") + } + if options.EvmKeeper == nil { + return sdkerrors.Wrap(sdkerrors.ErrLogic, "evm keeper is required for AnteHandler") + } + return nil +} + +func newEthAnteHandler(options HandlerOptions) sdk.AnteHandler { + return sdk.ChainAnteDecorators( + NewEthSetUpContextDecorator(), // outermost AnteDecorator. SetUpContext must be called first + NewEthMempoolFeeDecorator(options.EvmKeeper, options.FeeMarketKeeper), // Check eth effective gas price against minimal-gas-prices + NewEthValidateBasicDecorator(options.EvmKeeper), + NewEthSigVerificationDecorator(options.EvmKeeper), + NewEthAccountVerificationDecorator(options.AccountKeeper, options.BankKeeper, options.EvmKeeper), + NewEthNonceVerificationDecorator(options.AccountKeeper), + NewEthGasConsumeDecorator(options.EvmKeeper), + NewCanTransferDecorator(options.EvmKeeper, options.FeeMarketKeeper), + NewEthIncrementSenderSequenceDecorator(options.AccountKeeper), // innermost AnteDecorator. + ) +} + +func newCosmosAnteHandler(options HandlerOptions) sdk.AnteHandler { + return sdk.ChainAnteDecorators( + RejectMessagesDecorator{}, // reject MsgEthereumTxs + ante.NewSetUpContextDecorator(), + ante.NewRejectExtensionOptionsDecorator(), + ante.NewMempoolFeeDecorator(), + ante.NewValidateBasicDecorator(), + ante.NewTxTimeoutHeightDecorator(), + ante.NewValidateMemoDecorator(options.AccountKeeper), + ante.NewConsumeGasForTxSizeDecorator(options.AccountKeeper), + ante.NewDeductFeeDecorator(options.AccountKeeper, options.BankKeeper, options.FeegrantKeeper), + // SetPubKeyDecorator must be called before all signature verification decorators + ante.NewSetPubKeyDecorator(options.AccountKeeper), + ante.NewValidateSigCountDecorator(options.AccountKeeper), + ante.NewSigGasConsumeDecorator(options.AccountKeeper, options.SigGasConsumer), + ante.NewSigVerificationDecorator(options.AccountKeeper, options.SignModeHandler), + ante.NewIncrementSequenceDecorator(options.AccountKeeper), + ibcante.NewAnteDecorator(options.IBCChannelKeeper), + ) +} diff --git a/app/ante/interfaces.go b/app/ante/interfaces.go new file mode 100644 index 00000000..08bf9c72 --- /dev/null +++ b/app/ante/interfaces.go @@ -0,0 +1,33 @@ +package ante + +import ( + "math/big" + + sdk "github.com/cosmos/cosmos-sdk/types" + tx "github.com/cosmos/cosmos-sdk/types/tx" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/params" + evmtypes "github.com/tharsis/ethermint/x/evm/types" +) + +// EVMKeeper defines the expected keeper interface used on the Eth AnteHandler +type EVMKeeper interface { + vm.StateDB + + ChainID() *big.Int + GetParams(ctx sdk.Context) evmtypes.Params + WithContext(ctx sdk.Context) + ResetRefundTransient(ctx sdk.Context) + NewEVM(msg core.Message, cfg *evmtypes.EVMConfig, tracer vm.Tracer) *vm.EVM + GetCodeHash(addr common.Address) common.Hash + DeductTxCostsFromUserBalance( + ctx sdk.Context, msgEthTx evmtypes.MsgEthereumTx, txData evmtypes.TxData, denom string, homestead, istanbul, london bool, + ) (sdk.Coins, error) + BaseFee(ctx sdk.Context, ethCfg *params.ChainConfig) *big.Int +} + +type protoTxProvider interface { + GetProtoTx() *tx.Tx +} diff --git a/app/ante/reject_msgs.go b/app/ante/reject_msgs.go new file mode 100644 index 00000000..fafb159e --- /dev/null +++ b/app/ante/reject_msgs.go @@ -0,0 +1,25 @@ +package ante + +import ( + sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + evmtypes "github.com/tharsis/ethermint/x/evm/types" +) + +// RejectMessagesDecorator prevents invalid msg types from being executed +type RejectMessagesDecorator struct{} + +// AnteHandle rejects messages that requires ethereum-specific authentication. +// For example `MsgEthereumTx` requires fee to be deducted in the antehandler in +// order to perform the refund. +func (rmd RejectMessagesDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { + for _, msg := range tx.GetMsgs() { + if _, ok := msg.(*evmtypes.MsgEthereumTx); ok { + return ctx, sdkerrors.Wrapf( + sdkerrors.ErrInvalidType, + "MsgEthereumTx needs to be contained within a tx with 'ExtensionOptionsEthereumTx' option", + ) + } + } + return next(ctx, tx, simulate) +} diff --git a/app/ante/utils_test.go b/app/ante/utils_test.go index 5469ad11..3a94e8a2 100644 --- a/app/ante/utils_test.go +++ b/app/ante/utils_test.go @@ -81,11 +81,20 @@ func (suite *AnteTestSuite) SetupTest() { suite.clientCtx = client.Context{}.WithTxConfig(encodingConfig.TxConfig) - suite.anteHandler = ante.NewAnteHandler( - suite.app.AccountKeeper, suite.app.BankKeeper, suite.app.EvmKeeper, suite.app.FeeGrantKeeper, - suite.app.IBCKeeper.ChannelKeeper, suite.app.FeeMarketKeeper, - encodingConfig.TxConfig.SignModeHandler(), - ) + options := ante.HandlerOptions{ + AccountKeeper: suite.app.AccountKeeper, + BankKeeper: suite.app.BankKeeper, + EvmKeeper: suite.app.EvmKeeper, + FeegrantKeeper: suite.app.FeeGrantKeeper, + IBCChannelKeeper: suite.app.IBCKeeper.ChannelKeeper, + FeeMarketKeeper: suite.app.FeeMarketKeeper, + SignModeHandler: encodingConfig.TxConfig.SignModeHandler(), + SigGasConsumer: ante.DefaultSigVerificationGasConsumer, + } + + suite.Require().NoError(options.Validate()) + + suite.anteHandler = ante.NewAnteHandler(options) suite.ethSigner = ethtypes.LatestSignerForChainID(suite.app.EvmKeeper.ChainID()) } diff --git a/app/app.go b/app/app.go index 5d00dc78..d011bded 100644 --- a/app/app.go +++ b/app/app.go @@ -512,14 +512,23 @@ func NewEthermintApp( app.SetBeginBlocker(app.BeginBlocker) // use Ethermint's custom AnteHandler - app.SetAnteHandler( - ante.NewAnteHandler( - app.AccountKeeper, app.BankKeeper, app.EvmKeeper, app.FeeGrantKeeper, app.IBCKeeper.ChannelKeeper, - app.FeeMarketKeeper, - encodingConfig.TxConfig.SignModeHandler(), - ), - ) + options := ante.HandlerOptions{ + AccountKeeper: app.AccountKeeper, + BankKeeper: app.BankKeeper, + EvmKeeper: app.EvmKeeper, + FeegrantKeeper: app.FeeGrantKeeper, + IBCChannelKeeper: app.IBCKeeper.ChannelKeeper, + FeeMarketKeeper: app.FeeMarketKeeper, + SignModeHandler: encodingConfig.TxConfig.SignModeHandler(), + SigGasConsumer: ante.DefaultSigVerificationGasConsumer, + } + + if err := options.Validate(); err != nil { + panic(err) + } + + app.SetAnteHandler(ante.NewAnteHandler(options)) app.SetEndBlocker(app.EndBlocker) if loadLatest {