Inside Bitcoin
Opening the selected file…
Inside Bitcoin
Opening the selected file…
The code behind the network, with room to understand it.
Release published 2026-10-28 · Snapshot fetched 2026-11-01. Latest stable release at fetch time; development branches and Git history are excluded. Only the selected code window and visible folders load here.
1// Copyright (c) 2023-present The Bitcoin Core developers2// Distributed under the MIT software license, see the accompanying3// file COPYING or https://opensource.org/license/mit/.5#ifndef BITCOIN_PRIVATE_BROADCAST_H6#define BITCOIN_PRIVATE_BROADCAST_H8#include <net.h>9#include <primitives/transaction.h>10#include <primitives/transaction_identifier.h>11#include <sync.h>12#include <util/time.h>14#include <optional>15#include <tuple>16#include <unordered_map>17#include <vector>19/**20* Store a list of transactions to be broadcast privately. Supports the following operations:21* - Add a new transaction22* - Remove a transaction23* - Mark a transaction as resolved, so no more retry send attempts are granted24* - Mark a connection finished25* - Grant an additional send attempt for an unresolved transaction26* - Pick a transaction for sending to one recipient27* - Query which transaction has been picked for sending to a given recipient node28* - Mark that a given recipient node has confirmed receipt of a transaction29* - Query whether any transactions that need sending are currently on the list30*/31class PrivateBroadcast32{33public:35/// Number of connections to make for initial broadcast.36static constexpr size_t INITIAL_CONNECTION_COUNT{3};38/// If a transaction is not sent to any peer for this duration,39/// then we consider it stale / for rebroadcasting.40static constexpr auto INITIAL_STALE_DURATION{5min};42/// If a transaction is not received back from the network for this duration43/// after it is broadcast, then we consider it stale / for rebroadcasting.44static constexpr auto STALE_DURATION{1min};46/// Maximum number of transactions tracked simultaneously.47/// Additions that would exceed this are rejected (see Add()).48static constexpr size_t MAX_TRANSACTIONS{10'000};50/// Maximum number of send attempts for a transaction. Once this limit is51/// reached, the transaction remains tracked but is not sent again unless52/// explicitly re-added.53static constexpr size_t MAX_SEND_ATTEMPTS{1'000};55/// @param[in] max_transactions Cap on the number of simultaneously tracked56/// transactions. Defaults to MAX_TRANSACTIONS.57/// @param[in] max_send_attempts Cap on the number of send attempts per58/// transaction. Defaults to MAX_SEND_ATTEMPTS.59explicit PrivateBroadcast(size_t max_transactions = MAX_TRANSACTIONS,60size_t max_send_attempts = MAX_SEND_ATTEMPTS)61: m_max_transactions{max_transactions}, m_max_send_attempts{max_send_attempts} {}63struct PeerSendInfo {64CService address;65NodeClock::time_point sent;66std::optional<NodeClock::time_point> received;67};69struct TxBroadcastInfo {70CTransactionRef tx;71NodeClock::time_point time_added;72/// Number of additional send attempts allowed for this transaction (0 if exhausted).73size_t attempts_remaining;74std::vector<PeerSendInfo> peers;75};77/// Outcome of Add().78enum class AddResult {79//! The transaction was newly added or reset after exhausting its send attempts.80Added,81//! The transaction was already present with send attempts remaining; no change.82AlreadyPresent,83//! Rejected: the queue is already at MAX_TRANSACTIONS.84QueueFull,85};87/**88* Add a transaction to the storage, or reset an exhausted transaction so it89* can be broadcast again.90* @param[in] tx The transaction to add.91* @return Whether the transaction was newly added or reset, was already92* present with send attempts remaining, or was rejected because the queue is93* full (see AddResult).94*/95[[nodiscard]] AddResult Add(const CTransactionRef& tx)96EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);98/**99* Forget a transaction.100* @param[in] tx Transaction to forget.101* @retval !nullopt The number of planned send attempts not yet picked102* (if the transaction existed and was removed).103* @retval nullopt The transaction was not in the storage.104*/105std::optional<size_t> Remove(const CTransactionRef& tx)106EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);108/**109* Mark a transaction as resolved because it was received back from the network110* or is no longer acceptable to the mempool.111* @param[in] tx Transaction to resolve.112* @return Whether the transaction was found in the storage.113*/114bool MarkResolved(const CTransactionRef& tx)115EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);117/**118* Mark a connection finished.119* @param[in] nodeid Node whose connection has ended.120*/121void NodeDisconnected(NodeId nodeid)122EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);124/**125* Grant one additional send attempt if the transaction is unresolved, all126* planned sends have been picked, and the attempt limit has not been reached.127* The caller schedules the connection separately.128* @param[in] tx Transaction to retry.129* @return Whether an additional send attempt was granted.130*/131bool TryGrantRetry(const CTransactionRef& tx)132EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);134/**135* Pick the transaction with the fewest send attempts, and confirmations,136* and oldest send/confirm times.137* @param[in] will_send_to_nodeid Will remember that the returned transaction138* was picked for sending to this node. Calling this method more than once with139* the same `will_send_to_nodeid` is not allowed because sending more than one140* transaction to one node would be a privacy leak.141* @param[in] will_send_to_address Address of the peer to which this transaction142* will be sent.143* @return Most urgent transaction or nullopt if there are no transactions144* with send attempts remaining.145*/146std::optional<CTransactionRef> PickTxForSend(const NodeId& will_send_to_nodeid, const CService& will_send_to_address)147EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);149/**150* Get the transaction that was picked for sending to a given node by PickTxForSend().151* @param[in] nodeid Node to which a transaction is being (or was) sent.152* @return Transaction or nullopt if the nodeid is unknown.153*/154std::optional<CTransactionRef> GetTxForNode(const NodeId& nodeid)155EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);157/**158* Mark that the node has confirmed reception of the transaction we sent it by159* responding with `PONG` to our `PING` message.160* @param[in] nodeid Node that we sent a transaction to.161*/162void NodeConfirmedReception(const NodeId& nodeid)163EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);165/**166* Check if there are transactions with send attempts remaining.167*/168bool HavePendingTransactions()169EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);171/**172* Get the transactions that have not been broadcast recently and have send173* attempts remaining.174*/175std::vector<CTransactionRef> GetStale() const176EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);178/**179* Get stats about all transactions currently being privately broadcast.180*/181std::vector<TxBroadcastInfo> GetBroadcastInfo() const182EXCLUSIVE_LOCKS_REQUIRED(!m_mutex);184private:185/// Status of a transaction sent to a given node.186struct SendStatus {187/// Node to which the transaction will be sent (or was sent).188const NodeId nodeid;189/// Address of the node.190const CService address;191/// When was the transaction picked for sending to the node.192const NodeClock::time_point picked;193/// When was the transaction reception confirmed by the node (by PONG).194std::optional<NodeClock::time_point> confirmed;195/// Whether the node has disconnected.196bool disconnected{false};198SendStatus(const NodeId& nodeid, const CService& address, const NodeClock::time_point& picked) : nodeid{nodeid}, address{address}, picked{picked} {}199};201/// Cumulative stats from all the send attempts for a transaction. Used to prioritize transactions.202struct Priority {203size_t num_picked{0}; ///< Number of times the transaction was picked for sending.204NodeClock::time_point last_picked{}; ///< The most recent time when the transaction was picked for sending.205size_t num_confirmed{0}; ///< Number of nodes that have confirmed reception of a transaction (by PONG).206NodeClock::time_point last_confirmed{}; ///< The most recent time when the transaction was confirmed.208auto operator<=>(const Priority& other) const209{210// Invert `other` and `this` in the comparison because smaller num_picked, num_confirmed or211// earlier times mean greater priority. In other words, if this.num_picked < other.num_picked212// then this > other.213return std::tie(other.num_picked, other.num_confirmed, other.last_picked, other.last_confirmed) <=>214std::tie(num_picked, num_confirmed, last_picked, last_confirmed);215}216};218/// A pair of a transaction and a sent status for a given node. Convenience return type of GetSendStatusByNode().219struct TxAndSendStatusForNode {220const CTransactionRef& tx;221SendStatus& send_status;222};224// No need for salted hasher because we are going to store just a bunch of locally originating transactions.226struct CTransactionRefHash {227size_t operator()(const CTransactionRef& tx) const228{229return static_cast<size_t>(tx->GetWitnessHash().ToUint256().GetUint64(0));230}231};233struct CTransactionRefComp {234bool operator()(const CTransactionRef& a, const CTransactionRef& b) const235{236return a->GetWitnessHash() == b->GetWitnessHash(); // If wtxid equals, then txid also equals.237}238};240/**241* Derive the sending priority of a transaction.242* @param[in] sent_to List of nodes that the transaction has been sent to.243*/244static Priority DerivePriority(const std::vector<SendStatus>& sent_to);246/**247* Find which transaction we sent to a given node (marked by PickTxForSend()).248* @return That transaction together with the send status or nullopt if we did not249* send any transaction to the given node.250*/
Independent explanations by MyBitcoinExplorer. Not affiliated with or endorsed by Bitcoin Core. Guides are educational and do not replace the source or its tests.
Bitcoin Core source © its contributors, distributed under the MIT license. Original notices and bundled third-party licenses are retained in the source tree.