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