|
|
|
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|
|
|
// Copyright (c) 2009-2013 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 "chainparamsbase.h"
|
|
|
|
#include "clientversion.h"
|
|
|
|
#include "rpcclient.h"
|
|
|
|
#include "rpcprotocol.h"
|
|
|
|
#include "util.h"
|
Split up util.cpp/h
Split up util.cpp/h into:
- string utilities (hex, base32, base64): no internal dependencies, no dependency on boost (apart from foreach)
- money utilities (parsesmoney, formatmoney)
- time utilities (gettime*, sleep, format date):
- and the rest (logging, argument parsing, config file parsing)
The latter is basically the environment and OS handling,
and is stripped of all utility functions, so we may want to
rename it to something else than util.cpp/h for clarity (Matt suggested
osinterface).
Breaks dependency of sha256.cpp on all the things pulled in by util.
10 years ago
|
|
|
#include "utilstrencodings.h"
|
|
|
|
|
|
|
|
#include <boost/filesystem/operations.hpp>
|
|
|
|
|
|
|
|
#include "univalue/univalue.h"
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
|
|
|
std::string HelpMessageCli()
|
|
|
|
{
|
|
|
|
string strUsage;
|
|
|
|
strUsage += HelpMessageGroup(_("Options:"));
|
|
|
|
strUsage += HelpMessageOpt("-?", _("This help message"));
|
|
|
|
strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file (default: %s)"), "bitcoin.conf"));
|
|
|
|
strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory"));
|
|
|
|
strUsage += HelpMessageOpt("-testnet", _("Use the test network"));
|
|
|
|
strUsage += HelpMessageOpt("-regtest", _("Enter regression test mode, which uses a special chain in which blocks can be "
|
|
|
|
"solved instantly. This is intended for regression testing tools and app development."));
|
|
|
|
strUsage += HelpMessageOpt("-rpcconnect=<ip>", strprintf(_("Send commands to node running on <ip> (default: %s)"), "127.0.0.1"));
|
|
|
|
strUsage += HelpMessageOpt("-rpcport=<port>", strprintf(_("Connect to JSON-RPC on <port> (default: %u or testnet: %u)"), 8332, 18332));
|
|
|
|
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 += HelpMessageGroup(_("SSL options: (see the Bitcoin Wiki for SSL setup instructions)"));
|
|
|
|
strUsage += HelpMessageOpt("-rpcssl", _("Use OpenSSL (https) for JSON-RPC connections"));
|
|
|
|
|
|
|
|
return strUsage;
|
|
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Start
|
|
|
|
//
|
|
|
|
|
|
|
|
//
|
|
|
|
// Exception thrown on connection error. This error is used to determine
|
|
|
|
// when to wait if -rpcwait is given.
|
|
|
|
//
|
|
|
|
class CConnectionFailed : public std::runtime_error
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
|
|
|
|
explicit inline CConnectionFailed(const std::string& msg) :
|
|
|
|
std::runtime_error(msg)
|
|
|
|
{}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
static bool AppInitRPC(int argc, char* argv[])
|
|
|
|
{
|
|
|
|
//
|
|
|
|
// Parameters
|
|
|
|
//
|
|
|
|
ParseParameters(argc, argv);
|
|
|
|
if (argc<2 || mapArgs.count("-?") || mapArgs.count("-help") || mapArgs.count("-version")) {
|
|
|
|
std::string strUsage = _("Bitcoin Core RPC client version") + " " + FormatFullVersion() + "\n";
|
|
|
|
if (!mapArgs.count("-version")) {
|
|
|
|
strUsage += "\n" + _("Usage:") + "\n" +
|
|
|
|
" bitcoin-cli [options] <command> [params] " + _("Send command to Bitcoin Core") + "\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());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!boost::filesystem::is_directory(GetDataDir(false))) {
|
|
|
|
fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", mapArgs["-datadir"].c_str());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
ReadConfigFile(mapArgs, mapMultiArgs);
|
|
|
|
} catch (const std::exception& e) {
|
|
|
|
fprintf(stderr,"Error reading configuration file: %s\n", e.what());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// Check for -testnet or -regtest parameter (BaseParams() calls are only valid after this clause)
|
|
|
|
if (!SelectBaseParamsFromCommandLine()) {
|
|
|
|
fprintf(stderr, "Error: Invalid combination of -regtest and -testnet.\n");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
UniValue CallRPC(const string& strMethod, const UniValue& params)
|
|
|
|
{
|
|
|
|
// Connect to localhost
|
|
|
|
bool fUseSSL = GetBoolArg("-rpcssl", false);
|
|
|
|
boost::asio::io_service io_service;
|
|
|
|
boost::asio::ssl::context context(io_service, boost::asio::ssl::context::sslv23);
|
|
|
|
context.set_options(boost::asio::ssl::context::no_sslv2 | boost::asio::ssl::context::no_sslv3);
|
|
|
|
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> sslStream(io_service, context);
|
|
|
|
SSLIOStreamDevice<boost::asio::ip::tcp> d(sslStream, fUseSSL);
|
|
|
|
boost::iostreams::stream< SSLIOStreamDevice<boost::asio::ip::tcp> > stream(d);
|
|
|
|
|
|
|
|
const bool fConnected = d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", itostr(BaseParams().RPCPort())));
|
|
|
|
if (!fConnected)
|
|
|
|
throw CConnectionFailed("couldn't connect to server");
|
|
|
|
|
|
|
|
// Find credentials to use
|
|
|
|
std::string strRPCUserColonPass;
|
|
|
|
if (mapArgs["-rpcpassword"] == "") {
|
|
|
|
// Try fall back to cookie-based authentication if no password is provided
|
|
|
|
if (!GetAuthCookie(&strRPCUserColonPass)) {
|
|
|
|
throw runtime_error(strprintf(
|
|
|
|
_("You must set rpcpassword=<password> in the configuration file:\n%s\n"
|
|
|
|
"If the file does not exist, create it with owner-readable-only file permissions."),
|
|
|
|
GetConfigFile().string().c_str()));
|
|
|
|
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
strRPCUserColonPass = mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"];
|
|
|
|
}
|
|
|
|
|
|
|
|
// HTTP basic authentication
|
|
|
|
map<string, string> mapRequestHeaders;
|
|
|
|
mapRequestHeaders["Authorization"] = string("Basic ") + EncodeBase64(strRPCUserColonPass);
|
|
|
|
|
|
|
|
// Send request
|
|
|
|
string strRequest = JSONRPCRequest(strMethod, params, 1);
|
|
|
|
string strPost = HTTPPost(strRequest, mapRequestHeaders);
|
|
|
|
stream << strPost << std::flush;
|
|
|
|
|
|
|
|
// Receive HTTP reply status
|
|
|
|
int nProto = 0;
|
|
|
|
int nStatus = ReadHTTPStatus(stream, nProto);
|
|
|
|
|
|
|
|
// Receive HTTP reply message headers and body
|
|
|
|
map<string, string> mapHeaders;
|
|
|
|
string strReply;
|
|
|
|
ReadHTTPMessage(stream, mapHeaders, strReply, nProto, std::numeric_limits<size_t>::max());
|
|
|
|
|
|
|
|
if (nStatus == HTTP_UNAUTHORIZED)
|
|
|
|
throw runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
|
|
|
|
else if (nStatus >= 400 && nStatus != HTTP_BAD_REQUEST && nStatus != HTTP_NOT_FOUND && nStatus != HTTP_INTERNAL_SERVER_ERROR)
|
|
|
|
throw runtime_error(strprintf("server returned HTTP error %d", nStatus));
|
|
|
|
else if (strReply.empty())
|
|
|
|
throw runtime_error("no response from server");
|
|
|
|
|
|
|
|
// Parse reply
|
|
|
|
UniValue valReply(UniValue::VSTR);
|
|
|
|
if (!valReply.read(strReply))
|
|
|
|
throw runtime_error("couldn't parse reply from server");
|
|
|
|
const UniValue& reply = valReply.get_obj();
|
|
|
|
if (reply.empty())
|
|
|
|
throw runtime_error("expected reply to have result, error and id properties");
|
|
|
|
|
|
|
|
return reply;
|
|
|
|
}
|
|
|
|
|
|
|
|
int CommandLineRPC(int argc, char *argv[])
|
|
|
|
{
|
|
|
|
string strPrint;
|
|
|
|
int nRet = 0;
|
|
|
|
try {
|
|
|
|
// Skip switches
|
|
|
|
while (argc > 1 && IsSwitchChar(argv[1][0])) {
|
|
|
|
argc--;
|
|
|
|
argv++;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Method
|
|
|
|
if (argc < 2)
|
|
|
|
throw runtime_error("too few parameters");
|
|
|
|
string strMethod = argv[1];
|
|
|
|
|
|
|
|
// Parameters default to strings
|
|
|
|
std::vector<std::string> strParams(&argv[2], &argv[argc]);
|
|
|
|
UniValue params = RPCConvertValues(strMethod, strParams);
|
|
|
|
|
|
|
|
// 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 = 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();
|
|
|
|
|
|
|
|
try {
|
|
|
|
if(!AppInitRPC(argc, argv))
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
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;
|
|
|
|
}
|