p2p_permissions.py raw

   1  #!/usr/bin/env python3
   2  # Copyright (c) 2015-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 p2p permission message.
   6  
   7  Test that permissions are correctly calculated and applied
   8  """
   9  
  10  from test_framework.messages import (
  11      SEQUENCE_FINAL,
  12  )
  13  from test_framework.p2p import P2PDataStore
  14  from test_framework.test_node import ErrorMatch
  15  from test_framework.test_framework import LimenkaTestFramework
  16  from test_framework.util import (
  17      append_config,
  18      assert_equal,
  19      p2p_port,
  20      tor_port,
  21  )
  22  from test_framework.wallet import MiniWallet
  23  
  24  
  25  class P2PPermissionsTests(LimenkaTestFramework):
  26      def set_test_params(self):
  27          self.num_nodes = 2
  28  
  29      def run_test(self):
  30          self.wallet = MiniWallet(self.nodes[0])
  31  
  32          self.check_tx_relay()
  33  
  34          self.checkpermission(
  35              # default permissions (no specific permissions)
  36              ["-whitelist=127.0.0.1"],
  37              # Make sure the default values in the command line documentation match the ones here
  38              ["addr", "relay", "noban", "mempool", "download"])
  39  
  40          self.checkpermission(
  41              # no permission (even with forcerelay)
  42              ["-whitelist=@127.0.0.1", "-whitelistforcerelay=1"],
  43              [])
  44  
  45          self.checkpermission(
  46              # relay permission removed (no specific permissions)
  47              ["-whitelist=127.0.0.1", "-whitelistrelay=0"],
  48              ["addr", "noban", "mempool", "download"])
  49  
  50          self.checkpermission(
  51              # forcerelay and relay permission added
  52              # Legacy parameter interaction which set whitelistrelay to true
  53              # if whitelistforcerelay is true
  54              ["-whitelist=127.0.0.1", "-whitelistforcerelay"],
  55              ["addr", "forcerelay", "relay", "noban", "mempool", "download"])
  56  
  57          # Let's make sure permissions are merged correctly
  58          # For this, we need to use whitebind instead of bind
  59          # by modifying the configuration file.
  60          ip_port = "127.0.0.1:{}".format(p2p_port(1))
  61          self.nodes[1].replace_in_config([("bind=127.0.0.1", "whitebind=bloomfilter,forcerelay@" + ip_port)])
  62          # Explicitly bind the tor port to prevent collisions with the default tor port
  63          append_config(self.nodes[1].datadir_path, [f"bind=127.0.0.1:{tor_port(self.nodes[1].index)}=onion"])
  64          self.checkpermission(
  65              ["-whitelist=noban@127.0.0.1"],
  66              # Check parameter interaction forcerelay should activate relay
  67              ["noban", "bloomfilter", "forcerelay", "relay", "download"])
  68          self.nodes[1].replace_in_config([("whitebind=bloomfilter,forcerelay@" + ip_port, "bind=127.0.0.1")])
  69          self.nodes[1].replace_in_config([(f"bind=127.0.0.1:{tor_port(self.nodes[1].index)}=onion", "")])
  70  
  71          self.checkpermission(
  72              # legacy whitelistrelay should be ignored
  73              ["-whitelist=noban,mempool@127.0.0.1", "-whitelistrelay"],
  74              ["noban", "mempool", "download"])
  75  
  76          self.checkpermission(
  77              # legacy whitelistforcerelay should be ignored
  78              ["-whitelist=noban,mempool@127.0.0.1", "-whitelistforcerelay"],
  79              ["noban", "mempool", "download"])
  80  
  81          self.checkpermission(
  82              # missing mempool permission to be considered legacy whitelisted
  83              ["-whitelist=noban@127.0.0.1"],
  84              ["noban", "download"])
  85  
  86          self.checkpermission(
  87              # all permission added
  88              ["-whitelist=all@127.0.0.1"],
  89              [
  90                  "blockfilters",
  91                  "forcerelay",
  92                  "noban",
  93                  "mempool",
  94                  "bloomfilter",
  95                  "relay",
  96                  "download",
  97                  "addr",
  98              ])
  99  
 100          for flag, permissions in [(["-whitelist=noban,out@127.0.0.1"], ["bloomfilter", "noban", "download"]), (["-whitelist=noban@127.0.0.1"], ["bloomfilter"])]:
 101              self.restart_node(0, flag)
 102              self.connect_nodes(0, 1)
 103              peerinfo = self.nodes[0].getpeerinfo()[0]
 104              assert_equal(peerinfo['permissions'], permissions)
 105  
 106          self.stop_node(1)
 107          self.nodes[1].assert_start_raises_init_error(["-whitelist=in,out@127.0.0.1"], "Only direction was set, no permissions", match=ErrorMatch.PARTIAL_REGEX)
 108          self.nodes[1].assert_start_raises_init_error(["-whitelist=oopsie@127.0.0.1"], "Invalid P2P permission", match=ErrorMatch.PARTIAL_REGEX)
 109          self.nodes[1].assert_start_raises_init_error(["-whitelist=noban@127.0.0.1:230"], "Invalid netmask specified in", match=ErrorMatch.PARTIAL_REGEX)
 110          self.nodes[1].assert_start_raises_init_error(["-whitebind=noban@127.0.0.1/10"], "Cannot resolve -whitebind address", match=ErrorMatch.PARTIAL_REGEX)
 111          self.nodes[1].assert_start_raises_init_error(["-whitebind=noban@127.0.0.1", "-bind=127.0.0.1", "-listen=0"], "Cannot set -bind or -whitebind together with -listen=0", match=ErrorMatch.PARTIAL_REGEX)
 112  
 113      def check_tx_relay(self):
 114          self.log.debug("Create a connection from a forcerelay peer that rebroadcasts raw txs")
 115          # A test framework p2p connection is needed to send the raw transaction directly. If a full node was used, it could only
 116          # rebroadcast via the inv-getdata mechanism. However, even for forcerelay connections, a full node would
 117          # currently not request a txid that is already in the mempool.
 118          self.restart_node(1, extra_args=["-whitelist=forcerelay@127.0.0.1"])
 119          p2p_rebroadcast_wallet = self.nodes[1].add_p2p_connection(P2PDataStore())
 120  
 121          self.log.debug("Send a tx from the wallet initially")
 122          tx = self.wallet.create_self_transfer(sequence=SEQUENCE_FINAL)['tx']
 123          txid = tx.rehash()
 124  
 125          self.log.debug("Wait until tx is in node[1]'s mempool")
 126          p2p_rebroadcast_wallet.send_txs_and_test([tx], self.nodes[1])
 127  
 128          self.log.debug("Check that node[1] will send the tx to node[0] even though it is already in the mempool")
 129          self.connect_nodes(1, 0)
 130          with self.nodes[1].assert_debug_log(["Force relaying tx {} (wtxid={}) from peer=0".format(txid, tx.getwtxid())]):
 131              p2p_rebroadcast_wallet.send_txs_and_test([tx], self.nodes[1])
 132              self.wait_until(lambda: txid in self.nodes[0].getrawmempool())
 133  
 134          self.log.debug("Check that node[1] will not send an invalid tx to node[0]")
 135          tx.vout[0].nValue += 1
 136          # add dust to cause policy rejection but no disconnection
 137          tx.vout.append(tx.vout[0])
 138          tx.vout[-1].nValue = 0
 139          txid = tx.rehash()
 140          # Send the transaction twice. The first time, it'll be rejected by ATMP because it conflicts
 141          # with a mempool transaction. The second time, it'll be in the m_lazy_recent_rejects filter.
 142          p2p_rebroadcast_wallet.send_txs_and_test(
 143              [tx],
 144              self.nodes[1],
 145              success=False,
 146              reject_reason='{} (wtxid={}) from peer=0 was not accepted: dust'.format(txid, tx.getwtxid())
 147          )
 148  
 149          p2p_rebroadcast_wallet.send_txs_and_test(
 150              [tx],
 151              self.nodes[1],
 152              success=False,
 153              reject_reason='Not relaying non-mempool transaction {} (wtxid={}) from forcerelay peer=0'.format(txid, tx.getwtxid())
 154          )
 155  
 156      def checkpermission(self, args, expectedPermissions):
 157          self.restart_node(1, ['-peerbloomfilters=0'] + args)
 158          self.connect_nodes(0, 1)
 159          peerinfo = self.nodes[1].getpeerinfo()[0]
 160          assert_equal(len(expectedPermissions), len(peerinfo['permissions']))
 161          for p in expectedPermissions:
 162              if p not in peerinfo['permissions']:
 163                  raise AssertionError("Expected permissions %r is not granted." % p)
 164  
 165  
 166  if __name__ == '__main__':
 167      P2PPermissionsTests(__file__).main()
 168