diff --git a/src/coins.cpp b/src/coins.cpp index 42e83dab8c3..090d36dd041 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -111,9 +111,12 @@ void CCoinsViewCache::AddCoin(const COutPoint &outpoint, Coin&& coin, bool possi } void CCoinsViewCache::EmplaceCoinInternalDANGER(COutPoint&& outpoint, Coin&& coin) { - cachedCoinsUsage += coin.DynamicMemoryUsage(); + const auto mem_usage{coin.DynamicMemoryUsage()}; auto [it, inserted] = cacheCoins.try_emplace(std::move(outpoint), std::move(coin)); - if (inserted) CCoinsCacheEntry::SetDirty(*it, m_sentinel); + if (inserted) { + CCoinsCacheEntry::SetDirty(*it, m_sentinel); + cachedCoinsUsage += mem_usage; + } } void AddCoins(CCoinsViewCache& cache, const CTransaction &tx, int nHeight, bool check_for_overwrite) { diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp index 46b1e2eb955..a1152d245f7 100644 --- a/src/test/coins_tests.cpp +++ b/src/test/coins_tests.cpp @@ -1103,4 +1103,22 @@ BOOST_AUTO_TEST_CASE(ccoins_addcoin_exception_keeps_usage_balanced) BOOST_CHECK(cache.AccessCoin(outpoint) == coin1); } +BOOST_AUTO_TEST_CASE(ccoins_emplace_duplicate_keeps_usage_balanced) +{ + CCoinsView root; + CCoinsViewCacheTest cache{&root}; + + const COutPoint outpoint{Txid::FromUint256(m_rng.rand256()), m_rng.rand32()}; + + const Coin coin1{CTxOut{m_rng.randrange(10), CScript{} << m_rng.randbytes(CScriptBase::STATIC_SIZE + 1)}, 1, false}; + cache.EmplaceCoinInternalDANGER(COutPoint{outpoint}, Coin{coin1}); + cache.SelfTest(); + + const Coin coin2{CTxOut{m_rng.randrange(20), CScript{} << m_rng.randbytes(CScriptBase::STATIC_SIZE + 2)}, 2, false}; + cache.EmplaceCoinInternalDANGER(COutPoint{outpoint}, Coin{coin2}); + cache.SelfTest(); + + BOOST_CHECK(cache.AccessCoin(outpoint) == coin1); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/src/test/fuzz/coins_view.cpp b/src/test/fuzz/coins_view.cpp index 2b3557fffed..c687065423a 100644 --- a/src/test/fuzz/coins_view.cpp +++ b/src/test/fuzz/coins_view.cpp @@ -61,12 +61,16 @@ void TestCoinsView(FuzzedDataProvider& fuzzed_data_provider, CCoinsView& backend } COutPoint outpoint{random_out_point}; Coin coin{random_coin}; - const bool possible_overwrite{fuzzed_data_provider.ConsumeBool()}; - try { - coins_view_cache.AddCoin(outpoint, std::move(coin), possible_overwrite); - } catch (const std::logic_error& e) { - assert(e.what() == std::string{"Attempted to overwrite an unspent coin (when possible_overwrite is false)"}); - assert(!possible_overwrite); + if (fuzzed_data_provider.ConsumeBool()) { + const bool possible_overwrite{fuzzed_data_provider.ConsumeBool()}; + try { + coins_view_cache.AddCoin(outpoint, std::move(coin), possible_overwrite); + } catch (const std::logic_error& e) { + assert(e.what() == std::string{"Attempted to overwrite an unspent coin (when possible_overwrite is false)"}); + assert(!possible_overwrite); + } + } else { + coins_view_cache.EmplaceCoinInternalDANGER(std::move(outpoint), std::move(coin)); } }, [&] {