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) 2020-present The Bitcoin Core developers2// Distributed under the MIT software license, see the accompanying3// file COPYING or http://www.opensource.org/licenses/mit-license.php.5#include <txrequest.h>7#include <crypto/siphash.h>8#include <net.h>9#include <primitives/transaction.h>10#include <random.h>11#include <uint256.h>13#include <boost/multi_index/indexed_by.hpp>14#include <boost/multi_index/ordered_index.hpp>15#include <boost/multi_index/sequenced_index.hpp>16#include <boost/multi_index/tag.hpp>17#include <boost/multi_index_container.hpp>18#include <boost/tuple/tuple.hpp>20#include <chrono>21#include <unordered_map>22#include <utility>24#include <cassert>26namespace {28/** The various states a (txhash,peer) pair can be in.29*30* Note that CANDIDATE is split up into 3 substates (DELAYED, BEST, READY), allowing more efficient implementation.31* Also note that the sorting order of ByTxHashView relies on the specific order of values in this enum.32*33* Expected behaviour is:34* - When first announced by a peer, the state is CANDIDATE_DELAYED until reqtime is reached.35* - Announcements that have reached their reqtime but not been requested will be either CANDIDATE_READY or36* CANDIDATE_BEST. Neither of those has an expiration time; they remain in that state until they're requested or37* no longer needed. CANDIDATE_READY announcements are promoted to CANDIDATE_BEST when they're the best one left.38* - When requested, an announcement will be in state REQUESTED until expiry is reached.39* - If expiry is reached, or the peer replies to the request (either with NOTFOUND or the tx), the state becomes40* COMPLETED.41*/42enum class State : uint8_t {43/** A CANDIDATE announcement whose reqtime is in the future. */44CANDIDATE_DELAYED,45/** A CANDIDATE announcement that's not CANDIDATE_DELAYED or CANDIDATE_BEST. */46CANDIDATE_READY,47/** The best CANDIDATE for a given txhash; only if there is no REQUESTED announcement already for that txhash.48* The CANDIDATE_BEST is the highest-priority announcement among all CANDIDATE_READY (and _BEST) ones for that49* txhash. */50CANDIDATE_BEST,51/** A REQUESTED announcement. */52REQUESTED,53/** A COMPLETED announcement. */54COMPLETED,55};57//! Type alias for sequence numbers.58using SequenceNumber = uint64_t;60/** An announcement. This is the data we track for each txid or wtxid that is announced to us by each peer. */61struct Announcement {62/** Txid or wtxid that was announced. */63const GenTxid m_gtxid;64/** For CANDIDATE_{DELAYED,BEST,READY} the reqtime; for REQUESTED the expiry. */65std::chrono::microseconds m_time;66/** What peer the request was from. */67const NodeId m_peer;68/** What sequence number this announcement has. */69const SequenceNumber m_sequence : 59;70/** Whether the request is preferred. */71const bool m_preferred : 1;72/** What state this announcement is in. */73State m_state : 3 {State::CANDIDATE_DELAYED};74State GetState() const { return m_state; }75void SetState(State state) { m_state = state; }77/** Whether this announcement is selected. There can be at most 1 selected peer per txhash. */78bool IsSelected() const79{80return GetState() == State::CANDIDATE_BEST || GetState() == State::REQUESTED;81}83/** Whether this announcement is waiting for a certain time to pass. */84bool IsWaiting() const85{86return GetState() == State::REQUESTED || GetState() == State::CANDIDATE_DELAYED;87}89/** Whether this announcement can feasibly be selected if the current IsSelected() one disappears. */90bool IsSelectable() const91{92return GetState() == State::CANDIDATE_READY || GetState() == State::CANDIDATE_BEST;93}95/** Construct a new announcement from scratch, initially in CANDIDATE_DELAYED state. */96Announcement(const GenTxid& gtxid, NodeId peer, bool preferred, std::chrono::microseconds reqtime,97SequenceNumber sequence)98: m_gtxid(gtxid), m_time(reqtime), m_peer(peer), m_sequence(sequence), m_preferred(preferred) {}99};101//! Type alias for priorities.102using Priority = uint64_t;104/** A functor with embedded salt that computes priority of an announcement.105*106* Higher priorities are selected first.107*/108class PriorityComputer {109const uint64_t m_k0, m_k1;110public:111explicit PriorityComputer(bool deterministic) :112m_k0{deterministic ? 0 : FastRandomContext().rand64()},113m_k1{deterministic ? 0 : FastRandomContext().rand64()} {}115Priority operator()(const uint256& txhash, NodeId peer, bool preferred) const116{117uint64_t low_bits = CSipHasher(m_k0, m_k1).Write(txhash).Write(peer).Finalize() >> 1;118return low_bits | uint64_t{preferred} << 63;119}121Priority operator()(const Announcement& ann) const122{123return operator()(ann.m_gtxid.ToUint256(), ann.m_peer, ann.m_preferred);124}125};127// Definitions for the 3 indexes used in the main data structure.128//129// Each index has a By* type to identify it, a By*View data type to represent the view of announcement it is sorted130// by, and an By*ViewExtractor type to convert an announcement into the By*View type.131// See https://www.boost.org/doc/libs/1_58_0/libs/multi_index/doc/reference/key_extraction.html#key_extractors132// for more information about the key extraction concept.134// The ByPeer index is sorted by (peer, state == CANDIDATE_BEST, txhash)135//136// Uses:137// * Looking up existing announcements by peer/txhash, by checking both (peer, false, txhash) and138// (peer, true, txhash).139// * Finding all CANDIDATE_BEST announcements for a given peer in GetRequestable.140struct ByPeer {};141using ByPeerView = std::tuple<NodeId, bool, const uint256&>;142struct ByPeerViewExtractor143{144using result_type = ByPeerView;145result_type operator()(const Announcement& ann) const146{147return ByPeerView{ann.m_peer, ann.GetState() == State::CANDIDATE_BEST, ann.m_gtxid.ToUint256()};148}149};151// The ByTxHash index is sorted by (txhash, state, priority).152//153// Note: priority == 0 whenever state != CANDIDATE_READY.154//155// Uses:156// * Deleting all announcements with a given txhash in ForgetTxHash.157// * Finding the best CANDIDATE_READY to convert to CANDIDATE_BEST, when no other CANDIDATE_READY or REQUESTED158// announcement exists for that txhash.159// * Determining when no more non-COMPLETED announcements for a given txhash exist, so the COMPLETED ones can be160// deleted.161struct ByTxHash {};162using ByTxHashView = std::tuple<const uint256&, State, Priority>;163class ByTxHashViewExtractor {164const PriorityComputer& m_computer;165public:166explicit ByTxHashViewExtractor(const PriorityComputer& computer) : m_computer(computer) {}167using result_type = ByTxHashView;168result_type operator()(const Announcement& ann) const169{170const Priority prio = (ann.GetState() == State::CANDIDATE_READY) ? m_computer(ann) : 0;171return ByTxHashView{ann.m_gtxid.ToUint256(), ann.GetState(), prio};172}173};175enum class WaitState {176//! Used for announcements that need efficient testing of "is their timestamp in the future?".177FUTURE_EVENT,178//! Used for announcements whose timestamp is not relevant.179NO_EVENT,180//! Used for announcements that need efficient testing of "is their timestamp in the past?".181PAST_EVENT,182};184WaitState GetWaitState(const Announcement& ann)185{186if (ann.IsWaiting()) return WaitState::FUTURE_EVENT;187if (ann.IsSelectable()) return WaitState::PAST_EVENT;188return WaitState::NO_EVENT;189}191// The ByTime index is sorted by (wait_state, time).192//193// All announcements with a timestamp in the future can be found by iterating the index forward from the beginning.194// All announcements with a timestamp in the past can be found by iterating the index backwards from the end.195//196// Uses:197// * Finding CANDIDATE_DELAYED announcements whose reqtime has passed, and REQUESTED announcements whose expiry has198// passed.199// * Finding CANDIDATE_READY/BEST announcements whose reqtime is in the future (when the clock time went backwards).200struct ByTime {};201using ByTimeView = std::pair<WaitState, std::chrono::microseconds>;202struct ByTimeViewExtractor203{204using result_type = ByTimeView;205result_type operator()(const Announcement& ann) const206{207return ByTimeView{GetWaitState(ann), ann.m_time};208}209};212/** Data type for the main data structure (Announcement objects with ByPeer/ByTxHash/ByTime indexes). */213using Index = boost::multi_index_container<214Announcement,215boost::multi_index::indexed_by<216boost::multi_index::ordered_unique<boost::multi_index::tag<ByPeer>, ByPeerViewExtractor>,217boost::multi_index::ordered_non_unique<boost::multi_index::tag<ByTxHash>, ByTxHashViewExtractor>,218boost::multi_index::ordered_non_unique<boost::multi_index::tag<ByTime>, ByTimeViewExtractor>219>220>;222/** Helper type to simplify syntax of iterator types. */223template<typename Tag>224using Iter = typename Index::index<Tag>::type::iterator;226/** Per-peer statistics object. */227struct PeerInfo {228size_t m_total = 0; //!< Total number of announcements for this peer.229size_t m_completed = 0; //!< Number of COMPLETED announcements for this peer.230size_t m_requested = 0; //!< Number of REQUESTED announcements for this peer.231};233/** Per-txhash statistics object. Only used for sanity checking. */234struct TxHashInfo235{236//! Number of CANDIDATE_DELAYED announcements for this txhash.237size_t m_candidate_delayed = 0;238//! Number of CANDIDATE_READY announcements for this txhash.239size_t m_candidate_ready = 0;240//! Number of CANDIDATE_BEST announcements for this txhash (at most one).241size_t m_candidate_best = 0;242//! Number of REQUESTED announcements for this txhash (at most one; mutually exclusive with CANDIDATE_BEST).243size_t m_requested = 0;244//! The priority of the CANDIDATE_BEST announcement if one exists, or max() otherwise.245Priority m_priority_candidate_best = std::numeric_limits<Priority>::max();246//! The highest priority of all CANDIDATE_READY announcements (or min() if none exist).247Priority m_priority_best_candidate_ready = std::numeric_limits<Priority>::min();248//! All peers we have an announcement for this txhash for.249std::vector<NodeId> m_peers;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.