Coverage Report

Created: 2026-07-23 20:35

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