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()