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