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