From 877558ffc08ec3e5963b9aad839b250fc0993d49 Mon Sep 17 00:00:00 2001 From: Callum Waters Date: Mon, 25 Sep 2023 09:19:43 +0200 Subject: [PATCH] feat: add decorator to wrap panic messages with the transaction that caused them (#2559) Closes: #2557 This will allow us to understand when a panic happens, which transaction caused it. As an example here is a mock panic: ``` mock panic caused by transaction: *types.MsgSend{from_address:"celestia15v4r83er48zfrm7u6uuqs3lytwxc63c34ltdgn" to_address:"celestia1t5m2f2m000twkmsgaa777rty36wd254292am6c" amount: } ``` (cherry picked from commit a3d24539ac6c5c567503762fe218d7b9d93c85ec) --- app/ante/ante.go | 4 +++- app/ante/panic.go | 32 ++++++++++++++++++++++++++++++++ app/ante/panic_test.go | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 app/ante/panic.go create mode 100644 app/ante/panic_test.go diff --git a/app/ante/ante.go b/app/ante/ante.go index a6e1f0eeed..1988d02158 100644 --- a/app/ante/ante.go +++ b/app/ante/ante.go @@ -21,8 +21,10 @@ func NewAnteHandler( channelKeeper *ibckeeper.Keeper, ) sdk.AnteHandler { return sdk.ChainAnteDecorators( + // Wraps the panic with the string format of the transaction + NewHandlePanicDecorator(), // Set up the context with a gas meter. - // Contract: must be called first. + // Must be called before gas consumption occurs in any other decorator. ante.NewSetUpContextDecorator(), // Ensure the tx does not contain any extension options. ante.NewExtensionOptionsDecorator(nil), diff --git a/app/ante/panic.go b/app/ante/panic.go new file mode 100644 index 0000000000..3f869b7e7d --- /dev/null +++ b/app/ante/panic.go @@ -0,0 +1,32 @@ +package ante + +import ( + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" +) + +// HandlePanicDecorator that catches panics and wraps them in the transaction that caused the panic +type HandlePanicDecorator struct{} + +func NewHandlePanicDecorator() HandlePanicDecorator { + return HandlePanicDecorator{} +} + +func (d HandlePanicDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (newCtx sdk.Context, err error) { + defer func() { + if r := recover(); r != nil { + panic(fmt.Sprint(r, FormatTx(tx))) + } + }() + + return next(ctx, tx, simulate) +} + +func FormatTx(tx sdk.Tx) string { + output := "\ncaused by transaction:\n" + for _, msg := range tx.GetMsgs() { + output += fmt.Sprintf("%T{%s}\n", msg, msg) + } + return output +} diff --git a/app/ante/panic_test.go b/app/ante/panic_test.go new file mode 100644 index 0000000000..ef46a6e5a8 --- /dev/null +++ b/app/ante/panic_test.go @@ -0,0 +1,37 @@ +package ante_test + +import ( + "fmt" + "testing" + + "github.com/celestiaorg/celestia-app/app" + "github.com/celestiaorg/celestia-app/app/ante" + "github.com/celestiaorg/celestia-app/app/encoding" + "github.com/celestiaorg/celestia-app/test/util/testnode" + sdk "github.com/cosmos/cosmos-sdk/types" + banktypes "github.com/cosmos/cosmos-sdk/x/bank/types" + "github.com/stretchr/testify/require" +) + +func TestPanicHandlerDecorator(t *testing.T) { + decorator := ante.NewHandlePanicDecorator() + anteHandler := sdk.ChainAnteDecorators(decorator, mockPanicDecorator{}) + ctx := sdk.Context{} + encCfg := encoding.MakeConfig(app.ModuleEncodingRegisters...) + builder := encCfg.TxConfig.NewTxBuilder() + err := builder.SetMsgs(banktypes.NewMsgSend(testnode.RandomAddress().(sdk.AccAddress), testnode.RandomAddress().(sdk.AccAddress), sdk.NewCoins(sdk.NewInt64Coin(app.BondDenom, 10)))) + require.NoError(t, err) + tx := builder.GetTx() + defer func() { + r := recover() + require.NotNil(t, r) + require.Equal(t, fmt.Sprint("mock panic", ante.FormatTx(tx)), r) + }() + _, _ = anteHandler(ctx, tx, false) +} + +type mockPanicDecorator struct{} + +func (d mockPanicDecorator) AnteHandle(_ sdk.Context, _ sdk.Tx, _ bool, _ sdk.AnteHandler) (newCtx sdk.Context, err error) { + panic("mock panic") +}