/ test / functional / p2p_disconnect_ban.py
p2p_disconnect_ban.py
  1  #!/usr/bin/env python3
  2  # Copyright (c) 2014-2022 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 node disconnect and ban behavior"""
  6  import time
  7  from pathlib import Path
  8  
  9  from test_framework.test_framework import BitcoinTestFramework
 10  from test_framework.util import (
 11      assert_equal,
 12      assert_raises_rpc_error,
 13  )
 14  
 15  class DisconnectBanTest(BitcoinTestFramework):
 16      def set_test_params(self):
 17          self.num_nodes = 2
 18          self.supports_cli = False
 19  
 20      def run_test(self):
 21          self.log.info("Connect nodes both way")
 22          # By default, the test framework sets up an addnode connection from
 23          # node 1 --> node0. By connecting node0 --> node 1, we're left with
 24          # the two nodes being connected both ways.
 25          # Topology will look like: node0 <--> node1
 26          self.connect_nodes(0, 1)
 27  
 28          self.log.info("Test setban and listbanned RPCs")
 29  
 30          self.log.info("setban: successfully ban single IP address")
 31          assert_equal(len(self.nodes[1].getpeerinfo()), 2)  # node1 should have 2 connections to node0 at this point
 32          self.nodes[1].setban(subnet="127.0.0.1", command="add")
 33          self.wait_until(lambda: len(self.nodes[1].getpeerinfo()) == 0, timeout=10)
 34          assert_equal(len(self.nodes[1].getpeerinfo()), 0)  # all nodes must be disconnected at this point
 35          assert_equal(len(self.nodes[1].listbanned()), 1)
 36  
 37          self.log.info("clearbanned: successfully clear ban list")
 38          self.nodes[1].clearbanned()
 39          assert_equal(len(self.nodes[1].listbanned()), 0)
 40  
 41          self.log.info('Test banlist database recreation')
 42          self.stop_node(1)
 43          target_file = self.nodes[1].chain_path / "banlist.json"
 44          Path.unlink(target_file)
 45          with self.nodes[1].assert_debug_log(["Recreating the banlist database"]):
 46              self.start_node(1)
 47  
 48          assert Path.exists(target_file)
 49          assert_equal(self.nodes[1].listbanned(), [])
 50  
 51          self.nodes[1].setban("127.0.0.0/24", "add")
 52  
 53          self.log.info("setban: fail to ban an already banned subnet")
 54          assert_equal(len(self.nodes[1].listbanned()), 1)
 55          assert_raises_rpc_error(-23, "IP/Subnet already banned", self.nodes[1].setban, "127.0.0.1", "add")
 56  
 57          self.log.info("setban: fail to ban an invalid subnet")
 58          assert_raises_rpc_error(-30, "Error: Invalid IP/Subnet", self.nodes[1].setban, "127.0.0.1/42", "add")
 59          assert_equal(len(self.nodes[1].listbanned()), 1)  # still only one banned ip because 127.0.0.1 is within the range of 127.0.0.0/24
 60  
 61          self.log.info("setban: fail to ban with past absolute timestamp")
 62          assert_raises_rpc_error(-8, "Error: Absolute timestamp is in the past", self.nodes[1].setban, "127.27.0.1", "add", 123, True)
 63  
 64          self.log.info("setban remove: fail to unban a non-banned subnet")
 65          assert_raises_rpc_error(-30, "Error: Unban failed", self.nodes[1].setban, "127.0.0.1", "remove")
 66          assert_equal(len(self.nodes[1].listbanned()), 1)
 67  
 68          self.log.info("setban remove: successfully unban subnet")
 69          self.nodes[1].setban("127.0.0.0/24", "remove")
 70          assert_equal(len(self.nodes[1].listbanned()), 0)
 71          self.nodes[1].clearbanned()
 72          assert_equal(len(self.nodes[1].listbanned()), 0)
 73  
 74          self.log.info("setban: test persistence across node restart")
 75          # Set the mocktime so we can control when bans expire
 76          old_time = int(time.time())
 77          self.nodes[1].setmocktime(old_time)
 78          self.nodes[1].setban("127.0.0.0/32", "add")
 79          self.nodes[1].setban("127.0.0.0/24", "add")
 80          self.nodes[1].setban("192.168.0.1", "add", 1)  # ban for 1 seconds
 81          self.nodes[1].setban("2001:4d48:ac57:400:cacf:e9ff:fe1d:9c63/19", "add", 1000)  # ban for 1000 seconds
 82          listBeforeShutdown = self.nodes[1].listbanned()
 83          assert_equal("192.168.0.1/32", listBeforeShutdown[2]['address'])
 84  
 85          self.log.info("setban: test banning with absolute timestamp")
 86          self.nodes[1].setban("192.168.0.2", "add", old_time + 120, True)
 87  
 88          # Move time forward by 3 seconds so the third ban has expired
 89          self.nodes[1].setmocktime(old_time + 3)
 90          assert_equal(len(self.nodes[1].listbanned()), 4)
 91  
 92          self.log.info("Test ban_duration and time_remaining")
 93          for ban in self.nodes[1].listbanned():
 94              if ban["address"] in ["127.0.0.0/32", "127.0.0.0/24"]:
 95                  assert_equal(ban["ban_duration"], 86400)
 96                  assert_equal(ban["time_remaining"], 86397)
 97              elif ban["address"] == "2001:4d48:ac57:400:cacf:e9ff:fe1d:9c63/19":
 98                  assert_equal(ban["ban_duration"], 1000)
 99                  assert_equal(ban["time_remaining"], 997)
100              elif ban["address"] == "192.168.0.2/32":
101                  assert_equal(ban["ban_duration"], 120)
102                  assert_equal(ban["time_remaining"], 117)
103  
104          self.restart_node(1)
105  
106          listAfterShutdown = self.nodes[1].listbanned()
107          assert_equal("127.0.0.0/24", listAfterShutdown[0]['address'])
108          assert_equal("127.0.0.0/32", listAfterShutdown[1]['address'])
109          assert_equal("192.168.0.2/32", listAfterShutdown[2]['address'])
110          assert_equal("/19" in listAfterShutdown[3]['address'], True)
111  
112          # Clear ban lists
113          self.nodes[1].clearbanned()
114          self.log.info("Connect nodes both way")
115          self.connect_nodes(0, 1)
116          self.connect_nodes(1, 0)
117  
118          self.log.info("Test disconnectnode RPCs")
119  
120          self.log.info("disconnectnode: fail to disconnect when calling with address and nodeid")
121          address1 = self.nodes[0].getpeerinfo()[0]['addr']
122          node1 = self.nodes[0].getpeerinfo()[0]["id"]
123          assert_raises_rpc_error(-32602, "Only one of address and nodeid should be provided.", self.nodes[0].disconnectnode, address=address1, nodeid=node1)
124  
125          self.log.info("disconnectnode: fail to disconnect when calling with junk address")
126          assert_raises_rpc_error(-29, "Node not found in connected nodes", self.nodes[0].disconnectnode, address="221B Baker Street")
127  
128          self.log.info("disconnectnode: successfully disconnect node by address")
129          address1 = self.nodes[0].getpeerinfo()[0]['addr']
130          self.nodes[0].disconnectnode(address=address1)
131          self.wait_until(lambda: len(self.nodes[1].getpeerinfo()) == 1, timeout=10)
132          assert not [node for node in self.nodes[0].getpeerinfo() if node['addr'] == address1]
133  
134          self.log.info("disconnectnode: successfully reconnect node")
135          self.connect_nodes(0, 1)  # reconnect the node
136          assert_equal(len(self.nodes[0].getpeerinfo()), 2)
137          assert [node for node in self.nodes[0].getpeerinfo() if node['addr'] == address1]
138  
139          self.log.info("disconnectnode: successfully disconnect node by node id")
140          id1 = self.nodes[0].getpeerinfo()[0]['id']
141          self.nodes[0].disconnectnode(nodeid=id1)
142          self.wait_until(lambda: len(self.nodes[1].getpeerinfo()) == 1, timeout=10)
143          assert not [node for node in self.nodes[0].getpeerinfo() if node['id'] == id1]
144  
145  if __name__ == '__main__':
146      DisconnectBanTest().main()