diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index 79adb46523..cc9504cf9a 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -1,913 +1,929 @@ // Copyright (c) 2009-2016 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static UniValue getconnectioncount(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 0) { - throw std::runtime_error( - RPCHelpMan{"getconnectioncount", - "\nReturns the number of connections to other nodes.\n", - {}} - .ToString() + - "\nResult:\n" - "n (numeric) The connection count\n" - "\nExamples:\n" + - HelpExampleCli("getconnectioncount", "") + - HelpExampleRpc("getconnectioncount", "")); + throw std::runtime_error(RPCHelpMan{ + "getconnectioncount", + "\nReturns the number of connections to other nodes.\n", + {}, + RPCResult{"n (numeric) The connection count\n"}, + RPCExamples{HelpExampleCli("getconnectioncount", "") + + HelpExampleRpc("getconnectioncount", "")}, + } + .ToStringWithResultsAndExamples()); } if (!g_connman) { throw JSONRPCError( RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); } return int(g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL)); } static UniValue ping(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 0) { - throw std::runtime_error( - RPCHelpMan{ - "ping", - "\nRequests that a ping be sent to all other nodes, to measure " - "ping time.\n" - "Results provided in getpeerinfo, pingtime and pingwait fields " - "are decimal seconds.\n" - "Ping command is handled in queue with all other commands, so " - "it measures processing backlog, not just network ping.\n", - {}} - .ToString() + - "\nExamples:\n" + HelpExampleCli("ping", "") + - HelpExampleRpc("ping", "")); + throw std::runtime_error(RPCHelpMan{ + "ping", + "\nRequests that a ping be sent to all other nodes, to measure " + "ping time.\n" + "Results provided in getpeerinfo, pingtime and pingwait fields " + "are decimal seconds.\n" + "Ping command is handled in queue with all other commands, so " + "it measures processing backlog, not just network ping.\n", + {}, + RPCResults{}, + RPCExamples{HelpExampleCli("ping", "") + + HelpExampleRpc("ping", "")}, + } + .ToStringWithResultsAndExamples()); } if (!g_connman) { throw JSONRPCError( RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); } // Request that each node send a ping during next message processing pass g_connman->ForEachNode([](CNode *pnode) { pnode->fPingQueued = true; }); return NullUniValue; } static UniValue getpeerinfo(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 0) { - throw std::runtime_error( - RPCHelpMan{"getpeerinfo", - "\nReturns data about each connected network node as a " - "json array of objects.\n", - {}} - .ToString() + - "\nResult:\n" - "[\n" - " {\n" - " \"id\": n, (numeric) Peer index\n" - " \"addr\":\"host:port\", (string) The IP address and port " - "of the peer\n" - " \"addrbind\":\"ip:port\", (string) Bind address of the " - "connection to the peer\n" - " \"addrlocal\":\"ip:port\", (string) Local address as " - "reported by the peer\n" - " \"services\":\"xxxxxxxxxxxxxxxx\", (string) The services " - "offered\n" - " \"relaytxes\":true|false, (boolean) Whether peer has asked " - "us to relay transactions to it\n" - " \"lastsend\": ttt, (numeric) The time in seconds " - "since epoch (Jan 1 1970 GMT) of the last send\n" - " \"lastrecv\": ttt, (numeric) The time in seconds " - "since epoch (Jan 1 1970 GMT) of the last receive\n" - " \"bytessent\": n, (numeric) The total bytes sent\n" - " \"bytesrecv\": n, (numeric) The total bytes " - "received\n" - " \"conntime\": ttt, (numeric) The connection time in " - "seconds since epoch (Jan 1 1970 GMT)\n" - " \"timeoffset\": ttt, (numeric) The time offset in " - "seconds\n" - " \"pingtime\": n, (numeric) ping time (if " - "available)\n" - " \"minping\": n, (numeric) minimum observed ping " - "time (if any at all)\n" - " \"pingwait\": n, (numeric) ping wait (if " - "non-zero)\n" - " \"version\": v, (numeric) The peer version, such " - "as 70001\n" - " \"subver\": \"/Satoshi:0.8.5/\", (string) The string " - "version\n" - " \"inbound\": true|false, (boolean) Inbound (true) or " - "Outbound (false)\n" - " \"addnode\": true|false, (boolean) Whether connection was " - "due to addnode/-connect or if it was an automatic/inbound " - "connection\n" - " \"startingheight\": n, (numeric) The starting height " - "(block) of the peer\n" - " \"banscore\": n, (numeric) The ban score\n" - " \"synced_headers\": n, (numeric) The last header we " - "have in common with this peer\n" - " \"synced_blocks\": n, (numeric) The last block we have " - "in common with this peer\n" - " \"inflight\": [\n" - " n, (numeric) The heights of blocks " - "we're currently asking from this peer\n" - " ...\n" - " ],\n" - " \"whitelisted\": true|false, (boolean) Whether the peer is " - "whitelisted\n" - " \"minfeefilter\": n, (numeric) The minimum fee rate " - "for transactions this peer accepts\n" - " \"bytessent_per_msg\": {\n" - " \"addr\": n, (numeric) The total bytes sent " - "aggregated by message type\n" - " ...\n" - " },\n" - " \"bytesrecv_per_msg\": {\n" - " \"addr\": n, (numeric) The total bytes " - "received aggregated by message type\n" - " ...\n" - " }\n" - " }\n" - " ,...\n" - "]\n" - "\nExamples:\n" + - HelpExampleCli("getpeerinfo", "") + - HelpExampleRpc("getpeerinfo", "")); + throw std::runtime_error(RPCHelpMan{ + "getpeerinfo", + "\nReturns data about each connected network node as a json array " + "of objects.\n", + {}, + RPCResult{ + "[\n" + " {\n" + " \"id\": n, (numeric) Peer index\n" + " \"addr\":\"host:port\", (string) The IP address and " + "port of the peer\n" + " \"addrbind\":\"ip:port\", (string) Bind address of the " + "connection to the peer\n" + " \"addrlocal\":\"ip:port\", (string) Local address as " + "reported by the peer\n" + " \"services\":\"xxxxxxxxxxxxxxxx\", (string) The " + "services offered\n" + " \"relaytxes\":true|false, (boolean) Whether peer has " + "asked us to relay transactions to it\n" + " \"lastsend\": ttt, (numeric) The time in " + "seconds since epoch (Jan 1 1970 GMT) of the last send\n" + " \"lastrecv\": ttt, (numeric) The time in " + "seconds since epoch (Jan 1 1970 GMT) of the last receive\n" + " \"bytessent\": n, (numeric) The total bytes " + "sent\n" + " \"bytesrecv\": n, (numeric) The total bytes " + "received\n" + " \"conntime\": ttt, (numeric) The connection " + "time in seconds since epoch (Jan 1 1970 GMT)\n" + " \"timeoffset\": ttt, (numeric) The time offset in " + "seconds\n" + " \"pingtime\": n, (numeric) ping time (if " + "available)\n" + " \"minping\": n, (numeric) minimum observed " + "ping time (if any at all)\n" + " \"pingwait\": n, (numeric) ping wait (if " + "non-zero)\n" + " \"version\": v, (numeric) The peer version, " + "such as 70001\n" + " \"subver\": \"/Satoshi:0.8.5/\", (string) The string " + "version\n" + " \"inbound\": true|false, (boolean) Inbound (true) or " + "Outbound (false)\n" + " \"addnode\": true|false, (boolean) Whether connection " + "was due to addnode/-connect or if it was an automatic/inbound " + "connection\n" + " \"startingheight\": n, (numeric) The starting " + "height (block) of the peer\n" + " \"banscore\": n, (numeric) The ban score\n" + " \"synced_headers\": n, (numeric) The last header we " + "have in common with this peer\n" + " \"synced_blocks\": n, (numeric) The last block we " + "have in common with this peer\n" + " \"inflight\": [\n" + " n, (numeric) The heights of " + "blocks we're currently asking from this peer\n" + " ...\n" + " ],\n" + " \"whitelisted\": true|false, (boolean) Whether the peer " + "is whitelisted\n" + " \"minfeefilter\": n, (numeric) The minimum fee " + "rate for transactions this peer accepts\n" + " \"bytessent_per_msg\": {\n" + " \"addr\": n, (numeric) The total bytes " + "sent aggregated by message type\n" + " ...\n" + " },\n" + " \"bytesrecv_per_msg\": {\n" + " \"addr\": n, (numeric) The total bytes " + "received aggregated by message type\n" + " ...\n" + " }\n" + " }\n" + " ,...\n" + "]\n"}, + RPCExamples{HelpExampleCli("getpeerinfo", "") + + HelpExampleRpc("getpeerinfo", "")}, + } + .ToStringWithResultsAndExamples()); } if (!g_connman) { throw JSONRPCError( RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); } std::vector vstats; g_connman->GetNodeStats(vstats); UniValue ret(UniValue::VARR); for (const CNodeStats &stats : vstats) { UniValue obj(UniValue::VOBJ); CNodeStateStats statestats; bool fStateStats = GetNodeStateStats(stats.nodeid, statestats); obj.pushKV("id", stats.nodeid); obj.pushKV("addr", stats.addrName); if (!(stats.addrLocal.empty())) { obj.pushKV("addrlocal", stats.addrLocal); } if (stats.addrBind.IsValid()) { obj.pushKV("addrbind", stats.addrBind.ToString()); } obj.pushKV("services", strprintf("%016x", stats.nServices)); obj.pushKV("relaytxes", stats.fRelayTxes); obj.pushKV("lastsend", stats.nLastSend); obj.pushKV("lastrecv", stats.nLastRecv); obj.pushKV("bytessent", stats.nSendBytes); obj.pushKV("bytesrecv", stats.nRecvBytes); obj.pushKV("conntime", stats.nTimeConnected); obj.pushKV("timeoffset", stats.nTimeOffset); if (stats.dPingTime > 0.0) { obj.pushKV("pingtime", stats.dPingTime); } if (stats.dMinPing < static_cast(std::numeric_limits::max()) / 1e6) { obj.pushKV("minping", stats.dMinPing); } if (stats.dPingWait > 0.0) { obj.pushKV("pingwait", stats.dPingWait); } obj.pushKV("version", stats.nVersion); // Use the sanitized form of subver here, to avoid tricksy remote peers // from corrupting or modifying the JSON output by putting special // characters in their ver message. obj.pushKV("subver", stats.cleanSubVer); obj.pushKV("inbound", stats.fInbound); obj.pushKV("addnode", stats.m_manual_connection); obj.pushKV("startingheight", stats.nStartingHeight); if (fStateStats) { obj.pushKV("banscore", statestats.nMisbehavior); obj.pushKV("synced_headers", statestats.nSyncHeight); obj.pushKV("synced_blocks", statestats.nCommonHeight); UniValue heights(UniValue::VARR); for (const int height : statestats.vHeightInFlight) { heights.push_back(height); } obj.pushKV("inflight", heights); } obj.pushKV("whitelisted", stats.fWhitelisted); obj.pushKV("minfeefilter", ValueFromAmount(stats.minFeeFilter)); UniValue sendPerMsgCmd(UniValue::VOBJ); for (const mapMsgCmdSize::value_type &i : stats.mapSendBytesPerMsgCmd) { if (i.second > 0) { sendPerMsgCmd.pushKV(i.first, i.second); } } obj.pushKV("bytessent_per_msg", sendPerMsgCmd); UniValue recvPerMsgCmd(UniValue::VOBJ); for (const mapMsgCmdSize::value_type &i : stats.mapRecvBytesPerMsgCmd) { if (i.second > 0) { recvPerMsgCmd.pushKV(i.first, i.second); } } obj.pushKV("bytesrecv_per_msg", recvPerMsgCmd); ret.push_back(obj); } return ret; } static UniValue addnode(const Config &config, const JSONRPCRequest &request) { std::string strCommand; if (!request.params[1].isNull()) { strCommand = request.params[1].get_str(); } if (request.fHelp || request.params.size() != 2 || (strCommand != "onetry" && strCommand != "add" && strCommand != "remove")) { - throw std::runtime_error( - RPCHelpMan{ - "addnode", - "\nAttempts to add or remove a node from the addnode list.\n" - "Or try a connection to a node once.\n" - "Nodes added using addnode (or -connect) are protected from " - "DoS disconnection and are not required to be\n" - "full nodes as other outbound peers are (though such peers " - "will not be synced from).\n", - { - {"node", RPCArg::Type::STR, /* opt */ false, - /* default_val */ "", - "The node (see getpeerinfo for nodes)"}, - {"command", RPCArg::Type::STR, /* opt */ false, - /* default_val */ "", - "'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"}, - }} - .ToString() + - "\nExamples:\n" + - HelpExampleCli("addnode", "\"192.168.0.6:8333\" \"onetry\"") + - HelpExampleRpc("addnode", "\"192.168.0.6:8333\", \"onetry\"")); + throw std::runtime_error(RPCHelpMan{ + "addnode", + "\nAttempts to add or remove a node from the addnode list.\n" + "Or try a connection to a node once.\n" + "Nodes added using addnode (or -connect) are protected from " + "DoS disconnection and are not required to be\n" + "full nodes as other outbound peers are (though such peers " + "will not be synced from).\n", + { + {"node", RPCArg::Type::STR, /* opt */ false, + /* default_val */ "", "The node (see getpeerinfo for nodes)"}, + {"command", RPCArg::Type::STR, /* opt */ false, + /* default_val */ "", + "'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"}, + }, + RPCResults{}, + RPCExamples{ + HelpExampleCli("addnode", "\"192.168.0.6:8333\" \"onetry\"") + + HelpExampleRpc("addnode", "\"192.168.0.6:8333\", \"onetry\"")}, + } + .ToStringWithResultsAndExamples()); } if (!g_connman) { throw JSONRPCError( RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); } std::string strNode = request.params[0].get_str(); if (strCommand == "onetry") { CAddress addr; g_connman->OpenNetworkConnection(addr, false, nullptr, strNode.c_str(), false, false, true); return NullUniValue; } if ((strCommand == "add") && (!g_connman->AddNode(strNode))) { throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: Node already added"); } else if ((strCommand == "remove") && (!g_connman->RemoveAddedNode(strNode))) { throw JSONRPCError(RPC_CLIENT_NODE_NOT_ADDED, "Error: Node has not been added."); } return NullUniValue; } static UniValue disconnectnode(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() == 0 || request.params.size() >= 3) { - throw std::runtime_error( - RPCHelpMan{ - "disconnectnode", - "\nImmediately disconnects from the specified peer node.\n" - "\nStrictly one out of 'address' and 'nodeid' can be provided " - "to identify the node.\n" - "\nTo disconnect by nodeid, either set 'address' to the empty " - "string, or call using the named 'nodeid' argument only.\n", - { - {"address", RPCArg::Type::STR, /* opt */ true, - /* default_val */ "", "The IP address/port of the node"}, - {"nodeid", RPCArg::Type::NUM, /* opt */ true, - /* default_val */ "", - "The node ID (see getpeerinfo for node IDs)"}, - }} - .ToString() + - "\nExamples:\n" + - HelpExampleCli("disconnectnode", "\"192.168.0.6:8333\"") + - HelpExampleCli("disconnectnode", "\"\" 1") + - HelpExampleRpc("disconnectnode", "\"192.168.0.6:8333\"") + - HelpExampleRpc("disconnectnode", "\"\", 1")); + throw std::runtime_error(RPCHelpMan{ + "disconnectnode", + "\nImmediately disconnects from the specified peer node.\n" + "\nStrictly one out of 'address' and 'nodeid' can be provided " + "to identify the node.\n" + "\nTo disconnect by nodeid, either set 'address' to the empty " + "string, or call using the named 'nodeid' argument only.\n", + { + {"address", RPCArg::Type::STR, /* opt */ true, + /* default_val */ "", "The IP address/port of the node"}, + {"nodeid", RPCArg::Type::NUM, /* opt */ true, + /* default_val */ "", + "The node ID (see getpeerinfo for node IDs)"}, + }, + RPCResults{}, + RPCExamples{ + HelpExampleCli("disconnectnode", "\"192.168.0.6:8333\"") + + HelpExampleCli("disconnectnode", "\"\" 1") + + HelpExampleRpc("disconnectnode", "\"192.168.0.6:8333\"") + + HelpExampleRpc("disconnectnode", "\"\", 1")}, + } + .ToStringWithResultsAndExamples()); } if (!g_connman) { throw JSONRPCError( RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); } bool success; const UniValue &address_arg = request.params[0]; const UniValue &id_arg = request.params[1]; if (!address_arg.isNull() && id_arg.isNull()) { /* handle disconnect-by-address */ success = g_connman->DisconnectNode(address_arg.get_str()); } else if (!id_arg.isNull() && (address_arg.isNull() || (address_arg.isStr() && address_arg.get_str().empty()))) { /* handle disconnect-by-id */ NodeId nodeid = (NodeId)id_arg.get_int64(); success = g_connman->DisconnectNode(nodeid); } else { throw JSONRPCError( RPC_INVALID_PARAMS, "Only one of address and nodeid should be provided."); } if (!success) { throw JSONRPCError(RPC_CLIENT_NODE_NOT_CONNECTED, "Node not found in connected nodes"); } return NullUniValue; } static UniValue getaddednodeinfo(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() > 1) { - throw std::runtime_error( - RPCHelpMan{"getaddednodeinfo", - "\nReturns information about the given added node, or " - "all added nodes\n" - "(note that onetry addnodes are not listed here)\n", - { - {"node", RPCArg::Type::STR, /* opt */ true, - /* default_val */ "", - "If provided, return information about this " - "specific node, otherwise all nodes are returned."}, - }} - .ToString() + - "\nResult:\n" - "[\n" - " {\n" - " \"addednode\" : \"192.168.0.201\", (string) The node IP " - "address or name (as provided to addnode)\n" - " \"connected\" : true|false, (boolean) If connected\n" - " \"addresses\" : [ (list of objects) Only " - "when connected = true\n" - " {\n" - " \"address\" : \"192.168.0.201:8333\", (string) The " - "bitcoin server IP and port we're connected to\n" - " \"connected\" : \"outbound\" (string) " - "connection, inbound or outbound\n" - " }\n" - " ]\n" - " }\n" - " ,...\n" - "]\n" - "\nExamples:\n" + - HelpExampleCli("getaddednodeinfo", "\"192.168.0.201\"") + - HelpExampleRpc("getaddednodeinfo", "\"192.168.0.201\"")); + throw std::runtime_error(RPCHelpMan{ + "getaddednodeinfo", + "\nReturns information about the given added node, or " + "all added nodes\n" + "(note that onetry addnodes are not listed here)\n", + { + {"node", RPCArg::Type::STR, /* opt */ true, + /* default_val */ "", + "If provided, return information about this " + "specific node, otherwise all nodes are returned."}, + }, + RPCResult{ + "[\n" + " {\n" + " \"addednode\" : \"192.168.0.201\", (string) The node IP " + "address or name (as provided to addnode)\n" + " \"connected\" : true|false, (boolean) If " + "connected\n" + " \"addresses\" : [ (list of objects) " + "Only when connected = true\n" + " {\n" + " \"address\" : \"192.168.0.201:8333\", (string) The " + "bitcoin server IP and port we're connected to\n" + " \"connected\" : \"outbound\" (string) " + "connection, inbound or outbound\n" + " }\n" + " ]\n" + " }\n" + " ,...\n" + "]\n"}, + RPCExamples{ + HelpExampleCli("getaddednodeinfo", "\"192.168.0.201\"") + + HelpExampleRpc("getaddednodeinfo", "\"192.168.0.201\"")}, + } + .ToStringWithResultsAndExamples()); } if (!g_connman) { throw JSONRPCError( RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); } std::vector vInfo = g_connman->GetAddedNodeInfo(); if (!request.params[0].isNull()) { bool found = false; for (const AddedNodeInfo &info : vInfo) { if (info.strAddedNode == request.params[0].get_str()) { vInfo.assign(1, info); found = true; break; } } if (!found) { throw JSONRPCError(RPC_CLIENT_NODE_NOT_ADDED, "Error: Node has not been added."); } } UniValue ret(UniValue::VARR); for (const AddedNodeInfo &info : vInfo) { UniValue obj(UniValue::VOBJ); obj.pushKV("addednode", info.strAddedNode); obj.pushKV("connected", info.fConnected); UniValue addresses(UniValue::VARR); if (info.fConnected) { UniValue address(UniValue::VOBJ); address.pushKV("address", info.resolvedAddress.ToString()); address.pushKV("connected", info.fInbound ? "inbound" : "outbound"); addresses.push_back(address); } obj.pushKV("addresses", addresses); ret.push_back(obj); } return ret; } static UniValue getnettotals(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() > 0) { - throw std::runtime_error( - RPCHelpMan{"getnettotals", - "\nReturns information about network traffic, including " - "bytes in, bytes out,\n" - "and current time.\n", - {}} - .ToString() + - "\nResult:\n" - "{\n" - " \"totalbytesrecv\": n, (numeric) Total bytes received\n" - " \"totalbytessent\": n, (numeric) Total bytes sent\n" - " \"timemillis\": t, (numeric) Current UNIX time in " - "milliseconds\n" - " \"uploadtarget\":\n" - " {\n" - " \"timeframe\": n, (numeric) Length of " - "the measuring timeframe in seconds\n" - " \"target\": n, (numeric) Target in " - "bytes\n" - " \"target_reached\": true|false, (boolean) True if " - "target is reached\n" - " \"serve_historical_blocks\": true|false, (boolean) True if " - "serving historical blocks\n" - " \"bytes_left_in_cycle\": t, (numeric) Bytes " - "left in current time cycle\n" - " \"time_left_in_cycle\": t (numeric) Seconds " - "left in current time cycle\n" - " }\n" - "}\n" - "\nExamples:\n" + - HelpExampleCli("getnettotals", "") + - HelpExampleRpc("getnettotals", "")); + throw std::runtime_error(RPCHelpMan{ + "getnettotals", + "\nReturns information about network traffic, including " + "bytes in, bytes out,\n" + "and current time.\n", + {}, + RPCResult{ + "{\n" + " \"totalbytesrecv\": n, (numeric) Total bytes received\n" + " \"totalbytessent\": n, (numeric) Total bytes sent\n" + " \"timemillis\": t, (numeric) Current UNIX time in " + "milliseconds\n" + " \"uploadtarget\":\n" + " {\n" + " \"timeframe\": n, (numeric) " + "Length of the measuring timeframe in seconds\n" + " \"target\": n, (numeric) " + "Target in bytes\n" + " \"target_reached\": true|false, (boolean) True " + "if target is reached\n" + " \"serve_historical_blocks\": true|false, (boolean) True " + "if serving historical blocks\n" + " \"bytes_left_in_cycle\": t, (numeric) Bytes " + "left in current time cycle\n" + " \"time_left_in_cycle\": t (numeric) " + "Seconds left in current time cycle\n" + " }\n" + "}\n"}, + RPCExamples{HelpExampleCli("getnettotals", "") + + HelpExampleRpc("getnettotals", "")}, + } + .ToStringWithResultsAndExamples()); } if (!g_connman) { throw JSONRPCError( RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); } UniValue obj(UniValue::VOBJ); obj.pushKV("totalbytesrecv", g_connman->GetTotalBytesRecv()); obj.pushKV("totalbytessent", g_connman->GetTotalBytesSent()); obj.pushKV("timemillis", GetTimeMillis()); UniValue outboundLimit(UniValue::VOBJ); outboundLimit.pushKV("timeframe", g_connman->GetMaxOutboundTimeframe()); outboundLimit.pushKV("target", g_connman->GetMaxOutboundTarget()); outboundLimit.pushKV("target_reached", g_connman->OutboundTargetReached(false)); outboundLimit.pushKV("serve_historical_blocks", !g_connman->OutboundTargetReached(true)); outboundLimit.pushKV("bytes_left_in_cycle", g_connman->GetOutboundTargetBytesLeft()); outboundLimit.pushKV("time_left_in_cycle", g_connman->GetMaxOutboundTimeLeftInCycle()); obj.pushKV("uploadtarget", outboundLimit); return obj; } static UniValue GetNetworksInfo() { UniValue networks(UniValue::VARR); for (int n = 0; n < NET_MAX; ++n) { enum Network network = static_cast(n); if (network == NET_UNROUTABLE || network == NET_INTERNAL) { continue; } proxyType proxy; UniValue obj(UniValue::VOBJ); GetProxy(network, proxy); obj.pushKV("name", GetNetworkName(network)); obj.pushKV("limited", !IsReachable(network)); obj.pushKV("reachable", IsReachable(network)); obj.pushKV("proxy", proxy.IsValid() ? proxy.proxy.ToStringIPPort() : std::string()); obj.pushKV("proxy_randomize_credentials", proxy.randomize_credentials); networks.push_back(obj); } return networks; } static UniValue getnetworkinfo(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 0) { - throw std::runtime_error( - RPCHelpMan{"getnetworkinfo", - "Returns an object containing various state info " - "regarding P2P networking.\n", - {}} - .ToString() + - "\nResult:\n" - "{\n" - " \"version\": xxxxx, (numeric) the server " - "version\n" - " \"subversion\": \"/Satoshi:x.x.x/\", (string) the server " - "subversion string\n" - " \"protocolversion\": xxxxx, (numeric) the protocol " - "version\n" - " \"localservices\": \"xxxxxxxxxxxxxxxx\", (string) the services " - "we offer to the network\n" - " \"localrelay\": true|false, (bool) true if " - "transaction relay is requested from peers\n" - " \"timeoffset\": xxxxx, (numeric) the time " - "offset\n" - " \"connections\": xxxxx, (numeric) the number " - "of connections\n" - " \"networkactive\": true|false, (bool) whether p2p " - "networking is enabled\n" - " \"networks\": [ (array) information " - "per network\n" - " {\n" - " \"name\": \"xxx\", (string) network " - "(ipv4, ipv6 or onion)\n" - " \"limited\": true|false, (boolean) is the " - "network limited using -onlynet?\n" - " \"reachable\": true|false, (boolean) is the " - "network reachable?\n" - " \"proxy\": \"host:port\" (string) the proxy " - "that is used for this network, or empty if none\n" - " \"proxy_randomize_credentials\": true|false, (string) " - "Whether randomized credentials are used\n" - " }\n" - " ,...\n" - " ],\n" - " \"relayfee\": x.xxxxxxxx, (numeric) minimum " - "relay fee for transactions in " + - CURRENCY_UNIT + - "/kB\n" - " \"excessutxocharge\": x.xxxxxxxx, (numeric) minimum " - "charge for excess utxos in " + - CURRENCY_UNIT + - "\n" - " \"localaddresses\": [ " - "(array) list of local addresses\n" - " {\n" - " \"address\": \"xxxx\", " - "(string) network address\n" - " \"port\": xxx, " - "(numeric) network port\n" - " \"score\": xxx " - "(numeric) relative score\n" - " }\n" - " ,...\n" - " ]\n" - " \"warnings\": \"...\" (string) any network " - "and blockchain warnings\n" - "}\n" - "\nExamples:\n" + - HelpExampleCli("getnetworkinfo", "") + - HelpExampleRpc("getnetworkinfo", "")); + throw std::runtime_error(RPCHelpMan{ + "getnetworkinfo", + "Returns an object containing various state info " + "regarding P2P networking.\n", + {}, + RPCResult{"{\n" + " \"version\": xxxxx, (numeric) " + "the server version\n" + " \"subversion\": \"/Satoshi:x.x.x/\", (string) the " + "server subversion string\n" + " \"protocolversion\": xxxxx, (numeric) " + "the protocol version\n" + " \"localservices\": \"xxxxxxxxxxxxxxxx\", (string) the " + "services we offer to the network\n" + " \"localrelay\": true|false, (bool) true " + "if transaction relay is requested from peers\n" + " \"timeoffset\": xxxxx, (numeric) " + "the time offset\n" + " \"connections\": xxxxx, (numeric) " + "the number of connections\n" + " \"networkactive\": true|false, (bool) " + "whether p2p networking is enabled\n" + " \"networks\": [ (array) " + "information per network\n" + " {\n" + " \"name\": \"xxx\", (string) " + "network (ipv4, ipv6 or onion)\n" + " \"limited\": true|false, (boolean) is " + "the network limited using -onlynet?\n" + " \"reachable\": true|false, (boolean) is " + "the network reachable?\n" + " \"proxy\": \"host:port\" (string) the " + "proxy that is used for this network, or empty if none\n" + " \"proxy_randomize_credentials\": true|false, " + "(string) Whether randomized credentials are used\n" + " }\n" + " ,...\n" + " ],\n" + " \"relayfee\": x.xxxxxxxx, (numeric) " + "minimum relay fee for transactions in " + + CURRENCY_UNIT + + "/kB\n" + " \"excessutxocharge\": x.xxxxxxxx, (numeric) " + "minimum charge for excess utxos in " + + CURRENCY_UNIT + + "\n" + " \"localaddresses\": [ (array) list " + "of local addresses\n" + " {\n" + " \"address\": \"xxxx\", (string) " + "network address\n" + " \"port\": xxx, (numeric) " + "network port\n" + " \"score\": xxx (numeric) " + "relative score\n" + " }\n" + " ,...\n" + " ]\n" + " \"warnings\": \"...\" (string) any " + "network and blockchain warnings\n" + "}\n"}, + RPCExamples{HelpExampleCli("getnetworkinfo", "") + + HelpExampleRpc("getnetworkinfo", "")}, + } + .ToStringWithResultsAndExamples()); } LOCK(cs_main); UniValue obj(UniValue::VOBJ); obj.pushKV("version", CLIENT_VERSION); obj.pushKV("subversion", userAgent(config)); obj.pushKV("protocolversion", PROTOCOL_VERSION); if (g_connman) { obj.pushKV("localservices", strprintf("%016x", g_connman->GetLocalServices())); } obj.pushKV("localrelay", fRelayTxes); obj.pushKV("timeoffset", GetTimeOffset()); if (g_connman) { obj.pushKV("networkactive", g_connman->GetNetworkActive()); obj.pushKV("connections", int(g_connman->GetNodeCount(CConnman::CONNECTIONS_ALL))); } obj.pushKV("networks", GetNetworksInfo()); obj.pushKV("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK())); obj.pushKV("excessutxocharge", ValueFromAmount(config.GetExcessUTXOCharge())); UniValue localAddresses(UniValue::VARR); { LOCK(cs_mapLocalHost); for (const std::pair &item : mapLocalHost) { UniValue rec(UniValue::VOBJ); rec.pushKV("address", item.first.ToString()); rec.pushKV("port", item.second.nPort); rec.pushKV("score", item.second.nScore); localAddresses.push_back(rec); } } obj.pushKV("localaddresses", localAddresses); obj.pushKV("warnings", GetWarnings("statusbar")); return obj; } static UniValue setban(const Config &config, const JSONRPCRequest &request) { std::string strCommand; if (!request.params[1].isNull()) { strCommand = request.params[1].get_str(); } if (request.fHelp || request.params.size() < 2 || (strCommand != "add" && strCommand != "remove")) { - throw std::runtime_error( - RPCHelpMan{ - "setban", - "\nAttempts to add or remove an IP/Subnet from the " - "banned list.\n", - { - {"subnet", RPCArg::Type::STR, /* opt */ false, - /* default_val */ "", - "The IP/Subnet (see getpeerinfo for nodes IP) with an " - "optional netmask (default is /32 = single IP)"}, - {"command", RPCArg::Type::STR, /* opt */ false, - /* default_val */ "", - "'add' to add an IP/Subnet to the list, 'remove' to " - "remove an IP/Subnet from the list"}, - {"bantime", RPCArg::Type::NUM, /* opt */ true, - /* default_val */ "", - "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)"}, - {"absolute", RPCArg::Type::BOOL, /* opt */ true, - /* default_val */ "", - "If set, the bantime must be an absolute timestamp in " - "seconds since epoch (Jan 1 1970 GMT)"}, - }} - .ToString() + - "\nExamples:\n" + - HelpExampleCli("setban", "\"192.168.0.6\" \"add\" 86400") + - HelpExampleCli("setban", "\"192.168.0.0/24\" \"add\"") + - HelpExampleRpc("setban", "\"192.168.0.6\", \"add\", 86400")); + throw std::runtime_error(RPCHelpMan{ + "setban", + "\nAttempts to add or remove an IP/Subnet from the " + "banned list.\n", + { + {"subnet", RPCArg::Type::STR, /* opt */ false, + /* default_val */ "", + "The IP/Subnet (see getpeerinfo for nodes IP) with an " + "optional netmask (default is /32 = single IP)"}, + {"command", RPCArg::Type::STR, /* opt */ false, + /* default_val */ "", + "'add' to add an IP/Subnet to the list, 'remove' to " + "remove an IP/Subnet from the list"}, + {"bantime", RPCArg::Type::NUM, /* opt */ true, + /* default_val */ "", + "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)"}, + {"absolute", RPCArg::Type::BOOL, /* opt */ true, + /* default_val */ "", + "If set, the bantime must be an absolute timestamp in " + "seconds since epoch (Jan 1 1970 GMT)"}, + }, + RPCResults{}, + RPCExamples{ + HelpExampleCli("setban", "\"192.168.0.6\" \"add\" 86400") + + HelpExampleCli("setban", "\"192.168.0.0/24\" \"add\"") + + HelpExampleRpc("setban", "\"192.168.0.6\", \"add\", 86400")}, + } + .ToStringWithResultsAndExamples()); } - if (!g_banman) { throw JSONRPCError(RPC_DATABASE_ERROR, "Error: Ban database not loaded"); } CSubNet subNet; CNetAddr netAddr; bool isSubnet = false; if (request.params[0].get_str().find('/') != std::string::npos) { isSubnet = true; } if (!isSubnet) { CNetAddr resolved; LookupHost(request.params[0].get_str().c_str(), resolved, false); netAddr = resolved; } else { LookupSubNet(request.params[0].get_str().c_str(), subNet); } if (!(isSubnet ? subNet.IsValid() : netAddr.IsValid())) { throw JSONRPCError(RPC_CLIENT_INVALID_IP_OR_SUBNET, "Error: Invalid IP/Subnet"); } if (strCommand == "add") { if (isSubnet ? g_banman->IsBanned(subNet) : g_banman->IsBanned(netAddr)) { throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED, "Error: IP/Subnet already banned"); } // Use standard bantime if not specified. int64_t banTime = 0; if (!request.params[2].isNull()) { banTime = request.params[2].get_int64(); } bool absolute = false; if (request.params[3].isTrue()) { absolute = true; } if (isSubnet) { g_banman->Ban(subNet, BanReasonManuallyAdded, banTime, absolute); if (g_connman) { g_connman->DisconnectNode(subNet); } } else { g_banman->Ban(netAddr, BanReasonManuallyAdded, banTime, absolute); if (g_connman) { g_connman->DisconnectNode(netAddr); } } } else if (strCommand == "remove") { if (!(isSubnet ? g_banman->Unban(subNet) : g_banman->Unban(netAddr))) { throw JSONRPCError(RPC_CLIENT_INVALID_IP_OR_SUBNET, "Error: Unban failed. Requested address/subnet " "was not previously banned."); } } return NullUniValue; } static UniValue listbanned(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 0) { - throw std::runtime_error( - RPCHelpMan{"listbanned", "\nList all banned IPs/Subnets.\n", {}} - .ToString() + - "\nExamples:\n" + HelpExampleCli("listbanned", "") + - HelpExampleRpc("listbanned", "")); + throw std::runtime_error(RPCHelpMan{ + "listbanned", + "\nList all banned IPs/Subnets.\n", + {}, + RPCResults{}, + RPCExamples{HelpExampleCli("listbanned", "") + + HelpExampleRpc("listbanned", "")}, + } + .ToStringWithResultsAndExamples()); } if (!g_banman) { throw JSONRPCError(RPC_DATABASE_ERROR, "Error: Ban database not loaded"); } banmap_t banMap; g_banman->GetBanned(banMap); UniValue bannedAddresses(UniValue::VARR); for (const auto &entry : banMap) { const CBanEntry &banEntry = entry.second; UniValue rec(UniValue::VOBJ); rec.pushKV("address", entry.first.ToString()); rec.pushKV("banned_until", banEntry.nBanUntil); rec.pushKV("ban_created", banEntry.nCreateTime); rec.pushKV("ban_reason", banEntry.banReasonToString()); bannedAddresses.push_back(rec); } return bannedAddresses; } static UniValue clearbanned(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 0) { - throw std::runtime_error( - RPCHelpMan{"clearbanned", "\nClear all banned IPs.\n", {}} - .ToString() + - "\nExamples:\n" + HelpExampleCli("clearbanned", "") + - HelpExampleRpc("clearbanned", "")); + throw std::runtime_error(RPCHelpMan{ + "clearbanned", + "\nClear all banned IPs.\n", + {}, + RPCResults{}, + RPCExamples{HelpExampleCli("clearbanned", "") + + HelpExampleRpc("clearbanned", "")}, + } + .ToStringWithResultsAndExamples()); } if (!g_banman) { throw JSONRPCError( RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); } g_banman->ClearBanned(); return NullUniValue; } static UniValue setnetworkactive(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() != 1) { throw std::runtime_error(RPCHelpMan{ "setnetworkactive", "\nDisable/enable all p2p network activity.\n", { {"state", RPCArg::Type::BOOL, /* opt */ false, /* default_val */ "", "true to enable networking, false to disable"}, - }}.ToString()); + }, + RPCResults{}, + RPCExamples{""}, + } + .ToStringWithResultsAndExamples()); } if (!g_connman) { throw JSONRPCError( RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); } g_connman->SetNetworkActive(request.params[0].get_bool()); return g_connman->GetNetworkActive(); } static UniValue getnodeaddresses(const Config &config, const JSONRPCRequest &request) { if (request.fHelp || request.params.size() > 1) { - throw std::runtime_error( - RPCHelpMan{"getnodeaddresses", - "\nReturn known addresses which can potentially be used " - "to find new nodes in the network\n", - { - {"count", RPCArg::Type::NUM, /* opt */ true, - /* default_val */ "1", - "How many addresses to return. Limited to the " - "smaller of " + - std::to_string(ADDRMAN_GETADDR_MAX) + " or " + - std::to_string(ADDRMAN_GETADDR_MAX_PCT) + - "% of all known addresses."}, - }} - .ToString() + - "\nResult:\n" - "[\n" - " {\n" - " \"time\": ttt, (numeric) Timestamp in seconds " - "since epoch (Jan 1 1970 GMT) keeping track of when the node was " - "last seen\n" - " \"services\": n, (numeric) The services offered\n" - " \"address\": \"host\", (string) The address of the " - "node\n" - " \"port\": n (numeric) The port of the node\n" - " }\n" - " ,....\n" - "]\n" - "\nExamples:\n" + - HelpExampleCli("getnodeaddresses", "8") + - HelpExampleRpc("getnodeaddresses", "8")); + throw std::runtime_error(RPCHelpMan{ + "getnodeaddresses", + "\nReturn known addresses which can potentially be used " + "to find new nodes in the network\n", + { + {"count", RPCArg::Type::NUM, /* opt */ true, + /* default_val */ "1", + "How many addresses to return. Limited to the " + "smaller of " + + std::to_string(ADDRMAN_GETADDR_MAX) + " or " + + std::to_string(ADDRMAN_GETADDR_MAX_PCT) + + "% of all known addresses."}, + }, + RPCResult{"[\n" + " {\n" + " \"time\": ttt, (numeric) Timestamp " + "in seconds since epoch (Jan 1 1970 GMT) keeping track " + "of when the node was last seen\n" + " \"services\": n, (numeric) The " + "services offered\n" + " \"address\": \"host\", (string) The " + "address of the node\n" + " \"port\": n (numeric) The port of " + "the node\n" + " }\n" + " ,....\n" + "]\n"}, + RPCExamples{HelpExampleCli("getnodeaddresses", "8") + + HelpExampleRpc("getnodeaddresses", "8")}, + } + .ToStringWithResultsAndExamples()); } if (!g_connman) { throw JSONRPCError( RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled"); } int count = 1; if (!request.params[0].isNull()) { count = request.params[0].get_int(); if (count <= 0) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Address count out of range"); } } // returns a shuffled list of CAddress std::vector vAddr = g_connman->GetAddresses(); UniValue ret(UniValue::VARR); int address_return_count = std::min(count, vAddr.size()); for (int i = 0; i < address_return_count; ++i) { UniValue obj(UniValue::VOBJ); const CAddress &addr = vAddr[i]; obj.pushKV("time", int(addr.nTime)); obj.pushKV("services", uint64_t(addr.nServices)); obj.pushKV("address", addr.ToStringIP()); obj.pushKV("port", addr.GetPort()); ret.push_back(obj); } return ret; } // clang-format off static const ContextFreeRPCCommand commands[] = { // category name actor (function) argNames // ------------------- ------------------------ ---------------------- ---------- { "network", "getconnectioncount", getconnectioncount, {} }, { "network", "ping", ping, {} }, { "network", "getpeerinfo", getpeerinfo, {} }, { "network", "addnode", addnode, {"node","command"} }, { "network", "disconnectnode", disconnectnode, {"address", "nodeid"} }, { "network", "getaddednodeinfo", getaddednodeinfo, {"node"} }, { "network", "getnettotals", getnettotals, {} }, { "network", "getnetworkinfo", getnetworkinfo, {} }, { "network", "setban", setban, {"subnet", "command", "bantime", "absolute"} }, { "network", "listbanned", listbanned, {} }, { "network", "clearbanned", clearbanned, {} }, { "network", "setnetworkactive", setnetworkactive, {"state"} }, { "network", "getnodeaddresses", getnodeaddresses, {"count"} }, }; // clang-format on void RegisterNetRPCCommands(CRPCTable &t) { for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) { t.appendCommand(commands[vcidx].name, &commands[vcidx]); } }