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