mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-05-15 14:20:38 +02:00
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:
commit
2cd834e6c0
@ -78,16 +78,18 @@ public:
|
|||||||
consensus.nPowTargetSpacing = 10 * 60;
|
consensus.nPowTargetSpacing = 10 * 60;
|
||||||
consensus.fPowAllowMinDifficultyBlocks = false;
|
consensus.fPowAllowMinDifficultyBlocks = false;
|
||||||
consensus.fPowNoRetargeting = false;
|
consensus.fPowNoRetargeting = false;
|
||||||
consensus.nRuleChangeActivationThreshold = 1916; // 95% of 2016
|
consensus.nRuleChangeActivationThreshold = 1815; // 90% of 2016
|
||||||
consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing
|
consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing
|
||||||
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28;
|
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28;
|
||||||
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 1199145601; // January 1, 2008
|
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE;
|
||||||
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 1230767999; // December 31, 2008
|
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
|
||||||
|
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].min_activation_height = 0; // No activation delay
|
||||||
|
|
||||||
// Deployment of Taproot (BIPs 340-342)
|
// Deployment of Taproot (BIPs 340-342)
|
||||||
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].bit = 2;
|
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].bit = 2;
|
||||||
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = 1199145601; // January 1, 2008
|
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE;
|
||||||
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = 1230767999; // December 31, 2008
|
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
|
||||||
|
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 0; // No activation delay
|
||||||
|
|
||||||
consensus.nMinimumChainWork = uint256S("0x00000000000000000000000000000000000000001533efd8d716a517fe2c5008");
|
consensus.nMinimumChainWork = uint256S("0x00000000000000000000000000000000000000001533efd8d716a517fe2c5008");
|
||||||
consensus.defaultAssumeValid = uint256S("0x0000000000000000000b9d2ec5a352ecba0592946514a92f14319dc2b367fc72"); // 654683
|
consensus.defaultAssumeValid = uint256S("0x0000000000000000000b9d2ec5a352ecba0592946514a92f14319dc2b367fc72"); // 654683
|
||||||
@ -198,13 +200,15 @@ public:
|
|||||||
consensus.nRuleChangeActivationThreshold = 1512; // 75% for testchains
|
consensus.nRuleChangeActivationThreshold = 1512; // 75% for testchains
|
||||||
consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing
|
consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing
|
||||||
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28;
|
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28;
|
||||||
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 1199145601; // January 1, 2008
|
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE;
|
||||||
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 1230767999; // December 31, 2008
|
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
|
||||||
|
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].min_activation_height = 0; // No activation delay
|
||||||
|
|
||||||
// Deployment of Taproot (BIPs 340-342)
|
// Deployment of Taproot (BIPs 340-342)
|
||||||
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].bit = 2;
|
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].bit = 2;
|
||||||
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = 1199145601; // January 1, 2008
|
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE;
|
||||||
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = 1230767999; // December 31, 2008
|
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
|
||||||
|
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 0; // No activation delay
|
||||||
|
|
||||||
consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000000001db6ec4ac88cf2272c6");
|
consensus.nMinimumChainWork = uint256S("0x0000000000000000000000000000000000000000000001db6ec4ac88cf2272c6");
|
||||||
consensus.defaultAssumeValid = uint256S("0x000000000000006433d1efec504c53ca332b64963c425395515b01977bd7b3b0"); // 1864000
|
consensus.defaultAssumeValid = uint256S("0x000000000000006433d1efec504c53ca332b64963c425395515b01977bd7b3b0"); // 1864000
|
||||||
@ -328,18 +332,20 @@ public:
|
|||||||
consensus.nPowTargetSpacing = 10 * 60;
|
consensus.nPowTargetSpacing = 10 * 60;
|
||||||
consensus.fPowAllowMinDifficultyBlocks = false;
|
consensus.fPowAllowMinDifficultyBlocks = false;
|
||||||
consensus.fPowNoRetargeting = false;
|
consensus.fPowNoRetargeting = false;
|
||||||
consensus.nRuleChangeActivationThreshold = 1916; // 95% of 2016
|
consensus.nRuleChangeActivationThreshold = 1815; // 90% of 2016
|
||||||
consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing
|
consensus.nMinerConfirmationWindow = 2016; // nPowTargetTimespan / nPowTargetSpacing
|
||||||
consensus.MinBIP9WarningHeight = 0;
|
consensus.MinBIP9WarningHeight = 0;
|
||||||
consensus.powLimit = uint256S("00000377ae000000000000000000000000000000000000000000000000000000");
|
consensus.powLimit = uint256S("00000377ae000000000000000000000000000000000000000000000000000000");
|
||||||
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28;
|
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28;
|
||||||
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 1199145601; // January 1, 2008
|
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE;
|
||||||
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = 1230767999; // December 31, 2008
|
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
|
||||||
|
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].min_activation_height = 0; // No activation delay
|
||||||
|
|
||||||
// Activation of Taproot (BIPs 340-342)
|
// Activation of Taproot (BIPs 340-342)
|
||||||
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].bit = 2;
|
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].bit = 2;
|
||||||
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE;
|
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE;
|
||||||
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
|
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
|
||||||
|
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 0; // No activation delay
|
||||||
|
|
||||||
// message start is defined as the first 4 bytes of the sha256d of the block script
|
// message start is defined as the first 4 bytes of the sha256d of the block script
|
||||||
CHashWriter h(SER_DISK, 0);
|
CHashWriter h(SER_DISK, 0);
|
||||||
@ -398,12 +404,16 @@ public:
|
|||||||
consensus.fPowNoRetargeting = true;
|
consensus.fPowNoRetargeting = true;
|
||||||
consensus.nRuleChangeActivationThreshold = 108; // 75% for testchains
|
consensus.nRuleChangeActivationThreshold = 108; // 75% for testchains
|
||||||
consensus.nMinerConfirmationWindow = 144; // Faster than normal for regtest (144 instead of 2016)
|
consensus.nMinerConfirmationWindow = 144; // Faster than normal for regtest (144 instead of 2016)
|
||||||
|
|
||||||
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28;
|
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit = 28;
|
||||||
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 0;
|
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime = 0;
|
||||||
consensus.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
|
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_TAPROOT].bit = 2;
|
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].bit = 2;
|
||||||
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE;
|
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE;
|
||||||
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
|
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].nTimeout = Consensus::BIP9Deployment::NO_TIMEOUT;
|
||||||
|
consensus.vDeployments[Consensus::DEPLOYMENT_TAPROOT].min_activation_height = 0; // No activation delay
|
||||||
|
|
||||||
consensus.nMinimumChainWork = uint256{};
|
consensus.nMinimumChainWork = uint256{};
|
||||||
consensus.defaultAssumeValid = uint256{};
|
consensus.defaultAssumeValid = uint256{};
|
||||||
@ -467,10 +477,11 @@ public:
|
|||||||
/**
|
/**
|
||||||
* Allows modifying the Version Bits regtest parameters.
|
* 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, int min_activation_height)
|
||||||
{
|
{
|
||||||
consensus.vDeployments[d].nStartTime = nStartTime;
|
consensus.vDeployments[d].nStartTime = nStartTime;
|
||||||
consensus.vDeployments[d].nTimeout = nTimeout;
|
consensus.vDeployments[d].nTimeout = nTimeout;
|
||||||
|
consensus.vDeployments[d].min_activation_height = min_activation_height;
|
||||||
}
|
}
|
||||||
void UpdateActivationParametersFromArgs(const ArgsManager& args);
|
void UpdateActivationParametersFromArgs(const ArgsManager& args);
|
||||||
};
|
};
|
||||||
@ -493,22 +504,26 @@ void CRegTestParams::UpdateActivationParametersFromArgs(const ArgsManager& args)
|
|||||||
for (const std::string& strDeployment : args.GetArgs("-vbparams")) {
|
for (const std::string& strDeployment : args.GetArgs("-vbparams")) {
|
||||||
std::vector<std::string> vDeploymentParams;
|
std::vector<std::string> vDeploymentParams;
|
||||||
boost::split(vDeploymentParams, strDeployment, boost::is_any_of(":"));
|
boost::split(vDeploymentParams, strDeployment, boost::is_any_of(":"));
|
||||||
if (vDeploymentParams.size() != 3) {
|
if (vDeploymentParams.size() < 3 || 4 < vDeploymentParams.size()) {
|
||||||
throw std::runtime_error("Version bits parameters malformed, expecting deployment:start:end");
|
throw std::runtime_error("Version bits parameters malformed, expecting deployment:start:end[:min_activation_height]");
|
||||||
}
|
}
|
||||||
int64_t nStartTime, nTimeout;
|
int64_t nStartTime, nTimeout;
|
||||||
|
int min_activation_height = 0;
|
||||||
if (!ParseInt64(vDeploymentParams[1], &nStartTime)) {
|
if (!ParseInt64(vDeploymentParams[1], &nStartTime)) {
|
||||||
throw std::runtime_error(strprintf("Invalid nStartTime (%s)", vDeploymentParams[1]));
|
throw std::runtime_error(strprintf("Invalid nStartTime (%s)", vDeploymentParams[1]));
|
||||||
}
|
}
|
||||||
if (!ParseInt64(vDeploymentParams[2], &nTimeout)) {
|
if (!ParseInt64(vDeploymentParams[2], &nTimeout)) {
|
||||||
throw std::runtime_error(strprintf("Invalid nTimeout (%s)", vDeploymentParams[2]));
|
throw std::runtime_error(strprintf("Invalid nTimeout (%s)", vDeploymentParams[2]));
|
||||||
}
|
}
|
||||||
|
if (vDeploymentParams.size() >= 4 && !ParseInt32(vDeploymentParams[3], &min_activation_height)) {
|
||||||
|
throw std::runtime_error(strprintf("Invalid min_activation_height (%s)", vDeploymentParams[3]));
|
||||||
|
}
|
||||||
bool found = false;
|
bool found = false;
|
||||||
for (int j=0; j < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++j) {
|
for (int j=0; j < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++j) {
|
||||||
if (vDeploymentParams[0] == VersionBitsDeploymentInfo[j].name) {
|
if (vDeploymentParams[0] == VersionBitsDeploymentInfo[j].name) {
|
||||||
UpdateVersionBitsParameters(Consensus::DeploymentPos(j), nStartTime, nTimeout);
|
UpdateVersionBitsParameters(Consensus::DeploymentPos(j), nStartTime, nTimeout, min_activation_height);
|
||||||
found = true;
|
found = true;
|
||||||
LogPrintf("Setting version bits activation parameters for %s to start=%ld, timeout=%ld\n", vDeploymentParams[0], nStartTime, nTimeout);
|
LogPrintf("Setting version bits activation parameters for %s to start=%ld, timeout=%ld, min_activation_height=%d\n", vDeploymentParams[0], nStartTime, nTimeout, min_activation_height);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,7 +22,7 @@ void SetupChainParamsBaseOptions(ArgsManager& argsman)
|
|||||||
"This is intended for regression testing tools and app development. Equivalent to -chain=regtest.", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
|
"This is intended for regression testing tools and app development. Equivalent to -chain=regtest.", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
|
||||||
argsman.AddArg("-segwitheight=<n>", "Set the activation height of segwit. -1 to disable. (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
|
argsman.AddArg("-segwitheight=<n>", "Set the activation height of segwit. -1 to disable. (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
|
||||||
argsman.AddArg("-testnet", "Use the test chain. Equivalent to -chain=test.", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
|
argsman.AddArg("-testnet", "Use the test chain. Equivalent to -chain=test.", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
|
||||||
argsman.AddArg("-vbparams=deployment:start:end", "Use given start/end times for specified version bits deployment (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
|
argsman.AddArg("-vbparams=deployment:start:end[:min_activation_height]", "Use given start/end times and min_activation_height for specified version bits deployment (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
|
||||||
argsman.AddArg("-signet", "Use the signet chain. Equivalent to -chain=signet. Note that the network is defined by the -signetchallenge parameter", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
|
argsman.AddArg("-signet", "Use the signet chain. Equivalent to -chain=signet. Note that the network is defined by the -signetchallenge parameter", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
|
||||||
argsman.AddArg("-signetchallenge", "Blocks must satisfy the given script to be considered valid (only for signet networks; defaults to the global default signet test network challenge)", ArgsManager::ALLOW_STRING, OptionsCategory::CHAINPARAMS);
|
argsman.AddArg("-signetchallenge", "Blocks must satisfy the given script to be considered valid (only for signet networks; defaults to the global default signet test network challenge)", ArgsManager::ALLOW_STRING, OptionsCategory::CHAINPARAMS);
|
||||||
argsman.AddArg("-signetseednode", "Specify a seed node for the signet network, in the hostname[:port] format, e.g. sig.net:1234 (may be used multiple times to specify multiple seed nodes; defaults to the global default signet test network seed node(s))", ArgsManager::ALLOW_STRING, OptionsCategory::CHAINPARAMS);
|
argsman.AddArg("-signetseednode", "Specify a seed node for the signet network, in the hostname[:port] format, e.g. sig.net:1234 (may be used multiple times to specify multiple seed nodes; defaults to the global default signet test network seed node(s))", ArgsManager::ALLOW_STRING, OptionsCategory::CHAINPARAMS);
|
||||||
|
@ -29,6 +29,11 @@ struct BIP9Deployment {
|
|||||||
int64_t nStartTime;
|
int64_t nStartTime;
|
||||||
/** Timeout/expiry MedianTime for the deployment attempt. */
|
/** Timeout/expiry MedianTime for the deployment attempt. */
|
||||||
int64_t nTimeout;
|
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};
|
||||||
|
|
||||||
/** Constant for nTimeout very far in the future. */
|
/** Constant for nTimeout very far in the future. */
|
||||||
static constexpr int64_t NO_TIMEOUT = std::numeric_limits<int64_t>::max();
|
static constexpr int64_t NO_TIMEOUT = std::numeric_limits<int64_t>::max();
|
||||||
@ -38,6 +43,11 @@ struct BIP9Deployment {
|
|||||||
* process (which takes at least 3 BIP9 intervals). Only tests that specifically test the
|
* process (which takes at least 3 BIP9 intervals). Only tests that specifically test the
|
||||||
* behaviour during activation cannot use this. */
|
* behaviour during activation cannot use this. */
|
||||||
static constexpr int64_t ALWAYS_ACTIVE = -1;
|
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;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1228,10 +1228,8 @@ static void BuriedForkDescPushBack(UniValue& softforks, const std::string &name,
|
|||||||
static void BIP9SoftForkDescPushBack(UniValue& softforks, const std::string &name, const Consensus::Params& consensusParams, Consensus::DeploymentPos id) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
|
static void BIP9SoftForkDescPushBack(UniValue& softforks, const std::string &name, const Consensus::Params& consensusParams, Consensus::DeploymentPos id) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
|
||||||
{
|
{
|
||||||
// For BIP9 deployments.
|
// For BIP9 deployments.
|
||||||
// Deployments (e.g. testdummy) with timeout value before Jan 1, 2009 are hidden.
|
// Deployments that are never active are hidden.
|
||||||
// A timeout value of 0 guarantees a softfork will never be activated.
|
if (consensusParams.vDeployments[id].nStartTime == Consensus::BIP9Deployment::NEVER_ACTIVE) return;
|
||||||
// This is used when merging logic to implement a proposed softfork without a specified deployment schedule.
|
|
||||||
if (consensusParams.vDeployments[id].nTimeout <= 1230768000) return;
|
|
||||||
|
|
||||||
UniValue bip9(UniValue::VOBJ);
|
UniValue bip9(UniValue::VOBJ);
|
||||||
const ThresholdState thresholdState = VersionBitsState(::ChainActive().Tip(), consensusParams, id, versionbitscache);
|
const ThresholdState thresholdState = VersionBitsState(::ChainActive().Tip(), consensusParams, id, versionbitscache);
|
||||||
@ -1261,6 +1259,7 @@ static void BIP9SoftForkDescPushBack(UniValue& softforks, const std::string &nam
|
|||||||
statsUV.pushKV("possible", statsStruct.possible);
|
statsUV.pushKV("possible", statsStruct.possible);
|
||||||
bip9.pushKV("statistics", statsUV);
|
bip9.pushKV("statistics", statsUV);
|
||||||
}
|
}
|
||||||
|
bip9.pushKV("min_activation_height", consensusParams.vDeployments[id].min_activation_height);
|
||||||
|
|
||||||
UniValue rv(UniValue::VOBJ);
|
UniValue rv(UniValue::VOBJ);
|
||||||
rv.pushKV("type", "bip9");
|
rv.pushKV("type", "bip9");
|
||||||
@ -1307,6 +1306,7 @@ RPCHelpMan getblockchaininfo()
|
|||||||
{RPCResult::Type::NUM_TIME, "start_time", "the minimum median time past of a block at which the bit gains its meaning"},
|
{RPCResult::Type::NUM_TIME, "start_time", "the minimum median time past of a block at which the bit gains its meaning"},
|
||||||
{RPCResult::Type::NUM_TIME, "timeout", "the median time past of a block at which the deployment is considered failed if not yet locked in"},
|
{RPCResult::Type::NUM_TIME, "timeout", "the median time past of a block at which the deployment is considered failed if not yet locked in"},
|
||||||
{RPCResult::Type::NUM, "since", "height of the first block to which the status applies"},
|
{RPCResult::Type::NUM, "since", "height of the first block to which the status applies"},
|
||||||
|
{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 (only for \"started\" status)",
|
{RPCResult::Type::OBJ, "statistics", "numeric statistics about BIP9 signalling for a softfork (only for \"started\" status)",
|
||||||
{
|
{
|
||||||
{RPCResult::Type::NUM, "period", "the length in blocks of the BIP9 signalling period"},
|
{RPCResult::Type::NUM, "period", "the length in blocks of the BIP9 signalling period"},
|
||||||
|
@ -29,14 +29,16 @@ public:
|
|||||||
const int64_t m_end;
|
const int64_t m_end;
|
||||||
const int m_period;
|
const int m_period;
|
||||||
const int m_threshold;
|
const int m_threshold;
|
||||||
|
const int m_min_activation_height;
|
||||||
const int m_bit;
|
const int m_bit;
|
||||||
|
|
||||||
TestConditionChecker(int64_t begin, int64_t end, int period, int threshold, int 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_bit{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(m_period > 0);
|
||||||
assert(0 <= m_threshold && m_threshold <= m_period);
|
assert(0 <= m_threshold && m_threshold <= m_period);
|
||||||
assert(0 <= m_bit && m_bit < 32 && m_bit < VERSIONBITS_NUM_BITS);
|
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); }
|
bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const override { return Condition(pindex->nVersion); }
|
||||||
@ -44,6 +46,7 @@ public:
|
|||||||
int64_t EndTime(const Consensus::Params& params) const override { return m_end; }
|
int64_t EndTime(const Consensus::Params& params) const override { return m_end; }
|
||||||
int Period(const Consensus::Params& params) const override { return m_period; }
|
int Period(const Consensus::Params& params) const override { return m_period; }
|
||||||
int Threshold(const Consensus::Params& params) const override { return m_threshold; }
|
int Threshold(const Consensus::Params& params) 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); }
|
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); }
|
int GetStateSinceHeightFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateSinceHeightFor(pindexPrev, dummy_params, m_cache); }
|
||||||
@ -144,32 +147,27 @@ FUZZ_TARGET_INIT(versionbits, initialize)
|
|||||||
// pick the timestamp to switch based on a block
|
// pick the timestamp to switch based on a block
|
||||||
// note states will change *after* these blocks because mediantime lags
|
// note states will change *after* these blocks because mediantime lags
|
||||||
int start_block = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, period * (max_periods - 3));
|
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;
|
start_time = block_start_time + start_block * interval;
|
||||||
timeout = block_start_time + end_block * interval;
|
timeout = block_start_time + end_block * interval;
|
||||||
|
|
||||||
assert(start_time <= timeout);
|
|
||||||
|
|
||||||
// allow for times to not exactly match a block
|
// allow for times to not exactly match a block
|
||||||
if (fuzzed_data_provider.ConsumeBool()) start_time += interval / 2;
|
if (fuzzed_data_provider.ConsumeBool()) start_time += interval / 2;
|
||||||
if (fuzzed_data_provider.ConsumeBool()) timeout += 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 {
|
} else {
|
||||||
if (fuzzed_data_provider.ConsumeBool()) {
|
if (fuzzed_data_provider.ConsumeBool()) {
|
||||||
start_time = Consensus::BIP9Deployment::ALWAYS_ACTIVE;
|
start_time = Consensus::BIP9Deployment::ALWAYS_ACTIVE;
|
||||||
timeout = Consensus::BIP9Deployment::NO_TIMEOUT;
|
|
||||||
always_active_test = true;
|
always_active_test = true;
|
||||||
} else {
|
} else {
|
||||||
start_time = 1199145601; // January 1, 2008
|
start_time = Consensus::BIP9Deployment::NEVER_ACTIVE;
|
||||||
timeout = 1230767999; // December 31, 2008
|
|
||||||
never_active_test = true;
|
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
|
// Early exit if the versions don't signal sensibly for the deployment
|
||||||
if (!checker.Condition(ver_signal)) return;
|
if (!checker.Condition(ver_signal)) return;
|
||||||
@ -294,28 +292,35 @@ FUZZ_TARGET_INIT(versionbits, initialize)
|
|||||||
assert(since == 0);
|
assert(since == 0);
|
||||||
assert(exp_state == ThresholdState::DEFINED);
|
assert(exp_state == ThresholdState::DEFINED);
|
||||||
assert(current_block->GetMedianTimePast() < checker.m_begin);
|
assert(current_block->GetMedianTimePast() < checker.m_begin);
|
||||||
assert(current_block->GetMedianTimePast() < checker.m_end);
|
|
||||||
break;
|
break;
|
||||||
case ThresholdState::STARTED:
|
case ThresholdState::STARTED:
|
||||||
assert(current_block->GetMedianTimePast() >= checker.m_begin);
|
assert(current_block->GetMedianTimePast() >= checker.m_begin);
|
||||||
assert(current_block->GetMedianTimePast() < checker.m_end);
|
|
||||||
if (exp_state == ThresholdState::STARTED) {
|
if (exp_state == ThresholdState::STARTED) {
|
||||||
assert(blocks_sig < threshold);
|
assert(blocks_sig < threshold);
|
||||||
|
assert(current_block->GetMedianTimePast() < checker.m_end);
|
||||||
} else {
|
} else {
|
||||||
assert(exp_state == ThresholdState::DEFINED);
|
assert(exp_state == ThresholdState::DEFINED);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ThresholdState::LOCKED_IN:
|
case ThresholdState::LOCKED_IN:
|
||||||
assert(exp_state == ThresholdState::STARTED);
|
if (exp_state == ThresholdState::LOCKED_IN) {
|
||||||
assert(current_block->GetMedianTimePast() < checker.m_end);
|
assert(current_block->nHeight + 1 < min_activation);
|
||||||
assert(blocks_sig >= threshold);
|
} else {
|
||||||
|
assert(exp_state == ThresholdState::STARTED);
|
||||||
|
assert(blocks_sig >= threshold);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case ThresholdState::ACTIVE:
|
case ThresholdState::ACTIVE:
|
||||||
|
assert(always_active_test || min_activation <= current_block->nHeight + 1);
|
||||||
assert(exp_state == ThresholdState::ACTIVE || exp_state == ThresholdState::LOCKED_IN);
|
assert(exp_state == ThresholdState::ACTIVE || exp_state == ThresholdState::LOCKED_IN);
|
||||||
break;
|
break;
|
||||||
case ThresholdState::FAILED:
|
case ThresholdState::FAILED:
|
||||||
assert(current_block->GetMedianTimePast() >= checker.m_end);
|
assert(never_active_test || current_block->GetMedianTimePast() >= checker.m_end);
|
||||||
assert(exp_state != ThresholdState::LOCKED_IN && exp_state != ThresholdState::ACTIVE);
|
if (exp_state == ThresholdState::STARTED) {
|
||||||
|
assert(blocks_sig < threshold);
|
||||||
|
} else {
|
||||||
|
assert(exp_state == ThresholdState::FAILED);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
assert(false);
|
assert(false);
|
||||||
@ -326,26 +331,20 @@ FUZZ_TARGET_INIT(versionbits, initialize)
|
|||||||
assert(state == ThresholdState::ACTIVE || state == ThresholdState::FAILED);
|
assert(state == ThresholdState::ACTIVE || state == ThresholdState::FAILED);
|
||||||
}
|
}
|
||||||
|
|
||||||
// "always active" has additional restrictions
|
|
||||||
if (always_active_test) {
|
if (always_active_test) {
|
||||||
|
// "always active" has additional restrictions
|
||||||
assert(state == ThresholdState::ACTIVE);
|
assert(state == ThresholdState::ACTIVE);
|
||||||
assert(exp_state == ThresholdState::ACTIVE);
|
assert(exp_state == ThresholdState::ACTIVE);
|
||||||
assert(since == 0);
|
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 {
|
} 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(since > 0 || state == ThresholdState::DEFINED);
|
||||||
assert(exp_since > 0 || exp_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
|
} // namespace
|
||||||
|
@ -44,6 +44,12 @@ public:
|
|||||||
int GetStateSinceHeightFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateSinceHeightFor(pindexPrev, paramsDummy, cache); }
|
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
|
class TestAlwaysActiveConditionChecker : public TestConditionChecker
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -53,8 +59,7 @@ public:
|
|||||||
class TestNeverActiveConditionChecker : public TestConditionChecker
|
class TestNeverActiveConditionChecker : public TestConditionChecker
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
int64_t BeginTime(const Consensus::Params& params) const override { return 0; }
|
int64_t BeginTime(const Consensus::Params& params) const override { return Consensus::BIP9Deployment::NEVER_ACTIVE; }
|
||||||
int64_t EndTime(const Consensus::Params& params) const override { return 1230768000; }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#define CHECKERS 6
|
#define CHECKERS 6
|
||||||
@ -68,6 +73,8 @@ class VersionBitsTester
|
|||||||
// The first one performs all checks, the second only 50%, the third only 25%, etc...
|
// 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.
|
// This is to test whether lack of cached information leads to the same results.
|
||||||
TestConditionChecker checker[CHECKERS];
|
TestConditionChecker checker[CHECKERS];
|
||||||
|
// Another 6 that assume delayed activation
|
||||||
|
TestDelayedActivationConditionChecker checker_delayed[CHECKERS];
|
||||||
// Another 6 that assume always active activation
|
// Another 6 that assume always active activation
|
||||||
TestAlwaysActiveConditionChecker checker_always[CHECKERS];
|
TestAlwaysActiveConditionChecker checker_always[CHECKERS];
|
||||||
// Another 6 that assume never active activation
|
// Another 6 that assume never active activation
|
||||||
@ -77,14 +84,18 @@ class VersionBitsTester
|
|||||||
int num;
|
int num;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
VersionBitsTester() : num(0) {}
|
VersionBitsTester() : num(1000) {}
|
||||||
|
|
||||||
VersionBitsTester& Reset() {
|
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++) {
|
for (unsigned int i = 0; i < vpblock.size(); i++) {
|
||||||
delete vpblock[i];
|
delete vpblock[i];
|
||||||
}
|
}
|
||||||
for (unsigned int i = 0; i < CHECKERS; i++) {
|
for (unsigned int i = 0; i < CHECKERS; i++) {
|
||||||
checker[i] = TestConditionChecker();
|
checker[i] = TestConditionChecker();
|
||||||
|
checker_delayed[i] = TestDelayedActivationConditionChecker();
|
||||||
checker_always[i] = TestAlwaysActiveConditionChecker();
|
checker_always[i] = TestAlwaysActiveConditionChecker();
|
||||||
checker_never[i] = TestNeverActiveConditionChecker();
|
checker_never[i] = TestNeverActiveConditionChecker();
|
||||||
}
|
}
|
||||||
@ -100,7 +111,7 @@ public:
|
|||||||
while (vpblock.size() < height) {
|
while (vpblock.size() < height) {
|
||||||
CBlockIndex* pindex = new CBlockIndex();
|
CBlockIndex* pindex = new CBlockIndex();
|
||||||
pindex->nHeight = vpblock.size();
|
pindex->nHeight = vpblock.size();
|
||||||
pindex->pprev = vpblock.size() > 0 ? vpblock.back() : nullptr;
|
pindex->pprev = Tip();
|
||||||
pindex->nTime = nTime;
|
pindex->nTime = nTime;
|
||||||
pindex->nVersion = nVersion;
|
pindex->nVersion = nVersion;
|
||||||
pindex->BuildSkip();
|
pindex->BuildSkip();
|
||||||
@ -109,34 +120,53 @@ public:
|
|||||||
return *this;
|
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++) {
|
for (int i = 0; i < CHECKERS; i++) {
|
||||||
if (InsecureRandBits(i) == 0) {
|
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[i].GetStateSinceHeightFor(tip) == 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));
|
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));
|
||||||
// never active may go from DEFINED -> FAILED at the first period
|
BOOST_CHECK_MESSAGE(checker_never[i].GetStateSinceHeightFor(tip) == 0, strprintf("Test %i for StateSinceHeight (never active)", num));
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
num++;
|
num++;
|
||||||
return *this;
|
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++) {
|
for (int i = 0; i < CHECKERS; i++) {
|
||||||
if (InsecureRandBits(i) == 0) {
|
if (InsecureRandBits(i) == 0) {
|
||||||
const CBlockIndex* pindex = vpblock.empty() ? nullptr : vpblock.back();
|
|
||||||
ThresholdState got = checker[i].GetStateFor(pindex);
|
ThresholdState got = checker[i].GetStateFor(pindex);
|
||||||
|
ThresholdState got_delayed = checker_delayed[i].GetStateFor(pindex);
|
||||||
ThresholdState got_always = checker_always[i].GetStateFor(pindex);
|
ThresholdState got_always = checker_always[i].GetStateFor(pindex);
|
||||||
ThresholdState got_never = checker_never[i].GetStateFor(pindex);
|
ThresholdState got_never = checker_never[i].GetStateFor(pindex);
|
||||||
// nHeight of the next block. If vpblock is empty, the next (ie first)
|
// nHeight of the next block. If vpblock is empty, the next (ie first)
|
||||||
// block should be the genesis block with nHeight == 0.
|
// block should be the genesis block with nHeight == 0.
|
||||||
int height = pindex == nullptr ? 0 : pindex->nHeight + 1;
|
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 == 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_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++;
|
num++;
|
||||||
@ -149,7 +179,10 @@ public:
|
|||||||
VersionBitsTester& TestActive() { return TestState(ThresholdState::ACTIVE); }
|
VersionBitsTester& TestActive() { return TestState(ThresholdState::ACTIVE); }
|
||||||
VersionBitsTester& TestFailed() { return TestState(ThresholdState::FAILED); }
|
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)
|
BOOST_FIXTURE_TEST_SUITE(versionbits_tests, TestingSetup)
|
||||||
@ -157,18 +190,19 @@ BOOST_FIXTURE_TEST_SUITE(versionbits_tests, TestingSetup)
|
|||||||
BOOST_AUTO_TEST_CASE(versionbits_test)
|
BOOST_AUTO_TEST_CASE(versionbits_test)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 64; i++) {
|
for (int i = 0; i < 64; i++) {
|
||||||
// DEFINED -> FAILED
|
// DEFINED -> STARTED after timeout reached -> FAILED
|
||||||
VersionBitsTester().TestDefined().TestStateSinceHeight(0)
|
VersionBitsTester().TestDefined().TestStateSinceHeight(0)
|
||||||
.Mine(1, TestTime(1), 0x100).TestDefined().TestStateSinceHeight(0)
|
.Mine(1, TestTime(1), 0x100).TestDefined().TestStateSinceHeight(0)
|
||||||
.Mine(11, TestTime(11), 0x100).TestDefined().TestStateSinceHeight(0)
|
.Mine(11, TestTime(11), 0x100).TestDefined().TestStateSinceHeight(0)
|
||||||
.Mine(989, TestTime(989), 0x100).TestDefined().TestStateSinceHeight(0)
|
.Mine(989, TestTime(989), 0x100).TestDefined().TestStateSinceHeight(0)
|
||||||
.Mine(999, TestTime(20000), 0x100).TestDefined().TestStateSinceHeight(0)
|
.Mine(999, TestTime(20000), 0x100).TestDefined().TestStateSinceHeight(0) // Timeout and start time reached simultaneously
|
||||||
.Mine(1000, TestTime(20000), 0x100).TestFailed().TestStateSinceHeight(1000)
|
.Mine(1000, TestTime(20000), 0).TestStarted().TestStateSinceHeight(1000) // Hit started, stop signalling
|
||||||
.Mine(1999, TestTime(30001), 0x100).TestFailed().TestStateSinceHeight(1000)
|
.Mine(1999, TestTime(30001), 0).TestStarted().TestStateSinceHeight(1000)
|
||||||
.Mine(2000, TestTime(30002), 0x100).TestFailed().TestStateSinceHeight(1000)
|
.Mine(2000, TestTime(30002), 0x100).TestFailed().TestStateSinceHeight(2000) // Hit failed, start signalling again
|
||||||
.Mine(2001, TestTime(30003), 0x100).TestFailed().TestStateSinceHeight(1000)
|
.Mine(2001, TestTime(30003), 0x100).TestFailed().TestStateSinceHeight(2000)
|
||||||
.Mine(2999, TestTime(30004), 0x100).TestFailed().TestStateSinceHeight(1000)
|
.Mine(2999, TestTime(30004), 0x100).TestFailed().TestStateSinceHeight(2000)
|
||||||
.Mine(3000, TestTime(30005), 0x100).TestFailed().TestStateSinceHeight(1000)
|
.Mine(3000, TestTime(30005), 0x100).TestFailed().TestStateSinceHeight(2000)
|
||||||
|
.Mine(4000, TestTime(30006), 0x100).TestFailed().TestStateSinceHeight(2000)
|
||||||
|
|
||||||
// DEFINED -> STARTED -> FAILED
|
// DEFINED -> STARTED -> FAILED
|
||||||
.Reset().TestDefined().TestStateSinceHeight(0)
|
.Reset().TestDefined().TestStateSinceHeight(0)
|
||||||
@ -180,19 +214,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(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)
|
.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)
|
.Reset().TestDefined().TestStateSinceHeight(0)
|
||||||
.Mine(1, TestTime(1), 0).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(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(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(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(3000, TestTime(30000), 0x100).TestLockedIn().TestStateSinceHeight(3000) // 1 new block (so 1000 out of the past 1000 are new)
|
||||||
.Mine(3999, TestTime(30001), 0).TestFailed().TestStateSinceHeight(3000)
|
.Mine(3999, TestTime(30001), 0).TestLockedIn().TestStateSinceHeight(3000)
|
||||||
.Mine(4000, TestTime(30002), 0).TestFailed().TestStateSinceHeight(3000)
|
.Mine(4000, TestTime(30002), 0).TestActiveDelayed().TestStateSinceHeight(4000, 3000)
|
||||||
.Mine(14333, TestTime(30003), 0).TestFailed().TestStateSinceHeight(3000)
|
.Mine(14333, TestTime(30003), 0).TestActiveDelayed().TestStateSinceHeight(4000, 3000)
|
||||||
.Mine(24000, TestTime(40000), 0).TestFailed().TestStateSinceHeight(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()
|
.Reset().TestDefined()
|
||||||
.Mine(1, TestTime(1), 0).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(1000, TestTime(10000) - 1, 0x101).TestDefined().TestStateSinceHeight(0) // One second more and it would be defined
|
||||||
@ -202,9 +236,10 @@ BOOST_AUTO_TEST_CASE(versionbits_test)
|
|||||||
.Mine(2999, TestTime(19999), 0x200).TestStarted().TestStateSinceHeight(2000) // 49 old blocks
|
.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(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(3999, TestTime(30001), 0).TestLockedIn().TestStateSinceHeight(3000)
|
||||||
.Mine(4000, TestTime(30002), 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).TestActive().TestStateSinceHeight(4000)
|
.Mine(14333, TestTime(30003), 0).TestActiveDelayed().TestStateSinceHeight(4000, 3000)
|
||||||
.Mine(24000, TestTime(40000), 0).TestActive().TestStateSinceHeight(4000)
|
.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
|
// DEFINED multiple periods -> STARTED multiple periods -> FAILED
|
||||||
.Reset().TestDefined().TestStateSinceHeight(0)
|
.Reset().TestDefined().TestStateSinceHeight(0)
|
||||||
@ -214,10 +249,16 @@ BOOST_AUTO_TEST_CASE(versionbits_test)
|
|||||||
.Mine(3000, TestTime(10000), 0).TestStarted().TestStateSinceHeight(3000)
|
.Mine(3000, TestTime(10000), 0).TestStarted().TestStateSinceHeight(3000)
|
||||||
.Mine(4000, TestTime(10000), 0).TestStarted().TestStateSinceHeight(3000)
|
.Mine(4000, TestTime(10000), 0).TestStarted().TestStateSinceHeight(3000)
|
||||||
.Mine(5000, 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(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
|
// Sanity checks of version bit deployments
|
||||||
const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN);
|
const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN);
|
||||||
const Consensus::Params &mainnetParams = chainParams->GetConsensus();
|
const Consensus::Params &mainnetParams = chainParams->GetConsensus();
|
||||||
@ -226,6 +267,13 @@ BOOST_AUTO_TEST_CASE(versionbits_test)
|
|||||||
// Make sure that no deployment tries to set an invalid bit.
|
// Make sure that no deployment tries to set an invalid bit.
|
||||||
BOOST_CHECK_EQUAL(bitmask & ~(uint32_t)VERSIONBITS_TOP_MASK, bitmask);
|
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
|
// Verify that the deployment windows of different deployment using the
|
||||||
// same bit are disjoint.
|
// same bit are disjoint.
|
||||||
// This test may need modification at such time as a new deployment
|
// This test may need modification at such time as a new deployment
|
||||||
@ -242,81 +290,118 @@ 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
|
// This implicitly uses versionbitscache, so clear it every time
|
||||||
// on mainnet.
|
versionbitscache.Clear();
|
||||||
const auto chainParams = CreateChainParams(*m_node.args, CBaseChainParams::MAIN);
|
|
||||||
const Consensus::Params &mainnetParams = chainParams->GetConsensus();
|
|
||||||
|
|
||||||
// Use the TESTDUMMY deployment for testing purposes.
|
int64_t bit = params.vDeployments[dep].bit;
|
||||||
int64_t bit = mainnetParams.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].bit;
|
int64_t nStartTime = params.vDeployments[dep].nStartTime;
|
||||||
int64_t nStartTime = mainnetParams.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nStartTime;
|
int64_t nTimeout = params.vDeployments[dep].nTimeout;
|
||||||
int64_t nTimeout = mainnetParams.vDeployments[Consensus::DEPLOYMENT_TESTDUMMY].nTimeout;
|
int min_activation_height = params.vDeployments[dep].min_activation_height;
|
||||||
|
|
||||||
assert(nStartTime < nTimeout);
|
// should not be any signalling for first block
|
||||||
|
BOOST_CHECK_EQUAL(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 % params.nMinerConfirmationWindow, 0);
|
||||||
|
|
||||||
// In the first chain, test that the bit is set by CBV until it has failed.
|
// 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
|
// In the second chain, test the bit is set by CBV while STARTED and
|
||||||
// LOCKED-IN, and then no longer set while ACTIVE.
|
// LOCKED-IN, and then no longer set while ACTIVE.
|
||||||
VersionBitsTester firstChain, secondChain;
|
VersionBitsTester firstChain, secondChain;
|
||||||
|
|
||||||
// Start generating blocks before nStartTime
|
int64_t nTime = nStartTime;
|
||||||
int64_t nTime = nStartTime - 1;
|
|
||||||
|
const CBlockIndex *lastBlock = nullptr;
|
||||||
|
|
||||||
// Before MedianTimePast of the chain has crossed nStartTime, the bit
|
// Before MedianTimePast of the chain has crossed nStartTime, the bit
|
||||||
// should not be set.
|
// should not be set.
|
||||||
CBlockIndex *lastBlock = nullptr;
|
if (nTime == 0) {
|
||||||
lastBlock = firstChain.Mine(mainnetParams.nMinerConfirmationWindow, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
|
// since CBlockIndex::nTime is uint32_t we can't represent any
|
||||||
BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit), 0);
|
// 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(params.nMinerConfirmationWindow - 1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
|
||||||
|
BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, params) & (1<<bit), 0);
|
||||||
|
lastBlock = firstChain.Mine(params.nMinerConfirmationWindow, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
|
||||||
|
BOOST_CHECK((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.
|
// Start generating blocks before nStartTime
|
||||||
for (uint32_t i = 1; i < mainnetParams.nMinerConfirmationWindow - 4; i++) {
|
lastBlock = firstChain.Mine(params.nMinerConfirmationWindow, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
|
||||||
lastBlock = firstChain.Mine(mainnetParams.nMinerConfirmationWindow + i, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
|
BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, params) & (1<<bit), 0);
|
||||||
// This works because VERSIONBITS_LAST_OLD_BLOCK_VERSION happens
|
|
||||||
// to be 4, and the bit we're testing happens to be bit 28.
|
// Mine more blocks (4 less than the adjustment period) at the old time, and check that CBV isn't setting the bit yet.
|
||||||
BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit), 0);
|
for (uint32_t i = 1; i < params.nMinerConfirmationWindow - 4; i++) {
|
||||||
}
|
lastBlock = firstChain.Mine(params.nMinerConfirmationWindow + i, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
|
||||||
// Now mine 5 more blocks at the start time -- MTP should not have passed yet, so
|
BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, params) & (1<<bit), 0);
|
||||||
// CBV should still not yet set the bit.
|
}
|
||||||
nTime = nStartTime;
|
// Now mine 5 more blocks at the start time -- MTP should not have passed yet, so
|
||||||
for (uint32_t i = mainnetParams.nMinerConfirmationWindow - 4; i <= mainnetParams.nMinerConfirmationWindow; i++) {
|
// CBV should still not yet set the bit.
|
||||||
lastBlock = firstChain.Mine(mainnetParams.nMinerConfirmationWindow + i, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
|
nTime = nStartTime;
|
||||||
BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit), 0);
|
for (uint32_t i = params.nMinerConfirmationWindow - 4; i <= params.nMinerConfirmationWindow; i++) {
|
||||||
|
lastBlock = firstChain.Mine(params.nMinerConfirmationWindow + i, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
|
||||||
|
BOOST_CHECK_EQUAL(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(params.nMinerConfirmationWindow * 3, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
|
||||||
lastBlock = firstChain.Mine(mainnetParams.nMinerConfirmationWindow * 3, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
|
|
||||||
// so ComputeBlockVersion should now set the bit,
|
// so ComputeBlockVersion should now set the bit,
|
||||||
BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0);
|
BOOST_CHECK((ComputeBlockVersion(lastBlock, params) & (1<<bit)) != 0);
|
||||||
// and should also be using the VERSIONBITS_TOP_BITS.
|
// and should also be using the VERSIONBITS_TOP_BITS.
|
||||||
BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & VERSIONBITS_TOP_MASK, VERSIONBITS_TOP_BITS);
|
BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, params) & VERSIONBITS_TOP_MASK, VERSIONBITS_TOP_BITS);
|
||||||
|
|
||||||
// Check that ComputeBlockVersion will set the bit until nTimeout
|
// Check that ComputeBlockVersion will set the bit until nTimeout
|
||||||
nTime += 600;
|
nTime += 600;
|
||||||
uint32_t blocksToMine = mainnetParams.nMinerConfirmationWindow * 2; // test blocks for up to 2 time periods
|
uint32_t blocksToMine = params.nMinerConfirmationWindow * 2; // test blocks for up to 2 time periods
|
||||||
uint32_t nHeight = mainnetParams.nMinerConfirmationWindow * 3;
|
uint32_t nHeight = params.nMinerConfirmationWindow * 3;
|
||||||
// These blocks are all before nTimeout is reached.
|
// These blocks are all before nTimeout is reached.
|
||||||
while (nTime < nTimeout && blocksToMine > 0) {
|
while (nTime < nTimeout && blocksToMine > 0) {
|
||||||
lastBlock = firstChain.Mine(nHeight+1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
|
lastBlock = firstChain.Mine(nHeight+1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
|
||||||
BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0);
|
BOOST_CHECK((ComputeBlockVersion(lastBlock, params) & (1<<bit)) != 0);
|
||||||
BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & VERSIONBITS_TOP_MASK, VERSIONBITS_TOP_BITS);
|
BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, params) & VERSIONBITS_TOP_MASK, VERSIONBITS_TOP_BITS);
|
||||||
blocksToMine--;
|
blocksToMine--;
|
||||||
nTime += 600;
|
nTime += 600;
|
||||||
nHeight += 1;
|
nHeight += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
nTime = nTimeout;
|
if (nTimeout != Consensus::BIP9Deployment::NO_TIMEOUT) {
|
||||||
// FAILED is only triggered at the end of a period, so CBV should be setting
|
// can reach any nTimeout other than NO_TIMEOUT due to earlier BOOST_REQUIRE
|
||||||
// the bit until the period transition.
|
|
||||||
for (uint32_t i = 0; i < mainnetParams.nMinerConfirmationWindow - 1; i++) {
|
nTime = nTimeout;
|
||||||
|
|
||||||
|
// finish the last period before we start timing out
|
||||||
|
while (nHeight % params.nMinerConfirmationWindow != 0) {
|
||||||
|
lastBlock = firstChain.Mine(nHeight+1, nTime - 1, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
|
||||||
|
BOOST_CHECK((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 < params.nMinerConfirmationWindow - 1; i++) {
|
||||||
|
lastBlock = firstChain.Mine(nHeight+1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
|
||||||
|
BOOST_CHECK((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();
|
lastBlock = firstChain.Mine(nHeight+1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
|
||||||
BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0);
|
BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, 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_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit), 0);
|
|
||||||
|
|
||||||
// On a new chain:
|
// On a new chain:
|
||||||
// verify that the bit will be set after lock-in, and then stop being set
|
// verify that the bit will be set after lock-in, and then stop being set
|
||||||
@ -325,26 +410,62 @@ BOOST_AUTO_TEST_CASE(versionbits_computeblockversion)
|
|||||||
|
|
||||||
// Mine one period worth of blocks, and check that the bit will be on for the
|
// Mine one period worth of blocks, and check that the bit will be on for the
|
||||||
// next period.
|
// next period.
|
||||||
lastBlock = secondChain.Mine(mainnetParams.nMinerConfirmationWindow, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
|
lastBlock = secondChain.Mine(params.nMinerConfirmationWindow, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
|
||||||
BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0);
|
BOOST_CHECK((ComputeBlockVersion(lastBlock, params) & (1<<bit)) != 0);
|
||||||
|
|
||||||
// Mine another period worth of blocks, signaling the new bit.
|
// 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(params.nMinerConfirmationWindow * 2, nTime, VERSIONBITS_TOP_BITS | (1<<bit)).Tip();
|
||||||
// After one period of setting the bit on each block, it should have locked in.
|
// 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.
|
// We keep setting the bit for one more period though, until activation.
|
||||||
BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit)) != 0);
|
BOOST_CHECK((ComputeBlockVersion(lastBlock, params) & (1<<bit)) != 0);
|
||||||
|
|
||||||
// Now check that we keep mining the block until the end of this period, and
|
// Now check that we keep mining the block until the end of this period, and
|
||||||
// then stop at the beginning of the next period.
|
// then stop at the beginning of the next period.
|
||||||
lastBlock = secondChain.Mine((mainnetParams.nMinerConfirmationWindow * 3) - 1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
|
lastBlock = secondChain.Mine((params.nMinerConfirmationWindow * 3) - 1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
|
||||||
BOOST_CHECK((ComputeBlockVersion(lastBlock, mainnetParams) & (1 << bit)) != 0);
|
BOOST_CHECK((ComputeBlockVersion(lastBlock, params) & (1 << bit)) != 0);
|
||||||
lastBlock = secondChain.Mine(mainnetParams.nMinerConfirmationWindow * 3, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
|
lastBlock = secondChain.Mine(params.nMinerConfirmationWindow * 3, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
|
||||||
BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & (1<<bit), 0);
|
|
||||||
|
|
||||||
// Finally, verify that after a soft fork has activated, CBV no longer uses
|
if (lastBlock->nHeight + 1 < min_activation_height) {
|
||||||
// VERSIONBITS_LAST_OLD_BLOCK_VERSION.
|
// check signalling continues while min_activation_height is not reached
|
||||||
//BOOST_CHECK_EQUAL(ComputeBlockVersion(lastBlock, mainnetParams) & VERSIONBITS_TOP_MASK, VERSIONBITS_TOP_BITS);
|
lastBlock = secondChain.Mine(min_activation_height - 1, nTime, VERSIONBITS_LAST_OLD_BLOCK_VERSION).Tip();
|
||||||
|
BOOST_CHECK((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(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::REGTEST}) {
|
||||||
|
const auto chainParams = CreateChainParams(*m_node.args, chain_name);
|
||||||
|
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()
|
BOOST_AUTO_TEST_SUITE_END()
|
||||||
|
@ -9,6 +9,7 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex*
|
|||||||
{
|
{
|
||||||
int nPeriod = Period(params);
|
int nPeriod = Period(params);
|
||||||
int nThreshold = Threshold(params);
|
int nThreshold = Threshold(params);
|
||||||
|
int min_activation_height = MinActivationHeight(params);
|
||||||
int64_t nTimeStart = BeginTime(params);
|
int64_t nTimeStart = BeginTime(params);
|
||||||
int64_t nTimeTimeout = EndTime(params);
|
int64_t nTimeTimeout = EndTime(params);
|
||||||
|
|
||||||
@ -17,6 +18,11 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex*
|
|||||||
return ThresholdState::ACTIVE;
|
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.
|
// 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) {
|
if (pindexPrev != nullptr) {
|
||||||
pindexPrev = pindexPrev->GetAncestor(pindexPrev->nHeight - ((pindexPrev->nHeight + 1) % nPeriod));
|
pindexPrev = pindexPrev->GetAncestor(pindexPrev->nHeight - ((pindexPrev->nHeight + 1) % nPeriod));
|
||||||
@ -51,18 +57,12 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex*
|
|||||||
|
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case ThresholdState::DEFINED: {
|
case ThresholdState::DEFINED: {
|
||||||
if (pindexPrev->GetMedianTimePast() >= nTimeTimeout) {
|
if (pindexPrev->GetMedianTimePast() >= nTimeStart) {
|
||||||
stateNext = ThresholdState::FAILED;
|
|
||||||
} else if (pindexPrev->GetMedianTimePast() >= nTimeStart) {
|
|
||||||
stateNext = ThresholdState::STARTED;
|
stateNext = ThresholdState::STARTED;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ThresholdState::STARTED: {
|
case ThresholdState::STARTED: {
|
||||||
if (pindexPrev->GetMedianTimePast() >= nTimeTimeout) {
|
|
||||||
stateNext = ThresholdState::FAILED;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// We need to count
|
// We need to count
|
||||||
const CBlockIndex* pindexCount = pindexPrev;
|
const CBlockIndex* pindexCount = pindexPrev;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
@ -74,12 +74,16 @@ ThresholdState AbstractThresholdConditionChecker::GetStateFor(const CBlockIndex*
|
|||||||
}
|
}
|
||||||
if (count >= nThreshold) {
|
if (count >= nThreshold) {
|
||||||
stateNext = ThresholdState::LOCKED_IN;
|
stateNext = ThresholdState::LOCKED_IN;
|
||||||
|
} else if (pindexPrev->GetMedianTimePast() >= nTimeTimeout) {
|
||||||
|
stateNext = ThresholdState::FAILED;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ThresholdState::LOCKED_IN: {
|
case ThresholdState::LOCKED_IN: {
|
||||||
// Always progresses into ACTIVE.
|
// Progresses into ACTIVE provided activation height will have been reached.
|
||||||
stateNext = ThresholdState::ACTIVE;
|
if (pindexPrev->nHeight + 1 >= min_activation_height) {
|
||||||
|
stateNext = ThresholdState::ACTIVE;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ThresholdState::FAILED:
|
case ThresholdState::FAILED:
|
||||||
@ -126,7 +130,7 @@ BIP9Stats AbstractThresholdConditionChecker::GetStateStatisticsFor(const CBlockI
|
|||||||
int AbstractThresholdConditionChecker::GetStateSinceHeightFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const
|
int AbstractThresholdConditionChecker::GetStateSinceHeightFor(const CBlockIndex* pindexPrev, const Consensus::Params& params, ThresholdConditionCache& cache) const
|
||||||
{
|
{
|
||||||
int64_t start_time = BeginTime(params);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,6 +174,7 @@ private:
|
|||||||
protected:
|
protected:
|
||||||
int64_t BeginTime(const Consensus::Params& params) const override { return params.vDeployments[id].nStartTime; }
|
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 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.nMinerConfirmationWindow; }
|
int Period(const Consensus::Params& params) const override { return params.nMinerConfirmationWindow; }
|
||||||
int Threshold(const Consensus::Params& params) const override { return params.nRuleChangeActivationThreshold; }
|
int Threshold(const Consensus::Params& params) const override { return params.nRuleChangeActivationThreshold; }
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ static const int32_t VERSIONBITS_NUM_BITS = 29;
|
|||||||
enum class ThresholdState {
|
enum class ThresholdState {
|
||||||
DEFINED, // First state that each softfork starts out as. The genesis block is by definition in this state for each deployment.
|
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.
|
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)
|
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)
|
FAILED, // For all blocks once the first retarget period after the timeout time is hit, if LOCKED_IN wasn't already reached (final state)
|
||||||
};
|
};
|
||||||
@ -57,6 +57,7 @@ protected:
|
|||||||
virtual bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const =0;
|
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 BeginTime(const Consensus::Params& params) const =0;
|
||||||
virtual int64_t EndTime(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 Period(const Consensus::Params& params) const =0;
|
||||||
virtual int Threshold(const Consensus::Params& params) const =0;
|
virtual int Threshold(const Consensus::Params& params) const =0;
|
||||||
|
|
||||||
|
@ -149,6 +149,7 @@ class BlockchainTest(BitcoinTestFramework):
|
|||||||
'count': 57,
|
'count': 57,
|
||||||
'possible': True,
|
'possible': True,
|
||||||
},
|
},
|
||||||
|
'min_activation_height': 0,
|
||||||
},
|
},
|
||||||
'active': False
|
'active': False
|
||||||
},
|
},
|
||||||
@ -158,7 +159,8 @@ class BlockchainTest(BitcoinTestFramework):
|
|||||||
'status': 'active',
|
'status': 'active',
|
||||||
'start_time': -1,
|
'start_time': -1,
|
||||||
'timeout': 9223372036854775807,
|
'timeout': 9223372036854775807,
|
||||||
'since': 0
|
'since': 0,
|
||||||
|
'min_activation_height': 0,
|
||||||
},
|
},
|
||||||
'height': 0,
|
'height': 0,
|
||||||
'active': True
|
'active': True
|
||||||
|
Loading…
x
Reference in New Issue
Block a user