/bitcoin/src/policy/ephemeral_policy.cpp
Line | Count | Source |
1 | | // Copyright (c) 2024-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 <consensus/validation.h> |
6 | | #include <policy/ephemeral_policy.h> |
7 | | #include <policy/feerate.h> |
8 | | #include <policy/packages.h> |
9 | | #include <policy/policy.h> |
10 | | #include <primitives/transaction.h> |
11 | | #include <txmempool.h> |
12 | | #include <util/check.h> |
13 | | #include <util/hasher.h> |
14 | | |
15 | | #include <algorithm> |
16 | | #include <cstdint> |
17 | | #include <map> |
18 | | #include <memory> |
19 | | #include <unordered_set> |
20 | | #include <utility> |
21 | | #include <vector> |
22 | | |
23 | | bool PreCheckEphemeralTx(const CTransaction& tx, CFeeRate dust_relay_rate, CAmount base_fee, CAmount mod_fee, TxValidationState& state) |
24 | 63.2k | { |
25 | | // We never want to give incentives to mine this transaction alone |
26 | 63.2k | if ((base_fee != 0 || mod_fee != 0) && !GetDust(tx, dust_relay_rate).empty()) { Branch (26:9): [True: 488, False: 62.7k]
Branch (26:10): [True: 57.1k, False: 6.10k]
Branch (26:27): [True: 0, False: 6.10k]
Branch (26:44): [True: 488, False: 56.6k]
|
27 | 488 | return state.Invalid(TxValidationResult::TX_NOT_STANDARD, "dust", "tx with dust output must be 0-fee"); |
28 | 488 | } |
29 | | |
30 | 62.7k | return true; |
31 | 63.2k | } |
32 | | |
33 | | bool CheckEphemeralSpends(const Package& package, CFeeRate dust_relay_rate, const CTxMemPool& tx_pool, TxValidationState& out_child_state, Wtxid& out_child_wtxid) |
34 | 53.5k | { |
35 | 53.5k | if (!Assume(std::ranges::all_of(package, [](const auto& tx){return tx != nullptr;}))) { Branch (35:9): [True: 0, False: 53.5k]
|
36 | | // Bail out of spend checks if caller gave us an invalid package |
37 | 0 | return true; |
38 | 0 | } |
39 | | |
40 | 53.5k | std::map<Txid, CTransactionRef> map_txid_ref; |
41 | 53.6k | for (const auto& tx : package) { Branch (41:25): [True: 53.6k, False: 53.5k]
|
42 | 53.6k | map_txid_ref[tx->GetHash()] = tx; |
43 | 53.6k | } |
44 | | |
45 | 53.6k | for (const auto& tx : package) { Branch (45:25): [True: 53.6k, False: 53.4k]
|
46 | 53.6k | std::unordered_set<Txid, SaltedTxidHasher> processed_parent_set; |
47 | 53.6k | std::unordered_set<COutPoint, SaltedOutpointHasher> unspent_parent_dust; |
48 | | |
49 | 61.6k | for (const auto& tx_input : tx->vin) { Branch (49:35): [True: 61.6k, False: 53.6k]
|
50 | 61.6k | const Txid& parent_txid{tx_input.prevout.hash}; |
51 | | // Skip parents we've already checked dust for |
52 | 61.6k | if (processed_parent_set.contains(parent_txid)) continue; Branch (52:17): [True: 1.28k, False: 60.3k]
|
53 | | |
54 | | // We look for an in-package or in-mempool dependency |
55 | 60.3k | CTransactionRef parent_ref = nullptr; |
56 | 60.3k | if (auto it = map_txid_ref.find(parent_txid); it != map_txid_ref.end()) { Branch (56:59): [True: 84, False: 60.2k]
|
57 | 84 | parent_ref = it->second; |
58 | 60.2k | } else { |
59 | 60.2k | parent_ref = tx_pool.get(parent_txid); |
60 | 60.2k | } |
61 | | |
62 | | // Check for dust on parents |
63 | 60.3k | if (parent_ref) { Branch (63:17): [True: 36.4k, False: 23.9k]
|
64 | 76.4k | for (uint32_t out_index = 0; out_index < parent_ref->vout.size(); out_index++) { Branch (64:46): [True: 40.0k, False: 36.4k]
|
65 | 40.0k | const auto& tx_output = parent_ref->vout[out_index]; |
66 | 40.0k | if (IsDust(tx_output, dust_relay_rate)) { Branch (66:25): [True: 313, False: 39.6k]
|
67 | 313 | unspent_parent_dust.insert(COutPoint(parent_txid, out_index)); |
68 | 313 | } |
69 | 40.0k | } |
70 | 36.4k | } |
71 | | |
72 | 60.3k | processed_parent_set.insert(parent_txid); |
73 | 60.3k | } |
74 | | |
75 | 53.6k | if (unspent_parent_dust.empty()) { Branch (75:13): [True: 53.4k, False: 246]
|
76 | 53.4k | continue; |
77 | 53.4k | } |
78 | | |
79 | | // Now that we have gathered parents' dust, make sure it's spent |
80 | | // by the child |
81 | 351 | for (const auto& tx_input : tx->vin) { Branch (81:35): [True: 351, False: 246]
|
82 | 351 | unspent_parent_dust.erase(tx_input.prevout); |
83 | 351 | } |
84 | | |
85 | 246 | if (!unspent_parent_dust.empty()) { Branch (85:13): [True: 80, False: 166]
|
86 | 80 | const Txid& out_child_txid = tx->GetHash(); |
87 | 80 | out_child_wtxid = tx->GetWitnessHash(); |
88 | 80 | out_child_state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "missing-ephemeral-spends", |
89 | 80 | strprintf("tx %s (wtxid=%s) did not spend parent's ephemeral dust", out_child_txid.ToString(), out_child_wtxid.ToString())); |
90 | 80 | return false; |
91 | 80 | } |
92 | 246 | } |
93 | | |
94 | 53.4k | return true; |
95 | 53.5k | } |