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