diff --git a/src/bench/bench_bitcoin.cpp b/src/bench/bench_bitcoin.cpp index b40b01e6f..8c4d89040 100644 --- a/src/bench/bench_bitcoin.cpp +++ b/src/bench/bench_bitcoin.cpp @@ -1,98 +1,97 @@ // Copyright (c) 2015-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 "bench.h" #include "crypto/sha256.h" #include "key.h" #include "random.h" #include "util.h" #include "validation.h" #include #include static const int64_t DEFAULT_BENCH_EVALUATIONS = 5; static const char *DEFAULT_BENCH_FILTER = ".*"; static const char *DEFAULT_BENCH_SCALING = "1.0"; static const char *DEFAULT_BENCH_PRINTER = "console"; static const char *DEFAULT_PLOT_PLOTLYURL = "https://cdn.plot.ly/plotly-latest.min.js"; static const int64_t DEFAULT_PLOT_WIDTH = 1024; static const int64_t DEFAULT_PLOT_HEIGHT = 768; int main(int argc, char **argv) { gArgs.ParseParameters(argc, argv); - if (gArgs.IsArgSet("-?") || gArgs.IsArgSet("-h") || - gArgs.IsArgSet("-help")) { + if (HelpRequested(gArgs)) { std::cout << HelpMessageGroup(_("Options:")) << HelpMessageOpt("-?", _("Print this help message and exit")) << HelpMessageOpt("-list", _("List benchmarks without executing them. Can " "be combined with -scaling and -filter")) << HelpMessageOpt("-evals=", strprintf(_("Number of measurement evaluations " "to perform. (default: %u)"), DEFAULT_BENCH_EVALUATIONS)) << HelpMessageOpt("-filter=", strprintf(_("Regular expression filter to select " "benchmark by name (default: %s)"), DEFAULT_BENCH_FILTER)) << HelpMessageOpt("-scaling=", strprintf(_("Scaling factor for benchmark's " "runtime (default: %u)"), DEFAULT_BENCH_SCALING)) << HelpMessageOpt( "-printer=(console|plot)", strprintf(_("Choose printer format. console: print data to " "console. plot: Print results as HTML graph " "(default: %s)"), DEFAULT_BENCH_PRINTER)) << HelpMessageOpt( "-plot-plotlyurl=", strprintf(_("URL to use for plotly.js (default: %s)"), DEFAULT_PLOT_PLOTLYURL)) << HelpMessageOpt("-plot-width=", strprintf(_("Plot width in pixel (default: %u)"), DEFAULT_PLOT_WIDTH)) << HelpMessageOpt("-plot-height=", strprintf(_("Plot height in pixel (default: %u)"), DEFAULT_PLOT_HEIGHT)); return 0; } SHA256AutoDetect(); RandomInit(); ECC_Start(); SetupEnvironment(); // don't want to write to debug.log file GetLogger().m_print_to_file = false; int64_t evaluations = gArgs.GetArg("-evals", DEFAULT_BENCH_EVALUATIONS); std::string regex_filter = gArgs.GetArg("-filter", DEFAULT_BENCH_FILTER); std::string scaling_str = gArgs.GetArg("-scaling", DEFAULT_BENCH_SCALING); bool is_list_only = gArgs.GetBoolArg("-list", false); double scaling_factor = boost::lexical_cast(scaling_str); std::unique_ptr printer( new benchmark::ConsolePrinter()); std::string printer_arg = gArgs.GetArg("-printer", DEFAULT_BENCH_PRINTER); if ("plot" == printer_arg) { printer.reset(new benchmark::PlotlyPrinter( gArgs.GetArg("-plot-plotlyurl", DEFAULT_PLOT_PLOTLYURL), gArgs.GetArg("-plot-width", DEFAULT_PLOT_WIDTH), gArgs.GetArg("-plot-height", DEFAULT_PLOT_HEIGHT))); } benchmark::BenchRunner::RunAll(*printer, evaluations, scaling_factor, regex_filter, is_list_only); ECC_Stop(); } diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index 0a809b1c5..5542a3897 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -1,486 +1,485 @@ // 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. #if defined(HAVE_CONFIG_H) #include "config/bitcoin-config.h" #endif #include "chainparamsbase.h" #include "clientversion.h" #include "fs.h" #include "rpc/client.h" #include "rpc/protocol.h" #include "support/events.h" #include "util.h" #include "utilstrencodings.h" #include #include #include #include #include static const char DEFAULT_RPCCONNECT[] = "127.0.0.1"; static const int DEFAULT_HTTP_CLIENT_TIMEOUT = 900; static const bool DEFAULT_NAMED = false; static const int CONTINUE_EXECUTION = -1; std::string HelpMessageCli() { const auto defaultBaseParams = CreateBaseChainParams(CBaseChainParams::MAIN); const auto testnetBaseParams = CreateBaseChainParams(CBaseChainParams::TESTNET); std::string strUsage; strUsage += HelpMessageGroup(_("Options:")); strUsage += HelpMessageOpt("-?", _("This help message")); strUsage += HelpMessageOpt( "-conf=", strprintf(_("Specify configuration file (default: %s)"), BITCOIN_CONF_FILENAME)); strUsage += HelpMessageOpt("-datadir=", _("Specify data directory")); AppendParamsHelpMessages(strUsage); strUsage += HelpMessageOpt( "-named", strprintf(_("Pass named instead of positional arguments (default: %s)"), DEFAULT_NAMED)); strUsage += HelpMessageOpt( "-rpcconnect=", strprintf(_("Send commands to node running on (default: %s)"), DEFAULT_RPCCONNECT)); strUsage += HelpMessageOpt( "-rpcport=", strprintf( _("Connect to JSON-RPC on (default: %u or testnet: %u)"), defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort())); strUsage += HelpMessageOpt("-rpcwait", _("Wait for RPC server to start")); strUsage += HelpMessageOpt("-rpcuser=", _("Username for JSON-RPC connections")); strUsage += HelpMessageOpt("-rpcpassword=", _("Password for JSON-RPC connections")); strUsage += HelpMessageOpt("-rpcclienttimeout=", strprintf(_("Timeout in seconds during HTTP requests, " "or 0 for no timeout. (default: %d)"), DEFAULT_HTTP_CLIENT_TIMEOUT)); 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."))); strUsage += HelpMessageOpt( "-stdin", _("Read extra arguments from standard input, one per line " "until EOF/Ctrl-D (recommended for sensitive information " "such as passphrases)")); strUsage += HelpMessageOpt( "-rpcwallet=", _("Send RPC for non-default wallet on RPC server (argument is wallet " "filename in bitcoind directory, required if bitcoind/-Qt runs with " "multiple wallets)")); 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) {} }; // // 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 // gArgs.ParseParameters(argc, argv); - if (argc < 2 || gArgs.IsArgSet("-?") || gArgs.IsArgSet("-h") || - gArgs.IsArgSet("-help") || gArgs.IsArgSet("-version")) { + if (argc < 2 || HelpRequested(gArgs) || gArgs.IsArgSet("-version")) { std::string strUsage = strprintf(_("%s RPC client version"), _(PACKAGE_NAME)) + " " + FormatFullVersion() + "\n"; if (!gArgs.IsArgSet("-version")) { strUsage += "\n" + _("Usage:") + "\n" + " bitcoin-cli [options] [params] " + strprintf(_("Send command to %s"), _(PACKAGE_NAME)) + "\n" + " bitcoin-cli [options] -named [name=value] ... " + strprintf(_("Send command to %s (with named arguments)"), _(PACKAGE_NAME)) + "\n" + " bitcoin-cli [options] help " + _("List commands") + "\n" + " bitcoin-cli [options] help " + _("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 (!fs::is_directory(GetDataDir(false))) { fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "").c_str()); return EXIT_FAILURE; } try { gArgs.ReadConfigFile(gArgs.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 -testnet or -regtest parameter (BaseParams() calls are only // valid after this clause) try { SelectBaseParams(gArgs.GetChainName()); } catch (const std::exception &e) { fprintf(stderr, "Error: %s\n", e.what()); return EXIT_FAILURE; } if (gArgs.GetBoolArg("-rpcssl", false)) { fprintf(stderr, "Error: SSL mode for RPC (-rpcssl) is no longer supported.\n"); return EXIT_FAILURE; } return CONTINUE_EXECUTION; } /** Reply structure for request_done to fill in */ struct HTTPReply { HTTPReply() : status(0), error(-1) {} int status; int error; std::string body; }; const char *http_errorstring(int code) { switch (code) { #if LIBEVENT_VERSION_NUMBER >= 0x02010300 case EVREQ_HTTP_TIMEOUT: return "timeout reached"; case EVREQ_HTTP_EOF: return "EOF reached"; case EVREQ_HTTP_INVALID_HEADER: return "error while reading header, or invalid header"; case EVREQ_HTTP_BUFFER_ERROR: return "error encountered while reading or writing"; case EVREQ_HTTP_REQUEST_CANCEL: return "request was canceled"; case EVREQ_HTTP_DATA_TOO_LONG: return "response body is larger than allowed"; #endif default: return "unknown"; } } static void http_request_done(struct evhttp_request *req, void *ctx) { HTTPReply *reply = static_cast(ctx); if (req == nullptr) { /** * If req is nullptr, it means an error occurred while connecting: the * error code will have been passed to http_error_cb. */ reply->status = 0; return; } reply->status = evhttp_request_get_response_code(req); struct evbuffer *buf = evhttp_request_get_input_buffer(req); if (buf) { size_t size = evbuffer_get_length(buf); const char *data = (const char *)evbuffer_pullup(buf, size); if (data) reply->body = std::string(data, size); evbuffer_drain(buf, size); } } #if LIBEVENT_VERSION_NUMBER >= 0x02010300 static void http_error_cb(enum evhttp_request_error err, void *ctx) { HTTPReply *reply = static_cast(ctx); reply->error = err; } #endif static UniValue CallRPC(const std::string &strMethod, const UniValue ¶ms) { std::string host; // In preference order, we choose the following for the port: // 1. -rpcport // 2. port in -rpcconnect (ie following : in ipv4 or ]: in ipv6) // 3. default port for chain int port = BaseParams().RPCPort(); SplitHostPort(gArgs.GetArg("-rpcconnect", DEFAULT_RPCCONNECT), port, host); port = gArgs.GetArg("-rpcport", port); // Obtain event base raii_event_base base = obtain_event_base(); // Synchronously look up hostname raii_evhttp_connection evcon = obtain_evhttp_connection_base(base.get(), host, port); evhttp_connection_set_timeout( evcon.get(), gArgs.GetArg("-rpcclienttimeout", DEFAULT_HTTP_CLIENT_TIMEOUT)); HTTPReply response; raii_evhttp_request req = obtain_evhttp_request(http_request_done, (void *)&response); if (req == nullptr) throw std::runtime_error("create http request failed"); #if LIBEVENT_VERSION_NUMBER >= 0x02010300 evhttp_request_set_error_cb(req.get(), http_error_cb); #endif // Get credentials std::string strRPCUserColonPass; if (gArgs.GetArg("-rpcpassword", "") == "") { // Try fall back to cookie-based authentication if no password is // provided if (!GetAuthCookie(&strRPCUserColonPass)) { throw std::runtime_error(strprintf( _("Could not locate RPC credentials. No authentication cookie " "could be found, and RPC password is not set. See " "-rpcpassword and -stdinrpcpass. Configuration file: (%s)"), GetConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)) .string() .c_str())); } } else { strRPCUserColonPass = gArgs.GetArg("-rpcuser", "") + ":" + gArgs.GetArg("-rpcpassword", ""); } struct evkeyvalq *output_headers = evhttp_request_get_output_headers(req.get()); assert(output_headers); evhttp_add_header(output_headers, "Host", host.c_str()); evhttp_add_header(output_headers, "Connection", "close"); evhttp_add_header( output_headers, "Authorization", (std::string("Basic ") + EncodeBase64(strRPCUserColonPass)).c_str()); // Attach request data std::string strRequest = JSONRPCRequestObj(strMethod, params, 1).write() + "\n"; struct evbuffer *output_buffer = evhttp_request_get_output_buffer(req.get()); assert(output_buffer); evbuffer_add(output_buffer, strRequest.data(), strRequest.size()); // check if we should use a special wallet endpoint std::string endpoint = "/"; std::string walletName = gArgs.GetArg("-rpcwallet", ""); if (!walletName.empty()) { char *encodedURI = evhttp_uriencode(walletName.c_str(), walletName.size(), false); if (encodedURI) { endpoint = "/wallet/" + std::string(encodedURI); free(encodedURI); } else { throw CConnectionFailed("uri-encode failed"); } } int r = evhttp_make_request(evcon.get(), req.get(), EVHTTP_REQ_POST, endpoint.c_str()); // ownership moved to evcon in above call req.release(); if (r != 0) { throw CConnectionFailed("send http request failed"); } event_base_dispatch(base.get()); if (response.status == 0) { 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)); } else if (response.status == HTTP_UNAUTHORIZED) { throw std::runtime_error( "incorrect rpcuser or rpcpassword (authorization failed)"); } else if (response.status >= 400 && response.status != HTTP_BAD_REQUEST && response.status != HTTP_NOT_FOUND && response.status != HTTP_INTERNAL_SERVER_ERROR) { throw std::runtime_error( strprintf("server returned HTTP error %d", response.status)); } else if (response.body.empty()) { throw std::runtime_error("no response from server"); } // Parse reply UniValue valReply(UniValue::VSTR); if (!valReply.read(response.body)) { throw std::runtime_error("couldn't parse reply from server"); } const UniValue &reply = valReply.get_obj(); if (reply.empty()) { throw std::runtime_error( "expected reply to have result, error and id properties"); } return reply; } 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::string rpcPass; if (gArgs.GetBoolArg("-stdinrpcpass", false)) { if (!std::getline(std::cin, rpcPass)) throw std::runtime_error("-stdinrpcpass specified but failed " "to read from standard input"); gArgs.ForceSetArg("-rpcpassword", rpcPass); } std::vector args = std::vector(&argv[1], &argv[argc]); if (gArgs.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]; // Remove trailing method name from arguments vector 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 const bool fWait = gArgs.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(); } if (errCode.isNum() && errCode.get_int() == RPC_WALLET_NOT_SPECIFIED) { strPrint += "\nTry adding " "\"-rpcwallet=\" option to " "bitcoin-cli command line."; } } } 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(nullptr, "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(nullptr, "AppInitRPC()"); return EXIT_FAILURE; } int ret = EXIT_FAILURE; try { ret = CommandLineRPC(argc, argv); } catch (const std::exception &e) { PrintExceptionContinue(&e, "CommandLineRPC()"); } catch (...) { PrintExceptionContinue(nullptr, "CommandLineRPC()"); } return ret; } diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index 2e679c3fa..1249fbe3f 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -1,886 +1,885 @@ // 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. #if defined(HAVE_CONFIG_H) #include "config/bitcoin-config.h" #endif #include "base58.h" #include "chainparams.h" #include "clientversion.h" #include "coins.h" #include "consensus/consensus.h" #include "core_io.h" #include "dstencode.h" #include "keystore.h" #include "policy/policy.h" #include "primitives/transaction.h" #include "script/script.h" #include "script/sign.h" #include "univalue.h" #include "util.h" #include "utilmoneystr.h" #include "utilstrencodings.h" #include #include static bool fCreateBlank; static std::map registers; static const int CONTINUE_EXECUTION = -1; // // 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 AppInitRawTx(int argc, char *argv[]) { // // Parameters // gArgs.ParseParameters(argc, argv); // Check for -testnet or -regtest parameter (Params() calls are only valid // after this clause) try { SelectParams(gArgs.GetChainName()); } catch (const std::exception &e) { fprintf(stderr, "Error: %s\n", e.what()); return EXIT_FAILURE; } fCreateBlank = gArgs.GetBoolArg("-create", false); - if (argc < 2 || gArgs.IsArgSet("-?") || gArgs.IsArgSet("-h") || - gArgs.IsArgSet("-help")) { + if (argc < 2 || HelpRequested(gArgs)) { // First part of help message is specific to this utility std::string strUsage = strprintf(_("%s bitcoin-tx utility version"), _(PACKAGE_NAME)) + " " + FormatFullVersion() + "\n\n" + _("Usage:") + "\n" + " bitcoin-tx [options] [commands] " + _("Update hex-encoded bitcoin transaction") + "\n" + " bitcoin-tx [options] -create [commands] " + _("Create hex-encoded bitcoin transaction") + "\n" + "\n"; fprintf(stdout, "%s", strUsage.c_str()); strUsage = HelpMessageGroup(_("Options:")); strUsage += HelpMessageOpt("-?", _("This help message")); strUsage += HelpMessageOpt("-create", _("Create new, empty TX.")); strUsage += HelpMessageOpt("-json", _("Select JSON output")); strUsage += HelpMessageOpt("-txid", _("Output only the hex-encoded transaction " "id of the resultant transaction.")); AppendParamsHelpMessages(strUsage); fprintf(stdout, "%s", strUsage.c_str()); strUsage = HelpMessageGroup(_("Commands:")); strUsage += HelpMessageOpt("delin=N", _("Delete input N from TX")); strUsage += HelpMessageOpt("delout=N", _("Delete output N from TX")); strUsage += HelpMessageOpt("in=TXID:VOUT(:SEQUENCE_NUMBER)", _("Add input to TX")); strUsage += HelpMessageOpt("locktime=N", _("Set TX lock time to N")); strUsage += HelpMessageOpt("nversion=N", _("Set TX version to N")); strUsage += HelpMessageOpt("outaddr=VALUE:ADDRESS", _("Add address-based output to TX")); strUsage += HelpMessageOpt("outpubkey=VALUE:PUBKEY[:FLAGS]", _("Add pay-to-pubkey output to TX") + ". " + _("Optionally add the \"S\" flag to wrap the " "output in a pay-to-script-hash.")); strUsage += HelpMessageOpt("outdata=[VALUE:]DATA", _("Add data-based output to TX")); strUsage += HelpMessageOpt("outscript=VALUE:SCRIPT[:FLAGS]", _("Add raw script output to TX") + ". " + _("Optionally add the \"S\" flag to wrap the " "output in a pay-to-script-hash.")); strUsage += HelpMessageOpt( "outmultisig=VALUE:REQUIRED:PUBKEYS:PUBKEY1:PUBKEY2:....[:FLAGS]", _("Add Pay To n-of-m Multi-sig output to TX. n = REQUIRED, m = " "PUBKEYS") + ". " + _("Optionally add the \"S\" flag to wrap the output in " "a pay-to-script-hash.")); strUsage += HelpMessageOpt( "sign=SIGHASH-FLAGS", _("Add zero or more signatures to transaction") + ". " + _("This command requires JSON registers:") + _("prevtxs=JSON object") + ", " + _("privatekeys=JSON object") + ". " + _("See signrawtransaction docs for format of sighash " "flags, JSON objects.")); fprintf(stdout, "%s", strUsage.c_str()); strUsage = HelpMessageGroup(_("Register Commands:")); strUsage += HelpMessageOpt("load=NAME:FILENAME", _("Load JSON file FILENAME into register NAME")); strUsage += HelpMessageOpt("set=NAME:JSON-STRING", _("Set register NAME to given JSON-STRING")); fprintf(stdout, "%s", strUsage.c_str()); if (argc < 2) { fprintf(stderr, "Error: too few parameters\n"); return EXIT_FAILURE; } return EXIT_SUCCESS; } return CONTINUE_EXECUTION; } static void RegisterSetJson(const std::string &key, const std::string &rawJson) { UniValue val; if (!val.read(rawJson)) { std::string strErr = "Cannot parse JSON for key " + key; throw std::runtime_error(strErr); } registers[key] = val; } static void RegisterSet(const std::string &strInput) { // separate NAME:VALUE in string size_t pos = strInput.find(':'); if ((pos == std::string::npos) || (pos == 0) || (pos == (strInput.size() - 1))) { throw std::runtime_error("Register input requires NAME:VALUE"); } std::string key = strInput.substr(0, pos); std::string valStr = strInput.substr(pos + 1, std::string::npos); RegisterSetJson(key, valStr); } static void RegisterLoad(const std::string &strInput) { // separate NAME:FILENAME in string size_t pos = strInput.find(':'); if ((pos == std::string::npos) || (pos == 0) || (pos == (strInput.size() - 1))) { throw std::runtime_error("Register load requires NAME:FILENAME"); } std::string key = strInput.substr(0, pos); std::string filename = strInput.substr(pos + 1, std::string::npos); FILE *f = fopen(filename.c_str(), "r"); if (!f) { std::string strErr = "Cannot open file " + filename; throw std::runtime_error(strErr); } // load file chunks into one big buffer std::string valStr; while ((!feof(f)) && (!ferror(f))) { char buf[4096]; int bread = fread(buf, 1, sizeof(buf), f); if (bread <= 0) { break; } valStr.insert(valStr.size(), buf, bread); } int error = ferror(f); fclose(f); if (error) { std::string strErr = "Error reading file " + filename; throw std::runtime_error(strErr); } // evaluate as JSON buffer register RegisterSetJson(key, valStr); } static Amount ExtractAndValidateValue(const std::string &strValue) { Amount value; if (!ParseMoney(strValue, value)) { throw std::runtime_error("invalid TX output value"); } return value; } static void MutateTxVersion(CMutableTransaction &tx, const std::string &cmdVal) { int64_t newVersion = atoi64(cmdVal); if (newVersion < 1 || newVersion > CTransaction::MAX_STANDARD_VERSION) { throw std::runtime_error("Invalid TX version requested"); } tx.nVersion = int(newVersion); } static void MutateTxLocktime(CMutableTransaction &tx, const std::string &cmdVal) { int64_t newLocktime = atoi64(cmdVal); if (newLocktime < 0LL || newLocktime > 0xffffffffLL) { throw std::runtime_error("Invalid TX locktime requested"); } tx.nLockTime = (unsigned int)newLocktime; } static void MutateTxAddInput(CMutableTransaction &tx, const std::string &strInput) { std::vector vStrInputParts; boost::split(vStrInputParts, strInput, boost::is_any_of(":")); // separate TXID:VOUT in string if (vStrInputParts.size() < 2) { throw std::runtime_error("TX input missing separator"); } // extract and validate TXID std::string strTxid = vStrInputParts[0]; if ((strTxid.size() != 64) || !IsHex(strTxid)) { throw std::runtime_error("invalid TX input txid"); } TxId txid(uint256S(strTxid)); static const unsigned int minTxOutSz = 9; static const unsigned int maxVout = MAX_TX_SIZE / minTxOutSz; // extract and validate vout std::string strVout = vStrInputParts[1]; int vout = atoi(strVout); if ((vout < 0) || (vout > (int)maxVout)) { throw std::runtime_error("invalid TX input vout"); } // extract the optional sequence number uint32_t nSequenceIn = std::numeric_limits::max(); if (vStrInputParts.size() > 2) { nSequenceIn = std::stoul(vStrInputParts[2]); } // append to transaction input list CTxIn txin(txid, vout, CScript(), nSequenceIn); tx.vin.push_back(txin); } static void MutateTxAddOutAddr(CMutableTransaction &tx, const std::string &strInput, const CChainParams &chainParams) { // Separate into VALUE:ADDRESS std::vector vStrInputParts; boost::split(vStrInputParts, strInput, boost::is_any_of(":")); if (vStrInputParts.size() != 2) { throw std::runtime_error("TX output missing or too many separators"); } // Extract and validate VALUE Amount value = ExtractAndValidateValue(vStrInputParts[0]); // extract and validate ADDRESS std::string strAddr = vStrInputParts[1]; CTxDestination destination = DecodeDestination(strAddr, chainParams); if (!IsValidDestination(destination)) { throw std::runtime_error("invalid TX output address"); } CScript scriptPubKey = GetScriptForDestination(destination); // construct TxOut, append to transaction output list CTxOut txout(value, scriptPubKey); tx.vout.push_back(txout); } static void MutateTxAddOutPubKey(CMutableTransaction &tx, const std::string &strInput) { // Separate into VALUE:PUBKEY[:FLAGS] std::vector vStrInputParts; boost::split(vStrInputParts, strInput, boost::is_any_of(":")); if (vStrInputParts.size() < 2 || vStrInputParts.size() > 3) { throw std::runtime_error("TX output missing or too many separators"); } // Extract and validate VALUE Amount value = ExtractAndValidateValue(vStrInputParts[0]); // Extract and validate PUBKEY CPubKey pubkey(ParseHex(vStrInputParts[1])); if (!pubkey.IsFullyValid()) { throw std::runtime_error("invalid TX output pubkey"); } CScript scriptPubKey = GetScriptForRawPubKey(pubkey); // Extract and validate FLAGS bool bScriptHash = false; if (vStrInputParts.size() == 3) { std::string flags = vStrInputParts[2]; bScriptHash = (flags.find("S") != std::string::npos); } if (bScriptHash) { // Get the ID for the script, and then construct a P2SH destination for // it. scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey)); } // construct TxOut, append to transaction output list CTxOut txout(value, scriptPubKey); tx.vout.push_back(txout); } static void MutateTxAddOutMultiSig(CMutableTransaction &tx, const std::string &strInput) { // Separate into VALUE:REQUIRED:NUMKEYS:PUBKEY1:PUBKEY2:....[:FLAGS] std::vector vStrInputParts; boost::split(vStrInputParts, strInput, boost::is_any_of(":")); // Check that there are enough parameters if (vStrInputParts.size() < 3) { throw std::runtime_error("Not enough multisig parameters"); } // Extract and validate VALUE Amount value = ExtractAndValidateValue(vStrInputParts[0]); // Extract REQUIRED uint32_t required = stoul(vStrInputParts[1]); // Extract NUMKEYS uint32_t numkeys = stoul(vStrInputParts[2]); // Validate there are the correct number of pubkeys if (vStrInputParts.size() < numkeys + 3) { throw std::runtime_error("incorrect number of multisig pubkeys"); } if (required < 1 || required > 20 || numkeys < 1 || numkeys > 20 || numkeys < required) { throw std::runtime_error("multisig parameter mismatch. Required " + std::to_string(required) + " of " + std::to_string(numkeys) + "signatures."); } // extract and validate PUBKEYs std::vector pubkeys; for (int pos = 1; pos <= int(numkeys); pos++) { CPubKey pubkey(ParseHex(vStrInputParts[pos + 2])); if (!pubkey.IsFullyValid()) { throw std::runtime_error("invalid TX output pubkey"); } pubkeys.push_back(pubkey); } // Extract FLAGS bool bScriptHash = false; if (vStrInputParts.size() == numkeys + 4) { std::string flags = vStrInputParts.back(); bScriptHash = (flags.find("S") != std::string::npos); } else if (vStrInputParts.size() > numkeys + 4) { // Validate that there were no more parameters passed throw std::runtime_error("Too many parameters"); } CScript scriptPubKey = GetScriptForMultisig(required, pubkeys); if (bScriptHash) { // Get the ID for the script, and then construct a P2SH destination for // it. scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey)); } // construct TxOut, append to transaction output list CTxOut txout(value, scriptPubKey); tx.vout.push_back(txout); } static void MutateTxAddOutData(CMutableTransaction &tx, const std::string &strInput) { Amount value = Amount::zero(); // separate [VALUE:]DATA in string size_t pos = strInput.find(':'); if (pos == 0) { throw std::runtime_error("TX output value not specified"); } if (pos != std::string::npos) { // Extract and validate VALUE value = ExtractAndValidateValue(strInput.substr(0, pos)); } // extract and validate DATA std::string strData = strInput.substr(pos + 1, std::string::npos); if (!IsHex(strData)) { throw std::runtime_error("invalid TX output data"); } std::vector data = ParseHex(strData); CTxOut txout(value, CScript() << OP_RETURN << data); tx.vout.push_back(txout); } static void MutateTxAddOutScript(CMutableTransaction &tx, const std::string &strInput) { // separate VALUE:SCRIPT[:FLAGS] std::vector vStrInputParts; boost::split(vStrInputParts, strInput, boost::is_any_of(":")); if (vStrInputParts.size() < 2) throw std::runtime_error("TX output missing separator"); // Extract and validate VALUE Amount value = ExtractAndValidateValue(vStrInputParts[0]); // extract and validate script std::string strScript = vStrInputParts[1]; CScript scriptPubKey = ParseScript(strScript); // Extract FLAGS bool bScriptHash = false; if (vStrInputParts.size() == 3) { std::string flags = vStrInputParts.back(); bScriptHash = (flags.find("S") != std::string::npos); } if (bScriptHash) { scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey)); } // construct TxOut, append to transaction output list CTxOut txout(value, scriptPubKey); tx.vout.push_back(txout); } static void MutateTxDelInput(CMutableTransaction &tx, const std::string &strInIdx) { // parse requested deletion index int inIdx = atoi(strInIdx); if (inIdx < 0 || inIdx >= (int)tx.vin.size()) { std::string strErr = "Invalid TX input index '" + strInIdx + "'"; throw std::runtime_error(strErr.c_str()); } // delete input from transaction tx.vin.erase(tx.vin.begin() + inIdx); } static void MutateTxDelOutput(CMutableTransaction &tx, const std::string &strOutIdx) { // parse requested deletion index int outIdx = atoi(strOutIdx); if (outIdx < 0 || outIdx >= (int)tx.vout.size()) { std::string strErr = "Invalid TX output index '" + strOutIdx + "'"; throw std::runtime_error(strErr.c_str()); } // delete output from transaction tx.vout.erase(tx.vout.begin() + outIdx); } static const unsigned int N_SIGHASH_OPTS = 12; static const struct { const char *flagStr; int flags; } sigHashOptions[N_SIGHASH_OPTS] = { {"ALL", SIGHASH_ALL}, {"NONE", SIGHASH_NONE}, {"SINGLE", SIGHASH_SINGLE}, {"ALL|ANYONECANPAY", SIGHASH_ALL | SIGHASH_ANYONECANPAY}, {"NONE|ANYONECANPAY", SIGHASH_NONE | SIGHASH_ANYONECANPAY}, {"SINGLE|ANYONECANPAY", SIGHASH_SINGLE | SIGHASH_ANYONECANPAY}, {"ALL|FORKID", SIGHASH_ALL | SIGHASH_FORKID}, {"NONE|FORKID", SIGHASH_NONE | SIGHASH_FORKID}, {"SINGLE|FORKID", SIGHASH_SINGLE | SIGHASH_FORKID}, {"ALL|FORKID|ANYONECANPAY", SIGHASH_ALL | SIGHASH_FORKID | SIGHASH_ANYONECANPAY}, {"NONE|FORKID|ANYONECANPAY", SIGHASH_NONE | SIGHASH_FORKID | SIGHASH_ANYONECANPAY}, {"SINGLE|FORKID|ANYONECANPAY", SIGHASH_SINGLE | SIGHASH_FORKID | SIGHASH_ANYONECANPAY}, }; static bool findSigHashFlags(SigHashType &sigHashType, const std::string &flagStr) { sigHashType = SigHashType(); for (unsigned int i = 0; i < N_SIGHASH_OPTS; i++) { if (flagStr == sigHashOptions[i].flagStr) { sigHashType = SigHashType(sigHashOptions[i].flags); return true; } } return false; } static Amount AmountFromValue(const UniValue &value) { if (!value.isNum() && !value.isStr()) { throw std::runtime_error("Amount is not a number or string"); } int64_t n; if (!ParseFixedPoint(value.getValStr(), 8, &n)) { throw std::runtime_error("Invalid amount"); } Amount amount = n * SATOSHI; if (!MoneyRange(amount)) { throw std::runtime_error("Amount out of range"); } return amount; } static void MutateTxSign(CMutableTransaction &tx, const std::string &flagStr) { SigHashType sigHashType = SigHashType().withForkId(); if ((flagStr.size() > 0) && !findSigHashFlags(sigHashType, flagStr)) { throw std::runtime_error("unknown sighash flag/sign option"); } std::vector txVariants; txVariants.push_back(CTransaction(tx)); // mergedTx will end up with all the signatures; it starts as a clone of the // raw tx: CMutableTransaction mergedTx(txVariants[0]); bool fComplete = true; CCoinsView viewDummy; CCoinsViewCache view(&viewDummy); if (!registers.count("privatekeys")) { throw std::runtime_error("privatekeys register variable must be set."); } CBasicKeyStore tempKeystore; UniValue keysObj = registers["privatekeys"]; for (unsigned int kidx = 0; kidx < keysObj.size(); kidx++) { if (!keysObj[kidx].isStr()) { throw std::runtime_error("privatekey not a std::string"); } CBitcoinSecret vchSecret; bool fGood = vchSecret.SetString(keysObj[kidx].getValStr()); if (!fGood) { throw std::runtime_error("privatekey not valid"); } CKey key = vchSecret.GetKey(); tempKeystore.AddKey(key); } // Add previous txouts given in the RPC call: if (!registers.count("prevtxs")) { throw std::runtime_error("prevtxs register variable must be set."); } UniValue prevtxsObj = registers["prevtxs"]; for (unsigned int previdx = 0; previdx < prevtxsObj.size(); previdx++) { UniValue prevOut = prevtxsObj[previdx]; if (!prevOut.isObject()) { throw std::runtime_error("expected prevtxs internal object"); } std::map types = { {"txid", UniValue::VSTR}, {"vout", UniValue::VNUM}, {"scriptPubKey", UniValue::VSTR}}; if (!prevOut.checkObject(types)) { throw std::runtime_error("prevtxs internal object typecheck fail"); } TxId txid(ParseHashUV(prevOut["txid"], "txid")); int nOut = atoi(prevOut["vout"].getValStr()); if (nOut < 0) { throw std::runtime_error("vout must be positive"); } COutPoint out(txid, nOut); std::vector pkData( ParseHexUV(prevOut["scriptPubKey"], "scriptPubKey")); CScript scriptPubKey(pkData.begin(), pkData.end()); { const Coin &coin = view.AccessCoin(out); if (!coin.IsSpent() && coin.GetTxOut().scriptPubKey != scriptPubKey) { std::string err("Previous output scriptPubKey mismatch:\n"); err = err + ScriptToAsmStr(coin.GetTxOut().scriptPubKey) + "\nvs:\n" + ScriptToAsmStr(scriptPubKey); throw std::runtime_error(err); } CTxOut txout; txout.scriptPubKey = scriptPubKey; txout.nValue = Amount::zero(); if (prevOut.exists("amount")) { txout.nValue = AmountFromValue(prevOut["amount"]); } view.AddCoin(out, Coin(txout, 1, false), true); } // If redeemScript given and private keys given, add redeemScript to the // tempKeystore so it can be signed: if (scriptPubKey.IsPayToScriptHash() && prevOut.exists("redeemScript")) { UniValue v = prevOut["redeemScript"]; std::vector rsData(ParseHexUV(v, "redeemScript")); CScript redeemScript(rsData.begin(), rsData.end()); tempKeystore.AddCScript(redeemScript); } } const CKeyStore &keystore = tempKeystore; // Sign what we can: for (size_t i = 0; i < mergedTx.vin.size(); i++) { CTxIn &txin = mergedTx.vin[i]; const Coin &coin = view.AccessCoin(txin.prevout); if (coin.IsSpent()) { fComplete = false; continue; } const CScript &prevPubKey = coin.GetTxOut().scriptPubKey; const Amount amount = coin.GetTxOut().nValue; SignatureData sigdata; // Only sign SIGHASH_SINGLE if there's a corresponding output: if ((sigHashType.getBaseType() != BaseSigHashType::SINGLE) || (i < mergedTx.vout.size())) { ProduceSignature(MutableTransactionSignatureCreator( &keystore, &mergedTx, i, amount, sigHashType), prevPubKey, sigdata); } // ... and merge in other signatures: for (const CTransaction &txv : txVariants) { sigdata = CombineSignatures( prevPubKey, MutableTransactionSignatureChecker(&mergedTx, i, amount), sigdata, DataFromTransaction(txv, i)); } UpdateTransaction(mergedTx, i, sigdata); if (!VerifyScript( txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i, amount))) { fComplete = false; } } if (fComplete) { // do nothing... for now // perhaps store this for later optional JSON output } tx = mergedTx; } class Secp256k1Init { ECCVerifyHandle globalVerifyHandle; public: Secp256k1Init() { ECC_Start(); } ~Secp256k1Init() { ECC_Stop(); } }; static void MutateTx(CMutableTransaction &tx, const std::string &command, const std::string &commandVal, const CChainParams &chainParams) { std::unique_ptr ecc; if (command == "nversion") { MutateTxVersion(tx, commandVal); } else if (command == "locktime") { MutateTxLocktime(tx, commandVal); } else if (command == "delin") { MutateTxDelInput(tx, commandVal); } else if (command == "in") { MutateTxAddInput(tx, commandVal); } else if (command == "delout") { MutateTxDelOutput(tx, commandVal); } else if (command == "outaddr") { MutateTxAddOutAddr(tx, commandVal, chainParams); } else if (command == "outpubkey") { MutateTxAddOutPubKey(tx, commandVal); } else if (command == "outmultisig") { MutateTxAddOutMultiSig(tx, commandVal); } else if (command == "outscript") { MutateTxAddOutScript(tx, commandVal); } else if (command == "outdata") { MutateTxAddOutData(tx, commandVal); } else if (command == "sign") { if (!ecc) { ecc.reset(new Secp256k1Init()); } MutateTxSign(tx, commandVal); } else if (command == "load") { RegisterLoad(commandVal); } else if (command == "set") { RegisterSet(commandVal); } else { throw std::runtime_error("unknown command"); } } static void OutputTxJSON(const CTransaction &tx) { UniValue entry(UniValue::VOBJ); TxToUniv(tx, uint256(), entry); std::string jsonOutput = entry.write(4); fprintf(stdout, "%s\n", jsonOutput.c_str()); } static void OutputTxHash(const CTransaction &tx) { // the hex-encoded transaction id. std::string strHexHash = tx.GetId().GetHex(); fprintf(stdout, "%s\n", strHexHash.c_str()); } static void OutputTxHex(const CTransaction &tx) { std::string strHex = EncodeHexTx(tx); fprintf(stdout, "%s\n", strHex.c_str()); } static void OutputTx(const CTransaction &tx) { if (gArgs.GetBoolArg("-json", false)) { OutputTxJSON(tx); } else if (gArgs.GetBoolArg("-txid", false)) { OutputTxHash(tx); } else { OutputTxHex(tx); } } static std::string readStdin() { char buf[4096]; std::string ret; while (!feof(stdin)) { size_t bread = fread(buf, 1, sizeof(buf), stdin); ret.append(buf, bread); if (bread < sizeof(buf)) { break; } } if (ferror(stdin)) { throw std::runtime_error("error reading stdin"); } boost::algorithm::trim_right(ret); return ret; } static int CommandLineRawTx(int argc, char *argv[], const CChainParams &chainParams) { std::string strPrint; int nRet = 0; try { // Skip switches; Permit common stdin convention "-" while (argc > 1 && IsSwitchChar(argv[1][0]) && (argv[1][1] != 0)) { argc--; argv++; } CMutableTransaction tx; int startArg; if (!fCreateBlank) { // require at least one param if (argc < 2) { throw std::runtime_error("too few parameters"); } // param: hex-encoded bitcoin transaction std::string strHexTx(argv[1]); // "-" implies standard input if (strHexTx == "-") { strHexTx = readStdin(); } if (!DecodeHexTx(tx, strHexTx)) { throw std::runtime_error("invalid transaction encoding"); } startArg = 2; } else { startArg = 1; } for (int i = startArg; i < argc; i++) { std::string arg = argv[i]; std::string key, value; size_t eqpos = arg.find('='); if (eqpos == std::string::npos) { key = arg; } else { key = arg.substr(0, eqpos); value = arg.substr(eqpos + 1); } MutateTx(tx, key, value, chainParams); } OutputTx(CTransaction(tx)); } catch (const boost::thread_interrupted &) { throw; } catch (const std::exception &e) { strPrint = std::string("error: ") + e.what(); nRet = EXIT_FAILURE; } catch (...) { PrintExceptionContinue(nullptr, "CommandLineRawTx()"); throw; } if (strPrint != "") { fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str()); } return nRet; } int main(int argc, char *argv[]) { SetupEnvironment(); try { int ret = AppInitRawTx(argc, argv); if (ret != CONTINUE_EXECUTION) return ret; } catch (const std::exception &e) { PrintExceptionContinue(&e, "AppInitRawTx()"); return EXIT_FAILURE; } catch (...) { PrintExceptionContinue(nullptr, "AppInitRawTx()"); return EXIT_FAILURE; } int ret = EXIT_FAILURE; try { ret = CommandLineRawTx(argc, argv, Params()); } catch (const std::exception &e) { PrintExceptionContinue(&e, "CommandLineRawTx()"); } catch (...) { PrintExceptionContinue(nullptr, "CommandLineRawTx()"); } return ret; } diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index 9b567f537..0ae39bd41 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -1,222 +1,221 @@ // 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. #if defined(HAVE_CONFIG_H) #include "config/bitcoin-config.h" #endif #include "chainparams.h" #include "clientversion.h" #include "compat.h" #include "config.h" #include "fs.h" #include "httprpc.h" #include "httpserver.h" #include "init.h" #include "noui.h" #include "rpc/server.h" #include "scheduler.h" #include "util.h" #include "utilstrencodings.h" #if ENABLE_WALLET #include "wallet/init.h" #endif #include "walletinitinterface.h" #include #include /* Introduction text for doxygen: */ /*! \mainpage Developer documentation * * \section intro_sec Introduction * * This is the developer documentation of Bitcoin ABC * (https://www.bitcoinabc.org/). Bitcoin ABC is a client for the digital * currency called Bitcoin Cash (https://www.bitcoincash.org/), which enables * instant payments to anyone, anywhere in the world. Bitcoin Cash uses * peer-to-peer technology to operate with no central authority: managing * transactions and issuing money are carried out collectively by the network. * * The software is a community-driven open source project, released under the * MIT license. * * \section Navigation * Use the buttons Namespaces, Classes or * Files at the top of the page to start navigating the code. */ void WaitForShutdown(boost::thread_group *threadGroup) { while (!ShutdownRequested()) { MilliSleep(200); } if (threadGroup) { Interrupt(*threadGroup); threadGroup->join_all(); } } ////////////////////////////////////////////////////////////////////////////// // // Start // bool AppInit(int argc, char *argv[]) { boost::thread_group threadGroup; CScheduler scheduler; // FIXME: Ideally, we'd like to build the config here, but that's currently // not possible as the whole application has too many global state. However, // this is a first step. auto &config = const_cast(GetConfig()); RPCServer rpcServer; HTTPRPCRequestProcessor httpRPCRequestProcessor(config, rpcServer); bool fRet = false; #if ENABLE_WALLET g_wallet_init_interface.reset(new WalletInit); #else g_wallet_init_interface.reset(new DummyWalletInit); #endif // // Parameters // // If Qt is used, parameters/bitcoin.conf are parsed in qt/bitcoin.cpp's // main() gArgs.ParseParameters(argc, argv); // Process help and version before taking care about datadir - if (gArgs.IsArgSet("-?") || gArgs.IsArgSet("-h") || - gArgs.IsArgSet("-help") || gArgs.IsArgSet("-version")) { + if (HelpRequested(gArgs) || gArgs.IsArgSet("-version")) { std::string strUsage = strprintf(_("%s Daemon"), _(PACKAGE_NAME)) + " " + _("version") + " " + FormatFullVersion() + "\n"; if (gArgs.IsArgSet("-version")) { strUsage += FormatParagraph(LicenseInfo()); } else { strUsage += "\n" + _("Usage:") + "\n" + " bitcoind [options] " + strprintf(_("Start %s Daemon"), _(PACKAGE_NAME)) + "\n"; strUsage += "\n" + HelpMessage(HMM_BITCOIND); } fprintf(stdout, "%s", strUsage.c_str()); return true; } try { if (!fs::is_directory(GetDataDir(false))) { fprintf(stderr, "Error: Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "").c_str()); return false; } try { gArgs.ReadConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)); } catch (const std::exception &e) { fprintf(stderr, "Error reading configuration file: %s\n", e.what()); return false; } // Check for -testnet or -regtest parameter (Params() calls are only // valid after this clause) try { SelectParams(gArgs.GetChainName()); } catch (const std::exception &e) { fprintf(stderr, "Error: %s\n", e.what()); return false; } // Error out when loose non-argument tokens are encountered on command // line for (int i = 1; i < argc; i++) { if (!IsSwitchChar(argv[i][0])) { fprintf(stderr, "Error: Command line contains unexpected token " "'%s', see bitcoind -h for a list of " "options.\n", argv[i]); exit(EXIT_FAILURE); } } // -server defaults to true for bitcoind but not for the GUI so do this // here gArgs.SoftSetBoolArg("-server", true); // Set this early so that parameter interactions go to console InitLogging(); InitParameterInteraction(); if (!AppInitBasicSetup()) { // InitError will have been called with detailed error, which ends // up on console exit(1); } if (!AppInitParameterInteraction(config, rpcServer)) { // InitError will have been called with detailed error, which ends // up on console exit(1); } if (!AppInitSanityChecks()) { // InitError will have been called with detailed error, which ends // up on console exit(1); } if (gArgs.GetBoolArg("-daemon", false)) { #if HAVE_DECL_DAEMON fprintf(stdout, "Bitcoin server starting\n"); // Daemonize if (daemon(1, 0)) { // don't chdir (1), do close FDs (0) fprintf(stderr, "Error: daemon() failed: %s\n", strerror(errno)); return false; } #else fprintf( stderr, "Error: -daemon is not supported on this operating system\n"); return false; #endif // HAVE_DECL_DAEMON } // Lock data directory after daemonization if (!AppInitLockDataDirectory()) { // If locking the data directory failed, exit immediately exit(EXIT_FAILURE); } fRet = AppInitMain(config, httpRPCRequestProcessor, threadGroup, scheduler); } catch (const std::exception &e) { PrintExceptionContinue(&e, "AppInit()"); } catch (...) { PrintExceptionContinue(nullptr, "AppInit()"); } if (!fRet) { Interrupt(threadGroup); // threadGroup.join_all(); was left out intentionally here, because we // didn't re-test all of the startup-failure cases to make sure they // don't result in a hang due to some // thread-blocking-waiting-for-another-thread-during-startup case. } else { WaitForShutdown(&threadGroup); } Shutdown(); return fRet; } int main(int argc, char *argv[]) { SetupEnvironment(); // Connect bitcoind signal handlers noui_connect(); return (AppInit(argc, argv) ? EXIT_SUCCESS : EXIT_FAILURE); } diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index b0dc4464f..4d1fde86b 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -1,812 +1,811 @@ // Copyright (c) 2011-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. #if defined(HAVE_CONFIG_H) #include "config/bitcoin-config.h" #endif #include "bitcoingui.h" #include "chainparams.h" #include "clientmodel.h" #include "config.h" #include "guiconstants.h" #include "guiutil.h" #include "httprpc.h" #include "intro.h" #include "networkstyle.h" #include "optionsmodel.h" #include "platformstyle.h" #include "splashscreen.h" #include "utilitydialog.h" #include "winshutdownmonitor.h" #ifdef ENABLE_WALLET #include "paymentserver.h" #include "walletmodel.h" #endif #include "init.h" #include "rpc/server.h" #include "scheduler.h" #include "ui_interface.h" #include "util.h" #include "warnings.h" #ifdef ENABLE_WALLET #include "wallet/init.h" #include "wallet/wallet.h" #endif #include "walletinitinterface.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(QT_STATICPLUGIN) #include #if QT_VERSION < 0x050400 Q_IMPORT_PLUGIN(AccessibleFactory) #endif #if defined(QT_QPA_PLATFORM_XCB) Q_IMPORT_PLUGIN(QXcbIntegrationPlugin); #elif defined(QT_QPA_PLATFORM_WINDOWS) Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin); #elif defined(QT_QPA_PLATFORM_COCOA) Q_IMPORT_PLUGIN(QCocoaIntegrationPlugin); #endif #endif // Declare meta types used for QMetaObject::invokeMethod Q_DECLARE_METATYPE(bool *) Q_DECLARE_METATYPE(Amount) // Config is non-copyable so we can only register pointers to it Q_DECLARE_METATYPE(Config *) static void InitMessage(const std::string &message) { LogPrintf("init message: %s\n", message); } /** * Translate string to current locale using Qt. */ static std::string Translate(const char *psz) { return QCoreApplication::translate("bitcoin-abc", psz).toStdString(); } static QString GetLangTerritory() { QSettings settings; // Get desired locale (e.g. "de_DE") // 1) System default language QString lang_territory = QLocale::system().name(); // 2) Language from QSettings QString lang_territory_qsettings = settings.value("language", "").toString(); if (!lang_territory_qsettings.isEmpty()) lang_territory = lang_territory_qsettings; // 3) -lang command line argument lang_territory = QString::fromStdString( gArgs.GetArg("-lang", lang_territory.toStdString())); return lang_territory; } /** Set up translations */ static void initTranslations(QTranslator &qtTranslatorBase, QTranslator &qtTranslator, QTranslator &translatorBase, QTranslator &translator) { // Remove old translators QApplication::removeTranslator(&qtTranslatorBase); QApplication::removeTranslator(&qtTranslator); QApplication::removeTranslator(&translatorBase); QApplication::removeTranslator(&translator); // Get desired locale (e.g. "de_DE") // 1) System default language QString lang_territory = GetLangTerritory(); // Convert to "de" only by truncating "_DE" QString lang = lang_territory; lang.truncate(lang_territory.lastIndexOf('_')); // Load language files for configured locale: // - First load the translator for the base language, without territory // - Then load the more specific locale translator // Load e.g. qt_de.qm if (qtTranslatorBase.load( "qt_" + lang, QLibraryInfo::location(QLibraryInfo::TranslationsPath))) QApplication::installTranslator(&qtTranslatorBase); // Load e.g. qt_de_DE.qm if (qtTranslator.load( "qt_" + lang_territory, QLibraryInfo::location(QLibraryInfo::TranslationsPath))) QApplication::installTranslator(&qtTranslator); // Load e.g. bitcoin_de.qm (shortcut "de" needs to be defined in // bitcoin.qrc) if (translatorBase.load(lang, ":/translations/")) QApplication::installTranslator(&translatorBase); // Load e.g. bitcoin_de_DE.qm (shortcut "de_DE" needs to be defined in // bitcoin.qrc) if (translator.load(lang_territory, ":/translations/")) QApplication::installTranslator(&translator); } /* qDebug() message handler --> debug.log */ void DebugMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &msg) { Q_UNUSED(context); if (type == QtDebugMsg) { LogPrint(BCLog::QT, "GUI: %s\n", msg.toStdString()); } else { LogPrintf("GUI: %s\n", msg.toStdString()); } } /** * Class encapsulating Bitcoin ABC startup and shutdown. * Allows running startup and shutdown in a different thread from the UI thread. */ class BitcoinABC : public QObject { Q_OBJECT public: explicit BitcoinABC(); /** * Basic initialization, before starting initialization/shutdown thread. * Return true on success. */ static bool baseInitialize(Config &config, RPCServer &rpcServer); public Q_SLOTS: void initialize(Config *config, HTTPRPCRequestProcessor *httpRPCRequestProcessor); void shutdown(); Q_SIGNALS: void initializeResult(bool success); void shutdownResult(); void runawayException(const QString &message); private: boost::thread_group threadGroup; CScheduler scheduler; /// Pass fatal exception message to UI thread void handleRunawayException(const std::exception *e); }; /** Main Bitcoin application object */ class BitcoinApplication : public QApplication { Q_OBJECT public: explicit BitcoinApplication(int &argc, char **argv); ~BitcoinApplication(); #ifdef ENABLE_WALLET /// Create payment server void createPaymentServer(); #endif /// parameter interaction/setup based on rules void parameterSetup(); /// Create options model void createOptionsModel(bool resetSettings); /// Create main window void createWindow(const Config *, const NetworkStyle *networkStyle); /// Create splash screen void createSplashScreen(const NetworkStyle *networkStyle); /// Request core initialization void requestInitialize(Config &config, HTTPRPCRequestProcessor &httpRPCRequestProcessor, RPCServer &rpcServer); /// Request core shutdown void requestShutdown(Config &config); /// Get process return value int getReturnValue() { return returnValue; } /// Get window identifier of QMainWindow (BitcoinGUI) WId getMainWinId() const; public Q_SLOTS: void initializeResult(bool success); void shutdownResult(); /// Handle runaway exceptions. Shows a message box with the problem and /// quits the program. void handleRunawayException(const QString &message); Q_SIGNALS: void requestedInitialize(Config *config, HTTPRPCRequestProcessor *httpRPCRequestProcessor, RPCServer *rpcServer); void requestedShutdown(); void stopThread(); void splashFinished(QWidget *window); private: QThread *coreThread; OptionsModel *optionsModel; ClientModel *clientModel; BitcoinGUI *window; QTimer *pollShutdownTimer; #ifdef ENABLE_WALLET PaymentServer *paymentServer; WalletModel *walletModel; #endif int returnValue; const PlatformStyle *platformStyle; std::unique_ptr shutdownWindow; void startThread(); }; #include "bitcoin.moc" BitcoinABC::BitcoinABC() : QObject() {} void BitcoinABC::handleRunawayException(const std::exception *e) { PrintExceptionContinue(e, "Runaway exception"); Q_EMIT runawayException(QString::fromStdString(GetWarnings("gui"))); } bool BitcoinABC::baseInitialize(Config &config, RPCServer &rpcServer) { if (!AppInitBasicSetup()) { return false; } if (!AppInitParameterInteraction(config, rpcServer)) { return false; } if (!AppInitSanityChecks()) { return false; } if (!AppInitLockDataDirectory()) { return false; } return true; } void BitcoinABC::initialize(Config *cfg, HTTPRPCRequestProcessor *httpRPCRequestProcessor) { Config &config(*cfg); try { qDebug() << __func__ << ": Running initialization in thread"; bool rv = AppInitMain(config, *httpRPCRequestProcessor, threadGroup, scheduler); Q_EMIT initializeResult(rv); } catch (const std::exception &e) { handleRunawayException(&e); } catch (...) { handleRunawayException(nullptr); } } void BitcoinABC::shutdown() { try { qDebug() << __func__ << ": Running Shutdown in thread"; Interrupt(threadGroup); threadGroup.join_all(); Shutdown(); qDebug() << __func__ << ": Shutdown finished"; Q_EMIT shutdownResult(); } catch (const std::exception &e) { handleRunawayException(&e); } catch (...) { handleRunawayException(nullptr); } } BitcoinApplication::BitcoinApplication(int &argc, char **argv) : QApplication(argc, argv), coreThread(0), optionsModel(0), clientModel(0), window(0), pollShutdownTimer(0), #ifdef ENABLE_WALLET paymentServer(0), walletModel(0), #endif returnValue(0) { setQuitOnLastWindowClosed(false); // UI per-platform customization. // This must be done inside the BitcoinApplication constructor, or after it, // because PlatformStyle::instantiate requires a QApplication. std::string platformName; platformName = gArgs.GetArg("-uiplatform", BitcoinGUI::DEFAULT_UIPLATFORM); platformStyle = PlatformStyle::instantiate(QString::fromStdString(platformName)); // Fall back to "other" if specified name not found. if (!platformStyle) platformStyle = PlatformStyle::instantiate("other"); assert(platformStyle); } BitcoinApplication::~BitcoinApplication() { if (coreThread) { qDebug() << __func__ << ": Stopping thread"; Q_EMIT stopThread(); coreThread->wait(); qDebug() << __func__ << ": Stopped thread"; } delete window; window = 0; #ifdef ENABLE_WALLET delete paymentServer; paymentServer = 0; #endif delete optionsModel; optionsModel = 0; delete platformStyle; platformStyle = 0; } #ifdef ENABLE_WALLET void BitcoinApplication::createPaymentServer() { paymentServer = new PaymentServer(this); } #endif void BitcoinApplication::createOptionsModel(bool resetSettings) { optionsModel = new OptionsModel(nullptr, resetSettings); } void BitcoinApplication::createWindow(const Config *config, const NetworkStyle *networkStyle) { window = new BitcoinGUI(config, platformStyle, networkStyle, 0); pollShutdownTimer = new QTimer(window); connect(pollShutdownTimer, SIGNAL(timeout()), window, SLOT(detectShutdown())); pollShutdownTimer->start(200); } void BitcoinApplication::createSplashScreen(const NetworkStyle *networkStyle) { SplashScreen *splash = new SplashScreen(0, networkStyle); // We don't hold a direct pointer to the splash screen after creation, but // the splash screen will take care of deleting itself when slotFinish // happens. splash->show(); connect(this, SIGNAL(splashFinished(QWidget *)), splash, SLOT(slotFinish(QWidget *))); connect(this, SIGNAL(requestedShutdown()), splash, SLOT(close())); } void BitcoinApplication::startThread() { if (coreThread) return; coreThread = new QThread(this); BitcoinABC *executor = new BitcoinABC(); executor->moveToThread(coreThread); /* communication to and from thread */ connect(executor, SIGNAL(initializeResult(bool)), this, SLOT(initializeResult(bool))); connect(executor, SIGNAL(shutdownResult()), this, SLOT(shutdownResult())); connect(executor, SIGNAL(runawayException(QString)), this, SLOT(handleRunawayException(QString))); // Note on how Qt works: it tries to directly invoke methods if the signal // is emitted on the same thread that the target object 'lives' on. // But if the target object 'lives' on another thread (executor here does) // the SLOT will be invoked asynchronously at a later time in the thread // of the target object. So.. we pass a pointer around. If you pass // a reference around (even if it's non-const) you'll get Qt generating // code to copy-construct the parameter in question (Q_DECLARE_METATYPE // and qRegisterMetaType generate this code). For the Config class, // which is noncopyable, we can't do this. So.. we have to pass // pointers to Config around. Make sure Config &/Config * isn't a // temporary (eg it lives somewhere aside from the stack) or this will // crash because initialize() gets executed in another thread at some // unspecified time (after) requestedInitialize() is emitted! connect(this, SIGNAL(requestedInitialize( Config *, HTTPRPCRequestProcessor *, RPCServer *)), executor, SLOT(initialize(Config *, HTTPRPCRequestProcessor *))); connect(this, SIGNAL(requestedShutdown()), executor, SLOT(shutdown())); /* make sure executor object is deleted in its own thread */ connect(this, SIGNAL(stopThread()), executor, SLOT(deleteLater())); connect(this, SIGNAL(stopThread()), coreThread, SLOT(quit())); coreThread->start(); } void BitcoinApplication::parameterSetup() { InitLogging(); InitParameterInteraction(); } void BitcoinApplication::requestInitialize( Config &config, HTTPRPCRequestProcessor &httpRPCRequestProcessor, RPCServer &rpcServer) { qDebug() << __func__ << ": Requesting initialize"; startThread(); // IMPORTANT: config must NOT be a reference to a temporary because below // signal may be connected to a slot that will be executed as a queued // connection in another thread! Q_EMIT requestedInitialize(&config, &httpRPCRequestProcessor, &rpcServer); } void BitcoinApplication::requestShutdown(Config &config) { // Show a simple window indicating shutdown status. Do this first as some of // the steps may take some time below, for example the RPC console may still // be executing a command. shutdownWindow.reset(ShutdownWindow::showShutdownWindow(window)); qDebug() << __func__ << ": Requesting shutdown"; startThread(); window->hide(); window->setClientModel(0); pollShutdownTimer->stop(); #ifdef ENABLE_WALLET window->removeAllWallets(); delete walletModel; walletModel = 0; #endif delete clientModel; clientModel = 0; StartShutdown(); // Request shutdown from core thread Q_EMIT requestedShutdown(); } void BitcoinApplication::initializeResult(bool success) { qDebug() << __func__ << ": Initialization result: " << success; returnValue = success ? EXIT_SUCCESS : EXIT_FAILURE; if (success) { // Log this only after AppInit2 finishes, as then logging setup is // guaranteed complete. qWarning() << "Platform customization:" << platformStyle->getName(); #ifdef ENABLE_WALLET PaymentServer::LoadRootCAs(); paymentServer->setOptionsModel(optionsModel); #endif clientModel = new ClientModel(optionsModel); window->setClientModel(clientModel); #ifdef ENABLE_WALLET // TODO: Expose secondary wallets if (!vpwallets.empty()) { walletModel = new WalletModel(platformStyle, vpwallets[0], optionsModel); window->addWallet(BitcoinGUI::DEFAULT_WALLET, walletModel); window->setCurrentWallet(BitcoinGUI::DEFAULT_WALLET); connect(walletModel, SIGNAL(coinsSent(CWallet *, SendCoinsRecipient, QByteArray)), paymentServer, SLOT(fetchPaymentACK(CWallet *, const SendCoinsRecipient &, QByteArray))); } #endif // If -min option passed, start window minimized. if (gArgs.GetBoolArg("-min", false)) { window->showMinimized(); } else { window->show(); } Q_EMIT splashFinished(window); #ifdef ENABLE_WALLET // Now that initialization/startup is done, process any command-line // bitcoincash: URIs or payment requests: connect(paymentServer, SIGNAL(receivedPaymentRequest(SendCoinsRecipient)), window, SLOT(handlePaymentRequest(SendCoinsRecipient))); connect(window, SIGNAL(receivedURI(QString)), paymentServer, SLOT(handleURIOrFile(QString))); connect(paymentServer, SIGNAL(message(QString, QString, unsigned int)), window, SLOT(message(QString, QString, unsigned int))); QTimer::singleShot(100, paymentServer, SLOT(uiReady())); #endif } else { // Make sure splash screen doesn't stick around during shutdown. Q_EMIT splashFinished(window); // Exit first main loop invocation. quit(); } } void BitcoinApplication::shutdownResult() { // Exit second main loop invocation after shutdown finished. quit(); } void BitcoinApplication::handleRunawayException(const QString &message) { QMessageBox::critical( 0, "Runaway exception", BitcoinGUI::tr("A fatal error occurred. Bitcoin can no longer continue " "safely and will quit.") + QString("\n\n") + message); ::exit(EXIT_FAILURE); } WId BitcoinApplication::getMainWinId() const { if (!window) return 0; return window->winId(); } #ifndef BITCOIN_QT_TEST static void MigrateSettings() { assert(!QApplication::applicationName().isEmpty()); static const QString legacyAppName("Bitcoin-Qt"), #ifdef Q_OS_DARWIN // Macs and/or iOS et al use a domain-style name for Settings // files. All other platforms use a simple orgname. This // difference is documented in the QSettings class documentation. legacyOrg("bitcoin.org"); #else legacyOrg("Bitcoin"); #endif QSettings // below picks up settings file location based on orgname,appname legacy(legacyOrg, legacyAppName), // default c'tor below picks up settings file location based on // QApplication::applicationName(), et al -- which was already set // in main() abc; #ifdef Q_OS_DARWIN // Disable bogus OSX keys from MacOS system-wide prefs that may cloud our // judgement ;) (this behavior is also documented in QSettings docs) legacy.setFallbacksEnabled(false); abc.setFallbacksEnabled(false); #endif const QStringList legacyKeys(legacy.allKeys()); // We only migrate settings if we have Core settings but no Bitcoin-ABC // settings if (!legacyKeys.isEmpty() && abc.allKeys().isEmpty()) { for (const QString &key : legacyKeys) { // now, copy settings over abc.setValue(key, legacy.value(key)); } } } int main(int argc, char *argv[]) { SetupEnvironment(); /// 1. Parse command-line options. These take precedence over anything else. // Command-line options take precedence: gArgs.ParseParameters(argc, argv); // Do not refer to data directory yet, this can be overridden by // Intro::pickDataDirectory /// 2. Basic Qt initialization (not dependent on parameters or /// configuration) Q_INIT_RESOURCE(bitcoin); Q_INIT_RESOURCE(bitcoin_locale); BitcoinApplication app(argc, argv); #if QT_VERSION > 0x050100 // Generate high-dpi pixmaps QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps); #endif #if QT_VERSION >= 0x050600 QGuiApplication::setAttribute(Qt::AA_EnableHighDpiScaling); #endif #ifdef Q_OS_MAC QApplication::setAttribute(Qt::AA_DontShowIconsInMenus); #endif #if QT_VERSION >= 0x050500 // Because of the POODLE attack it is recommended to disable SSLv3 // (https://disablessl3.com/), so set SSL protocols to TLS1.0+. QSslConfiguration sslconf = QSslConfiguration::defaultConfiguration(); sslconf.setProtocol(QSsl::TlsV1_0OrLater); QSslConfiguration::setDefaultConfiguration(sslconf); #endif // Register meta types used for QMetaObject::invokeMethod qRegisterMetaType(); // Need to pass name here as Amount is a typedef (see // http://qt-project.org/doc/qt-5/qmetatype.html#qRegisterMetaType) // IMPORTANT if it is no longer a typedef use the normal variant above qRegisterMetaType("Amount"); qRegisterMetaType>("std::function"); // Need to register any types Qt doesn't know about if you intend // to use them with the signal/slot mechanism Qt provides. Even pointers. // Note that class Config is noncopyable and so we can't register a // non-pointer version of it with Qt, because Qt expects to be able to // copy-construct non-pointers to objects for invoking slots // behind-the-scenes in the 'Queued' connection case. qRegisterMetaType(); /// 3. Application identification // must be set before OptionsModel is initialized or translations are // loaded, as it is used to locate QSettings. // Note: If you move these calls somewhere else, be sure to bring // MigrateSettings() below along for the ride. QApplication::setOrganizationName(QAPP_ORG_NAME); QApplication::setOrganizationDomain(QAPP_ORG_DOMAIN); QApplication::setApplicationName(QAPP_APP_NAME_DEFAULT); // Migrate settings from core's/our old GUI settings to Bitcoin ABC // only if core's exist but Bitcoin ABC's doesn't. // NOTE -- this function needs to be called *after* the above 3 lines // that set the app orgname and app name! If you move the above 3 lines // to elsewhere, take this call with you! MigrateSettings(); GUIUtil::SubstituteFonts(GetLangTerritory()); /// 4. Initialization of translations, so that intro dialog is in user's /// language. Now that QSettings are accessible, initialize translations. QTranslator qtTranslatorBase, qtTranslator, translatorBase, translator; initTranslations(qtTranslatorBase, qtTranslator, translatorBase, translator); translationInterface.Translate.connect(Translate); // Show help message immediately after parsing command-line options (for // "-lang") and setting locale, but before showing splash screen. - if (gArgs.IsArgSet("-?") || gArgs.IsArgSet("-h") || - gArgs.IsArgSet("-help") || gArgs.IsArgSet("-version")) { + if (HelpRequested(gArgs) || gArgs.IsArgSet("-version")) { HelpMessageDialog help(nullptr, gArgs.IsArgSet("-version")); help.showOrPrint(); return EXIT_SUCCESS; } /// 5. Now that settings and translations are available, ask user for data /// directory. User language is set up: pick a data directory. if (!Intro::pickDataDirectory()) return EXIT_SUCCESS; /// 6. Determine availability of data directory and parse bitcoin.conf /// - Do not call GetDataDir(true) before this step finishes. if (!fs::is_directory(GetDataDir(false))) { QMessageBox::critical( 0, QObject::tr(PACKAGE_NAME), QObject::tr( "Error: Specified data directory \"%1\" does not exist.") .arg(QString::fromStdString(gArgs.GetArg("-datadir", "")))); return EXIT_FAILURE; } try { gArgs.ReadConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)); } catch (const std::exception &e) { QMessageBox::critical( 0, QObject::tr(PACKAGE_NAME), QObject::tr("Error: Cannot parse configuration file: %1. Only use " "key=value syntax.") .arg(e.what())); return EXIT_FAILURE; } /// 7. Determine network (and switch to network specific options) // - Do not call Params() before this step. // - Do this after parsing the configuration file, as the network can be // switched there. // - QSettings() will use the new application name after this, resulting in // network-specific settings. // - Needs to be done before createOptionsModel. // Check for -testnet or -regtest parameter (Params() calls are only valid // after this clause) try { SelectParams(gArgs.GetChainName()); } catch (std::exception &e) { QMessageBox::critical(0, QObject::tr(PACKAGE_NAME), QObject::tr("Error: %1").arg(e.what())); return EXIT_FAILURE; } #ifdef ENABLE_WALLET // Parse URIs on command line -- this can affect Params() PaymentServer::ipcParseCommandLine(argc, argv); #endif QScopedPointer networkStyle(NetworkStyle::instantiate( QString::fromStdString(Params().NetworkIDString()))); assert(!networkStyle.isNull()); // Allow for separate UI settings for testnets QApplication::setApplicationName(networkStyle->getAppName()); // Re-initialize translations after changing application name (language in // network-specific settings can be different) initTranslations(qtTranslatorBase, qtTranslator, translatorBase, translator); #ifdef ENABLE_WALLET /// 8. URI IPC sending // - Do this early as we don't want to bother initializing if we are just // calling IPC // - Do this *after* setting up the data directory, as the data directory // hash is used in the name // of the server. // - Do this after creating app and setting up translations, so errors are // translated properly. if (PaymentServer::ipcSendCommandLine()) exit(EXIT_SUCCESS); // Start up the payment server early, too, so impatient users that click on // bitcoincash: links repeatedly have their payment requests routed to this // process: app.createPaymentServer(); // Hook up the wallet init interface g_wallet_init_interface.reset(new WalletInit); #else g_wallet_init_interface.reset(new DummyWalletInit); #endif /// 9. Main GUI initialization // Install global event filter that makes sure that long tooltips can be // word-wrapped. app.installEventFilter( new GUIUtil::ToolTipToRichTextFilter(TOOLTIP_WRAP_THRESHOLD, &app)); #if defined(Q_OS_WIN) // Install global event filter for processing Windows session related // Windows messages (WM_QUERYENDSESSION and WM_ENDSESSION) qApp->installNativeEventFilter(new WinShutdownMonitor()); #endif // Install qDebug() message handler to route to debug.log qInstallMessageHandler(DebugMessageHandler); // Allow parameter interaction before we create the options model app.parameterSetup(); // Load GUI settings from QSettings app.createOptionsModel(gArgs.GetBoolArg("-resetguisettings", false)); // Subscribe to global signals from core uiInterface.InitMessage.connect(InitMessage); // Get global config Config &config = const_cast(GetConfig()); if (gArgs.GetBoolArg("-splash", DEFAULT_SPLASHSCREEN) && !gArgs.GetBoolArg("-min", false)) app.createSplashScreen(networkStyle.data()); RPCServer rpcServer; HTTPRPCRequestProcessor httpRPCRequestProcessor(config, rpcServer); int rv = EXIT_SUCCESS; try { app.createWindow(&config, networkStyle.data()); // Perform base initialization before spinning up // initialization/shutdown thread. This is acceptable because this // function only contains steps that are quick to execute, so the GUI // thread won't be held up. if (BitcoinABC::baseInitialize(config, rpcServer)) { app.requestInitialize(config, httpRPCRequestProcessor, rpcServer); #if defined(Q_OS_WIN) WinShutdownMonitor::registerShutdownBlockReason( QObject::tr("%1 didn't yet exit safely...") .arg(QObject::tr(PACKAGE_NAME)), (HWND)app.getMainWinId()); #endif app.exec(); app.requestShutdown(config); app.exec(); rv = app.getReturnValue(); } else { // A dialog with detailed error will have been shown by InitError() rv = EXIT_FAILURE; } } catch (const std::exception &e) { PrintExceptionContinue(&e, "Runaway exception"); app.handleRunawayException(QString::fromStdString(GetWarnings("gui"))); } catch (...) { PrintExceptionContinue(nullptr, "Runaway exception"); app.handleRunawayException(QString::fromStdString(GetWarnings("gui"))); } return rv; } #endif // BITCOIN_QT_TEST diff --git a/src/util.cpp b/src/util.cpp index a13154612..217ca443d 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -1,743 +1,747 @@ // 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. #if defined(HAVE_CONFIG_H) #include "config/bitcoin-config.h" #endif #include "util.h" #include "chainparamsbase.h" #include "fs.h" #include "random.h" #include "serialize.h" #include "utilstrencodings.h" #include "utiltime.h" #include #if (defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)) #include #include #endif #ifndef WIN32 // for posix_fallocate #ifdef __linux__ #ifdef _POSIX_C_SOURCE #undef _POSIX_C_SOURCE #endif #define _POSIX_C_SOURCE 200112L #endif // __linux__ #include #include #include #include #else #ifdef _MSC_VER #pragma warning(disable : 4786) #pragma warning(disable : 4804) #pragma warning(disable : 4805) #pragma warning(disable : 4717) #endif #ifdef _WIN32_WINNT #undef _WIN32_WINNT #endif #define _WIN32_WINNT 0x0501 #ifdef _WIN32_IE #undef _WIN32_IE #endif #define _WIN32_IE 0x0501 #define WIN32_LEAN_AND_MEAN 1 #ifndef NOMINMAX #define NOMINMAX #endif #include /* for _commit */ #include #endif #ifdef HAVE_SYS_PRCTL_H #include #endif #ifdef HAVE_MALLOPT_ARENA_MAX #include #endif #include #include #include #include // Application startup time (used for uptime calculation) const int64_t nStartupTime = GetTime(); const char *const BITCOIN_CONF_FILENAME = "bitcoin.conf"; const char *const BITCOIN_PID_FILENAME = "bitcoind.pid"; ArgsManager gArgs; CTranslationInterface translationInterface; /** Init OpenSSL library multithreading support */ static CCriticalSection **ppmutexOpenSSL; void locking_callback(int mode, int i, const char *file, int line) NO_THREAD_SAFETY_ANALYSIS { if (mode & CRYPTO_LOCK) { ENTER_CRITICAL_SECTION(*ppmutexOpenSSL[i]); } else { LEAVE_CRITICAL_SECTION(*ppmutexOpenSSL[i]); } } // Init class CInit { public: CInit() { // Init OpenSSL library multithreading support. ppmutexOpenSSL = (CCriticalSection **)OPENSSL_malloc( CRYPTO_num_locks() * sizeof(CCriticalSection *)); for (int i = 0; i < CRYPTO_num_locks(); i++) { ppmutexOpenSSL[i] = new CCriticalSection(); } CRYPTO_set_locking_callback(locking_callback); // OpenSSL can optionally load a config file which lists optional // loadable modules and engines. We don't use them so we don't require // the config. However some of our libs may call functions which attempt // to load the config file, possibly resulting in an exit() or crash if // it is missing or corrupt. Explicitly tell OpenSSL not to try to load // the file. The result for our libs will be that the config appears to // have been loaded and there are no modules/engines available. OPENSSL_no_config(); #ifdef WIN32 // Seed OpenSSL PRNG with current contents of the screen. RAND_screen(); #endif // Seed OpenSSL PRNG with performance counter. RandAddSeed(); } ~CInit() { // Securely erase the memory used by the PRNG. RAND_cleanup(); // Shutdown OpenSSL library multithreading support. CRYPTO_set_locking_callback(nullptr); for (int i = 0; i < CRYPTO_num_locks(); i++) { delete ppmutexOpenSSL[i]; } OPENSSL_free(ppmutexOpenSSL); } } instance_of_cinit; /** * Interpret a string argument as a boolean. * * The definition of atoi() requires that non-numeric string values like "foo", * return 0. This means that if a user unintentionally supplies a non-integer * argument here, the return value is always false. This means that -foo=false * does what the user probably expects, but -foo=true is well defined but does * not do what they probably expected. * * The return value of atoi() is undefined when given input not representable as * an int. On most systems this means string value between "-2147483648" and * "2147483647" are well defined (this method will return true). Setting * -txindex=2147483648 on most systems, however, is probably undefined. * * For a more extensive discussion of this topic (and a wide range of opinions * on the Right Way to change this code), see PR12713. */ static bool InterpretBool(const std::string &strValue) { if (strValue.empty()) { return true; } return (atoi(strValue) != 0); } /** * Interpret -nofoo as if the user supplied -foo=0. * * This method also tracks when the -no form was supplied, and treats "-foo" as * a negated option when this happens. This can be later checked using the * IsArgNegated() method. One use case for this is to have a way to disable * options that are not normally boolean (e.g. using -nodebuglogfile to request * that debug log output is not sent to any file at all). */ void ArgsManager::InterpretNegatedOption(std::string &key, std::string &val) { if (key.substr(0, 3) == "-no") { bool bool_val = InterpretBool(val); if (!bool_val) { // Double negatives like -nofoo=0 are supported (but discouraged) LogPrintf( "Warning: parsed potentially confusing double-negative %s=%s\n", key, val); } key.erase(1, 2); m_negated_args.insert(key); val = bool_val ? "0" : "1"; } else { // In an invocation like "bitcoind -nofoo -foo" we want to unmark -foo // as negated when we see the second option. m_negated_args.erase(key); } } void ArgsManager::ParseParameters(int argc, const char *const argv[]) { LOCK(cs_args); mapArgs.clear(); mapMultiArgs.clear(); m_negated_args.clear(); for (int i = 1; i < argc; i++) { std::string key(argv[i]); std::string val; size_t is_index = key.find('='); if (is_index != std::string::npos) { val = key.substr(is_index + 1); key.erase(is_index); } #ifdef WIN32 std::transform(key.begin(), key.end(), key.begin(), ::tolower); if (key[0] == '/') { key[0] = '-'; } #endif if (key[0] != '-') { break; } // Transform --foo to -foo if (key.length() > 1 && key[1] == '-') { key.erase(0, 1); } // Transform -nofoo to -foo=0 InterpretNegatedOption(key, val); mapArgs[key] = val; mapMultiArgs[key].push_back(val); } } std::vector ArgsManager::GetArgs(const std::string &strArg) const { LOCK(cs_args); auto it = mapMultiArgs.find(strArg); if (it != mapMultiArgs.end()) { return it->second; } return {}; } bool ArgsManager::IsArgSet(const std::string &strArg) const { LOCK(cs_args); return mapArgs.count(strArg); } bool ArgsManager::IsArgNegated(const std::string &strArg) const { LOCK(cs_args); return m_negated_args.find(strArg) != m_negated_args.end(); } std::string ArgsManager::GetArg(const std::string &strArg, const std::string &strDefault) const { LOCK(cs_args); auto it = mapArgs.find(strArg); if (it != mapArgs.end()) { return it->second; } return strDefault; } int64_t ArgsManager::GetArg(const std::string &strArg, int64_t nDefault) const { LOCK(cs_args); auto it = mapArgs.find(strArg); if (it != mapArgs.end()) { return atoi64(it->second); } return nDefault; } bool ArgsManager::GetBoolArg(const std::string &strArg, bool fDefault) const { LOCK(cs_args); auto it = mapArgs.find(strArg); if (it != mapArgs.end()) { return InterpretBool(it->second); } return fDefault; } bool ArgsManager::SoftSetArg(const std::string &strArg, const std::string &strValue) { LOCK(cs_args); if (IsArgSet(strArg)) { return false; } ForceSetArg(strArg, strValue); return true; } bool ArgsManager::SoftSetBoolArg(const std::string &strArg, bool fValue) { if (fValue) { return SoftSetArg(strArg, std::string("1")); } else { return SoftSetArg(strArg, std::string("0")); } } void ArgsManager::ForceSetArg(const std::string &strArg, const std::string &strValue) { LOCK(cs_args); mapArgs[strArg] = strValue; mapMultiArgs[strArg] = {strValue}; } /** * This function is only used for testing purpose so * so we should not worry about element uniqueness and * integrity of mapMultiArgs data structure */ void ArgsManager::ForceSetMultiArg(const std::string &strArg, const std::string &strValue) { LOCK(cs_args); if (mapArgs.count(strArg) == 0) { mapArgs[strArg] = strValue; } mapMultiArgs[strArg].push_back(strValue); } void ArgsManager::ClearArg(const std::string &strArg) { LOCK(cs_args); mapArgs.erase(strArg); } +bool HelpRequested(const ArgsManager &args) { + return args.IsArgSet("-?") || args.IsArgSet("-h") || args.IsArgSet("-help"); +} + static const int screenWidth = 79; static const int optIndent = 2; static const int msgIndent = 7; std::string HelpMessageGroup(const std::string &message) { return std::string(message) + std::string("\n\n"); } std::string HelpMessageOpt(const std::string &option, const std::string &message) { return std::string(optIndent, ' ') + std::string(option) + std::string("\n") + std::string(msgIndent, ' ') + FormatParagraph(message, screenWidth - msgIndent, msgIndent) + std::string("\n\n"); } static std::string FormatException(const std::exception *pex, const char *pszThread) { #ifdef WIN32 char pszModule[MAX_PATH] = ""; GetModuleFileNameA(nullptr, pszModule, sizeof(pszModule)); #else const char *pszModule = "bitcoin"; #endif if (pex) { return strprintf("EXCEPTION: %s \n%s \n%s in %s \n", typeid(*pex).name(), pex->what(), pszModule, pszThread); } else { return strprintf("UNKNOWN EXCEPTION \n%s in %s \n", pszModule, pszThread); } } void PrintExceptionContinue(const std::exception *pex, const char *pszThread) { std::string message = FormatException(pex, pszThread); LogPrintf("\n\n************************\n%s\n", message); fprintf(stderr, "\n\n************************\n%s\n", message.c_str()); } fs::path GetDefaultDataDir() { // Windows < Vista: C:\Documents and Settings\Username\Application Data\Bitcoin // Windows >= Vista: C:\Users\Username\AppData\Roaming\Bitcoin // Mac: ~/Library/Application Support/Bitcoin // Unix: ~/.bitcoin #ifdef WIN32 // Windows return GetSpecialFolderPath(CSIDL_APPDATA) / "Bitcoin"; #else fs::path pathRet; char *pszHome = getenv("HOME"); if (pszHome == nullptr || strlen(pszHome) == 0) { pathRet = fs::path("/"); } else { pathRet = fs::path(pszHome); } #ifdef MAC_OSX // Mac return pathRet / "Library/Application Support/Bitcoin"; #else // Unix return pathRet / ".bitcoin"; #endif #endif } static fs::path pathCached; static fs::path pathCachedNetSpecific; static CCriticalSection csPathCached; const fs::path &GetDataDir(bool fNetSpecific) { LOCK(csPathCached); fs::path &path = fNetSpecific ? pathCachedNetSpecific : pathCached; // This can be called during exceptions by LogPrintf(), so we cache the // value so we don't have to do memory allocations after that. if (!path.empty()) { return path; } if (gArgs.IsArgSet("-datadir")) { path = fs::system_complete(gArgs.GetArg("-datadir", "")); if (!fs::is_directory(path)) { path = ""; return path; } } else { path = GetDefaultDataDir(); } if (fNetSpecific) { path /= BaseParams().DataDir(); } fs::create_directories(path); return path; } void ClearDatadirCache() { LOCK(csPathCached); pathCached = fs::path(); pathCachedNetSpecific = fs::path(); } fs::path GetConfigFile(const std::string &confPath) { fs::path pathConfigFile(confPath); if (!pathConfigFile.is_complete()) { pathConfigFile = GetDataDir(false) / pathConfigFile; } return pathConfigFile; } void ArgsManager::ReadConfigStream(std::istream &stream) { LOCK(cs_args); std::set setOptions; setOptions.insert("*"); for (boost::program_options::detail::config_file_iterator it(stream, setOptions), end; it != end; ++it) { // Don't overwrite existing settings so command line settings override // bitcoin.conf std::string strKey = std::string("-") + it->string_key; std::string strValue = it->value[0]; InterpretNegatedOption(strKey, strValue); if (mapArgs.count(strKey) == 0) { mapArgs[strKey] = strValue; } mapMultiArgs[strKey].push_back(strValue); } } void ArgsManager::ReadConfigFile(const std::string &confPath) { fs::ifstream stream(GetConfigFile(confPath)); // ok to not have a config file if (stream.good()) { ReadConfigStream(stream); } // If datadir is changed in .conf file: ClearDatadirCache(); } std::string ArgsManager::GetChainName() const { bool fRegTest = GetBoolArg("-regtest", false); bool fTestNet = GetBoolArg("-testnet", false); if (fTestNet && fRegTest) { throw std::runtime_error( "Invalid combination of -regtest and -testnet."); } if (fRegTest) { return CBaseChainParams::REGTEST; } if (fTestNet) { return CBaseChainParams::TESTNET; } return CBaseChainParams::MAIN; } #ifndef WIN32 fs::path GetPidFile() { fs::path pathPidFile(gArgs.GetArg("-pid", BITCOIN_PID_FILENAME)); if (!pathPidFile.is_complete()) { pathPidFile = GetDataDir() / pathPidFile; } return pathPidFile; } void CreatePidFile(const fs::path &path, pid_t pid) { FILE *file = fsbridge::fopen(path, "w"); if (file) { fprintf(file, "%d\n", pid); fclose(file); } } #endif bool RenameOver(fs::path src, fs::path dest) { #ifdef WIN32 return MoveFileExA(src.string().c_str(), dest.string().c_str(), MOVEFILE_REPLACE_EXISTING) != 0; #else int rc = std::rename(src.string().c_str(), dest.string().c_str()); return (rc == 0); #endif /* WIN32 */ } /** * Ignores exceptions thrown by Boost's create_directories if the requested * directory exists. Specifically handles case where path p exists, but it * wasn't possible for the user to write to the parent directory. */ bool TryCreateDirectories(const fs::path &p) { try { return fs::create_directories(p); } catch (const fs::filesystem_error &) { if (!fs::exists(p) || !fs::is_directory(p)) { throw; } } // create_directory didn't create the directory, it had to have existed // already. return false; } void FileCommit(FILE *file) { // Harmless if redundantly called. fflush(file); #ifdef WIN32 HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file)); FlushFileBuffers(hFile); #else #if defined(__linux__) || defined(__NetBSD__) fdatasync(fileno(file)); #elif defined(__APPLE__) && defined(F_FULLFSYNC) fcntl(fileno(file), F_FULLFSYNC, 0); #else fsync(fileno(file)); #endif #endif } bool TruncateFile(FILE *file, unsigned int length) { #if defined(WIN32) return _chsize(_fileno(file), length) == 0; #else return ftruncate(fileno(file), length) == 0; #endif } /** * This function tries to raise the file descriptor limit to the requested * number. It returns the actual file descriptor limit (which may be more or * less than nMinFD) */ int RaiseFileDescriptorLimit(int nMinFD) { #if defined(WIN32) return 2048; #else struct rlimit limitFD; if (getrlimit(RLIMIT_NOFILE, &limitFD) != -1) { if (limitFD.rlim_cur < (rlim_t)nMinFD) { limitFD.rlim_cur = nMinFD; if (limitFD.rlim_cur > limitFD.rlim_max) { limitFD.rlim_cur = limitFD.rlim_max; } setrlimit(RLIMIT_NOFILE, &limitFD); getrlimit(RLIMIT_NOFILE, &limitFD); } return limitFD.rlim_cur; } // getrlimit failed, assume it's fine. return nMinFD; #endif } /** * This function tries to make a particular range of a file allocated * (corresponding to disk space) it is advisory, and the range specified in the * arguments will never contain live data. */ void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length) { #if defined(WIN32) // Windows-specific version. HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file)); LARGE_INTEGER nFileSize; int64_t nEndPos = (int64_t)offset + length; nFileSize.u.LowPart = nEndPos & 0xFFFFFFFF; nFileSize.u.HighPart = nEndPos >> 32; SetFilePointerEx(hFile, nFileSize, 0, FILE_BEGIN); SetEndOfFile(hFile); #elif defined(MAC_OSX) // OSX specific version. fstore_t fst; fst.fst_flags = F_ALLOCATECONTIG; fst.fst_posmode = F_PEOFPOSMODE; fst.fst_offset = 0; fst.fst_length = (off_t)offset + length; fst.fst_bytesalloc = 0; if (fcntl(fileno(file), F_PREALLOCATE, &fst) == -1) { fst.fst_flags = F_ALLOCATEALL; fcntl(fileno(file), F_PREALLOCATE, &fst); } ftruncate(fileno(file), fst.fst_length); #elif defined(__linux__) // Version using posix_fallocate. off_t nEndPos = (off_t)offset + length; posix_fallocate(fileno(file), 0, nEndPos); #else // Fallback version // TODO: just write one byte per block static const char buf[65536] = {}; fseek(file, offset, SEEK_SET); while (length > 0) { unsigned int now = 65536; if (length < now) { now = length; } // Allowed to fail; this function is advisory anyway. fwrite(buf, 1, now, file); length -= now; } #endif } #ifdef WIN32 fs::path GetSpecialFolderPath(int nFolder, bool fCreate) { char pszPath[MAX_PATH] = ""; if (SHGetSpecialFolderPathA(nullptr, pszPath, nFolder, fCreate)) { return fs::path(pszPath); } LogPrintf( "SHGetSpecialFolderPathA() failed, could not obtain requested path.\n"); return fs::path(""); } #endif void runCommand(const std::string &strCommand) { if (strCommand.empty()) { return; } int nErr = ::system(strCommand.c_str()); if (nErr) { LogPrintf("runCommand error: system(%s) returned %d\n", strCommand, nErr); } } void RenameThread(const char *name) { #if defined(PR_SET_NAME) // Only the first 15 characters are used (16 - NUL terminator) ::prctl(PR_SET_NAME, name, 0, 0, 0); #elif (defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)) pthread_set_name_np(pthread_self(), name); #elif defined(MAC_OSX) pthread_setname_np(name); #else // Prevent warnings for unused parameters... (void)name; #endif } void SetupEnvironment() { #ifdef HAVE_MALLOPT_ARENA_MAX // glibc-specific: On 32-bit systems set the number of arenas to 1. By // default, since glibc 2.10, the C library will create up to two heap // arenas per core. This is known to cause excessive virtual address space // usage in our usage. Work around it by setting the maximum number of // arenas to 1. if (sizeof(void *) == 4) { mallopt(M_ARENA_MAX, 1); } #endif // On most POSIX systems (e.g. Linux, but not BSD) the environment's locale may // be invalid, in which case the "C" locale is used as fallback. #if !defined(WIN32) && !defined(MAC_OSX) && !defined(__FreeBSD__) && \ !defined(__OpenBSD__) try { // Raises a runtime error if current locale is invalid. std::locale(""); } catch (const std::runtime_error &) { setenv("LC_ALL", "C", 1); } #endif // The path locale is lazy initialized and to avoid deinitialization errors // in multithreading environments, it is set explicitly by the main thread. // A dummy locale is used to extract the internal default locale, used by // fs::path, which is then used to explicitly imbue the path. std::locale loc = fs::path::imbue(std::locale::classic()); fs::path::imbue(loc); } bool SetupNetworking() { #ifdef WIN32 // Initialize Windows Sockets. WSADATA wsadata; int ret = WSAStartup(MAKEWORD(2, 2), &wsadata); if (ret != NO_ERROR || LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wVersion) != 2) { return false; } #endif return true; } int GetNumCores() { #if BOOST_VERSION >= 105600 return boost::thread::physical_concurrency(); #else // Must fall back to hardware_concurrency, which unfortunately counts // virtual cores. return boost::thread::hardware_concurrency(); #endif } std::string CopyrightHolders(const std::string &strPrefix) { return strPrefix + strprintf(_(COPYRIGHT_HOLDERS), _(COPYRIGHT_HOLDERS_SUBSTITUTION)); } // Obtain the application startup time (used for uptime calculation) int64_t GetStartupTime() { return nStartupTime; } diff --git a/src/util.h b/src/util.h index 76bb33302..4ddae77b8 100644 --- a/src/util.h +++ b/src/util.h @@ -1,256 +1,261 @@ // 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. /** * Server/client environment: argument handling, config file parsing, * thread wrappers, startup time */ #ifndef BITCOIN_UTIL_H #define BITCOIN_UTIL_H #if defined(HAVE_CONFIG_H) #include "config/bitcoin-config.h" #endif #include "compat.h" #include "fs.h" #include "logging.h" #include "sync.h" #include "tinyformat.h" #include "utiltime.h" #include #include #include #include #include #include #include #include #include // for boost::thread_interrupted // Application startup time (used for uptime calculation) int64_t GetStartupTime(); /** Signals for translation. */ class CTranslationInterface { public: /** Translate a message to the native language of the user. */ boost::signals2::signal Translate; }; extern CTranslationInterface translationInterface; extern const char *const BITCOIN_CONF_FILENAME; extern const char *const BITCOIN_PID_FILENAME; /** * Translation function: Call Translate signal on UI interface, which returns a * boost::optional result. If no translation slot is registered, nothing is * returned, and simply return the input. */ inline std::string _(const char *psz) { boost::optional rv = translationInterface.Translate(psz); return rv ? (*rv) : psz; } void SetupEnvironment(); bool SetupNetworking(); template bool error(const char *fmt, const Args &... args) { LogPrintf("ERROR: " + tfm::format(fmt, args...) + "\n"); return false; } void PrintExceptionContinue(const std::exception *pex, const char *pszThread); void FileCommit(FILE *file); bool TruncateFile(FILE *file, unsigned int length); int RaiseFileDescriptorLimit(int nMinFD); void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length); bool RenameOver(fs::path src, fs::path dest); bool TryCreateDirectories(const fs::path &p); fs::path GetDefaultDataDir(); const fs::path &GetDataDir(bool fNetSpecific = true); void ClearDatadirCache(); fs::path GetConfigFile(const std::string &confPath); #ifndef WIN32 fs::path GetPidFile(); void CreatePidFile(const fs::path &path, pid_t pid); #endif #ifdef WIN32 fs::path GetSpecialFolderPath(int nFolder, bool fCreate = true); #endif void runCommand(const std::string &strCommand); inline bool IsSwitchChar(char c) { #ifdef WIN32 return c == '-' || c == '/'; #else return c == '-'; #endif } class ArgsManager { protected: mutable CCriticalSection cs_args; std::map mapArgs; std::map> mapMultiArgs; std::unordered_set m_negated_args; void ReadConfigStream(std::istream &stream); public: void ParseParameters(int argc, const char *const argv[]); void ReadConfigFile(const std::string &confPath); /** * Return a vector of strings of the given argument * * @param strArg Argument to get (e.g. "-foo") * @return command-line arguments */ std::vector GetArgs(const std::string &strArg) const; /** * Return true if the given argument has been manually set. * * @param strArg Argument to get (e.g. "-foo") * @return true if the argument has been set */ bool IsArgSet(const std::string &strArg) const; /** * Return true if the argument was originally passed as a negated option, * i.e. -nofoo. * * @param strArg Argument to get (e.g. "-foo") * @return true if the argument was passed negated */ bool IsArgNegated(const std::string &strArg) const; /** * Return string argument or default value. * * @param strArg Argument to get (e.g. "-foo") * @param default (e.g. "1") * @return command-line argument or default value */ std::string GetArg(const std::string &strArg, const std::string &strDefault) const; /** * Return integer argument or default value. * * @param strArg Argument to get (e.g. "-foo") * @param default (e.g. 1) * @return command-line argument (0 if invalid number) or default value */ int64_t GetArg(const std::string &strArg, int64_t nDefault) const; /** * Return boolean argument or default value. * * @param strArg Argument to get (e.g. "-foo") * @param default (true or false) * @return command-line argument or default value */ bool GetBoolArg(const std::string &strArg, bool fDefault) const; /** * Set an argument if it doesn't already have a value. * * @param strArg Argument to set (e.g. "-foo") * @param strValue Value (e.g. "1") * @return true if argument gets set, false if it already had a value */ bool SoftSetArg(const std::string &strArg, const std::string &strValue); /** * Set a boolean argument if it doesn't already have a value. * * @param strArg Argument to set (e.g. "-foo") * @param fValue Value (e.g. false) * @return true if argument gets set, false if it already had a value */ bool SoftSetBoolArg(const std::string &strArg, bool fValue); // Forces a arg setting, used only in testing void ForceSetArg(const std::string &strArg, const std::string &strValue); // Forces a multi arg setting, used only in testing void ForceSetMultiArg(const std::string &strArg, const std::string &strValue); /** * Looks for -regtest, -testnet and returns the appropriate BIP70 chain * name. * @return CBaseChainParams::MAIN by default; raises runtime error if an * invalid combination is given. */ std::string GetChainName() const; // Remove an arg setting, used only in testing void ClearArg(const std::string &strArg); private: // Munge -nofoo into -foo=0 and track the value as negated. void InterpretNegatedOption(std::string &key, std::string &val); }; extern ArgsManager gArgs; +/** + * @return true if help has been requested via a command-line arg + */ +bool HelpRequested(const ArgsManager &args); + /** * Format a string to be used as group of options in help messages. * * @param message Group name (e.g. "RPC server options:") * @return the formatted string */ std::string HelpMessageGroup(const std::string &message); /** * Format a string to be used as option description in help messages. * * @param option Option message (e.g. "-rpcuser=") * @param message Option description (e.g. "Username for JSON-RPC connections") * @return the formatted string */ std::string HelpMessageOpt(const std::string &option, const std::string &message); /** * Return the number of physical cores available on the current system. * @note This does not count virtual cores, such as those provided by * HyperThreading when boost is newer than 1.56. */ int GetNumCores(); void RenameThread(const char *name); /** * .. and a wrapper that just calls func once */ template void TraceThread(const char *name, Callable func) { std::string s = strprintf("bitcoin-%s", name); RenameThread(s.c_str()); try { LogPrintf("%s thread start\n", name); func(); LogPrintf("%s thread exit\n", name); } catch (const boost::thread_interrupted &) { LogPrintf("%s thread interrupt\n", name); throw; } catch (const std::exception &e) { PrintExceptionContinue(&e, name); throw; } catch (...) { PrintExceptionContinue(nullptr, name); throw; } } std::string CopyrightHolders(const std::string &strPrefix); #endif // BITCOIN_UTIL_H