We already have WSA init calls in init.cpp
[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     strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
484     if (mapArgs["-rpcpassword"].empty())
485     {
486         unsigned char rand_pwd[32];
487         RAND_bytes(rand_pwd, 32);
488         string strWhatAmI = "To use novacoind";
489         if (mapArgs.count("-server"))
490             strWhatAmI = strprintf(_("To use the %s option"), "\"-server\"");
491         else if (mapArgs.count("-daemon"))
492             strWhatAmI = strprintf(_("To use the %s option"), "\"-daemon\"");
493         uiInterface.ThreadSafeMessageBox(strprintf(
494             _("%s, you must set a rpcpassword in the configuration file:\n %s\n"
495               "It is recommended you use the following random password:\n"
496               "rpcuser=novacoinrpc\n"
497               "rpcpassword=%s\n"
498               "(you do not need to remember this password)\n"
499               "If the file does not exist, create it with owner-readable-only file permissions.\n"),
500                 strWhatAmI.c_str(),
501                 GetConfigFile().string().c_str(),
502                 EncodeBase58(&rand_pwd[0],&rand_pwd[0]+32).c_str()),
503             _("Error"), CClientUIInterface::OK | CClientUIInterface::MODAL);
504         StartShutdown();
505         return;
506     }
507
508     string host = GetArg("-rpchost", "127.0.0.1");
509     int port = GetArg("-rpcport", GetDefaultRPCPort());
510
511     g_server = std::unique_ptr<ix::HttpServer>(new ix::HttpServer(port, host));
512
513     LOCK(cs_THREAD_RPCHANDLER);
514
515     g_server->setOnConnectionCallback([](ix::HttpRequestPtr request, std::shared_ptr<ix::ConnectionState> connectionState) -> ix::HttpResponsePtr {
516
517         ix::WebSocketHttpHeaders headers;
518         headers["Server"] = string("novacoin-json-rpc/") + FormatFullVersion();
519         headers["WWW-Authenticate"] = "Basic realm=\"jsonrpc\"";
520
521         if (!HTTPAuthorized(request->headers))
522         {
523             printf("ThreadRPCServer incorrect password attempt from %s\n", connectionState->getRemoteIp().c_str());
524             connectionState->setTerminated();
525             return std::make_shared<ix::HttpResponse>(401, "Unauthorized", ix::HttpErrorCode::Ok, headers, "Not authorized");
526         }
527
528         if (request->method != "POST") {
529             connectionState->setTerminated();
530             return std::make_shared<ix::HttpResponse>(400, "Bad request", ix::HttpErrorCode::Ok, headers, "Bad request");
531         }
532
533         JSONRequest jreq;
534
535         try
536         {
537             // Parse request
538             Value valRequest;
539             if (!read_string(request->body, valRequest))
540                 throw JSONRPCError(RPC_PARSE_ERROR, "Parse error"); 
541
542             string strReply;
543
544             // singleton request
545             if (valRequest.type() == obj_type) {
546                 jreq.parse(valRequest);
547
548                 // Execute request
549                 Value result = tableRPC.execute(jreq.strMethod, jreq.params);
550
551                 // Send reply
552                 strReply = JSONRPCReply(result, Value::null, jreq.id);
553
554             // array of requests
555             } else if (valRequest.type() == array_type)
556                 // Execute batch of requests
557                 strReply = JSONRPCExecBatch(valRequest.get_array());
558             else
559                 throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error");
560
561             // Send reply to client
562             return std::make_shared<ix::HttpResponse>(200, "OK", ix::HttpErrorCode::Ok, headers, strReply);
563         
564         }
565         catch(Object& objError)
566         {
567             return std::make_shared<ix::HttpResponse>(500, "Internal Server Error", ix::HttpErrorCode::Ok, headers, ErrorReply(objError, jreq.id));
568         }
569         catch(std::exception& e)
570         {
571             return std::make_shared<ix::HttpResponse>(500, "Internal Server Error", ix::HttpErrorCode::Ok, headers, ErrorReply(JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id));
572         }
573     });
574
575     std::pair<bool, std::string> result = g_server->listen();
576     if (!result.first) {
577         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());
578         uiInterface.ThreadSafeMessageBox(strerr, _("Error"), CClientUIInterface::OK | CClientUIInterface::MODAL);
579         return StartShutdown();
580     }
581
582     // Run listening thread
583     g_server->start();
584
585     // We're listening now
586     vnThreadsRunning[THREAD_RPCLISTENER]++;
587 }
588
589 void StopRPCServer()
590 {
591     LOCK(cs_THREAD_RPCHANDLER);
592     if (g_server) g_server->stop();
593     vnThreadsRunning[THREAD_RPCLISTENER]--;
594 }
595
596 json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_spirit::Array &params) const
597 {
598     // Find method
599     const CRPCCommand *pcmd = tableRPC[strMethod];
600     if (!pcmd)
601         throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found");
602
603     // Observe safe mode
604     string strWarning = GetWarnings("rpc");
605     if (!strWarning.empty() && !GetBoolArg("-disablesafemode") &&
606         !pcmd->okSafeMode)
607         throw JSONRPCError(RPC_FORBIDDEN_BY_SAFE_MODE, string("Safe mode: ") + strWarning);
608
609     try
610     {
611         // Execute
612         Value result;
613         {
614             if (pcmd->unlocked)
615                 result = pcmd->actor(params, false);
616             else {
617                 LOCK2(cs_main, pwalletMain->cs_wallet);
618                 result = pcmd->actor(params, false);
619             }
620         }
621         return result;
622     }
623     catch (std::exception& e)
624     {
625         throw JSONRPCError(RPC_MISC_ERROR, e.what());
626     }
627 }
628
629 std::vector<std::string> CRPCTable::listCommands() const
630 {
631     std::vector<std::string> commandList;
632     typedef std::map<std::string, const CRPCCommand*> commandMap;
633
634     std::transform( mapCommands.begin(), mapCommands.end(),
635                    std::back_inserter(commandList),
636                    boost::bind(&commandMap::value_type::first,_1) );
637     return commandList;
638 }
639
640 Object CallRPC(const string& strMethod, const Array& params)
641 {
642     if (mapArgs["-rpcuser"].empty() && mapArgs["-rpcpassword"].empty())
643         throw runtime_error(strprintf(
644             _("You must set rpcpassword=<password> in the configuration file:\n%s\n"
645               "If the file does not exist, create it with owner-readable-only file permissions."),
646                 GetConfigFile().string().c_str()));
647
648     // Init net subsystem
649     ix::initNetSystem();
650
651     // Create HTTP client
652     ix::HttpClient httpClient;
653     ix::HttpRequestArgsPtr args = httpClient.createRequest();
654
655     // HTTP basic authentication
656     ix::WebSocketHttpHeaders mapRequestHeaders;
657     string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]);
658     mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64;
659     args->extraHeaders = mapRequestHeaders;
660
661     // Timeouts
662     args->connectTimeout = GetArgInt("-rpc_connecttimeout", 30000);
663     args->transferTimeout = GetArgInt("-rpc_transfertimeout", 30000);
664
665     bool fUseSSL = GetBoolArg("-rpcssl");
666     string url = string(fUseSSL ? "https://" : "http://") + GetArg("-rpcconnect", "127.0.0.1") + ":" + GetArg("-rpcport", itostr(GetDefaultRPCPort()));
667
668     // Send request
669     string strRequest = JSONRPCRequest(strMethod, params, GetRandInt(INT32_MAX));
670     auto out = httpClient.post(url, strRequest, args);
671
672     // Process reply
673     int nStatus = out->statusCode;
674     string strReply = out->body;
675     ix::WebSocketHttpHeaders mapHeaders = out->headers;
676
677     // Receive reply
678     if (nStatus == HTTP_UNAUTHORIZED)
679         throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
680     else if (nStatus >= 400 && nStatus != HTTP_BAD_REQUEST && nStatus != HTTP_NOT_FOUND && nStatus != HTTP_INTERNAL_SERVER_ERROR)
681         throw runtime_error(strprintf("server returned HTTP error %d", nStatus));
682     else if (strReply.empty())
683         throw runtime_error("no response from server");
684
685     // Parse reply
686     Value valReply;
687     if (!read_string(strReply, valReply))
688         throw runtime_error("couldn't parse reply from server");
689     const Object& reply = valReply.get_obj();
690     if (reply.empty())
691         throw runtime_error("expected reply to have result, error and id properties");
692
693     return reply;
694 }
695
696
697
698
699 template<typename T>
700 void ConvertTo(Value& value, bool fAllowNull=false)
701 {
702     if (fAllowNull && value.type() == null_type)
703         return;
704     if (value.type() == str_type)
705     {
706         // reinterpret string as unquoted json value
707         Value value2;
708         string strJSON = value.get_str();
709         if (!read_string(strJSON, value2))
710             throw runtime_error(string("Error parsing JSON:")+strJSON);
711         ConvertTo<T>(value2, fAllowNull);
712         value = value2;
713     }
714     else
715     {
716         value = value.get_value<T>();
717     }
718 }
719
720 // Convert strings to command-specific RPC representation
721 Array RPCConvertValues(const std::string &strMethod, const std::vector<std::string> &strParams)
722 {
723     Array params;
724     BOOST_FOREACH(const std::string &param, strParams)
725         params.push_back(param);
726
727     size_t n = params.size();
728
729     //
730     // Special case non-string parameter types
731     //
732     if (strMethod == "stop"                   && n > 0) ConvertTo<bool>(params[0]);
733     if (strMethod == "getaddednodeinfo"       && n > 0) ConvertTo<bool>(params[0]);
734     if (strMethod == "sendtoaddress"          && n > 1) ConvertTo<double>(params[1]);
735     if (strMethod == "mergecoins"            && n > 0) ConvertTo<double>(params[0]);
736     if (strMethod == "mergecoins"            && n > 1) ConvertTo<double>(params[1]);
737     if (strMethod == "mergecoins"            && n > 2) ConvertTo<double>(params[2]);
738     if (strMethod == "settxfee"               && n > 0) ConvertTo<double>(params[0]);
739     if (strMethod == "getreceivedbyaddress"   && n > 1) ConvertTo<int64_t>(params[1]);
740     if (strMethod == "getreceivedbyaccount"   && n > 1) ConvertTo<int64_t>(params[1]);
741     if (strMethod == "listreceivedbyaddress"  && n > 0) ConvertTo<int64_t>(params[0]);
742     if (strMethod == "listreceivedbyaddress"  && n > 1) ConvertTo<bool>(params[1]);
743     if (strMethod == "listreceivedbyaccount"  && n > 0) ConvertTo<int64_t>(params[0]);
744     if (strMethod == "listreceivedbyaccount"  && n > 1) ConvertTo<bool>(params[1]);
745     if (strMethod == "getbalance"             && n > 1) ConvertTo<int64_t>(params[1]);
746     if (strMethod == "getblock"               && n > 1) ConvertTo<bool>(params[1]);
747     if (strMethod == "getblockbynumber"       && n > 0) ConvertTo<int64_t>(params[0]);
748     if (strMethod == "dumpblockbynumber"      && n > 0) ConvertTo<int64_t>(params[0]);
749     if (strMethod == "getblockbynumber"       && n > 1) ConvertTo<bool>(params[1]);
750     if (strMethod == "getblockhash"           && n > 0) ConvertTo<int64_t>(params[0]);
751     if (strMethod == "move"                   && n > 2) ConvertTo<double>(params[2]);
752     if (strMethod == "move"                   && n > 3) ConvertTo<int64_t>(params[3]);
753     if (strMethod == "sendfrom"               && n > 2) ConvertTo<double>(params[2]);
754     if (strMethod == "sendfrom"               && n > 3) ConvertTo<int64_t>(params[3]);
755     if (strMethod == "listtransactions"       && n > 1) ConvertTo<int64_t>(params[1]);
756     if (strMethod == "listtransactions"       && n > 2) ConvertTo<int64_t>(params[2]);
757     if (strMethod == "listaccounts"           && n > 0) ConvertTo<int64_t>(params[0]);
758     if (strMethod == "walletpassphrase"       && n > 1) ConvertTo<int64_t>(params[1]);
759     if (strMethod == "walletpassphrase"       && n > 2) ConvertTo<bool>(params[2]);
760     if (strMethod == "getblocktemplate"       && n > 0) ConvertTo<Object>(params[0]);
761     if (strMethod == "listsinceblock"         && n > 1) ConvertTo<int64_t>(params[1]);
762
763     if (strMethod == "scaninput"              && n > 0) ConvertTo<Object>(params[0]);
764
765     if (strMethod == "sendalert"              && n > 2) ConvertTo<int64_t>(params[2]);
766     if (strMethod == "sendalert"              && n > 3) ConvertTo<int64_t>(params[3]);
767     if (strMethod == "sendalert"              && n > 4) ConvertTo<int64_t>(params[4]);
768     if (strMethod == "sendalert"              && n > 5) ConvertTo<int64_t>(params[5]);
769     if (strMethod == "sendalert"              && n > 6) ConvertTo<int64_t>(params[6]);
770
771     if (strMethod == "sendmany"               && n > 1) ConvertTo<Object>(params[1]);
772     if (strMethod == "sendmany"               && n > 2) ConvertTo<int64_t>(params[2]);
773     if (strMethod == "reservebalance"         && n > 0) ConvertTo<bool>(params[0]);
774     if (strMethod == "reservebalance"         && n > 1) ConvertTo<double>(params[1]);
775     if (strMethod == "addmultisigaddress"     && n > 0) ConvertTo<int64_t>(params[0]);
776     if (strMethod == "addmultisigaddress"     && n > 1) ConvertTo<Array>(params[1]);
777     if (strMethod == "listunspent"            && n > 0) ConvertTo<int64_t>(params[0]);
778     if (strMethod == "listunspent"            && n > 1) ConvertTo<int64_t>(params[1]);
779     if (strMethod == "listunspent"            && n > 2) ConvertTo<Array>(params[2]);
780     if (strMethod == "getrawtransaction"      && n > 1) ConvertTo<int64_t>(params[1]);
781     if (strMethod == "createrawtransaction"   && n > 0) ConvertTo<Array>(params[0]);
782     if (strMethod == "createrawtransaction"   && n > 1) ConvertTo<Object>(params[1]);
783     if (strMethod == "createmultisig"         && n > 0) ConvertTo<int64_t>(params[0]);
784     if (strMethod == "createmultisig"         && n > 1) ConvertTo<Array>(params[1]);
785     if (strMethod == "signrawtransaction"     && n > 1) ConvertTo<Array>(params[1], true);
786     if (strMethod == "signrawtransaction"     && n > 2) ConvertTo<Array>(params[2], true);
787     if (strMethod == "keypoolrefill"          && n > 0) ConvertTo<int64_t>(params[0]);
788     if (strMethod == "keypoolreset"           && n > 0) ConvertTo<int64_t>(params[0]);
789     if (strMethod == "importaddress"          && n > 2) ConvertTo<bool>(params[2]);
790     if (strMethod == "importprivkey"          && n > 2) ConvertTo<bool>(params[2]);
791
792     return params;
793 }
794
795 int CommandLineRPC(int argc, char *argv[])
796 {
797     string strPrint;
798     int nRet = 0;
799     try
800     {
801         // Skip switches
802         while (argc > 1 && IsSwitchChar(argv[1][0]))
803         {
804             argc--;
805             argv++;
806         }
807
808         // Method
809         if (argc < 2)
810             throw runtime_error("too few parameters");
811         string strMethod = argv[1];
812
813         // Parameters default to strings
814         std::vector<std::string> strParams(&argv[2], &argv[argc]);
815         Array params = RPCConvertValues(strMethod, strParams);
816
817         // Execute
818         Object reply = CallRPC(strMethod, params);
819
820         // Parse reply
821         const Value& result = find_value(reply, "result");
822         const Value& error  = find_value(reply, "error");
823
824         if (error.type() != null_type)
825         {
826             // Error
827             strPrint = "error: " + write_string(error, false);
828             int code = find_value(error.get_obj(), "code").get_int();
829             nRet = abs(code);
830         }
831         else
832         {
833             // Result
834             if (result.type() == null_type)
835                 strPrint.clear();
836             else if (result.type() == str_type)
837                 strPrint = result.get_str();
838             else
839                 strPrint = write_string(result, true);
840         }
841     }
842     catch (std::exception& e)
843     {
844         strPrint = string("error: ") + e.what();
845         nRet = 87;
846     }
847     catch (...)
848     {
849         PrintException(NULL, "CommandLineRPC()");
850     }
851
852     if (!strPrint.empty())
853     {
854         fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
855     }
856     return nRet;
857 }
858
859
860 const CRPCTable tableRPC;