disconnected_transactions.cpp raw

   1  // Copyright (c) 2023-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 <kernel/disconnected_transactions.h>
   7  #include <primitives/block.h>
   8  #include <primitives/transaction.h>
   9  #include <script/script.h>
  10  #include <test/util/setup_common.h>
  11  #include <util/check.h>
  12  
  13  #include <algorithm>
  14  #include <cstddef>
  15  #include <cstdint>
  16  #include <iterator>
  17  #include <vector>
  18  
  19  constexpr size_t BLOCK_VTX_COUNT{4000};
  20  constexpr size_t BLOCK_VTX_COUNT_10PERCENT{400};
  21  
  22  using BlockTxns = decltype(CBlock::vtx);
  23  
  24  /** Reorg where 1 block is disconnected and 2 blocks are connected. */
  25  struct ReorgTxns {
  26      /** Disconnected block. */
  27      BlockTxns disconnected_txns;
  28      /** First connected block. */
  29      BlockTxns connected_txns_1;
  30      /** Second connected block, new chain tip. Has no overlap with disconnected_txns. */
  31      BlockTxns connected_txns_2;
  32      /** Transactions shared between disconnected_txns and connected_txns_1. */
  33      size_t num_shared;
  34  };
  35  
  36  static BlockTxns CreateRandomTransactions(size_t num_txns)
  37  {
  38      // Ensure every transaction has a different txid by having each one spend the previous one.
  39      static Txid prevout_hash{};
  40  
  41      BlockTxns txns;
  42      txns.reserve(num_txns);
  43      // Simplest spk for every tx
  44      CScript spk = CScript() << OP_TRUE;
  45      for (uint32_t i = 0; i < num_txns; ++i) {
  46          CMutableTransaction tx;
  47          tx.vin.emplace_back(COutPoint{prevout_hash, 0});
  48          tx.vout.emplace_back(CENT, spk);
  49          auto ptx{MakeTransactionRef(tx)};
  50          txns.emplace_back(ptx);
  51          prevout_hash = ptx->GetHash();
  52      }
  53      return txns;
  54  }
  55  
  56  /** Creates blocks for a Reorg, each with BLOCK_VTX_COUNT transactions. Between the disconnected
  57   * block and the first connected block, there will be num_not_shared transactions that are
  58   * different, and all other transactions the exact same. The second connected block has all unique
  59   * transactions. This is to simulate a reorg in which all but num_not_shared transactions are
  60   * confirmed in the new chain. */
  61  static ReorgTxns CreateBlocks(size_t num_not_shared)
  62  {
  63      auto num_shared{BLOCK_VTX_COUNT - num_not_shared};
  64      const auto shared_txns{CreateRandomTransactions(/*num_txns=*/num_shared)};
  65  
  66      // Create different sets of transactions...
  67      auto disconnected_block_txns{CreateRandomTransactions(/*num_txns=*/num_not_shared)};
  68      std::copy(shared_txns.begin(), shared_txns.end(), std::back_inserter(disconnected_block_txns));
  69  
  70      auto connected_block_txns{CreateRandomTransactions(/*num_txns=*/num_not_shared)};
  71      std::copy(shared_txns.begin(), shared_txns.end(), std::back_inserter(connected_block_txns));
  72  
  73      assert(disconnected_block_txns.size() == BLOCK_VTX_COUNT);
  74      assert(connected_block_txns.size() == BLOCK_VTX_COUNT);
  75  
  76      return ReorgTxns{/*disconnected_txns=*/disconnected_block_txns,
  77                       /*connected_txns_1=*/connected_block_txns,
  78                       /*connected_txns_2=*/CreateRandomTransactions(BLOCK_VTX_COUNT),
  79                       /*num_shared=*/num_shared};
  80  }
  81  
  82  static void Reorg(const ReorgTxns& reorg)
  83  {
  84      DisconnectedBlockTransactions disconnectpool{MAX_DISCONNECTED_TX_POOL_BYTES};
  85      // Disconnect block
  86      const auto evicted = disconnectpool.AddTransactionsFromBlock(reorg.disconnected_txns);
  87      assert(evicted.empty());
  88  
  89      // Connect first block
  90      disconnectpool.removeForBlock(reorg.connected_txns_1);
  91      // Connect new tip
  92      disconnectpool.removeForBlock(reorg.connected_txns_2);
  93  
  94      // Sanity Check
  95      assert(disconnectpool.size() == BLOCK_VTX_COUNT - reorg.num_shared);
  96  
  97      disconnectpool.clear();
  98  }
  99  
 100  /** Add transactions from DisconnectedBlockTransactions, remove all but one (the disconnected
 101   * block's coinbase transaction) of them, and then pop from the front until empty. This is a reorg
 102   * in which all of the non-coinbase transactions in the disconnected chain also exist in the new
 103   * chain. */
 104  static void AddAndRemoveDisconnectedBlockTransactionsAll(benchmark::Bench& bench)
 105  {
 106      const auto chains{CreateBlocks(/*num_not_shared=*/1)};
 107      assert(chains.num_shared == BLOCK_VTX_COUNT - 1);
 108  
 109      bench.minEpochIterations(10).run([&]() {
 110          Reorg(chains);
 111      });
 112  }
 113  
 114  /** Add transactions from DisconnectedBlockTransactions, remove 90% of them, and then pop from the front until empty. */
 115  static void AddAndRemoveDisconnectedBlockTransactions90(benchmark::Bench& bench)
 116  {
 117      const auto chains{CreateBlocks(/*num_not_shared=*/BLOCK_VTX_COUNT_10PERCENT)};
 118      assert(chains.num_shared == BLOCK_VTX_COUNT - BLOCK_VTX_COUNT_10PERCENT);
 119  
 120      bench.minEpochIterations(10).run([&]() {
 121          Reorg(chains);
 122      });
 123  }
 124  
 125  /** Add transactions from DisconnectedBlockTransactions, remove 10% of them, and then pop from the front until empty. */
 126  static void AddAndRemoveDisconnectedBlockTransactions10(benchmark::Bench& bench)
 127  {
 128      const auto chains{CreateBlocks(/*num_not_shared=*/BLOCK_VTX_COUNT - BLOCK_VTX_COUNT_10PERCENT)};
 129      assert(chains.num_shared == BLOCK_VTX_COUNT_10PERCENT);
 130  
 131      bench.minEpochIterations(10).run([&]() {
 132          Reorg(chains);
 133      });
 134  }
 135  
 136  BENCHMARK(AddAndRemoveDisconnectedBlockTransactionsAll);
 137  BENCHMARK(AddAndRemoveDisconnectedBlockTransactions90);
 138  BENCHMARK(AddAndRemoveDisconnectedBlockTransactions10);
 139