@@ -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;
@@ -1068,6 +1070,13 @@ bool AppInitParameterInteraction(const ArgsManager& args)
10681070 if (!blockman_result) {
10691071 return InitError (util::ErrorString (blockman_result));
10701072 }
1073+ CTxMemPool::Options mempool_opts{
1074+ .check_ratio = chainparams.DefaultConsistencyChecks () ? 1 : 0 ,
1075+ };
1076+ auto mempool_result{ApplyArgsManOptions (args, chainparams, mempool_opts)};
1077+ if (!mempool_result) {
1078+ return InitError (util::ErrorString (mempool_result));
1079+ }
10711080 }
10721081
10731082 return true ;
@@ -1172,6 +1181,104 @@ bool CheckHostPortOptions(const ArgsManager& args) {
11721181 return true ;
11731182}
11741183
1184+ // A GUI user may opt to retry once if there is a failure during chainstate initialization.
1185+ // The function therefore has to support re-entry.
1186+ static ChainstateLoadResult InitAndLoadChainstate (
1187+ NodeContext& node,
1188+ bool do_reindex,
1189+ const bool do_reindex_chainstate,
1190+ CacheSizes& cache_sizes,
1191+ const ArgsManager& args)
1192+ {
1193+ const CChainParams& chainparams = Params ();
1194+ CTxMemPool::Options mempool_opts{
1195+ .check_ratio = chainparams.DefaultConsistencyChecks () ? 1 : 0 ,
1196+ .signals = node.validation_signals .get (),
1197+ };
1198+ Assert (ApplyArgsManOptions (args, chainparams, mempool_opts)); // no error can happen, already checked in AppInitParameterInteraction
1199+ bilingual_str mempool_error;
1200+ node.mempool = std::make_unique<CTxMemPool>(mempool_opts, mempool_error);
1201+ if (!mempool_error.empty ()) {
1202+ return {ChainstateLoadStatus::FAILURE_FATAL, mempool_error};
1203+ }
1204+ 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 ));
1205+ ChainstateManager::Options chainman_opts{
1206+ .chainparams = chainparams,
1207+ .datadir = args.GetDataDirNet (),
1208+ .notifications = *node.notifications ,
1209+ .signals = node.validation_signals .get (),
1210+ };
1211+ Assert (ApplyArgsManOptions (args, chainman_opts)); // no error can happen, already checked in AppInitParameterInteraction
1212+ BlockManager::Options blockman_opts{
1213+ .chainparams = chainman_opts.chainparams ,
1214+ .blocks_dir = args.GetBlocksDirPath (),
1215+ .notifications = chainman_opts.notifications ,
1216+ };
1217+ Assert (ApplyArgsManOptions (args, blockman_opts)); // no error can happen, already checked in AppInitParameterInteraction
1218+ try {
1219+ node.chainman = std::make_unique<ChainstateManager>(*Assert (node.shutdown ), chainman_opts, blockman_opts);
1220+ } catch (std::exception& e) {
1221+ return {ChainstateLoadStatus::FAILURE_FATAL, strprintf (Untranslated (" Failed to initialize ChainstateManager: %s" ), e.what ())};
1222+ }
1223+ ChainstateManager& chainman = *node.chainman ;
1224+ // This is defined and set here instead of inline in validation.h to avoid a hard
1225+ // dependency between validation and index/base, since the latter is not in
1226+ // libbitcoinkernel.
1227+ chainman.snapshot_download_completed = [&node]() {
1228+ if (!node.chainman ->m_blockman .IsPruneMode ()) {
1229+ LogPrintf (" [snapshot] re-enabling NODE_NETWORK services\n " );
1230+ node.connman ->AddLocalServices (NODE_NETWORK);
1231+ }
1232+ LogPrintf (" [snapshot] restarting indexes\n " );
1233+ // Drain the validation interface queue to ensure that the old indexes
1234+ // don't have any pending work.
1235+ Assert (node.validation_signals )->SyncWithValidationInterfaceQueue ();
1236+ for (auto * index : node.indexes ) {
1237+ index->Interrupt ();
1238+ index->Stop ();
1239+ if (!(index->Init () && index->StartBackgroundSync ())) {
1240+ LogPrintf (" [snapshot] WARNING failed to restart index %s on snapshot chain\n " , index->GetName ());
1241+ }
1242+ }
1243+ };
1244+ node::ChainstateLoadOptions options;
1245+ options.mempool = Assert (node.mempool .get ());
1246+ options.wipe_block_tree_db = do_reindex;
1247+ options.wipe_chainstate_db = do_reindex || do_reindex_chainstate;
1248+ options.prune = chainman.m_blockman .IsPruneMode ();
1249+ options.check_blocks = args.GetIntArg (" -checkblocks" , DEFAULT_CHECKBLOCKS);
1250+ options.check_level = args.GetIntArg (" -checklevel" , DEFAULT_CHECKLEVEL);
1251+ options.require_full_verification = args.IsArgSet (" -checkblocks" ) || args.IsArgSet (" -checklevel" );
1252+ options.coins_error_cb = [] {
1253+ uiInterface.ThreadSafeMessageBox (
1254+ _ (" Error reading from database, shutting down." ),
1255+ " " , CClientUIInterface::MSG_ERROR);
1256+ };
1257+ uiInterface.InitMessage (_ (" Loading block index…" ).translated );
1258+ const auto load_block_index_start_time{SteadyClock::now ()};
1259+ auto catch_exceptions = [](auto && f) {
1260+ try {
1261+ return f ();
1262+ } catch (const std::exception& e) {
1263+ LogError (" %s\n " , e.what ());
1264+ return std::make_tuple (node::ChainstateLoadStatus::FAILURE, _ (" Error opening block database" ));
1265+ }
1266+ };
1267+ auto [status, error] = catch_exceptions ([&] { return LoadChainstate (chainman, cache_sizes, options); });
1268+ if (status == node::ChainstateLoadStatus::SUCCESS) {
1269+ uiInterface.InitMessage (_ (" Verifying blocks…" ).translated );
1270+ if (chainman.m_blockman .m_have_pruned && options.check_blocks > MIN_BLOCKS_TO_KEEP) {
1271+ LogWarning (" pruned datadir may not have more than %d blocks; only checking available blocks\n " ,
1272+ MIN_BLOCKS_TO_KEEP);
1273+ }
1274+ std::tie (status, error) = catch_exceptions ([&] { return VerifyLoadedChainstate (chainman, options); });
1275+ if (status == node::ChainstateLoadStatus::SUCCESS) {
1276+ LogPrintf (" block index %15dms\n " , Ticks<std::chrono::milliseconds>(SteadyClock::now () - load_block_index_start_time));
1277+ }
1278+ }
1279+ return {status, error};
1280+ };
1281+
11751282bool AppInitMain (NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
11761283{
11771284 const ArgsManager& args = *Assert (node.args );
@@ -1503,20 +1610,6 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
15031610
15041611 node.notifications = std::make_unique<KernelNotifications>(*Assert (node.shutdown ), node.exit_status , *Assert (node.warnings ));
15051612 ReadNotificationArgs (args, *node.notifications );
1506- ChainstateManager::Options chainman_opts{
1507- .chainparams = chainparams,
1508- .datadir = args.GetDataDirNet (),
1509- .notifications = *node.notifications ,
1510- .signals = &validation_signals,
1511- };
1512- Assert (ApplyArgsManOptions (args, chainman_opts)); // no error can happen, already checked in AppInitParameterInteraction
1513-
1514- BlockManager::Options blockman_opts{
1515- .chainparams = chainman_opts.chainparams ,
1516- .blocks_dir = args.GetBlocksDirPath (),
1517- .notifications = chainman_opts.notifications ,
1518- };
1519- Assert (ApplyArgsManOptions (args, blockman_opts)); // no error can happen, already checked in AppInitParameterInteraction
15201613
15211614 // cache size calculations
15221615 CacheSizes cache_sizes = CalculateCacheSizes (args, g_enabled_filter_types.size ());
@@ -1535,119 +1628,39 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
15351628 assert (!node.mempool );
15361629 assert (!node.chainman );
15371630
1538- CTxMemPool::Options mempool_opts{
1539- .check_ratio = chainparams.DefaultConsistencyChecks () ? 1 : 0 ,
1540- .signals = &validation_signals,
1541- };
1542- auto result{ApplyArgsManOptions (args, chainparams, mempool_opts)};
1543- if (!result) {
1544- return InitError (util::ErrorString (result));
1545- }
1546-
15471631 bool do_reindex{args.GetBoolArg (" -reindex" , false )};
15481632 const bool do_reindex_chainstate{args.GetBoolArg (" -reindex-chainstate" , false )};
15491633
1550- for (bool fLoaded = false ; !fLoaded && !ShutdownRequested (node);) {
1551- bilingual_str mempool_error;
1552- node.mempool = std::make_unique<CTxMemPool>(mempool_opts, mempool_error);
1553- if (!mempool_error.empty ()) {
1554- return InitError (mempool_error);
1555- }
1556- 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 ));
1557-
1558- try {
1559- node.chainman = std::make_unique<ChainstateManager>(*Assert (node.shutdown ), chainman_opts, blockman_opts);
1560- } catch (std::exception& e) {
1561- return InitError (strprintf (Untranslated (" Failed to initialize ChainstateManager: %s" ), e.what ()));
1562- }
1563- ChainstateManager& chainman = *node.chainman ;
1564-
1565- // This is defined and set here instead of inline in validation.h to avoid a hard
1566- // dependency between validation and index/base, since the latter is not in
1567- // libbitcoinkernel.
1568- chainman.snapshot_download_completed = [&node]() {
1569- if (!node.chainman ->m_blockman .IsPruneMode ()) {
1570- LogPrintf (" [snapshot] re-enabling NODE_NETWORK services\n " );
1571- node.connman ->AddLocalServices (NODE_NETWORK);
1572- }
1573-
1574- LogPrintf (" [snapshot] restarting indexes\n " );
1575-
1576- // Drain the validation interface queue to ensure that the old indexes
1577- // don't have any pending work.
1578- Assert (node.validation_signals )->SyncWithValidationInterfaceQueue ();
1579-
1580- for (auto * index : node.indexes ) {
1581- index->Interrupt ();
1582- index->Stop ();
1583- if (!(index->Init () && index->StartBackgroundSync ())) {
1584- LogPrintf (" [snapshot] WARNING failed to restart index %s on snapshot chain\n " , index->GetName ());
1585- }
1586- }
1587- };
1588-
1589- node::ChainstateLoadOptions options;
1590- options.mempool = Assert (node.mempool .get ());
1591- options.wipe_block_tree_db = do_reindex;
1592- options.wipe_chainstate_db = do_reindex || do_reindex_chainstate;
1593- options.prune = chainman.m_blockman .IsPruneMode ();
1594- options.check_blocks = args.GetIntArg (" -checkblocks" , DEFAULT_CHECKBLOCKS);
1595- options.check_level = args.GetIntArg (" -checklevel" , DEFAULT_CHECKLEVEL);
1596- options.require_full_verification = args.IsArgSet (" -checkblocks" ) || args.IsArgSet (" -checklevel" );
1597- options.coins_error_cb = [] {
1598- uiInterface.ThreadSafeMessageBox (
1599- _ (" Error reading from database, shutting down." ),
1600- " " , CClientUIInterface::MSG_ERROR);
1601- };
1602-
1603- uiInterface.InitMessage (_ (" Loading block index…" ).translated );
1604- const auto load_block_index_start_time{SteadyClock::now ()};
1605- auto catch_exceptions = [](auto && f) {
1606- try {
1607- return f ();
1608- } catch (const std::exception& e) {
1609- LogError (" %s\n " , e.what ());
1610- return std::make_tuple (node::ChainstateLoadStatus::FAILURE, _ (" Error opening block database" ));
1611- }
1612- };
1613- auto [status, error] = catch_exceptions ([&]{ return LoadChainstate (chainman, cache_sizes, options); });
1614- if (status == node::ChainstateLoadStatus::SUCCESS) {
1615- uiInterface.InitMessage (_ (" Verifying blocks…" ).translated );
1616- if (chainman.m_blockman .m_have_pruned && options.check_blocks > MIN_BLOCKS_TO_KEEP) {
1617- LogWarning (" pruned datadir may not have more than %d blocks; only checking available blocks\n " ,
1618- MIN_BLOCKS_TO_KEEP);
1619- }
1620- std::tie (status, error) = catch_exceptions ([&]{ return VerifyLoadedChainstate (chainman, options);});
1621- if (status == node::ChainstateLoadStatus::SUCCESS) {
1622- fLoaded = true ;
1623- LogPrintf (" block index %15dms\n " , Ticks<std::chrono::milliseconds>(SteadyClock::now () - load_block_index_start_time));
1624- }
1625- }
1626-
1627- if (status == node::ChainstateLoadStatus::FAILURE_FATAL || status == node::ChainstateLoadStatus::FAILURE_INCOMPATIBLE_DB || status == node::ChainstateLoadStatus::FAILURE_INSUFFICIENT_DBCACHE) {
1628- return InitError (error);
1634+ // Chainstate initialization and loading may be retried once with reindexing by GUI users
1635+ auto [status, error] = InitAndLoadChainstate (
1636+ node,
1637+ do_reindex,
1638+ do_reindex_chainstate,
1639+ cache_sizes,
1640+ args);
1641+ if (status == ChainstateLoadStatus::FAILURE && !do_reindex && !ShutdownRequested (node)) {
1642+ // suggest a reindex
1643+ bool do_retry = uiInterface.ThreadSafeQuestion (
1644+ error + Untranslated (" .\n\n " ) + _ (" Do you want to rebuild the block database now?" ),
1645+ error.original + " .\n Please restart with -reindex or -reindex-chainstate to recover." ,
1646+ " " , CClientUIInterface::MSG_ERROR | CClientUIInterface::BTN_ABORT);
1647+ if (!do_retry) {
1648+ LogError (" Aborted block database rebuild. Exiting.\n " );
1649+ return false ;
16291650 }
1630-
1631- if (!fLoaded && !ShutdownRequested (node)) {
1632- // first suggest a reindex
1633- if (!do_reindex) {
1634- bool fRet = uiInterface.ThreadSafeQuestion (
1635- error + Untranslated (" .\n\n " ) + _ (" Do you want to rebuild the block database now?" ),
1636- error.original + " .\n Please restart with -reindex or -reindex-chainstate to recover." ,
1637- " " , CClientUIInterface::MSG_ERROR | CClientUIInterface::BTN_ABORT);
1638- if (fRet ) {
1639- do_reindex = true ;
1640- if (!Assert (node.shutdown )->reset ()) {
1641- LogError (" Internal error: failed to reset shutdown signal.\n " );
1642- }
1643- } else {
1644- LogError (" Aborted block database rebuild. Exiting.\n " );
1645- return false ;
1646- }
1647- } else {
1648- return InitError (error);
1649- }
1651+ do_reindex = true ;
1652+ if (!Assert (node.shutdown )->reset ()) {
1653+ LogError (" Internal error: failed to reset shutdown signal.\n " );
16501654 }
1655+ std::tie (status, error) = InitAndLoadChainstate (
1656+ node,
1657+ do_reindex,
1658+ do_reindex_chainstate,
1659+ cache_sizes,
1660+ args);
1661+ }
1662+ if (status != ChainstateLoadStatus::SUCCESS && status != ChainstateLoadStatus::INTERRUPTED) {
1663+ return InitError (error);
16511664 }
16521665
16531666 // As LoadBlockIndex can take several minutes, it's possible the user
0 commit comments