Remove redundant nullptr checks before deallocation
[bitcoinplatinum.git] / src / rpc / server.cpp
blob9ad8d228faa66ae176594f19810e46bf88f1919d
1 // Copyright (c) 2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2016 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.
6 #include "rpc/server.h"
8 #include "base58.h"
9 #include "fs.h"
10 #include "init.h"
11 #include "random.h"
12 #include "sync.h"
13 #include "ui_interface.h"
14 #include "util.h"
15 #include "utilstrencodings.h"
17 #include <univalue.h>
19 #include <boost/bind.hpp>
20 #include <boost/signals2/signal.hpp>
21 #include <boost/algorithm/string/case_conv.hpp> // for to_upper()
22 #include <boost/algorithm/string/classification.hpp>
23 #include <boost/algorithm/string/split.hpp>
25 #include <memory> // for unique_ptr
26 #include <unordered_map>
28 static bool fRPCRunning = false;
29 static bool fRPCInWarmup = true;
30 static std::string rpcWarmupStatus("RPC server started");
31 static CCriticalSection cs_rpcWarmup;
32 /* Timer-creating functions */
33 static RPCTimerInterface* timerInterface = nullptr;
34 /* Map of name to timer. */
35 static std::map<std::string, std::unique_ptr<RPCTimerBase> > deadlineTimers;
37 static struct CRPCSignals
39 boost::signals2::signal<void ()> Started;
40 boost::signals2::signal<void ()> Stopped;
41 boost::signals2::signal<void (const CRPCCommand&)> PreCommand;
42 } g_rpcSignals;
44 void RPCServer::OnStarted(std::function<void ()> slot)
46 g_rpcSignals.Started.connect(slot);
49 void RPCServer::OnStopped(std::function<void ()> slot)
51 g_rpcSignals.Stopped.connect(slot);
54 void RPCServer::OnPreCommand(std::function<void (const CRPCCommand&)> slot)
56 g_rpcSignals.PreCommand.connect(boost::bind(slot, _1));
59 void RPCTypeCheck(const UniValue& params,
60 const std::list<UniValue::VType>& typesExpected,
61 bool fAllowNull)
63 unsigned int i = 0;
64 for (UniValue::VType t : typesExpected)
66 if (params.size() <= i)
67 break;
69 const UniValue& v = params[i];
70 if (!(fAllowNull && v.isNull())) {
71 RPCTypeCheckArgument(v, t);
73 i++;
77 void RPCTypeCheckArgument(const UniValue& value, UniValue::VType typeExpected)
79 if (value.type() != typeExpected) {
80 throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Expected type %s, got %s", uvTypeName(typeExpected), uvTypeName(value.type())));
84 void RPCTypeCheckObj(const UniValue& o,
85 const std::map<std::string, UniValueType>& typesExpected,
86 bool fAllowNull,
87 bool fStrict)
89 for (const auto& t : typesExpected) {
90 const UniValue& v = find_value(o, t.first);
91 if (!fAllowNull && v.isNull())
92 throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing %s", t.first));
94 if (!(t.second.typeAny || v.type() == t.second.type || (fAllowNull && v.isNull()))) {
95 std::string err = strprintf("Expected type %s for %s, got %s",
96 uvTypeName(t.second.type), t.first, uvTypeName(v.type()));
97 throw JSONRPCError(RPC_TYPE_ERROR, err);
101 if (fStrict)
103 for (const std::string& k : o.getKeys())
105 if (typesExpected.count(k) == 0)
107 std::string err = strprintf("Unexpected key %s", k);
108 throw JSONRPCError(RPC_TYPE_ERROR, err);
114 CAmount AmountFromValue(const UniValue& value)
116 if (!value.isNum() && !value.isStr())
117 throw JSONRPCError(RPC_TYPE_ERROR, "Amount is not a number or string");
118 CAmount amount;
119 if (!ParseFixedPoint(value.getValStr(), 8, &amount))
120 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount");
121 if (!MoneyRange(amount))
122 throw JSONRPCError(RPC_TYPE_ERROR, "Amount out of range");
123 return amount;
126 uint256 ParseHashV(const UniValue& v, std::string strName)
128 std::string strHex;
129 if (v.isStr())
130 strHex = v.get_str();
131 if (!IsHex(strHex)) // Note: IsHex("") is false
132 throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
133 if (64 != strHex.length())
134 throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s must be of length %d (not %d)", strName, 64, strHex.length()));
135 uint256 result;
136 result.SetHex(strHex);
137 return result;
139 uint256 ParseHashO(const UniValue& o, std::string strKey)
141 return ParseHashV(find_value(o, strKey), strKey);
143 std::vector<unsigned char> ParseHexV(const UniValue& v, std::string strName)
145 std::string strHex;
146 if (v.isStr())
147 strHex = v.get_str();
148 if (!IsHex(strHex))
149 throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
150 return ParseHex(strHex);
152 std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey)
154 return ParseHexV(find_value(o, strKey), strKey);
158 * Note: This interface may still be subject to change.
161 std::string CRPCTable::help(const std::string& strCommand, const JSONRPCRequest& helpreq) const
163 std::string strRet;
164 std::string category;
165 std::set<rpcfn_type> setDone;
166 std::vector<std::pair<std::string, const CRPCCommand*> > vCommands;
168 for (std::map<std::string, const CRPCCommand*>::const_iterator mi = mapCommands.begin(); mi != mapCommands.end(); ++mi)
169 vCommands.push_back(make_pair(mi->second->category + mi->first, mi->second));
170 sort(vCommands.begin(), vCommands.end());
172 JSONRPCRequest jreq(helpreq);
173 jreq.fHelp = true;
174 jreq.params = UniValue();
176 for (const std::pair<std::string, const CRPCCommand*>& command : vCommands)
178 const CRPCCommand *pcmd = command.second;
179 std::string strMethod = pcmd->name;
180 if ((strCommand != "" || pcmd->category == "hidden") && strMethod != strCommand)
181 continue;
182 jreq.strMethod = strMethod;
185 rpcfn_type pfn = pcmd->actor;
186 if (setDone.insert(pfn).second)
187 (*pfn)(jreq);
189 catch (const std::exception& e)
191 // Help text is returned in an exception
192 std::string strHelp = std::string(e.what());
193 if (strCommand == "")
195 if (strHelp.find('\n') != std::string::npos)
196 strHelp = strHelp.substr(0, strHelp.find('\n'));
198 if (category != pcmd->category)
200 if (!category.empty())
201 strRet += "\n";
202 category = pcmd->category;
203 std::string firstLetter = category.substr(0,1);
204 boost::to_upper(firstLetter);
205 strRet += "== " + firstLetter + category.substr(1) + " ==\n";
208 strRet += strHelp + "\n";
211 if (strRet == "")
212 strRet = strprintf("help: unknown command: %s\n", strCommand);
213 strRet = strRet.substr(0,strRet.size()-1);
214 return strRet;
217 UniValue help(const JSONRPCRequest& jsonRequest)
219 if (jsonRequest.fHelp || jsonRequest.params.size() > 1)
220 throw std::runtime_error(
221 "help ( \"command\" )\n"
222 "\nList all commands, or get help for a specified command.\n"
223 "\nArguments:\n"
224 "1. \"command\" (string, optional) The command to get help on\n"
225 "\nResult:\n"
226 "\"text\" (string) The help text\n"
229 std::string strCommand;
230 if (jsonRequest.params.size() > 0)
231 strCommand = jsonRequest.params[0].get_str();
233 return tableRPC.help(strCommand, jsonRequest);
237 UniValue stop(const JSONRPCRequest& jsonRequest)
239 // Accept the deprecated and ignored 'detach' boolean argument
240 if (jsonRequest.fHelp || jsonRequest.params.size() > 1)
241 throw std::runtime_error(
242 "stop\n"
243 "\nStop Bitcoin server.");
244 // Event loop will exit after current HTTP requests have been handled, so
245 // this reply will get back to the client.
246 StartShutdown();
247 return "Bitcoin server stopping";
250 UniValue uptime(const JSONRPCRequest& jsonRequest)
252 if (jsonRequest.fHelp || jsonRequest.params.size() > 1)
253 throw std::runtime_error(
254 "uptime\n"
255 "\nReturns the total uptime of the server.\n"
256 "\nResult:\n"
257 "ttt (numeric) The number of seconds that the server has been running\n"
258 "\nExamples:\n"
259 + HelpExampleCli("uptime", "")
260 + HelpExampleRpc("uptime", "")
263 return GetTime() - GetStartupTime();
267 * Call Table
269 static const CRPCCommand vRPCCommands[] =
270 { // category name actor (function) okSafe argNames
271 // --------------------- ------------------------ ----------------------- ------ ----------
272 /* Overall control/query calls */
273 { "control", "help", &help, true, {"command"} },
274 { "control", "stop", &stop, true, {} },
275 { "control", "uptime", &uptime, true, {} },
278 CRPCTable::CRPCTable()
280 unsigned int vcidx;
281 for (vcidx = 0; vcidx < (sizeof(vRPCCommands) / sizeof(vRPCCommands[0])); vcidx++)
283 const CRPCCommand *pcmd;
285 pcmd = &vRPCCommands[vcidx];
286 mapCommands[pcmd->name] = pcmd;
290 const CRPCCommand *CRPCTable::operator[](const std::string &name) const
292 std::map<std::string, const CRPCCommand*>::const_iterator it = mapCommands.find(name);
293 if (it == mapCommands.end())
294 return nullptr;
295 return (*it).second;
298 bool CRPCTable::appendCommand(const std::string& name, const CRPCCommand* pcmd)
300 if (IsRPCRunning())
301 return false;
303 // don't allow overwriting for now
304 std::map<std::string, const CRPCCommand*>::const_iterator it = mapCommands.find(name);
305 if (it != mapCommands.end())
306 return false;
308 mapCommands[name] = pcmd;
309 return true;
312 bool StartRPC()
314 LogPrint(BCLog::RPC, "Starting RPC\n");
315 fRPCRunning = true;
316 g_rpcSignals.Started();
317 return true;
320 void InterruptRPC()
322 LogPrint(BCLog::RPC, "Interrupting RPC\n");
323 // Interrupt e.g. running longpolls
324 fRPCRunning = false;
327 void StopRPC()
329 LogPrint(BCLog::RPC, "Stopping RPC\n");
330 deadlineTimers.clear();
331 DeleteAuthCookie();
332 g_rpcSignals.Stopped();
335 bool IsRPCRunning()
337 return fRPCRunning;
340 void SetRPCWarmupStatus(const std::string& newStatus)
342 LOCK(cs_rpcWarmup);
343 rpcWarmupStatus = newStatus;
346 void SetRPCWarmupFinished()
348 LOCK(cs_rpcWarmup);
349 assert(fRPCInWarmup);
350 fRPCInWarmup = false;
353 bool RPCIsInWarmup(std::string *outStatus)
355 LOCK(cs_rpcWarmup);
356 if (outStatus)
357 *outStatus = rpcWarmupStatus;
358 return fRPCInWarmup;
361 void JSONRPCRequest::parse(const UniValue& valRequest)
363 // Parse request
364 if (!valRequest.isObject())
365 throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object");
366 const UniValue& request = valRequest.get_obj();
368 // Parse id now so errors from here on will have the id
369 id = find_value(request, "id");
371 // Parse method
372 UniValue valMethod = find_value(request, "method");
373 if (valMethod.isNull())
374 throw JSONRPCError(RPC_INVALID_REQUEST, "Missing method");
375 if (!valMethod.isStr())
376 throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string");
377 strMethod = valMethod.get_str();
378 LogPrint(BCLog::RPC, "ThreadRPCServer method=%s\n", SanitizeString(strMethod));
380 // Parse params
381 UniValue valParams = find_value(request, "params");
382 if (valParams.isArray() || valParams.isObject())
383 params = valParams;
384 else if (valParams.isNull())
385 params = UniValue(UniValue::VARR);
386 else
387 throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array or object");
390 static UniValue JSONRPCExecOne(const UniValue& req)
392 UniValue rpc_result(UniValue::VOBJ);
394 JSONRPCRequest jreq;
395 try {
396 jreq.parse(req);
398 UniValue result = tableRPC.execute(jreq);
399 rpc_result = JSONRPCReplyObj(result, NullUniValue, jreq.id);
401 catch (const UniValue& objError)
403 rpc_result = JSONRPCReplyObj(NullUniValue, objError, jreq.id);
405 catch (const std::exception& e)
407 rpc_result = JSONRPCReplyObj(NullUniValue,
408 JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
411 return rpc_result;
414 std::string JSONRPCExecBatch(const UniValue& vReq)
416 UniValue ret(UniValue::VARR);
417 for (unsigned int reqIdx = 0; reqIdx < vReq.size(); reqIdx++)
418 ret.push_back(JSONRPCExecOne(vReq[reqIdx]));
420 return ret.write() + "\n";
424 * Process named arguments into a vector of positional arguments, based on the
425 * passed-in specification for the RPC call's arguments.
427 static inline JSONRPCRequest transformNamedArguments(const JSONRPCRequest& in, const std::vector<std::string>& argNames)
429 JSONRPCRequest out = in;
430 out.params = UniValue(UniValue::VARR);
431 // Build a map of parameters, and remove ones that have been processed, so that we can throw a focused error if
432 // there is an unknown one.
433 const std::vector<std::string>& keys = in.params.getKeys();
434 const std::vector<UniValue>& values = in.params.getValues();
435 std::unordered_map<std::string, const UniValue*> argsIn;
436 for (size_t i=0; i<keys.size(); ++i) {
437 argsIn[keys[i]] = &values[i];
439 // Process expected parameters.
440 int hole = 0;
441 for (const std::string &argNamePattern: argNames) {
442 std::vector<std::string> vargNames;
443 boost::algorithm::split(vargNames, argNamePattern, boost::algorithm::is_any_of("|"));
444 auto fr = argsIn.end();
445 for (const std::string & argName : vargNames) {
446 fr = argsIn.find(argName);
447 if (fr != argsIn.end()) {
448 break;
451 if (fr != argsIn.end()) {
452 for (int i = 0; i < hole; ++i) {
453 // Fill hole between specified parameters with JSON nulls,
454 // but not at the end (for backwards compatibility with calls
455 // that act based on number of specified parameters).
456 out.params.push_back(UniValue());
458 hole = 0;
459 out.params.push_back(*fr->second);
460 argsIn.erase(fr);
461 } else {
462 hole += 1;
465 // If there are still arguments in the argsIn map, this is an error.
466 if (!argsIn.empty()) {
467 throw JSONRPCError(RPC_INVALID_PARAMETER, "Unknown named parameter " + argsIn.begin()->first);
469 // Return request with named arguments transformed to positional arguments
470 return out;
473 UniValue CRPCTable::execute(const JSONRPCRequest &request) const
475 // Return immediately if in warmup
477 LOCK(cs_rpcWarmup);
478 if (fRPCInWarmup)
479 throw JSONRPCError(RPC_IN_WARMUP, rpcWarmupStatus);
482 // Find method
483 const CRPCCommand *pcmd = tableRPC[request.strMethod];
484 if (!pcmd)
485 throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found");
487 g_rpcSignals.PreCommand(*pcmd);
491 // Execute, convert arguments to array if necessary
492 if (request.params.isObject()) {
493 return pcmd->actor(transformNamedArguments(request, pcmd->argNames));
494 } else {
495 return pcmd->actor(request);
498 catch (const std::exception& e)
500 throw JSONRPCError(RPC_MISC_ERROR, e.what());
504 std::vector<std::string> CRPCTable::listCommands() const
506 std::vector<std::string> commandList;
507 typedef std::map<std::string, const CRPCCommand*> commandMap;
509 std::transform( mapCommands.begin(), mapCommands.end(),
510 std::back_inserter(commandList),
511 boost::bind(&commandMap::value_type::first,_1) );
512 return commandList;
515 std::string HelpExampleCli(const std::string& methodname, const std::string& args)
517 return "> bitcoin-cli " + methodname + " " + args + "\n";
520 std::string HelpExampleRpc(const std::string& methodname, const std::string& args)
522 return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", "
523 "\"method\": \"" + methodname + "\", \"params\": [" + args + "] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n";
526 void RPCSetTimerInterfaceIfUnset(RPCTimerInterface *iface)
528 if (!timerInterface)
529 timerInterface = iface;
532 void RPCSetTimerInterface(RPCTimerInterface *iface)
534 timerInterface = iface;
537 void RPCUnsetTimerInterface(RPCTimerInterface *iface)
539 if (timerInterface == iface)
540 timerInterface = nullptr;
543 void RPCRunLater(const std::string& name, std::function<void(void)> func, int64_t nSeconds)
545 if (!timerInterface)
546 throw JSONRPCError(RPC_INTERNAL_ERROR, "No timer handler registered for RPC");
547 deadlineTimers.erase(name);
548 LogPrint(BCLog::RPC, "queue run of timer %s in %i seconds (using %s)\n", name, nSeconds, timerInterface->Name());
549 deadlineTimers.emplace(name, std::unique_ptr<RPCTimerBase>(timerInterface->NewTimer(func, nSeconds*1000)));
552 int RPCSerializationFlags()
554 int flag = 0;
555 if (gArgs.GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) == 0)
556 flag |= SERIALIZE_TRANSACTION_NO_WITNESS;
557 return flag;
560 CRPCTable tableRPC;