diff options
Diffstat (limited to 'src')
61 files changed, 1385 insertions, 906 deletions
diff --git a/src/.clang-format b/src/.clang-format index 2d2ee67035..38e19edf2c 100644 --- a/src/.clang-format +++ b/src/.clang-format @@ -12,7 +12,10 @@ AlwaysBreakBeforeMultilineStrings: false AlwaysBreakTemplateDeclarations: true BinPackParameters: false BreakBeforeBinaryOperators: false -BreakBeforeBraces: Linux +BreakBeforeBraces: Custom +BraceWrapping: + AfterClass: true + AfterFunction: true BreakBeforeTernaryOperators: false BreakConstructorInitializersBeforeComma: false ColumnLimit: 0 diff --git a/src/Makefile.am b/src/Makefile.am index 1bbb92bf42..521687eb45 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -113,6 +113,7 @@ BITCOIN_CORE_H = \ keystore.h \ dbwrapper.h \ limitedmap.h \ + logging.h \ memusage.h \ merkleblock.h \ miner.h \ @@ -364,6 +365,7 @@ libbitcoin_util_a_SOURCES = \ fs.cpp \ interfaces/handler.cpp \ interfaces/node.cpp \ + logging.cpp \ random.cpp \ rpc/protocol.cpp \ rpc/util.cpp \ diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include index 748c5b7887..3306dcf598 100644 --- a/src/Makefile.bench.include +++ b/src/Makefile.bench.include @@ -25,8 +25,6 @@ bench_bench_bitcoin_SOURCES = \ bench/verify_script.cpp \ bench/base58.cpp \ bench/lockedpool.cpp \ - bench/perf.cpp \ - bench/perf.h \ bench/prevector.cpp nodist_bench_bench_bitcoin_SOURCES = $(GENERATED_BENCH_FILES) diff --git a/src/addrdb.cpp b/src/addrdb.cpp index e4620e63c6..59305ff187 100644 --- a/src/addrdb.cpp +++ b/src/addrdb.cpp @@ -49,7 +49,8 @@ bool SerializeFileDB(const std::string& prefix, const fs::path& path, const Data // Serialize if (!SerializeDB(fileout, data)) return false; - FileCommit(fileout.Get()); + if (!FileCommit(fileout.Get())) + return error("%s: Failed to flush file %s", __func__, pathTmp.string()); fileout.fclose(); // replace existing file, if any, with new file diff --git a/src/bench/bench.cpp b/src/bench/bench.cpp index 21329a5151..de3e57b04f 100644 --- a/src/bench/bench.cpp +++ b/src/bench/bench.cpp @@ -3,7 +3,6 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <bench/bench.h> -#include <bench/perf.h> #include <assert.h> #include <iostream> @@ -96,7 +95,6 @@ benchmark::BenchRunner::BenchRunner(std::string name, benchmark::BenchFunction f void benchmark::BenchRunner::RunAll(Printer& printer, uint64_t num_evals, double scaling, const std::string& filter, bool is_list_only) { - perf_init(); if (!std::ratio_less_equal<benchmark::clock::period, std::micro>::value) { std::cerr << "WARNING: Clock precision is worse than microsecond - benchmarks may be less accurate!\n"; } @@ -126,8 +124,6 @@ void benchmark::BenchRunner::RunAll(Printer& printer, uint64_t num_evals, double } printer.footer(); - - perf_fini(); } bool benchmark::State::UpdateTimer(const benchmark::time_point current_time) diff --git a/src/bench/mempool_eviction.cpp b/src/bench/mempool_eviction.cpp index cdda0bd9be..e05a5e3d1e 100644 --- a/src/bench/mempool_eviction.cpp +++ b/src/bench/mempool_eviction.cpp @@ -9,7 +9,7 @@ #include <list> #include <vector> -static void AddTx(const CTransaction& tx, const CAmount& nFee, CTxMemPool& pool) +static void AddTx(const CMutableTransaction& tx, const CAmount& nFee, CTxMemPool& pool) { int64_t nTime = 0; unsigned int nHeight = 1; diff --git a/src/bench/perf.cpp b/src/bench/perf.cpp deleted file mode 100644 index f92d08c56e..0000000000 --- a/src/bench/perf.cpp +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) 2016-2017 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#include <bench/perf.h> - -#if defined(__i386__) || defined(__x86_64__) - -/* These architectures support querying the cycle counter - * from user space, no need for any syscall overhead. - */ -void perf_init(void) { } -void perf_fini(void) { } - -#elif defined(__linux__) - -#include <unistd.h> -#include <sys/syscall.h> -#include <linux/perf_event.h> - -static int fd = -1; -static struct perf_event_attr attr; - -void perf_init(void) -{ - attr.type = PERF_TYPE_HARDWARE; - attr.config = PERF_COUNT_HW_CPU_CYCLES; - fd = syscall(__NR_perf_event_open, &attr, 0, -1, -1, 0); -} - -void perf_fini(void) -{ - if (fd != -1) { - close(fd); - } -} - -uint64_t perf_cpucycles(void) -{ - uint64_t result = 0; - if (fd == -1 || read(fd, &result, sizeof(result)) < (ssize_t)sizeof(result)) { - return 0; - } - return result; -} - -#else /* Unhandled platform */ - -void perf_init(void) { } -void perf_fini(void) { } -uint64_t perf_cpucycles(void) { return 0; } - -#endif diff --git a/src/bench/perf.h b/src/bench/perf.h deleted file mode 100644 index 73ea8b9647..0000000000 --- a/src/bench/perf.h +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) 2016 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -/** Functions for measurement of CPU cycles */ -#ifndef BITCOIN_BENCH_PERF_H -#define BITCOIN_BENCH_PERF_H - -#include <stdint.h> - -#if defined(__i386__) - -static inline uint64_t perf_cpucycles(void) -{ - uint64_t x; - __asm__ volatile (".byte 0x0f, 0x31" : "=A" (x)); - return x; -} - -#elif defined(__x86_64__) - -static inline uint64_t perf_cpucycles(void) -{ - uint32_t hi, lo; - __asm__ __volatile__ ("rdtsc" : "=a"(lo), "=d"(hi)); - return ((uint64_t)lo)|(((uint64_t)hi)<<32); -} -#else - -uint64_t perf_cpucycles(void); - -#endif - -void perf_init(void); -void perf_fini(void); - -#endif // BITCOIN_BENCH_PERF_H diff --git a/src/bench/verify_script.cpp b/src/bench/verify_script.cpp index 705fa368a5..4100519d48 100644 --- a/src/bench/verify_script.cpp +++ b/src/bench/verify_script.cpp @@ -71,7 +71,7 @@ static void VerifyScriptBench(benchmark::State& state) CScript scriptPubKey = CScript() << witnessversion << ToByteVector(pubkeyHash); CScript scriptSig; CScript witScriptPubkey = CScript() << OP_DUP << OP_HASH160 << ToByteVector(pubkeyHash) << OP_EQUALVERIFY << OP_CHECKSIG; - CTransaction txCredit = BuildCreditingTransaction(scriptPubKey); + const CMutableTransaction& txCredit = BuildCreditingTransaction(scriptPubKey); CMutableTransaction txSpend = BuildSpendingTransaction(scriptSig, txCredit); CScriptWitness& witness = txSpend.vin[0].scriptWitness; witness.stack.emplace_back(); diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index 83d9719df2..0dc2dfbf7d 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -42,12 +42,9 @@ void WaitForShutdown() { - bool fShutdown = ShutdownRequested(); - // Tell the main threads to shutdown. - while (!fShutdown) + while (!ShutdownRequested()) { MilliSleep(200); - fShutdown = ShutdownRequested(); } Interrupt(); } diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 6067503b0b..121d95af90 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -75,7 +75,7 @@ public: CMainParams() { strNetworkID = "main"; consensus.nSubsidyHalvingInterval = 210000; - consensus.BIP16Height = 173805; // 00000000000000ce80a7e057163a4db1d5ad7b20fb6f598c9597b9665c8fb0d4 - April 1, 2012 + consensus.BIP16Exception = uint256S("0x00000000000002dc756eebf4f49723ed8d30cc28a5f108eb94b1ba88ac4f9c22"); consensus.BIP34Height = 227931; consensus.BIP34Hash = uint256S("0x000000000000024b89b42a942fe0d9fea3bb44ab7bd1b19115dd6a759c0808b8"); consensus.BIP65Height = 388381; // 000000000000000004c2b624ed5d7756c508d90fd0da2c7c679febfa6c4735f0 @@ -190,7 +190,7 @@ public: CTestNetParams() { strNetworkID = "test"; consensus.nSubsidyHalvingInterval = 210000; - consensus.BIP16Height = 514; // 00000000040b4e986385315e14bee30ad876d8b47f748025b26683116d21aa65 + consensus.BIP16Exception = uint256S("0x00000000dd30457c001f4095d208cc1296b0eed002427aa599874af7a432b105"); consensus.BIP34Height = 21111; consensus.BIP34Hash = uint256S("0x0000000023b3a96d3484e5abb3755c413e7d41500f8e2a5c3f0dd01299cd8ef8"); consensus.BIP65Height = 581885; // 00000000007f6655f22f98e72ed80d8b06dc761d5da09df0fa1dc4be4f861eb6 @@ -283,7 +283,7 @@ public: CRegTestParams() { strNetworkID = "regtest"; consensus.nSubsidyHalvingInterval = 150; - consensus.BIP16Height = 0; // always enforce P2SH BIP16 on regtest + consensus.BIP16Exception = uint256(); consensus.BIP34Height = 100000000; // BIP34 has not activated on regtest (far in the future so block v1 are not rejected in tests) consensus.BIP34Hash = uint256(); consensus.BIP65Height = 1351; // BIP65 activated on regtest (Used in rpc activation tests) diff --git a/src/chainparams.h b/src/chainparams.h index 6b1f813afb..dd029b9d5b 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -25,6 +25,12 @@ struct CCheckpointData { MapCheckpoints mapCheckpoints; }; +/** + * Holds various statistics on transactions within a chain. Used to estimate + * verification progress during chain sync. + * + * See also: CChainParams::TxData, GuessVerificationProgress. + */ struct ChainTxData { int64_t nTime; int64_t nTxCount; diff --git a/src/chainparamsbase.cpp b/src/chainparamsbase.cpp index e840a2ed30..3ef9c2cfe5 100644 --- a/src/chainparamsbase.cpp +++ b/src/chainparamsbase.cpp @@ -47,4 +47,5 @@ std::unique_ptr<CBaseChainParams> CreateBaseChainParams(const std::string& chain void SelectBaseParams(const std::string& chain) { globalChainBaseParams = CreateBaseChainParams(chain); + gArgs.SelectConfigNetwork(chain); } diff --git a/src/compat.h b/src/compat.h index 8a0f901304..920b3f776d 100644 --- a/src/compat.h +++ b/src/compat.h @@ -96,6 +96,12 @@ typedef int32_t ssize_t; size_t strnlen( const char *start, size_t max_len); #endif // HAVE_DECL_STRNLEN +#ifndef WIN32 +typedef void* sockopt_arg_type; +#else +typedef char* sockopt_arg_type; +#endif + bool static inline IsSelectableSocket(const SOCKET& s) { #ifdef WIN32 return true; diff --git a/src/compat/endian.h b/src/compat/endian.h index e5c7e50223..4f244c3930 100644 --- a/src/compat/endian.h +++ b/src/compat/endian.h @@ -19,6 +19,51 @@ #include <sys/endian.h> #endif +#ifndef HAVE_CONFIG_H +// While not technically a supported configuration, defaulting to defining these +// DECLs when we were compiled without autotools makes it easier for other build +// systems to build things like libbitcoinconsensus for strange targets. +#ifdef htobe16 +#define HAVE_DECL_HTOBE16 1 +#endif +#ifdef htole16 +#define HAVE_DECL_HTOLE16 1 +#endif +#ifdef be16toh +#define HAVE_DECL_BE16TOH 1 +#endif +#ifdef le16toh +#define HAVE_DECL_LE16TOH 1 +#endif + +#ifdef htobe32 +#define HAVE_DECL_HTOBE32 1 +#endif +#ifdef htole32 +#define HAVE_DECL_HTOLE32 1 +#endif +#ifdef be32toh +#define HAVE_DECL_BE32TOH 1 +#endif +#ifdef le32toh +#define HAVE_DECL_LE32TOH 1 +#endif + +#ifdef htobe64 +#define HAVE_DECL_HTOBE64 1 +#endif +#ifdef htole64 +#define HAVE_DECL_HTOLE64 1 +#endif +#ifdef be64toh +#define HAVE_DECL_BE64TOH 1 +#endif +#ifdef le64toh +#define HAVE_DECL_LE64TOH 1 +#endif + +#endif // HAVE_CONFIG_H + #if defined(WORDS_BIGENDIAN) #if HAVE_DECL_HTOBE16 == 0 diff --git a/src/consensus/params.h b/src/consensus/params.h index 4ef808c856..0559304fc2 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -49,8 +49,8 @@ struct BIP9Deployment { struct Params { uint256 hashGenesisBlock; int nSubsidyHalvingInterval; - /** Block height at which BIP16 becomes active */ - int BIP16Height; + /* Block hash that is excepted from BIP16 enforcement */ + uint256 BIP16Exception; /** Block height and hash at which BIP34 becomes active */ int BIP34Height; uint256 BIP34Hash; diff --git a/src/core_write.cpp b/src/core_write.cpp index 58d3aad5bf..ee6737201b 100644 --- a/src/core_write.cpp +++ b/src/core_write.cpp @@ -161,6 +161,7 @@ void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, entry.pushKV("version", tx.nVersion); entry.pushKV("size", (int)::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION)); entry.pushKV("vsize", (GetTransactionWeight(tx) + WITNESS_SCALE_FACTOR - 1) / WITNESS_SCALE_FACTOR); + entry.pushKV("weight", GetTransactionWeight(tx)); entry.pushKV("locktime", (int64_t)tx.nLockTime); UniValue vin(UniValue::VARR); diff --git a/src/init.cpp b/src/init.cpp index 486c84f5a3..f403f90b08 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -76,19 +76,18 @@ std::unique_ptr<PeerLogicValidation> peerLogic; class DummyWalletInit : public WalletInitInterface { public: - std::string GetHelpString(bool showDebug) override {return std::string{};} - bool ParameterInteraction() override {return true;} - void RegisterRPC(CRPCTable &) override {} - bool Verify() override {return true;} - bool Open() override {LogPrintf("No wallet support compiled in!\n"); return true;} - void Start(CScheduler& scheduler) override {} - void Flush() override {} - void Stop() override {} - void Close() override {} + std::string GetHelpString(bool showDebug) const override {return std::string{};} + bool ParameterInteraction() const override {return true;} + void RegisterRPC(CRPCTable &) const override {} + bool Verify() const override {return true;} + bool Open() const override {LogPrintf("No wallet support compiled in!\n"); return true;} + void Start(CScheduler& scheduler) const override {} + void Flush() const override {} + void Stop() const override {} + void Close() const override {} }; -static DummyWalletInit g_dummy_wallet_init; -WalletInitInterface* const g_wallet_init_interface = &g_dummy_wallet_init; +const WalletInitInterface& g_wallet_init_interface = DummyWalletInit(); #endif #if ENABLE_ZMQ @@ -204,7 +203,7 @@ void Shutdown() StopREST(); StopRPC(); StopHTTPServer(); - g_wallet_init_interface->Flush(); + g_wallet_init_interface.Flush(); StopMapPort(); // Because these depend on each-other, we make sure that neither can be @@ -262,7 +261,7 @@ void Shutdown() pcoinsdbview.reset(); pblocktree.reset(); } - g_wallet_init_interface->Stop(); + g_wallet_init_interface.Stop(); #if ENABLE_ZMQ if (pzmqNotificationInterface) { @@ -282,7 +281,7 @@ void Shutdown() UnregisterAllValidationInterfaces(); GetMainSignals().UnregisterBackgroundSignalScheduler(); GetMainSignals().UnregisterWithMempoolSignals(mempool); - g_wallet_init_interface->Close(); + g_wallet_init_interface.Close(); globalVerifyHandle.reset(); ECC_Stop(); LogPrintf("%s: done\n", __func__); @@ -425,7 +424,7 @@ std::string HelpMessage(HelpMessageMode mode) 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")); - strUsage += g_wallet_init_interface->GetHelpString(showDebug); + strUsage += g_wallet_init_interface.GetHelpString(showDebug); #if ENABLE_ZMQ strUsage += HelpMessageGroup(_("ZeroMQ notification options:")); @@ -685,7 +684,7 @@ void ThreadImport(std::vector<fs::path> vImportFiles) // scan for better chains in the block chain database, that are not yet connected in the active best chain CValidationState state; if (!ActivateBestChain(state, chainparams)) { - LogPrintf("Failed to connect best block\n"); + LogPrintf("Failed to connect best block (%s)\n", FormatStateMessage(state)); StartShutdown(); return; } @@ -803,6 +802,11 @@ void InitParameterInteraction() if (gArgs.SoftSetBoolArg("-whitelistrelay", true)) LogPrintf("%s: parameter interaction: -whitelistforcerelay=1 -> setting -whitelistrelay=1\n", __func__); } + + // Warn if network-specific options (-addnode, -connect, etc) are + // specified in default section of config file, but not overridden + // on the command line or in this network's section of the config file. + gArgs.WarnForSectionOnlyArgs(); } static std::string ResolveErrMsg(const char * const optname, const std::string& strBind) @@ -810,14 +814,25 @@ static std::string ResolveErrMsg(const char * const optname, const std::string& return strprintf(_("Cannot resolve -%s address: '%s'"), optname, strBind); } +/** + * Initialize global loggers. + * + * Note that this is called very early in the process lifetime, so you should be + * careful about what global state you rely on here. + */ void InitLogging() { - fPrintToConsole = gArgs.GetBoolArg("-printtoconsole", false); + // Add newlines to the logfile to distinguish this execution from the last + // one; called before console logging is set up, so this is only sent to + // debug.log. + LogPrintf("\n\n\n\n\n"); + + fPrintToConsole = gArgs.GetBoolArg("-printtoconsole", !gArgs.GetBoolArg("-daemon", false)); + fPrintToDebugLog = !gArgs.IsArgNegated("-debuglogfile"); fLogTimestamps = gArgs.GetBoolArg("-logtimestamps", DEFAULT_LOGTIMESTAMPS); fLogTimeMicros = gArgs.GetBoolArg("-logtimemicros", DEFAULT_LOGTIMEMICROS); fLogIPs = gArgs.GetBoolArg("-logips", DEFAULT_LOGIPS); - LogPrintf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n"); std::string version_string = FormatFullVersion(); #ifdef DEBUG version_string += " (debug build)"; @@ -1093,7 +1108,7 @@ bool AppInitParameterInteraction() return InitError(strprintf("acceptnonstdtxn is not currently supported for %s chain", chainparams.NetworkIDString())); nBytesPerSigOp = gArgs.GetArg("-bytespersigop", nBytesPerSigOp); - if (!g_wallet_init_interface->ParameterInteraction()) return false; + if (!g_wallet_init_interface.ParameterInteraction()) return false; fIsBareMultisigStd = gArgs.GetBoolArg("-permitbaremultisig", DEFAULT_PERMIT_BAREMULTISIG); fAcceptDatacarrier = gArgs.GetBoolArg("-datacarrier", DEFAULT_ACCEPT_DATACARRIER); @@ -1211,13 +1226,12 @@ bool AppInitMain() #ifndef WIN32 CreatePidFile(GetPidFile(), getpid()); #endif - if (gArgs.GetBoolArg("-shrinkdebugfile", logCategories == BCLog::NONE)) { - // Do this first since it both loads a bunch of debug.log into memory, - // and because this needs to happen before any other debug.log printing - ShrinkDebugFile(); - } - if (fPrintToDebugLog) { + if (gArgs.GetBoolArg("-shrinkdebugfile", logCategories == BCLog::NONE)) { + // Do this first since it both loads a bunch of debug.log into memory, + // and because this needs to happen before any other debug.log printing + ShrinkDebugFile(); + } if (!OpenDebugLog()) { return InitError(strprintf("Could not open debug log file %s", GetDebugLogPath().string())); } @@ -1259,7 +1273,7 @@ bool AppInitMain() * available in the GUI RPC console even if external calls are disabled. */ RegisterAllCoreRPCCommands(tableRPC); - g_wallet_init_interface->RegisterRPC(tableRPC); + g_wallet_init_interface.RegisterRPC(tableRPC); /* Start the RPC server already. It will be started in "warmup" mode * and not really process calls already (but it will signify connections @@ -1276,7 +1290,7 @@ bool AppInitMain() int64_t nStart; // ********************************************************* Step 5: verify wallet database integrity - if (!g_wallet_init_interface->Verify()) return false; + if (!g_wallet_init_interface.Verify()) return false; // ********************************************************* Step 6: network initialization // Note that we absolutely cannot open any actual connections @@ -1595,7 +1609,7 @@ bool AppInitMain() fFeeEstimatesInitialized = true; // ********************************************************* Step 8: load wallet - if (!g_wallet_init_interface->Open()) return false; + if (!g_wallet_init_interface.Open()) return false; // ********************************************************* Step 9: data directory maintenance @@ -1741,7 +1755,7 @@ bool AppInitMain() SetRPCWarmupFinished(); uiInterface.InitMessage(_("Done loading")); - g_wallet_init_interface->Start(scheduler); + g_wallet_init_interface.Start(scheduler); return true; } diff --git a/src/init.h b/src/init.h index 829c110112..000c8c95e4 100644 --- a/src/init.h +++ b/src/init.h @@ -13,7 +13,7 @@ class CScheduler; class CWallet; class WalletInitInterface; -extern WalletInitInterface* const g_wallet_init_interface; +extern const WalletInitInterface& g_wallet_init_interface; namespace boost { diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp index 919748f942..e55cba3c65 100644 --- a/src/interfaces/node.cpp +++ b/src/interfaces/node.cpp @@ -216,9 +216,6 @@ class NodeImpl : public Node return result; } CFeeRate getDustRelayFee() override { return ::dustRelayFee; } - CFeeRate getFallbackFee() override { CHECK_WALLET(return CWallet::fallbackFee); } - CFeeRate getPayTxFee() override { CHECK_WALLET(return ::payTxFee); } - void setPayTxFee(CFeeRate rate) override { CHECK_WALLET(::payTxFee = rate); } UniValue executeRpc(const std::string& command, const UniValue& params, const std::string& uri) override { JSONRPCRequest req; @@ -239,7 +236,7 @@ class NodeImpl : public Node { #ifdef ENABLE_WALLET std::vector<std::unique_ptr<Wallet>> wallets; - for (CWalletRef wallet : ::vpwallets) { + for (CWallet* wallet : GetWallets()) { wallets.emplace_back(MakeWallet(*wallet)); } return wallets; diff --git a/src/interfaces/node.h b/src/interfaces/node.h index f375af2f19..84e869100a 100644 --- a/src/interfaces/node.h +++ b/src/interfaces/node.h @@ -173,15 +173,6 @@ public: //! Get dust relay fee. virtual CFeeRate getDustRelayFee() = 0; - //! Get fallback fee. - virtual CFeeRate getFallbackFee() = 0; - - //! Get pay tx fee. - virtual CFeeRate getPayTxFee() = 0; - - //! Set pay tx fee. - virtual void setPayTxFee(CFeeRate rate) = 0; - //! Execute rpc command. virtual UniValue executeRpc(const std::string& command, const UniValue& params, const std::string& uri) = 0; diff --git a/src/interfaces/wallet.cpp b/src/interfaces/wallet.cpp index fbf631f7da..3e6f0d6728 100644 --- a/src/interfaces/wallet.cpp +++ b/src/interfaces/wallet.cpp @@ -130,6 +130,7 @@ public: { return m_wallet.ChangeWalletPassphrase(old_wallet_passphrase, new_wallet_passphrase); } + void abortRescan() override { m_wallet.AbortRescan(); } bool backupWallet(const std::string& filename) override { return m_wallet.BackupWallet(filename); } std::string getWalletName() override { return m_wallet.GetName(); } bool getKeyFromPool(bool internal, CPubKey& pub_key) override diff --git a/src/interfaces/wallet.h b/src/interfaces/wallet.h index 3e27242c2c..9d4830d189 100644 --- a/src/interfaces/wallet.h +++ b/src/interfaces/wallet.h @@ -6,7 +6,7 @@ #define BITCOIN_INTERFACES_WALLET_H #include <amount.h> // For CAmount -#include <pubkey.h> // For CTxDestination (CKeyID and CScriptID) +#include <pubkey.h> // For CKeyID and CScriptID (definitions needed in CTxDestination instantiation) #include <script/ismine.h> // For isminefilter, isminetype #include <script/standard.h> // For CTxDestination #include <support/allocators/secure.h> // For SecureString @@ -65,6 +65,9 @@ public: virtual bool changeWalletPassphrase(const SecureString& old_wallet_passphrase, const SecureString& new_wallet_passphrase) = 0; + //! Abort a rescan. + virtual void abortRescan() = 0; + //! Back up wallet. virtual bool backupWallet(const std::string& filename) = 0; diff --git a/src/logging.cpp b/src/logging.cpp new file mode 100644 index 0000000000..e481582321 --- /dev/null +++ b/src/logging.cpp @@ -0,0 +1,283 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-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 <logging.h> +#include <util.h> +#include <utilstrencodings.h> + +#include <list> +#include <mutex> + +const char * const DEFAULT_DEBUGLOGFILE = "debug.log"; + +bool fPrintToConsole = false; +bool fPrintToDebugLog = true; + +bool fLogTimestamps = DEFAULT_LOGTIMESTAMPS; +bool fLogTimeMicros = DEFAULT_LOGTIMEMICROS; +bool fLogIPs = DEFAULT_LOGIPS; +std::atomic<bool> fReopenDebugLog(false); + +/** Log categories bitfield. */ +std::atomic<uint32_t> logCategories(0); +/** + * LogPrintf() has been broken a couple of times now + * by well-meaning people adding mutexes in the most straightforward way. + * It breaks because it may be called by global destructors during shutdown. + * Since the order of destruction of static/global objects is undefined, + * defining a mutex as a global object doesn't work (the mutex gets + * destroyed, and then some later destructor calls OutputDebugStringF, + * maybe indirectly, and you get a core dump at shutdown trying to lock + * the mutex). + */ + +static std::once_flag debugPrintInitFlag; + +/** + * We use std::call_once() to make sure mutexDebugLog and + * vMsgsBeforeOpenLog are initialized in a thread-safe manner. + * + * NOTE: fileout, mutexDebugLog and sometimes vMsgsBeforeOpenLog + * are leaked on exit. This is ugly, but will be cleaned up by + * the OS/libc. When the shutdown sequence is fully audited and + * tested, explicit destruction of these objects can be implemented. + */ +static FILE* fileout = nullptr; +static std::mutex* mutexDebugLog = nullptr; +static std::list<std::string>* vMsgsBeforeOpenLog; + +static int FileWriteStr(const std::string &str, FILE *fp) +{ + return fwrite(str.data(), 1, str.size(), fp); +} + +static void DebugPrintInit() +{ + assert(mutexDebugLog == nullptr); + mutexDebugLog = new std::mutex(); + vMsgsBeforeOpenLog = new std::list<std::string>; +} + +fs::path GetDebugLogPath() +{ + fs::path logfile(gArgs.GetArg("-debuglogfile", DEFAULT_DEBUGLOGFILE)); + return AbsPathForConfigVal(logfile); +} + +bool OpenDebugLog() +{ + std::call_once(debugPrintInitFlag, &DebugPrintInit); + std::lock_guard<std::mutex> scoped_lock(*mutexDebugLog); + + assert(fileout == nullptr); + assert(vMsgsBeforeOpenLog); + fs::path pathDebug = GetDebugLogPath(); + + fileout = fsbridge::fopen(pathDebug, "a"); + if (!fileout) { + return false; + } + + setbuf(fileout, nullptr); // unbuffered + // dump buffered messages from before we opened the log + while (!vMsgsBeforeOpenLog->empty()) { + FileWriteStr(vMsgsBeforeOpenLog->front(), fileout); + vMsgsBeforeOpenLog->pop_front(); + } + + delete vMsgsBeforeOpenLog; + vMsgsBeforeOpenLog = nullptr; + return true; +} + +struct CLogCategoryDesc +{ + uint32_t flag; + std::string category; +}; + +const CLogCategoryDesc LogCategories[] = +{ + {BCLog::NONE, "0"}, + {BCLog::NONE, "none"}, + {BCLog::NET, "net"}, + {BCLog::TOR, "tor"}, + {BCLog::MEMPOOL, "mempool"}, + {BCLog::HTTP, "http"}, + {BCLog::BENCH, "bench"}, + {BCLog::ZMQ, "zmq"}, + {BCLog::DB, "db"}, + {BCLog::RPC, "rpc"}, + {BCLog::ESTIMATEFEE, "estimatefee"}, + {BCLog::ADDRMAN, "addrman"}, + {BCLog::SELECTCOINS, "selectcoins"}, + {BCLog::REINDEX, "reindex"}, + {BCLog::CMPCTBLOCK, "cmpctblock"}, + {BCLog::RAND, "rand"}, + {BCLog::PRUNE, "prune"}, + {BCLog::PROXY, "proxy"}, + {BCLog::MEMPOOLREJ, "mempoolrej"}, + {BCLog::LIBEVENT, "libevent"}, + {BCLog::COINDB, "coindb"}, + {BCLog::QT, "qt"}, + {BCLog::LEVELDB, "leveldb"}, + {BCLog::ALL, "1"}, + {BCLog::ALL, "all"}, +}; + +bool GetLogCategory(uint32_t *f, const std::string *str) +{ + if (f && str) { + if (*str == "") { + *f = BCLog::ALL; + return true; + } + for (unsigned int i = 0; i < ARRAYLEN(LogCategories); i++) { + if (LogCategories[i].category == *str) { + *f = LogCategories[i].flag; + return true; + } + } + } + return false; +} + +std::string ListLogCategories() +{ + std::string ret; + int outcount = 0; + for (unsigned int i = 0; i < ARRAYLEN(LogCategories); i++) { + // Omit the special cases. + if (LogCategories[i].flag != BCLog::NONE && LogCategories[i].flag != BCLog::ALL) { + if (outcount != 0) ret += ", "; + ret += LogCategories[i].category; + outcount++; + } + } + return ret; +} + +std::vector<CLogCategoryActive> ListActiveLogCategories() +{ + std::vector<CLogCategoryActive> ret; + for (unsigned int i = 0; i < ARRAYLEN(LogCategories); i++) { + // Omit the special cases. + if (LogCategories[i].flag != BCLog::NONE && LogCategories[i].flag != BCLog::ALL) { + CLogCategoryActive catActive; + catActive.category = LogCategories[i].category; + catActive.active = LogAcceptCategory(LogCategories[i].flag); + ret.push_back(catActive); + } + } + return ret; +} + +/** + * fStartedNewLine is a state variable held by the calling context that will + * suppress printing of the timestamp when multiple calls are made that don't + * end in a newline. Initialize it to true, and hold it, in the calling context. + */ +static std::string LogTimestampStr(const std::string &str, std::atomic_bool *fStartedNewLine) +{ + std::string strStamped; + + if (!fLogTimestamps) + return str; + + if (*fStartedNewLine) { + int64_t nTimeMicros = GetTimeMicros(); + strStamped = FormatISO8601DateTime(nTimeMicros/1000000); + if (fLogTimeMicros) { + strStamped.pop_back(); + strStamped += strprintf(".%06dZ", nTimeMicros%1000000); + } + int64_t mocktime = GetMockTime(); + if (mocktime) { + strStamped += " (mocktime: " + FormatISO8601DateTime(mocktime) + ")"; + } + strStamped += ' ' + str; + } else + strStamped = str; + + if (!str.empty() && str[str.size()-1] == '\n') + *fStartedNewLine = true; + else + *fStartedNewLine = false; + + return strStamped; +} + +int LogPrintStr(const std::string &str) +{ + int ret = 0; // Returns total number of characters written + static std::atomic_bool fStartedNewLine(true); + + std::string strTimestamped = LogTimestampStr(str, &fStartedNewLine); + + if (fPrintToConsole) { + // print to console + ret = fwrite(strTimestamped.data(), 1, strTimestamped.size(), stdout); + fflush(stdout); + } + if (fPrintToDebugLog) { + std::call_once(debugPrintInitFlag, &DebugPrintInit); + std::lock_guard<std::mutex> scoped_lock(*mutexDebugLog); + + // buffer if we haven't opened the log yet + if (fileout == nullptr) { + assert(vMsgsBeforeOpenLog); + ret = strTimestamped.length(); + vMsgsBeforeOpenLog->push_back(strTimestamped); + } + else + { + // reopen the log file, if requested + if (fReopenDebugLog) { + fReopenDebugLog = false; + fs::path pathDebug = GetDebugLogPath(); + if (fsbridge::freopen(pathDebug,"a",fileout) != nullptr) + setbuf(fileout, nullptr); // unbuffered + } + + ret = FileWriteStr(strTimestamped, fileout); + } + } + return ret; +} + +void ShrinkDebugFile() +{ + // Amount of debug.log to save at end when shrinking (must fit in memory) + constexpr size_t RECENT_DEBUG_HISTORY_SIZE = 10 * 1000000; + // Scroll debug.log if it's getting too big + fs::path pathLog = GetDebugLogPath(); + FILE* file = fsbridge::fopen(pathLog, "r"); + + // Special files (e.g. device nodes) may not have a size. + size_t log_size = 0; + try { + log_size = fs::file_size(pathLog); + } catch (boost::filesystem::filesystem_error &) {} + + // If debug.log file is more than 10% bigger the RECENT_DEBUG_HISTORY_SIZE + // trim it down by saving only the last RECENT_DEBUG_HISTORY_SIZE bytes + if (file && log_size > 11 * (RECENT_DEBUG_HISTORY_SIZE / 10)) + { + // Restart the file with some of the end + std::vector<char> vch(RECENT_DEBUG_HISTORY_SIZE, 0); + fseek(file, -((long)vch.size()), SEEK_END); + int nBytes = fread(vch.data(), 1, vch.size(), file); + fclose(file); + + file = fsbridge::fopen(pathLog, "w"); + if (file) + { + fwrite(vch.data(), 1, nBytes, file); + fclose(file); + } + } + else if (file != nullptr) + fclose(file); +} diff --git a/src/logging.h b/src/logging.h new file mode 100644 index 0000000000..4053f75acf --- /dev/null +++ b/src/logging.h @@ -0,0 +1,125 @@ +// Copyright (c) 2009-2010 Satoshi Nakamoto +// Copyright (c) 2009-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. + +#ifndef BITCOIN_LOGGING_H +#define BITCOIN_LOGGING_H + +#include <fs.h> +#include <tinyformat.h> + +#include <atomic> +#include <cstdint> +#include <string> +#include <vector> + +static const bool DEFAULT_LOGTIMEMICROS = false; +static const bool DEFAULT_LOGIPS = false; +static const bool DEFAULT_LOGTIMESTAMPS = true; +extern const char * const DEFAULT_DEBUGLOGFILE; + +extern bool fPrintToConsole; +extern bool fPrintToDebugLog; + +extern bool fLogTimestamps; +extern bool fLogTimeMicros; +extern bool fLogIPs; +extern std::atomic<bool> fReopenDebugLog; + +extern std::atomic<uint32_t> logCategories; + +struct CLogCategoryActive +{ + std::string category; + bool active; +}; + +namespace BCLog { + enum LogFlags : uint32_t { + NONE = 0, + NET = (1 << 0), + TOR = (1 << 1), + MEMPOOL = (1 << 2), + HTTP = (1 << 3), + BENCH = (1 << 4), + ZMQ = (1 << 5), + DB = (1 << 6), + RPC = (1 << 7), + ESTIMATEFEE = (1 << 8), + ADDRMAN = (1 << 9), + SELECTCOINS = (1 << 10), + REINDEX = (1 << 11), + CMPCTBLOCK = (1 << 12), + RAND = (1 << 13), + PRUNE = (1 << 14), + PROXY = (1 << 15), + MEMPOOLREJ = (1 << 16), + LIBEVENT = (1 << 17), + COINDB = (1 << 18), + QT = (1 << 19), + LEVELDB = (1 << 20), + ALL = ~(uint32_t)0, + }; +} +/** Return true if log accepts specified category */ +static inline bool LogAcceptCategory(uint32_t category) +{ + return (logCategories.load(std::memory_order_relaxed) & category) != 0; +} + +/** Returns a string with the log categories. */ +std::string ListLogCategories(); + +/** Returns a vector of the active log categories. */ +std::vector<CLogCategoryActive> ListActiveLogCategories(); + +/** Return true if str parses as a log category and set the flags in f */ +bool GetLogCategory(uint32_t *f, const std::string *str); + +/** Send a string to the log output */ +int LogPrintStr(const std::string &str); + +/** Get format string from VA_ARGS for error reporting */ +template<typename... Args> std::string FormatStringFromLogArgs(const char *fmt, const Args&... args) { return fmt; } + +static inline void MarkUsed() {} +template<typename T, typename... Args> static inline void MarkUsed(const T& t, const Args&... args) +{ + (void)t; + MarkUsed(args...); +} + +// Be conservative when using LogPrintf/error or other things which +// unconditionally log to debug.log! It should not be the case that an inbound +// peer can fill up a user's disk with debug.log entries. + +#ifdef USE_COVERAGE +#define LogPrintf(...) do { MarkUsed(__VA_ARGS__); } while(0) +#define LogPrint(category, ...) do { MarkUsed(__VA_ARGS__); } while(0) +#else +#define LogPrintf(...) do { \ + if (fPrintToConsole || fPrintToDebugLog) { \ + std::string _log_msg_; /* Unlikely name to avoid shadowing variables */ \ + try { \ + _log_msg_ = tfm::format(__VA_ARGS__); \ + } catch (tinyformat::format_error &fmterr) { \ + /* Original format string will have newline so don't add one here */ \ + _log_msg_ = "Error \"" + std::string(fmterr.what()) + "\" while formatting log message: " + FormatStringFromLogArgs(__VA_ARGS__); \ + } \ + LogPrintStr(_log_msg_); \ + } \ +} while(0) + +#define LogPrint(category, ...) do { \ + if (LogAcceptCategory((category))) { \ + LogPrintf(__VA_ARGS__); \ + } \ +} while(0) +#endif + +fs::path GetDebugLogPath(); +bool OpenDebugLog(); +void ShrinkDebugFile(); + +#endif // BITCOIN_LOGGING_H diff --git a/src/net.cpp b/src/net.cpp index 342dfbaeb9..cd076c1ce2 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -124,7 +124,7 @@ bool GetLocal(CService& addr, const CNetAddr *paddrPeer) return nBestScore >= 0; } -//! Convert the pnSeeds6 array into usable address objects. +//! Convert the pnSeed6 array into usable address objects. static std::vector<CAddress> convertSeed6(const std::vector<SeedSpec6> &vSeedsIn) { // It'll only connect to one or two seed nodes because once it connects, @@ -368,7 +368,7 @@ static CAddress GetBindAddress(SOCKET sock) return addr_bind; } -CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure) +CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, bool manual_connection) { if (pszDest == nullptr) { if (IsLocal(addrConnect)) @@ -432,7 +432,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo if (hSocket == INVALID_SOCKET) { return nullptr; } - connected = ConnectSocketDirectly(addrConnect, hSocket, nConnectTimeout); + connected = ConnectSocketDirectly(addrConnect, hSocket, nConnectTimeout, manual_connection); } if (!proxyConnectionFailed) { // If a connection to the node was attempted, and failure (if any) is not caused by a problem connecting to @@ -1923,23 +1923,25 @@ std::vector<AddedNodeInfo> CConnman::GetAddedNodeInfo() for (const std::string& strAddNode : lAddresses) { CService service(LookupNumeric(strAddNode.c_str(), Params().GetDefaultPort())); + AddedNodeInfo addedNode{strAddNode, CService(), false, false}; if (service.IsValid()) { // strAddNode is an IP:port auto it = mapConnected.find(service); if (it != mapConnected.end()) { - ret.push_back(AddedNodeInfo{strAddNode, service, true, it->second}); - } else { - ret.push_back(AddedNodeInfo{strAddNode, CService(), false, false}); + addedNode.resolvedAddress = service; + addedNode.fConnected = true; + addedNode.fInbound = it->second; } } else { // strAddNode is a name auto it = mapConnectedByName.find(strAddNode); if (it != mapConnectedByName.end()) { - ret.push_back(AddedNodeInfo{strAddNode, it->second.second, true, it->second.first}); - } else { - ret.push_back(AddedNodeInfo{strAddNode, CService(), false, false}); + addedNode.resolvedAddress = it->second.second; + addedNode.fConnected = true; + addedNode.fInbound = it->second.first; } } + ret.emplace_back(std::move(addedNode)); } return ret; @@ -1992,7 +1994,7 @@ void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai } else if (FindNode(std::string(pszDest))) return; - CNode* pnode = ConnectNode(addrConnect, pszDest, fCountFailure); + CNode* pnode = ConnectNode(addrConnect, pszDest, fCountFailure, manual_connection); if (!pnode) return; @@ -2088,23 +2090,16 @@ bool CConnman::BindListenPort(const CService &addrBind, std::string& strError, b LogPrintf("%s\n", strError); return false; } -#ifndef WIN32 + // Allow binding if the port is still in TIME_WAIT state after // the program was closed and restarted. - setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (void*)&nOne, sizeof(int)); -#else - setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&nOne, sizeof(int)); -#endif + setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (sockopt_arg_type)&nOne, sizeof(int)); // some systems don't have IPV6_V6ONLY but are always v6only; others do have the option // and enable it by default or not. Try to enable it, if possible. if (addrBind.IsIPv6()) { #ifdef IPV6_V6ONLY -#ifdef WIN32 - setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (const char*)&nOne, sizeof(int)); -#else - setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (void*)&nOne, sizeof(int)); -#endif + setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_V6ONLY, (sockopt_arg_type)&nOne, sizeof(int)); #endif #ifdef WIN32 int nProtLevel = PROTECTION_LEVEL_UNRESTRICTED; @@ -338,7 +338,7 @@ private: CNode* FindNode(const CService& addr); bool AttemptToEvictConnection(); - CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure); + CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, bool manual_connection); bool IsWhitelistedRange(const CNetAddr &addr); void DeleteNode(CNode* pnode); diff --git a/src/net_processing.cpp b/src/net_processing.cpp index dbdae705de..ee4e9e61bc 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -1100,8 +1100,10 @@ void static ProcessGetBlockData(CNode* pfrom, const Consensus::Params& consensus } } // release cs_main before calling ActivateBestChain if (need_activate_chain) { - CValidationState dummy; - ActivateBestChain(dummy, Params(), a_recent_block); + CValidationState state; + if (!ActivateBestChain(state, Params(), a_recent_block)) { + LogPrint(BCLog::NET, "failed to activate chain (%s)\n", FormatStateMessage(state)); + } } LOCK(cs_main); @@ -1992,8 +1994,10 @@ bool static ProcessMessage(CNode* pfrom, const std::string& strCommand, CDataStr LOCK(cs_most_recent_block); a_recent_block = most_recent_block; } - CValidationState dummy; - ActivateBestChain(dummy, Params(), a_recent_block); + CValidationState state; + if (!ActivateBestChain(state, Params(), a_recent_block)) { + LogPrint(BCLog::NET, "failed to activate chain (%s)\n", FormatStateMessage(state)); + } } LOCK(cs_main); diff --git a/src/netaddress.cpp b/src/netaddress.cpp index 4f231d73c8..18d5948f85 100644 --- a/src/netaddress.cpp +++ b/src/netaddress.cpp @@ -14,7 +14,7 @@ static const unsigned char pchOnionCat[] = {0xFD,0x87,0xD8,0x7E,0xEB,0x43}; // 0xFD + sha256("bitcoin")[0:5] static const unsigned char g_internal_prefix[] = { 0xFD, 0x6B, 0x88, 0xC0, 0x87, 0x24 }; -void CNetAddr::Init() +CNetAddr::CNetAddr() { memset(ip, 0, sizeof(ip)); scopeId = 0; @@ -67,11 +67,6 @@ bool CNetAddr::SetSpecial(const std::string &strName) return false; } -CNetAddr::CNetAddr() -{ - Init(); -} - CNetAddr::CNetAddr(const struct in_addr& ipv4Addr) { SetRaw(NET_IPV4, (const uint8_t*)&ipv4Addr); @@ -290,11 +285,6 @@ bool operator==(const CNetAddr& a, const CNetAddr& b) return (memcmp(a.ip, b.ip, 16) == 0); } -bool operator!=(const CNetAddr& a, const CNetAddr& b) -{ - return (memcmp(a.ip, b.ip, 16) != 0); -} - bool operator<(const CNetAddr& a, const CNetAddr& b) { return (memcmp(a.ip, b.ip, 16) < 0); @@ -469,14 +459,8 @@ int CNetAddr::GetReachabilityFrom(const CNetAddr *paddrPartner) const } } -void CService::Init() +CService::CService() : port(0) { - port = 0; -} - -CService::CService() -{ - Init(); } CService::CService(const CNetAddr& cip, unsigned short portIn) : CNetAddr(cip), port(portIn) @@ -525,11 +509,6 @@ bool operator==(const CService& a, const CService& b) return static_cast<CNetAddr>(a) == static_cast<CNetAddr>(b) && a.port == b.port; } -bool operator!=(const CService& a, const CService& b) -{ - return static_cast<CNetAddr>(a) != static_cast<CNetAddr>(b) || a.port != b.port; -} - bool operator<(const CService& a, const CService& b) { return static_cast<CNetAddr>(a) < static_cast<CNetAddr>(b) || (static_cast<CNetAddr>(a) == static_cast<CNetAddr>(b) && a.port < b.port); @@ -663,16 +642,16 @@ bool CSubNet::Match(const CNetAddr &addr) const static inline int NetmaskBits(uint8_t x) { switch(x) { - case 0x00: return 0; break; - case 0x80: return 1; break; - case 0xc0: return 2; break; - case 0xe0: return 3; break; - case 0xf0: return 4; break; - case 0xf8: return 5; break; - case 0xfc: return 6; break; - case 0xfe: return 7; break; - case 0xff: return 8; break; - default: return -1; break; + case 0x00: return 0; + case 0x80: return 1; + case 0xc0: return 2; + case 0xe0: return 3; + case 0xf0: return 4; + case 0xf8: return 5; + case 0xfc: return 6; + case 0xfe: return 7; + case 0xff: return 8; + default: return -1; } } @@ -724,11 +703,6 @@ bool operator==(const CSubNet& a, const CSubNet& b) return a.valid == b.valid && a.network == b.network && !memcmp(a.netmask, b.netmask, 16); } -bool operator!=(const CSubNet& a, const CSubNet& b) -{ - return !(a==b); -} - bool operator<(const CSubNet& a, const CSubNet& b) { return (a.network < b.network || (a.network == b.network && memcmp(a.netmask, b.netmask, 16) < 0)); diff --git a/src/netaddress.h b/src/netaddress.h index 38f8709257..f8f2ab99ff 100644 --- a/src/netaddress.h +++ b/src/netaddress.h @@ -38,15 +38,16 @@ class CNetAddr public: CNetAddr(); explicit CNetAddr(const struct in_addr& ipv4Addr); - void Init(); void SetIP(const CNetAddr& ip); + private: /** * Set raw IPv4 or IPv6 address (in network byte order) * @note Only NET_IPV4 and NET_IPV6 are allowed for network. */ void SetRaw(Network network, const uint8_t *data); + public: /** * Transform an arbitrary string into a non-routable ipv6 address. * Useful for mapping resolved addresses back to their source. @@ -87,7 +88,7 @@ class CNetAddr bool GetIn6Addr(struct in6_addr* pipv6Addr) const; friend bool operator==(const CNetAddr& a, const CNetAddr& b); - friend bool operator!=(const CNetAddr& a, const CNetAddr& b); + friend bool operator!=(const CNetAddr& a, const CNetAddr& b) { return !(a == b); } friend bool operator<(const CNetAddr& a, const CNetAddr& b); ADD_SERIALIZE_METHODS; @@ -124,7 +125,7 @@ class CSubNet bool IsValid() const; friend bool operator==(const CSubNet& a, const CSubNet& b); - friend bool operator!=(const CSubNet& a, const CSubNet& b); + friend bool operator!=(const CSubNet& a, const CSubNet& b) { return !(a == b); } friend bool operator<(const CSubNet& a, const CSubNet& b); ADD_SERIALIZE_METHODS; @@ -148,12 +149,11 @@ class CService : public CNetAddr CService(const CNetAddr& ip, unsigned short port); CService(const struct in_addr& ipv4Addr, unsigned short port); explicit CService(const struct sockaddr_in& addr); - void Init(); unsigned short GetPort() const; bool GetSockAddr(struct sockaddr* paddr, socklen_t *addrlen) const; bool SetSockAddr(const struct sockaddr* paddr); friend bool operator==(const CService& a, const CService& b); - friend bool operator!=(const CService& a, const CService& b); + friend bool operator!=(const CService& a, const CService& b) { return !(a == b); } friend bool operator<(const CService& a, const CService& b); std::vector<unsigned char> GetKey() const; std::string ToString() const; diff --git a/src/netbase.cpp b/src/netbase.cpp index 92ac1c4c85..57835b5427 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -9,6 +9,7 @@ #include <sync.h> #include <uint256.h> #include <random.h> +#include <tinyformat.h> #include <util.h> #include <utilstrencodings.h> @@ -468,7 +469,17 @@ SOCKET CreateSocket(const CService &addrConnect) return hSocket; } -bool ConnectSocketDirectly(const CService &addrConnect, const SOCKET& hSocket, int nTimeout) +template<typename... Args> +static void LogConnectFailure(bool manual_connection, const char* fmt, const Args&... args) { + std::string error_message = tfm::format(fmt, args...); + if (manual_connection) { + LogPrintf("%s\n", error_message); + } else { + LogPrint(BCLog::NET, "%s\n", error_message); + } +} + +bool ConnectSocketDirectly(const CService &addrConnect, const SOCKET& hSocket, int nTimeout, bool manual_connection) { struct sockaddr_storage sockaddr; socklen_t len = sizeof(sockaddr); @@ -502,18 +513,14 @@ bool ConnectSocketDirectly(const CService &addrConnect, const SOCKET& hSocket, i return false; } socklen_t nRetSize = sizeof(nRet); -#ifdef WIN32 - if (getsockopt(hSocket, SOL_SOCKET, SO_ERROR, (char*)(&nRet), &nRetSize) == SOCKET_ERROR) -#else - if (getsockopt(hSocket, SOL_SOCKET, SO_ERROR, &nRet, &nRetSize) == SOCKET_ERROR) -#endif + if (getsockopt(hSocket, SOL_SOCKET, SO_ERROR, (sockopt_arg_type)&nRet, &nRetSize) == SOCKET_ERROR) { LogPrintf("getsockopt() for %s failed: %s\n", addrConnect.ToString(), NetworkErrorString(WSAGetLastError())); return false; } if (nRet != 0) { - LogPrintf("connect() to %s failed after select(): %s\n", addrConnect.ToString(), NetworkErrorString(nRet)); + LogConnectFailure(manual_connection, "connect() to %s failed after select(): %s", addrConnect.ToString(), NetworkErrorString(nRet)); return false; } } @@ -523,7 +530,7 @@ bool ConnectSocketDirectly(const CService &addrConnect, const SOCKET& hSocket, i else #endif { - LogPrintf("connect() to %s failed: %s\n", addrConnect.ToString(), NetworkErrorString(WSAGetLastError())); + LogConnectFailure(manual_connection, "connect() to %s failed: %s", addrConnect.ToString(), NetworkErrorString(WSAGetLastError())); return false; } } @@ -581,7 +588,7 @@ bool IsProxy(const CNetAddr &addr) { bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int port, const SOCKET& hSocket, int nTimeout, bool *outProxyConnectionFailed) { // first connect to proxy server - if (!ConnectSocketDirectly(proxy.proxy, hSocket, nTimeout)) { + if (!ConnectSocketDirectly(proxy.proxy, hSocket, nTimeout, true)) { if (outProxyConnectionFailed) *outProxyConnectionFailed = true; return false; @@ -601,6 +608,7 @@ bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int } return true; } + bool LookupSubNet(const char* pszName, CSubNet& ret) { std::string strSubnet(pszName); diff --git a/src/netbase.h b/src/netbase.h index c0921b6441..50d4bc54fa 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -52,7 +52,7 @@ bool Lookup(const char *pszName, std::vector<CService>& vAddr, int portDefault, CService LookupNumeric(const char *pszName, int portDefault = 0); bool LookupSubNet(const char *pszName, CSubNet& subnet); SOCKET CreateSocket(const CService &addrConnect); -bool ConnectSocketDirectly(const CService &addrConnect, const SOCKET& hSocketRet, int nTimeout); +bool ConnectSocketDirectly(const CService &addrConnect, const SOCKET& hSocketRet, int nTimeout, bool manual_connection); bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int port, const SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed); /** Return readable error string for a network error code */ std::string NetworkErrorString(int err); diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index bfa8844a09..aed5374a7d 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -968,6 +968,11 @@ void BitcoinGUI::changeEvent(QEvent *e) QTimer::singleShot(0, this, SLOT(hide())); e->ignore(); } + else if((wsevt->oldState() & Qt::WindowMinimized) && !isMinimized()) + { + QTimer::singleShot(0, this, SLOT(show())); + e->ignore(); + } } } #endif diff --git a/src/qt/test/wallettests.cpp b/src/qt/test/wallettests.cpp index dcc834c352..56d2d38194 100644 --- a/src/qt/test/wallettests.cpp +++ b/src/qt/test/wallettests.cpp @@ -180,9 +180,9 @@ void TestGUI() TransactionView transactionView(platformStyle.get()); auto node = interfaces::MakeNode(); OptionsModel optionsModel(*node); - vpwallets.insert(vpwallets.begin(), &wallet); - WalletModel walletModel(std::move(node->getWallets()[0]), *node, platformStyle.get(), &optionsModel); - vpwallets.erase(vpwallets.begin()); + AddWallet(&wallet); + WalletModel walletModel(std::move(node->getWallets().back()), *node, platformStyle.get(), &optionsModel); + RemoveWallet(&wallet); sendCoinsDialog.setModel(&walletModel); transactionView.setModel(&walletModel); diff --git a/src/qt/walletview.cpp b/src/qt/walletview.cpp index 8b9b85c8c9..d529595dec 100644 --- a/src/qt/walletview.cpp +++ b/src/qt/walletview.cpp @@ -315,9 +315,9 @@ void WalletView::showProgress(const QString &title, int nProgress) progressDialog = new QProgressDialog(title, "", 0, 100); progressDialog->setWindowModality(Qt::ApplicationModal); progressDialog->setMinimumDuration(0); - progressDialog->setCancelButton(0); progressDialog->setAutoClose(false); progressDialog->setValue(0); + progressDialog->setCancelButtonText(tr("Cancel")); } else if (nProgress == 100) { @@ -327,8 +327,13 @@ void WalletView::showProgress(const QString &title, int nProgress) progressDialog->deleteLater(); } } - else if (progressDialog) - progressDialog->setValue(nProgress); + else if (progressDialog) { + if (progressDialog->wasCanceled()) { + getWalletModel()->wallet().abortRescan(); + } else { + progressDialog->setValue(nProgress); + } + } } void WalletView::requestedSyncWarningInfo() diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 9a7c4b8e6d..b4bb78e689 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -116,7 +116,7 @@ UniValue generateBlocks(std::shared_ptr<CReserveScript> coinbaseScript, int nGen } unsigned int nExtraNonce = 0; UniValue blockHashes(UniValue::VARR); - while (nHeight < nHeightEnd) + while (nHeight < nHeightEnd && !ShutdownRequested()) { std::unique_ptr<CBlockTemplate> pblocktemplate(BlockAssembler(Params()).CreateNewBlock(coinbaseScript->reserveScript)); if (!pblocktemplate.get()) diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 49e865a64a..6754407dbd 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -69,7 +69,7 @@ UniValue validateaddress(const JSONRPCRequest& request) { #ifdef ENABLE_WALLET - if (!::vpwallets.empty() && IsDeprecatedRPCEnabled("validateaddress")) { + if (HasWallets() && IsDeprecatedRPCEnabled("validateaddress")) { ret.pushKVs(getaddressinfo(request)); } #endif diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 543e4fc358..7bdf09812b 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -94,6 +94,7 @@ UniValue getrawtransaction(const JSONRPCRequest& request) " \"hash\" : \"id\", (string) The transaction hash (differs from txid for witness transactions)\n" " \"size\" : n, (numeric) The serialized transaction size\n" " \"vsize\" : n, (numeric) The virtual transaction size (differs from size for witness transactions)\n" + " \"weight\" : n, (numeric) The transaction's weight (between vsize*4-3 and vsize*4)\n" " \"version\" : n, (numeric) The version\n" " \"locktime\" : ttt, (numeric) The lock time\n" " \"vin\" : [ (array of json objects)\n" @@ -494,6 +495,7 @@ UniValue decoderawtransaction(const JSONRPCRequest& request) " \"hash\" : \"id\", (string) The transaction hash (differs from txid for witness transactions)\n" " \"size\" : n, (numeric) The transaction size\n" " \"vsize\" : n, (numeric) The virtual transaction size (differs from size for witness transactions)\n" + " \"weight\" : n, (numeric) The transaction's weight (between vsize*4 - 3 and vsize*4)\n" " \"version\" : n, (numeric) The version\n" " \"locktime\" : ttt, (numeric) The lock time\n" " \"vin\" : [ (array of json objects)\n" diff --git a/src/test/blockencodings_tests.cpp b/src/test/blockencodings_tests.cpp index 32330e0548..8cffacbffe 100644 --- a/src/test/blockencodings_tests.cpp +++ b/src/test/blockencodings_tests.cpp @@ -52,8 +52,8 @@ static CBlock BuildBlockTestCase() { } // Number of shared use_counts we expect for a tx we haven't touched -// == 2 (mempool + our copy from the GetSharedTx call) -#define SHARED_TX_OFFSET 2 +// (block + mempool + our copy from the GetSharedTx call) +constexpr long SHARED_TX_OFFSET{3}; BOOST_AUTO_TEST_CASE(SimpleRoundTripTest) { @@ -61,7 +61,7 @@ BOOST_AUTO_TEST_CASE(SimpleRoundTripTest) TestMemPoolEntryHelper entry; CBlock block(BuildBlockTestCase()); - pool.addUnchecked(block.vtx[2]->GetHash(), entry.FromTx(*block.vtx[2])); + pool.addUnchecked(block.vtx[2]->GetHash(), entry.FromTx(block.vtx[2])); LOCK(pool.cs); BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0); @@ -161,7 +161,7 @@ BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest) TestMemPoolEntryHelper entry; CBlock block(BuildBlockTestCase()); - pool.addUnchecked(block.vtx[2]->GetHash(), entry.FromTx(*block.vtx[2])); + pool.addUnchecked(block.vtx[2]->GetHash(), entry.FromTx(block.vtx[2])); LOCK(pool.cs); BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0); @@ -188,7 +188,7 @@ BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest) BOOST_CHECK( partialBlock.IsTxAvailable(1)); BOOST_CHECK( partialBlock.IsTxAvailable(2)); - BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1); + BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1); // +1 because of partialBlock CBlock block2; { @@ -203,6 +203,7 @@ BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest) partialBlock.FillBlock(block2, {block.vtx[1]}); // Current implementation doesn't check txn here, but don't require that partialBlock = tmp; } + BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 2); // +2 because of partialBlock and block2 bool mutated; BOOST_CHECK(block.hashMerkleRoot != BlockMerkleRoot(block2, &mutated)); @@ -213,13 +214,15 @@ BOOST_AUTO_TEST_CASE(NonCoinbasePreforwardRTTest) BOOST_CHECK_EQUAL(block.hashMerkleRoot.ToString(), BlockMerkleRoot(block3, &mutated).ToString()); BOOST_CHECK(!mutated); + BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[2]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 3); // +2 because of partialBlock and block2 and block3 + txhash = block.vtx[2]->GetHash(); block.vtx.clear(); block2.vtx.clear(); block3.vtx.clear(); - BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1); // + 1 because of partialBlockCopy. + BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1 - 1); // + 1 because of partialBlock; -1 because of block. } - BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0); + BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET - 1); // -1 because of block } BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest) @@ -228,7 +231,7 @@ BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest) TestMemPoolEntryHelper entry; CBlock block(BuildBlockTestCase()); - pool.addUnchecked(block.vtx[1]->GetHash(), entry.FromTx(*block.vtx[1])); + pool.addUnchecked(block.vtx[1]->GetHash(), entry.FromTx(block.vtx[1])); LOCK(pool.cs); BOOST_CHECK_EQUAL(pool.mapTx.find(block.vtx[1]->GetHash())->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0); @@ -268,9 +271,9 @@ BOOST_AUTO_TEST_CASE(SufficientPreforwardRTTest) txhash = block.vtx[1]->GetHash(); block.vtx.clear(); block2.vtx.clear(); - BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1); // + 1 because of partialBlockCopy. + BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET + 1 - 1); // + 1 because of partialBlock; -1 because of block. } - BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET + 0); + BOOST_CHECK_EQUAL(pool.mapTx.find(txhash)->GetSharedTx().use_count(), SHARED_TX_OFFSET - 1); // -1 because of block } BOOST_AUTO_TEST_CASE(EmptyBlockRoundTripTest) diff --git a/src/test/multisig_tests.cpp b/src/test/multisig_tests.cpp index b593f9633c..066f6328a6 100644 --- a/src/test/multisig_tests.cpp +++ b/src/test/multisig_tests.cpp @@ -19,7 +19,7 @@ BOOST_FIXTURE_TEST_SUITE(multisig_tests, BasicTestingSetup) CScript -sign_multisig(CScript scriptPubKey, std::vector<CKey> keys, CTransaction transaction, int whichIn) +sign_multisig(const CScript& scriptPubKey, const std::vector<CKey>& keys, const CTransaction& transaction, int whichIn) { uint256 hash = SignatureHash(scriptPubKey, transaction, whichIn, SIGHASH_ALL, 0, SigVersion::BASE); diff --git a/src/test/script_tests.cpp b/src/test/script_tests.cpp index c26a6a88e8..068f1e66f4 100644 --- a/src/test/script_tests.cpp +++ b/src/test/script_tests.cpp @@ -1029,7 +1029,7 @@ BOOST_AUTO_TEST_CASE(script_PushData) } CScript -sign_multisig(CScript scriptPubKey, std::vector<CKey> keys, CTransaction transaction) +sign_multisig(const CScript& scriptPubKey, const std::vector<CKey>& keys, const CTransaction& transaction) { uint256 hash = SignatureHash(scriptPubKey, transaction, 0, SIGHASH_ALL, 0, SigVersion::BASE); @@ -1053,7 +1053,7 @@ sign_multisig(CScript scriptPubKey, std::vector<CKey> keys, CTransaction transac return result; } CScript -sign_multisig(CScript scriptPubKey, const CKey &key, CTransaction transaction) +sign_multisig(const CScript& scriptPubKey, const CKey& key, const CTransaction& transaction) { std::vector<CKey> keys; keys.push_back(key); diff --git a/src/test/serialize_tests.cpp b/src/test/serialize_tests.cpp index 94164346f3..eba58e0042 100644 --- a/src/test/serialize_tests.cpp +++ b/src/test/serialize_tests.cpp @@ -23,7 +23,7 @@ protected: CTransactionRef txval; public: CSerializeMethodsTestSingle() = default; - CSerializeMethodsTestSingle(int intvalin, bool boolvalin, std::string stringvalin, const char* charstrvalin, CTransaction txvalin) : intval(intvalin), boolval(boolvalin), stringval(std::move(stringvalin)), txval(MakeTransactionRef(txvalin)) + CSerializeMethodsTestSingle(int intvalin, bool boolvalin, std::string stringvalin, const char* charstrvalin, const CTransactionRef& txvalin) : intval(intvalin), boolval(boolvalin), stringval(std::move(stringvalin)), txval(txvalin) { memcpy(charstrval, charstrvalin, sizeof(charstrval)); } @@ -350,8 +350,9 @@ BOOST_AUTO_TEST_CASE(class_methods) std::string stringval("testing"); const char charstrval[16] = "testing charstr"; CMutableTransaction txval; - CSerializeMethodsTestSingle methodtest1(intval, boolval, stringval, charstrval, txval); - CSerializeMethodsTestMany methodtest2(intval, boolval, stringval, charstrval, txval); + CTransactionRef tx_ref{MakeTransactionRef(txval)}; + CSerializeMethodsTestSingle methodtest1(intval, boolval, stringval, charstrval, tx_ref); + CSerializeMethodsTestMany methodtest2(intval, boolval, stringval, charstrval, tx_ref); CSerializeMethodsTestSingle methodtest3; CSerializeMethodsTestMany methodtest4; CDataStream ss(SER_DISK, PROTOCOL_VERSION); diff --git a/src/test/test_bitcoin.cpp b/src/test/test_bitcoin.cpp index ff20d4b3d7..e9873f4526 100644 --- a/src/test/test_bitcoin.cpp +++ b/src/test/test_bitcoin.cpp @@ -85,7 +85,7 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha { CValidationState state; if (!ActivateBestChain(state, chainparams)) { - throw std::runtime_error("ActivateBestChain failed."); + throw std::runtime_error(strprintf("ActivateBestChain failed. (%s)", FormatStateMessage(state))); } } nScriptCheckThreads = 3; @@ -123,7 +123,7 @@ TestChain100Setup::TestChain100Setup() : TestingSetup(CBaseChainParams::REGTEST) { std::vector<CMutableTransaction> noTxns; CBlock b = CreateAndProcessBlock(noTxns, scriptPubKey); - coinbaseTxns.push_back(*b.vtx[0]); + m_coinbase_txns.push_back(b.vtx[0]); } } @@ -164,12 +164,12 @@ TestChain100Setup::~TestChain100Setup() CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CMutableTransaction &tx) { - CTransaction txn(tx); - return FromTx(txn); + return FromTx(MakeTransactionRef(tx)); } -CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CTransaction &txn) { - return CTxMemPoolEntry(MakeTransactionRef(txn), nFee, nTime, nHeight, +CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CTransactionRef& tx) +{ + return CTxMemPoolEntry(tx, nFee, nTime, nHeight, spendsCoinbase, sigOpCost, lp); } diff --git a/src/test/test_bitcoin.h b/src/test/test_bitcoin.h index 8136da3aa9..1f91eb622c 100644 --- a/src/test/test_bitcoin.h +++ b/src/test/test_bitcoin.h @@ -87,7 +87,7 @@ struct TestChain100Setup : public TestingSetup { ~TestChain100Setup(); - std::vector<CTransaction> coinbaseTxns; // For convenience, coinbase transactions + std::vector<CTransactionRef> m_coinbase_txns; // For convenience, coinbase transactions CKey coinbaseKey; // private/public key needed to spend coinbase transactions }; @@ -107,8 +107,8 @@ struct TestMemPoolEntryHelper nFee(0), nTime(0), nHeight(1), spendsCoinbase(false), sigOpCost(4) { } - CTxMemPoolEntry FromTx(const CMutableTransaction &tx); - CTxMemPoolEntry FromTx(const CTransaction &tx); + CTxMemPoolEntry FromTx(const CMutableTransaction& tx); + CTxMemPoolEntry FromTx(const CTransactionRef& tx); // Change the default value TestMemPoolEntryHelper &Fee(CAmount _fee) { nFee = _fee; return *this; } diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp index 71f3d727b1..7a52697859 100644 --- a/src/test/txvalidationcache_tests.cpp +++ b/src/test/txvalidationcache_tests.cpp @@ -48,7 +48,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup) { spends[i].nVersion = 1; spends[i].vin.resize(1); - spends[i].vin[0].prevout.hash = coinbaseTxns[0].GetHash(); + spends[i].vin[0].prevout.hash = m_coinbase_txns[0]->GetHash(); spends[i].vin[0].prevout.n = 0; spends[i].vout.resize(1); spends[i].vout[0].nValue = 11*CENT; @@ -167,7 +167,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup) spend_tx.nVersion = 1; spend_tx.vin.resize(1); - spend_tx.vin[0].prevout.hash = coinbaseTxns[0].GetHash(); + spend_tx.vin[0].prevout.hash = m_coinbase_txns[0]->GetHash(); spend_tx.vin[0].prevout.n = 0; spend_tx.vout.resize(4); spend_tx.vout[0].nValue = 11*CENT; diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index d41c43a795..1c3acfb1a5 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -159,17 +159,6 @@ BOOST_AUTO_TEST_CASE(util_HexStr) } -BOOST_AUTO_TEST_CASE(util_DateTimeStrFormat) -{ - BOOST_CHECK_EQUAL(DateTimeStrFormat("%Y-%m-%d %H:%M:%S", 0), "1970-01-01 00:00:00"); - BOOST_CHECK_EQUAL(DateTimeStrFormat("%Y-%m-%d %H:%M:%S", 0x7FFFFFFF), "2038-01-19 03:14:07"); - BOOST_CHECK_EQUAL(DateTimeStrFormat("%Y-%m-%d %H:%M:%S", 1317425777), "2011-09-30 23:36:17"); - BOOST_CHECK_EQUAL(DateTimeStrFormat("%Y-%m-%dT%H:%M:%SZ", 1317425777), "2011-09-30T23:36:17Z"); - BOOST_CHECK_EQUAL(DateTimeStrFormat("%H:%M:%SZ", 1317425777), "23:36:17Z"); - BOOST_CHECK_EQUAL(DateTimeStrFormat("%Y-%m-%d %H:%M", 1317425777), "2011-09-30 23:36"); - BOOST_CHECK_EQUAL(DateTimeStrFormat("%a, %d %b %Y %H:%M:%S +0000", 1317425777), "Fri, 30 Sep 2011 23:36:17 +0000"); -} - BOOST_AUTO_TEST_CASE(util_FormatISO8601DateTime) { BOOST_CHECK_EQUAL(FormatISO8601DateTime(1317425777), "2011-09-30T23:36:17Z"); @@ -187,13 +176,22 @@ BOOST_AUTO_TEST_CASE(util_FormatISO8601Time) struct TestArgsManager : public ArgsManager { - std::map<std::string, std::string>& GetMapArgs() { return mapArgs; } - const std::map<std::string, std::vector<std::string> >& GetMapMultiArgs() { return mapMultiArgs; } - const std::unordered_set<std::string>& GetNegatedArgs() { return m_negated_args; } + TestArgsManager() { m_network_only_args.clear(); } + std::map<std::string, std::vector<std::string> >& GetOverrideArgs() { return m_override_args; } + std::map<std::string, std::vector<std::string> >& GetConfigArgs() { return m_config_args; } void ReadConfigString(const std::string str_config) { - std::istringstream stream(str_config); - ReadConfigStream(stream); + std::istringstream streamConfig(str_config); + { + LOCK(cs_args); + m_config_args.clear(); + } + ReadConfigStream(streamConfig); + } + void SetNetworkOnlyArg(const std::string arg) + { + LOCK(cs_args); + m_network_only_args.insert(arg); } }; @@ -203,22 +201,26 @@ BOOST_AUTO_TEST_CASE(util_ParseParameters) const char *argv_test[] = {"-ignored", "-a", "-b", "-ccc=argument", "-ccc=multiple", "f", "-d=e"}; testArgs.ParseParameters(0, (char**)argv_test); - BOOST_CHECK(testArgs.GetMapArgs().empty() && testArgs.GetMapMultiArgs().empty()); + BOOST_CHECK(testArgs.GetOverrideArgs().empty() && testArgs.GetConfigArgs().empty()); testArgs.ParseParameters(1, (char**)argv_test); - BOOST_CHECK(testArgs.GetMapArgs().empty() && testArgs.GetMapMultiArgs().empty()); + BOOST_CHECK(testArgs.GetOverrideArgs().empty() && testArgs.GetConfigArgs().empty()); testArgs.ParseParameters(7, (char**)argv_test); // 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) - BOOST_CHECK(testArgs.GetMapArgs().size() == 3 && testArgs.GetMapMultiArgs().size() == 3); + BOOST_CHECK(testArgs.GetOverrideArgs().size() == 3 && testArgs.GetConfigArgs().empty()); BOOST_CHECK(testArgs.IsArgSet("-a") && testArgs.IsArgSet("-b") && testArgs.IsArgSet("-ccc") && !testArgs.IsArgSet("f") && !testArgs.IsArgSet("-d")); - BOOST_CHECK(testArgs.GetMapMultiArgs().count("-a") && testArgs.GetMapMultiArgs().count("-b") && testArgs.GetMapMultiArgs().count("-ccc") - && !testArgs.GetMapMultiArgs().count("f") && !testArgs.GetMapMultiArgs().count("-d")); - - BOOST_CHECK(testArgs.GetMapArgs()["-a"] == "" && testArgs.GetMapArgs()["-ccc"] == "multiple"); + BOOST_CHECK(testArgs.GetOverrideArgs().count("-a") && testArgs.GetOverrideArgs().count("-b") && testArgs.GetOverrideArgs().count("-ccc") + && !testArgs.GetOverrideArgs().count("f") && !testArgs.GetOverrideArgs().count("-d")); + + BOOST_CHECK(testArgs.GetOverrideArgs()["-a"].size() == 1); + BOOST_CHECK(testArgs.GetOverrideArgs()["-a"].front() == ""); + BOOST_CHECK(testArgs.GetOverrideArgs()["-ccc"].size() == 2); + BOOST_CHECK(testArgs.GetOverrideArgs()["-ccc"].front() == "argument"); + BOOST_CHECK(testArgs.GetOverrideArgs()["-ccc"].back() == "multiple"); BOOST_CHECK(testArgs.GetArgs("-ccc").size() == 2); } @@ -234,15 +236,14 @@ BOOST_AUTO_TEST_CASE(util_GetBoolArg) BOOST_CHECK(testArgs.IsArgSet({'-', opt}) || !opt); // Nothing else should be in the map - BOOST_CHECK(testArgs.GetMapArgs().size() == 6 && - testArgs.GetMapMultiArgs().size() == 6); + BOOST_CHECK(testArgs.GetOverrideArgs().size() == 6 && + testArgs.GetConfigArgs().empty()); // The -no prefix should get stripped on the way in. BOOST_CHECK(!testArgs.IsArgSet("-nob")); // The -b option is flagged as negated, and nothing else is BOOST_CHECK(testArgs.IsArgNegated("-b")); - BOOST_CHECK(testArgs.GetNegatedArgs().size() == 1); BOOST_CHECK(!testArgs.IsArgNegated("-a")); // Check expected values. @@ -267,8 +268,8 @@ BOOST_AUTO_TEST_CASE(util_GetBoolArgEdgeCases) BOOST_CHECK(!testArgs.IsArgNegated("-foo")); BOOST_CHECK(testArgs.GetArg("-foo", "xxx") == ""); - // A double negative is a positive. - BOOST_CHECK(testArgs.IsArgNegated("-bar")); + // A double negative is a positive, and not marked as negated. + BOOST_CHECK(!testArgs.IsArgNegated("-bar")); BOOST_CHECK(testArgs.GetArg("-bar", "xxx") == "1"); // Config test @@ -277,12 +278,12 @@ BOOST_AUTO_TEST_CASE(util_GetBoolArgEdgeCases) testArgs.ReadConfigString(conf_test); // This was passed twice, second one overrides the negative setting, - // but not the value. + // and the value. BOOST_CHECK(!testArgs.IsArgNegated("-foo")); - BOOST_CHECK(testArgs.GetArg("-foo", "xxx") == "0"); + BOOST_CHECK(testArgs.GetArg("-foo", "xxx") == "1"); - // A double negative is a positive. - BOOST_CHECK(testArgs.IsArgNegated("-bar")); + // A double negative is a positive, and does not count as negated. + BOOST_CHECK(!testArgs.IsArgNegated("-bar")); BOOST_CHECK(testArgs.GetArg("-bar", "xxx") == "1"); // Combined test @@ -292,18 +293,15 @@ BOOST_AUTO_TEST_CASE(util_GetBoolArgEdgeCases) testArgs.ReadConfigString(combo_test_conf); // Command line overrides, but doesn't erase old setting - BOOST_CHECK(!testArgs.IsArgNegated("-foo")); + BOOST_CHECK(testArgs.IsArgNegated("-foo")); BOOST_CHECK(testArgs.GetArg("-foo", "xxx") == "0"); - BOOST_CHECK(testArgs.GetArgs("-foo").size() == 2 - && testArgs.GetArgs("-foo").front() == "0" - && testArgs.GetArgs("-foo").back() == "1"); + BOOST_CHECK(testArgs.GetArgs("-foo").size() == 0); // Command line overrides, but doesn't erase old setting - BOOST_CHECK(testArgs.IsArgNegated("-bar")); + BOOST_CHECK(!testArgs.IsArgNegated("-bar")); BOOST_CHECK(testArgs.GetArg("-bar", "xxx") == ""); - BOOST_CHECK(testArgs.GetArgs("-bar").size() == 2 - && testArgs.GetArgs("-bar").front() == "" - && testArgs.GetArgs("-bar").back() == "0"); + BOOST_CHECK(testArgs.GetArgs("-bar").size() == 1 + && testArgs.GetArgs("-bar").front() == ""); } BOOST_AUTO_TEST_CASE(util_ReadConfigStream) @@ -319,24 +317,39 @@ BOOST_AUTO_TEST_CASE(util_ReadConfigStream) "h=1\n" "noh=1\n" "noi=1\n" - "i=1\n"; + "i=1\n" + "sec1.ccc=extend1\n" + "\n" + "[sec1]\n" + "ccc=extend2\n" + "d=eee\n" + "h=1\n" + "[sec2]\n" + "ccc=extend3\n" + "iii=2\n"; TestArgsManager test_args; test_args.ReadConfigString(str_config); // expectation: a, b, ccc, d, fff, ggg, h, i end up in map - - BOOST_CHECK(test_args.GetMapArgs().size() == 8); - BOOST_CHECK(test_args.GetMapMultiArgs().size() == 8); - - BOOST_CHECK(test_args.GetMapArgs().count("-a") - && test_args.GetMapArgs().count("-b") - && test_args.GetMapArgs().count("-ccc") - && test_args.GetMapArgs().count("-d") - && test_args.GetMapArgs().count("-fff") - && test_args.GetMapArgs().count("-ggg") - && test_args.GetMapArgs().count("-h") - && test_args.GetMapArgs().count("-i") + // so do sec1.ccc, sec1.d, sec1.h, sec2.ccc, sec2.iii + + BOOST_CHECK(test_args.GetOverrideArgs().empty()); + BOOST_CHECK(test_args.GetConfigArgs().size() == 13); + + BOOST_CHECK(test_args.GetConfigArgs().count("-a") + && test_args.GetConfigArgs().count("-b") + && test_args.GetConfigArgs().count("-ccc") + && test_args.GetConfigArgs().count("-d") + && test_args.GetConfigArgs().count("-fff") + && test_args.GetConfigArgs().count("-ggg") + && test_args.GetConfigArgs().count("-h") + && test_args.GetConfigArgs().count("-i") + ); + BOOST_CHECK(test_args.GetConfigArgs().count("-sec1.ccc") + && test_args.GetConfigArgs().count("-sec1.h") + && test_args.GetConfigArgs().count("-sec2.ccc") + && test_args.GetConfigArgs().count("-sec2.iii") ); BOOST_CHECK(test_args.IsArgSet("-a") @@ -348,6 +361,7 @@ BOOST_AUTO_TEST_CASE(util_ReadConfigStream) && test_args.IsArgSet("-h") && test_args.IsArgSet("-i") && !test_args.IsArgSet("-zzz") + && !test_args.IsArgSet("-iii") ); BOOST_CHECK(test_args.GetArg("-a", "xxx") == "" @@ -356,9 +370,10 @@ BOOST_AUTO_TEST_CASE(util_ReadConfigStream) && test_args.GetArg("-d", "xxx") == "e" && test_args.GetArg("-fff", "xxx") == "0" && test_args.GetArg("-ggg", "xxx") == "1" - && test_args.GetArg("-h", "xxx") == "1" // 1st value takes precedence - && test_args.GetArg("-i", "xxx") == "0" // 1st value takes precedence + && test_args.GetArg("-h", "xxx") == "0" + && test_args.GetArg("-i", "xxx") == "1" && test_args.GetArg("-zzz", "xxx") == "xxx" + && test_args.GetArg("-iii", "xxx") == "xxx" ); for (bool def : {false, true}) { @@ -368,9 +383,10 @@ BOOST_AUTO_TEST_CASE(util_ReadConfigStream) && !test_args.GetBoolArg("-d", def) && !test_args.GetBoolArg("-fff", def) && test_args.GetBoolArg("-ggg", def) - && test_args.GetBoolArg("-h", def) - && !test_args.GetBoolArg("-i", def) + && !test_args.GetBoolArg("-h", def) + && test_args.GetBoolArg("-i", def) && test_args.GetBoolArg("-zzz", def) == def + && test_args.GetBoolArg("-iii", def) == def ); } @@ -381,19 +397,15 @@ BOOST_AUTO_TEST_CASE(util_ReadConfigStream) BOOST_CHECK(test_args.GetArgs("-ccc").size() == 2 && test_args.GetArgs("-ccc").front() == "argument" && test_args.GetArgs("-ccc").back() == "multiple"); - BOOST_CHECK(test_args.GetArgs("-fff").size() == 1 - && test_args.GetArgs("-fff").front() == "0"); + BOOST_CHECK(test_args.GetArgs("-fff").size() == 0); BOOST_CHECK(test_args.GetArgs("-nofff").size() == 0); BOOST_CHECK(test_args.GetArgs("-ggg").size() == 1 && test_args.GetArgs("-ggg").front() == "1"); BOOST_CHECK(test_args.GetArgs("-noggg").size() == 0); - BOOST_CHECK(test_args.GetArgs("-h").size() == 2 - && test_args.GetArgs("-h").front() == "1" - && test_args.GetArgs("-h").back() == "0"); + BOOST_CHECK(test_args.GetArgs("-h").size() == 0); BOOST_CHECK(test_args.GetArgs("-noh").size() == 0); - BOOST_CHECK(test_args.GetArgs("-i").size() == 2 - && test_args.GetArgs("-i").front() == "0" - && test_args.GetArgs("-i").back() == "1"); + BOOST_CHECK(test_args.GetArgs("-i").size() == 1 + && test_args.GetArgs("-i").front() == "1"); BOOST_CHECK(test_args.GetArgs("-noi").size() == 0); BOOST_CHECK(test_args.GetArgs("-zzz").size() == 0); @@ -402,25 +414,98 @@ BOOST_AUTO_TEST_CASE(util_ReadConfigStream) BOOST_CHECK(!test_args.IsArgNegated("-ccc")); BOOST_CHECK(!test_args.IsArgNegated("-d")); BOOST_CHECK(test_args.IsArgNegated("-fff")); - BOOST_CHECK(test_args.IsArgNegated("-ggg")); // IsArgNegated==true when noggg=0 + BOOST_CHECK(!test_args.IsArgNegated("-ggg")); BOOST_CHECK(test_args.IsArgNegated("-h")); // last setting takes precedence BOOST_CHECK(!test_args.IsArgNegated("-i")); // last setting takes precedence BOOST_CHECK(!test_args.IsArgNegated("-zzz")); + + // Test sections work + test_args.SelectConfigNetwork("sec1"); + + // same as original + BOOST_CHECK(test_args.GetArg("-a", "xxx") == "" + && test_args.GetArg("-b", "xxx") == "1" + && test_args.GetArg("-fff", "xxx") == "0" + && test_args.GetArg("-ggg", "xxx") == "1" + && test_args.GetArg("-zzz", "xxx") == "xxx" + && test_args.GetArg("-iii", "xxx") == "xxx" + ); + // d is overridden + BOOST_CHECK(test_args.GetArg("-d", "xxx") == "eee"); + // section-specific setting + BOOST_CHECK(test_args.GetArg("-h", "xxx") == "1"); + // section takes priority for multiple values + BOOST_CHECK(test_args.GetArg("-ccc", "xxx") == "extend1"); + // check multiple values works + const std::vector<std::string> sec1_ccc_expected = {"extend1","extend2","argument","multiple"}; + const auto& sec1_ccc_res = test_args.GetArgs("-ccc"); + BOOST_CHECK_EQUAL_COLLECTIONS(sec1_ccc_res.begin(), sec1_ccc_res.end(), sec1_ccc_expected.begin(), sec1_ccc_expected.end()); + + test_args.SelectConfigNetwork("sec2"); + + // same as original + BOOST_CHECK(test_args.GetArg("-a", "xxx") == "" + && test_args.GetArg("-b", "xxx") == "1" + && test_args.GetArg("-d", "xxx") == "e" + && test_args.GetArg("-fff", "xxx") == "0" + && test_args.GetArg("-ggg", "xxx") == "1" + && test_args.GetArg("-zzz", "xxx") == "xxx" + && test_args.GetArg("-h", "xxx") == "0" + ); + // section-specific setting + BOOST_CHECK(test_args.GetArg("-iii", "xxx") == "2"); + // section takes priority for multiple values + BOOST_CHECK(test_args.GetArg("-ccc", "xxx") == "extend3"); + // check multiple values works + const std::vector<std::string> sec2_ccc_expected = {"extend3","argument","multiple"}; + const auto& sec2_ccc_res = test_args.GetArgs("-ccc"); + BOOST_CHECK_EQUAL_COLLECTIONS(sec2_ccc_res.begin(), sec2_ccc_res.end(), sec2_ccc_expected.begin(), sec2_ccc_expected.end()); + + // Test section only options + + test_args.SetNetworkOnlyArg("-d"); + test_args.SetNetworkOnlyArg("-ccc"); + test_args.SetNetworkOnlyArg("-h"); + + test_args.SelectConfigNetwork(CBaseChainParams::MAIN); + BOOST_CHECK(test_args.GetArg("-d", "xxx") == "e"); + BOOST_CHECK(test_args.GetArgs("-ccc").size() == 2); + BOOST_CHECK(test_args.GetArg("-h", "xxx") == "0"); + + test_args.SelectConfigNetwork("sec1"); + BOOST_CHECK(test_args.GetArg("-d", "xxx") == "eee"); + BOOST_CHECK(test_args.GetArgs("-d").size() == 1); + BOOST_CHECK(test_args.GetArgs("-ccc").size() == 2); + BOOST_CHECK(test_args.GetArg("-h", "xxx") == "1"); + + test_args.SelectConfigNetwork("sec2"); + BOOST_CHECK(test_args.GetArg("-d", "xxx") == "xxx"); + BOOST_CHECK(test_args.GetArgs("-d").size() == 0); + BOOST_CHECK(test_args.GetArgs("-ccc").size() == 1); + BOOST_CHECK(test_args.GetArg("-h", "xxx") == "0"); } BOOST_AUTO_TEST_CASE(util_GetArg) { TestArgsManager testArgs; - testArgs.GetMapArgs().clear(); - testArgs.GetMapArgs()["strtest1"] = "string..."; + testArgs.GetOverrideArgs().clear(); + testArgs.GetOverrideArgs()["strtest1"] = {"string..."}; // strtest2 undefined on purpose - testArgs.GetMapArgs()["inttest1"] = "12345"; - testArgs.GetMapArgs()["inttest2"] = "81985529216486895"; + testArgs.GetOverrideArgs()["inttest1"] = {"12345"}; + testArgs.GetOverrideArgs()["inttest2"] = {"81985529216486895"}; // inttest3 undefined on purpose - testArgs.GetMapArgs()["booltest1"] = ""; + testArgs.GetOverrideArgs()["booltest1"] = {""}; // booltest2 undefined on purpose - testArgs.GetMapArgs()["booltest3"] = "0"; - testArgs.GetMapArgs()["booltest4"] = "1"; + testArgs.GetOverrideArgs()["booltest3"] = {"0"}; + testArgs.GetOverrideArgs()["booltest4"] = {"1"}; + + // priorities + testArgs.GetOverrideArgs()["pritest1"] = {"a", "b"}; + testArgs.GetConfigArgs()["pritest2"] = {"a", "b"}; + testArgs.GetOverrideArgs()["pritest3"] = {"a"}; + testArgs.GetConfigArgs()["pritest3"] = {"b"}; + testArgs.GetOverrideArgs()["pritest4"] = {"a","b"}; + testArgs.GetConfigArgs()["pritest4"] = {"c","d"}; BOOST_CHECK_EQUAL(testArgs.GetArg("strtest1", "default"), "string..."); BOOST_CHECK_EQUAL(testArgs.GetArg("strtest2", "default"), "default"); @@ -431,6 +516,11 @@ BOOST_AUTO_TEST_CASE(util_GetArg) BOOST_CHECK_EQUAL(testArgs.GetBoolArg("booltest2", false), false); BOOST_CHECK_EQUAL(testArgs.GetBoolArg("booltest3", false), false); BOOST_CHECK_EQUAL(testArgs.GetBoolArg("booltest4", false), true); + + BOOST_CHECK_EQUAL(testArgs.GetArg("pritest1", "default"), "b"); + BOOST_CHECK_EQUAL(testArgs.GetArg("pritest2", "default"), "a"); + BOOST_CHECK_EQUAL(testArgs.GetArg("pritest3", "default"), "a"); + BOOST_CHECK_EQUAL(testArgs.GetArg("pritest4", "default"), "b"); } BOOST_AUTO_TEST_CASE(util_GetChainName) @@ -443,7 +533,8 @@ BOOST_AUTO_TEST_CASE(util_GetChainName) const char* argv_both[] = {"cmd", "-testnet", "-regtest"}; // equivalent to "-testnet" - const char* testnetconf = "testnet=1\nregtest=0\n"; + // regtest in testnet section is ignored + const char* testnetconf = "testnet=1\nregtest=0\n[test]\nregtest=1"; test_args.ParseParameters(0, (char**)argv_testnet); BOOST_CHECK_EQUAL(test_args.GetChainName(), "main"); @@ -479,6 +570,30 @@ BOOST_AUTO_TEST_CASE(util_GetChainName) test_args.ParseParameters(3, (char**)argv_both); test_args.ReadConfigString(testnetconf); BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error); + + // check setting the network to test (and thus making + // [test] regtest=1 potentially relevant) doesn't break things + test_args.SelectConfigNetwork("test"); + + test_args.ParseParameters(0, (char**)argv_testnet); + test_args.ReadConfigString(testnetconf); + BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); + + test_args.ParseParameters(2, (char**)argv_testnet); + test_args.ReadConfigString(testnetconf); + BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); + + test_args.ParseParameters(2, (char**)argv_regtest); + test_args.ReadConfigString(testnetconf); + BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error); + + test_args.ParseParameters(2, (char**)argv_test_no_reg); + test_args.ReadConfigString(testnetconf); + BOOST_CHECK_EQUAL(test_args.GetChainName(), "test"); + + test_args.ParseParameters(3, (char**)argv_both); + test_args.ReadConfigString(testnetconf); + BOOST_CHECK_THROW(test_args.GetChainName(), std::runtime_error); } BOOST_AUTO_TEST_CASE(util_FormatMoney) diff --git a/src/util.cpp b/src/util.cpp index f55c9c8c34..9a3067259f 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -84,21 +84,11 @@ const int64_t nStartupTime = GetTime(); const char * const BITCOIN_CONF_FILENAME = "bitcoin.conf"; const char * const BITCOIN_PID_FILENAME = "bitcoind.pid"; -const char * const DEFAULT_DEBUGLOGFILE = "debug.log"; ArgsManager gArgs; -bool fPrintToConsole = false; -bool fPrintToDebugLog = true; -bool fLogTimestamps = DEFAULT_LOGTIMESTAMPS; -bool fLogTimeMicros = DEFAULT_LOGTIMEMICROS; -bool fLogIPs = DEFAULT_LOGIPS; -std::atomic<bool> fReopenDebugLog(false); CTranslationInterface translationInterface; -/** Log categories bitfield. */ -std::atomic<uint32_t> logCategories(0); - /** Init OpenSSL library multithreading support */ static std::unique_ptr<CCriticalSection[]> ppmutexOpenSSL; void locking_callback(int mode, int i, const char* file, int line) NO_THREAD_SAFETY_ANALYSIS @@ -147,233 +137,6 @@ public: } instance_of_cinit; -/** - * LogPrintf() has been broken a couple of times now - * by well-meaning people adding mutexes in the most straightforward way. - * It breaks because it may be called by global destructors during shutdown. - * Since the order of destruction of static/global objects is undefined, - * defining a mutex as a global object doesn't work (the mutex gets - * destroyed, and then some later destructor calls OutputDebugStringF, - * maybe indirectly, and you get a core dump at shutdown trying to lock - * the mutex). - */ - -static std::once_flag debugPrintInitFlag; - -/** - * We use std::call_once() to make sure mutexDebugLog and - * vMsgsBeforeOpenLog are initialized in a thread-safe manner. - * - * NOTE: fileout, mutexDebugLog and sometimes vMsgsBeforeOpenLog - * are leaked on exit. This is ugly, but will be cleaned up by - * the OS/libc. When the shutdown sequence is fully audited and - * tested, explicit destruction of these objects can be implemented. - */ -static FILE* fileout = nullptr; -static std::mutex* mutexDebugLog = nullptr; -static std::list<std::string>* vMsgsBeforeOpenLog; - -static int FileWriteStr(const std::string &str, FILE *fp) -{ - return fwrite(str.data(), 1, str.size(), fp); -} - -static void DebugPrintInit() -{ - assert(mutexDebugLog == nullptr); - mutexDebugLog = new std::mutex(); - vMsgsBeforeOpenLog = new std::list<std::string>; -} - -fs::path GetDebugLogPath() -{ - fs::path logfile(gArgs.GetArg("-debuglogfile", DEFAULT_DEBUGLOGFILE)); - return AbsPathForConfigVal(logfile); -} - -bool OpenDebugLog() -{ - std::call_once(debugPrintInitFlag, &DebugPrintInit); - std::lock_guard<std::mutex> scoped_lock(*mutexDebugLog); - - assert(fileout == nullptr); - assert(vMsgsBeforeOpenLog); - fs::path pathDebug = GetDebugLogPath(); - - fileout = fsbridge::fopen(pathDebug, "a"); - if (!fileout) { - return false; - } - - setbuf(fileout, nullptr); // unbuffered - // dump buffered messages from before we opened the log - while (!vMsgsBeforeOpenLog->empty()) { - FileWriteStr(vMsgsBeforeOpenLog->front(), fileout); - vMsgsBeforeOpenLog->pop_front(); - } - - delete vMsgsBeforeOpenLog; - vMsgsBeforeOpenLog = nullptr; - return true; -} - -struct CLogCategoryDesc -{ - uint32_t flag; - std::string category; -}; - -const CLogCategoryDesc LogCategories[] = -{ - {BCLog::NONE, "0"}, - {BCLog::NONE, "none"}, - {BCLog::NET, "net"}, - {BCLog::TOR, "tor"}, - {BCLog::MEMPOOL, "mempool"}, - {BCLog::HTTP, "http"}, - {BCLog::BENCH, "bench"}, - {BCLog::ZMQ, "zmq"}, - {BCLog::DB, "db"}, - {BCLog::RPC, "rpc"}, - {BCLog::ESTIMATEFEE, "estimatefee"}, - {BCLog::ADDRMAN, "addrman"}, - {BCLog::SELECTCOINS, "selectcoins"}, - {BCLog::REINDEX, "reindex"}, - {BCLog::CMPCTBLOCK, "cmpctblock"}, - {BCLog::RAND, "rand"}, - {BCLog::PRUNE, "prune"}, - {BCLog::PROXY, "proxy"}, - {BCLog::MEMPOOLREJ, "mempoolrej"}, - {BCLog::LIBEVENT, "libevent"}, - {BCLog::COINDB, "coindb"}, - {BCLog::QT, "qt"}, - {BCLog::LEVELDB, "leveldb"}, - {BCLog::ALL, "1"}, - {BCLog::ALL, "all"}, -}; - -bool GetLogCategory(uint32_t *f, const std::string *str) -{ - if (f && str) { - if (*str == "") { - *f = BCLog::ALL; - return true; - } - for (unsigned int i = 0; i < ARRAYLEN(LogCategories); i++) { - if (LogCategories[i].category == *str) { - *f = LogCategories[i].flag; - return true; - } - } - } - return false; -} - -std::string ListLogCategories() -{ - std::string ret; - int outcount = 0; - for (unsigned int i = 0; i < ARRAYLEN(LogCategories); i++) { - // Omit the special cases. - if (LogCategories[i].flag != BCLog::NONE && LogCategories[i].flag != BCLog::ALL) { - if (outcount != 0) ret += ", "; - ret += LogCategories[i].category; - outcount++; - } - } - return ret; -} - -std::vector<CLogCategoryActive> ListActiveLogCategories() -{ - std::vector<CLogCategoryActive> ret; - for (unsigned int i = 0; i < ARRAYLEN(LogCategories); i++) { - // Omit the special cases. - if (LogCategories[i].flag != BCLog::NONE && LogCategories[i].flag != BCLog::ALL) { - CLogCategoryActive catActive; - catActive.category = LogCategories[i].category; - catActive.active = LogAcceptCategory(LogCategories[i].flag); - ret.push_back(catActive); - } - } - return ret; -} - -/** - * fStartedNewLine is a state variable held by the calling context that will - * suppress printing of the timestamp when multiple calls are made that don't - * end in a newline. Initialize it to true, and hold it, in the calling context. - */ -static std::string LogTimestampStr(const std::string &str, std::atomic_bool *fStartedNewLine) -{ - std::string strStamped; - - if (!fLogTimestamps) - return str; - - if (*fStartedNewLine) { - int64_t nTimeMicros = GetTimeMicros(); - strStamped = FormatISO8601DateTime(nTimeMicros/1000000); - if (fLogTimeMicros) { - strStamped.pop_back(); - strStamped += strprintf(".%06dZ", nTimeMicros%1000000); - } - int64_t mocktime = GetMockTime(); - if (mocktime) { - strStamped += " (mocktime: " + FormatISO8601DateTime(mocktime) + ")"; - } - strStamped += ' ' + str; - } else - strStamped = str; - - if (!str.empty() && str[str.size()-1] == '\n') - *fStartedNewLine = true; - else - *fStartedNewLine = false; - - return strStamped; -} - -int LogPrintStr(const std::string &str) -{ - int ret = 0; // Returns total number of characters written - static std::atomic_bool fStartedNewLine(true); - - std::string strTimestamped = LogTimestampStr(str, &fStartedNewLine); - - if (fPrintToConsole) - { - // print to console - ret = fwrite(strTimestamped.data(), 1, strTimestamped.size(), stdout); - fflush(stdout); - } - else if (fPrintToDebugLog) - { - std::call_once(debugPrintInitFlag, &DebugPrintInit); - std::lock_guard<std::mutex> scoped_lock(*mutexDebugLog); - - // buffer if we haven't opened the log yet - if (fileout == nullptr) { - assert(vMsgsBeforeOpenLog); - ret = strTimestamped.length(); - vMsgsBeforeOpenLog->push_back(strTimestamped); - } - else - { - // reopen the log file, if requested - if (fReopenDebugLog) { - fReopenDebugLog = false; - fs::path pathDebug = GetDebugLogPath(); - if (fsbridge::freopen(pathDebug,"a",fileout) != nullptr) - setbuf(fileout, nullptr); // unbuffered - } - - ret = FileWriteStr(strTimestamped, fileout); - } - } - return ret; -} - /** A map that contains all the currently held directory locks. After * successful locking, these will be held here until the global destructor * cleans them up and thus automatically unlocks them, or ReleaseDirectoryLocks @@ -455,39 +218,204 @@ static bool InterpretBool(const std::string& strValue) return (atoi(strValue) != 0); } +/** Internal helper functions for ArgsManager */ +class ArgsManagerHelper { +public: + typedef std::map<std::string, std::vector<std::string>> MapArgs; + + /** Determine whether to use config settings in the default section, + * See also comments around ArgsManager::ArgsManager() below. */ + static inline bool UseDefaultSection(const ArgsManager& am, const std::string& arg) + { + return (am.m_network == CBaseChainParams::MAIN || am.m_network_only_args.count(arg) == 0); + } + + /** Convert regular argument into the network-specific setting */ + static inline std::string NetworkArg(const ArgsManager& am, const std::string& arg) + { + assert(arg.length() > 1 && arg[0] == '-'); + return "-" + am.m_network + "." + arg.substr(1); + } + + /** Find arguments in a map and add them to a vector */ + static inline void AddArgs(std::vector<std::string>& res, const MapArgs& map_args, const std::string& arg) + { + auto it = map_args.find(arg); + if (it != map_args.end()) { + res.insert(res.end(), it->second.begin(), it->second.end()); + } + } + + /** Return true/false if an argument is set in a map, and also + * return the first (or last) of the possibly multiple values it has + */ + static inline std::pair<bool,std::string> GetArgHelper(const MapArgs& map_args, const std::string& arg, bool getLast = false) + { + auto it = map_args.find(arg); + + if (it == map_args.end() || it->second.empty()) { + return std::make_pair(false, std::string()); + } + + if (getLast) { + return std::make_pair(true, it->second.back()); + } else { + return std::make_pair(true, it->second.front()); + } + } + + /* Get the string value of an argument, returning a pair of a boolean + * indicating the argument was found, and the value for the argument + * if it was found (or the empty string if not found). + */ + static inline std::pair<bool,std::string> GetArg(const ArgsManager &am, const std::string& arg) + { + LOCK(am.cs_args); + std::pair<bool,std::string> found_result(false, std::string()); + + // We pass "true" to GetArgHelper in order to return the last + // argument value seen from the command line (so "bitcoind -foo=bar + // -foo=baz" gives GetArg(am,"foo")=={true,"baz"} + found_result = GetArgHelper(am.m_override_args, arg, true); + if (found_result.first) { + return found_result; + } + + // But in contrast we return the first argument seen in a config file, + // so "foo=bar \n foo=baz" in the config file gives + // GetArg(am,"foo")={true,"bar"} + if (!am.m_network.empty()) { + found_result = GetArgHelper(am.m_config_args, NetworkArg(am, arg)); + if (found_result.first) { + return found_result; + } + } + + if (UseDefaultSection(am, arg)) { + found_result = GetArgHelper(am.m_config_args, arg); + if (found_result.first) { + return found_result; + } + } + + return found_result; + } + + /* Special test for -testnet and -regtest args, because we + * don't want to be confused by craziness like "[regtest] testnet=1" + */ + static inline bool GetNetBoolArg(const ArgsManager &am, const std::string& net_arg) + { + std::pair<bool,std::string> found_result(false,std::string()); + found_result = GetArgHelper(am.m_override_args, net_arg, true); + if (!found_result.first) { + found_result = GetArgHelper(am.m_config_args, net_arg, true); + if (!found_result.first) { + return false; // not set + } + } + return InterpretBool(found_result.second); // is set, so evaluate + } +}; + /** * Interpret -nofoo as if the user supplied -foo=0. * - * This method also tracks when the -no form was supplied, and treats "-foo" as - * a negated option when this happens. This can be later checked using the + * This method also tracks when the -no form was supplied, and if so, + * checks whether there was a double-negative (-nofoo=0 -> -foo=1). + * + * If there was not a double negative, it removes the "no" from the key, + * and returns true, indicating the caller should clear the args vector + * to indicate a negated option. + * + * If there was a double negative, it removes "no" from the key, sets the + * value to "1" and returns false. + * + * If there was no "no", it leaves key and value untouched and returns + * false. + * + * Where an option was negated can be later checked using the * IsArgNegated() method. One use case for this is to have a way to disable * options that are not normally boolean (e.g. using -nodebuglogfile to request * that debug log output is not sent to any file at all). */ -void ArgsManager::InterpretNegatedOption(std::string& key, std::string& val) +static bool InterpretNegatedOption(std::string& key, std::string& val) { - if (key.substr(0, 3) == "-no") { + assert(key[0] == '-'); + + size_t option_index = key.find('.'); + if (option_index == std::string::npos) { + option_index = 1; + } else { + ++option_index; + } + if (key.substr(option_index, 2) == "no") { bool bool_val = InterpretBool(val); + key.erase(option_index, 2); if (!bool_val ) { // Double negatives like -nofoo=0 are supported (but discouraged) LogPrintf("Warning: parsed potentially confusing double-negative %s=%s\n", key, val); + val = "1"; + } else { + return true; } - key.erase(1, 2); - m_negated_args.insert(key); - val = bool_val ? "0" : "1"; - } else { - // In an invocation like "bitcoind -nofoo -foo" we want to unmark -foo - // as negated when we see the second option. - m_negated_args.erase(key); } + return false; +} + +ArgsManager::ArgsManager() : + /* These options would cause cross-contamination if values for + * mainnet were used while running on regtest/testnet (or vice-versa). + * Setting them as section_only_args ensures that sharing a config file + * between mainnet and regtest/testnet won't cause problems due to these + * parameters by accident. */ + m_network_only_args{ + "-addnode", "-connect", + "-port", "-bind", + "-rpcport", "-rpcbind", + "-wallet", + } +{ + // nothing to do +} + +void ArgsManager::WarnForSectionOnlyArgs() +{ + // if there's no section selected, don't worry + if (m_network.empty()) return; + + // if it's okay to use the default section for this network, don't worry + if (m_network == CBaseChainParams::MAIN) return; + + for (const auto& arg : m_network_only_args) { + std::pair<bool, std::string> found_result; + + // if this option is overridden it's fine + found_result = ArgsManagerHelper::GetArgHelper(m_override_args, arg); + if (found_result.first) continue; + + // if there's a network-specific value for this option, it's fine + found_result = ArgsManagerHelper::GetArgHelper(m_config_args, ArgsManagerHelper::NetworkArg(*this, arg)); + if (found_result.first) continue; + + // if there isn't a default value for this option, it's fine + found_result = ArgsManagerHelper::GetArgHelper(m_config_args, arg); + if (!found_result.first) continue; + + // otherwise, issue a warning + LogPrintf("Warning: Config setting for %s only applied on %s network when in [%s] section.\n", arg, m_network, m_network); + } +} + +void ArgsManager::SelectConfigNetwork(const std::string& network) +{ + m_network = network; } void ArgsManager::ParseParameters(int argc, const char* const argv[]) { LOCK(cs_args); - mapArgs.clear(); - mapMultiArgs.clear(); - m_negated_args.clear(); + m_override_args.clear(); for (int i = 1; i < argc; i++) { std::string key(argv[i]); @@ -510,55 +438,79 @@ void ArgsManager::ParseParameters(int argc, const char* const argv[]) if (key.length() > 1 && key[1] == '-') key.erase(0, 1); - // Transform -nofoo to -foo=0 - InterpretNegatedOption(key, val); - - mapArgs[key] = val; - mapMultiArgs[key].push_back(val); + // Check for -nofoo + if (InterpretNegatedOption(key, val)) { + m_override_args[key].clear(); + } else { + m_override_args[key].push_back(val); + } } } std::vector<std::string> ArgsManager::GetArgs(const std::string& strArg) const { + std::vector<std::string> result = {}; + if (IsArgNegated(strArg)) return result; // special case + LOCK(cs_args); - auto it = mapMultiArgs.find(strArg); - if (it != mapMultiArgs.end()) return it->second; - return {}; + + ArgsManagerHelper::AddArgs(result, m_override_args, strArg); + if (!m_network.empty()) { + ArgsManagerHelper::AddArgs(result, m_config_args, ArgsManagerHelper::NetworkArg(*this, strArg)); + } + + if (ArgsManagerHelper::UseDefaultSection(*this, strArg)) { + ArgsManagerHelper::AddArgs(result, m_config_args, strArg); + } + + return result; } bool ArgsManager::IsArgSet(const std::string& strArg) const { - LOCK(cs_args); - return mapArgs.count(strArg); + if (IsArgNegated(strArg)) return true; // special case + return ArgsManagerHelper::GetArg(*this, strArg).first; } bool ArgsManager::IsArgNegated(const std::string& strArg) const { LOCK(cs_args); - return m_negated_args.find(strArg) != m_negated_args.end(); + + const auto& ov = m_override_args.find(strArg); + if (ov != m_override_args.end()) return ov->second.empty(); + + if (!m_network.empty()) { + const auto& cfs = m_config_args.find(ArgsManagerHelper::NetworkArg(*this, strArg)); + if (cfs != m_config_args.end()) return cfs->second.empty(); + } + + const auto& cf = m_config_args.find(strArg); + if (cf != m_config_args.end()) return cf->second.empty(); + + return false; } std::string ArgsManager::GetArg(const std::string& strArg, const std::string& strDefault) const { - LOCK(cs_args); - auto it = mapArgs.find(strArg); - if (it != mapArgs.end()) return it->second; + if (IsArgNegated(strArg)) return "0"; + std::pair<bool,std::string> found_res = ArgsManagerHelper::GetArg(*this, strArg); + if (found_res.first) return found_res.second; return strDefault; } int64_t ArgsManager::GetArg(const std::string& strArg, int64_t nDefault) const { - LOCK(cs_args); - auto it = mapArgs.find(strArg); - if (it != mapArgs.end()) return atoi64(it->second); + if (IsArgNegated(strArg)) return 0; + std::pair<bool,std::string> found_res = ArgsManagerHelper::GetArg(*this, strArg); + if (found_res.first) return atoi64(found_res.second); return nDefault; } bool ArgsManager::GetBoolArg(const std::string& strArg, bool fDefault) const { - LOCK(cs_args); - auto it = mapArgs.find(strArg); - if (it != mapArgs.end()) return InterpretBool(it->second); + if (IsArgNegated(strArg)) return false; + std::pair<bool,std::string> found_res = ArgsManagerHelper::GetArg(*this, strArg); + if (found_res.first) return InterpretBool(found_res.second); return fDefault; } @@ -581,8 +533,7 @@ bool ArgsManager::SoftSetBoolArg(const std::string& strArg, bool fValue) void ArgsManager::ForceSetArg(const std::string& strArg, const std::string& strValue) { LOCK(cs_args); - mapArgs[strArg] = strValue; - mapMultiArgs[strArg] = {strValue}; + m_override_args[strArg] = {strValue}; } bool HelpRequested(const ArgsManager& args) @@ -745,18 +696,23 @@ void ArgsManager::ReadConfigStream(std::istream& stream) for (boost::program_options::detail::config_file_iterator it(stream, setOptions), end; it != end; ++it) { - // Don't overwrite existing settings so command line settings override bitcoin.conf std::string strKey = std::string("-") + it->string_key; std::string strValue = it->value[0]; - InterpretNegatedOption(strKey, strValue); - if (mapArgs.count(strKey) == 0) - mapArgs[strKey] = strValue; - mapMultiArgs[strKey].push_back(strValue); + if (InterpretNegatedOption(strKey, strValue)) { + m_config_args[strKey].clear(); + } else { + m_config_args[strKey].push_back(strValue); + } } } void ArgsManager::ReadConfigFile(const std::string& confPath) { + { + LOCK(cs_args); + m_config_args.clear(); + } + fs::ifstream stream(GetConfigFile(confPath)); // ok to not have a config file @@ -773,8 +729,8 @@ void ArgsManager::ReadConfigFile(const std::string& confPath) std::string ArgsManager::GetChainName() const { - bool fRegTest = GetBoolArg("-regtest", false); - bool fTestNet = GetBoolArg("-testnet", false); + bool fRegTest = ArgsManagerHelper::GetNetBoolArg(*this, "-regtest"); + bool fTestNet = ArgsManagerHelper::GetNetBoolArg(*this, "-testnet"); if (fTestNet && fRegTest) throw std::runtime_error("Invalid combination of -regtest and -testnet."); @@ -832,21 +788,37 @@ bool TryCreateDirectories(const fs::path& p) return false; } -void FileCommit(FILE *file) +bool FileCommit(FILE *file) { - fflush(file); // harmless if redundantly called + if (fflush(file) != 0) { // harmless if redundantly called + LogPrintf("%s: fflush failed: %d\n", __func__, errno); + return false; + } #ifdef WIN32 HANDLE hFile = (HANDLE)_get_osfhandle(_fileno(file)); - FlushFileBuffers(hFile); + if (FlushFileBuffers(hFile) == 0) { + LogPrintf("%s: FlushFileBuffers failed: %d\n", __func__, GetLastError()); + return false; + } #else #if defined(__linux__) || defined(__NetBSD__) - fdatasync(fileno(file)); + if (fdatasync(fileno(file)) != 0 && errno != EINVAL) { // Ignore EINVAL for filesystems that don't support sync + LogPrintf("%s: fdatasync failed: %d\n", __func__, errno); + return false; + } #elif defined(__APPLE__) && defined(F_FULLFSYNC) - fcntl(fileno(file), F_FULLFSYNC, 0); + if (fcntl(fileno(file), F_FULLFSYNC, 0) == -1) { // Manpage says "value other than -1" is returned on success + LogPrintf("%s: fcntl F_FULLFSYNC failed: %d\n", __func__, errno); + return false; + } #else - fsync(fileno(file)); + if (fsync(fileno(file)) != 0 && errno != EINVAL) { + LogPrintf("%s: fsync failed: %d\n", __func__, errno); + return false; + } #endif #endif + return true; } bool TruncateFile(FILE *file, unsigned int length) { @@ -926,34 +898,6 @@ void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length) { #endif } -void ShrinkDebugFile() -{ - // Amount of debug.log to save at end when shrinking (must fit in memory) - constexpr size_t RECENT_DEBUG_HISTORY_SIZE = 10 * 1000000; - // Scroll debug.log if it's getting too big - fs::path pathLog = GetDebugLogPath(); - FILE* file = fsbridge::fopen(pathLog, "r"); - // If debug.log file is more than 10% bigger the RECENT_DEBUG_HISTORY_SIZE - // trim it down by saving only the last RECENT_DEBUG_HISTORY_SIZE bytes - if (file && fs::file_size(pathLog) > 11 * (RECENT_DEBUG_HISTORY_SIZE / 10)) - { - // Restart the file with some of the end - std::vector<char> vch(RECENT_DEBUG_HISTORY_SIZE, 0); - fseek(file, -((long)vch.size()), SEEK_END); - int nBytes = fread(vch.data(), 1, vch.size(), file); - fclose(file); - - file = fsbridge::fopen(pathLog, "w"); - if (file) - { - fwrite(vch.data(), 1, nBytes, file); - fclose(file); - } - } - else if (file != nullptr) - fclose(file); -} - #ifdef WIN32 fs::path GetSpecialFolderPath(int nFolder, bool fCreate) { diff --git a/src/util.h b/src/util.h index 18d2076a05..ce94f396af 100644 --- a/src/util.h +++ b/src/util.h @@ -5,7 +5,7 @@ /** * Server/client environment: argument handling, config file parsing, - * logging, thread wrappers, startup time + * thread wrappers, startup time */ #ifndef BITCOIN_UTIL_H #define BITCOIN_UTIL_H @@ -16,6 +16,7 @@ #include <compat.h> #include <fs.h> +#include <logging.h> #include <sync.h> #include <tinyformat.h> #include <utiltime.h> @@ -24,6 +25,7 @@ #include <exception> #include <map> #include <memory> +#include <set> #include <stdint.h> #include <string> #include <unordered_set> @@ -35,11 +37,6 @@ // Application startup time (used for uptime calculation) int64_t GetStartupTime(); -static const bool DEFAULT_LOGTIMEMICROS = false; -static const bool DEFAULT_LOGIPS = false; -static const bool DEFAULT_LOGTIMESTAMPS = true; -extern const char * const DEFAULT_DEBUGLOGFILE; - /** Signals for translation. */ class CTranslationInterface { @@ -48,20 +45,11 @@ public: boost::signals2::signal<std::string (const char* psz)> Translate; }; -extern bool fPrintToConsole; -extern bool fPrintToDebugLog; - -extern bool fLogTimestamps; -extern bool fLogTimeMicros; -extern bool fLogIPs; -extern std::atomic<bool> fReopenDebugLog; extern CTranslationInterface translationInterface; extern const char * const BITCOIN_CONF_FILENAME; extern const char * const BITCOIN_PID_FILENAME; -extern std::atomic<uint32_t> logCategories; - /** * Translation function: Call Translate signal on UI interface, which returns a boost::optional result. * If no translation slot is registered, nothing is returned, and simply return the input. @@ -75,95 +63,6 @@ inline std::string _(const char* psz) void SetupEnvironment(); bool SetupNetworking(); -struct CLogCategoryActive -{ - std::string category; - bool active; -}; - -namespace BCLog { - enum LogFlags : uint32_t { - NONE = 0, - NET = (1 << 0), - TOR = (1 << 1), - MEMPOOL = (1 << 2), - HTTP = (1 << 3), - BENCH = (1 << 4), - ZMQ = (1 << 5), - DB = (1 << 6), - RPC = (1 << 7), - ESTIMATEFEE = (1 << 8), - ADDRMAN = (1 << 9), - SELECTCOINS = (1 << 10), - REINDEX = (1 << 11), - CMPCTBLOCK = (1 << 12), - RAND = (1 << 13), - PRUNE = (1 << 14), - PROXY = (1 << 15), - MEMPOOLREJ = (1 << 16), - LIBEVENT = (1 << 17), - COINDB = (1 << 18), - QT = (1 << 19), - LEVELDB = (1 << 20), - ALL = ~(uint32_t)0, - }; -} -/** Return true if log accepts specified category */ -static inline bool LogAcceptCategory(uint32_t category) -{ - return (logCategories.load(std::memory_order_relaxed) & category) != 0; -} - -/** Returns a string with the log categories. */ -std::string ListLogCategories(); - -/** Returns a vector of the active log categories. */ -std::vector<CLogCategoryActive> ListActiveLogCategories(); - -/** Return true if str parses as a log category and set the flags in f */ -bool GetLogCategory(uint32_t *f, const std::string *str); - -/** Send a string to the log output */ -int LogPrintStr(const std::string &str); - -/** Get format string from VA_ARGS for error reporting */ -template<typename... Args> std::string FormatStringFromLogArgs(const char *fmt, const Args&... args) { return fmt; } - -static inline void MarkUsed() {} -template<typename T, typename... Args> static inline void MarkUsed(const T& t, const Args&... args) -{ - (void)t; - MarkUsed(args...); -} - -// Be conservative when using LogPrintf/error or other things which -// unconditionally log to debug.log! It should not be the case that an inbound -// peer can fill up a user's disk with debug.log entries. - -#ifdef USE_COVERAGE -#define LogPrintf(...) do { MarkUsed(__VA_ARGS__); } while(0) -#define LogPrint(category, ...) do { MarkUsed(__VA_ARGS__); } while(0) -#else -#define LogPrintf(...) do { \ - if (fPrintToConsole || fPrintToDebugLog) { \ - std::string _log_msg_; /* Unlikely name to avoid shadowing variables */ \ - try { \ - _log_msg_ = tfm::format(__VA_ARGS__); \ - } catch (tinyformat::format_error &fmterr) { \ - /* Original format string will have newline so don't add one here */ \ - _log_msg_ = "Error \"" + std::string(fmterr.what()) + "\" while formatting log message: " + FormatStringFromLogArgs(__VA_ARGS__); \ - } \ - LogPrintStr(_log_msg_); \ - } \ -} while(0) - -#define LogPrint(category, ...) do { \ - if (LogAcceptCategory((category))) { \ - LogPrintf(__VA_ARGS__); \ - } \ -} while(0) -#endif - template<typename... Args> bool error(const char* fmt, const Args&... args) { @@ -172,7 +71,7 @@ bool error(const char* fmt, const Args&... args) } void PrintExceptionContinue(const std::exception *pex, const char* pszThread); -void FileCommit(FILE *file); +bool FileCommit(FILE *file); bool TruncateFile(FILE *file, unsigned int length); int RaiseFileDescriptorLimit(int nMinFD); void AllocateFileRange(FILE *file, unsigned int offset, unsigned int length); @@ -198,9 +97,6 @@ void CreatePidFile(const fs::path &path, pid_t pid); #ifdef WIN32 fs::path GetSpecialFolderPath(int nFolder, bool fCreate = true); #endif -fs::path GetDebugLogPath(); -bool OpenDebugLog(); -void ShrinkDebugFile(); void runCommand(const std::string& strCommand); /** @@ -225,18 +121,36 @@ inline bool IsSwitchChar(char c) class ArgsManager { protected: + friend class ArgsManagerHelper; + mutable CCriticalSection cs_args; - std::map<std::string, std::string> mapArgs; - std::map<std::string, std::vector<std::string>> mapMultiArgs; - std::unordered_set<std::string> m_negated_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; void ReadConfigStream(std::istream& stream); public: + ArgsManager(); + + /** + * Select the network in use + */ + void SelectConfigNetwork(const std::string& network); + void ParseParameters(int argc, const char*const argv[]); void ReadConfigFile(const std::string& confPath); /** + * Log warnings for options in m_section_only_args when + * they are specified in the default section but not overridden + * on the command line or in a network-specific section in the + * config file. + */ + void WarnForSectionOnlyArgs(); + + /** * Return a vector of strings of the given argument * * @param strArg Argument to get (e.g. "-foo") @@ -315,11 +229,6 @@ public: * @return CBaseChainParams::MAIN by default; raises runtime error if an invalid combination is given. */ std::string GetChainName() const; - -private: - - // Munge -nofoo into -foo=0 and track the value as negated. - void InterpretNegatedOption(std::string &key, std::string &val); }; extern ArgsManager gArgs; diff --git a/src/utiltime.cpp b/src/utiltime.cpp index 8a861039b3..34800c7b6d 100644 --- a/src/utiltime.cpp +++ b/src/utiltime.cpp @@ -10,9 +10,10 @@ #include <utiltime.h> #include <atomic> - #include <boost/date_time/posix_time/posix_time.hpp> #include <boost/thread.hpp> +#include <ctime> +#include <tinyformat.h> static std::atomic<int64_t> nMockTime(0); //!< For unit testing @@ -75,25 +76,23 @@ void MilliSleep(int64_t n) #endif } -std::string DateTimeStrFormat(const char* pszFormat, int64_t nTime) -{ - static std::locale classic(std::locale::classic()); - // std::locale takes ownership of the pointer - std::locale loc(classic, new boost::posix_time::time_facet(pszFormat)); - std::stringstream ss; - ss.imbue(loc); - ss << boost::posix_time::from_time_t(nTime); - return ss.str(); -} - std::string FormatISO8601DateTime(int64_t nTime) { - return DateTimeStrFormat("%Y-%m-%dT%H:%M:%SZ", nTime); + struct tm ts; + time_t time_val = nTime; + gmtime_r(&time_val, &ts); + return strprintf("%04i-%02i-%02iT%02i:%02i:%02iZ", ts.tm_year + 1900, ts.tm_mon + 1, ts.tm_mday, ts.tm_hour, ts.tm_min, ts.tm_sec); } std::string FormatISO8601Date(int64_t nTime) { - return DateTimeStrFormat("%Y-%m-%d", nTime); + struct tm ts; + time_t time_val = nTime; + gmtime_r(&time_val, &ts); + return strprintf("%04i-%02i-%02i", ts.tm_year + 1900, ts.tm_mon + 1, ts.tm_mday); } std::string FormatISO8601Time(int64_t nTime) { - return DateTimeStrFormat("%H:%M:%SZ", nTime); + struct tm ts; + time_t time_val = nTime; + gmtime_r(&time_val, &ts); + return strprintf("%02i:%02i:%02iZ", ts.tm_hour, ts.tm_min, ts.tm_sec); } diff --git a/src/utiltime.h b/src/utiltime.h index 807c52ffaf..3bcbb00c16 100644 --- a/src/utiltime.h +++ b/src/utiltime.h @@ -31,8 +31,6 @@ void MilliSleep(int64_t n); * ISO 8601 formatting is preferred. Use the FormatISO8601{DateTime,Date,Time} * helper functions if possible. */ -std::string DateTimeStrFormat(const char* pszFormat, int64_t nTime); - std::string FormatISO8601DateTime(int64_t nTime); std::string FormatISO8601Date(int64_t nTime); std::string FormatISO8601Time(int64_t nTime); diff --git a/src/validation.cpp b/src/validation.cpp index cbbc857fdc..ab44e73d7c 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -142,7 +142,7 @@ private: * ahead and mark descendants of invalid blocks as FAILED_CHILD at that time, * instead of putting things in this set. */ - std::set<CBlockIndex*> g_failed_blocks; + std::set<CBlockIndex*> m_failed_blocks; public: CChain chainActive; @@ -154,6 +154,10 @@ public: bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock); + /** + * If a block header hasn't already been seen, call CheckBlockHeader on it, ensure + * that it doesn't descend from an invalid block, and then add it to mapBlockIndex. + */ bool AcceptBlockHeader(const CBlockHeader& block, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex); bool AcceptBlock(const std::shared_ptr<const CBlock>& pblock, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const CDiskBlockPos* dbp, bool* fNewBlock); @@ -185,6 +189,11 @@ private: CBlockIndex* AddToBlockIndex(const CBlockHeader& block); /** Create a new block index entry for a given block hash */ CBlockIndex * InsertBlockIndex(const uint256& hash); + /** + * Make various assertions about the state of the block index. + * + * By default this only executes fully when using the Regtest chain; see: fCheckBlockIndex. + */ void CheckBlockIndex(const Consensus::Params& consensusParams); void InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state); @@ -264,7 +273,8 @@ CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& loc { AssertLockHeld(cs_main); - // Find the first block the caller has in the main chain + // Find the latest block common to locator and chain - we expect that + // locator.vHave is sorted descending by height. for (const uint256& hash : locator.vHave) { CBlockIndex* pindex = LookupBlockIndex(hash); if (pindex) { @@ -353,7 +363,7 @@ bool CheckSequenceLocks(const CTransaction &tx, int flags, LockPoints* lp, bool CBlockIndex* tip = chainActive.Tip(); assert(tip != nullptr); - + CBlockIndex index; index.pprev = tip; // CheckSequenceLocks() uses chainActive.Height()+1 to evaluate @@ -1285,7 +1295,7 @@ void static InvalidChainFound(CBlockIndex* pindexNew) void CChainState::InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state) { if (!state.CorruptionPossible()) { pindex->nStatus |= BLOCK_FAILED_VALID; - g_failed_blocks.insert(pindex); + m_failed_blocks.insert(pindex); setDirtyBlockIndex.insert(pindex); setBlockIndexCandidates.erase(pindex); InvalidChainFound(pindex); @@ -1614,22 +1624,27 @@ void static FlushBlockFile(bool fFinalize = false) LOCK(cs_LastBlockFile); CDiskBlockPos posOld(nLastBlockFile, 0); + bool status = true; FILE *fileOld = OpenBlockFile(posOld); if (fileOld) { if (fFinalize) - TruncateFile(fileOld, vinfoBlockFile[nLastBlockFile].nSize); - FileCommit(fileOld); + status &= TruncateFile(fileOld, vinfoBlockFile[nLastBlockFile].nSize); + status &= FileCommit(fileOld); fclose(fileOld); } fileOld = OpenUndoFile(posOld); if (fileOld) { if (fFinalize) - TruncateFile(fileOld, vinfoBlockFile[nLastBlockFile].nUndoSize); - FileCommit(fileOld); + status &= TruncateFile(fileOld, vinfoBlockFile[nLastBlockFile].nUndoSize); + status &= FileCommit(fileOld); fclose(fileOld); } + + if (!status) { + AbortNode("Flushing block file to disk failed. This is likely the result of an I/O error."); + } } static bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos, unsigned int nAddSize); @@ -1725,16 +1740,38 @@ public: // Protected by cs_main static ThresholdConditionCache warningcache[VERSIONBITS_NUM_BITS]; +// 0.13.0 was shipped with a segwit deployment defined for testnet, but not for +// mainnet. We no longer need to support disabling the segwit deployment +// except for testing purposes, due to limitations of the functional test +// environment. See test/functional/p2p-segwit.py. +static bool IsScriptWitnessEnabled(const Consensus::Params& params) +{ + return params.vDeployments[Consensus::DEPLOYMENT_SEGWIT].nTimeout != 0; +} + static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consensus::Params& consensusparams) { AssertLockHeld(cs_main); unsigned int flags = SCRIPT_VERIFY_NONE; - // Start enforcing P2SH (BIP16) - if (pindex->nHeight >= consensusparams.BIP16Height) { + // BIP16 didn't become active until Apr 1 2012 (on mainnet, and + // retroactively applied to testnet) + // However, only one historical block violated the P2SH rules (on both + // mainnet and testnet), so for simplicity, always leave P2SH + // on except for the one violating block. + if (consensusparams.BIP16Exception.IsNull() || // no bip16 exception on this chain + pindex->phashBlock == nullptr || // this is a new candidate block, eg from TestBlockValidity() + *pindex->phashBlock != consensusparams.BIP16Exception) // this block isn't the historical exception + { flags |= SCRIPT_VERIFY_P2SH; } + // Enforce WITNESS rules whenever P2SH is in effect (and the segwit + // deployment is defined). + if (flags & SCRIPT_VERIFY_P2SH && IsScriptWitnessEnabled(consensusparams)) { + flags |= SCRIPT_VERIFY_WITNESS; + } + // Start enforcing the DERSIG (BIP66) rule if (pindex->nHeight >= consensusparams.BIP66Height) { flags |= SCRIPT_VERIFY_DERSIG; @@ -1750,9 +1787,7 @@ static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consens flags |= SCRIPT_VERIFY_CHECKSEQUENCEVERIFY; } - // Start enforcing WITNESS rules using versionbits logic. - if (IsWitnessEnabled(pindex->pprev, consensusparams)) { - flags |= SCRIPT_VERIFY_WITNESS; + if (IsNullDummyEnabled(pindex->pprev, consensusparams)) { flags |= SCRIPT_VERIFY_NULLDUMMY; } @@ -2180,14 +2215,18 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState & void FlushStateToDisk() { CValidationState state; const CChainParams& chainparams = Params(); - FlushStateToDisk(chainparams, state, FlushStateMode::ALWAYS); + if (!FlushStateToDisk(chainparams, state, FlushStateMode::ALWAYS)) { + LogPrintf("%s: failed to flush state (%s)\n", __func__, FormatStateMessage(state)); + } } void PruneAndFlush() { CValidationState state; fCheckForPruning = true; const CChainParams& chainparams = Params(); - FlushStateToDisk(chainparams, state, FlushStateMode::NONE); + if (!FlushStateToDisk(chainparams, state, FlushStateMode::NONE)) { + LogPrintf("%s: failed to flush state (%s)\n", __func__, FormatStateMessage(state)); + } } static void DoWarning(const std::string& strWarning) @@ -2624,6 +2663,10 @@ static void NotifyHeaderTip() { * Make the best chain active, in multiple steps. The result is either failure * or an activated best chain. pblock is either nullptr or a pointer to a block * that is already loaded (to avoid loading it again from disk). + * + * ActivateBestChain is split into steps (see ActivateBestChainStep) so that + * we avoid holding cs_main for an extended period of time; the length of this + * call may be quite long during reindexing or a substantial reorg. */ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr<const CBlock> pblock) { // Note that while we're often called here from ProcessNewBlock, this is @@ -2677,18 +2720,17 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams& assert(trace.pblock && trace.pindex); GetMainSignals().BlockConnected(trace.pblock, trace.pindex, trace.conflictedTxs); } - } - // When we reach this point, we switched to a new tip (stored in pindexNewTip). - - // Notifications/callbacks that can run without cs_main - // Notify external listeners about the new tip. - GetMainSignals().UpdatedBlockTip(pindexNewTip, pindexFork, fInitialDownload); + // 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) { - uiInterface.NotifyBlockTip(fInitialDownload, pindexNewTip); + // Always notify the UI if a new block tip was connected + if (pindexFork != pindexNewTip) { + uiInterface.NotifyBlockTip(fInitialDownload, pindexNewTip); + } } + // When we reach this point, we switched to a new tip (stored in pindexNewTip). if (nStopAtHeight && pindexNewTip && pindexNewTip->nHeight >= nStopAtHeight) StartShutdown(); @@ -2783,7 +2825,7 @@ bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& c pindex->nStatus |= BLOCK_FAILED_VALID; setDirtyBlockIndex.insert(pindex); setBlockIndexCandidates.erase(pindex); - g_failed_blocks.insert(pindex); + m_failed_blocks.insert(pindex); // DisconnectTip will add transactions to disconnectpool; try to add these // back to the mempool. @@ -2829,7 +2871,7 @@ bool CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) { // Reset invalid block marker if it was pointing to one of those. pindexBestInvalid = nullptr; } - g_failed_blocks.erase(it->second); + m_failed_blocks.erase(it->second); } it++; } @@ -3099,6 +3141,12 @@ bool IsWitnessEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& pa return (VersionBitsState(pindexPrev, params, Consensus::DEPLOYMENT_SEGWIT, versionbitscache) == ThresholdState::ACTIVE); } +bool IsNullDummyEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& params) +{ + LOCK(cs_main); + return (VersionBitsState(pindexPrev, params, Consensus::DEPLOYMENT_SEGWIT, versionbitscache) == ThresholdState::ACTIVE); +} + // Compute at which vout of the block's coinbase transaction the witness // commitment occurs, or -1 if not found. static int GetWitnessCommitmentIndex(const CBlock& block) @@ -3322,8 +3370,11 @@ bool CChainState::AcceptBlockHeader(const CBlockHeader& block, CValidationState& if (!ContextualCheckBlockHeader(block, state, chainparams, pindexPrev, GetAdjustedTime())) return error("%s: Consensus::ContextualCheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state)); + // If the previous block index isn't valid, determine if it descends from any block which + // has been found invalid (g_failed_blocks), then mark pindexPrev and any blocks + // between them as failed. if (!pindexPrev->IsValid(BLOCK_VALID_SCRIPTS)) { - for (const CBlockIndex* failedit : g_failed_blocks) { + for (const CBlockIndex* failedit : m_failed_blocks) { if (pindexPrev->GetAncestor(failedit->nHeight) == failedit) { assert(failedit->nStatus & BLOCK_FAILED_VALID); CBlockIndex* invalid_walk = pindexPrev; @@ -3489,7 +3540,7 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<cons } if (!ret) { GetMainSignals().BlockChecked(*pblock, state); - return error("%s: AcceptBlock FAILED (%s)", __func__, state.GetDebugMessage()); + return error("%s: AcceptBlock FAILED (%s)", __func__, FormatStateMessage(state)); } } @@ -3497,7 +3548,7 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<cons CValidationState state; // Only used to report errors, not invalidity - ignore it if (!g_chainstate.ActivateBestChain(state, chainparams, pblock)) - return error("%s: ActivateBestChain failed", __func__); + return error("%s: ActivateBestChain failed (%s)", __func__, FormatStateMessage(state)); return true; } @@ -3615,7 +3666,9 @@ void PruneBlockFilesManual(int nManualPruneHeight) { CValidationState state; const CChainParams& chainparams = Params(); - FlushStateToDisk(chainparams, state, FlushStateMode::NONE, nManualPruneHeight); + if (!FlushStateToDisk(chainparams, state, FlushStateMode::NONE, nManualPruneHeight)) { + LogPrintf("%s: failed to flush state (%s)\n", __func__, FormatStateMessage(state)); + } } /** @@ -3869,6 +3922,7 @@ bool LoadChainTip(const CChainParams& chainparams) LogPrintf("%s: Connecting genesis block...\n", __func__); CValidationState state; if (!ActivateBestChain(state, chainparams)) { + LogPrintf("%s: failed to activate chain (%s)\n", __func__, FormatStateMessage(state)); return false; } } @@ -3983,7 +4037,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, if (!ReadBlockFromDisk(block, pindex, chainparams.GetConsensus())) return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); if (!g_chainstate.ConnectBlock(block, state, pindex, coins, chainparams)) - return error("VerifyDB(): *** found unconnectable block at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); + return error("VerifyDB(): *** found unconnectable block at %d, hash=%s (%s)", pindex->nHeight, pindex->GetBlockHash().ToString(), FormatStateMessage(state)); } } @@ -4091,6 +4145,9 @@ bool CChainState::RewindBlockIndex(const CChainParams& params) int nHeight = 1; while (nHeight <= chainActive.Height()) { + // Although SCRIPT_VERIFY_WITNESS is now generally enforced on all + // blocks in ConnectBlock, we don't need to go back and + // re-download/re-verify blocks from before segwit actually activated. if (IsWitnessEnabled(chainActive[nHeight - 1], params.GetConsensus()) && !(chainActive[nHeight]->nStatus & BLOCK_OPT_WITNESS)) { break; } @@ -4110,11 +4167,13 @@ bool CChainState::RewindBlockIndex(const CChainParams& params) break; } if (!DisconnectTip(state, params, nullptr)) { - return error("RewindBlockIndex: unable to disconnect block at height %i", pindex->nHeight); + return error("RewindBlockIndex: unable to disconnect block at height %i (%s)", pindex->nHeight, FormatStateMessage(state)); } // Occasionally flush state to disk. - if (!FlushStateToDisk(params, state, FlushStateMode::PERIODIC)) + if (!FlushStateToDisk(params, state, FlushStateMode::PERIODIC)) { + LogPrintf("RewindBlockIndex: unable to flush state to disk (%s)\n", FormatStateMessage(state)); return false; + } } // Reduce validity flag and have-data flags. @@ -4180,6 +4239,7 @@ bool RewindBlockIndex(const CChainParams& params) { // it'll get called a bunch real soon. CValidationState state; if (!FlushStateToDisk(params, state, FlushStateMode::ALWAYS)) { + LogPrintf("RewindBlockIndex: unable to flush state to disk (%s)\n", FormatStateMessage(state)); return false; } } @@ -4189,7 +4249,7 @@ bool RewindBlockIndex(const CChainParams& params) { void CChainState::UnloadBlockIndex() { nBlockSequenceId = 1; - g_failed_blocks.clear(); + m_failed_blocks.clear(); setBlockIndexCandidates.clear(); } @@ -4266,7 +4326,7 @@ bool CChainState::LoadGenesisBlock(const CChainParams& chainparams) CBlockIndex *pindex = AddToBlockIndex(block); CValidationState state; if (!ReceivedBlockTransactions(block, state, pindex, blockPos, chainparams.GetConsensus())) - return error("%s: genesis block not accepted", __func__); + return error("%s: genesis block not accepted (%s)", __func__, FormatStateMessage(state)); } catch (const std::runtime_error& e) { return error("%s: failed to write genesis block: %s", __func__, e.what()); } @@ -4731,7 +4791,8 @@ bool DumpMempool(void) } file << mapDeltas; - FileCommit(file.Get()); + if (!FileCommit(file.Get())) + throw std::runtime_error("FileCommit failed"); file.fclose(); RenameOver(GetDataDir() / "mempool.dat.new", GetDataDir() / "mempool.dat"); int64_t last = GetTimeMicros(); diff --git a/src/validation.h b/src/validation.h index 3668484696..b415a85053 100644 --- a/src/validation.h +++ b/src/validation.h @@ -411,6 +411,9 @@ bool TestBlockValidity(CValidationState& state, const CChainParams& chainparams, /** Check whether witness commitments are required for block. */ bool IsWitnessEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& params); +/** Check whether NULLDUMMY (BIP 147) has activated. */ +bool IsNullDummyEnabled(const CBlockIndex* pindexPrev, const Consensus::Params& params); + /** When there are blocks in the active chain with missing data, rewind the chainstate and remove them from the block index */ bool RewindBlockIndex(const CChainParams& params); diff --git a/src/validationinterface.cpp b/src/validationinterface.cpp index 928df4fa65..746263f113 100644 --- a/src/validationinterface.cpp +++ b/src/validationinterface.cpp @@ -139,6 +139,10 @@ void CMainSignals::MempoolEntryRemoved(CTransactionRef ptx, MemPoolRemovalReason } void CMainSignals::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) { + // Dependencies exist that require UpdatedBlockTip events to be delivered in the order in which + // the chain actually updates. One way to ensure this is for the caller to invoke this signal + // in the same critical section where the chain is updated + m_internals->m_schedulerClient.AddToProcessQueue([pindexNew, pindexFork, fInitialDownload, this] { m_internals->UpdatedBlockTip(pindexNew, pindexFork, fInitialDownload); }); diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp index b6f4a0e1e1..5091477dfd 100644 --- a/src/wallet/init.cpp +++ b/src/wallet/init.cpp @@ -18,39 +18,38 @@ class WalletInit : public WalletInitInterface { public: //! Return the wallets help message. - std::string GetHelpString(bool showDebug) override; + std::string GetHelpString(bool showDebug) const override; //! Wallets parameter interaction - bool ParameterInteraction() override; + bool ParameterInteraction() const override; //! Register wallet RPCs. - void RegisterRPC(CRPCTable &tableRPC) override; + void RegisterRPC(CRPCTable &tableRPC) const override; //! Responsible for reading and validating the -wallet arguments and verifying the wallet database. // This function will perform salvage on the wallet if requested, as long as only one wallet is // being loaded (WalletParameterInteraction forbids -salvagewallet, -zapwallettxes or -upgradewallet with multiwallet). - bool Verify() override; + bool Verify() const override; //! Load wallet databases. - bool Open() override; + bool Open() const override; //! Complete startup of wallets. - void Start(CScheduler& scheduler) override; + void Start(CScheduler& scheduler) const override; //! Flush all wallets in preparation for shutdown. - void Flush() override; + void Flush() const override; //! Stop all wallets. Wallets will be flushed first. - void Stop() override; + void Stop() const override; //! Close all wallets. - void Close() override; + void Close() const override; }; -static WalletInit g_wallet_init; -WalletInitInterface* const g_wallet_init_interface = &g_wallet_init; +const WalletInitInterface& g_wallet_init_interface = WalletInit(); -std::string WalletInit::GetHelpString(bool showDebug) +std::string WalletInit::GetHelpString(bool showDebug) 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))); @@ -92,7 +91,7 @@ std::string WalletInit::GetHelpString(bool showDebug) return strUsage; } -bool WalletInit::ParameterInteraction() +bool WalletInit::ParameterInteraction() const { if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { for (const std::string& wallet : gArgs.GetArgs("-wallet")) { @@ -119,19 +118,19 @@ bool WalletInit::ParameterInteraction() } } - int zapwallettxes = gArgs.GetArg("-zapwallettxes", 0); + bool zapwallettxes = gArgs.GetBoolArg("-zapwallettxes", false); // -zapwallettxes implies dropping the mempool on startup - if (zapwallettxes != 0 && gArgs.SoftSetBoolArg("-persistmempool", false)) { - LogPrintf("%s: parameter interaction: -zapwallettxes=%s -> setting -persistmempool=0\n", __func__, zapwallettxes); + if (zapwallettxes && gArgs.SoftSetBoolArg("-persistmempool", false)) { + LogPrintf("%s: parameter interaction: -zapwallettxes enabled -> setting -persistmempool=0\n", __func__); } // -zapwallettxes implies a rescan - if (zapwallettxes != 0) { + if (zapwallettxes) { if (is_multiwallet) { return InitError(strprintf("%s is only allowed with a single wallet file", "-zapwallettxes")); } if (gArgs.SoftSetBoolArg("-rescan", true)) { - LogPrintf("%s: parameter interaction: -zapwallettxes=%s -> setting -rescan=1\n", __func__, zapwallettxes); + LogPrintf("%s: parameter interaction: -zapwallettxes enabled -> setting -rescan=1\n", __func__); } } @@ -220,7 +219,7 @@ bool WalletInit::ParameterInteraction() return true; } -void WalletInit::RegisterRPC(CRPCTable &t) +void WalletInit::RegisterRPC(CRPCTable &t) const { if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { return; @@ -229,7 +228,7 @@ void WalletInit::RegisterRPC(CRPCTable &t) RegisterWalletRPCCommands(t); } -bool WalletInit::Verify() +bool WalletInit::Verify() const { if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { return true; @@ -304,7 +303,7 @@ bool WalletInit::Verify() return true; } -bool WalletInit::Open() +bool WalletInit::Open() const { if (gArgs.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { LogPrintf("Wallet disabled!\n"); @@ -316,37 +315,37 @@ bool WalletInit::Open() if (!pwallet) { return false; } - vpwallets.push_back(pwallet); + AddWallet(pwallet); } return true; } -void WalletInit::Start(CScheduler& scheduler) +void WalletInit::Start(CScheduler& scheduler) const { - for (CWalletRef pwallet : vpwallets) { + for (CWallet* pwallet : GetWallets()) { pwallet->postInitProcess(scheduler); } } -void WalletInit::Flush() +void WalletInit::Flush() const { - for (CWalletRef pwallet : vpwallets) { + for (CWallet* pwallet : GetWallets()) { pwallet->Flush(false); } } -void WalletInit::Stop() +void WalletInit::Stop() const { - for (CWalletRef pwallet : vpwallets) { + for (CWallet* pwallet : GetWallets()) { pwallet->Flush(true); } } -void WalletInit::Close() +void WalletInit::Close() const { - for (CWalletRef pwallet : vpwallets) { + for (CWallet* pwallet : GetWallets()) { + RemoveWallet(pwallet); delete pwallet; } - vpwallets.clear(); } diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index a3594aa692..b8533839a0 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -172,7 +172,13 @@ UniValue importprivkey(const JSONRPCRequest& request) } } if (fRescan) { - pwallet->RescanFromTime(TIMESTAMP_MIN, reserver, true /* update */); + int64_t scanned_time = pwallet->RescanFromTime(TIMESTAMP_MIN, reserver, true /* update */); + if (pwallet->IsAbortingRescan()) { + throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted by user."); + } + if (scanned_time > TIMESTAMP_MIN) { + throw JSONRPCError(RPC_WALLET_ERROR, "Rescan was unable to fully rescan the blockchain. Some transactions may be missing."); + } } return NullUniValue; @@ -310,7 +316,13 @@ UniValue importaddress(const JSONRPCRequest& request) } if (fRescan) { - pwallet->RescanFromTime(TIMESTAMP_MIN, reserver, true /* update */); + int64_t scanned_time = pwallet->RescanFromTime(TIMESTAMP_MIN, reserver, true /* update */); + if (pwallet->IsAbortingRescan()) { + throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted by user."); + } + if (scanned_time > TIMESTAMP_MIN) { + throw JSONRPCError(RPC_WALLET_ERROR, "Rescan was unable to fully rescan the blockchain. Some transactions may be missing."); + } pwallet->ReacceptWalletTransactions(); } @@ -479,7 +491,13 @@ UniValue importpubkey(const JSONRPCRequest& request) } if (fRescan) { - pwallet->RescanFromTime(TIMESTAMP_MIN, reserver, true /* update */); + int64_t scanned_time = pwallet->RescanFromTime(TIMESTAMP_MIN, reserver, true /* update */); + if (pwallet->IsAbortingRescan()) { + throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted by user."); + } + if (scanned_time > TIMESTAMP_MIN) { + throw JSONRPCError(RPC_WALLET_ERROR, "Rescan was unable to fully rescan the blockchain. Some transactions may be missing."); + } pwallet->ReacceptWalletTransactions(); } @@ -534,9 +552,11 @@ UniValue importwallet(const JSONRPCRequest& request) int64_t nFilesize = std::max((int64_t)1, (int64_t)file.tellg()); file.seekg(0, file.beg); - pwallet->ShowProgress(_("Importing..."), 0); // show progress dialog in GUI + // 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. + uiInterface.ShowProgress(_("Importing..."), 0, false); // show progress dialog in GUI while (file.good()) { - pwallet->ShowProgress("", std::max(1, std::min(99, (int)(((double)file.tellg() / (double)nFilesize) * 100)))); + uiInterface.ShowProgress("", std::max(1, std::min(99, (int)(((double)file.tellg() / (double)nFilesize) * 100))), false); std::string line; std::getline(file, line); if (line.empty() || line[0] == '#') @@ -599,10 +619,17 @@ UniValue importwallet(const JSONRPCRequest& request) } } file.close(); - pwallet->ShowProgress("", 100); // hide progress dialog in GUI + uiInterface.ShowProgress("", 100, false); // hide progress dialog in GUI pwallet->UpdateTimeFirstKey(nTimeBegin); } - pwallet->RescanFromTime(nTimeBegin, reserver, false /* update */); + uiInterface.ShowProgress("", 100, false); // hide progress dialog in GUI + int64_t scanned_time = pwallet->RescanFromTime(nTimeBegin, reserver, false /* update */); + if (pwallet->IsAbortingRescan()) { + throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted by user."); + } + if (scanned_time > nTimeBegin) { + throw JSONRPCError(RPC_WALLET_ERROR, "Rescan was unable to fully rescan the blockchain. Some transactions may be missing."); + } pwallet->MarkDirty(); if (!fGood) @@ -1212,6 +1239,9 @@ UniValue importmulti(const JSONRPCRequest& mainRequest) int64_t scannedTime = pwallet->RescanFromTime(nLowestTimestamp, reserver, true /* update */); pwallet->ReacceptWalletTransactions(); + if (pwallet->IsAbortingRescan()) { + throw JSONRPCError(RPC_MISC_ERROR, "Rescan aborted by user."); + } if (scannedTime > nLowestTimestamp) { std::vector<UniValue> results = response.getValues(); response.clear(); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 56bdc0695c..9b5fb0b062 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -46,14 +46,13 @@ 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())); - for (CWalletRef pwallet : ::vpwallets) { - if (pwallet->GetName() == requestedWallet) { - return pwallet; - } - } - throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Requested wallet does not exist or is not loaded"); + CWallet* pwallet = GetWallet(requestedWallet); + if (!pwallet) throw JSONRPCError(RPC_WALLET_NOT_FOUND, "Requested wallet does not exist or is not loaded"); + return pwallet; } - return ::vpwallets.size() == 1 || (request.fHelp && ::vpwallets.size() > 0) ? ::vpwallets[0] : nullptr; + + std::vector<CWallet*> wallets = GetWallets(); + return wallets.size() == 1 || (request.fHelp && wallets.size() > 0) ? wallets[0] : nullptr; } std::string HelpRequiringPassphrase(CWallet * const pwallet) @@ -67,7 +66,7 @@ bool EnsureWalletIsAvailable(CWallet * const pwallet, bool avoidException) { if (pwallet) return true; if (avoidException) return false; - if (::vpwallets.empty()) { + if (!HasWallets()) { // Note: It isn't currently possible to trigger this error because // wallet RPC methods aren't registered unless a wallet is loaded. But // this error is being kept as a precaution, because it's possible in @@ -2862,8 +2861,7 @@ UniValue listwallets(const JSONRPCRequest& request) UniValue obj(UniValue::VARR); - for (CWalletRef pwallet : vpwallets) { - + for (CWallet* pwallet : GetWallets()) { if (!EnsureWalletIsAvailable(pwallet, request.fHelp)) { return NullUniValue; } diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index 14c5ad7214..99c963a348 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -74,7 +74,7 @@ BOOST_FIXTURE_TEST_CASE(rescan, TestChain100Setup) // after. { CWallet wallet("dummy", WalletDatabase::CreateDummy()); - vpwallets.insert(vpwallets.begin(), &wallet); + 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)); - vpwallets.erase(vpwallets.begin()); + RemoveWallet(&wallet); } } @@ -119,14 +119,14 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup) // will pick up both blocks, not just the first. const int64_t BLOCK_TIME = chainActive.Tip()->GetBlockTimeMax() + 5; SetMockTime(BLOCK_TIME); - coinbaseTxns.emplace_back(*CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]); - coinbaseTxns.emplace_back(*CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]); + m_coinbase_txns.emplace_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]); + m_coinbase_txns.emplace_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]); // Set key birthday to block time increased by the timestamp window, so // rescan will start at the block time. const int64_t KEY_TIME = BLOCK_TIME + TIMESTAMP_WINDOW; SetMockTime(KEY_TIME); - coinbaseTxns.emplace_back(*CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]); + m_coinbase_txns.emplace_back(CreateAndProcessBlock({}, GetScriptForRawPubKey(coinbaseKey.GetPubKey())).vtx[0]); LOCK(cs_main); @@ -140,8 +140,9 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup) JSONRPCRequest request; request.params.setArray(); request.params.push_back((pathTemp / "wallet.backup").string()); - vpwallets.insert(vpwallets.begin(), &wallet); + AddWallet(&wallet); ::dumpwallet(request); + RemoveWallet(&wallet); } // Call importwallet RPC and verify all blocks with timestamps >= BLOCK_TIME @@ -152,21 +153,21 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup) JSONRPCRequest request; request.params.setArray(); request.params.push_back((pathTemp / "wallet.backup").string()); - vpwallets[0] = &wallet; + AddWallet(&wallet); ::importwallet(request); + RemoveWallet(&wallet); LOCK(wallet.cs_wallet); BOOST_CHECK_EQUAL(wallet.mapWallet.size(), 3U); - BOOST_CHECK_EQUAL(coinbaseTxns.size(), 103U); - for (size_t i = 0; i < coinbaseTxns.size(); ++i) { - bool found = wallet.GetWalletTx(coinbaseTxns[i].GetHash()); + 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 expected = i >= 100; BOOST_CHECK_EQUAL(found, expected); } } SetMockTime(0); - vpwallets.erase(vpwallets.begin()); } // Check that GetImmatureCredit() returns a newly calculated value instead of @@ -178,7 +179,7 @@ BOOST_FIXTURE_TEST_CASE(importwallet_rescan, TestChain100Setup) BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup) { CWallet wallet("dummy", WalletDatabase::CreateDummy()); - CWalletTx wtx(&wallet, MakeTransactionRef(coinbaseTxns.back())); + CWalletTx wtx(&wallet, m_coinbase_txns.back()); LOCK2(cs_main, wallet.cs_wallet); wtx.hashBlock = chainActive.Tip()->GetBlockHash(); wtx.nIndex = 0; diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 45c85a7912..8c392434fc 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -28,12 +28,50 @@ #include <utilmoneystr.h> #include <wallet/fees.h> +#include <algorithm> #include <assert.h> #include <future> #include <boost/algorithm/string/replace.hpp> -std::vector<CWalletRef> vpwallets; +static std::vector<CWallet*> vpwallets; + +bool AddWallet(CWallet* wallet) +{ + assert(wallet); + std::vector<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) +{ + assert(wallet); + std::vector<CWallet*>::iterator i = std::find(vpwallets.begin(), vpwallets.end(), wallet); + if (i == vpwallets.end()) return false; + vpwallets.erase(i); + return true; +} + +bool HasWallets() +{ + return !vpwallets.empty(); +} + +std::vector<CWallet*> GetWallets() +{ + return vpwallets; +} + +CWallet* GetWallet(const std::string& name) +{ + for (CWallet* wallet : vpwallets) { + if (wallet->GetName() == name) return wallet; + } + return nullptr; +} + /** Transaction fee set by the user */ CFeeRate payTxFee(DEFAULT_TRANSACTION_FEE); unsigned int nTxConfirmTarget = DEFAULT_TX_CONFIRM_TARGET; diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index b85f374a06..dd165de825 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -32,8 +32,11 @@ #include <utility> #include <vector> -typedef CWallet* CWalletRef; -extern std::vector<CWalletRef> vpwallets; +bool AddWallet(CWallet* wallet); +bool RemoveWallet(CWallet* wallet); +bool HasWallets(); +std::vector<CWallet*> GetWallets(); +CWallet* GetWallet(const std::string& name); /** * Settings @@ -268,7 +271,7 @@ public: //Get the marginal bytes of spending the specified output int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* pwallet); -/** +/** * A transaction with a bunch of additional info that only the owner cares about. * It includes any unrecorded transactions needed to link it back to the block chain. */ @@ -653,7 +656,7 @@ struct CoinEligibilityFilter }; class WalletRescanReserver; //forward declarations for ScanForWalletTransactions/RescanFromTime -/** +/** * A CWallet is an extension of a keystore, which also maintains a set of transactions and balances, * and provides the ability to create new transactions. */ @@ -903,7 +906,7 @@ public: void GetKeyBirthTimes(std::map<CTxDestination, int64_t> &mapKeyBirth) const; unsigned int ComputeTimeSmart(const CWalletTx& wtx) const; - /** + /** * Increment the next transaction order id * @return next transaction order id */ @@ -1032,7 +1035,7 @@ public: } void GetScriptForMining(std::shared_ptr<CReserveScript> &script); - + unsigned int GetKeyPoolSize() { AssertLockHeld(cs_wallet); // set{Ex,In}ternalKeyPool @@ -1057,7 +1060,7 @@ public: //! Flush wallet (bitdb flush) void Flush(bool shutdown=false); - /** + /** * Address book entry changed. * @note called with lock cs_wallet held. */ @@ -1066,7 +1069,7 @@ public: const std::string &purpose, ChangeType status)> NotifyAddressBookChanged; - /** + /** * Wallet transaction added, removed or updated. * @note called with lock cs_wallet held. */ @@ -1113,7 +1116,7 @@ public: /* Generates a new HD master key (will not be activated) */ CPubKey GenerateNewHDMasterKey(); - + /* 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 caller must ensure the current wallet version is correct before calling @@ -1184,7 +1187,7 @@ public: }; -/** +/** * DEPRECATED Account information. * Stored in wallet with key "acc"+string account name. */ @@ -1230,10 +1233,10 @@ std::vector<CTxDestination> GetAllDestinationsForKey(const CPubKey& key); class WalletRescanReserver { private: - CWalletRef m_wallet; + CWallet* m_wallet; bool m_could_reserve; public: - explicit WalletRescanReserver(CWalletRef w) : m_wallet(w), m_could_reserve(false) {} + explicit WalletRescanReserver(CWallet* w) : m_wallet(w), m_could_reserve(false) {} bool reserve() { diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index bcc7cf877d..5b275131af 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -756,7 +756,7 @@ void MaybeCompactWalletDB() return; } - for (CWalletRef pwallet : vpwallets) { + for (CWallet* pwallet : GetWallets()) { WalletDatabase& dbh = pwallet->GetDBHandle(); unsigned int nUpdateCounter = dbh.nUpdateCounter; diff --git a/src/walletinitinterface.h b/src/walletinitinterface.h index c7eee37ce5..5bfde6faaf 100644 --- a/src/walletinitinterface.h +++ b/src/walletinitinterface.h @@ -13,23 +13,23 @@ class CRPCTable; class WalletInitInterface { public: /** Get wallet help string */ - virtual std::string GetHelpString(bool showDebug) = 0; + virtual std::string GetHelpString(bool showDebug) const = 0; /** Check wallet parameter interaction */ - virtual bool ParameterInteraction() = 0; + virtual bool ParameterInteraction() const = 0; /** Register wallet RPC*/ - virtual void RegisterRPC(CRPCTable &) = 0; + virtual void RegisterRPC(CRPCTable &) const = 0; /** Verify wallets */ - virtual bool Verify() = 0; + virtual bool Verify() const = 0; /** Open wallets*/ - virtual bool Open() = 0; + virtual bool Open() const = 0; /** Start wallets*/ - virtual void Start(CScheduler& scheduler) = 0; + virtual void Start(CScheduler& scheduler) const = 0; /** Flush Wallets*/ - virtual void Flush() = 0; + virtual void Flush() const = 0; /** Stop Wallets*/ - virtual void Stop() = 0; + virtual void Stop() const = 0; /** Close wallets */ - virtual void Close() = 0; + virtual void Close() const = 0; virtual ~WalletInitInterface() {} }; |