diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 9620c85d40..ff13f4bcf0 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -82,15 +82,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; } } @@ -320,35 +326,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 @@ -514,35 +522,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 @@ -680,35 +690,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"); @@ -852,7 +864,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"); @@ -955,9 +969,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) diff --git a/src/chainparams.h b/src/chainparams.h index 3a216cb0fa..c39da1c85a 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -81,7 +81,7 @@ public: const std::vector& 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); @@ -151,7 +151,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 diff --git a/src/consensus/params.h b/src/consensus/params.h index 62c7456a84..fb0d338a97 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -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; diff --git a/src/init.cpp b/src/init.cpp index d3772e7c28..5d0cb21daa 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1351,10 +1351,10 @@ bool AppInitParameterInteraction() for (const std::string& strDeployment : gArgs.GetArgs("-vbparams")) { std::vector 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])); } @@ -1365,17 +1365,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; } } diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp index 0de4b81ebe..e287d67b35 100644 --- a/src/test/versionbits_tests.cpp +++ b/src/test/versionbits_tests.cpp @@ -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); } diff --git a/src/validation.cpp b/src/validation.cpp index 2bf0abf426..442dea885f 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -1915,7 +1915,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::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 { @@ -4895,7 +4895,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) diff --git a/src/versionbits.cpp b/src/versionbits.cpp index da6feb06c6..7b8b536f99 100644 --- a/src/versionbits.cpp +++ b/src/versionbits.cpp @@ -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::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 = THRESHOLD_FAILED; } else if (pindexPrev->GetMedianTimePast() >= nTimeStart) { stateNext = THRESHOLD_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::max()); + int nAttempt = (pindexCount->nHeight + 1 - nStartHeight) / nPeriod; + if (count >= Threshold(params, nAttempt)) { stateNext = THRESHOLD_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) diff --git a/src/versionbits.h b/src/versionbits.h index 8e26f1fea1..467764ced4 100644 --- a/src/versionbits.h +++ b/src/versionbits.h @@ -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); diff --git a/test/functional/feature_block_reward_reallocation.py b/test/functional/feature_block_reward_reallocation.py index 6d84f75078..5c3ad6c8cb 100755 --- a/test/functional/feature_block_reward_reallocation.py +++ b/test/functional/feature_block_reward_reallocation.py @@ -58,21 +58,27 @@ 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 - for i in range((num_blocks - 1) // 10): - self.bump_mocktime(10) - self.nodes[0].generate(10) + if num_blocks > 0: + for i in range((num_blocks - 1) // 10): + self.bump_mocktime(10) + self.nodes[0].generate(10) + self.sync_blocks() + self.nodes[0].generate((num_blocks - 1) % 10) self.sync_blocks() - self.nodes[0].generate((num_blocks - 1) % 10) + assert_equal(get_bip9_status(self.nodes[0], 'realloc')['status'], 'started') + self.nodes[0].generate(1) self.sync_blocks() - assert_equal(get_bip9_status(self.nodes[0], 'realloc')['status'], 'started') - bestblockhash = self.nodes[0].generate(1)[0] - 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() diff --git a/test/functional/test_runner.py b/test/functional/test_runner.py index 14553e1c0e..6decac6153 100755 --- a/test/functional/test_runner.py +++ b/test/functional/test_runner.py @@ -55,6 +55,7 @@ BASE_SCRIPTS= [ # Scripts that are run by the travis build process. # Longest test should go first, to favor running tests in parallel 'dip3-deterministicmns.py', # NOTE: needs dash_hash to pass + 'feature_block_reward_reallocation.py', 'wallet-hd.py', 'walletbackup.py', # vv Tests less than 5m vv @@ -77,7 +78,6 @@ BASE_SCRIPTS= [ 'llmq-is-retroactive.py', # NOTE: needs dash_hash to pass 'llmq-dkgerrors.py', # NOTE: needs dash_hash to pass 'dip4-coinbasemerkleroots.py', # NOTE: needs dash_hash to pass - 'feature_block_reward_reallocation.py', # vv Tests less than 60s vv 'sendheaders.py', # NOTE: needs dash_hash to pass 'zapwallettxes.py',