/tmp/bitcoin/src/private_broadcast.h
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 | | #ifndef BITCOIN_PRIVATE_BROADCAST_H |
6 | | #define BITCOIN_PRIVATE_BROADCAST_H |
7 | | |
8 | | #include <net.h> |
9 | | #include <primitives/transaction.h> |
10 | | #include <primitives/transaction_identifier.h> |
11 | | #include <sync.h> |
12 | | #include <util/time.h> |
13 | | |
14 | | #include <optional> |
15 | | #include <tuple> |
16 | | #include <unordered_map> |
17 | | #include <vector> |
18 | | |
19 | | /** |
20 | | * Store a list of transactions to be broadcast privately. Supports the following operations: |
21 | | * - Add a new transaction |
22 | | * - Remove a transaction |
23 | | * - Mark a transaction as resolved, so no more retry send attempts are granted |
24 | | * - Mark a connection finished |
25 | | * - Grant an additional send attempt for an unresolved transaction |
26 | | * - Pick a transaction for sending to one recipient |
27 | | * - Query which transaction has been picked for sending to a given recipient node |
28 | | * - Mark that a given recipient node has confirmed receipt of a transaction |
29 | | * - Query whether any transactions that need sending are currently on the list |
30 | | */ |
31 | | class PrivateBroadcast |
32 | | { |
33 | | public: |
34 | | |
35 | | /// Number of connections to make for initial broadcast. |
36 | | static constexpr size_t INITIAL_CONNECTION_COUNT{3}; |
37 | | |
38 | | /// If a transaction is not sent to any peer for this duration, |
39 | | /// then we consider it stale / for rebroadcasting. |
40 | | static constexpr auto INITIAL_STALE_DURATION{5min}; |
41 | | |
42 | | /// If a transaction is not received back from the network for this duration |
43 | | /// after it is broadcast, then we consider it stale / for rebroadcasting. |
44 | | static constexpr auto STALE_DURATION{1min}; |
45 | | |
46 | | /// Maximum number of transactions tracked simultaneously. |
47 | | /// Additions that would exceed this are rejected (see Add()). |
48 | | static constexpr size_t MAX_TRANSACTIONS{10'000}; |
49 | | |
50 | | /// Maximum number of send attempts for a transaction. Once this limit is |
51 | | /// reached, the transaction remains tracked but is not sent again unless |
52 | | /// explicitly re-added. |
53 | | static constexpr size_t MAX_SEND_ATTEMPTS{1'000}; |
54 | | |
55 | | /// @param[in] max_transactions Cap on the number of simultaneously tracked |
56 | | /// transactions. Defaults to MAX_TRANSACTIONS. |
57 | | /// @param[in] max_send_attempts Cap on the number of send attempts per |
58 | | /// transaction. Defaults to MAX_SEND_ATTEMPTS. |
59 | | explicit PrivateBroadcast(size_t max_transactions = MAX_TRANSACTIONS, |
60 | | size_t max_send_attempts = MAX_SEND_ATTEMPTS) |
61 | 1.31k | : m_max_transactions{max_transactions}, m_max_send_attempts{max_send_attempts} {} |
62 | | |
63 | | struct PeerSendInfo { |
64 | | CService address; |
65 | | NodeClock::time_point sent; |
66 | | std::optional<NodeClock::time_point> received; |
67 | | }; |
68 | | |
69 | | struct TxBroadcastInfo { |
70 | | CTransactionRef tx; |
71 | | NodeClock::time_point time_added; |
72 | | /// Number of additional send attempts allowed for this transaction (0 if exhausted). |
73 | | size_t attempts_remaining; |
74 | | std::vector<PeerSendInfo> peers; |
75 | | }; |
76 | | |
77 | | /// Outcome of Add(). |
78 | | enum class AddResult { |
79 | | //! The transaction was newly added or reset after exhausting its send attempts. |
80 | | Added, |
81 | | //! The transaction was already present with send attempts remaining; no change. |
82 | | AlreadyPresent, |
83 | | //! Rejected: the queue is already at MAX_TRANSACTIONS. |
84 | | QueueFull, |
85 | | }; |
86 | | |
87 | | /** |
88 | | * Add a transaction to the storage, or reset an exhausted transaction so it |
89 | | * can be broadcast again. |
90 | | * @param[in] tx The transaction to add. |
91 | | * @return Whether the transaction was newly added or reset, was already |
92 | | * present with send attempts remaining, or was rejected because the queue is |
93 | | * full (see AddResult). |
94 | | */ |
95 | | [[nodiscard]] AddResult Add(const CTransactionRef& tx) |
96 | | EXCLUSIVE_LOCKS_REQUIRED(!m_mutex); |
97 | | |
98 | | /** |
99 | | * Forget a transaction. |
100 | | * @param[in] tx Transaction to forget. |
101 | | * @retval !nullopt The number of planned send attempts not yet picked |
102 | | * (if the transaction existed and was removed). |
103 | | * @retval nullopt The transaction was not in the storage. |
104 | | */ |
105 | | std::optional<size_t> Remove(const CTransactionRef& tx) |
106 | | EXCLUSIVE_LOCKS_REQUIRED(!m_mutex); |
107 | | |
108 | | /** |
109 | | * Mark a transaction as resolved because it was received back from the network |
110 | | * or is no longer acceptable to the mempool. |
111 | | * @param[in] tx Transaction to resolve. |
112 | | * @return Whether the transaction was found in the storage. |
113 | | */ |
114 | | bool MarkResolved(const CTransactionRef& tx) |
115 | | EXCLUSIVE_LOCKS_REQUIRED(!m_mutex); |
116 | | |
117 | | /** |
118 | | * Mark a connection finished. |
119 | | * @param[in] nodeid Node whose connection has ended. |
120 | | */ |
121 | | void NodeDisconnected(NodeId nodeid) |
122 | | EXCLUSIVE_LOCKS_REQUIRED(!m_mutex); |
123 | | |
124 | | /** |
125 | | * Grant one additional send attempt if the transaction is unresolved, all |
126 | | * planned sends have been picked, and the attempt limit has not been reached. |
127 | | * The caller schedules the connection separately. |
128 | | * @param[in] tx Transaction to retry. |
129 | | * @return Whether an additional send attempt was granted. |
130 | | */ |
131 | | bool TryGrantRetry(const CTransactionRef& tx) |
132 | | EXCLUSIVE_LOCKS_REQUIRED(!m_mutex); |
133 | | |
134 | | /** |
135 | | * Pick the transaction with the fewest send attempts, and confirmations, |
136 | | * and oldest send/confirm times. |
137 | | * @param[in] will_send_to_nodeid Will remember that the returned transaction |
138 | | * was picked for sending to this node. Calling this method more than once with |
139 | | * the same `will_send_to_nodeid` is not allowed because sending more than one |
140 | | * transaction to one node would be a privacy leak. |
141 | | * @param[in] will_send_to_address Address of the peer to which this transaction |
142 | | * will be sent. |
143 | | * @return Most urgent transaction or nullopt if there are no transactions |
144 | | * with send attempts remaining. |
145 | | */ |
146 | | std::optional<CTransactionRef> PickTxForSend(const NodeId& will_send_to_nodeid, const CService& will_send_to_address) |
147 | | EXCLUSIVE_LOCKS_REQUIRED(!m_mutex); |
148 | | |
149 | | /** |
150 | | * Get the transaction that was picked for sending to a given node by PickTxForSend(). |
151 | | * @param[in] nodeid Node to which a transaction is being (or was) sent. |
152 | | * @return Transaction or nullopt if the nodeid is unknown. |
153 | | */ |
154 | | std::optional<CTransactionRef> GetTxForNode(const NodeId& nodeid) |
155 | | EXCLUSIVE_LOCKS_REQUIRED(!m_mutex); |
156 | | |
157 | | /** |
158 | | * Mark that the node has confirmed reception of the transaction we sent it by |
159 | | * responding with `PONG` to our `PING` message. |
160 | | * @param[in] nodeid Node that we sent a transaction to. |
161 | | */ |
162 | | void NodeConfirmedReception(const NodeId& nodeid) |
163 | | EXCLUSIVE_LOCKS_REQUIRED(!m_mutex); |
164 | | |
165 | | /** |
166 | | * Check if there are transactions with send attempts remaining. |
167 | | */ |
168 | | bool HavePendingTransactions() |
169 | | EXCLUSIVE_LOCKS_REQUIRED(!m_mutex); |
170 | | |
171 | | /** |
172 | | * Get the transactions that have not been broadcast recently and have send |
173 | | * attempts remaining. |
174 | | */ |
175 | | std::vector<CTransactionRef> GetStale() const |
176 | | EXCLUSIVE_LOCKS_REQUIRED(!m_mutex); |
177 | | |
178 | | /** |
179 | | * Get stats about all transactions currently being privately broadcast. |
180 | | */ |
181 | | std::vector<TxBroadcastInfo> GetBroadcastInfo() const |
182 | | EXCLUSIVE_LOCKS_REQUIRED(!m_mutex); |
183 | | |
184 | | private: |
185 | | /// Status of a transaction sent to a given node. |
186 | | struct SendStatus { |
187 | | /// Node to which the transaction will be sent (or was sent). |
188 | | const NodeId nodeid; |
189 | | /// Address of the node. |
190 | | const CService address; |
191 | | /// When was the transaction picked for sending to the node. |
192 | | const NodeClock::time_point picked; |
193 | | /// When was the transaction reception confirmed by the node (by PONG). |
194 | | std::optional<NodeClock::time_point> confirmed; |
195 | | /// Whether the node has disconnected. |
196 | | bool disconnected{false}; |
197 | | |
198 | 32 | SendStatus(const NodeId& nodeid, const CService& address, const NodeClock::time_point& picked) : nodeid{nodeid}, address{address}, picked{picked} {} |
199 | | }; |
200 | | |
201 | | /// Cumulative stats from all the send attempts for a transaction. Used to prioritize transactions. |
202 | | struct Priority { |
203 | | size_t num_picked{0}; ///< Number of times the transaction was picked for sending. |
204 | | NodeClock::time_point last_picked{}; ///< The most recent time when the transaction was picked for sending. |
205 | | size_t num_confirmed{0}; ///< Number of nodes that have confirmed reception of a transaction (by PONG). |
206 | | NodeClock::time_point last_confirmed{}; ///< The most recent time when the transaction was confirmed. |
207 | | |
208 | | auto operator<=>(const Priority& other) const |
209 | 3 | { |
210 | | // Invert `other` and `this` in the comparison because smaller num_picked, num_confirmed or |
211 | | // earlier times mean greater priority. In other words, if this.num_picked < other.num_picked |
212 | | // then this > other. |
213 | 3 | return std::tie(other.num_picked, other.num_confirmed, other.last_picked, other.last_confirmed) <=> |
214 | 3 | std::tie(num_picked, num_confirmed, last_picked, last_confirmed); |
215 | 3 | } |
216 | | }; |
217 | | |
218 | | /// A pair of a transaction and a sent status for a given node. Convenience return type of GetSendStatusByNode(). |
219 | | struct TxAndSendStatusForNode { |
220 | | const CTransactionRef& tx; |
221 | | SendStatus& send_status; |
222 | | }; |
223 | | |
224 | | // No need for salted hasher because we are going to store just a bunch of locally originating transactions. |
225 | | |
226 | | struct CTransactionRefHash { |
227 | | size_t operator()(const CTransactionRef& tx) const |
228 | 57.1k | { |
229 | 57.1k | return static_cast<size_t>(tx->GetWitnessHash().ToUint256().GetUint64(0)); |
230 | 57.1k | } |
231 | | }; |
232 | | |
233 | | struct CTransactionRefComp { |
234 | | bool operator()(const CTransactionRef& a, const CTransactionRef& b) const |
235 | 25 | { |
236 | 25 | return a->GetWitnessHash() == b->GetWitnessHash(); // If wtxid equals, then txid also equals. |
237 | 25 | } |
238 | | }; |
239 | | |
240 | | /** |
241 | | * Derive the sending priority of a transaction. |
242 | | * @param[in] sent_to List of nodes that the transaction has been sent to. |
243 | | */ |
244 | | static Priority DerivePriority(const std::vector<SendStatus>& sent_to); |
245 | | |
246 | | /** |
247 | | * Find which transaction we sent to a given node (marked by PickTxForSend()). |
248 | | * @return That transaction together with the send status or nullopt if we did not |
249 | | * send any transaction to the given node. |
250 | | */ |
251 | | std::optional<TxAndSendStatusForNode> GetSendStatusByNode(const NodeId& nodeid) |
252 | | EXCLUSIVE_LOCKS_REQUIRED(m_mutex); |
253 | | struct TxSendStatus { |
254 | | NodeClock::time_point time_added{NodeClock::now()}; |
255 | | std::vector<SendStatus> send_statuses; |
256 | | /// Total number of sends granted, including initial count. |
257 | | size_t planned_sends{INITIAL_CONNECTION_COUNT}; |
258 | | /// Whether the transaction no longer needs to be retried. |
259 | | bool resolved{false}; |
260 | | }; |
261 | | bool IsPending(const TxSendStatus& status) const; |
262 | | /// Cap on the number of simultaneously tracked transactions (see Add()). |
263 | | const size_t m_max_transactions; |
264 | | /// Cap on the number of send attempts per transaction (see PickTxForSend()). |
265 | | const size_t m_max_send_attempts; |
266 | | mutable Mutex m_mutex; |
267 | | std::unordered_map<CTransactionRef, TxSendStatus, CTransactionRefHash, CTransactionRefComp> |
268 | | m_transactions GUARDED_BY(m_mutex); |
269 | | }; |
270 | | |
271 | | #endif // BITCOIN_PRIVATE_BROADCAST_H |