LCOV - code coverage report
Current view: top level - src/node - txreconciliation.cpp (source / functions) Hit Total Coverage
Test: test_dash_coverage.info Lines: 53 53 100.0 %
Date: 2026-06-25 07:23:51 Functions: 20 20 100.0 %

          Line data    Source code
       1             : // Copyright (c) 2022 The Bitcoin Core developers
       2             : // Distributed under the MIT software license, see the accompanying
       3             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       4             : 
       5             : #include <node/txreconciliation.h>
       6             : 
       7             : #include <util/check.h>
       8             : #include <util/system.h>
       9             : 
      10             : #include <unordered_map>
      11             : #include <variant>
      12             : 
      13             : 
      14             : namespace {
      15             : 
      16             : /** Static salt component used to compute short txids for sketch construction, see BIP-330. */
      17             : const std::string RECON_STATIC_SALT = "Tx Relay Salting";
      18         146 : const HashWriter RECON_SALT_HASHER = TaggedHash(RECON_STATIC_SALT);
      19             : 
      20             : /**
      21             :  * Salt (specified by BIP-330) constructed from contributions from both peers. It is used
      22             :  * to compute transaction short IDs, which are then used to construct a sketch representing a set
      23             :  * of transactions we want to announce to the peer.
      24             :  */
      25           5 : uint256 ComputeSalt(uint64_t salt1, uint64_t salt2)
      26             : {
      27             :     // According to BIP-330, salts should be combined in ascending order.
      28           5 :     return (HashWriter(RECON_SALT_HASHER) << std::min(salt1, salt2) << std::max(salt1, salt2)).GetSHA256();
      29             : }
      30             : 
      31             : /**
      32             :  * Keeps track of txreconciliation-related per-peer state.
      33             :  */
      34             : class TxReconciliationState
      35             : {
      36             : public:
      37             :     /**
      38             :      * TODO: This field is public to ignore -Wunused-private-field. Make private once used in
      39             :      * the following commits.
      40             :      *
      41             :      * Reconciliation protocol assumes using one role consistently: either a reconciliation
      42             :      * initiator (requesting sketches), or responder (sending sketches). This defines our role,
      43             :      * based on the direction of the p2p connection.
      44             :      *
      45             :      */
      46             :     bool m_we_initiate;
      47             : 
      48             :     /**
      49             :      * TODO: These fields are public to ignore -Wunused-private-field. Make private once used in
      50             :      * the following commits.
      51             :      *
      52             :      * These values are used to salt short IDs, which is necessary for transaction reconciliations.
      53             :      */
      54             :     uint64_t m_k0, m_k1;
      55             : 
      56          10 :     TxReconciliationState(bool we_initiate, uint64_t k0, uint64_t k1) : m_we_initiate(we_initiate), m_k0(k0), m_k1(k1) {}
      57             : };
      58             : 
      59             : } // namespace
      60             : 
      61             : /** Actual implementation for TxReconciliationTracker's data structure. */
      62             : class TxReconciliationTracker::Impl
      63             : {
      64             : private:
      65             :     mutable Mutex m_txreconciliation_mutex;
      66             : 
      67             :     // Local protocol version
      68             :     uint32_t m_recon_version;
      69             : 
      70             :     /**
      71             :      * Keeps track of txreconciliation states of eligible peers.
      72             :      * For pre-registered peers, the locally generated salt is stored.
      73             :      * For registered peers, the locally generated salt is forgotten, and the state (including
      74             :      * "full" salt) is stored instead.
      75             :      */
      76             :     std::unordered_map<NodeId, std::variant<uint64_t, TxReconciliationState>> m_states GUARDED_BY(m_txreconciliation_mutex);
      77             : 
      78             : public:
      79           6 :     explicit Impl(uint32_t recon_version) : m_recon_version(recon_version) {}
      80             : 
      81           6 :     uint64_t PreRegisterPeer(NodeId peer_id) EXCLUSIVE_LOCKS_REQUIRED(!m_txreconciliation_mutex)
      82             :     {
      83           6 :         AssertLockNotHeld(m_txreconciliation_mutex);
      84           6 :         LOCK(m_txreconciliation_mutex);
      85             : 
      86           6 :         LogPrintLevel(BCLog::TXRECONCILIATION, BCLog::Level::Debug, "Pre-register peer=%d\n", peer_id);
      87           6 :         const uint64_t local_salt{GetRand(UINT64_MAX)};
      88             : 
      89             :         // We do this exactly once per peer (which are unique by NodeId, see GetNewNodeId) so it's
      90             :         // safe to assume we don't have this record yet.
      91           6 :         Assume(m_states.emplace(peer_id, local_salt).second);
      92           6 :         return local_salt;
      93           6 :     }
      94             : 
      95           9 :     ReconciliationRegisterResult RegisterPeer(NodeId peer_id, bool is_peer_inbound, uint32_t peer_recon_version,
      96             :                                               uint64_t remote_salt) EXCLUSIVE_LOCKS_REQUIRED(!m_txreconciliation_mutex)
      97             :     {
      98           9 :         AssertLockNotHeld(m_txreconciliation_mutex);
      99           9 :         LOCK(m_txreconciliation_mutex);
     100           9 :         auto recon_state = m_states.find(peer_id);
     101             : 
     102           9 :         if (recon_state == m_states.end()) return ReconciliationRegisterResult::NOT_FOUND;
     103             : 
     104           7 :         if (std::holds_alternative<TxReconciliationState>(recon_state->second)) {
     105           1 :             return ReconciliationRegisterResult::ALREADY_REGISTERED;
     106             :         }
     107             : 
     108           6 :         uint64_t local_salt = *std::get_if<uint64_t>(&recon_state->second);
     109             : 
     110             :         // If the peer supports the version which is lower than ours, we downgrade to the version
     111             :         // it supports. For now, this only guarantees that nodes with future reconciliation
     112             :         // versions have the choice of reconciling with this current version. However, they also
     113             :         // have the choice to refuse supporting reconciliations if the common version is not
     114             :         // satisfactory (e.g. too low).
     115           6 :         const uint32_t recon_version{std::min(peer_recon_version, m_recon_version)};
     116             :         // v1 is the lowest version, so suggesting something below must be a protocol violation.
     117           6 :         if (recon_version < 1) return ReconciliationRegisterResult::PROTOCOL_VIOLATION;
     118             : 
     119           5 :         LogPrintLevel(BCLog::TXRECONCILIATION, BCLog::Level::Debug, "Register peer=%d (inbound=%i)\n",
     120             :                       peer_id, is_peer_inbound);
     121             : 
     122           5 :         const uint256 full_salt{ComputeSalt(local_salt, remote_salt)};
     123           5 :         recon_state->second = TxReconciliationState(!is_peer_inbound, full_salt.GetUint64(0), full_salt.GetUint64(1));
     124           5 :         return ReconciliationRegisterResult::SUCCESS;
     125           9 :     }
     126             : 
     127           3 :     void ForgetPeer(NodeId peer_id) EXCLUSIVE_LOCKS_REQUIRED(!m_txreconciliation_mutex)
     128             :     {
     129           3 :         AssertLockNotHeld(m_txreconciliation_mutex);
     130           3 :         LOCK(m_txreconciliation_mutex);
     131           3 :         if (m_states.erase(peer_id)) {
     132           3 :             LogPrintLevel(BCLog::TXRECONCILIATION, BCLog::Level::Debug, "Forget txreconciliation state of peer=%d\n", peer_id);
     133           3 :         }
     134           3 :     }
     135             : 
     136          14 :     bool IsPeerRegistered(NodeId peer_id) const EXCLUSIVE_LOCKS_REQUIRED(!m_txreconciliation_mutex)
     137             :     {
     138          14 :         AssertLockNotHeld(m_txreconciliation_mutex);
     139          14 :         LOCK(m_txreconciliation_mutex);
     140          14 :         auto recon_state = m_states.find(peer_id);
     141          14 :         return (recon_state != m_states.end() &&
     142           8 :                 std::holds_alternative<TxReconciliationState>(recon_state->second));
     143          14 :     }
     144             : };
     145             : 
     146           6 : TxReconciliationTracker::TxReconciliationTracker(uint32_t recon_version) : m_impl{std::make_unique<TxReconciliationTracker::Impl>(recon_version)} {}
     147             : 
     148           6 : TxReconciliationTracker::~TxReconciliationTracker() = default;
     149             : 
     150           6 : uint64_t TxReconciliationTracker::PreRegisterPeer(NodeId peer_id)
     151             : {
     152           6 :     return m_impl->PreRegisterPeer(peer_id);
     153             : }
     154             : 
     155           9 : ReconciliationRegisterResult TxReconciliationTracker::RegisterPeer(NodeId peer_id, bool is_peer_inbound,
     156             :                                                           uint32_t peer_recon_version, uint64_t remote_salt)
     157             : {
     158           9 :     return m_impl->RegisterPeer(peer_id, is_peer_inbound, peer_recon_version, remote_salt);
     159             : }
     160             : 
     161           3 : void TxReconciliationTracker::ForgetPeer(NodeId peer_id)
     162             : {
     163           3 :     m_impl->ForgetPeer(peer_id);
     164           3 : }
     165             : 
     166          14 : bool TxReconciliationTracker::IsPeerRegistered(NodeId peer_id) const
     167             : {
     168          14 :     return m_impl->IsPeerRegistered(peer_id);
     169             : }

Generated by: LCOV version 1.16