private_broadcast.h raw

   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          : 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          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          {
 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              return std::tie(other.num_picked, other.num_confirmed, other.last_picked, other.last_confirmed) <=>
 175                     std::tie(num_picked, num_confirmed, last_picked, last_confirmed);
 176          }
 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          {
 190              return static_cast<size_t>(tx->GetWitnessHash().ToUint256().GetUint64(0));
 191          }
 192      };
 193  
 194      struct CTransactionRefComp {
 195          bool operator()(const CTransactionRef& a, const CTransactionRef& b) const
 196          {
 197              return a->GetWitnessHash() == b->GetWitnessHash(); // If wtxid equals, then txid also equals.
 198          }
 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
 226