@@ -3294,3 +3294,62 @@ def compare_unfinished_blocks(block1: UnfinishedBlock, block2: UnfinishedBlock)
32943294 # Final assertion to check the entire block
32953295 assert block1 == block2 , "The entire block objects are not identical"
32963296 return True
3297+
3298+
3299+ @pytest .mark .anyio
3300+ @pytest .mark .parametrize (
3301+ "condition, error" ,
3302+ [
3303+ (ConditionOpcode .ASSERT_HEIGHT_RELATIVE , "ASSERT_HEIGHT_RELATIVE_FAILED" ),
3304+ (ConditionOpcode .ASSERT_HEIGHT_ABSOLUTE , "ASSERT_HEIGHT_ABSOLUTE_FAILED" ),
3305+ ],
3306+ )
3307+ async def test_pending_tx_cache_retry_on_new_peak (
3308+ condition : ConditionOpcode , error : str , blockchain_constants : ConsensusConstants , caplog : pytest .LogCaptureFixture
3309+ ) -> None :
3310+ """
3311+ Covers PendingTXCache items that are placed there due to unmet relative or
3312+ absolute height conditions, to make sure those items get retried at peak
3313+ post processing when those conditions are met.
3314+ """
3315+ async with setup_simulators_and_wallets (1 , 0 , blockchain_constants ) as new :
3316+ full_node_api = new .simulators [0 ].peer_api
3317+ bt = new .bt
3318+ wallet = WalletTool (test_constants )
3319+ ph = wallet .get_new_puzzlehash ()
3320+ blocks = bt .get_consecutive_blocks (
3321+ 3 , guarantee_transaction_block = True , farmer_reward_puzzle_hash = ph , pool_reward_puzzle_hash = ph
3322+ )
3323+ for block in blocks :
3324+ await full_node_api .full_node .add_block (block )
3325+ peak = full_node_api .full_node .blockchain .get_peak ()
3326+ assert peak is not None
3327+ current_height = peak .height
3328+ # Create a transaction with a height condition that makes it pending
3329+ coin = blocks [- 1 ].get_included_reward_coins ()[0 ]
3330+ if condition == ConditionOpcode .ASSERT_HEIGHT_RELATIVE :
3331+ condition_height = 1
3332+ else :
3333+ condition_height = current_height + 1
3334+ condition_dic = {condition : [ConditionWithArgs (condition , [int_to_bytes (condition_height )])]}
3335+ sb = wallet .generate_signed_transaction (uint64 (42 ), ph , coin , condition_dic )
3336+ sb_name = sb .name ()
3337+ # Send the transaction
3338+ res = await full_node_api .send_transaction (SendTransaction (sb ))
3339+ assert res is not None
3340+ assert ProtocolMessageTypes (res .type ) == ProtocolMessageTypes .transaction_ack
3341+ transaction_ack = TransactionAck .from_bytes (res .data )
3342+ assert transaction_ack .status == MempoolInclusionStatus .PENDING .value
3343+ assert transaction_ack .error == error
3344+ # Make sure it ends up in the pending cache, not the mempool
3345+ assert full_node_api .full_node .mempool_manager .get_mempool_item (sb_name , include_pending = False ) is None
3346+ assert full_node_api .full_node .mempool_manager .get_mempool_item (sb_name , include_pending = True ) is not None
3347+ # Advance peak to meet the asserted height condition
3348+ with caplog .at_level (logging .DEBUG ):
3349+ blocks = bt .get_consecutive_blocks (2 , block_list_input = blocks , guarantee_transaction_block = True )
3350+ for block in blocks :
3351+ await full_node_api .full_node .add_block (block )
3352+ # This should trigger peak post processing with the added transaction
3353+ assert f"Added transaction to mempool: { sb_name } \n " in caplog .text
3354+ # Make sure the transaction was retried and got added to the mempool
3355+ assert full_node_api .full_node .mempool_manager .get_mempool_item (sb_name , include_pending = False ) is not None
0 commit comments