Coverage Report

Created: 2026-07-23 20:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/tmp/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
    /// @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
1.20k
        : 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
16
        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
8
        {
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
8
            return std::tie(other.num_picked, other.num_confirmed, other.last_picked, other.last_confirmed) <=>
175
8
                   std::tie(num_picked, num_confirmed, last_picked, last_confirmed);
176
8
        }
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
55.9k
        {
190
55.9k
            return static_cast<size_t>(tx->GetWitnessHash().ToUint256().GetUint64(0));
191
55.9k
        }
192
    };
193
194
    struct CTransactionRefComp {
195
        bool operator()(const CTransactionRef& a, const CTransactionRef& b) const
196
12
        {
197
12
            return a->GetWitnessHash() == b->GetWitnessHash(); // If wtxid equals, then txid also equals.
198
12
        }
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