Branch data Line data Source code
1 : : // Copyright (c) 2021-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 <node/chainstate.h>
6 : :
7 : : #include <arith_uint256.h>
8 : : #include <chain.h>
9 : : #include <coins.h>
10 : : #include <consensus/params.h>
11 : : #include <kernel/caches.h>
12 : : #include <logging.h>
13 : : #include <node/blockstorage.h>
14 : : #include <sync.h>
15 : : #include <threadsafety.h>
16 : : #include <tinyformat.h>
17 : : #include <txdb.h>
18 : : #include <uint256.h>
19 : : #include <util/fs.h>
20 : : #include <util/signalinterrupt.h>
21 : : #include <util/time.h>
22 : : #include <util/translation.h>
23 : : #include <validation.h>
24 : :
25 : : #include <algorithm>
26 : : #include <atomic>
27 : : #include <cassert>
28 : : #include <limits>
29 : : #include <memory>
30 : : #include <vector>
31 : :
32 : : using kernel::CacheSizes;
33 : :
34 : : namespace node {
35 : : // Complete initialization of chainstates after the initial call has been made
36 : : // to ChainstateManager::InitializeChainstate().
37 : 2041 : static ChainstateLoadResult CompleteChainstateInitialization(
38 : : ChainstateManager& chainman,
39 : : const CacheSizes& cache_sizes,
40 : : const ChainstateLoadOptions& options) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
41 : : {
42 : 2041 : auto& pblocktree{chainman.m_blockman.m_block_tree_db};
43 : : // new BlockTreeDB tries to delete the existing file, which
44 : : // fails if it's still open from the previous loop. Close it first:
45 [ + - ]: 2041 : pblocktree.reset();
46 : 2041 : try {
47 : 4082 : pblocktree = std::make_unique<BlockTreeDB>(DBParams{
48 [ + - + - : 6123 : .path = chainman.m_options.datadir / "blocks" / "index",
+ - ]
49 : 2041 : .cache_bytes = cache_sizes.block_tree_db,
50 : 2041 : .memory_only = options.block_tree_db_in_memory,
51 [ + - ]: 2041 : .wipe_data = options.wipe_block_tree_db,
52 : 2041 : .options = chainman.m_options.block_tree_db});
53 [ - - ]: 0 : } catch (dbwrapper_error& err) {
54 [ - - ]: 0 : LogError("%s\n", err.what());
55 [ - - ]: 0 : return {ChainstateLoadStatus::FAILURE, _("Error opening block database")};
56 : 0 : }
57 : :
58 [ - + ]: 2041 : if (options.wipe_block_tree_db) {
59 : 0 : pblocktree->WriteReindexing(true);
60 [ # # ]: 0 : chainman.m_blockman.m_blockfiles_indexed = false;
61 : : //If we're reindexing in prune mode, wipe away unusable block files and all undo data files
62 [ # # ]: 0 : if (options.prune) {
63 : 0 : chainman.m_blockman.CleanupBlockRevFiles();
64 : : }
65 : : }
66 : :
67 [ - + - - ]: 2041 : if (chainman.m_interrupt) return {ChainstateLoadStatus::INTERRUPTED, {}};
68 : :
69 : : // LoadBlockIndex will load m_have_pruned if we've ever removed a
70 : : // block file from disk.
71 : : // Note that it also sets m_blockfiles_indexed based on the disk flag!
72 [ - + ]: 2041 : if (!chainman.LoadBlockIndex()) {
73 [ # # # # ]: 0 : if (chainman.m_interrupt) return {ChainstateLoadStatus::INTERRUPTED, {}};
74 : 0 : return {ChainstateLoadStatus::FAILURE, _("Error loading block database")};
75 : : }
76 : :
77 [ - + - - ]: 2041 : if (!chainman.BlockIndex().empty() &&
78 : 0 : !chainman.m_blockman.LookupBlockIndex(chainman.GetConsensus().hashGenesisBlock)) {
79 : : // If the loaded chain has a wrong genesis, bail out immediately
80 : : // (we're likely using a testnet datadir, or the other way around).
81 : 0 : return {ChainstateLoadStatus::FAILURE_INCOMPATIBLE_DB, _("Incorrect or no genesis block found. Wrong datadir for network?")};
82 : : }
83 : :
84 : : // Check for changed -prune state. What we are concerned about is a user who has pruned blocks
85 : : // in the past, but is now trying to run unpruned.
86 [ - + - - ]: 2041 : if (chainman.m_blockman.m_have_pruned && !options.prune) {
87 : 0 : return {ChainstateLoadStatus::FAILURE, _("You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain")};
88 : : }
89 : :
90 : : // At this point blocktree args are consistent with what's on disk.
91 : : // If we're not mid-reindex (based on disk + args), add a genesis block on disk
92 : : // (otherwise we use the one already on disk).
93 : : // This is called again in ImportBlocks after the reindex completes.
94 [ + - + - ]: 2041 : if (chainman.m_blockman.m_blockfiles_indexed && !chainman.ActiveChainstate().LoadGenesisBlock()) {
95 : 0 : return {ChainstateLoadStatus::FAILURE, _("Error initializing block database")};
96 : : }
97 : :
98 : 4082 : auto is_coinsview_empty = [&](Chainstate* chainstate) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
99 [ + - + - ]: 2041 : return options.wipe_chainstate_db || chainstate->CoinsTip().GetBestBlock().IsNull();
100 : 2041 : };
101 : :
102 [ - + ]: 2041 : assert(chainman.m_total_coinstip_cache > 0);
103 [ - + ]: 2041 : assert(chainman.m_total_coinsdb_cache > 0);
104 : :
105 : : // If running with multiple chainstates, limit the cache sizes with a
106 : : // discount factor. If discounted the actual cache size will be
107 : : // recalculated by `chainman.MaybeRebalanceCaches()`. The discount factor
108 : : // is conservatively chosen such that the sum of the caches does not exceed
109 : : // the allowable amount during this temporary initialization state.
110 [ + - ]: 4082 : double init_cache_fraction = chainman.GetAll().size() > 1 ? 0.2 : 1.0;
111 : :
112 : : // At this point we're either in reindex or we've loaded a useful
113 : : // block tree into BlockIndex()!
114 : :
115 [ + + ]: 4082 : for (Chainstate* chainstate : chainman.GetAll()) {
116 [ + - + - ]: 2041 : LogPrintf("Initializing chainstate %s\n", chainstate->ToString());
117 : :
118 : 2041 : try {
119 [ + - ]: 2041 : chainstate->InitCoinsDB(
120 : 2041 : /*cache_size_bytes=*/chainman.m_total_coinsdb_cache * init_cache_fraction,
121 : 2041 : /*in_memory=*/options.coins_db_in_memory,
122 [ + - ]: 2041 : /*should_wipe=*/options.wipe_chainstate_db);
123 [ - - ]: 0 : } catch (dbwrapper_error& err) {
124 [ - - ]: 0 : LogError("%s\n", err.what());
125 [ - - ]: 0 : return {ChainstateLoadStatus::FAILURE, _("Error opening coins database")};
126 [ - - ]: 0 : }
127 : :
128 [ - + ]: 2041 : if (options.coins_error_cb) {
129 [ # # # # ]: 0 : chainstate->CoinsErrorCatcher().AddReadErrCallback(options.coins_error_cb);
130 : : }
131 : :
132 : : // Refuse to load unsupported database format.
133 : : // This is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate
134 [ + - + - : 2041 : if (chainstate->CoinsDB().NeedsUpgrade()) {
- + ]
135 [ # # ]: 0 : return {ChainstateLoadStatus::FAILURE_INCOMPATIBLE_DB, _("Unsupported chainstate database format found. "
136 : : "Please restart with -reindex-chainstate. This will "
137 : 0 : "rebuild the chainstate database.")};
138 : : }
139 : :
140 : : // ReplayBlocks is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate
141 [ + - - + ]: 2041 : if (!chainstate->ReplayBlocks()) {
142 [ # # ]: 0 : return {ChainstateLoadStatus::FAILURE, _("Unable to replay blocks. You will need to rebuild the database using -reindex-chainstate.")};
143 : : }
144 : :
145 : : // The on-disk coinsdb is now in a good state, create the cache
146 [ + - ]: 2041 : chainstate->InitCoinsCache(chainman.m_total_coinstip_cache * init_cache_fraction);
147 [ + - ]: 2041 : assert(chainstate->CanFlushToDisk());
148 : :
149 [ + - - + ]: 2041 : if (!is_coinsview_empty(chainstate)) {
150 : : // LoadChainTip initializes the chain based on CoinsTip()'s best block
151 [ # # # # ]: 0 : if (!chainstate->LoadChainTip()) {
152 [ # # ]: 0 : return {ChainstateLoadStatus::FAILURE, _("Error initializing block database")};
153 : : }
154 [ # # # # ]: 0 : assert(chainstate->m_chain.Tip() != nullptr);
155 : : }
156 : 0 : }
157 : :
158 [ + - ]: 2041 : if (!options.wipe_block_tree_db) {
159 : 2041 : auto chainstates{chainman.GetAll()};
160 [ + - - + ]: 2041 : if (std::any_of(chainstates.begin(), chainstates.end(),
161 : 2041 : [](const Chainstate* cs) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return cs->NeedsRedownload(); })) {
162 [ # # ]: 0 : return {ChainstateLoadStatus::FAILURE, strprintf(_("Witness data for blocks after height %d requires validation. Please restart with -reindex."),
163 [ # # ]: 0 : chainman.GetConsensus().SegwitHeight)};
164 : 2041 : };
165 : 2041 : }
166 : :
167 : : // Now that chainstates are loaded and we're able to flush to
168 : : // disk, rebalance the coins caches to desired levels based
169 : : // on the condition of each chainstate.
170 : 2041 : chainman.MaybeRebalanceCaches();
171 : :
172 [ + - ]: 4082 : return {ChainstateLoadStatus::SUCCESS, {}};
173 [ - - - - : 2041 : }
+ - ]
174 : :
175 : 2041 : ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSizes& cache_sizes,
176 : : const ChainstateLoadOptions& options)
177 : : {
178 [ + + ]: 2041 : if (!chainman.AssumedValidBlock().IsNull()) {
179 [ + - ]: 2 : LogPrintf("Assuming ancestors of block %s have valid signatures.\n", chainman.AssumedValidBlock().GetHex());
180 : : } else {
181 : 2040 : LogPrintf("Validating signatures for all blocks.\n");
182 : : }
183 [ + - ]: 2041 : LogPrintf("Setting nMinimumChainWork=%s\n", chainman.MinimumChainWork().GetHex());
184 [ - + ]: 2041 : if (chainman.MinimumChainWork() < UintToArith256(chainman.GetConsensus().nMinimumChainWork)) {
185 [ # # ]: 0 : LogPrintf("Warning: nMinimumChainWork set below default value of %s\n", chainman.GetConsensus().nMinimumChainWork.GetHex());
186 : : }
187 [ - + ]: 2041 : if (chainman.m_blockman.GetPruneTarget() == BlockManager::PRUNE_TARGET_MANUAL) {
188 : 0 : LogPrintf("Block pruning enabled. Use RPC call pruneblockchain(height) to manually prune block and undo files.\n");
189 [ - + ]: 2041 : } else if (chainman.m_blockman.GetPruneTarget()) {
190 : 0 : LogPrintf("Prune configured to target %u MiB on disk for block and undo files.\n", chainman.m_blockman.GetPruneTarget() / 1024 / 1024);
191 : : }
192 : :
193 : 2041 : LOCK(cs_main);
194 : :
195 : 2041 : chainman.m_total_coinstip_cache = cache_sizes.coins;
196 : 2041 : chainman.m_total_coinsdb_cache = cache_sizes.coins_db;
197 : :
198 : : // Load the fully validated chainstate.
199 [ + - ]: 2041 : chainman.InitializeChainstate(options.mempool);
200 : :
201 : : // Load a chain created from a UTXO snapshot, if any exist.
202 [ + - ]: 2041 : bool has_snapshot = chainman.DetectSnapshotChainstate();
203 : :
204 [ - + - - ]: 2041 : if (has_snapshot && options.wipe_chainstate_db) {
205 [ # # ]: 0 : LogPrintf("[snapshot] deleting snapshot chainstate due to reindexing\n");
206 [ # # # # ]: 0 : if (!chainman.DeleteSnapshotChainstate()) {
207 [ # # # # ]: 0 : return {ChainstateLoadStatus::FAILURE_FATAL, Untranslated("Couldn't remove snapshot chainstate.")};
208 : : }
209 : : }
210 : :
211 [ + - - + ]: 2041 : auto [init_status, init_error] = CompleteChainstateInitialization(chainman, cache_sizes, options);
212 [ - + ]: 2041 : if (init_status != ChainstateLoadStatus::SUCCESS) {
213 [ # # ]: 0 : return {init_status, init_error};
214 : : }
215 : :
216 : : // If a snapshot chainstate was fully validated by a background chainstate during
217 : : // the last run, detect it here and clean up the now-unneeded background
218 : : // chainstate.
219 : : //
220 : : // Why is this cleanup done here (on subsequent restart) and not just when the
221 : : // snapshot is actually validated? Because this entails unusual
222 : : // filesystem operations to move leveldb data directories around, and that seems
223 : : // too risky to do in the middle of normal runtime.
224 [ + - ]: 2041 : auto snapshot_completion = chainman.MaybeCompleteSnapshotValidation();
225 : :
226 [ - + ]: 2041 : if (snapshot_completion == SnapshotCompletionResult::SKIPPED) {
227 : : // do nothing; expected case
228 [ # # ]: 0 : } else if (snapshot_completion == SnapshotCompletionResult::SUCCESS) {
229 [ # # ]: 0 : LogPrintf("[snapshot] cleaning up unneeded background chainstate, then reinitializing\n");
230 [ # # # # ]: 0 : if (!chainman.ValidatedSnapshotCleanup()) {
231 [ # # # # ]: 0 : return {ChainstateLoadStatus::FAILURE_FATAL, Untranslated("Background chainstate cleanup failed unexpectedly.")};
232 : : }
233 : :
234 : : // Because ValidatedSnapshotCleanup() has torn down chainstates with
235 : : // ChainstateManager::ResetChainstates(), reinitialize them here without
236 : : // duplicating the blockindex work above.
237 [ # # # # ]: 0 : assert(chainman.GetAll().empty());
238 [ # # # # ]: 0 : assert(!chainman.IsSnapshotActive());
239 [ # # ]: 0 : assert(!chainman.IsSnapshotValidated());
240 : :
241 [ # # ]: 0 : chainman.InitializeChainstate(options.mempool);
242 : :
243 : : // A reload of the block index is required to recompute setBlockIndexCandidates
244 : : // for the fully validated chainstate.
245 [ # # # # ]: 0 : chainman.ActiveChainstate().ClearBlockIndexCandidates();
246 : :
247 [ # # # # ]: 0 : auto [init_status, init_error] = CompleteChainstateInitialization(chainman, cache_sizes, options);
248 [ # # ]: 0 : if (init_status != ChainstateLoadStatus::SUCCESS) {
249 [ # # ]: 0 : return {init_status, init_error};
250 : : }
251 : 0 : } else {
252 [ # # ]: 0 : return {ChainstateLoadStatus::FAILURE_FATAL, _(
253 : : "UTXO snapshot failed to validate. "
254 : 0 : "Restart to resume normal initial block download, or try loading a different snapshot.")};
255 : : }
256 : :
257 [ + - ]: 4082 : return {ChainstateLoadStatus::SUCCESS, {}};
258 [ + - ]: 6123 : }
259 : :
260 : 2041 : ChainstateLoadResult VerifyLoadedChainstate(ChainstateManager& chainman, const ChainstateLoadOptions& options)
261 : : {
262 : 4082 : auto is_coinsview_empty = [&](Chainstate* chainstate) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
263 [ + - + - ]: 2041 : return options.wipe_chainstate_db || chainstate->CoinsTip().GetBestBlock().IsNull();
264 : 2041 : };
265 : :
266 : 2041 : LOCK(cs_main);
267 : :
268 [ + - + + ]: 4082 : for (Chainstate* chainstate : chainman.GetAll()) {
269 [ + - - + ]: 2041 : if (!is_coinsview_empty(chainstate)) {
270 [ # # ]: 0 : const CBlockIndex* tip = chainstate->m_chain.Tip();
271 [ # # # # : 0 : if (tip && tip->nTime > GetTime() + MAX_FUTURE_BLOCK_TIME) {
# # ]
272 [ # # ]: 0 : return {ChainstateLoadStatus::FAILURE, _("The block database contains a block which appears to be from the future. "
273 : : "This may be due to your computer's date and time being set incorrectly. "
274 : 0 : "Only rebuild the block database if you are sure that your computer's date and time are correct")};
275 : : }
276 : :
277 [ # # # # ]: 0 : VerifyDBResult result = CVerifyDB(chainman.GetNotifications()).VerifyDB(
278 : 0 : *chainstate, chainman.GetConsensus(), chainstate->CoinsDB(),
279 : 0 : options.check_level,
280 [ # # ]: 0 : options.check_blocks);
281 [ # # # # ]: 0 : switch (result) {
282 : : case VerifyDBResult::SUCCESS:
283 : : case VerifyDBResult::SKIPPED_MISSING_BLOCKS:
284 : : break;
285 : 0 : case VerifyDBResult::INTERRUPTED:
286 [ # # ]: 0 : return {ChainstateLoadStatus::INTERRUPTED, _("Block verification was interrupted")};
287 : 0 : case VerifyDBResult::CORRUPTED_BLOCK_DB:
288 [ # # ]: 0 : return {ChainstateLoadStatus::FAILURE, _("Corrupted block database detected")};
289 : 0 : case VerifyDBResult::SKIPPED_L3_CHECKS:
290 [ # # ]: 0 : if (options.require_full_verification) {
291 [ # # ]: 0 : return {ChainstateLoadStatus::FAILURE_INSUFFICIENT_DBCACHE, _("Insufficient dbcache for block verification")};
292 : : }
293 : : break;
294 : : } // no default case, so the compiler can warn about missing cases
295 : : }
296 : 0 : }
297 : :
298 [ + - ]: 4082 : return {ChainstateLoadStatus::SUCCESS, {}};
299 [ + - ]: 4082 : }
300 : : } // namespace node
|