diff --git a/src/Makefile.am b/src/Makefile.am index 3016be47b..0a7682919 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -171,7 +171,6 @@ nodist_libbitcoin_common_a_SOURCES = $(srcdir)/obj/build.h # bitcoind binary # bitcoind_LDADD = \ libbitcoin_server.a \ - libbitcoin_cli.a \ libbitcoin_common.a \ $(LIBLEVELDB) \ $(LIBMEMENV) diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index 0c7d6fe5a..c7327fd7c 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -12,6 +12,32 @@ #include +using namespace boost; +using namespace boost::asio; +using namespace json_spirit; + +std::string HelpMessageCli() +{ + string strUsage; + strUsage += _("Options:") + "\n"; + strUsage += " -? " + _("This help message") + "\n"; + strUsage += " -conf= " + _("Specify configuration file (default: bitcoin.conf)") + "\n"; + strUsage += " -datadir= " + _("Specify data directory") + "\n"; + strUsage += " -testnet " + _("Use the test network") + "\n"; + strUsage += " -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.") + "\n"; + strUsage += " -rpcconnect= " + _("Send commands to node running on (default: 127.0.0.1)") + "\n"; + strUsage += " -rpcport= " + _("Connect to JSON-RPC on (default: 8332 or testnet: 18332)") + "\n"; + strUsage += " -rpcwait " + _("Wait for RPC server to start") + "\n"; + strUsage += " -rpcuser= " + _("Username for JSON-RPC connections") + "\n"; + strUsage += " -rpcpassword= " + _("Password for JSON-RPC connections") + "\n"; + + strUsage += "\n" + _("SSL options: (see the Bitcoin Wiki for SSL setup instructions)") + "\n"; + strUsage += " -rpcssl " + _("Use OpenSSL (https) for JSON-RPC connections") + "\n"; + + return strUsage; +} + ////////////////////////////////////////////////////////////////////////////// // // Start @@ -49,7 +75,7 @@ static bool AppInitRPC(int argc, char* argv[]) " bitcoin-cli [options] help " + _("List commands") + "\n" + " bitcoin-cli [options] help " + _("Get help for a command") + "\n"; - strUsage += "\n" + HelpMessageCli(true); + strUsage += "\n" + HelpMessageCli(); } fprintf(stdout, "%s", strUsage.c_str()); @@ -58,6 +84,136 @@ static bool AppInitRPC(int argc, char* argv[]) return true; } +Object CallRPC(const string& strMethod, const Array& params) +{ + if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "") + throw runtime_error(strprintf( + _("You must set rpcpassword= 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())); + + // Connect to localhost + bool fUseSSL = GetBoolArg("-rpcssl", false); + asio::io_service io_service; + ssl::context context(io_service, ssl::context::sslv23); + context.set_options(ssl::context::no_sslv2); + asio::ssl::stream sslStream(io_service, context); + SSLIOStreamDevice d(sslStream, fUseSSL); + iostreams::stream< SSLIOStreamDevice > stream(d); + + bool fWait = GetBoolArg("-rpcwait", false); // -rpcwait means try until server has started + do { + bool fConnected = d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", itostr(Params().RPCPort()))); + if (fConnected) break; + if (fWait) + MilliSleep(1000); + else + throw runtime_error("couldn't connect to server"); + } while (fWait); + + // HTTP basic authentication + string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]); + map mapRequestHeaders; + mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64; + + // 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 mapHeaders; + string strReply; + ReadHTTPMessage(stream, mapHeaders, strReply, nProto); + + 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 + Value valReply; + if (!read_string(strReply, valReply)) + throw runtime_error("couldn't parse reply from server"); + const Object& 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 strParams(&argv[2], &argv[argc]); + Array params = RPCConvertValues(strMethod, strParams); + + // Execute + Object reply = CallRPC(strMethod, params); + + // Parse reply + const Value& result = find_value(reply, "result"); + const Value& error = find_value(reply, "error"); + + if (error.type() != null_type) + { + // Error + strPrint = "error: " + write_string(error, false); + int code = find_value(error.get_obj(), "code").get_int(); + nRet = abs(code); + } + else + { + // Result + if (result.type() == null_type) + strPrint = ""; + else if (result.type() == str_type) + strPrint = result.get_str(); + else + strPrint = write_string(result, true); + } + } + catch (boost::thread_interrupted) { + throw; + } + catch (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(); diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index b7d8ee7f9..880955481 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -4,7 +4,6 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "rpcserver.h" -#include "rpcclient.h" #include "init.h" #include "main.h" #include "noui.h" @@ -94,14 +93,9 @@ bool AppInit(int argc, char* argv[]) else { strUsage += "\n" + _("Usage:") + "\n" + - " bitcoind [options] " + _("Start Bitcoin Core Daemon") + "\n" + - _("Usage (deprecated, use bitcoin-cli):") + "\n" + - " bitcoind [options] [params] " + _("Send command to Bitcoin Core") + "\n" + - " bitcoind [options] help " + _("List commands") + "\n" + - " bitcoind [options] help " + _("Get help for a command") + "\n"; + " bitcoind [options] " + _("Start Bitcoin Core Daemon") + "\n"; strUsage += "\n" + HelpMessage(HMM_BITCOIND); - strUsage += "\n" + HelpMessageCli(false); } fprintf(stdout, "%s", strUsage.c_str()); @@ -116,8 +110,8 @@ bool AppInit(int argc, char* argv[]) if (fCommandLine) { - int ret = CommandLineRPC(argc, argv); - exit(ret); + fprintf(stderr, "Error: There is no RPC client functionality in bitcoind anymore. Use the bitcoin-cli utility instead.\n"); + exit(1); } #ifndef WIN32 fDaemon = GetBoolArg("-daemon", false); diff --git a/src/rpcclient.cpp b/src/rpcclient.cpp index 1a5b3f741..3a06e3301 100644 --- a/src/rpcclient.cpp +++ b/src/rpcclient.cpp @@ -12,86 +12,9 @@ #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "json/json_spirit_writer_template.h" - using namespace std; -using namespace boost; -using namespace boost::asio; using namespace json_spirit; -Object CallRPC(const string& strMethod, const Array& params) -{ - if (mapArgs["-rpcuser"] == "" && mapArgs["-rpcpassword"] == "") - throw runtime_error(strprintf( - _("You must set rpcpassword= 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())); - - // Connect to localhost - bool fUseSSL = GetBoolArg("-rpcssl", false); - asio::io_service io_service; - ssl::context context(io_service, ssl::context::sslv23); - context.set_options(ssl::context::no_sslv2); - asio::ssl::stream sslStream(io_service, context); - SSLIOStreamDevice d(sslStream, fUseSSL); - iostreams::stream< SSLIOStreamDevice > stream(d); - - bool fWait = GetBoolArg("-rpcwait", false); // -rpcwait means try until server has started - do { - bool fConnected = d.connect(GetArg("-rpcconnect", "127.0.0.1"), GetArg("-rpcport", itostr(Params().RPCPort()))); - if (fConnected) break; - if (fWait) - MilliSleep(1000); - else - throw runtime_error("couldn't connect to server"); - } while (fWait); - - // HTTP basic authentication - string strUserPass64 = EncodeBase64(mapArgs["-rpcuser"] + ":" + mapArgs["-rpcpassword"]); - map mapRequestHeaders; - mapRequestHeaders["Authorization"] = string("Basic ") + strUserPass64; - - // 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 mapHeaders; - string strReply; - ReadHTTPMessage(stream, mapHeaders, strReply, nProto); - - 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 - Value valReply; - if (!read_string(strReply, valReply)) - throw runtime_error("couldn't parse reply from server"); - const Object& reply = valReply.get_obj(); - if (reply.empty()) - throw runtime_error("expected reply to have result, error and id properties"); - - return reply; -} - template void ConvertTo(Value& value, bool fAllowNull=false) { @@ -182,101 +105,3 @@ Array RPCConvertValues(const std::string &strMethod, const std::vector 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 strParams(&argv[2], &argv[argc]); - Array params = RPCConvertValues(strMethod, strParams); - - // Execute - Object reply = CallRPC(strMethod, params); - - // Parse reply - const Value& result = find_value(reply, "result"); - const Value& error = find_value(reply, "error"); - - if (error.type() != null_type) - { - // Error - strPrint = "error: " + write_string(error, false); - int code = find_value(error.get_obj(), "code").get_int(); - nRet = abs(code); - } - else - { - // Result - if (result.type() == null_type) - strPrint = ""; - else if (result.type() == str_type) - strPrint = result.get_str(); - else - strPrint = write_string(result, true); - } - } - catch (boost::thread_interrupted) { - throw; - } - catch (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; -} - -std::string HelpMessageCli(bool mainProgram) -{ - string strUsage; - if(mainProgram) - { - strUsage += _("Options:") + "\n"; - strUsage += " -? " + _("This help message") + "\n"; - strUsage += " -conf= " + _("Specify configuration file (default: bitcoin.conf)") + "\n"; - strUsage += " -datadir= " + _("Specify data directory") + "\n"; - strUsage += " -testnet " + _("Use the test network") + "\n"; - strUsage += " -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.") + "\n"; - } else { - strUsage += _("RPC client options:") + "\n"; - } - - strUsage += " -rpcconnect= " + _("Send commands to node running on (default: 127.0.0.1)") + "\n"; - strUsage += " -rpcport= " + _("Connect to JSON-RPC on (default: 8332 or testnet: 18332)") + "\n"; - strUsage += " -rpcwait " + _("Wait for RPC server to start") + "\n"; - - if(mainProgram) - { - strUsage += " -rpcuser= " + _("Username for JSON-RPC connections") + "\n"; - strUsage += " -rpcpassword= " + _("Password for JSON-RPC connections") + "\n"; - - strUsage += "\n" + _("SSL options: (see the Bitcoin Wiki for SSL setup instructions)") + "\n"; - strUsage += " -rpcssl " + _("Use OpenSSL (https) for JSON-RPC connections") + "\n"; - } - - return strUsage; -} - diff --git a/src/rpcclient.h b/src/rpcclient.h index e101d22ec..840890e34 100644 --- a/src/rpcclient.h +++ b/src/rpcclient.h @@ -10,16 +10,6 @@ #include "json/json_spirit_utils.h" #include "json/json_spirit_writer_template.h" -int CommandLineRPC(int argc, char *argv[]); - json_spirit::Array RPCConvertValues(const std::string &strMethod, const std::vector &strParams); -/** Show help message for bitcoin-cli. - * The mainProgram argument is used to determine whether to show this message as main program - * (and include some common options) or as sub-header of another help message. - * - * @note the argument can be removed once bitcoin-cli functionality is removed from bitcoind - */ -std::string HelpMessageCli(bool mainProgram); - #endif