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