Skip to content

Commit 599ae55

Browse files
fix: remove txs from mempool when antehandler fails in recheck (#20144)
Co-authored-by: Facundo <[email protected]>
1 parent 8f10576 commit 599ae55

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
@@ -2398,3 +2398,104 @@ func TestOptimisticExecution(t *testing.T) {
23982398

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

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)