elements/src/bitcoin-cli.cpp
2017-06-15 14:20:38 -04:00

228 lines
8.4 KiB
C++

// Copyright (c) 2009-2010 Satoshi Nakamoto
// Copyright (c) 2009-2016 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include "callrpc.h"
#if defined(HAVE_CONFIG_H)
#include "config/bitcoin-config.h"
#endif
#include "chainparamsbase.h"
#include "clientversion.h"
#include "rpc/client.h"
#include "rpc/protocol.h"
#include "util.h"
#include "utilstrencodings.h"
#include <boost/filesystem/operations.hpp>
#include <stdio.h>
#include "support/events.h"
#include <univalue.h>
static const int CONTINUE_EXECUTION=-1;
std::string HelpMessageCli()
{
const auto defaultBaseParams = CreateBaseChainParams(CBaseChainParams::MAIN);
std::string strUsage;
strUsage += HelpMessageGroup(_("Options:"));
strUsage += HelpMessageOpt("-?", _("This help message"));
strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file (default: %s)"), BITCOIN_CONF_FILENAME));
strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory"));
AppendParamsHelpMessages(strUsage);
strUsage += HelpMessageOpt("-named", strprintf(_("Pass named instead of positional arguments (default: %s)"), DEFAULT_NAMED));
strUsage += HelpMessageOpt("-rpcconnect=<ip>", strprintf(_("Send commands to node running on <ip> (default: %s)"), DEFAULT_RPCCONNECT));
strUsage += HelpMessageOpt("-rpcport=<port>", strprintf(_("Connect to JSON-RPC on <port> (default: %u)"), defaultBaseParams->RPCPort()));
strUsage += HelpMessageOpt("-rpcwait", _("Wait for RPC server to start"));
strUsage += HelpMessageOpt("-rpcuser=<user>", _("Username for JSON-RPC connections"));
strUsage += HelpMessageOpt("-rpcpassword=<pw>", _("Password for JSON-RPC connections"));
strUsage += HelpMessageOpt("-rpcclienttimeout=<n>", strprintf(_("Timeout during HTTP requests (default: %d)"), DEFAULT_HTTP_CLIENT_TIMEOUT));
strUsage += HelpMessageOpt("-stdin", _("Read extra arguments from standard input, one per line until EOF/Ctrl-D (recommended for sensitive information such as passphrases)"));
return strUsage;
}
//////////////////////////////////////////////////////////////////////////////
//
// Start
//
//
// This function returns either one of EXIT_ codes when it's expected to stop the process or
// CONTINUE_EXECUTION when it's expected to continue further.
//
static int AppInitRPC(int argc, char* argv[])
{
//
// Parameters
//
ParseParameters(argc, argv);
if (argc<2 || IsArgSet("-?") || IsArgSet("-h") || IsArgSet("-help") || IsArgSet("-version")) {
std::string strUsage = strprintf(_("%s RPC client version"), _(PACKAGE_NAME)) + " " + FormatFullVersion() + "\n";
if (!IsArgSet("-version")) {
strUsage += "\n" + _("Usage:") + "\n" +
" bitcoin-cli [options] <command> [params] " + strprintf(_("Send command to %s"), _(PACKAGE_NAME)) + "\n" +
" bitcoin-cli [options] -named <command> [name=value] ... " + strprintf(_("Send command to %s (with named arguments)"), _(PACKAGE_NAME)) + "\n" +
" bitcoin-cli [options] help " + _("List commands") + "\n" +
" bitcoin-cli [options] help <command> " + _("Get help for a command") + "\n";
strUsage += "\n" + HelpMessageCli();
}
fprintf(stdout, "%s", strUsage.c_str());
if (argc < 2) {
fprintf(stderr, "Error: too few parameters\n");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
if (!boost::filesystem::is_directory(GetDataDir(false))) {
fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", GetArg("-datadir", "").c_str());
return EXIT_FAILURE;
}
try {
ReadConfigFile(GetArg("-conf", BITCOIN_CONF_FILENAME));
} catch (const std::exception& e) {
fprintf(stderr,"Error reading configuration file: %s\n", e.what());
return EXIT_FAILURE;
}
// Check for -chain, -testnet or -regtest parameter (BaseParams() calls are only valid after this clause)
try {
SelectBaseParams(ChainNameFromCommandLine());
} catch (const std::exception& e) {
fprintf(stderr, "Error: %s\n", e.what());
return EXIT_FAILURE;
}
if (GetBoolArg("-rpcssl", false))
{
fprintf(stderr, "Error: SSL mode for RPC (-rpcssl) is no longer supported.\n");
return EXIT_FAILURE;
}
return CONTINUE_EXECUTION;
}
int CommandLineRPC(int argc, char *argv[])
{
std::string strPrint;
int nRet = 0;
try {
// Skip switches
while (argc > 1 && IsSwitchChar(argv[1][0])) {
argc--;
argv++;
}
std::vector<std::string> args = std::vector<std::string>(&argv[1], &argv[argc]);
if (GetBoolArg("-stdin", false)) {
// Read one arg per line from stdin and append
std::string line;
while (std::getline(std::cin,line))
args.push_back(line);
}
if (args.size() < 1)
throw std::runtime_error("too few parameters (need at least command)");
std::string strMethod = args[0];
args.erase(args.begin()); // Remove trailing method name from arguments vector
UniValue params;
if(GetBoolArg("-named", DEFAULT_NAMED)) {
params = RPCConvertNamedValues(strMethod, args);
} else {
params = RPCConvertValues(strMethod, args);
}
// Execute and handle connection failures with -rpcwait
const bool fWait = GetBoolArg("-rpcwait", false);
do {
try {
const UniValue reply = CallRPC(strMethod, params);
// Parse reply
const UniValue& result = find_value(reply, "result");
const UniValue& error = find_value(reply, "error");
if (!error.isNull()) {
// Error
int code = error["code"].get_int();
if (fWait && code == RPC_IN_WARMUP)
throw CConnectionFailed("server in warmup");
strPrint = "error: " + error.write();
nRet = abs(code);
if (error.isObject())
{
UniValue errCode = find_value(error, "code");
UniValue errMsg = find_value(error, "message");
strPrint = errCode.isNull() ? "" : "error code: "+errCode.getValStr()+"\n";
if (errMsg.isStr())
strPrint += "error message:\n"+errMsg.get_str();
}
} else {
// Result
if (result.isNull())
strPrint = "";
else if (result.isStr())
strPrint = result.get_str();
else
strPrint = result.write(2);
}
// Connection succeeded, no need to retry.
break;
}
catch (const CConnectionFailed&) {
if (fWait)
MilliSleep(1000);
else
throw;
}
} while (fWait);
}
catch (const boost::thread_interrupted&) {
throw;
}
catch (const std::exception& e) {
strPrint = std::string("error: ") + e.what();
nRet = EXIT_FAILURE;
}
catch (...) {
PrintExceptionContinue(NULL, "CommandLineRPC()");
throw;
}
if (strPrint != "") {
fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
}
return nRet;
}
int main(int argc, char* argv[])
{
SetupEnvironment();
if (!SetupNetworking()) {
fprintf(stderr, "Error: Initializing networking failed\n");
return EXIT_FAILURE;
}
try {
int ret = AppInitRPC(argc, argv);
if (ret != CONTINUE_EXECUTION)
return ret;
}
catch (const std::exception& e) {
PrintExceptionContinue(&e, "AppInitRPC()");
return EXIT_FAILURE;
} catch (...) {
PrintExceptionContinue(NULL, "AppInitRPC()");
return EXIT_FAILURE;
}
int ret = EXIT_FAILURE;
try {
ret = CommandLineRPC(argc, argv);
}
catch (const std::exception& e) {
PrintExceptionContinue(&e, "CommandLineRPC()");
} catch (...) {
PrintExceptionContinue(NULL, "CommandLineRPC()");
}
return ret;
}