mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-03-02 01:36:13 +00:00
fuzz: prevent invalid FRESH entries and surface BatchWrite errors
Modify fuzzer logic to avoid setting `FRESH` for an outpoint that already exists unspent in the parent view, and ensure `FRESH` implies `DIRTY`. This keeps cursor invariants realistic and lets `BatchWrite` failures expose real bugs without resetting state.
This commit is contained in:
parent
780f460635
commit
3281824ecf
@ -77,18 +77,7 @@ public:
|
||||
{
|
||||
// Nothing must modify cacheCoins other than BatchWrite.
|
||||
assert(ComputeCacheCoinsSnapshot() == m_expected_snapshot);
|
||||
try {
|
||||
CCoinsViewCache::BatchWrite(cursor, block_hash);
|
||||
} catch (const std::logic_error& e) {
|
||||
// This error is thrown if the cursor contains a fresh entry for an outpoint that we already have a fresh
|
||||
// entry for. This can happen if the fuzzer calls AddCoin -> Flush -> AddCoin -> Flush on the child cache.
|
||||
// There's not an easy way to prevent the fuzzer from reaching this, so we handle it here.
|
||||
// Since it is thrown in the middle of the write, we reset our own state and iterate through
|
||||
// the cursor so the caller's state is also reset.
|
||||
assert(e.what() == std::string{"FRESH flag misapplied to coin that exists in parent cache"});
|
||||
Reset();
|
||||
for (auto it{cursor.Begin()}; it != cursor.End(); it = cursor.NextAndMaybeErase(*it)) {}
|
||||
}
|
||||
CCoinsViewCache::BatchWrite(cursor, block_hash);
|
||||
m_expected_snapshot = ComputeCacheCoinsSnapshot();
|
||||
}
|
||||
|
||||
@ -199,8 +188,6 @@ void TestCoinsView(FuzzedDataProvider& fuzzed_data_provider, CCoinsViewCache& co
|
||||
LIMITED_WHILE(good_data && fuzzed_data_provider.ConsumeBool(), 10'000)
|
||||
{
|
||||
CCoinsCacheEntry coins_cache_entry;
|
||||
const auto dirty{fuzzed_data_provider.ConsumeBool()};
|
||||
const auto fresh{fuzzed_data_provider.ConsumeBool()};
|
||||
if (fuzzed_data_provider.ConsumeBool()) {
|
||||
coins_cache_entry.coin = random_coin;
|
||||
} else {
|
||||
@ -211,26 +198,20 @@ void TestCoinsView(FuzzedDataProvider& fuzzed_data_provider, CCoinsViewCache& co
|
||||
}
|
||||
coins_cache_entry.coin = *opt_coin;
|
||||
}
|
||||
// Avoid setting FRESH for an outpoint that already exists unspent in the parent view.
|
||||
bool fresh{!coins_view_cache.PeekCoin(random_out_point) && fuzzed_data_provider.ConsumeBool()};
|
||||
bool dirty{fresh || fuzzed_data_provider.ConsumeBool()};
|
||||
auto it{coins_map.emplace(random_out_point, std::move(coins_cache_entry)).first};
|
||||
if (dirty) CCoinsCacheEntry::SetDirty(*it, sentinel);
|
||||
if (fresh) CCoinsCacheEntry::SetFresh(*it, sentinel);
|
||||
dirty_count += dirty;
|
||||
}
|
||||
bool expected_code_path = false;
|
||||
try {
|
||||
auto cursor{CoinsViewCacheCursor(dirty_count, sentinel, coins_map, /*will_erase=*/true)};
|
||||
uint256 best_block{coins_view_cache.GetBestBlock()};
|
||||
if (fuzzed_data_provider.ConsumeBool()) best_block = ConsumeUInt256(fuzzed_data_provider);
|
||||
// Set best block hash to non-null to satisfy the assertion in CCoinsViewDB::BatchWrite().
|
||||
if (is_db && best_block.IsNull()) best_block = uint256::ONE;
|
||||
coins_view_cache.BatchWrite(cursor, best_block);
|
||||
expected_code_path = true;
|
||||
} catch (const std::logic_error& e) {
|
||||
if (e.what() == std::string{"FRESH flag misapplied to coin that exists in parent cache"}) {
|
||||
expected_code_path = true;
|
||||
}
|
||||
}
|
||||
assert(expected_code_path);
|
||||
auto cursor{CoinsViewCacheCursor(dirty_count, sentinel, coins_map, /*will_erase=*/true)};
|
||||
uint256 best_block{coins_view_cache.GetBestBlock()};
|
||||
if (fuzzed_data_provider.ConsumeBool()) best_block = ConsumeUInt256(fuzzed_data_provider);
|
||||
// Set best block hash to non-null to satisfy the assertion in CCoinsViewDB::BatchWrite().
|
||||
if (is_db && best_block.IsNull()) best_block = uint256::ONE;
|
||||
coins_view_cache.BatchWrite(cursor, best_block);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user