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 [ + - ]: 1049 : FUZZ_TARGET(txorphan, .init = initialize_orphanage)
40 : : {
41 : 591 : SeedRandomStateForTest(SeedRand::ZEROS);
42 : 591 : FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
43 : 591 : FastRandomContext orphanage_rng{ConsumeUInt256(fuzzed_data_provider)};
44 [ + - ]: 591 : NodeClockContext clock_ctx{ConsumeTime(fuzzed_data_provider)};
45 : :
46 : 591 : auto orphanage = node::MakeTxOrphanage();
47 : 591 : std::vector<COutPoint> outpoints; // Duplicates are tolerated
48 [ + - ]: 591 : outpoints.reserve(200'000);
49 : :
50 : : // initial outpoints used to construct transactions later
51 [ + + ]: 2955 : for (uint8_t i = 0; i < 4; i++) {
52 [ + - ]: 2364 : outpoints.emplace_back(Txid::FromUint256(uint256{i}), 0);
53 : : }
54 : :
55 : 591 : CTransactionRef ptx_potential_parent = nullptr;
56 : :
57 : 591 : std::vector<CTransactionRef> tx_history;
58 : :
59 [ - + + - : 125236 : LIMITED_WHILE(outpoints.size() < 200'000 && fuzzed_data_provider.ConsumeBool(), 1000)
+ + + + ]
60 : : {
61 : : // construct transaction
62 : 124054 : const CTransactionRef tx = [&] {
63 : 62027 : CMutableTransaction tx_mut;
64 [ - + ]: 62027 : const auto num_in = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, outpoints.size());
65 : 62027 : const auto num_out = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, 256);
66 : : // pick outpoints from outpoints as input. We allow input duplicates on purpose, given we are not
67 : : // running any transaction validation logic before adding transactions to the orphanage
68 [ + - ]: 62027 : tx_mut.vin.reserve(num_in);
69 [ + + ]: 11367388 : for (uint32_t i = 0; i < num_in; i++) {
70 : 11305361 : auto& prevout = PickValue(fuzzed_data_provider, outpoints);
71 : : // try making transactions unique by setting a random nSequence, but allow duplicate transactions if they happen
72 [ + - ]: 22610722 : tx_mut.vin.emplace_back(prevout, CScript{}, fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(0, CTxIn::SEQUENCE_FINAL));
73 : : }
74 : : // output amount will not affect txorphanage
75 [ + - ]: 62027 : tx_mut.vout.reserve(num_out);
76 [ + + ]: 4955125 : for (uint32_t i = 0; i < num_out; i++) {
77 [ + - ]: 9786196 : tx_mut.vout.emplace_back(CAmount{0}, CScript{});
78 : : }
79 [ + - ]: 62027 : auto new_tx = MakeTransactionRef(tx_mut);
80 : : // add newly constructed outpoints to the coin pool
81 [ + + ]: 4955125 : for (uint32_t i = 0; i < num_out; i++) {
82 [ + - ]: 4893098 : outpoints.emplace_back(new_tx->GetHash(), i);
83 : : }
84 : 62027 : return new_tx;
85 [ + - ]: 124054 : }();
86 : :
87 [ + - ]: 62027 : tx_history.push_back(tx);
88 : :
89 [ + + ]: 62027 : const auto wtxid{tx->GetWitnessHash()};
90 : :
91 : : // Trigger orphanage functions that are called using parents. ptx_potential_parent is a tx we constructed in a
92 : : // previous loop and potentially the parent of this tx.
93 [ + + ]: 62027 : if (ptx_potential_parent) {
94 : : // Set up future GetTxToReconsider call.
95 [ + - ]: 61444 : orphanage->AddChildrenToWorkSet(*ptx_potential_parent, orphanage_rng);
96 : :
97 : : // Check that all txns returned from GetChildrenFrom* are indeed a direct child of this tx.
98 : 61444 : NodeId peer_id = fuzzed_data_provider.ConsumeIntegral<NodeId>();
99 [ + - + + ]: 134811 : for (const auto& child : orphanage->GetChildrenFromSamePeer(ptx_potential_parent, peer_id)) {
100 [ - + ]: 951119 : assert(std::any_of(child->vin.cbegin(), child->vin.cend(), [&](const auto& input) {
101 : : return input.prevout.hash == ptx_potential_parent->GetHash();
102 : : }));
103 : 61444 : }
104 : : }
105 : :
106 : : // trigger orphanage functions
107 [ + + + + ]: 2471316 : LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 1000)
108 : : {
109 : 2409289 : NodeId peer_id = fuzzed_data_provider.ConsumeIntegral<NodeId>();
110 [ + - ]: 2409289 : const auto total_bytes_start{orphanage->TotalOrphanUsage()};
111 [ + - ]: 2409289 : const auto total_peer_bytes_start{orphanage->UsageByPeer(peer_id)};
112 : 2409289 : const auto tx_weight{GetTransactionWeight(*tx)};
113 : :
114 [ + - ]: 2409289 : CallOneOf(
115 : : fuzzed_data_provider,
116 : 13739 : [&] {
117 : 13739 : {
118 : 13739 : CTransactionRef ref = orphanage->GetTxToReconsider(peer_id);
119 [ + + ]: 13739 : if (ref) {
120 [ + - - + : 13739 : Assert(orphanage->HaveTx(ref->GetWitnessHash()));
+ + ]
121 : : }
122 : 13739 : }
123 : 13739 : },
124 : 2230071 : [&] {
125 : 2230071 : bool have_tx = orphanage->HaveTx(tx->GetWitnessHash());
126 : 2230071 : bool have_tx_and_peer = orphanage->HaveTxFromPeer(wtxid, peer_id);
127 : : // AddTx should return false if tx is too big or already have it
128 : : // tx weight is unknown, we only check when tx is already in orphanage
129 : 2230071 : {
130 : 2230071 : bool add_tx = orphanage->AddTx(tx, peer_id);
131 : : // have_tx == true -> add_tx == false
132 [ - + ]: 2230071 : Assert(!have_tx || !add_tx);
133 : : // have_tx_and_peer == true -> add_tx == false
134 [ - + ]: 2230071 : Assert(!have_tx_and_peer || !add_tx);
135 : : // After AddTx, the orphanage may trim itself, so the peer's usage may have gone up or down.
136 : :
137 [ + + ]: 2230071 : if (add_tx) {
138 [ - + ]: 114467 : Assert(tx_weight <= MAX_STANDARD_TX_WEIGHT);
139 : : } else {
140 : : // Peer may have been added as an announcer.
141 [ + + ]: 2115604 : if (orphanage->UsageByPeer(peer_id) > total_peer_bytes_start) {
142 [ - + ]: 230484 : Assert(orphanage->HaveTxFromPeer(wtxid, peer_id));
143 : : }
144 : :
145 : : // If announcement was added, total bytes does not increase.
146 : : // However, if eviction was triggered, the value may decrease.
147 [ - + ]: 2115604 : Assert(orphanage->TotalOrphanUsage() <= total_bytes_start);
148 : : }
149 : : }
150 : : // We are not guaranteed to have_tx after AddTx. There are a few possible reasons:
151 : : // - tx itself exceeds the per-peer memory usage limit, so LimitOrphans had to remove it immediately
152 : : // - tx itself exceeds the per-peer latency score limit, so LimitOrphans had to remove it immediately
153 : : // - the orphanage needed trim and all other announcements from this peer are reconsiderable
154 : 2230071 : },
155 : 6132 : [&] {
156 : 6132 : bool have_tx = orphanage->HaveTx(tx->GetWitnessHash());
157 : 6132 : bool have_tx_and_peer = orphanage->HaveTxFromPeer(tx->GetWitnessHash(), peer_id);
158 : : // AddAnnouncer should return false if tx doesn't exist or we already HaveTxFromPeer.
159 : 6132 : {
160 : 6132 : bool added_announcer = orphanage->AddAnnouncer(tx->GetWitnessHash(), peer_id);
161 : : // have_tx == false -> added_announcer == false
162 [ - + ]: 6132 : Assert(have_tx || !added_announcer);
163 : : // have_tx_and_peer == true -> added_announcer == false
164 [ - + ]: 6132 : Assert(!have_tx_and_peer || !added_announcer);
165 : :
166 : : // If announcement was added, total bytes does not increase.
167 : : // However, if eviction was triggered, the value may decrease.
168 [ - + ]: 6132 : Assert(orphanage->TotalOrphanUsage() <= total_bytes_start);
169 : : }
170 : 6132 : },
171 : 138274 : [&] {
172 : 138274 : bool have_tx = orphanage->HaveTx(tx->GetWitnessHash());
173 : 138274 : bool have_tx_and_peer{orphanage->HaveTxFromPeer(wtxid, peer_id)};
174 : : // EraseTx should return 0 if m_orphans doesn't have the tx
175 : 138274 : {
176 : 138274 : auto bytes_from_peer_before{orphanage->UsageByPeer(peer_id)};
177 [ - + ]: 138274 : Assert(have_tx == orphanage->EraseTx(tx->GetWitnessHash()));
178 : : // After EraseTx, the orphanage may trim itself, so all peers' usage may have gone up or down.
179 [ + + ]: 138274 : if (have_tx) {
180 [ + + ]: 66281 : if (!have_tx_and_peer) {
181 [ - + ]: 63528 : Assert(orphanage->UsageByPeer(peer_id) == bytes_from_peer_before);
182 : : }
183 : : }
184 : : }
185 : 138274 : have_tx = orphanage->HaveTx(tx->GetWitnessHash());
186 : 138274 : have_tx_and_peer = orphanage->HaveTxFromPeer(wtxid, peer_id);
187 : : // have_tx should be false and EraseTx should fail
188 : 138274 : {
189 [ + - - + : 138274 : Assert(!have_tx && !have_tx_and_peer && !orphanage->EraseTx(wtxid));
- + ]
190 : : }
191 : 138274 : },
192 : 12476 : [&] {
193 : 12476 : orphanage->EraseForPeer(peer_id);
194 [ - + ]: 12476 : Assert(!orphanage->HaveTxFromPeer(tx->GetWitnessHash(), peer_id));
195 [ - + ]: 12476 : Assert(orphanage->UsageByPeer(peer_id) == 0);
196 : 12476 : },
197 : 8597 : [&] {
198 : : // Make a block out of txs and then EraseForBlock
199 : 8597 : CBlock block;
200 : 8597 : int num_txs = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(0, 1000);
201 [ + + ]: 1390842 : for (int i{0}; i < num_txs; ++i) {
202 : 1382245 : auto& tx_to_remove = PickValue(fuzzed_data_provider, tx_history);
203 [ + - ]: 1382245 : block.vtx.push_back(tx_to_remove);
204 : : }
205 [ + - ]: 8597 : orphanage->EraseForBlock(block);
206 [ + + ]: 1390842 : for (const auto& tx_removed : block.vtx) {
207 [ + - - + ]: 1382245 : Assert(!orphanage->HaveTx(tx_removed->GetWitnessHash()));
208 [ + - - + ]: 1382245 : Assert(!orphanage->HaveTxFromPeer(tx_removed->GetWitnessHash(), peer_id));
209 : : }
210 : 8597 : }
211 : : );
212 : : }
213 : :
214 : : // Set tx as potential parent to be used for future GetChildren() calls.
215 [ + + + + ]: 123471 : if (!ptx_potential_parent || fuzzed_data_provider.ConsumeBool()) {
216 : 38904 : ptx_potential_parent = tx;
217 : : }
218 : :
219 [ + - ]: 62027 : const bool have_tx{orphanage->HaveTx(tx->GetWitnessHash())};
220 [ + - + + ]: 62027 : const bool get_tx_nonnull{orphanage->GetTx(tx->GetWitnessHash()) != nullptr};
221 [ - + + - ]: 62027 : Assert(have_tx == get_tx_nonnull);
222 : 62027 : }
223 [ + - ]: 591 : orphanage->SanityCheck();
224 [ + + ]: 1174 : }
225 : :
226 [ + - ]: 921 : FUZZ_TARGET(txorphan_protected, .init = initialize_orphanage)
227 : : {
228 : 463 : SeedRandomStateForTest(SeedRand::ZEROS);
229 : 463 : FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
230 : 463 : FastRandomContext orphanage_rng{ConsumeUInt256(fuzzed_data_provider)};
231 [ + - ]: 463 : NodeClockContext clock_ctx{ConsumeTime(fuzzed_data_provider)};
232 : :
233 : : // We have num_peers peers. Some subset of them will never exceed their reserved weight or announcement count, and
234 : : // should therefore never have any orphans evicted.
235 : 463 : const unsigned int MAX_PEERS = 125;
236 : 463 : const unsigned int num_peers = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(1, MAX_PEERS);
237 : : // Generate a vector of bools for whether each peer is protected from eviction
238 : 463 : std::bitset<MAX_PEERS> protected_peers;
239 [ + + ]: 10295 : for (unsigned int i = 0; i < num_peers; i++) {
240 [ + - ]: 9832 : protected_peers.set(i, fuzzed_data_provider.ConsumeBool());
241 : : }
242 : :
243 : : // Params for orphanage.
244 : 463 : const unsigned int global_latency_score_limit = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(num_peers, 6'000);
245 : 463 : const int64_t per_peer_weight_reservation = fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(1, 4'040'000);
246 : 463 : auto orphanage = node::MakeTxOrphanage(global_latency_score_limit, per_peer_weight_reservation);
247 : :
248 : : // The actual limit, MaxPeerLatencyScore(), may be higher, since TxOrphanage only counts peers
249 : : // that have announced an orphan. The honest peer will not experience evictions if it never
250 : : // exceeds this.
251 : 463 : const unsigned int honest_latency_limit = global_latency_score_limit / num_peers;
252 : : // Honest peer will not experience evictions if it never exceeds this.
253 : 463 : const int64_t honest_mem_limit = per_peer_weight_reservation;
254 : :
255 : 463 : std::vector<COutPoint> outpoints; // Duplicates are tolerated
256 [ + - ]: 463 : outpoints.reserve(400);
257 : :
258 : : // initial outpoints used to construct transactions later
259 [ + + ]: 2315 : for (uint8_t i = 0; i < 4; i++) {
260 [ + - ]: 1852 : outpoints.emplace_back(Txid::FromUint256(uint256{i}), 0);
261 : : }
262 : :
263 : : // These are honest peer's live announcements. We expect them to be protected from eviction.
264 : 463 : std::set<Wtxid> protected_wtxids;
265 : :
266 [ - + + + : 9590 : LIMITED_WHILE(outpoints.size() < 400 && fuzzed_data_provider.ConsumeBool(), 1000)
+ + - + ]
267 : : {
268 : : // construct transaction
269 : 8682 : const CTransactionRef tx = [&] {
270 : 4341 : CMutableTransaction tx_mut;
271 [ - + ]: 4341 : const auto num_in = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, outpoints.size());
272 : 4341 : const auto num_out = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, 256);
273 : : // pick outpoints from outpoints as input. We allow input duplicates on purpose, given we are not
274 : : // running any transaction validation logic before adding transactions to the orphanage
275 [ + - ]: 4341 : tx_mut.vin.reserve(num_in);
276 [ + + ]: 61738 : for (uint32_t i = 0; i < num_in; i++) {
277 : 57397 : auto& prevout = PickValue(fuzzed_data_provider, outpoints);
278 : : // try making transactions unique by setting a random nSequence, but allow duplicate transactions if they happen
279 [ + - ]: 114794 : tx_mut.vin.emplace_back(prevout, CScript{}, fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(0, CTxIn::SEQUENCE_FINAL));
280 : : }
281 : : // output amount or spendability will not affect txorphanage
282 [ + - ]: 4341 : tx_mut.vout.reserve(num_out);
283 [ + + ]: 44409 : for (uint32_t i = 0; i < num_out; i++) {
284 : 40068 : const auto payload_size = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(0, 100000);
285 [ + + ]: 40068 : if (payload_size) {
286 [ + - + - : 85131 : tx_mut.vout.emplace_back(0, CScript() << OP_RETURN << std::vector<unsigned char>(payload_size));
+ - ]
287 : : } else {
288 [ + - ]: 23382 : tx_mut.vout.emplace_back(0, CScript{});
289 : : }
290 : : }
291 [ + - ]: 4341 : auto new_tx = MakeTransactionRef(tx_mut);
292 : : // add newly constructed outpoints to the coin pool
293 [ + + ]: 44409 : for (uint32_t i = 0; i < num_out; i++) {
294 [ + - ]: 40068 : outpoints.emplace_back(new_tx->GetHash(), i);
295 : : }
296 : 4341 : return new_tx;
297 [ + - ]: 8682 : }();
298 : :
299 : 4341 : const auto wtxid{tx->GetWitnessHash()};
300 : :
301 : : // orphanage functions
302 [ + + + + ]: 2053420 : LIMITED_WHILE(fuzzed_data_provider.remaining_bytes(), 10 * global_latency_score_limit)
303 : : {
304 : 2049079 : NodeId peer_id = fuzzed_data_provider.ConsumeIntegralInRange<NodeId>(0, num_peers - 1);
305 : 2049079 : const auto tx_weight{GetTransactionWeight(*tx)};
306 : :
307 : : // This protected peer will never send orphans that would
308 : : // exceed their own personal allotment, so is never evicted.
309 [ + - ]: 2049079 : const bool peer_is_protected{protected_peers[peer_id]};
310 : :
311 [ + - ]: 2049079 : CallOneOf(
312 : : fuzzed_data_provider,
313 : 651053 : [&] { // AddTx
314 : 651053 : bool have_tx_and_peer = orphanage->HaveTxFromPeer(wtxid, peer_id);
315 [ + + + + ]: 651053 : if (peer_is_protected && !have_tx_and_peer &&
316 [ + + ]: 160670 : (orphanage->UsageByPeer(peer_id) + tx_weight > honest_mem_limit ||
317 [ - + + + ]: 85345 : orphanage->LatencyScoreFromPeer(peer_id) + (tx->vin.size() / 10) + 1 > honest_latency_limit)) {
318 : : // We never want our protected peer oversized or over-announced
319 : : } else {
320 : 570308 : orphanage->AddTx(tx, peer_id);
321 [ + + + + ]: 570308 : if (peer_is_protected && orphanage->HaveTxFromPeer(wtxid, peer_id)) {
322 : 182923 : protected_wtxids.insert(wtxid);
323 : : }
324 : : }
325 : 651053 : },
326 : 719572 : [&] { // AddAnnouncer
327 : 719572 : bool have_tx_and_peer = orphanage->HaveTxFromPeer(tx->GetWitnessHash(), peer_id);
328 : : // AddAnnouncer should return false if tx doesn't exist or we already HaveTxFromPeer.
329 : 719572 : {
330 [ + + + + ]: 719572 : if (peer_is_protected && !have_tx_and_peer &&
331 [ + + ]: 282814 : (orphanage->UsageByPeer(peer_id) + tx_weight > honest_mem_limit ||
332 [ - + + + ]: 151025 : orphanage->LatencyScoreFromPeer(peer_id) + (tx->vin.size() / 10) + 1 > honest_latency_limit)) {
333 : : // We never want our protected peer oversized
334 : : } else {
335 : 583643 : orphanage->AddAnnouncer(tx->GetWitnessHash(), peer_id);
336 [ + + + + ]: 583643 : if (peer_is_protected && orphanage->HaveTxFromPeer(wtxid, peer_id)) {
337 : 159182 : protected_wtxids.insert(wtxid);
338 : : }
339 : : }
340 : : }
341 : 719572 : },
342 : 275120 : [&] { // EraseTx
343 [ + + ]: 275120 : if (protected_wtxids.contains(tx->GetWitnessHash())) {
344 : 55846 : protected_wtxids.erase(wtxid);
345 : : }
346 : 275120 : orphanage->EraseTx(wtxid);
347 [ - + ]: 275120 : Assert(!orphanage->HaveTx(wtxid));
348 : 275120 : },
349 : 403334 : [&] { // EraseForPeer
350 [ + + ]: 403334 : if (!protected_peers[peer_id]) {
351 : 173914 : orphanage->EraseForPeer(peer_id);
352 [ - + ]: 173914 : Assert(orphanage->UsageByPeer(peer_id) == 0);
353 [ - + ]: 173914 : Assert(orphanage->LatencyScoreFromPeer(peer_id) == 0);
354 [ - + ]: 173914 : Assert(orphanage->AnnouncementsFromPeer(peer_id) == 0);
355 : : }
356 : 403334 : }
357 : : );
358 : : }
359 : 4341 : }
360 : :
361 [ + - ]: 463 : orphanage->SanityCheck();
362 : : // All of the honest peer's announcements are still present.
363 [ + + ]: 849 : for (const auto& wtxid : protected_wtxids) {
364 [ + - - + ]: 386 : Assert(orphanage->HaveTx(wtxid));
365 : : }
366 : 463 : }
367 : :
368 [ + - ]: 1581 : FUZZ_TARGET(txorphanage_sim)
369 : : {
370 : 1123 : SeedRandomStateForTest(SeedRand::ZEROS);
371 : : // This is a comprehensive simulation fuzz test, which runs through a scenario involving up to
372 : : // 16 transactions (which may have simple or complex topology, and may have duplicate txids
373 : : // with distinct wtxids, and up to 16 peers. The scenario is performed both on a real
374 : : // TxOrphanage object and the behavior is compared with a naive reimplementation (just a vector
375 : : // of announcements) where possible, and tested for desired properties where not possible.
376 : :
377 : : //
378 : : // 1. Setup.
379 : : //
380 : :
381 : : /** The total number of transactions this simulation uses (not all of which will necessarily
382 : : * be present in the orphanage at once). */
383 : 1123 : static constexpr unsigned NUM_TX = 16;
384 : : /** The number of peers this simulation uses (not all of which will necessarily be present in
385 : : * the orphanage at once). */
386 : 1123 : static constexpr unsigned NUM_PEERS = 16;
387 : : /** The maximum number of announcements this simulation uses (which may be higher than the
388 : : * number permitted inside the orphanage). */
389 : 1123 : static constexpr unsigned MAX_ANN = 64;
390 : :
391 : 1123 : FuzzedDataProvider provider(buffer.data(), buffer.size());
392 : : /** Local RNG. Only used for topology/sizes of the transaction set, the order of transactions
393 : : * in EraseForBlock, and for the randomized passed to AddChildrenToWorkSet. */
394 : 1123 : InsecureRandomContext rng(provider.ConsumeIntegral<uint64_t>());
395 : :
396 : : //
397 : : // 2. Construct an interesting set of 16 transactions.
398 : : //
399 : :
400 : : // - Pick a topological order among the transactions.
401 : 1123 : std::vector<unsigned> txorder(NUM_TX);
402 : 1123 : std::iota(txorder.begin(), txorder.end(), unsigned{0});
403 : 1123 : std::shuffle(txorder.begin(), txorder.end(), rng);
404 : : // - Pick a set of dependencies (pair<child_index, parent_index>).
405 : 1123 : std::vector<std::pair<unsigned, unsigned>> deps;
406 [ + - ]: 1123 : deps.reserve((NUM_TX * (NUM_TX - 1)) / 2);
407 [ + + ]: 17968 : for (unsigned p = 0; p < NUM_TX - 1; ++p) {
408 [ + + ]: 151605 : for (unsigned c = p + 1; c < NUM_TX; ++c) {
409 [ + - ]: 134760 : deps.emplace_back(c, p);
410 : : }
411 : : }
412 : 1123 : std::shuffle(deps.begin(), deps.end(), rng);
413 [ + - ]: 1123 : deps.resize(provider.ConsumeIntegralInRange<unsigned>(0, NUM_TX * 4 - 1));
414 : : // - Construct the actual transactions.
415 [ + - ]: 1123 : std::set<Wtxid> wtxids;
416 [ + - ]: 1123 : std::vector<CTransactionRef> txn(NUM_TX);
417 : : node::TxOrphanage::Usage total_usage{0};
418 [ + + ]: 19091 : for (unsigned t = 0; t < NUM_TX; ++t) {
419 [ + - ]: 17968 : CMutableTransaction tx;
420 [ + + + + ]: 17968 : if (t > 0 && rng.randrange(4) == 0) {
421 : : // Occasionally duplicate the previous transaction, so that repetitions of the same
422 : : // txid are possible (with different wtxid).
423 [ + - ]: 16874 : tx = CMutableTransaction(*txn[txorder[t - 1]]);
424 : : } else {
425 : 9531 : tx.version = 1;
426 : 9531 : tx.nLockTime = 0xffffffff;
427 : : // Construct 1 to 16 outputs.
428 : 9531 : auto num_outputs = rng.randrange<unsigned>(1 << rng.randrange<unsigned>(5)) + 1;
429 [ + + ]: 38767 : for (unsigned output = 0; output < num_outputs; ++output) {
430 : 29236 : CScript scriptpubkey;
431 : 29236 : scriptpubkey.resize(provider.ConsumeIntegralInRange<unsigned>(20, 34));
432 [ + - ]: 29236 : tx.vout.emplace_back(CAmount{0}, std::move(scriptpubkey));
433 : 29236 : }
434 : : // Construct inputs (one for each dependency).
435 [ + + + + ]: 316252 : for (auto& [child, parent] : deps) {
436 [ + + ]: 306721 : if (child == t) {
437 [ - + ]: 17772 : auto& partx = txn[txorder[parent]];
438 [ - + ]: 17772 : assert(partx->version == 1);
439 [ - + + - ]: 17772 : COutPoint outpoint(partx->GetHash(), rng.randrange<size_t>(partx->vout.size()));
440 [ + - ]: 17772 : tx.vin.emplace_back(outpoint);
441 : 17772 : tx.vin.back().scriptSig.resize(provider.ConsumeIntegralInRange<unsigned>(16, 200));
442 : : }
443 : : }
444 : : // Construct fallback input in case there are no dependencies.
445 [ + + ]: 9531 : if (tx.vin.empty()) {
446 [ + - ]: 4285 : COutPoint outpoint(Txid::FromUint256(rng.rand256()), rng.randrange<size_t>(16));
447 [ + - ]: 4285 : tx.vin.emplace_back(outpoint);
448 : 4285 : tx.vin.back().scriptSig.resize(provider.ConsumeIntegralInRange<unsigned>(16, 200));
449 : : }
450 : : }
451 : : // Optionally modify the witness (allowing wtxid != txid), and certainly when the wtxid
452 : : // already exists.
453 [ + - + + : 90030 : while (wtxids.contains(CTransaction(tx).GetWitnessHash()) || rng.randrange(4) == 0) {
+ + + + ]
454 [ - + ]: 27047 : auto& input = tx.vin[rng.randrange(tx.vin.size())];
455 [ + + ]: 27047 : if (rng.randbool()) {
456 [ + - ]: 12713 : input.scriptWitness.stack.resize(1);
457 [ + - ]: 12713 : input.scriptWitness.stack[0].resize(rng.randrange(100));
458 : : } else {
459 [ + - ]: 14334 : input.scriptWitness.stack.resize(0);
460 : : }
461 : : }
462 : : // Convert to CTransactionRef.
463 [ + - - + ]: 35936 : txn[txorder[t]] = MakeTransactionRef(std::move(tx));
464 [ + - ]: 17968 : wtxids.insert(txn[txorder[t]]->GetWitnessHash());
465 : 17968 : auto weight = GetTransactionWeight(*txn[txorder[t]]);
466 [ - + ]: 17968 : assert(weight < MAX_STANDARD_TX_WEIGHT);
467 : 17968 : total_usage += GetTransactionWeight(*txn[txorder[t]]);
468 : 17968 : }
469 : :
470 : : //
471 : : // 3. Initialize real orphanage
472 : : //
473 : :
474 : 1123 : auto max_global_latency_score = provider.ConsumeIntegralInRange<node::TxOrphanage::Count>(NUM_PEERS, MAX_ANN);
475 : 1123 : auto reserved_peer_usage = provider.ConsumeIntegralInRange<node::TxOrphanage::Usage>(1, total_usage);
476 : 1123 : auto real = node::MakeTxOrphanage(max_global_latency_score, reserved_peer_usage);
477 : :
478 : : //
479 : : // 4. Functions and data structures for the simulation.
480 : : //
481 : :
482 : : /** Data structure representing one announcement (pair of (tx, peer), plus whether it's
483 : : * reconsiderable or not. */
484 : 1123 : struct SimAnnouncement
485 : : {
486 : : unsigned tx;
487 : : NodeId announcer;
488 : : bool reconsider{false};
489 : 46478 : SimAnnouncement(unsigned tx_in, NodeId announcer_in, bool reconsider_in) noexcept :
490 : 46478 : tx(tx_in), announcer(announcer_in), reconsider(reconsider_in) {}
491 : : };
492 : : /** The entire simulated orphanage is represented by this list of announcements, in
493 : : * announcement order (unlike TxOrphanageImpl which uses a sequence number to represent
494 : : * announcement order). New announcements are added to the back. */
495 : 1123 : std::vector<SimAnnouncement> sim_announcements;
496 : :
497 : : /** Consume a transaction (index into txn) from provider. */
498 : 16415 : auto read_tx_fn = [&]() -> unsigned { return provider.ConsumeIntegralInRange<unsigned>(0, NUM_TX - 1); };
499 : : /** Consume a NodeId from provider. */
500 : 34757 : auto read_peer_fn = [&]() -> NodeId { return provider.ConsumeIntegralInRange<unsigned>(0, NUM_PEERS - 1); };
501 : : /** Consume both a transaction (index into txn) and a NodeId from provider. */
502 : 68167 : auto read_tx_peer_fn = [&]() -> std::pair<unsigned, NodeId> {
503 : 67044 : auto code = provider.ConsumeIntegralInRange<unsigned>(0, NUM_TX * NUM_PEERS - 1);
504 : 67044 : return {code % NUM_TX, code / NUM_TX};
505 : 1123 : };
506 : : /** Determine if we have any announcements of the given transaction in the simulation. */
507 : 2246423 : auto have_tx_fn = [&](unsigned tx) -> bool {
508 [ + + ]: 13303685 : for (auto& ann : sim_announcements) {
509 [ + + ]: 11750041 : if (ann.tx == tx) return true;
510 : : }
511 : : return false;
512 : 1123 : };
513 : : /** Count the number of peers in the simulation. */
514 : 234661 : auto count_peers_fn = [&]() -> unsigned {
515 : 233538 : std::bitset<NUM_PEERS> mask;
516 [ + + ]: 2058827 : for (auto& ann : sim_announcements) {
517 : 1825289 : mask.set(ann.announcer);
518 : : }
519 : 233538 : return mask.count();
520 : 1123 : };
521 : : /** Determine if we have any reconsiderable announcements of a given transaction. */
522 : 47712 : auto have_reconsiderable_fn = [&](unsigned tx) -> bool {
523 [ + + ]: 546796 : for (auto& ann : sim_announcements) {
524 [ + + + + ]: 516610 : if (ann.reconsider && ann.tx == tx) return true;
525 : : }
526 : : return false;
527 : 1123 : };
528 : : /** Determine if a peer has any transactions to reconsider. */
529 : 24171 : auto have_reconsider_fn = [&](NodeId peer) -> bool {
530 [ + + ]: 177049 : for (auto& ann : sim_announcements) {
531 [ + + + + ]: 155208 : if (ann.reconsider && ann.announcer == peer) return true;
532 : : }
533 : : return false;
534 : 1123 : };
535 : : /** Get an iterator to an existing (wtxid, peer) pair in the simulation. */
536 : 17159 : auto find_announce_wtxid_fn = [&](const Wtxid& wtxid, NodeId peer) -> std::vector<SimAnnouncement>::iterator {
537 [ + - ]: 92358 : for (auto it = sim_announcements.begin(); it != sim_announcements.end(); ++it) {
538 [ + + + + ]: 92358 : if (txn[it->tx]->GetWitnessHash() == wtxid && it->announcer == peer) return it;
539 : : }
540 : 0 : return sim_announcements.end();
541 : 1123 : };
542 : : /** Get an iterator to an existing (tx, peer) pair in the simulation. */
543 : 355655 : auto find_announce_fn = [&](unsigned tx, NodeId peer) {
544 [ + + ]: 2848164 : for (auto it = sim_announcements.begin(); it != sim_announcements.end(); ++it) {
545 [ + + + + ]: 2515545 : if (it->tx == tx && it->announcer == peer) return it;
546 : : }
547 : 332619 : return sim_announcements.end();
548 : 1123 : };
549 : : /** Compute a peer's DoS score according to simulation data. */
550 : 293235 : auto dos_score_fn = [&](NodeId peer, int32_t max_count, int32_t max_usage) -> FeeFrac {
551 : 292112 : int64_t count{0};
552 : 292112 : int64_t usage{0};
553 [ + + ]: 2466176 : for (auto& ann : sim_announcements) {
554 [ + + ]: 2174064 : if (ann.announcer != peer) continue;
555 [ - + ]: 135879 : count += 1 + (txn[ann.tx]->vin.size() / 10);
556 : 135879 : usage += GetTransactionWeight(*txn[ann.tx]);
557 : : }
558 : 292112 : return std::max(FeeFrac{count, max_count}, FeeFrac{usage, max_usage});
559 : 1123 : };
560 : :
561 : : //
562 : : // 5. Run through a scenario of mutators on both real and simulated orphanage.
563 : : //
564 : :
565 [ + + + + ]: 108202 : LIMITED_WHILE(provider.remaining_bytes() > 0, 200) {
566 : 107079 : int command = provider.ConsumeIntegralInRange<uint8_t>(0, 15);
567 : 152431 : while (true) {
568 [ - + + + : 152431 : if (sim_announcements.size() < MAX_ANN && command-- == 0) {
+ + ]
569 : : // AddTx
570 [ + - ]: 52938 : auto [tx, peer] = read_tx_peer_fn();
571 [ + - ]: 52938 : bool added = real->AddTx(txn[tx], peer);
572 : 52938 : bool sim_have_tx = have_tx_fn(tx);
573 [ - + ]: 52938 : assert(added == !sim_have_tx);
574 [ + + ]: 52938 : if (find_announce_fn(tx, peer) == sim_announcements.end()) {
575 [ + - ]: 42870 : sim_announcements.emplace_back(tx, peer, false);
576 : : }
577 : : break;
578 [ + + + + ]: 99493 : } else if (sim_announcements.size() < MAX_ANN && command-- == 0) {
579 : : // AddAnnouncer
580 [ + - ]: 14106 : auto [tx, peer] = read_tx_peer_fn();
581 [ + - ]: 14106 : bool added = real->AddAnnouncer(txn[tx]->GetWitnessHash(), peer);
582 : 14106 : bool sim_have_tx = have_tx_fn(tx);
583 : 14106 : auto sim_it = find_announce_fn(tx, peer);
584 [ + + + + : 24604 : assert(added == (sim_it == sim_announcements.end() && sim_have_tx));
- + ]
585 [ + + ]: 14106 : if (added) {
586 [ + - ]: 3608 : sim_announcements.emplace_back(tx, peer, false);
587 : : }
588 : : break;
589 [ + + ]: 85387 : } else if (command-- == 0) {
590 : : // EraseTx
591 : 5984 : auto tx = read_tx_fn();
592 [ + - ]: 5984 : bool erased = real->EraseTx(txn[tx]->GetWitnessHash());
593 : 5984 : bool sim_have = have_tx_fn(tx);
594 [ - + ]: 5984 : assert(erased == sim_have);
595 [ + + + + ]: 49167 : std::erase_if(sim_announcements, [&](auto& ann) { return ann.tx == tx; });
596 : : break;
597 [ + + ]: 79403 : } else if (command-- == 0) {
598 : : // EraseForPeer
599 : 6070 : auto peer = read_peer_fn();
600 [ + - ]: 6070 : real->EraseForPeer(peer);
601 [ + + + + ]: 39828 : std::erase_if(sim_announcements, [&](auto& ann) { return ann.announcer == peer; });
602 : : break;
603 [ + + ]: 73333 : } else if (command-- == 0) {
604 : : // EraseForBlock
605 : 7926 : auto pattern = provider.ConsumeIntegralInRange<uint64_t>(0, (uint64_t{1} << NUM_TX) - 1);
606 : 7926 : CBlock block;
607 : 7926 : std::set<COutPoint> spent;
608 [ + + ]: 134742 : for (unsigned tx = 0; tx < NUM_TX; ++tx) {
609 [ + + ]: 126816 : if ((pattern >> tx) & 1) {
610 [ + - ]: 38862 : block.vtx.emplace_back(txn[tx]);
611 [ + + ]: 127748 : for (auto& txin : block.vtx.back()->vin) {
612 [ + - ]: 88886 : spent.insert(txin.prevout);
613 : : }
614 : : }
615 : : }
616 : 7926 : std::shuffle(block.vtx.begin(), block.vtx.end(), rng);
617 [ + - ]: 7926 : real->EraseForBlock(block);
618 : 38520 : std::erase_if(sim_announcements, [&](auto& ann) {
619 [ + + ]: 64023 : for (auto& txin : txn[ann.tx]->vin) {
620 [ + + ]: 47635 : if (spent.contains(txin.prevout)) return true;
621 : : }
622 : : return false;
623 : : });
624 : 7926 : break;
625 [ + + ]: 73333 : } else if (command-- == 0) {
626 : : // AddChildrenToWorkSet
627 : 9308 : auto tx = read_tx_fn();
628 : 9308 : FastRandomContext rand_ctx(rng.rand256());
629 [ + - ]: 9308 : auto added = real->AddChildrenToWorkSet(*txn[tx], rand_ctx);
630 : : /** Set of not-already-reconsiderable child wtxids. */
631 : 9308 : std::set<Wtxid> child_wtxids;
632 [ + + ]: 158236 : for (unsigned child_tx = 0; child_tx < NUM_TX; ++child_tx) {
633 [ + + ]: 148928 : if (!have_tx_fn(child_tx)) continue;
634 [ + + ]: 46589 : if (have_reconsiderable_fn(child_tx)) continue;
635 : 30186 : bool child_of = false;
636 [ + + ]: 73687 : for (auto& txin : txn[child_tx]->vin) {
637 [ + + ]: 53870 : if (txin.prevout.hash == txn[tx]->GetHash()) {
638 : : child_of = true;
639 : : break;
640 : : }
641 : : }
642 [ + + ]: 30186 : if (child_of) {
643 [ + - ]: 10369 : child_wtxids.insert(txn[child_tx]->GetWitnessHash());
644 : : }
645 : : }
646 [ + + ]: 19677 : for (auto& [wtxid, peer] : added) {
647 : : // Wtxid must be a child of tx that is not yet reconsiderable.
648 : 10369 : auto child_wtxid_it = child_wtxids.find(wtxid);
649 [ - + ]: 10369 : assert(child_wtxid_it != child_wtxids.end());
650 : : // Announcement must exist.
651 : 10369 : auto sim_ann_it = find_announce_wtxid_fn(wtxid, peer);
652 [ - + ]: 10369 : assert(sim_ann_it != sim_announcements.end());
653 : : // Announcement must not yet be reconsiderable.
654 [ - + ]: 10369 : assert(sim_ann_it->reconsider == false);
655 : : // Make reconsiderable.
656 : 10369 : sim_ann_it->reconsider = true;
657 : : // Remove from child_wtxids map, to disallow it being reported a second time in added.
658 : 10369 : child_wtxids.erase(wtxid);
659 : : }
660 : : // Verify that AddChildrenToWorkSet does not select announcements that were already reconsiderable:
661 : : // Check all child wtxids which did not occur at least once in the result were already reconsiderable
662 : : // due to a previous AddChildrenToWorkSet.
663 [ - + ]: 9308 : assert(child_wtxids.empty());
664 : 9308 : break;
665 [ + + ]: 65407 : } else if (command-- == 0) {
666 : : // GetTxToReconsider.
667 : 10747 : auto peer = read_peer_fn();
668 [ + - ]: 10747 : auto result = real->GetTxToReconsider(peer);
669 [ + + ]: 10747 : if (result) {
670 : : // A transaction was found. It must have a corresponding reconsiderable
671 : : // announcement from peer.
672 : 5667 : auto sim_ann_it = find_announce_wtxid_fn(result->GetWitnessHash(), peer);
673 [ - + ]: 5667 : assert(sim_ann_it != sim_announcements.end());
674 [ - + ]: 5667 : assert(sim_ann_it->announcer == peer);
675 [ - + ]: 5667 : assert(sim_ann_it->reconsider);
676 : : // Make it non-reconsiderable.
677 : 5667 : sim_ann_it->reconsider = false;
678 : : } else {
679 : : // No reconsiderable transaction was found from peer. Verify that it does not
680 : : // have any.
681 [ - + ]: 5080 : assert(!have_reconsider_fn(peer));
682 : : }
683 [ + + ]: 10747 : break;
684 : 10747 : }
685 : : }
686 : : // Always trim after each command if needed.
687 [ + - + + ]: 107079 : const auto max_ann = max_global_latency_score / std::max<unsigned>(1, count_peers_fn());
688 : 107079 : const auto max_mem = reserved_peer_usage;
689 : 125336 : while (true) {
690 : : // Count global usage and number of peers.
691 : 125336 : node::TxOrphanage::Usage total_usage{0};
692 [ - + ]: 125336 : node::TxOrphanage::Count total_latency_score = sim_announcements.size();
693 [ + + ]: 2130712 : for (unsigned tx = 0; tx < NUM_TX; ++tx) {
694 [ + + ]: 2005376 : if (have_tx_fn(tx)) {
695 : 611481 : total_usage += GetTransactionWeight(*txn[tx]);
696 [ - + ]: 611481 : total_latency_score += txn[tx]->vin.size() / 10;
697 : : }
698 : : }
699 [ + - ]: 125336 : auto num_peers = count_peers_fn();
700 [ + + + + ]: 235713 : bool oversized = (total_usage > reserved_peer_usage * num_peers) ||
701 [ + - ]: 110377 : (total_latency_score > real->MaxGlobalLatencyScore());
702 : 125336 : if (!oversized) break;
703 : : // Find worst peer.
704 : 18257 : FeeFrac worst_dos_score{0, 1};
705 : 18257 : unsigned worst_peer = unsigned(-1);
706 [ + + ]: 310369 : for (unsigned peer = 0; peer < NUM_PEERS; ++peer) {
707 : 292112 : auto dos_score = dos_score_fn(peer, max_ann, max_mem);
708 : : // Use >= so that the more recent peer (higher NodeId) wins in case of
709 : : // ties.
710 [ + + ]: 292112 : if (dos_score >= worst_dos_score) {
711 : 35129 : worst_dos_score = dos_score;
712 : 35129 : worst_peer = peer;
713 : : }
714 : : }
715 [ - + ]: 18257 : assert(worst_peer != unsigned(-1));
716 [ - + ]: 18257 : assert(worst_dos_score >> FeeFrac(1, 1));
717 : : // Find oldest announcement from worst_peer, preferring non-reconsiderable ones.
718 : : bool done{false};
719 [ + - ]: 18468 : for (int reconsider = 0; reconsider < 2; ++reconsider) {
720 [ + + ]: 103318 : for (auto it = sim_announcements.begin(); it != sim_announcements.end(); ++it) {
721 [ + + + + ]: 103107 : if (it->announcer != worst_peer || it->reconsider != reconsider) continue;
722 : 18257 : sim_announcements.erase(it);
723 : 18257 : done = true;
724 : 18257 : break;
725 : : }
726 : 18468 : if (done) break;
727 : : }
728 [ + - ]: 18257 : assert(done);
729 : : }
730 : : // We must now be within limits, otherwise LimitOrphans should have continued further.
731 : : // We don't check the contents of the orphanage until the end to make fuzz runs faster.
732 [ + - + - : 107079 : assert(real->TotalLatencyScore() <= real->MaxGlobalLatencyScore());
- + ]
733 [ + - + - : 107079 : assert(real->TotalOrphanUsage() <= real->MaxGlobalUsage());
- + ]
734 : : }
735 : :
736 : : //
737 : : // 6. Perform a full comparison between the real orphanage's inspectors and the simulation.
738 : : //
739 : :
740 [ + - ]: 1123 : real->SanityCheck();
741 : :
742 : :
743 [ + - ]: 1123 : auto all_orphans = real->GetOrphanTransactions();
744 : 1123 : node::TxOrphanage::Usage orphan_usage{0};
745 [ + - ]: 1123 : std::vector<node::TxOrphanage::Usage> usage_by_peer(NUM_PEERS);
746 : 1123 : node::TxOrphanage::Count unique_orphans{0};
747 [ + - ]: 1123 : std::vector<node::TxOrphanage::Count> count_by_peer(NUM_PEERS);
748 [ - + ]: 1123 : node::TxOrphanage::Count total_latency_score = sim_announcements.size();
749 [ + + ]: 19091 : for (unsigned tx = 0; tx < NUM_TX; ++tx) {
750 : 17968 : bool sim_have_tx = have_tx_fn(tx);
751 [ + + ]: 17968 : if (sim_have_tx) {
752 : 4511 : orphan_usage += GetTransactionWeight(*txn[tx]);
753 [ - + ]: 4511 : total_latency_score += txn[tx]->vin.size() / 10;
754 : : }
755 : 17968 : unique_orphans += sim_have_tx;
756 : 72263 : auto orphans_it = std::find_if(all_orphans.begin(), all_orphans.end(), [&](auto& orph) { return orph.tx->GetWitnessHash() == txn[tx]->GetWitnessHash(); });
757 : : // GetOrphanTransactions (OrphanBase existence)
758 [ - + ]: 17968 : assert((orphans_it != all_orphans.end()) == sim_have_tx);
759 : : // HaveTx
760 [ + - ]: 17968 : bool have_tx = real->HaveTx(txn[tx]->GetWitnessHash());
761 [ - + ]: 17968 : assert(have_tx == sim_have_tx);
762 : : // GetTx
763 [ + - ]: 17968 : auto txref = real->GetTx(txn[tx]->GetWitnessHash());
764 [ - + ]: 17968 : assert(!!txref == sim_have_tx);
765 [ + + - + ]: 17968 : if (sim_have_tx) assert(txref->GetWitnessHash() == txn[tx]->GetWitnessHash());
766 : :
767 [ + + ]: 305456 : for (NodeId peer = 0; peer < NUM_PEERS; ++peer) {
768 : 287488 : auto it_sim_ann = find_announce_fn(tx, peer);
769 [ + + ]: 287488 : bool sim_have_ann = it_sim_ann != sim_announcements.end();
770 [ + + ]: 287488 : if (sim_have_ann) usage_by_peer[peer] += GetTransactionWeight(*txn[tx]);
771 [ + + ]: 287488 : count_by_peer[peer] += sim_have_ann;
772 : : // GetOrphanTransactions (announcers presence)
773 [ + + - + ]: 287488 : if (sim_have_ann) assert(sim_have_tx);
774 [ + + - + ]: 287488 : if (sim_have_tx) assert(orphans_it->announcers.count(peer) == sim_have_ann);
775 : : // HaveTxFromPeer
776 [ + - ]: 287488 : bool have_ann = real->HaveTxFromPeer(txn[tx]->GetWitnessHash(), peer);
777 [ - + ]: 287488 : assert(sim_have_ann == have_ann);
778 : : // GetChildrenFromSamePeer
779 [ + - ]: 287488 : auto children_from_peer = real->GetChildrenFromSamePeer(txn[tx], peer);
780 : 287488 : auto it = children_from_peer.rbegin();
781 [ + + ]: 862464 : for (int phase = 0; phase < 2; ++phase) {
782 : : // First expect all children which have reconsiderable announcement from peer, then the others.
783 [ + + ]: 4837376 : for (auto& ann : sim_announcements) {
784 [ + + ]: 4262400 : if (ann.announcer != peer) continue;
785 [ + + ]: 266400 : if (ann.reconsider != (phase == 1)) continue;
786 : 133200 : bool matching_parent{false};
787 [ + + ]: 457232 : for (const auto& vin : txn[ann.tx]->vin) {
788 [ + + ]: 324032 : if (vin.prevout.hash == txn[tx]->GetHash()) matching_parent = true;
789 : : }
790 [ + + ]: 133200 : if (!matching_parent) continue;
791 : : // Found an announcement from peer which is a child of txn[tx].
792 [ - + ]: 28467 : assert(it != children_from_peer.rend());
793 [ - + ]: 28467 : assert((*it)->GetWitnessHash() == txn[ann.tx]->GetWitnessHash());
794 : 28467 : ++it;
795 : : }
796 : : }
797 [ - + ]: 287488 : assert(it == children_from_peer.rend());
798 : 287488 : }
799 : 17968 : }
800 : : // TotalOrphanUsage
801 [ + - - + ]: 1123 : assert(orphan_usage == real->TotalOrphanUsage());
802 [ + + ]: 19091 : for (NodeId peer = 0; peer < NUM_PEERS; ++peer) {
803 : 17968 : bool sim_have_reconsider = have_reconsider_fn(peer);
804 : : // HaveTxToReconsider
805 [ + - ]: 17968 : bool have_reconsider = real->HaveTxToReconsider(peer);
806 [ - + ]: 17968 : assert(have_reconsider == sim_have_reconsider);
807 : : // UsageByPeer
808 [ + - - + ]: 17968 : assert(usage_by_peer[peer] == real->UsageByPeer(peer));
809 : : // AnnouncementsFromPeer
810 [ + - - + ]: 17968 : assert(count_by_peer[peer] == real->AnnouncementsFromPeer(peer));
811 : : }
812 : : // CountAnnouncements
813 [ - + + - : 1123 : assert(sim_announcements.size() == real->CountAnnouncements());
- + ]
814 : : // CountUniqueOrphans
815 [ + - - + ]: 1123 : assert(unique_orphans == real->CountUniqueOrphans());
816 : : // MaxGlobalLatencyScore
817 [ + - - + ]: 1123 : assert(max_global_latency_score == real->MaxGlobalLatencyScore());
818 : : // ReservedPeerUsage
819 [ + - - + ]: 1123 : assert(reserved_peer_usage == real->ReservedPeerUsage());
820 : : // MaxPeerLatencyScore
821 [ + - ]: 1123 : auto present_peers = count_peers_fn();
822 [ + + + - : 1578 : assert(max_global_latency_score / std::max<unsigned>(1, present_peers) == real->MaxPeerLatencyScore());
- + ]
823 : : // MaxGlobalUsage
824 [ + + + - : 1578 : assert(reserved_peer_usage * std::max<unsigned>(1, present_peers) == real->MaxGlobalUsage());
- + ]
825 : : // TotalLatencyScore.
826 [ + - - + ]: 1123 : assert(real->TotalLatencyScore() == total_latency_score);
827 : 1123 : }
|