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

Code before:
    Assert(m_active_chainstate->m_mempool->size() == 0);
    Assert(!m_snapshot_chainstate->m_mempool);
    m_snapshot_chainstate->m_mempool = m_active_chainstate->m_mempool;
    m_active_chainstate->m_mempool = nullptr;
    m_active_chainstate = m_snapshot_chainstate.get();
    m_blockman.m_snapshot_height = this->GetSnapshotBaseHeight();

    LogPrintf("[snapshot] successfully activated snapshot %s\n", base_blockhash.ToString());
    LogPrintf("[snapshot] (%.2f MB)\n",
        m_snapshot_chainstate->CoinsTip().DynamicMemoryUsage() / (1000 * 1000));

    this->MaybeRebalanceCaches();
    return snapshot_start_block;
}

static void FlushSnapshotToDisk(CCoinsViewCache& coins_cache, bool snapshot_loaded)
{
    LOG_TIME_MILLIS_WITH_CATEGORY_MSG_ONCE(
        strprintf("%s (%.2f MB)",
                  snapshot_loaded ? "saving snapshot chainstate" : "flushing coins cache",
                  coins_cache.DynamicMemoryUsage() / (1000 * 1000)),
        BCLog::LogFlags::ALL);

    coins_cache.Flush();
}

struct StopHashingException : public std::exception
{
    const char* what() const noexcept override
    {
        return "ComputeUTXOStats interrupted.";
    }
};

static void SnapshotUTXOHashBreakpoint(const util::SignalInterrupt& interrupt)
{
    if (interrupt) throw StopHashingException();
}

util::Result<void> ChainstateManager::PopulateAndValidateSnapshot(
    Chainstate& snapshot_chainstate,
    AutoFile& coins_file,
    const SnapshotMetadata& metadata)
{
    // It's okay to release cs_main before we're done using `coins_cache` because we know
    // that nothing else will be referencing the newly created snapshot_chainstate yet.
    CCoinsViewCache& coins_cache = *WITH_LOCK(::cs_main, return &snapshot_chainstate.CoinsTip());

    uint256 base_blockhash = metadata.m_base_blockhash;

    CBlockIndex* snapshot_start_block = WITH_LOCK(::cs_main, return m_blockman.LookupBlockIndex(base_blockhash));

Review comment (bug):
fd188650f0aefced09fdb5c3d21c960791104774: Just for reference, everything in this call graph is untranslated, so if there was a translation, it would already be a bug, and this patch wouldn't fix that bug.

Code after:
        snapshot_chainstate->InitCoinsCache(
            static_cast<size_t>(current_coinstip_cache_size * SNAPSHOT_CACHE_PERC));
    }

    auto cleanup_bad_snapshot = [&](bilingual_str reason) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
        this->MaybeRebalanceCaches();

        // PopulateAndValidateSnapshot can return (in error) before the leveldb datadir
        // has been created, so only attempt removal if we got that far.
        if (auto snapshot_datadir = node::FindSnapshotChainstateDir(m_options.datadir)) {
            // We have to destruct leveldb::DB in order to release the db lock, otherwise
            // DestroyDB() (in DeleteCoinsDBFromDisk()) will fail. See `leveldb::~DBImpl()`.
            // Destructing the chainstate (and so resetting the coinsviews object) does this.
            snapshot_chainstate.reset();
            bool removed = DeleteCoinsDBFromDisk(*snapshot_datadir, /*is_snapshot=*/true);
            if (!removed) {
                GetNotifications().fatalError(strprintf(_("Failed to remove snapshot chainstate dir (%s). "
                    "Manually remove it before restarting.\n"), fs::PathToString(*snapshot_datadir)));
            }
        }
        return util::Error{std::move(reason)};
    };

    if (auto res{this->PopulateAndValidateSnapshot(*snapshot_chainstate, coins_file, metadata)}; !res) {
        LOCK(::cs_main);
        return cleanup_bad_snapshot(Untranslated(strprintf("Population failed: %s", util::ErrorString(res).original)));
    }

    LOCK(::cs_main);  // cs_main required for rest of snapshot activation.

    // Do a final check to ensure that the snapshot chainstate is actually a more
    // work chain than the active chainstate; a user could have loaded a snapshot
    // very late in the IBD process, and we wouldn't want to load a useless chainstate.
    if (!CBlockIndexWorkComparator()(ActiveTip(), snapshot_chainstate->m_chain.Tip())) {
        return cleanup_bad_snapshot(Untranslated("work does not exceed active chainstate"));
    }
    // If not in-memory, persist the base blockhash for use during subsequent
    // initialization.
    if (!in_memory) {
        if (!node::WriteSnapshotBaseBlockhash(*snapshot_chainstate)) {
            return cleanup_bad_snapshot(Untranslated("could not write base blockhash"));
        }
    }

    assert(!m_snapshot_chainstate);
    m_snapshot_chainstate.swap(snapshot_chainstate);
    const bool chaintip_loaded = m_snapshot_chainstate->LoadChainTip();
    assert(chaintip_loaded);

    // Transfer possession of the mempool to the snapshot chainstate.
    // Mempool is empty at this point because we're still in IBD.