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