mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-03 09:56:38 -05:00
Merge bitcoin/bitcoin#25193: indexes: Read the locator's top block during init, allow interaction with reindex-chainstate
97844d9268
index: Enable reindex-chainstate with active indexes (Martin Zumsande)60bec3c82d
index: Use first block from locator instead of looking for fork point (Martin Zumsande) Pull request description: This makes two improvements to the index init phase: **1) Prevent index corruption in case a reorg happens when the index was switched off**: This is done by reading in the top block stored in the locator instead of looking for a fork point already in `BaseIndex::Init()`. Before, we'd just go back to the fork point by calling `FindForkInGlobalIndex()`, which would have corrupted the coinstatsindex because its saved muhash needs to be reverted step by step by un-applying all blocks in between, which wasn't done before. This is now being done a bit later in `ThreadSync()`, which has existing logic to call the custom `Rewind()` method when going back along the chain to the forking point (thanks ryanofsky for pointing this out to me!). **2) Allow using the `-reindex-chainstate` option without needing to disabling indexes**: With `BaseIndex::Init()` not calling `FindForkInGlobalIndex()` anymore, we can allow `reindex-chainstate` with active indexes. `reindex-chainstate` deletes the chain and rebuilds it later in `ThreadImport`, so there is no chain available during `BaseIndex::Init()`, which would lead to problems (see #24789). But now we'll only need the chain a bit later in `BaseIndex::ThreadSync`, which will wait for the reindex-chainstate in `ThreadImport` to finish and will continue syncing after that. ACKs for top commit: ryanofsky: Code review ACK97844d9268
. Just simple rebase since last review Tree-SHA512: e24973fc22e0b87a49026f4820aecb0a4e415f4d381bade9969dd31cf97afecfea0449dce7fcc797343b792199cc8287276d1f5ffa4433dcb54fb24a808db6fb
This commit is contained in:
commit
4e8a7654f6
7 changed files with 58 additions and 31 deletions
|
@ -23,6 +23,8 @@
|
|||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
using node::g_indexes_ready_to_sync;
|
||||
|
||||
constexpr uint8_t DB_BEST_BLOCK{'B'};
|
||||
|
||||
constexpr auto SYNC_LOG_INTERVAL{30s};
|
||||
|
@ -92,14 +94,22 @@ bool BaseIndex::Init()
|
|||
if (locator.IsNull()) {
|
||||
SetBestBlockIndex(nullptr);
|
||||
} else {
|
||||
SetBestBlockIndex(m_chainstate->FindForkInGlobalIndex(locator));
|
||||
// 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);
|
||||
}
|
||||
|
||||
// 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.
|
||||
m_synced = m_best_block_index.load() == active_chain.Tip();
|
||||
if (!m_synced) {
|
||||
|
||||
// Skip pruning check if indexes are not ready to sync (because reindex-chainstate has wiped the chain).
|
||||
if (!m_synced && g_indexes_ready_to_sync) {
|
||||
bool prune_violation = false;
|
||||
if (!m_best_block_index) {
|
||||
// index is not built yet
|
||||
|
@ -155,6 +165,12 @@ static const CBlockIndex* NextSyncBlock(const CBlockIndex* pindex_prev, CChain&
|
|||
void BaseIndex::ThreadSync()
|
||||
{
|
||||
SetSyscallSandboxPolicy(SyscallSandboxPolicy::TX_INDEX);
|
||||
// Wait for a possible reindex-chainstate to finish until continuing
|
||||
// with the index sync
|
||||
while (!g_indexes_ready_to_sync) {
|
||||
if (!m_interrupt.sleep_for(std::chrono::milliseconds(500))) return;
|
||||
}
|
||||
|
||||
const CBlockIndex* pindex = m_best_block_index.load();
|
||||
if (!m_synced) {
|
||||
std::chrono::steady_clock::time_point last_log_time{0s};
|
||||
|
|
19
src/init.cpp
19
src/init.cpp
|
@ -123,6 +123,7 @@ using node::CalculateCacheSizes;
|
|||
using node::DEFAULT_PERSIST_MEMPOOL;
|
||||
using node::DEFAULT_PRINTPRIORITY;
|
||||
using node::fReindex;
|
||||
using node::g_indexes_ready_to_sync;
|
||||
using node::LoadChainstate;
|
||||
using node::MempoolPath;
|
||||
using node::NodeContext;
|
||||
|
@ -456,7 +457,7 @@ void SetupServerArgs(ArgsManager& argsman)
|
|||
"Warning: Reverting this setting requires re-downloading the entire blockchain. "
|
||||
"(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);
|
||||
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::OPTIONS);
|
||||
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::OPTIONS);
|
||||
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::OPTIONS);
|
||||
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);
|
||||
#if HAVE_SYSTEM
|
||||
argsman.AddArg("-startupnotify=<cmd>", "Execute command on startup.", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
|
||||
|
@ -982,19 +983,6 @@ bool AppInitParameterInteraction(const ArgsManager& args, bool use_syscall_sandb
|
|||
if (args.GetIntArg("-rpcserialversion", DEFAULT_RPC_SERIALIZE_VERSION) > 1)
|
||||
return InitError(Untranslated("Unknown rpcserialversion requested."));
|
||||
|
||||
if (args.GetBoolArg("-reindex-chainstate", false)) {
|
||||
// indexes that must be deactivated to prevent index corruption, see #24630
|
||||
if (args.GetBoolArg("-coinstatsindex", DEFAULT_COINSTATSINDEX)) {
|
||||
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."));
|
||||
}
|
||||
if (g_enabled_filter_types.count(BlockFilterType::BASIC)) {
|
||||
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."));
|
||||
}
|
||||
if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
|
||||
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."));
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(USE_SYSCALL_SANDBOX)
|
||||
if (args.IsArgSet("-sandbox") && !args.IsArgNegated("-sandbox")) {
|
||||
const std::string sandbox_arg{args.GetArg("-sandbox", "")};
|
||||
|
@ -1571,6 +1559,9 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
|
|||
RegisterValidationInterface(node.peerman.get());
|
||||
|
||||
// ********************************************************* Step 8: start indexers
|
||||
|
||||
// If reindex-chainstate was specified, delay syncing indexes until ThreadImport has reindexed the chain
|
||||
if (!fReindexChainState) g_indexes_ready_to_sync = true;
|
||||
if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
|
||||
if (const auto error{WITH_LOCK(cs_main, return CheckLegacyTxindex(*Assert(chainman.m_blockman.m_block_tree_db)))}) {
|
||||
return InitError(*error);
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
|
||||
namespace node {
|
||||
std::atomic_bool fReindex(false);
|
||||
std::atomic_bool g_indexes_ready_to_sync{false};
|
||||
|
||||
bool CBlockIndexWorkComparator::operator()(const CBlockIndex* pa, const CBlockIndex* pb) const
|
||||
{
|
||||
|
@ -940,5 +941,6 @@ void ThreadImport(ChainstateManager& chainman, std::vector<fs::path> vImportFile
|
|||
}
|
||||
} // End scope of ImportingNow
|
||||
chainman.ActiveChainstate().LoadMempool(mempool_path);
|
||||
g_indexes_ready_to_sync = true;
|
||||
}
|
||||
} // namespace node
|
||||
|
|
|
@ -47,6 +47,7 @@ static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB
|
|||
static constexpr size_t BLOCK_SERIALIZATION_HEADER_SIZE = CMessageHeader::MESSAGE_START_SIZE + sizeof(unsigned int);
|
||||
|
||||
extern std::atomic_bool fReindex;
|
||||
extern std::atomic_bool g_indexes_ready_to_sync;
|
||||
|
||||
// Because validation code takes pointers to the map's CBlockIndex objects, if
|
||||
// we ever switch to another associative container, we need to either use a
|
||||
|
|
|
@ -155,6 +155,7 @@ BasicTestingSetup::BasicTestingSetup(const ChainType chainType, const std::vecto
|
|||
noui_connect();
|
||||
noui_connected = true;
|
||||
}
|
||||
node::g_indexes_ready_to_sync = true;
|
||||
}
|
||||
|
||||
BasicTestingSetup::~BasicTestingSetup()
|
||||
|
|
|
@ -52,6 +52,7 @@ class CoinStatsIndexTest(BitcoinTestFramework):
|
|||
self._test_use_index_option()
|
||||
self._test_reorg_index()
|
||||
self._test_index_rejects_hash_serialized()
|
||||
self._test_init_index_after_reorg()
|
||||
|
||||
def block_sanity_check(self, block_info):
|
||||
block_subsidy = 50
|
||||
|
@ -60,6 +61,9 @@ class CoinStatsIndexTest(BitcoinTestFramework):
|
|||
block_info['new_outputs_ex_coinbase'] + block_info['coinbase'] + block_info['unspendable']
|
||||
)
|
||||
|
||||
def sync_index_node(self):
|
||||
self.wait_until(lambda: self.nodes[1].getindexinfo()['coinstatsindex']['synced'] is True)
|
||||
|
||||
def _test_coin_stats_index(self):
|
||||
node = self.nodes[0]
|
||||
index_node = self.nodes[1]
|
||||
|
@ -227,18 +231,16 @@ class CoinStatsIndexTest(BitcoinTestFramework):
|
|||
self.log.info("Test that the index works with -reindex")
|
||||
|
||||
self.restart_node(1, extra_args=["-coinstatsindex", "-reindex"])
|
||||
self.sync_index_node()
|
||||
res11 = index_node.gettxoutsetinfo('muhash')
|
||||
assert_equal(res11, res10)
|
||||
|
||||
self.log.info("Test that -reindex-chainstate is disallowed with coinstatsindex")
|
||||
self.log.info("Test that the index works with -reindex-chainstate")
|
||||
|
||||
self.stop_node(1)
|
||||
self.nodes[1].assert_start_raises_init_error(
|
||||
expected_msg='Error: -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.',
|
||||
extra_args=['-coinstatsindex', '-reindex-chainstate'],
|
||||
)
|
||||
self.restart_node(1, extra_args=["-coinstatsindex"])
|
||||
self.restart_node(1, extra_args=["-coinstatsindex", "-reindex-chainstate"])
|
||||
self.sync_index_node()
|
||||
res12 = index_node.gettxoutsetinfo('muhash')
|
||||
assert_equal(res12, res10)
|
||||
|
||||
def _test_use_index_option(self):
|
||||
self.log.info("Test use_index option for nodes running the index")
|
||||
|
@ -257,6 +259,7 @@ class CoinStatsIndexTest(BitcoinTestFramework):
|
|||
index_node = self.nodes[1]
|
||||
reorg_blocks = self.generatetoaddress(index_node, 2, getnewdestination()[2])
|
||||
reorg_block = reorg_blocks[1]
|
||||
self.sync_index_node()
|
||||
res_invalid = index_node.gettxoutsetinfo('muhash')
|
||||
index_node.invalidateblock(reorg_blocks[0])
|
||||
assert_equal(index_node.gettxoutsetinfo('muhash')['height'], 110)
|
||||
|
@ -296,6 +299,26 @@ class CoinStatsIndexTest(BitcoinTestFramework):
|
|||
for use_index in {True, False, None}:
|
||||
assert_raises_rpc_error(-8, msg, self.nodes[1].gettxoutsetinfo, hash_type='hash_serialized_2', hash_or_height=111, use_index=use_index)
|
||||
|
||||
def _test_init_index_after_reorg(self):
|
||||
self.log.info("Test a reorg while the index is deactivated")
|
||||
index_node = self.nodes[1]
|
||||
block = self.nodes[0].getbestblockhash()
|
||||
self.generate(index_node, 2, sync_fun=self.no_op)
|
||||
self.sync_index_node()
|
||||
|
||||
# Restart without index
|
||||
self.restart_node(1, extra_args=[])
|
||||
self.connect_nodes(0, 1)
|
||||
index_node.invalidateblock(block)
|
||||
self.generatetoaddress(index_node, 5, getnewdestination()[2])
|
||||
res = index_node.gettxoutsetinfo(hash_type='muhash', hash_or_height=None, use_index=False)
|
||||
|
||||
# Restart with index that still has its best block on the old chain
|
||||
self.restart_node(1, extra_args=self.extra_args[1])
|
||||
self.sync_index_node()
|
||||
res1 = index_node.gettxoutsetinfo(hash_type='muhash', hash_or_height=None, use_index=True)
|
||||
assert_equal(res["muhash"], res1["muhash"])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
CoinStatsIndexTest().main()
|
||||
|
|
|
@ -255,13 +255,6 @@ class CompactFiltersTest(BitcoinTestFramework):
|
|||
msg = "Error: Unknown -blockfilterindex value abc."
|
||||
self.nodes[0].assert_start_raises_init_error(expected_msg=msg)
|
||||
|
||||
self.log.info("Test -blockfilterindex with -reindex-chainstate raises an error")
|
||||
self.nodes[0].assert_start_raises_init_error(
|
||||
expected_msg='Error: -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.',
|
||||
extra_args=['-blockfilterindex', '-reindex-chainstate'],
|
||||
)
|
||||
|
||||
def compute_last_header(prev_header, hashes):
|
||||
"""Compute the last filter header from a starting header and a sequence of filter hashes."""
|
||||
header = ser_uint256(prev_header)
|
||||
|
|
Loading…
Add table
Reference in a new issue