Line data Source code
1 : // Copyright (c) 2017-2021 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>
6 : #include <index/base.h>
7 : #include <node/blockstorage.h>
8 : #include <node/interface_ui.h>
9 : #include <shutdown.h>
10 : #include <tinyformat.h>
11 : #include <util/system.h>
12 : #include <util/thread.h>
13 : #include <util/translation.h>
14 : #include <validation.h>
15 : #include <warnings.h>
16 :
17 : using node::PruneLockInfo;
18 : using node::ReadBlockFromDisk;
19 : using node::fPruneMode;
20 :
21 : constexpr uint8_t DB_BEST_BLOCK{'B'};
22 :
23 : constexpr auto SYNC_LOG_INTERVAL{30s};
24 : constexpr auto SYNC_LOCATOR_WRITE_INTERVAL{30s};
25 :
26 0 : void BaseIndex::FatalErrorImpl(const std::string& message)
27 : {
28 0 : SetMiscWarning(Untranslated(message));
29 0 : LogPrintf("*** %s\n", message);
30 0 : AbortError(_("A fatal internal error occurred, see debug.log for details"));
31 0 : StartShutdown();
32 0 : }
33 :
34 5266 : BaseIndex::DB::DB(const fs::path& path, size_t n_cache_size, bool f_memory, bool f_wipe, bool f_obfuscate) :
35 4084 : CDBWrapper(path, n_cache_size, f_memory, f_wipe, f_obfuscate)
36 5266 : {}
37 :
38 3626 : bool BaseIndex::DB::ReadBestBlock(CBlockLocator& locator) const
39 : {
40 3626 : bool success = Read(DB_BEST_BLOCK, locator);
41 3626 : if (!success) {
42 1700 : locator.SetNull();
43 1700 : }
44 3626 : return success;
45 : }
46 :
47 23336 : void BaseIndex::DB::WriteBestBlock(CDBBatch& batch, const CBlockLocator& locator)
48 : {
49 23336 : batch.Write(DB_BEST_BLOCK, locator);
50 23336 : }
51 :
52 3629 : BaseIndex::~BaseIndex()
53 3629 : {
54 3629 : Interrupt();
55 3629 : Stop();
56 3629 : }
57 :
58 3626 : bool BaseIndex::Init()
59 : {
60 3626 : CBlockLocator locator;
61 3626 : if (!GetDB().ReadBestBlock(locator)) {
62 1700 : locator.SetNull();
63 1700 : }
64 :
65 3626 : LOCK(cs_main);
66 3626 : CChain& active_chain = m_chainstate->m_chain;
67 3626 : if (locator.IsNull()) {
68 1700 : SetBestBlockIndex(nullptr);
69 1700 : } else {
70 1926 : SetBestBlockIndex(m_chainstate->FindForkInGlobalIndex(locator));
71 : }
72 :
73 : // Note: this will latch to true immediately if the user starts up with an empty
74 : // datadir and an index enabled. If this is the case, indexation will happen solely
75 : // via `BlockConnected` signals until, possibly, the next restart.
76 3626 : m_synced = m_best_block_index.load() == active_chain.Tip();
77 3626 : if (!m_synced) {
78 773 : bool prune_violation = false;
79 773 : if (!m_best_block_index) {
80 : // index is not built yet
81 : // make sure we have all block data back to the genesis
82 739 : prune_violation = m_chainstate->m_blockman.GetFirstStoredBlock(*active_chain.Tip()) != active_chain.Genesis();
83 739 : }
84 : // in case the index has a best block set and is not fully synced
85 : // check if we have the required blocks to continue building the index
86 : else {
87 34 : const CBlockIndex* block_to_test = m_best_block_index.load();
88 34 : if (!active_chain.Contains(block_to_test)) {
89 : // if the bestblock is not part of the mainchain, find the fork
90 : // and make sure we have all data down to the fork
91 0 : block_to_test = active_chain.FindFork(block_to_test);
92 0 : }
93 34 : const CBlockIndex* block = active_chain.Tip();
94 34 : prune_violation = true;
95 : // check backwards from the tip if we have all block data until we reach the indexes bestblock
96 4915 : while (block_to_test && block && (block->nStatus & BLOCK_HAVE_DATA)) {
97 4915 : if (block_to_test == block) {
98 34 : prune_violation = false;
99 34 : break;
100 : }
101 : // block->pprev must exist at this point, since block_to_test is part of the chain
102 : // and thus must be encountered when going backwards from the tip
103 4881 : assert(block->pprev);
104 4881 : block = block->pprev;
105 : }
106 : }
107 773 : if (prune_violation) {
108 0 : return InitError(strprintf(Untranslated("%s best block of the index goes beyond pruned data. Please disable the index or reindex (which will download the whole blockchain again)"), GetName()));
109 : }
110 773 : }
111 3626 : return true;
112 3626 : }
113 :
114 102799 : static const CBlockIndex* NextSyncBlock(const CBlockIndex* pindex_prev, CChain& chain) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
115 : {
116 102799 : AssertLockHeld(cs_main);
117 :
118 102799 : if (!pindex_prev) {
119 739 : return chain.Genesis();
120 : }
121 :
122 102060 : const CBlockIndex* pindex = chain.Next(pindex_prev);
123 102060 : if (pindex) {
124 101321 : return pindex;
125 : }
126 :
127 739 : return chain.Next(chain.FindFork(pindex_prev));
128 102799 : }
129 :
130 3626 : void BaseIndex::ThreadSync()
131 : {
132 3626 : const CBlockIndex* pindex = m_best_block_index.load();
133 3626 : if (!m_synced) {
134 773 : auto& consensus_params = Params().GetConsensus();
135 :
136 773 : std::chrono::steady_clock::time_point last_log_time{0s};
137 773 : std::chrono::steady_clock::time_point last_locator_write_time{0s};
138 102833 : while (true) {
139 102833 : if (m_interrupt) {
140 34 : SetBestBlockIndex(pindex);
141 : // No need to handle errors in Commit. If it fails, the error will be already be
142 : // logged. The best way to recover is to continue, as index cannot be corrupted by
143 : // a missed commit to disk for an advanced index state.
144 34 : Commit();
145 34 : return;
146 : }
147 :
148 : {
149 102799 : LOCK(cs_main);
150 102799 : const CBlockIndex* pindex_next = NextSyncBlock(pindex, m_chainstate->m_chain);
151 102799 : if (!pindex_next) {
152 739 : SetBestBlockIndex(pindex);
153 : // No need to handle errors in Commit. See rationale above.
154 739 : Commit();
155 739 : m_synced = true;
156 739 : break;
157 : }
158 102060 : if (pindex_next->pprev != pindex && !Rewind(pindex, pindex_next->pprev)) {
159 0 : FatalError("%s: Failed to rewind index %s to a previous chain tip",
160 0 : __func__, GetName());
161 0 : return;
162 : }
163 102060 : pindex = pindex_next;
164 102799 : }
165 :
166 102070 : CBlock block;
167 102070 : if (!ReadBlockFromDisk(block, pindex, consensus_params)) {
168 0 : FatalError("%s: Failed to read block %s from disk",
169 0 : __func__, pindex->GetBlockHash().ToString());
170 0 : return;
171 : }
172 102058 : if (!WriteBlock(block, pindex)) {
173 0 : FatalError("%s: Failed to write block %s to index database",
174 0 : __func__, pindex->GetBlockHash().ToString());
175 0 : return;
176 : }
177 :
178 102058 : auto current_time{std::chrono::steady_clock::now()};
179 102058 : if (last_log_time + SYNC_LOG_INTERVAL < current_time) {
180 773 : LogPrintf("Syncing %s with block chain from height %d\n",
181 : GetName(), pindex->nHeight);
182 773 : last_log_time = current_time;
183 773 : }
184 :
185 102049 : if (last_locator_write_time + SYNC_LOCATOR_WRITE_INTERVAL < current_time) {
186 773 : SetBestBlockIndex(pindex);
187 773 : last_locator_write_time = current_time;
188 : // No need to handle errors in Commit. See rationale above.
189 773 : Commit();
190 773 : }
191 102052 : }
192 739 : }
193 :
194 3592 : if (pindex) {
195 2616 : LogPrintf("%s is enabled at height %d\n", GetName(), pindex->nHeight);
196 2616 : } else {
197 976 : LogPrintf("%s is enabled\n", GetName());
198 : }
199 3668 : }
200 :
201 23336 : bool BaseIndex::Commit()
202 : {
203 23336 : CDBBatch batch(GetDB());
204 23336 : if (!CommitInternal(batch) || !GetDB().WriteBatch(batch)) {
205 0 : return error("%s: Failed to commit latest %s state", __func__, GetName());
206 : }
207 23336 : return true;
208 23336 : }
209 :
210 23336 : bool BaseIndex::CommitInternal(CDBBatch& batch)
211 : {
212 23336 : LOCK(cs_main);
213 : // Don't commit anything if we haven't indexed any block yet
214 : // (this could happen if init is interrupted).
215 23336 : if (m_best_block_index == nullptr) {
216 0 : return false;
217 : }
218 23336 : GetDB().WriteBestBlock(batch, m_chainstate->m_chain.GetLocator(m_best_block_index));
219 23336 : return true;
220 23336 : }
221 :
222 16460 : bool BaseIndex::Rewind(const CBlockIndex* current_tip, const CBlockIndex* new_tip)
223 : {
224 16460 : assert(current_tip == m_best_block_index);
225 16460 : assert(current_tip->GetAncestor(new_tip->nHeight) == new_tip);
226 :
227 : // In the case of a reorg, ensure persisted block locator is not stale.
228 : // Pruning has a minimum of 288 blocks-to-keep and getting the index
229 : // out of sync may be possible but a users fault.
230 : // In case we reorg beyond the pruned depth, ReadBlockFromDisk would
231 : // throw and lead to a graceful shutdown
232 16460 : SetBestBlockIndex(new_tip);
233 16460 : if (!Commit()) {
234 : // If commit fails, revert the best block index to avoid corruption.
235 0 : SetBestBlockIndex(current_tip);
236 0 : return false;
237 : }
238 :
239 16460 : return true;
240 16460 : }
241 :
242 336041 : void BaseIndex::BlockConnected(const std::shared_ptr<const CBlock>& block, const CBlockIndex* pindex)
243 : {
244 336041 : if (!m_synced) {
245 14 : return;
246 : }
247 :
248 336027 : const CBlockIndex* best_block_index = m_best_block_index.load();
249 336027 : if (!best_block_index) {
250 976 : if (pindex->nHeight != 0) {
251 0 : FatalError("%s: First block connected is not the genesis block (height=%d)",
252 0 : __func__, pindex->nHeight);
253 0 : return;
254 : }
255 976 : } else {
256 : // Ensure block connects to an ancestor of the current best block. This should be the case
257 : // most of the time, but may not be immediately after the sync thread catches up and sets
258 : // m_synced. Consider the case where there is a reorg and the blocks on the stale branch are
259 : // in the ValidationInterface queue backlog even after the sync thread has caught up to the
260 : // new chain tip. In this unlikely event, log a warning and let the queue clear.
261 335051 : if (best_block_index->GetAncestor(pindex->nHeight - 1) != pindex->pprev) {
262 0 : LogPrintf("%s: WARNING: Block %s does not connect to an ancestor of " /* Continued */
263 : "known best chain (tip=%s); not updating index\n",
264 : __func__, pindex->GetBlockHash().ToString(),
265 : best_block_index->GetBlockHash().ToString());
266 0 : return;
267 : }
268 335051 : if (best_block_index != pindex->pprev && !Rewind(best_block_index, pindex->pprev)) {
269 0 : FatalError("%s: Failed to rewind index %s to a previous chain tip",
270 0 : __func__, GetName());
271 0 : return;
272 : }
273 : }
274 :
275 336027 : if (WriteBlock(*block, pindex)) {
276 : // Setting the best block index is intentionally the last step of this
277 : // function, so BlockUntilSyncedToCurrentChain callers waiting for the
278 : // best block index to be updated can rely on the block being fully
279 : // processed, and the index object being safe to delete.
280 336027 : SetBestBlockIndex(pindex);
281 336027 : } else {
282 0 : FatalError("%s: Failed to write block %s to index",
283 0 : __func__, pindex->GetBlockHash().ToString());
284 0 : return;
285 : }
286 336041 : }
287 :
288 16459 : void BaseIndex::BlockDisconnected(const std::shared_ptr<const CBlock>& block, const CBlockIndex* pindex)
289 : {
290 16459 : if (!m_synced) {
291 0 : return;
292 : }
293 :
294 16459 : const CBlockIndex* best_block_index = m_best_block_index.load();
295 :
296 : // Ignore stale-branch disconnect notifications that do not connect to the indexed chain.
297 : // We must check that pindex itself is on the indexed chain, not just that it shares
298 : // a parent — otherwise same-height siblings would incorrectly trigger a rewind.
299 16459 : if (best_block_index && best_block_index->nHeight >= pindex->nHeight && pindex->pprev) {
300 16459 : if (best_block_index->GetAncestor(pindex->nHeight) != pindex) {
301 0 : LogPrintf("%s: WARNING: Block %s is not on the indexed chain " /* Continued */
302 : "(tip=%s); not updating index\n",
303 : __func__, pindex->GetBlockHash().ToString(), best_block_index->GetBlockHash().ToString());
304 0 : return;
305 : }
306 16459 : if (!Rewind(best_block_index, pindex->pprev)) {
307 0 : FatalError("%s: Failed to rewind %s to previous block after disconnect", __func__, GetName());
308 0 : }
309 16459 : }
310 16459 : }
311 :
312 5364 : void BaseIndex::ChainStateFlushed(const CBlockLocator& locator)
313 : {
314 5364 : if (!m_synced) {
315 34 : return;
316 : }
317 :
318 5330 : const uint256& locator_tip_hash = locator.vHave.front();
319 : const CBlockIndex* locator_tip_index;
320 : {
321 5330 : LOCK(cs_main);
322 5330 : locator_tip_index = m_chainstate->m_blockman.LookupBlockIndex(locator_tip_hash);
323 5330 : }
324 :
325 5330 : if (!locator_tip_index) {
326 0 : FatalError("%s: First block (hash=%s) in locator was not found",
327 0 : __func__, locator_tip_hash.ToString());
328 0 : return;
329 : }
330 :
331 : // This checks that ChainStateFlushed callbacks are received after BlockConnected. The check may fail
332 : // immediately after the sync thread catches up and sets m_synced. Consider the case where
333 : // there is a reorg and the blocks on the stale branch are in the ValidationInterface queue
334 : // backlog even after the sync thread has caught up to the new chain tip. In this unlikely
335 : // event, log a warning and let the queue clear.
336 5330 : const CBlockIndex* best_block_index = m_best_block_index.load();
337 5330 : if (best_block_index->GetAncestor(locator_tip_index->nHeight) != locator_tip_index) {
338 0 : LogPrintf("%s: WARNING: Locator contains block (hash=%s) not on known best " /* Continued */
339 : "chain (tip=%s); not writing index locator\n",
340 : __func__, locator_tip_hash.ToString(),
341 : best_block_index->GetBlockHash().ToString());
342 0 : return;
343 : }
344 :
345 : // No need to handle errors in Commit. If it fails, the error will be already be logged. The
346 : // best way to recover is to continue, as index cannot be corrupted by a missed commit to disk
347 : // for an advanced index state.
348 5330 : Commit();
349 5364 : }
350 :
351 43866 : bool BaseIndex::BlockUntilSyncedToCurrentChain() const
352 : {
353 43866 : AssertLockNotHeld(cs_main);
354 :
355 43866 : if (!m_synced) {
356 185 : return false;
357 : }
358 :
359 : {
360 : // Skip the queue-draining stuff if we know we're caught up with
361 : // m_chain.Tip().
362 43681 : LOCK(cs_main);
363 43681 : const CBlockIndex* chain_tip = m_chainstate->m_chain.Tip();
364 43681 : const CBlockIndex* best_block_index = m_best_block_index.load();
365 43681 : if (best_block_index->GetAncestor(chain_tip->nHeight) == chain_tip) {
366 42312 : return true;
367 : }
368 43681 : }
369 :
370 1369 : LogPrintf("%s: %s is catching up on block notifications\n", __func__, GetName());
371 1369 : SyncWithValidationInterfaceQueue();
372 1369 : return true;
373 43866 : }
374 :
375 7251 : void BaseIndex::Interrupt()
376 : {
377 7251 : m_interrupt();
378 7251 : }
379 :
380 3626 : bool BaseIndex::Start(CChainState& active_chainstate)
381 : {
382 3626 : m_chainstate = &active_chainstate;
383 : // Need to register this ValidationInterface before running Init(), so that
384 : // callbacks are not missed if Init sets m_synced to true.
385 3626 : RegisterValidationInterface(this);
386 3626 : if (!Init()) {
387 0 : return false;
388 : }
389 :
390 7252 : m_thread_sync = std::thread(&util::TraceThread, GetName(), [this] { ThreadSync(); });
391 3626 : return true;
392 3626 : }
393 :
394 7255 : void BaseIndex::Stop()
395 : {
396 7255 : UnregisterValidationInterface(this);
397 :
398 7255 : if (m_thread_sync.joinable()) {
399 3626 : m_thread_sync.join();
400 3626 : }
401 7255 : }
402 :
403 855 : IndexSummary BaseIndex::GetSummary() const
404 : {
405 855 : IndexSummary summary{};
406 : summary.name = GetName();
407 : summary.synced = m_synced;
408 : summary.best_block_height = m_best_block_index ? m_best_block_index.load()->nHeight : 0;
409 : return summary;
410 : }
411 :
412 357659 : void BaseIndex::SetBestBlockIndex(const CBlockIndex* block) {
413 357659 : assert(!fPruneMode || AllowPrune());
414 :
415 357659 : if (AllowPrune() && block) {
416 99399 : PruneLockInfo prune_lock;
417 99399 : prune_lock.height_first = block->nHeight;
418 198798 : WITH_LOCK(::cs_main, m_chainstate->m_blockman.UpdatePruneLock(GetName(), prune_lock));
419 99399 : }
420 :
421 : // Intentionally set m_best_block_index as the last step in this function,
422 : // after updating prune locks above, and after making any other references
423 : // to *this, so the BlockUntilSyncedToCurrentChain function (which checks
424 : // m_best_block_index as an optimization) can be used to wait for the last
425 : // BlockConnected notification and safely assume that prune locks are
426 : // updated and that the index object is safe to delete.
427 357659 : m_best_block_index = block;
428 357659 : }
|