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