Initial merge
This commit is contained in:
@@ -12,16 +12,12 @@
|
||||
#include "ui_interface.h"
|
||||
#include "util.h"
|
||||
#include "utilstrencodings.h"
|
||||
#ifdef ENABLE_WALLET
|
||||
#include "wallet/wallet.h"
|
||||
#endif
|
||||
#include "asyncrpcqueue.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/asio/ssl.hpp>
|
||||
#include <univalue.h>
|
||||
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
@@ -30,28 +26,20 @@
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <boost/signals2/signal.hpp>
|
||||
#include <boost/thread.hpp>
|
||||
#include "json/json_spirit_writer_template.h"
|
||||
#include <boost/algorithm/string/case_conv.hpp> // for to_upper()
|
||||
|
||||
using namespace boost::asio;
|
||||
using namespace json_spirit;
|
||||
using namespace RPCServer;
|
||||
using namespace std;
|
||||
|
||||
static std::string strRPCUserColonPass;
|
||||
|
||||
static bool fRPCRunning = false;
|
||||
static bool fRPCInWarmup = true;
|
||||
static std::string rpcWarmupStatus("RPC server started");
|
||||
static CCriticalSection cs_rpcWarmup;
|
||||
|
||||
//! These are created by StartRPCThreads, destroyed in StopRPCThreads
|
||||
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;
|
||||
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;
|
||||
/* Timer-creating functions */
|
||||
static std::vector<RPCTimerInterface*> timerInterfaces;
|
||||
/* Map of name to timer.
|
||||
* @note Can be changed to std::unique_ptr when C++11 */
|
||||
static std::map<std::string, boost::shared_ptr<RPCTimerBase> > deadlineTimers;
|
||||
|
||||
static struct CRPCSignals
|
||||
{
|
||||
@@ -81,71 +69,72 @@ void RPCServer::OnPostCommand(boost::function<void (const CRPCCommand&)> slot)
|
||||
g_rpcSignals.PostCommand.connect(boost::bind(slot, _1));
|
||||
}
|
||||
|
||||
void RPCTypeCheck(const Array& params,
|
||||
const list<Value_type>& typesExpected,
|
||||
void RPCTypeCheck(const UniValue& params,
|
||||
const list<UniValue::VType>& typesExpected,
|
||||
bool fAllowNull)
|
||||
{
|
||||
unsigned int i = 0;
|
||||
BOOST_FOREACH(Value_type t, typesExpected)
|
||||
size_t i = 0;
|
||||
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)
|
||||
CAmount AmountFromValue(const UniValue& value)
|
||||
{
|
||||
return (int64_t)(d > 0 ? d + 0.5 : d - 0.5);
|
||||
}
|
||||
|
||||
CAmount AmountFromValue(const Value& value)
|
||||
{
|
||||
double dAmount = value.get_real();
|
||||
if (dAmount <= 0.0 || dAmount > 21000000.0)
|
||||
if (!value.isNum() && !value.isStr())
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, "Amount is not a number or string");
|
||||
CAmount amount;
|
||||
if (!ParseFixedPoint(value.getValStr(), 8, &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;
|
||||
bool sign = amount < 0;
|
||||
int64_t n_abs = (sign ? -amount : amount);
|
||||
int64_t quotient = n_abs / COIN;
|
||||
int64_t remainder = n_abs % COIN;
|
||||
return UniValue(UniValue::VNUM,
|
||||
strprintf("%s%d.%08d", sign ? "-" : "", quotient, remainder));
|
||||
}
|
||||
|
||||
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+"')");
|
||||
@@ -153,30 +142,29 @@ 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);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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;
|
||||
@@ -198,7 +186,7 @@ string CRPCTable::help(string strCommand) const
|
||||
continue;
|
||||
try
|
||||
{
|
||||
Array params;
|
||||
UniValue params;
|
||||
rpcfn_type pfn = pcmd->actor;
|
||||
if (setDone.insert(pfn).second)
|
||||
(*pfn)(params, true);
|
||||
@@ -231,7 +219,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(
|
||||
@@ -251,7 +239,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)
|
||||
@@ -263,8 +251,6 @@ Value stop(const Array& params, bool fHelp)
|
||||
return "Komodo server stopping";
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Call Table
|
||||
*/
|
||||
@@ -279,11 +265,15 @@ static const CRPCCommand vRPCCommands[] =
|
||||
/* P2P networking */
|
||||
{ "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 },
|
||||
@@ -291,7 +281,8 @@ static const CRPCCommand vRPCCommands[] =
|
||||
{ "blockchain", "getblockcount", &getblockcount, true },
|
||||
{ "blockchain", "getblock", &getblock, true },
|
||||
{ "blockchain", "getblockhash", &getblockhash, true },
|
||||
//{ "blockchain", "getchaintips", &getchaintips, true },
|
||||
{ "blockchain", "getblockheader", &getblockheader, true },
|
||||
{ "blockchain", "getchaintips", &getchaintips, true },
|
||||
{ "blockchain", "getdifficulty", &getdifficulty, true },
|
||||
{ "blockchain", "getmempoolinfo", &getmempoolinfo, true },
|
||||
{ "blockchain", "getrawmempool", &getrawmempool, true },
|
||||
@@ -320,7 +311,7 @@ static const CRPCCommand vRPCCommands[] =
|
||||
{ "mining", "submitblock", &submitblock, true },
|
||||
{ "mining", "getblocksubsidy", &getblocksubsidy, true },
|
||||
|
||||
#ifdef ENABLE_WALLET
|
||||
#ifdef ENABLE_MINING
|
||||
/* Coin generation */
|
||||
{ "generating", "getgenerate", &getgenerate, true },
|
||||
{ "generating", "setgenerate", &setgenerate, true },
|
||||
@@ -334,6 +325,9 @@ static const CRPCCommand vRPCCommands[] =
|
||||
{ "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 },
|
||||
@@ -428,7 +422,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())
|
||||
@@ -436,168 +430,9 @@ const CRPCCommand *CRPCTable::operator[](string name) const
|
||||
return (*it).second;
|
||||
}
|
||||
|
||||
|
||||
bool HTTPAuthorized(map<string, string>& 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;
|
||||
}
|
||||
|
||||
CNetAddr BoostAsioToCNetAddr(boost::asio::ip::address address)
|
||||
{
|
||||
CNetAddr netaddr;
|
||||
// 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()))
|
||||
address = address.to_v6().to_v4();
|
||||
|
||||
if(address.is_v4())
|
||||
{
|
||||
boost::asio::ip::address_v4::bytes_type bytes = address.to_v4().to_bytes();
|
||||
netaddr.SetRaw(NET_IPV4, &bytes[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
boost::asio::ip::address_v6::bytes_type bytes = address.to_v6().to_bytes();
|
||||
netaddr.SetRaw(NET_IPV6, &bytes[0]);
|
||||
}
|
||||
return netaddr;
|
||||
}
|
||||
|
||||
bool ClientAllowed(const boost::asio::ip::address& address)
|
||||
{
|
||||
CNetAddr netaddr = BoostAsioToCNetAddr(address);
|
||||
BOOST_FOREACH(const CSubNet &subnet, rpc_allow_subnets)
|
||||
if (subnet.Match(netaddr))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename Protocol>
|
||||
class AcceptedConnectionImpl : public AcceptedConnection
|
||||
{
|
||||
public:
|
||||
AcceptedConnectionImpl(
|
||||
boost::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;
|
||||
boost::asio::ssl::stream<typename Protocol::socket> sslStream;
|
||||
|
||||
private:
|
||||
SSLIOStreamDevice<Protocol> _d;
|
||||
boost::iostreams::stream< SSLIOStreamDevice<Protocol> > _stream;
|
||||
};
|
||||
|
||||
void ServiceConnection(AcceptedConnection *conn);
|
||||
|
||||
//! Forward declaration required for RPCListen
|
||||
template <typename Protocol, typename SocketAcceptorService>
|
||||
static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptor<Protocol, SocketAcceptorService> > acceptor,
|
||||
ssl::context& context,
|
||||
bool fUseSSL,
|
||||
boost::shared_ptr< AcceptedConnection > conn,
|
||||
const boost::system::error_code& error);
|
||||
|
||||
/**
|
||||
* Sets up I/O resources to accept and handle a new connection.
|
||||
*/
|
||||
template <typename Protocol, typename SocketAcceptorService>
|
||||
static void RPCListen(boost::shared_ptr< basic_socket_acceptor<Protocol, SocketAcceptorService> > acceptor,
|
||||
ssl::context& context,
|
||||
const bool fUseSSL)
|
||||
{
|
||||
// Accept connection
|
||||
boost::shared_ptr< AcceptedConnectionImpl<Protocol> > conn(new AcceptedConnectionImpl<Protocol>(acceptor->get_io_service(), context, fUseSSL));
|
||||
|
||||
acceptor->async_accept(
|
||||
conn->sslStream.lowest_layer(),
|
||||
conn->peer,
|
||||
boost::bind(&RPCAcceptHandler<Protocol, SocketAcceptorService>,
|
||||
acceptor,
|
||||
boost::ref(context),
|
||||
fUseSSL,
|
||||
conn,
|
||||
_1));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Accept and handle incoming connection.
|
||||
*/
|
||||
template <typename Protocol, typename SocketAcceptorService>
|
||||
static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptor<Protocol, SocketAcceptorService> > acceptor,
|
||||
ssl::context& context,
|
||||
const bool fUseSSL,
|
||||
boost::shared_ptr< 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 != boost::asio::error::operation_aborted && acceptor->is_open())
|
||||
RPCListen(acceptor, context, fUseSSL);
|
||||
|
||||
AcceptedConnectionImpl<ip::tcp>* tcp_conn = dynamic_cast< AcceptedConnectionImpl<ip::tcp>* >(conn.get());
|
||||
|
||||
if (error)
|
||||
{
|
||||
// TODO: Actually handle errors
|
||||
LogPrintf("%s: Error: %s\n", __func__, error.message());
|
||||
}
|
||||
// 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() << HTTPError(HTTP_FORBIDDEN, false) << std::flush;
|
||||
conn->close();
|
||||
}
|
||||
else {
|
||||
ServiceConnection(conn.get());
|
||||
conn->close();
|
||||
}
|
||||
}
|
||||
|
||||
static ip::tcp::endpoint ParseEndpoint(const std::string &strEndpoint, int defaultPort)
|
||||
bool StartRPC()
|
||||
{
|
||||
/*<<<<<<< HEA
|
||||
std::string addr;
|
||||
int port = defaultPort;
|
||||
SplitHostPort(strEndpoint, port, addr);
|
||||
@@ -633,26 +468,6 @@ void StartRPCThreads()
|
||||
|
||||
if (mapArgs["-rpcpassword"] == "")
|
||||
{
|
||||
/*<<<<<<< HEAD
|
||||
unsigned char rand_pwd[32];
|
||||
GetRandBytes(rand_pwd, 32);
|
||||
uiInterface.ThreadSafeMessageBox(strprintf(
|
||||
_("To use komodod you must set an rpcpassword in the configuration file:\n"
|
||||
"%s\n"
|
||||
"It is recommended you use the following random password:\n"
|
||||
"rpcuser=zcashrpc\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 \"Komodo Alert\" admin@foo.com\n"),
|
||||
GetConfigFile().string(),
|
||||
EncodeBase58(&rand_pwd[0],&rand_pwd[0]+32)),
|
||||
"", CClientUIInterface::MSG_ERROR | CClientUIInterface::SECURE);
|
||||
StartShutdown();
|
||||
return;
|
||||
=======*/
|
||||
LogPrintf("No rpcpassword set - using random cookie authentication\n");
|
||||
if (!GenerateAuthCookie(&strRPCUserColonPass)) {
|
||||
uiInterface.ThreadSafeMessageBox(
|
||||
@@ -663,7 +478,6 @@ void StartRPCThreads()
|
||||
}
|
||||
} else {
|
||||
strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
|
||||
//>>>>>>> zcash/master
|
||||
}
|
||||
|
||||
assert(rpc_io_service == NULL);
|
||||
@@ -771,6 +585,9 @@ 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(&boost::asio::io_service::run, rpc_io_service));
|
||||
=======
|
||||
LogPrint("rpc", "Starting RPC\n");
|
||||
>>>>>>> zcash/master*/
|
||||
fRPCRunning = true;
|
||||
g_rpcSignals.Started();
|
||||
|
||||
@@ -789,57 +606,21 @@ void StartRPCThreads()
|
||||
for (int i = 0; i < n; i++)
|
||||
getAsyncRPCQueue()->addWorker();
|
||||
*/
|
||||
return true;
|
||||
}
|
||||
|
||||
void StartDummyRPCThread()
|
||||
void InterruptRPC()
|
||||
{
|
||||
if(rpc_io_service == NULL)
|
||||
{
|
||||
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 boost::asio::io_service::work(*rpc_io_service);
|
||||
rpc_worker_group = new boost::thread_group();
|
||||
rpc_worker_group->create_thread(boost::bind(&boost::asio::io_service::run, rpc_io_service));
|
||||
fRPCRunning = true;
|
||||
}
|
||||
}
|
||||
|
||||
void StopRPCThreads()
|
||||
{
|
||||
if (rpc_io_service == NULL) return;
|
||||
// Set this to false first, so that longpolling loops will exit when woken up
|
||||
LogPrint("rpc", "Interrupting RPC\n");
|
||||
// Interrupt e.g. running longpolls
|
||||
fRPCRunning = false;
|
||||
}
|
||||
|
||||
// First, cancel all timers and acceptors
|
||||
// This is not done automatically by ->stop(), and in some cases the destructor of
|
||||
// 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)
|
||||
{
|
||||
acceptor->cancel(ec);
|
||||
if (ec)
|
||||
LogPrintf("%s: Warning: %s when cancelling acceptor\n", __func__, ec.message());
|
||||
}
|
||||
rpc_acceptors.clear();
|
||||
BOOST_FOREACH(const PAIRTYPE(std::string, boost::shared_ptr<deadline_timer>) &timer, deadlineTimers)
|
||||
{
|
||||
timer.second->cancel(ec);
|
||||
if (ec)
|
||||
LogPrintf("%s: Warning: %s when cancelling timer\n", __func__, ec.message());
|
||||
}
|
||||
void StopRPC()
|
||||
{
|
||||
LogPrint("rpc", "Stopping RPC\n");
|
||||
deadlineTimers.clear();
|
||||
|
||||
DeleteAuthCookie();
|
||||
|
||||
rpc_io_service->stop();
|
||||
g_rpcSignals.Stopped();
|
||||
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;
|
||||
|
||||
// Tells async queue to cancel all operations and shutdown.
|
||||
LogPrintf("%s: waiting for async rpc workers to stop\n", __func__);
|
||||
@@ -872,118 +653,83 @@ bool RPCIsInWarmup(std::string *outStatus)
|
||||
return fRPCInWarmup;
|
||||
}
|
||||
|
||||
void RPCRunHandler(const boost::system::error_code& err, boost::function<void(void)> func)
|
||||
{
|
||||
if (!err)
|
||||
func();
|
||||
}
|
||||
|
||||
void RPCRunLater(const std::string& name, boost::function<void(void)> func, int64_t nSeconds)
|
||||
{
|
||||
assert(rpc_io_service != NULL);
|
||||
|
||||
if (deadlineTimers.count(name) == 0)
|
||||
{
|
||||
deadlineTimers.insert(make_pair(name,
|
||||
boost::shared_ptr<deadline_timer>(new deadline_timer(*rpc_io_service))));
|
||||
}
|
||||
deadlineTimers[name]->expires_from_now(boost::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)
|
||||
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", 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)
|
||||
std::string JSONRPCExecBatch(const UniValue& vReq)
|
||||
{
|
||||
Array ret;
|
||||
for (unsigned int reqIdx = 0; reqIdx < vReq.size(); reqIdx++)
|
||||
UniValue ret(UniValue::VARR);
|
||||
for (size_t 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,
|
||||
string& strRequest,
|
||||
map<string, string>& mapHeaders,
|
||||
bool fRun)
|
||||
UniValue CRPCTable::execute(const std::string &strMethod, const UniValue ¶ms) const
|
||||
{
|
||||
// Check authorization
|
||||
if (mapHeaders.count("authorization") == 0)
|
||||
// Return immediately if in warmup
|
||||
{
|
||||
conn->stream() << HTTPError(HTTP_UNAUTHORIZED, false) << std::flush;
|
||||
return false;
|
||||
LOCK(cs_rpcWarmup);
|
||||
if (fRPCInWarmup)
|
||||
throw JSONRPCError(RPC_IN_WARMUP, rpcWarmupStatus);
|
||||
}
|
||||
|
||||
/*<<<<<<< HEA
|
||||
if (!HTTPAuthorized(mapHeaders))
|
||||
{
|
||||
LogPrintf("ThreadRPCServer incorrect password attempt from %s\n", conn->peer_address_to_string());
|
||||
/* Deter brute-forcing
|
||||
We don't support exposing the RPC port, so this shouldn't result
|
||||
in a DoS. */
|
||||
//Deter brute-forcing 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;
|
||||
@@ -1058,11 +804,11 @@ void ServiceConnection(AcceptedConnection *conn)
|
||||
// We have disabled support for HTTP Keep-Alive until resolution of #1680, upstream rpc deadlock.
|
||||
// Close connection immediately.
|
||||
fRun = false;
|
||||
/*
|
||||
|
||||
// HTTP Keep-Alive is false; close connection immediately
|
||||
if ((mapHeaders["connection"] == "close") || (!GetBoolArg("-rpckeepalive", true)))
|
||||
fRun = false;
|
||||
*/
|
||||
//if ((mapHeaders["connection"] == "close") || (!GetBoolArg("-rpckeepalive", true)))
|
||||
// fRun = false;
|
||||
|
||||
|
||||
// Process via JSON-RPC API
|
||||
if (strURI == "/") {
|
||||
@@ -1083,6 +829,8 @@ void ServiceConnection(AcceptedConnection *conn)
|
||||
|
||||
json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_spirit::Array ¶ms) const
|
||||
{
|
||||
=======
|
||||
>>>>>>> zcash/master*/
|
||||
// Find method
|
||||
const CRPCCommand *pcmd = tableRPC[strMethod];
|
||||
if (!pcmd)
|
||||
@@ -1103,15 +851,39 @@ json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_s
|
||||
g_rpcSignals.PostCommand(*pcmd);
|
||||
}
|
||||
|
||||
std::string HelpExampleCli(string methodname, string args){
|
||||
std::string HelpExampleCli(const std::string& methodname, const std::string& args)
|
||||
{
|
||||
return "> zcash-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:8232/\n";
|
||||
}
|
||||
|
||||
void RPCRegisterTimerInterface(RPCTimerInterface *iface)
|
||||
{
|
||||
timerInterfaces.push_back(iface);
|
||||
}
|
||||
|
||||
void RPCUnregisterTimerInterface(RPCTimerInterface *iface)
|
||||
{
|
||||
std::vector<RPCTimerInterface*>::iterator i = std::find(timerInterfaces.begin(), timerInterfaces.end(), iface);
|
||||
assert(i != timerInterfaces.end());
|
||||
timerInterfaces.erase(i);
|
||||
}
|
||||
|
||||
void RPCRunLater(const std::string& name, boost::function<void(void)> func, int64_t nSeconds)
|
||||
{
|
||||
if (timerInterfaces.empty())
|
||||
throw JSONRPCError(RPC_INTERNAL_ERROR, "No timer handler registered for RPC");
|
||||
deadlineTimers.erase(name);
|
||||
RPCTimerInterface* timerInterface = timerInterfaces[0];
|
||||
LogPrint("rpc", "queue run of timer %s in %i seconds (using %s)\n", name, nSeconds, timerInterface->Name());
|
||||
deadlineTimers.insert(std::make_pair(name, boost::shared_ptr<RPCTimerBase>(timerInterface->NewTimer(func, nSeconds*1000))));
|
||||
}
|
||||
|
||||
const CRPCTable tableRPC;
|
||||
|
||||
// Return async rpc queue
|
||||
|
||||
Reference in New Issue
Block a user