/root/bitcoin/src/test/fuzz/versionbits.cpp
| Line | Count | Source | 
| 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 |  |  | 
| 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 |  | class TestConditionChecker : public VersionBitsConditionChecker | 
| 25 |  | { | 
| 26 |  | private: | 
| 27 |  |     mutable ThresholdConditionCache m_cache; | 
| 28 |  |  | 
| 29 |  | public: | 
| 30 | 0 |     TestConditionChecker(const Consensus::BIP9Deployment& dep) : VersionBitsConditionChecker{dep} | 
| 31 | 0 |     { | 
| 32 | 0 |         assert(dep.period > 0); | 
| 33 | 0 |         assert(dep.threshold <= dep.period); | 
| 34 | 0 |         assert(0 <= dep.bit && dep.bit < 32 && dep.bit < VERSIONBITS_NUM_BITS); | 
| 35 | 0 |         assert(0 <= dep.min_activation_height); | 
| 36 | 0 |     } | 
| 37 |  |  | 
| 38 | 0 |     ThresholdState GetStateFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateFor(pindexPrev, m_cache); } | 
| 39 | 0 |     int GetStateSinceHeightFor(const CBlockIndex* pindexPrev) const { return AbstractThresholdConditionChecker::GetStateSinceHeightFor(pindexPrev, m_cache); } | 
| 40 |  | }; | 
| 41 |  |  | 
| 42 |  | /** Track blocks mined for test */ | 
| 43 |  | 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 |  |     Blocks(uint32_t start_time, uint32_t interval, int32_t signal, int32_t no_signal) | 
| 54 | 0 |         : m_start_time{start_time}, m_interval{interval}, m_signal{signal}, m_no_signal{no_signal} {} | 
| 55 |  |  | 
| 56 | 0 |     size_t size() const { return m_blocks.size(); } | 
| 57 |  |  | 
| 58 |  |     CBlockIndex* tip() const | 
| 59 | 0 |     { | 
| 60 | 0 |         return m_blocks.empty() ? nullptr : m_blocks.back().get(); | 
| 61 | 0 |     } | 
| 62 |  |  | 
| 63 |  |     CBlockIndex* mine_block(bool signal) | 
| 64 | 0 |     { | 
| 65 | 0 |         CBlockHeader header; | 
| 66 | 0 |         header.nVersion = signal ? m_signal : m_no_signal; | 
| 67 | 0 |         header.nTime = m_start_time + m_blocks.size() * m_interval; | 
| 68 | 0 |         header.nBits = 0x1d00ffff; | 
| 69 |  | 
 | 
| 70 | 0 |         auto current_block = std::make_unique<CBlockIndex>(header); | 
| 71 | 0 |         current_block->pprev = tip(); | 
| 72 | 0 |         current_block->nHeight = m_blocks.size(); | 
| 73 | 0 |         current_block->BuildSkip(); | 
| 74 |  | 
 | 
| 75 | 0 |         return m_blocks.emplace_back(std::move(current_block)).get(); | 
| 76 | 0 |     } | 
| 77 |  | }; | 
| 78 |  |  | 
| 79 |  | std::unique_ptr<const CChainParams> g_params; | 
| 80 |  |  | 
| 81 |  | void initialize() | 
| 82 | 0 | { | 
| 83 |  |     // this is actually comparatively slow, so only do it once | 
| 84 | 0 |     g_params = CreateChainParams(ArgsManager{}, ChainType::MAIN); | 
| 85 | 0 |     assert(g_params != nullptr); | 
| 86 | 0 | } | 
| 87 |  |  | 
| 88 |  | constexpr uint32_t MAX_START_TIME = 4102444800; // 2100-01-01 | 
| 89 |  |  | 
| 90 |  | FUZZ_TARGET(versionbits, .init = initialize) | 
| 91 | 0 | { | 
| 92 | 0 |     const CChainParams& params = *g_params; | 
| 93 | 0 |     const int64_t interval = params.GetConsensus().nPowTargetSpacing; | 
| 94 | 0 |     assert(interval > 1); // need to be able to halve it | 
| 95 | 0 |     assert(interval < std::numeric_limits<int32_t>::max()); | 
| 96 |  |  | 
| 97 | 0 |     FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); | 
| 98 |  |  | 
| 99 |  |     // making period/max_periods larger slows these tests down significantly | 
| 100 | 0 |     const uint32_t period = 32; | 
| 101 | 0 |     const size_t max_periods = 16; | 
| 102 | 0 |     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 | 0 |     assert(std::numeric_limits<uint32_t>::max() - MAX_START_TIME > interval * max_blocks); | 
| 107 |  |  | 
| 108 | 0 |     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 | 0 |     const int32_t ver_signal = fuzzed_data_provider.ConsumeIntegral<int32_t>(); | 
| 112 | 0 |     const int32_t ver_nosignal = fuzzed_data_provider.ConsumeIntegral<int32_t>(); | 
| 113 | 0 |     if (ver_nosignal < 0) return; // negative values are uninteresting | 
| 114 |  |  | 
| 115 |  |     // Now that we have chosen time and versions, setup to mine blocks | 
| 116 | 0 |     Blocks blocks(block_start_time, interval, ver_signal, ver_nosignal); | 
| 117 |  | 
 | 
| 118 | 0 |     const bool always_active_test = fuzzed_data_provider.ConsumeBool(); | 
| 119 | 0 |     const bool never_active_test = !always_active_test && fuzzed_data_provider.ConsumeBool(); | 
| 120 |  | 
 | 
| 121 | 0 |     const Consensus::BIP9Deployment dep{[&]() { | 
| 122 | 0 |         Consensus::BIP9Deployment dep; | 
| 123 | 0 |         dep.period = period; | 
| 124 |  | 
 | 
| 125 | 0 |         dep.threshold = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, period); | 
| 126 | 0 |         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 | 0 |         dep.bit = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, VERSIONBITS_NUM_BITS - 1); | 
| 130 |  | 
 | 
| 131 | 0 |         if (always_active_test) { | 
| 132 | 0 |             dep.nStartTime = Consensus::BIP9Deployment::ALWAYS_ACTIVE; | 
| 133 | 0 |             dep.nTimeout = fuzzed_data_provider.ConsumeBool() ? Consensus::BIP9Deployment::NO_TIMEOUT : fuzzed_data_provider.ConsumeIntegral<int64_t>(); | 
| 134 | 0 |         } else if (never_active_test) { | 
| 135 | 0 |             dep.nStartTime = Consensus::BIP9Deployment::NEVER_ACTIVE; | 
| 136 | 0 |             dep.nTimeout = fuzzed_data_provider.ConsumeBool() ? Consensus::BIP9Deployment::NO_TIMEOUT : fuzzed_data_provider.ConsumeIntegral<int64_t>(); | 
| 137 | 0 |         } else { | 
| 138 |  |             // pick the timestamp to switch based on a block | 
| 139 |  |             // note states will change *after* these blocks because mediantime lags | 
| 140 | 0 |             int start_block = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, period * (max_periods - 3)); | 
| 141 | 0 |             int end_block = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, period * (max_periods - 3)); | 
| 142 |  | 
 | 
| 143 | 0 |             dep.nStartTime = block_start_time + start_block * interval; | 
| 144 | 0 |             dep.nTimeout = block_start_time + end_block * interval; | 
| 145 |  |  | 
| 146 |  |             // allow for times to not exactly match a block | 
| 147 | 0 |             if (fuzzed_data_provider.ConsumeBool()) dep.nStartTime += interval / 2; | 
| 148 | 0 |             if (fuzzed_data_provider.ConsumeBool()) dep.nTimeout += interval / 2; | 
| 149 | 0 |         } | 
| 150 | 0 |         dep.min_activation_height = fuzzed_data_provider.ConsumeIntegralInRange<int>(0, period * max_periods); | 
| 151 | 0 |         return dep; | 
| 152 | 0 |     }()}; | 
| 153 | 0 |     TestConditionChecker checker(dep); | 
| 154 |  |  | 
| 155 |  |     // Early exit if the versions don't signal sensibly for the deployment | 
| 156 | 0 |     if (!checker.Condition(ver_signal)) return; | 
| 157 | 0 |     if (checker.Condition(ver_nosignal)) return; | 
| 158 |  |  | 
| 159 |  |     // TOP_BITS should ensure version will be positive and meet min | 
| 160 |  |     // version requirement | 
| 161 | 0 |     assert(ver_signal > 0); | 
| 162 | 0 |     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 | 0 |     const uint32_t signalling_mask = fuzzed_data_provider.ConsumeIntegral<uint32_t>(); | 
| 178 |  |  | 
| 179 |  |     // mine prior periods | 
| 180 | 0 |     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 | 0 |         bool signal = fuzzed_data_provider.ConsumeBool(); | 
| 183 | 0 |         for (uint32_t b = 0; b < period; ++b) { | 
| 184 | 0 |             blocks.mine_block(signal); | 
| 185 | 0 |         } | 
| 186 |  |  | 
| 187 |  |         // don't risk exceeding max_blocks or times may wrap around | 
| 188 | 0 |         if (blocks.size() + 2 * period > max_blocks) break; | 
| 189 | 0 |     } | 
| 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 | 0 |     uint32_t blocks_sig = 0; | 
| 196 |  |  | 
| 197 |  |     // get the info for the first block of the period | 
| 198 | 0 |     CBlockIndex* prev = blocks.tip(); | 
| 199 | 0 |     const int exp_since = checker.GetStateSinceHeightFor(prev); | 
| 200 | 0 |     const ThresholdState exp_state = checker.GetStateFor(prev); | 
| 201 |  |  | 
| 202 |  |     // get statistics from end of previous period, then reset | 
| 203 | 0 |     BIP9Stats last_stats; | 
| 204 | 0 |     last_stats.period = period; | 
| 205 | 0 |     last_stats.threshold = dep.threshold; | 
| 206 | 0 |     last_stats.count = last_stats.elapsed = 0; | 
| 207 | 0 |     last_stats.possible = (period >= dep.threshold); | 
| 208 | 0 |     std::vector<bool> last_signals{}; | 
| 209 |  | 
 | 
| 210 | 0 |     int prev_next_height = (prev == nullptr ? 0 : prev->nHeight + 1); | 
| 211 | 0 |     assert(exp_since <= prev_next_height); | 
| 212 |  |  | 
| 213 |  |     // mine (period-1) blocks and check state | 
| 214 | 0 |     for (uint32_t b = 1; b < period; ++b) { | 
| 215 | 0 |         const bool signal = (signalling_mask >> (b % 32)) & 1; | 
| 216 | 0 |         if (signal) ++blocks_sig; | 
| 217 |  | 
 | 
| 218 | 0 |         CBlockIndex* current_block = blocks.mine_block(signal); | 
| 219 |  |  | 
| 220 |  |         // verify that signalling attempt was interpreted correctly | 
| 221 | 0 |         assert(checker.Condition(current_block->nVersion) == signal); | 
| 222 |  |  | 
| 223 |  |         // state and since don't change within the period | 
| 224 | 0 |         const ThresholdState state = checker.GetStateFor(current_block); | 
| 225 | 0 |         const int since = checker.GetStateSinceHeightFor(current_block); | 
| 226 | 0 |         assert(state == exp_state); | 
| 227 | 0 |         assert(since == exp_since); | 
| 228 |  |  | 
| 229 |  |         // check that after mining this block stats change as expected | 
| 230 | 0 |         std::vector<bool> signals; | 
| 231 | 0 |         const BIP9Stats stats = checker.GetStateStatisticsFor(current_block, &signals); | 
| 232 | 0 |         const BIP9Stats stats_no_signals = checker.GetStateStatisticsFor(current_block); | 
| 233 | 0 |         assert(stats.period == stats_no_signals.period && stats.threshold == stats_no_signals.threshold | 
| 234 | 0 |                && stats.elapsed == stats_no_signals.elapsed && stats.count == stats_no_signals.count | 
| 235 | 0 |                && stats.possible == stats_no_signals.possible); | 
| 236 |  |  | 
| 237 | 0 |         assert(stats.period == period); | 
| 238 | 0 |         assert(stats.threshold == dep.threshold); | 
| 239 | 0 |         assert(stats.elapsed == b); | 
| 240 | 0 |         assert(stats.count == last_stats.count + (signal ? 1 : 0)); | 
| 241 | 0 |         assert(stats.possible == (stats.count + period >= stats.elapsed + dep.threshold)); | 
| 242 | 0 |         last_stats = stats; | 
| 243 |  | 
 | 
| 244 | 0 |         assert(signals.size() == last_signals.size() + 1); | 
| 245 | 0 |         assert(signals.back() == signal); | 
| 246 | 0 |         last_signals.push_back(signal); | 
| 247 | 0 |         assert(signals == last_signals); | 
| 248 | 0 |     } | 
| 249 |  |  | 
| 250 | 0 |     if (exp_state == ThresholdState::STARTED) { | 
| 251 |  |         // double check that stats.possible is sane | 
| 252 | 0 |         if (blocks_sig >= dep.threshold - 1) assert(last_stats.possible); | 
| 253 | 0 |     } | 
| 254 |  |  | 
| 255 |  |     // mine the final block | 
| 256 | 0 |     bool signal = (signalling_mask >> (period % 32)) & 1; | 
| 257 | 0 |     if (signal) ++blocks_sig; | 
| 258 | 0 |     CBlockIndex* current_block = blocks.mine_block(signal); | 
| 259 | 0 |     assert(checker.Condition(current_block->nVersion) == signal); | 
| 260 |  |  | 
| 261 | 0 |     const BIP9Stats stats = checker.GetStateStatisticsFor(current_block); | 
| 262 | 0 |     assert(stats.period == period); | 
| 263 | 0 |     assert(stats.threshold == dep.threshold); | 
| 264 | 0 |     assert(stats.elapsed == period); | 
| 265 | 0 |     assert(stats.count == blocks_sig); | 
| 266 | 0 |     assert(stats.possible == (stats.count + period >= stats.elapsed + dep.threshold)); | 
| 267 |  |  | 
| 268 |  |     // More interesting is whether the state changed. | 
| 269 | 0 |     const ThresholdState state = checker.GetStateFor(current_block); | 
| 270 | 0 |     const int since = checker.GetStateSinceHeightFor(current_block); | 
| 271 |  |  | 
| 272 |  |     // since is straightforward: | 
| 273 | 0 |     assert(since % period == 0); | 
| 274 | 0 |     assert(0 <= since && since <= current_block->nHeight + 1); | 
| 275 | 0 |     if (state == exp_state) { | 
| 276 | 0 |         assert(since == exp_since); | 
| 277 | 0 |     } else { | 
| 278 | 0 |         assert(since == current_block->nHeight + 1); | 
| 279 | 0 |     } | 
| 280 |  |  | 
| 281 |  |     // state is where everything interesting is | 
| 282 | 0 |     switch (state) { | 
| 283 | 0 |     case ThresholdState::DEFINED: | 
| 284 | 0 |         assert(since == 0); | 
| 285 | 0 |         assert(exp_state == ThresholdState::DEFINED); | 
| 286 | 0 |         assert(current_block->GetMedianTimePast() < dep.nStartTime); | 
| 287 | 0 |         break; | 
| 288 | 0 |     case ThresholdState::STARTED: | 
| 289 | 0 |         assert(current_block->GetMedianTimePast() >= dep.nStartTime); | 
| 290 | 0 |         if (exp_state == ThresholdState::STARTED) { | 
| 291 | 0 |             assert(blocks_sig < dep.threshold); | 
| 292 | 0 |             assert(current_block->GetMedianTimePast() < dep.nTimeout); | 
| 293 | 0 |         } else { | 
| 294 | 0 |             assert(exp_state == ThresholdState::DEFINED); | 
| 295 | 0 |         } | 
| 296 | 0 |         break; | 
| 297 | 0 |     case ThresholdState::LOCKED_IN: | 
| 298 | 0 |         if (exp_state == ThresholdState::LOCKED_IN) { | 
| 299 | 0 |             assert(current_block->nHeight + 1 < dep.min_activation_height); | 
| 300 | 0 |         } else { | 
| 301 | 0 |             assert(exp_state == ThresholdState::STARTED); | 
| 302 | 0 |             assert(blocks_sig >= dep.threshold); | 
| 303 | 0 |         } | 
| 304 | 0 |         break; | 
| 305 | 0 |     case ThresholdState::ACTIVE: | 
| 306 | 0 |         assert(always_active_test || dep.min_activation_height <= current_block->nHeight + 1); | 
| 307 | 0 |         assert(exp_state == ThresholdState::ACTIVE || exp_state == ThresholdState::LOCKED_IN); | 
| 308 | 0 |         break; | 
| 309 | 0 |     case ThresholdState::FAILED: | 
| 310 | 0 |         assert(never_active_test || current_block->GetMedianTimePast() >= dep.nTimeout); | 
| 311 | 0 |         if (exp_state == ThresholdState::STARTED) { | 
| 312 | 0 |             assert(blocks_sig < dep.threshold); | 
| 313 | 0 |         } else { | 
| 314 | 0 |             assert(exp_state == ThresholdState::FAILED); | 
| 315 | 0 |         } | 
| 316 | 0 |         break; | 
| 317 | 0 |     default: | 
| 318 | 0 |         assert(false); | 
| 319 | 0 |     } | 
| 320 |  |  | 
| 321 | 0 |     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 | 
| 323 | 0 |         assert(state == ThresholdState::ACTIVE || state == ThresholdState::FAILED); | 
| 324 | 0 |     } | 
| 325 |  |  | 
| 326 | 0 |     if (always_active_test) { | 
| 327 |  |         // "always active" has additional restrictions | 
| 328 | 0 |         assert(state == ThresholdState::ACTIVE); | 
| 329 | 0 |         assert(exp_state == ThresholdState::ACTIVE); | 
| 330 | 0 |         assert(since == 0); | 
| 331 | 0 |     } else if (never_active_test) { | 
| 332 |  |         // "never active" does too | 
| 333 | 0 |         assert(state == ThresholdState::FAILED); | 
| 334 | 0 |         assert(exp_state == ThresholdState::FAILED); | 
| 335 | 0 |         assert(since == 0); | 
| 336 | 0 |     } else { | 
| 337 |  |         // for signalled deployments, the initial state is always DEFINED | 
| 338 | 0 |         assert(since > 0 || state == ThresholdState::DEFINED); | 
| 339 | 0 |         assert(exp_since > 0 || exp_state == ThresholdState::DEFINED); | 
| 340 | 0 |     } | 
| 341 | 0 | } | 
| 342 |  | } // namespace |