Merge #10574: Remove includes in .cpp files for things the corresponding .h file...
[bitcoinplatinum.git] / src / rpc / server.cpp
blob2e7e0ba4744191772801c844b005485a3a68207e
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 <boost/bind.hpp>
18 #include <boost/signals2/signal.hpp>
19 #include <boost/algorithm/string/case_conv.hpp> // for to_upper()
20 #include <boost/algorithm/string/classification.hpp>
21 #include <boost/algorithm/string/split.hpp>
23 #include <memory> // for unique_ptr
24 #include <unordered_map>
26 static bool fRPCRunning = false;
27 static bool fRPCInWarmup = true;
28 static std::string rpcWarmupStatus("RPC server started");
29 static CCriticalSection cs_rpcWarmup;
30 /* Timer-creating functions */
31 static RPCTimerInterface* timerInterface = nullptr;
32 /* Map of name to timer. */
33 static std::map<std::string, std::unique_ptr<RPCTimerBase> > deadlineTimers;
35 static struct CRPCSignals
37 boost::signals2::signal<void ()> Started;
38 boost::signals2::signal<void ()> Stopped;
39 boost::signals2::signal<void (const CRPCCommand&)> PreCommand;
40 } g_rpcSignals;
42 void RPCServer::OnStarted(std::function<void ()> slot)
44 g_rpcSignals.Started.connect(slot);
47 void RPCServer::OnStopped(std::function<void ()> slot)
49 g_rpcSignals.Stopped.connect(slot);
52 void RPCTypeCheck(const UniValue& params,
53 const std::list<UniValue::VType>& typesExpected,
54 bool fAllowNull)
56 unsigned int i = 0;
57 for (UniValue::VType t : typesExpected)
59 if (params.size() <= i)
60 break;
62 const UniValue& v = params[i];
63 if (!(fAllowNull && v.isNull())) {
64 RPCTypeCheckArgument(v, t);
66 i++;
70 void RPCTypeCheckArgument(const UniValue& value, UniValue::VType typeExpected)
72 if (value.type() != typeExpected) {
73 throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Expected type %s, got %s", uvTypeName(typeExpected), uvTypeName(value.type())));
77 void RPCTypeCheckObj(const UniValue& o,
78 const std::map<std::string, UniValueType>& typesExpected,
79 bool fAllowNull,
80 bool fStrict)
82 for (const auto& t : typesExpected) {
83 const UniValue& v = find_value(o, t.first);
84 if (!fAllowNull && v.isNull())
85 throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing %s", t.first));
87 if (!(t.second.typeAny || v.type() == t.second.type || (fAllowNull && v.isNull()))) {
88 std::string err = strprintf("Expected type %s for %s, got %s",
89 uvTypeName(t.second.type), t.first, uvTypeName(v.type()));
90 throw JSONRPCError(RPC_TYPE_ERROR, err);
94 if (fStrict)
96 for (const std::string& k : o.getKeys())
98 if (typesExpected.count(k) == 0)
100 std::string err = strprintf("Unexpected key %s", k);
101 throw JSONRPCError(RPC_TYPE_ERROR, err);
107 CAmount AmountFromValue(const UniValue& value)
109 if (!value.isNum() && !value.isStr())
110 throw JSONRPCError(RPC_TYPE_ERROR, "Amount is not a number or string");
111 CAmount amount;
112 if (!ParseFixedPoint(value.getValStr(), 8, &amount))
113 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount");
114 if (!MoneyRange(amount))
115 throw JSONRPCError(RPC_TYPE_ERROR, "Amount out of range");
116 return amount;
119 uint256 ParseHashV(const UniValue& v, std::string strName)
121 std::string strHex;
122 if (v.isStr())
123 strHex = v.get_str();
124 if (!IsHex(strHex)) // Note: IsHex("") is false
125 throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
126 if (64 != strHex.length())
127 throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s must be of length %d (not %d)", strName, 64, strHex.length()));
128 uint256 result;
129 result.SetHex(strHex);
130 return result;
132 uint256 ParseHashO(const UniValue& o, std::string strKey)
134 return ParseHashV(find_value(o, strKey), strKey);
136 std::vector<unsigned char> ParseHexV(const UniValue& v, std::string strName)
138 std::string strHex;
139 if (v.isStr())
140 strHex = v.get_str();
141 if (!IsHex(strHex))
142 throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
143 return ParseHex(strHex);
145 std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey)
147 return ParseHexV(find_value(o, strKey), strKey);
151 * Note: This interface may still be subject to change.
154 std::string CRPCTable::help(const std::string& strCommand, const JSONRPCRequest& helpreq) const
156 std::string strRet;
157 std::string category;
158 std::set<rpcfn_type> setDone;
159 std::vector<std::pair<std::string, const CRPCCommand*> > vCommands;
161 for (const auto& entry : mapCommands)
162 vCommands.push_back(make_pair(entry.second->category + entry.first, entry.second));
163 sort(vCommands.begin(), vCommands.end());
165 JSONRPCRequest jreq(helpreq);
166 jreq.fHelp = true;
167 jreq.params = UniValue();
169 for (const std::pair<std::string, const CRPCCommand*>& command : vCommands)
171 const CRPCCommand *pcmd = command.second;
172 std::string strMethod = pcmd->name;
173 if ((strCommand != "" || pcmd->category == "hidden") && strMethod != strCommand)
174 continue;
175 jreq.strMethod = strMethod;
178 rpcfn_type pfn = pcmd->actor;
179 if (setDone.insert(pfn).second)
180 (*pfn)(jreq);
182 catch (const std::exception& e)
184 // Help text is returned in an exception
185 std::string strHelp = std::string(e.what());
186 if (strCommand == "")
188 if (strHelp.find('\n') != std::string::npos)
189 strHelp = strHelp.substr(0, strHelp.find('\n'));
191 if (category != pcmd->category)
193 if (!category.empty())
194 strRet += "\n";
195 category = pcmd->category;
196 std::string firstLetter = category.substr(0,1);
197 boost::to_upper(firstLetter);
198 strRet += "== " + firstLetter + category.substr(1) + " ==\n";
201 strRet += strHelp + "\n";
204 if (strRet == "")
205 strRet = strprintf("help: unknown command: %s\n", strCommand);
206 strRet = strRet.substr(0,strRet.size()-1);
207 return strRet;
210 UniValue help(const JSONRPCRequest& jsonRequest)
212 if (jsonRequest.fHelp || jsonRequest.params.size() > 1)
213 throw std::runtime_error(
214 "help ( \"command\" )\n"
215 "\nList all commands, or get help for a specified command.\n"
216 "\nArguments:\n"
217 "1. \"command\" (string, optional) The command to get help on\n"
218 "\nResult:\n"
219 "\"text\" (string) The help text\n"
222 std::string strCommand;
223 if (jsonRequest.params.size() > 0)
224 strCommand = jsonRequest.params[0].get_str();
226 return tableRPC.help(strCommand, jsonRequest);
230 UniValue stop(const JSONRPCRequest& jsonRequest)
232 // Accept the deprecated and ignored 'detach' boolean argument
233 if (jsonRequest.fHelp || jsonRequest.params.size() > 1)
234 throw std::runtime_error(
235 "stop\n"
236 "\nStop Bitcoin server.");
237 // Event loop will exit after current HTTP requests have been handled, so
238 // this reply will get back to the client.
239 StartShutdown();
240 return "Bitcoin server stopping";
243 UniValue uptime(const JSONRPCRequest& jsonRequest)
245 if (jsonRequest.fHelp || jsonRequest.params.size() > 1)
246 throw std::runtime_error(
247 "uptime\n"
248 "\nReturns the total uptime of the server.\n"
249 "\nResult:\n"
250 "ttt (numeric) The number of seconds that the server has been running\n"
251 "\nExamples:\n"
252 + HelpExampleCli("uptime", "")
253 + HelpExampleRpc("uptime", "")
256 return GetTime() - GetStartupTime();
260 * Call Table
262 static const CRPCCommand vRPCCommands[] =
263 { // category name actor (function) argNames
264 // --------------------- ------------------------ ----------------------- ----------
265 /* Overall control/query calls */
266 { "control", "help", &help, {"command"} },
267 { "control", "stop", &stop, {} },
268 { "control", "uptime", &uptime, {} },
271 CRPCTable::CRPCTable()
273 unsigned int vcidx;
274 for (vcidx = 0; vcidx < (sizeof(vRPCCommands) / sizeof(vRPCCommands[0])); vcidx++)
276 const CRPCCommand *pcmd;
278 pcmd = &vRPCCommands[vcidx];
279 mapCommands[pcmd->name] = pcmd;
283 const CRPCCommand *CRPCTable::operator[](const std::string &name) const
285 std::map<std::string, const CRPCCommand*>::const_iterator it = mapCommands.find(name);
286 if (it == mapCommands.end())
287 return nullptr;
288 return (*it).second;
291 bool CRPCTable::appendCommand(const std::string& name, const CRPCCommand* pcmd)
293 if (IsRPCRunning())
294 return false;
296 // don't allow overwriting for now
297 std::map<std::string, const CRPCCommand*>::const_iterator it = mapCommands.find(name);
298 if (it != mapCommands.end())
299 return false;
301 mapCommands[name] = pcmd;
302 return true;
305 bool StartRPC()
307 LogPrint(BCLog::RPC, "Starting RPC\n");
308 fRPCRunning = true;
309 g_rpcSignals.Started();
310 return true;
313 void InterruptRPC()
315 LogPrint(BCLog::RPC, "Interrupting RPC\n");
316 // Interrupt e.g. running longpolls
317 fRPCRunning = false;
320 void StopRPC()
322 LogPrint(BCLog::RPC, "Stopping RPC\n");
323 deadlineTimers.clear();
324 DeleteAuthCookie();
325 g_rpcSignals.Stopped();
328 bool IsRPCRunning()
330 return fRPCRunning;
333 void SetRPCWarmupStatus(const std::string& newStatus)
335 LOCK(cs_rpcWarmup);
336 rpcWarmupStatus = newStatus;
339 void SetRPCWarmupFinished()
341 LOCK(cs_rpcWarmup);
342 assert(fRPCInWarmup);
343 fRPCInWarmup = false;
346 bool RPCIsInWarmup(std::string *outStatus)
348 LOCK(cs_rpcWarmup);
349 if (outStatus)
350 *outStatus = rpcWarmupStatus;
351 return fRPCInWarmup;
354 void JSONRPCRequest::parse(const UniValue& valRequest)
356 // Parse request
357 if (!valRequest.isObject())
358 throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object");
359 const UniValue& request = valRequest.get_obj();
361 // Parse id now so errors from here on will have the id
362 id = find_value(request, "id");
364 // Parse method
365 UniValue valMethod = find_value(request, "method");
366 if (valMethod.isNull())
367 throw JSONRPCError(RPC_INVALID_REQUEST, "Missing method");
368 if (!valMethod.isStr())
369 throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string");
370 strMethod = valMethod.get_str();
371 LogPrint(BCLog::RPC, "ThreadRPCServer method=%s\n", SanitizeString(strMethod));
373 // Parse params
374 UniValue valParams = find_value(request, "params");
375 if (valParams.isArray() || valParams.isObject())
376 params = valParams;
377 else if (valParams.isNull())
378 params = UniValue(UniValue::VARR);
379 else
380 throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array or object");
383 bool IsDeprecatedRPCEnabled(const std::string& method)
385 const std::vector<std::string> enabled_methods = gArgs.GetArgs("-deprecatedrpc");
387 return find(enabled_methods.begin(), enabled_methods.end(), method) != enabled_methods.end();
390 static UniValue JSONRPCExecOne(JSONRPCRequest jreq, const UniValue& req)
392 UniValue rpc_result(UniValue::VOBJ);
394 try {
395 jreq.parse(req);
397 UniValue result = tableRPC.execute(jreq);
398 rpc_result = JSONRPCReplyObj(result, NullUniValue, jreq.id);
400 catch (const UniValue& objError)
402 rpc_result = JSONRPCReplyObj(NullUniValue, objError, jreq.id);
404 catch (const std::exception& e)
406 rpc_result = JSONRPCReplyObj(NullUniValue,
407 JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
410 return rpc_result;
413 std::string JSONRPCExecBatch(const JSONRPCRequest& jreq, const UniValue& vReq)
415 UniValue ret(UniValue::VARR);
416 for (unsigned int reqIdx = 0; reqIdx < vReq.size(); reqIdx++)
417 ret.push_back(JSONRPCExecOne(jreq, vReq[reqIdx]));
419 return ret.write() + "\n";
423 * Process named arguments into a vector of positional arguments, based on the
424 * passed-in specification for the RPC call's arguments.
426 static inline JSONRPCRequest transformNamedArguments(const JSONRPCRequest& in, const std::vector<std::string>& argNames)
428 JSONRPCRequest out = in;
429 out.params = UniValue(UniValue::VARR);
430 // Build a map of parameters, and remove ones that have been processed, so that we can throw a focused error if
431 // there is an unknown one.
432 const std::vector<std::string>& keys = in.params.getKeys();
433 const std::vector<UniValue>& values = in.params.getValues();
434 std::unordered_map<std::string, const UniValue*> argsIn;
435 for (size_t i=0; i<keys.size(); ++i) {
436 argsIn[keys[i]] = &values[i];
438 // Process expected parameters.
439 int hole = 0;
440 for (const std::string &argNamePattern: argNames) {
441 std::vector<std::string> vargNames;
442 boost::algorithm::split(vargNames, argNamePattern, boost::algorithm::is_any_of("|"));
443 auto fr = argsIn.end();
444 for (const std::string & argName : vargNames) {
445 fr = argsIn.find(argName);
446 if (fr != argsIn.end()) {
447 break;
450 if (fr != argsIn.end()) {
451 for (int i = 0; i < hole; ++i) {
452 // Fill hole between specified parameters with JSON nulls,
453 // but not at the end (for backwards compatibility with calls
454 // that act based on number of specified parameters).
455 out.params.push_back(UniValue());
457 hole = 0;
458 out.params.push_back(*fr->second);
459 argsIn.erase(fr);
460 } else {
461 hole += 1;
464 // If there are still arguments in the argsIn map, this is an error.
465 if (!argsIn.empty()) {
466 throw JSONRPCError(RPC_INVALID_PARAMETER, "Unknown named parameter " + argsIn.begin()->first);
468 // Return request with named arguments transformed to positional arguments
469 return out;
472 UniValue CRPCTable::execute(const JSONRPCRequest &request) const
474 // Return immediately if in warmup
476 LOCK(cs_rpcWarmup);
477 if (fRPCInWarmup)
478 throw JSONRPCError(RPC_IN_WARMUP, rpcWarmupStatus);
481 // Find method
482 const CRPCCommand *pcmd = tableRPC[request.strMethod];
483 if (!pcmd)
484 throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found");
486 g_rpcSignals.PreCommand(*pcmd);
490 // Execute, convert arguments to array if necessary
491 if (request.params.isObject()) {
492 return pcmd->actor(transformNamedArguments(request, pcmd->argNames));
493 } else {
494 return pcmd->actor(request);
497 catch (const std::exception& e)
499 throw JSONRPCError(RPC_MISC_ERROR, e.what());
503 std::vector<std::string> CRPCTable::listCommands() const
505 std::vector<std::string> commandList;
506 typedef std::map<std::string, const CRPCCommand*> commandMap;
508 std::transform( mapCommands.begin(), mapCommands.end(),
509 std::back_inserter(commandList),
510 boost::bind(&commandMap::value_type::first,_1) );
511 return commandList;
514 std::string HelpExampleCli(const std::string& methodname, const std::string& args)
516 return "> bitcoin-cli " + methodname + " " + args + "\n";
519 std::string HelpExampleRpc(const std::string& methodname, const std::string& args)
521 return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", "
522 "\"method\": \"" + methodname + "\", \"params\": [" + args + "] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n";
525 void RPCSetTimerInterfaceIfUnset(RPCTimerInterface *iface)
527 if (!timerInterface)
528 timerInterface = iface;
531 void RPCSetTimerInterface(RPCTimerInterface *iface)
533 timerInterface = iface;
536 void RPCUnsetTimerInterface(RPCTimerInterface *iface)
538 if (timerInterface == iface)
539 timerInterface = nullptr;
542 void RPCRunLater(const std::string& name, std::function<void(void)> func, int64_t nSeconds)
544 if (!timerInterface)
545 throw JSONRPCError(RPC_INTERNAL_ERROR, "No timer handler registered for RPC");
546 deadlineTimers.erase(name);
547 LogPrint(BCLog::RPC, "queue run of timer %s in %i seconds (using %s)\n", name, nSeconds, timerInterface->Name());
548 deadlineTimers.emplace(name, std::unique_ptr<RPCTimerBase>(timerInterface->NewTimer(func, nSeconds*1000)));
551 int RPCSerializationFlags()
553 int flag = 0;
554 if (gArgs.GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) == 0)
555 flag |= SERIALIZE_TRANSACTION_NO_WITNESS;
556 return flag;
559 CRPCTable tableRPC;