diff --git a/src/test/fuzz/coins_view.cpp b/src/test/fuzz/coins_view.cpp index 9ef9e47372e..5af5996fa6a 100644 --- a/src/test/fuzz/coins_view.cpp +++ b/src/test/fuzz/coins_view.cpp @@ -90,9 +90,11 @@ void initialize_coins_view() static const auto testing_setup = MakeNoLogFileContext<>(); } -void TestCoinsView(FuzzedDataProvider& fuzzed_data_provider, CCoinsViewCache& coins_view_cache, CCoinsView& backend_coins_view, bool is_db) +void TestCoinsView(FuzzedDataProvider& fuzzed_data_provider, CCoinsViewCache& coins_view_cache, CCoinsView* backend_coins_view, bool is_db) { bool good_data{true}; + auto* original_backend{backend_coins_view}; + CCoinsView coins_view_empty{}; if (is_db) coins_view_cache.SetBestBlock(uint256::ONE); COutPoint random_out_point; @@ -124,15 +126,13 @@ void TestCoinsView(FuzzedDataProvider& fuzzed_data_provider, CCoinsViewCache& co }, [&] { uint256 best_block{ConsumeUInt256(fuzzed_data_provider)}; - // Set best block hash to non-null to satisfy the assertion in CCoinsViewDB::BatchWrite(). + // `CCoinsViewDB::BatchWrite()` requires a non-null best block. if (is_db && best_block.IsNull()) best_block = uint256::ONE; coins_view_cache.SetBestBlock(best_block); }, [&] { - { - const auto reset_guard{coins_view_cache.CreateResetGuard()}; - } - // Set best block hash to non-null to satisfy the assertion in CCoinsViewDB::BatchWrite(). + (void)coins_view_cache.CreateResetGuard(); + // Reset() clears the best block, so reseed db-backed caches. if (is_db) { const uint256 best_block{ConsumeUInt256(fuzzed_data_provider)}; if (best_block.IsNull()) { @@ -150,10 +150,16 @@ void TestCoinsView(FuzzedDataProvider& fuzzed_data_provider, CCoinsViewCache& co coins_view_cache.Uncache(random_out_point); }, [&] { - if (fuzzed_data_provider.ConsumeBool()) { - backend_coins_view = CCoinsView{}; + const bool use_original_backend{fuzzed_data_provider.ConsumeBool()}; + if (use_original_backend && backend_coins_view != original_backend) { + // FRESH flags valid against the empty backend may be invalid + // against the original backend, so reset before restoring it. + (void)coins_view_cache.CreateResetGuard(); + // Reset() clears the best block; db backends require a non-null hash. + if (is_db) coins_view_cache.SetBestBlock(uint256::ONE); } - coins_view_cache.SetBackend(backend_coins_view); + backend_coins_view = use_original_backend ? original_backend : &coins_view_empty; + coins_view_cache.SetBackend(*backend_coins_view); }, [&] { const std::optional opt_out_point = ConsumeDeserializable(fuzzed_data_provider); @@ -232,13 +238,12 @@ void TestCoinsView(FuzzedDataProvider& fuzzed_data_provider, CCoinsViewCache& co } { - if (is_db) { - std::unique_ptr coins_view_cursor = backend_coins_view.Cursor(); - assert(!!coins_view_cursor); + if (is_db && backend_coins_view == original_backend) { + assert(backend_coins_view->Cursor()); } - (void)backend_coins_view.EstimateSize(); - (void)backend_coins_view.GetBestBlock(); - (void)backend_coins_view.GetHeadBlocks(); + (void)backend_coins_view->EstimateSize(); + (void)backend_coins_view->GetBestBlock(); + (void)backend_coins_view->GetHeadBlocks(); } if (fuzzed_data_provider.ConsumeBool()) { @@ -328,11 +333,11 @@ void TestCoinsView(FuzzedDataProvider& fuzzed_data_provider, CCoinsViewCache& co assert(!exists_using_access_coin && !exists_using_have_coin_in_cache && !exists_using_have_coin); } // If HaveCoin on the backend is true, it must also be on the cache if the coin wasn't spent. - const bool exists_using_have_coin_in_backend = backend_coins_view.HaveCoin(random_out_point); + const bool exists_using_have_coin_in_backend = backend_coins_view->HaveCoin(random_out_point); if (!coin_using_access_coin.IsSpent() && exists_using_have_coin_in_backend) { assert(exists_using_have_coin); } - if (auto coin{backend_coins_view.GetCoin(random_out_point)}) { + if (auto coin{backend_coins_view->GetCoin(random_out_point)}) { assert(exists_using_have_coin_in_backend); // Note we can't assert that `coin_using_get_coin == *coin` because the coin in // the cache may have been modified but not yet flushed. @@ -347,7 +352,7 @@ FUZZ_TARGET(coins_view, .init = initialize_coins_view) FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()}; CCoinsView backend_coins_view; CCoinsViewCache coins_view_cache{&backend_coins_view, /*deterministic=*/true}; - TestCoinsView(fuzzed_data_provider, coins_view_cache, backend_coins_view, /*is_db=*/false); + TestCoinsView(fuzzed_data_provider, coins_view_cache, &backend_coins_view, /*is_db=*/false); } FUZZ_TARGET(coins_view_db, .init = initialize_coins_view) @@ -360,7 +365,7 @@ FUZZ_TARGET(coins_view_db, .init = initialize_coins_view) }; CCoinsViewDB backend_coins_view{std::move(db_params), CoinsViewOptions{}}; CCoinsViewCache coins_view_cache{&backend_coins_view, /*deterministic=*/true}; - TestCoinsView(fuzzed_data_provider, coins_view_cache, backend_coins_view, /*is_db=*/true); + TestCoinsView(fuzzed_data_provider, coins_view_cache, &backend_coins_view, /*is_db=*/true); } // Creates a CoinsViewOverlay and a MutationGuardCoinsViewCache as the base. @@ -373,5 +378,5 @@ FUZZ_TARGET(coins_view_overlay, .init = initialize_coins_view) CCoinsView backend_base_coins_view; MutationGuardCoinsViewCache backend_cache{&backend_base_coins_view, /*deterministic=*/true}; CoinsViewOverlay coins_view_cache{&backend_cache, /*deterministic=*/true}; - TestCoinsView(fuzzed_data_provider, coins_view_cache, backend_cache, /*is_db=*/false); + TestCoinsView(fuzzed_data_provider, coins_view_cache, &backend_cache, /*is_db=*/false); }