/ src / rpc / net.cpp
net.cpp
   1  // Copyright (c) 2009-present The Bitcoin Core developers
   2  // Distributed under the MIT software license, see the accompanying
   3  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
   4  
   5  #include <rpc/server.h>
   6  
   7  #include <addrman.h>
   8  #include <addrman_impl.h>
   9  #include <banman.h>
  10  #include <chainparams.h>
  11  #include <clientversion.h>
  12  #include <core_io.h>
  13  #include <net_permissions.h>
  14  #include <net_processing.h>
  15  #include <net_types.h>
  16  #include <netbase.h>
  17  #include <node/context.h>
  18  #include <node/protocol_version.h>
  19  #include <node/warnings.h>
  20  #include <policy/settings.h>
  21  #include <protocol.h>
  22  #include <rpc/blockchain.h>
  23  #include <rpc/protocol.h>
  24  #include <rpc/server_util.h>
  25  #include <rpc/util.h>
  26  #include <sync.h>
  27  #include <univalue.h>
  28  #include <util/chaintype.h>
  29  #include <util/strencodings.h>
  30  #include <util/string.h>
  31  #include <util/time.h>
  32  #include <util/translation.h>
  33  #include <validation.h>
  34  
  35  #include <chrono>
  36  #include <optional>
  37  #include <stdexcept>
  38  #include <string>
  39  #include <string_view>
  40  #include <vector>
  41  
  42  using node::NodeContext;
  43  using util::Join;
  44  
  45  const std::vector<std::string> CONNECTION_TYPE_DOC{
  46          "outbound-full-relay (default automatic connections)",
  47          "block-relay-only (does not relay transactions or addresses)",
  48          "inbound (initiated by the peer)",
  49          "manual (added via addnode RPC or -addnode/-connect configuration options)",
  50          "addr-fetch (short-lived automatic connection for soliciting addresses)",
  51          "feeler (short-lived automatic connection for testing addresses)",
  52          "private-broadcast (short-lived automatic connection for broadcasting privacy-sensitive transactions)"
  53  };
  54  
  55  const std::vector<std::string> TRANSPORT_TYPE_DOC{
  56      "detecting (peer could be v1 or v2)",
  57      "v1 (plaintext transport protocol)",
  58      "v2 (BIP324 encrypted transport protocol)"
  59  };
  60  
  61  static RPCHelpMan getconnectioncount()
  62  {
  63      return RPCHelpMan{
  64          "getconnectioncount",
  65          "Returns the number of connections to other nodes.\n",
  66                  {},
  67                  RPCResult{
  68                      RPCResult::Type::NUM, "", "The connection count"
  69                  },
  70                  RPCExamples{
  71                      HelpExampleCli("getconnectioncount", "")
  72              + HelpExampleRpc("getconnectioncount", "")
  73                  },
  74          [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
  75  {
  76      NodeContext& node = EnsureAnyNodeContext(request.context);
  77      const CConnman& connman = EnsureConnman(node);
  78  
  79      return connman.GetNodeCount(ConnectionDirection::Both);
  80  },
  81      };
  82  }
  83  
  84  static RPCHelpMan ping()
  85  {
  86      return RPCHelpMan{
  87          "ping",
  88          "Requests that a ping be sent to all other nodes, to measure ping time.\n"
  89                  "Results are provided in getpeerinfo.\n"
  90                  "Ping command is handled in queue with all other commands, so it measures processing backlog, not just network ping.\n",
  91                  {},
  92                  RPCResult{RPCResult::Type::NONE, "", ""},
  93                  RPCExamples{
  94                      HelpExampleCli("ping", "")
  95              + HelpExampleRpc("ping", "")
  96                  },
  97          [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
  98  {
  99      NodeContext& node = EnsureAnyNodeContext(request.context);
 100      PeerManager& peerman = EnsurePeerman(node);
 101  
 102      // Request that each node send a ping during next message processing pass
 103      peerman.SendPings();
 104      return UniValue::VNULL;
 105  },
 106      };
 107  }
 108  
 109  /** Returns, given services flags, a list of humanly readable (known) network services */
 110  static UniValue GetServicesNames(ServiceFlags services)
 111  {
 112      UniValue servicesNames(UniValue::VARR);
 113  
 114      for (const auto& flag : serviceFlagsToStr(services)) {
 115          servicesNames.push_back(flag);
 116      }
 117  
 118      return servicesNames;
 119  }
 120  
 121  static RPCHelpMan getpeerinfo()
 122  {
 123      return RPCHelpMan{
 124          "getpeerinfo",
 125          "Returns data about each connected network peer as a json array of objects.",
 126          {},
 127          RPCResult{
 128              RPCResult::Type::ARR, "", "",
 129              {
 130                  {RPCResult::Type::OBJ, "", "",
 131                  {
 132                      {
 133                      {RPCResult::Type::NUM, "id", "Peer index"},
 134                      {RPCResult::Type::STR, "addr", "(host:port) The IP address/hostname optionally followed by :port of the peer"},
 135                      {RPCResult::Type::STR, "addrbind", /*optional=*/true, "(ip:port) Bind address of the connection to the peer"},
 136                      {RPCResult::Type::STR, "addrlocal", /*optional=*/true, "(ip:port) Local address as reported by the peer"},
 137                      {RPCResult::Type::STR, "network", "Network (" + Join(GetNetworkNames(/*append_unroutable=*/true), ", ") + ")"},
 138                      {RPCResult::Type::NUM, "mapped_as", /*optional=*/true, "Mapped AS (Autonomous System) number at the end of the BGP route to the peer, used for diversifying\n"
 139                                                          "peer selection (only displayed if the -asmap config option is set)"},
 140                      {RPCResult::Type::STR_HEX, "services", "The services offered"},
 141                      {RPCResult::Type::ARR, "servicesnames", "the services offered, in human-readable form",
 142                      {
 143                          {RPCResult::Type::STR, "SERVICE_NAME", "the service name if it is recognised"}
 144                      }},
 145                      {RPCResult::Type::BOOL, "relaytxes", "Whether we relay transactions to this peer"},
 146                      {RPCResult::Type::NUM, "last_inv_sequence", "Mempool sequence number of this peer's last INV"},
 147                      {RPCResult::Type::NUM, "inv_to_send", "How many txs we have queued to announce to this peer"},
 148                      {RPCResult::Type::NUM_TIME, "lastsend", "The " + UNIX_EPOCH_TIME + " of the last send"},
 149                      {RPCResult::Type::NUM_TIME, "lastrecv", "The " + UNIX_EPOCH_TIME + " of the last receive"},
 150                      {RPCResult::Type::NUM_TIME, "last_transaction", "The " + UNIX_EPOCH_TIME + " of the last valid transaction received from this peer"},
 151                      {RPCResult::Type::NUM_TIME, "last_block", "The " + UNIX_EPOCH_TIME + " of the last block received from this peer"},
 152                      {RPCResult::Type::NUM, "bytessent", "The total bytes sent"},
 153                      {RPCResult::Type::NUM, "bytesrecv", "The total bytes received"},
 154                      {RPCResult::Type::NUM_TIME, "conntime", "The " + UNIX_EPOCH_TIME + " of the connection"},
 155                      {RPCResult::Type::NUM, "timeoffset", "The time offset in seconds"},
 156                      {RPCResult::Type::NUM, "pingtime", /*optional=*/true, "The last ping time in seconds, if any"},
 157                      {RPCResult::Type::NUM, "minping", /*optional=*/true, "The minimum observed ping time in seconds, if any"},
 158                      {RPCResult::Type::NUM, "pingwait", /*optional=*/true, "The duration in seconds of an outstanding ping (if non-zero)"},
 159                      {RPCResult::Type::NUM, "version", "The peer version, such as 70001"},
 160                      {RPCResult::Type::STR, "subver", "The string version"},
 161                      {RPCResult::Type::BOOL, "inbound", "Inbound (true) or Outbound (false)"},
 162                      {RPCResult::Type::BOOL, "bip152_hb_to", "Whether we selected peer as (compact blocks) high-bandwidth peer"},
 163                      {RPCResult::Type::BOOL, "bip152_hb_from", "Whether peer selected us as (compact blocks) high-bandwidth peer"},
 164                      {RPCResult::Type::NUM, "startingheight", /*optional=*/true, "(DEPRECATED, returned only if config option -deprecatedrpc=startingheight is passed) The starting height (block) of the peer"},
 165                      {RPCResult::Type::NUM, "presynced_headers", "The current height of header pre-synchronization with this peer, or -1 if no low-work sync is in progress"},
 166                      {RPCResult::Type::NUM, "synced_headers", "The last header we have in common with this peer"},
 167                      {RPCResult::Type::NUM, "synced_blocks", "The last block we have in common with this peer"},
 168                      {RPCResult::Type::ARR, "inflight", "",
 169                      {
 170                          {RPCResult::Type::NUM, "n", "The heights of blocks we're currently asking from this peer"},
 171                      }},
 172                      {RPCResult::Type::BOOL, "addr_relay_enabled", "Whether we participate in address relay with this peer"},
 173                      {RPCResult::Type::NUM, "addr_processed", "The total number of addresses processed, excluding those dropped due to rate limiting"},
 174                      {RPCResult::Type::NUM, "addr_rate_limited", "The total number of addresses dropped due to rate limiting"},
 175                      {RPCResult::Type::ARR, "permissions", "Any special permissions that have been granted to this peer",
 176                      {
 177                          {RPCResult::Type::STR, "permission_type", Join(NET_PERMISSIONS_DOC, ",\n") + ".\n"},
 178                      }},
 179                      {RPCResult::Type::NUM, "minfeefilter", "The minimum fee rate for transactions this peer accepts"},
 180                      {RPCResult::Type::OBJ_DYN, "bytessent_per_msg", "",
 181                      {
 182                          {RPCResult::Type::NUM, "msg", "The total bytes sent aggregated by message type\n"
 183                                                        "When a message type is not listed in this json object, the bytes sent are 0.\n"
 184                                                        "Only known message types can appear as keys in the object."}
 185                      }},
 186                      {RPCResult::Type::OBJ_DYN, "bytesrecv_per_msg", "",
 187                      {
 188                          {RPCResult::Type::NUM, "msg", "The total bytes received aggregated by message type\n"
 189                                                        "When a message type is not listed in this json object, the bytes received are 0.\n"
 190                                                        "Only known message types can appear as keys in the object and all bytes received\n"
 191                                                        "of unknown message types are listed under '"+NET_MESSAGE_TYPE_OTHER+"'."}
 192                      }},
 193                      {RPCResult::Type::STR, "connection_type", "Type of connection: \n" + Join(CONNECTION_TYPE_DOC, ",\n") + ".\n"
 194                                                                "Please note this output is unlikely to be stable in upcoming releases as we iterate to\n"
 195                                                                "best capture connection behaviors."},
 196                      {RPCResult::Type::STR, "transport_protocol_type", "Type of transport protocol: \n" + Join(TRANSPORT_TYPE_DOC, ",\n") + ".\n"},
 197                      {RPCResult::Type::STR, "session_id", "The session ID for this connection, or \"\" if there is none (\"v2\" transport protocol only).\n"},
 198                  }},
 199              }},
 200          },
 201          RPCExamples{
 202              HelpExampleCli("getpeerinfo", "")
 203              + HelpExampleRpc("getpeerinfo", "")
 204          },
 205          [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
 206  {
 207      NodeContext& node = EnsureAnyNodeContext(request.context);
 208      const CConnman& connman = EnsureConnman(node);
 209      const PeerManager& peerman = EnsurePeerman(node);
 210  
 211      std::vector<CNodeStats> vstats;
 212      connman.GetNodeStats(vstats);
 213  
 214      UniValue ret(UniValue::VARR);
 215  
 216      for (const CNodeStats& stats : vstats) {
 217          UniValue obj(UniValue::VOBJ);
 218          CNodeStateStats statestats;
 219          bool fStateStats = peerman.GetNodeStateStats(stats.nodeid, statestats);
 220          // GetNodeStateStats() requires the existence of a CNodeState and a Peer object
 221          // to succeed for this peer. These are created at connection initialisation and
 222          // exist for the duration of the connection - except if there is a race where the
 223          // peer got disconnected in between the GetNodeStats() and the GetNodeStateStats()
 224          // calls. In this case, the peer doesn't need to be reported here.
 225          if (!fStateStats) {
 226              continue;
 227          }
 228          obj.pushKV("id", stats.nodeid);
 229          obj.pushKV("addr", stats.m_addr_name);
 230          if (stats.addrBind.IsValid()) {
 231              obj.pushKV("addrbind", stats.addrBind.ToStringAddrPort());
 232          }
 233          if (!(stats.addrLocal.empty())) {
 234              obj.pushKV("addrlocal", stats.addrLocal);
 235          }
 236          obj.pushKV("network", GetNetworkName(stats.m_network));
 237          if (stats.m_mapped_as != 0) {
 238              obj.pushKV("mapped_as", stats.m_mapped_as);
 239          }
 240          ServiceFlags services{statestats.their_services};
 241          obj.pushKV("services", strprintf("%016x", services));
 242          obj.pushKV("servicesnames", GetServicesNames(services));
 243          obj.pushKV("relaytxes", statestats.m_relay_txs);
 244          obj.pushKV("last_inv_sequence", statestats.m_last_inv_seq);
 245          obj.pushKV("inv_to_send", statestats.m_inv_to_send);
 246          obj.pushKV("lastsend", count_seconds(stats.m_last_send));
 247          obj.pushKV("lastrecv", count_seconds(stats.m_last_recv));
 248          obj.pushKV("last_transaction", count_seconds(stats.m_last_tx_time));
 249          obj.pushKV("last_block", count_seconds(stats.m_last_block_time));
 250          obj.pushKV("bytessent", stats.nSendBytes);
 251          obj.pushKV("bytesrecv", stats.nRecvBytes);
 252          obj.pushKV("conntime", count_seconds(stats.m_connected));
 253          obj.pushKV("timeoffset", Ticks<std::chrono::seconds>(statestats.time_offset));
 254          if (stats.m_last_ping_time > 0us) {
 255              obj.pushKV("pingtime", Ticks<SecondsDouble>(stats.m_last_ping_time));
 256          }
 257          if (stats.m_min_ping_time < std::chrono::microseconds::max()) {
 258              obj.pushKV("minping", Ticks<SecondsDouble>(stats.m_min_ping_time));
 259          }
 260          if (statestats.m_ping_wait > 0s) {
 261              obj.pushKV("pingwait", Ticks<SecondsDouble>(statestats.m_ping_wait));
 262          }
 263          obj.pushKV("version", stats.nVersion);
 264          // Use the sanitized form of subver here, to avoid tricksy remote peers from
 265          // corrupting or modifying the JSON output by putting special characters in
 266          // their ver message.
 267          obj.pushKV("subver", stats.cleanSubVer);
 268          obj.pushKV("inbound", stats.fInbound);
 269          obj.pushKV("bip152_hb_to", stats.m_bip152_highbandwidth_to);
 270          obj.pushKV("bip152_hb_from", stats.m_bip152_highbandwidth_from);
 271          if (IsDeprecatedRPCEnabled("startingheight")) {
 272              obj.pushKV("startingheight", statestats.m_starting_height);
 273          }
 274          obj.pushKV("presynced_headers", statestats.presync_height);
 275          obj.pushKV("synced_headers", statestats.nSyncHeight);
 276          obj.pushKV("synced_blocks", statestats.nCommonHeight);
 277          UniValue heights(UniValue::VARR);
 278          for (const int height : statestats.vHeightInFlight) {
 279              heights.push_back(height);
 280          }
 281          obj.pushKV("inflight", std::move(heights));
 282          obj.pushKV("addr_relay_enabled", statestats.m_addr_relay_enabled);
 283          obj.pushKV("addr_processed", statestats.m_addr_processed);
 284          obj.pushKV("addr_rate_limited", statestats.m_addr_rate_limited);
 285          UniValue permissions(UniValue::VARR);
 286          for (const auto& permission : NetPermissions::ToStrings(stats.m_permission_flags)) {
 287              permissions.push_back(permission);
 288          }
 289          obj.pushKV("permissions", std::move(permissions));
 290          obj.pushKV("minfeefilter", ValueFromAmount(statestats.m_fee_filter_received));
 291  
 292          UniValue sendPerMsgType(UniValue::VOBJ);
 293          for (const auto& i : stats.mapSendBytesPerMsgType) {
 294              if (i.second > 0)
 295                  sendPerMsgType.pushKV(i.first, i.second);
 296          }
 297          obj.pushKV("bytessent_per_msg", std::move(sendPerMsgType));
 298  
 299          UniValue recvPerMsgType(UniValue::VOBJ);
 300          for (const auto& i : stats.mapRecvBytesPerMsgType) {
 301              if (i.second > 0)
 302                  recvPerMsgType.pushKV(i.first, i.second);
 303          }
 304          obj.pushKV("bytesrecv_per_msg", std::move(recvPerMsgType));
 305          obj.pushKV("connection_type", ConnectionTypeAsString(stats.m_conn_type));
 306          obj.pushKV("transport_protocol_type", TransportTypeAsString(stats.m_transport_type));
 307          obj.pushKV("session_id", stats.m_session_id);
 308  
 309          ret.push_back(std::move(obj));
 310      }
 311  
 312      return ret;
 313  },
 314      };
 315  }
 316  
 317  static RPCHelpMan addnode()
 318  {
 319      return RPCHelpMan{
 320          "addnode",
 321          "Attempts to add or remove a node from the addnode list.\n"
 322                  "Or try a connection to a node once.\n"
 323                  "Nodes added using addnode (or -connect) are protected from DoS disconnection and are not required to be\n"
 324                  "full nodes/support SegWit as other outbound peers are (though such peers will not be synced from).\n" +
 325                  strprintf("Addnode connections are limited to %u at a time", MAX_ADDNODE_CONNECTIONS) +
 326                  " and are counted separately from the -maxconnections limit.\n",
 327                  {
 328                      {"node", RPCArg::Type::STR, RPCArg::Optional::NO, "The IP address/hostname optionally followed by :port of the peer to connect to"},
 329                      {"command", RPCArg::Type::STR, RPCArg::Optional::NO, "'add' to add a node to the list, 'remove' to remove a node from the list, 'onetry' to try a connection to the node once"},
 330                      {"v2transport", RPCArg::Type::BOOL, RPCArg::DefaultHint{"set by -v2transport"}, "Attempt to connect using BIP324 v2 transport protocol (ignored for 'remove' command)"},
 331                  },
 332                  RPCResult{RPCResult::Type::NONE, "", ""},
 333                  RPCExamples{
 334                      HelpExampleCli("addnode", "\"192.168.0.6:8333\" \"onetry\" true")
 335              + HelpExampleRpc("addnode", "\"192.168.0.6:8333\", \"onetry\" true")
 336                  },
 337          [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
 338  {
 339      const auto command{self.Arg<std::string_view>("command")};
 340      if (command != "onetry" && command != "add" && command != "remove") {
 341          throw std::runtime_error(
 342              self.ToString());
 343      }
 344  
 345      NodeContext& node = EnsureAnyNodeContext(request.context);
 346      CConnman& connman = EnsureConnman(node);
 347  
 348      const auto node_arg{self.Arg<std::string_view>("node")};
 349      bool node_v2transport = connman.GetLocalServices() & NODE_P2P_V2;
 350      bool use_v2transport = self.MaybeArg<bool>("v2transport").value_or(node_v2transport);
 351  
 352      if (use_v2transport && !node_v2transport) {
 353          throw JSONRPCError(RPC_INVALID_PARAMETER, "Error: v2transport requested but not enabled (see -v2transport)");
 354      }
 355  
 356      if (command == "onetry")
 357      {
 358          CAddress addr;
 359          connman.OpenNetworkConnection(addr, /*fCountFailure=*/false, /*grant_outbound=*/{}, std::string{node_arg}.c_str(), ConnectionType::MANUAL, use_v2transport);
 360          return UniValue::VNULL;
 361      }
 362  
 363      if (command == "add")
 364      {
 365          if (!connman.AddNode({std::string{node_arg}, use_v2transport})) {
 366              throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: Node already added");
 367          }
 368      }
 369      else if (command == "remove")
 370      {
 371          if (!connman.RemoveAddedNode(node_arg)) {
 372              throw JSONRPCError(RPC_CLIENT_NODE_NOT_ADDED, "Error: Node could not be removed. It has not been added previously.");
 373          }
 374      }
 375  
 376      return UniValue::VNULL;
 377  },
 378      };
 379  }
 380  
 381  static RPCHelpMan addconnection()
 382  {
 383      return RPCHelpMan{
 384          "addconnection",
 385          "Open an outbound connection to a specified node. This RPC is for testing only.\n",
 386          {
 387              {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The IP address and port to attempt connecting to."},
 388              {"connection_type", RPCArg::Type::STR, RPCArg::Optional::NO, "Type of connection to open (\"outbound-full-relay\", \"block-relay-only\", \"addr-fetch\" or \"feeler\")."},
 389              {"v2transport", RPCArg::Type::BOOL, RPCArg::Optional::NO, "Attempt to connect using BIP324 v2 transport protocol"},
 390          },
 391          RPCResult{
 392              RPCResult::Type::OBJ, "", "",
 393              {
 394                  { RPCResult::Type::STR, "address", "Address of newly added connection." },
 395                  { RPCResult::Type::STR, "connection_type", "Type of connection opened." },
 396              }},
 397          RPCExamples{
 398              HelpExampleCli("addconnection", "\"192.168.0.6:8333\" \"outbound-full-relay\" true")
 399              + HelpExampleRpc("addconnection", "\"192.168.0.6:8333\" \"outbound-full-relay\" true")
 400          },
 401          [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
 402  {
 403      if (Params().GetChainType() != ChainType::REGTEST) {
 404          throw std::runtime_error("addconnection is for regression testing (-regtest mode) only.");
 405      }
 406  
 407      const std::string address = request.params[0].get_str();
 408      auto conn_type_in{util::TrimStringView(self.Arg<std::string_view>("connection_type"))};
 409      ConnectionType conn_type{};
 410      if (conn_type_in == "outbound-full-relay") {
 411          conn_type = ConnectionType::OUTBOUND_FULL_RELAY;
 412      } else if (conn_type_in == "block-relay-only") {
 413          conn_type = ConnectionType::BLOCK_RELAY;
 414      } else if (conn_type_in == "addr-fetch") {
 415          conn_type = ConnectionType::ADDR_FETCH;
 416      } else if (conn_type_in == "feeler") {
 417          conn_type = ConnectionType::FEELER;
 418      } else {
 419          throw JSONRPCError(RPC_INVALID_PARAMETER, self.ToString());
 420      }
 421      bool use_v2transport{self.Arg<bool>("v2transport")};
 422  
 423      NodeContext& node = EnsureAnyNodeContext(request.context);
 424      CConnman& connman = EnsureConnman(node);
 425  
 426      if (use_v2transport && !(connman.GetLocalServices() & NODE_P2P_V2)) {
 427          throw JSONRPCError(RPC_INVALID_PARAMETER, "Error: Adding v2transport connections requires -v2transport init flag to be set.");
 428      }
 429  
 430      const bool success = connman.AddConnection(address, conn_type, use_v2transport);
 431      if (!success) {
 432          throw JSONRPCError(RPC_CLIENT_NODE_CAPACITY_REACHED, "Error: Already at capacity for specified connection type.");
 433      }
 434  
 435      UniValue info(UniValue::VOBJ);
 436      info.pushKV("address", address);
 437      info.pushKV("connection_type", conn_type_in);
 438  
 439      return info;
 440  },
 441      };
 442  }
 443  
 444  static RPCHelpMan disconnectnode()
 445  {
 446      return RPCHelpMan{
 447          "disconnectnode",
 448          "Immediately disconnects from the specified peer node.\n"
 449                  "\nStrictly one out of 'address' and 'nodeid' can be provided to identify the node.\n"
 450                  "\nTo disconnect by nodeid, either set 'address' to the empty string, or call using the named 'nodeid' argument only.\n",
 451                  {
 452                      {"address", RPCArg::Type::STR, RPCArg::DefaultHint{"fallback to nodeid"}, "The IP address/port of the node"},
 453                      {"nodeid", RPCArg::Type::NUM, RPCArg::DefaultHint{"fallback to address"}, "The node ID (see getpeerinfo for node IDs)"},
 454                  },
 455                  RPCResult{RPCResult::Type::NONE, "", ""},
 456                  RPCExamples{
 457                      HelpExampleCli("disconnectnode", "\"192.168.0.6:8333\"")
 458              + HelpExampleCli("disconnectnode", "\"\" 1")
 459              + HelpExampleRpc("disconnectnode", "\"192.168.0.6:8333\"")
 460              + HelpExampleRpc("disconnectnode", "\"\", 1")
 461                  },
 462          [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
 463  {
 464      NodeContext& node = EnsureAnyNodeContext(request.context);
 465      CConnman& connman = EnsureConnman(node);
 466  
 467      bool success;
 468      auto address{self.MaybeArg<std::string_view>("address")};
 469      auto node_id{self.MaybeArg<int64_t>("nodeid")};
 470  
 471      if (address && !node_id) {
 472          /* handle disconnect-by-address */
 473          success = connman.DisconnectNode(*address);
 474      } else if (node_id && (!address || address->empty())) {
 475          /* handle disconnect-by-id */
 476          success = connman.DisconnectNode(*node_id);
 477      } else {
 478          throw JSONRPCError(RPC_INVALID_PARAMS, "Only one of address and nodeid should be provided.");
 479      }
 480  
 481      if (!success) {
 482          throw JSONRPCError(RPC_CLIENT_NODE_NOT_CONNECTED, "Node not found in connected nodes");
 483      }
 484  
 485      return UniValue::VNULL;
 486  },
 487      };
 488  }
 489  
 490  static RPCHelpMan getaddednodeinfo()
 491  {
 492      return RPCHelpMan{
 493          "getaddednodeinfo",
 494          "Returns information about the given added node, or all added nodes\n"
 495                  "(note that onetry addnodes are not listed here)\n",
 496                  {
 497                      {"node", RPCArg::Type::STR, RPCArg::DefaultHint{"all nodes"}, "If provided, return information about this specific node, otherwise all nodes are returned."},
 498                  },
 499                  RPCResult{
 500                      RPCResult::Type::ARR, "", "",
 501                      {
 502                          {RPCResult::Type::OBJ, "", "",
 503                          {
 504                              {RPCResult::Type::STR, "addednode", "The node IP address or name (as provided to addnode)"},
 505                              {RPCResult::Type::BOOL, "connected", "If connected"},
 506                              {RPCResult::Type::ARR, "addresses", "Only when connected = true",
 507                              {
 508                                  {RPCResult::Type::OBJ, "", "",
 509                                  {
 510                                      {RPCResult::Type::STR, "address", "The bitcoin server IP and port we're connected to"},
 511                                      {RPCResult::Type::STR, "connected", "connection, inbound or outbound"},
 512                                  }},
 513                              }},
 514                          }},
 515                      }
 516                  },
 517                  RPCExamples{
 518                      HelpExampleCli("getaddednodeinfo", "\"192.168.0.201\"")
 519              + HelpExampleRpc("getaddednodeinfo", "\"192.168.0.201\"")
 520                  },
 521          [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
 522  {
 523      NodeContext& node = EnsureAnyNodeContext(request.context);
 524      const CConnman& connman = EnsureConnman(node);
 525  
 526      std::vector<AddedNodeInfo> vInfo = connman.GetAddedNodeInfo(/*include_connected=*/true);
 527  
 528      if (auto node{self.MaybeArg<std::string_view>("node")}) {
 529          bool found = false;
 530          for (const AddedNodeInfo& info : vInfo) {
 531              if (info.m_params.m_added_node == *node) {
 532                  vInfo.assign(1, info);
 533                  found = true;
 534                  break;
 535              }
 536          }
 537          if (!found) {
 538              throw JSONRPCError(RPC_CLIENT_NODE_NOT_ADDED, "Error: Node has not been added.");
 539          }
 540      }
 541  
 542      UniValue ret(UniValue::VARR);
 543  
 544      for (const AddedNodeInfo& info : vInfo) {
 545          UniValue obj(UniValue::VOBJ);
 546          obj.pushKV("addednode", info.m_params.m_added_node);
 547          obj.pushKV("connected", info.fConnected);
 548          UniValue addresses(UniValue::VARR);
 549          if (info.fConnected) {
 550              UniValue address(UniValue::VOBJ);
 551              address.pushKV("address", info.resolvedAddress.ToStringAddrPort());
 552              address.pushKV("connected", info.fInbound ? "inbound" : "outbound");
 553              addresses.push_back(std::move(address));
 554          }
 555          obj.pushKV("addresses", std::move(addresses));
 556          ret.push_back(std::move(obj));
 557      }
 558  
 559      return ret;
 560  },
 561      };
 562  }
 563  
 564  static RPCHelpMan getnettotals()
 565  {
 566      return RPCHelpMan{"getnettotals",
 567          "Returns information about network traffic, including bytes in, bytes out,\n"
 568          "and current system time.",
 569          {},
 570                  RPCResult{
 571                     RPCResult::Type::OBJ, "", "",
 572                     {
 573                         {RPCResult::Type::NUM, "totalbytesrecv", "Total bytes received"},
 574                         {RPCResult::Type::NUM, "totalbytessent", "Total bytes sent"},
 575                         {RPCResult::Type::NUM_TIME, "timemillis", "Current system " + UNIX_EPOCH_TIME + " in milliseconds"},
 576                         {RPCResult::Type::OBJ, "uploadtarget", "",
 577                         {
 578                             {RPCResult::Type::NUM, "timeframe", "Length of the measuring timeframe in seconds"},
 579                             {RPCResult::Type::NUM, "target", "Target in bytes"},
 580                             {RPCResult::Type::BOOL, "target_reached", "True if target is reached"},
 581                             {RPCResult::Type::BOOL, "serve_historical_blocks", "True if serving historical blocks"},
 582                             {RPCResult::Type::NUM, "bytes_left_in_cycle", "Bytes left in current time cycle"},
 583                             {RPCResult::Type::NUM, "time_left_in_cycle", "Seconds left in current time cycle"},
 584                          }},
 585                      }
 586                  },
 587                  RPCExamples{
 588                      HelpExampleCli("getnettotals", "")
 589              + HelpExampleRpc("getnettotals", "")
 590                  },
 591          [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
 592  {
 593      NodeContext& node = EnsureAnyNodeContext(request.context);
 594      const CConnman& connman = EnsureConnman(node);
 595  
 596      UniValue obj(UniValue::VOBJ);
 597      obj.pushKV("totalbytesrecv", connman.GetTotalBytesRecv());
 598      obj.pushKV("totalbytessent", connman.GetTotalBytesSent());
 599      obj.pushKV("timemillis", TicksSinceEpoch<std::chrono::milliseconds>(SystemClock::now()));
 600  
 601      UniValue outboundLimit(UniValue::VOBJ);
 602      outboundLimit.pushKV("timeframe", count_seconds(connman.GetMaxOutboundTimeframe()));
 603      outboundLimit.pushKV("target", connman.GetMaxOutboundTarget());
 604      outboundLimit.pushKV("target_reached", connman.OutboundTargetReached(false));
 605      outboundLimit.pushKV("serve_historical_blocks", !connman.OutboundTargetReached(true));
 606      outboundLimit.pushKV("bytes_left_in_cycle", connman.GetOutboundTargetBytesLeft());
 607      outboundLimit.pushKV("time_left_in_cycle", count_seconds(connman.GetMaxOutboundTimeLeftInCycle()));
 608      obj.pushKV("uploadtarget", std::move(outboundLimit));
 609      return obj;
 610  },
 611      };
 612  }
 613  
 614  static UniValue GetNetworksInfo()
 615  {
 616      UniValue networks(UniValue::VARR);
 617      for (int n = 0; n < NET_MAX; ++n) {
 618          enum Network network = static_cast<enum Network>(n);
 619          if (network == NET_UNROUTABLE || network == NET_INTERNAL) continue;
 620          Proxy proxy;
 621          UniValue obj(UniValue::VOBJ);
 622          GetProxy(network, proxy);
 623          obj.pushKV("name", GetNetworkName(network));
 624          obj.pushKV("limited", !g_reachable_nets.Contains(network));
 625          obj.pushKV("reachable", g_reachable_nets.Contains(network));
 626          obj.pushKV("proxy", proxy.IsValid() ? proxy.ToString() : std::string());
 627          obj.pushKV("proxy_randomize_credentials", proxy.m_tor_stream_isolation);
 628          networks.push_back(std::move(obj));
 629      }
 630      return networks;
 631  }
 632  
 633  static RPCHelpMan getnetworkinfo()
 634  {
 635      return RPCHelpMan{"getnetworkinfo",
 636                  "Returns an object containing various state info regarding P2P networking.\n",
 637                  {},
 638                  RPCResult{
 639                      RPCResult::Type::OBJ, "", "",
 640                      {
 641                          {RPCResult::Type::NUM, "version", "the server version"},
 642                          {RPCResult::Type::STR, "subversion", "the server subversion string"},
 643                          {RPCResult::Type::NUM, "protocolversion", "the protocol version"},
 644                          {RPCResult::Type::STR_HEX, "localservices", "the services we offer to the network"},
 645                          {RPCResult::Type::ARR, "localservicesnames", "the services we offer to the network, in human-readable form",
 646                          {
 647                              {RPCResult::Type::STR, "SERVICE_NAME", "the service name"},
 648                          }},
 649                          {RPCResult::Type::BOOL, "localrelay", "true if transaction relay is requested from peers"},
 650                          {RPCResult::Type::NUM, "timeoffset", "the time offset"},
 651                          {RPCResult::Type::NUM, "connections", "the total number of connections"},
 652                          {RPCResult::Type::NUM, "connections_in", "the number of inbound connections"},
 653                          {RPCResult::Type::NUM, "connections_out", "the number of outbound connections"},
 654                          {RPCResult::Type::BOOL, "networkactive", "whether p2p networking is enabled"},
 655                          {RPCResult::Type::ARR, "networks", "information per network",
 656                          {
 657                              {RPCResult::Type::OBJ, "", "",
 658                              {
 659                                  {RPCResult::Type::STR, "name", "network (" + Join(GetNetworkNames(), ", ") + ")"},
 660                                  {RPCResult::Type::BOOL, "limited", "is the network limited using -onlynet?"},
 661                                  {RPCResult::Type::BOOL, "reachable", "is the network reachable?"},
 662                                  {RPCResult::Type::STR, "proxy", "(\"host:port\") the proxy that is used for this network, or empty if none"},
 663                                  {RPCResult::Type::BOOL, "proxy_randomize_credentials", "Whether randomized credentials are used"},
 664                              }},
 665                          }},
 666                          {RPCResult::Type::NUM, "relayfee", "minimum relay fee rate for transactions in " + CURRENCY_UNIT + "/kvB"},
 667                          {RPCResult::Type::NUM, "incrementalfee", "minimum fee rate increment for mempool limiting or replacement in " + CURRENCY_UNIT + "/kvB"},
 668                          {RPCResult::Type::ARR, "localaddresses", "list of local addresses",
 669                          {
 670                              {RPCResult::Type::OBJ, "", "",
 671                              {
 672                                  {RPCResult::Type::STR, "address", "network address"},
 673                                  {RPCResult::Type::NUM, "port", "network port"},
 674                                  {RPCResult::Type::NUM, "score", "relative score"},
 675                              }},
 676                          }},
 677                          (IsDeprecatedRPCEnabled("warnings") ?
 678                              RPCResult{RPCResult::Type::STR, "warnings", "any network and blockchain warnings (DEPRECATED)"} :
 679                              RPCResult{RPCResult::Type::ARR, "warnings", "any network and blockchain warnings (run with `-deprecatedrpc=warnings` to return the latest warning as a single string)",
 680                              {
 681                                  {RPCResult::Type::STR, "", "warning"},
 682                              }
 683                              }
 684                          ),
 685                      }
 686                  },
 687                  RPCExamples{
 688                      HelpExampleCli("getnetworkinfo", "")
 689              + HelpExampleRpc("getnetworkinfo", "")
 690                  },
 691          [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
 692  {
 693      LOCK(cs_main);
 694      UniValue obj(UniValue::VOBJ);
 695      obj.pushKV("version",       CLIENT_VERSION);
 696      obj.pushKV("subversion",    strSubVersion);
 697      obj.pushKV("protocolversion",PROTOCOL_VERSION);
 698      NodeContext& node = EnsureAnyNodeContext(request.context);
 699      if (node.connman) {
 700          ServiceFlags services = node.connman->GetLocalServices();
 701          obj.pushKV("localservices", strprintf("%016x", services));
 702          obj.pushKV("localservicesnames", GetServicesNames(services));
 703      }
 704      if (node.peerman) {
 705          auto peerman_info{node.peerman->GetInfo()};
 706          obj.pushKV("localrelay", !peerman_info.ignores_incoming_txs);
 707          obj.pushKV("timeoffset", Ticks<std::chrono::seconds>(peerman_info.median_outbound_time_offset));
 708      }
 709      if (node.connman) {
 710          obj.pushKV("networkactive", node.connman->GetNetworkActive());
 711          obj.pushKV("connections", node.connman->GetNodeCount(ConnectionDirection::Both));
 712          obj.pushKV("connections_in", node.connman->GetNodeCount(ConnectionDirection::In));
 713          obj.pushKV("connections_out", node.connman->GetNodeCount(ConnectionDirection::Out));
 714      }
 715      obj.pushKV("networks",      GetNetworksInfo());
 716      if (node.mempool) {
 717          // Those fields can be deprecated, to be replaced by the getmempoolinfo fields
 718          obj.pushKV("relayfee", ValueFromAmount(node.mempool->m_opts.min_relay_feerate.GetFeePerK()));
 719          obj.pushKV("incrementalfee", ValueFromAmount(node.mempool->m_opts.incremental_relay_feerate.GetFeePerK()));
 720      }
 721      UniValue localAddresses(UniValue::VARR);
 722      {
 723          LOCK(g_maplocalhost_mutex);
 724          for (const std::pair<const CNetAddr, LocalServiceInfo> &item : mapLocalHost)
 725          {
 726              UniValue rec(UniValue::VOBJ);
 727              rec.pushKV("address", item.first.ToStringAddr());
 728              rec.pushKV("port", item.second.nPort);
 729              rec.pushKV("score", item.second.nScore);
 730              localAddresses.push_back(std::move(rec));
 731          }
 732      }
 733      obj.pushKV("localaddresses", std::move(localAddresses));
 734      obj.pushKV("warnings", node::GetWarningsForRpc(*CHECK_NONFATAL(node.warnings), IsDeprecatedRPCEnabled("warnings")));
 735      return obj;
 736  },
 737      };
 738  }
 739  
 740  static RPCHelpMan setban()
 741  {
 742      return RPCHelpMan{
 743          "setban",
 744          "Attempts to add or remove an IP/Subnet from the banned list.\n",
 745                  {
 746                      {"subnet", RPCArg::Type::STR, RPCArg::Optional::NO, "The IP/Subnet (see getpeerinfo for nodes IP) with an optional netmask (default is /32 = single IP)"},
 747                      {"command", RPCArg::Type::STR, RPCArg::Optional::NO, "'add' to add an IP/Subnet to the list, 'remove' to remove an IP/Subnet from the list"},
 748                      {"bantime", RPCArg::Type::NUM, RPCArg::Default{0}, "time in seconds how long (or until when if [absolute] is set) the IP is banned (0 or empty means using the default time of 24h which can also be overwritten by the -bantime startup argument)"},
 749                      {"absolute", RPCArg::Type::BOOL, RPCArg::Default{false}, "If set, the bantime must be an absolute timestamp expressed in " + UNIX_EPOCH_TIME},
 750                  },
 751                  RPCResult{RPCResult::Type::NONE, "", ""},
 752                  RPCExamples{
 753                      HelpExampleCli("setban", "\"192.168.0.6\" \"add\" 86400")
 754                              + HelpExampleCli("setban", "\"192.168.0.0/24\" \"add\"")
 755                              + HelpExampleRpc("setban", "\"192.168.0.6\", \"add\", 86400")
 756                  },
 757          [&](const RPCHelpMan& help, const JSONRPCRequest& request) -> UniValue
 758  {
 759      auto command{help.Arg<std::string_view>("command")};
 760      if (command != "add" && command != "remove") {
 761          throw std::runtime_error(help.ToString());
 762      }
 763      NodeContext& node = EnsureAnyNodeContext(request.context);
 764      BanMan& banman = EnsureBanman(node);
 765  
 766      CSubNet subNet;
 767      CNetAddr netAddr;
 768      std::string subnet_arg{help.Arg<std::string_view>("subnet")};
 769      const bool isSubnet{subnet_arg.find('/') != subnet_arg.npos};
 770  
 771      if (!isSubnet) {
 772          const std::optional<CNetAddr> addr{LookupHost(subnet_arg, false)};
 773          if (addr.has_value()) {
 774              netAddr = static_cast<CNetAddr>(MaybeFlipIPv6toCJDNS(CService{addr.value(), /*port=*/0}));
 775          }
 776      } else {
 777          subNet = LookupSubNet(subnet_arg);
 778      }
 779  
 780      if (! (isSubnet ? subNet.IsValid() : netAddr.IsValid()) ) {
 781          throw JSONRPCError(RPC_CLIENT_INVALID_IP_OR_SUBNET, "Error: Invalid IP/Subnet");
 782      }
 783  
 784      if (command == "add") {
 785          if (isSubnet ? banman.IsBanned(subNet) : banman.IsBanned(netAddr)) {
 786              throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: IP/Subnet already banned");
 787          }
 788  
 789          int64_t banTime = 0; //use standard bantime if not specified
 790          if (!request.params[2].isNull())
 791              banTime = request.params[2].getInt<int64_t>();
 792  
 793          const bool absolute{request.params[3].isNull() ? false : request.params[3].get_bool()};
 794  
 795          if (absolute && banTime < GetTime()) {
 796              throw JSONRPCError(RPC_INVALID_PARAMETER, "Error: Absolute timestamp is in the past");
 797          }
 798  
 799          if (isSubnet) {
 800              banman.Ban(subNet, banTime, absolute);
 801              if (node.connman) {
 802                  node.connman->DisconnectNode(subNet);
 803              }
 804          } else {
 805              banman.Ban(netAddr, banTime, absolute);
 806              if (node.connman) {
 807                  node.connman->DisconnectNode(netAddr);
 808              }
 809          }
 810      } else if(command == "remove") {
 811          if (!( isSubnet ? banman.Unban(subNet) : banman.Unban(netAddr) )) {
 812              throw JSONRPCError(RPC_CLIENT_INVALID_IP_OR_SUBNET, "Error: Unban failed. Requested address/subnet was not previously manually banned.");
 813          }
 814      }
 815      return UniValue::VNULL;
 816  },
 817      };
 818  }
 819  
 820  static RPCHelpMan listbanned()
 821  {
 822      return RPCHelpMan{
 823          "listbanned",
 824          "List all manually banned IPs/Subnets.\n",
 825                  {},
 826          RPCResult{RPCResult::Type::ARR, "", "",
 827              {
 828                  {RPCResult::Type::OBJ, "", "",
 829                      {
 830                          {RPCResult::Type::STR, "address", "The IP/Subnet of the banned node"},
 831                          {RPCResult::Type::NUM_TIME, "ban_created", "The " + UNIX_EPOCH_TIME + " the ban was created"},
 832                          {RPCResult::Type::NUM_TIME, "banned_until", "The " + UNIX_EPOCH_TIME + " the ban expires"},
 833                          {RPCResult::Type::NUM_TIME, "ban_duration", "The ban duration, in seconds"},
 834                          {RPCResult::Type::NUM_TIME, "time_remaining", "The time remaining until the ban expires, in seconds"},
 835                      }},
 836              }},
 837                  RPCExamples{
 838                      HelpExampleCli("listbanned", "")
 839                              + HelpExampleRpc("listbanned", "")
 840                  },
 841          [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
 842  {
 843      BanMan& banman = EnsureAnyBanman(request.context);
 844  
 845      banmap_t banMap;
 846      banman.GetBanned(banMap);
 847      const int64_t current_time{GetTime()};
 848  
 849      UniValue bannedAddresses(UniValue::VARR);
 850      for (const auto& entry : banMap)
 851      {
 852          const CBanEntry& banEntry = entry.second;
 853          UniValue rec(UniValue::VOBJ);
 854          rec.pushKV("address", entry.first.ToString());
 855          rec.pushKV("ban_created", banEntry.nCreateTime);
 856          rec.pushKV("banned_until", banEntry.nBanUntil);
 857          rec.pushKV("ban_duration", (banEntry.nBanUntil - banEntry.nCreateTime));
 858          rec.pushKV("time_remaining", (banEntry.nBanUntil - current_time));
 859  
 860          bannedAddresses.push_back(std::move(rec));
 861      }
 862  
 863      return bannedAddresses;
 864  },
 865      };
 866  }
 867  
 868  static RPCHelpMan clearbanned()
 869  {
 870      return RPCHelpMan{
 871          "clearbanned",
 872          "Clear all banned IPs.\n",
 873                  {},
 874                  RPCResult{RPCResult::Type::NONE, "", ""},
 875                  RPCExamples{
 876                      HelpExampleCli("clearbanned", "")
 877                              + HelpExampleRpc("clearbanned", "")
 878                  },
 879          [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
 880  {
 881      BanMan& banman = EnsureAnyBanman(request.context);
 882  
 883      banman.ClearBanned();
 884  
 885      return UniValue::VNULL;
 886  },
 887      };
 888  }
 889  
 890  static RPCHelpMan setnetworkactive()
 891  {
 892      return RPCHelpMan{
 893          "setnetworkactive",
 894          "Disable/enable all p2p network activity.\n",
 895                  {
 896                      {"state", RPCArg::Type::BOOL, RPCArg::Optional::NO, "true to enable networking, false to disable"},
 897                  },
 898                  RPCResult{RPCResult::Type::BOOL, "", "The value that was passed in"},
 899                  RPCExamples{""},
 900          [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
 901  {
 902      NodeContext& node = EnsureAnyNodeContext(request.context);
 903      CConnman& connman = EnsureConnman(node);
 904  
 905      connman.SetNetworkActive(request.params[0].get_bool());
 906  
 907      return connman.GetNetworkActive();
 908  },
 909      };
 910  }
 911  
 912  static RPCHelpMan getnodeaddresses()
 913  {
 914      return RPCHelpMan{"getnodeaddresses",
 915                  "Return known addresses, after filtering for quality and recency.\n"
 916                  "These can potentially be used to find new peers in the network.\n"
 917                  "The total number of addresses known to the node may be higher.",
 918                  {
 919                      {"count", RPCArg::Type::NUM, RPCArg::Default{1}, "The maximum number of addresses to return. Specify 0 to return all known addresses."},
 920                      {"network", RPCArg::Type::STR, RPCArg::DefaultHint{"all networks"}, "Return only addresses of the specified network. Can be one of: " + Join(GetNetworkNames(), ", ") + "."},
 921                  },
 922                  RPCResult{
 923                      RPCResult::Type::ARR, "", "",
 924                      {
 925                          {RPCResult::Type::OBJ, "", "",
 926                          {
 927                              {RPCResult::Type::NUM_TIME, "time", "The " + UNIX_EPOCH_TIME + " when the node was last seen"},
 928                              {RPCResult::Type::NUM, "services", "The services offered by the node"},
 929                              {RPCResult::Type::STR, "address", "The address of the node"},
 930                              {RPCResult::Type::NUM, "port", "The port number of the node"},
 931                              {RPCResult::Type::STR, "network", "The network (" + Join(GetNetworkNames(), ", ") + ") the node connected through"},
 932                          }},
 933                      }
 934                  },
 935                  RPCExamples{
 936                      HelpExampleCli("getnodeaddresses", "8")
 937                      + HelpExampleCli("getnodeaddresses", "4 \"i2p\"")
 938                      + HelpExampleCli("-named getnodeaddresses", "network=onion count=12")
 939                      + HelpExampleRpc("getnodeaddresses", "8")
 940                      + HelpExampleRpc("getnodeaddresses", "4, \"i2p\"")
 941                  },
 942          [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
 943  {
 944      NodeContext& node = EnsureAnyNodeContext(request.context);
 945      const CConnman& connman = EnsureConnman(node);
 946  
 947      const int count{request.params[0].isNull() ? 1 : request.params[0].getInt<int>()};
 948      if (count < 0) throw JSONRPCError(RPC_INVALID_PARAMETER, "Address count out of range");
 949  
 950      const std::optional<Network> network{request.params[1].isNull() ? std::nullopt : std::optional<Network>{ParseNetwork(request.params[1].get_str())}};
 951      if (network == NET_UNROUTABLE) {
 952          throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Network not recognized: %s", request.params[1].get_str()));
 953      }
 954  
 955      // returns a shuffled list of CAddress
 956      const std::vector<CAddress> vAddr{connman.GetAddressesUnsafe(count, /*max_pct=*/0, network)};
 957      UniValue ret(UniValue::VARR);
 958  
 959      for (const CAddress& addr : vAddr) {
 960          UniValue obj(UniValue::VOBJ);
 961          obj.pushKV("time", TicksSinceEpoch<std::chrono::seconds>(addr.nTime));
 962          obj.pushKV("services", static_cast<std::underlying_type_t<decltype(addr.nServices)>>(addr.nServices));
 963          obj.pushKV("address", addr.ToStringAddr());
 964          obj.pushKV("port", addr.GetPort());
 965          obj.pushKV("network", GetNetworkName(addr.GetNetClass()));
 966          ret.push_back(std::move(obj));
 967      }
 968      return ret;
 969  },
 970      };
 971  }
 972  
 973  static RPCHelpMan addpeeraddress()
 974  {
 975      return RPCHelpMan{"addpeeraddress",
 976          "Add the address of a potential peer to an address manager table. This RPC is for testing only.",
 977          {
 978              {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The IP address of the peer"},
 979              {"port", RPCArg::Type::NUM, RPCArg::Optional::NO, "The port of the peer"},
 980              {"tried", RPCArg::Type::BOOL, RPCArg::Default{false}, "If true, attempt to add the peer to the tried addresses table"},
 981          },
 982          RPCResult{
 983              RPCResult::Type::OBJ, "", "",
 984              {
 985                  {RPCResult::Type::BOOL, "success", "whether the peer address was successfully added to the address manager table"},
 986                  {RPCResult::Type::STR, "error", /*optional=*/true, "error description, if the address could not be added"},
 987              },
 988          },
 989          RPCExamples{
 990              HelpExampleCli("addpeeraddress", "\"1.2.3.4\" 8333 true")
 991      + HelpExampleRpc("addpeeraddress", "\"1.2.3.4\", 8333, true")
 992          },
 993          [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
 994  {
 995      AddrMan& addrman = EnsureAnyAddrman(request.context);
 996  
 997      const std::string& addr_string{request.params[0].get_str()};
 998      const auto port{request.params[1].getInt<uint16_t>()};
 999      const bool tried{request.params[2].isNull() ? false : request.params[2].get_bool()};
1000  
1001      UniValue obj(UniValue::VOBJ);
1002      std::optional<CNetAddr> net_addr{LookupHost(addr_string, false)};
1003      if (!net_addr.has_value()) {
1004          throw JSONRPCError(RPC_CLIENT_INVALID_IP_OR_SUBNET, "Invalid IP address");
1005      }
1006  
1007      bool success{false};
1008  
1009      CService service{net_addr.value(), port};
1010      CAddress address{MaybeFlipIPv6toCJDNS(service), ServiceFlags{NODE_NETWORK | NODE_WITNESS}};
1011      address.nTime = Now<NodeSeconds>();
1012      // The source address is set equal to the address. This is equivalent to the peer
1013      // announcing itself.
1014      if (addrman.Add({address}, address)) {
1015          success = true;
1016          if (tried) {
1017              // Attempt to move the address to the tried addresses table.
1018              if (!addrman.Good(address)) {
1019                  success = false;
1020                  obj.pushKV("error", "failed-adding-to-tried");
1021              }
1022          }
1023      } else {
1024          obj.pushKV("error", "failed-adding-to-new");
1025      }
1026  
1027      obj.pushKV("success", success);
1028      return obj;
1029  },
1030      };
1031  }
1032  
1033  static RPCHelpMan sendmsgtopeer()
1034  {
1035      return RPCHelpMan{
1036          "sendmsgtopeer",
1037          "Send a p2p message to a peer specified by id.\n"
1038          "The message type and body must be provided, the message header will be generated.\n"
1039          "This RPC is for testing only.",
1040          {
1041              {"peer_id", RPCArg::Type::NUM, RPCArg::Optional::NO, "The peer to send the message to."},
1042              {"msg_type", RPCArg::Type::STR, RPCArg::Optional::NO, strprintf("The message type (maximum length %i)", CMessageHeader::MESSAGE_TYPE_SIZE)},
1043              {"msg", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The serialized message body to send, in hex, without a message header"},
1044          },
1045          RPCResult{RPCResult::Type::OBJ, "", "", std::vector<RPCResult>{}},
1046          RPCExamples{
1047              HelpExampleCli("sendmsgtopeer", "0 \"addr\" \"ffffff\"") + HelpExampleRpc("sendmsgtopeer", "0 \"addr\" \"ffffff\"")},
1048          [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue {
1049              const NodeId peer_id{request.params[0].getInt<int64_t>()};
1050              const auto msg_type{self.Arg<std::string_view>("msg_type")};
1051              if (msg_type.size() > CMessageHeader::MESSAGE_TYPE_SIZE) {
1052                  throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Error: msg_type too long, max length is %i", CMessageHeader::MESSAGE_TYPE_SIZE));
1053              }
1054              auto msg{TryParseHex<unsigned char>(self.Arg<std::string_view>("msg"))};
1055              if (!msg.has_value()) {
1056                  throw JSONRPCError(RPC_INVALID_PARAMETER, "Error parsing input for msg");
1057              }
1058  
1059              NodeContext& node = EnsureAnyNodeContext(request.context);
1060              CConnman& connman = EnsureConnman(node);
1061  
1062              CSerializedNetMsg msg_ser;
1063              msg_ser.data = msg.value();
1064              msg_ser.m_type = msg_type;
1065  
1066              bool success = connman.ForNode(peer_id, [&](CNode* node) {
1067                  connman.PushMessage(node, std::move(msg_ser));
1068                  return true;
1069              });
1070  
1071              if (!success) {
1072                  throw JSONRPCError(RPC_MISC_ERROR, "Error: Could not send message to peer");
1073              }
1074  
1075              UniValue ret{UniValue::VOBJ};
1076              return ret;
1077          },
1078      };
1079  }
1080  
1081  static RPCHelpMan getaddrmaninfo()
1082  {
1083      return RPCHelpMan{
1084          "getaddrmaninfo",
1085          "Provides information about the node's address manager by returning the number of "
1086          "addresses in the `new` and `tried` tables and their sum for all networks.\n",
1087          {},
1088          RPCResult{
1089              RPCResult::Type::OBJ_DYN, "", "json object with network type as keys", {
1090                  {RPCResult::Type::OBJ, "network", "the network (" + Join(GetNetworkNames(), ", ") + ", all_networks)", {
1091                  {RPCResult::Type::NUM, "new", "number of addresses in the new table, which represent potential peers the node has discovered but hasn't yet successfully connected to."},
1092                  {RPCResult::Type::NUM, "tried", "number of addresses in the tried table, which represent peers the node has successfully connected to in the past."},
1093                  {RPCResult::Type::NUM, "total", "total number of addresses in both new/tried tables"},
1094              }},
1095          }},
1096          RPCExamples{HelpExampleCli("getaddrmaninfo", "") + HelpExampleRpc("getaddrmaninfo", "")},
1097          [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue {
1098              AddrMan& addrman = EnsureAnyAddrman(request.context);
1099  
1100              UniValue ret(UniValue::VOBJ);
1101              for (int n = 0; n < NET_MAX; ++n) {
1102                  enum Network network = static_cast<enum Network>(n);
1103                  if (network == NET_UNROUTABLE || network == NET_INTERNAL) continue;
1104                  UniValue obj(UniValue::VOBJ);
1105                  obj.pushKV("new", addrman.Size(network, true));
1106                  obj.pushKV("tried", addrman.Size(network, false));
1107                  obj.pushKV("total", addrman.Size(network));
1108                  ret.pushKV(GetNetworkName(network), std::move(obj));
1109              }
1110              UniValue obj(UniValue::VOBJ);
1111              obj.pushKV("new", addrman.Size(std::nullopt, true));
1112              obj.pushKV("tried", addrman.Size(std::nullopt, false));
1113              obj.pushKV("total", addrman.Size());
1114              ret.pushKV("all_networks", std::move(obj));
1115              return ret;
1116          },
1117      };
1118  }
1119  
1120  UniValue AddrmanEntryToJSON(const AddrInfo& info, const CConnman& connman)
1121  {
1122      UniValue ret(UniValue::VOBJ);
1123      ret.pushKV("address", info.ToStringAddr());
1124      const uint32_t mapped_as{connman.GetMappedAS(info)};
1125      if (mapped_as) {
1126          ret.pushKV("mapped_as", mapped_as);
1127      }
1128      ret.pushKV("port", info.GetPort());
1129      ret.pushKV("services", static_cast<std::underlying_type_t<decltype(info.nServices)>>(info.nServices));
1130      ret.pushKV("time", TicksSinceEpoch<std::chrono::seconds>(info.nTime));
1131      ret.pushKV("network", GetNetworkName(info.GetNetClass()));
1132      ret.pushKV("source", info.source.ToStringAddr());
1133      ret.pushKV("source_network", GetNetworkName(info.source.GetNetClass()));
1134      const uint32_t source_mapped_as{connman.GetMappedAS(info.source)};
1135      if (source_mapped_as) {
1136          ret.pushKV("source_mapped_as", source_mapped_as);
1137      }
1138      return ret;
1139  }
1140  
1141  UniValue AddrmanTableToJSON(const std::vector<std::pair<AddrInfo, AddressPosition>>& tableInfos, const CConnman& connman)
1142  {
1143      UniValue table(UniValue::VOBJ);
1144      for (const auto& e : tableInfos) {
1145          AddrInfo info = e.first;
1146          AddressPosition location = e.second;
1147          std::ostringstream key;
1148          key << location.bucket << "/" << location.position;
1149          // Address manager tables have unique entries so there is no advantage
1150          // in using UniValue::pushKV, which checks if the key already exists
1151          // in O(N). UniValue::pushKVEnd is used instead which currently is O(1).
1152          table.pushKVEnd(key.str(), AddrmanEntryToJSON(info, connman));
1153      }
1154      return table;
1155  }
1156  
1157  static RPCHelpMan getrawaddrman()
1158  {
1159      return RPCHelpMan{"getrawaddrman",
1160          "EXPERIMENTAL warning: this call may be changed in future releases.\n"
1161          "\nReturns information on all address manager entries for the new and tried tables.\n",
1162          {},
1163          RPCResult{
1164              RPCResult::Type::OBJ_DYN, "", "", {
1165                  {RPCResult::Type::OBJ_DYN, "table", "buckets with addresses in the address manager table ( new, tried )", {
1166                      {RPCResult::Type::OBJ, "bucket/position", "the location in the address manager table (<bucket>/<position>)", {
1167                          {RPCResult::Type::STR, "address", "The address of the node"},
1168                          {RPCResult::Type::NUM, "mapped_as", /*optional=*/true, "Mapped AS (Autonomous System) number at the end of the BGP route to the peer, used for diversifying peer selection (only displayed if the -asmap config option is set)"},
1169                          {RPCResult::Type::NUM, "port", "The port number of the node"},
1170                          {RPCResult::Type::STR, "network", "The network (" + Join(GetNetworkNames(), ", ") + ") of the address"},
1171                          {RPCResult::Type::NUM, "services", "The services offered by the node"},
1172                          {RPCResult::Type::NUM_TIME, "time", "The " + UNIX_EPOCH_TIME + " when the node was last seen"},
1173                          {RPCResult::Type::STR, "source", "The address that relayed the address to us"},
1174                          {RPCResult::Type::STR, "source_network", "The network (" + Join(GetNetworkNames(), ", ") + ") of the source address"},
1175                          {RPCResult::Type::NUM, "source_mapped_as", /*optional=*/true, "Mapped AS (Autonomous System) number at the end of the BGP route to the source, used for diversifying peer selection (only displayed if the -asmap config option is set)"}
1176                      }}
1177                  }}
1178              }
1179          },
1180          RPCExamples{
1181              HelpExampleCli("getrawaddrman", "")
1182              + HelpExampleRpc("getrawaddrman", "")
1183          },
1184          [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue {
1185              AddrMan& addrman = EnsureAnyAddrman(request.context);
1186              NodeContext& node_context = EnsureAnyNodeContext(request.context);
1187              CConnman& connman = EnsureConnman(node_context);
1188  
1189              UniValue ret(UniValue::VOBJ);
1190              ret.pushKV("new", AddrmanTableToJSON(addrman.GetEntries(false), connman));
1191              ret.pushKV("tried", AddrmanTableToJSON(addrman.GetEntries(true), connman));
1192              return ret;
1193          },
1194      };
1195  }
1196  
1197  void RegisterNetRPCCommands(CRPCTable& t)
1198  {
1199      static const CRPCCommand commands[]{
1200          {"network", &getconnectioncount},
1201          {"network", &ping},
1202          {"network", &getpeerinfo},
1203          {"network", &addnode},
1204          {"network", &disconnectnode},
1205          {"network", &getaddednodeinfo},
1206          {"network", &getnettotals},
1207          {"network", &getnetworkinfo},
1208          {"network", &setban},
1209          {"network", &listbanned},
1210          {"network", &clearbanned},
1211          {"network", &setnetworkactive},
1212          {"network", &getnodeaddresses},
1213          {"network", &getaddrmaninfo},
1214          {"hidden", &addconnection},
1215          {"hidden", &addpeeraddress},
1216          {"hidden", &sendmsgtopeer},
1217          {"hidden", &getrawaddrman},
1218      };
1219      for (const auto& c : commands) {
1220          t.appendCommand(c.name, &c);
1221      }
1222  }