/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 | } |