Branch data Line data Source code
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 [ # # # # : 0 : 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 : 0 : 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 : 0 : bool Condition(int32_t version) const 58 : : { 59 : 0 : uint32_t mask = (uint32_t{1}) << m_bit; 60 [ # # ]: 0 : return (((version & VERSIONBITS_TOP_MASK) == VERSIONBITS_TOP_BITS) && (version & mask) != 0); 61 : : } 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 : 2 : const int32_t m_no_signal; 75 : : 76 : : public: 77 : 0 : 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 : 0 : CBlockIndex* tip() const 83 : : { 84 [ # # ]: 0 : return m_blocks.empty() ? nullptr : m_blocks.back().get(); 85 : : } 86 : : 87 : 0 : CBlockIndex* mine_block(bool signal) 88 : : { 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 : 0 : void initialize() 106 : : { 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 [ + - - + ]: 4 : FUZZ_TARGET(versionbits, .init = initialize) 115 : : { 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 : : int64_t start_time; 147 : : 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 : : } 168 [ # # ]: 0 : timeout = fuzzed_data_provider.ConsumeBool() ? Consensus::BIP9Deployment::NO_TIMEOUT : fuzzed_data_provider.ConsumeIntegral<int64_t>(); 169 : : } 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 : : } 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 : : 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 : : && stats.elapsed == stats_no_signals.elapsed && stats.count == stats_no_signals.count 258 : : && 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 : : } 303 : : 304 : : // state is where everything interesting is 305 [ # # # # : 0 : switch (state) { # # ] 306 : : 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 : : 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 : : } 319 : 0 : break; 320 : : 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 : : } 327 : 0 : break; 328 : : 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 : : 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 : : } 339 : 0 : break; 340 : : default: 341 : 0 : assert(false); 342 : : } 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 : : } 364 [ # # ]: 0 : } 365 : : } // namespace