Bitcoin Core 32.99.0
P2P Digital Currency
server.cpp
Go to the documentation of this file.
1// Copyright (c) 2010 Satoshi Nakamoto
2// Copyright (c) 2009-present 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 <rpc/server.h>
9
10#include <common/args.h>
11#include <common/system.h>
12#include <logging.h>
13#include <node/context.h>
14#include <rpc/protocol.h>
15#include <rpc/server_util.h>
16#include <rpc/util.h>
17#include <sync.h>
18#include <tinyformat.h>
19#include <util/check.h>
20#include <util/fs.h>
21#include <util/overloaded.h>
22#include <util/strencodings.h>
23#include <util/string.h>
24#include <util/time.h>
25
26#include <algorithm>
27#include <atomic>
28#include <cstddef>
29#include <exception>
30#include <list>
31#include <mutex>
32#include <optional>
33#include <set>
34#include <span>
35#include <string_view>
36#include <unordered_map>
37#include <unordered_set>
38#include <variant>
39
41
43static std::atomic<bool> g_rpc_running{false};
44static bool fRPCInWarmup GUARDED_BY(g_rpc_warmup_mutex) = true;
45static std::string rpcWarmupStatus GUARDED_BY(g_rpc_warmup_mutex) = "RPC server started";
46static bool ExecuteCommand(const CRPCCommand& command, const JSONRPCRequest& request, UniValue& result, bool last_handler);
47
49{
50 std::string method;
51 SteadyClock::time_point start;
52};
53
55{
57 std::list<RPCCommandExecutionInfo> active_commands GUARDED_BY(mutex);
58};
59
61
63{
64 std::list<RPCCommandExecutionInfo>::iterator it;
65 explicit RPCCommandExecution(const std::string& method)
66 {
68 it = g_rpc_server_info.active_commands.insert(g_rpc_server_info.active_commands.end(), {method, SteadyClock::now()});
69 }
71 {
73 g_rpc_server_info.active_commands.erase(it);
74 }
75};
76
77std::string CRPCTable::help(std::string_view strCommand, const JSONRPCRequest& helpreq) const
78{
79 std::string strRet;
80 std::string category;
81 std::set<intptr_t> setDone;
82 std::vector<std::pair<std::string, const CRPCCommand*> > vCommands;
83 vCommands.reserve(mapCommands.size());
84
85 for (const auto& entry : mapCommands)
86 vCommands.emplace_back(entry.second.front()->category + entry.first, entry.second.front());
87 std::ranges::sort(vCommands);
88
89 JSONRPCRequest jreq = helpreq;
91 jreq.params = UniValue();
92
93 for (const auto& [_, pcmd] : vCommands) {
94 std::string strMethod = pcmd->name;
95 if ((strCommand != "" || pcmd->category == "hidden") && strMethod != strCommand)
96 continue;
97 jreq.strMethod = strMethod;
98 try
99 {
100 UniValue unused_result;
101 if (setDone.insert(pcmd->unique_id).second)
102 pcmd->actor(jreq, unused_result, /*last_handler=*/true);
103 } catch (const HelpResult& e) {
104 std::string strHelp{e.what()};
105 if (strCommand == "")
106 {
107 if (strHelp.find('\n') != std::string::npos)
108 strHelp = strHelp.substr(0, strHelp.find('\n'));
109
110 if (category != pcmd->category)
111 {
112 if (!category.empty())
113 strRet += "\n";
114 category = pcmd->category;
115 strRet += "== " + Capitalize(category) + " ==\n";
116 }
117 }
118 strRet += strHelp + "\n";
119 }
120 }
121 if (strRet == "")
122 strRet = strprintf("help: unknown command: %s\n", strCommand);
123 strRet = strRet.substr(0,strRet.size()-1);
124 return strRet;
125}
126
128{
129 return RPCMethod{
130 "help",
131 "List all commands, or get help for a specified command.\n",
132 {
133 {"command", RPCArg::Type::STR, RPCArg::DefaultHint{"all commands"}, "The command to get help on"},
134 },
135 {
136 RPCResult{RPCResult::Type::STR, "", "The help text"},
137 RPCResult{RPCResult::Type::ANY, "", "The command conversions. (Hidden in dump_all_command_conversions)", /*inner=*/{},
140 }},
141 },
142 RPCExamples{""},
143 [](const RPCMethod& self, const JSONRPCRequest& jsonRequest) -> UniValue
144 {
145 auto command{self.MaybeArg<std::string_view>("command")};
146 if (command == "dump_all_command_conversions") {
147 // Used for testing only, undocumented
148 return tableRPC.dumpArgMap(jsonRequest);
149 }
150
151 return tableRPC.help(command.value_or(""), jsonRequest);
152 },
153 };
154}
155
157{
158 static const std::string RESULT{CLIENT_NAME " stopping"};
159 return RPCMethod{
160 "stop",
161 // Also accept the hidden 'wait' integer argument (milliseconds)
162 // For instance, 'stop 1000' makes the call wait 1 second before returning
163 // to the client (intended for testing)
164 "Request a graceful shutdown of " CLIENT_NAME ".",
165 {
166 {"wait", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "how long to wait in ms", RPCArgOptions{.hidden=true}},
167 },
168 RPCResult{RPCResult::Type::STR, "", "A string with the content '" + RESULT + "'"},
169 RPCExamples{""},
170 [](const RPCMethod& self, const JSONRPCRequest& jsonRequest) -> UniValue
171{
172 // Event loop will exit after current HTTP requests have been handled, so
173 // this reply will get back to the client.
174 CHECK_NONFATAL((CHECK_NONFATAL(EnsureAnyNodeContext(jsonRequest.context).shutdown_request))());
175 if (jsonRequest.params[0].isNum()) {
176 UninterruptibleSleep(std::chrono::milliseconds{jsonRequest.params[0].getInt<int>()});
177 }
178 return RESULT;
179},
180 };
181}
182
184{
185 return RPCMethod{
186 "uptime",
187 "Returns the total uptime of the server.\n",
188 {},
189 RPCResult{
190 RPCResult::Type::NUM, "", "The number of seconds that the server has been running"
191 },
193 HelpExampleCli("uptime", "")
194 + HelpExampleRpc("uptime", "")
195 },
196 [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
197{
198 return TicksSeconds(GetUptime());
199}
200 };
201}
202
204{
205 return RPCMethod{
206 "getrpcinfo",
207 "Returns details of the RPC server.\n",
208 {},
209 RPCResult{
210 RPCResult::Type::OBJ, "", "",
211 {
212 {RPCResult::Type::ARR, "active_commands", "All active commands",
213 {
214 {RPCResult::Type::OBJ, "", "Information about an active command",
215 {
216 {RPCResult::Type::STR, "method", "The name of the RPC command"},
217 {RPCResult::Type::NUM, "duration", "The running time in microseconds"},
218 }},
219 }},
220 {RPCResult::Type::STR, "logpath", "The complete file path to the debug log"},
221 }
222 },
224 HelpExampleCli("getrpcinfo", "")
225 + HelpExampleRpc("getrpcinfo", "")},
226 [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
227{
229 UniValue active_commands(UniValue::VARR);
230 for (const RPCCommandExecutionInfo& info : g_rpc_server_info.active_commands) {
232 entry.pushKV("method", info.method);
233 entry.pushKV("duration", Ticks<std::chrono::microseconds>(SteadyClock::now() - info.start));
234 active_commands.push_back(std::move(entry));
235 }
236
237 UniValue result(UniValue::VOBJ);
238 result.pushKV("active_commands", std::move(active_commands));
239
240 const std::string path = LogInstance().m_file_path.utf8string();
241 UniValue log_path(UniValue::VSTR, path);
242 result.pushKV("logpath", std::move(log_path));
243
244 return result;
245}
246 };
247}
248
249namespace {
250UniValue OpenRPCArgSchema(const RPCArg& arg, bool include_hidden, bool in_skip_type_check);
251UniValue OpenRPCResultSchema(const RPCResult& result);
252
253UniValue MakeObject(std::initializer_list<std::pair<std::string, UniValue>> entries)
254{
256 for (const auto& [key, value] : entries) {
257 obj.pushKV(key, value);
258 }
259 return obj;
260}
261
262void PushUniqueSchema(UniValue& schemas, std::unordered_set<std::string>& seen, UniValue schema)
263{
264 const std::string serialized{schema.write()};
265 if (seen.insert(serialized).second) schemas.push_back(std::move(schema));
266}
267
268// NOLINTNEXTLINE(misc-no-recursion)
269UniValue DedupArrayItemsSchema(std::span<const RPCArg> inner, bool include_hidden, bool in_skip_type_check)
270{
271 if (inner.empty()) return UniValue{UniValue::VOBJ};
272 if (inner.size() == 1) return OpenRPCArgSchema(inner.front(), include_hidden, in_skip_type_check);
273
274 UniValue one_of{UniValue::VARR};
275 std::unordered_set<std::string> seen;
276 for (const auto& item : inner) {
277 PushUniqueSchema(one_of, seen, OpenRPCArgSchema(item, include_hidden, in_skip_type_check));
278 }
279
280 if (one_of.size() == 1) return one_of[0];
281
283 items.pushKV(in_skip_type_check ? "anyOf" : "oneOf", std::move(one_of));
284 return items;
285}
286
287// NOLINTNEXTLINE(misc-no-recursion)
288UniValue DedupArrayItemsSchema(std::span<const RPCResult> inner)
289{
290 if (inner.empty()) return UniValue{UniValue::VOBJ};
291 if (inner.size() == 1) return OpenRPCResultSchema(inner.front());
292
293 UniValue one_of{UniValue::VARR};
294 std::unordered_set<std::string> seen;
295 for (const auto& item : inner) {
296 PushUniqueSchema(one_of, seen, OpenRPCResultSchema(item));
297 }
298
299 if (one_of.size() == 1) return one_of[0];
300
302 items.pushKV("oneOf", std::move(one_of));
303 return items;
304}
305
306void ApplyTypeStrOverride(UniValue& schema, const RPCArg& arg)
307{
308 if (arg.m_opts.type_str.size() != 2) return;
309 const std::string& type_label{arg.m_opts.type_str[1]};
310 if (type_label.empty()) return;
311
312 static const std::unordered_set<std::string> number_or_string{
313 "integer / string",
314 "string or numeric",
315 };
316 if (number_or_string.contains(type_label)) {
317 UniValue one_of{UniValue::VARR};
318 one_of.push_back(MakeObject({{"type", "integer"}}));
319 one_of.push_back(MakeObject({{"type", "string"}}));
320 schema = UniValue{UniValue::VOBJ};
321 schema.pushKV("oneOf", std::move(one_of));
322 } else {
323 schema.pushKV("x-bitcoin-type-override", type_label);
324 }
325}
326
327void ApplyArgFallback(UniValue& schema, const RPCArg& arg)
328{
329 std::visit(util::Overloaded{
330 [&](const RPCArg::Default& def) { schema.pushKV("default", def); },
331 [&](const RPCArg::DefaultHint& hint) { schema.pushKV("x-bitcoin-default-hint", hint); },
332 [](const RPCArg::Optional&) {},
333 },
334 arg.m_fallback);
335}
336
337// NOLINTNEXTLINE(misc-no-recursion)
338UniValue OpenRPCArgSchema(const RPCArg& arg, bool include_hidden, bool in_skip_type_check)
339{
340 UniValue schema{UniValue::VOBJ};
341 if (arg.m_opts.skip_type_check) {
342 ApplyTypeStrOverride(schema, arg);
343 if (schema.empty() && arg.m_type == RPCArg::Type::ARR) {
345 items.pushKV("type", "array");
346 items.pushKV("items", DedupArrayItemsSchema(arg.m_inner, include_hidden, /*in_skip_type_check=*/true));
347
348 UniValue one_of{UniValue::VARR};
349 one_of.push_back(std::move(items));
350 one_of.push_back(MakeObject({{"type", "object"}}));
351 schema.pushKV("oneOf", std::move(one_of));
352 }
353 ApplyArgFallback(schema, arg);
354 return schema;
355 }
356
357 switch (arg.m_type) {
359 schema = MakeObject({{"type", "string"}});
360 break;
362 schema = MakeObject({{"type", "string"}, {"pattern", "^[0-9a-fA-F]+$"}});
363 break;
365 schema = MakeObject({{"type", "number"}});
366 break;
368 schema = MakeObject({{"type", "boolean"}});
369 break;
371 UniValue one_of{UniValue::VARR};
372 one_of.push_back(MakeObject({{"type", "number"}}));
373 one_of.push_back(MakeObject({{"type", "string"}}));
374 schema.pushKV("oneOf", std::move(one_of));
375 break;
376 }
377 case RPCArg::Type::RANGE: {
379 items.push_back(MakeObject({{"type", "number"}}));
380 items.push_back(MakeObject({{"type", "number"}}));
381 UniValue range_schema{UniValue::VOBJ};
382 range_schema.pushKV("type", "array");
383 range_schema.pushKV("items", std::move(items));
384 range_schema.pushKV("additionalItems", false);
385 range_schema.pushKV("minItems", 2);
386 range_schema.pushKV("maxItems", 2);
387 UniValue one_of{UniValue::VARR};
388 one_of.push_back(MakeObject({{"type", "number"}}));
389 one_of.push_back(std::move(range_schema));
390 schema.pushKV("oneOf", std::move(one_of));
391 break;
392 }
393 case RPCArg::Type::ARR: {
394 UniValue items{DedupArrayItemsSchema(arg.m_inner, include_hidden, in_skip_type_check)};
395 schema.pushKV("type", "array");
396 schema.pushKV("items", std::move(items));
397 break;
398 }
401 UniValue properties{UniValue::VOBJ};
402 UniValue required{UniValue::VARR};
403 for (const auto& inner : arg.m_inner) {
404 if (!include_hidden && inner.m_opts.hidden) continue;
405 UniValue prop{OpenRPCArgSchema(inner, include_hidden, in_skip_type_check)};
406 if (!inner.m_description.empty()) prop.pushKV("description", inner.m_description);
407 if (inner.m_opts.placeholder) prop.pushKV("x-bitcoin-placeholder", true);
408 if (inner.m_opts.also_positional) prop.pushKV("x-bitcoin-also-positional", true);
409 properties.pushKV(inner.GetFirstName(), std::move(prop));
410 if (!inner.IsOptional()) required.push_back(inner.GetFirstName());
411 }
412 schema.pushKV("type", "object");
413 schema.pushKV("properties", std::move(properties));
414 schema.pushKV("additionalProperties", false);
415 if (!required.empty()) schema.pushKV("required", std::move(required));
416 break;
417 }
419 schema.pushKV("type", "object");
420 if (!arg.m_inner.empty()) {
421 schema.pushKV("additionalProperties", OpenRPCArgSchema(arg.m_inner[0], include_hidden, in_skip_type_check));
422 if (!arg.m_inner[0].m_description.empty()) {
423 schema.pushKV("description", arg.m_inner[0].m_description);
424 }
425 } else {
426 schema.pushKV("additionalProperties", true);
427 }
428 break;
429 }
430 } // no default case, so the compiler can warn about missing cases
431 ApplyTypeStrOverride(schema, arg);
432 ApplyArgFallback(schema, arg);
433 return schema;
434}
435
436// NOLINTNEXTLINE(misc-no-recursion)
437UniValue OpenRPCResultSchema(const RPCResult& result)
438{
439 if (result.m_opts.skip_type_check) {
440 RPCResultOptions opts{result.m_opts};
441 opts.skip_type_check = false;
442 if (result.m_type == RPCResult::Type::OBJ) {
443 UniValue obj_schema{OpenRPCResultSchema(RPCResult{result, std::move(opts)})};
444 if (result.m_key_name.empty()) return obj_schema;
445
446 UniValue one_of{UniValue::VARR};
447 one_of.push_back(std::move(obj_schema));
448 one_of.push_back(MakeObject({{"const", false}}));
449 UniValue schema{UniValue::VOBJ};
450 schema.pushKV("oneOf", std::move(one_of));
451 return schema;
452 }
453 if (result.m_type == RPCResult::Type::ARR) return OpenRPCResultSchema(RPCResult{result, std::move(opts)});
454 return UniValue{UniValue::VOBJ};
455 }
456
457 switch (result.m_type) {
459 return MakeObject({{"type", "string"}});
461 return MakeObject({{"type", "number"}, {"x-bitcoin-unit", "amount"}});
463 return MakeObject({{"type", "string"}, {"pattern", "^[0-9a-fA-F]+$"}});
465 return MakeObject({{"type", "number"}});
467 UniValue schema{UniValue::VOBJ};
468 schema.pushKV("type", "number");
469 schema.pushKV("x-bitcoin-unit", "unix-time");
470 return schema;
471 }
473 return MakeObject({{"type", "boolean"}});
475 return MakeObject({{"type", "null"}});
477 UniValue items{DedupArrayItemsSchema(result.m_inner)};
478 UniValue schema{UniValue::VOBJ};
479 schema.pushKV("type", "array");
480 schema.pushKV("items", std::move(items));
481 return schema;
482 }
485 for (const auto& inner : result.m_inner) {
486 items.push_back(OpenRPCResultSchema(inner));
487 }
488 UniValue schema{UniValue::VOBJ};
489 schema.pushKV("type", "array");
490 schema.pushKV("items", std::move(items));
491 schema.pushKV("additionalItems", false);
492 schema.pushKV("minItems", uint64_t(result.m_inner.size()));
493 schema.pushKV("maxItems", uint64_t(result.m_inner.size()));
494 return schema;
495 }
497 UniValue properties{UniValue::VOBJ};
498 UniValue required{UniValue::VARR};
499 for (const auto& inner : result.m_inner) {
500 if (inner.m_key_name.empty()) continue;
501 UniValue prop{OpenRPCResultSchema(inner)};
502 if (!inner.m_description.empty()) prop.pushKV("description", inner.m_description);
503 properties.pushKV(inner.m_key_name, std::move(prop));
504 if (!inner.m_optional) required.push_back(inner.m_key_name);
505 }
506 UniValue schema{UniValue::VOBJ};
507 schema.pushKV("type", "object");
508 schema.pushKV("properties", std::move(properties));
509 schema.pushKV("additionalProperties", false);
510 if (!required.empty()) schema.pushKV("required", std::move(required));
511 return schema;
512 }
514 UniValue schema{UniValue::VOBJ};
515 schema.pushKV("type", "object");
516 if (!result.m_inner.empty()) {
517 schema.pushKV("additionalProperties", OpenRPCResultSchema(result.m_inner[0]));
518 } else {
519 schema.pushKV("additionalProperties", UniValue{UniValue::VOBJ});
520 }
521 return schema;
522 }
524 return UniValue{UniValue::VOBJ};
525 } // no default case, so the compiler can warn about missing cases
527}
528} // namespace
529
531{
532 return RPCResult{
533 RPCResult::Type::OBJ, "", "",
534 {
535 {RPCResult::Type::STR, "openrpc", "OpenRPC specification version."},
536 {RPCResult::Type::OBJ, "info", "Metadata about this JSON-RPC interface.",
537 {
538 {RPCResult::Type::STR, "title", "API title."},
539 {RPCResult::Type::STR, "version", "Bitcoin Core version string."},
540 {RPCResult::Type::STR, "description", "API description."},
541 }},
542 {RPCResult::Type::ARR, "methods", "Documented RPC methods.",
543 {{RPCResult::Type::OBJ, "", "An RPC method description object.",
544 {
545 {RPCResult::Type::STR, "name", "Method name."},
546 {RPCResult::Type::STR, "description", "Method description."},
547 {RPCResult::Type::ARR, "params", "Method parameters.",
548 {{RPCResult::Type::OBJ, "", "A parameter.",
549 {
550 {RPCResult::Type::STR, "name", "Parameter name."},
551 {RPCResult::Type::BOOL, "required", "Whether the parameter is required."},
552 {RPCResult::Type::ANY, "schema", "JSON Schema for the parameter."},
553 {RPCResult::Type::STR, "description", /*optional=*/true, "Parameter description."},
554 {RPCResult::Type::ARR, "x-bitcoin-aliases", /*optional=*/true, "Alternative parameter names.",
555 {{RPCResult::Type::STR, "", "An alias."}}},
556 {RPCResult::Type::BOOL, "x-bitcoin-placeholder", /*optional=*/true, "Whether the parameter is retained only for compatibility."},
557 {RPCResult::Type::BOOL, "x-bitcoin-also-positional", /*optional=*/true, "Whether the parameter can also be passed positionally."},
558 }}}},
559 {RPCResult::Type::OBJ, "result", "Method result.",
560 {
561 {RPCResult::Type::STR, "name", "Result name."},
562 {RPCResult::Type::ANY, "schema", "JSON Schema for the result. Numeric schemas may include "
563 "\"x-bitcoin-unit\" property: \"amount\" which denotes a Bitcoin amount in BTC."},
564 }},
565 {RPCResult::Type::STR, "x-bitcoin-category", "RPC category."},
566 }}}},
567 },
568 {.skip_type_check = true}};
569}
570
572{
573 return RPCMethod{
574 "getopenrpcinfo",
575 "Returns an OpenRPC document for currently available RPC commands.\n",
576 {
577 {"show_hidden", RPCArg::Type::BOOL, RPCArg::Default{false}, "Also include hidden RPC commands and arguments."},
578 },
581 HelpExampleCli("getopenrpcinfo", "")
582 + HelpExampleRpc("getopenrpcinfo", "")
583 },
584 [](const RPCMethod& self, const JSONRPCRequest& request) -> UniValue
585 {
586 const bool include_hidden{self.Arg<bool>("show_hidden")};
587 return tableRPC.buildOpenRPCDoc(include_hidden);
588 },
589 };
590}
591
593{
594 return RPCMethod{
595 "rpc.discover",
596 "Returns an OpenRPC schema as a description of this service.\n",
597 {},
600 HelpExampleCli("rpc.discover", "")
601 + HelpExampleRpc("rpc.discover", "")
602 },
603 [](const RPCMethod&, const JSONRPCRequest&) -> UniValue
604 {
605 return tableRPC.buildOpenRPCDoc(/*include_hidden=*/false);
606 },
607 };
608}
609
611 /* Overall control/query calls */
612 {"control", &getopenrpcinfo},
613 {"control", &rpc_discover},
614 {"control", &getrpcinfo},
615 {"control", &help},
616 {"control", &stop},
617 {"control", &uptime},
618};
619
621{
622 for (const auto& c : vRPCCommands) {
623 appendCommand(c.name, &c);
624 }
625}
626
627void CRPCTable::appendCommand(const std::string& name, const CRPCCommand* pcmd)
628{
629 CHECK_NONFATAL(!IsRPCRunning()); // Only add commands before rpc is running
630
631 mapCommands[name].push_back(pcmd);
632}
633
634bool CRPCTable::removeCommand(const std::string& name, const CRPCCommand* pcmd)
635{
636 auto it = mapCommands.find(name);
637 if (it != mapCommands.end()) {
638 auto new_end = std::remove(it->second.begin(), it->second.end(), pcmd);
639 if (it->second.end() != new_end) {
640 it->second.erase(new_end, it->second.end());
641 if (it->second.empty()) {
642 mapCommands.erase(it);
643 }
644 return true;
645 }
646 }
647 return false;
648}
649
651{
652 LogDebug(BCLog::RPC, "Starting RPC\n");
653 g_rpc_running = true;
654}
655
657{
658 static std::once_flag g_rpc_interrupt_flag;
659 // This function could be called twice if the GUI has been started with -server=1.
660 std::call_once(g_rpc_interrupt_flag, []() {
661 LogDebug(BCLog::RPC, "Interrupting RPC\n");
662 // Interrupt e.g. running longpolls
663 g_rpc_running = false;
664 });
665}
666
668{
669 static std::once_flag g_rpc_stop_flag;
670 // This function could be called twice if the GUI has been started with -server=1.
672 std::call_once(g_rpc_stop_flag, [&]() {
673 LogDebug(BCLog::RPC, "Stopping RPC\n");
675 LogDebug(BCLog::RPC, "RPC stopped.\n");
676 });
677}
678
680{
681 return g_rpc_running;
682}
683
685{
686 if (!IsRPCRunning()) throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Shutting down");
687}
688
689void SetRPCWarmupStatus(const std::string& newStatus)
690{
692 rpcWarmupStatus = newStatus;
693}
694
696{
698 fRPCInWarmup = true;
699}
700
702{
704 assert(fRPCInWarmup);
705 fRPCInWarmup = false;
706}
707
708bool RPCIsInWarmup(std::string *outStatus)
709{
711 if (outStatus)
712 *outStatus = rpcWarmupStatus;
713 return fRPCInWarmup;
714}
715
716bool IsDeprecatedRPCEnabled(const std::string& method)
717{
718 const std::vector<std::string> enabled_methods = gArgs.GetArgs("-deprecatedrpc");
719
720 return find(enabled_methods.begin(), enabled_methods.end(), method) != enabled_methods.end();
721}
722
723UniValue JSONRPCExec(const JSONRPCRequest& jreq, bool catch_errors)
724{
725 UniValue result;
726 if (catch_errors) {
727 try {
728 result = tableRPC.execute(jreq);
729 } catch (UniValue& e) {
730 return JSONRPCReplyObj(NullUniValue, std::move(e), jreq.id, jreq.m_json_version);
731 } catch (const std::exception& e) {
733 }
734 } else {
735 result = tableRPC.execute(jreq);
736 }
737
738 return JSONRPCReplyObj(std::move(result), NullUniValue, jreq.id, jreq.m_json_version);
739}
740
745static inline JSONRPCRequest transformNamedArguments(const JSONRPCRequest& in, const std::vector<std::pair<std::string, bool>>& argNames)
746{
747 JSONRPCRequest out = in;
748 out.params = UniValue(UniValue::VARR);
749 // Build a map of parameters, and remove ones that have been processed, so that we can throw a focused error if
750 // there is an unknown one.
751 const std::vector<std::string>& keys = in.params.getKeys();
752 const std::vector<UniValue>& values = in.params.getValues();
753 std::unordered_map<std::string, const UniValue*> argsIn;
754 for (size_t i=0; i<keys.size(); ++i) {
755 auto [_, inserted] = argsIn.emplace(keys[i], &values[i]);
756 if (!inserted) {
757 throw JSONRPCError(RPC_INVALID_PARAMETER, "Parameter " + keys[i] + " specified multiple times");
758 }
759 }
760 // Process expected parameters. If any parameters were left unspecified in
761 // the request before a parameter that was specified, null values need to be
762 // inserted at the unspecified parameter positions, and the "hole" variable
763 // below tracks the number of null values that need to be inserted.
764 // The "initial_hole_size" variable stores the size of the initial hole,
765 // i.e. how many initial positional arguments were left unspecified. This is
766 // used after the for-loop to add initial positional arguments from the
767 // "args" parameter, if present.
768 int hole = 0;
769 int initial_hole_size = 0;
770 const std::string* initial_param = nullptr;
771 UniValue options{UniValue::VOBJ};
772 for (const auto& [argNamePattern, named_only]: argNames) {
773 std::vector<std::string> vargNames = SplitString(argNamePattern, '|');
774 auto fr = argsIn.end();
775 for (const std::string & argName : vargNames) {
776 fr = argsIn.find(argName);
777 if (fr != argsIn.end()) {
778 break;
779 }
780 }
781
782 // Handle named-only parameters by pushing them into a temporary options
783 // object, and then pushing the accumulated options as the next
784 // positional argument.
785 if (named_only) {
786 if (fr != argsIn.end()) {
787 if (options.exists(fr->first)) {
788 throw JSONRPCError(RPC_INVALID_PARAMETER, "Parameter " + fr->first + " specified multiple times");
789 }
790 options.pushKVEnd(fr->first, *fr->second);
791 argsIn.erase(fr);
792 }
793 continue;
794 }
795
796 if (!options.empty() || fr != argsIn.end()) {
797 for (int i = 0; i < hole; ++i) {
798 // Fill hole between specified parameters with JSON nulls,
799 // but not at the end (for backwards compatibility with calls
800 // that act based on number of specified parameters).
801 out.params.push_back(UniValue());
802 }
803 hole = 0;
804 if (!initial_param) initial_param = &argNamePattern;
805 } else {
806 hole += 1;
807 if (out.params.empty()) initial_hole_size = hole;
808 }
809
810 // If named input parameter "fr" is present, push it onto out.params. If
811 // options are present, push them onto out.params. If both are present,
812 // throw an error.
813 if (fr != argsIn.end()) {
814 if (!options.empty()) {
815 throw JSONRPCError(RPC_INVALID_PARAMETER, "Parameter " + fr->first + " conflicts with parameter " + options.getKeys().front());
816 }
817 out.params.push_back(*fr->second);
818 argsIn.erase(fr);
819 }
820 if (!options.empty()) {
821 out.params.push_back(std::move(options));
822 options = UniValue{UniValue::VOBJ};
823 }
824 }
825 // If leftover "args" param was found, use it as a source of positional
826 // arguments and add named arguments after. This is a convenience for
827 // clients that want to pass a combination of named and positional
828 // arguments as described in doc/JSON-RPC-interface.md#parameter-passing
829 auto positional_args{argsIn.extract("args")};
830 if (positional_args && positional_args.mapped()->isArray()) {
831 if (initial_hole_size < (int)positional_args.mapped()->size() && initial_param) {
832 throw JSONRPCError(RPC_INVALID_PARAMETER, "Parameter " + *initial_param + " specified twice both as positional and named argument");
833 }
834 // Assign positional_args to out.params and append named_args after.
835 UniValue named_args{std::move(out.params)};
836 out.params = *positional_args.mapped();
837 for (size_t i{out.params.size()}; i < named_args.size(); ++i) {
838 out.params.push_back(named_args[i]);
839 }
840 }
841 // If there are still arguments in the argsIn map, this is an error.
842 if (!argsIn.empty()) {
843 throw JSONRPCError(RPC_INVALID_PARAMETER, "Unknown named parameter " + argsIn.begin()->first);
844 }
845 // Return request with named arguments transformed to positional arguments
846 return out;
847}
848
849static bool ExecuteCommands(const std::vector<const CRPCCommand*>& commands, const JSONRPCRequest& request, UniValue& result)
850{
851 for (const auto& command : commands) {
852 if (ExecuteCommand(*command, request, result, &command == &commands.back())) {
853 return true;
854 }
855 }
856 return false;
857}
858
860{
861 // Return immediately if in warmup
862 {
864 if (fRPCInWarmup)
865 throw JSONRPCError(RPC_IN_WARMUP, rpcWarmupStatus);
866 }
867
868 // Find method
869 auto it = mapCommands.find(request.strMethod);
870 if (it != mapCommands.end()) {
871 UniValue result;
872 if (ExecuteCommands(it->second, request, result)) {
873 return result;
874 }
875 }
876 throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found");
877}
878
879static bool ExecuteCommand(const CRPCCommand& command, const JSONRPCRequest& request, UniValue& result, bool last_handler)
880{
881 try {
882 RPCCommandExecution execution(request.strMethod);
883 // Execute, convert arguments to array if necessary
884 if (request.params.isObject()) {
885 return command.actor(transformNamedArguments(request, command.argNames), result, last_handler);
886 } else {
887 return command.actor(request, result, last_handler);
888 }
889 } catch (const UniValue::type_error& e) {
890 throw JSONRPCError(RPC_TYPE_ERROR, e.what());
891 } catch (const std::exception& e) {
892 throw JSONRPCError(RPC_MISC_ERROR, e.what());
893 }
894}
895
896std::vector<std::string> CRPCTable::listCommands() const
897{
898 std::vector<std::string> commandList;
899 commandList.reserve(mapCommands.size());
900 for (const auto& i : mapCommands) commandList.emplace_back(i.first);
901 return commandList;
902}
903
904UniValue CRPCTable::buildOpenRPCDoc(bool include_hidden) const
905{
906 std::vector<std::string> method_names;
907 for (const auto& [name, cmds] : mapCommands) {
908 if (cmds.empty()) continue;
909 const CRPCCommand* cmd{cmds.front()};
910 if ((!include_hidden && cmd->category == "hidden") || !cmd->metadata_fn) continue;
911 method_names.push_back(name);
912 }
913 std::sort(method_names.begin(), method_names.end());
914
915 UniValue methods{UniValue::VARR};
916 for (const auto& method_name : method_names) {
917 const CRPCCommand* cmd{mapCommands.at(method_name).front()};
918 RPCMethod helpman{cmd->metadata_fn()};
919
920 UniValue params{UniValue::VARR};
921 for (const auto& arg : helpman.GetArgs()) {
922 if (!include_hidden && arg.m_opts.hidden) continue;
924 param.pushKV("name", arg.GetFirstName());
925 param.pushKV("required", !arg.IsOptional());
926 param.pushKV("schema", OpenRPCArgSchema(arg, include_hidden, /*in_skip_type_check=*/false));
927
928 std::vector<std::string> names{SplitString(arg.m_names, '|')};
929 if (names.size() > 1) {
930 UniValue aliases{UniValue::VARR};
931 for (size_t i{1}; i < names.size(); ++i) aliases.push_back(names[i]);
932 param.pushKV("x-bitcoin-aliases", std::move(aliases));
933 }
934 if (arg.m_opts.placeholder) param.pushKV("x-bitcoin-placeholder", true);
935 if (arg.m_opts.also_positional) param.pushKV("x-bitcoin-also-positional", true);
936 if (!arg.m_description.empty()) param.pushKV("description", arg.m_description);
937 params.push_back(std::move(param));
938 }
939
940 UniValue result_schema{UniValue::VOBJ};
941 const auto& results{helpman.GetResults().m_results};
942 if (results.size() == 1 && results[0].m_type != RPCResult::Type::ANY) {
943 result_schema = OpenRPCResultSchema(results[0]);
944 } else if (results.size() > 1) {
945 UniValue one_of{UniValue::VARR};
946 for (const auto& r : results) {
947 if (r.m_type == RPCResult::Type::ANY) continue;
948 UniValue schema{OpenRPCResultSchema(r)};
949 if (!r.m_cond.empty()) schema.pushKV("description", r.m_cond);
950 one_of.push_back(std::move(schema));
951 }
952 if (one_of.size() == 1) {
953 result_schema = one_of[0];
954 } else if (one_of.size() > 1) {
955 result_schema.pushKV("oneOf", std::move(one_of));
956 }
957 }
958
959 UniValue method{UniValue::VOBJ};
960 method.pushKV("name", method_name);
961 method.pushKV("description", util::TrimString(helpman.GetDescription()));
962 method.pushKV("params", std::move(params));
963 UniValue result{UniValue::VOBJ};
964 result.pushKV("name", "result");
965 result.pushKV("schema", std::move(result_schema));
966 method.pushKV("result", std::move(result));
967 method.pushKV("x-bitcoin-category", cmd->category);
968 methods.push_back(std::move(method));
969 }
970
971 std::string version{"v" CLIENT_VERSION_STRING};
972 if (!CLIENT_VERSION_IS_RELEASE) version += "-dev";
973
975 info.pushKV("title", CLIENT_NAME " JSON-RPC");
976 info.pushKV("version", version);
977 info.pushKV("description", "Autogenerated from " CLIENT_NAME " RPC metadata.");
978
980 doc.pushKV("openrpc", "1.4.1");
981 doc.pushKV("info", std::move(info));
982 doc.pushKV("methods", std::move(methods));
983 return doc;
984}
985
987{
988 JSONRPCRequest request = args_request;
990
992 for (const auto& cmd : mapCommands) {
993 UniValue result;
994 if (ExecuteCommands(cmd.second, request, result)) {
995 for (const auto& values : result.getValues()) {
996 ret.push_back(values);
997 }
998 }
999 }
1000 return ret;
1001}
1002
ArgsManager gArgs
Definition: args.cpp:38
int ret
const auto cmd
const auto command
#define CHECK_NONFATAL(condition)
Identity function.
Definition: check.h:112
#define NONFATAL_UNREACHABLE()
NONFATAL_UNREACHABLE() is a macro that is used to mark unreachable code.
Definition: check.h:133
std::vector< std::string > GetArgs(const std::string &strArg) const EXCLUSIVE_LOCKS_REQUIRED(!cs_args)
Return a vector of strings of the given argument.
Definition: args.cpp:422
fs::path m_file_path
Definition: logging.h:177
RPC command dispatcher.
Definition: server.h:89
CRPCTable()
Definition: server.cpp:620
std::map< std::string, std::vector< const CRPCCommand * > > mapCommands
Definition: server.h:91
bool removeCommand(const std::string &name, const CRPCCommand *pcmd)
Definition: server.cpp:634
std::string help(std::string_view name, const JSONRPCRequest &helpreq) const
Definition: server.cpp:77
std::vector< std::string > listCommands() const
Returns a list of registered commands.
Definition: server.cpp:896
UniValue buildOpenRPCDoc(bool include_hidden=false) const
Return a complete OpenRPC 1.4.1 document for registered commands.
Definition: server.cpp:904
UniValue execute(const JSONRPCRequest &request) const
Execute a method.
Definition: server.cpp:859
void appendCommand(const std::string &name, const CRPCCommand *pcmd)
Appends a CRPCCommand to the dispatch table.
Definition: server.cpp:627
UniValue dumpArgMap(const JSONRPCRequest &request) const
Return all named arguments that need to be converted by the client from string to another JSON type.
Definition: server.cpp:986
Different type to mark Mutex at global scope.
Definition: sync.h:142
UniValue params
Definition: request.h:59
std::string strMethod
Definition: request.h:58
JSONRPCVersion m_json_version
Definition: request.h:65
enum JSONRPCRequest::Mode mode
std::optional< UniValue > id
Definition: request.h:57
auto MaybeArg(std::string_view key) const
Helper to get an optional request argument.
Definition: util.h:502
auto Arg(std::string_view key) const
Helper to get a required or default-valued request argument.
Definition: util.h:470
void push_back(UniValue val)
Definition: univalue.cpp:103
@ VOBJ
Definition: univalue.h:24
@ VSTR
Definition: univalue.h:24
@ VARR
Definition: univalue.h:24
std::string write(unsigned int prettyIndent=0, unsigned int indentLevel=0) const
const std::vector< UniValue > & getValues() const
const std::vector< std::string > & getKeys() const
bool empty() const
Definition: univalue.h:69
void pushKV(std::string key, UniValue val)
Definition: univalue.cpp:125
bool isObject() const
Definition: univalue.h:88
SteadyClock::duration GetUptime()
Monotonic uptime (not affected by system time changes).
Definition: system.cpp:125
#define LogDebug(category,...)
Definition: log.h:143
BCLog::Logger & LogInstance()
Definition: logging.cpp:26
@ RPC
Definition: categories.h:23
std::vector< std::string > SplitString(std::string_view str, char sep)
Definition: string.h:153
std::string TrimString(std::string_view str, std::string_view pattern=" \f\n\r\t\v")
Definition: string.h:173
UniValue JSONRPCError(int code, const std::string &message)
Definition: request.cpp:75
UniValue JSONRPCReplyObj(UniValue result, UniValue error, std::optional< UniValue > id, JSONRPCVersion jsonrpc_version)
Definition: request.cpp:56
void DeleteAuthCookie()
Delete RPC authentication cookie from disk.
Definition: request.cpp:169
const char * name
Definition: rest.cpp:71
@ RPC_MISC_ERROR
General application defined errors.
Definition: protocol.h:65
@ RPC_METHOD_NOT_FOUND
Definition: protocol.h:57
@ RPC_TYPE_ERROR
Unexpected type was passed as parameter.
Definition: protocol.h:66
@ RPC_CLIENT_NOT_CONNECTED
P2P client errors.
Definition: protocol.h:84
@ RPC_INVALID_PARAMETER
Invalid, missing or duplicate parameter.
Definition: protocol.h:69
@ RPC_IN_WARMUP
Client still warming up.
Definition: protocol.h:75
std::string HelpExampleCli(const std::string &methodname, const std::string &args)
Definition: util.cpp:188
std::string HelpExampleRpc(const std::string &methodname, const std::string &args)
Definition: util.cpp:206
static const int64_t values[]
A selection of numbers that do not trigger int64_t overflow when added/subtracted.
bool IsDeprecatedRPCEnabled(const std::string &method)
Definition: server.cpp:716
void SetRPCWarmupFinished()
Definition: server.cpp:701
static RPCMethod stop()
Definition: server.cpp:156
void StartRPC()
Definition: server.cpp:650
static bool ExecuteCommands(const std::vector< const CRPCCommand * > &commands, const JSONRPCRequest &request, UniValue &result)
Definition: server.cpp:849
static RPCResult OpenRPCDocResult()
Definition: server.cpp:530
bool RPCIsInWarmup(std::string *outStatus)
Definition: server.cpp:708
static bool ExecuteCommand(const CRPCCommand &command, const JSONRPCRequest &request, UniValue &result, bool last_handler)
Definition: server.cpp:879
void StopRPC()
Definition: server.cpp:667
static std::atomic< bool > g_rpc_running
Definition: server.cpp:43
static JSONRPCRequest transformNamedArguments(const JSONRPCRequest &in, const std::vector< std::pair< std::string, bool > > &argNames)
Process named arguments into a vector of positional arguments, based on the passed-in specification f...
Definition: server.cpp:745
bool IsRPCRunning()
Query whether RPC is running.
Definition: server.cpp:679
void SetRPCWarmupStarting()
Definition: server.cpp:695
static RPCMethod help()
Definition: server.cpp:127
void InterruptRPC()
Definition: server.cpp:656
static RPCMethod getrpcinfo()
Definition: server.cpp:203
UniValue JSONRPCExec(const JSONRPCRequest &jreq, bool catch_errors)
Definition: server.cpp:723
static RPCMethod getopenrpcinfo()
Definition: server.cpp:571
static bool fRPCInWarmup GUARDED_BY(g_rpc_warmup_mutex)
static GlobalMutex g_rpc_warmup_mutex
Definition: server.cpp:42
static RPCMethod uptime()
Definition: server.cpp:183
static RPCServerInfo g_rpc_server_info
Definition: server.cpp:60
static RPCMethod rpc_discover()
Definition: server.cpp:592
static const CRPCCommand vRPCCommands[]
Definition: server.cpp:610
void SetRPCWarmupStatus(const std::string &newStatus)
Set the RPC warmup status.
Definition: server.cpp:689
CRPCTable tableRPC
Definition: server.cpp:1003
void RpcInterruptionPoint()
Throw JSONRPCError if RPC is not running.
Definition: server.cpp:684
NodeContext & EnsureAnyNodeContext(const std::any &context)
Definition: server_util.cpp:28
field hidden from help
Definition: util.h:298
Definition: util.h:186
@ RANGE
Special type that is a NUM or [NUM,NUM].
@ OBJ_USER_KEYS
Special type where the user must set the keys e.g. to define multiple addresses; as opposed to e....
@ STR_HEX
Special type that is a STR with only hex chars.
@ AMOUNT
Special type representing a floating point amount (can be either NUM or STR)
@ OBJ_NAMED_PARAMS
Special type that behaves almost exactly like OBJ, defining an options object with a list of pre-defi...
const std::vector< RPCArg > m_inner
Only used for arrays or dicts.
Definition: util.h:227
const RPCArgOptions m_opts
Definition: util.h:230
const std::string m_names
The name of the arg (can be empty for inner args, can contain multiple aliases separated by | for nam...
Definition: util.h:225
const Fallback m_fallback
Definition: util.h:228
const std::string m_description
Definition: util.h:229
std::string DefaultHint
Hint for default value.
Definition: util.h:220
bool IsOptional() const
Definition: util.cpp:932
const Type m_type
Definition: util.h:226
std::string GetFirstName() const
Return the first of all aliases.
Definition: util.cpp:921
Optional
Definition: util.h:206
@ OMITTED
Optional argument for which the default value is omitted from help text for one of two reasons:
bool hidden
For testing only.
Definition: util.h:173
std::vector< std::string > type_str
Should be empty unless it is supposed to override the auto-generated type strings....
Definition: util.h:171
bool also_positional
If set allows a named-parameter field in an OBJ_NAMED_PARAM options object to have the same name as a...
Definition: util.h:174
bool placeholder
If set, the argument is retained only for compatibility and should generally be omitted.
Definition: util.h:172
bool skip_type_check
Definition: util.h:169
RPCCommandExecution(const std::string &method)
Definition: server.cpp:65
std::list< RPCCommandExecutionInfo >::iterator it
Definition: server.cpp:64
SteadyClock::time_point start
Definition: server.cpp:51
std::string method
Definition: server.cpp:50
@ NUM_TIME
Special numeric to denote unix epoch time.
@ ANY
Special type to disable type checks.
@ ARR_FIXED
Special array that has a fixed number of entries.
@ OBJ_DYN
Special dictionary with keys that are not literals.
@ STR_HEX
Special string with only hex chars.
@ STR_AMOUNT
Special string to represent a floating point amount.
const std::vector< RPCResult > m_inner
Only used for arrays or dicts.
Definition: util.h:325
const RPCResultOptions m_opts
Definition: util.h:327
const std::string m_key_name
Only used for dicts.
Definition: util.h:324
const Type m_type
Definition: util.h:323
bool skip_type_check
Definition: util.h:302
HelpElision print_elision
Definition: util.h:303
std::list< RPCCommandExecutionInfo > active_commands GUARDED_BY(mutex)
Mutex mutex
Definition: server.cpp:56
Overloaded helper for std::visit.
Definition: overloaded.h:16
#define LOCK(cs)
Definition: sync.h:268
std::vector< uint16_t > keys
Definition: dbwrapper.cpp:376
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1172
consteval auto _(util::TranslatedLiteral str)
Definition: translation.h:79
const UniValue NullUniValue
Definition: univalue.cpp:15
std::string Capitalize(std::string str)
Capitalizes the first character of the given string.
void UninterruptibleSleep(const std::chrono::microseconds &n)
Definition: time.cpp:30
constexpr int64_t TicksSeconds(Duration d)
Definition: time.h:88
assert(!tx.IsCoinBase())