Merge #12075: [scripts] Add missing univalue file to copyright_header.py
[bitcoinplatinum.git] / src / bitcoin-cli.cpp
blobe9ef75eb7b3c13b470c3a952c34db3a5a0d081ea
1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2017 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 #if defined(HAVE_CONFIG_H)
7 #include <config/bitcoin-config.h>
8 #endif
10 #include <chainparamsbase.h>
11 #include <clientversion.h>
12 #include <fs.h>
13 #include <rpc/client.h>
14 #include <rpc/protocol.h>
15 #include <util.h>
16 #include <utilstrencodings.h>
18 #include <stdio.h>
20 #include <event2/buffer.h>
21 #include <event2/keyvalq_struct.h>
22 #include <support/events.h>
24 #include <univalue.h>
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;
31 std::string HelpMessageCli()
33 const auto defaultBaseParams = CreateBaseChainParams(CBaseChainParams::MAIN);
34 const auto testnetBaseParams = CreateBaseChainParams(CBaseChainParams::TESTNET);
35 std::string strUsage;
36 strUsage += HelpMessageGroup(_("Options:"));
37 strUsage += HelpMessageOpt("-?", _("This help message"));
38 strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file (default: %s)"), BITCOIN_CONF_FILENAME));
39 strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory"));
40 strUsage += HelpMessageOpt("-getinfo", _("Get general information from the remote server. Note that unlike server-side RPC calls, the results of -getinfo is the result of multiple non-atomic requests. Some entries in the result may represent results from different states (e.g. wallet balance may be as of a different block from the chain state reported)"));
41 AppendParamsHelpMessages(strUsage);
42 strUsage += HelpMessageOpt("-named", strprintf(_("Pass named instead of positional arguments (default: %s)"), DEFAULT_NAMED));
43 strUsage += HelpMessageOpt("-rpcconnect=<ip>", strprintf(_("Send commands to node running on <ip> (default: %s)"), DEFAULT_RPCCONNECT));
44 strUsage += HelpMessageOpt("-rpcport=<port>", strprintf(_("Connect to JSON-RPC on <port> (default: %u or testnet: %u)"), defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort()));
45 strUsage += HelpMessageOpt("-rpcwait", _("Wait for RPC server to start"));
46 strUsage += HelpMessageOpt("-rpcuser=<user>", _("Username for JSON-RPC connections"));
47 strUsage += HelpMessageOpt("-rpcpassword=<pw>", _("Password for JSON-RPC connections"));
48 strUsage += HelpMessageOpt("-rpcclienttimeout=<n>", strprintf(_("Timeout in seconds during HTTP requests, or 0 for no timeout. (default: %d)"), DEFAULT_HTTP_CLIENT_TIMEOUT));
49 strUsage += HelpMessageOpt("-stdinrpcpass", strprintf(_("Read RPC password from standard input as a single line. When combined with -stdin, the first line from standard input is used for the RPC password.")));
50 strUsage += HelpMessageOpt("-stdin", _("Read extra arguments from standard input, one per line until EOF/Ctrl-D (recommended for sensitive information such as passphrases). When combined with -stdinrpcpass, the first line from standard input is used for the RPC password."));
51 strUsage += HelpMessageOpt("-rpcwallet=<walletname>", _("Send RPC for non-default wallet on RPC server (argument is wallet filename in bitcoind directory, required if bitcoind/-Qt runs with multiple wallets)"));
53 return strUsage;
56 //////////////////////////////////////////////////////////////////////////////
58 // Start
62 // Exception thrown on connection error. This error is used to determine
63 // when to wait if -rpcwait is given.
65 class CConnectionFailed : public std::runtime_error
67 public:
69 explicit inline CConnectionFailed(const std::string& msg) :
70 std::runtime_error(msg)
76 // This function returns either one of EXIT_ codes when it's expected to stop the process or
77 // CONTINUE_EXECUTION when it's expected to continue further.
79 static int AppInitRPC(int argc, char* argv[])
82 // Parameters
84 gArgs.ParseParameters(argc, argv);
85 if (argc<2 || gArgs.IsArgSet("-?") || gArgs.IsArgSet("-h") || gArgs.IsArgSet("-help") || gArgs.IsArgSet("-version")) {
86 std::string strUsage = strprintf(_("%s RPC client version"), _(PACKAGE_NAME)) + " " + FormatFullVersion() + "\n";
87 if (!gArgs.IsArgSet("-version")) {
88 strUsage += "\n" + _("Usage:") + "\n" +
89 " bitcoin-cli [options] <command> [params] " + strprintf(_("Send command to %s"), _(PACKAGE_NAME)) + "\n" +
90 " bitcoin-cli [options] -named <command> [name=value] ... " + strprintf(_("Send command to %s (with named arguments)"), _(PACKAGE_NAME)) + "\n" +
91 " bitcoin-cli [options] help " + _("List commands") + "\n" +
92 " bitcoin-cli [options] help <command> " + _("Get help for a command") + "\n";
94 strUsage += "\n" + HelpMessageCli();
97 fprintf(stdout, "%s", strUsage.c_str());
98 if (argc < 2) {
99 fprintf(stderr, "Error: too few parameters\n");
100 return EXIT_FAILURE;
102 return EXIT_SUCCESS;
104 if (!fs::is_directory(GetDataDir(false))) {
105 fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "").c_str());
106 return EXIT_FAILURE;
108 try {
109 gArgs.ReadConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME));
110 } catch (const std::exception& e) {
111 fprintf(stderr,"Error reading configuration file: %s\n", e.what());
112 return EXIT_FAILURE;
114 // Check for -testnet or -regtest parameter (BaseParams() calls are only valid after this clause)
115 try {
116 SelectBaseParams(ChainNameFromCommandLine());
117 } catch (const std::exception& e) {
118 fprintf(stderr, "Error: %s\n", e.what());
119 return EXIT_FAILURE;
121 if (gArgs.GetBoolArg("-rpcssl", false))
123 fprintf(stderr, "Error: SSL mode for RPC (-rpcssl) is no longer supported.\n");
124 return EXIT_FAILURE;
126 return CONTINUE_EXECUTION;
130 /** Reply structure for request_done to fill in */
131 struct HTTPReply
133 HTTPReply(): status(0), error(-1) {}
135 int status;
136 int error;
137 std::string body;
140 const char *http_errorstring(int code)
142 switch(code) {
143 #if LIBEVENT_VERSION_NUMBER >= 0x02010300
144 case EVREQ_HTTP_TIMEOUT:
145 return "timeout reached";
146 case EVREQ_HTTP_EOF:
147 return "EOF reached";
148 case EVREQ_HTTP_INVALID_HEADER:
149 return "error while reading header, or invalid header";
150 case EVREQ_HTTP_BUFFER_ERROR:
151 return "error encountered while reading or writing";
152 case EVREQ_HTTP_REQUEST_CANCEL:
153 return "request was canceled";
154 case EVREQ_HTTP_DATA_TOO_LONG:
155 return "response body is larger than allowed";
156 #endif
157 default:
158 return "unknown";
162 static void http_request_done(struct evhttp_request *req, void *ctx)
164 HTTPReply *reply = static_cast<HTTPReply*>(ctx);
166 if (req == nullptr) {
167 /* If req is nullptr, it means an error occurred while connecting: the
168 * error code will have been passed to http_error_cb.
170 reply->status = 0;
171 return;
174 reply->status = evhttp_request_get_response_code(req);
176 struct evbuffer *buf = evhttp_request_get_input_buffer(req);
177 if (buf)
179 size_t size = evbuffer_get_length(buf);
180 const char *data = (const char*)evbuffer_pullup(buf, size);
181 if (data)
182 reply->body = std::string(data, size);
183 evbuffer_drain(buf, size);
187 #if LIBEVENT_VERSION_NUMBER >= 0x02010300
188 static void http_error_cb(enum evhttp_request_error err, void *ctx)
190 HTTPReply *reply = static_cast<HTTPReply*>(ctx);
191 reply->error = err;
193 #endif
195 /** Class that handles the conversion from a command-line to a JSON-RPC request,
196 * as well as converting back to a JSON object that can be shown as result.
198 class BaseRequestHandler
200 public:
201 virtual UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) = 0;
202 virtual UniValue ProcessReply(const UniValue &batch_in) = 0;
205 /** Process getinfo requests */
206 class GetinfoRequestHandler: public BaseRequestHandler
208 public:
209 const int ID_NETWORKINFO = 0;
210 const int ID_BLOCKCHAININFO = 1;
211 const int ID_WALLETINFO = 2;
213 /** Create a simulated `getinfo` request. */
214 UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) override
216 if (!args.empty()) {
217 throw std::runtime_error("-getinfo takes no arguments");
219 UniValue result(UniValue::VARR);
220 result.push_back(JSONRPCRequestObj("getnetworkinfo", NullUniValue, ID_NETWORKINFO));
221 result.push_back(JSONRPCRequestObj("getblockchaininfo", NullUniValue, ID_BLOCKCHAININFO));
222 result.push_back(JSONRPCRequestObj("getwalletinfo", NullUniValue, ID_WALLETINFO));
223 return result;
226 /** Collect values from the batch and form a simulated `getinfo` reply. */
227 UniValue ProcessReply(const UniValue &batch_in) override
229 UniValue result(UniValue::VOBJ);
230 std::vector<UniValue> batch = JSONRPCProcessBatchReply(batch_in, 3);
231 // Errors in getnetworkinfo() and getblockchaininfo() are fatal, pass them on
232 // getwalletinfo() is allowed to fail in case there is no wallet.
233 if (!batch[ID_NETWORKINFO]["error"].isNull()) {
234 return batch[ID_NETWORKINFO];
236 if (!batch[ID_BLOCKCHAININFO]["error"].isNull()) {
237 return batch[ID_BLOCKCHAININFO];
239 result.pushKV("version", batch[ID_NETWORKINFO]["result"]["version"]);
240 result.pushKV("protocolversion", batch[ID_NETWORKINFO]["result"]["protocolversion"]);
241 if (!batch[ID_WALLETINFO].isNull()) {
242 result.pushKV("walletversion", batch[ID_WALLETINFO]["result"]["walletversion"]);
243 result.pushKV("balance", batch[ID_WALLETINFO]["result"]["balance"]);
245 result.pushKV("blocks", batch[ID_BLOCKCHAININFO]["result"]["blocks"]);
246 result.pushKV("timeoffset", batch[ID_NETWORKINFO]["result"]["timeoffset"]);
247 result.pushKV("connections", batch[ID_NETWORKINFO]["result"]["connections"]);
248 result.pushKV("proxy", batch[ID_NETWORKINFO]["result"]["networks"][0]["proxy"]);
249 result.pushKV("difficulty", batch[ID_BLOCKCHAININFO]["result"]["difficulty"]);
250 result.pushKV("testnet", UniValue(batch[ID_BLOCKCHAININFO]["result"]["chain"].get_str() == "test"));
251 if (!batch[ID_WALLETINFO].isNull()) {
252 result.pushKV("walletversion", batch[ID_WALLETINFO]["result"]["walletversion"]);
253 result.pushKV("balance", batch[ID_WALLETINFO]["result"]["balance"]);
254 result.pushKV("keypoololdest", batch[ID_WALLETINFO]["result"]["keypoololdest"]);
255 result.pushKV("keypoolsize", batch[ID_WALLETINFO]["result"]["keypoolsize"]);
256 if (!batch[ID_WALLETINFO]["result"]["unlocked_until"].isNull()) {
257 result.pushKV("unlocked_until", batch[ID_WALLETINFO]["result"]["unlocked_until"]);
259 result.pushKV("paytxfee", batch[ID_WALLETINFO]["result"]["paytxfee"]);
261 result.pushKV("relayfee", batch[ID_NETWORKINFO]["result"]["relayfee"]);
262 result.pushKV("warnings", batch[ID_NETWORKINFO]["result"]["warnings"]);
263 return JSONRPCReplyObj(result, NullUniValue, 1);
267 /** Process default single requests */
268 class DefaultRequestHandler: public BaseRequestHandler {
269 public:
270 UniValue PrepareRequest(const std::string& method, const std::vector<std::string>& args) override
272 UniValue params;
273 if(gArgs.GetBoolArg("-named", DEFAULT_NAMED)) {
274 params = RPCConvertNamedValues(method, args);
275 } else {
276 params = RPCConvertValues(method, args);
278 return JSONRPCRequestObj(method, params, 1);
281 UniValue ProcessReply(const UniValue &reply) override
283 return reply.get_obj();
287 static UniValue CallRPC(BaseRequestHandler *rh, const std::string& strMethod, const std::vector<std::string>& args)
289 std::string host;
290 // In preference order, we choose the following for the port:
291 // 1. -rpcport
292 // 2. port in -rpcconnect (ie following : in ipv4 or ]: in ipv6)
293 // 3. default port for chain
294 int port = BaseParams().RPCPort();
295 SplitHostPort(gArgs.GetArg("-rpcconnect", DEFAULT_RPCCONNECT), port, host);
296 port = gArgs.GetArg("-rpcport", port);
298 // Obtain event base
299 raii_event_base base = obtain_event_base();
301 // Synchronously look up hostname
302 raii_evhttp_connection evcon = obtain_evhttp_connection_base(base.get(), host, port);
303 evhttp_connection_set_timeout(evcon.get(), gArgs.GetArg("-rpcclienttimeout", DEFAULT_HTTP_CLIENT_TIMEOUT));
305 HTTPReply response;
306 raii_evhttp_request req = obtain_evhttp_request(http_request_done, (void*)&response);
307 if (req == nullptr)
308 throw std::runtime_error("create http request failed");
309 #if LIBEVENT_VERSION_NUMBER >= 0x02010300
310 evhttp_request_set_error_cb(req.get(), http_error_cb);
311 #endif
313 // Get credentials
314 std::string strRPCUserColonPass;
315 if (gArgs.GetArg("-rpcpassword", "") == "") {
316 // Try fall back to cookie-based authentication if no password is provided
317 if (!GetAuthCookie(&strRPCUserColonPass)) {
318 throw std::runtime_error(strprintf(
319 _("Could not locate RPC credentials. No authentication cookie could be found, and RPC password is not set. See -rpcpassword and -stdinrpcpass. Configuration file: (%s)"),
320 GetConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)).string().c_str()));
323 } else {
324 strRPCUserColonPass = gArgs.GetArg("-rpcuser", "") + ":" + gArgs.GetArg("-rpcpassword", "");
327 struct evkeyvalq* output_headers = evhttp_request_get_output_headers(req.get());
328 assert(output_headers);
329 evhttp_add_header(output_headers, "Host", host.c_str());
330 evhttp_add_header(output_headers, "Connection", "close");
331 evhttp_add_header(output_headers, "Authorization", (std::string("Basic ") + EncodeBase64(strRPCUserColonPass)).c_str());
333 // Attach request data
334 std::string strRequest = rh->PrepareRequest(strMethod, args).write() + "\n";
335 struct evbuffer* output_buffer = evhttp_request_get_output_buffer(req.get());
336 assert(output_buffer);
337 evbuffer_add(output_buffer, strRequest.data(), strRequest.size());
339 // check if we should use a special wallet endpoint
340 std::string endpoint = "/";
341 std::string walletName = gArgs.GetArg("-rpcwallet", "");
342 if (!walletName.empty()) {
343 char *encodedURI = evhttp_uriencode(walletName.c_str(), walletName.size(), false);
344 if (encodedURI) {
345 endpoint = "/wallet/"+ std::string(encodedURI);
346 free(encodedURI);
348 else {
349 throw CConnectionFailed("uri-encode failed");
352 int r = evhttp_make_request(evcon.get(), req.get(), EVHTTP_REQ_POST, endpoint.c_str());
353 req.release(); // ownership moved to evcon in above call
354 if (r != 0) {
355 throw CConnectionFailed("send http request failed");
358 event_base_dispatch(base.get());
360 if (response.status == 0)
361 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));
362 else if (response.status == HTTP_UNAUTHORIZED)
363 throw std::runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
364 else if (response.status >= 400 && response.status != HTTP_BAD_REQUEST && response.status != HTTP_NOT_FOUND && response.status != HTTP_INTERNAL_SERVER_ERROR)
365 throw std::runtime_error(strprintf("server returned HTTP error %d", response.status));
366 else if (response.body.empty())
367 throw std::runtime_error("no response from server");
369 // Parse reply
370 UniValue valReply(UniValue::VSTR);
371 if (!valReply.read(response.body))
372 throw std::runtime_error("couldn't parse reply from server");
373 const UniValue reply = rh->ProcessReply(valReply);
374 if (reply.empty())
375 throw std::runtime_error("expected reply to have result, error and id properties");
377 return reply;
380 int CommandLineRPC(int argc, char *argv[])
382 std::string strPrint;
383 int nRet = 0;
384 try {
385 // Skip switches
386 while (argc > 1 && IsSwitchChar(argv[1][0])) {
387 argc--;
388 argv++;
390 std::string rpcPass;
391 if (gArgs.GetBoolArg("-stdinrpcpass", false)) {
392 if (!std::getline(std::cin, rpcPass)) {
393 throw std::runtime_error("-stdinrpcpass specified but failed to read from standard input");
395 gArgs.ForceSetArg("-rpcpassword", rpcPass);
397 std::vector<std::string> args = std::vector<std::string>(&argv[1], &argv[argc]);
398 if (gArgs.GetBoolArg("-stdin", false)) {
399 // Read one arg per line from stdin and append
400 std::string line;
401 while (std::getline(std::cin, line)) {
402 args.push_back(line);
405 std::unique_ptr<BaseRequestHandler> rh;
406 std::string method;
407 if (gArgs.GetBoolArg("-getinfo", false)) {
408 rh.reset(new GetinfoRequestHandler());
409 method = "";
410 } else {
411 rh.reset(new DefaultRequestHandler());
412 if (args.size() < 1) {
413 throw std::runtime_error("too few parameters (need at least command)");
415 method = args[0];
416 args.erase(args.begin()); // Remove trailing method name from arguments vector
419 // Execute and handle connection failures with -rpcwait
420 const bool fWait = gArgs.GetBoolArg("-rpcwait", false);
421 do {
422 try {
423 const UniValue reply = CallRPC(rh.get(), method, args);
425 // Parse reply
426 const UniValue& result = find_value(reply, "result");
427 const UniValue& error = find_value(reply, "error");
429 if (!error.isNull()) {
430 // Error
431 int code = error["code"].get_int();
432 if (fWait && code == RPC_IN_WARMUP)
433 throw CConnectionFailed("server in warmup");
434 strPrint = "error: " + error.write();
435 nRet = abs(code);
436 if (error.isObject())
438 UniValue errCode = find_value(error, "code");
439 UniValue errMsg = find_value(error, "message");
440 strPrint = errCode.isNull() ? "" : "error code: "+errCode.getValStr()+"\n";
442 if (errMsg.isStr())
443 strPrint += "error message:\n"+errMsg.get_str();
445 if (errCode.isNum() && errCode.get_int() == RPC_WALLET_NOT_SPECIFIED) {
446 strPrint += "\nTry adding \"-rpcwallet=<filename>\" option to bitcoin-cli command line.";
449 } else {
450 // Result
451 if (result.isNull())
452 strPrint = "";
453 else if (result.isStr())
454 strPrint = result.get_str();
455 else
456 strPrint = result.write(2);
458 // Connection succeeded, no need to retry.
459 break;
461 catch (const CConnectionFailed&) {
462 if (fWait)
463 MilliSleep(1000);
464 else
465 throw;
467 } while (fWait);
469 catch (const boost::thread_interrupted&) {
470 throw;
472 catch (const std::exception& e) {
473 strPrint = std::string("error: ") + e.what();
474 nRet = EXIT_FAILURE;
476 catch (...) {
477 PrintExceptionContinue(nullptr, "CommandLineRPC()");
478 throw;
481 if (strPrint != "") {
482 fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
484 return nRet;
487 int main(int argc, char* argv[])
489 SetupEnvironment();
490 if (!SetupNetworking()) {
491 fprintf(stderr, "Error: Initializing networking failed\n");
492 return EXIT_FAILURE;
495 try {
496 int ret = AppInitRPC(argc, argv);
497 if (ret != CONTINUE_EXECUTION)
498 return ret;
500 catch (const std::exception& e) {
501 PrintExceptionContinue(&e, "AppInitRPC()");
502 return EXIT_FAILURE;
503 } catch (...) {
504 PrintExceptionContinue(nullptr, "AppInitRPC()");
505 return EXIT_FAILURE;
508 int ret = EXIT_FAILURE;
509 try {
510 ret = CommandLineRPC(argc, argv);
512 catch (const std::exception& e) {
513 PrintExceptionContinue(&e, "CommandLineRPC()");
514 } catch (...) {
515 PrintExceptionContinue(nullptr, "CommandLineRPC()");
517 return ret;