/root/bitcoin/src/test/fuzz/coins_view.cpp
Line | Count | Source |
1 | | // Copyright (c) 2020-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 <coins.h> |
6 | | #include <consensus/amount.h> |
7 | | #include <consensus/tx_check.h> |
8 | | #include <consensus/tx_verify.h> |
9 | | #include <consensus/validation.h> |
10 | | #include <kernel/chainstatemanager_opts.h> |
11 | | #include <kernel/cs_main.h> |
12 | | #include <policy/policy.h> |
13 | | #include <primitives/block.h> |
14 | | #include <primitives/transaction.h> |
15 | | #include <script/interpreter.h> |
16 | | #include <test/fuzz/FuzzedDataProvider.h> |
17 | | #include <test/fuzz/fuzz.h> |
18 | | #include <test/fuzz/util.h> |
19 | | #include <test/util/coins.h> |
20 | | #include <test/util/setup_common.h> |
21 | | #include <txdb.h> |
22 | | #include <util/hasher.h> |
23 | | #include <util/threadpool.h> |
24 | | |
25 | | #include <cassert> |
26 | | #include <algorithm> |
27 | | #include <cstdint> |
28 | | #include <functional> |
29 | | #include <limits> |
30 | | #include <memory> |
31 | | #include <optional> |
32 | | #include <ranges> |
33 | | #include <stdexcept> |
34 | | #include <string> |
35 | | #include <utility> |
36 | | #include <vector> |
37 | | |
38 | | namespace { |
39 | | /** |
40 | | * MutationGuardCoinsViewCache asserts that nothing mutates cacheCoins until |
41 | | * BatchWrite is called. It keeps a snapshot of the cacheCoins state, which it |
42 | | * uses for the assertion in BatchWrite. After the call to the superclass |
43 | | * CCoinsViewCache::BatchWrite returns, it recomputes the snapshot at that |
44 | | * moment. |
45 | | */ |
46 | | class MutationGuardCoinsViewCache final : public CCoinsViewCache |
47 | | { |
48 | | private: |
49 | | struct CacheCoinSnapshot { |
50 | | COutPoint outpoint; |
51 | | bool dirty{false}; |
52 | | bool fresh{false}; |
53 | | Coin coin; |
54 | 0 | bool operator==(const CacheCoinSnapshot&) const = default; |
55 | | }; |
56 | | |
57 | | std::vector<CacheCoinSnapshot> ComputeCacheCoinsSnapshot() const |
58 | 0 | { |
59 | 0 | std::vector<CacheCoinSnapshot> snapshot; |
60 | 0 | snapshot.reserve(cacheCoins.size()); |
61 | |
|
62 | 0 | for (const auto& [outpoint, entry] : cacheCoins) { Branch (62:44): [True: 0, False: 0]
|
63 | 0 | snapshot.emplace_back(outpoint, entry.IsDirty(), entry.IsFresh(), entry.coin); |
64 | 0 | } |
65 | |
|
66 | 0 | std::ranges::sort(snapshot, std::less<>{}, &CacheCoinSnapshot::outpoint); |
67 | 0 | return snapshot; |
68 | 0 | } |
69 | | |
70 | | mutable std::vector<CacheCoinSnapshot> m_expected_snapshot{ComputeCacheCoinsSnapshot()}; |
71 | | |
72 | | public: |
73 | | void BatchWrite(CoinsViewCacheCursor& cursor, const uint256& block_hash) override |
74 | 0 | { |
75 | | // Nothing must modify cacheCoins other than BatchWrite. |
76 | 0 | assert(ComputeCacheCoinsSnapshot() == m_expected_snapshot); Branch (76:9): [True: 0, False: 0]
|
77 | 0 | CCoinsViewCache::BatchWrite(cursor, block_hash); |
78 | 0 | m_expected_snapshot = ComputeCacheCoinsSnapshot(); |
79 | 0 | } |
80 | | |
81 | | using CCoinsViewCache::CCoinsViewCache; |
82 | | }; |
83 | | |
84 | | // Reuse a single global thread pool across fuzz iterations. Creating and destroying a pool every |
85 | | // iteration leaks memory, since iterations can run faster than the OS can tear down the threads. |
86 | | std::shared_ptr<ThreadPool> g_thread_pool{std::make_shared<ThreadPool>("view_fuzz")}; |
87 | | |
88 | | void StartPoolIfNeeded() |
89 | 0 | { |
90 | 0 | if (!g_thread_pool->WorkersCount()) g_thread_pool->Start(DEFAULT_PREVOUTFETCH_THREADS); Branch (90:9): [True: 0, False: 0]
|
91 | 0 | } |
92 | | |
93 | | //! Build a random block and seed a view with utxos for its inputs. |
94 | | CBlock BuildRandomBlock(FuzzedDataProvider& fuzzed_data_provider, CCoinsView& view) |
95 | 0 | { |
96 | 0 | CBlock block; |
97 | 0 | CMutableTransaction coinbase; |
98 | 0 | coinbase.vin.emplace_back(); |
99 | 0 | block.vtx.push_back(MakeTransactionRef(coinbase)); |
100 | |
|
101 | 0 | CCoinsViewCache seed_cache{&view, /*deterministic=*/true}; |
102 | 0 | seed_cache.SetBestBlock(uint256::ONE); |
103 | |
|
104 | 0 | Txid prevhash{Txid::FromUint256(ConsumeUInt256(fuzzed_data_provider))}; |
105 | 0 | LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 100) |
106 | 0 | { |
107 | 0 | CMutableTransaction tx; |
108 | 0 | LIMITED_WHILE(fuzzed_data_provider.ConsumeBool(), 100) |
109 | 0 | { |
110 | 0 | const Txid txid{fuzzed_data_provider.ConsumeBool() Branch (110:29): [True: 0, False: 0]
|
111 | 0 | ? Txid::FromUint256(ConsumeUInt256(fuzzed_data_provider)) |
112 | 0 | : prevhash}; |
113 | 0 | const COutPoint outpoint{txid, fuzzed_data_provider.ConsumeIntegral<uint32_t>()}; |
114 | 0 | if (auto coin{ConsumeDeserializable<Coin>(fuzzed_data_provider)}; coin && !coin->IsSpent()) { Branch (114:79): [True: 0, False: 0]
Branch (114:87): [True: 0, False: 0]
|
115 | 0 | seed_cache.AddCoin(outpoint, std::move(*coin), /*possible_overwrite=*/true); |
116 | 0 | } |
117 | 0 | tx.vin.emplace_back(outpoint); |
118 | 0 | } |
119 | 0 | prevhash = tx.GetHash(); |
120 | 0 | block.vtx.push_back(MakeTransactionRef(tx)); |
121 | 0 | } |
122 | |
|
123 | 0 | seed_cache.Flush(); |
124 | 0 | return block; |
125 | 0 | } |
126 | | |
127 | | } // namespace |
128 | | |
129 | | void initialize_coins_view() |
130 | 0 | { |
131 | 0 | static const auto testing_setup = MakeNoLogFileContext<>(); |
132 | 0 | } |
133 | | |
134 | | void TestCoinsView(FuzzedDataProvider& fuzzed_data_provider, CCoinsViewCache& coins_view_cache, CCoinsView* backend_coins_view) |
135 | 0 | { |
136 | 0 | auto* const db{dynamic_cast<CCoinsViewDB*>(backend_coins_view)}; |
137 | 0 | auto* const overlay{dynamic_cast<CoinsViewOverlay*>(&coins_view_cache)}; |
138 | 0 | const bool is_db{db != nullptr}; |
139 | 0 | bool good_data{true}; |
140 | 0 | auto* original_backend{backend_coins_view}; |
141 | |
|
142 | 0 | if (is_db) coins_view_cache.SetBestBlock(uint256::ONE); Branch (142:9): [True: 0, False: 0]
|
143 | 0 | COutPoint random_out_point; |
144 | 0 | Coin random_coin; |
145 | 0 | CMutableTransaction random_mutable_transaction; |
146 | 0 | LIMITED_WHILE (good_data && fuzzed_data_provider.ConsumeBool(), 10'000) { |
147 | 0 | CallOneOf( |
148 | 0 | fuzzed_data_provider, |
149 | 0 | [&] { |
150 | 0 | if (random_coin.IsSpent()) { Branch (150:21): [True: 0, False: 0]
|
151 | 0 | return; |
152 | 0 | } |
153 | 0 | COutPoint outpoint{random_out_point}; |
154 | 0 | Coin coin{random_coin}; |
155 | 0 | if (fuzzed_data_provider.ConsumeBool()) { Branch (155:21): [True: 0, False: 0]
|
156 | | // We can only skip the check if no unspent coin exists for this outpoint. |
157 | 0 | const bool possible_overwrite{coins_view_cache.PeekCoin(outpoint) || fuzzed_data_provider.ConsumeBool()}; Branch (157:51): [True: 0, False: 0]
Branch (157:90): [True: 0, False: 0]
|
158 | 0 | coins_view_cache.AddCoin(outpoint, std::move(coin), possible_overwrite); |
159 | 0 | } else { |
160 | 0 | coins_view_cache.EmplaceCoinInternalDANGER(outpoint, std::move(coin)); |
161 | 0 | } |
162 | 0 | }, |
163 | 0 | [&] { |
164 | 0 | if (overlay && !overlay->AllInputsConsumed()) return; // CoinsViewOverlay::Flush() must have all inputs consumed before being called Branch (164:21): [True: 0, False: 0]
Branch (164:32): [True: 0, False: 0]
|
165 | 0 | coins_view_cache.Flush(/*reallocate_cache=*/fuzzed_data_provider.ConsumeBool()); |
166 | 0 | }, |
167 | 0 | [&] { |
168 | 0 | if (overlay) return; // CoinsViewOverlay::Sync() is never called in production code Branch (168:21): [True: 0, False: 0]
|
169 | 0 | coins_view_cache.Sync(); |
170 | 0 | }, |
171 | 0 | [&] { |
172 | 0 | if (db) WITH_LOCK(::cs_main, (void)db->CompactFullAsync()); Branch (172:21): [True: 0, False: 0]
|
173 | 0 | }, |
174 | 0 | [&] { |
175 | 0 | uint256 best_block{ConsumeUInt256(fuzzed_data_provider)}; |
176 | | // `CCoinsViewDB::BatchWrite()` requires a non-null best block. |
177 | 0 | if (is_db && best_block.IsNull()) best_block = uint256::ONE; Branch (177:21): [True: 0, False: 0]
Branch (177:30): [True: 0, False: 0]
|
178 | 0 | coins_view_cache.SetBestBlock(best_block); |
179 | 0 | }, |
180 | 0 | [&] { |
181 | 0 | (void)coins_view_cache.CreateResetGuard(); |
182 | | // Reset() clears the best block, so reseed db-backed caches. |
183 | 0 | if (is_db) { Branch (183:21): [True: 0, False: 0]
|
184 | 0 | const uint256 best_block{ConsumeUInt256(fuzzed_data_provider)}; |
185 | 0 | if (best_block.IsNull()) { Branch (185:25): [True: 0, False: 0]
|
186 | 0 | good_data = false; |
187 | 0 | return; |
188 | 0 | } |
189 | 0 | coins_view_cache.SetBestBlock(best_block); |
190 | 0 | } |
191 | 0 | }, |
192 | 0 | [&] { |
193 | 0 | Coin move_to; |
194 | 0 | (void)coins_view_cache.SpendCoin(random_out_point, fuzzed_data_provider.ConsumeBool() ? &move_to : nullptr); Branch (194:68): [True: 0, False: 0]
|
195 | 0 | }, |
196 | 0 | [&] { |
197 | 0 | coins_view_cache.Uncache(random_out_point); |
198 | 0 | }, |
199 | 0 | [&] { |
200 | 0 | if (overlay) return; // CoinsViewOverlay::SetBackend() is never called in production code Branch (200:21): [True: 0, False: 0]
|
201 | 0 | const bool use_original_backend{fuzzed_data_provider.ConsumeBool()}; |
202 | 0 | if (use_original_backend && backend_coins_view != original_backend) { Branch (202:21): [True: 0, False: 0]
Branch (202:45): [True: 0, False: 0]
|
203 | | // FRESH flags valid against the empty backend may be invalid |
204 | | // against the original backend, so reset before restoring it. |
205 | 0 | (void)coins_view_cache.CreateResetGuard(); |
206 | | // Reset() clears the best block; db backends require a non-null hash. |
207 | 0 | if (is_db) coins_view_cache.SetBestBlock(uint256::ONE); Branch (207:25): [True: 0, False: 0]
|
208 | 0 | } |
209 | 0 | backend_coins_view = use_original_backend ? original_backend : &CoinsViewEmpty::Get(); Branch (209:38): [True: 0, False: 0]
|
210 | 0 | coins_view_cache.SetBackend(*backend_coins_view); |
211 | 0 | }, |
212 | 0 | [&] { |
213 | 0 | const std::optional<COutPoint> opt_out_point = ConsumeDeserializable<COutPoint>(fuzzed_data_provider); |
214 | 0 | if (!opt_out_point) { Branch (214:21): [True: 0, False: 0]
|
215 | 0 | good_data = false; |
216 | 0 | return; |
217 | 0 | } |
218 | 0 | random_out_point = *opt_out_point; |
219 | 0 | }, |
220 | 0 | [&] { |
221 | 0 | const std::optional<Coin> opt_coin = ConsumeDeserializable<Coin>(fuzzed_data_provider); |
222 | 0 | if (!opt_coin) { Branch (222:21): [True: 0, False: 0]
|
223 | 0 | good_data = false; |
224 | 0 | return; |
225 | 0 | } |
226 | 0 | random_coin = *opt_coin; |
227 | 0 | }, |
228 | 0 | [&] { |
229 | 0 | const std::optional<CMutableTransaction> opt_mutable_transaction = ConsumeDeserializable<CMutableTransaction>(fuzzed_data_provider, TX_WITH_WITNESS); |
230 | 0 | if (!opt_mutable_transaction) { Branch (230:21): [True: 0, False: 0]
|
231 | 0 | good_data = false; |
232 | 0 | return; |
233 | 0 | } |
234 | 0 | random_mutable_transaction = *opt_mutable_transaction; |
235 | 0 | }, |
236 | 0 | [&] { |
237 | 0 | CoinsCachePair sentinel{}; |
238 | 0 | sentinel.second.SelfRef(sentinel); |
239 | 0 | size_t dirty_count{0}; |
240 | 0 | CCoinsMapMemoryResource resource; |
241 | 0 | CCoinsMap coins_map{0, SaltedCoinsCacheHasher{/*deterministic=*/true}, CCoinsMap::key_equal{}, &resource}; |
242 | 0 | LIMITED_WHILE (good_data && fuzzed_data_provider.ConsumeBool(), 10'000) { |
243 | 0 | CCoinsCacheEntry coins_cache_entry; |
244 | 0 | if (fuzzed_data_provider.ConsumeBool()) { Branch (244:25): [True: 0, False: 0]
|
245 | 0 | coins_cache_entry.coin = random_coin; |
246 | 0 | } else { |
247 | 0 | const std::optional<Coin> opt_coin = ConsumeDeserializable<Coin>(fuzzed_data_provider); |
248 | 0 | if (!opt_coin) { Branch (248:29): [True: 0, False: 0]
|
249 | 0 | good_data = false; |
250 | 0 | return; |
251 | 0 | } |
252 | 0 | coins_cache_entry.coin = *opt_coin; |
253 | 0 | } |
254 | | // Avoid setting FRESH for an outpoint that already exists unspent in the parent view. |
255 | 0 | bool fresh{!coins_view_cache.PeekCoin(random_out_point) && fuzzed_data_provider.ConsumeBool()}; Branch (255:32): [True: 0, False: 0]
Branch (255:80): [True: 0, False: 0]
|
256 | 0 | bool dirty{fresh || fuzzed_data_provider.ConsumeBool()}; Branch (256:32): [True: 0, False: 0]
Branch (256:41): [True: 0, False: 0]
|
257 | 0 | auto it{coins_map.emplace(random_out_point, std::move(coins_cache_entry)).first}; |
258 | 0 | if (dirty) CCoinsCacheEntry::SetDirty(*it, sentinel); Branch (258:25): [True: 0, False: 0]
|
259 | 0 | if (fresh) CCoinsCacheEntry::SetFresh(*it, sentinel); Branch (259:25): [True: 0, False: 0]
|
260 | 0 | dirty_count += dirty; |
261 | 0 | } |
262 | 0 | auto cursor{CoinsViewCacheCursor(dirty_count, sentinel, coins_map, /*will_erase=*/true)}; |
263 | 0 | uint256 best_block{coins_view_cache.GetBestBlock()}; |
264 | 0 | if (fuzzed_data_provider.ConsumeBool()) best_block = ConsumeUInt256(fuzzed_data_provider); Branch (264:21): [True: 0, False: 0]
|
265 | | // Set best block hash to non-null to satisfy the assertion in CCoinsViewDB::BatchWrite(). |
266 | 0 | if (is_db && best_block.IsNull()) best_block = uint256::ONE; Branch (266:21): [True: 0, False: 0]
Branch (266:30): [True: 0, False: 0]
|
267 | 0 | coins_view_cache.BatchWrite(cursor, best_block); |
268 | 0 | }); |
269 | 0 | } |
270 | |
|
271 | 0 | { |
272 | 0 | (void)coins_view_cache.DynamicMemoryUsage(); |
273 | 0 | (void)coins_view_cache.EstimateSize(); |
274 | 0 | (void)coins_view_cache.GetBestBlock(); |
275 | 0 | (void)coins_view_cache.GetCacheSize(); |
276 | 0 | (void)coins_view_cache.GetHeadBlocks(); |
277 | 0 | (void)coins_view_cache.HaveInputs(CTransaction{random_mutable_transaction}); |
278 | 0 | } |
279 | |
|
280 | 0 | { |
281 | 0 | if (is_db && backend_coins_view == original_backend) { Branch (281:13): [True: 0, False: 0]
Branch (281:22): [True: 0, False: 0]
|
282 | 0 | assert(db->Cursor()); Branch (282:13): [True: 0, False: 0]
|
283 | 0 | } |
284 | 0 | (void)backend_coins_view->EstimateSize(); |
285 | 0 | (void)backend_coins_view->GetBestBlock(); |
286 | 0 | (void)backend_coins_view->GetHeadBlocks(); |
287 | 0 | } |
288 | | |
289 | 0 | if (fuzzed_data_provider.ConsumeBool()) { Branch (289:9): [True: 0, False: 0]
|
290 | 0 | CallOneOf( |
291 | 0 | fuzzed_data_provider, |
292 | 0 | [&] { |
293 | 0 | const CTransaction transaction{random_mutable_transaction}; |
294 | 0 | bool is_spent = false; |
295 | 0 | for (const CTxOut& tx_out : transaction.vout) { Branch (295:43): [True: 0, False: 0]
|
296 | 0 | if (Coin{tx_out, 0, transaction.IsCoinBase()}.IsSpent()) { Branch (296:25): [True: 0, False: 0]
|
297 | 0 | is_spent = true; |
298 | 0 | } |
299 | 0 | } |
300 | 0 | if (is_spent) { Branch (300:21): [True: 0, False: 0]
|
301 | | // Avoid: |
302 | | // coins.cpp:69: void CCoinsViewCache::AddCoin(const COutPoint &, Coin &&, bool): Assertion `!coin.IsSpent()' failed. |
303 | 0 | return; |
304 | 0 | } |
305 | 0 | const int height{int(fuzzed_data_provider.ConsumeIntegral<uint32_t>() >> 1)}; |
306 | 0 | const bool check_for_overwrite{transaction.IsCoinBase() || [&] { Branch (306:48): [True: 0, False: 0]
Branch (306:76): [True: 0, False: 0]
|
307 | 0 | for (uint32_t i{0}; i < transaction.vout.size(); ++i) { Branch (307:41): [True: 0, False: 0]
|
308 | 0 | if (coins_view_cache.PeekCoin(COutPoint{transaction.GetHash(), i})) return true; Branch (308:29): [True: 0, False: 0]
|
309 | 0 | } |
310 | 0 | return fuzzed_data_provider.ConsumeBool(); |
311 | 0 | }()}; // We can only skip the check if the current txid has no unspent outputs |
312 | 0 | AddCoins(coins_view_cache, transaction, height, check_for_overwrite); |
313 | 0 | }, |
314 | 0 | [&] { |
315 | 0 | (void)ValidateInputsStandardness(CTransaction{random_mutable_transaction}, coins_view_cache); |
316 | 0 | }, |
317 | 0 | [&] { |
318 | 0 | TxValidationState state; |
319 | 0 | CAmount tx_fee_out; |
320 | 0 | const CTransaction transaction{random_mutable_transaction}; |
321 | 0 | if (ContainsSpentInput(transaction, coins_view_cache)) { Branch (321:21): [True: 0, False: 0]
|
322 | | // Avoid: |
323 | | // consensus/tx_verify.cpp:171: bool Consensus::CheckTxInputs(const CTransaction &, TxValidationState &, const CCoinsViewCache &, int, CAmount &): Assertion `!coin.IsSpent()' failed. |
324 | 0 | return; |
325 | 0 | } |
326 | 0 | TxValidationState dummy; |
327 | 0 | if (!CheckTransaction(transaction, dummy)) { Branch (327:21): [True: 0, False: 0]
|
328 | | // It is not allowed to call CheckTxInputs if CheckTransaction failed |
329 | 0 | return; |
330 | 0 | } |
331 | 0 | if (Consensus::CheckTxInputs(transaction, state, coins_view_cache, fuzzed_data_provider.ConsumeIntegralInRange<int>(0, std::numeric_limits<int>::max()), tx_fee_out)) { Branch (331:21): [True: 0, False: 0]
|
332 | 0 | assert(MoneyRange(tx_fee_out)); Branch (332:21): [True: 0, False: 0]
|
333 | 0 | } |
334 | 0 | }, |
335 | 0 | [&] { |
336 | 0 | const CTransaction transaction{random_mutable_transaction}; |
337 | 0 | if (ContainsSpentInput(transaction, coins_view_cache)) { Branch (337:21): [True: 0, False: 0]
|
338 | | // Avoid: |
339 | | // consensus/tx_verify.cpp:130: unsigned int GetP2SHSigOpCount(const CTransaction &, const CCoinsViewCache &): Assertion `!coin.IsSpent()' failed. |
340 | 0 | return; |
341 | 0 | } |
342 | 0 | (void)GetP2SHSigOpCount(transaction, coins_view_cache); |
343 | 0 | }, |
344 | 0 | [&] { |
345 | 0 | const CTransaction transaction{random_mutable_transaction}; |
346 | 0 | if (ContainsSpentInput(transaction, coins_view_cache)) { Branch (346:21): [True: 0, False: 0]
|
347 | | // Avoid: |
348 | | // consensus/tx_verify.cpp:130: unsigned int GetP2SHSigOpCount(const CTransaction &, const CCoinsViewCache &): Assertion `!coin.IsSpent()' failed. |
349 | 0 | return; |
350 | 0 | } |
351 | 0 | const auto flags = script_verify_flags::from_int(fuzzed_data_provider.ConsumeIntegral<script_verify_flags::value_type>()); |
352 | 0 | if (!transaction.vin.empty() && (flags & SCRIPT_VERIFY_WITNESS) != 0 && (flags & SCRIPT_VERIFY_P2SH) == 0) { Branch (352:21): [True: 0, False: 0]
Branch (352:21): [True: 0, False: 0]
Branch (352:49): [True: 0, False: 0]
Branch (352:89): [True: 0, False: 0]
|
353 | | // Avoid: |
354 | | // script/interpreter.cpp:1705: size_t CountWitnessSigOps(const CScript &, const CScript &, const CScriptWitness &, unsigned int): Assertion `(flags & SCRIPT_VERIFY_P2SH) != 0' failed. |
355 | 0 | return; |
356 | 0 | } |
357 | 0 | (void)GetTransactionSigOpCost(transaction, coins_view_cache, flags); |
358 | 0 | }, |
359 | 0 | [&] { |
360 | 0 | (void)IsWitnessStandard(CTransaction{random_mutable_transaction}, coins_view_cache); |
361 | 0 | }); |
362 | 0 | } |
363 | |
|
364 | 0 | { |
365 | 0 | const Coin& coin_using_access_coin = coins_view_cache.AccessCoin(random_out_point); |
366 | 0 | const bool exists_using_access_coin = !coin_using_access_coin.IsSpent(); |
367 | 0 | const bool exists_using_have_coin = coins_view_cache.HaveCoin(random_out_point); |
368 | 0 | const bool exists_using_have_coin_in_cache = coins_view_cache.HaveCoinInCache(random_out_point); |
369 | 0 | if (auto coin{coins_view_cache.GetCoin(random_out_point)}) { Branch (369:18): [True: 0, False: 0]
|
370 | 0 | assert(*coin == coin_using_access_coin); Branch (370:13): [True: 0, False: 0]
|
371 | 0 | assert(exists_using_access_coin && exists_using_have_coin_in_cache && exists_using_have_coin); Branch (371:13): [True: 0, False: 0]
Branch (371:13): [True: 0, False: 0]
Branch (371:13): [True: 0, False: 0]
Branch (371:13): [True: 0, False: 0]
|
372 | 0 | } else { |
373 | 0 | assert(!exists_using_access_coin && !exists_using_have_coin_in_cache && !exists_using_have_coin); Branch (373:13): [True: 0, False: 0]
Branch (373:13): [True: 0, False: 0]
Branch (373:13): [True: 0, False: 0]
Branch (373:13): [True: 0, False: 0]
|
374 | 0 | } |
375 | | // If HaveCoin on the backend is true, it must also be on the cache if the coin wasn't spent. |
376 | 0 | std::optional<Coin> coin_in_backend; |
377 | 0 | bool exists_using_have_coin_in_backend; |
378 | 0 | if (overlay) { Branch (378:13): [True: 0, False: 0]
|
379 | | // PeekCoin does not mutate cacheCoins, so async workers can keep running. |
380 | 0 | coin_in_backend = backend_coins_view->PeekCoin(random_out_point); |
381 | 0 | exists_using_have_coin_in_backend = coin_in_backend.has_value(); |
382 | 0 | } else { |
383 | 0 | exists_using_have_coin_in_backend = backend_coins_view->HaveCoin(random_out_point); |
384 | 0 | coin_in_backend = backend_coins_view->GetCoin(random_out_point); |
385 | 0 | } |
386 | 0 | if (!coin_using_access_coin.IsSpent() && exists_using_have_coin_in_backend) { Branch (386:13): [True: 0, False: 0]
Branch (386:50): [True: 0, False: 0]
|
387 | 0 | assert(exists_using_have_coin); Branch (387:13): [True: 0, False: 0]
|
388 | 0 | } |
389 | 0 | if (coin_in_backend) { Branch (389:13): [True: 0, False: 0]
|
390 | 0 | assert(exists_using_have_coin_in_backend); Branch (390:13): [True: 0, False: 0]
|
391 | | // Note we can't assert that `coin_using_get_coin == *coin` because the coin in |
392 | | // the cache may have been modified but not yet flushed. |
393 | 0 | } else { |
394 | 0 | assert(!exists_using_have_coin_in_backend); Branch (394:13): [True: 0, False: 0]
|
395 | 0 | } |
396 | 0 | } |
397 | 0 | } |
398 | | |
399 | | FUZZ_TARGET(coins_view, .init = initialize_coins_view) |
400 | 0 | { |
401 | 0 | FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; |
402 | 0 | CCoinsViewCache coins_view_cache{&CoinsViewEmpty::Get(), /*deterministic=*/true}; |
403 | 0 | TestCoinsView(fuzzed_data_provider, coins_view_cache, &CoinsViewEmpty::Get()); |
404 | 0 | } |
405 | | |
406 | | FUZZ_TARGET(coins_view_db, .init = initialize_coins_view) |
407 | 0 | { |
408 | 0 | FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; |
409 | 0 | auto db_params = DBParams{ |
410 | 0 | .path = "", |
411 | 0 | .cache_bytes = 1_MiB, |
412 | 0 | .memory_only = true, |
413 | 0 | }; |
414 | 0 | CCoinsViewDB backend_coins_view{std::move(db_params), CoinsViewOptions{}}; |
415 | 0 | CCoinsViewCache coins_view_cache{&backend_coins_view, /*deterministic=*/true}; |
416 | 0 | TestCoinsView(fuzzed_data_provider, coins_view_cache, &backend_coins_view); |
417 | 0 | } |
418 | | |
419 | | // Creates a CoinsViewOverlay and a MutationGuardCoinsViewCache as the base. |
420 | | // This allows us to exercise all methods on a CoinsViewOverlay, while also |
421 | | // ensuring that nothing can mutate the underlying cache until Flush or Sync is |
422 | | // called. |
423 | | FUZZ_TARGET(coins_view_overlay, .init = initialize_coins_view) |
424 | 0 | { |
425 | 0 | SeedRandomStateForTest(SeedRand::ZEROS); // for SaltedCoinsCacheHasher |
426 | 0 | StartPoolIfNeeded(); |
427 | 0 | FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; |
428 | 0 | MutationGuardCoinsViewCache backend_cache{&CoinsViewEmpty::Get(), /*deterministic=*/true}; |
429 | 0 | CoinsViewOverlay coins_view_cache{&backend_cache, g_thread_pool, /*deterministic=*/true}; |
430 | 0 | CBlock block{BuildRandomBlock(fuzzed_data_provider, backend_cache)}; |
431 | 0 | const auto reset_guard{coins_view_cache.StartFetching(block)}; |
432 | 0 | TestCoinsView(fuzzed_data_provider, coins_view_cache, &backend_cache); |
433 | 0 | } |
434 | | |
435 | | FUZZ_TARGET(coins_view_stacked, .init = initialize_coins_view) |
436 | 0 | { |
437 | 0 | SeedRandomStateForTest(SeedRand::ZEROS); // for SaltedCoinsCacheHasher |
438 | 0 | StartPoolIfNeeded(); |
439 | 0 | FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; |
440 | 0 | auto db_params = DBParams{ |
441 | 0 | .path = "", |
442 | 0 | .cache_bytes = 1_MiB, |
443 | 0 | .memory_only = true, |
444 | 0 | }; |
445 | 0 | CCoinsViewDB backend_base_coins_view{std::move(db_params), CoinsViewOptions{}}; |
446 | 0 | CCoinsViewCache backend_cache{&backend_base_coins_view, /*deterministic=*/true}; |
447 | 0 | TestCoinsView(fuzzed_data_provider, backend_cache, &backend_base_coins_view); |
448 | 0 | CoinsViewOverlay coins_view_cache{&backend_cache, g_thread_pool, /*deterministic=*/true}; |
449 | 0 | CBlock block{BuildRandomBlock(fuzzed_data_provider, backend_base_coins_view)}; |
450 | 0 | { |
451 | 0 | const auto reset_guard{coins_view_cache.StartFetching(block)}; |
452 | 0 | TestCoinsView(fuzzed_data_provider, coins_view_cache, &backend_cache); |
453 | 0 | } |
454 | 0 | TestCoinsView(fuzzed_data_provider, backend_cache, &backend_base_coins_view); |
455 | 0 | } |