2450ca4638073ecee4598a1a0919dbf33e46f0de
[novacoin.git] / src / bitcoinrpc.cpp
1 // Copyright (c) 2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2012 The Bitcoin developers
3 // Distributed under the MIT/X11 software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
6 #include "init.h"
7 #include "util.h"
8 #include "sync.h"
9 #include "interface.h"
10 #include "base58.h"
11 #include "bitcoinrpc.h"
12 #include "db.h"
13
14 #undef printf
15 #include <memory>
16 #include <ixwebsocket/IXHttpServer.h>
17 #include <ixwebsocket/IXHttpClient.h>
18 #include <boost/asio.hpp>
19 #include <boost/asio/ip/v6_only.hpp>
20 #include <boost/bind.hpp>
21 #include <boost/filesystem.hpp>
22 #include <boost/foreach.hpp>
23 #include <boost/iostreams/concepts.hpp>
24 #include <boost/iostreams/stream.hpp>
25 #include <boost/algorithm/string.hpp>
26 #include <boost/lexical_cast.hpp>
27 #include <boost/asio/ssl.hpp>
28 #include <boost/filesystem/fstream.hpp>
29 #include <boost/shared_ptr.hpp>
30 #include <list>
31
32 #define printf OutputDebugStringF
33
34 using namespace std;
35 using namespace boost;
36 using namespace json_spirit;
37
38 std::unique_ptr<ix::HttpServer> g_server;
39
40 static std::string strRPCUserColonPass;
41
42 const Object emptyobj;
43
44 static inline unsigned short GetDefaultRPCPort()
45 {
46     return GetBoolArg("-testnet", false) ? 18344 : 8344;
47 }
48
49 Object JSONRPCError(int code, const string& message)
50 {
51     Object error;
52     error.push_back(Pair("code", code));
53     error.push_back(Pair("message", message));
54     return error;
55 }
56
57 void RPCTypeCheck(const Array& params,
58                   const list<Value_type>& typesExpected,
59                   bool fAllowNull)
60 {
61     unsigned int i = 0;
62     BOOST_FOREACH(Value_type t, typesExpected)
63     {
64         if (params.size() <= i)
65             break;
66
67         const Value& v = params[i];
68         if (!((v.type() == t) || (fAllowNull && (v.type() == null_type))))
69         {
70             string err = strprintf("Expected type %s, got %s",
71                                    Value_type_name[t], Value_type_name[v.type()]);
72             throw JSONRPCError(RPC_TYPE_ERROR, err);
73         }
74         i++;
75     }
76 }
77
78 void RPCTypeCheck(const Object& o,
79                   const map<string, Value_type>& typesExpected,
80                   bool fAllowNull)
81 {
82     BOOST_FOREACH(const PAIRTYPE(string, Value_type)& t, typesExpected)
83     {
84         const Value& v = find_value(o, t.first);
85         if (!fAllowNull && v.type() == null_type)
86             throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing %s", t.first.c_str()));
87
88         if (!((v.type() == t.second) || (fAllowNull && (v.type() == null_type))))
89         {
90             string err = strprintf("Expected type %s for %s, got %s",
91                                    Value_type_name[t.second], t.first.c_str(), Value_type_name[v.type()]);
92             throw JSONRPCError(RPC_TYPE_ERROR, err);
93         }
94     }
95 }
96
97 int64_t AmountFromValue(const Value& value)
98 {
99     double dAmount = value.get_real();
100     if (dAmount <= 0.0 || dAmount > MAX_MONEY)
101         throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount");
102     int64_t nAmount = roundint64(dAmount * COIN);
103     if (!MoneyRange(nAmount))
104         throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount");
105     return nAmount;
106 }
107
108 Value ValueFromAmount(int64_t amount)
109 {
110     return (double)amount / (double)COIN;
111 }
112
113 std::string HexBits(unsigned int nBits)
114 {
115     union {
116         int32_t nBits;
117         char cBits[4];
118     } uBits;
119     uBits.nBits = htonl((int32_t)nBits);
120     return HexStr(BEGIN(uBits.cBits), END(uBits.cBits));
121 }
122
123
124 //
125 // Utilities: convert hex-encoded Values
126 // (throws error if not hex).
127 //
128 uint256 ParseHashV(const Value& v, string strName)
129 {
130     string strHex;
131     if (v.type() == str_type)
132         strHex = v.get_str();
133     if (!IsHex(strHex)) // Note: IsHex("") is false
134         throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
135     uint256 result;
136     result.SetHex(strHex);
137     return result;
138 }
139
140 uint256 ParseHashO(const Object& o, string strKey)
141 {
142     return ParseHashV(find_value(o, strKey), strKey);
143 }
144
145 vector<unsigned char> ParseHexV(const Value& v, string strName)
146 {
147     string strHex;
148     if (v.type() == str_type)
149         strHex = v.get_str();
150     if (!IsHex(strHex))
151         throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
152     return ParseHex(strHex);
153 }
154
155 vector<unsigned char> ParseHexO(const Object& o, string strKey)
156 {
157     return ParseHexV(find_value(o, strKey), strKey);
158 }
159
160
161 ///
162 /// Note: This interface may still be subject to change.
163 ///
164
165 string CRPCTable::help(string strCommand) const
166 {
167     string strRet;
168     set<rpcfn_type> setDone;
169     for (map<string, const CRPCCommand*>::const_iterator mi = mapCommands.begin(); mi != mapCommands.end(); ++mi)
170     {
171         const CRPCCommand *pcmd = mi->second;
172         string strMethod = mi->first;
173         // We already filter duplicates, but these deprecated screw up the sort order
174         if (strMethod.find("label") != string::npos)
175             continue;
176         if (!strCommand.empty() && strMethod != strCommand)
177             continue;
178         try
179         {
180             Array params;
181             rpcfn_type pfn = pcmd->actor;
182             if (setDone.insert(pfn).second)
183                 (*pfn)(params, true);
184         }
185         catch (std::exception& e)
186         {
187             // Help text is returned in an exception
188             string strHelp = string(e.what());
189             if (strCommand.empty())
190                 if (strHelp.find('\n') != string::npos)
191                     strHelp = strHelp.substr(0, strHelp.find('\n'));
192             strRet += strHelp + "\n";
193         }
194     }
195     if (strRet.empty())
196         strRet = strprintf("help: unknown command: %s\n", strCommand.c_str());
197     strRet = strRet.substr(0,strRet.size()-1);
198     return strRet;
199 }
200
201 Value help(const Array& params, bool fHelp)
202 {
203     if (fHelp || params.size() > 1)
204         throw runtime_error(
205             "help [command]\n"
206             "List commands, or get help for a command.");
207
208     string strCommand;
209     if (params.size() > 0)
210         strCommand = params[0].get_str();
211
212     return tableRPC.help(strCommand);
213 }
214
215
216 Value stop(const Array& params, bool fHelp)
217 {
218     if (fHelp || params.size() > 1)
219         throw runtime_error(
220             "stop <detach>\n"
221             "<detach> is true or false to detach the database or not for this stop only\n"
222             "Stop NovaCoin server (and possibly override the detachdb config value).");
223     // Shutdown will take long enough that the response should get back
224     if (params.size() > 0)
225         bitdb.SetDetach(params[0].get_bool());
226     StartShutdown();
227     return "NovaCoin server stopping";
228 }
229
230
231
232 //
233 // Call Table
234 //
235
236
237 static const CRPCCommand vRPCCommands[] =
238 { //  name                      function                 safemd  unlocked
239   //  ------------------------  -----------------------  ------  --------
240     { "help",                       &help,                        true,   true },
241     { "stop",                       &stop,                        true,   true },
242     { "getbestblockhash",           &getbestblockhash,            true,   false },
243     { "getblockcount",              &getblockcount,               true,   false },
244     { "getconnectioncount",         &getconnectioncount,          true,   false },
245     { "getaddrmaninfo",             &getaddrmaninfo,              true,   false },
246     { "getpeerinfo",                &getpeerinfo,                 true,   false },
247     { "addnode",                    &addnode,                     true,   true  },
248     { "getaddednodeinfo",           &getaddednodeinfo,            true,   true  },
249     { "getdifficulty",              &getdifficulty,               true,   false },
250     { "getinfo",                    &getinfo,                     true,   false },
251     { "getsubsidy",                 &getsubsidy,                  true,   false },
252     { "getmininginfo",              &getmininginfo,               true,   false },
253     { "scaninput",                  &scaninput,                   true,   true },
254     { "getnewaddress",              &getnewaddress,               true,   false },
255     { "getnettotals",               &getnettotals,                true,   true  },
256     { "ntptime",                    &ntptime,                     true,   true  },
257     { "getaccountaddress",          &getaccountaddress,           true,   false },
258     { "setaccount",                 &setaccount,                  true,   false },
259     { "getaccount",                 &getaccount,                  false,  false },
260     { "getaddressesbyaccount",      &getaddressesbyaccount,       true,   false },
261     { "sendtoaddress",              &sendtoaddress,               false,  false },
262     { "mergecoins",                 &mergecoins,                  false,  false },
263     { "getreceivedbyaddress",       &getreceivedbyaddress,        false,  false },
264     { "getreceivedbyaccount",       &getreceivedbyaccount,        false,  false },
265     { "listreceivedbyaddress",      &listreceivedbyaddress,       false,  false },
266     { "listreceivedbyaccount",      &listreceivedbyaccount,       false,  false },
267     { "backupwallet",               &backupwallet,                true,   false },
268     { "keypoolrefill",              &keypoolrefill,               true,   false },
269     { "keypoolreset",               &keypoolreset,                true,   false },
270     { "walletpassphrase",           &walletpassphrase,            true,   false },
271     { "walletpassphrasechange",     &walletpassphrasechange,      false,  false },
272     { "walletlock",                 &walletlock,                  true,   false },
273     { "encryptwallet",              &encryptwallet,               false,  false },
274     { "validateaddress",            &validateaddress,             true,   false },
275     { "getbalance",                 &getbalance,                  false,  false },
276     { "move",                       &movecmd,                     false,  false },
277     { "sendfrom",                   &sendfrom,                    false,  false },
278     { "sendmany",                   &sendmany,                    false,  false },
279     { "addmultisigaddress",         &addmultisigaddress,          false,  false },
280     { "addredeemscript",            &addredeemscript,             false,  false },
281     { "getrawmempool",              &getrawmempool,               true,   false },
282     { "getblock",                   &getblock,                    false,  false },
283     { "getblockbynumber",           &getblockbynumber,            false,  false },
284     { "dumpblock",                  &dumpblock,                   false,  false },
285     { "dumpblockbynumber",          &dumpblockbynumber,           false,  false },
286     { "getblockhash",               &getblockhash,                false,  false },
287     { "gettransaction",             &gettransaction,              false,  false },
288     { "listtransactions",           &listtransactions,            false,  false },
289     { "listaddressgroupings",       &listaddressgroupings,        false,  false },
290     { "signmessage",                &signmessage,                 false,  false },
291     { "verifymessage",              &verifymessage,               false,  false },
292     { "getwork",                    &getwork,                     true,   false },
293     { "getworkex",                  &getworkex,                   true,   false },
294     { "listaccounts",               &listaccounts,                false,  false },
295     { "settxfee",                   &settxfee,                    false,  false },
296     { "getblocktemplate",           &getblocktemplate,            true,   false },
297     { "submitblock",                &submitblock,                 false,  false },
298     { "listsinceblock",             &listsinceblock,              false,  false },
299     { "dumpprivkey",                &dumpprivkey,                 false,  false },
300     { "dumpwallet",                 &dumpwallet,                  true,   false },
301     { "importwallet",               &importwallet,                false,  false },
302     { "importprivkey",              &importprivkey,               false,  false },
303     { "importaddress",              &importaddress,               false,  true  },
304     { "removeaddress",              &removeaddress,               false,  true  },
305     { "listunspent",                &listunspent,                 false,  false },
306     { "getrawtransaction",          &getrawtransaction,           false,  false },
307     { "createrawtransaction",       &createrawtransaction,        false,  false },
308     { "decoderawtransaction",       &decoderawtransaction,        false,  false },
309     { "createmultisig",             &createmultisig,              false,  false },
310     { "decodescript",               &decodescript,                false,  false },
311     { "signrawtransaction",         &signrawtransaction,          false,  false },
312     { "sendrawtransaction",         &sendrawtransaction,          false,  false },
313     { "getcheckpoint",              &getcheckpoint,               true,   false },
314     { "reservebalance",             &reservebalance,              false,  true},
315     { "checkwallet",                &checkwallet,                 false,  true},
316     { "repairwallet",               &repairwallet,                false,  true},
317     { "resendwallettransactions",   &resendwallettransactions,    false,  true},
318     { "makekeypair",                &makekeypair,                 false,  true},
319     { "newmalleablekey",            &newmalleablekey,             false,  false},
320     { "adjustmalleablekey",         &adjustmalleablekey,          false,  false},
321     { "adjustmalleablepubkey",      &adjustmalleablepubkey,       false,  false},
322     { "listmalleableviews",         &listmalleableviews,          false,  false},
323     { "dumpmalleablekey",           &dumpmalleablekey,            false,  false},
324     { "importmalleablekey",         &importmalleablekey,          true,   false },
325     { "sendalert",                  &sendalert,                   false,  false},
326 };
327
328 CRPCTable::CRPCTable()
329 {
330     unsigned int vcidx;
331     for (vcidx = 0; vcidx < (sizeof(vRPCCommands) / sizeof(vRPCCommands[0])); vcidx++)
332     {
333         const CRPCCommand *pcmd;
334
335         pcmd = &vRPCCommands[vcidx];
336         mapCommands[pcmd->name] = pcmd;
337     }
338 }
339
340 const CRPCCommand *CRPCTable::operator[](string name) const
341 {
342     map<string, const CRPCCommand*>::const_iterator it = mapCommands.find(name);
343     if (it == mapCommands.end())
344         return NULL;
345     return (*it).second;
346 }
347
348 bool HTTPAuthorized(ix::WebSocketHttpHeaders& mapHeaders)
349 {
350     string strAuth = mapHeaders["authorization"];
351     if (strAuth.substr(0,6) != "Basic ")
352         return false;
353     string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64);
354     string strUserPass = DecodeBase64(strUserPass64);
355     return TimingResistantEqual(strUserPass, strRPCUserColonPass);
356 }
357
358 //
359 // JSON-RPC protocol.  Bitcoin speaks version 1.0 for maximum compatibility,
360 // but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were
361 // unspecified (HTTP errors and contents of 'error').
362 //
363 // 1.0 spec: http://json-rpc.org/wiki/specification
364 // 1.2 spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http
365 // http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx
366 //
367
368 string JSONRPCRequest(const string& strMethod, const Array& params, const Value& id)
369 {
370     Object request;
371     request.push_back(Pair("method", strMethod));
372     request.push_back(Pair("params", params));
373     request.push_back(Pair("id", id));
374     return write_string(Value(request), false) + "\n";
375 }
376
377 Object JSONRPCReplyObj(const Value& result, const Value& error, const Value& id)
378 {
379     Object reply;
380     if (error.type() != null_type)
381         reply.push_back(Pair("result", Value::null));
382     else
383         reply.push_back(Pair("result", result));
384     reply.push_back(Pair("error", error));
385     reply.push_back(Pair("id", id));
386     return reply;
387 }
388
389 string JSONRPCReply(const Value& result, const Value& error, const Value& id)
390 {
391     Object reply = JSONRPCReplyObj(result, error, id);
392     return write_string(Value(reply), false) + "\n";
393 }
394
395 string ErrorReply(const Object& objError, const Value& id)
396 {
397     // Send error reply from json-rpc error object
398     int nStatus = HTTP_INTERNAL_SERVER_ERROR;
399     int code = find_value(objError, "code").get_int();
400     if (code == RPC_INVALID_REQUEST) nStatus = HTTP_BAD_REQUEST;
401     else if (code == RPC_METHOD_NOT_FOUND) nStatus = HTTP_NOT_FOUND;
402     return JSONRPCReply(Value::null, objError, id);
403 }
404
405 class JSONRequest
406 {
407 public:
408     Value id;
409     string strMethod;
410     Array params;
411
412     JSONRequest() { id = Value::null; }
413     void parse(const Value& valRequest);
414 };
415
416 void JSONRequest::parse(const Value& valRequest)
417 {
418     // Parse request
419     if (valRequest.type() != obj_type)
420         throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object");
421     const Object& request = valRequest.get_obj();
422
423     // Parse id now so errors from here on will have the id
424     id = find_value(request, "id");
425
426     // Parse method
427     Value valMethod = find_value(request, "method");
428     if (valMethod.type() == null_type)
429         throw JSONRPCError(RPC_INVALID_REQUEST, "Missing method");
430     if (valMethod.type() != str_type)
431         throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string");
432     strMethod = valMethod.get_str();
433     if (strMethod != "getwork" && strMethod != "getblocktemplate")
434         printf("RPCServer method=%s\n", strMethod.c_str());
435
436     // Parse params
437     Value valParams = find_value(request, "params");
438     if (valParams.type() == array_type)
439         params = valParams.get_array();
440     else if (valParams.type() == null_type)
441         params = Array();
442     else
443         throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array");
444 }
445
446 static Object JSONRPCExecOne(const Value& req)
447 {
448     Object rpc_result;
449
450     JSONRequest jreq;
451     try {
452         jreq.parse(req);
453
454         Value result = tableRPC.execute(jreq.strMethod, jreq.params);
455         rpc_result = JSONRPCReplyObj(result, Value::null, jreq.id);
456     }
457     catch (Object& objError)
458     {
459         rpc_result = JSONRPCReplyObj(Value::null, objError, jreq.id);
460     }
461     catch (std::exception& e)
462     {
463         rpc_result = JSONRPCReplyObj(Value::null,
464                                      JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
465     }
466
467     return rpc_result;
468 }
469
470 static string JSONRPCExecBatch(const Array& vReq)
471 {
472     Array ret;
473     for (unsigned int reqIdx = 0; reqIdx < vReq.size(); reqIdx++)
474         ret.push_back(JSONRPCExecOne(vReq[reqIdx]));
475
476     return write_string(Value(ret), false) + "\n";
477 }
478
479 static CCriticalSection cs_THREAD_RPCHANDLER;
480
481 void StartRPCServer()
482 {
483     // Init net subsystem
484     ix::initNetSystem();
485
486     strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
487     if (mapArgs["-rpcpassword"].empty())
488     {
489         unsigned char rand_pwd[32];
490         RAND_bytes(rand_pwd, 32);
491         string strWhatAmI = "To use novacoind";
492         if (mapArgs.count("-server"))
493             strWhatAmI = strprintf(_("To use the %s option"), "\"-server\"");
494         else if (mapArgs.count("-daemon"))
495             strWhatAmI = strprintf(_("To use the %s option"), "\"-daemon\"");
496         uiInterface.ThreadSafeMessageBox(strprintf(
497             _("%s, you must set a rpcpassword in the configuration file:\n %s\n"
498               "It is recommended you use the following random password:\n"
499               "rpcuser=novacoinrpc\n"
500               "rpcpassword=%s\n"
501               "(you do not need to remember this password)\n"
502               "If the file does not exist, create it with owner-readable-only file permissions.\n"),
503                 strWhatAmI.c_str(),
504                 GetConfigFile().string().c_str(),
505                 EncodeBase58(&rand_pwd[0],&rand_pwd[0]+32).c_str()),
506             _("Error"), CClientUIInterface::OK | CClientUIInterface::MODAL);
507         StartShutdown();
508         return;
509     }
510
511     string host = GetArg("-rpchost", "127.0.0.1");
512     int port = GetArg("-rpcport", GetDefaultRPCPort());
513
514     g_server = std::unique_ptr<ix::HttpServer>(new ix::HttpServer(port, host));
515
516     LOCK(cs_THREAD_RPCHANDLER);
517
518     g_server->setOnConnectionCallback([](ix::HttpRequestPtr request, std::shared_ptr<ix::ConnectionState> connectionState) -> ix::HttpResponsePtr {
519
520         ix::WebSocketHttpHeaders headers;
521         headers["Server"] = string("novacoin-json-rpc/") + FormatFullVersion();
522         headers["WWW-Authenticate"] = "Basic realm=\"jsonrpc\"";
523
524         if (!HTTPAuthorized(request->headers))
525         {
526             printf("ThreadRPCServer incorrect password attempt from %s\n", connectionState->getRemoteIp().c_str());
527             connectionState->setTerminated();
528             return std::make_shared<ix::HttpResponse>(401, "Unauthorized", ix::HttpErrorCode::Ok, headers, "Not authorized");
529         }
530
531         if (request->method != "POST") {
532             connectionState->setTerminated();
533             return std::make_shared<ix::HttpResponse>(400, "Bad request", ix::HttpErrorCode::Ok, headers, "Bad request");
534         }
535
536         JSONRequest jreq;
537
538         try
539         {
540             // Parse request
541             Value valRequest;
542             if (!read_string(request->body, valRequest))
543                 throw JSONRPCError(RPC_PARSE_ERROR, "Parse error"); 
544
545             string strReply;
546
547             // singleton request
548             if (valRequest.type() == obj_type) {
549                 jreq.parse(valRequest);
550
551                 // Execute request
552                 Value result = tableRPC.execute(jreq.strMethod, jreq.params);
553
554                 // Send reply
555                 strReply = JSONRPCReply(result, Value::null, jreq.id);
556
557             // array of requests
558             } else if (valRequest.type() == array_type)
559                 // Execute batch of requests
560                 strReply = JSONRPCExecBatch(valRequest.get_array());
561             else
562                 throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error");
563
564             // Send reply to client
565             return std::make_shared<ix::HttpResponse>(200, "OK", ix::HttpErrorCode::Ok, headers, strReply);
566         
567         }
568         catch(Object& objError)
569         {
570             return std::make_shared<ix::HttpResponse>(500, "Internal Server Error", ix::HttpErrorCode::Ok, headers, ErrorReply(objError, jreq.id));
571         }
572         catch(std::exception& e)
573         {
574             return std::make_shared<ix::HttpResponse>(500, "Internal Server Error", ix::HttpErrorCode::Ok, headers, ErrorReply(JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id));
575         }
576     });
577
578     std::pair<bool, std::string> result = g_server->listen();
579     if (!result.first) {
580         auto strerr = strprintf(_("An error occurred while setting up the RPC port %u for listening on host %s: %s"), port, host.c_str(), result.second.c_str());
581         uiInterface.ThreadSafeMessageBox(strerr, _("Error"), CClientUIInterface::OK | CClientUIInterface::MODAL);
582         return StartShutdown();
583     }
584
585     // Run listening thread
586     g_server->start();
587
588     // We're listening now
589     vnThreadsRunning[THREAD_RPCLISTENER]++;
590 }
591
592 void StopRPCServer()
593 {
594     LOCK(cs_THREAD_RPCHANDLER);
595     if (g_server) g_server->stop();
596     vnThreadsRunning[THREAD_RPCLISTENER]--;
597 }
598
599 json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_spirit::Array &params) const
600 {
601     // Find method
602     const CRPCCommand *pcmd = tableRPC[strMethod];
603     if (!pcmd)
604         throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found");
605
606     // Observe safe mode
607     string strWarning = GetWarnings("rpc");
608     if (!strWarning.empty() && !GetBoolArg("-disablesafemode") &&
609         !pcmd->okSafeMode)
610         throw JSONRPCError(RPC_FORBIDDEN_BY_SAFE_MODE, string("Safe mode: ") + strWarning);
611
612     try
613     {
614         // Execute
615         Value result;
616         {
617             if (pcmd->unlocked)
618                 result = pcmd->actor(params, false);
619             else {
620                 LOCK2(cs_main, pwalletMain->cs_wallet);
621                 result = pcmd->actor(params, false);
622             }
623         }
624         return result;
625     }
626     catch (std::exception& e)
627     {
628         throw JSONRPCError(RPC_MISC_ERROR, e.what());
629     }
630 }
631
632 std::vector<std::string> CRPCTable::listCommands() const
633 {
634     std::vector<std::string> commandList;
635     typedef std::map<std::string, const CRPCCommand*> commandMap;
636
637     std::transform( mapCommands.begin(), mapCommands.end(),
638                    std::back_inserter(commandList),
639                    boost::bind(&commandMap::value_type::first,_1) );
640     return commandList;
641 }
642
643 Object CallRPC(const string& strMethod, const Array& params)
644 {
645     if (mapArgs["-rpcuser"].empty() && mapArgs["-rpcpassword"].empty())
646         throw runtime_error(strprintf(
647             _("You must set rpcpassword=<password> in the configuration file:\n%s\n"
648               "If the file does not exist, create it with owner-readable-only file permissions."),
649                 GetConfigFile().string().c_str()));
650
651     // Init net subsystem
652     ix::initNetSystem();
653
654     // Create HTTP client
655     ix::HttpClient httpClient;
656     ix::HttpRequestArgsPtr args = httpClient.createRequest();
657
658     // HTTP basic authentication
659     ix::WebSocketHttpHeaders mapRequestHeaders;
660     string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]);
661     mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64;
662     args->extraHeaders = mapRequestHeaders;
663
664     // Timeouts
665     args->connectTimeout = GetArgInt("-rpc_connecttimeout", 30000);
666     args->transferTimeout = GetArgInt("-rpc_transfertimeout", 30000);
667
668     bool fUseSSL = GetBoolArg("-rpcssl");
669     string url = string(fUseSSL ? "https://" : "http://") + GetArg("-rpcconnect", "127.0.0.1") + ":" + GetArg("-rpcport", itostr(GetDefaultRPCPort()));
670
671     // Send request
672     string strRequest = JSONRPCRequest(strMethod, params, GetRandInt(INT32_MAX));
673     auto out = httpClient.post(url, strRequest, args);
674
675     // Process reply
676     int nStatus = out->statusCode;
677     string strReply = out->body;
678     ix::WebSocketHttpHeaders mapHeaders = out->headers;
679
680     // Receive reply
681     if (nStatus == HTTP_UNAUTHORIZED)
682         throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
683     else if (nStatus >= 400 && nStatus != HTTP_BAD_REQUEST && nStatus != HTTP_NOT_FOUND && nStatus != HTTP_INTERNAL_SERVER_ERROR)
684         throw runtime_error(strprintf("server returned HTTP error %d", nStatus));
685     else if (strReply.empty())
686         throw runtime_error("no response from server");
687
688     // Parse reply
689     Value valReply;
690     if (!read_string(strReply, valReply))
691         throw runtime_error("couldn't parse reply from server");
692     const Object& reply = valReply.get_obj();
693     if (reply.empty())
694         throw runtime_error("expected reply to have result, error and id properties");
695
696     return reply;
697 }
698
699
700
701
702 template<typename T>
703 void ConvertTo(Value& value, bool fAllowNull=false)
704 {
705     if (fAllowNull && value.type() == null_type)
706         return;
707     if (value.type() == str_type)
708     {
709         // reinterpret string as unquoted json value
710         Value value2;
711         string strJSON = value.get_str();
712         if (!read_string(strJSON, value2))
713             throw runtime_error(string("Error parsing JSON:")+strJSON);
714         ConvertTo<T>(value2, fAllowNull);
715         value = value2;
716     }
717     else
718     {
719         value = value.get_value<T>();
720     }
721 }
722
723 // Convert strings to command-specific RPC representation
724 Array RPCConvertValues(const std::string &strMethod, const std::vector<std::string> &strParams)
725 {
726     Array params;
727     BOOST_FOREACH(const std::string &param, strParams)
728         params.push_back(param);
729
730     size_t n = params.size();
731
732     //
733     // Special case non-string parameter types
734     //
735     if (strMethod == "stop"                   && n > 0) ConvertTo<bool>(params[0]);
736     if (strMethod == "getaddednodeinfo"       && n > 0) ConvertTo<bool>(params[0]);
737     if (strMethod == "sendtoaddress"          && n > 1) ConvertTo<double>(params[1]);
738     if (strMethod == "mergecoins"            && n > 0) ConvertTo<double>(params[0]);
739     if (strMethod == "mergecoins"            && n > 1) ConvertTo<double>(params[1]);
740     if (strMethod == "mergecoins"            && n > 2) ConvertTo<double>(params[2]);
741     if (strMethod == "settxfee"               && n > 0) ConvertTo<double>(params[0]);
742     if (strMethod == "getreceivedbyaddress"   && n > 1) ConvertTo<int64_t>(params[1]);
743     if (strMethod == "getreceivedbyaccount"   && n > 1) ConvertTo<int64_t>(params[1]);
744     if (strMethod == "listreceivedbyaddress"  && n > 0) ConvertTo<int64_t>(params[0]);
745     if (strMethod == "listreceivedbyaddress"  && n > 1) ConvertTo<bool>(params[1]);
746     if (strMethod == "listreceivedbyaccount"  && n > 0) ConvertTo<int64_t>(params[0]);
747     if (strMethod == "listreceivedbyaccount"  && n > 1) ConvertTo<bool>(params[1]);
748     if (strMethod == "getbalance"             && n > 1) ConvertTo<int64_t>(params[1]);
749     if (strMethod == "getblock"               && n > 1) ConvertTo<bool>(params[1]);
750     if (strMethod == "getblockbynumber"       && n > 0) ConvertTo<int64_t>(params[0]);
751     if (strMethod == "dumpblockbynumber"      && n > 0) ConvertTo<int64_t>(params[0]);
752     if (strMethod == "getblockbynumber"       && n > 1) ConvertTo<bool>(params[1]);
753     if (strMethod == "getblockhash"           && n > 0) ConvertTo<int64_t>(params[0]);
754     if (strMethod == "move"                   && n > 2) ConvertTo<double>(params[2]);
755     if (strMethod == "move"                   && n > 3) ConvertTo<int64_t>(params[3]);
756     if (strMethod == "sendfrom"               && n > 2) ConvertTo<double>(params[2]);
757     if (strMethod == "sendfrom"               && n > 3) ConvertTo<int64_t>(params[3]);
758     if (strMethod == "listtransactions"       && n > 1) ConvertTo<int64_t>(params[1]);
759     if (strMethod == "listtransactions"       && n > 2) ConvertTo<int64_t>(params[2]);
760     if (strMethod == "listaccounts"           && n > 0) ConvertTo<int64_t>(params[0]);
761     if (strMethod == "walletpassphrase"       && n > 1) ConvertTo<int64_t>(params[1]);
762     if (strMethod == "walletpassphrase"       && n > 2) ConvertTo<bool>(params[2]);
763     if (strMethod == "getblocktemplate"       && n > 0) ConvertTo<Object>(params[0]);
764     if (strMethod == "listsinceblock"         && n > 1) ConvertTo<int64_t>(params[1]);
765
766     if (strMethod == "scaninput"              && n > 0) ConvertTo<Object>(params[0]);
767
768     if (strMethod == "sendalert"              && n > 2) ConvertTo<int64_t>(params[2]);
769     if (strMethod == "sendalert"              && n > 3) ConvertTo<int64_t>(params[3]);
770     if (strMethod == "sendalert"              && n > 4) ConvertTo<int64_t>(params[4]);
771     if (strMethod == "sendalert"              && n > 5) ConvertTo<int64_t>(params[5]);
772     if (strMethod == "sendalert"              && n > 6) ConvertTo<int64_t>(params[6]);
773
774     if (strMethod == "sendmany"               && n > 1) ConvertTo<Object>(params[1]);
775     if (strMethod == "sendmany"               && n > 2) ConvertTo<int64_t>(params[2]);
776     if (strMethod == "reservebalance"         && n > 0) ConvertTo<bool>(params[0]);
777     if (strMethod == "reservebalance"         && n > 1) ConvertTo<double>(params[1]);
778     if (strMethod == "addmultisigaddress"     && n > 0) ConvertTo<int64_t>(params[0]);
779     if (strMethod == "addmultisigaddress"     && n > 1) ConvertTo<Array>(params[1]);
780     if (strMethod == "listunspent"            && n > 0) ConvertTo<int64_t>(params[0]);
781     if (strMethod == "listunspent"            && n > 1) ConvertTo<int64_t>(params[1]);
782     if (strMethod == "listunspent"            && n > 2) ConvertTo<Array>(params[2]);
783     if (strMethod == "getrawtransaction"      && n > 1) ConvertTo<int64_t>(params[1]);
784     if (strMethod == "createrawtransaction"   && n > 0) ConvertTo<Array>(params[0]);
785     if (strMethod == "createrawtransaction"   && n > 1) ConvertTo<Object>(params[1]);
786     if (strMethod == "createmultisig"         && n > 0) ConvertTo<int64_t>(params[0]);
787     if (strMethod == "createmultisig"         && n > 1) ConvertTo<Array>(params[1]);
788     if (strMethod == "signrawtransaction"     && n > 1) ConvertTo<Array>(params[1], true);
789     if (strMethod == "signrawtransaction"     && n > 2) ConvertTo<Array>(params[2], true);
790     if (strMethod == "keypoolrefill"          && n > 0) ConvertTo<int64_t>(params[0]);
791     if (strMethod == "keypoolreset"           && n > 0) ConvertTo<int64_t>(params[0]);
792     if (strMethod == "importaddress"          && n > 2) ConvertTo<bool>(params[2]);
793     if (strMethod == "importprivkey"          && n > 2) ConvertTo<bool>(params[2]);
794
795     return params;
796 }
797
798 int CommandLineRPC(int argc, char *argv[])
799 {
800     string strPrint;
801     int nRet = 0;
802     try
803     {
804         // Skip switches
805         while (argc > 1 && IsSwitchChar(argv[1][0]))
806         {
807             argc--;
808             argv++;
809         }
810
811         // Method
812         if (argc < 2)
813             throw runtime_error("too few parameters");
814         string strMethod = argv[1];
815
816         // Parameters default to strings
817         std::vector<std::string> strParams(&argv[2], &argv[argc]);
818         Array params = RPCConvertValues(strMethod, strParams);
819
820         // Execute
821         Object reply = CallRPC(strMethod, params);
822
823         // Parse reply
824         const Value& result = find_value(reply, "result");
825         const Value& error  = find_value(reply, "error");
826
827         if (error.type() != null_type)
828         {
829             // Error
830             strPrint = "error: " + write_string(error, false);
831             int code = find_value(error.get_obj(), "code").get_int();
832             nRet = abs(code);
833         }
834         else
835         {
836             // Result
837             if (result.type() == null_type)
838                 strPrint.clear();
839             else if (result.type() == str_type)
840                 strPrint = result.get_str();
841             else
842                 strPrint = write_string(result, true);
843         }
844     }
845     catch (std::exception& e)
846     {
847         strPrint = string("error: ") + e.what();
848         nRet = 87;
849     }
850     catch (...)
851     {
852         PrintException(NULL, "CommandLineRPC()");
853     }
854
855     if (!strPrint.empty())
856     {
857         fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
858     }
859     return nRet;
860 }
861
862
863 const CRPCTable tableRPC;