1 // Copyright (c) 2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2015 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 "rpc/server.h"
7
8 #include "base58.h"
9 #include "init.h"
10 #include "random.h"
11 #include "sync.h"
12 #include "ui_interface.h"
13 #include "util.h"
14 #include "utilstrencodings.h"
15
16 #include <univalue.h>
17
18 #include <boost/bind.hpp>
19 #include <boost/filesystem.hpp>
20 #include <boost/foreach.hpp>
21 #include <boost/iostreams/concepts.hpp>
22 #include <boost/iostreams/stream.hpp>
23 #include <boost/shared_ptr.hpp>
24 #include <boost/signals2/signal.hpp>
25 #include <boost/thread.hpp>
26 #include <boost/algorithm/string/case_conv.hpp> // for to_upper()
27
28 using namespace RPCServer;
29 using namespace std;
30
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 * @note Can be changed to std::unique_ptr when C++11 */
39 static std::map<std::string, boost::shared_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 boost::signals2::signal<void (const CRPCCommand&)> PostCommand;
47 } g_rpcSignals;
48
OnStarted(boost::function<void ()> slot)49 void RPCServer::OnStarted(boost::function<void ()> slot)
50 {
51 g_rpcSignals.Started.connect(slot);
52 }
53
OnStopped(boost::function<void ()> slot)54 void RPCServer::OnStopped(boost::function<void ()> slot)
55 {
56 g_rpcSignals.Stopped.connect(slot);
57 }
58
OnPreCommand(boost::function<void (const CRPCCommand &)> slot)59 void RPCServer::OnPreCommand(boost::function<void (const CRPCCommand&)> slot)
60 {
61 g_rpcSignals.PreCommand.connect(boost::bind(slot, _1));
62 }
63
OnPostCommand(boost::function<void (const CRPCCommand &)> slot)64 void RPCServer::OnPostCommand(boost::function<void (const CRPCCommand&)> slot)
65 {
66 g_rpcSignals.PostCommand.connect(boost::bind(slot, _1));
67 }
68
RPCTypeCheck(const UniValue & params,const list<UniValue::VType> & typesExpected,bool fAllowNull)69 void RPCTypeCheck(const UniValue& params,
70 const list<UniValue::VType>& typesExpected,
71 bool fAllowNull)
72 {
73 unsigned int i = 0;
74 BOOST_FOREACH(UniValue::VType t, typesExpected)
75 {
76 if (params.size() <= i)
77 break;
78
79 const UniValue& v = params[i];
80 if (!((v.type() == t) || (fAllowNull && (v.isNull()))))
81 {
82 string err = strprintf("Expected type %s, got %s",
83 uvTypeName(t), uvTypeName(v.type()));
84 throw JSONRPCError(RPC_TYPE_ERROR, err);
85 }
86 i++;
87 }
88 }
89
RPCTypeCheckObj(const UniValue & o,const map<string,UniValueType> & typesExpected,bool fAllowNull,bool fStrict)90 void RPCTypeCheckObj(const UniValue& o,
91 const map<string, UniValueType>& typesExpected,
92 bool fAllowNull,
93 bool fStrict)
94 {
95 for (const auto& t : typesExpected) {
96 const UniValue& v = find_value(o, t.first);
97 if (!fAllowNull && v.isNull())
98 throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing %s", t.first));
99
100 if (!(t.second.typeAny || v.type() == t.second.type || (fAllowNull && v.isNull()))) {
101 string err = strprintf("Expected type %s for %s, got %s",
102 uvTypeName(t.second.type), t.first, uvTypeName(v.type()));
103 throw JSONRPCError(RPC_TYPE_ERROR, err);
104 }
105 }
106
107 if (fStrict)
108 {
109 BOOST_FOREACH(const string& k, o.getKeys())
110 {
111 if (typesExpected.count(k) == 0)
112 {
113 string err = strprintf("Unexpected key %s", k);
114 throw JSONRPCError(RPC_TYPE_ERROR, err);
115 }
116 }
117 }
118 }
119
AmountFromValue(const UniValue & value)120 CAmount AmountFromValue(const UniValue& value)
121 {
122 if (!value.isNum() && !value.isStr())
123 throw JSONRPCError(RPC_TYPE_ERROR, "Amount is not a number or string");
124 CAmount amount;
125 if (!ParseFixedPoint(value.getValStr(), 8, &amount))
126 throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount");
127 if (!MoneyRange(amount))
128 throw JSONRPCError(RPC_TYPE_ERROR, "Amount out of range");
129 return amount;
130 }
131
ValueFromAmount(const CAmount & amount)132 UniValue ValueFromAmount(const CAmount& amount)
133 {
134 bool sign = amount < 0;
135 int64_t n_abs = (sign ? -amount : amount);
136 int64_t quotient = n_abs / COIN;
137 int64_t remainder = n_abs % COIN;
138 return UniValue(UniValue::VNUM,
139 strprintf("%s%d.%08d", sign ? "-" : "", quotient, remainder));
140 }
141
ParseHashV(const UniValue & v,string strName)142 uint256 ParseHashV(const UniValue& v, string strName)
143 {
144 string strHex;
145 if (v.isStr())
146 strHex = v.get_str();
147 if (!IsHex(strHex)) // Note: IsHex("") is false
148 throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
149 if (64 != strHex.length())
150 throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("%s must be of length %d (not %d)", strName, 64, strHex.length()));
151 uint256 result;
152 result.SetHex(strHex);
153 return result;
154 }
ParseHashO(const UniValue & o,string strKey)155 uint256 ParseHashO(const UniValue& o, string strKey)
156 {
157 return ParseHashV(find_value(o, strKey), strKey);
158 }
ParseHexV(const UniValue & v,string strName)159 vector<unsigned char> ParseHexV(const UniValue& v, string strName)
160 {
161 string strHex;
162 if (v.isStr())
163 strHex = v.get_str();
164 if (!IsHex(strHex))
165 throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
166 return ParseHex(strHex);
167 }
ParseHexO(const UniValue & o,string strKey)168 vector<unsigned char> ParseHexO(const UniValue& o, string strKey)
169 {
170 return ParseHexV(find_value(o, strKey), strKey);
171 }
172
173 /**
174 * Note: This interface may still be subject to change.
175 */
176
help(const std::string & strCommand) const177 std::string CRPCTable::help(const std::string& strCommand) const
178 {
179 string strRet;
180 string category;
181 set<rpcfn_type> setDone;
182 vector<pair<string, const CRPCCommand*> > vCommands;
183
184 for (map<string, const CRPCCommand*>::const_iterator mi = mapCommands.begin(); mi != mapCommands.end(); ++mi)
185 vCommands.push_back(make_pair(mi->second->category + mi->first, mi->second));
186 sort(vCommands.begin(), vCommands.end());
187
188 BOOST_FOREACH(const PAIRTYPE(string, const CRPCCommand*)& command, vCommands)
189 {
190 const CRPCCommand *pcmd = command.second;
191 string strMethod = pcmd->name;
192 // We already filter duplicates, but these deprecated screw up the sort order
193 if (strMethod.find("label") != string::npos)
194 continue;
195 if ((strCommand != "" || pcmd->category == "hidden") && strMethod != strCommand)
196 continue;
197 try
198 {
199 UniValue params;
200 rpcfn_type pfn = pcmd->actor;
201 if (setDone.insert(pfn).second)
202 (*pfn)(params, true);
203 }
204 catch (const std::exception& e)
205 {
206 // Help text is returned in an exception
207 string strHelp = string(e.what());
208 if (strCommand == "")
209 {
210 if (strHelp.find('\n') != string::npos)
211 strHelp = strHelp.substr(0, strHelp.find('\n'));
212
213 if (category != pcmd->category)
214 {
215 if (!category.empty())
216 strRet += "\n";
217 category = pcmd->category;
218 string firstLetter = category.substr(0,1);
219 boost::to_upper(firstLetter);
220 strRet += "== " + firstLetter + category.substr(1) + " ==\n";
221 }
222 }
223 strRet += strHelp + "\n";
224 }
225 }
226 if (strRet == "")
227 strRet = strprintf("help: unknown command: %s\n", strCommand);
228 strRet = strRet.substr(0,strRet.size()-1);
229 return strRet;
230 }
231
help(const UniValue & params,bool fHelp)232 UniValue help(const UniValue& params, bool fHelp)
233 {
234 if (fHelp || params.size() > 1)
235 throw runtime_error(
236 "help ( \"command\" )\n"
237 "\nList all commands, or get help for a specified command.\n"
238 "\nArguments:\n"
239 "1. \"command\" (string, optional) The command to get help on\n"
240 "\nResult:\n"
241 "\"text\" (string) The help text\n"
242 );
243
244 string strCommand;
245 if (params.size() > 0)
246 strCommand = params[0].get_str();
247
248 return tableRPC.help(strCommand);
249 }
250
251
stop(const UniValue & params,bool fHelp)252 UniValue stop(const UniValue& params, bool fHelp)
253 {
254 // Accept the deprecated and ignored 'detach' boolean argument
255 if (fHelp || params.size() > 1)
256 throw runtime_error(
257 "stop\n"
258 "\nStop Zetacoin server.");
259 // Event loop will exit after current HTTP requests have been handled, so
260 // this reply will get back to the client.
261 StartShutdown();
262 return "Zetacoin server stopping";
263 }
264
265 /**
266 * Call Table
267 */
268 static const CRPCCommand vRPCCommands[] =
269 { // category name actor (function) okSafeMode
270 // --------------------- ------------------------ ----------------------- ----------
271 /* Overall control/query calls */
272 { "control", "help", &help, true },
273 { "control", "stop", &stop, true },
274 };
275
CRPCTable()276 CRPCTable::CRPCTable()
277 {
278 unsigned int vcidx;
279 for (vcidx = 0; vcidx < (sizeof(vRPCCommands) / sizeof(vRPCCommands[0])); vcidx++)
280 {
281 const CRPCCommand *pcmd;
282
283 pcmd = &vRPCCommands[vcidx];
284 mapCommands[pcmd->name] = pcmd;
285 }
286 }
287
operator [](const std::string & name) const288 const CRPCCommand *CRPCTable::operator[](const std::string &name) const
289 {
290 map<string, const CRPCCommand*>::const_iterator it = mapCommands.find(name);
291 if (it == mapCommands.end())
292 return NULL;
293 return (*it).second;
294 }
295
appendCommand(const std::string & name,const CRPCCommand * pcmd)296 bool CRPCTable::appendCommand(const std::string& name, const CRPCCommand* pcmd)
297 {
298 if (IsRPCRunning())
299 return false;
300
301 // don't allow overwriting for now
302 map<string, const CRPCCommand*>::const_iterator it = mapCommands.find(name);
303 if (it != mapCommands.end())
304 return false;
305
306 mapCommands[name] = pcmd;
307 return true;
308 }
309
StartRPC()310 bool StartRPC()
311 {
312 LogPrint("rpc", "Starting RPC\n");
313 fRPCRunning = true;
314 g_rpcSignals.Started();
315 return true;
316 }
317
InterruptRPC()318 void InterruptRPC()
319 {
320 LogPrint("rpc", "Interrupting RPC\n");
321 // Interrupt e.g. running longpolls
322 fRPCRunning = false;
323 }
324
StopRPC()325 void StopRPC()
326 {
327 LogPrint("rpc", "Stopping RPC\n");
328 deadlineTimers.clear();
329 g_rpcSignals.Stopped();
330 }
331
IsRPCRunning()332 bool IsRPCRunning()
333 {
334 return fRPCRunning;
335 }
336
SetRPCWarmupStatus(const std::string & newStatus)337 void SetRPCWarmupStatus(const std::string& newStatus)
338 {
339 LOCK(cs_rpcWarmup);
340 rpcWarmupStatus = newStatus;
341 }
342
SetRPCWarmupFinished()343 void SetRPCWarmupFinished()
344 {
345 LOCK(cs_rpcWarmup);
346 assert(fRPCInWarmup);
347 fRPCInWarmup = false;
348 }
349
RPCIsInWarmup(std::string * outStatus)350 bool RPCIsInWarmup(std::string *outStatus)
351 {
352 LOCK(cs_rpcWarmup);
353 if (outStatus)
354 *outStatus = rpcWarmupStatus;
355 return fRPCInWarmup;
356 }
357
parse(const UniValue & valRequest)358 void JSONRequest::parse(const UniValue& valRequest)
359 {
360 // Parse request
361 if (!valRequest.isObject())
362 throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object");
363 const UniValue& request = valRequest.get_obj();
364
365 // Parse id now so errors from here on will have the id
366 id = find_value(request, "id");
367
368 // Parse method
369 UniValue valMethod = find_value(request, "method");
370 if (valMethod.isNull())
371 throw JSONRPCError(RPC_INVALID_REQUEST, "Missing method");
372 if (!valMethod.isStr())
373 throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string");
374 strMethod = valMethod.get_str();
375 if (strMethod != "getblocktemplate")
376 LogPrint("rpc", "ThreadRPCServer method=%s\n", SanitizeString(strMethod));
377
378 // Parse params
379 UniValue valParams = find_value(request, "params");
380 if (valParams.isArray())
381 params = valParams.get_array();
382 else if (valParams.isNull())
383 params = UniValue(UniValue::VARR);
384 else
385 throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array");
386 }
387
JSONRPCExecOne(const UniValue & req)388 static UniValue JSONRPCExecOne(const UniValue& req)
389 {
390 UniValue rpc_result(UniValue::VOBJ);
391
392 JSONRequest jreq;
393 try {
394 jreq.parse(req);
395
396 UniValue result = tableRPC.execute(jreq.strMethod, jreq.params);
397 rpc_result = JSONRPCReplyObj(result, NullUniValue, jreq.id);
398 }
399 catch (const UniValue& objError)
400 {
401 rpc_result = JSONRPCReplyObj(NullUniValue, objError, jreq.id);
402 }
403 catch (const std::exception& e)
404 {
405 rpc_result = JSONRPCReplyObj(NullUniValue,
406 JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
407 }
408
409 return rpc_result;
410 }
411
JSONRPCExecBatch(const UniValue & vReq)412 std::string JSONRPCExecBatch(const UniValue& vReq)
413 {
414 UniValue ret(UniValue::VARR);
415 for (unsigned int reqIdx = 0; reqIdx < vReq.size(); reqIdx++)
416 ret.push_back(JSONRPCExecOne(vReq[reqIdx]));
417
418 return ret.write() + "\n";
419 }
420
execute(const std::string & strMethod,const UniValue & params) const421 UniValue CRPCTable::execute(const std::string &strMethod, const UniValue ¶ms) const
422 {
423 // Return immediately if in warmup
424 {
425 LOCK(cs_rpcWarmup);
426 if (fRPCInWarmup)
427 throw JSONRPCError(RPC_IN_WARMUP, rpcWarmupStatus);
428 }
429
430 // Find method
431 const CRPCCommand *pcmd = tableRPC[strMethod];
432 if (!pcmd)
433 throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found");
434
435 g_rpcSignals.PreCommand(*pcmd);
436
437 try
438 {
439 // Execute
440 return pcmd->actor(params, false);
441 }
442 catch (const std::exception& e)
443 {
444 throw JSONRPCError(RPC_MISC_ERROR, e.what());
445 }
446
447 g_rpcSignals.PostCommand(*pcmd);
448 }
449
listCommands() const450 std::vector<std::string> CRPCTable::listCommands() const
451 {
452 std::vector<std::string> commandList;
453 typedef std::map<std::string, const CRPCCommand*> commandMap;
454
455 std::transform( mapCommands.begin(), mapCommands.end(),
456 std::back_inserter(commandList),
457 boost::bind(&commandMap::value_type::first,_1) );
458 return commandList;
459 }
460
HelpExampleCli(const std::string & methodname,const std::string & args)461 std::string HelpExampleCli(const std::string& methodname, const std::string& args)
462 {
463 return "> zetacoin-cli " + methodname + " " + args + "\n";
464 }
465
HelpExampleRpc(const std::string & methodname,const std::string & args)466 std::string HelpExampleRpc(const std::string& methodname, const std::string& args)
467 {
468 return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", "
469 "\"method\": \"" + methodname + "\", \"params\": [" + args + "] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n";
470 }
471
RPCSetTimerInterfaceIfUnset(RPCTimerInterface * iface)472 void RPCSetTimerInterfaceIfUnset(RPCTimerInterface *iface)
473 {
474 if (!timerInterface)
475 timerInterface = iface;
476 }
477
RPCSetTimerInterface(RPCTimerInterface * iface)478 void RPCSetTimerInterface(RPCTimerInterface *iface)
479 {
480 timerInterface = iface;
481 }
482
RPCUnsetTimerInterface(RPCTimerInterface * iface)483 void RPCUnsetTimerInterface(RPCTimerInterface *iface)
484 {
485 if (timerInterface == iface)
486 timerInterface = NULL;
487 }
488
RPCRunLater(const std::string & name,boost::function<void (void)> func,int64_t nSeconds)489 void RPCRunLater(const std::string& name, boost::function<void(void)> func, int64_t nSeconds)
490 {
491 if (!timerInterface)
492 throw JSONRPCError(RPC_INTERNAL_ERROR, "No timer handler registered for RPC");
493 deadlineTimers.erase(name);
494 LogPrint("rpc", "queue run of timer %s in %i seconds (using %s)\n", name, nSeconds, timerInterface->Name());
495 deadlineTimers.insert(std::make_pair(name, boost::shared_ptr<RPCTimerBase>(timerInterface->NewTimer(func, nSeconds*1000))));
496 }
497
RPCSerializationFlags()498 int RPCSerializationFlags()
499 {
500 int flag = 0;
501 if (GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) == 0)
502 flag |= SERIALIZE_TRANSACTION_NO_WITNESS;
503 return flag;
504 }
505
506 CRPCTable tableRPC;
507