@@ -29,70 +29,76 @@ def skip_test_if_missing_module(self):
2929 self .skip_if_no_wallet ()
3030
3131 def run_test (self ):
32+ # create two wallets to tests conflicts from both sender's and receiver's sides
33+ alice = self .nodes [0 ].get_wallet_rpc (self .default_wallet_name )
34+ self .nodes [0 ].createwallet (wallet_name = "bob" )
35+ bob = self .nodes [0 ].get_wallet_rpc ("bob" )
36+
3237 self .generate (self .nodes [1 ], COINBASE_MATURITY )
33- balance = self . nodes [ 0 ] .getbalance ()
34- txA = self . nodes [ 0 ]. sendtoaddress (self . nodes [ 0 ] .getnewaddress (), Decimal ("10" ))
35- txB = self . nodes [ 0 ]. sendtoaddress (self . nodes [ 0 ] .getnewaddress (), Decimal ("10" ))
36- txC = self . nodes [ 0 ]. sendtoaddress (self . nodes [ 0 ] .getnewaddress (), Decimal ("10" ))
38+ balance = alice .getbalance ()
39+ txA = alice . sendtoaddress (alice .getnewaddress (), Decimal ("10" ))
40+ txB = alice . sendtoaddress (alice .getnewaddress (), Decimal ("10" ))
41+ txC = alice . sendtoaddress (alice .getnewaddress (), Decimal ("10" ))
3742 self .sync_mempools ()
3843 self .generate (self .nodes [1 ], 1 )
3944
4045 # Can not abandon non-wallet transaction
41- assert_raises_rpc_error (- 5 , 'Invalid or non-wallet transaction id' , lambda : self . nodes [ 0 ] .abandontransaction (txid = 'ff' * 32 ))
46+ assert_raises_rpc_error (- 5 , 'Invalid or non-wallet transaction id' , lambda : alice .abandontransaction (txid = 'ff' * 32 ))
4247 # Can not abandon confirmed transaction
43- assert_raises_rpc_error (- 5 , 'Transaction not eligible for abandonment' , lambda : self . nodes [ 0 ] .abandontransaction (txid = txA ))
48+ assert_raises_rpc_error (- 5 , 'Transaction not eligible for abandonment' , lambda : alice .abandontransaction (txid = txA ))
4449
45- newbalance = self . nodes [ 0 ] .getbalance ()
50+ newbalance = alice .getbalance ()
4651 assert balance - newbalance < Decimal ("0.001" ) #no more than fees lost
4752 balance = newbalance
4853
4954 # Disconnect nodes so node0's transactions don't get into node1's mempool
5055 self .disconnect_nodes (0 , 1 )
5156
5257 # Identify the 10btc outputs
53- nA = next (tx_out ["vout" ] for tx_out in self . nodes [ 0 ] .gettransaction (txA )["details" ] if tx_out ["amount" ] == Decimal ("10" ))
54- nB = next (tx_out ["vout" ] for tx_out in self . nodes [ 0 ] .gettransaction (txB )["details" ] if tx_out ["amount" ] == Decimal ("10" ))
55- nC = next (tx_out ["vout" ] for tx_out in self . nodes [ 0 ] .gettransaction (txC )["details" ] if tx_out ["amount" ] == Decimal ("10" ))
58+ nA = next (tx_out ["vout" ] for tx_out in alice .gettransaction (txA )["details" ] if tx_out ["amount" ] == Decimal ("10" ))
59+ nB = next (tx_out ["vout" ] for tx_out in alice .gettransaction (txB )["details" ] if tx_out ["amount" ] == Decimal ("10" ))
60+ nC = next (tx_out ["vout" ] for tx_out in alice .gettransaction (txC )["details" ] if tx_out ["amount" ] == Decimal ("10" ))
5661
5762 inputs = []
5863 # spend 10btc outputs from txA and txB
5964 inputs .append ({"txid" : txA , "vout" : nA })
6065 inputs .append ({"txid" : txB , "vout" : nB })
6166 outputs = {}
6267
63- outputs [self . nodes [ 0 ] .getnewaddress ()] = Decimal ("14.99998" )
64- outputs [self . nodes [ 1 ] .getnewaddress ()] = Decimal ("5" )
65- signed = self . nodes [ 0 ]. signrawtransactionwithwallet (self . nodes [ 0 ] .createrawtransaction (inputs , outputs ))
68+ outputs [alice .getnewaddress ()] = Decimal ("14.99998" )
69+ outputs [bob .getnewaddress ()] = Decimal ("5" )
70+ signed = alice . signrawtransactionwithwallet (alice .createrawtransaction (inputs , outputs ))
6671 txAB1 = self .nodes [0 ].sendrawtransaction (signed ["hex" ])
6772
6873 # Identify the 14.99998btc output
69- nAB = next (tx_out ["vout" ] for tx_out in self . nodes [ 0 ] .gettransaction (txAB1 )["details" ] if tx_out ["amount" ] == Decimal ("14.99998" ))
74+ nAB = next (tx_out ["vout" ] for tx_out in alice .gettransaction (txAB1 )["details" ] if tx_out ["amount" ] == Decimal ("14.99998" ))
7075
7176 #Create a child tx spending AB1 and C
7277 inputs = []
7378 inputs .append ({"txid" : txAB1 , "vout" : nAB })
7479 inputs .append ({"txid" : txC , "vout" : nC })
7580 outputs = {}
76- outputs [self . nodes [ 0 ] .getnewaddress ()] = Decimal ("24.9996" )
77- signed2 = self . nodes [ 0 ]. signrawtransactionwithwallet (self . nodes [ 0 ] .createrawtransaction (inputs , outputs ))
81+ outputs [alice .getnewaddress ()] = Decimal ("24.9996" )
82+ signed2 = alice . signrawtransactionwithwallet (alice .createrawtransaction (inputs , outputs ))
7883 txABC2 = self .nodes [0 ].sendrawtransaction (signed2 ["hex" ])
7984
8085 # Create a child tx spending ABC2
8186 signed3_change = Decimal ("24.999" )
8287 inputs = [{"txid" : txABC2 , "vout" : 0 }]
83- outputs = {self . nodes [ 0 ] .getnewaddress (): signed3_change }
84- signed3 = self . nodes [ 0 ]. signrawtransactionwithwallet (self . nodes [ 0 ] .createrawtransaction (inputs , outputs ))
88+ outputs = {alice .getnewaddress (): signed3_change }
89+ signed3 = alice . signrawtransactionwithwallet (alice .createrawtransaction (inputs , outputs ))
8590 # note tx is never directly referenced, only abandoned as a child of the above
8691 self .nodes [0 ].sendrawtransaction (signed3 ["hex" ])
8792
8893 # In mempool txs from self should increase balance from change
89- newbalance = self . nodes [ 0 ] .getbalance ()
94+ newbalance = alice .getbalance ()
9095 assert_equal (newbalance , balance - Decimal ("30" ) + signed3_change )
9196 balance = newbalance
9297
9398 # Restart the node with a higher min relay fee so the parent tx is no longer in mempool
9499 # TODO: redo with eviction
95100 self .restart_node (0 , extra_args = ["-minrelaytxfee=0.0001" ])
101+ alice = self .nodes [0 ].get_wallet_rpc (self .default_wallet_name )
96102 assert self .nodes [0 ].getmempoolinfo ()['loaded' ]
97103
98104 # Verify txs no longer in either node's mempool
@@ -101,25 +107,25 @@ def run_test(self):
101107
102108 # Not in mempool txs from self should only reduce balance
103109 # inputs are still spent, but change not received
104- newbalance = self . nodes [ 0 ] .getbalance ()
110+ newbalance = alice .getbalance ()
105111 assert_equal (newbalance , balance - signed3_change )
106112 # Unconfirmed received funds that are not in mempool, also shouldn't show
107113 # up in unconfirmed balance
108- balances = self . nodes [ 0 ] .getbalances ()['mine' ]
114+ balances = alice .getbalances ()['mine' ]
109115 assert_equal (balances ['untrusted_pending' ] + balances ['trusted' ], newbalance )
110116 # Also shouldn't show up in listunspent
111- assert not txABC2 in [utxo ["txid" ] for utxo in self . nodes [ 0 ] .listunspent (0 )]
117+ assert not txABC2 in [utxo ["txid" ] for utxo in alice .listunspent (0 )]
112118 balance = newbalance
113119
114120 # Abandon original transaction and verify inputs are available again
115121 # including that the child tx was also abandoned
116- self . nodes [ 0 ] .abandontransaction (txAB1 )
117- newbalance = self . nodes [ 0 ] .getbalance ()
122+ alice .abandontransaction (txAB1 )
123+ newbalance = alice .getbalance ()
118124 assert_equal (newbalance , balance + Decimal ("30" ))
119125 balance = newbalance
120126
121127 self .log .info ("Check abandoned transactions in listsinceblock" )
122- listsinceblock = self . nodes [ 0 ] .listsinceblock ()
128+ listsinceblock = alice .listsinceblock ()
123129 txAB1_listsinceblock = [d for d in listsinceblock ['transactions' ] if d ['txid' ] == txAB1 and d ['category' ] == 'send' ]
124130 for tx in txAB1_listsinceblock :
125131 assert_equal (tx ['abandoned' ], True )
@@ -128,49 +134,53 @@ def run_test(self):
128134
129135 # Verify that even with a low min relay fee, the tx is not reaccepted from wallet on startup once abandoned
130136 self .restart_node (0 , extra_args = ["-minrelaytxfee=0.00001" ])
137+ alice = self .nodes [0 ].get_wallet_rpc (self .default_wallet_name )
131138 assert self .nodes [0 ].getmempoolinfo ()['loaded' ]
132139
133140 assert_equal (len (self .nodes [0 ].getrawmempool ()), 0 )
134- assert_equal (self . nodes [ 0 ] .getbalance (), balance )
141+ assert_equal (alice .getbalance (), balance )
135142
136143 # But if it is received again then it is unabandoned
137144 # And since now in mempool, the change is available
138145 # But its child tx remains abandoned
139146 self .nodes [0 ].sendrawtransaction (signed ["hex" ])
140- newbalance = self . nodes [ 0 ] .getbalance ()
147+ newbalance = alice .getbalance ()
141148 assert_equal (newbalance , balance - Decimal ("20" ) + Decimal ("14.99998" ))
142149 balance = newbalance
143150
144151 # Send child tx again so it is unabandoned
145152 self .nodes [0 ].sendrawtransaction (signed2 ["hex" ])
146- newbalance = self . nodes [ 0 ] .getbalance ()
153+ newbalance = alice .getbalance ()
147154 assert_equal (newbalance , balance - Decimal ("10" ) - Decimal ("14.99998" ) + Decimal ("24.9996" ))
148155 balance = newbalance
149156
150157 # Remove using high relay fee again
151158 self .restart_node (0 , extra_args = ["-minrelaytxfee=0.0001" ])
159+ alice = self .nodes [0 ].get_wallet_rpc (self .default_wallet_name )
152160 assert self .nodes [0 ].getmempoolinfo ()['loaded' ]
153161 assert_equal (len (self .nodes [0 ].getrawmempool ()), 0 )
154- newbalance = self . nodes [ 0 ] .getbalance ()
162+ newbalance = alice .getbalance ()
155163 assert_equal (newbalance , balance - Decimal ("24.9996" ))
156164 balance = newbalance
157165
158166 self .log .info ("Test transactions conflicted by a double spend" )
167+ self .nodes [0 ].loadwallet ("bob" )
168+ bob = self .nodes [0 ].get_wallet_rpc ("bob" )
169+
159170 # Create a double spend of AB1 by spending again from only A's 10 output
160171 # Mine double spend from node 1
161172 inputs = []
162173 inputs .append ({"txid" : txA , "vout" : nA })
163174 outputs = {}
164- outputs [self .nodes [1 ].getnewaddress ()] = Decimal ("9.9999" )
165- tx = self .nodes [0 ].createrawtransaction (inputs , outputs )
166- signed = self .nodes [0 ].signrawtransactionwithwallet (tx )
167- self .nodes [1 ].sendrawtransaction (signed ["hex" ])
168- self .generate (self .nodes [1 ], 1 , sync_fun = self .no_op )
169-
175+ outputs [self .nodes [1 ].getnewaddress ()] = Decimal ("3.9999" )
176+ outputs [bob .getnewaddress ()] = Decimal ("5.9999" )
177+ tx = alice .createrawtransaction (inputs , outputs )
178+ signed = alice .signrawtransactionwithwallet (tx )
179+ double_spend_txid = self .nodes [1 ].sendrawtransaction (signed ["hex" ])
170180 self .connect_nodes (0 , 1 )
171- self .sync_blocks ( )
181+ self .generate ( self . nodes [ 1 ], 1 )
172182
173- tx_list = self . nodes [ 0 ] .listtransactions ()
183+ tx_list = alice .listtransactions ()
174184
175185 conflicted = [tx for tx in tx_list if tx ["confirmations" ] < 0 ]
176186 assert_equal (4 , len (conflicted ))
@@ -179,7 +189,7 @@ def run_test(self):
179189 assert_equal (2 , len (wallet_conflicts ))
180190
181191 double_spends = [tx for tx in tx_list if tx ["walletconflicts" ] and tx ["confirmations" ] > 0 ]
182- assert_equal (1 , len (double_spends ))
192+ assert_equal (2 , len (double_spends )) # one for each output
183193 double_spend = double_spends [0 ]
184194
185195 # Test the properties of the conflicted transactions, i.e. with confirmations < 0.
@@ -198,16 +208,27 @@ def run_test(self):
198208 assert_equal (double_spend ["walletconflicts" ], [tx ["txid" ]])
199209 assert_equal (tx ["walletconflicts" ], [double_spend ["txid" ]])
200210
211+ # Test walletconflicts on the receiver's side
212+ txinfo = bob .gettransaction (txAB1 )
213+ assert_equal (txinfo ['confirmations' ], - 1 )
214+ assert_equal (txinfo ['walletconflicts' ], [double_spend ['txid' ]])
215+
216+ double_spends = [tx for tx in bob .listtransactions () if tx ["walletconflicts" ] and tx ["confirmations" ] > 0 ]
217+ assert_equal (1 , len (double_spends ))
218+ double_spend = double_spends [0 ]
219+ assert_equal (double_spend_txid , double_spend ['txid' ])
220+ assert_equal (double_spend ["walletconflicts" ], [txAB1 ])
221+
201222 # Verify that B and C's 10 BTC outputs are available for spending again because AB1 is now conflicted
202- newbalance = self . nodes [ 0 ] .getbalance ()
223+ newbalance = alice .getbalance ()
203224 assert_equal (newbalance , balance + Decimal ("20" ))
204225 balance = newbalance
205226
206227 # There is currently a minor bug around this and so this test doesn't work. See Issue #7315
207228 # Invalidate the block with the double spend and B's 10 BTC output should no longer be available
208229 # Don't think C's should either
209230 self .nodes [0 ].invalidateblock (self .nodes [0 ].getbestblockhash ())
210- newbalance = self . nodes [ 0 ] .getbalance ()
231+ newbalance = alice .getbalance ()
211232 #assert_equal(newbalance, balance - Decimal("10"))
212233 self .log .info ("If balance has not declined after invalidateblock then out of mempool wallet tx which is no longer" )
213234 self .log .info ("conflicted has not resumed causing its inputs to be seen as spent. See Issue #7315" )
0 commit comments