Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

imp(ante): Refactor ante handlers to be easier to use in partner chains (target main) #52

Merged
merged 11 commits into from
Oct 2, 2024
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ This changelog was created using the `clu` binary

### Improvements

- (ante) [#51](https://github.com/evmos/os/pull/51) Refactor ante handlers to be easier to use in partner chains.
MalteHerrmann marked this conversation as resolved.
Show resolved Hide resolved
- (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.
Expand Down
7 changes: 6 additions & 1 deletion ante/cosmos/reject_msgs.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
3 changes: 2 additions & 1 deletion ante/evm/06_account_verification.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -21,7 +22,7 @@ import (
// - account balance is lower than the transaction cost
func VerifyAccountBalance(
ctx sdk.Context,
accountKeeper evmtypes.AccountKeeper,
accountKeeper anteinterfaces.AccountKeeper,
MalteHerrmann marked this conversation as resolved.
Show resolved Hide resolved
account *statedb.Account,
from common.Address,
txData evmtypes.TxData,
Expand Down
16 changes: 5 additions & 11 deletions ante/evm/08_gas_consume.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -72,21 +65,22 @@ func ConsumeFeesAndEmitEvent(
// If the spendable balance is not enough, it tries to claim enough staking rewards to cover the fees.
MalteHerrmann marked this conversation as resolved.
Show resolved Hide resolved
func deductFees(
ctx sdktypes.Context,
keepers *ConsumeGasKeepers,
evmKeeper anteinterfaces.EVMKeeper,
fees sdktypes.Coins,
feePayer sdktypes.AccAddress,
) error {
if fees.IsZero() {
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
}

Expand Down
8 changes: 1 addition & 7 deletions ante/evm/08_gas_consume_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -161,7 +155,7 @@ func (suite *EvmAnteTestSuite) TestConsumeGasAndEmitEvent() {
// Function under test
err = evmante.ConsumeFeesAndEmitEvent(
unitNetwork.GetContext(),
keepers,
unitNetwork.App.EVMKeeper,
tc.fees,
sender,
)
Expand Down
4 changes: 2 additions & 2 deletions ante/evm/09_increment_sequence.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
8 changes: 1 addition & 7 deletions ante/evm/eth_benchmark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand All @@ -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,
)
Expand Down
237 changes: 237 additions & 0 deletions ante/evm/mono_decorator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
// 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

// 10. increment sequence
MalteHerrmann marked this conversation as resolved.
Show resolved Hide resolved
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)
MalteHerrmann marked this conversation as resolved.
Show resolved Hide resolved
}

if err := IncrementNonce(ctx, md.accountKeeper, acc, txData.GetNonce()); err != nil {
return ctx, err
}

// 11. gas wanted
if err := CheckGasWanted(ctx, md.feeMarketKeeper, tx, decUtils.Rules.IsLondon); err != nil {
return ctx, err
}

// 12. 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)
}
Loading
Loading