1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2013 The Bitcoin Core developers
3 // Distributed under the MIT software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
6 #include "chainparamsbase.h"
7 #include "clientversion.h"
9 #include "rpcprotocol.h"
11 #include "utilstrencodings.h"
13 #include <boost/filesystem/operations.hpp>
16 using namespace json_spirit;
18 std::string HelpMessageCli()
21 strUsage += HelpMessageGroup(_("Options:"));
22 strUsage += HelpMessageOpt("-?", _("This help message"));
23 strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file (default: %s)"), "zcash.conf"));
24 strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory"));
25 strUsage += HelpMessageOpt("-testnet", _("Use the test network"));
26 strUsage += HelpMessageOpt("-regtest", _("Enter regression test mode, which uses a special chain in which blocks can be "
27 "solved instantly. This is intended for regression testing tools and app development."));
28 strUsage += HelpMessageOpt("-rpcconnect=<ip>", strprintf(_("Send commands to node running on <ip> (default: %s)"), "127.0.0.1"));
29 strUsage += HelpMessageOpt("-rpcport=<port>", strprintf(_("Connect to JSON-RPC on <port> (default: %u or testnet: %u)"), 8232, 18232));
30 strUsage += HelpMessageOpt("-rpcwait", _("Wait for RPC server to start"));
31 strUsage += HelpMessageOpt("-rpcuser=<user>", _("Username for JSON-RPC connections"));
32 strUsage += HelpMessageOpt("-rpcpassword=<pw>", _("Password for JSON-RPC connections"));
34 strUsage += HelpMessageGroup(_("SSL options: (see the Bitcoin Wiki for SSL setup instructions)"));
35 strUsage += HelpMessageOpt("-rpcssl", _("Use OpenSSL (https) for JSON-RPC connections"));
40 //////////////////////////////////////////////////////////////////////////////
46 // Exception thrown on connection error. This error is used to determine
47 // when to wait if -rpcwait is given.
49 class CConnectionFailed : public std::runtime_error
53 explicit inline CConnectionFailed(const std::string& msg) :
54 std::runtime_error(msg)
59 static bool AppInitRPC(int argc, char* argv[])
64 ParseParameters(argc, argv);
65 if (argc<2 || mapArgs.count("-?") || mapArgs.count("-h") || mapArgs.count("-help") || mapArgs.count("-version")) {
66 std::string strUsage = _("Zcash RPC client version") + " " + FormatFullVersion() + "\n";
67 if (!mapArgs.count("-version")) {
68 strUsage += "\n" + _("Usage:") + "\n" +
69 " zcash-cli [options] <command> [params] " + _("Send command to Zcash") + "\n" +
70 " zcash-cli [options] help " + _("List commands") + "\n" +
71 " zcash-cli [options] help <command> " + _("Get help for a command") + "\n";
73 strUsage += "\n" + HelpMessageCli();
76 fprintf(stdout, "%s", strUsage.c_str());
79 if (!boost::filesystem::is_directory(GetDataDir(false))) {
80 fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", mapArgs["-datadir"].c_str());
84 ReadConfigFile(mapArgs, mapMultiArgs);
85 } catch (const std::exception& e) {
86 fprintf(stderr,"Error reading configuration file: %s\n", e.what());
89 // Check for -testnet or -regtest parameter (BaseParams() calls are only valid after this clause)
90 if (!SelectBaseParamsFromCommandLine()) {
91 fprintf(stderr, "Error: Invalid combination of -regtest and -testnet.\n");
97 Object CallRPC(const string& strMethod, const Array& params)
99 if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "")
100 throw runtime_error(strprintf(
101 _("You must set rpcpassword=<password> in the configuration file:\n%s\n"
102 "If the file does not exist, create it with owner-readable-only file permissions."),
103 GetConfigFile().string().c_str()));
105 // Connect to localhost
106 bool fUseSSL = GetBoolArg("-rpcssl", false);
107 boost::asio::io_service io_service;
108 boost::asio::ssl::context context(io_service, boost::asio::ssl::context::sslv23);
109 context.set_options(boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::no_sslv3);
110 boost::asio::ssl::stream<boost::asio::ip::tcp::socket> sslStream(io_service, context);
111 SSLIOStreamDevice<boost::asio::ip::tcp> d(sslStream, fUseSSL);
112 boost::iostreams::stream< SSLIOStreamDevice<boost::asio::ip::tcp> > stream(d);
114 const bool fConnected = d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", itostr(BaseParams().RPCPort())));
116 throw CConnectionFailed("couldn't connect to server");
118 // HTTP basic authentication
119 string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]);
120 map<string, string> mapRequestHeaders;
121 mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64;
124 string strRequest = JSONRPCRequest(strMethod, params, 1);
125 string strPost = HTTPPost(strRequest, mapRequestHeaders);
126 stream << strPost << std::flush;
128 // Receive HTTP reply status
130 int nStatus = ReadHTTPStatus(stream, nProto);
132 // Receive HTTP reply message headers and body
133 map<string, string> mapHeaders;
135 ReadHTTPMessage(stream, mapHeaders, strReply, nProto, std::numeric_limits<size_t>::max());
137 if (nStatus == HTTP_UNAUTHORIZED)
138 throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
139 else if (nStatus >= 400 && nStatus != HTTP_BAD_REQUEST && nStatus != HTTP_NOT_FOUND && nStatus != HTTP_INTERNAL_SERVER_ERROR)
140 throw runtime_error(strprintf("server returned HTTP error %d", nStatus));
141 else if (strReply.empty())
142 throw runtime_error("no response from server");
146 if (!read_string(strReply, valReply))
147 throw runtime_error("couldn't parse reply from server");
148 const Object& reply = valReply.get_obj();
150 throw runtime_error("expected reply to have result, error and id properties");
155 int CommandLineRPC(int argc, char *argv[])
161 while (argc > 1 && IsSwitchChar(argv[1][0])) {
168 throw runtime_error("too few parameters");
169 string strMethod = argv[1];
171 // Parameters default to strings
172 std::vector<std::string> strParams(&argv[2], &argv[argc]);
173 Array params = RPCConvertValues(strMethod, strParams);
175 // Execute and handle connection failures with -rpcwait
176 const bool fWait = GetBoolArg("-rpcwait", false);
179 const Object reply = CallRPC(strMethod, params);
182 const Value& result = find_value(reply, "result");
183 const Value& error = find_value(reply, "error");
185 if (error.type() != null_type) {
187 const int code = find_value(error.get_obj(), "code").get_int();
188 if (fWait && code == RPC_IN_WARMUP)
189 throw CConnectionFailed("server in warmup");
190 strPrint = "error: " + write_string(error, false);
194 if (result.type() == null_type)
196 else if (result.type() == str_type)
197 strPrint = result.get_str();
199 strPrint = write_string(result, true);
202 // Connection succeeded, no need to retry.
205 catch (const CConnectionFailed&) {
213 catch (const boost::thread_interrupted&) {
216 catch (const std::exception& e) {
217 strPrint = string("error: ") + e.what();
221 PrintExceptionContinue(NULL, "CommandLineRPC()");
225 if (strPrint != "") {
226 fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
231 int main(int argc, char* argv[])
236 if(!AppInitRPC(argc, argv))
239 catch (const std::exception& e) {
240 PrintExceptionContinue(&e, "AppInitRPC()");
243 PrintExceptionContinue(NULL, "AppInitRPC()");
247 int ret = EXIT_FAILURE;
249 ret = CommandLineRPC(argc, argv);
251 catch (const std::exception& e) {
252 PrintExceptionContinue(&e, "CommandLineRPC()");
254 PrintExceptionContinue(NULL, "CommandLineRPC()");