You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
219 lines
8.5 KiB
219 lines
8.5 KiB
// Copyright (c) 2016 The Bitcoin Core developers |
|
// Distributed under the MIT software license, see the accompanying |
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php. |
|
|
|
#include "versionbits.h" |
|
#include "consensus/params.h" |
|
|
|
const struct VBDeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION_BITS_DEPLOYMENTS] = { |
|
{ |
|
/*.name =*/ "testdummy", |
|
/*.gbt_force =*/ true, |
|
}, |
|
{ |
|
/*.name =*/ "csv", |
|
/*.gbt_force =*/ true, |
|
}, |
|
{ |
|
/*.name =*/ "segwit", |
|
/*.gbt_force =*/ true, |
|
} |
|
}; |
|
|
|
ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const |
|
{ |
|
int nPeriod = Period(params); |
|
int nThreshold = Threshold(params); |
|
int64_t nTimeStart = BeginTime(params); |
|
int64_t nTimeTimeout = EndTime(params); |
|
|
|
// A block's state is always the same as that of the first of its period, so it is computed based on a pindexPrev whose height equals a multiple of nPeriod - 1. |
|
if (pindexPrev != nullptr) { |
|
pindexPrev = pindexPrev->GetAncestor(pindexPrev->nHeight - ((pindexPrev->nHeight + 1) % nPeriod)); |
|
} |
|
|
|
// Walk backwards in steps of nPeriod to find a pindexPrev whose information is known |
|
std::vector<const CBlockIndex*> vToCompute; |
|
while (cache.count(pindexPrev) == 0) { |
|
if (pindexPrev == nullptr) { |
|
// The genesis block is by definition defined. |
|
cache[pindexPrev] = THRESHOLD_DEFINED; |
|
break; |
|
} |
|
if (pindexPrev->GetMedianTimePast() < nTimeStart) { |
|
// Optimization: don't recompute down further, as we know every earlier block will be before the start time |
|
cache[pindexPrev] = THRESHOLD_DEFINED; |
|
break; |
|
} |
|
vToCompute.push_back(pindexPrev); |
|
pindexPrev = pindexPrev->GetAncestor(pindexPrev->nHeight - nPeriod); |
|
} |
|
|
|
// At this point, cache[pindexPrev] is known |
|
assert(cache.count(pindexPrev)); |
|
ThresholdState state = cache[pindexPrev]; |
|
|
|
// Now walk forward and compute the state of descendants of pindexPrev |
|
while (!vToCompute.empty()) { |
|
ThresholdState stateNext = state; |
|
pindexPrev = vToCompute.back(); |
|
vToCompute.pop_back(); |
|
|
|
switch (state) { |
|
case THRESHOLD_DEFINED: { |
|
if (pindexPrev->GetMedianTimePast() >= nTimeTimeout) { |
|
stateNext = THRESHOLD_FAILED; |
|
} else if (pindexPrev->GetMedianTimePast() >= nTimeStart) { |
|
stateNext = THRESHOLD_STARTED; |
|
} |
|
break; |
|
} |
|
case THRESHOLD_STARTED: { |
|
if (pindexPrev->GetMedianTimePast() >= nTimeTimeout) { |
|
stateNext = THRESHOLD_FAILED; |
|
break; |
|
} |
|
// We need to count |
|
const CBlockIndex* pindexCount = pindexPrev; |
|
int count = 0; |
|
for (int i = 0; i < nPeriod; i++) { |
|
if (Condition(pindexCount, params)) { |
|
count++; |
|
} |
|
pindexCount = pindexCount->pprev; |
|
} |
|
if (count >= nThreshold) { |
|
stateNext = THRESHOLD_LOCKED_IN; |
|
} |
|
break; |
|
} |
|
case THRESHOLD_LOCKED_IN: { |
|
// Always progresses into ACTIVE. |
|
stateNext = THRESHOLD_ACTIVE; |
|
break; |
|
} |
|
case THRESHOLD_FAILED: |
|
case THRESHOLD_ACTIVE: { |
|
// Nothing happens, these are terminal states. |
|
break; |
|
} |
|
} |
|
cache[pindexPrev] = state = stateNext; |
|
} |
|
|
|
return state; |
|
} |
|
|
|
// return the numerical statistics of blocks signalling the specified BIP9 condition in this current period |
|
BIP9Stats AbstractThresholdConditionChecker::GetStateStatisticsFor(const CBlockIndex* pindex, const Consensus::Params& params) const |
|
{ |
|
BIP9Stats stats = {}; |
|
|
|
stats.period = Period(params); |
|
stats.threshold = Threshold(params); |
|
|
|
if (pindex == nullptr) |
|
return stats; |
|
|
|
// Find beginning of period |
|
const CBlockIndex* pindexEndOfPrevPeriod = pindex->GetAncestor(pindex->nHeight - ((pindex->nHeight + 1) % stats.period)); |
|
stats.elapsed = pindex->nHeight - pindexEndOfPrevPeriod->nHeight; |
|
|
|
// Count from current block to beginning of period |
|
int count = 0; |
|
const CBlockIndex* currentIndex = pindex; |
|
while (pindexEndOfPrevPeriod->nHeight != currentIndex->nHeight){ |
|
if (Condition(currentIndex, params)) |
|
count++; |
|
currentIndex = currentIndex->pprev; |
|
} |
|
|
|
stats.count = count; |
|
stats.possible = (stats.period - stats.threshold ) >= (stats.elapsed - count); |
|
|
|
return stats; |
|
} |
|
|
|
int AbstractThresholdConditionChecker::GetStateSinceHeightFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const |
|
{ |
|
const ThresholdState initialState = GetStateFor(pindexPrev, params, cache); |
|
|
|
// BIP 9 about state DEFINED: "The genesis block is by definition in this state for each deployment." |
|
if (initialState == THRESHOLD_DEFINED) { |
|
return 0; |
|
} |
|
|
|
const int nPeriod = Period(params); |
|
|
|
// A block's state is always the same as that of the first of its period, so it is computed based on a pindexPrev whose height equals a multiple of nPeriod - 1. |
|
// To ease understanding of the following height calculation, it helps to remember that |
|
// right now pindexPrev points to the block prior to the block that we are computing for, thus: |
|
// if we are computing for the last block of a period, then pindexPrev points to the second to last block of the period, and |
|
// if we are computing for the first block of a period, then pindexPrev points to the last block of the previous period. |
|
// The parent of the genesis block is represented by nullptr. |
|
pindexPrev = pindexPrev->GetAncestor(pindexPrev->nHeight - ((pindexPrev->nHeight + 1) % nPeriod)); |
|
|
|
const CBlockIndex* previousPeriodParent = pindexPrev->GetAncestor(pindexPrev->nHeight - nPeriod); |
|
|
|
while (previousPeriodParent != nullptr && GetStateFor(previousPeriodParent, params, cache) == initialState) { |
|
pindexPrev = previousPeriodParent; |
|
previousPeriodParent = pindexPrev->GetAncestor(pindexPrev->nHeight - nPeriod); |
|
} |
|
|
|
// Adjust the result because right now we point to the parent block. |
|
return pindexPrev->nHeight + 1; |
|
} |
|
|
|
namespace |
|
{ |
|
/** |
|
* Class to implement versionbits logic. |
|
*/ |
|
class VersionBitsConditionChecker : public AbstractThresholdConditionChecker { |
|
private: |
|
const Consensus::DeploymentPos id; |
|
|
|
protected: |
|
int64_t BeginTime(const Consensus::Params& params) const override { return params.vDeployments[id].nStartTime; } |
|
int64_t EndTime(const Consensus::Params& params) const override { return params.vDeployments[id].nTimeout; } |
|
int Period(const Consensus::Params& params) const override { return params.nMinerConfirmationWindow; } |
|
int Threshold(const Consensus::Params& params) const override { return params.nRuleChangeActivationThreshold; } |
|
|
|
bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const override |
|
{ |
|
return (((pindex->nVersion & VERSIONBITS_TOP_MASK) == VERSIONBITS_TOP_BITS) && (pindex->nVersion & Mask(params)) != 0); |
|
} |
|
|
|
public: |
|
explicit VersionBitsConditionChecker(Consensus::DeploymentPos id_) : id(id_) {} |
|
uint32_t Mask(const Consensus::Params& params) const { return ((uint32_t)1) << params.vDeployments[id].bit; } |
|
}; |
|
|
|
} // namespace |
|
|
|
ThresholdState VersionBitsState(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache) |
|
{ |
|
return VersionBitsConditionChecker(pos).GetStateFor(pindexPrev, params, cache.caches[pos]); |
|
} |
|
|
|
BIP9Stats VersionBitsStatistics(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos) |
|
{ |
|
return VersionBitsConditionChecker(pos).GetStateStatisticsFor(pindexPrev, params); |
|
} |
|
|
|
int VersionBitsStateSinceHeight(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache) |
|
{ |
|
return VersionBitsConditionChecker(pos).GetStateSinceHeightFor(pindexPrev, params, cache.caches[pos]); |
|
} |
|
|
|
uint32_t VersionBitsMask(const Consensus::Params& params, Consensus::DeploymentPos pos) |
|
{ |
|
return VersionBitsConditionChecker(pos).Mask(params); |
|
} |
|
|
|
void VersionBitsCache::Clear() |
|
{ |
|
for (unsigned int d = 0; d < Consensus::MAX_VERSION_BITS_DEPLOYMENTS; d++) { |
|
caches[d].clear(); |
|
} |
|
}
|
|
|