From 60c835d397b5fe668f7f5c02b8ea61b3c6bb449c Mon Sep 17 00:00:00 2001 From: shaolinfry Date: Mon, 10 Jul 2017 19:37:35 +0000 Subject: [PATCH 1/5] Add BIP8 height parameters --- src/consensus/params.h | 8 ++++++-- src/validation.cpp | 2 ++ src/versionbits.cpp | 2 ++ src/versionbits.h | 2 ++ 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/consensus/params.h b/src/consensus/params.h index 6240e82857e..8541921493e 100644 --- a/src/consensus/params.h +++ b/src/consensus/params.h @@ -28,9 +28,13 @@ 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; + int64_t nStartTime = 0; /** Timeout/expiry MedianTime for the deployment attempt. */ - int64_t nTimeout; + int64_t nTimeout = 0; + /** Start block height for version bits miner confirmation. Should be a retarget block, can be in the past */ + int64_t nStartHeight = 0; + /** Timeout/expiry block height for the deployment attempt. Should be a retarget block. */ + int64_t nTimeoutHeight = 0; }; /** diff --git a/src/validation.cpp b/src/validation.cpp index 09288be1ca4..797240ddb98 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -1570,6 +1570,8 @@ class WarningBitsConditionChecker : public AbstractThresholdConditionChecker 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(); } + int64_t BeginHeight(const Consensus::Params& params) const override { return 0; } + int64_t EndHeight(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; } diff --git a/src/versionbits.cpp b/src/versionbits.cpp index 8047e17aa82..ee287d1a99d 100644 --- a/src/versionbits.cpp +++ b/src/versionbits.cpp @@ -176,6 +176,8 @@ class VersionBitsConditionChecker : public AbstractThresholdConditionChecker { 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; } + int64_t BeginHeight(const Consensus::Params& params) const override { return params.vDeployments[id].nStartHeight; } + int64_t EndHeight(const Consensus::Params& params) const override { return params.vDeployments[id].nTimeoutHeight; } int Period(const Consensus::Params& params) const override { return params.nMinerConfirmationWindow; } int Threshold(const Consensus::Params& params) const override { return params.nRuleChangeActivationThreshold; } diff --git a/src/versionbits.h b/src/versionbits.h index f4dfb71515a..166ff2a394c 100644 --- a/src/versionbits.h +++ b/src/versionbits.h @@ -55,6 +55,8 @@ class AbstractThresholdConditionChecker { virtual bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const =0; virtual int64_t BeginTime(const Consensus::Params& params) const =0; virtual int64_t EndTime(const Consensus::Params& params) const =0; + virtual int64_t BeginHeight(const Consensus::Params& params) const =0; + virtual int64_t EndHeight(const Consensus::Params& params) const =0; virtual int Period(const Consensus::Params& params) const =0; virtual int Threshold(const Consensus::Params& params) const =0; From affa1024396216fd25309fe2791e5aff8c25ba2d Mon Sep 17 00:00:00 2001 From: shaolinfry Date: Mon, 10 Jul 2017 19:38:00 +0000 Subject: [PATCH 2/5] Main BIP8 implmentation --- src/versionbits.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/versionbits.cpp b/src/versionbits.cpp index ee287d1a99d..9da3c3ed9e9 100644 --- a/src/versionbits.cpp +++ b/src/versionbits.cpp @@ -26,6 +26,9 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex* int nThreshold = Threshold(params); int64_t nTimeStart = BeginTime(params); int64_t nTimeTimeout = EndTime(params); + int64_t nHeightStart = BeginHeight(params); + int64_t nHeightTimeout = EndHeight(params); + bool fHeightBased = (nTimeStart == 0 && nTimeTimeout == 0) ? true : false; // 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 != NULL) { @@ -40,7 +43,8 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex* cache[pindexPrev] = THRESHOLD_DEFINED; break; } - if (pindexPrev->GetMedianTimePast() < nTimeStart) { + if ((fHeightBased && (pindexPrev->nHeight + 1) < nHeightStart) || + (!fHeightBased && pindexPrev->GetMedianTimePast() < nTimeStart)) { // Optimization: don't recompute down further, as we know every earlier block will be before the start time cache[pindexPrev] = THRESHOLD_DEFINED; break; @@ -61,16 +65,19 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex* switch (state) { case THRESHOLD_DEFINED: { - if (pindexPrev->GetMedianTimePast() >= nTimeTimeout) { + if ((fHeightBased && (pindexPrev->nHeight + 1) >= nHeightTimeout) || + (!fHeightBased && pindexPrev->GetMedianTimePast() >= nTimeTimeout)) { stateNext = THRESHOLD_FAILED; - } else if (pindexPrev->GetMedianTimePast() >= nTimeStart) { + } else if ((fHeightBased && (pindexPrev->nHeight + 1) >= nHeightStart) || + (!fHeightBased && pindexPrev->GetMedianTimePast() >= nTimeStart)) { stateNext = THRESHOLD_STARTED; } break; } case THRESHOLD_STARTED: { - if (pindexPrev->GetMedianTimePast() >= nTimeTimeout) { - stateNext = THRESHOLD_FAILED; + if ((fHeightBased && (pindexPrev->nHeight + 1) >= nHeightTimeout) || + (!fHeightBased && pindexPrev->GetMedianTimePast() >= nTimeTimeout)) { + stateNext = (fHeightBased == true) ? THRESHOLD_LOCKED_IN : THRESHOLD_FAILED; break; } // We need to count From 747db77480c50e4f5f31ae9d2463c9b638b314df Mon Sep 17 00:00:00 2001 From: shaolinfry Date: Mon, 10 Jul 2017 19:45:08 +0000 Subject: [PATCH 3/5] Add add tests for BIP8 --- src/test/versionbits_tests.cpp | 46 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp index 722f6ae059c..6e9194215e2 100644 --- a/src/test/versionbits_tests.cpp +++ b/src/test/versionbits_tests.cpp @@ -20,13 +20,21 @@ class TestConditionChecker : public AbstractThresholdConditionChecker { private: mutable ThresholdConditionCache cache; + bool height_based = false; + int64_t height_start = 0; + int64_t height_timeout = 0; 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); } + int64_t BeginTime(const Consensus::Params& params) const override { return this->height_based ? 0 : TestTime(10000); } + int64_t EndTime(const Consensus::Params& params) const override { return this->height_based ? 0 : TestTime(20000); } + int64_t BeginHeight(const Consensus::Params& params) const override { return this->height_start; } + int64_t EndHeight(const Consensus::Params& params) const override { return this->height_timeout; } int Period(const Consensus::Params& params) const override { return 1000; } int Threshold(const Consensus::Params& params) const override { return 900; } bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const override { return (pindex->nVersion & 0x100); } + void SetHeightBased(int64_t flag) { this->height_based = flag; } + void SetHeightStart(int64_t n) { this->height_start = n; } + void SetHeightTimeout(int64_t n) { this->height_timeout = n; } ThresholdState GetStateFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateFor(pindexPrev, paramsDummy, cache); } int GetStateSinceHeightFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateSinceHeightFor(pindexPrev, paramsDummy, cache); } @@ -50,6 +58,15 @@ class VersionBitsTester public: VersionBitsTester() : num(0) {} + VersionBitsTester& SetCheckerHeightBasedTest(bool flag, int64_t height_start, int64_t height_timeout) { + for (unsigned int i = 0; i < CHECKERS; i++) { + checker[i].SetHeightBased(flag); + checker[i].SetHeightStart(height_start); + checker[i].SetHeightTimeout(height_timeout); + } + return *this; + } + VersionBitsTester& Reset() { for (unsigned int i = 0; i < vpblock.size(); i++) { delete vpblock[i]; @@ -204,7 +221,30 @@ BOOST_AUTO_TEST_CASE(versionbits_test) .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); + .Mine(7000, TestTime(20000), 0x100).TestFailed().TestStateSinceHeight(6000) + + // DEFINED -> STARTED -> LOCKEDIN -> ACTIVE (mandatory lockin) + .Reset().SetCheckerHeightBasedTest(true, 2000, 4000).TestDefined().TestStateSinceHeight(0) + .Mine(999, TestTime(999), 0).TestDefined().TestStateSinceHeight(0) + .Mine(1000, TestTime(1000), 0).TestDefined().TestStateSinceHeight(0) + .Mine(2000, TestTime(10000), 0).TestStarted().TestStateSinceHeight(2000) + .Mine(4000, TestTime(10001), 0).TestLockedIn().TestStateSinceHeight(4000) + .Mine(5000, TestTime(10002), 0).TestActive().TestStateSinceHeight(5000) + .Mine(7000, TestTime(20000), 0x100).TestActive().TestStateSinceHeight(5000) + + // DEFINED -> STARTED -> LOCKEDIN -> ACTIVE (mandatory lockin but activation by signalling) + .Reset().SetCheckerHeightBasedTest(true, 3000, 10000).TestDefined().TestStateSinceHeight(0) + .Mine(1, TestTime(1), 0x200).TestDefined().TestStateSinceHeight(0) + .Mine(1000, TestTime(9999) - 1, 0x200).TestDefined().TestStateSinceHeight(0) + .Mine(2000, TestTime(10000), 0x101).TestDefined().TestStateSinceHeight(0) + .Mine(3000, TestTime(10010), 0x200).TestStarted().TestStateSinceHeight(3000) + .Mine(3050, TestTime(10020), 0x200).TestStarted().TestStateSinceHeight(3000) // 50 old blocks + .Mine(3950, TestTime(11999), 0x100).TestStarted().TestStateSinceHeight(3000) // 900 new blocks + .Mine(3999, TestTime(12000), 0x200).TestStarted().TestStateSinceHeight(3000) // 49 old blocks + .Mine(4000, TestTime(12500), 0x200).TestLockedIn().TestStateSinceHeight(4000) // 1 old block + .Mine(4999, TestTime(13000), 0).TestLockedIn().TestStateSinceHeight(4000) + .Mine(5000, TestTime(13001), 0).TestActive().TestStateSinceHeight(5000) + ; } // Sanity checks of version bit deployments From 64f63e729a95442337c27f704461cadfb4c7231a Mon Sep 17 00:00:00 2001 From: shaolinfry Date: Mon, 10 Jul 2017 19:31:37 +0000 Subject: [PATCH 4/5] Add param override regtest and BIP8 --- src/chainparams.cpp | 8 +++++--- src/chainparams.h | 4 ++-- src/init.cpp | 15 ++++++++++----- test/functional/p2p-compactblocks.py | 2 +- test/functional/p2p-segwit.py | 2 +- 5 files changed, 19 insertions(+), 12 deletions(-) diff --git a/src/chainparams.cpp b/src/chainparams.cpp index dc4d2621ee0..fdd983683a6 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -53,10 +53,12 @@ static CBlock CreateGenesisBlock(uint32_t nTime, uint32_t nNonce, uint32_t nBits return CreateGenesisBlock(pszTimestamp, genesisOutputScript, nTime, nNonce, nBits, nVersion, genesisReward); } -void CChainParams::UpdateVersionBitsParameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout) +void CChainParams::UpdateVersionBitsParameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout, int64_t nStartHeight, int64_t nTimeoutHeight) { consensus.vDeployments[d].nStartTime = nStartTime; consensus.vDeployments[d].nTimeout = nTimeout; + consensus.vDeployments[d].nStartHeight = nStartHeight; + consensus.vDeployments[d].nTimeoutHeight = nTimeoutHeight; } /** @@ -357,7 +359,7 @@ void SelectParams(const std::string& network) globalChainParams = CreateChainParams(network); } -void UpdateVersionBitsParameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout) +void UpdateVersionBitsParameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout, int64_t nStartHeight, int64_t nTimeoutHeight) { - globalChainParams->UpdateVersionBitsParameters(d, nStartTime, nTimeout); + globalChainParams->UpdateVersionBitsParameters(d, nStartTime, nTimeout, nStartHeight, nTimeoutHeight); } diff --git a/src/chainparams.h b/src/chainparams.h index f55ae4cf7f0..b5465d3e8fd 100644 --- a/src/chainparams.h +++ b/src/chainparams.h @@ -76,7 +76,7 @@ class CChainParams 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); + void UpdateVersionBitsParameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout, int64_t nStartHeight, int64_t nTimeoutHeight); protected: CChainParams() {} @@ -118,6 +118,6 @@ void SelectParams(const std::string& chain); /** * Allows modifying the Version Bits regtest parameters. */ -void UpdateVersionBitsParameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout); +void UpdateVersionBitsParameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout, int64_t nStartHeight, int64_t nTimeoutHeight); #endif // BITCOIN_CHAINPARAMS_H diff --git a/src/init.cpp b/src/init.cpp index 672ef77e80f..714799eca50 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1091,21 +1091,26 @@ 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) { - return InitError("Version bits parameters malformed, expecting deployment:start:end"); + if (vDeploymentParams.size() != 5) { + return InitError("Version bits parameters malformed, expecting deployment:timestart:timeend:heighstart:heightend"); } - int64_t nStartTime, nTimeout; + int64_t nStartTime, nTimeout, nStartHeight, nTimeoutHeight; if (!ParseInt64(vDeploymentParams[1], &nStartTime)) { return InitError(strprintf("Invalid nStartTime (%s)", vDeploymentParams[1])); } if (!ParseInt64(vDeploymentParams[2], &nTimeout)) { return InitError(strprintf("Invalid nTimeout (%s)", vDeploymentParams[2])); } - bool found = false; + if (!ParseInt64(vDeploymentParams[3], &nStartHeight)) { + return InitError(strprintf("Invalid nStartHeight (%s)", vDeploymentParams[3])); + } + if (!ParseInt64(vDeploymentParams[4], &nTimeoutHeight)) { + return InitError(strprintf("Invalid nTimeoutHeight (%s)", vDeploymentParams[4])); + } 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); + UpdateVersionBitsParameters(Consensus::DeploymentPos(j), nStartTime, nTimeout, nStartHeight, nTimeoutHeight); found = true; LogPrintf("Setting version bits activation parameters for %s to start=%ld, timeout=%ld\n", vDeploymentParams[0], nStartTime, nTimeout); break; diff --git a/test/functional/p2p-compactblocks.py b/test/functional/p2p-compactblocks.py index ff76e49fba7..42ca065a842 100755 --- a/test/functional/p2p-compactblocks.py +++ b/test/functional/p2p-compactblocks.py @@ -98,7 +98,7 @@ def __init__(self): self.setup_clean_chain = True # Node0 = pre-segwit, node1 = segwit-aware self.num_nodes = 2 - self.extra_args = [["-vbparams=segwit:0:0"], ["-txindex"]] + self.extra_args = [["-vbparams=segwit:0:0:0:0"], ["-txindex"]] self.utxos = [] def build_block_on_tip(self, node, segwit=False): diff --git a/test/functional/p2p-segwit.py b/test/functional/p2p-segwit.py index 63dfbb8ae6e..d82637647b0 100755 --- a/test/functional/p2p-segwit.py +++ b/test/functional/p2p-segwit.py @@ -114,7 +114,7 @@ def __init__(self): super().__init__() self.setup_clean_chain = True self.num_nodes = 3 - self.extra_args = [["-whitelist=127.0.0.1"], ["-whitelist=127.0.0.1", "-acceptnonstdtxn=0"], ["-whitelist=127.0.0.1", "-vbparams=segwit:0:0"]] + self.extra_args = [["-whitelist=127.0.0.1"], ["-whitelist=127.0.0.1", "-acceptnonstdtxn=0"], ["-whitelist=127.0.0.1", "-vbparams=segwit:0:0:0:0"]] def setup_network(self): self.setup_nodes() From 4525433b30823765de1ee892625691189008155f Mon Sep 17 00:00:00 2001 From: shaolinfry Date: Sun, 30 Apr 2017 20:20:55 +0000 Subject: [PATCH 5/5] Add BIP8 reporting to getblockchaininfo RPC --- src/rpc/blockchain.cpp | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index c17ca2fa3a1..8f09317a195 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1075,7 +1075,7 @@ static UniValue SoftForkDesc(const std::string &name, int version, CBlockIndex* return rv; } -static UniValue BIP9SoftForkDesc(const Consensus::Params& consensusParams, Consensus::DeploymentPos id) +static UniValue VBSoftForkDesc(const Consensus::Params& consensusParams, Consensus::DeploymentPos id) { UniValue rv(UniValue::VOBJ); const ThresholdState thresholdState = VersionBitsTipState(consensusParams, id); @@ -1107,13 +1107,13 @@ static UniValue BIP9SoftForkDesc(const Consensus::Params& consensusParams, Conse return rv; } -void BIP9SoftForkDescPushBack(UniValue& bip9_softforks, const std::string &name, const Consensus::Params& consensusParams, Consensus::DeploymentPos id) +void VBSoftForkDescPushBack(UniValue& vb_softforks, const std::string &name, const Consensus::Params& consensusParams, Consensus::DeploymentPos id) { // Deployments with timeout value of 0 are hidden. // A timeout value of 0 guarantees a softfork will never be activated. // This is used when softfork codes are merged without specifying the deployment schedule. if (consensusParams.vDeployments[id].nTimeout > 0) - bip9_softforks.push_back(Pair(name, BIP9SoftForkDesc(consensusParams, id))); + vb_softforks.push_back(Pair(name, VBSoftForkDesc(consensusParams, id))); } UniValue getblockchaininfo(const JSONRPCRequest& request) @@ -1143,6 +1143,15 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) " },\n" " }, ...\n" " ],\n" + " \"bip8_softforks\": { (object) status of BIP8 softforks in progress\n" + " \"xxxx\" : { (string) name of the softfork\n" + " \"status\": \"xxxx\", (string) one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\"\n" + " \"bit\": xx, (numeric) the bit (0-28) in the block version field used to signal this softfork (only for \"started\" status)\n" + " \"startTime\": xx, (numeric) the minimum median time past of a block at which the bit gains its meaning\n" + " \"timeout\": xx, (numeric) the median time past of a block at which the deployment is considered failed if not yet locked in\n" + " \"since\": xx (numeric) height of the first block to which the status applies\n" + " }\n" + " },\n" " \"bip9_softforks\": { (object) status of BIP9 softforks in progress\n" " \"xxxx\" : { (string) name of the softfork\n" " \"status\": \"xxxx\", (string) one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\"\n" @@ -1181,13 +1190,15 @@ UniValue getblockchaininfo(const JSONRPCRequest& request) const Consensus::Params& consensusParams = Params().GetConsensus(); CBlockIndex* tip = chainActive.Tip(); UniValue softforks(UniValue::VARR); + UniValue bip8_softforks(UniValue::VOBJ); UniValue bip9_softforks(UniValue::VOBJ); softforks.push_back(SoftForkDesc("bip34", 2, tip, consensusParams)); softforks.push_back(SoftForkDesc("bip66", 3, tip, consensusParams)); softforks.push_back(SoftForkDesc("bip65", 4, tip, consensusParams)); - BIP9SoftForkDescPushBack(bip9_softforks, "csv", consensusParams, Consensus::DEPLOYMENT_CSV); - BIP9SoftForkDescPushBack(bip9_softforks, "segwit", consensusParams, Consensus::DEPLOYMENT_SEGWIT); + VBSoftForkDescPushBack(bip9_softforks, "csv", consensusParams, Consensus::DEPLOYMENT_CSV); + VBSoftForkDescPushBack(bip9_softforks, "segwit", consensusParams, Consensus::DEPLOYMENT_SEGWIT); obj.push_back(Pair("softforks", softforks)); + obj.push_back(Pair("bip8_softforks", bip8_softforks)); obj.push_back(Pair("bip9_softforks", bip9_softforks)); if (fPruneMode)