rpc_orphans.py raw
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()
158