Branch data Line data Source code
1 : : // Copyright (c) 2022-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 <consensus/amount.h>
6 : : #include <consensus/validation.h>
7 : : #include <net_processing.h>
8 : : #include <node/eviction.h>
9 : : #include <node/txorphanage.h>
10 : : #include <policy/policy.h>
11 : : #include <primitives/transaction.h>
12 : : #include <script/script.h>
13 : : #include <sync.h>
14 : : #include <test/fuzz/FuzzedDataProvider.h>
15 : : #include <test/fuzz/fuzz.h>
16 : : #include <test/fuzz/util.h>
17 : : #include <test/util/setup_common.h>
18 : : #include <uint256.h>
19 : : #include <util/check.h>
20 : : #include <util/feefrac.h>
21 : : #include <util/time.h>
22 : :
23 : : #include <algorithm>
24 : : #include <bitset>
25 : : #include <cmath>
26 : : #include <cstdint>
27 : : #include <iostream>
28 : : #include <memory>
29 : : #include <set>
30 : : #include <utility>
31 : : #include <vector>
32 : :
33 : 2 : void initialize_orphanage()
34 : : {
35 [ + - + - ]: 4 : static const auto testing_setup = MakeNoLogFileContext();
36 [ + - ]: 4 : }
37 : :
38 [ + - ]: 1188 : FUZZ_TARGET(txorphan, .init = initialize_orphanage)
39 : : {
40 : 734 : SeedRandomStateForTest(SeedRand::ZEROS);
41 : 734 : FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
42 : 734 : FastRandomContext orphanage_rng{ConsumeUInt256(fuzzed_data_provider)};
43 [ + - ]: 734 : SetMockTime(ConsumeTime(fuzzed_data_provider));
44 : :
45 : 734 : auto orphanage = node::MakeTxOrphanage();
46 : 734 : std::vector<COutPoint> outpoints; // Duplicates are tolerated
47 [ + - ]: 734 : outpoints.reserve(200'000);
48 : :
49 : : // initial outpoints used to construct transactions later
50 [ + + ]: 3670 : for (uint8_t i = 0; i < 4; i++) {
51 [ + - ]: 2936 : outpoints.emplace_back(Txid::FromUint256(uint256{i}), 0);
52 : : }
53 : :
54 : 734 : CTransactionRef ptx_potential_parent = nullptr;
55 : :
56 : 734 : std::vector<CTransactionRef> tx_history;
57 : :
58 [ + - + + : 94490 : LIMITED_WHILE(outpoints.size() < 200'000 && fuzzed_data_provider.ConsumeBool(), 1000)
+ + ]
59 : : {
60 : : // construct transaction
61 : 93022 : const CTransactionRef tx = [&] {
62 : 46511 : CMutableTransaction tx_mut;
63 : 46511 : const auto num_in = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, outpoints.size());
64 : 46511 : const auto num_out = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, 256);
65 : : // pick outpoints from outpoints as input. We allow input duplicates on purpose, given we are not
66 : : // running any transaction validation logic before adding transactions to the orphanage
67 [ + - ]: 46511 : tx_mut.vin.reserve(num_in);
68 [ + + ]: 5873647 : for (uint32_t i = 0; i < num_in; i++) {
69 : 5827136 : auto& prevout = PickValue(fuzzed_data_provider, outpoints);
70 : : // try making transactions unique by setting a random nSequence, but allow duplicate transactions if they happen
71 [ + - ]: 11654272 : tx_mut.vin.emplace_back(prevout, CScript{}, fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(0, CTxIn::SEQUENCE_FINAL));
72 : : }
73 : : // output amount will not affect txorphanage
74 [ + - ]: 46511 : tx_mut.vout.reserve(num_out);
75 [ + + ]: 4736703 : for (uint32_t i = 0; i < num_out; i++) {
76 [ + - ]: 9380384 : tx_mut.vout.emplace_back(CAmount{0}, CScript{});
77 : : }
78 [ + - ]: 46511 : auto new_tx = MakeTransactionRef(tx_mut);
79 : : // add newly constructed outpoints to the coin pool
80 [ + + ]: 4736703 : for (uint32_t i = 0; i < num_out; i++) {
81 [ + - ]: 4690192 : outpoints.emplace_back(new_tx->GetHash(), i);
82 : : }
83 : 46511 : return new_tx;
84 [ + - ]: 93022 : }();
85 : :
86 [ + - ]: 46511 : tx_history.push_back(tx);
87 : :
88 [ + + ]: 46511 : const auto wtxid{tx->GetWitnessHash()};
89 : :
90 : : // Trigger orphanage functions that are called using parents. ptx_potential_parent is a tx we constructed in a
91 : : // previous loop and potentially the parent of this tx.
92 [ + + ]: 46511 : if (ptx_potential_parent) {
93 : : // Set up future GetTxToReconsider call.
94 [ + - ]: 45818 : orphanage->AddChildrenToWorkSet(*ptx_potential_parent, orphanage_rng);
95 : :
96 : : // Check that all txns returned from GetChildrenFrom* are indeed a direct child of this tx.
97 : 45818 : NodeId peer_id = fuzzed_data_provider.ConsumeIntegral<NodeId>();
98 [ + - + + ]: 263781 : for (const auto& child : orphanage->GetChildrenFromSamePeer(ptx_potential_parent, peer_id)) {
99 [ - + ]: 1029306 : assert(std::any_of(child->vin.cbegin(), child->vin.cend(), [&](const auto& input) {
100 : : return input.prevout.hash == ptx_potential_parent->GetHash();
101 : : }));
102 : 45818 : }
103 : : }
104 : :
105 : : // trigger orphanage functions
106 [ + + + + ]: 2712473 : LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 1000)
107 : : {
108 : 2665962 : NodeId peer_id = fuzzed_data_provider.ConsumeIntegral<NodeId>();
109 [ + - ]: 2665962 : const auto total_bytes_start{orphanage->TotalOrphanUsage()};
110 [ + - ]: 2665962 : const auto total_peer_bytes_start{orphanage->UsageByPeer(peer_id)};
111 : 2665962 : const auto tx_weight{GetTransactionWeight(*tx)};
112 : :
113 [ + - ]: 2665962 : CallOneOf(
114 : : fuzzed_data_provider,
115 : 7773 : [&] {
116 : 7773 : {
117 : 7773 : CTransactionRef ref = orphanage->GetTxToReconsider(peer_id);
118 [ + + ]: 7773 : if (ref) {
119 [ + - + - ]: 1642 : Assert(orphanage->HaveTx(ref->GetWitnessHash()));
120 : : }
121 : 7773 : }
122 : 7773 : },
123 : 2498855 : [&] {
124 : 2498855 : bool have_tx = orphanage->HaveTx(tx->GetWitnessHash());
125 : : // AddTx should return false if tx is too big or already have it
126 : : // tx weight is unknown, we only check when tx is already in orphanage
127 : 2498855 : {
128 : 2498855 : bool add_tx = orphanage->AddTx(tx, peer_id);
129 : : // have_tx == true -> add_tx == false
130 : 2498855 : Assert(!have_tx || !add_tx);
131 : :
132 [ + + ]: 2498855 : if (add_tx) {
133 : 52602 : Assert(orphanage->UsageByPeer(peer_id) == tx_weight + total_peer_bytes_start);
134 : 52602 : Assert(orphanage->TotalOrphanUsage() == tx_weight + total_bytes_start);
135 : 52602 : Assert(tx_weight <= MAX_STANDARD_TX_WEIGHT);
136 : : } else {
137 : : // Peer may have been added as an announcer.
138 [ + + ]: 2446253 : if (orphanage->UsageByPeer(peer_id) == tx_weight + total_peer_bytes_start) {
139 : 37226 : Assert(orphanage->HaveTxFromPeer(wtxid, peer_id));
140 : : } else {
141 : : // Otherwise, there must not be any change to the peer byte count.
142 : 2409027 : Assert(orphanage->UsageByPeer(peer_id) == total_peer_bytes_start);
143 : : }
144 : :
145 : : // Regardless, total bytes should not have changed.
146 : 2446253 : Assert(orphanage->TotalOrphanUsage() == total_bytes_start);
147 : : }
148 : : }
149 : 2498855 : have_tx = orphanage->HaveTx(tx->GetWitnessHash());
150 : 2498855 : {
151 : 2498855 : bool add_tx = orphanage->AddTx(tx, peer_id);
152 : : // if have_tx is still false, it must be too big
153 : 2498855 : Assert(!have_tx == (tx_weight > MAX_STANDARD_TX_WEIGHT));
154 : 2498855 : Assert(!have_tx || !add_tx);
155 : : }
156 : 2498855 : },
157 : 52498 : [&] {
158 : 52498 : bool have_tx = orphanage->HaveTx(tx->GetWitnessHash());
159 : 52498 : bool have_tx_and_peer = orphanage->HaveTxFromPeer(tx->GetWitnessHash(), peer_id);
160 : : // AddAnnouncer should return false if tx doesn't exist or we already HaveTxFromPeer.
161 : 52498 : {
162 : 52498 : bool added_announcer = orphanage->AddAnnouncer(tx->GetWitnessHash(), peer_id);
163 : : // have_tx == false -> added_announcer == false
164 : 52498 : Assert(have_tx || !added_announcer);
165 : : // have_tx_and_peer == true -> added_announcer == false
166 : 52498 : Assert(!have_tx_and_peer || !added_announcer);
167 : :
168 : : // Total bytes should not have changed. If peer was added as announcer, byte
169 : : // accounting must have been updated.
170 : 52498 : Assert(orphanage->TotalOrphanUsage() == total_bytes_start);
171 [ + + ]: 52498 : if (added_announcer) {
172 : 3181 : Assert(orphanage->UsageByPeer(peer_id) == tx_weight + total_peer_bytes_start);
173 : : } else {
174 : 49317 : Assert(orphanage->UsageByPeer(peer_id) == total_peer_bytes_start);
175 : : }
176 : : }
177 : 52498 : },
178 : 74788 : [&] {
179 : 74788 : bool have_tx = orphanage->HaveTx(tx->GetWitnessHash());
180 : 74788 : bool have_tx_and_peer{orphanage->HaveTxFromPeer(wtxid, peer_id)};
181 : : // EraseTx should return 0 if m_orphans doesn't have the tx
182 : 74788 : {
183 : 74788 : auto bytes_from_peer_before{orphanage->UsageByPeer(peer_id)};
184 : 74788 : Assert(have_tx == orphanage->EraseTx(tx->GetWitnessHash()));
185 [ + + ]: 74788 : if (have_tx) {
186 : 29105 : Assert(orphanage->TotalOrphanUsage() == total_bytes_start - tx_weight);
187 [ + + ]: 29105 : if (have_tx_and_peer) {
188 : 328 : Assert(orphanage->UsageByPeer(peer_id) == bytes_from_peer_before - tx_weight);
189 : : } else {
190 : 28777 : Assert(orphanage->UsageByPeer(peer_id) == bytes_from_peer_before);
191 : : }
192 : : } else {
193 : 45683 : Assert(orphanage->TotalOrphanUsage() == total_bytes_start);
194 : : }
195 : : }
196 : 74788 : have_tx = orphanage->HaveTx(tx->GetWitnessHash());
197 : 74788 : have_tx_and_peer = orphanage->HaveTxFromPeer(wtxid, peer_id);
198 : : // have_tx should be false and EraseTx should fail
199 : 74788 : {
200 [ + - - + ]: 74788 : Assert(!have_tx && !have_tx_and_peer && !orphanage->EraseTx(wtxid));
201 : : }
202 : 74788 : },
203 : 10991 : [&] {
204 : 10991 : orphanage->EraseForPeer(peer_id);
205 : 10991 : Assert(!orphanage->HaveTxFromPeer(tx->GetWitnessHash(), peer_id));
206 : 10991 : Assert(orphanage->UsageByPeer(peer_id) == 0);
207 : 10991 : },
208 : 12190 : [&] {
209 : : // Make a block out of txs and then EraseForBlock
210 : 12190 : CBlock block;
211 : 12190 : int num_txs = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(0, 1000);
212 [ + + ]: 1297797 : for (int i{0}; i < num_txs; ++i) {
213 : 1285607 : auto& tx_to_remove = PickValue(fuzzed_data_provider, tx_history);
214 [ + - ]: 1285607 : block.vtx.push_back(tx_to_remove);
215 : : }
216 [ + - ]: 12190 : orphanage->EraseForBlock(block);
217 [ + + ]: 1297797 : for (const auto& tx_removed : block.vtx) {
218 [ + - + - ]: 1285607 : Assert(!orphanage->HaveTx(tx_removed->GetWitnessHash()));
219 [ + - + - ]: 1285607 : Assert(!orphanage->HaveTxFromPeer(tx_removed->GetWitnessHash(), peer_id));
220 : : }
221 : 12190 : },
222 : 8867 : [&] {
223 : : // test mocktime and expiry
224 : 8867 : SetMockTime(ConsumeTime(fuzzed_data_provider));
225 : 8867 : orphanage->LimitOrphans();
226 : 8867 : });
227 : :
228 : : }
229 : :
230 : : // Set tx as potential parent to be used for future GetChildren() calls.
231 [ + + + + ]: 92329 : if (!ptx_potential_parent || fuzzed_data_provider.ConsumeBool()) {
232 : 26351 : ptx_potential_parent = tx;
233 : : }
234 : :
235 [ + - ]: 46511 : const bool have_tx{orphanage->HaveTx(tx->GetWitnessHash())};
236 [ + - + + ]: 46511 : const bool get_tx_nonnull{orphanage->GetTx(tx->GetWitnessHash()) != nullptr};
237 [ + - ]: 46511 : Assert(have_tx == get_tx_nonnull);
238 : 46511 : }
239 [ + - ]: 734 : orphanage->SanityCheck();
240 [ + + ]: 1427 : }
241 : :
242 [ + - ]: 773 : FUZZ_TARGET(txorphan_protected, .init = initialize_orphanage)
243 : : {
244 : 319 : SeedRandomStateForTest(SeedRand::ZEROS);
245 : 319 : FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
246 : 319 : FastRandomContext orphanage_rng{ConsumeUInt256(fuzzed_data_provider)};
247 [ + - ]: 319 : SetMockTime(ConsumeTime(fuzzed_data_provider));
248 : :
249 : : // We have num_peers peers. Some subset of them will never exceed their reserved weight or announcement count, and
250 : : // should therefore never have any orphans evicted.
251 : 319 : const unsigned int MAX_PEERS = 125;
252 : 319 : const unsigned int num_peers = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(1, MAX_PEERS);
253 : : // Generate a vector of bools for whether each peer is protected from eviction
254 : 319 : std::bitset<MAX_PEERS> protected_peers;
255 [ + + ]: 6603 : for (unsigned int i = 0; i < num_peers; i++) {
256 [ + - ]: 6284 : protected_peers.set(i, fuzzed_data_provider.ConsumeBool());
257 : : }
258 : :
259 : : // Params for orphanage.
260 : 319 : const unsigned int global_latency_score_limit = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(num_peers, 6'000);
261 : 319 : const int64_t per_peer_weight_reservation = fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(1, 4'040'000);
262 : 319 : auto orphanage = node::MakeTxOrphanage(global_latency_score_limit, per_peer_weight_reservation);
263 : :
264 : : // The actual limit, MaxPeerLatencyScore(), may be higher, since TxOrphanage only counts peers
265 : : // that have announced an orphan. The honest peer will not experience evictions if it never
266 : : // exceeds this.
267 : 319 : const unsigned int honest_latency_limit = global_latency_score_limit / num_peers;
268 : : // Honest peer will not experience evictions if it never exceeds this.
269 : 319 : const int64_t honest_mem_limit = per_peer_weight_reservation;
270 : :
271 : 319 : std::vector<COutPoint> outpoints; // Duplicates are tolerated
272 [ + - ]: 319 : outpoints.reserve(400);
273 : :
274 : : // initial outpoints used to construct transactions later
275 [ + + ]: 1595 : for (uint8_t i = 0; i < 4; i++) {
276 [ + - ]: 1276 : outpoints.emplace_back(Txid::FromUint256(uint256{i}), 0);
277 : : }
278 : :
279 : : // These are honest peer's live announcements. We expect them to be protected from eviction.
280 : 319 : std::set<Wtxid> protected_wtxids;
281 : :
282 [ + + + + : 2135 : LIMITED_WHILE(outpoints.size() < 400 && fuzzed_data_provider.ConsumeBool(), 1000)
- + ]
283 : : {
284 : : // construct transaction
285 : 1498 : const CTransactionRef tx = [&] {
286 : 749 : CMutableTransaction tx_mut;
287 : 749 : const auto num_in = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, outpoints.size());
288 : 749 : const auto num_out = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, 256);
289 : : // pick outpoints from outpoints as input. We allow input duplicates on purpose, given we are not
290 : : // running any transaction validation logic before adding transactions to the orphanage
291 [ + - ]: 749 : tx_mut.vin.reserve(num_in);
292 [ + + ]: 3204 : for (uint32_t i = 0; i < num_in; i++) {
293 : 2455 : auto& prevout = PickValue(fuzzed_data_provider, outpoints);
294 : : // try making transactions unique by setting a random nSequence, but allow duplicate transactions if they happen
295 [ + - ]: 4910 : tx_mut.vin.emplace_back(prevout, CScript{}, fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(0, CTxIn::SEQUENCE_FINAL));
296 : : }
297 : : // output amount or spendability will not affect txorphanage
298 [ + - ]: 749 : tx_mut.vout.reserve(num_out);
299 [ + + ]: 15880 : for (uint32_t i = 0; i < num_out; i++) {
300 : 15131 : const auto payload_size = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(0, 100000);
301 [ + + ]: 15131 : if (payload_size) {
302 [ + - + - : 10108 : tx_mut.vout.emplace_back(0, CScript() << OP_RETURN << std::vector<unsigned char>(payload_size));
+ - ]
303 : : } else {
304 [ + - ]: 20154 : tx_mut.vout.emplace_back(0, CScript{});
305 : : }
306 : : }
307 [ + - ]: 749 : auto new_tx = MakeTransactionRef(tx_mut);
308 : : // add newly constructed outpoints to the coin pool
309 [ + + ]: 15880 : for (uint32_t i = 0; i < num_out; i++) {
310 [ + - ]: 15131 : outpoints.emplace_back(new_tx->GetHash(), i);
311 : : }
312 : 749 : return new_tx;
313 [ + - ]: 1498 : }();
314 : :
315 : 749 : const auto wtxid{tx->GetWitnessHash()};
316 : :
317 : : // orphanage functions
318 [ + + + + ]: 32760 : LIMITED_WHILE(fuzzed_data_provider.remaining_bytes(), 10 * global_latency_score_limit)
319 : : {
320 : 32011 : NodeId peer_id = fuzzed_data_provider.ConsumeIntegralInRange<NodeId>(0, num_peers - 1);
321 : 32011 : const auto tx_weight{GetTransactionWeight(*tx)};
322 : :
323 : : // This protected peer will never send orphans that would
324 : : // exceed their own personal allotment, so is never evicted.
325 [ + - ]: 32011 : const bool peer_is_protected{protected_peers[peer_id]};
326 : :
327 [ + - ]: 32011 : CallOneOf(
328 : : fuzzed_data_provider,
329 : 15554 : [&] { // AddTx
330 : 15554 : bool have_tx_and_peer = orphanage->HaveTxFromPeer(wtxid, peer_id);
331 [ + + + + ]: 15554 : if (peer_is_protected && !have_tx_and_peer &&
332 [ + + + + ]: 3722 : (orphanage->UsageByPeer(peer_id) + tx_weight > honest_mem_limit ||
333 [ + + ]: 1652 : orphanage->LatencyScoreFromPeer(peer_id) + (tx->vin.size() / 10) + 1 > honest_latency_limit)) {
334 : : // We never want our protected peer oversized or over-announced
335 : : } else {
336 : 15034 : orphanage->AddTx(tx, peer_id);
337 [ + + + + ]: 15034 : if (peer_is_protected && orphanage->HaveTxFromPeer(wtxid, peer_id)) {
338 : 4380 : protected_wtxids.insert(wtxid);
339 : : }
340 : : }
341 : 15554 : },
342 : 5642 : [&] { // AddAnnouncer
343 : 5642 : bool have_tx_and_peer = orphanage->HaveTxFromPeer(tx->GetWitnessHash(), peer_id);
344 : : // AddAnnouncer should return false if tx doesn't exist or we already HaveTxFromPeer.
345 : 5642 : {
346 [ + + + + ]: 5642 : if (peer_is_protected && !have_tx_and_peer &&
347 [ + + + + ]: 2452 : (orphanage->UsageByPeer(peer_id) + tx_weight > honest_mem_limit ||
348 [ + + ]: 1082 : orphanage->LatencyScoreFromPeer(peer_id) + (tx->vin.size()) + 1 > honest_latency_limit)) {
349 : : // We never want our protected peer oversized
350 : : } else {
351 : 5041 : orphanage->AddAnnouncer(tx->GetWitnessHash(), peer_id);
352 [ + + + + ]: 5041 : if (peer_is_protected && orphanage->HaveTxFromPeer(wtxid, peer_id)) {
353 : 1078 : protected_wtxids.insert(wtxid);
354 : : }
355 : : }
356 : : }
357 : 5642 : },
358 : 3223 : [&] { // EraseTx
359 [ + + ]: 3223 : if (protected_wtxids.count(tx->GetWitnessHash())) {
360 : 720 : protected_wtxids.erase(wtxid);
361 : : }
362 : 3223 : orphanage->EraseTx(wtxid);
363 : 3223 : Assert(!orphanage->HaveTx(wtxid));
364 : 3223 : },
365 : 4747 : [&] { // EraseForPeer
366 [ + + ]: 4747 : if (!protected_peers[peer_id]) {
367 : 3517 : orphanage->EraseForPeer(peer_id);
368 : 3517 : Assert(orphanage->UsageByPeer(peer_id) == 0);
369 : 3517 : Assert(orphanage->LatencyScoreFromPeer(peer_id) == 0);
370 : 3517 : Assert(orphanage->AnnouncementsFromPeer(peer_id) == 0);
371 : : }
372 : 4747 : },
373 : 2845 : [&] { // LimitOrphans
374 : : // Assert that protected peers are never affected by LimitOrphans.
375 : 2845 : unsigned int protected_count = 0;
376 : 2845 : unsigned int protected_bytes = 0;
377 [ + + ]: 57813 : for (unsigned int peer = 0; peer < num_peers; ++peer) {
378 [ + + ]: 54968 : if (protected_peers[peer]) {
379 : 27328 : protected_count += orphanage->LatencyScoreFromPeer(peer);
380 : 27328 : protected_bytes += orphanage->UsageByPeer(peer);
381 : : }
382 : : }
383 : 2845 : orphanage->LimitOrphans();
384 : 2845 : Assert(orphanage->TotalLatencyScore() <= global_latency_score_limit);
385 : 2845 : Assert(orphanage->TotalOrphanUsage() <= per_peer_weight_reservation * num_peers);
386 : :
387 : : // Number of announcements and usage should never differ before and after since
388 : : // we've never exceeded the per-peer reservations.
389 [ + + ]: 57813 : for (unsigned int peer = 0; peer < num_peers; ++peer) {
390 [ + + ]: 54968 : if (protected_peers[peer]) {
391 : 27328 : protected_count -= orphanage->LatencyScoreFromPeer(peer);
392 : 27328 : protected_bytes -= orphanage->UsageByPeer(peer);
393 : : }
394 : : }
395 : 2845 : Assert(protected_count == 0);
396 : 2845 : Assert(protected_bytes == 0);
397 : 2845 : });
398 : :
399 : : }
400 : 749 : }
401 : :
402 [ + - ]: 319 : orphanage->SanityCheck();
403 : : // All of the honest peer's announcements are still present.
404 [ + + ]: 451 : for (const auto& wtxid : protected_wtxids) {
405 [ + - + - ]: 132 : Assert(orphanage->HaveTx(wtxid));
406 : : }
407 : 319 : }
408 : :
409 [ + - ]: 965 : FUZZ_TARGET(txorphanage_sim)
410 : : {
411 : 511 : SeedRandomStateForTest(SeedRand::ZEROS);
412 : : // This is a comphehensive simulation fuzz test, which runs through a scenario involving up to
413 : : // 16 transactions (which may have simple or complex topology, and may have duplicate txids
414 : : // with distinct wtxids, and up to 16 peers. The scenario is performed both on a real
415 : : // TxOrphanage object and the behavior is compared with a naive reimplementation (just a vector
416 : : // of announcements) where possible, and tested for desired properties where not possible.
417 : :
418 : : //
419 : : // 1. Setup.
420 : : //
421 : :
422 : : /** The total number of transactions this simulation uses (not all of which will necessarily
423 : : * be present in the orphanage at once). */
424 : 511 : static constexpr unsigned NUM_TX = 16;
425 : : /** The number of peers this simulation uses (not all of which will necessarily be present in
426 : : * the orphanage at once). */
427 : 511 : static constexpr unsigned NUM_PEERS = 16;
428 : : /** The maximum number of announcements this simulation uses (which may be higher than the
429 : : * number permitted inside the orphanage). */
430 : 511 : static constexpr unsigned MAX_ANN = 64;
431 : :
432 : 511 : FuzzedDataProvider provider(buffer.data(), buffer.size());
433 : : /** Local RNG. Only used for topology/sizes of the transaction set, the order of transactions
434 : : * in EraseForBlock, and for the randomized passed to AddChildrenToWorkSet. */
435 : 511 : InsecureRandomContext rng(provider.ConsumeIntegral<uint64_t>());
436 : :
437 : : //
438 : : // 2. Construct an interesting set of 16 transactions.
439 : : //
440 : :
441 : : // - Pick a topological order among the transactions.
442 : 511 : std::vector<unsigned> txorder(NUM_TX);
443 : 511 : std::iota(txorder.begin(), txorder.end(), unsigned{0});
444 : 511 : std::shuffle(txorder.begin(), txorder.end(), rng);
445 : : // - Pick a set of dependencies (pair<child_index, parent_index>).
446 : 511 : std::vector<std::pair<unsigned, unsigned>> deps;
447 [ + - ]: 511 : deps.reserve((NUM_TX * (NUM_TX - 1)) / 2);
448 [ + + ]: 8176 : for (unsigned p = 0; p < NUM_TX - 1; ++p) {
449 [ + + ]: 68985 : for (unsigned c = p + 1; c < NUM_TX; ++c) {
450 [ + - ]: 61320 : deps.emplace_back(c, p);
451 : : }
452 : : }
453 : 511 : std::shuffle(deps.begin(), deps.end(), rng);
454 [ + - ]: 511 : deps.resize(provider.ConsumeIntegralInRange<unsigned>(0, NUM_TX * 4 - 1));
455 : : // - Construct the actual transactions.
456 [ + - ]: 511 : std::set<Wtxid> wtxids;
457 [ + - ]: 511 : std::vector<CTransactionRef> txn(NUM_TX);
458 : : node::TxOrphanage::Usage total_usage{0};
459 [ + + ]: 8687 : for (unsigned t = 0; t < NUM_TX; ++t) {
460 [ + - ]: 8176 : CMutableTransaction tx;
461 [ + + + + ]: 8176 : if (t > 0 && rng.randrange(4) == 0) {
462 : : // Occasionally duplicate the previous transaction, so that repetitions of the same
463 : : // txid are possible (with different wtxid).
464 [ + - ]: 6126 : tx = CMutableTransaction(*txn[txorder[t - 1]]);
465 : : } else {
466 : 5113 : tx.version = 1;
467 : 5113 : tx.nLockTime = 0xffffffff;
468 : : // Construct 1 to 16 outputs.
469 : 5113 : auto num_outputs = rng.randrange<unsigned>(1 << rng.randrange<unsigned>(5)) + 1;
470 [ + + ]: 21903 : for (unsigned output = 0; output < num_outputs; ++output) {
471 : 16790 : CScript scriptpubkey;
472 : 16790 : scriptpubkey.resize(provider.ConsumeIntegralInRange<unsigned>(20, 34));
473 [ + - ]: 16790 : tx.vout.emplace_back(CAmount{0}, std::move(scriptpubkey));
474 : 16790 : }
475 : : // Construct inputs (one for each dependency).
476 [ + + + + ]: 153448 : for (auto& [child, parent] : deps) {
477 [ + + ]: 148335 : if (child == t) {
478 [ - + ]: 8998 : auto& partx = txn[txorder[parent]];
479 [ - + ]: 8998 : assert(partx->version == 1);
480 [ + - ]: 8998 : COutPoint outpoint(partx->GetHash(), rng.randrange<size_t>(partx->vout.size()));
481 [ + - ]: 8998 : tx.vin.emplace_back(outpoint);
482 : 8998 : tx.vin.back().scriptSig.resize(provider.ConsumeIntegralInRange<unsigned>(16, 200));
483 : : }
484 : : }
485 : : // Construct fallback input in case there are no dependencies.
486 [ + + ]: 5113 : if (tx.vin.empty()) {
487 [ + - ]: 2423 : COutPoint outpoint(Txid::FromUint256(rng.rand256()), rng.randrange<size_t>(16));
488 [ + - ]: 2423 : tx.vin.emplace_back(outpoint);
489 : 2423 : tx.vin.back().scriptSig.resize(provider.ConsumeIntegralInRange<unsigned>(16, 200));
490 : : }
491 : : }
492 : : // Optionally modify the witness (allowing wtxid != txid), and certainly when the wtxid
493 : : // already exists.
494 [ + - + + : 37124 : while (wtxids.contains(CTransaction(tx).GetWitnessHash()) || rng.randrange(4) == 0) {
+ + + + ]
495 : 10386 : auto& input = tx.vin[rng.randrange(tx.vin.size())];
496 [ + + ]: 10386 : if (rng.randbool()) {
497 [ + - ]: 4633 : input.scriptWitness.stack.resize(1);
498 [ + - ]: 4633 : input.scriptWitness.stack[0].resize(rng.randrange(100));
499 : : } else {
500 [ + - ]: 5753 : input.scriptWitness.stack.resize(0);
501 : : }
502 : : }
503 : : // Convert to CTransactionRef.
504 [ + - - + ]: 16352 : txn[txorder[t]] = MakeTransactionRef(std::move(tx));
505 [ + - ]: 8176 : wtxids.insert(txn[txorder[t]]->GetWitnessHash());
506 : 8176 : auto weight = GetTransactionWeight(*txn[txorder[t]]);
507 [ - + ]: 8176 : assert(weight < MAX_STANDARD_TX_WEIGHT);
508 : 8176 : total_usage += GetTransactionWeight(*txn[txorder[t]]);
509 : 8176 : }
510 : :
511 : : //
512 : : // 3. Initialize real orphanage
513 : : //
514 : :
515 : 511 : auto max_global_ann = provider.ConsumeIntegralInRange<node::TxOrphanage::Count>(NUM_PEERS, MAX_ANN);
516 : 511 : auto reserved_peer_usage = provider.ConsumeIntegralInRange<node::TxOrphanage::Usage>(1, total_usage);
517 : 511 : auto real = node::MakeTxOrphanage(max_global_ann, reserved_peer_usage);
518 : :
519 : : //
520 : : // 4. Functions and data structures for the simulation.
521 : : //
522 : :
523 : : /** Data structure representing one announcement (pair of (tx, peer), plus whether it's
524 : : * reconsiderable or not. */
525 : 511 : struct SimAnnouncement
526 : : {
527 : : unsigned tx;
528 : : NodeId announcer;
529 : : bool reconsider{false};
530 : 21549 : SimAnnouncement(unsigned tx_in, NodeId announcer_in, bool reconsider_in) noexcept :
531 : 21549 : tx(tx_in), announcer(announcer_in), reconsider(reconsider_in) {}
532 : : };
533 : : /** The entire simulated orphanage is represented by this list of announcements, in
534 : : * announcement order (unlike TxOrphanageImpl which uses a sequence number to represent
535 : : * announcement order). New announcements are added to the back. */
536 : 511 : std::vector<SimAnnouncement> sim_announcements;
537 : :
538 : : /** Consume a transaction (index into txn) from provider. */
539 : 9619 : auto read_tx_fn = [&]() -> unsigned { return provider.ConsumeIntegralInRange<unsigned>(0, NUM_TX - 1); };
540 : : /** Consume a NodeId from provider. */
541 : 15979 : auto read_peer_fn = [&]() -> NodeId { return provider.ConsumeIntegralInRange<unsigned>(0, NUM_PEERS - 1); };
542 : : /** Consume both a transaction (index into txn) and a NodeId from provider. */
543 : 35549 : auto read_tx_peer_fn = [&]() -> std::pair<unsigned, NodeId> {
544 : 35038 : auto code = provider.ConsumeIntegralInRange<unsigned>(0, NUM_TX * NUM_PEERS - 1);
545 : 35038 : return {code % NUM_TX, code / NUM_TX};
546 : 511 : };
547 : : /** Determine if we have any announcements of the given transaction in the simulation. */
548 : 342617 : auto have_tx_fn = [&](unsigned tx) -> bool {
549 [ + + ]: 2145745 : for (auto& ann : sim_announcements) {
550 [ + + ]: 1919961 : if (ann.tx == tx) return true;
551 : : }
552 : : return false;
553 : 511 : };
554 : : /** Count the number of peers in the simulation. */
555 : 21377 : auto count_peers_fn = [&]() -> unsigned {
556 : 20866 : std::bitset<NUM_PEERS> mask;
557 [ + + ]: 190987 : for (auto& ann : sim_announcements) {
558 : 170121 : mask.set(ann.announcer);
559 : : }
560 : 20866 : return mask.count();
561 : 511 : };
562 : : /** Determine if we have any reconsiderable announcements of a given transaction. */
563 : 15705 : auto have_reconsiderable_fn = [&](unsigned tx) -> bool {
564 [ + + ]: 136333 : for (auto& ann : sim_announcements) {
565 [ + + + + ]: 132352 : if (ann.reconsider && ann.tx == tx) return true;
566 : : }
567 : : return false;
568 : 511 : };
569 : : /** Determine if a peer has any transactions to reconsider. */
570 : 11976 : auto have_reconsider_fn = [&](NodeId peer) -> bool {
571 [ + + ]: 86957 : for (auto& ann : sim_announcements) {
572 [ + + + + ]: 76182 : if (ann.reconsider && ann.announcer == peer) return true;
573 : : }
574 : : return false;
575 : 511 : };
576 : : /** Get an iterator to an existing (wtxid, peer) pair in the simulation. */
577 : 6734 : auto find_announce_wtxid_fn = [&](const Wtxid& wtxid, NodeId peer) -> std::vector<SimAnnouncement>::iterator {
578 [ + - ]: 51115 : for (auto it = sim_announcements.begin(); it != sim_announcements.end(); ++it) {
579 [ + + + + ]: 51115 : if (txn[it->tx]->GetWitnessHash() == wtxid && it->announcer == peer) return it;
580 : : }
581 : 0 : return sim_announcements.end();
582 : 511 : };
583 : : /** Get an iterator to an existing (tx, peer) pair in the simulation. */
584 : 166365 : auto find_announce_fn = [&](unsigned tx, NodeId peer) {
585 [ + + ]: 1397409 : for (auto it = sim_announcements.begin(); it != sim_announcements.end(); ++it) {
586 [ + + + + ]: 1246479 : if (it->tx == tx && it->announcer == peer) return it;
587 : : }
588 : 150930 : return sim_announcements.end();
589 : 511 : };
590 : : /** Compute a peer's DoS score according to simulation data. */
591 : 94799 : auto dos_score_fn = [&](NodeId peer, int32_t max_count, int32_t max_usage) -> FeeFrac {
592 : 94288 : int64_t count{0};
593 : 94288 : int64_t usage{0};
594 [ + + ]: 857760 : for (auto& ann : sim_announcements) {
595 [ + + ]: 763472 : if (ann.announcer != peer) continue;
596 : 47717 : count += 1 + (txn[ann.tx]->vin.size() / 10);
597 : 47717 : usage += GetTransactionWeight(*txn[ann.tx]);
598 : : }
599 : 94288 : return std::max(FeeFrac{count, max_count}, FeeFrac{usage, max_usage});
600 : 511 : };
601 : :
602 : : //
603 : : // 5. Run through a scenario of mutators on both real and simulated orphanage.
604 : : //
605 : :
606 [ + + + + ]: 62966 : LIMITED_WHILE(provider.remaining_bytes() > 0, 200) {
607 : 62455 : int command = provider.ConsumeIntegralInRange<uint8_t>(0, 15);
608 : 87564 : while (true) {
609 [ + + + + ]: 87564 : if (sim_announcements.size() < MAX_ANN && command-- == 0) {
610 : : // AddTx
611 [ + - ]: 29856 : auto [tx, peer] = read_tx_peer_fn();
612 [ + - ]: 29856 : bool added = real->AddTx(txn[tx], peer);
613 : 29856 : bool sim_have_tx = have_tx_fn(tx);
614 [ - + ]: 29856 : assert(added == !sim_have_tx);
615 [ + + ]: 29856 : if (find_announce_fn(tx, peer) == sim_announcements.end()) {
616 [ + - ]: 20444 : sim_announcements.emplace_back(tx, peer, false);
617 : : }
618 : : break;
619 [ + + + + ]: 57708 : } else if (sim_announcements.size() < MAX_ANN && command-- == 0) {
620 : : // AddAnnouncer
621 [ + - ]: 5182 : auto [tx, peer] = read_tx_peer_fn();
622 [ + - ]: 5182 : bool added = real->AddAnnouncer(txn[tx]->GetWitnessHash(), peer);
623 : 5182 : bool sim_have_tx = have_tx_fn(tx);
624 : 5182 : auto sim_it = find_announce_fn(tx, peer);
625 [ + + + + : 9259 : assert(added == (sim_it == sim_announcements.end() && sim_have_tx));
- + ]
626 [ + + ]: 5182 : if (added) {
627 [ + - ]: 1105 : sim_announcements.emplace_back(tx, peer, false);
628 : : }
629 : : break;
630 [ + + + + : 52526 : } else if (command-- == 0) {
+ + ]
631 : : // EraseTx
632 : 3788 : auto tx = read_tx_fn();
633 [ + - ]: 3788 : bool erased = real->EraseTx(txn[tx]->GetWitnessHash());
634 : 3788 : bool sim_have = have_tx_fn(tx);
635 [ - + ]: 3788 : assert(erased == sim_have);
636 [ + + + + : 16523 : std::erase_if(sim_announcements, [&](auto& ann) { return ann.tx == tx; });
+ + + + +
+ + + + +
+ + ]
637 : : break;
638 : : } else if (command-- == 0) {
639 : : // EraseForPeer
640 : 3084 : auto peer = read_peer_fn();
641 [ + - ]: 3084 : real->EraseForPeer(peer);
642 [ + + + + : 11633 : std::erase_if(sim_announcements, [&](auto& ann) { return ann.announcer == peer; });
+ + + + +
+ + + + +
+ + ]
643 : : break;
644 : : } else if (command-- == 0) {
645 : : // EraseForBlock
646 : 3344 : auto pattern = provider.ConsumeIntegralInRange<uint64_t>(0, (uint64_t{1} << NUM_TX) - 1);
647 : 3344 : CBlock block;
648 : 3344 : std::set<COutPoint> spent;
649 [ + + ]: 56848 : for (unsigned tx = 0; tx < NUM_TX; ++tx) {
650 [ + + ]: 53504 : if ((pattern >> tx) & 1) {
651 [ + - ]: 16999 : block.vtx.emplace_back(txn[tx]);
652 [ + + ]: 53641 : for (auto& txin : block.vtx.back()->vin) {
653 [ + - ]: 36642 : spent.insert(txin.prevout);
654 : : }
655 : : }
656 : : }
657 : 3344 : std::shuffle(block.vtx.begin(), block.vtx.end(), rng);
658 [ + - ]: 3344 : real->EraseForBlock(block);
659 : 18979 : std::erase_if(sim_announcements, [&](auto& ann) {
660 [ + + ]: 30517 : for (auto& txin : txn[ann.tx]->vin) {
661 [ + + ]: 22895 : if (spent.count(txin.prevout)) return true;
662 : : }
663 : : return false;
664 : : });
665 : 3344 : break;
666 : 3344 : } else if (command-- == 0) {
667 : : // AddChildrenToWorkSet
668 : 5320 : auto tx = read_tx_fn();
669 : 5320 : FastRandomContext rand_ctx(rng.rand256());
670 [ + - ]: 5320 : auto added = real->AddChildrenToWorkSet(*txn[tx], rand_ctx);
671 : : /** Map of all child wtxids, with value whether they already have a reconsiderable
672 : : announcement from some peer. */
673 : 5320 : std::map<Wtxid, bool> child_wtxids;
674 [ + + ]: 90440 : for (unsigned child_tx = 0; child_tx < NUM_TX; ++child_tx) {
675 [ + + ]: 85120 : if (!have_tx_fn(child_tx)) continue;
676 : 34378 : bool child_of = false;
677 [ + + ]: 87956 : for (auto& txin : txn[child_tx]->vin) {
678 [ + + ]: 68772 : if (txin.prevout.hash == txn[tx]->GetHash()) {
679 : : child_of = true;
680 : : break;
681 : : }
682 : : }
683 [ + + ]: 34378 : if (child_of) {
684 [ + - ]: 15194 : child_wtxids[txn[child_tx]->GetWitnessHash()] = have_reconsiderable_fn(child_tx);
685 : : }
686 : : }
687 [ + + ]: 10182 : for (auto& [wtxid, peer] : added) {
688 : : // Wtxid must be a child of tx.
689 : 4862 : auto child_wtxid_it = child_wtxids.find(wtxid);
690 [ - + ]: 4862 : assert(child_wtxid_it != child_wtxids.end());
691 : : // Announcement must exist.
692 : 4862 : auto sim_ann_it = find_announce_wtxid_fn(wtxid, peer);
693 [ - + ]: 4862 : assert(sim_ann_it != sim_announcements.end());
694 : : // Announcement must not yet be reconsiderable.
695 [ - + ]: 4862 : assert(sim_ann_it->reconsider == false);
696 : : // Make reconsiderable.
697 : 4862 : sim_ann_it->reconsider = true;
698 : : }
699 [ + + ]: 10182 : for (auto& [wtxid, peer] : added) {
700 : : // Remove from child_wtxids map, so we can check that only already-reconsiderable
701 : : // ones are missing from the result.
702 : 4862 : child_wtxids.erase(wtxid);
703 : : }
704 : : // Verify that AddChildrenToWorkSet does not select announcements that were already reconsiderable:
705 : : // Check all child wtxids which did not occur at least once in the result were already reconsiderable
706 : : // due to a previous AddChildrenToWorkSet.
707 [ - + + + ]: 15848 : for (auto& [wtxid, already_reconsider] : child_wtxids) {
708 [ - + ]: 10528 : assert(already_reconsider);
709 : : }
710 : 5320 : break;
711 : 5320 : } else if (command-- == 0) {
712 : : // GetTxToReconsider.
713 : 4650 : auto peer = read_peer_fn();
714 [ + - ]: 4650 : auto result = real->GetTxToReconsider(peer);
715 [ + + ]: 4650 : if (result) {
716 : : // A transaction was found. It must have a corresponding reconsiderable
717 : : // announcement from peer.
718 : 1361 : auto sim_ann_it = find_announce_wtxid_fn(result->GetWitnessHash(), peer);
719 [ - + ]: 1361 : assert(sim_ann_it != sim_announcements.end());
720 [ - + ]: 1361 : assert(sim_ann_it->announcer == peer);
721 [ - + ]: 1361 : assert(sim_ann_it->reconsider);
722 : : // Make it non-reconsiderable.
723 : 1361 : sim_ann_it->reconsider = false;
724 : : } else {
725 : : // No reconsiderable transaction was found from peer. Verify that it does not
726 : : // have any.
727 [ - + ]: 3289 : assert(!have_reconsider_fn(peer));
728 : : }
729 [ + + ]: 4650 : break;
730 [ + + ]: 36990 : } else if (command-- == 0) {
731 : : // LimitOrphans
732 [ + - + + ]: 7231 : const auto max_ann = max_global_ann / std::max<unsigned>(1, count_peers_fn());
733 : 7231 : const auto max_mem = reserved_peer_usage;
734 : 13124 : while (true) {
735 : : // Count global usage and number of peers.
736 : 13124 : node::TxOrphanage::Usage total_usage{0};
737 : 13124 : node::TxOrphanage::Count total_latency_score = sim_announcements.size();
738 [ + + ]: 223108 : for (unsigned tx = 0; tx < NUM_TX; ++tx) {
739 [ + + ]: 209984 : if (have_tx_fn(tx)) {
740 : 60111 : total_usage += GetTransactionWeight(*txn[tx]);
741 : 60111 : total_latency_score += txn[tx]->vin.size() / 10;
742 : : }
743 : : }
744 [ + - ]: 13124 : auto num_peers = count_peers_fn();
745 [ + + + + ]: 21379 : bool oversized = (total_usage > reserved_peer_usage * num_peers) ||
746 [ + - ]: 8255 : (total_latency_score > real->MaxGlobalLatencyScore());
747 : 13124 : if (!oversized) break;
748 : : // Find worst peer.
749 : 5893 : FeeFrac worst_dos_score{0, 1};
750 : 5893 : unsigned worst_peer = unsigned(-1);
751 [ + + ]: 100181 : for (unsigned peer = 0; peer < NUM_PEERS; ++peer) {
752 : 94288 : auto dos_score = dos_score_fn(peer, max_ann, max_mem);
753 : : // Use >= so that the more recent peer (higher NodeId) wins in case of
754 : : // ties.
755 [ + + ]: 94288 : if (dos_score >= worst_dos_score) {
756 : 14659 : worst_dos_score = dos_score;
757 : 14659 : worst_peer = peer;
758 : : }
759 : : }
760 [ - + ]: 5893 : assert(worst_peer != unsigned(-1));
761 [ - + ]: 5893 : assert(worst_dos_score >> FeeFrac(1, 1));
762 : : // Find oldest announcement from worst_peer, preferring non-reconsiderable ones.
763 : : bool done{false};
764 [ + - ]: 6137 : for (int reconsider = 0; reconsider < 2; ++reconsider) {
765 [ + + ]: 31551 : for (auto it = sim_announcements.begin(); it != sim_announcements.end(); ++it) {
766 [ + + + + ]: 31307 : if (it->announcer != worst_peer || it->reconsider != reconsider) continue;
767 : 5893 : sim_announcements.erase(it);
768 : 5893 : done = true;
769 : 5893 : break;
770 : : }
771 : 6137 : if (done) break;
772 : : }
773 [ + - ]: 5893 : assert(done);
774 : : }
775 [ + - ]: 7231 : real->LimitOrphans();
776 : : // We must now be within limits, otherwise LimitOrphans should have continued further).
777 : : // We don't check the contents of the orphanage until the end to make fuzz runs faster.
778 [ + - + - : 7231 : assert(real->TotalLatencyScore() <= real->MaxGlobalLatencyScore());
- + ]
779 [ + - + - : 7231 : assert(real->TotalOrphanUsage() <= real->MaxGlobalUsage());
- + ]
780 : : break;
781 : : }
782 : : }
783 : : }
784 : :
785 : : //
786 : : // 6. Perform a full comparison between the real orphanage's inspectors and the simulation.
787 : : //
788 : :
789 [ + - ]: 511 : real->SanityCheck();
790 : :
791 : :
792 [ + - ]: 511 : auto all_orphans = real->GetOrphanTransactions();
793 : 511 : node::TxOrphanage::Usage orphan_usage{0};
794 [ + - ]: 511 : std::vector<node::TxOrphanage::Usage> usage_by_peer(NUM_PEERS);
795 : 511 : node::TxOrphanage::Count unique_orphans{0};
796 [ + - ]: 511 : std::vector<node::TxOrphanage::Count> count_by_peer(NUM_PEERS);
797 : 511 : node::TxOrphanage::Count total_latency_score = sim_announcements.size();
798 [ + + ]: 8687 : for (unsigned tx = 0; tx < NUM_TX; ++tx) {
799 : 8176 : bool sim_have_tx = have_tx_fn(tx);
800 [ + + ]: 8176 : if (sim_have_tx) {
801 : 2088 : orphan_usage += GetTransactionWeight(*txn[tx]);
802 : 2088 : total_latency_score += txn[tx]->vin.size() / 10;
803 : : }
804 : 8176 : unique_orphans += sim_have_tx;
805 [ - + ]: 33076 : auto orphans_it = std::find_if(all_orphans.begin(), all_orphans.end(), [&](auto& orph) { return orph.tx->GetWitnessHash() == txn[tx]->GetWitnessHash(); });
806 : : // GetOrphanTransactions (OrphanBase existence)
807 [ - + ]: 8176 : assert((orphans_it != all_orphans.end()) == sim_have_tx);
808 : : // HaveTx
809 [ + - ]: 8176 : bool have_tx = real->HaveTx(txn[tx]->GetWitnessHash());
810 [ - + ]: 8176 : assert(have_tx == sim_have_tx);
811 : : // GetTx
812 [ + - ]: 8176 : auto txref = real->GetTx(txn[tx]->GetWitnessHash());
813 [ - + ]: 8176 : assert(!!txref == sim_have_tx);
814 [ + + - + ]: 8176 : if (sim_have_tx) assert(txref->GetWitnessHash() == txn[tx]->GetWitnessHash());
815 : :
816 [ + + ]: 138992 : for (NodeId peer = 0; peer < NUM_PEERS; ++peer) {
817 : 130816 : auto it_sim_ann = find_announce_fn(tx, peer);
818 [ + + ]: 130816 : bool sim_have_ann = it_sim_ann != sim_announcements.end();
819 [ + + ]: 130816 : if (sim_have_ann) usage_by_peer[peer] += GetTransactionWeight(*txn[tx]);
820 [ + + ]: 130816 : count_by_peer[peer] += sim_have_ann;
821 : : // GetOrphanTransactions (announcers presence)
822 [ + + - + ]: 130816 : if (sim_have_ann) assert(sim_have_tx);
823 [ + + - + ]: 130816 : if (sim_have_tx) assert(orphans_it->announcers.count(peer) == sim_have_ann);
824 : : // HaveTxFromPeer
825 [ + - ]: 130816 : bool have_ann = real->HaveTxFromPeer(txn[tx]->GetWitnessHash(), peer);
826 [ - + ]: 130816 : assert(sim_have_ann == have_ann);
827 : : // GetChildrenFromSamePeer
828 [ + - ]: 130816 : auto children_from_peer = real->GetChildrenFromSamePeer(txn[tx], peer);
829 : 130816 : auto it = children_from_peer.rbegin();
830 [ + + ]: 392448 : for (int phase = 0; phase < 2; ++phase) {
831 : : // First expect all children which have reconsiderable announcement from peer, then the others.
832 [ + + ]: 2307072 : for (auto& ann : sim_announcements) {
833 [ + + ]: 2045440 : if (ann.announcer != peer) continue;
834 [ + + ]: 127840 : if (ann.reconsider != (phase == 1)) continue;
835 : 63920 : bool matching_parent{false};
836 [ + + ]: 198752 : for (const auto& vin : txn[ann.tx]->vin) {
837 [ + + ]: 134832 : if (vin.prevout.hash == txn[tx]->GetHash()) matching_parent = true;
838 : : }
839 [ + + ]: 63920 : if (!matching_parent) continue;
840 : : // Found an announcement from peer which is a child of txn[tx].
841 [ - + ]: 10158 : assert(it != children_from_peer.rend());
842 [ - + ]: 10158 : assert((*it)->GetWitnessHash() == txn[ann.tx]->GetWitnessHash());
843 : 10158 : ++it;
844 : : }
845 : : }
846 [ - + ]: 130816 : assert(it == children_from_peer.rend());
847 : 130816 : }
848 : 8176 : }
849 : : // TotalOrphanUsage
850 [ + - - + ]: 511 : assert(orphan_usage == real->TotalOrphanUsage());
851 [ + + ]: 8687 : for (NodeId peer = 0; peer < NUM_PEERS; ++peer) {
852 : 8176 : bool sim_have_reconsider = have_reconsider_fn(peer);
853 : : // HaveTxToReconsider
854 [ + - ]: 8176 : bool have_reconsider = real->HaveTxToReconsider(peer);
855 [ - + ]: 8176 : assert(have_reconsider == sim_have_reconsider);
856 : : // UsageByPeer
857 [ + - - + ]: 8176 : assert(usage_by_peer[peer] == real->UsageByPeer(peer));
858 : : // AnnouncementsFromPeer
859 [ + - - + ]: 8176 : assert(count_by_peer[peer] == real->AnnouncementsFromPeer(peer));
860 : : }
861 : : // CountAnnouncements
862 [ + - - + ]: 511 : assert(sim_announcements.size() == real->CountAnnouncements());
863 : : // CountUniqueOrphans
864 [ + - - + ]: 511 : assert(unique_orphans == real->CountUniqueOrphans());
865 : : // MaxGlobalLatencyScore
866 [ + - - + ]: 511 : assert(max_global_ann == real->MaxGlobalLatencyScore());
867 : : // ReservedPeerUsage
868 [ + - - + ]: 511 : assert(reserved_peer_usage == real->ReservedPeerUsage());
869 : : // MaxPeerLatencyScore
870 [ + - ]: 511 : auto present_peers = count_peers_fn();
871 [ + + + - : 720 : assert(max_global_ann / std::max<unsigned>(1, present_peers) == real->MaxPeerLatencyScore());
- + ]
872 : : // MaxGlobalUsage
873 [ + + + - : 720 : assert(reserved_peer_usage * std::max<unsigned>(1, present_peers) == real->MaxGlobalUsage());
- + ]
874 : : // TotalLatencyScore.
875 [ + - - + ]: 511 : assert(real->TotalLatencyScore() == total_latency_score);
876 : 511 : }
|