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