Coverage Report

Created: 2026-08-25 19:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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 <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
    /// 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
    explicit PrivateBroadcast(size_t max_transactions = MAX_TRANSACTIONS,
55
                              size_t max_send_attempts = MAX_SEND_ATTEMPTS)
56
20.7k
        : m_max_transactions{max_transactions}, m_max_send_attempts{max_send_attempts} {}
57
58
    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
    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
12.5k
        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
        auto operator<=>(const Priority& other) const
184
26.1k
        {
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
26.1k
            return std::tie(other.num_picked, other.num_confirmed, other.last_picked, other.last_confirmed) <=>
189
26.1k
                   std::tie(num_picked, num_confirmed, last_picked, last_confirmed);
190
26.1k
        }
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
        size_t operator()(const CTransactionRef& tx) const
203
97.7k
        {
204
97.7k
            return static_cast<size_t>(tx->GetWitnessHash().ToUint256().GetUint64(0));
205
97.7k
        }
206
    };
207
208
    struct CTransactionRefComp {
209
        bool operator()(const CTransactionRef& a, const CTransactionRef& b) const
210
6.78k
        {
211
6.78k
            return a->GetWitnessHash() == b->GetWitnessHash(); // If wtxid equals, then txid also equals.
212
6.78k
        }
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
    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