diff --git a/CHANGELOG.md b/CHANGELOG.md index b33fff33..8e79a722 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ This changelog was created using the `clu` binary ### Improvements +- (ante) [#52](https://github.com/evmos/os/pull/52) Refactor ante handlers to be easier to use in partner chains. - (all) [#48](https://github.com/evmos/os/pull/48) Move latest changes from evmOS main (f943af3b incl. SDK v50). - (all) [#43](https://github.com/evmos/os/pull/43) Update with latest evmOS main changes (2b7a8e2). - (tests) [#41](https://github.com/evmos/os/pull/41) Add Solidity and Ledger tests. diff --git a/ante/cosmos/eip712.go b/ante/cosmos/eip712.go index 9d89d7fa..5933c281 100644 --- a/ante/cosmos/eip712.go +++ b/ante/cosmos/eip712.go @@ -19,10 +19,10 @@ import ( ethcrypto "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto/secp256k1" "github.com/ethereum/go-ethereum/signer/core/apitypes" + anteinterfaces "github.com/evmos/os/ante/interfaces" "github.com/evmos/os/crypto/ethsecp256k1" "github.com/evmos/os/ethereum/eip712" "github.com/evmos/os/types" - evmtypes "github.com/evmos/os/x/evm/types" ) var evmosCodec codec.ProtoCodecMarshaler @@ -40,12 +40,12 @@ func init() { // CONTRACT: Pubkeys are set in context for all signers before this decorator runs // CONTRACT: Tx must implement SigVerifiableTx interface type LegacyEip712SigVerificationDecorator struct { - ak evmtypes.AccountKeeper + ak anteinterfaces.AccountKeeper } // Deprecated: NewLegacyEip712SigVerificationDecorator creates a new LegacyEip712SigVerificationDecorator func NewLegacyEip712SigVerificationDecorator( - ak evmtypes.AccountKeeper, + ak anteinterfaces.AccountKeeper, ) LegacyEip712SigVerificationDecorator { return LegacyEip712SigVerificationDecorator{ ak: ak, diff --git a/ante/cosmos/reject_msgs.go b/ante/cosmos/reject_msgs.go index e3bc1e7d..e776747c 100644 --- a/ante/cosmos/reject_msgs.go +++ b/ante/cosmos/reject_msgs.go @@ -10,9 +10,14 @@ import ( evmtypes "github.com/evmos/os/x/evm/types" ) -// RejectMessagesDecorator prevents invalid msg types from being executed +// RejectMessagesDecorator prevents invalid msg types from being executed. type RejectMessagesDecorator struct{} +// NewRejectMessagesDecorator creates a new RejectMessagesDecorator. +func NewRejectMessagesDecorator() sdk.AnteDecorator { + return RejectMessagesDecorator{} +} + // 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. diff --git a/ante/evm/06_account_verification.go b/ante/evm/06_account_verification.go index 79ecd07a..c77e7513 100644 --- a/ante/evm/06_account_verification.go +++ b/ante/evm/06_account_verification.go @@ -9,6 +9,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" errortypes "github.com/cosmos/cosmos-sdk/types/errors" "github.com/ethereum/go-ethereum/common" + anteinterfaces "github.com/evmos/os/ante/interfaces" "github.com/evmos/os/x/evm/keeper" "github.com/evmos/os/x/evm/statedb" evmtypes "github.com/evmos/os/x/evm/types" @@ -21,7 +22,7 @@ import ( // - account balance is lower than the transaction cost func VerifyAccountBalance( ctx sdk.Context, - accountKeeper evmtypes.AccountKeeper, + accountKeeper anteinterfaces.AccountKeeper, account *statedb.Account, from common.Address, txData evmtypes.TxData, diff --git a/ante/evm/08_gas_consume.go b/ante/evm/08_gas_consume.go index 27d7b7ea..ff6d8747 100644 --- a/ante/evm/08_gas_consume.go +++ b/ante/evm/08_gas_consume.go @@ -36,23 +36,16 @@ func UpdateCumulativeGasWanted( return cumulativeGasWanted } -type ConsumeGasKeepers struct { - Bank anteinterfaces.BankKeeper - Distribution anteinterfaces.DistributionKeeper - Evm anteinterfaces.EVMKeeper - Staking anteinterfaces.StakingKeeper -} - // ConsumeFeesAndEmitEvent deduces fees from sender and emits the event func ConsumeFeesAndEmitEvent( ctx sdktypes.Context, - keepers *ConsumeGasKeepers, + evmKeeper anteinterfaces.EVMKeeper, fees sdktypes.Coins, from sdktypes.AccAddress, ) error { if err := deductFees( ctx, - keepers, + evmKeeper, fees, from, ); err != nil { @@ -69,10 +62,9 @@ func ConsumeFeesAndEmitEvent( } // deductFee checks if the fee payer has enough funds to pay for the fees and deducts them. -// If the spendable balance is not enough, it tries to claim enough staking rewards to cover the fees. func deductFees( ctx sdktypes.Context, - keepers *ConsumeGasKeepers, + evmKeeper anteinterfaces.EVMKeeper, fees sdktypes.Coins, feePayer sdktypes.AccAddress, ) error { @@ -80,13 +72,14 @@ func deductFees( return nil } - if err := keepers.Evm.DeductTxCostsFromUserBalance( + if err := evmKeeper.DeductTxCostsFromUserBalance( ctx, fees, common.BytesToAddress(feePayer), ); err != nil { return errorsmod.Wrapf(err, "failed to deduct transaction costs from user balance") } + return nil } diff --git a/ante/evm/08_gas_consume_test.go b/ante/evm/08_gas_consume_test.go index 7e7657d6..3165de37 100644 --- a/ante/evm/08_gas_consume_test.go +++ b/ante/evm/08_gas_consume_test.go @@ -146,12 +146,6 @@ func (suite *EvmAnteTestSuite) TestConsumeGasAndEmitEvent() { for _, tc := range testCases { suite.Run(tc.name, func() { - keepers := &evmante.ConsumeGasKeepers{ - Bank: unitNetwork.App.BankKeeper, - Distribution: unitNetwork.App.DistrKeeper, - Evm: unitNetwork.App.EVMKeeper, - Staking: unitNetwork.App.StakingKeeper, - } sender := tc.getSender() prevBalance, err := grpcHandler.GetAllBalances( sender, @@ -161,7 +155,7 @@ func (suite *EvmAnteTestSuite) TestConsumeGasAndEmitEvent() { // Function under test err = evmante.ConsumeFeesAndEmitEvent( unitNetwork.GetContext(), - keepers, + unitNetwork.App.EVMKeeper, tc.fees, sender, ) diff --git a/ante/evm/09_increment_sequence.go b/ante/evm/09_increment_sequence.go index 6ced9fad..2943bb6b 100644 --- a/ante/evm/09_increment_sequence.go +++ b/ante/evm/09_increment_sequence.go @@ -7,13 +7,13 @@ import ( errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" errortypes "github.com/cosmos/cosmos-sdk/types/errors" - evmtypes "github.com/evmos/os/x/evm/types" + anteinterfaces "github.com/evmos/os/ante/interfaces" ) // IncrementNonce increments the sequence of the account. func IncrementNonce( ctx sdk.Context, - accountKeeper evmtypes.AccountKeeper, + accountKeeper anteinterfaces.AccountKeeper, account sdk.AccountI, txNonce uint64, ) error { diff --git a/ante/evm/eth_benchmark_test.go b/ante/evm/eth_benchmark_test.go index 176234db..a3a55d68 100644 --- a/ante/evm/eth_benchmark_test.go +++ b/ante/evm/eth_benchmark_test.go @@ -65,12 +65,6 @@ func BenchmarkEthGasConsumeDecorator(b *testing.B) { vmdb = testutil.NewStateDB(cacheCtx, s.GetNetwork().App.EVMKeeper) cacheCtx = s.prepareAccount(cacheCtx, addr.Bytes(), tc.balance, tc.rewards) s.Require().NoError(vmdb.Commit()) - keepers := ethante.ConsumeGasKeepers{ - Bank: s.GetNetwork().App.BankKeeper, - Distribution: s.GetNetwork().App.DistrKeeper, - Evm: s.GetNetwork().App.EVMKeeper, - Staking: s.GetNetwork().App.StakingKeeper, - } baseFee := s.GetNetwork().App.FeeMarketKeeper.GetParams(ctx).BaseFee fee := tx.GetEffectiveFee(baseFee.BigInt()) @@ -83,7 +77,7 @@ func BenchmarkEthGasConsumeDecorator(b *testing.B) { err := ethante.ConsumeFeesAndEmitEvent( cacheCtx.WithIsCheckTx(true).WithGasMeter(storetypes.NewInfiniteGasMeter()), - &keepers, + s.GetNetwork().App.EVMKeeper, fees, bechAddr, ) diff --git a/ante/evm/mono_decorator.go b/ante/evm/mono_decorator.go new file mode 100644 index 00000000..678e0cd1 --- /dev/null +++ b/ante/evm/mono_decorator.go @@ -0,0 +1,240 @@ +// 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" + txtypes "github.com/cosmos/cosmos-sdk/types/tx" + "github.com/ethereum/go-ethereum/common" + ethtypes "github.com/ethereum/go-ethereum/core/types" + anteinterfaces "github.com/evmos/os/ante/interfaces" + evmkeeper "github.com/evmos/os/x/evm/keeper" + evmtypes "github.com/evmos/os/x/evm/types" +) + +// MonoDecorator is a single decorator that handles all the prechecks for +// ethereum transactions. +type MonoDecorator struct { + accountKeeper anteinterfaces.AccountKeeper + feeMarketKeeper anteinterfaces.FeeMarketKeeper + evmKeeper anteinterfaces.EVMKeeper + maxGasWanted uint64 +} + +// NewEVMMonoDecorator creates the 'mono' decorator, that is used to run the ante handle logic +// for EVM transactions on the chain. +// +// This runs all the default checks for EVM transactions enable through evmOS. +// Any partner chains can use this in their ante handler logic and build additional EVM +// decorators using the returned DecoratorUtils +func NewEVMMonoDecorator( + accountKeeper anteinterfaces.AccountKeeper, + feeMarketKeeper anteinterfaces.FeeMarketKeeper, + evmKeeper anteinterfaces.EVMKeeper, + maxGasWanted uint64, +) MonoDecorator { + return MonoDecorator{ + accountKeeper: accountKeeper, + feeMarketKeeper: feeMarketKeeper, + evmKeeper: evmKeeper, + maxGasWanted: maxGasWanted, + } +} + +// AnteHandle handles the entire decorator chain using a mono decorator. +func (md MonoDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { + // 0. Basic validation of the transaction + var txFeeInfo *txtypes.Fee + if !ctx.IsReCheckTx() { + txFeeInfo, err = ValidateTx(tx) + if err != nil { + return ctx, err + } + } + + // 1. setup ctx + ctx, err = SetupContext(ctx, tx, md.evmKeeper) + if err != nil { + return ctx, err + } + + // 2. get utils + decUtils, err := NewMonoDecoratorUtils(ctx, md.evmKeeper, md.feeMarketKeeper) + if err != nil { + return ctx, err + } + + // Use the lowest priority of all the messages as the final one. + for i, msg := range tx.GetMsgs() { + ethMsg, txData, from, err := evmtypes.UnpackEthMsg(msg) + if err != nil { + return ctx, err + } + + feeAmt := txData.Fee() + gas := txData.GetGas() + fee := sdkmath.LegacyNewDecFromBigInt(feeAmt) + gasLimit := sdkmath.LegacyNewDecFromBigInt(new(big.Int).SetUint64(gas)) + + // 2. mempool inclusion fee + if ctx.IsCheckTx() && !simulate { + if err := CheckMempoolFee(fee, decUtils.MempoolMinGasPrice, gasLimit, decUtils.Rules.IsLondon); err != nil { + return ctx, err + } + } + + // 3. min gas price (global min fee) + if txData.TxType() == ethtypes.DynamicFeeTxType && decUtils.BaseFee != nil { + feeAmt = txData.EffectiveFee(decUtils.BaseFee) + fee = sdkmath.LegacyNewDecFromBigInt(feeAmt) + } + + if err := CheckGlobalFee(fee, decUtils.GlobalMinGasPrice, gasLimit); err != nil { + return ctx, err + } + + // 4. validate msg contents + err = ValidateMsg( + decUtils.EvmParams, + txData, + from, + ) + if err != nil { + return ctx, err + } + + // 5. signature verification + if err := SignatureVerification( + ethMsg, + decUtils.Signer, + decUtils.EvmParams.AllowUnprotectedTxs, + ); err != nil { + return ctx, err + } + + // NOTE: sender address has been verified and cached + from = ethMsg.GetFrom() + + // 6. account balance verification + fromAddr := common.HexToAddress(ethMsg.From) + // TODO: Use account from AccountKeeper instead + account := md.evmKeeper.GetAccount(ctx, fromAddr) + if err := VerifyAccountBalance( + ctx, + md.accountKeeper, + account, + fromAddr, + txData, + ); err != nil { + return ctx, err + } + + // 7. can transfer + coreMsg, err := ethMsg.AsMessage(decUtils.Signer, decUtils.BaseFee) + if err != nil { + return ctx, errorsmod.Wrapf( + err, + "failed to create an ethereum core.Message from signer %T", decUtils.Signer, + ) + } + + if err := CanTransfer( + ctx, + md.evmKeeper, + coreMsg, + decUtils.BaseFee, + decUtils.EthConfig, + decUtils.EvmParams, + decUtils.Rules.IsLondon, + ); err != nil { + return ctx, err + } + + // 8. gas consumption + msgFees, err := evmkeeper.VerifyFee( + txData, + decUtils.EvmDenom, + decUtils.BaseFee, + decUtils.Rules.IsHomestead, + decUtils.Rules.IsIstanbul, + ctx.IsCheckTx(), + ) + if err != nil { + return ctx, err + } + + err = ConsumeFeesAndEmitEvent( + ctx, + md.evmKeeper, + msgFees, + from, + ) + if err != nil { + return ctx, err + } + + gasWanted := UpdateCumulativeGasWanted( + ctx, + txData.GetGas(), + md.maxGasWanted, + decUtils.GasWanted, + ) + decUtils.GasWanted = gasWanted + + minPriority := GetMsgPriority( + txData, + decUtils.MinPriority, + decUtils.BaseFee, + ) + decUtils.MinPriority = minPriority + + txFee := UpdateCumulativeTxFee( + decUtils.TxFee, + txData.Fee(), + decUtils.EvmDenom, + ) + decUtils.TxFee = txFee + decUtils.TxGasLimit += gas + + // 9. increment sequence + acc := md.accountKeeper.GetAccount(ctx, from) + if acc == nil { + // safety check: shouldn't happen + return ctx, errorsmod.Wrapf( + errortypes.ErrUnknownAddress, + "account %s does not exist", + from, + ) + } + + if err := IncrementNonce(ctx, md.accountKeeper, acc, txData.GetNonce()); err != nil { + return ctx, err + } + + // 10. gas wanted + if err := CheckGasWanted(ctx, md.feeMarketKeeper, tx, decUtils.Rules.IsLondon); err != nil { + return ctx, err + } + + // 11. emit events + txIdx := uint64(i) //nolint:gosec // G115 + EmitTxHashEvent(ctx, ethMsg, decUtils.BlockTxIndex, txIdx) + } + + if err := CheckTxFee(txFeeInfo, decUtils.TxFee, decUtils.TxGasLimit); err != nil { + return ctx, err + } + + ctx, err = CheckBlockGasLimit(ctx, decUtils.GasWanted, decUtils.MinPriority) + if err != nil { + return ctx, err + } + + return next(ctx, tx, simulate) +} diff --git a/ante/interfaces/cosmos.go b/ante/interfaces/cosmos.go index 743a30dd..55de20df 100644 --- a/ante/interfaces/cosmos.go +++ b/ante/interfaces/cosmos.go @@ -8,25 +8,23 @@ import ( addresscodec "cosmossdk.io/core/address" sdk "github.com/cosmos/cosmos-sdk/types" - stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" ) -// BankKeeper defines the exposed interface for using functionality of the bank keeper -// in the context of the AnteHandler utils package. -type BankKeeper interface { - GetBalance(ctx context.Context, addr sdk.AccAddress, denom string) sdk.Coin -} - -// DistributionKeeper defines the exposed interface for using functionality of the distribution -// keeper in the context of the AnteHandler utils package. -type DistributionKeeper interface { - WithdrawDelegationRewards(ctx context.Context, delAddr sdk.AccAddress, valAddr sdk.ValAddress) (sdk.Coins, error) +type AccountKeeper interface { + NewAccountWithAddress(ctx context.Context, addr sdk.AccAddress) sdk.AccountI + GetModuleAddress(moduleName string) sdk.AccAddress + GetAccount(ctx context.Context, addr sdk.AccAddress) sdk.AccountI + SetAccount(ctx context.Context, account sdk.AccountI) + RemoveAccount(ctx context.Context, account sdk.AccountI) + GetParams(ctx context.Context) (params authtypes.Params) + GetSequence(ctx context.Context, addr sdk.AccAddress) (uint64, error) + AddressCodec() addresscodec.Codec } -// StakingKeeper defines the exposed interface for using functionality of the staking keeper -// in the context of the AnteHandler utils package. -type StakingKeeper interface { - BondDenom(ctx context.Context) (string, error) - ValidatorAddressCodec() addresscodec.Codec - IterateDelegations(ctx context.Context, delegator sdk.AccAddress, fn func(index int64, delegation stakingtypes.DelegationI) (stop bool)) error +type BankKeeper interface { + GetBalance(ctx context.Context, addr sdk.AccAddress, denom string) sdk.Coin + IsSendEnabledCoins(ctx context.Context, coins ...sdk.Coin) error + SendCoins(ctx context.Context, from, to sdk.AccAddress, amt sdk.Coins) error + SendCoinsFromAccountToModule(ctx context.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error } diff --git a/ante/testutils/testutil.go b/ante/testutils/testutil.go index 6d4d2b74..43a9ff9c 100644 --- a/ante/testutils/testutil.go +++ b/ante/testutils/testutil.go @@ -98,23 +98,22 @@ func (suite *AnteTestSuite) SetupTest() { suite.Require().NotNil(suite.network.App.AppCodec()) - anteHandler := chainante.NewAnteHandler(chainante.HandlerOptions{ + options := chainante.HandlerOptions{ Cdc: suite.network.App.AppCodec(), AccountKeeper: suite.network.App.AccountKeeper, BankKeeper: suite.network.App.BankKeeper, - DistributionKeeper: suite.network.App.DistrKeeper, EvmKeeper: suite.network.App.EVMKeeper, FeegrantKeeper: suite.network.App.FeeGrantKeeper, IBCKeeper: suite.network.App.IBCKeeper, - StakingKeeper: suite.network.App.StakingKeeper, FeeMarketKeeper: suite.network.App.FeeMarketKeeper, SignModeHandler: encodingConfig.TxConfig.SignModeHandler(), SigGasConsumer: ante.SigVerificationGasConsumer, ExtensionOptionChecker: types.HasDynamicFeeExtensionOption, TxFeeChecker: evmante.NewDynamicFeeChecker(suite.network.App.EVMKeeper), - }) + } + suite.Require().NoError(options.Validate(), "invalid ante handler options") - suite.anteHandler = anteHandler + suite.anteHandler = chainante.NewAnteHandler(options) } func (suite *AnteTestSuite) WithFeemarketEnabled(enabled bool) { diff --git a/example_chain/ante/cosmos_handler.go b/example_chain/ante/cosmos_handler.go index 1ca92b4c..0f934754 100644 --- a/example_chain/ante/cosmos_handler.go +++ b/example_chain/ante/cosmos_handler.go @@ -16,7 +16,7 @@ import ( // newCosmosAnteHandler creates the default ante handler for Cosmos transactions func newCosmosAnteHandler(options HandlerOptions) sdk.AnteHandler { return sdk.ChainAnteDecorators( - evmoscosmosante.RejectMessagesDecorator{}, // reject MsgEthereumTxs + evmoscosmosante.NewRejectMessagesDecorator(), // reject MsgEthereumTxs evmoscosmosante.NewAuthzLimiterDecorator( // disable the Msg types that cannot be included on an authz.MsgExec msgs field sdk.MsgTypeURL(&evmtypes.MsgEthereumTx{}), sdk.MsgTypeURL(&sdkvesting.MsgCreateVestingAccount{}), diff --git a/example_chain/ante/evm_benchmark_test.go b/example_chain/ante/evm_benchmark_test.go index 43a52cf8..8fe5b0a0 100644 --- a/example_chain/ante/evm_benchmark_test.go +++ b/example_chain/ante/evm_benchmark_test.go @@ -146,9 +146,7 @@ func (s *benchmarkSuite) generateHandlerOptions() chainante.HandlerOptions { BankKeeper: s.network.App.BankKeeper, ExtensionOptionChecker: evmostypes.HasDynamicFeeExtensionOption, EvmKeeper: s.network.App.EVMKeeper, - StakingKeeper: s.network.App.StakingKeeper, FeegrantKeeper: s.network.App.FeeGrantKeeper, - DistributionKeeper: s.network.App.DistrKeeper, IBCKeeper: s.network.App.IBCKeeper, FeeMarketKeeper: s.network.App.FeeMarketKeeper, SignModeHandler: encCfg.TxConfig.SignModeHandler(), diff --git a/example_chain/ante/evm_handler.go b/example_chain/ante/evm_handler.go index 2c95a8b1..fdb7e54c 100644 --- a/example_chain/ante/evm_handler.go +++ b/example_chain/ante/evm_handler.go @@ -4,262 +4,18 @@ package ante 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" - txtypes "github.com/cosmos/cosmos-sdk/types/tx" - "github.com/ethereum/go-ethereum/common" - ethtypes "github.com/ethereum/go-ethereum/core/types" evmante "github.com/evmos/os/ante/evm" - anteinterfaces "github.com/evmos/os/ante/interfaces" - evmkeeper "github.com/evmos/os/x/evm/keeper" - evmtypes "github.com/evmos/os/x/evm/types" ) // newMonoEVMAnteHandler creates the sdk.AnteHandler implementation for the EVM transactions. func newMonoEVMAnteHandler(options HandlerOptions) sdk.AnteHandler { return sdk.ChainAnteDecorators( - NewEVMMonoDecorator( + evmante.NewEVMMonoDecorator( options.AccountKeeper, - options.BankKeeper, options.FeeMarketKeeper, options.EvmKeeper, - options.DistributionKeeper, - options.StakingKeeper, options.MaxTxGasWanted, ), ) } - -// MonoDecorator is a single decorator that handles all the prechecks for -// ethereum transactions. -type MonoDecorator struct { - accountKeeper evmtypes.AccountKeeper - bankKeeper evmtypes.BankKeeper - feeMarketKeeper anteinterfaces.FeeMarketKeeper - evmKeeper anteinterfaces.EVMKeeper - distributionKeeper anteinterfaces.DistributionKeeper - stakingKeeper anteinterfaces.StakingKeeper - maxGasWanted uint64 -} - -// NewEVMMonoDecorator creates the 'mono' decorator, that is used to run the ante handle logic -// for EVM transactions on the chain. -// -// TODO: make partners not have to duplicate this in their repo -func NewEVMMonoDecorator( - accountKeeper evmtypes.AccountKeeper, - bankKeeper evmtypes.BankKeeper, - feeMarketKeeper anteinterfaces.FeeMarketKeeper, - evmKeeper anteinterfaces.EVMKeeper, - distributionKeeper anteinterfaces.DistributionKeeper, - stakingKeeper anteinterfaces.StakingKeeper, - maxGasWanted uint64, -) MonoDecorator { - return MonoDecorator{ - accountKeeper: accountKeeper, - bankKeeper: bankKeeper, - feeMarketKeeper: feeMarketKeeper, - evmKeeper: evmKeeper, - distributionKeeper: distributionKeeper, - stakingKeeper: stakingKeeper, - maxGasWanted: maxGasWanted, - } -} - -// AnteHandle handles the entire decorator chain using a mono decorator. -func (md MonoDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { - // 0. Basic validation of the transaction - var txFeeInfo *txtypes.Fee - if !ctx.IsReCheckTx() { - txFeeInfo, err = evmante.ValidateTx(tx) - if err != nil { - return ctx, err - } - } - - // 1. setup ctx - ctx, err = evmante.SetupContext(ctx, tx, md.evmKeeper) - if err != nil { - return ctx, err - } - - // 2. get utils - decUtils, err := evmante.NewMonoDecoratorUtils(ctx, md.evmKeeper, md.feeMarketKeeper) - if err != nil { - return ctx, err - } - - // Use the lowest priority of all the messages as the final one. - for i, msg := range tx.GetMsgs() { - ethMsg, txData, from, err := evmtypes.UnpackEthMsg(msg) - if err != nil { - return ctx, err - } - - feeAmt := txData.Fee() - gas := txData.GetGas() - fee := sdkmath.LegacyNewDecFromBigInt(feeAmt) - gasLimit := sdkmath.LegacyNewDecFromBigInt(new(big.Int).SetUint64(gas)) - - // 2. mempool inclusion fee - if ctx.IsCheckTx() && !simulate { - if err := evmante.CheckMempoolFee(fee, decUtils.MempoolMinGasPrice, gasLimit, decUtils.Rules.IsLondon); err != nil { - return ctx, err - } - } - - // 3. min gas price (global min fee) - if txData.TxType() == ethtypes.DynamicFeeTxType && decUtils.BaseFee != nil { - feeAmt = txData.EffectiveFee(decUtils.BaseFee) - fee = sdkmath.LegacyNewDecFromBigInt(feeAmt) - } - - if err := evmante.CheckGlobalFee(fee, decUtils.GlobalMinGasPrice, gasLimit); err != nil { - return ctx, err - } - - // 4. validate msg contents - err = evmante.ValidateMsg( - decUtils.EvmParams, - txData, - from, - ) - if err != nil { - return ctx, err - } - - // 5. signature verification - if err := evmante.SignatureVerification( - ethMsg, - decUtils.Signer, - decUtils.EvmParams.AllowUnprotectedTxs, - ); err != nil { - return ctx, err - } - - // NOTE: sender address has been verified and cached - from = ethMsg.GetFrom() - - // 6. account balance verification - fromAddr := common.HexToAddress(ethMsg.From) - // TODO: Use account from AccountKeeper instead - account := md.evmKeeper.GetAccount(ctx, fromAddr) - if err := evmante.VerifyAccountBalance( - ctx, - md.accountKeeper, - account, - fromAddr, - txData, - ); err != nil { - return ctx, err - } - - // 7. can transfer - coreMsg, err := ethMsg.AsMessage(decUtils.Signer, decUtils.BaseFee) - if err != nil { - return ctx, errorsmod.Wrapf( - err, - "failed to create an ethereum core.Message from signer %T", decUtils.Signer, - ) - } - - if err := evmante.CanTransfer( - ctx, - md.evmKeeper, - coreMsg, - decUtils.BaseFee, - decUtils.EthConfig, - decUtils.EvmParams, - decUtils.Rules.IsLondon, - ); err != nil { - return ctx, err - } - - // 8. gas consumption - msgFees, err := evmkeeper.VerifyFee( - txData, - decUtils.EvmDenom, - decUtils.BaseFee, - decUtils.Rules.IsHomestead, - decUtils.Rules.IsIstanbul, - ctx.IsCheckTx(), - ) - if err != nil { - return ctx, err - } - - err = evmante.ConsumeFeesAndEmitEvent( - ctx, - &evmante.ConsumeGasKeepers{ - Bank: md.bankKeeper, - Distribution: md.distributionKeeper, - Evm: md.evmKeeper, - Staking: md.stakingKeeper, - }, - msgFees, - from, - ) - if err != nil { - return ctx, err - } - - gasWanted := evmante.UpdateCumulativeGasWanted( - ctx, - txData.GetGas(), - md.maxGasWanted, - decUtils.GasWanted, - ) - decUtils.GasWanted = gasWanted - - minPriority := evmante.GetMsgPriority( - txData, - decUtils.MinPriority, - decUtils.BaseFee, - ) - decUtils.MinPriority = minPriority - - txFee := evmante.UpdateCumulativeTxFee( - decUtils.TxFee, - txData.Fee(), - decUtils.EvmDenom, - ) - decUtils.TxFee = txFee - decUtils.TxGasLimit += gas - - // 10. increment sequence - acc := md.accountKeeper.GetAccount(ctx, from) - if acc == nil { - // safety check: shouldn't happen - return ctx, errorsmod.Wrapf(errortypes.ErrUnknownAddress, - "account %s does not exist", acc) - } - - if err := evmante.IncrementNonce(ctx, md.accountKeeper, acc, txData.GetNonce()); err != nil { - return ctx, err - } - - // 11. gas wanted - if err := evmante.CheckGasWanted(ctx, md.feeMarketKeeper, tx, decUtils.Rules.IsLondon); err != nil { - return ctx, err - } - - // 12. emit events - txIdx := uint64(i) //nolint:gosec // G115 - evmante.EmitTxHashEvent(ctx, ethMsg, decUtils.BlockTxIndex, txIdx) - } - - if err := evmante.CheckTxFee(txFeeInfo, decUtils.TxFee, decUtils.TxGasLimit); err != nil { - return ctx, err - } - - ctx, err = evmante.CheckBlockGasLimit(ctx, decUtils.GasWanted, decUtils.MinPriority) - if err != nil { - return ctx, err - } - - return next(ctx, tx, simulate) -} diff --git a/example_chain/ante/handler_options.go b/example_chain/ante/handler_options.go index 9478d6cd..8dcaa065 100644 --- a/example_chain/ante/handler_options.go +++ b/example_chain/ante/handler_options.go @@ -14,18 +14,15 @@ import ( authtypes "github.com/cosmos/cosmos-sdk/x/auth/types" ibckeeper "github.com/cosmos/ibc-go/v8/modules/core/keeper" anteinterfaces "github.com/evmos/os/ante/interfaces" - evmtypes "github.com/evmos/os/x/evm/types" ) // HandlerOptions defines the list of module keepers required to run the Evmos // AnteHandler decorators. type HandlerOptions struct { Cdc codec.BinaryCodec - AccountKeeper evmtypes.AccountKeeper - BankKeeper evmtypes.BankKeeper - DistributionKeeper anteinterfaces.DistributionKeeper + AccountKeeper anteinterfaces.AccountKeeper + BankKeeper anteinterfaces.BankKeeper IBCKeeper *ibckeeper.Keeper - StakingKeeper anteinterfaces.StakingKeeper FeeMarketKeeper anteinterfaces.FeeMarketKeeper EvmKeeper anteinterfaces.EVMKeeper FeegrantKeeper ante.FeegrantKeeper @@ -47,9 +44,6 @@ func (options HandlerOptions) Validate() error { if options.BankKeeper == nil { return errorsmod.Wrap(errortypes.ErrLogic, "bank keeper is required for AnteHandler") } - if options.StakingKeeper == nil { - return errorsmod.Wrap(errortypes.ErrLogic, "staking keeper is required for AnteHandler") - } if options.IBCKeeper == nil { return errorsmod.Wrap(errortypes.ErrLogic, "ibc keeper is required for AnteHandler") } @@ -65,9 +59,6 @@ func (options HandlerOptions) Validate() error { if options.SignModeHandler == nil { return errorsmod.Wrap(errortypes.ErrLogic, "sign mode handler is required for AnteHandler") } - if options.DistributionKeeper == nil { - return errorsmod.Wrap(errortypes.ErrLogic, "distribution keeper is required for AnteHandler") - } if options.TxFeeChecker == nil { return errorsmod.Wrap(errortypes.ErrLogic, "tx fee checker is required for AnteHandler") } diff --git a/example_chain/ante/handler_options_test.go b/example_chain/ante/handler_options_test.go index 5c100276..a0f7f5ae 100644 --- a/example_chain/ante/handler_options_test.go +++ b/example_chain/ante/handler_options_test.go @@ -40,53 +40,23 @@ func TestValidateHandlerOptions(t *testing.T) { }, false, }, - { - "fail - empty distribution keeper", - chainante.HandlerOptions{ - Cdc: nw.App.AppCodec(), - AccountKeeper: nw.App.AccountKeeper, - BankKeeper: nw.App.BankKeeper, - DistributionKeeper: nil, - - IBCKeeper: nil, - }, - false, - }, { "fail - empty IBC keeper", chainante.HandlerOptions{ - Cdc: nw.App.AppCodec(), - AccountKeeper: nw.App.AccountKeeper, - BankKeeper: nw.App.BankKeeper, - DistributionKeeper: nw.App.DistrKeeper, - - IBCKeeper: nil, - }, - false, - }, - { - "fail - empty staking keeper", - chainante.HandlerOptions{ - Cdc: nw.App.AppCodec(), - AccountKeeper: nw.App.AccountKeeper, - BankKeeper: nw.App.BankKeeper, - DistributionKeeper: nw.App.DistrKeeper, - - IBCKeeper: nw.App.IBCKeeper, - StakingKeeper: nil, + Cdc: nw.App.AppCodec(), + AccountKeeper: nw.App.AccountKeeper, + BankKeeper: nw.App.BankKeeper, + IBCKeeper: nil, }, false, }, { "fail - empty fee market keeper", chainante.HandlerOptions{ - Cdc: nw.App.AppCodec(), - AccountKeeper: nw.App.AccountKeeper, - BankKeeper: nw.App.BankKeeper, - DistributionKeeper: nw.App.DistrKeeper, - + Cdc: nw.App.AppCodec(), + AccountKeeper: nw.App.AccountKeeper, + BankKeeper: nw.App.BankKeeper, IBCKeeper: nw.App.IBCKeeper, - StakingKeeper: nw.App.StakingKeeper, FeeMarketKeeper: nil, }, false, @@ -94,62 +64,54 @@ func TestValidateHandlerOptions(t *testing.T) { { "fail - empty EVM keeper", chainante.HandlerOptions{ - Cdc: nw.App.AppCodec(), - AccountKeeper: nw.App.AccountKeeper, - BankKeeper: nw.App.BankKeeper, - DistributionKeeper: nw.App.DistrKeeper, - IBCKeeper: nw.App.IBCKeeper, - StakingKeeper: nw.App.StakingKeeper, - FeeMarketKeeper: nw.App.FeeMarketKeeper, - EvmKeeper: nil, + Cdc: nw.App.AppCodec(), + AccountKeeper: nw.App.AccountKeeper, + BankKeeper: nw.App.BankKeeper, + IBCKeeper: nw.App.IBCKeeper, + FeeMarketKeeper: nw.App.FeeMarketKeeper, + EvmKeeper: nil, }, false, }, { "fail - empty signature gas consumer", chainante.HandlerOptions{ - Cdc: nw.App.AppCodec(), - AccountKeeper: nw.App.AccountKeeper, - BankKeeper: nw.App.BankKeeper, - DistributionKeeper: nw.App.DistrKeeper, - IBCKeeper: nw.App.IBCKeeper, - StakingKeeper: nw.App.StakingKeeper, - FeeMarketKeeper: nw.App.FeeMarketKeeper, - EvmKeeper: nw.App.EVMKeeper, - SigGasConsumer: nil, + Cdc: nw.App.AppCodec(), + AccountKeeper: nw.App.AccountKeeper, + BankKeeper: nw.App.BankKeeper, + IBCKeeper: nw.App.IBCKeeper, + FeeMarketKeeper: nw.App.FeeMarketKeeper, + EvmKeeper: nw.App.EVMKeeper, + SigGasConsumer: nil, }, false, }, { "fail - empty signature mode handler", chainante.HandlerOptions{ - Cdc: nw.App.AppCodec(), - AccountKeeper: nw.App.AccountKeeper, - BankKeeper: nw.App.BankKeeper, - DistributionKeeper: nw.App.DistrKeeper, - IBCKeeper: nw.App.IBCKeeper, - StakingKeeper: nw.App.StakingKeeper, - FeeMarketKeeper: nw.App.FeeMarketKeeper, - EvmKeeper: nw.App.EVMKeeper, - SigGasConsumer: ante.SigVerificationGasConsumer, - SignModeHandler: nil, + Cdc: nw.App.AppCodec(), + AccountKeeper: nw.App.AccountKeeper, + BankKeeper: nw.App.BankKeeper, + IBCKeeper: nw.App.IBCKeeper, + FeeMarketKeeper: nw.App.FeeMarketKeeper, + EvmKeeper: nw.App.EVMKeeper, + SigGasConsumer: ante.SigVerificationGasConsumer, + SignModeHandler: nil, }, false, }, { "fail - empty tx fee checker", chainante.HandlerOptions{ - Cdc: nw.App.AppCodec(), - AccountKeeper: nw.App.AccountKeeper, - BankKeeper: nw.App.BankKeeper, - DistributionKeeper: nw.App.DistrKeeper, - IBCKeeper: nw.App.IBCKeeper, - StakingKeeper: nw.App.StakingKeeper, - FeeMarketKeeper: nw.App.FeeMarketKeeper, - EvmKeeper: nw.App.EVMKeeper, - SigGasConsumer: ante.SigVerificationGasConsumer, - SignModeHandler: nw.App.GetTxConfig().SignModeHandler(), - TxFeeChecker: nil, + Cdc: nw.App.AppCodec(), + AccountKeeper: nw.App.AccountKeeper, + BankKeeper: nw.App.BankKeeper, + IBCKeeper: nw.App.IBCKeeper, + FeeMarketKeeper: nw.App.FeeMarketKeeper, + EvmKeeper: nw.App.EVMKeeper, + SigGasConsumer: ante.SigVerificationGasConsumer, + SignModeHandler: nw.App.GetTxConfig().SignModeHandler(), + TxFeeChecker: nil, }, false, }, @@ -159,10 +121,8 @@ func TestValidateHandlerOptions(t *testing.T) { Cdc: nw.App.AppCodec(), AccountKeeper: nw.App.AccountKeeper, BankKeeper: nw.App.BankKeeper, - DistributionKeeper: nw.App.DistrKeeper, ExtensionOptionChecker: types.HasDynamicFeeExtensionOption, EvmKeeper: nw.App.EVMKeeper, - StakingKeeper: nw.App.StakingKeeper, FeegrantKeeper: nw.App.FeeGrantKeeper, IBCKeeper: nw.App.IBCKeeper, FeeMarketKeeper: nw.App.FeeMarketKeeper, diff --git a/example_chain/app.go b/example_chain/app.go index 6e61c9e7..0719ef22 100644 --- a/example_chain/app.go +++ b/example_chain/app.go @@ -795,9 +795,7 @@ func (app *ExampleChain) setAnteHandler(txConfig client.TxConfig, maxGasWanted u BankKeeper: app.BankKeeper, ExtensionOptionChecker: evmostypes.HasDynamicFeeExtensionOption, EvmKeeper: app.EVMKeeper, - StakingKeeper: app.StakingKeeper, FeegrantKeeper: app.FeeGrantKeeper, - DistributionKeeper: app.DistrKeeper, IBCKeeper: app.IBCKeeper, FeeMarketKeeper: app.FeeMarketKeeper, SignModeHandler: txConfig.SignModeHandler(), diff --git a/x/evm/keeper/keeper.go b/x/evm/keeper/keeper.go index 07097d4d..ecc905f6 100644 --- a/x/evm/keeper/keeper.go +++ b/x/evm/keeper/keeper.go @@ -4,6 +4,7 @@ package keeper import ( + "fmt" "math/big" errorsmod "cosmossdk.io/errors" @@ -119,7 +120,7 @@ func (k *Keeper) WithChainID(ctx sdk.Context) { } if k.eip155ChainID != nil && k.eip155ChainID.Cmp(chainID) != 0 { - panic("chain id already set") + panic(fmt.Sprintf("eip-155 chain id already set (%s); cannot change to %s", k.eip155ChainID, chainID)) } k.eip155ChainID = chainID diff --git a/x/evm/types/interfaces.go b/x/evm/types/interfaces.go index b5cf906a..a247cf64 100644 --- a/x/evm/types/interfaces.go +++ b/x/evm/types/interfaces.go @@ -20,7 +20,6 @@ import ( type AccountKeeper interface { NewAccountWithAddress(ctx context.Context, addr sdk.AccAddress) sdk.AccountI GetModuleAddress(moduleName string) sdk.AccAddress - IterateAccounts(ctx context.Context, cb func(account sdk.AccountI) bool) GetAccount(ctx context.Context, addr sdk.AccAddress) sdk.AccountI SetAccount(ctx context.Context, account sdk.AccountI) RemoveAccount(ctx context.Context, account sdk.AccountI)