wallet_conflicts.py raw

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