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

Code before:
    }

    const AssumeutxoData& au_data = *maybe_au_data;

    // This work comparison is a duplicate check with the one performed later in
    // ActivateSnapshot(), but is done so that we avoid doing the long work of staging
    // a snapshot that isn't actually usable.
    if (WITH_LOCK(::cs_main, return !CBlockIndexWorkComparator()(ActiveTip(), snapshot_start_block))) {
        LogPrintf("[snapshot] activation failed - work does not exceed active chainstate\n");
        return false;
    }

    const uint64_t coins_count = metadata.m_coins_count;
    uint64_t coins_left = metadata.m_coins_count;

    LogPrintf("[snapshot] loading %d coins from snapshot %s\n", coins_left, base_blockhash.ToString());
    int64_t coins_processed{0};

    while (coins_left > 0) {
        try {
            Txid txid;
            coins_file >> txid;
            size_t size{0};
            size = ReadCompactSize(coins_file);

            if(size > coins_left) {
                LogPrintf("[snapshot] mismatch in coins count in snapshot metadata and actual snapshot data\n");
                return false;
            }

            for (size_t i = 0; i < size; i++) {
                COutPoint outpoint;
                Coin coin;
                outpoint.n = static_cast<uint32_t>(ReadCompactSize(coins_file));
                outpoint.hash = txid;
                coins_file >> coin;
                if (coin.nHeight > base_height ||
                    outpoint.n >= std::numeric_limits<decltype(outpoint.n)>::max() // Avoid integer wrap-around in coinstats.cpp:ApplyHash
                ) {
                    LogPrintf("[snapshot] bad snapshot data after deserializing %d coins\n",
                              coins_count - coins_left);
                    return false;
                }
                if (!MoneyRange(coin.out.nValue)) {
                    LogPrintf("[snapshot] bad snapshot data after deserializing %d coins - bad tx out value\n",
                              coins_count - coins_left);
                    return false;
                }
                coins_cache.EmplaceCoinInternalDANGER(std::move(outpoint), std::move(coin));

                --coins_left;

Review comment (suggestion):
spacing nit:
```suggestion
            if (size > coins_left) {
```

Code after:

    if (!this->PopulateAndValidateSnapshot(*snapshot_chainstate, coins_file, metadata)) {
        LOCK(::cs_main);
        return cleanup_bad_snapshot("population failed");
    }

    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("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("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.
    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 true;
}

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);