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