Skip to content

Commit a2d860f

Browse files
committed
Merge bitcoin#25193: indexes: Read the locator's top block during init, allow interaction with reindex-chainstate
1 parent e63410e commit a2d860f

7 files changed

Lines changed: 59 additions & 32 deletions

File tree

‎src/index/base.cpp‎

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ using node::PruneLockInfo;
1818
using node::ReadBlockFromDisk;
1919
using node::fPruneMode;
2020

21+
using node::g_indexes_ready_to_sync;
22+
2123
constexpr uint8_t DB_BEST_BLOCK{'B'};
2224

2325
constexpr auto SYNC_LOG_INTERVAL{30s};
@@ -69,14 +71,22 @@ bool BaseIndex::Init()
6971
if (locator.IsNull()) {
7072
SetBestBlockIndex(nullptr);
7173
} else {
72-
SetBestBlockIndex(m_chainstate->FindForkInGlobalIndex(locator));
74+
// Setting the best block to the locator's top block. If it is not part of the
75+
// best chain, we will rewind to the fork point during index sync
76+
const CBlockIndex* locator_index{m_chainstate->m_blockman.LookupBlockIndex(locator.vHave.at(0))};
77+
if (!locator_index) {
78+
return InitError(strprintf(Untranslated("%s: best block of the index not found. Please rebuild the index."), GetName()));
79+
}
80+
SetBestBlockIndex(locator_index);
7381
}
7482

7583
// Note: this will latch to true immediately if the user starts up with an empty
7684
// datadir and an index enabled. If this is the case, indexation will happen solely
7785
// via `BlockConnected` signals until, possibly, the next restart.
7886
m_synced = m_best_block_index.load() == active_chain.Tip();
79-
if (!m_synced) {
87+
88+
// Skip pruning check if indexes are not ready to sync (because reindex-chainstate has wiped the chain).
89+
if (!m_synced && g_indexes_ready_to_sync) {
8090
bool prune_violation = false;
8191
if (!m_best_block_index) {
8292
// index is not built yet
@@ -131,6 +141,12 @@ static const CBlockIndex* NextSyncBlock(const CBlockIndex* pindex_prev, CChain&
131141

132142
void BaseIndex::ThreadSync()
133143
{
144+
// Wait for a possible reindex-chainstate to finish until continuing
145+
// with the index sync
146+
while (!g_indexes_ready_to_sync) {
147+
if (!m_interrupt.sleep_for(std::chrono::milliseconds(500))) return;
148+
}
149+
134150
const CBlockIndex* pindex = m_best_block_index.load();
135151
if (!m_synced) {
136152
auto& consensus_params = Params().GetConsensus();

‎src/init.cpp‎

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ using node::DEFAULT_PRINTPRIORITY;
149149
using node::DEFAULT_SPENTINDEX;
150150
using node::DEFAULT_STOPAFTERBLOCKIMPORT;
151151
using node::DEFAULT_TIMESTAMPINDEX;
152+
using node::g_indexes_ready_to_sync;
152153
using node::LoadChainstate;
153154
using node::NodeContext;
154155
using node::ThreadImport;
@@ -572,7 +573,7 @@ void SetupServerArgs(ArgsManager& argsman)
572573
argsman.AddArg("-pid=<file>", strprintf("Specify pid file. Relative paths will be prefixed by a net-specific datadir location. (default: %s)", BITCOIN_PID_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
573574
argsman.AddArg("-prune=<n>", strprintf("Reduce storage requirements by enabling pruning (deleting) of old blocks. This allows the pruneblockchain RPC to be called to delete specific blocks, and enables automatic pruning of old blocks if a target size in MiB is provided. This mode is incompatible with -txindex, -rescan and -disablegovernance=false. "
574575
"Warning: Reverting this setting requires re-downloading the entire blockchain. "
575-
"(default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, >%u = automatically prune block files to stay under the specified target size in MiB)", MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
576+
"(default: 0 = disable pruning blocks, 1 = allow manual pruning via RPC, >=%u = automatically prune block files to stay under the specified target size in MiB)", MIN_DISK_SPACE_FOR_BLOCK_FILES / 1024 / 1024), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
576577
argsman.AddArg("-settings=<file>", strprintf("Specify path to dynamic settings data file. Can be disabled with -nosettings. File is written at runtime and not meant to be edited by users (use %s instead for custom settings). Relative paths will be prefixed by datadir location. (default: %s)", BITCOIN_CONF_FILENAME, BITCOIN_SETTINGS_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
577578
argsman.AddArg("-syncmempool", strprintf("Sync mempool from other nodes on start (default: %u)", DEFAULT_SYNC_MEMPOOL), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
578579
#if HAVE_SYSTEM
@@ -588,7 +589,7 @@ void SetupServerArgs(ArgsManager& argsman)
588589

589590
argsman.AddArg("-addressindex", strprintf("Maintain a full address index, used to query for the balance, txids and unspent outputs for addresses (default: %u)", DEFAULT_ADDRESSINDEX), ArgsManager::ALLOW_ANY, OptionsCategory::INDEXING);
590591
argsman.AddArg("-reindex", "Rebuild chain state and block index from the blk*.dat files on disk. This will also rebuild active optional indexes.", ArgsManager::ALLOW_ANY, OptionsCategory::INDEXING);
591-
argsman.AddArg("-reindex-chainstate", "Rebuild chain state from the currently indexed blocks. When in pruning mode or if blocks on disk might be corrupted, use full -reindex instead. Deactivate all optional indexes before running this.", ArgsManager::ALLOW_ANY, OptionsCategory::INDEXING);
592+
argsman.AddArg("-reindex-chainstate", "Rebuild chain state from the currently indexed blocks. When in pruning mode or if blocks on disk might be corrupted, use full -reindex instead.", ArgsManager::ALLOW_ANY, OptionsCategory::INDEXING);
592593
argsman.AddArg("-spentindex", strprintf("Maintain a full spent index, used to query the spending txid and input index for an outpoint (default: %u)", DEFAULT_SPENTINDEX), ArgsManager::ALLOW_ANY, OptionsCategory::INDEXING);
593594
argsman.AddArg("-timestampindex", strprintf("Maintain a timestamp index for block hashes, used to query blocks hashes by a range of timestamps (default: %u)", DEFAULT_TIMESTAMPINDEX), ArgsManager::ALLOW_ANY, OptionsCategory::INDEXING);
594595
argsman.AddArg("-txindex", strprintf("Maintain a full transaction index, used by the getrawtransaction rpc call (default: %u)", DEFAULT_TXINDEX), ArgsManager::ALLOW_ANY, OptionsCategory::INDEXING);
@@ -1386,19 +1387,6 @@ bool AppInitParameterInteraction(const ArgsManager& args)
13861387

13871388
nMaxTipAge = args.GetIntArg("-maxtipage", DEFAULT_MAX_TIP_AGE);
13881389

1389-
if (args.GetBoolArg("-reindex-chainstate", false)) {
1390-
// indexes that must be deactivated to prevent index corruption, see #24630
1391-
if (args.GetBoolArg("-coinstatsindex", DEFAULT_COINSTATSINDEX)) {
1392-
return InitError(_("-reindex-chainstate option is not compatible with -coinstatsindex. Please temporarily disable coinstatsindex while using -reindex-chainstate, or replace -reindex-chainstate with -reindex to fully rebuild all indexes."));
1393-
}
1394-
if (g_enabled_filter_types.count(BlockFilterType::BASIC_FILTER)) {
1395-
return InitError(_("-reindex-chainstate option is not compatible with -blockfilterindex. Please temporarily disable blockfilterindex while using -reindex-chainstate, or replace -reindex-chainstate with -reindex to fully rebuild all indexes."));
1396-
}
1397-
if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
1398-
return InitError(_("-reindex-chainstate option is not compatible with -txindex. Please temporarily disable txindex while using -reindex-chainstate, or replace -reindex-chainstate with -reindex to fully rebuild all indexes."));
1399-
}
1400-
}
1401-
14021390
try {
14031391
const bool fRecoveryEnabled{llmq::QuorumDataRecoveryEnabled()};
14041392
const bool fQuorumVvecRequestsEnabled{llmq::GetEnabledQuorumVvecSyncEntries().size() > 0};
@@ -2235,6 +2223,9 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
22352223
}
22362224

22372225
// ********************************************************* Step 8: start indexers
2226+
2227+
// If reindex-chainstate was specified, delay syncing indexes until ThreadImport has reindexed the chain
2228+
if (!fReindexChainState) g_indexes_ready_to_sync = true;
22382229
if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
22392230
g_txindex = std::make_unique<TxIndex>(cache_sizes.tx_index, false, fReindex);
22402231
if (!g_txindex->Start(chainman.ActiveChainstate())) {

‎src/node/blockstorage.cpp‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
namespace node {
2727
std::atomic_bool fImporting(false);
2828
std::atomic_bool fReindex(false);
29+
std::atomic_bool g_indexes_ready_to_sync{false};
2930
bool fPruneMode = false;
3031
uint64_t nPruneTarget = 0;
3132

@@ -898,5 +899,6 @@ void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFile
898899
} // End scope of CImportingNow
899900

900901
chainman.ActiveChainstate().LoadMempool(args);
902+
g_indexes_ready_to_sync = true;
901903
}
902904
} // namespace node

‎src/node/blockstorage.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ static constexpr size_t BLOCK_SERIALIZATION_HEADER_SIZE = CMessageHeader::MESSAG
5050

5151
extern std::atomic_bool fImporting;
5252
extern std::atomic_bool fReindex;
53+
extern std::atomic_bool g_indexes_ready_to_sync;
5354
/** Pruning-related variables and constants */
5455
/** True if we're running in -prune mode. */
5556
extern bool fPruneMode;

‎src/test/util/setup_common.cpp‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,7 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName, const std::ve
229229
noui_connect();
230230
noui_connected = true;
231231
}
232+
node::g_indexes_ready_to_sync = true;
232233

233234
bls::bls_legacy_scheme.store(true);
234235
}

‎test/functional/feature_coinstatsindex.py‎

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ def run_test(self):
5454
self._test_use_index_option()
5555
self._test_reorg_index()
5656
self._test_index_rejects_hash_serialized()
57+
self._test_init_index_after_reorg()
5758

5859
def block_subsidy(self, prev_bits):
5960
# Subsidy calculations valid till block 4500
@@ -75,6 +76,9 @@ def block_sanity_check(self, block_info, prev_height):
7576
block_info['new_outputs_ex_coinbase'] + block_info['coinbase'] + block_info['unspendable']
7677
)
7778

79+
def sync_index_node(self):
80+
self.wait_until(lambda: self.nodes[1].getindexinfo()['coinstatsindex']['synced'] is True)
81+
7882
def _test_coin_stats_index(self):
7983
node = self.nodes[0]
8084
index_node = self.nodes[1]
@@ -241,18 +245,16 @@ def _test_coin_stats_index(self):
241245
self.log.info("Test that the index works with -reindex")
242246

243247
self.restart_node(1, extra_args=["-coinstatsindex", "-reindex"])
248+
self.sync_index_node()
244249
res11 = index_node.gettxoutsetinfo('muhash')
245250
assert_equal(res11, res10)
246251

247-
self.log.info("Test that -reindex-chainstate is disallowed with coinstatsindex")
252+
self.log.info("Test that the index works with -reindex-chainstate")
248253

249-
self.stop_node(1)
250-
self.nodes[1].assert_start_raises_init_error(
251-
expected_msg='Error: -reindex-chainstate option is not compatible with -coinstatsindex. '
252-
'Please temporarily disable coinstatsindex while using -reindex-chainstate, or replace -reindex-chainstate with -reindex to fully rebuild all indexes.',
253-
extra_args=['-coinstatsindex', '-reindex-chainstate'],
254-
)
255-
self.restart_node(1, extra_args=["-coinstatsindex"])
254+
self.restart_node(1, extra_args=["-coinstatsindex", "-reindex-chainstate"])
255+
self.sync_index_node()
256+
res12 = index_node.gettxoutsetinfo('muhash')
257+
assert_equal(res12, res10)
256258

257259
self.log.info("Test obtaining info for a non-existent block hash")
258260
assert_raises_rpc_error(-5, "Block not found", index_node.gettxoutsetinfo, hash_type="none", hash_or_height="ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", use_index=True)
@@ -274,6 +276,7 @@ def _test_reorg_index(self):
274276
index_node = self.nodes[1]
275277
reorg_blocks = self.generatetoaddress(index_node, 2, getnewdestination()[2])
276278
reorg_block = reorg_blocks[1]
279+
self.sync_index_node()
277280
res_invalid = index_node.gettxoutsetinfo('muhash')
278281
index_node.invalidateblock(reorg_blocks[0])
279282
assert_equal(index_node.gettxoutsetinfo('muhash')['height'], 110)
@@ -313,6 +316,26 @@ def _test_index_rejects_hash_serialized(self):
313316
for use_index in {True, False, None}:
314317
assert_raises_rpc_error(-8, msg, self.nodes[1].gettxoutsetinfo, hash_type='hash_serialized_2', hash_or_height=111, use_index=use_index)
315318

319+
def _test_init_index_after_reorg(self):
320+
self.log.info("Test a reorg while the index is deactivated")
321+
index_node = self.nodes[1]
322+
block = self.nodes[0].getbestblockhash()
323+
self.generate(index_node, 2, sync_fun=self.no_op)
324+
self.sync_index_node()
325+
326+
# Restart without index
327+
self.restart_node(1, extra_args=[])
328+
self.connect_nodes(0, 1)
329+
index_node.invalidateblock(block)
330+
self.generatetoaddress(index_node, 5, getnewdestination()[2])
331+
res = index_node.gettxoutsetinfo(hash_type='muhash', hash_or_height=None, use_index=False)
332+
333+
# Restart with index that still has its best block on the old chain
334+
self.restart_node(1, extra_args=self.extra_args[1])
335+
self.sync_index_node()
336+
res1 = index_node.gettxoutsetinfo(hash_type='muhash', hash_or_height=None, use_index=True)
337+
assert_equal(res["muhash"], res1["muhash"])
338+
316339

317340
if __name__ == '__main__':
318341
CoinStatsIndexTest().main()

‎test/functional/p2p_blockfilters.py‎

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -293,13 +293,6 @@ def run_test(self):
293293
msg = "Error: Unknown -blockfilterindex value abc."
294294
self.nodes[0].assert_start_raises_init_error(expected_msg=msg)
295295

296-
self.log.info("Test -blockfilterindex with -reindex-chainstate raises an error")
297-
self.nodes[0].assert_start_raises_init_error(
298-
expected_msg='Error: -reindex-chainstate option is not compatible with -blockfilterindex. '
299-
'Please temporarily disable blockfilterindex while using -reindex-chainstate, or replace -reindex-chainstate with -reindex to fully rebuild all indexes.',
300-
extra_args=['-blockfilterindex', '-reindex-chainstate'],
301-
)
302-
303296
self.test_special_transactions_in_filters()
304297

305298
def create_simple_assetlock(self, base_tx, base_tx_value, amount_locked_1, amount_locked_2=0):

0 commit comments

Comments
 (0)