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