Skip to content

Commit 8ffc807

Browse files
fix: remove txs from mempool when antehandler fails in recheck (backport #20144) (#20251)
Co-authored-by: Marko <[email protected]>
1 parent a65c2f7 commit 8ffc807

File tree

2 files changed

+107
-0
lines changed

2 files changed

+107
-0
lines changed

baseapp/abci_test.go

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2388,3 +2388,104 @@ func TestOptimisticExecution(t *testing.T) {
23882388

23892389
require.Equal(t, int64(50), suite.baseApp.LastBlockHeight())
23902390
}
2391+
2392+
func TestABCI_Proposal_FailReCheckTx(t *testing.T) {
2393+
pool := mempool.NewPriorityMempool[int64](mempool.PriorityNonceMempoolConfig[int64]{
2394+
TxPriority: mempool.NewDefaultTxPriority(),
2395+
MaxTx: 0,
2396+
SignerExtractor: mempool.NewDefaultSignerExtractionAdapter(),
2397+
})
2398+
2399+
anteOpt := func(bapp *baseapp.BaseApp) {
2400+
bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) {
2401+
// always fail on recheck, just to test the recheck logic
2402+
if ctx.IsReCheckTx() {
2403+
return ctx, errors.New("recheck failed in ante handler")
2404+
}
2405+
2406+
return ctx, nil
2407+
})
2408+
}
2409+
2410+
suite := NewBaseAppSuite(t, anteOpt, baseapp.SetMempool(pool))
2411+
baseapptestutil.RegisterKeyValueServer(suite.baseApp.MsgServiceRouter(), MsgKeyValueImpl{})
2412+
baseapptestutil.RegisterCounterServer(suite.baseApp.MsgServiceRouter(), NoopCounterServerImpl{})
2413+
2414+
_, err := suite.baseApp.InitChain(&abci.RequestInitChain{
2415+
ConsensusParams: &cmtproto.ConsensusParams{},
2416+
})
2417+
require.NoError(t, err)
2418+
2419+
tx := newTxCounter(t, suite.txConfig, 0, 1)
2420+
txBytes, err := suite.txConfig.TxEncoder()(tx)
2421+
require.NoError(t, err)
2422+
2423+
reqCheckTx := abci.RequestCheckTx{
2424+
Tx: txBytes,
2425+
Type: abci.CheckTxType_New,
2426+
}
2427+
_, err = suite.baseApp.CheckTx(&reqCheckTx)
2428+
require.NoError(t, err)
2429+
2430+
tx2 := newTxCounter(t, suite.txConfig, 1, 1)
2431+
2432+
tx2Bytes, err := suite.txConfig.TxEncoder()(tx2)
2433+
require.NoError(t, err)
2434+
2435+
err = pool.Insert(sdk.Context{}, tx2)
2436+
require.NoError(t, err)
2437+
2438+
require.Equal(t, 2, pool.CountTx())
2439+
2440+
// call prepareProposal before calling recheck tx, just as a sanity check
2441+
reqPrepareProposal := abci.RequestPrepareProposal{
2442+
MaxTxBytes: 1000,
2443+
Height: 1,
2444+
}
2445+
resPrepareProposal, err := suite.baseApp.PrepareProposal(&reqPrepareProposal)
2446+
require.NoError(t, err)
2447+
require.Equal(t, 2, len(resPrepareProposal.Txs))
2448+
2449+
// call recheck on the first tx, it MUST return an error
2450+
reqReCheckTx := abci.RequestCheckTx{
2451+
Tx: txBytes,
2452+
Type: abci.CheckTxType_Recheck,
2453+
}
2454+
resp, err := suite.baseApp.CheckTx(&reqReCheckTx)
2455+
require.NoError(t, err)
2456+
require.True(t, resp.IsErr())
2457+
require.Equal(t, "recheck failed in ante handler", resp.Log)
2458+
2459+
// call prepareProposal again, should return only the second tx
2460+
resPrepareProposal, err = suite.baseApp.PrepareProposal(&reqPrepareProposal)
2461+
require.NoError(t, err)
2462+
require.Equal(t, 1, len(resPrepareProposal.Txs))
2463+
require.Equal(t, tx2Bytes, resPrepareProposal.Txs[0])
2464+
2465+
// check the mempool, it should have only the second tx
2466+
require.Equal(t, 1, pool.CountTx())
2467+
2468+
reqProposalTxBytes := [][]byte{
2469+
tx2Bytes,
2470+
}
2471+
reqProcessProposal := abci.RequestProcessProposal{
2472+
Txs: reqProposalTxBytes,
2473+
Height: reqPrepareProposal.Height,
2474+
}
2475+
2476+
resProcessProposal, err := suite.baseApp.ProcessProposal(&reqProcessProposal)
2477+
require.NoError(t, err)
2478+
require.Equal(t, abci.ResponseProcessProposal_ACCEPT, resProcessProposal.Status)
2479+
2480+
// the same txs as in PrepareProposal
2481+
res, err := suite.baseApp.FinalizeBlock(&abci.RequestFinalizeBlock{
2482+
Height: suite.baseApp.LastBlockHeight() + 1,
2483+
Txs: reqProposalTxBytes,
2484+
})
2485+
require.NoError(t, err)
2486+
2487+
require.Equal(t, 0, pool.CountTx())
2488+
2489+
require.NotEmpty(t, res.TxResults[0].Events)
2490+
require.True(t, res.TxResults[0].IsOK(), fmt.Sprintf("%v", res))
2491+
}

baseapp/baseapp.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -915,6 +915,12 @@ func (app *BaseApp) runTx(mode execMode, txBytes []byte) (gInfo sdk.GasInfo, res
915915
gasWanted = ctx.GasMeter().Limit()
916916

917917
if err != nil {
918+
if mode == execModeReCheck {
919+
// if the ante handler fails on recheck, we want to remove the tx from the mempool
920+
if mempoolErr := app.mempool.Remove(tx); mempoolErr != nil {
921+
return gInfo, nil, anteEvents, errors.Join(err, mempoolErr)
922+
}
923+
}
918924
return gInfo, nil, nil, err
919925
}
920926

0 commit comments

Comments
 (0)