wallet_conflicts.py raw

   1  #!/usr/bin/env python3
   2  # Copyright (c) 2023-present The Bitcoin Core developers
   3  # Distributed under the MIT software license, see the accompanying
   4  # file COPYING or http://www.opensource.org/licenses/mit-license.php.
   5  
   6  """
   7  Test that wallet correctly tracks transactions that have been conflicted by blocks, particularly during reorgs.
   8  """
   9  
  10  from decimal import Decimal
  11  
  12  from test_framework.test_framework import BitcoinTestFramework
  13  from test_framework.util import (
  14          assert_equal,
  15  )
  16  
  17  class TxConflicts(BitcoinTestFramework):
  18      def set_test_params(self):
  19          self.num_nodes = 3
  20  
  21      def skip_test_if_missing_module(self):
  22          self.skip_if_no_wallet()
  23  
  24      def get_utxo_of_value(self, from_tx_id, search_value):
  25          return next(tx_out["vout"] for tx_out in self.nodes[0].gettransaction(from_tx_id)["details"] if tx_out["amount"] == Decimal(f"{search_value}"))
  26  
  27      def run_test(self):
  28          """
  29          The following tests check the behavior of the wallet when
  30          transaction conflicts are created. These conflicts are created
  31          using raw transaction RPCs that double-spend UTXOs and have more
  32          fees, replacing the original transaction.
  33          """
  34  
  35          self.test_block_conflicts()
  36          self.test_mempool_conflict()
  37          self.test_mempool_and_block_conflicts()
  38          self.test_descendants_with_mempool_conflicts()
  39  
  40      def test_block_conflicts(self):
  41          self.log.info("Send tx from which to conflict outputs later")
  42          txid_conflict_from_1 = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), Decimal("10"))
  43          txid_conflict_from_2 = self.nodes[0].sendtoaddress(self.nodes[0].getnewaddress(), Decimal("10"))
  44          self.generate(self.nodes[0], 1)
  45          self.sync_blocks()
  46  
  47          self.log.info("Disconnect nodes to broadcast conflicts on their respective chains")
  48          self.disconnect_nodes(0, 1)
  49          self.disconnect_nodes(2, 1)
  50  
  51          self.log.info("Create transactions that conflict with each other")
  52          output_A = self.get_utxo_of_value(from_tx_id=txid_conflict_from_1, search_value=10)
  53          output_B = self.get_utxo_of_value(from_tx_id=txid_conflict_from_2, search_value=10)
  54  
  55          # First create a transaction that consumes both A and B outputs.
  56          #
  57          # | tx1 |  ----->  |                |         |               |
  58          #                  |  AB_parent_tx  |  ---->  |   Child_Tx    |
  59          # | tx2 |  ----->  |                |         |               |
  60          #
  61          inputs_tx_AB_parent = [{"txid": txid_conflict_from_1, "vout": output_A}, {"txid": txid_conflict_from_2, "vout": output_B}]
  62          tx_AB_parent = self.nodes[0].signrawtransactionwithwallet(self.nodes[0].createrawtransaction(inputs_tx_AB_parent, {self.nodes[0].getnewaddress(): Decimal("19.99998")}))
  63  
  64          # Secondly, create two transactions: One consuming output_A, and another one consuming output_B
  65          #
  66          # | tx1 |  ----->  |     Tx_A_1     |
  67          #                   ----------------
  68          # | tx2 |  ----->  |     Tx_B_1     |
  69          #
  70          inputs_tx_A_1 = [{"txid": txid_conflict_from_1, "vout": output_A}]
  71          inputs_tx_B_1 = [{"txid": txid_conflict_from_2, "vout": output_B}]
  72          tx_A_1 = self.nodes[0].signrawtransactionwithwallet(self.nodes[0].createrawtransaction(inputs_tx_A_1, {self.nodes[0].getnewaddress(): Decimal("9.99998")}))
  73          tx_B_1 = self.nodes[0].signrawtransactionwithwallet(self.nodes[0].createrawtransaction(inputs_tx_B_1, {self.nodes[0].getnewaddress(): Decimal("9.99998")}))
  74  
  75          self.log.info("Broadcast conflicted transaction")
  76          txid_AB_parent = self.nodes[0].sendrawtransaction(tx_AB_parent["hex"])
  77          self.generate(self.nodes[0], 1, sync_fun=self.no_op)
  78  
  79          # Now that 'AB_parent_tx' was broadcast, build 'Child_Tx'
  80          output_c = self.get_utxo_of_value(from_tx_id=txid_AB_parent, search_value=19.99998)
  81          inputs_tx_C_child = [({"txid": txid_AB_parent, "vout": output_c})]
  82  
  83          tx_C_child = self.nodes[0].signrawtransactionwithwallet(self.nodes[0].createrawtransaction(inputs_tx_C_child, {self.nodes[0].getnewaddress() : Decimal("19.99996")}))
  84          tx_C_child_txid = self.nodes[0].sendrawtransaction(tx_C_child["hex"])
  85          self.generate(self.nodes[0], 1, sync_fun=self.no_op)
  86  
  87          self.log.info("Broadcast conflicting tx to node 1 and generate a longer chain")
  88          conflicting_txid_A = self.nodes[1].sendrawtransaction(tx_A_1["hex"])
  89          self.generate(self.nodes[1], 4, sync_fun=self.no_op)
  90          conflicting_txid_B = self.nodes[1].sendrawtransaction(tx_B_1["hex"])
  91          self.generate(self.nodes[1], 4, sync_fun=self.no_op)
  92  
  93          self.log.info("Connect nodes 0 and 1, trigger reorg and ensure that the tx is effectively conflicted")
  94          self.connect_nodes(0, 1)
  95          self.sync_blocks([self.nodes[0], self.nodes[1]])
  96          conflicted_AB_tx = self.nodes[0].gettransaction(txid_AB_parent)
  97          tx_C_child = self.nodes[0].gettransaction(tx_C_child_txid)
  98          conflicted_A_tx = self.nodes[0].gettransaction(conflicting_txid_A)
  99  
 100          self.log.info("Verify, after the reorg, that Tx_A was accepted, and tx_AB and its Child_Tx are conflicting now")
 101          # Tx A was accepted, Tx AB was not.
 102          assert conflicted_AB_tx["confirmations"] < 0
 103          assert conflicted_A_tx["confirmations"] > 0
 104  
 105          # Conflicted tx should have confirmations set to the confirmations of the most conflicting tx
 106          assert_equal(-conflicted_AB_tx["confirmations"], conflicted_A_tx["confirmations"])
 107          # Child should inherit conflicted state from parent
 108          assert_equal(-tx_C_child["confirmations"], conflicted_A_tx["confirmations"])
 109          # Check the confirmations of the conflicting transactions
 110          assert_equal(conflicted_A_tx["confirmations"], 8)
 111          assert_equal(self.nodes[0].gettransaction(conflicting_txid_B)["confirmations"], 4)
 112  
 113          self.log.info("Now generate a longer chain that does not contain any tx")
 114          # Node2 chain without conflicts
 115          self.generate(self.nodes[2], 15, sync_fun=self.no_op)
 116  
 117          # Connect node0 and node2 and wait reorg
 118          self.connect_nodes(0, 2)
 119          self.sync_blocks()
 120          conflicted = self.nodes[0].gettransaction(txid_AB_parent)
 121          tx_C_child = self.nodes[0].gettransaction(tx_C_child_txid)
 122  
 123          self.log.info("Test that formerly conflicted transaction are inactive after reorg")
 124          # Former conflicted tx should be unconfirmed as it hasn't been yet rebroadcast
 125          assert_equal(conflicted["confirmations"], 0)
 126          # Former conflicted child tx should be unconfirmed as it hasn't been rebroadcast
 127          assert_equal(tx_C_child["confirmations"], 0)
 128          # Rebroadcast former conflicted tx and check it confirms smoothly
 129          self.nodes[2].sendrawtransaction(conflicted["hex"])
 130          self.generate(self.nodes[2], 1)
 131          self.sync_blocks()
 132          former_conflicted = self.nodes[0].gettransaction(txid_AB_parent)
 133          assert_equal(former_conflicted["confirmations"], 1)
 134          assert_equal(former_conflicted["blockheight"], 217)
 135  
 136      def test_mempool_conflict(self):
 137          self.nodes[0].createwallet("alice")
 138          alice = self.nodes[0].get_wallet_rpc("alice")
 139  
 140          bob = self.nodes[1]
 141  
 142          self.nodes[2].send(outputs=[{alice.getnewaddress() : 25} for _ in range(3)])
 143          self.generate(self.nodes[2], 1)
 144  
 145          self.log.info("Test a scenario where a transaction has a mempool conflict")
 146  
 147          unspents = alice.listunspent()
 148          assert_equal(len(unspents), 3)
 149          for tx in unspents:
 150              assert_equal(tx["amount"], 25)
 151  
 152          # tx1 spends unspent[0] and unspent[1]
 153          raw_tx = alice.createrawtransaction(inputs=[unspents[0], unspents[1]], outputs=[{bob.getnewaddress() : 49.9999}])
 154          tx1 = alice.signrawtransactionwithwallet(raw_tx)['hex']
 155  
 156          # tx2 spends unspent[1] and unspent[2], conflicts with tx1
 157          raw_tx = alice.createrawtransaction(inputs=[unspents[1], unspents[2]], outputs=[{bob.getnewaddress() : 49.99}])
 158          tx2 = alice.signrawtransactionwithwallet(raw_tx)['hex']
 159  
 160          # tx3 spends unspent[2], conflicts with tx2
 161          raw_tx = alice.createrawtransaction(inputs=[unspents[2]], outputs=[{bob.getnewaddress() : 24.9899}])
 162          tx3 = alice.signrawtransactionwithwallet(raw_tx)['hex']
 163  
 164          # broadcast tx1
 165          tx1_txid = alice.sendrawtransaction(tx1)
 166  
 167          assert_equal(alice.listunspent(), [unspents[2]])
 168          assert_equal(alice.getbalance(), 25)
 169  
 170          # broadcast tx2, replaces tx1 in mempool
 171          tx2_txid = alice.sendrawtransaction(tx2)
 172  
 173          # Check that unspent[0] is now available because the transaction spending it has been replaced in the mempool
 174          assert_equal(alice.listunspent(), [unspents[0]])
 175          assert_equal(alice.getbalance(), 25)
 176  
 177          assert_equal(alice.gettransaction(tx1_txid)["mempoolconflicts"], [tx2_txid])
 178  
 179          self.log.info("Test scenario where a mempool conflict is removed")
 180  
 181          # broadcast tx3, replaces tx2 in mempool
 182          # Now that tx1's conflict has been removed, tx1 is now
 183          # not conflicted, and instead is inactive until it is
 184          # rebroadcasted. Now unspent[0] is not available, because
 185          # tx1 is no longer conflicted.
 186          alice.sendrawtransaction(tx3)
 187  
 188          assert_equal(alice.gettransaction(tx1_txid)["mempoolconflicts"], [])
 189          assert tx1_txid not in self.nodes[0].getrawmempool()
 190  
 191          # now all of alice's outputs should be considered spent
 192          # unspent[0]: spent by inactive tx1
 193          # unspent[1]: spent by inactive tx1
 194          # unspent[2]: spent by active tx3
 195          assert_equal(alice.listunspent(), [])
 196          assert_equal(alice.getbalance(), 0)
 197  
 198          # Clean up for next test
 199          bob.sendall([self.nodes[2].getnewaddress()])
 200          self.generate(self.nodes[2], 1)
 201  
 202          alice.unloadwallet()
 203  
 204      def test_mempool_and_block_conflicts(self):
 205          self.nodes[0].createwallet("alice_2")
 206          alice = self.nodes[0].get_wallet_rpc("alice_2")
 207          bob = self.nodes[1]
 208  
 209          self.nodes[2].send(outputs=[{alice.getnewaddress() : 25} for _ in range(3)])
 210          self.generate(self.nodes[2], 1)
 211  
 212          self.log.info("Test a scenario where a transaction has both a block conflict and a mempool conflict")
 213          unspents = [{"txid" : element["txid"], "vout" : element["vout"]} for element in alice.listunspent()]
 214  
 215          assert_equal(bob.getbalances()["mine"]["untrusted_pending"], 0)
 216  
 217          # alice and bob nodes are disconnected so that transactions can be
 218          # created by alice, but broadcasted from bob so that alice's wallet
 219          # doesn't know about them
 220          self.disconnect_nodes(0, 1)
 221  
 222          # Sends funds to bob
 223          raw_tx = alice.createrawtransaction(inputs=[unspents[0]], outputs=[{bob.getnewaddress() : 24.99999}])
 224          raw_tx1 = alice.signrawtransactionwithwallet(raw_tx)['hex']
 225          tx1_txid = bob.sendrawtransaction(raw_tx1) # broadcast original tx spending unspents[0] only to bob
 226  
 227          # create a conflict to previous tx (also spends unspents[0]), but don't broadcast, sends funds back to alice
 228          raw_tx = alice.createrawtransaction(inputs=[unspents[0], unspents[2]], outputs=[{alice.getnewaddress() : 49.999}])
 229          tx1_conflict = alice.signrawtransactionwithwallet(raw_tx)['hex']
 230  
 231          # Sends funds to bob
 232          raw_tx = alice.createrawtransaction(inputs=[unspents[1]], outputs=[{bob.getnewaddress() : 24.9999}])
 233          raw_tx2 = alice.signrawtransactionwithwallet(raw_tx)['hex']
 234          tx2_txid = bob.sendrawtransaction(raw_tx2) # broadcast another original tx spending unspents[1] only to bob
 235  
 236          # create a conflict to previous tx (also spends unspents[1]), but don't broadcast, sends funds to alice
 237          raw_tx = alice.createrawtransaction(inputs=[unspents[1]], outputs=[{alice.getnewaddress() : 24.9999}])
 238          tx2_conflict = alice.signrawtransactionwithwallet(raw_tx)['hex']
 239  
 240          bob_unspents = [{"txid" : element, "vout" : 0} for element in [tx1_txid, tx2_txid]]
 241  
 242          # tx1 and tx2 are now in bob's mempool, and they are unconflicted, so bob has these funds
 243          assert_equal(bob.getbalances()["mine"]["untrusted_pending"], Decimal("49.99989000"))
 244  
 245          # spend both of bob's unspents, child tx of tx1 and tx2
 246          raw_tx = bob.createrawtransaction(inputs=[bob_unspents[0], bob_unspents[1]], outputs=[{bob.getnewaddress() : 49.999}])
 247          raw_tx3 = bob.signrawtransactionwithwallet(raw_tx)['hex']
 248          tx3_txid = bob.sendrawtransaction(raw_tx3) # broadcast tx only to bob
 249  
 250          # alice knows about 0 txs, bob knows about 3
 251          assert_equal(len(alice.getrawmempool()), 0)
 252          assert_equal(len(bob.getrawmempool()), 3)
 253  
 254          assert_equal(bob.getbalances()["mine"]["untrusted_pending"], Decimal("49.99900000"))
 255  
 256          # bob broadcasts tx_1 conflict
 257          tx1_conflict_txid = bob.sendrawtransaction(tx1_conflict)
 258          assert_equal(len(alice.getrawmempool()), 0)
 259          assert_equal(len(bob.getrawmempool()), 2) # tx1_conflict kicks out both tx1, and its child tx3
 260  
 261          assert tx2_txid in bob.getrawmempool()
 262          assert tx1_conflict_txid in bob.getrawmempool()
 263  
 264          assert_equal(bob.gettransaction(tx1_txid)["mempoolconflicts"], [tx1_conflict_txid])
 265          assert_equal(bob.gettransaction(tx2_txid)["mempoolconflicts"], [])
 266          assert_equal(bob.gettransaction(tx3_txid)["mempoolconflicts"], [tx1_conflict_txid])
 267  
 268          # check that tx3 is now conflicted, so the output from tx2 can now be spent
 269          assert_equal(bob.getbalances()["mine"]["untrusted_pending"], Decimal("24.99990000"))
 270  
 271          # we will be disconnecting this block in the future
 272          alice.sendrawtransaction(tx2_conflict)
 273          assert_equal(len(alice.getrawmempool()), 1) # currently alice's mempool is only aware of tx2_conflict
 274          # 11 blocks are mined so that when they are invalidated, tx_2
 275          # does not get put back into the mempool
 276          blk = self.generate(self.nodes[0], 11, sync_fun=self.no_op)[0]
 277          assert_equal(len(alice.getrawmempool()), 0) # tx2_conflict is now mined
 278  
 279          self.connect_nodes(0, 1)
 280          self.sync_blocks()
 281          assert_equal(alice.getbestblockhash(), bob.getbestblockhash())
 282  
 283          # now that tx2 has a block conflict, tx1_conflict should be the only tx in bob's mempool
 284          assert tx1_conflict_txid in bob.getrawmempool()
 285          assert_equal(len(bob.getrawmempool()), 1)
 286  
 287          # tx3 should now also be block-conflicted by tx2_conflict
 288          assert_equal(bob.gettransaction(tx3_txid)["confirmations"], -11)
 289          # bob has no pending funds, since tx1, tx2, and tx3 are all conflicted
 290          assert_equal(bob.getbalances()["mine"]["untrusted_pending"], 0)
 291          bob.invalidateblock(blk) # remove tx2_conflict
 292          # bob should still have no pending funds because tx1 and tx3 are still conflicted, and tx2 has not been re-broadcast
 293          assert_equal(bob.getbalances()["mine"]["untrusted_pending"], 0)
 294          assert_equal(len(bob.getrawmempool()), 1)
 295          # check that tx3 is no longer block-conflicted
 296          assert_equal(bob.gettransaction(tx3_txid)["confirmations"], 0)
 297  
 298          bob.sendrawtransaction(raw_tx2)
 299          assert_equal(bob.getbalances()["mine"]["untrusted_pending"], Decimal("24.99990000"))
 300  
 301          # create a conflict to previous tx (also spends unspents[2]), but don't broadcast, sends funds back to alice
 302          raw_tx = alice.createrawtransaction(inputs=[unspents[2]], outputs=[{alice.getnewaddress() : 24.99}])
 303          tx1_conflict_conflict = alice.signrawtransactionwithwallet(raw_tx)['hex']
 304  
 305          bob.sendrawtransaction(tx1_conflict_conflict) # kick tx1_conflict out of the mempool
 306          bob.sendrawtransaction(raw_tx1) #re-broadcast tx1 because it is no longer conflicted
 307  
 308          # Now bob has pending funds because tx1 and tx2 are spent by tx3, which hasn't been re-broadcast yet
 309          bob_bal = bob.getbalances()["mine"]
 310          assert_equal(bob_bal["untrusted_pending"], -bob_bal["nonmempool"])
 311  
 312          bob.sendrawtransaction(raw_tx3)
 313          assert_equal(len(bob.getrawmempool()), 4) # The mempool contains: tx1, tx2, tx1_conflict_conflict, tx3
 314          assert_equal(bob.getbalances()["mine"]["untrusted_pending"], Decimal("49.99900000"))
 315  
 316          # Clean up for next test
 317          bob.reconsiderblock(blk)
 318          assert_equal(alice.getbestblockhash(), bob.getbestblockhash())
 319          self.sync_mempools()
 320          self.generate(self.nodes[2], 1)
 321  
 322          alice.unloadwallet()
 323  
 324      def test_descendants_with_mempool_conflicts(self):
 325          self.nodes[0].createwallet("alice_3")
 326          alice = self.nodes[0].get_wallet_rpc("alice_3")
 327  
 328          self.nodes[2].send(outputs=[{alice.getnewaddress() : 25} for _ in range(2)])
 329          self.generate(self.nodes[2], 1)
 330  
 331          self.nodes[1].createwallet("bob_1")
 332          bob = self.nodes[1].get_wallet_rpc("bob_1")
 333  
 334          self.nodes[2].createwallet("carol")
 335          carol = self.nodes[2].get_wallet_rpc("carol")
 336  
 337          self.log.info("Test a scenario where a transaction's parent has a mempool conflict")
 338  
 339          unspents = alice.listunspent()
 340          assert_equal(len(unspents), 2)
 341          for tx in unspents:
 342              assert_equal(tx["amount"], 25)
 343  
 344          assert_equal(alice.getrawmempool(), [])
 345  
 346          # Alice spends first utxo to bob in tx1
 347          raw_tx = alice.createrawtransaction(inputs=[unspents[0]], outputs=[{bob.getnewaddress() : 24.9999}])
 348          tx1 = alice.signrawtransactionwithwallet(raw_tx)['hex']
 349          tx1_txid = alice.sendrawtransaction(tx1)
 350  
 351          self.sync_mempools()
 352  
 353          assert_equal(alice.getbalance(), 25)
 354          assert_equal(bob.getbalances()["mine"]["untrusted_pending"], Decimal("24.99990000"))
 355  
 356          assert_equal(bob.gettransaction(tx1_txid)["mempoolconflicts"],  [])
 357  
 358          raw_tx = bob.createrawtransaction(inputs=[bob.listunspent(minconf=0)[0]], outputs=[{carol.getnewaddress() : 24.999}])
 359          # Bob creates a child to tx1
 360          tx1_child = bob.signrawtransactionwithwallet(raw_tx)['hex']
 361          tx1_child_txid = bob.sendrawtransaction(tx1_child)
 362  
 363          self.sync_mempools()
 364  
 365          # Currently neither tx1 nor tx1_child should have any conflicts
 366          assert_equal(bob.gettransaction(tx1_txid)["mempoolconflicts"],  [])
 367          assert_equal(bob.gettransaction(tx1_child_txid)["mempoolconflicts"],  [])
 368          assert tx1_txid in bob.getrawmempool()
 369          assert tx1_child_txid in bob.getrawmempool()
 370          assert_equal(len(bob.getrawmempool()), 2)
 371  
 372          assert_equal(bob.getbalances()["mine"]["untrusted_pending"], 0)
 373          assert_equal(carol.getbalances()["mine"]["untrusted_pending"], Decimal("24.99900000"))
 374  
 375          # Alice spends first unspent again, conflicting with tx1
 376          raw_tx = alice.createrawtransaction(inputs=[unspents[0], unspents[1]], outputs=[{carol.getnewaddress() : 49.99}])
 377          tx1_conflict = alice.signrawtransactionwithwallet(raw_tx)['hex']
 378          tx1_conflict_txid = alice.sendrawtransaction(tx1_conflict)
 379  
 380          self.sync_mempools()
 381  
 382          assert_equal(bob.getbalances()["mine"]["untrusted_pending"], 0)
 383          assert_equal(carol.getbalances()["mine"]["untrusted_pending"], Decimal("49.99000000"))
 384  
 385          assert tx1_txid not in bob.getrawmempool()
 386          assert tx1_child_txid not in bob.getrawmempool()
 387          assert tx1_conflict_txid in bob.getrawmempool()
 388          assert_equal(len(bob.getrawmempool()), 1)
 389  
 390          # Now both tx1 and tx1_child are conflicted by tx1_conflict
 391          assert_equal(bob.gettransaction(tx1_txid)["mempoolconflicts"],  [tx1_conflict_txid])
 392          assert_equal(bob.gettransaction(tx1_child_txid)["mempoolconflicts"],  [tx1_conflict_txid])
 393  
 394          # Now create a conflict to tx1_conflict, so that it gets kicked out of the mempool
 395          raw_tx = alice.createrawtransaction(inputs=[unspents[1]], outputs=[{carol.getnewaddress() : 24.9895}])
 396          tx1_conflict_conflict = alice.signrawtransactionwithwallet(raw_tx)['hex']
 397          tx1_conflict_conflict_txid = alice.sendrawtransaction(tx1_conflict_conflict)
 398  
 399          self.sync_mempools()
 400  
 401          # Now that tx1_conflict has been removed, both tx1 and tx1_child
 402          assert_equal(bob.gettransaction(tx1_txid)["mempoolconflicts"],  [])
 403          assert_equal(bob.gettransaction(tx1_child_txid)["mempoolconflicts"],  [])
 404  
 405          # Both tx1 and tx1_child are still not in the mempool because they have not be re-broadcasted
 406          assert tx1_txid not in bob.getrawmempool()
 407          assert tx1_child_txid not in bob.getrawmempool()
 408          assert tx1_conflict_txid not in bob.getrawmempool()
 409          assert tx1_conflict_conflict_txid in bob.getrawmempool()
 410          assert_equal(len(bob.getrawmempool()), 1)
 411  
 412          assert_equal(alice.getbalance(), 0)
 413          assert_equal(bob.getbalances()["mine"]["untrusted_pending"], 0)
 414          assert_equal(carol.getbalances()["mine"]["untrusted_pending"], Decimal("24.98950000"))
 415  
 416          # Both tx1 and tx1_child can now be re-broadcasted
 417          bob.sendrawtransaction(tx1)
 418          bob.sendrawtransaction(tx1_child)
 419          assert_equal(len(bob.getrawmempool()), 3)
 420  
 421          alice.unloadwallet()
 422          bob.unloadwallet()
 423          carol.unloadwallet()
 424  
 425  if __name__ == '__main__':
 426      TxConflicts(__file__).main()
 427