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 [ + - ]: 843 : FUZZ_TARGET(txorphan, .init = initialize_orphanage)
40 : : {
41 : 385 : SeedRandomStateForTest(SeedRand::ZEROS);
42 : 385 : FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
43 : 385 : FastRandomContext orphanage_rng{ConsumeUInt256(fuzzed_data_provider)};
44 [ + - ]: 385 : NodeClockContext clock_ctx{ConsumeTime(fuzzed_data_provider)};
45 : :
46 : 385 : auto orphanage = node::MakeTxOrphanage();
47 : 385 : std::vector<COutPoint> outpoints; // Duplicates are tolerated
48 [ + - ]: 385 : outpoints.reserve(200'000);
49 : :
50 : : // initial outpoints used to construct transactions later
51 [ + + ]: 1925 : for (uint8_t i = 0; i < 4; i++) {
52 [ + - ]: 1540 : outpoints.emplace_back(Txid::FromUint256(uint256{i}), 0);
53 : : }
54 : :
55 : 385 : CTransactionRef ptx_potential_parent = nullptr;
56 : :
57 : 385 : std::vector<CTransactionRef> tx_history;
58 : :
59 [ - + + - : 78718 : LIMITED_WHILE(outpoints.size() < 200'000 && fuzzed_data_provider.ConsumeBool(), 1000)
+ + + + ]
60 : : {
61 : : // construct transaction
62 : 77948 : const CTransactionRef tx = [&] {
63 : 38974 : CMutableTransaction tx_mut;
64 [ - + ]: 38974 : const auto num_in = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, outpoints.size());
65 : 38974 : 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 [ + - ]: 38974 : tx_mut.vin.reserve(num_in);
69 [ + + ]: 7782768 : for (uint32_t i = 0; i < num_in; i++) {
70 : 7743794 : 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 [ + - ]: 15487588 : 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 [ + - ]: 38974 : tx_mut.vout.reserve(num_out);
76 [ + + ]: 3534363 : for (uint32_t i = 0; i < num_out; i++) {
77 [ + - ]: 6990778 : tx_mut.vout.emplace_back(CAmount{0}, CScript{});
78 : : }
79 [ + - ]: 38974 : auto new_tx = MakeTransactionRef(tx_mut);
80 : : // add newly constructed outpoints to the coin pool
81 [ + + ]: 3534363 : for (uint32_t i = 0; i < num_out; i++) {
82 [ + - ]: 3495389 : outpoints.emplace_back(new_tx->GetHash(), i);
83 : : }
84 : 38974 : return new_tx;
85 [ + - ]: 77948 : }();
86 : :
87 [ + - ]: 38974 : tx_history.push_back(tx);
88 : :
89 [ + + ]: 38974 : 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 [ + + ]: 38974 : if (ptx_potential_parent) {
94 : : // Set up future GetTxToReconsider call.
95 [ + - ]: 38593 : 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 : 38593 : NodeId peer_id = fuzzed_data_provider.ConsumeIntegral<NodeId>();
99 [ + - + + ]: 96142 : for (const auto& child : orphanage->GetChildrenFromSamePeer(ptx_potential_parent, peer_id)) {
100 [ - + ]: 623149 : assert(std::any_of(child->vin.cbegin(), child->vin.cend(), [&](const auto& input) {
101 : : return input.prevout.hash == ptx_potential_parent->GetHash();
102 : : }));
103 : 38593 : }
104 : : }
105 : :
106 : : // trigger orphanage functions
107 [ + + + + ]: 2060887 : LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 1000)
108 : : {
109 : 2021913 : NodeId peer_id = fuzzed_data_provider.ConsumeIntegral<NodeId>();
110 [ + - ]: 2021913 : const auto total_bytes_start{orphanage->TotalOrphanUsage()};
111 [ + - ]: 2021913 : const auto total_peer_bytes_start{orphanage->UsageByPeer(peer_id)};
112 : 2021913 : const auto tx_weight{GetTransactionWeight(*tx)};
113 : :
114 [ + - ]: 2021913 : CallOneOf(
115 : : fuzzed_data_provider,
116 : 7370 : [&] {
117 : 7370 : {
118 : 7370 : CTransactionRef ref = orphanage->GetTxToReconsider(peer_id);
119 [ + + ]: 7370 : if (ref) {
120 [ + - - + : 7370 : Assert(orphanage->HaveTx(ref->GetWitnessHash()));
+ + ]
121 : : }
122 : 7370 : }
123 : 7370 : },
124 : 1911421 : [&] {
125 : 1911421 : bool have_tx = orphanage->HaveTx(tx->GetWitnessHash());
126 : 1911421 : 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 : 1911421 : {
130 : 1911421 : bool add_tx = orphanage->AddTx(tx, peer_id);
131 : : // have_tx == true -> add_tx == false
132 [ - + ]: 1911421 : Assert(!have_tx || !add_tx);
133 : : // have_tx_and_peer == true -> add_tx == false
134 [ - + ]: 1911421 : 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 [ + + ]: 1911421 : if (add_tx) {
138 [ - + ]: 71217 : Assert(tx_weight <= MAX_STANDARD_TX_WEIGHT);
139 : : } else {
140 : : // Peer may have been added as an announcer.
141 [ + + ]: 1840204 : if (orphanage->UsageByPeer(peer_id) > total_peer_bytes_start) {
142 [ - + ]: 132274 : 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 [ - + ]: 1840204 : 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 : 1911421 : },
155 : 4095 : [&] {
156 : 4095 : bool have_tx = orphanage->HaveTx(tx->GetWitnessHash());
157 : 4095 : 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 : 4095 : {
160 : 4095 : bool added_announcer = orphanage->AddAnnouncer(tx->GetWitnessHash(), peer_id);
161 : : // have_tx == false -> added_announcer == false
162 [ - + ]: 4095 : Assert(have_tx || !added_announcer);
163 : : // have_tx_and_peer == true -> added_announcer == false
164 [ - + ]: 4095 : 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 [ - + ]: 4095 : Assert(orphanage->TotalOrphanUsage() <= total_bytes_start);
169 : : }
170 : 4095 : },
171 : 85477 : [&] {
172 : 85477 : bool have_tx = orphanage->HaveTx(tx->GetWitnessHash());
173 : 85477 : bool have_tx_and_peer{orphanage->HaveTxFromPeer(wtxid, peer_id)};
174 : : // EraseTx should return 0 if m_orphans doesn't have the tx
175 : 85477 : {
176 : 85477 : auto bytes_from_peer_before{orphanage->UsageByPeer(peer_id)};
177 [ - + ]: 85477 : 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 [ + + ]: 85477 : if (have_tx) {
180 [ + + ]: 40296 : if (!have_tx_and_peer) {
181 [ - + ]: 38921 : Assert(orphanage->UsageByPeer(peer_id) == bytes_from_peer_before);
182 : : }
183 : : }
184 : : }
185 : 85477 : have_tx = orphanage->HaveTx(tx->GetWitnessHash());
186 : 85477 : have_tx_and_peer = orphanage->HaveTxFromPeer(wtxid, peer_id);
187 : : // have_tx should be false and EraseTx should fail
188 : 85477 : {
189 [ + - - + : 85477 : Assert(!have_tx && !have_tx_and_peer && !orphanage->EraseTx(wtxid));
- + ]
190 : : }
191 : 85477 : },
192 : 6841 : [&] {
193 : 6841 : orphanage->EraseForPeer(peer_id);
194 [ - + ]: 6841 : Assert(!orphanage->HaveTxFromPeer(tx->GetWitnessHash(), peer_id));
195 [ - + ]: 6841 : Assert(orphanage->UsageByPeer(peer_id) == 0);
196 : 6841 : },
197 : 6709 : [&] {
198 : : // Make a block out of txs and then EraseForBlock
199 : 6709 : CBlock block;
200 : 6709 : int num_txs = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(0, 1000);
201 [ + + ]: 1071314 : for (int i{0}; i < num_txs; ++i) {
202 : 1064605 : auto& tx_to_remove = PickValue(fuzzed_data_provider, tx_history);
203 [ + - ]: 1064605 : block.vtx.push_back(tx_to_remove);
204 : : }
205 [ + - ]: 6709 : orphanage->EraseForBlock(block);
206 [ + + ]: 1071314 : for (const auto& tx_removed : block.vtx) {
207 [ + - - + ]: 1064605 : Assert(!orphanage->HaveTx(tx_removed->GetWitnessHash()));
208 [ + - - + ]: 1064605 : Assert(!orphanage->HaveTxFromPeer(tx_removed->GetWitnessHash(), peer_id));
209 : : }
210 : 6709 : }
211 : : );
212 : : }
213 : :
214 : : // Set tx as potential parent to be used for future GetChildren() calls.
215 [ + + + + ]: 77567 : if (!ptx_potential_parent || fuzzed_data_provider.ConsumeBool()) {
216 : 22889 : ptx_potential_parent = tx;
217 : : }
218 : :
219 [ + - ]: 38974 : const bool have_tx{orphanage->HaveTx(tx->GetWitnessHash())};
220 [ + - + + ]: 38974 : const bool get_tx_nonnull{orphanage->GetTx(tx->GetWitnessHash()) != nullptr};
221 [ - + + - ]: 38974 : Assert(have_tx == get_tx_nonnull);
222 : 38974 : }
223 [ + - ]: 385 : orphanage->SanityCheck();
224 [ + + ]: 766 : }
225 : :
226 [ + - ]: 764 : FUZZ_TARGET(txorphan_protected, .init = initialize_orphanage)
227 : : {
228 : 306 : SeedRandomStateForTest(SeedRand::ZEROS);
229 : 306 : FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
230 : 306 : FastRandomContext orphanage_rng{ConsumeUInt256(fuzzed_data_provider)};
231 [ + - ]: 306 : 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 : 306 : const unsigned int MAX_PEERS = 125;
236 : 306 : 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 : 306 : std::bitset<MAX_PEERS> protected_peers;
239 [ + + ]: 6393 : for (unsigned int i = 0; i < num_peers; i++) {
240 [ + - ]: 6087 : protected_peers.set(i, fuzzed_data_provider.ConsumeBool());
241 : : }
242 : :
243 : : // Params for orphanage.
244 : 306 : const unsigned int global_latency_score_limit = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(num_peers, 6'000);
245 : 306 : const int64_t per_peer_weight_reservation = fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(1, 4'040'000);
246 : 306 : 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 : 306 : 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 : 306 : const int64_t honest_mem_limit = per_peer_weight_reservation;
254 : :
255 : 306 : std::vector<COutPoint> outpoints; // Duplicates are tolerated
256 [ + - ]: 306 : outpoints.reserve(400);
257 : :
258 : : // initial outpoints used to construct transactions later
259 [ + + ]: 1530 : for (uint8_t i = 0; i < 4; i++) {
260 [ + - ]: 1224 : 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 : 306 : std::set<Wtxid> protected_wtxids;
265 : :
266 [ - + + + : 5240 : LIMITED_WHILE(outpoints.size() < 400 && fuzzed_data_provider.ConsumeBool(), 1000)
+ + - + ]
267 : : {
268 : : // construct transaction
269 : 4638 : const CTransactionRef tx = [&] {
270 : 2319 : CMutableTransaction tx_mut;
271 [ - + ]: 2319 : const auto num_in = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, outpoints.size());
272 : 2319 : 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 [ + - ]: 2319 : tx_mut.vin.reserve(num_in);
276 [ + + ]: 34929 : for (uint32_t i = 0; i < num_in; i++) {
277 : 32610 : 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 [ + - ]: 65220 : 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 [ + - ]: 2319 : tx_mut.vout.reserve(num_out);
283 [ + + ]: 28044 : for (uint32_t i = 0; i < num_out; i++) {
284 : 25725 : const auto payload_size = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(0, 100000);
285 [ + + ]: 25725 : if (payload_size) {
286 [ + - + - : 52818 : tx_mut.vout.emplace_back(0, CScript() << OP_RETURN << std::vector<unsigned char>(payload_size));
+ - ]
287 : : } else {
288 [ + - ]: 16238 : tx_mut.vout.emplace_back(0, CScript{});
289 : : }
290 : : }
291 [ + - ]: 2319 : auto new_tx = MakeTransactionRef(tx_mut);
292 : : // add newly constructed outpoints to the coin pool
293 [ + + ]: 28044 : for (uint32_t i = 0; i < num_out; i++) {
294 [ + - ]: 25725 : outpoints.emplace_back(new_tx->GetHash(), i);
295 : : }
296 : 2319 : return new_tx;
297 [ + - ]: 4638 : }();
298 : :
299 : 2319 : const auto wtxid{tx->GetWitnessHash()};
300 : :
301 : : // orphanage functions
302 [ + + + + ]: 1006290 : LIMITED_WHILE(fuzzed_data_provider.remaining_bytes(), 10 * global_latency_score_limit)
303 : : {
304 : 1003971 : NodeId peer_id = fuzzed_data_provider.ConsumeIntegralInRange<NodeId>(0, num_peers - 1);
305 : 1003971 : 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 [ + - ]: 1003971 : const bool peer_is_protected{protected_peers[peer_id]};
310 : :
311 [ + - ]: 1003971 : CallOneOf(
312 : : fuzzed_data_provider,
313 : 306423 : [&] { // AddTx
314 : 306423 : bool have_tx_and_peer = orphanage->HaveTxFromPeer(wtxid, peer_id);
315 [ + + + + ]: 306423 : if (peer_is_protected && !have_tx_and_peer &&
316 [ + + ]: 50286 : (orphanage->UsageByPeer(peer_id) + tx_weight > honest_mem_limit ||
317 [ - + + + ]: 30189 : 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 : 283823 : orphanage->AddTx(tx, peer_id);
321 [ + + + + ]: 283823 : if (peer_is_protected && orphanage->HaveTxFromPeer(wtxid, peer_id)) {
322 : 53208 : protected_wtxids.insert(wtxid);
323 : : }
324 : : }
325 : 306423 : },
326 : 363265 : [&] { // AddAnnouncer
327 : 363265 : 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 : 363265 : {
330 [ + + + + ]: 363265 : if (peer_is_protected && !have_tx_and_peer &&
331 [ + + ]: 118603 : (orphanage->UsageByPeer(peer_id) + tx_weight > honest_mem_limit ||
332 [ - + + + ]: 64960 : orphanage->LatencyScoreFromPeer(peer_id) + (tx->vin.size() / 10) + 1 > honest_latency_limit)) {
333 : : // We never want our protected peer oversized
334 : : } else {
335 : 307263 : orphanage->AddAnnouncer(tx->GetWitnessHash(), peer_id);
336 [ + + + + ]: 307263 : if (peer_is_protected && orphanage->HaveTxFromPeer(wtxid, peer_id)) {
337 : 59610 : protected_wtxids.insert(wtxid);
338 : : }
339 : : }
340 : : }
341 : 363265 : },
342 : 164450 : [&] { // EraseTx
343 [ + + ]: 164450 : if (protected_wtxids.contains(tx->GetWitnessHash())) {
344 : 21154 : protected_wtxids.erase(wtxid);
345 : : }
346 : 164450 : orphanage->EraseTx(wtxid);
347 [ - + ]: 164450 : Assert(!orphanage->HaveTx(wtxid));
348 : 164450 : },
349 : 169833 : [&] { // EraseForPeer
350 [ + + ]: 169833 : if (!protected_peers[peer_id]) {
351 : 96561 : orphanage->EraseForPeer(peer_id);
352 [ - + ]: 96561 : Assert(orphanage->UsageByPeer(peer_id) == 0);
353 [ - + ]: 96561 : Assert(orphanage->LatencyScoreFromPeer(peer_id) == 0);
354 [ - + ]: 96561 : Assert(orphanage->AnnouncementsFromPeer(peer_id) == 0);
355 : : }
356 : 169833 : }
357 : : );
358 : : }
359 : 2319 : }
360 : :
361 [ + - ]: 306 : orphanage->SanityCheck();
362 : : // All of the honest peer's announcements are still present.
363 [ + + ]: 551 : for (const auto& wtxid : protected_wtxids) {
364 [ + - - + ]: 245 : Assert(orphanage->HaveTx(wtxid));
365 : : }
366 : 306 : }
367 : :
368 [ + - ]: 1297 : FUZZ_TARGET(txorphanage_sim)
369 : : {
370 : 839 : 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 : 839 : 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 : 839 : 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 : 839 : static constexpr unsigned MAX_ANN = 64;
390 : :
391 : 839 : 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 : 839 : 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 : 839 : std::vector<unsigned> txorder(NUM_TX);
402 : 839 : std::iota(txorder.begin(), txorder.end(), unsigned{0});
403 : 839 : std::shuffle(txorder.begin(), txorder.end(), rng);
404 : : // - Pick a set of dependencies (pair<child_index, parent_index>).
405 : 839 : std::vector<std::pair<unsigned, unsigned>> deps;
406 [ + - ]: 839 : deps.reserve((NUM_TX * (NUM_TX - 1)) / 2);
407 [ + + ]: 13424 : for (unsigned p = 0; p < NUM_TX - 1; ++p) {
408 [ + + ]: 113265 : for (unsigned c = p + 1; c < NUM_TX; ++c) {
409 [ + - ]: 100680 : deps.emplace_back(c, p);
410 : : }
411 : : }
412 : 839 : std::shuffle(deps.begin(), deps.end(), rng);
413 [ + - ]: 839 : deps.resize(provider.ConsumeIntegralInRange<unsigned>(0, NUM_TX * 4 - 1));
414 : : // - Construct the actual transactions.
415 [ + - ]: 839 : std::set<Wtxid> wtxids;
416 [ + - ]: 839 : std::vector<CTransactionRef> txn(NUM_TX);
417 : : node::TxOrphanage::Usage total_usage{0};
418 [ + + ]: 14263 : for (unsigned t = 0; t < NUM_TX; ++t) {
419 [ + - ]: 13424 : CMutableTransaction tx;
420 [ + + + + ]: 13424 : 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 [ + - ]: 12536 : tx = CMutableTransaction(*txn[txorder[t - 1]]);
424 : : } else {
425 : 7156 : tx.version = 1;
426 : 7156 : tx.nLockTime = 0xffffffff;
427 : : // Construct 1 to 16 outputs.
428 : 7156 : auto num_outputs = rng.randrange<unsigned>(1 << rng.randrange<unsigned>(5)) + 1;
429 [ + + ]: 29498 : for (unsigned output = 0; output < num_outputs; ++output) {
430 : 22342 : CScript scriptpubkey;
431 : 22342 : scriptpubkey.resize(provider.ConsumeIntegralInRange<unsigned>(20, 34));
432 [ + - ]: 22342 : tx.vout.emplace_back(CAmount{0}, std::move(scriptpubkey));
433 : 22342 : }
434 : : // Construct inputs (one for each dependency).
435 [ + + + + ]: 242328 : for (auto& [child, parent] : deps) {
436 [ + + ]: 235172 : if (child == t) {
437 [ - + ]: 13707 : auto& partx = txn[txorder[parent]];
438 [ - + ]: 13707 : assert(partx->version == 1);
439 [ - + + - ]: 13707 : COutPoint outpoint(partx->GetHash(), rng.randrange<size_t>(partx->vout.size()));
440 [ + - ]: 13707 : tx.vin.emplace_back(outpoint);
441 : 13707 : tx.vin.back().scriptSig.resize(provider.ConsumeIntegralInRange<unsigned>(16, 200));
442 : : }
443 : : }
444 : : // Construct fallback input in case there are no dependencies.
445 [ + + ]: 7156 : if (tx.vin.empty()) {
446 [ + - ]: 3197 : COutPoint outpoint(Txid::FromUint256(rng.rand256()), rng.randrange<size_t>(16));
447 [ + - ]: 3197 : tx.vin.emplace_back(outpoint);
448 : 3197 : 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 [ + - + + : 67160 : while (wtxids.contains(CTransaction(tx).GetWitnessHash()) || rng.randrange(4) == 0) {
+ + + + ]
454 [ - + ]: 20156 : auto& input = tx.vin[rng.randrange(tx.vin.size())];
455 [ + + ]: 20156 : if (rng.randbool()) {
456 [ + - ]: 9513 : input.scriptWitness.stack.resize(1);
457 [ + - ]: 9513 : input.scriptWitness.stack[0].resize(rng.randrange(100));
458 : : } else {
459 [ + - ]: 10643 : input.scriptWitness.stack.resize(0);
460 : : }
461 : : }
462 : : // Convert to CTransactionRef.
463 [ + - - + ]: 26848 : txn[txorder[t]] = MakeTransactionRef(std::move(tx));
464 [ + - ]: 13424 : wtxids.insert(txn[txorder[t]]->GetWitnessHash());
465 : 13424 : auto weight = GetTransactionWeight(*txn[txorder[t]]);
466 [ - + ]: 13424 : assert(weight < MAX_STANDARD_TX_WEIGHT);
467 : 13424 : total_usage += GetTransactionWeight(*txn[txorder[t]]);
468 : 13424 : }
469 : :
470 : : //
471 : : // 3. Initialize real orphanage
472 : : //
473 : :
474 : 839 : auto max_global_latency_score = provider.ConsumeIntegralInRange<node::TxOrphanage::Count>(NUM_PEERS, MAX_ANN);
475 : 839 : auto reserved_peer_usage = provider.ConsumeIntegralInRange<node::TxOrphanage::Usage>(1, total_usage);
476 : 839 : 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 : 839 : struct SimAnnouncement
485 : : {
486 : : unsigned tx;
487 : : NodeId announcer;
488 : : bool reconsider{false};
489 : 33976 : SimAnnouncement(unsigned tx_in, NodeId announcer_in, bool reconsider_in) noexcept :
490 : 33976 : 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 : 839 : std::vector<SimAnnouncement> sim_announcements;
496 : :
497 : : /** Consume a transaction (index into txn) from provider. */
498 : 11822 : auto read_tx_fn = [&]() -> unsigned { return provider.ConsumeIntegralInRange<unsigned>(0, NUM_TX - 1); };
499 : : /** Consume a NodeId from provider. */
500 : 24523 : 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 : 50823 : auto read_tx_peer_fn = [&]() -> std::pair<unsigned, NodeId> {
503 : 49984 : auto code = provider.ConsumeIntegralInRange<unsigned>(0, NUM_TX * NUM_PEERS - 1);
504 : 49984 : return {code % NUM_TX, code / NUM_TX};
505 : 839 : };
506 : : /** Determine if we have any announcements of the given transaction in the simulation. */
507 : 1639663 : auto have_tx_fn = [&](unsigned tx) -> bool {
508 [ + + ]: 9503644 : for (auto& ann : sim_announcements) {
509 [ + + ]: 8348332 : if (ann.tx == tx) return true;
510 : : }
511 : : return false;
512 : 839 : };
513 : : /** Count the number of peers in the simulation. */
514 : 171579 : auto count_peers_fn = [&]() -> unsigned {
515 : 170740 : std::bitset<NUM_PEERS> mask;
516 [ + + ]: 1432861 : for (auto& ann : sim_announcements) {
517 : 1262121 : mask.set(ann.announcer);
518 : : }
519 : 170740 : return mask.count();
520 : 839 : };
521 : : /** Determine if we have any reconsiderable announcements of a given transaction. */
522 : 34325 : auto have_reconsiderable_fn = [&](unsigned tx) -> bool {
523 [ + + ]: 384037 : for (auto& ann : sim_announcements) {
524 [ + + + + ]: 362208 : if (ann.reconsider && ann.tx == tx) return true;
525 : : }
526 : : return false;
527 : 839 : };
528 : : /** Determine if a peer has any transactions to reconsider. */
529 : 18002 : auto have_reconsider_fn = [&](NodeId peer) -> bool {
530 [ + + ]: 125977 : for (auto& ann : sim_announcements) {
531 [ + + + + ]: 109671 : if (ann.reconsider && ann.announcer == peer) return true;
532 : : }
533 : : return false;
534 : 839 : };
535 : : /** Get an iterator to an existing (wtxid, peer) pair in the simulation. */
536 : 12299 : auto find_announce_wtxid_fn = [&](const Wtxid& wtxid, NodeId peer) -> std::vector<SimAnnouncement>::iterator {
537 [ + - ]: 63918 : for (auto it = sim_announcements.begin(); it != sim_announcements.end(); ++it) {
538 [ + + + + ]: 63918 : if (txn[it->tx]->GetWitnessHash() == wtxid && it->announcer == peer) return it;
539 : : }
540 : 0 : return sim_announcements.end();
541 : 839 : };
542 : : /** Get an iterator to an existing (tx, peer) pair in the simulation. */
543 : 265607 : auto find_announce_fn = [&](unsigned tx, NodeId peer) {
544 [ + + ]: 2011045 : for (auto it = sim_announcements.begin(); it != sim_announcements.end(); ++it) {
545 [ + + + + ]: 1762385 : if (it->tx == tx && it->announcer == peer) return it;
546 : : }
547 : 248660 : return sim_announcements.end();
548 : 839 : };
549 : : /** Compute a peer's DoS score according to simulation data. */
550 : 207799 : auto dos_score_fn = [&](NodeId peer, int32_t max_count, int32_t max_usage) -> FeeFrac {
551 : 206960 : int64_t count{0};
552 : 206960 : int64_t usage{0};
553 [ + + ]: 1630816 : for (auto& ann : sim_announcements) {
554 [ + + ]: 1423856 : if (ann.announcer != peer) continue;
555 [ - + ]: 88991 : count += 1 + (txn[ann.tx]->vin.size() / 10);
556 : 88991 : usage += GetTransactionWeight(*txn[ann.tx]);
557 : : }
558 : 206960 : return std::max(FeeFrac{count, max_count}, FeeFrac{usage, max_usage});
559 : 839 : };
560 : :
561 : : //
562 : : // 5. Run through a scenario of mutators on both real and simulated orphanage.
563 : : //
564 : :
565 [ + + + + ]: 79322 : LIMITED_WHILE(provider.remaining_bytes() > 0, 200) {
566 : 78483 : int command = provider.ConsumeIntegralInRange<uint8_t>(0, 15);
567 : 112743 : while (true) {
568 [ - + + + : 112743 : if (sim_announcements.size() < MAX_ANN && command-- == 0) {
+ + ]
569 : : // AddTx
570 [ + - ]: 38851 : auto [tx, peer] = read_tx_peer_fn();
571 [ + - ]: 38851 : bool added = real->AddTx(txn[tx], peer);
572 : 38851 : bool sim_have_tx = have_tx_fn(tx);
573 [ - + ]: 38851 : assert(added == !sim_have_tx);
574 [ + + ]: 38851 : if (find_announce_fn(tx, peer) == sim_announcements.end()) {
575 [ + - ]: 31288 : sim_announcements.emplace_back(tx, peer, false);
576 : : }
577 : : break;
578 [ + + + + ]: 73892 : } else if (sim_announcements.size() < MAX_ANN && command-- == 0) {
579 : : // AddAnnouncer
580 [ + - ]: 11133 : auto [tx, peer] = read_tx_peer_fn();
581 [ + - ]: 11133 : bool added = real->AddAnnouncer(txn[tx]->GetWitnessHash(), peer);
582 : 11133 : bool sim_have_tx = have_tx_fn(tx);
583 : 11133 : auto sim_it = find_announce_fn(tx, peer);
584 [ + + + + : 19578 : assert(added == (sim_it == sim_announcements.end() && sim_have_tx));
- + ]
585 [ + + ]: 11133 : if (added) {
586 [ + - ]: 2688 : sim_announcements.emplace_back(tx, peer, false);
587 : : }
588 : : break;
589 [ + + ]: 62759 : } else if (command-- == 0) {
590 : : // EraseTx
591 : 4200 : auto tx = read_tx_fn();
592 [ + - ]: 4200 : bool erased = real->EraseTx(txn[tx]->GetWitnessHash());
593 : 4200 : bool sim_have = have_tx_fn(tx);
594 [ - + ]: 4200 : assert(erased == sim_have);
595 [ + + + + ]: 33171 : std::erase_if(sim_announcements, [&](auto& ann) { return ann.tx == tx; });
596 : : break;
597 [ + + ]: 58559 : } else if (command-- == 0) {
598 : : // EraseForPeer
599 : 4205 : auto peer = read_peer_fn();
600 [ + - ]: 4205 : real->EraseForPeer(peer);
601 [ + + + + ]: 26650 : std::erase_if(sim_announcements, [&](auto& ann) { return ann.announcer == peer; });
602 : : break;
603 [ + + ]: 54354 : } else if (command-- == 0) {
604 : : // EraseForBlock
605 : 5674 : auto pattern = provider.ConsumeIntegralInRange<uint64_t>(0, (uint64_t{1} << NUM_TX) - 1);
606 : 5674 : CBlock block;
607 : 5674 : std::set<COutPoint> spent;
608 [ + + ]: 96458 : for (unsigned tx = 0; tx < NUM_TX; ++tx) {
609 [ + + ]: 90784 : if ((pattern >> tx) & 1) {
610 [ + - ]: 29859 : block.vtx.emplace_back(txn[tx]);
611 [ + + ]: 99088 : for (auto& txin : block.vtx.back()->vin) {
612 [ + - ]: 69229 : spent.insert(txin.prevout);
613 : : }
614 : : }
615 : : }
616 : 5674 : std::shuffle(block.vtx.begin(), block.vtx.end(), rng);
617 [ + - ]: 5674 : real->EraseForBlock(block);
618 : 29569 : std::erase_if(sim_announcements, [&](auto& ann) {
619 [ + + ]: 50770 : for (auto& txin : txn[ann.tx]->vin) {
620 [ + + ]: 38137 : if (spent.contains(txin.prevout)) return true;
621 : : }
622 : : return false;
623 : : });
624 : 5674 : break;
625 [ + + ]: 54354 : } else if (command-- == 0) {
626 : : // AddChildrenToWorkSet
627 : 6783 : auto tx = read_tx_fn();
628 : 6783 : FastRandomContext rand_ctx(rng.rand256());
629 [ + - ]: 6783 : auto added = real->AddChildrenToWorkSet(*txn[tx], rand_ctx);
630 : : /** Set of not-already-reconsiderable child wtxids. */
631 : 6783 : std::set<Wtxid> child_wtxids;
632 [ + + ]: 115311 : for (unsigned child_tx = 0; child_tx < NUM_TX; ++child_tx) {
633 [ + + ]: 108528 : if (!have_tx_fn(child_tx)) continue;
634 [ + + ]: 33486 : if (have_reconsiderable_fn(child_tx)) continue;
635 : 21829 : bool child_of = false;
636 [ + + ]: 54351 : for (auto& txin : txn[child_tx]->vin) {
637 [ + + ]: 40084 : if (txin.prevout.hash == txn[tx]->GetHash()) {
638 : : child_of = true;
639 : : break;
640 : : }
641 : : }
642 [ + + ]: 21829 : if (child_of) {
643 [ + - ]: 7562 : child_wtxids.insert(txn[child_tx]->GetWitnessHash());
644 : : }
645 : : }
646 [ + + ]: 14345 : for (auto& [wtxid, peer] : added) {
647 : : // Wtxid must be a child of tx that is not yet reconsiderable.
648 : 7562 : auto child_wtxid_it = child_wtxids.find(wtxid);
649 [ - + ]: 7562 : assert(child_wtxid_it != child_wtxids.end());
650 : : // Announcement must exist.
651 : 7562 : auto sim_ann_it = find_announce_wtxid_fn(wtxid, peer);
652 [ - + ]: 7562 : assert(sim_ann_it != sim_announcements.end());
653 : : // Announcement must not yet be reconsiderable.
654 [ - + ]: 7562 : assert(sim_ann_it->reconsider == false);
655 : : // Make reconsiderable.
656 : 7562 : sim_ann_it->reconsider = true;
657 : : // Remove from child_wtxids map, to disallow it being reported a second time in added.
658 : 7562 : 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 [ - + ]: 6783 : assert(child_wtxids.empty());
664 : 6783 : break;
665 [ + + ]: 48680 : } else if (command-- == 0) {
666 : : // GetTxToReconsider.
667 : 7637 : auto peer = read_peer_fn();
668 [ + - ]: 7637 : auto result = real->GetTxToReconsider(peer);
669 [ + + ]: 7637 : if (result) {
670 : : // A transaction was found. It must have a corresponding reconsiderable
671 : : // announcement from peer.
672 : 3898 : auto sim_ann_it = find_announce_wtxid_fn(result->GetWitnessHash(), peer);
673 [ - + ]: 3898 : assert(sim_ann_it != sim_announcements.end());
674 [ - + ]: 3898 : assert(sim_ann_it->announcer == peer);
675 [ - + ]: 3898 : assert(sim_ann_it->reconsider);
676 : : // Make it non-reconsiderable.
677 : 3898 : sim_ann_it->reconsider = false;
678 : : } else {
679 : : // No reconsiderable transaction was found from peer. Verify that it does not
680 : : // have any.
681 [ - + ]: 3739 : assert(!have_reconsider_fn(peer));
682 : : }
683 [ + + ]: 7637 : break;
684 : 7637 : }
685 : : }
686 : : // Always trim after each command if needed.
687 [ + - + + ]: 78483 : const auto max_ann = max_global_latency_score / std::max<unsigned>(1, count_peers_fn());
688 : 78483 : const auto max_mem = reserved_peer_usage;
689 : 91418 : while (true) {
690 : : // Count global usage and number of peers.
691 : 91418 : node::TxOrphanage::Usage total_usage{0};
692 [ - + ]: 91418 : node::TxOrphanage::Count total_latency_score = sim_announcements.size();
693 [ + + ]: 1554106 : for (unsigned tx = 0; tx < NUM_TX; ++tx) {
694 [ + + ]: 1462688 : if (have_tx_fn(tx)) {
695 : 425299 : total_usage += GetTransactionWeight(*txn[tx]);
696 [ - + ]: 425299 : total_latency_score += txn[tx]->vin.size() / 10;
697 : : }
698 : : }
699 [ + - ]: 91418 : auto num_peers = count_peers_fn();
700 [ + + + + ]: 172245 : bool oversized = (total_usage > reserved_peer_usage * num_peers) ||
701 [ + - ]: 80827 : (total_latency_score > real->MaxGlobalLatencyScore());
702 : 91418 : if (!oversized) break;
703 : : // Find worst peer.
704 : 12935 : FeeFrac worst_dos_score{0, 1};
705 : 12935 : unsigned worst_peer = unsigned(-1);
706 [ + + ]: 219895 : for (unsigned peer = 0; peer < NUM_PEERS; ++peer) {
707 : 206960 : 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 [ + + ]: 206960 : if (dos_score >= worst_dos_score) {
711 : 25320 : worst_dos_score = dos_score;
712 : 25320 : worst_peer = peer;
713 : : }
714 : : }
715 [ - + ]: 12935 : assert(worst_peer != unsigned(-1));
716 [ - + ]: 12935 : assert(worst_dos_score >> FeeFrac(1, 1));
717 : : // Find oldest announcement from worst_peer, preferring non-reconsiderable ones.
718 : : bool done{false};
719 [ + - ]: 13058 : for (int reconsider = 0; reconsider < 2; ++reconsider) {
720 [ + + ]: 69351 : for (auto it = sim_announcements.begin(); it != sim_announcements.end(); ++it) {
721 [ + + + + ]: 69228 : if (it->announcer != worst_peer || it->reconsider != reconsider) continue;
722 : 12935 : sim_announcements.erase(it);
723 : 12935 : done = true;
724 : 12935 : break;
725 : : }
726 : 13058 : if (done) break;
727 : : }
728 [ + - ]: 12935 : 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 [ + - + - : 78483 : assert(real->TotalLatencyScore() <= real->MaxGlobalLatencyScore());
- + ]
733 [ + - + - : 78483 : 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 [ + - ]: 839 : real->SanityCheck();
741 : :
742 : :
743 [ + - ]: 839 : auto all_orphans = real->GetOrphanTransactions();
744 : 839 : node::TxOrphanage::Usage orphan_usage{0};
745 [ + - ]: 839 : std::vector<node::TxOrphanage::Usage> usage_by_peer(NUM_PEERS);
746 : 839 : node::TxOrphanage::Count unique_orphans{0};
747 [ + - ]: 839 : std::vector<node::TxOrphanage::Count> count_by_peer(NUM_PEERS);
748 [ - + ]: 839 : node::TxOrphanage::Count total_latency_score = sim_announcements.size();
749 [ + + ]: 14263 : for (unsigned tx = 0; tx < NUM_TX; ++tx) {
750 : 13424 : bool sim_have_tx = have_tx_fn(tx);
751 [ + + ]: 13424 : if (sim_have_tx) {
752 : 3233 : orphan_usage += GetTransactionWeight(*txn[tx]);
753 [ - + ]: 3233 : total_latency_score += txn[tx]->vin.size() / 10;
754 : : }
755 : 13424 : unique_orphans += sim_have_tx;
756 : 52677 : 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 [ - + ]: 13424 : assert((orphans_it != all_orphans.end()) == sim_have_tx);
759 : : // HaveTx
760 [ + - ]: 13424 : bool have_tx = real->HaveTx(txn[tx]->GetWitnessHash());
761 [ - + ]: 13424 : assert(have_tx == sim_have_tx);
762 : : // GetTx
763 [ + - ]: 13424 : auto txref = real->GetTx(txn[tx]->GetWitnessHash());
764 [ - + ]: 13424 : assert(!!txref == sim_have_tx);
765 [ + + - + ]: 13424 : if (sim_have_tx) assert(txref->GetWitnessHash() == txn[tx]->GetWitnessHash());
766 : :
767 [ + + ]: 228208 : for (NodeId peer = 0; peer < NUM_PEERS; ++peer) {
768 : 214784 : auto it_sim_ann = find_announce_fn(tx, peer);
769 [ + + ]: 214784 : bool sim_have_ann = it_sim_ann != sim_announcements.end();
770 [ + + ]: 214784 : if (sim_have_ann) usage_by_peer[peer] += GetTransactionWeight(*txn[tx]);
771 [ + + ]: 214784 : count_by_peer[peer] += sim_have_ann;
772 : : // GetOrphanTransactions (announcers presence)
773 [ + + - + ]: 214784 : if (sim_have_ann) assert(sim_have_tx);
774 [ + + - + ]: 214784 : if (sim_have_tx) assert(orphans_it->announcers.count(peer) == sim_have_ann);
775 : : // HaveTxFromPeer
776 [ + - ]: 214784 : bool have_ann = real->HaveTxFromPeer(txn[tx]->GetWitnessHash(), peer);
777 [ - + ]: 214784 : assert(sim_have_ann == have_ann);
778 : : // GetChildrenFromSamePeer
779 [ + - ]: 214784 : auto children_from_peer = real->GetChildrenFromSamePeer(txn[tx], peer);
780 : 214784 : auto it = children_from_peer.rbegin();
781 [ + + ]: 644352 : for (int phase = 0; phase < 2; ++phase) {
782 : : // First expect all children which have reconsiderable announcement from peer, then the others.
783 [ + + ]: 3422208 : for (auto& ann : sim_announcements) {
784 [ + + ]: 2992640 : if (ann.announcer != peer) continue;
785 [ + + ]: 187040 : if (ann.reconsider != (phase == 1)) continue;
786 : 93520 : bool matching_parent{false};
787 [ + + ]: 326144 : for (const auto& vin : txn[ann.tx]->vin) {
788 [ + + ]: 232624 : if (vin.prevout.hash == txn[tx]->GetHash()) matching_parent = true;
789 : : }
790 [ + + ]: 93520 : if (!matching_parent) continue;
791 : : // Found an announcement from peer which is a child of txn[tx].
792 [ - + ]: 20437 : assert(it != children_from_peer.rend());
793 [ - + ]: 20437 : assert((*it)->GetWitnessHash() == txn[ann.tx]->GetWitnessHash());
794 : 20437 : ++it;
795 : : }
796 : : }
797 [ - + ]: 214784 : assert(it == children_from_peer.rend());
798 : 214784 : }
799 : 13424 : }
800 : : // TotalOrphanUsage
801 [ + - - + ]: 839 : assert(orphan_usage == real->TotalOrphanUsage());
802 [ + + ]: 14263 : for (NodeId peer = 0; peer < NUM_PEERS; ++peer) {
803 : 13424 : bool sim_have_reconsider = have_reconsider_fn(peer);
804 : : // HaveTxToReconsider
805 [ + - ]: 13424 : bool have_reconsider = real->HaveTxToReconsider(peer);
806 [ - + ]: 13424 : assert(have_reconsider == sim_have_reconsider);
807 : : // UsageByPeer
808 [ + - - + ]: 13424 : assert(usage_by_peer[peer] == real->UsageByPeer(peer));
809 : : // AnnouncementsFromPeer
810 [ + - - + ]: 13424 : assert(count_by_peer[peer] == real->AnnouncementsFromPeer(peer));
811 : : }
812 : : // CountAnnouncements
813 [ - + + - : 839 : assert(sim_announcements.size() == real->CountAnnouncements());
- + ]
814 : : // CountUniqueOrphans
815 [ + - - + ]: 839 : assert(unique_orphans == real->CountUniqueOrphans());
816 : : // MaxGlobalLatencyScore
817 [ + - - + ]: 839 : assert(max_global_latency_score == real->MaxGlobalLatencyScore());
818 : : // ReservedPeerUsage
819 [ + - - + ]: 839 : assert(reserved_peer_usage == real->ReservedPeerUsage());
820 : : // MaxPeerLatencyScore
821 [ + - ]: 839 : auto present_peers = count_peers_fn();
822 [ + + + - : 1189 : assert(max_global_latency_score / std::max<unsigned>(1, present_peers) == real->MaxPeerLatencyScore());
- + ]
823 : : // MaxGlobalUsage
824 [ + + + - : 1189 : assert(reserved_peer_usage * std::max<unsigned>(1, present_peers) == real->MaxGlobalUsage());
- + ]
825 : : // TotalLatencyScore.
826 [ + - - + ]: 839 : assert(real->TotalLatencyScore() == total_latency_score);
827 : 839 : }
|