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