LCOV - code coverage report
Current view: top level - src/rpc - net.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 577 608 94.9 %
Date: 2020-09-26 01:30:44 Functions: 18 18 100.0 %

          Line data    Source code
       1             : // Copyright (c) 2009-2020 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 <banman.h>
       8             : #include <clientversion.h>
       9             : #include <core_io.h>
      10             : #include <net.h>
      11             : #include <net_permissions.h>
      12             : #include <net_processing.h>
      13             : #include <net_types.h> // For banmap_t
      14             : #include <netbase.h>
      15             : #include <node/context.h>
      16             : #include <policy/settings.h>
      17             : #include <rpc/blockchain.h>
      18             : #include <rpc/protocol.h>
      19             : #include <rpc/util.h>
      20             : #include <sync.h>
      21             : #include <timedata.h>
      22             : #include <util/strencodings.h>
      23             : #include <util/string.h>
      24             : #include <util/system.h>
      25             : #include <util/translation.h>
      26             : #include <validation.h>
      27             : #include <version.h>
      28             : #include <warnings.h>
      29             : 
      30             : #include <univalue.h>
      31             : 
      32           9 : static UniValue getconnectioncount(const JSONRPCRequest& request)
      33             : {
      34          27 :             RPCHelpMan{"getconnectioncount",
      35           9 :                 "\nReturns the number of connections to other nodes.\n",
      36           9 :                 {},
      37           9 :                 RPCResult{
      38           9 :                     RPCResult::Type::NUM, "", "The connection count"
      39             :                 },
      40           9 :                 RPCExamples{
      41           9 :                     HelpExampleCli("getconnectioncount", "")
      42           9 :             + HelpExampleRpc("getconnectioncount", "")
      43             :                 },
      44           9 :             }.Check(request);
      45             : 
      46           5 :     NodeContext& node = EnsureNodeContext(request.context);
      47           5 :     if(!node.connman)
      48           0 :         throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
      49             : 
      50           5 :     return (int)node.connman->GetNodeCount(CConnman::CONNECTIONS_ALL);
      51           9 : }
      52             : 
      53           6 : static UniValue ping(const JSONRPCRequest& request)
      54             : {
      55          18 :             RPCHelpMan{"ping",
      56           6 :                 "\nRequests that a ping be sent to all other nodes, to measure ping time.\n"
      57             :                 "Results provided in getpeerinfo, pingtime and pingwait fields are decimal seconds.\n"
      58             :                 "Ping command is handled in queue with all other commands, so it measures processing backlog, not just network ping.\n",
      59           6 :                 {},
      60           6 :                 RPCResult{RPCResult::Type::NONE, "", ""},
      61           6 :                 RPCExamples{
      62           6 :                     HelpExampleCli("ping", "")
      63           6 :             + HelpExampleRpc("ping", "")
      64             :                 },
      65           6 :             }.Check(request);
      66             : 
      67           2 :     NodeContext& node = EnsureNodeContext(request.context);
      68           2 :     if(!node.connman)
      69           0 :         throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
      70             : 
      71             :     // Request that each node send a ping during next message processing pass
      72           5 :     node.connman->ForEachNode([](CNode* pnode) {
      73           3 :         pnode->fPingQueued = true;
      74           3 :     });
      75           2 :     return NullUniValue;
      76           6 : }
      77             : 
      78        4764 : static UniValue getpeerinfo(const JSONRPCRequest& request)
      79             : {
      80      161976 :             RPCHelpMan{"getpeerinfo",
      81        4764 :                 "\nReturns data about each connected network node as a json array of objects.\n",
      82        4764 :                 {},
      83        4764 :                 RPCResult{
      84        4764 :                     RPCResult::Type::ARR, "", "",
      85        9528 :                     {
      86        9528 :                         {RPCResult::Type::OBJ, "", "",
      87        4764 :                         {
      88      152476 :                             {
      89        4764 :                             {RPCResult::Type::NUM, "id", "Peer index"},
      90        4764 :                             {RPCResult::Type::STR, "addr", "(host:port) The IP address and port of the peer"},
      91        4764 :                             {RPCResult::Type::STR, "addrbind", "(ip:port) Bind address of the connection to the peer"},
      92        4764 :                             {RPCResult::Type::STR, "addrlocal", "(ip:port) Local address as reported by the peer"},
      93        4764 :                             {RPCResult::Type::NUM, "mapped_as", "The AS in the BGP route to the peer used for diversifying\n"
      94             :                                                                 "peer selection (only available if the asmap config flag is set)"},
      95        4764 :                             {RPCResult::Type::STR_HEX, "services", "The services offered"},
      96        9528 :                             {RPCResult::Type::ARR, "servicesnames", "the services offered, in human-readable form",
      97        9528 :                             {
      98        4764 :                                 {RPCResult::Type::STR, "SERVICE_NAME", "the service name if it is recognised"}
      99             :                             }},
     100        4764 :                             {RPCResult::Type::BOOL, "relaytxes", "Whether peer has asked us to relay transactions to it"},
     101        4764 :                             {RPCResult::Type::NUM_TIME, "lastsend", "The " + UNIX_EPOCH_TIME + " of the last send"},
     102        4764 :                             {RPCResult::Type::NUM_TIME, "lastrecv", "The " + UNIX_EPOCH_TIME + " of the last receive"},
     103        4764 :                             {RPCResult::Type::NUM_TIME, "last_transaction", "The " + UNIX_EPOCH_TIME + " of the last valid transaction received from this peer"},
     104        4764 :                             {RPCResult::Type::NUM_TIME, "last_block", "The " + UNIX_EPOCH_TIME + " of the last block received from this peer"},
     105        4764 :                             {RPCResult::Type::NUM, "bytessent", "The total bytes sent"},
     106        4764 :                             {RPCResult::Type::NUM, "bytesrecv", "The total bytes received"},
     107        4764 :                             {RPCResult::Type::NUM_TIME, "conntime", "The " + UNIX_EPOCH_TIME + " of the connection"},
     108        4764 :                             {RPCResult::Type::NUM, "timeoffset", "The time offset in seconds"},
     109        4764 :                             {RPCResult::Type::NUM, "pingtime", "ping time (if available)"},
     110        4764 :                             {RPCResult::Type::NUM, "minping", "minimum observed ping time (if any at all)"},
     111        4764 :                             {RPCResult::Type::NUM, "pingwait", "ping wait (if non-zero)"},
     112        4764 :                             {RPCResult::Type::NUM, "version", "The peer version, such as 70001"},
     113        4764 :                             {RPCResult::Type::STR, "subver", "The string version"},
     114        4764 :                             {RPCResult::Type::BOOL, "inbound", "Inbound (true) or Outbound (false)"},
     115        4764 :                             {RPCResult::Type::BOOL, "addnode", "Whether connection was due to addnode/-connect or if it was an automatic/inbound connection"},
     116        4764 :                             {RPCResult::Type::NUM, "startingheight", "The starting height (block) of the peer"},
     117        4764 :                             {RPCResult::Type::NUM, "banscore", "The ban score (DEPRECATED, returned only if config option -deprecatedrpc=banscore is passed)"},
     118        4764 :                             {RPCResult::Type::NUM, "synced_headers", "The last header we have in common with this peer"},
     119        4764 :                             {RPCResult::Type::NUM, "synced_blocks", "The last block we have in common with this peer"},
     120        9528 :                             {RPCResult::Type::ARR, "inflight", "",
     121        9528 :                             {
     122        4764 :                                 {RPCResult::Type::NUM, "n", "The heights of blocks we're currently asking from this peer"},
     123             :                             }},
     124        4764 :                             {RPCResult::Type::BOOL, "whitelisted", "Whether the peer is whitelisted"},
     125        4764 :                             {RPCResult::Type::NUM, "minfeefilter", "The minimum fee rate for transactions this peer accepts"},
     126        9528 :                             {RPCResult::Type::OBJ_DYN, "bytessent_per_msg", "",
     127        9528 :                             {
     128        4764 :                                 {RPCResult::Type::NUM, "msg", "The total bytes sent aggregated by message type\n"
     129             :                                                               "When a message type is not listed in this json object, the bytes sent are 0.\n"
     130             :                                                               "Only known message types can appear as keys in the object."}
     131             :                             }},
     132        9528 :                             {RPCResult::Type::OBJ, "bytesrecv_per_msg", "",
     133        9528 :                             {
     134        9528 :                                 {RPCResult::Type::NUM, "msg", "The total bytes received aggregated by message type\n"
     135             :                                                               "When a message type is not listed in this json object, the bytes received are 0.\n"
     136        4764 :                                                               "Only known message types can appear as keys in the object and all bytes received of unknown message types are listed under '"+NET_MESSAGE_COMMAND_OTHER+"'."}
     137             :                             }},
     138             :                         }},
     139             :                     }},
     140             :                 },
     141        4764 :                 RPCExamples{
     142        4764 :                     HelpExampleCli("getpeerinfo", "")
     143        4764 :             + HelpExampleRpc("getpeerinfo", "")
     144             :                 },
     145        4764 :             }.Check(request);
     146             : 
     147        4760 :     NodeContext& node = EnsureNodeContext(request.context);
     148        4760 :     if(!node.connman)
     149           0 :         throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
     150             : 
     151        4760 :     std::vector<CNodeStats> vstats;
     152        4760 :     node.connman->GetNodeStats(vstats);
     153             : 
     154        4760 :     UniValue ret(UniValue::VARR);
     155             : 
     156       13395 :     for (const CNodeStats& stats : vstats) {
     157        8635 :         UniValue obj(UniValue::VOBJ);
     158        8635 :         CNodeStateStats statestats;
     159        8635 :         bool fStateStats = GetNodeStateStats(stats.nodeid, statestats);
     160        8635 :         obj.pushKV("id", stats.nodeid);
     161        8635 :         obj.pushKV("addr", stats.addrName);
     162        8635 :         if (!(stats.addrLocal.empty()))
     163         652 :             obj.pushKV("addrlocal", stats.addrLocal);
     164        8635 :         if (stats.addrBind.IsValid())
     165        8635 :             obj.pushKV("addrbind", stats.addrBind.ToString());
     166        8635 :         if (stats.m_mapped_as != 0) {
     167           0 :             obj.pushKV("mapped_as", uint64_t(stats.m_mapped_as));
     168           0 :         }
     169        8635 :         obj.pushKV("services", strprintf("%016x", stats.nServices));
     170        8635 :         obj.pushKV("servicesnames", GetServicesNames(stats.nServices));
     171        8635 :         obj.pushKV("relaytxes", stats.fRelayTxes);
     172        8635 :         obj.pushKV("lastsend", stats.nLastSend);
     173        8635 :         obj.pushKV("lastrecv", stats.nLastRecv);
     174        8635 :         obj.pushKV("last_transaction", stats.nLastTXTime);
     175        8635 :         obj.pushKV("last_block", stats.nLastBlockTime);
     176        8635 :         obj.pushKV("bytessent", stats.nSendBytes);
     177        8635 :         obj.pushKV("bytesrecv", stats.nRecvBytes);
     178        8635 :         obj.pushKV("conntime", stats.nTimeConnected);
     179        8635 :         obj.pushKV("timeoffset", stats.nTimeOffset);
     180        8635 :         if (stats.m_ping_usec > 0) {
     181        8342 :             obj.pushKV("pingtime", ((double)stats.m_ping_usec) / 1e6);
     182        8342 :         }
     183        8635 :         if (stats.m_min_ping_usec < std::numeric_limits<int64_t>::max()) {
     184        8342 :             obj.pushKV("minping", ((double)stats.m_min_ping_usec) / 1e6);
     185        8342 :         }
     186        8635 :         if (stats.m_ping_wait_usec > 0) {
     187         251 :             obj.pushKV("pingwait", ((double)stats.m_ping_wait_usec) / 1e6);
     188         251 :         }
     189        8635 :         obj.pushKV("version", stats.nVersion);
     190             :         // Use the sanitized form of subver here, to avoid tricksy remote peers from
     191             :         // corrupting or modifying the JSON output by putting special characters in
     192             :         // their ver message.
     193        8635 :         obj.pushKV("subver", stats.cleanSubVer);
     194        8635 :         obj.pushKV("inbound", stats.fInbound);
     195        8635 :         obj.pushKV("addnode", stats.m_manual_connection);
     196        8635 :         obj.pushKV("startingheight", stats.nStartingHeight);
     197        8635 :         if (fStateStats) {
     198        8635 :             if (IsDeprecatedRPCEnabled("banscore")) {
     199             :                 // banscore is deprecated in v0.21 for removal in v0.22
     200           4 :                 obj.pushKV("banscore", statestats.m_misbehavior_score);
     201           4 :             }
     202        8635 :             obj.pushKV("synced_headers", statestats.nSyncHeight);
     203        8635 :             obj.pushKV("synced_blocks", statestats.nCommonHeight);
     204        8635 :             UniValue heights(UniValue::VARR);
     205       12407 :             for (const int height : statestats.vHeightInFlight) {
     206        3772 :                 heights.push_back(height);
     207             :             }
     208        8635 :             obj.pushKV("inflight", heights);
     209        8635 :         }
     210        8635 :         obj.pushKV("whitelisted", stats.m_legacyWhitelisted);
     211        8635 :         UniValue permissions(UniValue::VARR);
     212       13100 :         for (const auto& permission : NetPermissions::ToStrings(stats.m_permissionFlags)) {
     213        4465 :             permissions.push_back(permission);
     214             :         }
     215        8635 :         obj.pushKV("permissions", permissions);
     216        8635 :         obj.pushKV("minfeefilter", ValueFromAmount(stats.minFeeFilter));
     217             : 
     218        8635 :         UniValue sendPerMsgCmd(UniValue::VOBJ);
     219      121551 :         for (const auto& i : stats.mapSendBytesPerMsgCmd) {
     220      112916 :             if (i.second > 0)
     221      112916 :                 sendPerMsgCmd.pushKV(i.first, i.second);
     222           0 :         }
     223        8635 :         obj.pushKV("bytessent_per_msg", sendPerMsgCmd);
     224             : 
     225        8635 :         UniValue recvPerMsgCmd(UniValue::VOBJ);
     226      293590 :         for (const auto& i : stats.mapRecvBytesPerMsgCmd) {
     227      284955 :             if (i.second > 0)
     228      108869 :                 recvPerMsgCmd.pushKV(i.first, i.second);
     229           0 :         }
     230        8635 :         obj.pushKV("bytesrecv_per_msg", recvPerMsgCmd);
     231             : 
     232        8635 :         ret.push_back(obj);
     233        8635 :     }
     234             : 
     235             :     return ret;
     236        4812 : }
     237             : 
     238         258 : static UniValue addnode(const JSONRPCRequest& request)
     239             : {
     240         258 :     std::string strCommand;
     241         258 :     if (!request.params[1].isNull())
     242         254 :         strCommand = request.params[1].get_str();
     243         260 :     if (request.fHelp || request.params.size() != 2 ||
     244         254 :         (strCommand != "onetry" && strCommand != "add" && strCommand != "remove"))
     245          12 :         throw std::runtime_error(
     246           8 :             RPCHelpMan{"addnode",
     247           4 :                 "\nAttempts to add or remove a node from the addnode list.\n"
     248             :                 "Or try a connection to a node once.\n"
     249             :                 "Nodes added using addnode (or -connect) are protected from DoS disconnection and are not required to be\n"
     250             :                 "full nodes/support SegWit as other outbound peers are (though such peers will not be synced from).\n",
     251          16 :                 {
     252           4 :                     {"node", RPCArg::Type::STR, RPCArg::Optional::NO, "The node (see getpeerinfo for nodes)"},
     253           4 :                     {"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"},
     254             :                 },
     255           4 :                 RPCResult{RPCResult::Type::NONE, "", ""},
     256           4 :                 RPCExamples{
     257           4 :                     HelpExampleCli("addnode", "\"192.168.0.6:8333\" \"onetry\"")
     258           4 :             + HelpExampleRpc("addnode", "\"192.168.0.6:8333\", \"onetry\"")
     259             :                 },
     260           4 :             }.ToString());
     261             : 
     262         254 :     NodeContext& node = EnsureNodeContext(request.context);
     263         254 :     if(!node.connman)
     264           0 :         throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
     265             : 
     266         254 :     std::string strNode = request.params[0].get_str();
     267             : 
     268         254 :     if (strCommand == "onetry")
     269             :     {
     270         250 :         CAddress addr;
     271         250 :         node.connman->OpenNetworkConnection(addr, false, nullptr, strNode.c_str(), ConnectionType::MANUAL);
     272         250 :         return NullUniValue;
     273         250 :     }
     274             : 
     275           4 :     if (strCommand == "add")
     276             :     {
     277           2 :         if(!node.connman->AddNode(strNode))
     278           1 :             throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: Node already added");
     279             :     }
     280           2 :     else if(strCommand == "remove")
     281             :     {
     282           2 :         if(!node.connman->RemoveAddedNode(strNode))
     283           1 :             throw JSONRPCError(RPC_CLIENT_NODE_NOT_ADDED, "Error: Node could not be removed. It has not been added previously.");
     284             :     }
     285             : 
     286           2 :     return NullUniValue;
     287         278 : }
     288             : 
     289          32 : static UniValue disconnectnode(const JSONRPCRequest& request)
     290             : {
     291         130 :             RPCHelpMan{"disconnectnode",
     292          32 :                 "\nImmediately disconnects from the specified peer node.\n"
     293             :                 "\nStrictly one out of 'address' and 'nodeid' can be provided to identify the node.\n"
     294             :                 "\nTo disconnect by nodeid, either set 'address' to the empty string, or call using the named 'nodeid' argument only.\n",
     295         100 :                 {
     296          32 :                     {"address", RPCArg::Type::STR, /* default */ "fallback to nodeid", "The IP address/port of the node"},
     297          32 :                     {"nodeid", RPCArg::Type::NUM, /* default */ "fallback to address", "The node ID (see getpeerinfo for node IDs)"},
     298             :                 },
     299          32 :                 RPCResult{RPCResult::Type::NONE, "", ""},
     300          32 :                 RPCExamples{
     301          32 :                     HelpExampleCli("disconnectnode", "\"192.168.0.6:8333\"")
     302          32 :             + HelpExampleCli("disconnectnode", "\"\" 1")
     303          32 :             + HelpExampleRpc("disconnectnode", "\"192.168.0.6:8333\"")
     304          32 :             + HelpExampleRpc("disconnectnode", "\"\", 1")
     305             :                 },
     306          32 :             }.Check(request);
     307             : 
     308          28 :     NodeContext& node = EnsureNodeContext(request.context);
     309          28 :     if(!node.connman)
     310           0 :         throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
     311             : 
     312             :     bool success;
     313          28 :     const UniValue &address_arg = request.params[0];
     314          28 :     const UniValue &id_arg = request.params[1];
     315             : 
     316          28 :     if (!address_arg.isNull() && id_arg.isNull()) {
     317             :         /* handle disconnect-by-address */
     318           2 :         success = node.connman->DisconnectNode(address_arg.get_str());
     319          28 :     } else if (!id_arg.isNull() && (address_arg.isNull() || (address_arg.isStr() && address_arg.get_str().empty()))) {
     320             :         /* handle disconnect-by-id */
     321          25 :         NodeId nodeid = (NodeId) id_arg.get_int64();
     322          25 :         success = node.connman->DisconnectNode(nodeid);
     323             :     } else {
     324           1 :         throw JSONRPCError(RPC_INVALID_PARAMS, "Only one of address and nodeid should be provided.");
     325             :     }
     326             : 
     327          27 :     if (!success) {
     328           1 :         throw JSONRPCError(RPC_CLIENT_NODE_NOT_CONNECTED, "Node not found in connected nodes");
     329             :     }
     330             : 
     331          26 :     return NullUniValue;
     332          48 : }
     333             : 
     334           8 : static UniValue getaddednodeinfo(const JSONRPCRequest& request)
     335             : {
     336          49 :             RPCHelpMan{"getaddednodeinfo",
     337           8 :                 "\nReturns information about the given added node, or all added nodes\n"
     338             :                 "(note that onetry addnodes are not listed here)\n",
     339          16 :                 {
     340           8 :                     {"node", RPCArg::Type::STR, /* default */ "all nodes", "If provided, return information about this specific node, otherwise all nodes are returned."},
     341             :                 },
     342           8 :                 RPCResult{
     343           8 :                     RPCResult::Type::ARR, "", "",
     344          16 :                     {
     345          16 :                         {RPCResult::Type::OBJ, "", "",
     346          36 :                         {
     347           8 :                             {RPCResult::Type::STR, "addednode", "The node IP address or name (as provided to addnode)"},
     348           8 :                             {RPCResult::Type::BOOL, "connected", "If connected"},
     349          16 :                             {RPCResult::Type::ARR, "addresses", "Only when connected = true",
     350          16 :                             {
     351          16 :                                 {RPCResult::Type::OBJ, "", "",
     352          28 :                                 {
     353           8 :                                     {RPCResult::Type::STR, "address", "The bitcoin server IP and port we're connected to"},
     354           8 :                                     {RPCResult::Type::STR, "connected", "connection, inbound or outbound"},
     355             :                                 }},
     356             :                             }},
     357             :                         }},
     358             :                     }
     359             :                 },
     360           8 :                 RPCExamples{
     361           8 :                     HelpExampleCli("getaddednodeinfo", "\"192.168.0.201\"")
     362           8 :             + HelpExampleRpc("getaddednodeinfo", "\"192.168.0.201\"")
     363             :                 },
     364           8 :             }.Check(request);
     365             : 
     366           4 :     NodeContext& node = EnsureNodeContext(request.context);
     367           4 :     if(!node.connman)
     368           0 :         throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
     369             : 
     370           4 :     std::vector<AddedNodeInfo> vInfo = node.connman->GetAddedNodeInfo();
     371             : 
     372           4 :     if (!request.params[0].isNull()) {
     373             :         bool found = false;
     374           3 :         for (const AddedNodeInfo& info : vInfo) {
     375           1 :             if (info.strAddedNode == request.params[0].get_str()) {
     376           1 :                 vInfo.assign(1, info);
     377             :                 found = true;
     378           1 :                 break;
     379             :             }
     380           0 :         }
     381           2 :         if (!found) {
     382           1 :             throw JSONRPCError(RPC_CLIENT_NODE_NOT_ADDED, "Error: Node has not been added.");
     383             :         }
     384           2 :     }
     385             : 
     386           3 :     UniValue ret(UniValue::VARR);
     387             : 
     388           4 :     for (const AddedNodeInfo& info : vInfo) {
     389           1 :         UniValue obj(UniValue::VOBJ);
     390           1 :         obj.pushKV("addednode", info.strAddedNode);
     391           1 :         obj.pushKV("connected", info.fConnected);
     392           1 :         UniValue addresses(UniValue::VARR);
     393           1 :         if (info.fConnected) {
     394           0 :             UniValue address(UniValue::VOBJ);
     395           0 :             address.pushKV("address", info.resolvedAddress.ToString());
     396           0 :             address.pushKV("connected", info.fInbound ? "inbound" : "outbound");
     397           0 :             addresses.push_back(address);
     398           0 :         }
     399           1 :         obj.pushKV("addresses", addresses);
     400           1 :         ret.push_back(obj);
     401           1 :     }
     402             : 
     403             :     return ret;
     404          53 : }
     405             : 
     406          12 : static UniValue getnettotals(const JSONRPCRequest& request)
     407             : {
     408         132 :             RPCHelpMan{"getnettotals",
     409          12 :                 "\nReturns information about network traffic, including bytes in, bytes out,\n"
     410             :                 "and current time.\n",
     411          12 :                 {},
     412          12 :                 RPCResult{
     413          12 :                    RPCResult::Type::OBJ, "", "",
     414          64 :                    {
     415          12 :                        {RPCResult::Type::NUM, "totalbytesrecv", "Total bytes received"},
     416          12 :                        {RPCResult::Type::NUM, "totalbytessent", "Total bytes sent"},
     417          12 :                        {RPCResult::Type::NUM_TIME, "timemillis", "Current " + UNIX_EPOCH_TIME + " in milliseconds"},
     418          24 :                        {RPCResult::Type::OBJ, "uploadtarget", "",
     419          88 :                        {
     420          12 :                            {RPCResult::Type::NUM, "timeframe", "Length of the measuring timeframe in seconds"},
     421          12 :                            {RPCResult::Type::NUM, "target", "Target in bytes"},
     422          12 :                            {RPCResult::Type::BOOL, "target_reached", "True if target is reached"},
     423          12 :                            {RPCResult::Type::BOOL, "serve_historical_blocks", "True if serving historical blocks"},
     424          12 :                            {RPCResult::Type::NUM, "bytes_left_in_cycle", "Bytes left in current time cycle"},
     425          12 :                            {RPCResult::Type::NUM, "time_left_in_cycle", "Seconds left in current time cycle"},
     426             :                         }},
     427             :                     }
     428             :                 },
     429          12 :                 RPCExamples{
     430          12 :                     HelpExampleCli("getnettotals", "")
     431          12 :             + HelpExampleRpc("getnettotals", "")
     432             :                 },
     433          12 :             }.Check(request);
     434           8 :     NodeContext& node = EnsureNodeContext(request.context);
     435           8 :     if(!node.connman)
     436           0 :         throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
     437             : 
     438           8 :     UniValue obj(UniValue::VOBJ);
     439           8 :     obj.pushKV("totalbytesrecv", node.connman->GetTotalBytesRecv());
     440           8 :     obj.pushKV("totalbytessent", node.connman->GetTotalBytesSent());
     441           8 :     obj.pushKV("timemillis", GetTimeMillis());
     442             : 
     443           8 :     UniValue outboundLimit(UniValue::VOBJ);
     444           8 :     outboundLimit.pushKV("timeframe", node.connman->GetMaxOutboundTimeframe());
     445           8 :     outboundLimit.pushKV("target", node.connman->GetMaxOutboundTarget());
     446           8 :     outboundLimit.pushKV("target_reached", node.connman->OutboundTargetReached(false));
     447           8 :     outboundLimit.pushKV("serve_historical_blocks", !node.connman->OutboundTargetReached(true));
     448           8 :     outboundLimit.pushKV("bytes_left_in_cycle", node.connman->GetOutboundTargetBytesLeft());
     449           8 :     outboundLimit.pushKV("time_left_in_cycle", node.connman->GetMaxOutboundTimeLeftInCycle());
     450           8 :     obj.pushKV("uploadtarget", outboundLimit);
     451             :     return obj;
     452          28 : }
     453             : 
     454          44 : static UniValue GetNetworksInfo()
     455             : {
     456          44 :     UniValue networks(UniValue::VARR);
     457         264 :     for(int n=0; n<NET_MAX; ++n)
     458             :     {
     459             :         enum Network network = static_cast<enum Network>(n);
     460         220 :         if(network == NET_UNROUTABLE || network == NET_INTERNAL)
     461          88 :             continue;
     462         132 :         proxyType proxy;
     463         132 :         UniValue obj(UniValue::VOBJ);
     464         132 :         GetProxy(network, proxy);
     465         132 :         obj.pushKV("name", GetNetworkName(network));
     466         132 :         obj.pushKV("limited", !IsReachable(network));
     467         132 :         obj.pushKV("reachable", IsReachable(network));
     468         132 :         obj.pushKV("proxy", proxy.IsValid() ? proxy.proxy.ToStringIPPort() : std::string());
     469         132 :         obj.pushKV("proxy_randomize_credentials", proxy.randomize_credentials);
     470         132 :         networks.push_back(obj);
     471         132 :     }
     472             :     return networks;
     473          44 : }
     474             : 
     475          48 : static UniValue getnetworkinfo(const JSONRPCRequest& request)
     476             : {
     477        1152 :             RPCHelpMan{"getnetworkinfo",
     478          48 :                 "Returns an object containing various state info regarding P2P networking.\n",
     479          48 :                 {},
     480          48 :                 RPCResult{
     481          48 :                     RPCResult::Type::OBJ, "", "",
     482         860 :                     {
     483          48 :                         {RPCResult::Type::NUM, "version", "the server version"},
     484          48 :                         {RPCResult::Type::STR, "subversion", "the server subversion string"},
     485          48 :                         {RPCResult::Type::NUM, "protocolversion", "the protocol version"},
     486          48 :                         {RPCResult::Type::STR_HEX, "localservices", "the services we offer to the network"},
     487          96 :                         {RPCResult::Type::ARR, "localservicesnames", "the services we offer to the network, in human-readable form",
     488          96 :                         {
     489          48 :                             {RPCResult::Type::STR, "SERVICE_NAME", "the service name"},
     490             :                         }},
     491          48 :                         {RPCResult::Type::BOOL, "localrelay", "true if transaction relay is requested from peers"},
     492          48 :                         {RPCResult::Type::NUM, "timeoffset", "the time offset"},
     493          48 :                         {RPCResult::Type::NUM, "connections", "the total number of connections"},
     494          48 :                         {RPCResult::Type::NUM, "connections_in", "the number of inbound connections"},
     495          48 :                         {RPCResult::Type::NUM, "connections_out", "the number of outbound connections"},
     496          48 :                         {RPCResult::Type::BOOL, "networkactive", "whether p2p networking is enabled"},
     497          96 :                         {RPCResult::Type::ARR, "networks", "information per network",
     498          96 :                         {
     499          96 :                             {RPCResult::Type::OBJ, "", "",
     500         292 :                             {
     501          48 :                                 {RPCResult::Type::STR, "name", "network (ipv4, ipv6 or onion)"},
     502          48 :                                 {RPCResult::Type::BOOL, "limited", "is the network limited using -onlynet?"},
     503          48 :                                 {RPCResult::Type::BOOL, "reachable", "is the network reachable?"},
     504          48 :                                 {RPCResult::Type::STR, "proxy", "(\"host:port\") the proxy that is used for this network, or empty if none"},
     505          48 :                                 {RPCResult::Type::BOOL, "proxy_randomize_credentials", "Whether randomized credentials are used"},
     506             :                             }},
     507             :                         }},
     508          48 :                         {RPCResult::Type::NUM, "relayfee", "minimum relay fee for transactions in " + CURRENCY_UNIT + "/kB"},
     509          48 :                         {RPCResult::Type::NUM, "incrementalfee", "minimum fee increment for mempool limiting or BIP 125 replacement in " + CURRENCY_UNIT + "/kB"},
     510          96 :                         {RPCResult::Type::ARR, "localaddresses", "list of local addresses",
     511          96 :                         {
     512          96 :                             {RPCResult::Type::OBJ, "", "",
     513         196 :                             {
     514          48 :                                 {RPCResult::Type::STR, "address", "network address"},
     515          48 :                                 {RPCResult::Type::NUM, "port", "network port"},
     516          48 :                                 {RPCResult::Type::NUM, "score", "relative score"},
     517             :                             }},
     518             :                         }},
     519          48 :                         {RPCResult::Type::STR, "warnings", "any network and blockchain warnings"},
     520             :                     }
     521             :                 },
     522          48 :                 RPCExamples{
     523          48 :                     HelpExampleCli("getnetworkinfo", "")
     524          48 :             + HelpExampleRpc("getnetworkinfo", "")
     525             :                 },
     526          48 :             }.Check(request);
     527             : 
     528          44 :     LOCK(cs_main);
     529          44 :     UniValue obj(UniValue::VOBJ);
     530          44 :     obj.pushKV("version",       CLIENT_VERSION);
     531          44 :     obj.pushKV("subversion",    strSubVersion);
     532          44 :     obj.pushKV("protocolversion",PROTOCOL_VERSION);
     533          44 :     NodeContext& node = EnsureNodeContext(request.context);
     534          44 :     if (node.connman) {
     535          44 :         ServiceFlags services = node.connman->GetLocalServices();
     536          44 :         obj.pushKV("localservices", strprintf("%016x", services));
     537          44 :         obj.pushKV("localservicesnames", GetServicesNames(services));
     538          44 :     }
     539          44 :     obj.pushKV("localrelay", g_relay_txes);
     540          44 :     obj.pushKV("timeoffset",    GetTimeOffset());
     541          44 :     if (node.connman) {
     542          44 :         obj.pushKV("networkactive", node.connman->GetNetworkActive());
     543          44 :         obj.pushKV("connections", (int)node.connman->GetNodeCount(CConnman::CONNECTIONS_ALL));
     544          44 :         obj.pushKV("connections_in", (int)node.connman->GetNodeCount(CConnman::CONNECTIONS_IN));
     545          44 :         obj.pushKV("connections_out", (int)node.connman->GetNodeCount(CConnman::CONNECTIONS_OUT));
     546          44 :     }
     547          44 :     obj.pushKV("networks",      GetNetworksInfo());
     548          44 :     obj.pushKV("relayfee",      ValueFromAmount(::minRelayTxFee.GetFeePerK()));
     549          44 :     obj.pushKV("incrementalfee", ValueFromAmount(::incrementalRelayFee.GetFeePerK()));
     550          44 :     UniValue localAddresses(UniValue::VARR);
     551             :     {
     552          44 :         LOCK(cs_mapLocalHost);
     553          44 :         for (const std::pair<const CNetAddr, LocalServiceInfo> &item : mapLocalHost)
     554             :         {
     555           0 :             UniValue rec(UniValue::VOBJ);
     556           0 :             rec.pushKV("address", item.first.ToString());
     557           0 :             rec.pushKV("port", item.second.nPort);
     558           0 :             rec.pushKV("score", item.second.nScore);
     559           0 :             localAddresses.push_back(rec);
     560           0 :         }
     561          44 :     }
     562          44 :     obj.pushKV("localaddresses", localAddresses);
     563          44 :     obj.pushKV("warnings",       GetWarnings(false).original);
     564             :     return obj;
     565          96 : }
     566             : 
     567          29 : static UniValue setban(const JSONRPCRequest& request)
     568             : {
     569         145 :     const RPCHelpMan help{"setban",
     570          29 :                 "\nAttempts to add or remove an IP/Subnet from the banned list.\n",
     571         145 :                 {
     572          29 :                     {"subnet", RPCArg::Type::STR, RPCArg::Optional::NO, "The IP/Subnet (see getpeerinfo for nodes IP) with an optional netmask (default is /32 = single IP)"},
     573          29 :                     {"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"},
     574          29 :                     {"bantime", RPCArg::Type::NUM, /* 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)"},
     575          29 :                     {"absolute", RPCArg::Type::BOOL, /* default */ "false", "If set, the bantime must be an absolute timestamp expressed in " + UNIX_EPOCH_TIME},
     576             :                 },
     577          29 :                 RPCResult{RPCResult::Type::NONE, "", ""},
     578          29 :                 RPCExamples{
     579          29 :                     HelpExampleCli("setban", "\"192.168.0.6\" \"add\" 86400")
     580          29 :                             + HelpExampleCli("setban", "\"192.168.0.0/24\" \"add\"")
     581          29 :                             + HelpExampleRpc("setban", "\"192.168.0.6\", \"add\", 86400")
     582             :                 },
     583             :     };
     584          29 :     std::string strCommand;
     585          29 :     if (!request.params[1].isNull())
     586          24 :         strCommand = request.params[1].get_str();
     587          29 :     if (request.fHelp || !help.IsValidNumArgs(request.params.size()) || (strCommand != "add" && strCommand != "remove")) {
     588           5 :         throw std::runtime_error(help.ToString());
     589             :     }
     590          24 :     NodeContext& node = EnsureNodeContext(request.context);
     591          24 :     if (!node.banman) {
     592           0 :         throw JSONRPCError(RPC_DATABASE_ERROR, "Error: Ban database not loaded");
     593             :     }
     594             : 
     595          24 :     CSubNet subNet;
     596          24 :     CNetAddr netAddr;
     597             :     bool isSubnet = false;
     598             : 
     599          24 :     if (request.params[0].get_str().find('/') != std::string::npos)
     600          12 :         isSubnet = true;
     601             : 
     602          24 :     if (!isSubnet) {
     603          12 :         CNetAddr resolved;
     604          12 :         LookupHost(request.params[0].get_str(), resolved, false);
     605          12 :         netAddr = resolved;
     606          12 :     }
     607             :     else
     608          12 :         LookupSubNet(request.params[0].get_str(), subNet);
     609             : 
     610          24 :     if (! (isSubnet ? subNet.IsValid() : netAddr.IsValid()) )
     611           2 :         throw JSONRPCError(RPC_CLIENT_INVALID_IP_OR_SUBNET, "Error: Invalid IP/Subnet");
     612             : 
     613          22 :     if (strCommand == "add")
     614             :     {
     615          17 :         if (isSubnet ? node.banman->IsBanned(subNet) : node.banman->IsBanned(netAddr)) {
     616           3 :             throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: IP/Subnet already banned");
     617             :         }
     618             : 
     619             :         int64_t banTime = 0; //use standard bantime if not specified
     620          14 :         if (!request.params[2].isNull())
     621           4 :             banTime = request.params[2].get_int64();
     622             : 
     623             :         bool absolute = false;
     624          14 :         if (request.params[3].isTrue())
     625           1 :             absolute = true;
     626             : 
     627          14 :         if (isSubnet) {
     628           9 :             node.banman->Ban(subNet, banTime, absolute);
     629           9 :             if (node.connman) {
     630           9 :                 node.connman->DisconnectNode(subNet);
     631             :             }
     632             :         } else {
     633           5 :             node.banman->Ban(netAddr, banTime, absolute);
     634           5 :             if (node.connman) {
     635           5 :                 node.connman->DisconnectNode(netAddr);
     636             :             }
     637             :         }
     638          14 :     }
     639           5 :     else if(strCommand == "remove")
     640             :     {
     641           5 :         if (!( isSubnet ? node.banman->Unban(subNet) : node.banman->Unban(netAddr) )) {
     642           1 :             throw JSONRPCError(RPC_CLIENT_INVALID_IP_OR_SUBNET, "Error: Unban failed. Requested address/subnet was not previously manually banned.");
     643             :         }
     644             :     }
     645          18 :     return NullUniValue;
     646          29 : }
     647             : 
     648          23 : static UniValue listbanned(const JSONRPCRequest& request)
     649             : {
     650         115 :             RPCHelpMan{"listbanned",
     651          23 :                 "\nList all manually banned IPs/Subnets.\n",
     652          23 :                 {},
     653          46 :         RPCResult{RPCResult::Type::ARR, "", "",
     654          46 :             {
     655          46 :                 {RPCResult::Type::OBJ, "", "",
     656          96 :                     {
     657          23 :                         {RPCResult::Type::STR, "address", ""},
     658          23 :                         {RPCResult::Type::NUM_TIME, "banned_until", ""},
     659          23 :                         {RPCResult::Type::NUM_TIME, "ban_created", ""},
     660             :                     }},
     661             :             }},
     662          23 :                 RPCExamples{
     663          23 :                     HelpExampleCli("listbanned", "")
     664          23 :                             + HelpExampleRpc("listbanned", "")
     665             :                 },
     666          23 :             }.Check(request);
     667             : 
     668          19 :     NodeContext& node = EnsureNodeContext(request.context);
     669          19 :     if(!node.banman) {
     670           0 :         throw JSONRPCError(RPC_DATABASE_ERROR, "Error: Ban database not loaded");
     671             :     }
     672             : 
     673          19 :     banmap_t banMap;
     674          19 :     node.banman->GetBanned(banMap);
     675             : 
     676          19 :     UniValue bannedAddresses(UniValue::VARR);
     677          39 :     for (const auto& entry : banMap)
     678             :     {
     679          20 :         const CBanEntry& banEntry = entry.second;
     680          20 :         UniValue rec(UniValue::VOBJ);
     681          20 :         rec.pushKV("address", entry.first.ToString());
     682          20 :         rec.pushKV("banned_until", banEntry.nBanUntil);
     683          20 :         rec.pushKV("ban_created", banEntry.nCreateTime);
     684             : 
     685          20 :         bannedAddresses.push_back(rec);
     686          20 :     }
     687             : 
     688             :     return bannedAddresses;
     689          39 : }
     690             : 
     691          12 : static UniValue clearbanned(const JSONRPCRequest& request)
     692             : {
     693          36 :             RPCHelpMan{"clearbanned",
     694          12 :                 "\nClear all banned IPs.\n",
     695          12 :                 {},
     696          12 :                 RPCResult{RPCResult::Type::NONE, "", ""},
     697          12 :                 RPCExamples{
     698          12 :                     HelpExampleCli("clearbanned", "")
     699          12 :                             + HelpExampleRpc("clearbanned", "")
     700             :                 },
     701          12 :             }.Check(request);
     702           8 :     NodeContext& node = EnsureNodeContext(request.context);
     703           8 :     if (!node.banman) {
     704           0 :         throw JSONRPCError(RPC_DATABASE_ERROR, "Error: Ban database not loaded");
     705             :     }
     706             : 
     707           8 :     node.banman->ClearBanned();
     708             : 
     709           8 :     return NullUniValue;
     710          12 : }
     711             : 
     712           9 : static UniValue setnetworkactive(const JSONRPCRequest& request)
     713             : {
     714          27 :             RPCHelpMan{"setnetworkactive",
     715           9 :                 "\nDisable/enable all p2p network activity.\n",
     716          18 :                 {
     717           9 :                     {"state", RPCArg::Type::BOOL, RPCArg::Optional::NO, "true to enable networking, false to disable"},
     718             :                 },
     719           9 :                 RPCResult{RPCResult::Type::BOOL, "", "The value that was passed in"},
     720           9 :                 RPCExamples{""},
     721           9 :             }.Check(request);
     722             : 
     723           5 :     NodeContext& node = EnsureNodeContext(request.context);
     724           5 :     if (!node.connman) {
     725           0 :         throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
     726             :     }
     727             : 
     728           5 :     node.connman->SetNetworkActive(request.params[0].get_bool());
     729             : 
     730           5 :     return node.connman->GetNetworkActive();
     731          21 : }
     732             : 
     733           8 : static UniValue getnodeaddresses(const JSONRPCRequest& request)
     734             : {
     735          49 :             RPCHelpMan{"getnodeaddresses",
     736           8 :                 "\nReturn known addresses which can potentially be used to find new nodes in the network\n",
     737          16 :                 {
     738           8 :                     {"count", RPCArg::Type::NUM, /* default */ "1", "The maximum number of addresses to return. Specify 0 to return all known addresses."},
     739             :                 },
     740           8 :                 RPCResult{
     741           8 :                     RPCResult::Type::ARR, "", "",
     742          16 :                     {
     743          16 :                         {RPCResult::Type::OBJ, "", "",
     744          44 :                         {
     745           8 :                             {RPCResult::Type::NUM_TIME, "time", "The " + UNIX_EPOCH_TIME + " of when the node was last seen"},
     746           8 :                             {RPCResult::Type::NUM, "services", "The services offered"},
     747           8 :                             {RPCResult::Type::STR, "address", "The address of the node"},
     748           8 :                             {RPCResult::Type::NUM, "port", "The port of the node"},
     749             :                         }},
     750             :                     }
     751             :                 },
     752           8 :                 RPCExamples{
     753           8 :                     HelpExampleCli("getnodeaddresses", "8")
     754           8 :             + HelpExampleRpc("getnodeaddresses", "8")
     755             :                 },
     756           8 :             }.Check(request);
     757           4 :     NodeContext& node = EnsureNodeContext(request.context);
     758           4 :     if (!node.connman) {
     759           0 :         throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
     760             :     }
     761             : 
     762             :     int count = 1;
     763           4 :     if (!request.params[0].isNull()) {
     764           4 :         count = request.params[0].get_int();
     765           4 :         if (count < 0) {
     766           1 :             throw JSONRPCError(RPC_INVALID_PARAMETER, "Address count out of range");
     767             :         }
     768             :     }
     769             :     // returns a shuffled list of CAddress
     770           3 :     std::vector<CAddress> vAddr = node.connman->GetAddresses(count, /* max_pct */ 0);
     771           3 :     UniValue ret(UniValue::VARR);
     772             : 
     773       17648 :     for (const CAddress& addr : vAddr) {
     774       17645 :         UniValue obj(UniValue::VOBJ);
     775       17645 :         obj.pushKV("time", (int)addr.nTime);
     776       17645 :         obj.pushKV("services", (uint64_t)addr.nServices);
     777       17645 :         obj.pushKV("address", addr.ToStringIP());
     778       17645 :         obj.pushKV("port", addr.GetPort());
     779       17645 :         ret.push_back(obj);
     780       17645 :     }
     781             :     return ret;
     782          36 : }
     783             : 
     784       10000 : static UniValue addpeeraddress(const JSONRPCRequest& request)
     785             : {
     786       40000 :     RPCHelpMan{"addpeeraddress",
     787       10000 :         "\nAdd the address of a potential peer to the address manager. This RPC is for testing only.\n",
     788       30000 :         {
     789       10000 :             {"address", RPCArg::Type::STR, RPCArg::Optional::NO, "The IP address of the peer"},
     790       10000 :             {"port", RPCArg::Type::NUM, RPCArg::Optional::NO, "The port of the peer"},
     791             :         },
     792       10000 :         RPCResult{
     793       10000 :             RPCResult::Type::OBJ, "", "",
     794       20000 :             {
     795       10000 :                 {RPCResult::Type::BOOL, "success", "whether the peer address was successfully added to the address manager"},
     796             :             },
     797             :         },
     798       10000 :         RPCExamples{
     799       10000 :             HelpExampleCli("addpeeraddress", "\"1.2.3.4\" 8333")
     800       10000 :     + HelpExampleRpc("addpeeraddress", "\"1.2.3.4\", 8333")
     801             :         },
     802       10000 :     }.Check(request);
     803             : 
     804       10000 :     NodeContext& node = EnsureNodeContext(request.context);
     805       10000 :     if (!node.connman) {
     806           0 :         throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
     807             :     }
     808             : 
     809       10000 :     UniValue obj(UniValue::VOBJ);
     810             : 
     811       10000 :     std::string addr_string = request.params[0].get_str();
     812       10000 :     uint16_t port = request.params[1].get_int();
     813             : 
     814       10000 :     CNetAddr net_addr;
     815       10000 :     if (!LookupHost(addr_string, net_addr, false)) {
     816           0 :         obj.pushKV("success", false);
     817           0 :         return obj;
     818             :     }
     819       10000 :     CAddress address = CAddress({net_addr, port}, ServiceFlags(NODE_NETWORK|NODE_WITNESS));
     820       10000 :     address.nTime = GetAdjustedTime();
     821             :     // The source address is set equal to the address. This is equivalent to the peer
     822             :     // announcing itself.
     823       10000 :     if (!node.connman->AddNewAddresses({address}, address)) {
     824         512 :         obj.pushKV("success", false);
     825         512 :         return obj;
     826             :     }
     827             : 
     828        9488 :     obj.pushKV("success", true);
     829        9488 :     return obj;
     830       10000 : }
     831             : 
     832         626 : void RegisterNetRPCCommands(CRPCTable &t)
     833             : {
     834             : // clang-format off
     835        1179 : static const CRPCCommand commands[] =
     836         553 : { //  category              name                      actor (function)         argNames
     837             :   //  --------------------- ------------------------  -----------------------  ----------
     838         553 :     { "network",            "getconnectioncount",     &getconnectioncount,     {} },
     839         553 :     { "network",            "ping",                   &ping,                   {} },
     840         553 :     { "network",            "getpeerinfo",            &getpeerinfo,            {} },
     841         553 :     { "network",            "addnode",                &addnode,                {"node","command"} },
     842         553 :     { "network",            "disconnectnode",         &disconnectnode,         {"address", "nodeid"} },
     843         553 :     { "network",            "getaddednodeinfo",       &getaddednodeinfo,       {"node"} },
     844         553 :     { "network",            "getnettotals",           &getnettotals,           {} },
     845         553 :     { "network",            "getnetworkinfo",         &getnetworkinfo,         {} },
     846         553 :     { "network",            "setban",                 &setban,                 {"subnet", "command", "bantime", "absolute"} },
     847         553 :     { "network",            "listbanned",             &listbanned,             {} },
     848         553 :     { "network",            "clearbanned",            &clearbanned,            {} },
     849         553 :     { "network",            "setnetworkactive",       &setnetworkactive,       {"state"} },
     850         553 :     { "network",            "getnodeaddresses",       &getnodeaddresses,       {"count"} },
     851         553 :     { "hidden",             "addpeeraddress",         &addpeeraddress,         {"address", "port"} },
     852             : };
     853             : // clang-format on
     854        9390 :     for (const auto& c : commands) {
     855        8764 :         t.appendCommand(c.name, &c);
     856             :     }
     857        8368 : }

Generated by: LCOV version 1.15