Reimplement RPC server and client
[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 "ui_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     { "dumppem",                    &dumppem,                     true,   false },
301     { "dumpwallet",                 &dumpwallet,                  true,   false },
302     { "importwallet",               &importwallet,                false,  false },
303     { "importprivkey",              &importprivkey,               false,  false },
304     { "importaddress",              &importaddress,               false,  true  },
305     { "removeaddress",              &removeaddress,               false,  true  },
306     { "listunspent",                &listunspent,                 false,  false },
307     { "getrawtransaction",          &getrawtransaction,           false,  false },
308     { "createrawtransaction",       &createrawtransaction,        false,  false },
309     { "decoderawtransaction",       &decoderawtransaction,        false,  false },
310     { "createmultisig",             &createmultisig,              false,  false },
311     { "decodescript",               &decodescript,                false,  false },
312     { "signrawtransaction",         &signrawtransaction,          false,  false },
313     { "sendrawtransaction",         &sendrawtransaction,          false,  false },
314     { "getcheckpoint",              &getcheckpoint,               true,   false },
315     { "reservebalance",             &reservebalance,              false,  true},
316     { "checkwallet",                &checkwallet,                 false,  true},
317     { "repairwallet",               &repairwallet,                false,  true},
318     { "resendwallettransactions",   &resendwallettransactions,    false,  true},
319     { "makekeypair",                &makekeypair,                 false,  true},
320     { "newmalleablekey",            &newmalleablekey,             false,  false},
321     { "adjustmalleablekey",         &adjustmalleablekey,          false,  false},
322     { "adjustmalleablepubkey",      &adjustmalleablepubkey,       false,  false},
323     { "listmalleableviews",         &listmalleableviews,          false,  false},
324     { "dumpmalleablekey",           &dumpmalleablekey,            false,  false},
325     { "importmalleablekey",         &importmalleablekey,          true,   false },
326     { "encryptdata",                &encryptdata,                 false,  false },
327     { "decryptdata",                &decryptdata,                 false,  false },
328     { "encryptmessage",             &encryptmessage,              false,  false },
329     { "decryptmessage",             &decryptmessage,              false,  false },
330     { "sendalert",                  &sendalert,                   false,  false},
331 };
332
333 CRPCTable::CRPCTable()
334 {
335     unsigned int vcidx;
336     for (vcidx = 0; vcidx < (sizeof(vRPCCommands) / sizeof(vRPCCommands[0])); vcidx++)
337     {
338         const CRPCCommand *pcmd;
339
340         pcmd = &vRPCCommands[vcidx];
341         mapCommands[pcmd->name] = pcmd;
342     }
343 }
344
345 const CRPCCommand *CRPCTable::operator[](string name) const
346 {
347     map<string, const CRPCCommand*>::const_iterator it = mapCommands.find(name);
348     if (it == mapCommands.end())
349         return NULL;
350     return (*it).second;
351 }
352
353 string rfc1123Time()
354 {
355     return DateTimeStrFormat("%a, %d %b %Y %H:%M:%S +0000", GetTime());
356 }
357
358 static string HTTPReply(int nStatus, const string& strMsg, bool keepalive)
359 {
360     if (nStatus == HTTP_UNAUTHORIZED)
361         return strprintf("HTTP/1.0 401 Authorization Required\r\n"
362             "Date: %s\r\n"
363             "Server: novacoin-json-rpc/%s\r\n"
364             "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n"
365             "Content-Type: text/html\r\n"
366             "Content-Length: 296\r\n"
367             "\r\n"
368             "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\r\n"
369             "\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">\r\n"
370             "<HTML>\r\n"
371             "<HEAD>\r\n"
372             "<TITLE>Error</TITLE>\r\n"
373             "<META HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=ISO-8859-1'>\r\n"
374             "</HEAD>\r\n"
375             "<BODY><H1>401 Unauthorized.</H1></BODY>\r\n"
376             "</HTML>\r\n", rfc1123Time().c_str(), FormatFullVersion().c_str());
377     const char *cStatus;
378          if (nStatus == HTTP_OK) cStatus = "OK";
379     else if (nStatus == HTTP_BAD_REQUEST) cStatus = "Bad Request";
380     else if (nStatus == HTTP_FORBIDDEN) cStatus = "Forbidden";
381     else if (nStatus == HTTP_NOT_FOUND) cStatus = "Not Found";
382     else if (nStatus == HTTP_INTERNAL_SERVER_ERROR) cStatus = "Internal Server Error";
383     else cStatus = "";
384     return strprintf(
385             "HTTP/1.1 %d %s\r\n"
386             "Date: %s\r\n"
387             "Connection: %s\r\n"
388             "Content-Length: %" PRIszu "\r\n"
389             "Content-Type: application/json\r\n"
390             "Server: novacoin-json-rpc/%s\r\n"
391             "\r\n"
392             "%s",
393         nStatus,
394         cStatus,
395         rfc1123Time().c_str(),
396         keepalive ? "keep-alive" : "close",
397         strMsg.size(),
398         FormatFullVersion().c_str(),
399         strMsg.c_str());
400 }
401
402 int ReadHTTPStatus(std::basic_istream<char>& stream, int &proto)
403 {
404     string str;
405     getline(stream, str);
406     vector<string> vWords;
407     istringstream iss(str);
408     copy(istream_iterator<string>(iss), istream_iterator<string>(), back_inserter(vWords));
409     if (vWords.size() < 2)
410         return HTTP_INTERNAL_SERVER_ERROR;
411     proto = 0;
412     const char *ver = strstr(str.c_str(), "HTTP/1.");
413     if (ver != NULL)
414         proto = atoi(ver+7);
415     return atoi(vWords[1].c_str());
416 }
417
418 int ReadHTTPHeader(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet)
419 {
420     int nLen = 0;
421     for ( ; ; )
422     {
423         string str;
424         std::getline(stream, str);
425         if (str.empty() || str == "\r")
426             break;
427         string::size_type nColon = str.find(":");
428         if (nColon != string::npos)
429         {
430             string strHeader = str.substr(0, nColon);
431             boost::trim(strHeader);
432             boost::to_lower(strHeader);
433             string strValue = str.substr(nColon+1);
434             boost::trim(strValue);
435             mapHeadersRet[strHeader] = strValue;
436             if (strHeader == "content-length")
437                 nLen = atoi(strValue.c_str());
438         }
439     }
440     return nLen;
441 }
442
443 int ReadHTTP(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet, string& strMessageRet)
444 {
445     mapHeadersRet.clear();
446     strMessageRet.clear();
447
448     // Read status
449     int nProto = 0;
450     int nStatus = ReadHTTPStatus(stream, nProto);
451
452     // Read header
453     int nLen = ReadHTTPHeader(stream, mapHeadersRet);
454     if (nLen < 0 || nLen > (int)MAX_SIZE)
455         return HTTP_INTERNAL_SERVER_ERROR;
456
457     // Read message
458     if (nLen > 0)
459     {
460         vector<char> vch(nLen);
461         stream.read(&vch[0], nLen);
462         strMessageRet = string(vch.begin(), vch.end());
463     }
464
465     string sConHdr = mapHeadersRet["connection"];
466
467     if ((sConHdr != "close") && (sConHdr != "keep-alive"))
468     {
469         if (nProto >= 1)
470             mapHeadersRet["connection"] = "keep-alive";
471         else
472             mapHeadersRet["connection"] = "close";
473     }
474
475     return nStatus;
476 }
477
478 bool HTTPAuthorized(map<string, string>& mapHeaders)
479 {
480     string strAuth = mapHeaders["authorization"];
481     if (strAuth.substr(0,6) != "Basic ")
482         return false;
483     string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64);
484     string strUserPass = DecodeBase64(strUserPass64);
485     return TimingResistantEqual(strUserPass, strRPCUserColonPass);
486 }
487
488 //
489 // JSON-RPC protocol.  Bitcoin speaks version 1.0 for maximum compatibility,
490 // but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were
491 // unspecified (HTTP errors and contents of 'error').
492 //
493 // 1.0 spec: http://json-rpc.org/wiki/specification
494 // 1.2 spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http
495 // http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx
496 //
497
498 string JSONRPCRequest(const string& strMethod, const Array& params, const Value& id)
499 {
500     Object request;
501     request.push_back(Pair("method", strMethod));
502     request.push_back(Pair("params", params));
503     request.push_back(Pair("id", id));
504     return write_string(Value(request), false) + "\n";
505 }
506
507 Object JSONRPCReplyObj(const Value& result, const Value& error, const Value& id)
508 {
509     Object reply;
510     if (error.type() != null_type)
511         reply.push_back(Pair("result", Value::null));
512     else
513         reply.push_back(Pair("result", result));
514     reply.push_back(Pair("error", error));
515     reply.push_back(Pair("id", id));
516     return reply;
517 }
518
519 string JSONRPCReply(const Value& result, const Value& error, const Value& id)
520 {
521     Object reply = JSONRPCReplyObj(result, error, id);
522     return write_string(Value(reply), false) + "\n";
523 }
524
525 string ErrorReply(const Object& objError, const Value& id)
526 {
527     // Send error reply from json-rpc error object
528     int nStatus = HTTP_INTERNAL_SERVER_ERROR;
529     int code = find_value(objError, "code").get_int();
530     if (code == RPC_INVALID_REQUEST) nStatus = HTTP_BAD_REQUEST;
531     else if (code == RPC_METHOD_NOT_FOUND) nStatus = HTTP_NOT_FOUND;
532     return JSONRPCReply(Value::null, objError, id);
533 }
534
535 class JSONRequest
536 {
537 public:
538     Value id;
539     string strMethod;
540     Array params;
541
542     JSONRequest() { id = Value::null; }
543     void parse(const Value& valRequest);
544 };
545
546 void JSONRequest::parse(const Value& valRequest)
547 {
548     // Parse request
549     if (valRequest.type() != obj_type)
550         throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object");
551     const Object& request = valRequest.get_obj();
552
553     // Parse id now so errors from here on will have the id
554     id = find_value(request, "id");
555
556     // Parse method
557     Value valMethod = find_value(request, "method");
558     if (valMethod.type() == null_type)
559         throw JSONRPCError(RPC_INVALID_REQUEST, "Missing method");
560     if (valMethod.type() != str_type)
561         throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string");
562     strMethod = valMethod.get_str();
563     if (strMethod != "getwork" && strMethod != "getblocktemplate")
564         printf("RPCServer method=%s\n", strMethod.c_str());
565
566     // Parse params
567     Value valParams = find_value(request, "params");
568     if (valParams.type() == array_type)
569         params = valParams.get_array();
570     else if (valParams.type() == null_type)
571         params = Array();
572     else
573         throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array");
574 }
575
576 static Object JSONRPCExecOne(const Value& req)
577 {
578     Object rpc_result;
579
580     JSONRequest jreq;
581     try {
582         jreq.parse(req);
583
584         Value result = tableRPC.execute(jreq.strMethod, jreq.params);
585         rpc_result = JSONRPCReplyObj(result, Value::null, jreq.id);
586     }
587     catch (Object& objError)
588     {
589         rpc_result = JSONRPCReplyObj(Value::null, objError, jreq.id);
590     }
591     catch (std::exception& e)
592     {
593         rpc_result = JSONRPCReplyObj(Value::null,
594                                      JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
595     }
596
597     return rpc_result;
598 }
599
600 static string JSONRPCExecBatch(const Array& vReq)
601 {
602     Array ret;
603     for (unsigned int reqIdx = 0; reqIdx < vReq.size(); reqIdx++)
604         ret.push_back(JSONRPCExecOne(vReq[reqIdx]));
605
606     return write_string(Value(ret), false) + "\n";
607 }
608
609 static CCriticalSection cs_THREAD_RPCHANDLER;
610
611 void StartRPCServer()
612 {
613     string host = GetArg("-rpchost", "127.0.0.1");
614     int port = GetArg("-rpcport", GetDefaultRPCPort());
615
616     g_server = std::unique_ptr<ix::HttpServer>(new ix::HttpServer(port, host));
617
618     LOCK(cs_THREAD_RPCHANDLER);
619
620     g_server->setOnConnectionCallback([](ix::HttpRequestPtr request, std::shared_ptr<ix::ConnectionState> connectionState) -> ix::HttpResponsePtr {
621
622         ix::WebSocketHttpHeaders headers;
623
624         if (request->method != "POST") {
625             return std::make_shared<ix::HttpResponse>(400, "Bad request", ix::HttpErrorCode::Ok, headers, "Bad request");
626         }
627
628         JSONRequest jreq;
629
630         try
631         {
632             // Parse request
633             Value valRequest;
634             if (!read_string(request->body, valRequest))
635                 throw JSONRPCError(RPC_PARSE_ERROR, "Parse error"); 
636
637             string strReply;
638
639             // singleton request
640             if (valRequest.type() == obj_type) {
641                 jreq.parse(valRequest);
642
643                 // Execute request
644                 Value result = tableRPC.execute(jreq.strMethod, jreq.params);
645
646                 // Send reply
647                 strReply = JSONRPCReply(result, Value::null, jreq.id);
648
649             // array of requests
650             } else if (valRequest.type() == array_type)
651                 // Execute batch of requests
652                 strReply = JSONRPCExecBatch(valRequest.get_array());
653             else
654                 throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error");
655
656             // Send reply to client
657             return std::make_shared<ix::HttpResponse>(200, "OK", ix::HttpErrorCode::Ok, headers, strReply);
658         
659         }
660         catch(Object& objError)
661         {
662             return std::make_shared<ix::HttpResponse>(500, "Internal Server Error", ix::HttpErrorCode::Ok, headers, ErrorReply(objError, jreq.id));
663         }
664         catch(std::exception& e)
665         {
666             return std::make_shared<ix::HttpResponse>(500, "Internal Server Error", ix::HttpErrorCode::Ok, headers, ErrorReply(JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id));
667         }
668     });
669
670     std::pair<bool, std::string> result = g_server->listen();
671     if (!result.first) {
672         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());
673         uiInterface.ThreadSafeMessageBox(strerr, _("Error"), CClientUIInterface::OK | CClientUIInterface::MODAL);
674         return StartShutdown();
675     }
676
677     // We're listening now
678     vnThreadsRunning[THREAD_RPCLISTENER]++;
679 }
680
681 void StopRPCServer()
682 {
683     LOCK(cs_THREAD_RPCHANDLER);
684     if (g_server) g_server->stop();
685     vnThreadsRunning[THREAD_RPCLISTENER]--;
686 }
687
688 json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_spirit::Array &params) const
689 {
690     // Find method
691     const CRPCCommand *pcmd = tableRPC[strMethod];
692     if (!pcmd)
693         throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found");
694
695     // Observe safe mode
696     string strWarning = GetWarnings("rpc");
697     if (!strWarning.empty() && !GetBoolArg("-disablesafemode") &&
698         !pcmd->okSafeMode)
699         throw JSONRPCError(RPC_FORBIDDEN_BY_SAFE_MODE, string("Safe mode: ") + strWarning);
700
701     try
702     {
703         // Execute
704         Value result;
705         {
706             if (pcmd->unlocked)
707                 result = pcmd->actor(params, false);
708             else {
709                 LOCK2(cs_main, pwalletMain->cs_wallet);
710                 result = pcmd->actor(params, false);
711             }
712         }
713         return result;
714     }
715     catch (std::exception& e)
716     {
717         throw JSONRPCError(RPC_MISC_ERROR, e.what());
718     }
719 }
720
721 std::vector<std::string> CRPCTable::listCommands() const
722 {
723     std::vector<std::string> commandList;
724     typedef std::map<std::string, const CRPCCommand*> commandMap;
725
726     std::transform( mapCommands.begin(), mapCommands.end(),
727                    std::back_inserter(commandList),
728                    boost::bind(&commandMap::value_type::first,_1) );
729     return commandList;
730 }
731
732 Object CallRPC(const string& strMethod, const Array& params)
733 {
734     if (mapArgs["-rpcuser"].empty() && mapArgs["-rpcpassword"].empty())
735         throw runtime_error(strprintf(
736             _("You must set rpcpassword=<password> in the configuration file:\n%s\n"
737               "If the file does not exist, create it with owner-readable-only file permissions."),
738                 GetConfigFile().string().c_str()));
739
740     // Create HTTP client
741     ix::HttpClient httpClient;
742     ix::HttpRequestArgsPtr args = httpClient.createRequest();
743
744     // HTTP basic authentication
745     ix::WebSocketHttpHeaders mapRequestHeaders;
746     string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]);
747     mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64;
748     args->extraHeaders = mapRequestHeaders;
749
750     // Timeouts
751     args->connectTimeout = GetArgInt("-rpc_connecttimeout", 30000);
752     args->transferTimeout = GetArgInt("-rpc_transfertimeout", 30000);
753
754     bool fUseSSL = GetBoolArg("-rpcssl");
755     string url = string(fUseSSL ? "https://" : "http://") + GetArg("-rpcconnect", "127.0.0.1") + ":" + GetArg("-rpcport", itostr(GetDefaultRPCPort()));
756
757     // Send request
758     string strRequest = JSONRPCRequest(strMethod, params, GetRandInt(INT32_MAX));
759     auto out = httpClient.post(url, strRequest, args);
760
761     // Process reply
762     int nStatus = out->statusCode;
763     string strReply = out->body;
764     ix::WebSocketHttpHeaders mapHeaders = out->headers;
765
766     // Receive reply
767     if (nStatus == HTTP_UNAUTHORIZED)
768         throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
769     else if (nStatus >= 400 && nStatus != HTTP_BAD_REQUEST && nStatus != HTTP_NOT_FOUND && nStatus != HTTP_INTERNAL_SERVER_ERROR)
770         throw runtime_error(strprintf("server returned HTTP error %d", nStatus));
771     else if (strReply.empty())
772         throw runtime_error("no response from server");
773
774     // Parse reply
775     Value valReply;
776     if (!read_string(strReply, valReply))
777         throw runtime_error("couldn't parse reply from server");
778     const Object& reply = valReply.get_obj();
779     if (reply.empty())
780         throw runtime_error("expected reply to have result, error and id properties");
781
782     return reply;
783 }
784
785
786
787
788 template<typename T>
789 void ConvertTo(Value& value, bool fAllowNull=false)
790 {
791     if (fAllowNull && value.type() == null_type)
792         return;
793     if (value.type() == str_type)
794     {
795         // reinterpret string as unquoted json value
796         Value value2;
797         string strJSON = value.get_str();
798         if (!read_string(strJSON, value2))
799             throw runtime_error(string("Error parsing JSON:")+strJSON);
800         ConvertTo<T>(value2, fAllowNull);
801         value = value2;
802     }
803     else
804     {
805         value = value.get_value<T>();
806     }
807 }
808
809 // Convert strings to command-specific RPC representation
810 Array RPCConvertValues(const std::string &strMethod, const std::vector<std::string> &strParams)
811 {
812     Array params;
813     BOOST_FOREACH(const std::string &param, strParams)
814         params.push_back(param);
815
816     size_t n = params.size();
817
818     //
819     // Special case non-string parameter types
820     //
821     if (strMethod == "stop"                   && n > 0) ConvertTo<bool>(params[0]);
822     if (strMethod == "getaddednodeinfo"       && n > 0) ConvertTo<bool>(params[0]);
823     if (strMethod == "sendtoaddress"          && n > 1) ConvertTo<double>(params[1]);
824     if (strMethod == "mergecoins"            && n > 0) ConvertTo<double>(params[0]);
825     if (strMethod == "mergecoins"            && n > 1) ConvertTo<double>(params[1]);
826     if (strMethod == "mergecoins"            && n > 2) ConvertTo<double>(params[2]);
827     if (strMethod == "settxfee"               && n > 0) ConvertTo<double>(params[0]);
828     if (strMethod == "getreceivedbyaddress"   && n > 1) ConvertTo<int64_t>(params[1]);
829     if (strMethod == "getreceivedbyaccount"   && n > 1) ConvertTo<int64_t>(params[1]);
830     if (strMethod == "listreceivedbyaddress"  && n > 0) ConvertTo<int64_t>(params[0]);
831     if (strMethod == "listreceivedbyaddress"  && n > 1) ConvertTo<bool>(params[1]);
832     if (strMethod == "listreceivedbyaccount"  && n > 0) ConvertTo<int64_t>(params[0]);
833     if (strMethod == "listreceivedbyaccount"  && n > 1) ConvertTo<bool>(params[1]);
834     if (strMethod == "getbalance"             && n > 1) ConvertTo<int64_t>(params[1]);
835     if (strMethod == "getblock"               && n > 1) ConvertTo<bool>(params[1]);
836     if (strMethod == "getblockbynumber"       && n > 0) ConvertTo<int64_t>(params[0]);
837     if (strMethod == "dumpblockbynumber"      && n > 0) ConvertTo<int64_t>(params[0]);
838     if (strMethod == "getblockbynumber"       && n > 1) ConvertTo<bool>(params[1]);
839     if (strMethod == "getblockhash"           && n > 0) ConvertTo<int64_t>(params[0]);
840     if (strMethod == "move"                   && n > 2) ConvertTo<double>(params[2]);
841     if (strMethod == "move"                   && n > 3) ConvertTo<int64_t>(params[3]);
842     if (strMethod == "sendfrom"               && n > 2) ConvertTo<double>(params[2]);
843     if (strMethod == "sendfrom"               && n > 3) ConvertTo<int64_t>(params[3]);
844     if (strMethod == "listtransactions"       && n > 1) ConvertTo<int64_t>(params[1]);
845     if (strMethod == "listtransactions"       && n > 2) ConvertTo<int64_t>(params[2]);
846     if (strMethod == "listaccounts"           && n > 0) ConvertTo<int64_t>(params[0]);
847     if (strMethod == "walletpassphrase"       && n > 1) ConvertTo<int64_t>(params[1]);
848     if (strMethod == "walletpassphrase"       && n > 2) ConvertTo<bool>(params[2]);
849     if (strMethod == "getblocktemplate"       && n > 0) ConvertTo<Object>(params[0]);
850     if (strMethod == "listsinceblock"         && n > 1) ConvertTo<int64_t>(params[1]);
851
852     if (strMethod == "scaninput"              && n > 0) ConvertTo<Object>(params[0]);
853
854     if (strMethod == "sendalert"              && n > 2) ConvertTo<int64_t>(params[2]);
855     if (strMethod == "sendalert"              && n > 3) ConvertTo<int64_t>(params[3]);
856     if (strMethod == "sendalert"              && n > 4) ConvertTo<int64_t>(params[4]);
857     if (strMethod == "sendalert"              && n > 5) ConvertTo<int64_t>(params[5]);
858     if (strMethod == "sendalert"              && n > 6) ConvertTo<int64_t>(params[6]);
859
860     if (strMethod == "sendmany"               && n > 1) ConvertTo<Object>(params[1]);
861     if (strMethod == "sendmany"               && n > 2) ConvertTo<int64_t>(params[2]);
862     if (strMethod == "reservebalance"         && n > 0) ConvertTo<bool>(params[0]);
863     if (strMethod == "reservebalance"         && n > 1) ConvertTo<double>(params[1]);
864     if (strMethod == "addmultisigaddress"     && n > 0) ConvertTo<int64_t>(params[0]);
865     if (strMethod == "addmultisigaddress"     && n > 1) ConvertTo<Array>(params[1]);
866     if (strMethod == "listunspent"            && n > 0) ConvertTo<int64_t>(params[0]);
867     if (strMethod == "listunspent"            && n > 1) ConvertTo<int64_t>(params[1]);
868     if (strMethod == "listunspent"            && n > 2) ConvertTo<Array>(params[2]);
869     if (strMethod == "getrawtransaction"      && n > 1) ConvertTo<int64_t>(params[1]);
870     if (strMethod == "createrawtransaction"   && n > 0) ConvertTo<Array>(params[0]);
871     if (strMethod == "createrawtransaction"   && n > 1) ConvertTo<Object>(params[1]);
872     if (strMethod == "createmultisig"         && n > 0) ConvertTo<int64_t>(params[0]);
873     if (strMethod == "createmultisig"         && n > 1) ConvertTo<Array>(params[1]);
874     if (strMethod == "signrawtransaction"     && n > 1) ConvertTo<Array>(params[1], true);
875     if (strMethod == "signrawtransaction"     && n > 2) ConvertTo<Array>(params[2], true);
876     if (strMethod == "keypoolrefill"          && n > 0) ConvertTo<int64_t>(params[0]);
877     if (strMethod == "keypoolreset"           && n > 0) ConvertTo<int64_t>(params[0]);
878     if (strMethod == "importaddress"          && n > 2) ConvertTo<bool>(params[2]);
879     if (strMethod == "importprivkey"          && n > 2) ConvertTo<bool>(params[2]);
880
881     return params;
882 }
883
884 int CommandLineRPC(int argc, char *argv[])
885 {
886     string strPrint;
887     int nRet = 0;
888     try
889     {
890         // Skip switches
891         while (argc > 1 && IsSwitchChar(argv[1][0]))
892         {
893             argc--;
894             argv++;
895         }
896
897         // Method
898         if (argc < 2)
899             throw runtime_error("too few parameters");
900         string strMethod = argv[1];
901
902         // Parameters default to strings
903         std::vector<std::string> strParams(&argv[2], &argv[argc]);
904         Array params = RPCConvertValues(strMethod, strParams);
905
906         // Execute
907         Object reply = CallRPC(strMethod, params);
908
909         // Parse reply
910         const Value& result = find_value(reply, "result");
911         const Value& error  = find_value(reply, "error");
912
913         if (error.type() != null_type)
914         {
915             // Error
916             strPrint = "error: " + write_string(error, false);
917             int code = find_value(error.get_obj(), "code").get_int();
918             nRet = abs(code);
919         }
920         else
921         {
922             // Result
923             if (result.type() == null_type)
924                 strPrint.clear();
925             else if (result.type() == str_type)
926                 strPrint = result.get_str();
927             else
928                 strPrint = write_string(result, true);
929         }
930     }
931     catch (std::exception& e)
932     {
933         strPrint = string("error: ") + e.what();
934         nRet = 87;
935     }
936     catch (...)
937     {
938         PrintException(NULL, "CommandLineRPC()");
939     }
940
941     if (!strPrint.empty())
942     {
943         fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
944     }
945     return nRet;
946 }
947
948
949 const CRPCTable tableRPC;