diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -358,6 +358,7 @@ primitives/block.cpp protocol.cpp scheduler.cpp + versionbitsinfo.cpp warnings.cpp ) @@ -458,6 +459,7 @@ ui_interface.cpp validation.cpp validationinterface.cpp + versionbits.cpp ) # This require libevent diff --git a/src/Makefile.am b/src/Makefile.am --- a/src/Makefile.am +++ b/src/Makefile.am @@ -220,6 +220,7 @@ validation.h \ validationinterface.h \ versionbits.h \ + versionbitsinfo.h \ walletinitinterface.h \ wallet/coincontrol.h \ wallet/coinselection.h \ @@ -302,6 +303,7 @@ ui_interface.cpp \ validation.cpp \ validationinterface.cpp \ + versionbits.cpp \ $(BITCOIN_CORE_H) if !ENABLE_WALLET @@ -457,6 +459,7 @@ script/ismine.cpp \ script/sign.cpp \ script/standard.cpp \ + versionbitsinfo.cpp \ warnings.cpp \ $(BITCOIN_CORE_H) diff --git a/src/Makefile.test.include b/src/Makefile.test.include --- a/src/Makefile.test.include +++ b/src/Makefile.test.include @@ -131,6 +131,7 @@ test/util_tests.cpp \ test/validation_block_tests.cpp \ test/validation_tests.cpp \ + test/versionbits_tests.cpp \ test/work_comparator_tests.cpp \ rpc/test/server_tests.cpp diff --git a/src/chainparams.cpp b/src/chainparams.cpp --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -98,6 +98,18 @@ consensus.fPowAllowMinDifficultyBlocks = false; consensus.fPowNoRetargeting = false; + // 95% of 2016 + consensus.nRuleChangeActivationThreshold = 1916; + // nPowTargetTimespan / nPowTargetSpacing + consensus.nMinerConfirmationWindow = 2016; + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28; + // January 1, 2008 + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = + 1199145601; + // December 31, 2008 + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = + 1230767999; + // The best chain should have at least this much work. consensus.nMinimumChainWork = ChainParamsConstants::MAINNET_MINIMUM_CHAIN_WORK; @@ -269,6 +281,18 @@ consensus.fPowAllowMinDifficultyBlocks = true; consensus.fPowNoRetargeting = false; + // 75% for testchains + consensus.nRuleChangeActivationThreshold = 1512; + // nPowTargetTimespan / nPowTargetSpacing + consensus.nMinerConfirmationWindow = 2016; + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28; + // January 1, 2008 + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = + 1199145601; + // December 31, 2008 + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = + 1230767999; + // The best chain should have at least this much work. consensus.nMinimumChainWork = ChainParamsConstants::TESTNET_MINIMUM_CHAIN_WORK; @@ -398,6 +422,15 @@ consensus.fPowAllowMinDifficultyBlocks = true; consensus.fPowNoRetargeting = true; + // 75% for testchains + consensus.nRuleChangeActivationThreshold = 108; + // Faster than normal for regtest (144 instead of 2016) + consensus.nMinerConfirmationWindow = 144; + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28; + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 0; + consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = + Consensus::BIP9Deployment::NO_TIMEOUT; + // The best chain should have at least this much work. consensus.nMinimumChainWork = uint256S("0x00"); diff --git a/src/consensus/params.h b/src/consensus/params.h --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -9,8 +9,44 @@ #include #include +#include + namespace Consensus { +enum DeploymentPos { + DEPLOYMENT_TESTDUMMY, + // NOTE: Also add new deployments to VersionBitsDeploymentInfo in + // versionbits.cpp + MAX_VERSION_BITS_DEPLOYMENTS, +}; + +/** + * Struct for each individual consensus rule change using BIP9. + */ +struct BIP9Deployment { + /** Bit position to select the particular bit in nVersion. */ + int bit; + /** + * Start MedianTime for version bits miner confirmation. Can be a date in + * the past. + */ + int64_t nStartTime; + /** Timeout/expiry MedianTime for the deployment attempt. */ + int64_t nTimeout; + + /** Constant for nTimeout very far in the future. */ + static constexpr int64_t NO_TIMEOUT = std::numeric_limits::max(); + + /** + * Special value for nStartTime indicating that the deployment is always + * active. This is useful for testing, as it means tests don't need to deal + * with the activation process (which takes at least 3 BIP9 intervals). Only + * tests that specifically test the behaviour during activation cannot use + * this. + */ + static constexpr int64_t ALWAYS_ACTIVE = -1; +}; + /** * Parameters that influence chain consensus. */ @@ -38,6 +74,22 @@ int gravitonHeight; /** Unix time used for MTP activation of 15 May 2020 12:00:00 UTC upgrade */ int phononActivationTime; + + /** + * Don't warn about unknown BIP 9 activations below this height. + * This prevents us from warning about the CSV and segwit activations. + */ + int MinBIP9WarningHeight; + /** + * Minimum blocks including miner confirmation of the total of 2016 blocks + * in a retargeting period, (nPowTargetTimespan / nPowTargetSpacing) which + * is also used for BIP9 deployments. Examples: 1916 for 95%, 1512 for + * testchains. + */ + uint32_t nRuleChangeActivationThreshold; + uint32_t nMinerConfirmationWindow; + BIP9Deployment vDeployments[MAX_VERSION_BITS_DEPLOYMENTS]; + /** Proof of work parameters */ uint256 powLimit; bool fPowAllowMinDifficultyBlocks; diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -169,6 +169,7 @@ util_tests.cpp validation_block_tests.cpp validation_tests.cpp + versionbits_tests.cpp work_comparator_tests.cpp # RPC Tests diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp new file mode 100644 --- /dev/null +++ b/src/test/versionbits_tests.cpp @@ -0,0 +1,590 @@ +// Copyright (c) 2014-2019 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 +#include +#include +#include +#include + +#include + +#include + +/* Define a virtual block time, one block per 10 minutes after Nov 14 2014, + * 0:55:36am */ +static 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); + } +}; + +class TestAlwaysActiveConditionChecker : public TestConditionChecker { +public: + int64_t BeginTime(const Consensus::Params ¶ms) const override { + return Consensus::BIP9Deployment::ALWAYS_ACTIVE; + } +}; + +#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]; + // Another 6 that assume always active activation + TestAlwaysActiveConditionChecker checker_always[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(); + checker_always[i] = TestAlwaysActiveConditionChecker(); + } + 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)); + BOOST_CHECK_MESSAGE( + checker_always[i].GetStateSinceHeightFor( + vpblock.empty() ? nullptr : vpblock.back()) == 0, + strprintf("Test %i for StateSinceHeight (always active)", + 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()) == + ThresholdState::DEFINED, + strprintf("Test %i for DEFINED", num)); + BOOST_CHECK_MESSAGE( + checker_always[i].GetStateFor( + vpblock.empty() ? nullptr : vpblock.back()) == + ThresholdState::ACTIVE, + strprintf("Test %i for ACTIVE (always active)", 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()) == + ThresholdState::STARTED, + strprintf("Test %i for STARTED", num)); + BOOST_CHECK_MESSAGE( + checker_always[i].GetStateFor( + vpblock.empty() ? nullptr : vpblock.back()) == + ThresholdState::ACTIVE, + strprintf("Test %i for ACTIVE (always active)", 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()) == + ThresholdState::LOCKED_IN, + strprintf("Test %i for LOCKED_IN", num)); + BOOST_CHECK_MESSAGE( + checker_always[i].GetStateFor( + vpblock.empty() ? nullptr : vpblock.back()) == + ThresholdState::ACTIVE, + strprintf("Test %i for ACTIVE (always active)", 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()) == + ThresholdState::ACTIVE, + strprintf("Test %i for ACTIVE", num)); + BOOST_CHECK_MESSAGE( + checker_always[i].GetStateFor( + vpblock.empty() ? nullptr : vpblock.back()) == + ThresholdState::ACTIVE, + strprintf("Test %i for ACTIVE (always 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()) == + ThresholdState::FAILED, + strprintf("Test %i for FAILED", num)); + BOOST_CHECK_MESSAGE( + checker_always[i].GetStateFor( + vpblock.empty() ? nullptr : vpblock.back()) == + ThresholdState::ACTIVE, + strprintf("Test %i for ACTIVE (always active)", 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, static_cast(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, + static_cast(j)) == + bitmask) { + BOOST_CHECK(mainnetParams.vDeployments[j].nStartTime > + mainnetParams.vDeployments[i].nTimeout || + mainnetParams.vDeployments[i].nStartTime > + mainnetParams.vDeployments[j].nTimeout); + } + } + } +} + +BOOST_AUTO_TEST_CASE(versionbits_computeblockversion) { + // Check that ComputeBlockVersion will set the appropriate bit correctly + // on mainnet. + const auto chainParams = CreateChainParams(CBaseChainParams::MAIN); + const Consensus::Params &mainnetParams = chainParams->GetConsensus(); + + // Use the TESTDUMMY deployment for testing purposes. + int64_t bit = + mainnetParams.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit; + int64_t nStartTime = + mainnetParams.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime; + int64_t nTimeout = + mainnetParams.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout; + + assert(nStartTime < nTimeout); + + // In the first chain, test that the bit is set by CBV until it has failed. + // In the second chain, test the bit is set by CBV while STARTED and + // LOCKED-IN, and then no longer set while ACTIVE. + VersionBitsTester firstChain, secondChain; + + // Start generating blocks before nStartTime + int64_t nTime = nStartTime - 1; + + // Before MedianTimePast of the chain has crossed nStartTime, the bit + // should not be set. + CBlockIndex *lastBlock = nullptr; + lastBlock = firstChain + .Mine(mainnetParams.nMinerConfirmationWindow, nTime, + VERSIONBITS_LAST_OLD_BLOCK_VERSION) + .Tip(); + BOOST_CHECK_EQUAL( + ComputeBlockVersion(lastBlock, mainnetParams) & (1 << bit), 0); + + // Mine more blocks (4 less than the adjustment period) at the old time, and + // check that CBV isn't setting the bit yet. + for (uint32_t i = 1; i < mainnetParams.nMinerConfirmationWindow - 4; i++) { + lastBlock = firstChain + .Mine(mainnetParams.nMinerConfirmationWindow + i, nTime, + VERSIONBITS_LAST_OLD_BLOCK_VERSION) + .Tip(); + // This works because VERSIONBITS_LAST_OLD_BLOCK_VERSION happens + // to be 4, and the bit we're testing happens to be bit 28. + BOOST_CHECK_EQUAL( + ComputeBlockVersion(lastBlock, mainnetParams) & (1 << bit), 0); + } + // Now mine 5 more blocks at the start time -- MTP should not have passed + // yet, so CBV should still not yet set the bit. + nTime = nStartTime; + for (uint32_t i = mainnetParams.nMinerConfirmationWindow - 4; + i <= mainnetParams.nMinerConfirmationWindow; i++) { + lastBlock = firstChain + .Mine(mainnetParams.nMinerConfirmationWindow + i, nTime, + VERSIONBITS_LAST_OLD_BLOCK_VERSION) + .Tip(); + BOOST_CHECK_EQUAL( + ComputeBlockVersion(lastBlock, mainnetParams) & (1 << bit), 0); + } + + // Advance to the next period and transition to STARTED, + lastBlock = firstChain + .Mine(mainnetParams.nMinerConfirmationWindow * 3, nTime, + VERSIONBITS_LAST_OLD_BLOCK_VERSION) + .Tip(); + // so ComputeBlockVersion should now set the bit, + printf("%x\n", ComputeBlockVersion(lastBlock, mainnetParams)); + BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1 << bit)) != + 0); + // and should also be using the VERSIONBITS_TOP_BITS. + BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & + VERSIONBITS_TOP_MASK, + VERSIONBITS_TOP_BITS); + + // Check that ComputeBlockVersion will set the bit until nTimeout + nTime += 600; + uint32_t blocksToMine = mainnetParams.nMinerConfirmationWindow * + 2; // test blocks for up to 2 time periods + uint32_t nHeight = mainnetParams.nMinerConfirmationWindow * 3; + // These blocks are all before nTimeout is reached. + while (nTime < nTimeout && blocksToMine > 0) { + lastBlock = + firstChain + .Mine(nHeight + 1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION) + .Tip(); + BOOST_CHECK( + (ComputeBlockVersion(lastBlock, mainnetParams) & (1 << bit)) != 0); + BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & + VERSIONBITS_TOP_MASK, + VERSIONBITS_TOP_BITS); + blocksToMine--; + nTime += 600; + nHeight += 1; + } + + nTime = nTimeout; + // FAILED is only triggered at the end of a period, so CBV should be setting + // the bit until the period transition. + for (uint32_t i = 0; i < mainnetParams.nMinerConfirmationWindow - 1; i++) { + lastBlock = + firstChain + .Mine(nHeight + 1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION) + .Tip(); + BOOST_CHECK( + (ComputeBlockVersion(lastBlock, mainnetParams) & (1 << bit)) != 0); + nHeight += 1; + } + // The next block should trigger no longer setting the bit. + lastBlock = + firstChain.Mine(nHeight + 1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION) + .Tip(); + BOOST_CHECK_EQUAL( + ComputeBlockVersion(lastBlock, mainnetParams) & (1 << bit), 0); + + // On a new chain: + // verify that the bit will be set after lock-in, and then stop being set + // after activation. + nTime = nStartTime; + + // Mine one period worth of blocks, and check that the bit will be on for + // the next period. + lastBlock = secondChain + .Mine(mainnetParams.nMinerConfirmationWindow, nTime, + VERSIONBITS_LAST_OLD_BLOCK_VERSION) + .Tip(); + BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1 << bit)) != + 0); + + // Mine another period worth of blocks, signaling the new bit. + lastBlock = secondChain + .Mine(mainnetParams.nMinerConfirmationWindow * 2, nTime, + VERSIONBITS_TOP_BITS | (1 << bit)) + .Tip(); + // After one period of setting the bit on each block, it should have locked + // in. We keep setting the bit for one more period though, until activation. + BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1 << bit)) != + 0); + + // Now check that we keep mining the block until the end of this period, and + // then stop at the beginning of the next period. + lastBlock = secondChain + .Mine((mainnetParams.nMinerConfirmationWindow * 3) - 1, + nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION) + .Tip(); + BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1 << bit)) != + 0); + lastBlock = secondChain + .Mine(mainnetParams.nMinerConfirmationWindow * 3, nTime, + VERSIONBITS_LAST_OLD_BLOCK_VERSION) + .Tip(); + BOOST_CHECK_EQUAL( + ComputeBlockVersion(lastBlock, mainnetParams) & (1 << bit), 0); + + // Finally, verify that after a soft fork has activated, CBV no longer uses + // VERSIONBITS_LAST_OLD_BLOCK_VERSION. + // BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & + // VERSIONBITS_TOP_MASK, VERSIONBITS_TOP_BITS); +} +BOOST_AUTO_TEST_SUITE_END() diff --git a/src/validation.cpp b/src/validation.cpp --- a/src/validation.cpp +++ b/src/validation.cpp @@ -1556,9 +1556,24 @@ scriptcheckqueue.Thread(); } +VersionBitsCache versionbitscache GUARDED_BY(cs_main); + int32_t ComputeBlockVersion(const CBlockIndex *pindexPrev, const Consensus::Params ¶ms) { + LOCK(cs_main); int32_t nVersion = VERSIONBITS_TOP_BITS; + + for (int i = 0; i < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; i++) { + ThresholdState state = VersionBitsState( + pindexPrev, params, static_cast(i), + versionbitscache); + if (state == ThresholdState::LOCKED_IN || + state == ThresholdState::STARTED) { + nVersion |= VersionBitsMask( + params, static_cast(i)); + } + } + return nVersion; } diff --git a/src/versionbits.h b/src/versionbits.h --- a/src/versionbits.h +++ b/src/versionbits.h @@ -1,11 +1,12 @@ -// Copyright (c) 2016 The Bitcoin Core developers +// Copyright (c) 2016-2018 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_VERSIONBITS_H #define BITCOIN_VERSIONBITS_H -#include +#include +#include /** What block version to use for new blocks (pre versionbits) */ static const int32_t VERSIONBITS_LAST_OLD_BLOCK_VERSION = 4; @@ -16,4 +17,114 @@ /** Total bits available for versionbits */ static const int32_t VERSIONBITS_NUM_BITS = 29; +/** + * BIP 9 defines a finite-state-machine to deploy a softfork in multiple stages. + * State transitions happen during retarget period if conditions are met. In + * case of reorg, transitions can go backward. Without transition, state is + * inherited between periods. All blocks of a period share the same state. + */ +enum class ThresholdState { + DEFINED, // First state that each softfork starts out as. The genesis block + // is by definition in this state for each deployment. + STARTED, // For blocks past the starttime. + LOCKED_IN, // For one retarget period after the first retarget period with + // STARTED blocks of which at least threshold have the associated + // bit set in nVersion. + ACTIVE, // For all blocks after the LOCKED_IN retarget period (final state) + FAILED, // For all blocks once the first retarget period after the timeout + // time is hit, if LOCKED_IN wasn't already reached (final state) +}; + +// 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; + +/** Display status of an in-progress BIP9 softfork */ +struct BIP9Stats { + /** Length of blocks of the BIP9 signalling period */ + int period; + /** + * Number of blocks with the version bit set required to activate the + * softfork. + */ + int threshold; + /** + * Number of blocks elapsed since the beginning of the current period. + */ + int elapsed; + /** + * Number of blocks with the version bit set since the beginning of the + * current period. + */ + int count; + /** + * False if there are not enough blocks left in this period to pass + * activation threshold. + */ + bool possible; +}; + +/** + * 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: + /** + * Returns the numerical statistics of an in-progress BIP9 softfork in the + * current period. + */ + BIP9Stats GetStateStatisticsFor(const CBlockIndex *pindex, + const Consensus::Params ¶ms) const; + /** + * Returns the state for pindex A based on parent pindexPrev B. Applies any + * state transition if conditions are present. Caches state from first block + * of period. + */ + ThresholdState GetStateFor(const CBlockIndex *pindexPrev, + const Consensus::Params ¶ms, + ThresholdConditionCache &cache) const; + /** + * Returns the height since when the ThresholdState has started for pindex. + * A based on parent pindexPrev B, all blocks of a period share the same. + */ + int GetStateSinceHeightFor(const CBlockIndex *pindexPrev, + const Consensus::Params ¶ms, + ThresholdConditionCache &cache) const; +}; + +/** + * BIP 9 allows multiple softforks to be deployed in parallel. We cache + * per-period state for every one of them keyed by the bit position used to + * signal support. + */ +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); +BIP9Stats VersionBitsStatistics(const CBlockIndex *pindexPrev, + const Consensus::Params ¶ms, + Consensus::DeploymentPos pos); +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 // BITCOIN_VERSIONBITS_H diff --git a/src/versionbits.cpp b/src/versionbits.cpp new file mode 100644 --- /dev/null +++ b/src/versionbits.cpp @@ -0,0 +1,251 @@ +// Copyright (c) 2016-2018 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 +#include + +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); + + // Check if this deployment is always active. + if (nTimeStart == Consensus::BIP9Deployment::ALWAYS_ACTIVE) { + return ThresholdState::ACTIVE; + } + + // 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] = 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] = 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 ThresholdState::DEFINED: { + if (pindexPrev->GetMedianTimePast() >= nTimeTimeout) { + stateNext = ThresholdState::FAILED; + } else if (pindexPrev->GetMedianTimePast() >= nTimeStart) { + stateNext = ThresholdState::STARTED; + } + break; + } + case ThresholdState::STARTED: { + if (pindexPrev->GetMedianTimePast() >= nTimeTimeout) { + 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 = ThresholdState::LOCKED_IN; + } + break; + } + case ThresholdState::LOCKED_IN: { + // Always progresses into ACTIVE. + stateNext = ThresholdState::ACTIVE; + break; + } + case ThresholdState::FAILED: + case ThresholdState::ACTIVE: { + // Nothing happens, these are terminal states. + break; + } + } + cache[pindexPrev] = state = stateNext; + } + + return state; +} + +BIP9Stats AbstractThresholdConditionChecker::GetStateStatisticsFor( + const CBlockIndex *pindex, const Consensus::Params ¶ms) 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 ¶ms, + ThresholdConditionCache &cache) const { + int64_t start_time = BeginTime(params); + if (start_time == Consensus::BIP9Deployment::ALWAYS_ACTIVE) { + return 0; + } + + 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 == 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]); +} + +BIP9Stats VersionBitsStatistics(const CBlockIndex *pindexPrev, + const Consensus::Params ¶ms, + Consensus::DeploymentPos pos) { + return VersionBitsConditionChecker(pos).GetStateStatisticsFor(pindexPrev, + params); +} + +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/versionbitsinfo.h b/src/versionbitsinfo.h new file mode 100644 --- /dev/null +++ b/src/versionbitsinfo.h @@ -0,0 +1,17 @@ +// Copyright (c) 2016-2018 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_VERSIONBITSINFO_H +#define BITCOIN_VERSIONBITSINFO_H + +struct VBDeploymentInfo { + /** Deployment name */ + const char *name; + /** Whether GBT clients can safely ignore this rule in simplified usage */ + bool gbt_force; +}; + +extern const struct VBDeploymentInfo VersionBitsDeploymentInfo[]; + +#endif // BITCOIN_VERSIONBITSINFO_H diff --git a/src/versionbitsinfo.cpp b/src/versionbitsinfo.cpp new file mode 100644 --- /dev/null +++ b/src/versionbitsinfo.cpp @@ -0,0 +1,15 @@ +// Copyright (c) 2016-2018 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 + +#include + +const struct VBDeploymentInfo + VersionBitsDeploymentInfo[Consensus::MAX_VERSION_BITS_DEPLOYMENTS] = { + { + /*.name =*/"testdummy", + /*.gbt_force =*/true, + }, +};