Bitcoin Core 29.99.0
P2P Digital Currency
versionbits.cpp
Go to the documentation of this file.
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#include <versionbits_impl.h>
13
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
23namespace {
25{
26private:
27 mutable ThresholdConditionCache m_cache;
28
29public:
31 {
32 assert(dep.period > 0);
33 assert(dep.threshold <= dep.period);
34 assert(0 <= dep.bit && dep.bit < 32 && dep.bit < VERSIONBITS_NUM_BITS);
36 }
37
38 ThresholdState GetStateFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateFor(pindexPrev, m_cache); }
39 int GetStateSinceHeightFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateSinceHeightFor(pindexPrev, m_cache); }
40};
41
43class Blocks
44{
45private:
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
52public:
53 Blocks(uint32_t start_time, uint32_t interval, int32_t signal, int32_t no_signal)
54 : m_start_time{start_time}, m_interval{interval}, m_signal{signal}, m_no_signal{no_signal} {}
55
56 size_t size() const { return m_blocks.size(); }
57
58 CBlockIndex* tip() const
59 {
60 return m_blocks.empty() ? nullptr : m_blocks.back().get();
61 }
62
63 CBlockIndex* mine_block(bool signal)
64 {
65 CBlockHeader header;
66 header.nVersion = signal ? m_signal : m_no_signal;
67 header.nTime = m_start_time + m_blocks.size() * m_interval;
68 header.nBits = 0x1d00ffff;
69
70 auto current_block = std::make_unique<CBlockIndex>(header);
71 current_block->pprev = tip();
72 current_block->nHeight = m_blocks.size();
73 current_block->BuildSkip();
74
75 return m_blocks.emplace_back(std::move(current_block)).get();
76 }
77};
78
79std::unique_ptr<const CChainParams> g_params;
80
81void initialize()
82{
83 // this is actually comparatively slow, so only do it once
85 assert(g_params != nullptr);
86}
87
88constexpr uint32_t MAX_START_TIME = 4102444800; // 2100-01-01
89
90FUZZ_TARGET(versionbits, .init = initialize)
91{
92 const CChainParams& params = *g_params;
93 const int64_t interval = params.GetConsensus().nPowTargetSpacing;
94 assert(interval > 1); // need to be able to halve it
95 assert(interval < std::numeric_limits<int32_t>::max());
96
97 FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
98
99 // making period/max_periods larger slows these tests down significantly
100 const uint32_t period = 32;
101 const size_t max_periods = 16;
102 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 assert(std::numeric_limits<uint32_t>::max() - MAX_START_TIME > interval * max_blocks);
107
108 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 const int32_t ver_signal = fuzzed_data_provider.ConsumeIntegral<int32_t>();
112 const int32_t ver_nosignal = fuzzed_data_provider.ConsumeIntegral<int32_t>();
113 if (ver_nosignal < 0) return; // negative values are uninteresting
114
115 // Now that we have chosen time and versions, setup to mine blocks
116 Blocks blocks(block_start_time, interval, ver_signal, ver_nosignal);
117
118 const bool always_active_test = fuzzed_data_provider.ConsumeBool();
119 const bool never_active_test = !always_active_test && fuzzed_data_provider.ConsumeBool();
120
121 const Consensus::BIP9Deployment dep{[&]() {
123 dep.period = period;
124
125 dep.threshold = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, period);
126 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 dep.bit = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, VERSIONBITS_NUM_BITS - 1);
130
131 if (always_active_test) {
133 dep.nTimeout = fuzzed_data_provider.ConsumeBool() ? Consensus::BIP9Deployment::NO_TIMEOUT : fuzzed_data_provider.ConsumeIntegral<int64_t>();
134 } else if (never_active_test) {
136 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 int start_block = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, period * (max_periods - 3));
141 int end_block = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, period * (max_periods - 3));
142
143 dep.nStartTime = block_start_time + start_block * interval;
144 dep.nTimeout = block_start_time + end_block * interval;
145
146 // allow for times to not exactly match a block
147 if (fuzzed_data_provider.ConsumeBool()) dep.nStartTime += interval / 2;
148 if (fuzzed_data_provider.ConsumeBool()) dep.nTimeout += interval / 2;
149 }
150 dep.min_activation_height = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, period * max_periods);
151 return dep;
152 }()};
153 TestConditionChecker checker(dep);
154
155 // Early exit if the versions don't signal sensibly for the deployment
156 if (!checker.Condition(ver_signal)) return;
157 if (checker.Condition(ver_nosignal)) return;
158
159 // TOP_BITS should ensure version will be positive and meet min
160 // version requirement
161 assert(ver_signal > 0);
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 const uint32_t signalling_mask = fuzzed_data_provider.ConsumeIntegral<uint32_t>();
178
179 // mine prior periods
180 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 bool signal = fuzzed_data_provider.ConsumeBool();
183 for (uint32_t b = 0; b < period; ++b) {
184 blocks.mine_block(signal);
185 }
186
187 // don't risk exceeding max_blocks or times may wrap around
188 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 uint32_t blocks_sig = 0;
196
197 // get the info for the first block of the period
198 CBlockIndex* prev = blocks.tip();
199 const int exp_since = checker.GetStateSinceHeightFor(prev);
200 const ThresholdState exp_state = checker.GetStateFor(prev);
201
202 // get statistics from end of previous period, then reset
203 BIP9Stats last_stats;
204 last_stats.period = period;
205 last_stats.threshold = dep.threshold;
206 last_stats.count = last_stats.elapsed = 0;
207 last_stats.possible = (period >= dep.threshold);
208 std::vector<bool> last_signals{};
209
210 int prev_next_height = (prev == nullptr ? 0 : prev->nHeight + 1);
211 assert(exp_since <= prev_next_height);
212
213 // mine (period-1) blocks and check state
214 for (uint32_t b = 1; b < period; ++b) {
215 const bool signal = (signalling_mask >> (b % 32)) & 1;
216 if (signal) ++blocks_sig;
217
218 CBlockIndex* current_block = blocks.mine_block(signal);
219
220 // verify that signalling attempt was interpreted correctly
221 assert(checker.Condition(current_block->nVersion) == signal);
222
223 // state and since don't change within the period
224 const ThresholdState state = checker.GetStateFor(current_block);
225 const int since = checker.GetStateSinceHeightFor(current_block);
226 assert(state == exp_state);
227 assert(since == exp_since);
228
229 // check that after mining this block stats change as expected
230 std::vector<bool> signals;
231 const BIP9Stats stats = checker.GetStateStatisticsFor(current_block, &signals);
232 const BIP9Stats stats_no_signals = checker.GetStateStatisticsFor(current_block);
233 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 assert(stats.period == period);
238 assert(stats.threshold == dep.threshold);
239 assert(stats.elapsed == b);
240 assert(stats.count == last_stats.count + (signal ? 1 : 0));
241 assert(stats.possible == (stats.count + period >= stats.elapsed + dep.threshold));
242 last_stats = stats;
243
244 assert(signals.size() == last_signals.size() + 1);
245 assert(signals.back() == signal);
246 last_signals.push_back(signal);
247 assert(signals == last_signals);
248 }
249
250 if (exp_state == ThresholdState::STARTED) {
251 // double check that stats.possible is sane
252 if (blocks_sig >= dep.threshold - 1) assert(last_stats.possible);
253 }
254
255 // mine the final block
256 bool signal = (signalling_mask >> (period % 32)) & 1;
257 if (signal) ++blocks_sig;
258 CBlockIndex* current_block = blocks.mine_block(signal);
259 assert(checker.Condition(current_block->nVersion) == signal);
260
261 const BIP9Stats stats = checker.GetStateStatisticsFor(current_block);
262 assert(stats.period == period);
263 assert(stats.threshold == dep.threshold);
264 assert(stats.elapsed == period);
265 assert(stats.count == blocks_sig);
266 assert(stats.possible == (stats.count + period >= stats.elapsed + dep.threshold));
267
268 // More interesting is whether the state changed.
269 const ThresholdState state = checker.GetStateFor(current_block);
270 const int since = checker.GetStateSinceHeightFor(current_block);
271
272 // since is straightforward:
273 assert(since % period == 0);
274 assert(0 <= since && since <= current_block->nHeight + 1);
275 if (state == exp_state) {
276 assert(since == exp_since);
277 } else {
278 assert(since == current_block->nHeight + 1);
279 }
280
281 // state is where everything interesting is
282 switch (state) {
284 assert(since == 0);
285 assert(exp_state == ThresholdState::DEFINED);
286 assert(current_block->GetMedianTimePast() < dep.nStartTime);
287 break;
289 assert(current_block->GetMedianTimePast() >= dep.nStartTime);
290 if (exp_state == ThresholdState::STARTED) {
291 assert(blocks_sig < dep.threshold);
292 assert(current_block->GetMedianTimePast() < dep.nTimeout);
293 } else {
294 assert(exp_state == ThresholdState::DEFINED);
295 }
296 break;
298 if (exp_state == ThresholdState::LOCKED_IN) {
299 assert(current_block->nHeight + 1 < dep.min_activation_height);
300 } else {
301 assert(exp_state == ThresholdState::STARTED);
302 assert(blocks_sig >= dep.threshold);
303 }
304 break;
306 assert(always_active_test || dep.min_activation_height <= current_block->nHeight + 1);
307 assert(exp_state == ThresholdState::ACTIVE || exp_state == ThresholdState::LOCKED_IN);
308 break;
310 assert(never_active_test || current_block->GetMedianTimePast() >= dep.nTimeout);
311 if (exp_state == ThresholdState::STARTED) {
312 assert(blocks_sig < dep.threshold);
313 } else {
314 assert(exp_state == ThresholdState::FAILED);
315 }
316 break;
317 default:
318 assert(false);
319 }
320
321 if (blocks.size() >= period * max_periods) {
322 // we chose the timeout (and block times) so that by the time we have this many blocks it's all over
324 }
325
326 if (always_active_test) {
327 // "always active" has additional restrictions
329 assert(exp_state == ThresholdState::ACTIVE);
330 assert(since == 0);
331 } else if (never_active_test) {
332 // "never active" does too
334 assert(exp_state == ThresholdState::FAILED);
335 assert(since == 0);
336 } else {
337 // for signalled deployments, the initial state is always DEFINED
338 assert(since > 0 || state == ThresholdState::DEFINED);
339 assert(exp_since > 0 || exp_state == ThresholdState::DEFINED);
340 }
341}
342} // namespace
std::unique_ptr< const CChainParams > CreateChainParams(const ArgsManager &args, const ChainType chain)
Creates and returns a std::unique_ptr<CChainParams> of the chosen chain.
ThresholdState GetStateFor(const CBlockIndex *pindexPrev, ThresholdConditionCache &cache) const
Returns the state for pindex A based on parent pindexPrev B.
Definition: versionbits.cpp:26
int GetStateSinceHeightFor(const CBlockIndex *pindexPrev, ThresholdConditionCache &cache) const
Returns the height since when the ThresholdState has started for pindex A based on parent pindexPrev ...
Nodes collect new transactions into a block, hash them into a hash tree, and scan through nonce value...
Definition: block.h:22
uint32_t nBits
Definition: block.h:29
uint32_t nTime
Definition: block.h:28
int32_t nVersion
Definition: block.h:25
The block chain is a tree shaped structure starting with the genesis block at the root,...
Definition: chain.h:141
int64_t GetMedianTimePast() const
Definition: chain.h:278
int32_t nVersion
block header
Definition: chain.h:187
int nHeight
height of the entry in the chain. The genesis block has height 0
Definition: chain.h:153
CChainParams defines various tweakable parameters of a given instance of the Bitcoin system.
Definition: chainparams.h:69
const CBlock & GenesisBlock() const
Definition: chainparams.h:86
const Consensus::Params & GetConsensus() const
Definition: chainparams.h:81
Class to implement versionbits logic.
static void initialize()
Definition: fuzz.cpp:95
#define FUZZ_TARGET(...)
Definition: fuzz.h:35
unsigned int nHeight
Display status of an in-progress BIP9 softfork.
Definition: versionbits.h:36
uint32_t count
Number of blocks with the version bit set since the beginning of the current period.
Definition: versionbits.h:44
uint32_t threshold
Number of blocks with the version bit set required to activate the softfork.
Definition: versionbits.h:40
uint32_t elapsed
Number of blocks elapsed since the beginning of the current period.
Definition: versionbits.h:42
bool possible
False if there are not enough blocks left in this period to pass activation threshold.
Definition: versionbits.h:46
uint32_t period
Length of blocks of the BIP9 signalling period.
Definition: versionbits.h:38
Struct for each individual consensus rule change using BIP9.
Definition: params.h:44
int min_activation_height
If lock in occurs, delay activation until at least this block height.
Definition: params.h:55
int bit
Bit position to select the particular bit in nVersion.
Definition: params.h:46
static constexpr int64_t ALWAYS_ACTIVE
Special value for nStartTime indicating that the deployment is always active.
Definition: params.h:72
static constexpr int64_t NEVER_ACTIVE
Special value for nStartTime indicating that the deployment is never active.
Definition: params.h:77
uint32_t period
Period of blocks to check signalling in (usually retarget period, ie params.DifficultyAdjustmentInter...
Definition: params.h:57
uint32_t threshold
Minimum blocks including miner confirmation of the total of 2016 blocks in a retargeting period,...
Definition: params.h:63
int64_t nTimeout
Timeout/expiry MedianTime for the deployment attempt.
Definition: params.h:50
static constexpr int64_t NO_TIMEOUT
Constant for nTimeout very far in the future.
Definition: params.h:66
int64_t nStartTime
Start MedianTime for version bits miner confirmation.
Definition: params.h:48
int64_t nPowTargetSpacing
Definition: params.h:119
assert(!tx.IsCoinBase())
std::map< const CBlockIndex *, ThresholdState > ThresholdConditionCache
Definition: versionbits.h:33
static const int32_t VERSIONBITS_NUM_BITS
Total bits available for versionbits.
Definition: versionbits.h:25
static const int32_t VERSIONBITS_LAST_OLD_BLOCK_VERSION
What block version to use for new blocks (pre versionbits)
Definition: versionbits.h:19
ThresholdState
BIP 9 defines a finite-state-machine to deploy a softfork in multiple stages.