/root/bitcoin/src/policy/ephemeral_policy.cpp
Line | Count | Source (jump to first uncovered line) |
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 | 0 | { |
25 | | // We never want to give incentives to mine this transaction alone |
26 | 0 | if ((base_fee != 0 || mod_fee != 0) && !GetDust(tx, dust_relay_rate).empty()) { |
27 | 0 | return state.Invalid(TxValidationResult::TX_NOT_STANDARD, "dust", "tx with dust output must be 0-fee"); |
28 | 0 | } |
29 | | |
30 | 0 | return true; |
31 | 0 | } |
32 | | |
33 | | bool CheckEphemeralSpends(const Package& package, CFeeRate dust_relay_rate, const CTxMemPool& tx_pool, TxValidationState& out_child_state, Txid& out_child_txid) |
34 | 0 | { |
35 | 0 | if (!Assume(std::ranges::all_of(package, [](const auto& tx){return tx != nullptr;}))) { |
36 | | // Bail out of spend checks if caller gave us an invalid package |
37 | 0 | return true; |
38 | 0 | } |
39 | | |
40 | 0 | std::map<Txid, CTransactionRef> map_txid_ref; |
41 | 0 | for (const auto& tx : package) { |
42 | 0 | map_txid_ref[tx->GetHash()] = tx; |
43 | 0 | } |
44 | |
|
45 | 0 | for (const auto& tx : package) { |
46 | 0 | std::unordered_set<Txid, SaltedTxidHasher> processed_parent_set; |
47 | 0 | std::unordered_set<COutPoint, SaltedOutpointHasher> unspent_parent_dust; |
48 | |
|
49 | 0 | for (const auto& tx_input : tx->vin) { |
50 | 0 | const Txid& parent_txid{tx_input.prevout.hash}; |
51 | | // Skip parents we've already checked dust for |
52 | 0 | if (processed_parent_set.contains(parent_txid)) continue; |
53 | | |
54 | | // We look for an in-package or in-mempool dependency |
55 | 0 | CTransactionRef parent_ref = nullptr; |
56 | 0 | if (auto it = map_txid_ref.find(parent_txid); it != map_txid_ref.end()) { |
57 | 0 | parent_ref = it->second; |
58 | 0 | } else { |
59 | 0 | parent_ref = tx_pool.get(parent_txid); |
60 | 0 | } |
61 | | |
62 | | // Check for dust on parents |
63 | 0 | if (parent_ref) { |
64 | 0 | for (uint32_t out_index = 0; out_index < parent_ref->vout.size(); out_index++) { |
65 | 0 | const auto& tx_output = parent_ref->vout[out_index]; |
66 | 0 | if (IsDust(tx_output, dust_relay_rate)) { |
67 | 0 | unspent_parent_dust.insert(COutPoint(parent_txid, out_index)); |
68 | 0 | } |
69 | 0 | } |
70 | 0 | } |
71 | |
|
72 | 0 | processed_parent_set.insert(parent_txid); |
73 | 0 | } |
74 | |
|
75 | 0 | if (unspent_parent_dust.empty()) { |
76 | 0 | continue; |
77 | 0 | } |
78 | | |
79 | | // Now that we have gathered parents' dust, make sure it's spent |
80 | | // by the child |
81 | 0 | for (const auto& tx_input : tx->vin) { |
82 | 0 | unspent_parent_dust.erase(tx_input.prevout); |
83 | 0 | } |
84 | |
|
85 | 0 | if (!unspent_parent_dust.empty()) { |
86 | 0 | out_child_txid = tx->GetHash(); |
87 | 0 | out_child_state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "missing-ephemeral-spends", |
88 | 0 | strprintf("tx %s did not spend parent's ephemeral dust", out_child_txid.ToString())); |
89 | 0 | return false; |
90 | 0 | } |
91 | 0 | } |
92 | | |
93 | 0 | return true; |
94 | 0 | } |