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 [ + - ]: 1022 : FUZZ_TARGET(txorphan, .init = initialize_orphanage)
34 : : {
35 : 576 : FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
36 : 576 : FastRandomContext orphanage_rng{/*fDeterministic=*/true};
37 [ + - ]: 576 : SetMockTime(ConsumeTime(fuzzed_data_provider));
38 : :
39 : 576 : TxOrphanage orphanage;
40 : 576 : std::vector<COutPoint> outpoints; // Duplicates are tolerated
41 [ + - ]: 576 : outpoints.reserve(200'000);
42 : :
43 : : // initial outpoints used to construct transactions later
44 [ + + ]: 2880 : for (uint8_t i = 0; i < 4; i++) {
45 [ + - ]: 2304 : outpoints.emplace_back(Txid::FromUint256(uint256{i}), 0);
46 : : }
47 : :
48 : 576 : CTransactionRef ptx_potential_parent = nullptr;
49 : :
50 : 576 : std::vector<CTransactionRef> tx_history;
51 : :
52 [ + - + + : 75960 : LIMITED_WHILE(outpoints.size() < 200'000 && fuzzed_data_provider.ConsumeBool(), 10 * DEFAULT_MAX_ORPHAN_TRANSACTIONS)
+ + ]
53 : : {
54 : : // construct transaction
55 : 74808 : const CTransactionRef tx = [&] {
56 : 37404 : CMutableTransaction tx_mut;
57 : 37404 : const auto num_in = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, outpoints.size());
58 : 37404 : const auto num_out = fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(1, 256);
59 : : // pick outpoints from outpoints as input. We allow input duplicates on purpose, given we are not
60 : : // running any transaction validation logic before adding transactions to the orphanage
61 [ + - ]: 37404 : tx_mut.vin.reserve(num_in);
62 [ + + ]: 5303246 : for (uint32_t i = 0; i < num_in; i++) {
63 : 5265842 : auto& prevout = PickValue(fuzzed_data_provider, outpoints);
64 : : // try making transactions unique by setting a random nSequence, but allow duplicate transactions if they happen
65 [ + - ]: 10531684 : tx_mut.vin.emplace_back(prevout, CScript{}, fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(0, CTxIn::SEQUENCE_FINAL));
66 : : }
67 : : // output amount will not affect txorphanage
68 [ + - ]: 37404 : tx_mut.vout.reserve(num_out);
69 [ + + ]: 4125992 : for (uint32_t i = 0; i < num_out; i++) {
70 [ + - ]: 8177176 : tx_mut.vout.emplace_back(CAmount{0}, CScript{});
71 : : }
72 [ + - ]: 37404 : auto new_tx = MakeTransactionRef(tx_mut);
73 : : // add newly constructed outpoints to the coin pool
74 [ + + ]: 4125992 : for (uint32_t i = 0; i < num_out; i++) {
75 [ + - ]: 4088588 : outpoints.emplace_back(new_tx->GetHash(), i);
76 : : }
77 : 37404 : return new_tx;
78 [ + - ]: 74808 : }();
79 : :
80 [ + - ]: 37404 : tx_history.push_back(tx);
81 : :
82 [ + + ]: 37404 : const auto wtxid{tx->GetWitnessHash()};
83 : :
84 : : // Trigger orphanage functions that are called using parents. ptx_potential_parent is a tx we constructed in a
85 : : // previous loop and potentially the parent of this tx.
86 [ + + ]: 37404 : if (ptx_potential_parent) {
87 : : // Set up future GetTxToReconsider call.
88 [ + - ]: 36833 : orphanage.AddChildrenToWorkSet(*ptx_potential_parent, orphanage_rng);
89 : :
90 : : // Check that all txns returned from GetChildrenFrom* are indeed a direct child of this tx.
91 : 36833 : NodeId peer_id = fuzzed_data_provider.ConsumeIntegral<NodeId>();
92 [ + - + + ]: 295315 : for (const auto& child : orphanage.GetChildrenFromSamePeer(ptx_potential_parent, peer_id)) {
93 [ - + ]: 1001311 : assert(std::any_of(child->vin.cbegin(), child->vin.cend(), [&](const auto& input) {
94 : : return input.prevout.hash == ptx_potential_parent->GetHash();
95 : : }));
96 : 36833 : }
97 : : }
98 : :
99 : : // trigger orphanage functions
100 [ + + + + ]: 2145549 : LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 10 * DEFAULT_MAX_ORPHAN_TRANSACTIONS)
101 : : {
102 : 2108145 : NodeId peer_id = fuzzed_data_provider.ConsumeIntegral<NodeId>();
103 : 2108145 : const auto total_bytes_start{orphanage.TotalOrphanUsage()};
104 : 2108145 : const auto total_peer_bytes_start{orphanage.UsageByPeer(peer_id)};
105 : 2108145 : const auto tx_weight{GetTransactionWeight(*tx)};
106 : :
107 [ + - ]: 2108145 : CallOneOf(
108 : : fuzzed_data_provider,
109 : 9281 : [&] {
110 : 9281 : {
111 : 9281 : CTransactionRef ref = orphanage.GetTxToReconsider(peer_id);
112 [ + + ]: 9281 : if (ref) {
113 [ + - + - ]: 2568 : Assert(orphanage.HaveTx(ref->GetWitnessHash()));
114 : : }
115 : 9281 : }
116 : 9281 : },
117 : 1929825 : [&] {
118 : 1929825 : bool have_tx = orphanage.HaveTx(tx->GetWitnessHash());
119 : : // AddTx should return false if tx is too big or already have it
120 : : // tx weight is unknown, we only check when tx is already in orphanage
121 : 1929825 : {
122 : 1929825 : bool add_tx = orphanage.AddTx(tx, peer_id);
123 : : // have_tx == true -> add_tx == false
124 : 1929825 : Assert(!have_tx || !add_tx);
125 : :
126 [ + + ]: 1929825 : if (add_tx) {
127 : 60981 : Assert(orphanage.UsageByPeer(peer_id) == tx_weight + total_peer_bytes_start);
128 : 60981 : Assert(orphanage.TotalOrphanUsage() == tx_weight + total_bytes_start);
129 : 60981 : Assert(tx_weight <= MAX_STANDARD_TX_WEIGHT);
130 : : } else {
131 : : // Peer may have been added as an announcer.
132 [ + + ]: 1868844 : if (orphanage.UsageByPeer(peer_id) == tx_weight + total_peer_bytes_start) {
133 : 25661 : Assert(orphanage.HaveTxFromPeer(wtxid, peer_id));
134 : : } else {
135 : : // Otherwise, there must not be any change to the peer byte count.
136 : 1843183 : Assert(orphanage.UsageByPeer(peer_id) == total_peer_bytes_start);
137 : : }
138 : :
139 : : // Regardless, total bytes should not have changed.
140 : 1868844 : Assert(orphanage.TotalOrphanUsage() == total_bytes_start);
141 : : }
142 : : }
143 : 1929825 : have_tx = orphanage.HaveTx(tx->GetWitnessHash());
144 : 1929825 : {
145 : 1929825 : bool add_tx = orphanage.AddTx(tx, peer_id);
146 : : // if have_tx is still false, it must be too big
147 : 1929825 : Assert(!have_tx == (tx_weight > MAX_STANDARD_TX_WEIGHT));
148 : 1929825 : Assert(!have_tx || !add_tx);
149 : : }
150 : 1929825 : },
151 : 41407 : [&] {
152 : 41407 : bool have_tx = orphanage.HaveTx(tx->GetWitnessHash());
153 : 41407 : bool have_tx_and_peer = orphanage.HaveTxFromPeer(tx->GetWitnessHash(), peer_id);
154 : : // AddAnnouncer should return false if tx doesn't exist or we already HaveTxFromPeer.
155 : 41407 : {
156 : 41407 : bool added_announcer = orphanage.AddAnnouncer(tx->GetWitnessHash(), peer_id);
157 : : // have_tx == false -> added_announcer == false
158 : 41407 : Assert(have_tx || !added_announcer);
159 : : // have_tx_and_peer == true -> added_announcer == false
160 : 41407 : Assert(!have_tx_and_peer || !added_announcer);
161 : :
162 : : // Total bytes should not have changed. If peer was added as announcer, byte
163 : : // accounting must have been updated.
164 : 41407 : Assert(orphanage.TotalOrphanUsage() == total_bytes_start);
165 [ + + ]: 41407 : if (added_announcer) {
166 : 3200 : Assert(orphanage.UsageByPeer(peer_id) == tx_weight + total_peer_bytes_start);
167 : : } else {
168 : 38207 : Assert(orphanage.UsageByPeer(peer_id) == total_peer_bytes_start);
169 : : }
170 : : }
171 : 41407 : },
172 : 81350 : [&] {
173 : 81350 : bool have_tx = orphanage.HaveTx(tx->GetWitnessHash());
174 : 81350 : bool have_tx_and_peer{orphanage.HaveTxFromPeer(wtxid, peer_id)};
175 : : // EraseTx should return 0 if m_orphans doesn't have the tx
176 : 81350 : {
177 : 81350 : auto bytes_from_peer_before{orphanage.UsageByPeer(peer_id)};
178 : 81350 : Assert(have_tx == orphanage.EraseTx(tx->GetWitnessHash()));
179 [ + + ]: 81350 : if (have_tx) {
180 : 40224 : Assert(orphanage.TotalOrphanUsage() == total_bytes_start - tx_weight);
181 [ + + ]: 40224 : if (have_tx_and_peer) {
182 : 305 : Assert(orphanage.UsageByPeer(peer_id) == bytes_from_peer_before - tx_weight);
183 : : } else {
184 : 39919 : Assert(orphanage.UsageByPeer(peer_id) == bytes_from_peer_before);
185 : : }
186 : : } else {
187 : 41126 : Assert(orphanage.TotalOrphanUsage() == total_bytes_start);
188 : : }
189 : : }
190 : 81350 : have_tx = orphanage.HaveTx(tx->GetWitnessHash());
191 : 81350 : have_tx_and_peer = orphanage.HaveTxFromPeer(wtxid, peer_id);
192 : : // have_tx should be false and EraseTx should fail
193 : 81350 : {
194 [ + - - + ]: 81350 : Assert(!have_tx && !have_tx_and_peer && !orphanage.EraseTx(wtxid));
195 : : }
196 : 81350 : },
197 : 9233 : [&] {
198 : 9233 : orphanage.EraseForPeer(peer_id);
199 : 9233 : Assert(!orphanage.HaveTxFromPeer(tx->GetWitnessHash(), peer_id));
200 : 9233 : Assert(orphanage.UsageByPeer(peer_id) == 0);
201 : 9233 : },
202 : 31209 : [&] {
203 : : // Make a block out of txs and then EraseForBlock
204 : 31209 : CBlock block;
205 : 31209 : int num_txs = fuzzed_data_provider.ConsumeIntegralInRange<unsigned int>(0, 1000);
206 [ + + ]: 2750188 : for (int i{0}; i < num_txs; ++i) {
207 : 2718979 : auto& tx_to_remove = PickValue(fuzzed_data_provider, tx_history);
208 [ + - ]: 2718979 : block.vtx.push_back(tx_to_remove);
209 : : }
210 [ + - ]: 31209 : orphanage.EraseForBlock(block);
211 [ + + ]: 2750188 : for (const auto& tx_removed : block.vtx) {
212 [ + - + - ]: 2718979 : Assert(!orphanage.HaveTx(tx_removed->GetWitnessHash()));
213 [ + - + - ]: 2718979 : Assert(!orphanage.HaveTxFromPeer(tx_removed->GetWitnessHash(), peer_id));
214 : : }
215 : 31209 : },
216 : 5840 : [&] {
217 : : // test mocktime and expiry
218 : 5840 : SetMockTime(ConsumeTime(fuzzed_data_provider));
219 : 5840 : auto limit = fuzzed_data_provider.ConsumeIntegral<unsigned int>();
220 : 5840 : orphanage.LimitOrphans(limit, orphanage_rng);
221 : 5840 : Assert(orphanage.Size() <= limit);
222 : 5840 : });
223 : :
224 : : }
225 : :
226 : : // Set tx as potential parent to be used for future GetChildren() calls.
227 [ + + + + ]: 74237 : if (!ptx_potential_parent || fuzzed_data_provider.ConsumeBool()) {
228 : 18635 : ptx_potential_parent = tx;
229 : : }
230 : :
231 [ + - ]: 37404 : const bool have_tx{orphanage.HaveTx(tx->GetWitnessHash())};
232 [ + - + + ]: 37404 : const bool get_tx_nonnull{orphanage.GetTx(tx->GetWitnessHash()) != nullptr};
233 [ + - ]: 37404 : Assert(have_tx == get_tx_nonnull);
234 : 37404 : }
235 [ + - ]: 576 : orphanage.SanityCheck();
236 [ + + ]: 1147 : }
|