Bitcoin ABC 0.30.5
P2P Digital Currency
disconnected_transactions.cpp
Go to the documentation of this file.
1// Copyright (c) 2023 The Bitcoin Core developers
2// Copyright (c) 2024 The Bitcoin developers
3// Distributed under the MIT software license, see the accompanying
4// file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
7
8#include <chain.h>
11#include <reverse_iterator.h>
12#include <sync.h>
13#include <validation.h>
14#include <validationinterface.h>
15
18
21 if (auto it = txInfo.find(tx->GetId()); it != txInfo.end()) {
22 return &it->second;
23 }
24
25 return nullptr;
26}
27
29 AssertLockHeld(pool.cs);
30 // addForBlock's algorithm sorts a vector of transactions back into
31 // topological order. We use it in a separate object to create a valid
32 // ordering of all mempool transactions, which we then splice in front of
33 // the current queuedTx. This results in a valid sequence of transactions to
34 // be reprocessed in updateMempoolForReorg.
35
36 // We create vtx in order of the entry_id index to facilitate for
37 // addForBlocks (which iterates in reverse order), as vtx probably end in
38 // the correct ordering for queuedTx.
39 std::vector<CTransactionRef> vtx;
40
41 vtx.reserve(pool.mapTx.size());
42 txInfo.reserve(pool.mapTx.size());
43 for (const CTxMemPoolEntryRef &e : pool.mapTx.get<entry_id>()) {
44 vtx.push_back(e->GetSharedTx());
45 // save entry time, feeDelta, and height for use in
46 // updateMempoolForReorg()
47 txInfo.try_emplace(e->GetTx().GetId(), e->GetTime(),
48 e->GetModifiedFee() - e->GetFee(), e->GetHeight());
49 }
50 for (const CTxMemPoolEntryRef &e :
51 reverse_iterate(pool.mapTx.get<entry_id>())) {
52 // Notify all observers of this (possibly temporary) removal. This is
53 // necessary for tracking the transactions that are removed from the
54 // mempool during a reorg and can't be added back due to missing parent.
55 // Transactions that are added back to the mempool will trigger another
56 // notification. Make sure to notify in reverse topological order,
57 // children first.
59 e->GetSharedTx(), MemPoolRemovalReason::REORG,
61 }
62 pool.clear();
63
64 // Use addForBlocks to sort the transactions and then splice them in front
65 // of queuedTx
66 DisconnectedBlockTransactions orderedTxnPool;
67 orderedTxnPool.addForBlock(vtx, pool);
68 cachedInnerUsage += orderedTxnPool.cachedInnerUsage;
69 queuedTx.get<insertion_order>().splice(
70 queuedTx.get<insertion_order>().begin(),
71 orderedTxnPool.queuedTx.get<insertion_order>());
72
73 // We limit memory usage because we can't know if more blocks will be
74 // disconnected
76 // Drop the earliest entry which, by definition, has no children
77 removeEntry(queuedTx.get<insertion_order>().begin());
78 }
79}
80
82 const std::vector<CTransactionRef> &vtx, CTxMemPool &pool) {
83 AssertLockHeld(pool.cs);
84 for (const auto &tx : reverse_iterate(vtx)) {
85 // If we already added it, just skip.
86 auto it = queuedTx.find(tx->GetId());
87 if (it != queuedTx.end()) {
88 continue;
89 }
90
91 // Insert the transaction into the pool.
93
94 // Fill in the set of parents.
95 std::unordered_set<TxId, SaltedTxIdHasher> parents;
96 for (const CTxIn &in : tx->vin) {
97 parents.insert(in.prevout.GetTxId());
98 }
99
100 // In order to make sure we keep things in topological order, we check
101 // if we already know of the parent of the current transaction. If so,
102 // we remove them from the set and then add them back.
103 while (parents.size() > 0) {
104 std::unordered_set<TxId, SaltedTxIdHasher> worklist(
105 std::move(parents));
106 parents.clear();
107
108 for (const TxId &txid : worklist) {
109 // If we do not have that txid in the set, nothing needs to be
110 // done.
111 auto pit = queuedTx.find(txid);
112 if (pit == queuedTx.end()) {
113 continue;
114 }
115
116 // We have parent in our set, we reinsert them at the right
117 // position.
118 const CTransactionRef ptx = *pit;
119 queuedTx.erase(pit);
120 queuedTx.insert(ptx);
121
122 // And we make sure ancestors are covered.
123 for (const CTxIn &in : ptx->vin) {
124 parents.insert(in.prevout.GetTxId());
125 }
126 }
127 }
128 }
129
130 // Keep the size under control.
132 // Drop the earliest entry, and remove its children from the
133 // mempool.
134 auto it = queuedTx.get<insertion_order>().begin();
136 removeEntry(it);
137 }
138}
139
141 const std::vector<CTransactionRef> &vtx, CTxMemPool &pool) {
142 AssertLockHeld(pool.cs);
143
144 if (pool.mapTx.empty() && pool.mapDeltas.empty()) {
145 // fast-path for IBD and/or when mempool is empty; there is no need to
146 // do any of the set-up work below which eats precious cycles.
147 // Note that this also skips updating the rolling fee udpate, which is
148 // fine: it is only recomputed when the mempool has to be trimmed down
149 // because it is full which is contradictory with this condition.
150 return;
151 }
152
153 addForBlock(vtx, pool);
154
155 for (const CTransactionRef &tx :
157 CTxMemPool::txiter it = pool.mapTx.find(tx->GetId());
158 if (it != pool.mapTx.end()) {
160 stage.insert(it);
162 } else {
163 // Conflicting txs can only exist if the tx was not in the mempool
164 pool.removeConflicts(*tx);
165 }
166 pool.ClearPrioritisation(tx->GetId());
167 }
168
169 pool.updateFeeForBlock();
170
171 removeForBlock(vtx);
172}
173
175 Chainstate &active_chainstate, bool fAddToMempool, CTxMemPool &pool) {
177 AssertLockHeld(pool.cs);
178
179 if (fAddToMempool) {
180 // disconnectpool's insertion_order index sorts the entries from oldest
181 // to newest, but the oldest entry will be the last tx from the latest
182 // mined block that was disconnected.
183 // Iterate disconnectpool in reverse, so that we add transactions back
184 // to the mempool starting with the earliest transaction that had been
185 // previously seen in a block.
186 for (const CTransactionRef &tx :
188 if (tx->IsCoinBase()) {
189 continue;
190 }
191 // restore saved PrioritiseTransaction state and nAcceptTime
192 const auto ptxInfo = getTxInfo(tx);
193 bool hasFeeDelta = false;
194 if (ptxInfo && ptxInfo->feeDelta != Amount::zero()) {
195 // manipulate mapDeltas directly (faster than calling
196 // PrioritiseTransaction)
197 pool.mapDeltas[tx->GetId()] = ptxInfo->feeDelta;
198 hasFeeDelta = true;
199 }
200 // ignore validation errors in resurrected transactions
201 auto result = AcceptToMemoryPool(
202 active_chainstate, tx,
203 /*accept_time=*/ptxInfo ? ptxInfo->time.count() : GetTime(),
204 /*bypass_limits=*/true, /*test_accept=*/false,
205 /*heightOverride=*/ptxInfo ? ptxInfo->height : 0);
206 if (result.m_result_type !=
208 LogPrint(
210 "AcceptToMemoryPool: tx %s rejected after reorg (%s)\n",
211 tx->GetId().ToString(), result.m_state.ToString());
212
213 if (hasFeeDelta) {
214 // tx not accepted: undo mapDelta insertion from above
215 pool.mapDeltas.erase(tx->GetId());
216 }
217 } else {
219 "AcceptToMemoryPool: tx %s accepted after reorg\n",
220 tx->GetId().ToString());
221 }
222 }
223 }
224
225 queuedTx.clear();
226 txInfo.clear();
227
228 // Re-limit mempool size, in case we added any transactions
229 pool.LimitSize(active_chainstate.CoinsTip());
230}
void TransactionRemovedFromMempool(const CTransactionRef &, MemPoolRemovalReason, uint64_t mempool_sequence)
CTxMemPool stores valid-according-to-the-current-best-chain transactions that may be included in the ...
Definition: txmempool.h:212
void removeConflicts(const CTransaction &tx) EXCLUSIVE_LOCKS_REQUIRED(cs)
Definition: txmempool.cpp:293
void ClearPrioritisation(const TxId &txid) EXCLUSIVE_LOCKS_REQUIRED(cs)
Definition: txmempool.cpp:566
std::set< txiter, CompareIteratorById > setEntries
Definition: txmempool.h:311
void updateFeeForBlock() EXCLUSIVE_LOCKS_REQUIRED(cs)
Called when a block is connected.
Definition: txmempool.cpp:311
RecursiveMutex cs
This mutex needs to be locked when accessing mapTx or other members that are guarded by it.
Definition: txmempool.h:307
void removeRecursive(const CTransaction &tx, MemPoolRemovalReason reason) EXCLUSIVE_LOCKS_REQUIRED(cs)
Definition: txmempool.cpp:262
void LimitSize(CCoinsViewCache &coins_cache) EXCLUSIVE_LOCKS_REQUIRED(cs
Reduce the size of the mempool by expiring and then trimming the mempool.
Definition: txmempool.cpp:699
void clear()
Definition: txmempool.cpp:343
CTransactionRef get(const TxId &txid) const
Definition: txmempool.cpp:508
indexed_transaction_set::nth_index< 0 >::type::const_iterator txiter
Definition: txmempool.h:310
uint64_t GetAndIncrementSequence() const EXCLUSIVE_LOCKS_REQUIRED(cs)
Guards this internal counter for external reporting.
Definition: txmempool.h:552
void RemoveStaged(const setEntries &stage, MemPoolRemovalReason reason) EXCLUSIVE_LOCKS_REQUIRED(cs)
Remove a set of transactions from the mempool.
Definition: txmempool.cpp:668
unsigned long size() const
Definition: txmempool.h:488
Chainstate stores and provides an API to update our local knowledge of the current best chain.
Definition: validation.h:699
CCoinsViewCache & CoinsTip() EXCLUSIVE_LOCKS_REQUIRED(
Definition: validation.h:834
void removeEntry(indexed_disconnected_transactions::index< insertion_order >::type::iterator entry)
indexed_disconnected_transactions queuedTx
TxInfoMap txInfo
populated by importMempool(); the original tx entry times and feeDeltas
void removeForBlock(const std::vector< CTransactionRef > &vtx)
const TxInfo * getTxInfo(const CTransactionRef &tx) const
void addTransaction(const CTransactionRef &tx)
void updateMempoolForReorg(Chainstate &active_chainstate, bool fAddToMempool, CTxMemPool &pool) EXCLUSIVE_LOCKS_REQUIRED(cs_main
Make mempool consistent after a reorg, by re-adding or recursively erasing disconnected block transac...
void addForBlock(const std::vector< CTransactionRef > &vtx, CTxMemPool &pool) EXCLUSIVE_LOCKS_REQUIRED(pool.cs)
void importMempool(CTxMemPool &pool) EXCLUSIVE_LOCKS_REQUIRED(pool.cs)
Definition: rcu.h:85
static const uint64_t DEFAULT_MAX_BLOCK_SIZE
Default setting for maximum allowed size for a block, in bytes.
Definition: consensus.h:20
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate.
Definition: cs_main.cpp:7
static const size_t MAX_DISCONNECTED_TX_POOL_SIZE
Maximum bytes for transactions to store for processing during reorg.
#define LogPrint(category,...)
Definition: logging.h:211
@ MEMPOOLREJ
Definition: logging.h:56
@ MEMPOOL
Definition: logging.h:42
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:315
reverse_range< T > reverse_iterate(T &x)
static constexpr Amount zero() noexcept
Definition: amount.h:32
@ VALID
Fully validated, valid.
A TxId is the identifier of a transaction.
Definition: txid.h:14
int64_t GetTime()
DEPRECATED Use either ClockType::now() or Now<TimePointType>() if a cast is needed.
Definition: time.cpp:109
@ BLOCK
Removed for block.
@ REORG
Removed for reorganization.
MempoolAcceptResult AcceptToMemoryPool(Chainstate &active_chainstate, const CTransactionRef &tx, int64_t accept_time, bool bypass_limits, bool test_accept, unsigned int heightOverride)
Try to add a transaction to the mempool.
AssertLockHeld(pool.cs)
CMainSignals & GetMainSignals()