Bitcoin Core  27.99.0
P2P Digital Currency
txorphan.cpp
Go to the documentation of this file.
1 // Copyright (c) 2022 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;
41  // initial outpoints used to construct transactions later
42  for (uint8_t i = 0; i < 4; i++) {
43  outpoints.emplace_back(Txid::FromUint256(uint256{i}), 0);
44  }
45  // if true, allow duplicate input when constructing tx
46  const bool duplicate_input = fuzzed_data_provider.ConsumeBool();
47 
48  CTransactionRef ptx_potential_parent = nullptr;
49 
50  LIMITED_WHILE(outpoints.size() < 200'000 && fuzzed_data_provider.ConsumeBool(), 10 * DEFAULT_MAX_ORPHAN_TRANSACTIONS)
51  {
52  // construct transaction
53  const CTransactionRef tx = [&] {
54  CMutableTransaction tx_mut;
55  const auto num_in = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, outpoints.size());
56  const auto num_out = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, outpoints.size());
57  // pick unique outpoints from outpoints as input
58  for (uint32_t i = 0; i < num_in; i++) {
59  auto& prevout = PickValue(fuzzed_data_provider, outpoints);
60  tx_mut.vin.emplace_back(prevout);
61  // pop the picked outpoint if duplicate input is not allowed
62  if (!duplicate_input) {
63  std::swap(prevout, outpoints.back());
64  outpoints.pop_back();
65  }
66  }
67  // output amount will not affect txorphanage
68  for (uint32_t i = 0; i < num_out; i++) {
69  tx_mut.vout.emplace_back(CAmount{0}, CScript{});
70  }
71  // restore previously popped outpoints
72  for (auto& in : tx_mut.vin) {
73  outpoints.push_back(in.prevout);
74  }
75  auto new_tx = MakeTransactionRef(tx_mut);
76  // add newly constructed transaction to outpoints
77  for (uint32_t i = 0; i < num_out; i++) {
78  outpoints.emplace_back(new_tx->GetHash(), i);
79  }
80  return new_tx;
81  }();
82 
83  // Trigger orphanage functions that are called using parents. ptx_potential_parent is a tx we constructed in a
84  // previous loop and potentially the parent of this tx.
85  if (ptx_potential_parent) {
86  // Set up future GetTxToReconsider call.
87  orphanage.AddChildrenToWorkSet(*ptx_potential_parent);
88 
89  // Check that all txns returned from GetChildrenFrom* are indeed a direct child of this tx.
90  NodeId peer_id = fuzzed_data_provider.ConsumeIntegral<NodeId>();
91  for (const auto& child : orphanage.GetChildrenFromSamePeer(ptx_potential_parent, peer_id)) {
92  assert(std::any_of(child->vin.cbegin(), child->vin.cend(), [&](const auto& input) {
93  return input.prevout.hash == ptx_potential_parent->GetHash();
94  }));
95  }
96  for (const auto& [child, peer] : orphanage.GetChildrenFromDifferentPeer(ptx_potential_parent, peer_id)) {
97  assert(std::any_of(child->vin.cbegin(), child->vin.cend(), [&](const auto& input) {
98  return input.prevout.hash == ptx_potential_parent->GetHash();
99  }));
100  assert(peer != peer_id);
101  }
102  }
103 
104  // trigger orphanage functions
105  LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10 * DEFAULT_MAX_ORPHAN_TRANSACTIONS)
106  {
107  NodeId peer_id = fuzzed_data_provider.ConsumeIntegral<NodeId>();
108 
109  CallOneOf(
110  fuzzed_data_provider,
111  [&] {
112  {
113  CTransactionRef ref = orphanage.GetTxToReconsider(peer_id);
114  if (ref) {
115  bool have_tx = orphanage.HaveTx(GenTxid::Txid(ref->GetHash())) || orphanage.HaveTx(GenTxid::Wtxid(ref->GetWitnessHash()));
116  Assert(have_tx);
117  }
118  }
119  },
120  [&] {
121  bool have_tx = orphanage.HaveTx(GenTxid::Txid(tx->GetHash())) || orphanage.HaveTx(GenTxid::Wtxid(tx->GetWitnessHash()));
122  // AddTx should return false if tx is too big or already have it
123  // tx weight is unknown, we only check when tx is already in orphanage
124  {
125  bool add_tx = orphanage.AddTx(tx, peer_id);
126  // have_tx == true -> add_tx == false
127  Assert(!have_tx || !add_tx);
128  }
129  have_tx = orphanage.HaveTx(GenTxid::Txid(tx->GetHash())) || orphanage.HaveTx(GenTxid::Wtxid(tx->GetWitnessHash()));
130  {
131  bool add_tx = orphanage.AddTx(tx, peer_id);
132  // if have_tx is still false, it must be too big
133  Assert(!have_tx == (GetTransactionWeight(*tx) > MAX_STANDARD_TX_WEIGHT));
134  Assert(!have_tx || !add_tx);
135  }
136  },
137  [&] {
138  bool have_tx = orphanage.HaveTx(GenTxid::Txid(tx->GetHash())) || orphanage.HaveTx(GenTxid::Wtxid(tx->GetWitnessHash()));
139  // EraseTx should return 0 if m_orphans doesn't have the tx
140  {
141  Assert(have_tx == orphanage.EraseTx(tx->GetHash()));
142  }
143  have_tx = orphanage.HaveTx(GenTxid::Txid(tx->GetHash())) || orphanage.HaveTx(GenTxid::Wtxid(tx->GetWitnessHash()));
144  // have_tx should be false and EraseTx should fail
145  {
146  Assert(!have_tx && !orphanage.EraseTx(tx->GetHash()));
147  }
148  },
149  [&] {
150  orphanage.EraseForPeer(peer_id);
151  },
152  [&] {
153  // test mocktime and expiry
154  SetMockTime(ConsumeTime(fuzzed_data_provider));
155  auto limit = fuzzed_data_provider.ConsumeIntegral<unsigned int>();
156  orphanage.LimitOrphans(limit, limit_orphans_rng);
157  Assert(orphanage.Size() <= limit);
158  });
159 
160  }
161  // Set tx as potential parent to be used for future GetChildren() calls.
162  if (!ptx_potential_parent || fuzzed_data_provider.ConsumeBool()) {
163  ptx_potential_parent = tx;
164  }
165 
166  }
167 }
int64_t CAmount
Amount in satoshis (Can be negative)
Definition: amount.h:12
#define Assert(val)
Identity function.
Definition: check.h:77
Serialized script, used inside transaction inputs and outputs.
Definition: script.h:414
Fast randomness source.
Definition: random.h:145
T ConsumeIntegralInRange(T min, T max)
static GenTxid Wtxid(const uint256 &hash)
Definition: transaction.h:435
static GenTxid Txid(const uint256 &hash)
Definition: transaction.h:434
A class to track orphan transactions (failed on TX_MISSING_INPUTS) Since we cannot distinguish orphan...
Definition: txorphanage.h:21
void AddChildrenToWorkSet(const CTransaction &tx) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Add any orphans that list a particular tx as a parent into the from peer's work set.
void LimitOrphans(unsigned int max_orphans, FastRandomContext &rng) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Limit the orphanage to the given maximum.
bool HaveTx(const GenTxid &gtxid) const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Check if we already have an orphan transaction (by txid or wtxid)
std::vector< std::pair< CTransactionRef, NodeId > > GetChildrenFromDifferentPeer(const CTransactionRef &parent, NodeId nodeid) const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Get all children that spend from this tx but were not received from nodeid.
size_t Size() EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Return how many entries exist in the orphange.
Definition: txorphanage.h:63
CTransactionRef GetTxToReconsider(NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Extract a transaction from a peer's work set Returns nullptr if there are no transactions to work on.
void EraseForPeer(NodeId peer) EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Erase all orphans announced by a peer (eg, after that peer disconnects)
Definition: txorphanage.cpp:97
std::vector< CTransactionRef > GetChildrenFromSamePeer(const CTransactionRef &parent, NodeId nodeid) const EXCLUSIVE_LOCKS_REQUIRED(!m_mutex)
Get all children that spend from this tx and were received from nodeid.
static transaction_identifier FromUint256(const uint256 &id)
256-bit opaque blob.
Definition: uint256.h:106
#define LIMITED_WHILE(condition, limit)
Can be used to limit a theoretically unbounded loop.
Definition: fuzz.h:23
int64_t NodeId
Definition: net.h:97
static const uint32_t DEFAULT_MAX_ORPHAN_TRANSACTIONS
Default for -maxorphantx, maximum number of orphan transactions kept in memory.
static CTransactionRef MakeTransactionRef(Tx &&txIn)
Definition: transaction.h:424
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:423
std::unique_ptr< T > MakeNoLogFileContext(const ChainType chain_type=ChainType::REGTEST, const std::vector< const char * > &extra_args={})
Make a test setup that has disk access to the debug.log file disabled.
Definition: setup_common.h:221
A mutable version of CTransaction.
Definition: transaction.h:378
std::vector< CTxOut > vout
Definition: transaction.h:380
std::vector< CTxIn > vin
Definition: transaction.h:379
int64_t ConsumeTime(FuzzedDataProvider &fuzzed_data_provider, const std::optional< int64_t > &min, const std::optional< int64_t > &max) noexcept
Definition: util.cpp:34
auto & PickValue(FuzzedDataProvider &fuzzed_data_provider, Collection &col)
Definition: util.h:47
size_t CallOneOf(FuzzedDataProvider &fuzzed_data_provider, Callables... callables)
Definition: util.h:35
void SetMockTime(int64_t nMockTimeIn)
DEPRECATED Use SetMockTime with chrono type.
Definition: time.cpp:32
void initialize_orphanage()
Definition: txorphan.cpp:28
FUZZ_TARGET(txorphan,.init=initialize_orphanage)
Definition: txorphan.cpp:33
assert(!tx.IsCoinBase())