/Users/eugenesiegel/btc/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 | 638k | { | 
| 25 |  |     // We never want to give incentives to mine this transaction alone | 
| 26 | 638k |     if ((base_fee != 0 || mod_fee != 00) && !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 | 638k |     return true; | 
| 31 | 638k | } | 
| 32 |  |  | 
| 33 |  | bool CheckEphemeralSpends(const Package& package, CFeeRate dust_relay_rate, const CTxMemPool& tx_pool, TxValidationState& out_child_state, Wtxid& out_child_wtxid) | 
| 34 | 638k | { | 
| 35 | 638k |     if (!Assume(std::ranges::all_of(package, [](const auto& tx){return tx != nullptr;}))) {| Line | Count | Source |  | 118 | 638k | #define Assume(val) inline_assertion_check<false>(val, __FILE__, __LINE__, __func__, #val) | 
 | 
| 36 |  |         // Bail out of spend checks if caller gave us an invalid package | 
| 37 | 0 |         return true; | 
| 38 | 0 |     } | 
| 39 |  |  | 
| 40 | 638k |     std::map<Txid, CTransactionRef> map_txid_ref; | 
| 41 | 638k |     for (const auto& tx : package) { | 
| 42 | 638k |         map_txid_ref[tx->GetHash()] = tx; | 
| 43 | 638k |     } | 
| 44 |  |  | 
| 45 | 638k |     for (const auto& tx : package) { | 
| 46 | 638k |         std::unordered_set<Txid, SaltedTxidHasher> processed_parent_set; | 
| 47 | 638k |         std::unordered_set<COutPoint, SaltedOutpointHasher> unspent_parent_dust; | 
| 48 |  |  | 
| 49 | 638k |         for (const auto& tx_input : tx->vin) { | 
| 50 | 638k |             const Txid& parent_txid{tx_input.prevout.hash}; | 
| 51 |  |             // Skip parents we've already checked dust for | 
| 52 | 638k |             if (processed_parent_set.contains(parent_txid)) continue0; | 
| 53 |  |  | 
| 54 |  |             // We look for an in-package or in-mempool dependency | 
| 55 | 638k |             CTransactionRef parent_ref = nullptr; | 
| 56 | 638k |             if (auto it = map_txid_ref.find(parent_txid); it != map_txid_ref.end()) { | 
| 57 | 0 |                 parent_ref = it->second; | 
| 58 | 638k |             } else { | 
| 59 | 638k |                 parent_ref = tx_pool.get(parent_txid); | 
| 60 | 638k |             } | 
| 61 |  |  | 
| 62 |  |             // Check for dust on parents | 
| 63 | 638k |             if (parent_ref) { | 
| 64 | 929k |                 for (uint32_t out_index = 0; out_index < parent_ref->vout.size(); out_index++464k) { | 
| 65 | 464k |                     const auto& tx_output = parent_ref->vout[out_index]; | 
| 66 | 464k |                     if (IsDust(tx_output, dust_relay_rate)) { | 
| 67 | 0 |                         unspent_parent_dust.insert(COutPoint(parent_txid, out_index)); | 
| 68 | 0 |                     } | 
| 69 | 464k |                 } | 
| 70 | 464k |             } | 
| 71 |  |  | 
| 72 | 638k |             processed_parent_set.insert(parent_txid); | 
| 73 | 638k |         } | 
| 74 |  |  | 
| 75 | 638k |         if (unspent_parent_dust.empty()) { | 
| 76 | 638k |             continue; | 
| 77 | 638k |         } | 
| 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 |             const Txid& out_child_txid = tx->GetHash(); | 
| 87 | 0 |             out_child_wtxid = tx->GetWitnessHash(); | 
| 88 | 0 |             out_child_state.Invalid(TxValidationResult::TX_MEMPOOL_POLICY, "missing-ephemeral-spends", | 
| 89 | 0 |                                 strprintf("tx %s (wtxid=%s) did not spend parent's ephemeral dust", out_child_txid.ToString(), out_child_wtxid.ToString()));| Line | Count | Source |  | 1172 | 0 | #define strprintf tfm::format | 
 | 
| 90 | 0 |             return false; | 
| 91 | 0 |         } | 
| 92 | 0 |     } | 
| 93 |  |  | 
| 94 | 638k |     return true; | 
| 95 | 638k | } |