versionbits: Move getdeploymentinfo logic to versionbits

Rather than having the RPC code have knowledge about how BIP9 is
implemented, create a reporting function in the versionbits code, and
limit the RPC code to coverting the result of that into Univalue/JSON.
This commit is contained in:
Anthony Towns 2023-12-08 14:20:19 +10:00
parent 3bd32c2055
commit b1e967c3ec
3 changed files with 85 additions and 46 deletions

View file

@ -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) static void SoftForkDescPushBack(const CBlockIndex* blockindex, UniValue& softforks, const ChainstateManager& chainman, Consensus::DeploymentPos id)
{ {
// For BIP9 deployments. // For BIP9 deployments.
if (!DeploymentEnabled(chainman, id)) return; if (!DeploymentEnabled(chainman, id)) return;
if (blockindex == nullptr) 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); UniValue bip9(UniValue::VOBJ);
BIP9Info info{chainman.m_versionbitscache.Info(*blockindex, chainman.GetConsensus(), id)};
const ThresholdState next_state = chainman.m_versionbitscache.State(blockindex, chainman.GetConsensus(), id); const auto& depparams{chainman.GetConsensus().vDeployments[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);
// BIP9 parameters // BIP9 parameters
if (has_signal) { if (info.stats.has_value()) {
bip9.pushKV("bit", chainman.GetConsensus().vDeployments[id].bit); bip9.pushKV("bit", depparams.bit);
} }
bip9.pushKV("start_time", chainman.GetConsensus().vDeployments[id].nStartTime); bip9.pushKV("start_time", depparams.nStartTime);
bip9.pushKV("timeout", chainman.GetConsensus().vDeployments[id].nTimeout); bip9.pushKV("timeout", depparams.nTimeout);
bip9.pushKV("min_activation_height", chainman.GetConsensus().vDeployments[id].min_activation_height); bip9.pushKV("min_activation_height", depparams.min_activation_height);
// BIP9 status // BIP9 status
bip9.pushKV("status", get_state_name(current_state)); bip9.pushKV("status", info.current_state);
bip9.pushKV("since", chainman.m_versionbitscache.StateSinceHeight(blockindex->pprev, chainman.GetConsensus(), id)); bip9.pushKV("since", info.since);
bip9.pushKV("status_next", get_state_name(next_state)); bip9.pushKV("status_next", info.next_state);
// BIP9 signalling status, if applicable // BIP9 signalling status, if applicable
if (has_signal) { if (info.stats.has_value()) {
UniValue statsUV(UniValue::VOBJ); UniValue statsUV(UniValue::VOBJ);
std::vector<bool> signals; statsUV.pushKV("period", info.stats->period);
BIP9Stats statsStruct = chainman.m_versionbitscache.Statistics(blockindex, chainman.GetConsensus(), id, &signals); statsUV.pushKV("elapsed", info.stats->elapsed);
statsUV.pushKV("period", statsStruct.period); statsUV.pushKV("count", info.stats->count);
statsUV.pushKV("elapsed", statsStruct.elapsed); if (info.stats->threshold > 0 || info.stats->possible) {
statsUV.pushKV("count", statsStruct.count); statsUV.pushKV("threshold", info.stats->threshold);
if (ThresholdState::LOCKED_IN != current_state) { statsUV.pushKV("possible", info.stats->possible);
statsUV.pushKV("threshold", statsStruct.threshold);
statsUV.pushKV("possible", statsStruct.possible);
} }
bip9.pushKV("statistics", std::move(statsUV)); bip9.pushKV("statistics", std::move(statsUV));
std::string sig; std::string sig;
sig.reserve(signals.size()); sig.reserve(info.signalling_blocks.size());
for (const bool s : signals) { for (const bool s : info.signalling_blocks) {
sig.push_back(s ? '#' : '-'); sig.push_back(s ? '#' : '-');
} }
bip9.pushKV("signalling", sig); bip9.pushKV("signalling", sig);
@ -1274,12 +1257,13 @@ static void SoftForkDescPushBack(const CBlockIndex* blockindex, UniValue& softfo
UniValue rv(UniValue::VOBJ); UniValue rv(UniValue::VOBJ);
rv.pushKV("type", "bip9"); rv.pushKV("type", "bip9");
if (ThresholdState::ACTIVE == next_state) { bool is_active = false;
rv.pushKV("height", chainman.m_versionbitscache.StateSinceHeight(blockindex, chainman.GetConsensus(), id)); 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("active", is_active);
rv.pushKV("bip9", std::move(bip9)); rv.pushKV("bip9", bip9);
softforks.pushKV(DeploymentName(id), std::move(rv)); softforks.pushKV(DeploymentName(id), std::move(rv));
} }

View file

@ -9,6 +9,18 @@
using enum ThresholdState; 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 ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex* pindexPrev, ThresholdConditionCache& cache) const
{ {
int nPeriod = Period(); int nPeriod = Period();
@ -205,6 +217,35 @@ public:
} // namespace } // 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) ThresholdState VersionBitsCache::State(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos)
{ {
LOCK(m_mutex); LOCK(m_mutex);

View file

@ -10,6 +10,8 @@
#include <array> #include <array>
#include <map> #include <map>
#include <optional>
#include <vector>
class CChainParams; class CChainParams;
@ -43,15 +45,25 @@ typedef std::map<const CBlockIndex*, ThresholdState> ThresholdConditionCache;
/** Display status of an in-progress BIP9 softfork */ /** Display status of an in-progress BIP9 softfork */
struct BIP9Stats { struct BIP9Stats {
/** Length of blocks of the BIP9 signalling period */ /** 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 */ /** 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 */ /** 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 */ /** 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 */ /** 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<BIP9Stats> stats;
std::vector<bool> signalling_blocks;
std::optional<int> active_since;
}; };
/** /**
@ -95,6 +107,8 @@ public:
static uint32_t Mask(const Consensus::Params& params, Consensus::DeploymentPos pos); 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. */ /** 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); ThresholdState State(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);