@@ -1183,3 +1183,97 @@ func TestReapMaxBytesMaxGas_EVMFirst(t *testing.T) {
11831183 require .True (t , strings .HasPrefix (string (reapedTxs [3 ]), "sender" ), "Fourth tx should be non-EVM: %s" , string (reapedTxs [3 ]))
11841184 require .True (t , strings .HasPrefix (string (reapedTxs [4 ]), "sender" ), "Fifth tx should be non-EVM: %s" , string (reapedTxs [4 ]))
11851185}
1186+
1187+ func TestBlockFailedTxNotReAdmittedAfterSecondFailure (t * testing.T ) {
1188+ ctx , cancel := context .WithCancel (context .Background ())
1189+ defer cancel ()
1190+
1191+ app := & application {Application : kvstore .NewApplication ()}
1192+ txmp := setup (t , app , 500 )
1193+
1194+ tx := types .Tx ("sender-0-0=key=1000" )
1195+
1196+ // Submit the tx — should enter the mempool
1197+ require .NoError (t , txmp .CheckTx (ctx , tx , nil , TxInfo {SenderID : 0 }))
1198+ require .Equal (t , 1 , txmp .Size ())
1199+
1200+ // Simulate block inclusion where the tx fails (non-OK code)
1201+ txmp .Lock ()
1202+ require .NoError (t , txmp .Update (ctx , 1 , types.Txs {tx }, []* abci.ExecTxResult {
1203+ {Code : 11 }, // out of gas
1204+ }, nil , nil , true ))
1205+ txmp .Unlock ()
1206+
1207+ // Tx should be removed from the mempool
1208+ require .Equal (t , 0 , txmp .Size ())
1209+
1210+ // First failure: tx should have been removed from cache, allowing re-entry
1211+ require .NoError (t , txmp .CheckTx (ctx , tx , nil , TxInfo {SenderID : 0 }))
1212+ require .Equal (t , 1 , txmp .Size ())
1213+
1214+ // Simulate a second block failure for the same tx
1215+ txmp .Lock ()
1216+ require .NoError (t , txmp .Update (ctx , 2 , types.Txs {tx }, []* abci.ExecTxResult {
1217+ {Code : 11 }, // out of gas again
1218+ }, nil , nil , true ))
1219+ txmp .Unlock ()
1220+
1221+ require .Equal (t , 0 , txmp .Size ())
1222+
1223+ // Second failure: tx should remain in cache — CheckTx should reject it
1224+ err := txmp .CheckTx (ctx , tx , nil , TxInfo {SenderID : 0 })
1225+ require .Equal (t , types .ErrTxInCache , err )
1226+ require .Equal (t , 0 , txmp .Size ())
1227+
1228+ // A different tx (different hash) should still be admitted
1229+ differentTx := types .Tx ("sender-0-0=key=2000" )
1230+ require .NoError (t , txmp .CheckTx (ctx , differentTx , nil , TxInfo {SenderID : 0 }))
1231+ require .Equal (t , 1 , txmp .Size ())
1232+ }
1233+
1234+ func TestBlockFailedTxTrackerClearedOnSuccess (t * testing.T ) {
1235+ ctx , cancel := context .WithCancel (context .Background ())
1236+ defer cancel ()
1237+
1238+ app := & application {Application : kvstore .NewApplication ()}
1239+ txmp := setup (t , app , 500 )
1240+
1241+ tx := types .Tx ("sender-0-0=key=1000" )
1242+ txKey := tx .Key ()
1243+
1244+ // Submit and fail once in a block
1245+ require .NoError (t , txmp .CheckTx (ctx , tx , nil , TxInfo {SenderID : 0 }))
1246+ txmp .Lock ()
1247+ require .NoError (t , txmp .Update (ctx , 1 , types.Txs {tx }, []* abci.ExecTxResult {
1248+ {Code : 11 },
1249+ }, nil , nil , true ))
1250+ txmp .Unlock ()
1251+
1252+ // Re-enter the mempool (first failure allows retry)
1253+ require .NoError (t , txmp .CheckTx (ctx , tx , nil , TxInfo {SenderID : 0 }))
1254+
1255+ // This time the tx succeeds in the block
1256+ txmp .Lock ()
1257+ require .NoError (t , txmp .Update (ctx , 2 , types.Txs {tx }, []* abci.ExecTxResult {
1258+ {Code : abci .CodeTypeOK },
1259+ }, nil , nil , true ))
1260+ txmp .Unlock ()
1261+
1262+ // Success clears the failure tracker. Simulate LRU eviction of the
1263+ // main cache entry so we can verify the tracker was actually reset.
1264+ txmp .cache .Remove (txKey )
1265+
1266+ // Tx should now be re-admittable
1267+ require .NoError (t , txmp .CheckTx (ctx , tx , nil , TxInfo {SenderID : 0 }))
1268+
1269+ // Fail again in a block — this should be treated as a fresh first failure
1270+ txmp .Lock ()
1271+ require .NoError (t , txmp .Update (ctx , 3 , types.Txs {tx }, []* abci.ExecTxResult {
1272+ {Code : 11 },
1273+ }, nil , nil , true ))
1274+ txmp .Unlock ()
1275+
1276+ // First-failure grace should be restored: tx allowed to re-enter
1277+ require .NoError (t , txmp .CheckTx (ctx , tx , nil , TxInfo {SenderID : 0 }))
1278+ require .Equal (t , 1 , txmp .Size ())
1279+ }
0 commit comments