Implementation of BIP8 #10772

Open
wants to merge 5 commits into
from
View
@@ -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);
}
View
@@ -76,7 +76,7 @@ class CChainParams
const std::vector<SeedSpec6>& FixedSeeds() const { return vFixedSeeds; }
const CCheckpointData& Checkpoints() const { return checkpointData; }
const ChainTxData& TxData() const { return chainTxData; }
- void UpdateVersionBitsParameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout);
+ 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
View
@@ -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;
};
/**
View
@@ -1091,21 +1091,26 @@ bool AppInitParameterInteraction()
for (const std::string& strDeployment : gArgs.GetArgs("-vbparams")) {
std::vector<std::string> vDeploymentParams;
boost::split(vDeploymentParams, strDeployment, boost::is_any_of(":"));
- if (vDeploymentParams.size() != 3) {
- 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;
View
@@ -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)
@@ -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
View
@@ -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<int64_t>::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<int64_t>::max(); }
int Period(const Consensus::Params& params) const override { return params.nMinerConfirmationWindow; }
int Threshold(const Consensus::Params& params) const override { return params.nRuleChangeActivationThreshold; }
View
@@ -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
@@ -176,6 +183,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; }
View
@@ -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;
@@ -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):
@@ -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()