Dogecoin Core  1.14.2
P2P Digital Currency
bitcoin-cli.cpp
Go to the documentation of this file.
1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2016 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.
5 
6 #if defined(HAVE_CONFIG_H)
7 #include "config/bitcoin-config.h"
8 #endif
9 
10 #include "chainparamsbase.h"
11 #include "clientversion.h"
12 #include "rpc/client.h"
13 #include "rpc/protocol.h"
14 #include "util.h"
15 #include "utilstrencodings.h"
16 
17 #include <boost/filesystem/operations.hpp>
18 #include <stdio.h>
19 
20 #include <event2/buffer.h>
21 #include <event2/keyvalq_struct.h>
22 #include "support/events.h"
23 
24 #include <univalue.h>
25 
26 static const char DEFAULT_RPCCONNECT[] = "127.0.0.1";
27 static const int DEFAULT_HTTP_CLIENT_TIMEOUT=900;
28 static const bool DEFAULT_NAMED=false;
29 static const int CONTINUE_EXECUTION=-1;
30 
31 std::string HelpMessageCli()
32 {
33  std::string strUsage;
34  strUsage += HelpMessageGroup(_("Options:"));
35  strUsage += HelpMessageOpt("-?", _("This help message"));
36  strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file (default: %s)"), BITCOIN_CONF_FILENAME));
37  strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory"));
38  AppendParamsHelpMessages(strUsage);
39  strUsage += HelpMessageOpt("-named", strprintf(_("Pass named instead of positional arguments (default: %s)"), DEFAULT_NAMED));
40  strUsage += HelpMessageOpt("-rpcconnect=<ip>", strprintf(_("Send commands to node running on <ip> (default: %s)"), DEFAULT_RPCCONNECT));
41  strUsage += HelpMessageOpt("-rpcport=<port>", strprintf(_("Connect to JSON-RPC on <port> (default: %u or testnet: %u)"), BaseParams(CBaseChainParams::MAIN).RPCPort(), BaseParams(CBaseChainParams::TESTNET).RPCPort()));
42  strUsage += HelpMessageOpt("-rpcwait", _("Wait for RPC server to start"));
43  strUsage += HelpMessageOpt("-rpcuser=<user>", _("Username for JSON-RPC connections"));
44  strUsage += HelpMessageOpt("-rpcpassword=<pw>", _("Password for JSON-RPC connections"));
45  strUsage += HelpMessageOpt("-rpcclienttimeout=<n>", strprintf(_("Timeout during HTTP requests (default: %d)"), DEFAULT_HTTP_CLIENT_TIMEOUT));
46  strUsage += HelpMessageOpt("-stdin", _("Read extra arguments from standard input, one per line until EOF/Ctrl-D (recommended for sensitive information such as passphrases)"));
47 
48  return strUsage;
49 }
50 
52 //
53 // Start
54 //
55 
56 //
57 // Exception thrown on connection error. This error is used to determine
58 // when to wait if -rpcwait is given.
59 //
60 class CConnectionFailed : public std::runtime_error
61 {
62 public:
63 
64  explicit inline CConnectionFailed(const std::string& msg) :
65  std::runtime_error(msg)
66  {}
67 
68 };
69 
70 //
71 // This function returns either one of EXIT_ codes when it's expected to stop the process or
72 // CONTINUE_EXECUTION when it's expected to continue further.
73 //
74 static int AppInitRPC(int argc, char* argv[])
75 {
76  //
77  // Parameters
78  //
79  ParseParameters(argc, argv);
80  if (argc<2 || IsArgSet("-?") || IsArgSet("-h") || IsArgSet("-help") || IsArgSet("-version")) {
81  std::string strUsage = strprintf(_("%s RPC client version"), _(PACKAGE_NAME)) + " " + FormatFullVersion() + "\n";
82  if (!IsArgSet("-version")) {
83  strUsage += "\n" + _("Usage:") + "\n" +
84  " dogecoin-cli [options] <command> [params] " + strprintf(_("Send command to %s"), _(PACKAGE_NAME)) + "\n" +
85  " dogecoin-cli [options] -named <command> [name=value] ... " + strprintf(_("Send command to %s (with named arguments)"), _(PACKAGE_NAME)) + "\n" +
86  " dogecoin-cli [options] help " + _("List commands") + "\n" +
87  " dogecoin-cli [options] help <command> " + _("Get help for a command") + "\n";
88 
89  strUsage += "\n" + HelpMessageCli();
90  }
91 
92  fprintf(stdout, "%s", strUsage.c_str());
93  if (argc < 2) {
94  fprintf(stderr, "Error: too few parameters\n");
95  return EXIT_FAILURE;
96  }
97  return EXIT_SUCCESS;
98  }
99  if (!boost::filesystem::is_directory(GetDataDir(false))) {
100  fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", GetArg("-datadir", "").c_str());
101  return EXIT_FAILURE;
102  }
103  try {
105  } catch (const std::exception& e) {
106  fprintf(stderr,"Error reading configuration file: %s\n", e.what());
107  return EXIT_FAILURE;
108  }
109  // Check for -testnet or -regtest parameter (BaseParams() calls are only valid after this clause)
110  try {
112  } catch (const std::exception& e) {
113  fprintf(stderr, "Error: %s\n", e.what());
114  return EXIT_FAILURE;
115  }
116  if (GetBoolArg("-rpcssl", false))
117  {
118  fprintf(stderr, "Error: SSL mode for RPC (-rpcssl) is no longer supported.\n");
119  return EXIT_FAILURE;
120  }
121  return CONTINUE_EXECUTION;
122 }
123 
124 
126 struct HTTPReply
127 {
128  HTTPReply(): status(0), error(-1) {}
129 
130  int status;
131  int error;
132  std::string body;
133 };
134 
135 const char *http_errorstring(int code)
136 {
137  switch(code) {
138 #if LIBEVENT_VERSION_NUMBER >= 0x02010300
139  case EVREQ_HTTP_TIMEOUT:
140  return "timeout reached";
141  case EVREQ_HTTP_EOF:
142  return "EOF reached";
143  case EVREQ_HTTP_INVALID_HEADER:
144  return "error while reading header, or invalid header";
145  case EVREQ_HTTP_BUFFER_ERROR:
146  return "error encountered while reading or writing";
147  case EVREQ_HTTP_REQUEST_CANCEL:
148  return "request was canceled";
149  case EVREQ_HTTP_DATA_TOO_LONG:
150  return "response body is larger than allowed";
151 #endif
152  default:
153  return "unknown";
154  }
155 }
156 
157 static void http_request_done(struct evhttp_request *req, void *ctx)
158 {
159  HTTPReply *reply = static_cast<HTTPReply*>(ctx);
160 
161  if (req == NULL) {
162  /* If req is NULL, it means an error occurred while connecting: the
163  * error code will have been passed to http_error_cb.
164  */
165  reply->status = 0;
166  return;
167  }
168 
169  reply->status = evhttp_request_get_response_code(req);
170 
171  struct evbuffer *buf = evhttp_request_get_input_buffer(req);
172  if (buf)
173  {
174  size_t size = evbuffer_get_length(buf);
175  const char *data = (const char*)evbuffer_pullup(buf, size);
176  if (data)
177  reply->body = std::string(data, size);
178  evbuffer_drain(buf, size);
179  }
180 }
181 
182 #if LIBEVENT_VERSION_NUMBER >= 0x02010300
183 static void http_error_cb(enum evhttp_request_error err, void *ctx)
184 {
185  HTTPReply *reply = static_cast<HTTPReply*>(ctx);
186  reply->error = err;
187 }
188 #endif
189 
190 UniValue CallRPC(const std::string& strMethod, const UniValue& params)
191 {
192  std::string host = GetArg("-rpcconnect", DEFAULT_RPCCONNECT);
193  int port = GetArg("-rpcport", BaseParams().RPCPort());
194 
195  // Obtain event base
196  raii_event_base base = obtain_event_base();
197 
198  // Synchronously look up hostname
199  raii_evhttp_connection evcon = obtain_evhttp_connection_base(base.get(), host, port);
200  evhttp_connection_set_timeout(evcon.get(), GetArg("-rpcclienttimeout", DEFAULT_HTTP_CLIENT_TIMEOUT));
201 
202  HTTPReply response;
203  raii_evhttp_request req = obtain_evhttp_request(http_request_done, (void*)&response);
204  if (req == NULL)
205  throw std::runtime_error("create http request failed");
206 #if LIBEVENT_VERSION_NUMBER >= 0x02010300
207  evhttp_request_set_error_cb(req.get(), http_error_cb);
208 #endif
209 
210  // Get credentials
211  std::string strRPCUserColonPass;
212  if (GetArg("-rpcpassword", "") == "") {
213  // Try fall back to cookie-based authentication if no password is provided
214  if (!GetAuthCookie(&strRPCUserColonPass)) {
215  throw std::runtime_error(strprintf(
216  _("Could not locate RPC credentials. No authentication cookie could be found, and no rpcpassword is set in the configuration file (%s)"),
217  GetConfigFile(GetArg("-conf", BITCOIN_CONF_FILENAME)).string().c_str()));
218 
219  }
220  } else {
221  strRPCUserColonPass = GetArg("-rpcuser", "") + ":" + GetArg("-rpcpassword", "");
222  }
223 
224  struct evkeyvalq* output_headers = evhttp_request_get_output_headers(req.get());
225  assert(output_headers);
226  evhttp_add_header(output_headers, "Host", host.c_str());
227  evhttp_add_header(output_headers, "Connection", "close");
228  evhttp_add_header(output_headers, "Authorization", (std::string("Basic ") + EncodeBase64(strRPCUserColonPass)).c_str());
229 
230  // Attach request data
231  std::string strRequest = JSONRPCRequestObj(strMethod, params, 1).write() + "\n";
232  struct evbuffer* output_buffer = evhttp_request_get_output_buffer(req.get());
233  assert(output_buffer);
234  evbuffer_add(output_buffer, strRequest.data(), strRequest.size());
235 
236  int r = evhttp_make_request(evcon.get(), req.get(), EVHTTP_REQ_POST, "/");
237  req.release(); // ownership moved to evcon in above call
238  if (r != 0) {
239  throw CConnectionFailed("send http request failed");
240  }
241 
242  event_base_dispatch(base.get());
243 
244  if (response.status == 0)
245  throw CConnectionFailed(strprintf("couldn't connect to server: %s (code %d)\n(make sure server is running and you are connecting to the correct RPC port)", http_errorstring(response.error), response.error));
246  else if (response.status == HTTP_UNAUTHORIZED)
247  throw std::runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
248  else if (response.status >= 400 && response.status != HTTP_BAD_REQUEST && response.status != HTTP_NOT_FOUND && response.status != HTTP_INTERNAL_SERVER_ERROR)
249  throw std::runtime_error(strprintf("server returned HTTP error %d", response.status));
250  else if (response.body.empty())
251  throw std::runtime_error("no response from server");
252 
253  // Parse reply
254  UniValue valReply(UniValue::VSTR);
255  if (!valReply.read(response.body))
256  throw std::runtime_error("couldn't parse reply from server");
257  const UniValue& reply = valReply.get_obj();
258  if (reply.empty())
259  throw std::runtime_error("expected reply to have result, error and id properties");
260 
261  return reply;
262 }
263 
264 int CommandLineRPC(int argc, char *argv[])
265 {
266  std::string strPrint;
267  int nRet = 0;
268  try {
269  // Skip switches
270  while (argc > 1 && IsSwitchChar(argv[1][0])) {
271  argc--;
272  argv++;
273  }
274  std::vector<std::string> args = std::vector<std::string>(&argv[1], &argv[argc]);
275  if (GetBoolArg("-stdin", false)) {
276  // Read one arg per line from stdin and append
277  std::string line;
278  while (std::getline(std::cin,line))
279  args.push_back(line);
280  }
281  if (args.size() < 1)
282  throw std::runtime_error("too few parameters (need at least command)");
283  std::string strMethod = args[0];
284  args.erase(args.begin()); // Remove trailing method name from arguments vector
285 
286  UniValue params;
287  if(GetBoolArg("-named", DEFAULT_NAMED)) {
288  params = RPCConvertNamedValues(strMethod, args);
289  } else {
290  params = RPCConvertValues(strMethod, args);
291  }
292 
293  // Execute and handle connection failures with -rpcwait
294  const bool fWait = GetBoolArg("-rpcwait", false);
295  do {
296  try {
297  const UniValue reply = CallRPC(strMethod, params);
298 
299  // Parse reply
300  const UniValue& result = find_value(reply, "result");
301  const UniValue& error = find_value(reply, "error");
302 
303  if (!error.isNull()) {
304  // Error
305  int code = error["code"].get_int();
306  if (fWait && code == RPC_IN_WARMUP)
307  throw CConnectionFailed("server in warmup");
308  strPrint = "error: " + error.write();
309  nRet = abs(code);
310  if (error.isObject())
311  {
312  UniValue errCode = find_value(error, "code");
313  UniValue errMsg = find_value(error, "message");
314  strPrint = errCode.isNull() ? "" : "error code: "+errCode.getValStr()+"\n";
315 
316  if (errMsg.isStr())
317  strPrint += "error message:\n"+errMsg.get_str();
318  }
319  } else {
320  // Result
321  if (result.isNull())
322  strPrint = "";
323  else if (result.isStr())
324  strPrint = result.get_str();
325  else
326  strPrint = result.write(2);
327  }
328  // Connection succeeded, no need to retry.
329  break;
330  }
331  catch (const CConnectionFailed&) {
332  if (fWait)
333  MilliSleep(1000);
334  else
335  throw;
336  }
337  } while (fWait);
338  }
339  catch (const boost::thread_interrupted&) {
340  throw;
341  }
342  catch (const std::exception& e) {
343  strPrint = std::string("error: ") + e.what();
344  nRet = EXIT_FAILURE;
345  }
346  catch (...) {
347  PrintExceptionContinue(NULL, "CommandLineRPC()");
348  throw;
349  }
350 
351  if (strPrint != "") {
352  fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
353  }
354  return nRet;
355 }
356 
357 int main(int argc, char* argv[])
358 {
360  if (!SetupNetworking()) {
361  fprintf(stderr, "Error: Initializing networking failed\n");
362  return EXIT_FAILURE;
363  }
364 
365  try {
366  int ret = AppInitRPC(argc, argv);
367  if (ret != CONTINUE_EXECUTION)
368  return ret;
369  }
370  catch (const std::exception& e) {
371  PrintExceptionContinue(&e, "AppInitRPC()");
372  return EXIT_FAILURE;
373  } catch (...) {
374  PrintExceptionContinue(NULL, "AppInitRPC()");
375  return EXIT_FAILURE;
376  }
377 
378  int ret = EXIT_FAILURE;
379  try {
380  ret = CommandLineRPC(argc, argv);
381  }
382  catch (const std::exception& e) {
383  PrintExceptionContinue(&e, "CommandLineRPC()");
384  } catch (...) {
385  PrintExceptionContinue(NULL, "CommandLineRPC()");
386  }
387  return ret;
388 }
int main(int argc, char *argv[])
std::string HelpMessageCli()
Definition: bitcoin-cli.cpp:31
int CommandLineRPC(int argc, char *argv[])
const char * http_errorstring(int code)
UniValue CallRPC(const std::string &strMethod, const UniValue &params)
const CBaseChainParams & BaseParams()
Return the currently selected parameters.
std::string ChainNameFromCommandLine()
Looks for -regtest, -testnet and returns the appropriate BIP70 chain name.
void SelectBaseParams(const std::string &chain)
Sets the params returned by Params() to those for the given network.
void AppendParamsHelpMessages(std::string &strUsage, bool debugHelp)
Append the help messages for the chainparams options to the parameter string.
static const std::string TESTNET
static const std::string MAIN
BIP70 chain name strings (main, test or regtest)
CConnectionFailed(const std::string &msg)
Definition: bitcoin-cli.cpp:64
const std::string & get_str() const
Definition: univalue.cpp:307
@ VSTR
Definition: univalue.h:21
bool isNull() const
Definition: univalue.h:77
const UniValue & get_obj() const
Definition: univalue.cpp:344
const std::string & getValStr() const
Definition: univalue.h:66
bool empty() const
Definition: univalue.h:67
bool read(const char *raw)
std::string write(unsigned int prettyIndent=0, unsigned int indentLevel=0) const
bool isStr() const
Definition: univalue.h:81
UniValue RPCConvertValues(const std::string &strMethod, const std::vector< std::string > &strParams)
Convert positional arguments to command-specific RPC representation.
Definition: client.cpp:180
UniValue RPCConvertNamedValues(const std::string &strMethod, const std::vector< std::string > &strParams)
Convert named arguments to command-specific RPC representation.
Definition: client.cpp:199
std::string FormatFullVersion()
raii_evhttp_request obtain_evhttp_request(void(*cb)(struct evhttp_request *, void *), void *arg)
Definition: events.h:45
raii_evhttp_connection obtain_evhttp_connection_base(struct event_base *base, std::string host, uint16_t port)
Definition: events.h:49
raii_event_base obtain_event_base()
Definition: events.h:30
bool GetAuthCookie(std::string *cookie_out)
Read the RPC authentication cookie from disk.
Definition: protocol.cpp:104
UniValue JSONRPCRequestObj(const string &strMethod, const UniValue &params, const UniValue &id)
JSON-RPC protocol.
Definition: protocol.cpp:29
@ HTTP_BAD_REQUEST
Definition: protocol.h:21
@ HTTP_UNAUTHORIZED
Definition: protocol.h:22
@ HTTP_NOT_FOUND
Definition: protocol.h:24
@ HTTP_INTERNAL_SERVER_ERROR
Definition: protocol.h:26
@ RPC_IN_WARMUP
Client still warming up.
Definition: protocol.h:52
Reply structure for request_done to fill in.
std::string body
#define strprintf
Definition: tinyformat.h:1047
struct event_base * base
Definition: torcontrol.cpp:679
const UniValue & find_value(const UniValue &obj, const std::string &name)
Definition: univalue.cpp:277
boost::filesystem::path GetConfigFile(const std::string &confPath)
Definition: util.cpp:551
std::string GetArg(const std::string &strArg, const std::string &strDefault)
Return string argument or default value.
Definition: util.cpp:395
void ParseParameters(int argc, const char *const argv[])
Definition: util.cpp:353
void ReadConfigFile(const std::string &confPath)
Definition: util.cpp:560
bool GetBoolArg(const std::string &strArg, bool fDefault)
Return boolean argument or default value.
Definition: util.cpp:411
bool SetupNetworking()
Definition: util.cpp:826
void SetupEnvironment()
Definition: util.cpp:797
void PrintExceptionContinue(const std::exception *pex, const char *pszThread)
Definition: util.cpp:475
std::string HelpMessageGroup(const std::string &message)
Format a string to be used as group of options in help messages.
Definition: util.cpp:448
const boost::filesystem::path & GetDataDir(bool fNetSpecific)
Definition: util.cpp:513
const char *const BITCOIN_CONF_FILENAME
Definition: util.cpp:106
bool IsArgSet(const std::string &strArg)
Return true if the given argument has been manually set.
Definition: util.cpp:389
std::string HelpMessageOpt(const std::string &option, const std::string &message)
Format a string to be used as option description in help messages.
Definition: util.cpp:452
bool error(const char *fmt, const Args &... args)
Definition: util.h:87
std::string _(const char *psz)
Translation function: Call Translate signal on UI interface, which returns a boost::optional result.
Definition: util.h:62
bool IsSwitchChar(char c)
Definition: util.h:117
string EncodeBase64(const unsigned char *pch, size_t len)
void MilliSleep(int64_t n)
Definition: utiltime.cpp:62