Bitcoin Core 22.99.0
P2P Digital Currency
validation_chainstatemanager_tests.cpp
Go to the documentation of this file.
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 <chainparams.h>
8#include <random.h>
9#include <rpc/blockchain.h>
10#include <sync.h>
13#include <uint256.h>
14#include <validation.h>
15#include <validationinterface.h>
16
17#include <tinyformat.h>
18
19#include <vector>
20
21#include <boost/test/unit_test.hpp>
22
23BOOST_FIXTURE_TEST_SUITE(validation_chainstatemanager_tests, ChainTestingSetup)
24
25
28BOOST_AUTO_TEST_CASE(chainstatemanager)
29{
31 CTxMemPool& mempool = *m_node.mempool;
32
33 std::vector<CChainState*> chainstates;
34
35 BOOST_CHECK(!manager.SnapshotBlockhash().has_value());
36
37 // Create a legacy (IBD) chainstate.
38 //
39 CChainState& c1 = WITH_LOCK(::cs_main, return manager.InitializeChainstate(&mempool));
40 chainstates.push_back(&c1);
41 c1.InitCoinsDB(
42 /* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
43 WITH_LOCK(::cs_main, c1.InitCoinsCache(1 << 23));
44
45 BOOST_CHECK(!manager.IsSnapshotActive());
47 auto all = manager.GetAll();
48 BOOST_CHECK_EQUAL_COLLECTIONS(all.begin(), all.end(), chainstates.begin(), chainstates.end());
49
50 auto& active_chain = manager.ActiveChain();
51 BOOST_CHECK_EQUAL(&active_chain, &c1.m_chain);
52
53 BOOST_CHECK_EQUAL(manager.ActiveHeight(), -1);
54
55 auto active_tip = manager.ActiveTip();
56 auto exp_tip = c1.m_chain.Tip();
57 BOOST_CHECK_EQUAL(active_tip, exp_tip);
58
59 BOOST_CHECK(!manager.SnapshotBlockhash().has_value());
60
61 // Create a snapshot-based chainstate.
62 //
63 const uint256 snapshot_blockhash = GetRandHash();
64 CChainState& c2 = WITH_LOCK(::cs_main, return manager.InitializeChainstate(
65 &mempool, snapshot_blockhash));
66 chainstates.push_back(&c2);
67
68 BOOST_CHECK_EQUAL(manager.SnapshotBlockhash().value(), snapshot_blockhash);
69
70 c2.InitCoinsDB(
71 /* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
72 WITH_LOCK(::cs_main, c2.InitCoinsCache(1 << 23));
73 // Unlike c1, which doesn't have any blocks. Gets us different tip, height.
76 BOOST_CHECK(c2.ActivateBestChain(_, nullptr));
77
80 BOOST_CHECK_EQUAL(&c2, &manager.ActiveChainstate());
81 BOOST_CHECK(&c1 != &manager.ActiveChainstate());
82 auto all2 = manager.GetAll();
83 BOOST_CHECK_EQUAL_COLLECTIONS(all2.begin(), all2.end(), chainstates.begin(), chainstates.end());
84
85 auto& active_chain2 = manager.ActiveChain();
86 BOOST_CHECK_EQUAL(&active_chain2, &c2.m_chain);
87
88 BOOST_CHECK_EQUAL(manager.ActiveHeight(), 0);
89
90 auto active_tip2 = manager.ActiveTip();
91 auto exp_tip2 = c2.m_chain.Tip();
92 BOOST_CHECK_EQUAL(active_tip2, exp_tip2);
93
94 // Ensure that these pointers actually correspond to different
95 // CCoinsViewCache instances.
96 BOOST_CHECK(exp_tip != exp_tip2);
97
98 // Let scheduler events finish running to avoid accessing memory that is going to be unloaded
100
101 WITH_LOCK(::cs_main, manager.Unload());
102}
103
105BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches)
106{
108 CTxMemPool& mempool = *m_node.mempool;
109
110 size_t max_cache = 10000;
111 manager.m_total_coinsdb_cache = max_cache;
112 manager.m_total_coinstip_cache = max_cache;
113
114 std::vector<CChainState*> chainstates;
115
116 // Create a legacy (IBD) chainstate.
117 //
118 CChainState& c1 = WITH_LOCK(cs_main, return manager.InitializeChainstate(&mempool));
119 chainstates.push_back(&c1);
120 c1.InitCoinsDB(
121 /* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
122
123 {
124 LOCK(::cs_main);
125 c1.InitCoinsCache(1 << 23);
126 BOOST_REQUIRE(c1.LoadGenesisBlock());
128 manager.MaybeRebalanceCaches();
129 }
130
133
134 // Create a snapshot-based chainstate.
135 //
136 CChainState& c2 = WITH_LOCK(cs_main, return manager.InitializeChainstate(&mempool, GetRandHash()));
137 chainstates.push_back(&c2);
138 c2.InitCoinsDB(
139 /* cache_size_bytes */ 1 << 23, /* in_memory */ true, /* should_wipe */ false);
140
141 {
142 LOCK(::cs_main);
143 c2.InitCoinsCache(1 << 23);
144 BOOST_REQUIRE(c2.LoadGenesisBlock());
146 manager.MaybeRebalanceCaches();
147 }
148
149 // Since both chainstates are considered to be in initial block download,
150 // the snapshot chainstate should take priority.
151 BOOST_CHECK_CLOSE(c1.m_coinstip_cache_size_bytes, max_cache * 0.05, 1);
152 BOOST_CHECK_CLOSE(c1.m_coinsdb_cache_size_bytes, max_cache * 0.05, 1);
153 BOOST_CHECK_CLOSE(c2.m_coinstip_cache_size_bytes, max_cache * 0.95, 1);
154 BOOST_CHECK_CLOSE(c2.m_coinsdb_cache_size_bytes, max_cache * 0.95, 1);
155}
156
158BOOST_FIXTURE_TEST_CASE(chainstatemanager_activate_snapshot, TestChain100Setup)
159{
161
162 size_t initial_size;
163 size_t initial_total_coins{100};
164
165 // Make some initial assertions about the contents of the chainstate.
166 {
167 LOCK(::cs_main);
168 CCoinsViewCache& ibd_coinscache = chainman.ActiveChainstate().CoinsTip();
169 initial_size = ibd_coinscache.GetCacheSize();
170 size_t total_coins{0};
171
172 for (CTransactionRef& txn : m_coinbase_txns) {
173 COutPoint op{txn->GetHash(), 0};
174 BOOST_CHECK(ibd_coinscache.HaveCoin(op));
175 total_coins++;
176 }
177
178 BOOST_CHECK_EQUAL(total_coins, initial_total_coins);
179 BOOST_CHECK_EQUAL(initial_size, initial_total_coins);
180 }
181
182 // Snapshot should refuse to load at this height.
183 BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(m_node, m_path_root));
185 BOOST_CHECK(!chainman.SnapshotBlockhash());
186
187 // Mine 10 more blocks, putting at us height 110 where a valid assumeutxo value can
188 // be found.
189 constexpr int snapshot_height = 110;
190 mineBlocks(10);
191 initial_size += 10;
192 initial_total_coins += 10;
193
194 // Should not load malleated snapshots
195 BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(
196 m_node, m_path_root, [](CAutoFile& auto_infile, SnapshotMetadata& metadata) {
197 // A UTXO is missing but count is correct
198 metadata.m_coins_count -= 1;
199
200 COutPoint outpoint;
201 Coin coin;
202
203 auto_infile >> outpoint;
204 auto_infile >> coin;
205 }));
206 BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(
207 m_node, m_path_root, [](CAutoFile& auto_infile, SnapshotMetadata& metadata) {
208 // Coins count is larger than coins in file
209 metadata.m_coins_count += 1;
210 }));
211 BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(
212 m_node, m_path_root, [](CAutoFile& auto_infile, SnapshotMetadata& metadata) {
213 // Coins count is smaller than coins in file
214 metadata.m_coins_count -= 1;
215 }));
216 BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(
217 m_node, m_path_root, [](CAutoFile& auto_infile, SnapshotMetadata& metadata) {
218 // Wrong hash
220 }));
221 BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(
222 m_node, m_path_root, [](CAutoFile& auto_infile, SnapshotMetadata& metadata) {
223 // Wrong hash
225 }));
226
227 BOOST_REQUIRE(CreateAndActivateUTXOSnapshot(m_node, m_path_root));
228
229 // Ensure our active chain is the snapshot chainstate.
233 *chainman.SnapshotBlockhash());
234
235 const AssumeutxoData& au_data = *ExpectedAssumeutxo(snapshot_height, ::Params());
236 const CBlockIndex* tip = chainman.ActiveTip();
237
238 BOOST_CHECK_EQUAL(tip->nChainTx, au_data.nChainTx);
239
240 // To be checked against later when we try loading a subsequent snapshot.
241 uint256 loaded_snapshot_blockhash{*chainman.SnapshotBlockhash()};
242
243 // Make some assertions about the both chainstates. These checks ensure the
244 // legacy chainstate hasn't changed and that the newly created chainstate
245 // reflects the expected content.
246 {
247 LOCK(::cs_main);
248 int chains_tested{0};
249
250 for (CChainState* chainstate : chainman.GetAll()) {
251 BOOST_TEST_MESSAGE("Checking coins in " << chainstate->ToString());
252 CCoinsViewCache& coinscache = chainstate->CoinsTip();
253
254 // Both caches will be empty initially.
255 BOOST_CHECK_EQUAL((unsigned int)0, coinscache.GetCacheSize());
256
257 size_t total_coins{0};
258
259 for (CTransactionRef& txn : m_coinbase_txns) {
260 COutPoint op{txn->GetHash(), 0};
261 BOOST_CHECK(coinscache.HaveCoin(op));
262 total_coins++;
263 }
264
265 BOOST_CHECK_EQUAL(initial_size , coinscache.GetCacheSize());
266 BOOST_CHECK_EQUAL(total_coins, initial_total_coins);
267 chains_tested++;
268 }
269
270 BOOST_CHECK_EQUAL(chains_tested, 2);
271 }
272
273 // Mine some new blocks on top of the activated snapshot chainstate.
274 constexpr size_t new_coins{100};
275 mineBlocks(new_coins); // Defined in TestChain100Setup.
276
277 {
278 LOCK(::cs_main);
279 size_t coins_in_active{0};
280 size_t coins_in_background{0};
281 size_t coins_missing_from_background{0};
282
283 for (CChainState* chainstate : chainman.GetAll()) {
284 BOOST_TEST_MESSAGE("Checking coins in " << chainstate->ToString());
285 CCoinsViewCache& coinscache = chainstate->CoinsTip();
286 bool is_background = chainstate != &chainman.ActiveChainstate();
287
288 for (CTransactionRef& txn : m_coinbase_txns) {
289 COutPoint op{txn->GetHash(), 0};
290 if (coinscache.HaveCoin(op)) {
291 (is_background ? coins_in_background : coins_in_active)++;
292 } else if (is_background) {
293 coins_missing_from_background++;
294 }
295 }
296 }
297
298 BOOST_CHECK_EQUAL(coins_in_active, initial_total_coins + new_coins);
299 BOOST_CHECK_EQUAL(coins_in_background, initial_total_coins);
300 BOOST_CHECK_EQUAL(coins_missing_from_background, new_coins);
301 }
302
303 // Snapshot should refuse to load after one has already loaded.
304 BOOST_REQUIRE(!CreateAndActivateUTXOSnapshot(m_node, m_path_root));
305
306 // Snapshot blockhash should be unchanged.
309 loaded_snapshot_blockhash);
310}
311
NodeContext m_node
Definition: bitcoin-gui.cpp:36
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate.
Definition: validation.cpp:118
const CChainParams & Params()
Return the currently selected parameters.
static bool CreateAndActivateUTXOSnapshot(NodeContext &node, const fs::path root, F malleation=NoMalleation)
Create and activate a UTXO snapshot, optionally providing a function to malleate the snapshot.
Definition: chainstate.h:27
#define Assert(val)
Identity function.
Definition: check.h:57
Non-refcounted RAII wrapper for FILE*.
Definition: streams.h:565
The block chain is a tree shaped structure starting with the genesis block at the root,...
Definition: chain.h:146
unsigned int nChainTx
(memory only) Number of transactions in the chain up to and including this block.
Definition: chain.h:187
CBlockIndex * Tip() const
Returns the index entry for the tip of this chain, or nullptr if none.
Definition: chain.h:421
CChainState stores and provides an API to update our local knowledge of the current best chain.
Definition: validation.h:544
bool ActivateBestChain(BlockValidationState &state, std::shared_ptr< const CBlock > pblock=nullptr) LOCKS_EXCLUDED(cs_main)
Find the best known block, and make it the tip of the block chain.
CCoinsViewCache & CoinsTip() EXCLUSIVE_LOCKS_REQUIRED(cs_main)
Definition: validation.h:638
const std::optional< uint256 > m_from_snapshot_blockhash
The blockhash which is the base of the snapshot this chainstate was created from.
Definition: validation.h:627
size_t m_coinsdb_cache_size_bytes
The cache size of the on-disk coins view.
Definition: validation.h:661
void InitCoinsDB(size_t cache_size_bytes, bool in_memory, bool should_wipe, std::string leveldb_name="chainstate")
Initialize the CoinsViews UTXO set database management data structures.
CChain m_chain
The current chain of blockheaders we consult and build on.
Definition: validation.h:620
bool LoadGenesisBlock()
Ensures we have a genesis block in the block tree, possibly writing one to disk.
size_t m_coinstip_cache_size_bytes
The cache size of the in-memory coins view.
Definition: validation.h:664
CCoinsView that adds a memory cache for transactions to another CCoinsView.
Definition: coins.h:214
unsigned int GetCacheSize() const
Calculate the size of the cache (in number of transaction outputs)
Definition: coins.cpp:238
void SetBestBlock(const uint256 &hashBlock)
Definition: coins.cpp:162
bool HaveCoin(const COutPoint &outpoint) const override
Just check whether a given outpoint is unspent.
Definition: coins.cpp:146
An outpoint - a combination of a transaction hash and an index n into its vout.
Definition: transaction.h:27
CTxMemPool stores valid-according-to-the-current-best-chain transactions that may be included in the ...
Definition: txmempool.h:424
Provides an interface for creating and interacting with one or two chainstates: an IBD chainstate gen...
Definition: validation.h:847
int ActiveHeight() const
Definition: validation.h:946
int64_t m_total_coinstip_cache
The total number of bytes available for us to use across all in-memory coins caches.
Definition: validation.h:906
int64_t m_total_coinsdb_cache
The total number of bytes available for us to use across all leveldb coins databases.
Definition: validation.h:910
CChainState &InitializeChainstate(CTxMemPool *mempool, const std::optional< uint256 > &snapshot_blockhash=std::nullopt) LIFETIMEBOUND EXCLUSIVE_LOCKS_REQUIRED(std::vector< CChainState * GetAll)()
Instantiate a new chainstate and assign it based upon whether it is from a snapshot.
Definition: validation.h:925
CChainState & ActiveChainstate() const
The most-work chain.
bool IsSnapshotActive() const
std::optional< uint256 > SnapshotBlockhash() const
CChain & ActiveChain() const
Definition: validation.h:945
CBlockIndex * ActiveTip() const
Definition: validation.h:947
bool IsSnapshotValidated() const
Is there a snapshot in use and has it been fully validated?
Definition: validation.h:961
A UTXO entry.
Definition: coins.h:31
Metadata describing a serialized version of a UTXO set from which an assumeutxo CChainState can be co...
Definition: utxo_snapshot.h:15
uint256 m_base_blockhash
The hash of the block that reflects the tip of the chain for the UTXO set contained in this snapshot.
Definition: utxo_snapshot.h:19
uint64_t m_coins_count
The number of coins in the UTXO set contained in this snapshot.
Definition: utxo_snapshot.h:23
256-bit opaque blob.
Definition: uint256.h:124
static const uint256 ONE
Definition: uint256.h:130
static const uint256 ZERO
Definition: uint256.h:129
BOOST_AUTO_TEST_SUITE_END()
#define BOOST_FIXTURE_TEST_SUITE(a, b)
Definition: object.cpp:14
#define BOOST_CHECK_EQUAL(v1, v2)
Definition: object.cpp:18
#define BOOST_CHECK(expr)
Definition: object.cpp:17
std::shared_ptr< const CTransaction > CTransactionRef
Definition: transaction.h:386
uint256 GetRandHash() noexcept
Definition: random.cpp:601
static uint256 InsecureRand256()
Definition: setup_common.h:66
Holds configuration for use during UTXO snapshot load and validation.
Definition: chainparams.h:40
const unsigned int nChainTx
Used to populate the nChainTx value, which is used during BlockManager::LoadBlockIndex().
Definition: chainparams.h:48
Testing setup that performs all steps up until right before ChainstateManager gets initialized.
Definition: setup_common.h:91
std::unique_ptr< ChainstateManager > chainman
Definition: context.h:47
std::unique_ptr< CTxMemPool > mempool
Definition: context.h:44
Testing fixture that pre-creates a 100-block REGTEST-mode block chain.
Definition: setup_common.h:116
#define LOCK(cs)
Definition: sync.h:226
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Definition: sync.h:270
bilingual_str _(const char *psz)
Translation function.
Definition: translation.h:63
const AssumeutxoData * ExpectedAssumeutxo(const int height, const CChainParams &chainparams)
Return the expected assumeutxo value for a given height, if one exists.
BOOST_FIXTURE_TEST_CASE(chainstatemanager_activate_snapshot, TestChain100Setup)
Test basic snapshot activation.
BOOST_AUTO_TEST_CASE(chainstatemanager)
Basic tests for ChainstateManager.
void SyncWithValidationInterfaceQueue()
This is a synonym for the following, which asserts certain locks are not held: std::promise<void> pro...