Coverage Report

Created: 2026-07-01 15:07

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