versionbits: Limit live activation params and activation warnings per BIP323
What changed, and why it matters
This Bitcoin Core change narrows which version bits trigger live 'unknown soft fork' warnings from 29 bits down to 5, following the new BIP323 specification. It does not change how many bits exist in block headers or how test networks behave; it only reduces false or misleading upgrade alerts for bits that the network no longer treats as active deployment candidates.
No immediate action required. Operators and integrators should be aware that unknown-version warnings are now restricted to BIP323's lower 5 bits, so alerts for higher bits will no longer be emitted. Review BIP323 for expected deployment-bit behavior.
Security signals we found
Consensus-adjacent constant change (versionbits warning scope)
BIP323 compliance change
Reduction of false-positive activation warnings
No change to block-version parsing or consensus rules
Evidence from the diff
The commit redefines VERSIONBITS_NUM_BITS from 29 to 5 in src/versionbits.h, limiting the set of bits that can produce live activation warnings. A new test-only constant VERSIONBITS_MAX_NUM_BITS (29) preserves the original BIP9 range for fuzzing and unit tests. The functional test is updated to use bit 3 instead of bit 27 as the ‘unknown’ bit, and unit tests now assert that real (non-regtest) deployments use bits below 5 while testdummy remains never-active. The change is explicitly described as warning-only.
Changed components
src/versionbits.hsrc/test/fuzz/versionbits.cppsrc/test/versionbits_tests.cppsrc/test/util/versionbits.htest/functional/feature_versionbits_warning.pyInspect captured patch +31 / −6
diff --git a/src/test/fuzz/versionbits.cpp b/src/test/fuzz/versionbits.cpp
index a2085e6a..0ee86e6e 100644
--- a/src/test/fuzz/versionbits.cpp
+++ b/src/test/fuzz/versionbits.cpp
@@ -14,6 +14,7 @@
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/util.h>
+#include <test/util/versionbits.h>
#include <cstdint>
#include <limits>
@@ -31,7 +32,7 @@ public:
{
assert(dep.period > 0);
assert(dep.threshold <= dep.period);
- assert(0 <= dep.bit && dep.bit < 32 && dep.bit < VERSIONBITS_NUM_BITS);
+ assert(0 <= dep.bit && dep.bit < 32 && dep.bit < VERSIONBITS_MAX_NUM_BITS);
assert(0 <= dep.min_activation_height);
}
@@ -126,7 +127,7 @@ FUZZ_TARGET(versionbits, .init = initialize)
assert(0 < dep.threshold && dep.threshold <= dep.period); // must be able to both pass and fail threshold!
// select deployment parameters: bit, start time, timeout
- dep.bit = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, VERSIONBITS_NUM_BITS - 1);
+ dep.bit = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, VERSIONBITS_MAX_NUM_BITS - 1);
if (always_active_test) {
dep.nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE;
diff --git a/src/test/util/versionbits.h b/src/test/util/versionbits.h
new file mode 100644
index 00000000..478b7882
--- /dev/null
+++ b/src/test/util/versionbits.h
@@ -0,0 +1,13 @@
+// Copyright (c) 2026-present The Bitcoin Core developers
+// Distributed under the MIT software license, see the accompanying
+// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+
+#ifndef BITCOIN_TEST_UTIL_VERSIONBITS_H
+#define BITCOIN_TEST_UTIL_VERSIONBITS_H
+
+#include <versionbits.h>
+
+/** Total possible bits available for versionbits per original BIP 9 specification */
+static constexpr int VERSIONBITS_MAX_NUM_BITS{29};
+
+#endif // BITCOIN_TEST_UTIL_VERSIONBITS_H
diff --git a/src/test/versionbits_tests.cpp b/src/test/versionbits_tests.cpp
index ad792053..77384c5f 100644
--- a/src/test/versionbits_tests.cpp
+++ b/src/test/versionbits_tests.cpp
@@ -8,6 +8,7 @@
#include <test/util/random.h>
#include <test/util/common.h>
#include <test/util/setup_common.h>
+#include <test/util/versionbits.h>
#include <util/chaintype.h>
#include <versionbits.h>
#include <versionbits_impl.h>
@@ -454,9 +455,19 @@ BOOST_FIXTURE_TEST_CASE(versionbits_computeblockversion, BlockVersionTest)
// not take precedence over STARTED/LOCKED_IN. So all softforks on
// the same bit might overlap, even when non-overlapping start-end
// times are picked.
- const uint32_t dep_mask{uint32_t{1} << chainParams->GetConsensus().vDeployments[dep].bit};
+ const auto& dep_info = chainParams->GetConsensus().vDeployments[dep];
+ const uint32_t dep_mask{uint32_t{1} << dep_info.bit};
BOOST_CHECK(!(chain_all_vbits & dep_mask));
chain_all_vbits |= dep_mask;
+ BOOST_CHECK(0 <= dep_info.bit && dep_info.bit < VERSIONBITS_MAX_NUM_BITS);
+ if (chain_type != ChainType::REGTEST) {
+ if (dep == Consensus::DEPLOYMENT_TESTDUMMY) {
+ BOOST_CHECK_EQUAL(dep_info.nStartTime, Consensus::BIP9Deployment::NEVER_ACTIVE);
+ BOOST_CHECK_EQUAL(dep_info.nTimeout, Consensus::BIP9Deployment::NO_TIMEOUT);
+ } else {
+ BOOST_CHECK(dep_info.bit < VERSIONBITS_NUM_BITS);
+ }
+ }
check_computeblockversion(vbcache, chainParams->GetConsensus(), dep);
}
}
diff --git a/src/versionbits.h b/src/versionbits.h
index 59b0cbee..0ac17287 100644
--- a/src/versionbits.h
+++ b/src/versionbits.h
@@ -21,8 +21,8 @@ static const int32_t VERSIONBITS_LAST_OLD_BLOCK_VERSION = 4;
static const int32_t VERSIONBITS_TOP_BITS = 0x20000000UL;
/** What bitmask determines whether versionbits is in use */
static const int32_t VERSIONBITS_TOP_MASK = 0xE0000000UL;
-/** Total bits available for versionbits */
-static const int32_t VERSIONBITS_NUM_BITS = 29;
+/** Total bits available for versionbits (BIP 323) */
+static const int32_t VERSIONBITS_NUM_BITS = 5;
/** Opaque type for BIP9 state. See versionbits_impl.h for details. */
enum class ThresholdState : uint8_t;
diff --git a/test/functional/feature_versionbits_warning.py b/test/functional/feature_versionbits_warning.py
index 79f512ad..1f858dc7 100755
--- a/test/functional/feature_versionbits_warning.py
+++ b/test/functional/feature_versionbits_warning.py
@@ -18,7 +18,7 @@ from test_framework.test_framework import BitcoinTestFramework
VB_PERIOD = 144 # versionbits period length for regtest
VB_THRESHOLD = 108 # versionbits activation threshold for regtest
VB_TOP_BITS = 0x20000000
-VB_UNKNOWN_BIT = 27 # Choose a bit unassigned to any deployment
+VB_UNKNOWN_BIT = 3 # Choose a bit unassigned to any deployment
VB_UNKNOWN_VERSION = VB_TOP_BITS | (1 << VB_UNKNOWN_BIT)
WARN_UNKNOWN_RULES_ACTIVE = f"Unknown new rules activated (versionbit {VB_UNKNOWN_BIT})"
Why this scored 27/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.