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