Branch data Line data Source code
1 : : // Copyright (c) 2023 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/validation.h>
6 : : #include <node/context.h>
7 : : #include <node/mempool_args.h>
8 : : #include <node/miner.h>
9 : : #include <node/txdownloadman.h>
10 : : #include <node/txdownloadman_impl.h>
11 : : #include <test/fuzz/FuzzedDataProvider.h>
12 : : #include <test/fuzz/fuzz.h>
13 : : #include <test/fuzz/util.h>
14 : : #include <test/fuzz/util/mempool.h>
15 : : #include <test/util/mining.h>
16 : : #include <test/util/script.h>
17 : : #include <test/util/setup_common.h>
18 : : #include <test/util/txmempool.h>
19 : : #include <util/hasher.h>
20 : : #include <util/rbf.h>
21 : : #include <util/time.h>
22 : : #include <txmempool.h>
23 : : #include <validation.h>
24 : : #include <validationinterface.h>
25 : :
26 : : namespace {
27 : :
28 : : const TestingSetup* g_setup;
29 : :
30 : : constexpr size_t NUM_COINS{50};
31 : : COutPoint COINS[NUM_COINS];
32 : :
33 : : static TxValidationResult TESTED_TX_RESULTS[] = {
34 : : // Skip TX_RESULT_UNSET
35 : : TxValidationResult::TX_CONSENSUS,
36 : : TxValidationResult::TX_INPUTS_NOT_STANDARD,
37 : : TxValidationResult::TX_NOT_STANDARD,
38 : : TxValidationResult::TX_MISSING_INPUTS,
39 : : TxValidationResult::TX_PREMATURE_SPEND,
40 : : TxValidationResult::TX_WITNESS_MUTATED,
41 : : TxValidationResult::TX_WITNESS_STRIPPED,
42 : : TxValidationResult::TX_CONFLICT,
43 : : TxValidationResult::TX_MEMPOOL_POLICY,
44 : : // Skip TX_NO_MEMPOOL
45 : : TxValidationResult::TX_RECONSIDERABLE,
46 : : TxValidationResult::TX_UNKNOWN,
47 : : };
48 : :
49 : : // Precomputed transactions. Some may conflict with each other.
50 : : std::vector<CTransactionRef> TRANSACTIONS;
51 : :
52 : : // Limit the total number of peers because we don't expect coverage to change much with lots more peers.
53 : : constexpr int NUM_PEERS = 16;
54 : :
55 : : // Precomputed random durations (positive and negative, each ~exponentially distributed).
56 : : std::chrono::microseconds TIME_SKIPS[128];
57 : :
58 : 50771 : static CTransactionRef MakeTransactionSpending(const std::vector<COutPoint>& outpoints, size_t num_outputs, bool add_witness)
59 : : {
60 : 50771 : CMutableTransaction tx;
61 : : // If no outpoints are given, create a random one.
62 [ + + ]: 550189 : for (const auto& outpoint : outpoints) {
63 [ + - ]: 499418 : tx.vin.emplace_back(outpoint);
64 : : }
65 [ + + ]: 50771 : if (add_witness) {
66 [ + - ]: 95684 : tx.vin[0].scriptWitness.stack.push_back({1});
67 : : }
68 [ + - + + ]: 14106145 : for (size_t o = 0; o < num_outputs; ++o) tx.vout.emplace_back(CENT, P2WSH_OP_TRUE);
69 [ + - ]: 101542 : return MakeTransactionRef(tx);
70 : 50771 : }
71 : 50635 : static std::vector<COutPoint> PickCoins(FuzzedDataProvider& fuzzed_data_provider)
72 : : {
73 : 50635 : std::vector<COutPoint> ret;
74 [ + - ]: 50635 : ret.push_back(fuzzed_data_provider.PickValueInArray(COINS));
75 [ + + + + ]: 499280 : LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10) {
76 [ + - ]: 448645 : ret.push_back(fuzzed_data_provider.PickValueInArray(COINS));
77 : : }
78 : 50635 : return ret;
79 : 0 : }
80 : :
81 : 2 : void initialize()
82 : : {
83 [ + - + - ]: 4 : static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
84 : 2 : g_setup = testing_setup.get();
85 [ + + ]: 102 : for (uint32_t i = 0; i < uint32_t{NUM_COINS}; ++i) {
86 : 100 : COINS[i] = COutPoint{Txid::FromUint256((HashWriter() << i).GetHash()), i};
87 : : }
88 : 2 : size_t outpoints_index = 0;
89 : : // 2 transactions same txid different witness
90 : 2 : {
91 [ + - + - ]: 4 : auto tx1{MakeTransactionSpending({COINS[outpoints_index]}, /*num_outputs=*/5, /*add_witness=*/false)};
92 [ + - + - : 4 : auto tx2{MakeTransactionSpending({COINS[outpoints_index]}, /*num_outputs=*/5, /*add_witness=*/true)};
+ - ]
93 [ + - ]: 2 : Assert(tx1->GetHash() == tx2->GetHash());
94 [ + - ]: 2 : TRANSACTIONS.emplace_back(tx1);
95 [ + - ]: 2 : TRANSACTIONS.emplace_back(tx2);
96 [ + - ]: 2 : outpoints_index += 1;
97 [ + - ]: 2 : }
98 : : // 2 parents 1 child
99 : 2 : {
100 [ + - + - ]: 4 : auto tx_parent_1{MakeTransactionSpending({COINS[outpoints_index++]}, /*num_outputs=*/1, /*add_witness=*/true)};
101 [ + - ]: 2 : TRANSACTIONS.emplace_back(tx_parent_1);
102 [ + - + - : 4 : auto tx_parent_2{MakeTransactionSpending({COINS[outpoints_index++]}, /*num_outputs=*/1, /*add_witness=*/false)};
+ - ]
103 [ + - ]: 2 : TRANSACTIONS.emplace_back(tx_parent_2);
104 [ + - + - : 4 : TRANSACTIONS.emplace_back(MakeTransactionSpending({COutPoint{tx_parent_1->GetHash(), 0}, COutPoint{tx_parent_2->GetHash(), 0}},
+ - + - ]
105 : : /*num_outputs=*/1, /*add_witness=*/true));
106 [ + - ]: 2 : }
107 : : // 1 parent 2 children
108 : 2 : {
109 [ + - + - ]: 4 : auto tx_parent{MakeTransactionSpending({COINS[outpoints_index++]}, /*num_outputs=*/2, /*add_witness=*/true)};
110 [ + - ]: 2 : TRANSACTIONS.emplace_back(tx_parent);
111 [ + - + - : 4 : TRANSACTIONS.emplace_back(MakeTransactionSpending({COutPoint{tx_parent->GetHash(), 0}},
+ - + - ]
112 : : /*num_outputs=*/1, /*add_witness=*/true));
113 [ + - + - : 4 : TRANSACTIONS.emplace_back(MakeTransactionSpending({COutPoint{tx_parent->GetHash(), 1}},
+ - + - ]
114 : : /*num_outputs=*/1, /*add_witness=*/true));
115 : 0 : }
116 : : // chain of 5 segwit
117 : 2 : {
118 : 2 : COutPoint& last_outpoint = COINS[outpoints_index++];
119 [ + + ]: 12 : for (auto i{0}; i < 5; ++i) {
120 [ + - + - ]: 20 : auto tx{MakeTransactionSpending({last_outpoint}, /*num_outputs=*/1, /*add_witness=*/true)};
121 [ + - ]: 10 : TRANSACTIONS.emplace_back(tx);
122 [ + - ]: 10 : last_outpoint = COutPoint{tx->GetHash(), 0};
123 : 10 : }
124 : : }
125 : : // chain of 5 non-segwit
126 : : {
127 : 12 : COutPoint& last_outpoint = COINS[outpoints_index++];
128 [ + + ]: 12 : for (auto i{0}; i < 5; ++i) {
129 [ + - + - ]: 20 : auto tx{MakeTransactionSpending({last_outpoint}, /*num_outputs=*/1, /*add_witness=*/false)};
130 [ + - ]: 10 : TRANSACTIONS.emplace_back(tx);
131 [ + - ]: 10 : last_outpoint = COutPoint{tx->GetHash(), 0};
132 : 10 : }
133 : : }
134 : : // Also create a loose tx for each outpoint. Some of these transactions conflict with the above
135 : : // or have the same txid.
136 [ + + ]: 102 : for (const auto& outpoint : COINS) {
137 [ + - + - ]: 200 : TRANSACTIONS.emplace_back(MakeTransactionSpending({outpoint}, /*num_outputs=*/1, /*add_witness=*/true));
138 : : }
139 : :
140 : : // Create random-looking time jumps
141 : : int i = 0;
142 : : // TIME_SKIPS[N] for N=0..15 is just N microseconds.
143 [ + + ]: 34 : for (; i < 16; ++i) {
144 : 32 : TIME_SKIPS[i] = std::chrono::microseconds{i};
145 : : }
146 : : // TIME_SKIPS[N] for N=16..127 has randomly-looking but roughly exponentially increasing values up to
147 : : // 198.416453 seconds.
148 [ + + ]: 226 : for (; i < 128; ++i) {
149 : 224 : int diff_bits = ((i - 10) * 2) / 9;
150 : 224 : uint64_t diff = 1 + (CSipHasher(0, 0).Write(i).Finalize() >> (64 - diff_bits));
151 : 224 : TIME_SKIPS[i] = TIME_SKIPS[i - 1] + std::chrono::microseconds{diff};
152 : : }
153 [ + - ]: 4 : }
154 : :
155 : 0 : void CheckPackageToValidate(const node::PackageToValidate& package_to_validate, NodeId peer)
156 : : {
157 : 0 : Assert(package_to_validate.m_senders.size() == 2);
158 : 0 : Assert(package_to_validate.m_senders.front() == peer);
159 : 0 : Assert(package_to_validate.m_senders.back() < NUM_PEERS);
160 : :
161 : : // Package is a 1p1c
162 : 0 : const auto& package = package_to_validate.m_txns;
163 : 0 : Assert(IsChildWithParents(package));
164 : 0 : Assert(package.size() == 2);
165 : 0 : }
166 : :
167 [ + - ]: 1867 : FUZZ_TARGET(txdownloadman, .init = initialize)
168 : : {
169 : 1435 : SeedRandomStateForTest(SeedRand::ZEROS);
170 : 1435 : FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
171 : 1435 : SetMockTime(ConsumeTime(fuzzed_data_provider));
172 : :
173 : : // Initialize txdownloadman
174 [ + - ]: 1435 : bilingual_str error;
175 [ + - + - ]: 1435 : CTxMemPool pool{MemPoolOptionsForTest(g_setup->m_node), error};
176 : 1435 : const auto max_orphan_count = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(0, 300);
177 : 1435 : FastRandomContext det_rand{true};
178 [ + - ]: 1435 : node::TxDownloadManager txdownloadman{node::TxDownloadOptions{pool, det_rand, max_orphan_count, true}};
179 : :
180 : 1435 : std::chrono::microseconds time{244466666};
181 : :
182 [ + + + + ]: 28594 : LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000)
183 : : {
184 : 27159 : NodeId rand_peer = fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(0, NUM_PEERS - 1);
185 : :
186 : : // Transaction can be one of the premade ones or a randomly generated one
187 [ + + ]: 27159 : auto rand_tx = fuzzed_data_provider.ConsumeBool() ?
188 [ - - ]: 26011 : MakeTransactionSpending(PickCoins(fuzzed_data_provider),
189 : 26011 : /*num_outputs=*/fuzzed_data_provider.ConsumeIntegralInRange(1, 500),
190 : 26011 : /*add_witness=*/fuzzed_data_provider.ConsumeBool()) :
191 [ + - + - : 27159 : TRANSACTIONS.at(fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, TRANSACTIONS.size() - 1));
+ - + - ]
192 : :
193 [ + - ]: 27159 : CallOneOf(
194 : : fuzzed_data_provider,
195 : 571 : [&] {
196 : 571 : node::TxDownloadConnectionInfo info{
197 : 571 : .m_preferred = fuzzed_data_provider.ConsumeBool(),
198 : 1142 : .m_relay_permissions = fuzzed_data_provider.ConsumeBool(),
199 : 1142 : .m_wtxid_relay = fuzzed_data_provider.ConsumeBool()
200 : 571 : };
201 : 571 : txdownloadman.ConnectedPeer(rand_peer, info);
202 : 571 : },
203 : 8243 : [&] {
204 : 8243 : txdownloadman.DisconnectedPeer(rand_peer);
205 : 8243 : txdownloadman.CheckIsEmpty(rand_peer);
206 : 8243 : },
207 : 86 : [&] {
208 : 86 : txdownloadman.ActiveTipChange();
209 : 86 : },
210 : 4901 : [&] {
211 : 4901 : CBlock block;
212 [ + - ]: 4901 : block.vtx.push_back(rand_tx);
213 [ + - + - : 9802 : txdownloadman.BlockConnected(std::make_shared<CBlock>(block));
- + ]
214 : 4901 : },
215 : 210 : [&] {
216 : 210 : txdownloadman.BlockDisconnected();
217 : 210 : },
218 : 509 : [&] {
219 : 509 : txdownloadman.MempoolAcceptedTx(rand_tx);
220 : 509 : },
221 : 527 : [&] {
222 [ + - ]: 527 : TxValidationState state;
223 [ + - + - : 1054 : state.Invalid(fuzzed_data_provider.PickValueInArray(TESTED_TX_RESULTS), "");
+ - ]
224 : 527 : bool first_time_failure{fuzzed_data_provider.ConsumeBool()};
225 : :
226 [ + - ]: 527 : node::RejectedTxTodo todo = txdownloadman.MempoolRejectedTx(rand_tx, state, rand_peer, first_time_failure);
227 [ + + - + : 527 : Assert(first_time_failure || !todo.m_should_add_extra_compact_tx);
+ - ]
228 : 1054 : },
229 : 756 : [&] {
230 [ + + ]: 756 : GenTxid gtxid = fuzzed_data_provider.ConsumeBool() ?
231 : 665 : GenTxid::Txid(rand_tx->GetHash()) :
232 : 91 : GenTxid::Wtxid(rand_tx->GetWitnessHash());
233 : 756 : txdownloadman.AddTxAnnouncement(rand_peer, gtxid, time);
234 : 756 : },
235 : 97 : [&] {
236 : 97 : txdownloadman.GetRequestsToSend(rand_peer, time);
237 : 97 : },
238 : 10910 : [&] {
239 [ - + ]: 10910 : txdownloadman.ReceivedTx(rand_peer, rand_tx);
240 [ + + ]: 10910 : const auto& [should_validate, maybe_package] = txdownloadman.ReceivedTx(rand_peer, rand_tx);
241 : : // The only possible results should be:
242 : : // - Don't validate the tx, no package.
243 : : // - Don't validate the tx, package.
244 : : // - Validate the tx, no package.
245 : : // The only combination that doesn't make sense is validate both tx and package.
246 [ + + + - : 21368 : Assert(!(should_validate && maybe_package.has_value()));
+ - ]
247 [ - + - - ]: 10910 : if (maybe_package.has_value()) CheckPackageToValidate(*maybe_package, rand_peer);
248 : 10910 : },
249 : 167 : [&] {
250 [ + - ]: 334 : txdownloadman.ReceivedNotFound(rand_peer, {rand_tx->GetWitnessHash()});
251 : 167 : },
252 : 182 : [&] {
253 : 182 : const bool expect_work{txdownloadman.HaveMoreWork(rand_peer)};
254 : 182 : const auto ptx = txdownloadman.GetTxToReconsider(rand_peer);
255 : : // expect_work=true doesn't necessarily mean the next item from the workset isn't a
256 : : // nullptr, as the transaction could have been removed from orphanage without being
257 : : // removed from the peer's workset.
258 [ + + ]: 182 : if (ptx) {
259 : : // However, if there was a non-null tx in the workset, HaveMoreWork should have
260 : : // returned true.
261 [ + - ]: 1 : Assert(expect_work);
262 : : }
263 : 182 : }
264 : : );
265 : : // Jump forwards or backwards
266 : 27159 : auto time_skip = fuzzed_data_provider.PickValueInArray(TIME_SKIPS);
267 [ + + ]: 27159 : if (fuzzed_data_provider.ConsumeBool()) time_skip *= -1;
268 [ + - ]: 27159 : time += time_skip;
269 : 27159 : }
270 : : // Disconnect everybody, check that all data structures are empty.
271 [ + + ]: 24395 : for (NodeId nodeid = 0; nodeid < NUM_PEERS; ++nodeid) {
272 [ + - ]: 22960 : txdownloadman.DisconnectedPeer(nodeid);
273 [ + - ]: 22960 : txdownloadman.CheckIsEmpty(nodeid);
274 : : }
275 [ + - ]: 1435 : txdownloadman.CheckIsEmpty();
276 : 2870 : }
277 : :
278 : : // Give node 0 relay permissions, and nobody else. This helps us remember who is a RelayPermissions
279 : : // peer without tracking anything (this is only for the txdownload_impl target).
280 : 28978 : static bool HasRelayPermissions(NodeId peer) { return peer == 0; }
281 : :
282 : 1751 : static void CheckInvariants(const node::TxDownloadManagerImpl& txdownload_impl, size_t max_orphan_count)
283 : : {
284 : 1751 : const TxOrphanage& orphanage = txdownload_impl.m_orphanage;
285 : :
286 : : // Orphanage usage should never exceed what is allowed
287 : 1751 : Assert(orphanage.Size() <= max_orphan_count);
288 : 1751 : txdownload_impl.m_orphanage.SanityCheck();
289 : :
290 : : // We should never have more than the maximum in-flight requests out for a peer.
291 [ + + ]: 29767 : for (NodeId peer = 0; peer < NUM_PEERS; ++peer) {
292 [ + + ]: 28016 : if (!HasRelayPermissions(peer)) {
293 : 26265 : Assert(txdownload_impl.m_txrequest.Count(peer) <= node::MAX_PEER_TX_ANNOUNCEMENTS);
294 : : }
295 : : }
296 : 1751 : txdownload_impl.m_txrequest.SanityCheck();
297 : 1751 : }
298 : :
299 [ + - ]: 2183 : FUZZ_TARGET(txdownloadman_impl, .init = initialize)
300 : : {
301 : 1751 : SeedRandomStateForTest(SeedRand::ZEROS);
302 : 1751 : FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
303 : 1751 : SetMockTime(ConsumeTime(fuzzed_data_provider));
304 : :
305 : : // Initialize a TxDownloadManagerImpl
306 [ + - ]: 1751 : bilingual_str error;
307 [ + - + - ]: 1751 : CTxMemPool pool{MemPoolOptionsForTest(g_setup->m_node), error};
308 : 1751 : const auto max_orphan_count = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(0, 300);
309 : 1751 : FastRandomContext det_rand{true};
310 [ + - ]: 1751 : node::TxDownloadManagerImpl txdownload_impl{node::TxDownloadOptions{pool, det_rand, max_orphan_count, true}};
311 : :
312 : 1751 : std::chrono::microseconds time{244466666};
313 : :
314 [ + + + + ]: 29058 : LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10000)
315 : : {
316 : 27307 : NodeId rand_peer = fuzzed_data_provider.ConsumeIntegralInRange<int64_t>(0, NUM_PEERS - 1);
317 : :
318 : : // Transaction can be one of the premade ones or a randomly generated one
319 [ + + ]: 27307 : auto rand_tx = fuzzed_data_provider.ConsumeBool() ?
320 [ - - ]: 24624 : MakeTransactionSpending(PickCoins(fuzzed_data_provider),
321 : 24624 : /*num_outputs=*/fuzzed_data_provider.ConsumeIntegralInRange(1, 500),
322 : 24624 : /*add_witness=*/fuzzed_data_provider.ConsumeBool()) :
323 [ + - + - : 27307 : TRANSACTIONS.at(fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, TRANSACTIONS.size() - 1));
+ - + - ]
324 : :
325 [ + - ]: 27307 : CallOneOf(
326 : : fuzzed_data_provider,
327 : 962 : [&] {
328 : 962 : node::TxDownloadConnectionInfo info{
329 : 962 : .m_preferred = fuzzed_data_provider.ConsumeBool(),
330 : 962 : .m_relay_permissions = HasRelayPermissions(rand_peer),
331 : 1924 : .m_wtxid_relay = fuzzed_data_provider.ConsumeBool()
332 : 962 : };
333 : 962 : txdownload_impl.ConnectedPeer(rand_peer, info);
334 : 962 : },
335 : 5885 : [&] {
336 : 5885 : txdownload_impl.DisconnectedPeer(rand_peer);
337 : 5885 : txdownload_impl.CheckIsEmpty(rand_peer);
338 : 5885 : },
339 : 2091 : [&] {
340 : 2091 : txdownload_impl.ActiveTipChange();
341 : : // After a block update, nothing should be in the rejection caches
342 [ + + ]: 144279 : for (const auto& tx : TRANSACTIONS) {
343 : 142188 : Assert(!txdownload_impl.RecentRejectsFilter().contains(tx->GetWitnessHash().ToUint256()));
344 : 142188 : Assert(!txdownload_impl.RecentRejectsFilter().contains(tx->GetHash().ToUint256()));
345 : 142188 : Assert(!txdownload_impl.RecentRejectsReconsiderableFilter().contains(tx->GetWitnessHash().ToUint256()));
346 : 142188 : Assert(!txdownload_impl.RecentRejectsReconsiderableFilter().contains(tx->GetHash().ToUint256()));
347 : : }
348 : 2091 : },
349 : 3838 : [&] {
350 : 3838 : CBlock block;
351 [ + - ]: 3838 : block.vtx.push_back(rand_tx);
352 [ + - + - : 7676 : txdownload_impl.BlockConnected(std::make_shared<CBlock>(block));
- + ]
353 : : // Block transactions must be removed from orphanage
354 [ + - + - ]: 3838 : Assert(!txdownload_impl.m_orphanage.HaveTx(rand_tx->GetWitnessHash()));
355 : 3838 : },
356 : 151 : [&] {
357 : 151 : txdownload_impl.BlockDisconnected();
358 : 151 : Assert(!txdownload_impl.RecentConfirmedTransactionsFilter().contains(rand_tx->GetWitnessHash().ToUint256()));
359 : 151 : Assert(!txdownload_impl.RecentConfirmedTransactionsFilter().contains(rand_tx->GetHash().ToUint256()));
360 : 151 : },
361 : 345 : [&] {
362 : 345 : txdownload_impl.MempoolAcceptedTx(rand_tx);
363 : 345 : },
364 : 204 : [&] {
365 [ + - ]: 204 : TxValidationState state;
366 [ + - + - : 408 : state.Invalid(fuzzed_data_provider.PickValueInArray(TESTED_TX_RESULTS), "");
+ - ]
367 : 204 : bool first_time_failure{fuzzed_data_provider.ConsumeBool()};
368 : :
369 [ + - + - ]: 204 : bool reject_contains_wtxid{txdownload_impl.RecentRejectsFilter().contains(rand_tx->GetWitnessHash().ToUint256())};
370 : :
371 [ + - ]: 204 : node::RejectedTxTodo todo = txdownload_impl.MempoolRejectedTx(rand_tx, state, rand_peer, first_time_failure);
372 [ + + - + : 204 : Assert(first_time_failure || !todo.m_should_add_extra_compact_tx);
+ - ]
373 [ + + + - ]: 204 : if (!reject_contains_wtxid) Assert(todo.m_unique_parents.size() <= rand_tx->vin.size());
374 : 408 : },
375 : 2333 : [&] {
376 [ + + ]: 2333 : GenTxid gtxid = fuzzed_data_provider.ConsumeBool() ?
377 : 2189 : GenTxid::Txid(rand_tx->GetHash()) :
378 : 144 : GenTxid::Wtxid(rand_tx->GetWitnessHash());
379 : 2333 : txdownload_impl.AddTxAnnouncement(rand_peer, gtxid, time);
380 : 2333 : },
381 : 1984 : [&] {
382 : 1984 : const auto getdata_requests = txdownload_impl.GetRequestsToSend(rand_peer, time);
383 : : // TxDownloadManager should not be telling us to request things we already have.
384 : : // Exclude m_lazy_recent_rejects_reconsiderable because it may request low-feerate parent of orphan.
385 [ - + ]: 1984 : for (const auto& gtxid : getdata_requests) {
386 [ # # # # ]: 0 : Assert(!txdownload_impl.AlreadyHaveTx(gtxid, /*include_reconsiderable=*/false));
387 : : }
388 : 1984 : },
389 : 8978 : [&] {
390 [ + + ]: 8978 : const auto& [should_validate, maybe_package] = txdownload_impl.ReceivedTx(rand_peer, rand_tx);
391 : : // The only possible results should be:
392 : : // - Don't validate the tx, no package.
393 : : // - Don't validate the tx, package.
394 : : // - Validate the tx, no package.
395 : : // The only combination that doesn't make sense is validate both tx and package.
396 [ + + + - : 17716 : Assert(!(should_validate && maybe_package.has_value()));
+ - ]
397 [ + + ]: 8978 : if (should_validate) {
398 [ + - + - ]: 8738 : Assert(!txdownload_impl.AlreadyHaveTx(GenTxid::Wtxid(rand_tx->GetWitnessHash()), /*include_reconsiderable=*/true));
399 : : }
400 [ - + ]: 8978 : if (maybe_package.has_value()) {
401 [ # # ]: 0 : CheckPackageToValidate(*maybe_package, rand_peer);
402 : :
403 [ # # ]: 0 : const auto& package = maybe_package->m_txns;
404 : : // Parent is in m_lazy_recent_rejects_reconsiderable and child is in m_orphanage
405 [ # # # # : 0 : Assert(txdownload_impl.RecentRejectsReconsiderableFilter().contains(rand_tx->GetWitnessHash().ToUint256()));
# # ]
406 [ # # # # ]: 0 : Assert(txdownload_impl.m_orphanage.HaveTx(maybe_package->m_txns.back()->GetWitnessHash()));
407 : : // Package has not been rejected
408 [ # # # # : 0 : Assert(!txdownload_impl.RecentRejectsReconsiderableFilter().contains(GetPackageHash(package)));
# # # # ]
409 : : // Neither is in m_lazy_recent_rejects
410 [ # # # # : 0 : Assert(!txdownload_impl.RecentRejectsFilter().contains(package.front()->GetWitnessHash().ToUint256()));
# # ]
411 [ # # # # : 0 : Assert(!txdownload_impl.RecentRejectsFilter().contains(package.back()->GetWitnessHash().ToUint256()));
# # ]
412 : : }
413 : 8978 : },
414 : 203 : [&] {
415 [ + - ]: 406 : txdownload_impl.ReceivedNotFound(rand_peer, {rand_tx->GetWitnessHash()});
416 : 203 : },
417 : 333 : [&] {
418 : 333 : const bool expect_work{txdownload_impl.HaveMoreWork(rand_peer)};
419 : 333 : const auto ptx{txdownload_impl.GetTxToReconsider(rand_peer)};
420 : : // expect_work=true doesn't necessarily mean the next item from the workset isn't a
421 : : // nullptr, as the transaction could have been removed from orphanage without being
422 : : // removed from the peer's workset.
423 [ - + ]: 333 : if (ptx) {
424 : : // However, if there was a non-null tx in the workset, HaveMoreWork should have
425 : : // returned true.
426 [ # # ]: 0 : Assert(expect_work);
427 [ # # # # ]: 0 : Assert(txdownload_impl.AlreadyHaveTx(GenTxid::Wtxid(ptx->GetWitnessHash()), /*include_reconsiderable=*/false));
428 : : // Presumably we have validated this tx. Use "missing inputs" to keep it in the
429 : : // orphanage longer. Later iterations might call MempoolAcceptedTx or
430 : : // MempoolRejectedTx with a different error.
431 [ # # ]: 0 : TxValidationState state_missing_inputs;
432 [ # # # # : 0 : state_missing_inputs.Invalid(TxValidationResult::TX_MISSING_INPUTS, "");
# # ]
433 [ # # ]: 0 : txdownload_impl.MempoolRejectedTx(ptx, state_missing_inputs, rand_peer, fuzzed_data_provider.ConsumeBool());
434 : 0 : }
435 : 333 : }
436 : : );
437 : :
438 : 27307 : auto time_skip = fuzzed_data_provider.PickValueInArray(TIME_SKIPS);
439 [ + + ]: 27307 : if (fuzzed_data_provider.ConsumeBool()) time_skip *= -1;
440 [ + - ]: 27307 : time += time_skip;
441 : 27307 : }
442 [ + - ]: 1751 : CheckInvariants(txdownload_impl, max_orphan_count);
443 : : // Disconnect everybody, check that all data structures are empty.
444 [ + + ]: 29767 : for (NodeId nodeid = 0; nodeid < NUM_PEERS; ++nodeid) {
445 [ + - ]: 28016 : txdownload_impl.DisconnectedPeer(nodeid);
446 [ + - ]: 28016 : txdownload_impl.CheckIsEmpty(nodeid);
447 : : }
448 [ + - ]: 1751 : txdownload_impl.CheckIsEmpty();
449 : 3502 : }
450 : :
451 : : } // namespace
|