@@ -123,17 +123,19 @@ using node::ApplyArgsManOptions;
123
123
using node::BlockManager;
124
124
using node::CacheSizes;
125
125
using node::CalculateCacheSizes;
126
+ using node::ChainstateLoadResult;
127
+ using node::ChainstateLoadStatus;
126
128
using node::DEFAULT_PERSIST_MEMPOOL;
127
129
using node::DEFAULT_PRINT_MODIFIED_FEE;
128
130
using node::DEFAULT_STOPATHEIGHT;
129
131
using node::DumpMempool;
130
- using node::LoadMempool ;
132
+ using node::ImportBlocks ;
131
133
using node::KernelNotifications;
132
134
using node::LoadChainstate;
135
+ using node::LoadMempool;
133
136
using node::MempoolPath;
134
137
using node::NodeContext;
135
138
using node::ShouldPersistMempool;
136
- using node::ImportBlocks;
137
139
using node::VerifyLoadedChainstate;
138
140
using util::Join;
139
141
using util::ReplaceAll;
@@ -1068,6 +1070,13 @@ bool AppInitParameterInteraction(const ArgsManager& args)
1068
1070
if (!blockman_result) {
1069
1071
return InitError (util::ErrorString (blockman_result));
1070
1072
}
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
+ }
1071
1080
}
1072
1081
1073
1082
return true ;
@@ -1172,6 +1181,104 @@ bool CheckHostPortOptions(const ArgsManager& args) {
1172
1181
return true ;
1173
1182
}
1174
1183
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
+
1175
1282
bool AppInitMain (NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
1176
1283
{
1177
1284
const ArgsManager& args = *Assert (node.args );
@@ -1503,20 +1610,6 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
1503
1610
1504
1611
node.notifications = std::make_unique<KernelNotifications>(*Assert (node.shutdown ), node.exit_status , *Assert (node.warnings ));
1505
1612
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
1520
1613
1521
1614
// cache size calculations
1522
1615
CacheSizes cache_sizes = CalculateCacheSizes (args, g_enabled_filter_types.size ());
@@ -1535,119 +1628,39 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
1535
1628
assert (!node.mempool );
1536
1629
assert (!node.chainman );
1537
1630
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
-
1547
1631
bool do_reindex{args.GetBoolArg (" -reindex" , false )};
1548
1632
const bool do_reindex_chainstate{args.GetBoolArg (" -reindex-chainstate" , false )};
1549
1633
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 ;
1629
1650
}
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 " );
1650
1654
}
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);
1651
1664
}
1652
1665
1653
1666
// As LoadBlockIndex can take several minutes, it's possible the user
0 commit comments