Repository: bitcoin/bitcoin
File: src/validation.cpp (C++)

Code before:

        auto rename_result = m_snapshot_chainstate->InvalidateCoinsDBOnDisk();
        if (!rename_result) {
            user_error += Untranslated("\n") + util::ErrorString(rename_result);
        }

        GetNotifications().fatalError(user_error);
    };

    if (index_new.GetBlockHash() != snapshot_blockhash) {
        LogPrintf("[snapshot] supposed base block %s does not match the "
          "snapshot base block %s (height %d). Snapshot is not valid.\n",
          index_new.ToString(), snapshot_blockhash.ToString(), snapshot_base_height);
        handle_invalid_snapshot();
        return SnapshotCompletionResult::BASE_BLOCKHASH_MISMATCH;
    }

    assert(index_new.nHeight == snapshot_base_height);

    int curr_height = m_ibd_chainstate->m_chain.Height();

    assert(snapshot_base_height == curr_height);
    assert(snapshot_base_height == index_new.nHeight);
    assert(this->IsUsable(m_snapshot_chainstate.get()));
    assert(this->GetAll().size() == 2);

    CCoinsViewDB& ibd_coins_db = m_ibd_chainstate->CoinsDB();
    m_ibd_chainstate->ForceFlushStateToDisk();

    const auto& maybe_au_data = m_options.chainparams.AssumeutxoForHeight(curr_height);
    if (!maybe_au_data) {
        LogPrintf("[snapshot] assumeutxo data not found for height "
            "(%d) - refusing to validate snapshot\n", curr_height);
        handle_invalid_snapshot();
        return SnapshotCompletionResult::MISSING_CHAINPARAMS;
    }

    const AssumeutxoData& au_data = *maybe_au_data;
    std::optional<CCoinsStats> maybe_ibd_stats;
    LogPrintf("[snapshot] computing UTXO stats for background chainstate to validate "
        "snapshot - this could take a few minutes\n");
    try {
        maybe_ibd_stats = ComputeUTXOStats(
            CoinStatsHashType::HASH_SERIALIZED,
            &ibd_coins_db,
            m_blockman,
            [&interrupt = m_interrupt] { SnapshotUTXOHashBreakpoint(interrupt); });
    } catch (StopHashingException const&) {
        return SnapshotCompletionResult::STATS_FAILED;
    }


Review comment (refactor):
I don't fully understand why this couldn't be an `Untranslated(strprintf` (is it because `bilingual_str user_error` doesn't have an `std::ostream` operator defined?), but if you touch again, consider simplifying to `+ ErrorString(rename_result)` (or extending `Result` with an error returning method as well, but that's likely outside the scope of this PR), i.e.
```C++
if (auto rename_result = m_snapshot_chainstate->InvalidateCoinsDBOnDisk(); !rename_result) {
    user_error += Untranslated("\n") + rename_result.error();
}
```
with
```C++
template <class M>
class Result
{
    // ...
    const bilingual_str& error() const
    {
        assert(!*this);
        return std::get<0>(m_variant);
    }
};

template <typename T>
bilingual_str ErrorString(const Result<T>& result)
{
    return result ? bilingual_str{} : result.error();
}
```

Code after:
        bilingual_str user_error = strprintf(_(
            "%s failed to validate the -assumeutxo snapshot state. "
            "This indicates a hardware problem, or a bug in the software, or a "
            "bad software modification that allowed an invalid snapshot to be "
            "loaded. As a result of this, the node will shut down and stop using any "
            "state that was built on the snapshot, resetting the chain height "
            "from %d to %d. On the next "
            "restart, the node will resume syncing from %d "
            "without using any snapshot data. "
            "Please report this incident to %s, including how you obtained the snapshot. "
            "The invalid snapshot chainstate will be left on disk in case it is "
            "helpful in diagnosing the issue that caused this error."),
            CLIENT_NAME, snapshot_tip_height, snapshot_base_height, snapshot_base_height, CLIENT_BUGREPORT
        );

        LogError("[snapshot] !!! %s\n", user_error.original);
        LogError("[snapshot] deleting snapshot, reverting to validated chain, and stopping node\n");

        m_active_chainstate = m_ibd_chainstate.get();
        m_snapshot_chainstate->m_disabled = true;
        assert(!this->IsUsable(m_snapshot_chainstate.get()));
        assert(this->IsUsable(m_ibd_chainstate.get()));

        auto rename_result = m_snapshot_chainstate->InvalidateCoinsDBOnDisk();
        if (!rename_result) {
            user_error += Untranslated("\n") + util::ErrorString(rename_result);
        }

        GetNotifications().fatalError(user_error);
    };

    if (index_new.GetBlockHash() != snapshot_blockhash) {
        LogPrintf("[snapshot] supposed base block %s does not match the "
          "snapshot base block %s (height %d). Snapshot is not valid.\n",
          index_new.ToString(), snapshot_blockhash.ToString(), snapshot_base_height);
        handle_invalid_snapshot();
        return SnapshotCompletionResult::BASE_BLOCKHASH_MISMATCH;
    }

    assert(index_new.nHeight == snapshot_base_height);

    int curr_height = m_ibd_chainstate->m_chain.Height();

    assert(snapshot_base_height == curr_height);
    assert(snapshot_base_height == index_new.nHeight);
    assert(this->IsUsable(m_snapshot_chainstate.get()));
    assert(this->GetAll().size() == 2);

    CCoinsViewDB& ibd_coins_db = m_ibd_chainstate->CoinsDB();
    m_ibd_chainstate->ForceFlushStateToDisk();