aboutsummaryrefslogtreecommitdiff
path: root/src/rpc/server.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/rpc/server.cpp')
-rw-r--r--src/rpc/server.cpp35
1 files changed, 32 insertions, 3 deletions
diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp
index 1d7bd2eb94..a026b7adfa 100644
--- a/src/rpc/server.cpp
+++ b/src/rpc/server.cpp
@@ -103,7 +103,7 @@ std::string CRPCTable::help(const std::string& strCommand, const JSONRPCRequest&
{
UniValue unused_result;
if (setDone.insert(pcmd->unique_id).second)
- pcmd->actor(jreq, unused_result, true /* last_handler */);
+ pcmd->actor(jreq, unused_result, /*last_handler=*/true);
}
catch (const std::exception& e)
{
@@ -399,10 +399,21 @@ static inline JSONRPCRequest transformNamedArguments(const JSONRPCRequest& in, c
const std::vector<UniValue>& values = in.params.getValues();
std::unordered_map<std::string, const UniValue*> argsIn;
for (size_t i=0; i<keys.size(); ++i) {
- argsIn[keys[i]] = &values[i];
+ auto [_, inserted] = argsIn.emplace(keys[i], &values[i]);
+ if (!inserted) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Parameter " + keys[i] + " specified multiple times");
+ }
}
- // Process expected parameters.
+ // Process expected parameters. If any parameters were left unspecified in
+ // the request before a parameter that was specified, null values need to be
+ // inserted at the unspecifed parameter positions, and the "hole" variable
+ // below tracks the number of null values that need to be inserted.
+ // The "initial_hole_size" variable stores the size of the initial hole,
+ // i.e. how many initial positional arguments were left unspecified. This is
+ // used after the for-loop to add initial positional arguments from the
+ // "args" parameter, if present.
int hole = 0;
+ int initial_hole_size = 0;
for (const std::string &argNamePattern: argNames) {
std::vector<std::string> vargNames = SplitString(argNamePattern, '|');
auto fr = argsIn.end();
@@ -424,6 +435,24 @@ static inline JSONRPCRequest transformNamedArguments(const JSONRPCRequest& in, c
argsIn.erase(fr);
} else {
hole += 1;
+ if (out.params.empty()) initial_hole_size = hole;
+ }
+ }
+ // If leftover "args" param was found, use it as a source of positional
+ // arguments and add named arguments after. This is a convenience for
+ // clients that want to pass a combination of named and positional
+ // arguments as described in doc/JSON-RPC-interface.md#parameter-passing
+ auto positional_args{argsIn.extract("args")};
+ if (positional_args && positional_args.mapped()->isArray()) {
+ const bool has_named_arguments{initial_hole_size < (int)argNames.size()};
+ if (initial_hole_size < (int)positional_args.mapped()->size() && has_named_arguments) {
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Parameter " + argNames[initial_hole_size] + " specified twice both as positional and named argument");
+ }
+ // Assign positional_args to out.params and append named_args after.
+ UniValue named_args{std::move(out.params)};
+ out.params = *positional_args.mapped();
+ for (size_t i{out.params.size()}; i < named_args.size(); ++i) {
+ out.params.push_back(named_args[i]);
}
}
// If there are still arguments in the argsIn map, this is an error.