diff options
Diffstat (limited to 'src')
111 files changed, 998 insertions, 745 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 627df97cad..13812de0e7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -496,7 +496,6 @@ libbitcoin_util_a_SOURCES = \ support/lockedpool.cpp \ chainparamsbase.cpp \ clientversion.cpp \ - compat/glibc_sanity_fdelt.cpp \ compat/glibc_sanity.cpp \ compat/glibcxx_sanity.cpp \ compat/strnlen.cpp \ diff --git a/src/Makefile.leveldb.include b/src/Makefile.leveldb.include index 04b53471e4..8a28f4f249 100644 --- a/src/Makefile.leveldb.include +++ b/src/Makefile.leveldb.include @@ -36,7 +36,7 @@ LEVELDB_CPPFLAGS_INT += -DLEVELDB_PLATFORM_POSIX endif leveldb_libleveldb_a_CPPFLAGS = $(AM_CPPFLAGS) $(LEVELDB_CPPFLAGS_INT) $(LEVELDB_CPPFLAGS) -leveldb_libleveldb_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +leveldb_libleveldb_a_CXXFLAGS = $(filter-out -Wconditional-uninitialized -Werror=conditional-uninitialized -Wsuggest-override -Werror=suggest-override, $(AM_CXXFLAGS)) $(PIE_FLAGS) leveldb_libleveldb_a_SOURCES= leveldb_libleveldb_a_SOURCES += leveldb/port/port_stdcxx.h diff --git a/src/Makefile.test.include b/src/Makefile.test.include index 68ba1bc68e..3a0d4fdc15 100644 --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -9,6 +9,7 @@ FUZZ_TARGETS = \ test/fuzz/address_deserialize \ test/fuzz/addrman_deserialize \ test/fuzz/asmap \ + test/fuzz/asmap_direct \ test/fuzz/banentry_deserialize \ test/fuzz/base_encode_decode \ test/fuzz/bech32 \ @@ -332,6 +333,12 @@ test_fuzz_asmap_LDADD = $(FUZZ_SUITE_LD_COMMON) test_fuzz_asmap_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) test_fuzz_asmap_SOURCES = test/fuzz/asmap.cpp +test_fuzz_asmap_direct_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) +test_fuzz_asmap_direct_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) +test_fuzz_asmap_direct_LDADD = $(FUZZ_SUITE_LD_COMMON) +test_fuzz_asmap_direct_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +test_fuzz_asmap_direct_SOURCES = test/fuzz/asmap_direct.cpp + test_fuzz_banentry_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DBANENTRY_DESERIALIZE=1 test_fuzz_banentry_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) test_fuzz_banentry_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON) diff --git a/src/addrman.cpp b/src/addrman.cpp index 02fb2fd491..7aba340d9d 100644 --- a/src/addrman.cpp +++ b/src/addrman.cpp @@ -644,5 +644,9 @@ std::vector<bool> CAddrMan::DecodeAsmap(fs::path path) bits.push_back((cur_byte >> bit) & 1); } } + if (!SanityCheckASMap(bits)) { + LogPrintf("Sanity check of asmap file %s failed\n", path); + return {}; + } return bits; } diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index 2aa416ca44..43d3f3c5ac 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -56,7 +56,7 @@ static bool AppInit(int argc, char* argv[]) SetupServerArgs(node); std::string error; if (!gArgs.ParseParameters(argc, argv, error)) { - return InitError(strprintf("Error parsing command line arguments: %s\n", error)); + return InitError(Untranslated(strprintf("Error parsing command line arguments: %s\n", error))); } // Process help and version before taking care about datadir @@ -80,22 +80,22 @@ static bool AppInit(int argc, char* argv[]) try { if (!CheckDataDirOption()) { - return InitError(strprintf("Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", ""))); + return InitError(Untranslated(strprintf("Specified data directory \"%s\" does not exist.\n", gArgs.GetArg("-datadir", "")))); } if (!gArgs.ReadConfigFiles(error, true)) { - return InitError(strprintf("Error reading configuration file: %s\n", error)); + return InitError(Untranslated(strprintf("Error reading configuration file: %s\n", error))); } // Check for -chain, -testnet or -regtest parameter (Params() calls are only valid after this clause) try { SelectParams(gArgs.GetChainName()); } catch (const std::exception& e) { - return InitError(strprintf("%s\n", e.what())); + return InitError(Untranslated(strprintf("%s\n", e.what()))); } // Error out when loose non-argument tokens are encountered on command line for (int i = 1; i < argc; i++) { if (!IsSwitchChar(argv[i][0])) { - return InitError(strprintf("Command line contains unexpected token '%s', see bitcoind -h for a list of options.\n", argv[i])); + return InitError(Untranslated(strprintf("Command line contains unexpected token '%s', see bitcoind -h for a list of options.\n", argv[i]))); } } @@ -130,13 +130,13 @@ static bool AppInit(int argc, char* argv[]) // Daemonize if (daemon(1, 0)) { // don't chdir (1), do close FDs (0) - return InitError(strprintf("daemon() failed: %s\n", strerror(errno))); + return InitError(Untranslated(strprintf("daemon() failed: %s\n", strerror(errno)))); } #if defined(MAC_OSX) #pragma GCC diagnostic pop #endif #else - return InitError("-daemon is not supported on this operating system\n"); + return InitError(Untranslated("-daemon is not supported on this operating system\n")); #endif // HAVE_DECL_DAEMON } // Lock data directory after daemonization diff --git a/src/bloom.cpp b/src/bloom.cpp index 30af507243..54fcf487e4 100644 --- a/src/bloom.cpp +++ b/src/bloom.cpp @@ -31,8 +31,6 @@ CBloomFilter::CBloomFilter(const unsigned int nElements, const double nFPRate, c * Again, we ignore filter parameters which will create a bloom filter with more hash functions than the protocol limits * See https://en.wikipedia.org/wiki/Bloom_filter for an explanation of these formulas */ - isFull(false), - isEmpty(true), nHashFuncs(std::min((unsigned int)(vData.size() * 8 / nElements * LN2), MAX_HASH_FUNCS)), nTweak(nTweakIn), nFlags(nFlagsIn) @@ -47,7 +45,7 @@ inline unsigned int CBloomFilter::Hash(unsigned int nHashNum, const std::vector< void CBloomFilter::insert(const std::vector<unsigned char>& vKey) { - if (isFull) + if (vData.empty()) // Avoid divide-by-zero (CVE-2013-5700) return; for (unsigned int i = 0; i < nHashFuncs; i++) { @@ -55,7 +53,6 @@ void CBloomFilter::insert(const std::vector<unsigned char>& vKey) // Sets bit nIndex of vData vData[nIndex >> 3] |= (1 << (7 & nIndex)); } - isEmpty = false; } void CBloomFilter::insert(const COutPoint& outpoint) @@ -74,10 +71,8 @@ void CBloomFilter::insert(const uint256& hash) bool CBloomFilter::contains(const std::vector<unsigned char>& vKey) const { - if (isFull) + if (vData.empty()) // Avoid divide-by-zero (CVE-2013-5700) return true; - if (isEmpty) - return false; for (unsigned int i = 0; i < nHashFuncs; i++) { unsigned int nIndex = Hash(i, vKey); @@ -112,10 +107,8 @@ bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx) bool fFound = false; // Match if the filter contains the hash of tx // for finding tx when they appear in a block - if (isFull) + if (vData.empty()) // zero-size = "match-all" filter return true; - if (isEmpty) - return false; const uint256& hash = tx.GetHash(); if (contains(hash)) fFound = true; @@ -177,19 +170,6 @@ bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx) return false; } -void CBloomFilter::UpdateEmptyFull() -{ - bool full = true; - bool empty = true; - for (unsigned int i = 0; i < vData.size(); i++) - { - full &= vData[i] == 0xff; - empty &= vData[i] == 0; - } - isFull = full; - isEmpty = empty; -} - CRollingBloomFilter::CRollingBloomFilter(const unsigned int nElements, const double fpRate) { double logFpRate = log(fpRate); diff --git a/src/bloom.h b/src/bloom.h index 8e3b7be54d..9173b80d66 100644 --- a/src/bloom.h +++ b/src/bloom.h @@ -45,8 +45,6 @@ class CBloomFilter { private: std::vector<unsigned char> vData; - bool isFull; - bool isEmpty; unsigned int nHashFuncs; unsigned int nTweak; unsigned char nFlags; @@ -64,7 +62,7 @@ public: * nFlags should be one of the BLOOM_UPDATE_* enums (not _MASK) */ CBloomFilter(const unsigned int nElements, const double nFPRate, const unsigned int nTweak, unsigned char nFlagsIn); - CBloomFilter() : isFull(true), isEmpty(false), nHashFuncs(0), nTweak(0), nFlags(0) {} + CBloomFilter() : nHashFuncs(0), nTweak(0), nFlags(0) {} ADD_SERIALIZE_METHODS; @@ -90,9 +88,6 @@ public: //! Also adds any outputs which match the filter to the filter (to match their spending txes) bool IsRelevantAndUpdate(const CTransaction& tx); - - //! Checks for empty and full filters to avoid wasting cpu - void UpdateEmptyFull(); }; /** diff --git a/src/clientversion.cpp b/src/clientversion.cpp index 5d9eaea6d0..993967a180 100644 --- a/src/clientversion.cpp +++ b/src/clientversion.cpp @@ -14,59 +14,34 @@ */ const std::string CLIENT_NAME("Satoshi"); -/** - * Client version number - */ -#define CLIENT_VERSION_SUFFIX "" - - -/** - * The following part of the code determines the CLIENT_BUILD variable. - * Several mechanisms are used for this: - * * first, if HAVE_BUILD_INFO is defined, include build.h, a file that is - * generated by the build environment, possibly containing the output - * of git-describe in a macro called BUILD_DESC - * * secondly, if this is an exported version of the code, GIT_ARCHIVE will - * be defined (automatically using the export-subst git attribute), and - * GIT_COMMIT will contain the commit id. - * * then, three options exist for determining CLIENT_BUILD: - * * if BUILD_DESC is defined, use that literally (output of git-describe) - * * if not, but GIT_COMMIT is defined, use v[maj].[min].[rev].[build]-g[commit] - * * otherwise, use v[maj].[min].[rev].[build]-unk - * finally CLIENT_VERSION_SUFFIX is added - */ -//! First, include build.h if requested #ifdef HAVE_BUILD_INFO #include <obj/build.h> +// The <obj/build.h>, which is generated by the build environment (share/genbuild.sh), +// could contain only one line of the following: +// - "#define BUILD_GIT_TAG ...", if the top commit is tagged +// - "#define BUILD_GIT_COMMIT ...", if the top commit is not tagged +// - "// No build information available", if proper git information is not available #endif -//! git will put "#define GIT_ARCHIVE 1" on the next line inside archives. $Format:%n#define GIT_ARCHIVE 1$ -#ifdef GIT_ARCHIVE -#define GIT_COMMIT_ID "$Format:%H$" -#define GIT_COMMIT_DATE "$Format:%cD$" -#endif - -#define BUILD_DESC_WITH_SUFFIX(maj, min, rev, build, suffix) \ - "v" DO_STRINGIZE(maj) "." DO_STRINGIZE(min) "." DO_STRINGIZE(rev) "." DO_STRINGIZE(build) "-" DO_STRINGIZE(suffix) - -#define BUILD_DESC_FROM_COMMIT(maj, min, rev, build, commit) \ - "v" DO_STRINGIZE(maj) "." DO_STRINGIZE(min) "." DO_STRINGIZE(rev) "." DO_STRINGIZE(build) "-g" commit +//! git will put "#define GIT_COMMIT_ID ..." on the next line inside archives. $Format:%n#define GIT_COMMIT_ID "%H"$ -#define BUILD_DESC_FROM_UNKNOWN(maj, min, rev, build) \ - "v" DO_STRINGIZE(maj) "." DO_STRINGIZE(min) "." DO_STRINGIZE(rev) "." DO_STRINGIZE(build) "-unk" - -#ifndef BUILD_DESC -#ifdef BUILD_SUFFIX -#define BUILD_DESC BUILD_DESC_WITH_SUFFIX(CLIENT_VERSION_MAJOR, CLIENT_VERSION_MINOR, CLIENT_VERSION_REVISION, CLIENT_VERSION_BUILD, BUILD_SUFFIX) -#elif defined(GIT_COMMIT_ID) -#define BUILD_DESC BUILD_DESC_FROM_COMMIT(CLIENT_VERSION_MAJOR, CLIENT_VERSION_MINOR, CLIENT_VERSION_REVISION, CLIENT_VERSION_BUILD, GIT_COMMIT_ID) +#ifdef BUILD_GIT_TAG + #define BUILD_DESC BUILD_GIT_TAG + #define BUILD_SUFFIX "" #else -#define BUILD_DESC BUILD_DESC_FROM_UNKNOWN(CLIENT_VERSION_MAJOR, CLIENT_VERSION_MINOR, CLIENT_VERSION_REVISION, CLIENT_VERSION_BUILD) -#endif + #define BUILD_DESC "v" STRINGIZE(CLIENT_VERSION_MAJOR) "." STRINGIZE(CLIENT_VERSION_MINOR) \ + "." STRINGIZE(CLIENT_VERSION_REVISION) "." STRINGIZE(CLIENT_VERSION_BUILD) + #ifdef BUILD_GIT_COMMIT + #define BUILD_SUFFIX "-" BUILD_GIT_COMMIT + #elif defined(GIT_COMMIT_ID) + #define BUILD_SUFFIX "-g" GIT_COMMIT_ID + #else + #define BUILD_SUFFIX "-unk" + #endif #endif -const std::string CLIENT_BUILD(BUILD_DESC CLIENT_VERSION_SUFFIX); +const std::string CLIENT_BUILD(BUILD_DESC BUILD_SUFFIX); static std::string FormatVersion(int nVersion) { diff --git a/src/coins.h b/src/coins.h index dcec741998..a3f34bb0ee 100644 --- a/src/coins.h +++ b/src/coins.h @@ -6,11 +6,11 @@ #ifndef BITCOIN_COINS_H #define BITCOIN_COINS_H -#include <primitives/transaction.h> #include <compressor.h> #include <core_memusage.h> #include <crypto/siphash.h> #include <memusage.h> +#include <primitives/transaction.h> #include <serialize.h> #include <uint256.h> @@ -25,7 +25,7 @@ * * Serialized format: * - VARINT((coinbase ? 1 : 0) | (height << 1)) - * - the non-spent CTxOut (via CTxOutCompressor) + * - the non-spent CTxOut (via TxOutCompression) */ class Coin { diff --git a/src/compat/glibc_compat.cpp b/src/compat/glibc_compat.cpp index 4de4fd7f45..d17de33e86 100644 --- a/src/compat/glibc_compat.cpp +++ b/src/compat/glibc_compat.cpp @@ -9,10 +9,6 @@ #include <cstddef> #include <cstdint> -#if defined(HAVE_SYS_SELECT_H) -#include <sys/select.h> -#endif - // Prior to GLIBC_2.14, memcpy was aliased to memmove. extern "C" void* memmove(void* a, const void* b, size_t c); extern "C" void* memcpy(void* a, const void* b, size_t c) @@ -20,15 +16,6 @@ extern "C" void* memcpy(void* a, const void* b, size_t c) return memmove(a, b, c); } -extern "C" void __chk_fail(void) __attribute__((__noreturn__)); -extern "C" FDELT_TYPE __fdelt_warn(FDELT_TYPE a) -{ - if (a >= FD_SETSIZE) - __chk_fail(); - return a / __NFDBITS; -} -extern "C" FDELT_TYPE __fdelt_chk(FDELT_TYPE) __attribute__((weak, alias("__fdelt_warn"))); - #if defined(__i386__) || defined(__arm__) extern "C" int64_t __udivmoddi4(uint64_t u, uint64_t v, uint64_t* rp); diff --git a/src/compat/glibc_sanity.cpp b/src/compat/glibc_sanity.cpp index cc74f28899..0367b9a53f 100644 --- a/src/compat/glibc_sanity.cpp +++ b/src/compat/glibc_sanity.cpp @@ -8,10 +8,6 @@ #include <cstddef> -#if defined(HAVE_SYS_SELECT_H) -bool sanity_test_fdelt(); -#endif - extern "C" void* memcpy(void* a, const void* b, size_t c); void* memcpy_int(void* a, const void* b, size_t c) { @@ -45,9 +41,5 @@ bool sanity_test_memcpy() bool glibc_sanity_test() { -#if defined(HAVE_SYS_SELECT_H) - if (!sanity_test_fdelt()) - return false; -#endif return sanity_test_memcpy<1025>(); } diff --git a/src/compat/glibc_sanity_fdelt.cpp b/src/compat/glibc_sanity_fdelt.cpp deleted file mode 100644 index 87140d0c71..0000000000 --- a/src/compat/glibc_sanity_fdelt.cpp +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2009-2019 The Bitcoin Core developers -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#if defined(HAVE_CONFIG_H) -#include <config/bitcoin-config.h> -#endif - -#if defined(HAVE_SYS_SELECT_H) -#ifdef HAVE_CSTRING_DEPENDENT_FD_ZERO -#include <cstring> -#endif -#include <sys/select.h> - -// trigger: Call FD_SET to trigger __fdelt_chk. FORTIFY_SOURCE must be defined -// as >0 and optimizations must be set to at least -O2. -// test: Add a file descriptor to an empty fd_set. Verify that it has been -// correctly added. -bool sanity_test_fdelt() -{ - fd_set fds; - FD_ZERO(&fds); - FD_SET(0, &fds); - return FD_ISSET(0, &fds); -} -#endif diff --git a/src/httprpc.cpp b/src/httprpc.cpp index 60c4d06f12..3c3e6e5bba 100644 --- a/src/httprpc.cpp +++ b/src/httprpc.cpp @@ -252,7 +252,7 @@ static bool InitRPCAuthentication() LogPrintf("No rpcpassword set - using random cookie authentication.\n"); if (!GenerateAuthCookie(&strRPCUserColonPass)) { uiInterface.ThreadSafeMessageBox( - _("Error: A fatal internal error occurred, see debug.log for details").translated, // Same message as AbortNode + _("Error: A fatal internal error occurred, see debug.log for details"), // Same message as AbortNode "", CClientUIInterface::MSG_ERROR); return false; } diff --git a/src/httpserver.cpp b/src/httpserver.cpp index 176284d103..ffe246b241 100644 --- a/src/httpserver.cpp +++ b/src/httpserver.cpp @@ -6,14 +6,15 @@ #include <chainparamsbase.h> #include <compat.h> -#include <util/threadnames.h> -#include <util/system.h> -#include <util/strencodings.h> #include <netbase.h> #include <rpc/protocol.h> // For HTTP status codes #include <shutdown.h> #include <sync.h> #include <ui_interface.h> +#include <util/strencodings.h> +#include <util/system.h> +#include <util/threadnames.h> +#include <util/translation.h> #include <deque> #include <memory> @@ -175,7 +176,7 @@ static bool InitHTTPAllowList() LookupSubNet(strAllow, subnet); if (!subnet.IsValid()) { uiInterface.ThreadSafeMessageBox( - strprintf("Invalid -rpcallowip subnet specification: %s. Valid are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24).", strAllow), + strprintf(Untranslated("Invalid -rpcallowip subnet specification: %s. Valid are a single IP (e.g. 1.2.3.4), a network/netmask (e.g. 1.2.3.4/255.255.255.0) or a network/CIDR (e.g. 1.2.3.4/24)."), strAllow), "", CClientUIInterface::MSG_ERROR); return false; } diff --git a/src/index/base.cpp b/src/index/base.cpp index 7bff463f5b..74ea421e13 100644 --- a/src/index/base.cpp +++ b/src/index/base.cpp @@ -8,6 +8,7 @@ #include <tinyformat.h> #include <ui_interface.h> #include <util/system.h> +#include <util/translation.h> #include <validation.h> #include <warnings.h> @@ -23,7 +24,7 @@ static void FatalError(const char* fmt, const Args&... args) SetMiscWarning(strMessage); LogPrintf("*** %s\n", strMessage); uiInterface.ThreadSafeMessageBox( - "Error: A fatal internal error occurred, see debug.log for details", + Untranslated("Error: A fatal internal error occurred, see debug.log for details"), "", CClientUIInterface::MSG_ERROR); StartShutdown(); } diff --git a/src/init.cpp b/src/init.cpp index 3e2ee4d425..3b97ba08d9 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -121,7 +121,7 @@ NODISCARD static bool CreatePidFile() #endif return true; } else { - return InitError(strprintf(_("Unable to create the PID file '%s': %s").translated, GetPidFile().string(), std::strerror(errno))); + return InitError(strprintf(_("Unable to create the PID file '%s': %s"), GetPidFile().string(), std::strerror(errno))); } } @@ -446,6 +446,7 @@ void SetupServerArgs(NodeContext& node) gArgs.AddArg("-onion=<ip:port>", "Use separate SOCKS5 proxy to reach peers via Tor hidden services, set -noonion to disable (default: -proxy)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); gArgs.AddArg("-onlynet=<net>", "Make outgoing connections only through network <net> (ipv4, ipv6 or onion). Incoming connections are not affected by this option. This option can be specified multiple times to allow multiple networks.", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); gArgs.AddArg("-peerbloomfilters", strprintf("Support filtering of blocks and transaction with bloom filters (default: %u)", DEFAULT_PEERBLOOMFILTERS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); + gArgs.AddArg("-peerblockfilters", strprintf("Serve compact block filters to peers per BIP 157 (default: %u)", DEFAULT_PEERBLOCKFILTERS), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); gArgs.AddArg("-permitbaremultisig", strprintf("Relay non-P2SH multisig (default: %u)", DEFAULT_PERMIT_BAREMULTISIG), ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); gArgs.AddArg("-port=<port>", strprintf("Listen for connections on <port> (default: %u, testnet: %u, regtest: %u)", defaultChainParams->GetDefaultPort(), testnetChainParams->GetDefaultPort(), regtestChainParams->GetDefaultPort()), ArgsManager::ALLOW_ANY | ArgsManager::NETWORK_ONLY, OptionsCategory::CONNECTION); gArgs.AddArg("-proxy=<ip:port>", "Connect through SOCKS5 proxy, set -noproxy to disable (default: disabled)", ArgsManager::ALLOW_ANY, OptionsCategory::CONNECTION); @@ -768,17 +769,15 @@ static void ThreadImport(std::vector<fs::path> vImportFiles) */ static bool InitSanityCheck() { - if(!ECC_InitSanityCheck()) { - InitError("Elliptic curve cryptography sanity check failure. Aborting."); - return false; + if (!ECC_InitSanityCheck()) { + return InitError(Untranslated("Elliptic curve cryptography sanity check failure. Aborting.")); } if (!glibc_sanity_test() || !glibcxx_sanity_test()) return false; if (!Random_SanityCheck()) { - InitError("OS cryptographic RNG sanity check failure. Aborting."); - return false; + return InitError(Untranslated("OS cryptographic RNG sanity check failure. Aborting.")); } return true; @@ -929,8 +928,9 @@ bool AppInitBasicSetup() HeapSetInformation(nullptr, HeapEnableTerminationOnCorruption, nullptr, 0); #endif - if (!SetupNetworking()) - return InitError("Initializing networking failed"); + if (!SetupNetworking()) { + return InitError(Untranslated("Initializing networking failed.")); + } #ifndef WIN32 if (!gArgs.GetBoolArg("-sysperms", false)) { @@ -967,16 +967,16 @@ bool AppInitParameterInteraction() // on the command line or in this network's section of the config file. std::string network = gArgs.GetChainName(); for (const auto& arg : gArgs.GetUnsuitableSectionOnlyArgs()) { - return InitError(strprintf(_("Config setting for %s only applied on %s network when in [%s] section.").translated, arg, network, network)); + return InitError(strprintf(_("Config setting for %s only applied on %s network when in [%s] section."), arg, network, network)); } // Warn if unrecognized section name are present in the config file. for (const auto& section : gArgs.GetUnrecognizedSections()) { - InitWarning(strprintf("%s:%i " + _("Section [%s] is not recognized.").translated, section.m_file, section.m_line, section.m_name)); + InitWarning(strprintf(Untranslated("%s:%i ") + _("Section [%s] is not recognized."), section.m_file, section.m_line, section.m_name)); } if (!fs::is_directory(GetBlocksDir())) { - return InitError(strprintf(_("Specified blocks directory \"%s\" does not exist.").translated, gArgs.GetArg("-blocksdir", ""))); + return InitError(strprintf(_("Specified blocks directory \"%s\" does not exist."), gArgs.GetArg("-blocksdir", ""))); } // parse and validate enabled filter types @@ -988,25 +988,32 @@ bool AppInitParameterInteraction() for (const auto& name : names) { BlockFilterType filter_type; if (!BlockFilterTypeByName(name, filter_type)) { - return InitError(strprintf(_("Unknown -blockfilterindex value %s.").translated, name)); + return InitError(strprintf(_("Unknown -blockfilterindex value %s."), name)); } g_enabled_filter_types.insert(filter_type); } } + // Basic filters are the only supported filters. The basic filters index must be enabled + // to serve compact filters + if (gArgs.GetBoolArg("-peerblockfilters", DEFAULT_PEERBLOCKFILTERS) && + g_enabled_filter_types.count(BlockFilterType::BASIC) != 1) { + return InitError(_("Cannot set -peerblockfilters without -blockfilterindex.")); + } + // if using block pruning, then disallow txindex if (gArgs.GetArg("-prune", 0)) { if (gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX)) - return InitError(_("Prune mode is incompatible with -txindex.").translated); + return InitError(_("Prune mode is incompatible with -txindex.")); if (!g_enabled_filter_types.empty()) { - return InitError(_("Prune mode is incompatible with -blockfilterindex.").translated); + return InitError(_("Prune mode is incompatible with -blockfilterindex.")); } } // -bind and -whitebind can't be set when not listening size_t nUserBind = gArgs.GetArgs("-bind").size() + gArgs.GetArgs("-whitebind").size(); if (nUserBind != 0 && !gArgs.GetBoolArg("-listen", DEFAULT_LISTEN)) { - return InitError("Cannot set -bind or -whitebind together with -listen=0"); + return InitError(Untranslated("Cannot set -bind or -whitebind together with -listen=0")); } // Make sure enough file descriptors are available @@ -1024,11 +1031,11 @@ bool AppInitParameterInteraction() #endif nMaxConnections = std::max(std::min<int>(nMaxConnections, fd_max - nBind - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS), 0); if (nFD < MIN_CORE_FILEDESCRIPTORS) - return InitError(_("Not enough file descriptors available.").translated); + return InitError(_("Not enough file descriptors available.")); nMaxConnections = std::min(nFD - MIN_CORE_FILEDESCRIPTORS - MAX_ADDNODE_CONNECTIONS, nMaxConnections); if (nMaxConnections < nUserMaxConnections) - InitWarning(strprintf(_("Reducing -maxconnections from %d to %d, because of system limitations.").translated, nUserMaxConnections, nMaxConnections)); + InitWarning(strprintf(_("Reducing -maxconnections from %d to %d, because of system limitations."), nUserMaxConnections, nMaxConnections)); // ********************************************************* Step 3: parameter-to-internal-flags if (gArgs.IsArgSet("-debug")) { @@ -1039,7 +1046,7 @@ bool AppInitParameterInteraction() [](std::string cat){return cat == "0" || cat == "none";})) { for (const auto& cat : categories) { if (!LogInstance().EnableCategory(cat)) { - InitWarning(strprintf(_("Unsupported logging category %s=%s.").translated, "-debug", cat)); + InitWarning(strprintf(_("Unsupported logging category %s=%s."), "-debug", cat)); } } } @@ -1048,7 +1055,7 @@ bool AppInitParameterInteraction() // Now remove the logging categories which were explicitly excluded for (const std::string& cat : gArgs.GetArgs("-debugexclude")) { if (!LogInstance().DisableCategory(cat)) { - InitWarning(strprintf(_("Unsupported logging category %s=%s.").translated, "-debugexclude", cat)); + InitWarning(strprintf(_("Unsupported logging category %s=%s."), "-debugexclude", cat)); } } @@ -1069,7 +1076,7 @@ bool AppInitParameterInteraction() if (gArgs.IsArgSet("-minimumchainwork")) { const std::string minChainWorkStr = gArgs.GetArg("-minimumchainwork", ""); if (!IsHexNumber(minChainWorkStr)) { - return InitError(strprintf("Invalid non-hex (%s) minimum chain work value specified", minChainWorkStr)); + return InitError(strprintf(Untranslated("Invalid non-hex (%s) minimum chain work value specified"), minChainWorkStr)); } nMinimumChainWork = UintToArith256(uint256S(minChainWorkStr)); } else { @@ -1084,21 +1091,21 @@ bool AppInitParameterInteraction() int64_t nMempoolSizeMax = gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000; int64_t nMempoolSizeMin = gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000 * 40; if (nMempoolSizeMax < 0 || nMempoolSizeMax < nMempoolSizeMin) - return InitError(strprintf(_("-maxmempool must be at least %d MB").translated, std::ceil(nMempoolSizeMin / 1000000.0))); + return InitError(strprintf(_("-maxmempool must be at least %d MB"), std::ceil(nMempoolSizeMin / 1000000.0))); // incremental relay fee sets the minimum feerate increase necessary for BIP 125 replacement in the mempool // and the amount the mempool min fee increases above the feerate of txs evicted due to mempool limiting. if (gArgs.IsArgSet("-incrementalrelayfee")) { CAmount n = 0; if (!ParseMoney(gArgs.GetArg("-incrementalrelayfee", ""), n)) - return InitError(AmountErrMsg("incrementalrelayfee", gArgs.GetArg("-incrementalrelayfee", "")).translated); + return InitError(AmountErrMsg("incrementalrelayfee", gArgs.GetArg("-incrementalrelayfee", ""))); incrementalRelayFee = CFeeRate(n); } // block pruning; get the amount of disk space (in MiB) to allot for block & undo files int64_t nPruneArg = gArgs.GetArg("-prune", 0); if (nPruneArg < 0) { - return InitError(_("Prune cannot be configured with a negative value.").translated); + return InitError(_("Prune cannot be configured with a negative value.")); } nPruneTarget = (uint64_t) nPruneArg * 1024 * 1024; if (nPruneArg == 1) { // manual pruning: -prune=1 @@ -1107,7 +1114,7 @@ bool AppInitParameterInteraction() fPruneMode = true; } else if (nPruneTarget) { if (nPruneTarget < MIN_DISK_SPACE_FOR_BLOCK_FILES) { - return InitError(strprintf(_("Prune configured below the minimum of %d MiB. Please use a higher number.").translated, MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024)); + return InitError(strprintf(_("Prune configured below the minimum of %d MiB. Please use a higher number."), MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024)); } LogPrintf("Prune configured to target %u MiB on disk for block and undo files.\n", nPruneTarget / 1024 / 1024); fPruneMode = true; @@ -1120,13 +1127,13 @@ bool AppInitParameterInteraction() peer_connect_timeout = gArgs.GetArg("-peertimeout", DEFAULT_PEER_CONNECT_TIMEOUT); if (peer_connect_timeout <= 0) { - return InitError("peertimeout cannot be configured with a negative value."); + return InitError(Untranslated("peertimeout cannot be configured with a negative value.")); } if (gArgs.IsArgSet("-minrelaytxfee")) { CAmount n = 0; if (!ParseMoney(gArgs.GetArg("-minrelaytxfee", ""), n)) { - return InitError(AmountErrMsg("minrelaytxfee", gArgs.GetArg("-minrelaytxfee", "")).translated); + return InitError(AmountErrMsg("minrelaytxfee", gArgs.GetArg("-minrelaytxfee", ""))); } // High fee check is done afterward in CWallet::CreateWalletFromFile() ::minRelayTxFee = CFeeRate(n); @@ -1142,7 +1149,7 @@ bool AppInitParameterInteraction() { CAmount n = 0; if (!ParseMoney(gArgs.GetArg("-blockmintxfee", ""), n)) - return InitError(AmountErrMsg("blockmintxfee", gArgs.GetArg("-blockmintxfee", "")).translated); + return InitError(AmountErrMsg("blockmintxfee", gArgs.GetArg("-blockmintxfee", ""))); } // Feerate used to define dust. Shouldn't be changed lightly as old @@ -1151,13 +1158,13 @@ bool AppInitParameterInteraction() { CAmount n = 0; if (!ParseMoney(gArgs.GetArg("-dustrelayfee", ""), n)) - return InitError(AmountErrMsg("dustrelayfee", gArgs.GetArg("-dustrelayfee", "")).translated); + return InitError(AmountErrMsg("dustrelayfee", gArgs.GetArg("-dustrelayfee", ""))); dustRelayFee = CFeeRate(n); } fRequireStandard = !gArgs.GetBoolArg("-acceptnonstdtxn", !chainparams.RequireStandard()); if (!chainparams.IsTestChain() && !fRequireStandard) { - return InitError(strprintf("acceptnonstdtxn is not currently supported for %s chain", chainparams.NetworkIDString())); + return InitError(strprintf(Untranslated("acceptnonstdtxn is not currently supported for %s chain"), chainparams.NetworkIDString())); } nBytesPerSigOp = gArgs.GetArg("-bytespersigop", nBytesPerSigOp); @@ -1174,10 +1181,10 @@ bool AppInitParameterInteraction() nLocalServices = ServiceFlags(nLocalServices | NODE_BLOOM); if (gArgs.GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) < 0) - return InitError("rpcserialversion must be non-negative."); + return InitError(Untranslated("rpcserialversion must be non-negative.")); if (gArgs.GetArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) > 1) - return InitError("unknown rpcserialversion requested."); + return InitError(Untranslated("Unknown rpcserialversion requested.")); nMaxTipAge = gArgs.GetArg("-maxtipage", DEFAULT_MAX_TIP_AGE); @@ -1189,10 +1196,10 @@ static bool LockDataDirectory(bool probeOnly) // Make sure only a single Bitcoin process is using the data directory. fs::path datadir = GetDataDir(); if (!DirIsWritable(datadir)) { - return InitError(strprintf(_("Cannot write to data directory '%s'; check permissions.").translated, datadir.string())); + return InitError(strprintf(_("Cannot write to data directory '%s'; check permissions."), datadir.string())); } if (!LockDirectory(datadir, ".lock", probeOnly)) { - return InitError(strprintf(_("Cannot obtain a lock on data directory %s. %s is probably already running.").translated, datadir.string(), PACKAGE_NAME)); + return InitError(strprintf(_("Cannot obtain a lock on data directory %s. %s is probably already running."), datadir.string(), PACKAGE_NAME)); } return true; } @@ -1210,7 +1217,7 @@ bool AppInitSanityChecks() // Sanity check if (!InitSanityCheck()) - return InitError(strprintf(_("Initialization sanity check failed. %s is shutting down.").translated, PACKAGE_NAME)); + return InitError(strprintf(_("Initialization sanity check failed. %s is shutting down."), PACKAGE_NAME)); // Probe the data directory lock to give an early error message, if possible // We cannot hold the data directory lock here, as the forking for daemon() hasn't yet happened, @@ -1246,7 +1253,7 @@ bool AppInitMain(NodeContext& node) } } if (!LogInstance().StartLogging()) { - return InitError(strprintf("Could not open debug log file %s", + return InitError(strprintf(Untranslated("Could not open debug log file %s"), LogInstance().m_file_path.string())); } @@ -1261,7 +1268,7 @@ bool AppInitMain(NodeContext& node) LogPrintf("Config file: %s\n", config_file_path.string()); } else if (gArgs.IsArgSet("-conf")) { // Warn if no conf file exists at path provided by user - InitWarning(strprintf(_("The specified config file %s does not exist\n").translated, config_file_path.string())); + InitWarning(strprintf(_("The specified config file %s does not exist\n"), config_file_path.string())); } else { // Not categorizing as "Warning" because it's the default behavior LogPrintf("Config file: %s (not found, skipping)\n", config_file_path.string()); @@ -1346,7 +1353,7 @@ bool AppInitMain(NodeContext& node) { uiInterface.InitMessage_connect(SetRPCWarmupStatus); if (!AppInitServers()) - return InitError(_("Unable to start HTTP server. See debug log for details.").translated); + return InitError(_("Unable to start HTTP server. See debug log for details.")); } // ********************************************************* Step 5: verify wallet database integrity @@ -1378,12 +1385,12 @@ bool AppInitMain(NodeContext& node) std::vector<std::string> uacomments; for (const std::string& cmt : gArgs.GetArgs("-uacomment")) { if (cmt != SanitizeString(cmt, SAFE_CHARS_UA_COMMENT)) - return InitError(strprintf(_("User Agent comment (%s) contains unsafe characters.").translated, cmt)); + return InitError(strprintf(_("User Agent comment (%s) contains unsafe characters."), cmt)); uacomments.push_back(cmt); } strSubVersion = FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, uacomments); if (strSubVersion.size() > MAX_SUBVERSION_LENGTH) { - return InitError(strprintf(_("Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments.").translated, + return InitError(strprintf(_("Total length of network version string (%i) exceeds maximum length (%i). Reduce the number or size of uacomments."), strSubVersion.size(), MAX_SUBVERSION_LENGTH)); } @@ -1392,7 +1399,7 @@ bool AppInitMain(NodeContext& node) for (const std::string& snet : gArgs.GetArgs("-onlynet")) { enum Network net = ParseNetwork(snet); if (net == NET_UNROUTABLE) - return InitError(strprintf(_("Unknown network specified in -onlynet: '%s'").translated, snet)); + return InitError(strprintf(_("Unknown network specified in -onlynet: '%s'"), snet)); nets.insert(net); } for (int n = 0; n < NET_MAX; n++) { @@ -1413,12 +1420,12 @@ bool AppInitMain(NodeContext& node) if (proxyArg != "" && proxyArg != "0") { CService proxyAddr; if (!Lookup(proxyArg, proxyAddr, 9050, fNameLookup)) { - return InitError(strprintf(_("Invalid -proxy address or hostname: '%s'").translated, proxyArg)); + return InitError(strprintf(_("Invalid -proxy address or hostname: '%s'"), proxyArg)); } proxyType addrProxy = proxyType(proxyAddr, proxyRandomize); if (!addrProxy.IsValid()) - return InitError(strprintf(_("Invalid -proxy address or hostname: '%s'").translated, proxyArg)); + return InitError(strprintf(_("Invalid -proxy address or hostname: '%s'"), proxyArg)); SetProxy(NET_IPV4, addrProxy); SetProxy(NET_IPV6, addrProxy); @@ -1437,11 +1444,11 @@ bool AppInitMain(NodeContext& node) } else { CService onionProxy; if (!Lookup(onionArg, onionProxy, 9050, fNameLookup)) { - return InitError(strprintf(_("Invalid -onion address or hostname: '%s'").translated, onionArg)); + return InitError(strprintf(_("Invalid -onion address or hostname: '%s'"), onionArg)); } proxyType addrOnion = proxyType(onionProxy, proxyRandomize); if (!addrOnion.IsValid()) - return InitError(strprintf(_("Invalid -onion address or hostname: '%s'").translated, onionArg)); + return InitError(strprintf(_("Invalid -onion address or hostname: '%s'"), onionArg)); SetProxy(NET_ONION, addrOnion); SetReachable(NET_ONION, true); } @@ -1457,7 +1464,7 @@ bool AppInitMain(NodeContext& node) if (Lookup(strAddr, addrLocal, GetListenPort(), fNameLookup) && addrLocal.IsValid()) AddLocal(addrLocal, LOCAL_MANUAL); else - return InitError(ResolveErrMsg("externalip", strAddr)); + return InitError(Untranslated(ResolveErrMsg("externalip", strAddr))); } // Read asmap file if configured @@ -1470,12 +1477,12 @@ bool AppInitMain(NodeContext& node) asmap_path = GetDataDir() / asmap_path; } if (!fs::exists(asmap_path)) { - InitError(strprintf(_("Could not find asmap file %s").translated, asmap_path)); + InitError(strprintf(_("Could not find asmap file %s"), asmap_path)); return false; } std::vector<bool> asmap = CAddrMan::DecodeAsmap(asmap_path); if (asmap.size() == 0) { - InitError(strprintf(_("Could not parse asmap file %s").translated, asmap_path)); + InitError(strprintf(_("Could not parse asmap file %s"), asmap_path)); return false; } const uint256 asmap_version = SerializeHash(asmap); @@ -1542,7 +1549,7 @@ bool AppInitMain(NodeContext& node) auto is_coinsview_empty = [&](CChainState* chainstate) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { return fReset || fReindexChainState || chainstate->CoinsTip().GetBestBlock().IsNull(); }; - std::string strLoadError; + bilingual_str strLoadError; uiInterface.InitMessage(_("Loading block index...").translated); @@ -1573,7 +1580,7 @@ bool AppInitMain(NodeContext& node) // From here on out fReindex and fReset mean something different! if (!LoadBlockIndex(chainparams)) { if (ShutdownRequested()) break; - strLoadError = _("Error loading block database").translated; + strLoadError = _("Error loading block database"); break; } @@ -1581,13 +1588,13 @@ bool AppInitMain(NodeContext& node) // (we're likely using a testnet datadir, or the other way around). if (!::BlockIndex().empty() && !LookupBlockIndex(chainparams.GetConsensus().hashGenesisBlock)) { - return InitError(_("Incorrect or no genesis block found. Wrong datadir for network?").translated); + return InitError(_("Incorrect or no genesis block found. Wrong datadir for network?")); } // Check for changed -prune state. What we are concerned about is a user who has pruned blocks // in the past, but is now trying to run unpruned. if (fHavePruned && !fPruneMode) { - strLoadError = _("You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain").translated; + strLoadError = _("You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain"); break; } @@ -1596,7 +1603,7 @@ bool AppInitMain(NodeContext& node) // (otherwise we use the one already on disk). // This is called again in ThreadImport after the reindex completes. if (!fReindex && !LoadGenesisBlock(chainparams)) { - strLoadError = _("Error initializing block database").translated; + strLoadError = _("Error initializing block database"); break; } @@ -1614,21 +1621,21 @@ bool AppInitMain(NodeContext& node) chainstate->CoinsErrorCatcher().AddReadErrCallback([]() { uiInterface.ThreadSafeMessageBox( - _("Error reading from database, shutting down.").translated, + _("Error reading from database, shutting down."), "", CClientUIInterface::MSG_ERROR); }); // If necessary, upgrade from older database format. // This is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate if (!chainstate->CoinsDB().Upgrade()) { - strLoadError = _("Error upgrading chainstate database").translated; + strLoadError = _("Error upgrading chainstate database"); failed_chainstate_init = true; break; } // ReplayBlocks is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate if (!chainstate->ReplayBlocks(chainparams)) { - strLoadError = _("Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.").translated; + strLoadError = _("Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate."); failed_chainstate_init = true; break; } @@ -1640,7 +1647,7 @@ bool AppInitMain(NodeContext& node) if (!is_coinsview_empty(chainstate)) { // LoadChainTip initializes the chain based on CoinsTip()'s best block if (!chainstate->LoadChainTip(chainparams)) { - strLoadError = _("Error initializing block database").translated; + strLoadError = _("Error initializing block database"); failed_chainstate_init = true; break; // out of the per-chainstate loop } @@ -1653,7 +1660,7 @@ bool AppInitMain(NodeContext& node) } } catch (const std::exception& e) { LogPrintf("%s\n", e.what()); - strLoadError = _("Error opening block database").translated; + strLoadError = _("Error opening block database"); break; } @@ -1669,7 +1676,7 @@ bool AppInitMain(NodeContext& node) if (!chainstate->RewindBlockIndex(chainparams)) { strLoadError = _( "Unable to rewind the database to a pre-fork state. " - "You will need to redownload the blockchain").translated; + "You will need to redownload the blockchain"); failed_rewind = true; break; // out of the per-chainstate loop } @@ -1698,7 +1705,7 @@ bool AppInitMain(NodeContext& node) if (tip && tip->nTime > GetAdjustedTime() + 2 * 60 * 60) { strLoadError = _("The block database contains a block which appears to be from the future. " "This may be due to your computer's date and time being set incorrectly. " - "Only rebuild the block database if you are sure that your computer's date and time are correct").translated; + "Only rebuild the block database if you are sure that your computer's date and time are correct"); failed_verification = true; break; } @@ -1710,7 +1717,7 @@ bool AppInitMain(NodeContext& node) chainparams, &chainstate->CoinsDB(), gArgs.GetArg("-checklevel", DEFAULT_CHECKLEVEL), gArgs.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS))) { - strLoadError = _("Corrupted block database detected").translated; + strLoadError = _("Corrupted block database detected"); failed_verification = true; break; } @@ -1718,7 +1725,7 @@ bool AppInitMain(NodeContext& node) } } catch (const std::exception& e) { LogPrintf("%s\n", e.what()); - strLoadError = _("Error opening block database").translated; + strLoadError = _("Error opening block database"); failed_verification = true; break; } @@ -1733,8 +1740,8 @@ bool AppInitMain(NodeContext& node) // first suggest a reindex if (!fReset) { bool fRet = uiInterface.ThreadSafeQuestion( - strLoadError + ".\n\n" + _("Do you want to rebuild the block database now?").translated, - strLoadError + ".\nPlease restart with -reindex or -reindex-chainstate to recover.", + strLoadError + Untranslated(".\n\n") + _("Do you want to rebuild the block database now?"), + strLoadError.original + ".\nPlease restart with -reindex or -reindex-chainstate to recover.", "", CClientUIInterface::MSG_ERROR | CClientUIInterface::BTN_ABORT); if (fRet) { fReindex = true; @@ -1807,11 +1814,11 @@ bool AppInitMain(NodeContext& node) // ********************************************************* Step 11: import blocks if (!CheckDiskSpace(GetDataDir())) { - InitError(strprintf(_("Error: Disk space is low for %s").translated, GetDataDir())); + InitError(strprintf(_("Error: Disk space is low for %s"), GetDataDir())); return false; } if (!CheckDiskSpace(GetBlocksDir())) { - InitError(strprintf(_("Error: Disk space is low for %s").translated, GetBlocksDir())); + InitError(strprintf(_("Error: Disk space is low for %s"), GetBlocksDir())); return false; } @@ -1880,7 +1887,7 @@ bool AppInitMain(NodeContext& node) connOptions.m_max_outbound_full_relay = std::min(MAX_OUTBOUND_FULL_RELAY_CONNECTIONS, connOptions.nMaxConnections); connOptions.m_max_outbound_block_relay = std::min(MAX_BLOCKS_ONLY_CONNECTIONS, connOptions.nMaxConnections-connOptions.m_max_outbound_full_relay); connOptions.nMaxAddnode = MAX_ADDNODE_CONNECTIONS; - connOptions.nMaxFeeler = 1; + connOptions.nMaxFeeler = MAX_FEELER_CONNECTIONS; connOptions.nBestHeight = chain_active_height; connOptions.uiInterface = &uiInterface; connOptions.m_banman = node.banman.get(); @@ -1896,21 +1903,21 @@ bool AppInitMain(NodeContext& node) for (const std::string& strBind : gArgs.GetArgs("-bind")) { CService addrBind; if (!Lookup(strBind, addrBind, GetListenPort(), false)) { - return InitError(ResolveErrMsg("bind", strBind)); + return InitError(Untranslated(ResolveErrMsg("bind", strBind))); } connOptions.vBinds.push_back(addrBind); } for (const std::string& strBind : gArgs.GetArgs("-whitebind")) { NetWhitebindPermissions whitebind; std::string error; - if (!NetWhitebindPermissions::TryParse(strBind, whitebind, error)) return InitError(error); + if (!NetWhitebindPermissions::TryParse(strBind, whitebind, error)) return InitError(Untranslated(error)); connOptions.vWhiteBinds.push_back(whitebind); } for (const auto& net : gArgs.GetArgs("-whitelist")) { NetWhitelistPermissions subnet; std::string error; - if (!NetWhitelistPermissions::TryParse(net, subnet, error)) return InitError(error); + if (!NetWhitelistPermissions::TryParse(net, subnet, error)) return InitError(Untranslated(error)); connOptions.vWhitelistedRange.push_back(subnet); } diff --git a/src/interfaces/chain.cpp b/src/interfaces/chain.cpp index c5262e4bc0..d8e459a8e8 100644 --- a/src/interfaces/chain.cpp +++ b/src/interfaces/chain.cpp @@ -127,7 +127,7 @@ public: ::tableRPC.appendCommand(m_command.name, &m_command); } - void disconnect() override final + void disconnect() final { if (m_wrapped_command) { m_wrapped_command = nullptr; @@ -344,8 +344,8 @@ public: bool shutdownRequested() override { return ShutdownRequested(); } int64_t getAdjustedTime() override { return GetAdjustedTime(); } void initMessage(const std::string& message) override { ::uiInterface.InitMessage(message); } - void initWarning(const std::string& message) override { InitWarning(message); } - void initError(const std::string& message) override { InitError(message); } + void initWarning(const bilingual_str& message) override { InitWarning(message); } + void initError(const bilingual_str& message) override { InitError(message); } void showProgress(const std::string& title, int progress, bool resume_possible) override { ::uiInterface.ShowProgress(title, progress, resume_possible); diff --git a/src/interfaces/chain.h b/src/interfaces/chain.h index e33fe54ac8..7dfc77db7b 100644 --- a/src/interfaces/chain.h +++ b/src/interfaces/chain.h @@ -21,6 +21,7 @@ class CScheduler; class Coin; class uint256; enum class RBFTransactionState; +struct bilingual_str; struct CBlockLocator; struct FeeCalculation; struct NodeContext; @@ -224,10 +225,10 @@ public: virtual void initMessage(const std::string& message) = 0; //! Send init warning. - virtual void initWarning(const std::string& message) = 0; + virtual void initWarning(const bilingual_str& message) = 0; //! Send init error. - virtual void initError(const std::string& message) = 0; + virtual void initError(const bilingual_str& message) = 0; //! Send progress indicator. virtual void showProgress(const std::string& title, int progress, bool resume_possible) = 0; diff --git a/src/interfaces/node.cpp b/src/interfaces/node.cpp index 8564819e6a..9e603a12cd 100644 --- a/src/interfaces/node.cpp +++ b/src/interfaces/node.cpp @@ -28,6 +28,7 @@ #include <txmempool.h> #include <ui_interface.h> #include <util/system.h> +#include <util/translation.h> #include <validation.h> #include <warnings.h> @@ -54,7 +55,7 @@ namespace { class NodeImpl : public Node { public: - void initError(const std::string& message) override { InitError(message); } + void initError(const std::string& message) override { InitError(Untranslated(message)); } bool parseParameters(int argc, const char* const argv[], std::string& error) override { return gArgs.ParseParameters(argc, argv, error); diff --git a/src/interfaces/node.h b/src/interfaces/node.h index db9b42b293..aef6b19458 100644 --- a/src/interfaces/node.h +++ b/src/interfaces/node.h @@ -213,11 +213,11 @@ public: //! Register handler for message box messages. using MessageBoxFn = - std::function<bool(const std::string& message, const std::string& caption, unsigned int style)>; + std::function<bool(const bilingual_str& message, const std::string& caption, unsigned int style)>; virtual std::unique_ptr<Handler> handleMessageBox(MessageBoxFn fn) = 0; //! Register handler for question messages. - using QuestionFn = std::function<bool(const std::string& message, + using QuestionFn = std::function<bool(const bilingual_str& message, const std::string& non_interactive_message, const std::string& caption, unsigned int style)>; diff --git a/src/net.cpp b/src/net.cpp index dcc613ba88..9950b9aea4 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -16,6 +16,7 @@ #include <crypto/sha256.h> #include <netbase.h> #include <net_permissions.h> +#include <protocol.h> #include <random.h> #include <scheduler.h> #include <ui_interface.h> @@ -631,14 +632,14 @@ int CNode::GetSendVersion() const int V1TransportDeserializer::readHeader(const char *pch, unsigned int nBytes) { // copy data to temporary parsing buffer - unsigned int nRemaining = 24 - nHdrPos; + unsigned int nRemaining = CMessageHeader::HEADER_SIZE - nHdrPos; unsigned int nCopy = std::min(nRemaining, nBytes); memcpy(&hdrbuf[nHdrPos], pch, nCopy); nHdrPos += nCopy; // if header incomplete, exit - if (nHdrPos < 24) + if (nHdrPos < CMessageHeader::HEADER_SIZE) return nCopy; // deserialize to CMessageHeader @@ -2068,9 +2069,8 @@ void CConnman::ThreadMessageHandler() -bool CConnman::BindListenPort(const CService& addrBind, std::string& strError, NetPermissionFlags permissions) +bool CConnman::BindListenPort(const CService& addrBind, bilingual_str& strError, NetPermissionFlags permissions) { - strError = ""; int nOne = 1; // Create socket for listening for incoming connections @@ -2078,16 +2078,16 @@ bool CConnman::BindListenPort(const CService& addrBind, std::string& strError, N socklen_t len = sizeof(sockaddr); if (!addrBind.GetSockAddr((struct sockaddr*)&sockaddr, &len)) { - strError = strprintf("Error: Bind address family for %s not supported", addrBind.ToString()); - LogPrintf("%s\n", strError); + strError = strprintf(Untranslated("Error: Bind address family for %s not supported"), addrBind.ToString()); + LogPrintf("%s\n", strError.original); return false; } SOCKET hListenSocket = CreateSocket(addrBind); if (hListenSocket == INVALID_SOCKET) { - strError = strprintf("Error: Couldn't open socket for incoming connections (socket returned error %s)", NetworkErrorString(WSAGetLastError())); - LogPrintf("%s\n", strError); + strError = strprintf(Untranslated("Error: Couldn't open socket for incoming connections (socket returned error %s)"), NetworkErrorString(WSAGetLastError())); + LogPrintf("%s\n", strError.original); return false; } @@ -2111,10 +2111,10 @@ bool CConnman::BindListenPort(const CService& addrBind, std::string& strError, N { int nErr = WSAGetLastError(); if (nErr == WSAEADDRINUSE) - strError = strprintf(_("Unable to bind to %s on this computer. %s is probably already running.").translated, addrBind.ToString(), PACKAGE_NAME); + strError = strprintf(_("Unable to bind to %s on this computer. %s is probably already running."), addrBind.ToString(), PACKAGE_NAME); else - strError = strprintf(_("Unable to bind to %s on this computer (bind returned error %s)").translated, addrBind.ToString(), NetworkErrorString(nErr)); - LogPrintf("%s\n", strError); + strError = strprintf(_("Unable to bind to %s on this computer (bind returned error %s)"), addrBind.ToString(), NetworkErrorString(nErr)); + LogPrintf("%s\n", strError.original); CloseSocket(hListenSocket); return false; } @@ -2123,8 +2123,8 @@ bool CConnman::BindListenPort(const CService& addrBind, std::string& strError, N // Listen for incoming connections if (listen(hListenSocket, SOMAXCONN) == SOCKET_ERROR) { - strError = strprintf(_("Error: Listening for incoming connections failed (listen returned error %s)").translated, NetworkErrorString(WSAGetLastError())); - LogPrintf("%s\n", strError); + strError = strprintf(_("Error: Listening for incoming connections failed (listen returned error %s)"), NetworkErrorString(WSAGetLastError())); + LogPrintf("%s\n", strError.original); CloseSocket(hListenSocket); return false; } @@ -2218,7 +2218,7 @@ NodeId CConnman::GetNewNodeId() bool CConnman::Bind(const CService &addr, unsigned int flags, NetPermissionFlags permissions) { if (!(flags & BF_EXPLICIT) && !IsReachable(addr)) return false; - std::string strError; + bilingual_str strError; if (!BindListenPort(addr, strError, permissions)) { if ((flags & BF_REPORT_ERROR) && clientInterface) { clientInterface->ThreadSafeMessageBox(strError, "", CClientUIInterface::MSG_ERROR); @@ -2265,7 +2265,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions) if (fListen && !InitBinds(connOptions.vBinds, connOptions.vWhiteBinds)) { if (clientInterface) { clientInterface->ThreadSafeMessageBox( - _("Failed to listen on any port. Use -listen=0 if you want this.").translated, + _("Failed to listen on any port. Use -listen=0 if you want this."), "", CClientUIInterface::MSG_ERROR); } return false; @@ -2331,7 +2331,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions) if (connOptions.m_use_addrman_outgoing && !connOptions.m_specified_outgoing.empty()) { if (clientInterface) { clientInterface->ThreadSafeMessageBox( - _("Cannot provide specific connections and have addrman find outgoing connections at the same.").translated, + _("Cannot provide specific connections and have addrman find outgoing connections at the same."), "", CClientUIInterface::MSG_ERROR); } return false; @@ -21,8 +21,8 @@ #include <random.h> #include <streams.h> #include <sync.h> -#include <uint256.h> #include <threadinterrupt.h> +#include <uint256.h> #include <atomic> #include <deque> @@ -39,6 +39,7 @@ class CScheduler; class CNode; class BanMan; +struct bilingual_str; /** Default for -whitelistrelay. */ static const bool DEFAULT_WHITELISTRELAY = true; @@ -61,6 +62,8 @@ static const int MAX_OUTBOUND_FULL_RELAY_CONNECTIONS = 8; static const int MAX_ADDNODE_CONNECTIONS = 8; /** Maximum number of block-relay-only outgoing connections */ static const int MAX_BLOCKS_ONLY_CONNECTIONS = 2; +/** Maximum number of feeler connections */ +static const int MAX_FEELER_CONNECTIONS = 1; /** -listen default */ static const bool DEFAULT_LISTEN = true; /** -upnp default */ @@ -334,7 +337,7 @@ private: NetPermissionFlags m_permissions; }; - bool BindListenPort(const CService& bindAddr, std::string& strError, NetPermissionFlags permissions); + bool BindListenPort(const CService& bindAddr, bilingual_str& strError, NetPermissionFlags permissions); bool Bind(const CService& addr, unsigned int flags, NetPermissionFlags permissions); bool InitBinds(const std::vector<CService>& binds, const std::vector<NetWhitebindPermissions>& whiteBinds); void ThreadOpenAddedConnections(); diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 50a8a8a882..1df1fab59d 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -8,9 +8,11 @@ #include <addrman.h> #include <banman.h> #include <blockencodings.h> +#include <blockfilter.h> #include <chainparams.h> #include <consensus/validation.h> #include <hash.h> +#include <index/blockfilterindex.h> #include <validation.h> #include <merkleblock.h> #include <netmessagemaker.h> @@ -127,6 +129,8 @@ static constexpr unsigned int INVENTORY_BROADCAST_MAX = 7 * INVENTORY_BROADCAST_ static constexpr unsigned int AVG_FEEFILTER_BROADCAST_INTERVAL = 10 * 60; /** Maximum feefilter broadcast delay after significant change. */ static constexpr unsigned int MAX_FEEFILTER_CHANGE_DELAY = 5 * 60; +/** Interval between compact filter checkpoints. See BIP 157. */ +static constexpr int CFCHECKPT_INTERVAL = 1000; struct COrphanTx { // When modifying, adapt the copy of this definition in tests/DoS_tests. @@ -1612,26 +1616,32 @@ void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnm std::vector<CInv> vNotFound; const CNetMsgMaker msgMaker(pfrom->GetSendVersion()); - // Note that if we receive a getdata for a MSG_TX or MSG_WITNESS_TX from a - // block-relay-only outbound peer, we will stop processing further getdata - // messages from this peer (likely resulting in our peer eventually - // disconnecting us). - if (pfrom->m_tx_relay != nullptr) { - // mempool entries added before this time have likely expired from mapRelay - const std::chrono::seconds longlived_mempool_time = GetTime<std::chrono::seconds>() - RELAY_TX_CACHE_TIME; - const std::chrono::seconds mempool_req = pfrom->m_tx_relay->m_last_mempool_req.load(); + // mempool entries added before this time have likely expired from mapRelay + const std::chrono::seconds longlived_mempool_time = GetTime<std::chrono::seconds>() - RELAY_TX_CACHE_TIME; + // Get last mempool request time + const std::chrono::seconds mempool_req = pfrom->m_tx_relay != nullptr ? pfrom->m_tx_relay->m_last_mempool_req.load() + : std::chrono::seconds::min(); + { LOCK(cs_main); + // Process as many TX items from the front of the getdata queue as + // possible, since they're common and it's efficient to batch process + // them. while (it != pfrom->vRecvGetData.end() && (it->type == MSG_TX || it->type == MSG_WITNESS_TX)) { if (interruptMsgProc) return; - // Don't bother if send buffer is too full to respond anyway + // The send buffer provides backpressure. If there's no space in + // the buffer, pause processing until the next call. if (pfrom->fPauseSend) break; - const CInv &inv = *it; - it++; + const CInv &inv = *it++; + + if (pfrom->m_tx_relay == nullptr) { + // Ignore GETDATA requests for transactions from blocks-only peers. + continue; + } // Send stream from relay memory bool push = false; @@ -1665,19 +1675,17 @@ void static ProcessGetData(CNode* pfrom, const CChainParams& chainparams, CConnm } } // release cs_main + // Only process one BLOCK item per call, since they're uncommon and can be + // expensive to process. if (it != pfrom->vRecvGetData.end() && !pfrom->fPauseSend) { - const CInv &inv = *it; + const CInv &inv = *it++; if (inv.type == MSG_BLOCK || inv.type == MSG_FILTERED_BLOCK || inv.type == MSG_CMPCT_BLOCK || inv.type == MSG_WITNESS_BLOCK) { - it++; ProcessGetBlockData(pfrom, chainparams, inv, connman); } + // else: If the first item on the queue is an unknown type, we erase it + // and continue processing the queue on the next call. } - // Unknown types in the GetData stay in vRecvGetData and block any future - // message from this peer, see vRecvGetData check in ProcessMessages(). - // Depending on future p2p changes, we might either drop unknown getdata on - // the floor or disconnect the peer. - pfrom->vRecvGetData.erase(pfrom->vRecvGetData.begin(), it); if (!vNotFound.empty()) { @@ -1965,6 +1973,107 @@ void static ProcessOrphanTx(CConnman* connman, CTxMemPool& mempool, std::set<uin } } +/** + * Validation logic for compact filters request handling. + * + * May disconnect from the peer in the case of a bad request. + * + * @param[in] pfrom The peer that we received the request from + * @param[in] chain_params Chain parameters + * @param[in] filter_type The filter type the request is for. Must be basic filters. + * @param[in] stop_hash The stop_hash for the request + * @param[out] stop_index The CBlockIndex for the stop_hash block, if the request can be serviced. + * @param[out] filter_index The filter index, if the request can be serviced. + * @return True if the request can be serviced. + */ +static bool PrepareBlockFilterRequest(CNode* pfrom, const CChainParams& chain_params, + BlockFilterType filter_type, + const uint256& stop_hash, + const CBlockIndex*& stop_index, + const BlockFilterIndex*& filter_index) +{ + const bool supported_filter_type = + (filter_type == BlockFilterType::BASIC && + gArgs.GetBoolArg("-peerblockfilters", DEFAULT_PEERBLOCKFILTERS)); + if (!supported_filter_type) { + LogPrint(BCLog::NET, "peer %d requested unsupported block filter type: %d\n", + pfrom->GetId(), static_cast<uint8_t>(filter_type)); + pfrom->fDisconnect = true; + return false; + } + + { + LOCK(cs_main); + stop_index = LookupBlockIndex(stop_hash); + + // Check that the stop block exists and the peer would be allowed to fetch it. + if (!stop_index || !BlockRequestAllowed(stop_index, chain_params.GetConsensus())) { + LogPrint(BCLog::NET, "peer %d requested invalid block hash: %s\n", + pfrom->GetId(), stop_hash.ToString()); + pfrom->fDisconnect = true; + return false; + } + } + + filter_index = GetBlockFilterIndex(filter_type); + if (!filter_index) { + LogPrint(BCLog::NET, "Filter index for supported type %s not found\n", BlockFilterTypeName(filter_type)); + return false; + } + + return true; +} + +/** + * Handle a getcfcheckpt request. + * + * May disconnect from the peer in the case of a bad request. + * + * @param[in] pfrom The peer that we received the request from + * @param[in] vRecv The raw message received + * @param[in] chain_params Chain parameters + * @param[in] connman Pointer to the connection manager + */ +static void ProcessGetCFCheckPt(CNode* pfrom, CDataStream& vRecv, const CChainParams& chain_params, + CConnman* connman) +{ + uint8_t filter_type_ser; + uint256 stop_hash; + + vRecv >> filter_type_ser >> stop_hash; + + const BlockFilterType filter_type = static_cast<BlockFilterType>(filter_type_ser); + + const CBlockIndex* stop_index; + const BlockFilterIndex* filter_index; + if (!PrepareBlockFilterRequest(pfrom, chain_params, filter_type, stop_hash, + stop_index, filter_index)) { + return; + } + + std::vector<uint256> headers(stop_index->nHeight / CFCHECKPT_INTERVAL); + + // Populate headers. + const CBlockIndex* block_index = stop_index; + for (int i = headers.size() - 1; i >= 0; i--) { + int height = (i + 1) * CFCHECKPT_INTERVAL; + block_index = block_index->GetAncestor(height); + + if (!filter_index->LookupFilterHeader(block_index, headers[i])) { + LogPrint(BCLog::NET, "Failed to find block filter header in index: filter_type=%s, block_hash=%s\n", + BlockFilterTypeName(filter_type), block_index->GetBlockHash().ToString()); + return; + } + } + + CSerializedNetMsg msg = CNetMsgMaker(pfrom->GetSendVersion()) + .Make(NetMsgType::CFCHECKPT, + filter_type_ser, + stop_index->GetBlockHash(), + headers); + connman->PushMessage(pfrom, std::move(msg)); +} + bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRecv, int64_t nTimeReceived, const CChainParams& chainparams, CTxMemPool& mempool, CConnman* connman, BanMan* banman, const std::atomic<bool>& interruptMsgProc) { LogPrint(BCLog::NET, "received: %s (%u bytes) peer=%d\n", SanitizeString(msg_type), vRecv.size(), pfrom->GetId()); @@ -3216,7 +3325,6 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec { LOCK(pfrom->m_tx_relay->cs_filter); pfrom->m_tx_relay->pfilter.reset(new CBloomFilter(filter)); - pfrom->m_tx_relay->pfilter->UpdateEmptyFull(); pfrom->m_tx_relay->fRelayTxes = true; } return true; @@ -3271,6 +3379,11 @@ bool ProcessMessage(CNode* pfrom, const std::string& msg_type, CDataStream& vRec return true; } + if (msg_type == NetMsgType::GETCFCHECKPT) { + ProcessGetCFCheckPt(pfrom, vRecv, chainparams, connman); + return true; + } + if (msg_type == NetMsgType::NOTFOUND) { // Remove the NOTFOUND transactions from the peer LOCK(cs_main); diff --git a/src/net_processing.h b/src/net_processing.h index a85d5e7c70..4033c85d07 100644 --- a/src/net_processing.h +++ b/src/net_processing.h @@ -21,6 +21,7 @@ static const unsigned int DEFAULT_MAX_ORPHAN_TRANSACTIONS = 100; /** Default number of orphan+recently-replaced txn to keep around for block reconstruction */ static const unsigned int DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN = 100; static const bool DEFAULT_PEERBLOOMFILTERS = false; +static const bool DEFAULT_PEERBLOCKFILTERS = false; class PeerLogicValidation final : public CValidationInterface, public NetEventsInterface { private: diff --git a/src/netaddress.cpp b/src/netaddress.cpp index 6adb171abd..f79425a52e 100644 --- a/src/netaddress.cpp +++ b/src/netaddress.cpp @@ -894,3 +894,8 @@ bool operator<(const CSubNet& a, const CSubNet& b) { return (a.network < b.network || (a.network == b.network && memcmp(a.netmask, b.netmask, 16) < 0)); } + +bool SanityCheckASMap(const std::vector<bool>& asmap) +{ + return SanityCheckASMap(asmap, 128); // For IP address lookups, the input is 128 bits +} diff --git a/src/netaddress.h b/src/netaddress.h index d0ab770379..d8f19deffe 100644 --- a/src/netaddress.h +++ b/src/netaddress.h @@ -180,4 +180,6 @@ class CService : public CNetAddr } }; +bool SanityCheckASMap(const std::vector<bool>& asmap); + #endif // BITCOIN_NETADDRESS_H diff --git a/src/noui.cpp b/src/noui.cpp index 11cfe7f94d..ddb3a50ff7 100644 --- a/src/noui.cpp +++ b/src/noui.cpp @@ -5,8 +5,9 @@ #include <noui.h> +#include <logging.h> #include <ui_interface.h> -#include <util/system.h> +#include <util/translation.h> #include <string> @@ -18,7 +19,7 @@ boost::signals2::connection noui_ThreadSafeMessageBoxConn; boost::signals2::connection noui_ThreadSafeQuestionConn; boost::signals2::connection noui_InitMessageConn; -bool noui_ThreadSafeMessageBox(const std::string& message, const std::string& caption, unsigned int style) +bool noui_ThreadSafeMessageBox(const bilingual_str& message, const std::string& caption, unsigned int style) { bool fSecure = style & CClientUIInterface::SECURE; style &= ~CClientUIInterface::SECURE; @@ -43,15 +44,15 @@ bool noui_ThreadSafeMessageBox(const std::string& message, const std::string& ca } if (!fSecure) { - LogPrintf("%s%s\n", strCaption, message); + LogPrintf("%s%s\n", strCaption, message.original); } - tfm::format(std::cerr, "%s%s\n", strCaption, message); + tfm::format(std::cerr, "%s%s\n", strCaption, message.original); return false; } -bool noui_ThreadSafeQuestion(const std::string& /* ignored interactive message */, const std::string& message, const std::string& caption, unsigned int style) +bool noui_ThreadSafeQuestion(const bilingual_str& /* ignored interactive message */, const std::string& message, const std::string& caption, unsigned int style) { - return noui_ThreadSafeMessageBox(message, caption, style); + return noui_ThreadSafeMessageBox(Untranslated(message), caption, style); } void noui_InitMessage(const std::string& message) @@ -66,13 +67,13 @@ void noui_connect() noui_InitMessageConn = uiInterface.InitMessage_connect(noui_InitMessage); } -bool noui_ThreadSafeMessageBoxRedirect(const std::string& message, const std::string& caption, unsigned int style) +bool noui_ThreadSafeMessageBoxRedirect(const bilingual_str& message, const std::string& caption, unsigned int style) { - LogPrintf("%s: %s\n", caption, message); + LogPrintf("%s: %s\n", caption, message.original); return false; } -bool noui_ThreadSafeQuestionRedirect(const std::string& /* ignored interactive message */, const std::string& message, const std::string& caption, unsigned int style) +bool noui_ThreadSafeQuestionRedirect(const bilingual_str& /* ignored interactive message */, const std::string& message, const std::string& caption, unsigned int style) { LogPrintf("%s: %s\n", caption, message); return false; diff --git a/src/noui.h b/src/noui.h index 5e5767b453..8ec5708328 100644 --- a/src/noui.h +++ b/src/noui.h @@ -7,10 +7,12 @@ #include <string> +struct bilingual_str; + /** Non-GUI handler, which logs and prints messages. */ -bool noui_ThreadSafeMessageBox(const std::string& message, const std::string& caption, unsigned int style); +bool noui_ThreadSafeMessageBox(const bilingual_str& message, const std::string& caption, unsigned int style); /** Non-GUI handler, which logs and prints questions. */ -bool noui_ThreadSafeQuestion(const std::string& /* ignored interactive message */, const std::string& message, const std::string& caption, unsigned int style); +bool noui_ThreadSafeQuestion(const bilingual_str& /* ignored interactive message */, const std::string& message, const std::string& caption, unsigned int style); /** Non-GUI handler, which only logs a message. */ void noui_InitMessage(const std::string& message); diff --git a/src/protocol.cpp b/src/protocol.cpp index a3e844e35b..25851e786c 100644 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -40,6 +40,8 @@ const char *SENDCMPCT="sendcmpct"; const char *CMPCTBLOCK="cmpctblock"; const char *GETBLOCKTXN="getblocktxn"; const char *BLOCKTXN="blocktxn"; +const char *GETCFCHECKPT="getcfcheckpt"; +const char *CFCHECKPT="cfcheckpt"; } // namespace NetMsgType /** All known message types. Keep this in the same order as the list of @@ -71,6 +73,8 @@ const static std::string allNetMessageTypes[] = { NetMsgType::CMPCTBLOCK, NetMsgType::GETBLOCKTXN, NetMsgType::BLOCKTXN, + NetMsgType::GETCFCHECKPT, + NetMsgType::CFCHECKPT, }; const static std::vector<std::string> allNetMessageTypesVec(allNetMessageTypes, allNetMessageTypes+ARRAYLEN(allNetMessageTypes)); diff --git a/src/protocol.h b/src/protocol.h index 6639ae2aac..dfcb0e0660 100644 --- a/src/protocol.h +++ b/src/protocol.h @@ -234,6 +234,20 @@ extern const char *GETBLOCKTXN; * @since protocol version 70014 as described by BIP 152 */ extern const char *BLOCKTXN; +/** + * getcfcheckpt requests evenly spaced compact filter headers, enabling + * parallelized download and validation of the headers between them. + * Only available with service bit NODE_COMPACT_FILTERS as described by + * BIP 157 & 158. + */ +extern const char *GETCFCHECKPT; +/** + * cfcheckpt is a response to a getcfcheckpt request containing a vector of + * evenly spaced filter headers for blocks on the requested chain. + * Only available with service bit NODE_COMPACT_FILTERS as described by + * BIP 157 & 158. + */ +extern const char *CFCHECKPT; }; /* Get a vector of all valid message types (see above) */ diff --git a/src/qt/addressbookpage.cpp b/src/qt/addressbookpage.cpp index f48f28d03a..0d3b08fe7d 100644 --- a/src/qt/addressbookpage.cpp +++ b/src/qt/addressbookpage.cpp @@ -35,7 +35,7 @@ public: } protected: - bool filterAcceptsRow(int row, const QModelIndex& parent) const + bool filterAcceptsRow(int row, const QModelIndex& parent) const override { auto model = sourceModel(); auto label = model->index(row, AddressTableModel::Label, parent); diff --git a/src/qt/addressbookpage.h b/src/qt/addressbookpage.h index 6394d26801..3d303a6f68 100644 --- a/src/qt/addressbookpage.h +++ b/src/qt/addressbookpage.h @@ -45,7 +45,7 @@ public: const QString &getReturnValue() const { return returnValue; } public Q_SLOTS: - void done(int retval); + void done(int retval) override; private: Ui::AddressBookPage *ui; diff --git a/src/qt/addresstablemodel.h b/src/qt/addresstablemodel.h index 035f3e0571..97f673caf1 100644 --- a/src/qt/addresstablemodel.h +++ b/src/qt/addresstablemodel.h @@ -52,14 +52,14 @@ public: /** @name Methods overridden from QAbstractTableModel @{*/ - int rowCount(const QModelIndex &parent) const; - int columnCount(const QModelIndex &parent) const; - QVariant data(const QModelIndex &index, int role) const; - bool setData(const QModelIndex &index, const QVariant &value, int role); - QVariant headerData(int section, Qt::Orientation orientation, int role) const; - QModelIndex index(int row, int column, const QModelIndex &parent) const; - bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); - Qt::ItemFlags flags(const QModelIndex &index) const; + int rowCount(const QModelIndex &parent) const override; + int columnCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role) const override; + bool setData(const QModelIndex &index, const QVariant &value, int role) override; + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + QModelIndex index(int row, int column, const QModelIndex &parent) const override; + bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; + Qt::ItemFlags flags(const QModelIndex &index) const override; /*@}*/ /* Add an address to the model. diff --git a/src/qt/askpassphrasedialog.h b/src/qt/askpassphrasedialog.h index 20fc5045ae..9557e72936 100644 --- a/src/qt/askpassphrasedialog.h +++ b/src/qt/askpassphrasedialog.h @@ -32,7 +32,7 @@ public: explicit AskPassphraseDialog(Mode mode, QWidget *parent, SecureString* passphrase_out = nullptr); ~AskPassphraseDialog(); - void accept(); + void accept() override; void setModel(WalletModel *model); @@ -49,8 +49,8 @@ private Q_SLOTS: void toggleShowPassword(bool); protected: - bool event(QEvent *event); - bool eventFilter(QObject *object, QEvent *event); + bool event(QEvent *event) override; + bool eventFilter(QObject *object, QEvent *event) override; }; #endif // BITCOIN_QT_ASKPASSPHRASEDIALOG_H diff --git a/src/qt/bantablemodel.h b/src/qt/bantablemodel.h index 88d5b5811b..57f559fc14 100644 --- a/src/qt/bantablemodel.h +++ b/src/qt/bantablemodel.h @@ -56,16 +56,17 @@ public: /** @name Methods overridden from QAbstractTableModel @{*/ - int rowCount(const QModelIndex &parent) const; - int columnCount(const QModelIndex &parent) const; - QVariant data(const QModelIndex &index, int role) const; - QVariant headerData(int section, Qt::Orientation orientation, int role) const; - QModelIndex index(int row, int column, const QModelIndex &parent) const; - Qt::ItemFlags flags(const QModelIndex &index) const; - void sort(int column, Qt::SortOrder order); - bool shouldShow(); + int rowCount(const QModelIndex &parent) const override; + int columnCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + QModelIndex index(int row, int column, const QModelIndex &parent) const override; + Qt::ItemFlags flags(const QModelIndex &index) const override; + void sort(int column, Qt::SortOrder order) override; /*@}*/ + bool shouldShow(); + public Q_SLOTS: void refresh(); diff --git a/src/qt/bitcoinaddressvalidator.h b/src/qt/bitcoinaddressvalidator.h index 30d4a26d0e..52c06828a3 100644 --- a/src/qt/bitcoinaddressvalidator.h +++ b/src/qt/bitcoinaddressvalidator.h @@ -17,7 +17,7 @@ class BitcoinAddressEntryValidator : public QValidator public: explicit BitcoinAddressEntryValidator(QObject *parent); - State validate(QString &input, int &pos) const; + State validate(QString &input, int &pos) const override; }; /** Bitcoin address widget validator, checks for a valid bitcoin address. @@ -29,7 +29,7 @@ class BitcoinAddressCheckValidator : public QValidator public: explicit BitcoinAddressCheckValidator(QObject *parent); - State validate(QString &input, int &pos) const; + State validate(QString &input, int &pos) const override; }; #endif // BITCOIN_QT_BITCOINADDRESSVALIDATOR_H diff --git a/src/qt/bitcoinamountfield.cpp b/src/qt/bitcoinamountfield.cpp index 7acc82370f..4c57f1e352 100644 --- a/src/qt/bitcoinamountfield.cpp +++ b/src/qt/bitcoinamountfield.cpp @@ -31,7 +31,7 @@ public: connect(lineEdit(), &QLineEdit::textEdited, this, &AmountSpinBox::valueChanged); } - QValidator::State validate(QString &text, int &pos) const + QValidator::State validate(QString &text, int &pos) const override { if(text.isEmpty()) return QValidator::Intermediate; @@ -41,7 +41,7 @@ public: return valid ? QValidator::Intermediate : QValidator::Invalid; } - void fixup(QString &input) const + void fixup(QString &input) const override { bool valid; CAmount val; @@ -87,7 +87,7 @@ public: m_max_amount = value; } - void stepBy(int steps) + void stepBy(int steps) override { bool valid = false; CAmount val = value(&valid); @@ -114,7 +114,7 @@ public: singleStep = step; } - QSize minimumSizeHint() const + QSize minimumSizeHint() const override { if(cachedMinimumSizeHint.isEmpty()) { @@ -175,7 +175,7 @@ private: } protected: - bool event(QEvent *event) + bool event(QEvent *event) override { if (event->type() == QEvent::KeyPress || event->type() == QEvent::KeyRelease) { @@ -190,7 +190,7 @@ protected: return QAbstractSpinBox::event(event); } - StepEnabled stepEnabled() const + StepEnabled stepEnabled() const override { if (isReadOnly()) // Disable steps when AmountSpinBox is read-only return StepNone; diff --git a/src/qt/bitcoinamountfield.h b/src/qt/bitcoinamountfield.h index 2db6b65f2c..d3e61aac29 100644 --- a/src/qt/bitcoinamountfield.h +++ b/src/qt/bitcoinamountfield.h @@ -70,7 +70,7 @@ Q_SIGNALS: protected: /** Intercept focus-in event and ',' key presses */ - bool eventFilter(QObject *object, QEvent *event); + bool eventFilter(QObject *object, QEvent *event) override; private: AmountSpinBox *amount; diff --git a/src/qt/bitcoingui.cpp b/src/qt/bitcoingui.cpp index cf2de1a417..3a1fdc22a6 100644 --- a/src/qt/bitcoingui.cpp +++ b/src/qt/bitcoingui.cpp @@ -36,6 +36,7 @@ #include <interfaces/node.h> #include <ui_interface.h> #include <util/system.h> +#include <util/translation.h> #include <QAction> #include <QApplication> @@ -1039,7 +1040,7 @@ void BitcoinGUI::setNumBlocks(int count, const QDateTime& blockDate, double nVer progressBar->setToolTip(tooltip); } -void BitcoinGUI::message(const QString& title, QString message, unsigned int style, bool* ret) +void BitcoinGUI::message(const QString& title, QString message, unsigned int style, bool* ret, const QString& detailed_message) { // Default title. On macOS, the window title is ignored (as required by the macOS Guidelines). QString strTitle{PACKAGE_NAME}; @@ -1093,6 +1094,7 @@ void BitcoinGUI::message(const QString& title, QString message, unsigned int sty showNormalIfMinimized(); QMessageBox mBox(static_cast<QMessageBox::Icon>(nMBoxIcon), strTitle, message, buttons, this); mBox.setTextFormat(Qt::PlainText); + mBox.setDetailedText(detailed_message); int r = mBox.exec(); if (ret != nullptr) *ret = r == QMessageBox::Ok; @@ -1368,20 +1370,27 @@ void BitcoinGUI::showModalOverlay() modalOverlay->toggleVisibility(); } -static bool ThreadSafeMessageBox(BitcoinGUI* gui, const std::string& message, const std::string& caption, unsigned int style) +static bool ThreadSafeMessageBox(BitcoinGUI* gui, const bilingual_str& message, const std::string& caption, unsigned int style) { bool modal = (style & CClientUIInterface::MODAL); // The SECURE flag has no effect in the Qt GUI. // bool secure = (style & CClientUIInterface::SECURE); style &= ~CClientUIInterface::SECURE; bool ret = false; + + QString detailed_message; // This is original message, in English, for googling and referencing. + if (message.original != message.translated) { + detailed_message = BitcoinGUI::tr("Original message:") + "\n" + QString::fromStdString(message.original); + } + // In case of modal message, use blocking connection to wait for user to click a button bool invoked = QMetaObject::invokeMethod(gui, "message", modal ? GUIUtil::blockingGUIThreadConnection() : Qt::QueuedConnection, Q_ARG(QString, QString::fromStdString(caption)), - Q_ARG(QString, QString::fromStdString(message)), + Q_ARG(QString, QString::fromStdString(message.translated)), Q_ARG(unsigned int, style), - Q_ARG(bool*, &ret)); + Q_ARG(bool*, &ret), + Q_ARG(QString, detailed_message)); assert(invoked); return ret; } diff --git a/src/qt/bitcoingui.h b/src/qt/bitcoingui.h index 70366e12d1..6733585f68 100644 --- a/src/qt/bitcoingui.h +++ b/src/qt/bitcoingui.h @@ -99,12 +99,12 @@ public: void unsubscribeFromCoreSignals(); protected: - void changeEvent(QEvent *e); - void closeEvent(QCloseEvent *event); - void showEvent(QShowEvent *event); - void dragEnterEvent(QDragEnterEvent *event); - void dropEvent(QDropEvent *event); - bool eventFilter(QObject *object, QEvent *event); + void changeEvent(QEvent *e) override; + void closeEvent(QCloseEvent *event) override; + void showEvent(QShowEvent *event) override; + void dragEnterEvent(QDragEnterEvent *event) override; + void dropEvent(QDropEvent *event) override; + bool eventFilter(QObject *object, QEvent *event) override; private: interfaces::Node& m_node; @@ -216,13 +216,14 @@ public Q_SLOTS: void setNumBlocks(int count, const QDateTime& blockDate, double nVerificationProgress, bool headers); /** Notify the user of an event from the core network or transaction handling code. - @param[in] title the message box / notification title - @param[in] message the displayed text - @param[in] style modality and style definitions (icon and used buttons - buttons only for message boxes) - @see CClientUIInterface::MessageBoxFlags - @param[in] ret pointer to a bool that will be modified to whether Ok was clicked (modal only) + @param[in] title the message box / notification title + @param[in] message the displayed text + @param[in] style modality and style definitions (icon and used buttons - buttons only for message boxes) + @see CClientUIInterface::MessageBoxFlags + @param[in] ret pointer to a bool that will be modified to whether Ok was clicked (modal only) + @param[in] detailed_message the text to be displayed in the details area */ - void message(const QString& title, QString message, unsigned int style, bool* ret = nullptr); + void message(const QString& title, QString message, unsigned int style, bool* ret = nullptr, const QString& detailed_message = QString()); #ifdef ENABLE_WALLET void setCurrentWallet(WalletModel* wallet_model); @@ -324,7 +325,7 @@ public: protected: /** So that it responds to left-button clicks */ - void mousePressEvent(QMouseEvent *event); + void mousePressEvent(QMouseEvent *event) override; private: OptionsModel *optionsModel; diff --git a/src/qt/bitcoinunits.h b/src/qt/bitcoinunits.h index 4c8a889965..1ff4702117 100644 --- a/src/qt/bitcoinunits.h +++ b/src/qt/bitcoinunits.h @@ -90,8 +90,8 @@ public: /** Unit identifier */ UnitRole = Qt::UserRole }; - int rowCount(const QModelIndex &parent) const; - QVariant data(const QModelIndex &index, int role) const; + int rowCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role) const override; ///@} static QString removeSpaces(QString text) diff --git a/src/qt/coincontroldialog.cpp b/src/qt/coincontroldialog.cpp index b46f416b6c..db77c17df0 100644 --- a/src/qt/coincontroldialog.cpp +++ b/src/qt/coincontroldialog.cpp @@ -41,10 +41,11 @@ bool CCoinControlWidgetItem::operator<(const QTreeWidgetItem &other) const { return QTreeWidgetItem::operator<(other); } -CoinControlDialog::CoinControlDialog(const PlatformStyle *_platformStyle, QWidget *parent) : +CoinControlDialog::CoinControlDialog(CCoinControl& coin_control, WalletModel* _model, const PlatformStyle *_platformStyle, QWidget *parent) : QDialog(parent), ui(new Ui::CoinControlDialog), - model(nullptr), + m_coin_control(coin_control), + model(_model), platformStyle(_platformStyle) { ui->setupUi(this); @@ -136,6 +137,13 @@ CoinControlDialog::CoinControlDialog(const PlatformStyle *_platformStyle, QWidge sortView(settings.value("nCoinControlSortColumn").toInt(), (static_cast<Qt::SortOrder>(settings.value("nCoinControlSortOrder").toInt()))); GUIUtil::handleCloseWindowShortcut(this); + + if(_model->getOptionsModel() && _model->getAddressTableModel()) + { + updateView(); + updateLabelLocked(); + CoinControlDialog::updateLabels(m_coin_control, _model, this); + } } CoinControlDialog::~CoinControlDialog() @@ -148,18 +156,6 @@ CoinControlDialog::~CoinControlDialog() delete ui; } -void CoinControlDialog::setModel(WalletModel *_model) -{ - this->model = _model; - - if(_model && _model->getOptionsModel() && _model->getAddressTableModel()) - { - updateView(); - updateLabelLocked(); - CoinControlDialog::updateLabels(_model, this); - } -} - // ok button void CoinControlDialog::buttonBoxClicked(QAbstractButton* button) { @@ -185,8 +181,8 @@ void CoinControlDialog::buttonSelectAllClicked() ui->treeWidget->topLevelItem(i)->setCheckState(COLUMN_CHECKBOX, state); ui->treeWidget->setEnabled(true); if (state == Qt::Unchecked) - coinControl()->UnSelectAll(); // just to be sure - CoinControlDialog::updateLabels(model, this); + m_coin_control.UnSelectAll(); // just to be sure + CoinControlDialog::updateLabels(m_coin_control, model, this); } // context menu @@ -371,15 +367,15 @@ void CoinControlDialog::viewItemChanged(QTreeWidgetItem* item, int column) COutPoint outpt(uint256S(item->data(COLUMN_ADDRESS, TxHashRole).toString().toStdString()), item->data(COLUMN_ADDRESS, VOutRole).toUInt()); if (item->checkState(COLUMN_CHECKBOX) == Qt::Unchecked) - coinControl()->UnSelect(outpt); + m_coin_control.UnSelect(outpt); else if (item->isDisabled()) // locked (this happens if "check all" through parent node) item->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked); else - coinControl()->Select(outpt); + m_coin_control.Select(outpt); // selection changed -> update labels if (ui->treeWidget->isEnabled()) // do not update on every click for (un)select all - CoinControlDialog::updateLabels(model, this); + CoinControlDialog::updateLabels(m_coin_control, model, this); } } @@ -396,7 +392,7 @@ void CoinControlDialog::updateLabelLocked() else ui->labelLocked->setVisible(false); } -void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) +void CoinControlDialog::updateLabels(CCoinControl& m_coin_control, WalletModel *model, QDialog* dialog) { if (!model) return; @@ -428,7 +424,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) bool fWitness = false; std::vector<COutPoint> vCoinControl; - coinControl()->ListSelected(vCoinControl); + m_coin_control.ListSelected(vCoinControl); size_t i = 0; for (const auto& out : model->wallet().getCoins(vCoinControl)) { @@ -439,7 +435,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) const COutPoint& outpt = vCoinControl[i++]; if (out.is_spent) { - coinControl()->UnSelect(outpt); + m_coin_control.UnSelect(outpt); continue; } @@ -492,7 +488,7 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) nBytes -= 34; // Fee - nPayFee = model->wallet().getMinimumFee(nBytes, *coinControl(), nullptr /* returned_target */, nullptr /* reason */); + nPayFee = model->wallet().getMinimumFee(nBytes, m_coin_control, nullptr /* returned_target */, nullptr /* reason */); if (nPayAmount > 0) { @@ -584,12 +580,6 @@ void CoinControlDialog::updateLabels(WalletModel *model, QDialog* dialog) label->setVisible(nChange < 0); } -CCoinControl* CoinControlDialog::coinControl() -{ - static CCoinControl coin_control; - return &coin_control; -} - void CoinControlDialog::updateView() { if (!model || !model->getOptionsModel() || !model->getAddressTableModel()) @@ -606,8 +596,7 @@ void CoinControlDialog::updateView() int nDisplayUnit = model->getOptionsModel()->getDisplayUnit(); for (const auto& coins : model->wallet().listCoins()) { - CCoinControlWidgetItem *itemWalletAddress = new CCoinControlWidgetItem(); - itemWalletAddress->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked); + CCoinControlWidgetItem* itemWalletAddress{nullptr}; QString sWalletAddress = QString::fromStdString(EncodeDestination(coins.first)); QString sWalletLabel = model->getAddressTableModel()->labelForAddress(sWalletAddress); if (sWalletLabel.isEmpty()) @@ -616,7 +605,7 @@ void CoinControlDialog::updateView() if (treeMode) { // wallet address - ui->treeWidget->addTopLevelItem(itemWalletAddress); + itemWalletAddress = new CCoinControlWidgetItem(ui->treeWidget); itemWalletAddress->setFlags(flgTristate); itemWalletAddress->setCheckState(COLUMN_CHECKBOX, Qt::Unchecked); @@ -690,13 +679,13 @@ void CoinControlDialog::updateView() // disable locked coins if (model->wallet().isLockedCoin(output)) { - coinControl()->UnSelect(output); // just to be sure + m_coin_control.UnSelect(output); // just to be sure itemOutput->setDisabled(true); itemOutput->setIcon(COLUMN_CHECKBOX, platformStyle->SingleColorIcon(":/icons/lock_closed")); } // set checkbox - if (coinControl()->IsSelected(output)) + if (m_coin_control.IsSelected(output)) itemOutput->setCheckState(COLUMN_CHECKBOX, Qt::Checked); } diff --git a/src/qt/coincontroldialog.h b/src/qt/coincontroldialog.h index efc06a7656..3de7fd6d54 100644 --- a/src/qt/coincontroldialog.h +++ b/src/qt/coincontroldialog.h @@ -31,10 +31,9 @@ class CCoinControlWidgetItem : public QTreeWidgetItem { public: explicit CCoinControlWidgetItem(QTreeWidget *parent, int type = Type) : QTreeWidgetItem(parent, type) {} - explicit CCoinControlWidgetItem(int type = Type) : QTreeWidgetItem(type) {} explicit CCoinControlWidgetItem(QTreeWidgetItem *parent, int type = Type) : QTreeWidgetItem(parent, type) {} - bool operator<(const QTreeWidgetItem &other) const; + bool operator<(const QTreeWidgetItem &other) const override; }; @@ -43,20 +42,18 @@ class CoinControlDialog : public QDialog Q_OBJECT public: - explicit CoinControlDialog(const PlatformStyle *platformStyle, QWidget *parent = nullptr); + explicit CoinControlDialog(CCoinControl& coin_control, WalletModel* model, const PlatformStyle *platformStyle, QWidget *parent = nullptr); ~CoinControlDialog(); - void setModel(WalletModel *model); - // static because also called from sendcoinsdialog - static void updateLabels(WalletModel*, QDialog*); + static void updateLabels(CCoinControl& m_coin_control, WalletModel*, QDialog*); static QList<CAmount> payAmounts; - static CCoinControl *coinControl(); static bool fSubtractFeeFromAmount; private: Ui::CoinControlDialog *ui; + CCoinControl& m_coin_control; WalletModel *model; int sortColumn; Qt::SortOrder sortOrder; diff --git a/src/qt/coincontroltreewidget.h b/src/qt/coincontroltreewidget.h index 86af555385..ac03a409c1 100644 --- a/src/qt/coincontroltreewidget.h +++ b/src/qt/coincontroltreewidget.h @@ -16,7 +16,7 @@ public: explicit CoinControlTreeWidget(QWidget *parent = nullptr); protected: - virtual void keyPressEvent(QKeyEvent *event); + virtual void keyPressEvent(QKeyEvent *event) override; }; #endif // BITCOIN_QT_COINCONTROLTREEWIDGET_H diff --git a/src/qt/editaddressdialog.h b/src/qt/editaddressdialog.h index 4d0e0709be..3be63156fd 100644 --- a/src/qt/editaddressdialog.h +++ b/src/qt/editaddressdialog.h @@ -40,7 +40,7 @@ public: void setAddress(const QString &address); public Q_SLOTS: - void accept(); + void accept() override; private: bool saveCurrentRow(); diff --git a/src/qt/guiutil.h b/src/qt/guiutil.h index 0d9293fbad..8b9fca4fb1 100644 --- a/src/qt/guiutil.h +++ b/src/qt/guiutil.h @@ -145,7 +145,7 @@ namespace GUIUtil explicit ToolTipToRichTextFilter(int size_threshold, QObject *parent = nullptr); protected: - bool eventFilter(QObject *obj, QEvent *evt); + bool eventFilter(QObject *obj, QEvent *evt) override; private: int size_threshold; @@ -227,7 +227,7 @@ namespace GUIUtil */ void clicked(const QPoint& point); protected: - void mouseReleaseEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event) override; }; class ClickableProgressBar : public QProgressBar @@ -240,7 +240,7 @@ namespace GUIUtil */ void clicked(const QPoint& point); protected: - void mouseReleaseEvent(QMouseEvent *event); + void mouseReleaseEvent(QMouseEvent *event) override; }; typedef ClickableProgressBar ProgressBar; @@ -255,7 +255,7 @@ namespace GUIUtil void keyEscapePressed(); private: - bool eventFilter(QObject *object, QEvent *event); + bool eventFilter(QObject *object, QEvent *event) override; }; // Fix known bugs in QProgressDialog class. diff --git a/src/qt/modaloverlay.h b/src/qt/modaloverlay.h index 076ec30b58..a565d7d8f3 100644 --- a/src/qt/modaloverlay.h +++ b/src/qt/modaloverlay.h @@ -35,8 +35,8 @@ public Q_SLOTS: bool isLayerVisible() const { return layerIsVisible; } protected: - bool eventFilter(QObject * obj, QEvent * ev); - bool event(QEvent* ev); + bool eventFilter(QObject * obj, QEvent * ev) override; + bool event(QEvent* ev) override; private: Ui::ModalOverlay *ui; diff --git a/src/qt/openuridialog.h b/src/qt/openuridialog.h index 4b610f74d7..667af2ec75 100644 --- a/src/qt/openuridialog.h +++ b/src/qt/openuridialog.h @@ -22,7 +22,7 @@ public: QString getURI(); protected Q_SLOTS: - void accept(); + void accept() override; private: Ui::OpenURIDialog *ui; diff --git a/src/qt/optionsdialog.h b/src/qt/optionsdialog.h index 9bc1c8ae4f..568c8b6fd0 100644 --- a/src/qt/optionsdialog.h +++ b/src/qt/optionsdialog.h @@ -28,7 +28,7 @@ class ProxyAddressValidator : public QValidator public: explicit ProxyAddressValidator(QObject *parent); - State validate(QString &input, int &pos) const; + State validate(QString &input, int &pos) const override; }; /** Preferences dialog. */ diff --git a/src/qt/optionsmodel.h b/src/qt/optionsmodel.h index b3260349e7..6ca5ac9d75 100644 --- a/src/qt/optionsmodel.h +++ b/src/qt/optionsmodel.h @@ -68,9 +68,9 @@ public: void Init(bool resetSettings = false); void Reset(); - int rowCount(const QModelIndex & parent = QModelIndex()) const; - QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const; - bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole); + int rowCount(const QModelIndex & parent = QModelIndex()) const override; + QVariant data(const QModelIndex & index, int role = Qt::DisplayRole) const override; + bool setData(const QModelIndex & index, const QVariant & value, int role = Qt::EditRole) override; /** Updates current unit in memory, settings and emits displayUnitChanged(newUnit) signal */ void setDisplayUnit(const QVariant &value); diff --git a/src/qt/overviewpage.cpp b/src/qt/overviewpage.cpp index e0ae1f9e92..e20ec229fc 100644 --- a/src/qt/overviewpage.cpp +++ b/src/qt/overviewpage.cpp @@ -35,7 +35,7 @@ public: } inline void paint(QPainter *painter, const QStyleOptionViewItem &option, - const QModelIndex &index ) const + const QModelIndex &index ) const override { painter->save(); @@ -99,7 +99,7 @@ public: painter->restore(); } - inline QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const + inline QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override { return QSize(DECORATION_SIZE, DECORATION_SIZE); } diff --git a/src/qt/paymentserver.h b/src/qt/paymentserver.h index aa9a7327ba..154f4a7ea6 100644 --- a/src/qt/paymentserver.h +++ b/src/qt/paymentserver.h @@ -98,7 +98,7 @@ private Q_SLOTS: protected: // Constructor registers this on the parent QApplication to // receive QEvent::FileOpen and QEvent:Drop events - bool eventFilter(QObject *object, QEvent *event); + bool eventFilter(QObject *object, QEvent *event) override; private: bool saveURIs; // true during startup diff --git a/src/qt/peertablemodel.h b/src/qt/peertablemodel.h index 0e20b1d9dc..99de772ac0 100644 --- a/src/qt/peertablemodel.h +++ b/src/qt/peertablemodel.h @@ -68,13 +68,13 @@ public: /** @name Methods overridden from QAbstractTableModel @{*/ - int rowCount(const QModelIndex &parent) const; - int columnCount(const QModelIndex &parent) const; - QVariant data(const QModelIndex &index, int role) const; - QVariant headerData(int section, Qt::Orientation orientation, int role) const; - QModelIndex index(int row, int column, const QModelIndex &parent) const; - Qt::ItemFlags flags(const QModelIndex &index) const; - void sort(int column, Qt::SortOrder order); + int rowCount(const QModelIndex &parent) const override; + int columnCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + QModelIndex index(int row, int column, const QModelIndex &parent) const override; + Qt::ItemFlags flags(const QModelIndex &index) const override; + void sort(int column, Qt::SortOrder order) override; /*@}*/ public Q_SLOTS: diff --git a/src/qt/qrimagewidget.h b/src/qt/qrimagewidget.h index 345bb64092..cca598c2ce 100644 --- a/src/qt/qrimagewidget.h +++ b/src/qt/qrimagewidget.h @@ -35,8 +35,8 @@ public Q_SLOTS: void copyImage(); protected: - virtual void mousePressEvent(QMouseEvent *event); - virtual void contextMenuEvent(QContextMenuEvent *event); + virtual void mousePressEvent(QMouseEvent *event) override; + virtual void contextMenuEvent(QContextMenuEvent *event) override; private: QMenu *contextMenu; diff --git a/src/qt/qvalidatedlineedit.h b/src/qt/qvalidatedlineedit.h index 815d5e0040..2c72b2ecda 100644 --- a/src/qt/qvalidatedlineedit.h +++ b/src/qt/qvalidatedlineedit.h @@ -21,8 +21,8 @@ public: bool isValid(); protected: - void focusInEvent(QFocusEvent *evt); - void focusOutEvent(QFocusEvent *evt); + void focusInEvent(QFocusEvent *evt) override; + void focusOutEvent(QFocusEvent *evt) override; private: bool valid; diff --git a/src/qt/receivecoinsdialog.h b/src/qt/receivecoinsdialog.h index 5bca2f46e2..2f48cd58f0 100644 --- a/src/qt/receivecoinsdialog.h +++ b/src/qt/receivecoinsdialog.h @@ -46,11 +46,11 @@ public: public Q_SLOTS: void clear(); - void reject(); - void accept(); + void reject() override; + void accept() override; protected: - virtual void keyPressEvent(QKeyEvent *event); + virtual void keyPressEvent(QKeyEvent *event) override; private: Ui::ReceiveCoinsDialog *ui; @@ -61,7 +61,7 @@ private: QModelIndex selectedRow(); void copyColumnToClipboard(int column); - virtual void resizeEvent(QResizeEvent *event); + virtual void resizeEvent(QResizeEvent *event) override; private Q_SLOTS: void on_receiveButton_clicked(); diff --git a/src/qt/recentrequeststablemodel.h b/src/qt/recentrequeststablemodel.h index 5e7f6acdc8..addf5ad0ae 100644 --- a/src/qt/recentrequeststablemodel.h +++ b/src/qt/recentrequeststablemodel.h @@ -73,14 +73,15 @@ public: /** @name Methods overridden from QAbstractTableModel @{*/ - int rowCount(const QModelIndex &parent) const; - int columnCount(const QModelIndex &parent) const; - QVariant data(const QModelIndex &index, int role) const; - bool setData(const QModelIndex &index, const QVariant &value, int role); - QVariant headerData(int section, Qt::Orientation orientation, int role) const; - QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; - bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()); - Qt::ItemFlags flags(const QModelIndex &index) const; + int rowCount(const QModelIndex &parent) const override; + int columnCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role) const override; + bool setData(const QModelIndex &index, const QVariant &value, int role) override; + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; + bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override; + Qt::ItemFlags flags(const QModelIndex &index) const override; + void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override; /*@}*/ const RecentRequestEntry &entry(int row) const { return list[row]; } @@ -89,7 +90,6 @@ public: void addNewRequest(RecentRequestEntry &recipient); public Q_SLOTS: - void sort(int column, Qt::SortOrder order = Qt::AscendingOrder); void updateDisplayUnit(); private: diff --git a/src/qt/rpcconsole.cpp b/src/qt/rpcconsole.cpp index bfe316bdcd..2d4af3f9e6 100644 --- a/src/qt/rpcconsole.cpp +++ b/src/qt/rpcconsole.cpp @@ -24,7 +24,7 @@ #include <univalue.h> #ifdef ENABLE_WALLET -#include <db_cxx.h> +#include <wallet/db.h> #include <wallet/wallet.h> #endif @@ -34,9 +34,10 @@ #include <QScrollBar> #include <QScreen> #include <QSettings> +#include <QString> +#include <QStringList> #include <QTime> #include <QTimer> -#include <QStringList> // TODO: add a scrollback limit, as there is currently none // TODO: make it possible to filter out categories (esp debug messages when implemented) @@ -115,8 +116,8 @@ class QtRPCTimerInterface: public RPCTimerInterface { public: ~QtRPCTimerInterface() {} - const char *Name() { return "Qt"; } - RPCTimerBase* NewTimer(std::function<void()>& func, int64_t millis) + const char *Name() override { return "Qt"; } + RPCTimerBase* NewTimer(std::function<void()>& func, int64_t millis) override { return new QtRPCTimerBase(func, millis); } @@ -480,7 +481,7 @@ RPCConsole::RPCConsole(interfaces::Node& node, const PlatformStyle *_platformSty // set library version labels #ifdef ENABLE_WALLET - ui->berkeleyDBVersion->setText(DbEnv::version(nullptr, nullptr, nullptr)); + ui->berkeleyDBVersion->setText(QString::fromStdString(BerkeleyDatabaseVersion())); #else ui->label_berkeleyDBVersion->hide(); ui->berkeleyDBVersion->hide(); diff --git a/src/qt/rpcconsole.h b/src/qt/rpcconsole.h index f586d04022..de8e37cca2 100644 --- a/src/qt/rpcconsole.h +++ b/src/qt/rpcconsole.h @@ -71,8 +71,8 @@ public: QKeySequence tabShortcut(TabTypes tab_type) const; protected: - virtual bool eventFilter(QObject* obj, QEvent *event); - void keyPressEvent(QKeyEvent *); + virtual bool eventFilter(QObject* obj, QEvent *event) override; + void keyPressEvent(QKeyEvent *) override; private Q_SLOTS: void on_lineEdit_returnPressed(); @@ -83,9 +83,9 @@ private Q_SLOTS: void on_sldGraphRange_valueChanged(int value); /** update traffic statistics */ void updateTrafficStats(quint64 totalBytesIn, quint64 totalBytesOut); - void resizeEvent(QResizeEvent *event); - void showEvent(QShowEvent *event); - void hideEvent(QHideEvent *event); + void resizeEvent(QResizeEvent *event) override; + void showEvent(QShowEvent *event) override; + void hideEvent(QHideEvent *event) override; /** Show custom context menu on Peers tab */ void showPeersTableContextMenu(const QPoint& point); /** Show custom context menu on Bans tab */ diff --git a/src/qt/sendcoinsdialog.cpp b/src/qt/sendcoinsdialog.cpp index 7b389a48d6..9e23fe78d8 100644 --- a/src/qt/sendcoinsdialog.cpp +++ b/src/qt/sendcoinsdialog.cpp @@ -57,6 +57,7 @@ SendCoinsDialog::SendCoinsDialog(const PlatformStyle *_platformStyle, QWidget *p ui(new Ui::SendCoinsDialog), clientModel(nullptr), model(nullptr), + m_coin_control(new CCoinControl), fNewRecipientAllowed(true), fFeeMinimized(true), platformStyle(_platformStyle) @@ -259,14 +260,9 @@ bool SendCoinsDialog::PrepareSendText(QString& question_string, QString& informa m_current_transaction = MakeUnique<WalletModelTransaction>(recipients); WalletModel::SendCoinsReturn prepareStatus; - // Always use a CCoinControl instance, use the CoinControlDialog instance if CoinControl has been enabled - CCoinControl ctrl; - if (model->getOptionsModel()->getCoinControlFeatures()) - ctrl = *CoinControlDialog::coinControl(); + updateCoinControlState(*m_coin_control); - updateCoinControlState(ctrl); - - prepareStatus = model->prepareTransaction(*m_current_transaction, ctrl); + prepareStatus = model->prepareTransaction(*m_current_transaction, *m_coin_control); // process prepareStatus and on error generate message shown to user processSendCoinsReturn(prepareStatus, @@ -454,7 +450,7 @@ void SendCoinsDialog::on_sendButton_clicked() } if (!send_failure) { accept(); - CoinControlDialog::coinControl()->UnSelectAll(); + m_coin_control->UnSelectAll(); coinControlUpdateLabels(); } fNewRecipientAllowed = true; @@ -466,7 +462,7 @@ void SendCoinsDialog::clear() m_current_transaction.reset(); // Clear coin control settings - CoinControlDialog::coinControl()->UnSelectAll(); + m_coin_control->UnSelectAll(); ui->checkBoxCoinControlChange->setChecked(false); ui->lineEditCoinControlChange->clear(); coinControlUpdateLabels(); @@ -689,17 +685,11 @@ void SendCoinsDialog::on_buttonMinimizeFee_clicked() void SendCoinsDialog::useAvailableBalance(SendCoinsEntry* entry) { - // Get CCoinControl instance if CoinControl is enabled or create a new one. - CCoinControl coin_control; - if (model->getOptionsModel()->getCoinControlFeatures()) { - coin_control = *CoinControlDialog::coinControl(); - } - // Include watch-only for wallets without private key - coin_control.fAllowWatchOnly = model->wallet().privateKeysDisabled(); + m_coin_control->fAllowWatchOnly = model->wallet().privateKeysDisabled(); // Calculate available amount to send. - CAmount amount = model->wallet().getAvailableBalance(coin_control); + CAmount amount = model->wallet().getAvailableBalance(*m_coin_control); for (int i = 0; i < ui->entries->count(); ++i) { SendCoinsEntry* e = qobject_cast<SendCoinsEntry*>(ui->entries->itemAt(i)->widget()); if (e && !e->isHidden() && e != entry) { @@ -758,12 +748,11 @@ void SendCoinsDialog::updateSmartFeeLabel() { if(!model || !model->getOptionsModel()) return; - CCoinControl coin_control; - updateCoinControlState(coin_control); - coin_control.m_feerate.reset(); // Explicitly use only fee estimation rate for smart fee labels + updateCoinControlState(*m_coin_control); + m_coin_control->m_feerate.reset(); // Explicitly use only fee estimation rate for smart fee labels int returned_target; FeeReason reason; - CFeeRate feeRate = CFeeRate(model->wallet().getMinimumFee(1000, coin_control, &returned_target, &reason)); + CFeeRate feeRate = CFeeRate(model->wallet().getMinimumFee(1000, *m_coin_control, &returned_target, &reason)); ui->labelSmartFee->setText(BitcoinUnits::formatWithUnit(model->getOptionsModel()->getDisplayUnit(), feeRate.GetFeePerK()) + "/kB"); @@ -834,7 +823,7 @@ void SendCoinsDialog::coinControlFeatureChanged(bool checked) ui->frameCoinControl->setVisible(checked); if (!checked && model) // coin control features disabled - CoinControlDialog::coinControl()->SetNull(); + m_coin_control->SetNull(); coinControlUpdateLabels(); } @@ -842,8 +831,7 @@ void SendCoinsDialog::coinControlFeatureChanged(bool checked) // Coin Control: button inputs -> show actual coin control dialog void SendCoinsDialog::coinControlButtonClicked() { - CoinControlDialog dlg(platformStyle); - dlg.setModel(model); + CoinControlDialog dlg(*m_coin_control, model, platformStyle); dlg.exec(); coinControlUpdateLabels(); } @@ -853,7 +841,7 @@ void SendCoinsDialog::coinControlChangeChecked(int state) { if (state == Qt::Unchecked) { - CoinControlDialog::coinControl()->destChange = CNoDestination(); + m_coin_control->destChange = CNoDestination(); ui->labelCoinControlChangeLabel->clear(); } else @@ -869,7 +857,7 @@ void SendCoinsDialog::coinControlChangeEdited(const QString& text) if (model && model->getAddressTableModel()) { // Default to no change address until verified - CoinControlDialog::coinControl()->destChange = CNoDestination(); + m_coin_control->destChange = CNoDestination(); ui->labelCoinControlChangeLabel->setStyleSheet("QLabel{color:red;}"); const CTxDestination dest = DecodeDestination(text.toStdString()); @@ -892,7 +880,7 @@ void SendCoinsDialog::coinControlChangeEdited(const QString& text) QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Cancel); if(btnRetVal == QMessageBox::Yes) - CoinControlDialog::coinControl()->destChange = dest; + m_coin_control->destChange = dest; else { ui->lineEditCoinControlChange->setText(""); @@ -911,7 +899,7 @@ void SendCoinsDialog::coinControlChangeEdited(const QString& text) else ui->labelCoinControlChangeLabel->setText(tr("(no label)")); - CoinControlDialog::coinControl()->destChange = dest; + m_coin_control->destChange = dest; } } } @@ -923,7 +911,7 @@ void SendCoinsDialog::coinControlUpdateLabels() if (!model || !model->getOptionsModel()) return; - updateCoinControlState(*CoinControlDialog::coinControl()); + updateCoinControlState(*m_coin_control); // set pay amounts CoinControlDialog::payAmounts.clear(); @@ -941,10 +929,10 @@ void SendCoinsDialog::coinControlUpdateLabels() } } - if (CoinControlDialog::coinControl()->HasSelected()) + if (m_coin_control->HasSelected()) { // actual coin control calculation - CoinControlDialog::updateLabels(model, this); + CoinControlDialog::updateLabels(*m_coin_control, model, this); // show coin control stats ui->labelCoinControlAutomaticallySelected->hide(); diff --git a/src/qt/sendcoinsdialog.h b/src/qt/sendcoinsdialog.h index 36bc2a846b..6961aa7821 100644 --- a/src/qt/sendcoinsdialog.h +++ b/src/qt/sendcoinsdialog.h @@ -12,6 +12,7 @@ #include <QString> #include <QTimer> +class CCoinControl; class ClientModel; class PlatformStyle; class SendCoinsEntry; @@ -47,8 +48,8 @@ public: public Q_SLOTS: void clear(); - void reject(); - void accept(); + void reject() override; + void accept() override; SendCoinsEntry *addEntry(); void updateTabsAndLabels(); void setBalance(const interfaces::WalletBalances& balances); @@ -60,6 +61,7 @@ private: Ui::SendCoinsDialog *ui; ClientModel *clientModel; WalletModel *model; + std::unique_ptr<CCoinControl> m_coin_control; std::unique_ptr<WalletModelTransaction> m_current_transaction; bool fNewRecipientAllowed; bool fFeeMinimized; @@ -112,7 +114,7 @@ class SendConfirmationDialog : public QMessageBox public: SendConfirmationDialog(const QString& title, const QString& text, const QString& informative_text = "", const QString& detailed_text = "", int secDelay = SEND_CONFIRM_DELAY, const QString& confirmText = "Send", QWidget* parent = nullptr); - int exec(); + int exec() override; private Q_SLOTS: void countDown(); diff --git a/src/qt/signverifymessagedialog.h b/src/qt/signverifymessagedialog.h index d2e04cd4fe..d33a2d038d 100644 --- a/src/qt/signverifymessagedialog.h +++ b/src/qt/signverifymessagedialog.h @@ -30,7 +30,7 @@ public: void showTab_VM(bool fShow); protected: - bool eventFilter(QObject *object, QEvent *event); + bool eventFilter(QObject *object, QEvent *event) override; private: Ui::SignVerifyMessageDialog *ui; diff --git a/src/qt/splashscreen.h b/src/qt/splashscreen.h index f99dd0c701..3158524117 100644 --- a/src/qt/splashscreen.h +++ b/src/qt/splashscreen.h @@ -32,8 +32,8 @@ public: ~SplashScreen(); protected: - void paintEvent(QPaintEvent *event); - void closeEvent(QCloseEvent *event); + void paintEvent(QPaintEvent *event) override; + void closeEvent(QCloseEvent *event) override; public Q_SLOTS: /** Hide the splash screen window and schedule the splash screen object for deletion */ @@ -43,7 +43,7 @@ public Q_SLOTS: void showMessage(const QString &message, int alignment, const QColor &color); protected: - bool eventFilter(QObject * obj, QEvent * ev); + bool eventFilter(QObject * obj, QEvent * ev) override; private: /** Connect core signals to splash screen */ diff --git a/src/qt/trafficgraphwidget.h b/src/qt/trafficgraphwidget.h index 7e8bfb2337..2d8c825815 100644 --- a/src/qt/trafficgraphwidget.h +++ b/src/qt/trafficgraphwidget.h @@ -25,7 +25,7 @@ public: int getGraphRangeMins() const; protected: - void paintEvent(QPaintEvent *); + void paintEvent(QPaintEvent *) override; public Q_SLOTS: void updateRates(); diff --git a/src/qt/transactionfilterproxy.h b/src/qt/transactionfilterproxy.h index 685f8d3a26..d6bb84f7e6 100644 --- a/src/qt/transactionfilterproxy.h +++ b/src/qt/transactionfilterproxy.h @@ -49,10 +49,10 @@ public: /** Set whether to show conflicted transactions. */ void setShowInactive(bool showInactive); - int rowCount(const QModelIndex &parent = QModelIndex()) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const override; protected: - bool filterAcceptsRow(int source_row, const QModelIndex & source_parent) const; + bool filterAcceptsRow(int source_row, const QModelIndex & source_parent) const override; private: QDateTime dateFrom; diff --git a/src/qt/transactiontablemodel.h b/src/qt/transactiontablemodel.h index 7a7d98962b..f06f0ea15f 100644 --- a/src/qt/transactiontablemodel.h +++ b/src/qt/transactiontablemodel.h @@ -76,11 +76,11 @@ public: RawDecorationRole, }; - int rowCount(const QModelIndex &parent) const; - int columnCount(const QModelIndex &parent) const; - QVariant data(const QModelIndex &index, int role) const; - QVariant headerData(int section, Qt::Orientation orientation, int role) const; - QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const; + int rowCount(const QModelIndex &parent) const override; + int columnCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const override; bool processingQueuedTransactions() const { return fProcessingQueuedTransactions; } private: diff --git a/src/qt/transactionview.h b/src/qt/transactionview.h index eca5656077..268e3751b3 100644 --- a/src/qt/transactionview.h +++ b/src/qt/transactionview.h @@ -82,9 +82,9 @@ private: GUIUtil::TableViewLastColumnResizingFixer *columnResizingFixer; - virtual void resizeEvent(QResizeEvent* event); + virtual void resizeEvent(QResizeEvent* event) override; - bool eventFilter(QObject *obj, QEvent *event); + bool eventFilter(QObject *obj, QEvent *event) override; private Q_SLOTS: void contextualMenu(const QPoint &); diff --git a/src/qt/utilitydialog.h b/src/qt/utilitydialog.h index b6a42d3d9d..425b468f40 100644 --- a/src/qt/utilitydialog.h +++ b/src/qt/utilitydialog.h @@ -51,7 +51,7 @@ public: static QWidget* showShutdownWindow(QMainWindow* window); protected: - void closeEvent(QCloseEvent *event); + void closeEvent(QCloseEvent *event) override; }; #endif // BITCOIN_QT_UTILITYDIALOG_H diff --git a/src/qt/walletcontroller.cpp b/src/qt/walletcontroller.cpp index 7cde3ca30b..20f2ef5b5f 100644 --- a/src/qt/walletcontroller.cpp +++ b/src/qt/walletcontroller.cpp @@ -248,7 +248,7 @@ void CreateWalletActivity::finish() if (!m_error_message.original.empty()) { QMessageBox::critical(m_parent_widget, tr("Create wallet failed"), QString::fromStdString(m_error_message.translated)); } else if (!m_warning_message.empty()) { - QMessageBox::warning(m_parent_widget, tr("Create wallet warning"), QString::fromStdString(Join(m_warning_message, "\n", OpTranslated))); + QMessageBox::warning(m_parent_widget, tr("Create wallet warning"), QString::fromStdString(Join(m_warning_message, Untranslated("\n")).translated)); } if (m_wallet_model) Q_EMIT created(m_wallet_model); @@ -289,7 +289,7 @@ void OpenWalletActivity::finish() if (!m_error_message.original.empty()) { QMessageBox::critical(m_parent_widget, tr("Open wallet failed"), QString::fromStdString(m_error_message.translated)); } else if (!m_warning_message.empty()) { - QMessageBox::warning(m_parent_widget, tr("Open wallet warning"), QString::fromStdString(Join(m_warning_message, "\n", OpTranslated))); + QMessageBox::warning(m_parent_widget, tr("Open wallet warning"), QString::fromStdString(Join(m_warning_message, Untranslated("\n")).translated)); } if (m_wallet_model) Q_EMIT opened(m_wallet_model); diff --git a/src/random.cpp b/src/random.cpp index b408b1e13e..5b8782d1ce 100644 --- a/src/random.cpp +++ b/src/random.cpp @@ -116,7 +116,10 @@ static uint64_t GetRdRand() noexcept // RdRand may very rarely fail. Invoke it up to 10 times in a loop to reduce this risk. #ifdef __i386__ uint8_t ok; - uint32_t r1, r2; + // Initialize to 0 to silence a compiler warning that r1 or r2 may be used + // uninitialized. Even if rdrand fails (!ok) it will set the output to 0, + // but there is no way that the compiler could know that. + uint32_t r1 = 0, r2 = 0; for (int i = 0; i < 10; ++i) { __asm__ volatile (".byte 0x0f, 0xc7, 0xf0; setc %1" : "=a"(r1), "=q"(ok) :: "cc"); // rdrand %eax if (ok) break; @@ -128,7 +131,7 @@ static uint64_t GetRdRand() noexcept return (((uint64_t)r2) << 32) | r1; #elif defined(__x86_64__) || defined(__amd64__) uint8_t ok; - uint64_t r1; + uint64_t r1 = 0; // See above why we initialize to 0. for (int i = 0; i < 10; ++i) { __asm__ volatile (".byte 0x48, 0x0f, 0xc7, 0xf0; setc %1" : "=a"(r1), "=q"(ok) :: "cc"); // rdrand %rax if (ok) break; diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index e2618c16da..219979f095 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -25,7 +25,8 @@ static std::string rpcWarmupStatus GUARDED_BY(cs_rpcWarmup) = "RPC server starte /* Timer-creating functions */ static RPCTimerInterface* timerInterface = nullptr; /* Map of name to timer. */ -static std::map<std::string, std::unique_ptr<RPCTimerBase> > deadlineTimers; +static Mutex g_deadline_timers_mutex; +static std::map<std::string, std::unique_ptr<RPCTimerBase> > deadlineTimers GUARDED_BY(g_deadline_timers_mutex); static bool ExecuteCommand(const CRPCCommand& command, const JSONRPCRequest& request, UniValue& result, bool last_handler); struct RPCCommandExecutionInfo @@ -298,7 +299,7 @@ void InterruptRPC() void StopRPC() { LogPrint(BCLog::RPC, "Stopping RPC\n"); - deadlineTimers.clear(); + WITH_LOCK(g_deadline_timers_mutex, deadlineTimers.clear()); DeleteAuthCookie(); g_rpcSignals.Stopped(); } @@ -486,6 +487,7 @@ void RPCRunLater(const std::string& name, std::function<void()> func, int64_t nS { if (!timerInterface) throw JSONRPCError(RPC_INTERNAL_ERROR, "No timer handler registered for RPC"); + LOCK(g_deadline_timers_mutex); deadlineTimers.erase(name); LogPrint(BCLog::RPC, "queue run of timer %s in %i seconds (using %s)\n", name, nSeconds, timerInterface->Name()); deadlineTimers.emplace(name, std::unique_ptr<RPCTimerBase>(timerInterface->NewTimer(func, nSeconds*1000))); diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp index 5279f40506..ed0175bb10 100644 --- a/src/script/descriptor.cpp +++ b/src/script/descriptor.cpp @@ -481,7 +481,7 @@ public: return AddChecksum(ret); } - bool ToPrivateString(const SigningProvider& arg, std::string& out) const override final + bool ToPrivateString(const SigningProvider& arg, std::string& out) const final { bool ret = ToStringHelper(&arg, out, true); out = AddChecksum(out); diff --git a/src/test/blockfilter_index_tests.cpp b/src/test/blockfilter_index_tests.cpp index ff01f730a3..e5043f6816 100644 --- a/src/test/blockfilter_index_tests.cpp +++ b/src/test/blockfilter_index_tests.cpp @@ -43,8 +43,8 @@ static bool CheckFilterLookups(BlockFilterIndex& filter_index, const CBlockIndex BOOST_CHECK(filter_index.LookupFilterHashRange(block_index->nHeight, block_index, filter_hashes)); - BOOST_CHECK_EQUAL(filters.size(), 1); - BOOST_CHECK_EQUAL(filter_hashes.size(), 1); + BOOST_CHECK_EQUAL(filters.size(), 1U); + BOOST_CHECK_EQUAL(filter_hashes.size(), 1U); BOOST_CHECK_EQUAL(filter.GetHash(), expected_filter.GetHash()); BOOST_CHECK_EQUAL(filter_header, expected_filter.ComputeHeader(last_header)); @@ -255,8 +255,9 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup) BOOST_CHECK(filter_index.LookupFilterRange(0, tip, filters)); BOOST_CHECK(filter_index.LookupFilterHashRange(0, tip, filter_hashes)); - BOOST_CHECK_EQUAL(filters.size(), tip->nHeight + 1); - BOOST_CHECK_EQUAL(filter_hashes.size(), tip->nHeight + 1); + assert(tip->nHeight >= 0); + BOOST_CHECK_EQUAL(filters.size(), tip->nHeight + 1U); + BOOST_CHECK_EQUAL(filter_hashes.size(), tip->nHeight + 1U); filters.clear(); filter_hashes.clear(); diff --git a/src/test/blockfilter_tests.cpp b/src/test/blockfilter_tests.cpp index e69503ef35..178c261365 100644 --- a/src/test/blockfilter_tests.cpp +++ b/src/test/blockfilter_tests.cpp @@ -42,14 +42,14 @@ BOOST_AUTO_TEST_CASE(gcsfilter_test) BOOST_AUTO_TEST_CASE(gcsfilter_default_constructor) { GCSFilter filter; - BOOST_CHECK_EQUAL(filter.GetN(), 0); - BOOST_CHECK_EQUAL(filter.GetEncoded().size(), 1); + BOOST_CHECK_EQUAL(filter.GetN(), 0U); + BOOST_CHECK_EQUAL(filter.GetEncoded().size(), 1U); const GCSFilter::Params& params = filter.GetParams(); - BOOST_CHECK_EQUAL(params.m_siphash_k0, 0); - BOOST_CHECK_EQUAL(params.m_siphash_k1, 0); + BOOST_CHECK_EQUAL(params.m_siphash_k0, 0U); + BOOST_CHECK_EQUAL(params.m_siphash_k1, 0U); BOOST_CHECK_EQUAL(params.m_P, 0); - BOOST_CHECK_EQUAL(params.m_M, 1); + BOOST_CHECK_EQUAL(params.m_M, 1U); } BOOST_AUTO_TEST_CASE(blockfilter_basic_test) diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp index a75d9fc9ec..736c260eeb 100644 --- a/src/test/bloom_tests.cpp +++ b/src/test/bloom_tests.cpp @@ -488,7 +488,7 @@ BOOST_AUTO_TEST_CASE(rolling_bloom) ++nHits; } // Expect about 100 hits - BOOST_CHECK_EQUAL(nHits, 75); + BOOST_CHECK_EQUAL(nHits, 75U); BOOST_CHECK(rb1.contains(data[DATASIZE-1])); rb1.reset(); @@ -516,7 +516,7 @@ BOOST_AUTO_TEST_CASE(rolling_bloom) ++nHits; } // Expect about 5 false positives - BOOST_CHECK_EQUAL(nHits, 6); + BOOST_CHECK_EQUAL(nHits, 6U); // last-1000-entry, 0.01% false positive: CRollingBloomFilter rb2(1000, 0.001); diff --git a/src/test/compress_tests.cpp b/src/test/compress_tests.cpp index df1a119d79..4ddbc8338e 100644 --- a/src/test/compress_tests.cpp +++ b/src/test/compress_tests.cpp @@ -70,14 +70,14 @@ BOOST_AUTO_TEST_CASE(compress_script_to_ckey_id) CPubKey pubkey = key.GetPubKey(); CScript script = CScript() << OP_DUP << OP_HASH160 << ToByteVector(pubkey.GetID()) << OP_EQUALVERIFY << OP_CHECKSIG; - BOOST_CHECK_EQUAL(script.size(), 25); + BOOST_CHECK_EQUAL(script.size(), 25U); std::vector<unsigned char> out; bool done = CompressScript(script, out); BOOST_CHECK_EQUAL(done, true); // Check compressed script - BOOST_CHECK_EQUAL(out.size(), 21); + BOOST_CHECK_EQUAL(out.size(), 21U); BOOST_CHECK_EQUAL(out[0], 0x00); BOOST_CHECK_EQUAL(memcmp(&out[1], &script[3], 20), 0); // compare the 20 relevant chars of the CKeyId in the script } @@ -87,14 +87,14 @@ BOOST_AUTO_TEST_CASE(compress_script_to_cscript_id) // case CScriptID CScript script, redeemScript; script << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL; - BOOST_CHECK_EQUAL(script.size(), 23); + BOOST_CHECK_EQUAL(script.size(), 23U); std::vector<unsigned char> out; bool done = CompressScript(script, out); BOOST_CHECK_EQUAL(done, true); // Check compressed script - BOOST_CHECK_EQUAL(out.size(), 21); + BOOST_CHECK_EQUAL(out.size(), 21U); BOOST_CHECK_EQUAL(out[0], 0x01); BOOST_CHECK_EQUAL(memcmp(&out[1], &script[2], 20), 0); // compare the 20 relevant chars of the CScriptId in the script } @@ -105,14 +105,14 @@ BOOST_AUTO_TEST_CASE(compress_script_to_compressed_pubkey_id) key.MakeNewKey(true); // case compressed PubKeyID CScript script = CScript() << ToByteVector(key.GetPubKey()) << OP_CHECKSIG; // COMPRESSED_PUBLIC_KEY_SIZE (33) - BOOST_CHECK_EQUAL(script.size(), 35); + BOOST_CHECK_EQUAL(script.size(), 35U); std::vector<unsigned char> out; bool done = CompressScript(script, out); BOOST_CHECK_EQUAL(done, true); // Check compressed script - BOOST_CHECK_EQUAL(out.size(), 33); + BOOST_CHECK_EQUAL(out.size(), 33U); BOOST_CHECK_EQUAL(memcmp(&out[0], &script[1], 1), 0); BOOST_CHECK_EQUAL(memcmp(&out[1], &script[2], 32), 0); // compare the 32 chars of the compressed CPubKey } @@ -122,14 +122,14 @@ BOOST_AUTO_TEST_CASE(compress_script_to_uncompressed_pubkey_id) CKey key; key.MakeNewKey(false); // case uncompressed PubKeyID CScript script = CScript() << ToByteVector(key.GetPubKey()) << OP_CHECKSIG; // PUBLIC_KEY_SIZE (65) - BOOST_CHECK_EQUAL(script.size(), 67); // 1 char code + 65 char pubkey + OP_CHECKSIG + BOOST_CHECK_EQUAL(script.size(), 67U); // 1 char code + 65 char pubkey + OP_CHECKSIG std::vector<unsigned char> out; bool done = CompressScript(script, out); BOOST_CHECK_EQUAL(done, true); // Check compressed script - BOOST_CHECK_EQUAL(out.size(), 33); + BOOST_CHECK_EQUAL(out.size(), 33U); BOOST_CHECK_EQUAL(memcmp(&out[1], &script[2], 32), 0); // first 32 chars of CPubKey are copied into out[1:] BOOST_CHECK_EQUAL(out[0], 0x04 | (script[65] & 0x01)); // least significant bit (lsb) of last char of pubkey is mapped into out[0] } diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp index 6314c1a42f..75b38670c9 100644 --- a/src/test/denialofservice_tests.cpp +++ b/src/test/denialofservice_tests.cpp @@ -151,11 +151,11 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management) auto peerLogic = MakeUnique<PeerLogicValidation>(connman.get(), nullptr, *m_node.scheduler, *m_node.mempool); const Consensus::Params& consensusParams = Params().GetConsensus(); - constexpr int max_outbound_full_relay = 8; + constexpr int max_outbound_full_relay = MAX_OUTBOUND_FULL_RELAY_CONNECTIONS; CConnman::Options options; - options.nMaxConnections = 125; + options.nMaxConnections = DEFAULT_MAX_PEER_CONNECTIONS; options.m_max_outbound_full_relay = max_outbound_full_relay; - options.nMaxFeeler = 1; + options.nMaxFeeler = MAX_FEELER_CONNECTIONS; connman->Init(options); std::vector<CNode *> vNodes; diff --git a/src/test/descriptor_tests.cpp b/src/test/descriptor_tests.cpp index 121b80ab2d..5f9a78ceb2 100644 --- a/src/test/descriptor_tests.cpp +++ b/src/test/descriptor_tests.cpp @@ -232,7 +232,7 @@ void DoCheck(const std::string& prv, const std::string& pub, int flags, const st std::vector<CScript> spks_inferred; FlatSigningProvider provider_inferred; BOOST_CHECK(inferred->Expand(0, provider_inferred, spks_inferred, provider_inferred)); - BOOST_CHECK_EQUAL(spks_inferred.size(), 1); + BOOST_CHECK_EQUAL(spks_inferred.size(), 1U); BOOST_CHECK(spks_inferred[0] == spks[n]); BOOST_CHECK_EQUAL(IsSolvable(provider_inferred, spks_inferred[0]), !(flags & UNSOLVABLE)); BOOST_CHECK(provider_inferred.origins == script_provider.origins); diff --git a/src/test/flatfile_tests.cpp b/src/test/flatfile_tests.cpp index 9bb0b3ef02..be7484cd0b 100644 --- a/src/test/flatfile_tests.cpp +++ b/src/test/flatfile_tests.cpp @@ -93,16 +93,16 @@ BOOST_AUTO_TEST_CASE(flatfile_allocate) bool out_of_space; - BOOST_CHECK_EQUAL(seq.Allocate(FlatFilePos(0, 0), 1, out_of_space), 100); - BOOST_CHECK_EQUAL(fs::file_size(seq.FileName(FlatFilePos(0, 0))), 100); + BOOST_CHECK_EQUAL(seq.Allocate(FlatFilePos(0, 0), 1, out_of_space), 100U); + BOOST_CHECK_EQUAL(fs::file_size(seq.FileName(FlatFilePos(0, 0))), 100U); BOOST_CHECK(!out_of_space); - BOOST_CHECK_EQUAL(seq.Allocate(FlatFilePos(0, 99), 1, out_of_space), 0); - BOOST_CHECK_EQUAL(fs::file_size(seq.FileName(FlatFilePos(0, 99))), 100); + BOOST_CHECK_EQUAL(seq.Allocate(FlatFilePos(0, 99), 1, out_of_space), 0U); + BOOST_CHECK_EQUAL(fs::file_size(seq.FileName(FlatFilePos(0, 99))), 100U); BOOST_CHECK(!out_of_space); - BOOST_CHECK_EQUAL(seq.Allocate(FlatFilePos(0, 99), 2, out_of_space), 101); - BOOST_CHECK_EQUAL(fs::file_size(seq.FileName(FlatFilePos(0, 99))), 200); + BOOST_CHECK_EQUAL(seq.Allocate(FlatFilePos(0, 99), 2, out_of_space), 101U); + BOOST_CHECK_EQUAL(fs::file_size(seq.FileName(FlatFilePos(0, 99))), 200U); BOOST_CHECK(!out_of_space); } @@ -116,11 +116,11 @@ BOOST_AUTO_TEST_CASE(flatfile_flush) // Flush without finalize should not truncate file. seq.Flush(FlatFilePos(0, 1)); - BOOST_CHECK_EQUAL(fs::file_size(seq.FileName(FlatFilePos(0, 1))), 100); + BOOST_CHECK_EQUAL(fs::file_size(seq.FileName(FlatFilePos(0, 1))), 100U); // Flush with finalize should truncate file. seq.Flush(FlatFilePos(0, 1), true); - BOOST_CHECK_EQUAL(fs::file_size(seq.FileName(FlatFilePos(0, 1))), 1); + BOOST_CHECK_EQUAL(fs::file_size(seq.FileName(FlatFilePos(0, 1))), 1U); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/fuzz/asmap.cpp b/src/test/fuzz/asmap.cpp index 7f3eef79a1..ea56277eac 100644 --- a/src/test/fuzz/asmap.cpp +++ b/src/test/fuzz/asmap.cpp @@ -3,26 +3,47 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include <netaddress.h> -#include <test/fuzz/FuzzedDataProvider.h> #include <test/fuzz/fuzz.h> #include <cstdint> #include <vector> +//! asmap code that consumes nothing +static const std::vector<bool> IPV6_PREFIX_ASMAP = {}; + +//! asmap code that consumes the 96 prefix bits of ::ffff:0/96 (IPv4-in-IPv6 map) +static const std::vector<bool> IPV4_PREFIX_ASMAP = { + true, true, false, true, true, true, true, true, true, true, false, false, false, false, false, false, false, false, // Match 0x00 + true, true, false, true, true, true, true, true, true, true, false, false, false, false, false, false, false, false, // Match 0x00 + true, true, false, true, true, true, true, true, true, true, false, false, false, false, false, false, false, false, // Match 0x00 + true, true, false, true, true, true, true, true, true, true, false, false, false, false, false, false, false, false, // Match 0x00 + true, true, false, true, true, true, true, true, true, true, false, false, false, false, false, false, false, false, // Match 0x00 + true, true, false, true, true, true, true, true, true, true, false, false, false, false, false, false, false, false, // Match 0x00 + true, true, false, true, true, true, true, true, true, true, false, false, false, false, false, false, false, false, // Match 0x00 + true, true, false, true, true, true, true, true, true, true, false, false, false, false, false, false, false, false, // Match 0x00 + true, true, false, true, true, true, true, true, true, true, false, false, false, false, false, false, false, false, // Match 0x00 + true, true, false, true, true, true, true, true, true, true, false, false, false, false, false, false, false, false, // Match 0x00 + true, true, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true, // Match 0xFF + true, true, false, true, true, true, true, true, true, true, true, true, true, true, true, true, true, true // Match 0xFF +}; + void test_one_input(const std::vector<uint8_t>& buffer) { - FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); - const Network network = fuzzed_data_provider.PickValueInArray({NET_IPV4, NET_IPV6}); - if (fuzzed_data_provider.remaining_bytes() < 16) { - return; - } - CNetAddr net_addr; - net_addr.SetRaw(network, fuzzed_data_provider.ConsumeBytes<uint8_t>(16).data()); - std::vector<bool> asmap; - for (const char cur_byte : fuzzed_data_provider.ConsumeRemainingBytes<char>()) { - for (int bit = 0; bit < 8; ++bit) { - asmap.push_back((cur_byte >> bit) & 1); + // Encoding: [7 bits: asmap size] [1 bit: ipv6?] [3-130 bytes: asmap] [4 or 16 bytes: addr] + if (buffer.size() < 1 + 3 + 4) return; + int asmap_size = 3 + (buffer[0] & 127); + bool ipv6 = buffer[0] & 128; + int addr_size = ipv6 ? 16 : 4; + if (buffer.size() < size_t(1 + asmap_size + addr_size)) return; + std::vector<bool> asmap = ipv6 ? IPV6_PREFIX_ASMAP : IPV4_PREFIX_ASMAP; + asmap.reserve(asmap.size() + 8 * asmap_size); + for (int i = 0; i < asmap_size; ++i) { + for (int j = 0; j < 8; ++j) { + asmap.push_back((buffer[1 + i] >> j) & 1); } } + if (!SanityCheckASMap(asmap)) return; + CNetAddr net_addr; + net_addr.SetRaw(ipv6 ? NET_IPV6 : NET_IPV4, buffer.data() + 1 + asmap_size); (void)net_addr.GetMappedAS(asmap); } diff --git a/src/test/fuzz/asmap_direct.cpp b/src/test/fuzz/asmap_direct.cpp new file mode 100644 index 0000000000..6d8a65f5ab --- /dev/null +++ b/src/test/fuzz/asmap_direct.cpp @@ -0,0 +1,46 @@ +// Copyright (c) 2020 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 <util/asmap.h> +#include <test/fuzz/fuzz.h> + +#include <cstdint> +#include <optional> +#include <vector> + +#include <assert.h> + +void test_one_input(const std::vector<uint8_t>& buffer) +{ + // Encoding: [asmap using 1 bit / byte] 0xFF [addr using 1 bit / byte] + std::optional<size_t> sep_pos_opt; + for (size_t pos = 0; pos < buffer.size(); ++pos) { + uint8_t x = buffer[pos]; + if ((x & 0xFE) == 0) continue; + if (x == 0xFF) { + if (sep_pos_opt) return; + sep_pos_opt = pos; + } else { + return; + } + } + if (!sep_pos_opt) return; // Needs exactly 1 separator + const size_t sep_pos{sep_pos_opt.value()}; + if (buffer.size() - sep_pos - 1 > 128) return; // At most 128 bits in IP address + + // Checks on asmap + std::vector<bool> asmap(buffer.begin(), buffer.begin() + sep_pos); + if (SanityCheckASMap(asmap, buffer.size() - 1 - sep_pos)) { + // Verify that for valid asmaps, no prefix (except up to 7 zero padding bits) is valid. + std::vector<bool> asmap_prefix = asmap; + while (!asmap_prefix.empty() && asmap_prefix.size() + 7 > asmap.size() && asmap_prefix.back() == false) asmap_prefix.pop_back(); + while (!asmap_prefix.empty()) { + asmap_prefix.pop_back(); + assert(!SanityCheckASMap(asmap_prefix, buffer.size() - 1 - sep_pos)); + } + // No address input should trigger assertions in interpreter + std::vector<bool> addr(buffer.begin() + sep_pos + 1, buffer.end()); + (void)Interpret(asmap, addr); + } +} diff --git a/src/test/fuzz/bloom_filter.cpp b/src/test/fuzz/bloom_filter.cpp index 50036ce5bd..7039bf16c1 100644 --- a/src/test/fuzz/bloom_filter.cpp +++ b/src/test/fuzz/bloom_filter.cpp @@ -25,7 +25,7 @@ void test_one_input(const std::vector<uint8_t>& buffer) fuzzed_data_provider.ConsumeIntegral<unsigned int>(), static_cast<unsigned char>(fuzzed_data_provider.PickValueInArray({BLOOM_UPDATE_NONE, BLOOM_UPDATE_ALL, BLOOM_UPDATE_P2PUBKEY_ONLY, BLOOM_UPDATE_MASK}))}; while (fuzzed_data_provider.remaining_bytes() > 0) { - switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 4)) { + switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 3)) { case 0: { const std::vector<unsigned char> b = ConsumeRandomLengthByteVector(fuzzed_data_provider); (void)bloom_filter.contains(b); @@ -65,9 +65,6 @@ void test_one_input(const std::vector<uint8_t>& buffer) (void)bloom_filter.IsRelevantAndUpdate(tx); break; } - case 4: - bloom_filter.UpdateEmptyFull(); - break; } (void)bloom_filter.IsWithinSizeConstraints(); } diff --git a/src/test/fuzz/system.cpp b/src/test/fuzz/system.cpp index 7f378c2b13..01b523cee4 100644 --- a/src/test/fuzz/system.cpp +++ b/src/test/fuzz/system.cpp @@ -85,7 +85,7 @@ void test_one_input(const std::vector<uint8_t>& buffer) case 7: { const std::vector<std::string> random_arguments = ConsumeRandomLengthStringVector(fuzzed_data_provider); std::vector<const char*> argv; - argv.resize(random_arguments.size()); + argv.reserve(random_arguments.size()); for (const std::string& random_argument : random_arguments) { argv.push_back(random_argument.c_str()); } diff --git a/src/test/netbase_tests.cpp b/src/test/netbase_tests.cpp index ec6a290334..2e1972cc3f 100644 --- a/src/test/netbase_tests.cpp +++ b/src/test/netbase_tests.cpp @@ -393,7 +393,7 @@ BOOST_AUTO_TEST_CASE(netpermissions_test) BOOST_CHECK(NetWhitelistPermissions::TryParse("bloom,forcerelay,noban,relay,mempool@1.2.3.4/32", whitelistPermissions, error)); const auto strings = NetPermissions::ToStrings(PF_ALL); - BOOST_CHECK_EQUAL(strings.size(), 5); + BOOST_CHECK_EQUAL(strings.size(), 5U); BOOST_CHECK(std::find(strings.begin(), strings.end(), "bloomfilter") != strings.end()); BOOST_CHECK(std::find(strings.begin(), strings.end(), "forcerelay") != strings.end()); BOOST_CHECK(std::find(strings.begin(), strings.end(), "relay") != strings.end()); diff --git a/src/test/random_tests.cpp b/src/test/random_tests.cpp index ca3b92f2e1..d1f60e8972 100644 --- a/src/test/random_tests.cpp +++ b/src/test/random_tests.cpp @@ -127,7 +127,7 @@ BOOST_AUTO_TEST_CASE(shuffle_stat_test) } BOOST_CHECK(chi_score > 58.1411); // 99.9999% confidence interval BOOST_CHECK(chi_score < 210.275); - BOOST_CHECK_EQUAL(sum, 12000); + BOOST_CHECK_EQUAL(sum, 12000U); } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/script_standard_tests.cpp b/src/test/script_standard_tests.cpp index de990d9254..b185d3b4ac 100644 --- a/src/test/script_standard_tests.cpp +++ b/src/test/script_standard_tests.cpp @@ -13,6 +13,12 @@ BOOST_FIXTURE_TEST_SUITE(script_standard_tests, BasicTestingSetup) +BOOST_AUTO_TEST_CASE(dest_default_is_no_dest) +{ + CTxDestination dest; + BOOST_CHECK(!IsValidDestination(dest)); +} + BOOST_AUTO_TEST_CASE(script_standard_Solver_success) { CKey keys[3]; diff --git a/src/test/streams_tests.cpp b/src/test/streams_tests.cpp index 177d8fda73..c509a252e0 100644 --- a/src/test/streams_tests.cpp +++ b/src/test/streams_tests.cpp @@ -72,28 +72,28 @@ BOOST_AUTO_TEST_CASE(streams_vector_reader) std::vector<unsigned char> vch = {1, 255, 3, 4, 5, 6}; VectorReader reader(SER_NETWORK, INIT_PROTO_VERSION, vch, 0); - BOOST_CHECK_EQUAL(reader.size(), 6); + BOOST_CHECK_EQUAL(reader.size(), 6U); BOOST_CHECK(!reader.empty()); // Read a single byte as an unsigned char. unsigned char a; reader >> a; BOOST_CHECK_EQUAL(a, 1); - BOOST_CHECK_EQUAL(reader.size(), 5); + BOOST_CHECK_EQUAL(reader.size(), 5U); BOOST_CHECK(!reader.empty()); // Read a single byte as a signed char. signed char b; reader >> b; BOOST_CHECK_EQUAL(b, -1); - BOOST_CHECK_EQUAL(reader.size(), 4); + BOOST_CHECK_EQUAL(reader.size(), 4U); BOOST_CHECK(!reader.empty()); // Read a 4 bytes as an unsigned int. unsigned int c; reader >> c; - BOOST_CHECK_EQUAL(c, 100992003); // 3,4,5,6 in little-endian base-256 - BOOST_CHECK_EQUAL(reader.size(), 0); + BOOST_CHECK_EQUAL(c, 100992003U); // 3,4,5,6 in little-endian base-256 + BOOST_CHECK_EQUAL(reader.size(), 0U); BOOST_CHECK(reader.empty()); // Reading after end of byte vector throws an error. @@ -104,7 +104,7 @@ BOOST_AUTO_TEST_CASE(streams_vector_reader) VectorReader new_reader(SER_NETWORK, INIT_PROTO_VERSION, vch, 0); new_reader >> d; BOOST_CHECK_EQUAL(d, 67370753); // 1,255,3,4 in little-endian base-256 - BOOST_CHECK_EQUAL(new_reader.size(), 2); + BOOST_CHECK_EQUAL(new_reader.size(), 2U); BOOST_CHECK(!new_reader.empty()); // Reading after end of byte vector throws an error even if the reader is @@ -136,14 +136,14 @@ BOOST_AUTO_TEST_CASE(bitstream_reader_writer) BOOST_CHECK_EQUAL(serialized_int2, (uint16_t)0x1072); // NOTE: Serialized as LE BitStreamReader<CDataStream> bit_reader(data_copy); - BOOST_CHECK_EQUAL(bit_reader.Read(1), 0); - BOOST_CHECK_EQUAL(bit_reader.Read(2), 2); - BOOST_CHECK_EQUAL(bit_reader.Read(3), 6); - BOOST_CHECK_EQUAL(bit_reader.Read(4), 11); - BOOST_CHECK_EQUAL(bit_reader.Read(5), 1); - BOOST_CHECK_EQUAL(bit_reader.Read(6), 32); - BOOST_CHECK_EQUAL(bit_reader.Read(7), 7); - BOOST_CHECK_EQUAL(bit_reader.Read(16), 30497); + BOOST_CHECK_EQUAL(bit_reader.Read(1), 0U); + BOOST_CHECK_EQUAL(bit_reader.Read(2), 2U); + BOOST_CHECK_EQUAL(bit_reader.Read(3), 6U); + BOOST_CHECK_EQUAL(bit_reader.Read(4), 11U); + BOOST_CHECK_EQUAL(bit_reader.Read(5), 1U); + BOOST_CHECK_EQUAL(bit_reader.Read(6), 32U); + BOOST_CHECK_EQUAL(bit_reader.Read(7), 7U); + BOOST_CHECK_EQUAL(bit_reader.Read(16), 30497U); BOOST_CHECK_THROW(bit_reader.Read(8), std::ios_base::failure); } @@ -236,7 +236,7 @@ BOOST_AUTO_TEST_CASE(streams_buffered_file) BOOST_CHECK_EQUAL(i, 1); // After reading bytes 0 and 1, we're positioned at 2. - BOOST_CHECK_EQUAL(bf.GetPos(), 2); + BOOST_CHECK_EQUAL(bf.GetPos(), 2U); // Rewind to offset 0, ok (within the 10 byte window). BOOST_CHECK(bf.SetPos(0)); @@ -263,18 +263,18 @@ BOOST_AUTO_TEST_CASE(streams_buffered_file) // The default argument removes the limit completely. BOOST_CHECK(bf.SetLimit()); // The read position should still be at 3 (no change). - BOOST_CHECK_EQUAL(bf.GetPos(), 3); + BOOST_CHECK_EQUAL(bf.GetPos(), 3U); // Read from current offset, 3, forward until position 10. for (uint8_t j = 3; j < 10; ++j) { bf >> i; BOOST_CHECK_EQUAL(i, j); } - BOOST_CHECK_EQUAL(bf.GetPos(), 10); + BOOST_CHECK_EQUAL(bf.GetPos(), 10U); // We're guaranteed (just barely) to be able to rewind to zero. BOOST_CHECK(bf.SetPos(0)); - BOOST_CHECK_EQUAL(bf.GetPos(), 0); + BOOST_CHECK_EQUAL(bf.GetPos(), 0U); bf >> i; BOOST_CHECK_EQUAL(i, 0); @@ -284,12 +284,12 @@ BOOST_AUTO_TEST_CASE(streams_buffered_file) BOOST_CHECK(bf.SetPos(10)); bf >> i; BOOST_CHECK_EQUAL(i, 10); - BOOST_CHECK_EQUAL(bf.GetPos(), 11); + BOOST_CHECK_EQUAL(bf.GetPos(), 11U); // Now it's only guaranteed that we can rewind to offset 1 // (current read position, 11, minus rewind amount, 10). BOOST_CHECK(bf.SetPos(1)); - BOOST_CHECK_EQUAL(bf.GetPos(), 1); + BOOST_CHECK_EQUAL(bf.GetPos(), 1U); bf >> i; BOOST_CHECK_EQUAL(i, 1); @@ -303,7 +303,7 @@ BOOST_AUTO_TEST_CASE(streams_buffered_file) BOOST_CHECK_EQUAL(a[j], 11 + j); } } - BOOST_CHECK_EQUAL(bf.GetPos(), 40); + BOOST_CHECK_EQUAL(bf.GetPos(), 40U); // We've read the entire file, the next read should throw. try { @@ -317,11 +317,11 @@ BOOST_AUTO_TEST_CASE(streams_buffered_file) BOOST_CHECK(bf.eof()); // Still at offset 40, we can go back 10, to 30. - BOOST_CHECK_EQUAL(bf.GetPos(), 40); + BOOST_CHECK_EQUAL(bf.GetPos(), 40U); BOOST_CHECK(bf.SetPos(30)); bf >> i; BOOST_CHECK_EQUAL(i, 30); - BOOST_CHECK_EQUAL(bf.GetPos(), 31); + BOOST_CHECK_EQUAL(bf.GetPos(), 31U); // We're too far to rewind to position zero. BOOST_CHECK(!bf.SetPos(0)); diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp index 45b7fd4932..cf26ca3adb 100644 --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -240,9 +240,9 @@ public: BOOST_CHECK_EQUAL(test.GetSetting("-value").write(), expect.setting.write()); auto settings_list = test.GetSettingsList("-value"); if (expect.setting.isNull() || expect.setting.isFalse()) { - BOOST_CHECK_EQUAL(settings_list.size(), 0); + BOOST_CHECK_EQUAL(settings_list.size(), 0U); } else { - BOOST_CHECK_EQUAL(settings_list.size(), 1); + BOOST_CHECK_EQUAL(settings_list.size(), 1U); BOOST_CHECK_EQUAL(settings_list[0].write(), expect.setting.write()); } @@ -1911,7 +1911,7 @@ BOOST_AUTO_TEST_CASE(test_spanparsing) input = "xxx"; results = Split(MakeSpan(input), 'x'); - BOOST_CHECK_EQUAL(results.size(), 4); + BOOST_CHECK_EQUAL(results.size(), 4U); BOOST_CHECK_EQUAL(SpanToStr(results[0]), ""); BOOST_CHECK_EQUAL(SpanToStr(results[1]), ""); BOOST_CHECK_EQUAL(SpanToStr(results[2]), ""); @@ -1919,19 +1919,19 @@ BOOST_AUTO_TEST_CASE(test_spanparsing) input = "one#two#three"; results = Split(MakeSpan(input), '-'); - BOOST_CHECK_EQUAL(results.size(), 1); + BOOST_CHECK_EQUAL(results.size(), 1U); BOOST_CHECK_EQUAL(SpanToStr(results[0]), "one#two#three"); input = "one#two#three"; results = Split(MakeSpan(input), '#'); - BOOST_CHECK_EQUAL(results.size(), 3); + BOOST_CHECK_EQUAL(results.size(), 3U); BOOST_CHECK_EQUAL(SpanToStr(results[0]), "one"); BOOST_CHECK_EQUAL(SpanToStr(results[1]), "two"); BOOST_CHECK_EQUAL(SpanToStr(results[2]), "three"); input = "*foo*bar*"; results = Split(MakeSpan(input), '*'); - BOOST_CHECK_EQUAL(results.size(), 4); + BOOST_CHECK_EQUAL(results.size(), 4U); BOOST_CHECK_EQUAL(SpanToStr(results[0]), ""); BOOST_CHECK_EQUAL(SpanToStr(results[1]), "foo"); BOOST_CHECK_EQUAL(SpanToStr(results[2]), "bar"); @@ -1990,24 +1990,24 @@ BOOST_AUTO_TEST_CASE(test_tracked_vector) BOOST_CHECK(t3.origin == &t3); auto v1 = Vector(t1); - BOOST_CHECK_EQUAL(v1.size(), 1); + BOOST_CHECK_EQUAL(v1.size(), 1U); BOOST_CHECK(v1[0].origin == &t1); BOOST_CHECK_EQUAL(v1[0].copies, 1); auto v2 = Vector(std::move(t2)); - BOOST_CHECK_EQUAL(v2.size(), 1); + BOOST_CHECK_EQUAL(v2.size(), 1U); BOOST_CHECK(v2[0].origin == &t2); BOOST_CHECK_EQUAL(v2[0].copies, 0); auto v3 = Vector(t1, std::move(t2)); - BOOST_CHECK_EQUAL(v3.size(), 2); + BOOST_CHECK_EQUAL(v3.size(), 2U); BOOST_CHECK(v3[0].origin == &t1); BOOST_CHECK(v3[1].origin == &t2); BOOST_CHECK_EQUAL(v3[0].copies, 1); BOOST_CHECK_EQUAL(v3[1].copies, 0); auto v4 = Vector(std::move(v3[0]), v3[1], std::move(t3)); - BOOST_CHECK_EQUAL(v4.size(), 3); + BOOST_CHECK_EQUAL(v4.size(), 3U); BOOST_CHECK(v4[0].origin == &t1); BOOST_CHECK(v4[1].origin == &t2); BOOST_CHECK(v4[2].origin == &t3); @@ -2016,7 +2016,7 @@ BOOST_AUTO_TEST_CASE(test_tracked_vector) BOOST_CHECK_EQUAL(v4[2].copies, 0); auto v5 = Cat(v1, v4); - BOOST_CHECK_EQUAL(v5.size(), 4); + BOOST_CHECK_EQUAL(v5.size(), 4U); BOOST_CHECK(v5[0].origin == &t1); BOOST_CHECK(v5[1].origin == &t1); BOOST_CHECK(v5[2].origin == &t2); @@ -2027,7 +2027,7 @@ BOOST_AUTO_TEST_CASE(test_tracked_vector) BOOST_CHECK_EQUAL(v5[3].copies, 1); auto v6 = Cat(std::move(v1), v3); - BOOST_CHECK_EQUAL(v6.size(), 3); + BOOST_CHECK_EQUAL(v6.size(), 3U); BOOST_CHECK(v6[0].origin == &t1); BOOST_CHECK(v6[1].origin == &t1); BOOST_CHECK(v6[2].origin == &t2); @@ -2036,7 +2036,7 @@ BOOST_AUTO_TEST_CASE(test_tracked_vector) BOOST_CHECK_EQUAL(v6[2].copies, 1); auto v7 = Cat(v2, std::move(v4)); - BOOST_CHECK_EQUAL(v7.size(), 4); + BOOST_CHECK_EQUAL(v7.size(), 4U); BOOST_CHECK(v7[0].origin == &t2); BOOST_CHECK(v7[1].origin == &t1); BOOST_CHECK(v7[2].origin == &t2); @@ -2047,7 +2047,7 @@ BOOST_AUTO_TEST_CASE(test_tracked_vector) BOOST_CHECK_EQUAL(v7[3].copies, 0); auto v8 = Cat(std::move(v2), std::move(v3)); - BOOST_CHECK_EQUAL(v8.size(), 3); + BOOST_CHECK_EQUAL(v8.size(), 3U); BOOST_CHECK(v8[0].origin == &t2); BOOST_CHECK(v8[1].origin == &t1); BOOST_CHECK(v8[2].origin == &t2); diff --git a/src/test/util_threadnames_tests.cpp b/src/test/util_threadnames_tests.cpp index f226caf717..4dcc080b2d 100644 --- a/src/test/util_threadnames_tests.cpp +++ b/src/test/util_threadnames_tests.cpp @@ -62,7 +62,7 @@ BOOST_AUTO_TEST_CASE(util_threadnames_test_rename_threaded) std::set<std::string> names = RenameEnMasse(100); - BOOST_CHECK_EQUAL(names.size(), 100); + BOOST_CHECK_EQUAL(names.size(), 100U); // Names "test_thread.[n]" should exist for n = [0, 99] for (int i = 0; i < 100; ++i) { diff --git a/src/test/validation_flush_tests.cpp b/src/test/validation_flush_tests.cpp index 388a2dbd13..a863e3a4d5 100644 --- a/src/test/validation_flush_tests.cpp +++ b/src/test/validation_flush_tests.cpp @@ -46,7 +46,7 @@ BOOST_AUTO_TEST_CASE(getcoinscachesizestate) // (prevector<28, unsigned char>) when assigned 56 bytes of data per above. // // See also: Coin::DynamicMemoryUsage(). - constexpr int COIN_SIZE = is_64_bit ? 80 : 64; + constexpr unsigned int COIN_SIZE = is_64_bit ? 80 : 64; auto print_view_mem_usage = [](CCoinsViewCache& view) { BOOST_TEST_MESSAGE("CCoinsViewCache memory usage: " << view.DynamicMemoryUsage()); @@ -79,7 +79,7 @@ BOOST_AUTO_TEST_CASE(getcoinscachesizestate) } print_view_mem_usage(view); - BOOST_CHECK_EQUAL(view.DynamicMemoryUsage(), is_64_bit ? 32 : 16); + BOOST_CHECK_EQUAL(view.DynamicMemoryUsage(), is_64_bit ? 32U : 16U); // We should be able to add COINS_UNTIL_CRITICAL coins to the cache before going CRITICAL. // This is contingent not only on the dynamic memory usage of the Coins diff --git a/src/timedata.cpp b/src/timedata.cpp index 942b3cb919..dd56acf44f 100644 --- a/src/timedata.cpp +++ b/src/timedata.cpp @@ -101,8 +101,8 @@ void AddTimeData(const CNetAddr& ip, int64_t nOffsetSample) if (!fMatch) { fDone = true; - std::string strMessage = strprintf(_("Please check that your computer's date and time are correct! If your clock is wrong, %s will not work properly.").translated, PACKAGE_NAME); - SetMiscWarning(strMessage); + bilingual_str strMessage = strprintf(_("Please check that your computer's date and time are correct! If your clock is wrong, %s will not work properly."), PACKAGE_NAME); + SetMiscWarning(strMessage.translated); uiInterface.ThreadSafeMessageBox(strMessage, "", CClientUIInterface::MSG_WARNING); } } diff --git a/src/ui_interface.cpp b/src/ui_interface.cpp index 85bb746d19..bb41154afc 100644 --- a/src/ui_interface.cpp +++ b/src/ui_interface.cpp @@ -4,6 +4,8 @@ #include <ui_interface.h> +#include <util/translation.h> + #include <boost/signals2/last_value.hpp> #include <boost/signals2/signal.hpp> @@ -40,8 +42,8 @@ ADD_SIGNALS_IMPL_WRAPPER(NotifyBlockTip); ADD_SIGNALS_IMPL_WRAPPER(NotifyHeaderTip); ADD_SIGNALS_IMPL_WRAPPER(BannedListChanged); -bool CClientUIInterface::ThreadSafeMessageBox(const std::string& message, const std::string& caption, unsigned int style) { return g_ui_signals.ThreadSafeMessageBox(message, caption, style); } -bool CClientUIInterface::ThreadSafeQuestion(const std::string& message, const std::string& non_interactive_message, const std::string& caption, unsigned int style) { return g_ui_signals.ThreadSafeQuestion(message, non_interactive_message, caption, style); } +bool CClientUIInterface::ThreadSafeMessageBox(const bilingual_str& message, const std::string& caption, unsigned int style) { return g_ui_signals.ThreadSafeMessageBox(message, caption, style); } +bool CClientUIInterface::ThreadSafeQuestion(const bilingual_str& message, const std::string& non_interactive_message, const std::string& caption, unsigned int style) { return g_ui_signals.ThreadSafeQuestion(message, non_interactive_message, caption, style); } void CClientUIInterface::InitMessage(const std::string& message) { return g_ui_signals.InitMessage(message); } void CClientUIInterface::NotifyNumConnectionsChanged(int newNumConnections) { return g_ui_signals.NotifyNumConnectionsChanged(newNumConnections); } void CClientUIInterface::NotifyNetworkActiveChanged(bool networkActive) { return g_ui_signals.NotifyNetworkActiveChanged(networkActive); } @@ -51,14 +53,13 @@ void CClientUIInterface::NotifyBlockTip(bool b, const CBlockIndex* i) { return g void CClientUIInterface::NotifyHeaderTip(bool b, const CBlockIndex* i) { return g_ui_signals.NotifyHeaderTip(b, i); } void CClientUIInterface::BannedListChanged() { return g_ui_signals.BannedListChanged(); } - -bool InitError(const std::string& str) +bool InitError(const bilingual_str& str) { uiInterface.ThreadSafeMessageBox(str, "", CClientUIInterface::MSG_ERROR); return false; } -void InitWarning(const std::string& str) +void InitWarning(const bilingual_str& str) { uiInterface.ThreadSafeMessageBox(str, "", CClientUIInterface::MSG_WARNING); } diff --git a/src/ui_interface.h b/src/ui_interface.h index b402177b85..9c49451e84 100644 --- a/src/ui_interface.h +++ b/src/ui_interface.h @@ -11,6 +11,8 @@ #include <string> class CBlockIndex; +struct bilingual_str; + namespace boost { namespace signals2 { class connection; @@ -82,10 +84,10 @@ public: boost::signals2::connection signal_name##_connect(std::function<signal_name##Sig> fn); /** Show message box. */ - ADD_SIGNALS_DECL_WRAPPER(ThreadSafeMessageBox, bool, const std::string& message, const std::string& caption, unsigned int style); + ADD_SIGNALS_DECL_WRAPPER(ThreadSafeMessageBox, bool, const bilingual_str& message, const std::string& caption, unsigned int style); /** If possible, ask the user a question. If not, falls back to ThreadSafeMessageBox(noninteractive_message, caption, style) and returns false. */ - ADD_SIGNALS_DECL_WRAPPER(ThreadSafeQuestion, bool, const std::string& message, const std::string& noninteractive_message, const std::string& caption, unsigned int style); + ADD_SIGNALS_DECL_WRAPPER(ThreadSafeQuestion, bool, const bilingual_str& message, const std::string& noninteractive_message, const std::string& caption, unsigned int style); /** Progress message during initialization. */ ADD_SIGNALS_DECL_WRAPPER(InitMessage, void, const std::string& message); @@ -118,10 +120,10 @@ public: }; /** Show warning message **/ -void InitWarning(const std::string& str); +void InitWarning(const bilingual_str& str); /** Show error message **/ -bool InitError(const std::string& str); +bool InitError(const bilingual_str& str); extern CClientUIInterface uiInterface; diff --git a/src/util/asmap.cpp b/src/util/asmap.cpp index 5354cdb962..bd77d74218 100644 --- a/src/util/asmap.cpp +++ b/src/util/asmap.cpp @@ -2,12 +2,15 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include <map> #include <vector> #include <assert.h> #include <crypto/common.h> namespace { +constexpr uint32_t INVALID = 0xFFFFFFFF; + uint32_t DecodeBits(std::vector<bool>::const_iterator& bitpos, const std::vector<bool>::const_iterator& endpos, uint8_t minval, const std::vector<uint8_t> &bit_sizes) { uint32_t val = minval; @@ -25,7 +28,7 @@ uint32_t DecodeBits(std::vector<bool>::const_iterator& bitpos, const std::vector val += (1 << *bit_sizes_it); } else { for (int b = 0; b < *bit_sizes_it; b++) { - if (bitpos == endpos) break; + if (bitpos == endpos) return INVALID; // Reached EOF in mantissa bit = *bitpos; bitpos++; val += bit << (*bit_sizes_it - 1 - b); @@ -33,13 +36,21 @@ uint32_t DecodeBits(std::vector<bool>::const_iterator& bitpos, const std::vector return val; } } - return -1; + return INVALID; // Reached EOF in exponent } +enum class Instruction : uint32_t +{ + RETURN = 0, + JUMP = 1, + MATCH = 2, + DEFAULT = 3, +}; + const std::vector<uint8_t> TYPE_BIT_SIZES{0, 0, 1}; -uint32_t DecodeType(std::vector<bool>::const_iterator& bitpos, const std::vector<bool>::const_iterator& endpos) +Instruction DecodeType(std::vector<bool>::const_iterator& bitpos, const std::vector<bool>::const_iterator& endpos) { - return DecodeBits(bitpos, endpos, 0, TYPE_BIT_SIZES); + return Instruction(DecodeBits(bitpos, endpos, 0, TYPE_BIT_SIZES)); } const std::vector<uint8_t> ASN_BIT_SIZES{15, 16, 17, 18, 19, 20, 21, 22, 23, 24}; @@ -70,34 +81,107 @@ uint32_t Interpret(const std::vector<bool> &asmap, const std::vector<bool> &ip) const std::vector<bool>::const_iterator endpos = asmap.end(); uint8_t bits = ip.size(); uint32_t default_asn = 0; - uint32_t opcode, jump, match, matchlen; + uint32_t jump, match, matchlen; + Instruction opcode; while (pos != endpos) { opcode = DecodeType(pos, endpos); - if (opcode == 0) { - return DecodeASN(pos, endpos); - } else if (opcode == 1) { + if (opcode == Instruction::RETURN) { + default_asn = DecodeASN(pos, endpos); + if (default_asn == INVALID) break; // ASN straddles EOF + return default_asn; + } else if (opcode == Instruction::JUMP) { jump = DecodeJump(pos, endpos); - if (bits == 0) break; + if (jump == INVALID) break; // Jump offset straddles EOF + if (bits == 0) break; // No input bits left + if (pos + jump < pos) break; // overflow + if (pos + jump >= endpos) break; // Jumping past EOF if (ip[ip.size() - bits]) { - if (jump >= endpos - pos) break; pos += jump; } bits--; - } else if (opcode == 2) { + } else if (opcode == Instruction::MATCH) { match = DecodeMatch(pos, endpos); + if (match == INVALID) break; // Match bits straddle EOF matchlen = CountBits(match) - 1; + if (bits < matchlen) break; // Not enough input bits for (uint32_t bit = 0; bit < matchlen; bit++) { - if (bits == 0) break; if ((ip[ip.size() - bits]) != ((match >> (matchlen - 1 - bit)) & 1)) { return default_asn; } bits--; } - } else if (opcode == 3) { + } else if (opcode == Instruction::DEFAULT) { default_asn = DecodeASN(pos, endpos); + if (default_asn == INVALID) break; // ASN straddles EOF } else { - break; + break; // Instruction straddles EOF } } + assert(false); // Reached EOF without RETURN, or aborted (see any of the breaks above) - should have been caught by SanityCheckASMap below return 0; // 0 is not a valid ASN } + +bool SanityCheckASMap(const std::vector<bool>& asmap, int bits) +{ + const std::vector<bool>::const_iterator begin = asmap.begin(), endpos = asmap.end(); + std::vector<bool>::const_iterator pos = begin; + std::vector<std::pair<uint32_t, int>> jumps; // All future positions we may jump to (bit offset in asmap -> bits to consume left) + jumps.reserve(bits); + Instruction prevopcode = Instruction::JUMP; + bool had_incomplete_match = false; + while (pos != endpos) { + uint32_t offset = pos - begin; + if (!jumps.empty() && offset >= jumps.back().first) return false; // There was a jump into the middle of the previous instruction + Instruction opcode = DecodeType(pos, endpos); + if (opcode == Instruction::RETURN) { + if (prevopcode == Instruction::DEFAULT) return false; // There should not be any RETURN immediately after a DEFAULT (could be combined into just RETURN) + uint32_t asn = DecodeASN(pos, endpos); + if (asn == INVALID) return false; // ASN straddles EOF + if (jumps.empty()) { + // Nothing to execute anymore + if (endpos - pos > 7) return false; // Excessive padding + while (pos != endpos) { + if (*pos) return false; // Nonzero padding bit + ++pos; + } + return true; // Sanely reached EOF + } else { + // Continue by pretending we jumped to the next instruction + offset = pos - begin; + if (offset != jumps.back().first) return false; // Unreachable code + bits = jumps.back().second; // Restore the number of bits we would have had left after this jump + jumps.pop_back(); + prevopcode = Instruction::JUMP; + } + } else if (opcode == Instruction::JUMP) { + uint32_t jump = DecodeJump(pos, endpos); + if (jump == INVALID) return false; // Jump offset straddles EOF + if (pos + jump < pos) return false; // overflow + if (pos + jump > endpos) return false; // Jump out of range + if (bits == 0) return false; // Consuming bits past the end of the input + --bits; + uint32_t jump_offset = pos - begin + jump; + if (!jumps.empty() && jump_offset >= jumps.back().first) return false; // Intersecting jumps + jumps.emplace_back(jump_offset, bits); + prevopcode = Instruction::JUMP; + } else if (opcode == Instruction::MATCH) { + uint32_t match = DecodeMatch(pos, endpos); + if (match == INVALID) return false; // Match bits straddle EOF + int matchlen = CountBits(match) - 1; + if (prevopcode != Instruction::MATCH) had_incomplete_match = false; + if (matchlen < 8 && had_incomplete_match) return false; // Within a sequence of matches only at most one should be incomplete + had_incomplete_match = (matchlen < 8); + if (bits < matchlen) return false; // Consuming bits past the end of the input + bits -= matchlen; + prevopcode = Instruction::MATCH; + } else if (opcode == Instruction::DEFAULT) { + if (prevopcode == Instruction::DEFAULT) return false; // There should not be two successive DEFAULTs (they could be combined into one) + uint32_t asn = DecodeASN(pos, endpos); + if (asn == INVALID) return false; // ASN straddles EOF + prevopcode = Instruction::DEFAULT; + } else { + return false; // Instruction straddles EOF + } + } + return false; // Reached EOF without RETURN instruction +} diff --git a/src/util/asmap.h b/src/util/asmap.h index a0e14013c5..b31e639bb5 100644 --- a/src/util/asmap.h +++ b/src/util/asmap.h @@ -5,6 +5,11 @@ #ifndef BITCOIN_UTIL_ASMAP_H #define BITCOIN_UTIL_ASMAP_H +#include <stdint.h> +#include <vector> + uint32_t Interpret(const std::vector<bool> &asmap, const std::vector<bool> &ip); +bool SanityCheckASMap(const std::vector<bool>& asmap, int bits); + #endif // BITCOIN_UTIL_ASMAP_H diff --git a/src/util/string.h b/src/util/string.h index b8e2a06235..cdb41630c6 100644 --- a/src/util/string.h +++ b/src/util/string.h @@ -30,10 +30,11 @@ NODISCARD inline std::string TrimString(const std::string& str, const std::strin * @param separator The separator * @param unary_op Apply this operator to each item in the list */ -template <typename T, typename UnaryOp> -std::string Join(const std::vector<T>& list, const std::string& separator, UnaryOp unary_op) +template <typename T, typename BaseType, typename UnaryOp> +auto Join(const std::vector<T>& list, const BaseType& separator, UnaryOp unary_op) + -> decltype(unary_op(list.at(0))) { - std::string ret; + decltype(unary_op(list.at(0))) ret; for (size_t i = 0; i < list.size(); ++i) { if (i > 0) ret += separator; ret += unary_op(list.at(i)); @@ -41,9 +42,16 @@ std::string Join(const std::vector<T>& list, const std::string& separator, Unary return ret; } +template <typename T> +T Join(const std::vector<T>& list, const T& separator) +{ + return Join(list, separator, [](const T& i) { return i; }); +} + +// Explicit overload needed for c_str arguments, which would otherwise cause a substitution failure in the template above. inline std::string Join(const std::vector<std::string>& list, const std::string& separator) { - return Join(list, separator, [](const std::string& i) { return i; }); + return Join<std::string>(list, separator); } /** diff --git a/src/util/translation.h b/src/util/translation.h index b2d964c977..268bcf30a7 100644 --- a/src/util/translation.h +++ b/src/util/translation.h @@ -16,21 +16,23 @@ struct bilingual_str { std::string original; std::string translated; + + bilingual_str& operator+=(const bilingual_str& rhs) + { + original += rhs.original; + translated += rhs.translated; + return *this; + } }; -inline bilingual_str operator+(const bilingual_str& lhs, const bilingual_str& rhs) +inline bilingual_str operator+(bilingual_str lhs, const bilingual_str& rhs) { - return bilingual_str{ - lhs.original + rhs.original, - lhs.translated + rhs.translated}; + lhs += rhs; + return lhs; } /** Mark a bilingual_str as untranslated */ -inline static bilingual_str Untranslated(std::string original) { return {original, original}; } -/** Unary operator to return the original */ -inline static std::string OpOriginal(const bilingual_str& b) { return b.original; } -/** Unary operator to return the translation */ -inline static std::string OpTranslated(const bilingual_str& b) { return b.translated; } +inline bilingual_str Untranslated(std::string original) { return {original, original}; } namespace tinyformat { template <typename... Args> diff --git a/src/validation.cpp b/src/validation.cpp index a924af90af..8a454c8d1b 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -1651,14 +1651,15 @@ bool UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex* pindex) } /** Abort with a message */ +// TODO: AbortNode() should take bilingual_str userMessage parameter. static bool AbortNode(const std::string& strMessage, const std::string& userMessage = "", unsigned int prefix = 0) { SetMiscWarning(strMessage); LogPrintf("*** %s\n", strMessage); if (!userMessage.empty()) { - uiInterface.ThreadSafeMessageBox(userMessage, "", CClientUIInterface::MSG_ERROR | prefix); + uiInterface.ThreadSafeMessageBox(Untranslated(userMessage), "", CClientUIInterface::MSG_ERROR | prefix); } else { - uiInterface.ThreadSafeMessageBox(_("Error: A fatal internal error occurred, see debug.log for details").translated, "", CClientUIInterface::MSG_ERROR | CClientUIInterface::MSG_NOPREFIX); + uiInterface.ThreadSafeMessageBox(_("Error: A fatal internal error occurred, see debug.log for details"), "", CClientUIInterface::MSG_ERROR | CClientUIInterface::MSG_NOPREFIX); } StartShutdown(); return false; diff --git a/src/wallet/db.cpp b/src/wallet/db.cpp index 3f7c2d09cc..1b2bd83a4c 100644 --- a/src/wallet/db.cpp +++ b/src/wallet/db.cpp @@ -399,7 +399,7 @@ bool BerkeleyBatch::VerifyEnvironment(const fs::path& file_path, bilingual_str& std::shared_ptr<BerkeleyEnvironment> env = GetWalletEnv(file_path, walletFile); fs::path walletDir = env->Directory(); - LogPrintf("Using BerkeleyDB version %s\n", DbEnv::version(nullptr, nullptr, nullptr)); + LogPrintf("Using BerkeleyDB version %s\n", BerkeleyDatabaseVersion()); LogPrintf("Using wallet %s\n", file_path.string()); if (!env->Open(true /* retry */)) { @@ -916,3 +916,8 @@ void BerkeleyDatabase::ReloadDbEnv() env->ReloadDbEnv(); } } + +std::string BerkeleyDatabaseVersion() +{ + return DbEnv::version(nullptr, nullptr, nullptr); +} diff --git a/src/wallet/db.h b/src/wallet/db.h index 1bf3375475..37f96a1a96 100644 --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -19,7 +19,14 @@ #include <unordered_map> #include <vector> +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsuggest-override" +#endif #include <db_cxx.h> +#if defined(__GNUC__) && !defined(__clang__) +#pragma GCC diagnostic pop +#endif struct bilingual_str; @@ -402,4 +409,6 @@ public: bool static Rewrite(BerkeleyDatabase& database, const char* pszSkip = nullptr); }; +std::string BerkeleyDatabaseVersion(); + #endif // BITCOIN_WALLET_DB_H diff --git a/src/wallet/init.cpp b/src/wallet/init.cpp index 8688f3df5e..6f973aab1c 100644 --- a/src/wallet/init.cpp +++ b/src/wallet/init.cpp @@ -91,7 +91,7 @@ bool WalletInit::ParameterInteraction() const if (gArgs.GetBoolArg("-salvagewallet", false)) { if (is_multiwallet) { - return InitError(strprintf("%s is only allowed with a single wallet file", "-salvagewallet")); + return InitError(strprintf(Untranslated("%s is only allowed with a single wallet file"), "-salvagewallet")); } // Rewrite just private keys: rescan to find transactions if (gArgs.SoftSetBoolArg("-rescan", true)) { @@ -108,7 +108,7 @@ bool WalletInit::ParameterInteraction() const // -zapwallettxes implies a rescan if (zapwallettxes) { if (is_multiwallet) { - return InitError(strprintf("%s is only allowed with a single wallet file", "-zapwallettxes")); + return InitError(strprintf(Untranslated("%s is only allowed with a single wallet file"), "-zapwallettxes")); } if (gArgs.SoftSetBoolArg("-rescan", true)) { LogPrintf("%s: parameter interaction: -zapwallettxes enabled -> setting -rescan=1\n", __func__); @@ -116,7 +116,7 @@ bool WalletInit::ParameterInteraction() const } if (gArgs.GetBoolArg("-sysperms", false)) - return InitError("-sysperms is not allowed in combination with enabled wallet functionality"); + return InitError(Untranslated("-sysperms is not allowed in combination with enabled wallet functionality")); return true; } diff --git a/src/wallet/load.cpp b/src/wallet/load.cpp index 217a950457..16f3699d37 100644 --- a/src/wallet/load.cpp +++ b/src/wallet/load.cpp @@ -20,14 +20,14 @@ bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wal // The canonical path cleans the path, preventing >1 Berkeley environment instances for the same directory fs::path canonical_wallet_dir = fs::canonical(wallet_dir, error); if (error || !fs::exists(wallet_dir)) { - chain.initError(strprintf(_("Specified -walletdir \"%s\" does not exist").translated, wallet_dir.string())); + chain.initError(strprintf(_("Specified -walletdir \"%s\" does not exist"), wallet_dir.string())); return false; } else if (!fs::is_directory(wallet_dir)) { - chain.initError(strprintf(_("Specified -walletdir \"%s\" is not a directory").translated, wallet_dir.string())); + chain.initError(strprintf(_("Specified -walletdir \"%s\" is not a directory"), wallet_dir.string())); return false; // The canonical path transforms relative paths into absolute ones, so we check the non-canonical version } else if (!wallet_dir.is_absolute()) { - chain.initError(strprintf(_("Specified -walletdir \"%s\" is a relative path").translated, wallet_dir.string())); + chain.initError(strprintf(_("Specified -walletdir \"%s\" is a relative path"), wallet_dir.string())); return false; } gArgs.ForceSetArg("-walletdir", canonical_wallet_dir.string()); @@ -49,16 +49,16 @@ bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wal WalletLocation location(wallet_file); if (!wallet_paths.insert(location.GetPath()).second) { - chain.initError(strprintf(_("Error loading wallet %s. Duplicate -wallet filename specified.").translated, wallet_file)); + chain.initError(strprintf(_("Error loading wallet %s. Duplicate -wallet filename specified."), wallet_file)); return false; } bilingual_str error_string; std::vector<bilingual_str> warnings; bool verify_success = CWallet::Verify(chain, location, salvage_wallet, error_string, warnings); - if (!warnings.empty()) chain.initWarning(Join(warnings, "\n", OpTranslated)); + if (!warnings.empty()) chain.initWarning(Join(warnings, Untranslated("\n"))); if (!verify_success) { - chain.initError(error_string.translated); + chain.initError(error_string); return false; } } @@ -73,16 +73,16 @@ bool LoadWallets(interfaces::Chain& chain, const std::vector<std::string>& walle bilingual_str error; std::vector<bilingual_str> warnings; std::shared_ptr<CWallet> pwallet = CWallet::CreateWalletFromFile(chain, WalletLocation(walletFile), error, warnings); - if (!warnings.empty()) chain.initWarning(Join(warnings, "\n", OpTranslated)); + if (!warnings.empty()) chain.initWarning(Join(warnings, Untranslated("\n"))); if (!pwallet) { - chain.initError(error.translated); + chain.initError(error); return false; } AddWallet(pwallet); } return true; } catch (const std::runtime_error& e) { - chain.initError(e.what()); + chain.initError(Untranslated(e.what())); return false; } } diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index c863d22530..7bf3d169c3 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -345,7 +345,6 @@ UniValue importprunedfunds(const JSONRPCRequest& request) if (!DecodeHexTx(tx, request.params[0].get_str())) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); uint256 hashTx = tx.GetHash(); - CWalletTx wtx(pwallet, MakeTransactionRef(std::move(tx))); CDataStream ssMB(ParseHexV(request.params[1], "proof"), SER_NETWORK, PROTOCOL_VERSION); CMerkleBlock merkleBlock; @@ -372,10 +371,10 @@ UniValue importprunedfunds(const JSONRPCRequest& request) unsigned int txnIndex = vIndex[it - vMatch.begin()]; CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, height, merkleBlock.header.GetHash(), txnIndex); - wtx.m_confirm = confirm; - if (pwallet->IsMine(*wtx.tx)) { - pwallet->AddToWallet(wtx, false); + CTransactionRef tx_ref = MakeTransactionRef(tx); + if (pwallet->IsMine(*tx_ref)) { + pwallet->AddToWallet(std::move(tx_ref), confirm); return NullUniValue; } diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index e666d55e11..250a4503f5 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -1583,7 +1583,7 @@ static UniValue listsinceblock(const JSONRPCRequest& request) UniValue transactions(UniValue::VARR); for (const std::pair<const uint256, CWalletTx>& pairWtx : pwallet->mapWallet) { - CWalletTx tx = pairWtx.second; + const CWalletTx& tx = pairWtx.second; if (depth == -1 || abs(tx.GetDepthInMainChain()) < depth) { ListTransactions(pwallet, tx, 0, true, transactions, filter, nullptr /* filter_label */); @@ -1892,6 +1892,9 @@ static UniValue walletpassphrase(const JSONRPCRequest& request) }.Check(request); int64_t nSleepTime; + int64_t relock_time; + // Prevent concurrent calls to walletpassphrase with the same wallet. + LOCK(pwallet->m_unlock_mutex); { LOCK(pwallet->cs_wallet); @@ -1929,6 +1932,7 @@ static UniValue walletpassphrase(const JSONRPCRequest& request) pwallet->TopUpKeyPool(); pwallet->nRelockTime = GetTime() + nSleepTime; + relock_time = pwallet->nRelockTime; } // rpcRunLater must be called without cs_wallet held otherwise a deadlock @@ -1940,9 +1944,11 @@ static UniValue walletpassphrase(const JSONRPCRequest& request) // wallet before the following callback is called. If a valid shared pointer // is acquired in the callback then the wallet is still loaded. std::weak_ptr<CWallet> weak_wallet = wallet; - pwallet->chain().rpcRunLater(strprintf("lockwallet(%s)", pwallet->GetName()), [weak_wallet] { + pwallet->chain().rpcRunLater(strprintf("lockwallet(%s)", pwallet->GetName()), [weak_wallet, relock_time] { if (auto shared_wallet = weak_wallet.lock()) { LOCK(shared_wallet->cs_wallet); + // Skip if this is not the most recent rpcRunLater callback. + if (shared_wallet->nRelockTime != relock_time) return; shared_wallet->Lock(); shared_wallet->nRelockTime = 0; } @@ -2597,7 +2603,7 @@ static UniValue loadwallet(const JSONRPCRequest& request) UniValue obj(UniValue::VOBJ); obj.pushKV("name", wallet->GetName()); - obj.pushKV("warning", Join(warnings, "\n", OpOriginal)); + obj.pushKV("warning", Join(warnings, Untranslated("\n")).original); return obj; } @@ -2737,7 +2743,7 @@ static UniValue createwallet(const JSONRPCRequest& request) UniValue obj(UniValue::VOBJ); obj.pushKV("name", wallet->GetName()); - obj.pushKV("warning", Join(warnings, "\n", OpOriginal)); + obj.pushKV("warning", Join(warnings, Untranslated("\n")).original); return obj; } diff --git a/src/wallet/test/coinselector_tests.cpp b/src/wallet/test/coinselector_tests.cpp index 66f4542cf9..657d0828f2 100644 --- a/src/wallet/test/coinselector_tests.cpp +++ b/src/wallet/test/coinselector_tests.cpp @@ -24,8 +24,6 @@ BOOST_FIXTURE_TEST_SUITE(coinselector_tests, WalletTestingSetup) // we repeat those tests this many times and only complain if all iterations of the test fail #define RANDOM_REPEATS 5 -std::vector<std::unique_ptr<CWalletTx>> wtxn; - typedef std::set<CInputCoin> CoinSet; static std::vector<COutput> vCoins; @@ -74,16 +72,14 @@ static void add_coin(CWallet& wallet, const CAmount& nValue, int nAge = 6*24, bo // so stop vin being empty, and cache a non-zero Debit to fake out IsFromMe() tx.vin.resize(1); } - std::unique_ptr<CWalletTx> wtx = MakeUnique<CWalletTx>(&wallet, MakeTransactionRef(std::move(tx))); + CWalletTx* wtx = wallet.AddToWallet(MakeTransactionRef(std::move(tx)), /* confirm= */ {}); if (fIsFromMe) { wtx->m_amounts[CWalletTx::DEBIT].Set(ISMINE_SPENDABLE, 1); wtx->m_is_cache_empty = false; } - COutput output(wtx.get(), nInput, nAge, true /* spendable */, true /* solvable */, true /* safe */); + COutput output(wtx, nInput, nAge, true /* spendable */, true /* solvable */, true /* safe */); vCoins.push_back(output); - wallet.AddToWallet(*wtx.get()); - wtxn.emplace_back(std::move(wtx)); } static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = false, int nInput=0, bool spendable = false) { @@ -93,7 +89,6 @@ static void add_coin(const CAmount& nValue, int nAge = 6*24, bool fIsFromMe = fa static void empty_wallet(void) { vCoins.clear(); - wtxn.clear(); balance = 0; } diff --git a/src/wallet/test/psbt_wallet_tests.cpp b/src/wallet/test/psbt_wallet_tests.cpp index 35860577cd..b4c65a8665 100644 --- a/src/wallet/test/psbt_wallet_tests.cpp +++ b/src/wallet/test/psbt_wallet_tests.cpp @@ -22,14 +22,12 @@ BOOST_AUTO_TEST_CASE(psbt_updater_test) CDataStream s_prev_tx1(ParseHex("0200000000010158e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd7501000000171600145f275f436b09a8cc9a2eb2a2f528485c68a56323feffffff02d8231f1b0100000017a914aed962d6654f9a2b36608eb9d64d2b260db4f1118700c2eb0b0000000017a914b7f5faf40e3d40a5a459b1db3535f2b72fa921e88702483045022100a22edcc6e5bc511af4cc4ae0de0fcd75c7e04d8c1c3a8aa9d820ed4b967384ec02200642963597b9b1bc22c75e9f3e117284a962188bf5e8a74c895089046a20ad770121035509a48eb623e10aace8bfd0212fdb8a8e5af3c94b0b133b95e114cab89e4f7965000000"), SER_NETWORK, PROTOCOL_VERSION); CTransactionRef prev_tx1; s_prev_tx1 >> prev_tx1; - CWalletTx prev_wtx1(&m_wallet, prev_tx1); - m_wallet.mapWallet.emplace(prev_wtx1.GetHash(), std::move(prev_wtx1)); + m_wallet.mapWallet.emplace(std::piecewise_construct, std::forward_as_tuple(prev_tx1->GetHash()), std::forward_as_tuple(&m_wallet, prev_tx1)); CDataStream s_prev_tx2(ParseHex("0200000001aad73931018bd25f84ae400b68848be09db706eac2ac18298babee71ab656f8b0000000048473044022058f6fc7c6a33e1b31548d481c826c015bd30135aad42cd67790dab66d2ad243b02204a1ced2604c6735b6393e5b41691dd78b00f0c5942fb9f751856faa938157dba01feffffff0280f0fa020000000017a9140fb9463421696b82c833af241c78c17ddbde493487d0f20a270100000017a91429ca74f8a08f81999428185c97b5d852e4063f618765000000"), SER_NETWORK, PROTOCOL_VERSION); CTransactionRef prev_tx2; s_prev_tx2 >> prev_tx2; - CWalletTx prev_wtx2(&m_wallet, prev_tx2); - m_wallet.mapWallet.emplace(prev_wtx2.GetHash(), std::move(prev_wtx2)); + m_wallet.mapWallet.emplace(std::piecewise_construct, std::forward_as_tuple(prev_tx2->GetHash()), std::forward_as_tuple(&m_wallet, prev_tx2)); // Add scripts CScript rs1; diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index 0826b88f0a..d888b8f842 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -332,6 +332,7 @@ BOOST_FIXTURE_TEST_CASE(coin_mark_dirty_immature_credit, TestChain100Setup) static int64_t AddTx(CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64_t blockTime) { CMutableTransaction tx; + CWalletTx::Confirmation confirm; tx.nLockTime = lockTime; SetMockTime(mockTime); CBlockIndex* block = nullptr; @@ -342,23 +343,15 @@ static int64_t AddTx(CWallet& wallet, uint32_t lockTime, int64_t mockTime, int64 block = inserted.first->second; block->nTime = blockTime; block->phashBlock = &hash; + confirm = {CWalletTx::Status::CONFIRMED, block->nHeight, hash, 0}; } - CWalletTx wtx(&wallet, MakeTransactionRef(tx)); - LOCK(wallet.cs_wallet); // If transaction is already in map, to avoid inconsistencies, unconfirmation // is needed before confirm again with different block. - std::map<uint256, CWalletTx>::iterator it = wallet.mapWallet.find(wtx.GetHash()); - if (it != wallet.mapWallet.end()) { + return wallet.AddToWallet(MakeTransactionRef(tx), confirm, [&](CWalletTx& wtx, bool /* new_tx */) { wtx.setUnconfirmed(); - wallet.AddToWallet(wtx); - } - if (block) { - CWalletTx::Confirmation confirm(CWalletTx::Status::CONFIRMED, block->nHeight, block->GetBlockHash(), 0); - wtx.m_confirm = confirm; - } - wallet.AddToWallet(wtx); - return wallet.mapWallet.at(wtx.GetHash()).nTimeSmart; + return true; + })->nTimeSmart; } // Simple test to verify assignment of CWalletTx::nSmartTime value. Could be @@ -749,8 +742,8 @@ BOOST_FIXTURE_TEST_CASE(CreateWalletFromFile, TestChain100Setup) BOOST_CHECK_EQUAL(addtx_count, 2); { LOCK(wallet->cs_wallet); - BOOST_CHECK_EQUAL(wallet->mapWallet.count(block_tx.GetHash()), 1); - BOOST_CHECK_EQUAL(wallet->mapWallet.count(mempool_tx.GetHash()), 1); + BOOST_CHECK_EQUAL(wallet->mapWallet.count(block_tx.GetHash()), 1U); + BOOST_CHECK_EQUAL(wallet->mapWallet.count(mempool_tx.GetHash()), 1U); } @@ -786,8 +779,8 @@ BOOST_FIXTURE_TEST_CASE(CreateWalletFromFile, TestChain100Setup) BOOST_CHECK_EQUAL(addtx_count, 4); { LOCK(wallet->cs_wallet); - BOOST_CHECK_EQUAL(wallet->mapWallet.count(block_tx.GetHash()), 1); - BOOST_CHECK_EQUAL(wallet->mapWallet.count(mempool_tx.GetHash()), 1); + BOOST_CHECK_EQUAL(wallet->mapWallet.count(block_tx.GetHash()), 1U); + BOOST_CHECK_EQUAL(wallet->mapWallet.count(mempool_tx.GetHash()), 1U); } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index a893548971..2b45c6a536 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -784,19 +784,19 @@ bool CWallet::IsSpentKey(const uint256& hash, unsigned int n) const return false; } -bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose) +CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const CWalletTx::Confirmation& confirm, const UpdateWalletTxFn& update_wtx, bool fFlushOnClose) { LOCK(cs_wallet); WalletBatch batch(*database, "r+", fFlushOnClose); - uint256 hash = wtxIn.GetHash(); + uint256 hash = tx->GetHash(); if (IsWalletFlagSet(WALLET_FLAG_AVOID_REUSE)) { // Mark used destinations std::set<CTxDestination> tx_destinations; - for (const CTxIn& txin : wtxIn.tx->vin) { + for (const CTxIn& txin : tx->vin) { const COutPoint& op = txin.prevout; SetSpentKeyState(batch, op.hash, op.n, true, tx_destinations); } @@ -805,11 +805,12 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose) } // Inserts only if not already there, returns tx inserted or tx found - std::pair<std::map<uint256, CWalletTx>::iterator, bool> ret = mapWallet.insert(std::make_pair(hash, wtxIn)); + auto ret = mapWallet.emplace(std::piecewise_construct, std::forward_as_tuple(hash), std::forward_as_tuple(this, tx)); CWalletTx& wtx = (*ret.first).second; - wtx.BindWallet(this); bool fInsertedNew = ret.second; + bool fUpdated = update_wtx && update_wtx(wtx, fInsertedNew); if (fInsertedNew) { + wtx.m_confirm = confirm; wtx.nTimeReceived = chain().getAdjustedTime(); wtx.nOrderPos = IncOrderPosNext(&batch); wtx.m_it_wtxOrdered = wtxOrdered.insert(std::make_pair(wtx.nOrderPos, &wtx)); @@ -817,43 +818,37 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose) AddToSpends(hash); } - bool fUpdated = false; if (!fInsertedNew) { - if (wtxIn.m_confirm.status != wtx.m_confirm.status) { - wtx.m_confirm.status = wtxIn.m_confirm.status; - wtx.m_confirm.nIndex = wtxIn.m_confirm.nIndex; - wtx.m_confirm.hashBlock = wtxIn.m_confirm.hashBlock; - wtx.m_confirm.block_height = wtxIn.m_confirm.block_height; + if (confirm.status != wtx.m_confirm.status) { + wtx.m_confirm.status = confirm.status; + wtx.m_confirm.nIndex = confirm.nIndex; + wtx.m_confirm.hashBlock = confirm.hashBlock; + wtx.m_confirm.block_height = confirm.block_height; fUpdated = true; } else { - assert(wtx.m_confirm.nIndex == wtxIn.m_confirm.nIndex); - assert(wtx.m_confirm.hashBlock == wtxIn.m_confirm.hashBlock); - assert(wtx.m_confirm.block_height == wtxIn.m_confirm.block_height); - } - if (wtxIn.fFromMe && wtxIn.fFromMe != wtx.fFromMe) - { - wtx.fFromMe = wtxIn.fFromMe; - fUpdated = true; + assert(wtx.m_confirm.nIndex == confirm.nIndex); + assert(wtx.m_confirm.hashBlock == confirm.hashBlock); + assert(wtx.m_confirm.block_height == confirm.block_height); } // If we have a witness-stripped version of this transaction, and we // see a new version with a witness, then we must be upgrading a pre-segwit // wallet. Store the new version of the transaction with the witness, // as the stripped-version must be invalid. // TODO: Store all versions of the transaction, instead of just one. - if (wtxIn.tx->HasWitness() && !wtx.tx->HasWitness()) { - wtx.SetTx(wtxIn.tx); + if (tx->HasWitness() && !wtx.tx->HasWitness()) { + wtx.SetTx(tx); fUpdated = true; } } //// debug print - WalletLogPrintf("AddToWallet %s %s%s\n", wtxIn.GetHash().ToString(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : "")); + WalletLogPrintf("AddToWallet %s %s%s\n", hash.ToString(), (fInsertedNew ? "new" : ""), (fUpdated ? "update" : "")); // Write to disk if (fInsertedNew || fUpdated) if (!batch.WriteTx(wtx)) - return false; + return nullptr; // Break debit/credit balance caches: wtx.MarkDirty(); @@ -867,7 +862,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose) if (!strCmd.empty()) { - boost::replace_all(strCmd, "%s", wtxIn.GetHash().GetHex()); + boost::replace_all(strCmd, "%s", hash.GetHex()); #ifndef WIN32 // Substituting the wallet name isn't currently supported on windows // because windows shell escaping has not been implemented yet: @@ -881,35 +876,36 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose) } #endif - return true; + return &wtx; } -void CWallet::LoadToWallet(CWalletTx& wtxIn) +bool CWallet::LoadToWallet(const uint256& hash, const UpdateWalletTxFn& fill_wtx) { + const auto& ins = mapWallet.emplace(std::piecewise_construct, std::forward_as_tuple(hash), std::forward_as_tuple(this, nullptr)); + CWalletTx& wtx = ins.first->second; + if (!fill_wtx(wtx, ins.second)) { + return false; + } // If wallet doesn't have a chain (e.g wallet-tool), don't bother to update txn. if (HaveChain()) { - Optional<int> block_height = chain().getBlockHeight(wtxIn.m_confirm.hashBlock); + Optional<int> block_height = chain().getBlockHeight(wtx.m_confirm.hashBlock); if (block_height) { // Update cached block height variable since it not stored in the // serialized transaction. - wtxIn.m_confirm.block_height = *block_height; - } else if (wtxIn.isConflicted() || wtxIn.isConfirmed()) { + wtx.m_confirm.block_height = *block_height; + } else if (wtx.isConflicted() || wtx.isConfirmed()) { // If tx block (or conflicting block) was reorged out of chain // while the wallet was shutdown, change tx status to UNCONFIRMED // and reset block height, hash, and index. ABANDONED tx don't have // associated blocks and don't need to be updated. The case where a // transaction was reorged out while online and then reconfirmed // while offline is covered by the rescan logic. - wtxIn.setUnconfirmed(); - wtxIn.m_confirm.hashBlock = uint256(); - wtxIn.m_confirm.block_height = 0; - wtxIn.m_confirm.nIndex = 0; + wtx.setUnconfirmed(); + wtx.m_confirm.hashBlock = uint256(); + wtx.m_confirm.block_height = 0; + wtx.m_confirm.nIndex = 0; } } - uint256 hash = wtxIn.GetHash(); - const auto& ins = mapWallet.emplace(hash, wtxIn); - CWalletTx& wtx = ins.first->second; - wtx.BindWallet(this); if (/* insertion took place */ ins.second) { wtx.m_it_wtxOrdered = wtxOrdered.insert(std::make_pair(wtx.nOrderPos, &wtx)); } @@ -923,6 +919,7 @@ void CWallet::LoadToWallet(CWalletTx& wtxIn) } } } + return true; } bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, CWalletTx::Confirmation confirm, bool fUpdate) @@ -961,13 +958,9 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransactionRef& ptx, CWalletTx::Co } } - CWalletTx wtx(this, ptx); - // Block disconnection override an abandoned tx as unconfirmed // which means user may have to call abandontransaction again - wtx.m_confirm = confirm; - - return AddToWallet(wtx, false); + return AddToWallet(MakeTransactionRef(tx), confirm, /* update_wtx= */ nullptr, /* fFlushOnClose= */ false); } } return false; @@ -2720,7 +2713,10 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransac error = _("Transaction needs a change address, but we can't generate it. Please call keypoolrefill first."); } scriptChange = GetScriptForDestination(dest); - assert(!dest.empty() || scriptChange.empty()); + // A valid destination implies a change script (and + // vice-versa). An empty change script will abort later, if the + // change keypool ran out, but change is required. + CHECK_NONFATAL(IsValidDestination(dest) != scriptChange.empty()); } CTxOut change_prototype_txout(0, scriptChange); coin_selection_params.change_output_size = GetSerializeSize(change_prototype_txout); @@ -2937,7 +2933,7 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransac continue; } - // Give up if change keypool ran out and we failed to find a solution without change: + // Give up if change keypool ran out and change is required if (scriptChange.empty() && nChangePosInOut != -1) { return false; } @@ -3008,29 +3004,30 @@ bool CWallet::CreateTransaction(const std::vector<CRecipient>& vecSend, CTransac void CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm) { LOCK(cs_wallet); - - CWalletTx wtxNew(this, std::move(tx)); - wtxNew.mapValue = std::move(mapValue); - wtxNew.vOrderForm = std::move(orderForm); - wtxNew.fTimeReceivedIsTxTime = true; - wtxNew.fFromMe = true; - - WalletLogPrintf("CommitTransaction:\n%s", wtxNew.tx->ToString()); /* Continued */ + WalletLogPrintf("CommitTransaction:\n%s", tx->ToString()); /* Continued */ // Add tx to wallet, because if it has change it's also ours, // otherwise just for transaction history. - AddToWallet(wtxNew); + AddToWallet(tx, {}, [&](CWalletTx& wtx, bool new_tx) { + CHECK_NONFATAL(wtx.mapValue.empty()); + CHECK_NONFATAL(wtx.vOrderForm.empty()); + wtx.mapValue = std::move(mapValue); + wtx.vOrderForm = std::move(orderForm); + wtx.fTimeReceivedIsTxTime = true; + wtx.fFromMe = true; + return true; + }); // Notify that old coins are spent - for (const CTxIn& txin : wtxNew.tx->vin) { + for (const CTxIn& txin : tx->vin) { CWalletTx &coin = mapWallet.at(txin.prevout.hash); - coin.BindWallet(this); + coin.MarkDirty(); NotifyTransactionChanged(this, coin.GetHash(), CT_UPDATED); } // Get the inserted-CWalletTx from mapWallet so that the // fInMempool flag is cached properly - CWalletTx& wtx = mapWallet.at(wtxNew.GetHash()); + CWalletTx& wtx = mapWallet.at(tx->GetHash()); if (!fBroadcastTransactions) { // Don't submit tx to the mempool @@ -3102,7 +3099,7 @@ DBErrors CWallet::ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256 return DBErrors::LOAD_OK; } -DBErrors CWallet::ZapWalletTx(std::vector<CWalletTx>& vWtx) +DBErrors CWallet::ZapWalletTx(std::list<CWalletTx>& vWtx) { DBErrors nZapWalletTxRet = WalletBatch(*database,"cr+").ZapWalletTx(vWtx); if (nZapWalletTxRet == DBErrors::NEED_REWRITE) @@ -3714,7 +3711,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain, const std::string walletFile = WalletDataFilePath(location.GetPath()).string(); // needed to restore wallet transaction meta data after -zapwallettxes - std::vector<CWalletTx> vWtx; + std::list<CWalletTx> vWtx; if (gArgs.GetBoolArg("-zapwallettxes", false)) { chain.initMessage(_("Zapping all transactions from wallet...").translated); diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 3be3435596..a29fa22207 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -340,15 +340,15 @@ public: mutable bool fInMempool; mutable CAmount nChangeCached; - CWalletTx(const CWallet* pwalletIn, CTransactionRef arg) - : tx(std::move(arg)) + CWalletTx(const CWallet* wallet, CTransactionRef arg) + : pwallet(wallet), + tx(std::move(arg)) { - Init(pwalletIn); + Init(); } - void Init(const CWallet* pwalletIn) + void Init() { - pwallet = pwalletIn; mapValue.clear(); vOrderForm.clear(); fTimeReceivedIsTxTime = false; @@ -416,7 +416,7 @@ public: template<typename Stream> void Unserialize(Stream& s) { - Init(nullptr); + Init(); std::vector<uint256> dummy_vector1; //!< Used to be vMerkleBranch std::vector<CMerkleTx> dummy_vector2; //!< Used to be vtxPrev @@ -465,12 +465,6 @@ public: m_is_cache_empty = true; } - void BindWallet(CWallet *pwalletIn) - { - pwallet = pwalletIn; - MarkDirty(); - } - //! filter decides which addresses will count towards the debit CAmount GetDebit(const isminefilter& filter) const; CAmount GetCredit(const isminefilter& filter) const; @@ -555,6 +549,12 @@ public: const uint256& GetHash() const { return tx->GetHash(); } bool IsCoinBase() const { return tx->IsCoinBase(); } bool IsImmatureCoinBase() const; + + // Disable copying of CWalletTx objects to prevent bugs where instances get + // copied in and out of the mapWallet map, and fields are updated in the + // wrong copy. + CWalletTx(CWalletTx const &) = delete; + void operator=(CWalletTx const &x) = delete; }; class COutput @@ -871,8 +871,10 @@ public: std::vector<std::string> GetDestValues(const std::string& prefix) const EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); //! Holds a timestamp at which point the wallet is scheduled (externally) to be relocked. Caller must arrange for actual relocking to occur via Lock(). - int64_t nRelockTime = 0; + int64_t nRelockTime GUARDED_BY(cs_wallet){0}; + // Used to prevent concurrent calls to walletpassphrase RPC. + Mutex m_unlock_mutex; bool Unlock(const SecureString& strWalletPassphrase, bool accept_no_keys = false); bool ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, const SecureString& strNewWalletPassphrase); bool EncryptWallet(const SecureString& strWalletPassphrase); @@ -888,8 +890,17 @@ public: DBErrors ReorderTransactions(); void MarkDirty(); - bool AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose=true); - void LoadToWallet(CWalletTx& wtxIn) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); + + //! Callback for updating transaction metadata in mapWallet. + //! + //! @param wtx - reference to mapWallet transaction to update + //! @param new_tx - true if wtx is newly inserted, false if it previously existed + //! + //! @return true if wtx is changed and needs to be saved to disk, otherwise false + using UpdateWalletTxFn = std::function<bool(CWalletTx& wtx, bool new_tx)>; + + CWalletTx* AddToWallet(CTransactionRef tx, const CWalletTx::Confirmation& confirm, const UpdateWalletTxFn& update_wtx=nullptr, bool fFlushOnClose=true); + bool LoadToWallet(const uint256& hash, const UpdateWalletTxFn& fill_wtx) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); void transactionAddedToMempool(const CTransactionRef& tx) override; void blockConnected(const CBlock& block, int height) override; void blockDisconnected(const CBlock& block, int height) override; @@ -1050,7 +1061,7 @@ public: void chainStateFlushed(const CBlockLocator& loc) override; DBErrors LoadWallet(bool& fFirstRunRet); - DBErrors ZapWalletTx(std::vector<CWalletTx>& vWtx); + DBErrors ZapWalletTx(std::list<CWalletTx>& vWtx); DBErrors ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256>& vHashOut) EXCLUSIVE_LOCKS_REQUIRED(cs_wallet); bool SetAddressBook(const CTxDestination& address, const std::string& strName, const std::string& purpose); diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 45033f9bac..98597bdb0f 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -272,36 +272,43 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, } else if (strType == DBKeys::TX) { uint256 hash; ssKey >> hash; - CWalletTx wtx(nullptr /* pwallet */, MakeTransactionRef()); - ssValue >> wtx; - if (wtx.GetHash() != hash) - return false; + // LoadToWallet call below creates a new CWalletTx that fill_wtx + // callback fills with transaction metadata. + auto fill_wtx = [&](CWalletTx& wtx, bool new_tx) { + assert(new_tx); + ssValue >> wtx; + if (wtx.GetHash() != hash) + return false; - // Undo serialize changes in 31600 - if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703) - { - if (!ssValue.empty()) - { - char fTmp; - char fUnused; - std::string unused_string; - ssValue >> fTmp >> fUnused >> unused_string; - strErr = strprintf("LoadWallet() upgrading tx ver=%d %d %s", - wtx.fTimeReceivedIsTxTime, fTmp, hash.ToString()); - wtx.fTimeReceivedIsTxTime = fTmp; - } - else + // Undo serialize changes in 31600 + if (31404 <= wtx.fTimeReceivedIsTxTime && wtx.fTimeReceivedIsTxTime <= 31703) { - strErr = strprintf("LoadWallet() repairing tx ver=%d %s", wtx.fTimeReceivedIsTxTime, hash.ToString()); - wtx.fTimeReceivedIsTxTime = 0; + if (!ssValue.empty()) + { + char fTmp; + char fUnused; + std::string unused_string; + ssValue >> fTmp >> fUnused >> unused_string; + strErr = strprintf("LoadWallet() upgrading tx ver=%d %d %s", + wtx.fTimeReceivedIsTxTime, fTmp, hash.ToString()); + wtx.fTimeReceivedIsTxTime = fTmp; + } + else + { + strErr = strprintf("LoadWallet() repairing tx ver=%d %s", wtx.fTimeReceivedIsTxTime, hash.ToString()); + wtx.fTimeReceivedIsTxTime = 0; + } + wss.vWalletUpgrade.push_back(hash); } - wss.vWalletUpgrade.push_back(hash); - } - if (wtx.nOrderPos == -1) - wss.fAnyUnordered = true; + if (wtx.nOrderPos == -1) + wss.fAnyUnordered = true; - pwallet->LoadToWallet(wtx); + return true; + }; + if (!pwallet->LoadToWallet(hash, fill_wtx)) { + return false; + } } else if (strType == DBKeys::WATCHS) { wss.nWatchKeys++; CScript script; @@ -731,7 +738,7 @@ DBErrors WalletBatch::LoadWallet(CWallet* pwallet) return result; } -DBErrors WalletBatch::FindWalletTx(std::vector<uint256>& vTxHash, std::vector<CWalletTx>& vWtx) +DBErrors WalletBatch::FindWalletTx(std::vector<uint256>& vTxHash, std::list<CWalletTx>& vWtx) { DBErrors result = DBErrors::LOAD_OK; @@ -769,12 +776,9 @@ DBErrors WalletBatch::FindWalletTx(std::vector<uint256>& vTxHash, std::vector<CW if (strType == DBKeys::TX) { uint256 hash; ssKey >> hash; - - CWalletTx wtx(nullptr /* pwallet */, MakeTransactionRef()); - ssValue >> wtx; - vTxHash.push_back(hash); - vWtx.push_back(wtx); + vWtx.emplace_back(nullptr /* wallet */, nullptr /* tx */); + ssValue >> vWtx.back(); } } pcursor->close(); @@ -793,7 +797,7 @@ DBErrors WalletBatch::ZapSelectTx(std::vector<uint256>& vTxHashIn, std::vector<u { // build list of wallet TXs and hashes std::vector<uint256> vTxHash; - std::vector<CWalletTx> vWtx; + std::list<CWalletTx> vWtx; DBErrors err = FindWalletTx(vTxHash, vWtx); if (err != DBErrors::LOAD_OK) { return err; @@ -827,7 +831,7 @@ DBErrors WalletBatch::ZapSelectTx(std::vector<uint256>& vTxHashIn, std::vector<u return DBErrors::LOAD_OK; } -DBErrors WalletBatch::ZapWalletTx(std::vector<CWalletTx>& vWtx) +DBErrors WalletBatch::ZapWalletTx(std::list<CWalletTx>& vWtx) { // build list of wallet TXs std::vector<uint256> vTxHash; diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index ae72a5b265..e2bf229c68 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -260,8 +260,8 @@ public: bool WriteActiveScriptPubKeyMan(uint8_t type, const uint256& id, bool internal); DBErrors LoadWallet(CWallet* pwallet); - DBErrors FindWalletTx(std::vector<uint256>& vTxHash, std::vector<CWalletTx>& vWtx); - DBErrors ZapWalletTx(std::vector<CWalletTx>& vWtx); + DBErrors FindWalletTx(std::vector<uint256>& vTxHash, std::list<CWalletTx>& vWtx); + DBErrors ZapWalletTx(std::list<CWalletTx>& vWtx); DBErrors ZapSelectTx(std::vector<uint256>& vHashIn, std::vector<uint256>& vHashOut); /* Try to (very carefully!) recover wallet database (with a possible key type filter) */ static bool Recover(const fs::path& wallet_path, void *callbackDataIn, bool (*recoverKVcallback)(void* callbackData, CDataStream ssKey, CDataStream ssValue), std::string& out_backup_filename); |