ante: add stacktrace (#123)

This commit is contained in:
Federico Kunze 2021-06-15 03:19:31 -04:00 committed by GitHub
parent 3a74d2402b
commit 7f72891c09
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 94 additions and 36 deletions

View File

@ -3,6 +3,7 @@ package ante
import ( import (
"runtime/debug" "runtime/debug"
"github.com/palantir/stacktrace"
log "github.com/xlab/suplog" log "github.com/xlab/suplog"
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
@ -73,8 +74,10 @@ func NewAnteHandler(
) )
default: default:
log.WithField("type_url", typeURL).Errorln("rejecting tx with unsupported extension option") return ctx, stacktrace.Propagate(
return ctx, sdkerrors.Wrap(sdkerrors.ErrUnknownExtensionOptions, typeURL) sdkerrors.Wrap(sdkerrors.ErrUnknownExtensionOptions, typeURL),
"rejecting tx with unsupported extension option",
)
} }
return anteHandler(ctx, tx, sim) return anteHandler(ctx, tx, sim)
@ -102,7 +105,10 @@ func NewAnteHandler(
authante.NewIncrementSequenceDecorator(ak), // innermost AnteDecorator authante.NewIncrementSequenceDecorator(ak), // innermost AnteDecorator
) )
default: default:
return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type: %T", tx) return ctx, stacktrace.Propagate(
sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type: %T", tx),
"transaction is not an SDK tx",
)
} }
return anteHandler(ctx, tx, sim) return anteHandler(ctx, tx, sim)

View File

@ -6,6 +6,7 @@ import (
sdk "github.com/cosmos/cosmos-sdk/types" sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
authante "github.com/cosmos/cosmos-sdk/x/auth/ante" authante "github.com/cosmos/cosmos-sdk/x/auth/ante"
"github.com/palantir/stacktrace"
ethermint "github.com/cosmos/ethermint/types" ethermint "github.com/cosmos/ethermint/types"
evmtypes "github.com/cosmos/ethermint/x/evm/types" evmtypes "github.com/cosmos/ethermint/x/evm/types"
@ -50,7 +51,10 @@ func (esvd EthSigVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, s
} }
if len(tx.GetMsgs()) != 1 { if len(tx.GetMsgs()) != 1 {
return ctx, sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "only 1 ethereum msg supported per tx, got %d", len(tx.GetMsgs())) return ctx, stacktrace.Propagate(
sdkerrors.Wrapf(sdkerrors.ErrInvalidRequest, "only 1 ethereum msg supported per tx, got %d", len(tx.GetMsgs())),
"",
)
} }
// get and set account must be called with an infinite gas meter in order to prevent // get and set account must be called with an infinite gas meter in order to prevent
@ -71,12 +75,19 @@ func (esvd EthSigVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, s
msg := tx.GetMsgs()[0] msg := tx.GetMsgs()[0]
msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx) msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx)
if !ok { if !ok {
return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type %T, expected %T", tx, &evmtypes.MsgEthereumTx{}) return ctx, stacktrace.Propagate(
sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type %T, expected %T", tx, &evmtypes.MsgEthereumTx{}),
"failed to cast transaction",
)
} }
sender, err := signer.Sender(msgEthTx.AsTransaction()) sender, err := signer.Sender(msgEthTx.AsTransaction())
if err != nil { if err != nil {
return ctx, sdkerrors.Wrap(sdkerrors.ErrorInvalidSigner, err.Error()) return ctx, stacktrace.Propagate(
sdkerrors.Wrap(sdkerrors.ErrorInvalidSigner, err.Error()),
"couldn't retrieve sender address ('%s') from the ethereum transaction",
msgEthTx.From,
)
} }
// set up the sender to the transaction field if not already // set up the sender to the transaction field if not already
@ -118,16 +129,22 @@ func (avd EthAccountVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx
evmDenom := avd.evmKeeper.GetParams(infCtx).EvmDenom evmDenom := avd.evmKeeper.GetParams(infCtx).EvmDenom
for _, msg := range tx.GetMsgs() { for i, msg := range tx.GetMsgs() {
msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx) msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx)
if !ok { if !ok {
return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type %T, expected %T", tx, &evmtypes.MsgEthereumTx{}) return ctx, stacktrace.Propagate(
sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type %T, expected %T", tx, &evmtypes.MsgEthereumTx{}),
"failed to cast transaction %d", i,
)
} }
// sender address should be in the tx cache from the previous AnteHandle call // sender address should be in the tx cache from the previous AnteHandle call
from := msgEthTx.GetFrom() from := msgEthTx.GetFrom()
if from.Empty() { if from.Empty() {
return ctx, sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "from address cannot be empty") return ctx, stacktrace.Propagate(
sdkerrors.Wrapf(sdkerrors.ErrInvalidAddress, "from address cannot be empty"),
"sender address should have been in the tx field from the previous AnteHandle call",
)
} }
acc := avd.ak.GetAccount(infCtx, from) acc := avd.ak.GetAccount(infCtx, from)
@ -136,12 +153,15 @@ func (avd EthAccountVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx
avd.ak.SetAccount(infCtx, acc) avd.ak.SetAccount(infCtx, acc)
} }
// validate sender has enough funds to pay for gas cost // validate sender has enough funds to pay for tx cost
balance := avd.bankKeeper.GetBalance(infCtx, from, evmDenom) balance := avd.bankKeeper.GetBalance(infCtx, from, evmDenom)
if balance.Amount.BigInt().Cmp(msgEthTx.Cost()) < 0 { if balance.Amount.BigInt().Cmp(msgEthTx.Cost()) < 0 {
return ctx, sdkerrors.Wrapf( return ctx, stacktrace.Propagate(
sdkerrors.ErrInsufficientFunds, sdkerrors.Wrapf(
"sender balance < tx gas cost (%s < %s%s)", balance.String(), msgEthTx.Cost().String(), evmDenom, sdkerrors.ErrInsufficientFunds,
"sender balance < tx cost (%s < %s%s)", balance, msgEthTx.Cost(), evmDenom,
),
"sender should have had enough funds to pay for tx cost = fee + amount (%s = %s + amount)", msgEthTx.Cost(), msgEthTx.Fee(),
) )
} }
} }
@ -174,25 +194,31 @@ func (nvd EthNonceVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx,
// additional gas from being deducted. // additional gas from being deducted.
infCtx := ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) infCtx := ctx.WithGasMeter(sdk.NewInfiniteGasMeter())
for _, msg := range tx.GetMsgs() { for i, msg := range tx.GetMsgs() {
msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx) msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx)
if !ok { if !ok {
return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type %T, expected %T", tx, &evmtypes.MsgEthereumTx{}) return ctx, stacktrace.Propagate(
sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type %T, expected %T", tx, &evmtypes.MsgEthereumTx{}),
"failed to cast transaction %d", i,
)
} }
// sender address should be in the tx cache from the previous AnteHandle call // sender address should be in the tx cache from the previous AnteHandle call
seq, err := nvd.ak.GetSequence(infCtx, msgEthTx.GetFrom()) seq, err := nvd.ak.GetSequence(infCtx, msgEthTx.GetFrom())
if err != nil { if err != nil {
return ctx, err return ctx, stacktrace.Propagate(err, "sequence not found for address %s", msgEthTx.From)
} }
// if multiple transactions are submitted in succession with increasing nonces, // if multiple transactions are submitted in succession with increasing nonces,
// all will be rejected except the first, since the first needs to be included in a block // all will be rejected except the first, since the first needs to be included in a block
// before the sequence increments // before the sequence increments
if msgEthTx.Data.Nonce != seq { if msgEthTx.Data.Nonce != seq {
return ctx, sdkerrors.Wrapf( return ctx, stacktrace.Propagate(
sdkerrors.ErrInvalidSequence, sdkerrors.Wrapf(
"invalid nonce; got %d, expected %d", msgEthTx.Data.Nonce, seq, sdkerrors.ErrInvalidSequence,
"invalid nonce; got %d, expected %d", msgEthTx.Data.Nonce, seq,
),
"",
) )
} }
} }
@ -250,10 +276,13 @@ func (egcd EthGasConsumeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simula
homestead := ethCfg.IsHomestead(blockHeight) homestead := ethCfg.IsHomestead(blockHeight)
istanbul := ethCfg.IsIstanbul(blockHeight) istanbul := ethCfg.IsIstanbul(blockHeight)
for _, msg := range tx.GetMsgs() { for i, msg := range tx.GetMsgs() {
msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx) msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx)
if !ok { if !ok {
return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type %T, expected %T", tx, &evmtypes.MsgEthereumTx{}) return ctx, stacktrace.Propagate(
sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type %T, expected %T", tx, &evmtypes.MsgEthereumTx{}),
"failed to cast transaction %d", i,
)
} }
isContractCreation := msgEthTx.To() == nil isContractCreation := msgEthTx.To() == nil
@ -261,7 +290,7 @@ func (egcd EthGasConsumeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simula
// fetch sender account from signature // fetch sender account from signature
signerAcc, err := authante.GetSignerAcc(infCtx, egcd.ak, msgEthTx.GetFrom()) signerAcc, err := authante.GetSignerAcc(infCtx, egcd.ak, msgEthTx.GetFrom())
if err != nil { if err != nil {
return ctx, err return ctx, stacktrace.Propagate(err, "account not found for sender %s", msgEthTx.From)
} }
gasLimit := msgEthTx.GetGas() gasLimit := msgEthTx.GetGas()
@ -273,7 +302,9 @@ func (egcd EthGasConsumeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simula
intrinsicGas, err := core.IntrinsicGas(msgEthTx.Data.Input, accessList, isContractCreation, homestead, istanbul) intrinsicGas, err := core.IntrinsicGas(msgEthTx.Data.Input, accessList, isContractCreation, homestead, istanbul)
if err != nil { if err != nil {
return ctx, sdkerrors.Wrap(err, "failed to compute intrinsic gas cost") return ctx, stacktrace.Propagate(
sdkerrors.Wrap(err, "failed to compute intrinsic gas cost"),
"failed to retrieve intrinsic gas, contract creation = %t; homestead = %t, istanbul = %t", isContractCreation, homestead, istanbul)
} }
// intrinsic gas verification during CheckTx // intrinsic gas verification during CheckTx
@ -289,7 +320,10 @@ func (egcd EthGasConsumeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simula
// deduct the full gas cost from the user balance // deduct the full gas cost from the user balance
if err := authante.DeductFees(egcd.bankKeeper, infCtx, signerAcc, fees); err != nil { if err := authante.DeductFees(egcd.bankKeeper, infCtx, signerAcc, fees); err != nil {
return ctx, err return ctx, stacktrace.Propagate(
err,
"failed to deduct full gas cost %s from the user %s balance", fees, msgEthTx.From,
)
} }
// consume intrinsic gas for the current transaction. After runTx is executed on Baseapp, the // consume intrinsic gas for the current transaction. After runTx is executed on Baseapp, the
@ -343,15 +377,21 @@ func (ctd CanTransferDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate
ethCfg := config.EthereumConfig(ctd.evmKeeper.ChainID()) ethCfg := config.EthereumConfig(ctd.evmKeeper.ChainID())
signer := ethtypes.MakeSigner(ethCfg, big.NewInt(ctx.BlockHeight())) signer := ethtypes.MakeSigner(ethCfg, big.NewInt(ctx.BlockHeight()))
for _, msg := range tx.GetMsgs() { for i, msg := range tx.GetMsgs() {
msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx) msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx)
if !ok { if !ok {
return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type: %T", msg) return ctx, stacktrace.Propagate(
sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type %T, expected %T", tx, &evmtypes.MsgEthereumTx{}),
"failed to cast transaction %d", i,
)
} }
coreMsg, err := msgEthTx.AsMessage(signer) coreMsg, err := msgEthTx.AsMessage(signer)
if err != nil { if err != nil {
return ctx, err return ctx, stacktrace.Propagate(
err,
"failed to create an ethereum core.Message from signer %T", signer,
)
} }
evm := ctd.evmKeeper.NewEVM(coreMsg, ethCfg) evm := ctd.evmKeeper.NewEVM(coreMsg, ethCfg)
@ -359,7 +399,10 @@ func (ctd CanTransferDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate
// check that caller has enough balance to cover asset transfer for **topmost** call // check that caller has enough balance to cover asset transfer for **topmost** call
// NOTE: here the gas consumed is from the context with the infinite gas meter // NOTE: here the gas consumed is from the context with the infinite gas meter
if coreMsg.Value().Sign() > 0 && !evm.Context.CanTransfer(ctd.evmKeeper, coreMsg.From(), coreMsg.Value()) { if coreMsg.Value().Sign() > 0 && !evm.Context.CanTransfer(ctd.evmKeeper, coreMsg.From(), coreMsg.Value()) {
return ctx, sdkerrors.Wrapf(sdkerrors.ErrInsufficientFunds, "address %s", coreMsg.From().Hex()) return ctx, stacktrace.Propagate(
sdkerrors.Wrapf(sdkerrors.ErrInsufficientFunds, "address %s", coreMsg.From()),
"failed to transfer %s using the EVM block context transfer function", coreMsg.Value(),
)
} }
} }
@ -414,10 +457,13 @@ func (ald AccessListDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate b
// setup the keeper context before setting the access list // setup the keeper context before setting the access list
ald.evmKeeper.WithContext(infCtx) ald.evmKeeper.WithContext(infCtx)
for _, msg := range tx.GetMsgs() { for i, msg := range tx.GetMsgs() {
msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx) msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx)
if !ok { if !ok {
return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type: %T", msg) return ctx, stacktrace.Propagate(
sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type %T, expected %T", tx, &evmtypes.MsgEthereumTx{}),
"failed to cast transaction %d", i,
)
} }
sender := common.BytesToAddress(msgEthTx.GetFrom()) sender := common.BytesToAddress(msgEthTx.GetFrom())
@ -450,10 +496,13 @@ func (issd EthIncrementSenderSequenceDecorator) AnteHandle(ctx sdk.Context, tx s
// additional gas from being deducted. // additional gas from being deducted.
infCtx := ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) infCtx := ctx.WithGasMeter(sdk.NewInfiniteGasMeter())
for _, msg := range tx.GetMsgs() { for i, msg := range tx.GetMsgs() {
msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx) msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx)
if !ok { if !ok {
return ctx, sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type: %T", msg) return ctx, stacktrace.Propagate(
sdkerrors.Wrapf(sdkerrors.ErrUnknownRequest, "invalid transaction type %T, expected %T", tx, &evmtypes.MsgEthereumTx{}),
"failed to cast transaction %d", i,
)
} }
// NOTE: on contract creation, the nonce is incremented within the EVM Create function during tx execution // NOTE: on contract creation, the nonce is incremented within the EVM Create function during tx execution
@ -469,14 +518,17 @@ func (issd EthIncrementSenderSequenceDecorator) AnteHandle(ctx sdk.Context, tx s
acc := issd.ak.GetAccount(infCtx, addr) acc := issd.ak.GetAccount(infCtx, addr)
if acc == nil { if acc == nil {
return ctx, sdkerrors.Wrapf( return ctx, stacktrace.Propagate(
sdkerrors.ErrUnknownAddress, sdkerrors.Wrapf(
"account %s (%s) is nil", common.BytesToAddress(addr.Bytes()), addr, sdkerrors.ErrUnknownAddress,
"account %s (%s) is nil", common.BytesToAddress(addr.Bytes()), addr,
),
"signer account not found",
) )
} }
if err := acc.SetSequence(acc.GetSequence() + 1); err != nil { if err := acc.SetSequence(acc.GetSequence() + 1); err != nil {
return ctx, err return ctx, stacktrace.Propagate(err, "failed to set sequence to %d", acc.GetSequence()+1)
} }
issd.ak.SetAccount(infCtx, acc) issd.ak.SetAccount(infCtx, acc)