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