private_broadcast_tests.cpp
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 <util/time.h> 9 10 #include <algorithm> 11 #include <boost/test/unit_test.hpp> 12 13 BOOST_FIXTURE_TEST_SUITE(private_broadcast_tests, BasicTestingSetup) 14 15 static CTransactionRef MakeDummyTx(uint32_t id, size_t num_witness) 16 { 17 CMutableTransaction mtx; 18 mtx.vin.resize(1); 19 mtx.vin[0].nSequence = id; 20 if (num_witness > 0) { 21 mtx.vin[0].scriptWitness = CScriptWitness{}; 22 mtx.vin[0].scriptWitness.stack.resize(num_witness); 23 } 24 return MakeTransactionRef(mtx); 25 } 26 27 BOOST_AUTO_TEST_CASE(basic) 28 { 29 SetMockTime(Now<NodeSeconds>()); 30 31 PrivateBroadcast pb; 32 const NodeId recipient1{1}; 33 in_addr ipv4Addr; 34 ipv4Addr.s_addr = 0xa0b0c001; 35 const CService addr1{ipv4Addr, 1111}; 36 37 // No transactions initially. 38 BOOST_CHECK(!pb.PickTxForSend(/*will_send_to_nodeid=*/recipient1, /*will_send_to_address=*/addr1).has_value()); 39 BOOST_CHECK_EQUAL(pb.GetStale().size(), 0); 40 BOOST_CHECK(!pb.HavePendingTransactions()); 41 BOOST_CHECK_EQUAL(pb.GetBroadcastInfo().size(), 0); 42 43 // Make a transaction and add it. 44 const auto tx1{MakeDummyTx(/*id=*/1, /*num_witness=*/0)}; 45 46 BOOST_CHECK(pb.Add(tx1)); 47 BOOST_CHECK(!pb.Add(tx1)); 48 49 // Make another transaction with same txid, different wtxid and add it. 50 const auto tx2{MakeDummyTx(/*id=*/1, /*num_witness=*/1)}; 51 BOOST_REQUIRE(tx1->GetHash() == tx2->GetHash()); 52 BOOST_REQUIRE(tx1->GetWitnessHash() != tx2->GetWitnessHash()); 53 54 BOOST_CHECK(pb.Add(tx2)); 55 const auto find_tx_info{[](auto& infos, const CTransactionRef& tx) -> const PrivateBroadcast::TxBroadcastInfo& { 56 const auto it{std::ranges::find(infos, tx->GetWitnessHash(), [](const auto& info) { return info.tx->GetWitnessHash(); })}; 57 BOOST_REQUIRE(it != infos.end()); 58 return *it; 59 }}; 60 const auto check_peer_counts{[&](size_t tx1_peer_count, size_t tx2_peer_count) { 61 const auto infos{pb.GetBroadcastInfo()}; 62 BOOST_CHECK_EQUAL(infos.size(), 2); 63 BOOST_CHECK_EQUAL(find_tx_info(infos, tx1).peers.size(), tx1_peer_count); 64 BOOST_CHECK_EQUAL(find_tx_info(infos, tx2).peers.size(), tx2_peer_count); 65 }}; 66 67 check_peer_counts(/*tx1_peer_count=*/0, /*tx2_peer_count=*/0); 68 69 const auto tx_for_recipient1{pb.PickTxForSend(/*will_send_to_nodeid=*/recipient1, /*will_send_to_address=*/addr1).value()}; 70 BOOST_CHECK(tx_for_recipient1 == tx1 || tx_for_recipient1 == tx2); 71 72 // A second pick must return the other transaction. 73 const NodeId recipient2{2}; 74 const CService addr2{ipv4Addr, 2222}; 75 const auto tx_for_recipient2{pb.PickTxForSend(/*will_send_to_nodeid=*/recipient2, /*will_send_to_address=*/addr2).value()}; 76 BOOST_CHECK(tx_for_recipient2 == tx1 || tx_for_recipient2 == tx2); 77 BOOST_CHECK_NE(tx_for_recipient1, tx_for_recipient2); 78 79 check_peer_counts(/*tx1_peer_count=*/1, /*tx2_peer_count=*/1); 80 81 const NodeId nonexistent_recipient{0}; 82 83 // Confirm transactions <-> recipients mapping is correct. 84 BOOST_CHECK(!pb.GetTxForNode(nonexistent_recipient).has_value()); 85 BOOST_CHECK_EQUAL(pb.GetTxForNode(recipient1).value(), tx_for_recipient1); 86 BOOST_CHECK_EQUAL(pb.GetTxForNode(recipient2).value(), tx_for_recipient2); 87 88 // Confirm none of the transactions' reception have been confirmed. 89 BOOST_CHECK(!pb.DidNodeConfirmReception(recipient1)); 90 BOOST_CHECK(!pb.DidNodeConfirmReception(recipient2)); 91 BOOST_CHECK(!pb.DidNodeConfirmReception(nonexistent_recipient)); 92 93 // 1. Freshly added transactions should NOT be stale yet. 94 BOOST_CHECK_EQUAL(pb.GetStale().size(), 0); 95 96 // 2. Fast-forward the mock clock past the INITIAL_STALE_DURATION. 97 SetMockTime(Now<NodeSeconds>() + PrivateBroadcast::INITIAL_STALE_DURATION + 1min); 98 99 // 3. Now that the initial duration has passed, both unconfirmed transactions should be stale. 100 BOOST_CHECK_EQUAL(pb.GetStale().size(), 2); 101 102 // Confirm reception by recipient1. 103 pb.NodeConfirmedReception(nonexistent_recipient); // Dummy call. 104 pb.NodeConfirmedReception(recipient1); 105 106 BOOST_CHECK(pb.DidNodeConfirmReception(recipient1)); 107 BOOST_CHECK(!pb.DidNodeConfirmReception(recipient2)); 108 109 const auto infos{pb.GetBroadcastInfo()}; 110 BOOST_CHECK_EQUAL(infos.size(), 2); 111 { 112 const auto& peers{find_tx_info(infos, tx_for_recipient1).peers}; 113 BOOST_CHECK_EQUAL(peers.size(), 1); 114 BOOST_CHECK_EQUAL(peers[0].address.ToStringAddrPort(), addr1.ToStringAddrPort()); 115 BOOST_CHECK(peers[0].received.has_value()); 116 } 117 { 118 const auto& peers{find_tx_info(infos, tx_for_recipient2).peers}; 119 BOOST_CHECK_EQUAL(peers.size(), 1); 120 BOOST_CHECK_EQUAL(peers[0].address.ToStringAddrPort(), addr2.ToStringAddrPort()); 121 BOOST_CHECK(!peers[0].received.has_value()); 122 } 123 124 const auto stale_state{pb.GetStale()}; 125 BOOST_CHECK_EQUAL(stale_state.size(), 1); 126 BOOST_CHECK_EQUAL(stale_state[0], tx_for_recipient2); 127 128 SetMockTime(Now<NodeSeconds>() + 10h); 129 130 BOOST_CHECK_EQUAL(pb.GetStale().size(), 2); 131 132 BOOST_CHECK_EQUAL(pb.Remove(tx_for_recipient1).value(), 1); 133 BOOST_CHECK(!pb.Remove(tx_for_recipient1).has_value()); 134 BOOST_CHECK_EQUAL(pb.Remove(tx_for_recipient2).value(), 0); 135 BOOST_CHECK(!pb.Remove(tx_for_recipient2).has_value()); 136 137 BOOST_CHECK_EQUAL(pb.GetBroadcastInfo().size(), 0); 138 const CService addr_nonexistent{ipv4Addr, 3333}; 139 BOOST_CHECK(!pb.PickTxForSend(/*will_send_to_nodeid=*/nonexistent_recipient, /*will_send_to_address=*/addr_nonexistent).has_value()); 140 } 141 142 BOOST_AUTO_TEST_CASE(stale_unpicked_tx) 143 { 144 SetMockTime(Now<NodeSeconds>()); 145 146 PrivateBroadcast pb; 147 const auto tx{MakeDummyTx(/*id=*/42, /*num_witness=*/0)}; 148 BOOST_REQUIRE(pb.Add(tx)); 149 150 // Unpicked transactions use the longer INITIAL_STALE_DURATION. 151 BOOST_CHECK_EQUAL(pb.GetStale().size(), 0); 152 SetMockTime(Now<NodeSeconds>() + PrivateBroadcast::INITIAL_STALE_DURATION - 1min); 153 BOOST_CHECK_EQUAL(pb.GetStale().size(), 0); 154 SetMockTime(Now<NodeSeconds>() + 2min); 155 const auto stale_state{pb.GetStale()}; 156 BOOST_REQUIRE_EQUAL(stale_state.size(), 1); 157 BOOST_CHECK_EQUAL(stale_state[0], tx); 158 } 159 160 BOOST_AUTO_TEST_SUITE_END()