Line data Source code
1 : // Copyright (c) 2010 Satoshi Nakamoto
2 : // Copyright (c) 2009-2014 The Bitcoin developers
3 : // Copyright (c) 2014-2015 The Dash developers
4 : // Copyright (c) 2015-2022 The PIVX Core developers
5 : // Distributed under the MIT software license, see the accompanying
6 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
7 :
8 : #include "rpc/server.h"
9 :
10 : #include "fs.h"
11 : #include "key_io.h"
12 : #include "random.h"
13 : #include "shutdown.h"
14 : #include "sync.h"
15 : #include "guiinterface.h"
16 : #include "util/system.h"
17 : #include "utilstrencodings.h"
18 :
19 : #ifdef ENABLE_WALLET
20 : #include "wallet/wallet.h"
21 : #endif // ENABLE_WALLET
22 :
23 : #include <boost/signals2/signal.hpp>
24 : #include <boost/thread.hpp>
25 : #include <boost/algorithm/string/classification.hpp>
26 : #include <boost/algorithm/string/split.hpp>
27 :
28 : #include <memory> // for unique_ptr
29 : #include <unordered_map>
30 :
31 : static std::atomic<bool> g_rpc_running{false};
32 : static bool fRPCInWarmup = true;
33 : static std::string rpcWarmupStatus("RPC server started");
34 : static RecursiveMutex cs_rpcWarmup;
35 :
36 : /* Timer-creating functions */
37 : static RPCTimerInterface* timerInterface = nullptr;
38 : /* Map of name to timer. */
39 : static std::map<std::string, std::unique_ptr<RPCTimerBase>> deadlineTimers;
40 :
41 : static struct CRPCSignals
42 : {
43 : boost::signals2::signal<void ()> Started;
44 : boost::signals2::signal<void ()> Stopped;
45 : boost::signals2::signal<void (const CRPCCommand&)> PreCommand;
46 : } g_rpcSignals;
47 :
48 375 : void RPCServer::OnStarted(std::function<void ()> slot)
49 : {
50 750 : g_rpcSignals.Started.connect(slot);
51 375 : }
52 :
53 375 : void RPCServer::OnStopped(std::function<void ()> slot)
54 : {
55 750 : g_rpcSignals.Stopped.connect(slot);
56 375 : }
57 :
58 375 : void RPCServer::OnPreCommand(std::function<void (const CRPCCommand&)> slot)
59 : {
60 750 : g_rpcSignals.PreCommand.connect(std::bind(slot, std::placeholders::_1));
61 375 : }
62 :
63 224873 : void RPCTypeCheck(const UniValue& params,
64 : const std::list<UniValue::VType>& typesExpected,
65 : bool fAllowNull)
66 : {
67 224873 : unsigned int i = 0;
68 458485 : for (UniValue::VType t : typesExpected) {
69 441675 : if (params.size() <= i)
70 : break;
71 :
72 233618 : const UniValue& v = params[i];
73 233618 : if (!((v.type() == t) || (fAllowNull && (v.isNull())))) {
74 6 : std::string err = strprintf("Expected type %s, got %s",
75 6 : uvTypeName(t), uvTypeName(v.type()));
76 6 : throw JSONRPCError(RPC_TYPE_ERROR, err);
77 : }
78 233612 : i++;
79 : }
80 224867 : }
81 :
82 156 : void RPCTypeCheckArgument(const UniValue& value, const UniValueType& typeExpected)
83 : {
84 156 : if (!typeExpected.typeAny && value.type() != typeExpected.type) {
85 0 : throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Expected type %s, got %s", uvTypeName(typeExpected.type), uvTypeName(value.type())));
86 : }
87 156 : }
88 :
89 83 : void RPCTypeCheckObj(const UniValue& o,
90 : const std::map<std::string, UniValueType>& typesExpected,
91 : bool fAllowNull,
92 : bool fStrict)
93 : {
94 380 : for (const auto& t : typesExpected) {
95 300 : const UniValue& v = find_value(o, t.first);
96 300 : if (!fAllowNull && v.isNull())
97 6 : throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing %s", t.first));
98 :
99 297 : if (!(t.second.typeAny || v.type() == t.second.type || (fAllowNull && v.isNull()))) {
100 0 : std::string err = strprintf("Expected type %s for %s, got %s",
101 0 : uvTypeName(t.second.type), t.first, uvTypeName(v.type()));
102 0 : throw JSONRPCError(RPC_TYPE_ERROR, err);
103 : }
104 : }
105 :
106 80 : if (fStrict) {
107 100 : for (const std::string& k : o.getKeys()) {
108 113 : if (typesExpected.count(k) == 0) {
109 1 : std::string err = strprintf("Unexpected key %s", k);
110 1 : throw JSONRPCError(RPC_TYPE_ERROR, err);
111 : }
112 : }
113 : }
114 79 : }
115 :
116 7986 : CAmount AmountFromValue(const UniValue& value)
117 : {
118 7986 : if (!value.isNum() && !value.isStr())
119 0 : throw JSONRPCError(RPC_TYPE_ERROR,"Amount is not a number or string");
120 7986 : CAmount nAmount;
121 7986 : if (!ParseFixedPoint(value.getValStr(), 8, &nAmount))
122 14 : throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount");
123 7979 : if (!Params().GetConsensus().MoneyRange(nAmount))
124 2 : throw JSONRPCError(RPC_TYPE_ERROR, "Amount out of range");
125 7978 : return nAmount;
126 : }
127 :
128 1975876 : UniValue ValueFromAmount(const CAmount& amount)
129 : {
130 1975876 : bool sign = amount < 0;
131 1975876 : int64_t n_abs = (sign ? -amount : amount);
132 1975876 : int64_t quotient = n_abs / COIN;
133 1975876 : int64_t remainder = n_abs % COIN;
134 1975876 : return UniValue(UniValue::VNUM,
135 5927236 : strprintf("%s%d.%08d", sign ? "-" : "", quotient, remainder));
136 : }
137 :
138 7146 : uint256 ParseHashV(const UniValue& v, std::string strName)
139 : {
140 7146 : std::string strHex(v.get_str());
141 7144 : if (64 != strHex.length())
142 10 : throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s must be of length %d (not %d, for '%s')", strName, 64, strHex.length(), strHex));
143 7139 : if (!IsHex(strHex)) // Note: IsHex("") is false
144 4 : throw JSONRPCError(RPC_INVALID_PARAMETER, strName + " must be hexadecimal string (not '" + strHex + "')");
145 21411 : return uint256S(strHex);
146 : }
147 2730 : uint256 ParseHashO(const UniValue& o, std::string strKey)
148 : {
149 5457 : return ParseHashV(find_value(o, strKey), strKey);
150 : }
151 102803 : std::vector<unsigned char> ParseHexV(const UniValue& v, std::string strName)
152 : {
153 102803 : std::string strHex;
154 102803 : if (v.isStr())
155 102803 : strHex = v.get_str();
156 102803 : if (!IsHex(strHex))
157 2 : throw JSONRPCError(RPC_INVALID_PARAMETER, strName + " must be hexadecimal string (not '" + strHex + "')");
158 205604 : return ParseHex(strHex);
159 : }
160 14 : std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey)
161 : {
162 28 : return ParseHexV(find_value(o, strKey), strKey);
163 : }
164 :
165 0 : int ParseInt(const UniValue& o, std::string strKey)
166 : {
167 0 : const UniValue& v = find_value(o, strKey);
168 0 : if (v.isNum())
169 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, " + strKey + "is not an int");
170 :
171 0 : return v.get_int();
172 : }
173 :
174 61 : double ParseDoubleV(const UniValue& v, const std::string &strName)
175 : {
176 61 : std::string strNum = v.getValStr();
177 61 : double num;
178 61 : if (!ParseDouble(strNum, &num))
179 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be a be number (not '"+strNum+"')");
180 61 : return num;
181 : }
182 :
183 0 : bool ParseBool(const UniValue& o, std::string strKey)
184 : {
185 0 : const UniValue& v = find_value(o, strKey);
186 0 : if (v.isBool())
187 0 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, " + strKey + "is not a bool");
188 :
189 0 : return v.get_bool();
190 : }
191 :
192 :
193 : /**
194 : * Note: This interface may still be subject to change.
195 : */
196 :
197 1 : std::string CRPCTable::help(const std::string& strCommand, const JSONRPCRequest& helpreq) const
198 : {
199 1 : std::string strRet;
200 0 : std::string category;
201 2 : std::set<rpcfn_type> setDone;
202 2 : std::vector<std::pair<std::string, const CRPCCommand*> > vCommands;
203 :
204 190 : for (const auto& entry : mapCommands)
205 378 : vCommands.emplace_back(entry.second->category + entry.first, entry.second);
206 1 : std::sort(vCommands.begin(), vCommands.end());
207 :
208 1 : JSONRPCRequest jreq(helpreq);
209 1 : jreq.fHelp = true;
210 1 : jreq.params = UniValue();
211 :
212 190 : for (const std::pair<std::string, const CRPCCommand*>& command : vCommands) {
213 189 : const CRPCCommand* pcmd = command.second;
214 190 : std::string strMethod = pcmd->name;
215 189 : if ((strCommand != "" || pcmd->category == "hidden") && strMethod != strCommand)
216 272 : continue;
217 1 : jreq.strMethod = strMethod;
218 1 : try {
219 1 : rpcfn_type pfn = pcmd->actor;
220 1 : if (setDone.insert(pfn).second)
221 1 : (*pfn)(jreq);
222 1 : } catch (const std::exception& e) {
223 : // Help text is returned in an exception
224 2 : std::string strHelp = std::string(e.what());
225 1 : if (strCommand == "") {
226 0 : if (strHelp.find('\n') != std::string::npos)
227 0 : strHelp = strHelp.substr(0, strHelp.find('\n'));
228 :
229 0 : if (category != pcmd->category) {
230 0 : if (!category.empty())
231 0 : strRet += "\n";
232 0 : category = pcmd->category;
233 0 : strRet += "== " + Capitalize(category) + " ==\n";
234 : }
235 : }
236 3 : strRet += strHelp + "\n";
237 : }
238 : }
239 1 : if (strRet == "")
240 0 : strRet = strprintf("help: unknown command: %s\n", strCommand);
241 1 : strRet = strRet.substr(0, strRet.size() - 1);
242 2 : return strRet;
243 : }
244 :
245 1 : UniValue help(const JSONRPCRequest& jsonRequest)
246 : {
247 1 : if (jsonRequest.fHelp || jsonRequest.params.size() > 1)
248 0 : throw std::runtime_error(
249 : "help ( \"command\" )\n"
250 : "\nList all commands, or get help for a specified command.\n"
251 : "\nArguments:\n"
252 : "1. \"command\" (string, optional) The command to get help on\n"
253 : "\nResult:\n"
254 0 : "\"text\" (string) The help text\n");
255 :
256 1 : std::string strCommand;
257 1 : if (jsonRequest.params.size() > 0)
258 1 : strCommand = jsonRequest.params[0].get_str();
259 :
260 3 : return tableRPC.help(strCommand, jsonRequest);
261 : }
262 :
263 :
264 354 : UniValue stop(const JSONRPCRequest& jsonRequest)
265 : {
266 : // Accept the hidden 'wait' integer argument (milliseconds)
267 : // For instance, 'stop 1000' makes the call wait 1 second before returning
268 : // to the client (intended for testing)
269 354 : if (jsonRequest.fHelp || jsonRequest.params.size() > 1)
270 0 : throw std::runtime_error(
271 : "stop\n"
272 0 : "\nStop PIVX server.");
273 : // Event loop will exit after current HTTP requests have been handled, so
274 : // this reply will get back to the client.
275 354 : StartShutdown();
276 354 : if (jsonRequest.params[0].isNum()) {
277 354 : MilliSleep(jsonRequest.params[0].get_int());
278 : }
279 354 : return "PIVX server stopping";
280 : }
281 :
282 :
283 : /**
284 : * Call Table
285 : */
286 : static const CRPCCommand vRPCCommands[] =
287 : {
288 : // category name actor (function) okSafe argNames
289 : // --------------------- ------------------------ ----------------------- ------ ----------
290 : /* Overall control/query calls */
291 : { "control", "help", &help, true, {"command"} },
292 : { "control", "stop", &stop, true, {"wait"} },
293 : };
294 :
295 479 : CRPCTable::CRPCTable()
296 : {
297 479 : unsigned int vcidx;
298 1437 : for (vcidx = 0; vcidx < (sizeof(vRPCCommands) / sizeof(vRPCCommands[0])); vcidx++) {
299 958 : const CRPCCommand* pcmd;
300 :
301 958 : pcmd = &vRPCCommands[vcidx];
302 958 : mapCommands[pcmd->name] = pcmd;
303 : }
304 479 : }
305 :
306 302249 : const CRPCCommand *CRPCTable::operator[](const std::string &name) const
307 : {
308 302249 : std::map<std::string, const CRPCCommand*>::const_iterator it = mapCommands.find(name);
309 302249 : if (it == mapCommands.end())
310 : return nullptr;
311 302248 : return (*it).second;
312 : }
313 :
314 85234 : bool CRPCTable::appendCommand(const std::string& name, const CRPCCommand* pcmd)
315 : {
316 85234 : if (IsRPCRunning())
317 : return false;
318 :
319 : // don't allow overwriting for now
320 85234 : std::map<std::string, const CRPCCommand*>::const_iterator it = mapCommands.find(name);
321 85234 : if (it != mapCommands.end())
322 : return false;
323 :
324 73671 : mapCommands[name] = pcmd;
325 73671 : return true;
326 : }
327 :
328 375 : bool StartRPC()
329 : {
330 375 : LogPrint(BCLog::RPC, "Starting RPC\n");
331 375 : g_rpc_running = true;
332 375 : g_rpcSignals.Started();
333 375 : return true;
334 : }
335 :
336 378 : void InterruptRPC()
337 : {
338 378 : LogPrint(BCLog::RPC, "Interrupting RPC\n");
339 : // Interrupt e.g. running longpolls
340 378 : g_rpc_running = false;
341 378 : }
342 :
343 378 : void StopRPC()
344 : {
345 378 : LogPrint(BCLog::RPC, "Stopping RPC\n");
346 378 : deadlineTimers.clear();
347 378 : DeleteAuthCookie();
348 378 : g_rpcSignals.Stopped();
349 378 : }
350 :
351 85236 : bool IsRPCRunning()
352 : {
353 85236 : return g_rpc_running;
354 : }
355 :
356 5624 : void SetRPCWarmupStatus(const std::string& newStatus)
357 : {
358 5624 : LOCK(cs_rpcWarmup);
359 5624 : rpcWarmupStatus = newStatus;
360 5624 : }
361 :
362 355 : void SetRPCWarmupFinished()
363 : {
364 355 : LOCK(cs_rpcWarmup);
365 355 : assert(fRPCInWarmup);
366 355 : fRPCInWarmup = false;
367 355 : }
368 :
369 298179 : bool RPCIsInWarmup(std::string* outStatus)
370 : {
371 298179 : LOCK(cs_rpcWarmup);
372 298179 : if (outStatus)
373 298179 : *outStatus = rpcWarmupStatus;
374 596358 : return fRPCInWarmup;
375 : }
376 :
377 298152 : void JSONRPCRequest::parse(const UniValue& valRequest)
378 : {
379 : // Parse request
380 298152 : if (!valRequest.isObject())
381 0 : throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object");
382 298152 : const UniValue& request = valRequest.get_obj();
383 :
384 : // Parse id now so errors from here on will have the id
385 298152 : id = find_value(request, "id");
386 :
387 : // Parse method
388 596304 : UniValue valMethod = find_value(request, "method");
389 298152 : if (valMethod.isNull())
390 0 : throw JSONRPCError(RPC_INVALID_REQUEST, "Missing method");
391 298152 : if (!valMethod.isStr())
392 0 : throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string");
393 298152 : strMethod = valMethod.get_str();
394 298152 : if (strMethod != "getblocktemplate")
395 595099 : LogPrint(BCLog::RPC, "ThreadRPCServer method=%s\n", SanitizeString(strMethod));
396 :
397 : // Parse params
398 596304 : UniValue valParams = find_value(request, "params");
399 298152 : if (valParams.isArray() || valParams.isObject())
400 298140 : params = valParams;
401 12 : else if (valParams.isNull())
402 12 : params = UniValue(UniValue::VARR);
403 : else
404 0 : throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array or object");
405 298152 : }
406 :
407 0 : bool IsDeprecatedRPCEnabled(const std::string& method)
408 : {
409 0 : const std::vector<std::string> enabled_methods = gArgs.GetArgs("-deprecatedrpc");
410 :
411 0 : return find(enabled_methods.begin(), enabled_methods.end(), method) != enabled_methods.end();
412 : }
413 :
414 2 : static UniValue JSONRPCExecOne(const UniValue& req)
415 : {
416 2 : UniValue rpc_result(UniValue::VOBJ);
417 :
418 2 : JSONRPCRequest jreq;
419 2 : try {
420 2 : jreq.parse(req);
421 :
422 2 : UniValue result = tableRPC.execute(jreq);
423 1 : rpc_result = JSONRPCReplyObj(result, NullUniValue, jreq.id);
424 1 : } catch (const UniValue& objError) {
425 1 : rpc_result = JSONRPCReplyObj(NullUniValue, objError, jreq.id);
426 0 : } catch (const std::exception& e) {
427 0 : rpc_result = JSONRPCReplyObj(NullUniValue,
428 0 : JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
429 : }
430 :
431 2 : return rpc_result;
432 : }
433 :
434 1 : std::string JSONRPCExecBatch(const UniValue& vReq)
435 : {
436 1 : UniValue ret(UniValue::VARR);
437 3 : for (unsigned int reqIdx = 0; reqIdx < vReq.size(); reqIdx++)
438 2 : ret.push_back(JSONRPCExecOne(vReq[reqIdx]));
439 :
440 2 : return ret.write() + "\n";
441 : }
442 :
443 : /**
444 : * Process named arguments into a vector of positional arguments, based on the
445 : * passed-in specification for the RPC call's arguments.
446 : */
447 47973 : static inline JSONRPCRequest transformNamedArguments(const JSONRPCRequest& in, const std::vector<std::string>& argNames)
448 : {
449 47973 : JSONRPCRequest out = in;
450 47973 : out.params = UniValue(UniValue::VARR);
451 : // Build a map of parameters, and remove ones that have been processed, so that we can throw a focused error if
452 : // there is an unknown one.
453 47973 : const std::vector<std::string>& keys = in.params.getKeys();
454 47973 : const std::vector<UniValue>& values = in.params.getValues();
455 47974 : std::unordered_map<std::string, const UniValue*> argsIn;
456 48348 : for (size_t i=0; i<keys.size(); ++i) {
457 375 : argsIn[keys[i]] = &values[i];
458 : }
459 : // Process expected parameters.
460 47973 : int hole = 0;
461 63658 : for (const std::string &argNamePattern: argNames) {
462 31370 : std::vector<std::string> vargNames;
463 15685 : boost::algorithm::split(vargNames, argNamePattern, boost::algorithm::is_any_of("|"));
464 15685 : auto fr = argsIn.end();
465 30998 : for (const std::string & argName : vargNames) {
466 15687 : fr = argsIn.find(argName);
467 15687 : if (fr != argsIn.end()) {
468 : break;
469 : }
470 : }
471 15685 : if (fr != argsIn.end()) {
472 400 : for (int i = 0; i < hole; ++i) {
473 : // Fill hole between specified parameters with JSON nulls,
474 : // but not at the end (for backwards compatibility with calls
475 : // that act based on number of specified parameters).
476 26 : out.params.push_back(UniValue());
477 : }
478 374 : hole = 0;
479 374 : out.params.push_back(*fr->second);
480 374 : argsIn.erase(fr);
481 : } else {
482 15311 : hole += 1;
483 : }
484 : }
485 : // If there are still arguments in the argsIn map, this is an error.
486 47973 : if (!argsIn.empty()) {
487 2 : throw JSONRPCError(RPC_INVALID_PARAMETER, "Unknown named parameter " + argsIn.begin()->first);
488 : }
489 : // Return request with named arguments transformed to positional arguments
490 47972 : return out;
491 : }
492 :
493 298152 : UniValue CRPCTable::execute(const JSONRPCRequest &request) const
494 : {
495 : // Return immediately if in warmup
496 596016 : std::string strWarmupStatus;
497 298152 : if (RPCIsInWarmup(&strWarmupStatus)) {
498 198 : throw JSONRPCError(RPC_IN_WARMUP, "RPC in warm-up: " + strWarmupStatus);
499 : }
500 :
501 : // Find method
502 298053 : const CRPCCommand* pcmd = tableRPC[request.strMethod];
503 298053 : if (!pcmd)
504 2 : throw JSONRPCError(RPC_METHOD_NOT_FOUND, strprintf("Method not found: %s", request.strMethod));
505 :
506 298052 : g_rpcSignals.PreCommand(*pcmd);
507 :
508 298052 : try {
509 : // Execute, convert arguments to array if necessary
510 298052 : if (request.params.isObject()) {
511 48160 : return pcmd->actor(transformNamedArguments(request, pcmd->argNames));
512 : } else {
513 250079 : return pcmd->actor(request);
514 : }
515 22 : } catch (const std::exception& e) {
516 22 : throw JSONRPCError(RPC_MISC_ERROR, e.what());
517 : }
518 : }
519 :
520 0 : std::vector<std::string> CRPCTable::listCommands() const
521 : {
522 0 : std::vector<std::string> commandList;
523 0 : for (const auto& i : mapCommands) commandList.emplace_back(i.first);
524 0 : return commandList;
525 : }
526 :
527 35 : std::string HelpExampleCli(std::string methodname, std::string args)
528 : {
529 70 : return "> pivx-cli " + methodname + " " + args + "\n";
530 : }
531 :
532 21 : std::string HelpExampleRpc(std::string methodname, std::string args)
533 : {
534 21 : return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", "
535 21 : "\"method\": \"" +
536 42 : methodname + "\", \"params\": [" + args + "] }' -H 'content-type: text/plain;' http://127.0.0.1:51473/\n";
537 : }
538 :
539 0 : void RPCSetTimerInterfaceIfUnset(RPCTimerInterface *iface)
540 : {
541 0 : if (!timerInterface)
542 0 : timerInterface = iface;
543 0 : }
544 :
545 375 : void RPCSetTimerInterface(RPCTimerInterface *iface)
546 : {
547 375 : timerInterface = iface;
548 375 : }
549 :
550 375 : void RPCUnsetTimerInterface(RPCTimerInterface *iface)
551 : {
552 375 : if (timerInterface == iface)
553 375 : timerInterface = nullptr;
554 375 : }
555 :
556 14 : void RPCRunLater(const std::string& name, std::function<void(void)> func, int64_t nSeconds)
557 : {
558 14 : if (!timerInterface)
559 0 : throw JSONRPCError(RPC_INTERNAL_ERROR, "No timer handler registered for RPC");
560 14 : deadlineTimers.erase(name);
561 14 : LogPrint(BCLog::RPC, "queue run of timer %s in %i seconds (using %s)\n", name, nSeconds, timerInterface->Name());
562 14 : deadlineTimers.emplace(name, std::unique_ptr<RPCTimerBase>(timerInterface->NewTimer(func, nSeconds*1000)));
563 14 : }
564 :
565 : CRPCTable tableRPC;
|