Skip to content

Commit f80287c

Browse files
Merge pull request #7 from luke-jr/bip8_lottrue-0.21
Bip8 lottrue 0.21
2 parents e03abd4 + 792361c commit f80287c

16 files changed

+439
-39
lines changed

doc/bips.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
BIPs that are implemented by Bitcoin Core (up-to-date up to **v0.21.0**):
22

3-
* [`BIP 8`](https://github.com/bitcoin/bips/blob/master/bip-0008.mediawiki): The changes for parallel, rapid deployment based on block height miner activation have been implemented since **v0.21.1** ([PR #21392](https://github.com/bitcoin/bitcoin/pull/21392)). The UASF fallback with forced signaling (`LOT=true`) has not yet been implemented. The current implementation is the equivalent of `LOT=false`.
3+
* [`BIP 8`](https://github.com/bitcoin/bips/blob/master/bip-0008.mediawiki): The changes for parallel, rapid deployment based on block height miner activation with UASF failover have been implemented since **v0.21.1** ([PR #19573](https://github.com/bitcoin/bitcoin/pull/19573)).
44
* [`BIP 9`](https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki): The changes allowing multiple soft-forks to be deployed in parallel have been implemented since **v0.12.1** ([PR #7575](https://github.com/bitcoin/bitcoin/pull/7575)) Support was removed in **v0.21.1** ([PR #21392](https://github.com/bitcoin/bitcoin/pull/21392)).
55
* [`BIP 11`](https://github.com/bitcoin/bips/blob/master/bip-0011.mediawiki): Multisig outputs are standard since **v0.6.0** ([PR #669](https://github.com/bitcoin/bitcoin/pull/669)).
66
* [`BIP 13`](https://github.com/bitcoin/bips/blob/master/bip-0013.mediawiki): The address format for P2SH addresses has been implemented since **v0.6.0** ([PR #669](https://github.com/bitcoin/bitcoin/pull/669)).

doc/release-notes-21392.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ Low-level changes
44
RPC
55
---
66

7-
* BIP 9 has been replaced with a partial implementation of BIP 8. This change is reflected in `getblockchaininfo` where references to BIP 9 have been replaced with references to BIP 8.
7+
* BIP 9 has been replaced with BIP 8. This change is reflected in `getblockchaininfo` where references to BIP 9 have been replaced with references to BIP 8.

src/chainparams.cpp

+17-6
Original file line numberDiff line numberDiff line change
@@ -457,11 +457,12 @@ class CRegTestParams : public CChainParams {
457457
/**
458458
* Allows modifying the Version Bits regtest parameters.
459459
*/
460-
void UpdateVersionBitsParameters(Consensus::DeploymentPos d, int startheight, int timeoutheight, int min_activation_height)
460+
void UpdateVersionBitsParameters(Consensus::DeploymentPos d, int startheight, int timeoutheight, int min_activation_height, bool lockinontimeout)
461461
{
462462
consensus.vDeployments[d].startheight = startheight;
463463
consensus.vDeployments[d].timeoutheight = timeoutheight;
464464
consensus.vDeployments[d].m_min_activation_height = min_activation_height;
465+
consensus.vDeployments[d].lockinontimeout = lockinontimeout;
465466
}
466467
void UpdateActivationParametersFromArgs(const ArgsManager& args);
467468
};
@@ -529,8 +530,12 @@ void CRegTestParams::UpdateActivationParametersFromArgs(const ArgsManager& args)
529530
for (const std::string& strDeployment : args.GetArgs("-vbparams")) {
530531
std::vector<std::string> vDeploymentParams;
531532
boost::split(vDeploymentParams, strDeployment, boost::is_any_of(":"));
532-
if (vDeploymentParams.size() < 3 || vDeploymentParams.size() > 4) {
533-
throw std::runtime_error("Version bits parameters malformed, expecting deployment:@startheight:@timeoutheight[:@min_activation_height]");
533+
if (vDeploymentParams.size() == 3 && vDeploymentParams[1].compare(0, 2, "@-") == 0) {
534+
// Don't require lockinontimeout for always/never-active special cases
535+
vDeploymentParams.emplace_back("0");
536+
}
537+
if (vDeploymentParams.size() < 4 || vDeploymentParams.size() > 5) {
538+
throw std::runtime_error("Version bits parameters malformed, expecting deployment:@startheight:@timeoutheight[:@min_activation_height]:lockinontimeout");
534539
}
535540
int32_t startheight = 0, timeoutheight = 0, min_activation_height = 0;
536541
if (vDeploymentParams[1].empty() || vDeploymentParams[1].front() != '@' || !ParseInt32(vDeploymentParams[1].substr(1), &startheight)) {
@@ -539,19 +544,25 @@ void CRegTestParams::UpdateActivationParametersFromArgs(const ArgsManager& args)
539544
if (vDeploymentParams[2].empty() || vDeploymentParams[2].front() != '@' || !ParseInt32(vDeploymentParams[2].substr(1), &timeoutheight)) {
540545
throw std::runtime_error(strprintf("Invalid timeoutheight (%s)", vDeploymentParams[2]));
541546
}
542-
if (vDeploymentParams.size() == 4 && (vDeploymentParams[3].front() != '@' || !ParseInt32(vDeploymentParams[3].substr(1), &min_activation_height))) {
547+
if (vDeploymentParams.size() == 5 && (vDeploymentParams[3].front() != '@' || !ParseInt32(vDeploymentParams[3].substr(1), &min_activation_height))) {
543548
throw std::runtime_error(strprintf("Invalid min_activation_height (%s)", vDeploymentParams[3]));
544549
}
550+
bool lockinontimeout = false;
551+
if (vDeploymentParams.back().size() != 1 || (vDeploymentParams.back().front() != '0' && vDeploymentParams.back().front() != '1')) {
552+
throw std::runtime_error(strprintf("Invalid lockinontimeout (%s)", vDeploymentParams.back()));
553+
} else {
554+
lockinontimeout = (vDeploymentParams.back().front() == '1');
555+
}
545556
std::string error;
546557
if (!CheckVBitsHeights(error, consensus, startheight, timeoutheight, min_activation_height)) {
547558
throw std::runtime_error(error);
548559
}
549560
bool found = false;
550561
for (int j=0; j < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++j) {
551562
if (vDeploymentParams[0] == VersionBitsDeploymentInfo[j].name) {
552-
UpdateVersionBitsParameters(Consensus::DeploymentPos(j), startheight, timeoutheight, min_activation_height);
563+
UpdateVersionBitsParameters(Consensus::DeploymentPos(j), startheight, timeoutheight, min_activation_height, lockinontimeout);
553564
found = true;
554-
LogPrintf("Setting version bits activation parameters for %s to startheight=%ld, timeoutheight=%ld, min_activation_height=%ld\n", vDeploymentParams[0], startheight, timeoutheight, min_activation_height);
565+
LogPrintf("Setting version bits activation parameters for %s to startheight=%ld, timeoutheight=%ld, min_activation_height=%ld, lockinontimeout=%d\n", vDeploymentParams[0], startheight, timeoutheight, min_activation_height, lockinontimeout);
555566
break;
556567
}
557568
}

src/chainparamsbase.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ void SetupChainParamsBaseOptions(ArgsManager& argsman)
2323
"This is intended for regression testing tools and app development. Equivalent to -chain=regtest.", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
2424
argsman.AddArg("-segwitheight=<n>", "Set the activation height of segwit. -1 to disable. (regtest-only)", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::DEBUG_TEST);
2525
argsman.AddArg("-testnet", "Use the test chain. Equivalent to -chain=test.", ArgsManager::ALLOW_ANY, OptionsCategory::CHAINPARAMS);
26-
argsman.AddArg("-vbparams=deployment:@startheight:@timeoutheight[:@min_activation_height]", "Use given start, timeout, and minimum activation heights for specified version bits deployment (regtest-only). For an always active deployment, use @-1:@-1. For a never active deployment, use @-2:@-2.", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
26+
argsman.AddArg("-vbparams=deployment:@startheight:@timeoutheight[:@min_activation_height]:lockinontimeout", "Use given start, timeout, and minimum activation heights and lockinontimeout (0/1) for specified version bits deployment (regtest-only). For an always active deployment, use @-1:@-1. For a never active deployment, use @-2:@-2.", ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::CHAINPARAMS);
2727
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);
2828
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);
2929
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);

src/consensus/params.h

+2
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ struct BIP9Deployment {
3535
* If lock in occurs, delay activation until at least this block height. Activations only occur on retargets.
3636
*/
3737
int m_min_activation_height{0};
38+
/** If true, final period before timeout will transition to MUST_SIGNAL. */
39+
bool lockinontimeout{false};
3840

3941
/** Constant for timeoutheight very far in the future. */
4042
static constexpr int NO_TIMEOUT = std::numeric_limits<int>::max();

src/rpc/blockchain.cpp

+9-6
Original file line numberDiff line numberDiff line change
@@ -1223,25 +1223,27 @@ static void BIP9SoftForkDescPushBack(UniValue& softforks, const std::string &nam
12231223
switch (thresholdState) {
12241224
case ThresholdState::DEFINED: bip9.pushKV("status", "defined"); break;
12251225
case ThresholdState::STARTED: bip9.pushKV("status", "started"); break;
1226+
case ThresholdState::MUST_SIGNAL: bip9.pushKV("status", "must_signal"); break;
12261227
case ThresholdState::LOCKED_IN: bip9.pushKV("status", "locked_in"); break;
12271228
case ThresholdState::ACTIVE: bip9.pushKV("status", "active"); break;
12281229
case ThresholdState::FAILED: bip9.pushKV("status", "failed"); break;
12291230
}
1230-
if (ThresholdState::STARTED == thresholdState)
1231-
{
1231+
if (ThresholdState::STARTED == thresholdState || ThresholdState::MUST_SIGNAL == thresholdState || ThresholdState::LOCKED_IN == thresholdState) {
12321232
bip9.pushKV("bit", consensusParams.vDeployments[id].bit);
12331233
}
12341234
bip9.pushKV("startheight", consensusParams.vDeployments[id].startheight);
12351235
bip9.pushKV("timeoutheight", consensusParams.vDeployments[id].timeoutheight);
12361236
bip9.pushKV("minimum_activation_height", consensusParams.vDeployments[id].m_min_activation_height);
1237+
bip9.pushKV("lockinontimeout", consensusParams.vDeployments[id].lockinontimeout);
12371238
int64_t since_height = VersionBitsTipStateSinceHeight(consensusParams, id);
12381239
bip9.pushKV("since", since_height);
1239-
if (ThresholdState::STARTED == thresholdState)
1240-
{
1240+
if (ThresholdState::STARTED == thresholdState || ThresholdState::MUST_SIGNAL == thresholdState || ThresholdState::LOCKED_IN == thresholdState) {
12411241
UniValue statsUV(UniValue::VOBJ);
12421242
BIP9Stats statsStruct = VersionBitsTipStatistics(consensusParams, id);
12431243
statsUV.pushKV("period", statsStruct.period);
1244-
statsUV.pushKV("threshold", statsStruct.threshold);
1244+
if (thresholdState != ThresholdState::LOCKED_IN) {
1245+
statsUV.pushKV("threshold", statsStruct.threshold);
1246+
}
12451247
statsUV.pushKV("elapsed", statsStruct.elapsed);
12461248
statsUV.pushKV("count", statsStruct.count);
12471249
statsUV.pushKV("possible", statsStruct.possible);
@@ -1288,11 +1290,12 @@ RPCHelpMan getblockchaininfo()
12881290
{RPCResult::Type::STR, "type", "one of \"buried\", \"bip8\""},
12891291
{RPCResult::Type::OBJ, "bip8", "status of BIP 8 softforks (only for \"bip8\" type)",
12901292
{
1291-
{RPCResult::Type::STR, "status", "one of \"defined\", \"started\", \"locked_in\", \"active\", \"failed\""},
1293+
{RPCResult::Type::STR, "status", "one of \"defined\", \"started\", \"must_signal\", \"locked_in\", \"active\", \"failed\""},
12921294
{RPCResult::Type::NUM, "bit", "the bit (0-28) in the block version field used to signal this softfork (only for \"started\" status)"},
12931295
{RPCResult::Type::NUM, "startheight", "the minimum height of a block at which the bit gains its meaning"},
12941296
{RPCResult::Type::NUM, "timeoutheight", "the height of a block at which the deployment is considered failed if not yet locked in"},
12951297
{RPCResult::Type::NUM, "minimum_activation_height", "the minimum block height at which activation is allowed to occur"},
1298+
{RPCResult::Type::BOOL, "lockinontimeout", "true if the period before timeoutheight transitions to must_signal"},
12961299
{RPCResult::Type::NUM, "since", "height of the first block to which the status applies"},
12971300
{RPCResult::Type::OBJ, "statistics", "numeric statistics about BIP8 signalling for a softfork (only for \"started\" status)",
12981301
{

src/rpc/mining.cpp

+6-1
Original file line numberDiff line numberDiff line change
@@ -815,6 +815,7 @@ static RPCHelpMan getblocktemplate()
815815
aRules.push_back("csv");
816816
if (!fPreSegWit) aRules.push_back("!segwit");
817817
UniValue vbavailable(UniValue::VOBJ);
818+
int32_t vbrequired{0};
818819
for (int j = 0; j < (int)Consensus::MAX_VERSION_BITS_DEPLOYMENTS; ++j) {
819820
Consensus::DeploymentPos pos = Consensus::DeploymentPos(j);
820821
ThresholdState state = VersionBitsState(pindexPrev, consensusParams, pos, versionbitscache);
@@ -823,6 +824,10 @@ static RPCHelpMan getblocktemplate()
823824
case ThresholdState::FAILED:
824825
// Not exposed to GBT at all
825826
break;
827+
case ThresholdState::MUST_SIGNAL:
828+
// Bit must be set in block version
829+
vbrequired |= VersionBitsMask(consensusParams, pos);
830+
// FALL THROUGH to set nVersion and get vbavailable set...
826831
case ThresholdState::LOCKED_IN:
827832
// Ensure bit is set in block version
828833
pblock->nVersion |= VersionBitsMask(consensusParams, pos);
@@ -858,7 +863,7 @@ static RPCHelpMan getblocktemplate()
858863
result.pushKV("version", pblock->nVersion);
859864
result.pushKV("rules", aRules);
860865
result.pushKV("vbavailable", vbavailable);
861-
result.pushKV("vbrequired", int(0));
866+
result.pushKV("vbrequired", vbrequired);
862867

863868
if (nMaxVersionPreVB >= 2) {
864869
// If VB is supported by the client, nMaxVersionPreVB is -1, so we won't get here

src/test/fuzz/versionbits.cpp

+24-8
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,10 @@ class TestConditionChecker : public AbstractThresholdConditionChecker
3131
const int m_period;
3232
const int m_threshold;
3333
const int m_bit;
34+
const bool m_lockinontimeout;
3435

35-
TestConditionChecker(int begin, int end, int min_act, int period, int threshold, int bit)
36-
: m_begin{begin}, m_end{end}, m_min_activation(min_act), m_period{period}, m_threshold{threshold}, m_bit{bit}
36+
TestConditionChecker(int begin, int end, int min_act, int period, int threshold, int bit, bool lockinontimeout)
37+
: m_begin{begin}, m_end{end}, m_min_activation(min_act), m_period{period}, m_threshold{threshold}, m_bit{bit}, m_lockinontimeout{lockinontimeout}
3738
{
3839
assert(m_period > 0);
3940
assert(0 <= m_threshold && m_threshold <= m_period);
@@ -43,6 +44,7 @@ class TestConditionChecker : public AbstractThresholdConditionChecker
4344
bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const override { return Condition(pindex->nVersion); }
4445
int StartHeight(const Consensus::Params& params) const override { return m_begin; }
4546
int TimeoutHeight(const Consensus::Params& params) const override { return m_end; }
47+
bool LockinOnTimeout(const Consensus::Params& params) const override { return m_lockinontimeout; }
4648
int MinActivationHeight(const Consensus::Params& params) const override { return m_min_activation; }
4749
int Period(const Consensus::Params& params) const override { return m_period; }
4850
int Threshold(const Consensus::Params& params) const override { return m_threshold; }
@@ -133,11 +135,17 @@ void test_one_input(const std::vector<uint8_t>& buffer)
133135
int startheight;
134136
int timeoutheight;
135137
int min_activation = 0;
138+
bool lockinontimeout = false;
136139
if (fuzzed_data_provider.ConsumeBool()) {
137140
// pick the timestamp to switch based on a block
138141
startheight = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, period * (max_periods - 2));
139142
timeoutheight = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, period * (max_periods - 2));
140143
min_activation = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, period * (max_periods - 1));
144+
if (startheight < int(period * (max_periods - 3)) && threshold < period) {
145+
// LOT=True requires 3 periods (STARTED->MUST_SIGNAL->LOCKED_IN), pushing it past the deadline
146+
// Furthermore, this fuzzer doesn't let us easily guarantee the signal of the first block in a period, so skip LOT=True when threshold is 100%
147+
lockinontimeout = fuzzed_data_provider.ConsumeBool();
148+
}
141149
} else {
142150
if (fuzzed_data_provider.ConsumeBool()) {
143151
startheight = Consensus::BIP9Deployment::ALWAYS_ACTIVE;
@@ -150,7 +158,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
150158
}
151159
}
152160

153-
TestConditionChecker checker(startheight, timeoutheight, min_activation, period, threshold, bit);
161+
TestConditionChecker checker(startheight, timeoutheight, min_activation, period, threshold, bit, lockinontimeout);
154162

155163
// Early exit if the versions don't signal sensibly for the deployment
156164
if (!checker.Condition(ver_signal)) return;
@@ -209,7 +217,11 @@ void test_one_input(const std::vector<uint8_t>& buffer)
209217

210218
// mine (period-1) blocks and check state
211219
for (int b = 1; b < period; ++b) {
212-
const bool signal = (signalling_mask >> (b % 32)) & 1;
220+
bool signal = (signalling_mask >> (b % 32)) & 1;
221+
if (exp_state == ThresholdState::MUST_SIGNAL && threshold - blocks_sig >= period - b) {
222+
// Further blocks need to signal to be valid
223+
signal = true;
224+
}
213225
if (signal) ++blocks_sig;
214226

215227
CBlockIndex* current_block = blocks.mine_block(signal);
@@ -223,8 +235,8 @@ void test_one_input(const std::vector<uint8_t>& buffer)
223235
assert(state == exp_state);
224236
assert(since == exp_since);
225237

226-
// GetStateStatistics may crash when state is not STARTED
227-
if (state != ThresholdState::STARTED) continue;
238+
// GetStateStatistics may crash when state is not STARTED or MUST_SIGNAL
239+
if (state != ThresholdState::STARTED && state != ThresholdState::MUST_SIGNAL) continue;
228240

229241
// check that after mining this block stats change as expected
230242
const BIP9Stats stats = checker.GetStateStatisticsFor(current_block);
@@ -236,7 +248,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
236248
last_stats = stats;
237249
}
238250

239-
if (exp_state == ThresholdState::STARTED) {
251+
if (exp_state == ThresholdState::STARTED || exp_state == ThresholdState::MUST_SIGNAL) {
240252
// double check that stats.possible is sane
241253
if (blocks_sig >= threshold - 1) assert(last_stats.possible);
242254
}
@@ -289,11 +301,15 @@ void test_one_input(const std::vector<uint8_t>& buffer)
289301
assert(exp_state == ThresholdState::DEFINED);
290302
}
291303
break;
304+
case ThresholdState::MUST_SIGNAL:
305+
assert(height >= checker.m_end - period);
306+
assert(exp_state == ThresholdState::STARTED);
307+
break;
292308
case ThresholdState::LOCKED_IN:
293309
if (exp_state == ThresholdState::LOCKED_IN) {
294310
assert(height < checker.m_min_activation);
295311
} else {
296-
assert(exp_state == ThresholdState::STARTED);
312+
assert(exp_state == ThresholdState::STARTED || exp_state == ThresholdState::MUST_SIGNAL);
297313
assert(blocks_sig >= threshold);
298314
}
299315
break;

0 commit comments

Comments
 (0)