Merge #10395: Replace boost::function with std::function (C++11)
[bitcoinplatinum.git] / src / rpc / server.cpp
blobc5fbff00775cdd546e301d702bf776d08f44579c
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/foreach.hpp>
21 #include <boost/shared_ptr.hpp>
22 #include <boost/signals2/signal.hpp>
23 #include <boost/thread.hpp>
24 #include <boost/algorithm/string/case_conv.hpp> // for to_upper()
25 #include <boost/algorithm/string/classification.hpp>
26 #include <boost/algorithm/string/split.hpp>
28 #include <memory> // for unique_ptr
29 #include <unordered_map>
31 static bool fRPCRunning = false;
32 static bool fRPCInWarmup = true;
33 static std::string rpcWarmupStatus("RPC server started");
34 static CCriticalSection cs_rpcWarmup;
35 /* Timer-creating functions */
36 static RPCTimerInterface* timerInterface = NULL;
37 /* Map of name to timer. */
38 static std::map<std::string, std::unique_ptr<RPCTimerBase> > deadlineTimers;
40 static struct CRPCSignals
42 boost::signals2::signal<void ()> Started;
43 boost::signals2::signal<void ()> Stopped;
44 boost::signals2::signal<void (const CRPCCommand&)> PreCommand;
45 } g_rpcSignals;
47 void RPCServer::OnStarted(std::function<void ()> slot)
49 g_rpcSignals.Started.connect(slot);
52 void RPCServer::OnStopped(std::function<void ()> slot)
54 g_rpcSignals.Stopped.connect(slot);
57 void RPCServer::OnPreCommand(std::function<void (const CRPCCommand&)> slot)
59 g_rpcSignals.PreCommand.connect(boost::bind(slot, _1));
62 void RPCTypeCheck(const UniValue& params,
63 const std::list<UniValue::VType>& typesExpected,
64 bool fAllowNull)
66 unsigned int i = 0;
67 BOOST_FOREACH(UniValue::VType t, typesExpected)
69 if (params.size() <= i)
70 break;
72 const UniValue& v = params[i];
73 if (!(fAllowNull && v.isNull())) {
74 RPCTypeCheckArgument(v, t);
76 i++;
80 void RPCTypeCheckArgument(const UniValue& value, UniValue::VType typeExpected)
82 if (value.type() != typeExpected) {
83 throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Expected type %s, got %s", uvTypeName(typeExpected), uvTypeName(value.type())));
87 void RPCTypeCheckObj(const UniValue& o,
88 const std::map<std::string, UniValueType>& typesExpected,
89 bool fAllowNull,
90 bool fStrict)
92 for (const auto& t : typesExpected) {
93 const UniValue& v = find_value(o, t.first);
94 if (!fAllowNull && v.isNull())
95 throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing %s", t.first));
97 if (!(t.second.typeAny || v.type() == t.second.type || (fAllowNull && v.isNull()))) {
98 std::string err = strprintf("Expected type %s for %s, got %s",
99 uvTypeName(t.second.type), t.first, uvTypeName(v.type()));
100 throw JSONRPCError(RPC_TYPE_ERROR, err);
104 if (fStrict)
106 BOOST_FOREACH(const std::string& k, o.getKeys())
108 if (typesExpected.count(k) == 0)
110 std::string err = strprintf("Unexpected key %s", k);
111 throw JSONRPCError(RPC_TYPE_ERROR, err);
117 CAmount AmountFromValue(const UniValue& value)
119 if (!value.isNum() && !value.isStr())
120 throw JSONRPCError(RPC_TYPE_ERROR, "Amount is not a number or string");
121 CAmount amount;
122 if (!ParseFixedPoint(value.getValStr(), 8, &amount))
123 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount");
124 if (!MoneyRange(amount))
125 throw JSONRPCError(RPC_TYPE_ERROR, "Amount out of range");
126 return amount;
129 UniValue ValueFromAmount(const CAmount& amount)
131 bool sign = amount < 0;
132 int64_t n_abs = (sign ? -amount : amount);
133 int64_t quotient = n_abs / COIN;
134 int64_t remainder = n_abs % COIN;
135 return UniValue(UniValue::VNUM,
136 strprintf("%s%d.%08d", sign ? "-" : "", quotient, remainder));
139 uint256 ParseHashV(const UniValue& v, std::string strName)
141 std::string strHex;
142 if (v.isStr())
143 strHex = v.get_str();
144 if (!IsHex(strHex)) // Note: IsHex("") is false
145 throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
146 if (64 != strHex.length())
147 throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s must be of length %d (not %d)", strName, 64, strHex.length()));
148 uint256 result;
149 result.SetHex(strHex);
150 return result;
152 uint256 ParseHashO(const UniValue& o, std::string strKey)
154 return ParseHashV(find_value(o, strKey), strKey);
156 std::vector<unsigned char> ParseHexV(const UniValue& v, std::string strName)
158 std::string strHex;
159 if (v.isStr())
160 strHex = v.get_str();
161 if (!IsHex(strHex))
162 throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
163 return ParseHex(strHex);
165 std::vector<unsigned char> ParseHexO(const UniValue& o, std::string strKey)
167 return ParseHexV(find_value(o, strKey), strKey);
171 * Note: This interface may still be subject to change.
174 std::string CRPCTable::help(const std::string& strCommand, const JSONRPCRequest& helpreq) const
176 std::string strRet;
177 std::string category;
178 std::set<rpcfn_type> setDone;
179 std::vector<std::pair<std::string, const CRPCCommand*> > vCommands;
181 for (std::map<std::string, const CRPCCommand*>::const_iterator mi = mapCommands.begin(); mi != mapCommands.end(); ++mi)
182 vCommands.push_back(make_pair(mi->second->category + mi->first, mi->second));
183 sort(vCommands.begin(), vCommands.end());
185 JSONRPCRequest jreq(helpreq);
186 jreq.fHelp = true;
187 jreq.params = UniValue();
189 BOOST_FOREACH(const PAIRTYPE(std::string, const CRPCCommand*)& command, vCommands)
191 const CRPCCommand *pcmd = command.second;
192 std::string strMethod = pcmd->name;
193 if ((strCommand != "" || pcmd->category == "hidden") && strMethod != strCommand)
194 continue;
195 jreq.strMethod = strMethod;
198 rpcfn_type pfn = pcmd->actor;
199 if (setDone.insert(pfn).second)
200 (*pfn)(jreq);
202 catch (const std::exception& e)
204 // Help text is returned in an exception
205 std::string strHelp = std::string(e.what());
206 if (strCommand == "")
208 if (strHelp.find('\n') != std::string::npos)
209 strHelp = strHelp.substr(0, strHelp.find('\n'));
211 if (category != pcmd->category)
213 if (!category.empty())
214 strRet += "\n";
215 category = pcmd->category;
216 std::string firstLetter = category.substr(0,1);
217 boost::to_upper(firstLetter);
218 strRet += "== " + firstLetter + category.substr(1) + " ==\n";
221 strRet += strHelp + "\n";
224 if (strRet == "")
225 strRet = strprintf("help: unknown command: %s\n", strCommand);
226 strRet = strRet.substr(0,strRet.size()-1);
227 return strRet;
230 UniValue help(const JSONRPCRequest& jsonRequest)
232 if (jsonRequest.fHelp || jsonRequest.params.size() > 1)
233 throw std::runtime_error(
234 "help ( \"command\" )\n"
235 "\nList all commands, or get help for a specified command.\n"
236 "\nArguments:\n"
237 "1. \"command\" (string, optional) The command to get help on\n"
238 "\nResult:\n"
239 "\"text\" (string) The help text\n"
242 std::string strCommand;
243 if (jsonRequest.params.size() > 0)
244 strCommand = jsonRequest.params[0].get_str();
246 return tableRPC.help(strCommand, jsonRequest);
250 UniValue stop(const JSONRPCRequest& jsonRequest)
252 // Accept the deprecated and ignored 'detach' boolean argument
253 if (jsonRequest.fHelp || jsonRequest.params.size() > 1)
254 throw std::runtime_error(
255 "stop\n"
256 "\nStop Bitcoin server.");
257 // Event loop will exit after current HTTP requests have been handled, so
258 // this reply will get back to the client.
259 StartShutdown();
260 return "Bitcoin server stopping";
264 * Call Table
266 static const CRPCCommand vRPCCommands[] =
267 { // category name actor (function) okSafe argNames
268 // --------------------- ------------------------ ----------------------- ------ ----------
269 /* Overall control/query calls */
270 { "control", "help", &help, true, {"command"} },
271 { "control", "stop", &stop, true, {} },
274 CRPCTable::CRPCTable()
276 unsigned int vcidx;
277 for (vcidx = 0; vcidx < (sizeof(vRPCCommands) / sizeof(vRPCCommands[0])); vcidx++)
279 const CRPCCommand *pcmd;
281 pcmd = &vRPCCommands[vcidx];
282 mapCommands[pcmd->name] = pcmd;
286 const CRPCCommand *CRPCTable::operator[](const std::string &name) const
288 std::map<std::string, const CRPCCommand*>::const_iterator it = mapCommands.find(name);
289 if (it == mapCommands.end())
290 return NULL;
291 return (*it).second;
294 bool CRPCTable::appendCommand(const std::string& name, const CRPCCommand* pcmd)
296 if (IsRPCRunning())
297 return false;
299 // don't allow overwriting for now
300 std::map<std::string, const CRPCCommand*>::const_iterator it = mapCommands.find(name);
301 if (it != mapCommands.end())
302 return false;
304 mapCommands[name] = pcmd;
305 return true;
308 bool StartRPC()
310 LogPrint(BCLog::RPC, "Starting RPC\n");
311 fRPCRunning = true;
312 g_rpcSignals.Started();
313 return true;
316 void InterruptRPC()
318 LogPrint(BCLog::RPC, "Interrupting RPC\n");
319 // Interrupt e.g. running longpolls
320 fRPCRunning = false;
323 void StopRPC()
325 LogPrint(BCLog::RPC, "Stopping RPC\n");
326 deadlineTimers.clear();
327 DeleteAuthCookie();
328 g_rpcSignals.Stopped();
331 bool IsRPCRunning()
333 return fRPCRunning;
336 void SetRPCWarmupStatus(const std::string& newStatus)
338 LOCK(cs_rpcWarmup);
339 rpcWarmupStatus = newStatus;
342 void SetRPCWarmupFinished()
344 LOCK(cs_rpcWarmup);
345 assert(fRPCInWarmup);
346 fRPCInWarmup = false;
349 bool RPCIsInWarmup(std::string *outStatus)
351 LOCK(cs_rpcWarmup);
352 if (outStatus)
353 *outStatus = rpcWarmupStatus;
354 return fRPCInWarmup;
357 void JSONRPCRequest::parse(const UniValue& valRequest)
359 // Parse request
360 if (!valRequest.isObject())
361 throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object");
362 const UniValue& request = valRequest.get_obj();
364 // Parse id now so errors from here on will have the id
365 id = find_value(request, "id");
367 // Parse method
368 UniValue valMethod = find_value(request, "method");
369 if (valMethod.isNull())
370 throw JSONRPCError(RPC_INVALID_REQUEST, "Missing method");
371 if (!valMethod.isStr())
372 throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string");
373 strMethod = valMethod.get_str();
374 LogPrint(BCLog::RPC, "ThreadRPCServer method=%s\n", SanitizeString(strMethod));
376 // Parse params
377 UniValue valParams = find_value(request, "params");
378 if (valParams.isArray() || valParams.isObject())
379 params = valParams;
380 else if (valParams.isNull())
381 params = UniValue(UniValue::VARR);
382 else
383 throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array or object");
386 static UniValue JSONRPCExecOne(const UniValue& req)
388 UniValue rpc_result(UniValue::VOBJ);
390 JSONRPCRequest jreq;
391 try {
392 jreq.parse(req);
394 UniValue result = tableRPC.execute(jreq);
395 rpc_result = JSONRPCReplyObj(result, NullUniValue, jreq.id);
397 catch (const UniValue& objError)
399 rpc_result = JSONRPCReplyObj(NullUniValue, objError, jreq.id);
401 catch (const std::exception& e)
403 rpc_result = JSONRPCReplyObj(NullUniValue,
404 JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
407 return rpc_result;
410 std::string JSONRPCExecBatch(const UniValue& vReq)
412 UniValue ret(UniValue::VARR);
413 for (unsigned int reqIdx = 0; reqIdx < vReq.size(); reqIdx++)
414 ret.push_back(JSONRPCExecOne(vReq[reqIdx]));
416 return ret.write() + "\n";
420 * Process named arguments into a vector of positional arguments, based on the
421 * passed-in specification for the RPC call's arguments.
423 static inline JSONRPCRequest transformNamedArguments(const JSONRPCRequest& in, const std::vector<std::string>& argNames)
425 JSONRPCRequest out = in;
426 out.params = UniValue(UniValue::VARR);
427 // Build a map of parameters, and remove ones that have been processed, so that we can throw a focused error if
428 // there is an unknown one.
429 const std::vector<std::string>& keys = in.params.getKeys();
430 const std::vector<UniValue>& values = in.params.getValues();
431 std::unordered_map<std::string, const UniValue*> argsIn;
432 for (size_t i=0; i<keys.size(); ++i) {
433 argsIn[keys[i]] = &values[i];
435 // Process expected parameters.
436 int hole = 0;
437 for (const std::string &argNamePattern: argNames) {
438 std::vector<std::string> vargNames;
439 boost::algorithm::split(vargNames, argNamePattern, boost::algorithm::is_any_of("|"));
440 auto fr = argsIn.end();
441 for (const std::string & argName : vargNames) {
442 fr = argsIn.find(argName);
443 if (fr != argsIn.end()) {
444 break;
447 if (fr != argsIn.end()) {
448 for (int i = 0; i < hole; ++i) {
449 // Fill hole between specified parameters with JSON nulls,
450 // but not at the end (for backwards compatibility with calls
451 // that act based on number of specified parameters).
452 out.params.push_back(UniValue());
454 hole = 0;
455 out.params.push_back(*fr->second);
456 argsIn.erase(fr);
457 } else {
458 hole += 1;
461 // If there are still arguments in the argsIn map, this is an error.
462 if (!argsIn.empty()) {
463 throw JSONRPCError(RPC_INVALID_PARAMETER, "Unknown named parameter " + argsIn.begin()->first);
465 // Return request with named arguments transformed to positional arguments
466 return out;
469 UniValue CRPCTable::execute(const JSONRPCRequest &request) const
471 // Return immediately if in warmup
473 LOCK(cs_rpcWarmup);
474 if (fRPCInWarmup)
475 throw JSONRPCError(RPC_IN_WARMUP, rpcWarmupStatus);
478 // Find method
479 const CRPCCommand *pcmd = tableRPC[request.strMethod];
480 if (!pcmd)
481 throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found");
483 g_rpcSignals.PreCommand(*pcmd);
487 // Execute, convert arguments to array if necessary
488 if (request.params.isObject()) {
489 return pcmd->actor(transformNamedArguments(request, pcmd->argNames));
490 } else {
491 return pcmd->actor(request);
494 catch (const std::exception& e)
496 throw JSONRPCError(RPC_MISC_ERROR, e.what());
500 std::vector<std::string> CRPCTable::listCommands() const
502 std::vector<std::string> commandList;
503 typedef std::map<std::string, const CRPCCommand*> commandMap;
505 std::transform( mapCommands.begin(), mapCommands.end(),
506 std::back_inserter(commandList),
507 boost::bind(&commandMap::value_type::first,_1) );
508 return commandList;
511 std::string HelpExampleCli(const std::string& methodname, const std::string& args)
513 return "> bitcoin-cli " + methodname + " " + args + "\n";
516 std::string HelpExampleRpc(const std::string& methodname, const std::string& args)
518 return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", "
519 "\"method\": \"" + methodname + "\", \"params\": [" + args + "] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n";
522 void RPCSetTimerInterfaceIfUnset(RPCTimerInterface *iface)
524 if (!timerInterface)
525 timerInterface = iface;
528 void RPCSetTimerInterface(RPCTimerInterface *iface)
530 timerInterface = iface;
533 void RPCUnsetTimerInterface(RPCTimerInterface *iface)
535 if (timerInterface == iface)
536 timerInterface = NULL;
539 void RPCRunLater(const std::string& name, std::function<void(void)> func, int64_t nSeconds)
541 if (!timerInterface)
542 throw JSONRPCError(RPC_INTERNAL_ERROR, "No timer handler registered for RPC");
543 deadlineTimers.erase(name);
544 LogPrint(BCLog::RPC, "queue run of timer %s in %i seconds (using %s)\n", name, nSeconds, timerInterface->Name());
545 deadlineTimers.emplace(name, std::unique_ptr<RPCTimerBase>(timerInterface->NewTimer(func, nSeconds*1000)));
548 int RPCSerializationFlags()
550 int flag = 0;
551 if (GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) == 0)
552 flag |= SERIALIZE_TRANSACTION_NO_WITNESS;
553 return flag;
556 CRPCTable tableRPC;