/root/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 <threadsafety.h> |
13 | | #include <util/time.h> |
14 | | |
15 | | #include <optional> |
16 | | #include <tuple> |
17 | | #include <unordered_map> |
18 | | #include <vector> |
19 | | |
20 | | /** |
21 | | * Store a list of transactions to be broadcast privately. Supports the following operations: |
22 | | * - Add a new transaction |
23 | | * - Remove a transaction |
24 | | * - Pick a transaction for sending to one recipient |
25 | | * - Query which transaction has been picked for sending to a given recipient node |
26 | | * - Mark that a given recipient node has confirmed receipt of a transaction |
27 | | * - Query whether a given recipient node has confirmed reception |
28 | | * - Query whether any transactions that need sending are currently on the list |
29 | | */ |
30 | | class PrivateBroadcast |
31 | | { |
32 | | public: |
33 | | struct PeerSendInfo { |
34 | | CService address; |
35 | | NodeClock::time_point sent; |
36 | | std::optional<NodeClock::time_point> received; |
37 | | }; |
38 | | |
39 | | struct TxBroadcastInfo { |
40 | | CTransactionRef tx; |
41 | | std::vector<PeerSendInfo> peers; |
42 | | }; |
43 | | |
44 | | /** |
45 | | * Add a transaction to the storage. |
46 | | * @param[in] tx The transaction to add. |
47 | | * @retval true The transaction was added. |
48 | | * @retval false The transaction was already present. |
49 | | */ |
50 | | bool Add(const CTransactionRef& tx) |
51 | | EXCLUSIVE_LOCKS_REQUIRED(!m_mutex); |
52 | | |
53 | | /** |
54 | | * Forget a transaction. |
55 | | * @param[in] tx Transaction to forget. |
56 | | * @retval !nullopt The number of times the transaction was sent and confirmed |
57 | | * by the recipient (if the transaction existed and was removed). |
58 | | * @retval nullopt The transaction was not in the storage. |
59 | | */ |
60 | | std::optional<size_t> Remove(const CTransactionRef& tx) |
61 | | EXCLUSIVE_LOCKS_REQUIRED(!m_mutex); |
62 | | |
63 | | /** |
64 | | * Pick the transaction with the fewest send attempts, and confirmations, |
65 | | * and oldest send/confirm times. |
66 | | * @param[in] will_send_to_nodeid Will remember that the returned transaction |
67 | | * was picked for sending to this node. |
68 | | * @param[in] will_send_to_address Address of the peer to which this transaction |
69 | | * will be sent. |
70 | | * @return Most urgent transaction or nullopt if there are no transactions. |
71 | | */ |
72 | | std::optional<CTransactionRef> PickTxForSend(const NodeId& will_send_to_nodeid, const CService& will_send_to_address) |
73 | | EXCLUSIVE_LOCKS_REQUIRED(!m_mutex); |
74 | | |
75 | | /** |
76 | | * Get the transaction that was picked for sending to a given node by PickTxForSend(). |
77 | | * @param[in] nodeid Node to which a transaction is being (or was) sent. |
78 | | * @return Transaction or nullopt if the nodeid is unknown. |
79 | | */ |
80 | | std::optional<CTransactionRef> GetTxForNode(const NodeId& nodeid) |
81 | | EXCLUSIVE_LOCKS_REQUIRED(!m_mutex); |
82 | | |
83 | | /** |
84 | | * Mark that the node has confirmed reception of the transaction we sent it by |
85 | | * responding with `PONG` to our `PING` message. |
86 | | * @param[in] nodeid Node that we sent a transaction to. |
87 | | */ |
88 | | void NodeConfirmedReception(const NodeId& nodeid) |
89 | | EXCLUSIVE_LOCKS_REQUIRED(!m_mutex); |
90 | | |
91 | | /** |
92 | | * Check if the node has confirmed reception of the transaction. |
93 | | * @retval true Node has confirmed, `NodeConfirmedReception()` has been called. |
94 | | * @retval false Node has not confirmed, `NodeConfirmedReception()` has not been called. |
95 | | */ |
96 | | bool DidNodeConfirmReception(const NodeId& nodeid) |
97 | | EXCLUSIVE_LOCKS_REQUIRED(!m_mutex); |
98 | | |
99 | | /** |
100 | | * Check if there are transactions that need to be broadcast. |
101 | | */ |
102 | | bool HavePendingTransactions() |
103 | | EXCLUSIVE_LOCKS_REQUIRED(!m_mutex); |
104 | | |
105 | | /** |
106 | | * Get the transactions that have not been broadcast recently. |
107 | | */ |
108 | | std::vector<CTransactionRef> GetStale() const |
109 | | EXCLUSIVE_LOCKS_REQUIRED(!m_mutex); |
110 | | |
111 | | /** |
112 | | * Get stats about all transactions currently being privately broadcast. |
113 | | */ |
114 | | std::vector<TxBroadcastInfo> GetBroadcastInfo() const |
115 | | EXCLUSIVE_LOCKS_REQUIRED(!m_mutex); |
116 | | |
117 | | private: |
118 | | /// Status of a transaction sent to a given node. |
119 | | struct SendStatus { |
120 | | const NodeId nodeid; /// Node to which the transaction will be sent (or was sent). |
121 | | const CService address; /// Address of the node. |
122 | | const NodeClock::time_point picked; ///< When was the transaction picked for sending to the node. |
123 | | std::optional<NodeClock::time_point> confirmed; ///< When was the transaction reception confirmed by the node (by PONG). |
124 | | |
125 | 0 | SendStatus(const NodeId& nodeid, const CService& address, const NodeClock::time_point& picked) : nodeid{nodeid}, address{address}, picked{picked} {} |
126 | | }; |
127 | | |
128 | | /// Cumulative stats from all the send attempts for a transaction. Used to prioritize transactions. |
129 | | struct Priority { |
130 | | size_t num_picked{0}; ///< Number of times the transaction was picked for sending. |
131 | | NodeClock::time_point last_picked{}; ///< The most recent time when the transaction was picked for sending. |
132 | | size_t num_confirmed{0}; ///< Number of nodes that have confirmed reception of a transaction (by PONG). |
133 | | NodeClock::time_point last_confirmed{}; ///< The most recent time when the transaction was confirmed. |
134 | | |
135 | | auto operator<=>(const Priority& other) const |
136 | 0 | { |
137 | | // Invert `other` and `this` in the comparison because smaller num_picked, num_confirmed or |
138 | | // earlier times mean greater priority. In other words, if this.num_picked < other.num_picked |
139 | | // then this > other. |
140 | 0 | return std::tie(other.num_picked, other.num_confirmed, other.last_picked, other.last_confirmed) <=> |
141 | 0 | std::tie(num_picked, num_confirmed, last_picked, last_confirmed); |
142 | 0 | } |
143 | | }; |
144 | | |
145 | | /// A pair of a transaction and a sent status for a given node. Convenience return type of GetSendStatusByNode(). |
146 | | struct TxAndSendStatusForNode { |
147 | | const CTransactionRef& tx; |
148 | | SendStatus& send_status; |
149 | | }; |
150 | | |
151 | | // No need for salted hasher because we are going to store just a bunch of locally originating transactions. |
152 | | |
153 | | struct CTransactionRefHash { |
154 | | size_t operator()(const CTransactionRef& tx) const |
155 | 0 | { |
156 | 0 | return static_cast<size_t>(tx->GetWitnessHash().ToUint256().GetUint64(0)); |
157 | 0 | } |
158 | | }; |
159 | | |
160 | | struct CTransactionRefComp { |
161 | | bool operator()(const CTransactionRef& a, const CTransactionRef& b) const |
162 | 0 | { |
163 | 0 | return a->GetWitnessHash() == b->GetWitnessHash(); // If wtxid equals, then txid also equals. |
164 | 0 | } |
165 | | }; |
166 | | |
167 | | /** |
168 | | * Derive the sending priority of a transaction. |
169 | | * @param[in] sent_to List of nodes that the transaction has been sent to. |
170 | | */ |
171 | | static Priority DerivePriority(const std::vector<SendStatus>& sent_to); |
172 | | |
173 | | /** |
174 | | * Find which transaction we sent to a given node (marked by PickTxForSend()). |
175 | | * @return That transaction together with the send status or nullopt if we did not |
176 | | * send any transaction to the given node. |
177 | | */ |
178 | | std::optional<TxAndSendStatusForNode> GetSendStatusByNode(const NodeId& nodeid) |
179 | | EXCLUSIVE_LOCKS_REQUIRED(m_mutex); |
180 | | |
181 | | mutable Mutex m_mutex; |
182 | | std::unordered_map<CTransactionRef, std::vector<SendStatus>, CTransactionRefHash, CTransactionRefComp> |
183 | | m_transactions GUARDED_BY(m_mutex); |
184 | | }; |
185 | | |
186 | | #endif // BITCOIN_PRIVATE_BROADCAST_H |