diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 8b688648ed083..c3c9ab4a6aeac 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -23,9 +23,9 @@ #include #include -#include - #include // for unique_ptr +#include +#include static bool fRPCRunning = false; static bool fRPCInWarmup = true; @@ -274,12 +274,11 @@ UniValue stop(const JSONRPCRequest& jsonRequest) */ static const CRPCCommand vRPCCommands[] = { - // category name actor (function) okSafeMode - // --------------------- ------------------------ ----------------------- ---------- - /* Overall control/query calls */ - - {"control", "help", &help, true }, - {"control", "stop", &stop, true }, + // category name actor (function) okSafe argNames + // --------------------- ------------------------ ----------------------- ------ ---------- + /* Overall control/query calls */ + { "control", "help", &help, true, {"command"} }, + { "control", "stop", &stop, true, {} }, }; CRPCTable::CRPCTable() @@ -385,12 +384,12 @@ void JSONRPCRequest::parse(const UniValue& valRequest) // Parse params UniValue valParams = find_value(request, "params"); - if (valParams.isArray()) - params = valParams.get_array(); + if (valParams.isArray() || valParams.isObject()) + params = valParams; else if (valParams.isNull()) params = UniValue(UniValue::VARR); else - throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array"); + throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array or object"); } bool IsDeprecatedRPCEnabled(const std::string& method) @@ -429,6 +428,48 @@ std::string JSONRPCExecBatch(const UniValue& vReq) return ret.write() + "\n"; } +/** + * Process named arguments into a vector of positional arguments, based on the + * passed-in specification for the RPC call's arguments. + */ +static inline JSONRPCRequest transformNamedArguments(const JSONRPCRequest& in, const std::vector& argNames) +{ + JSONRPCRequest out = in; + out.params = UniValue(UniValue::VARR); + // Build a map of parameters, and remove ones that have been processed, so that we can throw a focused error if + // there is an unknown one. + const std::vector& keys = in.params.getKeys(); + const std::vector& values = in.params.getValues(); + std::unordered_map argsIn; + for (size_t i=0; isecond); + argsIn.erase(fr); + } else { + hole += 1; + } + } + // If there are still arguments in the argsIn map, this is an error. + if (!argsIn.empty()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Unknown named parameter " + argsIn.begin()->first); + } + // Return request with named arguments transformed to positional arguments + return out; +} + UniValue CRPCTable::execute(const JSONRPCRequest &request) const { // Return immediately if in warmup @@ -445,8 +486,12 @@ UniValue CRPCTable::execute(const JSONRPCRequest &request) const g_rpcSignals.PreCommand(*pcmd); try { - // Execute - return pcmd->actor(request); + // Execute, convert arguments to array if necessary + if (request.params.isObject()) { + return pcmd->actor(transformNamedArguments(request, pcmd->argNames)); + } else { + return pcmd->actor(request); + } } catch (const std::exception& e) { throw JSONRPCError(RPC_MISC_ERROR, e.what()); } diff --git a/src/rpc/server.h b/src/rpc/server.h index 89739374a4ab2..1353635dabd21 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -138,6 +138,7 @@ class CRPCCommand std::string name; rpcfn_type actor; bool okSafeMode; + std::vector argNames; }; /**