Showing with 90 additions and 1 deletion.
  1. +4 −0 src/consensus/params.h
  2. +1 −0 src/rpc/blockchain.cpp
  3. +2 −0 src/rpc/mining.cpp
  4. +50 −1 src/test/versionbits_tests.cpp
  5. +16 −0 src/validation.cpp
  6. +3 −0 src/validation.h
  7. +12 −0 src/versionbits.cpp
  8. +2 −0 src/versionbits.h
4 changes: 4 additions & 0 deletions src/consensus/params.h
Expand Up @@ -31,6 +31,10 @@ struct BIP9Deployment {
int64_t nStartTime;
/** Timeout/expiry MedianTime for the deployment attempt. */
int64_t nTimeout;
/** Mandatory activation MedianTime, default 0 to disable
* the first retarget after this value for a STARTED deployment
* will transition deployment to PRE_LOCK_IN */
int64_t nActivationTime = 0;
};

/**
Expand Down
1 change: 1 addition & 0 deletions src/rpc/blockchain.cpp
Expand Up @@ -1053,6 +1053,7 @@ static UniValue BIP9SoftForkDesc(const Consensus::Params& consensusParams, Conse
switch (thresholdState) {
case THRESHOLD_DEFINED: rv.push_back(Pair("status", "defined")); break;
case THRESHOLD_STARTED: rv.push_back(Pair("status", "started")); break;
case THRESHOLD_PRE_LOCK_IN: rv.push_back(Pair("status", "pre_locked_in")); break;
case THRESHOLD_LOCKED_IN: rv.push_back(Pair("status", "locked_in")); break;
case THRESHOLD_ACTIVE: rv.push_back(Pair("status", "active")); break;
case THRESHOLD_FAILED: rv.push_back(Pair("status", "failed")); break;
Expand Down
2 changes: 2 additions & 0 deletions src/rpc/mining.cpp
Expand Up @@ -616,6 +616,8 @@ UniValue getblocktemplate(const JSONRPCRequest& request)
case THRESHOLD_FAILED:
// Not exposed to GBT at all
break;
case THRESHOLD_PRE_LOCK_IN:
// Ensure bit is set in block version
case THRESHOLD_LOCKED_IN:
// Ensure bit is set in block version
pblock->nVersion |= VersionBitsMask(consensusParams, pos);
Expand Down
51 changes: 50 additions & 1 deletion src/test/versionbits_tests.cpp
Expand Up @@ -14,20 +14,24 @@

/* 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; }
int32_t ActivationTestTime(int nHeight) { return nHeight == 0 ? 0 : TestTime(nHeight); }

static const Consensus::Params paramsDummy = Consensus::Params();

class TestConditionChecker : public AbstractThresholdConditionChecker
{
private:
mutable ThresholdConditionCache cache;
int64_t activation_time = 0;

public:
int64_t BeginTime(const Consensus::Params& params) const { return TestTime(10000); }
int64_t EndTime(const Consensus::Params& params) const { return TestTime(20000); }
int64_t ActivationTime(const Consensus::Params& params) const { return ActivationTestTime(this->activation_time); }
int Period(const Consensus::Params& params) const { return 1000; }
int Threshold(const Consensus::Params& params) const { return 900; }
bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const { return (pindex->nVersion & 0x100); }
void SetActivationTime(int64_t n) { this->activation_time = 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); }
Expand All @@ -51,6 +55,13 @@ class VersionBitsTester
public:
VersionBitsTester() : num(0) {}

VersionBitsTester& SetCheckerActivationTestTime(int64_t n) {
for (unsigned int i = 0; i < CHECKERS; i++) {
checker[i].SetActivationTime(n);
}
return *this;
}

VersionBitsTester& Reset() {
for (unsigned int i = 0; i < vpblock.size(); i++) {
delete vpblock[i];
Expand Down Expand Up @@ -109,6 +120,16 @@ class VersionBitsTester
return *this;
}

VersionBitsTester& TestPreLockIn() {
for (int i = 0; i < CHECKERS; i++) {
if ((insecure_rand() & ((1 << i) - 1)) == 0) {
BOOST_CHECK_MESSAGE(checker[i].GetStateFor(vpblock.empty() ? NULL : vpblock.back()) == THRESHOLD_PRE_LOCK_IN, strprintf("Test %i for PRE_LOCK_IN", num));
}
}
num++;
return *this;
}

VersionBitsTester& TestLockedIn() {
for (int i = 0; i < CHECKERS; i++) {
if ((insecure_rand() & ((1 << i) - 1)) == 0) {
Expand Down Expand Up @@ -205,7 +226,35 @@ 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 -> PRE_LOCKED_IN -> LOCKEDIN -> ACTIVE (activation mandatory)
.Reset().SetCheckerActivationTestTime(14999).TestDefined().TestStateSinceHeight(0)
.Mine(1, TestTime(1), 0x200).TestDefined().TestStateSinceHeight(0)
.Mine(1000, TestTime(9999) - 1, 0x200).TestDefined().TestStateSinceHeight(0) // One second more and it would be defined
.Mine(1999, TestTime(10000), 0).TestDefined().TestStateSinceHeight(0)
.Mine(2000, TestTime(11000), 0).TestStarted().TestStateSinceHeight(2000)
.Mine(4000, TestTime(12000), 0).TestStarted().TestStateSinceHeight(2000)
.Mine(5000, TestTime(13000), 0).TestStarted().TestStateSinceHeight(2000)
.Mine(5999, TestTime(14999), 0).TestStarted().TestStateSinceHeight(2000)
.Mine(6000, TestTime(15000), 0).TestPreLockIn().TestStateSinceHeight(6000)
.Mine(6999, TestTime(15999), 0).TestPreLockIn().TestStateSinceHeight(6000)
.Mine(7000, TestTime(16000), 0).TestLockedIn().TestStateSinceHeight(7000)
.Mine(7999, TestTime(17000), 0).TestLockedIn().TestStateSinceHeight(7000)
.Mine(8000, TestTime(17001), 0).TestActive().TestStateSinceHeight(8000) // state change

// DEFINED -> STARTED -> LOCKEDIN -> ACTIVE (activation by signal)
.Reset().SetCheckerActivationTestTime(15000).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).TestStarted().TestStateSinceHeight(2000)
.Mine(2050, TestTime(10010), 0x200).TestStarted().TestStateSinceHeight(2000)
.Mine(2950, TestTime(10020), 0x100).TestStarted().TestStateSinceHeight(2000)
.Mine(2999, TestTime(11999), 0x200).TestStarted().TestStateSinceHeight(2000)
.Mine(3000, TestTime(12000), 0x200).TestLockedIn().TestStateSinceHeight(3000)
.Mine(3999, TestTime(12500), 0).TestLockedIn().TestStateSinceHeight(3000)
.Mine(4000, TestTime(13000), 0).TestActive().TestStateSinceHeight(4000)
;
}

// Sanity checks of version bit deployments
Expand Down
16 changes: 16 additions & 0 deletions src/validation.cpp
Expand Up @@ -1713,6 +1713,7 @@ class WarningBitsConditionChecker : public AbstractThresholdConditionChecker

int64_t BeginTime(const Consensus::Params& params) const { return 0; }
int64_t EndTime(const Consensus::Params& params) const { return std::numeric_limits<int64_t>::max(); }
int64_t ActivationTime(const Consensus::Params& params) const { return std::numeric_limits<int64_t>::max(); }
int Period(const Consensus::Params& params) const { return params.nMinerConfirmationWindow; }
int Threshold(const Consensus::Params& params) const { return params.nRuleChangeActivationThreshold; }

Expand Down Expand Up @@ -2971,6 +2972,21 @@ std::vector<unsigned char> GenerateCoinbaseCommitment(CBlock& block, const CBloc
return commitment;
}

/**
* Return true if nVersion BIP9 deployment is signalling during
* mandatory periods.
*/
bool IsMandatorySignalling(int32_t nVersion, Consensus::DeploymentPos pos, const CBlockIndex* pindexPrev, const Consensus::Params& params)
{
// Check the deployment is in the correct state for this check to apply.
if (!((VersionBitsState(pindexPrev, params, pos, versionbitscache) == THRESHOLD_PRE_LOCK_IN) ||
(VersionBitsState(pindexPrev, params, pos, versionbitscache) == THRESHOLD_LOCKED_IN)))
return true;

// return signalling state
return (((nVersion & VERSIONBITS_TOP_MASK) == VERSIONBITS_TOP_BITS) && (nVersion & VersionBitsMask(params, pos)) != 0);
}

bool ContextualCheckBlockHeader(const CBlockHeader& block, CValidationState& state, const Consensus::Params& consensusParams, const CBlockIndex* pindexPrev, int64_t nAdjustedTime)
{
const int nHeight = pindexPrev == NULL ? 0 : pindexPrev->nHeight + 1;
Expand Down
3 changes: 3 additions & 0 deletions src/validation.h
Expand Up @@ -338,6 +338,9 @@ ThresholdState VersionBitsTipState(const Consensus::Params& params, Consensus::D
/** Get the block height at which the BIP9 deployment switched into the state for the block building on the current tip. */
int VersionBitsTipStateSinceHeight(const Consensus::Params& params, Consensus::DeploymentPos pos);

/** Check if versionbit is required for deployment */
bool IsMandatorySignalling(int32_t nVersion, Consensus::DeploymentPos pos, const CBlockIndex* pindexPrev, const Consensus::Params& params);

/**
* Count ECDSA signature operations the old-fashioned (pre-0.6) way
* @return number of sigops this transaction's outputs will produce when spent
Expand Down
12 changes: 12 additions & 0 deletions src/versionbits.cpp
Expand Up @@ -27,6 +27,7 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex*
int nThreshold = Threshold(params);
int64_t nTimeStart = BeginTime(params);
int64_t nTimeTimeout = EndTime(params);
int64_t nActivationTime = ActivationTime(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 != NULL) {
Expand Down Expand Up @@ -70,6 +71,11 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex*
break;
}
case THRESHOLD_STARTED: {
// Transition to THRESHOLD_PRE_LOCK_IN if mandatory activation is set
if ((nActivationTime != 0) && pindexPrev->GetMedianTimePast() >= nActivationTime) {
stateNext = THRESHOLD_PRE_LOCK_IN;
break;
}
if (pindexPrev->GetMedianTimePast() >= nTimeTimeout) {
stateNext = THRESHOLD_FAILED;
break;
Expand All @@ -88,6 +94,11 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex*
}
break;
}
case THRESHOLD_PRE_LOCK_IN: {
// Always progresses into THRESHOLD_LOCKED_IN.
stateNext = THRESHOLD_LOCKED_IN;
break;
}
case THRESHOLD_LOCKED_IN: {
// Always progresses into ACTIVE.
stateNext = THRESHOLD_ACTIVE;
Expand Down Expand Up @@ -147,6 +158,7 @@ class VersionBitsConditionChecker : public AbstractThresholdConditionChecker {
protected:
int64_t BeginTime(const Consensus::Params& params) const { return params.vDeployments[id].nStartTime; }
int64_t EndTime(const Consensus::Params& params) const { return params.vDeployments[id].nTimeout; }
int64_t ActivationTime(const Consensus::Params& params) const { return params.vDeployments[id].nActivationTime; }
int Period(const Consensus::Params& params) const { return params.nMinerConfirmationWindow; }
int Threshold(const Consensus::Params& params) const { return params.nRuleChangeActivationThreshold; }

Expand Down
2 changes: 2 additions & 0 deletions src/versionbits.h
Expand Up @@ -23,6 +23,7 @@ enum ThresholdState {
THRESHOLD_LOCKED_IN,
THRESHOLD_ACTIVE,
THRESHOLD_FAILED,
THRESHOLD_PRE_LOCK_IN,
};

// A map that gives the state for blocks whose height is a multiple of Period().
Expand All @@ -47,6 +48,7 @@ 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 ActivationTime(const Consensus::Params& params) const =0;
virtual int Period(const Consensus::Params& params) const =0;
virtual int Threshold(const Consensus::Params& params) const =0;

Expand Down