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