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