Bitcoin ABC  0.26.3
P2P Digital Currency
base.cpp
Go to the documentation of this file.
1 // Copyright (c) 2017-2018 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 <chain.h>
6 #include <chainparams.h>
7 #include <common/args.h>
8 #include <config.h>
9 #include <index/base.h>
10 #include <logging.h>
11 #include <node/blockstorage.h>
12 #include <node/database_args.h>
13 #include <node/ui_interface.h>
14 #include <shutdown.h>
15 #include <tinyformat.h>
16 #include <util/thread.h>
17 #include <util/translation.h>
18 #include <validation.h> // For Chainstate
19 #include <warnings.h>
20 
21 #include <functional>
22 
23 constexpr uint8_t DB_BEST_BLOCK{'B'};
24 
25 constexpr int64_t SYNC_LOG_INTERVAL = 30; // secon
26 constexpr int64_t SYNC_LOCATOR_WRITE_INTERVAL = 30; // seconds
27 
28 template <typename... Args>
29 static void FatalError(const char *fmt, const Args &...args) {
30  std::string strMessage = tfm::format(fmt, args...);
31  SetMiscWarning(Untranslated(strMessage));
32  LogPrintf("*** %s\n", strMessage);
33  AbortError(_("A fatal internal error occurred, see debug.log for details"));
34  StartShutdown();
35 }
36 
37 BaseIndex::DB::DB(const fs::path &path, size_t n_cache_size, bool f_memory,
38  bool f_wipe, bool f_obfuscate)
39  : CDBWrapper{DBParams{.path = path,
40  .cache_bytes = n_cache_size,
41  .memory_only = f_memory,
42  .wipe_data = f_wipe,
43  .obfuscate = f_obfuscate,
44  .options = [] {
47  return options;
48  }()}} {}
49 
51  bool success = Read(DB_BEST_BLOCK, locator);
52  if (!success) {
53  locator.SetNull();
54  }
55  return success;
56 }
57 
59  const CBlockLocator &locator) {
60  batch.Write(DB_BEST_BLOCK, locator);
61 }
62 
64  Interrupt();
65  Stop();
66 }
67 
69  CBlockLocator locator;
70  if (!GetDB().ReadBestBlock(locator)) {
71  locator.SetNull();
72  }
73 
74  LOCK(cs_main);
75  CChain &active_chain = m_chainstate->m_chain;
76  if (locator.IsNull()) {
77  SetBestBlockIndex(nullptr);
78  } else {
80  }
81  m_synced = m_best_block_index.load() == active_chain.Tip();
82  if (!m_synced) {
83  bool prune_violation = false;
84  if (!m_best_block_index) {
85  // index is not built yet
86  // make sure we have all block data back to the genesis
87  prune_violation =
88  m_chainstate->m_blockman.GetFirstStoredBlock(
89  *active_chain.Tip()) != active_chain.Genesis();
90  }
91  // in case the index has a best block set and is not fully synced
92  // check if we have the required blocks to continue building the index
93  else {
94  const CBlockIndex *block_to_test = m_best_block_index.load();
95  if (!active_chain.Contains(block_to_test)) {
96  // if the bestblock is not part of the mainchain, find the fork
97  // and make sure we have all data down to the fork
98  block_to_test = active_chain.FindFork(block_to_test);
99  }
100  const CBlockIndex *block = active_chain.Tip();
101  prune_violation = true;
102  // check backwards from the tip if we have all block data until we
103  // reach the indexes bestblock
104  while (block_to_test && block && block->nStatus.hasData()) {
105  if (block_to_test == block) {
106  prune_violation = false;
107  break;
108  }
109  // block->pprev must exist at this point, since block_to_test is
110  // part of the chain and thus must be encountered when going
111  // backwards from the tip
112  assert(block->pprev);
113  block = block->pprev;
114  }
115  }
116  if (prune_violation) {
117  return InitError(strprintf(
118  Untranslated("%s best block of the index goes beyond pruned "
119  "data. Please disable the index or reindex (which "
120  "will download the whole blockchain again)"),
121  GetName()));
122  }
123  }
124  return true;
125 }
126 
127 static const CBlockIndex *NextSyncBlock(const CBlockIndex *pindex_prev,
128  CChain &chain)
131 
132  if (!pindex_prev) {
133  return chain.Genesis();
134  }
135 
136  const CBlockIndex *pindex = chain.Next(pindex_prev);
137  if (pindex) {
138  return pindex;
139  }
140 
141  return chain.Next(chain.FindFork(pindex_prev));
142 }
143 
145  const CBlockIndex *pindex = m_best_block_index.load();
146  if (!m_synced) {
147  int64_t last_log_time = 0;
148  int64_t last_locator_write_time = 0;
149  while (true) {
150  if (m_interrupt) {
151  SetBestBlockIndex(pindex);
152  // No need to handle errors in Commit. If it fails, the error
153  // will be already be logged. The best way to recover is to
154  // continue, as index cannot be corrupted by a missed commit to
155  // disk for an advanced index state.
156  Commit();
157  return;
158  }
159 
160  {
161  LOCK(cs_main);
162  const CBlockIndex *pindex_next =
164  if (!pindex_next) {
165  SetBestBlockIndex(pindex);
166  m_synced = true;
167  // No need to handle errors in Commit. See rationale above.
168  Commit();
169  break;
170  }
171  if (pindex_next->pprev != pindex &&
172  !Rewind(pindex, pindex_next->pprev)) {
173  FatalError(
174  "%s: Failed to rewind index %s to a previous chain tip",
175  __func__, GetName());
176  return;
177  }
178  pindex = pindex_next;
179  }
180 
181  int64_t current_time = GetTime();
182  if (last_log_time + SYNC_LOG_INTERVAL < current_time) {
183  LogPrintf("Syncing %s with block chain from height %d\n",
184  GetName(), pindex->nHeight);
185  last_log_time = current_time;
186  }
187 
188  if (last_locator_write_time + SYNC_LOCATOR_WRITE_INTERVAL <
189  current_time) {
190  SetBestBlockIndex(pindex);
191  last_locator_write_time = current_time;
192  // No need to handle errors in Commit. See rationale above.
193  Commit();
194  }
195 
196  CBlock block;
197  if (!m_chainstate->m_blockman.ReadBlockFromDisk(block, *pindex)) {
198  FatalError("%s: Failed to read block %s from disk", __func__,
199  pindex->GetBlockHash().ToString());
200  return;
201  }
202  if (!WriteBlock(block, pindex)) {
203  FatalError("%s: Failed to write block %s to index database",
204  __func__, pindex->GetBlockHash().ToString());
205  return;
206  }
207  }
208  }
209 
210  if (pindex) {
211  LogPrintf("%s is enabled at height %d\n", GetName(), pindex->nHeight);
212  } else {
213  LogPrintf("%s is enabled\n", GetName());
214  }
215 }
216 
218  CDBBatch batch(GetDB());
219  if (!CommitInternal(batch) || !GetDB().WriteBatch(batch)) {
220  return error("%s: Failed to commit latest %s state", __func__,
221  GetName());
222  }
223  return true;
224 }
225 
227  LOCK(cs_main);
228  // Don't commit anything if we haven't indexed any block yet
229  // (this could happen if init is interrupted).
230  if (m_best_block_index == nullptr) {
231  return false;
232  }
234  return true;
235 }
236 
237 bool BaseIndex::Rewind(const CBlockIndex *current_tip,
238  const CBlockIndex *new_tip) {
239  assert(current_tip == m_best_block_index);
240  assert(current_tip->GetAncestor(new_tip->nHeight) == new_tip);
241 
242  // In the case of a reorg, ensure persisted block locator is not stale.
243  // Pruning has a minimum of 288 blocks-to-keep and getting the index
244  // out of sync may be possible but a users fault.
245  // In case we reorg beyond the pruned depth, ReadBlockFromDisk would
246  // throw and lead to a graceful shutdown
247  SetBestBlockIndex(new_tip);
248  if (!Commit()) {
249  // If commit fails, revert the best block index to avoid corruption.
250  SetBestBlockIndex(current_tip);
251  return false;
252  }
253 
254  return true;
255 }
256 
257 void BaseIndex::BlockConnected(const std::shared_ptr<const CBlock> &block,
258  const CBlockIndex *pindex) {
259  if (!m_synced) {
260  return;
261  }
262 
263  const CBlockIndex *best_block_index = m_best_block_index.load();
264  if (!best_block_index) {
265  if (pindex->nHeight != 0) {
266  FatalError("%s: First block connected is not the genesis block "
267  "(height=%d)",
268  __func__, pindex->nHeight);
269  return;
270  }
271  } else {
272  // Ensure block connects to an ancestor of the current best block. This
273  // should be the case most of the time, but may not be immediately after
274  // the the sync thread catches up and sets m_synced. Consider the case
275  // where there is a reorg and the blocks on the stale branch are in the
276  // ValidationInterface queue backlog even after the sync thread has
277  // caught up to the new chain tip. In this unlikely event, log a warning
278  // and let the queue clear.
279  if (best_block_index->GetAncestor(pindex->nHeight - 1) !=
280  pindex->pprev) {
281  LogPrintf("%s: WARNING: Block %s does not connect to an ancestor "
282  "of known best chain (tip=%s); not updating index\n",
283  __func__, pindex->GetBlockHash().ToString(),
284  best_block_index->GetBlockHash().ToString());
285  return;
286  }
287  if (best_block_index != pindex->pprev &&
288  !Rewind(best_block_index, pindex->pprev)) {
289  FatalError("%s: Failed to rewind index %s to a previous chain tip",
290  __func__, GetName());
291  return;
292  }
293  }
294 
295  if (WriteBlock(*block, pindex)) {
296  // Setting the best block index is intentionally the last step of this
297  // function, so BlockUntilSyncedToCurrentChain callers waiting for the
298  // best block index to be updated can rely on the block being fully
299  // processed, and the index object being safe to delete.
300  SetBestBlockIndex(pindex);
301  } else {
302  FatalError("%s: Failed to write block %s to index", __func__,
303  pindex->GetBlockHash().ToString());
304  return;
305  }
306 }
307 
309  if (!m_synced) {
310  return;
311  }
312 
313  const BlockHash &locator_tip_hash = locator.vHave.front();
314  const CBlockIndex *locator_tip_index;
315  {
316  LOCK(cs_main);
317  locator_tip_index =
318  m_chainstate->m_blockman.LookupBlockIndex(locator_tip_hash);
319  }
320 
321  if (!locator_tip_index) {
322  FatalError("%s: First block (hash=%s) in locator was not found",
323  __func__, locator_tip_hash.ToString());
324  return;
325  }
326 
327  // This checks that ChainStateFlushed callbacks are received after
328  // BlockConnected. The check may fail immediately after the the sync thread
329  // catches up and sets m_synced. Consider the case where there is a reorg
330  // and the blocks on the stale branch are in the ValidationInterface queue
331  // backlog even after the sync thread has caught up to the new chain tip. In
332  // this unlikely event, log a warning and let the queue clear.
333  const CBlockIndex *best_block_index = m_best_block_index.load();
334  if (best_block_index->GetAncestor(locator_tip_index->nHeight) !=
335  locator_tip_index) {
336  LogPrintf("%s: WARNING: Locator contains block (hash=%s) not on known "
337  "best chain (tip=%s); not writing index locator\n",
338  __func__, locator_tip_hash.ToString(),
339  best_block_index->GetBlockHash().ToString());
340  return;
341  }
342 
343  // No need to handle errors in Commit. If it fails, the error will be
344  // already be logged. The best way to recover is to continue, as index
345  // cannot be corrupted by a missed commit to disk for an advanced index
346  // state.
347  Commit();
348 }
349 
350 bool BaseIndex::BlockUntilSyncedToCurrentChain() const {
352 
353  if (!m_synced) {
354  return false;
355  }
356 
357  {
358  // Skip the queue-draining stuff if we know we're caught up with
359  // m_chain.Tip().
360  LOCK(cs_main);
361  const CBlockIndex *chain_tip = m_chainstate->m_chain.Tip();
362  const CBlockIndex *best_block_index = m_best_block_index.load();
363  if (best_block_index->GetAncestor(chain_tip->nHeight) == chain_tip) {
364  return true;
365  }
366  }
367 
368  LogPrintf("%s: %s is catching up on block notifications\n", __func__,
369  GetName());
371  return true;
372 }
373 
374 void BaseIndex::Interrupt() {
375  m_interrupt();
376 }
377 
378 bool BaseIndex::Start(Chainstate &active_chainstate) {
379  m_chainstate = &active_chainstate;
380  // Need to register this ValidationInterface before running Init(), so that
381  // callbacks are not missed if Init sets m_synced to true.
383  if (!Init()) {
384  return false;
385  }
386 
387  m_thread_sync =
388  std::thread(&util::TraceThread, GetName(), [this] { ThreadSync(); });
389  return true;
390 }
391 
394 
395  if (m_thread_sync.joinable()) {
396  m_thread_sync.join();
397  }
398 }
399 
401  IndexSummary summary{};
402  summary.name = GetName();
403  summary.synced = m_synced;
404  summary.best_block_height =
405  m_best_block_index ? m_best_block_index.load()->nHeight : 0;
406  return summary;
407 }
408 
411 
412  if (AllowPrune() && block) {
413  node::PruneLockInfo prune_lock;
414  prune_lock.height_first = block->nHeight;
415  WITH_LOCK(::cs_main, m_chainstate->m_blockman.UpdatePruneLock(
416  GetName(), prune_lock));
417  }
418 
419  // Intentionally set m_best_block_index as the last step in this function,
420  // after updating prune locks above, and after making any other references
421  // to *this, so the BlockUntilSyncedToCurrentChain function (which checks
422  // m_best_block_index as an optimization) can be used to wait for the last
423  // BlockConnected notification and safely assume that prune locks are
424  // updated and that the index object is safe to delete.
425  m_best_block_index = block;
426 }
ArgsManager gArgs
Definition: args.cpp:38
constexpr int64_t SYNC_LOG_INTERVAL
Definition: base.cpp:25
static const CBlockIndex * NextSyncBlock(const CBlockIndex *pindex_prev, CChain &chain) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
Definition: base.cpp:127
constexpr uint8_t DB_BEST_BLOCK
Definition: base.cpp:23
static void FatalError(const char *fmt, const Args &...args)
Definition: base.cpp:29
constexpr int64_t SYNC_LOCATOR_WRITE_INTERVAL
Definition: base.cpp:26
CBlockLocator GetLocator(const CBlockIndex *index)
Get a locator for a block index entry.
Definition: chain.cpp:41
void WriteBestBlock(CDBBatch &batch, const CBlockLocator &locator)
Write block locator of the chain that the index is in sync with.
Definition: base.cpp:58
DB(const fs::path &path, size_t n_cache_size, bool f_memory=false, bool f_wipe=false, bool f_obfuscate=false)
Definition: base.cpp:37
bool ReadBestBlock(CBlockLocator &locator) const
Read block locator of the chain that the index is in sync with.
Definition: base.cpp:50
void Stop()
Stops the instance from staying in sync with blockchain updates.
Definition: base.cpp:392
void SetBestBlockIndex(const CBlockIndex *block)
Update the internal best block index as well as the prune lock.
Definition: base.cpp:409
virtual bool Init()
Initialize internal state from the database and block index.
Definition: base.cpp:68
void BlockConnected(const std::shared_ptr< const CBlock > &block, const CBlockIndex *pindex) override
Notifies listeners of a block being connected.
Definition: base.cpp:257
bool Start(Chainstate &active_chainstate)
Start initializes the sync state and registers the instance as a ValidationInterface so that it stays...
Definition: base.cpp:378
virtual ~BaseIndex()
Destructor interrupts sync thread if running and blocks until it exits.
Definition: base.cpp:63
std::atomic< const CBlockIndex * > m_best_block_index
The last block in the chain that the index is in sync with.
Definition: base.h:55
virtual bool CommitInternal(CDBBatch &batch)
Virtual method called internally by Commit that can be overridden to atomically commit more index sta...
Definition: base.cpp:226
virtual bool AllowPrune() const =0
std::atomic< bool > m_synced
Whether the index is in sync with the main chain.
Definition: base.h:52
CThreadInterrupt m_interrupt
Definition: base.h:58
IndexSummary GetSummary() const
Get a summary of the index and its state.
Definition: base.cpp:400
void ChainStateFlushed(const CBlockLocator &locator) override
Notifies listeners of the new active block chain on-disk.
Definition: base.cpp:308
std::thread m_thread_sync
Definition: base.h:57
bool Commit()
Write the current index state (eg.
Definition: base.cpp:217
virtual bool WriteBlock(const CBlock &block, const CBlockIndex *pindex)
Write update index entries for a newly connected block.
Definition: base.h:95
void ThreadSync()
Sync the index with the block index starting from the current best block.
Definition: base.cpp:144
virtual DB & GetDB() const =0
Chainstate * m_chainstate
Definition: base.h:82
virtual bool Rewind(const CBlockIndex *current_tip, const CBlockIndex *new_tip)
Rewind index to an earlier chain tip during a chain reorg.
Definition: base.cpp:237
virtual const char * GetName() const =0
Get the name of the index for display in logs.
Definition: block.h:60
The block chain is a tree shaped structure starting with the genesis block at the root,...
Definition: blockindex.h:25
CBlockIndex * pprev
pointer to the index of the predecessor of this block
Definition: blockindex.h:32
CBlockIndex * GetAncestor(int height)
Efficiently find an ancestor of this block.
Definition: blockindex.cpp:78
BlockHash GetBlockHash() const
Definition: blockindex.h:146
int nHeight
height of the entry in the chain. The genesis block has height 0
Definition: blockindex.h:38
An in-memory indexed chain of blocks.
Definition: chain.h:134
CBlockIndex * Genesis() const
Returns the index entry for the genesis block of this chain, or nullptr if none.
Definition: chain.h:143
CBlockIndex * Tip() const
Returns the index entry for the tip of this chain, or nullptr if none.
Definition: chain.h:150
const CBlockIndex * FindFork(const CBlockIndex *pindex) const
Find the last common block between this chain and a block index entry.
Definition: chain.cpp:49
bool Contains(const CBlockIndex *pindex) const
Efficiently check whether a block is present in this chain.
Definition: chain.h:166
Batch of changes queued to be written to a CDBWrapper.
Definition: dbwrapper.h:78
void Write(const K &key, const V &value)
Definition: dbwrapper.h:103
leveldb::Options options
database options used
Definition: dbwrapper.h:210
Chainstate stores and provides an API to update our local knowledge of the current best chain.
Definition: validation.h:695
CChain m_chain
The current chain of blockheaders we consult and build on.
Definition: validation.h:804
node::BlockManager & m_blockman
Reference to a BlockManager instance which itself is shared across all Chainstate instances.
Definition: validation.h:768
const CBlockIndex * FindForkInGlobalIndex(const CBlockLocator &locator) const EXCLUSIVE_LOCKS_REQUIRED(cs_main)
Find the last common block of this chain and a locator.
Definition: validation.cpp:122
std::string ToString() const
Definition: uint256.h:80
Path class wrapper to block calls to the fs::path(std::string) implicit constructor and the fs::path:...
Definition: fs.h:30
bool ReadBlockFromDisk(CBlock &block, const FlatFilePos &pos) const
Functions for disk access for blocks.
CBlockIndex * LookupBlockIndex(const BlockHash &hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
bool IsPruneMode() const
Whether running in -prune mode.
Definition: blockstorage.h:235
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate.
Definition: cs_main.cpp:7
void Interrupt(NodeContext &node)
Interrupt threads.
Definition: init.cpp:198
bool error(const char *fmt, const Args &...args)
Definition: logging.h:226
#define LogPrintf(...)
Definition: logging.h:207
void ReadDatabaseArgs(const ArgsManager &args, DBOptions &options)
void format(std::ostream &out, const char *fmt, const Args &...args)
Format list of arguments to the stream according to given format string.
Definition: tinyformat.h:1112
void TraceThread(const char *thread_name, std::function< void()> thread_func)
A wrapper for do-something-once thread functions.
Definition: thread.cpp:13
void StartShutdown()
Request shutdown of the application.
Definition: shutdown.cpp:55
A BlockHash is a unqiue identifier for a block.
Definition: blockhash.h:13
Describes a place in the block chain to another node such that if the other node doesn't have the sam...
Definition: block.h:105
std::vector< BlockHash > vHave
Definition: block.h:106
bool IsNull() const
Definition: block.h:123
void SetNull()
Definition: block.h:121
User-controlled performance and debug options.
Definition: dbwrapper.h:26
Application-specific storage settings.
Definition: dbwrapper.h:32
std::string name
Definition: base.h:17
int height_first
Height of earliest block that should be kept and not pruned.
Definition: blockstorage.h:64
#define AssertLockNotHeld(cs)
Definition: sync.h:163
#define LOCK(cs)
Definition: sync.h:306
#define WITH_LOCK(cs, code)
Run code while locking a mutex.
Definition: sync.h:357
#define EXCLUSIVE_LOCKS_REQUIRED(...)
Definition: threadsafety.h:56
int64_t GetTime()
Definition: time.cpp:109
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1202
bilingual_str _(const char *psz)
Translation function.
Definition: translation.h:68
bilingual_str Untranslated(std::string original)
Mark a bilingual_str as untranslated.
Definition: translation.h:36
bool InitError(const bilingual_str &str)
Show error message.
constexpr auto AbortError
Definition: ui_interface.h:141
AssertLockHeld(pool.cs)
assert(!tx.IsCoinBase())
void UnregisterValidationInterface(CValidationInterface *callbacks)
Unregister subscriber.
void RegisterValidationInterface(CValidationInterface *callbacks)
Register subscriber.
void SyncWithValidationInterfaceQueue()
This is a synonym for the following, which asserts certain locks are not held: std::promise<void> pro...
void SetMiscWarning(const bilingual_str &warning)
Definition: warnings.cpp:21