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