LCOV - code coverage report
Current view: top level - src - private_broadcast.cpp (source / functions) Coverage Total Hit
Test: test_bitcoin_coverage.info Lines: 98.0 % 100 98
Test Date: 2026-08-23 07:26:31 Functions: 100.0 % 12 12
Branches: 68.5 % 108 74

             Branch data     Line data    Source code
       1                 :             : // Copyright (c) 2023-present The Bitcoin Core developers
       2                 :             : // Distributed under the MIT software license, see the accompanying
       3                 :             : // file COPYING or https://opensource.org/license/mit/.
       4                 :             : 
       5                 :             : #include <private_broadcast.h>
       6                 :             : 
       7                 :             : #include <util/check.h>
       8                 :             : 
       9                 :             : #include <algorithm>
      10                 :             : #include <ranges>
      11                 :             : 
      12                 :             : 
      13                 :       10017 : PrivateBroadcast::AddResult PrivateBroadcast::Add(const CTransactionRef& tx)
      14                 :             :     EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
      15                 :             : {
      16                 :       10017 :     LOCK(m_mutex);
      17         [ +  + ]:       10017 :     if (const auto it{m_transactions.find(tx)}; it != m_transactions.end()) {
      18   [ +  -  +  + ]:           4 :         if (IsPending(it->second)) return AddResult::AlreadyPresent;
      19                 :             : 
      20                 :             :         // An exhausted transaction can be explicitly retried by adding it again.
      21                 :           2 :         it->second.time_added = NodeClock::now();
      22                 :           2 :         it->second.send_statuses.clear();
      23                 :           2 :         return AddResult::Added;
      24                 :             :     }
      25                 :             : 
      26         [ +  + ]:       10013 :     if (m_transactions.size() >= m_max_transactions) return AddResult::QueueFull;
      27                 :             : 
      28         [ +  - ]:       10008 :     m_transactions.try_emplace(tx);
      29                 :             :     return AddResult::Added;
      30                 :       10017 : }
      31                 :             : 
      32                 :           7 : std::optional<size_t> PrivateBroadcast::Remove(const CTransactionRef& tx)
      33                 :             :     EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
      34                 :             : {
      35                 :           7 :     LOCK(m_mutex);
      36                 :           7 :     const auto handle{m_transactions.extract(tx)};
      37         [ +  + ]:           7 :     if (handle) {
      38         [ +  - ]:           5 :         const auto p{DerivePriority(handle.mapped().send_statuses)};
      39                 :           5 :         return p.num_confirmed;
      40                 :             :     }
      41                 :           2 :     return std::nullopt;
      42         [ +  - ]:          14 : }
      43                 :             : 
      44                 :          13 : std::optional<CTransactionRef> PrivateBroadcast::PickTxForSend(const NodeId& will_send_to_nodeid, const CService& will_send_to_address)
      45                 :             :     EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
      46                 :             : {
      47                 :          13 :     LOCK(m_mutex);
      48                 :             : 
      49   [ +  -  -  + ]:          13 :     if (GetSendStatusByNode(will_send_to_nodeid).has_value()) { // nodeid reuse, shouldn't send >1 tx to a given node
      50                 :           0 :         Assume(false);
      51                 :           0 :         return std::nullopt;
      52                 :             :     }
      53                 :             : 
      54         [ +  - ]:          28 :     auto pending_transactions{m_transactions | std::views::filter([this](const auto& entry) { return IsPending(entry.second); })};
      55         [ +  - ]:          13 :     const auto it{std::ranges::max_element(
      56                 :             :             pending_transactions,
      57                 :           3 :             [](const auto& a, const auto& b) { return a < b; },
      58                 :           3 :             [](const auto& el) { return DerivePriority(el.second.send_statuses); })};
      59                 :             : 
      60         [ +  + ]:          13 :     if (it != pending_transactions.end()) {
      61                 :          10 :         auto& [tx, state]{*it};
      62         [ +  - ]:          10 :         state.send_statuses.emplace_back(will_send_to_nodeid, will_send_to_address, NodeClock::now());
      63                 :          10 :         return tx;
      64                 :             :     }
      65                 :             : 
      66                 :           3 :     return std::nullopt;
      67                 :          13 : }
      68                 :             : 
      69                 :           4 : std::optional<CTransactionRef> PrivateBroadcast::GetTxForNode(const NodeId& nodeid)
      70                 :             :     EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
      71                 :             : {
      72                 :           4 :     LOCK(m_mutex);
      73         [ +  - ]:           4 :     const auto tx_and_status{GetSendStatusByNode(nodeid)};
      74         [ +  + ]:           4 :     if (tx_and_status.has_value()) {
      75                 :           2 :         return tx_and_status.value().tx;
      76                 :             :     }
      77                 :           2 :     return std::nullopt;
      78                 :           4 : }
      79                 :             : 
      80                 :           3 : void PrivateBroadcast::NodeConfirmedReception(const NodeId& nodeid)
      81                 :             :     EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
      82                 :             : {
      83                 :           3 :     LOCK(m_mutex);
      84         [ +  - ]:           3 :     const auto tx_and_status{GetSendStatusByNode(nodeid)};
      85         [ +  + ]:           3 :     if (tx_and_status.has_value()) {
      86   [ -  +  +  - ]:           5 :         tx_and_status.value().send_status.confirmed = NodeClock::now();
      87                 :             :     }
      88                 :           3 : }
      89                 :             : 
      90                 :           8 : bool PrivateBroadcast::DidNodeConfirmReception(const NodeId& nodeid)
      91                 :             :     EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
      92                 :             : {
      93                 :           8 :     LOCK(m_mutex);
      94         [ +  - ]:           8 :     const auto tx_and_status{GetSendStatusByNode(nodeid)};
      95         [ +  + ]:           8 :     if (tx_and_status.has_value()) {
      96                 :           5 :         return tx_and_status.value().send_status.confirmed.has_value();
      97                 :             :     }
      98                 :             :     return false;
      99                 :           8 : }
     100                 :             : 
     101                 :          12 : bool PrivateBroadcast::HavePendingTransactions()
     102                 :             :     EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
     103                 :             : {
     104                 :          12 :     LOCK(m_mutex);
     105   [ +  -  +  - ]:          22 :     return std::ranges::any_of(m_transactions, [this](const auto& entry) { return IsPending(entry.second); });
     106                 :          12 : }
     107                 :             : 
     108                 :          10 : std::vector<CTransactionRef> PrivateBroadcast::GetStale() const
     109                 :             :     EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
     110                 :             : {
     111                 :          10 :     LOCK(m_mutex);
     112                 :          10 :     const auto now{NodeClock::now()};
     113                 :          10 :     std::vector<CTransactionRef> stale;
     114   [ +  +  +  - ]:          24 :     for (const auto& [tx, state] : m_transactions) {
     115   [ +  +  +  - ]:          14 :         if (!IsPending(state)) continue;
     116         [ +  - ]:          13 :         const Priority p{DerivePriority(state.send_statuses)};
     117         [ +  + ]:          13 :         if (p.num_confirmed == 0) {
     118   [ +  +  +  - ]:          11 :             if (state.time_added < now - INITIAL_STALE_DURATION) stale.push_back(tx);
     119                 :             :         } else {
     120   [ +  +  +  - ]:           2 :             if (p.last_confirmed < now - STALE_DURATION) stale.push_back(tx);
     121                 :             :         }
     122                 :             :     }
     123         [ +  - ]:          10 :     return stale;
     124                 :          10 : }
     125                 :             : 
     126                 :          15 : std::vector<PrivateBroadcast::TxBroadcastInfo> PrivateBroadcast::GetBroadcastInfo() const
     127                 :             :     EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
     128                 :             : {
     129                 :          15 :     LOCK(m_mutex);
     130                 :          15 :     std::vector<TxBroadcastInfo> entries;
     131         [ +  - ]:          15 :     entries.reserve(m_transactions.size());
     132                 :             : 
     133   [ +  +  -  + ]:       70024 :     for (const auto& [tx, state] : m_transactions) {
     134                 :       70009 :         std::vector<PeerSendInfo> peers;
     135   [ +  -  -  + ]:       70009 :         peers.reserve(state.send_statuses.size());
     136         [ +  + ]:       70019 :         for (const auto& status : state.send_statuses) {
     137         [ +  - ]:          20 :             peers.emplace_back(PeerSendInfo{.address = status.address, .sent = status.picked, .received = status.confirmed});
     138                 :             :         }
     139   [ -  +  +  - ]:       70009 :         const size_t attempts_remaining{m_max_send_attempts - std::min(state.send_statuses.size(), m_max_send_attempts)};
     140   [ +  -  +  - ]:      140018 :         entries.emplace_back(TxBroadcastInfo{.tx = tx, .time_added = state.time_added, .attempts_remaining = attempts_remaining, .peers = std::move(peers)});
     141                 :       70009 :     }
     142                 :             : 
     143         [ +  - ]:          15 :     return entries;
     144                 :          15 : }
     145                 :             : 
     146                 :          43 : bool PrivateBroadcast::IsPending(const TxSendStatus& status) const
     147                 :             : {
     148         [ -  + ]:          43 :     return status.send_statuses.size() < m_max_send_attempts;
     149                 :             : }
     150                 :             : 
     151                 :          24 : PrivateBroadcast::Priority PrivateBroadcast::DerivePriority(const std::vector<SendStatus>& sent_to)
     152                 :             : {
     153                 :          24 :     Priority p;
     154         [ -  + ]:          24 :     p.num_picked = sent_to.size();
     155         [ +  + ]:          37 :     for (const auto& send_status : sent_to) {
     156                 :          13 :         p.last_picked = std::max(p.last_picked, send_status.picked);
     157         [ +  + ]:          13 :         if (send_status.confirmed.has_value()) {
     158                 :           3 :             ++p.num_confirmed;
     159                 :           3 :             p.last_confirmed = std::max(p.last_confirmed, send_status.confirmed.value());
     160                 :             :         }
     161                 :             :     }
     162                 :          24 :     return p;
     163                 :             : }
     164                 :             : 
     165                 :          28 : std::optional<PrivateBroadcast::TxAndSendStatusForNode> PrivateBroadcast::GetSendStatusByNode(const NodeId& nodeid)
     166                 :             :     EXCLUSIVE_LOCKS_REQUIRED(m_mutex)
     167                 :             : {
     168                 :          28 :     AssertLockHeld(m_mutex);
     169         [ +  + ]:          54 :     for (auto& [tx, state] : m_transactions) {
     170         [ +  + ]:          66 :         for (auto& send_status : state.send_statuses) {
     171         [ +  + ]:          40 :             if (send_status.nodeid == nodeid) {
     172                 :           9 :                 return TxAndSendStatusForNode{.tx = tx, .send_status = send_status};
     173                 :             :             }
     174                 :             :         }
     175                 :             :     }
     176                 :          19 :     return std::nullopt;
     177                 :             : }
        

Generated by: LCOV version 2.0-1