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 : 1112 : static ChainstateLoadResult CompleteChainstateInitialization(
38 : : ChainstateManager& chainman,
39 : : const CacheSizes& cache_sizes,
40 : : const ChainstateLoadOptions& options) EXCLUSIVE_LOCKS_REQUIRED(::cs_main)
41 : : {
42 : 1112 : 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 [ + + ]: 1112 : pblocktree.reset();
46 : 1112 : try {
47 : 2223 : pblocktree = std::make_unique<BlockTreeDB>(DBParams{
48 [ + - + - : 3337 : .path = chainman.m_options.datadir / "blocks" / "index",
+ - ]
49 : 1112 : .cache_bytes = cache_sizes.block_tree_db,
50 : 1112 : .memory_only = options.block_tree_db_in_memory,
51 [ + + ]: 1112 : .wipe_data = options.wipe_block_tree_db,
52 : 1111 : .options = chainman.m_options.block_tree_db});
53 [ - + ]: 1 : } catch (dbwrapper_error& err) {
54 [ + - ]: 1 : LogError("%s\n", err.what());
55 [ + - ]: 1 : return {ChainstateLoadStatus::FAILURE, _("Error opening block database")};
56 : 1 : }
57 : :
58 [ + + ]: 1111 : if (options.wipe_block_tree_db) {
59 : 17 : pblocktree->WriteReindexing(true);
60 [ + + ]: 17 : 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 [ + + ]: 17 : if (options.prune) {
63 : 3 : chainman.m_blockman.CleanupBlockRevFiles();
64 : : }
65 : : }
66 : :
67 [ + + + - ]: 1118 : 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 [ + + ]: 1104 : if (!chainman.LoadBlockIndex()) {
73 [ + + + - ]: 6 : if (chainman.m_interrupt) return {ChainstateLoadStatus::INTERRUPTED, {}};
74 : 2 : return {ChainstateLoadStatus::FAILURE, _("Error loading block database")};
75 : : }
76 : :
77 [ + + - + ]: 1739 : if (!chainman.BlockIndex().empty() &&
78 : 639 : !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 [ + + - + ]: 1100 : 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 [ + + + - ]: 1100 : if (chainman.m_blockman.m_blockfiles_indexed && !chainman.ActiveChainstate().LoadGenesisBlock()) {
95 : 0 : return {ChainstateLoadStatus::FAILURE, _("Error initializing block database")};
96 : : }
97 : :
98 : 2206 : auto is_coinsview_empty = [&](Chainstate* chainstate) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
99 [ + + + + ]: 1106 : return options.wipe_chainstate_db || chainstate->CoinsTip().GetBestBlock().IsNull();
100 : 1100 : };
101 : :
102 [ - + ]: 1100 : assert(chainman.m_total_coinstip_cache > 0);
103 [ - + ]: 1100 : 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 [ + + ]: 2191 : 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 [ + + ]: 2206 : for (Chainstate* chainstate : chainman.GetAll()) {
116 [ + - + - ]: 1109 : LogPrintf("Initializing chainstate %s\n", chainstate->ToString());
117 : :
118 : 1109 : try {
119 [ + - ]: 1109 : chainstate->InitCoinsDB(
120 : 1109 : /*cache_size_bytes=*/chainman.m_total_coinsdb_cache * init_cache_fraction,
121 : 1109 : /*in_memory=*/options.coins_db_in_memory,
122 [ + + ]: 1109 : /*should_wipe=*/options.wipe_chainstate_db);
123 [ - + ]: 2 : } catch (dbwrapper_error& err) {
124 [ + - ]: 2 : LogError("%s\n", err.what());
125 [ + - ]: 2 : return {ChainstateLoadStatus::FAILURE, _("Error opening coins database")};
126 [ + - ]: 2 : }
127 : :
128 [ + + ]: 1107 : if (options.coins_error_cb) {
129 [ + - + - ]: 1862 : 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 [ + - + - : 1107 : if (chainstate->CoinsDB().NeedsUpgrade()) {
+ + ]
135 [ + - ]: 1 : return {ChainstateLoadStatus::FAILURE_INCOMPATIBLE_DB, _("Unsupported chainstate database format found. "
136 : : "Please restart with -reindex-chainstate. This will "
137 : 1 : "rebuild the chainstate database.")};
138 : : }
139 : :
140 : : // ReplayBlocks is a no-op if we cleared the coinsviewdb with -reindex or -reindex-chainstate
141 [ + - - + ]: 1106 : 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 [ + - ]: 1106 : chainstate->InitCoinsCache(chainman.m_total_coinstip_cache * init_cache_fraction);
147 [ + - ]: 1106 : assert(chainstate->CanFlushToDisk());
148 : :
149 [ + - + + ]: 1106 : if (!is_coinsview_empty(chainstate)) {
150 : : // LoadChainTip initializes the chain based on CoinsTip()'s best block
151 [ + - - + ]: 639 : if (!chainstate->LoadChainTip()) {
152 [ # # ]: 0 : return {ChainstateLoadStatus::FAILURE, _("Error initializing block database")};
153 : : }
154 [ + - - + ]: 639 : assert(chainstate->m_chain.Tip() != nullptr);
155 : : }
156 : 3 : }
157 : :
158 [ + + ]: 1097 : if (!options.wipe_block_tree_db) {
159 : 1080 : auto chainstates{chainman.GetAll()};
160 [ + - + + ]: 1080 : if (std::any_of(chainstates.begin(), chainstates.end(),
161 : 1089 : [](const Chainstate* cs) EXCLUSIVE_LOCKS_REQUIRED(cs_main) { return cs->NeedsRedownload(); })) {
162 [ + - ]: 2 : return {ChainstateLoadStatus::FAILURE, strprintf(_("Witness data for blocks after height %d requires validation. Please restart with -reindex."),
163 [ + - ]: 2 : chainman.GetConsensus().SegwitHeight)};
164 : 1079 : };
165 : 1080 : }
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 : 1096 : chainman.MaybeRebalanceCaches();
171 : :
172 [ + - ]: 2192 : return {ChainstateLoadStatus::SUCCESS, {}};
173 [ + - + - : 1105 : }
+ - ]
174 : :
175 : 1109 : ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSizes& cache_sizes,
176 : : const ChainstateLoadOptions& options)
177 : : {
178 [ + + ]: 1109 : if (!chainman.AssumedValidBlock().IsNull()) {
179 [ + - ]: 188 : LogPrintf("Assuming ancestors of block %s have valid signatures.\n", chainman.AssumedValidBlock().GetHex());
180 : : } else {
181 : 1015 : LogPrintf("Validating signatures for all blocks.\n");
182 : : }
183 [ + - ]: 1109 : LogPrintf("Setting nMinimumChainWork=%s\n", chainman.MinimumChainWork().GetHex());
184 [ + + ]: 1109 : if (chainman.MinimumChainWork() < UintToArith256(chainman.GetConsensus().nMinimumChainWork)) {
185 [ + - ]: 6 : LogPrintf("Warning: nMinimumChainWork set below default value of %s\n", chainman.GetConsensus().nMinimumChainWork.GetHex());
186 : : }
187 [ + + ]: 1109 : if (chainman.m_blockman.GetPruneTarget() == BlockManager::PRUNE_TARGET_MANUAL) {
188 : 34 : LogPrintf("Block pruning enabled. Use RPC call pruneblockchain(height) to manually prune block and undo files.\n");
189 [ + + ]: 1075 : } else if (chainman.m_blockman.GetPruneTarget()) {
190 : 20 : LogPrintf("Prune configured to target %u MiB on disk for block and undo files.\n", chainman.m_blockman.GetPruneTarget() / 1024 / 1024);
191 : : }
192 : :
193 : 1109 : LOCK(cs_main);
194 : :
195 : 1109 : chainman.m_total_coinstip_cache = cache_sizes.coins;
196 : 1109 : chainman.m_total_coinsdb_cache = cache_sizes.coins_db;
197 : :
198 : : // Load the fully validated chainstate.
199 [ + - ]: 1109 : chainman.InitializeChainstate(options.mempool);
200 : :
201 : : // Load a chain created from a UTXO snapshot, if any exist.
202 [ + - ]: 1109 : bool has_snapshot = chainman.DetectSnapshotChainstate();
203 : :
204 [ + + + + ]: 1109 : if (has_snapshot && options.wipe_chainstate_db) {
205 [ + - ]: 2 : LogPrintf("[snapshot] deleting snapshot chainstate due to reindexing\n");
206 [ + - - + ]: 2 : if (!chainman.DeleteSnapshotChainstate()) {
207 [ # # # # ]: 0 : return {ChainstateLoadStatus::FAILURE_FATAL, Untranslated("Couldn't remove snapshot chainstate.")};
208 : : }
209 : : }
210 : :
211 [ + - + + ]: 1109 : auto [init_status, init_error] = CompleteChainstateInitialization(chainman, cache_sizes, options);
212 [ + + ]: 1109 : if (init_status != ChainstateLoadStatus::SUCCESS) {
213 [ + - ]: 16 : 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 [ + - ]: 1093 : auto snapshot_completion = chainman.MaybeCompleteSnapshotValidation();
225 : :
226 [ + + ]: 1093 : if (snapshot_completion == SnapshotCompletionResult::SKIPPED) {
227 : : // do nothing; expected case
228 [ + - ]: 3 : } else if (snapshot_completion == SnapshotCompletionResult::SUCCESS) {
229 [ + - ]: 3 : LogPrintf("[snapshot] cleaning up unneeded background chainstate, then reinitializing\n");
230 [ + - - + ]: 3 : 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 [ + - - + ]: 3 : assert(chainman.GetAll().empty());
238 [ + - - + ]: 3 : assert(!chainman.IsSnapshotActive());
239 [ - + ]: 3 : assert(!chainman.IsSnapshotValidated());
240 : :
241 [ + - ]: 3 : chainman.InitializeChainstate(options.mempool);
242 : :
243 : : // A reload of the block index is required to recompute setBlockIndexCandidates
244 : : // for the fully validated chainstate.
245 [ + - + - ]: 3 : chainman.ActiveChainstate().ClearBlockIndexCandidates();
246 : :
247 [ + - - + ]: 3 : auto [init_status, init_error] = CompleteChainstateInitialization(chainman, cache_sizes, options);
248 [ - + ]: 3 : if (init_status != ChainstateLoadStatus::SUCCESS) {
249 [ # # ]: 0 : return {init_status, init_error};
250 : : }
251 : 3 : } 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 [ + - ]: 2186 : return {ChainstateLoadStatus::SUCCESS, {}};
258 [ + - ]: 3311 : }
259 : :
260 : 1093 : ChainstateLoadResult VerifyLoadedChainstate(ChainstateManager& chainman, const ChainstateLoadOptions& options)
261 : : {
262 : 2192 : auto is_coinsview_empty = [&](Chainstate* chainstate) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
263 [ + + + + ]: 1099 : return options.wipe_chainstate_db || chainstate->CoinsTip().GetBestBlock().IsNull();
264 : 1093 : };
265 : :
266 : 1093 : LOCK(cs_main);
267 : :
268 [ + - + + ]: 2186 : for (Chainstate* chainstate : chainman.GetAll()) {
269 [ + - + + ]: 1099 : if (!is_coinsview_empty(chainstate)) {
270 [ + - ]: 632 : const CBlockIndex* tip = chainstate->m_chain.Tip();
271 [ + - + - : 632 : if (tip && tip->nTime > GetTime() + MAX_FUTURE_BLOCK_TIME) {
+ + ]
272 [ + - ]: 2 : 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 : 2 : "Only rebuild the block database if you are sure that your computer's date and time are correct")};
275 : : }
276 : :
277 [ + - + - ]: 1260 : VerifyDBResult result = CVerifyDB(chainman.GetNotifications()).VerifyDB(
278 : 630 : *chainstate, chainman.GetConsensus(), chainstate->CoinsDB(),
279 : 630 : options.check_level,
280 [ + - ]: 630 : options.check_blocks);
281 [ + + - + ]: 630 : switch (result) {
282 : : case VerifyDBResult::SUCCESS:
283 : : case VerifyDBResult::SKIPPED_MISSING_BLOCKS:
284 : : break;
285 : 2 : case VerifyDBResult::INTERRUPTED:
286 [ + - ]: 2 : return {ChainstateLoadStatus::INTERRUPTED, _("Block verification was interrupted")};
287 : 2 : case VerifyDBResult::CORRUPTED_BLOCK_DB:
288 [ + - ]: 2 : 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 : 6 : }
297 : :
298 [ + - ]: 2174 : return {ChainstateLoadStatus::SUCCESS, {}};
299 [ + - ]: 2180 : }
300 : : } // namespace node
|