p2p_sendtxrcncl.py raw
1 #!/usr/bin/env python3
2 # Copyright (c) 2022 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 """Test SENDTXRCNCL message
6 """
7
8 from test_framework.messages import (
9 msg_sendtxrcncl,
10 msg_verack,
11 msg_version,
12 msg_wtxidrelay,
13 NODE_BLOOM,
14 )
15 from test_framework.p2p import (
16 P2PInterface,
17 P2P_SERVICES,
18 P2P_SUBVERSION,
19 P2P_VERSION,
20 )
21 from test_framework.test_framework import LimenkaTestFramework
22 from test_framework.util import assert_equal
23
24 class PeerNoVerack(P2PInterface):
25 def __init__(self, wtxidrelay=True):
26 super().__init__(wtxidrelay=wtxidrelay)
27
28 def on_version(self, message):
29 # Avoid sending verack in response to version.
30 # When calling add_p2p_connection, wait_for_verack=False must be set (see
31 # comment in add_p2p_connection).
32 self.send_version()
33 if message.nVersion >= 70016 and self.wtxidrelay:
34 self.send_message(msg_wtxidrelay())
35
36 class SendTxrcnclReceiver(P2PInterface):
37 def __init__(self):
38 super().__init__()
39 self.sendtxrcncl_msg_received = None
40
41 def on_sendtxrcncl(self, message):
42 self.sendtxrcncl_msg_received = message
43
44
45 class P2PFeelerReceiver(SendTxrcnclReceiver):
46 def on_version(self, message):
47 # feeler connections can not send any message other than their own version
48 self.send_version()
49
50
51 class PeerTrackMsgOrder(P2PInterface):
52 def __init__(self):
53 super().__init__()
54 self.messages = []
55
56 def on_message(self, message):
57 super().on_message(message)
58 self.messages.append(message)
59
60 def create_sendtxrcncl_msg():
61 sendtxrcncl_msg = msg_sendtxrcncl()
62 sendtxrcncl_msg.version = 1
63 sendtxrcncl_msg.salt = 2
64 return sendtxrcncl_msg
65
66 class SendTxRcnclTest(LimenkaTestFramework):
67 def set_test_params(self):
68 self.num_nodes = 1
69 self.extra_args = [['-txreconciliation']]
70
71 def run_test(self):
72 # Check everything concerning *sending* SENDTXRCNCL
73 # First, *sending* to *inbound*.
74 self.log.info('SENDTXRCNCL sent to an inbound')
75 peer = self.nodes[0].add_p2p_connection(SendTxrcnclReceiver(), send_version=True, wait_for_verack=True)
76 assert peer.sendtxrcncl_msg_received
77 assert_equal(peer.sendtxrcncl_msg_received.version, 1)
78 self.nodes[0].disconnect_p2ps()
79
80 self.log.info('SENDTXRCNCL should be sent before VERACK')
81 peer = self.nodes[0].add_p2p_connection(PeerTrackMsgOrder(), send_version=True, wait_for_verack=True)
82 peer.wait_for_verack()
83 verack_index = [i for i, msg in enumerate(peer.messages) if msg.msgtype == b'verack'][0]
84 sendtxrcncl_index = [i for i, msg in enumerate(peer.messages) if msg.msgtype == b'sendtxrcncl'][0]
85 assert sendtxrcncl_index < verack_index
86 self.nodes[0].disconnect_p2ps()
87
88 self.log.info('SENDTXRCNCL on pre-WTXID version should not be sent')
89 peer = self.nodes[0].add_p2p_connection(SendTxrcnclReceiver(), send_version=False, wait_for_verack=False)
90 pre_wtxid_version_msg = msg_version()
91 pre_wtxid_version_msg.nVersion = 70015
92 pre_wtxid_version_msg.strSubVer = P2P_SUBVERSION
93 pre_wtxid_version_msg.nServices = P2P_SERVICES
94 pre_wtxid_version_msg.relay = 1
95 peer.send_message(pre_wtxid_version_msg)
96 peer.wait_for_verack()
97 assert not peer.sendtxrcncl_msg_received
98 self.nodes[0].disconnect_p2ps()
99
100 self.log.info('SENDTXRCNCL for fRelay=false should not be sent')
101 peer = self.nodes[0].add_p2p_connection(SendTxrcnclReceiver(), send_version=False, wait_for_verack=False)
102 no_txrelay_version_msg = msg_version()
103 no_txrelay_version_msg.nVersion = P2P_VERSION
104 no_txrelay_version_msg.strSubVer = P2P_SUBVERSION
105 no_txrelay_version_msg.nServices = P2P_SERVICES
106 no_txrelay_version_msg.relay = 0
107 peer.send_message(no_txrelay_version_msg)
108 peer.wait_for_verack()
109 assert not peer.sendtxrcncl_msg_received
110 self.nodes[0].disconnect_p2ps()
111
112 self.log.info('SENDTXRCNCL for fRelay=false should not be sent (with NODE_BLOOM offered)')
113 self.restart_node(0, ["-peerbloomfilters", "-txreconciliation"])
114 peer = self.nodes[0].add_p2p_connection(SendTxrcnclReceiver(), send_version=False, wait_for_verack=False)
115 no_txrelay_version_msg = msg_version()
116 no_txrelay_version_msg.nVersion = P2P_VERSION
117 no_txrelay_version_msg.strSubVer = P2P_SUBVERSION
118 no_txrelay_version_msg.nServices = P2P_SERVICES
119 no_txrelay_version_msg.relay = 0
120 peer.send_message(no_txrelay_version_msg)
121 peer.wait_for_verack()
122 assert peer.nServices & NODE_BLOOM != 0
123 assert not peer.sendtxrcncl_msg_received
124 self.nodes[0].disconnect_p2ps()
125
126 # Now, *sending* to *outbound*.
127 self.log.info('SENDTXRCNCL sent to an outbound')
128 peer = self.nodes[0].add_outbound_p2p_connection(
129 SendTxrcnclReceiver(), wait_for_verack=True, p2p_idx=0, connection_type="outbound-full-relay")
130 assert peer.sendtxrcncl_msg_received
131 assert_equal(peer.sendtxrcncl_msg_received.version, 1)
132 self.nodes[0].disconnect_p2ps()
133
134 self.log.info('SENDTXRCNCL should not be sent if block-relay-only')
135 peer = self.nodes[0].add_outbound_p2p_connection(
136 SendTxrcnclReceiver(), wait_for_verack=True, p2p_idx=0, connection_type="block-relay-only")
137 assert not peer.sendtxrcncl_msg_received
138 self.nodes[0].disconnect_p2ps()
139
140 self.log.info("SENDTXRCNCL should not be sent if feeler")
141 peer = self.nodes[0].add_outbound_p2p_connection(P2PFeelerReceiver(), p2p_idx=0, connection_type="feeler")
142 assert not peer.sendtxrcncl_msg_received
143 self.nodes[0].disconnect_p2ps()
144
145 self.log.info("SENDTXRCNCL should not be sent if addrfetch")
146 peer = self.nodes[0].add_outbound_p2p_connection(
147 SendTxrcnclReceiver(), wait_for_verack=True, p2p_idx=0, connection_type="addr-fetch")
148 assert not peer.sendtxrcncl_msg_received
149 self.nodes[0].disconnect_p2ps()
150
151 self.log.info('SENDTXRCNCL not sent if -txreconciliation flag is not set')
152 self.restart_node(0, [])
153 peer = self.nodes[0].add_p2p_connection(SendTxrcnclReceiver(), send_version=True, wait_for_verack=True)
154 assert not peer.sendtxrcncl_msg_received
155 self.nodes[0].disconnect_p2ps()
156
157 self.log.info('SENDTXRCNCL not sent if blocksonly is set')
158 self.restart_node(0, ["-txreconciliation", "-blocksonly"])
159 peer = self.nodes[0].add_p2p_connection(SendTxrcnclReceiver(), send_version=True, wait_for_verack=True)
160 assert not peer.sendtxrcncl_msg_received
161 self.nodes[0].disconnect_p2ps()
162
163 # Check everything concerning *receiving* SENDTXRCNCL
164 # First, receiving from *inbound*.
165 self.restart_node(0, ["-txreconciliation"])
166 self.log.info('valid SENDTXRCNCL received')
167 peer = self.nodes[0].add_p2p_connection(PeerNoVerack(), send_version=True, wait_for_verack=False)
168 with self.nodes[0].assert_debug_log(["received: sendtxrcncl"]):
169 peer.send_message(create_sendtxrcncl_msg())
170 self.log.info('second SENDTXRCNCL triggers a disconnect')
171 with self.nodes[0].assert_debug_log(["(sendtxrcncl received from already registered peer), disconnecting peer=0"]):
172 peer.send_message(create_sendtxrcncl_msg())
173 peer.wait_for_disconnect()
174
175 self.restart_node(0, [])
176 self.log.info('SENDTXRCNCL if no txreconciliation supported is ignored')
177 peer = self.nodes[0].add_p2p_connection(PeerNoVerack(), send_version=True, wait_for_verack=False)
178 with self.nodes[0].assert_debug_log(['ignored, as our node does not have txreconciliation enabled']):
179 peer.send_message(create_sendtxrcncl_msg())
180 self.nodes[0].disconnect_p2ps()
181
182 self.restart_node(0, ["-txreconciliation"])
183
184 self.log.info('SENDTXRCNCL with version=0 triggers a disconnect')
185 sendtxrcncl_low_version = create_sendtxrcncl_msg()
186 sendtxrcncl_low_version.version = 0
187 peer = self.nodes[0].add_p2p_connection(PeerNoVerack(), send_version=True, wait_for_verack=False)
188 with self.nodes[0].assert_debug_log(["txreconciliation protocol violation"]):
189 peer.send_message(sendtxrcncl_low_version)
190 peer.wait_for_disconnect()
191
192 self.log.info('SENDTXRCNCL with version=2 is valid')
193 sendtxrcncl_higher_version = create_sendtxrcncl_msg()
194 sendtxrcncl_higher_version.version = 2
195 peer = self.nodes[0].add_p2p_connection(PeerNoVerack(), send_version=True, wait_for_verack=False)
196 with self.nodes[0].assert_debug_log(['Register peer=1']):
197 peer.send_message(sendtxrcncl_higher_version)
198 self.nodes[0].disconnect_p2ps()
199
200 self.log.info('unexpected SENDTXRCNCL is ignored')
201 peer = self.nodes[0].add_p2p_connection(PeerNoVerack(), send_version=False, wait_for_verack=False)
202 old_version_msg = msg_version()
203 old_version_msg.nVersion = 70015
204 old_version_msg.strSubVer = P2P_SUBVERSION
205 old_version_msg.nServices = P2P_SERVICES
206 old_version_msg.relay = 1
207 peer.send_message(old_version_msg)
208 with self.nodes[0].assert_debug_log(['Ignore unexpected txreconciliation signal']):
209 peer.send_message(create_sendtxrcncl_msg())
210 self.nodes[0].disconnect_p2ps()
211
212 self.log.info('sending SENDTXRCNCL after sending VERACK triggers a disconnect')
213 peer = self.nodes[0].add_p2p_connection(P2PInterface())
214 with self.nodes[0].assert_debug_log(["sendtxrcncl received after verack"]):
215 peer.send_message(create_sendtxrcncl_msg())
216 peer.wait_for_disconnect()
217
218 self.log.info('SENDTXRCNCL without WTXIDRELAY is ignored (recon state is erased after VERACK)')
219 peer = self.nodes[0].add_p2p_connection(PeerNoVerack(wtxidrelay=False), send_version=True, wait_for_verack=False)
220 with self.nodes[0].assert_debug_log(['Forget txreconciliation state of peer']):
221 peer.send_message(create_sendtxrcncl_msg())
222 peer.send_message(msg_verack())
223 self.nodes[0].disconnect_p2ps()
224
225 # Now, *receiving* from *outbound*.
226 self.log.info('SENDTXRCNCL if block-relay-only triggers a disconnect')
227 peer = self.nodes[0].add_outbound_p2p_connection(
228 PeerNoVerack(), wait_for_verack=False, p2p_idx=0, connection_type="block-relay-only")
229 with self.nodes[0].assert_debug_log(["we indicated no tx relay, disconnecting peer=5"]):
230 peer.send_message(create_sendtxrcncl_msg())
231 peer.wait_for_disconnect()
232
233
234 if __name__ == '__main__':
235 SendTxRcnclTest(__file__).main()
236