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