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)
{
// 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<bool> 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));
}

View file

@ -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);

View file

@ -10,6 +10,8 @@
#include <array>
#include <map>
#include <optional>
#include <vector>
class CChainParams;
@ -43,15 +45,25 @@ typedef std::map<const CBlockIndex*, ThresholdState> 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<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);
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);