Dash Core  0.12.2.1
P2P Digital Currency
dash-cli.cpp
Go to the documentation of this file.
1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2015 The Bitcoin Core developers
3 // Copyright (c) 2014-2017 The Dash Core developers
4 // Distributed under the MIT software license, see the accompanying
5 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
6 
7 #include "chainparamsbase.h"
8 #include "clientversion.h"
9 #include "rpc/client.h"
10 #include "rpc/protocol.h"
11 #include "util.h"
12 #include "utilstrencodings.h"
13 
14 #include <boost/filesystem/operations.hpp>
15 #include <stdio.h>
16 
17 #include <event2/event.h>
18 #include <event2/http.h>
19 #include <event2/buffer.h>
20 #include <event2/keyvalq_struct.h>
21 
22 #include <univalue.h>
23 
24 using namespace std;
25 
26 static const char DEFAULT_RPCCONNECT[] = "127.0.0.1";
27 static const int DEFAULT_HTTP_CLIENT_TIMEOUT=900;
28 static const int CONTINUE_EXECUTION=-1;
29 
30 std::string HelpMessageCli()
31 {
32  string strUsage;
33  strUsage += HelpMessageGroup(_("Options:"));
34  strUsage += HelpMessageOpt("-?", _("This help message"));
35  strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file (default: %s)"), BITCOIN_CONF_FILENAME));
36  strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory"));
37  AppendParamsHelpMessages(strUsage);
38  strUsage += HelpMessageOpt("-rpcconnect=<ip>", strprintf(_("Send commands to node running on <ip> (default: %s)"), DEFAULT_RPCCONNECT));
39  strUsage += HelpMessageOpt("-rpcport=<port>", strprintf(_("Connect to JSON-RPC on <port> (default: %u or testnet: %u)"), BaseParams(CBaseChainParams::MAIN).RPCPort(), BaseParams(CBaseChainParams::TESTNET).RPCPort()));
40  strUsage += HelpMessageOpt("-rpcwait", _("Wait for RPC server to start"));
41  strUsage += HelpMessageOpt("-rpcuser=<user>", _("Username for JSON-RPC connections"));
42  strUsage += HelpMessageOpt("-rpcpassword=<pw>", _("Password for JSON-RPC connections"));
43  strUsage += HelpMessageOpt("-rpcclienttimeout=<n>", strprintf(_("Timeout during HTTP requests (default: %d)"), DEFAULT_HTTP_CLIENT_TIMEOUT));
44 
45  return strUsage;
46 }
47 
49 //
50 // Start
51 //
52 
53 //
54 // Exception thrown on connection error. This error is used to determine
55 // when to wait if -rpcwait is given.
56 //
57 class CConnectionFailed : public std::runtime_error
58 {
59 public:
60 
61  explicit inline CConnectionFailed(const std::string& msg) :
62  std::runtime_error(msg)
63  {}
64 
65 };
66 
67 //
68 // This function returns either one of EXIT_ codes when it's expected to stop the process or
69 // CONTINUE_EXECUTION when it's expected to continue further.
70 //
71 static int AppInitRPC(int argc, char* argv[])
72 {
73  //
74  // Parameters
75  //
76  ParseParameters(argc, argv);
77  if (argc<2 || mapArgs.count("-?") || mapArgs.count("-h") || mapArgs.count("-help") || mapArgs.count("-version")) {
78  std::string strUsage = _("Dash Core RPC client version") + " " + FormatFullVersion() + "\n";
79  if (!mapArgs.count("-version")) {
80  strUsage += "\n" + _("Usage:") + "\n" +
81  " dash-cli [options] <command> [params] " + _("Send command to Dash Core") + "\n" +
82  " dash-cli [options] help " + _("List commands") + "\n" +
83  " dash-cli [options] help <command> " + _("Get help for a command") + "\n";
84 
85  strUsage += "\n" + HelpMessageCli();
86  }
87 
88  fprintf(stdout, "%s", strUsage.c_str());
89  if (argc < 2) {
90  fprintf(stderr, "Error: too few parameters\n");
91  return EXIT_FAILURE;
92  }
93  return EXIT_SUCCESS;
94  }
95  bool datadirFromCmdLine = mapArgs.count("-datadir") != 0;
96  if (datadirFromCmdLine && !boost::filesystem::is_directory(GetDataDir(false))) {
97  fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", mapArgs["-datadir"].c_str());
98  return EXIT_FAILURE;
99  }
100  try {
102  } catch (const std::exception& e) {
103  fprintf(stderr,"Error reading configuration file: %s\n", e.what());
104  return EXIT_FAILURE;
105  }
106  if (!datadirFromCmdLine && !boost::filesystem::is_directory(GetDataDir(false))) {
107  fprintf(stderr, "Error: Specified data directory \"%s\" from config file does not exist.\n", mapArgs["-datadir"].c_str());
108  return EXIT_FAILURE;
109  }
110  // Check for -testnet or -regtest parameter (BaseParams() calls are only valid after this clause)
111  try {
113  } catch (const std::exception& e) {
114  fprintf(stderr, "Error: %s\n", e.what());
115  return EXIT_FAILURE;
116  }
117  if (GetBoolArg("-rpcssl", false))
118  {
119  fprintf(stderr, "Error: SSL mode for RPC (-rpcssl) is no longer supported.\n");
120  return EXIT_FAILURE;
121  }
122  return CONTINUE_EXECUTION;
123 }
124 
125 
127 struct HTTPReply
128 {
129  int status;
130  std::string body;
131 };
132 
133 static void http_request_done(struct evhttp_request *req, void *ctx)
134 {
135  HTTPReply *reply = static_cast<HTTPReply*>(ctx);
136 
137  if (req == NULL) {
138  /* If req is NULL, it means an error occurred while connecting, but
139  * I'm not sure how to find out which one. We also don't really care.
140  */
141  reply->status = 0;
142  return;
143  }
144 
145  reply->status = evhttp_request_get_response_code(req);
146 
147  struct evbuffer *buf = evhttp_request_get_input_buffer(req);
148  if (buf)
149  {
150  size_t size = evbuffer_get_length(buf);
151  const char *data = (const char*)evbuffer_pullup(buf, size);
152  if (data)
153  reply->body = std::string(data, size);
154  evbuffer_drain(buf, size);
155  }
156 }
157 
158 UniValue CallRPC(const string& strMethod, const UniValue& params)
159 {
160  std::string host = GetArg("-rpcconnect", DEFAULT_RPCCONNECT);
161  int port = GetArg("-rpcport", BaseParams().RPCPort());
162 
163  // Create event base
164  struct event_base *base = event_base_new(); // TODO RAII
165  if (!base)
166  throw runtime_error("cannot create event_base");
167 
168  // Synchronously look up hostname
169  struct evhttp_connection *evcon = evhttp_connection_base_new(base, NULL, host.c_str(), port); // TODO RAII
170  if (evcon == NULL)
171  throw runtime_error("create connection failed");
172  evhttp_connection_set_timeout(evcon, GetArg("-rpcclienttimeout", DEFAULT_HTTP_CLIENT_TIMEOUT));
173 
174  HTTPReply response;
175  struct evhttp_request *req = evhttp_request_new(http_request_done, (void*)&response); // TODO RAII
176  if (req == NULL)
177  throw runtime_error("create http request failed");
178 
179  // Get credentials
180  std::string strRPCUserColonPass;
181  if (mapArgs["-rpcpassword"] == "") {
182  // Try fall back to cookie-based authentication if no password is provided
184  throw runtime_error(strprintf(
185  _("Could not locate RPC credentials. No authentication cookie could be found, and no rpcpassword is set in the configuration file (%s)"),
186  GetConfigFile().string().c_str()));
187 
188  }
189  } else {
190  strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
191  }
192 
193  struct evkeyvalq *output_headers = evhttp_request_get_output_headers(req);
194  assert(output_headers);
195  evhttp_add_header(output_headers, "Host", host.c_str());
196  evhttp_add_header(output_headers, "Connection", "close");
197  evhttp_add_header(output_headers, "Authorization", (std::string("Basic ") + EncodeBase64(strRPCUserColonPass)).c_str());
198 
199  // Attach request data
200  std::string strRequest = JSONRPCRequest(strMethod, params, 1);
201  struct evbuffer * output_buffer = evhttp_request_get_output_buffer(req);
202  assert(output_buffer);
203  evbuffer_add(output_buffer, strRequest.data(), strRequest.size());
204 
205  int r = evhttp_make_request(evcon, req, EVHTTP_REQ_POST, "/");
206  if (r != 0) {
207  evhttp_connection_free(evcon);
208  event_base_free(base);
209  throw CConnectionFailed("send http request failed");
210  }
211 
212  event_base_dispatch(base);
213  evhttp_connection_free(evcon);
214  event_base_free(base);
215 
216  if (response.status == 0)
217  throw CConnectionFailed("couldn't connect to server");
218  else if (response.status == HTTP_UNAUTHORIZED)
219  throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
220  else if (response.status >= 400 && response.status != HTTP_BAD_REQUEST && response.status != HTTP_NOT_FOUND && response.status != HTTP_INTERNAL_SERVER_ERROR)
221  throw runtime_error(strprintf("server returned HTTP error %d", response.status));
222  else if (response.body.empty())
223  throw runtime_error("no response from server");
224 
225  // Parse reply
226  UniValue valReply(UniValue::VSTR);
227  if (!valReply.read(response.body))
228  throw runtime_error("couldn't parse reply from server");
229  const UniValue& reply = valReply.get_obj();
230  if (reply.empty())
231  throw runtime_error("expected reply to have result, error and id properties");
232 
233  return reply;
234 }
235 
236 int CommandLineRPC(int argc, char *argv[])
237 {
238  string strPrint;
239  int nRet = 0;
240  try {
241  // Skip switches
242  while (argc > 1 && IsSwitchChar(argv[1][0])) {
243  argc--;
244  argv++;
245  }
246 
247  // Method
248  if (argc < 2)
249  throw runtime_error("too few parameters");
250  string strMethod = argv[1];
251 
252  // Parameters default to strings
253  std::vector<std::string> strParams(&argv[2], &argv[argc]);
254  UniValue params = RPCConvertValues(strMethod, strParams);
255 
256  // Execute and handle connection failures with -rpcwait
257  const bool fWait = GetBoolArg("-rpcwait", false);
258  do {
259  try {
260  const UniValue reply = CallRPC(strMethod, params);
261 
262  // Parse reply
263  const UniValue& result = find_value(reply, "result");
264  const UniValue& error = find_value(reply, "error");
265 
266  if (!error.isNull()) {
267  // Error
268  int code = error["code"].get_int();
269  if (fWait && code == RPC_IN_WARMUP)
270  throw CConnectionFailed("server in warmup");
271  strPrint = "error: " + error.write();
272  nRet = abs(code);
273  if (error.isObject())
274  {
275  UniValue errCode = find_value(error, "code");
276  UniValue errMsg = find_value(error, "message");
277  strPrint = errCode.isNull() ? "" : "error code: "+errCode.getValStr()+"\n";
278 
279  if (errMsg.isStr())
280  strPrint += "error message:\n"+errMsg.get_str();
281  }
282  } else {
283  // Result
284  if (result.isNull())
285  strPrint = "";
286  else if (result.isStr())
287  strPrint = result.get_str();
288  else
289  strPrint = result.write(2);
290  }
291  // Connection succeeded, no need to retry.
292  break;
293  }
294  catch (const CConnectionFailed&) {
295  if (fWait)
296  MilliSleep(1000);
297  else
298  throw;
299  }
300  } while (fWait);
301  }
302  catch (const boost::thread_interrupted&) {
303  throw;
304  }
305  catch (const std::exception& e) {
306  strPrint = string("error: ") + e.what();
307  nRet = EXIT_FAILURE;
308  }
309  catch (...) {
310  PrintExceptionContinue(NULL, "CommandLineRPC()");
311  throw;
312  }
313 
314  if (strPrint != "") {
315  fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
316  }
317  return nRet;
318 }
319 
320 int main(int argc, char* argv[])
321 {
323  if (!SetupNetworking()) {
324  fprintf(stderr, "Error: Initializing networking failed\n");
325  exit(EXIT_FAILURE);
326  }
327 
328  try {
329  int ret = AppInitRPC(argc, argv);
330  if (ret != CONTINUE_EXECUTION)
331  return ret;
332  }
333  catch (const std::exception& e) {
334  PrintExceptionContinue(&e, "AppInitRPC()");
335  return EXIT_FAILURE;
336  } catch (...) {
337  PrintExceptionContinue(NULL, "AppInitRPC()");
338  return EXIT_FAILURE;
339  }
340 
341  int ret = EXIT_FAILURE;
342  try {
343  ret = CommandLineRPC(argc, argv);
344  }
345  catch (const std::exception& e) {
346  PrintExceptionContinue(&e, "CommandLineRPC()");
347  } catch (...) {
348  PrintExceptionContinue(NULL, "CommandLineRPC()");
349  }
350  return ret;
351 }
UniValue CallRPC(const string &strMethod, const UniValue &params)
Definition: dash-cli.cpp:158
const boost::filesystem::path & GetDataDir(bool fNetSpecific)
Definition: util.cpp:547
std::string HelpMessageOpt(const std::string &option, const std::string &message)
Definition: util.cpp:486
void AppendParamsHelpMessages(std::string &strUsage, bool debugHelp)
bool SetupNetworking()
Definition: util.cpp:923
static const int DEFAULT_HTTP_CLIENT_TIMEOUT
Definition: dash-cli.cpp:27
void MilliSleep(int64_t n)
Definition: utiltime.cpp:63
const char *const BITCOIN_CONF_FILENAME
Definition: util.cpp:119
std::string HelpMessageCli()
Definition: dash-cli.cpp:30
static std::string strRPCUserColonPass
Definition: httprpc.cpp:61
#define strprintf
Definition: tinyformat.h:1011
struct event_base * base
Definition: torcontrol.cpp:659
bool isStr() const
Definition: univalue.h:81
int CommandLineRPC(int argc, char *argv[])
Definition: dash-cli.cpp:236
static const std::string TESTNET
const std::string & getValStr() const
Definition: univalue.h:66
const CBaseChainParams & BaseParams()
void PrintExceptionContinue(const std::exception *pex, const char *pszThread)
Definition: util.cpp:509
string JSONRPCRequest(const string &strMethod, const UniValue &params, const UniValue &id)
Definition: protocol.cpp:30
int main(int argc, char *argv[])
Definition: dash-cli.cpp:320
Transaction already in chain.
Definition: protocol.h:52
const UniValue & find_value(const UniValue &obj, const std::string &name)
Definition: univalue.cpp:280
string EncodeBase64(const unsigned char *pch, size_t len)
UniValue RPCConvertValues(const std::string &strMethod, const std::vector< std::string > &strParams)
Definition: client.cpp:179
bool GetAuthCookie(std::string *cookie_out)
Definition: protocol.cpp:106
const UniValue & get_obj() const
Definition: univalue.cpp:347
bool GetBoolArg(const std::string &strArg, bool fDefault)
Definition: util.cpp:455
static secp256k1_context * ctx
Definition: tests.c:42
bool isNull() const
Definition: univalue.h:77
static bool error(const char *format)
Definition: util.h:131
static const char DEFAULT_RPCCONNECT[]
Definition: dash-cli.cpp:26
static int AppInitRPC(int argc, char *argv[])
Definition: dash-cli.cpp:71
void ParseParameters(int argc, const char *const argv[])
Definition: util.cpp:406
std::string FormatFullVersion()
static const int CONTINUE_EXECUTION
Definition: dash-cli.cpp:28
CConnectionFailed(const std::string &msg)
Definition: dash-cli.cpp:61
bool IsSwitchChar(char c)
Definition: util.h:164
void ReadConfigFile(map< string, string > &mapSettingsRet, map< string, vector< string > > &mapMultiSettingsRet)
Definition: util.cpp:627
std::string body
Definition: dash-cli.cpp:130
int status
Definition: dash-cli.cpp:129
std::string ChainNameFromCommandLine()
static void http_request_done(struct evhttp_request *req, void *ctx)
Definition: dash-cli.cpp:133
static const std::string MAIN
std::string GetArg(const std::string &strArg, const std::string &strDefault)
Definition: util.cpp:441
void SetupEnvironment()
Definition: util.cpp:904
int port
Definition: zmq_sub.py:8
void SelectBaseParams(const std::string &chain)
bool read(const char *raw)
map< string, vector< string > > mapMultiArgs
Definition: util.cpp:123
bool empty() const
Definition: univalue.h:67
std::string HelpMessageGroup(const std::string &message)
Definition: util.cpp:482
std::string get_str() const
Definition: univalue.cpp:310
std::string _(const char *psz)
Definition: util.h:84
map< string, string > mapArgs
Definition: util.cpp:122
result
Definition: rpcuser.py:37
boost::filesystem::path GetConfigFile()
Definition: util.cpp:611