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