diff --git a/CHANGELOG.md b/CHANGELOG.md index a8aadfbc5fd9..49a1565415a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -96,6 +96,7 @@ Ref: https://keepachangelog.com/en/1.0.0/ ### Bug Fixes +* (baseapp) [#25550](https://github.com/cosmos/cosmos-sdk/pull/25550) Prevent updating check state before mempool insert * (blockstm) [#25789](https://github.com/cosmos/cosmos-sdk/issues/25789) Wake up suspended executors when scheduler doesn't complete to prevent goroutine leaks. * (grpc) [#25647](https://github.com/cosmos/cosmos-sdk/pull/25647) Return actual `earliest_store_height` in `node.Status` gRPC endpoint instead of hardcoded `0`. * (types/query) [#25665](https://github.com/cosmos/cosmos-sdk/issues/25665) Fix pagination offset when querying a collection with predicate function. diff --git a/baseapp/abci_test.go b/baseapp/abci_test.go index 9bb14e309bb4..23a299b78400 100644 --- a/baseapp/abci_test.go +++ b/baseapp/abci_test.go @@ -696,6 +696,46 @@ func TestABCI_CheckTx(t *testing.T) { require.Nil(t, storedBytes) } +func TestABCI_CheckTx_DoesNotCorruptStateOnMempoolFailure(t *testing.T) { + counterKey := []byte("counter-key") + anteOpt := func(bapp *baseapp.BaseApp) { + bapp.SetAnteHandler(anteHandlerTxTest(t, capKey1, counterKey)) + } + + cfg := mempool.DefaultPriorityNonceMempoolConfig() + cfg.MaxTx = 1 + pool := mempool.NewPriorityMempool(cfg) + + suite := NewBaseAppSuite(t, anteOpt, baseapp.SetMempool(pool)) + baseapptestutil.RegisterCounterServer(suite.baseApp.MsgServiceRouter(), CounterServerImpl{t, capKey1, counterKey}) + + _, err := suite.baseApp.InitChain(&abci.RequestInitChain{ + ConsensusParams: &cmtproto.ConsensusParams{}, + }) + require.NoError(t, err) + + tx := newTxCounter(t, suite.txConfig, 0, 0) + txBytes, err := suite.txConfig.TxEncoder()(tx) + require.NoError(t, err) + + res, err := suite.baseApp.CheckTx(&abci.RequestCheckTx{Tx: txBytes}) + require.NoError(t, err) + require.True(t, res.IsOK(), fmt.Sprintf("%v", res)) + + failTx := newTxCounter(t, suite.txConfig, 1, 0) + failTxBytes, err := suite.txConfig.TxEncoder()(failTx) + require.NoError(t, err) + + failRes, err := suite.baseApp.CheckTx(&abci.RequestCheckTx{Tx: failTxBytes}) + require.NoError(t, err) + require.False(t, failRes.IsOK()) + require.Contains(t, failRes.Log, mempool.ErrMempoolTxMaxCapacity.Error()) + + checkStateStore := getCheckStateCtx(suite.baseApp).KVStore(capKey1) + require.Equal(t, int64(1), getIntFromStore(t, checkStateStore, counterKey)) + require.Equal(t, 1, pool.CountTx()) +} + func TestABCI_FinalizeBlock_DeliverTx(t *testing.T) { anteKey := []byte("ante-key") anteOpt := func(bapp *baseapp.BaseApp) { bapp.SetAnteHandler(anteHandlerTxTest(t, capKey1, anteKey)) } diff --git a/baseapp/baseapp.go b/baseapp/baseapp.go index a50238a59fe3..72e58632d66b 100644 --- a/baseapp/baseapp.go +++ b/baseapp/baseapp.go @@ -885,6 +885,9 @@ func (app *BaseApp) RunTx(mode sdk.ExecMode, txBytes []byte, tx sdk.Tx, txIndex } } + mempoolCtx := ctx + var commitAnteCache func() + if app.anteHandler != nil { var ( anteCtx sdk.Context @@ -929,16 +932,30 @@ func (app *BaseApp) RunTx(mode sdk.ExecMode, txBytes []byte, tx sdk.Tx, txIndex return gInfo, nil, nil, err } - msCache.Write() - anteEvents = events.ToABCIEvents() + commitAnteCache = func() { + if msCache != nil { + msCache.Write() + } + anteEvents = events.ToABCIEvents() + } + + if mode == execModeCheck { + mempoolCtx = ctx.WithMultiStore(msCache) + } else { + commitAnteCache() + commitAnteCache = nil + } } switch mode { case execModeCheck: - err = app.mempool.Insert(ctx, tx) - if err != nil { + if err := app.mempool.Insert(mempoolCtx, tx); err != nil { return gInfo, nil, anteEvents, err } + + if commitAnteCache != nil { + commitAnteCache() + } case execModeFinalize: err = app.mempool.Remove(tx) if err != nil && !errors.Is(err, mempool.ErrTxNotFound) {