diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp index f49430eee..633b49d53 100644 --- a/src/test/versionbits_tests.cpp +++ b/src/test/versionbits_tests.cpp @@ -1,391 +1,391 @@ // Copyright (c) 2014-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 "chain.h" #include "chainparams.h" #include "consensus/params.h" #include "test/test_bitcoin.h" #include "validation.h" #include "versionbits.h" #include /* Define a virtual block time, one block per 10 minutes after Nov 14 2014, * 0:55:36am */ int32_t TestTime(int nHeight) { return 1415926536 + 600 * nHeight; } static const Consensus::Params paramsDummy = Consensus::Params(); class TestConditionChecker : public AbstractThresholdConditionChecker { private: mutable ThresholdConditionCache cache; public: int64_t BeginTime(const Consensus::Params ¶ms) const override { return TestTime(10000); } int64_t EndTime(const Consensus::Params ¶ms) const override { return TestTime(20000); } int Period(const Consensus::Params ¶ms) const override { return 1000; } int Threshold(const Consensus::Params ¶ms) const override { return 900; } bool Condition(const CBlockIndex *pindex, const Consensus::Params ¶ms) const override { return (pindex->nVersion & 0x100); } ThresholdState GetStateFor(const CBlockIndex *pindexPrev) const { return AbstractThresholdConditionChecker::GetStateFor( pindexPrev, paramsDummy, cache); } int GetStateSinceHeightFor(const CBlockIndex *pindexPrev) const { return AbstractThresholdConditionChecker::GetStateSinceHeightFor( pindexPrev, paramsDummy, cache); } }; #define CHECKERS 6 class VersionBitsTester { // A fake blockchain std::vector vpblock; // 6 independent checkers for the same bit. // The first one performs all checks, the second only 50%, the third only // 25%, etc... // This is to test whether lack of cached information leads to the same // results. TestConditionChecker checker[CHECKERS]; // Test counter (to identify failures) int num; public: VersionBitsTester() : num(0) {} VersionBitsTester &Reset() { for (unsigned int i = 0; i < vpblock.size(); i++) { delete vpblock[i]; } for (unsigned int i = 0; i < CHECKERS; i++) { checker[i] = TestConditionChecker(); } vpblock.clear(); return *this; } ~VersionBitsTester() { Reset(); } VersionBitsTester &Mine(unsigned int height, int32_t nTime, int32_t nVersion) { while (vpblock.size() < height) { CBlockIndex *pindex = new CBlockIndex(); pindex->nHeight = vpblock.size(); pindex->pprev = vpblock.size() > 0 ? vpblock.back() : nullptr; pindex->nTime = nTime; pindex->nVersion = nVersion; pindex->BuildSkip(); vpblock.push_back(pindex); } return *this; } VersionBitsTester &TestStateSinceHeight(int height) { for (int i = 0; i < CHECKERS; i++) { if (InsecureRandBits(i) == 0) { BOOST_CHECK_MESSAGE( checker[i].GetStateSinceHeightFor( vpblock.empty() ? nullptr : vpblock.back()) == height, strprintf("Test %i for StateSinceHeight", num)); } } num++; return *this; } VersionBitsTester &TestDefined() { for (int i = 0; i < CHECKERS; i++) { if (InsecureRandBits(i) == 0) { BOOST_CHECK_MESSAGE( checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == - THRESHOLD_DEFINED, + ThresholdState::DEFINED, strprintf("Test %i for DEFINED", num)); } } num++; return *this; } VersionBitsTester &TestStarted() { for (int i = 0; i < CHECKERS; i++) { if (InsecureRandBits(i) == 0) { BOOST_CHECK_MESSAGE( checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == - THRESHOLD_STARTED, + ThresholdState::STARTED, strprintf("Test %i for STARTED", num)); } } num++; return *this; } VersionBitsTester &TestLockedIn() { for (int i = 0; i < CHECKERS; i++) { if (InsecureRandBits(i) == 0) { BOOST_CHECK_MESSAGE( checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == - THRESHOLD_LOCKED_IN, + ThresholdState::LOCKED_IN, strprintf("Test %i for LOCKED_IN", num)); } } num++; return *this; } VersionBitsTester &TestActive() { for (int i = 0; i < CHECKERS; i++) { if (InsecureRandBits(i) == 0) { BOOST_CHECK_MESSAGE( checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == - THRESHOLD_ACTIVE, + ThresholdState::ACTIVE, strprintf("Test %i for ACTIVE", num)); } } num++; return *this; } VersionBitsTester &TestFailed() { for (int i = 0; i < CHECKERS; i++) { if (InsecureRandBits(i) == 0) { BOOST_CHECK_MESSAGE( checker[i].GetStateFor(vpblock.empty() ? nullptr : vpblock.back()) == - THRESHOLD_FAILED, + ThresholdState::FAILED, strprintf("Test %i for FAILED", num)); } } num++; return *this; } CBlockIndex *Tip() { return vpblock.size() ? vpblock.back() : nullptr; } }; BOOST_FIXTURE_TEST_SUITE(versionbits_tests, TestingSetup) BOOST_AUTO_TEST_CASE(versionbits_test) { for (int i = 0; i < 64; i++) { // DEFINED -> FAILED VersionBitsTester() .TestDefined() .TestStateSinceHeight(0) .Mine(1, TestTime(1), 0x100) .TestDefined() .TestStateSinceHeight(0) .Mine(11, TestTime(11), 0x100) .TestDefined() .TestStateSinceHeight(0) .Mine(989, TestTime(989), 0x100) .TestDefined() .TestStateSinceHeight(0) .Mine(999, TestTime(20000), 0x100) .TestDefined() .TestStateSinceHeight(0) .Mine(1000, TestTime(20000), 0x100) .TestFailed() .TestStateSinceHeight(1000) .Mine(1999, TestTime(30001), 0x100) .TestFailed() .TestStateSinceHeight(1000) .Mine(2000, TestTime(30002), 0x100) .TestFailed() .TestStateSinceHeight(1000) .Mine(2001, TestTime(30003), 0x100) .TestFailed() .TestStateSinceHeight(1000) .Mine(2999, TestTime(30004), 0x100) .TestFailed() .TestStateSinceHeight(1000) .Mine(3000, TestTime(30005), 0x100) .TestFailed() .TestStateSinceHeight(1000) // DEFINED -> STARTED -> FAILED .Reset() .TestDefined() .TestStateSinceHeight(0) .Mine(1, TestTime(1), 0) .TestDefined() .TestStateSinceHeight(0) .Mine(1000, TestTime(10000) - 1, 0x100) .TestDefined() // One second more and it would be defined .TestStateSinceHeight(0) .Mine(2000, TestTime(10000), 0x100) .TestStarted() // So that's what happens the next period .TestStateSinceHeight(2000) .Mine(2051, TestTime(10010), 0) .TestStarted() // 51 old blocks .TestStateSinceHeight(2000) .Mine(2950, TestTime(10020), 0x100) .TestStarted() // 899 new blocks .TestStateSinceHeight(2000) .Mine(3000, TestTime(20000), 0) .TestFailed() // 50 old blocks (so 899 out of the past 1000) .TestStateSinceHeight(3000) .Mine(4000, TestTime(20010), 0x100) .TestFailed() .TestStateSinceHeight(3000) // DEFINED -> STARTED -> FAILED while threshold reached .Reset() .TestDefined() .TestStateSinceHeight(0) .Mine(1, TestTime(1), 0) .TestDefined() .TestStateSinceHeight(0) .Mine(1000, TestTime(10000) - 1, 0x101) .TestDefined() // One second more and it would be defined .TestStateSinceHeight(0) .Mine(2000, TestTime(10000), 0x101) .TestStarted() // So that's what happens the next period .TestStateSinceHeight(2000) .Mine(2999, TestTime(30000), 0x100) .TestStarted() // 999 new blocks .TestStateSinceHeight(2000) .Mine(3000, TestTime(30000), 0x100) .TestFailed() // 1 new block (so 1000 out of the past 1000 are new) .TestStateSinceHeight(3000) .Mine(3999, TestTime(30001), 0) .TestFailed() .TestStateSinceHeight(3000) .Mine(4000, TestTime(30002), 0) .TestFailed() .TestStateSinceHeight(3000) .Mine(14333, TestTime(30003), 0) .TestFailed() .TestStateSinceHeight(3000) .Mine(24000, TestTime(40000), 0) .TestFailed() .TestStateSinceHeight(3000) // DEFINED -> STARTED -> LOCKEDIN at the last minute -> ACTIVE .Reset() .TestDefined() .Mine(1, TestTime(1), 0) .TestDefined() .TestStateSinceHeight(0) .Mine(1000, TestTime(10000) - 1, 0x101) .TestDefined() // One second more and it would be defined .TestStateSinceHeight(0) .Mine(2000, TestTime(10000), 0x101) .TestStarted() // So that's what happens the next period .TestStateSinceHeight(2000) .Mine(2050, TestTime(10010), 0x200) .TestStarted() // 50 old blocks .TestStateSinceHeight(2000) .Mine(2950, TestTime(10020), 0x100) .TestStarted() // 900 new blocks .TestStateSinceHeight(2000) .Mine(2999, TestTime(19999), 0x200) .TestStarted() // 49 old blocks .TestStateSinceHeight(2000) .Mine(3000, TestTime(29999), 0x200) .TestLockedIn() // 1 old block (so 900 out of the past 1000) .TestStateSinceHeight(3000) .Mine(3999, TestTime(30001), 0) .TestLockedIn() .TestStateSinceHeight(3000) .Mine(4000, TestTime(30002), 0) .TestActive() .TestStateSinceHeight(4000) .Mine(14333, TestTime(30003), 0) .TestActive() .TestStateSinceHeight(4000) .Mine(24000, TestTime(40000), 0) .TestActive() .TestStateSinceHeight(4000) // DEFINED multiple periods -> STARTED multiple periods -> FAILED .Reset() .TestDefined() .TestStateSinceHeight(0) .Mine(999, TestTime(999), 0) .TestDefined() .TestStateSinceHeight(0) .Mine(1000, TestTime(1000), 0) .TestDefined() .TestStateSinceHeight(0) .Mine(2000, TestTime(2000), 0) .TestDefined() .TestStateSinceHeight(0) .Mine(3000, TestTime(10000), 0) .TestStarted() .TestStateSinceHeight(3000) .Mine(4000, TestTime(10000), 0) .TestStarted() .TestStateSinceHeight(3000) .Mine(5000, TestTime(10000), 0) .TestStarted() .TestStateSinceHeight(3000) .Mine(6000, TestTime(20000), 0) .TestFailed() .TestStateSinceHeight(6000) .Mine(7000, TestTime(20000), 0x100) .TestFailed() .TestStateSinceHeight(6000); } // Sanity checks of version bit deployments const auto chainParams = CreateChainParams(CBaseChainParams::MAIN); const Consensus::Params &mainnetParams = chainParams->GetConsensus(); for (int i = 0; i < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; i++) { uint32_t bitmask = VersionBitsMask(mainnetParams, (Consensus::DeploymentPos)i); // Make sure that no deployment tries to set an invalid bit. BOOST_CHECK_EQUAL(bitmask & ~(uint32_t)VERSIONBITS_TOP_MASK, bitmask); // Verify that the deployment windows of different deployment using the // same bit are disjoint. This test may need modification at such time // as a new deployment is proposed that reuses the bit of an activated // soft fork, before the end time of that soft fork. (Alternatively, // the end time of that activated soft fork could be later changed to be // earlier to avoid overlap.) for (int j = i + 1; j < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; j++) { if (VersionBitsMask(mainnetParams, (Consensus::DeploymentPos)j) == bitmask) { BOOST_CHECK(mainnetParams.vDeployments[j].nStartTime > mainnetParams.vDeployments[i].nTimeout || mainnetParams.vDeployments[i].nStartTime > mainnetParams.vDeployments[j].nTimeout); } } } } BOOST_AUTO_TEST_SUITE_END() diff --git a/src/versionbits.cpp b/src/versionbits.cpp index 27f82e15f..815fd127d 100644 --- a/src/versionbits.cpp +++ b/src/versionbits.cpp @@ -1,209 +1,209 @@ // 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 BIP9DeploymentInfo VersionBitsDeploymentInfo[Consensus::MAX_VERSION_BITS_DEPLOYMENTS] = { { /*.name =*/"testdummy", /*.gbt_force =*/true, }, }; ThresholdState AbstractThresholdConditionChecker::GetStateFor( const CBlockIndex *pindexPrev, const Consensus::Params ¶ms, 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 vToCompute; while (cache.count(pindexPrev) == 0) { if (pindexPrev == nullptr) { // The genesis block is by definition defined. - cache[pindexPrev] = THRESHOLD_DEFINED; + cache[pindexPrev] = ThresholdState::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; + cache[pindexPrev] = ThresholdState::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: { + case ThresholdState::DEFINED: { if (pindexPrev->GetMedianTimePast() >= nTimeTimeout) { - stateNext = THRESHOLD_FAILED; + stateNext = ThresholdState::FAILED; } else if (pindexPrev->GetMedianTimePast() >= nTimeStart) { - stateNext = THRESHOLD_STARTED; + stateNext = ThresholdState::STARTED; } break; } - case THRESHOLD_STARTED: { + case ThresholdState::STARTED: { if (pindexPrev->GetMedianTimePast() >= nTimeTimeout) { - stateNext = THRESHOLD_FAILED; + stateNext = ThresholdState::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; + stateNext = ThresholdState::LOCKED_IN; } break; } - case THRESHOLD_LOCKED_IN: { + case ThresholdState::LOCKED_IN: { // Always progresses into ACTIVE. - stateNext = THRESHOLD_ACTIVE; + stateNext = ThresholdState::ACTIVE; break; } - case THRESHOLD_FAILED: - case THRESHOLD_ACTIVE: { + case ThresholdState::FAILED: + case ThresholdState::ACTIVE: { // Nothing happens, these are terminal states. break; } } cache[pindexPrev] = state = stateNext; } return state; } int AbstractThresholdConditionChecker::GetStateSinceHeightFor( const CBlockIndex *pindexPrev, const Consensus::Params ¶ms, 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) { + if (initialState == ThresholdState::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 ¶ms) const override { return params.vDeployments[id].nStartTime; } int64_t EndTime(const Consensus::Params ¶ms) const override { return params.vDeployments[id].nTimeout; } int Period(const Consensus::Params ¶ms) const override { return params.nMinerConfirmationWindow; } int Threshold(const Consensus::Params ¶ms) const override { return params.nRuleChangeActivationThreshold; } bool Condition(const CBlockIndex *pindex, const Consensus::Params ¶ms) 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 ¶ms) const { return ((uint32_t)1) << params.vDeployments[id].bit; } }; } // namespace ThresholdState VersionBitsState(const CBlockIndex *pindexPrev, const Consensus::Params ¶ms, Consensus::DeploymentPos pos, VersionBitsCache &cache) { return VersionBitsConditionChecker(pos).GetStateFor(pindexPrev, params, cache.caches[pos]); } int VersionBitsStateSinceHeight(const CBlockIndex *pindexPrev, const Consensus::Params ¶ms, Consensus::DeploymentPos pos, VersionBitsCache &cache) { return VersionBitsConditionChecker(pos).GetStateSinceHeightFor( pindexPrev, params, cache.caches[pos]); } uint32_t VersionBitsMask(const Consensus::Params ¶ms, 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(); } } diff --git a/src/versionbits.h b/src/versionbits.h index 2d6a9f74f..c31b949c3 100644 --- a/src/versionbits.h +++ b/src/versionbits.h @@ -1,83 +1,83 @@ // 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. #ifndef BITCOIN_CONSENSUS_VERSIONBITS #define BITCOIN_CONSENSUS_VERSIONBITS #include "chain.h" #include /** What block version to use for new blocks (pre versionbits) */ static const int32_t VERSIONBITS_LAST_OLD_BLOCK_VERSION = 4; /** What bits to set in version for versionbits blocks */ static const int32_t VERSIONBITS_TOP_BITS = 0x20000000UL; /** What bitmask determines whether versionbits is in use */ static const int32_t VERSIONBITS_TOP_MASK = 0xE0000000UL; /** Total bits available for versionbits */ static const int32_t VERSIONBITS_NUM_BITS = 29; -enum ThresholdState { - THRESHOLD_DEFINED, - THRESHOLD_STARTED, - THRESHOLD_LOCKED_IN, - THRESHOLD_ACTIVE, - THRESHOLD_FAILED, +enum class ThresholdState { + DEFINED, + STARTED, + LOCKED_IN, + ACTIVE, + FAILED, }; // A map that gives the state for blocks whose height is a multiple of Period(). // The map is indexed by the block's parent, however, so all keys in the map // will either be nullptr or a block with (height + 1) % Period() == 0. typedef std::map ThresholdConditionCache; struct BIP9DeploymentInfo { /** Deployment name */ const char *name; /** Whether GBT clients can safely ignore this rule in simplified usage */ bool gbt_force; }; extern const struct BIP9DeploymentInfo VersionBitsDeploymentInfo[]; /** * Abstract class that implements BIP9-style threshold logic, and caches * results. */ class AbstractThresholdConditionChecker { protected: virtual bool Condition(const CBlockIndex *pindex, const Consensus::Params ¶ms) const = 0; virtual int64_t BeginTime(const Consensus::Params ¶ms) const = 0; virtual int64_t EndTime(const Consensus::Params ¶ms) const = 0; virtual int Period(const Consensus::Params ¶ms) const = 0; virtual int Threshold(const Consensus::Params ¶ms) const = 0; public: // 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 ¶ms, ThresholdConditionCache &cache) const; int GetStateSinceHeightFor(const CBlockIndex *pindexPrev, const Consensus::Params ¶ms, ThresholdConditionCache &cache) const; }; struct VersionBitsCache { ThresholdConditionCache caches[Consensus::MAX_VERSION_BITS_DEPLOYMENTS]; void Clear(); }; ThresholdState VersionBitsState(const CBlockIndex *pindexPrev, const Consensus::Params ¶ms, Consensus::DeploymentPos pos, VersionBitsCache &cache); int VersionBitsStateSinceHeight(const CBlockIndex *pindexPrev, const Consensus::Params ¶ms, Consensus::DeploymentPos pos, VersionBitsCache &cache); uint32_t VersionBitsMask(const Consensus::Params ¶ms, Consensus::DeploymentPos pos); #endif