LCOV - code coverage report
Current view: top level - src/node - txreconciliation.cpp (source / functions) Hit Total Coverage
Test: fuzz_coverage.info Lines: 57 57 100.0 %
Date: 2023-10-05 15:40:34 Functions: 20 20 100.0 %
Branches: 30 68 44.1 %

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

Generated by: LCOV version 1.14