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 <kernel/disconnected_transactions.h>
   6  
   7  #include <cassert>
   8  #include <core_memusage.h>
   9  #include <memusage.h>
  10  #include <primitives/transaction.h>
  11  #include <util/hasher.h>
  12  
  13  #include <memory>
  14  #include <utility>
  15  
  16  // It's almost certainly a logic bug if we don't clear out queuedTx before
  17  // destruction, as we add to it while disconnecting blocks, and then we
  18  // need to re-process remaining transactions to ensure mempool consistency.
  19  // For now, assert() that we've emptied out this object on destruction.
  20  // This assert() can always be removed if the reorg-processing code were
  21  // to be refactored such that this assumption is no longer true (for
  22  // instance if there was some other way we cleaned up the mempool after a
  23  // reorg, besides draining this object).
  24  DisconnectedBlockTransactions::~DisconnectedBlockTransactions()
  25  {
  26      assert(queuedTx.empty());
  27      assert(iters_by_txid.empty());
  28      assert(cachedInnerUsage == 0);
  29  }
  30  
  31  std::vector<CTransactionRef> DisconnectedBlockTransactions::LimitMemoryUsage()
  32  {
  33      std::vector<CTransactionRef> evicted;
  34  
  35      while (!queuedTx.empty() && DynamicMemoryUsage() > m_max_mem_usage) {
  36          evicted.emplace_back(queuedTx.front());
  37          cachedInnerUsage -= RecursiveDynamicUsage(queuedTx.front());
  38          iters_by_txid.erase(queuedTx.front()->GetHash());
  39          queuedTx.pop_front();
  40      }
  41      return evicted;
  42  }
  43  
  44  size_t DisconnectedBlockTransactions::DynamicMemoryUsage() const
  45  {
  46      return cachedInnerUsage + memusage::DynamicUsage(iters_by_txid) + memusage::DynamicUsage(queuedTx);
  47  }
  48  
  49  [[nodiscard]] std::vector<CTransactionRef> DisconnectedBlockTransactions::AddTransactionsFromBlock(const std::vector<CTransactionRef>& vtx)
  50  {
  51      iters_by_txid.reserve(iters_by_txid.size() + vtx.size());
  52      for (auto block_it = vtx.rbegin(); block_it != vtx.rend(); ++block_it) {
  53          auto it = queuedTx.insert(queuedTx.end(), *block_it);
  54          auto [_, inserted] = iters_by_txid.emplace((*block_it)->GetHash(), it);
  55          assert(inserted); // callers may never pass multiple transactions with the same txid
  56          cachedInnerUsage += RecursiveDynamicUsage(*block_it);
  57      }
  58      return LimitMemoryUsage();
  59  }
  60  
  61  void DisconnectedBlockTransactions::removeForBlock(const std::vector<CTransactionRef>& vtx)
  62  {
  63      // Short-circuit in the common case of a block being added to the tip
  64      if (queuedTx.empty()) {
  65          return;
  66      }
  67      for (const auto& tx : vtx) {
  68          auto iter = iters_by_txid.find(tx->GetHash());
  69          if (iter != iters_by_txid.end()) {
  70              auto list_iter = iter->second;
  71              iters_by_txid.erase(iter);
  72              cachedInnerUsage -= RecursiveDynamicUsage(*list_iter);
  73              queuedTx.erase(list_iter);
  74          }
  75      }
  76  }
  77  
  78  void DisconnectedBlockTransactions::clear()
  79  {
  80      cachedInnerUsage = 0;
  81      iters_by_txid.clear();
  82      queuedTx.clear();
  83  }
  84  
  85  std::list<CTransactionRef> DisconnectedBlockTransactions::take()
  86  {
  87      std::list<CTransactionRef> ret = std::move(queuedTx);
  88      clear();
  89      return ret;
  90  }
  91