Coverage Report

Created: 2025-02-21 14:36

/root/bitcoin/src/test/fuzz/versionbits.cpp
Line
Count
Source (jump to first uncovered line)
1
// Copyright (c) 2020-2021 The Bitcoin Core developers
2
// Distributed under the MIT software license, see the accompanying
3
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
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
13
#include <test/fuzz/FuzzedDataProvider.h>
14
#include <test/fuzz/fuzz.h>
15
#include <test/fuzz/util.h>
16
17
#include <cstdint>
18
#include <limits>
19
#include <memory>
20
#include <vector>
21
22
namespace {
23
class TestConditionChecker : public AbstractThresholdConditionChecker
24
{
25
private:
26
    mutable ThresholdConditionCache m_cache;
27
    const Consensus::Params dummy_params{};
28
29
public:
30
    const int64_t m_begin;
31
    const int64_t m_end;
32
    const int m_period;
33
    const int m_threshold;
34
    const int m_min_activation_height;
35
    const int m_bit;
36
37
    TestConditionChecker(int64_t begin, int64_t end, int period, int threshold, int min_activation_height, int bit)
38
0
        : m_begin{begin}, m_end{end}, m_period{period}, m_threshold{threshold}, m_min_activation_height{min_activation_height}, m_bit{bit}
39
0
    {
40
0
        assert(m_period > 0);
41
0
        assert(0 <= m_threshold && m_threshold <= m_period);
42
0
        assert(0 <= m_bit && m_bit < 32 && m_bit < VERSIONBITS_NUM_BITS);
43
0
        assert(0 <= m_min_activation_height);
44
0
    }
45
46
0
    bool Condition(const CBlockIndex* pindex, const Consensus::Params& params) const override { return Condition(pindex->nVersion); }
47
0
    int64_t BeginTime(const Consensus::Params& params) const override { return m_begin; }
48
0
    int64_t EndTime(const Consensus::Params& params) const override { return m_end; }
49
0
    int Period(const Consensus::Params& params) const override { return m_period; }
50
0
    int Threshold(const Consensus::Params& params) const override { return m_threshold; }
51
0
    int MinActivationHeight(const Consensus::Params& params) const override { return m_min_activation_height; }
52
53
0
    ThresholdState GetStateFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateFor(pindexPrev, dummy_params, m_cache); }
54
0
    int GetStateSinceHeightFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateSinceHeightFor(pindexPrev, dummy_params, m_cache); }
55
0
    BIP9Stats GetStateStatisticsFor(const CBlockIndex* pindex, std::vector<bool>* signals=nullptr) const { return AbstractThresholdConditionChecker::GetStateStatisticsFor(pindex, dummy_params, signals); }
56
57
    bool Condition(int32_t version) const
58
0
    {
59
0
        uint32_t mask = (uint32_t{1}) << m_bit;
60
0
        return (((version & VERSIONBITS_TOP_MASK) == VERSIONBITS_TOP_BITS) && (version & mask) != 0);
61
0
    }
62
63
0
    bool Condition(const CBlockIndex* pindex) const { return Condition(pindex->nVersion); }
64
};
65
66
/** Track blocks mined for test */
67
class Blocks
68
{
69
private:
70
    std::vector<std::unique_ptr<CBlockIndex>> m_blocks;
71
    const uint32_t m_start_time;
72
    const uint32_t m_interval;
73
    const int32_t m_signal;
74
    const int32_t m_no_signal;
75
76
public:
77
    Blocks(uint32_t start_time, uint32_t interval, int32_t signal, int32_t no_signal)
78
0
        : m_start_time{start_time}, m_interval{interval}, m_signal{signal}, m_no_signal{no_signal} {}
79
80
0
    size_t size() const { return m_blocks.size(); }
81
82
    CBlockIndex* tip() const
83
0
    {
84
0
        return m_blocks.empty() ? nullptr : m_blocks.back().get();
85
0
    }
86
87
    CBlockIndex* mine_block(bool signal)
88
0
    {
89
0
        CBlockHeader header;
90
0
        header.nVersion = signal ? m_signal : m_no_signal;
91
0
        header.nTime = m_start_time + m_blocks.size() * m_interval;
92
0
        header.nBits = 0x1d00ffff;
93
94
0
        auto current_block = std::make_unique<CBlockIndex>(header);
95
0
        current_block->pprev = tip();
96
0
        current_block->nHeight = m_blocks.size();
97
0
        current_block->BuildSkip();
98
99
0
        return m_blocks.emplace_back(std::move(current_block)).get();
100
0
    }
101
};
102
103
std::unique_ptr<const CChainParams> g_params;
104
105
void initialize()
106
0
{
107
    // this is actually comparatively slow, so only do it once
108
0
    g_params = CreateChainParams(ArgsManager{}, ChainType::MAIN);
109
0
    assert(g_params != nullptr);
110
0
}
111
112
constexpr uint32_t MAX_START_TIME = 4102444800; // 2100-01-01
113
114
FUZZ_TARGET(versionbits, .init = initialize)
115
0
{
116
0
    const CChainParams& params = *g_params;
117
0
    const int64_t interval = params.GetConsensus().nPowTargetSpacing;
118
0
    assert(interval > 1); // need to be able to halve it
119
0
    assert(interval < std::numeric_limits<int32_t>::max());
120
121
0
    FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
122
123
    // making period/max_periods larger slows these tests down significantly
124
0
    const int period = 32;
125
0
    const size_t max_periods = 16;
126
0
    const size_t max_blocks = 2 * period * max_periods;
127
128
0
    const int threshold = fuzzed_data_provider.ConsumeIntegralInRange(1, period);
129
0
    assert(0 < threshold && threshold <= period); // must be able to both pass and fail threshold!
130
131
    // too many blocks at 10min each might cause uint32_t time to overflow if
132
    // block_start_time is at the end of the range above
133
0
    assert(std::numeric_limits<uint32_t>::max() - MAX_START_TIME > interval * max_blocks);
134
135
0
    const int64_t block_start_time = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(params.GenesisBlock().nTime, MAX_START_TIME);
136
137
    // what values for version will we use to signal / not signal?
138
0
    const int32_t ver_signal = fuzzed_data_provider.ConsumeIntegral<int32_t>();
139
0
    const int32_t ver_nosignal = fuzzed_data_provider.ConsumeIntegral<int32_t>();
140
141
    // select deployment parameters: bit, start time, timeout
142
0
    const int bit = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, VERSIONBITS_NUM_BITS - 1);
143
144
0
    bool always_active_test = false;
145
0
    bool never_active_test = false;
146
0
    int64_t start_time;
147
0
    int64_t timeout;
148
0
    if (fuzzed_data_provider.ConsumeBool()) {
149
        // pick the timestamp to switch based on a block
150
        // note states will change *after* these blocks because mediantime lags
151
0
        int start_block = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, period * (max_periods - 3));
152
0
        int end_block = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, period * (max_periods - 3));
153
154
0
        start_time = block_start_time + start_block * interval;
155
0
        timeout = block_start_time + end_block * interval;
156
157
        // allow for times to not exactly match a block
158
0
        if (fuzzed_data_provider.ConsumeBool()) start_time += interval / 2;
159
0
        if (fuzzed_data_provider.ConsumeBool()) timeout += interval / 2;
160
0
    } else {
161
0
        if (fuzzed_data_provider.ConsumeBool()) {
162
0
            start_time = Consensus::BIP9Deployment::ALWAYS_ACTIVE;
163
0
            always_active_test = true;
164
0
        } else {
165
0
            start_time = Consensus::BIP9Deployment::NEVER_ACTIVE;
166
0
            never_active_test = true;
167
0
        }
168
0
        timeout = fuzzed_data_provider.ConsumeBool() ? Consensus::BIP9Deployment::NO_TIMEOUT : fuzzed_data_provider.ConsumeIntegral<int64_t>();
169
0
    }
170
0
    int min_activation = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, period * max_periods);
171
172
0
    TestConditionChecker checker(start_time, timeout, period, threshold, min_activation, bit);
173
174
    // Early exit if the versions don't signal sensibly for the deployment
175
0
    if (!checker.Condition(ver_signal)) return;
176
0
    if (checker.Condition(ver_nosignal)) return;
177
0
    if (ver_nosignal < 0) return;
178
179
    // TOP_BITS should ensure version will be positive and meet min
180
    // version requirement
181
0
    assert(ver_signal > 0);
182
0
    assert(ver_signal >= VERSIONBITS_LAST_OLD_BLOCK_VERSION);
183
184
    // Now that we have chosen time and versions, setup to mine blocks
185
0
    Blocks blocks(block_start_time, interval, ver_signal, ver_nosignal);
186
187
    /* Strategy:
188
     *  * we will mine a final period worth of blocks, with
189
     *    randomised signalling according to a mask
190
     *  * but before we mine those blocks, we will mine some
191
     *    randomised number of prior periods; with either all
192
     *    or no blocks in the period signalling
193
     *
194
     * We establish the mask first, then consume "bools" until
195
     * we run out of fuzz data to work out how many prior periods
196
     * there are and which ones will signal.
197
     */
198
199
    // establish the mask
200
0
    const uint32_t signalling_mask = fuzzed_data_provider.ConsumeIntegral<uint32_t>();
201
202
    // mine prior periods
203
0
    while (fuzzed_data_provider.remaining_bytes() > 0) { // early exit; no need for LIMITED_WHILE
204
        // all blocks in these periods either do or don't signal
205
0
        bool signal = fuzzed_data_provider.ConsumeBool();
206
0
        for (int b = 0; b < period; ++b) {
207
0
            blocks.mine_block(signal);
208
0
        }
209
210
        // don't risk exceeding max_blocks or times may wrap around
211
0
        if (blocks.size() + 2 * period > max_blocks) break;
212
0
    }
213
    // NOTE: fuzzed_data_provider may be fully consumed at this point and should not be used further
214
215
    // now we mine the final period and check that everything looks sane
216
217
    // count the number of signalling blocks
218
0
    int blocks_sig = 0;
219
220
    // get the info for the first block of the period
221
0
    CBlockIndex* prev = blocks.tip();
222
0
    const int exp_since = checker.GetStateSinceHeightFor(prev);
223
0
    const ThresholdState exp_state = checker.GetStateFor(prev);
224
225
    // get statistics from end of previous period, then reset
226
0
    BIP9Stats last_stats;
227
0
    last_stats.period = period;
228
0
    last_stats.threshold = threshold;
229
0
    last_stats.count = last_stats.elapsed = 0;
230
0
    last_stats.possible = (period >= threshold);
231
0
    std::vector<bool> last_signals{};
232
233
0
    int prev_next_height = (prev == nullptr ? 0 : prev->nHeight + 1);
234
0
    assert(exp_since <= prev_next_height);
235
236
    // mine (period-1) blocks and check state
237
0
    for (int b = 1; b < period; ++b) {
238
0
        const bool signal = (signalling_mask >> (b % 32)) & 1;
239
0
        if (signal) ++blocks_sig;
240
241
0
        CBlockIndex* current_block = blocks.mine_block(signal);
242
243
        // verify that signalling attempt was interpreted correctly
244
0
        assert(checker.Condition(current_block) == signal);
245
246
        // state and since don't change within the period
247
0
        const ThresholdState state = checker.GetStateFor(current_block);
248
0
        const int since = checker.GetStateSinceHeightFor(current_block);
249
0
        assert(state == exp_state);
250
0
        assert(since == exp_since);
251
252
        // check that after mining this block stats change as expected
253
0
        std::vector<bool> signals;
254
0
        const BIP9Stats stats = checker.GetStateStatisticsFor(current_block, &signals);
255
0
        const BIP9Stats stats_no_signals = checker.GetStateStatisticsFor(current_block);
256
0
        assert(stats.period == stats_no_signals.period && stats.threshold == stats_no_signals.threshold
257
0
               && stats.elapsed == stats_no_signals.elapsed && stats.count == stats_no_signals.count
258
0
               && stats.possible == stats_no_signals.possible);
259
260
0
        assert(stats.period == period);
261
0
        assert(stats.threshold == threshold);
262
0
        assert(stats.elapsed == b);
263
0
        assert(stats.count == last_stats.count + (signal ? 1 : 0));
264
0
        assert(stats.possible == (stats.count + period >= stats.elapsed + threshold));
265
0
        last_stats = stats;
266
267
0
        assert(signals.size() == last_signals.size() + 1);
268
0
        assert(signals.back() == signal);
269
0
        last_signals.push_back(signal);
270
0
        assert(signals == last_signals);
271
0
    }
272
273
0
    if (exp_state == ThresholdState::STARTED) {
274
        // double check that stats.possible is sane
275
0
        if (blocks_sig >= threshold - 1) assert(last_stats.possible);
276
0
    }
277
278
    // mine the final block
279
0
    bool signal = (signalling_mask >> (period % 32)) & 1;
280
0
    if (signal) ++blocks_sig;
281
0
    CBlockIndex* current_block = blocks.mine_block(signal);
282
0
    assert(checker.Condition(current_block) == signal);
283
284
0
    const BIP9Stats stats = checker.GetStateStatisticsFor(current_block);
285
0
    assert(stats.period == period);
286
0
    assert(stats.threshold == threshold);
287
0
    assert(stats.elapsed == period);
288
0
    assert(stats.count == blocks_sig);
289
0
    assert(stats.possible == (stats.count + period >= stats.elapsed + threshold));
290
291
    // More interesting is whether the state changed.
292
0
    const ThresholdState state = checker.GetStateFor(current_block);
293
0
    const int since = checker.GetStateSinceHeightFor(current_block);
294
295
    // since is straightforward:
296
0
    assert(since % period == 0);
297
0
    assert(0 <= since && since <= current_block->nHeight + 1);
298
0
    if (state == exp_state) {
299
0
        assert(since == exp_since);
300
0
    } else {
301
0
        assert(since == current_block->nHeight + 1);
302
0
    }
303
304
    // state is where everything interesting is
305
0
    switch (state) {
306
0
    case ThresholdState::DEFINED:
307
0
        assert(since == 0);
308
0
        assert(exp_state == ThresholdState::DEFINED);
309
0
        assert(current_block->GetMedianTimePast() < checker.m_begin);
310
0
        break;
311
0
    case ThresholdState::STARTED:
312
0
        assert(current_block->GetMedianTimePast() >= checker.m_begin);
313
0
        if (exp_state == ThresholdState::STARTED) {
314
0
            assert(blocks_sig < threshold);
315
0
            assert(current_block->GetMedianTimePast() < checker.m_end);
316
0
        } else {
317
0
            assert(exp_state == ThresholdState::DEFINED);
318
0
        }
319
0
        break;
320
0
    case ThresholdState::LOCKED_IN:
321
0
        if (exp_state == ThresholdState::LOCKED_IN) {
322
0
            assert(current_block->nHeight + 1 < min_activation);
323
0
        } else {
324
0
            assert(exp_state == ThresholdState::STARTED);
325
0
            assert(blocks_sig >= threshold);
326
0
        }
327
0
        break;
328
0
    case ThresholdState::ACTIVE:
329
0
        assert(always_active_test || min_activation <= current_block->nHeight + 1);
330
0
        assert(exp_state == ThresholdState::ACTIVE || exp_state == ThresholdState::LOCKED_IN);
331
0
        break;
332
0
    case ThresholdState::FAILED:
333
0
        assert(never_active_test || current_block->GetMedianTimePast() >= checker.m_end);
334
0
        if (exp_state == ThresholdState::STARTED) {
335
0
            assert(blocks_sig < threshold);
336
0
        } else {
337
0
            assert(exp_state == ThresholdState::FAILED);
338
0
        }
339
0
        break;
340
0
    default:
341
0
        assert(false);
342
0
    }
343
344
0
    if (blocks.size() >= period * max_periods) {
345
        // we chose the timeout (and block times) so that by the time we have this many blocks it's all over
346
0
        assert(state == ThresholdState::ACTIVE || state == ThresholdState::FAILED);
347
0
    }
348
349
0
    if (always_active_test) {
350
        // "always active" has additional restrictions
351
0
        assert(state == ThresholdState::ACTIVE);
352
0
        assert(exp_state == ThresholdState::ACTIVE);
353
0
        assert(since == 0);
354
0
    } else if (never_active_test) {
355
        // "never active" does too
356
0
        assert(state == ThresholdState::FAILED);
357
0
        assert(exp_state == ThresholdState::FAILED);
358
0
        assert(since == 0);
359
0
    } else {
360
        // for signalled deployments, the initial state is always DEFINED
361
0
        assert(since > 0 || state == ThresholdState::DEFINED);
362
0
        assert(exp_since > 0 || exp_state == ThresholdState::DEFINED);
363
0
    }
364
0
}
365
} // namespace