LCOV - code coverage report
Current view: top level - src/test - private_broadcast_tests.cpp (source / functions) Coverage Total Hit
Test: test_bitcoin_coverage.info Lines: 95.7 % 139 133
Test Date: 2026-07-17 06:52:43 Functions: 90.0 % 10 9
Branches: 50.6 % 712 360

             Branch data     Line data    Source code
       1                 :             : // Copyright (c) 2025-present 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 <primitives/transaction.h>
       6                 :             : #include <private_broadcast.h>
       7                 :             : #include <test/util/setup_common.h>
       8                 :             : #include <test/util/time.h>
       9                 :             : #include <util/time.h>
      10                 :             : 
      11                 :             : #include <algorithm>
      12                 :             : #include <ostream>
      13                 :             : #include <boost/test/unit_test.hpp>
      14                 :             : 
      15                 :           0 : std::ostream& operator<<(std::ostream& os, PrivateBroadcast::AddResult r)
      16                 :             : {
      17   [ #  #  #  # ]:           0 :     switch (r) {
      18                 :           0 :     case PrivateBroadcast::AddResult::Added: return os << "Added";
      19                 :           0 :     case PrivateBroadcast::AddResult::AlreadyPresent: return os << "AlreadyPresent";
      20                 :           0 :     case PrivateBroadcast::AddResult::QueueFull: return os << "QueueFull";
      21                 :             :     } // no default case, so the compiler can warn about missing cases
      22                 :           0 :     assert(false);
      23                 :             : }
      24                 :             : 
      25                 :             : BOOST_FIXTURE_TEST_SUITE(private_broadcast_tests, BasicTestingSetup)
      26                 :             : 
      27                 :       10009 : static CTransactionRef MakeDummyTx(uint32_t id, size_t num_witness)
      28                 :             : {
      29                 :       10009 :     CMutableTransaction mtx;
      30         [ +  - ]:       10009 :     mtx.vin.resize(1);
      31         [ +  + ]:       10009 :     mtx.vin[0].nSequence = id;
      32         [ +  + ]:       10009 :     if (num_witness > 0) {
      33                 :           1 :         mtx.vin[0].scriptWitness = CScriptWitness{};
      34         [ +  - ]:           1 :         mtx.vin[0].scriptWitness.stack.resize(num_witness);
      35                 :             :     }
      36         [ +  - ]:       20018 :     return MakeTransactionRef(mtx);
      37                 :       10009 : }
      38                 :             : 
      39   [ +  -  +  -  :           7 : BOOST_AUTO_TEST_CASE(basic)
          +  -  +  -  -  
          +  +  -  +  -  
          +  -  +  -  +  
          -  +  -  -  +  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  -  +  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  -  +  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  -  +  +  
                      - ]
      40                 :             : {
      41                 :           1 :     FakeNodeClock clock{};
      42                 :             : 
      43         [ +  - ]:           1 :     PrivateBroadcast pb;
      44                 :           1 :     const NodeId recipient1{1};
      45                 :           1 :     in_addr ipv4Addr;
      46                 :           1 :     ipv4Addr.s_addr = 0xa0b0c001;
      47         [ +  - ]:           1 :     const CService addr1{ipv4Addr, 1111};
      48                 :             : 
      49                 :             :     // No transactions initially.
      50   [ +  -  +  -  :           2 :     BOOST_CHECK(!pb.PickTxForSend(/*will_send_to_nodeid=*/recipient1, /*will_send_to_address=*/addr1).has_value());
             +  -  +  - ]
      51   [ +  -  +  -  :           2 :     BOOST_CHECK_EQUAL(pb.GetStale().size(), 0);
                   +  - ]
      52   [ +  -  +  -  :           2 :     BOOST_CHECK(!pb.HavePendingTransactions());
             +  -  +  - ]
      53   [ +  -  +  -  :           2 :     BOOST_CHECK_EQUAL(pb.GetBroadcastInfo().size(), 0);
                   +  - ]
      54                 :             : 
      55                 :             :     // Make a transaction and add it.
      56         [ +  - ]:           1 :     const auto tx1{MakeDummyTx(/*id=*/1, /*num_witness=*/0)};
      57                 :             : 
      58   [ +  -  +  -  :           1 :     BOOST_CHECK_EQUAL(pb.Add(tx1), PrivateBroadcast::AddResult::Added);
                   +  - ]
      59   [ +  -  +  -  :           1 :     BOOST_CHECK_EQUAL(pb.Add(tx1), PrivateBroadcast::AddResult::AlreadyPresent);
                   +  - ]
      60                 :             : 
      61                 :             :     // Make another transaction with same txid, different wtxid and add it.
      62         [ +  - ]:           1 :     const auto tx2{MakeDummyTx(/*id=*/1, /*num_witness=*/1)};
      63   [ +  -  +  -  :           2 :     BOOST_REQUIRE(tx1->GetHash() == tx2->GetHash());
                   +  - ]
      64   [ +  -  +  -  :           2 :     BOOST_REQUIRE(tx1->GetWitnessHash() != tx2->GetWitnessHash());
                   +  - ]
      65                 :             : 
      66   [ +  -  +  -  :           1 :     BOOST_CHECK_EQUAL(pb.Add(tx2), PrivateBroadcast::AddResult::Added);
                   +  - ]
      67                 :           7 :     const auto find_tx_info{[](auto& infos, const CTransactionRef& tx) -> const PrivateBroadcast::TxBroadcastInfo& {
      68         [ +  + ]:          15 :         const auto it{std::ranges::find(infos, tx->GetWitnessHash(), [](const auto& info) { return info.tx->GetWitnessHash(); })};
      69         [ +  - ]:          12 :         BOOST_REQUIRE(it != infos.end());
      70                 :           6 :         return *it;
      71                 :             :     }};
      72                 :           3 :     const auto check_peer_counts{[&](size_t tx1_peer_count, size_t tx2_peer_count) {
      73                 :           2 :         const auto infos{pb.GetBroadcastInfo()};
      74   [ +  -  -  +  :           2 :         BOOST_CHECK_EQUAL(infos.size(), 2);
                   +  - ]
      75   [ +  -  +  -  :           2 :         BOOST_CHECK_EQUAL(find_tx_info(infos, tx1).peers.size(), tx1_peer_count);
             -  +  +  - ]
      76   [ +  -  +  -  :           2 :         BOOST_CHECK_EQUAL(find_tx_info(infos, tx2).peers.size(), tx2_peer_count);
             -  +  +  - ]
      77                 :           3 :     }};
      78                 :             : 
      79         [ +  - ]:           1 :     check_peer_counts(/*tx1_peer_count=*/0, /*tx2_peer_count=*/0);
      80                 :             : 
      81         [ +  - ]:           2 :     const auto tx_for_recipient1{pb.PickTxForSend(/*will_send_to_nodeid=*/recipient1, /*will_send_to_address=*/addr1).value()};
      82   [ +  -  +  -  :           3 :     BOOST_CHECK(tx_for_recipient1 == tx1 || tx_for_recipient1 == tx2);
          +  -  +  -  +  
                      - ]
      83                 :             : 
      84                 :             :     // A second pick must return the other transaction.
      85                 :           1 :     const NodeId recipient2{2};
      86         [ +  - ]:           1 :     const CService addr2{ipv4Addr, 2222};
      87         [ +  - ]:           2 :     const auto tx_for_recipient2{pb.PickTxForSend(/*will_send_to_nodeid=*/recipient2, /*will_send_to_address=*/addr2).value()};
      88   [ +  -  -  +  :           2 :     BOOST_CHECK(tx_for_recipient2 == tx1 || tx_for_recipient2 == tx2);
          -  -  +  -  +  
                      - ]
      89   [ +  -  +  - ]:           1 :     BOOST_CHECK_NE(tx_for_recipient1, tx_for_recipient2);
      90                 :             : 
      91         [ +  - ]:           1 :     check_peer_counts(/*tx1_peer_count=*/1, /*tx2_peer_count=*/1);
      92                 :             : 
      93                 :           1 :     const NodeId nonexistent_recipient{0};
      94                 :             : 
      95                 :             :     // Confirm transactions <-> recipients mapping is correct.
      96   [ +  -  +  -  :           2 :     BOOST_CHECK(!pb.GetTxForNode(nonexistent_recipient).has_value());
             +  -  +  - ]
      97   [ +  -  +  -  :           2 :     BOOST_CHECK_EQUAL(pb.GetTxForNode(recipient1).value(), tx_for_recipient1);
                   +  - ]
      98   [ +  -  +  -  :           2 :     BOOST_CHECK_EQUAL(pb.GetTxForNode(recipient2).value(), tx_for_recipient2);
                   +  - ]
      99                 :             : 
     100                 :             :     // Confirm none of the transactions' reception have been confirmed.
     101   [ +  -  +  -  :           2 :     BOOST_CHECK(!pb.DidNodeConfirmReception(recipient1));
             +  -  +  - ]
     102   [ +  -  +  -  :           2 :     BOOST_CHECK(!pb.DidNodeConfirmReception(recipient2));
             +  -  +  - ]
     103   [ +  -  +  -  :           2 :     BOOST_CHECK(!pb.DidNodeConfirmReception(nonexistent_recipient));
             +  -  +  - ]
     104                 :             : 
     105                 :             :     // 1. Freshly added transactions should NOT be stale yet.
     106   [ +  -  +  -  :           2 :     BOOST_CHECK_EQUAL(pb.GetStale().size(), 0);
                   +  - ]
     107                 :             : 
     108                 :             :     // 2. Fast-forward the mock clock past the INITIAL_STALE_DURATION.
     109         [ +  - ]:           1 :     clock += PrivateBroadcast::INITIAL_STALE_DURATION + 1min;
     110                 :             : 
     111                 :             :     // 3. Now that the initial duration has passed, both unconfirmed transactions should be stale.
     112   [ +  -  +  -  :           2 :     BOOST_CHECK_EQUAL(pb.GetStale().size(), 2);
                   +  - ]
     113                 :             : 
     114                 :             :     // Confirm reception by recipient1.
     115         [ +  - ]:           1 :     pb.NodeConfirmedReception(nonexistent_recipient); // Dummy call.
     116         [ +  - ]:           1 :     pb.NodeConfirmedReception(recipient1);
     117                 :             : 
     118   [ +  -  +  -  :           2 :     BOOST_CHECK(pb.DidNodeConfirmReception(recipient1));
             +  -  +  - ]
     119   [ +  -  +  -  :           2 :     BOOST_CHECK(!pb.DidNodeConfirmReception(recipient2));
             +  -  +  - ]
     120                 :             : 
     121         [ +  - ]:           1 :     const auto infos{pb.GetBroadcastInfo()};
     122   [ +  -  -  +  :           1 :     BOOST_CHECK_EQUAL(infos.size(), 2);
                   +  - ]
     123                 :           1 :     {
     124         [ +  - ]:           1 :         const auto& peers{find_tx_info(infos, tx_for_recipient1).peers};
     125   [ +  -  -  +  :           1 :         BOOST_CHECK_EQUAL(peers.size(), 1);
                   +  - ]
     126   [ +  -  +  -  :           1 :         BOOST_CHECK_EQUAL(peers[0].address.ToStringAddrPort(), addr1.ToStringAddrPort());
             +  -  +  - ]
     127   [ +  -  +  -  :           2 :         BOOST_CHECK(peers[0].received.has_value());
                   +  - ]
     128                 :             :     }
     129                 :           1 :     {
     130         [ +  - ]:           1 :         const auto& peers{find_tx_info(infos, tx_for_recipient2).peers};
     131   [ +  -  -  +  :           1 :         BOOST_CHECK_EQUAL(peers.size(), 1);
                   +  - ]
     132   [ +  -  +  -  :           1 :         BOOST_CHECK_EQUAL(peers[0].address.ToStringAddrPort(), addr2.ToStringAddrPort());
             +  -  +  - ]
     133   [ +  -  +  -  :           2 :         BOOST_CHECK(!peers[0].received.has_value());
                   +  - ]
     134                 :             :     }
     135                 :             : 
     136         [ +  - ]:           1 :     const auto stale_state{pb.GetStale()};
     137   [ +  -  -  +  :           1 :     BOOST_CHECK_EQUAL(stale_state.size(), 1);
                   +  - ]
     138   [ +  -  +  - ]:           1 :     BOOST_CHECK_EQUAL(stale_state[0], tx_for_recipient2);
     139                 :             : 
     140         [ +  - ]:           1 :     clock += 10h;
     141                 :             : 
     142   [ +  -  +  -  :           2 :     BOOST_CHECK_EQUAL(pb.GetStale().size(), 2);
                   +  - ]
     143                 :             : 
     144   [ +  -  +  -  :           2 :     BOOST_CHECK_EQUAL(pb.Remove(tx_for_recipient1).value(), 1);
                   +  - ]
     145   [ +  -  +  -  :           2 :     BOOST_CHECK(!pb.Remove(tx_for_recipient1).has_value());
             +  -  +  - ]
     146   [ +  -  +  -  :           2 :     BOOST_CHECK_EQUAL(pb.Remove(tx_for_recipient2).value(), 0);
                   +  - ]
     147   [ +  -  +  -  :           2 :     BOOST_CHECK(!pb.Remove(tx_for_recipient2).has_value());
             +  -  +  - ]
     148                 :             : 
     149   [ +  -  +  -  :           2 :     BOOST_CHECK_EQUAL(pb.GetBroadcastInfo().size(), 0);
                   +  - ]
     150         [ +  - ]:           1 :     const CService addr_nonexistent{ipv4Addr, 3333};
     151   [ +  -  +  -  :           2 :     BOOST_CHECK(!pb.PickTxForSend(/*will_send_to_nodeid=*/nonexistent_recipient, /*will_send_to_address=*/addr_nonexistent).has_value());
                   +  - ]
     152   [ +  -  +  -  :           5 : }
             +  -  +  - ]
     153                 :             : 
     154   [ +  -  +  -  :           7 : BOOST_AUTO_TEST_CASE(stale_unpicked_tx)
          +  -  +  -  -  
          +  +  -  +  -  
          +  -  +  -  +  
          -  +  -  -  +  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  -  +  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  -  +  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  -  +  +  
                      - ]
     155                 :             : {
     156                 :           1 :     FakeNodeClock clock{};
     157                 :             : 
     158         [ +  - ]:           1 :     PrivateBroadcast pb;
     159         [ +  - ]:           1 :     const auto tx{MakeDummyTx(/*id=*/42, /*num_witness=*/0)};
     160   [ +  -  +  -  :           1 :     BOOST_REQUIRE_EQUAL(pb.Add(tx), PrivateBroadcast::AddResult::Added);
                   +  - ]
     161                 :             : 
     162                 :             :     // Unpicked transactions use the longer INITIAL_STALE_DURATION.
     163   [ +  -  +  -  :           2 :     BOOST_CHECK_EQUAL(pb.GetStale().size(), 0);
                   +  - ]
     164         [ +  - ]:           1 :     clock += PrivateBroadcast::INITIAL_STALE_DURATION - 1min;
     165   [ +  -  +  -  :           2 :     BOOST_CHECK_EQUAL(pb.GetStale().size(), 0);
                   +  - ]
     166         [ +  - ]:           1 :     clock += 2min;
     167         [ +  - ]:           1 :     const auto stale_state{pb.GetStale()};
     168   [ +  -  -  +  :           1 :     BOOST_REQUIRE_EQUAL(stale_state.size(), 1);
                   +  - ]
     169   [ +  -  +  - ]:           1 :     BOOST_CHECK_EQUAL(stale_state[0], tx);
     170         [ +  - ]:           2 : }
     171                 :             : 
     172   [ +  -  +  -  :           7 : BOOST_AUTO_TEST_CASE(rejection_at_cap)
          +  -  +  -  -  
          +  +  -  +  -  
          +  -  +  -  +  
          -  +  -  -  +  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  -  +  +  
          -  +  -  +  -  
          +  -  +  -  +  
          -  +  -  -  +  
          +  -  +  -  +  
          -  +  -  +  -  
          +  -  -  +  +  
                      - ]
     173                 :             : {
     174         [ +  - ]:           1 :     PrivateBroadcast pb;
     175                 :           1 :     constexpr size_t num_cap{PrivateBroadcast::MAX_TRANSACTIONS};
     176                 :           1 :     constexpr size_t num_over{5};
     177                 :             : 
     178                 :             :     // Fill the queue exactly to the cap; every distinct Add() succeeds.
     179                 :           1 :     std::vector<CTransactionRef> txs;
     180         [ +  - ]:           1 :     txs.reserve(num_cap);
     181         [ +  + ]:       10001 :     for (size_t i{0}; i < num_cap; ++i) {
     182         [ +  - ]:       10000 :         auto tx{MakeDummyTx(/*id=*/static_cast<uint32_t>(i), /*num_witness=*/0)};
     183   [ +  -  +  -  :       10000 :         BOOST_REQUIRE_EQUAL(pb.Add(tx), PrivateBroadcast::AddResult::Added);
                   +  - ]
     184   [ +  -  -  + ]:       10000 :         txs.push_back(std::move(tx));
     185                 :       10000 :     }
     186   [ +  -  +  -  :           2 :     BOOST_CHECK_EQUAL(pb.GetBroadcastInfo().size(), num_cap);
                   +  - ]
     187                 :             : 
     188                 :             :     // Further distinct transactions are rejected, and the queue is unchanged.
     189         [ +  + ]:           6 :     for (size_t i{0}; i < num_over; ++i) {
     190         [ +  - ]:           5 :         const auto tx{MakeDummyTx(/*id=*/static_cast<uint32_t>(num_cap + i), /*num_witness=*/0)};
     191   [ +  -  +  -  :           5 :         BOOST_CHECK_EQUAL(pb.Add(tx), PrivateBroadcast::AddResult::QueueFull);
             +  -  +  - ]
     192                 :           5 :     }
     193   [ +  -  +  -  :           2 :     BOOST_CHECK_EQUAL(pb.GetBroadcastInfo().size(), num_cap);
                   +  - ]
     194                 :             : 
     195                 :             :     // Nothing was evicted: all originally-added transactions are still present.
     196         [ +  - ]:           1 :     const auto infos{pb.GetBroadcastInfo()};
     197                 :           1 :     std::set<uint256> present_wtxids;
     198         [ +  + ]:       10001 :     for (const auto& info : infos) {
     199         [ +  - ]:       10000 :         present_wtxids.insert(info.tx->GetWitnessHash().ToUint256());
     200                 :             :     }
     201   [ +  -  -  +  :           1 :     BOOST_CHECK_EQUAL(present_wtxids.size(), infos.size());
                   +  - ]
     202         [ +  + ]:       10001 :     for (size_t i{0}; i < num_cap; ++i) {
     203   [ +  -  +  - ]:       20000 :         BOOST_CHECK_MESSAGE(present_wtxids.contains(txs[i]->GetWitnessHash().ToUint256()),
     204                 :             :                             "tx index " << i << " should still be present");
     205                 :             :     }
     206                 :             : 
     207                 :             :     // Re-adding an already-present tx is AlreadyPresent even at the cap (not QueueFull).
     208   [ +  -  +  -  :           1 :     BOOST_CHECK_EQUAL(pb.Add(txs[0]), PrivateBroadcast::AddResult::AlreadyPresent);
                   +  - ]
     209   [ +  -  +  -  :           2 :     BOOST_CHECK_EQUAL(pb.GetBroadcastInfo().size(), num_cap);
                   +  - ]
     210                 :             : 
     211                 :             :     // Removing one frees exactly one slot for a new transaction.
     212   [ +  -  +  -  :           2 :     BOOST_REQUIRE(pb.Remove(txs[0]).has_value());
             +  -  +  - ]
     213   [ +  -  +  -  :           2 :     BOOST_CHECK_EQUAL(pb.GetBroadcastInfo().size(), num_cap - 1);
                   +  - ]
     214         [ +  - ]:           1 :     const auto fresh{MakeDummyTx(/*id=*/0xffffffff, /*num_witness=*/0)};
     215   [ +  -  +  -  :           1 :     BOOST_CHECK_EQUAL(pb.Add(fresh), PrivateBroadcast::AddResult::Added);
                   +  - ]
     216   [ +  -  +  -  :           2 :     BOOST_CHECK_EQUAL(pb.GetBroadcastInfo().size(), num_cap);
                   +  - ]
     217                 :             : 
     218                 :             :     // A previously-removed tx can be added again as a brand-new entry.
     219   [ +  -  +  -  :           2 :     BOOST_REQUIRE(pb.Remove(fresh).has_value());
             +  -  +  - ]
     220   [ +  -  +  -  :           1 :     BOOST_CHECK_EQUAL(pb.Add(fresh), PrivateBroadcast::AddResult::Added);
                   +  - ]
     221   [ +  -  +  -  :           2 :     BOOST_CHECK_EQUAL(pb.GetBroadcastInfo().size(), num_cap);
             +  -  +  - ]
     222                 :           1 : }
     223                 :             : 
     224                 :             : BOOST_AUTO_TEST_SUITE_END()
        

Generated by: LCOV version 2.0-1