Branch data Line data Source code
1 : : // Copyright (c) 2023-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 <crypto/sha256.h>
7 : : #include <primitives/transaction.h>
8 : : #include <test/fuzz/FuzzedDataProvider.h>
9 : : #include <test/fuzz/fuzz.h>
10 : : #include <test/fuzz/util.h>
11 : :
12 : : #include <cassert>
13 : : #include <cstdint>
14 : : #include <memory>
15 : : #include <optional>
16 : : #include <vector>
17 : :
18 : : namespace {
19 : :
20 : : /** Number of distinct COutPoint values used in this test. */
21 : : constexpr uint32_t NUM_OUTPOINTS = 256;
22 : : /** Number of distinct Coin values used in this test (ignoring nHeight). */
23 : : constexpr uint32_t NUM_COINS = 256;
24 : : /** Maximum number CCoinsViewCache objects used in this test. */
25 : : constexpr uint32_t MAX_CACHES = 4;
26 : : /** Data type large enough to hold NUM_COINS-1. */
27 : : using coinidx_type = uint8_t;
28 : :
29 : : struct PrecomputedData
30 : : {
31 : : //! Randomly generated COutPoint values.
32 : : COutPoint outpoints[NUM_OUTPOINTS];
33 : :
34 : : //! Randomly generated Coin values.
35 : : Coin coins[NUM_COINS];
36 : :
37 : 1 : PrecomputedData()
38 [ + + + + : 513 : {
- - ]
39 : : static const uint8_t PREFIX_O[1] = {'o'}; /** Hash prefix for outpoint hashes. */
40 : : static const uint8_t PREFIX_S[1] = {'s'}; /** Hash prefix for coins scriptPubKeys. */
41 : : static const uint8_t PREFIX_M[1] = {'m'}; /** Hash prefix for coins nValue/fCoinBase. */
42 : :
43 [ + + ]: 257 : for (uint32_t i = 0; i < NUM_OUTPOINTS; ++i) {
44 : 256 : uint32_t idx = (i * 1200U) >> 12; /* Map 3 or 4 entries to same txid. */
45 : 256 : const uint8_t ser[4] = {uint8_t(idx), uint8_t(idx >> 8), uint8_t(idx >> 16), uint8_t(idx >> 24)};
46 : 256 : uint256 txid;
47 [ + - + - : 256 : CSHA256().Write(PREFIX_O, 1).Write(ser, sizeof(ser)).Finalize(txid.begin());
+ - + - ]
48 : 256 : outpoints[i].hash = Txid::FromUint256(txid);
49 : 256 : outpoints[i].n = i;
50 : : }
51 : :
52 [ + + ]: 257 : for (uint32_t i = 0; i < NUM_COINS; ++i) {
53 : 256 : const uint8_t ser[4] = {uint8_t(i), uint8_t(i >> 8), uint8_t(i >> 16), uint8_t(i >> 24)};
54 : 256 : uint256 hash;
55 [ + - + - : 256 : CSHA256().Write(PREFIX_S, 1).Write(ser, sizeof(ser)).Finalize(hash.begin());
+ - + - ]
56 : : /* Convert hash to scriptPubkeys (of different lengths, so SanityCheck's cached memory
57 : : * usage check has a chance to detect mismatches). */
58 [ + + + + : 256 : switch (i % 5U) {
+ - ]
59 : 52 : case 0: /* P2PKH */
60 : 52 : coins[i].out.scriptPubKey.resize(25);
61 [ + - ]: 52 : coins[i].out.scriptPubKey[0] = OP_DUP;
62 [ + - ]: 52 : coins[i].out.scriptPubKey[1] = OP_HASH160;
63 [ + - ]: 52 : coins[i].out.scriptPubKey[2] = 20;
64 [ + - ]: 104 : std::copy(hash.begin(), hash.begin() + 20, coins[i].out.scriptPubKey.begin() + 3);
65 [ + - ]: 52 : coins[i].out.scriptPubKey[23] = OP_EQUALVERIFY;
66 [ + - ]: 52 : coins[i].out.scriptPubKey[24] = OP_CHECKSIG;
67 : 52 : break;
68 : 51 : case 1: /* P2SH */
69 : 51 : coins[i].out.scriptPubKey.resize(23);
70 [ + - ]: 51 : coins[i].out.scriptPubKey[0] = OP_HASH160;
71 [ + - ]: 51 : coins[i].out.scriptPubKey[1] = 20;
72 [ + - ]: 102 : std::copy(hash.begin(), hash.begin() + 20, coins[i].out.scriptPubKey.begin() + 2);
73 [ + - ]: 51 : coins[i].out.scriptPubKey[12] = OP_EQUAL;
74 : 51 : break;
75 : 51 : case 2: /* P2WPKH */
76 : 51 : coins[i].out.scriptPubKey.resize(22);
77 [ + - ]: 51 : coins[i].out.scriptPubKey[0] = OP_0;
78 [ + - ]: 51 : coins[i].out.scriptPubKey[1] = 20;
79 [ + - ]: 102 : std::copy(hash.begin(), hash.begin() + 20, coins[i].out.scriptPubKey.begin() + 2);
80 : : break;
81 : 51 : case 3: /* P2WSH */
82 : 51 : coins[i].out.scriptPubKey.resize(34);
83 [ + - ]: 51 : coins[i].out.scriptPubKey[0] = OP_0;
84 [ + - ]: 51 : coins[i].out.scriptPubKey[1] = 32;
85 [ + - ]: 102 : std::copy(hash.begin(), hash.begin() + 32, coins[i].out.scriptPubKey.begin() + 2);
86 : : break;
87 : 51 : case 4: /* P2TR */
88 : 51 : coins[i].out.scriptPubKey.resize(34);
89 [ + - ]: 51 : coins[i].out.scriptPubKey[0] = OP_1;
90 [ + - ]: 51 : coins[i].out.scriptPubKey[1] = 32;
91 [ + - ]: 102 : std::copy(hash.begin(), hash.begin() + 32, coins[i].out.scriptPubKey.begin() + 2);
92 : : break;
93 : : }
94 : : /* Hash again to construct nValue and fCoinBase. */
95 [ + - + - : 256 : CSHA256().Write(PREFIX_M, 1).Write(ser, sizeof(ser)).Finalize(hash.begin());
+ - + - ]
96 : 256 : coins[i].out.nValue = CAmount(hash.GetUint64(0) % MAX_MONEY);
97 : 256 : coins[i].fCoinBase = (hash.GetUint64(1) & 7) == 0;
98 : 256 : coins[i].nHeight = 0; /* Real nHeight used in simulation is set dynamically. */
99 : : }
100 [ - - ]: 1 : }
101 : : };
102 : :
103 : : enum class EntryType : uint8_t
104 : : {
105 : : /* This entry in the cache does not exist (so we'd have to look in the parent cache). */
106 : : NONE,
107 : :
108 : : /* This entry in the cache corresponds to an unspent coin. */
109 : : UNSPENT,
110 : :
111 : : /* This entry in the cache corresponds to a spent coin. */
112 : : SPENT,
113 : : };
114 : :
115 : : struct CacheEntry
116 : : {
117 : : /* Type of entry. */
118 : : EntryType entrytype;
119 : :
120 : : /* Index in the coins array this entry corresponds to (only if entrytype == UNSPENT). */
121 : : coinidx_type coinidx;
122 : :
123 : : /* nHeight value for this entry (so the coins[coinidx].nHeight value is ignored; only if entrytype == UNSPENT). */
124 : : uint32_t height;
125 : : };
126 : :
127 : : struct CacheLevel
128 : : {
129 : : CacheEntry entry[NUM_OUTPOINTS];
130 : :
131 : 99892 : void Wipe() {
132 [ + + + + : 25672244 : for (uint32_t i = 0; i < NUM_OUTPOINTS; ++i) {
+ + + + ]
133 : 25572352 : entry[i].entrytype = EntryType::NONE;
134 : : }
135 : : }
136 : : };
137 : :
138 : : /** Class for the base of the hierarchy (roughly simulating a memory-backed CCoinsViewDB).
139 : : *
140 : : * The initial state consists of the empty UTXO set.
141 : : */
142 : 586 : class CoinsViewBottom final : public CCoinsView
143 : : {
144 : : std::map<COutPoint, Coin> m_data;
145 : :
146 : : public:
147 : 623444 : std::optional<Coin> GetCoin(const COutPoint& outpoint) const final
148 : : {
149 [ + + ]: 623444 : if (auto it{m_data.find(outpoint)}; it != m_data.end()) {
150 [ - + ]: 63575 : assert(!it->second.IsSpent());
151 : 63575 : return it->second;
152 : : }
153 : 559869 : return std::nullopt;
154 : : }
155 : :
156 : 0 : uint256 GetBestBlock() const final { return {}; }
157 : 0 : std::vector<uint256> GetHeadBlocks() const final { return {}; }
158 : 0 : std::unique_ptr<CCoinsViewCursor> Cursor() const final { return {}; }
159 : 0 : size_t EstimateSize() const final { return m_data.size(); }
160 : :
161 : 52551 : void BatchWrite(CoinsViewCacheCursor& cursor, const uint256&) final
162 : : {
163 [ + + ]: 129158 : for (auto it{cursor.Begin()}; it != cursor.End(); it = cursor.NextAndMaybeErase(*it)) {
164 [ + - ]: 76607 : if (it->second.IsDirty()) {
165 [ + + ]: 76607 : if (it->second.coin.IsSpent()) {
166 : 14746 : m_data.erase(it->first);
167 : : } else {
168 [ + + ]: 61861 : if (cursor.WillErase(*it)) {
169 : 16786 : m_data[it->first] = std::move(it->second.coin);
170 : : } else {
171 : 45075 : m_data[it->first] = it->second.coin;
172 : : }
173 : : }
174 : : } else {
175 : : /* For non-dirty entries being written, compare them with what we have. */
176 : 0 : auto it2 = m_data.find(it->first);
177 [ # # ]: 0 : if (it->second.coin.IsSpent()) {
178 [ # # ]: 0 : assert(it2 == m_data.end());
179 : : } else {
180 [ # # ]: 0 : assert(it2 != m_data.end());
181 [ # # ]: 0 : assert(it->second.coin.out == it2->second.out);
182 [ # # ]: 0 : assert(it->second.coin.fCoinBase == it2->second.fCoinBase);
183 [ # # ]: 0 : assert(it->second.coin.nHeight == it2->second.nHeight);
184 : : }
185 : : }
186 : : }
187 : 52551 : }
188 : : };
189 : :
190 : : } // namespace
191 : :
192 [ + - ]: 1040 : FUZZ_TARGET(coinscache_sim)
193 : : {
194 : : /** Precomputed COutPoint and CCoins values. */
195 [ + + + - : 586 : static const PrecomputedData data;
+ - ]
196 : :
197 : : /** Dummy coinsview instance (base of the hierarchy). */
198 : 586 : CoinsViewBottom bottom;
199 : : /** Real CCoinsViewCache objects. */
200 : 586 : std::vector<std::unique_ptr<CCoinsViewCache>> caches;
201 : : /** Simulated cache data (sim_caches[0] matches bottom, sim_caches[i+1] matches caches[i]). */
202 : 586 : CacheLevel sim_caches[MAX_CACHES + 1];
203 : : /** Current height in the simulation. */
204 : 586 : uint32_t current_height = 1U;
205 : :
206 : : // Initialize bottom simulated cache.
207 : 586 : sim_caches[0].Wipe();
208 : :
209 : : /** Helper lookup function in the simulated cache stack. */
210 : 792358 : auto lookup = [&](uint32_t outpointidx, int sim_idx = -1) -> std::optional<std::pair<coinidx_type, uint32_t>> {
211 [ + + - + ]: 791772 : uint32_t cache_idx = sim_idx == -1 ? caches.size() : sim_idx;
212 : 3044740 : while (true) {
213 : 1918256 : const auto& entry = sim_caches[cache_idx].entry[outpointidx];
214 [ + + ]: 1918256 : if (entry.entrytype == EntryType::UNSPENT) {
215 : 216878 : return {{entry.coinidx, entry.height}};
216 [ + + ]: 1701378 : } else if (entry.entrytype == EntryType::SPENT) {
217 : 127219 : return std::nullopt;
218 : 1574159 : };
219 [ + + ]: 1574159 : if (cache_idx == 0) break;
220 : 1126484 : --cache_idx;
221 : 1126484 : }
222 : 447675 : return std::nullopt;
223 : 586 : };
224 : :
225 : : /** Flush changes in top cache to the one below. */
226 : 152020 : auto flush = [&]() {
227 [ - + - + ]: 151434 : assert(caches.size() >= 1);
228 : 151434 : auto& cache = sim_caches[caches.size()];
229 : 151434 : auto& prev_cache = sim_caches[caches.size() - 1];
230 [ + + ]: 38918538 : for (uint32_t outpointidx = 0; outpointidx < NUM_OUTPOINTS; ++outpointidx) {
231 [ + + ]: 38767104 : if (cache.entry[outpointidx].entrytype != EntryType::NONE) {
232 : 360568 : prev_cache.entry[outpointidx] = cache.entry[outpointidx];
233 : 360568 : cache.entry[outpointidx].entrytype = EntryType::NONE;
234 : : }
235 : : }
236 : 152020 : };
237 : :
238 : : // Main simulation loop: read commands from the fuzzer input, and apply them
239 : : // to both the real cache stack and the simulation.
240 : 586 : FuzzedDataProvider provider(buffer.data(), buffer.size());
241 [ + + + + ]: 1097983 : LIMITED_WHILE(provider.remaining_bytes(), 10000) {
242 : : // Every operation (except "Change height") moves current height forward,
243 : : // so it functions as a kind of epoch, making ~all UTXOs unique.
244 : 1097397 : ++current_height;
245 : : // Make sure there is always at least one CCoinsViewCache.
246 [ + + ]: 1097397 : if (caches.empty()) {
247 [ + - + - : 21866 : caches.emplace_back(new CCoinsViewCache(&bottom, /*deterministic=*/true));
+ - - - ]
248 [ - + ]: 21866 : sim_caches[caches.size()].Wipe();
249 : : }
250 : :
251 : : // Execute command.
252 [ + - ]: 1097397 : CallOneOf(
253 : : provider,
254 : :
255 : 111309 : [&]() { // GetCoin
256 : 111309 : uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);
257 : : // Look up in simulation data.
258 : 111309 : auto sim = lookup(outpointidx);
259 : : // Look up in real caches.
260 : 111309 : auto realcoin = caches.back()->GetCoin(data.outpoints[outpointidx]);
261 : : // Compare results.
262 [ + + ]: 111309 : if (!sim.has_value()) {
263 [ - + ]: 77846 : assert(!realcoin);
264 : : } else {
265 [ + - - + ]: 33463 : assert(realcoin && !realcoin->IsSpent());
266 : 33463 : const auto& simcoin = data.coins[sim->first];
267 [ - + ]: 33463 : assert(realcoin->out == simcoin.out);
268 [ - + ]: 33463 : assert(realcoin->fCoinBase == simcoin.fCoinBase);
269 [ - + ]: 33463 : assert(realcoin->nHeight == sim->second);
270 : : }
271 : 111309 : },
272 : :
273 : 37554 : [&]() { // HaveCoin
274 : 37554 : uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);
275 : : // Look up in simulation data.
276 : 37554 : auto sim = lookup(outpointidx);
277 : : // Look up in real caches.
278 : 37554 : auto real = caches.back()->HaveCoin(data.outpoints[outpointidx]);
279 : : // Compare results.
280 [ - + ]: 37554 : assert(sim.has_value() == real);
281 : 37554 : },
282 : :
283 : 43100 : [&]() { // HaveCoinInCache
284 : 43100 : uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);
285 : : // Invoke on real cache (there is no equivalent in simulation, so nothing to compare result with).
286 : 43100 : (void)caches.back()->HaveCoinInCache(data.outpoints[outpointidx]);
287 : 43100 : },
288 : :
289 : 41684 : [&]() { // AccessCoin
290 : 41684 : uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);
291 : : // Look up in simulation data.
292 : 41684 : auto sim = lookup(outpointidx);
293 : : // Look up in real caches.
294 : 41684 : const auto& realcoin = caches.back()->AccessCoin(data.outpoints[outpointidx]);
295 : : // Compare results.
296 [ + + ]: 41684 : if (!sim.has_value()) {
297 [ - + ]: 27250 : assert(realcoin.IsSpent());
298 : : } else {
299 [ - + ]: 14434 : assert(!realcoin.IsSpent());
300 : 14434 : const auto& simcoin = data.coins[sim->first];
301 [ - + ]: 14434 : assert(simcoin.out == realcoin.out);
302 [ - + ]: 14434 : assert(simcoin.fCoinBase == realcoin.fCoinBase);
303 [ - + ]: 14434 : assert(realcoin.nHeight == sim->second);
304 : : }
305 : 41684 : },
306 : :
307 : 103840 : [&]() { // AddCoin (only possible_overwrite if necessary)
308 : 103840 : uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);
309 : 103840 : uint32_t coinidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_COINS - 1);
310 : : // Look up in simulation data (to know whether we must set possible_overwrite or not).
311 : 103840 : auto sim = lookup(outpointidx);
312 : : // Invoke on real caches.
313 : 103840 : Coin coin = data.coins[coinidx];
314 : 103840 : coin.nHeight = current_height;
315 [ + - ]: 103840 : caches.back()->AddCoin(data.outpoints[outpointidx], std::move(coin), sim.has_value());
316 : : // Apply to simulation data.
317 [ - + ]: 103840 : auto& entry = sim_caches[caches.size()].entry[outpointidx];
318 : 103840 : entry.entrytype = EntryType::UNSPENT;
319 : 103840 : entry.coinidx = coinidx;
320 : 103840 : entry.height = current_height;
321 : 103840 : },
322 : :
323 : 154883 : [&]() { // AddCoin (always possible_overwrite)
324 : 154883 : uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);
325 : 154883 : uint32_t coinidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_COINS - 1);
326 : : // Invoke on real caches.
327 : 154883 : Coin coin = data.coins[coinidx];
328 : 154883 : coin.nHeight = current_height;
329 [ + - ]: 154883 : caches.back()->AddCoin(data.outpoints[outpointidx], std::move(coin), true);
330 : : // Apply to simulation data.
331 [ - + ]: 154883 : auto& entry = sim_caches[caches.size()].entry[outpointidx];
332 : 154883 : entry.entrytype = EntryType::UNSPENT;
333 : 154883 : entry.coinidx = coinidx;
334 : 154883 : entry.height = current_height;
335 : 154883 : },
336 : :
337 : 78911 : [&]() { // SpendCoin (moveto = nullptr)
338 : 78911 : uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);
339 : : // Invoke on real caches.
340 : 78911 : caches.back()->SpendCoin(data.outpoints[outpointidx], nullptr);
341 : : // Apply to simulation data.
342 [ - + ]: 78911 : sim_caches[caches.size()].entry[outpointidx].entrytype = EntryType::SPENT;
343 : 78911 : },
344 : :
345 : 69353 : [&]() { // SpendCoin (with moveto)
346 : 69353 : uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);
347 : : // Look up in simulation data (to compare the returned *moveto with).
348 : 69353 : auto sim = lookup(outpointidx);
349 : : // Invoke on real caches.
350 : 69353 : Coin realcoin;
351 [ + - ]: 69353 : caches.back()->SpendCoin(data.outpoints[outpointidx], &realcoin);
352 : : // Apply to simulation data.
353 [ - + ]: 69353 : sim_caches[caches.size()].entry[outpointidx].entrytype = EntryType::SPENT;
354 : : // Compare *moveto with the value expected based on simulation data.
355 [ + + ]: 69353 : if (!sim.has_value()) {
356 [ - + ]: 38268 : assert(realcoin.IsSpent());
357 : : } else {
358 [ - + ]: 31085 : assert(!realcoin.IsSpent());
359 : 31085 : const auto& simcoin = data.coins[sim->first];
360 [ - + ]: 31085 : assert(simcoin.out == realcoin.out);
361 [ - + ]: 31085 : assert(simcoin.fCoinBase == realcoin.fCoinBase);
362 [ - + ]: 31085 : assert(realcoin.nHeight == sim->second);
363 : : }
364 : 69353 : },
365 : :
366 : 33025 : [&]() { // Uncache
367 : 33025 : uint32_t outpointidx = provider.ConsumeIntegralInRange<uint32_t>(0, NUM_OUTPOINTS - 1);
368 : : // Apply to real caches (there is no equivalent in our simulation).
369 : 33025 : caches.back()->Uncache(data.outpoints[outpointidx]);
370 : 33025 : },
371 : :
372 : 57338 : [&]() { // Add a cache level (if not already at the max).
373 [ - + + + ]: 57338 : if (caches.size() != MAX_CACHES) {
374 : : // Apply to real caches.
375 [ + - ]: 24535 : caches.emplace_back(new CCoinsViewCache(&*caches.back(), /*deterministic=*/true));
376 : : // Apply to simulation data.
377 [ - + ]: 24535 : sim_caches[caches.size()].Wipe();
378 : : }
379 : 57338 : },
380 : :
381 : 45315 : [&]() { // Remove a cache level.
382 : : // Apply to real caches (this reduces caches.size(), implicitly doing the same on the simulation data).
383 : 45315 : caches.back()->SanityCheck();
384 : 45315 : caches.pop_back();
385 : 45315 : },
386 : :
387 : 52141 : [&]() { // Flush.
388 : : // Apply to simulation data.
389 : 52141 : flush();
390 : : // Apply to real caches.
391 : 52141 : caches.back()->Flush(/*reallocate_cache=*/provider.ConsumeBool());
392 : 52141 : },
393 : :
394 : 99293 : [&]() { // Sync.
395 : : // Apply to simulation data (note that in our simulation, syncing and flushing is the same thing).
396 : 99293 : flush();
397 : : // Apply to real caches.
398 : 99293 : caches.back()->Sync();
399 : 99293 : },
400 : :
401 : 52905 : [&]() { // Reset.
402 [ - + ]: 52905 : sim_caches[caches.size()].Wipe();
403 : : // Apply to real caches.
404 : 52905 : {
405 : 52905 : const auto reset_guard{caches.back()->CreateResetGuard()};
406 : 52905 : }
407 : 52905 : },
408 : :
409 : 39842 : [&]() { // GetCacheSize
410 : 39842 : (void)caches.back()->GetCacheSize();
411 : 39842 : },
412 : :
413 : 28324 : [&]() { // DynamicMemoryUsage
414 : 28324 : (void)caches.back()->DynamicMemoryUsage();
415 : 28324 : },
416 : :
417 : 48580 : [&]() { // Change height
418 : 48580 : current_height = provider.ConsumeIntegralInRange<uint32_t>(1, current_height - 1);
419 : 48580 : }
420 : : );
421 : : }
422 : :
423 : : // Sanity check all the remaining caches
424 [ + + ]: 1672 : for (const auto& cache : caches) {
425 [ + - ]: 1086 : cache->SanityCheck();
426 : : }
427 : :
428 : : // Full comparison between caches and simulation data, from bottom to top,
429 : : // as AccessCoin on a higher cache may affect caches below it.
430 [ - + + + ]: 1672 : for (unsigned sim_idx = 1; sim_idx <= caches.size(); ++sim_idx) {
431 : 1086 : auto& cache = *caches[sim_idx - 1];
432 : 1086 : size_t cache_size = 0;
433 : :
434 [ + + ]: 279102 : for (uint32_t outpointidx = 0; outpointidx < NUM_OUTPOINTS; ++outpointidx) {
435 [ + - ]: 278016 : cache_size += cache.HaveCoinInCache(data.outpoints[outpointidx]);
436 [ + - ]: 278016 : const auto& real = cache.AccessCoin(data.outpoints[outpointidx]);
437 : 278016 : auto sim = lookup(outpointidx, sim_idx);
438 [ + + ]: 278016 : if (!sim.has_value()) {
439 [ - + ]: 230241 : assert(real.IsSpent());
440 : : } else {
441 [ - + ]: 47775 : assert(!real.IsSpent());
442 [ - + ]: 47775 : assert(real.out == data.coins[sim->first].out);
443 [ - + ]: 47775 : assert(real.fCoinBase == data.coins[sim->first].fCoinBase);
444 [ - + ]: 47775 : assert(real.nHeight == sim->second);
445 : : }
446 : : }
447 : :
448 : : // HaveCoinInCache ignores spent coins, so GetCacheSize() may exceed it. */
449 [ + - - + ]: 1086 : assert(cache.GetCacheSize() >= cache_size);
450 : : }
451 : :
452 : : // Compare the bottom coinsview (not a CCoinsViewCache) with sim_cache[0].
453 [ + + ]: 150602 : for (uint32_t outpointidx = 0; outpointidx < NUM_OUTPOINTS; ++outpointidx) {
454 : 150016 : auto realcoin = bottom.GetCoin(data.outpoints[outpointidx]);
455 : 150016 : auto sim = lookup(outpointidx, 0);
456 [ + + ]: 150016 : if (!sim.has_value()) {
457 [ - + ]: 133103 : assert(!realcoin);
458 : : } else {
459 [ + - - + ]: 16913 : assert(realcoin && !realcoin->IsSpent());
460 [ - + ]: 16913 : assert(realcoin->out == data.coins[sim->first].out);
461 [ - + ]: 16913 : assert(realcoin->fCoinBase == data.coins[sim->first].fCoinBase);
462 [ - + ]: 16913 : assert(realcoin->nHeight == sim->second);
463 : : }
464 : 150016 : }
465 : 586 : }
|