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