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 <test/util/time.h>
19 : : #include <uint256.h>
20 : : #include <util/check.h>
21 : : #include <util/feefrac.h>
22 : : #include <util/time.h>
23 : :
24 : : #include <algorithm>
25 : : #include <bitset>
26 : : #include <cmath>
27 : : #include <cstdint>
28 : : #include <iostream>
29 : : #include <memory>
30 : : #include <set>
31 : : #include <utility>
32 : : #include <vector>
33 : :
34 : 2 : void initialize_orphanage()
35 : : {
36 [ + - + - : 2 : static const auto testing_setup = MakeNoLogFileContext();
+ - ]
37 : 2 : }
38 : :
39 [ + - ]: 1417 : FUZZ_TARGET(txorphan, .init = initialize_orphanage)
40 : : {
41 : 941 : SeedRandomStateForTest(SeedRand::ZEROS);
42 : 941 : FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
43 : 941 : FastRandomContext orphanage_rng{ConsumeUInt256(fuzzed_data_provider)};
44 [ + - ]: 941 : FakeNodeClock clock{ConsumeTime(fuzzed_data_provider)};
45 : :
46 : 941 : auto orphanage = node::MakeTxOrphanage();
47 : 941 : std::vector<COutPoint> outpoints; // Duplicates are tolerated
48 [ + - ]: 941 : outpoints.reserve(200'000);
49 : :
50 : : // initial outpoints used to construct transactions later
51 [ + + ]: 4705 : for (uint8_t i = 0; i < 4; i++) {
52 [ + - ]: 3764 : outpoints.emplace_back(Txid::FromUint256(uint256{i}), 0);
53 : : }
54 : :
55 : 941 : CTransactionRef ptx_potential_parent = nullptr;
56 : :
57 : 941 : std::vector<CTransactionRef> tx_history;
58 : :
59 [ - + + - : 189876 : LIMITED_WHILE (outpoints.size() < 200'000 && fuzzed_data_provider.ConsumeBool(), 1000) {
+ + + + ]
60 : : // construct transaction
61 : 187994 : const CTransactionRef tx = [&] {
62 : 93997 : CMutableTransaction tx_mut;
63 [ - + ]: 93997 : const auto num_in = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, outpoints.size());
64 : 93997 : 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 [ + - ]: 93997 : tx_mut.vin.reserve(num_in);
68 [ + + ]: 17600398 : for (uint32_t i = 0; i < num_in; i++) {
69 : 17506401 : 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 [ + - ]: 35012802 : 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 [ + - ]: 93997 : tx_mut.vout.reserve(num_out);
75 [ + + ]: 7219617 : for (uint32_t i = 0; i < num_out; i++) {
76 [ + - ]: 14251240 : tx_mut.vout.emplace_back(CAmount{0}, CScript{});
77 : : }
78 [ + - ]: 93997 : auto new_tx = MakeTransactionRef(tx_mut);
79 : : // add newly constructed outpoints to the coin pool
80 [ + + ]: 7219617 : for (uint32_t i = 0; i < num_out; i++) {
81 [ + - ]: 7125620 : outpoints.emplace_back(new_tx->GetHash(), i);
82 : : }
83 : 93997 : return new_tx;
84 [ + - ]: 187994 : }();
85 : :
86 [ + - ]: 93997 : tx_history.push_back(tx);
87 : :
88 [ + + ]: 93997 : 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 [ + + ]: 93997 : if (ptx_potential_parent) {
93 : : // Set up future GetTxToReconsider call.
94 [ + - ]: 93067 : 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 : 93067 : NodeId peer_id = fuzzed_data_provider.ConsumeIntegral<NodeId>();
98 [ + - + + ]: 186289 : for (const auto& child : orphanage->GetChildrenFromSamePeer(ptx_potential_parent, peer_id)) {
99 [ - + ][ + + ]: 1757544 : assert(std::any_of(child->vin.cbegin(), child->vin.cend(), [&](const auto& input) {
100 : : return input.prevout.hash == ptx_potential_parent->GetHash();
101 : : }));
102 : 93067 : }
103 : : }
104 : :
105 : : // trigger orphanage functions
106 [ + + + + ]: 3538189 : LIMITED_WHILE (fuzzed_data_provider.ConsumeBool(), 1000) {
107 : 3444192 : NodeId peer_id = fuzzed_data_provider.ConsumeIntegral<NodeId>();
108 [ + - ]: 3444192 : const auto total_bytes_start{orphanage->TotalOrphanUsage()};
109 [ + - ]: 3444192 : const auto total_peer_bytes_start{orphanage->UsageByPeer(peer_id)};
110 : 3444192 : const auto tx_weight{GetTransactionWeight(*tx)};
111 : :
112 [ + - ]: 3444192 : CallOneOf(
113 : : fuzzed_data_provider,
114 : 23690 : [&] {
115 : 23690 : {
116 : 23690 : CTransactionRef ref = orphanage->GetTxToReconsider(peer_id);
117 [ + + ]: 23690 : if (ref) {
118 [ + - - + : 23690 : Assert(orphanage->HaveTx(ref->GetWitnessHash()));
+ + ]
119 : : }
120 : 23690 : }
121 : 23690 : },
122 : 3135411 : [&] {
123 : 3135411 : bool have_tx = orphanage->HaveTx(tx->GetWitnessHash());
124 : 3135411 : bool have_tx_and_peer = orphanage->HaveTxFromPeer(wtxid, peer_id);
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 : 3135411 : {
128 : 3135411 : bool add_tx = orphanage->AddTx(tx, peer_id);
129 : : // have_tx == true -> add_tx == false
130 [ - + ]: 3135411 : Assert(!have_tx || !add_tx);
131 : : // have_tx_and_peer == true -> add_tx == false
132 [ - + ]: 3135411 : Assert(!have_tx_and_peer || !add_tx);
133 : : // After AddTx, the orphanage may trim itself, so the peer's usage may have gone up or down.
134 : :
135 [ + + ]: 3135411 : if (add_tx) {
136 [ - + ]: 178830 : Assert(tx_weight <= MAX_STANDARD_TX_WEIGHT);
137 : : } else {
138 : : // Peer may have been added as an announcer.
139 [ + + ]: 2956581 : if (orphanage->UsageByPeer(peer_id) > total_peer_bytes_start) {
140 [ - + ]: 445139 : Assert(orphanage->HaveTxFromPeer(wtxid, peer_id));
141 : : }
142 : :
143 : : // If announcement was added, total bytes does not increase.
144 : : // However, if eviction was triggered, the value may decrease.
145 [ - + ]: 2956581 : Assert(orphanage->TotalOrphanUsage() <= total_bytes_start);
146 : : }
147 : : }
148 : : // We are not guaranteed to have_tx after AddTx. There are a few possible reasons:
149 : : // - tx itself exceeds the per-peer memory usage limit, so LimitOrphans had to remove it immediately
150 : : // - tx itself exceeds the per-peer latency score limit, so LimitOrphans had to remove it immediately
151 : : // - the orphanage needed trim and all other announcements from this peer are reconsiderable
152 : 3135411 : },
153 : 9959 : [&] {
154 : 9959 : bool have_tx = orphanage->HaveTx(tx->GetWitnessHash());
155 : 9959 : bool have_tx_and_peer = orphanage->HaveTxFromPeer(tx->GetWitnessHash(), peer_id);
156 : : // AddAnnouncer should return false if tx doesn't exist or we already HaveTxFromPeer.
157 : 9959 : {
158 : 9959 : bool added_announcer = orphanage->AddAnnouncer(tx->GetWitnessHash(), peer_id);
159 : : // have_tx == false -> added_announcer == false
160 [ - + ]: 9959 : Assert(have_tx || !added_announcer);
161 : : // have_tx_and_peer == true -> added_announcer == false
162 [ - + ]: 9959 : Assert(!have_tx_and_peer || !added_announcer);
163 : :
164 : : // If announcement was added, total bytes does not increase.
165 : : // However, if eviction was triggered, the value may decrease.
166 [ - + ]: 9959 : Assert(orphanage->TotalOrphanUsage() <= total_bytes_start);
167 : : }
168 : 9959 : },
169 : 243413 : [&] {
170 : 243413 : bool have_tx = orphanage->HaveTx(tx->GetWitnessHash());
171 : 243413 : bool have_tx_and_peer{orphanage->HaveTxFromPeer(wtxid, peer_id)};
172 : : // EraseTx should return 0 if m_orphans doesn't have the tx
173 : 243413 : {
174 : 243413 : auto bytes_from_peer_before{orphanage->UsageByPeer(peer_id)};
175 [ - + ]: 243413 : Assert(have_tx == orphanage->EraseTx(tx->GetWitnessHash()));
176 : : // After EraseTx, the orphanage may trim itself, so any peer's usage may decrease.
177 [ + + ]: 243413 : if (!have_tx) {
178 [ - + ]: 137084 : Assert(orphanage->UsageByPeer(peer_id) == bytes_from_peer_before);
179 [ + + ]: 106329 : } else if (have_tx_and_peer) {
180 [ - + ]: 5909 : Assert(orphanage->UsageByPeer(peer_id) <= bytes_from_peer_before - tx_weight);
181 : : } else {
182 [ - + ]: 100420 : Assert(orphanage->UsageByPeer(peer_id) <= bytes_from_peer_before);
183 : : }
184 : : }
185 : 243413 : have_tx = orphanage->HaveTx(tx->GetWitnessHash());
186 : 243413 : have_tx_and_peer = orphanage->HaveTxFromPeer(wtxid, peer_id);
187 : : // have_tx should be false and EraseTx should fail
188 : 243413 : {
189 [ + - - + : 243413 : Assert(!have_tx && !have_tx_and_peer && !orphanage->EraseTx(wtxid));
- + ]
190 : : }
191 : 243413 : },
192 : 18738 : [&] {
193 : 18738 : orphanage->EraseForPeer(peer_id);
194 [ - + ]: 18738 : Assert(!orphanage->HaveTxFromPeer(tx->GetWitnessHash(), peer_id));
195 [ - + ]: 18738 : Assert(orphanage->UsageByPeer(peer_id) == 0);
196 : 18738 : },
197 : 12981 : [&] {
198 : : // Make a block out of txs and then EraseForBlock
199 : 12981 : CBlock block;
200 : 12981 : int64_t block_weight{0};
201 : 12981 : int num_txs = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(0, 1000);
202 [ + + ]: 1694472 : for (int i{0}; i < num_txs; ++i) {
203 : 1685299 : auto& tx_to_remove = PickValue(fuzzed_data_provider, tx_history);
204 : 1685299 : const auto tx_weight = GetTransactionWeight(*tx_to_remove);
205 [ + + ]: 1685299 : if (block_weight + tx_weight > MAX_BLOCK_WEIGHT) break;
206 : 1681491 : block_weight += tx_weight;
207 [ + - ]: 1681491 : block.vtx.push_back(tx_to_remove);
208 : : }
209 [ + - ]: 12981 : orphanage->EraseForBlock(block);
210 [ + + ]: 1694472 : for (const auto& tx_removed : block.vtx) {
211 [ + - - + ]: 1681491 : Assert(!orphanage->HaveTx(tx_removed->GetWitnessHash()));
212 [ + - - + ]: 1681491 : Assert(!orphanage->HaveTxFromPeer(tx_removed->GetWitnessHash(), peer_id));
213 : : }
214 : 12981 : }
215 : : );
216 : : }
217 : :
218 : : // Set tx as potential parent to be used for future GetChildren() calls.
219 [ + + + + ]: 187064 : if (!ptx_potential_parent || fuzzed_data_provider.ConsumeBool()) {
220 : 60871 : ptx_potential_parent = tx;
221 : : }
222 : :
223 [ + - ]: 93997 : const bool have_tx{orphanage->HaveTx(tx->GetWitnessHash())};
224 [ + - + + ]: 93997 : const bool get_tx_nonnull{orphanage->GetTx(tx->GetWitnessHash()) != nullptr};
225 [ - + + - ]: 93997 : Assert(have_tx == get_tx_nonnull);
226 : 93997 : }
227 [ + - ]: 941 : orphanage->SanityCheck();
228 [ + + ]: 1871 : }
229 : :
230 [ + - ]: 1252 : FUZZ_TARGET(txorphan_protected, .init = initialize_orphanage)
231 : : {
232 : 776 : SeedRandomStateForTest(SeedRand::ZEROS);
233 : 776 : FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
234 : 776 : FastRandomContext orphanage_rng{ConsumeUInt256(fuzzed_data_provider)};
235 [ + - ]: 776 : FakeNodeClock clock{ConsumeTime(fuzzed_data_provider)};
236 : :
237 : : // We have num_peers peers. Some subset of them will never exceed their reserved weight or announcement count, and
238 : : // should therefore never have any orphans evicted.
239 : 776 : const unsigned int MAX_PEERS = 125;
240 : 776 : const unsigned int num_peers = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(1, MAX_PEERS);
241 : : // Generate a vector of bools for whether each peer is protected from eviction
242 : 776 : std::bitset<MAX_PEERS> protected_peers;
243 [ + + ]: 18417 : for (unsigned int i = 0; i < num_peers; i++) {
244 [ + - ]: 17641 : protected_peers.set(i, fuzzed_data_provider.ConsumeBool());
245 : : }
246 : :
247 : : // Params for orphanage.
248 : 776 : const unsigned int global_latency_score_limit = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(num_peers, 6'000);
249 : 776 : const int64_t per_peer_weight_reservation = fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(1, 4'040'000);
250 : 776 : auto orphanage = node::MakeTxOrphanage(global_latency_score_limit, per_peer_weight_reservation);
251 : :
252 : : // The actual limit, MaxPeerLatencyScore(), may be higher, since TxOrphanage only counts peers
253 : : // that have announced an orphan. The honest peer will not experience evictions if it never
254 : : // exceeds this.
255 : 776 : const unsigned int honest_latency_limit = global_latency_score_limit / num_peers;
256 : : // Honest peer will not experience evictions if it never exceeds this.
257 : 776 : const int64_t honest_mem_limit = per_peer_weight_reservation;
258 : :
259 : 776 : std::vector<COutPoint> outpoints; // Duplicates are tolerated
260 [ + - ]: 776 : outpoints.reserve(400);
261 : :
262 : : // initial outpoints used to construct transactions later
263 [ + + ]: 3880 : for (uint8_t i = 0; i < 4; i++) {
264 [ + - ]: 3104 : outpoints.emplace_back(Txid::FromUint256(uint256{i}), 0);
265 : : }
266 : :
267 : : // These are honest peer's live announcements. We expect them to be protected from eviction.
268 : 776 : std::set<Wtxid> protected_wtxids;
269 : :
270 [ - + + + : 17698 : LIMITED_WHILE (outpoints.size() < 400 && fuzzed_data_provider.ConsumeBool(), 1000) {
+ + - + ]
271 : : // construct transaction
272 : 16172 : const CTransactionRef tx = [&] {
273 : 8086 : CMutableTransaction tx_mut;
274 [ - + ]: 8086 : const auto num_in = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, outpoints.size());
275 : 8086 : const auto num_out = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, 256);
276 : : // pick outpoints from outpoints as input. We allow input duplicates on purpose, given we are not
277 : : // running any transaction validation logic before adding transactions to the orphanage
278 [ + - ]: 8086 : tx_mut.vin.reserve(num_in);
279 [ + + ]: 120192 : for (uint32_t i = 0; i < num_in; i++) {
280 : 112106 : auto& prevout = PickValue(fuzzed_data_provider, outpoints);
281 : : // try making transactions unique by setting a random nSequence, but allow duplicate transactions if they happen
282 [ + - ]: 224212 : tx_mut.vin.emplace_back(prevout, CScript{}, fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(0, CTxIn::SEQUENCE_FINAL));
283 : : }
284 : : // output amount or spendability will not affect txorphanage
285 [ + - ]: 8086 : tx_mut.vout.reserve(num_out);
286 [ + + ]: 77152 : for (uint32_t i = 0; i < num_out; i++) {
287 : 69066 : const auto payload_size = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(0, 100000);
288 [ + + ]: 69066 : if (payload_size) {
289 [ + - + - : 152898 : tx_mut.vout.emplace_back(0, CScript() << OP_RETURN << std::vector<unsigned char>(payload_size));
+ - ]
290 : : } else {
291 [ + - ]: 36200 : tx_mut.vout.emplace_back(0, CScript{});
292 : : }
293 : : }
294 [ + - ]: 8086 : auto new_tx = MakeTransactionRef(tx_mut);
295 : : // add newly constructed outpoints to the coin pool
296 [ + + ]: 77152 : for (uint32_t i = 0; i < num_out; i++) {
297 [ + - ]: 69066 : outpoints.emplace_back(new_tx->GetHash(), i);
298 : : }
299 : 8086 : return new_tx;
300 [ + - ]: 16172 : }();
301 : :
302 : 8086 : const auto wtxid{tx->GetWitnessHash()};
303 : :
304 : : // orphanage functions
305 [ + + + + ]: 3569803 : LIMITED_WHILE (fuzzed_data_provider.remaining_bytes(), 10 * global_latency_score_limit) {
306 : 3561717 : NodeId peer_id = fuzzed_data_provider.ConsumeIntegralInRange<NodeId>(0, num_peers - 1);
307 : 3561717 : const auto tx_weight{GetTransactionWeight(*tx)};
308 : :
309 : : // This protected peer will never send orphans that would
310 : : // exceed their own personal allotment, so is never evicted.
311 [ + - ]: 3561717 : const bool peer_is_protected{protected_peers[peer_id]};
312 : :
313 [ + - ]: 3561717 : CallOneOf(
314 : : fuzzed_data_provider,
315 : 1091324 : [&] { // AddTx
316 : 1091324 : bool have_tx_and_peer = orphanage->HaveTxFromPeer(wtxid, peer_id);
317 [ + + + + ]: 1091324 : if (peer_is_protected && !have_tx_and_peer &&
318 [ + + ]: 273889 : (orphanage->UsageByPeer(peer_id) + tx_weight > honest_mem_limit ||
319 [ - + + + ]: 157032 : orphanage->LatencyScoreFromPeer(peer_id) + (tx->vin.size() / 10) + 1 > honest_latency_limit)) {
320 : : // We never want our protected peer oversized or over-announced
321 : : } else {
322 : 962902 : orphanage->AddTx(tx, peer_id);
323 [ + + + + ]: 962902 : if (peer_is_protected && orphanage->HaveTxFromPeer(wtxid, peer_id)) {
324 : 313198 : protected_wtxids.insert(wtxid);
325 : : }
326 : : }
327 : 1091324 : },
328 : 1310614 : [&] { // AddAnnouncer
329 : 1310614 : bool have_tx_and_peer = orphanage->HaveTxFromPeer(tx->GetWitnessHash(), peer_id);
330 : : // AddAnnouncer should return false if tx doesn't exist or we already HaveTxFromPeer.
331 : 1310614 : {
332 [ + + + + ]: 1310614 : if (peer_is_protected && !have_tx_and_peer &&
333 [ + + ]: 551529 : (orphanage->UsageByPeer(peer_id) + tx_weight > honest_mem_limit ||
334 [ - + + + ]: 305106 : orphanage->LatencyScoreFromPeer(peer_id) + (tx->vin.size() / 10) + 1 > honest_latency_limit)) {
335 : : // We never want our protected peer oversized
336 : : } else {
337 : 1054721 : orphanage->AddAnnouncer(tx->GetWitnessHash(), peer_id);
338 [ + + + + ]: 1054721 : if (peer_is_protected && orphanage->HaveTxFromPeer(wtxid, peer_id)) {
339 : 285285 : protected_wtxids.insert(wtxid);
340 : : }
341 : : }
342 : : }
343 : 1310614 : },
344 : 443901 : [&] { // EraseTx
345 [ + + ]: 443901 : if (protected_wtxids.contains(tx->GetWitnessHash())) {
346 : 96666 : protected_wtxids.erase(wtxid);
347 : : }
348 : 443901 : orphanage->EraseTx(wtxid);
349 [ - + ]: 443901 : Assert(!orphanage->HaveTx(wtxid));
350 : 443901 : },
351 : 715878 : [&] { // EraseForPeer
352 [ + + ]: 715878 : if (!protected_peers[peer_id]) {
353 : 315481 : orphanage->EraseForPeer(peer_id);
354 [ - + ]: 315481 : Assert(orphanage->UsageByPeer(peer_id) == 0);
355 [ - + ]: 315481 : Assert(orphanage->LatencyScoreFromPeer(peer_id) == 0);
356 [ - + ]: 315481 : Assert(orphanage->AnnouncementsFromPeer(peer_id) == 0);
357 : : }
358 : 715878 : }
359 : : );
360 : : }
361 : 8086 : }
362 : :
363 [ + - ]: 776 : orphanage->SanityCheck();
364 : : // All of the honest peer's announcements are still present.
365 [ + + ]: 1453 : for (const auto& wtxid : protected_wtxids) {
366 [ + - - + ]: 677 : Assert(orphanage->HaveTx(wtxid));
367 : : }
368 : 776 : }
369 : :
370 [ + - ]: 1940 : FUZZ_TARGET(txorphanage_sim)
371 : : {
372 : 1464 : SeedRandomStateForTest(SeedRand::ZEROS);
373 : : // This is a comprehensive simulation fuzz test, which runs through a scenario involving up to
374 : : // 16 transactions (which may have simple or complex topology, and may have duplicate txids
375 : : // with distinct wtxids, and up to 16 peers. The scenario is performed both on a real
376 : : // TxOrphanage object and the behavior is compared with a naive reimplementation (just a vector
377 : : // of announcements) where possible, and tested for desired properties where not possible.
378 : :
379 : : //
380 : : // 1. Setup.
381 : : //
382 : :
383 : : /** The total number of transactions this simulation uses (not all of which will necessarily
384 : : * be present in the orphanage at once). */
385 : 1464 : static constexpr unsigned NUM_TX = 16;
386 : : /** The number of peers this simulation uses (not all of which will necessarily be present in
387 : : * the orphanage at once). */
388 : 1464 : static constexpr unsigned NUM_PEERS = 16;
389 : : /** The maximum number of announcements this simulation uses (which may be higher than the
390 : : * number permitted inside the orphanage). */
391 : 1464 : static constexpr unsigned MAX_ANN = 64;
392 : :
393 : 1464 : FuzzedDataProvider provider(buffer.data(), buffer.size());
394 : : /** Local RNG. Only used for topology/sizes of the transaction set, the order of transactions
395 : : * in EraseForBlock, and for the randomized passed to AddChildrenToWorkSet. */
396 : 1464 : InsecureRandomContext rng(provider.ConsumeIntegral<uint64_t>());
397 : :
398 : : //
399 : : // 2. Construct an interesting set of 16 transactions.
400 : : //
401 : :
402 : : // - Pick a topological order among the transactions.
403 : 1464 : std::vector<unsigned> txorder(NUM_TX);
404 : 1464 : std::iota(txorder.begin(), txorder.end(), unsigned{0});
405 : 1464 : std::shuffle(txorder.begin(), txorder.end(), rng);
406 : : // - Pick a set of dependencies (pair<child_index, parent_index>).
407 : 1464 : std::vector<std::pair<unsigned, unsigned>> deps;
408 [ + - ]: 1464 : deps.reserve((NUM_TX * (NUM_TX - 1)) / 2);
409 [ + + ]: 23424 : for (unsigned p = 0; p < NUM_TX - 1; ++p) {
410 [ + + ]: 197640 : for (unsigned c = p + 1; c < NUM_TX; ++c) {
411 [ + - ]: 175680 : deps.emplace_back(c, p);
412 : : }
413 : : }
414 : 1464 : std::shuffle(deps.begin(), deps.end(), rng);
415 [ + - ]: 1464 : deps.resize(provider.ConsumeIntegralInRange<unsigned>(0, NUM_TX * 4 - 1));
416 : : // - Construct the actual transactions.
417 [ + - ]: 1464 : std::set<Wtxid> wtxids;
418 [ + - ]: 1464 : std::vector<CTransactionRef> txn(NUM_TX);
419 : : node::TxOrphanage::Usage total_usage{0};
420 [ + + ]: 24888 : for (unsigned t = 0; t < NUM_TX; ++t) {
421 [ + - ]: 23424 : CMutableTransaction tx;
422 [ + + + + ]: 23424 : if (t > 0 && rng.randrange(4) == 0) {
423 : : // Occasionally duplicate the previous transaction, so that repetitions of the same
424 : : // txid are possible (with different wtxid).
425 [ + - ]: 22264 : tx = CMutableTransaction(*txn[txorder[t - 1]]);
426 : : } else {
427 : 12292 : tx.version = 1;
428 : 12292 : tx.nLockTime = 0xffffffff;
429 : : // Construct 1 to 16 outputs.
430 : 12292 : auto num_outputs = rng.randrange<unsigned>(1 << rng.randrange<unsigned>(5)) + 1;
431 [ + + ]: 49744 : for (unsigned output = 0; output < num_outputs; ++output) {
432 : 37452 : CScript scriptpubkey;
433 : 37452 : scriptpubkey.resize(provider.ConsumeIntegralInRange<unsigned>(20, 34));
434 [ + - ]: 37452 : tx.vout.emplace_back(CAmount{0}, std::move(scriptpubkey));
435 : 37452 : }
436 : : // Construct inputs (one for each dependency).
437 [ + + + + ]: 410081 : for (auto& [child, parent] : deps) {
438 [ + + ]: 397789 : if (child == t) {
439 [ - + ]: 23041 : auto& partx = txn[txorder[parent]];
440 [ - + ]: 23041 : assert(partx->version == 1);
441 [ - + + - ]: 23041 : COutPoint outpoint(partx->GetHash(), rng.randrange<size_t>(partx->vout.size()));
442 [ + - ]: 23041 : tx.vin.emplace_back(outpoint);
443 : 23041 : tx.vin.back().scriptSig.resize(provider.ConsumeIntegralInRange<unsigned>(16, 200));
444 : : }
445 : : }
446 : : // Construct fallback input in case there are no dependencies.
447 [ + + ]: 12292 : if (tx.vin.empty()) {
448 [ + - ]: 5453 : COutPoint outpoint(Txid::FromUint256(rng.rand256()), rng.randrange<size_t>(16));
449 [ + - ]: 5453 : tx.vin.emplace_back(outpoint);
450 : 5453 : tx.vin.back().scriptSig.resize(provider.ConsumeIntegralInRange<unsigned>(16, 200));
451 : : }
452 : : }
453 : : // Optionally modify the witness (allowing wtxid != txid), and certainly when the wtxid
454 : : // already exists.
455 [ + - + + : 117550 : while (wtxids.contains(CTransaction(tx).GetWitnessHash()) || rng.randrange(4) == 0) {
+ + + + ]
456 [ - + ]: 35351 : auto& input = tx.vin[rng.randrange(tx.vin.size())];
457 [ + + ]: 35351 : if (rng.randbool()) {
458 [ + - ]: 16639 : input.scriptWitness.stack.resize(1);
459 [ + - ]: 16639 : input.scriptWitness.stack[0].resize(rng.randrange(100));
460 : : } else {
461 [ + - ]: 18712 : input.scriptWitness.stack.resize(0);
462 : : }
463 : : }
464 : : // Convert to CTransactionRef.
465 [ + - - + ]: 46848 : txn[txorder[t]] = MakeTransactionRef(std::move(tx));
466 [ + - ]: 23424 : wtxids.insert(txn[txorder[t]]->GetWitnessHash());
467 : 23424 : auto weight = GetTransactionWeight(*txn[txorder[t]]);
468 [ - + ]: 23424 : assert(weight < MAX_STANDARD_TX_WEIGHT);
469 : 23424 : total_usage += GetTransactionWeight(*txn[txorder[t]]);
470 : 23424 : }
471 : :
472 : : //
473 : : // 3. Initialize real orphanage
474 : : //
475 : :
476 : 1464 : auto max_global_latency_score = provider.ConsumeIntegralInRange<node::TxOrphanage::Count>(NUM_PEERS, MAX_ANN);
477 : 1464 : auto reserved_peer_usage = provider.ConsumeIntegralInRange<node::TxOrphanage::Usage>(1, total_usage);
478 : 1464 : auto real = node::MakeTxOrphanage(max_global_latency_score, reserved_peer_usage);
479 : :
480 : : //
481 : : // 4. Functions and data structures for the simulation.
482 : : //
483 : :
484 : : /** Data structure representing one announcement (pair of (tx, peer), plus whether it's
485 : : * reconsiderable or not. */
486 : 1464 : struct SimAnnouncement
487 : : {
488 : : unsigned tx;
489 : : NodeId announcer;
490 : : bool reconsider{false};
491 : 62582 : SimAnnouncement(unsigned tx_in, NodeId announcer_in, bool reconsider_in) noexcept :
492 : 62582 : tx(tx_in), announcer(announcer_in), reconsider(reconsider_in) {}
493 : : };
494 : : /** The entire simulated orphanage is represented by this list of announcements, in
495 : : * announcement order (unlike TxOrphanageImpl which uses a sequence number to represent
496 : : * announcement order). New announcements are added to the back. */
497 : 1464 : std::vector<SimAnnouncement> sim_announcements;
498 : :
499 : : /** Consume a transaction (index into txn) from provider. */
500 : 22073 : auto read_tx_fn = [&]() -> unsigned { return provider.ConsumeIntegralInRange<unsigned>(0, NUM_TX - 1); };
501 : : /** Consume a NodeId from provider. */
502 : 46896 : auto read_peer_fn = [&]() -> NodeId { return provider.ConsumeIntegralInRange<unsigned>(0, NUM_PEERS - 1); };
503 : : /** Consume both a transaction (index into txn) and a NodeId from provider. */
504 : 89923 : auto read_tx_peer_fn = [&]() -> std::pair<unsigned, NodeId> {
505 : 88459 : auto code = provider.ConsumeIntegralInRange<unsigned>(0, NUM_TX * NUM_PEERS - 1);
506 : 88459 : return {code % NUM_TX, code / NUM_TX};
507 : 1464 : };
508 : : /** Determine if we have any announcements of the given transaction in the simulation. */
509 : 2984285 : auto have_tx_fn = [&](unsigned tx) -> bool {
510 [ + + ]: 17871521 : for (auto& ann : sim_announcements) {
511 [ + + ]: 15830870 : if (ann.tx == tx) return true;
512 : : }
513 : : return false;
514 : 1464 : };
515 : : /** Count the number of peers in the simulation. */
516 : 311724 : auto count_peers_fn = [&]() -> unsigned {
517 : 310260 : std::bitset<NUM_PEERS> mask;
518 [ + + ]: 2762698 : for (auto& ann : sim_announcements) {
519 : 2452438 : mask.set(ann.announcer);
520 : : }
521 : 310260 : return mask.count();
522 : 1464 : };
523 : : /** Determine if we have any reconsiderable announcements of a given transaction. */
524 : 67380 : auto have_reconsiderable_fn = [&](unsigned tx) -> bool {
525 [ + + ]: 792543 : for (auto& ann : sim_announcements) {
526 [ + + + + ]: 749684 : if (ann.reconsider && ann.tx == tx) return true;
527 : : }
528 : : return false;
529 : 1464 : };
530 : : /** Determine if a peer has any transactions to reconsider. */
531 : 31396 : auto have_reconsider_fn = [&](NodeId peer) -> bool {
532 [ + + ]: 232959 : for (auto& ann : sim_announcements) {
533 [ + + + + ]: 204656 : if (ann.reconsider && ann.announcer == peer) return true;
534 : : }
535 : : return false;
536 : 1464 : };
537 : : /** Get an iterator to an existing (wtxid, peer) pair in the simulation. */
538 : 25266 : auto find_announce_wtxid_fn = [&](const Wtxid& wtxid, NodeId peer) -> std::vector<SimAnnouncement>::iterator {
539 [ + - ]: 134706 : for (auto it = sim_announcements.begin(); it != sim_announcements.end(); ++it) {
540 [ + + + + ]: 134706 : if (txn[it->tx]->GetWitnessHash() == wtxid && it->announcer == peer) return it;
541 : : }
542 : 0 : return sim_announcements.end();
543 : 1464 : };
544 : : /** Get an iterator to an existing (tx, peer) pair in the simulation. */
545 : 464707 : auto find_announce_fn = [&](unsigned tx, NodeId peer) {
546 [ + + ]: 3763863 : for (auto it = sim_announcements.begin(); it != sim_announcements.end(); ++it) {
547 [ + + + + ]: 3329016 : if (it->tx == tx && it->announcer == peer) return it;
548 : : }
549 : 434847 : return sim_announcements.end();
550 : 1464 : };
551 : : /** Compute a peer's DoS score according to simulation data. */
552 : 382936 : auto dos_score_fn = [&](NodeId peer, int32_t max_count, int32_t max_usage) -> FeeFrac {
553 : 381472 : int64_t count{0};
554 : 381472 : int64_t usage{0};
555 [ + + ]: 3346400 : for (auto& ann : sim_announcements) {
556 [ + + ]: 2964928 : if (ann.announcer != peer) continue;
557 [ - + ]: 185308 : count += 1 + (txn[ann.tx]->vin.size() / 10);
558 : 185308 : usage += GetTransactionWeight(*txn[ann.tx]);
559 : : }
560 : 381472 : return std::max<ByRatioNegSize<FeeFrac>>(FeeFrac{count, max_count}, FeeFrac{usage, max_usage});
561 : 1464 : };
562 : :
563 : : //
564 : : // 5. Run through a scenario of mutators on both real and simulated orphanage.
565 : : //
566 : :
567 [ + + + + ]: 143941 : LIMITED_WHILE (provider.remaining_bytes() > 0, 200) {
568 : 142477 : int command = provider.ConsumeIntegralInRange<uint8_t>(0, 15);
569 : 201951 : while (true) {
570 [ - + + + : 201951 : if (sim_announcements.size() < MAX_ANN && command-- == 0) {
+ + ]
571 : : // AddTx
572 [ + - ]: 70900 : auto [tx, peer] = read_tx_peer_fn();
573 [ + - ]: 70900 : bool added = real->AddTx(txn[tx], peer);
574 : 70900 : bool sim_have_tx = have_tx_fn(tx);
575 [ - + ]: 70900 : assert(added == !sim_have_tx);
576 [ + + ]: 70900 : if (find_announce_fn(tx, peer) == sim_announcements.end()) {
577 [ + - ]: 57838 : sim_announcements.emplace_back(tx, peer, false);
578 : : }
579 : : break;
580 [ + + + + ]: 131051 : } else if (sim_announcements.size() < MAX_ANN && command-- == 0) {
581 : : // AddAnnouncer
582 [ + - ]: 17559 : auto [tx, peer] = read_tx_peer_fn();
583 [ + - ]: 17559 : bool added = real->AddAnnouncer(txn[tx]->GetWitnessHash(), peer);
584 : 17559 : bool sim_have_tx = have_tx_fn(tx);
585 : 17559 : auto sim_it = find_announce_fn(tx, peer);
586 [ + + + + : 30374 : assert(added == (sim_it == sim_announcements.end() && sim_have_tx));
- + ]
587 [ + + ]: 17559 : if (added) {
588 [ + - ]: 4744 : sim_announcements.emplace_back(tx, peer, false);
589 : : }
590 : : break;
591 [ + + ]: 113492 : } else if (command-- == 0) {
592 : : // EraseTx
593 : 7994 : auto tx = read_tx_fn();
594 [ + - ]: 7994 : bool erased = real->EraseTx(txn[tx]->GetWitnessHash());
595 : 7994 : bool sim_have = have_tx_fn(tx);
596 [ - + ]: 7994 : assert(erased == sim_have);
597 [ + + + + ]: 66373 : std::erase_if(sim_announcements, [&](auto& ann) { return ann.tx == tx; });
598 : : break;
599 [ + + ]: 105498 : } else if (command-- == 0) {
600 : : // EraseForPeer
601 : 7666 : auto peer = read_peer_fn();
602 [ + - ]: 7666 : real->EraseForPeer(peer);
603 [ + + + + ]: 51071 : std::erase_if(sim_announcements, [&](auto& ann) { return ann.announcer == peer; });
604 : : break;
605 [ + + ]: 97832 : } else if (command-- == 0) {
606 : : // EraseForBlock
607 : 10693 : auto pattern = provider.ConsumeIntegralInRange<uint64_t>(0, (uint64_t{1} << NUM_TX) - 1);
608 : 10693 : CBlock block;
609 : 10693 : std::set<COutPoint> spent;
610 [ + + ]: 181781 : for (unsigned tx = 0; tx < NUM_TX; ++tx) {
611 [ + + ]: 171088 : if ((pattern >> tx) & 1) {
612 [ + - ]: 53003 : block.vtx.emplace_back(txn[tx]);
613 [ + + ]: 175136 : for (auto& txin : block.vtx.back()->vin) {
614 [ + - ]: 122133 : spent.insert(txin.prevout);
615 : : }
616 : : }
617 : : }
618 : 10693 : std::shuffle(block.vtx.begin(), block.vtx.end(), rng);
619 [ + - ]: 10693 : real->EraseForBlock(block);
620 : 52611 : std::erase_if(sim_announcements, [&](auto& ann) {
621 [ + + ]: 86914 : for (auto& txin : txn[ann.tx]->vin) {
622 [ + + ]: 64701 : if (spent.contains(txin.prevout)) return true;
623 : : }
624 : : return false;
625 : : });
626 : 10693 : break;
627 [ + + ]: 97832 : } else if (command-- == 0) {
628 : : // AddChildrenToWorkSet
629 : 12615 : auto tx = read_tx_fn();
630 : 12615 : FastRandomContext rand_ctx(rng.rand256());
631 [ + - ]: 12615 : auto added = real->AddChildrenToWorkSet(*txn[tx], rand_ctx);
632 : : /** Set of not-already-reconsiderable child wtxids. */
633 : 12615 : std::set<Wtxid> child_wtxids;
634 [ + + ]: 214455 : for (unsigned child_tx = 0; child_tx < NUM_TX; ++child_tx) {
635 [ + + ]: 201840 : if (!have_tx_fn(child_tx)) continue;
636 [ + + ]: 65916 : if (have_reconsiderable_fn(child_tx)) continue;
637 : 42859 : bool child_of = false;
638 [ + + ]: 104805 : for (auto& txin : txn[child_tx]->vin) {
639 [ + + ]: 77206 : if (txin.prevout.hash == txn[tx]->GetHash()) {
640 : : child_of = true;
641 : : break;
642 : : }
643 : : }
644 [ + + ]: 42859 : if (child_of) {
645 [ + - ]: 15260 : child_wtxids.insert(txn[child_tx]->GetWitnessHash());
646 : : }
647 : : }
648 [ + + ]: 27875 : for (auto& [wtxid, peer] : added) {
649 : : // Wtxid must be a child of tx that is not yet reconsiderable.
650 : 15260 : auto child_wtxid_it = child_wtxids.find(wtxid);
651 [ - + ]: 15260 : assert(child_wtxid_it != child_wtxids.end());
652 : : // Announcement must exist.
653 : 15260 : auto sim_ann_it = find_announce_wtxid_fn(wtxid, peer);
654 [ - + ]: 15260 : assert(sim_ann_it != sim_announcements.end());
655 : : // Announcement must not yet be reconsiderable.
656 [ - + ]: 15260 : assert(sim_ann_it->reconsider == false);
657 : : // Make reconsiderable.
658 : 15260 : sim_ann_it->reconsider = true;
659 : : // Remove from child_wtxids map, to disallow it being reported a second time in added.
660 : 15260 : child_wtxids.erase(wtxid);
661 : : }
662 : : // Verify that AddChildrenToWorkSet does not select announcements that were already reconsiderable:
663 : : // Check all child wtxids which did not occur at least once in the result were already reconsiderable
664 : : // due to a previous AddChildrenToWorkSet.
665 [ - + ]: 12615 : assert(child_wtxids.empty());
666 : 12615 : break;
667 [ + + ]: 87139 : } else if (command-- == 0) {
668 : : // GetTxToReconsider.
669 : 15050 : auto peer = read_peer_fn();
670 [ + - ]: 15050 : auto result = real->GetTxToReconsider(peer);
671 [ + + ]: 15050 : if (result) {
672 : : // A transaction was found. It must have a corresponding reconsiderable
673 : : // announcement from peer.
674 : 8542 : auto sim_ann_it = find_announce_wtxid_fn(result->GetWitnessHash(), peer);
675 [ - + ]: 8542 : assert(sim_ann_it != sim_announcements.end());
676 [ - + ]: 8542 : assert(sim_ann_it->announcer == peer);
677 [ - + ]: 8542 : assert(sim_ann_it->reconsider);
678 : : // Make it non-reconsiderable.
679 : 8542 : sim_ann_it->reconsider = false;
680 : : } else {
681 : : // No reconsiderable transaction was found from peer. Verify that it does not
682 : : // have any.
683 [ - + ]: 6508 : assert(!have_reconsider_fn(peer));
684 : : }
685 [ + + ]: 15050 : break;
686 : 15050 : }
687 : : }
688 : : // Always trim after each command if needed.
689 [ + - + + ]: 142477 : const auto max_ann = max_global_latency_score / std::max<unsigned>(1, count_peers_fn());
690 : 142477 : const auto max_mem = reserved_peer_usage;
691 : 166319 : while (true) {
692 : : // Count global usage and number of peers.
693 : 166319 : node::TxOrphanage::Usage total_usage{0};
694 [ - + ]: 166319 : node::TxOrphanage::Count total_latency_score = sim_announcements.size();
695 [ + + ]: 2827423 : for (unsigned tx = 0; tx < NUM_TX; ++tx) {
696 [ + + ]: 2661104 : if (have_tx_fn(tx)) {
697 : 831834 : total_usage += GetTransactionWeight(*txn[tx]);
698 [ - + ]: 831834 : total_latency_score += txn[tx]->vin.size() / 10;
699 : : }
700 : : }
701 [ + - ]: 166319 : auto num_peers = count_peers_fn();
702 [ + + + + ]: 313298 : bool oversized = (total_usage > reserved_peer_usage * num_peers) ||
703 [ + - ]: 146979 : (total_latency_score > real->MaxGlobalLatencyScore());
704 : 166319 : if (!oversized) break;
705 : : // Find worst peer.
706 : 23842 : FeeFrac worst_dos_score{0, 1};
707 : 23842 : unsigned worst_peer = unsigned(-1);
708 [ + + ]: 405314 : for (unsigned peer = 0; peer < NUM_PEERS; ++peer) {
709 : 381472 : auto dos_score = dos_score_fn(peer, max_ann, max_mem);
710 : : // Use >= so that the more recent peer (higher NodeId) wins in case of
711 : : // ties.
712 [ + + ]: 381472 : if (ByRatioNegSize{dos_score} >= ByRatioNegSize{worst_dos_score}) {
713 : 44972 : worst_dos_score = dos_score;
714 : 44972 : worst_peer = peer;
715 : : }
716 : : }
717 [ - + ]: 23842 : assert(worst_peer != unsigned(-1));
718 [ - + ]: 23842 : assert(ByRatio{worst_dos_score} > ByRatio{FeeFrac(1, 1)});
719 : : // Find oldest announcement from worst_peer, preferring non-reconsiderable ones.
720 : : bool done{false};
721 [ + - ]: 24163 : for (int reconsider = 0; reconsider < 2; ++reconsider) {
722 [ + + ]: 140624 : for (auto it = sim_announcements.begin(); it != sim_announcements.end(); ++it) {
723 [ + + + + ]: 140303 : if (it->announcer != worst_peer || it->reconsider != reconsider) continue;
724 : 23842 : sim_announcements.erase(it);
725 : 23842 : done = true;
726 : 23842 : break;
727 : : }
728 : 24163 : if (done) break;
729 : : }
730 [ + - ]: 23842 : assert(done);
731 : : }
732 : : // We must now be within limits, otherwise LimitOrphans should have continued further.
733 : : // We don't check the contents of the orphanage until the end to make fuzz runs faster.
734 [ + - + - : 142477 : assert(real->TotalLatencyScore() <= real->MaxGlobalLatencyScore());
- + ]
735 [ + - + - : 142477 : assert(real->TotalOrphanUsage() <= real->MaxGlobalUsage());
- + ]
736 : : }
737 : :
738 : : //
739 : : // 6. Perform a full comparison between the real orphanage's inspectors and the simulation.
740 : : //
741 : :
742 [ + - ]: 1464 : real->SanityCheck();
743 : :
744 : :
745 [ + - ]: 1464 : auto all_orphans = real->GetOrphanTransactions();
746 : 1464 : node::TxOrphanage::Usage orphan_usage{0};
747 [ + - ]: 1464 : std::vector<node::TxOrphanage::Usage> usage_by_peer(NUM_PEERS);
748 : 1464 : node::TxOrphanage::Count unique_orphans{0};
749 [ + - ]: 1464 : std::vector<node::TxOrphanage::Count> count_by_peer(NUM_PEERS);
750 [ - + ]: 1464 : node::TxOrphanage::Count total_latency_score = sim_announcements.size();
751 [ + + ]: 24888 : for (unsigned tx = 0; tx < NUM_TX; ++tx) {
752 : 23424 : bool sim_have_tx = have_tx_fn(tx);
753 [ + + ]: 23424 : if (sim_have_tx) {
754 : 6041 : orphan_usage += GetTransactionWeight(*txn[tx]);
755 [ - + ]: 6041 : total_latency_score += txn[tx]->vin.size() / 10;
756 : : }
757 : 23424 : unique_orphans += sim_have_tx;
758 [ + + ]: 95989 : auto orphans_it = std::find_if(all_orphans.begin(), all_orphans.end(), [&](auto& orph) { return orph.tx->GetWitnessHash() == txn[tx]->GetWitnessHash(); });
759 : : // GetOrphanTransactions (OrphanBase existence)
760 [ - + ]: 23424 : assert((orphans_it != all_orphans.end()) == sim_have_tx);
761 : : // HaveTx
762 [ + - ]: 23424 : bool have_tx = real->HaveTx(txn[tx]->GetWitnessHash());
763 [ - + ]: 23424 : assert(have_tx == sim_have_tx);
764 : : // GetTx
765 [ + - ]: 23424 : auto txref = real->GetTx(txn[tx]->GetWitnessHash());
766 [ - + ]: 23424 : assert(!!txref == sim_have_tx);
767 [ + + - + ]: 23424 : if (sim_have_tx) assert(txref->GetWitnessHash() == txn[tx]->GetWitnessHash());
768 : :
769 [ + + ]: 398208 : for (NodeId peer = 0; peer < NUM_PEERS; ++peer) {
770 : 374784 : auto it_sim_ann = find_announce_fn(tx, peer);
771 [ + + ]: 374784 : bool sim_have_ann = it_sim_ann != sim_announcements.end();
772 [ + + ]: 374784 : if (sim_have_ann) usage_by_peer[peer] += GetTransactionWeight(*txn[tx]);
773 [ + + ]: 374784 : count_by_peer[peer] += sim_have_ann;
774 : : // GetOrphanTransactions (announcers presence)
775 [ + + - + ]: 374784 : if (sim_have_ann) assert(sim_have_tx);
776 [ + + - + ]: 374784 : if (sim_have_tx) assert(orphans_it->announcers.count(peer) == sim_have_ann);
777 : : // HaveTxFromPeer
778 [ + - ]: 374784 : bool have_ann = real->HaveTxFromPeer(txn[tx]->GetWitnessHash(), peer);
779 [ - + ]: 374784 : assert(sim_have_ann == have_ann);
780 : : // GetChildrenFromSamePeer
781 [ + - ]: 374784 : auto children_from_peer = real->GetChildrenFromSamePeer(txn[tx], peer);
782 : 374784 : auto it = children_from_peer.rbegin();
783 [ + + ]: 1124352 : for (int phase = 0; phase < 2; ++phase) {
784 : : // First expect all children which have reconsiderable announcement from peer, then the others.
785 [ + + ]: 6375424 : for (auto& ann : sim_announcements) {
786 [ + + ]: 5625856 : if (ann.announcer != peer) continue;
787 [ + + ]: 351616 : if (ann.reconsider != (phase == 1)) continue;
788 : 175808 : bool matching_parent{false};
789 [ + + ]: 602416 : for (const auto& vin : txn[ann.tx]->vin) {
790 [ + + ]: 426608 : if (vin.prevout.hash == txn[tx]->GetHash()) matching_parent = true;
791 : : }
792 [ + + ]: 175808 : if (!matching_parent) continue;
793 : : // Found an announcement from peer which is a child of txn[tx].
794 [ - + ]: 37756 : assert(it != children_from_peer.rend());
795 [ - + ]: 37756 : assert((*it)->GetWitnessHash() == txn[ann.tx]->GetWitnessHash());
796 : 37756 : ++it;
797 : : }
798 : : }
799 [ - + ]: 374784 : assert(it == children_from_peer.rend());
800 : 374784 : }
801 : 23424 : }
802 : : // TotalOrphanUsage
803 [ + - - + ]: 1464 : assert(orphan_usage == real->TotalOrphanUsage());
804 [ + + ]: 24888 : for (NodeId peer = 0; peer < NUM_PEERS; ++peer) {
805 : 23424 : bool sim_have_reconsider = have_reconsider_fn(peer);
806 : : // HaveTxToReconsider
807 [ + - ]: 23424 : bool have_reconsider = real->HaveTxToReconsider(peer);
808 [ - + ]: 23424 : assert(have_reconsider == sim_have_reconsider);
809 : : // UsageByPeer
810 [ + - - + ]: 23424 : assert(usage_by_peer[peer] == real->UsageByPeer(peer));
811 : : // AnnouncementsFromPeer
812 [ + - - + ]: 23424 : assert(count_by_peer[peer] == real->AnnouncementsFromPeer(peer));
813 : : }
814 : : // CountAnnouncements
815 [ - + + - : 1464 : assert(sim_announcements.size() == real->CountAnnouncements());
- + ]
816 : : // CountUniqueOrphans
817 [ + - - + ]: 1464 : assert(unique_orphans == real->CountUniqueOrphans());
818 : : // MaxGlobalLatencyScore
819 [ + - - + ]: 1464 : assert(max_global_latency_score == real->MaxGlobalLatencyScore());
820 : : // ReservedPeerUsage
821 [ + - - + ]: 1464 : assert(reserved_peer_usage == real->ReservedPeerUsage());
822 : : // MaxPeerLatencyScore
823 [ + - ]: 1464 : auto present_peers = count_peers_fn();
824 [ + + + - : 2035 : assert(max_global_latency_score / std::max<unsigned>(1, present_peers) == real->MaxPeerLatencyScore());
- + ]
825 : : // MaxGlobalUsage
826 [ + + + - : 2035 : assert(reserved_peer_usage * std::max<unsigned>(1, present_peers) == real->MaxGlobalUsage());
- + ]
827 : : // TotalLatencyScore.
828 [ + - - + ]: 1464 : assert(real->TotalLatencyScore() == total_latency_score);
829 : 1464 : }
|