Repository: bitcoin/bitcoin
File: src/test/fuzz/coins_view.cpp (C++)

Code before:
    {
        CallOneOf(
            fuzzed_data_provider,
            [&] {
                if (random_coin.IsSpent()) {
                    return;
                }
                COutPoint outpoint{random_out_point};
                Coin coin{random_coin};
                if (fuzzed_data_provider.ConsumeBool()) {
                    const bool possible_overwrite{fuzzed_data_provider.ConsumeBool()};
                    try {
                        coins_view_cache.AddCoin(outpoint, std::move(coin), possible_overwrite);
                    } catch (const std::logic_error& e) {
                        assert(e.what() == std::string{"Attempted to overwrite an unspent coin (when possible_overwrite is false)"});
                        assert(!possible_overwrite);
                    }
                } else {
                    coins_view_cache.EmplaceCoinInternalDANGER(std::move(outpoint), std::move(coin));
                }
            },
            [&] {
                try {
                    coins_view_cache.Flush(/*reallocate_cache=*/fuzzed_data_provider.ConsumeBool());
                } catch (const std::logic_error& e) {
                    assert(e.what() == std::string{"FRESH flag misapplied to coin that exists in parent cache"});
                    // Only a CCoinsViewCache will throw
                    assert(dynamic_cast<CCoinsViewCache*>(&backend_coins_view));
                    const auto reset_guard{coins_view_cache.CreateResetGuard()};
                }
            },
            [&] {
                    try {
                        coins_view_cache.Sync();
                    } catch (const std::logic_error& e) {
                        assert(e.what() == std::string{"FRESH flag misapplied to coin that exists in parent cache"});
                        // Only a CCoinsViewCache will throw
                        assert(dynamic_cast<CCoinsViewCache*>(&backend_coins_view));
                        const auto reset_guard{coins_view_cache.CreateResetGuard()};
                    }
            },
            [&] {
                uint256 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.SetBestBlock(best_block);
            },
            [&] {
                {
                    const auto reset_guard{coins_view_cache.CreateResetGuard()};
                }

Review comment (performance):
This is a bit concerning, the fuzzer shouldn't be able to trigger this logic error.
Would it help if #33018 cleanup landed first, would that simplify this change?
And regardless of the merge order, can we instead make the fuzzer avoid constructing invalid states in the first place and then drop the try/catches (e.g. by setting `possible_overwrite` properly based on `HaveCoinInCache` in `AddCoin`)?

Code after:
{
    bool good_data{true};

    if (is_db) coins_view_cache.SetBestBlock(uint256::ONE);
    COutPoint random_out_point;
    Coin random_coin;
    CMutableTransaction random_mutable_transaction;
    LIMITED_WHILE(good_data && fuzzed_data_provider.ConsumeBool(), 10'000)
    {
        CallOneOf(
            fuzzed_data_provider,
            [&] {
                if (random_coin.IsSpent()) {
                    return;
                }
                COutPoint outpoint{random_out_point};
                Coin coin{random_coin};
                if (fuzzed_data_provider.ConsumeBool()) {
                    const bool possible_overwrite{fuzzed_data_provider.ConsumeBool()};
                    try {
                        coins_view_cache.AddCoin(outpoint, std::move(coin), possible_overwrite);
                    } catch (const std::logic_error& e) {
                        assert(e.what() == std::string{"Attempted to overwrite an unspent coin (when possible_overwrite is false)"});
                        assert(!possible_overwrite);
                    }
                } else {
                    coins_view_cache.EmplaceCoinInternalDANGER(std::move(outpoint), std::move(coin));
                }
            },
            [&] {
                coins_view_cache.Flush(/*reallocate_cache=*/fuzzed_data_provider.ConsumeBool());
            },
            [&] {
                coins_view_cache.Sync();
            },
            [&] {
                uint256 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.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().
                if (is_db) {
                    const uint256 best_block{ConsumeUInt256(fuzzed_data_provider)};
                    if (best_block.IsNull()) {
                        good_data = false;
                        return;