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