diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp index 0b7af376eff..944a2a521b4 100644 --- a/src/test/coins_tests.cpp +++ b/src/test/coins_tests.cpp @@ -1059,6 +1059,69 @@ BOOST_FIXTURE_TEST_CASE(ccoins_flush_behavior, FlushTest) } } +BOOST_AUTO_TEST_CASE(ccoins_cache_behavior) +{ + class CCoinsViewSpy final : public CCoinsView + { + public: + const COutPoint expected; + mutable size_t getcoin_calls{0}; + bool allow_getcoin{true}; + + explicit CCoinsViewSpy(const COutPoint& out) : expected{out} {} + + std::optional GetCoin(const COutPoint& out) const override + { + BOOST_CHECK(out == expected); + if (!allow_getcoin) { + BOOST_FAIL("Base GetCoin should not be called when input is cached"); + return std::nullopt; + } + ++getcoin_calls; + return Coin{CTxOut{1, CScript{}}, 1, false}; + } + + bool HaveCoin(const COutPoint& out) const override + { + BOOST_CHECK(out == expected); + BOOST_FAIL("Base HaveCoin should never be called"); + return false; + } + }; + + const COutPoint prevout{Txid::FromUint256(m_rng.rand256()), 0}; + CCoinsViewSpy base{prevout}; + CCoinsViewCache cache{&base}; + + CMutableTransaction mtx; + mtx.vin.emplace_back(prevout); + const CTransaction tx{mtx}; + BOOST_CHECK(!tx.IsCoinBase()); + + BOOST_CHECK(cache.HaveInputs(tx)); + BOOST_CHECK_EQUAL(base.getcoin_calls, 1); + + base.allow_getcoin = false; + BOOST_CHECK(cache.HaveInputs(tx)); + BOOST_CHECK(cache.GetCoin(prevout)); + BOOST_CHECK(!cache.AccessCoin(prevout).IsSpent()); + BOOST_CHECK(cache.HaveCoin(prevout)); + BOOST_CHECK_EQUAL(base.getcoin_calls, 1); + + const COutPoint prefilled_prevout{Txid::FromUint256(m_rng.rand256()), 0}; + cache.AddCoin(prefilled_prevout, Coin{CTxOut{1, CScript{}}, 1, false}, /*possible_overwrite=*/false); + BOOST_CHECK(cache.HaveCoinInCache(prefilled_prevout)); + + CMutableTransaction prefilled_mtx; + prefilled_mtx.vin.emplace_back(prefilled_prevout); + const CTransaction prefilled_tx{prefilled_mtx}; + BOOST_CHECK(!prefilled_tx.IsCoinBase()); + BOOST_CHECK(cache.HaveInputs(prefilled_tx)); + + BOOST_CHECK(cache.SpendCoin(prevout)); + BOOST_CHECK(!cache.HaveCoinInCache(prevout)); +} + BOOST_AUTO_TEST_CASE(coins_resource_is_used) { CCoinsMapMemoryResource resource;