/ test / functional / rpc_orphans.py
rpc_orphans.py
  1  #!/usr/bin/env python3
  2  # Copyright (c) 2014-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  """Tests for orphan related RPCs."""
  6  
  7  from test_framework.mempool_util import (
  8      tx_in_orphanage,
  9  )
 10  from test_framework.messages import (
 11      CInv,
 12      msg_inv,
 13      msg_tx,
 14      MSG_WTX,
 15  )
 16  from test_framework.p2p import P2PInterface
 17  from test_framework.util import (
 18      assert_equal,
 19      assert_not_equal,
 20      assert_raises_rpc_error,
 21  )
 22  from test_framework.test_framework import BitcoinTestFramework
 23  from test_framework.wallet import MiniWallet
 24  
 25  
 26  class OrphanRPCsTest(BitcoinTestFramework):
 27      def set_test_params(self):
 28          self.num_nodes = 1
 29  
 30      def run_test(self):
 31          self.wallet = MiniWallet(self.nodes[0])
 32          self.test_orphan_activity()
 33          self.test_orphan_details()
 34          self.test_misc()
 35  
 36      def test_orphan_activity(self):
 37          self.log.info("Check that orphaned transactions are returned with getorphantxs")
 38          node = self.nodes[0]
 39  
 40          self.log.info("Create two 1P1C packages, but only broadcast the children")
 41          tx_parent_1 = self.wallet.create_self_transfer()
 42          tx_child_1 = self.wallet.create_self_transfer(utxo_to_spend=tx_parent_1["new_utxo"])
 43          tx_parent_2 = self.wallet.create_self_transfer()
 44          tx_child_2 = self.wallet.create_self_transfer(utxo_to_spend=tx_parent_2["new_utxo"])
 45          peer = node.add_p2p_connection(P2PInterface())
 46          peer.send_and_ping(msg_tx(tx_child_1["tx"]))
 47          peer.send_and_ping(msg_tx(tx_child_2["tx"]))
 48  
 49          self.log.info("Check that neither parent is in the mempool")
 50          assert_equal(node.getmempoolinfo()["size"], 0)
 51  
 52          orphanage = node.getorphantxs(verbosity=0)
 53          self.log.info("Check the size of the orphanage")
 54          assert_equal(len(orphanage), 2)
 55          self.log.info("Check that undefined verbosity is disallowed")
 56          assert_raises_rpc_error(-8, "Invalid verbosity value -1", node.getorphantxs, verbosity=-1)
 57          assert_raises_rpc_error(-8, "Invalid verbosity value 3", node.getorphantxs, verbosity=3)
 58          self.log.info("Check that both children are in the orphanage")
 59          assert tx_in_orphanage(node, tx_child_1["tx"])
 60          assert tx_in_orphanage(node, tx_child_2["tx"])
 61  
 62          self.log.info("Broadcast parent 1")
 63          peer.send_and_ping(msg_tx(tx_parent_1["tx"]))
 64          self.log.info("Check that parent 1 and child 1 are in the mempool")
 65          raw_mempool = node.getrawmempool()
 66          assert_equal(len(raw_mempool), 2)
 67          assert tx_parent_1["txid"] in raw_mempool
 68          assert tx_child_1["txid"] in raw_mempool
 69  
 70          self.log.info("Check that orphanage only contains child 2")
 71          orphanage = node.getorphantxs()
 72          assert_equal(len(orphanage), 1)
 73          assert tx_in_orphanage(node, tx_child_2["tx"])
 74  
 75          peer.send_and_ping(msg_tx(tx_parent_2["tx"]))
 76          self.log.info("Check that all parents and children are now in the mempool")
 77          raw_mempool = node.getrawmempool()
 78          assert_equal(len(raw_mempool), 4)
 79          assert tx_parent_1["txid"] in raw_mempool
 80          assert tx_child_1["txid"] in raw_mempool
 81          assert tx_parent_2["txid"] in raw_mempool
 82          assert tx_child_2["txid"] in raw_mempool
 83          self.log.info("Check that the orphanage is empty")
 84          assert_equal(len(node.getorphantxs()), 0)
 85  
 86          self.log.info("Confirm the transactions (clears mempool)")
 87          self.generate(node, 1)
 88          assert_equal(node.getmempoolinfo()["size"], 0)
 89  
 90      def test_orphan_details(self):
 91          self.log.info("Check the transaction details returned from getorphantxs")
 92          node = self.nodes[0]
 93  
 94          self.log.info("Create two orphans, from different peers")
 95          tx_parent_1 = self.wallet.create_self_transfer()
 96          tx_child_1 = self.wallet.create_self_transfer(utxo_to_spend=tx_parent_1["new_utxo"])
 97          tx_parent_2 = self.wallet.create_self_transfer()
 98          tx_child_2 = self.wallet.create_self_transfer(utxo_to_spend=tx_parent_2["new_utxo"])
 99          peer_1 = node.add_p2p_connection(P2PInterface())
100          peer_2 = node.add_p2p_connection(P2PInterface())
101          peer_1.send_and_ping(msg_tx(tx_child_1["tx"]))
102          peer_2.send_and_ping(msg_tx(tx_child_2["tx"]))
103  
104          orphanage = node.getorphantxs(verbosity=2)
105          assert tx_in_orphanage(node, tx_child_1["tx"])
106          assert tx_in_orphanage(node, tx_child_2["tx"])
107  
108          self.log.info("Check that orphan 1 and 2 were from different peers")
109          assert_not_equal(orphanage[0]["from"][0], orphanage[1]["from"][0])
110          peer_ids = [orphanage[0]["from"][0], orphanage[1]["from"][0]]
111  
112          self.log.info("Unorphan child 2")
113          peer_2.send_and_ping(msg_tx(tx_parent_2["tx"]))
114          assert not tx_in_orphanage(node, tx_child_2["tx"])
115  
116          self.log.info("Check that additional announcers are reflected in RPC result")
117          peer_2.send_and_ping(msg_inv([CInv(t=MSG_WTX, h=int(tx_child_1["wtxid"], 16))]))
118  
119          orphanage = node.getorphantxs(verbosity=2)
120          assert_equal(set(orphanage[0]["from"]), set(peer_ids))
121  
122          self.log.info("Checking orphan details")
123          assert_equal(len(node.getorphantxs()), 1)
124          orphan_1 = orphanage[0]
125          self.orphan_details_match(orphan_1, tx_child_1, verbosity=1)
126  
127          self.log.info("Checking orphan details (verbosity 2)")
128          orphanage = node.getorphantxs(verbosity=2)
129          orphan_1 = orphanage[0]
130          self.orphan_details_match(orphan_1, tx_child_1, verbosity=2)
131  
132      def orphan_details_match(self, orphan, tx, verbosity):
133          self.log.info("Check txid/wtxid of orphan")
134          assert_equal(orphan["txid"], tx["txid"])
135          assert_equal(orphan["wtxid"], tx["wtxid"])
136  
137          self.log.info("Check the sizes of orphan")
138          assert_equal(orphan["bytes"], len(tx["tx"].serialize()))
139          assert_equal(orphan["vsize"], tx["tx"].get_vsize())
140          assert_equal(orphan["weight"], tx["tx"].get_weight())
141  
142          if verbosity == 2:
143              self.log.info("Check the transaction hex of orphan")
144              assert_equal(orphan["hex"], tx["hex"])
145  
146      def test_misc(self):
147          node = self.nodes[0]
148          assert_raises_rpc_error(-3, "Verbosity was boolean but only integer allowed", node.getorphantxs, verbosity=True)
149          assert_raises_rpc_error(-3, "Verbosity was boolean but only integer allowed", node.getorphantxs, verbosity=False)
150          help_output = node.help()
151          self.log.info("Check that getorphantxs is a hidden RPC")
152          assert "getorphantxs" not in help_output
153          assert "unknown command: getorphantxs" not in node.help("getorphantxs")
154  
155  
156  if __name__ == '__main__':
157      OrphanRPCsTest(__file__).main()