Bitcoin ABC  0.29.2
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>
9 #include <consensus/consensus.h>
10 #include <primitives/transaction.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.
92  addTransaction(tx);
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  hasFeeDelta) {
209  // tx not accepted: undo mapDelta insertion from above
210  pool.mapDeltas.erase(tx->GetId());
211  }
212  }
213  }
214 
215  queuedTx.clear();
216  txInfo.clear();
217 
218  // Re-limit mempool size, in case we added any transactions
219  pool.LimitSize(active_chainstate.CoinsTip());
220 }
void TransactionRemovedFromMempool(const CTransactionRef &, MemPoolRemovalReason, uint64_t mempool_sequence)
const TxId & GetTxId() const
Definition: transaction.h:35
An input of a transaction.
Definition: transaction.h:59
COutPoint prevout
Definition: transaction.h:61
CTxMemPool stores valid-according-to-the-current-best-chain transactions that may be included in the ...
Definition: txmempool.h:209
void removeConflicts(const CTransaction &tx) EXCLUSIVE_LOCKS_REQUIRED(cs)
Definition: txmempool.cpp:289
void ClearPrioritisation(const TxId &txid) EXCLUSIVE_LOCKS_REQUIRED(cs)
Definition: txmempool.cpp:562
std::set< txiter, CompareIteratorById > setEntries
Definition: txmempool.h:300
void updateFeeForBlock() EXCLUSIVE_LOCKS_REQUIRED(cs)
Called when a block is connected.
Definition: txmempool.cpp:307
RecursiveMutex cs
This mutex needs to be locked when accessing mapTx or other members that are guarded by it.
Definition: txmempool.h:296
void removeRecursive(const CTransaction &tx, MemPoolRemovalReason reason) EXCLUSIVE_LOCKS_REQUIRED(cs)
Definition: txmempool.cpp:258
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:689
void clear()
Definition: txmempool.cpp:339
CTransactionRef get(const TxId &txid) const
Definition: txmempool.cpp:504
indexed_transaction_set::nth_index< 0 >::type::const_iterator txiter
Definition: txmempool.h:299
uint64_t GetAndIncrementSequence() const EXCLUSIVE_LOCKS_REQUIRED(cs)
Guards this internal counter for external reporting.
Definition: txmempool.h:541
void RemoveStaged(const setEntries &stage, MemPoolRemovalReason reason) EXCLUSIVE_LOCKS_REQUIRED(cs)
Remove a set of transactions from the mempool.
Definition: txmempool.cpp:658
unsigned long size() const
Definition: txmempool.h:477
Chainstate stores and provides an API to update our local knowledge of the current best chain.
Definition: validation.h:629
CCoinsViewCache & CoinsTip() EXCLUSIVE_LOCKS_REQUIRED(
Definition: validation.h:764
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.
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()
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()