diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp index d3b728831..6fa045d06 100644 --- a/src/interfaces/node.cpp +++ b/src/interfaces/node.cpp @@ -1,368 +1,376 @@ // Copyright (c) 2018 The Bitcoin Core developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(HAVE_CONFIG_H) #include #endif #include #include class HTTPRPCRequestProcessor; class CWallet; fs::path GetWalletDir(); std::vector ListWalletDir(); std::vector> GetWallets(); std::shared_ptr LoadWallet(const CChainParams &chainParams, interfaces::Chain &chain, const std::string &name, bilingual_str &error, std::vector &warnings); WalletCreationStatus CreateWallet(const CChainParams ¶ms, interfaces::Chain &chain, const SecureString &passphrase, uint64_t wallet_creation_flags, const std::string &name, bilingual_str &error, std::vector &warnings, std::shared_ptr &result); std::unique_ptr HandleLoadWallet(interfaces::Node::LoadWalletFn load_wallet); namespace interfaces { namespace { class NodeImpl : public Node { public: NodeImpl(NodeContext *context) { setContext(context); } void initLogging() override { InitLogging(*Assert(m_context->args)); } void initParameterInteraction() override { InitParameterInteraction(*Assert(m_context->args)); } std::string getWarnings() override { return GetWarnings(true); } bool baseInitialize(Config &config) override { return AppInitBasicSetup(gArgs) && AppInitParameterInteraction(config, gArgs) && AppInitSanityChecks() && AppInitLockDataDirectory(); } bool appInitMain(Config &config, RPCServer &rpcServer, HTTPRPCRequestProcessor &httpRPCRequestProcessor) override { m_context->chain = MakeChain(*m_context, config.GetChainParams()); return AppInitMain(config, rpcServer, httpRPCRequestProcessor, *m_context); } void appShutdown() override { Interrupt(*m_context); Shutdown(*m_context); } - void startShutdown() override { StartShutdown(); } + void startShutdown() override { + StartShutdown(); + // Stop RPC for clean shutdown if any of waitfor* commands is + // executed. + if (gArgs.GetBoolArg("-server", false)) { + InterruptRPC(); + StopRPC(); + } + } bool shutdownRequested() override { return ShutdownRequested(); } void mapPort(bool use_upnp) override { if (use_upnp) { StartMapPort(); } else { InterruptMapPort(); StopMapPort(); } } bool getProxy(Network net, proxyType &proxy_info) override { return GetProxy(net, proxy_info); } size_t getNodeCount(CConnman::NumConnections flags) override { return m_context->connman ? m_context->connman->GetNodeCount(flags) : 0; } bool getNodesStats(NodesStats &stats) override { stats.clear(); if (m_context->connman) { std::vector stats_temp; m_context->connman->GetNodeStats(stats_temp); stats.reserve(stats_temp.size()); for (auto &node_stats_temp : stats_temp) { stats.emplace_back(std::move(node_stats_temp), false, CNodeStateStats()); } // Try to retrieve the CNodeStateStats for each node. TRY_LOCK(::cs_main, lockMain); if (lockMain) { for (auto &node_stats : stats) { std::get<1>(node_stats) = GetNodeStateStats(std::get<0>(node_stats).nodeid, std::get<2>(node_stats)); } } return true; } return false; } bool getBanned(banmap_t &banmap) override { if (m_context->banman) { m_context->banman->GetBanned(banmap); return true; } return false; } bool ban(const CNetAddr &net_addr, int64_t ban_time_offset) override { if (m_context->banman) { m_context->banman->Ban(net_addr, ban_time_offset); return true; } return false; } bool unban(const CSubNet &ip) override { if (m_context->banman) { m_context->banman->Unban(ip); return true; } return false; } bool disconnectByAddress(const CNetAddr &net_addr) override { if (m_context->connman) { return m_context->connman->DisconnectNode(net_addr); } return false; } bool disconnectById(NodeId id) override { if (m_context->connman) { return m_context->connman->DisconnectNode(id); } return false; } int64_t getTotalBytesRecv() override { return m_context->connman ? m_context->connman->GetTotalBytesRecv() : 0; } int64_t getTotalBytesSent() override { return m_context->connman ? m_context->connman->GetTotalBytesSent() : 0; } size_t getMempoolSize() override { return m_context->mempool ? m_context->mempool->size() : 0; } size_t getMempoolDynamicUsage() override { return m_context->mempool ? m_context->mempool->DynamicMemoryUsage() : 0; } bool getHeaderTip(int &height, int64_t &block_time) override { LOCK(::cs_main); if (::pindexBestHeader) { height = ::pindexBestHeader->nHeight; block_time = ::pindexBestHeader->GetBlockTime(); return true; } return false; } int getNumBlocks() override { LOCK(::cs_main); return ::ChainActive().Height(); } BlockHash getBestBlockHash() override { const CBlockIndex *tip = WITH_LOCK(::cs_main, return ::ChainActive().Tip()); return tip ? tip->GetBlockHash() : Params().GenesisBlock().GetHash(); } int64_t getLastBlockTime() override { LOCK(::cs_main); if (::ChainActive().Tip()) { return ::ChainActive().Tip()->GetBlockTime(); } // Genesis block's time of current network return Params().GenesisBlock().GetBlockTime(); } double getVerificationProgress() override { const CBlockIndex *tip; { LOCK(::cs_main); tip = ::ChainActive().Tip(); } return GuessVerificationProgress(Params().TxData(), tip); } bool isInitialBlockDownload() override { return ::ChainstateActive().IsInitialBlockDownload(); } bool getReindex() override { return ::fReindex; } bool getImporting() override { return ::fImporting; } void setNetworkActive(bool active) override { if (m_context->connman) { m_context->connman->SetNetworkActive(active); } } bool getNetworkActive() override { return m_context->connman && m_context->connman->GetNetworkActive(); } CFeeRate estimateSmartFee() override { return m_context->mempool ? m_context->mempool->estimateFee() : CFeeRate(); } CFeeRate getDustRelayFee() override { return ::dustRelayFee; } UniValue executeRpc(Config &config, const std::string &command, const UniValue ¶ms, const std::string &uri) override { JSONRPCRequest req(m_context_ref); req.params = params; req.strMethod = command; req.URI = uri; return ::tableRPC.execute(config, req); } std::vector listRpcCommands() override { return ::tableRPC.listCommands(); } void rpcSetTimerInterfaceIfUnset(RPCTimerInterface *iface) override { RPCSetTimerInterfaceIfUnset(iface); } void rpcUnsetTimerInterface(RPCTimerInterface *iface) override { RPCUnsetTimerInterface(iface); } bool getUnspentOutput(const COutPoint &output, Coin &coin) override { LOCK(::cs_main); return ::ChainstateActive().CoinsTip().GetCoin(output, coin); } std::string getWalletDir() override { return GetWalletDir().string(); } std::vector listWalletDir() override { std::vector paths; for (auto &path : ListWalletDir()) { paths.push_back(path.string()); } return paths; } std::vector> getWallets() override { std::vector> wallets; for (auto &client : m_context->chain_clients) { auto client_wallets = client->getWallets(); std::move(client_wallets.begin(), client_wallets.end(), std::back_inserter(wallets)); } return wallets; } std::unique_ptr loadWallet(const CChainParams ¶ms, const std::string &name, bilingual_str &error, std::vector &warnings) const override { return MakeWallet( LoadWallet(params, *m_context->chain, name, error, warnings)); } std::unique_ptr createWallet(const CChainParams ¶ms, const SecureString &passphrase, uint64_t wallet_creation_flags, const std::string &name, bilingual_str &error, std::vector &warnings, WalletCreationStatus &status) override { std::shared_ptr wallet; status = CreateWallet(params, *m_context->chain, passphrase, wallet_creation_flags, name, error, warnings, wallet); return MakeWallet(wallet); } std::unique_ptr handleInitMessage(InitMessageFn fn) override { return MakeHandler(::uiInterface.InitMessage_connect(fn)); } std::unique_ptr handleMessageBox(MessageBoxFn fn) override { return MakeHandler(::uiInterface.ThreadSafeMessageBox_connect(fn)); } std::unique_ptr handleQuestion(QuestionFn fn) override { return MakeHandler(::uiInterface.ThreadSafeQuestion_connect(fn)); } std::unique_ptr handleShowProgress(ShowProgressFn fn) override { return MakeHandler(::uiInterface.ShowProgress_connect(fn)); } std::unique_ptr handleLoadWallet(LoadWalletFn fn) override { return HandleLoadWallet(std::move(fn)); } std::unique_ptr handleNotifyNumConnectionsChanged( NotifyNumConnectionsChangedFn fn) override { return MakeHandler( ::uiInterface.NotifyNumConnectionsChanged_connect(fn)); } std::unique_ptr handleNotifyNetworkActiveChanged( NotifyNetworkActiveChangedFn fn) override { return MakeHandler( ::uiInterface.NotifyNetworkActiveChanged_connect(fn)); } std::unique_ptr handleNotifyAlertChanged(NotifyAlertChangedFn fn) override { return MakeHandler(::uiInterface.NotifyAlertChanged_connect(fn)); } std::unique_ptr handleBannedListChanged(BannedListChangedFn fn) override { return MakeHandler(::uiInterface.BannedListChanged_connect(fn)); } std::unique_ptr handleNotifyBlockTip(NotifyBlockTipFn fn) override { return MakeHandler(::uiInterface.NotifyBlockTip_connect( [fn](SynchronizationState sync_state, const CBlockIndex *block) { fn(sync_state, BlockTip{block->nHeight, block->GetBlockTime(), block->GetBlockHash()}, GuessVerificationProgress(Params().TxData(), block)); })); } std::unique_ptr handleNotifyHeaderTip(NotifyHeaderTipFn fn) override { /* verification progress is unused when a header was received */ return MakeHandler(::uiInterface.NotifyHeaderTip_connect( [fn](SynchronizationState sync_state, const CBlockIndex *block) { fn(sync_state, BlockTip{block->nHeight, block->GetBlockTime(), block->GetBlockHash()}, 0); })); } NodeContext *context() override { return m_context; } void setContext(NodeContext *context) override { m_context = context; if (context) { m_context_ref.Set(*context); } else { m_context_ref.Clear(); } } NodeContext *m_context{nullptr}; util::Ref m_context_ref{m_context}; }; } // namespace std::unique_ptr MakeNode(NodeContext *context) { return std::make_unique(context); } } // namespace interfaces diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 456d1012b..ae2e2ae5e 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -1,563 +1,576 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2009-2018 The Bitcoin Core developers // Copyright (c) 2018-2019 The Bitcoin developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include #include #include #include #include #include #include #include #include +#include #include // for unique_ptr +#include #include #include static RecursiveMutex cs_rpcWarmup; static std::atomic g_rpc_running{false}; +static std::once_flag g_rpc_interrupt_flag; +static std::once_flag g_rpc_stop_flag; static bool fRPCInWarmup GUARDED_BY(cs_rpcWarmup) = true; static std::string rpcWarmupStatus GUARDED_BY(cs_rpcWarmup) = "RPC server started"; /* Timer-creating functions */ static RPCTimerInterface *timerInterface = nullptr; /* Map of name to timer. */ static Mutex g_deadline_timers_mutex; static std::map> deadlineTimers GUARDED_BY(g_deadline_timers_mutex); static bool ExecuteCommand(Config &config, const CRPCCommand &command, const JSONRPCRequest &request, UniValue &result, bool last_handler); struct RPCCommandExecutionInfo { std::string method; int64_t start; }; struct RPCServerInfo { Mutex mutex; std::list active_commands GUARDED_BY(mutex); }; static RPCServerInfo g_rpc_server_info; struct RPCCommandExecution { std::list::iterator it; explicit RPCCommandExecution(const std::string &method) { LOCK(g_rpc_server_info.mutex); it = g_rpc_server_info.active_commands.insert( g_rpc_server_info.active_commands.cend(), {method, GetTimeMicros()}); } ~RPCCommandExecution() { LOCK(g_rpc_server_info.mutex); g_rpc_server_info.active_commands.erase(it); } }; UniValue RPCServer::ExecuteCommand(Config &config, const JSONRPCRequest &request) const { // Return immediately if in warmup // This is retained from the old RPC implementation because a lot of state // is set during warmup that RPC commands may depend on. This can be // safely removed once global variable usage has been eliminated. { LOCK(cs_rpcWarmup); if (fRPCInWarmup) { throw JSONRPCError(RPC_IN_WARMUP, rpcWarmupStatus); } } std::string commandName = request.strMethod; { auto commandsReadView = commands.getReadView(); auto iter = commandsReadView->find(commandName); if (iter != commandsReadView.end()) { return iter->second.get()->Execute(request); } } // TODO Remove the below call to tableRPC.execute() and only call it for // context-free RPC commands via an implementation of RPCCommand. // Check if context-free RPC method is valid and execute it return tableRPC.execute(config, request); } void RPCServer::RegisterCommand(std::unique_ptr command) { if (command != nullptr) { const std::string &commandName = command->GetName(); commands.getWriteView()->insert( std::make_pair(commandName, std::move(command))); } } static struct CRPCSignals { boost::signals2::signal Started; boost::signals2::signal Stopped; } g_rpcSignals; void RPCServerSignals::OnStarted(std::function slot) { g_rpcSignals.Started.connect(slot); } void RPCServerSignals::OnStopped(std::function slot) { g_rpcSignals.Stopped.connect(slot); } std::string CRPCTable::help(Config &config, const std::string &strCommand, const JSONRPCRequest &helpreq) const { std::string strRet; std::string category; std::set setDone; std::vector> vCommands; for (const auto &entry : mapCommands) { vCommands.push_back( std::make_pair(entry.second.front()->category + entry.first, entry.second.front())); } sort(vCommands.begin(), vCommands.end()); JSONRPCRequest jreq(helpreq); jreq.fHelp = true; jreq.params = UniValue(); for (const std::pair &command : vCommands) { const CRPCCommand *pcmd = command.second; std::string strMethod = pcmd->name; if ((strCommand != "" || pcmd->category == "hidden") && strMethod != strCommand) { continue; } jreq.strMethod = strMethod; try { UniValue unused_result; if (setDone.insert(pcmd->unique_id).second) { pcmd->actor(config, jreq, unused_result, true /* last_handler */); } } catch (const std::exception &e) { // Help text is returned in an exception std::string strHelp = std::string(e.what()); if (strCommand == "") { if (strHelp.find('\n') != std::string::npos) { strHelp = strHelp.substr(0, strHelp.find('\n')); } if (category != pcmd->category) { if (!category.empty()) { strRet += "\n"; } category = pcmd->category; strRet += "== " + Capitalize(category) + " ==\n"; } } strRet += strHelp + "\n"; } } if (strRet == "") { strRet = strprintf("help: unknown command: %s\n", strCommand); } strRet = strRet.substr(0, strRet.size() - 1); return strRet; } static UniValue help(Config &config, const JSONRPCRequest &jsonRequest) { if (jsonRequest.fHelp || jsonRequest.params.size() > 1) { throw std::runtime_error(RPCHelpMan{ "help", "List all commands, or get help for a specified command.\n", { {"command", RPCArg::Type::STR, /* default */ "all commands", "The command to get help on"}, }, RPCResult{RPCResult::Type::STR, "", "The help text"}, RPCExamples{""}, } .ToString()); } std::string strCommand; if (jsonRequest.params.size() > 0) { strCommand = jsonRequest.params[0].get_str(); } return tableRPC.help(config, strCommand, jsonRequest); } static UniValue stop(const Config &config, const JSONRPCRequest &jsonRequest) { static const std::string RESULT{PACKAGE_NAME " stopping"}; // Accept the deprecated and ignored 'detach' boolean argument // Also accept the hidden 'wait' integer argument (milliseconds) // For instance, 'stop 1000' makes the call wait 1 second before returning // to the client (intended for testing) if (jsonRequest.fHelp || jsonRequest.params.size() > 1) { throw std::runtime_error(RPCHelpMan{ "stop", "\nRequest a graceful shutdown of " PACKAGE_NAME ".", {}, RPCResult{RPCResult::Type::STR, "", "A string with the content '" + RESULT + "'"}, RPCExamples{""}, } .ToString()); } // Event loop will exit after current HTTP requests have been handled, so // this reply will get back to the client. StartShutdown(); if (jsonRequest.params[0].isNum()) { UninterruptibleSleep( std::chrono::milliseconds{jsonRequest.params[0].get_int()}); } return RESULT; } static UniValue uptime(const Config &config, const JSONRPCRequest &request) { RPCHelpMan{ "uptime", "Returns the total uptime of the server.\n", {}, RPCResult{RPCResult::Type::NUM, "", "The number of seconds that the server has been running"}, RPCExamples{HelpExampleCli("uptime", "") + HelpExampleRpc("uptime", "")}, } .Check(request); return GetTime() - GetStartupTime(); } static UniValue getrpcinfo(const Config &config, const JSONRPCRequest &request) { RPCHelpMan{ "getrpcinfo", "Returns details of the RPC server.\n", {}, RPCResult{RPCResult::Type::OBJ, "", "", { {RPCResult::Type::ARR, "active_commands", "All active commands", { {RPCResult::Type::OBJ, "", "Information about an active command", { {RPCResult::Type::STR, "method", "The name of the RPC command"}, {RPCResult::Type::NUM, "duration", "The running time in microseconds"}, }}, }}, {RPCResult::Type::STR, "logpath", "The complete file path to the debug log"}, }}, RPCExamples{HelpExampleCli("getrpcinfo", "") + HelpExampleRpc("getrpcinfo", "")}, } .Check(request); LOCK(g_rpc_server_info.mutex); UniValue active_commands(UniValue::VARR); for (const RPCCommandExecutionInfo &info : g_rpc_server_info.active_commands) { UniValue entry(UniValue::VOBJ); entry.pushKV("method", info.method); entry.pushKV("duration", GetTimeMicros() - info.start); active_commands.push_back(entry); } UniValue result(UniValue::VOBJ); result.pushKV("active_commands", active_commands); const std::string path = LogInstance().m_file_path.string(); UniValue log_path(UniValue::VSTR, path); result.pushKV("logpath", log_path); return result; } // clang-format off static const CRPCCommand vRPCCommands[] = { // category name actor (function) argNames // ------------------- ------------------------ ---------------------- ---------- /* Overall control/query calls */ { "control", "getrpcinfo", getrpcinfo, {} }, { "control", "help", help, {"command"} }, { "control", "stop", stop, {"wait"} }, { "control", "uptime", uptime, {} }, }; // clang-format on CRPCTable::CRPCTable() { unsigned int vcidx; for (vcidx = 0; vcidx < (sizeof(vRPCCommands) / sizeof(vRPCCommands[0])); vcidx++) { const CRPCCommand *pcmd; pcmd = &vRPCCommands[vcidx]; mapCommands[pcmd->name].push_back(pcmd); } } bool CRPCTable::appendCommand(const std::string &name, const CRPCCommand *pcmd) { if (IsRPCRunning()) { return false; } mapCommands[name].push_back(pcmd); return true; } bool CRPCTable::removeCommand(const std::string &name, const CRPCCommand *pcmd) { auto it = mapCommands.find(name); if (it != mapCommands.end()) { auto new_end = std::remove(it->second.begin(), it->second.end(), pcmd); if (it->second.end() != new_end) { it->second.erase(new_end, it->second.end()); return true; } } return false; } void StartRPC() { LogPrint(BCLog::RPC, "Starting RPC\n"); g_rpc_running = true; g_rpcSignals.Started(); } void InterruptRPC() { - LogPrint(BCLog::RPC, "Interrupting RPC\n"); - // Interrupt e.g. running longpolls - g_rpc_running = false; + // This function could be called twice if the GUI has been started with + // -server=1. + std::call_once(g_rpc_interrupt_flag, []() { + LogPrint(BCLog::RPC, "Interrupting RPC\n"); + // Interrupt e.g. running longpolls + g_rpc_running = false; + }); } void StopRPC() { - LogPrint(BCLog::RPC, "Stopping RPC\n"); - WITH_LOCK(g_deadline_timers_mutex, deadlineTimers.clear()); - DeleteAuthCookie(); - g_rpcSignals.Stopped(); + // This function could be called twice if the GUI has been started with + // -server=1. + assert(!g_rpc_running); + std::call_once(g_rpc_stop_flag, []() { + LogPrint(BCLog::RPC, "Stopping RPC\n"); + WITH_LOCK(g_deadline_timers_mutex, deadlineTimers.clear()); + DeleteAuthCookie(); + g_rpcSignals.Stopped(); + }); } bool IsRPCRunning() { return g_rpc_running; } void RpcInterruptionPoint() { if (!IsRPCRunning()) { throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Shutting down"); } } void SetRPCWarmupStatus(const std::string &newStatus) { LOCK(cs_rpcWarmup); rpcWarmupStatus = newStatus; } void SetRPCWarmupFinished() { LOCK(cs_rpcWarmup); assert(fRPCInWarmup); fRPCInWarmup = false; } bool RPCIsInWarmup(std::string *outStatus) { LOCK(cs_rpcWarmup); if (outStatus) { *outStatus = rpcWarmupStatus; } return fRPCInWarmup; } bool IsDeprecatedRPCEnabled(const ArgsManager &args, const std::string &method) { const std::vector enabled_methods = args.GetArgs("-deprecatedrpc"); return find(enabled_methods.begin(), enabled_methods.end(), method) != enabled_methods.end(); } static UniValue JSONRPCExecOne(Config &config, RPCServer &rpcServer, JSONRPCRequest jreq, const UniValue &req) { UniValue rpc_result(UniValue::VOBJ); try { jreq.parse(req); UniValue result = rpcServer.ExecuteCommand(config, jreq); rpc_result = JSONRPCReplyObj(result, NullUniValue, jreq.id); } catch (const UniValue &objError) { rpc_result = JSONRPCReplyObj(NullUniValue, objError, jreq.id); } catch (const std::exception &e) { rpc_result = JSONRPCReplyObj( NullUniValue, JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id); } return rpc_result; } std::string JSONRPCExecBatch(Config &config, RPCServer &rpcServer, const JSONRPCRequest &jreq, const UniValue &vReq) { UniValue ret(UniValue::VARR); for (size_t i = 0; i < vReq.size(); i++) { ret.push_back(JSONRPCExecOne(config, rpcServer, jreq, vReq[i])); } 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; i < keys.size(); ++i) { argsIn[keys[i]] = &values[i]; } // Process expected parameters. int hole = 0; for (const std::string &argNamePattern : argNames) { std::vector vargNames; boost::algorithm::split(vargNames, argNamePattern, boost::algorithm::is_any_of("|")); auto fr = argsIn.end(); for (const std::string &argName : vargNames) { fr = argsIn.find(argName); if (fr != argsIn.end()) { break; } } if (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 that // act based on number of specified parameters). out.params.push_back(UniValue()); } hole = 0; out.params.push_back(*fr->second); 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(Config &config, const JSONRPCRequest &request) const { // Return immediately if in warmup { LOCK(cs_rpcWarmup); if (fRPCInWarmup) { throw JSONRPCError(RPC_IN_WARMUP, rpcWarmupStatus); } } // Find method auto it = mapCommands.find(request.strMethod); if (it != mapCommands.end()) { UniValue result; for (const auto &command : it->second) { if (ExecuteCommand(config, *command, request, result, &command == &it->second.back())) { return result; } } } throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found"); } static bool ExecuteCommand(Config &config, const CRPCCommand &command, const JSONRPCRequest &request, UniValue &result, bool last_handler) { try { RPCCommandExecution execution(request.strMethod); // Execute, convert arguments to array if necessary if (request.params.isObject()) { return command.actor( config, transformNamedArguments(request, command.argNames), result, last_handler); } else { return command.actor(config, request, result, last_handler); } } catch (const std::exception &e) { throw JSONRPCError(RPC_MISC_ERROR, e.what()); } } std::vector CRPCTable::listCommands() const { std::vector commandList; for (const auto &i : mapCommands) { commandList.emplace_back(i.first); } return commandList; } void RPCSetTimerInterfaceIfUnset(RPCTimerInterface *iface) { if (!timerInterface) { timerInterface = iface; } } void RPCSetTimerInterface(RPCTimerInterface *iface) { timerInterface = iface; } void RPCUnsetTimerInterface(RPCTimerInterface *iface) { if (timerInterface == iface) { timerInterface = nullptr; } } void RPCRunLater(const std::string &name, std::function func, int64_t nSeconds) { if (!timerInterface) { throw JSONRPCError(RPC_INTERNAL_ERROR, "No timer handler registered for RPC"); } LOCK(g_deadline_timers_mutex); deadlineTimers.erase(name); LogPrint(BCLog::RPC, "queue run of timer %s in %i seconds (using %s)\n", name, nSeconds, timerInterface->Name()); deadlineTimers.emplace( name, std::unique_ptr( timerInterface->NewTimer(func, nSeconds * 1000))); } int RPCSerializationFlags() { return 0; } CRPCTable tableRPC;