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 <crypto/common.h>6#include <crypto/sha256.h>7#include <crypto/siphash.h>8#include <primitives/transaction.h>9#include <test/fuzz/fuzz.h>10#include <txrequest.h>12#include <bitset>13#include <cstdint>14#include <queue>15#include <vector>17namespace {19constexpr int MAX_TXHASHES = 16;20constexpr int MAX_PEERS = 16;22//! Randomly generated txhashes used in this test (length is MAX_TXHASHES).23uint256 TXHASHES[MAX_TXHASHES];25//! Precomputed random durations (positive and negative, each ~exponentially distributed).26std::chrono::microseconds DELAYS[256];28struct Initializer29{30Initializer()31{32for (uint8_t txhash = 0; txhash < MAX_TXHASHES; txhash += 1) {33CSHA256().Write(&txhash, 1).Finalize(TXHASHES[txhash].begin());34}35int i = 0;36// DELAYS[N] for N=0..15 is just N microseconds.37for (; i < 16; ++i) {38DELAYS[i] = std::chrono::microseconds{i};39}40// DELAYS[N] for N=16..127 has randomly-looking but roughly exponentially increasing values up to41// 198.416453 seconds.42for (; i < 128; ++i) {43int diff_bits = ((i - 10) * 2) / 9;44uint64_t diff = 1 + (CSipHasher(0, 0).Write(i).Finalize() >> (64 - diff_bits));45DELAYS[i] = DELAYS[i - 1] + std::chrono::microseconds{diff};46}47// DELAYS[N] for N=128..255 are negative delays with the same magnitude as N=0..127.48for (; i < 256; ++i) {49DELAYS[i] = -DELAYS[255 - i];50}51}52} g_initializer;54/** Tester class for TxRequestTracker55*56* It includes a naive reimplementation of its behavior, for a limited set57* of MAX_TXHASHES distinct txids, and MAX_PEERS peer identifiers.58*59* All of the public member functions perform the same operation on60* an actual TxRequestTracker and on the state of the reimplementation.61* The output of GetRequestable is compared with the expected value62* as well.63*64* Check() calls the TxRequestTracker's sanity check, plus compares the65* output of the constant accessors (Size(), CountLoad(), CountTracked())66* with expected values.67*/68class Tester69{70//! TxRequestTracker object being tested.71TxRequestTracker m_tracker;73//! States for txid/peer combinations in the naive data structure.74enum class State {75NOTHING, //!< Absence of this txid/peer combination77// Note that this implementation does not distinguish between DELAYED/READY/BEST variants of CANDIDATE.78CANDIDATE,79REQUESTED,80COMPLETED,81};83//! Sequence numbers, incremented whenever a new CANDIDATE is added.84uint64_t m_current_sequence{0};86//! List of future 'events' (all inserted reqtimes/exptimes). This is used to implement AdvanceToEvent.87std::priority_queue<std::chrono::microseconds, std::vector<std::chrono::microseconds>,88std::greater<std::chrono::microseconds>> m_events;90//! Information about a txhash/peer combination.91struct Announcement92{93std::chrono::microseconds m_time;94uint64_t m_sequence;95State m_state{State::NOTHING};96bool m_preferred;97bool m_is_wtxid;98uint64_t m_priority; //!< Precomputed priority.99};101//! Information about all txhash/peer combination.102Announcement m_announcements[MAX_TXHASHES][MAX_PEERS];104//! The current time; can move forward and backward.105std::chrono::microseconds m_now{244466666};107//! Delete txhashes whose only announcements are COMPLETED.108void Cleanup(int txhash)109{110bool all_nothing = true;111for (int peer = 0; peer < MAX_PEERS; ++peer) {112const Announcement& ann = m_announcements[txhash][peer];113if (ann.m_state != State::NOTHING) {114if (ann.m_state != State::COMPLETED) return;115all_nothing = false;116}117}118if (all_nothing) return;119for (int peer = 0; peer < MAX_PEERS; ++peer) {120m_announcements[txhash][peer].m_state = State::NOTHING;121}122}124//! Find the current best peer to request from for a txhash (or -1 if none).125int GetSelected(int txhash) const126{127int ret = -1;128uint64_t ret_priority = 0;129for (int peer = 0; peer < MAX_PEERS; ++peer) {130const Announcement& ann = m_announcements[txhash][peer];131// Return -1 if there already is a (non-expired) in-flight request.132if (ann.m_state == State::REQUESTED) return -1;133// If it's a viable candidate, see if it has lower priority than the best one so far.134if (ann.m_state == State::CANDIDATE && ann.m_time <= m_now) {135if (ret == -1 || ann.m_priority > ret_priority) {136std::tie(ret, ret_priority) = std::tie(peer, ann.m_priority);137}138}139}140return ret;141}143public:144Tester() : m_tracker(true) {}146std::chrono::microseconds Now() const { return m_now; }148void AdvanceTime(std::chrono::microseconds offset)149{150m_now += offset;151while (!m_events.empty() && m_events.top() <= m_now) m_events.pop();152}154void AdvanceToEvent()155{156while (!m_events.empty() && m_events.top() <= m_now) m_events.pop();157if (!m_events.empty()) {158m_now = m_events.top();159m_events.pop();160}161}163void DisconnectedPeer(int peer)164{165// Apply to naive structure: all announcements for that peer are wiped.166for (int txhash = 0; txhash < MAX_TXHASHES; ++txhash) {167if (m_announcements[txhash][peer].m_state != State::NOTHING) {168m_announcements[txhash][peer].m_state = State::NOTHING;169Cleanup(txhash);170}171}173// Call TxRequestTracker's implementation.174m_tracker.DisconnectedPeer(peer);175}177void ForgetTxHash(int txhash)178{179// Apply to naive structure: all announcements for that txhash are wiped.180for (int peer = 0; peer < MAX_PEERS; ++peer) {181m_announcements[txhash][peer].m_state = State::NOTHING;182}183Cleanup(txhash);185// Call TxRequestTracker's implementation.186m_tracker.ForgetTxHash(TXHASHES[txhash]);187}189void ReceivedInv(int peer, int txhash, bool is_wtxid, bool preferred, std::chrono::microseconds reqtime)190{191// Apply to naive structure: if no announcement for txidnum/peer combination192// already, create a new CANDIDATE; otherwise do nothing.193Announcement& ann = m_announcements[txhash][peer];194if (ann.m_state == State::NOTHING) {195ann.m_preferred = preferred;196ann.m_state = State::CANDIDATE;197ann.m_time = reqtime;198ann.m_is_wtxid = is_wtxid;199ann.m_sequence = m_current_sequence++;200ann.m_priority = m_tracker.ComputePriority(TXHASHES[txhash], peer, ann.m_preferred);202// Add event so that AdvanceToEvent can quickly jump to the point where its reqtime passes.203if (reqtime > m_now) m_events.push(reqtime);204}206// Call TxRequestTracker's implementation.207auto gtxid = is_wtxid ? GenTxid{Wtxid::FromUint256(TXHASHES[txhash])} : GenTxid{Txid::FromUint256(TXHASHES[txhash])};208m_tracker.ReceivedInv(peer, gtxid, preferred, reqtime);209}211void RequestedTx(int peer, int txhash, std::chrono::microseconds exptime)212{213// Apply to naive structure: if a CANDIDATE announcement exists for peer/txhash,214// convert it to REQUESTED, and change any existing REQUESTED announcement for the same txhash to COMPLETED.215if (m_announcements[txhash][peer].m_state == State::CANDIDATE) {216for (int peer2 = 0; peer2 < MAX_PEERS; ++peer2) {217if (m_announcements[txhash][peer2].m_state == State::REQUESTED) {218m_announcements[txhash][peer2].m_state = State::COMPLETED;219}220}221m_announcements[txhash][peer].m_state = State::REQUESTED;222m_announcements[txhash][peer].m_time = exptime;223}225// Add event so that AdvanceToEvent can quickly jump to the point where its exptime passes.226if (exptime > m_now) m_events.push(exptime);228// Call TxRequestTracker's implementation.229m_tracker.RequestedTx(peer, TXHASHES[txhash], exptime);230}232void ReceivedResponse(int peer, int txhash)233{234// Apply to naive structure: convert anything to COMPLETED.235if (m_announcements[txhash][peer].m_state != State::NOTHING) {236m_announcements[txhash][peer].m_state = State::COMPLETED;237Cleanup(txhash);238}240// Call TxRequestTracker's implementation.241m_tracker.ReceivedResponse(peer, TXHASHES[txhash]);242}244void GetRequestable(int peer)245{246// Implement using naive structure:248//! list of (sequence number, txhash, is_wtxid) tuples.249std::vector<std::tuple<uint64_t, int, bool>> result;250std::vector<std::pair<NodeId, GenTxid>> expected_expired;
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.