aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/rpc/server.cpp50
-rw-r--r--src/rpc/server.h17
-rw-r--r--src/rpc/util.cpp44
-rw-r--r--src/rpc/util.h19
4 files changed, 85 insertions, 45 deletions
diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp
index de8791a935..e5f6b1b9f1 100644
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -130,11 +130,9 @@ std::string CRPCTable::help(const std::string& strCommand, const JSONRPCRequest&
return strRet;
}
-UniValue help(const JSONRPCRequest& jsonRequest)
+static RPCHelpMan help()
{
- if (jsonRequest.fHelp || jsonRequest.params.size() > 1)
- throw std::runtime_error(
- RPCHelpMan{"help",
+ return RPCHelpMan{"help",
"\nList all commands, or get help for a specified command.\n",
{
{"command", RPCArg::Type::STR, /* default */ "all commands", "The command to get help on"},
@@ -143,32 +141,32 @@ UniValue help(const JSONRPCRequest& jsonRequest)
RPCResult::Type::STR, "", "The help text"
},
RPCExamples{""},
- }.ToString()
- );
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& jsonRequest) -> UniValue
+{
std::string strCommand;
if (jsonRequest.params.size() > 0)
strCommand = jsonRequest.params[0].get_str();
return tableRPC.help(strCommand, jsonRequest);
+},
+ };
}
-
-UniValue stop(const JSONRPCRequest& jsonRequest)
+static RPCHelpMan stop()
{
static const std::string RESULT{PACKAGE_NAME " stopping"};
- // Accept the deprecated and ignored 'detach' boolean argument
+ return RPCHelpMan{"stop",
// Also accept the hidden 'wait' integer argument (milliseconds)
// For instance, 'stop 1000' makes the call wait 1 second before returning
// to the client (intended for testing)
- if (jsonRequest.fHelp || jsonRequest.params.size() > 1)
- throw std::runtime_error(
- RPCHelpMan{"stop",
"\nRequest a graceful shutdown of " PACKAGE_NAME ".",
- {},
+ {
+ {"wait", RPCArg::Type::NUM, RPCArg::Optional::OMITTED_NAMED_ARG, "how long to wait in ms", "", {}, /* hidden */ true},
+ },
RPCResult{RPCResult::Type::STR, "", "A string with the content '" + RESULT + "'"},
RPCExamples{""},
- }.ToString());
+ [&](const RPCHelpMan& self, const JSONRPCRequest& jsonRequest) -> UniValue
+{
// Event loop will exit after current HTTP requests have been handled, so
// this reply will get back to the client.
StartShutdown();
@@ -176,11 +174,13 @@ UniValue stop(const JSONRPCRequest& jsonRequest)
UninterruptibleSleep(std::chrono::milliseconds{jsonRequest.params[0].get_int()});
}
return RESULT;
+},
+ };
}
-static UniValue uptime(const JSONRPCRequest& jsonRequest)
+static RPCHelpMan uptime()
{
- RPCHelpMan{"uptime",
+ return RPCHelpMan{"uptime",
"\nReturns the total uptime of the server.\n",
{},
RPCResult{
@@ -190,14 +190,16 @@ static UniValue uptime(const JSONRPCRequest& jsonRequest)
HelpExampleCli("uptime", "")
+ HelpExampleRpc("uptime", "")
},
- }.Check(jsonRequest);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
return GetTime() - GetStartupTime();
}
+ };
+}
-static UniValue getrpcinfo(const JSONRPCRequest& request)
+static RPCHelpMan getrpcinfo()
{
- RPCHelpMan{"getrpcinfo",
+ return RPCHelpMan{"getrpcinfo",
"\nReturns details of the RPC server.\n",
{},
RPCResult{
@@ -217,8 +219,8 @@ static UniValue getrpcinfo(const JSONRPCRequest& request)
RPCExamples{
HelpExampleCli("getrpcinfo", "")
+ HelpExampleRpc("getrpcinfo", "")},
- }.Check(request);
-
+ [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
+{
LOCK(g_rpc_server_info.mutex);
UniValue active_commands(UniValue::VARR);
for (const RPCCommandExecutionInfo& info : g_rpc_server_info.active_commands) {
@@ -237,6 +239,8 @@ static UniValue getrpcinfo(const JSONRPCRequest& request)
return result;
}
+ };
+}
// clang-format off
static const CRPCCommand vRPCCommands[] =
diff --git a/src/rpc/server.h b/src/rpc/server.h
index d7a04ff6e8..6da3e94ea2 100644
--- a/src/rpc/server.h
+++ b/src/rpc/server.h
@@ -8,6 +8,7 @@
#include <amount.h>
#include <rpc/request.h>
+#include <rpc/util.h>
#include <functional>
#include <map>
@@ -85,6 +86,7 @@ void RPCUnsetTimerInterface(RPCTimerInterface *iface);
void RPCRunLater(const std::string& name, std::function<void()> func, int64_t nSeconds);
typedef UniValue(*rpcfn_type)(const JSONRPCRequest& jsonRequest);
+typedef RPCHelpMan (*RpcMethodFnType)();
class CRPCCommand
{
@@ -101,6 +103,19 @@ public:
{
}
+ //! Simplified constructor taking plain RpcMethodFnType function pointer.
+ CRPCCommand(std::string category, std::string name_in, RpcMethodFnType fn, std::vector<std::string> args_in)
+ : CRPCCommand(
+ category,
+ fn().m_name,
+ [fn](const JSONRPCRequest& request, UniValue& result, bool) { result = fn().HandleRequest(request); return true; },
+ fn().GetArgNames(),
+ intptr_t(fn))
+ {
+ CHECK_NONFATAL(fn().m_name == name_in);
+ CHECK_NONFATAL(fn().GetArgNames() == args_in);
+ }
+
//! Simplified constructor taking plain rpcfn_type function pointer.
CRPCCommand(const char* category, const char* name, rpcfn_type fn, std::initializer_list<const char*> args)
: CRPCCommand(category, name,
@@ -117,7 +132,7 @@ public:
};
/**
- * Bitcoin RPC command dispatcher.
+ * RPC command dispatcher.
*/
class CRPCTable
{
diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp
index ca73c699c9..9f4c7bee9c 100644
--- a/src/rpc/util.cpp
+++ b/src/rpc/util.cpp
@@ -385,9 +385,7 @@ struct Sections {
PushSection({indent + "]" + (outer_type != OuterType::NONE ? "," : ""), ""});
break;
}
-
- // no default case, so the compiler can warn about missing cases
- }
+ } // no default case, so the compiler can warn about missing cases
}
/**
@@ -398,6 +396,9 @@ struct Sections {
std::string ret;
const size_t pad = m_max_pad + 4;
for (const auto& s : m_sections) {
+ // The left part of a section is assumed to be a single line, usually it is the name of the JSON struct or a
+ // brace like {, }, [, or ]
+ CHECK_NONFATAL(s.m_left.find('\n') == std::string::npos);
if (s.m_right.empty()) {
ret += s.m_left;
ret += "\n";
@@ -432,7 +433,11 @@ struct Sections {
};
RPCHelpMan::RPCHelpMan(std::string name, std::string description, std::vector<RPCArg> args, RPCResults results, RPCExamples examples)
+ : RPCHelpMan{std::move(name), std::move(description), std::move(args), std::move(results), std::move(examples), nullptr} {}
+
+RPCHelpMan::RPCHelpMan(std::string name, std::string description, std::vector<RPCArg> args, RPCResults results, RPCExamples examples, RPCMethodImpl fun)
: m_name{std::move(name)},
+ m_fun{std::move(fun)},
m_description{std::move(description)},
m_args{std::move(args)},
m_results{std::move(results)},
@@ -481,6 +486,16 @@ bool RPCHelpMan::IsValidNumArgs(size_t num_args) const
}
return num_required_args <= num_args && num_args <= m_args.size();
}
+
+std::vector<std::string> RPCHelpMan::GetArgNames() const
+{
+ std::vector<std::string> ret;
+ for (const auto& arg : m_args) {
+ ret.emplace_back(arg.m_names);
+ }
+ return ret;
+}
+
std::string RPCHelpMan::ToString() const
{
std::string ret;
@@ -489,6 +504,7 @@ std::string RPCHelpMan::ToString() const
ret += m_name;
bool was_optional{false};
for (const auto& arg : m_args) {
+ if (arg.m_hidden) continue;
const bool optional = arg.IsOptional();
ret += " ";
if (optional) {
@@ -510,6 +526,7 @@ std::string RPCHelpMan::ToString() const
Sections sections;
for (size_t i{0}; i < m_args.size(); ++i) {
const auto& arg = m_args.at(i);
+ if (arg.m_hidden) continue;
if (i == 0) ret += "\nArguments:\n";
@@ -589,9 +606,7 @@ std::string RPCArg::ToDescriptionString() const
ret += "json array";
break;
}
-
- // no default case, so the compiler can warn about missing cases
- }
+ } // no default case, so the compiler can warn about missing cases
}
if (m_fallback.which() == 1) {
ret += ", optional, default=" + boost::get<std::string>(m_fallback);
@@ -609,9 +624,7 @@ std::string RPCArg::ToDescriptionString() const
ret += ", required";
break;
}
-
- // no default case, so the compiler can warn about missing cases
- }
+ } // no default case, so the compiler can warn about missing cases
}
ret += ")";
ret += m_description.empty() ? "" : " " + m_description;
@@ -706,10 +719,7 @@ void RPCResult::ToSections(Sections& sections, const OuterType outer_type, const
sections.PushSection({indent + "}" + maybe_separator, ""});
return;
}
-
- // no default case, so the compiler can warn about missing cases
- }
-
+ } // no default case, so the compiler can warn about missing cases
CHECK_NONFATAL(false);
}
@@ -746,9 +756,7 @@ std::string RPCArg::ToStringObj(const bool oneline) const
case Type::OBJ_USER_KEYS:
// Currently unused, so avoid writing dead code
CHECK_NONFATAL(false);
-
- // no default case, so the compiler can warn about missing cases
- }
+ } // no default case, so the compiler can warn about missing cases
CHECK_NONFATAL(false);
}
@@ -783,9 +791,7 @@ std::string RPCArg::ToString(const bool oneline) const
}
return "[" + res + "...]";
}
-
- // no default case, so the compiler can warn about missing cases
- }
+ } // no default case, so the compiler can warn about missing cases
CHECK_NONFATAL(false);
}
diff --git a/src/rpc/util.h b/src/rpc/util.h
index 96dd1ea74a..45b0bb0c7e 100644
--- a/src/rpc/util.h
+++ b/src/rpc/util.h
@@ -147,6 +147,7 @@ struct RPCArg {
using Fallback = boost::variant<Optional, /* default value for optional args */ std::string>;
const std::string m_names; //!< The name of the arg (can be empty for inner args, can contain multiple aliases separated by | for named request arguments)
const Type m_type;
+ const bool m_hidden;
const std::vector<RPCArg> m_inner; //!< Only used for arrays or dicts
const Fallback m_fallback;
const std::string m_description;
@@ -159,9 +160,11 @@ struct RPCArg {
const Fallback fallback,
const std::string description,
const std::string oneline_description = "",
- const std::vector<std::string> type_str = {})
+ const std::vector<std::string> type_str = {},
+ const bool hidden = false)
: m_names{std::move(name)},
m_type{std::move(type)},
+ m_hidden{hidden},
m_fallback{std::move(fallback)},
m_description{std::move(description)},
m_oneline_description{std::move(oneline_description)},
@@ -180,6 +183,7 @@ struct RPCArg {
const std::vector<std::string> type_str = {})
: m_names{std::move(name)},
m_type{std::move(type)},
+ m_hidden{false},
m_inner{std::move(inner)},
m_fallback{std::move(fallback)},
m_description{std::move(description)},
@@ -329,8 +333,15 @@ class RPCHelpMan
{
public:
RPCHelpMan(std::string name, std::string description, std::vector<RPCArg> args, RPCResults results, RPCExamples examples);
+ using RPCMethodImpl = std::function<UniValue(const RPCHelpMan&, const JSONRPCRequest&)>;
+ RPCHelpMan(std::string name, std::string description, std::vector<RPCArg> args, RPCResults results, RPCExamples examples, RPCMethodImpl fun);
std::string ToString() const;
+ UniValue HandleRequest(const JSONRPCRequest& request)
+ {
+ Check(request);
+ return m_fun(*this, request);
+ }
/** If the supplied number of args is neither too small nor too high */
bool IsValidNumArgs(size_t num_args) const;
/**
@@ -343,8 +354,12 @@ public:
}
}
-private:
+ std::vector<std::string> GetArgNames() const;
+
const std::string m_name;
+
+private:
+ const RPCMethodImpl m_fun;
const std::string m_description;
const std::vector<RPCArg> m_args;
const RPCResults m_results;