Bitcoin ABC 0.33.11
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 if (pool.m_opts.signals) {
51 for (const CTxMemPoolEntryRef &e :
52 reverse_iterate(pool.mapTx.get<entry_id>())) {
53 // Notify all observers of this (possibly temporary) removal. This
54 // is necessary for tracking the transactions that are removed from
55 // the mempool during a reorg and can't be added back due to missing
56 // parent. Transactions that are added back to the mempool will
57 // trigger another notification. Make sure to notify in reverse
58 // topological order, children first.
60 e->GetSharedTx(), MemPoolRemovalReason::REORG,
62 }
63 }
64 pool.clear();
65
66 // Use addForBlocks to sort the transactions and then splice them in front
67 // of queuedTx
68 DisconnectedBlockTransactions orderedTxnPool;
69 orderedTxnPool.addForBlock(vtx, pool);
70 cachedInnerUsage += orderedTxnPool.cachedInnerUsage;
71 queuedTx.get<insertion_order>().splice(
72 queuedTx.get<insertion_order>().begin(),
73 orderedTxnPool.queuedTx.get<insertion_order>());
74
75 // We limit memory usage because we can't know if more blocks will be
76 // disconnected
78 // Drop the earliest entry which, by definition, has no children
79 removeEntry(queuedTx.get<insertion_order>().begin());
80 }
81}
82
84 const std::vector<CTransactionRef> &vtx, CTxMemPool &pool) {
85 AssertLockHeld(pool.cs);
86 for (const auto &tx : reverse_iterate(vtx)) {
87 // If we already added it, just skip.
88 auto it = queuedTx.find(tx->GetId());
89 if (it != queuedTx.end()) {
90 continue;
91 }
92
93 // Insert the transaction into the pool.
95
96 // Fill in the set of parents.
97 std::unordered_set<TxId, SaltedTxIdHasher> parents;
98 for (const CTxIn &in : tx->vin) {
99 parents.insert(in.prevout.GetTxId());
100 }
101
102 // In order to make sure we keep things in topological order, we check
103 // if we already know of the parent of the current transaction. If so,
104 // we remove them from the set and then add them back.
105 while (parents.size() > 0) {
106 std::unordered_set<TxId, SaltedTxIdHasher> worklist(
107 std::move(parents));
108 parents.clear();
109
110 for (const TxId &txid : worklist) {
111 // If we do not have that txid in the set, nothing needs to be
112 // done.
113 auto pit = queuedTx.find(txid);
114 if (pit == queuedTx.end()) {
115 continue;
116 }
117
118 // We have parent in our set, we reinsert them at the right
119 // position.
120 // NOLINTNEXTLINE(performance-unnecessary-copy-initialization)
121 const CTransactionRef ptx = *pit;
122 queuedTx.erase(pit);
123 queuedTx.insert(ptx);
124
125 // And we make sure ancestors are covered.
126 for (const CTxIn &in : ptx->vin) {
127 parents.insert(in.prevout.GetTxId());
128 }
129 }
130 }
131 }
132
133 // Keep the size under control.
135 // Drop the earliest entry, and remove its children from the
136 // mempool.
137 auto it = queuedTx.get<insertion_order>().begin();
139 removeEntry(it);
140 }
141}
142
144 const std::vector<CTransactionRef> &vtx, CTxMemPool &pool) {
145 AssertLockHeld(pool.cs);
146
147 if (pool.mapTx.empty() && pool.mapDeltas.empty()) {
148 // fast-path for IBD and/or when mempool is empty; there is no need to
149 // do any of the set-up work below which eats precious cycles.
150 // Note that this also skips updating the rolling fee udpate, which is
151 // fine: it is only recomputed when the mempool has to be trimmed down
152 // because it is full which is contradictory with this condition.
153 return;
154 }
155
156 addForBlock(vtx, pool);
157
158 for (const CTransactionRef &tx :
160 CTxMemPool::txiter it = pool.mapTx.find(tx->GetId());
161 if (it != pool.mapTx.end()) {
163 stage.insert(it);
165 } else {
166 // Conflicting txs can only exist if the tx was not in the mempool
167 pool.removeConflicts(*tx);
168 }
169 pool.ClearPrioritisation(tx->GetId());
170 }
171
172 pool.updateFeeForBlock();
173
174 removeForBlock(vtx);
175}
176
178 Chainstate &active_chainstate, bool fAddToMempool, CTxMemPool &pool) {
180 AssertLockHeld(pool.cs);
181
182 if (fAddToMempool) {
183 // disconnectpool's insertion_order index sorts the entries from oldest
184 // to newest, but the oldest entry will be the last tx from the latest
185 // mined block that was disconnected.
186 // Iterate disconnectpool in reverse, so that we add transactions back
187 // to the mempool starting with the earliest transaction that had been
188 // previously seen in a block.
189 for (const CTransactionRef &tx :
191 if (tx->IsCoinBase()) {
192 continue;
193 }
194 // restore saved PrioritiseTransaction state and nAcceptTime
195 const auto ptxInfo = getTxInfo(tx);
196 bool hasFeeDelta = false;
197 if (ptxInfo && ptxInfo->feeDelta != Amount::zero()) {
198 // manipulate mapDeltas directly (faster than calling
199 // PrioritiseTransaction)
200 pool.mapDeltas[tx->GetId()] = ptxInfo->feeDelta;
201 hasFeeDelta = true;
202 }
203 // ignore validation errors in resurrected transactions
204 auto result = AcceptToMemoryPool(
205 active_chainstate, tx,
206 /*accept_time=*/ptxInfo ? ptxInfo->time.count() : GetTime(),
207 /*bypass_limits=*/true, /*test_accept=*/false,
208 /*heightOverride=*/ptxInfo ? ptxInfo->height : 0);
209 if (result.m_result_type !=
211 LogPrint(
213 "AcceptToMemoryPool: tx %s rejected after reorg (%s)\n",
214 tx->GetId().ToString(), result.m_state.ToString());
215
216 if (hasFeeDelta) {
217 // tx not accepted: undo mapDelta insertion from above
218 pool.mapDeltas.erase(tx->GetId());
219 }
220 } else {
222 "AcceptToMemoryPool: tx %s accepted after reorg\n",
223 tx->GetId().ToString());
224 }
225 }
226 }
227
228 queuedTx.clear();
229 txInfo.clear();
230
231 // Re-limit mempool size, in case we added any transactions
232 pool.LimitSize(active_chainstate.CoinsTip());
233}
CTxMemPool stores valid-according-to-the-current-best-chain transactions that may be included in the ...
Definition: txmempool.h:222
void removeConflicts(const CTransaction &tx) EXCLUSIVE_LOCKS_REQUIRED(cs)
Definition: txmempool.cpp:299
void ClearPrioritisation(const TxId &txid) EXCLUSIVE_LOCKS_REQUIRED(cs)
Definition: txmempool.cpp:734
std::set< txiter, CompareIteratorById > setEntries
Definition: txmempool.h:320
void updateFeeForBlock() EXCLUSIVE_LOCKS_REQUIRED(cs)
Called when a block is connected.
Definition: txmempool.cpp:320
RecursiveMutex cs
This mutex needs to be locked when accessing mapTx or other members that are guarded by it.
Definition: txmempool.h:316
void removeRecursive(const CTransaction &tx, MemPoolRemovalReason reason) EXCLUSIVE_LOCKS_REQUIRED(cs)
Definition: txmempool.cpp:268
const Options m_opts
Definition: txmempool.h:353
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:879
CTransactionRef get(const TxId &txid) const
Definition: txmempool.cpp:676
indexed_transaction_set::nth_index< 0 >::type::const_iterator txiter
Definition: txmempool.h:319
uint64_t GetAndIncrementSequence() const EXCLUSIVE_LOCKS_REQUIRED(cs)
Guards this internal counter for external reporting.
Definition: txmempool.h:579
void clear(bool include_finalized_txs=false)
Definition: txmempool.cpp:377
void RemoveStaged(const setEntries &stage, MemPoolRemovalReason reason) EXCLUSIVE_LOCKS_REQUIRED(cs)
Remove a set of transactions from the mempool.
Definition: txmempool.cpp:836
unsigned long size() const
Definition: txmempool.h:493
Chainstate stores and provides an API to update our local knowledge of the current best chain.
Definition: validation.h:725
CCoinsViewCache & CoinsTip() EXCLUSIVE_LOCKS_REQUIRED(
Definition: validation.h:851
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
void TransactionRemovedFromMempool(const CTransactionRef &, MemPoolRemovalReason, uint64_t mempool_sequence)
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:452
@ MEMPOOLREJ
Definition: logging.h:85
@ MEMPOOL
Definition: logging.h:71
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:36
@ VALID
Fully validated, valid.
A TxId is the identifier of a transaction.
Definition: txid.h:14
ValidationSignals * signals
int64_t GetTime()
DEPRECATED Use either ClockType::now() or Now<TimePointType>() if a cast is needed.
Definition: time.cpp:80
@ 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)