1111)
1212from test_framework .p2p import P2PTxInvStore
1313from test_framework .test_framework import BitcoinTestFramework
14- from test_framework .util import assert_equal
15-
14+ from test_framework .util import (
15+ assert_equal ,
16+ assert_raises_rpc_error ,
17+ )
1618
1719class ResendWalletTransactionsTest (BitcoinTestFramework ):
1820 def set_test_params (self ):
@@ -27,7 +29,10 @@ def run_test(self):
2729 peer_first = node .add_p2p_connection (P2PTxInvStore ())
2830
2931 self .log .info ("Create a new transaction and wait until it's broadcast" )
30- txid = node .sendtoaddress (node .getnewaddress (), 1 )
32+ unspent = node .listunspent ()
33+ parent_utxo = unspent [0 ]
34+ indep_utxo = unspent [1 ]
35+ txid = node .send (outputs = [{node .getnewaddress (): 1 }], options = {"inputs" : [parent_utxo ]})["txid" ]
3136
3237 # Wallet rebroadcast is first scheduled 1 sec after startup (see
3338 # nNextResend in ResendWalletTransactions()). Tell scheduler to call
@@ -74,6 +79,41 @@ def run_test(self):
7479 node .setmocktime (now + 36 * 60 * 60 + 600 )
7580 peer_second .wait_for_broadcast ([txid ])
7681
82+ self .log .info ("Chain of unconfirmed not-in-mempool txs are rebroadcast" )
83+ # Ideally we would add a transaction that would spend the existing transaction and also be
84+ # in mapWallet before it. However mapWallet is a std::unsorted_map which means that it is
85+ # hard for us to externally construct such a transaction. So we will ignore that fact and
86+ # rely on the fact that because there are only two items in mapWallet, 50% of the time the
87+ # child will be placed before the parent in mapWallet. The incorrect behavior that this
88+ # test tries to catch should thus fail intermittently at a rate of 50%.
89+ send_res = node .send (outputs = [{node .getnewaddress (): 0.5 }], options = {"inputs" : [{"txid" :txid , "vout" :0 }], "add_to_wallet" : False })
90+ child_txid = send_res ["txid" ]
91+ node .sendrawtransaction (send_res ["hex" ])
92+ entry_time = node .getmempoolentry (child_txid )["time" ]
93+
94+ block_time = entry_time + 6 * 60
95+ node .setmocktime (block_time )
96+ block = create_block (int (node .getbestblockhash (), 16 ), create_coinbase (node .getblockcount () + 1 ), block_time )
97+ block .solve ()
98+ node .submitblock (block .serialize ().hex ())
99+ node .syncwithvalidationinterfacequeue ()
100+
101+ # Evict these txs from the mempool
102+ evict_time = block_time + 60 * 60 * 336 + 5
103+ node .setmocktime (evict_time )
104+ indep_send = node .send (outputs = [{node .getnewaddress (): 1 }], options = {"inputs" : [indep_utxo ]})
105+ node .syncwithvalidationinterfacequeue ()
106+ node .getmempoolentry (indep_send ["txid" ])
107+ assert_raises_rpc_error (- 5 , "Transaction not in mempool" , node .getmempoolentry , txid )
108+ assert_raises_rpc_error (- 5 , "Transaction not in mempool" , node .getmempoolentry , child_txid )
109+
110+ # Rebroadcast and check that parent and child are both in the mempool
111+ with node .assert_debug_log (['ResendWalletTransactions: resubmit 2 unconfirmed transactions' ]):
112+ node .setmocktime (evict_time + 36 * 60 * 60 )
113+ node .mockscheduler (1 )
114+ node .getmempoolentry (txid )
115+ node .getmempoolentry (child_txid )
116+
77117
78118if __name__ == '__main__' :
79119 ResendWalletTransactionsTest ().main ()
0 commit comments