Branch data Line data Source code
1 : : // Copyright (c) 2023 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 <assert.h> 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 : 0 : DisconnectedBlockTransactions::~DisconnectedBlockTransactions() 25 : : { 26 [ # # ]: 0 : assert(queuedTx.empty()); 27 [ # # ]: 0 : assert(iters_by_txid.empty()); 28 [ # # ]: 0 : assert(cachedInnerUsage == 0); 29 : 0 : } 30 : : 31 : 0 : std::vector<CTransactionRef> DisconnectedBlockTransactions::LimitMemoryUsage() 32 : : { 33 : 0 : std::vector<CTransactionRef> evicted; 34 : : 35 [ # # ][ # # ]: 0 : while (!queuedTx.empty() && DynamicMemoryUsage() > m_max_mem_usage) { [ # # ] 36 [ # # ]: 0 : evicted.emplace_back(queuedTx.front()); 37 [ # # ]: 0 : cachedInnerUsage -= RecursiveDynamicUsage(queuedTx.front()); 38 [ # # ][ # # ]: 0 : iters_by_txid.erase(queuedTx.front()->GetHash()); [ # # ] 39 : 0 : queuedTx.pop_front(); 40 : : } 41 : 0 : return evicted; 42 [ # # ]: 0 : } 43 : : 44 : 0 : size_t DisconnectedBlockTransactions::DynamicMemoryUsage() const 45 : : { 46 : 0 : return cachedInnerUsage + memusage::DynamicUsage(iters_by_txid) + memusage::DynamicUsage(queuedTx); 47 : : } 48 : : 49 : 0 : [[nodiscard]] std::vector<CTransactionRef> DisconnectedBlockTransactions::AddTransactionsFromBlock(const std::vector<CTransactionRef>& vtx) 50 : : { 51 : 0 : iters_by_txid.reserve(iters_by_txid.size() + vtx.size()); 52 [ # # ]: 0 : for (auto block_it = vtx.rbegin(); block_it != vtx.rend(); ++block_it) { 53 : 0 : auto it = queuedTx.insert(queuedTx.end(), *block_it); 54 : 0 : auto [_, inserted] = iters_by_txid.emplace((*block_it)->GetHash(), it); 55 [ # # ]: 0 : assert(inserted); // callers may never pass multiple transactions with the same txid 56 : 0 : cachedInnerUsage += RecursiveDynamicUsage(*block_it); 57 : 0 : } 58 : 0 : return LimitMemoryUsage(); 59 : : } 60 : : 61 : 0 : 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 [ # # ]: 0 : if (queuedTx.empty()) { 65 : 0 : return; 66 : : } 67 [ # # ]: 0 : for (const auto& tx : vtx) { 68 : 0 : auto iter = iters_by_txid.find(tx->GetHash()); 69 [ # # ]: 0 : if (iter != iters_by_txid.end()) { 70 : 0 : auto list_iter = iter->second; 71 : 0 : iters_by_txid.erase(iter); 72 : 0 : cachedInnerUsage -= RecursiveDynamicUsage(*list_iter); 73 : 0 : queuedTx.erase(list_iter); 74 : 0 : } 75 : : } 76 : 0 : } 77 : : 78 : 0 : void DisconnectedBlockTransactions::clear() 79 : : { 80 : 0 : cachedInnerUsage = 0; 81 : 0 : iters_by_txid.clear(); 82 : 0 : queuedTx.clear(); 83 : 0 : } 84 : : 85 : 0 : std::list<CTransactionRef> DisconnectedBlockTransactions::take() 86 : : { 87 : 0 : std::list<CTransactionRef> ret = std::move(queuedTx); 88 : 0 : clear(); 89 : 0 : return ret; 90 [ # # ]: 0 : }