diff --git a/depends/packages/dbus.mk b/depends/packages/dbus.mk deleted file mode 100644 index ad10b0fdd7fb..000000000000 --- a/depends/packages/dbus.mk +++ /dev/null @@ -1,27 +0,0 @@ -package=dbus -$(package)_version=1.10.18 -$(package)_download_path=https://dbus.freedesktop.org/releases/dbus -$(package)_file_name=$(package)-$($(package)_version).tar.gz -$(package)_sha256_hash=6049ddd5f3f3e2618f615f1faeda0a115104423a7996b7aa73e2f36e38cc514a -$(package)_dependencies=expat - -define $(package)_set_vars - $(package)_config_opts=--disable-tests --disable-doxygen-docs --disable-xml-docs --disable-shared --without-x -endef - -define $(package)_config_cmds - $($(package)_autoconf) -endef - -define $(package)_build_cmds - $(MAKE) -C dbus libdbus-1.la -endef - -define $(package)_stage_cmds - $(MAKE) -C dbus DESTDIR=$($(package)_staging_dir) install-libLTLIBRARIES install-dbusincludeHEADERS install-nodist_dbusarchincludeHEADERS && \ - $(MAKE) DESTDIR=$($(package)_staging_dir) install-pkgconfigDATA -endef - -define $(package)_postprocess_cmds - rm lib/*.la -endef diff --git a/depends/packages/packages.mk b/depends/packages/packages.mk index 1b9ecd32cb6d..3452a9359c96 100644 --- a/depends/packages/packages.mk +++ b/depends/packages/packages.mk @@ -1,6 +1,6 @@ packages:=boost libevent gmp backtrace -qt_linux_packages:=qt expat dbus libxcb xcb_proto libXau xproto freetype fontconfig libxkbcommon +qt_linux_packages:=qt expat libxcb xcb_proto libXau xproto freetype fontconfig libxkbcommon qrencode_linux_packages = qrencode qrencode_android_packages = qrencode diff --git a/doc/dependencies.md b/doc/dependencies.md index cc74b0f4edc7..8869b93fee97 100644 --- a/doc/dependencies.md +++ b/doc/dependencies.md @@ -8,7 +8,6 @@ These are the dependencies currently used by Dash Core. You can find instruction | Berkeley DB | [4.8.30](https://www.oracle.com/technetwork/database/database-technologies/berkeleydb/downloads/index.html) | 4.8.x | No | | | | Boost | [1.73.0](https://www.boost.org/users/download/) | [1.64.0](https://github.com/bitcoin/bitcoin/pull/22320) | No | | | | Clang[ \* ](#note1) | | [5.0+](https://releases.llvm.org/download.html) (C++17 support) | | | | -| D-Bus | [1.10.18](https://cgit.freedesktop.org/dbus/dbus/tree/NEWS?h=dbus-1.10) | | No | Yes | | | Expat | [2.2.7](https://libexpat.github.io/) | | No | Yes | | | fontconfig | [2.12.1](https://www.freedesktop.org/software/fontconfig/release/) | | No | Yes | | | FreeType | [2.7.1](https://download.savannah.gnu.org/releases/freetype) | | No | | [Yes](https://github.com/dashpay/dash/blob/develop/depends/packages/qt.mk) (Android only) | diff --git a/doc/release-notes-5273.md b/doc/release-notes-5273.md new file mode 100644 index 000000000000..16bd0c88dcef --- /dev/null +++ b/doc/release-notes-5273.md @@ -0,0 +1,5 @@ +Added RPCs +-------- + +- `cleardiscouraged` clears all the already discouraged peers. + diff --git a/src/Makefile.bench.include b/src/Makefile.bench.include index db47baaffaf5..6171a0381a54 100644 --- a/src/Makefile.bench.include +++ b/src/Makefile.bench.include @@ -39,6 +39,7 @@ bench_bench_dash_SOURCES = \ bench/mempool_stress.cpp \ bench/nanobench.h \ bench/nanobench.cpp \ + bench/rpc_blockchain.cpp \ bench/rpc_mempool.cpp \ bench/util_time.cpp \ bench/base58.cpp \ diff --git a/src/banman.cpp b/src/banman.cpp index 48973e9c4a97..807ad19faa0a 100644 --- a/src/banman.cpp +++ b/src/banman.cpp @@ -68,6 +68,16 @@ void BanMan::ClearBanned() if (m_client_interface) m_client_interface->BannedListChanged(); } +void BanMan::ClearDiscouraged() +{ + { + LOCK(m_cs_banned); + m_discouraged.reset(); + m_is_dirty = true; + } + if (m_client_interface) m_client_interface->BannedListChanged(); +} + bool BanMan::IsDiscouraged(const CNetAddr& net_addr) { LOCK(m_cs_banned); diff --git a/src/banman.h b/src/banman.h index 6b972c84e28e..ca46677e3ee4 100644 --- a/src/banman.h +++ b/src/banman.h @@ -63,6 +63,7 @@ class BanMan void Ban(const CSubNet& sub_net, int64_t ban_time_offset = 0, bool since_unix_epoch = false); void Discourage(const CNetAddr& net_addr); void ClearBanned(); + void ClearDiscouraged(); //! Return whether net_addr is banned bool IsBanned(const CNetAddr& net_addr); diff --git a/src/bench/block_assemble.cpp b/src/bench/block_assemble.cpp index 222f57c8c214..1afedc9963f7 100644 --- a/src/bench/block_assemble.cpp +++ b/src/bench/block_assemble.cpp @@ -41,7 +41,7 @@ static void AssembleBlock(benchmark::Bench& bench) for (const auto& txr : txs) { CValidationState state; - bool ret{::AcceptToMemoryPool(*test_setup.m_node.mempool, state, txr, nullptr /* pfMissingInputs */, false /* bypass_limits */, /* nAbsurdFee */ 0)}; + bool ret{::AcceptToMemoryPool(::ChainstateActive(), *test_setup.m_node.mempool, state, txr, nullptr /* pfMissingInputs */, false /* bypass_limits */, /* nAbsurdFee */ 0)}; assert(ret); } } diff --git a/src/bench/rpc_blockchain.cpp b/src/bench/rpc_blockchain.cpp new file mode 100644 index 000000000000..3cf8947b9964 --- /dev/null +++ b/src/bench/rpc_blockchain.cpp @@ -0,0 +1,35 @@ +// Copyright (c) 2016-2019 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +static void BlockToJsonVerbose(benchmark::Bench& bench) { + CDataStream stream(benchmark::data::block813851, SER_NETWORK, PROTOCOL_VERSION); + char a = '\0'; + stream.write(&a, 1); // Prevent compaction + + CBlock block; + stream >> block; + + CBlockIndex blockindex; + const uint256 blockHash = block.GetHash(); + blockindex.phashBlock = &blockHash; + blockindex.nBits = 403014710; + + bench.run([&] { + (void)blockToJSON(block, &blockindex, &blockindex, *llmq::chainLocksHandler, *llmq::quorumInstantSendManager, /*verbose*/ true); + }); +} + +BENCHMARK(BlockToJsonVerbose); diff --git a/src/coinjoin/coinjoin.cpp b/src/coinjoin/coinjoin.cpp index 92acfa664cab..c18c72d148a5 100644 --- a/src/coinjoin/coinjoin.cpp +++ b/src/coinjoin/coinjoin.cpp @@ -352,7 +352,7 @@ bool CCoinJoin::IsCollateralValid(CTxMemPool& mempool, const CTransaction& txCol { LOCK(cs_main); CValidationState validationState; - if (!AcceptToMemoryPool(mempool, validationState, MakeTransactionRef(txCollateral), /*pfMissingInputs=*/nullptr, /*bypass_limits=*/false, /*nAbsurdFee=*/DEFAULT_MAX_RAW_TX_FEE, /*test_accept=*/true)) { + if (!AcceptToMemoryPool(::ChainstateActive(), mempool, validationState, MakeTransactionRef(txCollateral), /*pfMissingInputs=*/nullptr, /*bypass_limits=*/false, /*nAbsurdFee=*/DEFAULT_MAX_RAW_TX_FEE, /*test_accept=*/true)) { LogPrint(BCLog::COINJOIN, "CCoinJoin::IsCollateralValid -- didn't pass AcceptToMemoryPool()\n"); return false; } diff --git a/src/coinjoin/server.cpp b/src/coinjoin/server.cpp index 52dc6fea226c..1ebcc36b35bf 100644 --- a/src/coinjoin/server.cpp +++ b/src/coinjoin/server.cpp @@ -322,7 +322,7 @@ void CCoinJoinServer::CommitFinalTransaction() TRY_LOCK(cs_main, lockMain); CValidationState validationState; mempool.PrioritiseTransaction(hashTx, 0.1 * COIN); - if (!lockMain || !AcceptToMemoryPool(mempool, validationState, finalTransaction, nullptr /* pfMissingInputs */, false /* bypass_limits */, DEFAULT_MAX_RAW_TX_FEE /* nAbsurdFee */)) { + if (!lockMain || !AcceptToMemoryPool(::ChainstateActive(), mempool, validationState, finalTransaction, nullptr /* pfMissingInputs */, false /* bypass_limits */, DEFAULT_MAX_RAW_TX_FEE /* nAbsurdFee */)) { LogPrint(BCLog::COINJOIN, "CCoinJoinServer::CommitFinalTransaction -- AcceptToMemoryPool() error: Transaction not valid\n"); WITH_LOCK(cs_coinjoin, SetNull()); // not much we can do in this case, just notify clients @@ -455,7 +455,7 @@ void CCoinJoinServer::ConsumeCollateral(const CTransactionRef& txref) const { LOCK(cs_main); CValidationState validationState; - if (!AcceptToMemoryPool(mempool, validationState, txref, nullptr /* pfMissingInputs */, false /* bypass_limits */, 0 /* nAbsurdFee */)) { + if (!AcceptToMemoryPool(::ChainstateActive(), mempool, validationState, txref, nullptr /* pfMissingInputs */, false /* bypass_limits */, 0 /* nAbsurdFee */)) { LogPrint(BCLog::COINJOIN, "%s -- AcceptToMemoryPool failed\n", __func__); } else { connman.RelayTransaction(*txref); diff --git a/src/evo/deterministicmns.cpp b/src/evo/deterministicmns.cpp index 9905d351b542..fe266149ad9c 100644 --- a/src/evo/deterministicmns.cpp +++ b/src/evo/deterministicmns.cpp @@ -218,15 +218,35 @@ std::vector CDeterministicMNList::GetProjectedMNPayees(int if (nCount < 0 ) { return {}; } - nCount = std::min(nCount, int(GetValidMNsCount())); + nCount = std::min(nCount, int(GetValidWeightedMNsCount())); std::vector result; result.reserve(nCount); + auto remaining_hpmn_payments = 0; + CDeterministicMNCPtr hpmn_to_be_skipped = nullptr; ForEachMNShared(true, [&](const CDeterministicMNCPtr& dmn) { - result.emplace_back(dmn); + if (dmn->pdmnState->nLastPaidHeight == nHeight) { + // We found the last MN Payee. + // If the last payee is a HPMN, we need to check its consecutive payments and pay him again if needed + if (dmn->nType == MnType::HighPerformance && dmn->pdmnState->nConsecutivePayments < dmn_types::HighPerformance.voting_weight) { + remaining_hpmn_payments = dmn_types::HighPerformance.voting_weight - dmn->pdmnState->nConsecutivePayments; + for ([[maybe_unused]] auto _ : irange::range(remaining_hpmn_payments)) { + result.emplace_back(dmn); + hpmn_to_be_skipped = dmn; + } + } + } + return; + }); + + ForEachMNShared(true, [&](const CDeterministicMNCPtr& dmn) { + if (dmn == hpmn_to_be_skipped) return; + for ([[maybe_unused]] auto _ : irange::range(GetMnType(dmn->nType).voting_weight)) { + result.emplace_back(dmn); + } }); - std::sort(result.begin(), result.end(), [&](const CDeterministicMNCPtr& a, const CDeterministicMNCPtr& b) { + std::sort(result.begin() + remaining_hpmn_payments, result.end(), [&](const CDeterministicMNCPtr& a, const CDeterministicMNCPtr& b) { return CompareByLastPaid(a.get(), b.get()); }); diff --git a/src/evo/deterministicmns.h b/src/evo/deterministicmns.h index bd7f74d15fa7..108cbbc75f7d 100644 --- a/src/evo/deterministicmns.h +++ b/src/evo/deterministicmns.h @@ -19,6 +19,7 @@ #include +#include #include #include @@ -241,6 +242,14 @@ class CDeterministicMNList return ranges::count_if(mnMap, [](const auto& p) { return p.second->nType == MnType::HighPerformance && IsMNValid(*p.second); }); } + [[nodiscard]] size_t GetValidWeightedMNsCount() const + { + return std::accumulate(mnMap.begin(), mnMap.end(), 0, [](auto res, const auto& p) { + if (!IsMNValid(*p.second)) return res; + return res + GetMnType(p.second->nType).voting_weight; + }); + } + /** * Execute a callback on all masternodes in the mnList. This will pass a reference * of each masternode to the callback function. This should be preferred over ForEachMNShared. diff --git a/src/evo/mnauth.cpp b/src/evo/mnauth.cpp index d7527a55cb8d..d0122bf9fd43 100644 --- a/src/evo/mnauth.cpp +++ b/src/evo/mnauth.cpp @@ -87,6 +87,7 @@ void CMNAuth::ProcessMessage(CNode& peer, CConnman& connman, std::string_view ms if (!mnauth.sig.IsValid()) { Misbehaving(peer.GetId(), 100, "invalid mnauth signature"); + LogPrint(BCLog::NET_NETCONN, "CMNAuth::ProcessMessage -- invalid mnauth for protx=%s with sig=%s\n", mnauth.proRegTxHash.ToString(), mnauth.sig.ToString()); return; } diff --git a/src/evo/mnhftx.cpp b/src/evo/mnhftx.cpp index d3b86dc4beed..81b931016730 100644 --- a/src/evo/mnhftx.cpp +++ b/src/evo/mnhftx.cpp @@ -45,7 +45,7 @@ bool CheckMNHFTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CValidat return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-mnhf-version"); } - const CBlockIndex* pindexQuorum = LookupBlockIndex(mnhfTx.signal.quorumHash); + const CBlockIndex* pindexQuorum = g_chainman.m_blockman.LookupBlockIndex(mnhfTx.signal.quorumHash); if (!pindexQuorum) { return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-mnhf-quorum-hash"); } diff --git a/src/evo/simplifiedmns.cpp b/src/evo/simplifiedmns.cpp index 202396047e3f..f1f378e58f0b 100644 --- a/src/evo/simplifiedmns.cpp +++ b/src/evo/simplifiedmns.cpp @@ -243,14 +243,14 @@ bool BuildSimplifiedMNListDiff(const uint256& baseBlockHash, const uint256& bloc const CBlockIndex* baseBlockIndex = ::ChainActive().Genesis(); if (!baseBlockHash.IsNull()) { - baseBlockIndex = LookupBlockIndex(baseBlockHash); + baseBlockIndex = g_chainman.m_blockman.LookupBlockIndex(baseBlockHash); if (!baseBlockIndex) { errorRet = strprintf("block %s not found", baseBlockHash.ToString()); return false; } } - const CBlockIndex* blockIndex = LookupBlockIndex(blockHash); + const CBlockIndex* blockIndex = g_chainman.m_blockman.LookupBlockIndex(blockHash); if (!blockIndex) { errorRet = strprintf("block %s not found", blockHash.ToString()); return false; diff --git a/src/evo/specialtxman.cpp b/src/evo/specialtxman.cpp index 7a85c790bbbf..54dd69e63c89 100644 --- a/src/evo/specialtxman.cpp +++ b/src/evo/specialtxman.cpp @@ -42,7 +42,7 @@ bool CheckSpecialTx(const CTransaction& tx, const CBlockIndex* pindexPrev, CVali case TRANSACTION_QUORUM_COMMITMENT: return llmq::CheckLLMQCommitment(tx, pindexPrev, state); case TRANSACTION_MNHF_SIGNAL: - return VersionBitsTipState(Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0024) == ThresholdState::ACTIVE && CheckMNHFTx(tx, pindexPrev, state); + return VersionBitsState(::ChainActive().Tip(), Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0024, versionbitscache) == ThresholdState::ACTIVE && CheckMNHFTx(tx, pindexPrev, state); } } catch (const std::exception& e) { LogPrintf("%s -- failed: %s\n", __func__, e.what()); diff --git a/src/governance/classes.cpp b/src/governance/classes.cpp index eb0b75c4eedb..c4e305165cb1 100644 --- a/src/governance/classes.cpp +++ b/src/governance/classes.cpp @@ -525,7 +525,7 @@ void CSuperblock::ParsePaymentSchedule(const std::string& strPaymentAddresses, c // TODO: script addresses limit here and cs_main lock in // CGovernanceManager::InitOnLoad()once DIP0024 is active - bool fAllowScript = (VersionBitsTipState(Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0024) == ThresholdState::ACTIVE); + bool fAllowScript = (VersionBitsState(::ChainActive().Tip(), Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0024, versionbitscache) == ThresholdState::ACTIVE); for (int i = 0; i < (int)vecParsed1.size(); i++) { CTxDestination dest = DecodeDestination(vecParsed1[i]); if (!IsValidDestination(dest)) { diff --git a/src/governance/governance.cpp b/src/governance/governance.cpp index b584f4a0de18..d4c0b37336fb 100644 --- a/src/governance/governance.cpp +++ b/src/governance/governance.cpp @@ -395,7 +395,7 @@ void CGovernanceManager::UpdateCachesAndClean() } else { // NOTE: triggers are handled via triggerman if (pObj->GetObjectType() == GOVERNANCE_OBJECT_PROPOSAL) { - bool fAllowScript = (VersionBitsTipState(Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0024) == ThresholdState::ACTIVE); + bool fAllowScript = (VersionBitsState(::ChainActive().Tip(), Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0024, versionbitscache) == ThresholdState::ACTIVE); CProposalValidator validator(pObj->GetDataAsHexString(), fAllowScript); if (!validator.Validate()) { LogPrint(BCLog::GOBJECT, "CGovernanceManager::UpdateCachesAndClean -- set for deletion expired obj %s\n", strHash); @@ -635,7 +635,7 @@ void CGovernanceManager::SyncObjects(CNode& peer, CConnman& connman) const LogPrint(BCLog::GOBJECT, "CGovernanceManager::%s -- syncing all objects to peer=%d\n", __func__, peer.GetId()); - bool fAllowScript = WITH_LOCK(cs_main, return VersionBitsTipState(Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0024) == ThresholdState::ACTIVE); + bool fAllowScript = WITH_LOCK(cs_main, return VersionBitsState(::ChainActive().Tip(), Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0024, versionbitscache) == ThresholdState::ACTIVE); LOCK(cs); diff --git a/src/governance/object.cpp b/src/governance/object.cpp index 61b104335592..8b267e924590 100644 --- a/src/governance/object.cpp +++ b/src/governance/object.cpp @@ -469,7 +469,7 @@ bool CGovernanceObject::IsValidLocally(std::string& strError, bool& fMissingConf switch (nObjectType) { case GOVERNANCE_OBJECT_PROPOSAL: { - bool fAllowScript = (VersionBitsTipState(Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0024) == ThresholdState::ACTIVE); + bool fAllowScript = (VersionBitsState(::ChainActive().Tip(), Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0024, versionbitscache) == ThresholdState::ACTIVE); CProposalValidator validator(GetDataAsHexString(), fAllowScript); // Note: It's ok to have expired proposals // they are going to be cleared by CGovernanceManager::UpdateCachesAndClean() @@ -563,7 +563,7 @@ bool CGovernanceObject::IsCollateralValid(std::string& strError, bool& fMissingC findScript << OP_RETURN << ToByteVector(nExpectedHash); AssertLockHeld(cs_main); - bool fork_active = VersionBitsState(LookupBlockIndex(nBlockHash), Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0024, versionbitscache) == ThresholdState::ACTIVE; + bool fork_active = VersionBitsState(g_chainman.m_blockman.LookupBlockIndex(nBlockHash), Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0024, versionbitscache) == ThresholdState::ACTIVE; CAmount nMinFee = GetMinCollateralFee(fork_active); LogPrint(BCLog::GOBJECT, "CGovernanceObject::IsCollateralValid -- txCollateral->vout.size() = %s, findScript = %s, nMinFee = %lld\n", @@ -594,7 +594,7 @@ bool CGovernanceObject::IsCollateralValid(std::string& strError, bool& fMissingC AssertLockHeld(cs_main); int nConfirmationsIn = 0; if (nBlockHash != uint256()) { - const CBlockIndex* pindex = LookupBlockIndex(nBlockHash); + const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(nBlockHash); if (pindex && ::ChainActive().Contains(pindex)) { nConfirmationsIn += ::ChainActive().Height() - pindex->nHeight + 1; } @@ -692,7 +692,7 @@ void CGovernanceObject::Relay(CConnman& connman) const // But we don't want to relay it to pre-GOVSCRIPT_PROTO_VERSION peers if payment_address is p2sh // because they won't accept it anyway and will simply ban us eventually. LOCK(cs_main); - bool fAllowScript = (VersionBitsTipState(Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0024) == ThresholdState::ACTIVE); + bool fAllowScript = (VersionBitsState(::ChainActive().Tip(), Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0024, versionbitscache) == ThresholdState::ACTIVE); if (fAllowScript) { CProposalValidator validator(GetDataAsHexString(), false /* no script */); if (!validator.Validate(false /* ignore expiration */)) { diff --git a/src/index/base.cpp b/src/index/base.cpp index 2eaacc1f6eeb..c8fd962a186c 100644 --- a/src/index/base.cpp +++ b/src/index/base.cpp @@ -61,7 +61,7 @@ bool BaseIndex::Init() if (locator.IsNull()) { m_best_block_index = nullptr; } else { - m_best_block_index = FindForkInGlobalIndex(::ChainActive(), locator); + m_best_block_index = g_chainman.m_blockman.FindForkInGlobalIndex(::ChainActive(), locator); } m_synced = m_best_block_index.load() == ::ChainActive().Tip(); return true; @@ -238,7 +238,7 @@ void BaseIndex::ChainStateFlushed(const CBlockLocator& locator) const CBlockIndex* locator_tip_index; { LOCK(cs_main); - locator_tip_index = LookupBlockIndex(locator_tip_hash); + locator_tip_index = g_chainman.m_blockman.LookupBlockIndex(locator_tip_hash); } if (!locator_tip_index) { diff --git a/src/init.cpp b/src/init.cpp index 1a9dce565461..f2888ae7bbb9 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -893,7 +893,7 @@ static void ThreadImport(ChainstateManager& chainman, std::vector vImp if (!file) break; // This error is logged in OpenBlockFile LogPrintf("Reindexing block file blk%05u.dat...\n", (unsigned int)nFile); - LoadExternalBlockFile(chainparams, file, &pos); + ::ChainstateActive().LoadExternalBlockFile(chainparams, file, &pos); if (ShutdownRequested()) { LogPrintf("Shutdown requested. Exit %s\n", __func__); return; @@ -904,7 +904,7 @@ static void ThreadImport(ChainstateManager& chainman, std::vector vImp fReindex = false; LogPrintf("Reindexing finished\n"); // To avoid ending up in a situation without genesis block, re-try initializing (no-op if reindexing worked): - LoadGenesisBlock(chainparams); + ::ChainstateActive().LoadGenesisBlock(chainparams); } // -loadblock= @@ -912,7 +912,7 @@ static void ThreadImport(ChainstateManager& chainman, std::vector vImp FILE *file = fsbridge::fopen(path, "rb"); if (file) { LogPrintf("Importing blocks file %s...\n", path.string()); - LoadExternalBlockFile(chainparams, file); + ::ChainstateActive().LoadExternalBlockFile(chainparams, file); if (ShutdownRequested()) { LogPrintf("Shutdown requested. Exit %s\n", __func__); return; @@ -2015,7 +2015,7 @@ bool AppInitMain(const CoreContext& context, NodeContext& node, interfaces::Bloc chainman.m_total_coinstip_cache = nCoinCacheUsage; chainman.m_total_coinsdb_cache = nCoinDBCache; - UnloadBlockIndex(node.mempool.get()); + UnloadBlockIndex(node.mempool.get(), chainman); // new CBlockTreeDB tries to delete the existing file, which // fails if it's still open from the previous loop. Close it first: @@ -2056,12 +2056,12 @@ bool AppInitMain(const CoreContext& context, NodeContext& node, interfaces::Bloc // If the loaded chain has a wrong genesis, bail out immediately // (we're likely using a testnet datadir, or the other way around). if (!chainman.BlockIndex().empty() && - !LookupBlockIndex(chainparams.GetConsensus().hashGenesisBlock)) { + !g_chainman.m_blockman.LookupBlockIndex(chainparams.GetConsensus().hashGenesisBlock)) { return InitError(_("Incorrect or no genesis block found. Wrong datadir for network?")); } if (!chainparams.GetConsensus().hashDevnetGenesisBlock.IsNull() && !chainman.BlockIndex().empty() && - !LookupBlockIndex(chainparams.GetConsensus().hashDevnetGenesisBlock)) { + !g_chainman.m_blockman.LookupBlockIndex(chainparams.GetConsensus().hashDevnetGenesisBlock)) { return InitError(_("Incorrect or no devnet genesis block found. Wrong datadir for devnet specified?")); } @@ -2094,7 +2094,7 @@ bool AppInitMain(const CoreContext& context, NodeContext& node, interfaces::Bloc // If we're not mid-reindex (based on disk + args), add a genesis block on disk // (otherwise we use the one already on disk). // This is called again in ThreadImport after the reindex completes. - if (!fReindex && !LoadGenesisBlock(chainparams)) { + if (!fReindex && !::ChainstateActive().LoadGenesisBlock(chainparams)) { strLoadError = _("Error initializing block database"); break; } @@ -2194,7 +2194,7 @@ bool AppInitMain(const CoreContext& context, NodeContext& node, interfaces::Bloc // work when we allow VerifyDB to be parameterized by chainstate. if (&::ChainstateActive() == chainstate && !CVerifyDB().VerifyDB( - chainparams, &chainstate->CoinsDB(), + chainparams, *chainstate, &chainstate->CoinsDB(), *node.evodb, args.GetArg("-checklevel", DEFAULT_CHECKLEVEL), args.GetArg("-checkblocks", DEFAULT_CHECKBLOCKS))) { @@ -2215,7 +2215,7 @@ bool AppInitMain(const CoreContext& context, NodeContext& node, interfaces::Bloc } if (args.GetArg("-checklevel", DEFAULT_CHECKLEVEL) >= 3) { - ResetBlockFailureFlags(nullptr); + ::ChainstateActive().ResetBlockFailureFlags(nullptr); } } } catch (const std::exception& e) { diff --git a/src/interfaces/chain.cpp b/src/interfaces/chain.cpp index 3d745d4928c8..e5684485640c 100644 --- a/src/interfaces/chain.cpp +++ b/src/interfaces/chain.cpp @@ -149,7 +149,7 @@ class ChainImpl : public Chain std::optional getBlockHeight(const uint256& hash) override { LOCK(::cs_main); - CBlockIndex* block = LookupBlockIndex(hash); + CBlockIndex* block = g_chainman.m_blockman.LookupBlockIndex(hash); if (block && ::ChainActive().Contains(block)) { return block->nHeight; } @@ -209,7 +209,7 @@ class ChainImpl : public Chain std::optional findFork(const uint256& hash, std::optional* height) override { LOCK(cs_main); - const CBlockIndex* block = LookupBlockIndex(hash); + const CBlockIndex* block = g_chainman.m_blockman.LookupBlockIndex(hash); const CBlockIndex* fork = block ? ::ChainActive().FindFork(block) : nullptr; if (height) { if (block) { @@ -231,7 +231,7 @@ class ChainImpl : public Chain std::optional findLocatorFork(const CBlockLocator& locator) override { LOCK(cs_main); - if (CBlockIndex* fork = FindForkInGlobalIndex(::ChainActive(), locator)) { + if (CBlockIndex* fork = g_chainman.m_blockman.FindForkInGlobalIndex(::ChainActive(), locator)) { return fork->nHeight; } return std::nullopt; @@ -239,14 +239,14 @@ class ChainImpl : public Chain bool checkFinalTx(const CTransaction& tx) override { LOCK(cs_main); - return CheckFinalTx(tx); + return CheckFinalTx(::ChainActive().Tip(), tx); } bool findBlock(const uint256& hash, CBlock* block, int64_t* time, int64_t* time_max) override { CBlockIndex* index; { LOCK(cs_main); - index = LookupBlockIndex(hash); + index = g_chainman.m_blockman.LookupBlockIndex(hash); if (!index) { return false; } @@ -266,7 +266,7 @@ class ChainImpl : public Chain double guessVerificationProgress(const uint256& block_hash) override { LOCK(cs_main); - return GuessVerificationProgress(Params().TxData(), LookupBlockIndex(block_hash)); + return GuessVerificationProgress(Params().TxData(), g_chainman.m_blockman.LookupBlockIndex(block_hash)); } bool hasDescendantsInMempool(const uint256& txid) override { diff --git a/src/llmq/blockprocessor.cpp b/src/llmq/blockprocessor.cpp index 14580500ff7b..0e5f4943991e 100644 --- a/src/llmq/blockprocessor.cpp +++ b/src/llmq/blockprocessor.cpp @@ -72,7 +72,7 @@ void CQuorumBlockProcessor::ProcessMessage(const CNode& peer, std::string_view m const CBlockIndex* pQuorumBaseBlockIndex; { LOCK(cs_main); - pQuorumBaseBlockIndex = LookupBlockIndex(qc.quorumHash); + pQuorumBaseBlockIndex = g_chainman.m_blockman.LookupBlockIndex(qc.quorumHash); if (pQuorumBaseBlockIndex == nullptr) { LogPrint(BCLog::LLMQ, "CQuorumBlockProcessor::%s -- unknown block %s in commitment, peer=%d\n", __func__, qc.quorumHash.ToString(), peer.GetId()); @@ -261,7 +261,7 @@ bool CQuorumBlockProcessor::ProcessCommitment(int nHeight, const uint256& blockH return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-qc-height"); } - const auto* pQuorumBaseBlockIndex = LookupBlockIndex(qc.quorumHash); + const auto* pQuorumBaseBlockIndex = g_chainman.m_blockman.LookupBlockIndex(qc.quorumHash); if (!qc.Verify(pQuorumBaseBlockIndex, fBLSChecks)) { LogPrint(BCLog::LLMQ, "CQuorumBlockProcessor::%s height=%d, type=%d, quorumIndex=%d, quorumHash=%s, signers=%s, validMembers=%d, quorumPublicKey=%s qc verify failed.\n", __func__, @@ -386,7 +386,7 @@ bool CQuorumBlockProcessor::UpgradeDB() if (qc.IsNull()) { continue; } - const auto* pQuorumBaseBlockIndex = LookupBlockIndex(qc.quorumHash); + const auto* pQuorumBaseBlockIndex = g_chainman.m_blockman.LookupBlockIndex(qc.quorumHash); m_evoDb.GetRawDB().Write(std::make_pair(DB_MINED_COMMITMENT, std::make_pair(qc.llmqType, qc.quorumHash)), std::make_pair(qc, pindex->GetBlockHash())); const auto& llmq_params_opt = GetLLMQParams(qc.llmqType); assert(llmq_params_opt.has_value()); diff --git a/src/llmq/chainlocks.cpp b/src/llmq/chainlocks.cpp index 3ba96ec118f7..746a36de4f80 100644 --- a/src/llmq/chainlocks.cpp +++ b/src/llmq/chainlocks.cpp @@ -130,7 +130,7 @@ void CChainLocksHandler::ProcessNewChainLock(const NodeId from, const llmq::CCha return; } - CBlockIndex* pindex = WITH_LOCK(cs_main, return LookupBlockIndex(clsig.getBlockHash())); + CBlockIndex* pindex = WITH_LOCK(cs_main, return g_chainman.m_blockman.LookupBlockIndex(clsig.getBlockHash())); { LOCK(cs); @@ -414,7 +414,7 @@ CChainLocksHandler::BlockTxs::mapped_type CChainLocksHandler::GetBlockTxs(const uint32_t blockTime; { LOCK(cs_main); - auto* pindex = LookupBlockIndex(blockHash); + auto* pindex = g_chainman.m_blockman.LookupBlockIndex(blockHash); CBlock block; if (!ReadBlockFromDisk(block, pindex, Params().GetConsensus())) { return nullptr; @@ -501,12 +501,12 @@ void CChainLocksHandler::EnforceBestChainLock() // For each of these blocks, check if there are children that are NOT part of the chain referenced by clsig // and mark all of them as conflicting. LogPrint(BCLog::CHAINLOCKS, "CChainLocksHandler::%s -- enforcing block %s via CLSIG (%s)\n", __func__, pindex->GetBlockHash().ToString(), clsig->ToString()); - EnforceBlock(state, params, pindex); + ::ChainstateActive().EnforceBlock(state, params, pindex); bool activateNeeded = WITH_LOCK(::cs_main, return ::ChainActive().Tip()->GetAncestor(currentBestChainLockBlockIndex->nHeight)) != currentBestChainLockBlockIndex; if (activateNeeded) { - if(!ActivateBestChain(state, params)) { + if (!::ChainstateActive().ActivateBestChain(state, params)) { LogPrintf("CChainLocksHandler::%s -- ActivateBestChain failed: %s\n", __func__, FormatStateMessage(state)); return; } @@ -638,7 +638,7 @@ void CChainLocksHandler::Cleanup() } for (auto it = blockTxs.begin(); it != blockTxs.end(); ) { - auto* pindex = LookupBlockIndex(it->first); + auto* pindex = g_chainman.m_blockman.LookupBlockIndex(it->first); if (InternalHasChainLock(pindex->nHeight, pindex->GetBlockHash())) { for (const auto& txid : *it->second) { txFirstSeenTime.erase(txid); @@ -657,7 +657,7 @@ void CChainLocksHandler::Cleanup() // tx has vanished, probably due to conflicts it = txFirstSeenTime.erase(it); } else if (!hashBlock.IsNull()) { - auto* pindex = LookupBlockIndex(hashBlock); + auto* pindex = g_chainman.m_blockman.LookupBlockIndex(hashBlock); if (::ChainActive().Tip()->GetAncestor(pindex->nHeight) == pindex && ::ChainActive().Height() - pindex->nHeight >= 6) { // tx got confirmed >= 6 times, so we can stop keeping track of it it = txFirstSeenTime.erase(it); diff --git a/src/llmq/commitment.cpp b/src/llmq/commitment.cpp index 91c1bb2db993..7d4718b02424 100644 --- a/src/llmq/commitment.cpp +++ b/src/llmq/commitment.cpp @@ -209,7 +209,7 @@ bool CheckLLMQCommitment(const CTransaction& tx, const CBlockIndex* pindexPrev, return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-qc-height"); } - const CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(cs_main, return LookupBlockIndex(qcTx.commitment.quorumHash)); + const CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(cs_main, return g_chainman.m_blockman.LookupBlockIndex(qcTx.commitment.quorumHash)); if (pQuorumBaseBlockIndex == nullptr) { return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, "bad-qc-quorum-hash"); } diff --git a/src/llmq/debug.cpp b/src/llmq/debug.cpp index 003b977e5acf..8f02c3d8d867 100644 --- a/src/llmq/debug.cpp +++ b/src/llmq/debug.cpp @@ -25,7 +25,7 @@ UniValue CDKGDebugSessionStatus::ToJson(int quorumIndex, int detailLevel) const std::vector dmnMembers; if (detailLevel == 2) { - const CBlockIndex* pindex = WITH_LOCK(cs_main, return LookupBlockIndex(quorumHash)); + const CBlockIndex* pindex = WITH_LOCK(cs_main, return g_chainman.m_blockman.LookupBlockIndex(quorumHash)); if (pindex != nullptr) { dmnMembers = utils::GetAllQuorumMembers(llmqType, pindex); } diff --git a/src/llmq/dkgsessionhandler.cpp b/src/llmq/dkgsessionhandler.cpp index db14b8bde9c2..e29b88c44ec3 100644 --- a/src/llmq/dkgsessionhandler.cpp +++ b/src/llmq/dkgsessionhandler.cpp @@ -491,7 +491,7 @@ void CDKGSessionHandler::HandleDKGRound() curQuorumHash = quorumHash; } - const CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(cs_main, return LookupBlockIndex(curQuorumHash)); + const CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(cs_main, return g_chainman.m_blockman.LookupBlockIndex(curQuorumHash)); if (!InitNewQuorum(pQuorumBaseBlockIndex)) { // should actually never happen diff --git a/src/llmq/dkgsessionmgr.cpp b/src/llmq/dkgsessionmgr.cpp index 943168992071..8cea880fa0b2 100644 --- a/src/llmq/dkgsessionmgr.cpp +++ b/src/llmq/dkgsessionmgr.cpp @@ -217,7 +217,7 @@ void CDKGSessionManager::ProcessMessage(CNode& pfrom, const CQuorumManager& quor // No luck, try to compute if (quorumIndex == -1) { - CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(cs_main, return LookupBlockIndex(quorumHash)); + CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(cs_main, return g_chainman.m_blockman.LookupBlockIndex(quorumHash)); if (pQuorumBaseBlockIndex == nullptr) { LogPrintf("CDKGSessionManager -- unknown quorumHash %s\n", quorumHash.ToString()); // NOTE: do not insta-ban for this, we might be lagging behind @@ -476,7 +476,7 @@ void CDKGSessionManager::CleanupOldContributions() const break; } cnt_all++; - const CBlockIndex* pindexQuorum = LookupBlockIndex(std::get<2>(k)); + const CBlockIndex* pindexQuorum = g_chainman.m_blockman.LookupBlockIndex(std::get<2>(k)); if (pindexQuorum == nullptr || ::ChainActive().Tip()->nHeight - pindexQuorum->nHeight > MAX_STORE_DEPTH) { // not found or too old batch.Erase(k); diff --git a/src/llmq/instantsend.cpp b/src/llmq/instantsend.cpp index 4d2540faec98..d39fb1db88ae 100644 --- a/src/llmq/instantsend.cpp +++ b/src/llmq/instantsend.cpp @@ -619,7 +619,7 @@ bool CInstantSendManager::CheckCanLock(const COutPoint& outpoint, bool printDebu int nTxAge; { LOCK(cs_main); - pindexMined = LookupBlockIndex(hashBlock); + pindexMined = g_chainman.m_blockman.LookupBlockIndex(hashBlock); nTxAge = ::ChainActive().Height() - pindexMined->nHeight + 1; } @@ -796,7 +796,7 @@ void CInstantSendManager::ProcessMessageInstantSendLock(const CNode& pfrom, cons // Deterministic ISLocks are only produced by rotation quorums, if we don't see DIP24 as active, then we won't be able to validate it anyway if (islock->IsDeterministic() && fDIP0024IsActive) { - const auto blockIndex = WITH_LOCK(cs_main, return LookupBlockIndex(islock->cycleHash)); + const auto blockIndex = WITH_LOCK(cs_main, return g_chainman.m_blockman.LookupBlockIndex(islock->cycleHash)); if (blockIndex == nullptr) { // Maybe we don't have the block yet or maybe some peer spams invalid values for cycleHash Misbehaving(pfrom.GetId(), 1); @@ -967,7 +967,7 @@ std::unordered_set CInstantSendManager::ProcessPend if (islock->IsDeterministic()) { LOCK(cs_main); - const auto blockIndex = LookupBlockIndex(islock->cycleHash); + const auto blockIndex = g_chainman.m_blockman.LookupBlockIndex(islock->cycleHash); if (blockIndex == nullptr) { batchVerifier.badSources.emplace(nodeId); continue; @@ -1061,7 +1061,7 @@ void CInstantSendManager::ProcessInstantSendLock(NodeId from, const uint256& has const CBlockIndex* pindexMined{nullptr}; // we ignore failure here as we must be able to propagate the lock even if we don't have the TX locally if (tx && !hashBlock.IsNull()) { - pindexMined = WITH_LOCK(cs_main, return LookupBlockIndex(hashBlock)); + pindexMined = WITH_LOCK(cs_main, return g_chainman.m_blockman.LookupBlockIndex(hashBlock)); // Let's see if the TX that was locked by this islock is already mined in a ChainLocked block. If yes, // we can simply ignore the islock, as the ChainLock implies locking of all TXs in that chain @@ -1479,8 +1479,8 @@ void CInstantSendManager::ResolveBlockConflicts(const uint256& islockHash, const CValidationState state; // need non-const pointer - auto pindex2 = WITH_LOCK(::cs_main, return LookupBlockIndex(pindex->GetBlockHash())); - if (!InvalidateBlock(state, Params(), pindex2)) { + auto pindex2 = WITH_LOCK(::cs_main, return g_chainman.m_blockman.LookupBlockIndex(pindex->GetBlockHash())); + if (!::ChainstateActive().InvalidateBlock(state, Params(), pindex2)) { LogPrintf("CInstantSendManager::%s -- InvalidateBlock failed: %s\n", __func__, FormatStateMessage(state)); // This should not have happened and we are in a state were it's not safe to continue anymore assert(false); @@ -1490,13 +1490,13 @@ void CInstantSendManager::ResolveBlockConflicts(const uint256& islockHash, const } else { LogPrintf("CInstantSendManager::%s -- resetting block %s\n", __func__, pindex2->GetBlockHash().ToString()); LOCK(cs_main); - ResetBlockFailureFlags(pindex2); + ::ChainstateActive().ResetBlockFailureFlags(pindex2); } } if (activateBestChain) { CValidationState state; - if (!ActivateBestChain(state, Params())) { + if (!::ChainstateActive().ActivateBestChain(state, Params())) { LogPrintf("CChainLocksHandler::%s -- ActivateBestChain failed: %s\n", __func__, FormatStateMessage(state)); // This should not have happened and we are in a state were it's not safe to continue anymore assert(false); diff --git a/src/llmq/quorums.cpp b/src/llmq/quorums.cpp index dedf7a4263dd..37de78c4c66e 100644 --- a/src/llmq/quorums.cpp +++ b/src/llmq/quorums.cpp @@ -283,7 +283,7 @@ void CQuorumManager::UpdatedBlockTip(const CBlockIndex* pindexNew, bool fInitial LOCK(cs_data_requests); auto it = mapQuorumDataRequests.begin(); while (it != mapQuorumDataRequests.end()) { - if (it->second.IsExpired()) { + if (it->second.IsExpired(/*add_bias=*/true)) { it = mapQuorumDataRequests.erase(it); } else { ++it; @@ -468,19 +468,22 @@ bool CQuorumManager::RequestQuorumData(CNode* pfrom, Consensus::LLMQType llmqTyp } LOCK(cs_data_requests); - CQuorumDataRequestKey key; - key.proRegTx = pfrom->GetVerifiedProRegTxHash(); - key.flag = true; - key.quorumHash = pQuorumBaseBlockIndex->GetBlockHash(); - key.llmqType = llmqType; - auto it = mapQuorumDataRequests.emplace(key, CQuorumDataRequest(llmqType, pQuorumBaseBlockIndex->GetBlockHash(), nDataMask, proTxHash)); - if (!it.second && !it.first->second.IsExpired()) { - LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- Already requested\n", __func__); - return false; + const CQuorumDataRequestKey key(pfrom->GetVerifiedProRegTxHash(), true, pQuorumBaseBlockIndex->GetBlockHash(), llmqType); + const CQuorumDataRequest request(llmqType, pQuorumBaseBlockIndex->GetBlockHash(), nDataMask, proTxHash); + auto [old_pair, exists] = mapQuorumDataRequests.emplace(key, request); + if (!exists) { + if (old_pair->second.IsExpired(/*add_bias=*/true)) { + old_pair->second = request; + } else { + LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- Already requested\n", __func__); + return false; + } } + LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- sending QGETDATA quorumHash[%s] llmqType[%d] proRegTx[%s]\n", __func__, key.quorumHash.ToString(), + ToUnderlying(key.llmqType), key.proRegTx.ToString()); CNetMsgMaker msgMaker(pfrom->GetSendVersion()); - connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::QGETDATA, it.first->second)); + connman.PushMessage(pfrom, msgMaker.Make(NetMsgType::QGETDATA, request)); return true; } @@ -557,7 +560,7 @@ std::vector CQuorumManager::ScanQuorums(Consensus::LLMQType llmqTyp CQuorumCPtr CQuorumManager::GetQuorum(Consensus::LLMQType llmqType, const uint256& quorumHash) const { - const CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(cs_main, return LookupBlockIndex(quorumHash)); + const CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(cs_main, return g_chainman.m_blockman.LookupBlockIndex(quorumHash)); if (!pQuorumBaseBlockIndex) { LogPrint(BCLog::LLMQ, "CQuorumManager::%s -- block %s not found\n", __func__, quorumHash.ToString()); return nullptr; @@ -629,44 +632,56 @@ void CQuorumManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, C CQuorumDataRequest request; vRecv >> request; - auto sendQDATA = [&](CQuorumDataRequest::Errors nError = CQuorumDataRequest::Errors::UNDEFINED, + auto sendQDATA = [&](CQuorumDataRequest::Errors nError, + bool request_limit_exceeded, const CDataStream& body = CDataStream(SER_NETWORK, PROTOCOL_VERSION)) { + switch (nError) { + case (CQuorumDataRequest::Errors::NONE): + case (CQuorumDataRequest::Errors::QUORUM_TYPE_INVALID): + case (CQuorumDataRequest::Errors::QUORUM_BLOCK_NOT_FOUND): + case (CQuorumDataRequest::Errors::QUORUM_NOT_FOUND): + case (CQuorumDataRequest::Errors::MASTERNODE_IS_NO_MEMBER): + case (CQuorumDataRequest::Errors::UNDEFINED): + if (request_limit_exceeded) errorHandler("Request limit exceeded", 25); + break; + case (CQuorumDataRequest::Errors::QUORUM_VERIFICATION_VECTOR_MISSING): + case (CQuorumDataRequest::Errors::ENCRYPTED_CONTRIBUTIONS_MISSING): + // Do not punish limit exceed if we don't have the requested data + break; + } request.SetError(nError); CDataStream ssResponse(SER_NETWORK, pfrom.GetSendVersion(), request, body); connman.PushMessage(&pfrom, CNetMsgMaker(pfrom.GetSendVersion()).Make(NetMsgType::QDATA, ssResponse)); }; + bool request_limit_exceeded(false); { LOCK2(cs_main, cs_data_requests); - CQuorumDataRequestKey key; - key.proRegTx = pfrom.GetVerifiedProRegTxHash(); - key.flag = false; - key.quorumHash = request.GetQuorumHash(); - key.llmqType = request.GetLLMQType(); + const CQuorumDataRequestKey key(pfrom.GetVerifiedProRegTxHash(), false, request.GetQuorumHash(), request.GetLLMQType()); auto it = mapQuorumDataRequests.find(key); if (it == mapQuorumDataRequests.end()) { it = mapQuorumDataRequests.emplace(key, request).first; - } else if (it->second.IsExpired()) { + } else if (it->second.IsExpired(/*add_bias=*/false)) { it->second = request; } else { - errorHandler("Request limit exceeded", 25); + request_limit_exceeded = true; } } if (!GetLLMQParams(request.GetLLMQType()).has_value()) { - sendQDATA(CQuorumDataRequest::Errors::QUORUM_TYPE_INVALID); + sendQDATA(CQuorumDataRequest::Errors::QUORUM_TYPE_INVALID, request_limit_exceeded); return; } - const CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(cs_main, return LookupBlockIndex(request.GetQuorumHash())); + const CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(cs_main, return g_chainman.m_blockman.LookupBlockIndex(request.GetQuorumHash())); if (pQuorumBaseBlockIndex == nullptr) { - sendQDATA(CQuorumDataRequest::Errors::QUORUM_BLOCK_NOT_FOUND); + sendQDATA(CQuorumDataRequest::Errors::QUORUM_BLOCK_NOT_FOUND, request_limit_exceeded); return; } const CQuorumCPtr pQuorum = GetQuorum(request.GetLLMQType(), pQuorumBaseBlockIndex); if (pQuorum == nullptr) { - sendQDATA(CQuorumDataRequest::Errors::QUORUM_NOT_FOUND); + sendQDATA(CQuorumDataRequest::Errors::QUORUM_NOT_FOUND, request_limit_exceeded); return; } @@ -675,7 +690,7 @@ void CQuorumManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, C // Check if request wants QUORUM_VERIFICATION_VECTOR data if (request.GetDataMask() & CQuorumDataRequest::QUORUM_VERIFICATION_VECTOR) { if (!pQuorum->HasVerificationVector()) { - sendQDATA(CQuorumDataRequest::Errors::QUORUM_VERIFICATION_VECTOR_MISSING); + sendQDATA(CQuorumDataRequest::Errors::QUORUM_VERIFICATION_VECTOR_MISSING, request_limit_exceeded); return; } @@ -687,20 +702,20 @@ void CQuorumManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, C int memberIdx = pQuorum->GetMemberIndex(request.GetProTxHash()); if (memberIdx == -1) { - sendQDATA(CQuorumDataRequest::Errors::MASTERNODE_IS_NO_MEMBER); + sendQDATA(CQuorumDataRequest::Errors::MASTERNODE_IS_NO_MEMBER, request_limit_exceeded); return; } std::vector> vecEncrypted; if (!dkgManager.GetEncryptedContributions(request.GetLLMQType(), pQuorumBaseBlockIndex, pQuorum->qc->validMembers, request.GetProTxHash(), vecEncrypted)) { - sendQDATA(CQuorumDataRequest::Errors::ENCRYPTED_CONTRIBUTIONS_MISSING); + sendQDATA(CQuorumDataRequest::Errors::ENCRYPTED_CONTRIBUTIONS_MISSING, request_limit_exceeded); return; } ssResponseData << vecEncrypted; } - sendQDATA(CQuorumDataRequest::Errors::NONE, ssResponseData); + sendQDATA(CQuorumDataRequest::Errors::NONE, request_limit_exceeded, ssResponseData); return; } @@ -715,11 +730,7 @@ void CQuorumManager::ProcessMessage(CNode& pfrom, const std::string& msg_type, C { LOCK2(cs_main, cs_data_requests); - CQuorumDataRequestKey key; - key.proRegTx = pfrom.GetVerifiedProRegTxHash(); - key.flag = true; - key.quorumHash = request.GetQuorumHash(); - key.llmqType = request.GetLLMQType(); + const CQuorumDataRequestKey key(pfrom.GetVerifiedProRegTxHash(), true, request.GetQuorumHash(), request.GetLLMQType()); auto it = mapQuorumDataRequests.find(key); if (it == mapQuorumDataRequests.end()) { errorHandler("Not requested"); @@ -895,13 +906,9 @@ void CQuorumManager::StartQuorumDataRecoveryThread(const CQuorumCPtr pQuorum, co pCurrentMemberHash = &vecMemberHashes[(nMyStartOffset + nTries++) % vecMemberHashes.size()]; { LOCK(cs_data_requests); - CQuorumDataRequestKey key; - key.proRegTx = *pCurrentMemberHash; - key.flag = true; - key.quorumHash = pQuorum->qc->quorumHash; - key.llmqType = pQuorum->qc->llmqType; + const CQuorumDataRequestKey key(*pCurrentMemberHash, true, pQuorum->qc->quorumHash, pQuorum->qc->llmqType); auto it = mapQuorumDataRequests.find(key); - if (it != mapQuorumDataRequests.end() && !it->second.IsExpired()) { + if (it != mapQuorumDataRequests.end() && !it->second.IsExpired(/*add_bias=*/true)) { printLog("Already asked"); continue; } @@ -925,11 +932,7 @@ void CQuorumManager::StartQuorumDataRecoveryThread(const CQuorumCPtr pQuorum, co printLog("Requested"); } else { LOCK(cs_data_requests); - CQuorumDataRequestKey key; - key.proRegTx = *pCurrentMemberHash; - key.flag = true; - key.quorumHash = pQuorum->qc->quorumHash; - key.llmqType = pQuorum->qc->llmqType; + const CQuorumDataRequestKey key(*pCurrentMemberHash, true, pQuorum->qc->quorumHash, pQuorum->qc->llmqType); auto it = mapQuorumDataRequests.find(key); if (it == mapQuorumDataRequests.end()) { printLog("Failed"); diff --git a/src/llmq/quorums.h b/src/llmq/quorums.h index 5e6d12dd8c06..c254393322a9 100644 --- a/src/llmq/quorums.h +++ b/src/llmq/quorums.h @@ -39,14 +39,20 @@ static constexpr bool DEFAULT_WATCH_QUORUMS{false}; struct CQuorumDataRequestKey { uint256 proRegTx; - //TODO: Investigate purpose of this flag and rename accordingly - bool flag; + bool m_we_requested; uint256 quorumHash; Consensus::LLMQType llmqType; + CQuorumDataRequestKey(const uint256& proRegTxIn, const bool _m_we_requested, const uint256& quorumHashIn, const Consensus::LLMQType llmqTypeIn) : + proRegTx(proRegTxIn), + m_we_requested(_m_we_requested), + quorumHash(quorumHashIn), + llmqType(llmqTypeIn) + {} + bool operator ==(const CQuorumDataRequestKey& obj) const { - return (proRegTx == obj.proRegTx && flag == obj.flag && quorumHash == obj.quorumHash && llmqType == obj.llmqType); + return (proRegTx == obj.proRegTx && m_we_requested == obj.m_we_requested && quorumHash == obj.quorumHash && llmqType == obj.llmqType); } }; @@ -83,6 +89,7 @@ class CQuorumDataRequest bool fProcessed{false}; static constexpr int64_t EXPIRATION_TIMEOUT{300}; + static constexpr int64_t EXPIRATION_BIAS{60}; public: @@ -119,7 +126,7 @@ class CQuorumDataRequest Errors GetError() const { return nError; } std::string GetErrorString() const; - bool IsExpired() const { return (GetTime() - nTime) >= EXPIRATION_TIMEOUT; } + bool IsExpired(bool add_bias) const { return (GetTime() - nTime) >= (EXPIRATION_TIMEOUT + (add_bias ? EXPIRATION_BIAS : 0)); } bool IsProcessed() const { return fProcessed; } void SetProcessed() { fProcessed = true; } @@ -274,7 +281,7 @@ struct SaltedHasherImpl { CSipHasher c(k0, k1); c.Write((unsigned char*)&(v.proRegTx), sizeof(v.proRegTx)); - c.Write((unsigned char*)&(v.flag), sizeof(v.flag)); + c.Write((unsigned char*)&(v.m_we_requested), sizeof(v.m_we_requested)); c.Write((unsigned char*)&(v.quorumHash), sizeof(v.quorumHash)); c.Write((unsigned char*)&(v.llmqType), sizeof(v.llmqType)); return c.Finalize(); diff --git a/src/llmq/snapshot.cpp b/src/llmq/snapshot.cpp index 76bedfc7f3ff..1b46a033fe61 100644 --- a/src/llmq/snapshot.cpp +++ b/src/llmq/snapshot.cpp @@ -132,7 +132,7 @@ bool BuildQuorumRotationInfo(const CGetQuorumRotationInfo& request, CQuorumRotat baseBlockIndexes.push_back(blockIndex); } else { for (const auto& blockHash : request.baseBlockHashes) { - const CBlockIndex* blockIndex = LookupBlockIndex(blockHash); + const CBlockIndex* blockIndex = g_chainman.m_blockman.LookupBlockIndex(blockHash); if (!blockIndex) { errorRet = strprintf("block %s not found", blockHash.ToString()); return false; @@ -158,7 +158,7 @@ bool BuildQuorumRotationInfo(const CGetQuorumRotationInfo& request, CQuorumRotat return false; } - const CBlockIndex* blockIndex = LookupBlockIndex(request.blockRequestHash); + const CBlockIndex* blockIndex = g_chainman.m_blockman.LookupBlockIndex(request.blockRequestHash); if (!blockIndex) { errorRet = strprintf("block not found"); return false; diff --git a/src/miner.cpp b/src/miner.cpp index 277f7155779f..3e4b4171ec52 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -108,7 +108,7 @@ void BlockAssembler::resetBlock() nFees = 0; } -std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& scriptPubKeyIn) +std::unique_ptr BlockAssembler::CreateNewBlock(CChainState& chainstate, const CScript& scriptPubKeyIn) { int64_t nTimeStart = GetTimeMicros(); @@ -126,8 +126,8 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc pblocktemplate->vTxSigOps.push_back(-1); // updated at end LOCK2(cs_main, m_mempool.cs); - - CBlockIndex* pindexPrev = ::ChainActive().Tip(); + assert(std::addressof(*::ChainActive().Tip()) == std::addressof(*chainstate.m_chain.Tip())); + CBlockIndex* pindexPrev = chainstate.m_chain.Tip(); assert(pindexPrev != nullptr); nHeight = pindexPrev->nHeight + 1; @@ -234,7 +234,8 @@ std::unique_ptr BlockAssembler::CreateNewBlock(const CScript& sc pblocktemplate->vTxSigOps[0] = GetLegacySigOpCount(*pblock->vtx[0]); CValidationState state; - if (!TestBlockValidity(state, m_clhandler, m_evoDb, chainparams, *pblock, pindexPrev, false, false)) { + assert(std::addressof(::ChainstateActive()) == std::addressof(chainstate)); + if (!TestBlockValidity(state, m_clhandler, m_evoDb, chainparams, chainstate, *pblock, pindexPrev, false, false)) { throw std::runtime_error(strprintf("%s: TestBlockValidity failed: %s", __func__, FormatStateMessage(state))); } int64_t nTime2 = GetTimeMicros(); diff --git a/src/miner.h b/src/miner.h index e44dbcdf0db7..097e1bb67b39 100644 --- a/src/miner.h +++ b/src/miner.h @@ -177,7 +177,7 @@ class BlockAssembler llmq::CInstantSendManager& isman, CEvoDB& evoDb, const CTxMemPool& mempool, const CChainParams& params, const Options& options); /** Construct a new block template with coinbase to scriptPubKeyIn */ - std::unique_ptr CreateNewBlock(const CScript& scriptPubKeyIn); + std::unique_ptr CreateNewBlock(CChainState& chainstate, const CScript& scriptPubKeyIn); inline static std::optional m_last_block_num_txs{}; inline static std::optional m_last_block_size{}; diff --git a/src/net.cpp b/src/net.cpp index 1dd7e4495f80..51bf6c3c0f20 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -2534,31 +2534,31 @@ void CConnman::ThreadOpenMasternodeConnections() } const auto& addr2 = dmn->pdmnState->addr; if (connectedNodes.count(addr2) && !connectedProRegTxHashes.count(proRegTxHash)) { - // we probably connected to it before it became a masternode - // or maybe we are still waiting for mnauth - (void)ForNode(addr2, [&](CNode* pnode) { - if (pnode->nTimeFirstMessageReceived != 0 && GetSystemTimeInSeconds() - pnode->nTimeFirstMessageReceived > 5) { - // clearly not expecting mnauth to take that long even if it wasn't the first message - // we received (as it should normally), disconnect - LogPrint(BCLog::NET_NETCONN, "CConnman::%s -- dropping non-mnauth connection to %s, service=%s\n", _func_, proRegTxHash.ToString(), addr2.ToString(false)); - pnode->fDisconnect = true; - return true; - } - return false; - }); - // either way - it's not ready, skip it for now - continue; - } - if (!connectedNodes.count(addr2) && !IsMasternodeOrDisconnectRequested(addr2) && !connectedProRegTxHashes.count(proRegTxHash)) { - int64_t lastAttempt = mmetaman.GetMetaInfo(dmn->proTxHash)->GetLastOutboundAttempt(); - // back off trying connecting to an address if we already tried recently - if (nANow - lastAttempt < chainParams.LLMQConnectionRetryTimeout()) { - continue; + // we probably connected to it before it became a masternode + // or maybe we are still waiting for mnauth + (void)ForNode(addr2, [&](CNode* pnode) { + if (pnode->nTimeFirstMessageReceived != 0 && GetSystemTimeInSeconds() - pnode->nTimeFirstMessageReceived > 5) { + // clearly not expecting mnauth to take that long even if it wasn't the first message + // we received (as it should normally), disconnect + LogPrint(BCLog::NET_NETCONN, "CConnman::%s -- dropping non-mnauth connection to %s, service=%s\n", _func_, proRegTxHash.ToString(), addr2.ToString(false)); + pnode->fDisconnect = true; + return true; } - ret.emplace_back(dmn); + return false; + }); + // either way - it's not ready, skip it for now + continue; + } + if (!connectedNodes.count(addr2) && !IsMasternodeOrDisconnectRequested(addr2) && !connectedProRegTxHashes.count(proRegTxHash)) { + int64_t lastAttempt = mmetaman.GetMetaInfo(dmn->proTxHash)->GetLastOutboundAttempt(); + // back off trying connecting to an address if we already tried recently + if (nANow - lastAttempt < chainParams.LLMQConnectionRetryTimeout()) { + continue; } + ret.emplace_back(dmn); } } + } return ret; }; diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 681ecf74dcfa..f6001796eb12 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -598,7 +598,7 @@ static void ProcessBlockAvailability(NodeId nodeid) EXCLUSIVE_LOCKS_REQUIRED(cs_ assert(state != nullptr); if (!state->hashLastUnknownBlock.IsNull()) { - const CBlockIndex* pindex = LookupBlockIndex(state->hashLastUnknownBlock); + const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(state->hashLastUnknownBlock); if (pindex && pindex->nChainWork > 0) { if (state->pindexBestKnownBlock == nullptr || pindex->nChainWork >= state->pindexBestKnownBlock->nChainWork) { state->pindexBestKnownBlock = pindex; @@ -615,7 +615,7 @@ static void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) EXCLUSIV ProcessBlockAvailability(nodeid); - const CBlockIndex* pindex = LookupBlockIndex(hash); + const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(hash); if (pindex && pindex->nChainWork > 0) { // An actually better block was announced. if (state->pindexBestKnownBlock == nullptr || pindex->nChainWork >= state->pindexBestKnownBlock->nChainWork) { @@ -1594,7 +1594,7 @@ bool static AlreadyHave(const CInv& inv, const CTxMemPool& mempool, const LLMQCo } case MSG_BLOCK: - return LookupBlockIndex(inv.hash) != nullptr; + return g_chainman.m_blockman.LookupBlockIndex(inv.hash) != nullptr; /* Dash Related Inventory Messages @@ -1696,7 +1696,7 @@ void static ProcessGetBlockData(CNode& pfrom, const CChainParams& chainparams, c bool need_activate_chain = false; { LOCK(cs_main); - const CBlockIndex* pindex = LookupBlockIndex(inv.hash); + const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(inv.hash); if (pindex) { if (pindex->HaveTxsDownloaded() && !pindex->IsValid(BLOCK_VALID_SCRIPTS) && pindex->IsValid(BLOCK_VALID_TREE)) { @@ -1711,13 +1711,13 @@ void static ProcessGetBlockData(CNode& pfrom, const CChainParams& chainparams, c } // release cs_main before calling ActivateBestChain if (need_activate_chain) { CValidationState state; - if (!ActivateBestChain(state, Params(), a_recent_block)) { + if (!::ChainstateActive().ActivateBestChain(state, Params(), a_recent_block)) { LogPrint(BCLog::NET, "failed to activate chain (%s)\n", FormatStateMessage(state)); } } LOCK(cs_main); - const CBlockIndex* pindex = LookupBlockIndex(inv.hash); + const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(inv.hash); if (pindex) { send = BlockRequestAllowed(pindex, consensusParams); if (!send) { @@ -2089,7 +2089,7 @@ static void ProcessHeadersMessage(CNode& pfrom, CConnman& connman, ChainstateMan // don't connect before giving DoS points // - Once a headers message is received that is valid and does connect, // nUnconnectingHeaders gets reset back to 0. - if (!LookupBlockIndex(headers[0].hashPrevBlock) && nCount < MAX_BLOCKS_TO_ANNOUNCE) { + if (!g_chainman.m_blockman.LookupBlockIndex(headers[0].hashPrevBlock) && nCount < MAX_BLOCKS_TO_ANNOUNCE) { nodestate->nUnconnectingHeaders++; std::string msg_type = (pfrom.nServices & NODE_HEADERS_COMPRESSED) ? NetMsgType::GETHEADERS2 : NetMsgType::GETHEADERS; connman.PushMessage(&pfrom, msgMaker.Make(msg_type, ::ChainActive().GetLocator(pindexBestHeader), uint256())); @@ -2121,7 +2121,7 @@ static void ProcessHeadersMessage(CNode& pfrom, CConnman& connman, ChainstateMan // If we don't have the last header, then they'll have given us // something new (if these headers are valid). - if (!LookupBlockIndex(hashLastBlock)) { + if (!g_chainman.m_blockman.LookupBlockIndex(hashLastBlock)) { received_new_header = true; } } @@ -2272,7 +2272,7 @@ void static ProcessOrphanTx(CConnman& connman, CTxMemPool& mempool, std::setnStatus & BLOCK_HAVE_DATA)) { LogPrint(BCLog::NET, "Peer %d sent us a getblocktxn for a block we don't have\n", pfrom.GetId()); return; @@ -3266,7 +3266,7 @@ void PeerLogicValidation::ProcessMessage( if (locator.IsNull()) { // If locator is null, return the hashStop block - pindex = LookupBlockIndex(hashStop); + pindex = g_chainman.m_blockman.LookupBlockIndex(hashStop); if (!pindex) { return; } @@ -3279,7 +3279,7 @@ void PeerLogicValidation::ProcessMessage( else { // Find the last block the caller has in the main chain - pindex = FindForkInGlobalIndex(::ChainActive(), locator); + pindex = g_chainman.m_blockman.FindForkInGlobalIndex(::ChainActive(), locator); if (pindex) pindex = ::ChainActive().Next(pindex); } @@ -3368,7 +3368,7 @@ void PeerLogicValidation::ProcessMessage( bool fMissingInputs = false; CValidationState state; - if (!AlreadyHave(inv, m_mempool, *m_llmq_ctx) && AcceptToMemoryPool(m_mempool, state, ptx, &fMissingInputs /* pfMissingInputs */, + if (!AlreadyHave(inv, m_mempool, *m_llmq_ctx) && AcceptToMemoryPool(::ChainstateActive(), m_mempool, state, ptx, &fMissingInputs /* pfMissingInputs */, false /* bypass_limits */, 0 /* nAbsurdFee */)) { // Process custom txes, this changes AlreadyHave to "true" if (nInvType == MSG_DSTX) { @@ -3377,7 +3377,7 @@ void PeerLogicValidation::ProcessMessage( CCoinJoin::AddDSTX(dstx); } - m_mempool.check(&::ChainstateActive().CoinsTip()); + m_mempool.check(m_chainman.ActiveChainstate()); RelayTransaction(tx.GetHash(), m_connman); for (unsigned int i = 0; i < tx.vout.size(); i++) { @@ -3501,14 +3501,14 @@ void PeerLogicValidation::ProcessMessage( { LOCK(cs_main); - if (!LookupBlockIndex(cmpctblock.header.hashPrevBlock)) { + if (!g_chainman.m_blockman.LookupBlockIndex(cmpctblock.header.hashPrevBlock)) { // Doesn't connect (or is genesis), instead of DoSing in AcceptBlockHeader, request deeper headers if (!::ChainstateActive().IsInitialBlockDownload()) m_connman.PushMessage(&pfrom, msgMaker.Make((pfrom.nServices & NODE_HEADERS_COMPRESSED) ? NetMsgType::GETHEADERS2 : NetMsgType::GETHEADERS, ::ChainActive().GetLocator(pindexBestHeader), uint256())); return; } - if (!LookupBlockIndex(cmpctblock.header.GetHash())) { + if (!g_chainman.m_blockman.LookupBlockIndex(cmpctblock.header.GetHash())) { received_new_header = true; } } @@ -4597,7 +4597,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto) // then send all headers past that one. If we come across any // headers that aren't on ::ChainActive(), give up. for (const uint256 &hash : pto->vBlockHashesToAnnounce) { - const CBlockIndex* pindex = LookupBlockIndex(hash); + const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(hash); assert(pindex); if (::ChainActive()[pindex->nHeight] != pindex) { // Bail out if we reorged away from this block @@ -4705,7 +4705,7 @@ bool PeerLogicValidation::SendMessages(CNode* pto) // in the past. if (!pto->vBlockHashesToAnnounce.empty()) { const uint256 &hashToAnnounce = pto->vBlockHashesToAnnounce.back(); - const CBlockIndex* pindex = LookupBlockIndex(hashToAnnounce); + const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(hashToAnnounce); assert(pindex); // Warn if we're announcing a block that is not on the main chain. diff --git a/src/netaddress.cpp b/src/netaddress.cpp index bb4ba3095c8f..ce3a3745d6be 100644 --- a/src/netaddress.cpp +++ b/src/netaddress.cpp @@ -420,6 +420,16 @@ bool CNetAddr::IsLocal() const return false; } +/** + * @returns Whether or not this network address is a valid address that @a could + * be used to refer to an actual host. + * + * @note A valid address may or may not be publicly routable on the global + * internet. As in, the set of valid addresses is a superset of the set of + * publicly routable addresses. + * + * @see CNetAddr::IsRoutable() + */ bool CNetAddr::IsValid() const { // unspecified IPv6 address (::/128) @@ -450,6 +460,15 @@ bool CNetAddr::IsValid() const return true; } +/** + * @returns Whether or not this network address is publicly routable on the + * global internet. + * + * @note A routable address is always valid. As in, the set of routable addresses + * is a subset of the set of valid addresses. + * + * @see CNetAddr::IsValid() + */ bool CNetAddr::IsRoutable() const { if (!IsValid()) @@ -588,6 +607,16 @@ bool operator<(const CNetAddr& a, const CNetAddr& b) return std::tie(a.m_net, a.m_addr) < std::tie(b.m_net, b.m_addr); } +/** + * Try to get our IPv4 address. + * + * @param[out] pipv4Addr The in_addr struct to which to copy. + * + * @returns Whether or not the operation was successful, in particular, whether + * or not our address was an IPv4 address. + * + * @see CNetAddr::IsIPv4() + */ bool CNetAddr::GetInAddr(struct in_addr* pipv4Addr) const { if (!IsIPv4()) @@ -597,6 +626,16 @@ bool CNetAddr::GetInAddr(struct in_addr* pipv4Addr) const return true; } +/** + * Try to get our IPv6 (or CJDNS) address. + * + * @param[out] pipv6Addr The in6_addr struct to which to copy. + * + * @returns Whether or not the operation was successful, in particular, whether + * or not our address was an IPv6 address. + * + * @see CNetAddr::IsIPv6() + */ bool CNetAddr::GetIn6Addr(struct in6_addr* pipv6Addr) const { if (!IsIPv6()) { @@ -887,6 +926,18 @@ bool operator<(const CService& a, const CService& b) return static_cast(a) < static_cast(b) || (static_cast(a) == static_cast(b) && a.port < b.port); } +/** + * Obtain the IPv4/6 socket address this represents. + * + * @param[out] paddr The obtained socket address. + * @param[in,out] addrlen The size, in bytes, of the address structure pointed + * to by paddr. The value that's pointed to by this + * parameter might change after calling this function if + * the size of the corresponding address structure + * changed. + * + * @returns Whether or not the operation was successful. + */ bool CService::GetSockAddr(struct sockaddr* paddr, socklen_t *addrlen) const { if (IsIPv4()) { @@ -917,11 +968,14 @@ bool CService::GetSockAddr(struct sockaddr* paddr, socklen_t *addrlen) const return false; } +/** + * @returns An identifier unique to this service's address and port number. + */ std::vector CService::GetKey() const { auto key = GetAddrBytes(); - key.push_back(port / 0x100); - key.push_back(port & 0x0FF); + key.push_back(port / 0x100); // most significant byte of our port + key.push_back(port & 0x0FF); // least significant byte of our port return key; } @@ -1041,6 +1095,10 @@ CSubNet::CSubNet(const CNetAddr& addr) : CSubNet() network = addr; } +/** + * @returns True if this subnet is valid, the specified address is valid, and + * the specified address belongs in this subnet. + */ bool CSubNet::Match(const CNetAddr &addr) const { if (!valid || !addr.IsValid() || network.m_net != addr.m_net) diff --git a/src/netaddress.h b/src/netaddress.h index d25d63f65666..c771f760e340 100644 --- a/src/netaddress.h +++ b/src/netaddress.h @@ -153,12 +153,16 @@ class CNetAddr void SetRaw(Network network, const uint8_t *data); public: - /** - * Transform an arbitrary string into a non-routable ipv6 address. - * Useful for mapping resolved addresses back to their source. - */ bool SetInternal(const std::string& name); + /** + * Parse a Tor or I2P address and set this object to it. + * @param[in] addr Address to parse, for example + * pg6mmjiyjmcrsslvykfwnntlaru7p5svn6y2ymmju6nubxndf4pscryd.onion or + * ukeu3k5oycgaauneqgtnvselmt4yemvoilkln7jpvamvfx7dnkdq.b32.i2p. + * @returns Whether the operation was successful. + * @see CNetAddr::IsTor(), CNetAddr::IsI2P() + */ bool SetSpecial(const std::string &strName); // for Tor addresses bool IsBindAny() const; // INADDR_ANY equivalent bool IsIPv4() const; // IPv4 mapped address (::FFFF:0:0/96, 0.0.0.0/0) diff --git a/src/node/coinstats.cpp b/src/node/coinstats.cpp index 782aa76fc92e..8f8a74fca99e 100644 --- a/src/node/coinstats.cpp +++ b/src/node/coinstats.cpp @@ -92,7 +92,7 @@ static bool GetUTXOStats(CCoinsView* view, CCoinsStats& stats, T hash_obj, const stats.hashBlock = pcursor->GetBestBlock(); { LOCK(cs_main); - stats.nHeight = LookupBlockIndex(stats.hashBlock)->nHeight; + stats.nHeight = g_chainman.m_blockman.LookupBlockIndex(stats.hashBlock)->nHeight; } PrepareHash(hash_obj, stats); diff --git a/src/node/transaction.cpp b/src/node/transaction.cpp index df07816b6021..e44ae991977f 100644 --- a/src/node/transaction.cpp +++ b/src/node/transaction.cpp @@ -38,7 +38,7 @@ TransactionError BroadcastTransaction(NodeContext& node, const CTransactionRef t // Transaction is not already in the mempool. Submit it. CValidationState state; bool fMissingInputs; - if (!AcceptToMemoryPool(*node.mempool, state, std::move(tx), &fMissingInputs, + if (!AcceptToMemoryPool(::ChainstateActive(), *node.mempool, state, std::move(tx), &fMissingInputs, bypass_limits, max_tx_fee)) { if (state.IsInvalid()) { err_string = FormatStateMessage(state); diff --git a/src/qt/bitcoin.cpp b/src/qt/bitcoin.cpp index 4ee2d11fb03d..fc3c246c2960 100644 --- a/src/qt/bitcoin.cpp +++ b/src/qt/bitcoin.cpp @@ -206,6 +206,7 @@ BitcoinApplication::BitcoinApplication(interfaces::Node& node): pollShutdownTimer(nullptr), returnValue(0) { + // Qt runs setlocale(LC_ALL, "") on initialization. setQuitOnLastWindowClosed(false); } diff --git a/src/qt/test/apptests.cpp b/src/qt/test/apptests.cpp index 4486464cb89d..d4c4f3874974 100644 --- a/src/qt/test/apptests.cpp +++ b/src/qt/test/apptests.cpp @@ -87,8 +87,11 @@ void AppTests::appTests() // Reset global state to avoid interfering with later tests. LogInstance().DisconnectTestLogger(); AbortShutdown(); - UnloadBlockIndex(/* mempool */ nullptr); - WITH_LOCK(::cs_main, g_chainman.Reset()); + { + LOCK(cs_main); + UnloadBlockIndex(/* mempool */ nullptr, g_chainman); + g_chainman.Reset(); + } } //! Entry point for BitcoinGUI tests. diff --git a/src/rest.cpp b/src/rest.cpp index c561b1854358..2fa766b4763e 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -180,7 +180,7 @@ static bool rest_headers(const CoreContext& context, { LOCK(cs_main); tip = ::ChainActive().Tip(); - const CBlockIndex* pindex = LookupBlockIndex(hash); + const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(hash); while (pindex != nullptr && ::ChainActive().Contains(pindex)) { headers.push_back(pindex); if (headers.size() == (unsigned long)count) @@ -248,7 +248,7 @@ static bool rest_block(HTTPRequest* req, { LOCK(cs_main); tip = ::ChainActive().Tip(); - pblockindex = LookupBlockIndex(hash); + pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash); if (!pblockindex) { return RESTERR(req, HTTP_NOT_FOUND, hashStr + " not found"); } diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 67a3ba564cc9..d00e3773c7dc 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -49,10 +48,11 @@ #include -#include -#include #include +#include +#include + struct CUpdatedBlock { uint256 hash; @@ -299,7 +299,7 @@ static UniValue getbestchainlock(const JSONRPCRequest& request) result.pushKV("signature", clsig.getSig().ToString()); LOCK(cs_main); - result.pushKV("known_block", LookupBlockIndex(clsig.getBlockHash()) != nullptr); + result.pushKV("known_block", g_chainman.m_blockman.LookupBlockIndex(clsig.getBlockHash()) != nullptr); return result; } @@ -873,7 +873,7 @@ static UniValue getblockheader(const JSONRPCRequest& request) const CBlockIndex* tip; { LOCK(cs_main); - pblockindex = LookupBlockIndex(hash); + pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash); tip = ::ChainActive().Tip(); } @@ -943,7 +943,7 @@ static UniValue getblockheaders(const JSONRPCRequest& request) const CBlockIndex* tip; { LOCK(cs_main); - pblockindex = LookupBlockIndex(hash); + pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash); tip = ::ChainActive().Tip(); } @@ -1051,7 +1051,7 @@ static UniValue getmerkleblocks(const JSONRPCRequest& request) std::string strHash = request.params[1].get_str(); uint256 hash(uint256S(strHash)); - const CBlockIndex* pblockindex = LookupBlockIndex(hash); + const CBlockIndex* pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash); if (!pblockindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } @@ -1170,7 +1170,7 @@ static UniValue getblock(const JSONRPCRequest& request) const CBlockIndex* tip; { LOCK(cs_main); - pblockindex = LookupBlockIndex(hash); + pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash); tip = ::ChainActive().Tip(); if (!pblockindex) { @@ -1238,7 +1238,7 @@ static UniValue pruneblockchain(const JSONRPCRequest& request) height = chainHeight - MIN_BLOCKS_TO_KEEP; } - PruneBlockFilesManual(height); + PruneBlockFilesManual(::ChainstateActive(), height); const CBlockIndex* block = ::ChainActive().Tip(); CHECK_NONFATAL(block); while (block->pprev && (block->pprev->nStatus & BLOCK_HAVE_DATA)) { @@ -1380,7 +1380,7 @@ static UniValue gettxout(const JSONRPCRequest& request) } } - const CBlockIndex* pindex = LookupBlockIndex(coins_view->GetBestBlock()); + const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(coins_view->GetBestBlock()); ret.pushKV("bestblock", pindex->GetBlockHash().GetHex()); if (coin.nHeight == MEMPOOL_HEIGHT) { ret.pushKV("confirmations", 0); @@ -1420,7 +1420,7 @@ static UniValue verifychain(const JSONRPCRequest& request) const NodeContext& node_context = EnsureNodeContext(request.context); return CVerifyDB().VerifyDB( - Params(), &::ChainstateActive().CoinsTip(), *node_context.evodb, check_level, check_depth); + Params(), ::ChainstateActive(), &::ChainstateActive().CoinsTip(), *node_context.evodb, check_level, check_depth); } /** Implementation of IsSuperMajority with better feedback */ @@ -1456,7 +1456,7 @@ static UniValue SoftForkDesc(const std::string &name, int version, const CBlockI static UniValue BIP9SoftForkDesc(const Consensus::Params& consensusParams, Consensus::DeploymentPos id) { UniValue rv(UniValue::VOBJ); - const ThresholdState thresholdState = VersionBitsTipState(consensusParams, id); + const ThresholdState thresholdState = VersionBitsState(::ChainActive().Tip(), consensusParams, id, versionbitscache); switch (thresholdState) { case ThresholdState::DEFINED: rv.pushKV("status", "defined"); break; case ThresholdState::STARTED: rv.pushKV("status", "started"); break; @@ -1470,11 +1470,11 @@ static UniValue BIP9SoftForkDesc(const Consensus::Params& consensusParams, Conse } rv.pushKV("start_time", consensusParams.vDeployments[id].nStartTime); rv.pushKV("timeout", consensusParams.vDeployments[id].nTimeout); - rv.pushKV("since", VersionBitsTipStateSinceHeight(consensusParams, id)); + rv.pushKV("since", VersionBitsStateSinceHeight(::ChainActive().Tip(), consensusParams, id, versionbitscache)); if (ThresholdState::STARTED == thresholdState) { UniValue statsUV(UniValue::VOBJ); - BIP9Stats statsStruct = VersionBitsTipStatistics(consensusParams, id); + BIP9Stats statsStruct = VersionBitsStatistics(::ChainActive().Tip(), consensusParams, id, versionbitscache); statsUV.pushKV("period", statsStruct.period); statsUV.pushKV("threshold", statsStruct.threshold); statsUV.pushKV("elapsed", statsStruct.elapsed); @@ -1811,14 +1811,14 @@ static UniValue preciousblock(const JSONRPCRequest& request) { LOCK(cs_main); - pblockindex = LookupBlockIndex(hash); + pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash); if (!pblockindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } } CValidationState state; - PreciousBlock(state, Params(), pblockindex); + ::ChainstateActive().PreciousBlock(state, Params(), pblockindex); if (!state.IsValid()) { throw JSONRPCError(RPC_DATABASE_ERROR, FormatStateMessage(state)); @@ -1848,15 +1848,15 @@ static UniValue invalidateblock(const JSONRPCRequest& request) CBlockIndex* pblockindex; { LOCK(cs_main); - pblockindex = LookupBlockIndex(hash); + pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash); if (!pblockindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } } - InvalidateBlock(state, Params(), pblockindex); + ::ChainstateActive().InvalidateBlock(state, Params(), pblockindex); if (state.IsValid()) { - ActivateBestChain(state, Params()); + ::ChainstateActive().ActivateBestChain(state, Params()); } if (!state.IsValid()) { @@ -1886,16 +1886,16 @@ static UniValue reconsiderblock(const JSONRPCRequest& request) { LOCK(cs_main); - CBlockIndex* pblockindex = LookupBlockIndex(hash); + CBlockIndex* pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash); if (!pblockindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } - ResetBlockFailureFlags(pblockindex); + ::ChainstateActive().ResetBlockFailureFlags(pblockindex); } CValidationState state; - ActivateBestChain(state, Params()); + ::ChainstateActive().ActivateBestChain(state, Params()); if (!state.IsValid()) { throw JSONRPCError(RPC_DATABASE_ERROR, FormatStateMessage(state)); @@ -1939,7 +1939,7 @@ static UniValue getchaintxstats(const JSONRPCRequest& request) } else { uint256 hash = uint256S(request.params[1].get_str()); LOCK(cs_main); - pindex = LookupBlockIndex(hash); + pindex = g_chainman.m_blockman.LookupBlockIndex(hash); if (!pindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } @@ -2116,7 +2116,7 @@ static UniValue getblockstats(const JSONRPCRequest& request) } else { const uint256 hash = ParseHashV(request.params[0], "parameter 1"); - pindex = LookupBlockIndex(hash); + pindex = g_chainman.m_blockman.LookupBlockIndex(hash); if (!pindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } @@ -2336,7 +2336,7 @@ static UniValue getspecialtxes(const JSONRPCRequest& request) throw JSONRPCError(RPC_INVALID_PARAMETER, "Verbosity must be in range 0..2"); } } - const CBlockIndex* pblockindex = LookupBlockIndex(hash); + const CBlockIndex* pblockindex = g_chainman.m_blockman.LookupBlockIndex(hash); if (!pblockindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } @@ -2657,7 +2657,7 @@ static UniValue getblockfilter(const JSONRPCRequest& request) bool block_was_connected; { LOCK(cs_main); - block_index = LookupBlockIndex(block_hash); + block_index = g_chainman.m_blockman.LookupBlockIndex(block_hash); if (!block_index) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } @@ -2761,7 +2761,7 @@ UniValue dumptxoutset(const JSONRPCRequest& request) } pcursor = std::unique_ptr(::ChainstateActive().CoinsDB().Cursor()); - tip = LookupBlockIndex(stats.hashBlock); + tip = g_chainman.m_blockman.LookupBlockIndex(stats.hashBlock); CHECK_NONFATAL(tip); } diff --git a/src/rpc/governance.cpp b/src/rpc/governance.cpp index 585d79530eb8..e79eb3251b23 100644 --- a/src/rpc/governance.cpp +++ b/src/rpc/governance.cpp @@ -110,7 +110,7 @@ static UniValue gobject_check(const JSONRPCRequest& request) if (govobj.GetObjectType() == GOVERNANCE_OBJECT_PROPOSAL) { LOCK(cs_main); - bool fAllowScript = (VersionBitsTipState(Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0024) == ThresholdState::ACTIVE); + bool fAllowScript = (VersionBitsState(::ChainActive().Tip(), Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0024, versionbitscache) == ThresholdState::ACTIVE); CProposalValidator validator(strDataHex, fAllowScript); if (!validator.Validate()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid proposal data, error messages:" + validator.GetErrorMessages()); @@ -185,7 +185,7 @@ static UniValue gobject_prepare(const JSONRPCRequest& request) if (govobj.GetObjectType() == GOVERNANCE_OBJECT_PROPOSAL) { LOCK(cs_main); - bool fAllowScript = (VersionBitsTipState(Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0024) == ThresholdState::ACTIVE); + bool fAllowScript = (VersionBitsState(::ChainActive().Tip(), Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0024, versionbitscache) == ThresholdState::ACTIVE); CProposalValidator validator(strDataHex, fAllowScript); if (!validator.Validate()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid proposal data, error messages:" + validator.GetErrorMessages()); @@ -223,7 +223,7 @@ static UniValue gobject_prepare(const JSONRPCRequest& request) CTransactionRef tx; - bool fork_active = WITH_LOCK(cs_main, return VersionBitsTipState(Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0024) == ThresholdState::ACTIVE); + bool fork_active = WITH_LOCK(cs_main, return VersionBitsState(::ChainActive().Tip(), Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0024, versionbitscache) == ThresholdState::ACTIVE); if (!wallet->GetBudgetSystemCollateralTX(tx, govobj.GetHash(), govobj.GetMinCollateralFee(fork_active), outpoint)) { std::string err = "Error making collateral transaction for governance object. Please check your wallet balance and make sure your wallet is unlocked."; @@ -350,7 +350,7 @@ static UniValue gobject_submit(const JSONRPCRequest& request) if (govobj.GetObjectType() == GOVERNANCE_OBJECT_PROPOSAL) { LOCK(cs_main); - bool fAllowScript = (VersionBitsTipState(Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0024) == ThresholdState::ACTIVE); + bool fAllowScript = (VersionBitsState(::ChainActive().Tip(), Params().GetConsensus(), Consensus::DEPLOYMENT_DIP0024, versionbitscache) == ThresholdState::ACTIVE); CProposalValidator validator(strDataHex, fAllowScript); if (!validator.Validate()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid proposal data, error messages:" + validator.GetErrorMessages()); diff --git a/src/rpc/masternode.cpp b/src/rpc/masternode.cpp index 7d4fbb2473e8..82af40b21ca0 100644 --- a/src/rpc/masternode.cpp +++ b/src/rpc/masternode.cpp @@ -421,7 +421,7 @@ static UniValue masternode_payments(const JSONRPCRequest& request) } else { LOCK(cs_main); uint256 blockHash = ParseHashV(request.params[0], "blockhash"); - pindex = LookupBlockIndex(blockHash); + pindex = g_chainman.m_blockman.LookupBlockIndex(blockHash); if (pindex == nullptr) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 10da6d07916e..c65a4512c265 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -161,7 +161,7 @@ static UniValue generateBlocks(ChainstateManager& chainman, const CTxMemPool& me UniValue blockHashes(UniValue::VARR); while (nHeight < nHeightEnd && !ShutdownRequested()) { - std::unique_ptr pblocktemplate(BlockAssembler(*sporkManager, *governance, quorum_block_processor, clhandler, isman, evodb, mempool, Params()).CreateNewBlock(coinbase_script)); + std::unique_ptr pblocktemplate(BlockAssembler(*sporkManager, *governance, quorum_block_processor, clhandler, isman, evodb, mempool, Params()).CreateNewBlock(::ChainstateActive(), coinbase_script)); if (!pblocktemplate.get()) throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block"); CBlock *pblock = &pblocktemplate->block; @@ -367,7 +367,7 @@ static UniValue generateblock(const JSONRPCRequest& request) LOCK(cs_main); CTxMemPool empty_mempool; - std::unique_ptr blocktemplate(BlockAssembler(*sporkManager, *governance, *llmq_ctx.quorum_block_processor, *llmq_ctx.clhandler, *llmq_ctx.isman, *node_context.evodb, empty_mempool, chainparams).CreateNewBlock(coinbase_script)); + std::unique_ptr blocktemplate(BlockAssembler(*sporkManager, *governance, *llmq_ctx.quorum_block_processor, *llmq_ctx.clhandler, *llmq_ctx.isman, *node_context.evodb, empty_mempool, chainparams).CreateNewBlock(::ChainstateActive(), coinbase_script)); if (!blocktemplate) { throw JSONRPCError(RPC_INTERNAL_ERROR, "Couldn't create new block"); } @@ -383,7 +383,7 @@ static UniValue generateblock(const JSONRPCRequest& request) LOCK(cs_main); CValidationState state; - if (!TestBlockValidity(state, *llmq_ctx.clhandler, *node_context.evodb, chainparams, block, LookupBlockIndex(block.hashPrevBlock), false, false)) { + if (!TestBlockValidity(state, *llmq_ctx.clhandler, *node_context.evodb, chainparams, ::ChainstateActive(), block, g_chainman.m_blockman.LookupBlockIndex(block.hashPrevBlock), false, false)) { throw JSONRPCError(RPC_VERIFY_ERROR, strprintf("TestBlockValidity failed: %s", state.GetRejectReason())); } } @@ -657,7 +657,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Block decode failed"); uint256 hash = block.GetHash(); - const CBlockIndex* pindex = LookupBlockIndex(hash); + const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(hash); if (pindex) { if (pindex->IsValid(BLOCK_VALID_SCRIPTS)) return "duplicate"; @@ -673,7 +673,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) if (block.hashPrevBlock != pindexPrev->GetBlockHash()) return "inconclusive-not-best-prevblk"; CValidationState state; - TestBlockValidity(state, *llmq_ctx.clhandler, *node_context.evodb, Params(), block, pindexPrev, false, true); + TestBlockValidity(state, *llmq_ctx.clhandler, *node_context.evodb, Params(), ::ChainstateActive(), block, pindexPrev, false, true); return BIP22ValidationResult(state); } @@ -779,7 +779,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request) // Create new block CScript scriptDummy = CScript() << OP_TRUE; LLMQContext& llmq_ctx = EnsureLLMQContext(request.context); - pblocktemplate = BlockAssembler(*sporkManager, *governance, *llmq_ctx.quorum_block_processor, *llmq_ctx.clhandler, *llmq_ctx.isman, *node_context.evodb, mempool, Params()).CreateNewBlock(scriptDummy); + pblocktemplate = BlockAssembler(*sporkManager, *governance, *llmq_ctx.quorum_block_processor, *llmq_ctx.clhandler, *llmq_ctx.isman, *node_context.evodb, mempool, Params()).CreateNewBlock(::ChainstateActive(), scriptDummy); if (!pblocktemplate) throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory"); @@ -996,7 +996,7 @@ static UniValue submitblock(const JSONRPCRequest& request) uint256 hash = block.GetHash(); { LOCK(cs_main); - const CBlockIndex* pindex = LookupBlockIndex(hash); + const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(hash); if (pindex) { if (pindex->IsValid(BLOCK_VALID_SCRIPTS)) { return "duplicate"; @@ -1043,7 +1043,7 @@ static UniValue submitheader(const JSONRPCRequest& request) } { LOCK(cs_main); - if (!LookupBlockIndex(h.hashPrevBlock)) { + if (!g_chainman.m_blockman.LookupBlockIndex(h.hashPrevBlock)) { throw JSONRPCError(RPC_VERIFY_ERROR, "Must submit previous header (" + h.hashPrevBlock.GetHex() + ") first"); } } diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index eca3d2179c6f..8db4721c1922 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -736,6 +736,27 @@ static UniValue clearbanned(const JSONRPCRequest& request) return NullUniValue; } +static UniValue cleardiscouraged(const JSONRPCRequest& request) +{ + RPCHelpMan{"cleardiscouraged", + "\nClear all discouraged nodes.\n", + {}, + RPCResult{RPCResult::Type::NONE, "", ""}, + RPCExamples{ + HelpExampleCli("cleardiscouraged", "") + + HelpExampleRpc("cleardiscouraged", "") + }, + }.Check(request); + NodeContext& node = EnsureNodeContext(request.context); + if (!node.banman) { + throw JSONRPCError(RPC_DATABASE_ERROR, "Error: Ban database not loaded"); + } + + node.banman->ClearDiscouraged(); + + return NullUniValue; +} + static UniValue setnetworkactive(const JSONRPCRequest& request) { RPCHelpMan{"setnetworkactive", @@ -825,6 +846,7 @@ static const CRPCCommand commands[] = { "network", "setban", &setban, {"subnet", "command", "bantime", "absolute"} }, { "network", "listbanned", &listbanned, {} }, { "network", "clearbanned", &clearbanned, {} }, + { "network", "cleardiscouraged", &cleardiscouraged, {} }, { "network", "setnetworkactive", &setnetworkactive, {"state"} }, { "network", "getnodeaddresses", &getnodeaddresses, {"count"} }, }; diff --git a/src/rpc/quorums.cpp b/src/rpc/quorums.cpp index a0a0839e0d1a..2dfeb42143ad 100644 --- a/src/rpc/quorums.cpp +++ b/src/rpc/quorums.cpp @@ -741,7 +741,7 @@ static UniValue quorum_getdata(const JSONRPCRequest& request) NodeContext& node = EnsureNodeContext(request.context); LLMQContext& llmq_ctx = EnsureLLMQContext(request.context); - const CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(cs_main, return LookupBlockIndex(quorumHash)); + const CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(cs_main, return g_chainman.m_blockman.LookupBlockIndex(quorumHash)); return node.connman->ForNode(nodeId, [&](CNode* pNode) { return llmq_ctx.qman->RequestQuorumData(pNode, llmqType, pQuorumBaseBlockIndex, nDataMask, proTxHash); }); @@ -880,7 +880,7 @@ static UniValue verifychainlock(const JSONRPCRequest& request) int nBlockHeight; if (request.params[2].isNull()) { - const CBlockIndex* pIndex = WITH_LOCK(cs_main, return LookupBlockIndex(nBlockHash)); + const CBlockIndex* pIndex = WITH_LOCK(cs_main, return g_chainman.m_blockman.LookupBlockIndex(nBlockHash)); if (pIndex == nullptr) { throw JSONRPCError(RPC_INTERNAL_ERROR, "blockHash not found"); } @@ -933,7 +933,7 @@ static UniValue verifyislock(const JSONRPCRequest& request) uint256 hash_block; CTransactionRef tx = GetTransaction(/* block_index */ nullptr, /* mempool */ nullptr, txid, Params().GetConsensus(), hash_block); if (tx && !hash_block.IsNull()) { - pindexMined = LookupBlockIndex(hash_block); + pindexMined = g_chainman.m_blockman.LookupBlockIndex(hash_block); } } diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 9f90b07b0d47..22e6002c8728 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -92,7 +92,7 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, CTxMemPool& mempo LOCK(cs_main); entry.pushKV("blockhash", hashBlock.GetHex()); - CBlockIndex* pindex = LookupBlockIndex(hashBlock); + CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(hashBlock); if (pindex) { if (::ChainActive().Contains(pindex)) { entry.pushKV("height", pindex->nHeight); @@ -143,7 +143,7 @@ static UniValue getrawtransaction(const JSONRPCRequest& request) {RPCResult::Type::STR_HEX, "txid", "The transaction id (same as provided)"}, {RPCResult::Type::NUM, "size", "The serialized transaction size"}, {RPCResult::Type::NUM, "version", "The version"}, - {RPCResult::Type::NUM, "version", "The type"}, + {RPCResult::Type::NUM, "type", "The type"}, {RPCResult::Type::NUM_TIME, "locktime", "The lock time"}, {RPCResult::Type::ARR, "vin", "", { @@ -222,7 +222,7 @@ static UniValue getrawtransaction(const JSONRPCRequest& request) LOCK(cs_main); uint256 blockhash = ParseHashV(request.params[2], "parameter 3"); - blockindex = LookupBlockIndex(blockhash); + blockindex = g_chainman.m_blockman.LookupBlockIndex(blockhash); if (!blockindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block hash not found"); } @@ -313,7 +313,7 @@ static UniValue gettxoutproof(const JSONRPCRequest& request) if (!request.params[1].isNull()) { LOCK(cs_main); hashBlock = uint256S(request.params[1].get_str()); - pblockindex = LookupBlockIndex(hashBlock); + pblockindex = g_chainman.m_blockman.LookupBlockIndex(hashBlock); if (!pblockindex) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); } @@ -343,7 +343,7 @@ static UniValue gettxoutproof(const JSONRPCRequest& request) if (!tx || hashBlock.IsNull()) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not yet in block"); } - pblockindex = LookupBlockIndex(hashBlock); + pblockindex = g_chainman.m_blockman.LookupBlockIndex(hashBlock); if (!pblockindex) { throw JSONRPCError(RPC_INTERNAL_ERROR, "Transaction index corrupt"); } @@ -404,7 +404,7 @@ static UniValue verifytxoutproof(const JSONRPCRequest& request) LOCK(cs_main); - const CBlockIndex* pindex = LookupBlockIndex(merkleBlock.header.GetHash()); + const CBlockIndex* pindex = g_chainman.m_blockman.LookupBlockIndex(merkleBlock.header.GetHash()); if (!pindex || !::ChainActive().Contains(pindex) || pindex->nTx == 0) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain"); } @@ -494,7 +494,7 @@ static UniValue decoderawtransaction(const JSONRPCRequest& request) {RPCResult::Type::STR_HEX, "txid", "The transaction id"}, {RPCResult::Type::NUM, "size", "The transaction size"}, {RPCResult::Type::NUM, "version", "The version"}, - {RPCResult::Type::NUM, "version", "The type"}, + {RPCResult::Type::NUM, "type", "The type"}, {RPCResult::Type::NUM_TIME, "locktime", "The lock time"}, {RPCResult::Type::ARR, "vin", "", { @@ -926,7 +926,7 @@ static UniValue testmempoolaccept(const JSONRPCRequest& request) bool test_accept_res; { LOCK(cs_main); - test_accept_res = AcceptToMemoryPool(mempool, state, std::move(tx), &missing_inputs, + test_accept_res = AcceptToMemoryPool(::ChainstateActive(), mempool, state, std::move(tx), &missing_inputs, false /* bypass_limits */, max_raw_tx_fee, /* test_accept */ true); } result_0.pushKV("allowed", test_accept_res); diff --git a/src/rpc/rawtransaction_util.cpp b/src/rpc/rawtransaction_util.cpp index 9f22f0555a01..7c7823c5c119 100644 --- a/src/rpc/rawtransaction_util.cpp +++ b/src/rpc/rawtransaction_util.cpp @@ -207,6 +207,9 @@ void ParsePrevouts(const UniValue& prevTxsUnival, FillableSigningProvider* keyst throw JSONRPCError(RPC_INVALID_PARAMETER, "redeemScript does not match scriptPubKey"); } } + if (rs.isNull()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Missing redeemScript"); + } } } } diff --git a/src/test/block_reward_reallocation_tests.cpp b/src/test/block_reward_reallocation_tests.cpp index 8c457d79237e..9ea366407059 100644 --- a/src/test/block_reward_reallocation_tests.cpp +++ b/src/test/block_reward_reallocation_tests.cpp @@ -182,9 +182,9 @@ BOOST_FIXTURE_TEST_CASE(block_reward_reallocation, TestChainBRRBeforeActivationS } LOCK(cs_main); if (expected_lockin) { - BOOST_CHECK_EQUAL(VersionBitsTipState(consensus_params, deployment_id), ThresholdState::LOCKED_IN); + BOOST_CHECK_EQUAL(VersionBitsState(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache), ThresholdState::LOCKED_IN); } else { - BOOST_CHECK_EQUAL(VersionBitsTipState(consensus_params, deployment_id), ThresholdState::STARTED); + BOOST_CHECK_EQUAL(VersionBitsState(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache), ThresholdState::STARTED); } }; @@ -205,7 +205,7 @@ BOOST_FIXTURE_TEST_CASE(block_reward_reallocation, TestChainBRRBeforeActivationS BOOST_ASSERT(deterministicMNManager->GetListAtChainTip().HasMN(tx.GetHash())); BOOST_CHECK_EQUAL(::ChainActive().Height(), 498); - BOOST_CHECK_EQUAL(VersionBitsTipState(consensus_params, deployment_id), ThresholdState::DEFINED); + BOOST_CHECK_EQUAL(VersionBitsState(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache), ThresholdState::DEFINED); } CreateAndProcessBlock({}, coinbaseKey); @@ -216,10 +216,10 @@ BOOST_FIXTURE_TEST_CASE(block_reward_reallocation, TestChainBRRBeforeActivationS BOOST_CHECK_EQUAL(::ChainActive().Height(), 499); deterministicMNManager->UpdatedBlockTip(::ChainActive().Tip()); BOOST_ASSERT(deterministicMNManager->GetListAtChainTip().HasMN(tx.GetHash())); - BOOST_CHECK_EQUAL(VersionBitsTipState(consensus_params, deployment_id), ThresholdState::STARTED); - BOOST_CHECK_EQUAL(VersionBitsTipStatistics(consensus_params, deployment_id).threshold, threshold(0)); + BOOST_CHECK_EQUAL(VersionBitsState(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache), ThresholdState::STARTED); + BOOST_CHECK_EQUAL(VersionBitsStatistics(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache).threshold, threshold(0)); // Next block should be signaling by default - const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor, *m_node.llmq_ctx->clhandler, *m_node.llmq_ctx->isman, *m_node.evodb, *m_node.mempool, Params()).CreateNewBlock(coinbasePubKey); + const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor, *m_node.llmq_ctx->clhandler, *m_node.llmq_ctx->isman, *m_node.evodb, *m_node.mempool, Params()).CreateNewBlock(::ChainstateActive(), coinbasePubKey); const uint32_t bitmask = ((uint32_t)1) << consensus_params.vDeployments[deployment_id].bit; BOOST_CHECK_EQUAL(::ChainActive().Tip()->nVersion & bitmask, 0); BOOST_CHECK_EQUAL(pblocktemplate->block.nVersion & bitmask, bitmask); @@ -233,8 +233,8 @@ BOOST_FIXTURE_TEST_CASE(block_reward_reallocation, TestChainBRRBeforeActivationS BOOST_CHECK_EQUAL(::ChainActive().Height(), 999); deterministicMNManager->UpdatedBlockTip(::ChainActive().Tip()); BOOST_ASSERT(deterministicMNManager->GetListAtChainTip().HasMN(tx.GetHash())); - BOOST_CHECK_EQUAL(VersionBitsTipState(consensus_params, deployment_id), ThresholdState::STARTED); - BOOST_CHECK_EQUAL(VersionBitsTipStatistics(consensus_params, deployment_id).threshold, threshold(1)); + BOOST_CHECK_EQUAL(VersionBitsState(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache), ThresholdState::STARTED); + BOOST_CHECK_EQUAL(VersionBitsStatistics(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache).threshold, threshold(1)); } signal(threshold(1) - 1, false); // 1 block short again @@ -245,8 +245,8 @@ BOOST_FIXTURE_TEST_CASE(block_reward_reallocation, TestChainBRRBeforeActivationS BOOST_CHECK_EQUAL(::ChainActive().Height(), 1499); deterministicMNManager->UpdatedBlockTip(::ChainActive().Tip()); BOOST_ASSERT(deterministicMNManager->GetListAtChainTip().HasMN(tx.GetHash())); - BOOST_CHECK_EQUAL(VersionBitsTipState(consensus_params, deployment_id), ThresholdState::STARTED); - BOOST_CHECK_EQUAL(VersionBitsTipStatistics(consensus_params, deployment_id).threshold, threshold(2)); + BOOST_CHECK_EQUAL(VersionBitsState(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache), ThresholdState::STARTED); + BOOST_CHECK_EQUAL(VersionBitsStatistics(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache).threshold, threshold(2)); } signal(threshold(2), true); // just enough to lock in @@ -257,14 +257,14 @@ BOOST_FIXTURE_TEST_CASE(block_reward_reallocation, TestChainBRRBeforeActivationS BOOST_CHECK_EQUAL(::ChainActive().Height(), 1999); deterministicMNManager->UpdatedBlockTip(::ChainActive().Tip()); BOOST_ASSERT(deterministicMNManager->GetListAtChainTip().HasMN(tx.GetHash())); - BOOST_CHECK_EQUAL(VersionBitsTipState(consensus_params, deployment_id), ThresholdState::LOCKED_IN); + BOOST_CHECK_EQUAL(VersionBitsState(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache), ThresholdState::LOCKED_IN); } for ([[maybe_unused]] auto _ : irange::range(499)) { // Still LOCKED_IN at height = 2498 CreateAndProcessBlock({}, coinbaseKey); LOCK(cs_main); - BOOST_CHECK_EQUAL(VersionBitsTipState(consensus_params, deployment_id), ThresholdState::LOCKED_IN); + BOOST_CHECK_EQUAL(VersionBitsState(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache), ThresholdState::LOCKED_IN); } CreateAndProcessBlock({}, coinbaseKey); @@ -274,8 +274,8 @@ BOOST_FIXTURE_TEST_CASE(block_reward_reallocation, TestChainBRRBeforeActivationS BOOST_CHECK_EQUAL(::ChainActive().Height(), 2499); deterministicMNManager->UpdatedBlockTip(::ChainActive().Tip()); BOOST_ASSERT(deterministicMNManager->GetListAtChainTip().HasMN(tx.GetHash())); - BOOST_CHECK_EQUAL(VersionBitsTipState(consensus_params, deployment_id), ThresholdState::ACTIVE); - BOOST_CHECK_EQUAL(VersionBitsTipStateSinceHeight(consensus_params, deployment_id), 2500); + BOOST_CHECK_EQUAL(VersionBitsState(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache), ThresholdState::ACTIVE); + BOOST_CHECK_EQUAL(VersionBitsStateSinceHeight(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache), 2500); } { @@ -286,7 +286,7 @@ BOOST_FIXTURE_TEST_CASE(block_reward_reallocation, TestChainBRRBeforeActivationS deterministicMNManager->UpdatedBlockTip(::ChainActive().Tip()); BOOST_ASSERT(deterministicMNManager->GetListAtChainTip().HasMN(tx.GetHash())); auto masternode_payment = GetMasternodePayment(::ChainActive().Height(), GetBlockSubsidy(::ChainActive().Tip()->nBits, ::ChainActive().Height(), consensus_params), 2500); - const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor, *m_node.llmq_ctx->clhandler, *m_node.llmq_ctx->isman, *m_node.evodb, *m_node.mempool, Params()).CreateNewBlock(coinbasePubKey); + const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor, *m_node.llmq_ctx->clhandler, *m_node.llmq_ctx->isman, *m_node.evodb, *m_node.mempool, Params()).CreateNewBlock(::ChainstateActive(), coinbasePubKey); BOOST_CHECK_EQUAL(pblocktemplate->voutMasternodePayments[0].nValue, masternode_payment); } @@ -297,7 +297,7 @@ BOOST_FIXTURE_TEST_CASE(block_reward_reallocation, TestChainBRRBeforeActivationS { LOCK(cs_main); auto masternode_payment = GetMasternodePayment(::ChainActive().Height(), GetBlockSubsidy(::ChainActive().Tip()->nBits, ::ChainActive().Height(), consensus_params), 2500); - const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor, *m_node.llmq_ctx->clhandler, *m_node.llmq_ctx->isman, *m_node.evodb, *m_node.mempool, Params()).CreateNewBlock(coinbasePubKey); + const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor, *m_node.llmq_ctx->clhandler, *m_node.llmq_ctx->isman, *m_node.evodb, *m_node.mempool, Params()).CreateNewBlock(::ChainstateActive(), coinbasePubKey); BOOST_CHECK_EQUAL(pblocktemplate->block.vtx[0]->GetValueOut(), 13748571607); BOOST_CHECK_EQUAL(pblocktemplate->voutMasternodePayments[0].nValue, masternode_payment); BOOST_CHECK_EQUAL(pblocktemplate->voutMasternodePayments[0].nValue, 6874285801); // 0.4999999998 @@ -312,7 +312,7 @@ BOOST_FIXTURE_TEST_CASE(block_reward_reallocation, TestChainBRRBeforeActivationS } LOCK(cs_main); auto masternode_payment = GetMasternodePayment(::ChainActive().Height(), GetBlockSubsidy(::ChainActive().Tip()->nBits, ::ChainActive().Height(), consensus_params), 2500); - const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor, *m_node.llmq_ctx->clhandler, *m_node.llmq_ctx->isman, *m_node.evodb, *m_node.mempool, Params()).CreateNewBlock(coinbasePubKey); + const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor, *m_node.llmq_ctx->clhandler, *m_node.llmq_ctx->isman, *m_node.evodb, *m_node.mempool, Params()).CreateNewBlock(::ChainstateActive(), coinbasePubKey); BOOST_CHECK_EQUAL(pblocktemplate->voutMasternodePayments[0].nValue, masternode_payment); } } @@ -321,7 +321,7 @@ BOOST_FIXTURE_TEST_CASE(block_reward_reallocation, TestChainBRRBeforeActivationS // Reward split should reach ~60/40 after reallocation is done LOCK(cs_main); auto masternode_payment = GetMasternodePayment(::ChainActive().Height(), GetBlockSubsidy(::ChainActive().Tip()->nBits, ::ChainActive().Height(), consensus_params), 2500); - const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor, *m_node.llmq_ctx->clhandler, *m_node.llmq_ctx->isman, *m_node.evodb, *m_node.mempool, Params()).CreateNewBlock(coinbasePubKey); + const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor, *m_node.llmq_ctx->clhandler, *m_node.llmq_ctx->isman, *m_node.evodb, *m_node.mempool, Params()).CreateNewBlock(::ChainstateActive(), coinbasePubKey); BOOST_CHECK_EQUAL(pblocktemplate->block.vtx[0]->GetValueOut(), 10221599170); BOOST_CHECK_EQUAL(pblocktemplate->voutMasternodePayments[0].nValue, masternode_payment); BOOST_CHECK_EQUAL(pblocktemplate->voutMasternodePayments[0].nValue, 6132959502); // 0.6 @@ -335,7 +335,7 @@ BOOST_FIXTURE_TEST_CASE(block_reward_reallocation, TestChainBRRBeforeActivationS } LOCK(cs_main); auto masternode_payment = GetMasternodePayment(::ChainActive().Height(), GetBlockSubsidy(::ChainActive().Tip()->nBits, ::ChainActive().Height(), consensus_params), 2500); - const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor, *m_node.llmq_ctx->clhandler, *m_node.llmq_ctx->isman, *m_node.evodb, *m_node.mempool, Params()).CreateNewBlock(coinbasePubKey); + const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor, *m_node.llmq_ctx->clhandler, *m_node.llmq_ctx->isman, *m_node.evodb, *m_node.mempool, Params()).CreateNewBlock(::ChainstateActive(), coinbasePubKey); BOOST_CHECK_EQUAL(pblocktemplate->voutMasternodePayments[0].nValue, masternode_payment); } @@ -343,7 +343,7 @@ BOOST_FIXTURE_TEST_CASE(block_reward_reallocation, TestChainBRRBeforeActivationS // Reward split should reach ~60/40 after reallocation is done LOCK(cs_main); auto masternode_payment = GetMasternodePayment(::ChainActive().Height(), GetBlockSubsidy(::ChainActive().Tip()->nBits, ::ChainActive().Height(), consensus_params), 2500); - const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor, *m_node.llmq_ctx->clhandler, *m_node.llmq_ctx->isman, *m_node.evodb, *m_node.mempool, Params()).CreateNewBlock(coinbasePubKey); + const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor, *m_node.llmq_ctx->clhandler, *m_node.llmq_ctx->isman, *m_node.evodb, *m_node.mempool, Params()).CreateNewBlock(::ChainstateActive(), coinbasePubKey); BOOST_CHECK_EQUAL(pblocktemplate->block.vtx[0]->GetValueOut(), 9491484944); BOOST_CHECK_EQUAL(pblocktemplate->voutMasternodePayments[0].nValue, masternode_payment); BOOST_CHECK_EQUAL(pblocktemplate->voutMasternodePayments[0].nValue, 5694890966); // 0.6 diff --git a/src/test/blockfilter_index_tests.cpp b/src/test/blockfilter_index_tests.cpp index df0ea6e2610f..63fb2e39a2ff 100644 --- a/src/test/blockfilter_index_tests.cpp +++ b/src/test/blockfilter_index_tests.cpp @@ -69,7 +69,7 @@ CBlock BuildChainTestingSetup::CreateBlock(const CBlockIndex* prev, const CScript& scriptPubKey) { const CChainParams& chainparams = Params(); - std::unique_ptr pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor, *m_node.llmq_ctx->clhandler, *m_node.llmq_ctx->isman, *m_node.evodb, *m_node.mempool, chainparams).CreateNewBlock(scriptPubKey); + std::unique_ptr pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor, *m_node.llmq_ctx->clhandler, *m_node.llmq_ctx->isman, *m_node.evodb, *m_node.mempool, chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey); CBlock& block = pblocktemplate->block; block.hashPrevBlock = prev->GetBlockHash(); block.nTime = prev->nTime + 1; @@ -185,7 +185,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup) const CBlockIndex* block_index; { LOCK(cs_main); - block_index = LookupBlockIndex(block->GetHash()); + block_index = g_chainman.m_blockman.LookupBlockIndex(block->GetHash()); } BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain()); @@ -203,7 +203,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup) const CBlockIndex* block_index; { LOCK(cs_main); - block_index = LookupBlockIndex(block->GetHash()); + block_index = g_chainman.m_blockman.LookupBlockIndex(block->GetHash()); } BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain()); @@ -217,7 +217,7 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup) const CBlockIndex* block_index; { LOCK(cs_main); - block_index = LookupBlockIndex(block->GetHash()); + block_index = g_chainman.m_blockman.LookupBlockIndex(block->GetHash()); } BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain()); @@ -238,14 +238,14 @@ BOOST_FIXTURE_TEST_CASE(blockfilter_index_initial_sync, BuildChainTestingSetup) { LOCK(cs_main); - block_index = LookupBlockIndex(chainA[i]->GetHash()); + block_index = g_chainman.m_blockman.LookupBlockIndex(chainA[i]->GetHash()); } BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain()); CheckFilterLookups(filter_index, block_index, chainA_last_header); { LOCK(cs_main); - block_index = LookupBlockIndex(chainB[i]->GetHash()); + block_index = g_chainman.m_blockman.LookupBlockIndex(chainB[i]->GetHash()); } BOOST_CHECK(filter_index.BlockUntilSyncedToCurrentChain()); CheckFilterLookups(filter_index, block_index, chainB_last_header); diff --git a/src/test/dynamic_activation_thresholds_tests.cpp b/src/test/dynamic_activation_thresholds_tests.cpp index 1078c2c6f267..fad2518b261f 100644 --- a/src/test/dynamic_activation_thresholds_tests.cpp +++ b/src/test/dynamic_activation_thresholds_tests.cpp @@ -53,9 +53,9 @@ struct TestChainDATSetup : public TestChainSetup } LOCK(cs_main); if (expected_lockin) { - BOOST_CHECK_EQUAL(VersionBitsTipState(consensus_params, deployment_id), ThresholdState::LOCKED_IN); + BOOST_CHECK_EQUAL(VersionBitsState(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache), ThresholdState::LOCKED_IN); } else { - BOOST_CHECK_EQUAL(VersionBitsTipState(consensus_params, deployment_id), ThresholdState::STARTED); + BOOST_CHECK_EQUAL(VersionBitsState(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache), ThresholdState::STARTED); } } @@ -67,7 +67,7 @@ struct TestChainDATSetup : public TestChainSetup { LOCK(cs_main); BOOST_CHECK_EQUAL(::ChainActive().Height(), window - 2); - BOOST_CHECK_EQUAL(VersionBitsTipState(consensus_params, deployment_id), ThresholdState::DEFINED); + BOOST_CHECK_EQUAL(VersionBitsState(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache), ThresholdState::DEFINED); } CreateAndProcessBlock({}, coinbaseKey); @@ -76,10 +76,10 @@ struct TestChainDATSetup : public TestChainSetup LOCK(cs_main); // Advance from DEFINED to STARTED at height = window - 1 BOOST_CHECK_EQUAL(::ChainActive().Height(), window - 1); - BOOST_CHECK_EQUAL(VersionBitsTipState(consensus_params, deployment_id), ThresholdState::STARTED); - BOOST_CHECK_EQUAL(VersionBitsTipStatistics(consensus_params, deployment_id).threshold, threshold(0)); + BOOST_CHECK_EQUAL(VersionBitsState(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache), ThresholdState::STARTED); + BOOST_CHECK_EQUAL(VersionBitsStatistics(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache).threshold, threshold(0)); // Next block should be signaling by default - const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor, *m_node.llmq_ctx->clhandler, *m_node.llmq_ctx->isman, *m_node.evodb, *m_node.mempool, Params()).CreateNewBlock(coinbasePubKey); + const auto pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor, *m_node.llmq_ctx->clhandler, *m_node.llmq_ctx->isman, *m_node.evodb, *m_node.mempool, Params()).CreateNewBlock(::ChainstateActive(), coinbasePubKey); const uint32_t bitmask = ((uint32_t)1) << consensus_params.vDeployments[deployment_id].bit; BOOST_CHECK_EQUAL(::ChainActive().Tip()->nVersion & bitmask, 0); BOOST_CHECK_EQUAL(pblocktemplate->block.nVersion & bitmask, bitmask); @@ -93,17 +93,17 @@ struct TestChainDATSetup : public TestChainSetup // Still STARTED but with a (potentially) new threshold LOCK(cs_main); BOOST_CHECK_EQUAL(::ChainActive().Height(), window * (i + 2) - 1); - BOOST_CHECK_EQUAL(VersionBitsTipState(consensus_params, deployment_id), ThresholdState::STARTED); - const auto vbts = VersionBitsTipStatistics(consensus_params, deployment_id); + BOOST_CHECK_EQUAL(VersionBitsState(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache), ThresholdState::STARTED); + const auto vbts = VersionBitsStatistics(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache); BOOST_CHECK_EQUAL(vbts.threshold, threshold(i + 1)); BOOST_CHECK(vbts.threshold <= th_start); BOOST_CHECK(vbts.threshold >= th_end); } } if (LOCK(cs_main); check_activation_at_min) { - BOOST_CHECK_EQUAL(VersionBitsTipStatistics(consensus_params, deployment_id).threshold, th_end); + BOOST_CHECK_EQUAL(VersionBitsStatistics(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache).threshold, th_end); } else { - BOOST_CHECK(VersionBitsTipStatistics(consensus_params, deployment_id).threshold > th_end); + BOOST_CHECK(VersionBitsStatistics(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache).threshold > th_end); } // activate @@ -113,7 +113,7 @@ struct TestChainDATSetup : public TestChainSetup } { LOCK(cs_main); - BOOST_CHECK_EQUAL(VersionBitsTipState(consensus_params, deployment_id), ThresholdState::ACTIVE); + BOOST_CHECK_EQUAL(VersionBitsState(::ChainActive().Tip(), consensus_params, deployment_id, versionbitscache), ThresholdState::ACTIVE); } } diff --git a/src/test/evo_deterministicmns_tests.cpp b/src/test/evo_deterministicmns_tests.cpp index 20528bc7875c..da8fef5f36fe 100644 --- a/src/test/evo_deterministicmns_tests.cpp +++ b/src/test/evo_deterministicmns_tests.cpp @@ -655,7 +655,7 @@ void FuncVerifyDB(TestChainSetup& setup) // Verify db consistency LOCK(cs_main); - BOOST_ASSERT(CVerifyDB().VerifyDB(Params(), &::ChainstateActive().CoinsTip(), *(setup.m_node.evodb), 4, 2)); + BOOST_ASSERT(CVerifyDB().VerifyDB(Params(), ::ChainstateActive(), &::ChainstateActive().CoinsTip(), *(setup.m_node.evodb), 4, 2)); } BOOST_AUTO_TEST_SUITE(evo_dip3_activation_tests) diff --git a/src/test/fuzz/addrman.cpp b/src/test/fuzz/addrman.cpp index 14f133e40f20..b12f421bc833 100644 --- a/src/test/fuzz/addrman.cpp +++ b/src/test/fuzz/addrman.cpp @@ -35,83 +35,71 @@ FUZZ_TARGET_INIT(addrman, initialize_addrman) } } while (fuzzed_data_provider.ConsumeBool()) { - switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 11)) { - case 0: { - addr_man.Clear(); - break; - } - case 1: { - addr_man.ResolveCollisions(); - break; - } - case 2: { - (void)addr_man.SelectTriedCollision(); - break; - } - case 3: { - (void)addr_man.Select(fuzzed_data_provider.ConsumeBool()); - break; - } - case 4: { - (void)addr_man.GetAddr(); - break; - } - case 5: { - const std::optional opt_address = ConsumeDeserializable(fuzzed_data_provider); - const std::optional opt_net_addr = ConsumeDeserializable(fuzzed_data_provider); - if (opt_address && opt_net_addr) { - addr_man.Add(*opt_address, *opt_net_addr, fuzzed_data_provider.ConsumeIntegralInRange(0, 100000000)); - } - break; - } - case 6: { - std::vector addresses; - while (fuzzed_data_provider.ConsumeBool()) { + CallOneOf( + fuzzed_data_provider, + [&] { + addr_man.Clear(); + }, + [&] { + addr_man.ResolveCollisions(); + }, + [&] { + (void)addr_man.SelectTriedCollision(); + }, + [&] { + (void)addr_man.Select(fuzzed_data_provider.ConsumeBool()); + }, + [&] { + (void)addr_man.GetAddr(); + }, + [&] { const std::optional opt_address = ConsumeDeserializable(fuzzed_data_provider); - if (!opt_address) { - break; + const std::optional opt_net_addr = ConsumeDeserializable(fuzzed_data_provider); + if (opt_address && opt_net_addr) { + addr_man.Add(*opt_address, *opt_net_addr, fuzzed_data_provider.ConsumeIntegralInRange(0, 100000000)); } - addresses.push_back(*opt_address); - } - const std::optional opt_net_addr = ConsumeDeserializable(fuzzed_data_provider); - if (opt_net_addr) { - addr_man.Add(addresses, *opt_net_addr, fuzzed_data_provider.ConsumeIntegralInRange(0, 100000000)); - } - break; - } - case 7: { - const std::optional opt_service = ConsumeDeserializable(fuzzed_data_provider); - if (opt_service) { - addr_man.Good(*opt_service, fuzzed_data_provider.ConsumeBool(), ConsumeTime(fuzzed_data_provider)); - } - break; - } - case 8: { - const std::optional opt_service = ConsumeDeserializable(fuzzed_data_provider); - if (opt_service) { - addr_man.Attempt(*opt_service, fuzzed_data_provider.ConsumeBool(), ConsumeTime(fuzzed_data_provider)); - } - break; - } - case 9: { - const std::optional opt_service = ConsumeDeserializable(fuzzed_data_provider); - if (opt_service) { - addr_man.Connected(*opt_service, ConsumeTime(fuzzed_data_provider)); - } - break; - } - case 10: { - const std::optional opt_service = ConsumeDeserializable(fuzzed_data_provider); - if (opt_service) { - addr_man.SetServices(*opt_service, ServiceFlags{fuzzed_data_provider.ConsumeIntegral()}); - } - break; - } - case 11: { - (void)addr_man.Check(); - break; - } - } + }, + [&] { + std::vector addresses; + while (fuzzed_data_provider.ConsumeBool()) { + const std::optional opt_address = ConsumeDeserializable(fuzzed_data_provider); + if (!opt_address) { + break; + } + addresses.push_back(*opt_address); + } + const std::optional opt_net_addr = ConsumeDeserializable(fuzzed_data_provider); + if (opt_net_addr) { + addr_man.Add(addresses, *opt_net_addr, fuzzed_data_provider.ConsumeIntegralInRange(0, 100000000)); + } + }, + [&] { + const std::optional opt_service = ConsumeDeserializable(fuzzed_data_provider); + if (opt_service) { + addr_man.Good(*opt_service, fuzzed_data_provider.ConsumeBool(), ConsumeTime(fuzzed_data_provider)); + } + }, + [&] { + const std::optional opt_service = ConsumeDeserializable(fuzzed_data_provider); + if (opt_service) { + addr_man.Attempt(*opt_service, fuzzed_data_provider.ConsumeBool(), ConsumeTime(fuzzed_data_provider)); + } + }, + [&] { + const std::optional opt_service = ConsumeDeserializable(fuzzed_data_provider); + if (opt_service) { + addr_man.Connected(*opt_service, ConsumeTime(fuzzed_data_provider)); + } + }, + [&] { + const std::optional opt_service = ConsumeDeserializable(fuzzed_data_provider); + if (opt_service) { + addr_man.SetServices(*opt_service, ServiceFlags{fuzzed_data_provider.ConsumeIntegral()}); + } + }, + [&] { + (void)addr_man.Check(); + }); } (void)addr_man.size(); CDataStream data_stream(SER_NETWORK, PROTOCOL_VERSION); diff --git a/src/test/fuzz/autofile.cpp b/src/test/fuzz/autofile.cpp index 95be582d6587..e3f1a8572a4c 100644 --- a/src/test/fuzz/autofile.cpp +++ b/src/test/fuzz/autofile.cpp @@ -20,43 +20,37 @@ FUZZ_TARGET(autofile) FuzzedAutoFileProvider fuzzed_auto_file_provider = ConsumeAutoFile(fuzzed_data_provider); CAutoFile auto_file = fuzzed_auto_file_provider.open(); while (fuzzed_data_provider.ConsumeBool()) { - switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 5)) { - case 0: { - std::array arr{}; - try { - auto_file.read((char*)arr.data(), fuzzed_data_provider.ConsumeIntegralInRange(0, 4096)); - } catch (const std::ios_base::failure&) { - } - break; - } - case 1: { - const std::array arr{}; - try { - auto_file.write((const char*)arr.data(), fuzzed_data_provider.ConsumeIntegralInRange(0, 4096)); - } catch (const std::ios_base::failure&) { - } - break; - } - case 2: { - try { - auto_file.ignore(fuzzed_data_provider.ConsumeIntegralInRange(0, 4096)); - } catch (const std::ios_base::failure&) { - } - break; - } - case 3: { - auto_file.fclose(); - break; - } - case 4: { - ReadFromStream(fuzzed_data_provider, auto_file); - break; - } - case 5: { - WriteToStream(fuzzed_data_provider, auto_file); - break; - } - } + CallOneOf( + fuzzed_data_provider, + [&] { + std::array arr{}; + try { + auto_file.read((char*)arr.data(), fuzzed_data_provider.ConsumeIntegralInRange(0, 4096)); + } catch (const std::ios_base::failure&) { + } + }, + [&] { + const std::array arr{}; + try { + auto_file.write((const char*)arr.data(), fuzzed_data_provider.ConsumeIntegralInRange(0, 4096)); + } catch (const std::ios_base::failure&) { + } + }, + [&] { + try { + auto_file.ignore(fuzzed_data_provider.ConsumeIntegralInRange(0, 4096)); + } catch (const std::ios_base::failure&) { + } + }, + [&] { + auto_file.fclose(); + }, + [&] { + ReadFromStream(fuzzed_data_provider, auto_file); + }, + [&] { + WriteToStream(fuzzed_data_provider, auto_file); + }); } (void)auto_file.Get(); (void)auto_file.GetType(); diff --git a/src/test/fuzz/banman.cpp b/src/test/fuzz/banman.cpp index bc321932e633..17271ff2cb7f 100644 --- a/src/test/fuzz/banman.cpp +++ b/src/test/fuzz/banman.cpp @@ -37,51 +37,43 @@ FUZZ_TARGET_INIT(banman, initialize_banman) { BanMan ban_man{banlist_file, nullptr, ConsumeBanTimeOffset(fuzzed_data_provider)}; while (fuzzed_data_provider.ConsumeBool()) { - switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 11)) { - case 0: { - ban_man.Ban(ConsumeNetAddr(fuzzed_data_provider), - ConsumeBanTimeOffset(fuzzed_data_provider), fuzzed_data_provider.ConsumeBool()); - break; - } - case 1: { - ban_man.Ban(ConsumeSubNet(fuzzed_data_provider), - ConsumeBanTimeOffset(fuzzed_data_provider), fuzzed_data_provider.ConsumeBool()); - break; - } - case 2: { - ban_man.ClearBanned(); - break; - } - case 4: { - ban_man.IsBanned(ConsumeNetAddr(fuzzed_data_provider)); - break; - } - case 5: { - ban_man.IsBanned(ConsumeSubNet(fuzzed_data_provider)); - break; - } - case 6: { - ban_man.Unban(ConsumeNetAddr(fuzzed_data_provider)); - break; - } - case 7: { - ban_man.Unban(ConsumeSubNet(fuzzed_data_provider)); - break; - } - case 8: { - banmap_t banmap; - ban_man.GetBanned(banmap); - break; - } - case 9: { - ban_man.DumpBanlist(); - break; - } - case 11: { - ban_man.Discourage(ConsumeNetAddr(fuzzed_data_provider)); - break; - } - } + CallOneOf( + fuzzed_data_provider, + [&] { + ban_man.Ban(ConsumeNetAddr(fuzzed_data_provider), + ConsumeBanTimeOffset(fuzzed_data_provider), fuzzed_data_provider.ConsumeBool()); + }, + [&] { + ban_man.Ban(ConsumeSubNet(fuzzed_data_provider), + ConsumeBanTimeOffset(fuzzed_data_provider), fuzzed_data_provider.ConsumeBool()); + }, + [&] { + ban_man.ClearBanned(); + }, + [] {}, + [&] { + ban_man.IsBanned(ConsumeNetAddr(fuzzed_data_provider)); + }, + [&] { + ban_man.IsBanned(ConsumeSubNet(fuzzed_data_provider)); + }, + [&] { + ban_man.Unban(ConsumeNetAddr(fuzzed_data_provider)); + }, + [&] { + ban_man.Unban(ConsumeSubNet(fuzzed_data_provider)); + }, + [&] { + banmap_t banmap; + ban_man.GetBanned(banmap); + }, + [&] { + ban_man.DumpBanlist(); + }, + [] {}, + [&] { + ban_man.Discourage(ConsumeNetAddr(fuzzed_data_provider)); + }); } } fs::remove(banlist_file); diff --git a/src/test/fuzz/bloom_filter.cpp b/src/test/fuzz/bloom_filter.cpp index c0c66c564b2c..d43c182644bb 100644 --- a/src/test/fuzz/bloom_filter.cpp +++ b/src/test/fuzz/bloom_filter.cpp @@ -25,47 +25,43 @@ FUZZ_TARGET(bloom_filter) fuzzed_data_provider.ConsumeIntegral(), static_cast(fuzzed_data_provider.PickValueInArray({BLOOM_UPDATE_NONE, BLOOM_UPDATE_ALL, BLOOM_UPDATE_P2PUBKEY_ONLY, BLOOM_UPDATE_MASK}))}; while (fuzzed_data_provider.remaining_bytes() > 0) { - switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 3)) { - case 0: { - const std::vector b = ConsumeRandomLengthByteVector(fuzzed_data_provider); - (void)bloom_filter.contains(b); - bloom_filter.insert(b); - const bool present = bloom_filter.contains(b); - assert(present); - break; - } - case 1: { - const std::optional out_point = ConsumeDeserializable(fuzzed_data_provider); - if (!out_point) { - break; - } - (void)bloom_filter.contains(*out_point); - bloom_filter.insert(*out_point); - const bool present = bloom_filter.contains(*out_point); - assert(present); - break; - } - case 2: { - const std::optional u256 = ConsumeDeserializable(fuzzed_data_provider); - if (!u256) { - break; - } - (void)bloom_filter.contains(*u256); - bloom_filter.insert(*u256); - const bool present = bloom_filter.contains(*u256); - assert(present); - break; - } - case 3: { - const std::optional mut_tx = ConsumeDeserializable(fuzzed_data_provider); - if (!mut_tx) { - break; - } - const CTransaction tx{*mut_tx}; - (void)bloom_filter.IsRelevantAndUpdate(tx); - break; - } - } + CallOneOf( + fuzzed_data_provider, + [&] { + const std::vector b = ConsumeRandomLengthByteVector(fuzzed_data_provider); + (void)bloom_filter.contains(b); + bloom_filter.insert(b); + const bool present = bloom_filter.contains(b); + assert(present); + }, + [&] { + const std::optional out_point = ConsumeDeserializable(fuzzed_data_provider); + if (!out_point) { + return; + } + (void)bloom_filter.contains(*out_point); + bloom_filter.insert(*out_point); + const bool present = bloom_filter.contains(*out_point); + assert(present); + }, + [&] { + const std::optional u256 = ConsumeDeserializable(fuzzed_data_provider); + if (!u256) { + return; + } + (void)bloom_filter.contains(*u256); + bloom_filter.insert(*u256); + const bool present = bloom_filter.contains(*u256); + assert(present); + }, + [&] { + const std::optional mut_tx = ConsumeDeserializable(fuzzed_data_provider); + if (!mut_tx) { + return; + } + const CTransaction tx{*mut_tx}; + (void)bloom_filter.IsRelevantAndUpdate(tx); + }); (void)bloom_filter.IsWithinSizeConstraints(); } } diff --git a/src/test/fuzz/buffered_file.cpp b/src/test/fuzz/buffered_file.cpp index e5a987783936..b32a59986aaa 100644 --- a/src/test/fuzz/buffered_file.cpp +++ b/src/test/fuzz/buffered_file.cpp @@ -30,41 +30,36 @@ FUZZ_TARGET(buffered_file) if (opt_buffered_file && fuzzed_file != nullptr) { bool setpos_fail = false; while (fuzzed_data_provider.ConsumeBool()) { - switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 4)) { - case 0: { - std::array arr{}; - try { - opt_buffered_file->read((char*)arr.data(), fuzzed_data_provider.ConsumeIntegralInRange(0, 4096)); - } catch (const std::ios_base::failure&) { - } - break; - } - case 1: { - opt_buffered_file->SetLimit(fuzzed_data_provider.ConsumeIntegralInRange(0, 4096)); - break; - } - case 2: { - if (!opt_buffered_file->SetPos(fuzzed_data_provider.ConsumeIntegralInRange(0, 4096))) { - setpos_fail = true; - } - break; - } - case 3: { - if (setpos_fail) { - // Calling FindByte(...) after a failed SetPos(...) call may result in an infinite loop. - break; - } - try { - opt_buffered_file->FindByte(fuzzed_data_provider.ConsumeIntegral()); - } catch (const std::ios_base::failure&) { - } - break; - } - case 4: { - ReadFromStream(fuzzed_data_provider, *opt_buffered_file); - break; - } - } + CallOneOf( + fuzzed_data_provider, + [&] { + std::array arr{}; + try { + opt_buffered_file->read((char*)arr.data(), fuzzed_data_provider.ConsumeIntegralInRange(0, 4096)); + } catch (const std::ios_base::failure&) { + } + }, + [&] { + opt_buffered_file->SetLimit(fuzzed_data_provider.ConsumeIntegralInRange(0, 4096)); + }, + [&] { + if (!opt_buffered_file->SetPos(fuzzed_data_provider.ConsumeIntegralInRange(0, 4096))) { + setpos_fail = true; + } + }, + [&] { + if (setpos_fail) { + // Calling FindByte(...) after a failed SetPos(...) call may result in an infinite loop. + return; + } + try { + opt_buffered_file->FindByte(fuzzed_data_provider.ConsumeIntegral()); + } catch (const std::ios_base::failure&) { + } + }, + [&] { + ReadFromStream(fuzzed_data_provider, *opt_buffered_file); + }); } opt_buffered_file->GetPos(); opt_buffered_file->GetType(); diff --git a/src/test/fuzz/coins_view.cpp b/src/test/fuzz/coins_view.cpp index e3781d057a28..748c4c29a734 100644 --- a/src/test/fuzz/coins_view.cpp +++ b/src/test/fuzz/coins_view.cpp @@ -51,103 +51,93 @@ FUZZ_TARGET_INIT(coins_view, initialize_coins_view) Coin random_coin; CMutableTransaction random_mutable_transaction; while (fuzzed_data_provider.ConsumeBool()) { - switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 9)) { - case 0: { - if (random_coin.IsSpent()) { - break; - } - Coin coin = random_coin; - bool expected_code_path = false; - const bool possible_overwrite = fuzzed_data_provider.ConsumeBool(); - try { - coins_view_cache.AddCoin(random_out_point, std::move(coin), possible_overwrite); - expected_code_path = true; - } catch (const std::logic_error& e) { - if (e.what() == std::string{"Attempted to overwrite an unspent coin (when possible_overwrite is false)"}) { - assert(!possible_overwrite); + CallOneOf( + fuzzed_data_provider, + [&] { + if (random_coin.IsSpent()) { + return; + } + Coin coin = random_coin; + bool expected_code_path = false; + const bool possible_overwrite = fuzzed_data_provider.ConsumeBool(); + try { + coins_view_cache.AddCoin(random_out_point, std::move(coin), possible_overwrite); expected_code_path = true; + } catch (const std::logic_error& e) { + if (e.what() == std::string{"Attempted to overwrite an unspent coin (when possible_overwrite is false)"}) { + assert(!possible_overwrite); + expected_code_path = true; + } } - } - assert(expected_code_path); - break; - } - case 1: { - (void)coins_view_cache.Flush(); - break; - } - case 2: { - coins_view_cache.SetBestBlock(ConsumeUInt256(fuzzed_data_provider)); - break; - } - case 3: { - Coin move_to; - (void)coins_view_cache.SpendCoin(random_out_point, fuzzed_data_provider.ConsumeBool() ? &move_to : nullptr); - break; - } - case 4: { - coins_view_cache.Uncache(random_out_point); - break; - } - case 5: { - if (fuzzed_data_provider.ConsumeBool()) { - backend_coins_view = CCoinsView{}; - } - coins_view_cache.SetBackend(backend_coins_view); - break; - } - case 6: { - const std::optional opt_out_point = ConsumeDeserializable(fuzzed_data_provider); - if (!opt_out_point) { - break; - } - random_out_point = *opt_out_point; - break; - } - case 7: { - const std::optional opt_coin = ConsumeDeserializable(fuzzed_data_provider); - if (!opt_coin) { - break; - } - random_coin = *opt_coin; - break; - } - case 8: { - const std::optional opt_mutable_transaction = ConsumeDeserializable(fuzzed_data_provider); - if (!opt_mutable_transaction) { - break; - } - random_mutable_transaction = *opt_mutable_transaction; - break; - } - case 9: { - CCoinsMap coins_map; - while (fuzzed_data_provider.ConsumeBool()) { - CCoinsCacheEntry coins_cache_entry; - coins_cache_entry.flags = fuzzed_data_provider.ConsumeIntegral(); + assert(expected_code_path); + }, + [&] { + (void)coins_view_cache.Flush(); + }, + [&] { + coins_view_cache.SetBestBlock(ConsumeUInt256(fuzzed_data_provider)); + }, + [&] { + Coin move_to; + (void)coins_view_cache.SpendCoin(random_out_point, fuzzed_data_provider.ConsumeBool() ? &move_to : nullptr); + }, + [&] { + coins_view_cache.Uncache(random_out_point); + }, + [&] { if (fuzzed_data_provider.ConsumeBool()) { - coins_cache_entry.coin = random_coin; - } else { - const std::optional opt_coin = ConsumeDeserializable(fuzzed_data_provider); - if (!opt_coin) { - break; + backend_coins_view = CCoinsView{}; + } + coins_view_cache.SetBackend(backend_coins_view); + }, + [&] { + const std::optional opt_out_point = ConsumeDeserializable(fuzzed_data_provider); + if (!opt_out_point) { + return; + } + random_out_point = *opt_out_point; + }, + [&] { + const std::optional opt_coin = ConsumeDeserializable(fuzzed_data_provider); + if (!opt_coin) { + return; + } + random_coin = *opt_coin; + }, + [&] { + const std::optional opt_mutable_transaction = ConsumeDeserializable(fuzzed_data_provider); + if (!opt_mutable_transaction) { + return; + } + random_mutable_transaction = *opt_mutable_transaction; + }, + [&] { + CCoinsMap coins_map; + while (fuzzed_data_provider.ConsumeBool()) { + CCoinsCacheEntry coins_cache_entry; + coins_cache_entry.flags = fuzzed_data_provider.ConsumeIntegral(); + if (fuzzed_data_provider.ConsumeBool()) { + coins_cache_entry.coin = random_coin; + } else { + const std::optional opt_coin = ConsumeDeserializable(fuzzed_data_provider); + if (!opt_coin) { + return; + } + coins_cache_entry.coin = *opt_coin; } - coins_cache_entry.coin = *opt_coin; + coins_map.emplace(random_out_point, std::move(coins_cache_entry)); } - coins_map.emplace(random_out_point, std::move(coins_cache_entry)); - } - bool expected_code_path = false; - try { - coins_view_cache.BatchWrite(coins_map, fuzzed_data_provider.ConsumeBool() ? ConsumeUInt256(fuzzed_data_provider) : coins_view_cache.GetBestBlock()); - expected_code_path = true; - } catch (const std::logic_error& e) { - if (e.what() == std::string{"FRESH flag misapplied to coin that exists in parent cache"}) { + bool expected_code_path = false; + try { + coins_view_cache.BatchWrite(coins_map, fuzzed_data_provider.ConsumeBool() ? ConsumeUInt256(fuzzed_data_provider) : coins_view_cache.GetBestBlock()); expected_code_path = true; + } catch (const std::logic_error& e) { + if (e.what() == std::string{"FRESH flag misapplied to coin that exists in parent cache"}) { + expected_code_path = true; + } } - } - assert(expected_code_path); - break; - } - } + assert(expected_code_path); + }); } { @@ -200,92 +190,86 @@ FUZZ_TARGET_INIT(coins_view, initialize_coins_view) } if (fuzzed_data_provider.ConsumeBool()) { - switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 5)) { - case 0: { - const CTransaction transaction{random_mutable_transaction}; - bool is_spent = false; - for (const CTxOut& tx_out : transaction.vout) { - if (Coin{tx_out, 0, transaction.IsCoinBase()}.IsSpent()) { - is_spent = true; + CallOneOf( + fuzzed_data_provider, + [&] { + const CTransaction transaction{random_mutable_transaction}; + bool is_spent = false; + for (const CTxOut& tx_out : transaction.vout) { + if (Coin{tx_out, 0, transaction.IsCoinBase()}.IsSpent()) { + is_spent = true; + } + } + if (is_spent) { + // Avoid: + // coins.cpp:69: void CCoinsViewCache::AddCoin(const COutPoint &, Coin &&, bool): Assertion `!coin.IsSpent()' failed. + return; } - } - if (is_spent) { - // Avoid: - // coins.cpp:69: void CCoinsViewCache::AddCoin(const COutPoint &, Coin &&, bool): Assertion `!coin.IsSpent()' failed. - break; - } - bool expected_code_path = false; - const int height = fuzzed_data_provider.ConsumeIntegral(); - const bool possible_overwrite = fuzzed_data_provider.ConsumeBool(); - try { - AddCoins(coins_view_cache, transaction, height, possible_overwrite); - expected_code_path = true; - } catch (const std::logic_error& e) { - if (e.what() == std::string{"Attempted to overwrite an unspent coin (when possible_overwrite is false)"}) { - assert(!possible_overwrite); + bool expected_code_path = false; + const int height = fuzzed_data_provider.ConsumeIntegral(); + const bool possible_overwrite = fuzzed_data_provider.ConsumeBool(); + try { + AddCoins(coins_view_cache, transaction, height, possible_overwrite); expected_code_path = true; + } catch (const std::logic_error& e) { + if (e.what() == std::string{"Attempted to overwrite an unspent coin (when possible_overwrite is false)"}) { + assert(!possible_overwrite); + expected_code_path = true; + } } - } - assert(expected_code_path); - break; - } - case 1: { - (void)AreInputsStandard(CTransaction{random_mutable_transaction}, coins_view_cache); - break; - } - case 2: { - CValidationState state; - CAmount tx_fee_out; - const CTransaction transaction{random_mutable_transaction}; - if (ContainsSpentInput(transaction, coins_view_cache)) { - // Avoid: - // consensus/tx_verify.cpp:171: bool Consensus::CheckTxInputs(const CTransaction &, CValidationState &, const CCoinsViewCache &, int, CAmount &): Assertion `!coin.IsSpent()' failed. - break; - } - try { - (void)Consensus::CheckTxInputs(transaction, state, coins_view_cache, fuzzed_data_provider.ConsumeIntegralInRange(0, std::numeric_limits::max()), tx_fee_out); - assert(MoneyRange(tx_fee_out)); - } catch (const std::runtime_error&) { - } - break; - } - case 3: { - const CTransaction transaction{random_mutable_transaction}; - if (ContainsSpentInput(transaction, coins_view_cache)) { - // Avoid: - // consensus/tx_verify.cpp:130: unsigned int GetP2SHSigOpCount(const CTransaction &, const CCoinsViewCache &): Assertion `!coin.IsSpent()' failed. - break; - } - (void)GetP2SHSigOpCount(transaction, coins_view_cache); - break; - } - case 4: { - const CTransaction transaction{random_mutable_transaction}; - if (ContainsSpentInput(transaction, coins_view_cache)) { - // Avoid: - // consensus/tx_verify.cpp:130: unsigned int GetP2SHSigOpCount(const CTransaction &, const CCoinsViewCache &): Assertion `!coin.IsSpent()' failed. - break; - } - const int flags = fuzzed_data_provider.ConsumeIntegral(); - if (!transaction.vin.empty() && (flags & SCRIPT_VERIFY_P2SH) == 0) { - // Avoid: - // script/interpreter.cpp:1705: size_t CountWitnessSigOps(const CScript &, const CScript &, const CScriptWitness *, unsigned int): Assertion `(flags & SCRIPT_VERIFY_P2SH) != 0' failed. - break; - } - (void)GetTransactionSigOpCount(transaction, coins_view_cache, flags); - break; - } - case 5: { - CCoinsStats stats; - bool expected_code_path = false; - try { - (void)GetUTXOStats(&coins_view_cache, stats, CoinStatsHashType::HASH_SERIALIZED); - } catch (const std::logic_error&) { - expected_code_path = true; - } - assert(expected_code_path); - break; - } - } + assert(expected_code_path); + }, + [&] { + (void)AreInputsStandard(CTransaction{random_mutable_transaction}, coins_view_cache); + }, + [&] { + CValidationState state; + CAmount tx_fee_out; + const CTransaction transaction{random_mutable_transaction}; + if (ContainsSpentInput(transaction, coins_view_cache)) { + // Avoid: + // consensus/tx_verify.cpp:171: bool Consensus::CheckTxInputs(const CTransaction &, CValidationState &, const CCoinsViewCache &, int, CAmount &): Assertion `!coin.IsSpent()' failed. + return; + } + try { + (void)Consensus::CheckTxInputs(transaction, state, coins_view_cache, fuzzed_data_provider.ConsumeIntegralInRange(0, std::numeric_limits::max()), tx_fee_out); + assert(MoneyRange(tx_fee_out)); + } catch (const std::runtime_error&) { + } + }, + [&] { + const CTransaction transaction{random_mutable_transaction}; + if (ContainsSpentInput(transaction, coins_view_cache)) { + // Avoid: + // consensus/tx_verify.cpp:130: unsigned int GetP2SHSigOpCount(const CTransaction &, const CCoinsViewCache &): Assertion `!coin.IsSpent()' failed. + return; + } + (void)GetP2SHSigOpCount(transaction, coins_view_cache); + }, + [&] { + const CTransaction transaction{random_mutable_transaction}; + if (ContainsSpentInput(transaction, coins_view_cache)) { + // Avoid: + // consensus/tx_verify.cpp:130: unsigned int GetP2SHSigOpCount(const CTransaction &, const CCoinsViewCache &): Assertion `!coin.IsSpent()' failed. + return; + } + const int flags = fuzzed_data_provider.ConsumeIntegral(); + if (!transaction.vin.empty() && (flags & SCRIPT_VERIFY_P2SH) == 0) { + // Avoid: + // script/interpreter.cpp:1705: size_t CountWitnessSigOps(const CScript &, const CScript &, const CScriptWitness *, unsigned int): Assertion `(flags & SCRIPT_VERIFY_P2SH) != 0' failed. + return; + } + (void)GetTransactionSigOpCount(transaction, coins_view_cache, flags); + }, + [&] { + CCoinsStats stats; + bool expected_code_path = false; + try { + (void)GetUTXOStats(&coins_view_cache, stats, CoinStatsHashType::HASH_SERIALIZED); + } catch (const std::logic_error&) { + expected_code_path = true; + } + assert(expected_code_path); + }); } } diff --git a/src/test/fuzz/connman.cpp b/src/test/fuzz/connman.cpp index f2c52f7a37b6..13de7604bd37 100644 --- a/src/test/fuzz/connman.cpp +++ b/src/test/fuzz/connman.cpp @@ -30,90 +30,87 @@ FUZZ_TARGET_INIT(connman, initialize_connman) CSubNet random_subnet; std::string random_string; while (fuzzed_data_provider.ConsumeBool()) { - switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 23)) { - case 0: - random_netaddr = ConsumeNetAddr(fuzzed_data_provider); - break; - case 1: - random_subnet = ConsumeSubNet(fuzzed_data_provider); - break; - case 2: - random_string = fuzzed_data_provider.ConsumeRandomLengthString(64); - break; - case 3: - connman.AddNode(random_string); - break; - case 4: - connman.CheckIncomingNonce(fuzzed_data_provider.ConsumeIntegral()); - break; - case 5: - connman.DisconnectNode(fuzzed_data_provider.ConsumeIntegral()); - break; - case 6: - connman.DisconnectNode(random_netaddr); - break; - case 7: - connman.DisconnectNode(random_string); - break; - case 8: - connman.DisconnectNode(random_subnet); - break; - case 9: - connman.ForEachNode([](auto) {}); - break; - case 10: - connman.ForEachNodeThen([](auto) {}, []() {}); - break; - case 11: - (void)connman.ForNode(fuzzed_data_provider.ConsumeIntegral(), [&](auto) { return fuzzed_data_provider.ConsumeBool(); }); - break; - case 12: - (void)connman.GetAddresses(); - break; - case 13: { - (void)connman.GetAddresses(); - break; - } - case 14: - (void)connman.GetDeterministicRandomizer(fuzzed_data_provider.ConsumeIntegral()); - break; - case 15: - (void)connman.GetNodeCount(fuzzed_data_provider.PickValueInArray({CConnman::CONNECTIONS_NONE, CConnman::CONNECTIONS_IN, CConnman::CONNECTIONS_OUT, CConnman::CONNECTIONS_ALL})); - break; - case 16: - (void)connman.OutboundTargetReached(fuzzed_data_provider.ConsumeBool()); - break; - case 17: - // Limit now to int32_t to avoid signed integer overflow - (void)connman.PoissonNextSendInbound(fuzzed_data_provider.ConsumeIntegral(), fuzzed_data_provider.ConsumeIntegral()); - break; - case 18: { - CSerializedNetMsg serialized_net_msg; - serialized_net_msg.command = fuzzed_data_provider.ConsumeRandomLengthString(CMessageHeader::COMMAND_SIZE); - serialized_net_msg.data = ConsumeRandomLengthByteVector(fuzzed_data_provider); - connman.PushMessage(&random_node, std::move(serialized_net_msg)); - break; - } - case 19: - connman.RemoveAddedNode(random_string); - break; - case 20: { - const std::vector asmap = ConsumeRandomLengthBitVector(fuzzed_data_provider); - if (SanityCheckASMap(asmap)) { - connman.SetAsmap(asmap); - } - break; - } - case 21: - connman.SetBestHeight(fuzzed_data_provider.ConsumeIntegral()); - break; - case 22: - connman.SetNetworkActive(fuzzed_data_provider.ConsumeBool()); - break; - case 23: - connman.SetTryNewOutboundPeer(fuzzed_data_provider.ConsumeBool()); - break; - } + CallOneOf( + fuzzed_data_provider, + [&] { + random_netaddr = ConsumeNetAddr(fuzzed_data_provider); + }, + [&] { + random_subnet = ConsumeSubNet(fuzzed_data_provider); + }, + [&] { + random_string = fuzzed_data_provider.ConsumeRandomLengthString(64); + }, + [&] { + connman.AddNode(random_string); + }, + [&] { + connman.CheckIncomingNonce(fuzzed_data_provider.ConsumeIntegral()); + }, + [&] { + connman.DisconnectNode(fuzzed_data_provider.ConsumeIntegral()); + }, + [&] { + connman.DisconnectNode(random_netaddr); + }, + [&] { + connman.DisconnectNode(random_string); + }, + [&] { + connman.DisconnectNode(random_subnet); + }, + [&] { + connman.ForEachNode([](auto) {}); + }, + [&] { + connman.ForEachNodeThen([](auto) {}, []() {}); + }, + [&] { + (void)connman.ForNode(fuzzed_data_provider.ConsumeIntegral(), [&](auto) { return fuzzed_data_provider.ConsumeBool(); }); + }, + [&] { + (void)connman.GetAddresses(); + }, + [&] { + (void)connman.GetAddresses(); + }, + [&] { + (void)connman.GetDeterministicRandomizer(fuzzed_data_provider.ConsumeIntegral()); + }, + [&] { + (void)connman.GetNodeCount(fuzzed_data_provider.PickValueInArray({CConnman::CONNECTIONS_NONE, CConnman::CONNECTIONS_IN, CConnman::CONNECTIONS_OUT, CConnman::CONNECTIONS_ALL})); + }, + [&] { + (void)connman.OutboundTargetReached(fuzzed_data_provider.ConsumeBool()); + }, + [&] { + // Limit now to int32_t to avoid signed integer overflow + (void)connman.PoissonNextSendInbound(fuzzed_data_provider.ConsumeIntegral(), fuzzed_data_provider.ConsumeIntegral()); + }, + [&] { + CSerializedNetMsg serialized_net_msg; + serialized_net_msg.command = fuzzed_data_provider.ConsumeRandomLengthString(CMessageHeader::COMMAND_SIZE); + serialized_net_msg.data = ConsumeRandomLengthByteVector(fuzzed_data_provider); + connman.PushMessage(&random_node, std::move(serialized_net_msg)); + }, + [&] { + connman.RemoveAddedNode(random_string); + }, + [&] { + const std::vector asmap = ConsumeRandomLengthBitVector(fuzzed_data_provider); + if (SanityCheckASMap(asmap)) { + connman.SetAsmap(asmap); + } + }, + [&] { + connman.SetBestHeight(fuzzed_data_provider.ConsumeIntegral()); + }, + [&] { + connman.SetNetworkActive(fuzzed_data_provider.ConsumeBool()); + }, + [&] { + connman.SetTryNewOutboundPeer(fuzzed_data_provider.ConsumeBool()); + }); } (void)connman.GetAddedNodeInfo(); (void)connman.GetBestHeight(); diff --git a/src/test/fuzz/crypto.cpp b/src/test/fuzz/crypto.cpp index 08cfa823a91c..901378d66079 100644 --- a/src/test/fuzz/crypto.cpp +++ b/src/test/fuzz/crypto.cpp @@ -37,97 +37,84 @@ FUZZ_TARGET(crypto) CSipHasher sip_hasher{fuzzed_data_provider.ConsumeIntegral(), fuzzed_data_provider.ConsumeIntegral()}; while (fuzzed_data_provider.ConsumeBool()) { - switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 2)) { - case 0: { - if (fuzzed_data_provider.ConsumeBool()) { - data = ConsumeRandomLengthByteVector(fuzzed_data_provider); - if (data.empty()) { - data.resize(fuzzed_data_provider.ConsumeIntegralInRange(1, 4096), fuzzed_data_provider.ConsumeIntegral()); + CallOneOf( + fuzzed_data_provider, + [&] { + if (fuzzed_data_provider.ConsumeBool()) { + data = ConsumeRandomLengthByteVector(fuzzed_data_provider); + if (data.empty()) { + data.resize(fuzzed_data_provider.ConsumeIntegralInRange(1, 4096), fuzzed_data_provider.ConsumeIntegral()); + } } - } - (void)hash160.Write(data); - (void)hash256.Write(data); - (void)hmac_sha256.Write(data.data(), data.size()); - (void)hmac_sha512.Write(data.data(), data.size()); - (void)ripemd160.Write(data.data(), data.size()); - (void)sha1.Write(data.data(), data.size()); - (void)sha256.Write(data.data(), data.size()); - (void)sha3.Write(data); - (void)sha512.Write(data.data(), data.size()); - (void)sip_hasher.Write(data.data(), data.size()); + (void)hash160.Write(data); + (void)hash256.Write(data); + (void)hmac_sha256.Write(data.data(), data.size()); + (void)hmac_sha512.Write(data.data(), data.size()); + (void)ripemd160.Write(data.data(), data.size()); + (void)sha1.Write(data.data(), data.size()); + (void)sha256.Write(data.data(), data.size()); + (void)sha3.Write(data); + (void)sha512.Write(data.data(), data.size()); + (void)sip_hasher.Write(data.data(), data.size()); - (void)Hash160(data); - (void)Hash160(data.begin(), data.end()); - (void)sha512.Size(); - break; - } - case 1: { - (void)hash160.Reset(); - (void)hash256.Reset(); - (void)ripemd160.Reset(); - (void)sha1.Reset(); - (void)sha256.Reset(); - (void)sha3.Reset(); - (void)sha512.Reset(); - break; - } - case 2: { - switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 9)) { - case 0: { - data.resize(CHash160::OUTPUT_SIZE); - hash160.Finalize(data); - break; - } - case 1: { - data.resize(CHash256::OUTPUT_SIZE); - hash256.Finalize(data); - break; - } - case 2: { - data.resize(CHMAC_SHA256::OUTPUT_SIZE); - hmac_sha256.Finalize(data.data()); - break; - } - case 3: { - data.resize(CHMAC_SHA512::OUTPUT_SIZE); - hmac_sha512.Finalize(data.data()); - break; - } - case 4: { - data.resize(CRIPEMD160::OUTPUT_SIZE); - ripemd160.Finalize(data.data()); - break; - } - case 5: { - data.resize(CSHA1::OUTPUT_SIZE); - sha1.Finalize(data.data()); - break; - } - case 6: { - data.resize(CSHA256::OUTPUT_SIZE); - sha256.Finalize(data.data()); - break; - } - case 7: { - data.resize(CSHA512::OUTPUT_SIZE); - sha512.Finalize(data.data()); - break; - } - case 8: { - data.resize(1); - data[0] = sip_hasher.Finalize() % 256; - break; - } - case 9: { - data.resize(SHA3_256::OUTPUT_SIZE); - sha3.Finalize(data); - break; - } - } - break; - } - } + (void)Hash160(data); + (void)Hash160(data.begin(), data.end()); + (void)sha512.Size(); + }, + [&] { + (void)hash160.Reset(); + (void)hash256.Reset(); + (void)ripemd160.Reset(); + (void)sha1.Reset(); + (void)sha256.Reset(); + (void)sha3.Reset(); + (void)sha512.Reset(); + }, + [&] { + CallOneOf( + fuzzed_data_provider, + [&] { + data.resize(CHash160::OUTPUT_SIZE); + hash160.Finalize(data); + }, + [&] { + data.resize(CHash256::OUTPUT_SIZE); + hash256.Finalize(data); + }, + [&] { + data.resize(CHMAC_SHA256::OUTPUT_SIZE); + hmac_sha256.Finalize(data.data()); + }, + [&] { + data.resize(CHMAC_SHA512::OUTPUT_SIZE); + hmac_sha512.Finalize(data.data()); + }, + [&] { + data.resize(CRIPEMD160::OUTPUT_SIZE); + ripemd160.Finalize(data.data()); + }, + [&] { + data.resize(CSHA1::OUTPUT_SIZE); + sha1.Finalize(data.data()); + }, + [&] { + data.resize(CSHA256::OUTPUT_SIZE); + sha256.Finalize(data.data()); + }, + [&] { + data.resize(CSHA512::OUTPUT_SIZE); + sha512.Finalize(data.data()); + }, + [&] { + data.resize(1); + data[0] = sip_hasher.Finalize() % 256; + }, + [&] { + data.resize(SHA3_256::OUTPUT_SIZE); + sha3.Finalize(data); + }); + }); } if (fuzzed_data_provider.ConsumeBool()) { uint64_t state[25]; diff --git a/src/test/fuzz/crypto_chacha20.cpp b/src/test/fuzz/crypto_chacha20.cpp index d751466f1157..bb8dd4594ff9 100644 --- a/src/test/fuzz/crypto_chacha20.cpp +++ b/src/test/fuzz/crypto_chacha20.cpp @@ -20,31 +20,26 @@ FUZZ_TARGET(crypto_chacha20) chacha20 = ChaCha20{key.data(), key.size()}; } while (fuzzed_data_provider.ConsumeBool()) { - switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 4)) { - case 0: { - const std::vector key = ConsumeFixedLengthByteVector(fuzzed_data_provider, fuzzed_data_provider.ConsumeIntegralInRange(16, 32)); - chacha20.SetKey(key.data(), key.size()); - break; - } - case 1: { - chacha20.SetIV(fuzzed_data_provider.ConsumeIntegral()); - break; - } - case 2: { - chacha20.Seek(fuzzed_data_provider.ConsumeIntegral()); - break; - } - case 3: { - std::vector output(fuzzed_data_provider.ConsumeIntegralInRange(0, 4096)); - chacha20.Keystream(output.data(), output.size()); - break; - } - case 4: { - std::vector output(fuzzed_data_provider.ConsumeIntegralInRange(0, 4096)); - const std::vector input = ConsumeFixedLengthByteVector(fuzzed_data_provider, output.size()); - chacha20.Crypt(input.data(), output.data(), input.size()); - break; - } - } + CallOneOf( + fuzzed_data_provider, + [&] { + const std::vector key = ConsumeFixedLengthByteVector(fuzzed_data_provider, fuzzed_data_provider.ConsumeIntegralInRange(16, 32)); + chacha20.SetKey(key.data(), key.size()); + }, + [&] { + chacha20.SetIV(fuzzed_data_provider.ConsumeIntegral()); + }, + [&] { + chacha20.Seek(fuzzed_data_provider.ConsumeIntegral()); + }, + [&] { + std::vector output(fuzzed_data_provider.ConsumeIntegralInRange(0, 4096)); + chacha20.Keystream(output.data(), output.size()); + }, + [&] { + std::vector output(fuzzed_data_provider.ConsumeIntegralInRange(0, 4096)); + const std::vector input = ConsumeFixedLengthByteVector(fuzzed_data_provider, output.size()); + chacha20.Crypt(input.data(), output.data(), input.size()); + }); } } diff --git a/src/test/fuzz/crypto_chacha20_poly1305_aead.cpp b/src/test/fuzz/crypto_chacha20_poly1305_aead.cpp index 631af9c70dd0..1f122082b21d 100644 --- a/src/test/fuzz/crypto_chacha20_poly1305_aead.cpp +++ b/src/test/fuzz/crypto_chacha20_poly1305_aead.cpp @@ -29,44 +29,37 @@ FUZZ_TARGET(crypto_chacha20_poly1305_aead) std::vector out(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0); bool is_encrypt = fuzzed_data_provider.ConsumeBool(); while (fuzzed_data_provider.ConsumeBool()) { - switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 6)) { - case 0: { - buffer_size = fuzzed_data_provider.ConsumeIntegralInRange(64, 4096); - in = std::vector(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0); - out = std::vector(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0); - break; - } - case 1: { - (void)aead.Crypt(seqnr_payload, seqnr_aad, aad_pos, out.data(), out.size(), in.data(), buffer_size, is_encrypt); - break; - } - case 2: { - uint32_t len = 0; - const bool ok = aead.GetLength(&len, seqnr_aad, aad_pos, in.data()); - assert(ok); - break; - } - case 3: { - seqnr_payload += 1; - aad_pos += CHACHA20_POLY1305_AEAD_AAD_LEN; - if (aad_pos + CHACHA20_POLY1305_AEAD_AAD_LEN > CHACHA20_ROUND_OUTPUT) { - aad_pos = 0; - seqnr_aad += 1; - } - break; - } - case 4: { - seqnr_payload = fuzzed_data_provider.ConsumeIntegral(); - break; - } - case 5: { - seqnr_aad = fuzzed_data_provider.ConsumeIntegral(); - break; - } - case 6: { - is_encrypt = fuzzed_data_provider.ConsumeBool(); - break; - } - } + CallOneOf( + fuzzed_data_provider, + [&] { + buffer_size = fuzzed_data_provider.ConsumeIntegralInRange(64, 4096); + in = std::vector(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0); + out = std::vector(buffer_size + CHACHA20_POLY1305_AEAD_AAD_LEN + POLY1305_TAGLEN, 0); + }, + [&] { + (void)aead.Crypt(seqnr_payload, seqnr_aad, aad_pos, out.data(), out.size(), in.data(), buffer_size, is_encrypt); + }, + [&] { + uint32_t len = 0; + const bool ok = aead.GetLength(&len, seqnr_aad, aad_pos, in.data()); + assert(ok); + }, + [&] { + seqnr_payload += 1; + aad_pos += CHACHA20_POLY1305_AEAD_AAD_LEN; + if (aad_pos + CHACHA20_POLY1305_AEAD_AAD_LEN > CHACHA20_ROUND_OUTPUT) { + aad_pos = 0; + seqnr_aad += 1; + } + }, + [&] { + seqnr_payload = fuzzed_data_provider.ConsumeIntegral(); + }, + [&] { + seqnr_aad = fuzzed_data_provider.ConsumeIntegral(); + }, + [&] { + is_encrypt = fuzzed_data_provider.ConsumeBool(); + }); } } diff --git a/src/test/fuzz/load_external_block_file.cpp b/src/test/fuzz/load_external_block_file.cpp index c428a866310f..bbff8962e107 100644 --- a/src/test/fuzz/load_external_block_file.cpp +++ b/src/test/fuzz/load_external_block_file.cpp @@ -27,5 +27,5 @@ FUZZ_TARGET_INIT(load_external_block_file, initialize_load_external_block_file) return; } FlatFilePos flat_file_pos; - LoadExternalBlockFile(Params(), fuzzed_block_file, fuzzed_data_provider.ConsumeBool() ? &flat_file_pos : nullptr); + ::ChainstateActive().LoadExternalBlockFile(Params(), fuzzed_block_file, fuzzed_data_provider.ConsumeBool() ? &flat_file_pos : nullptr); } diff --git a/src/test/fuzz/merkleblock.cpp b/src/test/fuzz/merkleblock.cpp index 15bcfab3ad80..23e0baa56458 100644 --- a/src/test/fuzz/merkleblock.cpp +++ b/src/test/fuzz/merkleblock.cpp @@ -17,33 +17,31 @@ FUZZ_TARGET(merkleblock) { FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); CPartialMerkleTree partial_merkle_tree; - switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 1)) { - case 0: { - const std::optional opt_partial_merkle_tree = ConsumeDeserializable(fuzzed_data_provider); - if (opt_partial_merkle_tree) { - partial_merkle_tree = *opt_partial_merkle_tree; - } - break; - } - case 1: { - CMerkleBlock merkle_block; - const std::optional opt_block = ConsumeDeserializable(fuzzed_data_provider); - CBloomFilter bloom_filter; - std::set txids; - if (opt_block && !opt_block->vtx.empty()) { - if (fuzzed_data_provider.ConsumeBool()) { - merkle_block = CMerkleBlock{*opt_block, bloom_filter}; - } else if (fuzzed_data_provider.ConsumeBool()) { - while (fuzzed_data_provider.ConsumeBool()) { - txids.insert(ConsumeUInt256(fuzzed_data_provider)); + CallOneOf( + fuzzed_data_provider, + [&] { + const std::optional opt_partial_merkle_tree = ConsumeDeserializable(fuzzed_data_provider); + if (opt_partial_merkle_tree) { + partial_merkle_tree = *opt_partial_merkle_tree; + } + }, + [&] { + CMerkleBlock merkle_block; + const std::optional opt_block = ConsumeDeserializable(fuzzed_data_provider); + CBloomFilter bloom_filter; + std::set txids; + if (opt_block && !opt_block->vtx.empty()) { + if (fuzzed_data_provider.ConsumeBool()) { + merkle_block = CMerkleBlock{*opt_block, bloom_filter}; + } else if (fuzzed_data_provider.ConsumeBool()) { + while (fuzzed_data_provider.ConsumeBool()) { + txids.insert(ConsumeUInt256(fuzzed_data_provider)); + } + merkle_block = CMerkleBlock{*opt_block, txids}; } - merkle_block = CMerkleBlock{*opt_block, txids}; } - } - partial_merkle_tree = merkle_block.txn; - break; - } - } + partial_merkle_tree = merkle_block.txn; + }); (void)partial_merkle_tree.GetNumTransactions(); std::vector matches; std::vector indices; diff --git a/src/test/fuzz/net.cpp b/src/test/fuzz/net.cpp index 7a32948f8b3c..8f44042dd9e8 100644 --- a/src/test/fuzz/net.cpp +++ b/src/test/fuzz/net.cpp @@ -50,99 +50,86 @@ FUZZ_TARGET_INIT(net, initialize_net) fuzzed_data_provider.ConsumeBool() }; while (fuzzed_data_provider.ConsumeBool()) { - switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 12)) { - case 0: { - CAddrMan addrman; - CConnman connman{fuzzed_data_provider.ConsumeIntegral(), fuzzed_data_provider.ConsumeIntegral(), addrman}; - node.CloseSocketDisconnect(&connman); - break; - } - case 1: { - node.MaybeSetAddrName(fuzzed_data_provider.ConsumeRandomLengthString(32)); - break; - } - case 2: { - node.SetSendVersion(fuzzed_data_provider.ConsumeIntegral()); - break; - } - case 3: { - const std::vector asmap = ConsumeRandomLengthBitVector(fuzzed_data_provider); - if (!SanityCheckASMap(asmap)) { - break; - } - CNodeStats stats; - node.copyStats(stats, asmap); - break; - } - case 4: { - node.SetRecvVersion(fuzzed_data_provider.ConsumeIntegral()); - break; - } - case 5: { - const CNode* add_ref_node = node.AddRef(); - assert(add_ref_node == &node); - break; - } - case 6: { - if (node.GetRefCount() > 0) { - node.Release(); - } - break; - } - case 7: { - // if (node.m_addr_known == nullptr) { - // break; - // } - const std::optional addr_opt = ConsumeDeserializable(fuzzed_data_provider); - if (!addr_opt) { - break; - } - node.AddAddressKnown(*addr_opt); - break; - } - case 8: { - // if (node.m_addr_known == nullptr) { - // break; - // } - const std::optional addr_opt = ConsumeDeserializable(fuzzed_data_provider); - if (!addr_opt) { - break; - } - FastRandomContext fast_random_context{ConsumeUInt256(fuzzed_data_provider)}; - node.PushAddress(*addr_opt, fast_random_context); - break; - } - case 9: { - const std::optional inv_opt = ConsumeDeserializable(fuzzed_data_provider); - if (!inv_opt) { - break; - } - // node.AddKnownTx(inv_opt->hash); - break; - } - case 10: { - const std::optional inv_opt = ConsumeDeserializable(fuzzed_data_provider); - if (!inv_opt) { - break; - } - node.PushInventory(*inv_opt); - break; - } - case 11: { - const std::optional service_opt = ConsumeDeserializable(fuzzed_data_provider); - if (!service_opt) { - break; - } - node.SetAddrLocal(*service_opt); - break; - } - case 12: { - const std::vector b = ConsumeRandomLengthByteVector(fuzzed_data_provider); - bool complete; - node.ReceiveMsgBytes((const char*)b.data(), b.size(), complete); - break; - } - } + CallOneOf( + fuzzed_data_provider, + [&] { + CAddrMan addrman; + CConnman connman{fuzzed_data_provider.ConsumeIntegral(), fuzzed_data_provider.ConsumeIntegral(), addrman}; + node.CloseSocketDisconnect(&connman); + }, + [&] { + node.MaybeSetAddrName(fuzzed_data_provider.ConsumeRandomLengthString(32)); + }, + [&] { + node.SetSendVersion(fuzzed_data_provider.ConsumeIntegral()); + }, + [&] { + const std::vector asmap = ConsumeRandomLengthBitVector(fuzzed_data_provider); + if (!SanityCheckASMap(asmap)) { + return; + } + CNodeStats stats; + node.copyStats(stats, asmap); + }, + [&] { + node.SetRecvVersion(fuzzed_data_provider.ConsumeIntegral()); + }, + [&] { + const CNode* add_ref_node = node.AddRef(); + assert(add_ref_node == &node); + }, + [&] { + if (node.GetRefCount() > 0) { + node.Release(); + } + }, + [&] { + // if (node.m_addr_known == nullptr) { + // return; + // } + const std::optional addr_opt = ConsumeDeserializable(fuzzed_data_provider); + if (!addr_opt) { + return; + } + node.AddAddressKnown(*addr_opt); + }, + [&] { + // if (node.m_addr_known == nullptr) { + // return; + // } + const std::optional addr_opt = ConsumeDeserializable(fuzzed_data_provider); + if (!addr_opt) { + return; + } + FastRandomContext fast_random_context{ConsumeUInt256(fuzzed_data_provider)}; + node.PushAddress(*addr_opt, fast_random_context); + }, + [&] { + const std::optional inv_opt = ConsumeDeserializable(fuzzed_data_provider); + if (!inv_opt) { + return; + } + // node.AddKnownTx(inv_opt->hash); + }, + [&] { + const std::optional inv_opt = ConsumeDeserializable(fuzzed_data_provider); + if (!inv_opt) { + return; + } + node.PushInventory(*inv_opt); + }, + [&] { + const std::optional service_opt = ConsumeDeserializable(fuzzed_data_provider); + if (!service_opt) { + return; + } + node.SetAddrLocal(*service_opt); + }, + [&] { + const std::vector b = ConsumeRandomLengthByteVector(fuzzed_data_provider); + bool complete; + node.ReceiveMsgBytes((const char*)b.data(), b.size(), complete); + }); } (void)node.GetAddrLocal(); diff --git a/src/test/fuzz/policy_estimator.cpp b/src/test/fuzz/policy_estimator.cpp index 451d4a3b5b60..8c77391e99f7 100644 --- a/src/test/fuzz/policy_estimator.cpp +++ b/src/test/fuzz/policy_estimator.cpp @@ -19,46 +19,42 @@ FUZZ_TARGET(policy_estimator) FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); CBlockPolicyEstimator block_policy_estimator; while (fuzzed_data_provider.ConsumeBool()) { - switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 3)) { - case 0: { - const std::optional mtx = ConsumeDeserializable(fuzzed_data_provider); - if (!mtx) { - break; - } - const CTransaction tx{*mtx}; - block_policy_estimator.processTransaction(ConsumeTxMemPoolEntry(fuzzed_data_provider, tx), fuzzed_data_provider.ConsumeBool()); - if (fuzzed_data_provider.ConsumeBool()) { - (void)block_policy_estimator.removeTx(tx.GetHash(), /* inBlock */ fuzzed_data_provider.ConsumeBool()); - } - break; - } - case 1: { - std::vector mempool_entries; - while (fuzzed_data_provider.ConsumeBool()) { + CallOneOf( + fuzzed_data_provider, + [&] { const std::optional mtx = ConsumeDeserializable(fuzzed_data_provider); if (!mtx) { - break; + return; } const CTransaction tx{*mtx}; - mempool_entries.push_back(ConsumeTxMemPoolEntry(fuzzed_data_provider, tx)); - } - std::vector ptrs; - ptrs.reserve(mempool_entries.size()); - for (const CTxMemPoolEntry& mempool_entry : mempool_entries) { - ptrs.push_back(&mempool_entry); - } - block_policy_estimator.processBlock(fuzzed_data_provider.ConsumeIntegral(), ptrs); - break; - } - case 2: { - (void)block_policy_estimator.removeTx(ConsumeUInt256(fuzzed_data_provider), /* inBlock */ fuzzed_data_provider.ConsumeBool()); - break; - } - case 3: { - block_policy_estimator.FlushUnconfirmed(); - break; - } - } + block_policy_estimator.processTransaction(ConsumeTxMemPoolEntry(fuzzed_data_provider, tx), fuzzed_data_provider.ConsumeBool()); + if (fuzzed_data_provider.ConsumeBool()) { + (void)block_policy_estimator.removeTx(tx.GetHash(), /* inBlock */ fuzzed_data_provider.ConsumeBool()); + } + }, + [&] { + std::vector mempool_entries; + while (fuzzed_data_provider.ConsumeBool()) { + const std::optional mtx = ConsumeDeserializable(fuzzed_data_provider); + if (!mtx) { + break; + } + const CTransaction tx{*mtx}; + mempool_entries.push_back(ConsumeTxMemPoolEntry(fuzzed_data_provider, tx)); + } + std::vector ptrs; + ptrs.reserve(mempool_entries.size()); + for (const CTxMemPoolEntry& mempool_entry : mempool_entries) { + ptrs.push_back(&mempool_entry); + } + block_policy_estimator.processBlock(fuzzed_data_provider.ConsumeIntegral(), ptrs); + }, + [&] { + (void)block_policy_estimator.removeTx(ConsumeUInt256(fuzzed_data_provider), /* inBlock */ fuzzed_data_provider.ConsumeBool()); + }, + [&] { + block_policy_estimator.FlushUnconfirmed(); + }); (void)block_policy_estimator.estimateFee(fuzzed_data_provider.ConsumeIntegral()); EstimationResult result; (void)block_policy_estimator.estimateRawFee(fuzzed_data_provider.ConsumeIntegral(), fuzzed_data_provider.ConsumeFloatingPoint(), fuzzed_data_provider.PickValueInArray({FeeEstimateHorizon::SHORT_HALFLIFE, FeeEstimateHorizon::MED_HALFLIFE, FeeEstimateHorizon::LONG_HALFLIFE}), fuzzed_data_provider.ConsumeBool() ? &result : nullptr); diff --git a/src/test/fuzz/rolling_bloom_filter.cpp b/src/test/fuzz/rolling_bloom_filter.cpp index 6087ee964a99..2a08b45aa324 100644 --- a/src/test/fuzz/rolling_bloom_filter.cpp +++ b/src/test/fuzz/rolling_bloom_filter.cpp @@ -22,29 +22,27 @@ FUZZ_TARGET(rolling_bloom_filter) fuzzed_data_provider.ConsumeIntegralInRange(1, 1000), 0.999 / fuzzed_data_provider.ConsumeIntegralInRange(1, std::numeric_limits::max())}; while (fuzzed_data_provider.remaining_bytes() > 0) { - switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 2)) { - case 0: { - const std::vector b = ConsumeRandomLengthByteVector(fuzzed_data_provider); - (void)rolling_bloom_filter.contains(b); - rolling_bloom_filter.insert(b); - const bool present = rolling_bloom_filter.contains(b); - assert(present); - break; - } - case 1: { - const std::optional u256 = ConsumeDeserializable(fuzzed_data_provider); - if (!u256) { - break; - } - (void)rolling_bloom_filter.contains(*u256); - rolling_bloom_filter.insert(*u256); - const bool present = rolling_bloom_filter.contains(*u256); - assert(present); - break; - } - case 2: - rolling_bloom_filter.reset(); - break; - } + CallOneOf( + fuzzed_data_provider, + [&] { + const std::vector b = ConsumeRandomLengthByteVector(fuzzed_data_provider); + (void)rolling_bloom_filter.contains(b); + rolling_bloom_filter.insert(b); + const bool present = rolling_bloom_filter.contains(b); + assert(present); + }, + [&] { + const std::optional u256 = ConsumeDeserializable(fuzzed_data_provider); + if (!u256) { + return; + } + (void)rolling_bloom_filter.contains(*u256); + rolling_bloom_filter.insert(*u256); + const bool present = rolling_bloom_filter.contains(*u256); + assert(present); + }, + [&] { + rolling_bloom_filter.reset(); + }); } } diff --git a/src/test/fuzz/script_bitcoin_consensus.cpp b/src/test/fuzz/script_bitcoin_consensus.cpp index 94ee91d9da23..f2ab1a2ebb57 100644 --- a/src/test/fuzz/script_bitcoin_consensus.cpp +++ b/src/test/fuzz/script_bitcoin_consensus.cpp @@ -17,7 +17,6 @@ FUZZ_TARGET(script_bitcoin_consensus) FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); const std::vector random_bytes_1 = ConsumeRandomLengthByteVector(fuzzed_data_provider); const std::vector random_bytes_2 = ConsumeRandomLengthByteVector(fuzzed_data_provider); - const CAmount money = ConsumeMoney(fuzzed_data_provider); dashconsensus_error err; dashconsensus_error* err_p = fuzzed_data_provider.ConsumeBool() ? &err : nullptr; const unsigned int n_in = fuzzed_data_provider.ConsumeIntegral(); diff --git a/src/test/fuzz/script_ops.cpp b/src/test/fuzz/script_ops.cpp index 501e0c8ced90..569e85332652 100644 --- a/src/test/fuzz/script_ops.cpp +++ b/src/test/fuzz/script_ops.cpp @@ -16,49 +16,46 @@ FUZZ_TARGET(script_ops) FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); CScript script = ConsumeScript(fuzzed_data_provider); while (fuzzed_data_provider.remaining_bytes() > 0) { - switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 7)) { - case 0: { - CScript s = ConsumeScript(fuzzed_data_provider); - script = std::move(s); - break; - } - case 1: { - const CScript& s = ConsumeScript(fuzzed_data_provider); - script = s; - break; - } - case 2: - script << fuzzed_data_provider.ConsumeIntegral(); - break; - case 3: - script << ConsumeOpcodeType(fuzzed_data_provider); - break; - case 4: - script << ConsumeScriptNum(fuzzed_data_provider); - break; - case 5: - script << ConsumeRandomLengthByteVector(fuzzed_data_provider); - break; - case 6: - script.clear(); - break; - case 7: { - (void)script.GetSigOpCount(false); - (void)script.GetSigOpCount(true); - (void)script.GetSigOpCount(script); - (void)script.IsPayToScriptHash(); - (void)script.IsPushOnly(); - (void)script.IsUnspendable(); - { - CScript::const_iterator pc = script.begin(); - opcodetype opcode; - (void)script.GetOp(pc, opcode); - std::vector data; - (void)script.GetOp(pc, opcode, data); - (void)script.IsPushOnly(pc); - } - break; - } - } + CallOneOf( + fuzzed_data_provider, + [&] { + CScript s = ConsumeScript(fuzzed_data_provider); + script = std::move(s); + }, + [&] { + const CScript& s = ConsumeScript(fuzzed_data_provider); + script = s; + }, + [&] { + script << fuzzed_data_provider.ConsumeIntegral(); + }, + [&] { + script << ConsumeOpcodeType(fuzzed_data_provider); + }, + [&] { + script << ConsumeScriptNum(fuzzed_data_provider); + }, + [&] { + script << ConsumeRandomLengthByteVector(fuzzed_data_provider); + }, + [&] { + script.clear(); + }, + [&] { + (void)script.GetSigOpCount(false); + (void)script.GetSigOpCount(true); + (void)script.GetSigOpCount(script); + (void)script.IsPayToScriptHash(); + (void)script.IsPushOnly(); + (void)script.IsUnspendable(); + { + CScript::const_iterator pc = script.begin(); + opcodetype opcode; + (void)script.GetOp(pc, opcode); + std::vector data; + (void)script.GetOp(pc, opcode, data); + (void)script.IsPushOnly(pc); + } + }); } } diff --git a/src/test/fuzz/scriptnum_ops.cpp b/src/test/fuzz/scriptnum_ops.cpp index fa5605f7578e..e112c894b531 100644 --- a/src/test/fuzz/scriptnum_ops.cpp +++ b/src/test/fuzz/scriptnum_ops.cpp @@ -29,105 +29,99 @@ FUZZ_TARGET(scriptnum_ops) FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); CScriptNum script_num = ConsumeScriptNum(fuzzed_data_provider); while (fuzzed_data_provider.remaining_bytes() > 0) { - switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 11)) { - case 0: { - const int64_t i = fuzzed_data_provider.ConsumeIntegral(); - assert((script_num == i) != (script_num != i)); - assert((script_num <= i) != (script_num > i)); - assert((script_num >= i) != (script_num < i)); - // Avoid signed integer overflow: - // script/script.h:264:93: runtime error: signed integer overflow: -2261405121394637306 + -9223372036854775802 cannot be represented in type 'long' - if (IsValidAddition(script_num, CScriptNum{i})) { - assert((script_num + i) - i == script_num); - } - // Avoid signed integer overflow: - // script/script.h:265:93: runtime error: signed integer overflow: 9223371895120855039 - -9223372036854710486 cannot be represented in type 'long' - if (IsValidSubtraction(script_num, CScriptNum{i})) { - assert((script_num - i) + i == script_num); - } - break; - } - case 1: { - const CScriptNum random_script_num = ConsumeScriptNum(fuzzed_data_provider); - assert((script_num == random_script_num) != (script_num != random_script_num)); - assert((script_num <= random_script_num) != (script_num > random_script_num)); - assert((script_num >= random_script_num) != (script_num < random_script_num)); - // Avoid signed integer overflow: - // script/script.h:264:93: runtime error: signed integer overflow: -9223126527765971126 + -9223372036854756825 cannot be represented in type 'long' - if (IsValidAddition(script_num, random_script_num)) { - assert((script_num + random_script_num) - random_script_num == script_num); - } - // Avoid signed integer overflow: - // script/script.h:265:93: runtime error: signed integer overflow: 6052837899185946624 - -9223372036854775808 cannot be represented in type 'long' - if (IsValidSubtraction(script_num, random_script_num)) { - assert((script_num - random_script_num) + random_script_num == script_num); - } - break; - } - case 2: { - const CScriptNum random_script_num = ConsumeScriptNum(fuzzed_data_provider); - if (!IsValidAddition(script_num, random_script_num)) { - // Avoid assertion failure: - // ./script/script.h:292: CScriptNum &CScriptNum::operator+=(const int64_t &): Assertion `rhs == 0 || (rhs > 0 && m_value <= std::numeric_limits::max() - rhs) || (rhs < 0 && m_value >= std::numeric_limits::min() - rhs)' failed. - break; - } - script_num += random_script_num; - break; - } - case 3: { - const CScriptNum random_script_num = ConsumeScriptNum(fuzzed_data_provider); - if (!IsValidSubtraction(script_num, random_script_num)) { - // Avoid assertion failure: - // ./script/script.h:300: CScriptNum &CScriptNum::operator-=(const int64_t &): Assertion `rhs == 0 || (rhs > 0 && m_value >= std::numeric_limits::min() + rhs) || (rhs < 0 && m_value <= std::numeric_limits::max() + rhs)' failed. - break; - } - script_num -= random_script_num; - break; - } - case 4: - script_num = script_num & fuzzed_data_provider.ConsumeIntegral(); - break; - case 5: - script_num = script_num & ConsumeScriptNum(fuzzed_data_provider); - break; - case 6: - script_num &= ConsumeScriptNum(fuzzed_data_provider); - break; - case 7: - if (script_num == CScriptNum{std::numeric_limits::min()}) { - // Avoid assertion failure: - // ./script/script.h:279: CScriptNum CScriptNum::operator-() const: Assertion `m_value != std::numeric_limits::min()' failed. - break; - } - script_num = -script_num; - break; - case 8: - script_num = fuzzed_data_provider.ConsumeIntegral(); - break; - case 9: { - const int64_t random_integer = fuzzed_data_provider.ConsumeIntegral(); - if (!IsValidAddition(script_num, CScriptNum{random_integer})) { - // Avoid assertion failure: - // ./script/script.h:292: CScriptNum &CScriptNum::operator+=(const int64_t &): Assertion `rhs == 0 || (rhs > 0 && m_value <= std::numeric_limits::max() - rhs) || (rhs < 0 && m_value >= std::numeric_limits::min() - rhs)' failed. - break; - } - script_num += random_integer; - break; - } - case 10: { - const int64_t random_integer = fuzzed_data_provider.ConsumeIntegral(); - if (!IsValidSubtraction(script_num, CScriptNum{random_integer})) { - // Avoid assertion failure: - // ./script/script.h:300: CScriptNum &CScriptNum::operator-=(const int64_t &): Assertion `rhs == 0 || (rhs > 0 && m_value >= std::numeric_limits::min() + rhs) || (rhs < 0 && m_value <= std::numeric_limits::max() + rhs)' failed. - break; - } - script_num -= random_integer; - break; - } - case 11: - script_num &= fuzzed_data_provider.ConsumeIntegral(); - break; - } + CallOneOf( + fuzzed_data_provider, + [&] { + const int64_t i = fuzzed_data_provider.ConsumeIntegral(); + assert((script_num == i) != (script_num != i)); + assert((script_num <= i) != (script_num > i)); + assert((script_num >= i) != (script_num < i)); + // Avoid signed integer overflow: + // script/script.h:264:93: runtime error: signed integer overflow: -2261405121394637306 + -9223372036854775802 cannot be represented in type 'long' + if (IsValidAddition(script_num, CScriptNum{i})) { + assert((script_num + i) - i == script_num); + } + // Avoid signed integer overflow: + // script/script.h:265:93: runtime error: signed integer overflow: 9223371895120855039 - -9223372036854710486 cannot be represented in type 'long' + if (IsValidSubtraction(script_num, CScriptNum{i})) { + assert((script_num - i) + i == script_num); + } + }, + [&] { + const CScriptNum random_script_num = ConsumeScriptNum(fuzzed_data_provider); + assert((script_num == random_script_num) != (script_num != random_script_num)); + assert((script_num <= random_script_num) != (script_num > random_script_num)); + assert((script_num >= random_script_num) != (script_num < random_script_num)); + // Avoid signed integer overflow: + // script/script.h:264:93: runtime error: signed integer overflow: -9223126527765971126 + -9223372036854756825 cannot be represented in type 'long' + if (IsValidAddition(script_num, random_script_num)) { + assert((script_num + random_script_num) - random_script_num == script_num); + } + // Avoid signed integer overflow: + // script/script.h:265:93: runtime error: signed integer overflow: 6052837899185946624 - -9223372036854775808 cannot be represented in type 'long' + if (IsValidSubtraction(script_num, random_script_num)) { + assert((script_num - random_script_num) + random_script_num == script_num); + } + }, + [&] { + const CScriptNum random_script_num = ConsumeScriptNum(fuzzed_data_provider); + if (!IsValidAddition(script_num, random_script_num)) { + // Avoid assertion failure: + // ./script/script.h:292: CScriptNum &CScriptNum::operator+=(const int64_t &): Assertion `rhs == 0 || (rhs > 0 && m_value <= std::numeric_limits::max() - rhs) || (rhs < 0 && m_value >= std::numeric_limits::min() - rhs)' failed. + return; + } + script_num += random_script_num; + }, + [&] { + const CScriptNum random_script_num = ConsumeScriptNum(fuzzed_data_provider); + if (!IsValidSubtraction(script_num, random_script_num)) { + // Avoid assertion failure: + // ./script/script.h:300: CScriptNum &CScriptNum::operator-=(const int64_t &): Assertion `rhs == 0 || (rhs > 0 && m_value >= std::numeric_limits::min() + rhs) || (rhs < 0 && m_value <= std::numeric_limits::max() + rhs)' failed. + return; + } + script_num -= random_script_num; + }, + [&] { + script_num = script_num & fuzzed_data_provider.ConsumeIntegral(); + }, + [&] { + script_num = script_num & ConsumeScriptNum(fuzzed_data_provider); + }, + [&] { + script_num &= ConsumeScriptNum(fuzzed_data_provider); + }, + [&] { + if (script_num == CScriptNum{std::numeric_limits::min()}) { + // Avoid assertion failure: + // ./script/script.h:279: CScriptNum CScriptNum::operator-() const: Assertion `m_value != std::numeric_limits::min()' failed. + return; + } + script_num = -script_num; + }, + [&] { + script_num = fuzzed_data_provider.ConsumeIntegral(); + }, + [&] { + const int64_t random_integer = fuzzed_data_provider.ConsumeIntegral(); + if (!IsValidAddition(script_num, CScriptNum{random_integer})) { + // Avoid assertion failure: + // ./script/script.h:292: CScriptNum &CScriptNum::operator+=(const int64_t &): Assertion `rhs == 0 || (rhs > 0 && m_value <= std::numeric_limits::max() - rhs) || (rhs < 0 && m_value >= std::numeric_limits::min() - rhs)' failed. + return; + } + script_num += random_integer; + }, + [&] { + const int64_t random_integer = fuzzed_data_provider.ConsumeIntegral(); + if (!IsValidSubtraction(script_num, CScriptNum{random_integer})) { + // Avoid assertion failure: + // ./script/script.h:300: CScriptNum &CScriptNum::operator-=(const int64_t &): Assertion `rhs == 0 || (rhs > 0 && m_value >= std::numeric_limits::min() + rhs) || (rhs < 0 && m_value <= std::numeric_limits::max() + rhs)' failed. + return; + } + script_num -= random_integer; + }, + [&] { + script_num &= fuzzed_data_provider.ConsumeIntegral(); + }); (void)script_num.getint(); // Avoid negation failure: // script/script.h:332:35: runtime error: negation of -9223372036854775808 cannot be represented in type 'int64_t' (aka 'long'); cast to an unsigned type to negate this value to itself diff --git a/src/test/fuzz/strprintf.cpp b/src/test/fuzz/strprintf.cpp index 4af0e750ceb0..b66a7abfb317 100644 --- a/src/test/fuzz/strprintf.cpp +++ b/src/test/fuzz/strprintf.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -109,32 +110,32 @@ FUZZ_TARGET(str_printf) } try { - switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 5)) { - case 0: - (void)strprintf(format_string, fuzzed_data_provider.ConsumeRandomLengthString(32)); - (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeRandomLengthString(32)); - break; - case 1: - (void)strprintf(format_string, fuzzed_data_provider.ConsumeRandomLengthString(32).c_str()); - (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeRandomLengthString(32).c_str()); - break; - case 2: - (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral()); - (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral()); - break; - case 3: - (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral()); - (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral()); - break; - case 4: - (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral()); - (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral()); - break; - case 5: - (void)strprintf(format_string, fuzzed_data_provider.ConsumeBool()); - (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeBool()); - break; - } + CallOneOf( + fuzzed_data_provider, + [&] { + (void)strprintf(format_string, fuzzed_data_provider.ConsumeRandomLengthString(32)); + (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeRandomLengthString(32)); + }, + [&] { + (void)strprintf(format_string, fuzzed_data_provider.ConsumeRandomLengthString(32).c_str()); + (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeRandomLengthString(32).c_str()); + }, + [&] { + (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral()); + (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral()); + }, + [&] { + (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral()); + (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral()); + }, + [&] { + (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral()); + (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral()); + }, + [&] { + (void)strprintf(format_string, fuzzed_data_provider.ConsumeBool()); + (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeBool()); + }); } catch (const tinyformat::format_error&) { } @@ -155,40 +156,40 @@ FUZZ_TARGET(str_printf) } try { - switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 7)) { - case 0: - (void)strprintf(format_string, fuzzed_data_provider.ConsumeFloatingPoint()); - (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeFloatingPoint()); - break; - case 1: - (void)strprintf(format_string, fuzzed_data_provider.ConsumeFloatingPoint()); - (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeFloatingPoint()); - break; - case 2: - (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral()); - (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral()); - break; - case 3: - (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral()); - (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral()); - break; - case 4: - (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral()); - (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral()); - break; - case 5: - (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral()); - (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral()); - break; - case 6: - (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral()); - (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral()); - break; - case 7: - (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral()); - (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral()); - break; - } + CallOneOf( + fuzzed_data_provider, + [&] { + (void)strprintf(format_string, fuzzed_data_provider.ConsumeFloatingPoint()); + (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeFloatingPoint()); + }, + [&] { + (void)strprintf(format_string, fuzzed_data_provider.ConsumeFloatingPoint()); + (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeFloatingPoint()); + }, + [&] { + (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral()); + (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral()); + }, + [&] { + (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral()); + (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral()); + }, + [&] { + (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral()); + (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral()); + }, + [&] { + (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral()); + (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral()); + }, + [&] { + (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral()); + (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral()); + }, + [&] { + (void)strprintf(format_string, fuzzed_data_provider.ConsumeIntegral()); + (void)tinyformat::format(bilingual_string, fuzzed_data_provider.ConsumeIntegral()); + }); } catch (const tinyformat::format_error&) { } } diff --git a/src/test/fuzz/system.cpp b/src/test/fuzz/system.cpp index 70f616ed979f..274a71d8ab3e 100644 --- a/src/test/fuzz/system.cpp +++ b/src/test/fuzz/system.cpp @@ -32,71 +32,63 @@ FUZZ_TARGET(system) } while (fuzzed_data_provider.ConsumeBool()) { - switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 7)) { - case 0: { - args_manager.SelectConfigNetwork(fuzzed_data_provider.ConsumeRandomLengthString(16)); - break; - } - case 1: { - args_manager.SoftSetArg(fuzzed_data_provider.ConsumeRandomLengthString(16), fuzzed_data_provider.ConsumeRandomLengthString(16)); - break; - } - case 2: { - args_manager.ForceSetArg(fuzzed_data_provider.ConsumeRandomLengthString(16), fuzzed_data_provider.ConsumeRandomLengthString(16)); - break; - } - case 3: { - args_manager.SoftSetBoolArg(fuzzed_data_provider.ConsumeRandomLengthString(16), fuzzed_data_provider.ConsumeBool()); - break; - } - case 4: { - const OptionsCategory options_category = fuzzed_data_provider.PickValueInArray({OptionsCategory::OPTIONS, OptionsCategory::CONNECTION, OptionsCategory::WALLET, OptionsCategory::WALLET_DEBUG_TEST, OptionsCategory::ZMQ, OptionsCategory::DEBUG_TEST, OptionsCategory::CHAINPARAMS, OptionsCategory::NODE_RELAY, OptionsCategory::BLOCK_CREATION, OptionsCategory::RPC, OptionsCategory::GUI, OptionsCategory::COMMANDS, OptionsCategory::REGISTER_COMMANDS, OptionsCategory::HIDDEN}); - // Avoid hitting: - // util/system.cpp:425: void ArgsManager::AddArg(const std::string &, const std::string &, unsigned int, const OptionsCategory &): Assertion `ret.second' failed. - const std::string argument_name = GetArgumentName(fuzzed_data_provider.ConsumeRandomLengthString(16)); - if (args_manager.GetArgFlags(argument_name)) { - break; - } - args_manager.AddArg(argument_name, fuzzed_data_provider.ConsumeRandomLengthString(16), fuzzed_data_provider.ConsumeIntegral(), options_category); - break; - } - case 5: { - // Avoid hitting: - // util/system.cpp:425: void ArgsManager::AddArg(const std::string &, const std::string &, unsigned int, const OptionsCategory &): Assertion `ret.second' failed. - const std::vector names = ConsumeRandomLengthStringVector(fuzzed_data_provider); - std::vector hidden_arguments; - for (const std::string& name : names) { - const std::string hidden_argument = GetArgumentName(name); - if (args_manager.GetArgFlags(hidden_argument)) { - continue; + CallOneOf( + fuzzed_data_provider, + [&] { + args_manager.SelectConfigNetwork(fuzzed_data_provider.ConsumeRandomLengthString(16)); + }, + [&] { + args_manager.SoftSetArg(fuzzed_data_provider.ConsumeRandomLengthString(16), fuzzed_data_provider.ConsumeRandomLengthString(16)); + }, + [&] { + args_manager.ForceSetArg(fuzzed_data_provider.ConsumeRandomLengthString(16), fuzzed_data_provider.ConsumeRandomLengthString(16)); + }, + [&] { + args_manager.SoftSetBoolArg(fuzzed_data_provider.ConsumeRandomLengthString(16), fuzzed_data_provider.ConsumeBool()); + }, + [&] { + const OptionsCategory options_category = fuzzed_data_provider.PickValueInArray({OptionsCategory::OPTIONS, OptionsCategory::CONNECTION, OptionsCategory::WALLET, OptionsCategory::WALLET_DEBUG_TEST, OptionsCategory::ZMQ, OptionsCategory::DEBUG_TEST, OptionsCategory::CHAINPARAMS, OptionsCategory::NODE_RELAY, OptionsCategory::BLOCK_CREATION, OptionsCategory::RPC, OptionsCategory::GUI, OptionsCategory::COMMANDS, OptionsCategory::REGISTER_COMMANDS, OptionsCategory::HIDDEN}); + // Avoid hitting: + // util/system.cpp:425: void ArgsManager::AddArg(const std::string &, const std::string &, unsigned int, const OptionsCategory &): Assertion `ret.second' failed. + const std::string argument_name = GetArgumentName(fuzzed_data_provider.ConsumeRandomLengthString(16)); + if (args_manager.GetArgFlags(argument_name) != std::nullopt) { + return; } - if (std::find(hidden_arguments.begin(), hidden_arguments.end(), hidden_argument) != hidden_arguments.end()) { - continue; + args_manager.AddArg(argument_name, fuzzed_data_provider.ConsumeRandomLengthString(16), fuzzed_data_provider.ConsumeIntegral(), options_category); + }, + [&] { + // Avoid hitting: + // util/system.cpp:425: void ArgsManager::AddArg(const std::string &, const std::string &, unsigned int, const OptionsCategory &): Assertion `ret.second' failed. + const std::vector names = ConsumeRandomLengthStringVector(fuzzed_data_provider); + std::vector hidden_arguments; + for (const std::string& name : names) { + const std::string hidden_argument = GetArgumentName(name); + if (args_manager.GetArgFlags(hidden_argument) != std::nullopt) { + continue; + } + if (std::find(hidden_arguments.begin(), hidden_arguments.end(), hidden_argument) != hidden_arguments.end()) { + continue; + } + hidden_arguments.push_back(hidden_argument); } - hidden_arguments.push_back(hidden_argument); - } - args_manager.AddHiddenArgs(hidden_arguments); - break; - } - case 6: { - args_manager.ClearArgs(); - break; - } - case 7: { - const std::vector random_arguments = ConsumeRandomLengthStringVector(fuzzed_data_provider); - std::vector argv; - argv.reserve(random_arguments.size()); - for (const std::string& random_argument : random_arguments) { - argv.push_back(random_argument.c_str()); - } - try { - std::string error; - (void)args_manager.ParseParameters(argv.size(), argv.data(), error); - } catch (const std::logic_error&) { - } - break; - } - } + args_manager.AddHiddenArgs(hidden_arguments); + }, + [&] { + args_manager.ClearArgs(); + }, + [&] { + const std::vector random_arguments = ConsumeRandomLengthStringVector(fuzzed_data_provider); + std::vector argv; + argv.reserve(random_arguments.size()); + for (const std::string& random_argument : random_arguments) { + argv.push_back(random_argument.c_str()); + } + try { + std::string error; + (void)args_manager.ParseParameters(argv.size(), argv.data(), error); + } catch (const std::logic_error&) { + } + }); } const std::string s1 = fuzzed_data_provider.ConsumeRandomLengthString(16); diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h index c5ddecddf64e..0038fd27e4f5 100644 --- a/src/test/fuzz/util.h +++ b/src/test/fuzz/util.h @@ -36,6 +36,17 @@ #include #include +template +void CallOneOf(FuzzedDataProvider& fuzzed_data_provider, Callables... callables) +{ + constexpr size_t call_size{sizeof...(callables)}; + static_assert(call_size >= 1); + const size_t call_index{fuzzed_data_provider.ConsumeIntegralInRange(0, call_size - 1)}; + + size_t i{0}; + return ((i++ == call_index ? callables() : void()), ...); +} + [[ nodiscard ]] inline std::vector ConsumeRandomLengthByteVector(FuzzedDataProvider& fuzzed_data_provider, const size_t max_length = 4096) noexcept { const std::string s = fuzzed_data_provider.ConsumeRandomLengthString(max_length); @@ -157,20 +168,17 @@ template [[ nodiscard ]] inline CTxDestination ConsumeTxDestination(FuzzedDataProvider& fuzzed_data_provider) noexcept { CTxDestination tx_destination; - switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 2)) { - case 0: { - tx_destination = CNoDestination{}; - break; - } - case 1: { - tx_destination = PKHash{ConsumeUInt160(fuzzed_data_provider)}; - break; - } - case 2: { - tx_destination = ScriptHash{ConsumeUInt160(fuzzed_data_provider)}; - break; - } - } + CallOneOf( + fuzzed_data_provider, + [&] { + tx_destination = CNoDestination{}; + }, + [&] { + tx_destination = PKHash{ConsumeUInt160(fuzzed_data_provider)}; + }, + [&] { + tx_destination = ScriptHash{ConsumeUInt160(fuzzed_data_provider)}; + }); return tx_destination; } @@ -316,32 +324,26 @@ class FuzzedFileProvider return nullptr; } std::string mode; - switch (m_fuzzed_data_provider.ConsumeIntegralInRange(0, 5)) { - case 0: { - mode = "r"; - break; - } - case 1: { - mode = "r+"; - break; - } - case 2: { - mode = "w"; - break; - } - case 3: { - mode = "w+"; - break; - } - case 4: { - mode = "a"; - break; - } - case 5: { - mode = "a+"; - break; - } - } + CallOneOf( + m_fuzzed_data_provider, + [&] { + mode = "r"; + }, + [&] { + mode = "r+"; + }, + [&] { + mode = "w"; + }, + [&] { + mode = "w+"; + }, + [&] { + mode = "a"; + }, + [&] { + mode = "a+"; + }); #ifdef _GNU_SOURCE const cookie_io_functions_t io_hooks = { FuzzedFileProvider::read, @@ -439,66 +441,64 @@ class FuzzedAutoFileProvider return {fuzzed_data_provider}; } -#define WRITE_TO_STREAM_CASE(id, type, consume) \ - case id: { \ - type o = consume; \ - stream << o; \ - break; \ +#define WRITE_TO_STREAM_CASE(type, consume) \ + [&] { \ + type o = consume; \ + stream << o; \ } template void WriteToStream(FuzzedDataProvider& fuzzed_data_provider, Stream& stream) noexcept { while (fuzzed_data_provider.ConsumeBool()) { try { - switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 13)) { - WRITE_TO_STREAM_CASE(0, bool, fuzzed_data_provider.ConsumeBool()) - WRITE_TO_STREAM_CASE(1, char, fuzzed_data_provider.ConsumeIntegral()) - WRITE_TO_STREAM_CASE(2, int8_t, fuzzed_data_provider.ConsumeIntegral()) - WRITE_TO_STREAM_CASE(3, uint8_t, fuzzed_data_provider.ConsumeIntegral()) - WRITE_TO_STREAM_CASE(4, int16_t, fuzzed_data_provider.ConsumeIntegral()) - WRITE_TO_STREAM_CASE(5, uint16_t, fuzzed_data_provider.ConsumeIntegral()) - WRITE_TO_STREAM_CASE(6, int32_t, fuzzed_data_provider.ConsumeIntegral()) - WRITE_TO_STREAM_CASE(7, uint32_t, fuzzed_data_provider.ConsumeIntegral()) - WRITE_TO_STREAM_CASE(8, int64_t, fuzzed_data_provider.ConsumeIntegral()) - WRITE_TO_STREAM_CASE(9, uint64_t, fuzzed_data_provider.ConsumeIntegral()) - WRITE_TO_STREAM_CASE(10, float, fuzzed_data_provider.ConsumeFloatingPoint()) - WRITE_TO_STREAM_CASE(11, double, fuzzed_data_provider.ConsumeFloatingPoint()) - WRITE_TO_STREAM_CASE(12, std::string, fuzzed_data_provider.ConsumeRandomLengthString(32)) - WRITE_TO_STREAM_CASE(13, std::vector, ConsumeRandomLengthIntegralVector(fuzzed_data_provider)) - } + CallOneOf( + fuzzed_data_provider, + WRITE_TO_STREAM_CASE(bool, fuzzed_data_provider.ConsumeBool()), + WRITE_TO_STREAM_CASE(char, fuzzed_data_provider.ConsumeIntegral()), + WRITE_TO_STREAM_CASE(int8_t, fuzzed_data_provider.ConsumeIntegral()), + WRITE_TO_STREAM_CASE(uint8_t, fuzzed_data_provider.ConsumeIntegral()), + WRITE_TO_STREAM_CASE(int16_t, fuzzed_data_provider.ConsumeIntegral()), + WRITE_TO_STREAM_CASE(uint16_t, fuzzed_data_provider.ConsumeIntegral()), + WRITE_TO_STREAM_CASE(int32_t, fuzzed_data_provider.ConsumeIntegral()), + WRITE_TO_STREAM_CASE(uint32_t, fuzzed_data_provider.ConsumeIntegral()), + WRITE_TO_STREAM_CASE(int64_t, fuzzed_data_provider.ConsumeIntegral()), + WRITE_TO_STREAM_CASE(uint64_t, fuzzed_data_provider.ConsumeIntegral()), + WRITE_TO_STREAM_CASE(float, fuzzed_data_provider.ConsumeFloatingPoint()), + WRITE_TO_STREAM_CASE(double, fuzzed_data_provider.ConsumeFloatingPoint()), + WRITE_TO_STREAM_CASE(std::string, fuzzed_data_provider.ConsumeRandomLengthString(32)), + WRITE_TO_STREAM_CASE(std::vector, ConsumeRandomLengthIntegralVector(fuzzed_data_provider))); } catch (const std::ios_base::failure&) { break; } } } -#define READ_FROM_STREAM_CASE(id, type) \ - case id: { \ - type o; \ - stream >> o; \ - break; \ +#define READ_FROM_STREAM_CASE(type) \ + [&] { \ + type o; \ + stream >> o; \ } template void ReadFromStream(FuzzedDataProvider& fuzzed_data_provider, Stream& stream) noexcept { while (fuzzed_data_provider.ConsumeBool()) { try { - switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 13)) { - READ_FROM_STREAM_CASE(0, bool) - READ_FROM_STREAM_CASE(1, char) - READ_FROM_STREAM_CASE(2, int8_t) - READ_FROM_STREAM_CASE(3, uint8_t) - READ_FROM_STREAM_CASE(4, int16_t) - READ_FROM_STREAM_CASE(5, uint16_t) - READ_FROM_STREAM_CASE(6, int32_t) - READ_FROM_STREAM_CASE(7, uint32_t) - READ_FROM_STREAM_CASE(8, int64_t) - READ_FROM_STREAM_CASE(9, uint64_t) - READ_FROM_STREAM_CASE(10, float) - READ_FROM_STREAM_CASE(11, double) - READ_FROM_STREAM_CASE(12, std::string) - READ_FROM_STREAM_CASE(13, std::vector) - } + CallOneOf( + fuzzed_data_provider, + READ_FROM_STREAM_CASE(bool), + READ_FROM_STREAM_CASE(char), + READ_FROM_STREAM_CASE(int8_t), + READ_FROM_STREAM_CASE(uint8_t), + READ_FROM_STREAM_CASE(int16_t), + READ_FROM_STREAM_CASE(uint16_t), + READ_FROM_STREAM_CASE(int32_t), + READ_FROM_STREAM_CASE(uint32_t), + READ_FROM_STREAM_CASE(int64_t), + READ_FROM_STREAM_CASE(uint64_t), + READ_FROM_STREAM_CASE(float), + READ_FROM_STREAM_CASE(double), + READ_FROM_STREAM_CASE(std::string), + READ_FROM_STREAM_CASE(std::vector)); } catch (const std::ios_base::failure&) { break; } diff --git a/src/test/miner_tests.cpp b/src/test/miner_tests.cpp index 05c415f25689..91f3a1061371 100644 --- a/src/test/miner_tests.cpp +++ b/src/test/miner_tests.cpp @@ -35,7 +35,7 @@ struct MinerTestingSetup : public TestingSetup { void TestPackageSelection(const CChainParams& chainparams, const CScript& scriptPubKey, const std::vector& txFirst) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_node.mempool->cs); bool TestSequenceLocks(const CTransaction& tx, int flags) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_node.mempool->cs) { - return CheckSequenceLocks(*m_node.mempool, tx, flags); + return CheckSequenceLocks(::ChainstateActive(), *m_node.mempool, tx, flags); } BlockAssembler AssemblerForTest(const CChainParams& params); }; @@ -135,7 +135,7 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co uint256 hashHighFeeTx = tx.GetHash(); m_node.mempool->addUnchecked(entry.Fee(50000).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); - std::unique_ptr pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey); + std::unique_ptr pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey); BOOST_CHECK(pblocktemplate->block.vtx[1]->GetHash() == hashParentTx); BOOST_CHECK(pblocktemplate->block.vtx[2]->GetHash() == hashHighFeeTx); BOOST_CHECK(pblocktemplate->block.vtx[3]->GetHash() == hashMediumFeeTx); @@ -155,7 +155,7 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co tx.vout[0].nValue = 5000000000LL - 1000 - 50000 - feeToUse; uint256 hashLowFeeTx = tx.GetHash(); m_node.mempool->addUnchecked(entry.Fee(feeToUse).FromTx(tx)); - pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey); + pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey); // Verify that the free tx and the low fee tx didn't get selected for (size_t i=0; iblock.vtx.size(); ++i) { BOOST_CHECK(pblocktemplate->block.vtx[i]->GetHash() != hashFreeTx); @@ -169,7 +169,7 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co tx.vout[0].nValue -= 2; // Now we should be just over the min relay fee hashLowFeeTx = tx.GetHash(); m_node.mempool->addUnchecked(entry.Fee(feeToUse+2).FromTx(tx)); - pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey); + pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey); BOOST_CHECK(pblocktemplate->block.vtx[4]->GetHash() == hashFreeTx); BOOST_CHECK(pblocktemplate->block.vtx[5]->GetHash() == hashLowFeeTx); @@ -190,7 +190,7 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co tx.vout[0].nValue = 5000000000LL - 100000000 - feeToUse; uint256 hashLowFeeTx2 = tx.GetHash(); m_node.mempool->addUnchecked(entry.Fee(feeToUse).SpendsCoinbase(false).FromTx(tx)); - pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey); + pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey); // Verify that this tx isn't selected. for (size_t i=0; iblock.vtx.size(); ++i) { @@ -203,7 +203,7 @@ void MinerTestingSetup::TestPackageSelection(const CChainParams& chainparams, co tx.vin[0].prevout.n = 1; tx.vout[0].nValue = 100000000 - 10000; // 10k satoshi fee m_node.mempool->addUnchecked(entry.Fee(10000).FromTx(tx)); - pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey); + pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey); BOOST_CHECK(pblocktemplate->block.vtx[8]->GetHash() == hashLowFeeTx2); } @@ -224,7 +224,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) fCheckpointsEnabled = false; // Simple block creation, nothing special yet: - BOOST_CHECK(pemptyblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pemptyblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey)); // We can't make transactions until we have inputs // Therefore, load 100 blocks :) @@ -272,7 +272,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) LOCK(m_node.mempool->cs); // Just to make sure we can still make simple blocks - BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey)); const CAmount BLOCKSUBSIDY = 500*COIN; const CAmount LOWFEE = CENT; @@ -297,7 +297,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vin[0].prevout.hash = hash; } - BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-blk-sigops")); + BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey), std::runtime_error, HasReason("bad-blk-sigops")); m_node.mempool->clear(); tx.vin[0].prevout.hash = txFirst[0]->GetHash(); @@ -311,7 +311,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).SigOps(20).FromTx(tx)); tx.vin[0].prevout.hash = hash; } - BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey)); m_node.mempool->clear(); // block size > limit @@ -331,13 +331,13 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(spendsCoinbase).FromTx(tx)); tx.vin[0].prevout.hash = hash; } - BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey)); m_node.mempool->clear(); // orphan in *m_node.mempool, template creation fails hash = tx.GetHash(); m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).FromTx(tx)); - BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-txns-inputs-missingorspent")); + BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey), std::runtime_error, HasReason("bad-txns-inputs-missingorspent")); m_node.mempool->clear(); // child with higher feerate than parent @@ -354,7 +354,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vout[0].nValue = tx.vout[0].nValue+BLOCKSUBSIDY-HIGHERFEE; //First txn output + fresh coinbase - new txn fee hash = tx.GetHash(); m_node.mempool->addUnchecked(entry.Fee(HIGHERFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); - BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey)); m_node.mempool->clear(); // coinbase in *m_node.mempool, template creation fails @@ -366,7 +366,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) // give it a fee so it'll get mined m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); // Should throw bad-cb-multiple - BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-cb-multiple")); + BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey), std::runtime_error, HasReason("bad-cb-multiple")); m_node.mempool->clear(); // invalid (pre-p2sh) txn in *m_node.mempool, template creation fails @@ -384,7 +384,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) hash = tx.GetHash(); m_node.mempool->addUnchecked(entry.Fee(LOWFEE).Time(GetTime()).SpendsCoinbase(false).FromTx(tx)); // Should throw block-validation-failed - BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("block-validation-failed")); + BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey), std::runtime_error, HasReason("block-validation-failed")); m_node.mempool->clear(); // double spend txn pair in *m_node.mempool, template creation fails @@ -397,7 +397,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vout[0].scriptPubKey = CScript() << OP_2; hash = tx.GetHash(); m_node.mempool->addUnchecked(entry.Fee(HIGHFEE).Time(GetTime()).SpendsCoinbase(true).FromTx(tx)); - BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey), std::runtime_error, HasReason("bad-txns-inputs-missingorspent")); + BOOST_CHECK_EXCEPTION(AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey), std::runtime_error, HasReason("bad-txns-inputs-missingorspent")); m_node.mempool->clear(); // subsidy changing @@ -413,7 +413,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) // next->BuildSkip(); // ::ChainActive().SetTip(next); // } - //BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); + //BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey)); // // Extend to a 210000-long block chain. // while (::ChainActive().Tip()->nHeight < 210000) { // CBlockIndex* prev = ::ChainActive().Tip(); @@ -425,7 +425,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) // next->BuildSkip(); // ::ChainActive().SetTip(next); // } - //BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(scriptPubKey)); + //BOOST_CHECK(pblocktemplate = BlockAssembler(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey)); // // Delete the dummy blocks again. // while (::ChainActive().Tip()->nHeight > nHeight) { // CBlockIndex* del = ::ChainActive().Tip(); @@ -456,7 +456,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(CheckFinalTx(CTransaction(tx), flags)); // Locktime passes + BOOST_CHECK(CheckFinalTx(::ChainActive().Tip(), CTransaction(tx), flags)); // Locktime passes BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks fail BOOST_CHECK(SequenceLocks(CTransaction(tx), flags, prevheights, CreateBlockIndex(::ChainActive().Tip()->nHeight + 2))); // Sequence locks pass on 2nd block @@ -466,7 +466,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(CheckFinalTx(CTransaction(tx), flags)); // Locktime passes + BOOST_CHECK(CheckFinalTx(::ChainActive().Tip(), CTransaction(tx), flags)); // Locktime passes BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks fail for (int i = 0; i < CBlockIndex::nMedianTimeSpan; i++) @@ -482,7 +482,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.nLockTime = ::ChainActive().Tip()->nHeight + 1; hash = tx.GetHash(); m_node.mempool->addUnchecked(entry.Time(GetTime()).FromTx(tx)); - BOOST_CHECK(!CheckFinalTx(CTransaction(tx), flags)); // Locktime fails + BOOST_CHECK(!CheckFinalTx(::ChainActive().Tip(), CTransaction(tx), flags)); // Locktime fails BOOST_CHECK(TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks pass BOOST_CHECK(IsFinalTx(CTransaction(tx), ::ChainActive().Tip()->nHeight + 2, ::ChainActive().Tip()->GetMedianTimePast())); // Locktime passes on 2nd block @@ -493,7 +493,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(!CheckFinalTx(CTransaction(tx), flags)); // Locktime fails + BOOST_CHECK(!CheckFinalTx(::ChainActive().Tip(), CTransaction(tx), flags)); // Locktime fails BOOST_CHECK(TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks pass BOOST_CHECK(IsFinalTx(CTransaction(tx), ::ChainActive().Tip()->nHeight + 2, ::ChainActive().Tip()->GetMedianTimePast() + 1)); // Locktime passes 1 second later @@ -502,7 +502,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) prevheights[0] = ::ChainActive().Tip()->nHeight + 1; tx.nLockTime = 0; tx.vin[0].nSequence = 0; - BOOST_CHECK(CheckFinalTx(CTransaction(tx), flags)); // Locktime passes + BOOST_CHECK(CheckFinalTx(::ChainActive().Tip(), CTransaction(tx), flags)); // Locktime passes BOOST_CHECK(TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks pass tx.vin[0].nSequence = 1; BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks fail @@ -511,7 +511,7 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) tx.vin[0].nSequence = CTxIn::SEQUENCE_LOCKTIME_TYPE_FLAG | 1; BOOST_CHECK(!TestSequenceLocks(CTransaction(tx), flags)); // Sequence locks fail - BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey)); // None of the of the absolute height/time locked tx should have made // it into the template because we still check IsFinalTx in CreateNewBlock, @@ -532,12 +532,12 @@ BOOST_AUTO_TEST_CASE(CreateNewBlock_validity) SetMockTime(::ChainActive().Tip()->GetMedianTimePast() + 1); - BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(scriptPubKey)); + BOOST_CHECK(pblocktemplate = AssemblerForTest(chainparams).CreateNewBlock(::ChainstateActive(), scriptPubKey)); BOOST_CHECK_EQUAL(pblocktemplate->block.vtx.size(), 5U); } // unlock cs_main while calling InvalidateBlock CValidationState state; - InvalidateBlock(state, chainparams, WITH_LOCK(cs_main, return ::ChainActive().Tip())); + ::ChainstateActive().InvalidateBlock(state, chainparams, WITH_LOCK(cs_main, return ::ChainActive().Tip())); SetMockTime(0); m_node.mempool->clear(); diff --git a/src/test/txvalidation_tests.cpp b/src/test/txvalidation_tests.cpp index 7c597fe2b62c..fdb2b4a8c0f5 100644 --- a/src/test/txvalidation_tests.cpp +++ b/src/test/txvalidation_tests.cpp @@ -38,7 +38,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_reject_coinbase, TestChain100Setup) BOOST_CHECK_EQUAL( false, - AcceptToMemoryPool(*m_node.mempool, state, MakeTransactionRef(coinbaseTx), + AcceptToMemoryPool(::ChainstateActive(), *m_node.mempool, state, MakeTransactionRef(coinbaseTx), nullptr /* pfMissingInputs */, true /* bypass_limits */, 0 /* nAbsurdFee */)); diff --git a/src/test/txvalidationcache_tests.cpp b/src/test/txvalidationcache_tests.cpp index 85db73787b90..39f74e4acfb4 100644 --- a/src/test/txvalidationcache_tests.cpp +++ b/src/test/txvalidationcache_tests.cpp @@ -12,7 +12,7 @@ #include -bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector *pvChecks); +bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector *pvChecks); BOOST_AUTO_TEST_SUITE(txvalidationcache_tests) @@ -28,7 +28,7 @@ BOOST_FIXTURE_TEST_CASE(tx_mempool_block_doublespend, TestChain100Setup) LOCK(cs_main); CValidationState state; - return AcceptToMemoryPool(*m_node.mempool, state, MakeTransactionRef(tx), nullptr /* pfMissingInputs */, + return AcceptToMemoryPool(::ChainstateActive(), *m_node.mempool, state, MakeTransactionRef(tx), nullptr /* pfMissingInputs */, true /* bypass_limits */, 0 /* nAbsurdFee */); }; @@ -118,7 +118,7 @@ static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t fail // script/interpreter.cpp test_flags |= SCRIPT_VERIFY_P2SH; } - bool ret = CheckInputs(tx, state, &::ChainstateActive().CoinsTip(), true, test_flags, true, add_to_cache, txdata, nullptr); + bool ret = CheckInputs(tx, state, &::ChainstateActive().CoinsTip(), test_flags, true, add_to_cache, txdata, nullptr); // CheckInputs should succeed iff test_flags doesn't intersect with // failing_flags bool expected_return_value = !(test_flags & failing_flags); @@ -128,13 +128,13 @@ static void ValidateCheckInputsForAllFlags(const CTransaction &tx, uint32_t fail if (ret && add_to_cache) { // Check that we get a cache hit if the tx was valid std::vector scriptchecks; - BOOST_CHECK(CheckInputs(tx, state, &::ChainstateActive().CoinsTip(), true, test_flags, true, add_to_cache, txdata, &scriptchecks)); + BOOST_CHECK(CheckInputs(tx, state, &::ChainstateActive().CoinsTip(), test_flags, true, add_to_cache, txdata, &scriptchecks)); BOOST_CHECK(scriptchecks.empty()); } else { // Check that we get script executions to check, if the transaction // was invalid, or we didn't add to cache. std::vector scriptchecks; - BOOST_CHECK(CheckInputs(tx, state, &::ChainstateActive().CoinsTip(), true, test_flags, true, add_to_cache, txdata, &scriptchecks)); + BOOST_CHECK(CheckInputs(tx, state, &::ChainstateActive().CoinsTip(), test_flags, true, add_to_cache, txdata, &scriptchecks)); BOOST_CHECK_EQUAL(scriptchecks.size(), tx.vin.size()); } } @@ -196,13 +196,13 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup) CValidationState state; PrecomputedTransactionData ptd_spend_tx; - BOOST_CHECK(!CheckInputs(CTransaction(spend_tx), state, &::ChainstateActive().CoinsTip(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, nullptr)); + BOOST_CHECK(!CheckInputs(CTransaction(spend_tx), state, &::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, nullptr)); // If we call again asking for scriptchecks (as happens in // ConnectBlock), we should add a script check object for this -- we're // not caching invalidity (if that changes, delete this test case). std::vector scriptchecks; - BOOST_CHECK(CheckInputs(CTransaction(spend_tx), state, &::ChainstateActive().CoinsTip(), true, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, &scriptchecks)); + BOOST_CHECK(CheckInputs(CTransaction(spend_tx), state, &::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_DERSIG, true, true, ptd_spend_tx, &scriptchecks)); BOOST_CHECK_EQUAL(scriptchecks.size(), 1U); // Test that CheckInputs returns true iff DERSIG-enforcing flags are @@ -264,7 +264,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup) invalid_with_cltv_tx.vin[0].scriptSig = CScript() << vchSig << 100; CValidationState state; PrecomputedTransactionData txdata; - BOOST_CHECK(CheckInputs(CTransaction(invalid_with_cltv_tx), state, &::ChainstateActive().CoinsTip(), true, SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, true, txdata, nullptr)); + BOOST_CHECK(CheckInputs(CTransaction(invalid_with_cltv_tx), state, &::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_CHECKLOCKTIMEVERIFY, true, true, txdata, nullptr)); } // TEST CHECKSEQUENCEVERIFY @@ -292,7 +292,7 @@ BOOST_FIXTURE_TEST_CASE(checkinputs_test, TestChain100Setup) invalid_with_csv_tx.vin[0].scriptSig = CScript() << vchSig << 100; CValidationState state; PrecomputedTransactionData txdata; - BOOST_CHECK(CheckInputs(CTransaction(invalid_with_csv_tx), state, &::ChainstateActive().CoinsTip(), true, SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true, txdata, nullptr)); + BOOST_CHECK(CheckInputs(CTransaction(invalid_with_csv_tx), state, &::ChainstateActive().CoinsTip(), SCRIPT_VERIFY_CHECKSEQUENCEVERIFY, true, true, txdata, nullptr)); } // TODO: add tests for remaining script flags diff --git a/src/test/util.cpp b/src/test/util.cpp index 4933f52426d1..7c265630248b 100644 --- a/src/test/util.cpp +++ b/src/test/util.cpp @@ -81,7 +81,7 @@ std::shared_ptr PrepareBlock(const NodeContext& node, const CScript& coi assert(node.mempool); auto block = std::make_shared( BlockAssembler{*sporkManager, *governance, *node.llmq_ctx->quorum_block_processor, *node.llmq_ctx->clhandler, *node.llmq_ctx->isman, *node.evodb, *node.mempool, Params()} - .CreateNewBlock(coinbase_scriptPubKey) + .CreateNewBlock(::ChainstateActive(), coinbase_scriptPubKey) ->block); block->nTime = ::ChainActive().Tip()->GetMedianTimePast() + 1; diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index 8cad26fec4be..36f7156f1250 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -216,7 +216,7 @@ ChainTestingSetup::~ChainTestingSetup() m_node.addrman.reset(); m_node.args = nullptr; m_node.banman.reset(); - UnloadBlockIndex(m_node.mempool.get()); + UnloadBlockIndex(m_node.mempool.get(), *m_node.chainman); m_node.mempool.reset(); m_node.scheduler.reset(); m_node.llmq_ctx.reset(); @@ -239,7 +239,7 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector noTxns; CBlock b = CreateAndProcessBlock(noTxns, scriptPubKey); m_coinbase_txns.push_back(b.vtx[0]); @@ -288,8 +287,6 @@ TestChainSetup::TestChainSetup(int blockCount) } } -// Create a new block with just given transactions, coinbase paying to -// scriptPubKey, and try to add it to the current chain. CBlock TestChainSetup::CreateAndProcessBlock(const std::vector& txns, const CScript& scriptPubKey) { const CChainParams& chainparams = Params(); @@ -298,8 +295,7 @@ CBlock TestChainSetup::CreateAndProcessBlock(const std::vector shared_pblock = std::make_shared(block); Assert(m_node.chainman)->ProcessNewBlock(chainparams, shared_pblock, true, nullptr); - CBlock result = block; - return result; + return block; } CBlock TestChainSetup::CreateAndProcessBlock(const std::vector& txns, const CKey& scriptKey) @@ -311,8 +307,12 @@ CBlock TestChainSetup::CreateAndProcessBlock(const std::vector& txns, const CScript& scriptPubKey) { const CChainParams& chainparams = Params(); - std::unique_ptr pblocktemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor, *m_node.llmq_ctx->clhandler, *m_node.llmq_ctx->isman, *m_node.evodb, *m_node.mempool, chainparams).CreateNewBlock(scriptPubKey); - CBlock& block = pblocktemplate->block; + CTxMemPool empty_pool; + CBlock block = BlockAssembler( + *sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor, + *m_node.llmq_ctx->clhandler, *m_node.llmq_ctx->isman, *m_node.evodb, + empty_pool, chainparams + ).CreateNewBlock(::ChainstateActive(), scriptPubKey)->block; std::vector llmqCommitments; for (const auto& tx : block.vtx) { @@ -323,10 +323,13 @@ CBlock TestChainSetup::CreateBlock(const std::vector& txns, // Replace mempool-selected txns with just coinbase plus passed-in txns: block.vtx.resize(1); + Assert(block.vtx.size() == 1); + // Re-add quorum commitments block.vtx.insert(block.vtx.end(), llmqCommitments.begin(), llmqCommitments.end()); - for (const CMutableTransaction& tx : txns) + for (const CMutableTransaction& tx : txns) { block.vtx.push_back(MakeTransactionRef(tx)); + } // Manually update CbTx as we modified the block here if (block.vtx[0]->nType == TRANSACTION_COINBASE) { @@ -373,8 +376,8 @@ TestChainSetup::~TestChainSetup() g_txindex.reset(); } - -CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CMutableTransaction &tx) { +CTxMemPoolEntry TestMemPoolEntryHelper::FromTx(const CMutableTransaction& tx) +{ return FromTx(MakeTransactionRef(tx)); } diff --git a/src/test/util/setup_common.h b/src/test/util/setup_common.h index a009dd857222..d69dd73dbd4d 100644 --- a/src/test/util/setup_common.h +++ b/src/test/util/setup_common.h @@ -115,8 +115,10 @@ struct TestChainSetup : public RegTestingSetup TestChainSetup(int blockCount); ~TestChainSetup(); - // Create a new block with just given transactions, coinbase paying to - // scriptPubKey, and try to add it to the current chain. + /** + * Create a new block with just given transactions, coinbase paying to + * scriptPubKey, and try to add it to the current chain. + */ CBlock CreateAndProcessBlock(const std::vector& txns, const CScript& scriptPubKey); CBlock CreateAndProcessBlock(const std::vector& txns, @@ -130,10 +132,9 @@ struct TestChainSetup : public RegTestingSetup CKey coinbaseKey; // private/public key needed to spend coinbase transactions }; -// -// Testing fixture that pre-creates a -// 100-block REGTEST-mode block chain -// +/** + * Testing fixture that pre-creates a 100-block REGTEST-mode block chain + */ struct TestChain100Setup : public TestChainSetup { TestChain100Setup() : TestChainSetup(100) {} }; diff --git a/src/test/validation_block_tests.cpp b/src/test/validation_block_tests.cpp index 01be24a24aae..becce21a2bba 100644 --- a/src/test/validation_block_tests.cpp +++ b/src/test/validation_block_tests.cpp @@ -75,7 +75,7 @@ std::shared_ptr MinerTestingSetup::Block(const uint256& prev_hash) CScript pubKey; pubKey << i++ << OP_TRUE; - auto ptemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor, *m_node.llmq_ctx->clhandler, *m_node.llmq_ctx->isman, *m_node.evodb, *m_node.mempool, Params()).CreateNewBlock(pubKey); + auto ptemplate = BlockAssembler(*sporkManager, *governance, *m_node.llmq_ctx->quorum_block_processor, *m_node.llmq_ctx->clhandler, *m_node.llmq_ctx->isman, *m_node.evodb, *m_node.mempool, Params()).CreateNewBlock(::ChainstateActive(), pubKey); auto pblock = std::make_shared(ptemplate->block); pblock->hashPrevBlock = prev_hash; pblock->nTime = ++time; @@ -299,6 +299,7 @@ BOOST_AUTO_TEST_CASE(mempool_locks_reorg) CValidationState state; for (const auto& tx : txs) { BOOST_REQUIRE(AcceptToMemoryPool( + ::ChainstateActive(), *m_node.mempool, state, tx, diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 77e5ef90f1b1..e033b595ff4b 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -752,7 +752,7 @@ void CTxMemPool::removeRecursive(const CTransaction &origTx, MemPoolRemovalReaso RemoveStaged(setAllRemoves, false, reason); } -void CTxMemPool::removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMemPoolHeight, int flags) EXCLUSIVE_LOCKS_REQUIRED(cs_main) +void CTxMemPool::removeForReorg(CChainState& active_chainstate, int flags) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { // Remove transactions spending a coinbase which are now immature and no-longer-final transactions AssertLockHeld(cs); @@ -760,8 +760,9 @@ void CTxMemPool::removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMem for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) { const CTransaction& tx = it->GetTx(); LockPoints lp = it->GetLockPoints(); - bool validLP = TestLockPointValidity(&lp); - if (!CheckFinalTx(tx, flags) || !CheckSequenceLocks(*this, tx, flags, &lp, validLP)) { + assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate)); + bool validLP = TestLockPointValidity(active_chainstate.m_chain, &lp); + if (!CheckFinalTx(active_chainstate.m_chain.Tip(), tx, flags) || !CheckSequenceLocks(active_chainstate, *this, tx, flags, &lp, validLP)) { // Note if CheckSequenceLocks fails the LockPoints may still be invalid // So it's critical that we remove the tx and not depend on the LockPoints. txToRemove.insert(it); @@ -770,8 +771,9 @@ void CTxMemPool::removeForReorg(const CCoinsViewCache *pcoins, unsigned int nMem indexed_transaction_set::const_iterator it2 = mapTx.find(txin.prevout.hash); if (it2 != mapTx.end()) continue; - const Coin &coin = pcoins->AccessCoin(txin.prevout); + const Coin &coin = active_chainstate.CoinsTip().AccessCoin(txin.prevout); if (m_check_ratio != 0) assert(!coin.IsSpent()); + unsigned int nMemPoolHeight = active_chainstate.m_chain.Tip()->nHeight + 1; if (coin.IsSpent() || (coin.IsCoinBase() && ((signed long)nMemPoolHeight) - coin.nHeight < COINBASE_MATURITY)) { txToRemove.insert(it); break; @@ -1028,20 +1030,24 @@ static void CheckInputsAndUpdateCoins(const CTransaction& tx, CCoinsViewCache& m UpdateCoins(tx, mempoolDuplicate, std::numeric_limits::max()); } -void CTxMemPool::check(const CCoinsViewCache *pcoins) const +void CTxMemPool::check(CChainState& active_chainstate) const { if (m_check_ratio == 0) return; if (GetRand(m_check_ratio) >= 1) return; + AssertLockHeld(::cs_main); LOCK(cs); LogPrint(BCLog::MEMPOOL, "Checking mempool with %u transactions and %u inputs\n", (unsigned int)mapTx.size(), (unsigned int)mapNextTx.size()); uint64_t checkTotal = 0; uint64_t innerUsage = 0; - CCoinsViewCache mempoolDuplicate(const_cast(pcoins)); - const int64_t spendheight = GetSpendHeight(mempoolDuplicate); + CCoinsViewCache& active_coins_tip = active_chainstate.CoinsTip(); + assert(std::addressof(::ChainstateActive().CoinsTip()) == std::addressof(active_coins_tip)); // TODO: REVIEW-ONLY, REMOVE IN FUTURE COMMIT + CCoinsViewCache mempoolDuplicate(const_cast(&active_coins_tip)); + const int64_t spendheight = active_chainstate.m_chain.Height() + 1; + assert(g_chainman.m_blockman.GetSpendHeight(mempoolDuplicate) == spendheight); // TODO: REVIEW-ONLY, REMOVE IN FUTURE COMMIT std::list waitingOnDependants; for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) { @@ -1064,7 +1070,7 @@ void CTxMemPool::check(const CCoinsViewCache *pcoins) const fDependsWait = true; setParentCheck.insert(it2); } else { - assert(pcoins->HaveCoin(txin.prevout)); + assert(active_coins_tip.HaveCoin(txin.prevout)); } // Check whether its inputs are marked in mapNextTx. auto it3 = mapNextTx.find(txin.prevout); diff --git a/src/txmempool.h b/src/txmempool.h index ce5b4b4f6211..6fa21023a867 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -36,6 +36,7 @@ class CBLSPublicKey; class CBlockIndex; +class CChainState; extern CCriticalSection cs_main; /** Fake height value used in Coin to signify they are only in the memory pool (since 0.8) */ @@ -596,7 +597,7 @@ class CTxMemPool * all inputs are in the mapNextTx array). If sanity-checking is turned off, * check does nothing. */ - void check(const CCoinsViewCache *pcoins) const; + void check(CChainState& active_chainstate) const EXCLUSIVE_LOCKS_REQUIRED(::cs_main); // addUnchecked must updated state for all ancestors of a given transaction, // to track size/count of descendant transactions. First version of @@ -618,7 +619,7 @@ class CTxMemPool bool removeSpentIndex(const uint256 txhash); void removeRecursive(const CTransaction& tx, MemPoolRemovalReason reason) EXCLUSIVE_LOCKS_REQUIRED(cs); - void removeForReorg(const CCoinsViewCache* pcoins, unsigned int nMemPoolHeight, int flags) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main); + void removeForReorg(CChainState& active_chainstate, int flags) EXCLUSIVE_LOCKS_REQUIRED(cs, cs_main); void removeConflicts(const CTransaction& tx) EXCLUSIVE_LOCKS_REQUIRED(cs); void removeProTxPubKeyConflicts(const CTransaction &tx, const CKeyID &keyId) EXCLUSIVE_LOCKS_REQUIRED(cs); void removeProTxPubKeyConflicts(const CTransaction &tx, const CBLSPublicKey &pubKey) EXCLUSIVE_LOCKS_REQUIRED(cs); diff --git a/src/uint256.cpp b/src/uint256.cpp index 696a7081dd8d..8c89e896824c 100644 --- a/src/uint256.cpp +++ b/src/uint256.cpp @@ -40,16 +40,15 @@ void base_blob::SetHex(const char* psz) psz += 2; // hex string to uint - const char* pbegin = psz; - while (::HexDigit(*psz) != -1) - psz++; - psz--; + size_t digits = 0; + while (::HexDigit(psz[digits]) != -1) + digits++; unsigned char* p1 = (unsigned char*)m_data; unsigned char* pend = p1 + WIDTH; - while (psz >= pbegin && p1 < pend) { - *p1 = ::HexDigit(*psz--); - if (psz >= pbegin) { - *p1 |= ((unsigned char)::HexDigit(*psz--) << 4); + while (digits > 0 && p1 < pend) { + *p1 = ::HexDigit(psz[--digits]); + if (digits > 0) { + *p1 |= ((unsigned char)::HexDigit(psz[--digits]) << 4); p1++; } } diff --git a/src/validation.cpp b/src/validation.cpp index 1d4fe13252fe..b725cc1dd952 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -173,17 +173,19 @@ namespace { std::set setDirtyFileInfo; } // anon namespace -CBlockIndex* LookupBlockIndex(const uint256& hash) +CBlockIndex* BlockManager::LookupBlockIndex(const uint256& hash) { AssertLockHeld(cs_main); - BlockMap::const_iterator it = g_chainman.BlockIndex().find(hash); - return it == g_chainman.BlockIndex().end() ? nullptr : it->second; + assert(std::addressof(g_chainman.BlockIndex()) == std::addressof(m_block_index)); + BlockMap::const_iterator it = m_block_index.find(hash); + return it == m_block_index.end() ? nullptr : it->second; } -CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator) +CBlockIndex* BlockManager::FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator) { AssertLockHeld(cs_main); + assert(std::addressof(g_chainman.m_blockman) == std::addressof(*this)); // Find the latest block common to locator and chain - we expect that // locator.vHave is sorted descending by height. for (const uint256& hash : locator.vHave) { @@ -198,17 +200,16 @@ CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& loc std::unique_ptr pblocktree; -// See definition for documentation -static void FindFilesToPruneManual(ChainstateManager& chainman, std::set& setFilesToPrune, int nManualPruneHeight); -static void FindFilesToPrune(ChainstateManager& chainman, std::set& setFilesToPrune, uint64_t nPruneAfterHeight); -bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector *pvChecks = nullptr); +bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector *pvChecks = nullptr); static FILE* OpenUndoFile(const FlatFilePos &pos, bool fReadOnly = false); static FlatFileSeq BlockFileSeq(); static FlatFileSeq UndoFileSeq(); -bool CheckFinalTx(const CTransaction &tx, int flags) +bool CheckFinalTx(const CBlockIndex* active_chain_tip, const CTransaction &tx, int flags) { AssertLockHeld(cs_main); + assert(active_chain_tip); // TODO: Make active_chain_tip a reference + assert(std::addressof(*::ChainActive().Tip()) == std::addressof(*active_chain_tip)); // By convention a negative value for flags indicates that the // current network-enforced consensus rules should be used. In @@ -218,13 +219,13 @@ bool CheckFinalTx(const CTransaction &tx, int flags) // scheduled, so no flags are set. flags = std::max(flags, 0); - // CheckFinalTx() uses ::ChainActive().Height()+1 to evaluate + // CheckFinalTx() uses active_chain_tip.Height()+1 to evaluate // nLockTime because when IsFinalTx() is called within // CBlock::AcceptBlock(), the height of the block *being* // 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 ::ChainActive().Height(). - const int nBlockHeight = ::ChainActive().Height() + 1; + // IsFinalTx() with one more than active_chain_tip.Height(). + 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. @@ -232,13 +233,13 @@ bool CheckFinalTx(const CTransaction &tx, int flags) // chain tip, so we use that to calculate the median time passed to // IsFinalTx() if LOCKTIME_MEDIAN_TIME_PAST is set. const int64_t nBlockTime = (flags & LOCKTIME_MEDIAN_TIME_PAST) - ? ::ChainActive().Tip()->GetMedianTimePast() + ? active_chain_tip->GetMedianTimePast() : GetAdjustedTime(); return IsFinalTx(tx, nBlockHeight, nBlockTime); } -bool TestLockPointValidity(const LockPoints* lp) +bool TestLockPointValidity(CChain& active_chain, const LockPoints* lp) { AssertLockHeld(cs_main); assert(lp); @@ -247,7 +248,8 @@ bool TestLockPointValidity(const LockPoints* lp) if (lp->maxInputBlock) { // Check whether ::ChainActive() is an extension of the block at which the LockPoints // calculation was valid. If not LockPoints are no longer valid - if (!::ChainActive().Contains(lp->maxInputBlock)) { + assert(std::addressof(::ChainActive()) == std::addressof(active_chain)); + if (!active_chain.Contains(lp->maxInputBlock)) { return false; } } @@ -256,22 +258,28 @@ bool TestLockPointValidity(const LockPoints* lp) return true; } -bool CheckSequenceLocks(const CTxMemPool& pool, const CTransaction& tx, int flags, LockPoints* lp, bool useExistingLockPoints) +bool CheckSequenceLocks(CChainState& active_chainstate, + const CTxMemPool& pool, + const CTransaction& tx, + int flags, + LockPoints* lp, + bool useExistingLockPoints) { AssertLockHeld(cs_main); AssertLockHeld(pool.cs); + assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate)); - CBlockIndex* tip = ::ChainActive().Tip(); + CBlockIndex* tip = active_chainstate.m_chain.Tip(); assert(tip != nullptr); CBlockIndex index; index.pprev = tip; - // CheckSequenceLocks() uses ::ChainActive().Height()+1 to evaluate + // CheckSequenceLocks() uses active_chainstate.m_chain.Height()+1 to evaluate // height based locks because when SequenceLocks() is called within // ConnectBlock(), the height of the block *being* // evaluated is what is used. // Thus if we want to know if a transaction can be part of the - // *next* block, we need to use one more than ::ChainActive().Height() + // *next* block, we need to use one more than active_chainstate.m_chain.Height() index.nHeight = tip->nHeight + 1; std::pair lockPair; @@ -281,8 +289,8 @@ bool CheckSequenceLocks(const CTxMemPool& pool, const CTransaction& tx, int flag lockPair.second = lp->time; } else { - // CoinsTip() contains the UTXO set for ::ChainActive().Tip() - CCoinsViewMemPool viewMemPool(&::ChainstateActive().CoinsTip(), pool); + // CoinsTip() contains the UTXO set for active_chainstate.m_chain.Tip() + CCoinsViewMemPool viewMemPool(&active_chainstate.CoinsTip(), pool); std::vector prevheights; prevheights.resize(tx.vin.size()); for (size_t txinIndex = 0; txinIndex < tx.vin.size(); txinIndex++) { @@ -390,7 +398,8 @@ bool ContextualCheckTransaction(const CTransaction& tx, CValidationState &state, // Returns the script flags which should be checked for a given block static unsigned int GetBlockScriptFlags(const CBlockIndex* pindex, const Consensus::Params& chainparams); -static void LimitMempoolSize(CTxMemPool& pool, size_t limit, std::chrono::seconds age) EXCLUSIVE_LOCKS_REQUIRED(pool.cs, ::cs_main) +static void LimitMempoolSize(CTxMemPool& pool, CCoinsViewCache& coins_cache, size_t limit, std::chrono::seconds age) + EXCLUSIVE_LOCKS_REQUIRED(pool.cs, ::cs_main) { int expired = pool.Expire(GetTime() - age); if (expired != 0) { @@ -399,18 +408,20 @@ static void LimitMempoolSize(CTxMemPool& pool, size_t limit, std::chrono::second std::vector vNoSpendsRemaining; pool.TrimToSize(limit, &vNoSpendsRemaining); + assert(std::addressof(::ChainstateActive().CoinsTip()) == std::addressof(coins_cache)); for (const COutPoint& removed : vNoSpendsRemaining) - ::ChainstateActive().CoinsTip().Uncache(removed); + coins_cache.Uncache(removed); } -static bool IsCurrentForFeeEstimation() EXCLUSIVE_LOCKS_REQUIRED(cs_main) +static bool IsCurrentForFeeEstimation(CChainState& active_chainstate) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { AssertLockHeld(cs_main); - if (::ChainstateActive().IsInitialBlockDownload()) + assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate)); + if (active_chainstate.IsInitialBlockDownload()) return false; - if (::ChainActive().Tip()->GetBlockTime() < count_seconds(GetTime() - MAX_FEE_ESTIMATION_TIP_AGE)) + if (active_chainstate.m_chain.Tip()->GetBlockTime() < count_seconds(GetTime() - MAX_FEE_ESTIMATION_TIP_AGE)) return false; - if (::ChainActive().Height() < pindexBestHeader->nHeight - 1) + if (active_chainstate.m_chain.Height() < pindexBestHeader->nHeight - 1) return false; return true; } @@ -428,10 +439,11 @@ static bool IsCurrentForFeeEstimation() EXCLUSIVE_LOCKS_REQUIRED(cs_main) * and instead just erase from the mempool as needed. */ -static void UpdateMempoolForReorg(CTxMemPool& mempool, DisconnectedBlockTransactions& disconnectpool, bool fAddToMempool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, mempool.cs) +static void UpdateMempoolForReorg(CChainState& active_chainstate, CTxMemPool& mempool, DisconnectedBlockTransactions& disconnectpool, bool fAddToMempool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, mempool.cs) { AssertLockHeld(cs_main); AssertLockHeld(mempool.cs); + assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate)); std::vector vHashUpdate; // disconnectpool's insertion_order index sorts the entries from // oldest to newest, but the oldest entry will be the last tx from the @@ -444,7 +456,7 @@ static void UpdateMempoolForReorg(CTxMemPool& mempool, DisconnectedBlockTransact // ignore validation errors in resurrected transactions CValidationState stateDummy; if (!fAddToMempool || (*it)->IsCoinBase() || - !AcceptToMemoryPool(mempool, stateDummy, *it, nullptr /* pfMissingInputs */, + !AcceptToMemoryPool(active_chainstate, mempool, stateDummy, *it, nullptr /* pfMissingInputs */, true /* bypass_limits */, 0 /* nAbsurdFee */)) { // If the transaction doesn't make it in to the mempool, remove any // transactions that depend on it (which would now be orphans). @@ -463,16 +475,18 @@ static void UpdateMempoolForReorg(CTxMemPool& mempool, DisconnectedBlockTransact mempool.UpdateTransactionsFromBlock(vHashUpdate); // We also need to remove any now-immature transactions - mempool.removeForReorg(&::ChainstateActive().CoinsTip(), ::ChainActive().Tip()->nHeight + 1, STANDARD_LOCKTIME_VERIFY_FLAGS); - + mempool.removeForReorg(active_chainstate, STANDARD_LOCKTIME_VERIFY_FLAGS); // Re-limit mempool size, in case we added any transactions - LimitMempoolSize(mempool, gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, std::chrono::hours{gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)}); + LimitMempoolSize(mempool, active_chainstate.CoinsTip(), gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, std::chrono::hours{gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)}); } // Used to avoid mempool polluting consensus critical paths if CCoinsViewMempool // were somehow broken and returning the wrong scriptPubKeys -static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, CValidationState& state, const CCoinsViewCache& view, const CTxMemPool& pool, - unsigned int flags, PrecomputedTransactionData& txdata) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { +static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, CValidationState& state, + const CCoinsViewCache& view, const CTxMemPool& pool, + unsigned int flags, PrecomputedTransactionData& txdata, CCoinsViewCache& coins_tip) + EXCLUSIVE_LOCKS_REQUIRED(cs_main) +{ AssertLockHeld(cs_main); // pool.cs should be locked already, but go ahead and re-take the lock here @@ -496,32 +510,138 @@ static bool CheckInputsFromMempoolAndCache(const CTransaction& tx, CValidationSt assert(txFrom->vout.size() > txin.prevout.n); assert(txFrom->vout[txin.prevout.n] == coin.out); } else { - const Coin& coinFromDisk = ::ChainstateActive().CoinsTip().AccessCoin(txin.prevout); + assert(std::addressof(::ChainstateActive().CoinsTip()) == std::addressof(coins_tip)); + const Coin& coinFromDisk = coins_tip.AccessCoin(txin.prevout); assert(!coinFromDisk.IsSpent()); assert(coinFromDisk.out == coin.out); } } // Call CheckInputs() to cache signature and script validity against current tip consensus rules. - return CheckInputs(tx, state, view, true, flags, /* cacheSigStore = */ true, /* cacheFullSciptStore = */ true, txdata); + return CheckInputs(tx, state, view, flags, /* cacheSigStore = */ true, /* cacheFullSciptStore = */ true, txdata); } -/** - * @param[out] coins_to_uncache Return any outpoints which were not previously present in the - * coins cache, but were added as a result of validating the tx - * for mempool acceptance. This allows the caller to optionally - * remove the cache additions if the associated transaction ends - * up being rejected by the mempool. - */ -static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool& pool, CValidationState& state, const CTransactionRef& ptx, - bool* pfMissingInputs, int64_t nAcceptTime, bool bypass_limits, - const CAmount& nAbsurdFee, std::vector& coins_to_uncache, bool test_accept) EXCLUSIVE_LOCKS_REQUIRED(cs_main) +namespace { + +class MemPoolAccept { - boost::posix_time::ptime start = boost::posix_time::microsec_clock::local_time(); - const CTransaction& tx = *ptx; - const uint256 hash = tx.GetHash(); - AssertLockHeld(cs_main); - LOCK(pool.cs); // mempool "read lock" (held through GetMainSignals().TransactionAddedToMempool()) +public: + explicit MemPoolAccept(CTxMemPool& mempool, CChainState& active_chainstate) : m_pool(mempool), m_view(&m_dummy), m_viewmempool(&active_chainstate.CoinsTip(), m_pool), m_active_chainstate(active_chainstate), + m_limit_ancestors(gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT)), + m_limit_ancestor_size(gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT)*1000), + m_limit_descendants(gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT)), + m_limit_descendant_size(gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT)*1000) { + assert(std::addressof(::ChainstateActive()) == std::addressof(m_active_chainstate)); + } + + // We put the arguments we're handed into a struct, so we can pass them + // around easier. + struct ATMPArgs { + const CChainParams& m_chainparams; + CValidationState &m_state; + bool* m_missing_inputs; + const int64_t m_accept_time; + const bool m_bypass_limits; + const CAmount& m_absurd_fee; + /* + * Return any outpoints which were not previously present in the coins + * cache, but were added as a result of validating the tx for mempool + * acceptance. This allows the caller to optionally remove the cache + * additions if the associated transaction ends up being rejected by + * the mempool. + */ + std::vector& m_coins_to_uncache; + const bool m_test_accept; + }; + + // Single transaction acceptance + bool AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs& args) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + +private: + // All the intermediate state that gets passed between the various levels + // of checking a given transaction. + struct Workspace { + Workspace(const CTransactionRef& ptx) : m_ptx(ptx), m_hash(ptx->GetHash()) {} + CTxMemPool::setEntries m_ancestors; + std::unique_ptr m_entry; + + CAmount m_modified_fees; + + const CTransactionRef& m_ptx; + const uint256& m_hash; + }; + + // Run the policy checks on a given transaction, excluding any script checks. + // Looks up inputs, calculates feerate, considers replacement, evaluates + // package limits, etc. As this function can be invoked for "free" by a peer, + // only tests that are fast should be done here (to avoid CPU DoS). + bool PreChecks(ATMPArgs& args, Workspace& ws) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs); + + // Run the script checks using our policy flags. As this can be slow, we should + // only invoke this on transactions that have otherwise passed policy checks. + bool PolicyScriptChecks(ATMPArgs& args, Workspace& ws, PrecomputedTransactionData& txdata) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + + // Re-run the script checks, using consensus flags, and try to cache the + // result in the scriptcache. This should be done after + // PolicyScriptChecks(). This requires that all inputs either be in our + // utxo set or in the mempool. + bool ConsensusScriptChecks(ATMPArgs& args, Workspace& ws, PrecomputedTransactionData &txdata) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + + // Try to add the transaction to the mempool, removing any conflicts first. + // Returns true if the transaction is in the mempool after any size + // limiting is performed, false otherwise. + bool Finalize(ATMPArgs& args, Workspace& ws) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_pool.cs); + + // Compare a package's feerate against minimum allowed. + bool CheckFeeRate(size_t package_size, CAmount package_fee, CValidationState& state) + { + CAmount mempoolRejectFee = m_pool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(package_size); + if (mempoolRejectFee > 0 && package_fee < mempoolRejectFee) { + return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "mempool min fee not met", strprintf("%d < %d", package_fee, mempoolRejectFee)); + } + + if (package_fee < ::minRelayTxFee.GetFee(package_size)) { + return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "min relay fee not met", strprintf("%d < %d", package_fee, ::minRelayTxFee.GetFee(package_size))); + } + return true; + } + +private: + CTxMemPool& m_pool; + CCoinsViewCache m_view; + CCoinsViewMemPool m_viewmempool; + CCoinsView m_dummy; + CChainState& m_active_chainstate; + + // The package limits in effect at the time of invocation. + const size_t m_limit_ancestors; + const size_t m_limit_ancestor_size; + // These may be modified while evaluating a transaction (eg to account for + // in-mempool conflicts; see below). + size_t m_limit_descendants; + size_t m_limit_descendant_size; +}; + +bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws) +{ + const CTransactionRef& ptx = ws.m_ptx; + const CTransaction& tx = *ws.m_ptx; + const uint256& hash = ws.m_hash; + + // Copy/alias what we need out of args + const CChainParams& chainparams = args.m_chainparams; + CValidationState &state = args.m_state; + bool* pfMissingInputs = args.m_missing_inputs; + const int64_t nAcceptTime = args.m_accept_time; + const bool bypass_limits = args.m_bypass_limits; + const CAmount& nAbsurdFee = args.m_absurd_fee; + std::vector& coins_to_uncache = args.m_coins_to_uncache; + + // Alias what we need out of ws + CTxMemPool::setEntries& setAncestors = ws.m_ancestors; + std::unique_ptr& entry = ws.m_entry; + CAmount& nModifiedFees = ws.m_modified_fees; + if (pfMissingInputs) { *pfMissingInputs = false; } @@ -529,7 +649,8 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool if (!CheckTransaction(tx, state)) return false; // state filled in by CheckTransaction - if (!ContextualCheckTransaction(tx, state, chainparams.GetConsensus(), ::ChainActive().Tip())) + assert(std::addressof(::ChainstateActive()) == std::addressof(m_active_chainstate)); + if (!ContextualCheckTransaction(tx, state, chainparams.GetConsensus(), m_active_chainstate.m_chain.Tip())) return error("%s: ContextualCheckTransaction: %s, %s", __func__, hash.ToString(), FormatStateMessage(state)); if (tx.nVersion == 3 && tx.nType == TRANSACTION_QUORUM_COMMITMENT) { @@ -556,11 +677,11 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool // 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 (!CheckFinalTx(tx, STANDARD_LOCKTIME_VERIFY_FLAGS)) + if (!CheckFinalTx(m_active_chainstate.m_chain.Tip(), tx, STANDARD_LOCKTIME_VERIFY_FLAGS)) return state.Invalid(ValidationInvalidReason::TX_PREMATURE_SPEND, false, REJECT_NONSTANDARD, "non-final"); // is it already in the memory pool? - if (pool.exists(hash)) { + if (m_pool.exists(hash)) { statsClient.inc("transactions.duplicate", 1.0f); return state.Invalid(ValidationInvalidReason::TX_CONFLICT, false, REJECT_DUPLICATE, "txn-already-in-mempool"); } @@ -568,7 +689,7 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool llmq::CInstantSendLockPtr conflictLock = llmq::quorumInstantSendManager->GetConflictingLock(tx); if (conflictLock) { uint256 hashBlock; - CTransactionRef txConflict = GetTransaction(/* block_index */ nullptr, &pool, conflictLock->txid, chainparams.GetConsensus(), hashBlock); + CTransactionRef txConflict = GetTransaction(/* block_index */ nullptr, &m_pool, conflictLock->txid, chainparams.GetConsensus(), hashBlock); if (txConflict) { GetMainSignals().NotifyInstantSendDoubleSpendAttempt(ptx, txConflict); } @@ -576,13 +697,13 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool } if (llmq::quorumInstantSendManager->IsWaitingForTx(hash)) { - pool.removeConflicts(tx); - pool.removeProTxConflicts(tx); + m_pool.removeConflicts(tx); + m_pool.removeProTxConflicts(tx); } else { // Check for conflicts with in-memory transactions for (const CTxIn &txin : tx.vin) { - const CTransaction* ptxConflicting = pool.GetConflictTx(txin.prevout); + const CTransaction* ptxConflicting = m_pool.GetConflictTx(txin.prevout); if (ptxConflicting) { // Transaction conflicts with mempool and RBF doesn't exist in Dash @@ -591,212 +712,263 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool } } - { - CCoinsView dummy; - CCoinsViewCache view(&dummy); - - LockPoints lp; - CCoinsViewCache& coins_cache = ::ChainstateActive().CoinsTip(); - CCoinsViewMemPool viewMemPool(&coins_cache, pool); - view.SetBackend(viewMemPool); - - // do all inputs exist? - for (const CTxIn& txin : tx.vin) { - if (!coins_cache.HaveCoinInCache(txin.prevout)) { - coins_to_uncache.push_back(txin.prevout); - } + LockPoints lp; + m_view.SetBackend(m_viewmempool); - // Note: this call may add txin.prevout to the coins cache - // (pcoinsTip.cacheCoins) by way of FetchCoin(). It should be removed - // later (via coins_to_uncache) if this tx turns out to be invalid. - if (!view.HaveCoin(txin.prevout)) { - // Are inputs missing because we already have the tx? - for (size_t out = 0; out < tx.vout.size(); out++) { - // Optimistically just do efficient check of cache for outputs - if (coins_cache.HaveCoinInCache(COutPoint(hash, out))) { - return state.Invalid(ValidationInvalidReason::TX_CONFLICT, false, REJECT_DUPLICATE, "txn-already-known"); - } - } - // Otherwise assume this might be an orphan tx for which we just haven't seen parents yet - if (pfMissingInputs) { - *pfMissingInputs = true; + assert(std::addressof(::ChainstateActive().CoinsTip()) == std::addressof(m_active_chainstate.CoinsTip())); + CCoinsViewCache& coins_cache = m_active_chainstate.CoinsTip(); + // do all inputs exist? + for (const CTxIn& txin : tx.vin) { + if (!coins_cache.HaveCoinInCache(txin.prevout)) { + coins_to_uncache.push_back(txin.prevout); + } + + // Note: this call may add txin.prevout to the coins cache + // (coins_cache.cacheCoins) by way of FetchCoin(). It should be removed + // later (via coins_to_uncache) if this tx turns out to be invalid. + if (!m_view.HaveCoin(txin.prevout)) { + // Are inputs missing because we already have the tx? + for (size_t out = 0; out < tx.vout.size(); out++) { + // Optimistically just do efficient check of cache for outputs + if (coins_cache.HaveCoinInCache(COutPoint(hash, out))) { + return state.Invalid(ValidationInvalidReason::TX_CONFLICT, false, REJECT_DUPLICATE, "txn-already-known"); } - return false; // fMissingInputs and !state.IsInvalid() is used to detect this condition, don't set state.Invalid() } + // Otherwise assume this might be an orphan tx for which we just haven't seen parents yet + if (pfMissingInputs) { + *pfMissingInputs = true; + } + return false; // fMissingInputs and !state.IsInvalid() is used to detect this condition, don't set state.Invalid() } + } - // Bring the best block into scope - view.GetBestBlock(); + // Bring the best block into scope + m_view.GetBestBlock(); - // we have all inputs cached now, so switch back to dummy, so we don't need to keep lock on mempool - view.SetBackend(dummy); + // we have all inputs cached now, so switch back to dummy (to protect + // against bugs where we pull more inputs from disk that miss being added + // to coins_to_uncache) + m_view.SetBackend(m_dummy); - // Only accept BIP68 sequence locked 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. - // Must keep pool.cs for this unless we change CheckSequenceLocks to take a - // CoinsViewCache instead of create its own - if (!CheckSequenceLocks(pool, tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp)) - return state.Invalid(ValidationInvalidReason::TX_PREMATURE_SPEND, false, REJECT_NONSTANDARD, "non-BIP68-final"); + // Only accept BIP68 sequence locked 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. + // Must keep pool.cs for this unless we change CheckSequenceLocks to take a + // CoinsViewCache instead of create its own + assert(std::addressof(::ChainstateActive()) == std::addressof(m_active_chainstate)); + if (!CheckSequenceLocks(m_active_chainstate, m_pool, tx, STANDARD_LOCKTIME_VERIFY_FLAGS, &lp)) + return state.Invalid(ValidationInvalidReason::TX_PREMATURE_SPEND, false, REJECT_NONSTANDARD, "non-BIP68-final"); - CAmount nFees = 0; - if (!Consensus::CheckTxInputs(tx, state, view, GetSpendHeight(view), nFees)) { - return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, tx.GetHash().ToString(), FormatStateMessage(state)); - } + assert(std::addressof(g_chainman.m_blockman) == std::addressof(m_active_chainstate.m_blockman)); + CAmount nFees = 0; + if (!Consensus::CheckTxInputs(tx, state, m_view, m_active_chainstate.m_blockman.GetSpendHeight(m_view), nFees)) { + return error("%s: Consensus::CheckTxInputs: %s, %s", __func__, tx.GetHash().ToString(), FormatStateMessage(state)); + } - // Check for non-standard pay-to-script-hash in inputs - if (fRequireStandard && !AreInputsStandard(tx, view)) - return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_NONSTANDARD, "bad-txns-nonstandard-inputs"); + // Check for non-standard pay-to-script-hash in inputs + if (fRequireStandard && !AreInputsStandard(tx, m_view)) + return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_NONSTANDARD, "bad-txns-nonstandard-inputs"); - unsigned int nSigOps = GetTransactionSigOpCount(tx, view, STANDARD_SCRIPT_VERIFY_FLAGS); + unsigned int nSigOps = GetTransactionSigOpCount(tx, m_view, STANDARD_SCRIPT_VERIFY_FLAGS); - // nModifiedFees includes any fee deltas from PrioritiseTransaction - CAmount nModifiedFees = nFees; - pool.ApplyDelta(hash, nModifiedFees); + // nModifiedFees includes any fee deltas from PrioritiseTransaction + nModifiedFees = nFees; + m_pool.ApplyDelta(hash, nModifiedFees); - // Keep track of transactions that spend a coinbase, which we re-scan - // during reorgs to ensure COINBASE_MATURITY is still met. - bool fSpendsCoinbase = false; - for (const CTxIn &txin : tx.vin) { - const Coin &coin = view.AccessCoin(txin.prevout); - if (coin.IsCoinBase()) { - fSpendsCoinbase = true; - break; - } + // Keep track of transactions that spend a coinbase, which we re-scan + // during reorgs to ensure COINBASE_MATURITY is still met. + bool fSpendsCoinbase = false; + for (const CTxIn &txin : tx.vin) { + const Coin &coin = m_view.AccessCoin(txin.prevout); + if (coin.IsCoinBase()) { + fSpendsCoinbase = true; + break; } + } - CTxMemPoolEntry entry(ptx, nFees, nAcceptTime, ::ChainActive().Height(), - fSpendsCoinbase, nSigOps, lp); - unsigned int nSize = entry.GetTxSize(); + assert(std::addressof(::ChainActive()) == std::addressof(m_active_chainstate.m_chain)); + entry.reset(new CTxMemPoolEntry(ptx, nFees, nAcceptTime, m_active_chainstate.m_chain.Height(), + fSpendsCoinbase, nSigOps, lp)); + unsigned int nSize = entry->GetTxSize(); - if (nSigOps > MAX_STANDARD_TX_SIGOPS) - return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_NONSTANDARD, "bad-txns-too-many-sigops", + if (nSigOps > MAX_STANDARD_TX_SIGOPS) + return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_NONSTANDARD, "bad-txns-too-many-sigops", strprintf("%d", nSigOps)); - CAmount mempoolRejectFee = pool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000).GetFee(nSize); - if (!bypass_limits && mempoolRejectFee > 0 && nModifiedFees < mempoolRejectFee) { - return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "mempool min fee not met", strprintf("%d < %d", nModifiedFees, mempoolRejectFee)); - } - - // No transactions are allowed below minRelayTxFee except from disconnected blocks - if (!bypass_limits && nModifiedFees < ::minRelayTxFee.GetFee(nSize)) { - return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "min relay fee not met", strprintf("%d < %d", nModifiedFees, ::minRelayTxFee.GetFee(nSize))); - } + // No transactions are allowed below minRelayTxFee except from disconnected + // blocks + if (!bypass_limits && !CheckFeeRate(nSize, nModifiedFees, state)) return false; - if (nAbsurdFee && nFees > nAbsurdFee) - return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, + if (nAbsurdFee && nFees > nAbsurdFee) + return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_HIGHFEE, "absurdly-high-fee", strprintf("%d > %d", nFees, nAbsurdFee)); - // Calculate in-mempool ancestors, up to a limit. - CTxMemPool::setEntries setAncestors; - size_t nLimitAncestors = gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT); - size_t nLimitAncestorSize = gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT)*1000; - size_t nLimitDescendants = gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT); - size_t nLimitDescendantSize = gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT)*1000; - std::string errString; - if (!pool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) { - setAncestors.clear(); - // If CalculateMemPoolAncestors fails second time, we want the original error string. - std::string dummy_err_string; - // If the new transaction is relatively small (up to 40k weight) - // and has at most one ancestor (ie ancestor limit of 2, including - // the new transaction), allow it if its parent has exactly the - // descendant limit descendants. - // - // This allows protocols which rely on distrusting counterparties - // being able to broadcast descendants of an unconfirmed transaction - // to be secure by simply only having two immediately-spendable - // outputs - one for each counterparty. For more info on the uses for - // this, see https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2018-November/016518.html - if (nSize > EXTRA_DESCENDANT_TX_SIZE_LIMIT || - !pool.CalculateMemPoolAncestors(entry, setAncestors, 2, nLimitAncestorSize, nLimitDescendants + 1, nLimitDescendantSize + EXTRA_DESCENDANT_TX_SIZE_LIMIT, dummy_err_string)) { - return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_NONSTANDARD, "too-long-mempool-chain", errString); - } - } + // Calculate in-mempool ancestors, up to a limit. + std::string errString; + if (!m_pool.CalculateMemPoolAncestors(*entry, setAncestors, m_limit_ancestors, m_limit_ancestor_size, m_limit_descendants, m_limit_descendant_size, errString)) { + setAncestors.clear(); + // If CalculateMemPoolAncestors fails second time, we want the original error string. + std::string dummy_err_string; + // If the new transaction is relatively small (up to 40k weight) + // and has at most one ancestor (ie ancestor limit of 2, including + // the new transaction), allow it if its parent has exactly the + // descendant limit descendants. + // + // This allows protocols which rely on distrusting counterparties + // being able to broadcast descendants of an unconfirmed transaction + // to be secure by simply only having two immediately-spendable + // outputs - one for each counterparty. For more info on the uses for + // this, see https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2018-November/016518.html + if (nSize > EXTRA_DESCENDANT_TX_SIZE_LIMIT || + !m_pool.CalculateMemPoolAncestors(*entry, setAncestors, 2, m_limit_ancestor_size, m_limit_descendants + 1, m_limit_descendant_size + EXTRA_DESCENDANT_TX_SIZE_LIMIT, dummy_err_string)) { + return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_NONSTANDARD, "too-long-mempool-chain", errString); + } + } + + // check special TXs after all the other checks. If we'd do this before the other checks, we might end up + // DoS scoring a node for non-critical errors, e.g. duplicate keys because a TX is received that was already + // mined + // NOTE: we use UTXO here and do NOT allow mempool txes as masternode collaterals + if (!CheckSpecialTx(tx, m_active_chainstate.m_chain.Tip(), state, m_active_chainstate.CoinsTip(), true)) + return false; - // check special TXs after all the other checks. If we'd do this before the other checks, we might end up - // DoS scoring a node for non-critical errors, e.g. duplicate keys because a TX is received that was already - // mined - // NOTE: we use UTXO here and do NOT allow mempool txes as masternode collaterals - if (!CheckSpecialTx(tx, ::ChainActive().Tip(), state, ::ChainstateActive().CoinsTip(), true)) - return false; + if (m_pool.existsProviderTxConflict(tx)) { + return state.Invalid(ValidationInvalidReason::TX_CONFLICT, false, REJECT_DUPLICATE, "protx-dup"); + } - if (pool.existsProviderTxConflict(tx)) { - return state.Invalid(ValidationInvalidReason::TX_CONFLICT, false, REJECT_DUPLICATE, "protx-dup"); - } + return true; +} - constexpr unsigned int scriptVerifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS; +bool MemPoolAccept::PolicyScriptChecks(ATMPArgs& args, Workspace& ws, PrecomputedTransactionData& txdata) +{ + const CTransaction& tx = *ws.m_ptx; - // Check against previous transactions - // This is done last to help prevent CPU exhaustion denial-of-service attacks. - PrecomputedTransactionData txdata; - if (!CheckInputs(tx, state, view, true, scriptVerifyFlags, true, false, txdata)) { - assert(IsTransactionReason(state.GetReason())); - return false; // state filled in by CheckInputs - } + CValidationState &state = args.m_state; - // Check again against the current block tip's script verification - // flags to cache our script execution flags. This is, of course, - // useless if the next block has different script flags from the - // previous one, but because the cache tracks script flags for us it - // will auto-invalidate and we'll just have a few blocks of extra - // misses on soft-fork activation. - // - // This is also useful in case of bugs in the standard flags that cause - // transactions to pass as valid when they're actually invalid. For - // instance the STRICTENC flag was incorrectly allowing certain - // CHECKSIG NOT scripts to pass, even though they were invalid. - // - // There is a similar check in CreateNewBlock() to prevent creating - // invalid blocks (using TestBlockValidity), however allowing such - // transactions into the mempool can be exploited as a DoS attack. - unsigned int currentBlockScriptVerifyFlags = GetBlockScriptFlags(::ChainActive().Tip(), chainparams.GetConsensus()); - if (!CheckInputsFromMempoolAndCache(tx, state, view, pool, currentBlockScriptVerifyFlags, txdata)) { - return error("%s: BUG! PLEASE REPORT THIS! CheckInputs failed against latest-block but not STANDARD flags %s, %s", - __func__, hash.ToString(), FormatStateMessage(state)); - } + constexpr unsigned int scriptVerifyFlags = STANDARD_SCRIPT_VERIFY_FLAGS; - if (test_accept) { - // Tx was accepted, but not added - return true; - } + // Check against previous transactions + // This is done last to help prevent CPU exhaustion denial-of-service attacks. + if (!CheckInputs(tx, state, m_view, scriptVerifyFlags, true, false, txdata)) { + assert(IsTransactionReason(state.GetReason())); + return false; // state filled in by CheckInputs + } - // This transaction should only count for fee estimation if: - // - it's not being re-added during a reorg which bypasses typical mempool fee limits - // - the node is not behind - // - the transaction is not dependent on any other transactions in the mempool - // - the transaction is not a zero fee transaction - bool validForFeeEstimation = (nFees !=0) && !bypass_limits && IsCurrentForFeeEstimation() && pool.HasNoInputsOf(tx); + return true; +} - // Store transaction in memory - pool.addUnchecked(entry, setAncestors, validForFeeEstimation); - CAmount nValueOut = tx.GetValueOut(); - statsClient.count("transactions.sizeBytes", nSize, 1.0f); - statsClient.count("transactions.fees", nFees, 1.0f); - statsClient.count("transactions.inputValue", nValueOut - nFees, 1.0f); - statsClient.count("transactions.outputValue", nValueOut, 1.0f); - statsClient.count("transactions.sigOps", nSigOps, 1.0f); +bool MemPoolAccept::ConsensusScriptChecks(ATMPArgs& args, Workspace& ws, PrecomputedTransactionData& txdata) +{ + const CTransaction& tx = *ws.m_ptx; + const uint256& hash = ws.m_hash; + + CValidationState &state = args.m_state; + const CChainParams& chainparams = args.m_chainparams; + + // Check again against the current block tip's script verification + // flags to cache our script execution flags. This is, of course, + // useless if the next block has different script flags from the + // previous one, but because the cache tracks script flags for us it + // will auto-invalidate and we'll just have a few blocks of extra + // misses on soft-fork activation. + // + // This is also useful in case of bugs in the standard flags that cause + // transactions to pass as valid when they're actually invalid. For + // instance the STRICTENC flag was incorrectly allowing certain + // CHECKSIG NOT scripts to pass, even though they were invalid. + // + // There is a similar check in CreateNewBlock() to prevent creating + // invalid blocks (using TestBlockValidity), however allowing such + // transactions into the mempool can be exploited as a DoS attack. + assert(std::addressof(::ChainActive()) == std::addressof(m_active_chainstate.m_chain)); + unsigned int currentBlockScriptVerifyFlags = GetBlockScriptFlags(m_active_chainstate.m_chain.Tip(), chainparams.GetConsensus()); + assert(std::addressof(::ChainstateActive().CoinsTip()) == std::addressof(m_active_chainstate.CoinsTip())); + if (!CheckInputsFromMempoolAndCache(tx, state, m_view, m_pool, currentBlockScriptVerifyFlags, txdata, m_active_chainstate.CoinsTip())) { + return error("%s: BUG! PLEASE REPORT THIS! CheckInputs failed against latest-block but not STANDARD flags %s, %s", + __func__, hash.ToString(), FormatStateMessage(state)); + } - // Add memory address index - if (fAddressIndex) { - pool.addAddressIndex(entry, view); - } + return true; +} +bool MemPoolAccept::Finalize(ATMPArgs& args, Workspace& ws) +{ + const CTransaction& tx = *ws.m_ptx; + const uint256& hash = ws.m_hash; + CValidationState &state = args.m_state; + const bool bypass_limits = args.m_bypass_limits; + + CTxMemPool::setEntries& setAncestors = ws.m_ancestors; + const CAmount& nModifiedFees = ws.m_modified_fees; + std::unique_ptr& entry = ws.m_entry; + + // This transaction should only count for fee estimation if: + // - it's not being re-added during a reorg which bypasses typical mempool fee limits + // - the node is not behind + // - the transaction is not dependent on any other transactions in the mempool + // - the transaction is not a zero fee transaction + assert(std::addressof(::ChainstateActive()) == std::addressof(m_active_chainstate)); + bool validForFeeEstimation = (nModifiedFees != 0) && !bypass_limits && IsCurrentForFeeEstimation(m_active_chainstate) && m_pool.HasNoInputsOf(tx); + + // Store transaction in memory + m_pool.addUnchecked(*entry, setAncestors, validForFeeEstimation); + CAmount nValueOut = tx.GetValueOut(); + unsigned int nSigOps = GetTransactionSigOpCount(tx, m_view, STANDARD_SCRIPT_VERIFY_FLAGS); + + statsClient.count("transactions.sizeBytes", entry->GetTxSize(), 1.0f); + statsClient.count("transactions.fees", nModifiedFees, 1.0f); + statsClient.count("transactions.inputValue", nValueOut - nModifiedFees, 1.0f); + statsClient.count("transactions.outputValue", nValueOut, 1.0f); + statsClient.count("transactions.sigOps", nSigOps, 1.0f); + + // Add memory address index + if (fAddressIndex) { + m_pool.addAddressIndex(*entry, m_view); + } - // Add memory spent index - if (fSpentIndex) { - pool.addSpentIndex(entry, view); - } + // Add memory spent index + if (fSpentIndex) { + m_pool.addSpentIndex(*entry, m_view); + } - if (!bypass_limits) { - LimitMempoolSize(pool, gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, std::chrono::hours{gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)}); - if (!pool.exists(hash)) - return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "mempool full"); - } + if (!bypass_limits) { + assert(std::addressof(::ChainstateActive().CoinsTip()) == std::addressof(m_active_chainstate.CoinsTip())); + LimitMempoolSize(m_pool, m_active_chainstate.CoinsTip(), gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000, std::chrono::hours{gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY)}); + if (!m_pool.exists(hash)) + return state.Invalid(ValidationInvalidReason::TX_MEMPOOL_POLICY, false, REJECT_INSUFFICIENTFEE, "mempool full"); } + return true; +} +bool MemPoolAccept::AcceptSingleTransaction(const CTransactionRef& ptx, ATMPArgs& args) +{ + boost::posix_time::ptime start = boost::posix_time::microsec_clock::local_time(); + AssertLockHeld(cs_main); + LOCK(m_pool.cs); // mempool "read lock" (held through GetMainSignals().TransactionAddedToMempool()) + + Workspace workspace(ptx); + + if (!PreChecks(args, workspace)) return false; + + // Only compute the precomputed transaction data if we need to verify + // scripts (ie, other policy checks pass). We perform the inexpensive + // checks first and avoid hashing and signature verification unless those + // checks pass, to mitigate CPU exhaustion denial-of-service attacks. + PrecomputedTransactionData txdata(*ptx); + + if (!PolicyScriptChecks(args, workspace, txdata)) return false; + + if (!ConsensusScriptChecks(args, workspace, txdata)) return false; + + // Tx was accepted, but not added + if (args.m_test_accept) return true; + + if (!Finalize(args, workspace)) return false; + + const int64_t nAcceptTime = args.m_accept_time; GetMainSignals().TransactionAddedToMempool(ptx, nAcceptTime); + const CTransaction& tx = *ptx; boost::posix_time::ptime finish = boost::posix_time::microsec_clock::local_time(); boost::posix_time::time_duration diff = finish - start; statsClient.timing("AcceptToMemoryPool_ms", diff.total_milliseconds(), 1.0f); @@ -807,34 +979,41 @@ static bool AcceptToMemoryPoolWorker(const CChainParams& chainparams, CTxMemPool return true; } +} // anon namespace + /** (try to) add transaction to memory pool with a specified acceptance time **/ -static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, +static bool AcceptToMemoryPoolWithTime(const CChainParams& chainparams, CTxMemPool& pool, CChainState& active_chainstate, CValidationState &state, const CTransactionRef &tx, bool* pfMissingInputs, int64_t nAcceptTime, bool bypass_limits, const CAmount nAbsurdFee, bool test_accept) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { std::vector coins_to_uncache; - bool res = AcceptToMemoryPoolWorker(chainparams, pool, state, tx, pfMissingInputs, nAcceptTime, bypass_limits, nAbsurdFee, coins_to_uncache, test_accept); + MemPoolAccept::ATMPArgs args { chainparams, state, pfMissingInputs, nAcceptTime, bypass_limits, nAbsurdFee, coins_to_uncache, test_accept }; + + assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate)); + bool res = MemPoolAccept(pool, active_chainstate).AcceptSingleTransaction(tx, args); if (!res || test_accept) { - if(!res) LogPrint(BCLog::MEMPOOL, "%s: %s %s (%s)\n", __func__, tx->GetHash().ToString(), state.GetRejectReason(), state.GetDebugMessage()); - // Remove coins that were not present in the coins cache before calling ATMPW; - // this is to prevent memory DoS in case we receive a large number of - // invalid transactions that attempt to overrun the in-memory coins cache - // (`CCoinsViewCache::cacheCoins`). + if (!res) LogPrint(BCLog::MEMPOOL, "%s: %s %s (%s)\n", __func__, tx->GetHash().ToString(), state.GetRejectReason(), state.GetDebugMessage()); + + // Remove coins that were not present in the coins cache before calling ATMPW; + // this is to prevent memory DoS in case we receive a large number of + // invalid transactions that attempt to overrun the in-memory coins cache + // (`CCoinsViewCache::cacheCoins`). for (const COutPoint& hashTx : coins_to_uncache) - ::ChainstateActive().CoinsTip().Uncache(hashTx); + active_chainstate.CoinsTip().Uncache(hashTx); } // After we've (potentially) uncached entries, ensure our coins cache is still within its size limits CValidationState stateDummy; - ::ChainstateActive().FlushStateToDisk(chainparams, stateDummy, FlushStateMode::PERIODIC); + active_chainstate.FlushStateToDisk(chainparams, stateDummy, FlushStateMode::PERIODIC); return res; } -bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, +bool AcceptToMemoryPool(CChainState& active_chainstate, CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, bool* pfMissingInputs, bool bypass_limits, const CAmount nAbsurdFee, bool test_accept) { const CChainParams& chainparams = Params(); - return AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, pfMissingInputs, GetTime(), bypass_limits, nAbsurdFee, test_accept); + assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate)); + return AcceptToMemoryPoolWithTime(chainparams, pool, active_chainstate, state, tx, pfMissingInputs, GetTime(), bypass_limits, nAbsurdFee, test_accept); } bool GetTimestampIndex(const unsigned int &high, const unsigned int &low, std::vector &hashes) @@ -1135,12 +1314,12 @@ CChainState::CChainState(BlockManager& blockman, std::unique_ptr& evoDb, CTxMemPool& mempool, uint256 from_snapshot_blockhash) - : m_blockman(blockman), + : m_mempool(mempool), m_clhandler(clhandler), m_isman(isman), m_quorum_block_processor(quorum_block_processor), m_evoDb(evoDb), - m_mempool(mempool), + m_blockman(blockman), m_from_snapshot_blockhash(from_snapshot_blockhash) {} void CChainState::InitCoinsDB( @@ -1191,8 +1370,6 @@ bool CChainState::IsInitialBlockDownload() const return false; } -static CBlockIndex *pindexBestForkTip = nullptr, *pindexBestForkBase = nullptr; - static void AlertNotify(const std::string& strMessage) { uiInterface.NotifyAlertChanged(); @@ -1213,120 +1390,62 @@ static void AlertNotify(const std::string& strMessage) #endif } -static void CheckForkWarningConditions() EXCLUSIVE_LOCKS_REQUIRED(cs_main) +void CChainState::CheckForkWarningConditions() { AssertLockHeld(cs_main); + assert(std::addressof(::ChainstateActive()) == std::addressof(*this)); + // Before we get past initial download, we cannot reliably alert about forks // (we assume we don't get stuck on a fork before finishing our initial sync) - if (::ChainstateActive().IsInitialBlockDownload()) + if (IsInitialBlockDownload()) { return; - - // If our best fork is no longer within 72 blocks (+/- 3 hours if no one mines it) - // of our head, drop it - if (pindexBestForkTip && ::ChainActive().Height() - pindexBestForkTip->nHeight >= 72) - pindexBestForkTip = nullptr; - - if (pindexBestForkTip || (pindexBestInvalid && pindexBestInvalid->nChainWork > ::ChainActive().Tip()->nChainWork + (GetBlockProof(*::ChainActive().Tip()) * 6))) - { - if (!GetfLargeWorkForkFound() && pindexBestForkBase) - { - if(pindexBestForkBase->phashBlock){ - std::string warning = std::string("'Warning: Large-work fork detected, forking after block ") + - pindexBestForkBase->phashBlock->ToString() + std::string("'"); - AlertNotify(warning); - } - } - if (pindexBestForkTip && pindexBestForkBase) - { - if(pindexBestForkBase->phashBlock){ - LogPrintf("%s: Warning: Large valid fork found\n forking the chain at height %d (%s)\n lasting to height %d (%s).\nChain state database corruption likely.\n", __func__, - pindexBestForkBase->nHeight, pindexBestForkBase->phashBlock->ToString(), - pindexBestForkTip->nHeight, pindexBestForkTip->phashBlock->ToString()); - SetfLargeWorkForkFound(true); - } - } - else - { - if(pindexBestInvalid->nHeight > ::ChainActive().Height() + 6) - LogPrintf("%s: Warning: Found invalid chain at least ~6 blocks longer than our best chain.\nChain state database corruption likely.\n", __func__); - else - LogPrintf("%s: Warning: Found invalid chain which has higher work (at least ~6 blocks worth of work) than our best chain.\nChain state database corruption likely.\n", __func__); - SetfLargeWorkInvalidChainFound(true); - } - } - else - { - SetfLargeWorkForkFound(false); - SetfLargeWorkInvalidChainFound(false); } -} -static void CheckForkWarningConditionsOnNewFork(CBlockIndex* pindexNewForkTip) EXCLUSIVE_LOCKS_REQUIRED(cs_main) -{ - AssertLockHeld(cs_main); - // If we are on a fork that is sufficiently large, set a warning flag - CBlockIndex* pfork = pindexNewForkTip; - CBlockIndex* plonger = ::ChainActive().Tip(); - while (pfork && pfork != plonger) - { - while (plonger && plonger->nHeight > pfork->nHeight) - plonger = plonger->pprev; - if (pfork == plonger) - break; - pfork = pfork->pprev; - } - - // We define a condition where we should warn the user about as a fork of at least 7 blocks - // with a tip within 72 blocks (+/- 3 hours if no one mines it) of ours - // or a chain that is entirely longer than ours and invalid (note that this should be detected by both) - // We use 7 blocks rather arbitrarily as it represents just under 10% of sustained network - // hash rate operating on the fork. - // We define it this way because it allows us to only store the highest fork tip (+ base) which meets - // the 7-block condition and from this always have the most-likely-to-cause-warning fork - if (pfork && (!pindexBestForkTip || pindexNewForkTip->nHeight > pindexBestForkTip->nHeight) && - pindexNewForkTip->nChainWork - pfork->nChainWork > (GetBlockProof(*pfork) * 7) && - ::ChainActive().Height() - pindexNewForkTip->nHeight < 72) - { - pindexBestForkTip = pindexNewForkTip; - pindexBestForkBase = pfork; + if (pindexBestInvalid && pindexBestInvalid->nChainWork > m_chain.Tip()->nChainWork + (GetBlockProof(*m_chain.Tip()) * 6)) { + LogPrintf("%s: Warning: Found invalid chain which has higher work (at least ~6 blocks worth of work) than our best chain.\nChain state database corruption likely.\n", __func__); + SetfLargeWorkInvalidChainFound(true); + } else { + SetfLargeWorkInvalidChainFound(false); } - - CheckForkWarningConditions(); } // Called both upon regular invalid block discovery *and* InvalidateBlock -void static InvalidChainFound(CBlockIndex* pindexNew) EXCLUSIVE_LOCKS_REQUIRED(cs_main) +void CChainState::InvalidChainFound(CBlockIndex* pindexNew) { + assert(std::addressof(::ChainstateActive()) == std::addressof(*this)); + statsClient.inc("warnings.InvalidChainFound", 1.0f); if (!pindexBestInvalid || pindexNew->nChainWork > pindexBestInvalid->nChainWork) pindexBestInvalid = pindexNew; if (pindexBestHeader != nullptr && pindexBestHeader->GetAncestor(pindexNew->nHeight) == pindexNew) { - pindexBestHeader = ::ChainActive().Tip(); + pindexBestHeader = m_chain.Tip(); } LogPrintf("%s: invalid block=%s height=%d log2_work=%.8f date=%s\n", __func__, pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, log(pindexNew->nChainWork.getdouble())/log(2.0), FormatISO8601DateTime(pindexNew->GetBlockTime())); - CBlockIndex *tip = ::ChainActive().Tip(); + CBlockIndex *tip = m_chain.Tip(); assert (tip); LogPrintf("%s: current best=%s height=%d log2_work=%.8f date=%s\n", __func__, - tip->GetBlockHash().ToString(), ::ChainActive().Height(), log(tip->nChainWork.getdouble())/log(2.0), + tip->GetBlockHash().ToString(), m_chain.Height(), log(tip->nChainWork.getdouble())/log(2.0), FormatISO8601DateTime(tip->GetBlockTime())); CheckForkWarningConditions(); } -void static ConflictingChainFound(CBlockIndex* pindexNew) EXCLUSIVE_LOCKS_REQUIRED(cs_main) +void CChainState::ConflictingChainFound(CBlockIndex* pindexNew) { + assert(std::addressof(::ChainstateActive()) == std::addressof(*this)); + statsClient.inc("warnings.ConflictingChainFound", 1.0f); LogPrintf("%s: conflicting block=%s height=%d log2_work=%.8f date=%s\n", __func__, pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, log(pindexNew->nChainWork.getdouble())/log(2.0), FormatISO8601DateTime(pindexNew->GetBlockTime())); - CBlockIndex *tip = ::ChainActive().Tip(); + CBlockIndex *tip = m_chain.Tip(); assert (tip); LogPrintf("%s: current best=%s height=%d log2_work=%.8f date=%s\n", __func__, - tip->GetBlockHash().ToString(), ::ChainActive().Height(), log(tip->nChainWork.getdouble())/log(2.0), + tip->GetBlockHash().ToString(), m_chain.Height(), log(tip->nChainWork.getdouble())/log(2.0), FormatISO8601DateTime(tip->GetBlockTime())); CheckForkWarningConditions(); } @@ -1371,9 +1490,10 @@ bool CScriptCheck::operator()() { return VerifyScript(scriptSig, m_tx_out.scriptPubKey, nFlags, CachingTransactionSignatureChecker(ptxTo, nIn, m_tx_out.nValue, txdata, cacheStore), &error); } -int GetSpendHeight(const CCoinsViewCache& inputs) +int BlockManager::GetSpendHeight(const CCoinsViewCache& inputs) { - LOCK(cs_main); + AssertLockHeld(cs_main); + assert(std::addressof(g_chainman.m_blockman) == std::addressof(*this)); CBlockIndex* pindexPrev = LookupBlockIndex(inputs.GetBestBlock()); return pindexPrev->nHeight + 1; } @@ -1415,100 +1535,88 @@ void InitScriptExecutionCache() { * * Non-static (and re-declared) in src/test/txvalidationcache_tests.cpp */ -bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, bool fScriptChecks, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector *pvChecks) EXCLUSIVE_LOCKS_REQUIRED(cs_main) +bool CheckInputs(const CTransaction& tx, CValidationState &state, const CCoinsViewCache &inputs, unsigned int flags, bool cacheSigStore, bool cacheFullScriptStore, PrecomputedTransactionData& txdata, std::vector *pvChecks) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { boost::posix_time::ptime start = boost::posix_time::microsec_clock::local_time(); - if (!tx.IsCoinBase()) - { - if (pvChecks) - pvChecks->reserve(tx.vin.size()); - - // The first loop above does all the inexpensive checks. - // Only if ALL inputs pass do we perform expensive ECDSA signature checks. - // Helps prevent CPU exhaustion attacks. - - // Skip script verification when connecting blocks under the - // assumevalid block. Assuming the assumevalid block is valid this - // is safe because block merkle hashes are still computed and checked, - // Of course, if an assumed valid block is invalid due to false scriptSigs - // this optimization would allow an invalid chain to be accepted. - if (fScriptChecks) { - // First check if script executions have been cached with the same - // flags. Note that this assumes that the inputs provided are - // correct (ie that the transaction hash which is in tx's prevouts - // properly commits to the scriptPubKey in the inputs view of that - // transaction). - uint256 hashCacheEntry; - CSHA256 hasher = g_scriptExecutionCacheHasher; - hasher.Write(tx.GetHash().begin(), 32).Write((unsigned char*)&flags, sizeof(flags)).Finalize(hashCacheEntry.begin()); - AssertLockHeld(cs_main); //TODO: Remove this requirement by making CuckooCache not require external locks - if (g_scriptExecutionCache.contains(hashCacheEntry, !cacheFullScriptStore)) { - return true; - } - - if (!txdata.m_ready) { - txdata.Init(tx, {}); - } - - for (unsigned int i = 0; i < tx.vin.size(); i++) { - const COutPoint &prevout = tx.vin[i].prevout; - const Coin& coin = inputs.AccessCoin(prevout); - assert(!coin.IsSpent()); - - // We very carefully only pass in things to CScriptCheck which - // are clearly committed to by tx' witness hash. This provides - // a sanity check that our caching is not introducing consensus - // failures through additional data in, eg, the coins being - // spent being checked as a part of CScriptCheck. - - // Verify signature - CScriptCheck check(coin.out, tx, i, flags, cacheSigStore, &txdata); - if (pvChecks) { - pvChecks->push_back(CScriptCheck()); - check.swap(pvChecks->back()); - } else if (!check()) { - const bool hasNonMandatoryFlags = (flags & STANDARD_NOT_MANDATORY_VERIFY_FLAGS) != 0; - const bool hasDIP0020Opcodes = (flags & SCRIPT_ENABLE_DIP0020_OPCODES) != 0; - - if (hasNonMandatoryFlags || !hasDIP0020Opcodes) { - // Check whether the failure was caused by a - // non-mandatory script verification check, such as - // non-standard DER encodings or non-null dummy - // arguments; if so, ensure we return NOT_STANDARD - // instead of CONSENSUS to avoid downstream users - // splitting the network between upgraded and - // non-upgraded nodes by banning CONSENSUS-failing - // data providers. - CScriptCheck check2(coin.out, tx, i, - (flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS) | SCRIPT_ENABLE_DIP0020_OPCODES, cacheSigStore, &txdata); - if (check2()) - return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError()))); - } - // MANDATORY flag failures correspond to - // ValidationInvalidReason::CONSENSUS. Because CONSENSUS - // failures are the most serious case of validation - // failures, we may need to consider using - // RECENT_CONSENSUS_CHANGE for any script failure that - // could be due to non-upgraded nodes which we may want to - // support, to avoid splitting the network (but this - // depends on the details of how net_processing handles - // such errors). - return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, strprintf("mandatory-script-verify-flag-failed (%s)", ScriptErrorString(check.GetScriptError()))); - } - } + if (tx.IsCoinBase()) return true; + + if (pvChecks) { + pvChecks->reserve(tx.vin.size()); + } + + // First check if script executions have been cached with the same + // flags. Note that this assumes that the inputs provided are + // correct (ie that the transaction hash which is in tx's prevouts + // properly commits to the scriptPubKey in the inputs view of that + // transaction). + uint256 hashCacheEntry; + CSHA256 hasher = g_scriptExecutionCacheHasher; + hasher.Write(tx.GetHash().begin(), 32).Write((unsigned char*)&flags, sizeof(flags)).Finalize(hashCacheEntry.begin()); + AssertLockHeld(cs_main); //TODO: Remove this requirement by making CuckooCache not require external locks + if (g_scriptExecutionCache.contains(hashCacheEntry, !cacheFullScriptStore)) { + return true; + } - if (cacheFullScriptStore && !pvChecks) { - // We executed all of the provided scripts, and were told to - // cache the result. Do so now. - g_scriptExecutionCache.insert(hashCacheEntry); + if (!txdata.m_ready) { + txdata.Init(tx, {}); + } + + for (unsigned int i = 0; i < tx.vin.size(); i++) { + const COutPoint &prevout = tx.vin[i].prevout; + const Coin& coin = inputs.AccessCoin(prevout); + assert(!coin.IsSpent()); + + // We very carefully only pass in things to CScriptCheck which + // are clearly committed to by tx' witness hash. This provides + // a sanity check that our caching is not introducing consensus + // failures through additional data in, eg, the coins being + // spent being checked as a part of CScriptCheck. + + // Verify signature + CScriptCheck check(coin.out, tx, i, flags, cacheSigStore, &txdata); + if (pvChecks) { + pvChecks->push_back(CScriptCheck()); + check.swap(pvChecks->back()); + } else if (!check()) { + const bool hasNonMandatoryFlags = (flags & STANDARD_NOT_MANDATORY_VERIFY_FLAGS) != 0; + const bool hasDIP0020Opcodes = (flags & SCRIPT_ENABLE_DIP0020_OPCODES) != 0; + + if (hasNonMandatoryFlags || !hasDIP0020Opcodes) { + // Check whether the failure was caused by a + // non-mandatory script verification check, such as + // non-standard DER encodings or non-null dummy + // arguments; if so, ensure we return NOT_STANDARD + // instead of CONSENSUS to avoid downstream users + // splitting the network between upgraded and + // non-upgraded nodes by banning CONSENSUS-failing + // data providers. + CScriptCheck check2(coin.out, tx, i, + (flags & ~STANDARD_NOT_MANDATORY_VERIFY_FLAGS) | SCRIPT_ENABLE_DIP0020_OPCODES, cacheSigStore, &txdata); + if (check2()) + return state.Invalid(ValidationInvalidReason::TX_NOT_STANDARD, false, REJECT_NONSTANDARD, strprintf("non-mandatory-script-verify-flag (%s)", ScriptErrorString(check.GetScriptError()))); } + // MANDATORY flag failures correspond to + // ValidationInvalidReason::CONSENSUS. Because CONSENSUS + // failures are the most serious case of validation + // failures, we may need to consider using + // RECENT_CONSENSUS_CHANGE for any script failure that + // could be due to non-upgraded nodes which we may want to + // support, to avoid splitting the network (but this + // depends on the details of how net_processing handles + // such errors). + return state.Invalid(ValidationInvalidReason::CONSENSUS, false, REJECT_INVALID, strprintf("mandatory-script-verify-flag-failed (%s)", ScriptErrorString(check.GetScriptError()))); } } + if (cacheFullScriptStore && !pvChecks) { + // We executed all of the provided scripts, and were told to + // cache the result. Do so now. + g_scriptExecutionCache.insert(hashCacheEntry); + } + boost::posix_time::ptime finish = boost::posix_time::microsec_clock::local_time(); boost::posix_time::time_duration diff = finish - start; statsClient.timing("CheckInputs_ms", diff.total_milliseconds(), 1.0f); - return true; } @@ -2055,6 +2163,11 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl pindexBestHeader->GetAncestor(pindex->nHeight) == pindex && pindexBestHeader->nChainWork >= nMinimumChainWork) { // This block is a member of the assumed verified chain and an ancestor of the best header. + // Script verification is skipped when connecting blocks under the + // assumevalid block. Assuming the assumevalid block is valid this + // is safe because block merkle hashes are still computed and checked, + // Of course, if an assumed valid block is invalid due to false scriptSigs + // this optimization would allow an invalid chain to be accepted. // The equivalent time check discourages hash power from extorting the network via DOS attack // into accepting an invalid block through telling users they must manually set assumevalid. // Requiring a software change or burying the invalid block, regardless of the setting, makes @@ -2252,7 +2365,7 @@ bool CChainState::ConnectBlock(const CBlock& block, CValidationState& state, CBl std::vector vChecks; bool fCacheResults = fJustCheck; /* Don't cache results if we're actually connecting blocks (still consult the cache, though) */ - if (fScriptChecks && !CheckInputs(tx, state, view, fScriptChecks, flags, fCacheResults, fCacheResults, txsdata[i], g_parallel_script_checks ? &vChecks : nullptr)) { + if (fScriptChecks && !CheckInputs(tx, state, view, flags, fCacheResults, fCacheResults, txsdata[i], g_parallel_script_checks ? &vChecks : nullptr)) { if (state.GetReason() == ValidationInvalidReason::TX_NOT_STANDARD) { // CheckInputs may return NOT_STANDARD for extra flags we passed, // but we can't return that, as it's not defined for a block, so @@ -2490,11 +2603,11 @@ bool CChainState::FlushStateToDisk( if (nManualPruneHeight > 0) { LOG_TIME_MILLIS_WITH_CATEGORY("find files to prune (manual)", BCLog::BENCHMARK); - FindFilesToPruneManual(g_chainman, setFilesToPrune, nManualPruneHeight); + m_blockman.FindFilesToPruneManual(setFilesToPrune, nManualPruneHeight, m_chain.Height()); } else { LOG_TIME_MILLIS_WITH_CATEGORY("find files to prune", BCLog::BENCHMARK); - FindFilesToPrune(g_chainman, setFilesToPrune, chainparams.PruneAfterHeight()); + m_blockman.FindFilesToPrune(setFilesToPrune, chainparams.PruneAfterHeight(), m_chain.Height(), IsInitialBlockDownload()); fCheckForPruning = false; } if (!setFilesToPrune.empty()) { @@ -2636,7 +2749,7 @@ static void AppendWarning(std::string& res, const std::string& warn) } /** Check warning conditions and do some notifications on new chain tip set. */ -void static UpdateTip(CTxMemPool& mempool, const CBlockIndex *pindexNew, const CChainParams& chainParams, const CEvoDB& evoDb) +static void UpdateTip(CTxMemPool& mempool, const CBlockIndex* pindexNew, const CChainParams& chainParams, CChainState& active_chainstate, const CEvoDB& evoDb) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { // New best block @@ -2649,7 +2762,8 @@ void static UpdateTip(CTxMemPool& mempool, const CBlockIndex *pindexNew, const C } std::string warningMessages; - if (!::ChainstateActive().IsInitialBlockDownload()) + assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate)); + if (!active_chainstate.IsInitialBlockDownload()) { int nUpgraded = 0; const CBlockIndex* pindex = pindexNew; @@ -2676,11 +2790,12 @@ void static UpdateTip(CTxMemPool& mempool, const CBlockIndex *pindexNew, const C if (nUpgraded > 0) AppendWarning(warningMessages, strprintf(_("%d of last 100 blocks have unexpected version").translated, nUpgraded)); } + assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate)); LogPrintf("%s: new best=%s height=%d version=0x%08x log2_work=%.8g tx=%lu date='%s' progress=%f cache=%.1fMiB(%utxo) evodb_cache=%.1fMiB%s\n", __func__, pindexNew->GetBlockHash().ToString(), pindexNew->nHeight, pindexNew->nVersion, log(pindexNew->nChainWork.getdouble())/log(2.0), (unsigned long)pindexNew->nChainTx, FormatISO8601DateTime(pindexNew->GetBlockTime()), - GuessVerificationProgress(chainParams.TxData(), pindexNew), ::ChainstateActive().CoinsTip().DynamicMemoryUsage() * (1.0 / (1<<20)), ::ChainstateActive().CoinsTip().GetCacheSize(), + GuessVerificationProgress(chainParams.TxData(), pindexNew), active_chainstate.CoinsTip().DynamicMemoryUsage() * (1.0 / (1<<20)), active_chainstate.CoinsTip().GetCacheSize(), evoDb.GetMemoryUsage() * (1.0 / (1<<20)), !warningMessages.empty() ? strprintf(" warning='%s'", warningMessages) : ""); } @@ -2740,7 +2855,7 @@ bool CChainState::DisconnectTip(CValidationState& state, const CChainParams& cha m_chain.SetTip(pindexDelete->pprev); - UpdateTip(m_mempool, pindexDelete->pprev, chainparams, *m_evoDb); + UpdateTip(m_mempool, pindexDelete->pprev, chainparams, *this, *m_evoDb); // Let wallets know transactions went from 1-confirmed to // 0-confirmed or conflicted: GetMainSignals().BlockDisconnected(pblock, pindexDelete); @@ -2852,7 +2967,7 @@ bool CChainState::ConnectTip(CValidationState& state, const CChainParams& chainp disconnectpool.removeForBlock(blockConnecting.vtx); // Update m_chain & related variables. m_chain.SetTip(pindexNew); - UpdateTip(m_mempool, pindexNew, chainparams, *m_evoDb); + UpdateTip(m_mempool, pindexNew, chainparams, *this, *m_evoDb); int64_t nTime6 = GetTimeMicros(); nTimePostConnect += nTime6 - nTime5; nTimeTotal += nTime6 - nTime1; LogPrint(BCLog::BENCHMARK, " - Connect postprocess: %.2fms [%.2fs (%.2fms/blk)]\n", (nTime6 - nTime5) * MILLI, nTimePostConnect * MICRO, nTimePostConnect * MILLI / nBlocksTotal); @@ -2951,9 +3066,10 @@ bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainPar { AssertLockHeld(cs_main); AssertLockHeld(m_mempool.cs); + assert(std::addressof(::ChainstateActive()) == std::addressof(*this)); - const CBlockIndex *pindexOldTip = m_chain.Tip(); - const CBlockIndex *pindexFork = m_chain.FindFork(pindexMostWork); + const CBlockIndex* pindexOldTip = m_chain.Tip(); + const CBlockIndex* pindexFork = m_chain.FindFork(pindexMostWork); // Disconnect active blocks which are no longer in the best chain. bool fBlocksDisconnected = false; @@ -2962,7 +3078,7 @@ bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainPar if (!DisconnectTip(state, chainparams, &disconnectpool)) { // This is likely a fatal error, but keep the mempool consistent, // just in case. Only remove from the mempool in this case. - UpdateMempoolForReorg(m_mempool, disconnectpool, false); + UpdateMempoolForReorg(*this, m_mempool, disconnectpool, false); // If we're unable to disconnect a block during normal operation, // then that is a failure of our local system -- we should abort @@ -2973,7 +3089,7 @@ bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainPar fBlocksDisconnected = true; } - // Build list of new blocks to connect. + // Build list of new blocks to connect (in descending height order). std::vector vpindexToConnect; bool fContinue = true; int nHeight = pindexFork ? pindexFork->nHeight : -1; @@ -2983,7 +3099,7 @@ bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainPar int nTargetHeight = std::min(nHeight + 32, pindexMostWork->nHeight); vpindexToConnect.clear(); vpindexToConnect.reserve(nTargetHeight - nHeight); - CBlockIndex *pindexIter = pindexMostWork->GetAncestor(nTargetHeight); + CBlockIndex* pindexIter = pindexMostWork->GetAncestor(nTargetHeight); while (pindexIter && pindexIter->nHeight != nHeight) { vpindexToConnect.push_back(pindexIter); pindexIter = pindexIter->pprev; @@ -2991,7 +3107,7 @@ bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainPar nHeight = nTargetHeight; // Connect new blocks. - for (CBlockIndex *pindexConnect : reverse_iterate(vpindexToConnect)) { + for (CBlockIndex* pindexConnect : reverse_iterate(vpindexToConnect)) { if (!ConnectTip(state, chainparams, pindexConnect, pindexConnect == pindexMostWork ? pblock : std::shared_ptr(), connectTrace, disconnectpool)) { if (state.IsInvalid()) { // The block violates a consensus rule. @@ -3006,7 +3122,7 @@ bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainPar // A system error occurred (disk space, database error, ...). // Make the mempool consistent with the current tip, just in case // any observers try to use it before shutdown. - UpdateMempoolForReorg(m_mempool, disconnectpool, false); + UpdateMempoolForReorg(*this, m_mempool, disconnectpool, false); return false; } } else { @@ -3023,20 +3139,16 @@ bool CChainState::ActivateBestChainStep(CValidationState& state, const CChainPar if (fBlocksDisconnected) { // If any blocks were disconnected, disconnectpool may be non empty. Add // any disconnected transactions back to the mempool. - UpdateMempoolForReorg(m_mempool, disconnectpool, true); + UpdateMempoolForReorg(*this, m_mempool, disconnectpool, true); } - m_mempool.check(&CoinsTip()); + m_mempool.check(*this); - // Callbacks/notifications for a new best chain. - if (fInvalidFound) - CheckForkWarningConditionsOnNewFork(vpindexToConnect.back()); - else - CheckForkWarningConditions(); + CheckForkWarningConditions(); return true; } -static bool NotifyHeaderTip() LOCKS_EXCLUDED(cs_main) { +static bool NotifyHeaderTip(CChainState& chainstate) LOCKS_EXCLUDED(cs_main) { bool fNotify = false; bool fInitialBlockDownload = false; static CBlockIndex* pindexHeaderOld = nullptr; @@ -3047,7 +3159,8 @@ static bool NotifyHeaderTip() LOCKS_EXCLUDED(cs_main) { if (pindexHeader != pindexHeaderOld) { fNotify = true; - fInitialBlockDownload = ::ChainstateActive().IsInitialBlockDownload(); + assert(std::addressof(::ChainstateActive()) == std::addressof(chainstate)); + fInitialBlockDownload = chainstate.IsInitialBlockDownload(); pindexHeaderOld = pindexHeader; } } @@ -3172,10 +3285,6 @@ bool CChainState::ActivateBestChain(CValidationState &state, const CChainParams& return true; } -bool ActivateBestChain(CValidationState &state, const CChainParams& chainparams, std::shared_ptr pblock) { - return ::ChainstateActive().ActivateBestChain(state, chainparams, std::move(pblock)); -} - bool CChainState::PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex *pindex) { { @@ -3204,9 +3313,6 @@ bool CChainState::PreciousBlock(CValidationState& state, const CChainParams& par return ActivateBestChain(state, params, std::shared_ptr()); } -bool PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex *pindex) { - return ::ChainstateActive().PreciousBlock(state, params, pindex); -} bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex) { @@ -3278,7 +3384,8 @@ bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& c // transactions back to the mempool if disconnecting was successful, // and we're not doing a very deep invalidation (in which case // keeping the mempool up to date is probably futile anyway). - UpdateMempoolForReorg(m_mempool, disconnectpool, /* fAddToMempool = */ (++disconnected <= 10) && ret); + assert(std::addressof(::ChainstateActive()) == std::addressof(*this)); + UpdateMempoolForReorg(*this, m_mempool, disconnectpool, /* fAddToMempool = */ (++disconnected <= 10) && ret); if (!ret) return false; assert(invalid_walk_tip->pprev == m_chain.Tip()); @@ -3361,19 +3468,16 @@ bool CChainState::InvalidateBlock(CValidationState& state, const CChainParams& c return true; } -bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex) { - return ::ChainstateActive().InvalidateBlock(state, chainparams, pindex); -} - void CChainState::EnforceBlock(CValidationState& state, const CChainParams& chainparams, const CBlockIndex *pindex) { AssertLockNotHeld(::cs_main); LOCK2(m_cs_chainstate, ::cs_main); + assert(std::addressof(::ChainstateActive()) == std::addressof(*this)); const CBlockIndex* pindex_walk = pindex; - while (pindex_walk && !::ChainActive().Contains(pindex_walk)) { + while (pindex_walk && !m_chain.Contains(pindex_walk)) { // Mark all blocks that have the same prevBlockHash but are not equal to blockHash as conflicting auto itp = g_chainman.PrevBlockIndex().equal_range(pindex_walk->pprev->GetBlockHash()); for (auto jt = itp.first; jt != itp.second; ++jt) { @@ -3392,17 +3496,14 @@ void CChainState::EnforceBlock(CValidationState& state, const CChainParams& chai } // In case blocks from the enforced chain are invalid at the moment, reconsider them. if (!pindex->IsValid()) { - ResetBlockFailureFlags(LookupBlockIndex(pindex->GetBlockHash())); + ResetBlockFailureFlags(m_blockman.LookupBlockIndex(pindex->GetBlockHash())); } } -void EnforceBlock(CValidationState& state, const CChainParams& chainparams, const CBlockIndex *pindex) { - return ::ChainstateActive().EnforceBlock(state, chainparams, pindex); -} - bool CChainState::MarkConflictingBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex *pindex) { AssertLockHeld(cs_main); + assert(std::addressof(::ChainstateActive()) == std::addressof(*this)); // We first disconnect backwards and then mark the blocks as conflicting. @@ -3424,7 +3525,7 @@ bool CChainState::MarkConflictingBlock(CValidationState& state, const CChainPara if (!DisconnectTip(state, chainparams, &disconnectpool)) { // It's probably hopeless to try to make the mempool consistent // here if DisconnectTip failed, but we can try. - UpdateMempoolForReorg(m_mempool, disconnectpool, false); + UpdateMempoolForReorg(*this, m_mempool, disconnectpool, false); return false; } if (pindexOldTip == pindexBestHeader) { @@ -3446,7 +3547,7 @@ bool CChainState::MarkConflictingBlock(CValidationState& state, const CChainPara // DisconnectTip will add transactions to disconnectpool; try to add these // back to the mempool. - UpdateMempoolForReorg(m_mempool, disconnectpool, true); + UpdateMempoolForReorg(*this, m_mempool, disconnectpool, true); } // m_mempool.cs // The resulting new best tip may not be in setBlockIndexCandidates anymore, so @@ -3531,10 +3632,6 @@ void CChainState::ResetBlockFailureFlags(CBlockIndex *pindex) { } } -void ResetBlockFailureFlags(CBlockIndex *pindex) { - return ::ChainstateActive().ResetBlockFailureFlags(pindex); -} - CBlockIndex* BlockManager::AddToBlockIndex(const CBlockHeader& block, enum BlockStatus nStatus) { assert(!(nStatus & BLOCK_FAILED_MASK)); // no failed blocks allowed @@ -3628,7 +3725,7 @@ void CChainState::ReceivedBlockTransactions(const CBlock& block, CBlockIndex* pi } } -static bool FindBlockPos(FlatFilePos &pos, unsigned int nAddSize, unsigned int nHeight, uint64_t nTime, bool fKnown = false) +static bool FindBlockPos(FlatFilePos &pos, unsigned int nAddSize, unsigned int nHeight, CChain& active_chain, uint64_t nTime, bool fKnown = false) { LOCK(cs_LastBlockFile); @@ -3643,7 +3740,8 @@ static bool FindBlockPos(FlatFilePos &pos, unsigned int nAddSize, unsigned int n // when the undo file is keeping up with the block file, we want to flush it explicitly // when it is lagging behind (more blocks arrive than are being connected), we let the // undo block write case handle it - finalize_undo = (vinfoBlockFile[nFile].nHeightLast == (unsigned int)::ChainActive().Tip()->nHeight); + assert(std::addressof(::ChainActive()) == std::addressof(active_chain)); + finalize_undo = (vinfoBlockFile[nFile].nHeightLast == (unsigned int)active_chain.Tip()->nHeight); nFile++; if (vinfoBlockFile.size() <= nFile) { vinfoBlockFile.resize(nFile + 1); @@ -3788,14 +3886,14 @@ bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::P return true; } -//! Returns last CBlockIndex* that is a checkpoint -static CBlockIndex* GetLastCheckpoint(const CCheckpointData& data) EXCLUSIVE_LOCKS_REQUIRED(cs_main) +CBlockIndex* BlockManager::GetLastCheckpoint(const CCheckpointData& data) { const MapCheckpoints& checkpoints = data.mapCheckpoints; for (const MapCheckpoints::value_type& i : reverse_iterate(checkpoints)) { const uint256& hash = i.second; + assert(std::addressof(g_chainman.m_blockman) == std::addressof(*this)); CBlockIndex* pindex = LookupBlockIndex(hash); if (pindex) { return pindex; @@ -3813,7 +3911,7 @@ static CBlockIndex* GetLastCheckpoint(const CCheckpointData& data) EXCLUSIVE_LOC * in ConnectBlock(). * Note that -reindex-chainstate skips the validation that happens here! */ -static bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const CChainParams& params, const CBlockIndex* pindexPrev, int64_t nAdjustedTime) EXCLUSIVE_LOCKS_REQUIRED(cs_main) +static bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, BlockManager& blockman, const CChainParams& params, const CBlockIndex* pindexPrev, int64_t nAdjustedTime) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { assert(pindexPrev != nullptr); const int nHeight = pindexPrev->nHeight + 1; @@ -3839,7 +3937,8 @@ static bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationSta // Don't accept any forks from the main chain prior to last checkpoint. // GetLastCheckpoint finds the last checkpoint in MapCheckpoints that's in our // BlockIndex(). - CBlockIndex* pcheckpoint = GetLastCheckpoint(params.Checkpoints()); + assert(std::addressof(g_chainman.m_blockman) == std::addressof(blockman)); + CBlockIndex* pcheckpoint = blockman.GetLastCheckpoint(params.Checkpoints()); if (pcheckpoint && nHeight < pcheckpoint->nHeight) return state.Invalid(ValidationInvalidReason::BLOCK_CHECKPOINT, error("%s: forked chain older than last checkpoint (height %d)", __func__, nHeight), REJECT_CHECKPOINT, "bad-fork-prior-to-checkpoint"); } @@ -3969,7 +4068,7 @@ bool BlockManager::AcceptBlockHeader(const CBlockHeader& block, CValidationState // it's ok-ish, the other node is probably missing the latest chainlock return state.Invalid(ValidationInvalidReason::BLOCK_CHAINLOCK, error("%s: prev block %s conflicts with chainlock", __func__, block.hashPrevBlock.ToString()), REJECT_INVALID, "bad-prevblk-chainlock"); - if (!ContextualCheckBlockHeader(block, state, chainparams, pindexPrev, GetAdjustedTime())) + if (!ContextualCheckBlockHeader(block, state, *this, chainparams, pindexPrev, GetAdjustedTime())) return error("%s: Consensus::ContextualCheckBlockHeader: %s, %s", __func__, hash.ToString(), FormatStateMessage(state)); /* Determine if this block descends from any block which has been found @@ -4032,6 +4131,7 @@ bool BlockManager::AcceptBlockHeader(const CBlockHeader& block, CValidationState // Exposed wrapper for AcceptBlockHeader bool ChainstateManager::ProcessNewBlockHeaders(const std::vector& headers, CValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex, CBlockHeader *first_invalid) { + assert(std::addressof(::ChainstateActive()) == std::addressof(ActiveChainstate())); AssertLockNotHeld(cs_main); if (first_invalid != nullptr) first_invalid->SetNull(); { @@ -4040,7 +4140,7 @@ bool ChainstateManager::ProcessNewBlockHeaders(const std::vector& CBlockIndex *pindex = nullptr; // Use a temp pindex instead of ppindex to avoid a const_cast bool accepted = m_blockman.AcceptBlockHeader( header, state, chainparams, &pindex); - ::ChainstateActive().CheckBlockIndex(chainparams.GetConsensus()); + ActiveChainstate().CheckBlockIndex(chainparams.GetConsensus()); if (!accepted) { if (first_invalid) *first_invalid = header; @@ -4051,8 +4151,8 @@ bool ChainstateManager::ProcessNewBlockHeaders(const std::vector& } } } - if (NotifyHeaderTip()) { - if (::ChainstateActive().IsInitialBlockDownload() && ppindex && *ppindex) { + if (NotifyHeaderTip(ActiveChainstate())) { + if (ActiveChainstate().IsInitialBlockDownload() && ppindex && *ppindex) { LogPrintf("Synchronizing blockheaders, height: %d (~%.2f%%)\n", (*ppindex)->nHeight, 100.0/((*ppindex)->nHeight+(GetAdjustedTime() - (*ppindex)->GetBlockTime()) / Params().GetConsensus().nPowTargetSpacing) * (*ppindex)->nHeight); } } @@ -4060,12 +4160,12 @@ bool ChainstateManager::ProcessNewBlockHeaders(const std::vector& } /** Store block on disk. If dbp is non-nullptr, the file is known to already reside on disk */ -static FlatFilePos SaveBlockToDisk(const CBlock& block, int nHeight, const CChainParams& chainparams, const FlatFilePos* dbp) { +static FlatFilePos SaveBlockToDisk(const CBlock& block, int nHeight, CChain& active_chain, const CChainParams& chainparams, const FlatFilePos* dbp) { unsigned int nBlockSize = ::GetSerializeSize(block, CLIENT_VERSION); FlatFilePos blockPos; if (dbp != nullptr) blockPos = *dbp; - if (!FindBlockPos(blockPos, nBlockSize+8, nHeight, block.GetBlockTime(), dbp != nullptr)) { + if (!FindBlockPos(blockPos, nBlockSize+8, nHeight, active_chain, block.GetBlockTime(), dbp != nullptr)) { error("%s: FindBlockPos failed", __func__); return FlatFilePos(); } @@ -4146,8 +4246,9 @@ bool CChainState::AcceptBlock(const std::shared_ptr& pblock, CVali // Write block to history file if (fNewBlock) *fNewBlock = true; + assert(std::addressof(::ChainActive()) == std::addressof(m_chain)); try { - FlatFilePos blockPos = SaveBlockToDisk(block, pindex->nHeight, chainparams, dbp); + FlatFilePos blockPos = SaveBlockToDisk(block, pindex->nHeight, m_chain, chainparams, dbp); if (blockPos.IsNull()) { state.Error(strprintf("%s: Failed to find position to write new block to disk", __func__)); return false; @@ -4173,6 +4274,7 @@ bool CChainState::AcceptBlock(const std::shared_ptr& pblock, CVali bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr pblock, bool fForceProcessing, bool* fNewBlock) { AssertLockNotHeld(cs_main); + assert(std::addressof(::ChainstateActive()) == std::addressof(ActiveChainstate())); { CBlockIndex *pindex = nullptr; @@ -4188,7 +4290,7 @@ bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const s bool ret = CheckBlock(*pblock, state, chainparams.GetConsensus()); if (ret) { // Store to disk - ret = ::ChainstateActive().AcceptBlock(pblock, state, chainparams, &pindex, fForceProcessing, nullptr, fNewBlock); + ret = ActiveChainstate().AcceptBlock(pblock, state, chainparams, &pindex, fForceProcessing, nullptr, fNewBlock); } if (!ret) { GetMainSignals().BlockChecked(*pblock, state); @@ -4196,27 +4298,36 @@ bool ChainstateManager::ProcessNewBlock(const CChainParams& chainparams, const s } } - NotifyHeaderTip(); + NotifyHeaderTip(ActiveChainstate()); CValidationState state; // Only used to report errors, not invalidity - ignore it - if (!::ChainstateActive().ActivateBestChain(state, chainparams, pblock)) + if (!ActiveChainstate().ActivateBestChain(state, chainparams, pblock)) return error("%s: ActivateBestChain failed: %s", __func__, FormatStateMessage(state)); LogPrintf("%s : ACCEPTED\n", __func__); return true; } -bool TestBlockValidity(CValidationState& state, llmq::CChainLocksHandler& clhandler, CEvoDB& evoDb, const CChainParams& chainparams, const CBlock& block, CBlockIndex* pindexPrev, bool fCheckPOW, bool fCheckMerkleRoot) +bool TestBlockValidity(CValidationState& state, + llmq::CChainLocksHandler& clhandler, + CEvoDB& evoDb, + const CChainParams& chainparams, + CChainState& chainstate, + const CBlock& block, + CBlockIndex* pindexPrev, + bool fCheckPOW, + bool fCheckMerkleRoot) { AssertLockHeld(cs_main); - assert(pindexPrev && pindexPrev == ::ChainActive().Tip()); + assert(std::addressof(::ChainstateActive()) == std::addressof(chainstate)); + assert(pindexPrev && pindexPrev == chainstate.m_chain.Tip()); uint256 hash = block.GetHash(); if (clhandler.HasConflictingChainLock(pindexPrev->nHeight + 1, hash)) { return state.Invalid(ValidationInvalidReason::BLOCK_INVALID_PREV, error("%s: conflicting with chainlock", __func__), REJECT_INVALID, "bad-chainlock"); } - CCoinsViewCache viewNew(&::ChainstateActive().CoinsTip()); + CCoinsViewCache viewNew(&chainstate.CoinsTip()); uint256 block_hash(block.GetHash()); CBlockIndex indexDummy(block); indexDummy.pprev = pindexPrev; @@ -4227,13 +4338,14 @@ bool TestBlockValidity(CValidationState& state, llmq::CChainLocksHandler& clhand auto dbTx = evoDb.BeginTransaction(); // NOTE: CheckBlockHeader is called by CheckBlock - if (!ContextualCheckBlockHeader(block, state, chainparams, pindexPrev, GetAdjustedTime())) + assert(std::addressof(g_chainman.m_blockman) == std::addressof(chainstate.m_blockman)); + if (!ContextualCheckBlockHeader(block, state, chainstate.m_blockman, chainparams, pindexPrev, GetAdjustedTime())) return error("%s: Consensus::ContextualCheckBlockHeader: %s", __func__, FormatStateMessage(state)); if (!CheckBlock(block, state, chainparams.GetConsensus(), fCheckPOW, fCheckMerkleRoot)) return error("%s: Consensus::CheckBlock: %s", __func__, FormatStateMessage(state)); if (!ContextualCheckBlock(block, state, chainparams.GetConsensus(), pindexPrev)) return error("%s: Consensus::ContextualCheckBlock: %s", __func__, FormatStateMessage(state)); - if (!::ChainstateActive().ConnectBlock(block, state, &indexDummy, viewNew, chainparams, true)) + if (!chainstate.ConnectBlock(block, state, &indexDummy, viewNew, chainparams, true)) return false; assert(state.IsValid()); @@ -4256,12 +4368,12 @@ uint64_t CalculateCurrentUsage() return retval; } -void ChainstateManager::PruneOneBlockFile(const int fileNumber) +void BlockManager::PruneOneBlockFile(const int fileNumber) { AssertLockHeld(cs_main); LOCK(cs_LastBlockFile); - for (const auto& entry : m_blockman.m_block_index) { + for (const auto& entry : m_block_index) { CBlockIndex* pindex = entry.second; if (pindex->nFile == fileNumber) { pindex->nStatus &= ~BLOCK_HAVE_DATA; @@ -4275,12 +4387,12 @@ void ChainstateManager::PruneOneBlockFile(const int fileNumber) // to be downloaded again in order to consider its chain, at which // point it would be considered as a candidate for // m_blocks_unlinked or setBlockIndexCandidates. - auto range = m_blockman.m_blocks_unlinked.equal_range(pindex->pprev); + auto range = m_blocks_unlinked.equal_range(pindex->pprev); while (range.first != range.second) { std::multimap::iterator _it = range.first; range.first++; if (_it->second == pindex) { - m_blockman.m_blocks_unlinked.erase(_it); + m_blocks_unlinked.erase(_it); } } } @@ -4301,22 +4413,23 @@ void UnlinkPrunedFiles(const std::set& setFilesToPrune) } } -/* Calculate the block/rev files to delete based on height specified by user with RPC command pruneblockchain */ -static void FindFilesToPruneManual(ChainstateManager& chainman, std::set& setFilesToPrune, int nManualPruneHeight) +void BlockManager::FindFilesToPruneManual(std::set& setFilesToPrune, int nManualPruneHeight, int chain_tip_height) { assert(fPruneMode && nManualPruneHeight > 0); LOCK2(cs_main, cs_LastBlockFile); - if (::ChainActive().Tip() == nullptr) + if (chain_tip_height < 0) { return; + } // last block to prune is the lesser of (user-specified height, MIN_BLOCKS_TO_KEEP from the tip) - unsigned int nLastBlockWeCanPrune = std::min((unsigned)nManualPruneHeight, ::ChainActive().Tip()->nHeight - MIN_BLOCKS_TO_KEEP); - int count=0; + unsigned int nLastBlockWeCanPrune = std::min((unsigned)nManualPruneHeight, chain_tip_height - MIN_BLOCKS_TO_KEEP); + int count = 0; for (int fileNumber = 0; fileNumber < nLastBlockFile; fileNumber++) { - if (vinfoBlockFile[fileNumber].nSize == 0 || vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune) + if (vinfoBlockFile[fileNumber].nSize == 0 || vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune) { continue; - chainman.PruneOneBlockFile(fileNumber); + } + PruneOneBlockFile(fileNumber); setFilesToPrune.insert(fileNumber); count++; } @@ -4324,56 +4437,42 @@ static void FindFilesToPruneManual(ChainstateManager& chainman, std::set& s } /* This function is called from the RPC code for pruneblockchain */ -void PruneBlockFilesManual(int nManualPruneHeight) +void PruneBlockFilesManual(CChainState& active_chainstate, int nManualPruneHeight) { CValidationState state; const CChainParams& chainparams = Params(); - if (!::ChainstateActive().FlushStateToDisk( + assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate)); + if (!active_chainstate.FlushStateToDisk( chainparams, state, FlushStateMode::NONE, nManualPruneHeight)) { LogPrintf("%s: failed to flush state (%s)\n", __func__, FormatStateMessage(state)); } } -/** - * Prune block and undo files (blk???.dat and undo???.dat) so that the disk space used is less than a user-defined target. - * The user sets the target (in MB) on the command line or in config file. This will be run on startup and whenever new - * space is allocated in a block or undo file, staying below the target. Changing back to unpruned requires a reindex - * (which in this case means the blockchain must be re-downloaded.) - * - * Pruning functions are called from FlushStateToDisk when the global fCheckForPruning flag has been set. - * Block and undo files are deleted in lock-step (when blk00003.dat is deleted, so is rev00003.dat.) - * Pruning cannot take place until the longest chain is at least a certain length (100000 on mainnet, 1000 on testnet, 1000 on regtest). - * Pruning will never delete a block within a defined distance (currently 288) from the active chain's tip. - * The block index is updated by unsetting HAVE_DATA and HAVE_UNDO for any blocks that were stored in the deleted files. - * A db flag records the fact that at least some block files have been pruned. - * - * @param[out] setFilesToPrune The set of file indices that can be unlinked will be returned - */ -static void FindFilesToPrune(ChainstateManager& chainman, std::set& setFilesToPrune, uint64_t nPruneAfterHeight) +void BlockManager::FindFilesToPrune(std::set& setFilesToPrune, uint64_t nPruneAfterHeight, int chain_tip_height, bool is_ibd) { LOCK2(cs_main, cs_LastBlockFile); - if (::ChainActive().Tip() == nullptr || nPruneTarget == 0) { + if (chain_tip_height < 0 || nPruneTarget == 0) { return; } - if ((uint64_t)::ChainActive().Tip()->nHeight <= nPruneAfterHeight) { + if ((uint64_t)chain_tip_height <= nPruneAfterHeight) { return; } - unsigned int nLastBlockWeCanPrune = ::ChainActive().Tip()->nHeight - MIN_BLOCKS_TO_KEEP; + unsigned int nLastBlockWeCanPrune = chain_tip_height - MIN_BLOCKS_TO_KEEP; uint64_t nCurrentUsage = CalculateCurrentUsage(); // We don't check to prune until after we've allocated new space for files // So we should leave a buffer under our target to account for another allocation // before the next pruning. uint64_t nBuffer = BLOCKFILE_CHUNK_SIZE + UNDOFILE_CHUNK_SIZE; uint64_t nBytesToPrune; - int count=0; + int count = 0; if (nCurrentUsage + nBuffer >= nPruneTarget) { // On a prune event, the chainstate DB is flushed. // To avoid excessive prune events negating the benefit of high dbcache // values, we should not prune too rapidly. // So when pruning in IBD, increase the buffer a bit to avoid a re-prune too soon. - if (::ChainstateActive().IsInitialBlockDownload()) { + if (is_ibd) { // Since this is only relevant during IBD, we use a fixed 10% nBuffer += nPruneTarget / 10; } @@ -4381,17 +4480,20 @@ static void FindFilesToPrune(ChainstateManager& chainman, std::set& setFile for (int fileNumber = 0; fileNumber < nLastBlockFile; fileNumber++) { nBytesToPrune = vinfoBlockFile[fileNumber].nSize + vinfoBlockFile[fileNumber].nUndoSize; - if (vinfoBlockFile[fileNumber].nSize == 0) + if (vinfoBlockFile[fileNumber].nSize == 0) { continue; + } - if (nCurrentUsage + nBuffer < nPruneTarget) // are we below our target? + if (nCurrentUsage + nBuffer < nPruneTarget) { // are we below our target? break; + } // don't prune files that could have a block within MIN_BLOCKS_TO_KEEP of the main chain's tip but keep scanning - if (vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune) + if (vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune) { continue; + } - chainman.PruneOneBlockFile(fileNumber); + PruneOneBlockFile(fileNumber); // Queue up the files for removal setFilesToPrune.insert(fileNumber); nCurrentUsage -= nBytesToPrune; @@ -4521,11 +4623,12 @@ void BlockManager::Unload() { m_prev_block_index.clear(); } -bool static LoadBlockIndexDB(ChainstateManager& chainman, const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main) +bool CChainState::LoadBlockIndexDB(const CChainParams& chainparams) { - if (!chainman.m_blockman.LoadBlockIndex( + assert(std::addressof(::ChainstateActive()) == std::addressof(*this)); + if (!m_blockman.LoadBlockIndex( chainparams.GetConsensus(), *pblocktree, - ::ChainstateActive().setBlockIndexCandidates)) { + setBlockIndexCandidates)) { return false; } @@ -4549,7 +4652,7 @@ bool static LoadBlockIndexDB(ChainstateManager& chainman, const CChainParams& ch // Check presence of blk files LogPrintf("Checking all blk files are present...\n"); std::set setBlkDataFiles; - for (const std::pair& item : chainman.BlockIndex()) { + for (const std::pair& item : m_blockman.m_block_index) { CBlockIndex* pindex = item.second; if (pindex->nStatus & BLOCK_HAVE_DATA) { setBlkDataFiles.insert(pindex->nFile); @@ -4591,7 +4694,8 @@ bool static LoadBlockIndexDB(ChainstateManager& chainman, const CChainParams& ch void CChainState::LoadMempool(const ArgsManager& args) { if (args.GetArg("-persistmempool", DEFAULT_PERSIST_MEMPOOL)) { - ::LoadMempool(m_mempool); + assert(std::addressof(::ChainstateActive()) == std::addressof(*this)); + ::LoadMempool(m_mempool, *this); } m_mempool.SetIsLoaded(!ShutdownRequested()); } @@ -4608,7 +4712,7 @@ bool CChainState::LoadChainTip(const CChainParams& chainparams) } // Load pointer to end of best chain - CBlockIndex* pindex = LookupBlockIndex(coins_cache.GetBestBlock()); + CBlockIndex* pindex = m_blockman.LookupBlockIndex(coins_cache.GetBestBlock()); if (!pindex) { return false; } @@ -4634,18 +4738,20 @@ CVerifyDB::~CVerifyDB() uiInterface.ShowProgress("", 100, false); } -bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, CEvoDB& evoDb, int nCheckLevel, int nCheckDepth) +bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CChainState& active_chainstate, CCoinsView *coinsview, CEvoDB& evoDb, int nCheckLevel, int nCheckDepth) { - LOCK(cs_main); - if (::ChainActive().Tip() == nullptr || ::ChainActive().Tip()->pprev == nullptr) + AssertLockHeld(cs_main); + + assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate)); + if (active_chainstate.m_chain.Tip() == nullptr || active_chainstate.m_chain.Tip()->pprev == nullptr) return true; // begin tx and let it rollback auto dbTx = evoDb.BeginTransaction(); // Verify blocks in the best chain - if (nCheckDepth <= 0 || nCheckDepth > ::ChainActive().Height()) - nCheckDepth = ::ChainActive().Height(); + if (nCheckDepth <= 0 || nCheckDepth > active_chainstate.m_chain.Height()) + nCheckDepth = active_chainstate.m_chain.Height(); nCheckLevel = std::max(0, std::min(4, nCheckLevel)); LogPrintf("Verifying last %i blocks at level %i\n", nCheckDepth, nCheckLevel); CCoinsViewCache coins(coinsview); @@ -4655,15 +4761,15 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, CValidationState state; int reportDone = 0; LogPrintf("[0%%]..."); /* Continued */ - for (pindex = ::ChainActive().Tip(); pindex && pindex->pprev; pindex = pindex->pprev) { - const int percentageDone = std::max(1, std::min(99, (int)(((double)(::ChainActive().Height() - pindex->nHeight)) / (double)nCheckDepth * (nCheckLevel >= 4 ? 50 : 100)))); + for (pindex = active_chainstate.m_chain.Tip(); pindex && pindex->pprev; pindex = pindex->pprev) { + const int percentageDone = std::max(1, std::min(99, (int)(((double)(active_chainstate.m_chain.Height() - pindex->nHeight)) / (double)nCheckDepth * (nCheckLevel >= 4 ? 50 : 100)))); if (reportDone < percentageDone/10) { // report every 10% step LogPrintf("[%d%%]...", percentageDone); /* Continued */ reportDone = percentageDone/10; } uiInterface.ShowProgress(_("Verifying blocks...").translated, percentageDone, false); - if (pindex->nHeight <= ::ChainActive().Height()-nCheckDepth) + if (pindex->nHeight <= active_chainstate.m_chain.Height()-nCheckDepth) break; if (fPruneMode && !(pindex->nStatus & BLOCK_HAVE_DATA)) { // If pruning, only go back as far as we have data. @@ -4688,9 +4794,9 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, } } // check level 3: check for inconsistencies during memory-only disconnect of tip blocks - if (nCheckLevel >= 3 && (coins.DynamicMemoryUsage() + ::ChainstateActive().CoinsTip().DynamicMemoryUsage()) <= ::ChainstateActive().m_coinstip_cache_size_bytes) { + if (nCheckLevel >= 3 && (coins.DynamicMemoryUsage() + active_chainstate.CoinsTip().DynamicMemoryUsage()) <= active_chainstate.m_coinstip_cache_size_bytes) { assert(coins.GetBestBlock() == pindex->GetBlockHash()); - DisconnectResult res = ::ChainstateActive().DisconnectBlock(block, pindex, coins); + DisconnectResult res = active_chainstate.DisconnectBlock(block, pindex, coins); if (res == DISCONNECT_FAILED) { return error("VerifyDB(): *** irrecoverable inconsistency in block data at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); } @@ -4704,26 +4810,26 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, if (ShutdownRequested()) return true; } if (pindexFailure) - return error("VerifyDB(): *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", ::ChainActive().Height() - pindexFailure->nHeight + 1, nGoodTransactions); + return error("VerifyDB(): *** coin database inconsistencies found (last %i blocks, %i good transactions before that)\n", active_chainstate.m_chain.Height() - pindexFailure->nHeight + 1, nGoodTransactions); // store block count as we move pindex at check level >= 4 - int block_count = ::ChainActive().Height() - pindex->nHeight; + int block_count = active_chainstate.m_chain.Height() - pindex->nHeight; // check level 4: try reconnecting blocks if (nCheckLevel >= 4) { - while (pindex != ::ChainActive().Tip()) { - const int percentageDone = std::max(1, std::min(99, 100 - (int)(((double)(::ChainActive().Height() - pindex->nHeight)) / (double)nCheckDepth * 50))); + while (pindex != active_chainstate.m_chain.Tip()) { + const int percentageDone = std::max(1, std::min(99, 100 - (int)(((double)(active_chainstate.m_chain.Height() - pindex->nHeight)) / (double)nCheckDepth * 50))); if (reportDone < percentageDone/10) { // report every 10% step LogPrintf("[%d%%]...", percentageDone); /* Continued */ reportDone = percentageDone/10; } uiInterface.ShowProgress(_("Verifying blocks...").translated, percentageDone, false); - pindex = ::ChainActive().Next(pindex); + pindex = active_chainstate.m_chain.Next(pindex); CBlock block; if (!ReadBlockFromDisk(block, pindex, chainparams.GetConsensus())) return error("VerifyDB(): *** ReadBlockFromDisk failed at %d, hash=%s", pindex->nHeight, pindex->GetBlockHash().ToString()); - if (!::ChainstateActive().ConnectBlock(block, state, pindex, coins, chainparams)) + if (!active_chainstate.ConnectBlock(block, state, pindex, coins, chainparams)) return error("VerifyDB(): *** found unconnectable block at %d, hash=%s (%s)", pindex->nHeight, pindex->GetBlockHash().ToString(), FormatStateMessage(state)); if (ShutdownRequested()) return true; } @@ -4849,10 +4955,10 @@ void CChainState::UnloadBlockIndex() { // May NOT be used after any connections are up as much // of the peer-processing logic assumes a consistent // block index state -void UnloadBlockIndex(CTxMemPool* mempool) +void UnloadBlockIndex(CTxMemPool* mempool, ChainstateManager& chainman) { LOCK(cs_main); - g_chainman.Unload(); + chainman.Unload(); pindexBestInvalid = nullptr; pindexBestHeader = nullptr; if (mempool) mempool->clear(); @@ -4873,7 +4979,7 @@ bool ChainstateManager::LoadBlockIndex(const CChainParams& chainparams) // Load block index from databases bool needs_init = fReindex; if (!fReindex) { - bool ret = LoadBlockIndexDB(*this, chainparams); + bool ret = ActiveChainstate().LoadBlockIndexDB(chainparams); if (!ret) return false; needs_init = m_blockman.m_block_index.empty(); } @@ -4904,7 +5010,8 @@ bool ChainstateManager::LoadBlockIndex(const CChainParams& chainparams) bool CChainState::AddGenesisBlock(const CChainParams& chainparams, const CBlock& block, CValidationState& state) { - FlatFilePos blockPos = SaveBlockToDisk(block, 0, chainparams, nullptr); + assert(std::addressof(::ChainActive()) == std::addressof(m_chain)); + FlatFilePos blockPos = SaveBlockToDisk(block, 0, m_chain, chainparams, nullptr); if (blockPos.IsNull()) return error("%s: writing genesis block to disk failed (%s)", __func__, FormatStateMessage(state)); CBlockIndex* pindex = m_blockman.AddToBlockIndex(block); @@ -4945,12 +5052,7 @@ bool CChainState::LoadGenesisBlock(const CChainParams& chainparams) return true; } -bool LoadGenesisBlock(const CChainParams& chainparams) -{ - return ::ChainstateActive().LoadGenesisBlock(chainparams); -} - -void LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFilePos* dbp) +void CChainState::LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFilePos* dbp) { // Map of disk positions for blocks with unknown parent (only used for reindex) static std::multimap mapBlocksUnknownParent; @@ -5000,7 +5102,8 @@ void LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFi { LOCK(cs_main); // detect out of order blocks, and store them for later - if (hash != chainparams.GetConsensus().hashGenesisBlock && !LookupBlockIndex(block.hashPrevBlock)) { + assert(std::addressof(g_chainman.m_blockman) == std::addressof(m_blockman)); + if (hash != chainparams.GetConsensus().hashGenesisBlock && !m_blockman.LookupBlockIndex(block.hashPrevBlock)) { LogPrint(BCLog::REINDEX, "%s: Out of order block %s, parent %s not known\n", __func__, hash.ToString(), block.hashPrevBlock.ToString()); if (dbp) @@ -5009,10 +5112,12 @@ void LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFi } // process in case the block isn't known yet - CBlockIndex* pindex = LookupBlockIndex(hash); + assert(std::addressof(g_chainman.m_blockman) == std::addressof(m_blockman)); + CBlockIndex* pindex = m_blockman.LookupBlockIndex(hash); if (!pindex || (pindex->nStatus & BLOCK_HAVE_DATA) == 0) { CValidationState state; - if (::ChainstateActive().AcceptBlock(pblock, state, chainparams, nullptr, true, dbp, nullptr)) { + assert(std::addressof(::ChainstateActive()) == std::addressof(*this)); + if (AcceptBlock(pblock, state, chainparams, nullptr, true, dbp, nullptr)) { nLoaded++; } if (state.IsError()) { @@ -5026,12 +5131,14 @@ void LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFi // Activate the genesis block so normal node progress can continue if (hash == chainparams.GetConsensus().hashGenesisBlock) { CValidationState state; + assert(std::addressof(::ChainstateActive()) == std::addressof(*this)); if (!ActivateBestChain(state, chainparams, nullptr)) { break; } } - NotifyHeaderTip(); + assert(std::addressof(::ChainstateActive()) == std::addressof(*this)); + NotifyHeaderTip(*this); // Recursively process earlier encountered successors of this block std::deque queue; @@ -5049,7 +5156,8 @@ void LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFi head.ToString()); LOCK(cs_main); CValidationState dummy; - if (::ChainstateActive().AcceptBlock(pblockrecursive, dummy, chainparams, nullptr, true, &it->second, nullptr)) + assert(std::addressof(::ChainstateActive()) == std::addressof(*this)); + if (AcceptBlock(pblockrecursive, dummy, chainparams, nullptr, true, &it->second, nullptr)) { nLoaded++; queue.push_back(pblockrecursive->GetHash()); @@ -5057,7 +5165,8 @@ void LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFi } range.first++; mapBlocksUnknownParent.erase(it); - NotifyHeaderTip(); + assert(std::addressof(::ChainstateActive()) == std::addressof(*this)); + NotifyHeaderTip(*this); } } } catch (const std::exception& e) { @@ -5313,27 +5422,9 @@ CBlockFileInfo* GetBlockFileInfo(size_t n) return &vinfoBlockFile.at(n); } -ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::DeploymentPos pos) -{ - AssertLockHeld(cs_main); - return VersionBitsState(::ChainActive().Tip(), params, pos, versionbitscache); -} - -BIP9Stats VersionBitsTipStatistics(const Consensus::Params& params, Consensus::DeploymentPos pos) -{ - LOCK(cs_main); - return VersionBitsStatistics(::ChainActive().Tip(), params, pos, versionbitscache); -} - -int VersionBitsTipStateSinceHeight(const Consensus::Params& params, Consensus::DeploymentPos pos) -{ - LOCK(cs_main); - return VersionBitsStateSinceHeight(::ChainActive().Tip(), params, pos, versionbitscache); -} - static const uint64_t MEMPOOL_DUMP_VERSION = 1; -bool LoadMempool(CTxMemPool& pool) +bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate) { const CChainParams& chainparams = Params(); int64_t nExpiryTimeout = gArgs.GetArg("-mempoolexpiry", DEFAULT_MEMPOOL_EXPIRY) * 60 * 60; @@ -5375,7 +5466,8 @@ bool LoadMempool(CTxMemPool& pool) CValidationState state; if (nTime + nExpiryTimeout > nNow) { LOCK(cs_main); - AcceptToMemoryPoolWithTime(chainparams, pool, state, tx, nullptr /* pfMissingInputs */, nTime, + assert(std::addressof(::ChainstateActive()) == std::addressof(active_chainstate)); + AcceptToMemoryPoolWithTime(chainparams, pool, active_chainstate, state, tx, nullptr /* pfMissingInputs */, nTime, false /* bypass_limits */, 0 /* nAbsurdFee */, false /* test_accept */); if (state.IsValid()) { ++count; @@ -5506,21 +5598,8 @@ double GuessVerificationProgress(const ChainTxData& data, const CBlockIndex *pin return std::min(pindex->nChainTx / fTxTotal, 1.0); } -class CMainCleanup -{ -public: - CMainCleanup() {} - ~CMainCleanup() { - // block headers - BlockMap::iterator it1 = g_chainman.BlockIndex().begin(); - for (; it1 != g_chainman.BlockIndex().end(); it1++) - delete (*it1).second; - g_chainman.BlockIndex().clear(); - } -}; -static CMainCleanup instance_of_cmaincleanup; - std::optional ChainstateManager::SnapshotBlockhash() const { + LOCK(::cs_main); // for m_active_chainstate access if (m_active_chainstate != nullptr) { // If a snapshot chainstate exists, it will always be our active. return m_active_chainstate->m_from_snapshot_blockhash; @@ -5530,6 +5609,7 @@ std::optional ChainstateManager::SnapshotBlockhash() const { std::vector ChainstateManager::GetAll() { + LOCK(::cs_main); std::vector out; if (!IsSnapshotValidated() && m_ibd_chainstate) { @@ -5573,17 +5653,20 @@ CChainState& ChainstateManager::InitializeChainstate(std::unique_ptr pblock = std::shared_ptr()) LOCKS_EXCLUDED(cs_main); double ConvertBitsToDouble(unsigned int nBits); CAmount GetBlockSubsidy(int nBits, int nHeight, const Consensus::Params& consensusParams, bool fSuperblockPartOnly = false); @@ -222,10 +212,10 @@ uint64_t CalculateCurrentUsage(); void UnlinkPrunedFiles(const std::set& setFilesToPrune); /** Prune block files up to a given height */ -void PruneBlockFilesManual(int nManualPruneHeight); +void PruneBlockFilesManual(CChainState& active_chainstate, int nManualPruneHeight); /** (try to) add transaction to memory pool */ -bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, +bool AcceptToMemoryPool(CChainState& active_chainstate, CTxMemPool& pool, CValidationState &state, const CTransactionRef &tx, bool* pfMissingInputs, bool bypass_limits, const CAmount nAbsurdFee, bool test_accept=false) EXCLUSIVE_LOCKS_REQUIRED(cs_main); @@ -233,15 +223,6 @@ bool GetUTXOCoin(const COutPoint& outpoint, Coin& coin); int GetUTXOHeight(const COutPoint& outpoint); int GetUTXOConfirmations(const COutPoint& outpoint); -/** Get the BIP9 state for a given deployment at the current tip. */ -ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::DeploymentPos pos); - -/** Get the numerical statistics for the BIP9 state for a given deployment at the current tip. */ -BIP9Stats VersionBitsTipStatistics(const Consensus::Params& params, Consensus::DeploymentPos pos); - -/** Get the block height at which the BIP9 deployment switched into the state for the block building on the current tip. */ -int VersionBitsTipStateSinceHeight(const Consensus::Params& params, Consensus::DeploymentPos pos); - /** Apply the effects of this transaction on the UTXO set represented by view */ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight); @@ -254,12 +235,12 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight); * * See consensus/consensus.h for flag definitions. */ -bool CheckFinalTx(const CTransaction &tx, int flags = -1) EXCLUSIVE_LOCKS_REQUIRED(cs_main); +bool CheckFinalTx(const CBlockIndex* active_chain_tip, const CTransaction &tx, int flags = -1) EXCLUSIVE_LOCKS_REQUIRED(cs_main); /** * Test whether the LockPoints height and time are still valid on the current chain */ -bool TestLockPointValidity(const LockPoints* lp) EXCLUSIVE_LOCKS_REQUIRED(cs_main); +bool TestLockPointValidity(CChain& active_chain, const LockPoints* lp) EXCLUSIVE_LOCKS_REQUIRED(cs_main); /** * Check if transaction will be BIP 68 final in the next block to be created. @@ -272,7 +253,12 @@ bool TestLockPointValidity(const LockPoints* lp) EXCLUSIVE_LOCKS_REQUIRED(cs_mai * * See consensus/consensus.h for flag definitions. */ -bool CheckSequenceLocks(const CTxMemPool& pool, const CTransaction& tx, int flags, LockPoints* lp = nullptr, bool useExistingLockPoints = false) EXCLUSIVE_LOCKS_REQUIRED(cs_main); +bool CheckSequenceLocks(CChainState& active_chainstate, + const CTxMemPool& pool, + const CTransaction& tx, + int flags, + LockPoints* lp = nullptr, + bool useExistingLockPoints = false) EXCLUSIVE_LOCKS_REQUIRED(cs_main); /** * Closure representing one script verification @@ -332,21 +318,24 @@ bool UndoReadFromDisk(CBlockUndo& blockundo, const CBlockIndex* pindex); bool CheckBlock(const CBlock& block, CValidationState& state, const Consensus::Params& consensusParams, bool fCheckPOW = true, bool fCheckMerkleRoot = true); /** Check a block is completely valid from start to finish (only works on top of our current best block) */ -bool TestBlockValidity(CValidationState& state, llmq::CChainLocksHandler& clhandler, CEvoDB& evoDb, const CChainParams& chainparams, const CBlock& block, CBlockIndex* pindexPrev, bool fCheckPOW = true, bool fCheckMerkleRoot = true) EXCLUSIVE_LOCKS_REQUIRED(cs_main); +bool TestBlockValidity(CValidationState& state, + llmq::CChainLocksHandler& clhandler, + CEvoDB& evoDb, + const CChainParams& chainparams, + CChainState& chainstate, + const CBlock& block, + CBlockIndex* pindexPrev, + bool fCheckPOW = true, + bool fCheckMerkleRoot = true) EXCLUSIVE_LOCKS_REQUIRED(cs_main); /** RAII wrapper for VerifyDB: Verify consistency of the block and coin databases */ class CVerifyDB { public: CVerifyDB(); ~CVerifyDB(); - bool VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, CEvoDB& evoDb, int nCheckLevel, int nCheckDepth); + bool VerifyDB(const CChainParams& chainparams, CChainState& active_chainstate, CCoinsView *coinsview, CEvoDB& evoDb, int nCheckLevel, int nCheckDepth) EXCLUSIVE_LOCKS_REQUIRED(cs_main); }; -CBlockIndex* LookupBlockIndex(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main); - -/** Find the last common block between the parameter chain and a locator. */ -CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator) EXCLUSIVE_LOCKS_REQUIRED(cs_main); - enum DisconnectResult { DISCONNECT_OK, // All good. DISCONNECT_UNCLEAN, // Rolled back, but UTXO set was inconsistent with block. @@ -375,7 +364,31 @@ struct CBlockIndexWorkComparator * This data is used mostly in `CChainState` - information about, e.g., * candidate tips is not maintained here. */ -class BlockManager { +class BlockManager +{ + friend CChainState; + +private: + /* Calculate the block/rev files to delete based on height specified by user with RPC command pruneblockchain */ + void FindFilesToPruneManual(std::set& setFilesToPrune, int nManualPruneHeight, int chain_tip_height); + + /** + * Prune block and undo files (blk???.dat and undo???.dat) so that the disk space used is less than a user-defined target. + * The user sets the target (in MB) on the command line or in config file. This will be run on startup and whenever new + * space is allocated in a block or undo file, staying below the target. Changing back to unpruned requires a reindex + * (which in this case means the blockchain must be re-downloaded.) + * + * Pruning functions are called from FlushStateToDisk when the global fCheckForPruning flag has been set. + * Block and undo files are deleted in lock-step (when blk00003.dat is deleted, so is rev00003.dat.) + * Pruning cannot take place until the longest chain is at least a certain length (100000 on mainnet, 1000 on testnet, 1000 on regtest). + * Pruning will never delete a block within a defined distance (currently 288) from the active chain's tip. + * The block index is updated by unsetting HAVE_DATA and HAVE_UNDO for any blocks that were stored in the deleted files. + * A db flag records the fact that at least some block files have been pruned. + * + * @param[out] setFilesToPrune The set of file indices that can be unlinked will be returned + */ + void FindFilesToPrune(std::set& setFilesToPrune, uint64_t nPruneAfterHeight, int chain_tip_height, bool is_ibd); + public: BlockMap m_block_index GUARDED_BY(cs_main); PrevBlockMap m_prev_block_index GUARDED_BY(cs_main); @@ -426,6 +439,9 @@ class BlockManager { /** Create a new block index entry for a given block hash */ CBlockIndex* InsertBlockIndex(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + //! Mark one block file as pruned (modify associated database entries) + void PruneOneBlockFile(const int fileNumber) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + /** * If a block header hasn't already been seen, call CheckBlockHeader on it, ensure * that it doesn't descend from an invalid block, and then add it to m_block_index. @@ -435,6 +451,25 @@ class BlockManager { CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + + CBlockIndex* LookupBlockIndex(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + + /** Find the last common block between the parameter chain and a locator. */ + CBlockIndex* FindForkInGlobalIndex(const CChain& chain, const CBlockLocator& locator) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + + //! Returns last CBlockIndex* that is a checkpoint + CBlockIndex* GetLastCheckpoint(const CCheckpointData& data) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + + /** + * Return the spend height, which is one more than the inputs.GetBestBlock(). + * While checking, GetBestBlock() refers to the parent block. (protected by cs_main) + * This is also true for mempool checks. + */ + int GetSpendHeight(const CCoinsViewCache& inputs) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + + ~BlockManager() { + Unload(); + } }; /** @@ -525,11 +560,6 @@ class CChainState */ mutable std::atomic m_cached_finished_ibd{false}; - //! Reference to a BlockManager instance which itself is shared across all - //! CChainState instances. Keeping a local reference allows us to test more - //! easily as opposed to referencing a global. - BlockManager& m_blockman; - //! mempool that is kept in sync with the chain CTxMemPool& m_mempool; @@ -543,6 +573,10 @@ class CChainState std::unique_ptr& m_evoDb; public: + //! Reference to a BlockManager instance which itself is shared across all + //! CChainState instances. + BlockManager& m_blockman; + explicit CChainState(BlockManager& blockman, std::unique_ptr& clhandler, std::unique_ptr& isman, @@ -625,6 +659,9 @@ class CChainState bool ResizeCoinsCaches(size_t coinstip_size, size_t coinsdb_size) EXCLUSIVE_LOCKS_REQUIRED(::cs_main); + /** Import blocks from an external file */ + void LoadExternalBlockFile(const CChainParams& chainparams, FILE* fileIn, FlatFilePos* dbp = nullptr); + /** * Update the on-disk chain state. * The caches and indexes are flushed depending on the mode we're called with @@ -650,9 +687,10 @@ class CChainState void PruneAndFlush(); /** - * Make the best chain active, in multiple steps. The result is either failure - * or an activated best chain. pblock is either nullptr or a pointer to a block - * that is already loaded (to avoid loading it again from disk). + * Find the best known block, and make it the tip of the block chain. The + * result is either failure or an activated best chain. pblock is either + * nullptr or a pointer to a block that is already loaded (to avoid loading + * it again from disk). * * ActivateBestChain is split into steps (see ActivateBestChainStep) so that * we avoid holding cs_main for an extended period of time; the length of this @@ -666,7 +704,7 @@ class CChainState bool ActivateBestChain( CValidationState& state, const CChainParams& chainparams, - std::shared_ptr pblock) LOCKS_EXCLUDED(cs_main); + std::shared_ptr pblock = nullptr) LOCKS_EXCLUDED(cs_main); bool AcceptBlock(const std::shared_ptr& pblock, CValidationState& state, const CChainParams& chainparams, CBlockIndex** ppindex, bool fRequested, const FlatFilePos* dbp, bool* fNewBlock) EXCLUSIVE_LOCKS_REQUIRED(cs_main); @@ -678,13 +716,21 @@ class CChainState bool DisconnectTip(CValidationState& state, const CChainParams& chainparams, DisconnectedBlockTransactions* disconnectpool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, m_mempool.cs); // Manual block validity manipulation: + /** Mark a block as precious and reorganize. + * + * May not be called in a validationinterface callback. + */ bool PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main); + /** Mark a block as invalid. */ bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main); + /** Enforce a block marking all the other chains as conflicting. */ void EnforceBlock(CValidationState& state, const CChainParams& chainparams, const CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main); + /** Remove invalidity status from a block and its descendants. */ void ResetBlockFailureFlags(CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main); /** Replay blocks that aren't fully applied to the database. */ bool ReplayBlocks(const CChainParams& params); + /** Ensures we have a genesis block in the block tree, possibly writing one to disk. */ bool LoadGenesisBlock(const CChainParams& chainparams); bool AddGenesisBlock(const CChainParams& chainparams, const CBlock& block, CValidationState& state) EXCLUSIVE_LOCKS_REQUIRED(cs_main); @@ -738,24 +784,14 @@ class CChainState //! Mark a block as not having block data void EraseBlockData(CBlockIndex* index) EXCLUSIVE_LOCKS_REQUIRED(cs_main); - friend ChainstateManager; -}; + void CheckForkWarningConditions() EXCLUSIVE_LOCKS_REQUIRED(cs_main); + void InvalidChainFound(CBlockIndex* pindexNew) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + void ConflictingChainFound(CBlockIndex* pindexNew) EXCLUSIVE_LOCKS_REQUIRED(cs_main); -/** Mark a block as precious and reorganize. - * - * May not be called in a - * validationinterface callback. - */ -bool PreciousBlock(CValidationState& state, const CChainParams& params, CBlockIndex *pindex) LOCKS_EXCLUDED(cs_main); - -/** Mark a block as invalid. */ -bool InvalidateBlock(CValidationState& state, const CChainParams& chainparams, CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main); + bool LoadBlockIndexDB(const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main); -/** Enforce a block marking all the other chains as conflicting. */ -void EnforceBlock(CValidationState& state, const CChainParams& chainparams, const CBlockIndex* pindex) LOCKS_EXCLUDED(cs_main); - -/** Remove invalidity status from a block and its descendants. */ -void ResetBlockFailureFlags(CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main); + friend ChainstateManager; +}; /** * Provides an interface for creating and interacting with one or two @@ -810,7 +846,7 @@ class ChainstateManager //! This is especially important when, e.g., calling ActivateBestChain() //! on all chainstates because we are not able to hold ::cs_main going into //! that call. - std::unique_ptr m_ibd_chainstate; + std::unique_ptr m_ibd_chainstate GUARDED_BY(::cs_main); //! A chainstate initialized on the basis of a UTXO snapshot. If this is //! non-null, it is always our active chainstate. @@ -823,7 +859,7 @@ class ChainstateManager //! This is especially important when, e.g., calling ActivateBestChain() //! on all chainstates because we are not able to hold ::cs_main going into //! that call. - std::unique_ptr m_snapshot_chainstate; + std::unique_ptr m_snapshot_chainstate GUARDED_BY(::cs_main); //! Points to either the ibd or snapshot chainstate; indicates our //! most-work chain. @@ -836,7 +872,7 @@ class ChainstateManager //! This is especially important when, e.g., calling ActivateBestChain() //! on all chainstates because we are not able to hold ::cs_main going into //! that call. - CChainState* m_active_chainstate{nullptr}; + CChainState* m_active_chainstate GUARDED_BY(::cs_main) {nullptr}; //! If true, the assumed-valid chainstate has been fully validated //! by the background validation chainstate. @@ -948,9 +984,6 @@ class ChainstateManager */ bool ProcessNewBlockHeaders(const std::vector& block, CValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex = nullptr, CBlockHeader* first_invalid = nullptr) LOCKS_EXCLUDED(cs_main); - //! Mark one block file as pruned (modify associated database entries) - void PruneOneBlockFile(const int fileNumber) EXCLUSIVE_LOCKS_REQUIRED(cs_main); - //! Load the block tree and coins database from disk, initializing state if we're running with -reindex bool LoadBlockIndex(const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(cs_main); @@ -977,13 +1010,6 @@ CChain& ChainActive(); /** Global variable that points to the active block tree (protected by cs_main) */ extern std::unique_ptr pblocktree; -/** - * Return the spend height, which is one more than the inputs.GetBestBlock(). - * While checking, GetBestBlock() refers to the parent block. (protected by cs_main) - * This is also true for mempool checks. - */ -int GetSpendHeight(const CCoinsViewCache& inputs); - extern VersionBitsCache versionbitscache; /** @@ -1012,7 +1038,7 @@ CBlockFileInfo* GetBlockFileInfo(size_t n); bool DumpMempool(const CTxMemPool& pool); /** Load the mempool from disk. */ -bool LoadMempool(CTxMemPool& pool); +bool LoadMempool(CTxMemPool& pool, CChainState& active_chainstate); //! Check whether the block associated with this index entry is pruned or not. inline bool IsBlockPruned(const CBlockIndex* pblockindex) diff --git a/src/versionbits.h b/src/versionbits.h index cba2bae77703..cd34d2ecb5b4 100644 --- a/src/versionbits.h +++ b/src/versionbits.h @@ -79,8 +79,11 @@ struct VersionBitsCache void Clear(); }; +/** Get the BIP9 state for a given deployment at the current tip. */ ThresholdState VersionBitsState(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache); +/** Get the numerical statistics for the BIP9 state for a given deployment at the current tip. */ BIP9Stats VersionBitsStatistics(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache); +/** Get the block height at which the BIP9 deployment switched into the state for the block building on the current tip. */ int VersionBitsStateSinceHeight(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache); uint32_t VersionBitsMask(const Consensus::Params& params, Consensus::DeploymentPos pos); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 1c92cac730e7..613b0b18d295 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -2474,7 +2474,7 @@ static UniValue getwalletinfo(const JSONRPCRequest& request) {RPCResult::Type::STR_AMOUNT, "paytxfee", "the transaction fee configuration, set in " + CURRENCY_UNIT + "/kB"}, {RPCResult::Type::STR_HEX, "hdchainid", "the ID of the HD chain"}, {RPCResult::Type::STR, "hdaccountcount", "how many accounts of the HD chain are in this wallet"}, - {RPCResult::Type::ARR, "", "", + {RPCResult::Type::ARR, "hdaccounts", "", { {RPCResult::Type::OBJ, "", "", { diff --git a/src/wallet/test/wallet_tests.cpp b/src/wallet/test/wallet_tests.cpp index 6626347f0a1f..c1a94d383603 100644 --- a/src/wallet/test/wallet_tests.cpp +++ b/src/wallet/test/wallet_tests.cpp @@ -134,7 +134,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup) // Prune the older block file. { LOCK(cs_main); - Assert(m_node.chainman)->PruneOneBlockFile(oldTip->GetBlockPos().nFile); + Assert(m_node.chainman)->m_blockman.PruneOneBlockFile(oldTip->GetBlockPos().nFile); } UnlinkPrunedFiles({oldTip->GetBlockPos().nFile}); @@ -160,7 +160,7 @@ BOOST_FIXTURE_TEST_CASE(scan_for_wallet_transactions, TestChain100Setup) // Prune the remaining block file. { LOCK(cs_main); - Assert(m_node.chainman)->PruneOneBlockFile(newTip->GetBlockPos().nFile); + Assert(m_node.chainman)->m_blockman.PruneOneBlockFile(newTip->GetBlockPos().nFile); } UnlinkPrunedFiles({newTip->GetBlockPos().nFile}); @@ -199,7 +199,7 @@ BOOST_FIXTURE_TEST_CASE(importmulti_rescan, TestChain100Setup) // Prune the older block file. { LOCK(cs_main); - Assert(m_node.chainman)->PruneOneBlockFile(oldTip->GetBlockPos().nFile); + Assert(m_node.chainman)->m_blockman.PruneOneBlockFile(oldTip->GetBlockPos().nFile); } UnlinkPrunedFiles({oldTip->GetBlockPos().nFile}); diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index 32ca41e963bc..b32544b28a2b 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -55,6 +55,7 @@ const std::string ORDERPOSNEXT{"orderposnext"}; const std::string POOL{"pool"}; const std::string PURPOSE{"purpose"}; const std::string PRIVATESEND_SALT{"ps_salt"}; +const std::string SETTINGS{"settings"}; const std::string TX{"tx"}; const std::string VERSION{"version"}; const std::string WATCHMETA{"watchmeta"}; @@ -487,7 +488,8 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, strErr = "Found unsupported 'wkey' record, try loading with version 0.17"; return false; } else if (strType != DBKeys::BESTBLOCK && strType != DBKeys::BESTBLOCK_NOMERKLE && - strType != DBKeys::MINVERSION && strType != DBKeys::ACENTRY && strType != DBKeys::VERSION) { + strType != DBKeys::MINVERSION && strType != DBKeys::ACENTRY && + strType != DBKeys::VERSION && strType != DBKeys::SETTINGS) { wss.m_unknown_records++; } } catch (const std::exception& e) { diff --git a/src/wallet/walletdb.h b/src/wallet/walletdb.h index 2ec827c89ff8..878ed78df2d5 100644 --- a/src/wallet/walletdb.h +++ b/src/wallet/walletdb.h @@ -76,6 +76,7 @@ extern const std::string ORDERPOSNEXT; extern const std::string POOL; extern const std::string PURPOSE; extern const std::string PRIVATESEND_SALT; +extern const std::string SETTINGS; extern const std::string TX; extern const std::string VERSION; extern const std::string WATCHMETA; diff --git a/src/warnings.cpp b/src/warnings.cpp index f50a997b2c8d..db0fcf0f1b0d 100644 --- a/src/warnings.cpp +++ b/src/warnings.cpp @@ -12,7 +12,6 @@ static Mutex g_warnings_mutex; static std::string strMiscWarning GUARDED_BY(g_warnings_mutex); -static bool fLargeWorkForkFound GUARDED_BY(g_warnings_mutex) = false; static bool fLargeWorkInvalidChainFound GUARDED_BY(g_warnings_mutex) = false; void SetMiscWarning(const std::string& strWarning) @@ -21,18 +20,6 @@ void SetMiscWarning(const std::string& strWarning) strMiscWarning = strWarning; } -void SetfLargeWorkForkFound(bool flag) -{ - LOCK(g_warnings_mutex); - fLargeWorkForkFound = flag; -} - -bool GetfLargeWorkForkFound() -{ - LOCK(g_warnings_mutex); - return fLargeWorkForkFound; -} - void SetfLargeWorkInvalidChainFound(bool flag) { LOCK(g_warnings_mutex); @@ -59,10 +46,7 @@ std::string GetWarnings(bool verbose) warnings_verbose += (warnings_verbose.empty() ? "" : warning_separator) + strMiscWarning; } - if (fLargeWorkForkFound) { - warnings_concise = "Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues."; - warnings_verbose += (warnings_verbose.empty() ? "" : warning_separator) + _("Warning: The network does not appear to fully agree! Some miners appear to be experiencing issues.").translated; - } else if (fLargeWorkInvalidChainFound) { + if (fLargeWorkInvalidChainFound) { warnings_concise = "Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade."; warnings_verbose += (warnings_verbose.empty() ? "" : warning_separator) + _("Warning: We do not appear to fully agree with our peers! You may need to upgrade, or other nodes may need to upgrade.").translated; } diff --git a/src/warnings.h b/src/warnings.h index 4bf7af53685d..b7906f877be1 100644 --- a/src/warnings.h +++ b/src/warnings.h @@ -9,8 +9,6 @@ #include void SetMiscWarning(const std::string& strWarning); -void SetfLargeWorkForkFound(bool flag); -bool GetfLargeWorkForkFound(); void SetfLargeWorkInvalidChainFound(bool flag); /** Format a string that describes several potential problems detected by the core. * @param[in] verbose bool diff --git a/test/functional/feature_block.py b/test/functional/feature_block.py index 96f4d115dc9f..44dc80ccdb3c 100755 --- a/test/functional/feature_block.py +++ b/test/functional/feature_block.py @@ -506,6 +506,14 @@ def run_test(self): tx_last = tx_new b39_outputs += 1 + # The accounting in the loop above can be off, because it misses the + # compact size encoding of the number of transactions in the block. + # Make sure we didn't accidentally make too big a block. Note that the + # size of the block has non-determinism due to the ECDSA signature in + # the first transaction. + while (len(b39.serialize()) >= MAX_BLOCK_SIZE): + del b39.vtx[-1] + b39 = self.update_block(39, []) self.send_blocks([b39], True) self.save_spendable_output() diff --git a/test/functional/rpc_users.py b/test/functional/rpc_users.py index 22c8885b8dc5..76d66b2a2a79 100755 --- a/test/functional/rpc_users.py +++ b/test/functional/rpc_users.py @@ -20,6 +20,17 @@ import configparser import sys +def call_with_auth(node, user, password): + url = urllib.parse.urlparse(node.url) + headers = {"Authorization": "Basic " + str_to_b64str('{}:{}'.format(user, password))} + + conn = http.client.HTTPConnection(url.hostname, url.port) + conn.connect() + conn.request('POST', '/', '{"method": "getbestblockhash"}', headers) + resp = conn.getresponse() + conn.close() + return resp + class HTTPBasicsTest(BitcoinTestFramework): def set_test_params(self): @@ -29,15 +40,24 @@ def set_test_params(self): def setup_chain(self): super().setup_chain() #Append rpcauth to dash.conf before initialization + self.rtpassword = "cA773lm788buwYe4g4WT+05pKyNruVKjQ25x3n0DQcM=" rpcauth = "rpcauth=rt:93648e835a54c573682c2eb19f882535$7681e9c5b74bdd85e78166031d2058e1069b3ed7ed967c93fc63abba06f31144" - rpcauth2 = "rpcauth=rt2:f8607b1a88861fac29dfccf9b52ff9f$ff36a0c23c8c62b4846112e50fa888416e94c17bfd4c42f88fd8f55ec6a3137e" - rpcuser = "rpcuser=rpcuser💻" - rpcpassword = "rpcpassword=rpcpassword🔑" - self.user = ''.join(SystemRandom().choice(string.ascii_letters + string.digits) for _ in range(10)) + self.rpcuser = "rpcuser💻" + self.rpcpassword = "rpcpassword🔑" + config = configparser.ConfigParser() config.read_file(open(self.options.configfile)) gen_rpcauth = config['environment']['RPCAUTH'] + + # Generate RPCAUTH with specified password + self.rt2password = "8/F3uMDw4KSEbw96U3CA1C4X05dkHDN2BPFjTgZW4KI=" + p = subprocess.Popen([sys.executable, gen_rpcauth, 'rt2', self.rt2password], stdout=subprocess.PIPE, universal_newlines=True) + lines = p.stdout.read().splitlines() + rpcauth2 = lines[1] + + # Generate RPCAUTH without specifying password + self.user = ''.join(SystemRandom().choice(string.ascii_letters + string.digits) for _ in range(10)) p = subprocess.Popen([sys.executable, gen_rpcauth, self.user], stdout=subprocess.PIPE, universal_newlines=True) lines = p.stdout.read().splitlines() rpcauth3 = lines[1] @@ -48,155 +68,35 @@ def setup_chain(self): f.write(rpcauth2 + "\n") f.write(rpcauth3 + "\n") with open(os.path.join(get_datadir_path(self.options.tmpdir, 1), "dash.conf"), 'a', encoding='utf8') as f: - f.write(rpcuser + "\n") - f.write(rpcpassword + "\n") + f.write("rpcuser={}\n".format(self.rpcuser)) + f.write("rpcpassword={}\n".format(self.rpcpassword)) - def run_test(self): - self.log.info('Check correctness of the rpcauth config option') - url = urllib.parse.urlparse(self.nodes[0].url) - - #Old authpair - authpair = url.username + ':' + url.password - - #New authpair generated via share/rpcauth tool - password = "cA773lm788buwYe4g4WT+05pKyNruVKjQ25x3n0DQcM=" - - #Second authpair with different username - password2 = "8/F3uMDw4KSEbw96U3CA1C4X05dkHDN2BPFjTgZW4KI=" - authpairnew = "rt:"+password - - self.log.info('Correct...') - headers = {"Authorization": "Basic " + str_to_b64str(authpair)} - - conn = http.client.HTTPConnection(url.hostname, url.port) - conn.connect() - conn.request('POST', '/', '{"method": "getbestblockhash"}', headers) - resp = conn.getresponse() - assert_equal(resp.status, 200) - conn.close() - - #Use new authpair to confirm both work + def test_auth(self, node, user, password): self.log.info('Correct...') - headers = {"Authorization": "Basic " + str_to_b64str(authpairnew)} - - conn = http.client.HTTPConnection(url.hostname, url.port) - conn.connect() - conn.request('POST', '/', '{"method": "getbestblockhash"}', headers) - resp = conn.getresponse() - assert_equal(resp.status, 200) - conn.close() + assert_equal(200, call_with_auth(node, user, password).status) - #Wrong login name with rt's password self.log.info('Wrong...') - authpairnew = "rtwrong:"+password - headers = {"Authorization": "Basic " + str_to_b64str(authpairnew)} + assert_equal(401, call_with_auth(node, user, password+'wrong').status) - conn = http.client.HTTPConnection(url.hostname, url.port) - conn.connect() - conn.request('POST', '/', '{"method": "getbestblockhash"}', headers) - resp = conn.getresponse() - assert_equal(resp.status, 401) - conn.close() - - #Wrong password for rt self.log.info('Wrong...') - authpairnew = "rt:"+password+"wrong" - headers = {"Authorization": "Basic " + str_to_b64str(authpairnew)} - - conn = http.client.HTTPConnection(url.hostname, url.port) - conn.connect() - conn.request('POST', '/', '{"method": "getbestblockhash"}', headers) - resp = conn.getresponse() - assert_equal(resp.status, 401) - conn.close() + assert_equal(401, call_with_auth(node, user+'wrong', password).status) - #Correct for rt2 - self.log.info('Correct...') - authpairnew = "rt2:"+password2 - headers = {"Authorization": "Basic " + str_to_b64str(authpairnew)} - - conn = http.client.HTTPConnection(url.hostname, url.port) - conn.connect() - conn.request('POST', '/', '{"method": "getbestblockhash"}', headers) - resp = conn.getresponse() - assert_equal(resp.status, 200) - conn.close() - - #Wrong password for rt2 self.log.info('Wrong...') - authpairnew = "rt2:"+password2+"wrong" - headers = {"Authorization": "Basic " + str_to_b64str(authpairnew)} - - conn = http.client.HTTPConnection(url.hostname, url.port) - conn.connect() - conn.request('POST', '/', '{"method": "getbestblockhash"}', headers) - resp = conn.getresponse() - assert_equal(resp.status, 401) - conn.close() - - #Correct for randomly generated user - self.log.info('Correct...') - authpairnew = self.user+":"+self.password - headers = {"Authorization": "Basic " + str_to_b64str(authpairnew)} - - conn = http.client.HTTPConnection(url.hostname, url.port) - conn.connect() - conn.request('POST', '/', '{"method": "getbestblockhash"}', headers) - resp = conn.getresponse() - assert_equal(resp.status, 200) - conn.close() + assert_equal(401, call_with_auth(node, user+'wrong', password+'wrong').status) - #Wrong password for randomly generated user - self.log.info('Wrong...') - authpairnew = self.user+":"+self.password+"Wrong" - headers = {"Authorization": "Basic " + str_to_b64str(authpairnew)} + def run_test(self): + self.log.info('Check correctness of the rpcauth config option') + url = urllib.parse.urlparse(self.nodes[0].url) - conn = http.client.HTTPConnection(url.hostname, url.port) - conn.connect() - conn.request('POST', '/', '{"method": "getbestblockhash"}', headers) - resp = conn.getresponse() - assert_equal(resp.status, 401) - conn.close() + self.test_auth(self.nodes[0], url.username, url.password) + self.test_auth(self.nodes[0], 'rt', self.rtpassword) + self.test_auth(self.nodes[0], 'rt2', self.rt2password) + self.test_auth(self.nodes[0], self.user, self.password) self.log.info('Check correctness of the rpcuser/rpcpassword config options') url = urllib.parse.urlparse(self.nodes[1].url) - # rpcuser and rpcpassword authpair - self.log.info('Correct...') - rpcuserauthpair = "rpcuser💻:rpcpassword🔑" - - headers = {"Authorization": "Basic " + str_to_b64str(rpcuserauthpair)} - - conn = http.client.HTTPConnection(url.hostname, url.port) - conn.connect() - conn.request('POST', '/', '{"method": "getbestblockhash"}', headers) - resp = conn.getresponse() - assert_equal(resp.status, 200) - conn.close() - - #Wrong login name with rpcuser's password - rpcuserauthpair = "rpcuserwrong:rpcpassword" - headers = {"Authorization": "Basic " + str_to_b64str(rpcuserauthpair)} - - conn = http.client.HTTPConnection(url.hostname, url.port) - conn.connect() - conn.request('POST', '/', '{"method": "getbestblockhash"}', headers) - resp = conn.getresponse() - assert_equal(resp.status, 401) - conn.close() - - #Wrong password for rpcuser - self.log.info('Wrong...') - rpcuserauthpair = "rpcuser:rpcpasswordwrong" - headers = {"Authorization": "Basic " + str_to_b64str(rpcuserauthpair)} - - conn = http.client.HTTPConnection(url.hostname, url.port) - conn.connect() - conn.request('POST', '/', '{"method": "getbestblockhash"}', headers) - resp = conn.getresponse() - assert_equal(resp.status, 401) - conn.close() - + self.test_auth(self.nodes[1], self.rpcuser, self.rpcpassword) init_error = 'Error: Unable to start HTTP server. See debug log for details.' diff --git a/test/functional/test_framework/test_framework.py b/test/functional/test_framework/test_framework.py index 66d5745ab40c..799142a619fa 100755 --- a/test/functional/test_framework/test_framework.py +++ b/test/functional/test_framework/test_framework.py @@ -238,8 +238,18 @@ def setup(self): config = self.config - self.options.bitcoind = os.getenv("BITCOIND", default=config["environment"]["BUILDDIR"] + '/src/dashd' + config["environment"]["EXEEXT"]) - self.options.bitcoincli = os.getenv("BITCOINCLI", default=config["environment"]["BUILDDIR"] + '/src/dash-cli' + config["environment"]["EXEEXT"]) + fname_bitcoind = os.path.join( + config["environment"]["BUILDDIR"], + "src", + "dashd" + config["environment"]["EXEEXT"] + ) + fname_bitcoincli = os.path.join( + config["environment"]["BUILDDIR"], + "src", + "dash-cli" + config["environment"]["EXEEXT"] + ) + self.options.bitcoind = os.getenv("BITCOIND", default=fname_bitcoind) + self.options.bitcoincli = os.getenv("BITCOINCLI", default=fname_bitcoincli) self.extra_args_from_options = self.options.dashd_extra_args @@ -1000,8 +1010,8 @@ def set_dash_test_params(self, num_nodes, masterodes_count, extra_args=None, fas # This is nRequestTimeout in dash-q-recovery thread self.quorum_data_thread_request_timeout_seconds = 10 - # This is EXPIRATION_TIMEOUT in CQuorumDataRequest - self.quorum_data_request_expiration_timeout = 300 + # This is EXPIRATION_TIMEOUT + EXPIRATION_BIAS in CQuorumDataRequest + self.quorum_data_request_expiration_timeout = 360 def set_dash_dip8_activation(self, activate_after_block): self.dip8_activation_height = activate_after_block diff --git a/test/lint/lint-locale-dependence.sh b/test/lint/lint-locale-dependence.sh index 7020c43917d7..3f1ef8daf1b5 100755 --- a/test/lint/lint-locale-dependence.sh +++ b/test/lint/lint-locale-dependence.sh @@ -8,6 +8,39 @@ export LC_ALL=C # TODO: Reduce KNOWN_VIOLATIONS by replacing uses of locale dependent stoul/strtol with locale # independent ToIntegral(...). # TODO: Reduce KNOWN_VIOLATIONS by replacing uses of locale dependent snprintf with strprintf. + +# Be aware that bitcoind and bitcoin-qt differ in terms of localization: Qt +# opts in to POSIX localization by running setlocale(LC_ALL, "") on startup, +# whereas no such call is made in bitcoind. +# +# Qt runs setlocale(LC_ALL, "") on initialization. This installs the locale +# specified by the user's LC_ALL (or LC_*) environment variable as the new +# C locale. +# +# In contrast, bitcoind does not opt in to localization -- no call to +# setlocale(LC_ALL, "") is made and the environment variables LC_* are +# thus ignored. +# +# This results in situations where bitcoind is guaranteed to be running +# with the classic locale ("C") whereas the locale of bitcoin-qt will vary +# depending on the user's environment variables. +# +# An example: Assuming the environment variable LC_ALL=de_DE then the +# call std::to_string(1.23) will return "1.230000" in bitcoind but +# "1,230000" in bitcoin-qt. +# +# From the Qt documentation: +# "On Unix/Linux Qt is configured to use the system locale settings by default. +# This can cause a conflict when using POSIX functions, for instance, when +# converting between data types such as floats and strings, since the notation +# may differ between locales. To get around this problem, call the POSIX function +# setlocale(LC_NUMERIC,"C") right after initializing QApplication, QGuiApplication +# or QCoreApplication to reset the locale that is used for number formatting to +# "C"-locale." +# +# See https://doc.qt.io/qt-5/qcoreapplication.html#locale-settings and +# https://stackoverflow.com/a/34878283 for more details. + KNOWN_VIOLATIONS=( "src/bitcoin-tx.cpp.*stoul" "src/dbwrapper.cpp.*stoul"