Bitcoin ABC 0.31.0
P2P Digital Currency
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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 <interfaces/chain.h>
11#include <logging.h>
12#include <node/blockstorage.h>
13#include <node/context.h>
14#include <node/database_args.h>
15#include <node/ui_interface.h>
16#include <shutdown.h>
17#include <tinyformat.h>
18#include <util/thread.h>
19#include <util/translation.h>
20#include <validation.h> // For Chainstate
21#include <warnings.h>
22
23#include <functional>
24#include <string>
25#include <utility>
26
27constexpr uint8_t DB_BEST_BLOCK{'B'};
28
29constexpr int64_t SYNC_LOG_INTERVAL = 30; // secon
30constexpr int64_t SYNC_LOCATOR_WRITE_INTERVAL = 30; // seconds
31
32template <typename... Args>
33static void FatalError(const char *fmt, const Args &...args) {
34 std::string strMessage = tfm::format(fmt, args...);
35 SetMiscWarning(Untranslated(strMessage));
36 LogPrintf("*** %s\n", strMessage);
37 AbortError(_("A fatal internal error occurred, see debug.log for details"));
39}
40
41BaseIndex::DB::DB(const fs::path &path, size_t n_cache_size, bool f_memory,
42 bool f_wipe, bool f_obfuscate)
43 : CDBWrapper{DBParams{.path = path,
44 .cache_bytes = n_cache_size,
45 .memory_only = f_memory,
46 .wipe_data = f_wipe,
47 .obfuscate = f_obfuscate,
48 .options = [] {
51 return options;
52 }()}} {}
53
55 bool success = Read(DB_BEST_BLOCK, locator);
56 if (!success) {
57 locator.SetNull();
58 }
59 return success;
60}
61
63 const CBlockLocator &locator) {
64 batch.Write(DB_BEST_BLOCK, locator);
65}
66
67BaseIndex::BaseIndex(std::unique_ptr<interfaces::Chain> chain, std::string name)
68 : m_chain{std::move(chain)}, m_name{std::move(name)} {}
69
71 Interrupt();
72 Stop();
73}
74
76 // m_chainstate member gives indexing code access to node internals. It is
77 // removed in followup https://github.com/bitcoin/bitcoin/pull/24230
78 m_chainstate = &m_chain->context()->chainman->ActiveChainstate();
79 // Register to validation interface before setting the 'm_synced' flag, so
80 // that callbacks are not missed once m_synced is true.
82
83 CBlockLocator locator;
84 if (!GetDB().ReadBestBlock(locator)) {
85 locator.SetNull();
86 }
87
89 CChain &active_chain = m_chainstate->m_chain;
90 if (locator.IsNull()) {
91 SetBestBlockIndex(nullptr);
92 } else {
93 // Setting the best block to the locator's top block. If it is not part
94 // of the best chain, we will rewind to the fork point during index sync
95 const CBlockIndex *locator_index{
97 if (!locator_index) {
98 return InitError(
99 strprintf(Untranslated("%s: best block of the index not found. "
100 "Please rebuild the index."),
101 GetName()));
102 }
103 SetBestBlockIndex(locator_index);
104 }
105
106 // Child init
107 const CBlockIndex *start_block = m_best_block_index.load();
108 if (!CustomInit(start_block ? std::make_optional(interfaces::BlockKey{
109 start_block->GetBlockHash(),
110 start_block->nHeight})
111 : std::nullopt)) {
112 return false;
113 }
114
115 // Note: this will latch to true immediately if the user starts up with an
116 // empty datadir and an index enabled. If this is the case, indexation will
117 // happen solely via `BlockConnected` signals until, possibly, the next
118 // restart.
119 m_synced = start_block == active_chain.Tip();
120 m_init = true;
121 return true;
122}
123
124static const CBlockIndex *NextSyncBlock(const CBlockIndex *pindex_prev,
125 CChain &chain)
128
129 if (!pindex_prev) {
130 return chain.Genesis();
131 }
132
133 const CBlockIndex *pindex = chain.Next(pindex_prev);
134 if (pindex) {
135 return pindex;
136 }
137
138 return chain.Next(chain.FindFork(pindex_prev));
139}
140
142 const CBlockIndex *pindex = m_best_block_index.load();
143 if (!m_synced) {
144 int64_t last_log_time = 0;
145 int64_t last_locator_write_time = 0;
146 while (true) {
147 if (m_interrupt) {
148 SetBestBlockIndex(pindex);
149 // No need to handle errors in Commit. If it fails, the error
150 // will be already be logged. The best way to recover is to
151 // continue, as index cannot be corrupted by a missed commit to
152 // disk for an advanced index state.
153 Commit();
154 return;
155 }
156
157 {
158 LOCK(cs_main);
159 const CBlockIndex *pindex_next =
161 if (!pindex_next) {
162 SetBestBlockIndex(pindex);
163 m_synced = true;
164 // No need to handle errors in Commit. See rationale above.
165 Commit();
166 break;
167 }
168 if (pindex_next->pprev != pindex &&
169 !Rewind(pindex, pindex_next->pprev)) {
171 "%s: Failed to rewind index %s to a previous chain tip",
172 __func__, GetName());
173 return;
174 }
175 pindex = pindex_next;
176 }
177
178 CBlock block;
179 if (!m_chainstate->m_blockman.ReadBlockFromDisk(block, *pindex)) {
180 FatalError("%s: Failed to read block %s from disk", __func__,
181 pindex->GetBlockHash().ToString());
182 return;
183 }
184 if (!WriteBlock(block, pindex)) {
185 FatalError("%s: Failed to write block %s to index database",
186 __func__, pindex->GetBlockHash().ToString());
187 return;
188 }
189
190 int64_t current_time = GetTime();
191 if (last_log_time + SYNC_LOG_INTERVAL < current_time) {
192 LogPrintf("Syncing %s with block chain from height %d\n",
193 GetName(), pindex->nHeight);
194 last_log_time = current_time;
195 }
196
197 if (last_locator_write_time + SYNC_LOCATOR_WRITE_INTERVAL <
198 current_time) {
199 SetBestBlockIndex(pindex->pprev);
200 last_locator_write_time = current_time;
201 // No need to handle errors in Commit. See rationale above.
202 Commit();
203 }
204 }
205 }
206
207 if (pindex) {
208 LogPrintf("%s is enabled at height %d\n", GetName(), pindex->nHeight);
209 } else {
210 LogPrintf("%s is enabled\n", GetName());
211 }
212}
213
215 CDBBatch batch(GetDB());
216 if (!CommitInternal(batch) || !GetDB().WriteBatch(batch)) {
217 return error("%s: Failed to commit latest %s state", __func__,
218 GetName());
219 }
220 return true;
221}
222
224 LOCK(cs_main);
225 // Don't commit anything if we haven't indexed any block yet
226 // (this could happen if init is interrupted).
227 if (m_best_block_index == nullptr) {
228 return false;
229 }
231 return true;
232}
233
234bool BaseIndex::Rewind(const CBlockIndex *current_tip,
235 const CBlockIndex *new_tip) {
236 assert(current_tip == m_best_block_index);
237 assert(current_tip->GetAncestor(new_tip->nHeight) == new_tip);
238
239 // In the case of a reorg, ensure persisted block locator is not stale.
240 // Pruning has a minimum of 288 blocks-to-keep and getting the index
241 // out of sync may be possible but a users fault.
242 // In case we reorg beyond the pruned depth, ReadBlockFromDisk would
243 // throw and lead to a graceful shutdown
244 SetBestBlockIndex(new_tip);
245 if (!Commit()) {
246 // If commit fails, revert the best block index to avoid corruption.
247 SetBestBlockIndex(current_tip);
248 return false;
249 }
250
251 return true;
252}
253
255 const std::shared_ptr<const CBlock> &block,
256 const CBlockIndex *pindex) {
257 if (!m_synced) {
258 return;
259 }
260
261 const CBlockIndex *best_block_index = m_best_block_index.load();
262 if (!best_block_index) {
263 if (pindex->nHeight != 0) {
264 FatalError("%s: First block connected is not the genesis block "
265 "(height=%d)",
266 __func__, pindex->nHeight);
267 return;
268 }
269 } else {
270 // Ensure block connects to an ancestor of the current best block. This
271 // should be the case most of the time, but may not be immediately after
272 // the the sync thread catches up and sets m_synced. Consider the case
273 // where there is a reorg and the blocks on the stale branch are in the
274 // ValidationInterface queue backlog even after the sync thread has
275 // caught up to the new chain tip. In this unlikely event, log a warning
276 // and let the queue clear.
277 if (best_block_index->GetAncestor(pindex->nHeight - 1) !=
278 pindex->pprev) {
279 LogPrintf("%s: WARNING: Block %s does not connect to an ancestor "
280 "of known best chain (tip=%s); not updating index\n",
281 __func__, pindex->GetBlockHash().ToString(),
282 best_block_index->GetBlockHash().ToString());
283 return;
284 }
285 if (best_block_index != pindex->pprev &&
286 !Rewind(best_block_index, pindex->pprev)) {
287 FatalError("%s: Failed to rewind index %s to a previous chain tip",
288 __func__, GetName());
289 return;
290 }
291 }
292
293 if (WriteBlock(*block, pindex)) {
294 // Setting the best block index is intentionally the last step of this
295 // function, so BlockUntilSyncedToCurrentChain callers waiting for the
296 // best block index to be updated can rely on the block being fully
297 // processed, and the index object being safe to delete.
298 SetBestBlockIndex(pindex);
299 } else {
300 FatalError("%s: Failed to write block %s to index", __func__,
301 pindex->GetBlockHash().ToString());
302 return;
303 }
304}
305
307 const CBlockLocator &locator) {
308 if (!m_synced) {
309 return;
310 }
311
312 const BlockHash &locator_tip_hash = locator.vHave.front();
313 const CBlockIndex *locator_tip_index;
314 {
315 LOCK(cs_main);
316 locator_tip_index =
317 m_chainstate->m_blockman.LookupBlockIndex(locator_tip_hash);
318 }
319
320 if (!locator_tip_index) {
321 FatalError("%s: First block (hash=%s) in locator was not found",
322 __func__, locator_tip_hash.ToString());
323 return;
324 }
325
326 // This checks that ChainStateFlushed callbacks are received after
327 // BlockConnected. The check may fail immediately after the the sync thread
328 // catches up and sets m_synced. Consider the case where there is a reorg
329 // and the blocks on the stale branch are in the ValidationInterface queue
330 // backlog even after the sync thread has caught up to the new chain tip. In
331 // this unlikely event, log a warning and let the queue clear.
332 const CBlockIndex *best_block_index = m_best_block_index.load();
333 if (best_block_index->GetAncestor(locator_tip_index->nHeight) !=
334 locator_tip_index) {
335 LogPrintf("%s: WARNING: Locator contains block (hash=%s) not on known "
336 "best chain (tip=%s); not writing index locator\n",
337 __func__, locator_tip_hash.ToString(),
338 best_block_index->GetBlockHash().ToString());
339 return;
340 }
341
342 // No need to handle errors in Commit. If it fails, the error will be
343 // already be logged. The best way to recover is to continue, as index
344 // cannot be corrupted by a missed commit to disk for an advanced index
345 // state.
346 Commit();
347}
348
349bool BaseIndex::BlockUntilSyncedToCurrentChain() const {
351
352 if (!m_synced) {
353 return false;
354 }
355
356 {
357 // Skip the queue-draining stuff if we know we're caught up with
358 // m_chain.Tip().
359 LOCK(cs_main);
360 const CBlockIndex *chain_tip = m_chainstate->m_chain.Tip();
361 const CBlockIndex *best_block_index = m_best_block_index.load();
362 if (best_block_index->GetAncestor(chain_tip->nHeight) == chain_tip) {
363 return true;
364 }
365 }
366
367 LogPrintf("%s: %s is catching up on block notifications\n", __func__,
368 GetName());
370 return true;
371}
372
374 m_interrupt();
375}
376
378 if (!m_init) {
379 throw std::logic_error("Error: Cannot start a non-initialized index");
380 }
381
383 std::thread(&util::TraceThread, GetName(), [this] { ThreadSync(); });
384 return true;
385}
386
389
390 if (m_thread_sync.joinable()) {
391 m_thread_sync.join();
392 }
393}
394
396 IndexSummary summary{};
397 summary.name = GetName();
398 summary.synced = m_synced;
399 if (const auto &pindex = m_best_block_index.load()) {
400 summary.best_block_height = pindex->nHeight;
401 summary.best_block_hash = pindex->GetBlockHash();
402 } else {
403 summary.best_block_height = 0;
404 summary.best_block_hash = m_chain->getBlockHash(0);
405 }
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:29
static const CBlockIndex * NextSyncBlock(const CBlockIndex *pindex_prev, CChain &chain) EXCLUSIVE_LOCKS_REQUIRED(cs_main)
Definition: base.cpp:124
constexpr uint8_t DB_BEST_BLOCK
Definition: base.cpp:27
static void FatalError(const char *fmt, const Args &...args)
Definition: base.cpp:33
constexpr int64_t SYNC_LOCATOR_WRITE_INTERVAL
Definition: base.cpp:30
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:62
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:41
bool ReadBestBlock(CBlockLocator &locator) const
Read block locator of the chain that the index is in sync with.
Definition: base.cpp:54
void Stop()
Stops the instance from staying in sync with blockchain updates.
Definition: base.cpp:387
void SetBestBlockIndex(const CBlockIndex *block)
Update the internal best block index as well as the prune lock.
Definition: base.cpp:409
bool Init()
Initializes the sync state and registers the instance to the validation interface so that it stays in...
Definition: base.cpp:75
virtual ~BaseIndex()
Destructor interrupts sync thread if running and blocks until it exits.
Definition: base.cpp:70
std::atomic< const CBlockIndex * > m_best_block_index
The last block in the chain that the index is in sync with.
Definition: base.h:65
virtual bool CommitInternal(CDBBatch &batch)
Virtual method called internally by Commit that can be overridden to atomically commit more index sta...
Definition: base.cpp:223
const std::string & GetName() const LIFETIMEBOUND
Get the name of the index for display in logs.
Definition: base.h:126
bool BlockUntilSyncedToCurrentChain() const LOCKS_EXCLUDED(void Interrupt()
Blocks the current thread until the index is caught up to the current state of the block chain.
Definition: base.cpp:373
virtual bool AllowPrune() const =0
void BlockConnected(ChainstateRole role, const std::shared_ptr< const CBlock > &block, const CBlockIndex *pindex) override
Notifies listeners of a block being connected.
Definition: base.cpp:254
std::atomic< bool > m_synced
Whether the index is in sync with the main chain.
Definition: base.h:62
CThreadInterrupt m_interrupt
Definition: base.h:68
BaseIndex(std::unique_ptr< interfaces::Chain > chain, std::string name)
Definition: base.cpp:67
IndexSummary GetSummary() const
Get a summary of the index and its state.
Definition: base.cpp:395
const std::string m_name
Definition: base.h:94
virtual DB & GetDB() const =0
std::thread m_thread_sync
Definition: base.h:67
bool Commit()
Write the current index state (eg.
Definition: base.cpp:214
virtual bool WriteBlock(const CBlock &block, const CBlockIndex *pindex)
Write update index entries for a newly connected block.
Definition: base.h:110
virtual bool CustomInit(const std::optional< interfaces::BlockKey > &block)
Initialize internal state from the database and block index.
Definition: base.h:105
void ThreadSync()
Sync the index with the block index starting from the current best block.
Definition: base.cpp:141
Chainstate * m_chainstate
Definition: base.h:93
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:234
bool StartBackgroundSync()
Starts the initial sync process.
Definition: base.cpp:377
void ChainStateFlushed(ChainstateRole role, const CBlockLocator &locator) override
Notifies listeners of the new active block chain on-disk.
Definition: base.cpp:306
std::unique_ptr< interfaces::Chain > m_chain
Definition: base.h:92
std::atomic< bool > m_init
Whether the index has been initialized or not.
Definition: base.h:54
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:102
BlockHash GetBlockHash() const
Definition: blockindex.h:130
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 * Tip() const
Returns the index entry for the tip of this chain, or nullptr if none.
Definition: chain.h:150
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
CChain m_chain
The current chain of blockheaders we consult and build on.
Definition: validation.h:799
node::BlockManager & m_blockman
Reference to a BlockManager instance which itself is shared across all Chainstate instances.
Definition: validation.h:757
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:256
RecursiveMutex cs_main
Mutex to guard access to validation specific variables, such as reading or changing the chainstate.
Definition: cs_main.cpp:7
ChainstateRole
This enum describes the various roles a specific Chainstate instance can take.
Definition: chain.h:14
bool error(const char *fmt, const Args &...args)
Definition: logging.h:263
#define LogPrintf(...)
Definition: logging.h:227
void ReadDatabaseArgs(const ArgsManager &args, DBOptions &options)
Implement std::hash so RCUPtr can be used as a key for maps or sets.
Definition: rcu.h:259
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(std::string_view thread_name, std::function< void()> thread_func)
A wrapper for do-something-once thread functions.
Definition: thread.cpp:14
const char * name
Definition: rest.cpp:47
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:20
Hash/height pair to help track and identify blocks.
Definition: chain.h:49
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()
DEPRECATED Use either ClockType::now() or Now<TimePointType>() if a cast is needed.
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