diff options
Diffstat (limited to 'src')
111 files changed, 2723 insertions, 1177 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 397c7acb49..96e56915a6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -5,8 +5,8 @@ DIST_SUBDIRS = secp256k1 univalue AM_LDFLAGS = $(PTHREAD_CFLAGS) $(LIBTOOL_LDFLAGS) $(HARDENED_LDFLAGS) $(GPROF_LDFLAGS) $(SANITIZER_LDFLAGS) -AM_CXXFLAGS = $(HARDENED_CXXFLAGS) $(ERROR_CXXFLAGS) $(GPROF_CXXFLAGS) $(SANITIZER_CXXFLAGS) -AM_CPPFLAGS = $(HARDENED_CPPFLAGS) +AM_CXXFLAGS = $(DEBUG_CXXFLAGS) $(HARDENED_CXXFLAGS) $(WARN_CXXFLAGS) $(NOWARN_CXXFLAGS) $(ERROR_CXXFLAGS) $(GPROF_CXXFLAGS) $(SANITIZER_CXXFLAGS) +AM_CPPFLAGS = $(DEBUG_CPPFLAGS) $(HARDENED_CPPFLAGS) AM_LIBTOOLFLAGS = --preserve-dup-deps EXTRA_LIBRARIES = diff --git a/src/Makefile.qt.include b/src/Makefile.qt.include index 73380f6492..f8c31be3d4 100644 --- a/src/Makefile.qt.include +++ b/src/Makefile.qt.include @@ -278,6 +278,7 @@ RES_ICONS = \ qt/res/icons/network_disabled.png \ qt/res/icons/open.png \ qt/res/icons/overview.png \ + qt/res/icons/proxy.png \ qt/res/icons/quit.png \ qt/res/icons/receive.png \ qt/res/icons/remove.png \ diff --git a/src/Makefile.test.include b/src/Makefile.test.include index b3c4faed20..cbd63cd53d 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -86,9 +86,10 @@ BITCOIN_TESTS =\ test/txindex_tests.cpp \ test/txvalidation_tests.cpp \ test/txvalidationcache_tests.cpp \ - test/versionbits_tests.cpp \ test/uint256_tests.cpp \ - test/util_tests.cpp + test/util_tests.cpp \ + test/validation_block_tests.cpp \ + test/versionbits_tests.cpp if ENABLE_WALLET BITCOIN_TESTS += \ @@ -168,8 +169,8 @@ if EMBEDDED_UNIVALUE endif %.cpp.test: %.cpp - @echo Running tests: `cat $< | grep "BOOST_FIXTURE_TEST_SUITE(\|BOOST_AUTO_TEST_SUITE(" | cut -d '(' -f 2 | cut -d ',' -f 1 | cut -d ')' -f 1` from $< - $(AM_V_at)$(TEST_BINARY) -l test_suite -t "`cat $< | grep "BOOST_FIXTURE_TEST_SUITE(\|BOOST_AUTO_TEST_SUITE(" | cut -d '(' -f 2 | cut -d ',' -f 1 | cut -d ')' -f 1`" > $<.log 2>&1 || (cat $<.log && false) + @echo Running tests: `cat $< | grep -E "(BOOST_FIXTURE_TEST_SUITE\\(|BOOST_AUTO_TEST_SUITE\\()" | cut -d '(' -f 2 | cut -d ',' -f 1 | cut -d ')' -f 1` from $< + $(AM_V_at)$(TEST_BINARY) -l test_suite -t "`cat $< | grep -E "(BOOST_FIXTURE_TEST_SUITE\\(|BOOST_AUTO_TEST_SUITE\\()" | cut -d '(' -f 2 | cut -d ',' -f 1 | cut -d ')' -f 1`" > $<.log 2>&1 || (cat $<.log && false) %.json.h: %.json @$(MKDIR_P) $(@D) diff --git a/src/bech32.cpp b/src/bech32.cpp index 274782e467..c55f22b9b7 100644 --- a/src/bech32.cpp +++ b/src/bech32.cpp @@ -160,9 +160,9 @@ std::pair<std::string, data> Decode(const std::string& str) { bool lower = false, upper = false; for (size_t i = 0; i < str.size(); ++i) { unsigned char c = str[i]; - if (c < 33 || c > 126) return {}; if (c >= 'a' && c <= 'z') lower = true; - if (c >= 'A' && c <= 'Z') upper = true; + else if (c >= 'A' && c <= 'Z') upper = true; + else if (c < 33 || c > 126) return {}; } if (lower && upper) return {}; size_t pos = str.rfind('1'); @@ -172,7 +172,8 @@ std::pair<std::string, data> Decode(const std::string& str) { data values(str.size() - 1 - pos); for (size_t i = 0; i < str.size() - 1 - pos; ++i) { unsigned char c = str[i + pos + 1]; - int8_t rev = (c < 33 || c > 126) ? -1 : CHARSET_REV[c]; + int8_t rev = CHARSET_REV[c]; + if (rev == -1) { return {}; } diff --git a/src/bench/bench.h b/src/bench/bench.h index e15cb81869..5d131c2aa8 100644 --- a/src/bench/bench.h +++ b/src/bench/bench.h @@ -111,9 +111,9 @@ public: class ConsolePrinter : public Printer { public: - void header(); - void result(const State& state); - void footer(); + void header() override; + void result(const State& state) override; + void footer() override; }; // creates box plot with plotly.js @@ -121,9 +121,9 @@ class PlotlyPrinter : public Printer { public: PlotlyPrinter(std::string plotly_url, int64_t width, int64_t height); - void header(); - void result(const State& state); - void footer(); + void header() override; + void result(const State& state) override; + void footer() override; private: std::string m_plotly_url; diff --git a/src/bench/bench_bitcoin.cpp b/src/bench/bench_bitcoin.cpp index c1f3339830..f3302bfe5a 100644 --- a/src/bench/bench_bitcoin.cpp +++ b/src/bench/bench_bitcoin.cpp @@ -6,11 +6,10 @@ #include <crypto/sha256.h> #include <key.h> -#include <validation.h> -#include <util.h> #include <random.h> - -#include <boost/lexical_cast.hpp> +#include <util.h> +#include <utilstrencodings.h> +#include <validation.h> #include <memory> @@ -22,24 +21,36 @@ static const char* DEFAULT_PLOT_PLOTLYURL = "https://cdn.plot.ly/plotly-latest.m static const int64_t DEFAULT_PLOT_WIDTH = 1024; static const int64_t DEFAULT_PLOT_HEIGHT = 768; -int -main(int argc, char** argv) +static void SetupBenchArgs() +{ + gArgs.AddArg("-?", "Print this help message and exit", false, OptionsCategory::OPTIONS); + gArgs.AddArg("-list", "List benchmarks without executing them. Can be combined with -scaling and -filter", false, OptionsCategory::OPTIONS); + gArgs.AddArg("-evals=<n>", strprintf("Number of measurement evaluations to perform. (default: %u)", DEFAULT_BENCH_EVALUATIONS), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-filter=<regex>", strprintf("Regular expression filter to select benchmark by name (default: %s)", DEFAULT_BENCH_FILTER), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-scaling=<n>", strprintf("Scaling factor for benchmark's runtime (default: %u)", DEFAULT_BENCH_SCALING), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-printer=(console|plot)", strprintf("Choose printer format. console: print data to console. plot: Print results as HTML graph (default: %s)", DEFAULT_BENCH_PRINTER), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-plot-plotlyurl=<uri>", strprintf("URL to use for plotly.js (default: %s)", DEFAULT_PLOT_PLOTLYURL), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-plot-width=<x>", strprintf("Plot width in pixel (default: %u)", DEFAULT_PLOT_WIDTH), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-plot-height=<x>", strprintf("Plot height in pixel (default: %u)", DEFAULT_PLOT_HEIGHT), false, OptionsCategory::OPTIONS); + + // Hidden + gArgs.AddArg("-h", "", false, OptionsCategory::HIDDEN); + gArgs.AddArg("-help", "", false, OptionsCategory::HIDDEN); +} + +int main(int argc, char** argv) { - gArgs.ParseParameters(argc, argv); + SetupBenchArgs(); + std::string error; + if (!gArgs.ParseParameters(argc, argv, error)) { + fprintf(stderr, "Error parsing command line arguments: %s\n", error.c_str()); + return EXIT_FAILURE; + } 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=<n>", strprintf(_("Number of measurement evaluations to perform. (default: %u)"), DEFAULT_BENCH_EVALUATIONS)) - << HelpMessageOpt("-filter=<regex>", strprintf(_("Regular expression filter to select benchmark by name (default: %s)"), DEFAULT_BENCH_FILTER)) - << HelpMessageOpt("-scaling=<n>", 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=<uri>", strprintf(_("URL to use for plotly.js (default: %s)"), DEFAULT_PLOT_PLOTLYURL)) - << HelpMessageOpt("-plot-width=<x>", strprintf(_("Plot width in pixel (default: %u)"), DEFAULT_PLOT_WIDTH)) - << HelpMessageOpt("-plot-height=<x>", strprintf(_("Plot height in pixel (default: %u)"), DEFAULT_PLOT_HEIGHT)); - - return 0; + std::cout << gArgs.GetHelpMessage(); + + return EXIT_SUCCESS; } SHA256AutoDetect(); @@ -52,8 +63,11 @@ main(int argc, char** argv) std::string scaling_str = gArgs.GetArg("-scaling", DEFAULT_BENCH_SCALING); bool is_list_only = gArgs.GetBoolArg("-list", false); - double scaling_factor = boost::lexical_cast<double>(scaling_str); - + double scaling_factor; + if (!ParseDouble(scaling_str, &scaling_factor)) { + fprintf(stderr, "Error parsing scaling factor as double: %s\n", scaling_str.c_str()); + return EXIT_FAILURE; + } std::unique_ptr<benchmark::Printer> printer(new benchmark::ConsolePrinter()); std::string printer_arg = gArgs.GetArg("-printer", DEFAULT_BENCH_PRINTER); @@ -67,4 +81,6 @@ main(int argc, char** argv) benchmark::BenchRunner::RunAll(*printer, evaluations, scaling_factor, regex_filter, is_list_only); ECC_Stop(); + + return EXIT_SUCCESS; } diff --git a/src/bitcoin-cli.cpp b/src/bitcoin-cli.cpp index 34472a0e61..be5ce14480 100644 --- a/src/bitcoin-cli.cpp +++ b/src/bitcoin-cli.cpp @@ -29,29 +29,31 @@ static const int DEFAULT_HTTP_CLIENT_TIMEOUT=900; static const bool DEFAULT_NAMED=false; static const int CONTINUE_EXECUTION=-1; -static std::string HelpMessageCli() +static void SetupCliArgs() { 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=<file>", strprintf(_("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)"), BITCOIN_CONF_FILENAME)); - strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory")); - strUsage += HelpMessageOpt("-getinfo", _("Get general information from the remote server. Note that unlike server-side RPC calls, the results of -getinfo is the result of multiple non-atomic requests. Some entries in the result may represent results from different states (e.g. wallet balance may be as of a different block from the chain state reported)")); - AppendParamsHelpMessages(strUsage); - strUsage += HelpMessageOpt("-named", strprintf(_("Pass named instead of positional arguments (default: %s)"), DEFAULT_NAMED)); - strUsage += HelpMessageOpt("-rpcclienttimeout=<n>", strprintf(_("Timeout in seconds during HTTP requests, or 0 for no timeout. (default: %d)"), DEFAULT_HTTP_CLIENT_TIMEOUT)); - strUsage += HelpMessageOpt("-rpcconnect=<ip>", strprintf(_("Send commands to node running on <ip> (default: %s)"), DEFAULT_RPCCONNECT)); - strUsage += HelpMessageOpt("-rpcpassword=<pw>", _("Password for JSON-RPC connections")); - strUsage += HelpMessageOpt("-rpcport=<port>", strprintf(_("Connect to JSON-RPC on <port> (default: %u or testnet: %u)"), defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort())); - strUsage += HelpMessageOpt("-rpcuser=<user>", _("Username for JSON-RPC connections")); - strUsage += HelpMessageOpt("-rpcwait", _("Wait for RPC server to start")); - strUsage += HelpMessageOpt("-rpcwallet=<walletname>", _("Send RPC for non-default wallet on RPC server (needs to exactly match corresponding -wallet option passed to bitcoind)")); - strUsage += HelpMessageOpt("-stdin", _("Read extra arguments from standard input, one per line until EOF/Ctrl-D (recommended for sensitive information such as passphrases). When combined with -stdinrpcpass, the first line from standard input is used for the RPC password.")); - 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."))); - - return strUsage; + + gArgs.AddArg("-?", "This help message", false, OptionsCategory::OPTIONS); + gArgs.AddArg("-conf=<file>", strprintf("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-datadir=<dir>", "Specify data directory", false, OptionsCategory::OPTIONS); + gArgs.AddArg("-getinfo", "Get general information from the remote server. Note that unlike server-side RPC calls, the results of -getinfo is the result of multiple non-atomic requests. Some entries in the result may represent results from different states (e.g. wallet balance may be as of a different block from the chain state reported)", false, OptionsCategory::OPTIONS); + SetupChainParamsBaseOptions(); + gArgs.AddArg("-named", strprintf("Pass named instead of positional arguments (default: %s)", DEFAULT_NAMED), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-rpcclienttimeout=<n>", strprintf("Timeout in seconds during HTTP requests, or 0 for no timeout. (default: %d)", DEFAULT_HTTP_CLIENT_TIMEOUT), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-rpcconnect=<ip>", strprintf("Send commands to node running on <ip> (default: %s)", DEFAULT_RPCCONNECT), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-rpccookiefile=<loc>", _("Location of the auth cookie. Relative paths will be prefixed by a net-specific datadir location. (default: data dir)"), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-rpcpassword=<pw>", "Password for JSON-RPC connections", false, OptionsCategory::OPTIONS); + gArgs.AddArg("-rpcport=<port>", strprintf("Connect to JSON-RPC on <port> (default: %u or testnet: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort()), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-rpcuser=<user>", "Username for JSON-RPC connections", false, OptionsCategory::OPTIONS); + gArgs.AddArg("-rpcwait", "Wait for RPC server to start", false, OptionsCategory::OPTIONS); + gArgs.AddArg("-rpcwallet=<walletname>", "Send RPC for non-default wallet on RPC server (needs to exactly match corresponding -wallet option passed to bitcoind)", false, OptionsCategory::OPTIONS); + gArgs.AddArg("-stdin", "Read extra arguments from standard input, one per line until EOF/Ctrl-D (recommended for sensitive information such as passphrases). When combined with -stdinrpcpass, the first line from standard input is used for the RPC password.", false, OptionsCategory::OPTIONS); + gArgs.AddArg("-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."), false, OptionsCategory::OPTIONS); + + // Hidden + gArgs.AddArg("-h", "", false, OptionsCategory::HIDDEN); + gArgs.AddArg("-help", "", false, OptionsCategory::HIDDEN); } ////////////////////////////////////////////////////////////////////////////// @@ -82,17 +84,22 @@ static int AppInitRPC(int argc, char* argv[]) // // Parameters // - gArgs.ParseParameters(argc, argv); + SetupCliArgs(); + std::string error; + if (!gArgs.ParseParameters(argc, argv, error)) { + fprintf(stderr, "Error parsing command line arguments: %s\n", error.c_str()); + return EXIT_FAILURE; + } if (argc < 2 || HelpRequested(gArgs) || gArgs.IsArgSet("-version")) { - std::string strUsage = strprintf(_("%s RPC client version"), _(PACKAGE_NAME)) + " " + FormatFullVersion() + "\n"; + std::string strUsage = strprintf("%s RPC client version", PACKAGE_NAME) + " " + FormatFullVersion() + "\n"; if (!gArgs.IsArgSet("-version")) { - strUsage += "\n" + _("Usage:") + "\n" + - " bitcoin-cli [options] <command> [params] " + strprintf(_("Send command to %s"), _(PACKAGE_NAME)) + "\n" + - " bitcoin-cli [options] -named <command> [name=value] ... " + strprintf(_("Send command to %s (with named arguments)"), _(PACKAGE_NAME)) + "\n" + - " bitcoin-cli [options] help " + _("List commands") + "\n" + - " bitcoin-cli [options] help <command> " + _("Get help for a command") + "\n"; + strUsage += "\nUsage:\n" + " bitcoin-cli [options] <command> [params] " + strprintf("Send command to %s", PACKAGE_NAME) + "\n" + + " bitcoin-cli [options] -named <command> [name=value] ... " + strprintf("Send command to %s (with named arguments)", PACKAGE_NAME) + "\n" + + " bitcoin-cli [options] help List commands\n" + + " bitcoin-cli [options] help <command> Get help for a command\n"; - strUsage += "\n" + HelpMessageCli(); + strUsage += "\n" + gArgs.GetHelpMessage(); } fprintf(stdout, "%s", strUsage.c_str()); @@ -106,10 +113,8 @@ static int AppInitRPC(int argc, char* argv[]) 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()); + if (!gArgs.ReadConfigFiles(error, true)) { + fprintf(stderr, "Error reading configuration file: %s\n", error.c_str()); return EXIT_FAILURE; } // Check for -testnet or -regtest parameter (BaseParams() calls are only valid after this clause) @@ -366,7 +371,7 @@ static UniValue CallRPC(BaseRequestHandler *rh, const std::string& strMethod, co } else if (response.status == HTTP_UNAUTHORIZED) { if (failedToGetAuthCookie) { 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)"), + "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 { throw std::runtime_error("Authorization failed: Incorrect rpcuser or rpcpassword"); diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index 07ad09ea7b..3fb505d739 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -31,6 +31,45 @@ static bool fCreateBlank; static std::map<std::string,UniValue> registers; static const int CONTINUE_EXECUTION=-1; +static void SetupBitcoinTxArgs() +{ + gArgs.AddArg("-?", "This help message", false, OptionsCategory::OPTIONS); + gArgs.AddArg("-create", "Create new, empty TX.", false, OptionsCategory::OPTIONS); + gArgs.AddArg("-json", "Select JSON output", false, OptionsCategory::OPTIONS); + gArgs.AddArg("-txid", "Output only the hex-encoded transaction id of the resultant transaction.", false, OptionsCategory::OPTIONS); + SetupChainParamsBaseOptions(); + + gArgs.AddArg("delin=N", "Delete input N from TX", false, OptionsCategory::COMMANDS); + gArgs.AddArg("delout=N", "Delete output N from TX", false, OptionsCategory::COMMANDS); + gArgs.AddArg("in=TXID:VOUT(:SEQUENCE_NUMBER)", "Add input to TX", false, OptionsCategory::COMMANDS); + gArgs.AddArg("locktime=N", "Set TX lock time to N", false, OptionsCategory::COMMANDS); + gArgs.AddArg("nversion=N", "Set TX version to N", false, OptionsCategory::COMMANDS); + gArgs.AddArg("outaddr=VALUE:ADDRESS", "Add address-based output to TX", false, OptionsCategory::COMMANDS); + gArgs.AddArg("outdata=[VALUE:]DATA", "Add data-based output to TX", false, OptionsCategory::COMMANDS); + gArgs.AddArg("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 \"W\" flag to produce a pay-to-witness-script-hash output. " + "Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash.", false, OptionsCategory::COMMANDS); + gArgs.AddArg("outpubkey=VALUE:PUBKEY[:FLAGS]", "Add pay-to-pubkey output to TX. " + "Optionally add the \"W\" flag to produce a pay-to-witness-pubkey-hash output. " + "Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash.", false, OptionsCategory::COMMANDS); + gArgs.AddArg("outscript=VALUE:SCRIPT[:FLAGS]", "Add raw script output to TX. " + "Optionally add the \"W\" flag to produce a pay-to-witness-script-hash output. " + "Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash.", false, OptionsCategory::COMMANDS); + gArgs.AddArg("replaceable(=N)", "Set RBF opt-in sequence number for input N (if not provided, opt-in all available inputs)", false, OptionsCategory::COMMANDS); + gArgs.AddArg("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.", false, OptionsCategory::COMMANDS); + + gArgs.AddArg("load=NAME:FILENAME", "Load JSON file FILENAME into register NAME", false, OptionsCategory::REGISTER_COMMANDS); + gArgs.AddArg("set=NAME:JSON-STRING", "Set register NAME to given JSON-STRING", false, OptionsCategory::REGISTER_COMMANDS); + + // Hidden + gArgs.AddArg("-h", "", false, OptionsCategory::HIDDEN); + gArgs.AddArg("-help", "", false, OptionsCategory::HIDDEN); +} + // // 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. @@ -40,7 +79,12 @@ static int AppInitRawTx(int argc, char* argv[]) // // Parameters // - gArgs.ParseParameters(argc, argv); + SetupBitcoinTxArgs(); + std::string error; + if (!gArgs.ParseParameters(argc, argv, error)) { + fprintf(stderr, "Error parsing command line arguments: %s\n", error.c_str()); + return EXIT_FAILURE; + } // Check for -testnet or -regtest parameter (Params() calls are only valid after this clause) try { @@ -54,53 +98,15 @@ static int AppInitRawTx(int argc, char* argv[]) 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] <hex-tx> [commands] " + _("Update hex-encoded bitcoin transaction") + "\n" + - " bitcoin-tx [options] -create [commands] " + _("Create hex-encoded bitcoin transaction") + "\n" + + std::string strUsage = strprintf("%s bitcoin-tx utility version", PACKAGE_NAME) + " " + FormatFullVersion() + "\n\n" + + "Usage:\n" + " bitcoin-tx [options] <hex-tx> [commands] Update hex-encoded bitcoin transaction\n" + + " bitcoin-tx [options] -create [commands] Create hex-encoded bitcoin transaction\n" + "\n"; + strUsage += gArgs.GetHelpMessage(); 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("outdata=[VALUE:]DATA", _("Add data-based output to TX")); - 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 \"W\" flag to produce a pay-to-witness-script-hash output") + ". " + - _("Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash.")); - strUsage += HelpMessageOpt("outpubkey=VALUE:PUBKEY[:FLAGS]", _("Add pay-to-pubkey output to TX") + ". " + - _("Optionally add the \"W\" flag to produce a pay-to-witness-pubkey-hash output") + ". " + - _("Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash.")); - strUsage += HelpMessageOpt("outscript=VALUE:SCRIPT[:FLAGS]", _("Add raw script output to TX") + ". " + - _("Optionally add the \"W\" flag to produce a pay-to-witness-script-hash output") + ". " + - _("Optionally add the \"S\" flag to wrap the output in a pay-to-script-hash.")); - strUsage += HelpMessageOpt("replaceable(=N)", _("Set RBF opt-in sequence number for input N (if not provided, opt-in all available inputs)")); - 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; @@ -548,7 +554,7 @@ static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr) // mergedTx will end up with all the signatures; it // starts as a clone of the raw tx: CMutableTransaction mergedTx{tx}; - const CTransaction txv{tx}; + const CMutableTransaction txv{tx}; CCoinsView viewDummy; CCoinsViewCache view(&viewDummy); diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index 69de1a1666..a9b952e5a4 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -61,11 +61,19 @@ static bool AppInit(int argc, char* argv[]) // Parameters // // If Qt is used, parameters/bitcoin.conf are parsed in qt/bitcoin.cpp's main() - gArgs.ParseParameters(argc, argv); + SetupServerArgs(); +#if HAVE_DECL_DAEMON + gArgs.AddArg("-daemon", "Run in the background as a daemon and accept commands", false, OptionsCategory::OPTIONS); +#endif + std::string error; + if (!gArgs.ParseParameters(argc, argv, error)) { + fprintf(stderr, "Error parsing command line arguments: %s\n", error.c_str()); + return false; + } // Process help and version before taking care about datadir if (HelpRequested(gArgs) || gArgs.IsArgSet("-version")) { - std::string strUsage = strprintf(_("%s Daemon"), _(PACKAGE_NAME)) + " " + _("version") + " " + FormatFullVersion() + "\n"; + std::string strUsage = strprintf("%s Daemon", PACKAGE_NAME) + " version " + FormatFullVersion() + "\n"; if (gArgs.IsArgSet("-version")) { @@ -73,10 +81,10 @@ static bool AppInit(int argc, char* argv[]) } else { - strUsage += "\n" + _("Usage:") + "\n" + - " bitcoind [options] " + strprintf(_("Start %s Daemon"), _(PACKAGE_NAME)) + "\n"; + strUsage += "\nUsage:\n" + " bitcoind [options] " + strprintf("Start %s Daemon", PACKAGE_NAME) + "\n"; - strUsage += "\n" + HelpMessage(HelpMessageMode::BITCOIND); + strUsage += "\n" + gArgs.GetHelpMessage(); } fprintf(stdout, "%s", strUsage.c_str()); @@ -90,11 +98,8 @@ static bool AppInit(int argc, char* argv[]) 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()); + if (!gArgs.ReadConfigFiles(error)) { + fprintf(stderr, "Error reading configuration file: %s\n", error.c_str()); return false; } // Check for -testnet or -regtest parameter (Params() calls are only valid after this clause) diff --git a/src/bloom.cpp b/src/bloom.cpp index f07b5b6066..f8e28edded 100644 --- a/src/bloom.cpp +++ b/src/bloom.cpp @@ -245,6 +245,14 @@ static inline uint32_t RollingBloomHash(unsigned int nHashNum, uint32_t nTweak, return MurmurHash3(nHashNum * 0xFBA4C795 + nTweak, vDataToHash); } + +// A replacement for x % n. This assumes that x and n are 32bit integers, and x is a uniformly random distributed 32bit value +// which should be the case for a good hash. +// See https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction/ +static inline uint32_t FastMod(uint32_t x, size_t n) { + return ((uint64_t)x * (uint64_t)n) >> 32; +} + void CRollingBloomFilter::insert(const std::vector<unsigned char>& vKey) { if (nEntriesThisGeneration == nEntriesPerGeneration) { @@ -268,7 +276,8 @@ void CRollingBloomFilter::insert(const std::vector<unsigned char>& vKey) for (int n = 0; n < nHashFuncs; n++) { uint32_t h = RollingBloomHash(n, nTweak, vKey); int bit = h & 0x3F; - uint32_t pos = (h >> 6) % data.size(); + /* FastMod works with the upper bits of h, so it is safe to ignore that the lower bits of h are already used for bit. */ + uint32_t pos = FastMod(h, data.size()); /* The lowest bit of pos is ignored, and set to zero for the first bit, and to one for the second. */ data[pos & ~1] = (data[pos & ~1] & ~(((uint64_t)1) << bit)) | ((uint64_t)(nGeneration & 1)) << bit; data[pos | 1] = (data[pos | 1] & ~(((uint64_t)1) << bit)) | ((uint64_t)(nGeneration >> 1)) << bit; @@ -286,7 +295,7 @@ bool CRollingBloomFilter::contains(const std::vector<unsigned char>& vKey) const for (int n = 0; n < nHashFuncs; n++) { uint32_t h = RollingBloomHash(n, nTweak, vKey); int bit = h & 0x3F; - uint32_t pos = (h >> 6) % data.size(); + uint32_t pos = FastMod(h, data.size()); /* If the relevant bit is not set in either data[pos & ~1] or data[pos | 1], the filter does not contain vKey */ if (!(((data[pos & ~1] | data[pos | 1]) >> bit) & 1)) { return false; diff --git a/src/chain.h b/src/chain.h index 757840bb23..8e6ac8d821 100644 --- a/src/chain.h +++ b/src/chain.h @@ -7,8 +7,8 @@ #define BITCOIN_CHAIN_H #include <arith_uint256.h> +#include <consensus/params.h> #include <primitives/block.h> -#include <pow.h> #include <tinyformat.h> #include <uint256.h> diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp index 3ef9c2cfe5..787d8d8f6a 100644 --- a/src/chainparamsbase.cpp +++ b/src/chainparamsbase.cpp @@ -14,14 +14,11 @@ const std::string CBaseChainParams::MAIN = "main"; const std::string CBaseChainParams::TESTNET = "test"; const std::string CBaseChainParams::REGTEST = "regtest"; -void AppendParamsHelpMessages(std::string& strUsage, bool debugHelp) +void SetupChainParamsBaseOptions() { - strUsage += HelpMessageGroup(_("Chain selection options:")); - if (debugHelp) { - strUsage += HelpMessageOpt("-regtest", "Enter regression test mode, which uses a special chain in which blocks can be solved instantly. " - "This is intended for regression testing tools and app development."); - } - strUsage += HelpMessageOpt("-testnet", _("Use the test chain")); + gArgs.AddArg("-regtest", "Enter regression test mode, which uses a special chain in which blocks can be solved instantly. " + "This is intended for regression testing tools and app development.", true, OptionsCategory::CHAINPARAMS); + gArgs.AddArg("-testnet", "Use the test chain", false, OptionsCategory::CHAINPARAMS); } static std::unique_ptr<CBaseChainParams> globalChainBaseParams; diff --git a/src/chainparamsbase.h b/src/chainparamsbase.h index 5b11f36770..9f8bbafcd5 100644 --- a/src/chainparamsbase.h +++ b/src/chainparamsbase.h @@ -40,10 +40,9 @@ private: std::unique_ptr<CBaseChainParams> CreateBaseChainParams(const std::string& chain); /** - * Append the help messages for the chainparams options to the - * parameter string. + *Set the arguments for chainparams */ -void AppendParamsHelpMessages(std::string& strUsage, bool debugHelp=true); +void SetupChainParamsBaseOptions(); /** * Return the currently selected parameters. This won't change after app diff --git a/src/index/txindex.cpp b/src/index/txindex.cpp index 5b6e0f9980..3ff16b7664 100644 --- a/src/index/txindex.cpp +++ b/src/index/txindex.cpp @@ -170,7 +170,7 @@ void TxIndex::BlockConnected(const std::shared_ptr<const CBlock>& block, const C } } else { // Ensure block connects to an ancestor of the current best block. This should be the case - // most of the time, but may not be immediately after the the sync thread catches up and sets + // most of the time, but may not be immediately after the sync thread catches up and sets // m_synced. Consider the case where there is a reorg and the blocks on the stale branch are // in the ValidationInterface queue backlog even after the sync thread has caught up to the // new chain tip. In this unlikely event, log a warning and let the queue clear. @@ -212,7 +212,7 @@ void TxIndex::ChainStateFlushed(const CBlockLocator& locator) } // This checks that ChainStateFlushed callbacks are received after BlockConnected. The check may fail - // immediately after the the sync thread catches up and sets m_synced. Consider the case where + // immediately after the sync thread catches up and sets m_synced. Consider the case where // there is a reorg and the blocks on the stale branch are in the ValidationInterface queue // backlog even after the sync thread has caught up to the new chain tip. In this unlikely // event, log a warning and let the queue clear. diff --git a/src/init.cpp b/src/init.cpp index 7d8f462ef1..b4e2eec0d2 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -76,7 +76,7 @@ std::unique_ptr<PeerLogicValidation> peerLogic; class DummyWalletInit : public WalletInitInterface { public: - std::string GetHelpString(bool showDebug) const override {return std::string{};} + void AddWalletOptions() const override {} bool ParameterInteraction() const override {return true;} void RegisterRPC(CRPCTable &) const override {} bool Verify() const override {return true;} @@ -341,197 +341,181 @@ static void OnRPCStopped() LogPrint(BCLog::RPC, "RPC stopped.\n"); } -std::string HelpMessage(HelpMessageMode mode) +void SetupServerArgs() { const auto defaultBaseParams = CreateBaseChainParams(CBaseChainParams::MAIN); const auto testnetBaseParams = CreateBaseChainParams(CBaseChainParams::TESTNET); const auto defaultChainParams = CreateChainParams(CBaseChainParams::MAIN); const auto testnetChainParams = CreateChainParams(CBaseChainParams::TESTNET); - const bool showDebug = gArgs.GetBoolArg("-help-debug", false); + // Set all of the args and their help // When adding new options to the categories, please keep and ensure alphabetical ordering. - // Do not translate _(...) -help-debug options, Many technical terms, and only a very small audience, so is unnecessary stress to translators. - std::string strUsage = HelpMessageGroup(_("Options:")); - strUsage += HelpMessageOpt("-?", _("Print this help message and exit")); - strUsage += HelpMessageOpt("-version", _("Print version and exit")); - strUsage += HelpMessageOpt("-alertnotify=<cmd>", _("Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)")); - strUsage +=HelpMessageOpt("-assumevalid=<hex>", strprintf(_("If this block is in the chain assume that it and its ancestors are valid and potentially skip their script verification (0 to verify all, default: %s, testnet: %s)"), defaultChainParams->GetConsensus().defaultAssumeValid.GetHex(), testnetChainParams->GetConsensus().defaultAssumeValid.GetHex())); - strUsage += HelpMessageOpt("-blocksdir=<dir>", _("Specify blocks directory (default: <datadir>/blocks)")); - strUsage += HelpMessageOpt("-blocknotify=<cmd>", _("Execute command when the best block changes (%s in cmd is replaced by block hash)")); - strUsage += HelpMessageOpt("-blockreconstructionextratxn=<n>", strprintf(_("Extra transactions to keep in memory for compact block reconstructions (default: %u)"), DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN)); - if (showDebug) - strUsage += HelpMessageOpt("-blocksonly", strprintf(_("Whether to operate in a blocks only mode (default: %u)"), DEFAULT_BLOCKSONLY)); - strUsage += HelpMessageOpt("-conf=<file>", strprintf(_("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)"), BITCOIN_CONF_FILENAME)); - if (mode == HelpMessageMode::BITCOIND) - { -#if HAVE_DECL_DAEMON - strUsage += HelpMessageOpt("-daemon", _("Run in the background as a daemon and accept commands")); -#endif - } - strUsage += HelpMessageOpt("-datadir=<dir>", _("Specify data directory")); - if (showDebug) { - strUsage += HelpMessageOpt("-dbbatchsize", strprintf("Maximum database write batch size in bytes (default: %u)", nDefaultDbBatchSize)); - } - strUsage += HelpMessageOpt("-dbcache=<n>", strprintf(_("Set database cache size in megabytes (%d to %d, default: %d)"), nMinDbCache, nMaxDbCache, nDefaultDbCache)); - strUsage += HelpMessageOpt("-debuglogfile=<file>", strprintf(_("Specify location of debug log file. Relative paths will be prefixed by a net-specific datadir location. (default: %s)"), DEFAULT_DEBUGLOGFILE)); - if (showDebug) - strUsage += HelpMessageOpt("-feefilter", strprintf("Tell other nodes to filter invs to us by our mempool min fee (default: %u)", DEFAULT_FEEFILTER)); - strUsage += HelpMessageOpt("-loadblock=<file>", _("Imports blocks from external blk000??.dat file on startup")); - strUsage += HelpMessageOpt("-maxmempool=<n>", strprintf(_("Keep the transaction memory pool below <n> megabytes (default: %u)"), DEFAULT_MAX_MEMPOOL_SIZE)); - strUsage += HelpMessageOpt("-maxorphantx=<n>", strprintf(_("Keep at most <n> unconnectable transactions in memory (default: %u)"), DEFAULT_MAX_ORPHAN_TRANSACTIONS)); - strUsage += HelpMessageOpt("-mempoolexpiry=<n>", strprintf(_("Do not keep transactions in the mempool longer than <n> hours (default: %u)"), DEFAULT_MEMPOOL_EXPIRY)); - if (showDebug) { - strUsage += HelpMessageOpt("-minimumchainwork=<hex>", strprintf("Minimum work assumed to exist on a valid chain in hex (default: %s, testnet: %s)", defaultChainParams->GetConsensus().nMinimumChainWork.GetHex(), testnetChainParams->GetConsensus().nMinimumChainWork.GetHex())); - } - strUsage += HelpMessageOpt("-par=<n>", strprintf(_("Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)"), - -GetNumCores(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS)); - strUsage += HelpMessageOpt("-persistmempool", strprintf(_("Whether to save the mempool on shutdown and load on restart (default: %u)"), DEFAULT_PERSIST_MEMPOOL)); + gArgs.AddArg("-?", "Print this help message and exit", false, OptionsCategory::OPTIONS); + gArgs.AddArg("-version", "Print version and exit", false, OptionsCategory::OPTIONS); + gArgs.AddArg("-alertnotify=<cmd>", "Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)", false, OptionsCategory::OPTIONS); + gArgs.AddArg("-assumevalid=<hex>", strprintf("If this block is in the chain assume that it and its ancestors are valid and potentially skip their script verification (0 to verify all, default: %s, testnet: %s)", defaultChainParams->GetConsensus().defaultAssumeValid.GetHex(), testnetChainParams->GetConsensus().defaultAssumeValid.GetHex()), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-blocksdir=<dir>", "Specify blocks directory (default: <datadir>/blocks)", false, OptionsCategory::OPTIONS); + gArgs.AddArg("-blocknotify=<cmd>", "Execute command when the best block changes (%s in cmd is replaced by block hash)", false, OptionsCategory::OPTIONS); + gArgs.AddArg("-blockreconstructionextratxn=<n>", strprintf("Extra transactions to keep in memory for compact block reconstructions (default: %u)", DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-blocksonly", strprintf("Whether to operate in a blocks only mode (default: %u)", DEFAULT_BLOCKSONLY), true, OptionsCategory::OPTIONS); + gArgs.AddArg("-conf=<file>", strprintf("Specify configuration file. Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-datadir=<dir>", "Specify data directory", false, OptionsCategory::OPTIONS); + gArgs.AddArg("-dbbatchsize", strprintf("Maximum database write batch size in bytes (default: %u)", nDefaultDbBatchSize), true, OptionsCategory::OPTIONS); + gArgs.AddArg("-dbcache=<n>", strprintf("Set database cache size in megabytes (%d to %d, default: %d)", nMinDbCache, nMaxDbCache, nDefaultDbCache), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-debuglogfile=<file>", strprintf("Specify location of debug log file. Relative paths will be prefixed by a net-specific datadir location. (default: %s)", DEFAULT_DEBUGLOGFILE), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-feefilter", strprintf("Tell other nodes to filter invs to us by our mempool min fee (default: %u)", DEFAULT_FEEFILTER), true, OptionsCategory::OPTIONS); + gArgs.AddArg("-includeconf=<file>", "Specify additional configuration file, relative to the -datadir path (only useable from configuration file, not command line)", false, OptionsCategory::OPTIONS); + gArgs.AddArg("-loadblock=<file>", "Imports blocks from external blk000??.dat file on startup", false, OptionsCategory::OPTIONS); + gArgs.AddArg("-maxmempool=<n>", strprintf("Keep the transaction memory pool below <n> megabytes (default: %u)", DEFAULT_MAX_MEMPOOL_SIZE), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-maxorphantx=<n>", strprintf("Keep at most <n> unconnectable transactions in memory (default: %u)", DEFAULT_MAX_ORPHAN_TRANSACTIONS), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-mempoolexpiry=<n>", strprintf("Do not keep transactions in the mempool longer than <n> hours (default: %u)", DEFAULT_MEMPOOL_EXPIRY), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-minimumchainwork=<hex>", strprintf("Minimum work assumed to exist on a valid chain in hex (default: %s, testnet: %s)", defaultChainParams->GetConsensus().nMinimumChainWork.GetHex(), testnetChainParams->GetConsensus().nMinimumChainWork.GetHex()), true, OptionsCategory::OPTIONS); + gArgs.AddArg("-par=<n>", strprintf("Set the number of script verification threads (%u to %d, 0 = auto, <0 = leave that many cores free, default: %d)", + -GetNumCores(), MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-persistmempool", strprintf("Whether to save the mempool on shutdown and load on restart (default: %u)", DEFAULT_PERSIST_MEMPOOL), false, OptionsCategory::OPTIONS); #ifndef WIN32 - strUsage += HelpMessageOpt("-pid=<file>", strprintf(_("Specify pid file. Relative paths will be prefixed by a net-specific datadir location. (default: %s)"), BITCOIN_PID_FILENAME)); + gArgs.AddArg("-pid=<file>", strprintf("Specify pid file. Relative paths will be prefixed by a net-specific datadir location. (default: %s)", BITCOIN_PID_FILENAME), false, OptionsCategory::OPTIONS); #endif - strUsage += HelpMessageOpt("-prune=<n>", strprintf(_("Reduce storage requirements by enabling pruning (deleting) of old blocks. This allows the pruneblockchain RPC to be called to delete specific blocks, and enables automatic pruning of old blocks if a target size in MiB is provided. This mode is incompatible with -txindex and -rescan. " + gArgs.AddArg("-prune=<n>", strprintf("Reduce storage requirements by enabling pruning (deleting) of old blocks. This allows the pruneblockchain RPC to be called to delete specific blocks, and enables automatic pruning of old blocks if a target size in MiB is provided. This mode is incompatible with -txindex and -rescan. " "Warning: Reverting this setting requires re-downloading the entire blockchain. " - "(default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, >%u = automatically prune block files to stay under the specified target size in MiB)"), MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024)); - strUsage += HelpMessageOpt("-reindex", _("Rebuild chain state and block index from the blk*.dat files on disk")); - strUsage += HelpMessageOpt("-reindex-chainstate", _("Rebuild chain state from the currently indexed blocks")); + "(default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, >%u = automatically prune block files to stay under the specified target size in MiB)", MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024), false, OptionsCategory::OPTIONS); + gArgs.AddArg("-reindex", "Rebuild chain state and block index from the blk*.dat files on disk", false, OptionsCategory::OPTIONS); + gArgs.AddArg("-reindex-chainstate", "Rebuild chain state from the currently indexed blocks", false, OptionsCategory::OPTIONS); #ifndef WIN32 - strUsage += HelpMessageOpt("-sysperms", _("Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality)")); + gArgs.AddArg("-sysperms", "Create new files with system default permissions, instead of umask 077 (only effective with disabled wallet functionality)", false, OptionsCategory::OPTIONS); #endif - strUsage += HelpMessageOpt("-txindex", strprintf(_("Maintain a full transaction index, used by the getrawtransaction rpc call (default: %u)"), DEFAULT_TXINDEX)); - - strUsage += HelpMessageGroup(_("Connection options:")); - strUsage += HelpMessageOpt("-addnode=<ip>", _("Add a node to connect to and attempt to keep the connection open (see the `addnode` RPC command help for more info)")); - strUsage += HelpMessageOpt("-banscore=<n>", strprintf(_("Threshold for disconnecting misbehaving peers (default: %u)"), DEFAULT_BANSCORE_THRESHOLD)); - strUsage += HelpMessageOpt("-bantime=<n>", strprintf(_("Number of seconds to keep misbehaving peers from reconnecting (default: %u)"), DEFAULT_MISBEHAVING_BANTIME)); - strUsage += HelpMessageOpt("-bind=<addr>", _("Bind to given address and always listen on it. Use [host]:port notation for IPv6")); - strUsage += HelpMessageOpt("-connect=<ip>", _("Connect only to the specified node(s); -connect=0 disables automatic connections (the rules for this peer are the same as for -addnode)")); - strUsage += HelpMessageOpt("-discover", _("Discover own IP addresses (default: 1 when listening and no -externalip or -proxy)")); - strUsage += HelpMessageOpt("-dns", _("Allow DNS lookups for -addnode, -seednode and -connect") + " " + strprintf(_("(default: %u)"), DEFAULT_NAME_LOOKUP)); - strUsage += HelpMessageOpt("-dnsseed", _("Query for peer addresses via DNS lookup, if low on addresses (default: 1 unless -connect used)")); - strUsage += HelpMessageOpt("-externalip=<ip>", _("Specify your own public address")); - strUsage += HelpMessageOpt("-forcednsseed", strprintf(_("Always query for peer addresses via DNS lookup (default: %u)"), DEFAULT_FORCEDNSSEED)); - strUsage += HelpMessageOpt("-listen", _("Accept connections from outside (default: 1 if no -proxy or -connect)")); - strUsage += HelpMessageOpt("-listenonion", strprintf(_("Automatically create Tor hidden service (default: %d)"), DEFAULT_LISTEN_ONION)); - strUsage += HelpMessageOpt("-maxconnections=<n>", strprintf(_("Maintain at most <n> connections to peers (default: %u)"), DEFAULT_MAX_PEER_CONNECTIONS)); - strUsage += HelpMessageOpt("-maxreceivebuffer=<n>", strprintf(_("Maximum per-connection receive buffer, <n>*1000 bytes (default: %u)"), DEFAULT_MAXRECEIVEBUFFER)); - strUsage += HelpMessageOpt("-maxsendbuffer=<n>", strprintf(_("Maximum per-connection send buffer, <n>*1000 bytes (default: %u)"), DEFAULT_MAXSENDBUFFER)); - strUsage += HelpMessageOpt("-maxtimeadjustment", strprintf(_("Maximum allowed median peer time offset adjustment. Local perspective of time may be influenced by peers forward or backward by this amount. (default: %u seconds)"), DEFAULT_MAX_TIME_ADJUSTMENT)); - strUsage += HelpMessageOpt("-maxuploadtarget=<n>", strprintf(_("Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = no limit (default: %d)"), DEFAULT_MAX_UPLOAD_TARGET)); - strUsage += HelpMessageOpt("-onion=<ip:port>", strprintf(_("Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: %s)"), "-proxy")); - strUsage += HelpMessageOpt("-onlynet=<net>", _("Only connect to nodes in network <net> (ipv4, ipv6 or onion)")); - strUsage += HelpMessageOpt("-peerbloomfilters", strprintf(_("Support filtering of blocks and transaction with bloom filters (default: %u)"), DEFAULT_PEERBLOOMFILTERS)); - strUsage += HelpMessageOpt("-permitbaremultisig", strprintf(_("Relay non-P2SH multisig (default: %u)"), DEFAULT_PERMIT_BAREMULTISIG)); - strUsage += HelpMessageOpt("-port=<port>", strprintf(_("Listen for connections on <port> (default: %u or testnet: %u)"), defaultChainParams->GetDefaultPort(), testnetChainParams->GetDefaultPort())); - strUsage += HelpMessageOpt("-proxy=<ip:port>", _("Connect through SOCKS5 proxy")); - strUsage += HelpMessageOpt("-proxyrandomize", strprintf(_("Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)"), DEFAULT_PROXYRANDOMIZE)); - strUsage += HelpMessageOpt("-seednode=<ip>", _("Connect to a node to retrieve peer addresses, and disconnect")); - strUsage += HelpMessageOpt("-timeout=<n>", strprintf(_("Specify connection timeout in milliseconds (minimum: 1, default: %d)"), DEFAULT_CONNECT_TIMEOUT)); - strUsage += HelpMessageOpt("-torcontrol=<ip>:<port>", strprintf(_("Tor control port to use if onion listening enabled (default: %s)"), DEFAULT_TOR_CONTROL)); - strUsage += HelpMessageOpt("-torpassword=<pass>", _("Tor control port password (default: empty)")); + gArgs.AddArg("-txindex", strprintf("Maintain a full transaction index, used by the getrawtransaction rpc call (default: %u)", DEFAULT_TXINDEX), false, OptionsCategory::OPTIONS); + + gArgs.AddArg("-addnode=<ip>", "Add a node to connect to and attempt to keep the connection open (see the `addnode` RPC command help for more info)", false, OptionsCategory::CONNECTION); + gArgs.AddArg("-banscore=<n>", strprintf("Threshold for disconnecting misbehaving peers (default: %u)", DEFAULT_BANSCORE_THRESHOLD), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-bantime=<n>", strprintf("Number of seconds to keep misbehaving peers from reconnecting (default: %u)", DEFAULT_MISBEHAVING_BANTIME), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-bind=<addr>", "Bind to given address and always listen on it. Use [host]:port notation for IPv6", false, OptionsCategory::CONNECTION); + gArgs.AddArg("-connect=<ip>", "Connect only to the specified node(s); -connect=0 disables automatic connections (the rules for this peer are the same as for -addnode)", false, OptionsCategory::CONNECTION); + gArgs.AddArg("-discover", "Discover own IP addresses (default: 1 when listening and no -externalip or -proxy)", false, OptionsCategory::CONNECTION); + gArgs.AddArg("-dns", strprintf("Allow DNS lookups for -addnode, -seednode and -connect (default: %u)", DEFAULT_NAME_LOOKUP), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-dnsseed", "Query for peer addresses via DNS lookup, if low on addresses (default: 1 unless -connect used)", false, OptionsCategory::CONNECTION); + gArgs.AddArg("-enablebip61", strprintf("Send reject messages per BIP61 (default: %u)", DEFAULT_ENABLE_BIP61), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-externalip=<ip>", "Specify your own public address", false, OptionsCategory::CONNECTION); + gArgs.AddArg("-forcednsseed", strprintf("Always query for peer addresses via DNS lookup (default: %u)", DEFAULT_FORCEDNSSEED), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-listen", "Accept connections from outside (default: 1 if no -proxy or -connect)", false, OptionsCategory::CONNECTION); + gArgs.AddArg("-listenonion", strprintf("Automatically create Tor hidden service (default: %d)", DEFAULT_LISTEN_ONION), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-maxconnections=<n>", strprintf("Maintain at most <n> connections to peers (default: %u)", DEFAULT_MAX_PEER_CONNECTIONS), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-maxreceivebuffer=<n>", strprintf("Maximum per-connection receive buffer, <n>*1000 bytes (default: %u)", DEFAULT_MAXRECEIVEBUFFER), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-maxsendbuffer=<n>", strprintf("Maximum per-connection send buffer, <n>*1000 bytes (default: %u)", DEFAULT_MAXSENDBUFFER), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-maxtimeadjustment", strprintf("Maximum allowed median peer time offset adjustment. Local perspective of time may be influenced by peers forward or backward by this amount. (default: %u seconds)", DEFAULT_MAX_TIME_ADJUSTMENT), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-maxuploadtarget=<n>", strprintf("Tries to keep outbound traffic under the given target (in MiB per 24h), 0 = no limit (default: %d)", DEFAULT_MAX_UPLOAD_TARGET), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-onion=<ip:port>", "Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: -proxy)", false, OptionsCategory::CONNECTION); + gArgs.AddArg("-onlynet=<net>", "Only connect to nodes in network <net> (ipv4, ipv6 or onion)", false, OptionsCategory::CONNECTION); + gArgs.AddArg("-peerbloomfilters", strprintf("Support filtering of blocks and transaction with bloom filters (default: %u)", DEFAULT_PEERBLOOMFILTERS), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-permitbaremultisig", strprintf("Relay non-P2SH multisig (default: %u)", DEFAULT_PERMIT_BAREMULTISIG), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-port=<port>", strprintf("Listen for connections on <port> (default: %u or testnet: %u)", defaultChainParams->GetDefaultPort(), testnetChainParams->GetDefaultPort()), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-proxy=<ip:port>", "Connect through SOCKS5 proxy", false, OptionsCategory::CONNECTION); + gArgs.AddArg("-proxyrandomize", strprintf("Randomize credentials for every proxy connection. This enables Tor stream isolation (default: %u)", DEFAULT_PROXYRANDOMIZE), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-seednode=<ip>", "Connect to a node to retrieve peer addresses, and disconnect", false, OptionsCategory::CONNECTION); + gArgs.AddArg("-timeout=<n>", strprintf("Specify connection timeout in milliseconds (minimum: 1, default: %d)", DEFAULT_CONNECT_TIMEOUT), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-torcontrol=<ip>:<port>", strprintf("Tor control port to use if onion listening enabled (default: %s)", DEFAULT_TOR_CONTROL), false, OptionsCategory::CONNECTION); + gArgs.AddArg("-torpassword=<pass>", "Tor control port password (default: empty)", false, OptionsCategory::CONNECTION); #ifdef USE_UPNP #if USE_UPNP - strUsage += HelpMessageOpt("-upnp", _("Use UPnP to map the listening port (default: 1 when listening and no -proxy)")); + gArgs.AddArg("-upnp", "Use UPnP to map the listening port (default: 1 when listening and no -proxy)", false, OptionsCategory::CONNECTION); #else - strUsage += HelpMessageOpt("-upnp", strprintf(_("Use UPnP to map the listening port (default: %u)"), 0)); + gArgs.AddArg("-upnp", strprintf("Use UPnP to map the listening port (default: %u)", 0), false, OptionsCategory::CONNECTION); #endif #endif - strUsage += HelpMessageOpt("-whitebind=<addr>", _("Bind to given address and whitelist peers connecting to it. Use [host]:port notation for IPv6")); - strUsage += HelpMessageOpt("-whitelist=<IP address or network>", _("Whitelist peers connecting from the given IP address (e.g. 1.2.3.4) or CIDR notated network (e.g. 1.2.3.0/24). Can be specified multiple times.") + - " " + _("Whitelisted peers cannot be DoS banned and their transactions are always relayed, even if they are already in the mempool, useful e.g. for a gateway")); + gArgs.AddArg("-whitebind=<addr>", "Bind to given address and whitelist peers connecting to it. Use [host]:port notation for IPv6", false, OptionsCategory::CONNECTION); + gArgs.AddArg("-whitelist=<IP address or network>", "Whitelist peers connecting from the given IP address (e.g. 1.2.3.4) or CIDR notated network (e.g. 1.2.3.0/24). Can be specified multiple times." + " Whitelisted peers cannot be DoS banned and their transactions are always relayed, even if they are already in the mempool, useful e.g. for a gateway", false, OptionsCategory::CONNECTION); - strUsage += g_wallet_init_interface.GetHelpString(showDebug); + g_wallet_init_interface.AddWalletOptions(); #if ENABLE_ZMQ - strUsage += HelpMessageGroup(_("ZeroMQ notification options:")); - strUsage += HelpMessageOpt("-zmqpubhashblock=<address>", _("Enable publish hash block in <address>")); - strUsage += HelpMessageOpt("-zmqpubhashtx=<address>", _("Enable publish hash transaction in <address>")); - strUsage += HelpMessageOpt("-zmqpubrawblock=<address>", _("Enable publish raw block in <address>")); - strUsage += HelpMessageOpt("-zmqpubrawtx=<address>", _("Enable publish raw transaction in <address>")); + gArgs.AddArg("-zmqpubhashblock=<address>", "Enable publish hash block in <address>", false, OptionsCategory::ZMQ); + gArgs.AddArg("-zmqpubhashtx=<address>", "Enable publish hash transaction in <address>", false, OptionsCategory::ZMQ); + gArgs.AddArg("-zmqpubrawblock=<address>", "Enable publish raw block in <address>", false, OptionsCategory::ZMQ); + gArgs.AddArg("-zmqpubrawtx=<address>", "Enable publish raw transaction in <address>", false, OptionsCategory::ZMQ); #endif - strUsage += HelpMessageGroup(_("Debugging/Testing options:")); - if (showDebug) { - strUsage += HelpMessageOpt("-checkblocks=<n>", strprintf(_("How many blocks to check at startup (default: %u, 0 = all)"), DEFAULT_CHECKBLOCKS)); - strUsage += HelpMessageOpt("-checklevel=<n>", strprintf(_("How thorough the block verification of -checkblocks is (0-4, default: %u)"), DEFAULT_CHECKLEVEL)); - strUsage += HelpMessageOpt("-checkblockindex", strprintf("Do a full consistency check for mapBlockIndex, setBlockIndexCandidates, chainActive and mapBlocksUnlinked occasionally. (default: %u)", defaultChainParams->DefaultConsistencyChecks())); - strUsage += HelpMessageOpt("-checkmempool=<n>", strprintf("Run checks every <n> transactions (default: %u)", defaultChainParams->DefaultConsistencyChecks())); - strUsage += HelpMessageOpt("-checkpoints", strprintf("Disable expensive verification for known chain history (default: %u)", DEFAULT_CHECKPOINTS_ENABLED)); - strUsage += HelpMessageOpt("-deprecatedrpc=<method>", "Allows deprecated RPC method(s) to be used"); - strUsage += HelpMessageOpt("-dropmessagestest=<n>", "Randomly drop 1 of every <n> network messages"); - strUsage += HelpMessageOpt("-stopafterblockimport", strprintf("Stop running after importing blocks from disk (default: %u)", DEFAULT_STOPAFTERBLOCKIMPORT)); - strUsage += HelpMessageOpt("-stopatheight", strprintf("Stop running after reaching the given height in the main chain (default: %u)", DEFAULT_STOPATHEIGHT)); - - strUsage += HelpMessageOpt("-limitancestorcount=<n>", strprintf("Do not accept transactions if number of in-mempool ancestors is <n> or more (default: %u)", DEFAULT_ANCESTOR_LIMIT)); - strUsage += HelpMessageOpt("-limitancestorsize=<n>", strprintf("Do not accept transactions whose size with all in-mempool ancestors exceeds <n> kilobytes (default: %u)", DEFAULT_ANCESTOR_SIZE_LIMIT)); - strUsage += HelpMessageOpt("-limitdescendantcount=<n>", strprintf("Do not accept transactions if any ancestor would have <n> or more in-mempool descendants (default: %u)", DEFAULT_DESCENDANT_LIMIT)); - strUsage += HelpMessageOpt("-limitdescendantsize=<n>", strprintf("Do not accept transactions if any ancestor would have more than <n> kilobytes of in-mempool descendants (default: %u).", DEFAULT_DESCENDANT_SIZE_LIMIT)); - strUsage += HelpMessageOpt("-vbparams=deployment:start:end", "Use given start/end times for specified version bits deployment (regtest-only)"); - strUsage += HelpMessageOpt("-addrmantest", "Allows to test address relay on localhost"); - } - strUsage += HelpMessageOpt("-debug=<category>", strprintf(_("Output debugging information (default: %u, supplying <category> is optional)"), 0) + ". " + - _("If <category> is not supplied or if <category> = 1, output all debugging information.") + " " + _("<category> can be:") + " " + ListLogCategories() + "."); - strUsage += HelpMessageOpt("-debugexclude=<category>", strprintf(_("Exclude debugging information for a category. Can be used in conjunction with -debug=1 to output debug logs for all categories except one or more specified categories."))); - strUsage += HelpMessageOpt("-help-debug", _("Show all debugging options (usage: --help -help-debug)")); - strUsage += HelpMessageOpt("-logips", strprintf(_("Include IP addresses in debug output (default: %u)"), DEFAULT_LOGIPS)); - strUsage += HelpMessageOpt("-logtimestamps", strprintf(_("Prepend debug output with timestamp (default: %u)"), DEFAULT_LOGTIMESTAMPS)); - if (showDebug) - { - strUsage += HelpMessageOpt("-logtimemicros", strprintf("Add microsecond precision to debug timestamps (default: %u)", DEFAULT_LOGTIMEMICROS)); - strUsage += HelpMessageOpt("-mocktime=<n>", "Replace actual time with <n> seconds since epoch (default: 0)"); - strUsage += HelpMessageOpt("-maxsigcachesize=<n>", strprintf("Limit sum of signature cache and script execution cache sizes to <n> MiB (default: %u)", DEFAULT_MAX_SIG_CACHE_SIZE)); - strUsage += HelpMessageOpt("-maxtipage=<n>", strprintf("Maximum tip age in seconds to consider node in initial block download (default: %u)", DEFAULT_MAX_TIP_AGE)); - } - strUsage += HelpMessageOpt("-maxtxfee=<amt>", strprintf(_("Maximum total fees (in %s) to use in a single wallet transaction or raw transaction; setting this too low may abort large transactions (default: %s)"), - CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MAXFEE))); - if (showDebug) - { - strUsage += HelpMessageOpt("-printpriority", strprintf("Log transaction fee per kB when mining blocks (default: %u)", DEFAULT_PRINTPRIORITY)); - } - strUsage += HelpMessageOpt("-printtoconsole", _("Send trace/debug info to console instead of debug.log file")); - strUsage += HelpMessageOpt("-shrinkdebugfile", _("Shrink debug.log file on client startup (default: 1 when no -debug)")); - strUsage += HelpMessageOpt("-uacomment=<cmt>", _("Append comment to the user agent string")); - - AppendParamsHelpMessages(strUsage, showDebug); - - strUsage += HelpMessageGroup(_("Node relay options:")); - if (showDebug) { - strUsage += HelpMessageOpt("-acceptnonstdtxn", strprintf("Relay and mine \"non-standard\" transactions (%sdefault: %u)", "testnet/regtest only; ", !testnetChainParams->RequireStandard())); - strUsage += HelpMessageOpt("-incrementalrelayfee=<amt>", strprintf("Fee rate (in %s/kB) used to define cost of relay, used for mempool limiting and BIP 125 replacement. (default: %s)", CURRENCY_UNIT, FormatMoney(DEFAULT_INCREMENTAL_RELAY_FEE))); - strUsage += HelpMessageOpt("-dustrelayfee=<amt>", strprintf("Fee rate (in %s/kB) used to defined dust, the value of an output such that it will cost more than its value in fees at this fee rate to spend it. (default: %s)", CURRENCY_UNIT, FormatMoney(DUST_RELAY_TX_FEE))); - } - strUsage += HelpMessageOpt("-bytespersigop", strprintf(_("Equivalent bytes per sigop in transactions for relay and mining (default: %u)"), DEFAULT_BYTES_PER_SIGOP)); - strUsage += HelpMessageOpt("-datacarrier", strprintf(_("Relay and mine data carrier transactions (default: %u)"), DEFAULT_ACCEPT_DATACARRIER)); - strUsage += HelpMessageOpt("-datacarriersize", strprintf(_("Maximum size of data in data carrier transactions we relay and mine (default: %u)"), MAX_OP_RETURN_RELAY)); - strUsage += HelpMessageOpt("-mempoolreplacement", strprintf(_("Enable transaction replacement in the memory pool (default: %u)"), DEFAULT_ENABLE_REPLACEMENT)); - strUsage += HelpMessageOpt("-minrelaytxfee=<amt>", strprintf(_("Fees (in %s/kB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s)"), - CURRENCY_UNIT, FormatMoney(DEFAULT_MIN_RELAY_TX_FEE))); - strUsage += HelpMessageOpt("-whitelistforcerelay", strprintf(_("Force relay of transactions from whitelisted peers even if they violate local relay policy (default: %d)"), DEFAULT_WHITELISTFORCERELAY)); - strUsage += HelpMessageOpt("-whitelistrelay", strprintf(_("Accept relayed transactions received from whitelisted peers even when not relaying transactions (default: %d)"), DEFAULT_WHITELISTRELAY)); - - strUsage += HelpMessageGroup(_("Block creation options:")); - strUsage += HelpMessageOpt("-blockmaxweight=<n>", strprintf(_("Set maximum BIP141 block weight (default: %d)"), DEFAULT_BLOCK_MAX_WEIGHT)); - strUsage += HelpMessageOpt("-blockmintxfee=<amt>", strprintf(_("Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s)"), CURRENCY_UNIT, FormatMoney(DEFAULT_BLOCK_MIN_TX_FEE))); - if (showDebug) - strUsage += HelpMessageOpt("-blockversion=<n>", "Override block version to test forking scenarios"); - - strUsage += HelpMessageGroup(_("RPC server options:")); - strUsage += HelpMessageOpt("-rest", strprintf(_("Accept public REST requests (default: %u)"), DEFAULT_REST_ENABLE)); - strUsage += HelpMessageOpt("-rpcallowip=<ip>", _("Allow JSON-RPC connections from specified source. Valid for <ip> are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). This option can be specified multiple times")); - strUsage += HelpMessageOpt("-rpcauth=<userpw>", _("Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. The client then connects normally using the rpcuser=<USERNAME>/rpcpassword=<PASSWORD> pair of arguments. This option can be specified multiple times")); - strUsage += HelpMessageOpt("-rpcbind=<addr>[:port]", _("Bind to given address to listen for JSON-RPC connections. This option is ignored unless -rpcallowip is also passed. Port is optional and overrides -rpcport. Use [host]:port notation for IPv6. This option can be specified multiple times (default: 127.0.0.1 and ::1 i.e., localhost, or if -rpcallowip has been specified, 0.0.0.0 and :: i.e., all addresses)")); - strUsage += HelpMessageOpt("-rpccookiefile=<loc>", _("Location of the auth cookie. Relative paths will be prefixed by a net-specific datadir location. (default: data dir)")); - strUsage += HelpMessageOpt("-rpcpassword=<pw>", _("Password for JSON-RPC connections")); - strUsage += HelpMessageOpt("-rpcport=<port>", strprintf(_("Listen for JSON-RPC connections on <port> (default: %u or testnet: %u)"), defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort())); - strUsage += HelpMessageOpt("-rpcserialversion", strprintf(_("Sets the serialization of raw transaction or block hex returned in non-verbose mode, non-segwit(0) or segwit(1) (default: %d)"), DEFAULT_RPC_SERIALIZE_VERSION)); - if (showDebug) - strUsage += HelpMessageOpt("-rpcservertimeout=<n>", strprintf("Timeout during HTTP requests (default: %d)", DEFAULT_HTTP_SERVER_TIMEOUT)); - strUsage += HelpMessageOpt("-rpcthreads=<n>", strprintf(_("Set the number of threads to service RPC calls (default: %d)"), DEFAULT_HTTP_THREADS)); - strUsage += HelpMessageOpt("-rpcuser=<user>", _("Username for JSON-RPC connections")); - if (showDebug) - strUsage += HelpMessageOpt("-rpcworkqueue=<n>", strprintf("Set the depth of the work queue to service RPC calls (default: %d)", DEFAULT_HTTP_WORKQUEUE)); - strUsage += HelpMessageOpt("-server", _("Accept command line and JSON-RPC commands")); - - return strUsage; + gArgs.AddArg("-checkblocks=<n>", strprintf("How many blocks to check at startup (default: %u, 0 = all)", DEFAULT_CHECKBLOCKS), true, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-checklevel=<n>", strprintf("How thorough the block verification of -checkblocks is (0-4, default: %u)", DEFAULT_CHECKLEVEL), true, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-checkblockindex", strprintf("Do a full consistency check for mapBlockIndex, setBlockIndexCandidates, chainActive and mapBlocksUnlinked occasionally. (default: %u)", defaultChainParams->DefaultConsistencyChecks()), true, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-checkmempool=<n>", strprintf("Run checks every <n> transactions (default: %u)", defaultChainParams->DefaultConsistencyChecks()), true, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-checkpoints", strprintf("Disable expensive verification for known chain history (default: %u)", DEFAULT_CHECKPOINTS_ENABLED), true, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-deprecatedrpc=<method>", "Allows deprecated RPC method(s) to be used", true, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-dropmessagestest=<n>", "Randomly drop 1 of every <n> network messages", true, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-stopafterblockimport", strprintf("Stop running after importing blocks from disk (default: %u)", DEFAULT_STOPAFTERBLOCKIMPORT), true, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-stopatheight", strprintf("Stop running after reaching the given height in the main chain (default: %u)", DEFAULT_STOPATHEIGHT), true, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-limitancestorcount=<n>", strprintf("Do not accept transactions if number of in-mempool ancestors is <n> or more (default: %u)", DEFAULT_ANCESTOR_LIMIT), true, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-limitancestorsize=<n>", strprintf("Do not accept transactions whose size with all in-mempool ancestors exceeds <n> kilobytes (default: %u)", DEFAULT_ANCESTOR_SIZE_LIMIT), true, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-limitdescendantcount=<n>", strprintf("Do not accept transactions if any ancestor would have <n> or more in-mempool descendants (default: %u)", DEFAULT_DESCENDANT_LIMIT), true, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-limitdescendantsize=<n>", strprintf("Do not accept transactions if any ancestor would have more than <n> kilobytes of in-mempool descendants (default: %u).", DEFAULT_DESCENDANT_SIZE_LIMIT), true, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-vbparams=deployment:start:end", "Use given start/end times for specified version bits deployment (regtest-only)", true, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-addrmantest", "Allows to test address relay on localhost", true, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-debug=<category>", strprintf("Output debugging information (default: %u, supplying <category> is optional)", 0) + ". " + + "If <category> is not supplied or if <category> = 1, output all debugging information. <category> can be: " + ListLogCategories() + ".", false, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-debugexclude=<category>", strprintf("Exclude debugging information for a category. Can be used in conjunction with -debug=1 to output debug logs for all categories except one or more specified categories."), false, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-help-debug", "Show all debugging options (usage: --help -help-debug)", false, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-logips", strprintf("Include IP addresses in debug output (default: %u)", DEFAULT_LOGIPS), false, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-logtimestamps", strprintf("Prepend debug output with timestamp (default: %u)", DEFAULT_LOGTIMESTAMPS), false, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-logtimemicros", strprintf("Add microsecond precision to debug timestamps (default: %u)", DEFAULT_LOGTIMEMICROS), true, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-mocktime=<n>", "Replace actual time with <n> seconds since epoch (default: 0)", true, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-maxsigcachesize=<n>", strprintf("Limit sum of signature cache and script execution cache sizes to <n> MiB (default: %u)", DEFAULT_MAX_SIG_CACHE_SIZE), true, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-maxtipage=<n>", strprintf("Maximum tip age in seconds to consider node in initial block download (default: %u)", DEFAULT_MAX_TIP_AGE), true, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-maxtxfee=<amt>", strprintf("Maximum total fees (in %s) to use in a single wallet transaction or raw transaction; setting this too low may abort large transactions (default: %s)", + CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MAXFEE)), false, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-printpriority", strprintf("Log transaction fee per kB when mining blocks (default: %u)", DEFAULT_PRINTPRIORITY), true, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-printtoconsole", "Send trace/debug info to console instead of debug.log file", false, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-shrinkdebugfile", "Shrink debug.log file on client startup (default: 1 when no -debug)", false, OptionsCategory::DEBUG_TEST); + gArgs.AddArg("-uacomment=<cmt>", "Append comment to the user agent string", false, OptionsCategory::DEBUG_TEST); + + SetupChainParamsBaseOptions(); + + gArgs.AddArg("-acceptnonstdtxn", strprintf("Relay and mine \"non-standard\" transactions (%sdefault: %u)", "testnet/regtest only; ", !testnetChainParams->RequireStandard()), true, OptionsCategory::NODE_RELAY); + gArgs.AddArg("-incrementalrelayfee=<amt>", strprintf("Fee rate (in %s/kB) used to define cost of relay, used for mempool limiting and BIP 125 replacement. (default: %s)", CURRENCY_UNIT, FormatMoney(DEFAULT_INCREMENTAL_RELAY_FEE)), true, OptionsCategory::NODE_RELAY); + gArgs.AddArg("-dustrelayfee=<amt>", strprintf("Fee rate (in %s/kB) used to defined dust, the value of an output such that it will cost more than its value in fees at this fee rate to spend it. (default: %s)", CURRENCY_UNIT, FormatMoney(DUST_RELAY_TX_FEE)), true, OptionsCategory::NODE_RELAY); + gArgs.AddArg("-bytespersigop", strprintf("Equivalent bytes per sigop in transactions for relay and mining (default: %u)", DEFAULT_BYTES_PER_SIGOP), false, OptionsCategory::NODE_RELAY); + gArgs.AddArg("-datacarrier", strprintf("Relay and mine data carrier transactions (default: %u)", DEFAULT_ACCEPT_DATACARRIER), false, OptionsCategory::NODE_RELAY); + gArgs.AddArg("-datacarriersize", strprintf("Maximum size of data in data carrier transactions we relay and mine (default: %u)", MAX_OP_RETURN_RELAY), false, OptionsCategory::NODE_RELAY); + gArgs.AddArg("-mempoolreplacement", strprintf("Enable transaction replacement in the memory pool (default: %u)", DEFAULT_ENABLE_REPLACEMENT), false, OptionsCategory::NODE_RELAY); + gArgs.AddArg("-minrelaytxfee=<amt>", strprintf("Fees (in %s/kB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s)", + CURRENCY_UNIT, FormatMoney(DEFAULT_MIN_RELAY_TX_FEE)), false, OptionsCategory::NODE_RELAY); + gArgs.AddArg("-whitelistforcerelay", strprintf("Force relay of transactions from whitelisted peers even if they violate local relay policy (default: %d)", DEFAULT_WHITELISTFORCERELAY), false, OptionsCategory::NODE_RELAY); + gArgs.AddArg("-whitelistrelay", strprintf("Accept relayed transactions received from whitelisted peers even when not relaying transactions (default: %d)", DEFAULT_WHITELISTRELAY), false, OptionsCategory::NODE_RELAY); + + + gArgs.AddArg("-blockmaxweight=<n>", strprintf("Set maximum BIP141 block weight (default: %d)", DEFAULT_BLOCK_MAX_WEIGHT), false, OptionsCategory::BLOCK_CREATION); + gArgs.AddArg("-blockmintxfee=<amt>", strprintf("Set lowest fee rate (in %s/kB) for transactions to be included in block creation. (default: %s)", CURRENCY_UNIT, FormatMoney(DEFAULT_BLOCK_MIN_TX_FEE)), false, OptionsCategory::BLOCK_CREATION); + gArgs.AddArg("-blockversion=<n>", "Override block version to test forking scenarios", true, OptionsCategory::BLOCK_CREATION); + + gArgs.AddArg("-rest", strprintf("Accept public REST requests (default: %u)", DEFAULT_REST_ENABLE), false, OptionsCategory::RPC); + gArgs.AddArg("-rpcallowip=<ip>", "Allow JSON-RPC connections from specified source. Valid for <ip> are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24). This option can be specified multiple times", false, OptionsCategory::RPC); + gArgs.AddArg("-rpcauth=<userpw>", "Username and hashed password for JSON-RPC connections. The field <userpw> comes in the format: <USERNAME>:<SALT>$<HASH>. A canonical python script is included in share/rpcuser. The client then connects normally using the rpcuser=<USERNAME>/rpcpassword=<PASSWORD> pair of arguments. This option can be specified multiple times", false, OptionsCategory::RPC); + gArgs.AddArg("-rpcbind=<addr>[:port]", "Bind to given address to listen for JSON-RPC connections. This option is ignored unless -rpcallowip is also passed. Port is optional and overrides -rpcport. Use [host]:port notation for IPv6. This option can be specified multiple times (default: 127.0.0.1 and ::1 i.e., localhost, or if -rpcallowip has been specified, 0.0.0.0 and :: i.e., all addresses)", false, OptionsCategory::RPC); + gArgs.AddArg("-rpccookiefile=<loc>", "Location of the auth cookie. Relative paths will be prefixed by a net-specific datadir location. (default: data dir)", false, OptionsCategory::RPC); + gArgs.AddArg("-rpcpassword=<pw>", "Password for JSON-RPC connections", false, OptionsCategory::RPC); + gArgs.AddArg("-rpcport=<port>", strprintf("Listen for JSON-RPC connections on <port> (default: %u or testnet: %u)", defaultBaseParams->RPCPort(), testnetBaseParams->RPCPort()), false, OptionsCategory::RPC); + gArgs.AddArg("-rpcserialversion", strprintf("Sets the serialization of raw transaction or block hex returned in non-verbose mode, non-segwit(0) or segwit(1) (default: %d)", DEFAULT_RPC_SERIALIZE_VERSION), false, OptionsCategory::RPC); + gArgs.AddArg("-rpcservertimeout=<n>", strprintf("Timeout during HTTP requests (default: %d)", DEFAULT_HTTP_SERVER_TIMEOUT), true, OptionsCategory::RPC); + gArgs.AddArg("-rpcthreads=<n>", strprintf("Set the number of threads to service RPC calls (default: %d)", DEFAULT_HTTP_THREADS), false, OptionsCategory::RPC); + gArgs.AddArg("-rpcuser=<user>", "Username for JSON-RPC connections", false, OptionsCategory::RPC); + gArgs.AddArg("-rpcworkqueue=<n>", strprintf("Set the depth of the work queue to service RPC calls (default: %d)", DEFAULT_HTTP_WORKQUEUE), true, OptionsCategory::RPC); + gArgs.AddArg("-server", "Accept command line and JSON-RPC commands", false, OptionsCategory::RPC); + + // Hidden options + gArgs.AddArg("-rpcssl", "", false, OptionsCategory::HIDDEN); + gArgs.AddArg("-benchmark", "", false, OptionsCategory::HIDDEN); + gArgs.AddArg("-h", "", false, OptionsCategory::HIDDEN); + gArgs.AddArg("-help", "", false, OptionsCategory::HIDDEN); + gArgs.AddArg("-socks", "", false, OptionsCategory::HIDDEN); + gArgs.AddArg("-tor", "", false, OptionsCategory::HIDDEN); + gArgs.AddArg("-debugnet", "", false, OptionsCategory::HIDDEN); + gArgs.AddArg("-whitelistalwaysrelay", "", false, OptionsCategory::HIDDEN); + gArgs.AddArg("-prematurewitness", "", false, OptionsCategory::HIDDEN); + gArgs.AddArg("-walletprematurewitness", "", false, OptionsCategory::HIDDEN); + gArgs.AddArg("-promiscuousmempoolflags", "", false, OptionsCategory::HIDDEN); + gArgs.AddArg("-blockminsize", "", false, OptionsCategory::HIDDEN); + gArgs.AddArg("-dbcrashratio", "", false, OptionsCategory::HIDDEN); + gArgs.AddArg("-forcecompactdb", "", false, OptionsCategory::HIDDEN); + gArgs.AddArg("-usehd", "", false, OptionsCategory::HIDDEN); } std::string LicenseInfo() @@ -1132,6 +1116,8 @@ bool AppInitParameterInteraction() if (gArgs.GetBoolArg("-peerbloomfilters", DEFAULT_PEERBLOOMFILTERS)) nLocalServices = ServiceFlags(nLocalServices | NODE_BLOOM); + g_enable_bip61 = gArgs.GetBoolArg("-enablebip61", DEFAULT_ENABLE_BIP61); + if (gArgs.GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) < 0) return InitError("rpcserialversion must be non-negative."); diff --git a/src/init.h b/src/init.h index 000c8c95e4..5423a042a6 100644 --- a/src/init.h +++ b/src/init.h @@ -8,6 +8,7 @@ #include <memory> #include <string> +#include <util.h> class CScheduler; class CWallet; @@ -60,14 +61,11 @@ bool AppInitLockDataDirectory(); */ bool AppInitMain(); -/** The help message mode determines what help message to show */ -enum class HelpMessageMode { - BITCOIND, - BITCOIN_QT -}; +/** + * Setup the arguments for gArgs + */ +void SetupServerArgs(); -/** Help for options shared between UI and daemon (for -help) */ -std::string HelpMessage(HelpMessageMode mode); /** Returns licensing information (for -version) */ std::string LicenseInfo(); diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp index 53d2359caf..4189ff7497 100644 --- a/src/interfaces/node.cpp +++ b/src/interfaces/node.cpp @@ -48,11 +48,11 @@ namespace { class NodeImpl : public Node { - void parseParameters(int argc, const char* const argv[]) override + bool parseParameters(int argc, const char* const argv[], std::string& error) override { - gArgs.ParseParameters(argc, argv); + return gArgs.ParseParameters(argc, argv, error); } - void readConfigFile(const std::string& conf_path) override { gArgs.ReadConfigFile(conf_path); } + bool readConfigFiles(std::string& error) override { return gArgs.ReadConfigFiles(error); } bool softSetArg(const std::string& arg, const std::string& value) override { return gArgs.SoftSetArg(arg, value); } bool softSetBoolArg(const std::string& arg, bool value) override { return gArgs.SoftSetBoolArg(arg, value); } void selectParams(const std::string& network) override { SelectParams(network); } @@ -83,7 +83,7 @@ class NodeImpl : public Node StopMapPort(); } } - std::string helpMessage(HelpMessageMode mode) override { return HelpMessage(mode); } + void setupServerArgs() override { return SetupServerArgs(); } bool getProxy(Network net, proxyType& proxy_info) override { return GetProxy(net, proxy_info); } size_t getNodeCount(CConnman::NumConnections flags) override { @@ -222,8 +222,8 @@ class NodeImpl : public Node { #ifdef ENABLE_WALLET std::vector<std::unique_ptr<Wallet>> wallets; - for (CWallet* wallet : GetWallets()) { - wallets.emplace_back(MakeWallet(*wallet)); + for (const std::shared_ptr<CWallet>& wallet : GetWallets()) { + wallets.emplace_back(MakeWallet(wallet)); } return wallets; #else @@ -249,7 +249,7 @@ class NodeImpl : public Node std::unique_ptr<Handler> handleLoadWallet(LoadWalletFn fn) override { CHECK_WALLET( - return MakeHandler(::uiInterface.LoadWallet.connect([fn](CWallet* wallet) { fn(MakeWallet(*wallet)); }))); + return MakeHandler(::uiInterface.LoadWallet.connect([fn](std::shared_ptr<CWallet> wallet) { fn(MakeWallet(wallet)); }))); } std::unique_ptr<Handler> handleNotifyNumConnectionsChanged(NotifyNumConnectionsChangedFn fn) override { diff --git a/src/interfaces/node.h b/src/interfaces/node.h index 3cebe53eb0..8185c015a9 100644 --- a/src/interfaces/node.h +++ b/src/interfaces/node.h @@ -7,7 +7,6 @@ #include <addrdb.h> // For banmap_t #include <amount.h> // For CAmount -#include <init.h> // For HelpMessageMode #include <net.h> // For CConnman::NumConnections #include <netaddress.h> // For Network @@ -39,7 +38,7 @@ public: virtual ~Node() {} //! Set command line arguments. - virtual void parseParameters(int argc, const char* const argv[]) = 0; + virtual bool parseParameters(int argc, const char* const argv[], std::string& error) = 0; //! Set a command line argument if it doesn't already have a value virtual bool softSetArg(const std::string& arg, const std::string& value) = 0; @@ -48,7 +47,7 @@ public: virtual bool softSetBoolArg(const std::string& arg, bool value) = 0; //! Load settings from configuration file. - virtual void readConfigFile(const std::string& conf_path) = 0; + virtual bool readConfigFiles(std::string& error) = 0; //! Choose network parameters. virtual void selectParams(const std::string& network) = 0; @@ -83,8 +82,8 @@ public: //! Return whether shutdown was requested. virtual bool shutdownRequested() = 0; - //! Get help message string. - virtual std::string helpMessage(HelpMessageMode mode) = 0; + //! Setup arguments + virtual void setupServerArgs() = 0; //! Map port. virtual void mapPort(bool use_upnp) = 0; diff --git a/src/interfaces/wallet.cpp b/src/interfaces/wallet.cpp index 63b9d80a92..3029dbe8e3 100644 --- a/src/interfaces/wallet.cpp +++ b/src/interfaces/wallet.cpp @@ -118,7 +118,7 @@ WalletTxOut MakeWalletTxOut(CWallet& wallet, const CWalletTx& wtx, int n, int de class WalletImpl : public Wallet { public: - WalletImpl(CWallet& wallet) : m_wallet(wallet) {} + WalletImpl(const std::shared_ptr<CWallet>& wallet) : m_shared_wallet(wallet), m_wallet(*wallet.get()) {} bool encryptWallet(const SecureString& wallet_passphrase) override { @@ -453,11 +453,12 @@ public: return MakeHandler(m_wallet.NotifyWatchonlyChanged.connect(fn)); } + std::shared_ptr<CWallet> m_shared_wallet; CWallet& m_wallet; }; } // namespace -std::unique_ptr<Wallet> MakeWallet(CWallet& wallet) { return MakeUnique<WalletImpl>(wallet); } +std::unique_ptr<Wallet> MakeWallet(const std::shared_ptr<CWallet>& wallet) { return MakeUnique<WalletImpl>(wallet); } } // namespace interfaces diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h index ff779cd0ad..82ae0b14b5 100644 --- a/src/interfaces/wallet.h +++ b/src/interfaces/wallet.h @@ -363,7 +363,7 @@ struct WalletTxOut //! Return implementation of Wallet interface. This function will be undefined //! in builds where ENABLE_WALLET is false. -std::unique_ptr<Wallet> MakeWallet(CWallet& wallet); +std::unique_ptr<Wallet> MakeWallet(const std::shared_ptr<CWallet>& wallet); } // namespace interfaces diff --git a/src/key.cpp b/src/key.cpp index 042e687772..94be179bfb 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -273,7 +273,7 @@ bool CExtKey::Derive(CExtKey &out, unsigned int _nChild) const { return key.Derive(out.key, out.chaincode, _nChild, chaincode); } -void CExtKey::SetMaster(const unsigned char *seed, unsigned int nSeedLen) { +void CExtKey::SetSeed(const unsigned char *seed, unsigned int nSeedLen) { static const unsigned char hashkey[] = {'B','i','t','c','o','i','n',' ','s','e','e','d'}; std::vector<unsigned char, secure_allocator<unsigned char>> vout(64); CHMAC_SHA512(hashkey, sizeof(hashkey)).Write(seed, nSeedLen).Finalize(vout.data()); @@ -158,7 +158,7 @@ struct CExtKey { void Decode(const unsigned char code[BIP32_EXTKEY_SIZE]); bool Derive(CExtKey& out, unsigned int nChild) const; CExtPubKey Neuter() const; - void SetMaster(const unsigned char* seed, unsigned int nSeedLen); + void SetSeed(const unsigned char* seed, unsigned int nSeedLen); template <typename Stream> void Serialize(Stream& s) const { diff --git a/src/keystore.cpp b/src/keystore.cpp index e69d518890..ea93ed69fa 100644 --- a/src/keystore.cpp +++ b/src/keystore.cpp @@ -195,3 +195,10 @@ CKeyID GetKeyForDestination(const CKeyStore& store, const CTxDestination& dest) } return CKeyID(); } + +bool HaveKey(const CKeyStore& store, const CKey& key) +{ + CKey key2; + key2.Set(key.begin(), key.end(), !key.IsCompressed()); + return store.HaveKey(key.GetPubKey().GetID()) || store.HaveKey(key2.GetPubKey().GetID()); +} diff --git a/src/keystore.h b/src/keystore.h index c56e4751de..cd5ded9203 100644 --- a/src/keystore.h +++ b/src/keystore.h @@ -80,4 +80,7 @@ typedef std::map<CKeyID, std::pair<CPubKey, std::vector<unsigned char> > > Crypt /** Return the CKeyID of the key involved in a script (if there is a unique one). */ CKeyID GetKeyForDestination(const CKeyStore& store, const CTxDestination& dest); +/** Checks if a CKey is in the given CKeyStore compressed or otherwise*/ +bool HaveKey(const CKeyStore& store, const CKey& key); + #endif // BITCOIN_KEYSTORE_H diff --git a/src/miner.cpp b/src/miner.cpp index 0660df928c..d4527a1d67 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -14,7 +14,6 @@ #include <consensus/merkle.h> #include <consensus/validation.h> #include <hash.h> -#include <validation.h> #include <net.h> #include <policy/feerate.h> #include <policy/policy.h> diff --git a/src/miner.h b/src/miner.h index 33a22ba75f..ed1b4434f9 100644 --- a/src/miner.h +++ b/src/miner.h @@ -8,6 +8,7 @@ #include <primitives/block.h> #include <txmempool.h> +#include <validation.h> #include <stdint.h> #include <memory> @@ -169,7 +170,7 @@ private: /** Add transactions based on feerate including unconfirmed ancestors * Increments nPackagesSelected / nDescendantsUpdated with corresponding * statistics from the package selection (for logging statistics). */ - void addPackageTxs(int &nPackagesSelected, int &nDescendantsUpdated); + void addPackageTxs(int &nPackagesSelected, int &nDescendantsUpdated) EXCLUSIVE_LOCKS_REQUIRED(mempool.cs); // helper functions for addPackageTxs() /** Remove confirmed (inBlock) entries from given set */ @@ -183,13 +184,13 @@ private: bool TestPackageTransactions(const CTxMemPool::setEntries& package); /** Return true if given transaction from mapTx has already been evaluated, * or if the transaction's cached data in mapTx is incorrect. */ - bool SkipMapTxEntry(CTxMemPool::txiter it, indexed_modified_transaction_set &mapModifiedTx, CTxMemPool::setEntries &failedTx); + bool SkipMapTxEntry(CTxMemPool::txiter it, indexed_modified_transaction_set &mapModifiedTx, CTxMemPool::setEntries &failedTx) EXCLUSIVE_LOCKS_REQUIRED(mempool.cs); /** Sort the package in an order that is valid to appear in a block */ void SortForBlock(const CTxMemPool::setEntries& package, std::vector<CTxMemPool::txiter>& sortedEntries); /** Add descendants of given transactions to mapModifiedTx with ancestor * state updated assuming given transactions are inBlock. Returns number * of updated descendants. */ - int UpdatePackagesForAdded(const CTxMemPool::setEntries& alreadyAdded, indexed_modified_transaction_set &mapModifiedTx); + int UpdatePackagesForAdded(const CTxMemPool::setEntries& alreadyAdded, indexed_modified_transaction_set &mapModifiedTx) EXCLUSIVE_LOCKS_REQUIRED(mempool.cs); }; /** Modify the extranonce in a block */ diff --git a/src/net_processing.cpp b/src/net_processing.cpp index ed2fb598d2..fc05dd2ad2 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -37,6 +37,7 @@ #endif std::atomic<int64_t> nTimeBestReceived(0); // Used only to inform the wallet of when we last received a block +bool g_enable_bip61 = DEFAULT_ENABLE_BIP61; struct IteratorComparator { @@ -1070,12 +1071,13 @@ static void RelayAddress(const CAddress& addr, bool fReachable, CConnman* connma connman->ForEachNodeThen(std::move(sortfunc), std::move(pushfunc)); } -void static ProcessGetBlockData(CNode* pfrom, const Consensus::Params& consensusParams, const CInv& inv, CConnman* connman, const std::atomic<bool>& interruptMsgProc) +void static ProcessGetBlockData(CNode* pfrom, const CChainParams& chainparams, const CInv& inv, CConnman* connman, const std::atomic<bool>& interruptMsgProc) { bool send = false; std::shared_ptr<const CBlock> a_recent_block; std::shared_ptr<const CBlockHeaderAndShortTxIDs> a_recent_compact_block; bool fWitnessesPresentInARecentCompactBlock; + const Consensus::Params& consensusParams = chainparams.GetConsensus(); { LOCK(cs_most_recent_block); a_recent_block = most_recent_block; @@ -1142,6 +1144,15 @@ void static ProcessGetBlockData(CNode* pfrom, const Consensus::Params& consensus std::shared_ptr<const CBlock> pblock; if (a_recent_block && a_recent_block->GetHash() == pindex->GetBlockHash()) { pblock = a_recent_block; + } else if (inv.type == MSG_WITNESS_BLOCK) { + // Fast-path: in this case it is possible to serve the block directly from disk, + // as the network format matches the format on disk + std::vector<uint8_t> block_data; + if (!ReadRawBlockFromDisk(block_data, pindex, chainparams.MessageStart())) { + assert(!"cannot load block from disk"); + } + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::BLOCK, MakeSpan(block_data))); + // Don't set pblock as we've sent the block } else { // Send block from disk std::shared_ptr<CBlock> pblockRead = std::make_shared<CBlock>(); @@ -1149,53 +1160,55 @@ void static ProcessGetBlockData(CNode* pfrom, const Consensus::Params& consensus assert(!"cannot load block from disk"); pblock = pblockRead; } - if (inv.type == MSG_BLOCK) - connman->PushMessage(pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCK, *pblock)); - else if (inv.type == MSG_WITNESS_BLOCK) - connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::BLOCK, *pblock)); - else if (inv.type == MSG_FILTERED_BLOCK) - { - bool sendMerkleBlock = false; - CMerkleBlock merkleBlock; + if (pblock) { + if (inv.type == MSG_BLOCK) + connman->PushMessage(pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::BLOCK, *pblock)); + else if (inv.type == MSG_WITNESS_BLOCK) + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::BLOCK, *pblock)); + else if (inv.type == MSG_FILTERED_BLOCK) { - LOCK(pfrom->cs_filter); - if (pfrom->pfilter) { - sendMerkleBlock = true; - merkleBlock = CMerkleBlock(*pblock, *pfrom->pfilter); + bool sendMerkleBlock = false; + CMerkleBlock merkleBlock; + { + LOCK(pfrom->cs_filter); + if (pfrom->pfilter) { + sendMerkleBlock = true; + merkleBlock = CMerkleBlock(*pblock, *pfrom->pfilter); + } } + if (sendMerkleBlock) { + connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::MERKLEBLOCK, merkleBlock)); + // CMerkleBlock just contains hashes, so also push any transactions in the block the client did not see + // This avoids hurting performance by pointlessly requiring a round-trip + // Note that there is currently no way for a node to request any single transactions we didn't send here - + // they must either disconnect and retry or request the full block. + // Thus, the protocol spec specified allows for us to provide duplicate txn here, + // however we MUST always provide at least what the remote peer needs + typedef std::pair<unsigned int, uint256> PairType; + for (PairType& pair : merkleBlock.vMatchedTxn) + connman->PushMessage(pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::TX, *pblock->vtx[pair.first])); + } + // else + // no response } - if (sendMerkleBlock) { - connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::MERKLEBLOCK, merkleBlock)); - // CMerkleBlock just contains hashes, so also push any transactions in the block the client did not see - // This avoids hurting performance by pointlessly requiring a round-trip - // Note that there is currently no way for a node to request any single transactions we didn't send here - - // they must either disconnect and retry or request the full block. - // Thus, the protocol spec specified allows for us to provide duplicate txn here, - // however we MUST always provide at least what the remote peer needs - typedef std::pair<unsigned int, uint256> PairType; - for (PairType& pair : merkleBlock.vMatchedTxn) - connman->PushMessage(pfrom, msgMaker.Make(SERIALIZE_TRANSACTION_NO_WITNESS, NetMsgType::TX, *pblock->vtx[pair.first])); - } - // else - // no response - } - else if (inv.type == MSG_CMPCT_BLOCK) - { - // If a peer is asking for old blocks, we're almost guaranteed - // they won't have a useful mempool to match against a compact block, - // and we don't feel like constructing the object for them, so - // instead we respond with the full, non-compact block. - bool fPeerWantsWitness = State(pfrom->GetId())->fWantsCmpctWitness; - int nSendFlags = fPeerWantsWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS; - if (CanDirectFetch(consensusParams) && pindex->nHeight >= chainActive.Height() - MAX_CMPCTBLOCK_DEPTH) { - if ((fPeerWantsWitness || !fWitnessesPresentInARecentCompactBlock) && a_recent_compact_block && a_recent_compact_block->header.GetHash() == pindex->GetBlockHash()) { - connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, *a_recent_compact_block)); + else if (inv.type == MSG_CMPCT_BLOCK) + { + // If a peer is asking for old blocks, we're almost guaranteed + // they won't have a useful mempool to match against a compact block, + // and we don't feel like constructing the object for them, so + // instead we respond with the full, non-compact block. + bool fPeerWantsWitness = State(pfrom->GetId())->fWantsCmpctWitness; + int nSendFlags = fPeerWantsWitness ? 0 : SERIALIZE_TRANSACTION_NO_WITNESS; + if (CanDirectFetch(consensusParams) && pindex->nHeight >= chainActive.Height() - MAX_CMPCTBLOCK_DEPTH) { + if ((fPeerWantsWitness || !fWitnessesPresentInARecentCompactBlock) && a_recent_compact_block && a_recent_compact_block->header.GetHash() == pindex->GetBlockHash()) { + connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, *a_recent_compact_block)); + } else { + CBlockHeaderAndShortTxIDs cmpctblock(*pblock, fPeerWantsWitness); + connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock)); + } } else { - CBlockHeaderAndShortTxIDs cmpctblock(*pblock, fPeerWantsWitness); - connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::CMPCTBLOCK, cmpctblock)); + connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCK, *pblock)); } - } else { - connman->PushMessage(pfrom, msgMaker.Make(nSendFlags, NetMsgType::BLOCK, *pblock)); } } @@ -1213,7 +1226,7 @@ void static ProcessGetBlockData(CNode* pfrom, const Consensus::Params& consensus } } -void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParams, CConnman* connman, const std::atomic<bool>& interruptMsgProc) +void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnman* connman, const std::atomic<bool>& interruptMsgProc) { AssertLockNotHeld(cs_main); @@ -1262,7 +1275,7 @@ void static ProcessGetData(CNode* pfrom, const Consensus::Params& consensusParam const CInv &inv = *it; if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_CMPCT_BLOCK || inv.type == MSG_WITNESS_BLOCK) { it++; - ProcessGetBlockData(pfrom, consensusParams, inv, connman, interruptMsgProc); + ProcessGetBlockData(pfrom, chainparams, inv, connman, interruptMsgProc); } } @@ -1579,7 +1592,9 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr // Each connection can only send one version message if (pfrom->nVersion != 0) { - connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_DUPLICATE, std::string("Duplicate version message"))); + if (g_enable_bip61) { + connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_DUPLICATE, std::string("Duplicate version message"))); + } LOCK(cs_main); Misbehaving(pfrom->GetId(), 1); return false; @@ -1608,8 +1623,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr if (!pfrom->fInbound && !pfrom->fFeeler && !pfrom->m_manual_connection && !HasAllDesirableServiceFlags(nServices)) { LogPrint(BCLog::NET, "peer=%d does not offer the expected services (%08x offered, %08x expected); disconnecting\n", pfrom->GetId(), nServices, GetDesirableServiceFlags(nServices)); - connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_NONSTANDARD, - strprintf("Expected to offer services %08x", GetDesirableServiceFlags(nServices)))); + if (g_enable_bip61) { + connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_NONSTANDARD, + strprintf("Expected to offer services %08x", GetDesirableServiceFlags(nServices)))); + } pfrom->fDisconnect = true; return false; } @@ -1629,8 +1646,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr { // disconnect from peers older than this proto version LogPrint(BCLog::NET, "peer=%d using obsolete version %i; disconnecting\n", pfrom->GetId(), nVersion); - connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE, - strprintf("Version must be %d or greater", MIN_PEER_PROTO_VERSION))); + if (g_enable_bip61) { + connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_OBSOLETE, + strprintf("Version must be %d or greater", MIN_PEER_PROTO_VERSION))); + } pfrom->fDisconnect = true; return false; } @@ -1972,7 +1991,7 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr } pfrom->vRecvGetData.insert(pfrom->vRecvGetData.end(), vInv.begin(), vInv.end()); - ProcessGetData(pfrom, chainparams.GetConsensus(), connman, interruptMsgProc); + ProcessGetData(pfrom, chainparams, connman, interruptMsgProc); } @@ -2328,9 +2347,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr LogPrint(BCLog::MEMPOOLREJ, "%s from peer=%d was not accepted: %s\n", tx.GetHash().ToString(), pfrom->GetId(), FormatStateMessage(state)); - if (state.GetRejectCode() > 0 && state.GetRejectCode() < REJECT_INTERNAL) // Never send AcceptToMemoryPool's internal codes over P2P + if (g_enable_bip61 && state.GetRejectCode() > 0 && state.GetRejectCode() < REJECT_INTERNAL) { // Never send AcceptToMemoryPool's internal codes over P2P connman->PushMessage(pfrom, msgMaker.Make(NetMsgType::REJECT, strCommand, (unsigned char)state.GetRejectCode(), state.GetRejectReason().substr(0, MAX_REJECT_MESSAGE_LENGTH), inv.hash)); + } if (nDoS > 0) { Misbehaving(pfrom->GetId(), nDoS); } @@ -2903,8 +2923,10 @@ static bool SendRejectsAndCheckIfBanned(CNode* pnode, CConnman* connman) AssertLockHeld(cs_main); CNodeState &state = *State(pnode->GetId()); - for (const CBlockReject& reject : state.rejects) { - connman->PushMessage(pnode, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, std::string(NetMsgType::BLOCK), reject.chRejectCode, reject.strRejectReason, reject.hashBlock)); + if (g_enable_bip61) { + for (const CBlockReject& reject : state.rejects) { + connman->PushMessage(pnode, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, std::string(NetMsgType::BLOCK), reject.chRejectCode, reject.strRejectReason, reject.hashBlock)); + } } state.rejects.clear(); @@ -2942,7 +2964,7 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter bool fMoreWork = false; if (!pfrom->vRecvGetData.empty()) - ProcessGetData(pfrom, chainparams.GetConsensus(), connman, interruptMsgProc); + ProcessGetData(pfrom, chainparams, connman, interruptMsgProc); if (pfrom->fDisconnect) return false; @@ -3011,7 +3033,9 @@ bool PeerLogicValidation::ProcessMessages(CNode* pfrom, std::atomic<bool>& inter } catch (const std::ios_base::failure& e) { - connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_MALFORMED, std::string("error parsing message"))); + if (g_enable_bip61) { + connman->PushMessage(pfrom, CNetMsgMaker(INIT_PROTO_VERSION).Make(NetMsgType::REJECT, strCommand, REJECT_MALFORMED, std::string("error parsing message"))); + } if (strstr(e.what(), "end of data")) { // Allow exceptions from under-length message on vRecv diff --git a/src/net_processing.h b/src/net_processing.h index 195d0d2033..b0b905d922 100644 --- a/src/net_processing.h +++ b/src/net_processing.h @@ -35,6 +35,11 @@ static constexpr int64_t EXTRA_PEER_CHECK_INTERVAL = 45; /** Minimum time an outbound-peer-eviction candidate must be connected for, in order to evict, in seconds */ static constexpr int64_t MINIMUM_CONNECT_TIME = 30; +/** Default for BIP61 (sending reject messages) */ +static constexpr bool DEFAULT_ENABLE_BIP61 = true; +/** Enable BIP61 (sending reject messages) */ +extern bool g_enable_bip61; + class PeerLogicValidation final : public CValidationInterface, public NetEventsInterface { private: CConnman* const connman; diff --git a/src/policy/policy.h b/src/policy/policy.h index e4eda4b635..5ce019df4c 100644 --- a/src/policy/policy.h +++ b/src/policy/policy.h @@ -22,6 +22,8 @@ static const unsigned int DEFAULT_BLOCK_MAX_WEIGHT = MAX_BLOCK_WEIGHT - 4000; static const unsigned int DEFAULT_BLOCK_MIN_TX_FEE = 1000; /** The maximum weight for transactions we're willing to relay/mine */ static const unsigned int MAX_STANDARD_TX_WEIGHT = 400000; +/** The minimum non-witness size for transactions we're willing to relay/mine (1 segwit input + 1 P2WPKH output = 82 bytes) */ +static const unsigned int MIN_STANDARD_TX_NONWITNESS_SIZE = 82; /** Maximum number of signature check operations in an IsStandard() P2SH script */ static const unsigned int MAX_P2SH_SIGOPS = 15; /** The maximum number of sigops we're willing to relay/mine in a single tx */ @@ -63,7 +65,8 @@ static constexpr unsigned int STANDARD_SCRIPT_VERIFY_FLAGS = MANDATORY_SCRIPT_VE SCRIPT_VERIFY_LOW_S | SCRIPT_VERIFY_WITNESS | SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM | - SCRIPT_VERIFY_WITNESS_PUBKEYTYPE; + SCRIPT_VERIFY_WITNESS_PUBKEYTYPE | + SCRIPT_VERIFY_CONST_SCRIPTCODE; /** For convenience, standard but not mandatory verify flags. */ static constexpr unsigned int STANDARD_NOT_MANDATORY_VERIFY_FLAGS = STANDARD_SCRIPT_VERIFY_FLAGS & ~MANDATORY_SCRIPT_VERIFY_FLAGS; diff --git a/src/primitives/transaction.cpp b/src/primitives/transaction.cpp index 6f463cabf5..230f762a1b 100644 --- a/src/primitives/transaction.cpp +++ b/src/primitives/transaction.cpp @@ -67,18 +67,18 @@ uint256 CTransaction::ComputeHash() const return SerializeHash(*this, SER_GETHASH, SERIALIZE_TRANSACTION_NO_WITNESS); } -uint256 CTransaction::GetWitnessHash() const +uint256 CTransaction::ComputeWitnessHash() const { if (!HasWitness()) { - return GetHash(); + return hash; } return SerializeHash(*this, SER_GETHASH, 0); } /* For backward compatibility, the hash is initialized to 0. TODO: remove the need for this default constructor entirely. */ -CTransaction::CTransaction() : vin(), vout(), nVersion(CTransaction::CURRENT_VERSION), nLockTime(0), hash() {} -CTransaction::CTransaction(const CMutableTransaction &tx) : vin(tx.vin), vout(tx.vout), nVersion(tx.nVersion), nLockTime(tx.nLockTime), hash(ComputeHash()) {} -CTransaction::CTransaction(CMutableTransaction &&tx) : vin(std::move(tx.vin)), vout(std::move(tx.vout)), nVersion(tx.nVersion), nLockTime(tx.nLockTime), hash(ComputeHash()) {} +CTransaction::CTransaction() : vin(), vout(), nVersion(CTransaction::CURRENT_VERSION), nLockTime(0), hash{}, m_witness_hash{} {} +CTransaction::CTransaction(const CMutableTransaction& tx) : vin(tx.vin), vout(tx.vout), nVersion(tx.nVersion), nLockTime(tx.nLockTime), hash{ComputeHash()}, m_witness_hash{ComputeWitnessHash()} {} +CTransaction::CTransaction(CMutableTransaction&& tx) : vin(std::move(tx.vin)), vout(std::move(tx.vout)), nVersion(tx.nVersion), nLockTime(tx.nLockTime), hash{ComputeHash()}, m_witness_hash{ComputeWitnessHash()} {} CAmount CTransaction::GetValueOut() const { diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index cd348fdbe4..1c846d38ec 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -286,8 +286,10 @@ public: private: /** Memory only. */ const uint256 hash; + const uint256 m_witness_hash; uint256 ComputeHash() const; + uint256 ComputeWitnessHash() const; public: /** Construct a CTransaction that qualifies as IsNull() */ @@ -311,12 +313,8 @@ public: return vin.empty() && vout.empty(); } - const uint256& GetHash() const { - return hash; - } - - // Compute a hash that includes both transaction and witness data - uint256 GetWitnessHash() const; + const uint256& GetHash() const { return hash; } + const uint256& GetWitnessHash() const { return m_witness_hash; }; // Return sum of txouts. CAmount GetValueOut() const; @@ -367,7 +365,7 @@ struct CMutableTransaction uint32_t nLockTime; CMutableTransaction(); - CMutableTransaction(const CTransaction& tx); + explicit CMutableTransaction(const CTransaction& tx); template <typename Stream> inline void Serialize(Stream& s) const { diff --git a/src/pubkey.h b/src/pubkey.h index 9c6c6b085e..bb254547c8 100644 --- a/src/pubkey.h +++ b/src/pubkey.h @@ -33,10 +33,10 @@ public: /** * secp256k1: */ - static const unsigned int PUBLIC_KEY_SIZE = 65; - static const unsigned int COMPRESSED_PUBLIC_KEY_SIZE = 33; - static const unsigned int SIGNATURE_SIZE = 72; - static const unsigned int COMPACT_SIGNATURE_SIZE = 65; + static constexpr unsigned int PUBLIC_KEY_SIZE = 65; + static constexpr unsigned int COMPRESSED_PUBLIC_KEY_SIZE = 33; + static constexpr unsigned int SIGNATURE_SIZE = 72; + static constexpr unsigned int COMPACT_SIGNATURE_SIZE = 65; /** * see www.keylength.com * script supports up to 75 for single byte push diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 57fe4552a1..31d9f936e7 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -229,11 +229,15 @@ public: /// Get window identifier of QMainWindow (BitcoinGUI) WId getMainWinId() const; + /// Setup platform style + void setupPlatformStyle(); + 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); + void addWallet(WalletModel* walletModel); Q_SIGNALS: void requestedInitialize(); @@ -251,6 +255,7 @@ private: #ifdef ENABLE_WALLET PaymentServer* paymentServer; std::vector<WalletModel*> m_wallet_models; + std::unique_ptr<interfaces::Handler> m_handler_load_wallet; #endif int returnValue; const PlatformStyle *platformStyle; @@ -313,10 +318,14 @@ BitcoinApplication::BitcoinApplication(interfaces::Node& node, int &argc, char * paymentServer(0), m_wallet_models(), #endif - returnValue(0) + returnValue(0), + platformStyle(0) { setQuitOnLastWindowClosed(false); +} +void BitcoinApplication::setupPlatformStyle() +{ // UI per-platform customization // This must be done inside the BitcoinApplication constructor, or after it, because // PlatformStyle::instantiate requires a QApplication @@ -447,6 +456,22 @@ void BitcoinApplication::requestShutdown() Q_EMIT requestedShutdown(); } +void BitcoinApplication::addWallet(WalletModel* walletModel) +{ +#ifdef ENABLE_WALLET + window->addWallet(walletModel); + + if (m_wallet_models.empty()) { + window->setCurrentWallet(walletModel->getWalletName()); + } + + connect(walletModel, SIGNAL(coinsSent(WalletModel*, SendCoinsRecipient, QByteArray)), + paymentServer, SLOT(fetchPaymentACK(WalletModel*, const SendCoinsRecipient&, QByteArray))); + + m_wallet_models.push_back(walletModel); +#endif +} + void BitcoinApplication::initializeResult(bool success) { qDebug() << __func__ << ": Initialization result: " << success; @@ -465,21 +490,13 @@ void BitcoinApplication::initializeResult(bool success) window->setClientModel(clientModel); #ifdef ENABLE_WALLET - bool fFirstWallet = true; - auto wallets = m_node.getWallets(); - for (auto& wallet : wallets) { - WalletModel * const walletModel = new WalletModel(std::move(wallet), m_node, platformStyle, optionsModel); + m_handler_load_wallet = m_node.handleLoadWallet([this](std::unique_ptr<interfaces::Wallet> wallet) { + QMetaObject::invokeMethod(this, "addWallet", Qt::QueuedConnection, + Q_ARG(WalletModel*, new WalletModel(std::move(wallet), m_node, platformStyle, optionsModel))); + }); - window->addWallet(walletModel); - if (fFirstWallet) { - window->setCurrentWallet(walletModel->getWalletName()); - fFirstWallet = false; - } - - connect(walletModel, SIGNAL(coinsSent(WalletModel*,SendCoinsRecipient,QByteArray)), - paymentServer, SLOT(fetchPaymentACK(WalletModel*,const SendCoinsRecipient&,QByteArray))); - - m_wallet_models.push_back(walletModel); + for (auto& wallet : m_node.getWallets()) { + addWallet(new WalletModel(std::move(wallet), m_node, platformStyle, optionsModel)); } #endif @@ -531,6 +548,20 @@ WId BitcoinApplication::getMainWinId() const return window->winId(); } +static void SetupUIArgs() +{ +#ifdef ENABLE_WALLET + gArgs.AddArg("-allowselfsignedrootcertificates", strprintf("Allow self signed root certificates (default: %u)", DEFAULT_SELFSIGNED_ROOTCERTS), true, OptionsCategory::GUI); +#endif + gArgs.AddArg("-choosedatadir", strprintf("Choose data directory on startup (default: %u)", DEFAULT_CHOOSE_DATADIR), false, OptionsCategory::GUI); + gArgs.AddArg("-lang=<lang>", "Set language, for example \"de_DE\" (default: system locale)", false, OptionsCategory::GUI); + gArgs.AddArg("-min", "Start minimized", false, OptionsCategory::GUI); + gArgs.AddArg("-resetguisettings", "Reset all settings changed in the GUI", false, OptionsCategory::GUI); + gArgs.AddArg("-rootcertificates=<file>", "Set SSL root certificates for payment request (default: -system-)", false, OptionsCategory::GUI); + gArgs.AddArg("-splash", strprintf("Show splash screen on startup (default: %u)", DEFAULT_SPLASHSCREEN), false, OptionsCategory::GUI); + gArgs.AddArg("-uiplatform", strprintf("Select platform to customize UI for (one of windows, macosx, other; default: %s)", BitcoinGUI::DEFAULT_UIPLATFORM), true, OptionsCategory::GUI); +} + #ifndef BITCOIN_QT_TEST int main(int argc, char *argv[]) { @@ -538,13 +569,9 @@ int main(int argc, char *argv[]) std::unique_ptr<interfaces::Node> node = interfaces::MakeNode(); - /// 1. Parse command-line options. These take precedence over anything else. - // Command-line options take precedence: - node->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) + /// 1. Basic Qt initialization (not dependent on parameters or configuration) #if QT_VERSION < 0x050000 // Internal string conversion is all UTF-8 QTextCodec::setCodecForTr(QTextCodec::codecForName("UTF-8")); @@ -579,6 +606,23 @@ int main(int argc, char *argv[]) // IMPORTANT if it is no longer a typedef use the normal variant above qRegisterMetaType< CAmount >("CAmount"); qRegisterMetaType< std::function<void(void)> >("std::function<void(void)>"); +#ifdef ENABLE_WALLET + qRegisterMetaType<WalletModel*>("WalletModel*"); +#endif + + /// 2. Parse command-line options. We do this after qt in order to show an error if there are problems parsing these + // Command-line options take precedence: + node->setupServerArgs(); + SetupUIArgs(); + std::string error; + if (!node->parseParameters(argc, argv, error)) { + QMessageBox::critical(0, QObject::tr(PACKAGE_NAME), + QObject::tr("Error parsing command line arguments: %1.").arg(QString::fromStdString(error))); + return EXIT_FAILURE; + } + + // Now that the QApplication is setup and we have parsed our parameters, we can set the platform style + app.setupPlatformStyle(); /// 3. Application identification // must be set before OptionsModel is initialized or translations are loaded, @@ -615,11 +659,9 @@ int main(int argc, char *argv[]) QObject::tr("Error: Specified data directory \"%1\" does not exist.").arg(QString::fromStdString(gArgs.GetArg("-datadir", "")))); return EXIT_FAILURE; } - try { - node->readConfigFile(gArgs.GetArg("-conf", BITCOIN_CONF_FILENAME)); - } catch (const std::exception& e) { + if (!node->readConfigFiles(error)) { QMessageBox::critical(0, QObject::tr(PACKAGE_NAME), - QObject::tr("Error: Cannot parse configuration file: %1. Only use key=value syntax.").arg(e.what())); + QObject::tr("Error: Cannot parse configuration file: %1.").arg(QString::fromStdString(error))); return EXIT_FAILURE; } diff --git a/src/qt/bitcoin.qrc b/src/qt/bitcoin.qrc index 451d391237..fddc2a5685 100644 --- a/src/qt/bitcoin.qrc +++ b/src/qt/bitcoin.qrc @@ -53,6 +53,7 @@ <file alias="hd_enabled">res/icons/hd_enabled.png</file> <file alias="hd_disabled">res/icons/hd_disabled.png</file> <file alias="network_disabled">res/icons/network_disabled.png</file> + <file alias="proxy">res/icons/proxy.png</file> </qresource> <qresource prefix="/movies"> <file alias="spinner-000">res/movies/spinner-000.png</file> diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp index e8307ff125..68a9dc4c27 100644 --- a/src/qt/bitcoinamountfield.cpp +++ b/src/qt/bitcoinamountfield.cpp @@ -197,7 +197,7 @@ BitcoinAmountField::BitcoinAmountField(QWidget *parent) : amount = new AmountSpinBox(this); amount->setLocale(QLocale::c()); amount->installEventFilter(this); - amount->setMaximumWidth(170); + amount->setMaximumWidth(240); QHBoxLayout *layout = new QHBoxLayout(this); layout->addWidget(amount); diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index aed5374a7d..9f5ea02e14 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -83,6 +83,7 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty unitDisplayControl(0), labelWalletEncryptionIcon(0), labelWalletHDStatusIcon(0), + labelProxyIcon(0), connectionsControl(0), labelBlocksIcon(0), progressBarLabel(0), @@ -201,6 +202,7 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty unitDisplayControl = new UnitDisplayStatusBarControl(platformStyle); labelWalletEncryptionIcon = new QLabel(); labelWalletHDStatusIcon = new QLabel(); + labelProxyIcon = new QLabel(); connectionsControl = new GUIUtil::ClickableLabel(); labelBlocksIcon = new GUIUtil::ClickableLabel(); if(enableWallet) @@ -211,6 +213,7 @@ BitcoinGUI::BitcoinGUI(interfaces::Node& node, const PlatformStyle *_platformSty frameBlocksLayout->addWidget(labelWalletEncryptionIcon); frameBlocksLayout->addWidget(labelWalletHDStatusIcon); } + frameBlocksLayout->addWidget(labelProxyIcon); frameBlocksLayout->addStretch(); frameBlocksLayout->addWidget(connectionsControl); frameBlocksLayout->addStretch(); @@ -473,7 +476,7 @@ void BitcoinGUI::createToolBars() toolbar->addWidget(spacer); m_wallet_selector = new QComboBox(); - connect(m_wallet_selector, SIGNAL(currentIndexChanged(const QString&)), this, SLOT(setCurrentWallet(const QString&))); + connect(m_wallet_selector, SIGNAL(currentIndexChanged(int)), this, SLOT(setCurrentWalletBySelectorIndex(int))); #endif } } @@ -503,6 +506,9 @@ void BitcoinGUI::setClientModel(ClientModel *_clientModel) connect(_clientModel, SIGNAL(showProgress(QString,int)), this, SLOT(showProgress(QString,int))); rpcConsole->setClientModel(_clientModel); + + updateProxyIcon(); + #ifdef ENABLE_WALLET if(walletFrame) { @@ -546,8 +552,9 @@ bool BitcoinGUI::addWallet(WalletModel *walletModel) if(!walletFrame) return false; const QString name = walletModel->getWalletName(); + QString display_name = name.isEmpty() ? "["+tr("default wallet")+"]" : name; setWalletActionsEnabled(true); - m_wallet_selector->addItem(name); + m_wallet_selector->addItem(display_name, name); if (m_wallet_selector->count() == 2) { m_wallet_selector_label = new QLabel(); m_wallet_selector_label->setText(tr("Wallet:") + " "); @@ -566,6 +573,12 @@ bool BitcoinGUI::setCurrentWallet(const QString& name) return walletFrame->setCurrentWallet(name); } +bool BitcoinGUI::setCurrentWalletBySelectorIndex(int index) +{ + QString internal_name = m_wallet_selector->itemData(index).toString(); + return setCurrentWallet(internal_name); +} + void BitcoinGUI::removeAllWallets() { if(!walletFrame) @@ -1125,6 +1138,24 @@ void BitcoinGUI::updateWalletStatus() } #endif // ENABLE_WALLET +void BitcoinGUI::updateProxyIcon() +{ + std::string ip_port; + bool proxy_enabled = clientModel->getProxyInfo(ip_port); + + if (proxy_enabled) { + if (labelProxyIcon->pixmap() == 0) { + QString ip_port_q = QString::fromStdString(ip_port); + labelProxyIcon->setPixmap(platformStyle->SingleColorIcon(":/icons/proxy").pixmap(STATUSBAR_ICONSIZE, STATUSBAR_ICONSIZE)); + labelProxyIcon->setToolTip(tr("Proxy is <b>enabled</b>: %1").arg(ip_port_q)); + } else { + labelProxyIcon->show(); + } + } else { + labelProxyIcon->hide(); + } +} + void BitcoinGUI::showNormalIfMinimized(bool fToggleHidden) { if(!clientModel) diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index e59c71cd4f..89c1c73a79 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -92,6 +92,7 @@ private: UnitDisplayStatusBarControl *unitDisplayControl; QLabel *labelWalletEncryptionIcon; QLabel *labelWalletHDStatusIcon; + QLabel *labelProxyIcon; QLabel *connectionsControl; QLabel *labelBlocksIcon; QLabel *progressBarLabel; @@ -185,6 +186,7 @@ public Q_SLOTS: #ifdef ENABLE_WALLET bool setCurrentWallet(const QString& name); + bool setCurrentWalletBySelectorIndex(int index); /** Set the UI status indicators based on the currently selected wallet. */ void updateWalletStatus(); @@ -209,6 +211,10 @@ public Q_SLOTS: void incomingTransaction(const QString& date, int unit, const CAmount& amount, const QString& type, const QString& address, const QString& label, const QString& walletName); #endif // ENABLE_WALLET +private: + /** Set the proxy-enabled icon as shown in the UI. */ + void updateProxyIcon(); + private Q_SLOTS: #ifdef ENABLE_WALLET /** Switch to overview (home) page */ diff --git a/src/qt/bitcoinunits.cpp b/src/qt/bitcoinunits.cpp index 9df05d2a13..30625f419a 100644 --- a/src/qt/bitcoinunits.cpp +++ b/src/qt/bitcoinunits.cpp @@ -20,6 +20,7 @@ QList<BitcoinUnits::Unit> BitcoinUnits::availableUnits() unitlist.append(BTC); unitlist.append(mBTC); unitlist.append(uBTC); + unitlist.append(SAT); return unitlist; } @@ -30,6 +31,7 @@ bool BitcoinUnits::valid(int unit) case BTC: case mBTC: case uBTC: + case SAT: return true; default: return false; @@ -43,6 +45,7 @@ QString BitcoinUnits::longName(int unit) case BTC: return QString("BTC"); case mBTC: return QString("mBTC"); case uBTC: return QString::fromUtf8("µBTC (bits)"); + case SAT: return QString("Satoshi (sat)"); default: return QString("???"); } } @@ -52,7 +55,8 @@ QString BitcoinUnits::shortName(int unit) switch(unit) { case uBTC: return QString::fromUtf8("bits"); - default: return longName(unit); + case SAT: return QString("sat"); + default: return longName(unit); } } @@ -63,6 +67,7 @@ QString BitcoinUnits::description(int unit) case BTC: return QString("Bitcoins"); case mBTC: return QString("Milli-Bitcoins (1 / 1" THIN_SP_UTF8 "000)"); case uBTC: return QString("Micro-Bitcoins (bits) (1 / 1" THIN_SP_UTF8 "000" THIN_SP_UTF8 "000)"); + case SAT: return QString("Satoshi (sat) (1 / 100" THIN_SP_UTF8 "000" THIN_SP_UTF8 "000)"); default: return QString("???"); } } @@ -71,10 +76,11 @@ qint64 BitcoinUnits::factor(int unit) { switch(unit) { - case BTC: return 100000000; + case BTC: return 100000000; case mBTC: return 100000; case uBTC: return 100; - default: return 100000000; + case SAT: return 1; + default: return 100000000; } } @@ -85,6 +91,7 @@ int BitcoinUnits::decimals(int unit) case BTC: return 8; case mBTC: return 5; case uBTC: return 2; + case SAT: return 0; default: return 0; } } @@ -100,9 +107,7 @@ QString BitcoinUnits::format(int unit, const CAmount& nIn, bool fPlus, Separator int num_decimals = decimals(unit); qint64 n_abs = (n > 0 ? n : -n); qint64 quotient = n_abs / coin; - qint64 remainder = n_abs % coin; QString quotient_str = QString::number(quotient); - QString remainder_str = QString::number(remainder).rightJustified(num_decimals, '0'); // Use SI-style thin space separators as these are locale independent and can't be // confused with the decimal marker. @@ -116,7 +121,14 @@ QString BitcoinUnits::format(int unit, const CAmount& nIn, bool fPlus, Separator quotient_str.insert(0, '-'); else if (fPlus && n > 0) quotient_str.insert(0, '+'); - return quotient_str + QString(".") + remainder_str; + + if (num_decimals > 0) { + qint64 remainder = n_abs % coin; + QString remainder_str = QString::number(remainder).rightJustified(num_decimals, '0'); + return quotient_str + QString(".") + remainder_str; + } else { + return quotient_str; + } } diff --git a/src/qt/bitcoinunits.h b/src/qt/bitcoinunits.h index 310f651815..9b01b4678a 100644 --- a/src/qt/bitcoinunits.h +++ b/src/qt/bitcoinunits.h @@ -58,7 +58,8 @@ public: { BTC, mBTC, - uBTC + uBTC, + SAT }; enum SeparatorStyle diff --git a/src/qt/clientmodel.cpp b/src/qt/clientmodel.cpp index 37fd06ccc9..a623771aa0 100644 --- a/src/qt/clientmodel.cpp +++ b/src/qt/clientmodel.cpp @@ -17,6 +17,7 @@ #include <interfaces/node.h> #include <validation.h> #include <net.h> +#include <netbase.h> #include <txmempool.h> #include <ui_interface.h> #include <util.h> @@ -268,3 +269,13 @@ void ClientModel::unsubscribeFromCoreSignals() m_handler_notify_block_tip->disconnect(); m_handler_notify_header_tip->disconnect(); } + +bool ClientModel::getProxyInfo(std::string& ip_port) const +{ + proxyType ipv4, ipv6; + if (m_node.getProxy((Network) 1, ipv4) && m_node.getProxy((Network) 2, ipv6)) { + ip_port = ipv4.proxy.ToStringIPPort(); + return true; + } + return false; +} diff --git a/src/qt/clientmodel.h b/src/qt/clientmodel.h index a609222f7d..9d4fa74b7a 100644 --- a/src/qt/clientmodel.h +++ b/src/qt/clientmodel.h @@ -70,6 +70,8 @@ public: QString formatClientStartupTime() const; QString dataDir() const; + bool getProxyInfo(std::string& ip_port) const; + // caches for the best header mutable std::atomic<int> cachedBestHeaderHeight; mutable std::atomic<int64_t> cachedBestHeaderTime; diff --git a/src/qt/forms/receivecoinsdialog.ui b/src/qt/forms/receivecoinsdialog.ui index 09fb435a58..2f916d0b44 100644 --- a/src/qt/forms/receivecoinsdialog.ui +++ b/src/qt/forms/receivecoinsdialog.ui @@ -206,10 +206,10 @@ <enum>Qt::StrongFocus</enum> </property> <property name="toolTip"> - <string>Bech32 addresses (BIP-173) are cheaper to spend from and offer better protection against typos. When unchecked a P2SH wrapped SegWit address will be created, compatible with older wallets.</string> + <string>Native segwit addresses (aka Bech32 or BIP-173) reduce your transaction fees later on and offer better protection against typos, but old wallets don't support them. When unchecked, an address compatible with older wallets will be created instead.</string> </property> <property name="text"> - <string>Generate Bech32 address</string> + <string>Generate native segwit (Bech32) address</string> </property> </widget> </item> diff --git a/src/qt/receivecoinsdialog.cpp b/src/qt/receivecoinsdialog.cpp index 70e11f0296..e458a52856 100644 --- a/src/qt/receivecoinsdialog.cpp +++ b/src/qt/receivecoinsdialog.cpp @@ -94,14 +94,11 @@ void ReceiveCoinsDialog::setModel(WalletModel *_model) // Last 2 columns are set by the columnResizingFixer, when the table geometry is ready. columnResizingFixer = new GUIUtil::TableViewLastColumnResizingFixer(tableView, AMOUNT_MINIMUM_COLUMN_WIDTH, DATE_COLUMN_WIDTH, this); - // configure bech32 checkbox, disable if launched with legacy as default: if (model->wallet().getDefaultAddressType() == OutputType::BECH32) { ui->useBech32->setCheckState(Qt::Checked); } else { ui->useBech32->setCheckState(Qt::Unchecked); } - - ui->useBech32->setVisible(model->wallet().getDefaultAddressType() != OutputType::LEGACY); } } @@ -144,9 +141,14 @@ void ReceiveCoinsDialog::on_receiveButton_clicked() QString address; QString label = ui->reqLabel->text(); /* Generate new receiving address */ - OutputType address_type = model->wallet().getDefaultAddressType(); - if (address_type != OutputType::LEGACY) { - address_type = ui->useBech32->isChecked() ? OutputType::BECH32 : OutputType::P2SH_SEGWIT; + OutputType address_type; + if (ui->useBech32->isChecked()) { + address_type = OutputType::BECH32; + } else { + address_type = model->wallet().getDefaultAddressType(); + if (address_type == OutputType::BECH32) { + address_type = OutputType::P2SH_SEGWIT; + } } address = model->getAddressTableModel()->addRow(AddressTableModel::Receive, label, "", address_type); SendCoinsRecipient info(address, label, diff --git a/src/qt/res/icons/proxy.png b/src/qt/res/icons/proxy.png Binary files differnew file mode 100644 index 0000000000..67c552d0de --- /dev/null +++ b/src/qt/res/icons/proxy.png diff --git a/src/qt/res/src/proxy.svg b/src/qt/res/src/proxy.svg new file mode 100644 index 0000000000..b42fa63948 --- /dev/null +++ b/src/qt/res/src/proxy.svg @@ -0,0 +1,70 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- Created with Inkscape (http://www.inkscape.org/) --> + +<svg + xmlns:dc="http://purl.org/dc/elements/1.1/" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns="http://www.w3.org/2000/svg" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + width="128px" + height="128px" + id="svg2991" + version="1.1" + inkscape:version="0.48.4 r9939" + sodipodi:docname="proxy.svg" + inkscape:export-xdpi="90" + inkscape:export-ydpi="90"> + <defs + id="defs2993" /> + <sodipodi:namedview + id="base" + pagecolor="#ffffff" + bordercolor="#666666" + borderopacity="1.0" + inkscape:pageopacity="0.0" + inkscape:pageshadow="2" + inkscape:zoom="3.8890872" + inkscape:cx="4.0410731" + inkscape:cy="31.916897" + inkscape:current-layer="layer1" + showgrid="true" + inkscape:document-units="px" + inkscape:grid-bbox="true" + inkscape:window-width="1920" + inkscape:window-height="1056" + inkscape:window-x="0" + inkscape:window-y="24" + inkscape:window-maximized="1" /> + <metadata + id="metadata2996"> + <rdf:RDF> + <cc:Work + rdf:about=""> + <dc:format>image/svg+xml</dc:format> + <dc:type + rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> + <dc:title></dc:title> + </cc:Work> + </rdf:RDF> + </metadata> + <g + id="layer1" + inkscape:label="Layer 1" + inkscape:groupmode="layer"> + <text + xml:space="preserve" + style="font-size:40px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Sans" + x="26.981934" + y="110.45972" + id="text2999" + sodipodi:linespacing="125%"><tspan + sodipodi:role="line" + id="tspan3001" + x="26.981934" + y="110.45972" + style="font-size:111px">P</tspan></text> + </g> +</svg> diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index 7924840d0b..4550ae9396 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -70,6 +70,7 @@ namespace { const QStringList historyFilter = QStringList() << "importprivkey" << "importmulti" + << "sethdseed" << "signmessagewithprivkey" << "signrawtransaction" << "signrawtransactionwithkey" @@ -311,7 +312,7 @@ bool RPCConsole::RPCParseCommandLine(interfaces::Node* node, std::string &strRes std::string method = stack.back()[0]; std::string uri; #ifdef ENABLE_WALLET - if (walletID && !walletID->empty()) { + if (walletID) { QByteArray encodedName = QUrl::toPercentEncoding(QString::fromStdString(*walletID)); uri = "/wallet/"+std::string(encodedName.constData(), encodedName.length()); } @@ -424,7 +425,7 @@ void RPCExecutor::request(const QString &command, const QString &walletID) return; } std::string wallet_id = walletID.toStdString(); - if(!RPCConsole::RPCExecuteCommandLine(m_node, result, executableCommand, nullptr, &wallet_id)) + if (!RPCConsole::RPCExecuteCommandLine(m_node, result, executableCommand, nullptr, walletID.isNull() ? nullptr : &wallet_id)) { Q_EMIT reply(RPCConsole::CMD_ERROR, QString("Parse error: unbalanced ' or \"")); return; @@ -701,7 +702,8 @@ void RPCConsole::addWallet(WalletModel * const walletModel) { const QString name = walletModel->getWalletName(); // use name for text and internal data object (to allow to move to a wallet id later) - ui->WalletSelector->addItem(name, name); + QString display_name = name.isEmpty() ? "["+tr("default wallet")+"]" : name; + ui->WalletSelector->addItem(display_name, name); if (ui->WalletSelector->count() == 2 && !isVisible()) { // First wallet added, set to default so long as the window isn't presently visible (and potentially in use) ui->WalletSelector->setCurrentIndex(1); @@ -908,7 +910,7 @@ void RPCConsole::on_lineEdit_returnPressed() } if (m_last_wallet_id != walletID) { - if (walletID.isEmpty()) { + if (walletID.isNull()) { message(CMD_REQUEST, tr("Executing command without any wallet")); } else { message(CMD_REQUEST, tr("Executing command using \"%1\" wallet").arg(walletID)); diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 261ab7a948..5c946f7c77 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -288,44 +288,60 @@ void SendCoinsDialog::on_sendButton_clicked() address.append("</span>"); QString recipientElement; + recipientElement = "<br />"; if (!rcp.paymentRequest.IsInitialized()) // normal payment { if(rcp.label.length() > 0) // label with address { - recipientElement = tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(rcp.label)); + recipientElement.append(tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(rcp.label))); recipientElement.append(QString(" (%1)").arg(address)); } else // just address { - recipientElement = tr("%1 to %2").arg(amount, address); + recipientElement.append(tr("%1 to %2").arg(amount, address)); } } else if(!rcp.authenticatedMerchant.isEmpty()) // authenticated payment request { - recipientElement = tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(rcp.authenticatedMerchant)); + recipientElement.append(tr("%1 to %2").arg(amount, GUIUtil::HtmlEscape(rcp.authenticatedMerchant))); } else // unauthenticated payment request { - recipientElement = tr("%1 to %2").arg(amount, address); + recipientElement.append(tr("%1 to %2").arg(amount, address)); } formatted.append(recipientElement); } QString questionString = tr("Are you sure you want to send?"); - questionString.append("<br /><br />%1"); + questionString.append("<br /><span style='font-size:10pt;'>"); + questionString.append(tr("Please, review your transaction.")); + questionString.append("</span><br />%1"); if(txFee > 0) { // append fee string if a fee is required - questionString.append("<hr /><span style='color:#aa0000;'>"); - questionString.append(BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), txFee)); - questionString.append("</span> "); - questionString.append(tr("added as transaction fee")); + questionString.append("<hr /><b>"); + questionString.append(tr("Transaction fee")); + questionString.append("</b>"); // append transaction size - questionString.append(" (" + QString::number((double)currentTransaction.getTransactionSize() / 1000) + " kB)"); + questionString.append(" (" + QString::number((double)currentTransaction.getTransactionSize() / 1000) + " kB): "); + + // append transaction fee value + questionString.append("<span style='color:#aa0000; font-weight:bold;'>"); + questionString.append(BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), txFee)); + questionString.append("</span><br />"); + + // append RBF message according to transaction's signalling + questionString.append("<span style='font-size:10pt; font-weight:normal;'>"); + if (ui->optInRBF->isChecked()) { + questionString.append(tr("You can increase the fee later (signals Replace-By-Fee, BIP-125).")); + } else { + questionString.append(tr("Not signalling Replace-By-Fee, BIP-125.")); + } + questionString.append("</span>"); } // add total amount in all subdivision units @@ -337,19 +353,10 @@ void SendCoinsDialog::on_sendButton_clicked() if(u != model->getOptionsModel()->getDisplayUnit()) alternativeUnits.append(BitcoinUnits::formatHtmlWithUnit(u, totalAmount)); } - questionString.append(tr("Total Amount %1") + questionString.append(QString("<b>%1</b>: <b>%2</b>").arg(tr("Total Amount")) .arg(BitcoinUnits::formatHtmlWithUnit(model->getOptionsModel()->getDisplayUnit(), totalAmount))); - questionString.append(QString("<span style='font-size:10pt;font-weight:normal;'><br />(=%1)</span>") - .arg(alternativeUnits.join(" " + tr("or") + "<br />"))); - - questionString.append("<hr /><span>"); - if (ui->optInRBF->isChecked()) { - questionString.append(tr("You can increase the fee later (signals Replace-By-Fee, BIP-125).")); - } else { - questionString.append(tr("Not signalling Replace-By-Fee, BIP-125.")); - } - questionString.append("</span>"); - + questionString.append(QString("<br /><span style='font-size:10pt; font-weight:normal;'>(=%1)</span>") + .arg(alternativeUnits.join(" " + tr("or") + " "))); SendConfirmationDialog confirmationDialog(tr("Confirm send coins"), questionString.arg(formatted.join("<br />")), SEND_CONFIRM_DELAY, this); diff --git a/src/qt/test/addressbooktests.cpp b/src/qt/test/addressbooktests.cpp index 0c2e7ae71d..c3d33c76d4 100644 --- a/src/qt/test/addressbooktests.cpp +++ b/src/qt/test/addressbooktests.cpp @@ -56,15 +56,15 @@ void EditAddressAndSubmit( void TestAddAddressesToSendBook() { TestChain100Setup test; - CWallet wallet("mock", WalletDatabase::CreateMock()); + std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>("mock", WalletDatabase::CreateMock()); bool firstRun; - wallet.LoadWallet(firstRun); + wallet->LoadWallet(firstRun); auto build_address = [&wallet]() { CKey key; key.MakeNewKey(true); CTxDestination dest(GetDestinationForKey( - key.GetPubKey(), wallet.m_default_address_type)); + key.GetPubKey(), wallet->m_default_address_type)); return std::make_pair(dest, QString::fromStdString(EncodeDestination(dest))); }; @@ -87,13 +87,13 @@ void TestAddAddressesToSendBook() std::tie(std::ignore, new_address) = build_address(); { - LOCK(wallet.cs_wallet); - wallet.SetAddressBook(r_key_dest, r_label.toStdString(), "receive"); - wallet.SetAddressBook(s_key_dest, s_label.toStdString(), "send"); + LOCK(wallet->cs_wallet); + wallet->SetAddressBook(r_key_dest, r_label.toStdString(), "receive"); + wallet->SetAddressBook(s_key_dest, s_label.toStdString(), "send"); } auto check_addbook_size = [&wallet](int expected_size) { - QCOMPARE(static_cast<int>(wallet.mapAddressBook.size()), expected_size); + QCOMPARE(static_cast<int>(wallet->mapAddressBook.size()), expected_size); }; // We should start with the two addresses we added earlier and nothing else. @@ -103,9 +103,9 @@ void TestAddAddressesToSendBook() std::unique_ptr<const PlatformStyle> platformStyle(PlatformStyle::instantiate("other")); auto node = interfaces::MakeNode(); OptionsModel optionsModel(*node); - AddWallet(&wallet); + AddWallet(wallet); WalletModel walletModel(std::move(node->getWallets()[0]), *node, platformStyle.get(), &optionsModel); - RemoveWallet(&wallet); + RemoveWallet(wallet); EditAddressDialog editAddressDialog(EditAddressDialog::NewSendingAddress); editAddressDialog.setModel(walletModel.getAddressTableModel()); diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp index a09d98dfe5..33c49dc7cb 100644 --- a/src/qt/test/wallettests.cpp +++ b/src/qt/test/wallettests.cpp @@ -144,21 +144,21 @@ void TestGUI() for (int i = 0; i < 5; ++i) { test.CreateAndProcessBlock({}, GetScriptForRawPubKey(test.coinbaseKey.GetPubKey())); } - CWallet wallet("mock", WalletDatabase::CreateMock()); + std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>("mock", WalletDatabase::CreateMock()); bool firstRun; - wallet.LoadWallet(firstRun); + wallet->LoadWallet(firstRun); { - LOCK(wallet.cs_wallet); - wallet.SetAddressBook(GetDestinationForKey(test.coinbaseKey.GetPubKey(), wallet.m_default_address_type), "", "receive"); - wallet.AddKeyPubKey(test.coinbaseKey, test.coinbaseKey.GetPubKey()); + LOCK(wallet->cs_wallet); + wallet->SetAddressBook(GetDestinationForKey(test.coinbaseKey.GetPubKey(), wallet->m_default_address_type), "", "receive"); + wallet->AddKeyPubKey(test.coinbaseKey, test.coinbaseKey.GetPubKey()); } { LOCK(cs_main); - WalletRescanReserver reserver(&wallet); + WalletRescanReserver reserver(wallet.get()); reserver.reserve(); - wallet.ScanForWalletTransactions(chainActive.Genesis(), nullptr, reserver, true); + wallet->ScanForWalletTransactions(chainActive.Genesis(), nullptr, reserver, true); } - wallet.SetBroadcastTransactions(true); + wallet->SetBroadcastTransactions(true); // Create widgets for sending coins and listing transactions. std::unique_ptr<const PlatformStyle> platformStyle(PlatformStyle::instantiate("other")); @@ -166,17 +166,17 @@ void TestGUI() TransactionView transactionView(platformStyle.get()); auto node = interfaces::MakeNode(); OptionsModel optionsModel(*node); - AddWallet(&wallet); + AddWallet(wallet); WalletModel walletModel(std::move(node->getWallets().back()), *node, platformStyle.get(), &optionsModel); - RemoveWallet(&wallet); + RemoveWallet(wallet); sendCoinsDialog.setModel(&walletModel); transactionView.setModel(&walletModel); // Send two transactions, and verify they are added to transaction list. TransactionTableModel* transactionTableModel = walletModel.getTransactionTableModel(); QCOMPARE(transactionTableModel->rowCount({}), 105); - uint256 txid1 = SendCoins(wallet, sendCoinsDialog, CKeyID(), 5 * COIN, false /* rbf */); - uint256 txid2 = SendCoins(wallet, sendCoinsDialog, CKeyID(), 10 * COIN, true /* rbf */); + uint256 txid1 = SendCoins(*wallet.get(), sendCoinsDialog, CKeyID(), 5 * COIN, false /* rbf */); + uint256 txid2 = SendCoins(*wallet.get(), sendCoinsDialog, CKeyID(), 10 * COIN, true /* rbf */); QCOMPARE(transactionTableModel->rowCount({}), 107); QVERIFY(FindTx(*transactionTableModel, txid1).isValid()); QVERIFY(FindTx(*transactionTableModel, txid2).isValid()); diff --git a/src/qt/utilitydialog.cpp b/src/qt/utilitydialog.cpp index d5b98486ae..1da25b0761 100644 --- a/src/qt/utilitydialog.cpp +++ b/src/qt/utilitydialog.cpp @@ -70,29 +70,15 @@ HelpMessageDialog::HelpMessageDialog(interfaces::Node& node, QWidget *parent, bo ui->helpMessage->setVisible(false); } else { setWindowTitle(tr("Command-line options")); - QString header = tr("Usage:") + "\n" + - " bitcoin-qt [" + tr("command-line options") + "] " + "\n"; + QString header = "Usage:\n" + " bitcoin-qt [command-line options] \n"; QTextCursor cursor(ui->helpMessage->document()); cursor.insertText(version); cursor.insertBlock(); cursor.insertText(header); cursor.insertBlock(); - std::string strUsage = node.helpMessage(HelpMessageMode::BITCOIN_QT); - const bool showDebug = gArgs.GetBoolArg("-help-debug", false); - strUsage += HelpMessageGroup(tr("UI Options:").toStdString()); - if (showDebug) { - strUsage += HelpMessageOpt("-allowselfsignedrootcertificates", strprintf("Allow self signed root certificates (default: %u)", DEFAULT_SELFSIGNED_ROOTCERTS)); - } - strUsage += HelpMessageOpt("-choosedatadir", strprintf(tr("Choose data directory on startup (default: %u)").toStdString(), DEFAULT_CHOOSE_DATADIR)); - strUsage += HelpMessageOpt("-lang=<lang>", tr("Set language, for example \"de_DE\" (default: system locale)").toStdString()); - strUsage += HelpMessageOpt("-min", tr("Start minimized").toStdString()); - strUsage += HelpMessageOpt("-resetguisettings", tr("Reset all settings changed in the GUI").toStdString()); - strUsage += HelpMessageOpt("-rootcertificates=<file>", tr("Set SSL root certificates for payment request (default: -system-)").toStdString()); - strUsage += HelpMessageOpt("-splash", strprintf(tr("Show splash screen on startup (default: %u)").toStdString(), DEFAULT_SPLASHSCREEN)); - if (showDebug) { - strUsage += HelpMessageOpt("-uiplatform", strprintf("Select platform to customize UI for (one of windows, macosx, other; default: %s)", BitcoinGUI::DEFAULT_UIPLATFORM)); - } + std::string strUsage = gArgs.GetHelpMessage(); QString coreOptions = QString::fromStdString(strUsage); text = version + "\n" + header + "\n" + coreOptions; diff --git a/src/qt/walletframe.cpp b/src/qt/walletframe.cpp index 5b13353d7b..eb0eba21ef 100644 --- a/src/qt/walletframe.cpp +++ b/src/qt/walletframe.cpp @@ -57,8 +57,13 @@ bool WalletFrame::addWallet(WalletModel *walletModel) walletView->setWalletModel(walletModel); walletView->showOutOfSyncWarning(bOutOfSync); - /* TODO we should goto the currently selected page once dynamically adding wallets is supported */ - walletView->gotoOverviewPage(); + WalletView* current_wallet_view = currentWalletView(); + if (current_wallet_view) { + walletView->setCurrentIndex(current_wallet_view->currentIndex()); + } else { + walletView->gotoOverviewPage(); + } + walletStack->addWidget(walletView); mapWalletViews[name] = walletView; diff --git a/src/random.cpp b/src/random.cpp index 4ba86e4e7a..fee6c2d92a 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -11,14 +11,15 @@ #include <compat.h> // for Windows API #include <wincrypt.h> #endif -#include <util.h> // for LogPrint() -#include <utilstrencodings.h> // for GetTime() +#include <logging.h> // for LogPrint() +#include <utiltime.h> // for GetTime() #include <stdlib.h> #include <chrono> #include <thread> #ifndef WIN32 +#include <fcntl.h> #include <sys/time.h> #endif @@ -33,6 +34,7 @@ #include <sys/random.h> #endif #ifdef HAVE_SYSCTL_ARND +#include <utilstrencodings.h> // for ARRAYLEN #include <sys/sysctl.h> #endif diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 238d8c9d95..24fb522e60 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -13,6 +13,7 @@ #include <consensus/validation.h> #include <validation.h> #include <core_io.h> +#include <index/txindex.h> #include <policy/feerate.h> #include <policy/policy.h> #include <primitives/transaction.h> @@ -31,6 +32,7 @@ #include <univalue.h> +#include <boost/algorithm/string.hpp> #include <boost/thread/thread.hpp> // boost::thread::interrupt #include <memory> @@ -737,6 +739,25 @@ static UniValue getblockheader(const JSONRPCRequest& request) return blockheaderToJSON(pblockindex); } +static CBlock GetBlockChecked(const CBlockIndex* pblockindex) +{ + CBlock block; + if (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0) { + throw JSONRPCError(RPC_MISC_ERROR, "Block not available (pruned data)"); + } + + if (!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus())) { + // Block not found on disk. This could be because we have the block + // header in our index but don't have the block (for example if a + // non-whitelisted node sends us an unrequested long chain of valid + // blocks, we add the headers to our index, but don't accept the + // block). + throw JSONRPCError(RPC_MISC_ERROR, "Block not found on disk"); + } + + return block; +} + static UniValue getblock(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() < 1 || request.params.size() > 2) @@ -805,17 +826,7 @@ static UniValue getblock(const JSONRPCRequest& request) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } - CBlock block; - if (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0) - throw JSONRPCError(RPC_MISC_ERROR, "Block not available (pruned data)"); - - if (!ReadBlockFromDisk(block, pblockindex, Params().GetConsensus())) - // Block not found on disk. This could be because we have the block - // header in our index but don't have the block (for example if a - // non-whitelisted node sends us an unrequested long chain of valid - // blocks, we add the headers to our index, but don't accept the - // block). - throw JSONRPCError(RPC_MISC_ERROR, "Block not found on disk"); + const CBlock block = GetBlockChecked(pblockindex); if (verbosity <= 0) { @@ -1614,6 +1625,284 @@ static UniValue getchaintxstats(const JSONRPCRequest& request) return ret; } +template<typename T> +static T CalculateTruncatedMedian(std::vector<T>& scores) +{ + size_t size = scores.size(); + if (size == 0) { + return 0; + } + + std::sort(scores.begin(), scores.end()); + if (size % 2 == 0) { + return (scores[size / 2 - 1] + scores[size / 2]) / 2; + } else { + return scores[size / 2]; + } +} + +template<typename T> +static inline bool SetHasKeys(const std::set<T>& set) {return false;} +template<typename T, typename Tk, typename... Args> +static inline bool SetHasKeys(const std::set<T>& set, const Tk& key, const Args&... args) +{ + return (set.count(key) != 0) || SetHasKeys(set, args...); +} + +// outpoint (needed for the utxo index) + nHeight + fCoinBase +static constexpr size_t PER_UTXO_OVERHEAD = sizeof(COutPoint) + sizeof(uint32_t) + sizeof(bool); + +static UniValue getblockstats(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() < 1 || request.params.size() > 4) { + throw std::runtime_error( + "getblockstats hash_or_height ( stats )\n" + "\nCompute per block statistics for a given window. All amounts are in satoshis.\n" + "It won't work for some heights with pruning.\n" + "It won't work without -txindex for utxo_size_inc, *fee or *feerate stats.\n" + "\nArguments:\n" + "1. \"hash_or_height\" (string or numeric, required) The block hash or height of the target block\n" + "2. \"stats\" (array, optional) Values to plot, by default all values (see result below)\n" + " [\n" + " \"height\", (string, optional) Selected statistic\n" + " \"time\", (string, optional) Selected statistic\n" + " ,...\n" + " ]\n" + "\nResult:\n" + "{ (json object)\n" + " \"avgfee\": xxxxx, (numeric) Average fee in the block\n" + " \"avgfeerate\": xxxxx, (numeric) Average feerate (in satoshis per virtual byte)\n" + " \"avgtxsize\": xxxxx, (numeric) Average transaction size\n" + " \"blockhash\": xxxxx, (string) The block hash (to check for potential reorgs)\n" + " \"height\": xxxxx, (numeric) The height of the block\n" + " \"ins\": xxxxx, (numeric) The number of inputs (excluding coinbase)\n" + " \"maxfee\": xxxxx, (numeric) Maximum fee in the block\n" + " \"maxfeerate\": xxxxx, (numeric) Maximum feerate (in satoshis per virtual byte)\n" + " \"maxtxsize\": xxxxx, (numeric) Maximum transaction size\n" + " \"medianfee\": xxxxx, (numeric) Truncated median fee in the block\n" + " \"medianfeerate\": xxxxx, (numeric) Truncated median feerate (in satoshis per virtual byte)\n" + " \"mediantime\": xxxxx, (numeric) The block median time past\n" + " \"mediantxsize\": xxxxx, (numeric) Truncated median transaction size\n" + " \"minfee\": xxxxx, (numeric) Minimum fee in the block\n" + " \"minfeerate\": xxxxx, (numeric) Minimum feerate (in satoshis per virtual byte)\n" + " \"mintxsize\": xxxxx, (numeric) Minimum transaction size\n" + " \"outs\": xxxxx, (numeric) The number of outputs\n" + " \"subsidy\": xxxxx, (numeric) The block subsidy\n" + " \"swtotal_size\": xxxxx, (numeric) Total size of all segwit transactions\n" + " \"swtotal_weight\": xxxxx, (numeric) Total weight of all segwit transactions divided by segwit scale factor (4)\n" + " \"swtxs\": xxxxx, (numeric) The number of segwit transactions\n" + " \"time\": xxxxx, (numeric) The block time\n" + " \"total_out\": xxxxx, (numeric) Total amount in all outputs (excluding coinbase and thus reward [ie subsidy + totalfee])\n" + " \"total_size\": xxxxx, (numeric) Total size of all non-coinbase transactions\n" + " \"total_weight\": xxxxx, (numeric) Total weight of all non-coinbase transactions divided by segwit scale factor (4)\n" + " \"totalfee\": xxxxx, (numeric) The fee total\n" + " \"txs\": xxxxx, (numeric) The number of transactions (excluding coinbase)\n" + " \"utxo_increase\": xxxxx, (numeric) The increase/decrease in the number of unspent outputs\n" + " \"utxo_size_inc\": xxxxx, (numeric) The increase/decrease in size for the utxo index (not discounting op_return and similar)\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("getblockstats", "1000 '[\"minfeerate\",\"avgfeerate\"]'") + + HelpExampleRpc("getblockstats", "1000 '[\"minfeerate\",\"avgfeerate\"]'") + ); + } + + LOCK(cs_main); + + CBlockIndex* pindex; + if (request.params[0].isNum()) { + const int height = request.params[0].get_int(); + const int current_tip = chainActive.Height(); + if (height < 0) { + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Target block height %d is negative", height)); + } + if (height > current_tip) { + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Target block height %d after current tip %d", height, current_tip)); + } + + pindex = chainActive[height]; + } else { + const std::string strHash = request.params[0].get_str(); + const uint256 hash(uint256S(strHash)); + pindex = LookupBlockIndex(hash); + if (!pindex) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); + } + if (!chainActive.Contains(pindex)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Block is not in chain %s", Params().NetworkIDString())); + } + } + + assert(pindex != nullptr); + + std::set<std::string> stats; + if (!request.params[1].isNull()) { + const UniValue stats_univalue = request.params[1].get_array(); + for (unsigned int i = 0; i < stats_univalue.size(); i++) { + const std::string stat = stats_univalue[i].get_str(); + stats.insert(stat); + } + } + + const CBlock block = GetBlockChecked(pindex); + + const bool do_all = stats.size() == 0; // Calculate everything if nothing selected (default) + const bool do_mediantxsize = do_all || stats.count("mediantxsize") != 0; + const bool do_medianfee = do_all || stats.count("medianfee") != 0; + const bool do_medianfeerate = do_all || stats.count("medianfeerate") != 0; + const bool loop_inputs = do_all || do_medianfee || do_medianfeerate || + SetHasKeys(stats, "utxo_size_inc", "totalfee", "avgfee", "avgfeerate", "minfee", "maxfee", "minfeerate", "maxfeerate"); + const bool loop_outputs = do_all || loop_inputs || stats.count("total_out"); + const bool do_calculate_size = do_mediantxsize || + SetHasKeys(stats, "total_size", "avgtxsize", "mintxsize", "maxtxsize", "swtotal_size"); + const bool do_calculate_weight = do_all || SetHasKeys(stats, "total_weight", "avgfeerate", "swtotal_weight", "avgfeerate", "medianfeerate", "minfeerate", "maxfeerate"); + const bool do_calculate_sw = do_all || SetHasKeys(stats, "swtxs", "swtotal_size", "swtotal_weight"); + + CAmount maxfee = 0; + CAmount maxfeerate = 0; + CAmount minfee = MAX_MONEY; + CAmount minfeerate = MAX_MONEY; + CAmount total_out = 0; + CAmount totalfee = 0; + int64_t inputs = 0; + int64_t maxtxsize = 0; + int64_t mintxsize = MAX_BLOCK_SERIALIZED_SIZE; + int64_t outputs = 0; + int64_t swtotal_size = 0; + int64_t swtotal_weight = 0; + int64_t swtxs = 0; + int64_t total_size = 0; + int64_t total_weight = 0; + int64_t utxo_size_inc = 0; + std::vector<CAmount> fee_array; + std::vector<CAmount> feerate_array; + std::vector<int64_t> txsize_array; + + for (const auto& tx : block.vtx) { + outputs += tx->vout.size(); + + CAmount tx_total_out = 0; + if (loop_outputs) { + for (const CTxOut& out : tx->vout) { + tx_total_out += out.nValue; + utxo_size_inc += GetSerializeSize(out, SER_NETWORK, PROTOCOL_VERSION) + PER_UTXO_OVERHEAD; + } + } + + if (tx->IsCoinBase()) { + continue; + } + + inputs += tx->vin.size(); // Don't count coinbase's fake input + total_out += tx_total_out; // Don't count coinbase reward + + int64_t tx_size = 0; + if (do_calculate_size) { + + tx_size = tx->GetTotalSize(); + if (do_mediantxsize) { + txsize_array.push_back(tx_size); + } + maxtxsize = std::max(maxtxsize, tx_size); + mintxsize = std::min(mintxsize, tx_size); + total_size += tx_size; + } + + int64_t weight = 0; + if (do_calculate_weight) { + weight = GetTransactionWeight(*tx); + total_weight += weight; + } + + if (do_calculate_sw && tx->HasWitness()) { + ++swtxs; + swtotal_size += tx_size; + swtotal_weight += weight; + } + + if (loop_inputs) { + + if (!g_txindex) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "One or more of the selected stats requires -txindex enabled"); + } + CAmount tx_total_in = 0; + for (const CTxIn& in : tx->vin) { + CTransactionRef tx_in; + uint256 hashBlock; + if (!GetTransaction(in.prevout.hash, tx_in, Params().GetConsensus(), hashBlock, false)) { + throw JSONRPCError(RPC_INTERNAL_ERROR, std::string("Unexpected internal error (tx index seems corrupt)")); + } + + CTxOut prevoutput = tx_in->vout[in.prevout.n]; + + tx_total_in += prevoutput.nValue; + utxo_size_inc -= GetSerializeSize(prevoutput, SER_NETWORK, PROTOCOL_VERSION) + PER_UTXO_OVERHEAD; + } + + CAmount txfee = tx_total_in - tx_total_out; + assert(MoneyRange(txfee)); + if (do_medianfee) { + fee_array.push_back(txfee); + } + maxfee = std::max(maxfee, txfee); + minfee = std::min(minfee, txfee); + totalfee += txfee; + + // New feerate uses satoshis per virtual byte instead of per serialized byte + CAmount feerate = weight ? (txfee * WITNESS_SCALE_FACTOR) / weight : 0; + if (do_medianfeerate) { + feerate_array.push_back(feerate); + } + maxfeerate = std::max(maxfeerate, feerate); + minfeerate = std::min(minfeerate, feerate); + } + } + + UniValue ret_all(UniValue::VOBJ); + ret_all.pushKV("avgfee", (block.vtx.size() > 1) ? totalfee / (block.vtx.size() - 1) : 0); + ret_all.pushKV("avgfeerate", total_weight ? (totalfee * WITNESS_SCALE_FACTOR) / total_weight : 0); // Unit: sat/vbyte + ret_all.pushKV("avgtxsize", (block.vtx.size() > 1) ? total_size / (block.vtx.size() - 1) : 0); + ret_all.pushKV("blockhash", pindex->GetBlockHash().GetHex()); + ret_all.pushKV("height", (int64_t)pindex->nHeight); + ret_all.pushKV("ins", inputs); + ret_all.pushKV("maxfee", maxfee); + ret_all.pushKV("maxfeerate", maxfeerate); + ret_all.pushKV("maxtxsize", maxtxsize); + ret_all.pushKV("medianfee", CalculateTruncatedMedian(fee_array)); + ret_all.pushKV("medianfeerate", CalculateTruncatedMedian(feerate_array)); + ret_all.pushKV("mediantime", pindex->GetMedianTimePast()); + ret_all.pushKV("mediantxsize", CalculateTruncatedMedian(txsize_array)); + ret_all.pushKV("minfee", (minfee == MAX_MONEY) ? 0 : minfee); + ret_all.pushKV("minfeerate", (minfeerate == MAX_MONEY) ? 0 : minfeerate); + ret_all.pushKV("mintxsize", mintxsize == MAX_BLOCK_SERIALIZED_SIZE ? 0 : mintxsize); + ret_all.pushKV("outs", outputs); + ret_all.pushKV("subsidy", GetBlockSubsidy(pindex->nHeight, Params().GetConsensus())); + ret_all.pushKV("swtotal_size", swtotal_size); + ret_all.pushKV("swtotal_weight", swtotal_weight); + ret_all.pushKV("swtxs", swtxs); + ret_all.pushKV("time", pindex->GetBlockTime()); + ret_all.pushKV("total_out", total_out); + ret_all.pushKV("total_size", total_size); + ret_all.pushKV("total_weight", total_weight); + ret_all.pushKV("totalfee", totalfee); + ret_all.pushKV("txs", (int64_t)block.vtx.size()); + ret_all.pushKV("utxo_increase", outputs - inputs); + ret_all.pushKV("utxo_size_inc", utxo_size_inc); + + if (do_all) { + return ret_all; + } + + UniValue ret(UniValue::VOBJ); + for (const std::string& stat : stats) { + const UniValue& value = ret_all[stat]; + if (value.isNull()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid selected statistic %s", stat)); + } + ret.pushKV(stat, value); + } + return ret; +} + static UniValue savemempool(const JSONRPCRequest& request) { if (request.fHelp || request.params.size() != 0) { @@ -1642,6 +1931,7 @@ static const CRPCCommand commands[] = // --------------------- ------------------------ ----------------------- ---------- { "blockchain", "getblockchaininfo", &getblockchaininfo, {} }, { "blockchain", "getchaintxstats", &getchaintxstats, {"nblocks", "blockhash"} }, + { "blockchain", "getblockstats", &getblockstats, {"hash_or_height", "stats"} }, { "blockchain", "getbestblockhash", &getbestblockhash, {} }, { "blockchain", "getblockcount", &getblockcount, {} }, { "blockchain", "getblock", &getblock, {"blockhash","verbosity|verbose"} }, diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index 34c41b3b6b..bb68f72ccc 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -38,6 +38,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "sendtoaddress", 5 , "replaceable" }, { "sendtoaddress", 6 , "conf_target" }, { "settxfee", 0, "amount" }, + { "sethdseed", 0, "newkeypool" }, { "getreceivedbyaddress", 1, "minconf" }, { "getreceivedbyaccount", 1, "minconf" }, { "getreceivedbylabel", 1, "minconf" }, @@ -122,6 +123,8 @@ static const CRPCConvertParam vRPCConvertParams[] = { "importmulti", 1, "options" }, { "verifychain", 0, "checklevel" }, { "verifychain", 1, "nblocks" }, + { "getblockstats", 0, "hash_or_height" }, + { "getblockstats", 1, "stats" }, { "pruneblockchain", 0, "height" }, { "keypoolrefill", 0, "newsize" }, { "getrawmempool", 0, "verbose" }, diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 45ec501b9d..203fac39e2 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -525,6 +525,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) // Need to update only after we know CreateNewBlock succeeded pindexPrev = pindexPrevNew; } + assert(pindexPrev); CBlock* pblock = &pblocktemplate->block; // pointer for convenience const Consensus::Params& consensusParams = Params().GetConsensus(); diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index c5185ca599..ad2d55afe7 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -988,7 +988,8 @@ static UniValue signrawtransactionwithkey(const JSONRPCRequest& request) UniValue signrawtransaction(const JSONRPCRequest& request) { #ifdef ENABLE_WALLET - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); #endif if (request.fHelp || request.params.size() < 1 || request.params.size() > 4) diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 7edd51d3d7..10040b1255 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -23,10 +23,10 @@ #include <memory> // for unique_ptr #include <unordered_map> -static bool fRPCRunning = false; -static bool fRPCInWarmup = true; -static std::string rpcWarmupStatus("RPC server started"); static CCriticalSection cs_rpcWarmup; +static bool fRPCRunning = false; +static bool fRPCInWarmup GUARDED_BY(cs_rpcWarmup) = true; +static std::string rpcWarmupStatus GUARDED_BY(cs_rpcWarmup) = "RPC server started"; /* Timer-creating functions */ static RPCTimerInterface* timerInterface = nullptr; /* Map of name to timer. */ diff --git a/src/scheduler.h b/src/scheduler.h index a41838a295..7169dceee5 100644 --- a/src/scheduler.h +++ b/src/scheduler.h @@ -95,8 +95,8 @@ private: CScheduler *m_pscheduler; CCriticalSection m_cs_callbacks_pending; - std::list<std::function<void (void)>> m_callbacks_pending; - bool m_are_callbacks_running = false; + std::list<std::function<void (void)>> m_callbacks_pending GUARDED_BY(m_cs_callbacks_pending); + bool m_are_callbacks_running GUARDED_BY(m_cs_callbacks_pending) = false; void MaybeScheduleProcessQueue(); void ProcessQueue(); diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index e0d193fa38..4b982d647d 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -336,6 +336,10 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& opcode == OP_RSHIFT) return set_error(serror, SCRIPT_ERR_DISABLED_OPCODE); // Disabled opcodes. + // With SCRIPT_VERIFY_CONST_SCRIPTCODE, OP_CODESEPARATOR in non-segwit script is rejected even in an unexecuted branch + if (opcode == OP_CODESEPARATOR && sigversion == SigVersion::BASE && (flags & SCRIPT_VERIFY_CONST_SCRIPTCODE)) + return set_error(serror, SCRIPT_ERR_OP_CODESEPARATOR); + if (fExec && 0 <= opcode && opcode <= OP_PUSHDATA4) { if (fRequireMinimal && !CheckMinimalPush(vchPushValue, opcode)) { return set_error(serror, SCRIPT_ERR_MINIMALDATA); @@ -899,6 +903,9 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& case OP_CODESEPARATOR: { + // If SCRIPT_VERIFY_CONST_SCRIPTCODE flag is set, use of OP_CODESEPARATOR is rejected in pre-segwit + // script, even in an unexecuted branch (this is checked above the opcode case statement). + // Hash starts after the code separator pbegincodehash = pc; } @@ -919,7 +926,9 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& // Drop the signature in pre-segwit scripts but not segwit scripts if (sigversion == SigVersion::BASE) { - FindAndDelete(scriptCode, CScript(vchSig)); + int found = FindAndDelete(scriptCode, CScript(vchSig)); + if (found > 0 && (flags & SCRIPT_VERIFY_CONST_SCRIPTCODE)) + return set_error(serror, SCRIPT_ERR_SIG_FINDANDDELETE); } if (!CheckSignatureEncoding(vchSig, flags, serror) || !CheckPubKeyEncoding(vchPubKey, flags, sigversion, serror)) { @@ -983,7 +992,9 @@ bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& { valtype& vchSig = stacktop(-isig-k); if (sigversion == SigVersion::BASE) { - FindAndDelete(scriptCode, CScript(vchSig)); + int found = FindAndDelete(scriptCode, CScript(vchSig)); + if (found > 0 && (flags & SCRIPT_VERIFY_CONST_SCRIPTCODE)) + return set_error(serror, SCRIPT_ERR_SIG_FINDANDDELETE); } } @@ -1078,9 +1089,11 @@ namespace { * Wrapper that serializes like CTransaction, but with the modifications * required for the signature hash done in-place */ -class CTransactionSignatureSerializer { +template <class T> +class CTransactionSignatureSerializer +{ private: - const CTransaction& txTo; //!< reference to the spending transaction (the one being serialized) + const T& txTo; //!< reference to the spending transaction (the one being serialized) const CScript& scriptCode; //!< output script being consumed const unsigned int nIn; //!< input index of txTo being signed const bool fAnyoneCanPay; //!< whether the hashtype has the SIGHASH_ANYONECANPAY flag set @@ -1088,7 +1101,7 @@ private: const bool fHashNone; //!< whether the hashtype is SIGHASH_NONE public: - CTransactionSignatureSerializer(const CTransaction &txToIn, const CScript &scriptCodeIn, unsigned int nInIn, int nHashTypeIn) : + CTransactionSignatureSerializer(const T& txToIn, const CScript& scriptCodeIn, unsigned int nInIn, int nHashTypeIn) : txTo(txToIn), scriptCode(scriptCodeIn), nIn(nInIn), fAnyoneCanPay(!!(nHashTypeIn & SIGHASH_ANYONECANPAY)), fHashSingle((nHashTypeIn & 0x1f) == SIGHASH_SINGLE), @@ -1169,7 +1182,9 @@ public: } }; -uint256 GetPrevoutHash(const CTransaction& txTo) { +template <class T> +uint256 GetPrevoutHash(const T& txTo) +{ CHashWriter ss(SER_GETHASH, 0); for (const auto& txin : txTo.vin) { ss << txin.prevout; @@ -1177,7 +1192,9 @@ uint256 GetPrevoutHash(const CTransaction& txTo) { return ss.GetHash(); } -uint256 GetSequenceHash(const CTransaction& txTo) { +template <class T> +uint256 GetSequenceHash(const T& txTo) +{ CHashWriter ss(SER_GETHASH, 0); for (const auto& txin : txTo.vin) { ss << txin.nSequence; @@ -1185,7 +1202,9 @@ uint256 GetSequenceHash(const CTransaction& txTo) { return ss.GetHash(); } -uint256 GetOutputsHash(const CTransaction& txTo) { +template <class T> +uint256 GetOutputsHash(const T& txTo) +{ CHashWriter ss(SER_GETHASH, 0); for (const auto& txout : txTo.vout) { ss << txout; @@ -1195,7 +1214,8 @@ uint256 GetOutputsHash(const CTransaction& txTo) { } // namespace -PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo) +template <class T> +PrecomputedTransactionData::PrecomputedTransactionData(const T& txTo) { // Cache is calculated only for transactions with witness if (txTo.HasWitness()) { @@ -1206,7 +1226,12 @@ PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo) } } -uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache) +// explicit instantiation +template PrecomputedTransactionData::PrecomputedTransactionData(const CTransaction& txTo); +template PrecomputedTransactionData::PrecomputedTransactionData(const CMutableTransaction& txTo); + +template <class T> +uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache) { assert(nIn < txTo.vin.size()); @@ -1267,7 +1292,7 @@ uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsig } // Wrapper to serialize only the necessary parts of the transaction being signed - CTransactionSignatureSerializer txTmp(txTo, scriptCode, nIn, nHashType); + CTransactionSignatureSerializer<T> txTmp(txTo, scriptCode, nIn, nHashType); // Serialize and hash CHashWriter ss(SER_GETHASH, 0); @@ -1275,12 +1300,14 @@ uint256 SignatureHash(const CScript& scriptCode, const CTransaction& txTo, unsig return ss.GetHash(); } -bool TransactionSignatureChecker::VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const +template <class T> +bool GenericTransactionSignatureChecker<T>::VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& pubkey, const uint256& sighash) const { return pubkey.Verify(sighash, vchSig); } -bool TransactionSignatureChecker::CheckSig(const std::vector<unsigned char>& vchSigIn, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const +template <class T> +bool GenericTransactionSignatureChecker<T>::CheckSig(const std::vector<unsigned char>& vchSigIn, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const { CPubKey pubkey(vchPubKey); if (!pubkey.IsValid()) @@ -1301,7 +1328,8 @@ bool TransactionSignatureChecker::CheckSig(const std::vector<unsigned char>& vch return true; } -bool TransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) const +template <class T> +bool GenericTransactionSignatureChecker<T>::CheckLockTime(const CScriptNum& nLockTime) const { // There are two kinds of nLockTime: lock-by-blockheight // and lock-by-blocktime, distinguished by whether @@ -1337,7 +1365,8 @@ bool TransactionSignatureChecker::CheckLockTime(const CScriptNum& nLockTime) con return true; } -bool TransactionSignatureChecker::CheckSequence(const CScriptNum& nSequence) const +template <class T> +bool GenericTransactionSignatureChecker<T>::CheckSequence(const CScriptNum& nSequence) const { // Relative lock times are supported by comparing the passed // in operand to the sequence number of the input. @@ -1383,6 +1412,10 @@ bool TransactionSignatureChecker::CheckSequence(const CScriptNum& nSequence) con return true; } +// explicit instantiation +template class GenericTransactionSignatureChecker<CTransaction>; +template class GenericTransactionSignatureChecker<CMutableTransaction>; + static bool VerifyWitnessProgram(const CScriptWitness& witness, int witversion, const std::vector<unsigned char>& program, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror) { std::vector<std::vector<unsigned char> > stack; diff --git a/src/script/interpreter.h b/src/script/interpreter.h index 50c747900a..2d21aa81df 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -111,6 +111,10 @@ enum // Public keys in segregated witness scripts must be compressed // SCRIPT_VERIFY_WITNESS_PUBKEYTYPE = (1U << 15), + + // Making OP_CODESEPARATOR and FindAndDelete fail any non-segwit scripts + // + SCRIPT_VERIFY_CONST_SCRIPTCODE = (1U << 16), }; bool CheckSignatureEncoding(const std::vector<unsigned char> &vchSig, unsigned int flags, ScriptError* serror); @@ -120,7 +124,8 @@ struct PrecomputedTransactionData uint256 hashPrevouts, hashSequence, hashOutputs; bool ready = false; - explicit PrecomputedTransactionData(const CTransaction& tx); + template <class T> + explicit PrecomputedTransactionData(const T& tx); }; enum class SigVersion @@ -133,7 +138,8 @@ enum class SigVersion static constexpr size_t WITNESS_V0_SCRIPTHASH_SIZE = 32; static constexpr size_t WITNESS_V0_KEYHASH_SIZE = 20; -uint256 SignatureHash(const CScript &scriptCode, const CTransaction& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache = nullptr); +template <class T> +uint256 SignatureHash(const CScript& scriptCode, const T& txTo, unsigned int nIn, int nHashType, const CAmount& amount, SigVersion sigversion, const PrecomputedTransactionData* cache = nullptr); class BaseSignatureChecker { @@ -156,10 +162,11 @@ public: virtual ~BaseSignatureChecker() {} }; -class TransactionSignatureChecker : public BaseSignatureChecker +template <class T> +class GenericTransactionSignatureChecker : public BaseSignatureChecker { private: - const CTransaction* txTo; + const T* txTo; unsigned int nIn; const CAmount amount; const PrecomputedTransactionData* txdata; @@ -168,21 +175,15 @@ protected: virtual bool VerifySignature(const std::vector<unsigned char>& vchSig, const CPubKey& vchPubKey, const uint256& sighash) const; public: - TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(nullptr) {} - TransactionSignatureChecker(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, const PrecomputedTransactionData& txdataIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(&txdataIn) {} + GenericTransactionSignatureChecker(const T* txToIn, unsigned int nInIn, const CAmount& amountIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(nullptr) {} + GenericTransactionSignatureChecker(const T* txToIn, unsigned int nInIn, const CAmount& amountIn, const PrecomputedTransactionData& txdataIn) : txTo(txToIn), nIn(nInIn), amount(amountIn), txdata(&txdataIn) {} bool CheckSig(const std::vector<unsigned char>& scriptSig, const std::vector<unsigned char>& vchPubKey, const CScript& scriptCode, SigVersion sigversion) const override; bool CheckLockTime(const CScriptNum& nLockTime) const override; bool CheckSequence(const CScriptNum& nSequence) const override; }; -class MutableTransactionSignatureChecker : public TransactionSignatureChecker -{ -private: - const CTransaction txTo; - -public: - MutableTransactionSignatureChecker(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn) : TransactionSignatureChecker(&txTo, nInIn, amountIn), txTo(*txToIn) {} -}; +using TransactionSignatureChecker = GenericTransactionSignatureChecker<CTransaction>; +using MutableTransactionSignatureChecker = GenericTransactionSignatureChecker<CMutableTransaction>; bool EvalScript(std::vector<std::vector<unsigned char> >& stack, const CScript& script, unsigned int flags, const BaseSignatureChecker& checker, SigVersion sigversion, ScriptError* error = nullptr); bool VerifyScript(const CScript& scriptSig, const CScript& scriptPubKey, const CScriptWitness* witness, unsigned int flags, const BaseSignatureChecker& checker, ScriptError* serror = nullptr); diff --git a/src/script/ismine.cpp b/src/script/ismine.cpp index fefa02fdef..43dd9e582e 100644 --- a/src/script/ismine.cpp +++ b/src/script/ismine.cpp @@ -28,6 +28,19 @@ enum class IsMineSigVersion WITNESS_V0 = 2 //! P2WSH witness script execution }; +/** + * This is an internal representation of isminetype + invalidity. + * Its order is significant, as we return the max of all explored + * possibilities. + */ +enum class IsMineResult +{ + NO = 0, //! Not ours + WATCH_ONLY = 1, //! Included in watch-only balance + SPENDABLE = 2, //! Included in all balances + INVALID = 3, //! Not spendable by anyone +}; + bool PermitsUncompressed(IsMineSigVersion sigversion) { return sigversion == IsMineSigVersion::TOP || sigversion == IsMineSigVersion::P2SH; @@ -42,17 +55,13 @@ bool HaveKeys(const std::vector<valtype>& pubkeys, const CKeyStore& keystore) return true; } -isminetype IsMineInner(const CKeyStore& keystore, const CScript& scriptPubKey, bool& isInvalid, IsMineSigVersion sigversion) +IsMineResult IsMineInner(const CKeyStore& keystore, const CScript& scriptPubKey, IsMineSigVersion sigversion) { - isInvalid = false; + IsMineResult ret = IsMineResult::NO; std::vector<valtype> vSolutions; txnouttype whichType; - if (!Solver(scriptPubKey, whichType, vSolutions)) { - if (keystore.HaveWatchOnly(scriptPubKey)) - return ISMINE_WATCH_UNSOLVABLE; - return ISMINE_NO; - } + Solver(scriptPubKey, whichType, vSolutions); CKeyID keyID; switch (whichType) @@ -64,23 +73,25 @@ isminetype IsMineInner(const CKeyStore& keystore, const CScript& scriptPubKey, b case TX_PUBKEY: keyID = CPubKey(vSolutions[0]).GetID(); if (!PermitsUncompressed(sigversion) && vSolutions[0].size() != 33) { - isInvalid = true; - return ISMINE_NO; + return IsMineResult::INVALID; + } + if (keystore.HaveKey(keyID)) { + ret = std::max(ret, IsMineResult::SPENDABLE); } - if (keystore.HaveKey(keyID)) - return ISMINE_SPENDABLE; break; case TX_WITNESS_V0_KEYHASH: { + if (sigversion == IsMineSigVersion::WITNESS_V0) { + // P2WPKH inside P2WSH is invalid. + return IsMineResult::INVALID; + } if (sigversion == IsMineSigVersion::TOP && !keystore.HaveCScript(CScriptID(CScript() << OP_0 << vSolutions[0]))) { // We do not support bare witness outputs unless the P2SH version of it would be // acceptable as well. This protects against matching before segwit activates. // This also applies to the P2WSH case. break; } - isminetype ret = IsMineInner(keystore, GetScriptForDestination(CKeyID(uint160(vSolutions[0]))), isInvalid, IsMineSigVersion::WITNESS_V0); - if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || (ret == ISMINE_NO && isInvalid)) - return ret; + ret = std::max(ret, IsMineInner(keystore, GetScriptForDestination(CKeyID(uint160(vSolutions[0]))), IsMineSigVersion::WITNESS_V0)); break; } case TX_PUBKEYHASH: @@ -88,26 +99,32 @@ isminetype IsMineInner(const CKeyStore& keystore, const CScript& scriptPubKey, b if (!PermitsUncompressed(sigversion)) { CPubKey pubkey; if (keystore.GetPubKey(keyID, pubkey) && !pubkey.IsCompressed()) { - isInvalid = true; - return ISMINE_NO; + return IsMineResult::INVALID; } } - if (keystore.HaveKey(keyID)) - return ISMINE_SPENDABLE; + if (keystore.HaveKey(keyID)) { + ret = std::max(ret, IsMineResult::SPENDABLE); + } break; case TX_SCRIPTHASH: { + if (sigversion != IsMineSigVersion::TOP) { + // P2SH inside P2WSH or P2SH is invalid. + return IsMineResult::INVALID; + } CScriptID scriptID = CScriptID(uint160(vSolutions[0])); CScript subscript; if (keystore.GetCScript(scriptID, subscript)) { - isminetype ret = IsMineInner(keystore, subscript, isInvalid, IsMineSigVersion::P2SH); - if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || (ret == ISMINE_NO && isInvalid)) - return ret; + ret = std::max(ret, IsMineInner(keystore, subscript, IsMineSigVersion::P2SH)); } break; } case TX_WITNESS_V0_SCRIPTHASH: { + if (sigversion == IsMineSigVersion::WITNESS_V0) { + // P2WSH inside P2WSH is invalid. + return IsMineResult::INVALID; + } if (sigversion == IsMineSigVersion::TOP && !keystore.HaveCScript(CScriptID(CScript() << OP_0 << vSolutions[0]))) { break; } @@ -116,9 +133,7 @@ isminetype IsMineInner(const CKeyStore& keystore, const CScript& scriptPubKey, b CScriptID scriptID = CScriptID(hash); CScript subscript; if (keystore.GetCScript(scriptID, subscript)) { - isminetype ret = IsMineInner(keystore, subscript, isInvalid, IsMineSigVersion::WITNESS_V0); - if (ret == ISMINE_SPENDABLE || ret == ISMINE_WATCH_SOLVABLE || (ret == ISMINE_NO && isInvalid)) - return ret; + ret = std::max(ret, IsMineInner(keystore, subscript, IsMineSigVersion::WITNESS_V0)); } break; } @@ -126,7 +141,9 @@ isminetype IsMineInner(const CKeyStore& keystore, const CScript& scriptPubKey, b case TX_MULTISIG: { // Never treat bare multisig outputs as ours (they can still be made watchonly-though) - if (sigversion == IsMineSigVersion::TOP) break; + if (sigversion == IsMineSigVersion::TOP) { + break; + } // Only consider transactions "mine" if we own ALL the // keys involved. Multi-signature transactions that are @@ -137,30 +154,39 @@ isminetype IsMineInner(const CKeyStore& keystore, const CScript& scriptPubKey, b if (!PermitsUncompressed(sigversion)) { for (size_t i = 0; i < keys.size(); i++) { if (keys[i].size() != 33) { - isInvalid = true; - return ISMINE_NO; + return IsMineResult::INVALID; } } } - if (HaveKeys(keys, keystore)) - return ISMINE_SPENDABLE; + if (HaveKeys(keys, keystore)) { + ret = std::max(ret, IsMineResult::SPENDABLE); + } break; } } - if (keystore.HaveWatchOnly(scriptPubKey)) { - // TODO: This could be optimized some by doing some work after the above solver - SignatureData sigs; - return ProduceSignature(keystore, DUMMY_SIGNATURE_CREATOR, scriptPubKey, sigs) ? ISMINE_WATCH_SOLVABLE : ISMINE_WATCH_UNSOLVABLE; + if (ret == IsMineResult::NO && keystore.HaveWatchOnly(scriptPubKey)) { + ret = std::max(ret, IsMineResult::WATCH_ONLY); } - return ISMINE_NO; + return ret; } } // namespace isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey, bool& isInvalid) { - return IsMineInner(keystore, scriptPubKey, isInvalid, IsMineSigVersion::TOP); + isInvalid = false; + switch (IsMineInner(keystore, scriptPubKey, IsMineSigVersion::TOP)) { + case IsMineResult::INVALID: + isInvalid = true; + case IsMineResult::NO: + return ISMINE_NO; + case IsMineResult::WATCH_ONLY: + return ISMINE_WATCH_ONLY; + case IsMineResult::SPENDABLE: + return ISMINE_SPENDABLE; + } + assert(false); } isminetype IsMine(const CKeyStore& keystore, const CScript& scriptPubKey) diff --git a/src/script/ismine.h b/src/script/ismine.h index 8573bdfbd2..a15768aecb 100644 --- a/src/script/ismine.h +++ b/src/script/ismine.h @@ -17,12 +17,8 @@ class CScript; enum isminetype { ISMINE_NO = 0, - //! Indicates that we don't know how to create a scriptSig that would solve this if we were given the appropriate private keys - ISMINE_WATCH_UNSOLVABLE = 1, - //! Indicates that we know how to create a scriptSig that would solve this if we were given the appropriate private keys - ISMINE_WATCH_SOLVABLE = 2, - ISMINE_WATCH_ONLY = ISMINE_WATCH_SOLVABLE | ISMINE_WATCH_UNSOLVABLE, - ISMINE_SPENDABLE = 4, + ISMINE_WATCH_ONLY = 1, + ISMINE_SPENDABLE = 2, ISMINE_ALL = ISMINE_WATCH_ONLY | ISMINE_SPENDABLE }; /** used for bitflags of isminetype */ diff --git a/src/script/script.cpp b/src/script/script.cpp index 7f25d915a8..c84c7b8ec1 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -141,11 +141,6 @@ const char* GetOpName(opcodetype opcode) case OP_INVALIDOPCODE : return "OP_INVALIDOPCODE"; - // Note: - // The template matching params OP_SMALLINTEGER/etc are defined in opcodetype enum - // as kind of implementation hack, they are *NOT* real opcodes. If found in real - // Script, just let the default: case deal with them. - default: return "OP_UNKNOWN"; } diff --git a/src/script/script.h b/src/script/script.h index d8b7c06013..a4f377dd94 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -181,13 +181,6 @@ enum opcodetype OP_NOP9 = 0xb8, OP_NOP10 = 0xb9, - - // template matching params - OP_SMALLINTEGER = 0xfa, - OP_PUBKEYS = 0xfb, - OP_PUBKEYHASH = 0xfd, - OP_PUBKEY = 0xfe, - OP_INVALIDOPCODE = 0xff, }; diff --git a/src/script/script_error.cpp b/src/script/script_error.cpp index dbceb1f740..ceda740580 100644 --- a/src/script/script_error.cpp +++ b/src/script/script_error.cpp @@ -89,6 +89,10 @@ const char* ScriptErrorString(const ScriptError serror) return "Witness provided for non-witness script"; case SCRIPT_ERR_WITNESS_PUBKEYTYPE: return "Using non-compressed keys in segwit"; + case SCRIPT_ERR_OP_CODESEPARATOR: + return "Using OP_CODESEPARATOR in non-witness script"; + case SCRIPT_ERR_SIG_FINDANDDELETE: + return "Signature is found in scriptCode"; case SCRIPT_ERR_UNKNOWN_ERROR: case SCRIPT_ERR_ERROR_COUNT: default: break; diff --git a/src/script/script_error.h b/src/script/script_error.h index 3200e94707..6982a087f4 100644 --- a/src/script/script_error.h +++ b/src/script/script_error.h @@ -64,6 +64,10 @@ typedef enum ScriptError_t SCRIPT_ERR_WITNESS_UNEXPECTED, SCRIPT_ERR_WITNESS_PUBKEYTYPE, + /* Constant scriptCode */ + SCRIPT_ERR_OP_CODESEPARATOR, + SCRIPT_ERR_SIG_FINDANDDELETE, + SCRIPT_ERR_ERROR_COUNT } ScriptError; diff --git a/src/script/sign.cpp b/src/script/sign.cpp index ac35f17f3e..c5468f633b 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -14,9 +14,9 @@ typedef std::vector<unsigned char> valtype; -TransactionSignatureCreator::TransactionSignatureCreator(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn) {} +MutableTransactionSignatureCreator::MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn) {} -bool TransactionSignatureCreator::CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& address, const CScript& scriptCode, SigVersion sigversion) const +bool MutableTransactionSignatureCreator::CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& address, const CScript& scriptCode, SigVersion sigversion) const { CKey key; if (!provider.GetKey(address, key)) @@ -209,8 +209,7 @@ bool SignSignature(const SigningProvider &provider, const CScript& fromPubKey, C { assert(nIn < txTo.vin.size()); - CTransaction txToConst(txTo); - TransactionSignatureCreator creator(&txToConst, nIn, amount, nHashType); + MutableTransactionSignatureCreator creator(&txTo, nIn, amount, nHashType); SignatureData sigdata; bool ret = ProduceSignature(provider, creator, fromPubKey, sigdata); diff --git a/src/script/sign.h b/src/script/sign.h index cf3651c1de..a10366dcd1 100644 --- a/src/script/sign.h +++ b/src/script/sign.h @@ -37,26 +37,19 @@ public: }; /** A signature creator for transactions. */ -class TransactionSignatureCreator : public BaseSignatureCreator { - const CTransaction* txTo; +class MutableTransactionSignatureCreator : public BaseSignatureCreator { + const CMutableTransaction* txTo; unsigned int nIn; int nHashType; CAmount amount; - const TransactionSignatureChecker checker; + const MutableTransactionSignatureChecker checker; public: - TransactionSignatureCreator(const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn=SIGHASH_ALL); + MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn = SIGHASH_ALL); const BaseSignatureChecker& Checker() const override { return checker; } bool CreateSig(const SigningProvider& provider, std::vector<unsigned char>& vchSig, const CKeyID& keyid, const CScript& scriptCode, SigVersion sigversion) const override; }; -class MutableTransactionSignatureCreator : public TransactionSignatureCreator { - CTransaction tx; - -public: - MutableTransactionSignatureCreator(const CMutableTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : TransactionSignatureCreator(&tx, nInIn, amountIn, nHashTypeIn), tx(*txToIn) {} -}; - /** A signature creator that just produces 72-byte empty signatures. */ extern const BaseSignatureCreator& DUMMY_SIGNATURE_CREATOR; diff --git a/src/script/standard.cpp b/src/script/standard.cpp index 76778112aa..53fcbe37de 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -35,22 +35,54 @@ const char* GetTxnOutputType(txnouttype t) return nullptr; } -bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::vector<unsigned char> >& vSolutionsRet) +static bool MatchPayToPubkey(const CScript& script, valtype& pubkey) { - // Templates - static std::multimap<txnouttype, CScript> mTemplates; - if (mTemplates.empty()) - { - // Standard tx, sender provides pubkey, receiver adds signature - mTemplates.insert(std::make_pair(TX_PUBKEY, CScript() << OP_PUBKEY << OP_CHECKSIG)); + if (script.size() == CPubKey::PUBLIC_KEY_SIZE + 2 && script[0] == CPubKey::PUBLIC_KEY_SIZE && script.back() == OP_CHECKSIG) { + pubkey = valtype(script.begin() + 1, script.begin() + CPubKey::PUBLIC_KEY_SIZE + 1); + return CPubKey::ValidSize(pubkey); + } + if (script.size() == CPubKey::COMPRESSED_PUBLIC_KEY_SIZE + 2 && script[0] == CPubKey::COMPRESSED_PUBLIC_KEY_SIZE && script.back() == OP_CHECKSIG) { + pubkey = valtype(script.begin() + 1, script.begin() + CPubKey::COMPRESSED_PUBLIC_KEY_SIZE + 1); + return CPubKey::ValidSize(pubkey); + } + return false; +} - // Bitcoin address tx, sender provides hash of pubkey, receiver provides signature and pubkey - mTemplates.insert(std::make_pair(TX_PUBKEYHASH, CScript() << OP_DUP << OP_HASH160 << OP_PUBKEYHASH << OP_EQUALVERIFY << OP_CHECKSIG)); +static bool MatchPayToPubkeyHash(const CScript& script, valtype& pubkeyhash) +{ + if (script.size() == 25 && script[0] == OP_DUP && script[1] == OP_HASH160 && script[2] == 20 && script[23] == OP_EQUALVERIFY && script[24] == OP_CHECKSIG) { + pubkeyhash = valtype(script.begin () + 3, script.begin() + 23); + return true; + } + return false; +} + +/** Test for "small positive integer" script opcodes - OP_1 through OP_16. */ +static constexpr bool IsSmallInteger(opcodetype opcode) +{ + return opcode >= OP_1 && opcode <= OP_16; +} - // Sender provides N pubkeys, receivers provides M signatures - mTemplates.insert(std::make_pair(TX_MULTISIG, CScript() << OP_SMALLINTEGER << OP_PUBKEYS << OP_SMALLINTEGER << OP_CHECKMULTISIG)); +static bool MatchMultisig(const CScript& script, unsigned int& required, std::vector<valtype>& pubkeys) +{ + opcodetype opcode; + valtype data; + CScript::const_iterator it = script.begin(); + if (script.size() < 1 || script.back() != OP_CHECKMULTISIG) return false; + + if (!script.GetOp(it, opcode, data) || !IsSmallInteger(opcode)) return false; + required = CScript::DecodeOP_N(opcode); + while (script.GetOp(it, opcode, data) && CPubKey::ValidSize(data)) { + pubkeys.emplace_back(std::move(data)); } + if (!IsSmallInteger(opcode)) return false; + unsigned int keys = CScript::DecodeOP_N(opcode); + if (pubkeys.size() != keys || keys < required) return false; + return (it + 1 == script.end()); +} +bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::vector<unsigned char> >& vSolutionsRet) +{ vSolutionsRet.clear(); // Shortcut for pay-to-script-hash, which are more constrained than the other types: @@ -95,84 +127,27 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, std::vector<std::v return true; } - // Scan templates - const CScript& script1 = scriptPubKey; - for (const std::pair<txnouttype, CScript>& tplate : mTemplates) - { - const CScript& script2 = tplate.second; - vSolutionsRet.clear(); + std::vector<unsigned char> data; + if (MatchPayToPubkey(scriptPubKey, data)) { + typeRet = TX_PUBKEY; + vSolutionsRet.push_back(std::move(data)); + return true; + } - opcodetype opcode1, opcode2; - std::vector<unsigned char> vch1, vch2; + if (MatchPayToPubkeyHash(scriptPubKey, data)) { + typeRet = TX_PUBKEYHASH; + vSolutionsRet.push_back(std::move(data)); + return true; + } - // Compare - CScript::const_iterator pc1 = script1.begin(); - CScript::const_iterator pc2 = script2.begin(); - while (true) - { - if (pc1 == script1.end() && pc2 == script2.end()) - { - // Found a match - typeRet = tplate.first; - if (typeRet == TX_MULTISIG) - { - // Additional checks for TX_MULTISIG: - unsigned char m = vSolutionsRet.front()[0]; - unsigned char n = vSolutionsRet.back()[0]; - if (m < 1 || n < 1 || m > n || vSolutionsRet.size()-2 != n) - return false; - } - return true; - } - if (!script1.GetOp(pc1, opcode1, vch1)) - break; - if (!script2.GetOp(pc2, opcode2, vch2)) - break; - - // Template matching opcodes: - if (opcode2 == OP_PUBKEYS) - { - while (CPubKey::ValidSize(vch1)) - { - vSolutionsRet.push_back(vch1); - if (!script1.GetOp(pc1, opcode1, vch1)) - break; - } - if (!script2.GetOp(pc2, opcode2, vch2)) - break; - // Normal situation is to fall through - // to other if/else statements - } - - if (opcode2 == OP_PUBKEY) - { - if (!CPubKey::ValidSize(vch1)) - break; - vSolutionsRet.push_back(vch1); - } - else if (opcode2 == OP_PUBKEYHASH) - { - if (vch1.size() != sizeof(uint160)) - break; - vSolutionsRet.push_back(vch1); - } - else if (opcode2 == OP_SMALLINTEGER) - { // Single-byte small integer pushed onto vSolutions - if (opcode1 == OP_0 || - (opcode1 >= OP_1 && opcode1 <= OP_16)) - { - char n = (char)CScript::DecodeOP_N(opcode1); - vSolutionsRet.push_back(valtype(1, n)); - } - else - break; - } - else if (opcode1 != opcode2 || vch1 != vch2) - { - // Others must match exactly - break; - } - } + unsigned int required; + std::vector<std::vector<unsigned char>> keys; + if (MatchMultisig(scriptPubKey, required, keys)) { + typeRet = TX_MULTISIG; + vSolutionsRet.push_back({static_cast<unsigned char>(required)}); // safe as required is in range 1..16 + vSolutionsRet.insert(vSolutionsRet.end(), keys.begin(), keys.end()); + vSolutionsRet.push_back({static_cast<unsigned char>(keys.size())}); // safe as size is in range 1..16 + return true; } vSolutionsRet.clear(); diff --git a/src/sync.cpp b/src/sync.cpp index 6f21d498ee..28a4e37e68 100644 --- a/src/sync.cpp +++ b/src/sync.cpp @@ -4,13 +4,15 @@ #include <sync.h> -#include <memory> -#include <set> -#include <util.h> +#include <logging.h> #include <utilstrencodings.h> #include <stdio.h> +#include <map> +#include <memory> +#include <set> + #ifdef DEBUG_LOCKCONTENTION #if !defined(HAVE_THREAD_LOCAL) static_assert(false, "thread_local is not supported"); @@ -73,7 +75,7 @@ struct LockData { std::mutex dd_mutex; } static lockdata; -static thread_local std::unique_ptr<LockStack> lockstack; +static thread_local LockStack g_lockstack; static void potential_deadlock_detected(const std::pair<void*, void*>& mismatch, const LockStack& s1, const LockStack& s2) { @@ -103,21 +105,18 @@ static void potential_deadlock_detected(const std::pair<void*, void*>& mismatch, static void push_lock(void* c, const CLockLocation& locklocation) { - if (!lockstack) - lockstack.reset(new LockStack); - std::lock_guard<std::mutex> lock(lockdata.dd_mutex); - lockstack->push_back(std::make_pair(c, locklocation)); + g_lockstack.push_back(std::make_pair(c, locklocation)); - for (const std::pair<void*, CLockLocation> & i : (*lockstack)) { + for (const std::pair<void*, CLockLocation>& i : g_lockstack) { if (i.first == c) break; std::pair<void*, void*> p1 = std::make_pair(i.first, c); if (lockdata.lockorders.count(p1)) continue; - lockdata.lockorders[p1] = (*lockstack); + lockdata.lockorders[p1] = g_lockstack; std::pair<void*, void*> p2 = std::make_pair(c, i.first); lockdata.invlockorders.insert(p2); @@ -128,7 +127,7 @@ static void push_lock(void* c, const CLockLocation& locklocation) static void pop_lock() { - (*lockstack).pop_back(); + g_lockstack.pop_back(); } void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry) @@ -144,14 +143,14 @@ void LeaveCritical() std::string LocksHeld() { std::string result; - for (const std::pair<void*, CLockLocation> & i : *lockstack) + for (const std::pair<void*, CLockLocation>& i : g_lockstack) result += i.second.ToString() + std::string("\n"); return result; } void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) { - for (const std::pair<void*, CLockLocation> & i : *lockstack) + for (const std::pair<void*, CLockLocation>& i : g_lockstack) if (i.first == cs) return; fprintf(stderr, "Assertion failed: lock %s not held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld().c_str()); @@ -160,7 +159,7 @@ void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) { - for (const std::pair<void*, CLockLocation>& i : *lockstack) { + for (const std::pair<void*, CLockLocation>& i : g_lockstack) { if (i.first == cs) { fprintf(stderr, "Assertion failed: lock %s held in %s:%i; locks held:\n%s", pszName, pszFile, nLine, LocksHeld().c_str()); abort(); diff --git a/src/test/bech32_tests.cpp b/src/test/bech32_tests.cpp index c23e23f6a1..6ecc9ac705 100644 --- a/src/test/bech32_tests.cpp +++ b/src/test/bech32_tests.cpp @@ -57,6 +57,8 @@ BOOST_AUTO_TEST_CASE(bip173_testvectors_invalid) "A1G7SGD8", "10a06t8", "1qzzfhee", + "a12UEL5L", + "A12uEL5L", }; for (const std::string& str : CASES) { auto ret = bech32::Decode(str); diff --git a/src/test/bip32_tests.cpp b/src/test/bip32_tests.cpp index 51308847f6..2c625c089c 100644 --- a/src/test/bip32_tests.cpp +++ b/src/test/bip32_tests.cpp @@ -91,7 +91,7 @@ static void RunTest(const TestVector &test) { std::vector<unsigned char> seed = ParseHex(test.strHexMaster); CExtKey key; CExtPubKey pubkey; - key.SetMaster(seed.data(), seed.size()); + key.SetSeed(seed.data(), seed.size()); pubkey = key.Neuter(); for (const TestDerivation &derive : test.vDerive) { unsigned char data[74]; diff --git a/src/test/blockencodings_tests.cpp b/src/test/blockencodings_tests.cpp index 8cffacbffe..3dd5356164 100644 --- a/src/test/blockencodings_tests.cpp +++ b/src/test/blockencodings_tests.cpp @@ -5,6 +5,7 @@ #include <blockencodings.h> #include <consensus/merkle.h> #include <chainparams.h> +#include <pow.h> #include <random.h> #include <test/test_bitcoin.h> diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp index 276d5b80ee..b792ff8b45 100644 --- a/src/test/coins_tests.cpp +++ b/src/test/coins_tests.cpp @@ -312,7 +312,7 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test) if (InsecureRandRange(10) == 0 && coinbase_coins.size()) { auto utxod = FindRandomFrom(coinbase_coins); // Reuse the exact same coinbase - tx = std::get<0>(utxod->second); + tx = CMutableTransaction{std::get<0>(utxod->second)}; // shouldn't be available for reconnection if it's been duplicated disconnected_coins.erase(utxod->first); @@ -331,7 +331,7 @@ BOOST_AUTO_TEST_CASE(updatecoins_simulation_test) // 1/20 times reconnect a previously disconnected tx if (randiter % 20 == 2 && disconnected_coins.size()) { auto utxod = FindRandomFrom(disconnected_coins); - tx = std::get<0>(utxod->second); + tx = CMutableTransaction{std::get<0>(utxod->second)}; prevout = tx.vin[0].prevout; if (!CTransaction(tx).IsCoinBase() && !utxoset.count(prevout)) { disconnected_coins.erase(utxod->first); diff --git a/src/test/data/tx_invalid.json b/src/test/data/tx_invalid.json index abb46fe533..918df6d8d9 100644 --- a/src/test/data/tx_invalid.json +++ b/src/test/data/tx_invalid.json @@ -340,5 +340,53 @@ [[["9628667ad48219a169b41b020800162287d2c0f713c04157e95c484a8dcb7592", 7500, "0x00 0x20 0x9b66c15b4e0b4eb49fa877982cafded24859fe5b0e2dbfbe4f0df1de7743fd52", 200000]], "010000000001019275cb8d4a485ce95741c013f7c0d28722160008021bb469a11982d47a6628964c1d000000ffffffff0101000000000000000007004830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c03959601010221023cb6055f4b57a1580c5a753e19610cafaedf7e0ff377731c77837fd666eae1712102c1b1db303ac232ffa8e5e7cc2cf5f96c6e40d3e6914061204c0541cb2043a0969552af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c039596017500000000", "P2SH,WITNESS"], +["SCRIPT_VERIFY_CONST_SCRIPTCODE tests"], +["All transactions are copied from OP_CODESEPARATOR tests in tx_valid.json"], + +[[["bc7fd132fcf817918334822ee6d9bd95c889099c96e07ca2c1eb2cc70db63224", 0, "CODESEPARATOR 0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIG"]], + "01000000012432b60dc72cebc1a27ce0969c0989c895bdd9e62e8234839117f8fc32d17fbc000000004a493046022100a576b52051962c25e642c0fd3d77ee6c92487048e5d90818bcf5b51abaccd7900221008204f8fb121be4ec3b24483b1f92d89b1b0548513a134e345c5442e86e8617a501ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"], +[[["83e194f90b6ef21fa2e3a365b63794fb5daa844bdc9b25de30899fcfe7b01047", 0, "CODESEPARATOR CODESEPARATOR 0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIG"]], + "01000000014710b0e7cf9f8930de259bdc4b84aa5dfb9437b665a3e3a21ff26e0bf994e183000000004a493046022100a166121a61b4eeb19d8f922b978ff6ab58ead8a5a5552bf9be73dc9c156873ea02210092ad9bc43ee647da4f6652c320800debcf08ec20a094a0aaf085f63ecb37a17201ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"], + +[[["326882a7f22b5191f1a0cc9962ca4b878cd969cf3b3a70887aece4d801a0ba5e", 0, "0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CODESEPARATOR CHECKSIG"]], + "01000000015ebaa001d8e4ec7a88703a3bcf69d98c874bca6299cca0f191512bf2a7826832000000004948304502203bf754d1c6732fbf87c5dcd81258aefd30f2060d7bd8ac4a5696f7927091dad1022100f5bcb726c4cf5ed0ed34cc13dadeedf628ae1045b7cb34421bc60b89f4cecae701ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"], + +[[["a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944", 0, "0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIGVERIFY CODESEPARATOR 0x21 0x038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041 CHECKSIGVERIFY CODESEPARATOR 1"]], + "010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a900000000924830450221009c0a27f886a1d8cb87f6f595fbc3163d28f7a81ec3c4b252ee7f3ac77fd13ffa02203caa8dfa09713c8c4d7ef575c75ed97812072405d932bd11e6a1593a98b679370148304502201e3861ef39a526406bad1e20ecad06be7375ad40ddb582c9be42d26c3a0d7b240221009d0a3985e96522e59635d19cc4448547477396ce0ef17a58e7d74c3ef464292301ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"], + +["CODESEPARATOR in an unexecuted IF block is still invalid"], +[[["a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944", 0, "IF CODESEPARATOR ENDIF 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 CHECKSIGVERIFY CODESEPARATOR 1"]], + "010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a9000000004a48304502207a6974a77c591fa13dff60cabbb85a0de9e025c09c65a4b2285e47ce8e22f761022100f0efaac9ff8ac36b10721e0aae1fb975c90500b50c56e8a0cc52b0403f0425dd0100ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"], + +["CODESEPARATOR in an executed IF block is invalid"], +[[["a955032f4d6b0c9bfe8cad8f00a8933790b9c1dc28c82e0f48e75b35da0e4944", 0, "IF CODESEPARATOR ENDIF 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 CHECKSIGVERIFY CODESEPARATOR 1"]], + "010000000144490eda355be7480f2ec828dcc1b9903793a8008fad8cfe9b0c6b4d2f0355a9000000004a483045022100fa4a74ba9fd59c59f46c3960cf90cbe0d2b743c471d24a3d5d6db6002af5eebb02204d70ec490fd0f7055a7c45f86514336e3a7f03503dacecabb247fc23f15c83510151ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"], + + +["Using CHECKSIG with singatures in scriptSigs will trigger FindAndDelete, which is invalid"], +[[["ccf7f4053a02e653c36ac75c891b7496d0dc5ce5214f6c913d9cf8f1329ebee0", 0, "DUP HASH160 0x14 0xee5a6aa40facefb2655ac23c0c28c57c65c41f9b EQUALVERIFY CHECKSIG"]], + "0100000001e0be9e32f1f89c3d916c4f21e55cdcd096741b895cc76ac353e6023a05f4f7cc00000000d86149304602210086e5f736a2c3622ebb62bd9d93d8e5d76508b98be922b97160edc3dcca6d8c47022100b23c312ac232a4473f19d2aeb95ab7bdf2b65518911a0d72d50e38b5dd31dc820121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ac4730440220508fa761865c8abd81244a168392876ee1d94e8ed83897066b5e2df2400dad24022043f5ee7538e87e9c6aef7ef55133d3e51da7cc522830a9c4d736977a76ef755c0121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"], + +["OP_CODESEPARATOR in scriptSig is invalid"], +[[["10c9f0effe83e97f80f067de2b11c6a00c3088a4bce42c5ae761519af9306f3c", 1, "DUP HASH160 0x14 0xee5a6aa40facefb2655ac23c0c28c57c65c41f9b EQUALVERIFY CHECKSIG"]], + "01000000013c6f30f99a5161e75a2ce4bca488300ca0c6112bde67f0807fe983feeff0c91001000000e608646561646265656675ab61493046022100ce18d384221a731c993939015e3d1bcebafb16e8c0b5b5d14097ec8177ae6f28022100bcab227af90bab33c3fe0a9abfee03ba976ee25dc6ce542526e9b2e56e14b7f10121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ac493046022100c3b93edcc0fd6250eb32f2dd8a0bba1754b0f6c3be8ed4100ed582f3db73eba2022100bf75b5bd2eff4d6bf2bda2e34a40fcc07d4aa3cf862ceaa77b47b81eff829f9a01ab21038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"], + +["Again, FindAndDelete() in scriptSig"], +[[["6056ebd549003b10cbbd915cea0d82209fe40b8617104be917a26fa92cbe3d6f", 0, "DUP HASH160 0x14 0xee5a6aa40facefb2655ac23c0c28c57c65c41f9b EQUALVERIFY CHECKSIG"]], + "01000000016f3dbe2ca96fa217e94b1017860be49f20820dea5c91bdcb103b0049d5eb566000000000fd1d0147304402203989ac8f9ad36b5d0919d97fa0a7f70c5272abee3b14477dc646288a8b976df5022027d19da84a066af9053ad3d1d7459d171b7e3a80bc6c4ef7a330677a6be548140147304402203989ac8f9ad36b5d0919d97fa0a7f70c5272abee3b14477dc646288a8b976df5022027d19da84a066af9053ad3d1d7459d171b7e3a80bc6c4ef7a330677a6be548140121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ac47304402203757e937ba807e4a5da8534c17f9d121176056406a6465054bdd260457515c1a02200f02eccf1bec0f3a0d65df37889143c2e88ab7acec61a7b6f5aa264139141a2b0121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"], + +[[["5a6b0021a6042a686b6b94abc36b387bef9109847774e8b1e51eb8cc55c53921", 1, "DUP HASH160 0x14 0xee5a6aa40facefb2655ac23c0c28c57c65c41f9b EQUALVERIFY CHECKSIG"]], + "01000000012139c555ccb81ee5b1e87477840991ef7b386bc3ab946b6b682a04a621006b5a01000000fdb40148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390121038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f2204148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a5800390175ac4830450220646b72c35beeec51f4d5bc1cbae01863825750d7f490864af354e6ea4f625e9c022100f04b98432df3a9641719dbced53393022e7249fb59db993af1118539830aab870148304502201723e692e5f409a7151db386291b63524c5eb2030df652b1f53022fd8207349f022100b90d9bbf2f3366ce176e5e780a00433da67d9e5c79312c6388312a296a580039017521038479a0fa998cd35259a2ef0a7a5c68662c1474f88ccb6d08a7677bbec7f22041ffffffff010000000000000000016a00000000", "P2SH,CONST_SCRIPTCODE"], + +["FindAndDelete() in redeemScript"], +[[["b5b598de91787439afd5938116654e0b16b7a0d0f82742ba37564219c5afcbf9", 0, "DUP HASH160 0x14 0xf6f365c40f0739b61de827a44751e5e99032ed8f EQUALVERIFY CHECKSIG"], + ["ab9805c6d57d7070d9a42c5176e47bb705023e6b67249fb6760880548298e742", 0, "HASH160 0x14 0xd8dacdadb7462ae15cd906f1878706d0da8660e6 EQUAL"]], + "0100000002f9cbafc519425637ba4227f8d0a0b7160b4e65168193d5af39747891de98b5b5000000006b4830450221008dd619c563e527c47d9bd53534a770b102e40faa87f61433580e04e271ef2f960220029886434e18122b53d5decd25f1f4acb2480659fea20aabd856987ba3c3907e0121022b78b756e2258af13779c1a1f37ea6800259716ca4b7f0b87610e0bf3ab52a01ffffffff42e7988254800876b69f24676b3e0205b77be476512ca4d970707dd5c60598ab00000000fd260100483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a53034930460221008431bdfa72bc67f9d41fe72e94c88fb8f359ffa30b33c72c121c5a877d922e1002210089ef5fc22dd8bfc6bf9ffdb01a9862d27687d424d1fefbab9e9c7176844a187a014c9052483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303210378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71210378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c7153aeffffffff01a08601000000000017a914d8dacdadb7462ae15cd906f1878706d0da8660e68700000000", "P2SH,CONST_SCRIPTCODE"], + +["FindAndDelete() in bare CHECKMULTISIG"], +[[["ceafe58e0f6e7d67c0409fbbf673c84c166e3c5d3c24af58f7175b18df3bb3db", 0, "DUP HASH160 0x14 0xf6f365c40f0739b61de827a44751e5e99032ed8f EQUALVERIFY CHECKSIG"], + ["ceafe58e0f6e7d67c0409fbbf673c84c166e3c5d3c24af58f7175b18df3bb3db", 1, "2 0x48 0x3045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 0x21 0x0378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71 3 CHECKMULTISIG"]], + "0100000002dbb33bdf185b17f758af243c5d3c6e164cc873f6bb9f40c0677d6e0f8ee5afce000000006b4830450221009627444320dc5ef8d7f68f35010b4c050a6ed0d96b67a84db99fda9c9de58b1e02203e4b4aaa019e012e65d69b487fdf8719df72f488fa91506a80c49a33929f1fd50121022b78b756e2258af13779c1a1f37ea6800259716ca4b7f0b87610e0bf3ab52a01ffffffffdbb33bdf185b17f758af243c5d3c6e164cc873f6bb9f40c0677d6e0f8ee5afce010000009300483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303483045022015bd0139bcccf990a6af6ec5c1c52ed8222e03a0d51c334df139968525d2fcd20221009f9efe325476eb64c3958e4713e9eefe49bf1d820ed58d2112721b134e2a1a5303ffffffff01a0860100000000001976a9149bc0bbdd3024da4d0c38ed1aecf5c68dd1d3fa1288ac00000000", "P2SH,CONST_SCRIPTCODE"], + ["Make diffs cleaner by leaving a comment here without comma at the end"] ] diff --git a/src/test/data/tx_valid.json b/src/test/data/tx_valid.json index 0bcecc58fe..4a1c77166d 100644 --- a/src/test/data/tx_valid.json +++ b/src/test/data/tx_valid.json @@ -477,17 +477,17 @@ ["BIP143 example: P2WSH with OP_CODESEPARATOR and out-of-range SIGHASH_SINGLE."], [[["6eb316926b1c5d567cd6f5e6a84fec606fc53d7b474526d1fff3948020c93dfe", 0, "0x21 0x036d5c20fa14fb2f635474c1dc4ef5909d4568e5569b79fc94d3448486e14685f8 CHECKSIG", 156250000], ["f825690aee1b3dc247da796cacb12687a5e802429fd291cfd63e010f02cf1508", 0, "0x00 0x20 0x5d1b56b63d714eebe542309525f484b7e9d6f686b3781b6f61ef925d66d6f6a0", 4900000000]], -"01000000000102fe3dc9208094f3ffd12645477b3dc56f60ec4fa8e6f5d67c565d1c6b9216b36e000000004847304402200af4e47c9b9629dbecc21f73af989bdaa911f7e6f6c2e9394588a3aa68f81e9902204f3fcf6ade7e5abb1295b6774c8e0abd94ae62217367096bc02ee5e435b67da201ffffffff0815cf020f013ed6cf91d29f4202e8a58726b1ac6c79da47c23d1bee0a6925f80000000000ffffffff0100f2052a010000001976a914a30741f8145e5acadf23f751864167f32e0963f788ac000347304402200de66acf4527789bfda55fc5459e214fa6083f936b430a762c629656216805ac0220396f550692cd347171cbc1ef1f51e15282e837bb2b30860dc77c8f78bc8501e503473044022027dc95ad6b740fe5129e7e62a75dd00f291a2aeb1200b84b09d9e3789406b6c002201a9ecd315dd6a0e632ab20bbb98948bc0c6fb204f2c286963bb48517a7058e27034721026dccc749adc2a9d0d89497ac511f760f45c47dc5ed9cf352a58ac706453880aeadab210255a9626aebf5e29c0e6538428ba0d1dcf6ca98ffdf086aa8ced5e0d0215ea465ac00000000", "P2SH,WITNESS"], +"01000000000102fe3dc9208094f3ffd12645477b3dc56f60ec4fa8e6f5d67c565d1c6b9216b36e000000004847304402200af4e47c9b9629dbecc21f73af989bdaa911f7e6f6c2e9394588a3aa68f81e9902204f3fcf6ade7e5abb1295b6774c8e0abd94ae62217367096bc02ee5e435b67da201ffffffff0815cf020f013ed6cf91d29f4202e8a58726b1ac6c79da47c23d1bee0a6925f80000000000ffffffff0100f2052a010000001976a914a30741f8145e5acadf23f751864167f32e0963f788ac000347304402200de66acf4527789bfda55fc5459e214fa6083f936b430a762c629656216805ac0220396f550692cd347171cbc1ef1f51e15282e837bb2b30860dc77c8f78bc8501e503473044022027dc95ad6b740fe5129e7e62a75dd00f291a2aeb1200b84b09d9e3789406b6c002201a9ecd315dd6a0e632ab20bbb98948bc0c6fb204f2c286963bb48517a7058e27034721026dccc749adc2a9d0d89497ac511f760f45c47dc5ed9cf352a58ac706453880aeadab210255a9626aebf5e29c0e6538428ba0d1dcf6ca98ffdf086aa8ced5e0d0215ea465ac00000000", "P2SH,WITNESS,CONST_SCRIPTCODE"], ["BIP143 example: P2WSH with unexecuted OP_CODESEPARATOR and SINGLE|ANYONECANPAY"], [[["01c0cf7fba650638e55eb91261b183251fbb466f90dff17f10086817c542b5e9", 0, "0x00 0x20 0xba468eea561b26301e4cf69fa34bde4ad60c81e70f059f045ca9a79931004a4d", 16777215], ["1b2a9a426ba603ba357ce7773cb5805cb9c7c2b386d100d1fc9263513188e680", 0, "0x00 0x20 0xd9bbfbe56af7c4b7f960a70d7ea107156913d9e5a26b0a71429df5e097ca6537", 16777215]], -"01000000000102e9b542c5176808107ff1df906f46bb1f2583b16112b95ee5380665ba7fcfc0010000000000ffffffff80e68831516392fcd100d186b3c2c7b95c80b53c77e77c35ba03a66b429a2a1b0000000000ffffffff0280969800000000001976a914de4b231626ef508c9a74a8517e6783c0546d6b2888ac80969800000000001976a9146648a8cd4531e1ec47f35916de8e259237294d1e88ac02483045022100f6a10b8604e6dc910194b79ccfc93e1bc0ec7c03453caaa8987f7d6c3413566002206216229ede9b4d6ec2d325be245c5b508ff0339bf1794078e20bfe0babc7ffe683270063ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac024730440220032521802a76ad7bf74d0e2c218b72cf0cbc867066e2e53db905ba37f130397e02207709e2188ed7f08f4c952d9d13986da504502b8c3be59617e043552f506c46ff83275163ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac00000000", "P2SH,WITNESS"], +"01000000000102e9b542c5176808107ff1df906f46bb1f2583b16112b95ee5380665ba7fcfc0010000000000ffffffff80e68831516392fcd100d186b3c2c7b95c80b53c77e77c35ba03a66b429a2a1b0000000000ffffffff0280969800000000001976a914de4b231626ef508c9a74a8517e6783c0546d6b2888ac80969800000000001976a9146648a8cd4531e1ec47f35916de8e259237294d1e88ac02483045022100f6a10b8604e6dc910194b79ccfc93e1bc0ec7c03453caaa8987f7d6c3413566002206216229ede9b4d6ec2d325be245c5b508ff0339bf1794078e20bfe0babc7ffe683270063ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac024730440220032521802a76ad7bf74d0e2c218b72cf0cbc867066e2e53db905ba37f130397e02207709e2188ed7f08f4c952d9d13986da504502b8c3be59617e043552f506c46ff83275163ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac00000000", "P2SH,WITNESS,CONST_SCRIPTCODE"], ["BIP143 example: Same as the previous example with input-output pairs swapped"], [[["1b2a9a426ba603ba357ce7773cb5805cb9c7c2b386d100d1fc9263513188e680", 0, "0x00 0x20 0xd9bbfbe56af7c4b7f960a70d7ea107156913d9e5a26b0a71429df5e097ca6537", 16777215], ["01c0cf7fba650638e55eb91261b183251fbb466f90dff17f10086817c542b5e9", 0, "0x00 0x20 0xba468eea561b26301e4cf69fa34bde4ad60c81e70f059f045ca9a79931004a4d", 16777215]], -"0100000000010280e68831516392fcd100d186b3c2c7b95c80b53c77e77c35ba03a66b429a2a1b0000000000ffffffffe9b542c5176808107ff1df906f46bb1f2583b16112b95ee5380665ba7fcfc0010000000000ffffffff0280969800000000001976a9146648a8cd4531e1ec47f35916de8e259237294d1e88ac80969800000000001976a914de4b231626ef508c9a74a8517e6783c0546d6b2888ac024730440220032521802a76ad7bf74d0e2c218b72cf0cbc867066e2e53db905ba37f130397e02207709e2188ed7f08f4c952d9d13986da504502b8c3be59617e043552f506c46ff83275163ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac02483045022100f6a10b8604e6dc910194b79ccfc93e1bc0ec7c03453caaa8987f7d6c3413566002206216229ede9b4d6ec2d325be245c5b508ff0339bf1794078e20bfe0babc7ffe683270063ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac00000000", "P2SH,WITNESS"], +"0100000000010280e68831516392fcd100d186b3c2c7b95c80b53c77e77c35ba03a66b429a2a1b0000000000ffffffffe9b542c5176808107ff1df906f46bb1f2583b16112b95ee5380665ba7fcfc0010000000000ffffffff0280969800000000001976a9146648a8cd4531e1ec47f35916de8e259237294d1e88ac80969800000000001976a914de4b231626ef508c9a74a8517e6783c0546d6b2888ac024730440220032521802a76ad7bf74d0e2c218b72cf0cbc867066e2e53db905ba37f130397e02207709e2188ed7f08f4c952d9d13986da504502b8c3be59617e043552f506c46ff83275163ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac02483045022100f6a10b8604e6dc910194b79ccfc93e1bc0ec7c03453caaa8987f7d6c3413566002206216229ede9b4d6ec2d325be245c5b508ff0339bf1794078e20bfe0babc7ffe683270063ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac00000000", "P2SH,WITNESS,CONST_SCRIPTCODE"], ["BIP143 example: P2SH-P2WSH 6-of-6 multisig signed with 6 different SIGHASH types"], [[["6eb98797a21c6c10aa74edf29d618be109f48a8e94c694f3701e08ca69186436", 1, "HASH160 0x14 0x9993a429037b5d912407a71c252019287b8d27a5 EQUAL", 987654321]], @@ -504,7 +504,7 @@ "010000000169c12106097dc2e0526493ef67f21269fe888ef05c7a3a5dacab38e1ac8387f1581b0000b64830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0121037a3fb04bcdb09eba90f69961ba1692a3528e45e67c85b200df820212d7594d334aad4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e01ffffffff0101000000000000000000000000", "P2SH,WITNESS"], ["BIP143: correct sighash (without FindAndDelete) = 71c9cd9b2869b9c70b01b1f0360c148f42dee72297db312638df136f43311f23"], [[["f18783ace138abac5d3a7a5cf08e88fe6912f267ef936452e0c27d090621c169", 7500, "0x00 0x20 0x9e1be07558ea5cc8e02ed1d80c0911048afad949affa36d5c3951e3159dbea19", 200000]], -"0100000000010169c12106097dc2e0526493ef67f21269fe888ef05c7a3a5dacab38e1ac8387f14c1d000000ffffffff01010000000000000000034830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e012102a9781d66b61fb5a7ef00ac5ad5bc6ffc78be7b44a566e3c87870e1079368df4c4aad4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0100000000", "P2SH,WITNESS"], +"0100000000010169c12106097dc2e0526493ef67f21269fe888ef05c7a3a5dacab38e1ac8387f14c1d000000ffffffff01010000000000000000034830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e012102a9781d66b61fb5a7ef00ac5ad5bc6ffc78be7b44a566e3c87870e1079368df4c4aad4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0100000000", "P2SH,WITNESS,CONST_SCRIPTCODE"], ["This is multisig version of the FindAndDelete tests"], ["Script is 2 CHECKMULTISIGVERIFY <sig1> <sig2> DROP"], ["52af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c0395960175"], @@ -514,7 +514,7 @@ "01000000019275cb8d4a485ce95741c013f7c0d28722160008021bb469a11982d47a662896581b0000fd6f01004830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c03959601522102cd74a2809ffeeed0092bc124fd79836706e41f048db3f6ae9df8708cefb83a1c2102e615999372426e46fd107b76eaf007156a507584aa2cc21de9eee3bdbd26d36c4c9552af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c0395960175ffffffff0101000000000000000000000000", "P2SH,WITNESS"], ["BIP143: correct sighash (without FindAndDelete) = c1628a1e7c67f14ca0c27c06e4fdeec2e6d1a73c7a91d7c046ff83e835aebb72"], [[["9628667ad48219a169b41b020800162287d2c0f713c04157e95c484a8dcb7592", 7500, "0x00 0x20 0x9b66c15b4e0b4eb49fa877982cafded24859fe5b0e2dbfbe4f0df1de7743fd52", 200000]], -"010000000001019275cb8d4a485ce95741c013f7c0d28722160008021bb469a11982d47a6628964c1d000000ffffffff0101000000000000000007004830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c0395960101022102966f109c54e85d3aee8321301136cedeb9fc710fdef58a9de8a73942f8e567c021034ffc99dd9a79dd3cb31e2ab3e0b09e0e67db41ac068c625cd1f491576016c84e9552af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c039596017500000000", "P2SH,WITNESS"], +"010000000001019275cb8d4a485ce95741c013f7c0d28722160008021bb469a11982d47a6628964c1d000000ffffffff0101000000000000000007004830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c0395960101022102966f109c54e85d3aee8321301136cedeb9fc710fdef58a9de8a73942f8e567c021034ffc99dd9a79dd3cb31e2ab3e0b09e0e67db41ac068c625cd1f491576016c84e9552af4830450220487fb382c4974de3f7d834c1b617fe15860828c7f96454490edd6d891556dcc9022100baf95feb48f845d5bfc9882eb6aeefa1bc3790e39f59eaa46ff7f15ae626c53e0148304502205286f726690b2e9b0207f0345711e63fa7012045b9eb0f19c2458ce1db90cf43022100e89f17f86abc5b149eba4115d4f128bcf45d77fb3ecdd34f594091340c039596017500000000", "P2SH,WITNESS,CONST_SCRIPTCODE"], ["Test long outputs, which are streamed using length-prefixed bitcoin strings. This might be surprising."], [[["1111111111111111111111111111111111111111111111111111111111111111", 0, "0x00 0x14 0x751e76e8199196d454941c45d1b3a323f1433bd6", 5000000]], diff --git a/src/test/getarg_tests.cpp b/src/test/getarg_tests.cpp index cd603b7f58..c065e25676 100644 --- a/src/test/getarg_tests.cpp +++ b/src/test/getarg_tests.cpp @@ -27,11 +27,21 @@ static void ResetArgs(const std::string& strArg) for (std::string& s : vecArg) vecChar.push_back(s.c_str()); - gArgs.ParseParameters(vecChar.size(), vecChar.data()); + std::string error; + gArgs.ParseParameters(vecChar.size(), vecChar.data(), error); +} + +static void SetupArgs(const std::vector<std::string>& args) +{ + gArgs.ClearArgs(); + for (const std::string& arg : args) { + gArgs.AddArg(arg, "", false, OptionsCategory::OPTIONS); + } } BOOST_AUTO_TEST_CASE(boolarg) { + SetupArgs({"-foo"}); ResetArgs("-foo"); BOOST_CHECK(gArgs.GetBoolArg("-foo", false)); BOOST_CHECK(gArgs.GetBoolArg("-foo", true)); @@ -84,6 +94,7 @@ BOOST_AUTO_TEST_CASE(boolarg) BOOST_AUTO_TEST_CASE(stringarg) { + SetupArgs({"-foo", "-bar"}); ResetArgs(""); BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", ""), ""); BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", "eleven"), "eleven"); @@ -108,6 +119,7 @@ BOOST_AUTO_TEST_CASE(stringarg) BOOST_AUTO_TEST_CASE(intarg) { + SetupArgs({"-foo", "-bar"}); ResetArgs(""); BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", 11), 11); BOOST_CHECK_EQUAL(gArgs.GetArg("-foo", 0), 0); @@ -127,6 +139,7 @@ BOOST_AUTO_TEST_CASE(intarg) BOOST_AUTO_TEST_CASE(doubledash) { + SetupArgs({"-foo", "-bar"}); ResetArgs("--foo"); BOOST_CHECK_EQUAL(gArgs.GetBoolArg("-foo", false), true); @@ -137,6 +150,7 @@ BOOST_AUTO_TEST_CASE(doubledash) BOOST_AUTO_TEST_CASE(boolargno) { + SetupArgs({"-foo", "-bar"}); ResetArgs("-nofoo"); BOOST_CHECK(!gArgs.GetBoolArg("-foo", true)); BOOST_CHECK(!gArgs.GetBoolArg("-foo", false)); diff --git a/src/test/mempool_tests.cpp b/src/test/mempool_tests.cpp index c4b18151a7..5ca243f42e 100644 --- a/src/test/mempool_tests.cpp +++ b/src/test/mempool_tests.cpp @@ -106,7 +106,7 @@ BOOST_AUTO_TEST_CASE(MempoolRemoveTest) } template<typename name> -static void CheckSort(CTxMemPool &pool, std::vector<std::string> &sortedOrder) +static void CheckSort(CTxMemPool &pool, std::vector<std::string> &sortedOrder) EXCLUSIVE_LOCKS_REQUIRED(pool.cs) { BOOST_CHECK_EQUAL(pool.size(), sortedOrder.size()); typename CTxMemPool::indexed_transaction_set::index<name>::type::iterator it = pool.mapTx.get<name>().begin(); diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index f561660fef..c05e60996d 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -97,6 +97,8 @@ static ScriptErrorDesc script_errors[]={ {SCRIPT_ERR_WITNESS_MALLEATED_P2SH, "WITNESS_MALLEATED_P2SH"}, {SCRIPT_ERR_WITNESS_UNEXPECTED, "WITNESS_UNEXPECTED"}, {SCRIPT_ERR_WITNESS_PUBKEYTYPE, "WITNESS_PUBKEYTYPE"}, + {SCRIPT_ERR_OP_CODESEPARATOR, "OP_CODESEPARATOR"}, + {SCRIPT_ERR_SIG_FINDANDDELETE, "SIG_FINDANDDELETE"}, }; static const char *FormatScriptError(ScriptError_t err) @@ -135,7 +137,7 @@ CMutableTransaction BuildCreditingTransaction(const CScript& scriptPubKey, int n return txCredit; } -CMutableTransaction BuildSpendingTransaction(const CScript& scriptSig, const CScriptWitness& scriptWitness, const CMutableTransaction& txCredit) +CMutableTransaction BuildSpendingTransaction(const CScript& scriptSig, const CScriptWitness& scriptWitness, const CTransaction& txCredit) { CMutableTransaction txSpend; txSpend.nVersion = 1; @@ -161,7 +163,7 @@ void DoTest(const CScript& scriptPubKey, const CScript& scriptSig, const CScript flags |= SCRIPT_VERIFY_WITNESS; } ScriptError err; - CMutableTransaction txCredit = BuildCreditingTransaction(scriptPubKey, nValue); + const CTransaction txCredit{BuildCreditingTransaction(scriptPubKey, nValue)}; CMutableTransaction tx = BuildSpendingTransaction(scriptSig, scriptWitness, txCredit); CMutableTransaction tx2 = tx; BOOST_CHECK_MESSAGE(VerifyScript(scriptSig, scriptPubKey, &scriptWitness, flags, MutableTransactionSignatureChecker(&tx, 0, txCredit.vout[0].nValue), &err) == expect, message); @@ -1071,7 +1073,7 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG12) CScript scriptPubKey12; scriptPubKey12 << OP_1 << ToByteVector(key1.GetPubKey()) << ToByteVector(key2.GetPubKey()) << OP_2 << OP_CHECKMULTISIG; - CMutableTransaction txFrom12 = BuildCreditingTransaction(scriptPubKey12); + const CTransaction txFrom12{BuildCreditingTransaction(scriptPubKey12)}; CMutableTransaction txTo12 = BuildSpendingTransaction(CScript(), CScriptWitness(), txFrom12); CScript goodsig1 = sign_multisig(scriptPubKey12, key1, txTo12); @@ -1102,7 +1104,7 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG23) CScript scriptPubKey23; scriptPubKey23 << OP_2 << ToByteVector(key1.GetPubKey()) << ToByteVector(key2.GetPubKey()) << ToByteVector(key3.GetPubKey()) << OP_3 << OP_CHECKMULTISIG; - CMutableTransaction txFrom23 = BuildCreditingTransaction(scriptPubKey23); + const CTransaction txFrom23{BuildCreditingTransaction(scriptPubKey23)}; CMutableTransaction txTo23 = BuildSpendingTransaction(CScript(), CScriptWitness(), txFrom23); std::vector<CKey> keys; diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index fe816a6f79..e9814edc23 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -11,6 +11,7 @@ #include <validation.h> #include <miner.h> #include <net_processing.h> +#include <pow.h> #include <ui_interface.h> #include <streams.h> #include <rpc/server.h> @@ -38,6 +39,12 @@ FastRandomContext insecure_rand_ctx(insecure_rand_seed); extern bool fPrintToConsole; extern void noui_connect(); +std::ostream& operator<<(std::ostream& os, const uint256& num) +{ + os << num.ToString(); + return os; +} + BasicTestingSetup::BasicTestingSetup(const std::string& chainName) { SHA256AutoDetect(); diff --git a/src/test/test_bitcoin.h b/src/test/test_bitcoin.h index 1f91eb622c..d013613de2 100644 --- a/src/test/test_bitcoin.h +++ b/src/test/test_bitcoin.h @@ -120,4 +120,7 @@ struct TestMemPoolEntryHelper CBlock getBlock13b8a(); +// define an implicit conversion here so that uint256 may be used directly in BOOST_CHECK_* +std::ostream& operator<<(std::ostream& os, const uint256& num); + #endif diff --git a/src/test/torcontrol_tests.cpp b/src/test/torcontrol_tests.cpp index 8bd5ce1222..c7ceb2f1e9 100644 --- a/src/test/torcontrol_tests.cpp +++ b/src/test/torcontrol_tests.cpp @@ -3,10 +3,18 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. // #include <test/test_bitcoin.h> -#include <torcontrol.cpp> +#include <torcontrol.h> #include <boost/test/unit_test.hpp> +#include <map> +#include <string> +#include <utility> + + +std::pair<std::string, std::string> SplitTorReplyLine(const std::string& s); +std::map<std::string, std::string> ParseTorReplyMapping(const std::string& s); + BOOST_FIXTURE_TEST_SUITE(torcontrol_tests, BasicTestingSetup) diff --git a/src/test/transaction_tests.cpp b/src/test/transaction_tests.cpp index c753e0a11d..cc72e96eb1 100644 --- a/src/test/transaction_tests.cpp +++ b/src/test/transaction_tests.cpp @@ -53,6 +53,7 @@ static std::map<std::string, unsigned int> mapFlagNames = { {std::string("WITNESS"), (unsigned int)SCRIPT_VERIFY_WITNESS}, {std::string("DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM"), (unsigned int)SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_WITNESS_PROGRAM}, {std::string("WITNESS_PUBKEYTYPE"), (unsigned int)SCRIPT_VERIFY_WITNESS_PUBKEYTYPE}, + {std::string("CONST_SCRIPTCODE"), (unsigned int)SCRIPT_VERIFY_CONST_SCRIPTCODE}, }; unsigned int ParseScriptFlags(std::string strFlags) diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp index eb23ba5ad2..06497667c3 100644 --- a/src/test/txvalidationcache_tests.cpp +++ b/src/test/txvalidationcache_tests.cpp @@ -24,7 +24,7 @@ bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsVi BOOST_AUTO_TEST_SUITE(tx_validationcache_tests) static bool -ToMemPool(CMutableTransaction& tx) +ToMemPool(const CMutableTransaction& tx) { LOCK(cs_main); diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 1c3acfb1a5..611ccc9b77 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -186,27 +186,37 @@ struct TestArgsManager : public ArgsManager LOCK(cs_args); m_config_args.clear(); } - ReadConfigStream(streamConfig); + std::string error; + ReadConfigStream(streamConfig, error); } void SetNetworkOnlyArg(const std::string arg) { LOCK(cs_args); m_network_only_args.insert(arg); } + void SetupArgs(int argv, const char* args[]) + { + for (int i = 0; i < argv; ++i) { + AddArg(args[i], "", false, OptionsCategory::OPTIONS); + } + } }; BOOST_AUTO_TEST_CASE(util_ParseParameters) { TestArgsManager testArgs; + const char* avail_args[] = {"-a", "-b", "-ccc", "-d"}; const char *argv_test[] = {"-ignored", "-a", "-b", "-ccc=argument", "-ccc=multiple", "f", "-d=e"}; - testArgs.ParseParameters(0, (char**)argv_test); + std::string error; + testArgs.SetupArgs(4, avail_args); + testArgs.ParseParameters(0, (char**)argv_test, error); BOOST_CHECK(testArgs.GetOverrideArgs().empty() && testArgs.GetConfigArgs().empty()); - testArgs.ParseParameters(1, (char**)argv_test); + testArgs.ParseParameters(1, (char**)argv_test, error); BOOST_CHECK(testArgs.GetOverrideArgs().empty() && testArgs.GetConfigArgs().empty()); - testArgs.ParseParameters(7, (char**)argv_test); + testArgs.ParseParameters(7, (char**)argv_test, error); // expectation: -ignored is ignored (program name argument), // -a, -b and -ccc end up in map, -d ignored because it is after // a non-option argument (non-GNU option parsing) @@ -227,9 +237,12 @@ BOOST_AUTO_TEST_CASE(util_ParseParameters) BOOST_AUTO_TEST_CASE(util_GetBoolArg) { TestArgsManager testArgs; + const char* avail_args[] = {"-a", "-b", "-c", "-d", "-e", "-f"}; const char *argv_test[] = { "ignored", "-a", "-nob", "-c=0", "-d=1", "-e=false", "-f=true"}; - testArgs.ParseParameters(7, (char**)argv_test); + std::string error; + testArgs.SetupArgs(6, avail_args); + testArgs.ParseParameters(7, (char**)argv_test, error); // Each letter should be set. for (char opt : "abcdef") @@ -261,8 +274,11 @@ BOOST_AUTO_TEST_CASE(util_GetBoolArgEdgeCases) TestArgsManager testArgs; // Params test + const char* avail_args[] = {"-foo", "-bar"}; const char *argv_test[] = {"ignored", "-nofoo", "-foo", "-nobar=0"}; - testArgs.ParseParameters(4, (char**)argv_test); + testArgs.SetupArgs(2, avail_args); + std::string error; + testArgs.ParseParameters(4, (char**)argv_test, error); // This was passed twice, second one overrides the negative setting. BOOST_CHECK(!testArgs.IsArgNegated("-foo")); @@ -274,7 +290,7 @@ BOOST_AUTO_TEST_CASE(util_GetBoolArgEdgeCases) // Config test const char *conf_test = "nofoo=1\nfoo=1\nnobar=0\n"; - testArgs.ParseParameters(1, (char**)argv_test); + testArgs.ParseParameters(1, (char**)argv_test, error); testArgs.ReadConfigString(conf_test); // This was passed twice, second one overrides the negative setting, @@ -289,7 +305,7 @@ BOOST_AUTO_TEST_CASE(util_GetBoolArgEdgeCases) // Combined test const char *combo_test_args[] = {"ignored", "-nofoo", "-bar"}; const char *combo_test_conf = "foo=1\nnobar=1\n"; - testArgs.ParseParameters(3, (char**)combo_test_args); + testArgs.ParseParameters(3, (char**)combo_test_args, error); testArgs.ReadConfigString(combo_test_conf); // Command line overrides, but doesn't erase old setting @@ -329,6 +345,8 @@ BOOST_AUTO_TEST_CASE(util_ReadConfigStream) "iii=2\n"; TestArgsManager test_args; + const char* avail_args[] = {"-a", "-b", "-ccc", "-d", "-e", "-fff", "-ggg", "-h", "-i", "-iii"}; + test_args.SetupArgs(10, avail_args); test_args.ReadConfigString(str_config); // expectation: a, b, ccc, d, fff, ggg, h, i end up in map @@ -526,6 +544,8 @@ BOOST_AUTO_TEST_CASE(util_GetArg) BOOST_AUTO_TEST_CASE(util_GetChainName) { TestArgsManager test_args; + const char* avail_args[] = {"-testnet", "-regtest"}; + test_args.SetupArgs(2, avail_args); const char* argv_testnet[] = {"cmd", "-testnet"}; const char* argv_regtest[] = {"cmd", "-regtest"}; @@ -535,39 +555,40 @@ BOOST_AUTO_TEST_CASE(util_GetChainName) // equivalent to "-testnet" // regtest in testnet section is ignored const char* testnetconf = "testnet=1\nregtest=0\n[test]\nregtest=1"; + std::string error; - test_args.ParseParameters(0, (char**)argv_testnet); + test_args.ParseParameters(0, (char**)argv_testnet, error); BOOST_CHECK_EQUAL(test_args.GetChainName(), "main"); - test_args.ParseParameters(2, (char**)argv_testnet); + test_args.ParseParameters(2, (char**)argv_testnet, error); BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); - test_args.ParseParameters(2, (char**)argv_regtest); + test_args.ParseParameters(2, (char**)argv_regtest, error); BOOST_CHECK_EQUAL(test_args.GetChainName(), "regtest"); - test_args.ParseParameters(3, (char**)argv_test_no_reg); + test_args.ParseParameters(3, (char**)argv_test_no_reg, error); BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); - test_args.ParseParameters(3, (char**)argv_both); + test_args.ParseParameters(3, (char**)argv_both, error); BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error); - test_args.ParseParameters(0, (char**)argv_testnet); + test_args.ParseParameters(0, (char**)argv_testnet, error); test_args.ReadConfigString(testnetconf); BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); - test_args.ParseParameters(2, (char**)argv_testnet); + test_args.ParseParameters(2, (char**)argv_testnet, error); test_args.ReadConfigString(testnetconf); BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); - test_args.ParseParameters(2, (char**)argv_regtest); + test_args.ParseParameters(2, (char**)argv_regtest, error); test_args.ReadConfigString(testnetconf); BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error); - test_args.ParseParameters(3, (char**)argv_test_no_reg); + test_args.ParseParameters(3, (char**)argv_test_no_reg, error); test_args.ReadConfigString(testnetconf); BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); - test_args.ParseParameters(3, (char**)argv_both); + test_args.ParseParameters(3, (char**)argv_both, error); test_args.ReadConfigString(testnetconf); BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error); @@ -575,23 +596,23 @@ BOOST_AUTO_TEST_CASE(util_GetChainName) // [test] regtest=1 potentially relevant) doesn't break things test_args.SelectConfigNetwork("test"); - test_args.ParseParameters(0, (char**)argv_testnet); + test_args.ParseParameters(0, (char**)argv_testnet, error); test_args.ReadConfigString(testnetconf); BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); - test_args.ParseParameters(2, (char**)argv_testnet); + test_args.ParseParameters(2, (char**)argv_testnet, error); test_args.ReadConfigString(testnetconf); BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); - test_args.ParseParameters(2, (char**)argv_regtest); + test_args.ParseParameters(2, (char**)argv_regtest, error); test_args.ReadConfigString(testnetconf); BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error); - test_args.ParseParameters(2, (char**)argv_test_no_reg); + test_args.ParseParameters(2, (char**)argv_test_no_reg, error); test_args.ReadConfigString(testnetconf); BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); - test_args.ParseParameters(3, (char**)argv_both); + test_args.ParseParameters(3, (char**)argv_both, error); test_args.ReadConfigString(testnetconf); BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error); } @@ -1065,6 +1086,7 @@ static void TestOtherProcess(fs::path dirname, std::string lockname, int fd) ReleaseDirectoryLocks(); ch = true; // Always succeeds rv = write(fd, &ch, 1); + assert(rv == 1); break; case ExitCommand: close(fd); diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp new file mode 100644 index 0000000000..37c4f79133 --- /dev/null +++ b/src/test/validation_block_tests.cpp @@ -0,0 +1,184 @@ +// Copyright (c) 2018 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 <boost/test/unit_test.hpp> + +#include <chainparams.h> +#include <consensus/merkle.h> +#include <consensus/validation.h> +#include <miner.h> +#include <pow.h> +#include <random.h> +#include <test/test_bitcoin.h> +#include <validation.h> +#include <validationinterface.h> + +struct RegtestingSetup : public TestingSetup { + RegtestingSetup() : TestingSetup(CBaseChainParams::REGTEST) {} +}; + +BOOST_FIXTURE_TEST_SUITE(validation_block_tests, RegtestingSetup) + +struct TestSubscriber : public CValidationInterface { + uint256 m_expected_tip; + + TestSubscriber(uint256 tip) : m_expected_tip(tip) {} + + void UpdatedBlockTip(const CBlockIndex* pindexNew, const CBlockIndex* pindexFork, bool fInitialDownload) override + { + BOOST_CHECK_EQUAL(m_expected_tip, pindexNew->GetBlockHash()); + } + + void BlockConnected(const std::shared_ptr<const CBlock>& block, const CBlockIndex* pindex, const std::vector<CTransactionRef>& txnConflicted) override + { + BOOST_CHECK_EQUAL(m_expected_tip, block->hashPrevBlock); + BOOST_CHECK_EQUAL(m_expected_tip, pindex->pprev->GetBlockHash()); + + m_expected_tip = block->GetHash(); + } + + void BlockDisconnected(const std::shared_ptr<const CBlock>& block) override + { + BOOST_CHECK_EQUAL(m_expected_tip, block->GetHash()); + + m_expected_tip = block->hashPrevBlock; + } +}; + +std::shared_ptr<CBlock> Block(const uint256& prev_hash) +{ + static int i = 0; + static uint64_t time = Params().GenesisBlock().nTime; + + CScript pubKey; + pubKey << i++ << OP_TRUE; + + auto ptemplate = BlockAssembler(Params()).CreateNewBlock(pubKey, false); + auto pblock = std::make_shared<CBlock>(ptemplate->block); + pblock->hashPrevBlock = prev_hash; + pblock->nTime = ++time; + + CMutableTransaction txCoinbase(*pblock->vtx[0]); + txCoinbase.vout.resize(1); + txCoinbase.vin[0].scriptWitness.SetNull(); + pblock->vtx[0] = MakeTransactionRef(std::move(txCoinbase)); + + return pblock; +} + +std::shared_ptr<CBlock> FinalizeBlock(std::shared_ptr<CBlock> pblock) +{ + pblock->hashMerkleRoot = BlockMerkleRoot(*pblock); + + while (!CheckProofOfWork(pblock->GetHash(), pblock->nBits, Params().GetConsensus())) { + ++(pblock->nNonce); + } + + return pblock; +} + +// construct a valid block +const std::shared_ptr<const CBlock> GoodBlock(const uint256& prev_hash) +{ + return FinalizeBlock(Block(prev_hash)); +} + +// construct an invalid block (but with a valid header) +const std::shared_ptr<const CBlock> BadBlock(const uint256& prev_hash) +{ + auto pblock = Block(prev_hash); + + CMutableTransaction coinbase_spend; + coinbase_spend.vin.push_back(CTxIn(COutPoint(pblock->vtx[0]->GetHash(), 0), CScript(), 0)); + coinbase_spend.vout.push_back(pblock->vtx[0]->vout[0]); + + CTransactionRef tx = MakeTransactionRef(coinbase_spend); + pblock->vtx.push_back(tx); + + auto ret = FinalizeBlock(pblock); + return ret; +} + +void BuildChain(const uint256& root, int height, const unsigned int invalid_rate, const unsigned int branch_rate, const unsigned int max_size, std::vector<std::shared_ptr<const CBlock>>& blocks) +{ + if (height <= 0 || blocks.size() >= max_size) return; + + bool gen_invalid = GetRand(100) < invalid_rate; + bool gen_fork = GetRand(100) < branch_rate; + + const std::shared_ptr<const CBlock> pblock = gen_invalid ? BadBlock(root) : GoodBlock(root); + blocks.push_back(pblock); + if (!gen_invalid) { + BuildChain(pblock->GetHash(), height - 1, invalid_rate, branch_rate, max_size, blocks); + } + + if (gen_fork) { + blocks.push_back(GoodBlock(root)); + BuildChain(blocks.back()->GetHash(), height - 1, invalid_rate, branch_rate, max_size, blocks); + } +} + +BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering) +{ + // build a large-ish chain that's likely to have some forks + std::vector<std::shared_ptr<const CBlock>> blocks; + while (blocks.size() < 50) { + blocks.clear(); + BuildChain(Params().GenesisBlock().GetHash(), 100, 15, 10, 500, blocks); + } + + bool ignored; + CValidationState state; + std::vector<CBlockHeader> headers; + std::transform(blocks.begin(), blocks.end(), std::back_inserter(headers), [](std::shared_ptr<const CBlock> b) { return b->GetBlockHeader(); }); + + // Process all the headers so we understand the toplogy of the chain + BOOST_CHECK(ProcessNewBlockHeaders(headers, state, Params())); + + // Connect the genesis block and drain any outstanding events + ProcessNewBlock(Params(), std::make_shared<CBlock>(Params().GenesisBlock()), true, &ignored); + SyncWithValidationInterfaceQueue(); + + // subscribe to events (this subscriber will validate event ordering) + const CBlockIndex* initial_tip = nullptr; + { + LOCK(cs_main); + initial_tip = chainActive.Tip(); + } + TestSubscriber sub(initial_tip->GetBlockHash()); + RegisterValidationInterface(&sub); + + // create a bunch of threads that repeatedly process a block generated above at random + // this will create parallelism and randomness inside validation - the ValidationInterface + // will subscribe to events generated during block validation and assert on ordering invariance + boost::thread_group threads; + for (int i = 0; i < 10; i++) { + threads.create_thread([&blocks]() { + bool ignored; + for (int i = 0; i < 1000; i++) { + auto block = blocks[GetRand(blocks.size() - 1)]; + ProcessNewBlock(Params(), block, true, &ignored); + } + + // to make sure that eventually we process the full chain - do it here + for (auto block : blocks) { + if (block->vtx.size() == 1) { + bool processed = ProcessNewBlock(Params(), block, true, &ignored); + assert(processed); + } + } + }); + } + + threads.join_all(); + while (GetMainSignals().CallbacksPending() > 0) { + MilliSleep(100); + } + + UnregisterValidationInterface(&sub); + + BOOST_CHECK_EQUAL(sub.m_expected_tip, chainActive.Tip()->GetBlockHash()); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/timedata.cpp b/src/timedata.cpp index 27d08172f5..dfb8fe55af 100644 --- a/src/timedata.cpp +++ b/src/timedata.cpp @@ -17,7 +17,7 @@ static CCriticalSection cs_nTimeOffset; -static int64_t nTimeOffset = 0; +static int64_t nTimeOffset GUARDED_BY(cs_nTimeOffset) = 0; /** * "Never go to sea with two chronometers; take one or three." diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp index 717d1cf7e5..1791bfd7f7 100644 --- a/src/torcontrol.cpp +++ b/src/torcontrol.cpp @@ -251,7 +251,7 @@ bool TorControlConnection::Command(const std::string &cmd, const ReplyHandlerCB& * Grammar is implicitly defined in https://spec.torproject.org/control-spec by * the server reply formats for PROTOCOLINFO (S3.21) and AUTHCHALLENGE (S3.24). */ -static std::pair<std::string,std::string> SplitTorReplyLine(const std::string &s) +std::pair<std::string,std::string> SplitTorReplyLine(const std::string &s) { size_t ptr=0; std::string type; @@ -270,7 +270,7 @@ static std::pair<std::string,std::string> SplitTorReplyLine(const std::string &s * the server reply formats for PROTOCOLINFO (S3.21), AUTHCHALLENGE (S3.24), * and ADD_ONION (S3.27). See also sections 2.1 and 2.3. */ -static std::map<std::string,std::string> ParseTorReplyMapping(const std::string &s) +std::map<std::string,std::string> ParseTorReplyMapping(const std::string &s) { std::map<std::string,std::string> mapping; size_t ptr=0; @@ -773,4 +773,3 @@ void StopTorControl() gBase = nullptr; } } - diff --git a/src/txmempool.cpp b/src/txmempool.cpp index d03429ca81..bb585fc075 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -618,6 +618,7 @@ static void CheckInputsAndUpdateCoins(const CTransaction& tx, CCoinsViewCache& m void CTxMemPool::check(const CCoinsViewCache *pcoins) const { + LOCK(cs); if (nCheckFrequency == 0) return; @@ -632,7 +633,6 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const CCoinsViewCache mempoolDuplicate(const_cast<CCoinsViewCache*>(pcoins)); const int64_t spendheight = GetSpendHeight(mempoolDuplicate); - LOCK(cs); std::list<const CTxMemPoolEntry*> waitingOnDependants; for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) { unsigned int i = 0; diff --git a/src/txmempool.h b/src/txmempool.h index 3f9fb4850c..ca7b1cd4be 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -440,7 +440,7 @@ public: class CTxMemPool { private: - uint32_t nCheckFrequency; //!< Value n means that n times in 2^32 we check. + uint32_t nCheckFrequency GUARDED_BY(cs); //!< Value n means that n times in 2^32 we check. unsigned int nTransactionsUpdated; //!< Used by getblocktemplate to trigger CreateNewBlock() invocation CBlockPolicyEstimator* minerPolicyEstimator; @@ -484,7 +484,7 @@ public: > indexed_transaction_set; mutable CCriticalSection cs; - indexed_transaction_set mapTx; + indexed_transaction_set mapTx GUARDED_BY(cs); typedef indexed_transaction_set::nth_index<0>::type::iterator txiter; std::vector<std::pair<uint256, txiter> > vTxHashes; //!< All tx witness hashes/entries in mapTx, in random order @@ -496,8 +496,8 @@ public: }; typedef std::set<txiter, CompareIteratorByHash> setEntries; - const setEntries & GetMemPoolParents(txiter entry) const; - const setEntries & GetMemPoolChildren(txiter entry) const; + const setEntries & GetMemPoolParents(txiter entry) const EXCLUSIVE_LOCKS_REQUIRED(cs); + const setEntries & GetMemPoolChildren(txiter entry) const EXCLUSIVE_LOCKS_REQUIRED(cs); private: typedef std::map<txiter, setEntries, CompareIteratorByHash> cacheMap; @@ -515,7 +515,7 @@ private: std::vector<indexed_transaction_set::const_iterator> GetSortedDepthAndScore() const EXCLUSIVE_LOCKS_REQUIRED(cs); public: - indirectmap<COutPoint, const CTransaction*> mapNextTx; + indirectmap<COutPoint, const CTransaction*> mapNextTx GUARDED_BY(cs); std::map<uint256, CAmount> mapDeltas; /** Create a new CTxMemPool. @@ -529,7 +529,7 @@ public: * check does nothing. */ void check(const CCoinsViewCache *pcoins) const; - void setSanityCheck(double dFrequency = 1.0) { nCheckFrequency = static_cast<uint32_t>(dFrequency * 4294967295.0); } + void setSanityCheck(double dFrequency = 1.0) { LOCK(cs); nCheckFrequency = static_cast<uint32_t>(dFrequency * 4294967295.0); } // addUnchecked must updated state for all ancestors of a given transaction, // to track size/count of descendant transactions. First version of @@ -547,7 +547,7 @@ public: void removeForBlock(const std::vector<CTransactionRef>& vtx, unsigned int nBlockHeight); void clear(); - void _clear(); //lock free + void _clear() EXCLUSIVE_LOCKS_REQUIRED(cs); //lock free bool CompareDepthAndScore(const uint256& hasha, const uint256& hashb); void queryHashes(std::vector<uint256>& vtxid); bool isSpent(const COutPoint& outpoint) const; @@ -600,7 +600,7 @@ public: /** Populate setDescendants with all in-mempool descendants of hash. * Assumes that setDescendants includes all in-mempool descendants of anything * already in it. */ - void CalculateDescendants(txiter it, setEntries& setDescendants) const; + void CalculateDescendants(txiter it, setEntries& setDescendants) const EXCLUSIVE_LOCKS_REQUIRED(cs); /** The minimum fee to get into the mempool, which may itself not be enough * for larger-sized transactions. @@ -665,17 +665,17 @@ private: */ void UpdateForDescendants(txiter updateIt, cacheMap &cachedDescendants, - const std::set<uint256> &setExclude); + const std::set<uint256> &setExclude) EXCLUSIVE_LOCKS_REQUIRED(cs); /** Update ancestors of hash to add/remove it as a descendant transaction. */ - void UpdateAncestorsOf(bool add, txiter hash, setEntries &setAncestors); + void UpdateAncestorsOf(bool add, txiter hash, setEntries &setAncestors) EXCLUSIVE_LOCKS_REQUIRED(cs); /** Set ancestor state for an entry */ - void UpdateEntryForAncestors(txiter it, const setEntries &setAncestors); + void UpdateEntryForAncestors(txiter it, const setEntries &setAncestors) EXCLUSIVE_LOCKS_REQUIRED(cs); /** For each transaction being removed, update ancestors and any direct children. * If updateDescendants is true, then also update in-mempool descendants' * ancestor state. */ - void UpdateForRemoveFromMempool(const setEntries &entriesToRemove, bool updateDescendants); + void UpdateForRemoveFromMempool(const setEntries &entriesToRemove, bool updateDescendants) EXCLUSIVE_LOCKS_REQUIRED(cs); /** Sever link between specified transaction and direct children. */ - void UpdateChildrenForRemoval(txiter entry); + void UpdateChildrenForRemoval(txiter entry) EXCLUSIVE_LOCKS_REQUIRED(cs); /** Before calling removeUnchecked for a given transaction, * UpdateForRemoveFromMempool must be called on the entire (dependent) set @@ -685,7 +685,7 @@ private: * transactions in a chain before we've updated all the state for the * removal. */ - void removeUnchecked(txiter entry, MemPoolRemovalReason reason = MemPoolRemovalReason::UNKNOWN); + void removeUnchecked(txiter entry, MemPoolRemovalReason reason = MemPoolRemovalReason::UNKNOWN) EXCLUSIVE_LOCKS_REQUIRED(cs); }; /** diff --git a/src/ui_interface.h b/src/ui_interface.h index 33099b4e73..3add369df0 100644 --- a/src/ui_interface.h +++ b/src/ui_interface.h @@ -6,6 +6,7 @@ #ifndef BITCOIN_UI_INTERFACE_H #define BITCOIN_UI_INTERFACE_H +#include <memory> #include <stdint.h> #include <string> @@ -92,7 +93,7 @@ public: boost::signals2::signal<void ()> NotifyAlertChanged; /** A wallet has been loaded. */ - boost::signals2::signal<void (CWallet* wallet)> LoadWallet; + boost::signals2::signal<void (std::shared_ptr<CWallet> wallet)> LoadWallet; /** * Show progress e.g. for verifychain. diff --git a/src/util.cpp b/src/util.cpp index b4d0a61ab2..34483d95b0 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -412,7 +412,7 @@ void ArgsManager::SelectConfigNetwork(const std::string& network) m_network = network; } -void ArgsManager::ParseParameters(int argc, const char* const argv[]) +bool ArgsManager::ParseParameters(int argc, const char* const argv[], std::string& error) { LOCK(cs_args); m_override_args.clear(); @@ -444,7 +444,43 @@ void ArgsManager::ParseParameters(int argc, const char* const argv[]) } else { m_override_args[key].push_back(val); } + + // Check that the arg is known + if (!(IsSwitchChar(key[0]) && key.size() == 1)) { + if (!IsArgKnown(key, error)) { + error = strprintf("Invalid parameter %s", key.c_str()); + return false; + } + } + } + + // we do not allow -includeconf from command line, so we clear it here + auto it = m_override_args.find("-includeconf"); + if (it != m_override_args.end()) { + if (it->second.size() > 0) { + for (const auto& ic : it->second) { + fprintf(stderr, "warning: -includeconf cannot be used from commandline; ignoring -includeconf=%s\n", ic.c_str()); + } + m_override_args.erase(it); + } } + return true; +} + +bool ArgsManager::IsArgKnown(const std::string& key, std::string& error) +{ + size_t option_index = key.find('.'); + std::string arg_no_net; + if (option_index == std::string::npos) { + arg_no_net = key; + } else { + arg_no_net = std::string("-") + key.substr(option_index + 1, std::string::npos); + } + + for (const auto& arg_map : m_available_args) { + if (arg_map.second.count(arg_no_net)) return true; + } + return false; } std::vector<std::string> ArgsManager::GetArgs(const std::string& strArg) const @@ -536,6 +572,87 @@ void ArgsManager::ForceSetArg(const std::string& strArg, const std::string& strV m_override_args[strArg] = {strValue}; } +void ArgsManager::AddArg(const std::string& name, const std::string& help, const bool debug_only, const OptionsCategory& cat) +{ + // Split arg name from its help param + size_t eq_index = name.find('='); + if (eq_index == std::string::npos) { + eq_index = name.size(); + } + + std::map<std::string, Arg>& arg_map = m_available_args[cat]; + auto ret = arg_map.emplace(name.substr(0, eq_index), Arg(name.substr(eq_index, name.size() - eq_index), help, debug_only)); + assert(ret.second); // Make sure an insertion actually happened +} + +std::string ArgsManager::GetHelpMessage() +{ + const bool show_debug = gArgs.GetBoolArg("-help-debug", false); + + std::string usage = ""; + for (const auto& arg_map : m_available_args) { + switch(arg_map.first) { + case OptionsCategory::OPTIONS: + usage += HelpMessageGroup("Options:"); + break; + case OptionsCategory::CONNECTION: + usage += HelpMessageGroup("Connection options:"); + break; + case OptionsCategory::ZMQ: + usage += HelpMessageGroup("ZeroMQ notification options:"); + break; + case OptionsCategory::DEBUG_TEST: + usage += HelpMessageGroup("Debugging/Testing options:"); + break; + case OptionsCategory::NODE_RELAY: + usage += HelpMessageGroup("Node relay options:"); + break; + case OptionsCategory::BLOCK_CREATION: + usage += HelpMessageGroup("Block creation options:"); + break; + case OptionsCategory::RPC: + usage += HelpMessageGroup("RPC server options:"); + break; + case OptionsCategory::WALLET: + usage += HelpMessageGroup("Wallet options:"); + break; + case OptionsCategory::WALLET_DEBUG_TEST: + if (show_debug) usage += HelpMessageGroup("Wallet debugging/testing options:"); + break; + case OptionsCategory::CHAINPARAMS: + usage += HelpMessageGroup("Chain selection options:"); + break; + case OptionsCategory::GUI: + usage += HelpMessageGroup("UI Options:"); + break; + case OptionsCategory::COMMANDS: + usage += HelpMessageGroup("Commands:"); + break; + case OptionsCategory::REGISTER_COMMANDS: + usage += HelpMessageGroup("Register Commands:"); + break; + default: + break; + } + + // When we get to the hidden options, stop + if (arg_map.first == OptionsCategory::HIDDEN) break; + + for (const auto& arg : arg_map.second) { + if (show_debug || !arg.second.m_debug_only) { + std::string name; + if (arg.second.m_help_param.empty()) { + name = arg.first; + } else { + name = arg.first + arg.second.m_help_param; + } + usage += HelpMessageOpt(name, arg.second.m_help_text); + } + } + } + return usage; +} + bool HelpRequested(const ArgsManager& args) { return args.IsArgSet("-?") || args.IsArgSet("-h") || args.IsArgSet("-help"); @@ -687,7 +804,7 @@ fs::path GetConfigFile(const std::string& confPath) return AbsPathForConfigVal(fs::path(confPath), false); } -void ArgsManager::ReadConfigStream(std::istream& stream) +bool ArgsManager::ReadConfigStream(std::istream& stream, std::string& error, bool ignore_invalid_keys) { LOCK(cs_args); @@ -698,33 +815,87 @@ void ArgsManager::ReadConfigStream(std::istream& stream) { std::string strKey = std::string("-") + it->string_key; std::string strValue = it->value[0]; + if (InterpretNegatedOption(strKey, strValue)) { m_config_args[strKey].clear(); } else { m_config_args[strKey].push_back(strValue); } + + // Check that the arg is known + if (!IsArgKnown(strKey, error) && !ignore_invalid_keys) { + error = strprintf("Invalid configuration value %s", it->string_key.c_str()); + return false; + } } + return true; } -void ArgsManager::ReadConfigFile(const std::string& confPath) +bool ArgsManager::ReadConfigFiles(std::string& error, bool ignore_invalid_keys) { { LOCK(cs_args); m_config_args.clear(); } + const std::string confPath = GetArg("-conf", BITCOIN_CONF_FILENAME); fs::ifstream stream(GetConfigFile(confPath)); // ok to not have a config file if (stream.good()) { - ReadConfigStream(stream); + if (!ReadConfigStream(stream, error, ignore_invalid_keys)) { + return false; + } + // if there is an -includeconf in the override args, but it is empty, that means the user + // passed '-noincludeconf' on the command line, in which case we should not include anything + if (m_override_args.count("-includeconf") == 0) { + std::vector<std::string> includeconf(GetArgs("-includeconf")); + { + // We haven't set m_network yet (that happens in SelectParams()), so manually check + // for network.includeconf args. + std::vector<std::string> includeconf_net(GetArgs(std::string("-") + GetChainName() + ".includeconf")); + includeconf.insert(includeconf.end(), includeconf_net.begin(), includeconf_net.end()); + } + + // Remove -includeconf from configuration, so we can warn about recursion + // later + { + LOCK(cs_args); + m_config_args.erase("-includeconf"); + m_config_args.erase(std::string("-") + GetChainName() + ".includeconf"); + } + + for (const std::string& to_include : includeconf) { + fs::ifstream include_config(GetConfigFile(to_include)); + if (include_config.good()) { + if (!ReadConfigStream(include_config, error, ignore_invalid_keys)) { + return false; + } + LogPrintf("Included configuration file %s\n", to_include.c_str()); + } else { + fprintf(stderr, "Failed to include configuration file %s\n", to_include.c_str()); + } + } + + // Warn about recursive -includeconf + includeconf = GetArgs("-includeconf"); + { + std::vector<std::string> includeconf_net(GetArgs(std::string("-") + GetChainName() + ".includeconf")); + includeconf.insert(includeconf.end(), includeconf_net.begin(), includeconf_net.end()); + } + for (const std::string& to_include : includeconf) { + fprintf(stderr, "warning: -includeconf cannot be used from included files; ignoring -includeconf=%s\n", to_include.c_str()); + } + } } // If datadir is changed in .conf file: ClearDatadirCache(); if (!fs::is_directory(GetDataDir(false))) { - throw std::runtime_error(strprintf("specified data directory \"%s\" does not exist.", gArgs.GetArg("-datadir", "").c_str())); + error = strprintf("specified data directory \"%s\" does not exist.", gArgs.GetArg("-datadir", "").c_str()); + return false; } + return true; } std::string ArgsManager::GetChainName() const diff --git a/src/util.h b/src/util.h index 2da8023285..efd8a4bd9d 100644 --- a/src/util.h +++ b/src/util.h @@ -118,18 +118,46 @@ inline bool IsSwitchChar(char c) #endif } +enum class OptionsCategory { + OPTIONS, + CONNECTION, + WALLET, + WALLET_DEBUG_TEST, + ZMQ, + DEBUG_TEST, + CHAINPARAMS, + NODE_RELAY, + BLOCK_CREATION, + RPC, + GUI, + COMMANDS, + REGISTER_COMMANDS, + + HIDDEN // Always the last option to avoid printing these in the help +}; + class ArgsManager { protected: friend class ArgsManagerHelper; + struct Arg + { + std::string m_help_param; + std::string m_help_text; + bool m_debug_only; + + Arg(const std::string& help_param, const std::string& help_text, bool debug_only) : m_help_param(help_param), m_help_text(help_text), m_debug_only(debug_only) {}; + }; + mutable CCriticalSection cs_args; std::map<std::string, std::vector<std::string>> m_override_args; std::map<std::string, std::vector<std::string>> m_config_args; std::string m_network; std::set<std::string> m_network_only_args; + std::map<OptionsCategory, std::map<std::string, Arg>> m_available_args; - void ReadConfigStream(std::istream& stream); + bool ReadConfigStream(std::istream& stream, std::string& error, bool ignore_invalid_keys = false); public: ArgsManager(); @@ -139,8 +167,8 @@ public: */ void SelectConfigNetwork(const std::string& network); - void ParseParameters(int argc, const char*const argv[]); - void ReadConfigFile(const std::string& confPath); + bool ParseParameters(int argc, const char* const argv[], std::string& error); + bool ReadConfigFiles(std::string& error, bool ignore_invalid_keys = false); /** * Log warnings for options in m_section_only_args when @@ -229,6 +257,26 @@ public: * @return CBaseChainParams::MAIN by default; raises runtime error if an invalid combination is given. */ std::string GetChainName() const; + + /** + * Add argument + */ + void AddArg(const std::string& name, const std::string& help, const bool debug_only, const OptionsCategory& cat); + + /** + * Clear available arguments + */ + void ClearArgs() { m_available_args.clear(); } + + /** + * Get the help string + */ + std::string GetHelpMessage(); + + /** + * Check whether we know of this arg + */ + bool IsArgKnown(const std::string& key, std::string& error); }; extern ArgsManager gArgs; @@ -305,7 +353,7 @@ std::unique_ptr<T> MakeUnique(Args&&... args) * CPU-intensive and non-interactive. See SCHED_BATCH in sched(7) for details. * * @return The return value of sched_setschedule(), or 1 on systems without - * sched_setchedule(). + * sched_setschedule(). */ int ScheduleBatchPriority(void); diff --git a/src/validation.cpp b/src/validation.cpp index fc1f6477d5..9791d6e2d8 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -145,6 +145,12 @@ private: */ std::set<CBlockIndex*> m_failed_blocks; + /** + * the ChainState CriticalSection + * A lock that must be held when modifying this ChainState - held in ActivateBestChain() + */ + CCriticalSection m_cs_chainstate; + public: CChain chainActive; BlockMap mapBlockIndex; @@ -582,6 +588,12 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool if (fRequireStandard && !IsStandardTx(tx, reason, witnessEnabled)) return state.DoS(0, false, REJECT_NONSTANDARD, reason); + // Do not work on transactions that are too small. + // A transaction with 1 segwit input and 1 P2WPHK output has non-witness size of 82 bytes. + // Transactions smaller than this are not relayed to reduce unnecessary malloc overhead. + if (::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION | SERIALIZE_TRANSACTION_NO_WITNESS) < MIN_STANDARD_TX_NONWITNESS_SIZE) + return state.DoS(0, false, REJECT_NONSTANDARD, "tx-size-small"); + // Only accept nLockTime-using transactions that can be mined in the next // block; we don't want our mempool filled up with transactions that can't // be mined yet. @@ -1125,6 +1137,52 @@ bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus return true; } +bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& message_start) +{ + CDiskBlockPos hpos = pos; + hpos.nPos -= 8; // Seek back 8 bytes for meta header + CAutoFile filein(OpenBlockFile(hpos, true), SER_DISK, CLIENT_VERSION); + if (filein.IsNull()) { + return error("%s: OpenBlockFile failed for %s", __func__, pos.ToString()); + } + + try { + CMessageHeader::MessageStartChars blk_start; + unsigned int blk_size; + + filein >> blk_start >> blk_size; + + if (memcmp(blk_start, message_start, CMessageHeader::MESSAGE_START_SIZE)) { + return error("%s: Block magic mismatch for %s: %s versus expected %s", __func__, pos.ToString(), + HexStr(blk_start, blk_start + CMessageHeader::MESSAGE_START_SIZE), + HexStr(message_start, message_start + CMessageHeader::MESSAGE_START_SIZE)); + } + + if (blk_size > MAX_SIZE) { + return error("%s: Block data is larger than maximum deserialization size for %s: %s versus %s", __func__, pos.ToString(), + blk_size, MAX_SIZE); + } + + block.resize(blk_size); // Zeroing of memory is intentional here + filein.read((char*)block.data(), blk_size); + } catch(const std::exception& e) { + return error("%s: Read from block file failed: %s for %s", __func__, e.what(), pos.ToString()); + } + + return true; +} + +bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const CBlockIndex* pindex, const CMessageHeader::MessageStartChars& message_start) +{ + CDiskBlockPos block_pos; + { + LOCK(cs_main); + block_pos = pindex->GetBlockPos(); + } + + return ReadRawBlockFromDisk(block, block_pos, message_start); +} + CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams) { int halvings = nHeight / consensusParams.nSubsidyHalvingInterval; @@ -2513,6 +2571,7 @@ void CChainState::PruneBlockIndexCandidates() { bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindexMostWork, const std::shared_ptr<const CBlock>& pblock, bool& fInvalidFound, ConnectTrace& connectTrace) { AssertLockHeld(cs_main); + const CBlockIndex *pindexOldTip = chainActive.Tip(); const CBlockIndex *pindexFork = chainActive.FindFork(pindexMostWork); @@ -2551,8 +2610,9 @@ bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainPar if (!ConnectTip(state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : std::shared_ptr<const CBlock>(), connectTrace, disconnectpool)) { if (state.IsInvalid()) { // The block violates a consensus rule. - if (!state.CorruptionPossible()) - InvalidChainFound(vpindexToConnect.back()); + if (!state.CorruptionPossible()) { + InvalidChainFound(vpindexToConnect.front()); + } state = CValidationState(); fInvalidFound = true; fContinue = false; @@ -2628,6 +2688,12 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams& // sanely for performance or correctness! AssertLockNotHeld(cs_main); + // ABC maintains a fair degree of expensive-to-calculate internal state + // because this function periodically releases cs_main so that it does not lock up other threads for too long + // during large connects - and to allow for e.g. the callback queue to drain + // we use m_cs_chainstate to enforce mutual exclusion so that only one caller may execute this function at a time + LOCK(m_cs_chainstate); + CBlockIndex *pindexMostWork = nullptr; CBlockIndex *pindexNewTip = nullptr; int nStopAtHeight = gArgs.GetArg("-stopatheight", DEFAULT_STOPATHEIGHT); @@ -2641,45 +2707,53 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams& SyncWithValidationInterfaceQueue(); } - const CBlockIndex *pindexFork; - bool fInitialDownload; { LOCK(cs_main); - ConnectTrace connectTrace(mempool); // Destructed before cs_main is unlocked + CBlockIndex* starting_tip = chainActive.Tip(); + bool blocks_connected = false; + do { + // We absolutely may not unlock cs_main until we've made forward progress + // (with the exception of shutdown due to hardware issues, low disk space, etc). + ConnectTrace connectTrace(mempool); // Destructed before cs_main is unlocked + + if (pindexMostWork == nullptr) { + pindexMostWork = FindMostWorkChain(); + } - CBlockIndex *pindexOldTip = chainActive.Tip(); - if (pindexMostWork == nullptr) { - pindexMostWork = FindMostWorkChain(); - } + // Whether we have anything to do at all. + if (pindexMostWork == nullptr || pindexMostWork == chainActive.Tip()) { + break; + } - // Whether we have anything to do at all. - if (pindexMostWork == nullptr || pindexMostWork == chainActive.Tip()) - return true; + bool fInvalidFound = false; + std::shared_ptr<const CBlock> nullBlockPtr; + if (!ActivateBestChainStep(state, chainparams, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : nullBlockPtr, fInvalidFound, connectTrace)) + return false; + blocks_connected = true; - bool fInvalidFound = false; - std::shared_ptr<const CBlock> nullBlockPtr; - if (!ActivateBestChainStep(state, chainparams, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : nullBlockPtr, fInvalidFound, connectTrace)) - return false; + if (fInvalidFound) { + // Wipe cache, we may need another branch now. + pindexMostWork = nullptr; + } + pindexNewTip = chainActive.Tip(); - if (fInvalidFound) { - // Wipe cache, we may need another branch now. - pindexMostWork = nullptr; - } - pindexNewTip = chainActive.Tip(); - pindexFork = chainActive.FindFork(pindexOldTip); - fInitialDownload = IsInitialBlockDownload(); + for (const PerBlockConnectTrace& trace : connectTrace.GetBlocksConnected()) { + assert(trace.pblock && trace.pindex); + GetMainSignals().BlockConnected(trace.pblock, trace.pindex, trace.conflictedTxs); + } + } while (!chainActive.Tip() || (starting_tip && CBlockIndexWorkComparator()(chainActive.Tip(), starting_tip))); + if (!blocks_connected) return true; - for (const PerBlockConnectTrace& trace : connectTrace.GetBlocksConnected()) { - assert(trace.pblock && trace.pindex); - GetMainSignals().BlockConnected(trace.pblock, trace.pindex, trace.conflictedTxs); - } + const CBlockIndex* pindexFork = chainActive.FindFork(starting_tip); + bool fInitialDownload = IsInitialBlockDownload(); // Notify external listeners about the new tip. // Enqueue while holding cs_main to ensure that UpdatedBlockTip is called in the order in which blocks are connected - GetMainSignals().UpdatedBlockTip(pindexNewTip, pindexFork, fInitialDownload); - - // Always notify the UI if a new block tip was connected if (pindexFork != pindexNewTip) { + // Notify ValidationInterface subscribers + GetMainSignals().UpdatedBlockTip(pindexNewTip, pindexFork, fInitialDownload); + + // Always notify the UI if a new block tip was connected uiInterface.NotifyBlockTip(fInitialDownload, pindexNewTip); } } @@ -2703,6 +2777,7 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams& return true; } + bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock) { return g_chainstate.ActivateBestChain(state, chainparams, std::move(pblock)); } @@ -2834,6 +2909,7 @@ bool CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) { if (pindex->nStatus & BLOCK_FAILED_MASK) { pindex->nStatus &= ~BLOCK_FAILED_MASK; setDirtyBlockIndex.insert(pindex); + m_failed_blocks.erase(pindex); } pindex = pindex->pprev; } diff --git a/src/validation.h b/src/validation.h index 9b40100765..b5ab10786a 100644 --- a/src/validation.h +++ b/src/validation.h @@ -398,6 +398,8 @@ void InitScriptExecutionCache(); /** Functions for disk access for blocks */ bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos, const Consensus::Params& consensusParams); bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex, const Consensus::Params& consensusParams); +bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const CDiskBlockPos& pos, const CMessageHeader::MessageStartChars& message_start); +bool ReadRawBlockFromDisk(std::vector<uint8_t>& block, const CBlockIndex* pindex, const CMessageHeader::MessageStartChars& message_start); /** Functions for validating blocks and updating the block tree */ diff --git a/src/validationinterface.h b/src/validationinterface.h index 3a5fed0106..0ca82235da 100644 --- a/src/validationinterface.h +++ b/src/validationinterface.h @@ -61,7 +61,11 @@ protected: */ ~CValidationInterface() = default; /** - * Notifies listeners of updated block chain tip + * Notifies listeners when the block chain tip advances. + * + * When multiple blocks are connected at once, UpdatedBlockTip will be called on the final tip + * but may not be called on every intermediate tip. If the latter behavior is desired, + * subscribe to BlockConnected() instead. * * Called on a background thread. */ diff --git a/src/wallet/coincontrol.h b/src/wallet/coincontrol.h index 2f08162ee4..98b4298507 100644 --- a/src/wallet/coincontrol.h +++ b/src/wallet/coincontrol.h @@ -22,7 +22,7 @@ public: boost::optional<OutputType> m_change_type; //! If false, allows unselected inputs, but requires all selected inputs be used bool fAllowOtherInputs; - //! Includes watch only addresses which match the ISMINE_WATCH_SOLVABLE criteria + //! Includes watch only addresses which are solvable bool fAllowWatchOnly; //! Override automatic min/max checks on fee, m_feerate must be set if true bool fOverrideFeeRate; diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index 10a06e4b9a..410dd5009f 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -53,7 +53,7 @@ void CheckUniqueFileid(const BerkeleyEnvironment& env, const std::string& filena } CCriticalSection cs_db; -std::map<std::string, BerkeleyEnvironment> g_dbenvs; //!< Map from directory name to open db environment. +std::map<std::string, BerkeleyEnvironment> g_dbenvs GUARDED_BY(cs_db); //!< Map from directory name to open db environment. } // namespace BerkeleyEnvironment* GetWalletEnv(const fs::path& wallet_path, std::string& database_filename) @@ -102,7 +102,7 @@ void BerkeleyEnvironment::Close() int ret = dbenv->close(0); if (ret != 0) - LogPrintf("BerkeleyEnvironment::EnvShutdown: Error %d shutting down database environment: %s\n", ret, DbEnv::strerror(ret)); + LogPrintf("BerkeleyEnvironment::Close: Error %d closing database environment: %s\n", ret, DbEnv::strerror(ret)); if (!fMockDb) DbEnv((u_int32_t)0).remove(strPath.c_str(), 0); } @@ -168,8 +168,12 @@ bool BerkeleyEnvironment::Open(bool retry) nEnvFlags, S_IRUSR | S_IWUSR); if (ret != 0) { - dbenv->close(0); LogPrintf("BerkeleyEnvironment::Open: Error %d opening database environment: %s\n", ret, DbEnv::strerror(ret)); + int ret2 = dbenv->close(0); + if (ret2 != 0) { + LogPrintf("BerkeleyEnvironment::Open: Error %d closing failed database environment: %s\n", ret2, DbEnv::strerror(ret2)); + } + Reset(); if (retry) { // try moving the database env out of the way fs::path pathDatabaseBak = pathIn / strprintf("database.%d.bak", GetTime()); diff --git a/src/wallet/feebumper.cpp b/src/wallet/feebumper.cpp index 4d70dde72a..0eb85a6e5c 100644 --- a/src/wallet/feebumper.cpp +++ b/src/wallet/feebumper.cpp @@ -18,7 +18,7 @@ //! Check whether transaction has descendant in wallet or mempool, or has been //! mined, or conflicts with a mined transaction. Return a feebumper::Result. -static feebumper::Result PreconditionChecks(const CWallet* wallet, const CWalletTx& wtx, std::vector<std::string>& errors) +static feebumper::Result PreconditionChecks(const CWallet* wallet, const CWalletTx& wtx, std::vector<std::string>& errors) EXCLUSIVE_LOCKS_REQUIRED(wallet->cs_wallet) { if (wallet->HasWalletSpend(wtx.GetHash())) { errors.push_back("Transaction has descendants in the wallet"); @@ -185,7 +185,7 @@ Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoin // If the output is not large enough to pay the fee, fail. CAmount nDelta = new_fee - old_fee; assert(nDelta > 0); - mtx = *wtx.tx; + mtx = CMutableTransaction{*wtx.tx}; CTxOut* poutput = &(mtx.vout[nOutput]); if (poutput->nValue < nDelta) { errors.push_back("Change output is too small to bump the fee"); diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp index 06f9c730c0..daeff1d0e8 100644 --- a/src/wallet/init.cpp +++ b/src/wallet/init.cpp @@ -6,6 +6,7 @@ #include <chainparams.h> #include <init.h> #include <net.h> +#include <scheduler.h> #include <util.h> #include <utilmoneystr.h> #include <validation.h> @@ -18,7 +19,7 @@ class WalletInit : public WalletInitInterface { public: //! Return the wallets help message. - std::string GetHelpString(bool showDebug) const override; + void AddWalletOptions() const override; //! Wallets parameter interaction bool ParameterInteraction() const override; @@ -49,46 +50,38 @@ public: const WalletInitInterface& g_wallet_init_interface = WalletInit(); -std::string WalletInit::GetHelpString(bool showDebug) const +void WalletInit::AddWalletOptions() const { - std::string strUsage = HelpMessageGroup(_("Wallet options:")); - strUsage += HelpMessageOpt("-addresstype", strprintf("What type of addresses to use (\"legacy\", \"p2sh-segwit\", or \"bech32\", default: \"%s\")", FormatOutputType(DEFAULT_ADDRESS_TYPE))); - strUsage += HelpMessageOpt("-changetype", "What type of change to use (\"legacy\", \"p2sh-segwit\", or \"bech32\"). Default is same as -addresstype, except when -addresstype=p2sh-segwit a native segwit output is used when sending to a native segwit address)"); - strUsage += HelpMessageOpt("-disablewallet", _("Do not load the wallet and disable wallet RPC calls")); - strUsage += HelpMessageOpt("-discardfee=<amt>", strprintf(_("The fee rate (in %s/kB) that indicates your tolerance for discarding change by adding it to the fee (default: %s). " - "Note: An output is discarded if it is dust at this rate, but we will always discard up to the dust relay fee and a discard fee above that is limited by the fee estimate for the longest target"), - CURRENCY_UNIT, FormatMoney(DEFAULT_DISCARD_FEE))); - strUsage += HelpMessageOpt("-fallbackfee=<amt>", strprintf(_("A fee rate (in %s/kB) that will be used when fee estimation has insufficient data (default: %s)"), - CURRENCY_UNIT, FormatMoney(DEFAULT_FALLBACK_FEE))); - strUsage += HelpMessageOpt("-keypool=<n>", strprintf(_("Set key pool size to <n> (default: %u)"), DEFAULT_KEYPOOL_SIZE)); - strUsage += HelpMessageOpt("-mintxfee=<amt>", strprintf(_("Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s)"), - CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MINFEE))); - strUsage += HelpMessageOpt("-paytxfee=<amt>", strprintf(_("Fee (in %s/kB) to add to transactions you send (default: %s)"), - CURRENCY_UNIT, FormatMoney(CFeeRate{DEFAULT_PAY_TX_FEE}.GetFeePerK()))); - strUsage += HelpMessageOpt("-rescan", _("Rescan the block chain for missing wallet transactions on startup")); - strUsage += HelpMessageOpt("-salvagewallet", _("Attempt to recover private keys from a corrupt wallet on startup")); - strUsage += HelpMessageOpt("-spendzeroconfchange", strprintf(_("Spend unconfirmed change when sending transactions (default: %u)"), DEFAULT_SPEND_ZEROCONF_CHANGE)); - strUsage += HelpMessageOpt("-txconfirmtarget=<n>", strprintf(_("If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)"), DEFAULT_TX_CONFIRM_TARGET)); - strUsage += HelpMessageOpt("-upgradewallet", _("Upgrade wallet to latest format on startup")); - strUsage += HelpMessageOpt("-wallet=<path>", _("Specify wallet database path. Can be specified multiple times to load multiple wallets. Path is interpreted relative to <walletdir> if it is not absolute, and will be created if it does not exist (as a directory containing a wallet.dat file and log files). For backwards compatibility this will also accept names of existing data files in <walletdir>.)")); - strUsage += HelpMessageOpt("-walletbroadcast", _("Make the wallet broadcast transactions") + " " + strprintf(_("(default: %u)"), DEFAULT_WALLETBROADCAST)); - strUsage += HelpMessageOpt("-walletdir=<dir>", _("Specify directory to hold wallets (default: <datadir>/wallets if it exists, otherwise <datadir>)")); - strUsage += HelpMessageOpt("-walletnotify=<cmd>", _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)")); - strUsage += HelpMessageOpt("-walletrbf", strprintf(_("Send transactions with full-RBF opt-in enabled (RPC only, default: %u)"), DEFAULT_WALLET_RBF)); - strUsage += HelpMessageOpt("-zapwallettxes=<mode>", _("Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup") + - " " + _("(1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data)")); - - if (showDebug) - { - strUsage += HelpMessageGroup(_("Wallet debugging/testing options:")); - - strUsage += HelpMessageOpt("-dblogsize=<n>", strprintf("Flush wallet database activity from memory to disk log every <n> megabytes (default: %u)", DEFAULT_WALLET_DBLOGSIZE)); - strUsage += HelpMessageOpt("-flushwallet", strprintf("Run a thread to flush wallet periodically (default: %u)", DEFAULT_FLUSHWALLET)); - strUsage += HelpMessageOpt("-privdb", strprintf("Sets the DB_PRIVATE flag in the wallet db environment (default: %u)", DEFAULT_WALLET_PRIVDB)); - strUsage += HelpMessageOpt("-walletrejectlongchains", strprintf(_("Wallet will not create transactions that violate mempool chain limits (default: %u)"), DEFAULT_WALLET_REJECT_LONG_CHAINS)); - } - - return strUsage; + gArgs.AddArg("-addresstype", strprintf("What type of addresses to use (\"legacy\", \"p2sh-segwit\", or \"bech32\", default: \"%s\")", FormatOutputType(DEFAULT_ADDRESS_TYPE)), false, OptionsCategory::WALLET); + gArgs.AddArg("-changetype", "What type of change to use (\"legacy\", \"p2sh-segwit\", or \"bech32\"). Default is same as -addresstype, except when -addresstype=p2sh-segwit a native segwit output is used when sending to a native segwit address)", false, OptionsCategory::WALLET); + gArgs.AddArg("-disablewallet", "Do not load the wallet and disable wallet RPC calls", false, OptionsCategory::WALLET); + gArgs.AddArg("-discardfee=<amt>", strprintf("The fee rate (in %s/kB) that indicates your tolerance for discarding change by adding it to the fee (default: %s). " + "Note: An output is discarded if it is dust at this rate, but we will always discard up to the dust relay fee and a discard fee above that is limited by the fee estimate for the longest target", + CURRENCY_UNIT, FormatMoney(DEFAULT_DISCARD_FEE)), false, OptionsCategory::WALLET); + gArgs.AddArg("-fallbackfee=<amt>", strprintf("A fee rate (in %s/kB) that will be used when fee estimation has insufficient data (default: %s)", + CURRENCY_UNIT, FormatMoney(DEFAULT_FALLBACK_FEE)), false, OptionsCategory::WALLET); + gArgs.AddArg("-keypool=<n>", strprintf("Set key pool size to <n> (default: %u)", DEFAULT_KEYPOOL_SIZE), false, OptionsCategory::WALLET); + gArgs.AddArg("-mintxfee=<amt>", strprintf("Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s)", + CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MINFEE)), false, OptionsCategory::WALLET); + gArgs.AddArg("-paytxfee=<amt>", strprintf("Fee (in %s/kB) to add to transactions you send (default: %s)", + CURRENCY_UNIT, FormatMoney(CFeeRate{DEFAULT_PAY_TX_FEE}.GetFeePerK())), false, OptionsCategory::WALLET); + gArgs.AddArg("-rescan", "Rescan the block chain for missing wallet transactions on startup", false, OptionsCategory::WALLET); + gArgs.AddArg("-salvagewallet", "Attempt to recover private keys from a corrupt wallet on startup", false, OptionsCategory::WALLET); + gArgs.AddArg("-spendzeroconfchange", strprintf("Spend unconfirmed change when sending transactions (default: %u)", DEFAULT_SPEND_ZEROCONF_CHANGE), false, OptionsCategory::WALLET); + gArgs.AddArg("-txconfirmtarget=<n>", strprintf("If paytxfee is not set, include enough fee so transactions begin confirmation on average within n blocks (default: %u)", DEFAULT_TX_CONFIRM_TARGET), false, OptionsCategory::WALLET); + gArgs.AddArg("-upgradewallet", "Upgrade wallet to latest format on startup", false, OptionsCategory::WALLET); + gArgs.AddArg("-wallet=<path>", "Specify wallet database path. Can be specified multiple times to load multiple wallets. Path is interpreted relative to <walletdir> if it is not absolute, and will be created if it does not exist (as a directory containing a wallet.dat file and log files). For backwards compatibility this will also accept names of existing data files in <walletdir>.)", false, OptionsCategory::WALLET); + gArgs.AddArg("-walletbroadcast", strprintf("Make the wallet broadcast transactions (default: %u)", DEFAULT_WALLETBROADCAST), false, OptionsCategory::WALLET); + gArgs.AddArg("-walletdir=<dir>", "Specify directory to hold wallets (default: <datadir>/wallets if it exists, otherwise <datadir>)", false, OptionsCategory::WALLET); + gArgs.AddArg("-walletnotify=<cmd>", "Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)", false, OptionsCategory::WALLET); + gArgs.AddArg("-walletrbf", strprintf("Send transactions with full-RBF opt-in enabled (RPC only, default: %u)", DEFAULT_WALLET_RBF), false, OptionsCategory::WALLET); + gArgs.AddArg("-zapwallettxes=<mode>", "Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup" + " (1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data)", false, OptionsCategory::WALLET); + + gArgs.AddArg("-dblogsize=<n>", strprintf("Flush wallet database activity from memory to disk log every <n> megabytes (default: %u)", DEFAULT_WALLET_DBLOGSIZE), true, OptionsCategory::WALLET_DEBUG_TEST); + gArgs.AddArg("-flushwallet", strprintf("Run a thread to flush wallet periodically (default: %u)", DEFAULT_FLUSHWALLET), true, OptionsCategory::WALLET_DEBUG_TEST); + gArgs.AddArg("-privdb", strprintf("Sets the DB_PRIVATE flag in the wallet db environment (default: %u)", DEFAULT_WALLET_PRIVDB), true, OptionsCategory::WALLET_DEBUG_TEST); + gArgs.AddArg("-walletrejectlongchains", strprintf("Wallet will not create transactions that violate mempool chain limits (default: %u)", DEFAULT_WALLET_REJECT_LONG_CHAINS), true, OptionsCategory::WALLET_DEBUG_TEST); } bool WalletInit::ParameterInteraction() const @@ -197,55 +190,29 @@ bool WalletInit::Verify() const uiInterface.InitMessage(_("Verifying wallet(s)...")); + std::vector<std::string> wallet_files = gArgs.GetArgs("-wallet"); + + // Parameter interaction code should have thrown an error if -salvagewallet + // was enabled with more than wallet file, so the wallet_files size check + // here should have no effect. + bool salvage_wallet = gArgs.GetBoolArg("-salvagewallet", false) && wallet_files.size() <= 1; + // Keep track of each wallet absolute path to detect duplicates. std::set<fs::path> wallet_paths; - for (const std::string& walletFile : gArgs.GetArgs("-wallet")) { - // Do some checking on wallet path. It should be either a: - // - // 1. Path where a directory can be created. - // 2. Path to an existing directory. - // 3. Path to a symlink to a directory. - // 4. For backwards compatibility, the name of a data file in -walletdir. - fs::path wallet_path = fs::absolute(walletFile, GetWalletDir()); - fs::file_type path_type = fs::symlink_status(wallet_path).type(); - if (!(path_type == fs::file_not_found || path_type == fs::directory_file || - (path_type == fs::symlink_file && fs::is_directory(wallet_path)) || - (path_type == fs::regular_file && fs::path(walletFile).filename() == walletFile))) { - return InitError(strprintf( - _("Invalid -wallet path '%s'. -wallet path should point to a directory where wallet.dat and " - "database/log.?????????? files can be stored, a location where such a directory could be created, " - "or (for backwards compatibility) the name of an existing data file in -walletdir (%s)"), - walletFile, GetWalletDir())); - } + for (const auto wallet_file : wallet_files) { + fs::path wallet_path = fs::absolute(wallet_file, GetWalletDir()); if (!wallet_paths.insert(wallet_path).second) { - return InitError(strprintf(_("Error loading wallet %s. Duplicate -wallet filename specified."), walletFile)); + return InitError(strprintf(_("Error loading wallet %s. Duplicate -wallet filename specified."), wallet_file)); } - std::string strError; - if (!WalletBatch::VerifyEnvironment(wallet_path, strError)) { - return InitError(strError); - } - - if (gArgs.GetBoolArg("-salvagewallet", false)) { - // Recover readable keypairs: - CWallet dummyWallet("dummy", WalletDatabase::CreateDummy()); - std::string backup_filename; - if (!WalletBatch::Recover(wallet_path, (void *)&dummyWallet, WalletBatch::RecoverKeysOnlyFilter, backup_filename)) { - return false; - } - } - - std::string strWarning; - bool dbV = WalletBatch::VerifyDatabaseFile(wallet_path, strWarning, strError); - if (!strWarning.empty()) { - InitWarning(strWarning); - } - if (!dbV) { - InitError(strError); - return false; - } + std::string error_string; + std::string warning_string; + bool verify_success = CWallet::Verify(wallet_file, salvage_wallet, error_string, warning_string); + if (!error_string.empty()) InitError(error_string); + if (!warning_string.empty()) InitWarning(warning_string); + if (!verify_success) return false; } return true; @@ -259,7 +226,7 @@ bool WalletInit::Open() const } for (const std::string& walletFile : gArgs.GetArgs("-wallet")) { - CWallet * const pwallet = CWallet::CreateWalletFromFile(walletFile, fs::absolute(walletFile, GetWalletDir())); + std::shared_ptr<CWallet> pwallet = CWallet::CreateWalletFromFile(walletFile, fs::absolute(walletFile, GetWalletDir())); if (!pwallet) { return false; } @@ -271,29 +238,31 @@ bool WalletInit::Open() const void WalletInit::Start(CScheduler& scheduler) const { - for (CWallet* pwallet : GetWallets()) { - pwallet->postInitProcess(scheduler); + for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) { + pwallet->postInitProcess(); } + + // Run a thread to flush wallet periodically + scheduler.scheduleEvery(MaybeCompactWalletDB, 500); } void WalletInit::Flush() const { - for (CWallet* pwallet : GetWallets()) { + for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) { pwallet->Flush(false); } } void WalletInit::Stop() const { - for (CWallet* pwallet : GetWallets()) { + for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) { pwallet->Flush(true); } } void WalletInit::Close() const { - for (CWallet* pwallet : GetWallets()) { + for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) { RemoveWallet(pwallet); - delete pwallet; } } diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index e957c1b1ca..d09af1dbd1 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -56,7 +56,7 @@ static std::string DecodeDumpString(const std::string &str) { for (unsigned int pos = 0; pos < str.length(); pos++) { unsigned char c = str[pos]; if (c == '%' && pos+2 < str.length()) { - c = (((str[pos+1]>>6)*9+((str[pos+1]-'0')&15)) << 4) | + c = (((str[pos+1]>>6)*9+((str[pos+1]-'0')&15)) << 4) | ((str[pos+2]>>6)*9+((str[pos+2]-'0')&15)); pos += 2; } @@ -89,7 +89,8 @@ static bool GetWalletAddressesForKey(CWallet * const pwallet, const CKeyID &keyi UniValue importprivkey(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -185,7 +186,8 @@ UniValue importprivkey(const JSONRPCRequest& request) UniValue abortrescan(const JSONRPCRequest& request) { - CWallet* const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -209,7 +211,7 @@ UniValue abortrescan(const JSONRPCRequest& request) } static void ImportAddress(CWallet*, const CTxDestination& dest, const std::string& strLabel); -static void ImportScript(CWallet* const pwallet, const CScript& script, const std::string& strLabel, bool isRedeemScript) +static void ImportScript(CWallet* const pwallet, const CScript& script, const std::string& strLabel, bool isRedeemScript) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) { if (!isRedeemScript && ::IsMine(*pwallet, script) == ISMINE_SPENDABLE) { throw JSONRPCError(RPC_WALLET_ERROR, "The wallet already contains the private key for this address or script"); @@ -235,7 +237,7 @@ static void ImportScript(CWallet* const pwallet, const CScript& script, const st } } -static void ImportAddress(CWallet* const pwallet, const CTxDestination& dest, const std::string& strLabel) +static void ImportAddress(CWallet* const pwallet, const CTxDestination& dest, const std::string& strLabel) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) { CScript script = GetScriptForDestination(dest); ImportScript(pwallet, script, strLabel, false); @@ -246,7 +248,8 @@ static void ImportAddress(CWallet* const pwallet, const CTxDestination& dest, co UniValue importaddress(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -330,7 +333,8 @@ UniValue importaddress(const JSONRPCRequest& request) UniValue importprunedfunds(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -392,7 +396,8 @@ UniValue importprunedfunds(const JSONRPCRequest& request) UniValue removeprunedfunds(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -430,7 +435,8 @@ UniValue removeprunedfunds(const JSONRPCRequest& request) UniValue importpubkey(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -506,7 +512,8 @@ UniValue importpubkey(const JSONRPCRequest& request) UniValue importwallet(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -552,7 +559,7 @@ UniValue importwallet(const JSONRPCRequest& request) file.seekg(0, file.beg); // Use uiInterface.ShowProgress instead of pwallet.ShowProgress because pwallet.ShowProgress has a cancel button tied to AbortRescan which - // we don't want for this progress bar shoing the import progress. uiInterface.ShowProgress does not have a cancel button. + // we don't want for this progress bar showing the import progress. uiInterface.ShowProgress does not have a cancel button. uiInterface.ShowProgress(_("Importing..."), 0, false); // show progress dialog in GUI while (file.good()) { uiInterface.ShowProgress("", std::max(1, std::min(99, (int)(((double)file.tellg() / (double)nFilesize) * 100))), false); @@ -640,7 +647,8 @@ UniValue importwallet(const JSONRPCRequest& request) UniValue dumpprivkey(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -683,7 +691,8 @@ UniValue dumpprivkey(const JSONRPCRequest& request) UniValue dumpwallet(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -752,13 +761,13 @@ UniValue dumpwallet(const JSONRPCRequest& request) file << "\n"; // add the base58check encoded extended master if the wallet uses HD - CKeyID masterKeyID = pwallet->GetHDChain().masterKeyID; - if (!masterKeyID.IsNull()) + CKeyID seed_id = pwallet->GetHDChain().seed_id; + if (!seed_id.IsNull()) { - CKey key; - if (pwallet->GetKey(masterKeyID, key)) { + CKey seed; + if (pwallet->GetKey(seed_id, seed)) { CExtKey masterKey; - masterKey.SetMaster(key.begin(), key.size()); + masterKey.SetSeed(seed.begin(), seed.size()); file << "# extended private masterkey: " << EncodeExtKey(masterKey) << "\n\n"; } @@ -773,12 +782,12 @@ UniValue dumpwallet(const JSONRPCRequest& request) file << strprintf("%s %s ", EncodeSecret(key), strTime); if (GetWalletAddressesForKey(pwallet, keyid, strAddr, strLabel)) { file << strprintf("label=%s", strLabel); - } else if (keyid == masterKeyID) { - file << "hdmaster=1"; + } else if (keyid == seed_id) { + file << "hdseed=1"; } else if (mapKeyPool.count(keyid)) { file << "reserve=1"; - } else if (pwallet->mapKeyMetadata[keyid].hdKeypath == "m") { - file << "inactivehdmaster=1"; + } else if (pwallet->mapKeyMetadata[keyid].hdKeypath == "s") { + file << "inactivehdseed=1"; } else { file << "change=1"; } @@ -811,7 +820,7 @@ UniValue dumpwallet(const JSONRPCRequest& request) } -static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int64_t timestamp) +static UniValue ProcessImport(CWallet * const pwallet, const UniValue& data, const int64_t timestamp) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) { try { bool success = false; @@ -1127,7 +1136,8 @@ static int64_t GetImportTimestamp(const UniValue& data, int64_t now) UniValue importmulti(const JSONRPCRequest& mainRequest) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(mainRequest); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(mainRequest); + CWallet* const pwallet = wallet.get(); if (!EnsureWalletIsAvailable(pwallet, mainRequest.fHelp)) { return NullUniValue; } diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 94e61aa20e..456f08bc14 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -40,17 +40,17 @@ static const std::string WALLET_ENDPOINT_BASE = "/wallet/"; -CWallet *GetWalletForJSONRPCRequest(const JSONRPCRequest& request) +std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& request) { if (request.URI.substr(0, WALLET_ENDPOINT_BASE.size()) == WALLET_ENDPOINT_BASE) { // wallet endpoint was used std::string requestedWallet = urlDecode(request.URI.substr(WALLET_ENDPOINT_BASE.size())); - CWallet* pwallet = GetWallet(requestedWallet); + std::shared_ptr<CWallet> pwallet = GetWallet(requestedWallet); if (!pwallet) throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Requested wallet does not exist or is not loaded"); return pwallet; } - std::vector<CWallet*> wallets = GetWallets(); + std::vector<std::shared_ptr<CWallet>> wallets = GetWallets(); return wallets.size() == 1 || (request.fHelp && wallets.size() > 0) ? wallets[0] : nullptr; } @@ -134,7 +134,9 @@ static std::string LabelFromValue(const UniValue& value) static UniValue getnewaddress(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -198,7 +200,9 @@ CTxDestination GetLabelDestination(CWallet* const pwallet, const std::string& la static UniValue getlabeladdress(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -256,7 +260,9 @@ static UniValue getlabeladdress(const JSONRPCRequest& request) static UniValue getrawchangeaddress(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -304,7 +310,9 @@ static UniValue getrawchangeaddress(const JSONRPCRequest& request) static UniValue setlabel(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -358,7 +366,9 @@ static UniValue setlabel(const JSONRPCRequest& request) static UniValue getaccount(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -401,7 +411,9 @@ static UniValue getaccount(const JSONRPCRequest& request) static UniValue getaddressesbyaccount(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -487,7 +499,9 @@ static CTransactionRef SendMoney(CWallet * const pwallet, const CTxDestination & static UniValue sendtoaddress(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -574,7 +588,9 @@ static UniValue sendtoaddress(const JSONRPCRequest& request) static UniValue listaddressgroupings(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -631,7 +647,9 @@ static UniValue listaddressgroupings(const JSONRPCRequest& request) static UniValue signmessage(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -692,7 +710,9 @@ static UniValue signmessage(const JSONRPCRequest& request) static UniValue getreceivedbyaddress(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -757,7 +777,9 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request) static UniValue getreceivedbylabel(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -827,7 +849,9 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request) static UniValue getbalance(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -910,7 +934,9 @@ static UniValue getbalance(const JSONRPCRequest& request) static UniValue getunconfirmedbalance(const JSONRPCRequest &request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -932,7 +958,9 @@ static UniValue getunconfirmedbalance(const JSONRPCRequest &request) static UniValue movecmd(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -989,7 +1017,9 @@ static UniValue movecmd(const JSONRPCRequest& request) static UniValue sendfrom(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -1062,7 +1092,9 @@ static UniValue sendfrom(const JSONRPCRequest& request) static UniValue sendmany(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -1257,7 +1289,9 @@ static UniValue sendmany(const JSONRPCRequest& request) static UniValue addmultisigaddress(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -1392,7 +1426,9 @@ public: static UniValue addwitnessaddress(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -1624,7 +1660,9 @@ static UniValue ListReceived(CWallet * const pwallet, const UniValue& params, bo static UniValue listreceivedbyaddress(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -1673,7 +1711,9 @@ static UniValue listreceivedbyaddress(const JSONRPCRequest& request) static UniValue listreceivedbylabel(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -1838,7 +1878,9 @@ static void AcentryToJSON(const CAccountingEntry& acentry, const std::string& st UniValue listtransactions(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -2026,7 +2068,9 @@ UniValue listtransactions(const JSONRPCRequest& request) static UniValue listaccounts(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -2119,7 +2163,9 @@ static UniValue listaccounts(const JSONRPCRequest& request) static UniValue listsinceblock(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -2258,7 +2304,9 @@ static UniValue listsinceblock(const JSONRPCRequest& request) static UniValue gettransaction(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -2352,7 +2400,9 @@ static UniValue gettransaction(const JSONRPCRequest& request) static UniValue abandontransaction(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -2396,7 +2446,9 @@ static UniValue abandontransaction(const JSONRPCRequest& request) static UniValue backupwallet(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -2429,7 +2481,9 @@ static UniValue backupwallet(const JSONRPCRequest& request) static UniValue keypoolrefill(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -2476,7 +2530,9 @@ static void LockWallet(CWallet* pWallet) static UniValue walletpassphrase(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -2549,7 +2605,9 @@ static UniValue walletpassphrase(const JSONRPCRequest& request) static UniValue walletpassphrasechange(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -2598,7 +2656,9 @@ static UniValue walletpassphrasechange(const JSONRPCRequest& request) static UniValue walletlock(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -2636,7 +2696,9 @@ static UniValue walletlock(const JSONRPCRequest& request) static UniValue encryptwallet(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -2696,7 +2758,9 @@ static UniValue encryptwallet(const JSONRPCRequest& request) static UniValue lockunspent(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -2823,7 +2887,9 @@ static UniValue lockunspent(const JSONRPCRequest& request) static UniValue listlockunspent(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -2874,7 +2940,9 @@ static UniValue listlockunspent(const JSONRPCRequest& request) static UniValue settxfee(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -2903,7 +2971,9 @@ static UniValue settxfee(const JSONRPCRequest& request) static UniValue getwalletinfo(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -2925,7 +2995,8 @@ static UniValue getwalletinfo(const JSONRPCRequest& request) " \"keypoolsize_hd_internal\": xxxx, (numeric) how many new keys are pre-generated for internal use (used for change outputs, only appears if the wallet is using this feature, otherwise external keys are used)\n" " \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n" " \"paytxfee\": x.xxxx, (numeric) the transaction fee configuration, set in " + CURRENCY_UNIT + "/kB\n" - " \"hdmasterkeyid\": \"<hash160>\" (string, optional) the Hash160 of the HD master pubkey (only present when HD is enabled)\n" + " \"hdseedid\": \"<hash160>\" (string, optional) the Hash160 of the HD seed (only present when HD is enabled)\n" + " \"hdmasterkeyid\": \"<hash160>\" (string, optional) alias for hdseedid retained for backwards-compatibility. Will be removed in V0.18.\n" "}\n" "\nExamples:\n" + HelpExampleCli("getwalletinfo", "") @@ -2949,16 +3020,18 @@ static UniValue getwalletinfo(const JSONRPCRequest& request) obj.pushKV("txcount", (int)pwallet->mapWallet.size()); obj.pushKV("keypoololdest", pwallet->GetOldestKeyPoolTime()); obj.pushKV("keypoolsize", (int64_t)kpExternalSize); - CKeyID masterKeyID = pwallet->GetHDChain().masterKeyID; - if (!masterKeyID.IsNull() && pwallet->CanSupportFeature(FEATURE_HD_SPLIT)) { + CKeyID seed_id = pwallet->GetHDChain().seed_id; + if (!seed_id.IsNull() && pwallet->CanSupportFeature(FEATURE_HD_SPLIT)) { obj.pushKV("keypoolsize_hd_internal", (int64_t)(pwallet->GetKeyPoolSize() - kpExternalSize)); } if (pwallet->IsCrypted()) { obj.pushKV("unlocked_until", pwallet->nRelockTime); } obj.pushKV("paytxfee", ValueFromAmount(pwallet->m_pay_tx_fee.GetFeePerK())); - if (!masterKeyID.IsNull()) - obj.pushKV("hdmasterkeyid", masterKeyID.GetHex()); + if (!seed_id.IsNull()) { + obj.pushKV("hdseedid", seed_id.GetHex()); + obj.pushKV("hdmasterkeyid", seed_id.GetHex()); + } return obj; } @@ -2981,22 +3054,118 @@ static UniValue listwallets(const JSONRPCRequest& request) UniValue obj(UniValue::VARR); - for (CWallet* pwallet : GetWallets()) { - if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { + for (const std::shared_ptr<CWallet>& wallet : GetWallets()) { + if (!EnsureWalletIsAvailable(wallet.get(), request.fHelp)) { return NullUniValue; } - LOCK(pwallet->cs_wallet); + LOCK(wallet->cs_wallet); + + obj.push_back(wallet->GetName()); + } + + return obj; +} + +UniValue loadwallet(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() != 1) + throw std::runtime_error( + "loadwallet \"filename\"\n" + "\nLoads a wallet from a wallet file or directory." + "\nNote that all wallet command-line options used when starting bitcoind will be" + "\napplied to the new wallet (eg -zapwallettxes, upgradewallet, rescan, etc).\n" + "\nArguments:\n" + "1. \"filename\" (string, required) The wallet directory or .dat file.\n" + "\nResult:\n" + "{\n" + " \"name\" : <wallet_name>, (string) The wallet name if loaded successfully.\n" + " \"warning\" : <warning>, (string) Warning message if wallet was not loaded cleanly.\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("loadwallet", "\"test.dat\"") + + HelpExampleRpc("loadwallet", "\"test.dat\"") + ); + std::string wallet_file = request.params[0].get_str(); + std::string error; + + fs::path wallet_path = fs::absolute(wallet_file, GetWalletDir()); + if (fs::symlink_status(wallet_path).type() == fs::file_not_found) { + throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Wallet " + wallet_file + " not found."); + } + + std::string warning; + if (!CWallet::Verify(wallet_file, false, error, warning)) { + throw JSONRPCError(RPC_WALLET_ERROR, "Wallet file verification failed: " + error); + } + + std::shared_ptr<CWallet> const wallet = CWallet::CreateWalletFromFile(wallet_file, fs::absolute(wallet_file, GetWalletDir())); + if (!wallet) { + throw JSONRPCError(RPC_WALLET_ERROR, "Wallet loading failed."); + } + AddWallet(wallet); + + wallet->postInitProcess(); + + UniValue obj(UniValue::VOBJ); + obj.pushKV("name", wallet->GetName()); + obj.pushKV("warning", warning); + + return obj; +} + +UniValue createwallet(const JSONRPCRequest& request) +{ + if (request.fHelp || request.params.size() != 1) { + throw std::runtime_error( + "createwallet \"wallet_name\"\n" + "\nCreates and loads a new wallet.\n" + "\nArguments:\n" + "1. \"wallet_name\" (string, required) The name for the new wallet. If this is a path, the wallet will be created at the path location.\n" + "\nResult:\n" + "{\n" + " \"name\" : <wallet_name>, (string) The wallet name if created successfully. If the wallet was created using a full path, the wallet_name will be the full path.\n" + " \"warning\" : <warning>, (string) Warning message if wallet was not loaded cleanly.\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("createwallet", "\"testwallet\"") + + HelpExampleRpc("createwallet", "\"testwallet\"") + ); + } + std::string wallet_name = request.params[0].get_str(); + std::string error; + std::string warning; + + fs::path wallet_path = fs::absolute(wallet_name, GetWalletDir()); + if (fs::symlink_status(wallet_path).type() != fs::file_not_found) { + throw JSONRPCError(RPC_WALLET_ERROR, "Wallet " + wallet_name + " already exists."); + } - obj.push_back(pwallet->GetName()); + // Wallet::Verify will check if we're trying to create a wallet with a duplication name. + if (!CWallet::Verify(wallet_name, false, error, warning)) { + throw JSONRPCError(RPC_WALLET_ERROR, "Wallet file verification failed: " + error); } + std::shared_ptr<CWallet> const wallet = CWallet::CreateWalletFromFile(wallet_name, fs::absolute(wallet_name, GetWalletDir())); + if (!wallet) { + throw JSONRPCError(RPC_WALLET_ERROR, "Wallet creation failed."); + } + AddWallet(wallet); + + wallet->postInitProcess(); + + UniValue obj(UniValue::VOBJ); + obj.pushKV("name", wallet->GetName()); + obj.pushKV("warning", warning); + return obj; } static UniValue resendwallettransactions(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -3031,7 +3200,9 @@ static UniValue resendwallettransactions(const JSONRPCRequest& request) static UniValue listunspent(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -3202,7 +3373,9 @@ static UniValue listunspent(const JSONRPCRequest& request) static UniValue fundrawtransaction(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -3402,7 +3575,9 @@ static UniValue fundrawtransaction(const JSONRPCRequest& request) UniValue signrawtransactionwithwallet(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -3471,7 +3646,9 @@ UniValue signrawtransactionwithwallet(const JSONRPCRequest& request) static UniValue bumpfee(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) return NullUniValue; @@ -3620,7 +3797,9 @@ static UniValue bumpfee(const JSONRPCRequest& request) UniValue generate(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; @@ -3665,7 +3844,9 @@ UniValue generate(const JSONRPCRequest& request) UniValue rescanblockchain(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -3870,7 +4051,9 @@ static UniValue AddressBookDataToJSON(const CAddressBookData& data, const bool v UniValue getaddressinfo(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -3901,13 +4084,14 @@ UniValue getaddressinfo(const JSONRPCRequest& request) " ]\n" " \"sigsrequired\" : xxxxx (numeric, optional) Number of signatures required to spend multisig output (only if \"script\" is \"multisig\")\n" " \"pubkey\" : \"publickeyhex\", (string, optional) The hex value of the raw public key, for single-key addresses (possibly embedded in P2SH or P2WSH)\n" - " \"embedded\" : {...}, (object, optional) Information about the address embedded in P2SH or P2WSH, if relevant and known. It includes all getaddressinfo output fields for the embedded address, excluding metadata (\"timestamp\", \"hdkeypath\", \"hdmasterkeyid\") and relation to the wallet (\"ismine\", \"iswatchonly\", \"account\").\n" + " \"embedded\" : {...}, (object, optional) Information about the address embedded in P2SH or P2WSH, if relevant and known. It includes all getaddressinfo output fields for the embedded address, excluding metadata (\"timestamp\", \"hdkeypath\", \"hdseedid\") and relation to the wallet (\"ismine\", \"iswatchonly\", \"account\").\n" " \"iscompressed\" : true|false, (boolean) If the address is compressed\n" " \"label\" : \"label\" (string) The label associated with the address, \"\" is the default account\n" " \"account\" : \"account\" (string) DEPRECATED. This field will be removed in V0.18. To see this deprecated field, start bitcoind with -deprecatedrpc=accounts. The account associated with the address, \"\" is the default account\n" " \"timestamp\" : timestamp, (number, optional) The creation time of the key if available in seconds since epoch (Jan 1 1970 GMT)\n" " \"hdkeypath\" : \"keypath\" (string, optional) The HD keypath if the key is HD and available\n" - " \"hdmasterkeyid\" : \"<hash160>\" (string, optional) The Hash160 of the HD master pubkey\n" + " \"hdseedid\" : \"<hash160>\" (string, optional) The Hash160 of the HD seed\n" + " \"hdmasterkeyid\" : \"<hash160>\" (string, optional) alias for hdseedid maintained for backwards compatibility. Will be removed in V0.18.\n" " \"labels\" (object) Array of labels associated with the address.\n" " [\n" " { (json object of label data)\n" @@ -3967,7 +4151,8 @@ UniValue getaddressinfo(const JSONRPCRequest& request) ret.pushKV("timestamp", meta->nCreateTime); if (!meta->hdKeypath.empty()) { ret.pushKV("hdkeypath", meta->hdKeypath); - ret.pushKV("hdmasterkeyid", meta->hdMasterKeyID.GetHex()); + ret.pushKV("hdseedid", meta->hd_seed_id.GetHex()); + ret.pushKV("hdmasterkeyid", meta->hd_seed_id.GetHex()); } } @@ -3986,7 +4171,9 @@ UniValue getaddressinfo(const JSONRPCRequest& request) static UniValue getaddressesbylabel(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -4029,7 +4216,9 @@ static UniValue getaddressesbylabel(const JSONRPCRequest& request) static UniValue listlabels(const JSONRPCRequest& request) { - CWallet * const pwallet = GetWalletForJSONRPCRequest(request); + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } @@ -4079,6 +4268,77 @@ static UniValue listlabels(const JSONRPCRequest& request) return ret; } +UniValue sethdseed(const JSONRPCRequest& request) +{ + std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest(request); + CWallet* const pwallet = wallet.get(); + + if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { + return NullUniValue; + } + + if (request.fHelp || request.params.size() > 2) { + throw std::runtime_error( + "sethdseed ( \"newkeypool\" \"seed\" )\n" + "\nSet or generate a new HD wallet seed. Non-HD wallets will not be upgraded to being a HD wallet. Wallets that are already\n" + "HD will have a new HD seed set so that new keys added to the keypool will be derived from this new seed.\n" + "\nNote that you will need to MAKE A NEW BACKUP of your wallet after setting the HD wallet seed.\n" + + HelpRequiringPassphrase(pwallet) + + "\nArguments:\n" + "1. \"newkeypool\" (boolean, optional, default=true) Whether to flush old unused addresses, including change addresses, from the keypool and regenerate it.\n" + " If true, the next address from getnewaddress and change address from getrawchangeaddress will be from this new seed.\n" + " If false, addresses (including change addresses if the wallet already had HD Chain Split enabled) from the existing\n" + " keypool will be used until it has been depleted.\n" + "2. \"seed\" (string, optional) The WIF private key to use as the new HD seed; if not provided a random seed will be used.\n" + " The seed value can be retrieved using the dumpwallet command. It is the private key marked hdseed=1\n" + "\nExamples:\n" + + HelpExampleCli("sethdseed", "") + + HelpExampleCli("sethdseed", "false") + + HelpExampleCli("sethdseed", "true \"wifkey\"") + + HelpExampleRpc("sethdseed", "true, \"wifkey\"") + ); + } + + if (IsInitialBlockDownload()) { + throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Cannot set a new HD seed while still in Initial Block Download"); + } + + LOCK2(cs_main, pwallet->cs_wallet); + + // Do not do anything to non-HD wallets + if (!pwallet->IsHDEnabled()) { + throw JSONRPCError(RPC_WALLET_ERROR, "Cannot set a HD seed on a non-HD wallet. Start with -upgradewallet in order to upgrade a non-HD wallet to HD"); + } + + EnsureWalletIsUnlocked(pwallet); + + bool flush_key_pool = true; + if (!request.params[0].isNull()) { + flush_key_pool = request.params[0].get_bool(); + } + + CPubKey master_pub_key; + if (request.params[1].isNull()) { + master_pub_key = pwallet->GenerateNewSeed(); + } else { + CKey key = DecodeSecret(request.params[1].get_str()); + if (!key.IsValid()) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key"); + } + + if (HaveKey(*pwallet, key)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Already have this key (either as an HD seed or as a loose private key)"); + } + + master_pub_key = pwallet->DeriveNewSeed(key); + } + + pwallet->SetHDSeed(master_pub_key); + if (flush_key_pool) pwallet->NewKeyPool(); + + return NullUniValue; +} + extern UniValue abortrescan(const JSONRPCRequest& request); // in rpcdump.cpp extern UniValue dumpprivkey(const JSONRPCRequest& request); // in rpcdump.cpp extern UniValue importprivkey(const JSONRPCRequest& request); @@ -4102,6 +4362,7 @@ static const CRPCCommand commands[] = { "hidden", "addwitnessaddress", &addwitnessaddress, {"address","p2sh"} }, { "wallet", "backupwallet", &backupwallet, {"destination"} }, { "wallet", "bumpfee", &bumpfee, {"txid", "options"} }, + { "wallet", "createwallet", &createwallet, {"wallet_name"} }, { "wallet", "dumpprivkey", &dumpprivkey, {"address"} }, { "wallet", "dumpwallet", &dumpwallet, {"filename"} }, { "wallet", "encryptwallet", &encryptwallet, {"passphrase"} }, @@ -4127,6 +4388,7 @@ static const CRPCCommand commands[] = { "wallet", "listtransactions", &listtransactions, {"account|dummy","count","skip","include_watchonly"} }, { "wallet", "listunspent", &listunspent, {"minconf","maxconf","addresses","include_unsafe","query_options"} }, { "wallet", "listwallets", &listwallets, {} }, + { "wallet", "loadwallet", &loadwallet, {"filename"} }, { "wallet", "lockunspent", &lockunspent, {"unlock","transactions"} }, { "wallet", "sendfrom", &sendfrom, {"fromaccount","toaddress","amount","minconf","comment","comment_to"} }, { "wallet", "sendmany", &sendmany, {"fromaccount|dummy","amounts","minconf","comment","subtractfeefrom","replaceable","conf_target","estimate_mode"} }, @@ -4139,6 +4401,7 @@ static const CRPCCommand commands[] = { "wallet", "walletpassphrase", &walletpassphrase, {"passphrase","timeout"} }, { "wallet", "removeprunedfunds", &removeprunedfunds, {"txid"} }, { "wallet", "rescanblockchain", &rescanblockchain, {"start_height", "stop_height"} }, + { "wallet", "sethdseed", &sethdseed, {"newkeypool","seed"} }, /** Account functions (deprecated) */ { "wallet", "getaccountaddress", &getlabeladdress, {"account"} }, diff --git a/src/wallet/rpcwallet.h b/src/wallet/rpcwallet.h index 84f161abb5..b841f3e424 100644 --- a/src/wallet/rpcwallet.h +++ b/src/wallet/rpcwallet.h @@ -20,7 +20,7 @@ void RegisterWalletRPCCommands(CRPCTable &t); * @param[in] request JSONRPCRequest that wishes to access a wallet * @return nullptr if no wallet should be used, or a pointer to the CWallet */ -CWallet *GetWalletForJSONRPCRequest(const JSONRPCRequest& request); +std::shared_ptr<CWallet> GetWalletForJSONRPCRequest(const JSONRPCRequest& request); std::string HelpRequiringPassphrase(CWallet *); void EnsureWalletIsUnlocked(CWallet *); diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp index ac47d4448a..e90370cf06 100644 --- a/src/wallet/test/coinselector_tests.cpp +++ b/src/wallet/test/coinselector_tests.cpp @@ -536,19 +536,9 @@ BOOST_AUTO_TEST_CASE(SelectCoins_test) std::exponential_distribution<double> distribution (100); FastRandomContext rand; - // Output stuff - CAmount out_value = 0; - CoinSet out_set; - CAmount target = 0; - bool bnb_used; - // Run this test 100 times for (int i = 0; i < 100; ++i) { - // Reset - out_value = 0; - target = 0; - out_set.clear(); empty_wallet(); // Make a wallet with 1000 exponentially distributed random inputs @@ -561,11 +551,14 @@ BOOST_AUTO_TEST_CASE(SelectCoins_test) CFeeRate rate(rand.randrange(300) + 100); // Generate a random target value between 1000 and wallet balance - target = rand.randrange(balance - 1000) + 1000; + CAmount target = rand.randrange(balance - 1000) + 1000; // Perform selection CoinSelectionParams coin_selection_params_knapsack(false, 34, 148, CFeeRate(0), 0); CoinSelectionParams coin_selection_params_bnb(true, 34, 148, CFeeRate(0), 0); + CoinSet out_set; + CAmount out_value = 0; + bool bnb_used = false; BOOST_CHECK(testWallet.SelectCoinsMinConf(target, filter_standard, vCoins, out_set, out_value, coin_selection_params_bnb, bnb_used) || testWallet.SelectCoinsMinConf(target, filter_standard, vCoins, out_set, out_value, coin_selection_params_knapsack, bnb_used)); BOOST_CHECK_GE(out_value, target); diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index 99c963a348..03754154fc 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -73,8 +73,8 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) // before the missing block, and success for a key whose creation time is // after. { - CWallet wallet("dummy", WalletDatabase::CreateDummy()); - AddWallet(&wallet); + std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>("dummy", WalletDatabase::CreateDummy()); + AddWallet(wallet); UniValue keys; keys.setArray(); UniValue key; @@ -105,7 +105,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) "downloading and rescanning the relevant blocks (see -reindex and -rescan " "options).\"}},{\"success\":true}]", 0, oldTip->GetBlockTimeMax(), TIMESTAMP_WINDOW)); - RemoveWallet(&wallet); + RemoveWallet(wallet); } } @@ -132,36 +132,36 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup) // Import key into wallet and call dumpwallet to create backup file. { - CWallet wallet("dummy", WalletDatabase::CreateDummy()); - LOCK(wallet.cs_wallet); - wallet.mapKeyMetadata[coinbaseKey.GetPubKey().GetID()].nCreateTime = KEY_TIME; - wallet.AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()); + std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>("dummy", WalletDatabase::CreateDummy()); + LOCK(wallet->cs_wallet); + wallet->mapKeyMetadata[coinbaseKey.GetPubKey().GetID()].nCreateTime = KEY_TIME; + wallet->AddKeyPubKey(coinbaseKey, coinbaseKey.GetPubKey()); JSONRPCRequest request; request.params.setArray(); request.params.push_back((pathTemp / "wallet.backup").string()); - AddWallet(&wallet); + AddWallet(wallet); ::dumpwallet(request); - RemoveWallet(&wallet); + RemoveWallet(wallet); } // Call importwallet RPC and verify all blocks with timestamps >= BLOCK_TIME // were scanned, and no prior blocks were scanned. { - CWallet wallet("dummy", WalletDatabase::CreateDummy()); + std::shared_ptr<CWallet> wallet = std::make_shared<CWallet>("dummy", WalletDatabase::CreateDummy()); JSONRPCRequest request; request.params.setArray(); request.params.push_back((pathTemp / "wallet.backup").string()); - AddWallet(&wallet); + AddWallet(wallet); ::importwallet(request); - RemoveWallet(&wallet); + RemoveWallet(wallet); - LOCK(wallet.cs_wallet); - BOOST_CHECK_EQUAL(wallet.mapWallet.size(), 3U); + LOCK(wallet->cs_wallet); + BOOST_CHECK_EQUAL(wallet->mapWallet.size(), 3U); BOOST_CHECK_EQUAL(m_coinbase_txns.size(), 103U); for (size_t i = 0; i < m_coinbase_txns.size(); ++i) { - bool found = wallet.GetWalletTx(m_coinbase_txns[i]->GetHash()); + bool found = wallet->GetWalletTx(m_coinbase_txns[i]->GetHash()); bool expected = i >= 100; BOOST_CHECK_EQUAL(found, expected); } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 96242c6b13..3987e8e70a 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -23,11 +23,11 @@ #include <primitives/block.h> #include <primitives/transaction.h> #include <script/script.h> -#include <scheduler.h> #include <timedata.h> #include <txmempool.h> #include <utilmoneystr.h> #include <wallet/fees.h> +#include <wallet/walletutil.h> #include <algorithm> #include <assert.h> @@ -36,23 +36,23 @@ #include <boost/algorithm/string/replace.hpp> static CCriticalSection cs_wallets; -static std::vector<CWallet*> vpwallets GUARDED_BY(cs_wallets); +static std::vector<std::shared_ptr<CWallet>> vpwallets GUARDED_BY(cs_wallets); -bool AddWallet(CWallet* wallet) +bool AddWallet(const std::shared_ptr<CWallet>& wallet) { LOCK(cs_wallets); assert(wallet); - std::vector<CWallet*>::const_iterator i = std::find(vpwallets.begin(), vpwallets.end(), wallet); + std::vector<std::shared_ptr<CWallet>>::const_iterator i = std::find(vpwallets.begin(), vpwallets.end(), wallet); if (i != vpwallets.end()) return false; vpwallets.push_back(wallet); return true; } -bool RemoveWallet(CWallet* wallet) +bool RemoveWallet(const std::shared_ptr<CWallet>& wallet) { LOCK(cs_wallets); assert(wallet); - std::vector<CWallet*>::iterator i = std::find(vpwallets.begin(), vpwallets.end(), wallet); + std::vector<std::shared_ptr<CWallet>>::iterator i = std::find(vpwallets.begin(), vpwallets.end(), wallet); if (i == vpwallets.end()) return false; vpwallets.erase(i); return true; @@ -64,16 +64,16 @@ bool HasWallets() return !vpwallets.empty(); } -std::vector<CWallet*> GetWallets() +std::vector<std::shared_ptr<CWallet>> GetWallets() { LOCK(cs_wallets); return vpwallets; } -CWallet* GetWallet(const std::string& name) +std::shared_ptr<CWallet> GetWallet(const std::string& name) { LOCK(cs_wallets); - for (CWallet* wallet : vpwallets) { + for (const std::shared_ptr<CWallet>& wallet : vpwallets) { if (wallet->GetName() == name) return wallet; } return nullptr; @@ -191,17 +191,17 @@ CPubKey CWallet::GenerateNewKey(WalletBatch &batch, bool internal) void CWallet::DeriveNewChildKey(WalletBatch &batch, CKeyMetadata& metadata, CKey& secret, bool internal) { // for now we use a fixed keypath scheme of m/0'/0'/k - CKey key; //master key seed (256bit) + CKey seed; //seed (256bit) CExtKey masterKey; //hd master key CExtKey accountKey; //key at m/0' CExtKey chainChildKey; //key at m/0'/0' (external) or m/0'/1' (internal) CExtKey childKey; //key at m/0'/0'/<n>' - // try to get the master key - if (!GetKey(hdChain.masterKeyID, key)) - throw std::runtime_error(std::string(__func__) + ": Master key not found"); + // try to get the seed + if (!GetKey(hdChain.seed_id, seed)) + throw std::runtime_error(std::string(__func__) + ": seed not found"); - masterKey.SetMaster(key.begin(), key.size()); + masterKey.SetSeed(seed.begin(), seed.size()); // derive m/0' // use hardened derivation (child keys >= 0x80000000 are hardened after bip32) @@ -228,7 +228,7 @@ void CWallet::DeriveNewChildKey(WalletBatch &batch, CKeyMetadata& metadata, CKey } } while (HaveKey(childKey.key.GetPubKey().GetID())); secret = childKey.key; - metadata.hdMasterKeyID = hdChain.masterKeyID; + metadata.hd_seed_id = hdChain.seed_id; // update the chain model in the database if (!batch.WriteHDChain(hdChain)) throw std::runtime_error(std::string(__func__) + ": Writing HD chain model failed"); @@ -549,7 +549,9 @@ void CWallet::SyncMetaData(std::pair<TxSpends::iterator, TxSpends::iterator> ran } } - assert(copyFrom); + if (!copyFrom) { + return; + } // Now copy data from copyFrom to rest: for (TxSpends::iterator it = range.first; it != range.second; ++it) @@ -687,9 +689,9 @@ bool CWallet::EncryptWallet(const SecureString& strWalletPassphrase) Lock(); Unlock(strWalletPassphrase); - // if we are using HD, replace the HD master key (seed) with a new one + // if we are using HD, replace the HD seed with a new one if (IsHDEnabled()) { - if (!SetHDMasterKey(GenerateNewHDMasterKey())) { + if (!SetHDSeed(GenerateNewSeed())) { return false; } } @@ -1448,37 +1450,41 @@ CAmount CWallet::GetChange(const CTransaction& tx) const return nChange; } -CPubKey CWallet::GenerateNewHDMasterKey() +CPubKey CWallet::GenerateNewSeed() { CKey key; key.MakeNewKey(true); + return DeriveNewSeed(key); +} +CPubKey CWallet::DeriveNewSeed(const CKey& key) +{ int64_t nCreationTime = GetTime(); CKeyMetadata metadata(nCreationTime); - // calculate the pubkey - CPubKey pubkey = key.GetPubKey(); - assert(key.VerifyPubKey(pubkey)); + // calculate the seed + CPubKey seed = key.GetPubKey(); + assert(key.VerifyPubKey(seed)); - // set the hd keypath to "m" -> Master, refers the masterkeyid to itself - metadata.hdKeypath = "m"; - metadata.hdMasterKeyID = pubkey.GetID(); + // set the hd keypath to "s" -> Seed, refers the seed to itself + metadata.hdKeypath = "s"; + metadata.hd_seed_id = seed.GetID(); { LOCK(cs_wallet); // mem store the metadata - mapKeyMetadata[pubkey.GetID()] = metadata; + mapKeyMetadata[seed.GetID()] = metadata; // write the key&metadata to the database - if (!AddKeyPubKey(key, pubkey)) + if (!AddKeyPubKey(key, seed)) throw std::runtime_error(std::string(__func__) + ": AddKeyPubKey failed"); } - return pubkey; + return seed; } -bool CWallet::SetHDMasterKey(const CPubKey& pubkey) +bool CWallet::SetHDSeed(const CPubKey& seed) { LOCK(cs_wallet); // store the keyid (hash160) together with @@ -1486,7 +1492,7 @@ bool CWallet::SetHDMasterKey(const CPubKey& pubkey) // as a hdchain object CHDChain newHdChain; newHdChain.nVersion = CanSupportFeature(FEATURE_HD_SPLIT) ? CHDChain::VERSION_HD_CHAIN_SPLIT : CHDChain::VERSION_HD_BASE; - newHdChain.masterKeyID = pubkey.GetID(); + newHdChain.seed_id = seed.GetID(); SetHDChain(newHdChain, false); return true; @@ -1504,7 +1510,7 @@ bool CWallet::SetHDChain(const CHDChain& chain, bool memonly) bool CWallet::IsHDEnabled() const { - return !hdChain.masterKeyID.IsNull(); + return !hdChain.seed_id.IsNull(); } int64_t CWalletTx::GetTxTime() const @@ -2069,8 +2075,8 @@ bool CWalletTx::IsTrusted() const bool CWalletTx::IsEquivalentTo(const CWalletTx& _tx) const { - CMutableTransaction tx1 = *this->tx; - CMutableTransaction tx2 = *_tx.tx; + CMutableTransaction tx1 {*this->tx}; + CMutableTransaction tx2 {*_tx.tx}; for (auto& txin : tx1.vin) txin.scriptSig = CScript(); for (auto& txin : tx2.vin) txin.scriptSig = CScript(); return CTransaction(tx1) == CTransaction(tx2); @@ -2370,10 +2376,10 @@ void CWallet::AvailableCoins(std::vector<COutput> &vCoins, bool fOnlySafe, const continue; } - bool fSpendableIn = ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || (coinControl && coinControl->fAllowWatchOnly && (mine & ISMINE_WATCH_SOLVABLE) != ISMINE_NO); - bool fSolvableIn = (mine & (ISMINE_SPENDABLE | ISMINE_WATCH_SOLVABLE)) != ISMINE_NO; + bool solvable = IsSolvable(*this, pcoin->tx->vout[i].scriptPubKey); + bool spendable = ((mine & ISMINE_SPENDABLE) != ISMINE_NO) || (((mine & ISMINE_WATCH_ONLY) != ISMINE_NO) && (coinControl && coinControl->fAllowWatchOnly && solvable)); - vCoins.push_back(COutput(pcoin, i, nDepth, fSpendableIn, fSolvableIn, safeTx)); + vCoins.push_back(COutput(pcoin, i, nDepth, spendable, solvable, safeTx)); // Checks the sum amount of all UTXO's. if (nMinimumSumAmount != MAX_MONEY) { @@ -2602,7 +2608,6 @@ bool CWallet::SignTransaction(CMutableTransaction &tx) AssertLockHeld(cs_wallet); // mapWallet // sign the new tx - CTransaction txNewConst(tx); int nIn = 0; for (const auto& input : tx.vin) { std::map<uint256, CWalletTx>::const_iterator mi = mapWallet.find(input.prevout.hash); @@ -2612,7 +2617,7 @@ bool CWallet::SignTransaction(CMutableTransaction &tx) const CScript& scriptPubKey = mi->second.tx->vout[input.prevout.n].scriptPubKey; const CAmount& amount = mi->second.tx->vout[input.prevout.n].nValue; SignatureData sigdata; - if (!ProduceSignature(*this, TransactionSignatureCreator(&txNewConst, nIn, amount, SIGHASH_ALL), scriptPubKey, sigdata)) { + if (!ProduceSignature(*this, MutableTransactionSignatureCreator(&tx, nIn, amount, SIGHASH_ALL), scriptPubKey, sigdata)) { return false; } UpdateTransaction(tx, nIn, sigdata); @@ -3034,14 +3039,13 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransac if (sign) { - CTransaction txNewConst(txNew); int nIn = 0; for (const auto& coin : selected_coins) { const CScript& scriptPubKey = coin.txout.scriptPubKey; SignatureData sigdata; - if (!ProduceSignature(*this, TransactionSignatureCreator(&txNewConst, nIn, coin.txout.nValue, SIGHASH_ALL), scriptPubKey, sigdata)) + if (!ProduceSignature(*this, MutableTransactionSignatureCreator(&txNew, nIn, coin.txout.nValue, SIGHASH_ALL), scriptPubKey, sigdata)) { strFailReason = _("Signing transaction failed"); return false; @@ -3195,8 +3199,6 @@ DBErrors CWallet::LoadWallet(bool& fFirstRunRet) if (nLoadWalletRet != DBErrors::LOAD_OK) return nLoadWalletRet; - uiInterface.LoadWallet(this); - return DBErrors::LOAD_OK; } @@ -3326,6 +3328,11 @@ bool CWallet::NewKeyPool() } setExternalKeyPool.clear(); + for (int64_t nIndex : set_pre_split_keypool) { + batch.ErasePool(nIndex); + } + set_pre_split_keypool.clear(); + m_pool_key_to_index.clear(); if (!TopUpKeyPool()) { @@ -3339,13 +3346,15 @@ bool CWallet::NewKeyPool() size_t CWallet::KeypoolCountExternalKeys() { AssertLockHeld(cs_wallet); // setExternalKeyPool - return setExternalKeyPool.size(); + return setExternalKeyPool.size() + set_pre_split_keypool.size(); } void CWallet::LoadKeyPool(int64_t nIndex, const CKeyPool &keypool) { AssertLockHeld(cs_wallet); - if (keypool.fInternal) { + if (keypool.m_pre_split) { + set_pre_split_keypool.insert(nIndex); + } else if (keypool.fInternal) { setInternalKeyPool.insert(nIndex); } else { setExternalKeyPool.insert(nIndex); @@ -3410,13 +3419,13 @@ bool CWallet::TopUpKeyPool(unsigned int kpSize) m_pool_key_to_index[pubkey.GetID()] = index; } if (missingInternal + missingExternal > 0) { - LogPrintf("keypool added %d keys (%d internal), size=%u (%u internal)\n", missingInternal + missingExternal, missingInternal, setInternalKeyPool.size() + setExternalKeyPool.size(), setInternalKeyPool.size()); + LogPrintf("keypool added %d keys (%d internal), size=%u (%u internal)\n", missingInternal + missingExternal, missingInternal, setInternalKeyPool.size() + setExternalKeyPool.size() + set_pre_split_keypool.size(), setInternalKeyPool.size()); } } return true; } -void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRequestedInternal) +bool CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRequestedInternal) { nIndex = -1; keypool.vchPubKey = CPubKey(); @@ -3427,11 +3436,13 @@ void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRe TopUpKeyPool(); bool fReturningInternal = IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT) && fRequestedInternal; - std::set<int64_t>& setKeyPool = fReturningInternal ? setInternalKeyPool : setExternalKeyPool; + bool use_split_keypool = set_pre_split_keypool.empty(); + std::set<int64_t>& setKeyPool = use_split_keypool ? (fReturningInternal ? setInternalKeyPool : setExternalKeyPool) : set_pre_split_keypool; // Get the oldest key - if(setKeyPool.empty()) - return; + if (setKeyPool.empty()) { + return false; + } WalletBatch batch(*database); @@ -3444,14 +3455,18 @@ void CWallet::ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRe if (!HaveKey(keypool.vchPubKey.GetID())) { throw std::runtime_error(std::string(__func__) + ": unknown key in key pool"); } - if (keypool.fInternal != fReturningInternal) { + // If the key was pre-split keypool, we don't care about what type it is + if (use_split_keypool && keypool.fInternal != fReturningInternal) { throw std::runtime_error(std::string(__func__) + ": keypool entry misclassified"); } + if (!keypool.vchPubKey.IsValid()) { + throw std::runtime_error(std::string(__func__) + ": keypool entry invalid"); + } - assert(keypool.vchPubKey.IsValid()); m_pool_key_to_index.erase(keypool.vchPubKey.GetID()); LogPrintf("keypool reserve %d\n", nIndex); } + return true; } void CWallet::KeepKey(int64_t nIndex) @@ -3469,6 +3484,8 @@ void CWallet::ReturnKey(int64_t nIndex, bool fInternal, const CPubKey& pubkey) LOCK(cs_wallet); if (fInternal) { setInternalKeyPool.insert(nIndex); + } else if (!set_pre_split_keypool.empty()) { + set_pre_split_keypool.insert(nIndex); } else { setExternalKeyPool.insert(nIndex); } @@ -3482,10 +3499,8 @@ bool CWallet::GetKeyFromPool(CPubKey& result, bool internal) CKeyPool keypool; { LOCK(cs_wallet); - int64_t nIndex = 0; - ReserveKeyFromKeyPool(nIndex, keypool, internal); - if (nIndex == -1) - { + int64_t nIndex; + if (!ReserveKeyFromKeyPool(nIndex, keypool, internal)) { if (IsLocked()) return false; WalletBatch batch(*database); result = GenerateNewKey(batch, internal); @@ -3521,6 +3536,9 @@ int64_t CWallet::GetOldestKeyPoolTime() int64_t oldestKey = GetOldestKeyTimeInPool(setExternalKeyPool, batch); if (IsHDEnabled() && CanSupportFeature(FEATURE_HD_SPLIT)) { oldestKey = std::max(GetOldestKeyTimeInPool(setInternalKeyPool, batch), oldestKey); + if (!set_pre_split_keypool.empty()) { + oldestKey = std::max(GetOldestKeyTimeInPool(set_pre_split_keypool, batch), oldestKey); + } } return oldestKey; @@ -3684,12 +3702,10 @@ bool CReserveKey::GetReservedKey(CPubKey& pubkey, bool internal) if (nIndex == -1) { CKeyPool keypool; - pwallet->ReserveKeyFromKeyPool(nIndex, keypool, internal); - if (nIndex != -1) - vchPubKey = keypool.vchPubKey; - else { + if (!pwallet->ReserveKeyFromKeyPool(nIndex, keypool, internal)) { return false; } + vchPubKey = keypool.vchPubKey; fInternal = keypool.fInternal; } assert(vchPubKey.IsValid()); @@ -3718,8 +3734,8 @@ void CWallet::MarkReserveKeysAsUsed(int64_t keypool_id) { AssertLockHeld(cs_wallet); bool internal = setInternalKeyPool.count(keypool_id); - if (!internal) assert(setExternalKeyPool.count(keypool_id)); - std::set<int64_t> *setKeyPool = internal ? &setInternalKeyPool : &setExternalKeyPool; + if (!internal) assert(setExternalKeyPool.count(keypool_id) || set_pre_split_keypool.count(keypool_id)); + std::set<int64_t> *setKeyPool = internal ? &setInternalKeyPool : (set_pre_split_keypool.empty() ? &setExternalKeyPool : &set_pre_split_keypool); auto it = setKeyPool->begin(); WalletBatch batch(*database); @@ -3955,7 +3971,71 @@ std::vector<std::string> CWallet::GetDestValues(const std::string& prefix) const return values; } -CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path& path) +void CWallet::MarkPreSplitKeys() +{ + WalletBatch batch(*database); + for (auto it = setExternalKeyPool.begin(); it != setExternalKeyPool.end();) { + int64_t index = *it; + CKeyPool keypool; + if (!batch.ReadPool(index, keypool)) { + throw std::runtime_error(std::string(__func__) + ": read keypool entry failed"); + } + keypool.m_pre_split = true; + if (!batch.WritePool(index, keypool)) { + throw std::runtime_error(std::string(__func__) + ": writing modified keypool entry failed"); + } + set_pre_split_keypool.insert(index); + it = setExternalKeyPool.erase(it); + } +} + +bool CWallet::Verify(std::string wallet_file, bool salvage_wallet, std::string& error_string, std::string& warning_string) +{ + // Do some checking on wallet path. It should be either a: + // + // 1. Path where a directory can be created. + // 2. Path to an existing directory. + // 3. Path to a symlink to a directory. + // 4. For backwards compatibility, the name of a data file in -walletdir. + LOCK(cs_wallets); + fs::path wallet_path = fs::absolute(wallet_file, GetWalletDir()); + fs::file_type path_type = fs::symlink_status(wallet_path).type(); + if (!(path_type == fs::file_not_found || path_type == fs::directory_file || + (path_type == fs::symlink_file && fs::is_directory(wallet_path)) || + (path_type == fs::regular_file && fs::path(wallet_file).filename() == wallet_file))) { + error_string = strprintf( + "Invalid -wallet path '%s'. -wallet path should point to a directory where wallet.dat and " + "database/log.?????????? files can be stored, a location where such a directory could be created, " + "or (for backwards compatibility) the name of an existing data file in -walletdir (%s)", + wallet_file, GetWalletDir()); + return false; + } + + // Make sure that the wallet path doesn't clash with an existing wallet path + for (auto wallet : GetWallets()) { + if (fs::absolute(wallet->GetName(), GetWalletDir()) == wallet_path) { + error_string = strprintf("Error loading wallet %s. Duplicate -wallet filename specified.", wallet_file); + return false; + } + } + + if (!WalletBatch::VerifyEnvironment(wallet_path, error_string)) { + return false; + } + + if (salvage_wallet) { + // Recover readable keypairs: + CWallet dummyWallet("dummy", WalletDatabase::CreateDummy()); + std::string backup_filename; + if (!WalletBatch::Recover(wallet_path, (void *)&dummyWallet, WalletBatch::RecoverKeysOnlyFilter, backup_filename)) { + return false; + } + } + + return WalletBatch::VerifyDatabaseFile(wallet_path, warning_string, error_string); +} + +std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(const std::string& name, const fs::path& path) { const std::string& walletFile = name; @@ -3977,7 +4057,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path& int64_t nStart = GetTimeMillis(); bool fFirstRun = true; - CWallet *walletInstance = new CWallet(name, WalletDatabase::Create(path)); + std::shared_ptr<CWallet> walletInstance = std::make_shared<CWallet>(name, WalletDatabase::Create(path)); DBErrors nLoadWalletRet = walletInstance->LoadWallet(fFirstRun); if (nLoadWalletRet != DBErrors::LOAD_OK) { @@ -4006,6 +4086,9 @@ CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path& } } + uiInterface.LoadWallet(walletInstance); + + int prev_version = walletInstance->nWalletVersion; if (gArgs.GetBoolArg("-upgradewallet", fFirstRun)) { int nMaxVersion = gArgs.GetArg("-upgradewallet", 0); @@ -4025,6 +4108,49 @@ CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path& walletInstance->SetMaxVersion(nMaxVersion); } + // Upgrade to HD if explicit upgrade + if (gArgs.GetBoolArg("-upgradewallet", false)) { + LOCK(walletInstance->cs_wallet); + + // Do not upgrade versions to any version between HD_SPLIT and FEATURE_PRE_SPLIT_KEYPOOL unless already supporting HD_SPLIT + int max_version = walletInstance->nWalletVersion; + if (!walletInstance->CanSupportFeature(FEATURE_HD_SPLIT) && max_version >=FEATURE_HD_SPLIT && max_version < FEATURE_PRE_SPLIT_KEYPOOL) { + InitError(_("Cannot upgrade a non HD split wallet without upgrading to support pre split keypool. Please use -upgradewallet=169900 or -upgradewallet with no version specified.")); + return nullptr; + } + + bool hd_upgrade = false; + bool split_upgrade = false; + if (walletInstance->CanSupportFeature(FEATURE_HD) && !walletInstance->IsHDEnabled()) { + LogPrintf("Upgrading wallet to HD\n"); + walletInstance->SetMinVersion(FEATURE_HD); + + // generate a new master key + CPubKey masterPubKey = walletInstance->GenerateNewSeed(); + if (!walletInstance->SetHDSeed(masterPubKey)) { + throw std::runtime_error(std::string(__func__) + ": Storing master key failed"); + } + hd_upgrade = true; + } + // Upgrade to HD chain split if necessary + if (walletInstance->CanSupportFeature(FEATURE_HD_SPLIT)) { + LogPrintf("Upgrading wallet to use HD chain split\n"); + walletInstance->SetMinVersion(FEATURE_PRE_SPLIT_KEYPOOL); + split_upgrade = FEATURE_HD_SPLIT > prev_version; + } + // Mark all keys currently in the keypool as pre-split + if (split_upgrade) { + walletInstance->MarkPreSplitKeys(); + } + // Regenerate the keypool if upgraded to HD + if (hd_upgrade) { + if (!walletInstance->TopUpKeyPool()) { + InitError(_("Unable to generate keys") += "\n"); + return nullptr; + } + } + } + if (fFirstRun) { // ensure this wallet.dat can only be opened by clients supporting HD with chain split and expects no default key @@ -4032,12 +4158,12 @@ CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path& InitError(strprintf(_("Error creating %s: You can't create non-HD wallets with this version."), walletFile)); return nullptr; } - walletInstance->SetMinVersion(FEATURE_NO_DEFAULT_KEY); + walletInstance->SetMinVersion(FEATURE_LATEST); - // generate a new master key - CPubKey masterPubKey = walletInstance->GenerateNewHDMasterKey(); - if (!walletInstance->SetHDMasterKey(masterPubKey)) - throw std::runtime_error(std::string(__func__) + ": Storing master key failed"); + // generate a new seed + CPubKey seed = walletInstance->GenerateNewSeed(); + if (!walletInstance->SetHDSeed(seed)) + throw std::runtime_error(std::string(__func__) + ": Storing HD seed failed"); // Top up the keypool if (!walletInstance->TopUpKeyPool()) { @@ -4145,7 +4271,6 @@ CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path& } walletInstance->m_last_block_processed = chainActive.Tip(); - RegisterValidationInterface(walletInstance); if (chainActive.Tip() && chainActive.Tip() != pindexRescan) { @@ -4175,7 +4300,7 @@ CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path& nStart = GetTimeMillis(); { - WalletRescanReserver reserver(walletInstance); + WalletRescanReserver reserver(walletInstance.get()); if (!reserver.reserve()) { InitError(_("Failed to rescan the wallet during initialization")); return nullptr; @@ -4211,6 +4336,10 @@ CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path& } } } + + // Register with the validation interface. It's ok to do this after rescan since we're still holding cs_main. + RegisterValidationInterface(walletInstance.get()); + walletInstance->SetBroadcastTransactions(gArgs.GetBoolArg("-walletbroadcast", DEFAULT_WALLETBROADCAST)); { @@ -4223,18 +4352,11 @@ CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path& return walletInstance; } -std::atomic<bool> CWallet::fFlushScheduled(false); - -void CWallet::postInitProcess(CScheduler& scheduler) +void CWallet::postInitProcess() { // Add wallet transactions that aren't already in a block to mempool // Do this here as mempool requires genesis block to be loaded ReacceptWalletTransactions(); - - // Run a thread to flush wallet periodically - if (!CWallet::fFlushScheduled.exchange(true)) { - scheduler.scheduleEvery(MaybeCompactWalletDB, 500); - } } bool CWallet::BackupWallet(const std::string& strDest) @@ -4246,6 +4368,7 @@ CKeyPool::CKeyPool() { nTime = GetTime(); fInternal = false; + m_pre_split = false; } CKeyPool::CKeyPool(const CPubKey& vchPubKeyIn, bool internalIn) @@ -4253,6 +4376,7 @@ CKeyPool::CKeyPool(const CPubKey& vchPubKeyIn, bool internalIn) nTime = GetTime(); vchPubKey = vchPubKeyIn; fInternal = internalIn; + m_pre_split = false; } CWalletKey::CWalletKey(int64_t nExpires) diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 3208a20f0f..f1761b0fcf 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -32,11 +32,11 @@ #include <utility> #include <vector> -bool AddWallet(CWallet* wallet); -bool RemoveWallet(CWallet* wallet); +bool AddWallet(const std::shared_ptr<CWallet>& wallet); +bool RemoveWallet(const std::shared_ptr<CWallet>& wallet); bool HasWallets(); -std::vector<CWallet*> GetWallets(); -CWallet* GetWallet(const std::string& name); +std::vector<std::shared_ptr<CWallet>> GetWallets(); +std::shared_ptr<CWallet> GetWallet(const std::string& name); //! Default for -keypool static const unsigned int DEFAULT_KEYPOOL_SIZE = 1000; @@ -68,7 +68,6 @@ class CCoinControl; class COutput; class CReserveKey; class CScript; -class CScheduler; class CTxMemPool; class CBlockPolicyEstimator; class CWalletTx; @@ -89,7 +88,9 @@ enum WalletFeature FEATURE_NO_DEFAULT_KEY = 159900, // Wallet without a default key written - FEATURE_LATEST = FEATURE_COMPRPUBKEY // HD is optional, use FEATURE_COMPRPUBKEY as latest version + FEATURE_PRE_SPLIT_KEYPOOL = 169900, // Upgraded to HD SPLIT and can have a pre-split keypool + + FEATURE_LATEST = FEATURE_PRE_SPLIT_KEYPOOL }; enum class OutputType { @@ -119,6 +120,7 @@ public: int64_t nTime; CPubKey vchPubKey; bool fInternal; // for change outputs + bool m_pre_split; // For keys generated before keypool split upgrade CKeyPool(); CKeyPool(const CPubKey& vchPubKeyIn, bool internalIn); @@ -141,9 +143,18 @@ public: (this will be the case for any wallet before the HD chain split version) */ fInternal = false; } + try { + READWRITE(m_pre_split); + } + catch (std::ios_base::failure&) { + /* flag as postsplit address if we can't read the m_pre_split boolean + (this will be the case for any wallet that upgrades to HD chain split)*/ + m_pre_split = false; + } } else { READWRITE(fInternal); + READWRITE(m_pre_split); } } }; @@ -663,7 +674,6 @@ class WalletRescanReserver; //forward declarations for ScanForWalletTransactions class CWallet final : public CCryptoKeyStore, public CValidationInterface { private: - static std::atomic<bool> fFlushScheduled; std::atomic<bool> fAbortRescan{false}; std::atomic<bool> fScanningWallet{false}; // controlled by WalletRescanReserver std::mutex mutexScanning; @@ -698,16 +708,17 @@ private: /* Used by TransactionAddedToMemorypool/BlockConnected/Disconnected. * Should be called with pindexBlock and posInBlock if this is for a transaction that is included in a block. */ - void SyncTransaction(const CTransactionRef& tx, const CBlockIndex *pindex = nullptr, int posInBlock = 0); + void SyncTransaction(const CTransactionRef& tx, const CBlockIndex *pindex = nullptr, int posInBlock = 0) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); /* the HD chain data model (external chain counters) */ CHDChain hdChain; /* HD derive new child key (on internal or external chain) */ - void DeriveNewChildKey(WalletBatch &batch, CKeyMetadata& metadata, CKey& secret, bool internal = false); + void DeriveNewChildKey(WalletBatch &batch, CKeyMetadata& metadata, CKey& secret, bool internal = false) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); std::set<int64_t> setInternalKeyPool; std::set<int64_t> setExternalKeyPool; + std::set<int64_t> set_pre_split_keypool; int64_t m_max_keypool_index = 0; std::map<CKeyID, int64_t> m_pool_key_to_index; @@ -722,7 +733,7 @@ private: * of the other AddWatchOnly which accepts a timestamp and sets * nTimeFirstKey more intelligently for more efficient rescans. */ - bool AddWatchOnly(const CScript& dest) override; + bool AddWatchOnly(const CScript& dest) override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); /** * Wallet filename from wallet=<path> command line or config option. @@ -773,7 +784,8 @@ public: */ const std::string& GetName() const { return m_name; } - void LoadKeyPool(int64_t nIndex, const CKeyPool &keypool); + void LoadKeyPool(int64_t nIndex, const CKeyPool &keypool) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + void MarkPreSplitKeys(); // Map from Key ID to key metadata. std::map<CKeyID, CKeyMetadata> mapKeyMetadata; @@ -814,12 +826,12 @@ public: const CWalletTx* GetWalletTx(const uint256& hash) const; //! check whether we are allowed to upgrade (or already support) to the named feature - bool CanSupportFeature(enum WalletFeature wf) const { AssertLockHeld(cs_wallet); return nWalletMaxVersion >= wf; } + bool CanSupportFeature(enum WalletFeature wf) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(cs_wallet); return nWalletMaxVersion >= wf; } /** * populate vCoins with vector of available COutputs. */ - void AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe=true, const CCoinControl *coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0, const int nMinDepth = 0, const int nMaxDepth = 9999999) const; + void AvailableCoins(std::vector<COutput>& vCoins, bool fOnlySafe=true, const CCoinControl *coinControl = nullptr, const CAmount& nMinimumAmount = 1, const CAmount& nMaximumAmount = MAX_MONEY, const CAmount& nMinimumSumAmount = MAX_MONEY, const uint64_t nMaximumCount = 0, const int nMinDepth = 0, const int nMaxDepth = 9999999) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); /** * Return list of available coins and locked coins grouped by non-change output address. @@ -842,11 +854,11 @@ public: bool IsSpent(const uint256& hash, unsigned int n) const; - bool IsLockedCoin(uint256 hash, unsigned int n) const; - void LockCoin(const COutPoint& output); - void UnlockCoin(const COutPoint& output); - void UnlockAllCoins(); - void ListLockedCoins(std::vector<COutPoint>& vOutpts) const; + bool IsLockedCoin(uint256 hash, unsigned int n) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + void LockCoin(const COutPoint& output) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + void UnlockCoin(const COutPoint& output) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + void UnlockAllCoins() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + void ListLockedCoins(std::vector<COutPoint>& vOutpts) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); /* * Rescan abort properties @@ -859,18 +871,18 @@ public: * keystore implementation * Generate a new key */ - CPubKey GenerateNewKey(WalletBatch& batch, bool internal = false); + CPubKey GenerateNewKey(WalletBatch& batch, bool internal = false) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); //! Adds a key to the store, and saves it to disk. - bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey) override; - bool AddKeyPubKeyWithDB(WalletBatch &batch,const CKey& key, const CPubKey &pubkey); + bool AddKeyPubKey(const CKey& key, const CPubKey &pubkey) override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + bool AddKeyPubKeyWithDB(WalletBatch &batch,const CKey& key, const CPubKey &pubkey) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); //! Adds a key to the store, without saving it to disk (used by LoadWallet) bool LoadKey(const CKey& key, const CPubKey &pubkey) { return CCryptoKeyStore::AddKeyPubKey(key, pubkey); } //! Load metadata (used by LoadWallet) - bool LoadKeyMetadata(const CKeyID& keyID, const CKeyMetadata &metadata); - bool LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata &metadata); + bool LoadKeyMetadata(const CKeyID& keyID, const CKeyMetadata &metadata) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + bool LoadScriptMetadata(const CScriptID& script_id, const CKeyMetadata &metadata) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); - bool LoadMinVersion(int nVersion) { AssertLockHeld(cs_wallet); nWalletVersion = nVersion; nWalletMaxVersion = std::max(nWalletMaxVersion, nVersion); return true; } - void UpdateTimeFirstKey(int64_t nCreateTime); + bool LoadMinVersion(int nVersion) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(cs_wallet); nWalletVersion = nVersion; nWalletMaxVersion = std::max(nWalletMaxVersion, nVersion); return true; } + void UpdateTimeFirstKey(int64_t nCreateTime) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); //! Adds an encrypted key to the store, and saves it to disk. bool AddCryptedKey(const CPubKey &vchPubKey, const std::vector<unsigned char> &vchCryptedSecret) override; @@ -891,8 +903,8 @@ public: std::vector<std::string> GetDestValues(const std::string& prefix) const; //! Adds a watch-only address to the store, and saves it to disk. - bool AddWatchOnly(const CScript& dest, int64_t nCreateTime); - bool RemoveWatchOnly(const CScript &dest) override; + bool AddWatchOnly(const CScript& dest, int64_t nCreateTime) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + bool RemoveWatchOnly(const CScript &dest) override EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); //! Adds a watch-only address to the store, without saving it to disk (used by LoadWallet) bool LoadWatchOnly(const CScript &dest); @@ -903,16 +915,16 @@ public: bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase); bool EncryptWallet(const SecureString& strWalletPassphrase); - void GetKeyBirthTimes(std::map<CTxDestination, int64_t> &mapKeyBirth) const; + void GetKeyBirthTimes(std::map<CTxDestination, int64_t> &mapKeyBirth) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); unsigned int ComputeTimeSmart(const CWalletTx& wtx) const; /** * Increment the next transaction order id * @return next transaction order id */ - int64_t IncOrderPosNext(WalletBatch *batch = nullptr); + int64_t IncOrderPosNext(WalletBatch *batch = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); DBErrors ReorderTransactions(); - bool AccountMove(std::string strFrom, std::string strTo, CAmount nAmount, std::string strComment = ""); + bool AccountMove(std::string strFrom, std::string strTo, CAmount nAmount, std::string strComment = "") EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); bool GetLabelDestination(CTxDestination &dest, const std::string& label, bool bForceNew = false); void MarkDirty(); @@ -921,7 +933,7 @@ public: void TransactionAddedToMempool(const CTransactionRef& tx) override; void BlockConnected(const std::shared_ptr<const CBlock>& pblock, const CBlockIndex *pindex, const std::vector<CTransactionRef>& vtxConflicted) override; void BlockDisconnected(const std::shared_ptr<const CBlock>& pblock) override; - bool AddToWalletIfInvolvingMe(const CTransactionRef& tx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate); + bool AddToWalletIfInvolvingMe(const CTransactionRef& tx, const CBlockIndex* pIndex, int posInBlock, bool fUpdate) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); int64_t RescanFromTime(int64_t startTime, const WalletRescanReserver& reserver, bool update); CBlockIndex* ScanForWalletTransactions(CBlockIndex* pindexStart, CBlockIndex* pindexStop, const WalletRescanReserver& reserver, bool fUpdate = false); void TransactionRemovedFromMempool(const CTransactionRef &ptx) override; @@ -945,7 +957,7 @@ public: * calling CreateTransaction(); */ bool FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, std::string& strFailReason, bool lockUnspents, const std::set<int>& setSubtractFeeFromOutputs, CCoinControl); - bool SignTransaction(CMutableTransaction& tx); + bool SignTransaction(CMutableTransaction& tx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); /** * Create a new transaction paying the recipients with a set of coins @@ -985,9 +997,24 @@ public: OutputType m_default_change_type{DEFAULT_CHANGE_TYPE}; bool NewKeyPool(); - size_t KeypoolCountExternalKeys(); + size_t KeypoolCountExternalKeys() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); bool TopUpKeyPool(unsigned int kpSize = 0); - void ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRequestedInternal); + + /** + * Reserves a key from the keypool and sets nIndex to its index + * + * @param[out] nIndex the index of the key in keypool + * @param[out] keypool the keypool the key was drawn from, which could be the + * the pre-split pool if present, or the internal or external pool + * @param fRequestedInternal true if the caller would like the key drawn + * from the internal keypool, false if external is preferred + * + * @return true if succeeded, false if failed due to empty keypool + * @throws std::runtime_error if keypool read failed, key was invalid, + * was not found in the wallet, or was misclassified in the internal + * or external keypool + */ + bool ReserveKeyFromKeyPool(int64_t& nIndex, CKeyPool& keypool, bool fRequestedInternal); void KeepKey(int64_t nIndex); void ReturnKey(int64_t nIndex, bool fInternal, const CPubKey& pubkey); bool GetKeyFromPool(CPubKey &key, bool internal = false); @@ -995,10 +1022,10 @@ public: /** * Marks all keys in the keypool up to and including reserve_key as used. */ - void MarkReserveKeysAsUsed(int64_t keypool_id); + void MarkReserveKeysAsUsed(int64_t keypool_id) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); const std::map<CKeyID, int64_t>& GetAllReserveKeys() const { return m_pool_key_to_index; } - std::set< std::set<CTxDestination> > GetAddressGroupings(); + std::set<std::set<CTxDestination>> GetAddressGroupings() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); std::map<CTxDestination, CAmount> GetAddressBalances(); std::set<CTxDestination> GetLabelAddresses(const std::string& label) const; @@ -1026,7 +1053,7 @@ public: DBErrors LoadWallet(bool& fFirstRunRet); DBErrors ZapWalletTx(std::vector<CWalletTx>& vWtx); - DBErrors ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256>& vHashOut); + DBErrors ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256>& vHashOut) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); bool SetAddressBook(const CTxDestination& address, const std::string& strName, const std::string& purpose); @@ -1046,7 +1073,7 @@ public: void GetScriptForMining(std::shared_ptr<CReserveScript> &script); - unsigned int GetKeyPoolSize() + unsigned int GetKeyPoolSize() EXCLUSIVE_LOCKS_REQUIRED(cs_wallet) { AssertLockHeld(cs_wallet); // set{Ex,In}ternalKeyPool return setInternalKeyPool.size() + setExternalKeyPool.size(); @@ -1065,7 +1092,7 @@ public: std::set<uint256> GetConflicts(const uint256& txid) const; //! Check if a given transaction has any of its outputs spent by another transaction in the wallet - bool HasWalletSpend(const uint256& txid) const; + bool HasWalletSpend(const uint256& txid) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); //! Flush wallet (bitdb flush) void Flush(bool shutdown=false); @@ -1106,14 +1133,17 @@ public: /** Mark a transaction as replaced by another transaction (e.g., BIP 125). */ bool MarkReplaced(const uint256& originalHash, const uint256& newHash); + //! Verify wallet naming and perform salvage on the wallet if required + static bool Verify(std::string wallet_file, bool salvage_wallet, std::string& error_string, std::string& warning_string); + /* Initializes the wallet, returns a new CWallet instance or a null pointer in case of an error */ - static CWallet* CreateWalletFromFile(const std::string& name, const fs::path& path); + static std::shared_ptr<CWallet> CreateWalletFromFile(const std::string& name, const fs::path& path); /** * Wallet post-init setup * Gives the wallet a chance to register repetitive tasks and complete post-init tasks */ - void postInitProcess(CScheduler& scheduler); + void postInitProcess(); bool BackupWallet(const std::string& strDest); @@ -1124,14 +1154,17 @@ public: /* Returns true if HD is enabled */ bool IsHDEnabled() const; - /* Generates a new HD master key (will not be activated) */ - CPubKey GenerateNewHDMasterKey(); + /* Generates a new HD seed (will not be activated) */ + CPubKey GenerateNewSeed(); + + /* Derives a new HD seed (will not be activated) */ + CPubKey DeriveNewSeed(const CKey& key); - /* Set the current HD master key (will reset the chain child index counters) - Sets the master key's version based on the current wallet version (so the + /* Set the current HD seed (will reset the chain child index counters) + Sets the seed's version based on the current wallet version (so the caller must ensure the current wallet version is correct before calling this function). */ - bool SetHDMasterKey(const CPubKey& key); + bool SetHDSeed(const CPubKey& key); /** * Blocks until the wallet state is up-to-date to /at least/ the current @@ -1139,7 +1172,7 @@ public: * Obviously holding cs_main/cs_wallet when going into this call may cause * deadlock */ - void BlockUntilSyncedToCurrentChain(); + void BlockUntilSyncedToCurrentChain() LOCKS_EXCLUDED(cs_wallet); /** * Explicitly make the wallet learn the related scripts for outputs to the diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 4d1a6d48d0..4b4460a794 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -248,7 +248,7 @@ public: static bool ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, - CWalletScanState &wss, std::string& strType, std::string& strErr) + CWalletScanState &wss, std::string& strType, std::string& strErr) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet) { try { // Unserialize @@ -756,7 +756,7 @@ void MaybeCompactWalletDB() return; } - for (CWallet* pwallet : GetWallets()) { + for (const std::shared_ptr<CWallet>& pwallet : GetWallets()) { WalletDatabase& dbh = pwallet->GetDBHandle(); unsigned int nUpdateCounter = dbh.nUpdateCounter; diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index a73d727c0c..3237376f63 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -62,7 +62,7 @@ class CHDChain public: uint32_t nExternalChainCounter; uint32_t nInternalChainCounter; - CKeyID masterKeyID; //!< master key hash160 + CKeyID seed_id; //!< seed hash160 static const int VERSION_HD_BASE = 1; static const int VERSION_HD_CHAIN_SPLIT = 2; @@ -76,7 +76,7 @@ public: { READWRITE(this->nVersion); READWRITE(nExternalChainCounter); - READWRITE(masterKeyID); + READWRITE(seed_id); if (this->nVersion >= VERSION_HD_CHAIN_SPLIT) READWRITE(nInternalChainCounter); } @@ -86,7 +86,7 @@ public: nVersion = CHDChain::CURRENT_VERSION; nExternalChainCounter = 0; nInternalChainCounter = 0; - masterKeyID.SetNull(); + seed_id.SetNull(); } }; @@ -99,7 +99,7 @@ public: int nVersion; int64_t nCreateTime; // 0 means unknown std::string hdKeypath; //optional HD/bip32 keypath - CKeyID hdMasterKeyID; //id of the HD masterkey used to derive this key + CKeyID hd_seed_id; //id of the HD seed used to derive this key CKeyMetadata() { @@ -120,7 +120,7 @@ public: if (this->nVersion >= VERSION_WITH_HDDATA) { READWRITE(hdKeypath); - READWRITE(hdMasterKeyID); + READWRITE(hd_seed_id); } } @@ -129,7 +129,7 @@ public: nVersion = CKeyMetadata::CURRENT_VERSION; nCreateTime = 0; hdKeypath.clear(); - hdMasterKeyID.SetNull(); + hd_seed_id.SetNull(); } }; diff --git a/src/walletinitinterface.h b/src/walletinitinterface.h index 5bfde6faaf..020d8971cb 100644 --- a/src/walletinitinterface.h +++ b/src/walletinitinterface.h @@ -13,7 +13,7 @@ class CRPCTable; class WalletInitInterface { public: /** Get wallet help string */ - virtual std::string GetHelpString(bool showDebug) const = 0; + virtual void AddWalletOptions() const = 0; /** Check wallet parameter interaction */ virtual bool ParameterInteraction() const = 0; /** Register wallet RPC*/ diff --git a/src/warnings.cpp b/src/warnings.cpp index dc4e6e4842..534745f998 100644 --- a/src/warnings.cpp +++ b/src/warnings.cpp @@ -9,9 +9,9 @@ #include <warnings.h> CCriticalSection cs_warnings; -std::string strMiscWarning; -bool fLargeWorkForkFound = false; -bool fLargeWorkInvalidChainFound = false; +std::string strMiscWarning GUARDED_BY(cs_warnings); +bool fLargeWorkForkFound GUARDED_BY(cs_warnings) = false; +bool fLargeWorkInvalidChainFound GUARDED_BY(cs_warnings) = false; void SetMiscWarning(const std::string& strWarning) { |