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 &params) 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