// Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin developers // Copyright (c) 2014 Joey Krug and Jack Peterson // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "rpcserver.h" #include "base58.h" #include "init.h" #include #include "main.h" #include "ui_interface.h" #include "util.h" #ifdef ENABLE_WALLET #include "wallet.h" #endif #ifdef _WIN32 #include #include #endif #include "unistd.h" #include #include #include #include #include #include #include #include #include #include #include "json/json_spirit_writer_template.h" using namespace std; using namespace boost; using namespace boost::asio; using namespace json_spirit; static std::string strRPCUserColonPass; // These are created by StartRPCThreads, destroyed in StopRPCThreads static asio::io_service* rpc_io_service = NULL; static map > deadlineTimers; static ssl::context* rpc_ssl_context = NULL; static boost::thread_group* rpc_worker_group = NULL; static boost::asio::io_service::work *rpc_dummy_work = NULL; void RPCTypeCheck(const Array& params, const list& typesExpected, bool fAllowNull) { unsigned int i = 0; BOOST_FOREACH(Value_type t, typesExpected) { if (params.size() <= i) break; const Value& v = params[i]; if (!((v.type() == t) || (fAllowNull && (v.type() == null_type)))) { string err = strprintf("Expected type %s, got %s", Value_type_name[t], Value_type_name[v.type()]); throw JSONRPCError(RPC_TYPE_ERROR, err); } i++; } } void RPCTypeCheck(const Object& o, const map& typesExpected, bool fAllowNull) { BOOST_FOREACH(const PAIRTYPE(string, Value_type)& t, typesExpected) { const Value& v = find_value(o, t.first); if (!fAllowNull && v.type() == null_type) throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing %s", t.first)); if (!((v.type() == t.second) || (fAllowNull && (v.type() == null_type)))) { string err = strprintf("Expected type %s for %s, got %s", Value_type_name[t.second], t.first, Value_type_name[v.type()]); throw JSONRPCError(RPC_TYPE_ERROR, err); } } } int64_t AmountFromValue(const Value& value) { double dAmount = value.get_real(); if (dAmount <= 0.0 || dAmount > 21000000.0) throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount"); int64_t nAmount = roundint64(dAmount * COIN); if (!MoneyRange(nAmount)) throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount"); return nAmount; } Value ValueFromAmount(int64_t amount) { return (double)amount / (double)COIN; } std::string HexBits(unsigned int nBits) { union { int32_t nBits; char cBits[4]; } uBits; uBits.nBits = htonl((int32_t)nBits); return HexStr(BEGIN(uBits.cBits), END(uBits.cBits)); } uint256 ParseHashV(const Value& v, string strName) { 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, string strKey) { return ParseHashV(find_value(o, strKey), strKey); } vector ParseHexV(const Value& v, string strName) { 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); } vector ParseHexO(const Object& o, string strKey) { return ParseHexV(find_value(o, strKey), strKey); } /// /// Note: This interface may still be subject to change. /// string CRPCTable::help(string strCommand) const { string strRet; set setDone; for (map::const_iterator mi = mapCommands.begin(); mi != mapCommands.end(); ++mi) { const CRPCCommand *pcmd = mi->second; string strMethod = mi->first; // We already filter duplicates, but these deprecated screw up the sort order if (strMethod.find("label") != string::npos) continue; if (strCommand != "" && strMethod != strCommand) continue; #ifdef ENABLE_WALLET if (pcmd->reqWallet && !pwalletMain) continue; #endif try { Array params; rpcfn_type pfn = pcmd->actor; if (setDone.insert(pfn).second) (*pfn)(params, true); } catch (std::exception& e) { // Help text is returned in an exception string strHelp = string(e.what()); if (strCommand == "") if (strHelp.find('\n') != string::npos) strHelp = strHelp.substr(0, strHelp.find('\n')); strRet += strHelp + "\n"; } } if (strRet == "") strRet = strprintf("help: unknown command: %s\n", strCommand); strRet = strRet.substr(0,strRet.size()-1); return strRet; } Value help(const Array& params, bool fHelp) { if (fHelp || params.size() > 1) throw runtime_error( "help ( \"command\" )\n" "\nList all commands, or get help for a specified command.\n" "\nArguments:\n" "1. \"command\" (string, optional) The command to get help on\n" "\nResult:\n" "\"text\" (string) The help text\n" ); string strCommand; if (params.size() > 0) strCommand = params[0].get_str(); return tableRPC.help(strCommand); } Value claimtx(const Array& params, bool fHelp) { if (fHelp || params.size() < 1) throw runtime_error( "claimtx \"bitcoin address\"\n" "\nArguments:\n" "1. \"address\" (string, required) The bitcoin address you want to use to claim your coins\n" "\nResult :\n" "{\n" " \"rawtx\" : \"hash\", (string) execute signrawtransaction with your bitcoin client, then type sendrawtransaction and the result that bitcoinqt gave you \n" " \"autoSuccess\" : true or false, (boolean) True if we successfully automate this claim process if you have bitcoinqt already installed on your computer\n" "}\n" "\"data\" (string) A raw transaction ready to be signed'.\n" "\nExamples:\n" + HelpExampleCli("claimtx", "\"1F1tAaz5x1HUXrCNLbtMDqcw6o5GNn4xqX\"") + HelpExampleRpc("claimtx", "\"12DL1vZrJVphm8PXcV9GNCtBYo3PCMn2No\"") ); CTransaction prevTx; CBlock block; bool defined = false; std::string btcHash160Str; std::string address; std::string scriptPubKeyString; uint256 prevTxid; uint64_t txAmt = 0; if (params.size() > 0) { address = params[0].get_str(); } std::ifstream snapfile; #ifdef _WIN32 char username[UNLEN+1]; DWORD username_len = UNLEN+1; GetUserName(username, &username_len); std::string userString(username); std::string str(getlogin()); std::string SNAPSHOT_FILE = "C:\Users\\" + userString + "\Appdata\Roaming\Sidecoin\balances\snapshotToImport.txt"; #elif __APPLE__ std::string str(getlogin()); std::string SNAPSHOT_FILE = "/users/" + str + "/Library/Application Support/Sidecoin/balances/snapshotToImport.txt"; #elif __linux std::string str(getlogin()); std::string SNAPSHOT_FILE = "/home/" + str + "/.sidecoin/balances/snapshotToImport.txt"; #elif __unix std::string str(getlogin()); std::string SNAPSHOT_FILE = "/home/" + str + "/.sidecoin/balances/snapshotToImport.txt"; #endif //printf("path: %s\n", SNAPSHOT_FILE.c_str()); snapfile.open(SNAPSHOT_FILE.c_str()); if (snapfile.good()) { while (!snapfile.eof()) { char buffer[1024]; const char* btcBalance = 0; const char* btcHash160 = 0; const char* btcAddress = 0; snapfile.getline(buffer, 1024); btcBalance = strtok(buffer, "\t"); if (btcBalance) { btcHash160 = strtok(0, "\t"); if (btcHash160) { btcAddress = strtok(0, "\n"); if(btcAddress==address) { btcHash160Str = btcHash160; std::cout << "hash160:"+btcHash160Str << std::endl; break; } } } } } CScript scriptPubKey = CScript() << OP_DUP << OP_HASH160 << ParseHex(btcHash160Str) << OP_EQUALVERIFY << OP_CHECKSIG; //uint256 hash = Params().GenesisBlock().GetHash(); uint256 hash = uint256("00000000e5cabdb3fa5ae55a45a5473e4f05eefb48b58fc917c43b4ae57c9116"); // fetch genesis block CBlockIndex* pblockindex = mapBlockIndex[hash]; std::cout << pblockindex->ToString() << std::endl; // read the genesis block into block bool blockRead = ReadBlockFromDisk(block, pblockindex->GetBlockPos()); if(blockRead) { std::cout << "read" << std::endl; } else { std::cout << "not_read" << std::endl; } // Find UTXO matching user's Bitcoin hash-160 pubkey for (unsigned i = 0, len = block.vtx.size(); i < len; ++i) { printf("%s\n", block.vtx[i].vout[0].scriptPubKey.ToString().c_str()); std::cout << scriptPubKey.ToString() << std::endl; if (block.vtx[i].vout[0].scriptPubKey == scriptPubKey) { prevTx = block.vtx[i]; txAmt = (block.vtx[i].vout[0].nValue - 100000); scriptPubKeyString = HexStr(scriptPubKey.begin(), scriptPubKey.end(), true).c_str(); std::string::iterator end_pos = std::remove(scriptPubKeyString.begin(), scriptPubKeyString.end(), ' '); scriptPubKeyString.erase(end_pos, str.end()); // prev out is 0 // put the previous output hash as the txid in our input prevTxid = prevTx.GetHash(); std::cout << "true" << std::endl; defined = true; break; } } if(defined) { CTransaction rawTx; uint256 txid = prevTxid; int nOutput = 0; CTxIn in(COutPoint(txid, nOutput)); rawTx.vin.push_back(in); std::cout << address << std::endl; CScript scriptPubKey; Array addressParams; addressParams.push_back(""); std::string addString = getnewaddress(addressParams,false).get_str(); CSidecoinAddress addressToSend; addressToSend = CSidecoinAddress(addString); if (!addressToSend.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Sidecoin address: ")); scriptPubKey.SetDestination(addressToSend.Get()); int64_t nAmount = txAmt; CTxOut out(nAmount, scriptPubKey); rawTx.vout.push_back(out); CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); ss << rawTx; return "Open your bitcoin-qt client's rpc console (settings -> console) \n and paste the following and hit enter, then copy the data on the otherside of the \"hex:\" transaction text up to the comma and come back to sidecoin \n and type sendrawtransaction then paste your raw transaction, hit enter \n after that you'll have claimed your sidecoins! \n signrawtransaction \n '" + HexStr(ss.begin(), ss.end()) + "' '[{\"txid\":\"" + prevTxid.ToString() +"\",\"vout\":0,\"scriptPubKey\":\"" + scriptPubKeyString + "\"}]'"; //string txAmtString = boost::lexical_cast(txAmt); //return "[{\"txid\":\""+prevTxid.ToString()+"\",\"vout\":0}]" + " {" + address +":" + txAmtString + "}"; } else { throw runtime_error( "Couldn't find your address in the snapshot" "\nExamples:\n" + HelpExampleCli("claimtx", "\"1F1tAaz5x1HUXrCNLbtMDqcw6o5GNn4xqX\"") + HelpExampleRpc("claimtx", "\"12DL1vZrJVphm8PXcV9GNCtBYo3PCMn2No\"") ); return ""; } } Value stop(const Array& params, bool fHelp) { // Accept the deprecated and ignored 'detach' boolean argument if (fHelp || params.size() > 1) throw runtime_error( "stop\n" "\nStop Sidecoin server."); // Shutdown will take long enough that the response should get back StartShutdown(); return "Sidecoin server stopping"; } // // Call Table // static const CRPCCommand vRPCCommands[] = { // name actor (function) okSafeMode threadSafe reqWallet // ------------------------ ----------------------- ---------- ---------- --------- { "help", &help, true, true, false }, { "stop", &stop, true, true, false }, { "getblockcount", &getblockcount, true, false, false }, { "getbestblockhash", &getbestblockhash, true, false, false }, { "getconnectioncount", &getconnectioncount, true, false, false }, { "getpeerinfo", &getpeerinfo, true, false, false }, { "ping", &ping, true, false, false }, { "addnode", &addnode, true, true, false }, { "getaddednodeinfo", &getaddednodeinfo, true, true, false }, { "getnettotals", &getnettotals, true, true, false }, { "getdifficulty", &getdifficulty, true, false, false }, { "getinfo", &getinfo, true, false, false }, { "getrawmempool", &getrawmempool, true, false, false }, { "getblock", &getblock, false, false, false }, { "getblockhash", &getblockhash, false, false, false }, { "getrawtransaction", &getrawtransaction, false, false, false }, { "createrawtransaction", &createrawtransaction, false, false, false }, { "claimtx", &claimtx, false, false, false }, { "decoderawtransaction", &decoderawtransaction, false, false, false }, { "decodescript", &decodescript, false, false, false }, { "signrawtransaction", &signrawtransaction, false, false, false }, { "sendrawtransaction", &sendrawtransaction, false, false, false }, { "gettxoutsetinfo", &gettxoutsetinfo, true, false, false }, { "gettxout", &gettxout, true, false, false }, { "verifychain", &verifychain, true, false, false }, /* Mining */ { "getnetworkhashps", &getnetworkhashps, true, false, false }, { "getmininginfo", &getmininginfo, true, false, false }, { "getblocktemplate", &getblocktemplate, true, false, false }, { "submitblock", &submitblock, false, false, false }, { "validateaddress", &validateaddress, true, false, false }, { "createmultisig", &createmultisig, true, true , false }, { "verifymessage", &verifymessage, false, false, false }, #ifdef ENABLE_WALLET /* Wallet */ { "getnewaddress", &getnewaddress, true, false, true }, { "getaccountaddress", &getaccountaddress, true, false, true }, { "getrawchangeaddress", &getrawchangeaddress, true, false, true }, { "setaccount", &setaccount, true, false, true }, { "getaccount", &getaccount, false, false, true }, { "getaddressesbyaccount", &getaddressesbyaccount, true, false, true }, { "sendtoaddress", &sendtoaddress, false, false, true }, { "getreceivedbyaddress", &getreceivedbyaddress, false, false, true }, { "getreceivedbyaccount", &getreceivedbyaccount, false, false, true }, { "listreceivedbyaddress", &listreceivedbyaddress, false, false, true }, { "listreceivedbyaccount", &listreceivedbyaccount, false, false, true }, { "backupwallet", &backupwallet, true, false, true }, { "keypoolrefill", &keypoolrefill, true, false, true }, { "walletpassphrase", &walletpassphrase, true, false, true }, { "walletpassphrasechange", &walletpassphrasechange, false, false, true }, { "walletlock", &walletlock, true, false, true }, { "encryptwallet", &encryptwallet, false, false, true }, { "getbalance", &getbalance, false, false, true }, { "getunconfirmedbalance", &getunconfirmedbalance, false, false, true }, { "move", &movecmd, false, false, true }, { "sendfrom", &sendfrom, false, false, true }, { "sendmany", &sendmany, false, false, true }, { "addmultisigaddress", &addmultisigaddress, false, false, true }, { "gettransaction", &gettransaction, false, false, true }, { "listtransactions", &listtransactions, false, false, true }, { "listaddressgroupings", &listaddressgroupings, false, false, true }, { "signmessage", &signmessage, false, false, true }, { "listaccounts", &listaccounts, false, false, true }, { "listsinceblock", &listsinceblock, false, false, true }, { "dumpprivkey", &dumpprivkey, true, false, true }, { "dumpwallet", &dumpwallet, true, false, true }, { "importprivkey", &importprivkey, false, false, true }, { "importwallet", &importwallet, false, false, true }, { "listunspent", &listunspent, false, false, true }, { "lockunspent", &lockunspent, false, false, true }, { "listlockunspent", &listlockunspent, false, false, true }, { "settxfee", &settxfee, false, false, true }, /* Wallet-enabled mining */ { "getgenerate", &getgenerate, true, false, false }, { "setgenerate", &setgenerate, true, true, false }, { "gethashespersec", &gethashespersec, true, false, false }, { "getwork", &getwork, true, false, true }, #endif // ENABLE_WALLET }; CRPCTable::CRPCTable() { unsigned int vcidx; for (vcidx = 0; vcidx < (sizeof(vRPCCommands) / sizeof(vRPCCommands[0])); vcidx++) { const CRPCCommand *pcmd; pcmd = &vRPCCommands[vcidx]; mapCommands[pcmd->name] = pcmd; } } const CRPCCommand *CRPCTable::operator[](string name) const { map::const_iterator it = mapCommands.find(name); if (it == mapCommands.end()) return NULL; return (*it).second; } bool HTTPAuthorized(map& mapHeaders) { 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 TimingResistantEqual(strUserPass, strRPCUserColonPass); } void ErrorReply(std::ostream& stream, 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; } 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 ServiceConnection(AcceptedConnection *conn); // 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) { // 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; } else { ServiceConnection(conn); conn->close(); delete conn; } } void StartRPCThreads() { strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]; if (((mapArgs["-rpcpassword"] == "") || (mapArgs["-rpcuser"] == mapArgs["-rpcpassword"])) && Params().RequireRPCPassword()) { unsigned char rand_pwd[32]; RAND_bytes(rand_pwd, 32); string strWhatAmI = "To use sidecoind"; 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=sidecoinrpc\n" "rpcpassword=%s\n" "(you do not need to remember this password)\n" "The username and password MUST NOT be the same.\n" "If the file does not exist, create it with owner-readable-only file permissions.\n" "It is also recommended to set alertnotify so you are notified of problems;\n" "for example: alertnotify=echo %%s | mail -s \"Sidecoin Alert\" admin@foo.com\n"), strWhatAmI, GetConfigFile().string(), EncodeBase58(&rand_pwd[0],&rand_pwd[0]+32)), "", CClientUIInterface::MSG_ERROR); StartShutdown(); return; } assert(rpc_io_service == NULL); rpc_io_service = new asio::io_service(); rpc_ssl_context = new ssl::context(*rpc_io_service, ssl::context::sslv23); const bool fUseSSL = GetBoolArg("-rpcssl", false); if (fUseSSL) { rpc_ssl_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)) rpc_ssl_context->use_certificate_chain_file(pathCertFile.string()); else LogPrintf("ThreadRPCServer ERROR: missing server certificate file %s\n", pathCertFile.string()); filesystem::path pathPKFile(GetArg("-rpcsslprivatekeyfile", "server.pem")); if (!pathPKFile.is_complete()) pathPKFile = filesystem::path(GetDataDir()) / pathPKFile; if (filesystem::exists(pathPKFile)) rpc_ssl_context->use_private_key_file(pathPKFile.string(), ssl::context::pem); else LogPrintf("ThreadRPCServer ERROR: missing server private key file %s\n", pathPKFile.string()); string strCiphers = GetArg("-rpcsslciphers", "TLSv1.2+HIGH:TLSv1+HIGH:!SSLv2:!aNULL:!eNULL:!3DES:@STRENGTH"); SSL_CTX_set_cipher_list(rpc_ssl_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", Params().RPCPort())); boost::system::error_code v6_only_error; boost::shared_ptr acceptor(new ip::tcp::acceptor(*rpc_io_service)); 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, *rpc_ssl_context, fUseSSL); 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(*rpc_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, *rpc_ssl_context, fUseSSL); 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, "", CClientUIInterface::MSG_ERROR); StartShutdown(); return; } rpc_worker_group = new boost::thread_group(); for (int i = 0; i < GetArg("-rpcthreads", 4); i++) rpc_worker_group->create_thread(boost::bind(&asio::io_service::run, rpc_io_service)); } void StartDummyRPCThread() { if(rpc_io_service == NULL) { rpc_io_service = new asio::io_service(); /* Create dummy "work" to keep the thread from exiting when no timeouts active, * see http://www.boost.org/doc/libs/1_51_0/doc/html/boost_asio/reference/io_service.html#boost_asio.reference.io_service.stopping_the_io_service_from_running_out_of_work */ rpc_dummy_work = new asio::io_service::work(*rpc_io_service); rpc_worker_group = new boost::thread_group(); rpc_worker_group->create_thread(boost::bind(&asio::io_service::run, rpc_io_service)); } } void StopRPCThreads() { if (rpc_io_service == NULL) return; deadlineTimers.clear(); rpc_io_service->stop(); if (rpc_worker_group != NULL) rpc_worker_group->join_all(); delete rpc_dummy_work; rpc_dummy_work = NULL; delete rpc_worker_group; rpc_worker_group = NULL; delete rpc_ssl_context; rpc_ssl_context = NULL; delete rpc_io_service; rpc_io_service = NULL; } void RPCRunHandler(const boost::system::error_code& err, boost::function func) { if (!err) func(); } void RPCRunLater(const std::string& name, boost::function func, int64_t nSeconds) { assert(rpc_io_service != NULL); if (deadlineTimers.count(name) == 0) { deadlineTimers.insert(make_pair(name, boost::shared_ptr(new deadline_timer(*rpc_io_service)))); } deadlineTimers[name]->expires_from_now(posix_time::seconds(nSeconds)); deadlineTimers[name]->async_wait(boost::bind(RPCRunHandler, _1, func)); } class JSONRequest { public: Value id; string strMethod; Array params; JSONRequest() { id = Value::null; } void parse(const Value& valRequest); }; void JSONRequest::parse(const Value& valRequest) { // Parse request if (valRequest.type() != obj_type) throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object"); const Object& request = valRequest.get_obj(); // Parse id now so errors from here on will have the id id = find_value(request, "id"); // Parse method Value valMethod = find_value(request, "method"); if (valMethod.type() == null_type) throw JSONRPCError(RPC_INVALID_REQUEST, "Missing method"); if (valMethod.type() != str_type) throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string"); strMethod = valMethod.get_str(); if (strMethod != "getwork" && strMethod != "getblocktemplate") LogPrint("rpc", "ThreadRPCServer method=%s\n", strMethod); // Parse params Value valParams = find_value(request, "params"); if (valParams.type() == array_type) params = valParams.get_array(); else if (valParams.type() == null_type) params = Array(); else throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array"); } static Object JSONRPCExecOne(const Value& req) { Object rpc_result; JSONRequest jreq; try { jreq.parse(req); Value result = tableRPC.execute(jreq.strMethod, jreq.params); rpc_result = JSONRPCReplyObj(result, Value::null, jreq.id); } catch (Object& objError) { rpc_result = JSONRPCReplyObj(Value::null, objError, jreq.id); } catch (std::exception& e) { rpc_result = JSONRPCReplyObj(Value::null, JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id); } return rpc_result; } static string JSONRPCExecBatch(const Array& vReq) { Array ret; for (unsigned int reqIdx = 0; reqIdx < vReq.size(); reqIdx++) ret.push_back(JSONRPCExecOne(vReq[reqIdx])); return write_string(Value(ret), false) + "\n"; } void ServiceConnection(AcceptedConnection *conn) { bool fRun = true; while (fRun) { int nProto = 0; map mapHeaders; string strRequest, strMethod, strURI; // Read HTTP request line if (!ReadHTTPRequestLine(conn->stream(), nProto, strMethod, strURI)) break; // Read HTTP message headers and body ReadHTTPMessage(conn->stream(), mapHeaders, strRequest, nProto); if (strURI != "/") { conn->stream() << HTTPReply(HTTP_NOT_FOUND, "", false) << std::flush; break; } // Check authorization if (mapHeaders.count("authorization") == 0) { conn->stream() << HTTPReply(HTTP_UNAUTHORIZED, "", false) << std::flush; break; } if (!HTTPAuthorized(mapHeaders)) { LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", conn->peer_address_to_string()); /* 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) MilliSleep(250); conn->stream() << HTTPReply(HTTP_UNAUTHORIZED, "", false) << std::flush; break; } 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"); string strReply; // singleton request if (valRequest.type() == obj_type) { jreq.parse(valRequest); Value result = tableRPC.execute(jreq.strMethod, jreq.params); // Send reply strReply = JSONRPCReply(result, Value::null, jreq.id); // array of requests } else if (valRequest.type() == array_type) 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; } catch (Object& objError) { ErrorReply(conn->stream(), objError, jreq.id); break; } catch (std::exception& e) { ErrorReply(conn->stream(), JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id); break; } } } json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_spirit::Array ¶ms) const { // Find method const CRPCCommand *pcmd = tableRPC[strMethod]; if (!pcmd) throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found"); #ifdef ENABLE_WALLET if (pcmd->reqWallet && !pwalletMain) throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found (disabled)"); #endif // Observe safe mode string strWarning = GetWarnings("rpc"); if (strWarning != "" && !GetBoolArg("-disablesafemode", false) && !pcmd->okSafeMode) throw JSONRPCError(RPC_FORBIDDEN_BY_SAFE_MODE, string("Safe mode: ") + strWarning); try { // Execute Value result; { if (pcmd->threadSafe) result = pcmd->actor(params, false); #ifdef ENABLE_WALLET else if (!pwalletMain) { LOCK(cs_main); result = pcmd->actor(params, false); } else { LOCK2(cs_main, pwalletMain->cs_wallet); result = pcmd->actor(params, false); } #else // ENABLE_WALLET else { LOCK(cs_main); result = pcmd->actor(params, false); } #endif // !ENABLE_WALLET } return result; } catch (std::exception& e) { throw JSONRPCError(RPC_MISC_ERROR, e.what()); } } std::string HelpExampleCli(string methodname, string args){ return "> sidecoin-cli " + methodname + " " + args + "\n"; } std::string HelpExampleRpc(string methodname, string args){ return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", " "\"method\": \"" + methodname + "\", \"params\": [" + args + "] }' -H 'content-type: text/plain;' http://127.0.0.1:8332/\n"; } const CRPCTable tableRPC;