Merge pull request #3284
fb78cc2
Split up bitcoinrpc (code movement only) (Wladimir J. van der Laan)
This commit is contained in:
commit
5f083afde1
@ -12,7 +12,10 @@ DIST_SUBDIRS = . qt test
|
||||
.PHONY: FORCE
|
||||
# bitcoin core #
|
||||
BITCOIN_CORE_H = addrman.h alert.h allocators.h base58.h bignum.h \
|
||||
bitcoinrpc.h bloom.h chainparams.h checkpoints.h checkqueue.h \
|
||||
rpcclient.h \
|
||||
rpcprotocol.h \
|
||||
rpcserver.h \
|
||||
bloom.h chainparams.h checkpoints.h checkqueue.h \
|
||||
clientversion.h coincontrol.h compat.h core.h coins.h crypter.h db.h hash.h init.h \
|
||||
key.h keystore.h leveldbwrapper.h limitedmap.h main.h miner.h mruset.h \
|
||||
netbase.h net.h noui.h protocol.h script.h serialize.h sync.h threadsafety.h \
|
||||
@ -30,7 +33,11 @@ obj/build.h: FORCE
|
||||
$(abs_top_srcdir)
|
||||
version.o: obj/build.h
|
||||
|
||||
libbitcoin_a_SOURCES = addrman.cpp alert.cpp allocators.cpp bitcoinrpc.cpp bloom.cpp \
|
||||
libbitcoin_a_SOURCES = addrman.cpp alert.cpp allocators.cpp \
|
||||
rpcclient.cpp \
|
||||
rpcprotocol.cpp \
|
||||
rpcserver.cpp \
|
||||
bloom.cpp \
|
||||
chainparams.cpp checkpoints.cpp core.cpp coins.cpp crypter.cpp db.cpp hash.cpp \
|
||||
init.cpp key.cpp keystore.cpp leveldbwrapper.cpp main.cpp miner.cpp \
|
||||
netbase.cpp net.cpp noui.cpp protocol.cpp rpcblockchain.cpp rpcdump.cpp \
|
||||
|
@ -5,7 +5,7 @@
|
||||
|
||||
#include "util.h"
|
||||
#include "init.h"
|
||||
#include "bitcoinrpc.h"
|
||||
#include "rpcclient.h"
|
||||
#include "ui_interface.h" /* for _(...) */
|
||||
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
|
@ -3,7 +3,8 @@
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "bitcoinrpc.h"
|
||||
#include "rpcserver.h"
|
||||
#include "rpcclient.h"
|
||||
#include "init.h"
|
||||
#include "main.h"
|
||||
#include "noui.h"
|
||||
|
@ -10,7 +10,7 @@
|
||||
#include "init.h"
|
||||
|
||||
#include "addrman.h"
|
||||
#include "bitcoinrpc.h"
|
||||
#include "rpcserver.h"
|
||||
#include "checkpoints.h"
|
||||
#include "miner.h"
|
||||
#include "net.h"
|
||||
|
@ -8,7 +8,8 @@
|
||||
#include "clientmodel.h"
|
||||
#include "guiutil.h"
|
||||
|
||||
#include "bitcoinrpc.h"
|
||||
#include "rpcserver.h"
|
||||
#include "rpcclient.h"
|
||||
|
||||
#include "json/json_spirit_value.h"
|
||||
#include <openssl/crypto.h>
|
||||
|
@ -3,9 +3,7 @@
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
|
||||
|
||||
#include "bitcoinrpc.h"
|
||||
#include "rpcserver.h"
|
||||
#include "main.h"
|
||||
#include "sync.h"
|
||||
|
||||
|
246
src/rpcclient.cpp
Normal file
246
src/rpcclient.cpp
Normal file
@ -0,0 +1,246 @@
|
||||
// Copyright (c) 2010 Satoshi Nakamoto
|
||||
// Copyright (c) 2009-2013 The Bitcoin developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "rpcclient.h"
|
||||
|
||||
#include "rpcprotocol.h"
|
||||
#include "util.h"
|
||||
#include "ui_interface.h"
|
||||
#include "chainparams.h" // for Params().RPCPort()
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/asio/ssl.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/iostreams/concepts.hpp>
|
||||
#include <boost/iostreams/stream.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include "json/json_spirit_writer_template.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace boost;
|
||||
using namespace boost::asio;
|
||||
using namespace json_spirit;
|
||||
|
||||
Object CallRPC(const string& strMethod, const Array& params)
|
||||
{
|
||||
if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "")
|
||||
throw runtime_error(strprintf(
|
||||
_("You must set rpcpassword=<password> in the configuration file:\n%s\n"
|
||||
"If the file does not exist, create it with owner-readable-only file permissions."),
|
||||
GetConfigFile().string().c_str()));
|
||||
|
||||
// Connect to localhost
|
||||
bool fUseSSL = GetBoolArg("-rpcssl", false);
|
||||
asio::io_service io_service;
|
||||
ssl::context context(io_service, ssl::context::sslv23);
|
||||
context.set_options(ssl::context::no_sslv2);
|
||||
asio::ssl::stream<asio::ip::tcp::socket> sslStream(io_service, context);
|
||||
SSLIOStreamDevice<asio::ip::tcp> d(sslStream, fUseSSL);
|
||||
iostreams::stream< SSLIOStreamDevice<asio::ip::tcp> > stream(d);
|
||||
|
||||
bool fWait = GetBoolArg("-rpcwait", false); // -rpcwait means try until server has started
|
||||
do {
|
||||
bool fConnected = d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", itostr(Params().RPCPort())));
|
||||
if (fConnected) break;
|
||||
if (fWait)
|
||||
MilliSleep(1000);
|
||||
else
|
||||
throw runtime_error("couldn't connect to server");
|
||||
} while (fWait);
|
||||
|
||||
// HTTP basic authentication
|
||||
string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]);
|
||||
map<string, string> mapRequestHeaders;
|
||||
mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64;
|
||||
|
||||
// Send request
|
||||
string strRequest = JSONRPCRequest(strMethod, params, 1);
|
||||
string strPost = HTTPPost(strRequest, mapRequestHeaders);
|
||||
stream << strPost << std::flush;
|
||||
|
||||
// Receive HTTP reply status
|
||||
int nProto = 0;
|
||||
int nStatus = ReadHTTPStatus(stream, nProto);
|
||||
|
||||
// Receive HTTP reply message headers and body
|
||||
map<string, string> mapHeaders;
|
||||
string strReply;
|
||||
ReadHTTPMessage(stream, mapHeaders, strReply, nProto);
|
||||
|
||||
if (nStatus == HTTP_UNAUTHORIZED)
|
||||
throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
|
||||
else if (nStatus >= 400 && nStatus != HTTP_BAD_REQUEST && nStatus != HTTP_NOT_FOUND && nStatus != HTTP_INTERNAL_SERVER_ERROR)
|
||||
throw runtime_error(strprintf("server returned HTTP error %d", nStatus));
|
||||
else if (strReply.empty())
|
||||
throw runtime_error("no response from server");
|
||||
|
||||
// Parse reply
|
||||
Value valReply;
|
||||
if (!read_string(strReply, valReply))
|
||||
throw runtime_error("couldn't parse reply from server");
|
||||
const Object& reply = valReply.get_obj();
|
||||
if (reply.empty())
|
||||
throw runtime_error("expected reply to have result, error and id properties");
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void ConvertTo(Value& value, bool fAllowNull=false)
|
||||
{
|
||||
if (fAllowNull && value.type() == null_type)
|
||||
return;
|
||||
if (value.type() == str_type)
|
||||
{
|
||||
// reinterpret string as unquoted json value
|
||||
Value value2;
|
||||
string strJSON = value.get_str();
|
||||
if (!read_string(strJSON, value2))
|
||||
throw runtime_error(string("Error parsing JSON:")+strJSON);
|
||||
ConvertTo<T>(value2, fAllowNull);
|
||||
value = value2;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = value.get_value<T>();
|
||||
}
|
||||
}
|
||||
|
||||
// Convert strings to command-specific RPC representation
|
||||
Array RPCConvertValues(const std::string &strMethod, const std::vector<std::string> &strParams)
|
||||
{
|
||||
Array params;
|
||||
BOOST_FOREACH(const std::string ¶m, strParams)
|
||||
params.push_back(param);
|
||||
|
||||
int n = params.size();
|
||||
|
||||
//
|
||||
// Special case non-string parameter types
|
||||
//
|
||||
if (strMethod == "stop" && n > 0) ConvertTo<bool>(params[0]);
|
||||
if (strMethod == "getaddednodeinfo" && n > 0) ConvertTo<bool>(params[0]);
|
||||
if (strMethod == "setgenerate" && n > 0) ConvertTo<bool>(params[0]);
|
||||
if (strMethod == "setgenerate" && n > 1) ConvertTo<boost::int64_t>(params[1]);
|
||||
if (strMethod == "getnetworkhashps" && n > 0) ConvertTo<boost::int64_t>(params[0]);
|
||||
if (strMethod == "getnetworkhashps" && n > 1) ConvertTo<boost::int64_t>(params[1]);
|
||||
if (strMethod == "sendtoaddress" && n > 1) ConvertTo<double>(params[1]);
|
||||
if (strMethod == "settxfee" && n > 0) ConvertTo<double>(params[0]);
|
||||
if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo<boost::int64_t>(params[1]);
|
||||
if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo<boost::int64_t>(params[1]);
|
||||
if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
|
||||
if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo<bool>(params[1]);
|
||||
if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo<boost::int64_t>(params[0]);
|
||||
if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo<bool>(params[1]);
|
||||
if (strMethod == "getbalance" && n > 1) ConvertTo<boost::int64_t>(params[1]);
|
||||
if (strMethod == "getblockhash" && n > 0) ConvertTo<boost::int64_t>(params[0]);
|
||||
if (strMethod == "move" && n > 2) ConvertTo<double>(params[2]);
|
||||
if (strMethod == "move" && n > 3) ConvertTo<boost::int64_t>(params[3]);
|
||||
if (strMethod == "sendfrom" && n > 2) ConvertTo<double>(params[2]);
|
||||
if (strMethod == "sendfrom" && n > 3) ConvertTo<boost::int64_t>(params[3]);
|
||||
if (strMethod == "listtransactions" && n > 1) ConvertTo<boost::int64_t>(params[1]);
|
||||
if (strMethod == "listtransactions" && n > 2) ConvertTo<boost::int64_t>(params[2]);
|
||||
if (strMethod == "listaccounts" && n > 0) ConvertTo<boost::int64_t>(params[0]);
|
||||
if (strMethod == "walletpassphrase" && n > 1) ConvertTo<boost::int64_t>(params[1]);
|
||||
if (strMethod == "getblocktemplate" && n > 0) ConvertTo<Object>(params[0]);
|
||||
if (strMethod == "listsinceblock" && n > 1) ConvertTo<boost::int64_t>(params[1]);
|
||||
if (strMethod == "sendmany" && n > 1) ConvertTo<Object>(params[1]);
|
||||
if (strMethod == "sendmany" && n > 2) ConvertTo<boost::int64_t>(params[2]);
|
||||
if (strMethod == "addmultisigaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
|
||||
if (strMethod == "addmultisigaddress" && n > 1) ConvertTo<Array>(params[1]);
|
||||
if (strMethod == "createmultisig" && n > 0) ConvertTo<boost::int64_t>(params[0]);
|
||||
if (strMethod == "createmultisig" && n > 1) ConvertTo<Array>(params[1]);
|
||||
if (strMethod == "listunspent" && n > 0) ConvertTo<boost::int64_t>(params[0]);
|
||||
if (strMethod == "listunspent" && n > 1) ConvertTo<boost::int64_t>(params[1]);
|
||||
if (strMethod == "listunspent" && n > 2) ConvertTo<Array>(params[2]);
|
||||
if (strMethod == "getblock" && n > 1) ConvertTo<bool>(params[1]);
|
||||
if (strMethod == "getrawtransaction" && n > 1) ConvertTo<boost::int64_t>(params[1]);
|
||||
if (strMethod == "createrawtransaction" && n > 0) ConvertTo<Array>(params[0]);
|
||||
if (strMethod == "createrawtransaction" && n > 1) ConvertTo<Object>(params[1]);
|
||||
if (strMethod == "signrawtransaction" && n > 1) ConvertTo<Array>(params[1], true);
|
||||
if (strMethod == "signrawtransaction" && n > 2) ConvertTo<Array>(params[2], true);
|
||||
if (strMethod == "sendrawtransaction" && n > 1) ConvertTo<bool>(params[1], true);
|
||||
if (strMethod == "gettxout" && n > 1) ConvertTo<boost::int64_t>(params[1]);
|
||||
if (strMethod == "gettxout" && n > 2) ConvertTo<bool>(params[2]);
|
||||
if (strMethod == "lockunspent" && n > 0) ConvertTo<bool>(params[0]);
|
||||
if (strMethod == "lockunspent" && n > 1) ConvertTo<Array>(params[1]);
|
||||
if (strMethod == "importprivkey" && n > 2) ConvertTo<bool>(params[2]);
|
||||
if (strMethod == "verifychain" && n > 0) ConvertTo<boost::int64_t>(params[0]);
|
||||
if (strMethod == "verifychain" && n > 1) ConvertTo<boost::int64_t>(params[1]);
|
||||
if (strMethod == "keypoolrefill" && n > 0) ConvertTo<boost::int64_t>(params[0]);
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
int CommandLineRPC(int argc, char *argv[])
|
||||
{
|
||||
string strPrint;
|
||||
int nRet = 0;
|
||||
try
|
||||
{
|
||||
// Skip switches
|
||||
while (argc > 1 && IsSwitchChar(argv[1][0]))
|
||||
{
|
||||
argc--;
|
||||
argv++;
|
||||
}
|
||||
|
||||
// Method
|
||||
if (argc < 2)
|
||||
throw runtime_error("too few parameters");
|
||||
string strMethod = argv[1];
|
||||
|
||||
// Parameters default to strings
|
||||
std::vector<std::string> strParams(&argv[2], &argv[argc]);
|
||||
Array params = RPCConvertValues(strMethod, strParams);
|
||||
|
||||
// Execute
|
||||
Object reply = CallRPC(strMethod, params);
|
||||
|
||||
// Parse reply
|
||||
const Value& result = find_value(reply, "result");
|
||||
const Value& error = find_value(reply, "error");
|
||||
|
||||
if (error.type() != null_type)
|
||||
{
|
||||
// Error
|
||||
strPrint = "error: " + write_string(error, false);
|
||||
int code = find_value(error.get_obj(), "code").get_int();
|
||||
nRet = abs(code);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Result
|
||||
if (result.type() == null_type)
|
||||
strPrint = "";
|
||||
else if (result.type() == str_type)
|
||||
strPrint = result.get_str();
|
||||
else
|
||||
strPrint = write_string(result, true);
|
||||
}
|
||||
}
|
||||
catch (boost::thread_interrupted) {
|
||||
throw;
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
strPrint = string("error: ") + e.what();
|
||||
nRet = 87;
|
||||
}
|
||||
catch (...) {
|
||||
PrintException(NULL, "CommandLineRPC()");
|
||||
}
|
||||
|
||||
if (strPrint != "")
|
||||
{
|
||||
fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
|
||||
}
|
||||
return nRet;
|
||||
}
|
17
src/rpcclient.h
Normal file
17
src/rpcclient.h
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright (c) 2010 Satoshi Nakamoto
|
||||
// Copyright (c) 2009-2013 The Bitcoin developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#ifndef _BITCOINRPC_CLIENT_H_
|
||||
#define _BITCOINRPC_CLIENT_H_ 1
|
||||
|
||||
#include "json/json_spirit_reader_template.h"
|
||||
#include "json/json_spirit_utils.h"
|
||||
#include "json/json_spirit_writer_template.h"
|
||||
|
||||
int CommandLineRPC(int argc, char *argv[]);
|
||||
|
||||
json_spirit::Array RPCConvertValues(const std::string &strMethod, const std::vector<std::string> &strParams);
|
||||
|
||||
#endif
|
@ -2,10 +2,8 @@
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
|
||||
|
||||
#include "base58.h"
|
||||
#include "bitcoinrpc.h"
|
||||
#include "rpcserver.h"
|
||||
#include "init.h"
|
||||
#include "main.h"
|
||||
#include "sync.h"
|
||||
|
@ -3,7 +3,7 @@
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "bitcoinrpc.h"
|
||||
#include "rpcserver.h"
|
||||
#include "chainparams.h"
|
||||
#include "db.h"
|
||||
#include "init.h"
|
||||
|
@ -2,9 +2,7 @@
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
|
||||
|
||||
#include "bitcoinrpc.h"
|
||||
#include "rpcserver.h"
|
||||
#include "net.h"
|
||||
#include "netbase.h"
|
||||
#include "protocol.h"
|
||||
|
262
src/rpcprotocol.cpp
Normal file
262
src/rpcprotocol.cpp
Normal file
@ -0,0 +1,262 @@
|
||||
// Copyright (c) 2010 Satoshi Nakamoto
|
||||
// Copyright (c) 2009-2013 The Bitcoin developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "rpcprotocol.h"
|
||||
|
||||
#include "util.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/asio/ssl.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/iostreams/concepts.hpp>
|
||||
#include <boost/iostreams/stream.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include "json/json_spirit_writer_template.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace boost;
|
||||
using namespace boost::asio;
|
||||
using namespace json_spirit;
|
||||
|
||||
//
|
||||
// HTTP protocol
|
||||
//
|
||||
// This ain't Apache. We're just using HTTP header for the length field
|
||||
// and to be compatible with other JSON-RPC implementations.
|
||||
//
|
||||
|
||||
string HTTPPost(const string& strMsg, const map<string,string>& mapRequestHeaders)
|
||||
{
|
||||
ostringstream s;
|
||||
s << "POST / HTTP/1.1\r\n"
|
||||
<< "User-Agent: bitcoin-json-rpc/" << FormatFullVersion() << "\r\n"
|
||||
<< "Host: 127.0.0.1\r\n"
|
||||
<< "Content-Type: application/json\r\n"
|
||||
<< "Content-Length: " << strMsg.size() << "\r\n"
|
||||
<< "Connection: close\r\n"
|
||||
<< "Accept: application/json\r\n";
|
||||
BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapRequestHeaders)
|
||||
s << item.first << ": " << item.second << "\r\n";
|
||||
s << "\r\n" << strMsg;
|
||||
|
||||
return s.str();
|
||||
}
|
||||
|
||||
static string rfc1123Time()
|
||||
{
|
||||
char buffer[64];
|
||||
time_t now;
|
||||
time(&now);
|
||||
struct tm* now_gmt = gmtime(&now);
|
||||
string locale(setlocale(LC_TIME, NULL));
|
||||
setlocale(LC_TIME, "C"); // we want POSIX (aka "C") weekday/month strings
|
||||
strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S +0000", now_gmt);
|
||||
setlocale(LC_TIME, locale.c_str());
|
||||
return string(buffer);
|
||||
}
|
||||
|
||||
string HTTPReply(int nStatus, const string& strMsg, bool keepalive)
|
||||
{
|
||||
if (nStatus == HTTP_UNAUTHORIZED)
|
||||
return strprintf("HTTP/1.0 401 Authorization Required\r\n"
|
||||
"Date: %s\r\n"
|
||||
"Server: bitcoin-json-rpc/%s\r\n"
|
||||
"WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n"
|
||||
"Content-Type: text/html\r\n"
|
||||
"Content-Length: 296\r\n"
|
||||
"\r\n"
|
||||
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\r\n"
|
||||
"\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">\r\n"
|
||||
"<HTML>\r\n"
|
||||
"<HEAD>\r\n"
|
||||
"<TITLE>Error</TITLE>\r\n"
|
||||
"<META HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=ISO-8859-1'>\r\n"
|
||||
"</HEAD>\r\n"
|
||||
"<BODY><H1>401 Unauthorized.</H1></BODY>\r\n"
|
||||
"</HTML>\r\n", rfc1123Time().c_str(), FormatFullVersion().c_str());
|
||||
const char *cStatus;
|
||||
if (nStatus == HTTP_OK) cStatus = "OK";
|
||||
else if (nStatus == HTTP_BAD_REQUEST) cStatus = "Bad Request";
|
||||
else if (nStatus == HTTP_FORBIDDEN) cStatus = "Forbidden";
|
||||
else if (nStatus == HTTP_NOT_FOUND) cStatus = "Not Found";
|
||||
else if (nStatus == HTTP_INTERNAL_SERVER_ERROR) cStatus = "Internal Server Error";
|
||||
else cStatus = "";
|
||||
return strprintf(
|
||||
"HTTP/1.1 %d %s\r\n"
|
||||
"Date: %s\r\n"
|
||||
"Connection: %s\r\n"
|
||||
"Content-Length: %"PRIszu"\r\n"
|
||||
"Content-Type: application/json\r\n"
|
||||
"Server: bitcoin-json-rpc/%s\r\n"
|
||||
"\r\n"
|
||||
"%s",
|
||||
nStatus,
|
||||
cStatus,
|
||||
rfc1123Time().c_str(),
|
||||
keepalive ? "keep-alive" : "close",
|
||||
strMsg.size(),
|
||||
FormatFullVersion().c_str(),
|
||||
strMsg.c_str());
|
||||
}
|
||||
|
||||
bool ReadHTTPRequestLine(std::basic_istream<char>& stream, int &proto,
|
||||
string& http_method, string& http_uri)
|
||||
{
|
||||
string str;
|
||||
getline(stream, str);
|
||||
|
||||
// HTTP request line is space-delimited
|
||||
vector<string> vWords;
|
||||
boost::split(vWords, str, boost::is_any_of(" "));
|
||||
if (vWords.size() < 2)
|
||||
return false;
|
||||
|
||||
// HTTP methods permitted: GET, POST
|
||||
http_method = vWords[0];
|
||||
if (http_method != "GET" && http_method != "POST")
|
||||
return false;
|
||||
|
||||
// HTTP URI must be an absolute path, relative to current host
|
||||
http_uri = vWords[1];
|
||||
if (http_uri.size() == 0 || http_uri[0] != '/')
|
||||
return false;
|
||||
|
||||
// parse proto, if present
|
||||
string strProto = "";
|
||||
if (vWords.size() > 2)
|
||||
strProto = vWords[2];
|
||||
|
||||
proto = 0;
|
||||
const char *ver = strstr(strProto.c_str(), "HTTP/1.");
|
||||
if (ver != NULL)
|
||||
proto = atoi(ver+7);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int ReadHTTPStatus(std::basic_istream<char>& stream, int &proto)
|
||||
{
|
||||
string str;
|
||||
getline(stream, str);
|
||||
vector<string> vWords;
|
||||
boost::split(vWords, str, boost::is_any_of(" "));
|
||||
if (vWords.size() < 2)
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
proto = 0;
|
||||
const char *ver = strstr(str.c_str(), "HTTP/1.");
|
||||
if (ver != NULL)
|
||||
proto = atoi(ver+7);
|
||||
return atoi(vWords[1].c_str());
|
||||
}
|
||||
|
||||
int ReadHTTPHeaders(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet)
|
||||
{
|
||||
int nLen = 0;
|
||||
while (true)
|
||||
{
|
||||
string str;
|
||||
std::getline(stream, str);
|
||||
if (str.empty() || str == "\r")
|
||||
break;
|
||||
string::size_type nColon = str.find(":");
|
||||
if (nColon != string::npos)
|
||||
{
|
||||
string strHeader = str.substr(0, nColon);
|
||||
boost::trim(strHeader);
|
||||
boost::to_lower(strHeader);
|
||||
string strValue = str.substr(nColon+1);
|
||||
boost::trim(strValue);
|
||||
mapHeadersRet[strHeader] = strValue;
|
||||
if (strHeader == "content-length")
|
||||
nLen = atoi(strValue.c_str());
|
||||
}
|
||||
}
|
||||
return nLen;
|
||||
}
|
||||
|
||||
|
||||
int ReadHTTPMessage(std::basic_istream<char>& stream, map<string,
|
||||
string>& mapHeadersRet, string& strMessageRet,
|
||||
int nProto)
|
||||
{
|
||||
mapHeadersRet.clear();
|
||||
strMessageRet = "";
|
||||
|
||||
// Read header
|
||||
int nLen = ReadHTTPHeaders(stream, mapHeadersRet);
|
||||
if (nLen < 0 || nLen > (int)MAX_SIZE)
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
|
||||
// Read message
|
||||
if (nLen > 0)
|
||||
{
|
||||
vector<char> vch(nLen);
|
||||
stream.read(&vch[0], nLen);
|
||||
strMessageRet = string(vch.begin(), vch.end());
|
||||
}
|
||||
|
||||
string sConHdr = mapHeadersRet["connection"];
|
||||
|
||||
if ((sConHdr != "close") && (sConHdr != "keep-alive"))
|
||||
{
|
||||
if (nProto >= 1)
|
||||
mapHeadersRet["connection"] = "keep-alive";
|
||||
else
|
||||
mapHeadersRet["connection"] = "close";
|
||||
}
|
||||
|
||||
return HTTP_OK;
|
||||
}
|
||||
|
||||
//
|
||||
// JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility,
|
||||
// but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were
|
||||
// unspecified (HTTP errors and contents of 'error').
|
||||
//
|
||||
// 1.0 spec: http://json-rpc.org/wiki/specification
|
||||
// 1.2 spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http
|
||||
// http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx
|
||||
//
|
||||
|
||||
string JSONRPCRequest(const string& strMethod, const Array& params, const Value& id)
|
||||
{
|
||||
Object request;
|
||||
request.push_back(Pair("method", strMethod));
|
||||
request.push_back(Pair("params", params));
|
||||
request.push_back(Pair("id", id));
|
||||
return write_string(Value(request), false) + "\n";
|
||||
}
|
||||
|
||||
Object JSONRPCReplyObj(const Value& result, const Value& error, const Value& id)
|
||||
{
|
||||
Object reply;
|
||||
if (error.type() != null_type)
|
||||
reply.push_back(Pair("result", Value::null));
|
||||
else
|
||||
reply.push_back(Pair("result", result));
|
||||
reply.push_back(Pair("error", error));
|
||||
reply.push_back(Pair("id", id));
|
||||
return reply;
|
||||
}
|
||||
|
||||
string JSONRPCReply(const Value& result, const Value& error, const Value& id)
|
||||
{
|
||||
Object reply = JSONRPCReplyObj(result, error, id);
|
||||
return write_string(Value(reply), false) + "\n";
|
||||
}
|
||||
|
||||
Object JSONRPCError(int code, const string& message)
|
||||
{
|
||||
Object error;
|
||||
error.push_back(Pair("code", code));
|
||||
error.push_back(Pair("message", message));
|
||||
return error;
|
||||
}
|
138
src/rpcprotocol.h
Normal file
138
src/rpcprotocol.h
Normal file
@ -0,0 +1,138 @@
|
||||
// Copyright (c) 2010 Satoshi Nakamoto
|
||||
// Copyright (c) 2009-2013 The Bitcoin developers
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#ifndef _BITCOINRPC_PROTOCOL_H_
|
||||
#define _BITCOINRPC_PROTOCOL_H_ 1
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <stdint.h>
|
||||
#include <string>
|
||||
#include <boost/iostreams/concepts.hpp>
|
||||
#include <boost/iostreams/stream.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/asio/ssl.hpp>
|
||||
|
||||
#include "json/json_spirit_reader_template.h"
|
||||
#include "json/json_spirit_utils.h"
|
||||
#include "json/json_spirit_writer_template.h"
|
||||
|
||||
// HTTP status codes
|
||||
enum HTTPStatusCode
|
||||
{
|
||||
HTTP_OK = 200,
|
||||
HTTP_BAD_REQUEST = 400,
|
||||
HTTP_UNAUTHORIZED = 401,
|
||||
HTTP_FORBIDDEN = 403,
|
||||
HTTP_NOT_FOUND = 404,
|
||||
HTTP_INTERNAL_SERVER_ERROR = 500,
|
||||
};
|
||||
|
||||
// Bitcoin RPC error codes
|
||||
enum RPCErrorCode
|
||||
{
|
||||
// Standard JSON-RPC 2.0 errors
|
||||
RPC_INVALID_REQUEST = -32600,
|
||||
RPC_METHOD_NOT_FOUND = -32601,
|
||||
RPC_INVALID_PARAMS = -32602,
|
||||
RPC_INTERNAL_ERROR = -32603,
|
||||
RPC_PARSE_ERROR = -32700,
|
||||
|
||||
// General application defined errors
|
||||
RPC_MISC_ERROR = -1, // std::exception thrown in command handling
|
||||
RPC_FORBIDDEN_BY_SAFE_MODE = -2, // Server is in safe mode, and command is not allowed in safe mode
|
||||
RPC_TYPE_ERROR = -3, // Unexpected type was passed as parameter
|
||||
RPC_INVALID_ADDRESS_OR_KEY = -5, // Invalid address or key
|
||||
RPC_OUT_OF_MEMORY = -7, // Ran out of memory during operation
|
||||
RPC_INVALID_PARAMETER = -8, // Invalid, missing or duplicate parameter
|
||||
RPC_DATABASE_ERROR = -20, // Database error
|
||||
RPC_DESERIALIZATION_ERROR = -22, // Error parsing or validating structure in raw format
|
||||
RPC_SERVER_NOT_STARTED = -18, // RPC server was not started (StartRPCThreads() not called)
|
||||
|
||||
// P2P client errors
|
||||
RPC_CLIENT_NOT_CONNECTED = -9, // Bitcoin is not connected
|
||||
RPC_CLIENT_IN_INITIAL_DOWNLOAD = -10, // Still downloading initial blocks
|
||||
RPC_CLIENT_NODE_ALREADY_ADDED = -23, // Node is already added
|
||||
RPC_CLIENT_NODE_NOT_ADDED = -24, // Node has not been added before
|
||||
|
||||
// Wallet errors
|
||||
RPC_WALLET_ERROR = -4, // Unspecified problem with wallet (key not found etc.)
|
||||
RPC_WALLET_INSUFFICIENT_FUNDS = -6, // Not enough funds in wallet or account
|
||||
RPC_WALLET_INVALID_ACCOUNT_NAME = -11, // Invalid account name
|
||||
RPC_WALLET_KEYPOOL_RAN_OUT = -12, // Keypool ran out, call keypoolrefill first
|
||||
RPC_WALLET_UNLOCK_NEEDED = -13, // Enter the wallet passphrase with walletpassphrase first
|
||||
RPC_WALLET_PASSPHRASE_INCORRECT = -14, // The wallet passphrase entered was incorrect
|
||||
RPC_WALLET_WRONG_ENC_STATE = -15, // Command given in wrong wallet encryption state (encrypting an encrypted wallet etc.)
|
||||
RPC_WALLET_ENCRYPTION_FAILED = -16, // Failed to encrypt the wallet
|
||||
RPC_WALLET_ALREADY_UNLOCKED = -17, // Wallet is already unlocked
|
||||
};
|
||||
|
||||
//
|
||||
// IOStream device that speaks SSL but can also speak non-SSL
|
||||
//
|
||||
template <typename Protocol>
|
||||
class SSLIOStreamDevice : public boost::iostreams::device<boost::iostreams::bidirectional> {
|
||||
public:
|
||||
SSLIOStreamDevice(boost::asio::ssl::stream<typename Protocol::socket> &streamIn, bool fUseSSLIn) : stream(streamIn)
|
||||
{
|
||||
fUseSSL = fUseSSLIn;
|
||||
fNeedHandshake = fUseSSLIn;
|
||||
}
|
||||
|
||||
void handshake(boost::asio::ssl::stream_base::handshake_type role)
|
||||
{
|
||||
if (!fNeedHandshake) return;
|
||||
fNeedHandshake = false;
|
||||
stream.handshake(role);
|
||||
}
|
||||
std::streamsize read(char* s, std::streamsize n)
|
||||
{
|
||||
handshake(boost::asio::ssl::stream_base::server); // HTTPS servers read first
|
||||
if (fUseSSL) return stream.read_some(boost::asio::buffer(s, n));
|
||||
return stream.next_layer().read_some(boost::asio::buffer(s, n));
|
||||
}
|
||||
std::streamsize write(const char* s, std::streamsize n)
|
||||
{
|
||||
handshake(boost::asio::ssl::stream_base::client); // HTTPS clients write first
|
||||
if (fUseSSL) return boost::asio::write(stream, boost::asio::buffer(s, n));
|
||||
return boost::asio::write(stream.next_layer(), boost::asio::buffer(s, n));
|
||||
}
|
||||
bool connect(const std::string& server, const std::string& port)
|
||||
{
|
||||
boost::asio::ip::tcp::resolver resolver(stream.get_io_service());
|
||||
boost::asio::ip::tcp::resolver::query query(server.c_str(), port.c_str());
|
||||
boost::asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
|
||||
boost::asio::ip::tcp::resolver::iterator end;
|
||||
boost::system::error_code error = boost::asio::error::host_not_found;
|
||||
while (error && endpoint_iterator != end)
|
||||
{
|
||||
stream.lowest_layer().close();
|
||||
stream.lowest_layer().connect(*endpoint_iterator++, error);
|
||||
}
|
||||
if (error)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
bool fNeedHandshake;
|
||||
bool fUseSSL;
|
||||
boost::asio::ssl::stream<typename Protocol::socket>& stream;
|
||||
};
|
||||
|
||||
std::string HTTPPost(const std::string& strMsg, const std::map<std::string,std::string>& mapRequestHeaders);
|
||||
std::string HTTPReply(int nStatus, const std::string& strMsg, bool keepalive);
|
||||
bool ReadHTTPRequestLine(std::basic_istream<char>& stream, int &proto,
|
||||
std::string& http_method, std::string& http_uri);
|
||||
int ReadHTTPStatus(std::basic_istream<char>& stream, int &proto);
|
||||
int ReadHTTPHeaders(std::basic_istream<char>& stream, std::map<std::string, std::string>& mapHeadersRet);
|
||||
int ReadHTTPMessage(std::basic_istream<char>& stream, std::map<std::string, std::string>& mapHeadersRet,
|
||||
std::string& strMessageRet, int nProto);
|
||||
std::string JSONRPCRequest(const std::string& strMethod, const json_spirit::Array& params, const json_spirit::Value& id);
|
||||
json_spirit::Object JSONRPCReplyObj(const json_spirit::Value& result, const json_spirit::Value& error, const json_spirit::Value& id);
|
||||
std::string JSONRPCReply(const json_spirit::Value& result, const json_spirit::Value& error, const json_spirit::Value& id);
|
||||
json_spirit::Object JSONRPCError(int code, const std::string& message);
|
||||
|
||||
#endif
|
@ -3,10 +3,8 @@
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
|
||||
|
||||
#include "base58.h"
|
||||
#include "bitcoinrpc.h"
|
||||
#include "rpcserver.h"
|
||||
#include "init.h"
|
||||
#include "net.h"
|
||||
#include "uint256.h"
|
||||
|
@ -3,7 +3,7 @@
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include "bitcoinrpc.h"
|
||||
#include "rpcserver.h"
|
||||
|
||||
#include "base58.h"
|
||||
#include "init.h"
|
||||
@ -36,14 +36,6 @@ static map<string, boost::shared_ptr<deadline_timer> > deadlineTimers;
|
||||
static ssl::context* rpc_ssl_context = NULL;
|
||||
static boost::thread_group* rpc_worker_group = NULL;
|
||||
|
||||
Object JSONRPCError(int code, const string& message)
|
||||
{
|
||||
Object error;
|
||||
error.push_back(Pair("code", code));
|
||||
error.push_back(Pair("message", message));
|
||||
return error;
|
||||
}
|
||||
|
||||
void RPCTypeCheck(const Array& params,
|
||||
const list<Value_type>& typesExpected,
|
||||
bool fAllowNull)
|
||||
@ -318,194 +310,6 @@ const CRPCCommand *CRPCTable::operator[](string name) const
|
||||
return (*it).second;
|
||||
}
|
||||
|
||||
//
|
||||
// HTTP protocol
|
||||
//
|
||||
// This ain't Apache. We're just using HTTP header for the length field
|
||||
// and to be compatible with other JSON-RPC implementations.
|
||||
//
|
||||
|
||||
string HTTPPost(const string& strMsg, const map<string,string>& mapRequestHeaders)
|
||||
{
|
||||
ostringstream s;
|
||||
s << "POST / HTTP/1.1\r\n"
|
||||
<< "User-Agent: bitcoin-json-rpc/" << FormatFullVersion() << "\r\n"
|
||||
<< "Host: 127.0.0.1\r\n"
|
||||
<< "Content-Type: application/json\r\n"
|
||||
<< "Content-Length: " << strMsg.size() << "\r\n"
|
||||
<< "Connection: close\r\n"
|
||||
<< "Accept: application/json\r\n";
|
||||
BOOST_FOREACH(const PAIRTYPE(string, string)& item, mapRequestHeaders)
|
||||
s << item.first << ": " << item.second << "\r\n";
|
||||
s << "\r\n" << strMsg;
|
||||
|
||||
return s.str();
|
||||
}
|
||||
|
||||
string rfc1123Time()
|
||||
{
|
||||
char buffer[64];
|
||||
time_t now;
|
||||
time(&now);
|
||||
struct tm* now_gmt = gmtime(&now);
|
||||
string locale(setlocale(LC_TIME, NULL));
|
||||
setlocale(LC_TIME, "C"); // we want POSIX (aka "C") weekday/month strings
|
||||
strftime(buffer, sizeof(buffer), "%a, %d %b %Y %H:%M:%S +0000", now_gmt);
|
||||
setlocale(LC_TIME, locale.c_str());
|
||||
return string(buffer);
|
||||
}
|
||||
|
||||
static string HTTPReply(int nStatus, const string& strMsg, bool keepalive)
|
||||
{
|
||||
if (nStatus == HTTP_UNAUTHORIZED)
|
||||
return strprintf("HTTP/1.0 401 Authorization Required\r\n"
|
||||
"Date: %s\r\n"
|
||||
"Server: bitcoin-json-rpc/%s\r\n"
|
||||
"WWW-Authenticate: Basic realm=\"jsonrpc\"\r\n"
|
||||
"Content-Type: text/html\r\n"
|
||||
"Content-Length: 296\r\n"
|
||||
"\r\n"
|
||||
"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"\r\n"
|
||||
"\"http://www.w3.org/TR/1999/REC-html401-19991224/loose.dtd\">\r\n"
|
||||
"<HTML>\r\n"
|
||||
"<HEAD>\r\n"
|
||||
"<TITLE>Error</TITLE>\r\n"
|
||||
"<META HTTP-EQUIV='Content-Type' CONTENT='text/html; charset=ISO-8859-1'>\r\n"
|
||||
"</HEAD>\r\n"
|
||||
"<BODY><H1>401 Unauthorized.</H1></BODY>\r\n"
|
||||
"</HTML>\r\n", rfc1123Time().c_str(), FormatFullVersion().c_str());
|
||||
const char *cStatus;
|
||||
if (nStatus == HTTP_OK) cStatus = "OK";
|
||||
else if (nStatus == HTTP_BAD_REQUEST) cStatus = "Bad Request";
|
||||
else if (nStatus == HTTP_FORBIDDEN) cStatus = "Forbidden";
|
||||
else if (nStatus == HTTP_NOT_FOUND) cStatus = "Not Found";
|
||||
else if (nStatus == HTTP_INTERNAL_SERVER_ERROR) cStatus = "Internal Server Error";
|
||||
else cStatus = "";
|
||||
return strprintf(
|
||||
"HTTP/1.1 %d %s\r\n"
|
||||
"Date: %s\r\n"
|
||||
"Connection: %s\r\n"
|
||||
"Content-Length: %"PRIszu"\r\n"
|
||||
"Content-Type: application/json\r\n"
|
||||
"Server: bitcoin-json-rpc/%s\r\n"
|
||||
"\r\n"
|
||||
"%s",
|
||||
nStatus,
|
||||
cStatus,
|
||||
rfc1123Time().c_str(),
|
||||
keepalive ? "keep-alive" : "close",
|
||||
strMsg.size(),
|
||||
FormatFullVersion().c_str(),
|
||||
strMsg.c_str());
|
||||
}
|
||||
|
||||
bool ReadHTTPRequestLine(std::basic_istream<char>& stream, int &proto,
|
||||
string& http_method, string& http_uri)
|
||||
{
|
||||
string str;
|
||||
getline(stream, str);
|
||||
|
||||
// HTTP request line is space-delimited
|
||||
vector<string> vWords;
|
||||
boost::split(vWords, str, boost::is_any_of(" "));
|
||||
if (vWords.size() < 2)
|
||||
return false;
|
||||
|
||||
// HTTP methods permitted: GET, POST
|
||||
http_method = vWords[0];
|
||||
if (http_method != "GET" && http_method != "POST")
|
||||
return false;
|
||||
|
||||
// HTTP URI must be an absolute path, relative to current host
|
||||
http_uri = vWords[1];
|
||||
if (http_uri.size() == 0 || http_uri[0] != '/')
|
||||
return false;
|
||||
|
||||
// parse proto, if present
|
||||
string strProto = "";
|
||||
if (vWords.size() > 2)
|
||||
strProto = vWords[2];
|
||||
|
||||
proto = 0;
|
||||
const char *ver = strstr(strProto.c_str(), "HTTP/1.");
|
||||
if (ver != NULL)
|
||||
proto = atoi(ver+7);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int ReadHTTPStatus(std::basic_istream<char>& stream, int &proto)
|
||||
{
|
||||
string str;
|
||||
getline(stream, str);
|
||||
vector<string> vWords;
|
||||
boost::split(vWords, str, boost::is_any_of(" "));
|
||||
if (vWords.size() < 2)
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
proto = 0;
|
||||
const char *ver = strstr(str.c_str(), "HTTP/1.");
|
||||
if (ver != NULL)
|
||||
proto = atoi(ver+7);
|
||||
return atoi(vWords[1].c_str());
|
||||
}
|
||||
|
||||
int ReadHTTPHeaders(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet)
|
||||
{
|
||||
int nLen = 0;
|
||||
while (true)
|
||||
{
|
||||
string str;
|
||||
std::getline(stream, str);
|
||||
if (str.empty() || str == "\r")
|
||||
break;
|
||||
string::size_type nColon = str.find(":");
|
||||
if (nColon != string::npos)
|
||||
{
|
||||
string strHeader = str.substr(0, nColon);
|
||||
boost::trim(strHeader);
|
||||
boost::to_lower(strHeader);
|
||||
string strValue = str.substr(nColon+1);
|
||||
boost::trim(strValue);
|
||||
mapHeadersRet[strHeader] = strValue;
|
||||
if (strHeader == "content-length")
|
||||
nLen = atoi(strValue.c_str());
|
||||
}
|
||||
}
|
||||
return nLen;
|
||||
}
|
||||
|
||||
int ReadHTTPMessage(std::basic_istream<char>& stream, map<string,
|
||||
string>& mapHeadersRet, string& strMessageRet,
|
||||
int nProto)
|
||||
{
|
||||
mapHeadersRet.clear();
|
||||
strMessageRet = "";
|
||||
|
||||
// Read header
|
||||
int nLen = ReadHTTPHeaders(stream, mapHeadersRet);
|
||||
if (nLen < 0 || nLen > (int)MAX_SIZE)
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
|
||||
// Read message
|
||||
if (nLen > 0)
|
||||
{
|
||||
vector<char> vch(nLen);
|
||||
stream.read(&vch[0], nLen);
|
||||
strMessageRet = string(vch.begin(), vch.end());
|
||||
}
|
||||
|
||||
string sConHdr = mapHeadersRet["connection"];
|
||||
|
||||
if ((sConHdr != "close") && (sConHdr != "keep-alive"))
|
||||
{
|
||||
if (nProto >= 1)
|
||||
mapHeadersRet["connection"] = "keep-alive";
|
||||
else
|
||||
mapHeadersRet["connection"] = "close";
|
||||
}
|
||||
|
||||
return HTTP_OK;
|
||||
}
|
||||
|
||||
bool HTTPAuthorized(map<string, string>& mapHeaders)
|
||||
{
|
||||
@ -517,43 +321,6 @@ bool HTTPAuthorized(map<string, string>& mapHeaders)
|
||||
return TimingResistantEqual(strUserPass, strRPCUserColonPass);
|
||||
}
|
||||
|
||||
//
|
||||
// JSON-RPC protocol. Bitcoin speaks version 1.0 for maximum compatibility,
|
||||
// but uses JSON-RPC 1.1/2.0 standards for parts of the 1.0 standard that were
|
||||
// unspecified (HTTP errors and contents of 'error').
|
||||
//
|
||||
// 1.0 spec: http://json-rpc.org/wiki/specification
|
||||
// 1.2 spec: http://groups.google.com/group/json-rpc/web/json-rpc-over-http
|
||||
// http://www.codeproject.com/KB/recipes/JSON_Spirit.aspx
|
||||
//
|
||||
|
||||
string JSONRPCRequest(const string& strMethod, const Array& params, const Value& id)
|
||||
{
|
||||
Object request;
|
||||
request.push_back(Pair("method", strMethod));
|
||||
request.push_back(Pair("params", params));
|
||||
request.push_back(Pair("id", id));
|
||||
return write_string(Value(request), false) + "\n";
|
||||
}
|
||||
|
||||
Object JSONRPCReplyObj(const Value& result, const Value& error, const Value& id)
|
||||
{
|
||||
Object reply;
|
||||
if (error.type() != null_type)
|
||||
reply.push_back(Pair("result", Value::null));
|
||||
else
|
||||
reply.push_back(Pair("result", result));
|
||||
reply.push_back(Pair("error", error));
|
||||
reply.push_back(Pair("id", id));
|
||||
return reply;
|
||||
}
|
||||
|
||||
string JSONRPCReply(const Value& result, const Value& error, const Value& id)
|
||||
{
|
||||
Object reply = JSONRPCReplyObj(result, error, id);
|
||||
return write_string(Value(reply), false) + "\n";
|
||||
}
|
||||
|
||||
void ErrorReply(std::ostream& stream, const Object& objError, const Value& id)
|
||||
{
|
||||
// Send error reply from json-rpc error object
|
||||
@ -588,59 +355,6 @@ bool ClientAllowed(const boost::asio::ip::address& address)
|
||||
return false;
|
||||
}
|
||||
|
||||
//
|
||||
// IOStream device that speaks SSL but can also speak non-SSL
|
||||
//
|
||||
template <typename Protocol>
|
||||
class SSLIOStreamDevice : public iostreams::device<iostreams::bidirectional> {
|
||||
public:
|
||||
SSLIOStreamDevice(asio::ssl::stream<typename Protocol::socket> &streamIn, bool fUseSSLIn) : stream(streamIn)
|
||||
{
|
||||
fUseSSL = fUseSSLIn;
|
||||
fNeedHandshake = fUseSSLIn;
|
||||
}
|
||||
|
||||
void handshake(ssl::stream_base::handshake_type role)
|
||||
{
|
||||
if (!fNeedHandshake) return;
|
||||
fNeedHandshake = false;
|
||||
stream.handshake(role);
|
||||
}
|
||||
std::streamsize read(char* s, std::streamsize n)
|
||||
{
|
||||
handshake(ssl::stream_base::server); // HTTPS servers read first
|
||||
if (fUseSSL) return stream.read_some(asio::buffer(s, n));
|
||||
return stream.next_layer().read_some(asio::buffer(s, n));
|
||||
}
|
||||
std::streamsize write(const char* s, std::streamsize n)
|
||||
{
|
||||
handshake(ssl::stream_base::client); // HTTPS clients write first
|
||||
if (fUseSSL) return asio::write(stream, asio::buffer(s, n));
|
||||
return asio::write(stream.next_layer(), asio::buffer(s, n));
|
||||
}
|
||||
bool connect(const std::string& server, const std::string& port)
|
||||
{
|
||||
ip::tcp::resolver resolver(stream.get_io_service());
|
||||
ip::tcp::resolver::query query(server.c_str(), port.c_str());
|
||||
ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
|
||||
ip::tcp::resolver::iterator end;
|
||||
boost::system::error_code error = asio::error::host_not_found;
|
||||
while (error && endpoint_iterator != end)
|
||||
{
|
||||
stream.lowest_layer().close();
|
||||
stream.lowest_layer().connect(*endpoint_iterator++, error);
|
||||
}
|
||||
if (error)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
bool fNeedHandshake;
|
||||
bool fUseSSL;
|
||||
asio::ssl::stream<typename Protocol::socket>& stream;
|
||||
};
|
||||
|
||||
class AcceptedConnection
|
||||
{
|
||||
public:
|
||||
@ -720,6 +434,7 @@ static void RPCListen(boost::shared_ptr< basic_socket_acceptor<Protocol, SocketA
|
||||
boost::asio::placeholders::error));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Accept and handle incoming connection.
|
||||
*/
|
||||
@ -910,7 +625,6 @@ void RPCRunLater(const std::string& name, boost::function<void(void)> func, int6
|
||||
deadlineTimers[name]->async_wait(boost::bind(RPCRunHandler, _1, func));
|
||||
}
|
||||
|
||||
|
||||
class JSONRequest
|
||||
{
|
||||
public:
|
||||
@ -952,6 +666,7 @@ void JSONRequest::parse(const Value& valRequest)
|
||||
throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array");
|
||||
}
|
||||
|
||||
|
||||
static Object JSONRPCExecOne(const Value& req)
|
||||
{
|
||||
Object rpc_result;
|
||||
@ -1105,263 +820,4 @@ json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_s
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Object CallRPC(const string& strMethod, const Array& params)
|
||||
{
|
||||
if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "")
|
||||
throw runtime_error(strprintf(
|
||||
_("You must set rpcpassword=<password> in the configuration file:\n%s\n"
|
||||
"If the file does not exist, create it with owner-readable-only file permissions."),
|
||||
GetConfigFile().string().c_str()));
|
||||
|
||||
// Connect to localhost
|
||||
bool fUseSSL = GetBoolArg("-rpcssl", false);
|
||||
asio::io_service io_service;
|
||||
ssl::context context(io_service, ssl::context::sslv23);
|
||||
context.set_options(ssl::context::no_sslv2);
|
||||
asio::ssl::stream<asio::ip::tcp::socket> sslStream(io_service, context);
|
||||
SSLIOStreamDevice<asio::ip::tcp> d(sslStream, fUseSSL);
|
||||
iostreams::stream< SSLIOStreamDevice<asio::ip::tcp> > stream(d);
|
||||
|
||||
bool fWait = GetBoolArg("-rpcwait", false); // -rpcwait means try until server has started
|
||||
do {
|
||||
bool fConnected = d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", itostr(Params().RPCPort())));
|
||||
if (fConnected) break;
|
||||
if (fWait)
|
||||
MilliSleep(1000);
|
||||
else
|
||||
throw runtime_error("couldn't connect to server");
|
||||
} while (fWait);
|
||||
|
||||
// HTTP basic authentication
|
||||
string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]);
|
||||
map<string, string> mapRequestHeaders;
|
||||
mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64;
|
||||
|
||||
// Send request
|
||||
string strRequest = JSONRPCRequest(strMethod, params, 1);
|
||||
string strPost = HTTPPost(strRequest, mapRequestHeaders);
|
||||
stream << strPost << std::flush;
|
||||
|
||||
// Receive HTTP reply status
|
||||
int nProto = 0;
|
||||
int nStatus = ReadHTTPStatus(stream, nProto);
|
||||
|
||||
// Receive HTTP reply message headers and body
|
||||
map<string, string> mapHeaders;
|
||||
string strReply;
|
||||
ReadHTTPMessage(stream, mapHeaders, strReply, nProto);
|
||||
|
||||
if (nStatus == HTTP_UNAUTHORIZED)
|
||||
throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
|
||||
else if (nStatus >= 400 && nStatus != HTTP_BAD_REQUEST && nStatus != HTTP_NOT_FOUND && nStatus != HTTP_INTERNAL_SERVER_ERROR)
|
||||
throw runtime_error(strprintf("server returned HTTP error %d", nStatus));
|
||||
else if (strReply.empty())
|
||||
throw runtime_error("no response from server");
|
||||
|
||||
// Parse reply
|
||||
Value valReply;
|
||||
if (!read_string(strReply, valReply))
|
||||
throw runtime_error("couldn't parse reply from server");
|
||||
const Object& reply = valReply.get_obj();
|
||||
if (reply.empty())
|
||||
throw runtime_error("expected reply to have result, error and id properties");
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
template<typename T>
|
||||
void ConvertTo(Value& value, bool fAllowNull=false)
|
||||
{
|
||||
if (fAllowNull && value.type() == null_type)
|
||||
return;
|
||||
if (value.type() == str_type)
|
||||
{
|
||||
// reinterpret string as unquoted json value
|
||||
Value value2;
|
||||
string strJSON = value.get_str();
|
||||
if (!read_string(strJSON, value2))
|
||||
throw runtime_error(string("Error parsing JSON:")+strJSON);
|
||||
ConvertTo<T>(value2, fAllowNull);
|
||||
value = value2;
|
||||
}
|
||||
else
|
||||
{
|
||||
value = value.get_value<T>();
|
||||
}
|
||||
}
|
||||
|
||||
// Convert strings to command-specific RPC representation
|
||||
Array RPCConvertValues(const std::string &strMethod, const std::vector<std::string> &strParams)
|
||||
{
|
||||
Array params;
|
||||
BOOST_FOREACH(const std::string ¶m, strParams)
|
||||
params.push_back(param);
|
||||
|
||||
int n = params.size();
|
||||
|
||||
//
|
||||
// Special case non-string parameter types
|
||||
//
|
||||
if (strMethod == "stop" && n > 0) ConvertTo<bool>(params[0]);
|
||||
if (strMethod == "getaddednodeinfo" && n > 0) ConvertTo<bool>(params[0]);
|
||||
if (strMethod == "setgenerate" && n > 0) ConvertTo<bool>(params[0]);
|
||||
if (strMethod == "setgenerate" && n > 1) ConvertTo<boost::int64_t>(params[1]);
|
||||
if (strMethod == "getnetworkhashps" && n > 0) ConvertTo<boost::int64_t>(params[0]);
|
||||
if (strMethod == "getnetworkhashps" && n > 1) ConvertTo<boost::int64_t>(params[1]);
|
||||
if (strMethod == "sendtoaddress" && n > 1) ConvertTo<double>(params[1]);
|
||||
if (strMethod == "settxfee" && n > 0) ConvertTo<double>(params[0]);
|
||||
if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo<boost::int64_t>(params[1]);
|
||||
if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo<boost::int64_t>(params[1]);
|
||||
if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
|
||||
if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo<bool>(params[1]);
|
||||
if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo<boost::int64_t>(params[0]);
|
||||
if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo<bool>(params[1]);
|
||||
if (strMethod == "getbalance" && n > 1) ConvertTo<boost::int64_t>(params[1]);
|
||||
if (strMethod == "getblockhash" && n > 0) ConvertTo<boost::int64_t>(params[0]);
|
||||
if (strMethod == "move" && n > 2) ConvertTo<double>(params[2]);
|
||||
if (strMethod == "move" && n > 3) ConvertTo<boost::int64_t>(params[3]);
|
||||
if (strMethod == "sendfrom" && n > 2) ConvertTo<double>(params[2]);
|
||||
if (strMethod == "sendfrom" && n > 3) ConvertTo<boost::int64_t>(params[3]);
|
||||
if (strMethod == "listtransactions" && n > 1) ConvertTo<boost::int64_t>(params[1]);
|
||||
if (strMethod == "listtransactions" && n > 2) ConvertTo<boost::int64_t>(params[2]);
|
||||
if (strMethod == "listaccounts" && n > 0) ConvertTo<boost::int64_t>(params[0]);
|
||||
if (strMethod == "walletpassphrase" && n > 1) ConvertTo<boost::int64_t>(params[1]);
|
||||
if (strMethod == "getblocktemplate" && n > 0) ConvertTo<Object>(params[0]);
|
||||
if (strMethod == "listsinceblock" && n > 1) ConvertTo<boost::int64_t>(params[1]);
|
||||
if (strMethod == "sendmany" && n > 1) ConvertTo<Object>(params[1]);
|
||||
if (strMethod == "sendmany" && n > 2) ConvertTo<boost::int64_t>(params[2]);
|
||||
if (strMethod == "addmultisigaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
|
||||
if (strMethod == "addmultisigaddress" && n > 1) ConvertTo<Array>(params[1]);
|
||||
if (strMethod == "createmultisig" && n > 0) ConvertTo<boost::int64_t>(params[0]);
|
||||
if (strMethod == "createmultisig" && n > 1) ConvertTo<Array>(params[1]);
|
||||
if (strMethod == "listunspent" && n > 0) ConvertTo<boost::int64_t>(params[0]);
|
||||
if (strMethod == "listunspent" && n > 1) ConvertTo<boost::int64_t>(params[1]);
|
||||
if (strMethod == "listunspent" && n > 2) ConvertTo<Array>(params[2]);
|
||||
if (strMethod == "getblock" && n > 1) ConvertTo<bool>(params[1]);
|
||||
if (strMethod == "getrawtransaction" && n > 1) ConvertTo<boost::int64_t>(params[1]);
|
||||
if (strMethod == "createrawtransaction" && n > 0) ConvertTo<Array>(params[0]);
|
||||
if (strMethod == "createrawtransaction" && n > 1) ConvertTo<Object>(params[1]);
|
||||
if (strMethod == "signrawtransaction" && n > 1) ConvertTo<Array>(params[1], true);
|
||||
if (strMethod == "signrawtransaction" && n > 2) ConvertTo<Array>(params[2], true);
|
||||
if (strMethod == "sendrawtransaction" && n > 1) ConvertTo<bool>(params[1], true);
|
||||
if (strMethod == "gettxout" && n > 1) ConvertTo<boost::int64_t>(params[1]);
|
||||
if (strMethod == "gettxout" && n > 2) ConvertTo<bool>(params[2]);
|
||||
if (strMethod == "lockunspent" && n > 0) ConvertTo<bool>(params[0]);
|
||||
if (strMethod == "lockunspent" && n > 1) ConvertTo<Array>(params[1]);
|
||||
if (strMethod == "importprivkey" && n > 2) ConvertTo<bool>(params[2]);
|
||||
if (strMethod == "verifychain" && n > 0) ConvertTo<boost::int64_t>(params[0]);
|
||||
if (strMethod == "verifychain" && n > 1) ConvertTo<boost::int64_t>(params[1]);
|
||||
if (strMethod == "keypoolrefill" && n > 0) ConvertTo<boost::int64_t>(params[0]);
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
int CommandLineRPC(int argc, char *argv[])
|
||||
{
|
||||
string strPrint;
|
||||
int nRet = 0;
|
||||
try
|
||||
{
|
||||
// Skip switches
|
||||
while (argc > 1 && IsSwitchChar(argv[1][0]))
|
||||
{
|
||||
argc--;
|
||||
argv++;
|
||||
}
|
||||
|
||||
// Method
|
||||
if (argc < 2)
|
||||
throw runtime_error("too few parameters");
|
||||
string strMethod = argv[1];
|
||||
|
||||
// Parameters default to strings
|
||||
std::vector<std::string> strParams(&argv[2], &argv[argc]);
|
||||
Array params = RPCConvertValues(strMethod, strParams);
|
||||
|
||||
// Execute
|
||||
Object reply = CallRPC(strMethod, params);
|
||||
|
||||
// Parse reply
|
||||
const Value& result = find_value(reply, "result");
|
||||
const Value& error = find_value(reply, "error");
|
||||
|
||||
if (error.type() != null_type)
|
||||
{
|
||||
// Error
|
||||
strPrint = "error: " + write_string(error, false);
|
||||
int code = find_value(error.get_obj(), "code").get_int();
|
||||
nRet = abs(code);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Result
|
||||
if (result.type() == null_type)
|
||||
strPrint = "";
|
||||
else if (result.type() == str_type)
|
||||
strPrint = result.get_str();
|
||||
else
|
||||
strPrint = write_string(result, true);
|
||||
}
|
||||
}
|
||||
catch (boost::thread_interrupted) {
|
||||
throw;
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
strPrint = string("error: ") + e.what();
|
||||
nRet = 87;
|
||||
}
|
||||
catch (...) {
|
||||
PrintException(NULL, "CommandLineRPC()");
|
||||
}
|
||||
|
||||
if (strPrint != "")
|
||||
{
|
||||
fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
|
||||
}
|
||||
return nRet;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef TEST
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
#ifdef _MSC_VER
|
||||
// Turn off Microsoft heap dump noise
|
||||
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
|
||||
_CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0));
|
||||
#endif
|
||||
setbuf(stdin, NULL);
|
||||
setbuf(stdout, NULL);
|
||||
setbuf(stderr, NULL);
|
||||
|
||||
try
|
||||
{
|
||||
if (argc >= 2 && string(argv[1]) == "-server")
|
||||
{
|
||||
LogPrintf("server ready\n");
|
||||
ThreadRPCServer(NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
return CommandLineRPC(argc, argv);
|
||||
}
|
||||
}
|
||||
catch (boost::thread_interrupted) {
|
||||
throw;
|
||||
}
|
||||
catch (std::exception& e) {
|
||||
PrintException(&e, "main()");
|
||||
} catch (...) {
|
||||
PrintException(NULL, "main()");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
const CRPCTable tableRPC;
|
@ -3,10 +3,11 @@
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#ifndef _BITCOINRPC_H_
|
||||
#define _BITCOINRPC_H_ 1
|
||||
#ifndef _BITCOINRPC_SERVER_H_
|
||||
#define _BITCOINRPC_SERVER_H_ 1
|
||||
|
||||
#include "uint256.h"
|
||||
#include "rpcprotocol.h"
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
@ -18,66 +19,9 @@
|
||||
#include "json/json_spirit_writer_template.h"
|
||||
|
||||
class CBlockIndex;
|
||||
class CReserveKey;
|
||||
|
||||
// HTTP status codes
|
||||
enum HTTPStatusCode
|
||||
{
|
||||
HTTP_OK = 200,
|
||||
HTTP_BAD_REQUEST = 400,
|
||||
HTTP_UNAUTHORIZED = 401,
|
||||
HTTP_FORBIDDEN = 403,
|
||||
HTTP_NOT_FOUND = 404,
|
||||
HTTP_INTERNAL_SERVER_ERROR = 500,
|
||||
};
|
||||
|
||||
// Bitcoin RPC error codes
|
||||
enum RPCErrorCode
|
||||
{
|
||||
// Standard JSON-RPC 2.0 errors
|
||||
RPC_INVALID_REQUEST = -32600,
|
||||
RPC_METHOD_NOT_FOUND = -32601,
|
||||
RPC_INVALID_PARAMS = -32602,
|
||||
RPC_INTERNAL_ERROR = -32603,
|
||||
RPC_PARSE_ERROR = -32700,
|
||||
|
||||
// General application defined errors
|
||||
RPC_MISC_ERROR = -1, // std::exception thrown in command handling
|
||||
RPC_FORBIDDEN_BY_SAFE_MODE = -2, // Server is in safe mode, and command is not allowed in safe mode
|
||||
RPC_TYPE_ERROR = -3, // Unexpected type was passed as parameter
|
||||
RPC_INVALID_ADDRESS_OR_KEY = -5, // Invalid address or key
|
||||
RPC_OUT_OF_MEMORY = -7, // Ran out of memory during operation
|
||||
RPC_INVALID_PARAMETER = -8, // Invalid, missing or duplicate parameter
|
||||
RPC_DATABASE_ERROR = -20, // Database error
|
||||
RPC_DESERIALIZATION_ERROR = -22, // Error parsing or validating structure in raw format
|
||||
RPC_SERVER_NOT_STARTED = -18, // RPC server was not started (StartRPCThreads() not called)
|
||||
|
||||
// P2P client errors
|
||||
RPC_CLIENT_NOT_CONNECTED = -9, // Bitcoin is not connected
|
||||
RPC_CLIENT_IN_INITIAL_DOWNLOAD = -10, // Still downloading initial blocks
|
||||
RPC_CLIENT_NODE_ALREADY_ADDED = -23, // Node is already added
|
||||
RPC_CLIENT_NODE_NOT_ADDED = -24, // Node has not been added before
|
||||
|
||||
// Wallet errors
|
||||
RPC_WALLET_ERROR = -4, // Unspecified problem with wallet (key not found etc.)
|
||||
RPC_WALLET_INSUFFICIENT_FUNDS = -6, // Not enough funds in wallet or account
|
||||
RPC_WALLET_INVALID_ACCOUNT_NAME = -11, // Invalid account name
|
||||
RPC_WALLET_KEYPOOL_RAN_OUT = -12, // Keypool ran out, call keypoolrefill first
|
||||
RPC_WALLET_UNLOCK_NEEDED = -13, // Enter the wallet passphrase with walletpassphrase first
|
||||
RPC_WALLET_PASSPHRASE_INCORRECT = -14, // The wallet passphrase entered was incorrect
|
||||
RPC_WALLET_WRONG_ENC_STATE = -15, // Command given in wrong wallet encryption state (encrypting an encrypted wallet etc.)
|
||||
RPC_WALLET_ENCRYPTION_FAILED = -16, // Failed to encrypt the wallet
|
||||
RPC_WALLET_ALREADY_UNLOCKED = -17, // Wallet is already unlocked
|
||||
};
|
||||
|
||||
json_spirit::Object JSONRPCError(int code, const std::string& message);
|
||||
|
||||
void StartRPCThreads();
|
||||
void StopRPCThreads();
|
||||
int CommandLineRPC(int argc, char *argv[]);
|
||||
|
||||
/** Convert parameter values for RPC call from strings to command-specific JSON objects. */
|
||||
json_spirit::Array RPCConvertValues(const std::string &strMethod, const std::vector<std::string> &strParams);
|
||||
|
||||
/*
|
||||
Type-check arguments; throws JSONRPCError if wrong type given. Does not check that
|
@ -3,10 +3,8 @@
|
||||
// Distributed under the MIT/X11 software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
|
||||
|
||||
#include "base58.h"
|
||||
#include "bitcoinrpc.h"
|
||||
#include "rpcserver.h"
|
||||
#include "init.h"
|
||||
#include "net.h"
|
||||
#include "netbase.h"
|
||||
|
@ -1,4 +1,5 @@
|
||||
#include "bitcoinrpc.h"
|
||||
#include "rpcserver.h"
|
||||
#include "rpcclient.h"
|
||||
|
||||
#include "base58.h"
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user