diff --git a/src/test/fuzz/coins_view.cpp b/src/test/fuzz/coins_view.cpp index 011b660eeea..a47dc3706c8 100644 --- a/src/test/fuzz/coins_view.cpp +++ b/src/test/fuzz/coins_view.cpp @@ -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); }); }