Bitcoin Core  28.1.0
P2P Digital Currency
txorphan.cpp
Go to the documentation of this file.
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 <policy/policy.h>
10 #include <primitives/transaction.h>
11 #include <script/script.h>
12 #include <sync.h>
14 #include <test/fuzz/fuzz.h>
15 #include <test/fuzz/util.h>
16 #include <test/util/setup_common.h>
17 #include <txorphanage.h>
18 #include <uint256.h>
19 #include <util/check.h>
20 #include <util/time.h>
21 
22 #include <cstdint>
23 #include <memory>
24 #include <set>
25 #include <utility>
26 #include <vector>
27 
29 {
30  static const auto testing_setup = MakeNoLogFileContext();
31 }
32 
34 {
35  FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
36  FastRandomContext limit_orphans_rng{/*fDeterministic=*/true};
37  SetMockTime(ConsumeTime(fuzzed_data_provider));
38 
39  TxOrphanage orphanage;
40  std::vector<COutPoint> outpoints; // Duplicates are tolerated
41 
42  // initial outpoints used to construct transactions later
43  for (uint8_t i = 0; i < 4; i++) {
44  outpoints.emplace_back(Txid::FromUint256(uint256{i}), 0);
45  }
46 
47  CTransactionRef ptx_potential_parent = nullptr;
48 
49  LIMITED_WHILE(outpoints.size() < 200'000 && fuzzed_data_provider.ConsumeBool(), 10 * DEFAULT_MAX_ORPHAN_TRANSACTIONS)
50  {
51  // construct transaction
52  const CTransactionRef tx = [&] {
53  CMutableTransaction tx_mut;
54  const auto num_in = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, outpoints.size());
55  const auto num_out = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, 256);
56  // pick outpoints from outpoints as input. We allow input duplicates on purpose, given we are not
57  // running any transaction validation logic before adding transactions to the orphanage
58  for (uint32_t i = 0; i < num_in; i++) {
59  auto& prevout = PickValue(fuzzed_data_provider, outpoints);
60  // try making transactions unique by setting a random nSequence, but allow duplicate transactions if they happen
61  tx_mut.vin.emplace_back(prevout, CScript{}, fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(0, CTxIn::SEQUENCE_FINAL));
62  }
63  // output amount will not affect txorphanage
64  for (uint32_t i = 0; i < num_out; i++) {
65  tx_mut.vout.emplace_back(CAmount{0}, CScript{});
66  }
67  auto new_tx = MakeTransactionRef(tx_mut);
68  // add newly constructed outpoints to the coin pool
69  for (uint32_t i = 0; i < num_out; i++) {
70  outpoints.emplace_back(new_tx->GetHash(), i);
71  }
72  return new_tx;
73  }();
74 
75  // Trigger orphanage functions that are called using parents. ptx_potential_parent is a tx we constructed in a
76  // previous loop and potentially the parent of this tx.
77  if (ptx_potential_parent) {
78  // Set up future GetTxToReconsider call.
79  orphanage.AddChildrenToWorkSet(*ptx_potential_parent);
80 
81  // Check that all txns returned from GetChildrenFrom* are indeed a direct child of this tx.
82  NodeId peer_id = fuzzed_data_provider.ConsumeIntegral<NodeId>();
83  for (const auto& child : orphanage.GetChildrenFromSamePeer(ptx_potential_parent, peer_id)) {
84  assert(std::any_of(child->vin.cbegin(), child->vin.cend(), [&](const auto& input) {
85  return input.prevout.hash == ptx_potential_parent->GetHash();
86  }));
87  }
88  for (const auto& [child, peer] : orphanage.GetChildrenFromDifferentPeer(ptx_potential_parent, peer_id)) {
89  assert(std::any_of(child->vin.cbegin(), child->vin.cend(), [&](const auto& input) {
90  return input.prevout.hash == ptx_potential_parent->GetHash();
91  }));
92  assert(peer != peer_id);
93  }
94  }
95 
96  // trigger orphanage functions
97  LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10 * DEFAULT_MAX_ORPHAN_TRANSACTIONS)
98  {
99  NodeId peer_id = fuzzed_data_provider.ConsumeIntegral<NodeId>();
100 
101  CallOneOf(
102  fuzzed_data_provider,
103  [&] {
104  {
105  CTransactionRef ref = orphanage.GetTxToReconsider(peer_id);
106  if (ref) {
107  Assert(orphanage.HaveTx(ref->GetWitnessHash()));
108  }
109  }
110  },
111  [&] {
112  bool have_tx = orphanage.HaveTx(tx->GetWitnessHash());
113  // AddTx should return false if tx is too big or already have it
114  // tx weight is unknown, we only check when tx is already in orphanage
115  {
116  bool add_tx = orphanage.AddTx(tx, peer_id);
117  // have_tx == true -> add_tx == false
118  Assert(!have_tx || !add_tx);
119  }
120  have_tx = orphanage.HaveTx(tx->GetWitnessHash());
121  {
122  bool add_tx = orphanage.AddTx(tx, peer_id);
123  // if have_tx is still false, it must be too big
124  Assert(!have_tx == (GetTransactionWeight(*tx) > MAX_STANDARD_TX_WEIGHT));
125  Assert(!have_tx || !add_tx);
126  }
127  },
128  [&] {
129  bool have_tx = orphanage.HaveTx(tx->GetWitnessHash());
130  // EraseTx should return 0 if m_orphans doesn't have the tx
131  {
132  Assert(have_tx == orphanage.EraseTx(tx->GetWitnessHash()));
133  }
134  have_tx = orphanage.HaveTx(tx->GetWitnessHash());
135  // have_tx should be false and EraseTx should fail
136  {
137  Assert(!have_tx && !orphanage.EraseTx(tx->GetWitnessHash()));
138  }
139  },
140  [&] {
141  orphanage.EraseForPeer(peer_id);
142  },
143  [&] {
144  // test mocktime and expiry
145  SetMockTime(ConsumeTime(fuzzed_data_provider));
146  auto limit = fuzzed_data_provider.ConsumeIntegral<unsigned int>();
147  orphanage.LimitOrphans(limit, limit_orphans_rng);
148  Assert(orphanage.Size() <= limit);
149  });
150 
151  }
152  // Set tx as potential parent to be used for future GetChildren() calls.
153  if (!ptx_potential_parent || fuzzed_data_provider.ConsumeBool()) {
154  ptx_potential_parent = tx;
155  }
156 
157  }
158 }
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:423
int EraseTx(const Wtxid &wtxid)
Erase an orphan by wtxid.
Definition: txorphanage.cpp:48
void initialize_orphanage()
Definition: txorphan.cpp:28
A class to track orphan transactions (failed on TX_MISSING_INPUTS) Since we cannot distinguish orphan...
Definition: txorphanage.h:28
#define LIMITED_WHILE(condition, limit)
Can be used to limit a theoretically unbounded loop.
Definition: fuzz.h:22
void SetMockTime(int64_t nMockTimeIn)
DEPRECATED Use SetMockTime with chrono type.
Definition: time.cpp:32
Fast randomness source.
Definition: random.h:376
256-bit opaque blob.
Definition: uint256.h:178
int64_t ConsumeTime(FuzzedDataProvider &fuzzed_data_provider, const std::optional< int64_t > &min, const std::optional< int64_t > &max) noexcept
Definition: util.cpp:34
static transaction_identifier FromUint256(const uint256 &id)
FUZZ_TARGET(txorphan,.init=initialize_orphanage)
Definition: txorphan.cpp:33
bool HaveTx(const Wtxid &wtxid) const
Check if we already have an orphan transaction (by wtxid only)
#define Assert(val)
Identity function.
Definition: check.h:77
std::unique_ptr< T > MakeNoLogFileContext(const ChainType chain_type=ChainType::REGTEST, TestOpts opts={})
Make a test setup that has disk access to the debug.log file disabled.
Definition: setup_common.h:230