mempool_unbroadcast.py raw

   1  #!/usr/bin/env python3
   2  # Copyright (c) 2017-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  """Test that the mempool ensures transaction delivery by periodically sending
   6  to peers until a GETDATA is received."""
   7  
   8  from test_framework.p2p import P2PTxInvStore
   9  from test_framework.test_framework import BitcoinTestFramework
  10  from test_framework.util import (
  11      assert_equal,
  12      ensure_for,
  13  )
  14  from test_framework.wallet import MiniWallet
  15  
  16  MAX_INITIAL_BROADCAST_DELAY = 15 * 60 # 15 minutes in seconds
  17  
  18  class MempoolUnbroadcastTest(BitcoinTestFramework):
  19      def set_test_params(self):
  20          self.num_nodes = 2
  21          self.uses_wallet = None
  22  
  23      def run_test(self):
  24          self.wallet = MiniWallet(self.nodes[0])
  25          self.test_broadcast()
  26          self.test_txn_removal()
  27  
  28      def test_broadcast(self):
  29          self.log.info("Test that mempool reattempts delivery of locally submitted transaction")
  30          node = self.nodes[0]
  31  
  32          self.disconnect_nodes(0, 1)
  33  
  34          self.log.info("Generate transactions that only node 0 knows about")
  35  
  36          if self.is_wallet_compiled():
  37              self.import_deterministic_coinbase_privkeys()
  38              # generate a wallet txn
  39              addr = node.getnewaddress()
  40              wallet_tx_hsh = node.sendtoaddress(addr, 0.0001)
  41  
  42          # generate a txn using sendrawtransaction
  43          txFS = self.wallet.create_self_transfer()
  44          rpc_tx_hsh = node.sendrawtransaction(txFS["hex"])
  45  
  46          # check transactions are in unbroadcast using rpc
  47          mempoolinfo = self.nodes[0].getmempoolinfo()
  48          unbroadcast_count = 1
  49          if self.is_wallet_compiled():
  50              unbroadcast_count += 1
  51          assert_equal(mempoolinfo['unbroadcastcount'], unbroadcast_count)
  52          mempool = self.nodes[0].getrawmempool(True)
  53          for tx in mempool:
  54              assert_equal(mempool[tx]['unbroadcast'], True)
  55  
  56          # check that second node doesn't have these two txns
  57          mempool = self.nodes[1].getrawmempool()
  58          assert rpc_tx_hsh not in mempool
  59          if self.is_wallet_compiled():
  60              assert wallet_tx_hsh not in mempool
  61  
  62          # ensure that unbroadcast txs are persisted to mempool.dat
  63          self.restart_node(0)
  64  
  65          self.log.info("Reconnect nodes & check if they are sent to node 1")
  66          self.connect_nodes(0, 1)
  67  
  68          # fast forward into the future & ensure that the second node has the txns
  69          node.mockscheduler(MAX_INITIAL_BROADCAST_DELAY)
  70          self.sync_mempools(timeout=30)
  71          mempool = self.nodes[1].getrawmempool()
  72          assert rpc_tx_hsh in mempool
  73          if self.is_wallet_compiled():
  74              assert wallet_tx_hsh in mempool
  75  
  76          # check that transactions are no longer in first node's unbroadcast set
  77          mempool = self.nodes[0].getrawmempool(True)
  78          for tx in mempool:
  79              assert_equal(mempool[tx]['unbroadcast'], False)
  80  
  81          self.log.info("Add another connection & ensure transactions aren't broadcast again")
  82  
  83          conn = node.add_p2p_connection(P2PTxInvStore())
  84          node.mockscheduler(MAX_INITIAL_BROADCAST_DELAY)
  85          # allow sufficient time for possibility of broadcast
  86          ensure_for(duration=2, f=lambda: len(conn.get_invs()) == 0)
  87  
  88          self.disconnect_nodes(0, 1)
  89          node.disconnect_p2ps()
  90  
  91          self.log.info("Rebroadcast transaction and ensure it is not added to unbroadcast set when already in mempool")
  92          rpc_tx_hsh = node.sendrawtransaction(txFS["hex"])
  93          assert not node.getmempoolentry(rpc_tx_hsh)['unbroadcast']
  94  
  95      def test_txn_removal(self):
  96          self.log.info("Test that transactions removed from mempool are removed from unbroadcast set")
  97          node = self.nodes[0]
  98  
  99          # since the node doesn't have any connections, it will not receive
 100          # any GETDATAs & thus the transaction will remain in the unbroadcast set.
 101          txhsh = self.wallet.send_self_transfer(from_node=node)["txid"]
 102  
 103          # check transaction was removed from unbroadcast set due to presence in
 104          # a block
 105          removal_reason = "Removed {} from set of unbroadcast txns before confirmation that txn was sent out".format(txhsh)
 106          with node.assert_debug_log([removal_reason]):
 107              self.generate(node, 1, sync_fun=self.no_op)
 108  
 109  
 110  if __name__ == "__main__":
 111      MempoolUnbroadcastTest(__file__).main()
 112