LCOV - code coverage report
Current view: top level - src/test - validation_flush_tests.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 32 69 46.4 %
Date: 2020-09-26 01:30:44 Functions: 11 12 91.7 %

          Line data    Source code
       1             : // Copyright (c) 2019-2020 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 <sync.h>
       6             : #include <test/util/setup_common.h>
       7             : #include <txmempool.h>
       8             : #include <validation.h>
       9             : 
      10             : #include <boost/test/unit_test.hpp>
      11             : 
      12          89 : BOOST_FIXTURE_TEST_SUITE(validation_flush_tests, BasicTestingSetup)
      13             : 
      14             : //! Test utilities for detecting when we need to flush the coins cache based
      15             : //! on estimated memory usage.
      16             : //!
      17             : //! @sa CChainState::GetCoinsCacheSizeState()
      18             : //!
      19          95 : BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
      20             : {
      21           1 :     CTxMemPool mempool;
      22           1 :     BlockManager blockman{};
      23           1 :     CChainState chainstate{mempool, blockman};
      24           1 :     chainstate.InitCoinsDB(/*cache_size_bytes*/ 1 << 10, /*in_memory*/ true, /*should_wipe*/ false);
      25           2 :     WITH_LOCK(::cs_main, chainstate.InitCoinsCache(1 << 10));
      26           1 :     CTxMemPool tx_pool{};
      27             : 
      28             :     constexpr bool is_64_bit = sizeof(void*) == 8;
      29             : 
      30           1 :     LOCK(::cs_main);
      31           1 :     auto& view = chainstate.CoinsTip();
      32             : 
      33             :     //! Create and add a Coin with DynamicMemoryUsage of 80 bytes to the given view.
      34        1001 :     auto add_coin = [](CCoinsViewCache& coins_view) -> COutPoint {
      35        1000 :         Coin newcoin;
      36        1000 :         uint256 txid = InsecureRand256();
      37        1000 :         COutPoint outp{txid, 0};
      38        1000 :         newcoin.nHeight = 1;
      39        1000 :         newcoin.out.nValue = InsecureRand32();
      40        1000 :         newcoin.out.scriptPubKey.assign((uint32_t)56, 1);
      41        1000 :         coins_view.AddCoin(outp, std::move(newcoin), false);
      42             : 
      43             :         return outp;
      44        1000 :     };
      45             : 
      46             :     // The number of bytes consumed by coin's heap data, i.e. CScript
      47             :     // (prevector<28, unsigned char>) when assigned 56 bytes of data per above.
      48             :     //
      49             :     // See also: Coin::DynamicMemoryUsage().
      50           1 :     constexpr unsigned int COIN_SIZE = is_64_bit ? 80 : 64;
      51             : 
      52           1 :     auto print_view_mem_usage = [](CCoinsViewCache& view) {
      53           0 :         BOOST_TEST_MESSAGE("CCoinsViewCache memory usage: " << view.DynamicMemoryUsage());
      54           0 :     };
      55             : 
      56             :     constexpr size_t MAX_COINS_CACHE_BYTES = 1024;
      57             : 
      58             :     // Without any coins in the cache, we shouldn't need to flush.
      59           1 :     BOOST_CHECK_EQUAL(
      60             :         chainstate.GetCoinsCacheSizeState(&tx_pool, MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes*/ 0),
      61             :         CoinsCacheSizeState::OK);
      62             : 
      63             :     // If the initial memory allocations of cacheCoins don't match these common
      64             :     // cases, we can't really continue to make assertions about memory usage.
      65             :     // End the test early.
      66           1 :     if (view.DynamicMemoryUsage() != 32 && view.DynamicMemoryUsage() != 16) {
      67             :         // Add a bunch of coins to see that we at least flip over to CRITICAL.
      68             : 
      69        1001 :         for (int i{0}; i < 1000; ++i) {
      70        1000 :             COutPoint res = add_coin(view);
      71        1000 :             BOOST_CHECK_EQUAL(view.AccessCoin(res).DynamicMemoryUsage(), COIN_SIZE);
      72        1000 :         }
      73             : 
      74           1 :         BOOST_CHECK_EQUAL(
      75             :             chainstate.GetCoinsCacheSizeState(&tx_pool, MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes*/ 0),
      76             :             CoinsCacheSizeState::CRITICAL);
      77             : 
      78           1 :         BOOST_TEST_MESSAGE("Exiting cache flush tests early due to unsupported arch");
      79           1 :         return;
      80             :     }
      81             : 
      82           0 :     print_view_mem_usage(view);
      83           0 :     BOOST_CHECK_EQUAL(view.DynamicMemoryUsage(), is_64_bit ? 32U : 16U);
      84             : 
      85             :     // We should be able to add COINS_UNTIL_CRITICAL coins to the cache before going CRITICAL.
      86             :     // This is contingent not only on the dynamic memory usage of the Coins
      87             :     // that we're adding (COIN_SIZE bytes per), but also on how much memory the
      88             :     // cacheCoins (unordered_map) preallocates.
      89             :     constexpr int COINS_UNTIL_CRITICAL{3};
      90             : 
      91           0 :     for (int i{0}; i < COINS_UNTIL_CRITICAL; ++i) {
      92           0 :         COutPoint res = add_coin(view);
      93           0 :         print_view_mem_usage(view);
      94           0 :         BOOST_CHECK_EQUAL(view.AccessCoin(res).DynamicMemoryUsage(), COIN_SIZE);
      95           0 :         BOOST_CHECK_EQUAL(
      96             :             chainstate.GetCoinsCacheSizeState(&tx_pool, MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes*/ 0),
      97             :             CoinsCacheSizeState::OK);
      98           0 :     }
      99             : 
     100             :     // Adding some additional coins will push us over the edge to CRITICAL.
     101           0 :     for (int i{0}; i < 4; ++i) {
     102           0 :         add_coin(view);
     103           0 :         print_view_mem_usage(view);
     104           0 :         if (chainstate.GetCoinsCacheSizeState(&tx_pool, MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes*/ 0) ==
     105             :             CoinsCacheSizeState::CRITICAL) {
     106           0 :             break;
     107             :         }
     108             :     }
     109             : 
     110           0 :     BOOST_CHECK_EQUAL(
     111             :         chainstate.GetCoinsCacheSizeState(&tx_pool, MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes*/ 0),
     112             :         CoinsCacheSizeState::CRITICAL);
     113             : 
     114             :     // Passing non-zero max mempool usage should allow us more headroom.
     115           0 :     BOOST_CHECK_EQUAL(
     116             :         chainstate.GetCoinsCacheSizeState(&tx_pool, MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes*/ 1 << 10),
     117             :         CoinsCacheSizeState::OK);
     118             : 
     119           0 :     for (int i{0}; i < 3; ++i) {
     120           0 :         add_coin(view);
     121           0 :         print_view_mem_usage(view);
     122           0 :         BOOST_CHECK_EQUAL(
     123             :             chainstate.GetCoinsCacheSizeState(&tx_pool, MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes*/ 1 << 10),
     124             :             CoinsCacheSizeState::OK);
     125             :     }
     126             : 
     127             :     // Adding another coin with the additional mempool room will put us >90%
     128             :     // but not yet critical.
     129           0 :     add_coin(view);
     130           0 :     print_view_mem_usage(view);
     131             : 
     132             :     // Only perform these checks on 64 bit hosts; I haven't done the math for 32.
     133             :     if (is_64_bit) {
     134           0 :         float usage_percentage = (float)view.DynamicMemoryUsage() / (MAX_COINS_CACHE_BYTES + (1 << 10));
     135           0 :         BOOST_TEST_MESSAGE("CoinsTip usage percentage: " << usage_percentage);
     136           0 :         BOOST_CHECK(usage_percentage >= 0.9);
     137           0 :         BOOST_CHECK(usage_percentage < 1);
     138           0 :         BOOST_CHECK_EQUAL(
     139             :             chainstate.GetCoinsCacheSizeState(&tx_pool, MAX_COINS_CACHE_BYTES, 1 << 10),
     140             :             CoinsCacheSizeState::LARGE);
     141           0 :     }
     142             : 
     143             :     // Using the default max_* values permits way more coins to be added.
     144           0 :     for (int i{0}; i < 1000; ++i) {
     145           0 :         add_coin(view);
     146           0 :         BOOST_CHECK_EQUAL(
     147             :             chainstate.GetCoinsCacheSizeState(&tx_pool),
     148             :             CoinsCacheSizeState::OK);
     149             :     }
     150             : 
     151             :     // Flushing the view doesn't take us back to OK because cacheCoins has
     152             :     // preallocated memory that doesn't get reclaimed even after flush.
     153             : 
     154           0 :     BOOST_CHECK_EQUAL(
     155             :         chainstate.GetCoinsCacheSizeState(&tx_pool, MAX_COINS_CACHE_BYTES, 0),
     156             :         CoinsCacheSizeState::CRITICAL);
     157             : 
     158           0 :     view.SetBestBlock(InsecureRand256());
     159           0 :     BOOST_CHECK(view.Flush());
     160           0 :     print_view_mem_usage(view);
     161             : 
     162           0 :     BOOST_CHECK_EQUAL(
     163             :         chainstate.GetCoinsCacheSizeState(&tx_pool, MAX_COINS_CACHE_BYTES, 0),
     164             :         CoinsCacheSizeState::CRITICAL);
     165           1 : }
     166             : 
     167          89 : BOOST_AUTO_TEST_SUITE_END()

Generated by: LCOV version 1.15