LCOV - code coverage report
Current view: top level - src - private_broadcast.h (source / functions) Coverage Total Hit
Test: test_bitcoin_coverage.info Lines: 100.0 % 14 14
Test Date: 2026-08-23 07:26:31 Functions: 100.0 % 2 2
Branches: 50.0 % 16 8

             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 <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                 :           5 : 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                 :         179 :     explicit PrivateBroadcast(size_t max_transactions = MAX_TRANSACTIONS,
      55                 :             :                               size_t max_send_attempts = MAX_SEND_ATTEMPTS)
      56   [ +  -  +  -  :         179 :         : m_max_transactions{max_transactions}, m_max_send_attempts{max_send_attempts} {}
          +  -  +  -  +  
                      - ]
      57                 :             : 
      58                 :          30 :     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                 :          17 :     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                 :          10 :         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                 :           3 :         auto operator<=>(const Priority& other) const
     184                 :             :         {
     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                 :           3 :             return std::tie(other.num_picked, other.num_confirmed, other.last_picked, other.last_confirmed) <=>
     189                 :           3 :                    std::tie(num_picked, num_confirmed, last_picked, last_confirmed);
     190                 :             :         }
     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                 :       20032 :         size_t operator()(const CTransactionRef& tx) const
     203                 :             :         {
     204         [ +  + ]:       20032 :             return static_cast<size_t>(tx->GetWitnessHash().ToUint256().GetUint64(0));
     205                 :             :         }
     206                 :             :     };
     207                 :             : 
     208                 :             :     struct CTransactionRefComp {
     209                 :           9 :         bool operator()(const CTransactionRef& a, const CTransactionRef& b) const
     210                 :             :         {
     211   [ -  +  -  - ]:           9 :             return a->GetWitnessHash() == b->GetWitnessHash(); // If wtxid equals, then txid also equals.
     212                 :             :         }
     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                 :       10008 :     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
        

Generated by: LCOV version 2.0-1