/bitcoin/src/rpc/node.cpp
Line | Count | Source |
1 | | // Copyright (c) 2010 Satoshi Nakamoto |
2 | | // Copyright (c) 2009-2022 The Bitcoin Core developers |
3 | | // Distributed under the MIT software license, see the accompanying |
4 | | // file COPYING or http://www.opensource.org/licenses/mit-license.php. |
5 | | |
6 | | #include <bitcoin-build-config.h> // IWYU pragma: keep |
7 | | |
8 | | #include <chainparams.h> |
9 | | #include <httpserver.h> |
10 | | #include <index/blockfilterindex.h> |
11 | | #include <index/coinstatsindex.h> |
12 | | #include <index/txindex.h> |
13 | | #include <interfaces/chain.h> |
14 | | #include <interfaces/echo.h> |
15 | | #include <interfaces/init.h> |
16 | | #include <interfaces/ipc.h> |
17 | | #include <kernel/cs_main.h> |
18 | | #include <logging.h> |
19 | | #include <node/context.h> |
20 | | #include <rpc/server.h> |
21 | | #include <rpc/server_util.h> |
22 | | #include <rpc/util.h> |
23 | | #include <scheduler.h> |
24 | | #include <univalue.h> |
25 | | #include <util/any.h> |
26 | | #include <util/check.h> |
27 | | #include <util/time.h> |
28 | | |
29 | | #include <stdint.h> |
30 | | #ifdef HAVE_MALLOC_INFO |
31 | | #include <malloc.h> |
32 | | #endif |
33 | | |
34 | | using node::NodeContext; |
35 | | |
36 | | static RPCHelpMan setmocktime() |
37 | 2.28M | { |
38 | 2.28M | return RPCHelpMan{ |
39 | 2.28M | "setmocktime", |
40 | 2.28M | "Set the local time to given timestamp (-regtest only)\n", |
41 | 2.28M | { |
42 | 2.28M | {"timestamp", RPCArg::Type::NUM, RPCArg::Optional::NO, UNIX_EPOCH_TIME + "\n" |
43 | 2.28M | "Pass 0 to go back to using the system time."}, |
44 | 2.28M | }, |
45 | 2.28M | RPCResult{RPCResult::Type::NONE, "", ""}, |
46 | 2.28M | RPCExamples{""}, |
47 | 2.28M | [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue |
48 | 2.28M | { |
49 | 2.25M | if (!Params().IsMockableChain()) { Branch (49:9): [True: 0, False: 2.25M]
|
50 | 0 | throw std::runtime_error("setmocktime is for regression testing (-regtest mode) only"); |
51 | 0 | } |
52 | | |
53 | | // For now, don't change mocktime if we're in the middle of validation, as |
54 | | // this could have an effect on mempool time-based eviction, as well as |
55 | | // IsCurrentForFeeEstimation() and IsInitialBlockDownload(). |
56 | | // TODO: figure out the right way to synchronize around mocktime, and |
57 | | // ensure all call sites of GetTime() are accessing this safely. |
58 | 2.25M | LOCK(cs_main); |
59 | | |
60 | 2.25M | const int64_t time{request.params[0].getInt<int64_t>()}; |
61 | 2.25M | constexpr int64_t max_time{Ticks<std::chrono::seconds>(std::chrono::nanoseconds::max())}; |
62 | 2.25M | if (time < 0 || time > max_time) { Branch (62:9): [True: 0, False: 2.25M]
Branch (62:21): [True: 4.78k, False: 2.25M]
|
63 | 4.78k | throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Mocktime must be in the range [0, %s], not %s.", max_time, time)); |
64 | 4.78k | } |
65 | | |
66 | 2.25M | SetMockTime(time); |
67 | 2.25M | const NodeContext& node_context{EnsureAnyNodeContext(request.context)}; |
68 | 2.25M | for (const auto& chain_client : node_context.chain_clients) { Branch (68:35): [True: 2.25M, False: 2.25M]
|
69 | 2.25M | chain_client->setMockTime(time); |
70 | 2.25M | } |
71 | | |
72 | 2.25M | return UniValue::VNULL; |
73 | 2.25M | }, |
74 | 2.28M | }; |
75 | 2.28M | } |
76 | | |
77 | | static RPCHelpMan mockscheduler() |
78 | 22.1k | { |
79 | 22.1k | return RPCHelpMan{ |
80 | 22.1k | "mockscheduler", |
81 | 22.1k | "Bump the scheduler into the future (-regtest only)\n", |
82 | 22.1k | { |
83 | 22.1k | {"delta_time", RPCArg::Type::NUM, RPCArg::Optional::NO, "Number of seconds to forward the scheduler into the future." }, |
84 | 22.1k | }, |
85 | 22.1k | RPCResult{RPCResult::Type::NONE, "", ""}, |
86 | 22.1k | RPCExamples{""}, |
87 | 22.1k | [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue |
88 | 22.1k | { |
89 | 0 | if (!Params().IsMockableChain()) { Branch (89:9): [True: 0, False: 0]
|
90 | 0 | throw std::runtime_error("mockscheduler is for regression testing (-regtest mode) only"); |
91 | 0 | } |
92 | | |
93 | 0 | int64_t delta_seconds = request.params[0].getInt<int64_t>(); |
94 | 0 | if (delta_seconds <= 0 || delta_seconds > 3600) { Branch (94:9): [True: 0, False: 0]
Branch (94:31): [True: 0, False: 0]
|
95 | 0 | throw std::runtime_error("delta_time must be between 1 and 3600 seconds (1 hr)"); |
96 | 0 | } |
97 | | |
98 | 0 | const NodeContext& node_context{EnsureAnyNodeContext(request.context)}; |
99 | 0 | CHECK_NONFATAL(node_context.scheduler)->MockForward(std::chrono::seconds{delta_seconds}); |
100 | 0 | CHECK_NONFATAL(node_context.validation_signals)->SyncWithValidationInterfaceQueue(); |
101 | 0 | for (const auto& chain_client : node_context.chain_clients) { Branch (101:35): [True: 0, False: 0]
|
102 | 0 | chain_client->schedulerMockForward(std::chrono::seconds(delta_seconds)); |
103 | 0 | } |
104 | |
|
105 | 0 | return UniValue::VNULL; |
106 | 0 | }, |
107 | 22.1k | }; |
108 | 22.1k | } |
109 | | |
110 | | static UniValue RPCLockedMemoryInfo() |
111 | 0 | { |
112 | 0 | LockedPool::Stats stats = LockedPoolManager::Instance().stats(); |
113 | 0 | UniValue obj(UniValue::VOBJ); |
114 | 0 | obj.pushKV("used", uint64_t(stats.used)); |
115 | 0 | obj.pushKV("free", uint64_t(stats.free)); |
116 | 0 | obj.pushKV("total", uint64_t(stats.total)); |
117 | 0 | obj.pushKV("locked", uint64_t(stats.locked)); |
118 | 0 | obj.pushKV("chunks_used", uint64_t(stats.chunks_used)); |
119 | 0 | obj.pushKV("chunks_free", uint64_t(stats.chunks_free)); |
120 | 0 | return obj; |
121 | 0 | } |
122 | | |
123 | | #ifdef HAVE_MALLOC_INFO |
124 | | static std::string RPCMallocInfo() |
125 | 0 | { |
126 | 0 | char *ptr = nullptr; |
127 | 0 | size_t size = 0; |
128 | 0 | FILE *f = open_memstream(&ptr, &size); |
129 | 0 | if (f) { Branch (129:9): [True: 0, False: 0]
|
130 | 0 | malloc_info(0, f); |
131 | 0 | fclose(f); |
132 | 0 | if (ptr) { Branch (132:13): [True: 0, False: 0]
|
133 | 0 | std::string rv(ptr, size); |
134 | 0 | free(ptr); |
135 | 0 | return rv; |
136 | 0 | } |
137 | 0 | } |
138 | 0 | return ""; |
139 | 0 | } |
140 | | #endif |
141 | | |
142 | | static RPCHelpMan getmemoryinfo() |
143 | 22.1k | { |
144 | | /* Please, avoid using the word "pool" here in the RPC interface or help, |
145 | | * as users will undoubtedly confuse it with the other "memory pool" |
146 | | */ |
147 | 22.1k | return RPCHelpMan{"getmemoryinfo", |
148 | 22.1k | "Returns an object containing information about memory usage.\n", |
149 | 22.1k | { |
150 | 22.1k | {"mode", RPCArg::Type::STR, RPCArg::Default{"stats"}, "determines what kind of information is returned.\n" |
151 | 22.1k | " - \"stats\" returns general statistics about memory usage in the daemon.\n" |
152 | 22.1k | " - \"mallocinfo\" returns an XML string describing low-level heap state (only available if compiled with glibc)."}, |
153 | 22.1k | }, |
154 | 22.1k | { |
155 | 22.1k | RPCResult{"mode \"stats\"", |
156 | 22.1k | RPCResult::Type::OBJ, "", "", |
157 | 22.1k | { |
158 | 22.1k | {RPCResult::Type::OBJ, "locked", "Information about locked memory manager", |
159 | 22.1k | { |
160 | 22.1k | {RPCResult::Type::NUM, "used", "Number of bytes used"}, |
161 | 22.1k | {RPCResult::Type::NUM, "free", "Number of bytes available in current arenas"}, |
162 | 22.1k | {RPCResult::Type::NUM, "total", "Total number of bytes managed"}, |
163 | 22.1k | {RPCResult::Type::NUM, "locked", "Amount of bytes that succeeded locking. If this number is smaller than total, locking pages failed at some point and key data could be swapped to disk."}, |
164 | 22.1k | {RPCResult::Type::NUM, "chunks_used", "Number allocated chunks"}, |
165 | 22.1k | {RPCResult::Type::NUM, "chunks_free", "Number unused chunks"}, |
166 | 22.1k | }}, |
167 | 22.1k | } |
168 | 22.1k | }, |
169 | 22.1k | RPCResult{"mode \"mallocinfo\"", |
170 | 22.1k | RPCResult::Type::STR, "", "\"<malloc version=\"1\">...\"" |
171 | 22.1k | }, |
172 | 22.1k | }, |
173 | 22.1k | RPCExamples{ |
174 | 22.1k | HelpExampleCli("getmemoryinfo", "") |
175 | 22.1k | + HelpExampleRpc("getmemoryinfo", "") |
176 | 22.1k | }, |
177 | 22.1k | [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue |
178 | 22.1k | { |
179 | 0 | std::string mode = request.params[0].isNull() ? "stats" : request.params[0].get_str(); Branch (179:24): [True: 0, False: 0]
|
180 | 0 | if (mode == "stats") { Branch (180:9): [True: 0, False: 0]
|
181 | 0 | UniValue obj(UniValue::VOBJ); |
182 | 0 | obj.pushKV("locked", RPCLockedMemoryInfo()); |
183 | 0 | return obj; |
184 | 0 | } else if (mode == "mallocinfo") { Branch (184:16): [True: 0, False: 0]
|
185 | 0 | #ifdef HAVE_MALLOC_INFO |
186 | 0 | return RPCMallocInfo(); |
187 | | #else |
188 | | throw JSONRPCError(RPC_INVALID_PARAMETER, "mallocinfo mode not available"); |
189 | | #endif |
190 | 0 | } else { |
191 | 0 | throw JSONRPCError(RPC_INVALID_PARAMETER, "unknown mode " + mode); |
192 | 0 | } |
193 | 0 | }, |
194 | 22.1k | }; |
195 | 22.1k | } |
196 | | |
197 | 0 | static void EnableOrDisableLogCategories(UniValue cats, bool enable) { |
198 | 0 | cats = cats.get_array(); |
199 | 0 | for (unsigned int i = 0; i < cats.size(); ++i) { Branch (199:30): [True: 0, False: 0]
|
200 | 0 | std::string cat = cats[i].get_str(); |
201 | |
|
202 | 0 | bool success; |
203 | 0 | if (enable) { Branch (203:13): [True: 0, False: 0]
|
204 | 0 | success = LogInstance().EnableCategory(cat); |
205 | 0 | } else { |
206 | 0 | success = LogInstance().DisableCategory(cat); |
207 | 0 | } |
208 | |
|
209 | 0 | if (!success) { Branch (209:13): [True: 0, False: 0]
|
210 | 0 | throw JSONRPCError(RPC_INVALID_PARAMETER, "unknown logging category " + cat); |
211 | 0 | } |
212 | 0 | } |
213 | 0 | } |
214 | | |
215 | | static RPCHelpMan logging() |
216 | 22.1k | { |
217 | 22.1k | return RPCHelpMan{"logging", |
218 | 22.1k | "Gets and sets the logging configuration.\n" |
219 | 22.1k | "When called without an argument, returns the list of categories with status that are currently being debug logged or not.\n" |
220 | 22.1k | "When called with arguments, adds or removes categories from debug logging and return the lists above.\n" |
221 | 22.1k | "The arguments are evaluated in order \"include\", \"exclude\".\n" |
222 | 22.1k | "If an item is both included and excluded, it will thus end up being excluded.\n" |
223 | 22.1k | "The valid logging categories are: " + LogInstance().LogCategoriesString() + "\n" |
224 | 22.1k | "In addition, the following are available as category names with special meanings:\n" |
225 | 22.1k | " - \"all\", \"1\" : represent all logging categories.\n" |
226 | 22.1k | , |
227 | 22.1k | { |
228 | 22.1k | {"include", RPCArg::Type::ARR, RPCArg::Optional::OMITTED, "The categories to add to debug logging", |
229 | 22.1k | { |
230 | 22.1k | {"include_category", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "the valid logging category"}, |
231 | 22.1k | }}, |
232 | 22.1k | {"exclude", RPCArg::Type::ARR, RPCArg::Optional::OMITTED, "The categories to remove from debug logging", |
233 | 22.1k | { |
234 | 22.1k | {"exclude_category", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "the valid logging category"}, |
235 | 22.1k | }}, |
236 | 22.1k | }, |
237 | 22.1k | RPCResult{ |
238 | 22.1k | RPCResult::Type::OBJ_DYN, "", "keys are the logging categories, and values indicates its status", |
239 | 22.1k | { |
240 | 22.1k | {RPCResult::Type::BOOL, "category", "if being debug logged or not. false:inactive, true:active"}, |
241 | 22.1k | } |
242 | 22.1k | }, |
243 | 22.1k | RPCExamples{ |
244 | 22.1k | HelpExampleCli("logging", "\"[\\\"all\\\"]\" \"[\\\"http\\\"]\"") |
245 | 22.1k | + HelpExampleRpc("logging", "[\"all\"], [\"libevent\"]") |
246 | 22.1k | }, |
247 | 22.1k | [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue |
248 | 22.1k | { |
249 | 0 | BCLog::CategoryMask original_log_categories = LogInstance().GetCategoryMask(); |
250 | 0 | if (request.params[0].isArray()) { Branch (250:9): [True: 0, False: 0]
|
251 | 0 | EnableOrDisableLogCategories(request.params[0], true); |
252 | 0 | } |
253 | 0 | if (request.params[1].isArray()) { Branch (253:9): [True: 0, False: 0]
|
254 | 0 | EnableOrDisableLogCategories(request.params[1], false); |
255 | 0 | } |
256 | 0 | BCLog::CategoryMask updated_log_categories = LogInstance().GetCategoryMask(); |
257 | 0 | BCLog::CategoryMask changed_log_categories = original_log_categories ^ updated_log_categories; |
258 | | |
259 | | // Update libevent logging if BCLog::LIBEVENT has changed. |
260 | 0 | if (changed_log_categories & BCLog::LIBEVENT) { Branch (260:9): [True: 0, False: 0]
|
261 | 0 | UpdateHTTPServerLogging(LogInstance().WillLogCategory(BCLog::LIBEVENT)); |
262 | 0 | } |
263 | |
|
264 | 0 | UniValue result(UniValue::VOBJ); |
265 | 0 | for (const auto& logCatActive : LogInstance().LogCategoriesList()) { Branch (265:35): [True: 0, False: 0]
|
266 | 0 | result.pushKV(logCatActive.category, logCatActive.active); |
267 | 0 | } |
268 | |
|
269 | 0 | return result; |
270 | 0 | }, |
271 | 22.1k | }; |
272 | 22.1k | } |
273 | | |
274 | | static RPCHelpMan echo(const std::string& name) |
275 | 55.4k | { |
276 | 55.4k | return RPCHelpMan{ |
277 | 55.4k | name, |
278 | 55.4k | "Simply echo back the input arguments. This command is for testing.\n" |
279 | 55.4k | "\nIt will return an internal bug report when arg9='trigger_internal_bug' is passed.\n" |
280 | 55.4k | "\nThe difference between echo and echojson is that echojson has argument conversion enabled in the client-side table in " |
281 | 55.4k | "bitcoin-cli and the GUI. There is no server-side difference.", |
282 | 55.4k | { |
283 | 55.4k | {"arg0", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "", RPCArgOptions{.skip_type_check = true}}, |
284 | 55.4k | {"arg1", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "", RPCArgOptions{.skip_type_check = true}}, |
285 | 55.4k | {"arg2", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "", RPCArgOptions{.skip_type_check = true}}, |
286 | 55.4k | {"arg3", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "", RPCArgOptions{.skip_type_check = true}}, |
287 | 55.4k | {"arg4", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "", RPCArgOptions{.skip_type_check = true}}, |
288 | 55.4k | {"arg5", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "", RPCArgOptions{.skip_type_check = true}}, |
289 | 55.4k | {"arg6", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "", RPCArgOptions{.skip_type_check = true}}, |
290 | 55.4k | {"arg7", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "", RPCArgOptions{.skip_type_check = true}}, |
291 | 55.4k | {"arg8", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "", RPCArgOptions{.skip_type_check = true}}, |
292 | 55.4k | {"arg9", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "", RPCArgOptions{.skip_type_check = true}}, |
293 | 55.4k | }, |
294 | 55.4k | RPCResult{RPCResult::Type::ANY, "", "Returns whatever was passed in"}, |
295 | 55.4k | RPCExamples{""}, |
296 | 55.4k | [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue |
297 | 55.4k | { |
298 | 11.0k | if (request.params[9].isStr()) { Branch (298:9): [True: 0, False: 11.0k]
|
299 | 0 | CHECK_NONFATAL(request.params[9].get_str() != "trigger_internal_bug"); |
300 | 0 | } |
301 | | |
302 | 11.0k | return request.params; |
303 | 11.0k | }, |
304 | 55.4k | }; |
305 | 55.4k | } |
306 | | |
307 | 33.2k | static RPCHelpMan echo() { return echo("echo"); } |
308 | 22.1k | static RPCHelpMan echojson() { return echo("echojson"); } |
309 | | |
310 | | static RPCHelpMan echoipc() |
311 | 22.1k | { |
312 | 22.1k | return RPCHelpMan{ |
313 | 22.1k | "echoipc", |
314 | 22.1k | "Echo back the input argument, passing it through a spawned process in a multiprocess build.\n" |
315 | 22.1k | "This command is for testing.\n", |
316 | 22.1k | {{"arg", RPCArg::Type::STR, RPCArg::Optional::NO, "The string to echo",}}, |
317 | 22.1k | RPCResult{RPCResult::Type::STR, "echo", "The echoed string."}, |
318 | 22.1k | RPCExamples{HelpExampleCli("echo", "\"Hello world\"") + |
319 | 22.1k | HelpExampleRpc("echo", "\"Hello world\"")}, |
320 | 22.1k | [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue { |
321 | 0 | interfaces::Init& local_init = *EnsureAnyNodeContext(request.context).init; |
322 | 0 | std::unique_ptr<interfaces::Echo> echo; |
323 | 0 | if (interfaces::Ipc* ipc = local_init.ipc()) { Branch (323:34): [True: 0, False: 0]
|
324 | | // Spawn a new bitcoin-node process and call makeEcho to get a |
325 | | // client pointer to a interfaces::Echo instance running in |
326 | | // that process. This is just for testing. A slightly more |
327 | | // realistic test spawning a different executable instead of |
328 | | // the same executable would add a new bitcoin-echo executable, |
329 | | // and spawn bitcoin-echo below instead of bitcoin-node. But |
330 | | // using bitcoin-node avoids the need to build and install a |
331 | | // new executable just for this one test. |
332 | 0 | auto init = ipc->spawnProcess("bitcoin-node"); |
333 | 0 | echo = init->makeEcho(); |
334 | 0 | ipc->addCleanup(*echo, [init = init.release()] { delete init; }); |
335 | 0 | } else { |
336 | | // IPC support is not available because this is a bitcoind |
337 | | // process not a bitcoind-node process, so just create a local |
338 | | // interfaces::Echo object and return it so the `echoipc` RPC |
339 | | // method will work, and the python test calling `echoipc` |
340 | | // can expect the same result. |
341 | 0 | echo = local_init.makeEcho(); |
342 | 0 | } |
343 | 0 | return echo->echo(request.params[0].get_str()); |
344 | 0 | }, |
345 | 22.1k | }; |
346 | 22.1k | } |
347 | | |
348 | | static UniValue SummaryToJSON(const IndexSummary&& summary, std::string index_name) |
349 | 0 | { |
350 | 0 | UniValue ret_summary(UniValue::VOBJ); |
351 | 0 | if (!index_name.empty() && index_name != summary.name) return ret_summary; Branch (351:9): [True: 0, False: 0]
Branch (351:32): [True: 0, False: 0]
|
352 | | |
353 | 0 | UniValue entry(UniValue::VOBJ); |
354 | 0 | entry.pushKV("synced", summary.synced); |
355 | 0 | entry.pushKV("best_block_height", summary.best_block_height); |
356 | 0 | ret_summary.pushKV(summary.name, std::move(entry)); |
357 | 0 | return ret_summary; |
358 | 0 | } |
359 | | |
360 | | static RPCHelpMan getindexinfo() |
361 | 22.1k | { |
362 | 22.1k | return RPCHelpMan{ |
363 | 22.1k | "getindexinfo", |
364 | 22.1k | "Returns the status of one or all available indices currently running in the node.\n", |
365 | 22.1k | { |
366 | 22.1k | {"index_name", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "Filter results for an index with a specific name."}, |
367 | 22.1k | }, |
368 | 22.1k | RPCResult{ |
369 | 22.1k | RPCResult::Type::OBJ_DYN, "", "", { |
370 | 22.1k | { |
371 | 22.1k | RPCResult::Type::OBJ, "name", "The name of the index", |
372 | 22.1k | { |
373 | 22.1k | {RPCResult::Type::BOOL, "synced", "Whether the index is synced or not"}, |
374 | 22.1k | {RPCResult::Type::NUM, "best_block_height", "The block height to which the index is synced"}, |
375 | 22.1k | } |
376 | 22.1k | }, |
377 | 22.1k | }, |
378 | 22.1k | }, |
379 | 22.1k | RPCExamples{ |
380 | 22.1k | HelpExampleCli("getindexinfo", "") |
381 | 22.1k | + HelpExampleRpc("getindexinfo", "") |
382 | 22.1k | + HelpExampleCli("getindexinfo", "txindex") |
383 | 22.1k | + HelpExampleRpc("getindexinfo", "txindex") |
384 | 22.1k | }, |
385 | 22.1k | [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue |
386 | 22.1k | { |
387 | 0 | UniValue result(UniValue::VOBJ); |
388 | 0 | const std::string index_name = request.params[0].isNull() ? "" : request.params[0].get_str(); Branch (388:36): [True: 0, False: 0]
|
389 | |
|
390 | 0 | if (g_txindex) { Branch (390:9): [True: 0, False: 0]
|
391 | 0 | result.pushKVs(SummaryToJSON(g_txindex->GetSummary(), index_name)); |
392 | 0 | } |
393 | |
|
394 | 0 | if (g_coin_stats_index) { Branch (394:9): [True: 0, False: 0]
|
395 | 0 | result.pushKVs(SummaryToJSON(g_coin_stats_index->GetSummary(), index_name)); |
396 | 0 | } |
397 | |
|
398 | 0 | ForEachBlockFilterIndex([&result, &index_name](const BlockFilterIndex& index) { |
399 | 0 | result.pushKVs(SummaryToJSON(index.GetSummary(), index_name)); |
400 | 0 | }); |
401 | |
|
402 | 0 | return result; |
403 | 0 | }, |
404 | 22.1k | }; |
405 | 22.1k | } |
406 | | |
407 | | void RegisterNodeRPCCommands(CRPCTable& t) |
408 | 11.0k | { |
409 | 11.0k | static const CRPCCommand commands[]{ |
410 | 11.0k | {"control", &getmemoryinfo}, |
411 | 11.0k | {"control", &logging}, |
412 | 11.0k | {"util", &getindexinfo}, |
413 | 11.0k | {"hidden", &setmocktime}, |
414 | 11.0k | {"hidden", &mockscheduler}, |
415 | 11.0k | {"hidden", &echo}, |
416 | 11.0k | {"hidden", &echojson}, |
417 | 11.0k | {"hidden", &echoipc}, |
418 | 11.0k | }; |
419 | 88.7k | for (const auto& c : commands) { Branch (419:24): [True: 88.7k, False: 11.0k]
|
420 | 88.7k | t.appendCommand(c.name, &c); |
421 | 88.7k | } |
422 | 11.0k | } |