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