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