From 240cad09baefcf363cce36a4b2795122adfce27f Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Fri, 12 Nov 2021 07:14:21 +1000 Subject: [PATCH] rpc: getdeploymentinfo: include signalling info --- src/rpc/blockchain.cpp | 11 ++++++++++- src/test/fuzz/versionbits.cpp | 16 ++++++++++++++-- src/versionbits.cpp | 24 ++++++++++++++++-------- src/versionbits.h | 12 ++++++++---- test/functional/rpc_blockchain.py | 1 + 5 files changed, 49 insertions(+), 15 deletions(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 32cb65679e6..c1a73944de5 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1448,7 +1448,8 @@ static void SoftForkDescPushBack(const CBlockIndex* active_chain_tip, UniValue& // BIP9 signalling status, if applicable if (has_signal) { UniValue statsUV(UniValue::VOBJ); - BIP9Stats statsStruct = g_versionbitscache.Statistics(active_chain_tip, consensusParams, id); + std::vector signals; + BIP9Stats statsStruct = g_versionbitscache.Statistics(active_chain_tip, consensusParams, id, &signals); statsUV.pushKV("period", statsStruct.period); statsUV.pushKV("elapsed", statsStruct.elapsed); statsUV.pushKV("count", statsStruct.count); @@ -1457,6 +1458,13 @@ static void SoftForkDescPushBack(const CBlockIndex* active_chain_tip, UniValue& statsUV.pushKV("possible", statsStruct.possible); } bip9.pushKV("statistics", statsUV); + + std::string sig; + sig.reserve(signals.size()); + for (const bool s : signals) { + sig.push_back(s ? '#' : '-'); + } + bip9.pushKV("signalling", sig); } UniValue rv(UniValue::VOBJ); @@ -1585,6 +1593,7 @@ const std::vector RPCHelpForDeployment{ {RPCResult::Type::NUM, "count", "the number of blocks with the version bit set in the current period"}, {RPCResult::Type::BOOL, "possible", /*optional=*/true, "returns false if there are not enough blocks left in this period to pass activation threshold (only for \"started\" status)"}, }}, + {RPCResult::Type::STR, "signalling", "indicates blocks that signalled with a # and blocks that did not with a -"}, }}, }; diff --git a/src/test/fuzz/versionbits.cpp b/src/test/fuzz/versionbits.cpp index 890267db181..95eb71099db 100644 --- a/src/test/fuzz/versionbits.cpp +++ b/src/test/fuzz/versionbits.cpp @@ -51,7 +51,7 @@ public: ThresholdState GetStateFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateFor(pindexPrev, dummy_params, m_cache); } int GetStateSinceHeightFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateSinceHeightFor(pindexPrev, dummy_params, m_cache); } - BIP9Stats GetStateStatisticsFor(const CBlockIndex* pindex) const { return AbstractThresholdConditionChecker::GetStateStatisticsFor(pindex, dummy_params); } + BIP9Stats GetStateStatisticsFor(const CBlockIndex* pindex, std::vector* signals=nullptr) const { return AbstractThresholdConditionChecker::GetStateStatisticsFor(pindex, dummy_params, signals); } bool Condition(int32_t version) const { @@ -227,6 +227,7 @@ FUZZ_TARGET_INIT(versionbits, initialize) last_stats.threshold = threshold; last_stats.count = last_stats.elapsed = 0; last_stats.possible = (period >= threshold); + std::vector last_signals{}; int prev_next_height = (prev == nullptr ? 0 : prev->nHeight + 1); assert(exp_since <= prev_next_height); @@ -248,13 +249,24 @@ FUZZ_TARGET_INIT(versionbits, initialize) assert(since == exp_since); // check that after mining this block stats change as expected - const BIP9Stats stats = checker.GetStateStatisticsFor(current_block); + std::vector signals; + const BIP9Stats stats = checker.GetStateStatisticsFor(current_block, &signals); + const BIP9Stats stats_no_signals = checker.GetStateStatisticsFor(current_block); + assert(stats.period == stats_no_signals.period && stats.threshold == stats_no_signals.threshold + && stats.elapsed == stats_no_signals.elapsed && stats.count == stats_no_signals.count + && stats.possible == stats_no_signals.possible); + assert(stats.period == period); assert(stats.threshold == threshold); assert(stats.elapsed == b); assert(stats.count == last_stats.count + (signal ? 1 : 0)); assert(stats.possible == (stats.count + period >= stats.elapsed + threshold)); last_stats = stats; + + assert(signals.size() == last_signals.size() + 1); + assert(signals.back() == signal); + last_signals.push_back(signal); + assert(signals == last_signals); } if (exp_state == ThresholdState::STARTED) { diff --git a/src/versionbits.cpp b/src/versionbits.cpp index f8945b78b3c..90e4b0ec365 100644 --- a/src/versionbits.cpp +++ b/src/versionbits.cpp @@ -98,7 +98,7 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex* return state; } -BIP9Stats AbstractThresholdConditionChecker::GetStateStatisticsFor(const CBlockIndex* pindex, const Consensus::Params& params) const +BIP9Stats AbstractThresholdConditionChecker::GetStateStatisticsFor(const CBlockIndex* pindex, const Consensus::Params& params, std::vector* signalling_blocks) const { BIP9Stats stats = {}; @@ -108,18 +108,26 @@ BIP9Stats AbstractThresholdConditionChecker::GetStateStatisticsFor(const CBlockI if (pindex == nullptr) return stats; // Find beginning of period - int start_height = pindex->nHeight - (pindex->nHeight % stats.period); + int blocks_in_period = 1 + (pindex->nHeight % stats.period); + + // Reset signalling_blocks + if (signalling_blocks) { + signalling_blocks->assign(blocks_in_period, false); + } // Count from current block to beginning of period int elapsed = 0; int count = 0; const CBlockIndex* currentIndex = pindex; - for(;;) { + do { ++elapsed; - if (Condition(currentIndex, params)) ++count; - if (currentIndex->nHeight <= start_height) break; + --blocks_in_period; + if (Condition(currentIndex, params)) { + ++count; + if (signalling_blocks) signalling_blocks->at(blocks_in_period) = true; + } currentIndex = currentIndex->pprev; - } + } while(blocks_in_period > 0); stats.elapsed = elapsed; stats.count = count; @@ -197,9 +205,9 @@ ThresholdState VersionBitsCache::State(const CBlockIndex* pindexPrev, const Cons return VersionBitsConditionChecker(pos).GetStateFor(pindexPrev, params, m_caches[pos]); } -BIP9Stats VersionBitsCache::Statistics(const CBlockIndex* pindex, const Consensus::Params& params, Consensus::DeploymentPos pos) +BIP9Stats VersionBitsCache::Statistics(const CBlockIndex* pindex, const Consensus::Params& params, Consensus::DeploymentPos pos, std::vector* signalling_blocks) { - return VersionBitsConditionChecker(pos).GetStateStatisticsFor(pindex, params); + return VersionBitsConditionChecker(pos).GetStateStatisticsFor(pindex, params, signalling_blocks); } int VersionBitsCache::StateSinceHeight(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos) diff --git a/src/versionbits.h b/src/versionbits.h index 34fba47b5f7..81e6bcf0d5f 100644 --- a/src/versionbits.h +++ b/src/versionbits.h @@ -64,8 +64,10 @@ protected: virtual int Threshold(const Consensus::Params& params) const =0; public: - /** Returns the numerical statistics of an in-progress BIP9 softfork in the period including pindex */ - BIP9Stats GetStateStatisticsFor(const CBlockIndex* pindex, const Consensus::Params& params) const; + /** Returns the numerical statistics of an in-progress BIP9 softfork in the period including pindex + * If provided, signalling_blocks is set to true/false based on whether each block in the period signalled + */ + BIP9Stats GetStateStatisticsFor(const CBlockIndex* pindex, const Consensus::Params& params, std::vector* signalling_blocks = nullptr) const; /** Returns the state for pindex A based on parent pindexPrev B. Applies any state transition if conditions are present. * Caches state from first block of period. */ ThresholdState GetStateFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const; @@ -82,8 +84,10 @@ private: ThresholdConditionCache m_caches[Consensus::MAX_VERSION_BITS_DEPLOYMENTS] GUARDED_BY(m_mutex); public: - /** Get the numerical statistics for a given deployment for the signalling period that includes pindex. */ - static BIP9Stats Statistics(const CBlockIndex* pindex, const Consensus::Params& params, Consensus::DeploymentPos pos); + /** Get the numerical statistics for a given deployment for the signalling period that includes pindex. + * If provided, signalling_blocks is set to true/false based on whether each block in the period signalled + */ + static BIP9Stats Statistics(const CBlockIndex* pindex, const Consensus::Params& params, Consensus::DeploymentPos pos, std::vector* signalling_blocks = nullptr); static uint32_t Mask(const Consensus::Params& params, Consensus::DeploymentPos pos); diff --git a/test/functional/rpc_blockchain.py b/test/functional/rpc_blockchain.py index 0e560abc491..12814c42bcc 100755 --- a/test/functional/rpc_blockchain.py +++ b/test/functional/rpc_blockchain.py @@ -202,6 +202,7 @@ class BlockchainTest(BitcoinTestFramework): 'count': height - 143, 'possible': True, }, + 'signalling': '#'*(height-143), }, 'active': False },