Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1// Copyright (c) 2020-present The Bitcoin Core developers2// Distributed under the MIT software license, see the accompanying3// file COPYING or http://www.opensource.org/licenses/mit-license.php.5#include <chain.h>6#include <chainparams.h>7#include <common/args.h>8#include <consensus/params.h>9#include <primitives/block.h>10#include <util/chaintype.h>11#include <versionbits.h>12#include <versionbits_impl.h>14#include <test/fuzz/FuzzedDataProvider.h>15#include <test/fuzz/fuzz.h>16#include <test/fuzz/util.h>17#include <test/util/versionbits.h>19#include <cstdint>20#include <limits>21#include <memory>22#include <vector>24namespace {25class TestConditionChecker : public VersionBitsConditionChecker26{27private:28mutable ThresholdConditionCache m_cache;30public:31TestConditionChecker(const Consensus::BIP9Deployment& dep) : VersionBitsConditionChecker{dep}32{33assert(dep.period > 0);34assert(dep.threshold <= dep.period);35assert(0 <= dep.bit && dep.bit < 32 && dep.bit < VERSIONBITS_MAX_NUM_BITS);36assert(0 <= dep.min_activation_height);37}39ThresholdState GetStateFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateFor(pindexPrev, m_cache); }40int GetStateSinceHeightFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateSinceHeightFor(pindexPrev, m_cache); }41};43/** Track blocks mined for test */44class Blocks45{46private:47std::vector<std::unique_ptr<CBlockIndex>> m_blocks;48const uint32_t m_start_time;49const uint32_t m_interval;50const int32_t m_signal;51const int32_t m_no_signal;53public:54Blocks(uint32_t start_time, uint32_t interval, int32_t signal, int32_t no_signal)55: m_start_time{start_time}, m_interval{interval}, m_signal{signal}, m_no_signal{no_signal} {}57size_t size() const { return m_blocks.size(); }59CBlockIndex* tip() const60{61return m_blocks.empty() ? nullptr : m_blocks.back().get();62}64CBlockIndex* mine_block(bool signal)65{66CBlockHeader header;67header.nVersion = signal ? m_signal : m_no_signal;68header.nTime = m_start_time + m_blocks.size() * m_interval;69header.nBits = 0x1d00ffff;71auto current_block = std::make_unique<CBlockIndex>(header);72current_block->pprev = tip();73current_block->nHeight = m_blocks.size();74current_block->BuildSkip();76return m_blocks.emplace_back(std::move(current_block)).get();77}78};80std::unique_ptr<const CChainParams> g_params;82void initialize()83{84// this is actually comparatively slow, so only do it once85g_params = CreateChainParams(ArgsManager{}, ChainType::MAIN);86assert(g_params != nullptr);87}89constexpr uint32_t MAX_START_TIME = 4102444800; // 2100-01-0191FUZZ_TARGET(versionbits, .init = initialize)92{93const CChainParams& params = *g_params;94const int64_t interval = params.GetConsensus().nPowTargetSpacing;95assert(interval > 1); // need to be able to halve it96assert(interval < std::numeric_limits<int32_t>::max());98FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());100// making period/max_periods larger slows these tests down significantly101const uint32_t period = 32;102const size_t max_periods = 16;103const size_t max_blocks = 2 * period * max_periods;105// too many blocks at 10min each might cause uint32_t time to overflow if106// block_start_time is at the end of the range above107assert(std::numeric_limits<uint32_t>::max() - MAX_START_TIME > interval * max_blocks);109const int64_t block_start_time = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(params.GenesisBlock().nTime, MAX_START_TIME);111// what values for version will we use to signal / not signal?112const int32_t ver_signal = fuzzed_data_provider.ConsumeIntegral<int32_t>();113const int32_t ver_nosignal = fuzzed_data_provider.ConsumeIntegral<int32_t>();114if (ver_nosignal < 0) return; // negative values are uninteresting116// Now that we have chosen time and versions, setup to mine blocks117Blocks blocks(block_start_time, interval, ver_signal, ver_nosignal);119const bool always_active_test = fuzzed_data_provider.ConsumeBool();120const bool never_active_test = !always_active_test && fuzzed_data_provider.ConsumeBool();122const Consensus::BIP9Deployment dep{[&]() {123Consensus::BIP9Deployment dep;124dep.period = period;126dep.threshold = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, period);127assert(0 < dep.threshold && dep.threshold <= dep.period); // must be able to both pass and fail threshold!129// select deployment parameters: bit, start time, timeout130dep.bit = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, VERSIONBITS_MAX_NUM_BITS - 1);132if (always_active_test) {133dep.nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE;134dep.nTimeout = fuzzed_data_provider.ConsumeBool() ? Consensus::BIP9Deployment::NO_TIMEOUT : fuzzed_data_provider.ConsumeIntegral<int64_t>();135} else if (never_active_test) {136dep.nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE;137dep.nTimeout = fuzzed_data_provider.ConsumeBool() ? Consensus::BIP9Deployment::NO_TIMEOUT : fuzzed_data_provider.ConsumeIntegral<int64_t>();138} else {139// pick the timestamp to switch based on a block140// note states will change *after* these blocks because mediantime lags141int start_block = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, period * (max_periods - 3));142int end_block = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, period * (max_periods - 3));144dep.nStartTime = block_start_time + start_block * interval;145dep.nTimeout = block_start_time + end_block * interval;147// allow for times to not exactly match a block148if (fuzzed_data_provider.ConsumeBool()) dep.nStartTime += interval / 2;149if (fuzzed_data_provider.ConsumeBool()) dep.nTimeout += interval / 2;150}151dep.min_activation_height = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, period * max_periods);152return dep;153}()};154TestConditionChecker checker(dep);156// Early exit if the versions don't signal sensibly for the deployment157if (!checker.Condition(ver_signal)) return;158if (checker.Condition(ver_nosignal)) return;160// TOP_BITS should ensure version will be positive and meet min161// version requirement162assert(ver_signal > 0);163assert(ver_signal >= VERSIONBITS_LAST_OLD_BLOCK_VERSION);165/* Strategy:166* * we will mine a final period worth of blocks, with167* randomised signalling according to a mask168* * but before we mine those blocks, we will mine some169* randomised number of prior periods; with either all170* or no blocks in the period signalling171*172* We establish the mask first, then consume "bools" until173* we run out of fuzz data to work out how many prior periods174* there are and which ones will signal.175*/177// establish the mask178const uint32_t signalling_mask = fuzzed_data_provider.ConsumeIntegral<uint32_t>();180// mine prior periods181while (fuzzed_data_provider.remaining_bytes() > 0) { // early exit; no need for LIMITED_WHILE182// all blocks in these periods either do or don't signal183bool signal = fuzzed_data_provider.ConsumeBool();184for (uint32_t b = 0; b < period; ++b) {185blocks.mine_block(signal);186}188// don't risk exceeding max_blocks or times may wrap around189if (blocks.size() + 2 * period > max_blocks) break;190}191// NOTE: fuzzed_data_provider may be fully consumed at this point and should not be used further193// now we mine the final period and check that everything looks sane195// count the number of signalling blocks196uint32_t blocks_sig = 0;198// get the info for the first block of the period199CBlockIndex* prev = blocks.tip();200const int exp_since = checker.GetStateSinceHeightFor(prev);201const ThresholdState exp_state = checker.GetStateFor(prev);203// get statistics from end of previous period, then reset204BIP9Stats last_stats;205last_stats.period = period;206last_stats.threshold = dep.threshold;207last_stats.count = last_stats.elapsed = 0;208last_stats.possible = (period >= dep.threshold);209std::vector<bool> last_signals{};211int prev_next_height = (prev == nullptr ? 0 : prev->nHeight + 1);212assert(exp_since <= prev_next_height);214// mine (period-1) blocks and check state215for (uint32_t b = 1; b < period; ++b) {216const bool signal = (signalling_mask >> (b % 32)) & 1;217if (signal) ++blocks_sig;219CBlockIndex* current_block = blocks.mine_block(signal);221// verify that signalling attempt was interpreted correctly222assert(checker.Condition(current_block->nVersion) == signal);224// state and since don't change within the period225const ThresholdState state = checker.GetStateFor(current_block);226const int since = checker.GetStateSinceHeightFor(current_block);227assert(state == exp_state);228assert(since == exp_since);230// check that after mining this block stats change as expected231std::vector<bool> signals;232const BIP9Stats stats = checker.GetStateStatisticsFor(current_block, &signals);233const BIP9Stats stats_no_signals = checker.GetStateStatisticsFor(current_block);234assert(stats.period == stats_no_signals.period && stats.threshold == stats_no_signals.threshold235&& stats.elapsed == stats_no_signals.elapsed && stats.count == stats_no_signals.count236&& stats.possible == stats_no_signals.possible);238assert(stats.period == period);239assert(stats.threshold == dep.threshold);240assert(stats.elapsed == b);241assert(stats.count == last_stats.count + (signal ? 1 : 0));242assert(stats.possible == (stats.count + period >= stats.elapsed + dep.threshold));243last_stats = stats;245assert(signals.size() == last_signals.size() + 1);246assert(signals.back() == signal);247last_signals.push_back(signal);248assert(signals == last_signals);249}
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.