X-Git-Url: https://git.novaco.in/?a=blobdiff_plain;f=src%2Fbitcoinrpc.cpp;h=b411be47000122b796aab9d1541dfec13f27f081;hb=cc5173a20534bd761caca6c5291cadc42a0ba5d7;hp=33961bdbcce4905c5928208103f8f91c848b09ac;hpb=6145f2d5e9f380a11bce933fe343d48fd8936a33;p=novacoin.git diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp index 33961bd..b411be4 100644 --- a/src/bitcoinrpc.cpp +++ b/src/bitcoinrpc.cpp @@ -4,49 +4,38 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "init.h" -#include "util.h" -#include "sync.h" -#include "ui_interface.h" #include "base58.h" #include "bitcoinrpc.h" #include "db.h" +#include "interface.h" +#include "sync.h" +#include "util.h" #undef printf -#include -#include -#include -#include -#include -#include -#include + #include -#include -#include -#include -#include +#include +#include + #include +#include #define printf OutputDebugStringF -using namespace std; -using namespace boost; -using namespace boost::asio; using namespace json_spirit; -void ThreadRPCServer2(void* parg); +std::unique_ptr g_server; static std::string strRPCUserColonPass; const Object emptyobj; -void ThreadRPCServer3(void* parg); - static inline unsigned short GetDefaultRPCPort() { return GetBoolArg("-testnet", false) ? 18344 : 8344; } -Object JSONRPCError(int code, const string& message) +Object JSONRPCError(int code, const std::string& message) { Object error; error.push_back(Pair("code", code)); @@ -55,11 +44,11 @@ Object JSONRPCError(int code, const string& message) } void RPCTypeCheck(const Array& params, - const list& typesExpected, + const std::list& typesExpected, bool fAllowNull) { unsigned int i = 0; - BOOST_FOREACH(Value_type t, typesExpected) + for(Value_type t : typesExpected) { if (params.size() <= i) break; @@ -67,7 +56,7 @@ void RPCTypeCheck(const Array& params, const Value& v = params[i]; if (!((v.type() == t) || (fAllowNull && (v.type() == null_type)))) { - string err = strprintf("Expected type %s, got %s", + std::string err = strprintf("Expected type %s, got %s", Value_type_name[t], Value_type_name[v.type()]); throw JSONRPCError(RPC_TYPE_ERROR, err); } @@ -76,10 +65,10 @@ void RPCTypeCheck(const Array& params, } void RPCTypeCheck(const Object& o, - const map& typesExpected, + const std::map& typesExpected, bool fAllowNull) { - BOOST_FOREACH(const PAIRTYPE(string, Value_type)& t, typesExpected) + for(const auto& t : typesExpected) { const Value& v = find_value(o, t.first); if (!fAllowNull && v.type() == null_type) @@ -87,25 +76,25 @@ void RPCTypeCheck(const Object& o, if (!((v.type() == t.second) || (fAllowNull && (v.type() == null_type)))) { - string err = strprintf("Expected type %s for %s, got %s", + std::string err = strprintf("Expected type %s for %s, got %s", Value_type_name[t.second], t.first.c_str(), Value_type_name[v.type()]); throw JSONRPCError(RPC_TYPE_ERROR, err); } } } -int64 AmountFromValue(const Value& value) +int64_t AmountFromValue(const Value& value) { double dAmount = value.get_real(); - if (dAmount <= 0.0 || dAmount > MAX_MONEY) + if (dAmount <= 0.0 || dAmount > (double) (MAX_MONEY / 100000000)) throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount"); - int64 nAmount = roundint64(dAmount * COIN); + int64_t nAmount = roundint64(dAmount * COIN); if (!MoneyRange(nAmount)) throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount"); return nAmount; } -Value ValueFromAmount(int64 amount) +Value ValueFromAmount(int64_t amount) { return (double)amount / (double)COIN; } @@ -121,23 +110,59 @@ std::string HexBits(unsigned int nBits) } +// +// Utilities: convert hex-encoded Values +// (throws error if not hex). +// +uint256 ParseHashV(const Value& v, const std::string& strName) +{ + std::string strHex; + if (v.type() == str_type) + strHex = v.get_str(); + if (!IsHex(strHex)) // Note: IsHex("") is false + throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')"); + uint256 result; + result.SetHex(strHex); + return result; +} + +uint256 ParseHashO(const Object& o, const std::string& strKey) +{ + return ParseHashV(find_value(o, strKey), strKey); +} + +std::vector ParseHexV(const Value& v, const std::string& strName) +{ + std::string strHex; + if (v.type() == str_type) + strHex = v.get_str(); + if (!IsHex(strHex)) + throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')"); + return ParseHex(strHex); +} + +std::vector ParseHexO(const Object& o, const std::string& strKey) +{ + return ParseHexV(find_value(o, strKey), strKey); +} + /// /// Note: This interface may still be subject to change. /// -string CRPCTable::help(string strCommand) const +std::string CRPCTable::help(const std::string& strCommand) const { - string strRet; - set setDone; - for (map::const_iterator mi = mapCommands.begin(); mi != mapCommands.end(); ++mi) + std::string strRet; + std::set setDone; + for (const auto & mapCommand : mapCommands) { - const CRPCCommand *pcmd = mi->second; - string strMethod = mi->first; + const CRPCCommand *pcmd = mapCommand.second; + std::string strMethod = mapCommand.first; // We already filter duplicates, but these deprecated screw up the sort order - if (strMethod.find("label") != string::npos) + if (strMethod.find("label") != std::string::npos) continue; - if (strCommand != "" && strMethod != strCommand) + if (!strCommand.empty() && strMethod != strCommand) continue; try { @@ -146,17 +171,17 @@ string CRPCTable::help(string strCommand) const if (setDone.insert(pfn).second) (*pfn)(params, true); } - catch (std::exception& e) + catch (const std::exception& e) { // Help text is returned in an exception - string strHelp = string(e.what()); - if (strCommand == "") - if (strHelp.find('\n') != string::npos) + std::string strHelp = std::string(e.what()); + if (strCommand.empty()) + if (strHelp.find('\n') != std::string::npos) strHelp = strHelp.substr(0, strHelp.find('\n')); - strRet += strHelp + "\n"; + strRet += strHelp + '\n'; } } - if (strRet == "") + if (strRet.empty()) strRet = strprintf("help: unknown command: %s\n", strCommand.c_str()); strRet = strRet.substr(0,strRet.size()-1); return strRet; @@ -165,12 +190,12 @@ string CRPCTable::help(string strCommand) const Value help(const Array& params, bool fHelp) { if (fHelp || params.size() > 1) - throw runtime_error( + throw std::runtime_error( "help [command]\n" "List commands, or get help for a command."); - string strCommand; - if (params.size() > 0) + std::string strCommand; + if (!params.empty()) strCommand = params[0].get_str(); return tableRPC.help(strCommand); @@ -180,12 +205,12 @@ Value help(const Array& params, bool fHelp) Value stop(const Array& params, bool fHelp) { if (fHelp || params.size() > 1) - throw runtime_error( + throw std::runtime_error( "stop \n" " is true or false to detach the database or not for this stop only\n" "Stop NovaCoin server (and possibly override the detachdb config value)."); // Shutdown will take long enough that the response should get back - if (params.size() > 0) + if (!params.empty()) bitdb.SetDetach(params[0].get_bool()); StartShutdown(); return "NovaCoin server stopping"; @@ -201,258 +226,121 @@ Value stop(const Array& params, bool fHelp) static const CRPCCommand vRPCCommands[] = { // name function safemd unlocked // ------------------------ ----------------------- ------ -------- - { "help", &help, true, true }, - { "stop", &stop, true, true }, - { "getblockcount", &getblockcount, true, false }, - { "getconnectioncount", &getconnectioncount, true, false }, - { "getpeerinfo", &getpeerinfo, true, false }, - { "getdifficulty", &getdifficulty, true, false }, - { "getgenerate", &getgenerate, true, false }, - { "setgenerate", &setgenerate, true, false }, - { "gethashespersec", &gethashespersec, true, false }, - { "getinfo", &getinfo, true, false }, - { "getmininginfo", &getmininginfo, true, false }, - { "getnewaddress", &getnewaddress, true, false }, - { "getnewpubkey", &getnewpubkey, true, false }, - { "getaccountaddress", &getaccountaddress, true, false }, - { "setaccount", &setaccount, true, false }, - { "getaccount", &getaccount, false, false }, - { "getaddressesbyaccount", &getaddressesbyaccount, true, false }, - { "sendtoaddress", &sendtoaddress, false, false }, - { "getreceivedbyaddress", &getreceivedbyaddress, false, false }, - { "getreceivedbyaccount", &getreceivedbyaccount, false, false }, - { "listreceivedbyaddress", &listreceivedbyaddress, false, false }, - { "listreceivedbyaccount", &listreceivedbyaccount, false, false }, - { "backupwallet", &backupwallet, true, false }, - { "keypoolrefill", &keypoolrefill, true, false }, - { "walletpassphrase", &walletpassphrase, true, false }, - { "walletpassphrasechange", &walletpassphrasechange, false, false }, - { "walletlock", &walletlock, true, false }, - { "encryptwallet", &encryptwallet, false, false }, - { "validateaddress", &validateaddress, true, false }, - { "validatepubkey", &validatepubkey, true, false }, - { "getbalance", &getbalance, false, false }, - { "move", &movecmd, false, false }, - { "sendfrom", &sendfrom, false, false }, - { "sendmany", &sendmany, false, false }, - { "addmultisigaddress", &addmultisigaddress, false, false }, - { "getrawmempool", &getrawmempool, true, false }, - { "getblock", &getblock, false, false }, - { "getblockbynumber", &getblockbynumber, false, false }, - { "getblockhash", &getblockhash, false, false }, - { "gettransaction", &gettransaction, false, false }, - { "listtransactions", &listtransactions, false, false }, - { "listaddressgroupings", &listaddressgroupings, false, false }, - { "signmessage", &signmessage, false, false }, - { "verifymessage", &verifymessage, false, false }, - { "getwork", &getwork, true, false }, - { "getworkex", &getworkex, true, false }, - { "listaccounts", &listaccounts, false, false }, - { "settxfee", &settxfee, false, false }, - { "getblocktemplate", &getblocktemplate, true, false }, - { "submitblock", &submitblock, false, false }, - { "listsinceblock", &listsinceblock, false, false }, - { "dumpprivkey", &dumpprivkey, false, false }, - { "importprivkey", &importprivkey, false, false }, - { "listunspent", &listunspent, false, false }, - { "getrawtransaction", &getrawtransaction, false, false }, - { "createrawtransaction", &createrawtransaction, false, false }, - { "decoderawtransaction", &decoderawtransaction, false, false }, - { "signrawtransaction", &signrawtransaction, false, false }, - { "sendrawtransaction", &sendrawtransaction, false, false }, - { "getcheckpoint", &getcheckpoint, true, false }, - { "reservebalance", &reservebalance, false, true}, - { "checkwallet", &checkwallet, false, true}, - { "repairwallet", &repairwallet, false, true}, - { "resendtx", &resendtx, false, true}, - { "makekeypair", &makekeypair, false, true}, - { "sendalert", &sendalert, false, false}, + { "help", &help, true, true }, + { "stop", &stop, true, true }, + { "getbestblockhash", &getbestblockhash, true, false }, + { "getblockcount", &getblockcount, true, false }, + { "getconnectioncount", &getconnectioncount, true, false }, + { "getaddrmaninfo", &getaddrmaninfo, true, false }, + { "getpeerinfo", &getpeerinfo, true, false }, + { "addnode", &addnode, true, true }, + { "getaddednodeinfo", &getaddednodeinfo, true, true }, + { "getdifficulty", &getdifficulty, true, false }, + { "getinfo", &getinfo, true, false }, + { "getsubsidy", &getsubsidy, true, false }, + { "getmininginfo", &getmininginfo, true, false }, + { "scaninput", &scaninput, true, true }, + { "getnewaddress", &getnewaddress, true, false }, + { "getnettotals", &getnettotals, true, true }, + { "ntptime", &ntptime, true, true }, + { "getaccountaddress", &getaccountaddress, true, false }, + { "setaccount", &setaccount, true, false }, + { "getaccount", &getaccount, false, false }, + { "getaddressesbyaccount", &getaddressesbyaccount, true, false }, + { "sendtoaddress", &sendtoaddress, false, false }, + { "mergecoins", &mergecoins, false, false }, + { "getreceivedbyaddress", &getreceivedbyaddress, false, false }, + { "getreceivedbyaccount", &getreceivedbyaccount, false, false }, + { "listreceivedbyaddress", &listreceivedbyaddress, false, false }, + { "listreceivedbyaccount", &listreceivedbyaccount, false, false }, + { "backupwallet", &backupwallet, true, false }, + { "keypoolrefill", &keypoolrefill, true, false }, + { "keypoolreset", &keypoolreset, true, false }, + { "walletpassphrase", &walletpassphrase, true, false }, + { "walletpassphrasechange", &walletpassphrasechange, false, false }, + { "walletlock", &walletlock, true, false }, + { "encryptwallet", &encryptwallet, false, false }, + { "validateaddress", &validateaddress, true, false }, + { "getbalance", &getbalance, false, false }, + { "move", &movecmd, false, false }, + { "sendfrom", &sendfrom, false, false }, + { "sendmany", &sendmany, false, false }, + { "addmultisigaddress", &addmultisigaddress, false, false }, + { "addredeemscript", &addredeemscript, false, false }, + { "getrawmempool", &getrawmempool, true, false }, + { "getblock", &getblock, false, false }, + { "getblockbynumber", &getblockbynumber, false, false }, + { "dumpblock", &dumpblock, false, false }, + { "dumpblockbynumber", &dumpblockbynumber, false, false }, + { "getblockhash", &getblockhash, false, false }, + { "gettransaction", &gettransaction, false, false }, + { "listtransactions", &listtransactions, false, false }, + { "listaddressgroupings", &listaddressgroupings, false, false }, + { "signmessage", &signmessage, false, false }, + { "verifymessage", &verifymessage, false, false }, + { "getwork", &getwork, true, false }, + { "getworkex", &getworkex, true, false }, + { "listaccounts", &listaccounts, false, false }, + { "settxfee", &settxfee, false, false }, + { "getblocktemplate", &getblocktemplate, true, false }, + { "submitblock", &submitblock, false, false }, + { "listsinceblock", &listsinceblock, false, false }, + { "dumpprivkey", &dumpprivkey, false, false }, + { "dumpwallet", &dumpwallet, true, false }, + { "importwallet", &importwallet, false, false }, + { "importprivkey", &importprivkey, false, false }, + { "importaddress", &importaddress, false, true }, + { "removeaddress", &removeaddress, false, true }, + { "listunspent", &listunspent, false, false }, + { "getrawtransaction", &getrawtransaction, false, false }, + { "createrawtransaction", &createrawtransaction, false, false }, + { "decoderawtransaction", &decoderawtransaction, false, false }, + { "createmultisig", &createmultisig, false, false }, + { "decodescript", &decodescript, false, false }, + { "signrawtransaction", &signrawtransaction, false, false }, + { "sendrawtransaction", &sendrawtransaction, false, false }, + { "getcheckpoint", &getcheckpoint, true, false }, + { "reservebalance", &reservebalance, false, true}, + { "checkwallet", &checkwallet, false, true}, + { "repairwallet", &repairwallet, false, true}, + { "resendwallettransactions", &resendwallettransactions, false, true}, + { "makekeypair", &makekeypair, false, true}, + { "newmalleablekey", &newmalleablekey, false, false}, + { "adjustmalleablekey", &adjustmalleablekey, false, false}, + { "adjustmalleablepubkey", &adjustmalleablepubkey, false, false}, + { "listmalleableviews", &listmalleableviews, false, false}, + { "dumpmalleablekey", &dumpmalleablekey, false, false}, + { "importmalleablekey", &importmalleablekey, true, false }, + { "sendalert", &sendalert, false, false}, }; CRPCTable::CRPCTable() { - unsigned int vcidx; - for (vcidx = 0; vcidx < (sizeof(vRPCCommands) / sizeof(vRPCCommands[0])); vcidx++) + for (const auto & vRPCCommand : vRPCCommands) { const CRPCCommand *pcmd; - pcmd = &vRPCCommands[vcidx]; + pcmd = &vRPCCommand; mapCommands[pcmd->name] = pcmd; } } -const CRPCCommand *CRPCTable::operator[](string name) const +const CRPCCommand *CRPCTable::operator[](const std::string& name) const { - map::const_iterator it = mapCommands.find(name); + auto it = mapCommands.find(name); if (it == mapCommands.end()) - return NULL; + return nullptr; return (*it).second; } -// -// HTTP protocol -// -// This ain't Apache. We're just using HTTP header for the length field -// and to be compatible with other JSON-RPC implementations. -// - -string HTTPPost(const string& strMsg, const map& mapRequestHeaders) -{ - ostringstream s; - s << "POST / HTTP/1.1\r\n" - << "User-Agent: novacoin-json-rpc/" << FormatFullVersion() << "\r\n" - << "Host: 127.0.0.1\r\n" - << "Content-Type: application/json\r\n" - << "Content-Length: " << strMsg.size() << "\r\n" - << "Connection: close\r\n" - << "Accept: application/json\r\n"; - BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapRequestHeaders) - s << item.first << ": " << item.second << "\r\n"; - s << "\r\n" << strMsg; - - return s.str(); -} - -string rfc1123Time() -{ - char buffer[64]; - time_t now; - time(&now); - struct tm* now_gmt = gmtime(&now); - string locale(setlocale(LC_TIME, NULL)); - setlocale(LC_TIME, "C"); // we want POSIX (aka "C") weekday/month strings - strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S +0000", now_gmt); - setlocale(LC_TIME, locale.c_str()); - return string(buffer); -} - -static string HTTPReply(int nStatus, const string& strMsg, bool keepalive) +bool HTTPAuthorized(ix::WebSocketHttpHeaders& mapHeaders) { - if (nStatus == HTTP_UNAUTHORIZED) - return strprintf("HTTP/1.0 401 Authorization Required\r\n" - "Date: %s\r\n" - "Server: novacoin-json-rpc/%s\r\n" - "WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n" - "Content-Type: text/html\r\n" - "Content-Length: 296\r\n" - "\r\n" - "\r\n" - "\r\n" - "\r\n" - "Error\r\n" - "\r\n" - "\r\n" - "

401 Unauthorized.

\r\n" - "\r\n", rfc1123Time().c_str(), FormatFullVersion().c_str()); - const char *cStatus; - if (nStatus == HTTP_OK) cStatus = "OK"; - else if (nStatus == HTTP_BAD_REQUEST) cStatus = "Bad Request"; - else if (nStatus == HTTP_FORBIDDEN) cStatus = "Forbidden"; - else if (nStatus == HTTP_NOT_FOUND) cStatus = "Not Found"; - else if (nStatus == HTTP_INTERNAL_SERVER_ERROR) cStatus = "Internal Server Error"; - else cStatus = ""; - return strprintf( - "HTTP/1.1 %d %s\r\n" - "Date: %s\r\n" - "Connection: %s\r\n" - "Content-Length: %"PRIszu"\r\n" - "Content-Type: application/json\r\n" - "Server: novacoin-json-rpc/%s\r\n" - "\r\n" - "%s", - nStatus, - cStatus, - rfc1123Time().c_str(), - keepalive ? "keep-alive" : "close", - strMsg.size(), - FormatFullVersion().c_str(), - strMsg.c_str()); -} - -int ReadHTTPStatus(std::basic_istream& stream, int &proto) -{ - string str; - getline(stream, str); - vector vWords; - boost::split(vWords, str, boost::is_any_of(" ")); - if (vWords.size() < 2) - return HTTP_INTERNAL_SERVER_ERROR; - proto = 0; - const char *ver = strstr(str.c_str(), "HTTP/1."); - if (ver != NULL) - proto = atoi(ver+7); - return atoi(vWords[1].c_str()); -} - -int ReadHTTPHeader(std::basic_istream& stream, map& mapHeadersRet) -{ - int nLen = 0; - loop - { - string str; - std::getline(stream, str); - if (str.empty() || str == "\r") - break; - string::size_type nColon = str.find(":"); - if (nColon != string::npos) - { - string strHeader = str.substr(0, nColon); - boost::trim(strHeader); - boost::to_lower(strHeader); - string strValue = str.substr(nColon+1); - boost::trim(strValue); - mapHeadersRet[strHeader] = strValue; - if (strHeader == "content-length") - nLen = atoi(strValue.c_str()); - } - } - return nLen; -} - -int ReadHTTP(std::basic_istream& stream, map& mapHeadersRet, string& strMessageRet) -{ - mapHeadersRet.clear(); - strMessageRet = ""; - - // Read status - int nProto = 0; - int nStatus = ReadHTTPStatus(stream, nProto); - - // Read header - int nLen = ReadHTTPHeader(stream, mapHeadersRet); - if (nLen < 0 || nLen > (int)MAX_SIZE) - return HTTP_INTERNAL_SERVER_ERROR; - - // Read message - if (nLen > 0) - { - vector vch(nLen); - stream.read(&vch[0], nLen); - strMessageRet = string(vch.begin(), vch.end()); - } - - string sConHdr = mapHeadersRet["connection"]; - - if ((sConHdr != "close") && (sConHdr != "keep-alive")) - { - if (nProto >= 1) - mapHeadersRet["connection"] = "keep-alive"; - else - mapHeadersRet["connection"] = "close"; - } - - return nStatus; -} - -bool HTTPAuthorized(map& mapHeaders) -{ - string strAuth = mapHeaders["authorization"]; + std::string strAuth = mapHeaders["authorization"]; if (strAuth.substr(0,6) != "Basic ") return false; - string strUserPass64 = strAuth.substr(6); boost::trim(strUserPass64); - string strUserPass = DecodeBase64(strUserPass64); - return strUserPass == strRPCUserColonPass; + std::string strUserPass64 = strAuth.substr(6); boost::algorithm::trim(strUserPass64); + std::string strUserPass = DecodeBase64(strUserPass64); + return TimingResistantEqual(strUserPass, strRPCUserColonPass); } // @@ -465,7 +353,7 @@ bool HTTPAuthorized(map& mapHeaders) // http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx // -string JSONRPCRequest(const string& strMethod, const Array& params, const Value& id) +std::string JSONRPCRequest(const std::string& strMethod, const Array& params, const Value& id) { Object request; request.push_back(Pair("method", strMethod)); @@ -486,375 +374,27 @@ Object JSONRPCReplyObj(const Value& result, const Value& error, const Value& id) return reply; } -string JSONRPCReply(const Value& result, const Value& error, const Value& id) +std::string JSONRPCReply(const Value& result, const Value& error, const Value& id) { Object reply = JSONRPCReplyObj(result, error, id); return write_string(Value(reply), false) + "\n"; } -void ErrorReply(std::ostream& stream, const Object& objError, const Value& id) +std::string ErrorReply(const Object& objError, const Value& id) { // Send error reply from json-rpc error object int nStatus = HTTP_INTERNAL_SERVER_ERROR; int code = find_value(objError, "code").get_int(); if (code == RPC_INVALID_REQUEST) nStatus = HTTP_BAD_REQUEST; else if (code == RPC_METHOD_NOT_FOUND) nStatus = HTTP_NOT_FOUND; - string strReply = JSONRPCReply(Value::null, objError, id); - stream << HTTPReply(nStatus, strReply, false) << std::flush; -} - -bool ClientAllowed(const boost::asio::ip::address& address) -{ - // Make sure that IPv4-compatible and IPv4-mapped IPv6 addresses are treated as IPv4 addresses - if (address.is_v6() - && (address.to_v6().is_v4_compatible() - || address.to_v6().is_v4_mapped())) - return ClientAllowed(address.to_v6().to_v4()); - - if (address == asio::ip::address_v4::loopback() - || address == asio::ip::address_v6::loopback() - || (address.is_v4() - // Check whether IPv4 addresses match 127.0.0.0/8 (loopback subnet) - && (address.to_v4().to_ulong() & 0xff000000) == 0x7f000000)) - return true; - - const string strAddress = address.to_string(); - const vector& vAllow = mapMultiArgs["-rpcallowip"]; - BOOST_FOREACH(string strAllow, vAllow) - if (WildcardMatch(strAddress, strAllow)) - return true; - return false; -} - -// -// IOStream device that speaks SSL but can also speak non-SSL -// -template -class SSLIOStreamDevice : public iostreams::device { -public: - SSLIOStreamDevice(asio::ssl::stream &streamIn, bool fUseSSLIn) : stream(streamIn) - { - fUseSSL = fUseSSLIn; - fNeedHandshake = fUseSSLIn; - } - - void handshake(ssl::stream_base::handshake_type role) - { - if (!fNeedHandshake) return; - fNeedHandshake = false; - stream.handshake(role); - } - std::streamsize read(char* s, std::streamsize n) - { - handshake(ssl::stream_base::server); // HTTPS servers read first - if (fUseSSL) return stream.read_some(asio::buffer(s, n)); - return stream.next_layer().read_some(asio::buffer(s, n)); - } - std::streamsize write(const char* s, std::streamsize n) - { - handshake(ssl::stream_base::client); // HTTPS clients write first - if (fUseSSL) return asio::write(stream, asio::buffer(s, n)); - return asio::write(stream.next_layer(), asio::buffer(s, n)); - } - bool connect(const std::string& server, const std::string& port) - { - ip::tcp::resolver resolver(stream.get_io_service()); - ip::tcp::resolver::query query(server.c_str(), port.c_str()); - ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query); - ip::tcp::resolver::iterator end; - boost::system::error_code error = asio::error::host_not_found; - while (error && endpoint_iterator != end) - { - stream.lowest_layer().close(); - stream.lowest_layer().connect(*endpoint_iterator++, error); - } - if (error) - return false; - return true; - } - -private: - bool fNeedHandshake; - bool fUseSSL; - asio::ssl::stream& stream; -}; - -class AcceptedConnection -{ -public: - virtual ~AcceptedConnection() {} - - virtual std::iostream& stream() = 0; - virtual std::string peer_address_to_string() const = 0; - virtual void close() = 0; -}; - -template -class AcceptedConnectionImpl : public AcceptedConnection -{ -public: - AcceptedConnectionImpl( - asio::io_service& io_service, - ssl::context &context, - bool fUseSSL) : - sslStream(io_service, context), - _d(sslStream, fUseSSL), - _stream(_d) - { - } - - virtual std::iostream& stream() - { - return _stream; - } - - virtual std::string peer_address_to_string() const - { - return peer.address().to_string(); - } - - virtual void close() - { - _stream.close(); - } - - typename Protocol::endpoint peer; - asio::ssl::stream sslStream; - -private: - SSLIOStreamDevice _d; - iostreams::stream< SSLIOStreamDevice > _stream; -}; - -void ThreadRPCServer(void* parg) -{ - // Make this thread recognisable as the RPC listener - RenameThread("bitcoin-rpclist"); - - try - { - vnThreadsRunning[THREAD_RPCLISTENER]++; - ThreadRPCServer2(parg); - vnThreadsRunning[THREAD_RPCLISTENER]--; - } - catch (std::exception& e) { - vnThreadsRunning[THREAD_RPCLISTENER]--; - PrintException(&e, "ThreadRPCServer()"); - } catch (...) { - vnThreadsRunning[THREAD_RPCLISTENER]--; - PrintException(NULL, "ThreadRPCServer()"); - } - printf("ThreadRPCServer exited\n"); -} - -// Forward declaration required for RPCListen -template -static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptor > acceptor, - ssl::context& context, - bool fUseSSL, - AcceptedConnection* conn, - const boost::system::error_code& error); - -/** - * Sets up I/O resources to accept and handle a new connection. - */ -template -static void RPCListen(boost::shared_ptr< basic_socket_acceptor > acceptor, - ssl::context& context, - const bool fUseSSL) -{ - // Accept connection - AcceptedConnectionImpl* conn = new AcceptedConnectionImpl(acceptor->get_io_service(), context, fUseSSL); - - acceptor->async_accept( - conn->sslStream.lowest_layer(), - conn->peer, - boost::bind(&RPCAcceptHandler, - acceptor, - boost::ref(context), - fUseSSL, - conn, - boost::asio::placeholders::error)); -} - -/** - * Accept and handle incoming connection. - */ -template -static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptor > acceptor, - ssl::context& context, - const bool fUseSSL, - AcceptedConnection* conn, - const boost::system::error_code& error) -{ - vnThreadsRunning[THREAD_RPCLISTENER]++; - - // Immediately start accepting new connections, except when we're cancelled or our socket is closed. - if (error != asio::error::operation_aborted - && acceptor->is_open()) - RPCListen(acceptor, context, fUseSSL); - - AcceptedConnectionImpl* tcp_conn = dynamic_cast< AcceptedConnectionImpl* >(conn); - - // TODO: Actually handle errors - if (error) - { - delete conn; - } - - // Restrict callers by IP. It is important to - // do this before starting client thread, to filter out - // certain DoS and misbehaving clients. - else if (tcp_conn - && !ClientAllowed(tcp_conn->peer.address())) - { - // Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake. - if (!fUseSSL) - conn->stream() << HTTPReply(HTTP_FORBIDDEN, "", false) << std::flush; - delete conn; - } - - // start HTTP client thread - else if (!NewThread(ThreadRPCServer3, conn)) { - printf("Failed to create RPC server client thread\n"); - delete conn; - } - - vnThreadsRunning[THREAD_RPCLISTENER]--; -} - -void ThreadRPCServer2(void* parg) -{ - printf("ThreadRPCServer started\n"); - - strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]; - if (mapArgs["-rpcpassword"] == "") - { - unsigned char rand_pwd[32]; - RAND_bytes(rand_pwd, 32); - string strWhatAmI = "To use novacoind"; - if (mapArgs.count("-server")) - strWhatAmI = strprintf(_("To use the %s option"), "\"-server\""); - else if (mapArgs.count("-daemon")) - strWhatAmI = strprintf(_("To use the %s option"), "\"-daemon\""); - uiInterface.ThreadSafeMessageBox(strprintf( - _("%s, you must set a rpcpassword in the configuration file:\n %s\n" - "It is recommended you use the following random password:\n" - "rpcuser=bitcoinrpc\n" - "rpcpassword=%s\n" - "(you do not need to remember this password)\n" - "If the file does not exist, create it with owner-readable-only file permissions.\n"), - strWhatAmI.c_str(), - GetConfigFile().string().c_str(), - EncodeBase58(&rand_pwd[0],&rand_pwd[0]+32).c_str()), - _("Error"), CClientUIInterface::OK | CClientUIInterface::MODAL); - StartShutdown(); - return; - } - - const bool fUseSSL = GetBoolArg("-rpcssl"); - - asio::io_service io_service; - - ssl::context context(io_service, ssl::context::sslv23); - if (fUseSSL) - { - context.set_options(ssl::context::no_sslv2); - - filesystem::path pathCertFile(GetArg("-rpcsslcertificatechainfile", "server.cert")); - if (!pathCertFile.is_complete()) pathCertFile = filesystem::path(GetDataDir()) / pathCertFile; - if (filesystem::exists(pathCertFile)) context.use_certificate_chain_file(pathCertFile.string()); - else printf("ThreadRPCServer ERROR: missing server certificate file %s\n", pathCertFile.string().c_str()); - - filesystem::path pathPKFile(GetArg("-rpcsslprivatekeyfile", "server.pem")); - if (!pathPKFile.is_complete()) pathPKFile = filesystem::path(GetDataDir()) / pathPKFile; - if (filesystem::exists(pathPKFile)) context.use_private_key_file(pathPKFile.string(), ssl::context::pem); - else printf("ThreadRPCServer ERROR: missing server private key file %s\n", pathPKFile.string().c_str()); - - string strCiphers = GetArg("-rpcsslciphers", "TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!AH:!3DES:@STRENGTH"); - SSL_CTX_set_cipher_list(context.impl(), strCiphers.c_str()); - } - - // Try a dual IPv6/IPv4 socket, falling back to separate IPv4 and IPv6 sockets - const bool loopback = !mapArgs.count("-rpcallowip"); - asio::ip::address bindAddress = loopback ? asio::ip::address_v6::loopback() : asio::ip::address_v6::any(); - ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", GetDefaultRPCPort())); - boost::system::error_code v6_only_error; - boost::shared_ptr acceptor(new ip::tcp::acceptor(io_service)); - - boost::signals2::signal StopRequests; - - bool fListening = false; - std::string strerr; - try - { - acceptor->open(endpoint.protocol()); - acceptor->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); - - // Try making the socket dual IPv6/IPv4 (if listening on the "any" address) - acceptor->set_option(boost::asio::ip::v6_only(loopback), v6_only_error); - - acceptor->bind(endpoint); - acceptor->listen(socket_base::max_connections); - - RPCListen(acceptor, context, fUseSSL); - // Cancel outstanding listen-requests for this acceptor when shutting down - StopRequests.connect(signals2::slot( - static_cast(&ip::tcp::acceptor::close), acceptor.get()) - .track(acceptor)); - - fListening = true; - } - catch(boost::system::system_error &e) - { - strerr = strprintf(_("An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s"), endpoint.port(), e.what()); - } - - try { - // If dual IPv6/IPv4 failed (or we're opening loopback interfaces only), open IPv4 separately - if (!fListening || loopback || v6_only_error) - { - bindAddress = loopback ? asio::ip::address_v4::loopback() : asio::ip::address_v4::any(); - endpoint.address(bindAddress); - - acceptor.reset(new ip::tcp::acceptor(io_service)); - acceptor->open(endpoint.protocol()); - acceptor->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); - acceptor->bind(endpoint); - acceptor->listen(socket_base::max_connections); - - RPCListen(acceptor, context, fUseSSL); - // Cancel outstanding listen-requests for this acceptor when shutting down - StopRequests.connect(signals2::slot( - static_cast(&ip::tcp::acceptor::close), acceptor.get()) - .track(acceptor)); - - fListening = true; - } - } - catch(boost::system::system_error &e) - { - strerr = strprintf(_("An error occurred while setting up the RPC port %u for listening on IPv4: %s"), endpoint.port(), e.what()); - } - - if (!fListening) { - uiInterface.ThreadSafeMessageBox(strerr, _("Error"), CClientUIInterface::OK | CClientUIInterface::MODAL); - StartShutdown(); - return; - } - - vnThreadsRunning[THREAD_RPCLISTENER]--; - while (!fShutdown) - io_service.run_one(); - vnThreadsRunning[THREAD_RPCLISTENER]++; - StopRequests(); + return JSONRPCReply(Value::null, objError, id); } class JSONRequest { public: Value id; - string strMethod; + std::string strMethod; Array params; JSONRequest() { id = Value::null; } @@ -879,7 +419,7 @@ void JSONRequest::parse(const Value& valRequest) throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string"); strMethod = valMethod.get_str(); if (strMethod != "getwork" && strMethod != "getblocktemplate") - printf("ThreadRPCServer method=%s\n", strMethod.c_str()); + printf("RPCServer method=%s\n", strMethod.c_str()); // Parse params Value valParams = find_value(request, "params"); @@ -906,7 +446,7 @@ static Object JSONRPCExecOne(const Value& req) { rpc_result = JSONRPCReplyObj(Value::null, objError, jreq.id); } - catch (std::exception& e) + catch (const std::exception& e) { rpc_result = JSONRPCReplyObj(Value::null, JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id); @@ -915,80 +455,85 @@ static Object JSONRPCExecOne(const Value& req) return rpc_result; } -static string JSONRPCExecBatch(const Array& vReq) +static std::string JSONRPCExecBatch(const Array& vReq) { Array ret; - for (unsigned int reqIdx = 0; reqIdx < vReq.size(); reqIdx++) - ret.push_back(JSONRPCExecOne(vReq[reqIdx])); + for (const auto & reqIdx : vReq) + ret.push_back(JSONRPCExecOne(reqIdx)); return write_string(Value(ret), false) + "\n"; } static CCriticalSection cs_THREAD_RPCHANDLER; -void ThreadRPCServer3(void* parg) +void StartRPCServer() { - // Make this thread recognisable as the RPC handler - RenameThread("bitcoin-rpchand"); - + strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]; + if (mapArgs["-rpcpassword"].empty()) { - LOCK(cs_THREAD_RPCHANDLER); - vnThreadsRunning[THREAD_RPCHANDLER]++; + unsigned char rand_pwd[32]; + RAND_bytes(rand_pwd, 32); + std::string strWhatAmI = "To use novacoind"; + if (mapArgs.count("-server")) + strWhatAmI = strprintf(_("To use the %s option"), "\"-server\""); + else if (mapArgs.count("-daemon")) + strWhatAmI = strprintf(_("To use the %s option"), "\"-daemon\""); + uiInterface.ThreadSafeMessageBox(strprintf( + _("%s, you must set a rpcpassword in the configuration file:\n %s\n" + "It is recommended you use the following random password:\n" + "rpcuser=novacoinrpc\n" + "rpcpassword=%s\n" + "(you do not need to remember this password)\n" + "If the file does not exist, create it with owner-readable-only file permissions.\n"), + strWhatAmI.c_str(), + GetConfigFile().string().c_str(), + EncodeBase58(&rand_pwd[0],&rand_pwd[0]+32).c_str()), + _("Error"), CClientUIInterface::OK | CClientUIInterface::MODAL); + StartShutdown(); + return; } - AcceptedConnection *conn = (AcceptedConnection *) parg; - bool fRun = true; - loop { - if (fShutdown || !fRun) - { - conn->close(); - delete conn; - { - LOCK(cs_THREAD_RPCHANDLER); - --vnThreadsRunning[THREAD_RPCHANDLER]; - } - return; - } - map mapHeaders; - string strRequest; + std::string host = GetArg("-rpchost", "127.0.0.1"); + int port = GetArg("-rpcport", GetDefaultRPCPort()); + + g_server = std::make_unique(port, host); + + LOCK(cs_THREAD_RPCHANDLER); - ReadHTTP(conn->stream(), mapHeaders, strRequest); + g_server->setOnConnectionCallback([](const ix::HttpRequestPtr& request, const std::shared_ptr& connectionState) -> ix::HttpResponsePtr { - // Check authorization - if (mapHeaders.count("authorization") == 0) + ix::WebSocketHttpHeaders headers; + headers["Server"] = std::string("novacoin-json-rpc/") + FormatFullVersion(); + headers["WWW-Authenticate"] = R"(Basic realm="jsonrpc")"; + + if (!HTTPAuthorized(request->headers)) { - conn->stream() << HTTPReply(HTTP_UNAUTHORIZED, "", false) << std::flush; - break; + printf("ThreadRPCServer incorrect password attempt from %s\n", connectionState->getRemoteIp().c_str()); + connectionState->setTerminated(); + return std::make_shared(HTTP_UNAUTHORIZED, "Unauthorized", ix::HttpErrorCode::Ok, headers, "Not authorized"); } - if (!HTTPAuthorized(mapHeaders)) - { - printf("ThreadRPCServer incorrect password attempt from %s\n", conn->peer_address_to_string().c_str()); - /* Deter brute-forcing short passwords. - If this results in a DOS the user really - shouldn't have their RPC port exposed.*/ - if (mapArgs["-rpcpassword"].size() < 20) - Sleep(250); - - conn->stream() << HTTPReply(HTTP_UNAUTHORIZED, "", false) << std::flush; - break; + + if (request->method != "POST") { + connectionState->setTerminated(); + return std::make_shared(HTTP_BAD_REQUEST, "Bad request", ix::HttpErrorCode::Ok, headers, "Bad request"); } - if (mapHeaders["connection"] == "close") - fRun = false; JSONRequest jreq; + try { // Parse request Value valRequest; - if (!read_string(strRequest, valRequest)) - throw JSONRPCError(RPC_PARSE_ERROR, "Parse error"); + if (!read_string(request->body, valRequest)) + throw JSONRPCError(RPC_PARSE_ERROR, "Parse error"); - string strReply; + std::string strReply; // singleton request if (valRequest.type() == obj_type) { jreq.parse(valRequest); + // Execute request Value result = tableRPC.execute(jreq.strMethod, jreq.params); // Send reply @@ -996,32 +541,47 @@ void ThreadRPCServer3(void* parg) // array of requests } else if (valRequest.type() == array_type) + // Execute batch of requests strReply = JSONRPCExecBatch(valRequest.get_array()); else throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error"); - conn->stream() << HTTPReply(HTTP_OK, strReply, fRun) << std::flush; + // Send reply to client + return std::make_shared(200, "OK", ix::HttpErrorCode::Ok, headers, strReply); + } - catch (Object& objError) + catch(Object& objError) { - ErrorReply(conn->stream(), objError, jreq.id); - break; + return std::make_shared(HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error", ix::HttpErrorCode::Ok, headers, ErrorReply(objError, jreq.id)); } - catch (std::exception& e) + catch(const std::exception& e) { - ErrorReply(conn->stream(), JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id); - break; + return std::make_shared(HTTP_INTERNAL_SERVER_ERROR, "Internal Server Error", ix::HttpErrorCode::Ok, headers, ErrorReply(JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id)); } - } + }); - delete conn; - { - LOCK(cs_THREAD_RPCHANDLER); - vnThreadsRunning[THREAD_RPCHANDLER]--; + std::pair result = g_server->listen(); + if (!result.first) { + 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()); + uiInterface.ThreadSafeMessageBox(strerr, _("Error"), CClientUIInterface::OK | CClientUIInterface::MODAL); + return StartShutdown(); } + + // Run listening thread + g_server->start(); + + // We're listening now + vnThreadsRunning[THREAD_RPCLISTENER]++; +} + +void StopRPCServer() +{ + LOCK(cs_THREAD_RPCHANDLER); + if (g_server) g_server->stop(); + vnThreadsRunning[THREAD_RPCLISTENER]--; } -json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_spirit::Array ¶ms) const + json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_spirit::Array ¶ms) const { // Find method const CRPCCommand *pcmd = tableRPC[strMethod]; @@ -1029,10 +589,10 @@ json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_s throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found"); // Observe safe mode - string strWarning = GetWarnings("rpc"); - if (strWarning != "" && !GetBoolArg("-disablesafemode") && + std::string strWarning = GetWarnings("rpc"); + if (!strWarning.empty() && !GetBoolArg("-disablesafemode") && !pcmd->okSafeMode) - throw JSONRPCError(RPC_FORBIDDEN_BY_SAFE_MODE, string("Safe mode: ") + strWarning); + throw JSONRPCError(RPC_FORBIDDEN_BY_SAFE_MODE, std::string("Safe mode: ") + strWarning); try { @@ -1048,60 +608,71 @@ json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_s } return result; } - catch (std::exception& e) + catch (const std::exception& e) { throw JSONRPCError(RPC_MISC_ERROR, e.what()); } } +std::vector CRPCTable::listCommands() const +{ + std::vector commandList; + for (const auto& i : mapCommands) commandList.emplace_back(i.first); + return commandList; +} -Object CallRPC(const string& strMethod, const Array& params) +Object CallRPC(const std::string& strMethod, const Array& params) { - if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "") - throw runtime_error(strprintf( + if (mapArgs["-rpcuser"].empty() && mapArgs["-rpcpassword"].empty()) + throw std::runtime_error(strprintf( _("You must set rpcpassword= in the configuration file:\n%s\n" "If the file does not exist, create it with owner-readable-only file permissions."), GetConfigFile().string().c_str())); - // Connect to localhost - bool fUseSSL = GetBoolArg("-rpcssl"); - asio::io_service io_service; - ssl::context context(io_service, ssl::context::sslv23); - context.set_options(ssl::context::no_sslv2); - asio::ssl::stream sslStream(io_service, context); - SSLIOStreamDevice d(sslStream, fUseSSL); - iostreams::stream< SSLIOStreamDevice > stream(d); - if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", itostr(GetDefaultRPCPort())))) - throw runtime_error("couldn't connect to server"); + // Init net subsystem + ix::initNetSystem(); + + // Create HTTP client + ix::HttpClient httpClient; + ix::HttpRequestArgsPtr args = httpClient.createRequest(); // HTTP basic authentication - string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]); - map mapRequestHeaders; - mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64; + ix::WebSocketHttpHeaders mapRequestHeaders; + std::string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]); + mapRequestHeaders["Authorization"] = std::string("Basic ") + strUserPass64; + args->extraHeaders = mapRequestHeaders; + + // Timeouts + args->connectTimeout = GetArgInt("-rpc_connecttimeout", 30000); + args->transferTimeout = GetArgInt("-rpc_transfertimeout", 30000); + + bool fUseSSL = GetBoolArg("-rpcssl"); + std::string url = std::string(fUseSSL ? "https://" : "http://") + GetArg("-rpcconnect", "127.0.0.1") + ":" + GetArg("-rpcport", itostr(GetDefaultRPCPort())); // Send request - string strRequest = JSONRPCRequest(strMethod, params, 1); - string strPost = HTTPPost(strRequest, mapRequestHeaders); - stream << strPost << std::flush; + std::string strRequest = JSONRPCRequest(strMethod, params, GetRandInt(INT32_MAX)); + auto out = httpClient.post(url, strRequest, args); + + // Process reply + int nStatus = out->statusCode; + std::string strReply = out->body; + ix::WebSocketHttpHeaders mapHeaders = out->headers; // Receive reply - map mapHeaders; - string strReply; - int nStatus = ReadHTTP(stream, mapHeaders, strReply); if (nStatus == HTTP_UNAUTHORIZED) - throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)"); - else if (nStatus >= 400 && nStatus != HTTP_BAD_REQUEST && nStatus != HTTP_NOT_FOUND && nStatus != HTTP_INTERNAL_SERVER_ERROR) - throw runtime_error(strprintf("server returned HTTP error %d", nStatus)); + throw std::runtime_error("incorrect rpcuser or rpcpassword (authorization failed)"); + else if (nStatus >= HTTP_BAD_REQUEST && nStatus != HTTP_BAD_REQUEST && nStatus != HTTP_NOT_FOUND && nStatus != HTTP_INTERNAL_SERVER_ERROR) + throw std::runtime_error(strprintf("server returned HTTP error %d", nStatus)); else if (strReply.empty()) - throw runtime_error("no response from server"); + throw std::runtime_error("no response from server"); // Parse reply Value valReply; if (!read_string(strReply, valReply)) - throw runtime_error("couldn't parse reply from server"); + throw std::runtime_error("couldn't parse reply from server"); const Object& reply = valReply.get_obj(); if (reply.empty()) - throw runtime_error("expected reply to have result, error and id properties"); + throw std::runtime_error("expected reply to have result, error and id properties"); return reply; } @@ -1118,9 +689,9 @@ void ConvertTo(Value& value, bool fAllowNull=false) { // reinterpret string as unquoted json value Value value2; - string strJSON = value.get_str(); + std::string strJSON = value.get_str(); if (!read_string(strJSON, value2)) - throw runtime_error(string("Error parsing JSON:")+strJSON); + throw std::runtime_error(std::string("Error parsing JSON:")+strJSON); ConvertTo(value2, fAllowNull); value = value2; } @@ -1134,62 +705,80 @@ void ConvertTo(Value& value, bool fAllowNull=false) Array RPCConvertValues(const std::string &strMethod, const std::vector &strParams) { Array params; - BOOST_FOREACH(const std::string ¶m, strParams) + for(const auto ¶m : strParams) params.push_back(param); - int n = params.size(); + size_t n = params.size(); // // Special case non-string parameter types // if (strMethod == "stop" && n > 0) ConvertTo(params[0]); - if (strMethod == "setgenerate" && n > 0) ConvertTo(params[0]); - if (strMethod == "setgenerate" && n > 1) ConvertTo(params[1]); + if (strMethod == "getaddednodeinfo" && n > 0) ConvertTo(params[0]); if (strMethod == "sendtoaddress" && n > 1) ConvertTo(params[1]); + if (strMethod == "mergecoins" && n > 0) ConvertTo(params[0]); + if (strMethod == "mergecoins" && n > 1) ConvertTo(params[1]); + if (strMethod == "mergecoins" && n > 2) ConvertTo(params[2]); if (strMethod == "settxfee" && n > 0) ConvertTo(params[0]); - if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo(params[1]); - if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo(params[1]); - if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo(params[0]); + if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo(params[1]); + if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo(params[1]); + if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo(params[0]); if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo(params[1]); - if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo(params[0]); + if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo(params[0]); if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo(params[1]); - if (strMethod == "getbalance" && n > 1) ConvertTo(params[1]); + if (strMethod == "getbalance" && n > 1) ConvertTo(params[1]); if (strMethod == "getblock" && n > 1) ConvertTo(params[1]); - if (strMethod == "getblockbynumber" && n > 0) ConvertTo(params[0]); + if (strMethod == "getblockbynumber" && n > 0) ConvertTo(params[0]); + if (strMethod == "dumpblockbynumber" && n > 0) ConvertTo(params[0]); if (strMethod == "getblockbynumber" && n > 1) ConvertTo(params[1]); - if (strMethod == "getblockhash" && n > 0) ConvertTo(params[0]); + if (strMethod == "getblockhash" && n > 0) ConvertTo(params[0]); if (strMethod == "move" && n > 2) ConvertTo(params[2]); - if (strMethod == "move" && n > 3) ConvertTo(params[3]); + if (strMethod == "move" && n > 3) ConvertTo(params[3]); if (strMethod == "sendfrom" && n > 2) ConvertTo(params[2]); - if (strMethod == "sendfrom" && n > 3) ConvertTo(params[3]); - if (strMethod == "listtransactions" && n > 1) ConvertTo(params[1]); - if (strMethod == "listtransactions" && n > 2) ConvertTo(params[2]); - if (strMethod == "listaccounts" && n > 0) ConvertTo(params[0]); - if (strMethod == "walletpassphrase" && n > 1) ConvertTo(params[1]); + if (strMethod == "sendfrom" && n > 3) ConvertTo(params[3]); + if (strMethod == "listtransactions" && n > 1) ConvertTo(params[1]); + if (strMethod == "listtransactions" && n > 2) ConvertTo(params[2]); + if (strMethod == "listaccounts" && n > 0) ConvertTo(params[0]); + if (strMethod == "walletpassphrase" && n > 1) ConvertTo(params[1]); if (strMethod == "walletpassphrase" && n > 2) ConvertTo(params[2]); if (strMethod == "getblocktemplate" && n > 0) ConvertTo(params[0]); - if (strMethod == "listsinceblock" && n > 1) ConvertTo(params[1]); + if (strMethod == "listsinceblock" && n > 1) ConvertTo(params[1]); + + if (strMethod == "scaninput" && n > 0) ConvertTo(params[0]); + + if (strMethod == "sendalert" && n > 2) ConvertTo(params[2]); + if (strMethod == "sendalert" && n > 3) ConvertTo(params[3]); + if (strMethod == "sendalert" && n > 4) ConvertTo(params[4]); + if (strMethod == "sendalert" && n > 5) ConvertTo(params[5]); + if (strMethod == "sendalert" && n > 6) ConvertTo(params[6]); + if (strMethod == "sendmany" && n > 1) ConvertTo(params[1]); - if (strMethod == "sendmany" && n > 2) ConvertTo(params[2]); - if (strMethod == "reservebalance" && n > 0) ConvertTo(params[0]); - if (strMethod == "reservebalance" && n > 1) ConvertTo(params[1]); - if (strMethod == "addmultisigaddress" && n > 0) ConvertTo(params[0]); + if (strMethod == "sendmany" && n > 2) ConvertTo(params[2]); + if (strMethod == "reservebalance" && n > 0) ConvertTo(params[0]); + if (strMethod == "reservebalance" && n > 1) ConvertTo(params[1]); + if (strMethod == "addmultisigaddress" && n > 0) ConvertTo(params[0]); if (strMethod == "addmultisigaddress" && n > 1) ConvertTo(params[1]); - if (strMethod == "listunspent" && n > 0) ConvertTo(params[0]); - if (strMethod == "listunspent" && n > 1) ConvertTo(params[1]); + if (strMethod == "listunspent" && n > 0) ConvertTo(params[0]); + if (strMethod == "listunspent" && n > 1) ConvertTo(params[1]); if (strMethod == "listunspent" && n > 2) ConvertTo(params[2]); - if (strMethod == "getrawtransaction" && n > 1) ConvertTo(params[1]); + if (strMethod == "getrawtransaction" && n > 1) ConvertTo(params[1]); if (strMethod == "createrawtransaction" && n > 0) ConvertTo(params[0]); if (strMethod == "createrawtransaction" && n > 1) ConvertTo(params[1]); + if (strMethod == "createmultisig" && n > 0) ConvertTo(params[0]); + if (strMethod == "createmultisig" && n > 1) ConvertTo(params[1]); if (strMethod == "signrawtransaction" && n > 1) ConvertTo(params[1], true); if (strMethod == "signrawtransaction" && n > 2) ConvertTo(params[2], true); + if (strMethod == "keypoolrefill" && n > 0) ConvertTo(params[0]); + if (strMethod == "keypoolreset" && n > 0) ConvertTo(params[0]); + if (strMethod == "importaddress" && n > 2) ConvertTo(params[2]); + if (strMethod == "importprivkey" && n > 2) ConvertTo(params[2]); return params; } int CommandLineRPC(int argc, char *argv[]) { - string strPrint; + std::string strPrint; int nRet = 0; try { @@ -1202,8 +791,8 @@ int CommandLineRPC(int argc, char *argv[]) // Method if (argc < 2) - throw runtime_error("too few parameters"); - string strMethod = argv[1]; + throw std::runtime_error("too few parameters"); + std::string strMethod = argv[1]; // Parameters default to strings std::vector strParams(&argv[2], &argv[argc]); @@ -1227,24 +816,24 @@ int CommandLineRPC(int argc, char *argv[]) { // Result if (result.type() == null_type) - strPrint = ""; + strPrint.clear(); else if (result.type() == str_type) strPrint = result.get_str(); else strPrint = write_string(result, true); } } - catch (std::exception& e) + catch (const std::exception& e) { - strPrint = string("error: ") + e.what(); + strPrint = std::string("error: ") + e.what(); nRet = 87; } catch (...) { - PrintException(NULL, "CommandLineRPC()"); + PrintException(nullptr, "CommandLineRPC()"); } - if (strPrint != "") + if (!strPrint.empty()) { fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str()); } @@ -1252,39 +841,4 @@ int CommandLineRPC(int argc, char *argv[]) } - - -#ifdef TEST -int main(int argc, char *argv[]) -{ -#ifdef _MSC_VER - // Turn off Microsoft heap dump noise - _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); - _CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0)); -#endif - setbuf(stdin, NULL); - setbuf(stdout, NULL); - setbuf(stderr, NULL); - - try - { - if (argc >= 2 && string(argv[1]) == "-server") - { - printf("server ready\n"); - ThreadRPCServer(NULL); - } - else - { - return CommandLineRPC(argc, argv); - } - } - catch (std::exception& e) { - PrintException(&e, "main()"); - } catch (...) { - PrintException(NULL, "main()"); - } - return 0; -} -#endif - const CRPCTable tableRPC;