Changeset View
Changeset View
Standalone View
Standalone View
src/bitcoin-cli.cpp
Show All 35 Lines | const auto testnetBaseParams = | ||||
CreateBaseChainParams(CBaseChainParams::TESTNET); | CreateBaseChainParams(CBaseChainParams::TESTNET); | ||||
std::string strUsage; | std::string strUsage; | ||||
strUsage += HelpMessageGroup(_("Options:")); | strUsage += HelpMessageGroup(_("Options:")); | ||||
strUsage += HelpMessageOpt("-?", _("This help message")); | strUsage += HelpMessageOpt("-?", _("This help message")); | ||||
strUsage += HelpMessageOpt( | strUsage += HelpMessageOpt( | ||||
"-conf=<file>", strprintf(_("Specify configuration file (default: %s)"), | "-conf=<file>", strprintf(_("Specify configuration file (default: %s)"), | ||||
BITCOIN_CONF_FILENAME)); | BITCOIN_CONF_FILENAME)); | ||||
strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory")); | strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory")); | ||||
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)")); | |||||
AppendParamsHelpMessages(strUsage); | AppendParamsHelpMessages(strUsage); | ||||
strUsage += HelpMessageOpt( | strUsage += HelpMessageOpt( | ||||
"-named", | "-named", | ||||
strprintf(_("Pass named instead of positional arguments (default: %s)"), | strprintf(_("Pass named instead of positional arguments (default: %s)"), | ||||
DEFAULT_NAMED)); | DEFAULT_NAMED)); | ||||
strUsage += HelpMessageOpt( | strUsage += HelpMessageOpt( | ||||
"-rpcconnect=<ip>", | "-rpcconnect=<ip>", | ||||
strprintf(_("Send commands to node running on <ip> (default: %s)"), | strprintf(_("Send commands to node running on <ip> (default: %s)"), | ||||
▲ Show 20 Lines • Show All 166 Lines • ▼ Show 20 Lines | |||||
#if LIBEVENT_VERSION_NUMBER >= 0x02010300 | #if LIBEVENT_VERSION_NUMBER >= 0x02010300 | ||||
static void http_error_cb(enum evhttp_request_error err, void *ctx) { | static void http_error_cb(enum evhttp_request_error err, void *ctx) { | ||||
HTTPReply *reply = static_cast<HTTPReply *>(ctx); | HTTPReply *reply = static_cast<HTTPReply *>(ctx); | ||||
reply->error = err; | reply->error = err; | ||||
} | } | ||||
#endif | #endif | ||||
static UniValue CallRPC(const std::string &strMethod, const UniValue ¶ms) { | /** | ||||
* Class that handles the conversion from a command-line to a JSON-RPC request, | |||||
* as well as converting back to a JSON object that can be shown as result. | |||||
*/ | |||||
class BaseRequestHandler { | |||||
public: | |||||
virtual UniValue PrepareRequest(const std::string &method, | |||||
const std::vector<std::string> &args) = 0; | |||||
virtual UniValue ProcessReply(const UniValue &batch_in) = 0; | |||||
}; | |||||
/** Process getinfo requests */ | |||||
class GetinfoRequestHandler : public BaseRequestHandler { | |||||
public: | |||||
const int ID_NETWORKINFO = 0; | |||||
const int ID_BLOCKCHAININFO = 1; | |||||
const int ID_WALLETINFO = 2; | |||||
/** Create a simulated `getinfo` request. */ | |||||
UniValue PrepareRequest(const std::string &method, | |||||
const std::vector<std::string> &args) override { | |||||
UniValue result(UniValue::VARR); | |||||
result.push_back( | |||||
JSONRPCRequestObj("getnetworkinfo", NullUniValue, ID_NETWORKINFO)); | |||||
result.push_back(JSONRPCRequestObj("getblockchaininfo", NullUniValue, | |||||
ID_BLOCKCHAININFO)); | |||||
result.push_back( | |||||
JSONRPCRequestObj("getwalletinfo", NullUniValue, ID_WALLETINFO)); | |||||
return result; | |||||
} | |||||
/** Collect values from the batch and form a simulated `getinfo` reply. */ | |||||
UniValue ProcessReply(const UniValue &batch_in) override { | |||||
UniValue result(UniValue::VOBJ); | |||||
std::vector<UniValue> batch = JSONRPCProcessBatchReply(batch_in, 3); | |||||
// Errors in getnetworkinfo() and getblockchaininfo() are fatal, pass | |||||
// them on getwalletinfo() is allowed to fail in case there is no | |||||
// wallet. | |||||
if (!batch[ID_NETWORKINFO]["error"].isNull()) { | |||||
return batch[ID_NETWORKINFO]; | |||||
} | |||||
if (!batch[ID_BLOCKCHAININFO]["error"].isNull()) { | |||||
return batch[ID_BLOCKCHAININFO]; | |||||
} | |||||
result.pushKV("version", batch[ID_NETWORKINFO]["result"]["version"]); | |||||
result.pushKV("protocolversion", | |||||
batch[ID_NETWORKINFO]["result"]["protocolversion"]); | |||||
if (!batch[ID_WALLETINFO].isNull()) { | |||||
result.pushKV("walletversion", | |||||
batch[ID_WALLETINFO]["result"]["walletversion"]); | |||||
result.pushKV("balance", batch[ID_WALLETINFO]["result"]["balance"]); | |||||
} | |||||
result.pushKV("blocks", batch[ID_BLOCKCHAININFO]["result"]["blocks"]); | |||||
result.pushKV("timeoffset", | |||||
batch[ID_NETWORKINFO]["result"]["timeoffset"]); | |||||
result.pushKV("connections", | |||||
batch[ID_NETWORKINFO]["result"]["connections"]); | |||||
result.pushKV("proxy", | |||||
batch[ID_NETWORKINFO]["result"]["networks"][0]["proxy"]); | |||||
result.pushKV("difficulty", | |||||
batch[ID_BLOCKCHAININFO]["result"]["difficulty"]); | |||||
result.pushKV( | |||||
"testnet", | |||||
UniValue(batch[ID_BLOCKCHAININFO]["result"]["chain"].get_str() == | |||||
"test")); | |||||
if (!batch[ID_WALLETINFO].isNull()) { | |||||
result.pushKV("walletversion", | |||||
batch[ID_WALLETINFO]["result"]["walletversion"]); | |||||
result.pushKV("balance", batch[ID_WALLETINFO]["result"]["balance"]); | |||||
result.pushKV("keypoololdest", | |||||
batch[ID_WALLETINFO]["result"]["keypoololdest"]); | |||||
result.pushKV("keypoolsize", | |||||
batch[ID_WALLETINFO]["result"]["keypoolsize"]); | |||||
if (!batch[ID_WALLETINFO]["result"]["unlocked_until"].isNull()) { | |||||
result.pushKV("unlocked_until", | |||||
batch[ID_WALLETINFO]["result"]["unlocked_until"]); | |||||
} | |||||
result.pushKV("paytxfee", | |||||
batch[ID_WALLETINFO]["result"]["paytxfee"]); | |||||
} | |||||
result.pushKV("relayfee", batch[ID_NETWORKINFO]["result"]["relayfee"]); | |||||
result.pushKV("warnings", batch[ID_NETWORKINFO]["result"]["warnings"]); | |||||
return JSONRPCReplyObj(result, NullUniValue, 1); | |||||
} | |||||
}; | |||||
/** Process default single requests */ | |||||
class DefaultRequestHandler : public BaseRequestHandler { | |||||
public: | |||||
UniValue PrepareRequest(const std::string &method, | |||||
const std::vector<std::string> &args) override { | |||||
UniValue params; | |||||
if (gArgs.GetBoolArg("-named", DEFAULT_NAMED)) { | |||||
params = RPCConvertNamedValues(method, args); | |||||
} else { | |||||
params = RPCConvertValues(method, args); | |||||
} | |||||
return JSONRPCRequestObj(method, params, 1); | |||||
} | |||||
UniValue ProcessReply(const UniValue &reply) override { | |||||
return reply.get_obj(); | |||||
} | |||||
}; | |||||
static UniValue CallRPC(BaseRequestHandler *rh, const std::string &strMethod, | |||||
const std::vector<std::string> &args) { | |||||
std::string host; | std::string host; | ||||
// In preference order, we choose the following for the port: | // In preference order, we choose the following for the port: | ||||
// 1. -rpcport | // 1. -rpcport | ||||
// 2. port in -rpcconnect (ie following : in ipv4 or ]: in ipv6) | // 2. port in -rpcconnect (ie following : in ipv4 or ]: in ipv6) | ||||
// 3. default port for chain | // 3. default port for chain | ||||
int port = BaseParams().RPCPort(); | int port = BaseParams().RPCPort(); | ||||
SplitHostPort(gArgs.GetArg("-rpcconnect", DEFAULT_RPCCONNECT), port, host); | SplitHostPort(gArgs.GetArg("-rpcconnect", DEFAULT_RPCCONNECT), port, host); | ||||
port = gArgs.GetArg("-rpcport", port); | port = gArgs.GetArg("-rpcport", port); | ||||
Show All 40 Lines | #endif | ||||
assert(output_headers); | assert(output_headers); | ||||
evhttp_add_header(output_headers, "Host", host.c_str()); | evhttp_add_header(output_headers, "Host", host.c_str()); | ||||
evhttp_add_header(output_headers, "Connection", "close"); | evhttp_add_header(output_headers, "Connection", "close"); | ||||
evhttp_add_header( | evhttp_add_header( | ||||
output_headers, "Authorization", | output_headers, "Authorization", | ||||
(std::string("Basic ") + EncodeBase64(strRPCUserColonPass)).c_str()); | (std::string("Basic ") + EncodeBase64(strRPCUserColonPass)).c_str()); | ||||
// Attach request data | // Attach request data | ||||
std::string strRequest = | std::string strRequest = rh->PrepareRequest(strMethod, args).write() + "\n"; | ||||
JSONRPCRequestObj(strMethod, params, 1).write() + "\n"; | |||||
struct evbuffer *output_buffer = | struct evbuffer *output_buffer = | ||||
evhttp_request_get_output_buffer(req.get()); | evhttp_request_get_output_buffer(req.get()); | ||||
assert(output_buffer); | assert(output_buffer); | ||||
evbuffer_add(output_buffer, strRequest.data(), strRequest.size()); | evbuffer_add(output_buffer, strRequest.data(), strRequest.size()); | ||||
// check if we should use a special wallet endpoint | // check if we should use a special wallet endpoint | ||||
std::string endpoint = "/"; | std::string endpoint = "/"; | ||||
std::string walletName = gArgs.GetArg("-rpcwallet", ""); | std::string walletName = gArgs.GetArg("-rpcwallet", ""); | ||||
Show All 34 Lines | if (response.status == 0) { | ||||
throw std::runtime_error("no response from server"); | throw std::runtime_error("no response from server"); | ||||
} | } | ||||
// Parse reply | // Parse reply | ||||
UniValue valReply(UniValue::VSTR); | UniValue valReply(UniValue::VSTR); | ||||
if (!valReply.read(response.body)) { | if (!valReply.read(response.body)) { | ||||
throw std::runtime_error("couldn't parse reply from server"); | throw std::runtime_error("couldn't parse reply from server"); | ||||
} | } | ||||
const UniValue &reply = valReply.get_obj(); | const UniValue reply = rh->ProcessReply(valReply); | ||||
if (reply.empty()) { | if (reply.empty()) { | ||||
throw std::runtime_error( | throw std::runtime_error( | ||||
"expected reply to have result, error and id properties"); | "expected reply to have result, error and id properties"); | ||||
} | } | ||||
return reply; | return reply; | ||||
} | } | ||||
Show All 17 Lines | try { | ||||
std::vector<std::string>(&argv[1], &argv[argc]); | std::vector<std::string>(&argv[1], &argv[argc]); | ||||
if (gArgs.GetBoolArg("-stdin", false)) { | if (gArgs.GetBoolArg("-stdin", false)) { | ||||
// Read one arg per line from stdin and append | // Read one arg per line from stdin and append | ||||
std::string line; | std::string line; | ||||
while (std::getline(std::cin, line)) { | while (std::getline(std::cin, line)) { | ||||
args.push_back(line); | args.push_back(line); | ||||
} | } | ||||
} | } | ||||
std::unique_ptr<BaseRequestHandler> rh; | |||||
std::string method; | |||||
if (gArgs.GetBoolArg("-getinfo", false)) { | |||||
rh.reset(new GetinfoRequestHandler()); | |||||
method = ""; | |||||
} else { | |||||
rh.reset(new DefaultRequestHandler()); | |||||
if (args.size() < 1) { | if (args.size() < 1) { | ||||
throw std::runtime_error( | throw std::runtime_error( | ||||
"too few parameters (need at least command)"); | "too few parameters (need at least command)"); | ||||
} | } | ||||
std::string strMethod = args[0]; | method = args[0]; | ||||
// Remove trailing method name from arguments vector | // Remove trailing method name from arguments vector | ||||
args.erase(args.begin()); | args.erase(args.begin()); | ||||
UniValue params; | |||||
if (gArgs.GetBoolArg("-named", DEFAULT_NAMED)) { | |||||
params = RPCConvertNamedValues(strMethod, args); | |||||
} else { | |||||
params = RPCConvertValues(strMethod, args); | |||||
} | } | ||||
// Execute and handle connection failures with -rpcwait | // Execute and handle connection failures with -rpcwait | ||||
const bool fWait = gArgs.GetBoolArg("-rpcwait", false); | const bool fWait = gArgs.GetBoolArg("-rpcwait", false); | ||||
do { | do { | ||||
try { | try { | ||||
const UniValue reply = CallRPC(strMethod, params); | const UniValue reply = CallRPC(rh.get(), method, args); | ||||
// Parse reply | // Parse reply | ||||
const UniValue &result = find_value(reply, "result"); | const UniValue &result = find_value(reply, "result"); | ||||
const UniValue &error = find_value(reply, "error"); | const UniValue &error = find_value(reply, "error"); | ||||
if (!error.isNull()) { | if (!error.isNull()) { | ||||
// Error | // Error | ||||
int code = error["code"].get_int(); | int code = error["code"].get_int(); | ||||
▲ Show 20 Lines • Show All 89 Lines • Show Last 20 Lines |