server.cpp raw

   1  // Copyright (c) 2010 Satoshi Nakamoto
   2  // Copyright (c) 2009-2022 The Limenka developers
   3  // Distributed under the MIT software license, see the accompanying
   4  // file COPYING or http://www.opensource.org/licenses/mit-license.php.
   5  
   6  #include <limenka-build-config.h> // IWYU pragma: keep
   7  
   8  #include <rpc/server.h>
   9  
  10  #include <common/args.h>
  11  #include <common/system.h>
  12  #include <httprpc.h>
  13  #include <logging.h>
  14  #include <node/context.h>
  15  #include <node/kernel_notifications.h>
  16  #include <rpc/request.h>
  17  #include <rpc/server_util.h>
  18  #include <rpc/util.h>
  19  #include <sync.h>
  20  #include <util/any.h>
  21  #include <util/signalinterrupt.h>
  22  #include <util/strencodings.h>
  23  #include <util/string.h>
  24  #include <util/time.h>
  25  #include <validation.h>
  26  
  27  #ifdef ENABLE_WALLET
  28  #include <interfaces/wallet.h>
  29  #include <wallet/wallet.h>
  30  #endif
  31  
  32  #include <cassert>
  33  #include <chrono>
  34  #include <memory>
  35  #include <mutex>
  36  #include <unordered_map>
  37  
  38  using util::SplitString;
  39  
  40  static GlobalMutex g_rpc_warmup_mutex;
  41  static std::atomic<bool> g_rpc_running{false};
  42  static bool fRPCInWarmup GUARDED_BY(g_rpc_warmup_mutex) = true;
  43  static std::string rpcWarmupStatus GUARDED_BY(g_rpc_warmup_mutex) = "RPC server started";
  44  /* Timer-creating functions */
  45  static RPCTimerInterface* timerInterface = nullptr;
  46  /* Map of name to timer. */
  47  static GlobalMutex g_deadline_timers_mutex;
  48  static std::map<std::string, std::unique_ptr<RPCTimerBase> > deadlineTimers GUARDED_BY(g_deadline_timers_mutex);
  49  static bool ExecuteCommand(const CRPCCommand& command, const JSONRPCRequest& request, UniValue& result, bool last_handler);
  50  
  51  struct RPCCommandExecutionInfo
  52  {
  53      std::string method;
  54      SteadyClock::time_point start;
  55  };
  56  
  57  struct RPCServerInfo
  58  {
  59      Mutex mutex;
  60      std::list<RPCCommandExecutionInfo> active_commands GUARDED_BY(mutex);
  61  };
  62  
  63  static RPCServerInfo g_rpc_server_info;
  64  
  65  struct RPCCommandExecution
  66  {
  67      std::list<RPCCommandExecutionInfo>::iterator it;
  68      explicit RPCCommandExecution(const std::string& method)
  69      {
  70          LOCK(g_rpc_server_info.mutex);
  71          it = g_rpc_server_info.active_commands.insert(g_rpc_server_info.active_commands.end(), {method, SteadyClock::now()});
  72      }
  73      ~RPCCommandExecution()
  74      {
  75          LOCK(g_rpc_server_info.mutex);
  76          g_rpc_server_info.active_commands.erase(it);
  77      }
  78  };
  79  
  80  std::string CRPCTable::help(const std::string& strCommand, const JSONRPCRequest& helpreq) const
  81  {
  82      std::string strRet;
  83      std::string category;
  84      std::set<intptr_t> setDone;
  85      std::vector<std::pair<std::string, const CRPCCommand*> > vCommands;
  86      vCommands.reserve(mapCommands.size());
  87  
  88      for (const auto& entry : mapCommands)
  89          vCommands.emplace_back(entry.second.front()->category + entry.first, entry.second.front());
  90      sort(vCommands.begin(), vCommands.end());
  91  
  92      JSONRPCRequest jreq = helpreq;
  93      jreq.mode = JSONRPCRequest::GET_HELP;
  94      jreq.params = UniValue();
  95  
  96      for (const std::pair<std::string, const CRPCCommand*>& command : vCommands)
  97      {
  98          const CRPCCommand *pcmd = command.second;
  99          std::string strMethod = pcmd->name;
 100          if ((strCommand != "" || pcmd->category == "hidden") && strMethod != strCommand)
 101              continue;
 102          jreq.strMethod = strMethod;
 103          try
 104          {
 105              UniValue unused_result;
 106              if (setDone.insert(pcmd->unique_id).second)
 107                  pcmd->actor(jreq, unused_result, /*last_handler=*/true);
 108          }
 109          catch (const std::exception& e)
 110          {
 111              // Help text is returned in an exception
 112              std::string strHelp = std::string(e.what());
 113              if (strCommand == "")
 114              {
 115                  if (strHelp.find('\n') != std::string::npos)
 116                      strHelp = strHelp.substr(0, strHelp.find('\n'));
 117  
 118                  if (category != pcmd->category)
 119                  {
 120                      if (!category.empty())
 121                          strRet += "\n";
 122                      category = pcmd->category;
 123                      strRet += "== " + Capitalize(category) + " ==\n";
 124                  }
 125              }
 126              strRet += strHelp + "\n";
 127          }
 128      }
 129      if (strRet == "")
 130          strRet = strprintf("help: unknown command: %s\n", strCommand);
 131      strRet = strRet.substr(0,strRet.size()-1);
 132      return strRet;
 133  }
 134  
 135  static RPCHelpMan help()
 136  {
 137      return RPCHelpMan{"help",
 138                  "\nList all commands, or get help for a specified command.\n",
 139                  {
 140                      {"command", RPCArg::Type::STR, RPCArg::DefaultHint{"all commands"}, "The command to get help on"},
 141                  },
 142                  {
 143                      RPCResult{RPCResult::Type::STR, "", "The help text"},
 144                      RPCResult{RPCResult::Type::ANY, "", ""},
 145                  },
 146                  RPCExamples{""},
 147          [&](const RPCHelpMan& self, const JSONRPCRequest& jsonRequest) -> UniValue
 148  {
 149      std::string strCommand;
 150      if (jsonRequest.params.size() > 0) {
 151          strCommand = jsonRequest.params[0].get_str();
 152      }
 153      if (strCommand == "dump_all_command_conversions") {
 154          // Used for testing only, undocumented
 155          return tableRPC.dumpArgMap(jsonRequest);
 156      }
 157  
 158      return tableRPC.help(strCommand, jsonRequest);
 159  },
 160      };
 161  }
 162  
 163  static RPCHelpMan stop()
 164  {
 165      static const std::string RESULT{CLIENT_NAME " stopping"};
 166      return RPCHelpMan{"stop",
 167      // Also accept the hidden 'wait' integer argument (milliseconds)
 168      // For instance, 'stop 1000' makes the call wait 1 second before returning
 169      // to the client (intended for testing)
 170                  "\nRequest a graceful shutdown of " CLIENT_NAME ".",
 171                  {
 172                      {"wait", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "how long to wait in ms", RPCArgOptions{.hidden=true}},
 173                  },
 174                  RPCResult{RPCResult::Type::STR, "", "A string with the content '" + RESULT + "'"},
 175                  RPCExamples{""},
 176          [&](const RPCHelpMan& self, const JSONRPCRequest& jsonRequest) -> UniValue
 177  {
 178      // Event loop will exit after current HTTP requests have been handled, so
 179      // this reply will get back to the client.
 180      CHECK_NONFATAL((CHECK_NONFATAL(EnsureAnyNodeContext(jsonRequest.context).shutdown_request))());
 181      if (jsonRequest.params[0].isNum()) {
 182          UninterruptibleSleep(std::chrono::milliseconds{jsonRequest.params[0].getInt<int>()});
 183      }
 184      return RESULT;
 185  },
 186      };
 187  }
 188  
 189  static RPCHelpMan uptime()
 190  {
 191      return RPCHelpMan{"uptime",
 192                  "\nReturns the total uptime of the server.\n",
 193                              {},
 194                              RPCResult{
 195                                  RPCResult::Type::NUM, "", "The number of seconds that the server has been running"
 196                              },
 197                  RPCExamples{
 198                      HelpExampleCli("uptime", "")
 199                  + HelpExampleRpc("uptime", "")
 200                  },
 201          [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
 202  {
 203      return TicksSeconds(GetUptime());
 204  }
 205      };
 206  }
 207  
 208  static RPCHelpMan getrpcinfo()
 209  {
 210      return RPCHelpMan{"getrpcinfo",
 211                  "\nReturns details of the RPC server.\n",
 212                  {},
 213                  RPCResult{
 214                      RPCResult::Type::OBJ, "", "",
 215                      {
 216                          {RPCResult::Type::ARR, "active_commands", "All active commands",
 217                          {
 218                              {RPCResult::Type::OBJ, "", "Information about an active command",
 219                              {
 220                                   {RPCResult::Type::STR, "method", "The name of the RPC command"},
 221                                   {RPCResult::Type::NUM, "duration", "The running time in microseconds"},
 222                              }},
 223                          }},
 224                          {RPCResult::Type::STR, "logpath", "The complete file path to the debug log"},
 225                      }
 226                  },
 227                  RPCExamples{
 228                      HelpExampleCli("getrpcinfo", "")
 229                  + HelpExampleRpc("getrpcinfo", "")},
 230          [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
 231  {
 232      LOCK(g_rpc_server_info.mutex);
 233      UniValue active_commands(UniValue::VARR);
 234      for (const RPCCommandExecutionInfo& info : g_rpc_server_info.active_commands) {
 235          UniValue entry(UniValue::VOBJ);
 236          entry.pushKV("method", info.method);
 237          entry.pushKV("duration", int64_t{Ticks<std::chrono::microseconds>(SteadyClock::now() - info.start)});
 238          active_commands.push_back(std::move(entry));
 239      }
 240  
 241      UniValue result(UniValue::VOBJ);
 242      result.pushKV("active_commands", std::move(active_commands));
 243  
 244      const std::string path = LogInstance().m_file_path.utf8string();
 245      UniValue log_path(UniValue::VSTR, path);
 246      result.pushKV("logpath", std::move(log_path));
 247  
 248      return result;
 249  }
 250      };
 251  }
 252  
 253  static RPCHelpMan getrpcwhitelist()
 254  {
 255      return RPCHelpMan{"getrpcwhitelist",
 256                  "\nReturns whitelisted RPCs for the current user.\n",
 257                  {},
 258                  RPCResult{
 259                      RPCResult::Type::OBJ, "", "",
 260                      {
 261                          {RPCResult::Type::OBJ_DYN, "methods", "List of RPCs that the user is allowed to call",
 262                          {
 263                              {RPCResult::Type::NONE, "rpc", "Key is name of RPC method, value is null"},
 264                          }},
 265                          {RPCResult::Type::OBJ_DYN, "wallets", "List of wallets that the user is allowed to access",
 266                          {
 267                              {RPCResult::Type::NONE, "wallet_name", "Key is name of wallet, value is null"},
 268                          }},
 269                      }
 270                  },
 271                  RPCExamples{
 272                      HelpExampleCli("getrpcwhitelist", "")
 273                  + HelpExampleRpc("getrpcwhitelist", "")},
 274          [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
 275  {
 276      UniValue whitelisted_rpcs(UniValue::VOBJ);
 277      const std::set<std::string>& whitelist = GetWhitelistedRpcs(request.authUser);
 278      for (const auto& rpc : whitelist) {
 279          whitelisted_rpcs.pushKV(rpc, NullUniValue);
 280      }
 281  
 282      UniValue whitelisted_wallets(UniValue::VOBJ);
 283  #ifdef ENABLE_WALLET
 284      std::string authorized_wallet_name;
 285      const bool have_wallet_restriction = GetWalletRestrictionFromJSONRPCRequest(request, authorized_wallet_name);
 286      if (have_wallet_restriction) {
 287          if (authorized_wallet_name != "-") {
 288              whitelisted_wallets.pushKV(authorized_wallet_name, NullUniValue);
 289          }
 290      } else {
 291          // All wallets are allowed
 292          auto node_context = util::AnyPtr<node::NodeContext>(request.context);
 293          if (node_context && node_context->wallet_loader && node_context->wallet_loader->context()) {
 294              for (const std::shared_ptr<wallet::CWallet>& wallet : wallet::GetWallets(*node_context->wallet_loader->context())) {
 295                  if (!wallet.get()) continue;
 296  
 297                  LOCK(wallet->cs_wallet);
 298                  whitelisted_wallets.pushKV(wallet->GetName(), NullUniValue);
 299              }
 300          }
 301      }
 302  #endif
 303  
 304      UniValue result(UniValue::VOBJ);
 305      result.pushKV("methods", whitelisted_rpcs);
 306      result.pushKV("wallets", whitelisted_wallets);
 307  
 308      return result;
 309  }
 310      };
 311  }
 312  
 313  static const CRPCCommand vRPCCommands[]{
 314      /* Overall control/query calls */
 315      {"control", &getrpcinfo},
 316      {"control", &getrpcwhitelist},
 317      {"control", &help},
 318      {"control", &stop},
 319      {"control", &uptime},
 320  };
 321  
 322  CRPCTable::CRPCTable()
 323  {
 324      for (const auto& c : vRPCCommands) {
 325          appendCommand(c.name, &c);
 326      }
 327  }
 328  
 329  void CRPCTable::appendCommand(const std::string& name, const CRPCCommand* pcmd)
 330  {
 331      CHECK_NONFATAL(!IsRPCRunning()); // Only add commands before rpc is running
 332  
 333      mapCommands[name].push_back(pcmd);
 334  }
 335  
 336  bool CRPCTable::removeCommand(const std::string& name, const CRPCCommand* pcmd)
 337  {
 338      auto it = mapCommands.find(name);
 339      if (it != mapCommands.end()) {
 340          auto new_end = std::remove(it->second.begin(), it->second.end(), pcmd);
 341          if (it->second.end() != new_end) {
 342              it->second.erase(new_end, it->second.end());
 343              return true;
 344          }
 345      }
 346      return false;
 347  }
 348  
 349  void StartRPC()
 350  {
 351      LogDebug(BCLog::RPC, "Starting RPC\n");
 352      g_rpc_running = true;
 353  }
 354  
 355  void InterruptRPC()
 356  {
 357      static std::once_flag g_rpc_interrupt_flag;
 358      // This function could be called twice if the GUI has been started with -server=1.
 359      std::call_once(g_rpc_interrupt_flag, []() {
 360          LogDebug(BCLog::RPC, "Interrupting RPC\n");
 361          // Interrupt e.g. running longpolls
 362          g_rpc_running = false;
 363      });
 364  }
 365  
 366  void StopRPC()
 367  {
 368      static std::once_flag g_rpc_stop_flag;
 369      // This function could be called twice if the GUI has been started with -server=1.
 370      assert(!g_rpc_running);
 371      std::call_once(g_rpc_stop_flag, [&]() {
 372          LogDebug(BCLog::RPC, "Stopping RPC\n");
 373          WITH_LOCK(g_deadline_timers_mutex, deadlineTimers.clear());
 374          DeleteAuthCookie();
 375          LogDebug(BCLog::RPC, "RPC stopped.\n");
 376      });
 377  }
 378  
 379  bool IsRPCRunning()
 380  {
 381      return g_rpc_running;
 382  }
 383  
 384  void RpcInterruptionPoint()
 385  {
 386      if (!IsRPCRunning()) throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Shutting down");
 387  }
 388  
 389  void SetRPCWarmupStatus(const std::string& newStatus)
 390  {
 391      LOCK(g_rpc_warmup_mutex);
 392      rpcWarmupStatus = newStatus;
 393  }
 394  
 395  void SetRPCWarmupFinished()
 396  {
 397      LOCK(g_rpc_warmup_mutex);
 398      assert(fRPCInWarmup);
 399      fRPCInWarmup = false;
 400  }
 401  
 402  bool RPCIsInWarmup(std::string *outStatus)
 403  {
 404      LOCK(g_rpc_warmup_mutex);
 405      if (outStatus)
 406          *outStatus = rpcWarmupStatus;
 407      return fRPCInWarmup;
 408  }
 409  
 410  bool IsDeprecatedRPCEnabled(const std::string& method)
 411  {
 412      const std::vector<std::string> enabled_methods = gArgs.GetArgs("-deprecatedrpc");
 413  
 414      return find(enabled_methods.begin(), enabled_methods.end(), method) != enabled_methods.end();
 415  }
 416  
 417  UniValue JSONRPCExec(const JSONRPCRequest& jreq, bool catch_errors)
 418  {
 419      UniValue result;
 420      if (catch_errors) {
 421          try {
 422              result = tableRPC.execute(jreq);
 423          } catch (UniValue& e) {
 424              return JSONRPCReplyObj(NullUniValue, std::move(e), jreq.id, jreq.m_json_version);
 425          } catch (const std::exception& e) {
 426              return JSONRPCReplyObj(NullUniValue, JSONRPCError(RPC_MISC_ERROR, e.what()), jreq.id, jreq.m_json_version);
 427          }
 428      } else {
 429          result = tableRPC.execute(jreq);
 430      }
 431  
 432      return JSONRPCReplyObj(std::move(result), NullUniValue, jreq.id, jreq.m_json_version);
 433  }
 434  
 435  /**
 436   * Process named arguments into a vector of positional arguments, based on the
 437   * passed-in specification for the RPC call's arguments.
 438   */
 439  static inline JSONRPCRequest transformNamedArguments(const JSONRPCRequest& in, const std::vector<std::pair<std::string, bool>>& argNames)
 440  {
 441      JSONRPCRequest out = in;
 442      out.params = UniValue(UniValue::VARR);
 443      // Build a map of parameters, and remove ones that have been processed, so that we can throw a focused error if
 444      // there is an unknown one.
 445      const std::vector<std::string>& keys = in.params.getKeys();
 446      const std::vector<UniValue>& values = in.params.getValues();
 447      std::unordered_map<std::string, const UniValue*> argsIn;
 448      for (size_t i=0; i<keys.size(); ++i) {
 449          auto [_, inserted] = argsIn.emplace(keys[i], &values[i]);
 450          if (!inserted) {
 451              throw JSONRPCError(RPC_INVALID_PARAMETER, "Parameter " + keys[i] + " specified multiple times");
 452          }
 453      }
 454      // Process expected parameters. If any parameters were left unspecified in
 455      // the request before a parameter that was specified, null values need to be
 456      // inserted at the unspecified parameter positions, and the "hole" variable
 457      // below tracks the number of null values that need to be inserted.
 458      // The "initial_hole_size" variable stores the size of the initial hole,
 459      // i.e. how many initial positional arguments were left unspecified. This is
 460      // used after the for-loop to add initial positional arguments from the
 461      // "args" parameter, if present.
 462      int hole = 0;
 463      int initial_hole_size = 0;
 464      const std::string* initial_param = nullptr;
 465      auto positional_args{argsIn.extract("args")};
 466      if (!positional_args) {
 467          // nothing to do
 468      } else if (!positional_args.mapped()->isArray()) {
 469          argsIn.insert(std::move(positional_args));
 470          positional_args = {};
 471      }
 472      UniValue options{UniValue::VOBJ};
 473      for (const auto& [argNamePattern, named_only]: argNames) {
 474          std::vector<std::string> vargNames = SplitString(argNamePattern, '|');
 475          auto fr = argsIn.end();
 476          for (const std::string & argName : vargNames) {
 477              fr = argsIn.find(argName);
 478              if (fr != argsIn.end()) {
 479                  break;
 480              }
 481          }
 482  
 483          // Handle named-only parameters by pushing them into a temporary options
 484          // object, and then pushing the accumulated options as the next
 485          // positional argument.
 486          if (named_only) {
 487              if (options.empty()) {
 488                  if (options.isNull()) continue;
 489                  if (positional_args && positional_args.mapped()->size() > (size_t)hole) {
 490                      // some alternative to options is specified positionally; we can't use options at all
 491                      options = UniValue::VNULL;
 492                      continue;
 493                  }
 494              }
 495              if (fr != argsIn.end()) {
 496                  if (options.exists(fr->first)) {
 497                      throw JSONRPCError(RPC_INVALID_PARAMETER, "Parameter " + fr->first + " specified multiple times");
 498                  }
 499                  options.pushKVEnd(fr->first, *fr->second);
 500                  argsIn.erase(fr);
 501              }
 502              continue;
 503          }
 504  
 505          if (!options.empty() || fr != argsIn.end()) {
 506              for (int i = 0; i < hole; ++i) {
 507                  // Fill hole between specified parameters with JSON nulls,
 508                  // but not at the end (for backwards compatibility with calls
 509                  // that act based on number of specified parameters).
 510                  out.params.push_back(UniValue());
 511              }
 512              hole = 0;
 513              if (!initial_param) initial_param = &argNamePattern;
 514          } else {
 515              hole += 1;
 516              if (out.params.empty()) initial_hole_size = hole;
 517          }
 518  
 519          // If named input parameter "fr" is present, push it onto out.params. If
 520          // options are present, push them onto out.params. If both are present,
 521          // throw an error.
 522          if (fr != argsIn.end()) {
 523              if (!options.empty()) {
 524                  throw JSONRPCError(RPC_INVALID_PARAMETER, "Parameter " + fr->first + " conflicts with parameter " + options.getKeys().front());
 525              }
 526              out.params.push_back(*fr->second);
 527              argsIn.erase(fr);
 528          }
 529          if (!options.empty()) {
 530              out.params.push_back(std::move(options));
 531              options = UniValue{UniValue::VOBJ};
 532          }
 533      }
 534      // If leftover "args" param was found, use it as a source of positional
 535      // arguments and add named arguments after. This is a convenience for
 536      // clients that want to pass a combination of named and positional
 537      // arguments as described in doc/JSON-RPC-interface.md#parameter-passing
 538      if (positional_args) {
 539          if (initial_hole_size < (int)positional_args.mapped()->size() && initial_param) {
 540              throw JSONRPCError(RPC_INVALID_PARAMETER, "Parameter " + *initial_param + " specified twice both as positional and named argument");
 541          }
 542          // Assign positional_args to out.params and append named_args after.
 543          UniValue named_args{std::move(out.params)};
 544          out.params = *positional_args.mapped();
 545          for (size_t i{out.params.size()}; i < named_args.size(); ++i) {
 546              out.params.push_back(named_args[i]);
 547          }
 548      }
 549      // If there are still arguments in the argsIn map, this is an error.
 550      if (!argsIn.empty()) {
 551          if (options.isNull()) {
 552              throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both 'options' and named parameter " + argsIn.begin()->first);
 553          }
 554          throw JSONRPCError(RPC_INVALID_PARAMETER, "Unknown named parameter " + argsIn.begin()->first);
 555      }
 556      // Return request with named arguments transformed to positional arguments
 557      return out;
 558  }
 559  
 560  static bool ExecuteCommands(const std::vector<const CRPCCommand*>& commands, const JSONRPCRequest& request, UniValue& result)
 561  {
 562      for (const auto& command : commands) {
 563          if (ExecuteCommand(*command, request, result, &command == &commands.back())) {
 564              return true;
 565          }
 566      }
 567      return false;
 568  }
 569  
 570  UniValue CRPCTable::execute(const std::string method, const JSONRPCRequest &request) const
 571  {
 572      // Return immediately if in warmup
 573      {
 574          LOCK(g_rpc_warmup_mutex);
 575          if (fRPCInWarmup)
 576              throw JSONRPCError(RPC_IN_WARMUP, rpcWarmupStatus);
 577      }
 578  
 579      // Find method
 580      auto it = mapCommands.find(method);
 581      if (it != mapCommands.end()) {
 582          UniValue result;
 583          if (ExecuteCommands(it->second, request, result)) {
 584              return result;
 585          }
 586      }
 587      throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found");
 588  }
 589  
 590  UniValue CRPCTable::execute(const JSONRPCRequest &request) const
 591  {
 592      return this->execute(request.strMethod, request);
 593  }
 594  
 595  static bool ExecuteCommand(const CRPCCommand& command, const JSONRPCRequest& request, UniValue& result, bool last_handler)
 596  {
 597      try {
 598          RPCCommandExecution execution(request.strMethod);
 599          // Execute, convert arguments to array if necessary
 600          if (request.params.isObject()) {
 601              return command.actor(transformNamedArguments(request, command.argNames), result, last_handler);
 602          } else {
 603              return command.actor(request, result, last_handler);
 604          }
 605      } catch (const UniValue::type_error& e) {
 606          throw JSONRPCError(RPC_TYPE_ERROR, e.what());
 607      } catch (const std::exception& e) {
 608          throw JSONRPCError(RPC_MISC_ERROR, e.what());
 609      }
 610  }
 611  
 612  std::vector<std::string> CRPCTable::listCommands() const
 613  {
 614      std::vector<std::string> commandList;
 615      commandList.reserve(mapCommands.size());
 616      for (const auto& i : mapCommands) commandList.emplace_back(i.first);
 617      return commandList;
 618  }
 619  
 620  UniValue CRPCTable::dumpArgMap(const JSONRPCRequest& args_request) const
 621  {
 622      JSONRPCRequest request = args_request;
 623      request.mode = JSONRPCRequest::GET_ARGS;
 624  
 625      UniValue ret{UniValue::VARR};
 626      for (const auto& cmd : mapCommands) {
 627          UniValue result;
 628          if (ExecuteCommands(cmd.second, request, result)) {
 629              for (const auto& values : result.getValues()) {
 630                  ret.push_back(values);
 631              }
 632          }
 633      }
 634      return ret;
 635  }
 636  
 637  void RPCSetTimerInterfaceIfUnset(RPCTimerInterface *iface)
 638  {
 639      if (!timerInterface)
 640          timerInterface = iface;
 641  }
 642  
 643  void RPCSetTimerInterface(RPCTimerInterface *iface)
 644  {
 645      timerInterface = iface;
 646  }
 647  
 648  void RPCUnsetTimerInterface(RPCTimerInterface *iface)
 649  {
 650      if (timerInterface == iface)
 651          timerInterface = nullptr;
 652  }
 653  
 654  void RPCRunLater(const std::string& name, std::function<void()> func, int64_t nSeconds)
 655  {
 656      if (!timerInterface)
 657          throw JSONRPCError(RPC_INTERNAL_ERROR, "No timer handler registered for RPC");
 658      LOCK(g_deadline_timers_mutex);
 659      deadlineTimers.erase(name);
 660      LogDebug(BCLog::RPC, "queue run of timer %s in %i seconds (using %s)\n", name, nSeconds, timerInterface->Name());
 661      deadlineTimers.emplace(name, std::unique_ptr<RPCTimerBase>(timerInterface->NewTimer(func, nSeconds*1000)));
 662  }
 663  
 664  CRPCTable tableRPC;
 665