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