Branch data Line data Source code
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>
13 : : #include <test/fuzz/FuzzedDataProvider.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 : :
28 : 1 : void initialize_orphanage()
29 : : {
30 [ + - + - ]: 2 : static const auto testing_setup = MakeNoLogFileContext();
31 [ + - ]: 2 : }
32 : :
33 [ + - ]: 949 : FUZZ_TARGET(txorphan, .init = initialize_orphanage)
34 : : {
35 : 535 : FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
36 : 535 : FastRandomContext limit_orphans_rng{/*fDeterministic=*/true};
37 [ + - ]: 535 : SetMockTime(ConsumeTime(fuzzed_data_provider));
38 : :
39 : 535 : TxOrphanage orphanage;
40 : 535 : std::vector<COutPoint> outpoints; // Duplicates are tolerated
41 [ + - ]: 535 : outpoints.reserve(200'000);
42 : :
43 : : // initial outpoints used to construct transactions later
44 [ + + ]: 2675 : for (uint8_t i = 0; i < 4; i++) {
45 [ + - ]: 2140 : outpoints.emplace_back(Txid::FromUint256(uint256{i}), 0);
46 : : }
47 : :
48 : 535 : CTransactionRef ptx_potential_parent = nullptr;
49 : :
50 [ + - + + : 8304 : LIMITED_WHILE(outpoints.size() < 200'000 && fuzzed_data_provider.ConsumeBool(), 10 * DEFAULT_MAX_ORPHAN_TRANSACTIONS)
- + ]
51 : : {
52 : : // construct transaction
53 : 7234 : const CTransactionRef tx = [&] {
54 : 3617 : CMutableTransaction tx_mut;
55 : 3617 : const auto num_in = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, outpoints.size());
56 : 3617 : const auto num_out = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, 256);
57 : : // pick outpoints from outpoints as input. We allow input duplicates on purpose, given we are not
58 : : // running any transaction validation logic before adding transactions to the orphanage
59 [ + - ]: 3617 : tx_mut.vin.reserve(num_in);
60 [ + + ]: 597860 : for (uint32_t i = 0; i < num_in; i++) {
61 : 594243 : auto& prevout = PickValue(fuzzed_data_provider, outpoints);
62 : : // try making transactions unique by setting a random nSequence, but allow duplicate transactions if they happen
63 [ + - ]: 1188486 : tx_mut.vin.emplace_back(prevout, CScript{}, fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(0, CTxIn::SEQUENCE_FINAL));
64 : : }
65 : : // output amount will not affect txorphanage
66 [ + - ]: 3617 : tx_mut.vout.reserve(num_out);
67 [ + + ]: 209561 : for (uint32_t i = 0; i < num_out; i++) {
68 [ + - ]: 411888 : tx_mut.vout.emplace_back(CAmount{0}, CScript{});
69 : : }
70 [ + - ]: 3617 : auto new_tx = MakeTransactionRef(tx_mut);
71 : : // add newly constructed outpoints to the coin pool
72 [ + + ]: 209561 : for (uint32_t i = 0; i < num_out; i++) {
73 [ + - ]: 205944 : outpoints.emplace_back(new_tx->GetHash(), i);
74 : : }
75 : 3617 : return new_tx;
76 [ + - ]: 7234 : }();
77 : :
78 : : // Trigger orphanage functions that are called using parents. ptx_potential_parent is a tx we constructed in a
79 : : // previous loop and potentially the parent of this tx.
80 [ + + ]: 3617 : if (ptx_potential_parent) {
81 : : // Set up future GetTxToReconsider call.
82 [ + - ]: 3089 : orphanage.AddChildrenToWorkSet(*ptx_potential_parent);
83 : :
84 : : // Check that all txns returned from GetChildrenFrom* are indeed a direct child of this tx.
85 : 3089 : NodeId peer_id = fuzzed_data_provider.ConsumeIntegral<NodeId>();
86 [ + - + + ]: 3926 : for (const auto& child : orphanage.GetChildrenFromSamePeer(ptx_potential_parent, peer_id)) {
87 [ - + ]: 2515 : assert(std::any_of(child->vin.cbegin(), child->vin.cend(), [&](const auto& input) {
88 : : return input.prevout.hash == ptx_potential_parent->GetHash();
89 : : }));
90 : 3089 : }
91 : : }
92 : :
93 : : // trigger orphanage functions
94 [ + + + + ]: 320110 : LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10 * DEFAULT_MAX_ORPHAN_TRANSACTIONS)
95 : : {
96 : 316493 : NodeId peer_id = fuzzed_data_provider.ConsumeIntegral<NodeId>();
97 : :
98 [ + - ]: 316493 : CallOneOf(
99 : : fuzzed_data_provider,
100 : 1315 : [&] {
101 : 1315 : {
102 : 1315 : CTransactionRef ref = orphanage.GetTxToReconsider(peer_id);
103 [ + + ]: 1315 : if (ref) {
104 [ + - + - ]: 94 : Assert(orphanage.HaveTx(ref->GetWitnessHash()));
105 : : }
106 : 1315 : }
107 : 1315 : },
108 : 112279 : [&] {
109 : 112279 : bool have_tx = orphanage.HaveTx(tx->GetWitnessHash());
110 : : // AddTx should return false if tx is too big or already have it
111 : : // tx weight is unknown, we only check when tx is already in orphanage
112 : 112279 : {
113 : 112279 : bool add_tx = orphanage.AddTx(tx, peer_id);
114 : : // have_tx == true -> add_tx == false
115 : 112279 : Assert(!have_tx || !add_tx);
116 : : }
117 : 112279 : have_tx = orphanage.HaveTx(tx->GetWitnessHash());
118 : 112279 : {
119 : 112279 : bool add_tx = orphanage.AddTx(tx, peer_id);
120 : : // if have_tx is still false, it must be too big
121 : 112279 : Assert(!have_tx == (GetTransactionWeight(*tx) > MAX_STANDARD_TX_WEIGHT));
122 : 112279 : Assert(!have_tx || !add_tx);
123 : : }
124 : 112279 : },
125 : 1088 : [&] {
126 : 1088 : bool have_tx = orphanage.HaveTx(tx->GetWitnessHash());
127 : 1088 : bool have_tx_and_peer = orphanage.HaveTxFromPeer(tx->GetWitnessHash(), peer_id);
128 : : // AddAnnouncer should return false if tx doesn't exist or we already HaveTxFromPeer.
129 : 1088 : {
130 : 1088 : bool added_announcer = orphanage.AddAnnouncer(tx->GetWitnessHash(), peer_id);
131 : : // have_tx == false -> added_announcer == false
132 : 1088 : Assert(have_tx || !added_announcer);
133 : : // have_tx_and_peer == true -> added_announcer == false
134 : 1088 : Assert(!have_tx_and_peer || !added_announcer);
135 : : }
136 : 1088 : },
137 : 20166 : [&] {
138 : 20166 : bool have_tx = orphanage.HaveTx(tx->GetWitnessHash());
139 : : // EraseTx should return 0 if m_orphans doesn't have the tx
140 : 20166 : {
141 : 20166 : Assert(have_tx == orphanage.EraseTx(tx->GetWitnessHash()));
142 : : }
143 : 20166 : have_tx = orphanage.HaveTx(tx->GetWitnessHash());
144 : : // have_tx should be false and EraseTx should fail
145 : 20166 : {
146 [ + - - + ]: 20166 : Assert(!have_tx && !orphanage.EraseTx(tx->GetWitnessHash()));
147 : : }
148 : 20166 : },
149 : 1313 : [&] {
150 : 1313 : orphanage.EraseForPeer(peer_id);
151 : 1313 : Assert(!orphanage.HaveTxFromPeer(tx->GetWitnessHash(), peer_id));
152 : 1313 : },
153 : 180332 : [&] {
154 : : // test mocktime and expiry
155 : 180332 : SetMockTime(ConsumeTime(fuzzed_data_provider));
156 : 180332 : auto limit = fuzzed_data_provider.ConsumeIntegral<unsigned int>();
157 : 180332 : orphanage.LimitOrphans(limit, limit_orphans_rng);
158 : 180332 : Assert(orphanage.Size() <= limit);
159 : 180332 : });
160 : :
161 : : }
162 : : // Set tx as potential parent to be used for future GetChildren() calls.
163 [ + + + + ]: 6706 : if (!ptx_potential_parent || fuzzed_data_provider.ConsumeBool()) {
164 : 1758 : ptx_potential_parent = tx;
165 : : }
166 : :
167 [ + - ]: 3617 : const bool have_tx{orphanage.HaveTx(tx->GetWitnessHash())};
168 [ + - + + ]: 3617 : const bool get_tx_nonnull{orphanage.GetTx(tx->GetWitnessHash()) != nullptr};
169 [ + - ]: 3617 : Assert(have_tx == get_tx_nonnull);
170 : 3617 : }
171 : 535 : }
|