Bitcoin Core  27.99.0
P2P Digital Currency
validation_flush_tests.cpp
Go to the documentation of this file.
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>
9 #include <validation.h>
10 
11 #include <boost/test/unit_test.hpp>
12 
13 BOOST_FIXTURE_TEST_SUITE(validation_flush_tests, TestingSetup)
14 
15 BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
21 {
22  Chainstate& chainstate{m_node.chainman->ActiveChainstate()};
23 
24  constexpr bool is_64_bit = sizeof(void*) == 8;
25 
26  LOCK(::cs_main);
27  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  constexpr unsigned int COIN_SIZE = is_64_bit ? 80 : 64;
34 
35  auto print_view_mem_usage = [](CCoinsViewCache& view) {
36  BOOST_TEST_MESSAGE("CCoinsViewCache memory usage: " << view.DynamicMemoryUsage());
37  };
38 
39  // PoolResource defaults to 256 KiB that will be allocated, so we'll take that and make it a bit larger.
40  constexpr size_t MAX_COINS_CACHE_BYTES = 262144 + 512;
41 
42  // Without any coins in the cache, we shouldn't need to flush.
43  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  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  for (int i{0}; i < 1000; ++i) {
53  const COutPoint res = AddTestCoin(view);
54  BOOST_CHECK_EQUAL(view.AccessCoin(res).DynamicMemoryUsage(), COIN_SIZE);
55  }
56 
58  chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes=*/0),
60 
61  BOOST_TEST_MESSAGE("Exiting cache flush tests early due to unsupported arch");
62  return;
63  }
64 
65  print_view_mem_usage(view);
66  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  constexpr int COINS_UNTIL_CRITICAL{3};
73 
74  // no coin added, so we have plenty of space left.
76  chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes*/ 0),
78 
79  for (int i{0}; i < COINS_UNTIL_CRITICAL; ++i) {
80  const COutPoint res = AddTestCoin(view);
81  print_view_mem_usage(view);
82  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
87  chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes=*/ 0),
89  }
90 
91  // Adding some additional coins will push us over the edge to CRITICAL.
92  for (int i{0}; i < 4; ++i) {
93  AddTestCoin(view);
94  print_view_mem_usage(view);
95  if (chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes=*/0) ==
97  break;
98  }
99  }
100 
102  chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes=*/0),
104 
105  // Passing non-zero max mempool usage (512 KiB) should allow us more headroom.
107  chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes=*/ 1 << 19),
109 
110  for (int i{0}; i < 3; ++i) {
111  AddTestCoin(view);
112  print_view_mem_usage(view);
114  chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes=*/ 1 << 19),
116  }
117 
118  // Adding another coin with the additional mempool room will put us >90%
119  // but not yet critical.
120  AddTestCoin(view);
121  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  float usage_percentage = (float)view.DynamicMemoryUsage() / (MAX_COINS_CACHE_BYTES + (1 << 10));
126  BOOST_TEST_MESSAGE("CoinsTip usage percentage: " << usage_percentage);
127  BOOST_CHECK(usage_percentage >= 0.9);
128  BOOST_CHECK(usage_percentage < 1);
130  chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, /*max_mempool_size_bytes*/ 1 << 10), // 1024
132  }
133 
134  // Using the default max_* values permits way more coins to be added.
135  for (int i{0}; i < 1000; ++i) {
136  AddTestCoin(view);
138  chainstate.GetCoinsCacheSizeState(),
140  }
141 
142  // Flushing the view does take us back to OK because ReallocateCache() is called
143 
145  chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, 0),
147 
148  view.SetBestBlock(InsecureRand256());
149  BOOST_CHECK(view.Flush());
150  print_view_mem_usage(view);
151 
153  chainstate.GetCoinsCacheSizeState(MAX_COINS_CACHE_BYTES, 0),
155 }
156 
node::NodeContext m_node
Definition: bitcoin-gui.cpp:37
CCoinsView that adds a memory cache for transactions to another CCoinsView.
Definition: coins.h:229
An outpoint - a combination of a transaction hash and an index n into its vout.
Definition: transaction.h:29
Chainstate stores and provides an API to update our local knowledge of the current best chain.
Definition: validation.h:491
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate.
Definition: cs_main.cpp:8
BOOST_AUTO_TEST_SUITE_END()
#define BOOST_CHECK_EQUAL(v1, v2)
Definition: object.cpp:18
#define BOOST_CHECK(expr)
Definition: object.cpp:17
Testing setup that configures a complete environment.
Definition: setup_common.h:83
std::unique_ptr< ChainstateManager > chainman
Definition: context.h:62
#define LOCK(cs)
Definition: sync.h:257
COutPoint AddTestCoin(CCoinsViewCache &coins_view)
Create a Coin with DynamicMemoryUsage of 80 bytes and add it to the given view.
Definition: coins.cpp:16
static uint256 InsecureRand256()
Definition: random.h:50
@ LARGE
The cache is at >= 90% capacity.
@ CRITICAL
The coins cache is in immediate need of a flush.
BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
Test utilities for detecting when we need to flush the coins cache based on estimated memory usage.