From 3bd32c20550e69688a4ff02409fb34b9a637b9c4 Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Mon, 20 Jan 2025 12:51:46 +1000 Subject: [PATCH] versionbits: Move WarningBits logic from validation to versionbits --- src/validation.cpp | 56 +++++------------------------------- src/validation.h | 2 -- src/versionbits.cpp | 69 ++++++++++++++++++++++++++++++++++++++++++--- src/versionbits.h | 8 ++++++ 4 files changed, 80 insertions(+), 55 deletions(-) diff --git a/src/validation.cpp b/src/validation.cpp index 95bffd15a82..ca7d252b0e2 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -2364,44 +2364,6 @@ DisconnectResult Chainstate::DisconnectBlock(const CBlock& block, const CBlockIn return fClean ? DISCONNECT_OK : DISCONNECT_UNCLEAN; } -/** - * Threshold condition checker that triggers when unknown versionbits are seen on the network. - */ -class WarningBitsConditionChecker : public AbstractThresholdConditionChecker -{ -private: - const ChainstateManager& m_chainman; - int m_bit; - -public: - explicit WarningBitsConditionChecker(const ChainstateManager& chainman, int bit) : m_chainman{chainman}, m_bit(bit) {} - - int64_t BeginTime() const override { return 0; } - int64_t EndTime() const override { return std::numeric_limits::max(); } - int Period() const override { - if (m_chainman.GetParams().IsTestChain()) { - return m_chainman.GetConsensus().DifficultyAdjustmentInterval(); - } else { - return 2016; - } - } - int Threshold() const override { - if (m_chainman.GetParams().IsTestChain()) { - return m_chainman.GetConsensus().DifficultyAdjustmentInterval() * 3 / 4; // 75% for test nets per BIP9 suggestion - } else { - return 1815; // 90% threshold used in BIP 341 - } - } - - bool Condition(const CBlockIndex* pindex) const override - { - return pindex->nHeight >= m_chainman.GetConsensus().MinBIP9WarningHeight && - ((pindex->nVersion & VERSIONBITS_TOP_MASK) == VERSIONBITS_TOP_BITS) && - ((pindex->nVersion >> m_bit) & 1) != 0 && - ((m_chainman.m_versionbitscache.ComputeBlockVersion(pindex->pprev, m_chainman.GetConsensus()) >> m_bit) & 1) == 0; - } -}; - static unsigned int GetBlockScriptFlags(const CBlockIndex& block_index, const ChainstateManager& chainman) { const Consensus::Params& consensusparams = chainman.GetConsensus(); @@ -3038,17 +3000,13 @@ void Chainstate::UpdateTip(const CBlockIndex* pindexNew) std::vector warning_messages; if (!m_chainman.IsInitialBlockDownload()) { - const CBlockIndex* pindex = pindexNew; - for (int bit = 0; bit < VERSIONBITS_NUM_BITS; bit++) { - WarningBitsConditionChecker checker(m_chainman, bit); - ThresholdState state = checker.GetStateFor(pindex, m_chainman.m_warningcache.at(bit)); - if (state == ThresholdState::ACTIVE || state == ThresholdState::LOCKED_IN) { - const bilingual_str warning = strprintf(_("Unknown new rules activated (versionbit %i)"), bit); - if (state == ThresholdState::ACTIVE) { - m_chainman.GetNotifications().warningSet(kernel::Warning::UNKNOWN_NEW_RULES_ACTIVATED, warning); - } else { - warning_messages.push_back(warning); - } + auto bits = m_chainman.m_versionbitscache.CheckUnknownActivations(pindexNew, m_chainman.GetParams()); + for (auto [bit, active] : bits) { + const bilingual_str warning = strprintf(_("Unknown new rules activated (versionbit %i)"), bit); + if (active) { + m_chainman.GetNotifications().warningSet(kernel::Warning::UNKNOWN_NEW_RULES_ACTIVATED, warning); + } else { + warning_messages.push_back(warning); } } } diff --git a/src/validation.h b/src/validation.h index e2ff5925c58..68b23caa8dd 100644 --- a/src/validation.h +++ b/src/validation.h @@ -935,8 +935,6 @@ private: /** Most recent headers presync progress update, for rate-limiting. */ std::chrono::time_point m_last_presync_update GUARDED_BY(::cs_main) {}; - std::array m_warningcache GUARDED_BY(::cs_main); - //! Return true if a chainstate is considered usable. //! //! This is false when a background validation chainstate has completed its diff --git a/src/versionbits.cpp b/src/versionbits.cpp index e986427bd65..6869189879c 100644 --- a/src/versionbits.cpp +++ b/src/versionbits.cpp @@ -3,9 +3,12 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include +#include #include #include +using enum ThresholdState; + ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex* pindexPrev, ThresholdConditionCache& cache) const { int nPeriod = Period(); @@ -224,22 +227,28 @@ uint32_t VersionBitsCache::Mask(const Consensus::Params& params, Consensus::Depl return VersionBitsConditionChecker(params, pos).Mask(); } -int32_t VersionBitsCache::ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Params& params) +static int32_t ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Params& params, std::array& caches) { - LOCK(m_mutex); int32_t nVersion = VERSIONBITS_TOP_BITS; for (int i = 0; i < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; i++) { Consensus::DeploymentPos pos = static_cast(i); - ThresholdState state = VersionBitsConditionChecker(params, pos).GetStateFor(pindexPrev, m_caches[pos]); + VersionBitsConditionChecker checker(params, pos); + ThresholdState state = checker.GetStateFor(pindexPrev, caches[pos]); if (state == ThresholdState::LOCKED_IN || state == ThresholdState::STARTED) { - nVersion |= Mask(params, pos); + nVersion |= checker.Mask(); } } return nVersion; } +int32_t VersionBitsCache::ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Params& params) +{ + LOCK(m_mutex); + return ::ComputeBlockVersion(pindexPrev, params, m_caches); +} + void VersionBitsCache::Clear() { LOCK(m_mutex); @@ -247,3 +256,55 @@ void VersionBitsCache::Clear() m_caches[d].clear(); } } + +namespace { +/** + * Threshold condition checker that triggers when unknown versionbits are seen on the network. + */ +class WarningBitsConditionChecker : public AbstractThresholdConditionChecker +{ +private: + const Consensus::Params& m_params; + std::array& m_caches; + int m_bit; + int period{2016}; + int threshold{1815}; // 90% threshold used in BIP 341 + +public: + explicit WarningBitsConditionChecker(const CChainParams& chainparams, std::array& caches, int bit) + : m_params{chainparams.GetConsensus()}, m_caches{caches}, m_bit(bit) + { + if (chainparams.IsTestChain()) { + period = chainparams.GetConsensus().DifficultyAdjustmentInterval(); + threshold = period * 3 / 4; // 75% for test nets per BIP9 suggestion + } + } + + int64_t BeginTime() const override { return 0; } + int64_t EndTime() const override { return std::numeric_limits::max(); } + int Period() const override { return period; } + int Threshold() const override { return threshold; } + + bool Condition(const CBlockIndex* pindex) const override + { + return pindex->nHeight >= m_params.MinBIP9WarningHeight && + ((pindex->nVersion & VERSIONBITS_TOP_MASK) == VERSIONBITS_TOP_BITS) && + ((pindex->nVersion >> m_bit) & 1) != 0 && + ((::ComputeBlockVersion(pindex->pprev, m_params, m_caches) >> m_bit) & 1) == 0; + } +}; +} // anonymous namespace + +std::vector> VersionBitsCache::CheckUnknownActivations(const CBlockIndex* pindex, const CChainParams& chainparams) +{ + LOCK(m_mutex); + std::vector> result; + for (int bit = 0; bit < VERSIONBITS_NUM_BITS; ++bit) { + WarningBitsConditionChecker checker(chainparams, m_caches, bit); + ThresholdState state = checker.GetStateFor(pindex, m_warning_caches.at(bit)); + if (state == ACTIVE || state == LOCKED_IN) { + result.emplace_back(bit, state == ACTIVE); + } + } + return result; +} diff --git a/src/versionbits.h b/src/versionbits.h index db76eadfa01..d61bd5f226c 100644 --- a/src/versionbits.h +++ b/src/versionbits.h @@ -11,6 +11,8 @@ #include #include +class CChainParams; + /** What block version to use for new blocks (pre versionbits) */ static const int32_t VERSIONBITS_LAST_OLD_BLOCK_VERSION = 4; /** What bits to set in version for versionbits blocks */ @@ -82,6 +84,7 @@ class VersionBitsCache { private: Mutex m_mutex; + std::array m_warning_caches GUARDED_BY(m_mutex); std::array m_caches GUARDED_BY(m_mutex); public: @@ -102,6 +105,11 @@ public: */ int32_t ComputeBlockVersion(const CBlockIndex* pindexPrev, const Consensus::Params& params) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex); + /** Check for unknown activations + * Returns a vector containing the bit number used for signalling and a bool + * indicating the deployment is likely to be ACTIVE, rather than merely LOCKED_IN. */ + std::vector> CheckUnknownActivations(const CBlockIndex* pindex, const CChainParams& chainparams) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex); + void Clear() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex); };