Branch data Line data Source code
1 : : // Copyright (c) 2023-present The Bitcoin Core developers
2 : : // Distributed under the MIT software license, see the accompanying
3 : : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 : :
5 : : #include <node/mini_miner.h>
6 : :
7 : : #include <boost/multi_index/detail/hash_index_iterator.hpp>
8 : : #include <boost/operators.hpp>
9 : : #include <consensus/amount.h>
10 : : #include <policy/feerate.h>
11 : : #include <primitives/transaction.h>
12 : : #include <sync.h>
13 : : #include <txmempool.h>
14 : : #include <uint256.h>
15 : : #include <util/check.h>
16 : :
17 : : #include <algorithm>
18 : : #include <numeric>
19 : : #include <ranges>
20 : : #include <utility>
21 : : #include <vector>
22 : :
23 : : namespace node {
24 : :
25 [ + - ]: 10143 : MiniMiner::MiniMiner(const CTxMemPool& mempool, const std::vector<COutPoint>& outpoints)
26 : : {
27 [ + - ]: 10143 : LOCK(mempool.cs);
28 : : // Find which outpoints to calculate bump fees for.
29 : : // Anything that's spent by the mempool is to-be-replaced
30 : : // Anything otherwise unavailable just has a bump fee of 0
31 [ + + ]: 403338 : for (const auto& outpoint : outpoints) {
32 [ + - + + ]: 393195 : if (!mempool.exists(outpoint.hash)) {
33 : : // This UTXO is either confirmed or not yet submitted to mempool.
34 : : // If it's confirmed, no bump fee is required.
35 : : // If it's not yet submitted, we have no information, so return 0.
36 [ + - ]: 307289 : m_bump_fees.emplace(outpoint, 0);
37 : 307289 : continue;
38 : : }
39 : :
40 : : // UXTO is created by transaction in mempool, add to map.
41 : : // Note: This will either create a missing entry or add the outpoint to an existing entry
42 [ + - + - ]: 85906 : m_requested_outpoints_by_txid[outpoint.hash].push_back(outpoint);
43 : :
44 [ + - + + ]: 85906 : if (const auto ptx{mempool.GetConflictTx(outpoint)}) {
45 : : // This outpoint is already being spent by another transaction in the mempool. We
46 : : // assume that the caller wants to replace this transaction and its descendants. It
47 : : // would be unusual for the transaction to have descendants as the wallet won’t normally
48 : : // attempt to replace transactions with descendants. If the outpoint is from a mempool
49 : : // transaction, we still need to calculate its ancestors bump fees (added to
50 : : // m_requested_outpoints_by_txid below), but after removing the to-be-replaced entries.
51 : : //
52 : : // Note that the descendants of a transaction include the transaction itself. Also note,
53 : : // that this is only calculating bump fees. RBF fee rules should be handled separately.
54 [ + - ]: 17212 : CTxMemPool::setEntries descendants;
55 [ + - + - ]: 34424 : mempool.CalculateDescendants(mempool.GetIter(ptx->GetHash()).value(), descendants);
56 [ + + ]: 253118 : for (const auto& desc_txiter : descendants) {
57 [ + - ]: 235906 : m_to_be_replaced.insert(desc_txiter->GetTx().GetHash());
58 : : }
59 : 17212 : }
60 : : }
61 : :
62 : : // No unconfirmed UTXOs, so nothing mempool-related needs to be calculated.
63 [ + + ]: 10143 : if (m_requested_outpoints_by_txid.empty()) return;
64 : :
65 : : // Calculate the cluster and construct the entry map.
66 [ + - ]: 2300 : auto txids_needed{m_requested_outpoints_by_txid | std::views::keys};
67 [ + - + - : 4600 : const auto cluster = mempool.GatherClusters({txids_needed.begin(), txids_needed.end()});
- + ]
68 [ - + ]: 2300 : if (cluster.empty()) {
69 : : // An empty cluster means that at least one of the transactions is missing from the mempool
70 : : // (should not be possible given processing above) or DoS limit was hit.
71 : 0 : m_ready_to_calculate = false;
72 : 0 : return;
73 : : }
74 : :
75 : : // Add every entry to m_entries_by_txid and m_entries, except the ones that will be replaced.
76 [ + + ]: 140888 : for (const auto& txiter : cluster) {
77 [ + + ]: 138588 : if (!m_to_be_replaced.contains(txiter->GetTx().GetHash())) {
78 [ + - + - ]: 97510 : auto [ancestor_count, ancestor_size, ancestor_fee] = mempool.CalculateAncestorData(*txiter);
79 [ + - + - : 195020 : auto [mapiter, success] = m_entries_by_txid.emplace(txiter->GetTx().GetHash(),
+ - ]
80 [ + - + - : 292530 : MiniMinerMempoolEntry{/*tx_in=*/txiter->GetSharedTx(),
+ - ]
81 [ + - ]: 97510 : /*vsize_self=*/txiter->GetTxSize(),
82 : : /*vsize_ancestor=*/int64_t(ancestor_size),
83 : : /*fee_self=*/txiter->GetModifiedFee(),
84 [ + - + - : 195020 : /*fee_ancestor=*/ancestor_fee});
+ - ]
85 [ + - ]: 97510 : m_entries.push_back(mapiter);
86 : : } else {
87 : 41078 : auto outpoints_it = m_requested_outpoints_by_txid.find(txiter->GetTx().GetHash());
88 [ + + ]: 41078 : if (outpoints_it != m_requested_outpoints_by_txid.end()) {
89 : : // This UTXO is the output of a to-be-replaced transaction. Bump fee is 0; spending
90 : : // this UTXO is impossible as it will no longer exist after the replacement.
91 [ + + ]: 44832 : for (const auto& outpoint : outpoints_it->second) {
92 [ + - ]: 22458 : m_bump_fees.emplace(outpoint, 0);
93 : : }
94 : 22374 : m_requested_outpoints_by_txid.erase(outpoints_it);
95 : : }
96 : : }
97 : : }
98 : :
99 : : // Build the m_descendant_set_by_txid cache.
100 [ + + ]: 140888 : for (const auto& txiter : cluster) {
101 : 138588 : const auto& txid = txiter->GetTx().GetHash();
102 : : // Cache descendants for future use. Unlike the real mempool, a descendant MiniMinerMempoolEntry
103 : : // will not exist without its ancestor MiniMinerMempoolEntry, so these sets won't be invalidated.
104 : 138588 : std::vector<MockEntryMap::iterator> cached_descendants;
105 : 138588 : const bool remove{m_to_be_replaced.contains(txid)};
106 [ + - ]: 138588 : CTxMemPool::setEntries descendants;
107 [ + - ]: 138588 : mempool.CalculateDescendants(txiter, descendants);
108 [ + - ]: 138588 : Assume(descendants.contains(txiter));
109 [ + + ]: 1638916 : for (const auto& desc_txiter : descendants) {
110 : 1500328 : const auto txid_desc = desc_txiter->GetTx().GetHash();
111 : 1500328 : const bool remove_desc{m_to_be_replaced.contains(txid_desc)};
112 : 1500328 : auto desc_it{m_entries_by_txid.find(txid_desc)};
113 [ + - ]: 1500328 : Assume((desc_it == m_entries_by_txid.end()) == remove_desc);
114 [ + + + - ]: 1500328 : if (remove) Assume(remove_desc);
115 : : // It's possible that remove=false but remove_desc=true.
116 [ + + ]: 975040 : if (!remove && !remove_desc) {
117 [ + - ]: 632898 : cached_descendants.push_back(desc_it);
118 : : }
119 : : }
120 [ + + ]: 138588 : if (remove) {
121 [ + - ]: 41078 : Assume(cached_descendants.empty());
122 : : } else {
123 [ + - ]: 97510 : m_descendant_set_by_txid.emplace(txid, cached_descendants);
124 : : }
125 : 138588 : }
126 : :
127 : : // Release the mempool lock; we now have all the information we need for a subset of the entries
128 : : // we care about. We will solely operate on the MiniMinerMempoolEntry map from now on.
129 [ + - ]: 2300 : Assume(m_in_block.empty());
130 [ - + + - ]: 2300 : Assume(m_requested_outpoints_by_txid.size() <= outpoints.size());
131 [ + - ]: 2300 : SanityCheck();
132 [ + - ]: 12443 : }
133 : :
134 : 0 : MiniMiner::MiniMiner(const std::vector<MiniMinerMempoolEntry>& manual_entries,
135 : 0 : const std::map<Txid, std::set<Txid>>& descendant_caches)
136 : : {
137 [ # # ]: 0 : for (const auto& entry : manual_entries) {
138 : 0 : const auto& txid = entry.GetTx().GetHash();
139 : : // We need to know the descendant set of every transaction.
140 [ # # # # ]: 0 : if (!Assume(descendant_caches.contains(txid))) {
141 : 0 : m_ready_to_calculate = false;
142 : 0 : return;
143 : : }
144 : : // Just forward these args onto MiniMinerMempoolEntry
145 [ # # # # ]: 0 : auto [mapiter, success] = m_entries_by_txid.emplace(txid, entry);
146 : : // Txids must be unique; this txid shouldn't already be an entry in m_entries_by_txid
147 [ # # # # : 0 : if (Assume(success)) m_entries.push_back(mapiter);
# # ]
148 : : }
149 : : // Descendant cache is already built, but we need to translate them to m_entries_by_txid iters.
150 [ # # # # ]: 0 : for (const auto& [txid, desc_txids] : descendant_caches) {
151 : : // Descendant cache should include at least the tx itself.
152 [ # # # # ]: 0 : if (!Assume(!desc_txids.empty())) {
153 : 0 : m_ready_to_calculate = false;
154 : 0 : return;
155 : : }
156 : 0 : std::vector<MockEntryMap::iterator> descendants;
157 [ # # ]: 0 : for (const auto& desc_txid : desc_txids) {
158 : 0 : auto desc_it{m_entries_by_txid.find(desc_txid)};
159 : : // Descendants should only include transactions with corresponding entries.
160 [ # # # # ]: 0 : if (!Assume(desc_it != m_entries_by_txid.end())) {
161 : 0 : m_ready_to_calculate = false;
162 : 0 : return;
163 : : } else {
164 [ # # ]: 0 : descendants.emplace_back(desc_it);
165 : : }
166 : : }
167 [ # # ]: 0 : m_descendant_set_by_txid.emplace(txid, descendants);
168 : 0 : }
169 [ # # ]: 0 : Assume(m_to_be_replaced.empty());
170 [ # # ]: 0 : Assume(m_requested_outpoints_by_txid.empty());
171 [ # # ]: 0 : Assume(m_bump_fees.empty());
172 [ # # ]: 0 : Assume(m_inclusion_order.empty());
173 [ # # ]: 0 : SanityCheck();
174 : 0 : }
175 : :
176 : : // Compare by min(ancestor feerate, individual feerate), then txid
177 : : //
178 : : // Under the ancestor-based mining approach, high-feerate children can pay for parents, but high-feerate
179 : : // parents do not incentive inclusion of their children. Therefore the mining algorithm only considers
180 : : // transactions for inclusion on basis of the minimum of their own feerate or their ancestor feerate.
181 : : struct AncestorFeerateComparator
182 : : {
183 : : template<typename I>
184 [ + + ]: 10438210 : bool operator()(const I& a, const I& b) const {
185 : 20876420 : auto min_feerate = [](const MiniMinerMempoolEntry& e) -> FeeFrac {
186 : 20876420 : FeeFrac self_feerate(e.GetModifiedFee(), e.GetTxSize());
187 : 20876420 : FeeFrac ancestor_feerate(e.GetModFeesWithAncestors(), e.GetSizeWithAncestors());
188 : 20876420 : return std::min<ByRatioNegSize<FeeFrac>>(ancestor_feerate, self_feerate);
189 : : };
190 : 10438210 : FeeFrac a_feerate{min_feerate(a->second)};
191 [ + + ]: 10438210 : FeeFrac b_feerate{min_feerate(b->second)};
192 : 10438210 : if (a_feerate != b_feerate) {
193 : 3485008 : return ByRatioNegSize{a_feerate} > ByRatioNegSize{b_feerate};
194 : : }
195 : : // Use txid as tiebreaker for stable sorting
196 : 6953202 : return a->first < b->first;
197 : : }
198 : : };
199 : :
200 : 62976 : void MiniMiner::DeleteAncestorPackage(const std::set<MockEntryMap::iterator, IteratorComparator>& ancestors)
201 : : {
202 : 62976 : Assume(ancestors.size() >= 1);
203 : : // "Mine" all transactions in this ancestor set.
204 [ + + ]: 142578 : for (auto& anc : ancestors) {
205 : 79602 : Assume(!m_in_block.contains(anc->first));
206 : 79602 : m_in_block.insert(anc->first);
207 : 79602 : m_total_fees += anc->second.GetModifiedFee();
208 : 79602 : m_total_vsize += anc->second.GetTxSize();
209 : 79602 : auto it = m_descendant_set_by_txid.find(anc->first);
210 : : // Each entry’s descendant set includes itself
211 : 79602 : Assume(it != m_descendant_set_by_txid.end());
212 [ + + ]: 560964 : for (auto& descendant : it->second) {
213 : : // If this fails, we must be double-deducting. Don't check fees because negative is possible.
214 : 481362 : Assume(descendant->second.GetSizeWithAncestors() >= anc->second.GetTxSize());
215 : 481362 : descendant->second.UpdateAncestorState(-anc->second.GetTxSize(), -anc->second.GetModifiedFee());
216 : : }
217 : : }
218 : : // Delete these entries.
219 [ + + ]: 142578 : for (const auto& anc : ancestors) {
220 : 79602 : m_descendant_set_by_txid.erase(anc->first);
221 : : // The above loop should have deducted each ancestor's size and fees from each of their
222 : : // respective descendants exactly once.
223 : 79602 : Assume(anc->second.GetModFeesWithAncestors() == 0);
224 : 79602 : Assume(anc->second.GetSizeWithAncestors() == 0);
225 : 79602 : Assume(std::erase(m_entries, anc) == 1);
226 : 79602 : m_entries_by_txid.erase(anc);
227 : : }
228 : 62976 : }
229 : :
230 : 65276 : void MiniMiner::SanityCheck() const
231 : : {
232 : : // m_entries, m_entries_by_txid, and m_descendant_set_by_txid all same size
233 [ - + ]: 65276 : Assume(m_entries.size() == m_entries_by_txid.size());
234 [ - + ]: 65276 : Assume(m_entries.size() == m_descendant_set_by_txid.size());
235 : : // Cached ancestor values should be at least as large as the transaction's own size
236 [ + - ]: 2245826 : Assume(std::all_of(m_entries.begin(), m_entries.end(), [](const auto& entry) {
237 : : return entry->second.GetSizeWithAncestors() >= entry->second.GetTxSize();}));
238 : : // None of the entries should be to-be-replaced transactions
239 : 653468 : Assume(std::all_of(m_to_be_replaced.begin(), m_to_be_replaced.end(),
240 : : [&](const auto& txid){ return !m_entries_by_txid.contains(txid); }));
241 : 65276 : }
242 : :
243 : 10143 : void MiniMiner::BuildMockTemplate(std::optional<CFeeRate> target_feerate)
244 : : {
245 : 10143 : const auto num_txns{m_entries_by_txid.size()};
246 : 10143 : uint32_t sequence_num{0};
247 [ + + ]: 73119 : while (!m_entries_by_txid.empty()) {
248 : : // Sort again, since transaction removal may change some m_entries' ancestor feerates.
249 : 63594 : std::sort(m_entries.begin(), m_entries.end(), AncestorFeerateComparator());
250 : :
251 : : // Pick highest ancestor feerate entry.
252 : 63594 : auto best_iter = m_entries.begin();
253 : 63594 : Assume(best_iter != m_entries.end());
254 [ + - ]: 63594 : const auto ancestor_package_size = (*best_iter)->second.GetSizeWithAncestors();
255 [ + - ]: 63594 : const auto ancestor_package_fee = (*best_iter)->second.GetModFeesWithAncestors();
256 : : // Stop here. Everything that didn't "make it into the block" has bumpfee.
257 [ + - + + ]: 127188 : if (target_feerate.has_value() &&
258 : 63594 : ancestor_package_fee < target_feerate->GetFee(ancestor_package_size)) {
259 : : break;
260 : : }
261 : :
262 : : // Calculate ancestors on the fly. This lookup should be fairly cheap, and ancestor sets
263 : : // change at every iteration, so this is more efficient than maintaining a cache.
264 [ + - ]: 62976 : std::set<MockEntryMap::iterator, IteratorComparator> ancestors;
265 : 62976 : {
266 : 62976 : std::set<MockEntryMap::iterator, IteratorComparator> to_process;
267 [ + - ]: 62976 : to_process.insert(*best_iter);
268 [ + + ]: 142578 : while (!to_process.empty()) {
269 [ + - ]: 79602 : auto iter = to_process.begin();
270 [ + - ]: 79602 : Assume(iter != to_process.end());
271 [ + - ]: 79602 : ancestors.insert(*iter);
272 [ + + ]: 449812 : for (const auto& input : (*iter)->second.GetTx().vin) {
273 [ + + ]: 370210 : if (auto parent_it{m_entries_by_txid.find(input.prevout.hash)}; parent_it != m_entries_by_txid.end()) {
274 [ + + ]: 92664 : if (!ancestors.contains(parent_it)) {
275 [ + - ]: 76084 : to_process.insert(parent_it);
276 : : }
277 : : }
278 : : }
279 : 79602 : to_process.erase(iter);
280 : : }
281 : 0 : }
282 : : // Track the order in which transactions were selected.
283 [ + + ]: 142578 : for (const auto& ancestor : ancestors) {
284 [ + - ]: 79602 : m_inclusion_order.emplace(ancestor->first, sequence_num);
285 : : }
286 [ + - ]: 62976 : DeleteAncestorPackage(ancestors);
287 [ + - ]: 62976 : SanityCheck();
288 : 62976 : ++sequence_num;
289 : 62976 : }
290 [ - + ]: 10143 : if (!target_feerate.has_value()) {
291 : 0 : Assume(m_in_block.size() == num_txns);
292 : : } else {
293 [ + + + - ]: 12039 : Assume(m_in_block.empty() || m_total_fees >= target_feerate->GetFee(m_total_vsize));
294 : : }
295 [ + + - + ]: 10143 : Assume(m_in_block.empty() || sequence_num > 0);
296 : 10143 : Assume(m_in_block.size() == m_inclusion_order.size());
297 : : // Do not try to continue building the block template with a different feerate.
298 : 10143 : m_ready_to_calculate = false;
299 : 10143 : }
300 : :
301 : :
302 : 0 : std::map<Txid, uint32_t> MiniMiner::Linearize()
303 : : {
304 : 0 : BuildMockTemplate(std::nullopt);
305 : 0 : return m_inclusion_order;
306 : : }
307 : :
308 : 3121 : std::map<COutPoint, CAmount> MiniMiner::CalculateBumpFees(const CFeeRate& target_feerate)
309 : : {
310 [ - + ]: 3121 : if (!m_ready_to_calculate) return {};
311 : : // Build a block template until the target feerate is hit.
312 : 3121 : BuildMockTemplate(target_feerate);
313 : :
314 : : // Each transaction that "made it into the block" has a bumpfee of 0, i.e. they are part of an
315 : : // ancestor package with at least the target feerate and don't need to be bumped.
316 [ + + ]: 42922 : for (const auto& txid : m_in_block) {
317 : : // Not all of the block transactions were necessarily requested.
318 : 39801 : auto it = m_requested_outpoints_by_txid.find(txid);
319 [ + + ]: 39801 : if (it != m_requested_outpoints_by_txid.end()) {
320 [ + + ]: 47957 : for (const auto& outpoint : it->second) {
321 : 24072 : m_bump_fees.emplace(outpoint, 0);
322 : : }
323 : 23885 : m_requested_outpoints_by_txid.erase(it);
324 : : }
325 : : }
326 : :
327 : : // A transactions and its ancestors will only be picked into a block when
328 : : // both the ancestor set feerate and the individual feerate meet the target
329 : : // feerate.
330 : : //
331 : : // We had to convince ourselves that after running the mini miner and
332 : : // picking all eligible transactions into our MockBlockTemplate, there
333 : : // could still be transactions remaining that have a lower individual
334 : : // feerate than their ancestor feerate. So here is an example:
335 : : //
336 : : // ┌─────────────────┐
337 : : // │ │
338 : : // │ Grandparent │
339 : : // │ 1700 vB │
340 : : // │ 1700 sats │ Target feerate: 10 s/vB
341 : : // │ 1 s/vB │ GP Ancestor Set Feerate (ASFR): 1 s/vB
342 : : // │ │ P1_ASFR: 9.84 s/vB
343 : : // └──────▲───▲──────┘ P2_ASFR: 2.47 s/vB
344 : : // │ │ C_ASFR: 10.27 s/vB
345 : : // ┌───────────────┐ │ │ ┌──────────────┐
346 : : // │ ├────┘ └────┤ │ ⇒ C_FR < TFR < C_ASFR
347 : : // │ Parent 1 │ │ Parent 2 │
348 : : // │ 200 vB │ │ 200 vB │
349 : : // │ 17000 sats │ │ 3000 sats │
350 : : // │ 85 s/vB │ │ 15 s/vB │
351 : : // │ │ │ │
352 : : // └───────────▲───┘ └───▲──────────┘
353 : : // │ │
354 : : // │ ┌───────────┐ │
355 : : // └────┤ ├────┘
356 : : // │ Child │
357 : : // │ 100 vB │
358 : : // │ 900 sats │
359 : : // │ 9 s/vB │
360 : : // │ │
361 : : // └───────────┘
362 : : //
363 : : // We therefore calculate both the bump fee that is necessary to elevate
364 : : // the individual transaction to the target feerate:
365 : : // target_feerate × tx_size - tx_fees
366 : : // and the bump fee that is necessary to bump the entire ancestor set to
367 : : // the target feerate:
368 : : // target_feerate × ancestor_set_size - ancestor_set_fees
369 : : // By picking the maximum from the two, we ensure that a transaction meets
370 : : // both criteria.
371 [ + + ]: 10505 : for (const auto& [txid, outpoints] : m_requested_outpoints_by_txid) {
372 : 7384 : auto it = m_entries_by_txid.find(txid);
373 : 7384 : Assume(it != m_entries_by_txid.end());
374 [ + - ]: 7384 : if (it != m_entries_by_txid.end()) {
375 [ + - ]: 14768 : Assume(target_feerate.GetFee(it->second.GetSizeWithAncestors()) > std::min(it->second.GetModifiedFee(), it->second.GetModFeesWithAncestors()));
376 : 7384 : CAmount bump_fee_with_ancestors = target_feerate.GetFee(it->second.GetSizeWithAncestors()) - it->second.GetModFeesWithAncestors();
377 [ + + ]: 7384 : CAmount bump_fee_individual = target_feerate.GetFee(it->second.GetTxSize()) - it->second.GetModifiedFee();
378 [ + + ]: 7384 : const CAmount bump_fee{std::max(bump_fee_with_ancestors, bump_fee_individual)};
379 : 7384 : Assume(bump_fee >= 0);
380 [ + + ]: 15036 : for (const auto& outpoint : outpoints) {
381 : 7652 : m_bump_fees.emplace(outpoint, bump_fee);
382 : : }
383 : : }
384 : : }
385 : 3121 : return m_bump_fees;
386 : : }
387 : :
388 : 7022 : std::optional<CAmount> MiniMiner::CalculateTotalBumpFees(const CFeeRate& target_feerate)
389 : : {
390 [ - + ]: 7022 : if (!m_ready_to_calculate) return std::nullopt;
391 : : // Build a block template until the target feerate is hit.
392 : 7022 : BuildMockTemplate(target_feerate);
393 : :
394 : : // All remaining ancestors that are not part of m_in_block must be bumped, but no other relatives
395 : 7022 : std::set<MockEntryMap::iterator, IteratorComparator> ancestors;
396 : 7022 : std::set<MockEntryMap::iterator, IteratorComparator> to_process;
397 [ + + ]: 38291 : for (const auto& [txid, outpoints] : m_requested_outpoints_by_txid) {
398 : : // Skip any ancestors that already have a miner score higher than the target feerate
399 : : // (already "made it" into the block)
400 [ + + ]: 31269 : if (m_in_block.contains(txid)) continue;
401 : 7384 : auto iter = m_entries_by_txid.find(txid);
402 [ - + ]: 7384 : if (iter == m_entries_by_txid.end()) continue;
403 [ + - ]: 7384 : to_process.insert(iter);
404 [ + - ]: 7384 : ancestors.insert(iter);
405 : : }
406 : :
407 : 7022 : std::set<Txid> has_been_processed;
408 [ + + ]: 15159 : while (!to_process.empty()) {
409 : 8137 : auto iter = to_process.begin();
410 : 8137 : const CTransaction& tx = (*iter)->second.GetTx();
411 [ + + ]: 94086 : for (const auto& input : tx.vin) {
412 [ + + ]: 85949 : if (auto parent_it{m_entries_by_txid.find(input.prevout.hash)}; parent_it != m_entries_by_txid.end()) {
413 [ + + ]: 59775 : if (!has_been_processed.contains(input.prevout.hash)) {
414 [ + - ]: 31106 : to_process.insert(parent_it);
415 : : }
416 [ + - ]: 59775 : ancestors.insert(parent_it);
417 : : }
418 : : }
419 [ + - ]: 8137 : has_been_processed.insert(tx.GetHash());
420 : 8137 : to_process.erase(iter);
421 : : }
422 : 7022 : const auto ancestor_package_size = std::accumulate(ancestors.cbegin(), ancestors.cend(), int64_t{0},
423 : 8137 : [](int64_t sum, const auto it) {return sum + it->second.GetTxSize();});
424 : 7022 : const auto ancestor_package_fee = std::accumulate(ancestors.cbegin(), ancestors.cend(), CAmount{0},
425 : 8137 : [](CAmount sum, const auto it) {return sum + it->second.GetModifiedFee();});
426 [ + - ]: 7022 : return target_feerate.GetFee(ancestor_package_size) - ancestor_package_fee;
427 : 7022 : }
428 : : } // namespace node
|