/bitcoin/src/txmempool.cpp
Line | Count | Source |
1 | | // Copyright (c) 2009-2010 Satoshi Nakamoto |
2 | | // Copyright (c) 2009-2022 The Bitcoin Core developers |
3 | | // Distributed under the MIT software license, see the accompanying |
4 | | // file COPYING or http://www.opensource.org/licenses/mit-license.php. |
5 | | |
6 | | #include <txmempool.h> |
7 | | |
8 | | #include <chain.h> |
9 | | #include <coins.h> |
10 | | #include <common/system.h> |
11 | | #include <consensus/consensus.h> |
12 | | #include <consensus/tx_verify.h> |
13 | | #include <consensus/validation.h> |
14 | | #include <logging.h> |
15 | | #include <policy/policy.h> |
16 | | #include <policy/settings.h> |
17 | | #include <random.h> |
18 | | #include <tinyformat.h> |
19 | | #include <util/check.h> |
20 | | #include <util/feefrac.h> |
21 | | #include <util/moneystr.h> |
22 | | #include <util/overflow.h> |
23 | | #include <util/result.h> |
24 | | #include <util/time.h> |
25 | | #include <util/trace.h> |
26 | | #include <util/translation.h> |
27 | | #include <validationinterface.h> |
28 | | |
29 | | #include <algorithm> |
30 | | #include <cmath> |
31 | | #include <numeric> |
32 | | #include <optional> |
33 | | #include <ranges> |
34 | | #include <string_view> |
35 | | #include <utility> |
36 | | |
37 | | TRACEPOINT_SEMAPHORE(mempool, added); |
38 | | TRACEPOINT_SEMAPHORE(mempool, removed); |
39 | | |
40 | | bool TestLockPointValidity(CChain& active_chain, const LockPoints& lp) |
41 | 23.1k | { |
42 | 23.1k | AssertLockHeld(cs_main); |
43 | | // If there are relative lock times then the maxInputBlock will be set |
44 | | // If there are no relative lock times, the LockPoints don't depend on the chain |
45 | 23.1k | if (lp.maxInputBlock) { Branch (45:9): [True: 23.1k, False: 0]
|
46 | | // Check whether active_chain is an extension of the block at which the LockPoints |
47 | | // calculation was valid. If not LockPoints are no longer valid |
48 | 23.1k | if (!active_chain.Contains(lp.maxInputBlock)) { Branch (48:13): [True: 23, False: 23.1k]
|
49 | 23 | return false; |
50 | 23 | } |
51 | 23.1k | } |
52 | | |
53 | | // LockPoints still valid |
54 | 23.1k | return true; |
55 | 23.1k | } |
56 | | |
57 | | void CTxMemPool::UpdateForDescendants(txiter updateIt, cacheMap& cachedDescendants, |
58 | | const std::set<uint256>& setExclude, std::set<uint256>& descendants_to_remove) |
59 | 4.98k | { |
60 | 4.98k | CTxMemPoolEntry::Children stageEntries, descendants; |
61 | 4.98k | stageEntries = updateIt->GetMemPoolChildrenConst(); |
62 | | |
63 | 39.4k | while (!stageEntries.empty()) { Branch (63:12): [True: 34.4k, False: 4.98k]
|
64 | 34.4k | const CTxMemPoolEntry& descendant = *stageEntries.begin(); |
65 | 34.4k | descendants.insert(descendant); |
66 | 34.4k | stageEntries.erase(descendant); |
67 | 34.4k | const CTxMemPoolEntry::Children& children = descendant.GetMemPoolChildrenConst(); |
68 | 34.4k | for (const CTxMemPoolEntry& childEntry : children) { Branch (68:48): [True: 30.9k, False: 34.4k]
|
69 | 30.9k | cacheMap::iterator cacheIt = cachedDescendants.find(mapTx.iterator_to(childEntry)); |
70 | 30.9k | if (cacheIt != cachedDescendants.end()) { Branch (70:17): [True: 834, False: 30.1k]
|
71 | | // We've already calculated this one, just add the entries for this set |
72 | | // but don't traverse again. |
73 | 10.6k | for (txiter cacheEntry : cacheIt->second) { Branch (73:40): [True: 10.6k, False: 834]
|
74 | 10.6k | descendants.insert(*cacheEntry); |
75 | 10.6k | } |
76 | 30.1k | } else if (!descendants.count(childEntry)) { Branch (76:24): [True: 30.1k, False: 22]
|
77 | | // Schedule for later processing |
78 | 30.1k | stageEntries.insert(childEntry); |
79 | 30.1k | } |
80 | 30.9k | } |
81 | 34.4k | } |
82 | | // descendants now contains all in-mempool descendants of updateIt. |
83 | | // Update and add to cached descendant map |
84 | 4.98k | int32_t modifySize = 0; |
85 | 4.98k | CAmount modifyFee = 0; |
86 | 4.98k | int64_t modifyCount = 0; |
87 | 45.1k | for (const CTxMemPoolEntry& descendant : descendants) { Branch (87:44): [True: 45.1k, False: 4.98k]
|
88 | 45.1k | if (!setExclude.count(descendant.GetTx().GetHash())) { Branch (88:13): [True: 13.4k, False: 31.6k]
|
89 | 13.4k | modifySize += descendant.GetTxSize(); |
90 | 13.4k | modifyFee += descendant.GetModifiedFee(); |
91 | 13.4k | modifyCount++; |
92 | 13.4k | cachedDescendants[updateIt].insert(mapTx.iterator_to(descendant)); |
93 | | // Update ancestor state for each descendant |
94 | 13.4k | mapTx.modify(mapTx.iterator_to(descendant), [=](CTxMemPoolEntry& e) { |
95 | 13.4k | e.UpdateAncestorState(updateIt->GetTxSize(), updateIt->GetModifiedFee(), 1, updateIt->GetSigOpCost()); |
96 | 13.4k | }); |
97 | | // Don't directly remove the transaction here -- doing so would |
98 | | // invalidate iterators in cachedDescendants. Mark it for removal |
99 | | // by inserting into descendants_to_remove. |
100 | 13.4k | if (descendant.GetCountWithAncestors() > uint64_t(m_opts.limits.ancestor_count) || descendant.GetSizeWithAncestors() > m_opts.limits.ancestor_size_vbytes) { Branch (100:17): [True: 5.01k, False: 8.47k]
Branch (100:96): [True: 22, False: 8.45k]
|
101 | 5.04k | descendants_to_remove.insert(descendant.GetTx().GetHash()); |
102 | 5.04k | } |
103 | 13.4k | } |
104 | 45.1k | } |
105 | 4.98k | mapTx.modify(updateIt, [=](CTxMemPoolEntry& e) { e.UpdateDescendantState(modifySize, modifyFee, modifyCount); }); |
106 | 4.98k | } |
107 | | |
108 | | void CTxMemPool::UpdateTransactionsFromBlock(const std::vector<uint256>& vHashesToUpdate) |
109 | 2.62k | { |
110 | 2.62k | AssertLockHeld(cs); |
111 | | // For each entry in vHashesToUpdate, store the set of in-mempool, but not |
112 | | // in-vHashesToUpdate transactions, so that we don't have to recalculate |
113 | | // descendants when we come across a previously seen entry. |
114 | 2.62k | cacheMap mapMemPoolDescendantsToUpdate; |
115 | | |
116 | | // Use a set for lookups into vHashesToUpdate (these entries are already |
117 | | // accounted for in the state of their ancestors) |
118 | 2.62k | std::set<uint256> setAlreadyIncluded(vHashesToUpdate.begin(), vHashesToUpdate.end()); |
119 | | |
120 | 2.62k | std::set<uint256> descendants_to_remove; |
121 | | |
122 | | // Iterate in reverse, so that whenever we are looking at a transaction |
123 | | // we are sure that all in-mempool descendants have already been processed. |
124 | | // This maximizes the benefit of the descendant cache and guarantees that |
125 | | // CTxMemPoolEntry::m_children will be updated, an assumption made in |
126 | | // UpdateForDescendants. |
127 | 4.99k | for (const uint256& hash : vHashesToUpdate | std::views::reverse) { Branch (127:30): [True: 4.99k, False: 2.62k]
|
128 | | // calculate children from mapNextTx |
129 | 4.99k | txiter it = mapTx.find(hash); |
130 | 4.99k | if (it == mapTx.end()) { Branch (130:13): [True: 8, False: 4.98k]
|
131 | 8 | continue; |
132 | 8 | } |
133 | 4.98k | auto iter = mapNextTx.lower_bound(COutPoint(Txid::FromUint256(hash), 0)); |
134 | | // First calculate the children, and update CTxMemPoolEntry::m_children to |
135 | | // include them, and update their CTxMemPoolEntry::m_parents to include this tx. |
136 | | // we cache the in-mempool children to avoid duplicate updates |
137 | 4.98k | { |
138 | 4.98k | WITH_FRESH_EPOCH(m_epoch); |
139 | 9.38k | for (; iter != mapNextTx.end() && iter->first->hash == hash; ++iter) { Branch (139:20): [True: 8.97k, False: 410]
Branch (139:20): [True: 4.39k, False: 4.98k]
Branch (139:47): [True: 4.39k, False: 4.57k]
|
140 | 4.39k | const uint256 &childHash = iter->second->GetHash(); |
141 | 4.39k | txiter childIter = mapTx.find(childHash); |
142 | 4.39k | assert(childIter != mapTx.end()); Branch (142:17): [True: 4.39k, False: 0]
|
143 | | // We can skip updating entries we've encountered before or that |
144 | | // are in the block (which are already accounted for). |
145 | 4.39k | if (!visited(childIter) && !setAlreadyIncluded.count(childHash)) { Branch (145:21): [True: 4.33k, False: 68]
Branch (145:44): [True: 301, False: 4.03k]
|
146 | 301 | UpdateChild(it, childIter, true); |
147 | 301 | UpdateParent(childIter, it, true); |
148 | 301 | } |
149 | 4.39k | } |
150 | 4.98k | } // release epoch guard for UpdateForDescendants |
151 | 4.98k | UpdateForDescendants(it, mapMemPoolDescendantsToUpdate, setAlreadyIncluded, descendants_to_remove); |
152 | 4.98k | } |
153 | | |
154 | 2.62k | for (const auto& txid : descendants_to_remove) { Branch (154:27): [True: 475, False: 2.62k]
|
155 | | // This txid may have been removed already in a prior call to removeRecursive. |
156 | | // Therefore we ensure it is not yet removed already. |
157 | 475 | if (const std::optional<txiter> txiter = GetIter(txid)) { Branch (157:41): [True: 140, False: 335]
|
158 | 140 | removeRecursive((*txiter)->GetTx(), MemPoolRemovalReason::SIZELIMIT); |
159 | 140 | } |
160 | 475 | } |
161 | 2.62k | } |
162 | | |
163 | | util::Result<CTxMemPool::setEntries> CTxMemPool::CalculateAncestorsAndCheckLimits( |
164 | | int64_t entry_size, |
165 | | size_t entry_count, |
166 | | CTxMemPoolEntry::Parents& staged_ancestors, |
167 | | const Limits& limits) const |
168 | 1.55M | { |
169 | 1.55M | int64_t totalSizeWithAncestors = entry_size; |
170 | 1.55M | setEntries ancestors; |
171 | | |
172 | 11.7M | while (!staged_ancestors.empty()) { Branch (172:12): [True: 10.2M, False: 1.55M]
|
173 | 10.2M | const CTxMemPoolEntry& stage = staged_ancestors.begin()->get(); |
174 | 10.2M | txiter stageit = mapTx.iterator_to(stage); |
175 | | |
176 | 10.2M | ancestors.insert(stageit); |
177 | 10.2M | staged_ancestors.erase(stage); |
178 | 10.2M | totalSizeWithAncestors += stageit->GetTxSize(); |
179 | | |
180 | 10.2M | if (stageit->GetSizeWithDescendants() + entry_size > limits.descendant_size_vbytes) { Branch (180:13): [True: 387, False: 10.2M]
|
181 | 387 | return util::Error{Untranslated(strprintf("exceeds descendant size limit for tx %s [limit: %u]", stageit->GetTx().GetHash().ToString(), limits.descendant_size_vbytes))}; |
182 | 10.2M | } else if (stageit->GetCountWithDescendants() + entry_count > static_cast<uint64_t>(limits.descendant_count)) { Branch (182:20): [True: 30, False: 10.2M]
|
183 | 30 | return util::Error{Untranslated(strprintf("too many descendants for tx %s [limit: %u]", stageit->GetTx().GetHash().ToString(), limits.descendant_count))}; |
184 | 10.2M | } else if (totalSizeWithAncestors > limits.ancestor_size_vbytes) { Branch (184:20): [True: 12, False: 10.2M]
|
185 | 12 | return util::Error{Untranslated(strprintf("exceeds ancestor size limit [limit: %u]", limits.ancestor_size_vbytes))}; |
186 | 12 | } |
187 | | |
188 | 10.2M | const CTxMemPoolEntry::Parents& parents = stageit->GetMemPoolParentsConst(); |
189 | 10.2M | for (const CTxMemPoolEntry& parent : parents) { Branch (189:44): [True: 9.07M, False: 10.2M]
|
190 | 9.07M | txiter parent_it = mapTx.iterator_to(parent); |
191 | | |
192 | | // If this is a new ancestor, add it. |
193 | 9.07M | if (ancestors.count(parent_it) == 0) { Branch (193:17): [True: 9.04M, False: 36.2k]
|
194 | 9.04M | staged_ancestors.insert(parent); |
195 | 9.04M | } |
196 | 9.07M | if (staged_ancestors.size() + ancestors.size() + entry_count > static_cast<uint64_t>(limits.ancestor_count)) { Branch (196:17): [True: 165, False: 9.07M]
|
197 | 165 | return util::Error{Untranslated(strprintf("too many unconfirmed ancestors [limit: %u]", limits.ancestor_count))}; |
198 | 165 | } |
199 | 9.07M | } |
200 | 10.2M | } |
201 | | |
202 | 1.55M | return ancestors; |
203 | 1.55M | } |
204 | | |
205 | | util::Result<void> CTxMemPool::CheckPackageLimits(const Package& package, |
206 | | const int64_t total_vsize) const |
207 | 1.10k | { |
208 | 1.10k | size_t pack_count = package.size(); |
209 | | |
210 | | // Package itself is busting mempool limits; should be rejected even if no staged_ancestors exist |
211 | 1.10k | if (pack_count > static_cast<uint64_t>(m_opts.limits.ancestor_count)) { Branch (211:9): [True: 0, False: 1.10k]
|
212 | 0 | return util::Error{Untranslated(strprintf("package count %u exceeds ancestor count limit [limit: %u]", pack_count, m_opts.limits.ancestor_count))}; |
213 | 1.10k | } else if (pack_count > static_cast<uint64_t>(m_opts.limits.descendant_count)) { Branch (213:16): [True: 0, False: 1.10k]
|
214 | 0 | return util::Error{Untranslated(strprintf("package count %u exceeds descendant count limit [limit: %u]", pack_count, m_opts.limits.descendant_count))}; |
215 | 1.10k | } else if (total_vsize > m_opts.limits.ancestor_size_vbytes) { Branch (215:16): [True: 0, False: 1.10k]
|
216 | 0 | return util::Error{Untranslated(strprintf("package size %u exceeds ancestor size limit [limit: %u]", total_vsize, m_opts.limits.ancestor_size_vbytes))}; |
217 | 1.10k | } else if (total_vsize > m_opts.limits.descendant_size_vbytes) { Branch (217:16): [True: 0, False: 1.10k]
|
218 | 0 | return util::Error{Untranslated(strprintf("package size %u exceeds descendant size limit [limit: %u]", total_vsize, m_opts.limits.descendant_size_vbytes))}; |
219 | 0 | } |
220 | | |
221 | 1.10k | CTxMemPoolEntry::Parents staged_ancestors; |
222 | 2.21k | for (const auto& tx : package) { Branch (222:25): [True: 2.21k, False: 1.10k]
|
223 | 4.99k | for (const auto& input : tx->vin) { Branch (223:32): [True: 4.99k, False: 2.21k]
|
224 | 4.99k | std::optional<txiter> piter = GetIter(input.prevout.hash); |
225 | 4.99k | if (piter) { Branch (225:17): [True: 40, False: 4.95k]
|
226 | 40 | staged_ancestors.insert(**piter); |
227 | 40 | if (staged_ancestors.size() + package.size() > static_cast<uint64_t>(m_opts.limits.ancestor_count)) { Branch (227:21): [True: 0, False: 40]
|
228 | 0 | return util::Error{Untranslated(strprintf("too many unconfirmed parents [limit: %u]", m_opts.limits.ancestor_count))}; |
229 | 0 | } |
230 | 40 | } |
231 | 4.99k | } |
232 | 2.21k | } |
233 | | // When multiple transactions are passed in, the ancestors and descendants of all transactions |
234 | | // considered together must be within limits even if they are not interdependent. This may be |
235 | | // stricter than the limits for each individual transaction. |
236 | 1.10k | const auto ancestors{CalculateAncestorsAndCheckLimits(total_vsize, package.size(), |
237 | 1.10k | staged_ancestors, m_opts.limits)}; |
238 | | // It's possible to overestimate the ancestor/descendant totals. |
239 | 1.10k | if (!ancestors.has_value()) return util::Error{Untranslated("possibly " + util::ErrorString(ancestors).original)}; Branch (239:9): [True: 10, False: 1.09k]
|
240 | 1.09k | return {}; |
241 | 1.10k | } |
242 | | |
243 | | util::Result<CTxMemPool::setEntries> CTxMemPool::CalculateMemPoolAncestors( |
244 | | const CTxMemPoolEntry &entry, |
245 | | const Limits& limits, |
246 | | bool fSearchForParents /* = true */) const |
247 | 1.55M | { |
248 | 1.55M | CTxMemPoolEntry::Parents staged_ancestors; |
249 | 1.55M | const CTransaction &tx = entry.GetTx(); |
250 | | |
251 | 1.55M | if (fSearchForParents) { Branch (251:9): [True: 1.53M, False: 15.7k]
|
252 | | // Get parents of this transaction that are in the mempool |
253 | | // GetMemPoolParents() is only valid for entries in the mempool, so we |
254 | | // iterate mapTx to find parents. |
255 | 3.16M | for (unsigned int i = 0; i < tx.vin.size(); i++) { Branch (255:34): [True: 1.63M, False: 1.53M]
|
256 | 1.63M | std::optional<txiter> piter = GetIter(tx.vin[i].prevout.hash); |
257 | 1.63M | if (piter) { Branch (257:17): [True: 1.16M, False: 470k]
|
258 | 1.16M | staged_ancestors.insert(**piter); |
259 | 1.16M | if (staged_ancestors.size() + 1 > static_cast<uint64_t>(limits.ancestor_count)) { Branch (259:21): [True: 17, False: 1.16M]
|
260 | 17 | return util::Error{Untranslated(strprintf("too many unconfirmed parents [limit: %u]", limits.ancestor_count))}; |
261 | 17 | } |
262 | 1.16M | } |
263 | 1.63M | } |
264 | 1.53M | } else { |
265 | | // If we're not searching for parents, we require this to already be an |
266 | | // entry in the mempool and use the entry's cached parents. |
267 | 15.7k | txiter it = mapTx.iterator_to(entry); |
268 | 15.7k | staged_ancestors = it->GetMemPoolParentsConst(); |
269 | 15.7k | } |
270 | | |
271 | 1.55M | return CalculateAncestorsAndCheckLimits(entry.GetTxSize(), /*entry_count=*/1, staged_ancestors, |
272 | 1.55M | limits); |
273 | 1.55M | } |
274 | | |
275 | | CTxMemPool::setEntries CTxMemPool::AssumeCalculateMemPoolAncestors( |
276 | | std::string_view calling_fn_name, |
277 | | const CTxMemPoolEntry &entry, |
278 | | const Limits& limits, |
279 | | bool fSearchForParents /* = true */) const |
280 | 1.49M | { |
281 | 1.49M | auto result{CalculateMemPoolAncestors(entry, limits, fSearchForParents)}; |
282 | 1.49M | if (!Assume(result)) { Branch (282:9): [True: 0, False: 1.49M]
|
283 | 0 | LogPrintLevel(BCLog::MEMPOOL, BCLog::Level::Error, "%s: CalculateMemPoolAncestors failed unexpectedly, continuing with empty ancestor set (%s)\n", |
284 | 0 | calling_fn_name, util::ErrorString(result).original); |
285 | 0 | } |
286 | 1.49M | return std::move(result).value_or(CTxMemPool::setEntries{}); |
287 | 1.49M | } |
288 | | |
289 | | void CTxMemPool::UpdateAncestorsOf(bool add, txiter it, setEntries &setAncestors) |
290 | 58.9k | { |
291 | 58.9k | const CTxMemPoolEntry::Parents& parents = it->GetMemPoolParentsConst(); |
292 | | // add or remove this tx as a child of each parent |
293 | 58.9k | for (const CTxMemPoolEntry& parent : parents) { Branch (293:40): [True: 40.7k, False: 58.9k]
|
294 | 40.7k | UpdateChild(mapTx.iterator_to(parent), it, add); |
295 | 40.7k | } |
296 | 58.9k | const int32_t updateCount = (add ? 1 : -1); Branch (296:34): [True: 43.1k, False: 15.7k]
|
297 | 58.9k | const int32_t updateSize{updateCount * it->GetTxSize()}; |
298 | 58.9k | const CAmount updateFee = updateCount * it->GetModifiedFee(); |
299 | 401k | for (txiter ancestorIt : setAncestors) { Branch (299:28): [True: 401k, False: 58.9k]
|
300 | 401k | mapTx.modify(ancestorIt, [=](CTxMemPoolEntry& e) { e.UpdateDescendantState(updateSize, updateFee, updateCount); }); |
301 | 401k | } |
302 | 58.9k | } |
303 | | |
304 | | void CTxMemPool::UpdateEntryForAncestors(txiter it, const setEntries &setAncestors) |
305 | 43.1k | { |
306 | 43.1k | int64_t updateCount = setAncestors.size(); |
307 | 43.1k | int64_t updateSize = 0; |
308 | 43.1k | CAmount updateFee = 0; |
309 | 43.1k | int64_t updateSigOpsCost = 0; |
310 | 302k | for (txiter ancestorIt : setAncestors) { Branch (310:28): [True: 302k, False: 43.1k]
|
311 | 302k | updateSize += ancestorIt->GetTxSize(); |
312 | 302k | updateFee += ancestorIt->GetModifiedFee(); |
313 | 302k | updateSigOpsCost += ancestorIt->GetSigOpCost(); |
314 | 302k | } |
315 | 43.1k | mapTx.modify(it, [=](CTxMemPoolEntry& e){ e.UpdateAncestorState(updateSize, updateFee, updateCount, updateSigOpsCost); }); |
316 | 43.1k | } |
317 | | |
318 | | void CTxMemPool::UpdateChildrenForRemoval(txiter it) |
319 | 15.7k | { |
320 | 15.7k | const CTxMemPoolEntry::Children& children = it->GetMemPoolChildrenConst(); |
321 | 15.7k | for (const CTxMemPoolEntry& updateIt : children) { Branch (321:42): [True: 3.84k, False: 15.7k]
|
322 | 3.84k | UpdateParent(mapTx.iterator_to(updateIt), it, false); |
323 | 3.84k | } |
324 | 15.7k | } |
325 | | |
326 | | void CTxMemPool::UpdateForRemoveFromMempool(const setEntries &entriesToRemove, bool updateDescendants) |
327 | 99.0k | { |
328 | | // For each entry, walk back all ancestors and decrement size associated with this |
329 | | // transaction |
330 | 99.0k | if (updateDescendants) { Branch (330:9): [True: 4.51k, False: 94.5k]
|
331 | | // updateDescendants should be true whenever we're not recursively |
332 | | // removing a tx and all its descendants, eg when a transaction is |
333 | | // confirmed in a block. |
334 | | // Here we only update statistics and not data in CTxMemPool::Parents |
335 | | // and CTxMemPoolEntry::Children (which we need to preserve until we're |
336 | | // finished with all operations that need to traverse the mempool). |
337 | 4.51k | for (txiter removeIt : entriesToRemove) { Branch (337:30): [True: 4.51k, False: 4.51k]
|
338 | 4.51k | setEntries setDescendants; |
339 | 4.51k | CalculateDescendants(removeIt, setDescendants); |
340 | 4.51k | setDescendants.erase(removeIt); // don't update state for self |
341 | 4.51k | int32_t modifySize = -removeIt->GetTxSize(); |
342 | 4.51k | CAmount modifyFee = -removeIt->GetModifiedFee(); |
343 | 4.51k | int modifySigOps = -removeIt->GetSigOpCost(); |
344 | 37.2k | for (txiter dit : setDescendants) { Branch (344:29): [True: 37.2k, False: 4.51k]
|
345 | 37.2k | mapTx.modify(dit, [=](CTxMemPoolEntry& e){ e.UpdateAncestorState(modifySize, modifyFee, -1, modifySigOps); }); |
346 | 37.2k | } |
347 | 4.51k | } |
348 | 4.51k | } |
349 | 99.0k | for (txiter removeIt : entriesToRemove) { Branch (349:26): [True: 15.7k, False: 99.0k]
|
350 | 15.7k | const CTxMemPoolEntry &entry = *removeIt; |
351 | | // Since this is a tx that is already in the mempool, we can call CMPA |
352 | | // with fSearchForParents = false. If the mempool is in a consistent |
353 | | // state, then using true or false should both be correct, though false |
354 | | // should be a bit faster. |
355 | | // However, if we happen to be in the middle of processing a reorg, then |
356 | | // the mempool can be in an inconsistent state. In this case, the set |
357 | | // of ancestors reachable via GetMemPoolParents()/GetMemPoolChildren() |
358 | | // will be the same as the set of ancestors whose packages include this |
359 | | // transaction, because when we add a new transaction to the mempool in |
360 | | // addNewTransaction(), we assume it has no children, and in the case of a |
361 | | // reorg where that assumption is false, the in-mempool children aren't |
362 | | // linked to the in-block tx's until UpdateTransactionsFromBlock() is |
363 | | // called. |
364 | | // So if we're being called during a reorg, ie before |
365 | | // UpdateTransactionsFromBlock() has been called, then |
366 | | // GetMemPoolParents()/GetMemPoolChildren() will differ from the set of |
367 | | // mempool parents we'd calculate by searching, and it's important that |
368 | | // we use the cached notion of ancestor transactions as the set of |
369 | | // things to update for removal. |
370 | 15.7k | auto ancestors{AssumeCalculateMemPoolAncestors(__func__, entry, Limits::NoLimits(), /*fSearchForParents=*/false)}; |
371 | | // Note that UpdateAncestorsOf severs the child links that point to |
372 | | // removeIt in the entries for the parents of removeIt. |
373 | 15.7k | UpdateAncestorsOf(false, removeIt, ancestors); |
374 | 15.7k | } |
375 | | // After updating all the ancestor sizes, we can now sever the link between each |
376 | | // transaction being removed and any mempool children (ie, update CTxMemPoolEntry::m_parents |
377 | | // for each direct child of a transaction being removed). |
378 | 99.0k | for (txiter removeIt : entriesToRemove) { Branch (378:26): [True: 15.7k, False: 99.0k]
|
379 | 15.7k | UpdateChildrenForRemoval(removeIt); |
380 | 15.7k | } |
381 | 99.0k | } |
382 | | |
383 | | void CTxMemPoolEntry::UpdateDescendantState(int32_t modifySize, CAmount modifyFee, int64_t modifyCount) |
384 | 406k | { |
385 | 406k | nSizeWithDescendants += modifySize; |
386 | 406k | assert(nSizeWithDescendants > 0); Branch (386:5): [True: 406k, False: 0]
|
387 | 406k | nModFeesWithDescendants = SaturatingAdd(nModFeesWithDescendants, modifyFee); |
388 | 406k | m_count_with_descendants += modifyCount; |
389 | 406k | assert(m_count_with_descendants > 0); Branch (389:5): [True: 406k, False: 0]
|
390 | 406k | } |
391 | | |
392 | | void CTxMemPoolEntry::UpdateAncestorState(int32_t modifySize, CAmount modifyFee, int64_t modifyCount, int64_t modifySigOps) |
393 | 93.8k | { |
394 | 93.8k | nSizeWithAncestors += modifySize; |
395 | 93.8k | assert(nSizeWithAncestors > 0); Branch (395:5): [True: 93.8k, False: 0]
|
396 | 93.8k | nModFeesWithAncestors = SaturatingAdd(nModFeesWithAncestors, modifyFee); |
397 | 93.8k | m_count_with_ancestors += modifyCount; |
398 | 93.8k | assert(m_count_with_ancestors > 0); Branch (398:5): [True: 93.8k, False: 0]
|
399 | 93.8k | nSigOpCostWithAncestors += modifySigOps; |
400 | 93.8k | assert(int(nSigOpCostWithAncestors) >= 0); Branch (400:5): [True: 93.8k, False: 0]
|
401 | 93.8k | } |
402 | | |
403 | | //! Clamp option values and populate the error if options are not valid. |
404 | | static CTxMemPool::Options&& Flatten(CTxMemPool::Options&& opts, bilingual_str& error) |
405 | 11.0k | { |
406 | 11.0k | opts.check_ratio = std::clamp<int>(opts.check_ratio, 0, 1'000'000); |
407 | 11.0k | int64_t descendant_limit_bytes = opts.limits.descendant_size_vbytes * 40; |
408 | 11.0k | if (opts.max_size_bytes < 0 || opts.max_size_bytes < descendant_limit_bytes) { Branch (408:9): [True: 0, False: 11.0k]
Branch (408:36): [True: 0, False: 11.0k]
|
409 | 0 | error = strprintf(_("-maxmempool must be at least %d MB"), std::ceil(descendant_limit_bytes / 1'000'000.0)); |
410 | 0 | } |
411 | 11.0k | return std::move(opts); |
412 | 11.0k | } |
413 | | |
414 | | CTxMemPool::CTxMemPool(Options opts, bilingual_str& error) |
415 | 11.0k | : m_opts{Flatten(std::move(opts), error)} |
416 | 11.0k | { |
417 | 11.0k | } |
418 | | |
419 | | bool CTxMemPool::isSpent(const COutPoint& outpoint) const |
420 | 0 | { |
421 | 0 | LOCK(cs); |
422 | 0 | return mapNextTx.count(outpoint); |
423 | 0 | } |
424 | | |
425 | | unsigned int CTxMemPool::GetTransactionsUpdated() const |
426 | 0 | { |
427 | 0 | return nTransactionsUpdated; |
428 | 0 | } |
429 | | |
430 | | void CTxMemPool::AddTransactionsUpdated(unsigned int n) |
431 | 2.25M | { |
432 | 2.25M | nTransactionsUpdated += n; |
433 | 2.25M | } |
434 | | |
435 | | void CTxMemPool::Apply(ChangeSet* changeset) |
436 | 43.1k | { |
437 | 43.1k | AssertLockHeld(cs); |
438 | 43.1k | RemoveStaged(changeset->m_to_remove, false, MemPoolRemovalReason::REPLACED); |
439 | | |
440 | 86.3k | for (size_t i=0; i<changeset->m_entry_vec.size(); ++i) { Branch (440:22): [True: 43.1k, False: 43.1k]
|
441 | 43.1k | auto tx_entry = changeset->m_entry_vec[i]; |
442 | 43.1k | std::optional<CTxMemPool::setEntries> ancestors; |
443 | 43.1k | if (i == 0) { Branch (443:13): [True: 43.1k, False: 60]
|
444 | | // Note: ChangeSet::CalculateMemPoolAncestors() will return a |
445 | | // cached value if mempool ancestors for this transaction were |
446 | | // previously calculated. |
447 | | // We can only use a cached ancestor calculation for the first |
448 | | // transaction in a package, because in-package parents won't be |
449 | | // present in the cached ancestor sets of in-package children. |
450 | | // We pass in Limits::NoLimits() to ensure that this function won't fail |
451 | | // (we're going to be applying this set of transactions whether or |
452 | | // not the mempool policy limits are being respected). |
453 | 43.1k | ancestors = *Assume(changeset->CalculateMemPoolAncestors(tx_entry, Limits::NoLimits())); |
454 | 43.1k | } |
455 | | // First splice this entry into mapTx. |
456 | 43.1k | auto node_handle = changeset->m_to_add.extract(tx_entry); |
457 | 43.1k | auto result = mapTx.insert(std::move(node_handle)); |
458 | | |
459 | 43.1k | Assume(result.inserted); |
460 | 43.1k | txiter it = result.position; |
461 | | |
462 | | // Now update the entry for ancestors/descendants. |
463 | 43.1k | if (ancestors.has_value()) { Branch (463:13): [True: 43.1k, False: 60]
|
464 | 43.1k | addNewTransaction(it, *ancestors); |
465 | 43.1k | } else { |
466 | 60 | addNewTransaction(it); |
467 | 60 | } |
468 | 43.1k | } |
469 | 43.1k | } |
470 | | |
471 | | void CTxMemPool::addNewTransaction(CTxMemPool::txiter it) |
472 | 60 | { |
473 | 60 | auto ancestors{AssumeCalculateMemPoolAncestors(__func__, *it, Limits::NoLimits())}; |
474 | 60 | return addNewTransaction(it, ancestors); |
475 | 60 | } |
476 | | |
477 | | void CTxMemPool::addNewTransaction(CTxMemPool::txiter newit, CTxMemPool::setEntries& setAncestors) |
478 | 43.1k | { |
479 | 43.1k | const CTxMemPoolEntry& entry = *newit; |
480 | | |
481 | | // Update cachedInnerUsage to include contained transaction's usage. |
482 | | // (When we update the entry for in-mempool parents, memory usage will be |
483 | | // further updated.) |
484 | 43.1k | cachedInnerUsage += entry.DynamicMemoryUsage(); |
485 | | |
486 | 43.1k | const CTransaction& tx = newit->GetTx(); |
487 | 43.1k | std::set<Txid> setParentTransactions; |
488 | 88.6k | for (unsigned int i = 0; i < tx.vin.size(); i++) { Branch (488:30): [True: 45.4k, False: 43.1k]
|
489 | 45.4k | mapNextTx.insert(std::make_pair(&tx.vin[i].prevout, &tx)); |
490 | 45.4k | setParentTransactions.insert(tx.vin[i].prevout.hash); |
491 | 45.4k | } |
492 | | // Don't bother worrying about child transactions of this one. |
493 | | // Normal case of a new transaction arriving is that there can't be any |
494 | | // children, because such children would be orphans. |
495 | | // An exception to that is if a transaction enters that used to be in a block. |
496 | | // In that case, our disconnect block logic will call UpdateTransactionsFromBlock |
497 | | // to clean up the mess we're leaving here. |
498 | | |
499 | | // Update ancestors with information about this tx |
500 | 43.1k | for (const auto& pit : GetIterSet(setParentTransactions)) { Branch (500:26): [True: 31.8k, False: 43.1k]
|
501 | 31.8k | UpdateParent(newit, pit, true); |
502 | 31.8k | } |
503 | 43.1k | UpdateAncestorsOf(true, newit, setAncestors); |
504 | 43.1k | UpdateEntryForAncestors(newit, setAncestors); |
505 | | |
506 | 43.1k | nTransactionsUpdated++; |
507 | 43.1k | totalTxSize += entry.GetTxSize(); |
508 | 43.1k | m_total_fee += entry.GetFee(); |
509 | | |
510 | 43.1k | txns_randomized.emplace_back(newit->GetSharedTx()); |
511 | 43.1k | newit->idx_randomized = txns_randomized.size() - 1; |
512 | | |
513 | 43.1k | TRACEPOINT(mempool, added, |
514 | 43.1k | entry.GetTx().GetHash().data(), |
515 | 43.1k | entry.GetTxSize(), |
516 | 43.1k | entry.GetFee() |
517 | 43.1k | ); |
518 | 43.1k | } |
519 | | |
520 | | void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason) |
521 | 15.7k | { |
522 | | // We increment mempool sequence value no matter removal reason |
523 | | // even if not directly reported below. |
524 | 15.7k | uint64_t mempool_sequence = GetAndIncrementSequence(); |
525 | | |
526 | 15.7k | if (reason != MemPoolRemovalReason::BLOCK && m_opts.signals) { Branch (526:9): [True: 11.2k, False: 4.51k]
Branch (526:50): [True: 11.2k, False: 0]
|
527 | | // Notify clients that a transaction has been removed from the mempool |
528 | | // for any reason except being included in a block. Clients interested |
529 | | // in transactions included in blocks can subscribe to the BlockConnected |
530 | | // notification. |
531 | 11.2k | m_opts.signals->TransactionRemovedFromMempool(it->GetSharedTx(), reason, mempool_sequence); |
532 | 11.2k | } |
533 | 15.7k | TRACEPOINT(mempool, removed, |
534 | 15.7k | it->GetTx().GetHash().data(), |
535 | 15.7k | RemovalReasonToString(reason).c_str(), |
536 | 15.7k | it->GetTxSize(), |
537 | 15.7k | it->GetFee(), |
538 | 15.7k | std::chrono::duration_cast<std::chrono::duration<std::uint64_t>>(it->GetTime()).count() |
539 | 15.7k | ); |
540 | | |
541 | 15.7k | for (const CTxIn& txin : it->GetTx().vin) Branch (541:28): [True: 16.3k, False: 15.7k]
|
542 | 16.3k | mapNextTx.erase(txin.prevout); |
543 | | |
544 | 15.7k | RemoveUnbroadcastTx(it->GetTx().GetHash(), true /* add logging because unchecked */); |
545 | | |
546 | 15.7k | if (txns_randomized.size() > 1) { Branch (546:9): [True: 14.1k, False: 1.58k]
|
547 | | // Update idx_randomized of the to-be-moved entry. |
548 | 14.1k | Assert(GetEntry(txns_randomized.back()->GetHash()))->idx_randomized = it->idx_randomized; |
549 | | // Remove entry from txns_randomized by replacing it with the back and deleting the back. |
550 | 14.1k | txns_randomized[it->idx_randomized] = std::move(txns_randomized.back()); |
551 | 14.1k | txns_randomized.pop_back(); |
552 | 14.1k | if (txns_randomized.size() * 2 < txns_randomized.capacity()) Branch (552:13): [True: 2.30k, False: 11.8k]
|
553 | 2.30k | txns_randomized.shrink_to_fit(); |
554 | 14.1k | } else |
555 | 1.58k | txns_randomized.clear(); |
556 | | |
557 | 15.7k | totalTxSize -= it->GetTxSize(); |
558 | 15.7k | m_total_fee -= it->GetFee(); |
559 | 15.7k | cachedInnerUsage -= it->DynamicMemoryUsage(); |
560 | 15.7k | cachedInnerUsage -= memusage::DynamicUsage(it->GetMemPoolParentsConst()) + memusage::DynamicUsage(it->GetMemPoolChildrenConst()); |
561 | 15.7k | mapTx.erase(it); |
562 | 15.7k | nTransactionsUpdated++; |
563 | 15.7k | } |
564 | | |
565 | | // Calculates descendants of entry that are not already in setDescendants, and adds to |
566 | | // setDescendants. Assumes entryit is already a tx in the mempool and CTxMemPoolEntry::m_children |
567 | | // is correct for tx and all descendants. |
568 | | // Also assumes that if an entry is in setDescendants already, then all |
569 | | // in-mempool descendants of it are already in setDescendants as well, so that we |
570 | | // can save time by not iterating over those entries. |
571 | | void CTxMemPool::CalculateDescendants(txiter entryit, setEntries& setDescendants) const |
572 | 13.8k | { |
573 | 13.8k | setEntries stage; |
574 | 13.8k | if (setDescendants.count(entryit) == 0) { Branch (574:9): [True: 11.7k, False: 2.16k]
|
575 | 11.7k | stage.insert(entryit); |
576 | 11.7k | } |
577 | | // Traverse down the children of entry, only adding children that are not |
578 | | // accounted for in setDescendants already (because those children have either |
579 | | // already been walked, or will be walked in this iteration). |
580 | 75.4k | while (!stage.empty()) { Branch (580:12): [True: 61.5k, False: 13.8k]
|
581 | 61.5k | txiter it = *stage.begin(); |
582 | 61.5k | setDescendants.insert(it); |
583 | 61.5k | stage.erase(it); |
584 | | |
585 | 61.5k | const CTxMemPoolEntry::Children& children = it->GetMemPoolChildrenConst(); |
586 | 61.5k | for (const CTxMemPoolEntry& child : children) { Branch (586:43): [True: 50.4k, False: 61.5k]
|
587 | 50.4k | txiter childiter = mapTx.iterator_to(child); |
588 | 50.4k | if (!setDescendants.count(childiter)) { Branch (588:17): [True: 49.8k, False: 522]
|
589 | 49.8k | stage.insert(childiter); |
590 | 49.8k | } |
591 | 50.4k | } |
592 | 61.5k | } |
593 | 13.8k | } |
594 | | |
595 | | void CTxMemPool::removeRecursive(const CTransaction &origTx, MemPoolRemovalReason reason) |
596 | 6.19k | { |
597 | | // Remove transaction from memory pool |
598 | 6.19k | AssertLockHeld(cs); |
599 | 6.19k | Assume(!m_have_changeset); |
600 | 6.19k | setEntries txToRemove; |
601 | 6.19k | txiter origit = mapTx.find(origTx.GetHash()); |
602 | 6.19k | if (origit != mapTx.end()) { Branch (602:13): [True: 510, False: 5.68k]
|
603 | 510 | txToRemove.insert(origit); |
604 | 5.68k | } else { |
605 | | // When recursively removing but origTx isn't in the mempool |
606 | | // be sure to remove any children that are in the pool. This can |
607 | | // happen during chain re-orgs if origTx isn't re-accepted into |
608 | | // the mempool for any reason. |
609 | 15.2k | for (unsigned int i = 0; i < origTx.vout.size(); i++) { Branch (609:38): [True: 9.61k, False: 5.68k]
|
610 | 9.61k | auto it = mapNextTx.find(COutPoint(origTx.GetHash(), i)); |
611 | 9.61k | if (it == mapNextTx.end()) Branch (611:21): [True: 9.55k, False: 58]
|
612 | 9.55k | continue; |
613 | 58 | txiter nextit = mapTx.find(it->second->GetHash()); |
614 | 58 | assert(nextit != mapTx.end()); Branch (614:17): [True: 58, False: 0]
|
615 | 58 | txToRemove.insert(nextit); |
616 | 58 | } |
617 | 5.68k | } |
618 | 6.19k | setEntries setAllRemoves; |
619 | 6.19k | for (txiter it : txToRemove) { Branch (619:24): [True: 559, False: 6.19k]
|
620 | 559 | CalculateDescendants(it, setAllRemoves); |
621 | 559 | } |
622 | | |
623 | 6.19k | RemoveStaged(setAllRemoves, false, reason); |
624 | 6.19k | } |
625 | | |
626 | | void CTxMemPool::removeForReorg(CChain& chain, std::function<bool(txiter)> check_final_and_mature) |
627 | 2.62k | { |
628 | | // Remove transactions spending a coinbase which are now immature and no-longer-final transactions |
629 | 2.62k | AssertLockHeld(cs); |
630 | 2.62k | AssertLockHeld(::cs_main); |
631 | 2.62k | Assume(!m_have_changeset); |
632 | | |
633 | 2.62k | setEntries txToRemove; |
634 | 14.2k | for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) { Branch (634:70): [True: 11.6k, False: 2.62k]
|
635 | 11.6k | if (check_final_and_mature(it)) txToRemove.insert(it); Branch (635:13): [True: 5, False: 11.6k]
|
636 | 11.6k | } |
637 | 2.62k | setEntries setAllRemoves; |
638 | 2.62k | for (txiter it : txToRemove) { Branch (638:20): [True: 5, False: 2.62k]
|
639 | 5 | CalculateDescendants(it, setAllRemoves); |
640 | 5 | } |
641 | 2.62k | RemoveStaged(setAllRemoves, false, MemPoolRemovalReason::REORG); |
642 | 14.1k | for (indexed_transaction_set::const_iterator it = mapTx.begin(); it != mapTx.end(); it++) { Branch (642:70): [True: 11.5k, False: 2.62k]
|
643 | 11.5k | assert(TestLockPointValidity(chain, it->GetLockPoints())); Branch (643:9): [True: 11.5k, False: 0]
|
644 | 11.5k | } |
645 | 2.62k | } |
646 | | |
647 | | void CTxMemPool::removeConflicts(const CTransaction &tx) |
648 | 2.25M | { |
649 | | // Remove transactions which depend on inputs of tx, recursively |
650 | 2.25M | AssertLockHeld(cs); |
651 | 2.25M | for (const CTxIn &txin : tx.vin) { Branch (651:28): [True: 2.25M, False: 2.25M]
|
652 | 2.25M | auto it = mapNextTx.find(txin.prevout); |
653 | 2.25M | if (it != mapNextTx.end()) { Branch (653:13): [True: 370, False: 2.25M]
|
654 | 370 | const CTransaction &txConflict = *it->second; |
655 | 370 | if (txConflict != tx) Branch (655:17): [True: 370, False: 0]
|
656 | 370 | { |
657 | 370 | ClearPrioritisation(txConflict.GetHash()); |
658 | 370 | removeRecursive(txConflict, MemPoolRemovalReason::CONFLICT); |
659 | 370 | } |
660 | 370 | } |
661 | 2.25M | } |
662 | 2.25M | } |
663 | | |
664 | | /** |
665 | | * Called when a block is connected. Removes from mempool. |
666 | | */ |
667 | | void CTxMemPool::removeForBlock(const std::vector<CTransactionRef>& vtx, unsigned int nBlockHeight) |
668 | 2.23M | { |
669 | 2.23M | AssertLockHeld(cs); |
670 | 2.23M | Assume(!m_have_changeset); |
671 | 2.23M | std::vector<RemovedMempoolTransactionInfo> txs_removed_for_block; |
672 | 2.23M | txs_removed_for_block.reserve(vtx.size()); |
673 | 2.23M | for (const auto& tx : vtx) Branch (673:25): [True: 2.25M, False: 2.23M]
|
674 | 2.25M | { |
675 | 2.25M | txiter it = mapTx.find(tx->GetHash()); |
676 | 2.25M | if (it != mapTx.end()) { Branch (676:13): [True: 4.51k, False: 2.24M]
|
677 | 4.51k | setEntries stage; |
678 | 4.51k | stage.insert(it); |
679 | 4.51k | txs_removed_for_block.emplace_back(*it); |
680 | 4.51k | RemoveStaged(stage, true, MemPoolRemovalReason::BLOCK); |
681 | 4.51k | } |
682 | 2.25M | removeConflicts(*tx); |
683 | 2.25M | ClearPrioritisation(tx->GetHash()); |
684 | 2.25M | } |
685 | 2.23M | if (m_opts.signals) { Branch (685:9): [True: 2.23M, False: 0]
|
686 | 2.23M | m_opts.signals->MempoolTransactionsRemovedForBlock(txs_removed_for_block, nBlockHeight); |
687 | 2.23M | } |
688 | 2.23M | lastRollingFeeUpdate = GetTime(); |
689 | 2.23M | blockSinceLastRollingFeeBump = true; |
690 | 2.23M | } |
691 | | |
692 | | void CTxMemPool::check(const CCoinsViewCache& active_coins_tip, int64_t spendheight) const |
693 | 2.62M | { |
694 | 2.62M | if (m_opts.check_ratio == 0) return; Branch (694:9): [True: 0, False: 2.62M]
|
695 | | |
696 | 2.62M | if (FastRandomContext().randrange(m_opts.check_ratio) >= 1) return; Branch (696:9): [True: 0, False: 2.62M]
|
697 | | |
698 | 2.62M | AssertLockHeld(::cs_main); |
699 | 2.62M | LOCK(cs); |
700 | 2.62M | LogDebug(BCLog::MEMPOOL, "Checking mempool with %u transactions and %u inputs\n", (unsigned int)mapTx.size(), (unsigned int)mapNextTx.size()); |
701 | | |
702 | 2.62M | uint64_t checkTotal = 0; |
703 | 2.62M | CAmount check_total_fee{0}; |
704 | 2.62M | uint64_t innerUsage = 0; |
705 | 2.62M | uint64_t prev_ancestor_count{0}; |
706 | | |
707 | 2.62M | CCoinsViewCache mempoolDuplicate(const_cast<CCoinsViewCache*>(&active_coins_tip)); |
708 | | |
709 | 2.62M | for (const auto& it : GetSortedDepthAndScore()) { Branch (709:25): [True: 1.47M, False: 2.62M]
|
710 | 1.47M | checkTotal += it->GetTxSize(); |
711 | 1.47M | check_total_fee += it->GetFee(); |
712 | 1.47M | innerUsage += it->DynamicMemoryUsage(); |
713 | 1.47M | const CTransaction& tx = it->GetTx(); |
714 | 1.47M | innerUsage += memusage::DynamicUsage(it->GetMemPoolParentsConst()) + memusage::DynamicUsage(it->GetMemPoolChildrenConst()); |
715 | 1.47M | CTxMemPoolEntry::Parents setParentCheck; |
716 | 1.56M | for (const CTxIn &txin : tx.vin) { Branch (716:32): [True: 1.56M, False: 1.47M]
|
717 | | // Check that every mempool transaction's inputs refer to available coins, or other mempool tx's. |
718 | 1.56M | indexed_transaction_set::const_iterator it2 = mapTx.find(txin.prevout.hash); |
719 | 1.56M | if (it2 != mapTx.end()) { Branch (719:17): [True: 1.12M, False: 439k]
|
720 | 1.12M | const CTransaction& tx2 = it2->GetTx(); |
721 | 1.12M | assert(tx2.vout.size() > txin.prevout.n && !tx2.vout[txin.prevout.n].IsNull()); Branch (721:17): [True: 1.12M, False: 0]
Branch (721:17): [True: 1.12M, False: 0]
Branch (721:17): [True: 1.12M, False: 0]
|
722 | 1.12M | setParentCheck.insert(*it2); |
723 | 1.12M | } |
724 | | // We are iterating through the mempool entries sorted in order by ancestor count. |
725 | | // All parents must have been checked before their children and their coins added to |
726 | | // the mempoolDuplicate coins cache. |
727 | 1.56M | assert(mempoolDuplicate.HaveCoin(txin.prevout)); Branch (727:13): [True: 1.56M, False: 0]
|
728 | | // Check whether its inputs are marked in mapNextTx. |
729 | 1.56M | auto it3 = mapNextTx.find(txin.prevout); |
730 | 1.56M | assert(it3 != mapNextTx.end()); Branch (730:13): [True: 1.56M, False: 0]
|
731 | 1.56M | assert(it3->first == &txin.prevout); Branch (731:13): [True: 1.56M, False: 0]
|
732 | 1.56M | assert(it3->second == &tx); Branch (732:13): [True: 1.56M, False: 0]
|
733 | 1.56M | } |
734 | 2.23M | auto comp = [](const CTxMemPoolEntry& a, const CTxMemPoolEntry& b) -> bool { |
735 | 2.23M | return a.GetTx().GetHash() == b.GetTx().GetHash(); |
736 | 2.23M | }; |
737 | 1.47M | assert(setParentCheck.size() == it->GetMemPoolParentsConst().size()); Branch (737:9): [True: 1.47M, False: 0]
|
738 | 1.47M | assert(std::equal(setParentCheck.begin(), setParentCheck.end(), it->GetMemPoolParentsConst().begin(), comp)); Branch (738:9): [True: 1.47M, False: 0]
|
739 | | // Verify ancestor state is correct. |
740 | 1.47M | auto ancestors{AssumeCalculateMemPoolAncestors(__func__, *it, Limits::NoLimits())}; |
741 | 1.47M | uint64_t nCountCheck = ancestors.size() + 1; |
742 | 1.47M | int32_t nSizeCheck = it->GetTxSize(); |
743 | 1.47M | CAmount nFeesCheck = it->GetModifiedFee(); |
744 | 1.47M | int64_t nSigOpCheck = it->GetSigOpCost(); |
745 | | |
746 | 9.78M | for (txiter ancestorIt : ancestors) { Branch (746:32): [True: 9.78M, False: 1.47M]
|
747 | 9.78M | nSizeCheck += ancestorIt->GetTxSize(); |
748 | 9.78M | nFeesCheck += ancestorIt->GetModifiedFee(); |
749 | 9.78M | nSigOpCheck += ancestorIt->GetSigOpCost(); |
750 | 9.78M | } |
751 | | |
752 | 1.47M | assert(it->GetCountWithAncestors() == nCountCheck); Branch (752:9): [True: 1.47M, False: 0]
|
753 | 1.47M | assert(it->GetSizeWithAncestors() == nSizeCheck); Branch (753:9): [True: 1.47M, False: 0]
|
754 | 1.47M | assert(it->GetSigOpCostWithAncestors() == nSigOpCheck); Branch (754:9): [True: 1.47M, False: 0]
|
755 | 1.47M | assert(it->GetModFeesWithAncestors() == nFeesCheck); Branch (755:9): [True: 1.47M, False: 0]
|
756 | | // Sanity check: we are walking in ascending ancestor count order. |
757 | 1.47M | assert(prev_ancestor_count <= it->GetCountWithAncestors()); Branch (757:9): [True: 1.47M, False: 0]
|
758 | 1.47M | prev_ancestor_count = it->GetCountWithAncestors(); |
759 | | |
760 | | // Check children against mapNextTx |
761 | 1.47M | CTxMemPoolEntry::Children setChildrenCheck; |
762 | 1.47M | auto iter = mapNextTx.lower_bound(COutPoint(it->GetTx().GetHash(), 0)); |
763 | 1.47M | int32_t child_sizes{0}; |
764 | 2.60M | for (; iter != mapNextTx.end() && iter->first->hash == it->GetTx().GetHash(); ++iter) { Branch (764:16): [True: 2.41M, False: 191k]
Branch (764:16): [True: 1.12M, False: 1.47M]
Branch (764:43): [True: 1.12M, False: 1.28M]
|
765 | 1.12M | txiter childit = mapTx.find(iter->second->GetHash()); |
766 | 1.12M | assert(childit != mapTx.end()); // mapNextTx points to in-mempool transactions Branch (766:13): [True: 1.12M, False: 0]
|
767 | 1.12M | if (setChildrenCheck.insert(*childit).second) { Branch (767:17): [True: 1.11M, False: 7.22k]
|
768 | 1.11M | child_sizes += childit->GetTxSize(); |
769 | 1.11M | } |
770 | 1.12M | } |
771 | 1.47M | assert(setChildrenCheck.size() == it->GetMemPoolChildrenConst().size()); Branch (771:9): [True: 1.47M, False: 0]
|
772 | 1.47M | assert(std::equal(setChildrenCheck.begin(), setChildrenCheck.end(), it->GetMemPoolChildrenConst().begin(), comp)); Branch (772:9): [True: 1.47M, False: 0]
|
773 | | // Also check to make sure size is greater than sum with immediate children. |
774 | | // just a sanity check, not definitive that this calc is correct... |
775 | 1.47M | assert(it->GetSizeWithDescendants() >= child_sizes + it->GetTxSize()); Branch (775:9): [True: 1.47M, False: 0]
|
776 | | |
777 | 1.47M | TxValidationState dummy_state; // Not used. CheckTxInputs() should always pass |
778 | 1.47M | CAmount txfee = 0; |
779 | 1.47M | assert(!tx.IsCoinBase()); Branch (779:9): [True: 1.47M, False: 0]
|
780 | 1.47M | assert(Consensus::CheckTxInputs(tx, dummy_state, mempoolDuplicate, spendheight, txfee)); Branch (780:9): [True: 1.47M, False: 0]
|
781 | 1.56M | for (const auto& input: tx.vin) mempoolDuplicate.SpendCoin(input.prevout); Branch (781:31): [True: 1.56M, False: 1.47M]
|
782 | 1.47M | AddCoins(mempoolDuplicate, tx, std::numeric_limits<int>::max()); |
783 | 1.47M | } |
784 | 4.18M | for (auto it = mapNextTx.cbegin(); it != mapNextTx.cend(); it++) { Branch (784:40): [True: 1.56M, False: 2.62M]
|
785 | 1.56M | uint256 hash = it->second->GetHash(); |
786 | 1.56M | indexed_transaction_set::const_iterator it2 = mapTx.find(hash); |
787 | 1.56M | const CTransaction& tx = it2->GetTx(); |
788 | 1.56M | assert(it2 != mapTx.end()); Branch (788:9): [True: 1.56M, False: 0]
|
789 | 1.56M | assert(&tx == it->second); Branch (789:9): [True: 1.56M, False: 0]
|
790 | 1.56M | } |
791 | | |
792 | 2.62M | assert(totalTxSize == checkTotal); Branch (792:5): [True: 2.62M, False: 0]
|
793 | 2.62M | assert(m_total_fee == check_total_fee); Branch (793:5): [True: 2.62M, False: 0]
|
794 | 2.62M | assert(innerUsage == cachedInnerUsage); Branch (794:5): [True: 2.62M, False: 0]
|
795 | 2.62M | } |
796 | | |
797 | | bool CTxMemPool::CompareDepthAndScore(const uint256& hasha, const uint256& hashb, bool wtxid) |
798 | 411k | { |
799 | | /* Return `true` if hasha should be considered sooner than hashb. Namely when: |
800 | | * a is not in the mempool, but b is |
801 | | * both are in the mempool and a has fewer ancestors than b |
802 | | * both are in the mempool and a has a higher score than b |
803 | | */ |
804 | 411k | LOCK(cs); |
805 | 411k | indexed_transaction_set::const_iterator j = wtxid ? get_iter_from_wtxid(hashb) : mapTx.find(hashb); Branch (805:49): [True: 411k, False: 0]
|
806 | 411k | if (j == mapTx.end()) return false; Branch (806:9): [True: 46.9k, False: 364k]
|
807 | 364k | indexed_transaction_set::const_iterator i = wtxid ? get_iter_from_wtxid(hasha) : mapTx.find(hasha); Branch (807:49): [True: 364k, False: 0]
|
808 | 364k | if (i == mapTx.end()) return true; Branch (808:9): [True: 12.0k, False: 352k]
|
809 | 352k | uint64_t counta = i->GetCountWithAncestors(); |
810 | 352k | uint64_t countb = j->GetCountWithAncestors(); |
811 | 352k | if (counta == countb) { Branch (811:9): [True: 18.0k, False: 334k]
|
812 | 18.0k | return CompareTxMemPoolEntryByScore()(*i, *j); |
813 | 18.0k | } |
814 | 334k | return counta < countb; |
815 | 352k | } |
816 | | |
817 | | namespace { |
818 | | class DepthAndScoreComparator |
819 | | { |
820 | | public: |
821 | | bool operator()(const CTxMemPool::indexed_transaction_set::const_iterator& a, const CTxMemPool::indexed_transaction_set::const_iterator& b) |
822 | 5.51M | { |
823 | 5.51M | uint64_t counta = a->GetCountWithAncestors(); |
824 | 5.51M | uint64_t countb = b->GetCountWithAncestors(); |
825 | 5.51M | if (counta == countb) { Branch (825:13): [True: 478k, False: 5.03M]
|
826 | 478k | return CompareTxMemPoolEntryByScore()(*a, *b); |
827 | 478k | } |
828 | 5.03M | return counta < countb; |
829 | 5.51M | } |
830 | | }; |
831 | | } // namespace |
832 | | |
833 | | std::vector<CTxMemPool::indexed_transaction_set::const_iterator> CTxMemPool::GetSortedDepthAndScore() const |
834 | 2.64M | { |
835 | 2.64M | std::vector<indexed_transaction_set::const_iterator> iters; |
836 | 2.64M | AssertLockHeld(cs); |
837 | | |
838 | 2.64M | iters.reserve(mapTx.size()); |
839 | | |
840 | 4.15M | for (indexed_transaction_set::iterator mi = mapTx.begin(); mi != mapTx.end(); ++mi) { Branch (840:64): [True: 1.50M, False: 2.64M]
|
841 | 1.50M | iters.push_back(mi); |
842 | 1.50M | } |
843 | 2.64M | std::sort(iters.begin(), iters.end(), DepthAndScoreComparator()); |
844 | 2.64M | return iters; |
845 | 2.64M | } |
846 | | |
847 | 124k | static TxMempoolInfo GetInfo(CTxMemPool::indexed_transaction_set::const_iterator it) { |
848 | 124k | return TxMempoolInfo{it->GetSharedTx(), it->GetTime(), it->GetFee(), it->GetTxSize(), it->GetModifiedFee() - it->GetFee()}; |
849 | 124k | } |
850 | | |
851 | | std::vector<CTxMemPoolEntryRef> CTxMemPool::entryAll() const |
852 | 11.0k | { |
853 | 11.0k | AssertLockHeld(cs); |
854 | | |
855 | 11.0k | std::vector<CTxMemPoolEntryRef> ret; |
856 | 11.0k | ret.reserve(mapTx.size()); |
857 | 11.0k | for (const auto& it : GetSortedDepthAndScore()) { Branch (857:25): [True: 0, False: 11.0k]
|
858 | 0 | ret.emplace_back(*it); |
859 | 0 | } |
860 | 11.0k | return ret; |
861 | 11.0k | } |
862 | | |
863 | | std::vector<TxMempoolInfo> CTxMemPool::infoAll() const |
864 | 11.2k | { |
865 | 11.2k | LOCK(cs); |
866 | 11.2k | auto iters = GetSortedDepthAndScore(); |
867 | | |
868 | 11.2k | std::vector<TxMempoolInfo> ret; |
869 | 11.2k | ret.reserve(mapTx.size()); |
870 | 29.4k | for (auto it : iters) { Branch (870:18): [True: 29.4k, False: 11.2k]
|
871 | 29.4k | ret.push_back(GetInfo(it)); |
872 | 29.4k | } |
873 | | |
874 | 11.2k | return ret; |
875 | 11.2k | } |
876 | | |
877 | | const CTxMemPoolEntry* CTxMemPool::GetEntry(const Txid& txid) const |
878 | 14.1k | { |
879 | 14.1k | AssertLockHeld(cs); |
880 | 14.1k | const auto i = mapTx.find(txid); |
881 | 14.1k | return i == mapTx.end() ? nullptr : &(*i); Branch (881:12): [True: 0, False: 14.1k]
|
882 | 14.1k | } |
883 | | |
884 | | CTransactionRef CTxMemPool::get(const uint256& hash) const |
885 | 506k | { |
886 | 506k | LOCK(cs); |
887 | 506k | indexed_transaction_set::const_iterator i = mapTx.find(hash); |
888 | 506k | if (i == mapTx.end()) Branch (888:9): [True: 392k, False: 113k]
|
889 | 392k | return nullptr; |
890 | 113k | return i->GetSharedTx(); |
891 | 506k | } |
892 | | |
893 | | TxMempoolInfo CTxMemPool::info(const GenTxid& gtxid) const |
894 | 108k | { |
895 | 108k | LOCK(cs); |
896 | 108k | indexed_transaction_set::const_iterator i = (gtxid.IsWtxid() ? get_iter_from_wtxid(gtxid.GetHash()) : mapTx.find(gtxid.GetHash())); Branch (896:50): [True: 108k, False: 0]
|
897 | 108k | if (i == mapTx.end()) Branch (897:9): [True: 13.9k, False: 94.5k]
|
898 | 13.9k | return TxMempoolInfo(); |
899 | 94.5k | return GetInfo(i); |
900 | 108k | } |
901 | | |
902 | | TxMempoolInfo CTxMemPool::info_for_relay(const GenTxid& gtxid, uint64_t last_sequence) const |
903 | 37.8k | { |
904 | 37.8k | LOCK(cs); |
905 | 37.8k | indexed_transaction_set::const_iterator i = (gtxid.IsWtxid() ? get_iter_from_wtxid(gtxid.GetHash()) : mapTx.find(gtxid.GetHash())); Branch (905:50): [True: 15.5k, False: 22.3k]
|
906 | 37.8k | if (i != mapTx.end() && i->GetSequence() < last_sequence) { Branch (906:9): [True: 3.31k, False: 34.5k]
Branch (906:9): [True: 648, False: 37.2k]
Branch (906:29): [True: 648, False: 2.66k]
|
907 | 648 | return GetInfo(i); |
908 | 37.2k | } else { |
909 | 37.2k | return TxMempoolInfo(); |
910 | 37.2k | } |
911 | 37.8k | } |
912 | | |
913 | | void CTxMemPool::PrioritiseTransaction(const uint256& hash, const CAmount& nFeeDelta) |
914 | 0 | { |
915 | 0 | { |
916 | 0 | LOCK(cs); |
917 | 0 | CAmount &delta = mapDeltas[hash]; |
918 | 0 | delta = SaturatingAdd(delta, nFeeDelta); |
919 | 0 | txiter it = mapTx.find(hash); |
920 | 0 | if (it != mapTx.end()) { Branch (920:13): [True: 0, False: 0]
|
921 | 0 | mapTx.modify(it, [&nFeeDelta](CTxMemPoolEntry& e) { e.UpdateModifiedFee(nFeeDelta); }); |
922 | | // Now update all ancestors' modified fees with descendants |
923 | 0 | auto ancestors{AssumeCalculateMemPoolAncestors(__func__, *it, Limits::NoLimits(), /*fSearchForParents=*/false)}; |
924 | 0 | for (txiter ancestorIt : ancestors) { Branch (924:36): [True: 0, False: 0]
|
925 | 0 | mapTx.modify(ancestorIt, [=](CTxMemPoolEntry& e){ e.UpdateDescendantState(0, nFeeDelta, 0);}); |
926 | 0 | } |
927 | | // Now update all descendants' modified fees with ancestors |
928 | 0 | setEntries setDescendants; |
929 | 0 | CalculateDescendants(it, setDescendants); |
930 | 0 | setDescendants.erase(it); |
931 | 0 | for (txiter descendantIt : setDescendants) { Branch (931:38): [True: 0, False: 0]
|
932 | 0 | mapTx.modify(descendantIt, [=](CTxMemPoolEntry& e){ e.UpdateAncestorState(0, nFeeDelta, 0, 0); }); |
933 | 0 | } |
934 | 0 | ++nTransactionsUpdated; |
935 | 0 | } |
936 | 0 | if (delta == 0) { Branch (936:13): [True: 0, False: 0]
|
937 | 0 | mapDeltas.erase(hash); |
938 | 0 | LogPrintf("PrioritiseTransaction: %s (%sin mempool) delta cleared\n", hash.ToString(), it == mapTx.end() ? "not " : ""); |
939 | 0 | } else { |
940 | 0 | LogPrintf("PrioritiseTransaction: %s (%sin mempool) fee += %s, new delta=%s\n", |
941 | 0 | hash.ToString(), |
942 | 0 | it == mapTx.end() ? "not " : "", |
943 | 0 | FormatMoney(nFeeDelta), |
944 | 0 | FormatMoney(delta)); |
945 | 0 | } |
946 | 0 | } |
947 | 0 | } |
948 | | |
949 | | void CTxMemPool::ApplyDelta(const uint256& hash, CAmount &nFeeDelta) const |
950 | 63.2k | { |
951 | 63.2k | AssertLockHeld(cs); |
952 | 63.2k | std::map<uint256, CAmount>::const_iterator pos = mapDeltas.find(hash); |
953 | 63.2k | if (pos == mapDeltas.end()) Branch (953:9): [True: 63.2k, False: 0]
|
954 | 63.2k | return; |
955 | 0 | const CAmount &delta = pos->second; |
956 | 0 | nFeeDelta += delta; |
957 | 0 | } |
958 | | |
959 | | void CTxMemPool::ClearPrioritisation(const uint256& hash) |
960 | 2.25M | { |
961 | 2.25M | AssertLockHeld(cs); |
962 | 2.25M | mapDeltas.erase(hash); |
963 | 2.25M | } |
964 | | |
965 | | std::vector<CTxMemPool::delta_info> CTxMemPool::GetPrioritisedTransactions() const |
966 | 0 | { |
967 | 0 | AssertLockNotHeld(cs); |
968 | 0 | LOCK(cs); |
969 | 0 | std::vector<delta_info> result; |
970 | 0 | result.reserve(mapDeltas.size()); |
971 | 0 | for (const auto& [txid, delta] : mapDeltas) { Branch (971:36): [True: 0, False: 0]
|
972 | 0 | const auto iter{mapTx.find(txid)}; |
973 | 0 | const bool in_mempool{iter != mapTx.end()}; |
974 | 0 | std::optional<CAmount> modified_fee; |
975 | 0 | if (in_mempool) modified_fee = iter->GetModifiedFee(); Branch (975:13): [True: 0, False: 0]
|
976 | 0 | result.emplace_back(delta_info{in_mempool, delta, modified_fee, txid}); |
977 | 0 | } |
978 | 0 | return result; |
979 | 0 | } |
980 | | |
981 | | const CTransaction* CTxMemPool::GetConflictTx(const COutPoint& prevout) const |
982 | 470k | { |
983 | 470k | const auto it = mapNextTx.find(prevout); |
984 | 470k | return it == mapNextTx.end() ? nullptr : it->second; Branch (984:12): [True: 454k, False: 15.9k]
|
985 | 470k | } |
986 | | |
987 | | std::optional<CTxMemPool::txiter> CTxMemPool::GetIter(const uint256& txid) const |
988 | 1.73M | { |
989 | 1.73M | auto it = mapTx.find(txid); |
990 | 1.73M | if (it != mapTx.end()) return it; Branch (990:9): [True: 1.24M, False: 489k]
|
991 | 489k | return std::nullopt; |
992 | 1.73M | } |
993 | | |
994 | | CTxMemPool::setEntries CTxMemPool::GetIterSet(const std::set<Txid>& hashes) const |
995 | 100k | { |
996 | 100k | CTxMemPool::setEntries ret; |
997 | 100k | for (const auto& h : hashes) { Branch (997:24): [True: 55.4k, False: 100k]
|
998 | 55.4k | const auto mi = GetIter(h); |
999 | 55.4k | if (mi) ret.insert(*mi); Branch (999:13): [True: 42.2k, False: 13.2k]
|
1000 | 55.4k | } |
1001 | 100k | return ret; |
1002 | 100k | } |
1003 | | |
1004 | | std::vector<CTxMemPool::txiter> CTxMemPool::GetIterVec(const std::vector<uint256>& txids) const |
1005 | 0 | { |
1006 | 0 | AssertLockHeld(cs); |
1007 | 0 | std::vector<txiter> ret; |
1008 | 0 | ret.reserve(txids.size()); |
1009 | 0 | for (const auto& txid : txids) { Branch (1009:27): [True: 0, False: 0]
|
1010 | 0 | const auto it{GetIter(txid)}; |
1011 | 0 | if (!it) return {}; Branch (1011:13): [True: 0, False: 0]
|
1012 | 0 | ret.push_back(*it); |
1013 | 0 | } |
1014 | 0 | return ret; |
1015 | 0 | } |
1016 | | |
1017 | | bool CTxMemPool::HasNoInputsOf(const CTransaction &tx) const |
1018 | 43.1k | { |
1019 | 56.3k | for (unsigned int i = 0; i < tx.vin.size(); i++) Branch (1019:30): [True: 44.9k, False: 11.3k]
|
1020 | 44.9k | if (exists(GenTxid::Txid(tx.vin[i].prevout.hash))) Branch (1020:13): [True: 31.7k, False: 13.1k]
|
1021 | 31.7k | return false; |
1022 | 11.3k | return true; |
1023 | 43.1k | } |
1024 | | |
1025 | 398k | CCoinsViewMemPool::CCoinsViewMemPool(CCoinsView* baseIn, const CTxMemPool& mempoolIn) : CCoinsViewBacked(baseIn), mempool(mempoolIn) { } |
1026 | | |
1027 | | std::optional<Coin> CCoinsViewMemPool::GetCoin(const COutPoint& outpoint) const |
1028 | 403k | { |
1029 | | // Check to see if the inputs are made available by another tx in the package. |
1030 | | // These Coins would not be available in the underlying CoinsView. |
1031 | 403k | if (auto it = m_temp_added.find(outpoint); it != m_temp_added.end()) { Branch (1031:48): [True: 3.54k, False: 400k]
|
1032 | 3.54k | return it->second; |
1033 | 3.54k | } |
1034 | | |
1035 | | // If an entry in the mempool exists, always return that one, as it's guaranteed to never |
1036 | | // conflict with the underlying cache, and it cannot have pruned entries (as it contains full) |
1037 | | // transactions. First checking the underlying cache risks returning a pruned entry instead. |
1038 | 400k | CTransactionRef ptx = mempool.get(outpoint.hash); |
1039 | 400k | if (ptx) { Branch (1039:9): [True: 45.0k, False: 355k]
|
1040 | 45.0k | if (outpoint.n < ptx->vout.size()) { Branch (1040:13): [True: 45.0k, False: 0]
|
1041 | 45.0k | Coin coin(ptx->vout[outpoint.n], MEMPOOL_HEIGHT, false); |
1042 | 45.0k | m_non_base_coins.emplace(outpoint); |
1043 | 45.0k | return coin; |
1044 | 45.0k | } |
1045 | 0 | return std::nullopt; |
1046 | 45.0k | } |
1047 | 355k | return base->GetCoin(outpoint); |
1048 | 400k | } |
1049 | | |
1050 | | void CCoinsViewMemPool::PackageAddTransaction(const CTransactionRef& tx) |
1051 | 3.13k | { |
1052 | 10.7k | for (unsigned int n = 0; n < tx->vout.size(); ++n) { Branch (1052:30): [True: 7.66k, False: 3.13k]
|
1053 | 7.66k | m_temp_added.emplace(COutPoint(tx->GetHash(), n), Coin(tx->vout[n], MEMPOOL_HEIGHT, false)); |
1054 | 7.66k | m_non_base_coins.emplace(tx->GetHash(), n); |
1055 | 7.66k | } |
1056 | 3.13k | } |
1057 | | void CCoinsViewMemPool::Reset() |
1058 | 7.86k | { |
1059 | 7.86k | m_temp_added.clear(); |
1060 | 7.86k | m_non_base_coins.clear(); |
1061 | 7.86k | } |
1062 | | |
1063 | 7.20M | size_t CTxMemPool::DynamicMemoryUsage() const { |
1064 | 7.20M | LOCK(cs); |
1065 | | // Estimate the overhead of mapTx to be 15 pointers + an allocation, as no exact formula for boost::multi_index_contained is implemented. |
1066 | 7.20M | return memusage::MallocUsage(sizeof(CTxMemPoolEntry) + 15 * sizeof(void*)) * mapTx.size() + memusage::DynamicUsage(mapNextTx) + memusage::DynamicUsage(mapDeltas) + memusage::DynamicUsage(txns_randomized) + cachedInnerUsage; |
1067 | 7.20M | } |
1068 | | |
1069 | 17.8k | void CTxMemPool::RemoveUnbroadcastTx(const uint256& txid, const bool unchecked) { |
1070 | 17.8k | LOCK(cs); |
1071 | | |
1072 | 17.8k | if (m_unbroadcast_txids.erase(txid)) Branch (1072:9): [True: 0, False: 17.8k]
|
1073 | 0 | { |
1074 | 0 | LogDebug(BCLog::MEMPOOL, "Removed %i from set of unbroadcast txns%s\n", txid.GetHex(), (unchecked ? " before confirmation that txn was sent out" : "")); |
1075 | 0 | } |
1076 | 17.8k | } |
1077 | | |
1078 | 99.0k | void CTxMemPool::RemoveStaged(setEntries &stage, bool updateDescendants, MemPoolRemovalReason reason) { |
1079 | 99.0k | AssertLockHeld(cs); |
1080 | 99.0k | UpdateForRemoveFromMempool(stage, updateDescendants); |
1081 | 99.0k | for (txiter it : stage) { Branch (1081:20): [True: 15.7k, False: 99.0k]
|
1082 | 15.7k | removeUnchecked(it, reason); |
1083 | 15.7k | } |
1084 | 99.0k | } |
1085 | | |
1086 | | int CTxMemPool::Expire(std::chrono::seconds time) |
1087 | 42.6k | { |
1088 | 42.6k | AssertLockHeld(cs); |
1089 | 42.6k | Assume(!m_have_changeset); |
1090 | 42.6k | indexed_transaction_set::index<entry_time>::type::iterator it = mapTx.get<entry_time>().begin(); |
1091 | 42.6k | setEntries toremove; |
1092 | 46.3k | while (it != mapTx.get<entry_time>().end() && it->GetTime() < time) { Branch (1092:12): [True: 44.8k, False: 1.47k]
Branch (1092:12): [True: 3.73k, False: 42.6k]
Branch (1092:51): [True: 3.73k, False: 41.1k]
|
1093 | 3.73k | toremove.insert(mapTx.project<0>(it)); |
1094 | 3.73k | it++; |
1095 | 3.73k | } |
1096 | 42.6k | setEntries stage; |
1097 | 42.6k | for (txiter removeit : toremove) { Branch (1097:26): [True: 3.73k, False: 42.6k]
|
1098 | 3.73k | CalculateDescendants(removeit, stage); |
1099 | 3.73k | } |
1100 | 42.6k | RemoveStaged(stage, false, MemPoolRemovalReason::EXPIRY); |
1101 | 42.6k | return stage.size(); |
1102 | 42.6k | } |
1103 | | |
1104 | | void CTxMemPool::UpdateChild(txiter entry, txiter child, bool add) |
1105 | 41.0k | { |
1106 | 41.0k | AssertLockHeld(cs); |
1107 | 41.0k | CTxMemPoolEntry::Children s; |
1108 | 41.0k | if (add && entry->GetMemPoolChildren().insert(*child).second) { Branch (1108:9): [True: 32.1k, False: 8.84k]
Branch (1108:9): [True: 32.1k, False: 8.84k]
Branch (1108:16): [True: 32.1k, False: 0]
|
1109 | 32.1k | cachedInnerUsage += memusage::IncrementalDynamicUsage(s); |
1110 | 32.1k | } else if (!add && entry->GetMemPoolChildren().erase(*child)) { Branch (1110:16): [True: 8.84k, False: 0]
Branch (1110:16): [True: 8.84k, False: 0]
Branch (1110:24): [True: 8.84k, False: 0]
|
1111 | 8.84k | cachedInnerUsage -= memusage::IncrementalDynamicUsage(s); |
1112 | 8.84k | } |
1113 | 41.0k | } |
1114 | | |
1115 | | void CTxMemPool::UpdateParent(txiter entry, txiter parent, bool add) |
1116 | 36.0k | { |
1117 | 36.0k | AssertLockHeld(cs); |
1118 | 36.0k | CTxMemPoolEntry::Parents s; |
1119 | 36.0k | if (add && entry->GetMemPoolParents().insert(*parent).second) { Branch (1119:9): [True: 32.1k, False: 3.84k]
Branch (1119:9): [True: 32.1k, False: 3.84k]
Branch (1119:16): [True: 32.1k, False: 0]
|
1120 | 32.1k | cachedInnerUsage += memusage::IncrementalDynamicUsage(s); |
1121 | 32.1k | } else if (!add && entry->GetMemPoolParents().erase(*parent)) { Branch (1121:16): [True: 3.84k, False: 0]
Branch (1121:16): [True: 3.84k, False: 0]
Branch (1121:24): [True: 3.84k, False: 0]
|
1122 | 3.84k | cachedInnerUsage -= memusage::IncrementalDynamicUsage(s); |
1123 | 3.84k | } |
1124 | 36.0k | } |
1125 | | |
1126 | 41.8M | CFeeRate CTxMemPool::GetMinFee(size_t sizelimit) const { |
1127 | 41.8M | LOCK(cs); |
1128 | 41.8M | if (!blockSinceLastRollingFeeBump || rollingMinimumFeeRate == 0) Branch (1128:9): [True: 0, False: 41.8M]
Branch (1128:42): [True: 41.8M, False: 0]
|
1129 | 41.8M | return CFeeRate(llround(rollingMinimumFeeRate)); |
1130 | | |
1131 | 0 | int64_t time = GetTime(); |
1132 | 0 | if (time > lastRollingFeeUpdate + 10) { Branch (1132:9): [True: 0, False: 0]
|
1133 | 0 | double halflife = ROLLING_FEE_HALFLIFE; |
1134 | 0 | if (DynamicMemoryUsage() < sizelimit / 4) Branch (1134:13): [True: 0, False: 0]
|
1135 | 0 | halflife /= 4; |
1136 | 0 | else if (DynamicMemoryUsage() < sizelimit / 2) Branch (1136:18): [True: 0, False: 0]
|
1137 | 0 | halflife /= 2; |
1138 | |
|
1139 | 0 | rollingMinimumFeeRate = rollingMinimumFeeRate / pow(2.0, (time - lastRollingFeeUpdate) / halflife); |
1140 | 0 | lastRollingFeeUpdate = time; |
1141 | |
|
1142 | 0 | if (rollingMinimumFeeRate < (double)m_opts.incremental_relay_feerate.GetFeePerK() / 2) { Branch (1142:13): [True: 0, False: 0]
|
1143 | 0 | rollingMinimumFeeRate = 0; |
1144 | 0 | return CFeeRate(0); |
1145 | 0 | } |
1146 | 0 | } |
1147 | 0 | return std::max(CFeeRate(llround(rollingMinimumFeeRate)), m_opts.incremental_relay_feerate); |
1148 | 0 | } |
1149 | | |
1150 | 0 | void CTxMemPool::trackPackageRemoved(const CFeeRate& rate) { |
1151 | 0 | AssertLockHeld(cs); |
1152 | 0 | if (rate.GetFeePerK() > rollingMinimumFeeRate) { Branch (1152:9): [True: 0, False: 0]
|
1153 | 0 | rollingMinimumFeeRate = rate.GetFeePerK(); |
1154 | 0 | blockSinceLastRollingFeeBump = false; |
1155 | 0 | } |
1156 | 0 | } |
1157 | | |
1158 | 42.6k | void CTxMemPool::TrimToSize(size_t sizelimit, std::vector<COutPoint>* pvNoSpendsRemaining) { |
1159 | 42.6k | AssertLockHeld(cs); |
1160 | 42.6k | Assume(!m_have_changeset); |
1161 | | |
1162 | 42.6k | unsigned nTxnRemoved = 0; |
1163 | 42.6k | CFeeRate maxFeeRateRemoved(0); |
1164 | 42.6k | while (!mapTx.empty() && DynamicMemoryUsage() > sizelimit) { Branch (1164:12): [True: 41.1k, False: 1.50k]
Branch (1164:30): [True: 0, False: 41.1k]
|
1165 | 0 | indexed_transaction_set::index<descendant_score>::type::iterator it = mapTx.get<descendant_score>().begin(); |
1166 | | |
1167 | | // We set the new mempool min fee to the feerate of the removed set, plus the |
1168 | | // "minimum reasonable fee rate" (ie some value under which we consider txn |
1169 | | // to have 0 fee). This way, we don't allow txn to enter mempool with feerate |
1170 | | // equal to txn which were removed with no block in between. |
1171 | 0 | CFeeRate removed(it->GetModFeesWithDescendants(), it->GetSizeWithDescendants()); |
1172 | 0 | removed += m_opts.incremental_relay_feerate; |
1173 | 0 | trackPackageRemoved(removed); |
1174 | 0 | maxFeeRateRemoved = std::max(maxFeeRateRemoved, removed); |
1175 | |
|
1176 | 0 | setEntries stage; |
1177 | 0 | CalculateDescendants(mapTx.project<0>(it), stage); |
1178 | 0 | nTxnRemoved += stage.size(); |
1179 | |
|
1180 | 0 | std::vector<CTransaction> txn; |
1181 | 0 | if (pvNoSpendsRemaining) { Branch (1181:13): [True: 0, False: 0]
|
1182 | 0 | txn.reserve(stage.size()); |
1183 | 0 | for (txiter iter : stage) Branch (1183:30): [True: 0, False: 0]
|
1184 | 0 | txn.push_back(iter->GetTx()); |
1185 | 0 | } |
1186 | 0 | RemoveStaged(stage, false, MemPoolRemovalReason::SIZELIMIT); |
1187 | 0 | if (pvNoSpendsRemaining) { Branch (1187:13): [True: 0, False: 0]
|
1188 | 0 | for (const CTransaction& tx : txn) { Branch (1188:41): [True: 0, False: 0]
|
1189 | 0 | for (const CTxIn& txin : tx.vin) { Branch (1189:40): [True: 0, False: 0]
|
1190 | 0 | if (exists(GenTxid::Txid(txin.prevout.hash))) continue; Branch (1190:25): [True: 0, False: 0]
|
1191 | 0 | pvNoSpendsRemaining->push_back(txin.prevout); |
1192 | 0 | } |
1193 | 0 | } |
1194 | 0 | } |
1195 | 0 | } |
1196 | | |
1197 | 42.6k | if (maxFeeRateRemoved > CFeeRate(0)) { Branch (1197:9): [True: 0, False: 42.6k]
|
1198 | 0 | LogDebug(BCLog::MEMPOOL, "Removed %u txn, rolling minimum fee bumped to %s\n", nTxnRemoved, maxFeeRateRemoved.ToString()); |
1199 | 0 | } |
1200 | 42.6k | } |
1201 | | |
1202 | 0 | uint64_t CTxMemPool::CalculateDescendantMaximum(txiter entry) const { |
1203 | | // find parent with highest descendant count |
1204 | 0 | std::vector<txiter> candidates; |
1205 | 0 | setEntries counted; |
1206 | 0 | candidates.push_back(entry); |
1207 | 0 | uint64_t maximum = 0; |
1208 | 0 | while (candidates.size()) { Branch (1208:12): [True: 0, False: 0]
|
1209 | 0 | txiter candidate = candidates.back(); |
1210 | 0 | candidates.pop_back(); |
1211 | 0 | if (!counted.insert(candidate).second) continue; Branch (1211:13): [True: 0, False: 0]
|
1212 | 0 | const CTxMemPoolEntry::Parents& parents = candidate->GetMemPoolParentsConst(); |
1213 | 0 | if (parents.size() == 0) { Branch (1213:13): [True: 0, False: 0]
|
1214 | 0 | maximum = std::max(maximum, candidate->GetCountWithDescendants()); |
1215 | 0 | } else { |
1216 | 0 | for (const CTxMemPoolEntry& i : parents) { Branch (1216:43): [True: 0, False: 0]
|
1217 | 0 | candidates.push_back(mapTx.iterator_to(i)); |
1218 | 0 | } |
1219 | 0 | } |
1220 | 0 | } |
1221 | 0 | return maximum; |
1222 | 0 | } |
1223 | | |
1224 | 0 | void CTxMemPool::GetTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants, size_t* const ancestorsize, CAmount* const ancestorfees) const { |
1225 | 0 | LOCK(cs); |
1226 | 0 | auto it = mapTx.find(txid); |
1227 | 0 | ancestors = descendants = 0; |
1228 | 0 | if (it != mapTx.end()) { Branch (1228:9): [True: 0, False: 0]
|
1229 | 0 | ancestors = it->GetCountWithAncestors(); |
1230 | 0 | if (ancestorsize) *ancestorsize = it->GetSizeWithAncestors(); Branch (1230:13): [True: 0, False: 0]
|
1231 | 0 | if (ancestorfees) *ancestorfees = it->GetModFeesWithAncestors(); Branch (1231:13): [True: 0, False: 0]
|
1232 | 0 | descendants = CalculateDescendantMaximum(it); |
1233 | 0 | } |
1234 | 0 | } |
1235 | | |
1236 | | bool CTxMemPool::GetLoadTried() const |
1237 | 11.0k | { |
1238 | 11.0k | LOCK(cs); |
1239 | 11.0k | return m_load_tried; |
1240 | 11.0k | } |
1241 | | |
1242 | | void CTxMemPool::SetLoadTried(bool load_tried) |
1243 | 11.0k | { |
1244 | 11.0k | LOCK(cs); |
1245 | 11.0k | m_load_tried = load_tried; |
1246 | 11.0k | } |
1247 | | |
1248 | | std::vector<CTxMemPool::txiter> CTxMemPool::GatherClusters(const std::vector<uint256>& txids) const |
1249 | 0 | { |
1250 | 0 | AssertLockHeld(cs); |
1251 | 0 | std::vector<txiter> clustered_txs{GetIterVec(txids)}; |
1252 | | // Use epoch: visiting an entry means we have added it to the clustered_txs vector. It does not |
1253 | | // necessarily mean the entry has been processed. |
1254 | 0 | WITH_FRESH_EPOCH(m_epoch); |
1255 | 0 | for (const auto& it : clustered_txs) { Branch (1255:25): [True: 0, False: 0]
|
1256 | 0 | visited(it); |
1257 | 0 | } |
1258 | | // i = index of where the list of entries to process starts |
1259 | 0 | for (size_t i{0}; i < clustered_txs.size(); ++i) { Branch (1259:23): [True: 0, False: 0]
|
1260 | | // DoS protection: if there are 500 or more entries to process, just quit. |
1261 | 0 | if (clustered_txs.size() > 500) return {}; Branch (1261:13): [True: 0, False: 0]
|
1262 | 0 | const txiter& tx_iter = clustered_txs.at(i); |
1263 | 0 | for (const auto& entries : {tx_iter->GetMemPoolParentsConst(), tx_iter->GetMemPoolChildrenConst()}) { Branch (1263:34): [True: 0, False: 0]
|
1264 | 0 | for (const CTxMemPoolEntry& entry : entries) { Branch (1264:47): [True: 0, False: 0]
|
1265 | 0 | const auto entry_it = mapTx.iterator_to(entry); |
1266 | 0 | if (!visited(entry_it)) { Branch (1266:21): [True: 0, False: 0]
|
1267 | 0 | clustered_txs.push_back(entry_it); |
1268 | 0 | } |
1269 | 0 | } |
1270 | 0 | } |
1271 | 0 | } |
1272 | 0 | return clustered_txs; |
1273 | 0 | } |
1274 | | |
1275 | | std::optional<std::string> CTxMemPool::CheckConflictTopology(const setEntries& direct_conflicts) |
1276 | 283 | { |
1277 | 720 | for (const auto& direct_conflict : direct_conflicts) { Branch (1277:38): [True: 720, False: 231]
|
1278 | | // Ancestor and descendant counts are inclusive of the tx itself. |
1279 | 720 | const auto ancestor_count{direct_conflict->GetCountWithAncestors()}; |
1280 | 720 | const auto descendant_count{direct_conflict->GetCountWithDescendants()}; |
1281 | 720 | const bool has_ancestor{ancestor_count > 1}; |
1282 | 720 | const bool has_descendant{descendant_count > 1}; |
1283 | 720 | const auto& txid_string{direct_conflict->GetSharedTx()->GetHash().ToString()}; |
1284 | | // The only allowed configurations are: |
1285 | | // 1 ancestor and 0 descendant |
1286 | | // 0 ancestor and 1 descendant |
1287 | | // 0 ancestor and 0 descendant |
1288 | 720 | if (ancestor_count > 2) { Branch (1288:13): [True: 22, False: 698]
|
1289 | 22 | return strprintf("%s has %u ancestors, max 1 allowed", txid_string, ancestor_count - 1); |
1290 | 698 | } else if (descendant_count > 2) { Branch (1290:20): [True: 11, False: 687]
|
1291 | 11 | return strprintf("%s has %u descendants, max 1 allowed", txid_string, descendant_count - 1); |
1292 | 687 | } else if (has_ancestor && has_descendant) { Branch (1292:20): [True: 284, False: 403]
Branch (1292:36): [True: 9, False: 275]
|
1293 | 9 | return strprintf("%s has both ancestor and descendant, exceeding cluster limit of 2", txid_string); |
1294 | 9 | } |
1295 | | // Additionally enforce that: |
1296 | | // If we have a child, we are its only parent. |
1297 | | // If we have a parent, we are its only child. |
1298 | 678 | if (has_descendant) { Branch (1298:13): [True: 214, False: 464]
|
1299 | 214 | const auto& our_child = direct_conflict->GetMemPoolChildrenConst().begin(); |
1300 | 214 | if (our_child->get().GetCountWithAncestors() > 2) { Branch (1300:17): [True: 1, False: 213]
|
1301 | 1 | return strprintf("%s is not the only parent of child %s", |
1302 | 1 | txid_string, our_child->get().GetSharedTx()->GetHash().ToString()); |
1303 | 1 | } |
1304 | 464 | } else if (has_ancestor) { Branch (1304:20): [True: 275, False: 189]
|
1305 | 275 | const auto& our_parent = direct_conflict->GetMemPoolParentsConst().begin(); |
1306 | 275 | if (our_parent->get().GetCountWithDescendants() > 2) { Branch (1306:17): [True: 9, False: 266]
|
1307 | 9 | return strprintf("%s is not the only child of parent %s", |
1308 | 9 | txid_string, our_parent->get().GetSharedTx()->GetHash().ToString()); |
1309 | 9 | } |
1310 | 275 | } |
1311 | 678 | } |
1312 | 231 | return std::nullopt; |
1313 | 283 | } |
1314 | | |
1315 | | util::Result<std::pair<std::vector<FeeFrac>, std::vector<FeeFrac>>> CTxMemPool::ChangeSet::CalculateChunksForRBF() |
1316 | 283 | { |
1317 | 283 | LOCK(m_pool->cs); |
1318 | 283 | FeeFrac replacement_feerate{0, 0}; |
1319 | 566 | for (auto it : m_entry_vec) { Branch (1319:18): [True: 566, False: 283]
|
1320 | 566 | replacement_feerate += {it->GetModifiedFee(), it->GetTxSize()}; |
1321 | 566 | } |
1322 | | |
1323 | 283 | auto err_string{m_pool->CheckConflictTopology(m_to_remove)}; |
1324 | 283 | if (err_string.has_value()) { Branch (1324:9): [True: 52, False: 231]
|
1325 | | // Unsupported topology for calculating a feerate diagram |
1326 | 52 | return util::Error{Untranslated(err_string.value())}; |
1327 | 52 | } |
1328 | | |
1329 | | // new diagram will have chunks that consist of each ancestor of |
1330 | | // direct_conflicts that is at its own fee/size, along with the replacement |
1331 | | // tx/package at its own fee/size |
1332 | | |
1333 | | // old diagram will consist of the ancestors and descendants of each element of |
1334 | | // all_conflicts. every such transaction will either be at its own feerate (followed |
1335 | | // by any descendant at its own feerate), or as a single chunk at the descendant's |
1336 | | // ancestor feerate. |
1337 | | |
1338 | 231 | std::vector<FeeFrac> old_chunks; |
1339 | | // Step 1: build the old diagram. |
1340 | | |
1341 | | // The above clusters are all trivially linearized; |
1342 | | // they have a strict topology of 1 or two connected transactions. |
1343 | | |
1344 | | // OLD: Compute existing chunks from all affected clusters |
1345 | 659 | for (auto txiter : m_to_remove) { Branch (1345:22): [True: 659, False: 231]
|
1346 | | // Does this transaction have descendants? |
1347 | 659 | if (txiter->GetCountWithDescendants() > 1) { Branch (1347:13): [True: 207, False: 452]
|
1348 | | // Consider this tx when we consider the descendant. |
1349 | 207 | continue; |
1350 | 207 | } |
1351 | | // Does this transaction have ancestors? |
1352 | 452 | FeeFrac individual{txiter->GetModifiedFee(), txiter->GetTxSize()}; |
1353 | 452 | if (txiter->GetCountWithAncestors() > 1) { Branch (1353:13): [True: 264, False: 188]
|
1354 | | // We'll add chunks for either the ancestor by itself and this tx |
1355 | | // by itself, or for a combined package. |
1356 | 264 | FeeFrac package{txiter->GetModFeesWithAncestors(), static_cast<int32_t>(txiter->GetSizeWithAncestors())}; |
1357 | 264 | if (individual >> package) { Branch (1357:17): [True: 54, False: 210]
|
1358 | | // The individual feerate is higher than the package, and |
1359 | | // therefore higher than the parent's fee. Chunk these |
1360 | | // together. |
1361 | 54 | old_chunks.emplace_back(package); |
1362 | 210 | } else { |
1363 | | // Add two points, one for the parent and one for this child. |
1364 | 210 | old_chunks.emplace_back(package - individual); |
1365 | 210 | old_chunks.emplace_back(individual); |
1366 | 210 | } |
1367 | 264 | } else { |
1368 | 188 | old_chunks.emplace_back(individual); |
1369 | 188 | } |
1370 | 452 | } |
1371 | | |
1372 | | // No topology restrictions post-chunking; sort |
1373 | 231 | std::sort(old_chunks.begin(), old_chunks.end(), std::greater()); |
1374 | | |
1375 | 231 | std::vector<FeeFrac> new_chunks; |
1376 | | |
1377 | | /* Step 2: build the NEW diagram |
1378 | | * CON = Conflicts of proposed chunk |
1379 | | * CNK = Proposed chunk |
1380 | | * NEW = OLD - CON + CNK: New diagram includes all chunks in OLD, minus |
1381 | | * the conflicts, plus the proposed chunk |
1382 | | */ |
1383 | | |
1384 | | // OLD - CON: Add any parents of direct conflicts that are not conflicted themselves |
1385 | 659 | for (auto direct_conflict : m_to_remove) { Branch (1385:31): [True: 659, False: 231]
|
1386 | | // If a direct conflict has an ancestor that is not in all_conflicts, |
1387 | | // it can be affected by the replacement of the child. |
1388 | 659 | if (direct_conflict->GetMemPoolParentsConst().size() > 0) { Branch (1388:13): [True: 264, False: 395]
|
1389 | | // Grab the parent. |
1390 | 264 | const CTxMemPoolEntry& parent = direct_conflict->GetMemPoolParentsConst().begin()->get(); |
1391 | 264 | if (!m_to_remove.contains(m_pool->mapTx.iterator_to(parent))) { Branch (1391:17): [True: 57, False: 207]
|
1392 | | // This transaction would be left over, so add to the NEW |
1393 | | // diagram. |
1394 | 57 | new_chunks.emplace_back(parent.GetModifiedFee(), parent.GetTxSize()); |
1395 | 57 | } |
1396 | 264 | } |
1397 | 659 | } |
1398 | | // + CNK: Add the proposed chunk itself |
1399 | 231 | new_chunks.emplace_back(replacement_feerate); |
1400 | | |
1401 | | // No topology restrictions post-chunking; sort |
1402 | 231 | std::sort(new_chunks.begin(), new_chunks.end(), std::greater()); |
1403 | 231 | return std::make_pair(old_chunks, new_chunks); |
1404 | 283 | } |
1405 | | |
1406 | | CTxMemPool::ChangeSet::TxHandle CTxMemPool::ChangeSet::StageAddition(const CTransactionRef& tx, const CAmount fee, int64_t time, unsigned int entry_height, uint64_t entry_sequence, bool spends_coinbase, int64_t sigops_cost, LockPoints lp) |
1407 | 63.2k | { |
1408 | 63.2k | LOCK(m_pool->cs); |
1409 | 63.2k | Assume(m_to_add.find(tx->GetHash()) == m_to_add.end()); |
1410 | 63.2k | auto newit = m_to_add.emplace(tx, fee, time, entry_height, entry_sequence, spends_coinbase, sigops_cost, lp).first; |
1411 | 63.2k | CAmount delta{0}; |
1412 | 63.2k | m_pool->ApplyDelta(tx->GetHash(), delta); |
1413 | 63.2k | if (delta) m_to_add.modify(newit, [&delta](CTxMemPoolEntry& e) { e.UpdateModifiedFee(delta); }); Branch (1413:9): [True: 0, False: 63.2k]
|
1414 | | |
1415 | 63.2k | m_entry_vec.push_back(newit); |
1416 | 63.2k | return newit; |
1417 | 63.2k | } |
1418 | | |
1419 | | void CTxMemPool::ChangeSet::Apply() |
1420 | 43.1k | { |
1421 | 43.1k | LOCK(m_pool->cs); |
1422 | 43.1k | m_pool->Apply(this); |
1423 | 43.1k | m_to_add.clear(); |
1424 | 43.1k | m_to_remove.clear(); |
1425 | 43.1k | m_entry_vec.clear(); |
1426 | 43.1k | m_ancestors.clear(); |
1427 | 43.1k | } |