Coverage Report

Created: 2026-07-14 18:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/bitcoin/src/private_broadcast.cpp
Line
Count
Source
1
// Copyright (c) 2023-present The Bitcoin Core developers
2
// Distributed under the MIT software license, see the accompanying
3
// file COPYING or https://opensource.org/license/mit/.
4
5
#include <private_broadcast.h>
6
#include <util/check.h>
7
8
#include <algorithm>
9
10
11
bool PrivateBroadcast::Add(const CTransactionRef& tx)
12
    EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
13
0
{
14
0
    LOCK(m_mutex);
15
0
    const bool inserted{m_transactions.try_emplace(tx).second};
16
0
    return inserted;
17
0
}
18
19
std::optional<size_t> PrivateBroadcast::Remove(const CTransactionRef& tx)
20
    EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
21
1.87M
{
22
1.87M
    LOCK(m_mutex);
23
1.87M
    const auto handle{m_transactions.extract(tx)};
24
1.87M
    if (handle) {
  Branch (24:9): [True: 0, False: 1.87M]
25
0
        const auto p{DerivePriority(handle.mapped().send_statuses)};
26
0
        return p.num_confirmed;
27
0
    }
28
1.87M
    return std::nullopt;
29
1.87M
}
30
31
std::optional<CTransactionRef> PrivateBroadcast::PickTxForSend(const NodeId& will_send_to_nodeid, const CService& will_send_to_address)
32
    EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
33
0
{
34
0
    LOCK(m_mutex);
35
36
0
    if (GetSendStatusByNode(will_send_to_nodeid).has_value()) { // nodeid reuse, shouldn't send >1 tx to a given node
  Branch (36:9): [True: 0, False: 0]
37
0
        Assume(false);
38
0
        return std::nullopt;
39
0
    }
40
41
0
    const auto it{std::ranges::max_element(
42
0
            m_transactions,
43
0
            [](const auto& a, const auto& b) { return a < b; },
44
0
            [](const auto& el) { return DerivePriority(el.second.send_statuses); })};
45
46
0
    if (it != m_transactions.end()) {
  Branch (46:9): [True: 0, False: 0]
47
0
        auto& [tx, state]{*it};
48
0
        state.send_statuses.emplace_back(will_send_to_nodeid, will_send_to_address, NodeClock::now());
49
0
        return tx;
50
0
    }
51
52
0
    return std::nullopt;
53
0
}
54
55
std::optional<CTransactionRef> PrivateBroadcast::GetTxForNode(const NodeId& nodeid)
56
    EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
57
0
{
58
0
    LOCK(m_mutex);
59
0
    const auto tx_and_status{GetSendStatusByNode(nodeid)};
60
0
    if (tx_and_status.has_value()) {
  Branch (60:9): [True: 0, False: 0]
61
0
        return tx_and_status.value().tx;
62
0
    }
63
0
    return std::nullopt;
64
0
}
65
66
void PrivateBroadcast::NodeConfirmedReception(const NodeId& nodeid)
67
    EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
68
0
{
69
0
    LOCK(m_mutex);
70
0
    const auto tx_and_status{GetSendStatusByNode(nodeid)};
71
0
    if (tx_and_status.has_value()) {
  Branch (71:9): [True: 0, False: 0]
72
0
        tx_and_status.value().send_status.confirmed = NodeClock::now();
73
0
    }
74
0
}
75
76
bool PrivateBroadcast::DidNodeConfirmReception(const NodeId& nodeid)
77
    EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
78
0
{
79
0
    LOCK(m_mutex);
80
0
    const auto tx_and_status{GetSendStatusByNode(nodeid)};
81
0
    if (tx_and_status.has_value()) {
  Branch (81:9): [True: 0, False: 0]
82
0
        return tx_and_status.value().send_status.confirmed.has_value();
83
0
    }
84
0
    return false;
85
0
}
86
87
bool PrivateBroadcast::HavePendingTransactions()
88
    EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
89
0
{
90
0
    LOCK(m_mutex);
91
0
    return !m_transactions.empty();
92
0
}
93
94
std::vector<CTransactionRef> PrivateBroadcast::GetStale() const
95
    EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
96
0
{
97
0
    LOCK(m_mutex);
98
0
    const auto now{NodeClock::now()};
99
0
    std::vector<CTransactionRef> stale;
100
0
    for (const auto& [tx, state] : m_transactions) {
  Branch (100:34): [True: 0, False: 0]
101
0
        const Priority p{DerivePriority(state.send_statuses)};
102
0
        if (p.num_confirmed == 0) {
  Branch (102:13): [True: 0, False: 0]
103
0
            if (state.time_added < now - INITIAL_STALE_DURATION) stale.push_back(tx);
  Branch (103:17): [True: 0, False: 0]
104
0
        } else {
105
0
            if (p.last_confirmed < now - STALE_DURATION) stale.push_back(tx);
  Branch (105:17): [True: 0, False: 0]
106
0
        }
107
0
    }
108
0
    return stale;
109
0
}
110
111
std::vector<PrivateBroadcast::TxBroadcastInfo> PrivateBroadcast::GetBroadcastInfo() const
112
    EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
113
0
{
114
0
    LOCK(m_mutex);
115
0
    std::vector<TxBroadcastInfo> entries;
116
0
    entries.reserve(m_transactions.size());
117
118
0
    for (const auto& [tx, state] : m_transactions) {
  Branch (118:34): [True: 0, False: 0]
119
0
        std::vector<PeerSendInfo> peers;
120
0
        peers.reserve(state.send_statuses.size());
121
0
        for (const auto& status : state.send_statuses) {
  Branch (121:33): [True: 0, False: 0]
122
0
            peers.emplace_back(PeerSendInfo{.address = status.address, .sent = status.picked, .received = status.confirmed});
123
0
        }
124
0
        entries.emplace_back(TxBroadcastInfo{.tx = tx, .time_added = state.time_added, .peers = std::move(peers)});
125
0
    }
126
127
0
    return entries;
128
0
}
129
130
PrivateBroadcast::Priority PrivateBroadcast::DerivePriority(const std::vector<SendStatus>& sent_to)
131
0
{
132
0
    Priority p;
133
0
    p.num_picked = sent_to.size();
134
0
    for (const auto& send_status : sent_to) {
  Branch (134:34): [True: 0, False: 0]
135
0
        p.last_picked = std::max(p.last_picked, send_status.picked);
136
0
        if (send_status.confirmed.has_value()) {
  Branch (136:13): [True: 0, False: 0]
137
0
            ++p.num_confirmed;
138
0
            p.last_confirmed = std::max(p.last_confirmed, send_status.confirmed.value());
139
0
        }
140
0
    }
141
0
    return p;
142
0
}
143
144
std::optional<PrivateBroadcast::TxAndSendStatusForNode> PrivateBroadcast::GetSendStatusByNode(const NodeId& nodeid)
145
    EXCLUSIVE_LOCKS_REQUIRED(m_mutex)
146
0
{
147
0
    AssertLockHeld(m_mutex);
148
0
    for (auto& [tx, state] : m_transactions) {
  Branch (148:28): [True: 0, False: 0]
149
0
        for (auto& send_status : state.send_statuses) {
  Branch (149:32): [True: 0, False: 0]
150
0
            if (send_status.nodeid == nodeid) {
  Branch (150:17): [True: 0, False: 0]
151
0
                return TxAndSendStatusForNode{.tx = tx, .send_status = send_status};
152
0
            }
153
0
        }
154
0
    }
155
0
    return std::nullopt;
156
0
}