/ test / functional / mempool_unbroadcast.py
mempool_unbroadcast.py
  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()