Skip to content

Commit c1d8870

Browse files
refactor: Move most of init retry for loop to a function
This makes it clearer which state is being mutated by the function and facilitates getting rid of the for loop in the following commit. Move creation of the required options into the function too, such that the function takes fewer arguments and is more self-contained. Co-Authored-By: Ryan Ofsky <[email protected]>
1 parent 781c01f commit c1d8870

File tree

1 file changed

+112
-97
lines changed

1 file changed

+112
-97
lines changed

src/init.cpp

+112-97
Original file line numberDiff line numberDiff line change
@@ -123,17 +123,19 @@ using node::ApplyArgsManOptions;
123123
using node::BlockManager;
124124
using node::CacheSizes;
125125
using node::CalculateCacheSizes;
126+
using node::ChainstateLoadResult;
127+
using node::ChainstateLoadStatus;
126128
using node::DEFAULT_PERSIST_MEMPOOL;
127129
using node::DEFAULT_PRINT_MODIFIED_FEE;
128130
using node::DEFAULT_STOPATHEIGHT;
129131
using node::DumpMempool;
130-
using node::LoadMempool;
132+
using node::ImportBlocks;
131133
using node::KernelNotifications;
132134
using node::LoadChainstate;
135+
using node::LoadMempool;
133136
using node::MempoolPath;
134137
using node::NodeContext;
135138
using node::ShouldPersistMempool;
136-
using node::ImportBlocks;
137139
using node::VerifyLoadedChainstate;
138140
using util::Join;
139141
using util::ReplaceAll;
@@ -1183,6 +1185,104 @@ bool CheckHostPortOptions(const ArgsManager& args) {
11831185
return true;
11841186
}
11851187

1188+
// A GUI user may opt to retry once if there is a failure during chainstate initialization.
1189+
// The function therefore has to support re-entry.
1190+
static ChainstateLoadResult InitAndLoadChainstate(
1191+
NodeContext& node,
1192+
bool do_reindex,
1193+
const bool do_reindex_chainstate,
1194+
CacheSizes& cache_sizes,
1195+
const ArgsManager& args)
1196+
{
1197+
const CChainParams& chainparams = Params();
1198+
CTxMemPool::Options mempool_opts{
1199+
.check_ratio = chainparams.DefaultConsistencyChecks() ? 1 : 0,
1200+
.signals = node.validation_signals.get(),
1201+
};
1202+
Assert(ApplyArgsManOptions(args, chainparams, mempool_opts)); // no error can happen, already checked in AppInitParameterInteraction
1203+
bilingual_str mempool_error;
1204+
node.mempool = std::make_unique<CTxMemPool>(mempool_opts, mempool_error);
1205+
if (!mempool_error.empty()) {
1206+
return {ChainstateLoadStatus::FAILURE_FATAL, mempool_error};
1207+
}
1208+
LogPrintf("* Using %.1f MiB for in-memory UTXO set (plus up to %.1f MiB of unused mempool space)\n", cache_sizes.coins * (1.0 / 1024 / 1024), mempool_opts.max_size_bytes * (1.0 / 1024 / 1024));
1209+
ChainstateManager::Options chainman_opts{
1210+
.chainparams = chainparams,
1211+
.datadir = args.GetDataDirNet(),
1212+
.notifications = *node.notifications,
1213+
.signals = node.validation_signals.get(),
1214+
};
1215+
Assert(ApplyArgsManOptions(args, chainman_opts)); // no error can happen, already checked in AppInitParameterInteraction
1216+
BlockManager::Options blockman_opts{
1217+
.chainparams = chainman_opts.chainparams,
1218+
.blocks_dir = args.GetBlocksDirPath(),
1219+
.notifications = chainman_opts.notifications,
1220+
};
1221+
Assert(ApplyArgsManOptions(args, blockman_opts)); // no error can happen, already checked in AppInitParameterInteraction
1222+
try {
1223+
node.chainman = std::make_unique<ChainstateManager>(*Assert(node.shutdown), chainman_opts, blockman_opts);
1224+
} catch (std::exception& e) {
1225+
return {ChainstateLoadStatus::FAILURE_FATAL, strprintf(Untranslated("Failed to initialize ChainstateManager: %s"), e.what())};
1226+
}
1227+
ChainstateManager& chainman = *node.chainman;
1228+
// This is defined and set here instead of inline in validation.h to avoid a hard
1229+
// dependency between validation and index/base, since the latter is not in
1230+
// libbitcoinkernel.
1231+
chainman.snapshot_download_completed = [&node]() {
1232+
if (!node.chainman->m_blockman.IsPruneMode()) {
1233+
LogPrintf("[snapshot] re-enabling NODE_NETWORK services\n");
1234+
node.connman->AddLocalServices(NODE_NETWORK);
1235+
}
1236+
LogPrintf("[snapshot] restarting indexes\n");
1237+
// Drain the validation interface queue to ensure that the old indexes
1238+
// don't have any pending work.
1239+
Assert(node.validation_signals)->SyncWithValidationInterfaceQueue();
1240+
for (auto* index : node.indexes) {
1241+
index->Interrupt();
1242+
index->Stop();
1243+
if (!(index->Init() && index->StartBackgroundSync())) {
1244+
LogPrintf("[snapshot] WARNING failed to restart index %s on snapshot chain\n", index->GetName());
1245+
}
1246+
}
1247+
};
1248+
node::ChainstateLoadOptions options;
1249+
options.mempool = Assert(node.mempool.get());
1250+
options.wipe_block_tree_db = do_reindex;
1251+
options.wipe_chainstate_db = do_reindex || do_reindex_chainstate;
1252+
options.prune = chainman.m_blockman.IsPruneMode();
1253+
options.check_blocks = args.GetIntArg("-checkblocks", DEFAULT_CHECKBLOCKS);
1254+
options.check_level = args.GetIntArg("-checklevel", DEFAULT_CHECKLEVEL);
1255+
options.require_full_verification = args.IsArgSet("-checkblocks") || args.IsArgSet("-checklevel");
1256+
options.coins_error_cb = [] {
1257+
uiInterface.ThreadSafeMessageBox(
1258+
_("Error reading from database, shutting down."),
1259+
"", CClientUIInterface::MSG_ERROR);
1260+
};
1261+
uiInterface.InitMessage(_("Loading block index…").translated);
1262+
const auto load_block_index_start_time{SteadyClock::now()};
1263+
auto catch_exceptions = [](auto&& f) {
1264+
try {
1265+
return f();
1266+
} catch (const std::exception& e) {
1267+
LogError("%s\n", e.what());
1268+
return std::make_tuple(node::ChainstateLoadStatus::FAILURE, _("Error opening block database"));
1269+
}
1270+
};
1271+
auto [status, error] = catch_exceptions([&] { return LoadChainstate(chainman, cache_sizes, options); });
1272+
if (status == node::ChainstateLoadStatus::SUCCESS) {
1273+
uiInterface.InitMessage(_("Verifying blocks…").translated);
1274+
if (chainman.m_blockman.m_have_pruned && options.check_blocks > MIN_BLOCKS_TO_KEEP) {
1275+
LogWarning("pruned datadir may not have more than %d blocks; only checking available blocks\n",
1276+
MIN_BLOCKS_TO_KEEP);
1277+
}
1278+
std::tie(status, error) = catch_exceptions([&] { return VerifyLoadedChainstate(chainman, options); });
1279+
if (status == node::ChainstateLoadStatus::SUCCESS) {
1280+
LogPrintf(" block index %15dms\n", Ticks<std::chrono::milliseconds>(SteadyClock::now() - load_block_index_start_time));
1281+
}
1282+
}
1283+
return {status, error};
1284+
};
1285+
11861286
bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
11871287
{
11881288
const ArgsManager& args = *Assert(node.args);
@@ -1514,20 +1614,6 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
15141614

15151615
node.notifications = std::make_unique<KernelNotifications>(*Assert(node.shutdown), node.exit_status, *Assert(node.warnings));
15161616
ReadNotificationArgs(args, *node.notifications);
1517-
ChainstateManager::Options chainman_opts{
1518-
.chainparams = chainparams,
1519-
.datadir = args.GetDataDirNet(),
1520-
.notifications = *node.notifications,
1521-
.signals = &validation_signals,
1522-
};
1523-
Assert(ApplyArgsManOptions(args, chainman_opts)); // no error can happen, already checked in AppInitParameterInteraction
1524-
1525-
BlockManager::Options blockman_opts{
1526-
.chainparams = chainman_opts.chainparams,
1527-
.blocks_dir = args.GetBlocksDirPath(),
1528-
.notifications = chainman_opts.notifications,
1529-
};
1530-
Assert(ApplyArgsManOptions(args, blockman_opts)); // no error can happen, already checked in AppInitParameterInteraction
15311617

15321618
// cache size calculations
15331619
CacheSizes cache_sizes = CalculateCacheSizes(args, g_enabled_filter_types.size());
@@ -1546,96 +1632,25 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
15461632
assert(!node.mempool);
15471633
assert(!node.chainman);
15481634

1549-
CTxMemPool::Options mempool_opts{
1550-
.check_ratio = chainparams.DefaultConsistencyChecks() ? 1 : 0,
1551-
.signals = &validation_signals,
1552-
};
1553-
Assert(ApplyArgsManOptions(args, chainparams, mempool_opts)); // no error can happen, already checked in AppInitParameterInteraction
1554-
15551635
bool do_reindex{args.GetBoolArg("-reindex", false)};
15561636
const bool do_reindex_chainstate{args.GetBoolArg("-reindex-chainstate", false)};
15571637

15581638
for (bool fLoaded = false; !fLoaded && !ShutdownRequested(node);) {
1559-
bilingual_str mempool_error;
1560-
node.mempool = std::make_unique<CTxMemPool>(mempool_opts, mempool_error);
1561-
if (!mempool_error.empty()) {
1562-
return InitError(mempool_error);
1563-
}
1564-
LogPrintf("* Using %.1f MiB for in-memory UTXO set (plus up to %.1f MiB of unused mempool space)\n", cache_sizes.coins * (1.0 / 1024 / 1024), mempool_opts.max_size_bytes * (1.0 / 1024 / 1024));
1565-
1566-
try {
1567-
node.chainman = std::make_unique<ChainstateManager>(*Assert(node.shutdown), chainman_opts, blockman_opts);
1568-
} catch (std::exception& e) {
1569-
return InitError(strprintf(Untranslated("Failed to initialize ChainstateManager: %s"), e.what()));
1570-
}
1571-
ChainstateManager& chainman = *node.chainman;
1572-
1573-
// This is defined and set here instead of inline in validation.h to avoid a hard
1574-
// dependency between validation and index/base, since the latter is not in
1575-
// libbitcoinkernel.
1576-
chainman.snapshot_download_completed = [&node]() {
1577-
if (!node.chainman->m_blockman.IsPruneMode()) {
1578-
LogPrintf("[snapshot] re-enabling NODE_NETWORK services\n");
1579-
node.connman->AddLocalServices(NODE_NETWORK);
1580-
}
1581-
1582-
LogPrintf("[snapshot] restarting indexes\n");
1583-
1584-
// Drain the validation interface queue to ensure that the old indexes
1585-
// don't have any pending work.
1586-
Assert(node.validation_signals)->SyncWithValidationInterfaceQueue();
1587-
1588-
for (auto* index : node.indexes) {
1589-
index->Interrupt();
1590-
index->Stop();
1591-
if (!(index->Init() && index->StartBackgroundSync())) {
1592-
LogPrintf("[snapshot] WARNING failed to restart index %s on snapshot chain\n", index->GetName());
1593-
}
1594-
}
1595-
};
1596-
1597-
node::ChainstateLoadOptions options;
1598-
options.mempool = Assert(node.mempool.get());
1599-
options.wipe_block_tree_db = do_reindex;
1600-
options.wipe_chainstate_db = do_reindex || do_reindex_chainstate;
1601-
options.prune = chainman.m_blockman.IsPruneMode();
1602-
options.check_blocks = args.GetIntArg("-checkblocks", DEFAULT_CHECKBLOCKS);
1603-
options.check_level = args.GetIntArg("-checklevel", DEFAULT_CHECKLEVEL);
1604-
options.require_full_verification = args.IsArgSet("-checkblocks") || args.IsArgSet("-checklevel");
1605-
options.coins_error_cb = [] {
1606-
uiInterface.ThreadSafeMessageBox(
1607-
_("Error reading from database, shutting down."),
1608-
"", CClientUIInterface::MSG_ERROR);
1609-
};
1610-
1611-
uiInterface.InitMessage(_("Loading block index…").translated);
1612-
const auto load_block_index_start_time{SteadyClock::now()};
1613-
auto catch_exceptions = [](auto&& f) {
1614-
try {
1615-
return f();
1616-
} catch (const std::exception& e) {
1617-
LogError("%s\n", e.what());
1618-
return std::make_tuple(node::ChainstateLoadStatus::FAILURE, _("Error opening block database"));
1619-
}
1620-
};
1621-
auto [status, error] = catch_exceptions([&]{ return LoadChainstate(chainman, cache_sizes, options); });
1622-
if (status == node::ChainstateLoadStatus::SUCCESS) {
1623-
uiInterface.InitMessage(_("Verifying blocks…").translated);
1624-
if (chainman.m_blockman.m_have_pruned && options.check_blocks > MIN_BLOCKS_TO_KEEP) {
1625-
LogWarning("pruned datadir may not have more than %d blocks; only checking available blocks\n",
1626-
MIN_BLOCKS_TO_KEEP);
1627-
}
1628-
std::tie(status, error) = catch_exceptions([&]{ return VerifyLoadedChainstate(chainman, options);});
1629-
if (status == node::ChainstateLoadStatus::SUCCESS) {
1630-
fLoaded = true;
1631-
LogPrintf(" block index %15dms\n", Ticks<std::chrono::milliseconds>(SteadyClock::now() - load_block_index_start_time));
1632-
}
1633-
}
1639+
auto [status, error] = InitAndLoadChainstate(
1640+
node,
1641+
do_reindex,
1642+
do_reindex_chainstate,
1643+
cache_sizes,
1644+
args);
16341645

16351646
if (status == node::ChainstateLoadStatus::FAILURE_FATAL || status == node::ChainstateLoadStatus::FAILURE_INCOMPATIBLE_DB || status == node::ChainstateLoadStatus::FAILURE_INSUFFICIENT_DBCACHE) {
16361647
return InitError(error);
16371648
}
16381649

1650+
if (status == ChainstateLoadStatus::SUCCESS) {
1651+
fLoaded = true;
1652+
}
1653+
16391654
if (!fLoaded && !ShutdownRequested(node)) {
16401655
// first suggest a reindex
16411656
if (!do_reindex) {

0 commit comments

Comments
 (0)