Coverage Report

Created: 2026-07-14 18:13

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