Wladimir J. van der Laan
11 years ago
18 changed files with 692 additions and 629 deletions
@ -0,0 +1,246 @@
@@ -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; |
||||
} |
@ -0,0 +1,17 @@
@@ -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 |
@ -0,0 +1,262 @@
@@ -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; |
||||
} |
@ -0,0 +1,138 @@
@@ -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 |
Loading…
Reference in new issue