p2p_sendtxrcncl.py
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()