Implement dynamic activation thresholds (#3692)

* Implement dynamic activation thresholds

* fix

* Revert unrelated changes

* Clarify switching to/staying in LOCKED_IN state

* Fix signal function to work correctly with num_blocks=0

* Add simplified threshold calculation and use it in tests

* Check that thresholds are decreasing, reach the min level and stay there

* Drop `;`
This commit is contained in:
UdjinM6 2020-09-12 17:33:12 +03:00 committed by GitHub
parent 5176a26007
commit ab8347e06b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 172 additions and 73 deletions

View File

@ -81,15 +81,21 @@ static CBlock CreateGenesisBlock(uint32_t nTime, uint32_t nNonce, uint32_t nBits
}
void CChainParams::UpdateVersionBitsParameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout, int64_t nWindowSize, int64_t nThreshold)
void CChainParams::UpdateVersionBitsParameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout, int64_t nWindowSize, int64_t nThresholdStart, int64_t nThresholdMin, int64_t nFalloffCoeff)
{
consensus.vDeployments[d].nStartTime = nStartTime;
consensus.vDeployments[d].nTimeout = nTimeout;
if (nWindowSize != -1) {
consensus.vDeployments[d].nWindowSize = nWindowSize;
}
if (nThreshold != -1) {
consensus.vDeployments[d].nThreshold = nThreshold;
if (nThresholdStart != -1) {
consensus.vDeployments[d].nThresholdStart = nThresholdStart;
}
if (nThresholdMin != -1) {
consensus.vDeployments[d].nThresholdMin = nThresholdMin;
}
if (nFalloffCoeff != -1) {
consensus.vDeployments[d].nFalloffCoeff = nFalloffCoeff;
}
}
@ -319,35 +325,37 @@ public:
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0001].nStartTime = 1508025600; // Oct 15th, 2017
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0001].nTimeout = 1539561600; // Oct 15th, 2018
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0001].nWindowSize = 4032;
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0001].nThreshold = 3226; // 80% of 4032
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0001].nThresholdStart = 3226; // 80% of 4032
// Deployment of BIP147
consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].bit = 2;
consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].nStartTime = 1524477600; // Apr 23th, 2018
consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].nTimeout = 1556013600; // Apr 23th, 2019
consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].nWindowSize = 4032;
consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].nThreshold = 3226; // 80% of 4032
consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].nThresholdStart = 3226; // 80% of 4032
// Deployment of DIP0003
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].bit = 3;
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].nStartTime = 1546300800; // Jan 1st, 2019
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].nTimeout = 1577836800; // Jan 1st, 2020
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].nWindowSize = 4032;
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].nThreshold = 3226; // 80% of 4032
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].nThresholdStart = 3226; // 80% of 4032
// Deployment of DIP0008
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0008].bit = 4;
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0008].nStartTime = 1557878400; // May 15th, 2019
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0008].nTimeout = 1589500800; // May 15th, 2020
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0008].nWindowSize = 4032;
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0008].nThreshold = 3226; // 80% of 4032
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0008].nThresholdStart = 3226; // 80% of 4032
// Deployment of Block Reward Reallocation
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].bit = 5;
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nStartTime = 1601510400; // Oct 1st, 2020
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nTimeout = 1633046400; // Oct 1st, 2021
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nWindowSize = 4032;
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nThreshold = 3226; // 80% of 4032
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nThresholdStart = 3226; // 80% of 4032
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nThresholdMin = 2420; // 60% of 4032
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nFalloffCoeff = 5; // this corresponds to 10 periods
// The best chain should have at least this much work.
consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000000027b81f49774e9f7fc93f"); // 1215000
@ -513,35 +521,37 @@ public:
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0001].nStartTime = 1544655600; // Dec 13th, 2018
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0001].nTimeout = 1576191600; // Dec 13th, 2019
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0001].nWindowSize = 100;
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0001].nThreshold = 50; // 50% of 100
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0001].nThresholdStart = 50; // 50% of 100
// Deployment of BIP147
consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].bit = 2;
consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].nStartTime = 1544655600; // Dec 13th, 2018
consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].nTimeout = 1576191600; // Dec 13th, 2019
consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].nWindowSize = 100;
consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].nThreshold = 50; // 50% of 100
consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].nThresholdStart = 50; // 50% of 100
// Deployment of DIP0003
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].bit = 3;
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].nStartTime = 1544655600; // Dec 13th, 2018
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].nTimeout = 1576191600; // Dec 13th, 2019
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].nWindowSize = 100;
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].nThreshold = 50; // 50% of 100
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].nThresholdStart = 50; // 50% of 100
// Deployment of DIP0008
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0008].bit = 4;
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0008].nStartTime = 1553126400; // Mar 21st, 2019
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0008].nTimeout = 1584748800; // Mar 21st, 2020
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0008].nWindowSize = 100;
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0008].nThreshold = 50; // 50% of 100
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0008].nThresholdStart = 50; // 50% of 100
// Deployment of Block Reward Reallocation
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].bit = 5;
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nStartTime = 1598918400; // Sep 1st, 2020
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nTimeout = 1630454400; // Sep 1st, 2021
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nWindowSize = 100;
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nThreshold = 50; // 50% of 100
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nThresholdStart = 80; // 80% of 100
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nThresholdMin = 60; // 60% of 100
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nFalloffCoeff = 5; // this corresponds to 10 periods
// The best chain should have at least this much work.
consensus.nMinimumChainWork = uint256S("0x00000000000000000000000000000000000000000000000000ac720e0b2ed13d"); // 260000
@ -679,35 +689,37 @@ public:
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0001].nStartTime = 1505692800; // Sep 18th, 2017
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0001].nTimeout = 1537228800; // Sep 18th, 2018
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0001].nWindowSize = 100;
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0001].nThreshold = 50; // 50% of 100
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0001].nThresholdStart = 50; // 50% of 100
// Deployment of BIP147
consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].bit = 2;
consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].nStartTime = 1517792400; // Feb 5th, 2018
consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].nTimeout = 1549328400; // Feb 5th, 2019
consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].nWindowSize = 100;
consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].nThreshold = 50; // 50% of 100
consensus.vDeployments[Consensus::DEPLOYMENT_BIP147].nThresholdStart = 50; // 50% of 100
// Deployment of DIP0003
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].bit = 3;
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].nStartTime = 1535752800; // Sep 1st, 2018
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].nTimeout = 1567288800; // Sep 1st, 2019
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].nWindowSize = 100;
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].nThreshold = 50; // 50% of 100
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0003].nThresholdStart = 50; // 50% of 100
// Deployment of DIP0008
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0008].bit = 4;
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0008].nStartTime = 1553126400; // Mar 21st, 2019
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0008].nTimeout = 1900281600; // Mar 21st, 2030
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0008].nWindowSize = 100;
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0008].nThreshold = 50; // 50% of 100
consensus.vDeployments[Consensus::DEPLOYMENT_DIP0008].nThresholdStart = 50; // 50% of 100
// Deployment of Block Reward Reallocation
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].bit = 5;
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nStartTime = 1598918400; // Sep 1st, 2020
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nTimeout = 1900281600; // Mar 21st, 2030
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nWindowSize = 100;
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nThreshold = 50; // 50% of 100
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nThresholdStart = 80; // 80% of 100
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nThresholdMin = 60; // 60% of 100
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nFalloffCoeff = 5; // this corresponds to 10 periods
// The best chain should have at least this much work.
consensus.nMinimumChainWork = uint256S("0x000000000000000000000000000000000000000000000000000000000000000");
@ -851,7 +863,9 @@ public:
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nStartTime = 0;
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nTimeout = 999999999999ULL;
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nWindowSize = 500;
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nThreshold = 400; // 80%
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nThresholdStart = 400; // 80%
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nThresholdMin = 300; // 60%
consensus.vDeployments[Consensus::DEPLOYMENT_REALLOC].nFalloffCoeff = 5;
// The best chain should have at least this much work.
consensus.nMinimumChainWork = uint256S("0x00");
@ -953,9 +967,9 @@ void SelectParams(const std::string& network)
globalChainParams = CreateChainParams(network);
}
void UpdateVersionBitsParameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout, int64_t nWindowSize, int64_t nThreshold)
void UpdateVersionBitsParameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout, int64_t nWindowSize, int64_t nThresholdStart, int64_t nThresholdMin, int64_t nFalloffCoeff)
{
globalChainParams->UpdateVersionBitsParameters(d, nStartTime, nTimeout, nWindowSize, nThreshold);
globalChainParams->UpdateVersionBitsParameters(d, nStartTime, nTimeout, nWindowSize, nThresholdStart, nThresholdMin, nFalloffCoeff);
}
void UpdateDIP3Parameters(int nActivationHeight, int nEnforcementHeight)

View File

@ -87,7 +87,7 @@ public:
const std::vector<SeedSpec6>& FixedSeeds() const { return vFixedSeeds; }
const CCheckpointData& Checkpoints() const { return checkpointData; }
const ChainTxData& TxData() const { return chainTxData; }
void UpdateVersionBitsParameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout, int64_t nWindowSize, int64_t nThreshold);
void UpdateVersionBitsParameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout, int64_t nWindowSize, int64_t nThresholdStart, int64_t nThresholdMin, int64_t nFalloffCoeff);
void UpdateDIP3Parameters(int nActivationHeight, int nEnforcementHeight);
void UpdateBudgetParameters(int nMasternodePaymentsStartBlock, int nBudgetPaymentsStartBlock, int nSuperblockStartBlock);
void UpdateSubsidyAndDiffParams(int nMinimumDifficultyBlocks, int nHighSubsidyBlocks, int nHighSubsidyFactor);
@ -157,7 +157,7 @@ void SelectParams(const std::string& chain);
/**
* Allows modifying the Version Bits regtest parameters.
*/
void UpdateVersionBitsParameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout, int64_t nWindowSize, int64_t nThreshold);
void UpdateVersionBitsParameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout, int64_t nWindowSize, int64_t nThresholdStart, int64_t nThresholdMin, int64_t nFalloffCoeff);
/**
* Allows modifying the DIP3 activation and enforcement height

View File

@ -37,8 +37,12 @@ struct BIP9Deployment {
int64_t nTimeout;
/** The number of past blocks (including the block under consideration) to be taken into account for locking in a fork. */
int64_t nWindowSize{0};
/** A number of blocks, in the range of 1..nWindowSize, which must signal for a fork in order to lock it in. */
int64_t nThreshold{0};
/** A starting number of blocks, in the range of 1..nWindowSize, which must signal for a fork in order to lock it in. */
int64_t nThresholdStart{0};
/** A minimum number of blocks, in the range of 1..nWindowSize, which must signal for a fork in order to lock it in. */
int64_t nThresholdMin{0};
/** A coefficient which adjusts the speed a required number of signaling blocks is decreasing from nThresholdStart to nThresholdMin at with each period. */
int64_t nFalloffCoeff{0};
};
enum LLMQType : uint8_t
@ -158,7 +162,7 @@ struct Params {
/**
* Minimum blocks including miner confirmation of the total of nMinerConfirmationWindow blocks in a retargeting period,
* (nPowTargetTimespan / nPowTargetSpacing) which is also used for BIP9 deployments.
* Default BIP9Deployment::nThreshold value for deployments where it's not specified and for unknown deployments.
* Default BIP9Deployment::nThresholdStart value for deployments where it's not specified and for unknown deployments.
* Examples: 1916 for 95%, 1512 for testchains.
*/
uint32_t nRuleChangeActivationThreshold;

View File

@ -1353,10 +1353,10 @@ bool AppInitParameterInteraction()
for (const std::string& strDeployment : gArgs.GetArgs("-vbparams")) {
std::vector<std::string> vDeploymentParams;
boost::split(vDeploymentParams, strDeployment, boost::is_any_of(":"));
if (vDeploymentParams.size() != 3 && vDeploymentParams.size() != 5) {
return InitError("Version bits parameters malformed, expecting deployment:start:end or deployment:start:end:window:threshold");
if (vDeploymentParams.size() != 3 && vDeploymentParams.size() != 5 && vDeploymentParams.size() != 7) {
return InitError("Version bits parameters malformed, expecting deployment:start:end or deployment:start:end:window:threshold or deployment:start:end:window:thresholdstart:thresholdmin:falloffcoeff");
}
int64_t nStartTime, nTimeout, nWindowSize = -1, nThreshold = -1;
int64_t nStartTime, nTimeout, nWindowSize = -1, nThresholdStart = -1, nThresholdMin = -1, nFalloffCoeff = -1;
if (!ParseInt64(vDeploymentParams[1], &nStartTime)) {
return InitError(strprintf("Invalid nStartTime (%s)", vDeploymentParams[1]));
}
@ -1367,17 +1367,26 @@ bool AppInitParameterInteraction()
if (!ParseInt64(vDeploymentParams[3], &nWindowSize)) {
return InitError(strprintf("Invalid nWindowSize (%s)", vDeploymentParams[3]));
}
if (!ParseInt64(vDeploymentParams[4], &nThreshold)) {
return InitError(strprintf("Invalid nThreshold (%s)", vDeploymentParams[4]));
if (!ParseInt64(vDeploymentParams[4], &nThresholdStart)) {
return InitError(strprintf("Invalid nThresholdStart (%s)", vDeploymentParams[4]));
}
}
if (vDeploymentParams.size() == 7) {
if (!ParseInt64(vDeploymentParams[5], &nThresholdMin)) {
return InitError(strprintf("Invalid nThresholdMin (%s)", vDeploymentParams[5]));
}
if (!ParseInt64(vDeploymentParams[6], &nFalloffCoeff)) {
return InitError(strprintf("Invalid nFalloffCoeff (%s)", vDeploymentParams[6]));
}
}
bool found = false;
for (int j=0; j<(int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++j)
{
if (vDeploymentParams[0].compare(VersionBitsDeploymentInfo[j].name) == 0) {
UpdateVersionBitsParameters(Consensus::DeploymentPos(j), nStartTime, nTimeout, nWindowSize, nThreshold);
UpdateVersionBitsParameters(Consensus::DeploymentPos(j), nStartTime, nTimeout, nWindowSize, nThresholdStart, nThresholdMin, nFalloffCoeff);
found = true;
LogPrintf("Setting version bits activation parameters for %s to start=%ld, timeout=%ld, window=%ld, threshold=%ld\n", vDeploymentParams[0], nStartTime, nTimeout, nWindowSize, nThreshold);
LogPrintf("Setting version bits activation parameters for %s to start=%ld, timeout=%ld, window=%ld, thresholdstart=%ld, thresholdmin=%ld, falloffcoeff=%ld\n",
vDeploymentParams[0], nStartTime, nTimeout, nWindowSize, nThresholdStart, nThresholdMin, nFalloffCoeff);
break;
}
}

View File

@ -25,7 +25,7 @@ public:
int64_t BeginTime(const Consensus::Params& params) const override { return TestTime(10000); }
int64_t EndTime(const Consensus::Params& params) const override { return TestTime(20000); }
int Period(const Consensus::Params& params) const override { return 1000; }
int Threshold(const Consensus::Params& params) const override { return 900; }
int Threshold(const Consensus::Params& params, int nAttempt) const override { return 900; }
bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const override { return (pindex->nVersion & 0x100); }
ThresholdState GetStateFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateFor(pindexPrev, paramsDummy, cache); }

View File

@ -1925,7 +1925,7 @@ public:
int64_t BeginTime(const Consensus::Params& params) const override { return 0; }
int64_t EndTime(const Consensus::Params& params) const override { return std::numeric_limits<int64_t>::max(); }
int Period(const Consensus::Params& params) const override { return params.nMinerConfirmationWindow; }
int Threshold(const Consensus::Params& params) const override { return params.nRuleChangeActivationThreshold; }
int Threshold(const Consensus::Params& params, int nAttempt) const override { return params.nRuleChangeActivationThreshold; }
bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const override
{
@ -4931,7 +4931,7 @@ ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::D
BIP9Stats VersionBitsTipStatistics(const Consensus::Params& params, Consensus::DeploymentPos pos)
{
LOCK(cs_main);
return VersionBitsStatistics(chainActive.Tip(), params, pos);
return VersionBitsStatistics(chainActive.Tip(), params, pos, versionbitscache);
}
int VersionBitsTipStateSinceHeight(const Consensus::Params& params, Consensus::DeploymentPos pos)

View File

@ -46,7 +46,6 @@ const struct VBDeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION_B
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);
@ -76,6 +75,13 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex*
assert(cache.count(pindexPrev));
ThresholdState state = cache[pindexPrev];
int nStartHeight{std::numeric_limits<int>::max()};
for (const auto& pair : cache) {
if (pair.second == ThresholdState::STARTED && nStartHeight > pair.first->nHeight + 1) {
nStartHeight = pair.first->nHeight + 1;
}
}
// Now walk forward and compute the state of descendants of pindexPrev
while (!vToCompute.empty()) {
ThresholdState stateNext = state;
@ -88,6 +94,7 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex*
stateNext = ThresholdState::FAILED;
} else if (pindexPrev->GetMedianTimePast() >= nTimeStart) {
stateNext = ThresholdState::STARTED;
nStartHeight = pindexPrev->nHeight + 1;
}
break;
}
@ -105,7 +112,9 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex*
}
pindexCount = pindexCount->pprev;
}
if (count >= nThreshold) {
assert(nStartHeight > 0 && nStartHeight < std::numeric_limits<int>::max());
int nAttempt = (pindexCount->nHeight + 1 - nStartHeight) / nPeriod;
if (count >= Threshold(params, nAttempt)) {
stateNext = ThresholdState::LOCKED_IN;
}
break;
@ -128,12 +137,12 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex*
}
// 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 AbstractThresholdConditionChecker::GetStateStatisticsFor(const CBlockIndex* pindex, const Consensus::Params& params, ThresholdConditionCache& cache) const
{
BIP9Stats stats = {};
stats.period = Period(params);
stats.threshold = Threshold(params);
stats.threshold = Threshold(params, 0);
if (pindex == nullptr)
return stats;
@ -142,6 +151,15 @@ BIP9Stats AbstractThresholdConditionChecker::GetStateStatisticsFor(const CBlockI
const CBlockIndex* pindexEndOfPrevPeriod = pindex->GetAncestor(pindex->nHeight - ((pindex->nHeight + 1) % stats.period));
stats.elapsed = pindex->nHeight - pindexEndOfPrevPeriod->nHeight;
// Re-calculate current threshold
int nAttempt{0};
const ThresholdState state = GetStateFor(pindexEndOfPrevPeriod, params, cache);
if (state == ThresholdState::STARTED) {
int nStartHeight = GetStateSinceHeightFor(pindexEndOfPrevPeriod, params, cache);
nAttempt = (pindexEndOfPrevPeriod->nHeight + 1 - nStartHeight)/stats.period;
}
stats.threshold = Threshold(params, nAttempt);
// Count from current block to beginning of period
int count = 0;
const CBlockIndex* currentIndex = pindex;
@ -200,7 +218,17 @@ 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.vDeployments[id].nWindowSize ? params.vDeployments[id].nWindowSize : params.nMinerConfirmationWindow; }
int Threshold(const Consensus::Params& params) const override { return params.vDeployments[id].nThreshold ? params.vDeployments[id].nThreshold : params.nRuleChangeActivationThreshold; }
int Threshold(const Consensus::Params& params, int nAttempt) const override
{
if (params.vDeployments[id].nThresholdStart == 0) {
return params.nRuleChangeActivationThreshold;
}
if (params.vDeployments[id].nThresholdMin == 0 || params.vDeployments[id].nFalloffCoeff == 0) {
return params.vDeployments[id].nThresholdStart;
}
int64_t nThresholdCalc = params.vDeployments[id].nThresholdStart - nAttempt * nAttempt * Period(params) / 100 / params.vDeployments[id].nFalloffCoeff;
return std::max(params.vDeployments[id].nThresholdMin, nThresholdCalc);
}
bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const override
{
@ -219,9 +247,9 @@ ThresholdState VersionBitsState(const CBlockIndex* pindexPrev, const Consensus::
return VersionBitsConditionChecker(pos).GetStateFor(pindexPrev, params, cache.caches[pos]);
}
BIP9Stats VersionBitsStatistics(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos)
BIP9Stats VersionBitsStatistics(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache)
{
return VersionBitsConditionChecker(pos).GetStateStatisticsFor(pindexPrev, params);
return VersionBitsConditionChecker(pos).GetStateStatisticsFor(pindexPrev, params, cache.caches[pos]);
}
int VersionBitsStateSinceHeight(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache)

View File

@ -58,10 +58,10 @@ protected:
virtual int64_t BeginTime(const Consensus::Params& params) const =0;
virtual int64_t EndTime(const Consensus::Params& params) const =0;
virtual int Period(const Consensus::Params& params) const =0;
virtual int Threshold(const Consensus::Params& params) const =0;
virtual int Threshold(const Consensus::Params& params, int nAttempt) const =0;
public:
BIP9Stats GetStateStatisticsFor(const CBlockIndex* pindex, const Consensus::Params& params) const;
BIP9Stats GetStateStatisticsFor(const CBlockIndex* pindex, const Consensus::Params& params, ThresholdConditionCache& cache) const;
// Note that the functions below take a pindexPrev as input: they compute information for block B based on its parent.
ThresholdState GetStateFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const;
int GetStateSinceHeightFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const;
@ -75,7 +75,7 @@ struct VersionBitsCache
};
ThresholdState VersionBitsState(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache);
BIP9Stats VersionBitsStatistics(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos);
BIP9Stats VersionBitsStatistics(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache);
int VersionBitsStateSinceHeight(const CBlockIndex* pindexPrev, const Consensus::Params& params, Consensus::DeploymentPos pos, VersionBitsCache& cache);
uint32_t VersionBitsMask(const Consensus::Params& params, Consensus::DeploymentPos pos);

View File

@ -58,6 +58,7 @@ class BlockRewardReallocationTest(DashTestFramework):
test_block = self.create_test_block()
self.nodes[0].p2p.send_blocks_and_test([test_block], self.nodes[0], timeout=5)
# generate at most 10 signaling blocks at a time
if num_blocks > 0:
for i in range((num_blocks - 1) // 10):
self.bump_mocktime(10)
self.nodes[0].generate(10)
@ -65,14 +66,19 @@ class BlockRewardReallocationTest(DashTestFramework):
self.nodes[0].generate((num_blocks - 1) % 10)
self.sync_blocks()
assert_equal(get_bip9_status(self.nodes[0], 'realloc')['status'], 'started')
bestblockhash = self.nodes[0].generate(1)[0]
self.nodes[0].generate(1)
self.sync_blocks()
self.nodes[0].getblock(bestblockhash, 1)
if expected_lockin:
assert_equal(get_bip9_status(self.nodes[0], 'realloc')['status'], 'locked_in')
else:
assert_equal(get_bip9_status(self.nodes[0], 'realloc')['status'], 'started')
def threshold(self, attempt):
threshold_calc = 400 - attempt * attempt
if threshold_calc < 300:
return 300
return threshold_calc
def run_test(self):
self.log.info("Wait for DIP3 to activate")
while get_bip9_status(self.nodes[0], 'dip0003')['status'] != 'active':
@ -101,40 +107,56 @@ class BlockRewardReallocationTest(DashTestFramework):
bi = self.nodes[0].getblockchaininfo()
assert_equal(bi['blocks'], 499)
assert_equal(bi['bip9_softforks']['realloc']['status'], 'started')
assert_equal(bi['bip9_softforks']['realloc']['statistics']['threshold'], 400)
assert_equal(bi['bip9_softforks']['realloc']['statistics']['threshold'], self.threshold(0))
self.signal(399, False) # 1 block short
self.signal(400, True) # just enough to lock in
self.log.info("Still LOCKED_IN at height = 1498")
self.log.info("Still STARTED but new threshold should be lower at height = 999")
bi = self.nodes[0].getblockchaininfo()
assert_equal(bi['blocks'], 999)
assert_equal(bi['bip9_softforks']['realloc']['statistics']['threshold'], self.threshold(1))
self.signal(398, False) # 1 block short again
self.log.info("Still STARTED but new threshold should be even lower at height = 1499")
bi = self.nodes[0].getblockchaininfo()
assert_equal(bi['blocks'], 1499)
assert_equal(bi['bip9_softforks']['realloc']['statistics']['threshold'], self.threshold(2))
pre_locked_in_blockhash = bi['bestblockhash']
self.signal(396, True) # just enough to lock in
self.log.info("Advanced to LOCKED_IN at height = 1999")
for i in range(49):
self.bump_mocktime(10)
self.nodes[0].generate(10)
self.sync_blocks()
self.nodes[0].generate(9)
self.sync_blocks()
self.log.info("Still LOCKED_IN at height = 2498")
bi = self.nodes[0].getblockchaininfo()
assert_equal(bi['blocks'], 1998)
assert_equal(bi['blocks'], 2498)
assert_equal(bi['bip9_softforks']['realloc']['status'], 'locked_in')
self.log.info("Advance from LOCKED_IN to ACTIVE at height = 1999")
self.log.info("Advance from LOCKED_IN to ACTIVE at height = 2499")
self.nodes[0].generate(1) # activation
bi = self.nodes[0].getblockchaininfo()
assert_equal(bi['blocks'], 1999)
assert_equal(bi['blocks'], 2499)
assert_equal(bi['bip9_softforks']['realloc']['status'], 'active')
assert_equal(bi['bip9_softforks']['realloc']['since'], 2000)
assert_equal(bi['bip9_softforks']['realloc']['since'], 2500)
self.log.info("Reward split should stay ~50/50 before the first superblock after activation")
# This applies even if reallocation was activated right at superblock height like it does here
bt = self.nodes[0].getblocktemplate()
assert_equal(bt['height'], 2000)
assert_equal(bt['masternode'][0]['amount'], get_masternode_payment(bt['height'], bt['coinbasevalue'], 2000))
assert_equal(bt['height'], 2500)
assert_equal(bt['masternode'][0]['amount'], get_masternode_payment(bt['height'], bt['coinbasevalue'], 2500))
self.nodes[0].generate(9)
self.sync_blocks()
bt = self.nodes[0].getblocktemplate()
assert_equal(bt['masternode'][0]['amount'], get_masternode_payment(bt['height'], bt['coinbasevalue'], 2000))
assert_equal(bt['coinbasevalue'], 17171634268)
assert_equal(bt['masternode'][0]['amount'], 8585817128) # 0.4999999997
assert_equal(bt['masternode'][0]['amount'], get_masternode_payment(bt['height'], bt['coinbasevalue'], 2500))
assert_equal(bt['coinbasevalue'], 13748571607)
assert_equal(bt['masternode'][0]['amount'], 6874285801) # 0.4999999998
self.log.info("Reallocation should kick-in with the superblock mined at height = 2010")
for period in range(19): # there will be 19 adjustments, 3 superblocks long each
@ -143,11 +165,11 @@ class BlockRewardReallocationTest(DashTestFramework):
self.nodes[0].generate(10)
self.sync_blocks()
bt = self.nodes[0].getblocktemplate()
assert_equal(bt['masternode'][0]['amount'], get_masternode_payment(bt['height'], bt['coinbasevalue'], 2000))
assert_equal(bt['masternode'][0]['amount'], get_masternode_payment(bt['height'], bt['coinbasevalue'], 2500))
self.log.info("Reward split should reach ~60/40 after reallocation is done")
assert_equal(bt['coinbasevalue'], 12766530779)
assert_equal(bt['masternode'][0]['amount'], 7659918467) # 0.6
assert_equal(bt['coinbasevalue'], 10221599170)
assert_equal(bt['masternode'][0]['amount'], 6132959502) # 0.6
self.log.info("Reward split should stay ~60/40 after reallocation is done")
for period in range(10): # check 10 next superblocks
@ -155,9 +177,31 @@ class BlockRewardReallocationTest(DashTestFramework):
self.nodes[0].generate(10)
self.sync_blocks()
bt = self.nodes[0].getblocktemplate()
assert_equal(bt['masternode'][0]['amount'], get_masternode_payment(bt['height'], bt['coinbasevalue'], 2000))
assert_equal(bt['coinbasevalue'], 12766530779)
assert_equal(bt['masternode'][0]['amount'], 7659918467) # 0.6
assert_equal(bt['masternode'][0]['amount'], get_masternode_payment(bt['height'], bt['coinbasevalue'], 2500))
assert_equal(bt['coinbasevalue'], 9491484944)
assert_equal(bt['masternode'][0]['amount'], 5694890966) # 0.6
self.log.info("Rollback the chain back to the STARTED state")
self.mocktime = self.nodes[0].getblock(pre_locked_in_blockhash, 1)['time']
for node in self.nodes:
node.invalidateblock(pre_locked_in_blockhash)
self.sync_all()
# create and send non-signalling block
test_block = self.create_test_block()
self.nodes[0].p2p.send_blocks_and_test([test_block], self.nodes[0], timeout=5)
bi = self.nodes[0].getblockchaininfo()
assert_equal(bi['blocks'], 1499)
assert_equal(bi['bip9_softforks']['realloc']['status'], 'started')
assert_equal(bi['bip9_softforks']['realloc']['statistics']['threshold'], self.threshold(2))
self.log.info("Check thresholds reach min level and stay there")
for i in range(8): # 7 to reach min level and 1 more to check it doesn't go lower than that
self.signal(0, False) # no need to signal
bi = self.nodes[0].getblockchaininfo()
assert_equal(bi['blocks'], 1999 + i * 500)
assert_equal(bi['bip9_softforks']['realloc']['status'], 'started')
assert_equal(bi['bip9_softforks']['realloc']['statistics']['threshold'], self.threshold(i + 3))
assert_equal(bi['bip9_softforks']['realloc']['statistics']['threshold'], 300)
if __name__ == '__main__':
BlockRewardReallocationTest().main()

View File

@ -59,6 +59,7 @@ BASE_SCRIPTS= [
# Scripts that are run by the travis build process.
# Longest test should go first, to favor running tests in parallel
'feature_dip3_deterministicmns.py', # NOTE: needs dash_hash to pass
'feature_block_reward_reallocation.py',
'wallet_hd.py',
'wallet_backup.py',
# vv Tests less than 5m vv
@ -82,7 +83,6 @@ BASE_SCRIPTS= [
'feature_llmq_is_retroactive.py', # NOTE: needs dash_hash to pass
'feature_llmq_dkgerrors.py', # NOTE: needs dash_hash to pass
'feature_dip4_coinbasemerkleroots.py', # NOTE: needs dash_hash to pass
'feature_block_reward_reallocation.py',
# vv Tests less than 60s vv
'p2p_sendheaders.py', # NOTE: needs dash_hash to pass
'wallet_zapwallettxes.py',