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