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 <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 CChainState
16 #include <warnings.h>
17 
18 #include <functional>
19 
21 
22 constexpr char 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  // throw error and graceful shutdown if we can't build the index
107  FatalError("%s: %s best block of the index goes beyond pruned "
108  "data. Please disable the index or reindex (which will "
109  "download the whole blockchain again)",
110  __func__, GetName());
111  return false;
112  }
113  }
114  return true;
115 }
116 
117 static const CBlockIndex *NextSyncBlock(const CBlockIndex *pindex_prev,
118  CChain &chain)
121 
122  if (!pindex_prev) {
123  return chain.Genesis();
124  }
125 
126  const CBlockIndex *pindex = chain.Next(pindex_prev);
127  if (pindex) {
128  return pindex;
129  }
130 
131  return chain.Next(chain.FindFork(pindex_prev));
132 }
133 
135  const CBlockIndex *pindex = m_best_block_index.load();
136  if (!m_synced) {
137  auto &consensus_params = GetConfig().GetChainParams().GetConsensus();
138 
139  int64_t last_log_time = 0;
140  int64_t last_locator_write_time = 0;
141  while (true) {
142  if (m_interrupt) {
143  m_best_block_index = pindex;
144  // No need to handle errors in Commit. If it fails, the error
145  // will be already be logged. The best way to recover is to
146  // continue, as index cannot be corrupted by a missed commit to
147  // disk for an advanced index state.
148  Commit();
149  return;
150  }
151 
152  {
153  LOCK(cs_main);
154  const CBlockIndex *pindex_next =
156  if (!pindex_next) {
157  m_best_block_index = pindex;
158  m_synced = true;
159  // No need to handle errors in Commit. See rationale above.
160  Commit();
161  break;
162  }
163  if (pindex_next->pprev != pindex &&
164  !Rewind(pindex, pindex_next->pprev)) {
165  FatalError(
166  "%s: Failed to rewind index %s to a previous chain tip",
167  __func__, GetName());
168  return;
169  }
170  pindex = pindex_next;
171  }
172 
173  int64_t current_time = GetTime();
174  if (last_log_time + SYNC_LOG_INTERVAL < current_time) {
175  LogPrintf("Syncing %s with block chain from height %d\n",
176  GetName(), pindex->nHeight);
177  last_log_time = current_time;
178  }
179 
180  if (last_locator_write_time + SYNC_LOCATOR_WRITE_INTERVAL <
181  current_time) {
182  m_best_block_index = pindex;
183  last_locator_write_time = current_time;
184  // No need to handle errors in Commit. See rationale above.
185  Commit();
186  }
187 
188  CBlock block;
189  if (!ReadBlockFromDisk(block, pindex, consensus_params)) {
190  FatalError("%s: Failed to read block %s from disk", __func__,
191  pindex->GetBlockHash().ToString());
192  return;
193  }
194  if (!WriteBlock(block, pindex)) {
195  FatalError("%s: Failed to write block %s to index database",
196  __func__, pindex->GetBlockHash().ToString());
197  return;
198  }
199  }
200  }
201 
202  if (pindex) {
203  LogPrintf("%s is enabled at height %d\n", GetName(), pindex->nHeight);
204  } else {
205  LogPrintf("%s is enabled\n", GetName());
206  }
207 }
208 
210  CDBBatch batch(GetDB());
211  if (!CommitInternal(batch) || !GetDB().WriteBatch(batch)) {
212  return error("%s: Failed to commit latest %s state", __func__,
213  GetName());
214  }
215  return true;
216 }
217 
219  LOCK(cs_main);
220  // Don't commit anything if we haven't indexed any block yet
221  // (this could happen if init is interrupted).
222  if (m_best_block_index == nullptr) {
223  return false;
224  }
227  return true;
228 }
229 
230 bool BaseIndex::Rewind(const CBlockIndex *current_tip,
231  const CBlockIndex *new_tip) {
232  assert(current_tip == m_best_block_index);
233  assert(current_tip->GetAncestor(new_tip->nHeight) == new_tip);
234 
235  // In the case of a reorg, ensure persisted block locator is not stale.
236  // Pruning has a minimum of 288 blocks-to-keep and getting the index
237  // out of sync may be possible but a users fault.
238  // In case we reorg beyond the pruned depth, ReadBlockFromDisk would
239  // throw and lead to a graceful shutdown
240  m_best_block_index = new_tip;
241  if (!Commit()) {
242  // If commit fails, revert the best block index to avoid corruption.
243  m_best_block_index = current_tip;
244  return false;
245  }
246 
247  return true;
248 }
249 
250 void BaseIndex::BlockConnected(const std::shared_ptr<const CBlock> &block,
251  const CBlockIndex *pindex) {
252  if (!m_synced) {
253  return;
254  }
255 
256  const CBlockIndex *best_block_index = m_best_block_index.load();
257  if (!best_block_index) {
258  if (pindex->nHeight != 0) {
259  FatalError("%s: First block connected is not the genesis block "
260  "(height=%d)",
261  __func__, pindex->nHeight);
262  return;
263  }
264  } else {
265  // Ensure block connects to an ancestor of the current best block. This
266  // should be the case most of the time, but may not be immediately after
267  // the the sync thread catches up and sets m_synced. Consider the case
268  // where there is a reorg and the blocks on the stale branch are in the
269  // ValidationInterface queue backlog even after the sync thread has
270  // caught up to the new chain tip. In this unlikely event, log a warning
271  // and let the queue clear.
272  if (best_block_index->GetAncestor(pindex->nHeight - 1) !=
273  pindex->pprev) {
274  LogPrintf("%s: WARNING: Block %s does not connect to an ancestor "
275  "of known best chain (tip=%s); not updating index\n",
276  __func__, pindex->GetBlockHash().ToString(),
277  best_block_index->GetBlockHash().ToString());
278  return;
279  }
280  if (best_block_index != pindex->pprev &&
281  !Rewind(best_block_index, pindex->pprev)) {
282  FatalError("%s: Failed to rewind index %s to a previous chain tip",
283  __func__, GetName());
284  return;
285  }
286  }
287 
288  if (WriteBlock(*block, pindex)) {
289  m_best_block_index = pindex;
290  } else {
291  FatalError("%s: Failed to write block %s to index", __func__,
292  pindex->GetBlockHash().ToString());
293  return;
294  }
295 }
296 
298  if (!m_synced) {
299  return;
300  }
301 
302  const BlockHash &locator_tip_hash = locator.vHave.front();
303  const CBlockIndex *locator_tip_index;
304  {
305  LOCK(cs_main);
306  locator_tip_index =
307  m_chainstate->m_blockman.LookupBlockIndex(locator_tip_hash);
308  }
309 
310  if (!locator_tip_index) {
311  FatalError("%s: First block (hash=%s) in locator was not found",
312  __func__, locator_tip_hash.ToString());
313  return;
314  }
315 
316  // This checks that ChainStateFlushed callbacks are received after
317  // BlockConnected. The check may fail immediately after the the sync thread
318  // catches up and sets m_synced. Consider the case where there is a reorg
319  // and the blocks on the stale branch are in the ValidationInterface queue
320  // backlog even after the sync thread has caught up to the new chain tip. In
321  // this unlikely event, log a warning and let the queue clear.
322  const CBlockIndex *best_block_index = m_best_block_index.load();
323  if (best_block_index->GetAncestor(locator_tip_index->nHeight) !=
324  locator_tip_index) {
325  LogPrintf("%s: WARNING: Locator contains block (hash=%s) not on known "
326  "best chain (tip=%s); not writing index locator\n",
327  __func__, locator_tip_hash.ToString(),
328  best_block_index->GetBlockHash().ToString());
329  return;
330  }
331 
332  // No need to handle errors in Commit. If it fails, the error will be
333  // already be logged. The best way to recover is to continue, as index
334  // cannot be corrupted by a missed commit to disk for an advanced index
335  // state.
336  Commit();
337 }
338 
339 bool BaseIndex::BlockUntilSyncedToCurrentChain() const {
341 
342  if (!m_synced) {
343  return false;
344  }
345 
346  {
347  // Skip the queue-draining stuff if we know we're caught up with
348  // m_chain.Tip().
349  LOCK(cs_main);
350  const CBlockIndex *chain_tip = m_chainstate->m_chain.Tip();
351  const CBlockIndex *best_block_index = m_best_block_index.load();
352  if (best_block_index->GetAncestor(chain_tip->nHeight) == chain_tip) {
353  return true;
354  }
355  }
356 
357  LogPrintf("%s: %s is catching up on block notifications\n", __func__,
358  GetName());
360  return true;
361 }
362 
363 void BaseIndex::Interrupt() {
364  m_interrupt();
365 }
366 
367 void BaseIndex::Start(CChainState &active_chainstate) {
368  m_chainstate = &active_chainstate;
369  // Need to register this ValidationInterface before running Init(), so that
370  // callbacks are not missed if Init sets m_synced to true.
372  if (!Init()) {
373  FatalError("%s: %s failed to initialize", __func__, GetName());
374  return;
375  }
376 
377  m_thread_sync =
378  std::thread(&util::TraceThread, GetName(), [this] { ThreadSync(); });
379 }
380 
383 
384  if (m_thread_sync.joinable()) {
385  m_thread_sync.join();
386  }
387 }
388 
390  IndexSummary summary{};
391  summary.name = GetName();
392  summary.synced = m_synced;
393  summary.best_block_height =
394  m_best_block_index ? m_best_block_index.load()->nHeight : 0;
395  return summary;
396 }
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:117
static void FatalError(const char *fmt, const Args &...args)
Definition: base.cpp:28
constexpr int64_t SYNC_LOCATOR_WRITE_INTERVAL
Definition: base.cpp:25
constexpr char DB_BEST_BLOCK
Definition: base.cpp:22
RecursiveMutex cs_main
Global state.
Definition: validation.cpp:111
void WriteBestBlock(CDBBatch &batch, const CBlockLocator &locator)
Write block locator of the chain that the txindex 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 txindex is in sync with.
Definition: base.cpp:40
void Start(CChainState &active_chainstate)
Start initializes the sync state and registers the instance as a ValidationInterface so that it stays...
Definition: base.cpp:367
void Stop()
Stops the instance from staying in sync with blockchain updates.
Definition: base.cpp:381
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:250
CChainState * m_chainstate
Definition: base.h:80
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:218
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:389
void ChainStateFlushed(const CBlockLocator &locator) override
Notifies listeners of the new active block chain on-disk.
Definition: base.cpp:297
std::thread m_thread_sync
Definition: base.h:57
bool Commit()
Write the current index state (eg.
Definition: base.cpp:209
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:134
virtual DB & GetDB() const =0
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:230
virtual const char * GetName() const =0
Get the name of the index for display in logs.
Definition: block.h:55
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:141
CBlockIndex * Genesis() const
Returns the index entry for the genesis block of this chain, or nullptr if none.
Definition: chain.h:150
CBlockIndex * Tip() const
Returns the index entry for the tip of this chain, or nullptr if none.
Definition: chain.h:157
CBlockLocator GetLocator(const CBlockIndex *pindex=nullptr) const
Return a CBlockLocator that refers to a block in this chain (by default the tip).
Definition: chain.cpp:21
const CBlockIndex * FindFork(const CBlockIndex *pindex) const
Find the last common block between this chain and a block index entry.
Definition: chain.cpp:52
bool Contains(const CBlockIndex *pindex) const
Efficiently check whether a block is present in this chain.
Definition: chain.h:173
const Consensus::Params & GetConsensus() const
Definition: chainparams.h:86
CChainState stores and provides an API to update our local knowledge of the current best chain.
Definition: validation.h:642
node::BlockManager & m_blockman
Reference to a BlockManager instance which itself is shared across all CChainState instances.
Definition: validation.h:692
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:131
CChain m_chain
The current chain of blockheaders we consult and build on.
Definition: validation.h:731
Batch of changes queued to be written to a CDBWrapper.
Definition: dbwrapper.h:48
void Write(const K &key, const V &value)
Definition: dbwrapper.h:73
virtual const CChainParams & GetChainParams() const =0
std::string ToString() const
Definition: uint256.h:78
Path class wrapper to prepare application code for transition from boost::filesystem library to std::...
Definition: fs.h:33
CBlockIndex * LookupBlockIndex(const BlockHash &hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
const Config & GetConfig()
Definition: config.cpp:34
void Interrupt(NodeContext &node)
Interrupt threads.
Definition: init.cpp:181
#define LogPrintf(...)
Definition: logging.h:204
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:1111
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:100
std::vector< BlockHash > vHave
Definition: block.h:101
bool IsNull() const
Definition: block.h:118
void SetNull()
Definition: block.h:116
std::string name
Definition: base.h:17
#define AssertLockNotHeld(cs)
Definition: sync.h:97
#define LOCK(cs)
Definition: sync.h:243
bool error(const char *fmt, const Args &...args)
Definition: system.h:46
#define EXCLUSIVE_LOCKS_REQUIRED(...)
Definition: threadsafety.h:56
T GetTime()
Return system time (or mocked time, if set)
Definition: time.cpp:71
bilingual_str _(const char *psz)
Translation function.
Definition: translation.h:55
bilingual_str Untranslated(std::string original)
Mark a bilingual_str as untranslated.
Definition: translation.h:36
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