aboutsummaryrefslogtreecommitdiff
path: root/src/bitcoinrpc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/bitcoinrpc.cpp')
-rw-r--r--src/bitcoinrpc.cpp183
1 files changed, 121 insertions, 62 deletions
diff --git a/src/bitcoinrpc.cpp b/src/bitcoinrpc.cpp
index fa3923636..bfb696da3 100644
--- a/src/bitcoinrpc.cpp
+++ b/src/bitcoinrpc.cpp
@@ -9,8 +9,8 @@
#include "ui_interface.h"
#include "base58.h"
#include "bitcoinrpc.h"
+#include "db.h"
-#undef printf
#include <boost/asio.hpp>
#include <boost/asio/ip/v6_only.hpp>
#include <boost/bind.hpp>
@@ -25,8 +25,6 @@
#include <boost/shared_ptr.hpp>
#include <list>
-#define printf OutputDebugStringF
-
using namespace std;
using namespace boost;
using namespace boost::asio;
@@ -40,6 +38,11 @@ const Object emptyobj;
void ThreadRPCServer3(void* parg);
+static inline unsigned short GetDefaultRPCPort()
+{
+ return GetBoolArg("-testnet", false) ? 18332 : 8332;
+}
+
Object JSONRPCError(int code, const string& message)
{
Object error;
@@ -63,7 +66,7 @@ void RPCTypeCheck(const Array& params,
{
string err = strprintf("Expected type %s, got %s",
Value_type_name[t], Value_type_name[v.type()]);
- throw JSONRPCError(-3, err);
+ throw JSONRPCError(RPC_TYPE_ERROR, err);
}
i++;
}
@@ -77,13 +80,13 @@ void RPCTypeCheck(const Object& o,
{
const Value& v = find_value(o, t.first);
if (!fAllowNull && v.type() == null_type)
- throw JSONRPCError(-3, strprintf("Missing %s", t.first.c_str()));
+ throw JSONRPCError(RPC_TYPE_ERROR, strprintf("Missing %s", t.first.c_str()));
if (!((v.type() == t.second) || (fAllowNull && (v.type() == null_type))))
{
string err = strprintf("Expected type %s for %s, got %s",
Value_type_name[t.second], t.first.c_str(), Value_type_name[v.type()]);
- throw JSONRPCError(-3, err);
+ throw JSONRPCError(RPC_TYPE_ERROR, err);
}
}
}
@@ -92,10 +95,10 @@ int64 AmountFromValue(const Value& value)
{
double dAmount = value.get_real();
if (dAmount <= 0.0 || dAmount > 21000000.0)
- throw JSONRPCError(-3, "Invalid amount");
+ throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount");
int64 nAmount = roundint64(dAmount * COIN);
if (!MoneyRange(nAmount))
- throw JSONRPCError(-3, "Invalid amount");
+ throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount");
return nAmount;
}
@@ -173,7 +176,8 @@ Value help(const Array& params, bool fHelp)
Value stop(const Array& params, bool fHelp)
{
- if (fHelp || params.size() != 0)
+ // Accept the deprecated and ignored 'detach´ boolean argument
+ if (fHelp || params.size() > 1)
throw runtime_error(
"stop\n"
"Stop Bitcoin server.");
@@ -225,6 +229,7 @@ static const CRPCCommand vRPCCommands[] =
{ "sendfrom", &sendfrom, false, false },
{ "sendmany", &sendmany, false, false },
{ "addmultisigaddress", &addmultisigaddress, false, false },
+ { "createmultisig", &createmultisig, true, true },
{ "getrawmempool", &getrawmempool, true, false },
{ "getblock", &getblock, false, false },
{ "getblockhash", &getblockhash, false, false },
@@ -247,6 +252,10 @@ static const CRPCCommand vRPCCommands[] =
{ "decoderawtransaction", &decoderawtransaction, false, false },
{ "signrawtransaction", &signrawtransaction, false, false },
{ "sendrawtransaction", &sendrawtransaction, false, false },
+ { "gettxoutsetinfo", &gettxoutsetinfo, true, false },
+ { "gettxout", &gettxout, true, false },
+ { "lockunspent", &lockunspent, false, false },
+ { "listlockunspent", &listlockunspent, false, false },
};
CRPCTable::CRPCTable()
@@ -308,7 +317,7 @@ string rfc1123Time()
static string HTTPReply(int nStatus, const string& strMsg, bool keepalive)
{
- if (nStatus == 401)
+ 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"
@@ -326,17 +335,17 @@ static string HTTPReply(int nStatus, const string& strMsg, bool keepalive)
"<BODY><H1>401 Unauthorized.</H1></BODY>\r\n"
"</HTML>\r\n", rfc1123Time().c_str(), FormatFullVersion().c_str());
const char *cStatus;
- if (nStatus == 200) cStatus = "OK";
- else if (nStatus == 400) cStatus = "Bad Request";
- else if (nStatus == 403) cStatus = "Forbidden";
- else if (nStatus == 404) cStatus = "Not Found";
- else if (nStatus == 500) cStatus = "Internal Server Error";
+ 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: %d\r\n"
+ "Content-Length: %"PRIszu"\r\n"
"Content-Type: application/json\r\n"
"Server: bitcoin-json-rpc/%s\r\n"
"\r\n"
@@ -350,6 +359,41 @@ static string HTTPReply(int nStatus, const string& strMsg, bool keepalive)
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;
@@ -357,7 +401,7 @@ int ReadHTTPStatus(std::basic_istream<char>& stream, int &proto)
vector<string> vWords;
boost::split(vWords, str, boost::is_any_of(" "));
if (vWords.size() < 2)
- return 500;
+ return HTTP_INTERNAL_SERVER_ERROR;
proto = 0;
const char *ver = strstr(str.c_str(), "HTTP/1.");
if (ver != NULL)
@@ -365,7 +409,7 @@ int ReadHTTPStatus(std::basic_istream<char>& stream, int &proto)
return atoi(vWords[1].c_str());
}
-int ReadHTTPHeader(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet)
+int ReadHTTPHeaders(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet)
{
int nLen = 0;
loop
@@ -390,19 +434,17 @@ int ReadHTTPHeader(std::basic_istream<char>& stream, map<string, string>& mapHea
return nLen;
}
-int ReadHTTP(std::basic_istream<char>& stream, map<string, string>& mapHeadersRet, string& strMessageRet)
+int ReadHTTPMessage(std::basic_istream<char>& stream, map<string,
+ string>& mapHeadersRet, string& strMessageRet,
+ int nProto)
{
mapHeadersRet.clear();
strMessageRet = "";
- // Read status
- int nProto = 0;
- int nStatus = ReadHTTPStatus(stream, nProto);
-
// Read header
- int nLen = ReadHTTPHeader(stream, mapHeadersRet);
+ int nLen = ReadHTTPHeaders(stream, mapHeadersRet);
if (nLen < 0 || nLen > (int)MAX_SIZE)
- return 500;
+ return HTTP_INTERNAL_SERVER_ERROR;
// Read message
if (nLen > 0)
@@ -422,7 +464,7 @@ int ReadHTTP(std::basic_istream<char>& stream, map<string, string>& mapHeadersRe
mapHeadersRet["connection"] = "close";
}
- return nStatus;
+ return HTTP_OK;
}
bool HTTPAuthorized(map<string, string>& mapHeaders)
@@ -475,10 +517,10 @@ string JSONRPCReply(const Value& result, const Value& error, const Value& id)
void ErrorReply(std::ostream& stream, const Object& objError, const Value& id)
{
// Send error reply from json-rpc error object
- int nStatus = 500;
+ int nStatus = HTTP_INTERNAL_SERVER_ERROR;
int code = find_value(objError, "code").get_int();
- if (code == -32600) nStatus = 400;
- else if (code == -32601) nStatus = 404;
+ if (code == RPC_INVALID_REQUEST) nStatus = HTTP_BAD_REQUEST;
+ else if (code == RPC_METHOD_NOT_FOUND) nStatus = HTTP_NOT_FOUND;
string strReply = JSONRPCReply(Value::null, objError, id);
stream << HTTPReply(nStatus, strReply, false) << std::flush;
}
@@ -608,8 +650,6 @@ private:
void ThreadRPCServer(void* parg)
{
- IMPLEMENT_RANDOMIZE_STACK(ThreadRPCServer(parg));
-
// Make this thread recognisable as the RPC listener
RenameThread("bitcoin-rpclist");
@@ -692,7 +732,7 @@ static void RPCAcceptHandler(boost::shared_ptr< basic_socket_acceptor<Protocol,
{
// Only send a 403 if we're not using SSL to prevent a DoS during the SSL handshake.
if (!fUseSSL)
- conn->stream() << HTTPReply(403, "", false) << std::flush;
+ conn->stream() << HTTPReply(HTTP_FORBIDDEN, "", false) << std::flush;
delete conn;
}
@@ -710,7 +750,8 @@ void ThreadRPCServer2(void* parg)
printf("ThreadRPCServer started\n");
strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
- if (mapArgs["-rpcpassword"] == "")
+ if ((mapArgs["-rpcpassword"] == "") ||
+ (mapArgs["-rpcuser"] == mapArgs["-rpcpassword"]))
{
unsigned char rand_pwd[32];
RAND_bytes(rand_pwd, 32);
@@ -725,11 +766,12 @@ void ThreadRPCServer2(void* parg)
"rpcuser=bitcoinrpc\n"
"rpcpassword=%s\n"
"(you do not need to remember this password)\n"
+ "The username and password MUST NOT be the same.\n"
"If the file does not exist, create it with owner-readable-only file permissions.\n"),
strWhatAmI.c_str(),
GetConfigFile().string().c_str(),
EncodeBase58(&rand_pwd[0],&rand_pwd[0]+32).c_str()),
- _("Error"), CClientUIInterface::OK | CClientUIInterface::MODAL);
+ "", CClientUIInterface::MSG_ERROR);
StartShutdown();
return;
}
@@ -760,7 +802,7 @@ void ThreadRPCServer2(void* parg)
// Try a dual IPv6/IPv4 socket, falling back to separate IPv4 and IPv6 sockets
const bool loopback = !mapArgs.count("-rpcallowip");
asio::ip::address bindAddress = loopback ? asio::ip::address_v6::loopback() : asio::ip::address_v6::any();
- ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", 8332));
+ ip::tcp::endpoint endpoint(bindAddress, GetArg("-rpcport", GetDefaultRPCPort()));
boost::system::error_code v6_only_error;
boost::shared_ptr<ip::tcp::acceptor> acceptor(new ip::tcp::acceptor(io_service));
@@ -789,7 +831,7 @@ void ThreadRPCServer2(void* parg)
}
catch(boost::system::system_error &e)
{
- strerr = strprintf(_("An error occurred while setting up the RPC port %i for listening on IPv6, falling back to IPv4: %s"), endpoint.port(), e.what());
+ strerr = strprintf(_("An error occurred while setting up the RPC port %u for listening on IPv6, falling back to IPv4: %s"), endpoint.port(), e.what());
}
try {
@@ -816,11 +858,11 @@ void ThreadRPCServer2(void* parg)
}
catch(boost::system::system_error &e)
{
- strerr = strprintf(_("An error occurred while setting up the RPC port %i for listening on IPv4: %s"), endpoint.port(), e.what());
+ strerr = strprintf(_("An error occurred while setting up the RPC port %u for listening on IPv4: %s"), endpoint.port(), e.what());
}
if (!fListening) {
- uiInterface.ThreadSafeMessageBox(strerr, _("Error"), CClientUIInterface::OK | CClientUIInterface::MODAL);
+ uiInterface.ThreadSafeMessageBox(strerr, "", CClientUIInterface::MSG_ERROR);
StartShutdown();
return;
}
@@ -847,7 +889,7 @@ void JSONRequest::parse(const Value& valRequest)
{
// Parse request
if (valRequest.type() != obj_type)
- throw JSONRPCError(-32600, "Invalid Request object");
+ throw JSONRPCError(RPC_INVALID_REQUEST, "Invalid Request object");
const Object& request = valRequest.get_obj();
// Parse id now so errors from here on will have the id
@@ -856,9 +898,9 @@ void JSONRequest::parse(const Value& valRequest)
// Parse method
Value valMethod = find_value(request, "method");
if (valMethod.type() == null_type)
- throw JSONRPCError(-32600, "Missing method");
+ throw JSONRPCError(RPC_INVALID_REQUEST, "Missing method");
if (valMethod.type() != str_type)
- throw JSONRPCError(-32600, "Method must be a string");
+ throw JSONRPCError(RPC_INVALID_REQUEST, "Method must be a string");
strMethod = valMethod.get_str();
if (strMethod != "getwork" && strMethod != "getblocktemplate")
printf("ThreadRPCServer method=%s\n", strMethod.c_str());
@@ -870,7 +912,7 @@ void JSONRequest::parse(const Value& valRequest)
else if (valParams.type() == null_type)
params = Array();
else
- throw JSONRPCError(-32600, "Params must be an array");
+ throw JSONRPCError(RPC_INVALID_REQUEST, "Params must be an array");
}
static Object JSONRPCExecOne(const Value& req)
@@ -891,7 +933,7 @@ static Object JSONRPCExecOne(const Value& req)
catch (std::exception& e)
{
rpc_result = JSONRPCReplyObj(Value::null,
- JSONRPCError(-32700, e.what()), jreq.id);
+ JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
}
return rpc_result;
@@ -910,8 +952,6 @@ static CCriticalSection cs_THREAD_RPCHANDLER;
void ThreadRPCServer3(void* parg)
{
- IMPLEMENT_RANDOMIZE_STACK(ThreadRPCServer3(parg));
-
// Make this thread recognisable as the RPC handler
RenameThread("bitcoin-rpchand");
@@ -933,15 +973,22 @@ void ThreadRPCServer3(void* parg)
}
return;
}
+
+ int nProto = 0;
map<string, string> mapHeaders;
- string strRequest;
+ string strRequest, strMethod, strURI;
- ReadHTTP(conn->stream(), mapHeaders, strRequest);
+ // Read HTTP request line
+ if (!ReadHTTPRequestLine(conn->stream(), nProto, strMethod, strURI))
+ break;
+
+ // Read HTTP message headers and body
+ ReadHTTPMessage(conn->stream(), mapHeaders, strRequest, nProto);
// Check authorization
if (mapHeaders.count("authorization") == 0)
{
- conn->stream() << HTTPReply(401, "", false) << std::flush;
+ conn->stream() << HTTPReply(HTTP_UNAUTHORIZED, "", false) << std::flush;
break;
}
if (!HTTPAuthorized(mapHeaders))
@@ -953,7 +1000,7 @@ void ThreadRPCServer3(void* parg)
if (mapArgs["-rpcpassword"].size() < 20)
Sleep(250);
- conn->stream() << HTTPReply(401, "", false) << std::flush;
+ conn->stream() << HTTPReply(HTTP_UNAUTHORIZED, "", false) << std::flush;
break;
}
if (mapHeaders["connection"] == "close")
@@ -965,7 +1012,7 @@ void ThreadRPCServer3(void* parg)
// Parse request
Value valRequest;
if (!read_string(strRequest, valRequest))
- throw JSONRPCError(-32700, "Parse error");
+ throw JSONRPCError(RPC_PARSE_ERROR, "Parse error");
string strReply;
@@ -982,9 +1029,9 @@ void ThreadRPCServer3(void* parg)
} else if (valRequest.type() == array_type)
strReply = JSONRPCExecBatch(valRequest.get_array());
else
- throw JSONRPCError(-32700, "Top-level object parse error");
-
- conn->stream() << HTTPReply(200, strReply, fRun) << std::flush;
+ throw JSONRPCError(RPC_PARSE_ERROR, "Top-level object parse error");
+
+ conn->stream() << HTTPReply(HTTP_OK, strReply, fRun) << std::flush;
}
catch (Object& objError)
{
@@ -993,7 +1040,7 @@ void ThreadRPCServer3(void* parg)
}
catch (std::exception& e)
{
- ErrorReply(conn->stream(), JSONRPCError(-32700, e.what()), jreq.id);
+ ErrorReply(conn->stream(), JSONRPCError(RPC_PARSE_ERROR, e.what()), jreq.id);
break;
}
}
@@ -1010,13 +1057,13 @@ json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_s
// Find method
const CRPCCommand *pcmd = tableRPC[strMethod];
if (!pcmd)
- throw JSONRPCError(-32601, "Method not found");
+ throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found");
// Observe safe mode
string strWarning = GetWarnings("rpc");
if (strWarning != "" && !GetBoolArg("-disablesafemode") &&
!pcmd->okSafeMode)
- throw JSONRPCError(-2, string("Safe mode: ") + strWarning);
+ throw JSONRPCError(RPC_FORBIDDEN_BY_SAFE_MODE, string("Safe mode: ") + strWarning);
try
{
@@ -1034,7 +1081,7 @@ json_spirit::Value CRPCTable::execute(const std::string &strMethod, const json_s
}
catch (std::exception& e)
{
- throw JSONRPCError(-1, e.what());
+ throw JSONRPCError(RPC_MISC_ERROR, e.what());
}
}
@@ -1055,7 +1102,7 @@ Object CallRPC(const string& strMethod, const Array& params)
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);
- if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", "8332")))
+ if (!d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", itostr(GetDefaultRPCPort()))))
throw runtime_error("couldn't connect to server");
// HTTP basic authentication
@@ -1068,13 +1115,18 @@ Object CallRPC(const string& strMethod, const Array& params)
string strPost = HTTPPost(strRequest, mapRequestHeaders);
stream << strPost << std::flush;
- // Receive reply
+ // 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;
- int nStatus = ReadHTTP(stream, mapHeaders, strReply);
- if (nStatus == 401)
+ ReadHTTPMessage(stream, mapHeaders, strReply, nProto);
+
+ if (nStatus == HTTP_UNAUTHORIZED)
throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
- else if (nStatus >= 400 && nStatus != 400 && nStatus != 404 && nStatus != 500)
+ 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");
@@ -1126,6 +1178,7 @@ Array RPCConvertValues(const std::string &strMethod, const std::vector<std::stri
//
// Special case non-string parameter types
//
+ if (strMethod == "stop" && 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 == "sendtoaddress" && n > 1) ConvertTo<double>(params[1]);
@@ -1152,6 +1205,8 @@ Array RPCConvertValues(const std::string &strMethod, const std::vector<std::stri
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]);
@@ -1160,6 +1215,10 @@ Array RPCConvertValues(const std::string &strMethod, const std::vector<std::stri
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 == "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]);
return params;
}