Coverage Report

Created: 2025-06-10 13:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}