mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-01-19 14:53:43 +01:00
RPC: Add add OBJ_NAMED_PARAMS type
OBJ_NAMED_PARAMS type works the same as OBJ type except it registers the object keys to be accepted as top-level named-only RPC parameters. Generated documentation also lists the object keys seperately in a new "Named arguments" section of help text. Named-only RPC parameters have the same semantics as python keyword-only arguments (https://peps.python.org/pep-3102/). They are always required to be passed by name, so they don't affect interpretation of positional arguments, and aren't affected when positional arguments are added or removed. The new OBJ_NAMED_PARAMS type is used in the next commit to make it easier to pass options values to various RPC methods. Co-authored-by: Andrew Chow <github@achow101.com>
This commit is contained in:
@@ -392,7 +392,7 @@ std::string JSONRPCExecBatch(const JSONRPCRequest& jreq, const UniValue& vReq)
|
||||
* 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<std::string>& argNames)
|
||||
static inline JSONRPCRequest transformNamedArguments(const JSONRPCRequest& in, const std::vector<std::pair<std::string, bool>>& argNames)
|
||||
{
|
||||
JSONRPCRequest out = in;
|
||||
out.params = UniValue(UniValue::VARR);
|
||||
@@ -417,7 +417,9 @@ static inline JSONRPCRequest transformNamedArguments(const JSONRPCRequest& in, c
|
||||
// "args" parameter, if present.
|
||||
int hole = 0;
|
||||
int initial_hole_size = 0;
|
||||
for (const std::string &argNamePattern: argNames) {
|
||||
const std::string* initial_param = nullptr;
|
||||
UniValue options{UniValue::VOBJ};
|
||||
for (const auto& [argNamePattern, named_only]: argNames) {
|
||||
std::vector<std::string> vargNames = SplitString(argNamePattern, '|');
|
||||
auto fr = argsIn.end();
|
||||
for (const std::string & argName : vargNames) {
|
||||
@@ -426,7 +428,22 @@ static inline JSONRPCRequest transformNamedArguments(const JSONRPCRequest& in, c
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (fr != argsIn.end()) {
|
||||
|
||||
// Handle named-only parameters by pushing them into a temporary options
|
||||
// object, and then pushing the accumulated options as the next
|
||||
// positional argument.
|
||||
if (named_only) {
|
||||
if (fr != argsIn.end()) {
|
||||
if (options.exists(fr->first)) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Parameter " + fr->first + " specified multiple times");
|
||||
}
|
||||
options.__pushKV(fr->first, *fr->second);
|
||||
argsIn.erase(fr);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!options.empty() || fr != argsIn.end()) {
|
||||
for (int i = 0; i < hole; ++i) {
|
||||
// Fill hole between specified parameters with JSON nulls,
|
||||
// but not at the end (for backwards compatibility with calls
|
||||
@@ -434,12 +451,26 @@ static inline JSONRPCRequest transformNamedArguments(const JSONRPCRequest& in, c
|
||||
out.params.push_back(UniValue());
|
||||
}
|
||||
hole = 0;
|
||||
out.params.push_back(*fr->second);
|
||||
argsIn.erase(fr);
|
||||
if (!initial_param) initial_param = &argNamePattern;
|
||||
} else {
|
||||
hole += 1;
|
||||
if (out.params.empty()) initial_hole_size = hole;
|
||||
}
|
||||
|
||||
// If named input parameter "fr" is present, push it onto out.params. If
|
||||
// options are present, push them onto out.params. If both are present,
|
||||
// throw an error.
|
||||
if (fr != argsIn.end()) {
|
||||
if (!options.empty()) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Parameter " + fr->first + " conflicts with parameter " + options.getKeys().front());
|
||||
}
|
||||
out.params.push_back(*fr->second);
|
||||
argsIn.erase(fr);
|
||||
}
|
||||
if (!options.empty()) {
|
||||
out.params.push_back(std::move(options));
|
||||
options = UniValue{UniValue::VOBJ};
|
||||
}
|
||||
}
|
||||
// If leftover "args" param was found, use it as a source of positional
|
||||
// arguments and add named arguments after. This is a convenience for
|
||||
@@ -447,9 +478,8 @@ static inline JSONRPCRequest transformNamedArguments(const JSONRPCRequest& in, c
|
||||
// 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");
|
||||
if (initial_hole_size < (int)positional_args.mapped()->size() && initial_param) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Parameter " + *initial_param + " 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)};
|
||||
|
||||
Reference in New Issue
Block a user