1 // Copyright (c) 2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2013 The Bitcoin developers
3 // Distributed under the MIT/X11 software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
8 #include "rpcprotocol.h"
10 #include "ui_interface.h"
11 #include "chainparams.h" // for Params().RPCPort()
15 #include <boost/algorithm/string.hpp>
16 #include <boost/asio.hpp>
17 #include <boost/asio/ssl.hpp>
18 #include <boost/bind.hpp>
19 #include <boost/filesystem.hpp>
20 #include <boost/foreach.hpp>
21 #include <boost/iostreams/concepts.hpp>
22 #include <boost/iostreams/stream.hpp>
23 #include <boost/lexical_cast.hpp>
24 #include <boost/shared_ptr.hpp>
25 #include "json/json_spirit_writer_template.h"
28 using namespace boost;
29 using namespace boost::asio;
30 using namespace json_spirit;
32 Object CallRPC(const string& strMethod, const Array& params)
34 if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "")
35 throw runtime_error(strprintf(
36 _("You must set rpcpassword=<password> in the configuration file:\n%s\n"
37 "If the file does not exist, create it with owner-readable-only file permissions."),
38 GetConfigFile().string().c_str()));
40 // Connect to localhost
41 bool fUseSSL = GetBoolArg("-rpcssl", false);
42 asio::io_service io_service;
43 ssl::context context(io_service, ssl::context::sslv23);
44 context.set_options(ssl::context::no_sslv2);
45 asio::ssl::stream<asio::ip::tcp::socket> sslStream(io_service, context);
46 SSLIOStreamDevice<asio::ip::tcp> d(sslStream, fUseSSL);
47 iostreams::stream< SSLIOStreamDevice<asio::ip::tcp> > stream(d);
49 bool fWait = GetBoolArg("-rpcwait", false); // -rpcwait means try until server has started
51 bool fConnected = d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", itostr(Params().RPCPort())));
52 if (fConnected) break;
56 throw runtime_error("couldn't connect to server");
59 // HTTP basic authentication
60 string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]);
61 map<string, string> mapRequestHeaders;
62 mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64;
65 string strRequest = JSONRPCRequest(strMethod, params, 1);
66 string strPost = HTTPPost(strRequest, mapRequestHeaders);
67 stream << strPost << std::flush;
69 // Receive HTTP reply status
71 int nStatus = ReadHTTPStatus(stream, nProto);
73 // Receive HTTP reply message headers and body
74 map<string, string> mapHeaders;
76 ReadHTTPMessage(stream, mapHeaders, strReply, nProto);
78 if (nStatus == HTTP_UNAUTHORIZED)
79 throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
80 else if (nStatus >= 400 && nStatus != HTTP_BAD_REQUEST && nStatus != HTTP_NOT_FOUND && nStatus != HTTP_INTERNAL_SERVER_ERROR)
81 throw runtime_error(strprintf("server returned HTTP error %d", nStatus));
82 else if (strReply.empty())
83 throw runtime_error("no response from server");
87 if (!read_string(strReply, valReply))
88 throw runtime_error("couldn't parse reply from server");
89 const Object& reply = valReply.get_obj();
91 throw runtime_error("expected reply to have result, error and id properties");
97 void ConvertTo(Value& value, bool fAllowNull=false)
99 if (fAllowNull && value.type() == null_type)
101 if (value.type() == str_type)
103 // reinterpret string as unquoted json value
105 string strJSON = value.get_str();
106 if (!read_string(strJSON, value2))
107 throw runtime_error(string("Error parsing JSON:")+strJSON);
108 ConvertTo<T>(value2, fAllowNull);
113 value = value.get_value<T>();
117 // Convert strings to command-specific RPC representation
118 Array RPCConvertValues(const std::string &strMethod, const std::vector<std::string> &strParams)
121 BOOST_FOREACH(const std::string ¶m, strParams)
122 params.push_back(param);
124 int n = params.size();
127 // Special case non-string parameter types
129 if (strMethod == "stop" && n > 0) ConvertTo<bool>(params[0]);
130 if (strMethod == "getaddednodeinfo" && n > 0) ConvertTo<bool>(params[0]);
131 if (strMethod == "setgenerate" && n > 0) ConvertTo<bool>(params[0]);
132 if (strMethod == "setgenerate" && n > 1) ConvertTo<boost::int64_t>(params[1]);
133 if (strMethod == "getnetworkhashps" && n > 0) ConvertTo<boost::int64_t>(params[0]);
134 if (strMethod == "getnetworkhashps" && n > 1) ConvertTo<boost::int64_t>(params[1]);
135 if (strMethod == "sendtoaddress" && n > 1) ConvertTo<double>(params[1]);
136 if (strMethod == "settxfee" && n > 0) ConvertTo<double>(params[0]);
137 if (strMethod == "getreceivedbyaddress" && n > 1) ConvertTo<boost::int64_t>(params[1]);
138 if (strMethod == "getreceivedbyaccount" && n > 1) ConvertTo<boost::int64_t>(params[1]);
139 if (strMethod == "listreceivedbyaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
140 if (strMethod == "listreceivedbyaddress" && n > 1) ConvertTo<bool>(params[1]);
141 if (strMethod == "listreceivedbyaccount" && n > 0) ConvertTo<boost::int64_t>(params[0]);
142 if (strMethod == "listreceivedbyaccount" && n > 1) ConvertTo<bool>(params[1]);
143 if (strMethod == "getbalance" && n > 1) ConvertTo<boost::int64_t>(params[1]);
144 if (strMethod == "getblockhash" && n > 0) ConvertTo<boost::int64_t>(params[0]);
145 if (strMethod == "move" && n > 2) ConvertTo<double>(params[2]);
146 if (strMethod == "move" && n > 3) ConvertTo<boost::int64_t>(params[3]);
147 if (strMethod == "sendfrom" && n > 2) ConvertTo<double>(params[2]);
148 if (strMethod == "sendfrom" && n > 3) ConvertTo<boost::int64_t>(params[3]);
149 if (strMethod == "listtransactions" && n > 1) ConvertTo<boost::int64_t>(params[1]);
150 if (strMethod == "listtransactions" && n > 2) ConvertTo<boost::int64_t>(params[2]);
151 if (strMethod == "listaccounts" && n > 0) ConvertTo<boost::int64_t>(params[0]);
152 if (strMethod == "walletpassphrase" && n > 1) ConvertTo<boost::int64_t>(params[1]);
153 if (strMethod == "getblocktemplate" && n > 0) ConvertTo<Object>(params[0]);
154 if (strMethod == "listsinceblock" && n > 1) ConvertTo<boost::int64_t>(params[1]);
155 if (strMethod == "sendmany" && n > 1) ConvertTo<Object>(params[1]);
156 if (strMethod == "sendmany" && n > 2) ConvertTo<boost::int64_t>(params[2]);
157 if (strMethod == "addmultisigaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
158 if (strMethod == "addmultisigaddress" && n > 1) ConvertTo<Array>(params[1]);
159 if (strMethod == "createmultisig" && n > 0) ConvertTo<boost::int64_t>(params[0]);
160 if (strMethod == "createmultisig" && n > 1) ConvertTo<Array>(params[1]);
161 if (strMethod == "listunspent" && n > 0) ConvertTo<boost::int64_t>(params[0]);
162 if (strMethod == "listunspent" && n > 1) ConvertTo<boost::int64_t>(params[1]);
163 if (strMethod == "listunspent" && n > 2) ConvertTo<Array>(params[2]);
164 if (strMethod == "getblock" && n > 1) ConvertTo<bool>(params[1]);
165 if (strMethod == "getrawtransaction" && n > 1) ConvertTo<boost::int64_t>(params[1]);
166 if (strMethod == "createrawtransaction" && n > 0) ConvertTo<Array>(params[0]);
167 if (strMethod == "createrawtransaction" && n > 1) ConvertTo<Object>(params[1]);
168 if (strMethod == "signrawtransaction" && n > 1) ConvertTo<Array>(params[1], true);
169 if (strMethod == "signrawtransaction" && n > 2) ConvertTo<Array>(params[2], true);
170 if (strMethod == "sendrawtransaction" && n > 1) ConvertTo<bool>(params[1], true);
171 if (strMethod == "gettxout" && n > 1) ConvertTo<boost::int64_t>(params[1]);
172 if (strMethod == "gettxout" && n > 2) ConvertTo<bool>(params[2]);
173 if (strMethod == "lockunspent" && n > 0) ConvertTo<bool>(params[0]);
174 if (strMethod == "lockunspent" && n > 1) ConvertTo<Array>(params[1]);
175 if (strMethod == "importprivkey" && n > 2) ConvertTo<bool>(params[2]);
176 if (strMethod == "verifychain" && n > 0) ConvertTo<boost::int64_t>(params[0]);
177 if (strMethod == "verifychain" && n > 1) ConvertTo<boost::int64_t>(params[1]);
178 if (strMethod == "keypoolrefill" && n > 0) ConvertTo<boost::int64_t>(params[0]);
183 int CommandLineRPC(int argc, char *argv[])
190 while (argc > 1 && IsSwitchChar(argv[1][0]))
198 throw runtime_error("too few parameters");
199 string strMethod = argv[1];
201 // Parameters default to strings
202 std::vector<std::string> strParams(&argv[2], &argv[argc]);
203 Array params = RPCConvertValues(strMethod, strParams);
206 Object reply = CallRPC(strMethod, params);
209 const Value& result = find_value(reply, "result");
210 const Value& error = find_value(reply, "error");
212 if (error.type() != null_type)
215 strPrint = "error: " + write_string(error, false);
216 int code = find_value(error.get_obj(), "code").get_int();
222 if (result.type() == null_type)
224 else if (result.type() == str_type)
225 strPrint = result.get_str();
227 strPrint = write_string(result, true);
230 catch (boost::thread_interrupted) {
233 catch (std::exception& e) {
234 strPrint = string("error: ") + e.what();
238 PrintException(NULL, "CommandLineRPC()");
243 fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
248 std::string HelpMessageCli(bool mainProgram)
253 strUsage += _("Options:") + "\n";
254 strUsage += " -? " + _("This help message") + "\n";
255 strUsage += " -conf=<file> " + _("Specify configuration file (default: bitcoin.conf)") + "\n";
256 strUsage += " -datadir=<dir> " + _("Specify data directory") + "\n";
257 strUsage += " -testnet " + _("Use the test network") + "\n";
258 strUsage += " -regtest " + _("Enter regression test mode, which uses a special chain in which blocks can be "
259 "solved instantly. This is intended for regression testing tools and app development.") + "\n";
261 strUsage += _("RPC client options:") + "\n";
264 strUsage += " -rpcconnect=<ip> " + _("Send commands to node running on <ip> (default: 127.0.0.1)") + "\n";
265 strUsage += " -rpcport=<port> " + _("Connect to JSON-RPC on <port> (default: 8332 or testnet: 18332)") + "\n";
266 strUsage += " -rpcwait " + _("Wait for RPC server to start") + "\n";
267 strUsage += " -rpcuser=<user> " + _("Username for JSON-RPC connections") + "\n";
268 strUsage += " -rpcpassword=<pw> " + _("Password for JSON-RPC connections") + "\n";
272 strUsage += "\n" + _("SSL options: (see the Bitcoin Wiki for SSL setup instructions)") + "\n";
273 strUsage += " -rpcssl " + _("Use OpenSSL (https) for JSON-RPC connections") + "\n";