diff --git a/.github/workflows/guix-build.yml b/.github/workflows/guix-build.yml index cc37826496..49d18bc292 100644 --- a/.github/workflows/guix-build.yml +++ b/.github/workflows/guix-build.yml @@ -110,7 +110,6 @@ jobs: run: | docker run --privileged -d --rm -t \ --name guix-daemon \ - -e ADDITIONAL_GUIX_COMMON_FLAGS="--max-jobs=$(nproc --all)" \ -v ${{ github.workspace }}/dash:/src/dash \ -v ${{ github.workspace }}/.cache:/home/ubuntu/.cache \ -w /src/dash \ diff --git a/configure.ac b/configure.ac index 10a5a83026..4081edea60 100644 --- a/configure.ac +++ b/configure.ac @@ -1081,23 +1081,6 @@ AC_CHECK_DECLS([bswap_16, bswap_32, bswap_64],,, #include #endif]) -AC_MSG_CHECKING(for __builtin_clzl) - -AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ ]], [[ - (void) __builtin_clzl(0); - ]])], - [ AC_MSG_RESULT(yes); have_clzl=yes; AC_DEFINE(HAVE_BUILTIN_CLZL, 1, [Define this symbol if you have __builtin_clzl])], - [ AC_MSG_RESULT(no); have_clzl=no;] -) - -AC_MSG_CHECKING(for __builtin_clzll) -AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ ]], [[ - (void) __builtin_clzll(0); - ]])], - [ AC_MSG_RESULT(yes); have_clzll=yes; AC_DEFINE(HAVE_BUILTIN_CLZLL, 1, [Define this symbol if you have __builtin_clzll])], - [ AC_MSG_RESULT(no); have_clzll=no;] -) - dnl Check for mallopt(M_ARENA_MAX) (to set glibc arenas) AC_MSG_CHECKING(for mallopt M_ARENA_MAX) AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], @@ -1870,7 +1853,6 @@ AM_CONDITIONAL([USE_UPNP],[test x$use_upnp = xyes]) dnl for minisketch AM_CONDITIONAL([ENABLE_CLMUL],[test x$enable_clmul = xyes]) -AM_CONDITIONAL([HAVE_CLZ],[test x$have_clzl$have_clzll = xyesyes]) AC_DEFINE(CLIENT_VERSION_MAJOR, _CLIENT_VERSION_MAJOR, [Major version]) AC_DEFINE(CLIENT_VERSION_MINOR, _CLIENT_VERSION_MINOR, [Minor version]) diff --git a/contrib/builder-keys/README.md b/contrib/builder-keys/README.md index 8b4303d00a..71a7d4ee97 100644 --- a/contrib/builder-keys/README.md +++ b/contrib/builder-keys/README.md @@ -18,9 +18,15 @@ gpg --refresh-keys To fetch keys of builders and active developers, feed the list of fingerprints of the primary keys into gpg: +On \*NIX: ```sh while read fingerprint keyholder_name; do gpg --keyserver hkps://keys.openpgp.org --recv-keys ${fingerprint}; done < ./keys.txt ``` +On Windows (requires Gpg4win >= 4.0.0): +``` +FOR /F "tokens=1" %i IN (keys.txt) DO gpg --keyserver hkps://keys.openpgp.org --recv-keys %i +``` + Add your key to the list if you provided Guix attestations for two major or minor releases of Dash Core. diff --git a/depends/packages/libmultiprocess.mk b/depends/packages/libmultiprocess.mk index 765d649377..c292c49bfb 100644 --- a/depends/packages/libmultiprocess.mk +++ b/depends/packages/libmultiprocess.mk @@ -20,7 +20,7 @@ define $(package)_config_cmds endef define $(package)_build_cmds - $(MAKE) + $(MAKE) multiprocess endef define $(package)_stage_cmds diff --git a/depends/packages/qt.mk b/depends/packages/qt.mk index 92b03c46b1..7a60de2ee6 100644 --- a/depends/packages/qt.mk +++ b/depends/packages/qt.mk @@ -180,6 +180,7 @@ $(package)_config_opts_mingw32 += -xplatform win32-g++ $(package)_config_opts_mingw32 += "QMAKE_CFLAGS = '$($(package)_cflags) $($(package)_cppflags)'" $(package)_config_opts_mingw32 += "QMAKE_CXX = '$($(package)_cxx)'" $(package)_config_opts_mingw32 += "QMAKE_CXXFLAGS = '$($(package)_cxxflags) $($(package)_cppflags)'" +$(package)_config_opts_mingw32 += "QMAKE_LINK = '$($(package)_cxx)'" $(package)_config_opts_mingw32 += "QMAKE_LFLAGS = '$($(package)_ldflags)'" $(package)_config_opts_mingw32 += "QMAKE_LIB = '$($(package)_ar) rc'" $(package)_config_opts_mingw32 += -device-option CROSS_COMPILE="$(host)-" diff --git a/doc/JSON-RPC-interface.md b/doc/JSON-RPC-interface.md index 118251cb90..30cd655380 100644 --- a/doc/JSON-RPC-interface.md +++ b/doc/JSON-RPC-interface.md @@ -24,7 +24,7 @@ This endpoint is only activated when the wallet component has been compiled in. It can service both wallet and non-wallet requests. It MUST be used for wallet requests when two or more wallets are loaded. -This is the endpoint used by bitcoin-cli when a `-rpcwallet=` parameter is passed in. +This is the endpoint used by dash-cli when a `-rpcwallet=` parameter is passed in. Best practice would dictate using the `/wallet//` endpoint for ALL requests when multiple wallets are in use. diff --git a/src/Makefile.minisketch.include b/src/Makefile.minisketch.include index 1363bec34e..c6f894f0ca 100644 --- a/src/Makefile.minisketch.include +++ b/src/Makefile.minisketch.include @@ -12,10 +12,6 @@ LIBMINISKETCH_CPPFLAGS += -DHAVE_CLMUL MINISKETCH_LIBS += $(LIBMINISKETCH_CLMUL) endif -if HAVE_CLZ -LIBMINISKETCH_CPPFLAGS += -DHAVE_CLZ -endif - EXTRA_LIBRARIES += $(MINISKETCH_LIBS) minisketch_libminisketch_clmul_a_SOURCES = $(MINISKETCH_FIELD_CLMUL_SOURCES_INT) $(MINISKETCH_FIELD_CLMUL_HEADERS_INT) diff --git a/src/bench/rollingbloom.cpp b/src/bench/rollingbloom.cpp index 30bc1d5fdf..ade4768d67 100644 --- a/src/bench/rollingbloom.cpp +++ b/src/bench/rollingbloom.cpp @@ -5,6 +5,9 @@ #include #include +#include + +#include static void RollingBloom(benchmark::Bench& bench) { @@ -13,16 +16,10 @@ static void RollingBloom(benchmark::Bench& bench) uint32_t count = 0; bench.run([&] { count++; - data[0] = count & 0xFF; - data[1] = (count >> 8) & 0xFF; - data[2] = (count >> 16) & 0xFF; - data[3] = (count >> 24) & 0xFF; + WriteLE32(data.data(), count); filter.insert(data); - data[0] = (count >> 24) & 0xFF; - data[1] = (count >> 16) & 0xFF; - data[2] = (count >> 8) & 0xFF; - data[3] = count & 0xFF; + WriteBE32(data.data(), count); filter.contains(data); }); } diff --git a/src/chainparams.cpp b/src/chainparams.cpp index c27d1cf5aa..eeab97c2c4 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -295,6 +295,8 @@ class CMainParams : public CChainParams { vSporkAddresses = {"Xgtyuk76vhuFW2iT7UAiHgNdWXCf3J34wh"}; nMinSporkKeys = 1; + nCreditPoolPeriodBlocks = 576; + checkpointData = { { {1500, uint256S("0x000000aaf0300f59f49bc3e970bad15c11f961fe2347accffff19d96ec9778e3")}, @@ -484,6 +486,8 @@ class CTestNetParams : public CChainParams { vSporkAddresses = {"yjPtiKh2uwk3bDutTEA2q9mCtXyiZRWn55"}; nMinSporkKeys = 1; + nCreditPoolPeriodBlocks = 576; + checkpointData = { { {255, uint256S("0x0000080b600e06f4c07880673f027210f9314575f5f875fafe51971e268b886a")}, @@ -664,6 +668,8 @@ class CDevNetParams : public CChainParams { vSporkAddresses = {"yjPtiKh2uwk3bDutTEA2q9mCtXyiZRWn55"}; nMinSporkKeys = 1; + nCreditPoolPeriodBlocks = 576; + checkpointData = (CCheckpointData) { { { 0, uint256S("0x000008ca1832a4baf228eb1553c03d3a2c8e02399550dd6ea8d65cec3ef23d2e")}, @@ -812,9 +818,9 @@ class CRegTestParams : public CChainParams { consensus.vDeployments[Consensus::DEPLOYMENT_WITHDRAWALS].bit = 11; consensus.vDeployments[Consensus::DEPLOYMENT_WITHDRAWALS].nStartTime = 0; consensus.vDeployments[Consensus::DEPLOYMENT_WITHDRAWALS].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT; - consensus.vDeployments[Consensus::DEPLOYMENT_WITHDRAWALS].nWindowSize = 600; - consensus.vDeployments[Consensus::DEPLOYMENT_WITHDRAWALS].nThresholdStart = 600 / 5 * 4; // 80% of window size - consensus.vDeployments[Consensus::DEPLOYMENT_WITHDRAWALS].nThresholdMin = 600 / 5 * 3; // 60% of window size + consensus.vDeployments[Consensus::DEPLOYMENT_WITHDRAWALS].nWindowSize = 200; + consensus.vDeployments[Consensus::DEPLOYMENT_WITHDRAWALS].nThresholdStart = 200 / 5 * 4; // 80% of window size + consensus.vDeployments[Consensus::DEPLOYMENT_WITHDRAWALS].nThresholdMin = 200 / 5 * 3; // 60% of window size consensus.vDeployments[Consensus::DEPLOYMENT_WITHDRAWALS].nFalloffCoeff = 5; // this corresponds to 10 periods consensus.vDeployments[Consensus::DEPLOYMENT_WITHDRAWALS].useEHF = true; @@ -866,6 +872,8 @@ class CRegTestParams : public CChainParams { vSporkAddresses = {"yj949n1UH6fDhw6HtVE5VMj2iSTaSWBMcW"}; nMinSporkKeys = 1; + nCreditPoolPeriodBlocks = 100; + checkpointData = { { {0, uint256S("0x000008ca1832a4baf228eb1553c03d3a2c8e02399550dd6ea8d65cec3ef23d2e")}, diff --git a/src/chainparams.h b/src/chainparams.h index 7dc89a36b9..b832f04880 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -152,6 +152,7 @@ class CChainParams int FulfilledRequestExpireTime() const { return nFulfilledRequestExpireTime; } const std::vector& SporkAddresses() const { return vSporkAddresses; } int MinSporkKeys() const { return nMinSporkKeys; } + int CreditPoolPeriodBlocks() const { return nCreditPoolPeriodBlocks; } [[nodiscard]] std::optional GetLLMQ(Consensus::LLMQType llmqType) const; protected: @@ -188,6 +189,8 @@ class CChainParams int nMinSporkKeys; uint16_t nDefaultPlatformP2PPort; uint16_t nDefaultPlatformHTTPPort; + /// The number of blocks the credit pool tracks; 576 (one day) on mainnet, reduced on regtest + int nCreditPoolPeriodBlocks; void AddLLMQ(Consensus::LLMQType llmqType); }; diff --git a/src/evo/creditpool.cpp b/src/evo/creditpool.cpp index b3474a78a3..abcf72650a 100644 --- a/src/evo/creditpool.cpp +++ b/src/evo/creditpool.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -153,7 +154,7 @@ CCreditPool CCreditPoolManager::ConstructCreditPool(const CBlockIndex* const blo return opt_cbTx->creditPoolBalance; }(); - // We use here sliding window with LimitBlocksToTrace to determine + // We use here sliding window with Params().CreditPoolPeriodBlocks to determine // current limits for asset unlock transactions. // Indexes should not be duplicated since genesis block, but the Unlock Amount // of withdrawal transaction is limited only by this window @@ -164,7 +165,7 @@ CCreditPool CCreditPoolManager::ConstructCreditPool(const CBlockIndex* const blo } const CBlockIndex* distant_block_index = block_index; - for (size_t i = 0; i < CCreditPoolManager::LimitBlocksToTrace; ++i) { + for ([[maybe_unused]] auto _ : irange::range(Params().CreditPoolPeriodBlocks())) { distant_block_index = distant_block_index->pprev; if (distant_block_index == nullptr) break; } diff --git a/src/evo/creditpool.h b/src/evo/creditpool.h index 9545922caf..dfb3004f8e 100644 --- a/src/evo/creditpool.h +++ b/src/evo/creditpool.h @@ -114,7 +114,6 @@ class CCreditPoolManager static constexpr int DISK_SNAPSHOT_PERIOD = 576; // once per day public: - static constexpr int LimitBlocksToTrace = 576; static constexpr CAmount LimitAmountLow = 100 * COIN; static constexpr CAmount LimitAmountHigh = 1000 * COIN; static constexpr CAmount LimitAmountV22 = 2000 * COIN; diff --git a/src/flat-database.h b/src/flat-database.h index e36f9f7585..b17947b7fa 100644 --- a/src/flat-database.h +++ b/src/flat-database.h @@ -36,7 +36,7 @@ class CFlatDB std::string strFilename; std::string strMagicMessage; - bool CoreWrite(const T& objToSave) + [[nodiscard]] bool CoreWrite(const T& objToSave) { // LOCK(objToSave.cs); @@ -53,8 +53,9 @@ class CFlatDB // open output file, and associate with CAutoFile FILE *file = fsbridge::fopen(pathDB, "wb"); CAutoFile fileout(file, SER_DISK, CLIENT_VERSION); - if (fileout.IsNull()) + if (fileout.IsNull()) { return error("%s: Failed to open file %s", __func__, fs::PathToString(pathDB)); + } // Write and commit header, data try { @@ -71,7 +72,7 @@ class CFlatDB return true; } - ReadResult CoreRead(T& objToLoad) + [[nodiscard]] ReadResult CoreRead(T& objToLoad) { //LOCK(objToLoad.cs); @@ -79,9 +80,8 @@ class CFlatDB // open input file, and associate with CAutoFile FILE *file = fsbridge::fopen(pathDB, "rb"); CAutoFile filein(file, SER_DISK, CLIENT_VERSION); - if (filein.IsNull()) - { - error("%s: Failed to open file %s", __func__, fs::PathToString(pathDB)); + if (filein.IsNull()) { + // It is not actually error, maybe it's a first initialization of core. return FileError; } @@ -156,14 +156,14 @@ class CFlatDB return Ok; } - bool Read(T& objToLoad) + [[nodiscard]] bool Read(T& objToLoad) { ReadResult readResult = CoreRead(objToLoad); if (readResult == FileError) LogPrintf("Missing file %s, will try to recreate\n", strFilename); else if (readResult != Ok) { - LogPrintf("Error reading %s: ", strFilename); + LogPrintf("ERROR: CFlatDB::Read Error reading %s: ", strFilename); if(readResult == IncorrectFormat) { LogPrintf("%s: Magic is ok but data has invalid format, will try to recreate\n", __func__); @@ -185,7 +185,7 @@ class CFlatDB { } - bool Load(T& objToLoad) + [[nodiscard]] bool Load(T& objToLoad) { LogPrintf("Reading info from %s...\n", strFilename); return Read(objToLoad); @@ -200,10 +200,10 @@ class CFlatDB int64_t nStart = GetTimeMillis(); LogPrintf("Writing info to %s...\n", strFilename); - CoreWrite(objToSave); + const bool ret = CoreWrite(objToSave); LogPrintf("%s dump finished %dms\n", strFilename, GetTimeMillis() - nStart); - return true; + return ret; } }; diff --git a/src/index/base.cpp b/src/index/base.cpp index e132e27ee5..cae1cc75fd 100644 --- a/src/index/base.cpp +++ b/src/index/base.cpp @@ -76,7 +76,7 @@ bool BaseIndex::Init() if (!m_best_block_index) { // index is not built yet // make sure we have all block data back to the genesis - prune_violation = GetFirstStoredBlock(active_chain.Tip()) != active_chain.Genesis(); + prune_violation = m_chainstate->m_blockman.GetFirstStoredBlock(*active_chain.Tip()) != active_chain.Genesis(); } // in case the index has a best block set and is not fully synced // check if we have the required blocks to continue building the index @@ -160,6 +160,18 @@ void BaseIndex::ThreadSync() pindex = pindex_next; } + CBlock block; + if (!ReadBlockFromDisk(block, pindex, consensus_params)) { + FatalError("%s: Failed to read block %s from disk", + __func__, pindex->GetBlockHash().ToString()); + return; + } + if (!WriteBlock(block, pindex)) { + FatalError("%s: Failed to write block %s to index database", + __func__, pindex->GetBlockHash().ToString()); + return; + } + auto current_time{std::chrono::steady_clock::now()}; if (last_log_time + SYNC_LOG_INTERVAL < current_time) { LogPrintf("Syncing %s with block chain from height %d\n", @@ -168,23 +180,11 @@ void BaseIndex::ThreadSync() } if (last_locator_write_time + SYNC_LOCATOR_WRITE_INTERVAL < current_time) { - SetBestBlockIndex(pindex->pprev); + SetBestBlockIndex(pindex); last_locator_write_time = current_time; // No need to handle errors in Commit. See rationale above. Commit(); } - - CBlock block; - if (!ReadBlockFromDisk(block, pindex, consensus_params)) { - FatalError("%s: Failed to read block %s from disk", - __func__, pindex->GetBlockHash().ToString()); - return; - } - if (!WriteBlock(block, pindex)) { - FatalError("%s: Failed to write block %s to index database", - __func__, pindex->GetBlockHash().ToString()); - return; - } } } diff --git a/src/init.cpp b/src/init.cpp index a7d9467381..efb29eae5d 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1470,7 +1470,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) LogPrintf("Using at most %i automatic connections (%i file descriptors available)\n", nMaxConnections, nFD); // Warn about relative -datadir path. - if (args.IsArgSet("-datadir") && !fs::PathFromString(args.GetArg("-datadir", "")).is_absolute()) { + if (args.IsArgSet("-datadir") && !args.GetPathArg("-datadir").is_absolute()) { LogPrintf("Warning: relative datadir option '%s' specified, which will be interpreted relative to the " /* Continued */ "current working directory '%s'. This is fragile, because if Dash Core is started in the future " "from a different location, it will be unable to locate the current data files. There could " diff --git a/src/interfaces/node.h b/src/interfaces/node.h index cda4c1add1..11d6a936d0 100644 --- a/src/interfaces/node.h +++ b/src/interfaces/node.h @@ -35,6 +35,7 @@ class UniValue; class Proxy; struct bilingual_str; enum class SynchronizationState; +enum class TransactionError; struct CNodeStateStats; struct NodeContext; @@ -264,6 +265,9 @@ class Node //! Get unspent outputs associated with a transaction. virtual bool getUnspentOutput(const COutPoint& output, Coin& coin) = 0; + //! Broadcast transaction. + virtual TransactionError broadcastTransaction(CTransactionRef tx, CAmount max_tx_fee, bilingual_str& err_string) = 0; + //! Get wallet loader. virtual WalletLoader& walletLoader() = 0; diff --git a/src/key.cpp b/src/key.cpp index 66209c7274..2e7f6dd34e 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -348,11 +348,11 @@ bool CExtKey::Derive(CExtKey &out, unsigned int _nChild) const { return key.Derive(out.key, out.chaincode, _nChild, chaincode); } -void CExtKey::SetSeed(Span seed) +void CExtKey::SetSeed(Span seed) { static const unsigned char hashkey[] = {'B','i','t','c','o','i','n',' ','s','e','e','d'}; std::vector> vout(64); - CHMAC_SHA512{hashkey, sizeof(hashkey)}.Write(seed.data(), seed.size()).Finalize(vout.data()); + CHMAC_SHA512{hashkey, sizeof(hashkey)}.Write(UCharCast(seed.data()), seed.size()).Finalize(vout.data()); key.Set(vout.data(), vout.data() + 32, true); memcpy(chaincode.begin(), vout.data() + 32, 32); nDepth = 0; diff --git a/src/key.h b/src/key.h index f9723e4f22..a7a57f7d89 100644 --- a/src/key.h +++ b/src/key.h @@ -91,7 +91,7 @@ class CKey //! Simple read-only vector-like interface. unsigned int size() const { return (fValid ? keydata.size() : 0); } - const unsigned char* data() const { return keydata.data(); } + const std::byte* data() const { return reinterpret_cast(keydata.data()); } const unsigned char* begin() const { return keydata.data(); } const unsigned char* end() const { return keydata.data() + size(); } @@ -188,7 +188,7 @@ struct CExtKey { void Decode(const unsigned char code[BIP32_EXTKEY_SIZE]); bool Derive(CExtKey& out, unsigned int nChild) const; CExtPubKey Neuter() const; - void SetSeed(Span seed); + void SetSeed(Span seed); }; /** Initialize the elliptic curve support. May not be called twice without calling ECC_Stop first. */ diff --git a/src/masternode/payments.cpp b/src/masternode/payments.cpp index 916bc5e2ff..b90dc41936 100644 --- a/src/masternode/payments.cpp +++ b/src/masternode/payments.cpp @@ -51,8 +51,12 @@ CAmount PlatformShare(const CAmount reward) LogPrint(BCLog::MNPAYMENTS, "CMNPaymentsProcessor::%s -- MN reward %lld reallocated to credit pool\n", __func__, platformReward); voutMasternodePaymentsRet.emplace_back(platformReward, CScript() << OP_RETURN); } - - auto dmnPayee = m_dmnman.GetListForBlock(pindexPrev).GetMNPayee(pindexPrev); + const auto mnList = m_dmnman.GetListForBlock(pindexPrev); + if (mnList.GetAllMNsCount() == 0) { + LogPrint(BCLog::MNPAYMENTS, "CMNPaymentsProcessor::%s -- no masternode registered to receive a payment\n", __func__); + return true; + } + const auto dmnPayee = mnList.GetMNPayee(pindexPrev); if (!dmnPayee) { return false; } @@ -88,7 +92,7 @@ CAmount PlatformShare(const CAmount reward) voutMasternodePaymentsRet.clear(); if(!GetBlockTxOuts(pindexPrev, blockSubsidy, feeReward, voutMasternodePaymentsRet)) { - LogPrintf("CMNPaymentsProcessor::%s -- No payee (deterministic masternode list empty)\n", __func__); + LogPrintf("CMNPaymentsProcessor::%s -- ERROR Failed to get payee\n", __func__); return false; } diff --git a/src/minisketch/.cirrus.yml b/src/minisketch/.cirrus.yml index 4a5353f137..5ceefee2cf 100644 --- a/src/minisketch/.cirrus.yml +++ b/src/minisketch/.cirrus.yml @@ -36,17 +36,6 @@ env_matrix_snippet: &ENV_MATRIX_VALGRIND TESTRUNS: 1 BUILD: -env_matrix_snippet: &ENV_MATRIX_SAN - - env: - ENABLE_FIELDS: 28 - - env: - BUILD: distcheck - - env: - CXXFLAGS: "-fsanitize=undefined -fno-omit-frame-pointer" - LDFLAGS: "-fsanitize=undefined -fno-omit-frame-pointer" - UBSAN_OPTIONS: "print_stacktrace=1:halt_on_error=1" - BENCH: no - env_matrix_snippet: &ENV_MATRIX_SAN_VALGRIND - env: ENABLE_FIELDS: "11,64,37" @@ -72,9 +61,9 @@ task: << : *ENV_MATRIX_SAN_VALGRIND matrix: - env: - CC: gcc + CXX: g++ - env: - CC: clang + CXX: clang++ -gdwarf-4 << : *MERGE_BASE test_script: - ./ci/cirrus.sh @@ -92,30 +81,45 @@ task: << : *ENV_MATRIX_VALGRIND matrix: - env: - CC: i686-linux-gnu-gcc + CXX: i686-linux-gnu-g++ - env: - CC: clang --target=i686-pc-linux-gnu -isystem /usr/i686-linux-gnu/include + CXX: clang++ --target=i686-linux-gnu -gdwarf-4 + CXXFLAGS: -g -O2 -isystem /usr/i686-linux-gnu/include -isystem /usr/i686-linux-gnu/include/c++/10/i686-linux-gnu test_script: - ./ci/cirrus.sh << : *CAT_LOGS task: - name: "x86_64: macOS Catalina" + name: "arm64: macOS Monterey" macos_instance: - image: catalina-base + image: ghcr.io/cirruslabs/macos-monterey-base:latest env: - # Cirrus gives us a fixed number of 12 virtual CPUs. - MAKEFLAGS: -j13 - matrix: - << : *ENV_MATRIX_SAN + # Cirrus gives us a fixed number of 4 virtual CPUs. + MAKEFLAGS: -j5 matrix: - env: - CC: gcc-9 + CXX: g++-11 + # Homebrew's gcc for arm64 has no libubsan. + matrix: + - env: + ENABLE_FIELDS: 28 + - env: + BUILD: distcheck - env: - CC: clang + CXX: clang++ + matrix: + - env: + ENABLE_FIELDS: 28 + - env: + BUILD: distcheck + - env: + CXXFLAGS: "-fsanitize=undefined -fno-omit-frame-pointer" + LDFLAGS: "-fsanitize=undefined -fno-omit-frame-pointer" + UBSAN_OPTIONS: "print_stacktrace=1:halt_on_error=1" + BENCH: no brew_script: - brew update - - brew install automake libtool gcc@9 + - brew install automake libtool gcc@11 << : *MERGE_BASE test_script: - ./ci/cirrus.sh @@ -128,13 +132,11 @@ task: cpu: 4 memory: 2G env: - EXEC_CMD: qemu-s390x -L /usr/s390x-linux-gnu + EXEC_CMD: qemu-s390x HOST: s390x-linux-gnu BUILD: << : *MERGE_BASE test_script: - # https://sourceware.org/bugzilla/show_bug.cgi?id=27008 - - rm /etc/ld.so.cache - ./ci/cirrus.sh << : *CAT_LOGS @@ -146,6 +148,7 @@ task: memory: 2G env: EXEC_CMD: wine + EXEC_EXT: .exe HOST: x86_64-w64-mingw32 BUILD: << : *MERGE_BASE diff --git a/src/minisketch/ci/cirrus.sh b/src/minisketch/ci/cirrus.sh index 02f737ca7f..36250d1651 100755 --- a/src/minisketch/ci/cirrus.sh +++ b/src/minisketch/ci/cirrus.sh @@ -7,7 +7,7 @@ export LC_ALL=C env >> test_env.log -$CC -v || true +$CXX -v || true valgrind --version || true ./autogen.sh @@ -32,10 +32,10 @@ then fi if [ -n "$EXEC_CMD" ]; then - $EXEC_CMD ./test $TESTRUNS - $EXEC_CMD ./test-verify $TESTRUNS + $EXEC_CMD "./test$EXEC_EXT" $TESTRUNS + $EXEC_CMD "./test-verify$EXEC_EXT" $TESTRUNS fi if [ "$BENCH" = "yes" ]; then - $EXEC_CMD ./bench + $EXEC_CMD "./bench$EXEC_EXT" fi diff --git a/src/minisketch/ci/linux-debian.Dockerfile b/src/minisketch/ci/linux-debian.Dockerfile index 63e5412ee7..122af36e1f 100644 --- a/src/minisketch/ci/linux-debian.Dockerfile +++ b/src/minisketch/ci/linux-debian.Dockerfile @@ -8,10 +8,10 @@ RUN apt-get update RUN apt-get install --no-install-recommends --no-upgrade -y \ git ca-certificates \ make automake libtool pkg-config dpkg-dev valgrind qemu-user \ - gcc g++ clang libc6-dbg \ + gcc g++ clang libclang-rt-dev libc6-dbg \ gcc-i686-linux-gnu g++-i686-linux-gnu libc6-dev-i386-cross libc6-dbg:i386 \ - g++-s390x-linux-gnu gcc-s390x-linux-gnu libc6-dev-s390x-cross libc6-dbg:s390x \ - wine g++-mingw-w64-x86-64 + g++-s390x-linux-gnu libstdc++6:s390x gcc-s390x-linux-gnu libc6-dev-s390x-cross libc6-dbg:s390x \ + wine wine64 g++-mingw-w64-x86-64 # Run a dummy command in wine to make it set up configuration RUN wine true || true diff --git a/src/minisketch/configure.ac b/src/minisketch/configure.ac index 83910448a2..65a47b45c2 100644 --- a/src/minisketch/configure.ac +++ b/src/minisketch/configure.ac @@ -102,13 +102,9 @@ case $host in esac AX_CHECK_COMPILE_FLAG([-Wall],[WARN_CXXFLAGS="$WARN_CXXFLAGS -Wall"],,[[$CXXFLAG_WERROR]]) +AX_CHECK_COMPILE_FLAG([-Wundef], [WARN_CXXFLAGS="$WARN_CXXFLAGS -Wundef"], [], [$CXXFLAG_WERROR]) AX_CHECK_COMPILE_FLAG([-fvisibility=hidden],[CXXFLAGS="$CXXFLAGS -fvisibility=hidden"],[],[$CXXFLAG_WERROR]) -## Some compilers (gcc) ignore unknown -Wno-* options, but warn about all -## unknown options if any other warning is produced. Test the -Wfoo case, and -## set the -Wno-foo case if it works. -AX_CHECK_COMPILE_FLAG([-Wshift-count-overflow],[NOWARN_CXXFLAGS="$NOWARN_CXXFLAGS -Wno-shift-count-overflow"],,[[$CXXFLAG_WERROR]]) - if test "x$use_ccache" != "xno"; then AC_MSG_CHECKING(if ccache should be used) if test x$CCACHE = x; then @@ -119,7 +115,6 @@ if test "x$use_ccache" != "xno"; then fi else use_ccache=yes - CC="$ac_cv_path_CCACHE $CC" CXX="$ac_cv_path_CCACHE $CXX" fi AC_MSG_RESULT($use_ccache) diff --git a/src/minisketch/include/minisketch.h b/src/minisketch/include/minisketch.h index 24d6b4e1c0..b0571d2788 100644 --- a/src/minisketch/include/minisketch.h +++ b/src/minisketch/include/minisketch.h @@ -239,7 +239,7 @@ class Minisketch /** Make this Minisketch a clone of the specified one. */ Minisketch& operator=(const Minisketch& sketch) noexcept { - if (sketch.m_minisketch) { + if (this != &sketch && sketch.m_minisketch) { m_minisketch = std::unique_ptr(minisketch_clone(sketch.m_minisketch.get())); } return *this; diff --git a/src/minisketch/src/false_positives.h b/src/minisketch/src/false_positives.h index 44ebb3e94c..9d0358997f 100644 --- a/src/minisketch/src/false_positives.h +++ b/src/minisketch/src/false_positives.h @@ -81,7 +81,8 @@ uint64_t BaseFPBits(uint32_t bits, uint32_t capacity) { size_t ComputeCapacity(uint32_t bits, size_t max_elements, uint32_t fpbits) { if (bits == 0) return 0; - uint64_t base_fpbits = BaseFPBits(bits, max_elements); + if (max_elements > 0xffffffff) return max_elements; + uint64_t base_fpbits = BaseFPBits(bits, static_cast(max_elements)); // The fpbits provided by the base max_elements==capacity case are sufficient. if (base_fpbits >= fpbits) return max_elements; // Otherwise, increment capacity by ceil(fpbits / bits) beyond that. @@ -90,6 +91,7 @@ size_t ComputeCapacity(uint32_t bits, size_t max_elements, uint32_t fpbits) { size_t ComputeMaxElements(uint32_t bits, size_t capacity, uint32_t fpbits) { if (bits == 0) return 0; + if (capacity > 0xffffffff) return capacity; // Start with max_elements=capacity, and decrease max_elements until the corresponding capacity is capacity. size_t max_elements = capacity; while (true) { diff --git a/src/minisketch/src/int_utils.h b/src/minisketch/src/int_utils.h index d21ba56f33..a6b89cd63c 100644 --- a/src/minisketch/src/int_utils.h +++ b/src/minisketch/src/int_utils.h @@ -7,13 +7,16 @@ #ifndef _MINISKETCH_INT_UTILS_H_ #define _MINISKETCH_INT_UTILS_H_ +#include #include #include #include #include -#ifdef _MSC_VER +#if defined(__cpp_lib_int_pow2) && __cpp_lib_int_pow2 >= 202002L +# include +#elif defined(_MSC_VER) # include #endif @@ -54,11 +57,10 @@ class BitWriter { int offset = 0; unsigned char* out; -public: - BitWriter(unsigned char* output) : out(output) {} - template - inline void Write(I val) { + inline void WriteInner(I val) { + // We right shift by up to 8 bits below. Verify that's well defined for the type I. + static_assert(std::numeric_limits::digits > 8, "BitWriter::WriteInner needs I > 8 bits"); int bits = BITS; if (bits + offset >= 8) { state |= ((val & ((I(1) << (8 - offset)) - 1)) << offset); @@ -77,6 +79,19 @@ class BitWriter { offset += bits; } + +public: + BitWriter(unsigned char* output) : out(output) {} + + template + inline void Write(I val) { + // If I is smaller than an unsigned int, invoke WriteInner with argument converted to unsigned. + using compute_type = typename std::conditional< + (std::numeric_limits::digits < std::numeric_limits::digits), + unsigned, I>::type; + return WriteInner(val); + } + inline void Flush() { if (offset) { *(out++) = state; @@ -129,7 +144,11 @@ constexpr inline I Mask() { return ((I((I(-1)) << (std::numeric_limits::digit /** Compute the smallest power of two that is larger than val. */ template static inline int CountBits(I val, int max) { -#ifdef _MSC_VER +#if defined(__cpp_lib_int_pow2) && __cpp_lib_int_pow2 >= 202002L + // c++20 impl + (void)max; + return std::bit_width(val); +#elif defined(_MSC_VER) (void)max; unsigned long index; unsigned char ret; @@ -140,7 +159,7 @@ static inline int CountBits(I val, int max) { } if (!ret) return 0; return index + 1; -#elif HAVE_CLZ +#elif defined(HAVE_CLZ) (void)max; if (val == 0) return 0; if (std::numeric_limits::digits >= std::numeric_limits::digits) { @@ -175,6 +194,7 @@ class BitsInt { } static constexpr inline bool IsZero(I a) { return a == 0; } + static constexpr inline bool IsOne(I a) { return a == 1; } static constexpr inline I Mask(I val) { return val & MASK; } static constexpr inline I Shift(I val, int bits) { return ((val << bits) & MASK); } static constexpr inline I UnsafeShift(I val, int bits) { return (val << bits); } @@ -190,7 +210,7 @@ class BitsInt { static constexpr inline int TopBits(I val) { static_assert(Count > 0, "BitsInt::TopBits needs Count > 0"); static_assert(Count <= BITS, "BitsInt::TopBits needs Offset <= BITS"); - return val >> (BITS - Count); + return static_cast(val >> (BITS - Count)); } static inline constexpr I CondXorWith(I val, bool cond, I v) { @@ -233,7 +253,7 @@ template inline constexpr I GFMul(con template inline I InvExtGCD(I x) { - if (F::IsZero(x)) return x; + if (F::IsZero(x) || F::IsOne(x)) return x; I t(0), newt(1); I r(MOD), newr = x; int rlen = BITS + 1, newrlen = F::Bits(newr, BITS); diff --git a/src/minisketch/src/minisketch.cpp b/src/minisketch/src/minisketch.cpp index d003fdf755..2e45409243 100644 --- a/src/minisketch/src/minisketch.cpp +++ b/src/minisketch/src/minisketch.cpp @@ -468,7 +468,7 @@ size_t minisketch_merge(minisketch* sketch, const minisketch* other_sketch) { ssize_t minisketch_decode(const minisketch* sketch, size_t max_elements, uint64_t* output) { const Sketch* s = (const Sketch*)sketch; s->Check(); - return s->Decode(max_elements, output); + return s->Decode(static_cast(max_elements), output); } void minisketch_set_seed(minisketch* sketch, uint64_t seed) { diff --git a/src/minisketch/src/sketch.h b/src/minisketch/src/sketch.h index 3e9bad793d..662b4e982f 100644 --- a/src/minisketch/src/sketch.h +++ b/src/minisketch/src/sketch.h @@ -29,7 +29,7 @@ class Sketch virtual ~Sketch() {} virtual size_t Syndromes() const = 0; - virtual void Init(int syndromes) = 0; + virtual void Init(size_t syndromes) = 0; virtual void Add(uint64_t element) = 0; virtual void Serialize(unsigned char*) const = 0; virtual void Deserialize(const unsigned char*) = 0; diff --git a/src/minisketch/src/sketch_impl.h b/src/minisketch/src/sketch_impl.h index 4547b742f2..c357f0e823 100644 --- a/src/minisketch/src/sketch_impl.h +++ b/src/minisketch/src/sketch_impl.h @@ -92,7 +92,8 @@ template void Sqr(std::vector& poly, const F& field) { if (poly.size() == 0) return; poly.resize(poly.size() * 2 - 1); - for (int x = poly.size() - 1; x >= 0; --x) { + for (size_t i = 0; i < poly.size(); ++i) { + auto x = poly.size() - i - 1; poly[x] = (x & 1) ? 0 : field.Sqr(poly[x / 2]); } } @@ -217,7 +218,7 @@ bool RecFindRoots(std::vector>& stack, size_t pos, } if (fully_factorizable) { - // Every succesful iteration of this algorithm splits the input + // Every successful iteration of this algorithm splits the input // polynomial further into buckets, each corresponding to a subset // of 2^(BITS-depth) roots. If after depth splits the degree of // the polynomial is >= 2^(BITS-depth), something is wrong. @@ -297,7 +298,7 @@ std::vector BerlekampMassey(const std::vector(n + 1 - (current.size() - 1) - (prev.size() - 1)); if (!b_have_inv) { b_inv = field.Inv(b); b_have_inv = true; @@ -366,7 +367,7 @@ class SketchImpl final : public Sketch } size_t Syndromes() const override { return m_syndromes.size(); } - void Init(int count) override { m_syndromes.assign(count, 0); } + void Init(size_t count) override { m_syndromes.assign(count, 0); } void Add(uint64_t val) override { @@ -405,7 +406,7 @@ class SketchImpl final : public Sketch for (const auto& root : roots) { *(out++) = m_field.ToUint64(root); } - return roots.size(); + return static_cast(roots.size()); } size_t Merge(const Sketch* other_sketch) override diff --git a/src/net_processing.cpp b/src/net_processing.cpp index d621b1008f..8676ad3371 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -79,11 +79,11 @@ static constexpr int32_t MAX_PEER_OBJECT_IN_FLIGHT = 100; /** Maximum number of announced objects from a peer */ static constexpr int32_t MAX_PEER_OBJECT_ANNOUNCEMENTS = 2 * MAX_INV_SZ; /** How many microseconds to delay requesting transactions from inbound peers */ -static constexpr std::chrono::microseconds INBOUND_PEER_TX_DELAY{std::chrono::seconds{2}}; -/** How long to wait (in microseconds) before downloading a transaction from an additional peer */ -static constexpr std::chrono::microseconds GETDATA_TX_INTERVAL{std::chrono::seconds{60}}; -/** Maximum delay (in microseconds) for transaction requests to avoid biasing some peers over others. */ -static constexpr std::chrono::microseconds MAX_GETDATA_RANDOM_DELAY{std::chrono::seconds{2}}; +static constexpr auto INBOUND_PEER_TX_DELAY{2s}; +/** How long to wait before downloading a transaction from an additional peer */ +static constexpr auto GETDATA_TX_INTERVAL{60s}; +/** Maximum delay for transaction requests to avoid biasing some peers over others. */ +static constexpr auto MAX_GETDATA_RANDOM_DELAY{2s}; /** How long to wait (expiry * factor microseconds) before expiring an in-flight getdata request to a peer */ static constexpr int64_t TX_EXPIRY_INTERVAL_FACTOR = 10; static_assert(INBOUND_PEER_TX_DELAY >= MAX_GETDATA_RANDOM_DELAY, @@ -112,7 +112,7 @@ static constexpr auto STALE_CHECK_INTERVAL{150s}; // 2.5 minutes (~block interva /** How frequently to check for extra outbound peers and disconnect */ static constexpr auto EXTRA_PEER_CHECK_INTERVAL{45s}; /** Minimum time an outbound-peer-eviction candidate must be connected for, in order to evict */ -static constexpr std::chrono::seconds MINIMUM_CONNECT_TIME{30}; +static constexpr auto MINIMUM_CONNECT_TIME{30s}; /** SHA256("main address relay")[0:8] */ static constexpr uint64_t RANDOMIZER_ID_ADDRESS_RELAY = 0x3cac0035b5866b90ULL; /// Age after which a stale block will no longer be served if requested as @@ -122,7 +122,7 @@ static constexpr int STALE_RELAY_AGE_LIMIT = 30 * 24 * 60 * 60; /// limiting block relay. Set to one week, denominated in seconds. static constexpr int HISTORICAL_BLOCK_AGE = 7 * 24 * 60 * 60; /** Time between pings automatically sent out for latency probing and keepalive */ -static constexpr std::chrono::minutes PING_INTERVAL{2}; +static constexpr auto PING_INTERVAL{2min}; /** The maximum number of entries in a locator */ static const unsigned int MAX_LOCATOR_SZ = 101; /** Number of blocks that can be requested at any given time from a single peer. */ @@ -153,17 +153,17 @@ static const int MAX_UNCONNECTING_HEADERS = 10; /** Minimum blocks required to signal NODE_NETWORK_LIMITED */ static const unsigned int NODE_NETWORK_LIMITED_MIN_BLOCKS = 288; /** Average delay between local address broadcasts */ -static constexpr auto AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL = 24h; +static constexpr auto AVG_LOCAL_ADDRESS_BROADCAST_INTERVAL{24h}; /** Average delay between peer address broadcasts */ -static constexpr auto AVG_ADDRESS_BROADCAST_INTERVAL = 30s; +static constexpr auto AVG_ADDRESS_BROADCAST_INTERVAL{30s}; /** Average delay between trickled inventory transmissions for inbound peers. * Blocks and peers with NetPermissionFlags::NoBan permission bypass this. */ -static constexpr auto INBOUND_INVENTORY_BROADCAST_INTERVAL = 5s; +static constexpr auto INBOUND_INVENTORY_BROADCAST_INTERVAL{5s}; /** Average delay between trickled inventory transmissions for outbound peers. * Use a smaller delay as there is less privacy concern for them. * Blocks and peers with NetPermissionFlags::NoBan permission bypass this. * Masternode outbound peers get half this delay. */ -static constexpr auto OUTBOUND_INVENTORY_BROADCAST_INTERVAL = 2s; +static constexpr auto OUTBOUND_INVENTORY_BROADCAST_INTERVAL{2s}; /** Maximum rate of inventory items to send per second. * Limits the impact of low-fee transaction floods. * We have 4 times smaller block times in Dash, so we need to push 4 times more invs per 1MB. */ @@ -459,7 +459,7 @@ struct CNodeState { * - its chain tip has at least as much work as ours * * CHAIN_SYNC_TIMEOUT: if a peer's best known block has less work than our tip, - * set a timeout CHAIN_SYNC_TIMEOUT seconds in the future: + * set a timeout CHAIN_SYNC_TIMEOUT in the future: * - If at timeout their best known block now has more work than our tip * when the timeout was set, then either reset the timeout or clear it * (after comparing against our current tip's work) @@ -1479,11 +1479,11 @@ std::chrono::microseconds GetObjectInterval(int invType) switch(invType) { case MSG_QUORUM_RECOVERED_SIG: - return std::chrono::seconds{15}; + return 15s; case MSG_CLSIG: - return std::chrono::seconds{5}; + return 5s; case MSG_ISDLOCK: - return std::chrono::seconds{10}; + return 10s; default: return GETDATA_TX_INTERVAL; } @@ -1606,7 +1606,7 @@ void PeerManagerImpl::ReattemptInitialBroadcast(CScheduler& scheduler) // Schedule next run for 10-15 minutes in the future. // We add randomness on every cycle to avoid the possibility of P2P fingerprinting. - const std::chrono::milliseconds delta = std::chrono::minutes{10} + GetRandMillis(std::chrono::minutes{5}); + const std::chrono::milliseconds delta = 10min + GetRandMillis(5min); scheduler.scheduleFromNow([&] { ReattemptInitialBroadcast(scheduler); }, delta); } @@ -1709,7 +1709,7 @@ bool PeerManagerImpl::GetNodeStateStats(NodeId nodeid, CNodeStateStats& stats) c // since pingtime does not update until the ping is complete, which might take a while. // So, if a ping is taking an unusually long time in flight, // the caller can immediately detect that this is happening. - std::chrono::microseconds ping_wait{0}; + auto ping_wait{0us}; if ((0 != peer->m_ping_nonce_sent) && (0 != peer->m_ping_start.load().count())) { ping_wait = GetTime() - peer->m_ping_start.load(); } @@ -1955,7 +1955,7 @@ void PeerManagerImpl::StartScheduledTasks(CScheduler& scheduler) scheduler.scheduleEvery([this] { this->CheckForStaleTipAndEvictPeers(); }, std::chrono::seconds{EXTRA_PEER_CHECK_INTERVAL}); // schedule next run for 10-15 minutes in the future - const std::chrono::milliseconds delta = std::chrono::minutes{10} + GetRandMillis(std::chrono::minutes{5}); + const std::chrono::milliseconds delta = 10min + GetRandMillis(5min); scheduler.scheduleFromNow([&] { ReattemptInitialBroadcast(scheduler); }, delta); } @@ -2566,10 +2566,10 @@ void PeerManagerImpl::ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic std::vector vNotFound; const CNetMsgMaker msgMaker(pfrom.GetCommonVersion()); - const std::chrono::seconds now = GetTime(); + const auto now{GetTime()}; // Get last mempool request time - const std::chrono::seconds mempool_req = tx_relay != nullptr ? tx_relay->m_last_mempool_req.load() - : std::chrono::seconds::min(); + const auto mempool_req = tx_relay != nullptr ? tx_relay->m_last_mempool_req.load() + : std::chrono::seconds::min(); // 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 @@ -3868,7 +3868,7 @@ void PeerManagerImpl::ProcessMessage( int64_t nSince = nNow - 10 * 60; // Update/increment addr rate limiting bucket. - const auto current_time = GetTime(); + const auto current_time{GetTime()}; if (peer->m_addr_token_bucket < MAX_ADDR_PROCESSING_TOKEN_BUCKET) { // Don't increment bucket if it's already full const auto time_diff = std::max(current_time - peer->m_addr_token_timestamp, 0us); @@ -3963,7 +3963,7 @@ void PeerManagerImpl::ProcessMessage( LOCK(cs_main); - const auto current_time = GetTime(); + const auto current_time{GetTime()}; uint256* best_block{nullptr}; for (CInv& inv : vInv) { @@ -4401,7 +4401,7 @@ void PeerManagerImpl::ProcessMessage( } } if (!fRejectedParents) { - const auto current_time = GetTime(); + const auto current_time{GetTime()}; for (const uint256& parent_txid : unique_parents) { CInv _inv(MSG_TX, parent_txid); @@ -5659,7 +5659,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto) // If we get here, the outgoing message serialization version is set and can't change. const CNetMsgMaker msgMaker(pto->GetCommonVersion()); - const auto current_time = GetTime(); + const auto current_time{GetTime()}; if (pto->IsAddrFetchConn() && current_time - pto->m_connected > 10 * AVG_ADDRESS_BROADCAST_INTERVAL) { LogPrint(BCLog::NET_NETCONN, "addrfetch connection timeout; disconnecting peer=%d\n", pto->GetId()); diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp index a86437099b..3c13592e8a 100644 --- a/src/node/blockstorage.cpp +++ b/src/node/blockstorage.cpp @@ -421,10 +421,10 @@ bool BlockManager::IsBlockPruned(const CBlockIndex* pblockindex) return (m_have_pruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0); } -const CBlockIndex* GetFirstStoredBlock(const CBlockIndex* start_block) { +const CBlockIndex* BlockManager::GetFirstStoredBlock(const CBlockIndex& start_block) +{ AssertLockHeld(::cs_main); - assert(start_block); - const CBlockIndex* last_block = start_block; + const CBlockIndex* last_block = &start_block; while (last_block->pprev && (last_block->pprev->nStatus & BLOCK_HAVE_DATA)) { last_block = last_block->pprev; } diff --git a/src/node/blockstorage.h b/src/node/blockstorage.h index a0c25f10d9..5b7468e79c 100644 --- a/src/node/blockstorage.h +++ b/src/node/blockstorage.h @@ -5,9 +5,10 @@ #ifndef BITCOIN_NODE_BLOCKSTORAGE_H #define BITCOIN_NODE_BLOCKSTORAGE_H +#include #include #include -#include // For CMessageHeader::MessageStartChars +#include #include #include @@ -196,6 +197,9 @@ class BlockManager //! Returns last CBlockIndex* that is a checkpoint const CBlockIndex* GetLastCheckpoint(const CCheckpointData& data) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + //! Find the first block that is not pruned + const CBlockIndex* GetFirstStoredBlock(const CBlockIndex& start_block LIFETIMEBOUND) EXCLUSIVE_LOCKS_REQUIRED(::cs_main); + /** True if any block files have ever been pruned. */ bool m_have_pruned = false; @@ -206,9 +210,6 @@ class BlockManager void UpdatePruneLock(const std::string& name, const PruneLockInfo& lock_info) EXCLUSIVE_LOCKS_REQUIRED(::cs_main); }; -//! Find the first block that is not pruned -const CBlockIndex* GetFirstStoredBlock(const CBlockIndex* start_block) EXCLUSIVE_LOCKS_REQUIRED(::cs_main); - void CleanupBlockRevFiles(); /** Open a block file (blk?????.dat) */ diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index e1418aa442..6e7b646eff 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -496,6 +496,10 @@ class NodeImpl : public Node LOCK(::cs_main); return chainman().ActiveChainstate().CoinsTip().GetCoin(output, coin); } + TransactionError broadcastTransaction(CTransactionRef tx, CAmount max_tx_fee, bilingual_str& err_string) override + { + return BroadcastTransaction(*m_context, std::move(tx), err_string, max_tx_fee, /*relay=*/ true, /*wait_callback=*/ false); + } WalletLoader& walletLoader() override { return *Assert(m_context->wallet_loader); @@ -767,7 +771,7 @@ class ChainImpl : public Chain bool checkFinalTx(const CTransaction& tx) override { LOCK(cs_main); - return CheckFinalTxAtTip(m_node.chainman->ActiveChain().Tip(), tx); + return CheckFinalTxAtTip(*Assert(m_node.chainman->ActiveChain().Tip()), tx); } bool isInstantSendLockedTx(const uint256& hash) override { diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 4e9a04b914..4a408958e2 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -74,25 +74,45 @@ class CTxIn CScript scriptSig; uint32_t nSequence; - /* Setting nSequence to this value for every input in a transaction - * disables nLockTime. */ + /** + * Setting nSequence to this value for every input in a transaction + * disables nLockTime/IsFinalTx(). + * It fails OP_CHECKLOCKTIMEVERIFY/CheckLockTime() for any input that has + * it set (BIP 65). + * It has SEQUENCE_LOCKTIME_DISABLE_FLAG set (BIP 68/112). + */ static const uint32_t SEQUENCE_FINAL = 0xffffffff; + /** + * This is the maximum sequence number that enables both nLockTime and + * OP_CHECKLOCKTIMEVERIFY (BIP 65). + * It has SEQUENCE_LOCKTIME_DISABLE_FLAG set (BIP 68/112). + */ + static const uint32_t MAX_SEQUENCE_NONFINAL{SEQUENCE_FINAL - 1}; - /* Below flags apply in the context of BIP 68*/ - /* If this flag set, CTxIn::nSequence is NOT interpreted as a - * relative lock-time. */ + // Below flags apply in the context of BIP 68. BIP 68 requires the tx + // version to be set to 2, or higher. + /** + * If this flag is set, CTxIn::nSequence is NOT interpreted as a + * relative lock-time. + * It skips SequenceLocks() for any input that has it set (BIP 68). + * It fails OP_CHECKSEQUENCEVERIFY/CheckSequence() for any input that has + * it set (BIP 112). + */ static const uint32_t SEQUENCE_LOCKTIME_DISABLE_FLAG = (1U << 31); - /* If CTxIn::nSequence encodes a relative lock-time and this flag + /** + * If CTxIn::nSequence encodes a relative lock-time and this flag * is set, the relative lock-time has units of 512 seconds, * otherwise it specifies blocks with a granularity of 1. */ static const uint32_t SEQUENCE_LOCKTIME_TYPE_FLAG = (1 << 22); - /* If CTxIn::nSequence encodes a relative lock-time, this mask is + /** + * If CTxIn::nSequence encodes a relative lock-time, this mask is * applied to extract that lock-time from the sequence field. */ static const uint32_t SEQUENCE_LOCKTIME_MASK = 0x0000ffff; - /* In order to use the same number of bits to encode roughly the + /** + * In order to use the same number of bits to encode roughly the * same wall-clock duration, and because blocks are naturally * limited to occur every 600s on average, the minimum granularity * for time-based relative lock-time is fixed at 512 seconds. diff --git a/src/qt/psbtoperationsdialog.cpp b/src/qt/psbtoperationsdialog.cpp index 25ac248e58..9811e7ec50 100644 --- a/src/qt/psbtoperationsdialog.cpp +++ b/src/qt/psbtoperationsdialog.cpp @@ -108,8 +108,8 @@ void PSBTOperationsDialog::broadcastTransaction() CTransactionRef tx = MakeTransactionRef(mtx); bilingual_str err_string; - TransactionError error = BroadcastTransaction( - *m_client_model->node().context(), tx, err_string, DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK(), /* relay */ true, /* await_callback */ false); + TransactionError error = + m_client_model->node().broadcastTransaction(tx, DEFAULT_MAX_RAW_TX_FEE_RATE.GetFeePerK(), err_string); if (error == TransactionError::OK) { showStatus(tr("Transaction broadcast successfully! Transaction ID: %1") diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index d80263fc69..5baf3c3d33 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1335,8 +1335,9 @@ static RPCHelpMan pruneblockchain() CChain& active_chain = active_chainstate.m_chain; int heightParam = request.params[0].get_int(); - if (heightParam < 0) + if (heightParam < 0) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative block height."); + } // Height value more than a billion is too high to be a block height, and // too low to be a block time (corresponds to timestamp from Sep 2001). @@ -1361,8 +1362,8 @@ static RPCHelpMan pruneblockchain() } PruneBlockFilesManual(active_chainstate, height); - const CBlockIndex* block = CHECK_NONFATAL(active_chain.Tip()); - const CBlockIndex* last_block = GetFirstStoredBlock(block); + const CBlockIndex& block{*CHECK_NONFATAL(active_chain.Tip())}; + const CBlockIndex* last_block{active_chainstate.m_blockman.GetFirstStoredBlock(block)}; return static_cast(last_block->nHeight); }, @@ -1782,10 +1783,10 @@ RPCHelpMan getblockchaininfo() LOCK(cs_main); CChainState& active_chainstate = chainman.ActiveChainstate(); - const CBlockIndex* tip = CHECK_NONFATAL(active_chainstate.m_chain.Tip()); - const int height = tip->nHeight; + const CBlockIndex& tip{*CHECK_NONFATAL(active_chainstate.m_chain.Tip())}; + const int height{tip.nHeight}; - const auto ehfSignals = CHECK_NONFATAL(node.mnhf_manager)->GetSignalsStage(tip); + const auto ehfSignals{CHECK_NONFATAL(node.mnhf_manager)->GetSignalsStage(&tip)}; UniValue obj(UniValue::VOBJ); if (args.IsArgSet("-devnet")) { @@ -1795,18 +1796,17 @@ RPCHelpMan getblockchaininfo() } obj.pushKV("blocks", height); obj.pushKV("headers", chainman.m_best_header ? chainman.m_best_header->nHeight : -1); - obj.pushKV("bestblockhash", tip->GetBlockHash().GetHex()); - obj.pushKV("difficulty", (double)GetDifficulty(tip)); - obj.pushKV("time", (int64_t)tip->nTime); - obj.pushKV("mediantime", (int64_t)tip->GetMedianTimePast()); - obj.pushKV("verificationprogress", GuessVerificationProgress(Params().TxData(), tip)); + obj.pushKV("bestblockhash", tip.GetBlockHash().GetHex()); + obj.pushKV("difficulty", GetDifficulty(&tip)); + obj.pushKV("time", tip.GetBlockTime()); + obj.pushKV("mediantime", tip.GetMedianTimePast()); + obj.pushKV("verificationprogress", GuessVerificationProgress(Params().TxData(), &tip)); obj.pushKV("initialblockdownload", active_chainstate.IsInitialBlockDownload()); - obj.pushKV("chainwork", tip->nChainWork.GetHex()); + obj.pushKV("chainwork", tip.nChainWork.GetHex()); obj.pushKV("size_on_disk", chainman.m_blockman.CalculateCurrentUsage()); obj.pushKV("pruned", fPruneMode); if (fPruneMode) { - const CBlockIndex* block = CHECK_NONFATAL(tip); - obj.pushKV("pruneheight", GetFirstStoredBlock(block)->nHeight); + obj.pushKV("pruneheight", chainman.m_blockman.GetFirstStoredBlock(tip)->nHeight); // if 0, execution bypasses the whole if block. bool automatic_pruning{args.GetArg("-prune", 0) != 1}; @@ -1818,24 +1818,28 @@ RPCHelpMan getblockchaininfo() const Consensus::Params& consensusParams = Params().GetConsensus(); UniValue softforks(UniValue::VOBJ); - // sorted by activation block - SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_HEIGHTINCB); - SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_DERSIG); - SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_CLTV); - SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_BIP147); - SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_CSV); - SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_DIP0001); - SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_DIP0003); - SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_DIP0008); - SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_DIP0020); - SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_DIP0024); - SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_BRR); - SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_V19); - SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_V20); - SoftForkDescPushBack(tip, softforks, consensusParams, Consensus::DEPLOYMENT_MN_RR); - SoftForkDescPushBack(tip, ehfSignals, softforks, consensusParams, Consensus::DEPLOYMENT_WITHDRAWALS); - SoftForkDescPushBack(tip, ehfSignals, softforks, consensusParams, Consensus::DEPLOYMENT_TESTDUMMY); - + for (auto deploy : { /* sorted by activation block */ + Consensus::DEPLOYMENT_HEIGHTINCB, + Consensus::DEPLOYMENT_DERSIG, + Consensus::DEPLOYMENT_CLTV, + Consensus::DEPLOYMENT_BIP147, + Consensus::DEPLOYMENT_CSV, + Consensus::DEPLOYMENT_DIP0001, + Consensus::DEPLOYMENT_DIP0003, + Consensus::DEPLOYMENT_DIP0008, + Consensus::DEPLOYMENT_DIP0020, + Consensus::DEPLOYMENT_DIP0024, + Consensus::DEPLOYMENT_BRR, + Consensus::DEPLOYMENT_V19, + Consensus::DEPLOYMENT_V20, + Consensus::DEPLOYMENT_MN_RR }) { + SoftForkDescPushBack(&tip, softforks, consensusParams, deploy); + } + for (auto ehf_deploy : { /* sorted by activation block */ + Consensus::DEPLOYMENT_WITHDRAWALS, + Consensus::DEPLOYMENT_TESTDUMMY }) { + SoftForkDescPushBack(&tip, ehfSignals, softforks, consensusParams, ehf_deploy); + } obj.pushKV("softforks", softforks); obj.pushKV("warnings", GetWarnings(false).original); diff --git a/src/rpc/rawtransaction_util.cpp b/src/rpc/rawtransaction_util.cpp index 9e41942965..5291696b85 100644 --- a/src/rpc/rawtransaction_util.cpp +++ b/src/rpc/rawtransaction_util.cpp @@ -58,7 +58,12 @@ CMutableTransaction ConstructTransaction(const UniValue& inputs_in, const UniVal if (nOutput < 0) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout cannot be negative"); - uint32_t nSequence = (rawTx.nLockTime ? CTxIn::SEQUENCE_FINAL - 1 : CTxIn::SEQUENCE_FINAL); + uint32_t nSequence; + if (rawTx.nLockTime) { + nSequence = CTxIn::MAX_SEQUENCE_NONFINAL; /* CTxIn::SEQUENCE_FINAL - 1 */ + } else { + nSequence = CTxIn::SEQUENCE_FINAL; + } // set the sequence number if passed in the parameters object const UniValue& sequenceObj = find_value(o, "sequence"); diff --git a/src/test/bip32_tests.cpp b/src/test/bip32_tests.cpp index 0fa6b7784f..314e3ea0d8 100644 --- a/src/test/bip32_tests.cpp +++ b/src/test/bip32_tests.cpp @@ -120,11 +120,12 @@ const std::vector TEST5 = { "xprv9s21ZrQH143K3QTDL4LXw2F7HEK3wJUD2nW2nRk4stbPy6cq3jPPqjiChkVvvNKmPGJxWUtg6LnF5kejMRNNU3TGtRBeJgk33yuGBxrMPHL" }; -void RunTest(const TestVector &test) { - std::vector seed = ParseHex(test.strHexMaster); +void RunTest(const TestVector& test) +{ + std::vector seed{ParseHex(test.strHexMaster)}; CExtKey key; CExtPubKey pubkey; - key.SetSeed(seed); + key.SetSeed(MakeByteSpan(seed)); pubkey = key.Neuter(); for (const TestDerivation &derive : test.vDerive) { unsigned char data[74]; diff --git a/src/test/fuzz/util.cpp b/src/test/fuzz/util.cpp index 848040bb6f..bd55d99f97 100644 --- a/src/test/fuzz/util.cpp +++ b/src/test/fuzz/util.cpp @@ -348,7 +348,7 @@ uint32_t ConsumeSequence(FuzzedDataProvider& fuzzed_data_provider) noexcept return fuzzed_data_provider.ConsumeBool() ? fuzzed_data_provider.PickValueInArray({ CTxIn::SEQUENCE_FINAL, - CTxIn::SEQUENCE_FINAL - 1 + CTxIn::MAX_SEQUENCE_NONFINAL, }) : fuzzed_data_provider.ConsumeIntegral(); } diff --git a/src/test/getarg_tests.cpp b/src/test/getarg_tests.cpp index 78a314d2ee..7d7687e909 100644 --- a/src/test/getarg_tests.cpp +++ b/src/test/getarg_tests.cpp @@ -153,6 +153,98 @@ BOOST_AUTO_TEST_CASE(intarg) BOOST_CHECK_EQUAL(m_local_args.GetArg("-bar", 11), 0); } +BOOST_AUTO_TEST_CASE(patharg) +{ + const auto dir = std::make_pair("-dir", ArgsManager::ALLOW_ANY); + SetupArgs({dir}); + ResetArgs(""); + BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), fs::path{}); + + const fs::path root_path{"/"}; + ResetArgs("-dir=/"); + BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), root_path); + + ResetArgs("-dir=/."); + BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), root_path); + + ResetArgs("-dir=/./"); + BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), root_path); + + ResetArgs("-dir=/.//"); + BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), root_path); + +#ifdef WIN32 + const fs::path win_root_path{"C:\\"}; + ResetArgs("-dir=C:\\"); + BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), win_root_path); + + ResetArgs("-dir=C:/"); + BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), win_root_path); + + ResetArgs("-dir=C:\\\\"); + BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), win_root_path); + + ResetArgs("-dir=C:\\."); + BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), win_root_path); + + ResetArgs("-dir=C:\\.\\"); + BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), win_root_path); + + ResetArgs("-dir=C:\\.\\\\"); + BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), win_root_path); +#endif + + const fs::path absolute_path{"/home/user/.bitcoin"}; + ResetArgs("-dir=/home/user/.bitcoin"); + BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), absolute_path); + + ResetArgs("-dir=/root/../home/user/.bitcoin"); + BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), absolute_path); + + ResetArgs("-dir=/home/./user/.bitcoin"); + BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), absolute_path); + + ResetArgs("-dir=/home/user/.bitcoin/"); + BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), absolute_path); + + ResetArgs("-dir=/home/user/.bitcoin//"); + BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), absolute_path); + + ResetArgs("-dir=/home/user/.bitcoin/."); + BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), absolute_path); + + ResetArgs("-dir=/home/user/.bitcoin/./"); + BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), absolute_path); + + ResetArgs("-dir=/home/user/.bitcoin/.//"); + BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), absolute_path); + + const fs::path relative_path{"user/.bitcoin"}; + ResetArgs("-dir=user/.bitcoin"); + BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), relative_path); + + ResetArgs("-dir=somewhere/../user/.bitcoin"); + BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), relative_path); + + ResetArgs("-dir=user/./.bitcoin"); + BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), relative_path); + + ResetArgs("-dir=user/.bitcoin/"); + BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), relative_path); + + ResetArgs("-dir=user/.bitcoin//"); + BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), relative_path); + + ResetArgs("-dir=user/.bitcoin/."); + BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), relative_path); + + ResetArgs("-dir=user/.bitcoin/./"); + BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), relative_path); + + ResetArgs("-dir=user/.bitcoin/.//"); + BOOST_CHECK_EQUAL(m_local_args.GetPathArg("-dir"), relative_path); +} + BOOST_AUTO_TEST_CASE(doubledash) { const auto foo = std::make_pair("-foo", ArgsManager::ALLOW_ANY); diff --git a/src/test/key_io_tests.cpp b/src/test/key_io_tests.cpp index 683fe85900..496c7fd27d 100644 --- a/src/test/key_io_tests.cpp +++ b/src/test/key_io_tests.cpp @@ -36,7 +36,7 @@ BOOST_AUTO_TEST_CASE(key_io_valid_parse) continue; } std::string exp_base58string = test[0].get_str(); - std::vector exp_payload = ParseHex(test[1].get_str()); + const std::vector exp_payload{ParseHex(test[1].get_str())}; const UniValue &metadata = test[2].get_obj(); bool isPrivkey = find_value(metadata, "isPrivkey").get_bool(); SelectParams(find_value(metadata, "chain").get_str()); diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index 76017966b0..db9361aaca 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -448,7 +448,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.nLockTime = 0; hash = tx.GetHash(); m_node.mempool->addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); - BOOST_CHECK(CheckFinalTxAtTip(m_node.chainman->ActiveChain().Tip(), CTransaction{tx})); // Locktime passes + BOOST_CHECK(CheckFinalTxAtTip(*Assert(m_node.chainman->ActiveChain().Tip()), CTransaction{tx})); // Locktime passes BOOST_CHECK(!TestSequenceLocks(CTransaction{tx})); // Sequence locks fail { @@ -462,7 +462,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) prevheights[0] = baseheight + 2; hash = tx.GetHash(); m_node.mempool->addUnchecked(entry.Time(GetTime()).FromTx(tx)); - BOOST_CHECK(CheckFinalTxAtTip(m_node.chainman->ActiveChain().Tip(), CTransaction{tx})); // Locktime passes + BOOST_CHECK(CheckFinalTxAtTip(*Assert(m_node.chainman->ActiveChain().Tip()), CTransaction{tx})); // Locktime passes BOOST_CHECK(!TestSequenceLocks(CTransaction{tx})); // Sequence locks fail for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++) @@ -478,12 +478,12 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) // absolute height locked tx.vin[0].prevout.hash = txFirst[2]->GetHash(); - tx.vin[0].nSequence = CTxIn::SEQUENCE_FINAL - 1; + tx.vin[0].nSequence = CTxIn::MAX_SEQUENCE_NONFINAL; prevheights[0] = baseheight + 3; tx.nLockTime = m_node.chainman->ActiveChain().Tip()->nHeight + 1; hash = tx.GetHash(); m_node.mempool->addUnchecked(entry.Time(GetTime()).FromTx(tx)); - BOOST_CHECK(!CheckFinalTxAtTip(m_node.chainman->ActiveChain().Tip(), CTransaction{tx})); // Locktime fails + BOOST_CHECK(!CheckFinalTxAtTip(*Assert(m_node.chainman->ActiveChain().Tip()), CTransaction{tx})); // Locktime fails BOOST_CHECK(TestSequenceLocks(CTransaction{tx})); // Sequence locks pass BOOST_CHECK(IsFinalTx(CTransaction(tx), m_node.chainman->ActiveChain().Tip()->nHeight + 2, m_node.chainman->ActiveChain().Tip()->GetMedianTimePast())); // Locktime passes on 2nd block @@ -494,7 +494,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) prevheights[0] = baseheight + 4; hash = tx.GetHash(); m_node.mempool->addUnchecked(entry.Time(GetTime()).FromTx(tx)); - BOOST_CHECK(!CheckFinalTxAtTip(m_node.chainman->ActiveChain().Tip(), CTransaction{tx})); // Locktime fails + BOOST_CHECK(!CheckFinalTxAtTip(*Assert(m_node.chainman->ActiveChain().Tip()), CTransaction{tx})); // Locktime fails BOOST_CHECK(TestSequenceLocks(CTransaction{tx})); // Sequence locks pass BOOST_CHECK(IsFinalTx(CTransaction(tx), m_node.chainman->ActiveChain().Tip()->nHeight + 2, m_node.chainman->ActiveChain().Tip()->GetMedianTimePast() + 1)); // Locktime passes 1 second later @@ -503,7 +503,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) prevheights[0] = m_node.chainman->ActiveChain().Tip()->nHeight + 1; tx.nLockTime = 0; tx.vin[0].nSequence = 0; - BOOST_CHECK(CheckFinalTxAtTip(m_node.chainman->ActiveChain().Tip(), CTransaction{tx})); // Locktime passes + BOOST_CHECK(CheckFinalTxAtTip(*Assert(m_node.chainman->ActiveChain().Tip()), CTransaction{tx})); // Locktime passes BOOST_CHECK(TestSequenceLocks(CTransaction{tx})); // Sequence locks pass tx.vin[0].nSequence = 1; BOOST_CHECK(!TestSequenceLocks(CTransaction{tx})); // Sequence locks fail diff --git a/src/test/net_peer_connection_tests.cpp b/src/test/net_peer_connection_tests.cpp index 87a190e88c..2a09791daf 100644 --- a/src/test/net_peer_connection_tests.cpp +++ b/src/test/net_peer_connection_tests.cpp @@ -118,7 +118,10 @@ BOOST_AUTO_TEST_CASE(test_addnode_getaddednodeinfo_and_connection_detection) BOOST_TEST_MESSAGE("\nCall AddNode() with 2 addrs resolving to existing localhost addnode entry; neither should be added"); BOOST_CHECK(!connman->AddNode({/*m_added_node=*/"127.0.0.1", /*m_use_v2transport=*/true})); + // OpenBSD doesn't support the IPv4 shorthand notation with omitted zero-bytes. +#if !defined(__OpenBSD__) BOOST_CHECK(!connman->AddNode({/*m_added_node=*/"127.1", /*m_use_v2transport=*/true})); +#endif BOOST_TEST_MESSAGE("\nExpect GetAddedNodeInfo to return expected number of peers with `include_connected` true/false"); BOOST_CHECK_EQUAL(connman->GetAddedNodeInfo(/*include_connected=*/true).size(), nodes.size()); diff --git a/src/test/serfloat_tests.cpp b/src/test/serfloat_tests.cpp index 15612e2950..ef5b3e3986 100644 --- a/src/test/serfloat_tests.cpp +++ b/src/test/serfloat_tests.cpp @@ -36,6 +36,7 @@ uint64_t TestDouble(double f) { } // namespace BOOST_AUTO_TEST_CASE(double_serfloat_tests) { + // Test specific values against their expected encoding. BOOST_CHECK_EQUAL(TestDouble(0.0), 0U); BOOST_CHECK_EQUAL(TestDouble(-0.0), 0x8000000000000000); BOOST_CHECK_EQUAL(TestDouble(std::numeric_limits::infinity()), 0x7ff0000000000000U); @@ -45,55 +46,76 @@ BOOST_AUTO_TEST_CASE(double_serfloat_tests) { BOOST_CHECK_EQUAL(TestDouble(2.0), 0x4000000000000000ULL); BOOST_CHECK_EQUAL(TestDouble(4.0), 0x4010000000000000ULL); BOOST_CHECK_EQUAL(TestDouble(785.066650390625), 0x4088888880000000ULL); + BOOST_CHECK_EQUAL(TestDouble(3.7243058682384174), 0x400dcb60e0031440); + BOOST_CHECK_EQUAL(TestDouble(91.64070592566159), 0x4056e901536d447a); + BOOST_CHECK_EQUAL(TestDouble(-98.63087668642575), 0xc058a860489c007a); + BOOST_CHECK_EQUAL(TestDouble(4.908737756962054), 0x4013a28c268b2b70); + BOOST_CHECK_EQUAL(TestDouble(77.9247330021754), 0x40537b2ed3547804); + BOOST_CHECK_EQUAL(TestDouble(40.24732825357566), 0x40441fa873c43dfc); + BOOST_CHECK_EQUAL(TestDouble(71.39395607929222), 0x4051d936938f27b6); + BOOST_CHECK_EQUAL(TestDouble(58.80100710817612), 0x404d668766a2bd70); + BOOST_CHECK_EQUAL(TestDouble(-30.10665786964975), 0xc03e1b4dee1e01b8); + BOOST_CHECK_EQUAL(TestDouble(60.15231509068704), 0x404e137f0f969814); + BOOST_CHECK_EQUAL(TestDouble(-48.15848711335961), 0xc04814494e445bc6); + BOOST_CHECK_EQUAL(TestDouble(26.68450101125353), 0x403aaf3b755169b0); + BOOST_CHECK_EQUAL(TestDouble(-65.72071986604303), 0xc0506e2046378ede); + BOOST_CHECK_EQUAL(TestDouble(17.95575825512381), 0x4031f4ac92b0a388); + BOOST_CHECK_EQUAL(TestDouble(-35.27171863226279), 0xc041a2c7ad17a42a); + BOOST_CHECK_EQUAL(TestDouble(-8.58810329425124), 0xc0212d1bdffef538); + BOOST_CHECK_EQUAL(TestDouble(88.51393044338977), 0x405620e43c83b1c8); + BOOST_CHECK_EQUAL(TestDouble(48.07224932612732), 0x4048093f77466ffc); + BOOST_CHECK_EQUAL(TestDouble(9.867348871395659e+117), 0x586f4daeb2459b9f); + BOOST_CHECK_EQUAL(TestDouble(-1.5166424385129721e+206), 0xeabe3bbc484bd458); + BOOST_CHECK_EQUAL(TestDouble(-8.585156555624594e-275), 0x8707c76eee012429); + BOOST_CHECK_EQUAL(TestDouble(2.2794371091628822e+113), 0x5777b2184458f4ee); + BOOST_CHECK_EQUAL(TestDouble(-1.1290476594131867e+163), 0xe1c91893d3488bb0); + BOOST_CHECK_EQUAL(TestDouble(9.143848423979275e-246), 0x0d0ff76e5f2620a3); + BOOST_CHECK_EQUAL(TestDouble(-2.8366718125941117e+81), 0xd0d7ec7e754b394a); + BOOST_CHECK_EQUAL(TestDouble(-1.2754409481684012e+229), 0xef80d32f8ec55342); + BOOST_CHECK_EQUAL(TestDouble(6.000577060053642e-186), 0x197a1be7c8209b6a); + BOOST_CHECK_EQUAL(TestDouble(2.0839423284378986e-302), 0x014c94f8689cb0a5); + BOOST_CHECK_EQUAL(TestDouble(-1.422140051483753e+259), 0xf5bd99271d04bb35); + BOOST_CHECK_EQUAL(TestDouble(-1.0593973991188853e+46), 0xc97db0cdb72d1046); + BOOST_CHECK_EQUAL(TestDouble(2.62945125875249e+190), 0x67779b36366c993b); + BOOST_CHECK_EQUAL(TestDouble(-2.920377657275094e+115), 0xd7e7b7b45908e23b); + BOOST_CHECK_EQUAL(TestDouble(9.790289014855851e-118), 0x27a3c031cc428bcc); + BOOST_CHECK_EQUAL(TestDouble(-4.629317182034961e-114), 0xa866ccf0b753705a); + BOOST_CHECK_EQUAL(TestDouble(-1.7674605603846528e+279), 0xf9e8ed383ffc3e25); + BOOST_CHECK_EQUAL(TestDouble(2.5308171727712605e+120), 0x58ef5cd55f0ec997); + BOOST_CHECK_EQUAL(TestDouble(-1.05034156412799e+54), 0xcb25eea1b9350fa0); - // Roundtrip test on IEC559-compatible systems - if (std::numeric_limits::is_iec559) { - BOOST_CHECK_EQUAL(sizeof(double), 8U); - BOOST_CHECK_EQUAL(sizeof(uint64_t), 8U); - // Test extreme values - TestDouble(std::numeric_limits::min()); - TestDouble(-std::numeric_limits::min()); - TestDouble(std::numeric_limits::max()); - TestDouble(-std::numeric_limits::max()); - TestDouble(std::numeric_limits::lowest()); - TestDouble(-std::numeric_limits::lowest()); - TestDouble(std::numeric_limits::quiet_NaN()); - TestDouble(-std::numeric_limits::quiet_NaN()); - TestDouble(std::numeric_limits::signaling_NaN()); - TestDouble(-std::numeric_limits::signaling_NaN()); - TestDouble(std::numeric_limits::denorm_min()); - TestDouble(-std::numeric_limits::denorm_min()); - // Test exact encoding: on currently supported platforms, EncodeDouble - // should produce exactly the same as the in-memory representation for non-NaN. - for (int j = 0; j < 1000; ++j) { - // Iterate over 9 specific bits exhaustively; the others are chosen randomly. - // These specific bits are the sign bit, and the 2 top and bottom bits of - // exponent and mantissa in the IEEE754 binary64 format. - for (int x = 0; x < 512; ++x) { - uint64_t v = InsecureRandBits(64); - v &= ~(uint64_t{1} << 0); - if (x & 1) v |= (uint64_t{1} << 0); - v &= ~(uint64_t{1} << 1); - if (x & 2) v |= (uint64_t{1} << 1); - v &= ~(uint64_t{1} << 50); - if (x & 4) v |= (uint64_t{1} << 50); - v &= ~(uint64_t{1} << 51); - if (x & 8) v |= (uint64_t{1} << 51); - v &= ~(uint64_t{1} << 52); - if (x & 16) v |= (uint64_t{1} << 52); - v &= ~(uint64_t{1} << 53); - if (x & 32) v |= (uint64_t{1} << 53); - v &= ~(uint64_t{1} << 61); - if (x & 64) v |= (uint64_t{1} << 61); - v &= ~(uint64_t{1} << 62); - if (x & 128) v |= (uint64_t{1} << 62); - v &= ~(uint64_t{1} << 63); - if (x & 256) v |= (uint64_t{1} << 63); - double f; - memcpy(&f, &v, 8); - uint64_t v2 = TestDouble(f); - if (!std::isnan(f)) BOOST_CHECK_EQUAL(v, v2); + // Test extreme values + BOOST_CHECK_EQUAL(TestDouble(std::numeric_limits::min()), 0x10000000000000); + BOOST_CHECK_EQUAL(TestDouble(-std::numeric_limits::min()), 0x8010000000000000); + BOOST_CHECK_EQUAL(TestDouble(std::numeric_limits::max()), 0x7fefffffffffffff); + BOOST_CHECK_EQUAL(TestDouble(-std::numeric_limits::max()), 0xffefffffffffffff); + BOOST_CHECK_EQUAL(TestDouble(std::numeric_limits::lowest()), 0xffefffffffffffff); + BOOST_CHECK_EQUAL(TestDouble(-std::numeric_limits::lowest()), 0x7fefffffffffffff); + BOOST_CHECK_EQUAL(TestDouble(std::numeric_limits::denorm_min()), 0x1); + BOOST_CHECK_EQUAL(TestDouble(-std::numeric_limits::denorm_min()), 0x8000000000000001); + // Note that all NaNs are encoded the same way. + BOOST_CHECK_EQUAL(TestDouble(std::numeric_limits::quiet_NaN()), 0x7ff8000000000000); + BOOST_CHECK_EQUAL(TestDouble(-std::numeric_limits::quiet_NaN()), 0x7ff8000000000000); + BOOST_CHECK_EQUAL(TestDouble(std::numeric_limits::signaling_NaN()), 0x7ff8000000000000); + BOOST_CHECK_EQUAL(TestDouble(-std::numeric_limits::signaling_NaN()), 0x7ff8000000000000); + + // Construct doubles to test from the encoding. + static_assert(sizeof(double) == 8); + static_assert(sizeof(uint64_t) == 8); + for (int j = 0; j < 1000; ++j) { + // Iterate over 9 specific bits exhaustively; the others are chosen randomly. + // These specific bits are the sign bit, and the 2 top and bottom bits of + // exponent and mantissa in the IEEE754 binary64 format. + for (int x = 0; x < 512; ++x) { + uint64_t v = InsecureRandBits(64); + int x_pos = 0; + for (int v_pos : {0, 1, 50, 51, 52, 53, 61, 62, 63}) { + v &= ~(uint64_t{1} << v_pos); + if ((x >> (x_pos++)) & 1) v |= (uint64_t{1} << v_pos); } + double f; + memcpy(&f, &v, 8); + TestDouble(f); } } } diff --git a/src/torcontrol.cpp b/src/torcontrol.cpp index 21ab55195a..26154a9939 100644 --- a/src/torcontrol.cpp +++ b/src/torcontrol.cpp @@ -48,6 +48,7 @@ static const float RECONNECT_TIMEOUT_EXP = 1.5; * this is belt-and-suspenders sanity limit to prevent memory exhaustion. */ static const int MAX_LINE_LENGTH = 100000; +static const uint16_t DEFAULT_TOR_SOCKS_PORT = 9050; /****** Low-level TorControlConnection ********/ @@ -333,6 +334,73 @@ TorController::~TorController() } } +void TorController::get_socks_cb(TorControlConnection& _conn, const TorControlReply& reply) +{ + // NOTE: We can only get here if -onion is unset + std::string socks_location; + if (reply.code == 250) { + for (const auto& line : reply.lines) { + if (0 == line.compare(0, 20, "net/listeners/socks=")) { + const std::string port_list_str = line.substr(20); + std::vector port_list = SplitString(port_list_str, ' '); + + for (auto& portstr : port_list) { + if (portstr.empty()) continue; + if ((portstr[0] == '"' || portstr[0] == '\'') && portstr.size() >= 2 && (*portstr.rbegin() == portstr[0])) { + portstr = portstr.substr(1, portstr.size() - 2); + if (portstr.empty()) continue; + } + socks_location = portstr; + if (0 == portstr.compare(0, 10, "127.0.0.1:")) { + // Prefer localhost - ignore other ports + break; + } + } + } + } + if (!socks_location.empty()) { + LogPrint(BCLog::TOR, "tor: Get SOCKS port command yielded %s\n", socks_location); + } else { + LogPrintf("tor: Get SOCKS port command returned nothing\n"); + } + } else if (reply.code == 510) { // 510 Unrecognized command + LogPrintf("tor: Get SOCKS port command failed with unrecognized command (You probably should upgrade Tor)\n"); + } else { + LogPrintf("tor: Get SOCKS port command failed; error code %d\n", reply.code); + } + + CService resolved; + Assume(!resolved.IsValid()); + if (!socks_location.empty()) { + resolved = LookupNumeric(socks_location, DEFAULT_TOR_SOCKS_PORT); + } + if (!resolved.IsValid()) { + // Fallback to old behaviour + resolved = LookupNumeric("127.0.0.1", DEFAULT_TOR_SOCKS_PORT); + } + + Assume(resolved.IsValid()); + LogPrint(BCLog::TOR, "tor: Configuring onion proxy for %s\n", resolved.ToStringAddrPort()); + Proxy addrOnion = Proxy(resolved, true); + SetProxy(NET_ONION, addrOnion); + + const auto onlynets = gArgs.GetArgs("-onlynet"); + + const bool onion_allowed_by_onlynet{ + !gArgs.IsArgSet("-onlynet") || + std::any_of(onlynets.begin(), onlynets.end(), [](const auto& n) { + return ParseNetwork(n) == NET_ONION; + })}; + + if (onion_allowed_by_onlynet) { + // If NET_ONION is reachable, then the below is a noop. + // + // If NET_ONION is not reachable, then none of -proxy or -onion was given. + // Since we are here, then -torcontrol and -torpassword were given. + SetReachable(NET_ONION, true); + } +} + void TorController::add_onion_cb(TorControlConnection& _conn, const TorControlReply& reply) { if (reply.code == 250) { @@ -376,25 +444,7 @@ void TorController::auth_cb(TorControlConnection& _conn, const TorControlReply& // Now that we know Tor is running setup the proxy for onion addresses // if -onion isn't set to something else. if (gArgs.GetArg("-onion", "") == "") { - CService resolved(LookupNumeric("127.0.0.1", 9050)); - Proxy addrOnion = Proxy(resolved, true); - SetProxy(NET_ONION, addrOnion); - - const auto onlynets = gArgs.GetArgs("-onlynet"); - - const bool onion_allowed_by_onlynet{ - !gArgs.IsArgSet("-onlynet") || - std::any_of(onlynets.begin(), onlynets.end(), [](const auto& n) { - return ParseNetwork(n) == NET_ONION; - })}; - - if (onion_allowed_by_onlynet) { - // If NET_ONION is reachable, then the below is a noop. - // - // If NET_ONION is not reachable, then none of -proxy or -onion was given. - // Since we are here, then -torcontrol and -torpassword were given. - SetReachable(NET_ONION, true); - } + _conn.Command("GETINFO net/listeners/socks", std::bind(&TorController::get_socks_cb, this, std::placeholders::_1, std::placeholders::_2)); } // Finally - now create the service diff --git a/src/torcontrol.h b/src/torcontrol.h index 7258f27cb6..75464b148e 100644 --- a/src/torcontrol.h +++ b/src/torcontrol.h @@ -140,6 +140,8 @@ class TorController std::vector clientNonce; public: + /** Callback for GETINFO net/listeners/socks result */ + void get_socks_cb(TorControlConnection& conn, const TorControlReply& reply); /** Callback for ADD_ONION result */ void add_onion_cb(TorControlConnection& conn, const TorControlReply& reply); /** Callback for AUTHENTICATE result */ diff --git a/src/util/system.cpp b/src/util/system.cpp index 9ecadeabce..6834189aa6 100644 --- a/src/util/system.cpp +++ b/src/util/system.cpp @@ -261,19 +261,6 @@ static bool CheckValid(const std::string& key, const util::SettingsValue& val, u return true; } -namespace { -fs::path StripRedundantLastElementsOfPath(const fs::path& path) -{ - auto result = path; - while (result.filename().empty() || fs::PathToString(result.filename()) == ".") { - result = result.parent_path(); - } - - assert(fs::equivalent(result, path.lexically_normal())); - return result; -} -} // namespace - // Define default constructor and destructor that are not inline, so code instantiating this class doesn't need to // #include class definitions for all members. // For example, m_settings has an internal dependency on univalue. @@ -420,6 +407,13 @@ std::optional ArgsManager::GetArgFlags(const std::string& name) co return std::nullopt; } +fs::path ArgsManager::GetPathArg(std::string pathlike_arg) const +{ + auto result = fs::PathFromString(GetArg(pathlike_arg, "")).lexically_normal(); + // Remove trailing slash, if present. + return result.has_filename() ? result : result.parent_path(); +} + const fs::path ArgsManager::GetBlocksDirPath() const { LOCK(cs_args); @@ -430,7 +424,7 @@ const fs::path ArgsManager::GetBlocksDirPath() const if (!path.empty()) return path; if (IsArgSet("-blocksdir")) { - path = fs::absolute(fs::PathFromString(GetArg("-blocksdir", ""))); + path = fs::absolute(GetPathArg("-blocksdir")); if (!fs::is_directory(path)) { path = ""; return path; @@ -454,9 +448,9 @@ const fs::path ArgsManager::GetDataDir(bool net_specific) const // this function if (!path.empty()) return path; - std::string datadir = GetArg("-datadir", ""); + const fs::path datadir{GetPathArg("-datadir")}; if (!datadir.empty()) { - path = fs::absolute(StripRedundantLastElementsOfPath(fs::PathFromString(datadir))); + path = fs::absolute(datadir); if (!fs::is_directory(path)) { path = ""; return path; @@ -476,7 +470,6 @@ const fs::path ArgsManager::GetDataDir(bool net_specific) const } } - path = StripRedundantLastElementsOfPath(path); return path; } @@ -871,8 +864,8 @@ fs::path GetBackupsDir() bool CheckDataDirOption() { - std::string datadir = gArgs.GetArg("-datadir", ""); - return datadir.empty() || fs::is_directory(fs::absolute(fs::PathFromString(datadir))); + const fs::path datadir{gArgs.GetPathArg("-datadir")}; + return datadir.empty() || fs::is_directory(fs::absolute(datadir)); } fs::path GetConfigFile(const std::string& confPath) diff --git a/src/util/system.h b/src/util/system.h index da838c61c4..b99a5f9a1e 100644 --- a/src/util/system.h +++ b/src/util/system.h @@ -283,6 +283,16 @@ class ArgsManager */ const std::map> GetCommandLineArgs() const; + /** + * Get a normalized path from a specified pathlike argument + * + * It is guaranteed that the returned path has no trailing slashes. + * + * @param pathlike_arg Pathlike argument to get a path from (e.g., "-datadir", "-blocksdir" or "-walletdir") + * @return Normalized path which is get from a specified pathlike argument + */ + fs::path GetPathArg(std::string pathlike_arg) const; + /** * Get blocks directory path * diff --git a/src/validation.cpp b/src/validation.cpp index b4f0c1810b..58ef87b637 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -145,10 +145,9 @@ const CBlockIndex* CChainState::FindForkInGlobalIndex(const CBlockLocator& locat bool CheckInputScripts(const CTransaction& tx, TxValidationState &state, const CCoinsViewCache &inputs, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector *pvChecks = nullptr); -bool CheckFinalTxAtTip(const CBlockIndex* active_chain_tip, const CTransaction& tx) +bool CheckFinalTxAtTip(const CBlockIndex& active_chain_tip, const CTransaction& tx) { AssertLockHeld(cs_main); - assert(active_chain_tip); // TODO: Make active_chain_tip a reference // CheckFinalTxAtTip() uses active_chain_tip.Height()+1 to evaluate // nLockTime because when IsFinalTx() is called within @@ -156,14 +155,14 @@ bool CheckFinalTxAtTip(const CBlockIndex* active_chain_tip, const CTransaction& // evaluated is what is used. Thus if we want to know if a // transaction can be part of the *next* block, we need to call // IsFinalTx() with one more than active_chain_tip.Height(). - const int nBlockHeight = active_chain_tip->nHeight + 1; + const int nBlockHeight = active_chain_tip.nHeight + 1; // BIP113 requires that time-locked transactions have nLockTime set to // less than the median time of the previous block they're contained in. // When the next block is created its previous block will be the current // chain tip, so we use that to calculate the median time passed to // IsFinalTx(). - const int64_t nBlockTime{active_chain_tip->GetMedianTimePast()}; + const int64_t nBlockTime{active_chain_tip.GetMedianTimePast()}; return IsFinalTx(tx, nBlockHeight, nBlockTime); } @@ -380,7 +379,7 @@ void CChainState::MaybeUpdateMempoolForReorg( const CTransaction& tx = it->GetTx(); // The transaction must be final. - if (!CheckFinalTxAtTip(m_chain.Tip(), tx)) return true; + if (!CheckFinalTxAtTip(*Assert(m_chain.Tip()), tx)) return true; LockPoints lp = it->GetLockPoints(); const bool validLP{TestLockPointValidity(m_chain, lp)}; CCoinsViewMemPool view_mempool(&CoinsTip(), *m_mempool); @@ -632,7 +631,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws) // Only accept nLockTime-using transactions that can be mined in the next // block; we don't want our mempool filled up with transactions that can't // be mined yet. - if (!CheckFinalTxAtTip(m_active_chainstate.m_chain.Tip(), tx)) { + if (!CheckFinalTxAtTip(*Assert(m_active_chainstate.m_chain.Tip()), tx)) { return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "non-final"); } @@ -2307,12 +2306,12 @@ bool CChainState::ConnectBlock(const CBlock& block, BlockValidationState& state, ::g_stats_client->gauge("blocks.tip.SigOps", nSigOps, 1.0f); TRACE6(validation, block_connected, - block.GetHash().data(), + block_hash.data(), pindex->nHeight, block.vtx.size(), nInputs, nSigOps, - GetTimeMicros() - nTimeStart // in microseconds (µs) + nTime8 - nTimeStart // in microseconds (µs) ); return true; diff --git a/src/validation.h b/src/validation.h index 0987366216..fd9bf01d89 100644 --- a/src/validation.h +++ b/src/validation.h @@ -275,12 +275,12 @@ bool GetUTXOCoin(CChainState& active_chainstate, const COutPoint& outpoint, Coin int GetUTXOHeight(CChainState& active_chainstate, const COutPoint& outpoint); int GetUTXOConfirmations(CChainState& active_chainstate, const COutPoint& outpoint); -/* Transaction policy functions */ +/* Mempool validation helper functions */ /** * Check if transaction will be final in the next block to be created. */ -bool CheckFinalTxAtTip(const CBlockIndex* active_chain_tip, const CTransaction& tx) EXCLUSIVE_LOCKS_REQUIRED(::cs_main); +bool CheckFinalTxAtTip(const CBlockIndex& active_chain_tip, const CTransaction& tx) EXCLUSIVE_LOCKS_REQUIRED(::cs_main); /** * Check if transaction will be BIP68 final in the next block to be created on top of tip. diff --git a/src/wallet/hdchain.cpp b/src/wallet/hdchain.cpp index 0f3b2dd498..710784d37f 100644 --- a/src/wallet/hdchain.cpp +++ b/src/wallet/hdchain.cpp @@ -140,7 +140,7 @@ void CHDChain::DeriveChildExtKey(uint32_t nAccountIndex, bool fInternal, uint32_ CExtKey changeKey; //key at m/purpose'/coin_type'/account'/change CExtKey childKey; //key at m/purpose'/coin_type'/account'/change/address_index - masterKey.SetSeed(vchSeed); + masterKey.SetSeed(MakeByteSpan(vchSeed)); // Use hardened derivation for purpose, coin_type and account // (keys >= 0x80000000 are hardened after bip32) diff --git a/src/wallet/load.cpp b/src/wallet/load.cpp index 5ecaa2faae..6f5b661efd 100644 --- a/src/wallet/load.cpp +++ b/src/wallet/load.cpp @@ -24,7 +24,7 @@ bool VerifyWallets(interfaces::Chain& chain) { if (gArgs.IsArgSet("-walletdir")) { - fs::path wallet_dir = fs::PathFromString(gArgs.GetArg("-walletdir", "")); + const fs::path wallet_dir{gArgs.GetPathArg("-walletdir")}; std::error_code error; // The canonical path cleans the path, preventing >1 Berkeley environment instances for the same directory // It also lets the fs::exists and fs::is_directory checks below pass on windows, since they return false diff --git a/src/wallet/rpcdump.cpp b/src/wallet/rpcdump.cpp index 3a69d4337f..d829bbab2c 100644 --- a/src/wallet/rpcdump.cpp +++ b/src/wallet/rpcdump.cpp @@ -1007,7 +1007,7 @@ RPCHelpMan dumpwallet() file << "# HD seed: " << HexStr(vchSeed) << "\n\n"; CExtKey masterKey; - masterKey.SetSeed(vchSeed); + masterKey.SetSeed(MakeByteSpan(vchSeed)); file << "# extended private masterkey: " << EncodeExtKey(masterKey) << "\n"; diff --git a/src/wallet/scriptpubkeyman.cpp b/src/wallet/scriptpubkeyman.cpp index 29ad2f0df8..66c553cfc9 100644 --- a/src/wallet/scriptpubkeyman.cpp +++ b/src/wallet/scriptpubkeyman.cpp @@ -350,7 +350,7 @@ void LegacyScriptPubKeyMan::UpgradeKeyMetadata() CExtKey masterKey; SecureVector vchSeed = hdChainCurrent.GetSeed(); - masterKey.SetSeed(vchSeed); + masterKey.SetSeed(MakeByteSpan(vchSeed)); CKeyID master_id = masterKey.key.GetPubKey().GetID(); std::unique_ptr batch = std::make_unique(m_storage.GetDatabase()); diff --git a/src/wallet/sqlite.cpp b/src/wallet/sqlite.cpp index 1437f7ae6f..f41b27d28f 100644 --- a/src/wallet/sqlite.cpp +++ b/src/wallet/sqlite.cpp @@ -36,6 +36,22 @@ static void ErrorLogCallback(void* arg, int code, const char* msg) LogPrintf("SQLite Error. Code: %d. Message: %s\n", code, msg); } +static bool BindBlobToStatement(sqlite3_stmt* stmt, + int index, + Span blob, + const std::string& description) +{ + int res = sqlite3_bind_blob(stmt, index, blob.data(), blob.size(), SQLITE_STATIC); + if (res != SQLITE_OK) { + LogPrintf("Unable to bind %s to statement: %s\n", description, sqlite3_errstr(res)); + sqlite3_clear_bindings(stmt); + sqlite3_reset(stmt); + return false; + } + + return true; +} + static std::optional ReadPragmaInteger(sqlite3* db, const std::string& key, const std::string& description, bilingual_str& error) { std::string stmt_text = strprintf("PRAGMA %s", key); @@ -376,14 +392,8 @@ bool SQLiteBatch::ReadKey(CDataStream&& key, CDataStream& value) assert(m_read_stmt); // Bind: leftmost parameter in statement is index 1 - int res = sqlite3_bind_blob(m_read_stmt, 1, key.data(), key.size(), SQLITE_STATIC); - if (res != SQLITE_OK) { - LogPrintf("%s: Unable to bind statement: %s\n", __func__, sqlite3_errstr(res)); - sqlite3_clear_bindings(m_read_stmt); - sqlite3_reset(m_read_stmt); - return false; - } - res = sqlite3_step(m_read_stmt); + if (!BindBlobToStatement(m_read_stmt, 1, key, "key")) return false; + int res = sqlite3_step(m_read_stmt); if (res != SQLITE_ROW) { if (res != SQLITE_DONE) { // SQLITE_DONE means "not found", don't log an error in that case. @@ -417,23 +427,11 @@ bool SQLiteBatch::WriteKey(CDataStream&& key, CDataStream&& value, bool overwrit // Bind: leftmost parameter in statement is index 1 // Insert index 1 is key, 2 is value - int res = sqlite3_bind_blob(stmt, 1, key.data(), key.size(), SQLITE_STATIC); - if (res != SQLITE_OK) { - LogPrintf("%s: Unable to bind key to statement: %s\n", __func__, sqlite3_errstr(res)); - sqlite3_clear_bindings(stmt); - sqlite3_reset(stmt); - return false; - } - res = sqlite3_bind_blob(stmt, 2, value.data(), value.size(), SQLITE_STATIC); - if (res != SQLITE_OK) { - LogPrintf("%s: Unable to bind value to statement: %s\n", __func__, sqlite3_errstr(res)); - sqlite3_clear_bindings(stmt); - sqlite3_reset(stmt); - return false; - } + if (!BindBlobToStatement(stmt, 1, key, "key")) return false; + if (!BindBlobToStatement(stmt, 2, value, "value")) return false; // Execute - res = sqlite3_step(stmt); + int res = sqlite3_step(stmt); sqlite3_clear_bindings(stmt); sqlite3_reset(stmt); if (res != SQLITE_DONE) { @@ -448,16 +446,10 @@ bool SQLiteBatch::EraseKey(CDataStream&& key) assert(m_delete_stmt); // Bind: leftmost parameter in statement is index 1 - int res = sqlite3_bind_blob(m_delete_stmt, 1, key.data(), key.size(), SQLITE_STATIC); - if (res != SQLITE_OK) { - LogPrintf("%s: Unable to bind statement: %s\n", __func__, sqlite3_errstr(res)); - sqlite3_clear_bindings(m_delete_stmt); - sqlite3_reset(m_delete_stmt); - return false; - } + if (!BindBlobToStatement(m_delete_stmt, 1, key, "key")) return false; // Execute - res = sqlite3_step(m_delete_stmt); + int res = sqlite3_step(m_delete_stmt); sqlite3_clear_bindings(m_delete_stmt); sqlite3_reset(m_delete_stmt); if (res != SQLITE_DONE) { @@ -472,18 +464,11 @@ bool SQLiteBatch::HasKey(CDataStream&& key) assert(m_read_stmt); // Bind: leftmost parameter in statement is index 1 - bool ret = false; - int res = sqlite3_bind_blob(m_read_stmt, 1, key.data(), key.size(), SQLITE_STATIC); - if (res == SQLITE_OK) { - res = sqlite3_step(m_read_stmt); - if (res == SQLITE_ROW) { - ret = true; - } - } - + if (!BindBlobToStatement(m_read_stmt, 1, key, "key")) return false; + int res = sqlite3_step(m_read_stmt); sqlite3_clear_bindings(m_read_stmt); sqlite3_reset(m_read_stmt); - return ret; + return res == SQLITE_ROW; } bool SQLiteBatch::StartCursor() diff --git a/src/wallet/test/bip39_tests.cpp b/src/wallet/test/bip39_tests.cpp index 7ea5e5c988..612ad59d9a 100644 --- a/src/wallet/test/bip39_tests.cpp +++ b/src/wallet/test/bip39_tests.cpp @@ -54,7 +54,7 @@ BOOST_AUTO_TEST_CASE(bip39_vectors) CExtKey key; CExtPubKey pubkey; - key.SetSeed(seed); + key.SetSeed(MakeByteSpan(seed)); pubkey = key.Neuter(); // printf("CBitcoinExtKey: %s\n", EncodeExtKey(key).c_str()); diff --git a/src/wallet/test/init_tests.cpp b/src/wallet/test/init_tests.cpp index ca27dbdc03..4c0b7af8cb 100644 --- a/src/wallet/test/init_tests.cpp +++ b/src/wallet/test/init_tests.cpp @@ -19,7 +19,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_default) bool result = m_wallet_loader->verify(); BOOST_CHECK(result == true); } - fs::path walletdir = fs::PathFromString(gArgs.GetArg("-walletdir", "")); + fs::path walletdir = gArgs.GetPathArg("-walletdir"); fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]); BOOST_CHECK_EQUAL(walletdir, expected_path); } @@ -32,7 +32,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_custom) bool result = m_wallet_loader->verify(); BOOST_CHECK(result == true); } - fs::path walletdir = fs::PathFromString(gArgs.GetArg("-walletdir", "")); + fs::path walletdir = gArgs.GetPathArg("-walletdir"); fs::path expected_path = fs::canonical(m_walletdir_path_cases["custom"]); BOOST_CHECK_EQUAL(walletdir, expected_path); } @@ -75,7 +75,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing) bool result = m_wallet_loader->verify(); BOOST_CHECK(result == true); } - fs::path walletdir = fs::PathFromString(gArgs.GetArg("-walletdir", "")); + fs::path walletdir = gArgs.GetPathArg("-walletdir"); fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]); BOOST_CHECK_EQUAL(walletdir, expected_path); } @@ -88,7 +88,7 @@ BOOST_AUTO_TEST_CASE(walletinit_verify_walletdir_no_trailing2) bool result = m_wallet_loader->verify(); BOOST_CHECK(result == true); } - fs::path walletdir = fs::PathFromString(gArgs.GetArg("-walletdir", "")); + fs::path walletdir = gArgs.GetPathArg("-walletdir"); fs::path expected_path = fs::canonical(m_walletdir_path_cases["default"]); BOOST_CHECK_EQUAL(walletdir, expected_path); } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 049100a764..576703ab60 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -5866,7 +5866,7 @@ void CWallet::SetupDescriptorScriptPubKeyMans() // Get the extended key CExtKey master_key; - master_key.SetSeed(seed_key); + master_key.SetSeed(MakeByteSpan(seed_key)); for (bool internal : {false, true}) { { // OUTPUT_TYPE is only one: LEGACY diff --git a/src/wallet/walletutil.cpp b/src/wallet/walletutil.cpp index 08af6a59ee..b76bcc1e19 100644 --- a/src/wallet/walletutil.cpp +++ b/src/wallet/walletutil.cpp @@ -12,7 +12,7 @@ fs::path GetWalletDir() fs::path path; if (gArgs.IsArgSet("-walletdir")) { - path = fs::PathFromString(gArgs.GetArg("-walletdir", "")); + path = gArgs.GetPathArg("-walletdir"); if (!fs::is_directory(path)) { // If the path specified doesn't exist, we return the deliberately // invalid empty string. diff --git a/test/functional/feature_asset_locks.py b/test/functional/feature_asset_locks.py index 1bc8309c6b..a6ecb4debf 100755 --- a/test/functional/feature_asset_locks.py +++ b/test/functional/feature_asset_locks.py @@ -44,7 +44,7 @@ llmq_type_test = 106 # LLMQType::LLMQ_TEST_PLATFORM tiny_amount = int(Decimal("0.0007") * COIN) -blocks_in_one_day = 576 +blocks_in_one_day = 100 HEIGHT_DIFF_EXPIRING = 48 class AssetLocksTest(DashTestFramework): @@ -52,7 +52,7 @@ def set_test_params(self): self.set_dash_test_params(4, 2, [[ "-whitelist=127.0.0.1", "-llmqtestinstantsenddip0024=llmq_test_instantsend", - "-testactivationheight=mn_rr@2500", + "-testactivationheight=mn_rr@1400", ]] * 4, evo_count=2) def skip_test_if_missing_module(self): @@ -621,9 +621,10 @@ def test_withdrawal_limits(self, node_wallet, node, pubkey): def test_mn_rr(self, node_wallet, node, pubkey): + self.log.info(node_wallet.getblockcount()) self.log.info("Activate mn_rr...") locked = self.get_credit_pool_balance() - self.activate_mn_rr(expected_activation_height=2500) + self.activate_mn_rr(expected_activation_height=1400) self.log.info(f'mn-rr height: {node.getblockcount()} credit: {self.get_credit_pool_balance()}') assert_equal(locked, self.get_credit_pool_balance()) @@ -635,7 +636,7 @@ def test_mn_rr(self, node_wallet, node, pubkey): all_mn_rewards = platform_reward + owner_reward + operator_reward assert_equal(all_mn_rewards, bt['coinbasevalue'] * 3 // 4) # 75/25 mn/miner reward split assert_equal(platform_reward, all_mn_rewards * 375 // 1000) # 0.375 platform share - assert_equal(platform_reward, 34371430) + assert_equal(platform_reward, 57741807) assert_equal(locked, self.get_credit_pool_balance()) self.generate(node, 1) locked += platform_reward diff --git a/test/functional/interface_zmq_dash.py b/test/functional/interface_zmq_dash.py index fdec9a4744..86b7f8ef7d 100755 --- a/test/functional/interface_zmq_dash.py +++ b/test/functional/interface_zmq_dash.py @@ -14,7 +14,11 @@ from test_framework.test_framework import DashTestFramework from test_framework.p2p import P2PInterface -from test_framework.util import assert_equal, assert_raises_rpc_error +from test_framework.util import ( + assert_equal, + assert_raises_rpc_error, + p2p_port, +) from test_framework.messages import ( CBlock, CGovernanceObject, @@ -99,16 +103,20 @@ def on_getdata(self, message): class DashZMQTest (DashTestFramework): def set_test_params(self): + self.set_dash_test_params(5, 4) + # That's where the zmq publisher will listen for subscriber - self.address = "tcp://127.0.0.1:28331" + self.zmq_port_base = p2p_port(self.num_nodes + 1) + self.address = f"tcp://127.0.0.1:{self.zmq_port_base}" + # node0 creates all available ZMQ publisher - node0_extra_args = ["-zmqpub%s=%s" % (pub.value, self.address) for pub in ZMQPublisher] + node0_extra_args = [f"-zmqpub{pub.value}={self.address}" for pub in ZMQPublisher] node0_extra_args.append("-whitelist=127.0.0.1") node0_extra_args.append("-watchquorums") # have to watch quorums to receive recsigs and trigger zmq - extra_args = [[]] * 5 - extra_args[0] = node0_extra_args - self.set_dash_test_params(5, 4, extra_args=extra_args) + #extra_args = [node0_extra_args, [], [], [], []] + self.extra_args[0] = node0_extra_args + self.set_dash_llmq_test_params(4, 4) def skip_test_if_missing_module(self): diff --git a/test/functional/test_framework/messages.py b/test/functional/test_framework/messages.py index d2f96e1efd..ac0cb3faa2 100755 --- a/test/functional/test_framework/messages.py +++ b/test/functional/test_framework/messages.py @@ -1069,9 +1069,15 @@ def __repr__(self): class CMerkleBlock: __slots__ = ("header", "txn") - def __init__(self, header=CBlockHeader(), txn=CPartialMerkleTree()): - self.header = header - self.txn = txn + def __init__(self, header=None, txn=None): + if header is None: + self.header = CBlockHeader() + else: + self.header = header + if txn is None: + self.txn = CPartialMerkleTree() + else: + self.txn = txn def deserialize(self, f): self.header.deserialize(f) diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index e87d8af957..8da7df9f1e 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -93,8 +93,6 @@ BASE_SCRIPTS = [ # Scripts that are run by default. # Longest test should go first, to favor running tests in parallel - 'feature_dip3_deterministicmns.py --legacy-wallet', # NOTE: needs dash_hash to pass - 'feature_dip3_deterministicmns.py --descriptors', # NOTE: needs dash_hash to pass 'feature_llmq_data_recovery.py', 'wallet_hd.py --legacy-wallet', 'wallet_hd.py --descriptors', @@ -122,23 +120,27 @@ 'wallet_dump.py --legacy-wallet', 'feature_multikeysporks.py', 'feature_dip3_v19.py', + 'feature_asset_locks.py', # NOTE: needs dash_hash to pass + 'feature_llmq_connections.py', # NOTE: needs dash_hash to pass + 'feature_llmq_is_retroactive.py', # NOTE: needs dash_hash to pass + 'feature_llmq_chainlocks.py', # NOTE: needs dash_hash to pass + 'feature_llmq_simplepose.py', # NOTE: needs dash_hash to pass + 'feature_dip3_deterministicmns.py --legacy-wallet', # NOTE: needs dash_hash to pass + 'feature_dip3_deterministicmns.py --descriptors', # NOTE: needs dash_hash to pass 'feature_llmq_signing.py', # NOTE: needs dash_hash to pass 'feature_llmq_signing.py --spork21', # NOTE: needs dash_hash to pass - 'feature_llmq_chainlocks.py', # NOTE: needs dash_hash to pass 'feature_llmq_rotation.py', # NOTE: needs dash_hash to pass - 'feature_llmq_connections.py', # NOTE: needs dash_hash to pass 'feature_llmq_evo.py', # NOTE: needs dash_hash to pass - 'feature_llmq_simplepose.py', # NOTE: needs dash_hash to pass 'feature_llmq_is_cl_conflicts.py', # NOTE: needs dash_hash to pass - 'feature_llmq_is_retroactive.py', # NOTE: needs dash_hash to pass 'feature_llmq_dkgerrors.py', # NOTE: needs dash_hash to pass 'feature_dip4_coinbasemerkleroots.py', # NOTE: needs dash_hash to pass - 'feature_asset_locks.py', # NOTE: needs dash_hash to pass 'feature_mnehf.py', # NOTE: needs dash_hash to pass 'feature_governance.py --legacy-wallet', 'feature_governance.py --descriptors', 'feature_governance_cl.py --legacy-wallet', 'feature_governance_cl.py --descriptors', + 'rpc_verifyislock.py', + 'feature_notifications.py', # vv Tests less than 60s vv 'p2p_sendheaders.py', # NOTE: needs dash_hash to pass 'p2p_sendheaders_compressed.py', # NOTE: needs dash_hash to pass @@ -153,8 +155,7 @@ 'feature_reindex.py', 'feature_abortnode.py', # vv Tests less than 30s vv - 'rpc_quorum.py --legacy-wallet', - 'rpc_quorum.py --descriptors', + 'rpc_quorum.py', 'wallet_keypool_topup.py --legacy-wallet', 'wallet_keypool_topup.py --descriptors', 'feature_fee_estimation.py', @@ -253,7 +254,6 @@ 'feature_backwards_compatibility.py --legacy-wallet', 'feature_backwards_compatibility.py --descriptors', 'wallet_txn_clone.py --mineblock', - 'feature_notifications.py', 'rpc_getblockfilter.py', 'rpc_getblockfrompeer.py', 'rpc_invalidateblock.py', @@ -331,7 +331,6 @@ 'rpc_coinjoin.py', 'rpc_masternode.py', 'rpc_mnauth.py', - 'rpc_verifyislock.py', 'rpc_verifychainlock.py', 'wallet_create_tx.py --legacy-wallet', 'wallet_send.py --legacy-wallet', diff --git a/test/functional/wallet_send.py b/test/functional/wallet_send.py index 033d38c191..acfab19439 100755 --- a/test/functional/wallet_send.py +++ b/test/functional/wallet_send.py @@ -189,9 +189,7 @@ def run_test(self): "internal": True }]) else: - w2.upgradetohd() - # TODO: replace upgradetohd to sethdseed when it is implemented - # w2.sethdseed(True) + w2.sethdseed(True) # w3 is a watch-only wallet, based on w2 self.nodes[1].createwallet(wallet_name="w3", disable_private_keys=True)