|
28 | 28 | #include <vector>
|
29 | 29 |
|
30 | 30 | namespace node {
|
31 |
| -ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSizes& cache_sizes, |
32 |
| - const ChainstateLoadOptions& options) |
| 31 | +// Complete initialization of chainstates after the initial call has been made |
| 32 | +// to ChainstateManager::InitializeChainstate(). |
| 33 | +static ChainstateLoadResult CompleteChainstateInitialization( |
| 34 | + ChainstateManager& chainman, |
| 35 | + const CacheSizes& cache_sizes, |
| 36 | + const ChainstateLoadOptions& options) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) |
33 | 37 | {
|
34 |
| - auto is_coinsview_empty = [&](Chainstate* chainstate) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { |
35 |
| - return options.reindex || options.reindex_chainstate || chainstate->CoinsTip().GetBestBlock().IsNull(); |
36 |
| - }; |
37 |
| - |
38 |
| - if (!chainman.AssumedValidBlock().IsNull()) { |
39 |
| - LogPrintf("Assuming ancestors of block %s have valid signatures.\n", chainman.AssumedValidBlock().GetHex()); |
40 |
| - } else { |
41 |
| - LogPrintf("Validating signatures for all blocks.\n"); |
42 |
| - } |
43 |
| - LogPrintf("Setting nMinimumChainWork=%s\n", chainman.MinimumChainWork().GetHex()); |
44 |
| - if (chainman.MinimumChainWork() < UintToArith256(chainman.GetConsensus().nMinimumChainWork)) { |
45 |
| - LogPrintf("Warning: nMinimumChainWork set below default value of %s\n", chainman.GetConsensus().nMinimumChainWork.GetHex()); |
46 |
| - } |
47 |
| - if (chainman.m_blockman.GetPruneTarget() == std::numeric_limits<uint64_t>::max()) { |
48 |
| - LogPrintf("Block pruning enabled. Use RPC call pruneblockchain(height) to manually prune block and undo files.\n"); |
49 |
| - } else if (chainman.m_blockman.GetPruneTarget()) { |
50 |
| - LogPrintf("Prune configured to target %u MiB on disk for block and undo files.\n", chainman.m_blockman.GetPruneTarget() / 1024 / 1024); |
51 |
| - } |
52 |
| - |
53 |
| - LOCK(cs_main); |
54 |
| - chainman.m_total_coinstip_cache = cache_sizes.coins; |
55 |
| - chainman.m_total_coinsdb_cache = cache_sizes.coins_db; |
56 |
| - |
57 |
| - // Load the fully validated chainstate. |
58 |
| - chainman.InitializeChainstate(options.mempool); |
59 |
| - |
60 |
| - // Load a chain created from a UTXO snapshot, if any exist. |
61 |
| - chainman.DetectSnapshotChainstate(options.mempool); |
62 |
| - |
63 | 38 | auto& pblocktree{chainman.m_blockman.m_block_tree_db};
|
64 | 39 | // new CBlockTreeDB tries to delete the existing file, which
|
65 | 40 | // fails if it's still open from the previous loop. Close it first:
|
@@ -111,6 +86,13 @@ ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSize
|
111 | 86 | return {ChainstateLoadStatus::FAILURE, _("Error initializing block database")};
|
112 | 87 | }
|
113 | 88 |
|
| 89 | + auto is_coinsview_empty = [&](Chainstate* chainstate) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { |
| 90 | + return options.reindex || options.reindex_chainstate || chainstate->CoinsTip().GetBestBlock().IsNull(); |
| 91 | + }; |
| 92 | + |
| 93 | + assert(chainman.m_total_coinstip_cache > 0); |
| 94 | + assert(chainman.m_total_coinsdb_cache > 0); |
| 95 | + |
114 | 96 | // Conservative value which is arbitrarily chosen, as it will ultimately be changed
|
115 | 97 | // by a call to `chainman.MaybeRebalanceCaches()`. We just need to make sure
|
116 | 98 | // that the sum of the two caches (40%) does not exceed the allowable amount
|
@@ -175,6 +157,84 @@ ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSize
|
175 | 157 | return {ChainstateLoadStatus::SUCCESS, {}};
|
176 | 158 | }
|
177 | 159 |
|
| 160 | +ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSizes& cache_sizes, |
| 161 | + const ChainstateLoadOptions& options) |
| 162 | +{ |
| 163 | + if (!chainman.AssumedValidBlock().IsNull()) { |
| 164 | + LogPrintf("Assuming ancestors of block %s have valid signatures.\n", chainman.AssumedValidBlock().GetHex()); |
| 165 | + } else { |
| 166 | + LogPrintf("Validating signatures for all blocks.\n"); |
| 167 | + } |
| 168 | + LogPrintf("Setting nMinimumChainWork=%s\n", chainman.MinimumChainWork().GetHex()); |
| 169 | + if (chainman.MinimumChainWork() < UintToArith256(chainman.GetConsensus().nMinimumChainWork)) { |
| 170 | + LogPrintf("Warning: nMinimumChainWork set below default value of %s\n", chainman.GetConsensus().nMinimumChainWork.GetHex()); |
| 171 | + } |
| 172 | + if (chainman.m_blockman.GetPruneTarget() == std::numeric_limits<uint64_t>::max()) { |
| 173 | + LogPrintf("Block pruning enabled. Use RPC call pruneblockchain(height) to manually prune block and undo files.\n"); |
| 174 | + } else if (chainman.m_blockman.GetPruneTarget()) { |
| 175 | + LogPrintf("Prune configured to target %u MiB on disk for block and undo files.\n", chainman.m_blockman.GetPruneTarget() / 1024 / 1024); |
| 176 | + } |
| 177 | + |
| 178 | + LOCK(cs_main); |
| 179 | + |
| 180 | + chainman.m_total_coinstip_cache = cache_sizes.coins; |
| 181 | + chainman.m_total_coinsdb_cache = cache_sizes.coins_db; |
| 182 | + |
| 183 | + // Load the fully validated chainstate. |
| 184 | + chainman.InitializeChainstate(options.mempool); |
| 185 | + |
| 186 | + // Load a chain created from a UTXO snapshot, if any exist. |
| 187 | + chainman.DetectSnapshotChainstate(options.mempool); |
| 188 | + |
| 189 | + auto [init_status, init_error] = CompleteChainstateInitialization(chainman, cache_sizes, options); |
| 190 | + if (init_status != ChainstateLoadStatus::SUCCESS) { |
| 191 | + return {init_status, init_error}; |
| 192 | + } |
| 193 | + |
| 194 | + // If a snapshot chainstate was fully validated by a background chainstate during |
| 195 | + // the last run, detect it here and clean up the now-unneeded background |
| 196 | + // chainstate. |
| 197 | + // |
| 198 | + // Why is this cleanup done here (on subsequent restart) and not just when the |
| 199 | + // snapshot is actually validated? Because this entails unusual |
| 200 | + // filesystem operations to move leveldb data directories around, and that seems |
| 201 | + // too risky to do in the middle of normal runtime. |
| 202 | + auto snapshot_completion = chainman.MaybeCompleteSnapshotValidation(); |
| 203 | + |
| 204 | + if (snapshot_completion == SnapshotCompletionResult::SKIPPED) { |
| 205 | + // do nothing; expected case |
| 206 | + } else if (snapshot_completion == SnapshotCompletionResult::SUCCESS) { |
| 207 | + LogPrintf("[snapshot] cleaning up unneeded background chainstate, then reinitializing\n"); |
| 208 | + if (!chainman.ValidatedSnapshotCleanup()) { |
| 209 | + AbortNode("Background chainstate cleanup failed unexpectedly."); |
| 210 | + } |
| 211 | + |
| 212 | + // Because ValidatedSnapshotCleanup() has torn down chainstates with |
| 213 | + // ChainstateManager::ResetChainstates(), reinitialize them here without |
| 214 | + // duplicating the blockindex work above. |
| 215 | + assert(chainman.GetAll().empty()); |
| 216 | + assert(!chainman.IsSnapshotActive()); |
| 217 | + assert(!chainman.IsSnapshotValidated()); |
| 218 | + |
| 219 | + chainman.InitializeChainstate(options.mempool); |
| 220 | + |
| 221 | + // A reload of the block index is required to recompute setBlockIndexCandidates |
| 222 | + // for the fully validated chainstate. |
| 223 | + chainman.ActiveChainstate().UnloadBlockIndex(); |
| 224 | + |
| 225 | + auto [init_status, init_error] = CompleteChainstateInitialization(chainman, cache_sizes, options); |
| 226 | + if (init_status != ChainstateLoadStatus::SUCCESS) { |
| 227 | + return {init_status, init_error}; |
| 228 | + } |
| 229 | + } else { |
| 230 | + return {ChainstateLoadStatus::FAILURE, _( |
| 231 | + "UTXO snapshot failed to validate. " |
| 232 | + "Restart to resume normal initial block download, or try loading a different snapshot.")}; |
| 233 | + } |
| 234 | + |
| 235 | + return {ChainstateLoadStatus::SUCCESS, {}}; |
| 236 | +} |
| 237 | + |
178 | 238 | ChainstateLoadResult VerifyLoadedChainstate(ChainstateManager& chainman, const ChainstateLoadOptions& options)
|
179 | 239 | {
|
180 | 240 | auto is_coinsview_empty = [&](Chainstate* chainstate) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) {
|
|
0 commit comments