diff options
Diffstat (limited to 'src/rpcserver.cpp')
| -rw-r--r-- | src/rpcserver.cpp | 506 |
1 files changed, 259 insertions, 247 deletions
diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp index c5d74afba..287cfb2f1 100644 --- a/src/rpcserver.cpp +++ b/src/rpcserver.cpp @@ -7,11 +7,14 @@ #include "base58.h" #include "init.h" -#include "main.h" +#include "random.h" +#include "sync.h" #include "ui_interface.h" #include "util.h" +#include "utilmoneystr.h" +#include "utilstrencodings.h" #ifdef ENABLE_WALLET -#include "wallet.h" +#include "wallet/wallet.h" #endif #include <boost/algorithm/string.hpp> @@ -23,12 +26,13 @@ #include <boost/iostreams/concepts.hpp> #include <boost/iostreams/stream.hpp> #include <boost/shared_ptr.hpp> +#include <boost/signals2/signal.hpp> #include <boost/thread.hpp> -#include "json/json_spirit_writer_template.h" -using namespace boost; +#include "univalue/univalue.h" + using namespace boost::asio; -using namespace json_spirit; +using namespace RPCServer; using namespace std; static std::string strRPCUserColonPass; @@ -39,7 +43,7 @@ static std::string rpcWarmupStatus("RPC server started"); static CCriticalSection cs_rpcWarmup; //! These are created by StartRPCThreads, destroyed in StopRPCThreads -static asio::io_service* rpc_io_service = NULL; +static boost::asio::io_service* rpc_io_service = NULL; static map<string, boost::shared_ptr<deadline_timer> > deadlineTimers; static ssl::context* rpc_ssl_context = NULL; static boost::thread_group* rpc_worker_group = NULL; @@ -47,71 +51,95 @@ static boost::asio::io_service::work *rpc_dummy_work = NULL; static std::vector<CSubNet> rpc_allow_subnets; //!< List of subnets to allow RPC connections from static std::vector< boost::shared_ptr<ip::tcp::acceptor> > rpc_acceptors; -void RPCTypeCheck(const Array& params, - const list<Value_type>& typesExpected, +static struct CRPCSignals +{ + boost::signals2::signal<void ()> Started; + boost::signals2::signal<void ()> Stopped; + boost::signals2::signal<void (const CRPCCommand&)> PreCommand; + boost::signals2::signal<void (const CRPCCommand&)> PostCommand; +} g_rpcSignals; + +void RPCServer::OnStarted(boost::function<void ()> slot) +{ + g_rpcSignals.Started.connect(slot); +} + +void RPCServer::OnStopped(boost::function<void ()> slot) +{ + g_rpcSignals.Stopped.connect(slot); +} + +void RPCServer::OnPreCommand(boost::function<void (const CRPCCommand&)> slot) +{ + g_rpcSignals.PreCommand.connect(boost::bind(slot, _1)); +} + +void RPCServer::OnPostCommand(boost::function<void (const CRPCCommand&)> slot) +{ + g_rpcSignals.PostCommand.connect(boost::bind(slot, _1)); +} + +void RPCTypeCheck(const UniValue& params, + const list<UniValue::VType>& typesExpected, bool fAllowNull) { unsigned int i = 0; - BOOST_FOREACH(Value_type t, typesExpected) + BOOST_FOREACH(UniValue::VType t, typesExpected) { if (params.size() <= i) break; - const Value& v = params[i]; - if (!((v.type() == t) || (fAllowNull && (v.type() == null_type)))) + const UniValue& v = params[i]; + if (!((v.type() == t) || (fAllowNull && (v.isNull())))) { string err = strprintf("Expected type %s, got %s", - Value_type_name[t], Value_type_name[v.type()]); + uvTypeName(t), uvTypeName(v.type())); throw JSONRPCError(RPC_TYPE_ERROR, err); } i++; } } -void RPCTypeCheck(const Object& o, - const map<string, Value_type>& typesExpected, +void RPCTypeCheckObj(const UniValue& o, + const map<string, UniValue::VType>& typesExpected, bool fAllowNull) { - BOOST_FOREACH(const PAIRTYPE(string, Value_type)& t, typesExpected) + BOOST_FOREACH(const PAIRTYPE(string, UniValue::VType)& t, typesExpected) { - const Value& v = find_value(o, t.first); - if (!fAllowNull && v.type() == null_type) + const UniValue& v = find_value(o, t.first); + if (!fAllowNull && v.isNull()) throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing %s", t.first)); - if (!((v.type() == t.second) || (fAllowNull && (v.type() == null_type)))) + if (!((v.type() == t.second) || (fAllowNull && (v.isNull())))) { string err = strprintf("Expected type %s for %s, got %s", - Value_type_name[t.second], t.first, Value_type_name[v.type()]); + uvTypeName(t.second), t.first, uvTypeName(v.type())); throw JSONRPCError(RPC_TYPE_ERROR, err); } } } -static inline int64_t roundint64(double d) -{ - return (int64_t)(d > 0 ? d + 0.5 : d - 0.5); -} - -CAmount AmountFromValue(const Value& value) +CAmount AmountFromValue(const UniValue& value) { - double dAmount = value.get_real(); - if (dAmount <= 0.0 || dAmount > 21000000.0) + if (!value.isReal() && !value.isNum()) + throw JSONRPCError(RPC_TYPE_ERROR, "Amount is not a number"); + CAmount amount; + if (!ParseMoney(value.getValStr(), amount)) throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount"); - CAmount nAmount = roundint64(dAmount * COIN); - if (!MoneyRange(nAmount)) - throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount"); - return nAmount; + if (!MoneyRange(amount)) + throw JSONRPCError(RPC_TYPE_ERROR, "Amount out of range"); + return amount; } -Value ValueFromAmount(const CAmount& amount) +UniValue ValueFromAmount(const CAmount& amount) { - return (double)amount / (double)COIN; + return UniValue(UniValue::VREAL, FormatMoney(amount)); } -uint256 ParseHashV(const Value& v, string strName) +uint256 ParseHashV(const UniValue& v, string strName) { string strHex; - if (v.type() == str_type) + if (v.isStr()) strHex = v.get_str(); if (!IsHex(strHex)) // Note: IsHex("") is false throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')"); @@ -119,20 +147,20 @@ uint256 ParseHashV(const Value& v, string strName) result.SetHex(strHex); return result; } -uint256 ParseHashO(const Object& o, string strKey) +uint256 ParseHashO(const UniValue& o, string strKey) { return ParseHashV(find_value(o, strKey), strKey); } -vector<unsigned char> ParseHexV(const Value& v, string strName) +vector<unsigned char> ParseHexV(const UniValue& v, string strName) { string strHex; - if (v.type() == str_type) + if (v.isStr()) strHex = v.get_str(); if (!IsHex(strHex)) throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')"); return ParseHex(strHex); } -vector<unsigned char> ParseHexO(const Object& o, string strKey) +vector<unsigned char> ParseHexO(const UniValue& o, string strKey) { return ParseHexV(find_value(o, strKey), strKey); } @@ -142,7 +170,7 @@ vector<unsigned char> ParseHexO(const Object& o, string 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; string category; @@ -162,14 +190,9 @@ string CRPCTable::help(string strCommand) const continue; if ((strCommand != "" || pcmd->category == "hidden") && strMethod != strCommand) continue; -#ifdef ENABLE_WALLET - if (pcmd->reqWallet && !pwalletMain) - continue; -#endif - try { - Array params; + UniValue params; rpcfn_type pfn = pcmd->actor; if (setDone.insert(pfn).second) (*pfn)(params, true); @@ -202,7 +225,7 @@ string CRPCTable::help(string strCommand) const return strRet; } -Value help(const Array& params, bool fHelp) +UniValue help(const UniValue& params, bool fHelp) { if (fHelp || params.size() > 1) throw runtime_error( @@ -222,7 +245,7 @@ Value help(const Array& params, bool fHelp) } -Value stop(const Array& params, bool fHelp) +UniValue stop(const UniValue& params, bool fHelp) { // Accept the deprecated and ignored 'detach' boolean argument if (fHelp || params.size() > 1) @@ -240,113 +263,122 @@ Value stop(const Array& params, bool fHelp) * Call Table */ static const CRPCCommand vRPCCommands[] = -{ // category name actor (function) okSafeMode threadSafe reqWallet - // --------------------- ------------------------ ----------------------- ---------- ---------- --------- +{ // category name actor (function) okSafeMode + // --------------------- ------------------------ ----------------------- ---------- /* Overall control/query calls */ - { "control", "getinfo", &getinfo, true, false, false }, /* uses wallet if enabled */ - { "control", "help", &help, true, true, false }, - { "control", "stop", &stop, true, true, false }, + { "control", "getinfo", &getinfo, true }, /* uses wallet if enabled */ + { "control", "help", &help, true }, + { "control", "stop", &stop, true }, /* P2P networking */ - { "network", "getnetworkinfo", &getnetworkinfo, true, false, false }, - { "network", "addnode", &addnode, true, true, false }, - { "network", "getaddednodeinfo", &getaddednodeinfo, true, true, false }, - { "network", "getconnectioncount", &getconnectioncount, true, false, false }, - { "network", "getnettotals", &getnettotals, true, true, false }, - { "network", "getpeerinfo", &getpeerinfo, true, false, false }, - { "network", "ping", &ping, true, false, false }, + { "network", "getnetworkinfo", &getnetworkinfo, true }, + { "network", "addnode", &addnode, true }, + { "network", "disconnectnode", &disconnectnode, true }, + { "network", "getaddednodeinfo", &getaddednodeinfo, true }, + { "network", "getconnectioncount", &getconnectioncount, true }, + { "network", "getnettotals", &getnettotals, true }, + { "network", "getpeerinfo", &getpeerinfo, true }, + { "network", "ping", &ping, true }, + { "network", "setban", &setban, true }, + { "network", "listbanned", &listbanned, true }, + { "network", "clearbanned", &clearbanned, true }, /* Block chain and UTXO */ - { "blockchain", "getblockchaininfo", &getblockchaininfo, true, false, false }, - { "blockchain", "getbestblockhash", &getbestblockhash, true, false, false }, - { "blockchain", "getblockcount", &getblockcount, true, false, false }, - { "blockchain", "getblock", &getblock, true, false, false }, - { "blockchain", "getblockhash", &getblockhash, true, false, false }, - { "blockchain", "getchaintips", &getchaintips, true, false, false }, - { "blockchain", "getdifficulty", &getdifficulty, true, false, false }, - { "blockchain", "getmempoolinfo", &getmempoolinfo, true, true, false }, - { "blockchain", "getrawmempool", &getrawmempool, true, false, false }, - { "blockchain", "gettxout", &gettxout, true, false, false }, - { "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, true, false, false }, - { "blockchain", "verifychain", &verifychain, true, false, false }, - { "blockchain", "invalidateblock", &invalidateblock, true, true, false }, - { "blockchain", "reconsiderblock", &reconsiderblock, true, true, false }, + { "blockchain", "getblockchaininfo", &getblockchaininfo, true }, + { "blockchain", "getbestblockhash", &getbestblockhash, true }, + { "blockchain", "getblockcount", &getblockcount, true }, + { "blockchain", "getblock", &getblock, true }, + { "blockchain", "getblockhash", &getblockhash, true }, + { "blockchain", "getblockheader", &getblockheader, true }, + { "blockchain", "getchaintips", &getchaintips, true }, + { "blockchain", "getdifficulty", &getdifficulty, true }, + { "blockchain", "getmempoolinfo", &getmempoolinfo, true }, + { "blockchain", "getrawmempool", &getrawmempool, true }, + { "blockchain", "gettxout", &gettxout, true }, + { "blockchain", "gettxoutproof", &gettxoutproof, true }, + { "blockchain", "verifytxoutproof", &verifytxoutproof, true }, + { "blockchain", "gettxoutsetinfo", &gettxoutsetinfo, true }, + { "blockchain", "verifychain", &verifychain, true }, /* Mining */ - { "mining", "getblocktemplate", &getblocktemplate, true, false, false }, - { "mining", "getmininginfo", &getmininginfo, true, false, false }, - { "mining", "getnetworkhashps", &getnetworkhashps, true, false, false }, - { "mining", "prioritisetransaction", &prioritisetransaction, true, false, false }, - { "mining", "submitblock", &submitblock, true, true, false }, + { "mining", "getblocktemplate", &getblocktemplate, true }, + { "mining", "getmininginfo", &getmininginfo, true }, + { "mining", "getnetworkhashps", &getnetworkhashps, true }, + { "mining", "prioritisetransaction", &prioritisetransaction, true }, + { "mining", "submitblock", &submitblock, true }, -#ifdef ENABLE_WALLET /* Coin generation */ - { "generating", "getgenerate", &getgenerate, true, false, false }, - { "generating", "gethashespersec", &gethashespersec, true, false, false }, - { "generating", "setgenerate", &setgenerate, true, true, false }, -#endif + { "generating", "getgenerate", &getgenerate, true }, + { "generating", "setgenerate", &setgenerate, true }, + { "generating", "generate", &generate, true }, /* Raw transactions */ - { "rawtransactions", "createrawtransaction", &createrawtransaction, true, false, false }, - { "rawtransactions", "decoderawtransaction", &decoderawtransaction, true, false, false }, - { "rawtransactions", "decodescript", &decodescript, true, false, false }, - { "rawtransactions", "getrawtransaction", &getrawtransaction, true, false, false }, - { "rawtransactions", "sendrawtransaction", &sendrawtransaction, false, false, false }, - { "rawtransactions", "signrawtransaction", &signrawtransaction, false, false, false }, /* uses wallet if enabled */ + { "rawtransactions", "createrawtransaction", &createrawtransaction, true }, + { "rawtransactions", "decoderawtransaction", &decoderawtransaction, true }, + { "rawtransactions", "decodescript", &decodescript, true }, + { "rawtransactions", "getrawtransaction", &getrawtransaction, true }, + { "rawtransactions", "sendrawtransaction", &sendrawtransaction, false }, + { "rawtransactions", "signrawtransaction", &signrawtransaction, false }, /* uses wallet if enabled */ +#ifdef ENABLE_WALLET + { "rawtransactions", "fundrawtransaction", &fundrawtransaction, false }, +#endif /* Utility functions */ - { "util", "createmultisig", &createmultisig, true, true , false }, - { "util", "validateaddress", &validateaddress, true, false, false }, /* uses wallet if enabled */ - { "util", "verifymessage", &verifymessage, true, false, false }, - { "util", "estimatefee", &estimatefee, true, true, false }, - { "util", "estimatepriority", &estimatepriority, true, true, false }, + { "util", "createmultisig", &createmultisig, true }, + { "util", "validateaddress", &validateaddress, true }, /* uses wallet if enabled */ + { "util", "verifymessage", &verifymessage, true }, + { "util", "estimatefee", &estimatefee, true }, + { "util", "estimatepriority", &estimatepriority, true }, /* Not shown in help */ - { "hidden", "invalidateblock", &invalidateblock, true, true, false }, - { "hidden", "reconsiderblock", &reconsiderblock, true, true, false }, - { "hidden", "setmocktime", &setmocktime, true, false, false }, + { "hidden", "invalidateblock", &invalidateblock, true }, + { "hidden", "reconsiderblock", &reconsiderblock, true }, + { "hidden", "setmocktime", &setmocktime, true }, +#ifdef ENABLE_WALLET + { "hidden", "resendwallettransactions", &resendwallettransactions, true}, +#endif #ifdef ENABLE_WALLET /* Wallet */ - { "wallet", "addmultisigaddress", &addmultisigaddress, true, false, true }, - { "wallet", "backupwallet", &backupwallet, true, false, true }, - { "wallet", "dumpprivkey", &dumpprivkey, true, false, true }, - { "wallet", "dumpwallet", &dumpwallet, true, false, true }, - { "wallet", "encryptwallet", &encryptwallet, true, false, true }, - { "wallet", "getaccountaddress", &getaccountaddress, true, false, true }, - { "wallet", "getaccount", &getaccount, true, false, true }, - { "wallet", "getaddressesbyaccount", &getaddressesbyaccount, true, false, true }, - { "wallet", "getbalance", &getbalance, false, false, true }, - { "wallet", "getnewaddress", &getnewaddress, true, false, true }, - { "wallet", "getrawchangeaddress", &getrawchangeaddress, true, false, true }, - { "wallet", "getreceivedbyaccount", &getreceivedbyaccount, false, false, true }, - { "wallet", "getreceivedbyaddress", &getreceivedbyaddress, false, false, true }, - { "wallet", "gettransaction", &gettransaction, false, false, true }, - { "wallet", "getunconfirmedbalance", &getunconfirmedbalance, false, false, true }, - { "wallet", "getwalletinfo", &getwalletinfo, false, false, true }, - { "wallet", "importprivkey", &importprivkey, true, false, true }, - { "wallet", "importwallet", &importwallet, true, false, true }, - { "wallet", "importaddress", &importaddress, true, false, true }, - { "wallet", "keypoolrefill", &keypoolrefill, true, false, true }, - { "wallet", "listaccounts", &listaccounts, false, false, true }, - { "wallet", "listaddressgroupings", &listaddressgroupings, false, false, true }, - { "wallet", "listlockunspent", &listlockunspent, false, false, true }, - { "wallet", "listreceivedbyaccount", &listreceivedbyaccount, false, false, true }, - { "wallet", "listreceivedbyaddress", &listreceivedbyaddress, false, false, true }, - { "wallet", "listsinceblock", &listsinceblock, false, false, true }, - { "wallet", "listtransactions", &listtransactions, false, false, true }, - { "wallet", "listunspent", &listunspent, false, false, true }, - { "wallet", "lockunspent", &lockunspent, true, false, true }, - { "wallet", "move", &movecmd, false, false, true }, - { "wallet", "sendfrom", &sendfrom, false, false, true }, - { "wallet", "sendmany", &sendmany, false, false, true }, - { "wallet", "sendtoaddress", &sendtoaddress, false, false, true }, - { "wallet", "setaccount", &setaccount, true, false, true }, - { "wallet", "settxfee", &settxfee, true, false, true }, - { "wallet", "signmessage", &signmessage, true, false, true }, - { "wallet", "walletlock", &walletlock, true, false, true }, - { "wallet", "walletpassphrasechange", &walletpassphrasechange, true, false, true }, - { "wallet", "walletpassphrase", &walletpassphrase, true, false, true }, + { "wallet", "addmultisigaddress", &addmultisigaddress, true }, + { "wallet", "backupwallet", &backupwallet, true }, + { "wallet", "dumpprivkey", &dumpprivkey, true }, + { "wallet", "dumpwallet", &dumpwallet, true }, + { "wallet", "encryptwallet", &encryptwallet, true }, + { "wallet", "getaccountaddress", &getaccountaddress, true }, + { "wallet", "getaccount", &getaccount, true }, + { "wallet", "getaddressesbyaccount", &getaddressesbyaccount, true }, + { "wallet", "getbalance", &getbalance, false }, + { "wallet", "getnewaddress", &getnewaddress, true }, + { "wallet", "getrawchangeaddress", &getrawchangeaddress, true }, + { "wallet", "getreceivedbyaccount", &getreceivedbyaccount, false }, + { "wallet", "getreceivedbyaddress", &getreceivedbyaddress, false }, + { "wallet", "gettransaction", &gettransaction, false }, + { "wallet", "getunconfirmedbalance", &getunconfirmedbalance, false }, + { "wallet", "getwalletinfo", &getwalletinfo, false }, + { "wallet", "importprivkey", &importprivkey, true }, + { "wallet", "importwallet", &importwallet, true }, + { "wallet", "importaddress", &importaddress, true }, + { "wallet", "keypoolrefill", &keypoolrefill, true }, + { "wallet", "listaccounts", &listaccounts, false }, + { "wallet", "listaddressgroupings", &listaddressgroupings, false }, + { "wallet", "listlockunspent", &listlockunspent, false }, + { "wallet", "listreceivedbyaccount", &listreceivedbyaccount, false }, + { "wallet", "listreceivedbyaddress", &listreceivedbyaddress, false }, + { "wallet", "listsinceblock", &listsinceblock, false }, + { "wallet", "listtransactions", &listtransactions, false }, + { "wallet", "listunspent", &listunspent, false }, + { "wallet", "lockunspent", &lockunspent, true }, + { "wallet", "move", &movecmd, false }, + { "wallet", "sendfrom", &sendfrom, false }, + { "wallet", "sendmany", &sendmany, false }, + { "wallet", "sendtoaddress", &sendtoaddress, false }, + { "wallet", "setaccount", &setaccount, true }, + { "wallet", "settxfee", &settxfee, true }, + { "wallet", "signmessage", &signmessage, true }, + { "wallet", "walletlock", &walletlock, true }, + { "wallet", "walletpassphrasechange", &walletpassphrasechange, true }, + { "wallet", "walletpassphrase", &walletpassphrase, true }, #endif // ENABLE_WALLET }; @@ -362,7 +394,7 @@ CRPCTable::CRPCTable() } } -const CRPCCommand *CRPCTable::operator[](string name) const +const CRPCCommand *CRPCTable::operator[](const std::string& name) const { map<string, const CRPCCommand*>::const_iterator it = mapCommands.find(name); if (it == mapCommands.end()) @@ -381,14 +413,14 @@ bool HTTPAuthorized(map<string, string>& mapHeaders) return TimingResistantEqual(strUserPass, strRPCUserColonPass); } -void ErrorReply(std::ostream& stream, const Object& objError, const Value& id) +void ErrorReply(std::ostream& stream, const UniValue& objError, const UniValue& 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); + string strReply = JSONRPCReply(NullUniValue, objError, id); stream << HTTPReply(nStatus, strReply, false) << std::flush; } @@ -428,7 +460,7 @@ class AcceptedConnectionImpl : public AcceptedConnection { public: AcceptedConnectionImpl( - asio::io_service& io_service, + boost::asio::io_service& io_service, ssl::context &context, bool fUseSSL) : sslStream(io_service, context), @@ -453,11 +485,11 @@ public: } typename Protocol::endpoint peer; - asio::ssl::stream<typename Protocol::socket> sslStream; + boost::asio::ssl::stream<typename Protocol::socket> sslStream; private: SSLIOStreamDevice<Protocol> _d; - iostreams::stream< SSLIOStreamDevice<Protocol> > _stream; + boost::iostreams::stream< SSLIOStreamDevice<Protocol> > _stream; }; void ServiceConnection(AcceptedConnection *conn); @@ -504,7 +536,7 @@ static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptor<Protocol, 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()) + if (error != boost::asio::error::operation_aborted && acceptor->is_open()) RPCListen(acceptor, context, fUseSSL); AcceptedConnectionImpl<ip::tcp>* tcp_conn = dynamic_cast< AcceptedConnectionImpl<ip::tcp>* >(conn.get()); @@ -535,7 +567,7 @@ static ip::tcp::endpoint ParseEndpoint(const std::string &strEndpoint, int defau std::string addr; int port = defaultPort; SplitHostPort(strEndpoint, port, addr); - return ip::tcp::endpoint(asio::ip::address::from_string(addr), port); + return ip::tcp::endpoint(boost::asio::ip::address::from_string(addr), port); } void StartRPCThreads() @@ -590,7 +622,7 @@ void StartRPCThreads() } assert(rpc_io_service == NULL); - rpc_io_service = new asio::io_service(); + rpc_io_service = new boost::asio::io_service(); rpc_ssl_context = new ssl::context(*rpc_io_service, ssl::context::sslv23); const bool fUseSSL = GetBoolArg("-rpcssl", false); @@ -599,14 +631,14 @@ void StartRPCThreads() { rpc_ssl_context->set_options(ssl::context::no_sslv2 | ssl::context::no_sslv3); - 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()); + boost::filesystem::path pathCertFile(GetArg("-rpcsslcertificatechainfile", "server.cert")); + if (!pathCertFile.is_complete()) pathCertFile = boost::filesystem::path(GetDataDir()) / pathCertFile; + if (boost::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); + boost::filesystem::path pathPKFile(GetArg("-rpcsslprivatekeyfile", "server.pem")); + if (!pathPKFile.is_complete()) pathPKFile = boost::filesystem::path(GetDataDir()) / pathPKFile; + if (boost::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"); @@ -618,8 +650,8 @@ void StartRPCThreads() int defaultPort = GetArg("-rpcport", BaseParams().RPCPort()); if (!mapArgs.count("-rpcallowip")) // Default to loopback if not allowing external IPs { - vEndpoints.push_back(ip::tcp::endpoint(asio::ip::address_v6::loopback(), defaultPort)); - vEndpoints.push_back(ip::tcp::endpoint(asio::ip::address_v4::loopback(), defaultPort)); + vEndpoints.push_back(ip::tcp::endpoint(boost::asio::ip::address_v6::loopback(), defaultPort)); + vEndpoints.push_back(ip::tcp::endpoint(boost::asio::ip::address_v4::loopback(), defaultPort)); if (mapArgs.count("-rpcbind")) { LogPrintf("WARNING: option -rpcbind was ignored because -rpcallowip was not specified, refusing to allow everyone to connect\n"); @@ -641,8 +673,8 @@ void StartRPCThreads() } } } else { // No specific bind address specified, bind to any - vEndpoints.push_back(ip::tcp::endpoint(asio::ip::address_v6::any(), defaultPort)); - vEndpoints.push_back(ip::tcp::endpoint(asio::ip::address_v4::any(), defaultPort)); + vEndpoints.push_back(ip::tcp::endpoint(boost::asio::ip::address_v6::any(), defaultPort)); + vEndpoints.push_back(ip::tcp::endpoint(boost::asio::ip::address_v4::any(), defaultPort)); // Prefer making the socket dual IPv6/IPv4 instead of binding // to both addresses seperately. bBindAny = true; @@ -650,20 +682,22 @@ void StartRPCThreads() bool fListening = false; std::string strerr; + std::string straddress; BOOST_FOREACH(const ip::tcp::endpoint &endpoint, vEndpoints) { - asio::ip::address bindAddress = endpoint.address(); - LogPrintf("Binding RPC on address %s port %i (IPv4+IPv6 bind any: %i)\n", bindAddress.to_string(), endpoint.port(), bBindAny); - boost::system::error_code v6_only_error; - boost::shared_ptr<ip::tcp::acceptor> acceptor(new ip::tcp::acceptor(*rpc_io_service)); - try { + boost::asio::ip::address bindAddress = endpoint.address(); + straddress = bindAddress.to_string(); + LogPrintf("Binding RPC on address %s port %i (IPv4+IPv6 bind any: %i)\n", straddress, endpoint.port(), bBindAny); + boost::system::error_code v6_only_error; + boost::shared_ptr<ip::tcp::acceptor> acceptor(new ip::tcp::acceptor(*rpc_io_service)); + acceptor->open(endpoint.protocol()); acceptor->set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); // Try making the socket dual IPv6/IPv4 when listening on the IPv6 "any" address acceptor->set_option(boost::asio::ip::v6_only( - !bBindAny || bindAddress != asio::ip::address_v6::any()), v6_only_error); + !bBindAny || bindAddress != boost::asio::ip::address_v6::any()), v6_only_error); acceptor->bind(endpoint); acceptor->listen(socket_base::max_connections); @@ -673,13 +707,13 @@ void StartRPCThreads() fListening = true; rpc_acceptors.push_back(acceptor); // If dual IPv6/IPv4 bind successful, skip binding to IPv4 separately - if(bBindAny && bindAddress == asio::ip::address_v6::any() && !v6_only_error) + if(bBindAny && bindAddress == boost::asio::ip::address_v6::any() && !v6_only_error) break; } catch (const boost::system::system_error& e) { - LogPrintf("ERROR: Binding RPC on address %s port %i failed: %s\n", bindAddress.to_string(), endpoint.port(), e.what()); - strerr = strprintf(_("An error occurred while setting up the RPC address %s port %u for listening: %s"), bindAddress.to_string(), endpoint.port(), e.what()); + LogPrintf("ERROR: Binding RPC on address %s port %i failed: %s\n", straddress, endpoint.port(), e.what()); + strerr = strprintf(_("An error occurred while setting up the RPC address %s port %u for listening: %s"), straddress, endpoint.port(), e.what()); } } @@ -691,20 +725,21 @@ void StartRPCThreads() 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)); + rpc_worker_group->create_thread(boost::bind(&boost::asio::io_service::run, rpc_io_service)); fRPCRunning = true; + g_rpcSignals.Started(); } void StartDummyRPCThread() { if(rpc_io_service == NULL) { - rpc_io_service = new asio::io_service(); + rpc_io_service = new boost::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_dummy_work = new boost::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)); + rpc_worker_group->create_thread(boost::bind(&boost::asio::io_service::run, rpc_io_service)); fRPCRunning = true; } } @@ -717,7 +752,7 @@ void StopRPCThreads() // First, cancel all timers and acceptors // This is not done automatically by ->stop(), and in some cases the destructor of - // asio::io_service can hang if this is skipped. + // boost::asio::io_service can hang if this is skipped. boost::system::error_code ec; BOOST_FOREACH(const boost::shared_ptr<ip::tcp::acceptor> &acceptor, rpc_acceptors) { @@ -735,7 +770,7 @@ void StopRPCThreads() deadlineTimers.clear(); rpc_io_service->stop(); - cvBlockChange.notify_all(); + g_rpcSignals.Stopped(); if (rpc_worker_group != NULL) rpc_worker_group->join_all(); delete rpc_dummy_work; rpc_dummy_work = NULL; @@ -785,83 +820,83 @@ void RPCRunLater(const std::string& name, boost::function<void(void)> func, int6 deadlineTimers.insert(make_pair(name, boost::shared_ptr<deadline_timer>(new deadline_timer(*rpc_io_service)))); } - deadlineTimers[name]->expires_from_now(posix_time::seconds(nSeconds)); + deadlineTimers[name]->expires_from_now(boost::posix_time::seconds(nSeconds)); deadlineTimers[name]->async_wait(boost::bind(RPCRunHandler, _1, func)); } class JSONRequest { public: - Value id; + UniValue id; string strMethod; - Array params; + UniValue params; - JSONRequest() { id = Value::null; } - void parse(const Value& valRequest); + JSONRequest() { id = NullUniValue; } + void parse(const UniValue& valRequest); }; -void JSONRequest::parse(const Value& valRequest) +void JSONRequest::parse(const UniValue& valRequest) { // Parse request - if (valRequest.type() != obj_type) + if (!valRequest.isObject()) throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object"); - const Object& request = valRequest.get_obj(); + const UniValue& 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) + UniValue valMethod = find_value(request, "method"); + if (valMethod.isNull()) throw JSONRPCError(RPC_INVALID_REQUEST, "Missing method"); - if (valMethod.type() != str_type) + if (!valMethod.isStr()) throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string"); strMethod = valMethod.get_str(); if (strMethod != "getblocktemplate") - LogPrint("rpc", "ThreadRPCServer method=%s\n", strMethod); + LogPrint("rpc", "ThreadRPCServer method=%s\n", SanitizeString(strMethod)); // Parse params - Value valParams = find_value(request, "params"); - if (valParams.type() == array_type) + UniValue valParams = find_value(request, "params"); + if (valParams.isArray()) params = valParams.get_array(); - else if (valParams.type() == null_type) - params = Array(); + else if (valParams.isNull()) + params = UniValue(UniValue::VARR); else throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array"); } -static Object JSONRPCExecOne(const Value& req) +static UniValue JSONRPCExecOne(const UniValue& req) { - Object rpc_result; + UniValue rpc_result(UniValue::VOBJ); JSONRequest jreq; try { jreq.parse(req); - Value result = tableRPC.execute(jreq.strMethod, jreq.params); - rpc_result = JSONRPCReplyObj(result, Value::null, jreq.id); + UniValue result = tableRPC.execute(jreq.strMethod, jreq.params); + rpc_result = JSONRPCReplyObj(result, NullUniValue, jreq.id); } - catch (const Object& objError) + catch (const UniValue& objError) { - rpc_result = JSONRPCReplyObj(Value::null, objError, jreq.id); + rpc_result = JSONRPCReplyObj(NullUniValue, objError, jreq.id); } catch (const std::exception& e) { - rpc_result = JSONRPCReplyObj(Value::null, + rpc_result = JSONRPCReplyObj(NullUniValue, JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id); } return rpc_result; } -static string JSONRPCExecBatch(const Array& vReq) +static string JSONRPCExecBatch(const UniValue& vReq) { - Array ret; + UniValue ret(UniValue::VARR); for (unsigned int reqIdx = 0; reqIdx < vReq.size(); reqIdx++) ret.push_back(JSONRPCExecOne(vReq[reqIdx])); - return write_string(Value(ret), false) + "\n"; + return ret.write() + "\n"; } static bool HTTPReq_JSONRPC(AcceptedConnection *conn, @@ -880,8 +915,8 @@ static bool HTTPReq_JSONRPC(AcceptedConnection *conn, { LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", conn->peer_address_to_string()); /* Deter brute-forcing - If this results in a DoS the user really - shouldn't have their RPC port exposed. */ + We don't support exposing the RPC port, so this shouldn't result + in a DoS. */ MilliSleep(250); conn->stream() << HTTPError(HTTP_UNAUTHORIZED, false) << std::flush; @@ -892,37 +927,30 @@ static bool HTTPReq_JSONRPC(AcceptedConnection *conn, try { // Parse request - Value valRequest; - if (!read_string(strRequest, valRequest)) + UniValue valRequest; + if (!valRequest.read(strRequest)) throw JSONRPCError(RPC_PARSE_ERROR, "Parse error"); - // Return immediately if in warmup - { - LOCK(cs_rpcWarmup); - if (fRPCInWarmup) - throw JSONRPCError(RPC_IN_WARMUP, rpcWarmupStatus); - } - string strReply; // singleton request - if (valRequest.type() == obj_type) { + if (valRequest.isObject()) { jreq.parse(valRequest); - Value result = tableRPC.execute(jreq.strMethod, jreq.params); + UniValue result = tableRPC.execute(jreq.strMethod, jreq.params); // Send reply - strReply = JSONRPCReply(result, Value::null, jreq.id); + strReply = JSONRPCReply(result, NullUniValue, jreq.id); // array of requests - } else if (valRequest.type() == array_type) + } else if (valRequest.isArray()) strReply = JSONRPCExecBatch(valRequest.get_array()); else throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error"); conn->stream() << HTTPReplyHeader(HTTP_OK, fRun, strReply.size()) << strReply << std::flush; } - catch (const Object& objError) + catch (const UniValue& objError) { ErrorReply(conn->stream(), objError, jreq.id); return false; @@ -952,7 +980,7 @@ void ServiceConnection(AcceptedConnection *conn) ReadHTTPMessage(conn->stream(), mapHeaders, strRequest, nProto, MAX_SIZE); // HTTP Keep-Alive is false; close connection immediately - if (mapHeaders["connection"] == "close") + if ((mapHeaders["connection"] == "close") || (!GetBoolArg("-rpckeepalive", true))) fRun = false; // Process via JSON-RPC API @@ -962,7 +990,7 @@ void ServiceConnection(AcceptedConnection *conn) // Process via HTTP REST API } else if (strURI.substr(0, 6) == "/rest/" && GetBoolArg("-rest", false)) { - if (!HTTPReq_REST(conn, strURI, mapHeaders, fRun)) + if (!HTTPReq_REST(conn, strURI, strRequest, mapHeaders, fRun)) break; } else { @@ -972,58 +1000,42 @@ void ServiceConnection(AcceptedConnection *conn) } } -json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_spirit::Array ¶ms) const +UniValue CRPCTable::execute(const std::string &strMethod, const UniValue ¶ms) const { + // Return immediately if in warmup + { + LOCK(cs_rpcWarmup); + if (fRPCInWarmup) + throw JSONRPCError(RPC_IN_WARMUP, rpcWarmupStatus); + } + // 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); + g_rpcSignals.PreCommand(*pcmd); 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; + return pcmd->actor(params, false); } catch (const std::exception& e) { throw JSONRPCError(RPC_MISC_ERROR, e.what()); } + + g_rpcSignals.PostCommand(*pcmd); } -std::string HelpExampleCli(string methodname, string args){ +std::string HelpExampleCli(const std::string& methodname, const std::string& args) +{ return "> bitcoin-cli " + methodname + " " + args + "\n"; } -std::string HelpExampleRpc(string methodname, string args){ +std::string HelpExampleRpc(const std::string& methodname, const std::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"; } |