Branch data Line data Source code
1 : : // Copyright (c) 2023 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 <consensus/amount.h>
8 : : #include <policy/feerate.h>
9 : : #include <primitives/transaction.h>
10 : : #include <util/check.h>
11 : :
12 : : #include <algorithm>
13 : : #include <numeric>
14 : : #include <utility>
15 : :
16 : : namespace node {
17 [ + - ]: 2 :
18 [ + - ]: 2 : MiniMiner::MiniMiner(const CTxMemPool& mempool, const std::vector<COutPoint>& outpoints)
19 : : {
20 [ # # # # ]: 0 : LOCK(mempool.cs);
21 : : // Find which outpoints to calculate bump fees for.
22 : : // Anything that's spent by the mempool is to-be-replaced
23 : : // Anything otherwise unavailable just has a bump fee of 0
24 [ # # ]: 0 : for (const auto& outpoint : outpoints) {
25 [ # # # # : 0 : if (!mempool.exists(GenTxid::Txid(outpoint.hash))) {
# # ]
26 : : // This UTXO is either confirmed or not yet submitted to mempool.
27 : : // If it's confirmed, no bump fee is required.
28 : : // If it's not yet submitted, we have no information, so return 0.
29 [ # # ]: 0 : m_bump_fees.emplace(outpoint, 0);
30 : 0 : continue;
31 : : }
32 : :
33 : : // UXTO is created by transaction in mempool, add to map.
34 : : // Note: This will either create a missing entry or add the outpoint to an existing entry
35 [ # # # # ]: 0 : m_requested_outpoints_by_txid[outpoint.hash].push_back(outpoint);
36 : :
37 [ # # # # ]: 0 : if (const auto ptx{mempool.GetConflictTx(outpoint)}) {
38 : : // This outpoint is already being spent by another transaction in the mempool. We
39 : : // assume that the caller wants to replace this transaction and its descendants. It
40 : : // would be unusual for the transaction to have descendants as the wallet won’t normally
41 : : // attempt to replace transactions with descendants. If the outpoint is from a mempool
42 : : // transaction, we still need to calculate its ancestors bump fees (added to
43 : : // m_requested_outpoints_by_txid below), but after removing the to-be-replaced entries.
44 : : //
45 : : // Note that the descendants of a transaction include the transaction itself. Also note,
46 : : // that this is only calculating bump fees. RBF fee rules should be handled separately.
47 : 0 : CTxMemPool::setEntries descendants;
48 [ # # # # : 0 : mempool.CalculateDescendants(mempool.GetIter(ptx->GetHash()).value(), descendants);
# # # # ]
49 [ # # ]: 0 : for (const auto& desc_txiter : descendants) {
50 [ # # # # : 0 : m_to_be_replaced.insert(desc_txiter->GetTx().GetHash());
# # # # ]
51 : : }
52 : 0 : }
53 : : }
54 : :
55 : : // No unconfirmed UTXOs, so nothing mempool-related needs to be calculated.
56 [ # # ]: 0 : if (m_requested_outpoints_by_txid.empty()) return;
57 : :
58 : : // Calculate the cluster and construct the entry map.
59 : 0 : std::vector<uint256> txids_needed;
60 [ # # ]: 0 : txids_needed.reserve(m_requested_outpoints_by_txid.size());
61 [ # # ]: 0 : for (const auto& [txid, _]: m_requested_outpoints_by_txid) {
62 [ # # ]: 0 : txids_needed.push_back(txid);
63 : : }
64 [ # # ]: 0 : const auto cluster = mempool.GatherClusters(txids_needed);
65 [ # # ]: 0 : if (cluster.empty()) {
66 : : // An empty cluster means that at least one of the transactions is missing from the mempool
67 : : // (should not be possible given processing above) or DoS limit was hit.
68 : 0 : m_ready_to_calculate = false;
69 : 0 : return;
70 : : }
71 : :
72 : : // Add every entry to m_entries_by_txid and m_entries, except the ones that will be replaced.
73 [ # # ]: 0 : for (const auto& txiter : cluster) {
74 [ # # # # : 2 : if (!m_to_be_replaced.count(txiter->GetTx().GetHash())) {
# # # # #
# ]
75 [ # # # # : 0 : auto [mapiter, success] = m_entries_by_txid.emplace(txiter->GetTx().GetHash(), MiniMinerMempoolEntry(txiter));
# # # # #
# ]
76 [ # # # # ]: 0 : m_entries.push_back(mapiter);
77 : 0 : } else {
78 [ # # # # : 0 : auto outpoints_it = m_requested_outpoints_by_txid.find(txiter->GetTx().GetHash());
# # # # ]
79 [ # # ]: 0 : if (outpoints_it != m_requested_outpoints_by_txid.end()) {
80 : : // This UTXO is the output of a to-be-replaced transaction. Bump fee is 0; spending
81 : : // this UTXO is impossible as it will no longer exist after the replacement.
82 [ # # ]: 0 : for (const auto& outpoint : outpoints_it->second) {
83 [ # # ]: 0 : m_bump_fees.emplace(outpoint, 0);
84 : : }
85 [ # # ]: 0 : m_requested_outpoints_by_txid.erase(outpoints_it);
86 : 0 : }
87 : : }
88 : : }
89 : :
90 : : // Build the m_descendant_set_by_txid cache.
91 [ # # ]: 0 : for (const auto& txiter : cluster) {
92 [ # # # # : 0 : const auto& txid = txiter->GetTx().GetHash();
# # ]
93 : : // Cache descendants for future use. Unlike the real mempool, a descendant MiniMinerMempoolEntry
94 : : // will not exist without its ancestor MiniMinerMempoolEntry, so these sets won't be invalidated.
95 : 0 : std::vector<MockEntryMap::iterator> cached_descendants;
96 [ # # ]: 0 : const bool remove{m_to_be_replaced.count(txid) > 0};
97 : 0 : CTxMemPool::setEntries descendants;
98 [ # # ]: 0 : mempool.CalculateDescendants(txiter, descendants);
99 [ # # # # ]: 0 : Assume(descendants.count(txiter) > 0);
100 [ # # ]: 0 : for (const auto& desc_txiter : descendants) {
101 [ # # # # : 0 : const auto txid_desc = desc_txiter->GetTx().GetHash();
# # ]
102 [ # # ]: 0 : const bool remove_desc{m_to_be_replaced.count(txid_desc) > 0};
103 [ # # ]: 0 : auto desc_it{m_entries_by_txid.find(txid_desc)};
104 [ # # ]: 0 : Assume((desc_it == m_entries_by_txid.end()) == remove_desc);
105 [ # # # # ]: 0 : if (remove) Assume(remove_desc);
106 : : // It's possible that remove=false but remove_desc=true.
107 [ # # # # ]: 0 : if (!remove && !remove_desc) {
108 [ # # ]: 0 : cached_descendants.push_back(desc_it);
109 : 0 : }
110 : : }
111 [ # # ]: 0 : if (remove) {
112 [ # # ]: 0 : Assume(cached_descendants.empty());
113 : 0 : } else {
114 [ # # ]: 0 : m_descendant_set_by_txid.emplace(txid, cached_descendants);
115 : : }
116 : 0 : }
117 : :
118 : : // Release the mempool lock; we now have all the information we need for a subset of the entries
119 : : // we care about. We will solely operate on the MiniMinerMempoolEntry map from now on.
120 [ # # ]: 0 : Assume(m_in_block.empty());
121 [ # # ]: 0 : Assume(m_requested_outpoints_by_txid.size() <= outpoints.size());
122 [ # # ]: 0 : SanityCheck();
123 [ # # ]: 0 : }
124 : :
125 : : // Compare by min(ancestor feerate, individual feerate), then iterator
126 : : //
127 : : // Under the ancestor-based mining approach, high-feerate children can pay for parents, but high-feerate
128 : : // parents do not incentive inclusion of their children. Therefore the mining algorithm only considers
129 : : // transactions for inclusion on basis of the minimum of their own feerate or their ancestor feerate.
130 : : struct AncestorFeerateComparator
131 : : {
132 : : template<typename I>
133 : 0 : bool operator()(const I& a, const I& b) const {
134 : 0 : auto min_feerate = [](const MiniMinerMempoolEntry& e) -> CFeeRate {
135 : 0 : const CAmount ancestor_fee{e.GetModFeesWithAncestors()};
136 : 0 : const int64_t ancestor_size{e.GetSizeWithAncestors()};
137 : 0 : const CAmount tx_fee{e.GetModifiedFee()};
138 : 0 : const int64_t tx_size{e.GetTxSize()};
139 : : // Comparing ancestor feerate with individual feerate:
140 : : // ancestor_fee / ancestor_size <= tx_fee / tx_size
141 : : // Avoid division and possible loss of precision by
142 : : // multiplying both sides by the sizes:
143 [ # # ]: 0 : return ancestor_fee * tx_size < tx_fee * ancestor_size ?
144 : 0 : CFeeRate(ancestor_fee, ancestor_size) :
145 : 0 : CFeeRate(tx_fee, tx_size);
146 : : };
147 : 0 : CFeeRate a_feerate{min_feerate(a->second)};
148 : 0 : CFeeRate b_feerate{min_feerate(b->second)};
149 [ # # ]: 0 : if (a_feerate != b_feerate) {
150 : 0 : return a_feerate > b_feerate;
151 : : }
152 : : // Use txid as tiebreaker for stable sorting
153 : 0 : return a->first < b->first;
154 : 0 : }
155 : : };
156 : :
157 : 0 : void MiniMiner::DeleteAncestorPackage(const std::set<MockEntryMap::iterator, IteratorComparator>& ancestors)
158 : : {
159 : 0 : Assume(ancestors.size() >= 1);
160 : : // "Mine" all transactions in this ancestor set.
161 [ # # ]: 0 : for (auto& anc : ancestors) {
162 : 0 : Assume(m_in_block.count(anc->first) == 0);
163 : 0 : m_in_block.insert(anc->first);
164 : 0 : m_total_fees += anc->second.GetModifiedFee();
165 : 0 : m_total_vsize += anc->second.GetTxSize();
166 : 0 : auto it = m_descendant_set_by_txid.find(anc->first);
167 : : // Each entry’s descendant set includes itself
168 : 0 : Assume(it != m_descendant_set_by_txid.end());
169 [ # # ]: 0 : for (auto& descendant : it->second) {
170 : : // If these fail, we must be double-deducting.
171 : 0 : Assume(descendant->second.GetModFeesWithAncestors() >= anc->second.GetModifiedFee());
172 : 0 : Assume(descendant->second.GetSizeWithAncestors() >= anc->second.GetTxSize());
173 : 0 : descendant->second.UpdateAncestorState(-anc->second.GetTxSize(), -anc->second.GetModifiedFee());
174 : : }
175 : : }
176 : : // Delete these entries.
177 [ # # ]: 0 : for (const auto& anc : ancestors) {
178 : 0 : m_descendant_set_by_txid.erase(anc->first);
179 : : // The above loop should have deducted each ancestor's size and fees from each of their
180 : : // respective descendants exactly once.
181 : 0 : Assume(anc->second.GetModFeesWithAncestors() == 0);
182 : 0 : Assume(anc->second.GetSizeWithAncestors() == 0);
183 : 0 : auto vec_it = std::find(m_entries.begin(), m_entries.end(), anc);
184 : 0 : Assume(vec_it != m_entries.end());
185 : 0 : m_entries.erase(vec_it);
186 : 0 : m_entries_by_txid.erase(anc);
187 : : }
188 : 0 : }
189 : :
190 : 0 : void MiniMiner::SanityCheck() const
191 : : {
192 : : // m_entries, m_entries_by_txid, and m_descendant_set_by_txid all same size
193 : 0 : Assume(m_entries.size() == m_entries_by_txid.size());
194 : 0 : Assume(m_entries.size() == m_descendant_set_by_txid.size());
195 : : // Cached ancestor values should be at least as large as the transaction's own fee and size
196 [ # # ]: 0 : Assume(std::all_of(m_entries.begin(), m_entries.end(), [](const auto& entry) {
197 : : return entry->second.GetSizeWithAncestors() >= entry->second.GetTxSize() &&
198 : : entry->second.GetModFeesWithAncestors() >= entry->second.GetModifiedFee();}));
199 : : // None of the entries should be to-be-replaced transactions
200 : 0 : Assume(std::all_of(m_to_be_replaced.begin(), m_to_be_replaced.end(),
201 : : [&](const auto& txid){return m_entries_by_txid.find(txid) == m_entries_by_txid.end();}));
202 : 0 : }
203 : :
204 : 0 : void MiniMiner::BuildMockTemplate(const CFeeRate& target_feerate)
205 : : {
206 [ # # ]: 0 : while (!m_entries_by_txid.empty()) {
207 : : // Sort again, since transaction removal may change some m_entries' ancestor feerates.
208 : 0 : std::sort(m_entries.begin(), m_entries.end(), AncestorFeerateComparator());
209 : :
210 : : // Pick highest ancestor feerate entry.
211 : 0 : auto best_iter = m_entries.begin();
212 : 0 : Assume(best_iter != m_entries.end());
213 : 0 : const auto ancestor_package_size = (*best_iter)->second.GetSizeWithAncestors();
214 : 0 : const auto ancestor_package_fee = (*best_iter)->second.GetModFeesWithAncestors();
215 : : // Stop here. Everything that didn't "make it into the block" has bumpfee.
216 [ # # ]: 0 : if (ancestor_package_fee < target_feerate.GetFee(ancestor_package_size)) {
217 : 0 : break;
218 : : }
219 : :
220 : : // Calculate ancestors on the fly. This lookup should be fairly cheap, and ancestor sets
221 : : // change at every iteration, so this is more efficient than maintaining a cache.
222 : 0 : std::set<MockEntryMap::iterator, IteratorComparator> ancestors;
223 : : {
224 : 0 : std::set<MockEntryMap::iterator, IteratorComparator> to_process;
225 [ # # ]: 0 : to_process.insert(*best_iter);
226 [ # # ]: 0 : while (!to_process.empty()) {
227 : 0 : auto iter = to_process.begin();
228 [ # # ]: 0 : Assume(iter != to_process.end());
229 [ # # ]: 0 : ancestors.insert(*iter);
230 [ # # # # ]: 0 : for (const auto& input : (*iter)->second.GetTx().vin) {
231 [ # # # # ]: 0 : if (auto parent_it{m_entries_by_txid.find(input.prevout.hash)}; parent_it != m_entries_by_txid.end()) {
232 [ # # # # ]: 0 : if (ancestors.count(parent_it) == 0) {
233 [ # # ]: 0 : to_process.insert(parent_it);
234 : 0 : }
235 : 0 : }
236 : : }
237 [ # # ]: 0 : to_process.erase(iter);
238 : : }
239 : 0 : }
240 [ # # ]: 0 : DeleteAncestorPackage(ancestors);
241 [ # # ]: 0 : SanityCheck();
242 : 0 : }
243 [ # # ]: 0 : Assume(m_in_block.empty() || m_total_fees >= target_feerate.GetFee(m_total_vsize));
244 : : // Do not try to continue building the block template with a different feerate.
245 : 0 : m_ready_to_calculate = false;
246 : 0 : }
247 : :
248 : 0 : std::map<COutPoint, CAmount> MiniMiner::CalculateBumpFees(const CFeeRate& target_feerate)
249 : : {
250 [ # # ]: 0 : if (!m_ready_to_calculate) return {};
251 : : // Build a block template until the target feerate is hit.
252 : 0 : BuildMockTemplate(target_feerate);
253 : :
254 : : // Each transaction that "made it into the block" has a bumpfee of 0, i.e. they are part of an
255 : : // ancestor package with at least the target feerate and don't need to be bumped.
256 [ # # ]: 0 : for (const auto& txid : m_in_block) {
257 : : // Not all of the block transactions were necessarily requested.
258 : 0 : auto it = m_requested_outpoints_by_txid.find(txid);
259 [ # # ]: 0 : if (it != m_requested_outpoints_by_txid.end()) {
260 [ # # ]: 0 : for (const auto& outpoint : it->second) {
261 : 0 : m_bump_fees.emplace(outpoint, 0);
262 : : }
263 : 0 : m_requested_outpoints_by_txid.erase(it);
264 : 0 : }
265 : : }
266 : :
267 : : // A transactions and its ancestors will only be picked into a block when
268 : : // both the ancestor set feerate and the individual feerate meet the target
269 : : // feerate.
270 : : //
271 : : // We had to convince ourselves that after running the mini miner and
272 : : // picking all eligible transactions into our MockBlockTemplate, there
273 : : // could still be transactions remaining that have a lower individual
274 : : // feerate than their ancestor feerate. So here is an example:
275 : : //
276 : : // ┌─────────────────┐
277 : : // │ │
278 : : // │ Grandparent │
279 : : // │ 1700 vB │
280 : : // │ 1700 sats │ Target feerate: 10 s/vB
281 : : // │ 1 s/vB │ GP Ancestor Set Feerate (ASFR): 1 s/vB
282 : : // │ │ P1_ASFR: 9.84 s/vB
283 : : // └──────▲───▲──────┘ P2_ASFR: 2.47 s/vB
284 : : // │ │ C_ASFR: 10.27 s/vB
285 : : // ┌───────────────┐ │ │ ┌──────────────┐
286 : : // │ ├────┘ └────┤ │ ⇒ C_FR < TFR < C_ASFR
287 : : // │ Parent 1 │ │ Parent 2 │
288 : : // │ 200 vB │ │ 200 vB │
289 : : // │ 17000 sats │ │ 3000 sats │
290 : : // │ 85 s/vB │ │ 15 s/vB │
291 : : // │ │ │ │
292 : : // └───────────▲───┘ └───▲──────────┘
293 : : // │ │
294 : : // │ ┌───────────┐ │
295 : : // └────┤ ├────┘
296 : : // │ Child │
297 : : // │ 100 vB │
298 : : // │ 900 sats │
299 : : // │ 9 s/vB │
300 : : // │ │
301 : : // └───────────┘
302 : : //
303 : : // We therefore calculate both the bump fee that is necessary to elevate
304 : : // the individual transaction to the target feerate:
305 : : // target_feerate × tx_size - tx_fees
306 : : // and the bump fee that is necessary to bump the entire ancestor set to
307 : : // the target feerate:
308 : : // target_feerate × ancestor_set_size - ancestor_set_fees
309 : : // By picking the maximum from the two, we ensure that a transaction meets
310 : : // both criteria.
311 [ # # ]: 0 : for (const auto& [txid, outpoints] : m_requested_outpoints_by_txid) {
312 : 0 : auto it = m_entries_by_txid.find(txid);
313 : 0 : Assume(it != m_entries_by_txid.end());
314 [ # # ]: 0 : if (it != m_entries_by_txid.end()) {
315 : 0 : Assume(target_feerate.GetFee(it->second.GetSizeWithAncestors()) > std::min(it->second.GetModifiedFee(), it->second.GetModFeesWithAncestors()));
316 : 0 : CAmount bump_fee_with_ancestors = target_feerate.GetFee(it->second.GetSizeWithAncestors()) - it->second.GetModFeesWithAncestors();
317 : 0 : CAmount bump_fee_individual = target_feerate.GetFee(it->second.GetTxSize()) - it->second.GetModifiedFee();
318 : 0 : const CAmount bump_fee{std::max(bump_fee_with_ancestors, bump_fee_individual)};
319 : 0 : Assume(bump_fee >= 0);
320 [ # # ]: 0 : for (const auto& outpoint : outpoints) {
321 : 0 : m_bump_fees.emplace(outpoint, bump_fee);
322 : : }
323 : 0 : }
324 : : }
325 : 0 : return m_bump_fees;
326 : 0 : }
327 : :
328 : 0 : std::optional<CAmount> MiniMiner::CalculateTotalBumpFees(const CFeeRate& target_feerate)
329 : : {
330 [ # # ]: 0 : if (!m_ready_to_calculate) return std::nullopt;
331 : : // Build a block template until the target feerate is hit.
332 : 0 : BuildMockTemplate(target_feerate);
333 : :
334 : : // All remaining ancestors that are not part of m_in_block must be bumped, but no other relatives
335 : 0 : std::set<MockEntryMap::iterator, IteratorComparator> ancestors;
336 : 0 : std::set<MockEntryMap::iterator, IteratorComparator> to_process;
337 [ # # ]: 0 : for (const auto& [txid, outpoints] : m_requested_outpoints_by_txid) {
338 : : // Skip any ancestors that already have a miner score higher than the target feerate
339 : : // (already "made it" into the block)
340 [ # # # # : 0 : if (m_in_block.count(txid)) continue;
# # ]
341 [ # # # # ]: 0 : auto iter = m_entries_by_txid.find(txid);
342 [ # # ]: 0 : if (iter == m_entries_by_txid.end()) continue;
343 [ # # ]: 0 : to_process.insert(iter);
344 [ # # ]: 0 : ancestors.insert(iter);
345 : : }
346 : :
347 : 0 : std::set<uint256> has_been_processed;
348 [ # # ]: 0 : while (!to_process.empty()) {
349 : 0 : auto iter = to_process.begin();
350 [ # # ]: 0 : const CTransaction& tx = (*iter)->second.GetTx();
351 [ # # ]: 0 : for (const auto& input : tx.vin) {
352 [ # # # # ]: 0 : if (auto parent_it{m_entries_by_txid.find(input.prevout.hash)}; parent_it != m_entries_by_txid.end()) {
353 [ # # # # ]: 0 : if (!has_been_processed.count(input.prevout.hash)) {
354 [ # # ]: 0 : to_process.insert(parent_it);
355 : 0 : }
356 [ # # ]: 0 : ancestors.insert(parent_it);
357 : 0 : }
358 : : }
359 [ # # # # ]: 0 : has_been_processed.insert(tx.GetHash());
360 [ # # ]: 0 : to_process.erase(iter);
361 : : }
362 [ # # ]: 0 : const auto ancestor_package_size = std::accumulate(ancestors.cbegin(), ancestors.cend(), int64_t{0},
363 : 0 : [](int64_t sum, const auto it) {return sum + it->second.GetTxSize();});
364 [ # # ]: 0 : const auto ancestor_package_fee = std::accumulate(ancestors.cbegin(), ancestors.cend(), CAmount{0},
365 : 0 : [](CAmount sum, const auto it) {return sum + it->second.GetModifiedFee();});
366 [ # # # # ]: 0 : return target_feerate.GetFee(ancestor_package_size) - ancestor_package_fee;
367 : 0 : }
368 : : } // namespace node
|