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