Skip to content

Commit 7009a2e

Browse files
fix: remove txs from mempool when antehandler fails in recheck (backport #20144) (#20252)
Co-authored-by: Marko <[email protected]> Co-authored-by: marbar3778 <[email protected]>
1 parent 00e4273 commit 7009a2e

File tree

2 files changed

+101
-0
lines changed

2 files changed

+101
-0
lines changed

baseapp/abci_test.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010

1111
dbm "github.com/cometbft/cometbft-db"
1212
abci "github.com/cometbft/cometbft/abci/types"
13+
cmtproto "github.com/cometbft/cometbft/proto/tendermint/types"
1314
tmproto "github.com/cometbft/cometbft/proto/tendermint/types"
1415
"github.com/cosmos/gogoproto/jsonpb"
1516
"github.com/stretchr/testify/require"
@@ -1693,3 +1694,97 @@ func TestABCI_Proposal_Reset_State_Between_Calls(t *testing.T) {
16931694
Header: tmproto.Header{Height: suite.baseApp.LastBlockHeight() + 1},
16941695
})
16951696
}
1697+
1698+
func TestABCI_Proposal_FailReCheckTx(t *testing.T) {
1699+
pool := mempool.NewSenderNonceMempool()
1700+
1701+
anteOpt := func(bapp *baseapp.BaseApp) {
1702+
bapp.SetAnteHandler(func(ctx sdk.Context, tx sdk.Tx, simulate bool) (sdk.Context, error) {
1703+
// always fail on recheck, just to test the recheck logic
1704+
if ctx.IsReCheckTx() {
1705+
return ctx, errors.New("recheck failed in ante handler")
1706+
}
1707+
1708+
return ctx, nil
1709+
})
1710+
}
1711+
1712+
suite := NewBaseAppSuite(t, anteOpt, baseapp.SetMempool(pool))
1713+
baseapptestutil.RegisterKeyValueServer(suite.baseApp.MsgServiceRouter(), MsgKeyValueImpl{})
1714+
baseapptestutil.RegisterCounterServer(suite.baseApp.MsgServiceRouter(), NoopCounterServerImpl{})
1715+
1716+
suite.baseApp.InitChain(abci.RequestInitChain{
1717+
ConsensusParams: &cmtproto.ConsensusParams{},
1718+
})
1719+
1720+
tx := newTxCounter(t, suite.txConfig, 0, 1)
1721+
txBytes, err := suite.txConfig.TxEncoder()(tx)
1722+
require.NoError(t, err)
1723+
1724+
reqCheckTx := abci.RequestCheckTx{
1725+
Tx: txBytes,
1726+
Type: abci.CheckTxType_New,
1727+
}
1728+
_ = suite.baseApp.CheckTx(reqCheckTx)
1729+
1730+
tx2 := newTxCounter(t, suite.txConfig, 1, 1)
1731+
1732+
tx2Bytes, err := suite.txConfig.TxEncoder()(tx2)
1733+
require.NoError(t, err)
1734+
1735+
err = pool.Insert(sdk.Context{}, tx2)
1736+
require.NoError(t, err)
1737+
1738+
require.Equal(t, 2, pool.CountTx())
1739+
1740+
// call prepareProposal before calling recheck tx, just as a sanity check
1741+
reqPrepareProposal := abci.RequestPrepareProposal{
1742+
MaxTxBytes: 1000,
1743+
Height: 1,
1744+
}
1745+
resPrepareProposal := suite.baseApp.PrepareProposal(reqPrepareProposal)
1746+
require.Equal(t, 2, len(resPrepareProposal.Txs))
1747+
1748+
// call recheck on the first tx, it MUST return an error
1749+
reqReCheckTx := abci.RequestCheckTx{
1750+
Tx: txBytes,
1751+
Type: abci.CheckTxType_Recheck,
1752+
}
1753+
resp := suite.baseApp.CheckTx(reqReCheckTx)
1754+
1755+
require.True(t, resp.IsErr())
1756+
require.Equal(t, "recheck failed in ante handler", resp.Log)
1757+
1758+
// call prepareProposal again, should return only the second tx
1759+
resPrepareProposal = suite.baseApp.PrepareProposal(reqPrepareProposal)
1760+
require.Equal(t, 1, len(resPrepareProposal.Txs))
1761+
require.Equal(t, tx2Bytes, resPrepareProposal.Txs[0])
1762+
1763+
// check the mempool, it should have only the second tx
1764+
require.Equal(t, 1, pool.CountTx())
1765+
1766+
reqProposalTxBytes := tx2Bytes
1767+
1768+
reqProcessProposal := abci.RequestProcessProposal{
1769+
Txs: [][]byte{reqProposalTxBytes},
1770+
Height: reqPrepareProposal.Height,
1771+
}
1772+
1773+
resProcessProposal := suite.baseApp.ProcessProposal(reqProcessProposal)
1774+
require.Equal(t, abci.ResponseProcessProposal_ACCEPT, resProcessProposal.Status)
1775+
1776+
suite.baseApp.BeginBlock(abci.RequestBeginBlock{
1777+
Header: tmproto.Header{Height: suite.baseApp.LastBlockHeight() + 1},
1778+
})
1779+
1780+
// the same txs as in PrepareProposal
1781+
res := suite.baseApp.DeliverTx(abci.RequestDeliverTx{
1782+
Tx: reqProposalTxBytes,
1783+
})
1784+
require.NoError(t, err)
1785+
1786+
require.Equal(t, 0, pool.CountTx())
1787+
1788+
require.NotEmpty(t, res.Events)
1789+
require.True(t, res.IsOK(), fmt.Sprintf("%v", res))
1790+
}

baseapp/baseapp.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -717,6 +717,12 @@ func (app *BaseApp) runTx(mode runTxMode, txBytes []byte) (gInfo sdk.GasInfo, re
717717
gasWanted = ctx.GasMeter().Limit()
718718

719719
if err != nil {
720+
if mode == runTxModeReCheck {
721+
// if the ante handler fails on recheck, we want to remove the tx from the mempool
722+
if mempoolErr := app.mempool.Remove(tx); mempoolErr != nil {
723+
return gInfo, nil, anteEvents, 0, fmt.Errorf("error: %v, mempool error: %w", err, mempoolErr)
724+
}
725+
}
720726
return gInfo, nil, nil, 0, err
721727
}
722728

0 commit comments

Comments
 (0)