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

Code before:

    BOOST_CHECK(all_caches[1]->SpendCoin(outp));
    flush_all(/*erase*/ false);
    BOOST_CHECK(!base.HaveCoin(outp));
    BOOST_CHECK(!all_caches[0]->HaveCoin(outp));
    BOOST_CHECK(!all_caches[1]->HaveCoin(outp));

    flush_all(/*erase*/ true); // Erase all cache content.

    // --- Bonus check 2: ensure that a FRESH coin is deleted by Sync()
    //
    txid = InsecureRand256();
    outp = COutPoint(txid, 0);
    coin = MakeCoin();
    BOOST_CHECK(!base.HaveCoin(outp));
    BOOST_CHECK(!all_caches[0]->HaveCoin(outp));
    BOOST_CHECK(!all_caches[1]->HaveCoin(outp));

    // Add and spend from same cache without flushing.
    all_caches[0]->AddCoin(outp, std::move(coin), false);

    // Coin should be FRESH in the cache.
    GetCoinsMapEntry(all_caches[0]->map(), value, flags, outp);
    BOOST_CHECK_EQUAL(value, coin.out.nValue);
    BOOST_CHECK_EQUAL(flags, DIRTY|FRESH);

    // Base shouldn't have seen coin.
    BOOST_CHECK(!base.HaveCoin(outp));

    BOOST_CHECK(all_caches[0]->SpendCoin(outp));
    all_caches[0]->Sync();

    // Ensure there is no sign of the coin after spend/flush.
    GetCoinsMapEntry(all_caches[0]->map(), value, flags, outp);
    BOOST_CHECK_EQUAL(value, ABSENT);
    BOOST_CHECK_EQUAL(flags, NO_ENTRY);
    BOOST_CHECK(!all_caches[0]->HaveCoinInCache(outp));
    BOOST_CHECK(!base.HaveCoin(outp));
}

BOOST_AUTO_TEST_CASE(ccoins_flush_behavior)
{
    // Create two in-memory caches atop a leveldb view.
    CCoinsViewDB base{"test", /*nCacheSize*/ 1 << 23, /*fMemory*/ true, /*fWipe*/ false};
    std::vector<CCoinsViewCacheTest*> caches;
    caches.push_back(new CCoinsViewCacheTest(&base));
    caches.push_back(new CCoinsViewCacheTest(caches.back()));

    for (CCoinsViewCacheTest* view : caches) {
        TestFlushBehavior(view, base, caches, /*do_erasing_flush*/ false);
        TestFlushBehavior(view, base, caches, /*do_erasing_flush*/ true);

Review comment (suggestion):
I think this should say "ensure that a FRESH spent coin is deleted by Sync()". If the coin is FRESH but not spent, then a `Sync()` won't delete it.

Code after:
    txid = InsecureRand256();
    outp = COutPoint(txid, 0);
    coin = MakeCoin();
    BOOST_CHECK(!base.HaveCoin(outp));
    BOOST_CHECK(!all_caches[0]->HaveCoin(outp));
    BOOST_CHECK(!all_caches[1]->HaveCoin(outp));

    all_caches[0]->AddCoin(outp, std::move(coin), false);
    all_caches[0]->Sync();
    BOOST_CHECK(base.HaveCoin(outp));
    BOOST_CHECK(all_caches[0]->HaveCoin(outp));
    BOOST_CHECK(!all_caches[1]->HaveCoinInCache(outp));

    BOOST_CHECK(all_caches[1]->SpendCoin(outp));
    flush_all(/*erase=*/ false);
    BOOST_CHECK(!base.HaveCoin(outp));
    BOOST_CHECK(!all_caches[0]->HaveCoin(outp));
    BOOST_CHECK(!all_caches[1]->HaveCoin(outp));

    flush_all(/*erase=*/ true); // Erase all cache content.

    // --- Bonus check 2: ensure that a FRESH, spent coin is deleted by Sync()
    //
    txid = InsecureRand256();
    outp = COutPoint(txid, 0);
    coin = MakeCoin();
    CAmount coin_val = coin.out.nValue;
    BOOST_CHECK(!base.HaveCoin(outp));
    BOOST_CHECK(!all_caches[0]->HaveCoin(outp));
    BOOST_CHECK(!all_caches[1]->HaveCoin(outp));

    // Add and spend from same cache without flushing.
    all_caches[0]->AddCoin(outp, std::move(coin), false);

    // Coin should be FRESH in the cache.
    GetCoinsMapEntry(all_caches[0]->map(), value, flags, outp);
    BOOST_CHECK_EQUAL(value, coin_val);
    BOOST_CHECK_EQUAL(flags, DIRTY|FRESH);

    // Base shouldn't have seen coin.
    BOOST_CHECK(!base.HaveCoin(outp));

    BOOST_CHECK(all_caches[0]->SpendCoin(outp));
    all_caches[0]->Sync();

    // Ensure there is no sign of the coin after spend/flush.
    GetCoinsMapEntry(all_caches[0]->map(), value, flags, outp);
    BOOST_CHECK_EQUAL(value, ABSENT);
    BOOST_CHECK_EQUAL(flags, NO_ENTRY);
    BOOST_CHECK(!all_caches[0]->HaveCoinInCache(outp));
    BOOST_CHECK(!base.HaveCoin(outp));