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 : 25040 : 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 : 12520 : explicit RPCCommandExecution(const std::string& method)
57 : 12520 : {
58 : 12520 : LOCK(g_rpc_server_info.mutex);
59 [ + - + - ]: 12520 : it = g_rpc_server_info.active_commands.insert(g_rpc_server_info.active_commands.end(), {method, SteadyClock::now()});
60 [ + - + - ]: 37560 : }
61 : 12520 : ~RPCCommandExecution()
62 : : {
63 : 12520 : LOCK(g_rpc_server_info.mutex);
64 [ + - ]: 12520 : g_rpc_server_info.active_commands.erase(it);
65 : 12520 : }
66 : : };
67 : :
68 : 28 : std::string CRPCTable::help(const std::string& strCommand, const JSONRPCRequest& helpreq) const
69 : : {
70 [ + - ]: 28 : std::string strRet;
71 : 28 : std::string category;
72 [ + - ]: 28 : std::set<intptr_t> setDone;
73 : 28 : std::vector<std::pair<std::string, const CRPCCommand*> > vCommands;
74 [ + - ]: 28 : vCommands.reserve(mapCommands.size());
75 : :
76 [ + + ]: 3052 : for (const auto& entry : mapCommands)
77 [ + - + - ]: 6048 : vCommands.emplace_back(entry.second.front()->category + entry.first, entry.second.front());
78 : 28 : std::ranges::sort(vCommands);
79 : :
80 [ + - ]: 28 : JSONRPCRequest jreq = helpreq;
81 : 28 : jreq.mode = JSONRPCRequest::GET_HELP;
82 : 28 : jreq.params = UniValue();
83 : :
84 [ + - + + ]: 3052 : for (const auto& [_, pcmd] : vCommands) {
85 [ + - ]: 3024 : std::string strMethod = pcmd->name;
86 [ + + + + : 3024 : if ((strCommand != "" || pcmd->category == "hidden") && strMethod != strCommand)
+ + ]
87 : 2287 : continue;
88 [ + - ]: 737 : jreq.strMethod = strMethod;
89 : 737 : try
90 : : {
91 [ + - ]: 737 : UniValue unused_result;
92 [ + - + - ]: 737 : if (setDone.insert(pcmd->unique_id).second)
93 [ - + ]: 737 : pcmd->actor(jreq, unused_result, /*last_handler=*/true);
94 [ - + ]: 1474 : } catch (const HelpResult& e) {
95 [ + - ]: 737 : std::string strHelp{e.what()};
96 [ + + ]: 737 : if (strCommand == "")
97 : : {
98 [ + - ]: 720 : if (strHelp.find('\n') != std::string::npos)
99 [ + - ]: 720 : strHelp = strHelp.substr(0, strHelp.find('\n'));
100 : :
101 [ + + ]: 720 : if (category != pcmd->category)
102 : : {
103 [ + + ]: 48 : if (!category.empty())
104 [ + - ]: 40 : strRet += "\n";
105 [ + - ]: 48 : category = pcmd->category;
106 [ + - + - : 144 : strRet += "== " + Capitalize(category) + " ==\n";
+ - + - ]
107 : : }
108 : : }
109 [ + - ]: 1474 : strRet += strHelp + "\n";
110 [ + - ]: 737 : }
111 : 3024 : }
112 [ + + ]: 28 : if (strRet == "")
113 [ + - ]: 3 : strRet = strprintf("help: unknown command: %s\n", strCommand);
114 [ + - ]: 28 : strRet = strRet.substr(0,strRet.size()-1);
115 : 56 : return strRet;
116 : 28 : }
117 : :
118 : 494 : static RPCHelpMan help()
119 : : {
120 : 494 : return RPCHelpMan{
121 : : "help",
122 : : "List all commands, or get help for a specified command.\n",
123 : : {
124 [ + - ]: 988 : {"command", RPCArg::Type::STR, RPCArg::DefaultHint{"all commands"}, "The command to get help on"},
125 : : },
126 : : {
127 [ + - + - : 988 : RPCResult{RPCResult::Type::STR, "", "The help text"},
+ - ]
128 [ + - + - : 988 : RPCResult{RPCResult::Type::ANY, "", ""},
+ - ]
129 : : },
130 [ + - + - ]: 1482 : RPCExamples{""},
131 : 29 : [&](const RPCHelpMan& self, const JSONRPCRequest& jsonRequest) -> UniValue
132 : : {
133 [ + + ]: 29 : std::string strCommand;
134 [ + + ]: 29 : if (jsonRequest.params.size() > 0) {
135 [ + - + - : 26 : strCommand = jsonRequest.params[0].get_str();
+ - ]
136 : : }
137 [ + + ]: 29 : if (strCommand == "dump_all_command_conversions") {
138 : : // Used for testing only, undocumented
139 [ + - ]: 1 : return tableRPC.dumpArgMap(jsonRequest);
140 : : }
141 : :
142 [ + - + - ]: 56 : return tableRPC.help(strCommand, jsonRequest);
143 : 29 : },
144 [ + - + - : 6916 : };
+ - + - +
- + - + -
+ + + + -
- - - ]
145 [ + - + - : 2964 : }
+ - + - +
- - - ]
146 : :
147 : 463 : static RPCHelpMan stop()
148 : : {
149 [ + + + - : 463 : static const std::string RESULT{CLIENT_NAME " stopping"};
+ - ]
150 : 463 : 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 [ + - + - ]: 926 : {"wait", RPCArg::Type::NUM, RPCArg::Optional::OMITTED, "how long to wait in ms", RPCArgOptions{.hidden=true}},
158 : : },
159 [ + - + - : 1389 : RPCResult{RPCResult::Type::STR, "", "A string with the content '" + RESULT + "'"},
+ - ]
160 [ + - + - ]: 1389 : RPCExamples{""},
161 : 0 : [&](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 : 0 : CHECK_NONFATAL((CHECK_NONFATAL(EnsureAnyNodeContext(jsonRequest.context).shutdown_request))());
166 [ # # ]: 0 : if (jsonRequest.params[0].isNum()) {
167 : 0 : UninterruptibleSleep(std::chrono::milliseconds{jsonRequest.params[0].getInt<int>()});
168 : : }
169 : 0 : return RESULT;
170 : : },
171 [ + - + - : 4167 : };
+ - + - +
- + - + +
- - ]
172 [ + - + - ]: 926 : }
173 : :
174 : 490 : static RPCHelpMan uptime()
175 : : {
176 : 490 : 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 [ + - + - : 980 : },
+ - ]
183 : 490 : RPCExamples{
184 [ + - + - : 980 : HelpExampleCli("uptime", "")
+ - ]
185 [ + - + - : 1960 : + HelpExampleRpc("uptime", "")
+ - + - ]
186 [ + - ]: 490 : },
187 : 2 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
188 : : {
189 : 2 : return GetTime() - GetStartupTime();
190 : : }
191 [ + - + - : 2940 : };
+ - + - ]
192 : : }
193 : :
194 : 498 : static RPCHelpMan getrpcinfo()
195 : : {
196 : 498 : 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 [ + - + - : 4482 : },
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
- + - + -
+ - + - +
+ + + + +
- - - - -
- ]
214 : 498 : RPCExamples{
215 [ + - + - : 996 : HelpExampleCli("getrpcinfo", "")
+ - ]
216 [ + - + - : 2490 : + HelpExampleRpc("getrpcinfo", "")},
+ - + - +
- ]
217 : 1 : [&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
218 : : {
219 : 1 : LOCK(g_rpc_server_info.mutex);
220 : 1 : UniValue active_commands(UniValue::VARR);
221 [ + + ]: 2 : for (const RPCCommandExecutionInfo& info : g_rpc_server_info.active_commands) {
222 : 1 : UniValue entry(UniValue::VOBJ);
223 [ + - + - : 2 : entry.pushKV("method", info.method);
+ - ]
224 [ + - + - : 2 : entry.pushKV("duration", int64_t{Ticks<std::chrono::microseconds>(SteadyClock::now() - info.start)});
+ - ]
225 [ + - ]: 1 : active_commands.push_back(std::move(entry));
226 : 1 : }
227 : :
228 : 1 : UniValue result(UniValue::VOBJ);
229 [ + - + - ]: 2 : result.pushKV("active_commands", std::move(active_commands));
230 : :
231 [ + - + - ]: 1 : const std::string path = LogInstance().m_file_path.utf8string();
232 [ + - ]: 2 : UniValue log_path(UniValue::VSTR, path);
233 [ + - + - ]: 2 : result.pushKV("logpath", std::move(log_path));
234 : :
235 : 2 : return result;
236 [ + - ]: 2 : }
237 [ + - + - : 2988 : };
+ - + - ]
238 [ + - + - : 2988 : }
+ - + - +
- + - - -
- - ]
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 : 227 : CRPCTable::CRPCTable()
249 : : {
250 [ + + ]: 1135 : for (const auto& c : vRPCCommands) {
251 [ + - ]: 908 : appendCommand(c.name, &c);
252 : : }
253 : 227 : }
254 : :
255 : 3716 : void CRPCTable::appendCommand(const std::string& name, const CRPCCommand* pcmd)
256 : : {
257 : 3716 : CHECK_NONFATAL(!IsRPCRunning()); // Only add commands before rpc is running
258 : :
259 : 3716 : mapCommands[name].push_back(pcmd);
260 : 3716 : }
261 : :
262 : 0 : bool CRPCTable::removeCommand(const std::string& name, const CRPCCommand* pcmd)
263 : : {
264 : 0 : auto it = mapCommands.find(name);
265 [ # # ]: 0 : if (it != mapCommands.end()) {
266 : 0 : auto new_end = std::remove(it->second.begin(), it->second.end(), pcmd);
267 [ # # ]: 0 : if (it->second.end() != new_end) {
268 : 0 : it->second.erase(new_end, it->second.end());
269 : 0 : return true;
270 : : }
271 : : }
272 : : return false;
273 : : }
274 : :
275 : 0 : void StartRPC()
276 : : {
277 [ # # ]: 0 : LogDebug(BCLog::RPC, "Starting RPC\n");
278 : 0 : g_rpc_running = true;
279 : 0 : }
280 : :
281 : 0 : void InterruptRPC()
282 : : {
283 : 0 : 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 : 0 : std::call_once(g_rpc_interrupt_flag, []() {
286 [ # # ]: 0 : LogDebug(BCLog::RPC, "Interrupting RPC\n");
287 : : // Interrupt e.g. running longpolls
288 : 0 : g_rpc_running = false;
289 : 0 : });
290 : 0 : }
291 : :
292 : 0 : void StopRPC()
293 : : {
294 : 0 : 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 [ # # ]: 0 : assert(!g_rpc_running);
297 : 0 : std::call_once(g_rpc_stop_flag, [&]() {
298 [ # # ]: 0 : LogDebug(BCLog::RPC, "Stopping RPC\n");
299 : 0 : DeleteAuthCookie();
300 [ # # ]: 0 : LogDebug(BCLog::RPC, "RPC stopped.\n");
301 : 0 : });
302 : 0 : }
303 : :
304 : 3716 : bool IsRPCRunning()
305 : : {
306 : 3716 : return g_rpc_running;
307 : : }
308 : :
309 : 0 : void RpcInterruptionPoint()
310 : : {
311 [ # # # # : 0 : if (!IsRPCRunning()) throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Shutting down");
# # ]
312 : 0 : }
313 : :
314 : 0 : void SetRPCWarmupStatus(const std::string& newStatus)
315 : : {
316 : 0 : LOCK(g_rpc_warmup_mutex);
317 [ # # # # ]: 0 : rpcWarmupStatus = newStatus;
318 : 0 : }
319 : :
320 : 1 : void SetRPCWarmupFinished()
321 : : {
322 : 1 : LOCK(g_rpc_warmup_mutex);
323 [ - + ]: 1 : assert(fRPCInWarmup);
324 [ + - ]: 1 : fRPCInWarmup = false;
325 : 1 : }
326 : :
327 : 0 : bool RPCIsInWarmup(std::string *outStatus)
328 : : {
329 : 0 : LOCK(g_rpc_warmup_mutex);
330 [ # # ]: 0 : if (outStatus)
331 [ # # ]: 0 : *outStatus = rpcWarmupStatus;
332 [ # # ]: 0 : return fRPCInWarmup;
333 : 0 : }
334 : :
335 : 1348 : bool IsDeprecatedRPCEnabled(const std::string& method)
336 : : {
337 [ + - ]: 1348 : const std::vector<std::string> enabled_methods = gArgs.GetArgs("-deprecatedrpc");
338 : :
339 : 1348 : return find(enabled_methods.begin(), enabled_methods.end(), method) != enabled_methods.end();
340 : 1348 : }
341 : :
342 : 0 : UniValue JSONRPCExec(const JSONRPCRequest& jreq, bool catch_errors)
343 : : {
344 [ # # ]: 0 : UniValue result;
345 [ # # ]: 0 : if (catch_errors) {
346 : 0 : try {
347 [ # # ]: 0 : result = tableRPC.execute(jreq);
348 [ - - - ]: 0 : } catch (UniValue& e) {
349 [ - - - - : 0 : return JSONRPCReplyObj(NullUniValue, std::move(e), jreq.id, jreq.m_json_version);
- - ]
350 : 0 : } 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 [ # # ]: 0 : result = tableRPC.execute(jreq);
355 : : }
356 : :
357 [ # # # # : 0 : return JSONRPCReplyObj(std::move(result), NullUniValue, jreq.id, jreq.m_json_version);
# # ]
358 : 0 : }
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 : 0 : static inline JSONRPCRequest transformNamedArguments(const JSONRPCRequest& in, const std::vector<std::pair<std::string, bool>>& argNames)
365 : : {
366 : 0 : JSONRPCRequest out = in;
367 : 0 : 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 [ # # ]: 0 : const std::vector<std::string>& keys = in.params.getKeys();
371 [ # # ]: 0 : const std::vector<UniValue>& values = in.params.getValues();
372 : 0 : std::unordered_map<std::string, const UniValue*> argsIn;
373 [ # # ]: 0 : for (size_t i=0; i<keys.size(); ++i) {
374 [ # # # # ]: 0 : auto [_, inserted] = argsIn.emplace(keys[i], &values[i]);
375 [ # # ]: 0 : if (!inserted) {
376 [ # # # # ]: 0 : 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 : 0 : int hole = 0;
388 : 0 : int initial_hole_size = 0;
389 : 0 : const std::string* initial_param = nullptr;
390 : 0 : UniValue options{UniValue::VOBJ};
391 [ # # # # ]: 0 : for (const auto& [argNamePattern, named_only]: argNames) {
392 [ # # ]: 0 : std::vector<std::string> vargNames = SplitString(argNamePattern, '|');
393 : 0 : auto fr = argsIn.end();
394 [ # # ]: 0 : for (const std::string & argName : vargNames) {
395 : 0 : fr = argsIn.find(argName);
396 [ # # ]: 0 : 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 [ # # ]: 0 : if (named_only) {
405 [ # # ]: 0 : if (fr != argsIn.end()) {
406 [ # # # # ]: 0 : if (options.exists(fr->first)) {
407 [ # # # # ]: 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Parameter " + fr->first + " specified multiple times");
408 : : }
409 [ # # # # : 0 : options.pushKVEnd(fr->first, *fr->second);
# # ]
410 : 0 : argsIn.erase(fr);
411 : : }
412 : 0 : continue;
413 : : }
414 : :
415 [ # # # # ]: 0 : if (!options.empty() || fr != argsIn.end()) {
416 [ # # ]: 0 : 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 [ # # ]: 0 : out.params.push_back(UniValue());
421 : : }
422 : 0 : hole = 0;
423 [ # # ]: 0 : if (!initial_param) initial_param = &argNamePattern;
424 : : } else {
425 : 0 : hole += 1;
426 [ # # ]: 0 : 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 [ # # ]: 0 : if (fr != argsIn.end()) {
433 [ # # ]: 0 : if (!options.empty()) {
434 [ # # # # : 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Parameter " + fr->first + " conflicts with parameter " + options.getKeys().front());
# # # # ]
435 : : }
436 [ # # # # ]: 0 : out.params.push_back(*fr->second);
437 : 0 : argsIn.erase(fr);
438 : : }
439 [ # # ]: 0 : if (!options.empty()) {
440 [ # # ]: 0 : out.params.push_back(std::move(options));
441 : 0 : options = UniValue{UniValue::VOBJ};
442 : : }
443 : 0 : }
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 [ # # ]: 0 : auto positional_args{argsIn.extract("args")};
449 [ # # # # ]: 0 : if (positional_args && positional_args.mapped()->isArray()) {
450 [ # # # # ]: 0 : if (initial_hole_size < (int)positional_args.mapped()->size() && initial_param) {
451 [ # # # # ]: 0 : 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 : 0 : UniValue named_args{std::move(out.params)};
455 [ # # ]: 0 : out.params = *positional_args.mapped();
456 [ # # ]: 0 : for (size_t i{out.params.size()}; i < named_args.size(); ++i) {
457 [ # # # # : 0 : out.params.push_back(named_args[i]);
# # ]
458 : : }
459 : 0 : }
460 : : // If there are still arguments in the argsIn map, this is an error.
461 [ # # ]: 0 : if (!argsIn.empty()) {
462 [ # # # # ]: 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Unknown named parameter " + argsIn.begin()->first);
463 : : }
464 : : // Return request with named arguments transformed to positional arguments
465 [ # # ]: 0 : return out;
466 : 0 : }
467 : :
468 : 12520 : static bool ExecuteCommands(const std::vector<const CRPCCommand*>& commands, const JSONRPCRequest& request, UniValue& result)
469 : : {
470 [ + - ]: 12520 : for (const auto& command : commands) {
471 [ - + ]: 12520 : if (ExecuteCommand(*command, request, result, &command == &commands.back())) {
472 : : return true;
473 : : }
474 : : }
475 : : return false;
476 : : }
477 : :
478 : 12412 : UniValue CRPCTable::execute(const JSONRPCRequest &request) const
479 : : {
480 : : // Return immediately if in warmup
481 : 12412 : {
482 : 12412 : LOCK(g_rpc_warmup_mutex);
483 [ - + ]: 12412 : if (fRPCInWarmup)
484 [ # # ]: 0 : throw JSONRPCError(RPC_IN_WARMUP, rpcWarmupStatus);
485 : 0 : }
486 : :
487 : : // Find method
488 : 12412 : auto it = mapCommands.find(request.strMethod);
489 [ + - ]: 12412 : if (it != mapCommands.end()) {
490 [ + + ]: 12412 : UniValue result;
491 [ + + - + ]: 12412 : if (ExecuteCommands(it->second, request, result)) {
492 : 7903 : return result;
493 : : }
494 : 4509 : }
495 [ # # # # ]: 0 : throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found");
496 : : }
497 : :
498 : 12520 : static bool ExecuteCommand(const CRPCCommand& command, const JSONRPCRequest& request, UniValue& result, bool last_handler)
499 : : {
500 : 12520 : try {
501 [ + - ]: 12520 : RPCCommandExecution execution(request.strMethod);
502 : : // Execute, convert arguments to array if necessary
503 [ - + ]: 12520 : if (request.params.isObject()) {
504 [ # # # # ]: 0 : return command.actor(transformNamedArguments(request, command.argNames), result, last_handler);
505 : : } else {
506 [ + + ]: 12520 : return command.actor(request, result, last_handler);
507 : : }
508 [ + + + ]: 17029 : } catch (const UniValue::type_error& e) {
509 [ + - + - ]: 82 : throw JSONRPCError(RPC_TYPE_ERROR, e.what());
510 : 1012 : } catch (const std::exception& e) {
511 [ + - + - ]: 1942 : throw JSONRPCError(RPC_MISC_ERROR, e.what());
512 : 971 : }
513 : : }
514 : :
515 : 1 : std::vector<std::string> CRPCTable::listCommands() const
516 : : {
517 : 1 : std::vector<std::string> commandList;
518 [ + - ]: 1 : commandList.reserve(mapCommands.size());
519 [ + - + + ]: 109 : for (const auto& i : mapCommands) commandList.emplace_back(i.first);
520 : 1 : 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 [ + + ]: 109 : for (const auto& cmd : mapCommands) {
530 [ + - ]: 108 : UniValue result;
531 [ + - + - ]: 108 : if (ExecuteCommands(cmd.second, request, result)) {
532 [ + - + + ]: 304 : for (const auto& values : result.getValues()) {
533 [ + - + - ]: 196 : ret.push_back(values);
534 : : }
535 : : }
536 : 108 : }
537 : 1 : return ret;
538 : 1 : }
539 : :
540 : : CRPCTable tableRPC;
|