blockencodings.cpp raw

   1  // Copyright (c) 2025-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 <bench/bench.h>
   6  #include <blockencodings.h>
   7  #include <consensus/amount.h>
   8  #include <kernel/cs_main.h>
   9  #include <net_processing.h>
  10  #include <primitives/block.h>
  11  #include <primitives/transaction.h>
  12  #include <random.h>
  13  #include <script/script.h>
  14  #include <sync.h>
  15  #include <test/util/setup_common.h>
  16  #include <test/util/txmempool.h>
  17  #include <txmempool.h>
  18  #include <uint256.h>
  19  #include <util/check.h>
  20  
  21  #include <algorithm>
  22  #include <array>
  23  #include <cstddef>
  24  #include <memory>
  25  #include <span>
  26  #include <utility>
  27  #include <vector>
  28  
  29  
  30  static void AddTx(const CTransactionRef& tx, const CAmount& fee, CTxMemPool& pool) EXCLUSIVE_LOCKS_REQUIRED(cs_main, pool.cs)
  31  {
  32      LockPoints lp;
  33      TryAddToMempool(pool, CTxMemPoolEntry(tx, fee, /*time=*/0, /*entry_height=*/1, /*entry_sequence=*/0, /*spends_coinbase=*/false, /*sigops_cost=*/4, lp));
  34  }
  35  
  36  namespace {
  37  class BenchCBHAST : public CBlockHeaderAndShortTxIDs
  38  {
  39  private:
  40      static CBlock DummyBlock()
  41      {
  42          CBlock block;
  43          block.nVersion = 5;
  44          block.hashPrevBlock.SetNull();
  45          block.hashMerkleRoot.SetNull();
  46          block.nTime = 1231006505;
  47          block.nBits = 0x1d00ffff;
  48          block.nNonce = 2083236893;
  49          block.fChecked = false;
  50          CMutableTransaction tx;
  51          tx.vin.resize(1);
  52          tx.vout.resize(1);
  53          block.vtx.emplace_back(MakeTransactionRef(tx)); // dummy coinbase
  54          return block;
  55      }
  56  
  57  public:
  58      BenchCBHAST(InsecureRandomContext& rng, int txs) : CBlockHeaderAndShortTxIDs(DummyBlock(), rng.rand64())
  59      {
  60          shorttxids.reserve(txs);
  61          while (txs-- > 0) {
  62              shorttxids.push_back(rng.randbits<SHORTTXIDS_LENGTH*8>());
  63          }
  64      }
  65  };
  66  } // anon namespace
  67  
  68  static void BlockEncodingBench(benchmark::Bench& bench, size_t n_pool, size_t n_extra)
  69  {
  70      const auto testing_setup = MakeNoLogFileContext<const ChainTestingSetup>(ChainType::MAIN);
  71      CTxMemPool& pool = *Assert(testing_setup->m_node.mempool);
  72      InsecureRandomContext rng(11);
  73  
  74      LOCK2(cs_main, pool.cs);
  75  
  76      std::vector<std::pair<Wtxid, CTransactionRef>> extratxn;
  77      extratxn.reserve(n_extra);
  78  
  79      // bump up the size of txs
  80      std::array<std::byte,200> sigspam;
  81      sigspam.fill(std::byte(42));
  82  
  83      // a reasonably large mempool of 50k txs, ~10MB total
  84      std::vector<CTransactionRef> refs;
  85      refs.reserve(n_pool + n_extra);
  86      for (size_t i = 0; i < n_pool + n_extra; ++i) {
  87          CMutableTransaction tx = CMutableTransaction();
  88          tx.vin.resize(1);
  89          tx.vin[0].scriptSig = CScript() << sigspam;
  90          tx.vin[0].scriptWitness.stack.push_back({1});
  91          tx.vout.resize(1);
  92          tx.vout[0].scriptPubKey = CScript() << OP_1 << OP_EQUAL;
  93          tx.vout[0].nValue = i;
  94          refs.push_back(MakeTransactionRef(tx));
  95      }
  96  
  97      // ensure mempool ordering is different to memory ordering of transactions,
  98      // to simulate a mempool that has changed over time
  99      std::shuffle(refs.begin(), refs.end(), rng);
 100  
 101      for (size_t i = 0; i < n_pool; ++i) {
 102          AddTx(refs[i], /*fee=*/refs[i]->vout[0].nValue, pool);
 103      }
 104      for (size_t i = n_pool; i < n_pool + n_extra; ++i) {
 105          extratxn.emplace_back(refs[i]->GetWitnessHash(), refs[i]);
 106      }
 107  
 108      BenchCBHAST cmpctblock{rng, 3000};
 109  
 110      bench.run([&] {
 111          PartiallyDownloadedBlock pdb{&pool};
 112          auto res = pdb.InitData(cmpctblock, extratxn);
 113  
 114          // if there were duplicates the benchmark will be invalid
 115          // (eg, extra txns will be skipped) and we will receive
 116          // READ_STATUS_FAILED
 117          assert(res == READ_STATUS_OK);
 118      });
 119  }
 120  
 121  static void BlockEncodingNoExtra(benchmark::Bench& bench)
 122  {
 123      BlockEncodingBench(bench, 50000, 0);
 124  }
 125  
 126  static void BlockEncodingStdExtra(benchmark::Bench& bench)
 127  {
 128      static_assert(DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN == 100);
 129      BlockEncodingBench(bench, 50000, 100);
 130  }
 131  
 132  static void BlockEncodingLargeExtra(benchmark::Bench& bench)
 133  {
 134      BlockEncodingBench(bench, 50000, 5000);
 135  }
 136  
 137  BENCHMARK(BlockEncodingNoExtra);
 138  BENCHMARK(BlockEncodingStdExtra);
 139  BENCHMARK(BlockEncodingLargeExtra);
 140