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