diff --git a/doc/release-notes.md b/doc/release-notes.md
index 75ca421e9..e756ec49a 100644
--- a/doc/release-notes.md
+++ b/doc/release-notes.md
@@ -1,11 +1,16 @@
# Bitcoin ABC 0.24.5 Release Notes
Bitcoin ABC version 0.24.5 is now available from:
This release includes the following features and fixes:
- The `getpeerinfo` RPC now returns a `connection_type` field. This indicates
the type of connection established with the peer. It will return one of six
options. For more information, see the `getpeerinfo` help documentation.
+- The `getpeerinfo` RPC no longer returns the `addnode` field by default. This
+ field will be fully removed in a future release. It can be accessed
+ with the configuration option `-deprecatedrpc=getpeerinfo_addnode`. However,
+ it is recommended to instead use the `connection_type` field (it will return
+ `manual` when addnode is true).
diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp
index fbd67c486..4973c6c04 100644
--- a/src/rpc/net.cpp
+++ b/src/rpc/net.cpp
@@ -1,1096 +1,1101 @@
// Copyright (c) 2009-2019 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 // For banmap_t
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
static RPCHelpMan getconnectioncount() {
return RPCHelpMan{
"getconnectioncount",
"Returns the number of connections to other nodes.\n",
{},
RPCResult{RPCResult::Type::NUM, "", "The connection count"},
RPCExamples{HelpExampleCli("getconnectioncount", "") +
HelpExampleRpc("getconnectioncount", "")},
[&](const RPCHelpMan &self, const Config &config,
const JSONRPCRequest &request) -> UniValue {
NodeContext &node = EnsureNodeContext(request.context);
if (!node.connman) {
throw JSONRPCError(
RPC_CLIENT_P2P_DISABLED,
"Error: Peer-to-peer functionality missing or disabled");
}
return int(node.connman->GetNodeCount(CConnman::CONNECTIONS_ALL));
},
};
}
static RPCHelpMan ping() {
return RPCHelpMan{
"ping",
"Requests 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",
{},
RPCResult{RPCResult::Type::NONE, "", ""},
RPCExamples{HelpExampleCli("ping", "") + HelpExampleRpc("ping", "")},
[&](const RPCHelpMan &self, const Config &config,
const JSONRPCRequest &request) -> UniValue {
NodeContext &node = EnsureNodeContext(request.context);
if (!node.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
node.connman->ForEachNode(
[](CNode *pnode) { pnode->fPingQueued = true; });
return NullUniValue;
},
};
}
static RPCHelpMan getpeerinfo() {
return RPCHelpMan{
"getpeerinfo",
"Returns data about each connected network node as a json array of "
"objects.\n",
{},
RPCResult{
RPCResult::Type::ARR,
"",
"",
{{
RPCResult::Type::OBJ,
"",
"",
{{
{RPCResult::Type::NUM, "id", "Peer index"},
{RPCResult::Type::STR, "addr",
"(host:port) The IP address and port of the peer"},
{RPCResult::Type::STR, "addrbind",
"(ip:port) Bind address of the connection to the peer"},
{RPCResult::Type::STR, "addrlocal",
"(ip:port) Local address as reported by the peer"},
{RPCResult::Type::NUM, "mapped_as",
"The AS in the BGP route to the peer used for "
"diversifying\n"
"peer selection (only available if the asmap config flag "
"is set)\n"},
{RPCResult::Type::STR_HEX, "services",
"The services offered"},
{RPCResult::Type::ARR,
"servicesnames",
"the services offered, in human-readable form",
{{RPCResult::Type::STR, "SERVICE_NAME",
"the service name if it is recognised"}}},
{RPCResult::Type::BOOL, "relaytxes",
"Whether peer has asked us to relay transactions to it"},
{RPCResult::Type::NUM_TIME, "lastsend",
"The " + UNIX_EPOCH_TIME + " of the last send"},
{RPCResult::Type::NUM_TIME, "lastrecv",
"The " + UNIX_EPOCH_TIME + " of the last receive"},
{RPCResult::Type::NUM_TIME, "last_transaction",
"The " + UNIX_EPOCH_TIME +
" of the last valid transaction received from this "
"peer"},
{RPCResult::Type::NUM_TIME, "last_block",
"The " + UNIX_EPOCH_TIME +
" of the last block received from this peer"},
{RPCResult::Type::NUM, "bytessent", "The total bytes sent"},
{RPCResult::Type::NUM, "bytesrecv",
"The total bytes received"},
{RPCResult::Type::NUM_TIME, "conntime",
"The " + UNIX_EPOCH_TIME + " of the connection"},
{RPCResult::Type::NUM, "timeoffset",
"The time offset in seconds"},
{RPCResult::Type::NUM, "pingtime",
"ping time (if available)"},
{RPCResult::Type::NUM, "minping",
"minimum observed ping time (if any at all)"},
{RPCResult::Type::NUM, "pingwait",
"ping wait (if non-zero)"},
{RPCResult::Type::NUM, "version",
"The peer version, such as 70001"},
{RPCResult::Type::STR, "subver", "The string version"},
{RPCResult::Type::BOOL, "inbound",
"Inbound (true) or Outbound (false)"},
{RPCResult::Type::BOOL, "addnode",
"Whether connection was due to addnode/-connect or if it "
- "was an automatic/inbound connection"},
+ "was an automatic/inbound connection\n(DEPRECATED, "
+ "returned only if the config option "
+ "-deprecatedrpc=getpeerinfo_addnode is passed)"},
{RPCResult::Type::STR, "connection_type",
"Type of connection: \n" +
Join(CONNECTION_TYPE_DOC, ",\n") + "."},
{RPCResult::Type::NUM, "startingheight",
"The starting height (block) of the peer"},
{RPCResult::Type::NUM, "banscore",
"The ban score (DEPRECATED, returned only if config "
"option -deprecatedrpc=banscore is passed)"},
{RPCResult::Type::NUM, "synced_headers",
"The last header we have in common with this peer"},
{RPCResult::Type::NUM, "synced_blocks",
"The last block we have in common with this peer"},
{RPCResult::Type::ARR,
"inflight",
"",
{
{RPCResult::Type::NUM, "n",
"The heights of blocks we're currently asking from "
"this peer"},
}},
{RPCResult::Type::BOOL, "whitelisted",
"Whether the peer is whitelisted"},
{RPCResult::Type::NUM, "minfeefilter",
"The minimum fee rate for transactions this peer accepts"},
{RPCResult::Type::OBJ_DYN,
"bytessent_per_msg",
"",
{{RPCResult::Type::NUM, "msg",
"The total bytes sent aggregated by message type\n"
"When a message type is not listed in this json object, "
"the bytes sent are 0.\n"
"Only known message types can appear as keys in the "
"object."}}},
{RPCResult::Type::OBJ,
"bytesrecv_per_msg",
"",
{{RPCResult::Type::NUM, "msg",
"The total bytes received aggregated by message type\n"
"When a message type is not listed in this json object, "
"the bytes received are 0.\n"
"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 + "'."}}},
}},
}},
},
RPCExamples{HelpExampleCli("getpeerinfo", "") +
HelpExampleRpc("getpeerinfo", "")},
[&](const RPCHelpMan &self, const Config &config,
const JSONRPCRequest &request) -> UniValue {
NodeContext &node = EnsureNodeContext(request.context);
if (!node.connman) {
throw JSONRPCError(
RPC_CLIENT_P2P_DISABLED,
"Error: Peer-to-peer functionality missing or disabled");
}
std::vector vstats;
node.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());
}
if (stats.m_mapped_as != 0) {
obj.pushKV("mapped_as", uint64_t(stats.m_mapped_as));
}
obj.pushKV("services", strprintf("%016x", stats.nServices));
obj.pushKV("servicesnames", GetServicesNames(stats.nServices));
obj.pushKV("relaytxes", stats.fRelayTxes);
obj.pushKV("lastsend", stats.nLastSend);
obj.pushKV("lastrecv", stats.nLastRecv);
obj.pushKV("last_transaction", stats.nLastTXTime);
if (g_avalanche) {
obj.pushKV("last_proof", stats.nLastProofTime);
}
obj.pushKV("last_block", stats.nLastBlockTime);
obj.pushKV("bytessent", stats.nSendBytes);
obj.pushKV("bytesrecv", stats.nRecvBytes);
obj.pushKV("conntime", stats.nTimeConnected);
obj.pushKV("timeoffset", stats.nTimeOffset);
if (stats.m_ping_usec > 0) {
obj.pushKV("pingtime", double(stats.m_ping_usec) / 1e6);
}
if (stats.m_min_ping_usec <
std::numeric_limits::max()) {
obj.pushKV("minping", double(stats.m_min_ping_usec) / 1e6);
}
if (stats.m_ping_wait_usec > 0) {
obj.pushKV("pingwait",
double(stats.m_ping_wait_usec) / 1e6);
}
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);
+ if (IsDeprecatedRPCEnabled(gArgs, "getpeerinfo_addnode")) {
+ // addnode is deprecated in v0.24.5 for removal in v0.25.x
+ obj.pushKV("addnode", stats.m_manual_connection);
+ }
obj.pushKV("startingheight", stats.nStartingHeight);
if (fStateStats) {
if (IsDeprecatedRPCEnabled(gArgs, "banscore")) {
// banscore is deprecated in v0.22.11 for removal in
// v0.23
obj.pushKV("banscore", statestats.m_misbehavior_score);
}
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.m_legacyWhitelisted);
UniValue permissions(UniValue::VARR);
for (const auto &permission :
NetPermissions::ToStrings(stats.m_permissionFlags)) {
permissions.push_back(permission);
}
obj.pushKV("permissions", permissions);
obj.pushKV("minfeefilter", stats.minFeeFilter);
UniValue sendPerMsgCmd(UniValue::VOBJ);
for (const auto &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 auto &i : stats.mapRecvBytesPerMsgCmd) {
if (i.second > 0) {
recvPerMsgCmd.pushKV(i.first, i.second);
}
}
obj.pushKV("bytesrecv_per_msg", recvPerMsgCmd);
obj.pushKV("connection_type", stats.m_conn_type_string);
ret.push_back(obj);
}
return ret;
},
};
}
static RPCHelpMan addnode() {
return RPCHelpMan{
"addnode",
"Attempts 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, RPCArg::Optional::NO,
"The node (see getpeerinfo for nodes)"},
{"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"},
},
RPCResult{RPCResult::Type::NONE, "", ""},
RPCExamples{
HelpExampleCli("addnode", "\"192.168.0.6:8333\" \"onetry\"") +
HelpExampleRpc("addnode", "\"192.168.0.6:8333\", \"onetry\"")},
[&](const RPCHelpMan &self, const Config &config,
const JSONRPCRequest &request) -> UniValue {
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(self.ToString());
}
NodeContext &node = EnsureNodeContext(request.context);
if (!node.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;
node.connman->OpenNetworkConnection(addr, false, nullptr,
strNode.c_str(),
ConnectionType::MANUAL);
return NullUniValue;
}
if ((strCommand == "add") && (!node.connman->AddNode(strNode))) {
throw JSONRPCError(RPC_CLIENT_NODE_ALREADY_ADDED,
"Error: Node already added");
} else if ((strCommand == "remove") &&
(!node.connman->RemoveAddedNode(strNode))) {
throw JSONRPCError(
RPC_CLIENT_NODE_NOT_ADDED,
"Error: Node could not be removed. It has not been "
"added previously.");
}
return NullUniValue;
},
};
}
static RPCHelpMan disconnectnode() {
return RPCHelpMan{
"disconnectnode",
"Immediately 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,
/* default */ "fallback to nodeid",
"The IP address/port of the node"},
{"nodeid", RPCArg::Type::NUM,
/* default */ "fallback to address",
"The node ID (see getpeerinfo for node IDs)"},
},
RPCResult{RPCResult::Type::NONE, "", ""},
RPCExamples{HelpExampleCli("disconnectnode", "\"192.168.0.6:8333\"") +
HelpExampleCli("disconnectnode", "\"\" 1") +
HelpExampleRpc("disconnectnode", "\"192.168.0.6:8333\"") +
HelpExampleRpc("disconnectnode", "\"\", 1")},
[&](const RPCHelpMan &self, const Config &config,
const JSONRPCRequest &request) -> UniValue {
NodeContext &node = EnsureNodeContext(request.context);
if (!node.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 = node.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 = node.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 RPCHelpMan getaddednodeinfo() {
return RPCHelpMan{
"getaddednodeinfo",
"Returns information about the given added node, or all added nodes\n"
"(note that onetry addnodes are not listed here)\n",
{
{"node", RPCArg::Type::STR, /* default */ "all nodes",
"If provided, return information about this specific node, "
"otherwise all nodes are returned."},
},
RPCResult{
RPCResult::Type::ARR,
"",
"",
{
{RPCResult::Type::OBJ,
"",
"",
{
{RPCResult::Type::STR, "addednode",
"The node IP address or name (as provided to addnode)"},
{RPCResult::Type::BOOL, "connected", "If connected"},
{RPCResult::Type::ARR,
"addresses",
"Only when connected = true",
{
{RPCResult::Type::OBJ,
"",
"",
{
{RPCResult::Type::STR, "address",
"The bitcoin server IP and port we're "
"connected to"},
{RPCResult::Type::STR, "connected",
"connection, inbound or outbound"},
}},
}},
}},
}},
RPCExamples{HelpExampleCli("getaddednodeinfo", "\"192.168.0.201\"") +
HelpExampleRpc("getaddednodeinfo", "\"192.168.0.201\"")},
[&](const RPCHelpMan &self, const Config &config,
const JSONRPCRequest &request) -> UniValue {
NodeContext &node = EnsureNodeContext(request.context);
if (!node.connman) {
throw JSONRPCError(
RPC_CLIENT_P2P_DISABLED,
"Error: Peer-to-peer functionality missing or disabled");
}
std::vector vInfo = node.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 RPCHelpMan getnettotals() {
return RPCHelpMan{
"getnettotals",
"Returns information about network traffic, including bytes in, "
"bytes out,\n"
"and current time.\n",
{},
RPCResult{
RPCResult::Type::OBJ,
"",
"",
{
{RPCResult::Type::NUM, "totalbytesrecv",
"Total bytes received"},
{RPCResult::Type::NUM, "totalbytessent", "Total bytes sent"},
{RPCResult::Type::NUM_TIME, "timemillis",
"Current " + UNIX_EPOCH_TIME + " in milliseconds"},
{RPCResult::Type::OBJ,
"uploadtarget",
"",
{
{RPCResult::Type::NUM, "timeframe",
"Length of the measuring timeframe in seconds"},
{RPCResult::Type::NUM, "target", "Target in bytes"},
{RPCResult::Type::BOOL, "target_reached",
"True if target is reached"},
{RPCResult::Type::BOOL, "serve_historical_blocks",
"True if serving historical blocks"},
{RPCResult::Type::NUM, "bytes_left_in_cycle",
"Bytes left in current time cycle"},
{RPCResult::Type::NUM, "time_left_in_cycle",
"Seconds left in current time cycle"},
}},
}},
RPCExamples{HelpExampleCli("getnettotals", "") +
HelpExampleRpc("getnettotals", "")},
[&](const RPCHelpMan &self, const Config &config,
const JSONRPCRequest &request) -> UniValue {
NodeContext &node = EnsureNodeContext(request.context);
if (!node.connman) {
throw JSONRPCError(
RPC_CLIENT_P2P_DISABLED,
"Error: Peer-to-peer functionality missing or disabled");
}
UniValue obj(UniValue::VOBJ);
obj.pushKV("totalbytesrecv", node.connman->GetTotalBytesRecv());
obj.pushKV("totalbytessent", node.connman->GetTotalBytesSent());
obj.pushKV("timemillis", GetTimeMillis());
UniValue outboundLimit(UniValue::VOBJ);
outboundLimit.pushKV("timeframe",
node.connman->GetMaxOutboundTimeframe());
outboundLimit.pushKV("target",
node.connman->GetMaxOutboundTarget());
outboundLimit.pushKV("target_reached",
node.connman->OutboundTargetReached(false));
outboundLimit.pushKV("serve_historical_blocks",
!node.connman->OutboundTargetReached(true));
outboundLimit.pushKV("bytes_left_in_cycle",
node.connman->GetOutboundTargetBytesLeft());
outboundLimit.pushKV("time_left_in_cycle",
node.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 RPCHelpMan getnetworkinfo() {
const auto &ticker = Currency::get().ticker;
return RPCHelpMan{
"getnetworkinfo",
"Returns an object containing various state info regarding P2P "
"networking.\n",
{},
RPCResult{
RPCResult::Type::OBJ,
"",
"",
{
{RPCResult::Type::NUM, "version", "the server version"},
{RPCResult::Type::STR, "subversion",
"the server subversion string"},
{RPCResult::Type::NUM, "protocolversion",
"the protocol version"},
{RPCResult::Type::STR_HEX, "localservices",
"the services we offer to the network"},
{RPCResult::Type::ARR,
"localservicesnames",
"the services we offer to the network, in human-readable form",
{
{RPCResult::Type::STR, "SERVICE_NAME", "the service name"},
}},
{RPCResult::Type::BOOL, "localrelay",
"true if transaction relay is requested from peers"},
{RPCResult::Type::NUM, "timeoffset", "the time offset"},
{RPCResult::Type::NUM, "connections",
"the total number of connections"},
{RPCResult::Type::NUM, "connections_in",
"the number of inbound connections"},
{RPCResult::Type::NUM, "connections_out",
"the number of outbound connections"},
{RPCResult::Type::BOOL, "networkactive",
"whether p2p networking is enabled"},
{RPCResult::Type::ARR,
"networks",
"information per network",
{
{RPCResult::Type::OBJ,
"",
"",
{
{RPCResult::Type::STR, "name",
"network (ipv4, ipv6 or onion)"},
{RPCResult::Type::BOOL, "limited",
"is the network limited using -onlynet?"},
{RPCResult::Type::BOOL, "reachable",
"is the network reachable?"},
{RPCResult::Type::STR, "proxy",
"(\"host:port\") the proxy that is used for this "
"network, or empty if none"},
{RPCResult::Type::BOOL, "proxy_randomize_credentials",
"Whether randomized credentials are used"},
}},
}},
{RPCResult::Type::NUM, "relayfee",
"minimum relay fee for transactions in " + ticker + "/kB"},
{RPCResult::Type::NUM, "excessutxocharge",
"minimum charge for excess utxos in " + ticker},
{RPCResult::Type::ARR,
"localaddresses",
"list of local addresses",
{
{RPCResult::Type::OBJ,
"",
"",
{
{RPCResult::Type::STR, "address", "network address"},
{RPCResult::Type::NUM, "port", "network port"},
{RPCResult::Type::NUM, "score", "relative score"},
}},
}},
{RPCResult::Type::STR, "warnings",
"any network and blockchain warnings"},
}},
RPCExamples{HelpExampleCli("getnetworkinfo", "") +
HelpExampleRpc("getnetworkinfo", "")},
[&](const RPCHelpMan &self, const Config &config,
const JSONRPCRequest &request) -> UniValue {
LOCK(cs_main);
UniValue obj(UniValue::VOBJ);
obj.pushKV("version", CLIENT_VERSION);
obj.pushKV("subversion", userAgent(config));
obj.pushKV("protocolversion", PROTOCOL_VERSION);
NodeContext &node = EnsureNodeContext(request.context);
if (node.connman) {
ServiceFlags services = node.connman->GetLocalServices();
obj.pushKV("localservices", strprintf("%016x", services));
obj.pushKV("localservicesnames", GetServicesNames(services));
}
obj.pushKV("localrelay", g_relay_txes);
obj.pushKV("timeoffset", GetTimeOffset());
if (node.connman) {
obj.pushKV("networkactive", node.connman->GetNetworkActive());
obj.pushKV("connections", int(node.connman->GetNodeCount(
CConnman::CONNECTIONS_ALL)));
obj.pushKV("connections_in", int(node.connman->GetNodeCount(
CConnman::CONNECTIONS_IN)));
obj.pushKV("connections_out", int(node.connman->GetNodeCount(
CConnman::CONNECTIONS_OUT)));
}
obj.pushKV("networks", GetNetworksInfo());
obj.pushKV("relayfee", ::minRelayTxFee.GetFeePerK());
obj.pushKV("excessutxocharge", 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(false).original);
return obj;
},
};
}
static RPCHelpMan setban() {
return RPCHelpMan{
"setban",
"Attempts to add or remove an IP/Subnet from the banned list.\n",
{
{"subnet", RPCArg::Type::STR, RPCArg::Optional::NO,
"The IP/Subnet (see getpeerinfo for nodes IP) with an optional "
"netmask (default is /32 = single IP)"},
{"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"},
{"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)"},
{"absolute", RPCArg::Type::BOOL, /* default */ "false",
"If set, the bantime must be an absolute timestamp expressed in " +
UNIX_EPOCH_TIME},
},
RPCResult{RPCResult::Type::NONE, "", ""},
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")},
[&](const RPCHelpMan &help, const Config &config,
const JSONRPCRequest &request) -> UniValue {
std::string strCommand;
if (!request.params[1].isNull()) {
strCommand = request.params[1].get_str();
}
if (request.fHelp || !help.IsValidNumArgs(request.params.size()) ||
(strCommand != "add" && strCommand != "remove")) {
throw std::runtime_error(help.ToString());
}
NodeContext &node = EnsureNodeContext(request.context);
if (!node.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(), resolved, false);
netAddr = resolved;
} else {
LookupSubNet(request.params[0].get_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 ? node.banman->IsBanned(subNet)
: node.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) {
node.banman->Ban(subNet, banTime, absolute);
if (node.connman) {
node.connman->DisconnectNode(subNet);
}
} else {
node.banman->Ban(netAddr, banTime, absolute);
if (node.connman) {
node.connman->DisconnectNode(netAddr);
}
}
} else if (strCommand == "remove") {
if (!(isSubnet ? node.banman->Unban(subNet)
: node.banman->Unban(netAddr))) {
throw JSONRPCError(
RPC_CLIENT_INVALID_IP_OR_SUBNET,
"Error: Unban failed. Requested address/subnet "
"was not previously manually banned.");
}
}
return NullUniValue;
},
};
}
static RPCHelpMan listbanned() {
return RPCHelpMan{
"listbanned",
"List all manually banned IPs/Subnets.\n",
{},
RPCResult{RPCResult::Type::ARR,
"",
"",
{
{RPCResult::Type::OBJ,
"",
"",
{
{RPCResult::Type::STR, "address", ""},
{RPCResult::Type::NUM_TIME, "banned_until", ""},
{RPCResult::Type::NUM_TIME, "ban_created", ""},
{RPCResult::Type::STR, "ban_reason", ""},
}},
}},
RPCExamples{HelpExampleCli("listbanned", "") +
HelpExampleRpc("listbanned", "")},
[&](const RPCHelpMan &self, const Config &config,
const JSONRPCRequest &request) -> UniValue {
NodeContext &node = EnsureNodeContext(request.context);
if (!node.banman) {
throw JSONRPCError(RPC_DATABASE_ERROR,
"Error: Ban database not loaded");
}
banmap_t banMap;
node.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);
bannedAddresses.push_back(rec);
}
return bannedAddresses;
},
};
}
static RPCHelpMan clearbanned() {
return RPCHelpMan{
"clearbanned",
"Clear all banned IPs.\n",
{},
RPCResult{RPCResult::Type::NONE, "", ""},
RPCExamples{HelpExampleCli("clearbanned", "") +
HelpExampleRpc("clearbanned", "")},
[&](const RPCHelpMan &self, const Config &config,
const JSONRPCRequest &request) -> UniValue {
NodeContext &node = EnsureNodeContext(request.context);
if (!node.banman) {
throw JSONRPCError(
RPC_CLIENT_P2P_DISABLED,
"Error: Peer-to-peer functionality missing or disabled");
}
node.banman->ClearBanned();
return NullUniValue;
},
};
}
static RPCHelpMan setnetworkactive() {
return RPCHelpMan{
"setnetworkactive",
"Disable/enable all p2p network activity.\n",
{
{"state", RPCArg::Type::BOOL, RPCArg::Optional::NO,
"true to enable networking, false to disable"},
},
RPCResult{RPCResult::Type::BOOL, "", "The value that was passed in"},
RPCExamples{""},
[&](const RPCHelpMan &self, const Config &config,
const JSONRPCRequest &request) -> UniValue {
NodeContext &node = EnsureNodeContext(request.context);
if (!node.banman) {
throw JSONRPCError(
RPC_CLIENT_P2P_DISABLED,
"Error: Peer-to-peer functionality missing or disabled");
}
node.connman->SetNetworkActive(request.params[0].get_bool());
return node.connman->GetNetworkActive();
},
};
}
static RPCHelpMan getnodeaddresses() {
return RPCHelpMan{
"getnodeaddresses",
"Return known addresses which can potentially be used to find new "
"nodes in the network\n",
{
{"count", RPCArg::Type::NUM, /* default */ "1",
"The maximum number of addresses to return. Specify 0 to return "
"all known addresses."},
},
RPCResult{
RPCResult::Type::ARR,
"",
"",
{
{RPCResult::Type::OBJ,
"",
"",
{
{RPCResult::Type::NUM_TIME, "time",
"The " + UNIX_EPOCH_TIME +
" of when the node was last seen"},
{RPCResult::Type::NUM, "services", "The services offered"},
{RPCResult::Type::STR, "address",
"The address of the node"},
{RPCResult::Type::NUM, "port", "The port of the node"},
}},
}},
RPCExamples{HelpExampleCli("getnodeaddresses", "8") +
HelpExampleRpc("getnodeaddresses", "8")},
[&](const RPCHelpMan &self, const Config &config,
const JSONRPCRequest &request) -> UniValue {
NodeContext &node = EnsureNodeContext(request.context);
if (!node.banman) {
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 =
node.connman->GetAddresses(count, /* max_pct */ 0);
UniValue ret(UniValue::VARR);
for (const CAddress &addr : vAddr) {
UniValue obj(UniValue::VOBJ);
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;
},
};
}
static RPCHelpMan addpeeraddress() {
return RPCHelpMan{
"addpeeraddress",
"Add the address of a potential peer to the address manager. This "
"RPC is for testing only.\n",
{
{"address", RPCArg::Type::STR, RPCArg::Optional::NO,
"The IP address of the peer"},
{"port", RPCArg::Type::NUM, RPCArg::Optional::NO,
"The port of the peer"},
},
RPCResult{
RPCResult::Type::OBJ,
"",
"",
{
{RPCResult::Type::BOOL, "success",
"whether the peer address was successfully added to the "
"address manager"},
},
},
RPCExamples{HelpExampleCli("addpeeraddress", "\"1.2.3.4\" 8333") +
HelpExampleRpc("addpeeraddress", "\"1.2.3.4\", 8333")},
[&](const RPCHelpMan &self, const Config &config,
const JSONRPCRequest &request) -> UniValue {
NodeContext &node = EnsureNodeContext(request.context);
if (!node.connman) {
throw JSONRPCError(
RPC_CLIENT_P2P_DISABLED,
"Error: Peer-to-peer functionality missing or disabled");
}
UniValue obj(UniValue::VOBJ);
std::string addr_string = request.params[0].get_str();
uint16_t port = request.params[1].get_int();
CNetAddr net_addr;
if (!LookupHost(addr_string, net_addr, false)) {
obj.pushKV("success", false);
return obj;
}
CAddress address =
CAddress({net_addr, port}, ServiceFlags(NODE_NETWORK));
address.nTime = GetAdjustedTime();
// The source address is set equal to the address. This is
// equivalent to the peer announcing itself.
if (!node.connman->AddNewAddresses({address}, address)) {
obj.pushKV("success", false);
return obj;
}
obj.pushKV("success", true);
return obj;
},
};
}
void RegisterNetRPCCommands(CRPCTable &t) {
// clang-format off
static const CRPCCommand 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"} },
{ "hidden", "addpeeraddress", addpeeraddress, {"address", "port"} },
};
// clang-format on
for (const auto &c : commands) {
t.appendCommand(c.name, &c);
}
}
diff --git a/test/functional/rpc_getpeerinfo_deprecation.py b/test/functional/rpc_getpeerinfo_deprecation.py
index 73d7fb6e7..f8c05d43c 100755
--- a/test/functional/rpc_getpeerinfo_deprecation.py
+++ b/test/functional/rpc_getpeerinfo_deprecation.py
@@ -1,29 +1,43 @@
#!/usr/bin/env python3
# Copyright (c) 2020 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Test deprecation of getpeerinfo RPC fields."""
from test_framework.test_framework import BitcoinTestFramework
+from test_framework.util import connect_nodes
class GetpeerinfoDeprecationTest(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 2
self.extra_args = [[], ["-deprecatedrpc=banscore"]]
def run_test(self):
self.test_banscore_deprecation()
+ self.test_addnode_deprecation()
def test_banscore_deprecation(self):
self.log.info(
"Test getpeerinfo by default no longer returns a banscore field")
assert "banscore" not in self.nodes[0].getpeerinfo()[0].keys()
self.log.info(
"Test getpeerinfo returns banscore with -deprecatedrpc=banscore")
assert "banscore" in self.nodes[1].getpeerinfo()[0].keys()
+ def test_addnode_deprecation(self):
+ self.restart_node(1, ["-deprecatedrpc=getpeerinfo_addnode"])
+ connect_nodes(self.nodes[0], self.nodes[1])
+
+ self.log.info(
+ "Test getpeerinfo by default no longer returns an addnode field")
+ assert "addnode" not in self.nodes[0].getpeerinfo()[0].keys()
+
+ self.log.info(
+ "Test getpeerinfo returns addnode with -deprecatedrpc=addnode")
+ assert "addnode" in self.nodes[1].getpeerinfo()[0].keys()
+
if __name__ == "__main__":
GetpeerinfoDeprecationTest().main()