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