rpc_orphans.py raw

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