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