-
Notifications
You must be signed in to change notification settings - Fork 834
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
imp(ante): MonoAnteHandler for EVM transactions (#2028)
* imp(ante): MonoAnteHandler for EVM transactions * cont refactor * can transfer * cont refactor * fix from * consume gas * fixes * update setup * use struct * add ante handler benchmarking tests * check error on factory * run make format * make effective fee compatible with old ante * add comment * fix benchmark tests * improve benchmarking * add comments * add licenses * Update app/ante/evm.go Co-authored-by: Federico Kunze Küllmer <31522760+fedekunze@users.noreply.github.com> * add comment * add changelog * add changelog * fix lint * fix licenses * fix werc20 tests * fix lint --------- Co-authored-by: Freddy Caceres <facs95@gmail.com> Co-authored-by: facs95 <facs95@users.noreply.github.com>
- Loading branch information
1 parent
84ca6b3
commit 8a46ed8
Showing
30 changed files
with
1,805 additions
and
951 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
// Copyright Tharsis Labs Ltd.(Evmos) | ||
// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) | ||
|
||
package ante | ||
|
||
import ( | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
"github.com/cosmos/cosmos-sdk/x/auth/ante" | ||
sdkvesting "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" | ||
ibcante "github.com/cosmos/ibc-go/v7/modules/core/ante" | ||
cosmosante "github.com/evmos/evmos/v15/app/ante/cosmos" | ||
evmante "github.com/evmos/evmos/v15/app/ante/evm" | ||
evmtypes "github.com/evmos/evmos/v15/x/evm/types" | ||
) | ||
|
||
// newCosmosAnteHandler creates the default ante handler for Cosmos transactions | ||
func newCosmosAnteHandler(options HandlerOptions) sdk.AnteHandler { | ||
return sdk.ChainAnteDecorators( | ||
cosmosante.RejectMessagesDecorator{}, // reject MsgEthereumTxs | ||
cosmosante.NewAuthzLimiterDecorator( // disable the Msg types that cannot be included on an authz.MsgExec msgs field | ||
sdk.MsgTypeURL(&evmtypes.MsgEthereumTx{}), | ||
sdk.MsgTypeURL(&sdkvesting.MsgCreateVestingAccount{}), | ||
), | ||
ante.NewSetUpContextDecorator(), | ||
ante.NewExtensionOptionsDecorator(options.ExtensionOptionChecker), | ||
ante.NewValidateBasicDecorator(), | ||
ante.NewTxTimeoutHeightDecorator(), | ||
ante.NewValidateMemoDecorator(options.AccountKeeper), | ||
cosmosante.NewMinGasPriceDecorator(options.FeeMarketKeeper, options.EvmKeeper), | ||
ante.NewConsumeGasForTxSizeDecorator(options.AccountKeeper), | ||
cosmosante.NewDeductFeeDecorator(options.AccountKeeper, options.BankKeeper, options.DistributionKeeper, options.FeegrantKeeper, options.StakingKeeper, options.TxFeeChecker), | ||
cosmosante.NewVestingDelegationDecorator(options.AccountKeeper, options.StakingKeeper, options.BankKeeper, options.Cdc), | ||
// 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.NewRedundantRelayDecorator(options.IBCKeeper), | ||
evmante.NewGasWantedDecorator(options.EvmKeeper, options.FeeMarketKeeper), | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
// Copyright Tharsis Labs Ltd.(Evmos) | ||
// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) | ||
|
||
package ante | ||
|
||
import ( | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
"github.com/cosmos/cosmos-sdk/x/auth/ante" | ||
sdkvesting "github.com/cosmos/cosmos-sdk/x/auth/vesting/types" | ||
ibcante "github.com/cosmos/ibc-go/v7/modules/core/ante" | ||
cosmosante "github.com/evmos/evmos/v15/app/ante/cosmos" | ||
evmante "github.com/evmos/evmos/v15/app/ante/evm" | ||
evmtypes "github.com/evmos/evmos/v15/x/evm/types" | ||
) | ||
|
||
func newMonoEVMAnteHandler(options HandlerOptions) sdk.AnteHandler { | ||
return sdk.ChainAnteDecorators( | ||
evmante.NewMonoDecorator( | ||
options.AccountKeeper, | ||
options.BankKeeper, | ||
options.FeeMarketKeeper, | ||
options.EvmKeeper, | ||
options.DistributionKeeper, | ||
options.StakingKeeper, | ||
options.MaxTxGasWanted, | ||
), | ||
) | ||
} | ||
|
||
// newEVMAnteHandler creates the default ante handler for Ethereum transactions | ||
func newEVMAnteHandler(options HandlerOptions) sdk.AnteHandler { //nolint: unused | ||
return sdk.ChainAnteDecorators( | ||
// outermost AnteDecorator. SetUpContext must be called first | ||
evmante.NewEthSetUpContextDecorator(options.EvmKeeper), | ||
// Check eth effective gas price against the node's minimal-gas-prices config | ||
evmante.NewEthMempoolFeeDecorator(options.EvmKeeper), | ||
// Check eth effective gas price against the global MinGasPrice | ||
evmante.NewEthMinGasPriceDecorator(options.FeeMarketKeeper, options.EvmKeeper), | ||
evmante.NewEthValidateBasicDecorator(options.EvmKeeper), | ||
evmante.NewEthSigVerificationDecorator(options.EvmKeeper), | ||
evmante.NewEthAccountVerificationDecorator(options.AccountKeeper, options.EvmKeeper), | ||
evmante.NewCanTransferDecorator(options.EvmKeeper), | ||
evmante.NewEthVestingTransactionDecorator(options.AccountKeeper, options.BankKeeper, options.EvmKeeper), | ||
evmante.NewEthGasConsumeDecorator(options.BankKeeper, options.DistributionKeeper, options.EvmKeeper, options.StakingKeeper, options.MaxTxGasWanted), | ||
evmante.NewEthIncrementSenderSequenceDecorator(options.AccountKeeper), | ||
evmante.NewGasWantedDecorator(options.EvmKeeper, options.FeeMarketKeeper), | ||
// emit eth tx hash and index at the very last ante handler. | ||
evmante.NewEthEmitEventDecorator(options.EvmKeeper), | ||
) | ||
} | ||
|
||
// newCosmosAnteHandlerEip712 creates the ante handler for transactions signed with EIP712 | ||
func newLegacyCosmosAnteHandlerEip712(options HandlerOptions) sdk.AnteHandler { | ||
return sdk.ChainAnteDecorators( | ||
cosmosante.RejectMessagesDecorator{}, // reject MsgEthereumTxs | ||
cosmosante.NewAuthzLimiterDecorator( // disable the Msg types that cannot be included on an authz.MsgExec msgs field | ||
sdk.MsgTypeURL(&evmtypes.MsgEthereumTx{}), | ||
sdk.MsgTypeURL(&sdkvesting.MsgCreateVestingAccount{}), | ||
), | ||
ante.NewSetUpContextDecorator(), | ||
ante.NewValidateBasicDecorator(), | ||
ante.NewTxTimeoutHeightDecorator(), | ||
cosmosante.NewMinGasPriceDecorator(options.FeeMarketKeeper, options.EvmKeeper), | ||
ante.NewValidateMemoDecorator(options.AccountKeeper), | ||
ante.NewConsumeGasForTxSizeDecorator(options.AccountKeeper), | ||
cosmosante.NewDeductFeeDecorator(options.AccountKeeper, options.BankKeeper, options.DistributionKeeper, options.FeegrantKeeper, options.StakingKeeper, options.TxFeeChecker), | ||
cosmosante.NewVestingDelegationDecorator(options.AccountKeeper, options.StakingKeeper, options.BankKeeper, options.Cdc), | ||
// SetPubKeyDecorator must be called before all signature verification decorators | ||
ante.NewSetPubKeyDecorator(options.AccountKeeper), | ||
ante.NewValidateSigCountDecorator(options.AccountKeeper), | ||
ante.NewSigGasConsumeDecorator(options.AccountKeeper, options.SigGasConsumer), | ||
// Note: signature verification uses EIP instead of the cosmos signature validator | ||
//nolint: staticcheck | ||
cosmosante.NewLegacyEip712SigVerificationDecorator(options.AccountKeeper, options.SignModeHandler), | ||
ante.NewIncrementSequenceDecorator(options.AccountKeeper), | ||
ibcante.NewRedundantRelayDecorator(options.IBCKeeper), | ||
evmante.NewGasWantedDecorator(options.EvmKeeper, options.FeeMarketKeeper), | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
// Copyright Tharsis Labs Ltd.(Evmos) | ||
// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) | ||
package evm | ||
|
||
import ( | ||
errorsmod "cosmossdk.io/errors" | ||
storetypes "github.com/cosmos/cosmos-sdk/store/types" | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
errortypes "github.com/cosmos/cosmos-sdk/types/errors" | ||
authante "github.com/cosmos/cosmos-sdk/x/auth/ante" | ||
) | ||
|
||
var _ sdk.AnteDecorator = &EthSetupContextDecorator{} | ||
|
||
// EthSetupContextDecorator is adapted from SetUpContextDecorator from cosmos-sdk, it ignores gas consumption | ||
// by setting the gas meter to infinite | ||
type EthSetupContextDecorator struct { | ||
evmKeeper EVMKeeper | ||
} | ||
|
||
func NewEthSetUpContextDecorator(evmKeeper EVMKeeper) EthSetupContextDecorator { | ||
return EthSetupContextDecorator{ | ||
evmKeeper: evmKeeper, | ||
} | ||
} | ||
|
||
func (esc EthSetupContextDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { | ||
newCtx, err = SetupContext(ctx, tx, esc.evmKeeper) | ||
if err != nil { | ||
return ctx, err | ||
} | ||
return next(newCtx, tx, simulate) | ||
} | ||
|
||
func SetupContext(ctx sdk.Context, tx sdk.Tx, evmKeeper EVMKeeper) (sdk.Context, error) { | ||
// all transactions must implement GasTx | ||
_, ok := tx.(authante.GasTx) | ||
if !ok { | ||
return ctx, errorsmod.Wrapf(errortypes.ErrInvalidType, "invalid transaction type %T, expected GasTx", tx) | ||
} | ||
|
||
// We need to setup an empty gas config so that the gas is consistent with Ethereum. | ||
newCtx := ctx.WithGasMeter(sdk.NewInfiniteGasMeter()). | ||
WithKVGasConfig(storetypes.GasConfig{}). | ||
WithTransientKVGasConfig(storetypes.GasConfig{}) | ||
|
||
// Reset transient gas used to prepare the execution of current cosmos tx. | ||
// Transient gas-used is necessary to sum the gas-used of cosmos tx, when it contains multiple eth msgs. | ||
evmKeeper.ResetTransientGasUsed(ctx) | ||
|
||
return newCtx, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
// Copyright Tharsis Labs Ltd.(Evmos) | ||
// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) | ||
package evm | ||
|
||
import ( | ||
"math/big" | ||
|
||
errorsmod "cosmossdk.io/errors" | ||
sdkmath "cosmossdk.io/math" | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
errortypes "github.com/cosmos/cosmos-sdk/types/errors" | ||
evmtypes "github.com/evmos/evmos/v15/x/evm/types" | ||
) | ||
|
||
// EthMempoolFeeDecorator will check if the transaction's effective fee is at least as large | ||
// as the local validator's minimum gasFee (defined in validator config). | ||
// If fee is too low, decorator returns error and tx is rejected from mempool. | ||
// Note this only applies when ctx.CheckTx = true | ||
// If fee is high enough or not CheckTx, then call next AnteHandler | ||
// CONTRACT: Tx must implement FeeTx to use MempoolFeeDecorator | ||
type EthMempoolFeeDecorator struct { | ||
evmKeeper EVMKeeper | ||
} | ||
|
||
// NewEthMempoolFeeDecorator creates a new NewEthMempoolFeeDecorator instance used only for | ||
// Ethereum transactions. | ||
func NewEthMempoolFeeDecorator(ek EVMKeeper) EthMempoolFeeDecorator { | ||
return EthMempoolFeeDecorator{ | ||
evmKeeper: ek, | ||
} | ||
} | ||
|
||
// AnteHandle ensures that the provided fees meet a minimum threshold for the validator. | ||
// This check only for local mempool purposes, and thus it is only run on (Re)CheckTx. | ||
// The logic is also skipped if the London hard fork and EIP-1559 are enabled. | ||
func (mfd EthMempoolFeeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { | ||
if !ctx.IsCheckTx() || simulate { | ||
return next(ctx, tx, simulate) | ||
} | ||
|
||
evmParams := mfd.evmKeeper.GetParams(ctx) | ||
chainCfg := evmParams.GetChainConfig() | ||
ethCfg := chainCfg.EthereumConfig(mfd.evmKeeper.ChainID()) | ||
isLondon := ethCfg.IsLondon(big.NewInt(ctx.BlockHeight())) | ||
|
||
// skip check as the London hard fork and EIP-1559 are enabled | ||
if isLondon { | ||
return next(ctx, tx, simulate) | ||
} | ||
|
||
evmDenom := evmParams.GetEvmDenom() | ||
minGasPrice := ctx.MinGasPrices().AmountOf(evmDenom) | ||
|
||
for _, msg := range tx.GetMsgs() { | ||
_, txData, _, err := evmtypes.UnpackEthMsg(msg) | ||
if err != nil { | ||
return ctx, err | ||
} | ||
|
||
gasLimit := sdkmath.LegacyNewDecFromBigInt(new(big.Int).SetUint64(txData.GetGas())) | ||
fee := sdkmath.LegacyNewDecFromBigInt(txData.Fee()) | ||
|
||
if err := CheckMempoolFee(fee, minGasPrice, gasLimit, isLondon); err != nil { | ||
return ctx, err | ||
} | ||
} | ||
|
||
return next(ctx, tx, simulate) | ||
} | ||
|
||
// CheckMempoolFee checks if the provided fee is at least as large as the local validator's | ||
func CheckMempoolFee(fee, mempoolMinGasPrice, gasLimit sdkmath.LegacyDec, isLondon bool) error { | ||
if isLondon { | ||
return nil | ||
} | ||
|
||
requiredFee := mempoolMinGasPrice.Mul(gasLimit) | ||
|
||
if fee.LT(requiredFee) { | ||
return errorsmod.Wrapf( | ||
errortypes.ErrInsufficientFee, | ||
"insufficient fee; got: %s required: %s", | ||
fee, requiredFee, | ||
) | ||
} | ||
|
||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
// Copyright Tharsis Labs Ltd.(Evmos) | ||
// SPDX-License-Identifier:ENCL-1.0(https://github.com/evmos/evmos/blob/main/LICENSE) | ||
package evm | ||
|
||
import ( | ||
"math/big" | ||
|
||
errorsmod "cosmossdk.io/errors" | ||
sdkmath "cosmossdk.io/math" | ||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
errortypes "github.com/cosmos/cosmos-sdk/types/errors" | ||
|
||
ethtypes "github.com/ethereum/go-ethereum/core/types" | ||
evmtypes "github.com/evmos/evmos/v15/x/evm/types" | ||
) | ||
|
||
// EthMinGasPriceDecorator will check if the transaction's fee is at least as large | ||
// as the MinGasPrices param. If fee is too low, decorator returns error and tx | ||
// is rejected. This applies to both CheckTx and DeliverTx and regardless | ||
// if London hard fork or fee market params (EIP-1559) are enabled. | ||
// If fee is high enough, then call next AnteHandler | ||
type EthMinGasPriceDecorator struct { | ||
feesKeeper FeeMarketKeeper | ||
evmKeeper EVMKeeper | ||
} | ||
|
||
// NewEthMinGasPriceDecorator creates a new MinGasPriceDecorator instance used only for | ||
// Ethereum transactions. | ||
func NewEthMinGasPriceDecorator(fk FeeMarketKeeper, ek EVMKeeper) EthMinGasPriceDecorator { | ||
return EthMinGasPriceDecorator{feesKeeper: fk, evmKeeper: ek} | ||
} | ||
|
||
// AnteHandle ensures that the effective fee from the transaction is greater than the | ||
// minimum global fee, which is defined by the MinGasPrice (parameter) * GasLimit (tx argument). | ||
func (empd EthMinGasPriceDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { | ||
minGasPrice := empd.feesKeeper.GetParams(ctx).MinGasPrice | ||
|
||
// short-circuit if min gas price is 0 | ||
if minGasPrice.IsZero() { | ||
return next(ctx, tx, simulate) | ||
} | ||
|
||
evmParams := empd.evmKeeper.GetParams(ctx) | ||
chainCfg := evmParams.GetChainConfig() | ||
ethCfg := chainCfg.EthereumConfig(empd.evmKeeper.ChainID()) | ||
baseFee := empd.evmKeeper.GetBaseFee(ctx, ethCfg) | ||
|
||
for _, msg := range tx.GetMsgs() { | ||
_, txData, _, err := evmtypes.UnpackEthMsg(msg) | ||
if err != nil { | ||
return ctx, err | ||
} | ||
|
||
feeAmt := txData.Fee() | ||
|
||
if txData.TxType() != ethtypes.LegacyTxType { | ||
feeAmt = txData.EffectiveFee(baseFee) | ||
} | ||
|
||
gasLimit := sdk.NewDecFromBigInt(new(big.Int).SetUint64(txData.GetGas())) | ||
fee := sdkmath.LegacyNewDecFromBigInt(feeAmt) | ||
|
||
if err := CheckGlobalFee(fee, minGasPrice, gasLimit); err != nil { | ||
return ctx, err | ||
} | ||
} | ||
|
||
return next(ctx, tx, simulate) | ||
} | ||
|
||
// For dynamic transactions, GetFee() uses the GasFeeCap value, which | ||
// is the maximum gas price that the signer can pay. In practice, the | ||
// signer can pay less, if the block's BaseFee is lower. So, in this case, | ||
// we use the EffectiveFee. If the feemarket formula results in a BaseFee | ||
// that lowers EffectivePrice until it is < MinGasPrices, the users must | ||
// increase the GasTipCap (priority fee) until EffectivePrice > MinGasPrices. | ||
// Transactions with MinGasPrices * gasUsed < tx fees < EffectiveFee are rejected | ||
// by the feemarket AnteHandle | ||
func CheckGlobalFee(fee, globalMinGasPrice, gasLimit sdkmath.LegacyDec) error { | ||
if globalMinGasPrice.IsZero() { | ||
return nil | ||
} | ||
|
||
requiredFee := globalMinGasPrice.Mul(gasLimit) | ||
|
||
if fee.LT(requiredFee) { | ||
return errorsmod.Wrapf( | ||
errortypes.ErrInsufficientFee, | ||
"provided fee < minimum global fee (%s < %s). Please increase the priority tip (for EIP-1559 txs) or the gas prices (for access list or legacy txs)", //nolint:lll | ||
fee.TruncateInt().String(), requiredFee.TruncateInt().String(), | ||
) | ||
} | ||
|
||
return nil | ||
} |
Oops, something went wrong.