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

Add EVM support #221

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
91 changes: 0 additions & 91 deletions app/ante.go

This file was deleted.

120 changes: 120 additions & 0 deletions app/ante/ante.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
package ante

import (
"fmt"
"github.com/armon/go-metrics"
"github.com/cosmos/cosmos-sdk/telemetry"
"runtime/debug"

tmlog "github.com/tendermint/tendermint/libs/log"

sdk "github.com/cosmos/cosmos-sdk/types"
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"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"

"github.com/evmos/ethermint/crypto/ethsecp256k1"
)

const (
secp256k1VerifyCost uint64 = 21000
)

// NewAnteHandler returns an ante handler responsible for attempting to route an
// 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(options HandlerOptions) (sdk.AnteHandler, error) {
if err := options.validate(); err != nil {
return nil, err
}

defaultCosmosAnteHandler := newCosmosAnteHandler(options)
// Cache AnteHandlers so we don't recreate on every tx
requestHandlerMap := map[string]sdk.AnteHandler{
"/ethermint.evm.v1.ExtensionOptionsEthereumTx": newEthAnteHandler(options),
"/ethermint.types.v1.ExtensionOptionsWeb3Tx": newCosmosAnteHandlerEip712(options),
"/ethermint.types.v1.ExtensionOptionDynamicFeeTx": defaultCosmosAnteHandler,
}
fmt.Println("finding handler")

return func(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it possible to return built EthAnteHandler & CosmosAnteHandler map here? or register ethAnteHandler to the cosmos ante decorator? returning a func here seems to make each tx creates a new AnteHandler here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point. Done!

ctx sdk.Context, tx sdk.Tx, sim bool,
) (newCtx sdk.Context, err error) {
var anteHandler sdk.AnteHandler

defer Recover(ctx.Logger(), &err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we want to remove the panic-recover here? we properly want to avoid recover panic as that may hide more severe bugs (and we may not be able to monitor this each time)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a good question. I think it doesn't make sense for a single txn to bring down the whole chain though. Ill think it makes sense to add monitoring though

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yea the thing i'm worried about is this may hide err and make the chain get into a corrupted state. I'm aligned with having this on testnet + monitor, and remove before mainnet


txWithExtensions, ok := tx.(authante.HasExtensionOptionsTx)
if ok {
opts := txWithExtensions.GetExtensionOptions()
if len(opts) > 0 {
typeURL := opts[0].GetTypeUrl()
if reqAnteHandler, ok := requestHandlerMap[typeURL]; ok {
fmt.Println("ether")
return reqAnteHandler(ctx, tx, sim)
} else {
return ctx, sdkerrors.Wrapf(
sdkerrors.ErrUnknownExtensionOptions,
"rejecting tx with unsupported extension option: %s", typeURL,
)
}
}
fmt.Println("len opts 0")
}
// handle as totally normal Cosmos SDK tx
switch tx.(type) {
case sdk.Tx:
fmt.Println("default")
anteHandler = defaultCosmosAnteHandler
default:
return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type: %T", tx)
}
return anteHandler(ctx, tx, sim)
}, nil
}

func Recover(logger tmlog.Logger, err *error) {
telemetry.IncrCounterWithLabels(
[]string{("antehandler_panic")},
1,
[]metrics.Label{
telemetry.NewLabel("error", fmt.Sprintf("%s", err)),
},
)
if r := recover(); r != nil {
*err = sdkerrors.Wrapf(sdkerrors.ErrPanic, "%v", r)

if e, ok := r.(error); ok {
logger.Error(
"ante handler panicked",
"error", e,
"stack trace", string(debug.Stack()),
)
} else {
logger.Error(
"ante handler panicked",
"recover", fmt.Sprintf("%v", r),
)
}
}
}

var _ authante.SignatureVerificationGasConsumer = DefaultSigVerificationGasConsumer

// DefaultSigVerificationGasConsumer is the default implementation of SignatureVerificationGasConsumer. It consumes gas
// for signature verification based upon the public key type. The cost is fetched from the given params and is matched
// by the concrete type.
func DefaultSigVerificationGasConsumer(
meter sdk.GasMeter, sig signing.SignatureV2, params authtypes.Params,
) error {
// support for ethereum ECDSA secp256k1 keys
_, ok := sig.PubKey.(*ethsecp256k1.PubKey)
if ok {
meter.ConsumeGas(secp256k1VerifyCost, "ante verify: eth_secp256k1")
return nil
}

return authante.DefaultSigVerificationGasConsumer(meter, sig, params)
}
128 changes: 128 additions & 0 deletions app/ante/handler_options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package ante

import (
wasmkeeper "github.com/CosmWasm/wasmd/x/wasm/keeper"
wasmtypes "github.com/CosmWasm/wasmd/x/wasm/types"
"github.com/cosmos/cosmos-sdk/codec"
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"
ibcante "github.com/cosmos/ibc-go/v3/modules/core/ante"
ibckeeper "github.com/cosmos/ibc-go/v3/modules/core/keeper"
"github.com/sei-protocol/sei-chain/utils/tracing"
dexkeeper "github.com/sei-protocol/sei-chain/x/dex/keeper"
oraclekeeper "github.com/sei-protocol/sei-chain/x/oracle/keeper"

ethante "github.com/evmos/ethermint/app/ante"
evmtypes "github.com/evmos/ethermint/x/evm/types"
)

// HandlerOptions defines the list of module keepers required to run the Evmos
// AnteHandler decorators.
type HandlerOptions struct {
AccountKeeper evmtypes.AccountKeeper
BankKeeper evmtypes.BankKeeper
FeeMarketKeeper evmtypes.FeeMarketKeeper
SignModeHandler authsigning.SignModeHandler
SigGasConsumer func(meter sdk.GasMeter, sig signing.SignatureV2, params authtypes.Params) error
IBCKeeper *ibckeeper.Keeper
WasmKeeper *wasmkeeper.Keeper
OracleKeeper *oraclekeeper.Keeper
DexKeeper *dexkeeper.Keeper
TxCounterStoreKey sdk.StoreKey
WasmConfig *wasmtypes.WasmConfig
EvmKeeper ethante.EVMKeeper
FeegrantKeeper ante.FeegrantKeeper

Cdc codec.BinaryCodec
MaxTxGasWanted uint64
TracingInfo *tracing.Info
}

// Validate checks if the keepers are defined
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
}

// newEthAnteHandler creates the default ante handler for Ethereum transactions
func newEthAnteHandler(options HandlerOptions) sdk.AnteHandler {
return sdk.ChainAnteDecorators(
ethante.NewEthSetUpContextDecorator(options.EvmKeeper), // outermost AnteDecorator. SetUpContext must be called first
ethante.NewEthMempoolFeeDecorator(options.EvmKeeper), // Check eth effective gas price against the node's minimal-gas-prices config
ethante.NewEthMinGasPriceDecorator(options.FeeMarketKeeper, options.EvmKeeper), // Check eth effective gas price against the global MinGasPrice
ethante.NewEthValidateBasicDecorator(options.EvmKeeper),
ethante.NewEthSigVerificationDecorator(options.EvmKeeper),
ethante.NewEthAccountVerificationDecorator(options.AccountKeeper, options.EvmKeeper),
ethante.NewCanTransferDecorator(options.EvmKeeper),
ethante.NewEthGasConsumeDecorator(options.EvmKeeper, options.MaxTxGasWanted),
ethante.NewEthIncrementSenderSequenceDecorator(options.AccountKeeper),
ethante.NewGasWantedDecorator(options.EvmKeeper, options.FeeMarketKeeper),
ethante.NewEthEmitEventDecorator(options.EvmKeeper), // emit eth tx hash and index at the very last ante handler.
)
}

// newCosmosAnteHandler creates the default ante handler for Cosmos transactions
func newCosmosAnteHandler(options HandlerOptions) sdk.AnteHandler {
return sdk.ChainAnteDecorators(
ethante.RejectMessagesDecorator{}, // reject MsgEthereumTxs
ante.NewSetUpContextDecorator(),
ante.NewRejectExtensionOptionsDecorator(),
ante.NewValidateBasicDecorator(),
ante.NewMempoolFeeDecorator(),
ethante.NewMinGasPriceDecorator(options.FeeMarketKeeper, options.EvmKeeper),
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.IBCKeeper),
ethante.NewGasWantedDecorator(options.EvmKeeper, options.FeeMarketKeeper),
)
}

// newCosmosAnteHandlerEip712 creates the ante handler for transactions signed with EIP712
func newCosmosAnteHandlerEip712(options HandlerOptions) sdk.AnteHandler {
return sdk.ChainAnteDecorators(
ethante.RejectMessagesDecorator{}, // reject MsgEthereumTxs
ante.NewSetUpContextDecorator(),
ante.NewMempoolFeeDecorator(),
ante.NewValidateBasicDecorator(),
ethante.NewMinGasPriceDecorator(options.FeeMarketKeeper, options.EvmKeeper),
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),
// Note: signature verification uses EIP instead of the cosmos signature validator
ethante.NewEip712SigVerificationDecorator(options.AccountKeeper, options.SignModeHandler),
ante.NewIncrementSequenceDecorator(options.AccountKeeper),
ibcante.NewAnteDecorator(options.IBCKeeper),
ethante.NewGasWantedDecorator(options.EvmKeeper, options.FeeMarketKeeper),
)
}
Loading