Merge #21377: Speedy trial support for versionbits

ffe33dfbd4c3b11e3475b022b6c1dd077613de79 chainparams: drop versionbits threshold to 90% for mainnnet and signet (Anthony Towns)
f054f6bcd2c2ce5fea84cf8681013f85a444e7ea versionbits: simplify state transitions (Anthony Towns)
55ac5f568a3b73d6f1ef4654617fb76e8bcbccdf versionbits: Add explicit NEVER_ACTIVE deployments (Anthony Towns)
dd07e6da48040dc7eae46bc7941db48d98a669fd fuzz: test versionbits delayed activation (Anthony Towns)
dd85d5411c1702c8ae259610fe55050ba212e21e tests: test versionbits delayed activation (Anthony Towns)
73d4a706393e6dbd6b6d6b6428f8d3233ac0a2d8 versionbits: Add support for delayed activation (Anthony Towns)
9e6b65f6fa205eee5c3b99343988adcb8d320460 tests: clean up versionbits test (Anthony Towns)
593274445004506c921d5d851361aefb3434d744 tests: test ComputeBlockVersion for all deployments (Anthony Towns)
63879f0a4760c0c0f784029849cb5d21ee088abb tests: pull ComputeBlockVersion test into its own function (Anthony Towns)

Pull request description:

  BIP9-based implementation of "speedy trial" activation specification, see https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2021-March/018583.html

  Edge cases are tested by fuzzing added in #21380.

ACKs for top commit:
  instagibbs:
    tACK ffe33dfbd4
  jnewbery:
    utACK ffe33dfbd4c3b11e3475b022b6c1dd077613de79
  MarcoFalke:
    review ACK ffe33dfbd4c3b11e3475b022b6c1dd077613de79 💈
  achow101:
    re-ACK ffe33dfbd4c3b11e3475b022b6c1dd077613de79
  gmaxwell:
    ACK ffe33dfbd4c3b11e3475b022b6c1dd077613de79
  benthecarman:
    ACK ffe33dfbd4c3b11e3475b022b6c1dd077613de79
  Sjors:
    ACK ffe33dfbd4c3b11e3475b022b6c1dd077613de79
  jonatack:
    Initial approach ACK ffe33dfbd4c3b11e3475b022b6c1dd077613de79 after a first pass of review, building and testing each commit, mostly looking at the changes and diffs. Will do a more high-level review iteration. A few minor comments follow to pick/choose/ignore.
  ariard:
    Code Review ACK ffe33df

Tree-SHA512: f79a7146b2450057ee92155cbbbcec12cd64334236d9239c6bd7d31b32eec145a9781c320f178da7b44ababdb8808b84d9d22a40e0851e229ba6d224e3be747c
This commit is contained in:
fanquake 2021-04-15 09:33:47 +08:00 committed by Konstantin Akimov
parent ecade9bc39
commit 51911388f2
No known key found for this signature in database
GPG Key ID: 2176C4A5D01EA524
18 changed files with 333 additions and 174 deletions

View File

@ -200,11 +200,12 @@ public:
consensus.fPowNoRetargeting = false;
consensus.nPowKGWHeight = 15200;
consensus.nPowDGWHeight = 34140;
consensus.nRuleChangeActivationThreshold = 1916; // 95% of 2016
consensus.nRuleChangeActivationThreshold = 1815; // 90% of 2016
consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28;
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 1199145601; // January 1, 2008
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 1230767999; // December 31, 2008
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE;
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].min_activation_height = 0; // No activation delay
consensus.vDeployments[Consensus::DEPLOYMENT_V20].bit = 9;
consensus.vDeployments[Consensus::DEPLOYMENT_V20].nStartTime = 1700006400; // November 15, 2023
@ -403,8 +404,9 @@ public:
consensus.nRuleChangeActivationThreshold = 1512; // 75% for testchains
consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28;
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 1199145601; // January 1, 2008
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 1230767999; // December 31, 2008
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE;
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].min_activation_height = 0; // No activation delay
consensus.vDeployments[Consensus::DEPLOYMENT_V20].bit = 9;
consensus.vDeployments[Consensus::DEPLOYMENT_V20].nStartTime = 1693526400; // Friday, September 1, 2023 0:00:00
@ -577,8 +579,9 @@ public:
consensus.nRuleChangeActivationThreshold = 1512; // 75% for testchains
consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28;
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 1199145601; // January 1, 2008
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 1230767999; // December 31, 2008
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE;
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].min_activation_height = 0; // No activation delay
consensus.vDeployments[Consensus::DEPLOYMENT_V20].bit = 9;
consensus.vDeployments[Consensus::DEPLOYMENT_V20].nStartTime = 1661990400; // Sep 1st, 2022
@ -815,9 +818,11 @@ public:
consensus.nPowDGWHeight = 34140; // same as mainnet
consensus.nRuleChangeActivationThreshold = 108; // 75% for testchains
consensus.nMinerConfirmationWindow = 144; // Faster than normal for regtest (144 instead of 2016)
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28;
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 0;
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].min_activation_height = 0; // No activation delay
consensus.vDeployments[Consensus::DEPLOYMENT_V20].bit = 9;
consensus.vDeployments[Consensus::DEPLOYMENT_V20].nStartTime = 0;
@ -939,10 +944,11 @@ public:
/**
* Allows modifying the Version Bits regtest parameters.
*/
void UpdateVersionBitsParameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout, int64_t nWindowSize, int64_t nThresholdStart, int64_t nThresholdMin, int64_t nFalloffCoeff, int64_t nUseEHF)
void UpdateVersionBitsParameters(Consensus::DeploymentPos d, int64_t nStartTime, int64_t nTimeout, int min_activation_height, int64_t nWindowSize, int64_t nThresholdStart, int64_t nThresholdMin, int64_t nFalloffCoeff, int64_t nUseEHF)
{
consensus.vDeployments[d].nStartTime = nStartTime;
consensus.vDeployments[d].nTimeout = nTimeout;
consensus.vDeployments[d].min_activation_height = min_activation_height;
if (nWindowSize != -1) {
consensus.vDeployments[d].nWindowSize = nWindowSize;
}
@ -1022,45 +1028,50 @@ void CRegTestParams::UpdateActivationParametersFromArgs(const ArgsManager& args)
for (const std::string& strDeployment : args.GetArgs("-vbparams")) {
std::vector<std::string> vDeploymentParams = SplitString(strDeployment, ':');
if (vDeploymentParams.size() != 3 && vDeploymentParams.size() != 5 && vDeploymentParams.size() != 8) {
if (vDeploymentParams.size() != 3 && vDeploymentParams.size() != 4 && vDeploymentParams.size() != 6 && vDeploymentParams.size() != 9) {
throw std::runtime_error("Version bits parameters malformed, expecting "
"<deployment>:<start>:<end> or "
"<deployment>:<start>:<end>:<window>:<threshold> or "
"<deployment>:<start>:<end>:<window>:<thresholdstart>:<thresholdmin>:<falloffcoeff>:<useehf>");
"<deployment>:<start>:<end>:<min_activation_height> or "
"<deployment>:<start>:<end>:<min_activation_height>:<window>:<threshold> or "
"<deployment>:<start>:<end>:<min_activation_height>:<window>:<thresholdstart>:<thresholdmin>:<falloffcoeff>:<useehf>");
}
int64_t nStartTime, nTimeout, nWindowSize = -1, nThresholdStart = -1, nThresholdMin = -1, nFalloffCoeff = -1, nUseEHF = -1;
int min_activation_height = 0;
if (!ParseInt64(vDeploymentParams[1], &nStartTime)) {
throw std::runtime_error(strprintf("Invalid nStartTime (%s)", vDeploymentParams[1]));
}
if (!ParseInt64(vDeploymentParams[2], &nTimeout)) {
throw std::runtime_error(strprintf("Invalid nTimeout (%s)", vDeploymentParams[2]));
}
if (vDeploymentParams.size() >= 5) {
if (!ParseInt64(vDeploymentParams[3], &nWindowSize)) {
throw std::runtime_error(strprintf("Invalid nWindowSize (%s)", vDeploymentParams[3]));
if (vDeploymentParams.size() >= 4 && !ParseInt32(vDeploymentParams[3], &min_activation_height)) {
throw std::runtime_error(strprintf("Invalid min_activation_height (%s)", vDeploymentParams[3]));
}
if (vDeploymentParams.size() >= 6) {
if (!ParseInt64(vDeploymentParams[4], &nWindowSize)) {
throw std::runtime_error(strprintf("Invalid nWindowSize (%s)", vDeploymentParams[4]));
}
if (!ParseInt64(vDeploymentParams[4], &nThresholdStart)) {
throw std::runtime_error(strprintf("Invalid nThresholdStart (%s)", vDeploymentParams[4]));
if (!ParseInt64(vDeploymentParams[5], &nThresholdStart)) {
throw std::runtime_error(strprintf("Invalid nThresholdStart (%s)", vDeploymentParams[5]));
}
}
if (vDeploymentParams.size() == 8) {
if (!ParseInt64(vDeploymentParams[5], &nThresholdMin)) {
throw std::runtime_error(strprintf("Invalid nThresholdMin (%s)", vDeploymentParams[5]));
if (vDeploymentParams.size() == 9) {
if (!ParseInt64(vDeploymentParams[6], &nThresholdMin)) {
throw std::runtime_error(strprintf("Invalid nThresholdMin (%s)", vDeploymentParams[6]));
}
if (!ParseInt64(vDeploymentParams[6], &nFalloffCoeff)) {
throw std::runtime_error(strprintf("Invalid nFalloffCoeff (%s)", vDeploymentParams[6]));
if (!ParseInt64(vDeploymentParams[7], &nFalloffCoeff)) {
throw std::runtime_error(strprintf("Invalid nFalloffCoeff (%s)", vDeploymentParams[7]));
}
if (!ParseInt64(vDeploymentParams[7], &nUseEHF)) {
throw std::runtime_error(strprintf("Invalid nUseEHF (%s)", vDeploymentParams[7]));
if (!ParseInt64(vDeploymentParams[8], &nUseEHF)) {
throw std::runtime_error(strprintf("Invalid nUseEHF (%s)", vDeploymentParams[8]));
}
}
bool found = false;
for (int j=0; j < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++j) {
if (vDeploymentParams[0] == VersionBitsDeploymentInfo[j].name) {
UpdateVersionBitsParameters(Consensus::DeploymentPos(j), nStartTime, nTimeout, nWindowSize, nThresholdStart, nThresholdMin, nFalloffCoeff, nUseEHF);
UpdateVersionBitsParameters(Consensus::DeploymentPos(j), nStartTime, nTimeout, min_activation_height, nWindowSize, nThresholdStart, nThresholdMin, nFalloffCoeff, nUseEHF);
found = true;
LogPrintf("Setting version bits activation parameters for %s to start=%ld, timeout=%ld, window=%ld, thresholdstart=%ld, thresholdmin=%ld, falloffcoeff=%ld, useehf=%ld\n",
vDeploymentParams[0], nStartTime, nTimeout, nWindowSize, nThresholdStart, nThresholdMin, nFalloffCoeff, nUseEHF);
LogPrintf("Setting version bits activation parameters for %s to start=%ld, timeout=%ld, min_activation_height=%ld, window=%ld, thresholdstart=%ld, thresholdmin=%ld, falloffcoeff=%ld, useehf=%ld\n",
vDeploymentParams[0], nStartTime, nTimeout, min_activation_height, nWindowSize, nThresholdStart, nThresholdMin, nFalloffCoeff, nUseEHF);
break;
}
}

View File

@ -37,8 +37,8 @@ void SetupChainParamsBaseOptions(ArgsManager& argsman)
argsman.AddArg("-regtest", "Enter regression test mode, which uses a special chain in which blocks can be solved instantly. "
"This is intended for regression testing tools and app development. Equivalent to -chain=regtest", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-testnet", "Use the test chain. Equivalent to -chain=test", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
argsman.AddArg("-vbparams=<deployment>:<start>:<end>(:<window>:<threshold/thresholdstart>(:<thresholdmin>:<falloffcoeff>:<mnactivation>))",
"Use given start/end times for specified version bits deployment (regtest-only). "
argsman.AddArg("-vbparams=<deployment>:<start>:<end>(:min_activation_height(:<window>:<threshold/thresholdstart>(:<thresholdmin>:<falloffcoeff>:<mnactivation>)))",
"Use given start/end times and min_activation_height for specified version bits deployment (regtest-only). "
"Specifying window, threshold/thresholdstart, thresholdmin, falloffcoeff and mnactivation is optional.", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
}

View File

@ -51,6 +51,11 @@ struct BIP9Deployment {
int64_t nStartTime;
/** Timeout/expiry MedianTime for the deployment attempt. */
int64_t nTimeout;
/** If lock in occurs, delay activation until at least this block
* height. Note that activation will only occur on a retarget
* boundary.
*/
int min_activation_height{0};
/** The number of past blocks (including the block under consideration) to be taken into account for locking in a fork. */
int64_t nWindowSize{0};
/** A starting number of blocks, in the range of 1..nWindowSize, which must signal for a fork in order to lock it in. */
@ -73,6 +78,11 @@ struct BIP9Deployment {
* 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;
/** Special value for nStartTime indicating that the deployment is never active.
* This is useful for integrating the code changes for a new feature
* prior to deploying it on some or all networks. */
static constexpr int64_t NEVER_ACTIVE = -2;
};
/**

View File

@ -55,6 +55,7 @@ CMNHFManager::~CMNHFManager()
CMNHFManager::Signals CMNHFManager::GetSignalsStage(const CBlockIndex* const pindexPrev)
{
Signals signals = GetForBlock(pindexPrev);
if (pindexPrev == nullptr) return {};
const int height = pindexPrev->nHeight + 1;
for (auto it = signals.begin(); it != signals.end(); ) {
bool found{false};

View File

@ -1636,10 +1636,8 @@ static void SoftForkDescPushBack(const CBlockIndex* active_chain_tip, UniValue&
static void SoftForkDescPushBack(const CBlockIndex* active_chain_tip, const std::unordered_map<uint8_t, int>& signals, UniValue& softforks, const Consensus::Params& consensusParams, Consensus::DeploymentPos id)
{
// For BIP9 deployments.
// Deployments (e.g. testdummy) with timeout value before Jan 1, 2009 are hidden.
// A timeout value of 0 guarantees a softfork will never be activated.
// This is used when merging logic to implement a proposed softfork without a specified deployment schedule.
if (consensusParams.vDeployments[id].nTimeout <= 1230768000) return;
// Deployments that are never active are hidden.
if (consensusParams.vDeployments[id].nStartTime == Consensus::BIP9Deployment::NEVER_ACTIVE) return;
UniValue bip9(UniValue::VOBJ);
const ThresholdState thresholdState = g_versionbitscache.State(active_chain_tip, consensusParams, id);
@ -1676,6 +1674,7 @@ static void SoftForkDescPushBack(const CBlockIndex* active_chain_tip, const std:
else if (ThresholdState::LOCKED_IN == thresholdState) {
bip9.pushKV("activation_height", since_height + static_cast<int>(consensusParams.vDeployments[id].nWindowSize));
}
bip9.pushKV("min_activation_height", consensusParams.vDeployments[id].min_activation_height);
UniValue rv(UniValue::VOBJ);
rv.pushKV("type", "bip9");
@ -1725,6 +1724,7 @@ RPCHelpMan getblockchaininfo()
{RPCResult::Type::NUM, "ehf_height", /* optional */ true, "the minimum height when miner's signals for the deployment matter. Below this height miner signaling cannot trigger hard fork lock-in. Not specified for non-EHF forks"},
{RPCResult::Type::NUM, "since", "height of the first block to which the status applies"},
{RPCResult::Type::NUM, "activation_height", "expected activation height for this softfork (only for \"locked_in\" status)"},
{RPCResult::Type::NUM, "min_activation_height", "minimum height of blocks for which the rules may be enforced"},
{RPCResult::Type::OBJ, "statistics", "numeric statistics about BIP9 signalling for a softfork",
{
{RPCResult::Type::NUM, "period", "the length in blocks of the BIP9 signalling period"},

View File

@ -41,7 +41,7 @@ using SimpleUTXOMap = std::map<COutPoint, std::pair<int, CAmount>>;
struct TestChainBRRBeforeActivationSetup : public TestChainSetup
{
// Force fast DIP3 activation
TestChainBRRBeforeActivationSetup() : TestChainSetup(497, {"-dip3params=30:50", "-vbparams=mn_rr:0:999999999999:20:16:12:5:1"}) {}
TestChainBRRBeforeActivationSetup() : TestChainSetup(497, {"-dip3params=30:50", "-vbparams=mn_rr:0:999999999999:0:20:16:12:5:1"}) {}
};
static SimpleUTXOMap BuildSimpleUtxoMap(const std::vector<CTransactionRef>& txs)

View File

@ -36,7 +36,7 @@ static constexpr int threshold(int attempt)
struct TestChainDATSetup : public TestChainSetup
{
TestChainDATSetup() : TestChainSetup(window - 2, {"-vbparams=testdummy:0:999999999999:100:80:60:5:0"}) {}
TestChainDATSetup() : TestChainSetup(window - 2, {"-vbparams=testdummy:0:999999999999:0:100:80:60:5:0"}) {}
void signal(int num_blocks, bool expected_lockin)
{

View File

@ -30,14 +30,16 @@ public:
const int64_t m_end;
const int m_period;
const int m_threshold;
const int m_min_activation_height;
const int m_bit;
TestConditionChecker(int64_t begin, int64_t end, int period, int threshold, int bit)
: m_begin{begin}, m_end{end}, m_period{period}, m_threshold{threshold}, m_bit{bit}
TestConditionChecker(int64_t begin, int64_t end, int period, int threshold, int min_activation_height, int bit)
: m_begin{begin}, m_end{end}, m_period{period}, m_threshold{threshold}, m_min_activation_height{min_activation_height}, m_bit{bit}
{
assert(m_period > 0);
assert(0 <= m_threshold && m_threshold <= m_period);
assert(0 <= m_bit && m_bit < 32 && m_bit < VERSIONBITS_NUM_BITS);
assert(0 <= m_min_activation_height);
}
bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const override { return Condition(pindex->nVersion); }
@ -46,6 +48,7 @@ public:
int64_t EndTime(const Consensus::Params& params) const override { return m_end; }
int Period(const Consensus::Params& params) const override { return m_period; }
int Threshold(const Consensus::Params& params, int nAttempt) const override { return m_threshold; }
int MinActivationHeight(const Consensus::Params& params) const override { return m_min_activation_height; }
ThresholdState GetStateFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateFor(pindexPrev, dummy_params, m_cache); }
int GetStateSinceHeightFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateSinceHeightFor(pindexPrev, dummy_params, m_cache); }
@ -146,32 +149,27 @@ FUZZ_TARGET_INIT(versionbits, initialize_versionbits)
// pick the timestamp to switch based on a block
// note states will change *after* these blocks because mediantime lags
int start_block = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, period * (max_periods - 3));
int end_block = fuzzed_data_provider.ConsumeIntegralInRange<int>(start_block, period * (max_periods - 3));
int end_block = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, period * (max_periods - 3));
start_time = block_start_time + start_block * interval;
timeout = block_start_time + end_block * interval;
assert(start_time <= timeout);
// allow for times to not exactly match a block
if (fuzzed_data_provider.ConsumeBool()) start_time += interval / 2;
if (fuzzed_data_provider.ConsumeBool()) timeout += interval / 2;
// this may make timeout too early; if so, don't run the test
if (start_time > timeout) return;
} else {
if (fuzzed_data_provider.ConsumeBool()) {
start_time = Consensus::BIP9Deployment::ALWAYS_ACTIVE;
timeout = Consensus::BIP9Deployment::NO_TIMEOUT;
always_active_test = true;
} else {
start_time = 1199145601; // January 1, 2008
timeout = 1230767999; // December 31, 2008
start_time = Consensus::BIP9Deployment::NEVER_ACTIVE;
never_active_test = true;
}
timeout = fuzzed_data_provider.ConsumeBool() ? Consensus::BIP9Deployment::NO_TIMEOUT : fuzzed_data_provider.ConsumeIntegral<int64_t>();
}
int min_activation = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, period * max_periods);
TestConditionChecker checker(start_time, timeout, period, threshold, bit);
TestConditionChecker checker(start_time, timeout, period, threshold, min_activation, bit);
// Early exit if the versions don't signal sensibly for the deployment
if (!checker.Condition(ver_signal)) return;
@ -296,28 +294,35 @@ FUZZ_TARGET_INIT(versionbits, initialize_versionbits)
assert(since == 0);
assert(exp_state == ThresholdState::DEFINED);
assert(current_block->GetMedianTimePast() < checker.m_begin);
assert(current_block->GetMedianTimePast() < checker.m_end);
break;
case ThresholdState::STARTED:
assert(current_block->GetMedianTimePast() >= checker.m_begin);
assert(current_block->GetMedianTimePast() < checker.m_end);
if (exp_state == ThresholdState::STARTED) {
assert(blocks_sig < threshold);
assert(current_block->GetMedianTimePast() < checker.m_end);
} else {
assert(exp_state == ThresholdState::DEFINED);
}
break;
case ThresholdState::LOCKED_IN:
assert(exp_state == ThresholdState::STARTED);
assert(current_block->GetMedianTimePast() < checker.m_end);
assert(blocks_sig >= threshold);
if (exp_state == ThresholdState::LOCKED_IN) {
assert(current_block->nHeight + 1 < min_activation);
} else {
assert(exp_state == ThresholdState::STARTED);
assert(blocks_sig >= threshold);
}
break;
case ThresholdState::ACTIVE:
assert(always_active_test || min_activation <= current_block->nHeight + 1);
assert(exp_state == ThresholdState::ACTIVE || exp_state == ThresholdState::LOCKED_IN);
break;
case ThresholdState::FAILED:
assert(current_block->GetMedianTimePast() >= checker.m_end);
assert(exp_state != ThresholdState::LOCKED_IN && exp_state != ThresholdState::ACTIVE);
assert(never_active_test || current_block->GetMedianTimePast() >= checker.m_end);
if (exp_state == ThresholdState::STARTED) {
assert(blocks_sig < threshold);
} else {
assert(exp_state == ThresholdState::FAILED);
}
break;
default:
assert(false);
@ -328,26 +333,20 @@ FUZZ_TARGET_INIT(versionbits, initialize_versionbits)
assert(state == ThresholdState::ACTIVE || state == ThresholdState::FAILED);
}
// "always active" has additional restrictions
if (always_active_test) {
// "always active" has additional restrictions
assert(state == ThresholdState::ACTIVE);
assert(exp_state == ThresholdState::ACTIVE);
assert(since == 0);
} else if (never_active_test) {
// "never active" does too
assert(state == ThresholdState::FAILED);
assert(exp_state == ThresholdState::FAILED);
assert(since == 0);
} else {
// except for always active, the initial state is always DEFINED
// for signalled deployments, the initial state is always DEFINED
assert(since > 0 || state == ThresholdState::DEFINED);
assert(exp_since > 0 || exp_state == ThresholdState::DEFINED);
}
// "never active" does too
if (never_active_test) {
assert(state == ThresholdState::FAILED);
assert(since == period);
if (exp_since == 0) {
assert(exp_state == ThresholdState::DEFINED);
} else {
assert(exp_state == ThresholdState::FAILED);
}
}
}
} // namespace

View File

@ -46,6 +46,12 @@ public:
int GetStateSinceHeightFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateSinceHeightFor(pindexPrev, paramsDummy, cache); }
};
class TestDelayedActivationConditionChecker : public TestConditionChecker
{
public:
int MinActivationHeight(const Consensus::Params& params) const override { return 15000; }
};
class TestAlwaysActiveConditionChecker : public TestConditionChecker
{
public:
@ -55,8 +61,7 @@ public:
class TestNeverActiveConditionChecker : public TestConditionChecker
{
public:
int64_t BeginTime(const Consensus::Params& params) const override { return 0; }
int64_t EndTime(const Consensus::Params& params) const override { return 1230768000; }
int64_t BeginTime(const Consensus::Params& params) const override { return Consensus::BIP9Deployment::NEVER_ACTIVE; }
};
#define CHECKERS 6
@ -70,6 +75,8 @@ class VersionBitsTester
// 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 delayed activation
TestDelayedActivationConditionChecker checker_delayed[CHECKERS];
// Another 6 that assume always active activation
TestAlwaysActiveConditionChecker checker_always[CHECKERS];
// Another 6 that assume never active activation
@ -79,14 +86,18 @@ class VersionBitsTester
int num;
public:
VersionBitsTester() : num(0) {}
VersionBitsTester() : num(1000) {}
VersionBitsTester& Reset() {
// Have each group of tests be counted by the 1000s part, starting at 1000
num = num - (num % 1000) + 1000;
for (unsigned int i = 0; i < vpblock.size(); i++) {
delete vpblock[i];
}
for (unsigned int i = 0; i < CHECKERS; i++) {
checker[i] = TestConditionChecker();
checker_delayed[i] = TestDelayedActivationConditionChecker();
checker_always[i] = TestAlwaysActiveConditionChecker();
checker_never[i] = TestNeverActiveConditionChecker();
}
@ -102,7 +113,7 @@ public:
while (vpblock.size() < height) {
CBlockIndex* pindex = new CBlockIndex();
pindex->nHeight = vpblock.size();
pindex->pprev = vpblock.size() > 0 ? vpblock.back() : nullptr;
pindex->pprev = Tip();
pindex->nTime = nTime;
pindex->nVersion = nVersion;
pindex->BuildSkip();
@ -111,34 +122,53 @@ public:
return *this;
}
VersionBitsTester& TestStateSinceHeight(int height) {
VersionBitsTester& TestStateSinceHeight(int height)
{
return TestStateSinceHeight(height, height);
}
VersionBitsTester& TestStateSinceHeight(int height, int height_delayed)
{
const CBlockIndex* tip = Tip();
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));
// never active may go from DEFINED -> FAILED at the first period
const auto never_height = checker_never[i].GetStateSinceHeightFor(vpblock.empty() ? nullptr : vpblock.back());
BOOST_CHECK_MESSAGE(never_height == 0 || never_height == checker_never[i].Period(paramsDummy), strprintf("Test %i for StateSinceHeight (never active)", num));
BOOST_CHECK_MESSAGE(checker[i].GetStateSinceHeightFor(tip) == height, strprintf("Test %i for StateSinceHeight", num));
BOOST_CHECK_MESSAGE(checker_delayed[i].GetStateSinceHeightFor(tip) == height_delayed, strprintf("Test %i for StateSinceHeight (delayed)", num));
BOOST_CHECK_MESSAGE(checker_always[i].GetStateSinceHeightFor(tip) == 0, strprintf("Test %i for StateSinceHeight (always active)", num));
BOOST_CHECK_MESSAGE(checker_never[i].GetStateSinceHeightFor(tip) == 0, strprintf("Test %i for StateSinceHeight (never active)", num));
}
}
num++;
return *this;
}
VersionBitsTester& TestState(ThresholdState exp) {
VersionBitsTester& TestState(ThresholdState exp)
{
return TestState(exp, exp);
}
VersionBitsTester& TestState(ThresholdState exp, ThresholdState exp_delayed)
{
if (exp != exp_delayed) {
// only expected differences are that delayed stays in locked_in longer
BOOST_CHECK_EQUAL(exp, ThresholdState::ACTIVE);
BOOST_CHECK_EQUAL(exp_delayed, ThresholdState::LOCKED_IN);
}
const CBlockIndex* pindex = Tip();
for (int i = 0; i < CHECKERS; i++) {
if (InsecureRandBits(i) == 0) {
const CBlockIndex* pindex = vpblock.empty() ? nullptr : vpblock.back();
ThresholdState got = checker[i].GetStateFor(pindex);
ThresholdState got_delayed = checker_delayed[i].GetStateFor(pindex);
ThresholdState got_always = checker_always[i].GetStateFor(pindex);
ThresholdState got_never = checker_never[i].GetStateFor(pindex);
// nHeight of the next block. If vpblock is empty, the next (ie first)
// block should be the genesis block with nHeight == 0.
int height = pindex == nullptr ? 0 : pindex->nHeight + 1;
BOOST_CHECK_MESSAGE(got == exp, strprintf("Test %i for %s height %d (got %s)", num, StateName(exp), height, StateName(got)));
BOOST_CHECK_MESSAGE(got_delayed == exp_delayed, strprintf("Test %i for %s height %d (got %s; delayed case)", num, StateName(exp_delayed), height, StateName(got_delayed)));
BOOST_CHECK_MESSAGE(got_always == ThresholdState::ACTIVE, strprintf("Test %i for ACTIVE height %d (got %s; always active case)", num, height, StateName(got_always)));
BOOST_CHECK_MESSAGE(got_never == ThresholdState::DEFINED|| got_never == ThresholdState::FAILED, strprintf("Test %i for DEFINED/FAILED height %d (got %s; never active case)", num, height, StateName(got_never)));
BOOST_CHECK_MESSAGE(got_never == ThresholdState::FAILED, strprintf("Test %i for FAILED height %d (got %s; never active case)", num, height, StateName(got_never)));
}
}
num++;
@ -151,7 +181,10 @@ public:
VersionBitsTester& TestActive() { return TestState(ThresholdState::ACTIVE); }
VersionBitsTester& TestFailed() { return TestState(ThresholdState::FAILED); }
CBlockIndex * Tip() { return vpblock.size() ? vpblock.back() : nullptr; }
// non-delayed should be active; delayed should still be locked in
VersionBitsTester& TestActiveDelayed() { return TestState(ThresholdState::ACTIVE, ThresholdState::LOCKED_IN); }
CBlockIndex* Tip() { return vpblock.empty() ? nullptr : vpblock.back(); }
};
BOOST_FIXTURE_TEST_SUITE(versionbits_tests, TestingSetup)
@ -159,18 +192,19 @@ BOOST_FIXTURE_TEST_SUITE(versionbits_tests, TestingSetup)
BOOST_AUTO_TEST_CASE(versionbits_test)
{
for (int i = 0; i < 64; i++) {
// DEFINED -> FAILED
// DEFINED -> STARTED after timeout reached -> 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)
.Mine(999, TestTime(20000), 0x100).TestDefined().TestStateSinceHeight(0) // Timeout and start time reached simultaneously
.Mine(1000, TestTime(20000), 0).TestStarted().TestStateSinceHeight(1000) // Hit started, stop signalling
.Mine(1999, TestTime(30001), 0).TestStarted().TestStateSinceHeight(1000)
.Mine(2000, TestTime(30002), 0x100).TestFailed().TestStateSinceHeight(2000) // Hit failed, start signalling again
.Mine(2001, TestTime(30003), 0x100).TestFailed().TestStateSinceHeight(2000)
.Mine(2999, TestTime(30004), 0x100).TestFailed().TestStateSinceHeight(2000)
.Mine(3000, TestTime(30005), 0x100).TestFailed().TestStateSinceHeight(2000)
.Mine(4000, TestTime(30006), 0x100).TestFailed().TestStateSinceHeight(2000)
// DEFINED -> STARTED -> FAILED
.Reset().TestDefined().TestStateSinceHeight(0)
@ -182,19 +216,19 @@ BOOST_AUTO_TEST_CASE(versionbits_test)
.Mine(3000, TestTime(20000), 0).TestFailed().TestStateSinceHeight(3000) // 50 old blocks (so 899 out of the past 1000)
.Mine(4000, TestTime(20010), 0x100).TestFailed().TestStateSinceHeight(3000)
// DEFINED -> STARTED -> FAILED while threshold reached
// DEFINED -> STARTED -> LOCKEDIN after timeout reached -> ACTIVE
.Reset().TestDefined().TestStateSinceHeight(0)
.Mine(1, TestTime(1), 0).TestDefined().TestStateSinceHeight(0)
.Mine(1000, TestTime(10000) - 1, 0x101).TestDefined().TestStateSinceHeight(0) // One second more and it would be defined
.Mine(2000, TestTime(10000), 0x101).TestStarted().TestStateSinceHeight(2000) // So that's what happens the next period
.Mine(2999, TestTime(30000), 0x100).TestStarted().TestStateSinceHeight(2000) // 999 new blocks
.Mine(3000, TestTime(30000), 0x100).TestFailed().TestStateSinceHeight(3000) // 1 new block (so 1000 out of the past 1000 are new)
.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)
.Mine(3000, TestTime(30000), 0x100).TestLockedIn().TestStateSinceHeight(3000) // 1 new block (so 1000 out of the past 1000 are new)
.Mine(3999, TestTime(30001), 0).TestLockedIn().TestStateSinceHeight(3000)
.Mine(4000, TestTime(30002), 0).TestActiveDelayed().TestStateSinceHeight(4000, 3000)
.Mine(14333, TestTime(30003), 0).TestActiveDelayed().TestStateSinceHeight(4000, 3000)
.Mine(24000, TestTime(40000), 0).TestActive().TestStateSinceHeight(4000, 15000)
// DEFINED -> STARTED -> LOCKEDIN at the last minute -> ACTIVE
// DEFINED -> STARTED -> LOCKEDIN before timeout -> ACTIVE
.Reset().TestDefined()
.Mine(1, TestTime(1), 0).TestDefined().TestStateSinceHeight(0)
.Mine(1000, TestTime(10000) - 1, 0x101).TestDefined().TestStateSinceHeight(0) // One second more and it would be defined
@ -204,9 +238,10 @@ BOOST_AUTO_TEST_CASE(versionbits_test)
.Mine(2999, TestTime(19999), 0x200).TestStarted().TestStateSinceHeight(2000) // 49 old blocks
.Mine(3000, TestTime(29999), 0x200).TestLockedIn().TestStateSinceHeight(3000) // 1 old block (so 900 out of the past 1000)
.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)
.Mine(4000, TestTime(30002), 0).TestActiveDelayed().TestStateSinceHeight(4000, 3000) // delayed will not become active until height=15000
.Mine(14333, TestTime(30003), 0).TestActiveDelayed().TestStateSinceHeight(4000, 3000)
.Mine(15000, TestTime(40000), 0).TestActive().TestStateSinceHeight(4000, 15000)
.Mine(24000, TestTime(40000), 0).TestActive().TestStateSinceHeight(4000, 15000)
// DEFINED multiple periods -> STARTED multiple periods -> FAILED
.Reset().TestDefined().TestStateSinceHeight(0)
@ -216,10 +251,16 @@ BOOST_AUTO_TEST_CASE(versionbits_test)
.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(5999, TestTime(20000), 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)
.Mine(24000, TestTime(20000), 0x100).TestFailed().TestStateSinceHeight(6000) // stay in FAILED no matter how much we signal
;
}
}
BOOST_AUTO_TEST_CASE(versionbits_sanity)
{
// Sanity checks of version bit deployments
const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN);
const Consensus::Params &mainnetParams = chainParams->GetConsensus();
@ -228,6 +269,13 @@ BOOST_AUTO_TEST_CASE(versionbits_test)
// Make sure that no deployment tries to set an invalid bit.
BOOST_CHECK_EQUAL(bitmask & ~(uint32_t)VERSIONBITS_TOP_MASK, bitmask);
// Check min_activation_height is on a retarget boundary
BOOST_CHECK_EQUAL(mainnetParams.vDeployments[i].min_activation_height % mainnetParams.nMinerConfirmationWindow, 0);
// Check min_activation_height is 0 for ALWAYS_ACTIVE and never active deployments
if (mainnetParams.vDeployments[i].nStartTime == Consensus::BIP9Deployment::ALWAYS_ACTIVE || mainnetParams.vDeployments[i].nStartTime == Consensus::BIP9Deployment::NEVER_ACTIVE) {
BOOST_CHECK_EQUAL(mainnetParams.vDeployments[i].min_activation_height, 0);
}
// 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
@ -244,81 +292,124 @@ BOOST_AUTO_TEST_CASE(versionbits_test)
}
}
BOOST_AUTO_TEST_CASE(versionbits_computeblockversion)
/** Check that ComputeBlockVersion will set the appropriate bit correctly */
static void check_computeblockversion(const Consensus::Params& params, Consensus::DeploymentPos dep)
{
// Check that ComputeBlockVersion will set the appropriate bit correctly
// on mainnet.
const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN);
const Consensus::Params &mainnetParams = chainParams->GetConsensus();
// This implicitly uses versionbitscache, so clear it every time
g_versionbitscache.Clear();
// 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;
int64_t bit = params.vDeployments[dep].bit;
int64_t nStartTime = params.vDeployments[dep].nStartTime;
int64_t nTimeout = params.vDeployments[dep].nTimeout;
int min_activation_height = params.vDeployments[dep].min_activation_height;
uint32_t window_size = params.vDeployments[dep].nWindowSize;
if (window_size == 0) window_size = params.nMinerConfirmationWindow;
assert(nStartTime < nTimeout);
// TODO: make these unit tests to works with EHF too
bool useEHF = params.vDeployments[dep].useEHF;
if (useEHF) return;
// should not be any signalling for first block
BOOST_CHECK_EQUAL(g_versionbitscache.ComputeBlockVersion(nullptr, params), VERSIONBITS_TOP_BITS);
// always/never active deployments shouldn't need to be tested further
if (nStartTime == Consensus::BIP9Deployment::ALWAYS_ACTIVE) return;
if (nStartTime == Consensus::BIP9Deployment::NEVER_ACTIVE) return;
BOOST_REQUIRE(nStartTime < nTimeout);
BOOST_REQUIRE(nStartTime >= 0);
BOOST_REQUIRE(nTimeout <= std::numeric_limits<uint32_t>::max() || nTimeout == Consensus::BIP9Deployment::NO_TIMEOUT);
BOOST_REQUIRE(0 <= bit && bit < 32);
BOOST_REQUIRE(((1 << bit) & VERSIONBITS_TOP_MASK) == 0);
BOOST_REQUIRE(min_activation_height >= 0);
BOOST_REQUIRE_EQUAL(min_activation_height % window_size, 0);
// 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;
int64_t nTime = nStartTime;
const CBlockIndex *lastBlock = nullptr;
// 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(g_versionbitscache.ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit), 0);
if (nTime == 0) {
// since CBlockIndex::nTime is uint32_t we can't represent any
// earlier time, so will transition from DEFINED to STARTED at the
// end of the first period by mining blocks at nTime == 0
lastBlock = firstChain.Mine(window_size - 1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
BOOST_CHECK_EQUAL(g_versionbitscache.ComputeBlockVersion(lastBlock, params) & (1<<bit), 0);
lastBlock = firstChain.Mine(window_size, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
BOOST_CHECK((g_versionbitscache.ComputeBlockVersion(lastBlock, params) & (1<<bit)) != 0);
// then we'll keep mining at nStartTime...
} else {
// use a time 1s earlier than start time to check we stay DEFINED
--nTime;
// 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(g_versionbitscache.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(g_versionbitscache.ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit), 0);
// Start generating blocks before nStartTime
lastBlock = firstChain.Mine(window_size, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
BOOST_CHECK_EQUAL(g_versionbitscache.ComputeBlockVersion(lastBlock, params) & (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 < window_size - 4; i++) {
lastBlock = firstChain.Mine(window_size + i, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
BOOST_CHECK_EQUAL(g_versionbitscache.ComputeBlockVersion(lastBlock, params) & (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 = window_size - 4; i <= window_size; i++) {
lastBlock = firstChain.Mine(window_size + i, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
BOOST_CHECK_EQUAL(g_versionbitscache.ComputeBlockVersion(lastBlock, params) & (1<<bit), 0);
}
// Next we will advance to the next period and transition to STARTED,
}
// Advance to the next period and transition to STARTED,
lastBlock = firstChain.Mine(mainnetParams.nMinerConfirmationWindow * 3, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
lastBlock = firstChain.Mine(window_size * 3, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
// so ComputeBlockVersion should now set the bit,
BOOST_CHECK((g_versionbitscache.ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0);
BOOST_CHECK((g_versionbitscache.ComputeBlockVersion(lastBlock, params) & (1<<bit)) != 0);
// and should also be using the VERSIONBITS_TOP_BITS.
BOOST_CHECK_EQUAL(g_versionbitscache.ComputeBlockVersion(lastBlock, mainnetParams) & VERSIONBITS_TOP_MASK, VERSIONBITS_TOP_BITS);
BOOST_CHECK_EQUAL(g_versionbitscache.ComputeBlockVersion(lastBlock, params) & 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;
uint32_t blocksToMine = window_size * 2; // test blocks for up to 2 time periods
uint32_t nHeight = window_size * 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((g_versionbitscache.ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0);
BOOST_CHECK_EQUAL(g_versionbitscache.ComputeBlockVersion(lastBlock, mainnetParams) & VERSIONBITS_TOP_MASK, VERSIONBITS_TOP_BITS);
BOOST_CHECK((g_versionbitscache.ComputeBlockVersion(lastBlock, params) & (1<<bit)) != 0);
BOOST_CHECK_EQUAL(g_versionbitscache.ComputeBlockVersion(lastBlock, params) & 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++) {
if (nTimeout != Consensus::BIP9Deployment::NO_TIMEOUT) {
// can reach any nTimeout other than NO_TIMEOUT due to earlier BOOST_REQUIRE
nTime = nTimeout;
// finish the last period before we start timing out
while (nHeight % window_size != 0) {
lastBlock = firstChain.Mine(nHeight+1, nTime - 1, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
BOOST_CHECK((g_versionbitscache.ComputeBlockVersion(lastBlock, params) & (1<<bit)) != 0);
nHeight += 1;
}
// 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 < window_size - 1; i++) {
lastBlock = firstChain.Mine(nHeight+1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
BOOST_CHECK((g_versionbitscache.ComputeBlockVersion(lastBlock, params) & (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((g_versionbitscache.ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0);
nHeight += 1;
BOOST_CHECK_EQUAL(g_versionbitscache.ComputeBlockVersion(lastBlock, params) & (1<<bit), 0);
}
// 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(g_versionbitscache.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
@ -327,26 +418,64 @@ BOOST_AUTO_TEST_CASE(versionbits_computeblockversion)
// 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((g_versionbitscache.ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0);
lastBlock = secondChain.Mine(window_size, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
BOOST_CHECK((g_versionbitscache.ComputeBlockVersion(lastBlock, params) & (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();
lastBlock = secondChain.Mine(window_size * 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((g_versionbitscache.ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0);
BOOST_CHECK((g_versionbitscache.ComputeBlockVersion(lastBlock, params) & (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((g_versionbitscache.ComputeBlockVersion(lastBlock, mainnetParams) & (1 << bit)) != 0);
lastBlock = secondChain.Mine(mainnetParams.nMinerConfirmationWindow * 3, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
BOOST_CHECK_EQUAL(g_versionbitscache.ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit), 0);
lastBlock = secondChain.Mine((window_size * 3) - 1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
BOOST_CHECK((g_versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit)) != 0);
lastBlock = secondChain.Mine(window_size * 3, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
// Finally, verify that after a soft fork has activated, CBV no longer uses
// VERSIONBITS_LAST_OLD_BLOCK_VERSION.
//BOOST_CHECK_EQUAL(g_versionbitscache.ComputeBlockVersion(lastBlock, mainnetParams) & VERSIONBITS_TOP_MASK, VERSIONBITS_TOP_BITS);
if (lastBlock->nHeight + 1 < min_activation_height) {
// check signalling continues while min_activation_height is not reached
lastBlock = secondChain.Mine(min_activation_height - 1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
BOOST_CHECK((g_versionbitscache.ComputeBlockVersion(lastBlock, params) & (1 << bit)) != 0);
// then reach min_activation_height, which was already REQUIRE'd to start a new period
lastBlock = secondChain.Mine(min_activation_height, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
}
// Check that we don't signal after activation
BOOST_CHECK_EQUAL(g_versionbitscache.ComputeBlockVersion(lastBlock, params) & (1<<bit), 0);
}
BOOST_AUTO_TEST_CASE(versionbits_computeblockversion)
{
// check that any deployment on any chain can conceivably reach both
// ACTIVE and FAILED states in roughly the way we expect
for (const auto& chain_name : {CBaseChainParams::MAIN, CBaseChainParams::TESTNET, /*CBaseChainParams::SIGNET,*/ CBaseChainParams::DEVNET, CBaseChainParams::REGTEST}) {
if (chain_name == CBaseChainParams::DEVNET) gArgs.SoftSetBoolArg("-devnet", true);
const auto chainParams = CreateChainParams(*m_node.args, chain_name);
if (chain_name == CBaseChainParams::DEVNET) gArgs.ForceRemoveArg("devnet");
for (int i = 0; i < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++i) {
check_computeblockversion(chainParams->GetConsensus(), static_cast<Consensus::DeploymentPos>(i));
}
}
{
// Use regtest/testdummy to ensure we always exercise some
// deployment that's not always/never active
ArgsManager args;
args.ForceSetArg("-vbparams", "testdummy:1199145601:1230767999"); // January 1, 2008 - December 31, 2008
const auto chainParams = CreateChainParams(args, CBaseChainParams::REGTEST);
check_computeblockversion(chainParams->GetConsensus(), Consensus::DEPLOYMENT_TESTDUMMY);
}
{
// Use regtest/testdummy to ensure we always exercise the
// min_activation_height test, even if we're not using that in a
// live deployment
ArgsManager args;
args.ForceSetArg("-vbparams", "testdummy:1199145601:1230767999:403200"); // January 1, 2008 - December 31, 2008, min act height 403200
const auto chainParams = CreateChainParams(args, CBaseChainParams::REGTEST);
check_computeblockversion(chainParams->GetConsensus(), Consensus::DEPLOYMENT_TESTDUMMY);
}
}
BOOST_AUTO_TEST_SUITE_END()

View File

@ -32,6 +32,7 @@ static int calculateStartHeight(const CBlockIndex* pindexPrev, ThresholdState st
ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const
{
int nPeriod = Period(params);
int min_activation_height = MinActivationHeight(params);
int64_t nTimeStart = BeginTime(params);
int masternodeStartHeight = SignalHeight(pindexPrev, params);
int64_t nTimeTimeout = EndTime(params);
@ -41,6 +42,11 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex*
return ThresholdState::ACTIVE;
}
// Check if this deployment is never active.
if (nTimeStart == Consensus::BIP9Deployment::NEVER_ACTIVE) {
return ThresholdState::FAILED;
}
// 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));
@ -77,19 +83,13 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex*
switch (state) {
case ThresholdState::DEFINED: {
if (pindexPrev->GetMedianTimePast() >= nTimeTimeout) {
stateNext = ThresholdState::FAILED;
} else if (pindexPrev->GetMedianTimePast() >= nTimeStart && pindexPrev->nHeight >= masternodeStartHeight) {
if (pindexPrev->GetMedianTimePast() >= nTimeStart && pindexPrev->nHeight >= masternodeStartHeight) {
stateNext = ThresholdState::STARTED;
nStartHeight = pindexPrev->nHeight + 1;
}
break;
}
case ThresholdState::STARTED: {
if (pindexPrev->GetMedianTimePast() >= nTimeTimeout) {
stateNext = ThresholdState::FAILED;
break;
}
// We need to count
const CBlockIndex* pindexCount = pindexPrev;
int count = 0;
@ -103,12 +103,16 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex*
int nAttempt = (pindexCount->nHeight + 1 - nStartHeight) / nPeriod;
if (count >= Threshold(params, nAttempt)) {
stateNext = ThresholdState::LOCKED_IN;
} else if (pindexPrev->GetMedianTimePast() >= nTimeTimeout) {
stateNext = ThresholdState::FAILED;
}
break;
}
case ThresholdState::LOCKED_IN: {
// Always progresses into ACTIVE.
stateNext = ThresholdState::ACTIVE;
// Progresses into ACTIVE provided activation height will have been reached.
if (pindexPrev->nHeight + 1 >= min_activation_height) {
stateNext = ThresholdState::ACTIVE;
}
break;
}
case ThresholdState::FAILED:
@ -164,7 +168,7 @@ BIP9Stats AbstractThresholdConditionChecker::GetStateStatisticsFor(const CBlockI
int AbstractThresholdConditionChecker::GetStateSinceHeightFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const
{
int64_t start_time = BeginTime(params);
if (start_time == Consensus::BIP9Deployment::ALWAYS_ACTIVE) {
if (start_time == Consensus::BIP9Deployment::ALWAYS_ACTIVE || start_time == Consensus::BIP9Deployment::NEVER_ACTIVE) {
return 0;
}
@ -223,6 +227,7 @@ protected:
return it->second;
}
int64_t EndTime(const Consensus::Params& params) const override { return params.vDeployments[id].nTimeout; }
int MinActivationHeight(const Consensus::Params& params) const override { return params.vDeployments[id].min_activation_height; }
int Period(const Consensus::Params& params) const override { return params.vDeployments[id].nWindowSize ? params.vDeployments[id].nWindowSize : params.nMinerConfirmationWindow; }
int Threshold(const Consensus::Params& params, int nAttempt) const override
{

View File

@ -28,7 +28,7 @@ static const int32_t VERSIONBITS_NUM_BITS = 29;
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.
LOCKED_IN, // For at least one retarget period after the first retarget period with STARTED blocks of which at least threshold have the associated bit set in nVersion, until min_activation_height is reached.
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)
};
@ -61,6 +61,7 @@ protected:
virtual int64_t BeginTime(const Consensus::Params& params) const =0;
virtual int SignalHeight(const CBlockIndex* pindexPrev, const Consensus::Params& params) const = 0;
virtual int64_t EndTime(const Consensus::Params& params) const =0;
virtual int MinActivationHeight(const Consensus::Params& params) const { return 0; }
virtual int Period(const Consensus::Params& params) const =0;
virtual int Threshold(const Consensus::Params& params, int nAttempt) const =0;

View File

@ -60,7 +60,7 @@ class BIP65Test(BitcoinTestFramework):
'-dip3params=9000:9000',
'-par=1', # Use only one script thread to get the exact reject reason for testing
'-acceptnonstdtxn=1', # cltv_invalidate is nonstandard
'-vbparams=v20:0:999999999999:480:384:288:5:0' # Delay v20 for this test as we don't need it
'-vbparams=v20:0:999999999999:0:480:384:288:5:0' # Delay v20 for this test as we don't need it
]]
self.setup_clean_chain = True
self.rpc_timeout = 480

View File

@ -40,7 +40,7 @@ def unDERify(tx):
class BIP66Test(BitcoinTestFramework):
def set_test_params(self):
self.num_nodes = 1
self.extra_args = [['-whitelist=noban@127.0.0.1', '-dip3params=9000:9000', '-par=1', '-vbparams=v20:0:999999999999:480:384:288:5:0']] # Use only one script thread to get the exact reject reason for testing
self.extra_args = [['-whitelist=noban@127.0.0.1', '-dip3params=9000:9000', '-par=1', '-vbparams=v20:0:999999999999:0:480:384:288:5:0']] # Use only one script thread to get the exact reject reason for testing
self.setup_clean_chain = True
self.rpc_timeout = 240

View File

@ -14,7 +14,7 @@ class DashGovernanceTest (DashTestFramework):
def set_test_params(self):
self.v20_start_time = 1417713500
# using adjusted v20 deployment params to test an edge case where superblock maturity window is equal to deployment window size
self.set_dash_test_params(6, 5, [["-budgetparams=10:10:10", f"-vbparams=v20:{self.v20_start_time}:999999999999:10:8:6:5:0"]] * 6, fast_dip3_enforcement=True)
self.set_dash_test_params(6, 5, [["-budgetparams=10:10:10", f"-vbparams=v20:{self.v20_start_time}:999999999999:0:10:8:6:5:0"]] * 6, fast_dip3_enforcement=True)
def prepare_object(self, object_type, parent_hash, creation_time, revision, name, amount, payment_address):
proposal_rev = revision

View File

@ -24,7 +24,7 @@ llmq_type_strings = {llmq_test: 'llmq_test', llmq_test_v17: 'llmq_test_v17'}
class QuorumDataRecoveryTest(DashTestFramework):
def set_test_params(self):
extra_args = [["-vbparams=testdummy:0:999999999999:10:8:6:5:-1"] for _ in range(9)]
extra_args = [["-vbparams=testdummy:0:999999999999:0:10:8:6:5:-1"] for _ in range(9)]
self.set_dash_test_params(9, 7, fast_dip3_enforcement=True, extra_args=extra_args)
self.set_dash_llmq_test_params(4, 3)

View File

@ -25,7 +25,7 @@ from test_framework.util import (
class MnehfTest(DashTestFramework):
def set_test_params(self):
extra_args = [["-vbparams=testdummy:0:999999999999:12:12:12:5:1", "-persistmempool=0"] for _ in range(4)]
extra_args = [["-vbparams=testdummy:0:999999999999:0:12:12:12:5:1", "-persistmempool=0"] for _ in range(4)]
self.set_dash_test_params(4, 3, fast_dip3_enforcement=True, extra_args=extra_args)
def skip_test_if_missing_module(self):
@ -35,7 +35,7 @@ class MnehfTest(DashTestFramework):
for inode in range(self.num_nodes):
self.log.info(f"Restart node {inode} with {self.extra_args[inode]}")
if params is not None:
self.extra_args[inode][0] = f"-vbparams=testdummy:{params[0]}:{params[1]}:12:12:12:5:1"
self.extra_args[inode][0] = f"-vbparams=testdummy:{params[0]}:{params[1]}:0:12:12:12:5:1"
self.log.info(f"Actual restart options: {self.extra_args[inode]}")
self.restart_node(0)

View File

@ -17,7 +17,7 @@ class NewQuorumTypeActivationTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 1
self.extra_args = [["-vbparams=testdummy:0:999999999999:10:8:6:5:0"]]
self.extra_args = [["-vbparams=testdummy:0:999999999999:0:10:8:6:5:0"]]
def run_test(self):
self.log.info(get_bip9_details(self.nodes[0], 'testdummy'))

View File

@ -152,6 +152,7 @@ class BlockchainTest(BitcoinTestFramework):
'start_time': 0,
'timeout': 9223372036854775807,
'since': 0,
'min_activation_height': 0,
'ehf': False,
}, 'active': False},
'mn_rr': {
@ -161,6 +162,7 @@ class BlockchainTest(BitcoinTestFramework):
'start_time': 0,
'timeout': 9223372036854775807,
'since': 0,
'min_activation_height': 0,
'ehf': True,
},
'active': False},
@ -179,6 +181,7 @@ class BlockchainTest(BitcoinTestFramework):
'count': 57,
'possible': True,
},
'min_activation_height': 0,
'ehf': False,
},
'active': False},