aboutsummaryrefslogtreecommitdiff
path: root/src/rpcclient.cpp
diff options
context:
space:
mode:
authorWladimir J. van der Laan <laanwj@gmail.com>2013-11-20 14:18:57 +0100
committerWladimir J. van der Laan <laanwj@gmail.com>2013-11-27 06:00:29 +0100
commitfb78cc23784b2fa478324aac35ca76c7cfe683a4 (patch)
tree85b9ec58146c64dbb52b1bcd1fea938055be7500 /src/rpcclient.cpp
parent03b6a1cee4fe7f38ca16c0bc2d08d8d4d1288f2f (diff)
downloadbitcoin-fb78cc23784b2fa478324aac35ca76c7cfe683a4.tar.xz
Split up bitcoinrpc (code movement only)
Split bitcoinrpc up into - rpcserver: bitcoind RPC server - rpcclient: bitcoin-cli RPC client - rpcprotocol: shared common HTTP/JSON-RPC protocol code One step towards making bitcoin-cli independent from the rest of the code, and thus a smaller executable that doesn't have to be linked against leveldb. This commit only does code movement, there are no functional changes.
Diffstat (limited to 'src/rpcclient.cpp')
-rw-r--r--src/rpcclient.cpp246
1 files changed, 246 insertions, 0 deletions
diff --git a/src/rpcclient.cpp b/src/rpcclient.cpp
new file mode 100644
index 0000000000..2667a5d5a5
--- /dev/null
+++ b/src/rpcclient.cpp
@@ -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 &param, 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;
+}