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