Inside Bitcoin
Opening the selected file…
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) 2022-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 <node/txreconciliation.h>7#include <common/system.h>8#include <util/check.h>9#include <util/log.h>11#include <unordered_map>12#include <variant>15namespace {17/** Static salt component used to compute short txids for sketch construction, see BIP-330. */18const std::string RECON_STATIC_SALT = "Tx Relay Salting";19const HashWriter RECON_SALT_HASHER = TaggedHash(RECON_STATIC_SALT);21/**22* Salt (specified by BIP-330) constructed from contributions from both peers. It is used23* to compute transaction short IDs, which are then used to construct a sketch representing a set24* of transactions we want to announce to the peer.25*/26uint256 ComputeSalt(uint64_t salt1, uint64_t salt2)27{28// According to BIP-330, salts should be combined in ascending order.29return (HashWriter(RECON_SALT_HASHER) << std::min(salt1, salt2) << std::max(salt1, salt2)).GetSHA256();30}32/**33* Keeps track of txreconciliation-related per-peer state.34*/35class TxReconciliationState36{37public:38/**39* TODO: This field is public to ignore -Wunused-private-field. Make private once used in40* the following commits.41*42* Reconciliation protocol assumes using one role consistently: either a reconciliation43* initiator (requesting sketches), or responder (sending sketches). This defines our role,44* based on the direction of the p2p connection.45*46*/47bool m_we_initiate;49/**50* TODO: These fields are public to ignore -Wunused-private-field. Make private once used in51* the following commits.52*53* These values are used to salt short IDs, which is necessary for transaction reconciliations.54*/55uint64_t m_k0, m_k1;57TxReconciliationState(bool we_initiate, uint64_t k0, uint64_t k1) : m_we_initiate(we_initiate), m_k0(k0), m_k1(k1) {}58};60} // namespace62/** Actual implementation for TxReconciliationTracker's data structure. */63class TxReconciliationTracker::Impl64{65private:66mutable Mutex m_txreconciliation_mutex;68// Local protocol version69uint32_t m_recon_version;71/**72* Keeps track of txreconciliation states of eligible peers.73* For pre-registered peers, the locally generated salt is stored.74* For registered peers, the locally generated salt is forgotten, and the state (including75* "full" salt) is stored instead.76*/77std::unordered_map<NodeId, std::variant<uint64_t, TxReconciliationState>> m_states GUARDED_BY(m_txreconciliation_mutex);79public:80explicit Impl(uint32_t recon_version) : m_recon_version(recon_version) {}82uint64_t PreRegisterPeer(NodeId peer_id) EXCLUSIVE_LOCKS_REQUIRED(!m_txreconciliation_mutex)83{84AssertLockNotHeld(m_txreconciliation_mutex);85LOCK(m_txreconciliation_mutex);87LogDebug(BCLog::TXRECONCILIATION, "Pre-register peer=%d\n", peer_id);88const uint64_t local_salt{FastRandomContext().rand64()};90// We do this exactly once per peer (which are unique by NodeId, see GetNewNodeId) so it's91// safe to assume we don't have this record yet.92Assume(m_states.emplace(peer_id, local_salt).second);93return local_salt;94}96ReconciliationRegisterResult RegisterPeer(NodeId peer_id, bool is_peer_inbound, uint32_t peer_recon_version,97uint64_t remote_salt) EXCLUSIVE_LOCKS_REQUIRED(!m_txreconciliation_mutex)98{99AssertLockNotHeld(m_txreconciliation_mutex);100LOCK(m_txreconciliation_mutex);101auto recon_state = m_states.find(peer_id);103if (recon_state == m_states.end()) return ReconciliationRegisterResult::NOT_FOUND;105if (std::holds_alternative<TxReconciliationState>(recon_state->second)) {106return ReconciliationRegisterResult::ALREADY_REGISTERED;107}109uint64_t local_salt = *std::get_if<uint64_t>(&recon_state->second);111// If the peer supports the version which is lower than ours, we downgrade to the version112// it supports. For now, this only guarantees that nodes with future reconciliation113// versions have the choice of reconciling with this current version. However, they also114// have the choice to refuse supporting reconciliations if the common version is not115// satisfactory (e.g. too low).116const uint32_t recon_version{std::min(peer_recon_version, m_recon_version)};117// v1 is the lowest version, so suggesting something below must be a protocol violation.118if (recon_version < 1) return ReconciliationRegisterResult::PROTOCOL_VIOLATION;120LogDebug(BCLog::TXRECONCILIATION, "Register peer=%d (inbound=%i)\n",121peer_id, is_peer_inbound);123const uint256 full_salt{ComputeSalt(local_salt, remote_salt)};124recon_state->second = TxReconciliationState(!is_peer_inbound, full_salt.GetUint64(0), full_salt.GetUint64(1));125return ReconciliationRegisterResult::SUCCESS;126}128void ForgetPeer(NodeId peer_id) EXCLUSIVE_LOCKS_REQUIRED(!m_txreconciliation_mutex)129{130AssertLockNotHeld(m_txreconciliation_mutex);131LOCK(m_txreconciliation_mutex);132if (m_states.erase(peer_id)) {133LogDebug(BCLog::TXRECONCILIATION, "Forget txreconciliation state of peer=%d\n", peer_id);134}135}137bool IsPeerRegistered(NodeId peer_id) const EXCLUSIVE_LOCKS_REQUIRED(!m_txreconciliation_mutex)138{139AssertLockNotHeld(m_txreconciliation_mutex);140LOCK(m_txreconciliation_mutex);141auto recon_state = m_states.find(peer_id);142return (recon_state != m_states.end() &&143std::holds_alternative<TxReconciliationState>(recon_state->second));144}145};147TxReconciliationTracker::TxReconciliationTracker(uint32_t recon_version) : m_impl{std::make_unique<TxReconciliationTracker::Impl>(recon_version)} {}149TxReconciliationTracker::~TxReconciliationTracker() = default;151uint64_t TxReconciliationTracker::PreRegisterPeer(NodeId peer_id)152{153return m_impl->PreRegisterPeer(peer_id);154}156ReconciliationRegisterResult TxReconciliationTracker::RegisterPeer(NodeId peer_id, bool is_peer_inbound,157uint32_t peer_recon_version, uint64_t remote_salt)158{159return m_impl->RegisterPeer(peer_id, is_peer_inbound, peer_recon_version, remote_salt);160}162void TxReconciliationTracker::ForgetPeer(NodeId peer_id)163{164m_impl->ForgetPeer(peer_id);165}167bool TxReconciliationTracker::IsPeerRegistered(NodeId peer_id) const168{169return m_impl->IsPeerRegistered(peer_id);170}
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.