26
26
#include < boost/algorithm/string/case_conv.hpp> // for to_upper()
27
27
28
28
#include < memory> // for unique_ptr
29
+ #include < unordered_map>
29
30
30
31
using namespace RPCServer ;
31
32
using namespace std ;
@@ -268,11 +269,11 @@ UniValue stop(const JSONRPCRequest& jsonRequest)
268
269
* Call Table
269
270
*/
270
271
static const CRPCCommand vRPCCommands[] =
271
- { // category name actor (function) okSafeMode
272
- // --------------------- ------------------------ ----------------------- ----------
272
+ { // category name actor (function) okSafe argNames
273
+ // --------------------- ------------------------ ----------------------- ------ ------ ----
273
274
/* Overall control/query calls */
274
- { " control" , " help" , &help, true },
275
- { " control" , " stop" , &stop, true },
275
+ { " control" , " help" , &help, true , { " command " } },
276
+ { " control" , " stop" , &stop, true , {} },
276
277
};
277
278
278
279
CRPCTable::CRPCTable ()
@@ -379,12 +380,12 @@ void JSONRPCRequest::parse(const UniValue& valRequest)
379
380
380
381
// Parse params
381
382
UniValue valParams = find_value (request, " params" );
382
- if (valParams.isArray ())
383
- params = valParams. get_array () ;
383
+ if (valParams.isArray () || valParams. isObject () )
384
+ params = valParams;
384
385
else if (valParams.isNull ())
385
386
params = UniValue (UniValue::VARR);
386
387
else
387
- throw JSONRPCError (RPC_INVALID_REQUEST, " Params must be an array" );
388
+ throw JSONRPCError (RPC_INVALID_REQUEST, " Params must be an array or object " );
388
389
}
389
390
390
391
static UniValue JSONRPCExecOne (const UniValue& req)
@@ -420,6 +421,48 @@ std::string JSONRPCExecBatch(const UniValue& vReq)
420
421
return ret.write () + " \n " ;
421
422
}
422
423
424
+ /* *
425
+ * Process named arguments into a vector of positional arguments, based on the
426
+ * passed-in specification for the RPC call's arguments.
427
+ */
428
+ static inline JSONRPCRequest transformNamedArguments (const JSONRPCRequest& in, const std::vector<std::string>& argNames)
429
+ {
430
+ JSONRPCRequest out = in;
431
+ out.params = UniValue (UniValue::VARR);
432
+ // Build a map of parameters, and remove ones that have been processed, so that we can throw a focused error if
433
+ // there is an unknown one.
434
+ const std::vector<std::string>& keys = in.params .getKeys ();
435
+ const std::vector<UniValue>& values = in.params .getValues ();
436
+ std::unordered_map<std::string, const UniValue*> argsIn;
437
+ for (size_t i=0 ; i<keys.size (); ++i) {
438
+ argsIn[keys[i]] = &values[i];
439
+ }
440
+ // Process expected parameters.
441
+ int hole = 0 ;
442
+ for (const std::string &argName: argNames) {
443
+ auto fr = argsIn.find (argName);
444
+ if (fr != argsIn.end ()) {
445
+ for (int i = 0 ; i < hole; ++i) {
446
+ // Fill hole between specified parameters with JSON nulls,
447
+ // but not at the end (for backwards compatibility with calls
448
+ // that act based on number of specified parameters).
449
+ out.params .push_back (UniValue ());
450
+ }
451
+ hole = 0 ;
452
+ out.params .push_back (*fr->second );
453
+ argsIn.erase (fr);
454
+ } else {
455
+ hole += 1 ;
456
+ }
457
+ }
458
+ // If there are still arguments in the argsIn map, this is an error.
459
+ if (!argsIn.empty ()) {
460
+ throw JSONRPCError (RPC_INVALID_PARAMETER, " Unknown named parameter " + argsIn.begin ()->first );
461
+ }
462
+ // Return request with named arguments transformed to positional arguments
463
+ return out;
464
+ }
465
+
423
466
UniValue CRPCTable::execute (const JSONRPCRequest &request) const
424
467
{
425
468
// Return immediately if in warmup
@@ -438,8 +481,12 @@ UniValue CRPCTable::execute(const JSONRPCRequest &request) const
438
481
439
482
try
440
483
{
441
- // Execute
442
- return pcmd->actor (request);
484
+ // Execute, convert arguments to array if necessary
485
+ if (request.params .isObject ()) {
486
+ return pcmd->actor (transformNamedArguments (request, pcmd->argNames ));
487
+ } else {
488
+ return pcmd->actor (request);
489
+ }
443
490
}
444
491
catch (const std::exception& e)
445
492
{
0 commit comments