Merge pull request #3284
fb78cc2
Split up bitcoinrpc (code movement only) (Wladimir J. van der Laan)
This commit is contained in:
commit
5f083afde1
18 changed files with 692 additions and 629 deletions
|
@ -12,7 +12,10 @@ DIST_SUBDIRS = . qt test
|
||||||
.PHONY: FORCE
|
.PHONY: FORCE
|
||||||
# bitcoin core #
|
# bitcoin core #
|
||||||
BITCOIN_CORE_H = addrman.h alert.h allocators.h base58.h bignum.h \
|
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 \
|
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 \
|
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 \
|
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)
|
$(abs_top_srcdir)
|
||||||
version.o: obj/build.h
|
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 \
|
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 \
|
init.cpp key.cpp keystore.cpp leveldbwrapper.cpp main.cpp miner.cpp \
|
||||||
netbase.cpp net.cpp noui.cpp protocol.cpp rpcblockchain.cpp rpcdump.cpp \
|
netbase.cpp net.cpp noui.cpp protocol.cpp rpcblockchain.cpp rpcdump.cpp \
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "init.h"
|
#include "init.h"
|
||||||
#include "bitcoinrpc.h"
|
#include "rpcclient.h"
|
||||||
#include "ui_interface.h" /* for _(...) */
|
#include "ui_interface.h" /* for _(...) */
|
||||||
|
|
||||||
#include <boost/filesystem/operations.hpp>
|
#include <boost/filesystem/operations.hpp>
|
||||||
|
|
|
@ -3,7 +3,8 @@
|
||||||
// Distributed under the MIT/X11 software license, see the accompanying
|
// Distributed under the MIT/X11 software license, see the accompanying
|
||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
#include "bitcoinrpc.h"
|
#include "rpcserver.h"
|
||||||
|
#include "rpcclient.h"
|
||||||
#include "init.h"
|
#include "init.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "noui.h"
|
#include "noui.h"
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
#include "init.h"
|
#include "init.h"
|
||||||
|
|
||||||
#include "addrman.h"
|
#include "addrman.h"
|
||||||
#include "bitcoinrpc.h"
|
#include "rpcserver.h"
|
||||||
#include "checkpoints.h"
|
#include "checkpoints.h"
|
||||||
#include "miner.h"
|
#include "miner.h"
|
||||||
#include "net.h"
|
#include "net.h"
|
||||||
|
|
|
@ -8,7 +8,8 @@
|
||||||
#include "clientmodel.h"
|
#include "clientmodel.h"
|
||||||
#include "guiutil.h"
|
#include "guiutil.h"
|
||||||
|
|
||||||
#include "bitcoinrpc.h"
|
#include "rpcserver.h"
|
||||||
|
#include "rpcclient.h"
|
||||||
|
|
||||||
#include "json/json_spirit_value.h"
|
#include "json/json_spirit_value.h"
|
||||||
#include <openssl/crypto.h>
|
#include <openssl/crypto.h>
|
||||||
|
|
|
@ -3,9 +3,7 @@
|
||||||
// Distributed under the MIT/X11 software license, see the accompanying
|
// Distributed under the MIT/X11 software license, see the accompanying
|
||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#include "rpcserver.h"
|
||||||
|
|
||||||
#include "bitcoinrpc.h"
|
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "sync.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
|
// Distributed under the MIT/X11 software license, see the accompanying
|
||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include "base58.h"
|
#include "base58.h"
|
||||||
#include "bitcoinrpc.h"
|
#include "rpcserver.h"
|
||||||
#include "init.h"
|
#include "init.h"
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "sync.h"
|
#include "sync.h"
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
// Distributed under the MIT/X11 software license, see the accompanying
|
// Distributed under the MIT/X11 software license, see the accompanying
|
||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
#include "bitcoinrpc.h"
|
#include "rpcserver.h"
|
||||||
#include "chainparams.h"
|
#include "chainparams.h"
|
||||||
#include "db.h"
|
#include "db.h"
|
||||||
#include "init.h"
|
#include "init.h"
|
||||||
|
|
|
@ -2,9 +2,7 @@
|
||||||
// Distributed under the MIT/X11 software license, see the accompanying
|
// Distributed under the MIT/X11 software license, see the accompanying
|
||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
#include "rpcserver.h"
|
||||||
|
|
||||||
#include "bitcoinrpc.h"
|
|
||||||
#include "net.h"
|
#include "net.h"
|
||||||
#include "netbase.h"
|
#include "netbase.h"
|
||||||
#include "protocol.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
|
// Distributed under the MIT/X11 software license, see the accompanying
|
||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include "base58.h"
|
#include "base58.h"
|
||||||
#include "bitcoinrpc.h"
|
#include "rpcserver.h"
|
||||||
#include "init.h"
|
#include "init.h"
|
||||||
#include "net.h"
|
#include "net.h"
|
||||||
#include "uint256.h"
|
#include "uint256.h"
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
// Distributed under the MIT/X11 software license, see the accompanying
|
// Distributed under the MIT/X11 software license, see the accompanying
|
||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
#include "bitcoinrpc.h"
|
#include "rpcserver.h"
|
||||||
|
|
||||||
#include "base58.h"
|
#include "base58.h"
|
||||||
#include "init.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 ssl::context* rpc_ssl_context = NULL;
|
||||||
static boost::thread_group* rpc_worker_group = 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,
|
void RPCTypeCheck(const Array& params,
|
||||||
const list<Value_type>& typesExpected,
|
const list<Value_type>& typesExpected,
|
||||||
bool fAllowNull)
|
bool fAllowNull)
|
||||||
|
@ -318,194 +310,6 @@ const CRPCCommand *CRPCTable::operator[](string name) const
|
||||||
return (*it).second;
|
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)
|
bool HTTPAuthorized(map<string, string>& mapHeaders)
|
||||||
{
|
{
|
||||||
|
@ -517,43 +321,6 @@ bool HTTPAuthorized(map<string, string>& mapHeaders)
|
||||||
return TimingResistantEqual(strUserPass, strRPCUserColonPass);
|
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)
|
void ErrorReply(std::ostream& stream, const Object& objError, const Value& id)
|
||||||
{
|
{
|
||||||
// Send error reply from json-rpc error object
|
// Send error reply from json-rpc error object
|
||||||
|
@ -588,59 +355,6 @@ bool ClientAllowed(const boost::asio::ip::address& address)
|
||||||
return false;
|
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
|
class AcceptedConnection
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -720,6 +434,7 @@ static void RPCListen(boost::shared_ptr< basic_socket_acceptor<Protocol, SocketA
|
||||||
boost::asio::placeholders::error));
|
boost::asio::placeholders::error));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Accept and handle incoming connection.
|
* 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));
|
deadlineTimers[name]->async_wait(boost::bind(RPCRunHandler, _1, func));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class JSONRequest
|
class JSONRequest
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -952,6 +666,7 @@ void JSONRequest::parse(const Value& valRequest)
|
||||||
throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array");
|
throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static Object JSONRPCExecOne(const Value& req)
|
static Object JSONRPCExecOne(const Value& req)
|
||||||
{
|
{
|
||||||
Object rpc_result;
|
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;
|
const CRPCTable tableRPC;
|
|
@ -3,10 +3,11 @@
|
||||||
// Distributed under the MIT/X11 software license, see the accompanying
|
// Distributed under the MIT/X11 software license, see the accompanying
|
||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
#ifndef _BITCOINRPC_H_
|
#ifndef _BITCOINRPC_SERVER_H_
|
||||||
#define _BITCOINRPC_H_ 1
|
#define _BITCOINRPC_SERVER_H_ 1
|
||||||
|
|
||||||
#include "uint256.h"
|
#include "uint256.h"
|
||||||
|
#include "rpcprotocol.h"
|
||||||
|
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
@ -18,66 +19,9 @@
|
||||||
#include "json/json_spirit_writer_template.h"
|
#include "json/json_spirit_writer_template.h"
|
||||||
|
|
||||||
class CBlockIndex;
|
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 StartRPCThreads();
|
||||||
void StopRPCThreads();
|
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
|
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
|
// Distributed under the MIT/X11 software license, see the accompanying
|
||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include "base58.h"
|
#include "base58.h"
|
||||||
#include "bitcoinrpc.h"
|
#include "rpcserver.h"
|
||||||
#include "init.h"
|
#include "init.h"
|
||||||
#include "net.h"
|
#include "net.h"
|
||||||
#include "netbase.h"
|
#include "netbase.h"
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
#include "bitcoinrpc.h"
|
#include "rpcserver.h"
|
||||||
|
#include "rpcclient.h"
|
||||||
|
|
||||||
#include "base58.h"
|
#include "base58.h"
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue