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