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