LCOV - code coverage report
Current view: top level - src/test - validation_flush_tests.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 20 61 32.8 %
Date: 2026-06-25 07:23:43 Functions: 10 11 90.9 %

          Line data    Source code
       1             : // Copyright (c) 2019-2021 The Bitcoin Core developers
       2             : // Distributed under the MIT software license, see the accompanying
       3             : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
       4             : //
       5             : #include <evo/evodb.h>
       6             : #include <sync.h>
       7             : #include <test/util/coins.h>
       8             : #include <test/util/random.h>
       9             : #include <test/util/setup_common.h>
      10             : #include <validation.h>
      11             : 
      12             : #include <boost/test/unit_test.hpp>
      13             : 
      14         146 : BOOST_FIXTURE_TEST_SUITE(validation_flush_tests, TestingSetup)
      15             : 
      16             : //! Test utilities for detecting when we need to flush the coins cache based
      17             : //! on estimated memory usage.
      18             : //!
      19             : //! @sa CChainState::GetCoinsCacheSizeState()
      20             : //!
      21         149 : BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
      22             : {
      23           1 :     CChainState& chainstate{m_node.chainman->ActiveChainstate()};
      24             : 
      25           1 :     constexpr bool is_64_bit = sizeof(void*) == 8;
      26             : 
      27           1 :     LOCK(::cs_main);
      28           1 :     auto& view = chainstate.CoinsTip();
      29             : 
      30             :     // The number of bytes consumed by coin's heap data, i.e. CScript
      31             :     // (prevector<28, unsigned char>) when assigned 56 bytes of data per above.
      32             :     //
      33             :     // See also: Coin::DynamicMemoryUsage().
      34           1 :     constexpr unsigned int COIN_SIZE = is_64_bit ? 80 : 64;
      35             : 
      36           1 :     auto print_view_mem_usage = [](CCoinsViewCache& view) {
      37           0 :         BOOST_TEST_MESSAGE("CCoinsViewCache memory usage: " << view.DynamicMemoryUsage());
      38           0 :     };
      39             : 
      40             :     // PoolResource defaults to 256 KiB that will be allocated, so we'll take that and make it a bit larger.
      41           1 :     constexpr size_t MAX_COINS_CACHE_BYTES = 262144 + 512;
      42             : 
      43             :     // Without any coins in the cache, we shouldn't need to flush.
      44           1 :     BOOST_TEST(
      45             :         chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes=*/ 0) != CoinsCacheSizeState::CRITICAL);
      46             : 
      47             :     // If the initial memory allocations of cacheCoins don't match these common
      48             :     // cases, we can't really continue to make assertions about memory usage.
      49             :     // End the test early.
      50           1 :     if (view.DynamicMemoryUsage() != 32 && view.DynamicMemoryUsage() != 16) {
      51             :         // Add a bunch of coins to see that we at least flip over to CRITICAL.
      52             : 
      53        1001 :         for (int i{0}; i < 1000; ++i) {
      54        1000 :             const COutPoint res = AddTestCoin(view);
      55        1000 :             BOOST_CHECK_EQUAL(view.AccessCoin(res).DynamicMemoryUsage(), COIN_SIZE);
      56        1000 :         }
      57             : 
      58           1 :         BOOST_CHECK_EQUAL(
      59             :             chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes=*/0),
      60             :             CoinsCacheSizeState::CRITICAL);
      61             : 
      62           1 :         BOOST_TEST_MESSAGE("Exiting cache flush tests early due to unsupported arch");
      63           1 :         return;
      64             :     }
      65             : 
      66           0 :     print_view_mem_usage(view);
      67           0 :     BOOST_CHECK_EQUAL(view.DynamicMemoryUsage(), is_64_bit ? 32U : 16U);
      68             : 
      69             :     // We should be able to add COINS_UNTIL_CRITICAL coins to the cache before going CRITICAL.
      70             :     // This is contingent not only on the dynamic memory usage of the Coins
      71             :     // that we're adding (COIN_SIZE bytes per), but also on how much memory the
      72             :     // cacheCoins (unordered_map) preallocates.
      73           0 :     constexpr int COINS_UNTIL_CRITICAL{3};
      74             : 
      75             :     // no coin added, so we have plenty of space left.
      76           0 :     BOOST_CHECK_EQUAL(
      77             :         chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes=*/0),
      78             :         CoinsCacheSizeState::OK);
      79             : 
      80           0 :     for (int i{0}; i < COINS_UNTIL_CRITICAL; ++i) {
      81           0 :         const COutPoint res = AddTestCoin(view);
      82           0 :         print_view_mem_usage(view);
      83           0 :         BOOST_CHECK_EQUAL(view.AccessCoin(res).DynamicMemoryUsage(), COIN_SIZE);
      84             : 
      85             :         // adding first coin causes the MemoryResource to allocate one 256 KiB chunk of memory,
      86             :         // pushing us immediately over to LARGE
      87           0 :         BOOST_CHECK_EQUAL(
      88             :             chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes=*/ 0),
      89             :             CoinsCacheSizeState::LARGE);
      90           0 :     }
      91             : 
      92             :     // Adding some additional coins will push us over the edge to CRITICAL.
      93           0 :     for (int i{0}; i < 4; ++i) {
      94           0 :         AddTestCoin(view);
      95           0 :         print_view_mem_usage(view);
      96           0 :         if (chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes=*/0) ==
      97             :             CoinsCacheSizeState::CRITICAL) {
      98           0 :             break;
      99             :         }
     100           0 :     }
     101             : 
     102           0 :     BOOST_CHECK_EQUAL(
     103             :         chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes=*/0),
     104             :         CoinsCacheSizeState::CRITICAL);
     105             : 
     106             :     // Passing non-zero max mempool usage (512 KiB) should allow us more headroom.
     107           0 :     BOOST_CHECK_EQUAL(
     108             :         chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes=*/ 1 << 19),
     109             :         CoinsCacheSizeState::OK);
     110             : 
     111           0 :     for (int i{0}; i < 3; ++i) {
     112           0 :         AddTestCoin(view);
     113           0 :         print_view_mem_usage(view);
     114           0 :         BOOST_CHECK_EQUAL(
     115             :             chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes=*/ 1 << 19),
     116             :             CoinsCacheSizeState::OK);
     117           0 :     }
     118             : 
     119             :     // Adding another coin with the additional mempool room will put us >90%
     120             :     // but not yet critical.
     121           0 :     AddTestCoin(view);
     122           0 :     print_view_mem_usage(view);
     123             : 
     124             :     // Only perform these checks on 64 bit hosts; I haven't done the math for 32.
     125             :     if (is_64_bit) {
     126           0 :         float usage_percentage = (float)view.DynamicMemoryUsage() / (MAX_COINS_CACHE_BYTES + (1 << 10));
     127           0 :         BOOST_TEST_MESSAGE("CoinsTip usage percentage: " << usage_percentage);
     128           0 :         BOOST_CHECK(usage_percentage >= 0.9);
     129           0 :         BOOST_CHECK(usage_percentage < 1);
     130           0 :         BOOST_CHECK_EQUAL(
     131             :             chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes=*/1 << 10), // 1024
     132             :             CoinsCacheSizeState::LARGE);
     133             :     }
     134             : 
     135             :     // Using the default max_* values permits way more coins to be added.
     136           0 :     for (int i{0}; i < 1000; ++i) {
     137           0 :         AddTestCoin(view);
     138           0 :         BOOST_CHECK_EQUAL(
     139             :             chainstate.GetCoinsCacheSizeState(),
     140             :             CoinsCacheSizeState::OK);
     141           0 :     }
     142             : 
     143             :     // Flushing the view does take us back to OK because ReallocateCache() is called
     144             : 
     145           0 :     BOOST_CHECK_EQUAL(
     146             :         chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, 0),
     147             :         CoinsCacheSizeState::CRITICAL);
     148             : 
     149           0 :     view.SetBestBlock(InsecureRand256());
     150           0 :     BOOST_CHECK(view.Flush());
     151           0 :     print_view_mem_usage(view);
     152             : 
     153           0 :     BOOST_CHECK_EQUAL(
     154             :         chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, 0),
     155             :         CoinsCacheSizeState::OK);
     156           1 : }
     157             : 
     158         146 : BOOST_AUTO_TEST_SUITE_END()

Generated by: LCOV version 1.16