From f802edf57cc844f9a1708b395906a974dded451c Mon Sep 17 00:00:00 2001 From: Anthony Towns Date: Tue, 10 Mar 2026 17:55:12 +1000 Subject: [PATCH 1/4] versionbits: Limit live activation params and activation warnings per BIP323 Test bits are conserved. This only has an effect on the warnings. Co-Authored-By: Antoine Poinsot --- src/test/fuzz/versionbits.cpp | 5 +++-- src/test/util/versionbits.h | 13 +++++++++++++ src/test/versionbits_tests.cpp | 13 ++++++++++++- src/versionbits.h | 4 ++-- test/functional/feature_versionbits_warning.py | 2 +- 5 files changed, 31 insertions(+), 6 deletions(-) create mode 100644 src/test/util/versionbits.h diff --git a/src/test/fuzz/versionbits.cpp b/src/test/fuzz/versionbits.cpp index a2085e6a409..0ee86e6e58b 100644 --- a/src/test/fuzz/versionbits.cpp +++ b/src/test/fuzz/versionbits.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -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(0, VERSIONBITS_NUM_BITS - 1); + dep.bit = fuzzed_data_provider.ConsumeIntegralInRange(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 00000000000..478b7882fab --- /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 + +/** 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 ad792053117..77384c5f2f1 100644 --- a/src/test/versionbits_tests.cpp +++ b/src/test/versionbits_tests.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -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 59b0cbeee71..0ac17287da9 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 79f512adea0..1f858dc734f 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})" From 1d5240574a125af55a07cd74b70df61b3f5a27c9 Mon Sep 17 00:00:00 2001 From: Antoine Poinsot Date: Mon, 9 Mar 2026 13:19:14 -0400 Subject: [PATCH 2/4] qa: test we don't warn for ignored unknown version bits deployments Co-Authored-by: Anthony Towns --- .../functional/feature_versionbits_warning.py | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/test/functional/feature_versionbits_warning.py b/test/functional/feature_versionbits_warning.py index 1f858dc734f..8bb457e2734 100755 --- a/test/functional/feature_versionbits_warning.py +++ b/test/functional/feature_versionbits_warning.py @@ -18,8 +18,13 @@ 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 = 3 # Choose a bit unassigned to any deployment + +# Choose a bit unassigned to any deployment, or start the +# node with the deployment matching this bit disabled. +VB_UNKNOWN_BIT = 3 VB_UNKNOWN_VERSION = VB_TOP_BITS | (1 << VB_UNKNOWN_BIT) +VB_IGNORED_BIT = 5 +VB_IGNORED_VERSION = VB_TOP_BITS | (1 << VB_IGNORED_BIT) WARN_UNKNOWN_RULES_ACTIVE = f"Unknown new rules activated (versionbit {VB_UNKNOWN_BIT})" VB_PATTERN = re.compile("Unknown new rules activated.*versionbit") @@ -76,11 +81,24 @@ class VersionBitsWarningTest(BitcoinTestFramework): assert not VB_PATTERN.match(",".join(node.getmininginfo()["warnings"])) assert not VB_PATTERN.match(",".join(node.getnetworkinfo()["warnings"])) + self.log.info("Check that there is no warning if previous VB_BLOCKS have VB_PERIOD blocks with ignored versionbits version.") + # Build one period of blocks with VB_THRESHOLD blocks signaling some unknown bit + self.send_blocks_with_version(peer, VB_THRESHOLD, VB_IGNORED_VERSION) + self.generatetoaddress(node, VB_PERIOD - VB_THRESHOLD, node_deterministic_address) + + # Move the ignored deployment state to ACTIVE and make sure we're out of IBD. + self.generatetoaddress(node, VB_PERIOD, node_deterministic_address) + self.wait_until(lambda: not node.getblockchaininfo()['initialblockdownload']) + + # Check that we're not getting any versionbit-related warnings in get*info() + assert not VB_PATTERN.match(", ".join(node.getmininginfo()["warnings"])) + assert not VB_PATTERN.match(", ".join(node.getnetworkinfo()["warnings"])) + + self.log.info("Check that there is a warning if previous VB_BLOCKS have >=VB_THRESHOLD blocks with unknown versionbits version.") # Build one period of blocks with VB_THRESHOLD blocks signaling some unknown bit self.send_blocks_with_version(peer, VB_THRESHOLD, VB_UNKNOWN_VERSION) self.generatetoaddress(node, VB_PERIOD - VB_THRESHOLD, node_deterministic_address) - self.log.info("Check that there is a warning if previous VB_BLOCKS have >=VB_THRESHOLD blocks with unknown versionbits version.") # Mine a period worth of expected blocks so the generic block-version warning # is cleared. This will move the versionbit state to ACTIVE. self.generatetoaddress(node, VB_PERIOD, node_deterministic_address) From 94e3ac0b215a8fd749bd83947af5a36be44032c8 Mon Sep 17 00:00:00 2001 From: Antoine Poinsot Date: Wed, 20 May 2026 10:51:24 -0400 Subject: [PATCH 3/4] doc: release notes and bips doc update for #34779 --- doc/bips.md | 1 + doc/release-notes-34779.md | 5 +++++ 2 files changed, 6 insertions(+) create mode 100644 doc/release-notes-34779.md diff --git a/doc/bips.md b/doc/bips.md index c814717a09f..1dec268f920 100644 --- a/doc/bips.md +++ b/doc/bips.md @@ -49,6 +49,7 @@ BIPs that are implemented by Bitcoin Core: * [`BIP 173`](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki): Bech32 addresses for native Segregated Witness outputs are supported as of **v0.16.0** ([PR 11167](https://github.com/bitcoin/bitcoin/pull/11167)). Bech32 addresses are generated by default as of **v0.20.0** ([PR 16884](https://github.com/bitcoin/bitcoin/pull/16884)). * [`BIP 174`](https://github.com/bitcoin/bips/blob/master/bip-0174.mediawiki): RPCs to operate on Partially Signed Bitcoin Transactions (PSBT) are present as of **v0.17.0** ([PR 13557](https://github.com/bitcoin/bitcoin/pull/13557)). * [`BIP 176`](https://github.com/bitcoin/bips/blob/master/bip-0176.mediawiki): Bits Denomination [QT only] is supported as of **v0.16.0** ([PR 12035](https://github.com/bitcoin/bitcoin/pull/12035)). +* [`BIP 323`](https://github.com/bitcoin/bips/blob/master/bip-0323.mediawiki): BIP 9 bits 5 to 28 (inclusive) are ignored for soft-fork signalling and unknown soft fork warnings as of **v32.0**. * [`BIP 324`](https://github.com/bitcoin/bips/blob/master/bip-0324.mediawiki): The v2 transport protocol specified by BIP324 and the associated `NODE_P2P_V2` service bit are supported as of **v26.0**, but off by default ([PR 28331](https://github.com/bitcoin/bitcoin/pull/28331)). On by default as of **v27.0** ([PR 29347](https://github.com/bitcoin/bitcoin/pull/29347)). * [`BIP 325`](https://github.com/bitcoin/bips/blob/master/bip-0325.mediawiki): Signet test network is supported as of **v0.21.0** ([PR 18267](https://github.com/bitcoin/bitcoin/pull/18267)). * [`BIP 339`](https://github.com/bitcoin/bips/blob/master/bip-0339.mediawiki): Relay of transactions by wtxid is supported as of **v0.21.0** ([PR 18044](https://github.com/bitcoin/bitcoin/pull/18044)). diff --git a/doc/release-notes-34779.md b/doc/release-notes-34779.md new file mode 100644 index 00000000000..7f1f5099d06 --- /dev/null +++ b/doc/release-notes-34779.md @@ -0,0 +1,5 @@ +Logging +------- + +- BIP 9 bits 5 to 28 inclusive are now ignored for soft fork signaling, as per BIP 323. We won't + warn about unknown deployments when receiving blocks that set any of those bits in their version. From 107d4178d9184f62c74084f5954cc36f3213434f Mon Sep 17 00:00:00 2001 From: Antoine Poinsot Date: Wed, 20 May 2026 10:54:39 -0400 Subject: [PATCH 4/4] versionbits: update VersionBitsCache doc comment to match current behaviour Co-Authored-by: Anthony Towns --- src/versionbits.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/versionbits.h b/src/versionbits.h index 0ac17287da9..f88ead0dce6 100644 --- a/src/versionbits.h +++ b/src/versionbits.h @@ -72,7 +72,8 @@ struct BIP9GBTStatus { }; /** BIP 9 allows multiple softforks to be deployed in parallel. We cache - * per-period state for every one of them. */ + * per-period state for every one we implement and warning state for each + * BIP 323 allowed bit. */ class VersionBitsCache { private: