Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion ante/ante.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/cosmos/cosmos-sdk/types/tx/signing"
"github.com/cosmos/cosmos-sdk/x/auth/ante"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
feesponsorkeeper "github.com/cosmos/evm/x/feesponsor/keeper"
)

// HandlerOptions defines the list of module keepers required to run the Cosmos EVM
Expand All @@ -25,7 +26,8 @@ type HandlerOptions struct {
IBCKeeper *ibckeeper.Keeper
FeeMarketKeeper anteinterfaces.FeeMarketKeeper
EvmKeeper anteinterfaces.EVMKeeper
FeegrantKeeper ante.FeegrantKeeper
FeegrantKeeper anteinterfaces.FeegrantKeeper
FeesponsorKeeper feesponsorkeeper.Keeper
ExtensionOptionChecker ante.ExtensionOptionChecker
SignModeHandler *txsigning.HandlerMap
SigGasConsumer func(meter storetypes.GasMeter, sig signing.SignatureV2, params authtypes.Params) error
Expand Down
2 changes: 2 additions & 0 deletions ante/evm.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ func newMonoEVMAnteHandler(ctx sdk.Context, options HandlerOptions) sdk.AnteHand
options.AccountKeeper,
options.FeeMarketKeeper,
options.EvmKeeper,
options.FeegrantKeeper,
options.FeesponsorKeeper,
options.MaxTxGasWanted,
&evmParams,
&feemarketParams,
Expand Down
31 changes: 23 additions & 8 deletions ante/evm/06_account_verification.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package evm

import (
"math/big"

"github.com/ethereum/go-ethereum/common"
ethtypes "github.com/ethereum/go-ethereum/core/types"

Expand All @@ -15,26 +17,25 @@ import (
errortypes "github.com/cosmos/cosmos-sdk/types/errors"
)

// VerifyAccountBalance checks that the account balance is greater than the total transaction cost.
// VerifyAccount checks that the account is valid and is an EOA (Externally Owned Account).
// The account will be set to store if it doesn't exist, i.e. cannot be found on store.
// This method will fail if:
// - from address is NOT an EOA
// - account balance is lower than the transaction cost
func VerifyAccountBalance(
// - from address is NOT an EOA (unless it's an EIP-7702 delegated account)
// Returns the account (either the input account or newly created empty account).
func VerifyAccount(
ctx sdk.Context,
evmKeeper anteinterfaces.EVMKeeper,
accountKeeper anteinterfaces.AccountKeeper,
account *statedb.Account,
from common.Address,
ethTx *ethtypes.Transaction,
) error {
) (*statedb.Account, error) {
// Only EOA are allowed to send transactions.
if account != nil && account.HasCodeHash() {
// check eip-7702
code := evmKeeper.GetCode(ctx, common.BytesToHash(account.CodeHash))
_, delegated := ethtypes.ParseDelegation(code)
if len(code) > 0 && !delegated {
return errorsmod.Wrapf(
return nil, errorsmod.Wrapf(
errortypes.ErrInvalidType,
"the sender is not EOA: address %s", from,
)
Expand All @@ -46,7 +47,21 @@ func VerifyAccountBalance(
account = statedb.NewEmptyAccount()
}

if err := keeper.CheckSenderBalance(sdkmath.NewIntFromBigInt(account.Balance.ToBig()), ethTx); err != nil {
return account, nil
}

// VerifyAccountBalance checks that the account balance is greater than the total transaction cost.
// This method will fail if:
// - account balance is lower than the transaction cost
func VerifyAccountBalance(
account *statedb.Account,
cost *big.Int,
) error {
if account == nil {
return errorsmod.Wrap(errortypes.ErrInvalidAddress, "account not found")
}

if err := keeper.CheckSenderBalance(sdkmath.NewIntFromBigInt(account.Balance.ToBig()), cost); err != nil {
return errorsmod.Wrap(err, "failed to check sender balance")
}

Expand Down
155 changes: 119 additions & 36 deletions ante/evm/mono_decorator.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,11 @@ import (
errorsmod "cosmossdk.io/errors"
sdkmath "cosmossdk.io/math"

"cosmossdk.io/x/feegrant"
sdk "github.com/cosmos/cosmos-sdk/types"
errortypes "github.com/cosmos/cosmos-sdk/types/errors"
txtypes "github.com/cosmos/cosmos-sdk/types/tx"
feesponsorkeeper "github.com/cosmos/evm/x/feesponsor/keeper"
)

const AcceptedTxType = 0 |
Expand All @@ -30,12 +32,14 @@ const AcceptedTxType = 0 |
// 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
evmParams *evmtypes.Params
feemarketParams *feemarkettypes.Params
accountKeeper anteinterfaces.AccountKeeper
feeMarketKeeper anteinterfaces.FeeMarketKeeper
evmKeeper anteinterfaces.EVMKeeper
maxGasWanted uint64
evmParams *evmtypes.Params
feemarketParams *feemarkettypes.Params
feegrantKeeper anteinterfaces.FeegrantKeeper
feesponsorKeeper feesponsorkeeper.Keeper
}

// NewEVMMonoDecorator creates the 'mono' decorator, that is used to run the ante handle logic
Expand All @@ -48,17 +52,21 @@ func NewEVMMonoDecorator(
accountKeeper anteinterfaces.AccountKeeper,
feeMarketKeeper anteinterfaces.FeeMarketKeeper,
evmKeeper anteinterfaces.EVMKeeper,
feegrantKeeper anteinterfaces.FeegrantKeeper,
feesponsorKeeper feesponsorkeeper.Keeper,
maxGasWanted uint64,
evmParams *evmtypes.Params,
feemarketParams *feemarkettypes.Params,
) MonoDecorator {
return MonoDecorator{
accountKeeper: accountKeeper,
feeMarketKeeper: feeMarketKeeper,
evmKeeper: evmKeeper,
maxGasWanted: maxGasWanted,
evmParams: evmParams,
feemarketParams: feemarketParams,
accountKeeper: accountKeeper,
feeMarketKeeper: feeMarketKeeper,
evmKeeper: evmKeeper,
feegrantKeeper: feegrantKeeper,
feesponsorKeeper: feesponsorKeeper,
maxGasWanted: maxGasWanted,
evmParams: evmParams,
feemarketParams: feemarketParams,
}
}

Expand Down Expand Up @@ -172,38 +180,27 @@ func (md MonoDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, ne
}

from := ethMsg.GetFrom()
fromAddr := common.BytesToAddress(from)
haveSponsor := false
feePayer := common.BytesToAddress(from)
globalFeePayer, found := md.feesponsorKeeper.GetFeePayer(ctx)

// 6. account balance verification
// We get the account with the balance from the EVM keeper because it is
// using a wrapper of the bank keeper as a dependency to scale all
// balances to 18 decimals.
account := md.evmKeeper.GetAccount(ctx, fromAddr)
if err := VerifyAccountBalance(
// 6. account verification and balance check

// Verify account is valid EOA
account := md.evmKeeper.GetAccount(ctx, common.BytesToAddress(from))
account, err = VerifyAccount(
ctx,
md.evmKeeper,
md.accountKeeper,
account,
fromAddr,
ethTx,
); err != nil {
return ctx, err
}

// 7. can transfer
coreMsg := ethMsg.AsMessage(decUtils.BaseFee)
if err := CanTransfer(
ctx,
md.evmKeeper,
*coreMsg,
decUtils.BaseFee,
decUtils.EvmParams,
decUtils.Rules.IsLondon,
); err != nil {
common.BytesToAddress(from),
)
if err != nil {
return ctx, err
}

// 8. gas consumption
// VerifyFee early for fee sponsor check
msgFees, err := evmkeeper.VerifyFee(
ethTx,
evmDenom,
Expand All @@ -217,11 +214,97 @@ func (md MonoDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, ne
return ctx, err
}

// We get the account with the balance from the EVM keeper because it is
// using a wrapper of the bank keeper as a dependency to scale all
// balances to 18 decimals.

// Check if we found a global fee payer and the account exists
if found {
grant, err := md.feegrantKeeper.Allowance(ctx, &feegrant.QueryAllowanceRequest{
Granter: sdk.AccAddress(globalFeePayer).String(),
Grantee: from.String(),
})

// If have grant, check global fee payer can cover tx fee
// (not contains tx value)
if grant != nil && err == nil {
account := md.evmKeeper.GetAccount(ctx, common.BytesToAddress(globalFeePayer))

feeAmount := big.NewInt(0)
if len(msgFees) != 0 {
feeAmount = msgFees[0].Amount.BigInt()
}

// Verify global fee account balance with tx fees
err = VerifyAccountBalance(
account,
feeAmount,
)
if err != nil {
haveSponsor = false
ctx.Logger().Info("fee sponsor fallback: insufficient sponsor balance",
"sponsor", sdk.AccAddress(globalFeePayer).String(),
"sender", from.String(),
)
} else {
err = md.feegrantKeeper.UseGrantedFees(
ctx,
globalFeePayer,
from,
msgFees,
msgs,
)
// If grant is not enough or expired, deduct fee from tx sender
if err != nil {
haveSponsor = false
ctx.Logger().Info("fee sponsor fallback: grant use failed",
"sponsor", sdk.AccAddress(globalFeePayer).String(),
"sender", from.String(),
"error", err.Error(),
)
} else {
haveSponsor = true
feePayer = common.BytesToAddress(globalFeePayer)
ctx.Logger().Info("fee sponsor: sponsor pays fee",
"sponsor", sdk.AccAddress(globalFeePayer).String(),
"sender", from.String(),
)
}
}
}
}

// If no fee sponsor, verify sender has enough balance for total cost (fees + value)
if !haveSponsor {
// Verify sender has enough balance for total cost (fees + value)
if err := VerifyAccountBalance(account, ethTx.Cost()); err != nil {
return ctx, err
}
} else {
// Verify the sender has balance >= ethTx.Value()
if err := VerifyAccountBalance(account, ethTx.Value()); err != nil {
return ctx, err
}
}

// 7. can transfer
coreMsg := ethMsg.AsMessage(decUtils.BaseFee)
if err := CanTransfer(
ctx,
md.evmKeeper,
*coreMsg,
decUtils.BaseFee,
decUtils.EvmParams,
decUtils.Rules.IsLondon,
); err != nil {
return ctx, err
}

err = ConsumeFeesAndEmitEvent(
ctx,
md.evmKeeper,
msgFees,
from,
feePayer.Bytes(),
)
if err != nil {
return ctx, err
Expand Down
6 changes: 5 additions & 1 deletion ante/evm/mono_decorator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@ import (
"cosmossdk.io/math"
storetypes "cosmossdk.io/store/types"

feegrantkeeper "cosmossdk.io/x/feegrant/keeper"
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
feesponsorkeeper "github.com/cosmos/evm/x/feesponsor/keeper"
)

// adds missing methods
Expand Down Expand Up @@ -219,7 +221,9 @@ func TestMonoDecorator(t *testing.T) {
feeMarketKeeper := MockFeeMarketKeeper{}
params := keeper.GetParams(sdk.Context{})
feemarketParams := feeMarketKeeper.GetParams(sdk.Context{})
monoDec := evm.NewEVMMonoDecorator(accountKeeper, feeMarketKeeper, keeper, 0, &params, &feemarketParams)
feegrantKeeper := feegrantkeeper.Keeper{}
feesponsorKeeper := feesponsorkeeper.Keeper{}
monoDec := evm.NewEVMMonoDecorator(accountKeeper, feeMarketKeeper, keeper, feegrantKeeper, feesponsorKeeper, 0, &params, &feemarketParams)
ctx := sdk.NewContext(nil, tmproto.Header{}, false, log.NewNopLogger())
ctx = ctx.WithBlockGasMeter(storetypes.NewGasMeter(1e19))

Expand Down
8 changes: 8 additions & 0 deletions ante/interfaces/cosmos.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import (

addresscodec "cosmossdk.io/core/address"

"cosmossdk.io/x/feegrant"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth/ante"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
)

Expand All @@ -30,3 +32,9 @@ type BankKeeper interface {
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
}

// FeegrantKeeper defines the expected feegrant keeper.
type FeegrantKeeper interface {
ante.FeegrantKeeper
Allowance(c context.Context, req *feegrant.QueryAllowanceRequest) (*feegrant.QueryAllowanceResponse, error)
}
Loading
Loading