Add CCoinsViewCache::SanityCheck() and use it in fuzz test

This commit is contained in:
Pieter Wuille 2023-02-01 18:28:08 -05:00
parent 3c9cea1340
commit b0ff310840
3 changed files with 28 additions and 1 deletions

View file

@ -314,6 +314,23 @@ void CCoinsViewCache::ReallocateCache()
::new (&cacheCoins) CCoinsMap();
}
void CCoinsViewCache::SanityCheck() const
{
size_t recomputed_usage = 0;
for (const auto& [_, entry] : cacheCoins) {
unsigned attr = 0;
if (entry.flags & CCoinsCacheEntry::DIRTY) attr |= 1;
if (entry.flags & CCoinsCacheEntry::FRESH) attr |= 2;
if (entry.coin.IsSpent()) attr |= 4;
// Only 5 combinations are possible.
assert(attr != 2 && attr != 4 && attr != 7);
// Recompute cachedCoinsUsage.
recomputed_usage += entry.coin.DynamicMemoryUsage();
}
assert(recomputed_usage == cachedCoinsUsage);
}
static const size_t MIN_TRANSACTION_OUTPUT_WEIGHT = WITNESS_SCALE_FACTOR * ::GetSerializeSize(CTxOut(), PROTOCOL_VERSION);
static const size_t MAX_OUTPUTS_PER_BLOCK = MAX_BLOCK_WEIGHT / MIN_TRANSACTION_OUTPUT_WEIGHT;

View file

@ -320,6 +320,9 @@ public:
//! See: https://stackoverflow.com/questions/42114044/how-to-release-unordered-map-memory
void ReallocateCache();
//! Run an internal sanity check on the cache data structure. */
void SanityCheck() const;
private:
/**
* @note this is marked const, but may actually append to `cacheCoins`, increasing

View file

@ -51,7 +51,8 @@ struct PrecomputedData
const uint8_t ser[4] = {uint8_t(i), uint8_t(i >> 8), uint8_t(i >> 16), uint8_t(i >> 24)};
uint256 hash;
CSHA256().Write(PREFIX_S, 1).Write(ser, sizeof(ser)).Finalize(hash.begin());
/* Convert hash to scriptPubkeys. */
/* Convert hash to scriptPubkeys (of different lengths, so SanityCheck's cached memory
* usage check has a chance to detect mismatches). */
switch (i % 5U) {
case 0: /* P2PKH */
coins[i].out.scriptPubKey.resize(25);
@ -381,6 +382,7 @@ FUZZ_TARGET(coinscache_sim)
[&]() { // Remove a cache level.
// Apply to real caches (this reduces caches.size(), implicitly doing the same on the simulation data).
caches.back()->SanityCheck();
caches.pop_back();
},
@ -420,6 +422,11 @@ FUZZ_TARGET(coinscache_sim)
);
}
// Sanity check all the remaining caches
for (const auto& cache : caches) {
cache->SanityCheck();
}
// Full comparison between caches and simulation data, from bottom to top,
// as AccessCoin on a higher cache may affect caches below it.
for (unsigned sim_idx = 1; sim_idx <= caches.size(); ++sim_idx) {