mempool_unbroadcast.py raw

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