partially_downloaded_block.cpp raw

   1  #include <blockencodings.h>
   2  #include <consensus/merkle.h>
   3  #include <consensus/validation.h>
   4  #include <primitives/block.h>
   5  #include <primitives/transaction.h>
   6  #include <test/fuzz/FuzzedDataProvider.h>
   7  #include <test/fuzz/fuzz.h>
   8  #include <test/fuzz/util.h>
   9  #include <test/fuzz/util/mempool.h>
  10  #include <test/util/setup_common.h>
  11  #include <test/util/txmempool.h>
  12  #include <txmempool.h>
  13  #include <util/check.h>
  14  #include <util/time.h>
  15  #include <util/translation.h>
  16  
  17  #include <cstddef>
  18  #include <cstdint>
  19  #include <limits>
  20  #include <memory>
  21  #include <optional>
  22  #include <set>
  23  #include <vector>
  24  
  25  namespace {
  26  const TestingSetup* g_setup;
  27  } // namespace
  28  
  29  void initialize_pdb()
  30  {
  31      static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
  32      g_setup = testing_setup.get();
  33  }
  34  
  35  PartiallyDownloadedBlock::IsBlockMutatedFn FuzzedIsBlockMutated(bool result)
  36  {
  37      return [result](const CBlock& block, bool) {
  38          return result;
  39      };
  40  }
  41  
  42  FUZZ_TARGET(partially_downloaded_block, .init = initialize_pdb)
  43  {
  44      SeedRandomStateForTest(SeedRand::ZEROS);
  45      FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
  46      SetMockTime(ConsumeTime(fuzzed_data_provider));
  47  
  48      auto block{ConsumeDeserializable<CBlock>(fuzzed_data_provider, TX_WITH_WITNESS)};
  49      if (!block || block->vtx.size() == 0 ||
  50          block->vtx.size() >= std::numeric_limits<uint16_t>::max()) {
  51          return;
  52      }
  53  
  54      CBlockHeaderAndShortTxIDs cmpctblock{*block, fuzzed_data_provider.ConsumeIntegral<uint64_t>()};
  55  
  56      bilingual_str error;
  57      CTxMemPool pool{MemPoolOptionsForTest(g_setup->m_node), error};
  58      Assert(error.empty());
  59      PartiallyDownloadedBlock pdb{&pool};
  60  
  61      // Set of available transactions (mempool or extra_txn)
  62      std::set<uint16_t> available;
  63      // The coinbase is always available
  64      available.insert(0);
  65  
  66      std::vector<CTransactionRef> extra_txn;
  67      for (size_t i = 1; i < block->vtx.size(); ++i) {
  68          auto tx{block->vtx[i]};
  69  
  70          bool add_to_extra_txn{fuzzed_data_provider.ConsumeBool()};
  71          bool add_to_mempool{fuzzed_data_provider.ConsumeBool()};
  72  
  73          if (add_to_extra_txn) {
  74              extra_txn.emplace_back(tx);
  75              available.insert(i);
  76          }
  77  
  78          if (add_to_mempool && (!pool.exists(GenTxid::Txid(tx->GetHash()))) && SanityCheckForConsumeTxMemPoolEntry(*tx)) {
  79              LOCK2(cs_main, pool.cs);
  80              AddToMempool(pool, ConsumeTxMemPoolEntry(fuzzed_data_provider, *tx));
  81              available.insert(i);
  82          }
  83      }
  84  
  85      auto init_status{pdb.InitData(cmpctblock, extra_txn)};
  86  
  87      std::vector<CTransactionRef> missing;
  88      // Whether we skipped a transaction that should be included in `missing`.
  89      // FillBlock should never return READ_STATUS_OK if that is the case.
  90      bool skipped_missing{false};
  91      for (size_t i = 0; i < cmpctblock.BlockTxCount(); i++) {
  92          // If init_status == READ_STATUS_OK then a available transaction in the
  93          // compact block (i.e. IsTxAvailable(i) == true) implies that we marked
  94          // that transaction as available above (i.e. available.count(i) > 0).
  95          // The reverse is not true, due to possible compact block short id
  96          // collisions (i.e. available.count(i) > 0 does not imply
  97          // IsTxAvailable(i) == true).
  98          if (init_status == READ_STATUS_OK) {
  99              assert(!pdb.IsTxAvailable(i) || available.count(i) > 0);
 100          }
 101  
 102          bool skip{fuzzed_data_provider.ConsumeBool()};
 103          if (!pdb.IsTxAvailable(i) && !skip) {
 104              missing.push_back(block->vtx[i]);
 105          }
 106  
 107          skipped_missing |= (!pdb.IsTxAvailable(i) && skip);
 108      }
 109  
 110      bool segwit_active{fuzzed_data_provider.ConsumeBool()};
 111  
 112      // Mock IsBlockMutated
 113      bool fail_block_mutated{fuzzed_data_provider.ConsumeBool()};
 114      pdb.m_check_block_mutated_mock = FuzzedIsBlockMutated(fail_block_mutated);
 115  
 116      CBlock reconstructed_block;
 117      auto fill_status{pdb.FillBlock(reconstructed_block, missing, segwit_active)};
 118      switch (fill_status) {
 119      case READ_STATUS_OK:
 120          assert(!skipped_missing);
 121          assert(!fail_block_mutated);
 122          assert(block->GetHash() == reconstructed_block.GetHash());
 123          break;
 124      case READ_STATUS_FAILED:
 125          assert(fail_block_mutated);
 126          break;
 127      case READ_STATUS_INVALID:
 128          break;
 129      }
 130  }
 131