9
9
create_block ,
10
10
create_coinbase ,
11
11
)
12
+ from test_framework .messages import DEFAULT_MEMPOOL_EXPIRY_HOURS
12
13
from test_framework .p2p import P2PTxInvStore
13
14
from 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
+ )
16
19
17
20
class ResendWalletTransactionsTest (BitcoinTestFramework ):
18
21
def set_test_params (self ):
@@ -27,7 +30,9 @@ def run_test(self):
27
30
peer_first = node .add_p2p_connection (P2PTxInvStore ())
28
31
29
32
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" ]
31
36
32
37
# Can take a few seconds due to transaction trickling
33
38
peer_first .wait_for_broadcast ([txid ])
@@ -68,6 +73,49 @@ def run_test(self):
68
73
node .setmocktime (now + 36 * 60 * 60 + 600 )
69
74
peer_second .wait_for_broadcast ([txid ])
70
75
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
+
71
119
72
120
if __name__ == '__main__' :
73
121
ResendWalletTransactionsTest ().main ()
0 commit comments