Skip to content

Commit b0ff310

Browse files
committed
Add CCoinsViewCache::SanityCheck() and use it in fuzz test
1 parent 3c9cea1 commit b0ff310

File tree

3 files changed

+28
-1
lines changed

3 files changed

+28
-1
lines changed

src/coins.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,23 @@ void CCoinsViewCache::ReallocateCache()
314314
::new (&cacheCoins) CCoinsMap();
315315
}
316316

317+
void CCoinsViewCache::SanityCheck() const
318+
{
319+
size_t recomputed_usage = 0;
320+
for (const auto& [_, entry] : cacheCoins) {
321+
unsigned attr = 0;
322+
if (entry.flags & CCoinsCacheEntry::DIRTY) attr |= 1;
323+
if (entry.flags & CCoinsCacheEntry::FRESH) attr |= 2;
324+
if (entry.coin.IsSpent()) attr |= 4;
325+
// Only 5 combinations are possible.
326+
assert(attr != 2 && attr != 4 && attr != 7);
327+
328+
// Recompute cachedCoinsUsage.
329+
recomputed_usage += entry.coin.DynamicMemoryUsage();
330+
}
331+
assert(recomputed_usage == cachedCoinsUsage);
332+
}
333+
317334
static const size_t MIN_TRANSACTION_OUTPUT_WEIGHT = WITNESS_SCALE_FACTOR * ::GetSerializeSize(CTxOut(), PROTOCOL_VERSION);
318335
static const size_t MAX_OUTPUTS_PER_BLOCK = MAX_BLOCK_WEIGHT / MIN_TRANSACTION_OUTPUT_WEIGHT;
319336

src/coins.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,9 @@ class CCoinsViewCache : public CCoinsViewBacked
320320
//! See: https://stackoverflow.com/questions/42114044/how-to-release-unordered-map-memory
321321
void ReallocateCache();
322322

323+
//! Run an internal sanity check on the cache data structure. */
324+
void SanityCheck() const;
325+
323326
private:
324327
/**
325328
* @note this is marked const, but may actually append to `cacheCoins`, increasing

src/test/fuzz/coinscache_sim.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ struct PrecomputedData
5151
const uint8_t ser[4] = {uint8_t(i), uint8_t(i >> 8), uint8_t(i >> 16), uint8_t(i >> 24)};
5252
uint256 hash;
5353
CSHA256().Write(PREFIX_S, 1).Write(ser, sizeof(ser)).Finalize(hash.begin());
54-
/* Convert hash to scriptPubkeys. */
54+
/* Convert hash to scriptPubkeys (of different lengths, so SanityCheck's cached memory
55+
* usage check has a chance to detect mismatches). */
5556
switch (i % 5U) {
5657
case 0: /* P2PKH */
5758
coins[i].out.scriptPubKey.resize(25);
@@ -381,6 +382,7 @@ FUZZ_TARGET(coinscache_sim)
381382

382383
[&]() { // Remove a cache level.
383384
// Apply to real caches (this reduces caches.size(), implicitly doing the same on the simulation data).
385+
caches.back()->SanityCheck();
384386
caches.pop_back();
385387
},
386388

@@ -420,6 +422,11 @@ FUZZ_TARGET(coinscache_sim)
420422
);
421423
}
422424

425+
// Sanity check all the remaining caches
426+
for (const auto& cache : caches) {
427+
cache->SanityCheck();
428+
}
429+
423430
// Full comparison between caches and simulation data, from bottom to top,
424431
// as AccessCoin on a higher cache may affect caches below it.
425432
for (unsigned sim_idx = 1; sim_idx <= caches.size(); ++sim_idx) {

0 commit comments

Comments
 (0)