Bitcoin Core 31.0.0
P2P Digital Currency
Loading...
Searching...
No Matches
txdownloadman.cpp
Go to the documentation of this file.
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
6#include <node/context.h>
7#include <node/mempool_args.h>
8#include <node/miner.h>
12#include <test/fuzz/fuzz.h>
13#include <test/fuzz/util.h>
15#include <test/util/mining.h>
16#include <test/util/script.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
26namespace {
27
29
30constexpr size_t NUM_COINS{50};
32
34 // Skip TX_RESULT_UNSET
44 // Skip TX_NO_MEMPOOL
47};
48
49// Precomputed transactions. Some may conflict with each other.
50std::vector<CTransactionRef> TRANSACTIONS;
51
52// Limit the total number of peers because we don't expect coverage to change much with lots more peers.
53constexpr int NUM_PEERS = 16;
54
55// Precomputed random durations (positive and negative, each ~exponentially distributed).
56std::chrono::microseconds TIME_SKIPS[128];
57
58static CTransactionRef MakeTransactionSpending(const std::vector<COutPoint>& outpoints, size_t num_outputs, bool add_witness)
59{
61 // If no outpoints are given, create a random one.
62 for (const auto& outpoint : outpoints) {
63 tx.vin.emplace_back(outpoint);
64 }
65 if (add_witness) {
66 tx.vin[0].scriptWitness.stack.push_back({1});
67 }
68 for (size_t o = 0; o < num_outputs; ++o) tx.vout.emplace_back(CENT, P2WSH_OP_TRUE);
69 return MakeTransactionRef(tx);
70}
71static std::vector<COutPoint> PickCoins(FuzzedDataProvider& fuzzed_data_provider)
72{
73 std::vector<COutPoint> ret;
77 }
78 return ret;
79}
80
81void initialize()
82{
83 static const auto testing_setup = MakeNoLogFileContext<const TestingSetup>();
84 g_setup = testing_setup.get();
85 for (uint32_t i = 0; i < uint32_t{NUM_COINS}; ++i) {
86 COINS[i] = COutPoint{Txid::FromUint256((HashWriter() << i).GetHash()), i};
87 }
88 size_t outpoints_index = 0;
89 // 2 transactions same txid different witness
90 {
91 auto tx1{MakeTransactionSpending({COINS[outpoints_index]}, /*num_outputs=*/5, /*add_witness=*/false)};
92 auto tx2{MakeTransactionSpending({COINS[outpoints_index]}, /*num_outputs=*/5, /*add_witness=*/true)};
93 Assert(tx1->GetHash() == tx2->GetHash());
94 TRANSACTIONS.emplace_back(tx1);
95 TRANSACTIONS.emplace_back(tx2);
96 outpoints_index += 1;
97 }
98 // 2 parents 1 child
99 {
100 auto tx_parent_1{MakeTransactionSpending({COINS[outpoints_index++]}, /*num_outputs=*/1, /*add_witness=*/true)};
101 TRANSACTIONS.emplace_back(tx_parent_1);
102 auto tx_parent_2{MakeTransactionSpending({COINS[outpoints_index++]}, /*num_outputs=*/1, /*add_witness=*/false)};
103 TRANSACTIONS.emplace_back(tx_parent_2);
104 TRANSACTIONS.emplace_back(MakeTransactionSpending({COutPoint{tx_parent_1->GetHash(), 0}, COutPoint{tx_parent_2->GetHash(), 0}},
105 /*num_outputs=*/1, /*add_witness=*/true));
106 }
107 // 1 parent 2 children
108 {
109 auto tx_parent{MakeTransactionSpending({COINS[outpoints_index++]}, /*num_outputs=*/2, /*add_witness=*/true)};
110 TRANSACTIONS.emplace_back(tx_parent);
111 TRANSACTIONS.emplace_back(MakeTransactionSpending({COutPoint{tx_parent->GetHash(), 0}},
112 /*num_outputs=*/1, /*add_witness=*/true));
113 TRANSACTIONS.emplace_back(MakeTransactionSpending({COutPoint{tx_parent->GetHash(), 1}},
114 /*num_outputs=*/1, /*add_witness=*/true));
115 }
116 // chain of 5 segwit
117 {
119 for (auto i{0}; i < 5; ++i) {
120 auto tx{MakeTransactionSpending({last_outpoint}, /*num_outputs=*/1, /*add_witness=*/true)};
121 TRANSACTIONS.emplace_back(tx);
122 last_outpoint = COutPoint{tx->GetHash(), 0};
123 }
124 }
125 // chain of 5 non-segwit
126 {
128 for (auto i{0}; i < 5; ++i) {
129 auto tx{MakeTransactionSpending({last_outpoint}, /*num_outputs=*/1, /*add_witness=*/false)};
130 TRANSACTIONS.emplace_back(tx);
131 last_outpoint = COutPoint{tx->GetHash(), 0};
132 }
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 for (const auto& outpoint : COINS) {
137 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 for (; i < 16; ++i) {
144 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 for (; i < 128; ++i) {
149 int diff_bits = ((i - 10) * 2) / 9;
150 uint64_t diff = 1 + (CSipHasher(0, 0).Write(i).Finalize() >> (64 - diff_bits));
151 TIME_SKIPS[i] = TIME_SKIPS[i - 1] + std::chrono::microseconds{diff};
152 }
153}
154
156{
157 Assert(package_to_validate.m_senders.size() == 2);
158 Assert(package_to_validate.m_senders.front() == peer);
159 Assert(package_to_validate.m_senders.back() < NUM_PEERS);
160
161 // Package is a 1p1c
162 const auto& package = package_to_validate.m_txns;
164 Assert(package.size() == 2);
165}
166
168{
170 FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
172
173 // Initialize txdownloadman
174 bilingual_str error;
178
179 std::chrono::microseconds time{244466666};
180
182 {
184
185 // Transaction can be one of the premade ones or a randomly generated one
188 /*num_outputs=*/fuzzed_data_provider.ConsumeIntegralInRange(1, 500),
189 /*add_witness=*/fuzzed_data_provider.ConsumeBool()) :
190 TRANSACTIONS.at(fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, TRANSACTIONS.size() - 1));
191
192 CallOneOf(
194 [&] {
197 .m_relay_permissions = fuzzed_data_provider.ConsumeBool(),
198 .m_wtxid_relay = fuzzed_data_provider.ConsumeBool()
199 };
200 txdownloadman.ConnectedPeer(rand_peer, info);
201 },
202 [&] {
203 txdownloadman.DisconnectedPeer(rand_peer);
204 txdownloadman.CheckIsEmpty(rand_peer);
205 },
206 [&] {
207 txdownloadman.ActiveTipChange();
208 },
209 [&] {
210 CBlock block;
211 block.vtx.push_back(rand_tx);
212 txdownloadman.BlockConnected(std::make_shared<CBlock>(block));
213 },
214 [&] {
215 txdownloadman.BlockDisconnected();
216 },
217 [&] {
218 txdownloadman.MempoolAcceptedTx(rand_tx);
219 },
220 [&] {
221 TxValidationState state;
224
226 Assert(first_time_failure || !todo.m_should_add_extra_compact_tx);
227 },
228 [&] {
230 GenTxid{rand_tx->GetHash()} :
231 GenTxid{rand_tx->GetWitnessHash()};
232 txdownloadman.AddTxAnnouncement(rand_peer, gtxid, time);
233 },
234 [&] {
235 txdownloadman.GetRequestsToSend(rand_peer, time);
236 },
237 [&] {
238 txdownloadman.ReceivedTx(rand_peer, rand_tx);
239 const auto& [should_validate, maybe_package] = txdownloadman.ReceivedTx(rand_peer, rand_tx);
240 // The only possible results should be:
241 // - Don't validate the tx, no package.
242 // - Don't validate the tx, package.
243 // - Validate the tx, no package.
244 // The only combination that doesn't make sense is validate both tx and package.
245 Assert(!(should_validate && maybe_package.has_value()));
247 },
248 [&] {
249 txdownloadman.ReceivedNotFound(rand_peer, {rand_tx->GetWitnessHash()});
250 },
251 [&] {
252 const bool expect_work{txdownloadman.HaveMoreWork(rand_peer)};
253 const auto ptx = txdownloadman.GetTxToReconsider(rand_peer);
254 // expect_work=true doesn't necessarily mean the next item from the workset isn't a
255 // nullptr, as the transaction could have been removed from orphanage without being
256 // removed from the peer's workset.
257 if (ptx) {
258 // However, if there was a non-null tx in the workset, HaveMoreWork should have
259 // returned true.
261 }
262 });
263 // Jump forwards or backwards
266 time += time_skip;
267 }
268 // Disconnect everybody, check that all data structures are empty.
269 for (NodeId nodeid = 0; nodeid < NUM_PEERS; ++nodeid) {
270 txdownloadman.DisconnectedPeer(nodeid);
271 txdownloadman.CheckIsEmpty(nodeid);
272 }
273 txdownloadman.CheckIsEmpty();
274}
275
276// Give node 0 relay permissions, and nobody else. This helps us remember who is a RelayPermissions
277// peer without tracking anything (this is only for the txdownload_impl target).
278static bool HasRelayPermissions(NodeId peer) { return peer == 0; }
279
281{
282 txdownload_impl.m_orphanage->SanityCheck();
283 // We should never have more than the maximum in-flight requests out for a peer.
284 for (NodeId peer = 0; peer < NUM_PEERS; ++peer) {
285 if (!HasRelayPermissions(peer)) {
286 Assert(txdownload_impl.m_txrequest.Count(peer) <= node::MAX_PEER_TX_ANNOUNCEMENTS);
287 }
288 }
289 txdownload_impl.m_txrequest.SanityCheck();
290}
291
293{
295 FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
297
298 // Initialize a TxDownloadManagerImpl
299 bilingual_str error;
303
304 std::chrono::microseconds time{244466666};
305
307 {
309
310 // Transaction can be one of the premade ones or a randomly generated one
313 /*num_outputs=*/fuzzed_data_provider.ConsumeIntegralInRange(1, 500),
314 /*add_witness=*/fuzzed_data_provider.ConsumeBool()) :
315 TRANSACTIONS.at(fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, TRANSACTIONS.size() - 1));
316
317 CallOneOf(
319 [&] {
322 .m_relay_permissions = HasRelayPermissions(rand_peer),
323 .m_wtxid_relay = fuzzed_data_provider.ConsumeBool()
324 };
325 txdownload_impl.ConnectedPeer(rand_peer, info);
326 },
327 [&] {
328 txdownload_impl.DisconnectedPeer(rand_peer);
329 txdownload_impl.CheckIsEmpty(rand_peer);
330 },
331 [&] {
332 txdownload_impl.ActiveTipChange();
333 // After a block update, nothing should be in the rejection caches
334 for (const auto& tx : TRANSACTIONS) {
335 Assert(!txdownload_impl.RecentRejectsFilter().contains(tx->GetWitnessHash().ToUint256()));
336 Assert(!txdownload_impl.RecentRejectsFilter().contains(tx->GetHash().ToUint256()));
337 Assert(!txdownload_impl.RecentRejectsReconsiderableFilter().contains(tx->GetWitnessHash().ToUint256()));
338 Assert(!txdownload_impl.RecentRejectsReconsiderableFilter().contains(tx->GetHash().ToUint256()));
339 }
340 },
341 [&] {
342 CBlock block;
343 block.vtx.push_back(rand_tx);
344 txdownload_impl.BlockConnected(std::make_shared<CBlock>(block));
345 // Block transactions must be removed from orphanage
346 Assert(!txdownload_impl.m_orphanage->HaveTx(rand_tx->GetWitnessHash()));
347 },
348 [&] {
349 txdownload_impl.BlockDisconnected();
350 Assert(!txdownload_impl.RecentConfirmedTransactionsFilter().contains(rand_tx->GetWitnessHash().ToUint256()));
351 Assert(!txdownload_impl.RecentConfirmedTransactionsFilter().contains(rand_tx->GetHash().ToUint256()));
352 },
353 [&] {
354 txdownload_impl.MempoolAcceptedTx(rand_tx);
355 },
356 [&] {
357 TxValidationState state;
360
361 bool reject_contains_wtxid{txdownload_impl.RecentRejectsFilter().contains(rand_tx->GetWitnessHash().ToUint256())};
362
364 Assert(first_time_failure || !todo.m_should_add_extra_compact_tx);
365 if (!reject_contains_wtxid) Assert(todo.m_unique_parents.size() <= rand_tx->vin.size());
366 },
367 [&] {
369 GenTxid{rand_tx->GetHash()} :
370 GenTxid{rand_tx->GetWitnessHash()};
371 txdownload_impl.AddTxAnnouncement(rand_peer, gtxid, time);
372 },
373 [&] {
374 const auto getdata_requests = txdownload_impl.GetRequestsToSend(rand_peer, time);
375 // TxDownloadManager should not be telling us to request things we already have.
376 // Exclude m_lazy_recent_rejects_reconsiderable because it may request low-feerate parent of orphan.
377 for (const auto& gtxid : getdata_requests) {
378 Assert(!txdownload_impl.AlreadyHaveTx(gtxid, /*include_reconsiderable=*/false));
379 }
380 },
381 [&] {
382 const auto& [should_validate, maybe_package] = txdownload_impl.ReceivedTx(rand_peer, rand_tx);
383 // The only possible results should be:
384 // - Don't validate the tx, no package.
385 // - Don't validate the tx, package.
386 // - Validate the tx, no package.
387 // The only combination that doesn't make sense is validate both tx and package.
388 Assert(!(should_validate && maybe_package.has_value()));
389 if (should_validate) {
390 Assert(!txdownload_impl.AlreadyHaveTx(rand_tx->GetWitnessHash(), /*include_reconsiderable=*/true));
391 }
392 if (maybe_package.has_value()) {
394
395 const auto& package = maybe_package->m_txns;
396 // Parent is in m_lazy_recent_rejects_reconsiderable and child is in m_orphanage
397 Assert(txdownload_impl.RecentRejectsReconsiderableFilter().contains(rand_tx->GetWitnessHash().ToUint256()));
398 Assert(txdownload_impl.m_orphanage->HaveTx(maybe_package->m_txns.back()->GetWitnessHash()));
399 // Package has not been rejected
400 Assert(!txdownload_impl.RecentRejectsReconsiderableFilter().contains(GetPackageHash(package)));
401 // Neither is in m_lazy_recent_rejects
402 Assert(!txdownload_impl.RecentRejectsFilter().contains(package.front()->GetWitnessHash().ToUint256()));
403 Assert(!txdownload_impl.RecentRejectsFilter().contains(package.back()->GetWitnessHash().ToUint256()));
404 }
405 },
406 [&] {
407 txdownload_impl.ReceivedNotFound(rand_peer, {rand_tx->GetWitnessHash()});
408 },
409 [&] {
410 const bool expect_work{txdownload_impl.HaveMoreWork(rand_peer)};
411 const auto ptx{txdownload_impl.GetTxToReconsider(rand_peer)};
412 // expect_work=true doesn't necessarily mean the next item from the workset isn't a
413 // nullptr, as the transaction could have been removed from orphanage without being
414 // removed from the peer's workset.
415 if (ptx) {
416 // However, if there was a non-null tx in the workset, HaveMoreWork should have
417 // returned true.
419 Assert(txdownload_impl.AlreadyHaveTx(ptx->GetWitnessHash(), /*include_reconsiderable=*/false));
420 // Presumably we have validated this tx. Use "missing inputs" to keep it in the
421 // orphanage longer. Later iterations might call MempoolAcceptedTx or
422 // MempoolRejectedTx with a different error.
426 }
427 });
428
431 time += time_skip;
432 }
434 // Disconnect everybody, check that all data structures are empty.
435 for (NodeId nodeid = 0; nodeid < NUM_PEERS; ++nodeid) {
436 txdownload_impl.DisconnectedPeer(nodeid);
437 txdownload_impl.CheckIsEmpty(nodeid);
438 }
439 txdownload_impl.CheckIsEmpty();
440}
441
442} // namespace
int ret
const TestingSetup * g_setup
#define Assert(val)
Identity function.
Definition check.h:113
Definition block.h:74
std::vector< CTransactionRef > vtx
Definition block.h:77
An outpoint - a combination of a transaction hash and an index n into its vout.
Definition transaction.h:29
General SipHash-2-4 implementation.
Definition siphash.h:27
uint64_t Finalize() const
Compute the 64-bit SipHash-2-4 of the data written so far.
Definition siphash.cpp:73
CSipHasher & Write(uint64_t data)
Hash a 64-bit integer worth of data.
Definition siphash.cpp:24
CTxMemPool stores valid-according-to-the-current-best-chain transactions that may be included in the ...
Definition txmempool.h:187
Fast randomness source.
Definition random.h:386
T ConsumeIntegralInRange(T min, T max)
T PickValueInArray(const T(&array)[size])
A writer stream (for serialization) that computes a 256-bit hash.
Definition hash.h:101
bool Invalid(Result result, const std::string &reject_reason="", const std::string &debug_message="")
Definition validation.h:88
Class responsible for deciding what transactions to request and, once downloaded, whether and how to ...
const uint256 & ToUint256() const LIFETIMEBOUND
static transaction_identifier FromUint256(const uint256 &id)
TxValidationResult
A "reason" why a transaction was invalid, suitable for determining whether the provider of the transa...
Definition validation.h:23
@ TX_MISSING_INPUTS
transaction was missing some of its inputs
@ TX_MEMPOOL_POLICY
violated mempool's fee/size/descendant/RBF/etc limits
@ TX_UNKNOWN
transaction was not validated because package failed
@ TX_PREMATURE_SPEND
transaction spends a coinbase too early, or violates locktime/sequence locks
@ TX_INPUTS_NOT_STANDARD
inputs (covered by txid) failed policy rules
@ TX_WITNESS_STRIPPED
Transaction is missing a witness.
@ TX_CONFLICT
Tx already in mempool or conflicts with a tx in the chain (if it conflicts with another tx in mempool...
@ TX_NOT_STANDARD
otherwise didn't meet our local policy rules
@ TX_WITNESS_MUTATED
Transaction might have a witness prior to SegWit activation, or witness may have been malleated (whic...
@ TX_CONSENSUS
invalid by consensus rules
@ TX_RECONSIDERABLE
fails some policy, but might be acceptable if submitted in a (different) package
static void initialize()
Definition fuzz.cpp:95
#define FUZZ_TARGET(...)
Definition fuzz.h:35
#define LIMITED_WHILE(condition, limit)
Can be used to limit a theoretically unbounded loop.
Definition fuzz.h:22
static constexpr int32_t MAX_PEER_TX_ANNOUNCEMENTS
Maximum number of transactions to consider for requesting, per peer.
int64_t NodeId
Definition net.h:103
static CTransactionRef MakeTransactionSpending(const std::vector< COutPoint > &outpoints, FastRandomContext &det_rand)
bool IsChildWithParents(const Package &package)
Context-free check that a package is exactly one child and its parents; not all parents need to be pr...
Definition packages.cpp:119
uint256 GetPackageHash(const std::vector< CTransactionRef > &transactions)
Get the hash of the concatenated wtxids of transactions, with wtxids treated as a little-endian numbe...
Definition packages.cpp:151
static CTransactionRef MakeTransactionRef(Tx &&txIn)
std::shared_ptr< const CTransaction > CTransactionRef
static constexpr CAmount CENT
node::NodeContext m_node
A mutable version of CTransaction.
std::vector< CTxOut > vout
Txid GetHash() const
Compute the hash of this CMutableTransaction.
std::vector< CTxIn > vin
Testing setup that configures a complete environment.
Bilingual messages:
Definition translation.h:24
const bool m_preferred
Whether this peer is preferred for transaction download.
NodeSeconds ConsumeTime(FuzzedDataProvider &fuzzed_data_provider, const std::optional< int64_t > &min, const std::optional< int64_t > &max) noexcept
Definition util.cpp:34
size_t CallOneOf(FuzzedDataProvider &fuzzed_data_provider, Callables... callables)
Definition util.h:35
void SeedRandomStateForTest(SeedRand seedtype)
Seed the global RNG state for testing and log the seed value.
Definition random.cpp:19
@ ZEROS
Seed with a compile time constant of zeros.
static const CScript P2WSH_OP_TRUE
Definition script.h:13
CTxMemPool::Options MemPoolOptionsForTest(const NodeContext &node)
Definition txmempool.cpp:21
void SetMockTime(int64_t nMockTimeIn)
DEPRECATED Use SetMockTime with chrono type.
Definition time.cpp:44
constexpr auto Ticks(Dur2 d)
Helper to count the seconds of a duration/time_point.
Definition time.h:73
FuzzedDataProvider & fuzzed_data_provider
Definition fees.cpp:38