From 7f72891c09c5632c3eb215113757b24997f78fe0 Mon Sep 17 00:00:00 2001 From: Federico Kunze <31522760+fedekunze@users.noreply.github.com> Date: Tue, 15 Jun 2021 03:19:31 -0400 Subject: [PATCH] ante: add stacktrace (#123) --- app/ante/ante.go | 12 +++-- app/ante/eth.go | 118 ++++++++++++++++++++++++++++++++++------------- 2 files changed, 94 insertions(+), 36 deletions(-) diff --git a/app/ante/ante.go b/app/ante/ante.go index 5d659a93..05c541ec 100644 --- a/app/ante/ante.go +++ b/app/ante/ante.go @@ -3,6 +3,7 @@ package ante import ( "runtime/debug" + "github.com/palantir/stacktrace" log "github.com/xlab/suplog" sdk "github.com/cosmos/cosmos-sdk/types" @@ -73,8 +74,10 @@ func NewAnteHandler( ) default: - log.WithField("type_url", typeURL).Errorln("rejecting tx with unsupported extension option") - return ctx, sdkerrors.Wrap(sdkerrors.ErrUnknownExtensionOptions, typeURL) + return ctx, stacktrace.Propagate( + sdkerrors.Wrap(sdkerrors.ErrUnknownExtensionOptions, typeURL), + "rejecting tx with unsupported extension option", + ) } return anteHandler(ctx, tx, sim) @@ -102,7 +105,10 @@ func NewAnteHandler( authante.NewIncrementSequenceDecorator(ak), // innermost AnteDecorator ) 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) diff --git a/app/ante/eth.go b/app/ante/eth.go index b2d404d6..496fed91 100644 --- a/app/ante/eth.go +++ b/app/ante/eth.go @@ -6,6 +6,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" authante "github.com/cosmos/cosmos-sdk/x/auth/ante" + "github.com/palantir/stacktrace" ethermint "github.com/cosmos/ethermint/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 { - 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 @@ -71,12 +75,19 @@ func (esvd EthSigVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, s msg := tx.GetMsgs()[0] msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx) 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()) 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 @@ -118,16 +129,22 @@ func (avd EthAccountVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx evmDenom := avd.evmKeeper.GetParams(infCtx).EvmDenom - for _, msg := range tx.GetMsgs() { + for i, msg := range tx.GetMsgs() { msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx) 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 from := msgEthTx.GetFrom() 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) @@ -136,12 +153,15 @@ func (avd EthAccountVerificationDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx 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) if balance.Amount.BigInt().Cmp(msgEthTx.Cost()) < 0 { - return ctx, sdkerrors.Wrapf( - sdkerrors.ErrInsufficientFunds, - "sender balance < tx gas cost (%s < %s%s)", balance.String(), msgEthTx.Cost().String(), evmDenom, + return ctx, stacktrace.Propagate( + sdkerrors.Wrapf( + 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. infCtx := ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) - for _, msg := range tx.GetMsgs() { + for i, msg := range tx.GetMsgs() { msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx) 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 seq, err := nvd.ak.GetSequence(infCtx, msgEthTx.GetFrom()) 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, // all will be rejected except the first, since the first needs to be included in a block // before the sequence increments if msgEthTx.Data.Nonce != seq { - return ctx, sdkerrors.Wrapf( - sdkerrors.ErrInvalidSequence, - "invalid nonce; got %d, expected %d", msgEthTx.Data.Nonce, seq, + return ctx, stacktrace.Propagate( + sdkerrors.Wrapf( + 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) istanbul := ethCfg.IsIstanbul(blockHeight) - for _, msg := range tx.GetMsgs() { + for i, msg := range tx.GetMsgs() { msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx) 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 @@ -261,7 +290,7 @@ func (egcd EthGasConsumeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simula // fetch sender account from signature signerAcc, err := authante.GetSignerAcc(infCtx, egcd.ak, msgEthTx.GetFrom()) if err != nil { - return ctx, err + return ctx, stacktrace.Propagate(err, "account not found for sender %s", msgEthTx.From) } 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) 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 @@ -289,7 +320,10 @@ func (egcd EthGasConsumeDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simula // deduct the full gas cost from the user balance 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 @@ -343,15 +377,21 @@ func (ctd CanTransferDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate ethCfg := config.EthereumConfig(ctd.evmKeeper.ChainID()) signer := ethtypes.MakeSigner(ethCfg, big.NewInt(ctx.BlockHeight())) - for _, msg := range tx.GetMsgs() { + for i, msg := range tx.GetMsgs() { msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx) 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) 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) @@ -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 // 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()) { - 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 ald.evmKeeper.WithContext(infCtx) - for _, msg := range tx.GetMsgs() { + for i, msg := range tx.GetMsgs() { msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx) 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()) @@ -450,10 +496,13 @@ func (issd EthIncrementSenderSequenceDecorator) AnteHandle(ctx sdk.Context, tx s // additional gas from being deducted. infCtx := ctx.WithGasMeter(sdk.NewInfiniteGasMeter()) - for _, msg := range tx.GetMsgs() { + for i, msg := range tx.GetMsgs() { msgEthTx, ok := msg.(*evmtypes.MsgEthereumTx) 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 @@ -469,14 +518,17 @@ func (issd EthIncrementSenderSequenceDecorator) AnteHandle(ctx sdk.Context, tx s acc := issd.ak.GetAccount(infCtx, addr) if acc == nil { - return ctx, sdkerrors.Wrapf( - sdkerrors.ErrUnknownAddress, - "account %s (%s) is nil", common.BytesToAddress(addr.Bytes()), addr, + return ctx, stacktrace.Propagate( + sdkerrors.Wrapf( + 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 { - return ctx, err + return ctx, stacktrace.Propagate(err, "failed to set sequence to %d", acc.GetSequence()+1) } issd.ak.SetAccount(infCtx, acc)