Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 29 additions & 4 deletions src/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ void ReadRegTestArgs(const ArgsManager& args, CChainParams::RegTestOptions& opti

for (const std::string& strDeployment : args.GetArgs("-vbparams")) {
std::vector<std::string> vDeploymentParams = SplitString(strDeployment, ':');
if (vDeploymentParams.size() < 3 || 4 < vDeploymentParams.size()) {
throw std::runtime_error("Version bits parameters malformed, expecting deployment:start:end[:min_activation_height]");
if (vDeploymentParams.size() < 3 || 7 < vDeploymentParams.size()) {
throw std::runtime_error("Version bits parameters malformed, expecting deployment:start:end[:min_activation_height[:max_activation_height[:active_duration[:threshold]]]]");
}
CChainParams::VersionBitsParameters vbparams{};
const auto start_time{ToIntegral<int64_t>(vDeploymentParams[1])};
Expand All @@ -91,13 +91,38 @@ void ReadRegTestArgs(const ArgsManager& args, CChainParams::RegTestOptions& opti
} else {
vbparams.min_activation_height = 0;
}
if (vDeploymentParams.size() >= 5) {
const auto max_activation_height{ToIntegral<int>(vDeploymentParams[4])};
if (!max_activation_height) {
throw std::runtime_error(strprintf("Invalid max_activation_height (%s)", vDeploymentParams[4]));
}
vbparams.max_activation_height = *max_activation_height;
}
if (vDeploymentParams.size() >= 6) {
const auto active_duration{ToIntegral<int>(vDeploymentParams[5])};
if (!active_duration) {
throw std::runtime_error(strprintf("Invalid active_duration (%s)", vDeploymentParams[5]));
}
vbparams.active_duration = *active_duration;
}
if (vDeploymentParams.size() >= 7) {
const auto threshold{ToIntegral<int>(vDeploymentParams[6])};
if (!threshold) {
throw std::runtime_error(strprintf("Invalid threshold (%s)", vDeploymentParams[6]));
}
vbparams.threshold = *threshold;
}
// Validate that timeout and max_activation_height are mutually exclusive
if (vbparams.timeout != Consensus::BIP9Deployment::NO_TIMEOUT && vbparams.max_activation_height < std::numeric_limits<int>::max()) {
throw std::runtime_error(strprintf("Cannot specify both timeout (%ld) and max_activation_height (%d) for deployment %s. Use timeout for BIP9 or max_activation_height for mandatory activation deadline, not both.", vbparams.timeout, vbparams.max_activation_height, vDeploymentParams[0]));
}
bool found = false;
for (int j=0; j < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++j) {
if (vDeploymentParams[0] == VersionBitsDeploymentInfo[j].name) {
options.version_bits_parameters[Consensus::DeploymentPos(j)] = vbparams;
found = true;
LogInfo("Setting version bits activation parameters for %s to start=%ld, timeout=%ld, min_activation_height=%d",
vDeploymentParams[0], vbparams.start_time, vbparams.timeout, vbparams.min_activation_height);
LogInfo("Setting version bits activation parameters for %s to start=%ld, timeout=%ld, min_activation_height=%d, max_activation_height=%d, active_duration=%d, threshold=%d",
vDeploymentParams[0], vbparams.start_time, vbparams.timeout, vbparams.min_activation_height, vbparams.max_activation_height, vbparams.active_duration, vbparams.threshold);
break;
}
}
Expand Down
7 changes: 7 additions & 0 deletions src/consensus/params.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ struct BIP9Deployment {
* boundary.
*/
int min_activation_height{0};
/** Maximum height for activation. If less than INT_MAX, the deployment will activate
* at this height regardless of signaling (similar to BIP8 flag day).
* std::numeric_limits<int>::max() means no maximum (activation only via signaling). */
int max_activation_height{std::numeric_limits<int>::max()};
/** Period of blocks to check signalling in (usually retarget period, ie params.DifficultyAdjustmentInterval()) */
uint32_t period{2016};
/**
Expand All @@ -62,6 +66,9 @@ struct BIP9Deployment {
* Examples: 1916 for 95%, 1512 for testchains.
*/
uint32_t threshold{1916};
/** For temporary softforks: number of blocks the deployment remains active after activation.
* std::numeric_limits<int>::max() means permanent (never expires). */
int active_duration{std::numeric_limits<int>::max()};

/** Constant for nTimeout very far in the future. */
static constexpr int64_t NO_TIMEOUT = std::numeric_limits<int64_t>::max();
Expand Down
1 change: 1 addition & 0 deletions src/deploymentstatus.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,5 @@ inline bool DeploymentEnabled(const Consensus::Params& params, Consensus::Deploy
return params.vDeployments[dep].nStartTime != Consensus::BIP9Deployment::NEVER_ACTIVE;
}


#endif // BITCOIN_DEPLOYMENTSTATUS_H
9 changes: 9 additions & 0 deletions src/kernel/chainparams.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <primitives/transaction.h>
#include <script/interpreter.h>
#include <script/script.h>
#include <tinyformat.h>
#include <uint256.h>
#include <util/chaintype.h>
#include <util/log.h>
Expand All @@ -29,6 +30,7 @@
#include <iterator>
#include <map>
#include <span>
#include <stdexcept>
#include <utility>

using namespace util::hex_literals;
Expand Down Expand Up @@ -629,6 +631,13 @@ class CRegTestParams : public CChainParams
consensus.vDeployments[deployment_pos].nStartTime = version_bits_params.start_time;
consensus.vDeployments[deployment_pos].nTimeout = version_bits_params.timeout;
consensus.vDeployments[deployment_pos].min_activation_height = version_bits_params.min_activation_height;
consensus.vDeployments[deployment_pos].max_activation_height = version_bits_params.max_activation_height;
consensus.vDeployments[deployment_pos].active_duration = version_bits_params.active_duration;
// Validated here rather than in src/chainparams.cpp because period is not yet available at parse time
if (version_bits_params.active_duration != std::numeric_limits<int>::max() && version_bits_params.active_duration % consensus.vDeployments[deployment_pos].period != 0) {
throw std::runtime_error(strprintf("active_duration (%d) must be a multiple of period (%d)", version_bits_params.active_duration, consensus.vDeployments[deployment_pos].period));
}
consensus.vDeployments[deployment_pos].threshold = version_bits_params.threshold;
}

genesis = CreateGenesisBlock(1296688602, 2, 0x207fffff, 1, 50 * COIN);
Expand Down
4 changes: 4 additions & 0 deletions src/kernel/chainparams.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

#include <cstddef>
#include <cstdint>
#include <limits>
#include <memory>
#include <optional>
#include <string>
Expand Down Expand Up @@ -142,6 +143,9 @@ class CChainParams
int64_t start_time;
int64_t timeout;
int min_activation_height;
int max_activation_height{std::numeric_limits<int>::max()};
int active_duration{std::numeric_limits<int>::max()};
int threshold{108}; // regtest default: 75% of 144
};

/**
Expand Down
14 changes: 12 additions & 2 deletions src/rpc/blockchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1322,6 +1322,9 @@ static void SoftForkDescPushBack(const CBlockIndex* blockindex, UniValue& softfo
bip9.pushKV("start_time", depparams.nStartTime);
bip9.pushKV("timeout", depparams.nTimeout);
bip9.pushKV("min_activation_height", depparams.min_activation_height);
if (depparams.max_activation_height < std::numeric_limits<int>::max()) {
bip9.pushKV("max_activation_height", depparams.max_activation_height);
}

// BIP9 status
bip9.pushKV("status", info.current_state);
Expand Down Expand Up @@ -1354,6 +1357,11 @@ static void SoftForkDescPushBack(const CBlockIndex* blockindex, UniValue& softfo
if (info.active_since.has_value()) {
rv.pushKV("height", *info.active_since);
is_active = (*info.active_since <= blockindex->nHeight + 1);
// Add height_end for temporary softforks
const auto& deployment = chainman.GetConsensus().vDeployments[id];
if (deployment.active_duration < std::numeric_limits<int>::max()) {
rv.pushKV("height_end", *info.active_since + deployment.active_duration - 1);
}
}
rv.pushKV("active", is_active);
rv.pushKV("bip9", bip9);
Expand Down Expand Up @@ -1449,15 +1457,17 @@ RPCHelpMan getblockchaininfo()
namespace {
const std::vector<RPCResult> RPCHelpForDeployment{
{RPCResult::Type::STR, "type", "one of \"buried\", \"bip9\""},
{RPCResult::Type::NUM, "height", /*optional=*/true, "height of the first block which the rules are or will be enforced (only for \"buried\" type, or \"bip9\" type with \"active\" status)"},
{RPCResult::Type::NUM, "height", /*optional=*/true, "height of the first block which enforces the rules (only for \"buried\" type, or \"bip9\" type with \"active\" status)"},
{RPCResult::Type::NUM, "height_end", /*optional=*/true, "height of the last block which enforces the rules (only for \"bip9\" type with \"active\" status and temporary deployments)"},
{RPCResult::Type::BOOL, "active", "true if the rules are enforced for the mempool and the next block"},
{RPCResult::Type::OBJ, "bip9", /*optional=*/true, "status of bip9 softforks (only for \"bip9\" type)",
{
{RPCResult::Type::NUM, "bit", /*optional=*/true, "the bit (0-28) in the block version field used to signal this softfork (only for \"started\" and \"locked_in\" status)"},
{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, "min_activation_height", "minimum height of blocks for which the rules may be enforced"},
{RPCResult::Type::STR, "status", "status of deployment at specified block (one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\")"},
{RPCResult::Type::NUM, "max_activation_height", /*optional=*/true, "height at which the deployment will unconditionally activate (absent for miner-vetoable deployments)"},
{RPCResult::Type::STR, "status", "status of deployment at specified block (one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\", \"expired\")"},
{RPCResult::Type::NUM, "since", "height of the first block to which the status applies"},
{RPCResult::Type::STR, "status_next", "status of deployment at the next block"},
{RPCResult::Type::OBJ, "statistics", /*optional=*/true, "numeric statistics about signalling for a softfork (only for \"started\" and \"locked_in\" status)",
Expand Down
2 changes: 1 addition & 1 deletion src/rpc/mining.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -986,7 +986,7 @@ static RPCHelpMan getblocktemplate()
result.pushKV("version", block.nVersion);
result.pushKV("rules", std::move(aRules));
result.pushKV("vbavailable", std::move(vbavailable));
result.pushKV("vbrequired", 0);
result.pushKV("vbrequired", gbtstatus.vbrequired);

result.pushKV("previousblockhash", block.hashPrevBlock.GetHex());
result.pushKV("transactions", std::move(transactions));
Expand Down
13 changes: 12 additions & 1 deletion src/test/fuzz/versionbits.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class TestConditionChecker : public VersionBitsConditionChecker
assert(dep.threshold <= dep.period);
assert(0 <= dep.bit && dep.bit < 32 && dep.bit < VERSIONBITS_NUM_BITS);
assert(0 <= dep.min_activation_height);
assert(dep.active_duration > 0);
}

ThresholdState GetStateFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateFor(pindexPrev, m_cache); }
Expand Down Expand Up @@ -148,6 +149,7 @@ FUZZ_TARGET(versionbits, .init = initialize)
if (fuzzed_data_provider.ConsumeBool()) dep.nTimeout += interval / 2;
}
dep.min_activation_height = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, period * max_periods);
dep.active_duration = fuzzed_data_provider.ConsumeBool() ? std::numeric_limits<int>::max() : (fuzzed_data_provider.ConsumeIntegralInRange<int>(1, max_periods) * period);
return dep;
}()};
TestConditionChecker checker(dep);
Expand Down Expand Up @@ -314,13 +316,22 @@ FUZZ_TARGET(versionbits, .init = initialize)
assert(exp_state == ThresholdState::FAILED);
}
break;
case ThresholdState::EXPIRED:
assert(!always_active_test);
assert(dep.active_duration < std::numeric_limits<int>::max());
assert(dep.min_activation_height <= current_block->nHeight + 1);
assert(exp_state == ThresholdState::EXPIRED || exp_state == ThresholdState::ACTIVE);
if (exp_state == ThresholdState::ACTIVE) {
assert(since == exp_since + dep.active_duration); // EXPIRED starts exactly active_duration blocks after ACTIVE started
}
break;
default:
assert(false);
}

if (blocks.size() >= period * max_periods) {
// we chose the timeout (and block times) so that by the time we have this many blocks it's all over
assert(state == ThresholdState::ACTIVE || state == ThresholdState::FAILED);
assert(state == ThresholdState::ACTIVE || state == ThresholdState::FAILED || state == ThresholdState::EXPIRED);
}

if (always_active_test) {
Expand Down
Loading