6#include <bitcoin-build-config.h>
23#include <validation.h>
32#include <unordered_map>
33#include <unordered_set>
60 std::list<RPCCommandExecutionInfo>::iterator
it;
77 std::set<intptr_t> setDone;
78 std::vector<std::pair<std::string, const CRPCCommand*> > vCommands;
82 vCommands.emplace_back(entry.second.front()->category + entry.first, entry.second.front());
83 std::ranges::sort(vCommands);
89 for (
const auto& [
_, pcmd] : vCommands) {
90 std::string strMethod = pcmd->name;
91 if ((strCommand !=
"" || pcmd->category ==
"hidden") && strMethod != strCommand)
97 if (setDone.insert(pcmd->unique_id).second)
98 pcmd->actor(jreq, unused_result,
true);
100 std::string strHelp{e.what()};
101 if (strCommand ==
"")
103 if (strHelp.find(
'\n') != std::string::npos)
104 strHelp = strHelp.substr(0, strHelp.find(
'\n'));
106 if (category != pcmd->category)
108 if (!category.empty())
110 category = pcmd->category;
111 strRet +=
"== " +
Capitalize(category) +
" ==\n";
114 strRet += strHelp +
"\n";
118 strRet =
strprintf(
"help: unknown command: %s\n", strCommand);
119 strRet = strRet.substr(0,strRet.size()-1);
127 "List all commands, or get help for a specified command.\n",
142 if (
command ==
"dump_all_command_conversions") {
154 static const std::string RESULT{CLIENT_NAME
" stopping"};
160 "Request a graceful shutdown of " CLIENT_NAME
".",
171 if (jsonRequest.params[0].isNum()) {
183 "Returns the total uptime of the server.\n",
203 "Returns details of the RPC server.\n",
229 entry.
pushKV(
"duration", Ticks<std::chrono::microseconds>(SteadyClock::now() - info.
start));
230 active_commands.
push_back(std::move(entry));
234 result.
pushKV(
"active_commands", std::move(active_commands));
238 result.
pushKV(
"logpath", std::move(log_path));
246UniValue OpenRPCArgSchema(
const RPCArg& arg,
bool include_hidden,
bool in_skip_type_check);
249UniValue MakeObject(std::initializer_list<std::pair<std::string, UniValue>> entries)
252 for (
const auto& [key, value] : entries) {
253 obj.pushKV(key, value);
258void PushUniqueSchema(
UniValue& schemas, std::unordered_set<std::string>& seen,
UniValue schema)
260 const std::string serialized{schema.
write()};
261 if (seen.insert(serialized).second) schemas.
push_back(std::move(schema));
265UniValue DedupArrayItemsSchema(std::span<const RPCArg> inner,
bool include_hidden,
bool in_skip_type_check)
268 if (inner.size() == 1)
return OpenRPCArgSchema(inner.front(), include_hidden, in_skip_type_check);
271 std::unordered_set<std::string> seen;
272 for (
const auto& item : inner) {
273 PushUniqueSchema(one_of, seen, OpenRPCArgSchema(item, include_hidden, in_skip_type_check));
276 if (one_of.size() == 1)
return one_of[0];
279 items.pushKV(in_skip_type_check ?
"anyOf" :
"oneOf", std::move(one_of));
284UniValue DedupArrayItemsSchema(std::span<const RPCResult> inner)
287 if (inner.size() == 1)
return OpenRPCResultSchema(inner.front());
290 std::unordered_set<std::string> seen;
291 for (
const auto& item : inner) {
292 PushUniqueSchema(one_of, seen, OpenRPCResultSchema(item));
295 if (one_of.size() == 1)
return one_of[0];
298 items.pushKV(
"oneOf", std::move(one_of));
306 if (type_label.empty())
return;
308 static const std::unordered_set<std::string> number_or_string{
312 if (number_or_string.contains(type_label)) {
314 one_of.push_back(MakeObject({{
"type",
"integer"}}));
315 one_of.push_back(MakeObject({{
"type",
"string"}}));
317 schema.
pushKV(
"oneOf", std::move(one_of));
319 schema.
pushKV(
"x-bitcoin-type-override", type_label);
334UniValue OpenRPCArgSchema(
const RPCArg& arg,
bool include_hidden,
bool in_skip_type_check)
338 ApplyTypeStrOverride(schema, arg);
341 items.pushKV(
"type",
"array");
342 items.pushKV(
"items", DedupArrayItemsSchema(arg.
m_inner, include_hidden,
true));
345 one_of.push_back(std::move(items));
346 one_of.push_back(MakeObject({{
"type",
"object"}}));
347 schema.
pushKV(
"oneOf", std::move(one_of));
349 ApplyArgFallback(schema, arg);
355 schema = MakeObject({{
"type",
"string"}});
358 schema = MakeObject({{
"type",
"string"}, {
"pattern",
"^[0-9a-fA-F]+$"}});
361 schema = MakeObject({{
"type",
"number"}});
364 schema = MakeObject({{
"type",
"boolean"}});
368 one_of.push_back(MakeObject({{
"type",
"number"}}));
369 one_of.push_back(MakeObject({{
"type",
"string"}}));
370 schema.
pushKV(
"oneOf", std::move(one_of));
375 items.push_back(MakeObject({{
"type",
"number"}}));
376 items.push_back(MakeObject({{
"type",
"number"}}));
378 range_schema.pushKV(
"type",
"array");
379 range_schema.pushKV(
"items", std::move(items));
380 range_schema.pushKV(
"additionalItems",
false);
381 range_schema.pushKV(
"minItems", 2);
382 range_schema.pushKV(
"maxItems", 2);
384 one_of.push_back(MakeObject({{
"type",
"number"}}));
385 one_of.push_back(std::move(range_schema));
386 schema.
pushKV(
"oneOf", std::move(one_of));
390 UniValue items{DedupArrayItemsSchema(arg.
m_inner, include_hidden, in_skip_type_check)};
391 schema.
pushKV(
"type",
"array");
392 schema.
pushKV(
"items", std::move(items));
399 for (
const auto& inner : arg.
m_inner) {
400 if (!include_hidden && inner.m_opts.hidden)
continue;
401 UniValue prop{OpenRPCArgSchema(inner, include_hidden, in_skip_type_check)};
402 if (!inner.m_description.empty()) prop.
pushKV(
"description", inner.m_description);
403 if (inner.m_opts.placeholder) prop.pushKV(
"x-bitcoin-placeholder",
true);
404 if (inner.m_opts.also_positional) prop.pushKV(
"x-bitcoin-also-positional",
true);
405 properties.pushKV(inner.GetFirstName(), std::move(prop));
406 if (!inner.IsOptional()) required.push_back(inner.GetFirstName());
408 schema.
pushKV(
"type",
"object");
409 schema.
pushKV(
"properties", std::move(properties));
410 schema.
pushKV(
"additionalProperties",
false);
411 if (!required.empty()) schema.
pushKV(
"required", std::move(required));
415 schema.
pushKV(
"type",
"object");
417 schema.
pushKV(
"additionalProperties", OpenRPCArgSchema(arg.
m_inner[0], include_hidden, in_skip_type_check));
418 if (!arg.
m_inner[0].m_description.empty()) {
422 schema.
pushKV(
"additionalProperties",
true);
427 ApplyTypeStrOverride(schema, arg);
428 ApplyArgFallback(schema, arg);
440 if (result.
m_key_name.empty())
return obj_schema;
443 one_of.push_back(std::move(obj_schema));
444 one_of.push_back(MakeObject({{
"const",
false}}));
446 schema.
pushKV(
"oneOf", std::move(one_of));
455 return MakeObject({{
"type",
"string"}});
457 return MakeObject({{
"type",
"number"}, {
"x-bitcoin-unit",
"amount"}});
459 return MakeObject({{
"type",
"string"}, {
"pattern",
"^[0-9a-fA-F]+$"}});
461 return MakeObject({{
"type",
"number"}});
464 schema.
pushKV(
"type",
"number");
465 schema.
pushKV(
"x-bitcoin-unit",
"unix-time");
469 return MakeObject({{
"type",
"boolean"}});
471 return MakeObject({{
"type",
"null"}});
475 schema.
pushKV(
"type",
"array");
476 schema.
pushKV(
"items", std::move(items));
481 for (
const auto& inner : result.
m_inner) {
482 items.push_back(OpenRPCResultSchema(inner));
485 schema.
pushKV(
"type",
"array");
486 schema.
pushKV(
"items", std::move(items));
487 schema.
pushKV(
"additionalItems",
false);
495 for (
const auto& inner : result.
m_inner) {
496 if (inner.m_key_name.empty())
continue;
497 UniValue prop{OpenRPCResultSchema(inner)};
498 if (!inner.m_description.empty()) prop.
pushKV(
"description", inner.m_description);
499 properties.pushKV(inner.m_key_name, std::move(prop));
500 if (!inner.m_optional) required.push_back(inner.m_key_name);
503 schema.
pushKV(
"type",
"object");
504 schema.
pushKV(
"properties", std::move(properties));
505 schema.
pushKV(
"additionalProperties",
false);
506 if (!required.empty()) schema.
pushKV(
"required", std::move(required));
511 schema.
pushKV(
"type",
"object");
513 schema.
pushKV(
"additionalProperties", OpenRPCResultSchema(result.
m_inner[0]));
552 {
RPCResult::Type::BOOL,
"x-bitcoin-placeholder",
true,
"Whether the parameter is retained only for compatibility."},
553 {
RPCResult::Type::BOOL,
"x-bitcoin-also-positional",
true,
"Whether the parameter can also be passed positionally."},
563 {.skip_type_check =
true}};
570 "Returns an OpenRPC document for currently available RPC commands.\n",
581 const bool include_hidden{self.
Arg<
bool>(
"show_hidden")};
591 "Returns an OpenRPC schema as a description of this service.\n",
633 auto new_end = std::remove(it->second.begin(), it->second.end(), pcmd);
634 if (it->second.end() != new_end) {
635 it->second.erase(new_end, it->second.end());
636 if (it->second.empty()) {
653 static std::once_flag g_rpc_interrupt_flag;
655 std::call_once(g_rpc_interrupt_flag, []() {
664 static std::once_flag g_rpc_stop_flag;
667 std::call_once(g_rpc_stop_flag, [&]() {
687 rpcWarmupStatus = newStatus;
700 fRPCInWarmup =
false;
707 *outStatus = rpcWarmupStatus;
713 const std::vector<std::string> enabled_methods =
gArgs.
GetArgs(
"-deprecatedrpc");
715 return find(enabled_methods.begin(), enabled_methods.end(), method) != enabled_methods.end();
726 }
catch (
const std::exception& e) {
748 std::unordered_map<std::string, const UniValue*> argsIn;
749 for (
size_t i=0; i<
keys.size(); ++i) {
750 auto [
_, inserted] = argsIn.emplace(
keys[i], &
values[i]);
764 int initial_hole_size = 0;
765 const std::string* initial_param =
nullptr;
767 for (
const auto& [argNamePattern, named_only]: argNames) {
768 std::vector<std::string> vargNames =
SplitString(argNamePattern,
'|');
769 auto fr = argsIn.end();
770 for (
const std::string & argName : vargNames) {
771 fr = argsIn.find(argName);
772 if (fr != argsIn.end()) {
781 if (fr != argsIn.end()) {
782 if (options.exists(fr->first)) {
785 options.pushKVEnd(fr->first, *fr->second);
791 if (!options.empty() || fr != argsIn.end()) {
792 for (
int i = 0; i < hole; ++i) {
799 if (!initial_param) initial_param = &argNamePattern;
802 if (
out.params.empty()) initial_hole_size = hole;
808 if (fr != argsIn.end()) {
809 if (!options.empty()) {
812 out.params.push_back(*fr->second);
815 if (!options.empty()) {
816 out.params.push_back(std::move(options));
824 auto positional_args{argsIn.extract(
"args")};
825 if (positional_args && positional_args.mapped()->isArray()) {
826 if (initial_hole_size < (
int)positional_args.mapped()->size() && initial_param) {
831 out.params = *positional_args.mapped();
832 for (
size_t i{
out.params.size()}; i < named_args.size(); ++i) {
833 out.params.push_back(named_args[i]);
837 if (!argsIn.empty()) {
846 for (
const auto&
command : commands) {
882 return command.actor(request, result, last_handler);
886 }
catch (
const std::exception& e) {
893 std::vector<std::string> commandList;
895 for (
const auto& i :
mapCommands) commandList.emplace_back(i.first);
901 std::vector<std::string> method_names;
903 if (cmds.empty())
continue;
905 if ((!include_hidden &&
cmd->category ==
"hidden") || !
cmd->metadata_fn)
continue;
906 method_names.push_back(
name);
908 std::sort(method_names.begin(), method_names.end());
911 for (
const auto& method_name : method_names) {
916 for (
const auto& arg : helpman.GetArgs()) {
921 param.pushKV(
"schema", OpenRPCArgSchema(arg, include_hidden,
false));
924 if (names.size() > 1) {
926 for (
size_t i{1}; i < names.size(); ++i) aliases.push_back(names[i]);
927 param.pushKV(
"x-bitcoin-aliases", std::move(aliases));
932 params.push_back(std::move(param));
936 const auto& results{helpman.GetResults().m_results};
938 result_schema = OpenRPCResultSchema(results[0]);
939 }
else if (results.size() > 1) {
941 for (
const auto& r : results) {
943 UniValue schema{OpenRPCResultSchema(r)};
944 if (!r.m_cond.empty()) schema.
pushKV(
"description", r.m_cond);
945 one_of.push_back(std::move(schema));
947 if (one_of.size() == 1) {
948 result_schema = one_of[0];
949 }
else if (one_of.size() > 1) {
950 result_schema.pushKV(
"oneOf", std::move(one_of));
955 method.pushKV(
"name", method_name);
957 method.pushKV(
"params", std::move(params));
959 result.pushKV(
"name",
"result");
960 result.pushKV(
"schema", std::move(result_schema));
961 method.pushKV(
"result", std::move(result));
962 method.pushKV(
"x-bitcoin-category",
cmd->category);
963 methods.push_back(std::move(method));
966 std::string version{
"v" CLIENT_VERSION_STRING};
967 if (!CLIENT_VERSION_IS_RELEASE) version +=
"-dev";
970 info.pushKV(
"title", CLIENT_NAME
" JSON-RPC");
971 info.pushKV(
"version", version);
972 info.pushKV(
"description",
"Autogenerated from " CLIENT_NAME
" RPC metadata.");
975 doc.pushKV(
"openrpc",
"1.4.1");
976 doc.pushKV(
"info", std::move(info));
977 doc.pushKV(
"methods", std::move(methods));
#define CHECK_NONFATAL(condition)
Identity function.
#define NONFATAL_UNREACHABLE()
NONFATAL_UNREACHABLE() is a macro that is used to mark unreachable code.
std::vector< std::string > GetArgs(const std::string &strArg) const EXCLUSIVE_LOCKS_REQUIRED(!cs_args)
Return a vector of strings of the given argument.
std::map< std::string, std::vector< const CRPCCommand * > > mapCommands
bool removeCommand(const std::string &name, const CRPCCommand *pcmd)
std::string help(std::string_view name, const JSONRPCRequest &helpreq) const
std::vector< std::string > listCommands() const
Returns a list of registered commands.
UniValue buildOpenRPCDoc(bool include_hidden=false) const
Return a complete OpenRPC 1.4.1 document for registered commands.
UniValue execute(const JSONRPCRequest &request) const
Execute a method.
void appendCommand(const std::string &name, const CRPCCommand *pcmd)
Appends a CRPCCommand to the dispatch table.
UniValue dumpArgMap(const JSONRPCRequest &request) const
Return all named arguments that need to be converted by the client from string to another JSON type.
Different type to mark Mutex at global scope.
JSONRPCVersion m_json_version
enum JSONRPCRequest::Mode mode
std::optional< UniValue > id
auto MaybeArg(std::string_view key) const
Helper to get an optional request argument.
auto Arg(std::string_view key) const
Helper to get a required or default-valued request argument.
void push_back(UniValue val)
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
void pushKV(std::string key, UniValue val)
SteadyClock::duration GetUptime()
Monotonic uptime (not affected by system time changes).
#define LogDebug(category,...)
BCLog::Logger & LogInstance()
std::vector< std::string > SplitString(std::string_view str, char sep)
std::string TrimString(std::string_view str, std::string_view pattern=" \f\n\r\t\v")
UniValue JSONRPCError(int code, const std::string &message)
UniValue JSONRPCReplyObj(UniValue result, UniValue error, std::optional< UniValue > id, JSONRPCVersion jsonrpc_version)
void DeleteAuthCookie()
Delete RPC authentication cookie from disk.
@ RPC_MISC_ERROR
General application defined errors.
@ RPC_TYPE_ERROR
Unexpected type was passed as parameter.
@ RPC_CLIENT_NOT_CONNECTED
P2P client errors.
@ RPC_INVALID_PARAMETER
Invalid, missing or duplicate parameter.
@ RPC_IN_WARMUP
Client still warming up.
std::string HelpExampleCli(const std::string &methodname, const std::string &args)
std::string HelpExampleRpc(const std::string &methodname, const std::string &args)
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)
void SetRPCWarmupFinished()
static bool ExecuteCommands(const std::vector< const CRPCCommand * > &commands, const JSONRPCRequest &request, UniValue &result)
static RPCResult OpenRPCDocResult()
bool RPCIsInWarmup(std::string *outStatus)
static bool ExecuteCommand(const CRPCCommand &command, const JSONRPCRequest &request, UniValue &result, bool last_handler)
static std::atomic< bool > g_rpc_running
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...
bool IsRPCRunning()
Query whether RPC is running.
void SetRPCWarmupStarting()
static RPCMethod getrpcinfo()
UniValue JSONRPCExec(const JSONRPCRequest &jreq, bool catch_errors)
static RPCMethod getopenrpcinfo()
static bool fRPCInWarmup GUARDED_BY(g_rpc_warmup_mutex)
static GlobalMutex g_rpc_warmup_mutex
static RPCMethod uptime()
static RPCServerInfo g_rpc_server_info
static RPCMethod rpc_discover()
static const CRPCCommand vRPCCommands[]
void SetRPCWarmupStatus(const std::string &newStatus)
Set the RPC warmup status.
void RpcInterruptionPoint()
Throw JSONRPCError if RPC is not running.
NodeContext & EnsureAnyNodeContext(const std::any &context)
@ 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.
const RPCArgOptions m_opts
const std::string m_names
The name of the arg (can be empty for inner args, can contain multiple aliases separated by | for nam...
const Fallback m_fallback
const std::string m_description
std::string DefaultHint
Hint for default value.
std::string GetFirstName() const
Return the first of all aliases.
@ OMITTED
Optional argument for which the default value is omitted from help text for one of two reasons:
bool hidden
For testing only.
std::vector< std::string > type_str
Should be empty unless it is supposed to override the auto-generated type strings....
bool also_positional
If set allows a named-parameter field in an OBJ_NAMED_PARAM options object to have the same name as a...
bool placeholder
If set, the argument is retained only for compatibility and should generally be omitted.
RPCCommandExecution(const std::string &method)
std::list< RPCCommandExecutionInfo >::iterator it
SteadyClock::time_point start
@ 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.
const RPCResultOptions m_opts
const std::string m_key_name
Only used for dicts.
HelpElision print_elision
std::list< RPCCommandExecutionInfo > active_commands GUARDED_BY(mutex)
Overloaded helper for std::visit.
std::vector< uint16_t > keys
consteval auto _(util::TranslatedLiteral str)
const UniValue NullUniValue
std::string Capitalize(std::string str)
Capitalizes the first character of the given string.
void UninterruptibleSleep(const std::chrono::microseconds &n)
constexpr int64_t TicksSeconds(Duration d)