2022-12-24 23:49:50 +00:00
// Copyright (c) 2017-2022 The Bitcoin Core developers
2018-05-15 15:57:48 -07:00
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
# include <chainparams.h>
2023-03-23 12:23:29 +01:00
# include <common/args.h>
2018-05-15 15:57:48 -07:00
# include <index/base.h>
2022-01-13 07:57:54 -05:00
# include <interfaces/chain.h>
2022-01-17 20:33:16 -05:00
# include <kernel/chain.h>
2023-03-06 22:01:13 +01:00
# include <logging.h>
2021-04-02 20:42:05 +02:00
# include <node/blockstorage.h>
2022-01-13 07:57:54 -05:00
# include <node/context.h>
2022-08-16 23:32:55 -04:00
# include <node/database_args.h>
2022-06-14 10:38:51 +02:00
# include <node/interface_ui.h>
2018-05-16 19:17:40 +00:00
# include <shutdown.h>
2018-05-15 15:57:48 -07:00
# include <tinyformat.h>
2021-10-01 13:53:59 +00:00
# include <util/syscall_sandbox.h>
2021-04-13 20:44:46 +03:00
# include <util/thread.h>
2020-04-11 18:47:17 +03:00
# include <util/translation.h>
2021-04-02 20:42:05 +02:00
# include <validation.h> // For g_chainman
2018-05-15 15:57:48 -07:00
# include <warnings.h>
2022-08-31 16:43:58 +01:00
# include <string>
# include <utility>
2021-05-01 13:49:37 +02:00
constexpr uint8_t DB_BEST_BLOCK { ' B ' } ;
2018-05-15 17:20:17 -07:00
2022-05-06 15:17:44 -03:00
constexpr auto SYNC_LOG_INTERVAL { 30 s } ;
constexpr auto SYNC_LOCATOR_WRITE_INTERVAL { 30 s } ;
2018-05-15 15:57:48 -07:00
2020-06-16 11:02:46 -04:00
template < typename . . . Args >
2018-05-15 15:57:48 -07:00
static void FatalError ( const char * fmt , const Args & . . . args )
{
std : : string strMessage = tfm : : format ( fmt , args . . . ) ;
2020-06-10 12:14:32 +03:00
SetMiscWarning ( Untranslated ( strMessage ) ) ;
2018-05-15 15:57:48 -07:00
LogPrintf ( " *** %s \n " , strMessage ) ;
2023-02-24 13:44:07 -05:00
InitError ( _ ( " A fatal internal error occurred, see debug.log for details " ) ) ;
2018-05-15 15:57:48 -07:00
StartShutdown ( ) ;
}
2022-01-17 20:35:02 -05:00
CBlockLocator GetLocator ( interfaces : : Chain & chain , const uint256 & block_hash )
{
CBlockLocator locator ;
bool found = chain . findBlock ( block_hash , interfaces : : FoundBlock ( ) . locator ( locator ) ) ;
assert ( found ) ;
assert ( ! locator . IsNull ( ) ) ;
return locator ;
}
2018-05-15 17:20:17 -07:00
BaseIndex : : DB : : DB ( const fs : : path & path , size_t n_cache_size , bool f_memory , bool f_wipe , bool f_obfuscate ) :
2022-08-16 23:32:55 -04:00
CDBWrapper { DBParams {
. path = path ,
. cache_bytes = n_cache_size ,
. memory_only = f_memory ,
. wipe_data = f_wipe ,
. obfuscate = f_obfuscate ,
. options = [ ] { DBOptions options ; node : : ReadDatabaseArgs ( gArgs , options ) ; return options ; } ( ) } }
2018-05-15 17:20:17 -07:00
{ }
bool BaseIndex : : DB : : ReadBestBlock ( CBlockLocator & locator ) const
{
bool success = Read ( DB_BEST_BLOCK , locator ) ;
if ( ! success ) {
locator . SetNull ( ) ;
}
return success ;
}
2019-03-02 18:35:55 -08:00
void BaseIndex : : DB : : WriteBestBlock ( CDBBatch & batch , const CBlockLocator & locator )
2018-05-15 17:20:17 -07:00
{
2019-03-02 18:35:55 -08:00
batch . Write ( DB_BEST_BLOCK , locator ) ;
2018-05-15 17:20:17 -07:00
}
2022-08-31 16:43:58 +01:00
BaseIndex : : BaseIndex ( std : : unique_ptr < interfaces : : Chain > chain , std : : string name )
: m_chain { std : : move ( chain ) } , m_name { std : : move ( name ) } { }
2022-01-13 07:57:54 -05:00
2018-05-15 15:57:48 -07:00
BaseIndex : : ~ BaseIndex ( )
{
Interrupt ( ) ;
Stop ( ) ;
}
bool BaseIndex : : Init ( )
{
CBlockLocator locator ;
if ( ! GetDB ( ) . ReadBestBlock ( locator ) ) {
locator . SetNull ( ) ;
}
LOCK ( cs_main ) ;
2021-05-07 14:50:10 -04:00
CChain & active_chain = m_chainstate - > m_chain ;
2018-08-27 12:28:35 -07:00
if ( locator . IsNull ( ) ) {
2021-04-18 23:06:18 +02:00
SetBestBlockIndex ( nullptr ) ;
2018-08-27 12:28:35 -07:00
} else {
2022-05-22 20:29:45 +02:00
// Setting the best block to the locator's top block. If it is not part of the
// best chain, we will rewind to the fork point during index sync
const CBlockIndex * locator_index { m_chainstate - > m_blockman . LookupBlockIndex ( locator . vHave . at ( 0 ) ) } ;
if ( ! locator_index ) {
return InitError ( strprintf ( Untranslated ( " %s: best block of the index not found. Please rebuild the index. " ) , GetName ( ) ) ) ;
}
SetBestBlockIndex ( locator_index ) ;
2018-08-27 12:28:35 -07:00
}
2021-07-18 11:58:47 -04:00
// Note: this will latch to true immediately if the user starts up with an empty
// datadir and an index enabled. If this is the case, indexation will happen solely
// via `BlockConnected` signals until, possibly, the next restart.
2020-10-14 16:48:17 -04:00
m_synced = m_best_block_index . load ( ) = = active_chain . Tip ( ) ;
2019-05-02 20:41:01 +02:00
if ( ! m_synced ) {
bool prune_violation = false ;
if ( ! m_best_block_index ) {
// index is not built yet
// make sure we have all block data back to the genesis
2022-04-28 11:15:38 +02:00
prune_violation = m_chainstate - > m_blockman . GetFirstStoredBlock ( * active_chain . Tip ( ) ) ! = active_chain . Genesis ( ) ;
2019-05-02 20:41:01 +02:00
}
// in case the index has a best block set and is not fully synced
// check if we have the required blocks to continue building the index
else {
const CBlockIndex * block_to_test = m_best_block_index . load ( ) ;
2020-10-14 16:48:17 -04:00
if ( ! active_chain . Contains ( block_to_test ) ) {
2019-05-02 20:41:01 +02:00
// if the bestblock is not part of the mainchain, find the fork
// and make sure we have all data down to the fork
2020-10-14 16:48:17 -04:00
block_to_test = active_chain . FindFork ( block_to_test ) ;
2019-05-02 20:41:01 +02:00
}
2020-10-14 16:48:17 -04:00
const CBlockIndex * block = active_chain . Tip ( ) ;
2019-05-02 20:41:01 +02:00
prune_violation = true ;
// check backwards from the tip if we have all block data until we reach the indexes bestblock
2021-10-26 22:25:04 +02:00
while ( block_to_test & & block & & ( block - > nStatus & BLOCK_HAVE_DATA ) ) {
2019-05-02 20:41:01 +02:00
if ( block_to_test = = block ) {
prune_violation = false ;
break ;
}
2021-12-14 21:36:00 +00:00
// block->pprev must exist at this point, since block_to_test is part of the chain
// and thus must be encountered when going backwards from the tip
2021-10-26 22:25:04 +02:00
assert ( block - > pprev ) ;
2019-05-02 20:41:01 +02:00
block = block - > pprev ;
}
}
if ( prune_violation ) {
2021-04-28 14:01:37 +02:00
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())) ;
2019-05-02 20:41:01 +02:00
}
}
2018-05-15 15:57:48 -07:00
return true ;
}
2020-10-14 16:48:17 -04:00
static const CBlockIndex * NextSyncBlock ( const CBlockIndex * pindex_prev , CChain & chain ) EXCLUSIVE_LOCKS_REQUIRED ( cs_main )
2018-05-15 15:57:48 -07:00
{
AssertLockHeld ( cs_main ) ;
if ( ! pindex_prev ) {
2020-10-14 16:48:17 -04:00
return chain . Genesis ( ) ;
2018-05-15 15:57:48 -07:00
}
2020-10-14 16:48:17 -04:00
const CBlockIndex * pindex = chain . Next ( pindex_prev ) ;
2018-05-15 15:57:48 -07:00
if ( pindex ) {
return pindex ;
}
2020-10-14 16:48:17 -04:00
return chain . Next ( chain . FindFork ( pindex_prev ) ) ;
2018-05-15 15:57:48 -07:00
}
void BaseIndex : : ThreadSync ( )
{
2021-10-01 13:53:59 +00:00
SetSyscallSandboxPolicy ( SyscallSandboxPolicy : : TX_INDEX ) ;
2018-05-15 15:57:48 -07:00
const CBlockIndex * pindex = m_best_block_index . load ( ) ;
if ( ! m_synced ) {
2022-05-06 15:17:44 -03:00
std : : chrono : : steady_clock : : time_point last_log_time { 0 s } ;
std : : chrono : : steady_clock : : time_point last_locator_write_time { 0 s } ;
2018-05-15 15:57:48 -07:00
while ( true ) {
if ( m_interrupt ) {
2021-04-18 23:06:18 +02:00
SetBestBlockIndex ( pindex ) ;
2019-03-02 18:35:55 -08:00
// No need to handle errors in Commit. If it fails, the error will be already be
// logged. The best way to recover is to continue, as index cannot be corrupted by
// a missed commit to disk for an advanced index state.
Commit ( ) ;
2018-05-15 15:57:48 -07:00
return ;
}
{
LOCK ( cs_main ) ;
2020-10-14 16:48:17 -04:00
const CBlockIndex * pindex_next = NextSyncBlock ( pindex , m_chainstate - > m_chain ) ;
2018-05-15 15:57:48 -07:00
if ( ! pindex_next ) {
2021-04-18 23:06:18 +02:00
SetBestBlockIndex ( pindex ) ;
2018-05-15 15:57:48 -07:00
m_synced = true ;
2019-03-02 18:35:55 -08:00
// No need to handle errors in Commit. See rationale above.
Commit ( ) ;
2018-05-15 15:57:48 -07:00
break ;
}
2018-08-27 15:26:29 -07:00
if ( pindex_next - > pprev ! = pindex & & ! Rewind ( pindex , pindex_next - > pprev ) ) {
FatalError ( " %s: Failed to rewind index %s to a previous chain tip " ,
__func__ , GetName ( ) ) ;
return ;
}
2018-05-15 15:57:48 -07:00
pindex = pindex_next ;
}
2022-05-06 15:17:44 -03:00
auto current_time { std : : chrono : : steady_clock : : now ( ) } ;
2018-05-15 15:57:48 -07:00
if ( last_log_time + SYNC_LOG_INTERVAL < current_time ) {
LogPrintf ( " Syncing %s with block chain from height %d \n " ,
GetName ( ) , pindex - > nHeight ) ;
last_log_time = current_time ;
}
if ( last_locator_write_time + SYNC_LOCATOR_WRITE_INTERVAL < current_time ) {
2022-05-06 14:07:51 +02:00
SetBestBlockIndex ( pindex - > pprev ) ;
2018-05-15 15:57:48 -07:00
last_locator_write_time = current_time ;
2019-03-02 18:35:55 -08:00
// No need to handle errors in Commit. See rationale above.
Commit ( ) ;
2018-05-15 15:57:48 -07:00
}
CBlock block ;
2022-01-17 20:33:16 -05:00
interfaces : : BlockInfo block_info = kernel : : MakeBlockInfo ( pindex ) ;
2023-02-18 15:49:41 +01:00
if ( ! m_chainstate - > m_blockman . ReadBlockFromDisk ( block , * pindex ) ) {
2018-05-15 15:57:48 -07:00
FatalError ( " %s: Failed to read block %s from disk " ,
__func__ , pindex - > GetBlockHash ( ) . ToString ( ) ) ;
return ;
2022-01-17 20:33:16 -05:00
} else {
block_info . data = & block ;
2018-05-15 15:57:48 -07:00
}
2022-01-17 20:33:16 -05:00
if ( ! CustomAppend ( block_info ) ) {
2018-05-15 15:57:48 -07:00
FatalError ( " %s: Failed to write block %s to index database " ,
__func__ , pindex - > GetBlockHash ( ) . ToString ( ) ) ;
return ;
}
}
}
if ( pindex ) {
LogPrintf ( " %s is enabled at height %d \n " , GetName ( ) , pindex - > nHeight ) ;
} else {
LogPrintf ( " %s is enabled \n " , GetName ( ) ) ;
}
}
2019-03-02 18:35:55 -08:00
bool BaseIndex : : Commit ( )
2018-05-15 15:57:48 -07:00
{
2022-01-20 22:21:56 +01:00
// Don't commit anything if we haven't indexed any block yet
// (this could happen if init is interrupted).
2022-01-17 20:35:02 -05:00
bool ok = m_best_block_index ! = nullptr ;
if ( ok ) {
CDBBatch batch ( GetDB ( ) ) ;
ok = CustomCommit ( batch ) ;
if ( ok ) {
GetDB ( ) . WriteBestBlock ( batch , GetLocator ( * m_chain , m_best_block_index . load ( ) - > GetBlockHash ( ) ) ) ;
ok = GetDB ( ) . WriteBatch ( batch ) ;
}
}
if ( ! ok ) {
return error ( " %s: Failed to commit latest %s state " , __func__ , GetName ( ) ) ;
2022-01-20 22:21:56 +01:00
}
2019-03-02 18:35:55 -08:00
return true ;
}
2018-08-27 15:26:29 -07:00
bool BaseIndex : : Rewind ( const CBlockIndex * current_tip , const CBlockIndex * new_tip )
{
assert ( current_tip = = m_best_block_index ) ;
assert ( current_tip - > GetAncestor ( new_tip - > nHeight ) = = new_tip ) ;
2022-01-17 20:33:52 -05:00
if ( ! CustomRewind ( { current_tip - > GetBlockHash ( ) , current_tip - > nHeight } , { new_tip - > GetBlockHash ( ) , new_tip - > nHeight } ) ) {
return false ;
}
2018-08-27 15:26:29 -07:00
// In the case of a reorg, ensure persisted block locator is not stale.
2019-05-02 20:41:01 +02:00
// Pruning has a minimum of 288 blocks-to-keep and getting the index
// out of sync may be possible but a users fault.
// In case we reorg beyond the pruned depth, ReadBlockFromDisk would
// throw and lead to a graceful shutdown
2021-04-18 23:06:18 +02:00
SetBestBlockIndex ( new_tip ) ;
2018-08-27 15:26:29 -07:00
if ( ! Commit ( ) ) {
// If commit fails, revert the best block index to avoid corruption.
2021-04-18 23:06:18 +02:00
SetBestBlockIndex ( current_tip ) ;
2018-08-27 15:26:29 -07:00
return false ;
}
return true ;
}
2019-11-11 10:43:34 -05:00
void BaseIndex : : BlockConnected ( const std : : shared_ptr < const CBlock > & block , const CBlockIndex * pindex )
2018-05-15 15:57:48 -07:00
{
if ( ! m_synced ) {
return ;
}
const CBlockIndex * best_block_index = m_best_block_index . load ( ) ;
if ( ! best_block_index ) {
if ( pindex - > nHeight ! = 0 ) {
FatalError ( " %s: First block connected is not the genesis block (height=%d) " ,
__func__ , pindex - > nHeight ) ;
return ;
}
} else {
// Ensure block connects to an ancestor of the current best block. This should be the case
// most of the time, but may not be immediately after the sync thread catches up and sets
// m_synced. Consider the case where there is a reorg and the blocks on the stale branch are
// in the ValidationInterface queue backlog even after the sync thread has caught up to the
// new chain tip. In this unlikely event, log a warning and let the queue clear.
if ( best_block_index - > GetAncestor ( pindex - > nHeight - 1 ) ! = pindex - > pprev ) {
LogPrintf ( " %s: WARNING: Block %s does not connect to an ancestor of " /* Continued */
" known best chain (tip=%s); not updating index \n " ,
__func__ , pindex - > GetBlockHash ( ) . ToString ( ) ,
best_block_index - > GetBlockHash ( ) . ToString ( ) ) ;
return ;
}
2018-08-27 15:26:29 -07:00
if ( best_block_index ! = pindex - > pprev & & ! Rewind ( best_block_index , pindex - > pprev ) ) {
FatalError ( " %s: Failed to rewind index %s to a previous chain tip " ,
__func__ , GetName ( ) ) ;
return ;
}
2018-05-15 15:57:48 -07:00
}
2022-01-17 20:33:16 -05:00
interfaces : : BlockInfo block_info = kernel : : MakeBlockInfo ( pindex , block . get ( ) ) ;
if ( CustomAppend ( block_info ) ) {
index: Improve BaseIndex::BlockUntilSyncedToCurrentChain reliability
Since commit f08c9fb0c6a799e3cb75ca5f763a746471625beb from PR
https://github.com/bitcoin/bitcoin/pull/21726, index
`BlockUntilSyncedToCurrentChain` behavior has been less reliable, and there has
also been a race condition in the `coinstatsindex_initial_sync` unit test.
It seems better for `BlockUntilSyncedToCurrentChain` to actually wait for the
last connected block to be fully processed, than to be able to return before
prune locks are set, so this switches the order of `m_best_block_index =
block;` and `UpdatePruneLock` statements in `SetBestBlockIndex` to make it more
reliable.
Also since commit f08c9fb0c6a799e3cb75ca5f763a746471625beb, there has been a
race condition in the `coinstatsindex_initial_sync` test. Before that commit,
the atomic index best block pointer `m_best_block_index` was updated as the
last step of `BaseIndex::BlockConnected`, so `BlockUntilSyncedToCurrentChain`
could safely be used in tests to wait for the last `BlockConnected`
notification to be finished before stopping and destroying the index. But
after that commit, calling `BlockUntilSyncedToCurrentChain` is no longer
sufficient, and there is a race between the test shutdown code which destroys
the index object and the new code introduced in that commit calling
`AllowPrune()` and `GetName()` on the index object. Reproducibility
instructions for this are in
https://github.com/bitcoin/bitcoin/issues/25365#issuecomment-1259744133
This commit fixes the `coinstatsindex_initial_sync` race condition, even though
it will require an additional change to silence TSAN false positives,
https://github.com/bitcoin/bitcoin/pull/26188, after it is fixed. So this
partially addresses but does not resolve the bug reporting TSAN errors
https://github.com/bitcoin/bitcoin/issues/25365.
There is no known race condition outside of test code currently, because the
bitcoind `Shutdown` function calls `FlushBackgroundCallbacks` not
`BlockUntilSyncedToCurrentChain` to safely shut down.
Co-authored-by: Vasil Dimov <vd@FreeBSD.org>
Co-authored-by: MacroFake <falke.marco@gmail.com>
2022-09-30 08:44:04 -04:00
// Setting the best block index is intentionally the last step of this
// function, so BlockUntilSyncedToCurrentChain callers waiting for the
// best block index to be updated can rely on the block being fully
// processed, and the index object being safe to delete.
2021-04-18 23:06:18 +02:00
SetBestBlockIndex ( pindex ) ;
2018-05-15 15:57:48 -07:00
} else {
FatalError ( " %s: Failed to write block %s to index " ,
__func__ , pindex - > GetBlockHash ( ) . ToString ( ) ) ;
return ;
}
}
void BaseIndex : : ChainStateFlushed ( const CBlockLocator & locator )
{
if ( ! m_synced ) {
return ;
}
const uint256 & locator_tip_hash = locator . vHave . front ( ) ;
const CBlockIndex * locator_tip_index ;
{
LOCK ( cs_main ) ;
2020-10-14 16:48:17 -04:00
locator_tip_index = m_chainstate - > m_blockman . LookupBlockIndex ( locator_tip_hash ) ;
2018-05-15 15:57:48 -07:00
}
if ( ! locator_tip_index ) {
FatalError ( " %s: First block (hash=%s) in locator was not found " ,
__func__ , locator_tip_hash . ToString ( ) ) ;
return ;
}
// This checks that ChainStateFlushed callbacks are received after BlockConnected. The check may fail
// immediately after the sync thread catches up and sets m_synced. Consider the case where
// there is a reorg and the blocks on the stale branch are in the ValidationInterface queue
// backlog even after the sync thread has caught up to the new chain tip. In this unlikely
// event, log a warning and let the queue clear.
const CBlockIndex * best_block_index = m_best_block_index . load ( ) ;
if ( best_block_index - > GetAncestor ( locator_tip_index - > nHeight ) ! = locator_tip_index ) {
LogPrintf ( " %s: WARNING: Locator contains block (hash=%s) not on known best " /* Continued */
" chain (tip=%s); not writing index locator \n " ,
__func__ , locator_tip_hash . ToString ( ) ,
best_block_index - > GetBlockHash ( ) . ToString ( ) ) ;
return ;
}
2019-03-02 18:35:55 -08:00
// No need to handle errors in Commit. If it fails, the error will be already be logged. The
// best way to recover is to continue, as index cannot be corrupted by a missed commit to disk
// for an advanced index state.
Commit ( ) ;
2018-05-15 15:57:48 -07:00
}
2020-03-02 15:57:25 +09:00
bool BaseIndex : : BlockUntilSyncedToCurrentChain ( ) const
2018-05-15 15:57:48 -07:00
{
AssertLockNotHeld ( cs_main ) ;
if ( ! m_synced ) {
return false ;
}
{
// Skip the queue-draining stuff if we know we're caught up with
2021-10-12 14:36:51 +13:00
// m_chain.Tip().
2018-05-15 15:57:48 -07:00
LOCK ( cs_main ) ;
2020-10-14 16:48:17 -04:00
const CBlockIndex * chain_tip = m_chainstate - > m_chain . Tip ( ) ;
2018-05-15 15:57:48 -07:00
const CBlockIndex * best_block_index = m_best_block_index . load ( ) ;
if ( best_block_index - > GetAncestor ( chain_tip - > nHeight ) = = chain_tip ) {
return true ;
}
}
LogPrintf ( " %s: %s is catching up on block notifications \n " , __func__ , GetName ( ) ) ;
SyncWithValidationInterfaceQueue ( ) ;
return true ;
}
void BaseIndex : : Interrupt ( )
{
m_interrupt ( ) ;
}
2022-01-13 07:57:54 -05:00
bool BaseIndex : : Start ( )
2018-05-15 15:57:48 -07:00
{
2022-01-13 07:57:54 -05:00
// m_chainstate member gives indexing code access to node internals. It is
// removed in followup https://github.com/bitcoin/bitcoin/pull/24230
m_chainstate = & m_chain - > context ( ) - > chainman - > ActiveChainstate ( ) ;
2018-05-15 15:57:48 -07:00
// Need to register this ValidationInterface before running Init(), so that
// callbacks are not missed if Init sets m_synced to true.
RegisterValidationInterface ( this ) ;
2022-01-17 18:36:40 -05:00
if ( ! Init ( ) ) return false ;
const CBlockIndex * index = m_best_block_index . load ( ) ;
if ( ! CustomInit ( index ? std : : make_optional ( interfaces : : BlockKey { index - > GetBlockHash ( ) , index - > nHeight } ) : std : : nullopt ) ) {
2021-04-28 14:01:37 +02:00
return false ;
2018-05-15 15:57:48 -07:00
}
2021-04-13 21:22:52 +03:00
m_thread_sync = std : : thread ( & util : : TraceThread , GetName ( ) , [ this ] { ThreadSync ( ) ; } ) ;
2021-04-28 14:01:37 +02:00
return true ;
2018-05-15 15:57:48 -07:00
}
void BaseIndex : : Stop ( )
{
UnregisterValidationInterface ( this ) ;
if ( m_thread_sync . joinable ( ) ) {
m_thread_sync . join ( ) ;
}
}
2020-07-31 14:01:31 +02:00
IndexSummary BaseIndex : : GetSummary ( ) const
{
IndexSummary summary { } ;
summary . name = GetName ( ) ;
summary . synced = m_synced ;
2020-12-10 13:36:47 +01:00
summary . best_block_height = m_best_block_index ? m_best_block_index . load ( ) - > nHeight : 0 ;
2020-07-31 14:01:31 +02:00
return summary ;
}
2021-04-18 23:06:18 +02:00
2023-01-16 17:23:18 +01:00
void BaseIndex : : SetBestBlockIndex ( const CBlockIndex * block )
{
assert ( ! m_chainstate - > m_blockman . IsPruneMode ( ) | | AllowPrune ( ) ) ;
2021-04-18 23:06:18 +02:00
if ( AllowPrune ( ) & & block ) {
node : : PruneLockInfo prune_lock ;
prune_lock . height_first = block - > nHeight ;
WITH_LOCK ( : : cs_main , m_chainstate - > m_blockman . UpdatePruneLock ( GetName ( ) , prune_lock ) ) ;
}
index: Improve BaseIndex::BlockUntilSyncedToCurrentChain reliability
Since commit f08c9fb0c6a799e3cb75ca5f763a746471625beb from PR
https://github.com/bitcoin/bitcoin/pull/21726, index
`BlockUntilSyncedToCurrentChain` behavior has been less reliable, and there has
also been a race condition in the `coinstatsindex_initial_sync` unit test.
It seems better for `BlockUntilSyncedToCurrentChain` to actually wait for the
last connected block to be fully processed, than to be able to return before
prune locks are set, so this switches the order of `m_best_block_index =
block;` and `UpdatePruneLock` statements in `SetBestBlockIndex` to make it more
reliable.
Also since commit f08c9fb0c6a799e3cb75ca5f763a746471625beb, there has been a
race condition in the `coinstatsindex_initial_sync` test. Before that commit,
the atomic index best block pointer `m_best_block_index` was updated as the
last step of `BaseIndex::BlockConnected`, so `BlockUntilSyncedToCurrentChain`
could safely be used in tests to wait for the last `BlockConnected`
notification to be finished before stopping and destroying the index. But
after that commit, calling `BlockUntilSyncedToCurrentChain` is no longer
sufficient, and there is a race between the test shutdown code which destroys
the index object and the new code introduced in that commit calling
`AllowPrune()` and `GetName()` on the index object. Reproducibility
instructions for this are in
https://github.com/bitcoin/bitcoin/issues/25365#issuecomment-1259744133
This commit fixes the `coinstatsindex_initial_sync` race condition, even though
it will require an additional change to silence TSAN false positives,
https://github.com/bitcoin/bitcoin/pull/26188, after it is fixed. So this
partially addresses but does not resolve the bug reporting TSAN errors
https://github.com/bitcoin/bitcoin/issues/25365.
There is no known race condition outside of test code currently, because the
bitcoind `Shutdown` function calls `FlushBackgroundCallbacks` not
`BlockUntilSyncedToCurrentChain` to safely shut down.
Co-authored-by: Vasil Dimov <vd@FreeBSD.org>
Co-authored-by: MacroFake <falke.marco@gmail.com>
2022-09-30 08:44:04 -04:00
// Intentionally set m_best_block_index as the last step in this function,
// after updating prune locks above, and after making any other references
// to *this, so the BlockUntilSyncedToCurrentChain function (which checks
// m_best_block_index as an optimization) can be used to wait for the last
// BlockConnected notification and safely assume that prune locks are
// updated and that the index object is safe to delete.
m_best_block_index = block ;
2021-04-18 23:06:18 +02:00
}