diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 89e01bbf7fe..69204b3184c 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1215,58 +1215,41 @@ static void SoftForkDescPushBack(const CBlockIndex* blockindex, UniValue& softfo static void SoftForkDescPushBack(const CBlockIndex* blockindex, UniValue& softforks, const ChainstateManager& chainman, Consensus::DeploymentPos id) { // For BIP9 deployments. - if (!DeploymentEnabled(chainman, id)) return; if (blockindex == nullptr) return; - auto get_state_name = [](const ThresholdState state) -> std::string { - switch (state) { - case ThresholdState::DEFINED: return "defined"; - case ThresholdState::STARTED: return "started"; - case ThresholdState::LOCKED_IN: return "locked_in"; - case ThresholdState::ACTIVE: return "active"; - case ThresholdState::FAILED: return "failed"; - } - return "invalid"; - }; - UniValue bip9(UniValue::VOBJ); - - const ThresholdState next_state = chainman.m_versionbitscache.State(blockindex, chainman.GetConsensus(), id); - const ThresholdState current_state = chainman.m_versionbitscache.State(blockindex->pprev, chainman.GetConsensus(), id); - - const bool has_signal = (ThresholdState::STARTED == current_state || ThresholdState::LOCKED_IN == current_state); + BIP9Info info{chainman.m_versionbitscache.Info(*blockindex, chainman.GetConsensus(), id)}; + const auto& depparams{chainman.GetConsensus().vDeployments[id]}; // BIP9 parameters - if (has_signal) { - bip9.pushKV("bit", chainman.GetConsensus().vDeployments[id].bit); + if (info.stats.has_value()) { + bip9.pushKV("bit", depparams.bit); } - bip9.pushKV("start_time", chainman.GetConsensus().vDeployments[id].nStartTime); - bip9.pushKV("timeout", chainman.GetConsensus().vDeployments[id].nTimeout); - bip9.pushKV("min_activation_height", chainman.GetConsensus().vDeployments[id].min_activation_height); + bip9.pushKV("start_time", depparams.nStartTime); + bip9.pushKV("timeout", depparams.nTimeout); + bip9.pushKV("min_activation_height", depparams.min_activation_height); // BIP9 status - bip9.pushKV("status", get_state_name(current_state)); - bip9.pushKV("since", chainman.m_versionbitscache.StateSinceHeight(blockindex->pprev, chainman.GetConsensus(), id)); - bip9.pushKV("status_next", get_state_name(next_state)); + bip9.pushKV("status", info.current_state); + bip9.pushKV("since", info.since); + bip9.pushKV("status_next", info.next_state); // BIP9 signalling status, if applicable - if (has_signal) { + if (info.stats.has_value()) { UniValue statsUV(UniValue::VOBJ); - std::vector signals; - BIP9Stats statsStruct = chainman.m_versionbitscache.Statistics(blockindex, chainman.GetConsensus(), id, &signals); - statsUV.pushKV("period", statsStruct.period); - statsUV.pushKV("elapsed", statsStruct.elapsed); - statsUV.pushKV("count", statsStruct.count); - if (ThresholdState::LOCKED_IN != current_state) { - statsUV.pushKV("threshold", statsStruct.threshold); - statsUV.pushKV("possible", statsStruct.possible); + statsUV.pushKV("period", info.stats->period); + statsUV.pushKV("elapsed", info.stats->elapsed); + statsUV.pushKV("count", info.stats->count); + if (info.stats->threshold > 0 || info.stats->possible) { + statsUV.pushKV("threshold", info.stats->threshold); + statsUV.pushKV("possible", info.stats->possible); } bip9.pushKV("statistics", std::move(statsUV)); std::string sig; - sig.reserve(signals.size()); - for (const bool s : signals) { + sig.reserve(info.signalling_blocks.size()); + for (const bool s : info.signalling_blocks) { sig.push_back(s ? '#' : '-'); } bip9.pushKV("signalling", sig); @@ -1274,12 +1257,13 @@ static void SoftForkDescPushBack(const CBlockIndex* blockindex, UniValue& softfo UniValue rv(UniValue::VOBJ); rv.pushKV("type", "bip9"); - if (ThresholdState::ACTIVE == next_state) { - rv.pushKV("height", chainman.m_versionbitscache.StateSinceHeight(blockindex, chainman.GetConsensus(), id)); + bool is_active = false; + if (info.active_since.has_value()) { + rv.pushKV("height", *info.active_since); + is_active = (*info.active_since <= blockindex->nHeight + 1); } - rv.pushKV("active", ThresholdState::ACTIVE == next_state); - rv.pushKV("bip9", std::move(bip9)); - + rv.pushKV("active", is_active); + rv.pushKV("bip9", bip9); softforks.pushKV(DeploymentName(id), std::move(rv)); } diff --git a/src/versionbits.cpp b/src/versionbits.cpp index 6869189879c..f54f8f6a787 100644 --- a/src/versionbits.cpp +++ b/src/versionbits.cpp @@ -9,6 +9,18 @@ using enum ThresholdState; +static std::string StateName(ThresholdState state) +{ + switch (state) { + case DEFINED: return "defined"; + case STARTED: return "started"; + case LOCKED_IN: return "locked_in"; + case ACTIVE: return "active"; + case FAILED: return "failed"; + } + return "invalid"; +} + ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex* pindexPrev, ThresholdConditionCache& cache) const { int nPeriod = Period(); @@ -205,6 +217,35 @@ public: } // namespace +BIP9Info VersionBitsCache::Info(const CBlockIndex& block_index, const Consensus::Params& params, Consensus::DeploymentPos id) +{ + BIP9Info result; + + const auto current_state = State(block_index.pprev, params, id); + result.current_state = StateName(current_state); + result.since = StateSinceHeight(block_index.pprev, params, id); + + const auto next_state = State(&block_index, params, id); + result.next_state = StateName(next_state); + + const bool has_signal = (STARTED == current_state || LOCKED_IN == current_state); + if (has_signal) { + result.stats.emplace(Statistics(&block_index, params, id, &result.signalling_blocks)); + if (LOCKED_IN == current_state) { + result.stats->threshold = 0; + result.stats->possible = false; + } + } + + if (current_state == ACTIVE) { + result.active_since = result.since; + } else if (next_state == ACTIVE) { + result.active_since = block_index.nHeight + 1; + } + + return result; +} + ThresholdState VersionBitsCache::State(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos) { LOCK(m_mutex); diff --git a/src/versionbits.h b/src/versionbits.h index d61bd5f226c..b435d313821 100644 --- a/src/versionbits.h +++ b/src/versionbits.h @@ -10,6 +10,8 @@ #include #include +#include +#include class CChainParams; @@ -43,15 +45,25 @@ typedef std::map ThresholdConditionCache; /** Display status of an in-progress BIP9 softfork */ struct BIP9Stats { /** Length of blocks of the BIP9 signalling period */ - uint32_t period; + uint32_t period{0}; /** Number of blocks with the version bit set required to activate the softfork */ - uint32_t threshold; + uint32_t threshold{0}; /** Number of blocks elapsed since the beginning of the current period */ - uint32_t elapsed; + uint32_t elapsed{0}; /** Number of blocks with the version bit set since the beginning of the current period */ - uint32_t count; + uint32_t count{0}; /** False if there are not enough blocks left in this period to pass activation threshold */ - bool possible; + bool possible{false}; +}; + +/** Detailed status of an enabled BIP9 deployment */ +struct BIP9Info { + int since{0}; + std::string current_state{}; + std::string next_state{}; + std::optional stats; + std::vector signalling_blocks; + std::optional active_since; }; /** @@ -95,6 +107,8 @@ public: static uint32_t Mask(const Consensus::Params& params, Consensus::DeploymentPos pos); + BIP9Info Info(const CBlockIndex& block_index, const Consensus::Params& params, Consensus::DeploymentPos id) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex); + /** Get the BIP9 state for a given deployment for the block after pindexPrev. */ ThresholdState State(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);