99 create_block ,
1010 create_coinbase ,
1111)
12+ from test_framework .messages import DEFAULT_MEMPOOL_EXPIRY_HOURS
1213from test_framework .p2p import P2PTxInvStore
1314from test_framework .test_framework import BitcoinTestFramework
14- from test_framework .util import assert_equal
15-
15+ from test_framework .util import (
16+ assert_equal ,
17+ assert_raises_rpc_error ,
18+ )
1619
1720class ResendWalletTransactionsTest (BitcoinTestFramework ):
1821 def set_test_params (self ):
@@ -27,7 +30,9 @@ def run_test(self):
2730 peer_first = node .add_p2p_connection (P2PTxInvStore ())
2831
2932 self .log .info ("Create a new transaction and wait until it's broadcast" )
30- txid = node .sendtoaddress (node .getnewaddress (), 1 )
33+ parent_utxo , indep_utxo = node .listunspent ()[:2 ]
34+ addr = node .getnewaddress ()
35+ txid = node .send (outputs = [{addr : 1 }], options = {"inputs" : [parent_utxo ]})["txid" ]
3136
3237 # Can take a few seconds due to transaction trickling
3338 peer_first .wait_for_broadcast ([txid ])
@@ -68,6 +73,49 @@ def run_test(self):
6873 node .setmocktime (now + 36 * 60 * 60 + 600 )
6974 peer_second .wait_for_broadcast ([txid ])
7075
76+ self .log .info ("Chain of unconfirmed not-in-mempool txs are rebroadcast" )
77+ # This tests that the node broadcasts the parent transaction before the child transaction.
78+ # To test that scenario, we need a method to reliably get a child transaction placed
79+ # in mapWallet positioned before the parent. We cannot predict the position in mapWallet,
80+ # but we can observe it using listreceivedbyaddress and other related RPCs.
81+ #
82+ # So we will create the child transaction, use listreceivedbyaddress to see what the
83+ # ordering of mapWallet is, if the child is not before the parent, we will create a new
84+ # child (via bumpfee) and remove the old child (via removeprunedfunds) until we get the
85+ # ordering of child before parent.
86+ child_txid = node .send (outputs = [{addr : 0.5 }], options = {"inputs" : [{"txid" :txid , "vout" :0 }]})["txid" ]
87+ while True :
88+ txids = node .listreceivedbyaddress (minconf = 0 , address_filter = addr )[0 ]["txids" ]
89+ if txids == [child_txid , txid ]:
90+ break
91+ bumped = node .bumpfee (child_txid )
92+ node .removeprunedfunds (child_txid )
93+ child_txid = bumped ["txid" ]
94+ entry_time = node .getmempoolentry (child_txid )["time" ]
95+
96+ block_time = entry_time + 6 * 60
97+ node .setmocktime (block_time )
98+ block = create_block (int (node .getbestblockhash (), 16 ), create_coinbase (node .getblockcount () + 1 ), block_time )
99+ block .solve ()
100+ node .submitblock (block .serialize ().hex ())
101+ node .syncwithvalidationinterfacequeue ()
102+
103+ # Evict these txs from the mempool
104+ evict_time = block_time + 60 * 60 * DEFAULT_MEMPOOL_EXPIRY_HOURS + 5
105+ node .setmocktime (evict_time )
106+ indep_send = node .send (outputs = [{node .getnewaddress (): 1 }], options = {"inputs" : [indep_utxo ]})
107+ node .syncwithvalidationinterfacequeue ()
108+ node .getmempoolentry (indep_send ["txid" ])
109+ assert_raises_rpc_error (- 5 , "Transaction not in mempool" , node .getmempoolentry , txid )
110+ assert_raises_rpc_error (- 5 , "Transaction not in mempool" , node .getmempoolentry , child_txid )
111+
112+ # Rebroadcast and check that parent and child are both in the mempool
113+ with node .assert_debug_log (['resubmit 2 unconfirmed transactions' ]):
114+ node .setmocktime (evict_time + 36 * 60 * 60 ) # 36 hrs is the upper limit of the resend timer
115+ node .mockscheduler (60 )
116+ node .getmempoolentry (txid )
117+ node .getmempoolentry (child_txid )
118+
71119
72120if __name__ == '__main__' :
73121 ResendWalletTransactionsTest ().main ()
0 commit comments