Bitcoin ABC  0.29.2
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 <config.h>
8 #include <index/base.h>
9 #include <node/blockstorage.h>
10 #include <node/ui_interface.h>
11 #include <shutdown.h>
12 #include <tinyformat.h>
13 #include <util/thread.h>
14 #include <util/translation.h>
15 #include <validation.h> // For Chainstate
16 #include <warnings.h>
17 
18 #include <functional>
19 
21 
22 constexpr uint8_t DB_BEST_BLOCK{'B'};
23 
24 constexpr int64_t SYNC_LOG_INTERVAL = 30; // secon
25 constexpr int64_t SYNC_LOCATOR_WRITE_INTERVAL = 30; // seconds
26 
27 template <typename... Args>
28 static void FatalError(const char *fmt, const Args &...args) {
29  std::string strMessage = tfm::format(fmt, args...);
30  SetMiscWarning(Untranslated(strMessage));
31  LogPrintf("*** %s\n", strMessage);
32  AbortError(_("A fatal internal error occurred, see debug.log for details"));
33  StartShutdown();
34 }
35 
36 BaseIndex::DB::DB(const fs::path &path, size_t n_cache_size, bool f_memory,
37  bool f_wipe, bool f_obfuscate)
38  : CDBWrapper(path, n_cache_size, f_memory, f_wipe, f_obfuscate) {}
39 
41  bool success = Read(DB_BEST_BLOCK, locator);
42  if (!success) {
43  locator.SetNull();
44  }
45  return success;
46 }
47 
49  const CBlockLocator &locator) {
50  batch.Write(DB_BEST_BLOCK, locator);
51 }
52 
54  Interrupt();
55  Stop();
56 }
57 
59  CBlockLocator locator;
60  if (!GetDB().ReadBestBlock(locator)) {
61  locator.SetNull();
62  }
63 
64  LOCK(cs_main);
65  CChain &active_chain = m_chainstate->m_chain;
66  if (locator.IsNull()) {
67  m_best_block_index = nullptr;
68  } else {
70  }
71  m_synced = m_best_block_index.load() == active_chain.Tip();
72  if (!m_synced) {
73  bool prune_violation = false;
74  if (!m_best_block_index) {
75  // index is not built yet
76  // make sure we have all block data back to the genesis
77  prune_violation = node::GetFirstStoredBlock(active_chain.Tip()) !=
78  active_chain.Genesis();
79  }
80  // in case the index has a best block set and is not fully synced
81  // check if we have the required blocks to continue building the index
82  else {
83  const CBlockIndex *block_to_test = m_best_block_index.load();
84  if (!active_chain.Contains(block_to_test)) {
85  // if the bestblock is not part of the mainchain, find the fork
86  // and make sure we have all data down to the fork
87  block_to_test = active_chain.FindFork(block_to_test);
88  }
89  const CBlockIndex *block = active_chain.Tip();
90  prune_violation = true;
91  // check backwards from the tip if we have all block data until we
92  // reach the indexes bestblock
93  while (block_to_test && block && block->nStatus.hasData()) {
94  if (block_to_test == block) {
95  prune_violation = false;
96  break;
97  }
98  // block->pprev must exist at this point, since block_to_test is
99  // part of the chain and thus must be encountered when going
100  // backwards from the tip
101  assert(block->pprev);
102  block = block->pprev;
103  }
104  }
105  if (prune_violation) {
106  return InitError(strprintf(
107  Untranslated("%s best block of the index goes beyond pruned "
108  "data. Please disable the index or reindex (which "
109  "will download the whole blockchain again)"),
110  GetName()));
111  }
112  }
113  return true;
114 }
115 
116 static const CBlockIndex *NextSyncBlock(const CBlockIndex *pindex_prev,
117  CChain &chain)
120 
121  if (!pindex_prev) {
122  return chain.Genesis();
123  }
124 
125  const CBlockIndex *pindex = chain.Next(pindex_prev);
126  if (pindex) {
127  return pindex;
128  }
129 
130  return chain.Next(chain.FindFork(pindex_prev));
131 }
132 
134  const CBlockIndex *pindex = m_best_block_index.load();
135  if (!m_synced) {
136  auto &consensus_params = GetConfig().GetChainParams().GetConsensus();
137 
138  int64_t last_log_time = 0;
139  int64_t last_locator_write_time = 0;
140  while (true) {
141  if (m_interrupt) {
142  m_best_block_index = pindex;
143  // No need to handle errors in Commit. If it fails, the error
144  // will be already be logged. The best way to recover is to
145  // continue, as index cannot be corrupted by a missed commit to
146  // disk for an advanced index state.
147  Commit();
148  return;
149  }
150 
151  {
152  LOCK(cs_main);
153  const CBlockIndex *pindex_next =
155  if (!pindex_next) {
156  m_best_block_index = pindex;
157  m_synced = true;
158  // No need to handle errors in Commit. See rationale above.
159  Commit();
160  break;
161  }
162  if (pindex_next->pprev != pindex &&
163  !Rewind(pindex, pindex_next->pprev)) {
164  FatalError(
165  "%s: Failed to rewind index %s to a previous chain tip",
166  __func__, GetName());
167  return;
168  }
169  pindex = pindex_next;
170  }
171 
172  int64_t current_time = GetTime();
173  if (last_log_time + SYNC_LOG_INTERVAL < current_time) {
174  LogPrintf("Syncing %s with block chain from height %d\n",
175  GetName(), pindex->nHeight);
176  last_log_time = current_time;
177  }
178 
179  if (last_locator_write_time + SYNC_LOCATOR_WRITE_INTERVAL <
180  current_time) {
181  m_best_block_index = pindex;
182  last_locator_write_time = current_time;
183  // No need to handle errors in Commit. See rationale above.
184  Commit();
185  }
186 
187  CBlock block;
188  if (!ReadBlockFromDisk(block, pindex, consensus_params)) {
189  FatalError("%s: Failed to read block %s from disk", __func__,
190  pindex->GetBlockHash().ToString());
191  return;
192  }
193  if (!WriteBlock(block, pindex)) {
194  FatalError("%s: Failed to write block %s to index database",
195  __func__, pindex->GetBlockHash().ToString());
196  return;
197  }
198  }
199  }
200 
201  if (pindex) {
202  LogPrintf("%s is enabled at height %d\n", GetName(), pindex->nHeight);
203  } else {
204  LogPrintf("%s is enabled\n", GetName());
205  }
206 }
207 
209  CDBBatch batch(GetDB());
210  if (!CommitInternal(batch) || !GetDB().WriteBatch(batch)) {
211  return error("%s: Failed to commit latest %s state", __func__,
212  GetName());
213  }
214  return true;
215 }
216 
218  LOCK(cs_main);
219  // Don't commit anything if we haven't indexed any block yet
220  // (this could happen if init is interrupted).
221  if (m_best_block_index == nullptr) {
222  return false;
223  }
225  return true;
226 }
227 
228 bool BaseIndex::Rewind(const CBlockIndex *current_tip,
229  const CBlockIndex *new_tip) {
230  assert(current_tip == m_best_block_index);
231  assert(current_tip->GetAncestor(new_tip->nHeight) == new_tip);
232 
233  // In the case of a reorg, ensure persisted block locator is not stale.
234  // Pruning has a minimum of 288 blocks-to-keep and getting the index
235  // out of sync may be possible but a users fault.
236  // In case we reorg beyond the pruned depth, ReadBlockFromDisk would
237  // throw and lead to a graceful shutdown
238  m_best_block_index = new_tip;
239  if (!Commit()) {
240  // If commit fails, revert the best block index to avoid corruption.
241  m_best_block_index = current_tip;
242  return false;
243  }
244 
245  return true;
246 }
247 
248 void BaseIndex::BlockConnected(const std::shared_ptr<const CBlock> &block,
249  const CBlockIndex *pindex) {
250  if (!m_synced) {
251  return;
252  }
253 
254  const CBlockIndex *best_block_index = m_best_block_index.load();
255  if (!best_block_index) {
256  if (pindex->nHeight != 0) {
257  FatalError("%s: First block connected is not the genesis block "
258  "(height=%d)",
259  __func__, pindex->nHeight);
260  return;
261  }
262  } else {
263  // Ensure block connects to an ancestor of the current best block. This
264  // should be the case most of the time, but may not be immediately after
265  // the the sync thread catches up and sets m_synced. Consider the case
266  // where there is a reorg and the blocks on the stale branch are in the
267  // ValidationInterface queue backlog even after the sync thread has
268  // caught up to the new chain tip. In this unlikely event, log a warning
269  // and let the queue clear.
270  if (best_block_index->GetAncestor(pindex->nHeight - 1) !=
271  pindex->pprev) {
272  LogPrintf("%s: WARNING: Block %s does not connect to an ancestor "
273  "of known best chain (tip=%s); not updating index\n",
274  __func__, pindex->GetBlockHash().ToString(),
275  best_block_index->GetBlockHash().ToString());
276  return;
277  }
278  if (best_block_index != pindex->pprev &&
279  !Rewind(best_block_index, pindex->pprev)) {
280  FatalError("%s: Failed to rewind index %s to a previous chain tip",
281  __func__, GetName());
282  return;
283  }
284  }
285 
286  if (WriteBlock(*block, pindex)) {
287  m_best_block_index = pindex;
288  } else {
289  FatalError("%s: Failed to write block %s to index", __func__,
290  pindex->GetBlockHash().ToString());
291  return;
292  }
293 }
294 
296  if (!m_synced) {
297  return;
298  }
299 
300  const BlockHash &locator_tip_hash = locator.vHave.front();
301  const CBlockIndex *locator_tip_index;
302  {
303  LOCK(cs_main);
304  locator_tip_index =
305  m_chainstate->m_blockman.LookupBlockIndex(locator_tip_hash);
306  }
307 
308  if (!locator_tip_index) {
309  FatalError("%s: First block (hash=%s) in locator was not found",
310  __func__, locator_tip_hash.ToString());
311  return;
312  }
313 
314  // This checks that ChainStateFlushed callbacks are received after
315  // BlockConnected. The check may fail immediately after the the sync thread
316  // catches up and sets m_synced. Consider the case where there is a reorg
317  // and the blocks on the stale branch are in the ValidationInterface queue
318  // backlog even after the sync thread has caught up to the new chain tip. In
319  // this unlikely event, log a warning and let the queue clear.
320  const CBlockIndex *best_block_index = m_best_block_index.load();
321  if (best_block_index->GetAncestor(locator_tip_index->nHeight) !=
322  locator_tip_index) {
323  LogPrintf("%s: WARNING: Locator contains block (hash=%s) not on known "
324  "best chain (tip=%s); not writing index locator\n",
325  __func__, locator_tip_hash.ToString(),
326  best_block_index->GetBlockHash().ToString());
327  return;
328  }
329 
330  // No need to handle errors in Commit. If it fails, the error will be
331  // already be logged. The best way to recover is to continue, as index
332  // cannot be corrupted by a missed commit to disk for an advanced index
333  // state.
334  Commit();
335 }
336 
337 bool BaseIndex::BlockUntilSyncedToCurrentChain() const {
339 
340  if (!m_synced) {
341  return false;
342  }
343 
344  {
345  // Skip the queue-draining stuff if we know we're caught up with
346  // m_chain.Tip().
347  LOCK(cs_main);
348  const CBlockIndex *chain_tip = m_chainstate->m_chain.Tip();
349  const CBlockIndex *best_block_index = m_best_block_index.load();
350  if (best_block_index->GetAncestor(chain_tip->nHeight) == chain_tip) {
351  return true;
352  }
353  }
354 
355  LogPrintf("%s: %s is catching up on block notifications\n", __func__,
356  GetName());
358  return true;
359 }
360 
361 void BaseIndex::Interrupt() {
362  m_interrupt();
363 }
364 
365 bool BaseIndex::Start(Chainstate &active_chainstate) {
366  m_chainstate = &active_chainstate;
367  // Need to register this ValidationInterface before running Init(), so that
368  // callbacks are not missed if Init sets m_synced to true.
370  if (!Init()) {
371  return false;
372  }
373 
374  m_thread_sync =
375  std::thread(&util::TraceThread, GetName(), [this] { ThreadSync(); });
376  return true;
377 }
378 
381 
382  if (m_thread_sync.joinable()) {
383  m_thread_sync.join();
384  }
385 }
386 
388  IndexSummary summary{};
389  summary.name = GetName();
390  summary.synced = m_synced;
391  summary.best_block_height =
392  m_best_block_index ? m_best_block_index.load()->nHeight : 0;
393  return summary;
394 }
constexpr int64_t SYNC_LOG_INTERVAL
Definition: base.cpp:24
static const CBlockIndex * NextSyncBlock(const CBlockIndex *pindex_prev, CChain &chain) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
Definition: base.cpp:116
constexpr uint8_t DB_BEST_BLOCK
Definition: base.cpp:22
static void FatalError(const char *fmt, const Args &...args)
Definition: base.cpp:28
constexpr int64_t SYNC_LOCATOR_WRITE_INTERVAL
Definition: base.cpp:25
CBlockLocator GetLocator(const CBlockIndex *index)
Get a locator for a block index entry.
Definition: chain.cpp:45
void WriteBestBlock(CDBBatch &batch, const CBlockLocator &locator)
Write block locator of the chain that the index is in sync with.
Definition: base.cpp:48
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:36
bool ReadBestBlock(CBlockLocator &locator) const
Read block locator of the chain that the index is in sync with.
Definition: base.cpp:40
void Stop()
Stops the instance from staying in sync with blockchain updates.
Definition: base.cpp:379
virtual bool Init()
Initialize internal state from the database and block index.
Definition: base.cpp:58
void BlockConnected(const std::shared_ptr< const CBlock > &block, const CBlockIndex *pindex) override
Notifies listeners of a block being connected.
Definition: base.cpp:248
bool Start(Chainstate &active_chainstate)
Start initializes the sync state and registers the instance as a ValidationInterface so that it stays...
Definition: base.cpp:365
virtual ~BaseIndex()
Destructor interrupts sync thread if running and blocks until it exits.
Definition: base.cpp:53
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:217
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:387
void ChainStateFlushed(const CBlockLocator &locator) override
Notifies listeners of the new active block chain on-disk.
Definition: base.cpp:295
std::thread m_thread_sync
Definition: base.h:57
bool Commit()
Write the current index state (eg.
Definition: base.cpp:208
virtual bool WriteBlock(const CBlock &block, const CBlockIndex *pindex)
Write update index entries for a newly connected block.
Definition: base.h:93
void ThreadSync()
Sync the index with the block index starting from the current best block.
Definition: base.cpp:133
virtual DB & GetDB() const =0
Chainstate * m_chainstate
Definition: base.h:80
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:228
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:26
CBlockIndex * pprev
pointer to the index of the predecessor of this block
Definition: blockindex.h:33
CBlockIndex * GetAncestor(int height)
Efficiently find an ancestor of this block.
Definition: blockindex.cpp:71
BlockHash GetBlockHash() const
Definition: blockindex.h:147
int nHeight
height of the entry in the chain. The genesis block has height 0
Definition: blockindex.h:39
An in-memory indexed chain of blocks.
Definition: chain.h:140
CBlockIndex * Genesis() const
Returns the index entry for the genesis block of this chain, or nullptr if none.
Definition: chain.h:149
CBlockIndex * Tip() const
Returns the index entry for the tip of this chain, or nullptr if none.
Definition: chain.h:156
const CBlockIndex * FindFork(const CBlockIndex *pindex) const
Find the last common block between this chain and a block index entry.
Definition: chain.cpp:53
bool Contains(const CBlockIndex *pindex) const
Efficiently check whether a block is present in this chain.
Definition: chain.h:172
const Consensus::Params & GetConsensus() const
Definition: chainparams.h:86
Batch of changes queued to be written to a CDBWrapper.
Definition: dbwrapper.h:54
void Write(const K &key, const V &value)
Definition: dbwrapper.h:79
Chainstate stores and provides an API to update our local knowledge of the current best chain.
Definition: validation.h:628
CChain m_chain
The current chain of blockheaders we consult and build on.
Definition: validation.h:737
node::BlockManager & m_blockman
Reference to a BlockManager instance which itself is shared across all Chainstate instances.
Definition: validation.h:701
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:118
virtual const CChainParams & GetChainParams() const =0
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
CBlockIndex * LookupBlockIndex(const BlockHash &hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
const Config & GetConfig()
Definition: config.cpp:34
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:193
#define LogPrintf(...)
Definition: logging.h:206
const CBlockIndex * GetFirstStoredBlock(const CBlockIndex *start_block)
bool ReadBlockFromDisk(CBlock &block, const FlatFilePos &pos, const Consensus::Params &params)
Functions for disk access for blocks.
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
std::string name
Definition: base.h:17
#define AssertLockNotHeld(cs)
Definition: sync.h:163
#define LOCK(cs)
Definition: sync.h:306
bool error(const char *fmt, const Args &...args)
Definition: system.h:45
#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