fix(baseapp)!: postHandler should run regardless of result (#18627)
This commit is contained in:
parent
142f32589d
commit
b2084dceb9
@ -185,6 +185,7 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
||||
|
||||
### State Machine Breaking
|
||||
|
||||
* (baseapp) [#18627](https://github.com/cosmos/cosmos-sdk/pull/18627) Post handlers are run on non successful transaction executions too.
|
||||
* (x/upgrade) [#16244](https://github.com/cosmos/cosmos-sdk/pull/16244) Upgrade module no longer stores the app version but gets and sets the app version stored in the `ParamStore` of baseapp.
|
||||
* (x/staking) [#17655](https://github.com/cosmos/cosmos-sdk/pull/17655) `HistoricalInfo` was replaced with `HistoricalRecord`, it removes the validator set and comet header and only keep what is needed for IBC.
|
||||
|
||||
|
||||
@ -938,24 +938,25 @@ func (app *BaseApp) runTx(mode execMode, txBytes []byte) (gInfo sdk.GasInfo, res
|
||||
if err == nil {
|
||||
result, err = app.runMsgs(runMsgCtx, msgs, msgsV2, mode)
|
||||
}
|
||||
if err == nil {
|
||||
// Run optional postHandlers.
|
||||
//
|
||||
// Note: If the postHandler fails, we also revert the runMsgs state.
|
||||
if app.postHandler != nil {
|
||||
// The runMsgCtx context currently contains events emitted by the ante handler.
|
||||
// We clear this to correctly order events without duplicates.
|
||||
// Note that the state is still preserved.
|
||||
postCtx := runMsgCtx.WithEventManager(sdk.NewEventManager())
|
||||
|
||||
newCtx, err := app.postHandler(postCtx, tx, mode == execModeSimulate, err == nil)
|
||||
if err != nil {
|
||||
return gInfo, nil, anteEvents, err
|
||||
}
|
||||
// Run optional postHandlers (should run regardless of the execution result).
|
||||
//
|
||||
// Note: If the postHandler fails, we also revert the runMsgs state.
|
||||
if app.postHandler != nil {
|
||||
// The runMsgCtx context currently contains events emitted by the ante handler.
|
||||
// We clear this to correctly order events without duplicates.
|
||||
// Note that the state is still preserved.
|
||||
postCtx := runMsgCtx.WithEventManager(sdk.NewEventManager())
|
||||
|
||||
result.Events = append(result.Events, newCtx.EventManager().ABCIEvents()...)
|
||||
newCtx, err := app.postHandler(postCtx, tx, mode == execModeSimulate, err == nil)
|
||||
if err != nil {
|
||||
return gInfo, nil, anteEvents, err
|
||||
}
|
||||
|
||||
result.Events = append(result.Events, newCtx.EventManager().ABCIEvents()...)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
if mode == execModeFinalize {
|
||||
// When block gas exceeds, it'll panic and won't commit the cached store.
|
||||
consumeBlockGas()
|
||||
|
||||
@ -594,6 +594,53 @@ func TestBaseAppAnteHandler(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestBaseAppPostHandler(t *testing.T) {
|
||||
postHandlerRun := false
|
||||
anteOpt := func(bapp *baseapp.BaseApp) {
|
||||
bapp.SetPostHandler(func(ctx sdk.Context, tx sdk.Tx, simulate, success bool) (newCtx sdk.Context, err error) {
|
||||
postHandlerRun = true
|
||||
return ctx, nil
|
||||
})
|
||||
}
|
||||
|
||||
suite := NewBaseAppSuite(t, anteOpt)
|
||||
|
||||
baseapptestutil.RegisterCounterServer(suite.baseApp.MsgServiceRouter(), CounterServerImpl{t, capKey1, []byte("foo")})
|
||||
|
||||
_, err := suite.baseApp.InitChain(&abci.RequestInitChain{
|
||||
ConsensusParams: &cmtproto.ConsensusParams{},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// execute a tx that will fail ante handler execution
|
||||
//
|
||||
// NOTE: State should not be mutated here. This will be implicitly checked by
|
||||
// the next txs ante handler execution (anteHandlerTxTest).
|
||||
tx := newTxCounter(t, suite.txConfig, 0, 0)
|
||||
txBytes, err := suite.txConfig.TxEncoder()(tx)
|
||||
require.NoError(t, err)
|
||||
|
||||
res, err := suite.baseApp.FinalizeBlock(&abci.RequestFinalizeBlock{Height: 1, Txs: [][]byte{txBytes}})
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, res.Events)
|
||||
require.True(t, res.TxResults[0].IsOK(), fmt.Sprintf("%v", res))
|
||||
|
||||
// PostHandler runs on successful message execution
|
||||
require.True(t, postHandlerRun)
|
||||
|
||||
// It should also run on failed message execution
|
||||
postHandlerRun = false
|
||||
tx = setFailOnHandler(t, suite.txConfig, tx, true)
|
||||
txBytes, err = suite.txConfig.TxEncoder()(tx)
|
||||
require.NoError(t, err)
|
||||
res, err = suite.baseApp.FinalizeBlock(&abci.RequestFinalizeBlock{Height: 1, Txs: [][]byte{txBytes}})
|
||||
require.NoError(t, err)
|
||||
require.Empty(t, res.Events)
|
||||
require.False(t, res.TxResults[0].IsOK(), fmt.Sprintf("%v", res))
|
||||
|
||||
require.True(t, postHandlerRun)
|
||||
}
|
||||
|
||||
// Test and ensure that invalid block heights always cause errors.
|
||||
// See issues:
|
||||
// - https://github.com/cosmos/cosmos-sdk/issues/11220
|
||||
|
||||
Loading…
Reference in New Issue
Block a user