@@ -123,17 +123,19 @@ using node::ApplyArgsManOptions;
123123using node::BlockManager;
124124using node::CacheSizes;
125125using node::CalculateCacheSizes;
126+ using node::ChainstateLoadResult;
127+ using node::ChainstateLoadStatus;
126128using node::DEFAULT_PERSIST_MEMPOOL;
127129using node::DEFAULT_PRINT_MODIFIED_FEE;
128130using node::DEFAULT_STOPATHEIGHT;
129131using node::DumpMempool;
130- using node::LoadMempool ;
132+ using node::ImportBlocks ;
131133using node::KernelNotifications;
132134using node::LoadChainstate;
135+ using node::LoadMempool;
133136using node::MempoolPath;
134137using node::NodeContext;
135138using node::ShouldPersistMempool;
136- using node::ImportBlocks;
137139using node::VerifyLoadedChainstate;
138140using util::Join;
139141using 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+
11861286bool 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