From 2db5ddf52b4b8100b03c1235d3e94a00d66a16cb Mon Sep 17 00:00:00 2001 From: TheCharlatan Date: Tue, 9 May 2023 11:15:46 +0200 Subject: [PATCH] kernel: Add fatalError method to notifications FatalError replaces what previously was the AbortNode function in shutdown.cpp. This commit is part of the libbitcoinkernel project and removes the shutdown's and, more generally, the kernel library's dependency on interface_ui with an options method. By removing interface_ui from the kernel library, its dependency on boost is reduced to just boost::multi_index. Also add shutdown switch in node::KernelNotifications. This simplifies testing of validation methods that may normally trigger a shutdown on a fatal error by leaving out the call to StartShutdown. Co-authored-by: Russell Yanofsky Co-authored-by: TheCharlatan --- src/Makefile.am | 1 - src/bitcoin-chainstate.cpp | 13 ++++++- src/init.cpp | 2 + src/kernel/blockmanager_opts.h | 2 + src/kernel/notifications_interface.h | 9 ++++- src/node/blockstorage.cpp | 13 ++++--- src/node/chainstate.cpp | 2 +- src/node/kernel_notifications.cpp | 10 +++++ src/node/kernel_notifications.h | 5 +++ src/shutdown.cpp | 14 ------- src/shutdown.h | 5 --- src/test/blockmanager_tests.cpp | 6 ++- src/test/util/setup_common.cpp | 1 + .../validation_chainstatemanager_tests.cpp | 14 +++---- src/validation.cpp | 37 +++++++++---------- src/validation.h | 7 +--- 16 files changed, 79 insertions(+), 62 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index 0b25ef9c6b445..9576f7848383d 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -939,7 +939,6 @@ libbitcoinkernel_la_SOURCES = \ logging.cpp \ node/blockstorage.cpp \ node/chainstate.cpp \ - node/interface_ui.cpp \ node/utxo_snapshot.cpp \ policy/feerate.cpp \ policy/fees.cpp \ diff --git a/src/bitcoin-chainstate.cpp b/src/bitcoin-chainstate.cpp index 5250272724186..9225f2c6e337b 100644 --- a/src/bitcoin-chainstate.cpp +++ b/src/bitcoin-chainstate.cpp @@ -90,7 +90,11 @@ int main(int argc, char* argv[]) class KernelNotifications : public kernel::Notifications { + private: + std::atomic& m_shutdown_requested; + public: + KernelNotifications(std::atomic& shutdown_requested) : m_shutdown_requested{shutdown_requested} {} void blockTip(SynchronizationState, CBlockIndex&) override { std::cout << "Block tip changed" << std::endl; @@ -107,8 +111,14 @@ int main(int argc, char* argv[]) { std::cout << "Warning: " << warning.original << std::endl; } + void fatalError(const std::string& debug_message, const bilingual_str& user_message) override + { + m_shutdown_requested = true; + std::cerr << "Error: " << debug_message << std::endl; + std::cerr << (user_message.empty() ? "A fatal internal error occurred." : user_message.original) << std::endl; + } }; - auto notifications = std::make_unique(); + auto notifications = std::make_unique(shutdown_requested); // SETUP: Chainstate const ChainstateManager::Options chainman_opts{ @@ -122,6 +132,7 @@ int main(int argc, char* argv[]) .chainparams = chainman_opts.chainparams, .blocks_dir = gArgs.GetBlocksDirPath(), .shutdown_requested = chainman_opts.shutdown_requested, + .notifications = chainman_opts.notifications, }; ChainstateManager chainman{chainman_opts, blockman_opts}; diff --git a/src/init.cpp b/src/init.cpp index ceca9ee121fee..3f6b998f06c13 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1037,6 +1037,7 @@ bool AppInitParameterInteraction(const ArgsManager& args, bool use_syscall_sandb .chainparams = chainman_opts_dummy.chainparams, .blocks_dir = args.GetBlocksDirPath(), .shutdown_requested = chainman_opts_dummy.shutdown_requested, + .notifications = chainman_opts_dummy.notifications, }; auto blockman_result{ApplyArgsManOptions(args, blockman_opts_dummy)}; if (!blockman_result) { @@ -1453,6 +1454,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) .chainparams = chainman_opts.chainparams, .blocks_dir = args.GetBlocksDirPath(), .shutdown_requested = chainman_opts.shutdown_requested, + .notifications = chainman_opts.notifications, }; Assert(ApplyArgsManOptions(args, blockman_opts)); // no error can happen, already checked in AppInitParameterInteraction diff --git a/src/kernel/blockmanager_opts.h b/src/kernel/blockmanager_opts.h index 17b7f05584fb8..10f87ab37c5ec 100644 --- a/src/kernel/blockmanager_opts.h +++ b/src/kernel/blockmanager_opts.h @@ -5,6 +5,7 @@ #ifndef BITCOIN_KERNEL_BLOCKMANAGER_OPTS_H #define BITCOIN_KERNEL_BLOCKMANAGER_OPTS_H +#include #include #include @@ -27,6 +28,7 @@ struct BlockManagerOpts { bool stop_after_block_import{DEFAULT_STOPAFTERBLOCKIMPORT}; const fs::path blocks_dir; std::atomic& shutdown_requested; + Notifications& notifications; }; } // namespace kernel diff --git a/src/kernel/notifications_interface.h b/src/kernel/notifications_interface.h index 48248e9aa0b65..39da87805f0b5 100644 --- a/src/kernel/notifications_interface.h +++ b/src/kernel/notifications_interface.h @@ -5,12 +5,13 @@ #ifndef BITCOIN_KERNEL_NOTIFICATIONS_INTERFACE_H #define BITCOIN_KERNEL_NOTIFICATIONS_INTERFACE_H +#include + #include #include class CBlockIndex; enum class SynchronizationState; -struct bilingual_str; namespace kernel { @@ -27,6 +28,12 @@ class Notifications virtual void headerTip(SynchronizationState state, int64_t height, int64_t timestamp, bool presync) {} virtual void progress(const bilingual_str& title, int progress_percent, bool resume_possible) {} virtual void warning(const bilingual_str& warning) {} + + //! The fatal error notification is sent to notify the user and start + //! shutting down if an error happens in kernel code that can't be recovered + //! from. After this notification is sent, whatever function triggered the + //! error should also return an error code or raise an exception. + virtual void fatalError(const std::string& debug_message, const bilingual_str& user_message = {}) {} }; } // namespace kernel diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp index e0859e521b526..cc7def3094b02 100644 --- a/src/node/blockstorage.cpp +++ b/src/node/blockstorage.cpp @@ -529,7 +529,7 @@ void BlockManager::FlushUndoFile(int block_file, bool finalize) { FlatFilePos undo_pos_old(block_file, m_blockfile_info[block_file].nUndoSize); if (!UndoFileSeq().Flush(undo_pos_old, finalize)) { - AbortNode("Flushing undo file to disk failed. This is likely the result of an I/O error."); + m_opts.notifications.fatalError("Flushing undo file to disk failed. This is likely the result of an I/O error."); } } @@ -548,7 +548,7 @@ void BlockManager::FlushBlockFile(bool fFinalize, bool finalize_undo) FlatFilePos block_pos_old(m_last_blockfile, m_blockfile_info[m_last_blockfile].nSize); if (!BlockFileSeq().Flush(block_pos_old, fFinalize)) { - AbortNode("Flushing block file to disk failed. This is likely the result of an I/O error."); + m_opts.notifications.fatalError("Flushing block file to disk failed. This is likely the result of an I/O error."); } // we do not always flush the undo file, as the chain tip may be lagging behind the incoming blocks, // e.g. during IBD or a sync after a node going offline @@ -660,7 +660,8 @@ bool BlockManager::FindBlockPos(FlatFilePos& pos, unsigned int nAddSize, unsigne bool out_of_space; size_t bytes_allocated = BlockFileSeq().Allocate(pos, nAddSize, out_of_space); if (out_of_space) { - return AbortNode("Disk space is too low!", _("Disk space is too low!")); + m_opts.notifications.fatalError("Disk space is too low!", _("Disk space is too low!")); + return false; } if (bytes_allocated != 0 && IsPruneMode()) { m_check_for_pruning = true; @@ -684,7 +685,7 @@ bool BlockManager::FindUndoPos(BlockValidationState& state, int nFile, FlatFileP bool out_of_space; size_t bytes_allocated = UndoFileSeq().Allocate(pos, nAddSize, out_of_space); if (out_of_space) { - return AbortNode(state, "Disk space is too low!", _("Disk space is too low!")); + return FatalError(m_opts.notifications, state, "Disk space is too low!", _("Disk space is too low!")); } if (bytes_allocated != 0 && IsPruneMode()) { m_check_for_pruning = true; @@ -726,7 +727,7 @@ bool BlockManager::WriteUndoDataForBlock(const CBlockUndo& blockundo, BlockValid return error("ConnectBlock(): FindUndoPos failed"); } if (!UndoWriteToDisk(blockundo, _pos, block.pprev->GetBlockHash(), GetParams().MessageStart())) { - return AbortNode(state, "Failed to write undo data"); + return FatalError(m_opts.notifications, state, "Failed to write undo data"); } // rev files are written in block height order, whereas blk files are written as blocks come in (often out of order) // we want to flush the rev (undo) file once we've written the last block, which is indicated by the last height @@ -844,7 +845,7 @@ FlatFilePos BlockManager::SaveBlockToDisk(const CBlock& block, int nHeight, CCha } if (!position_known) { if (!WriteBlockToDisk(block, blockPos, GetParams().MessageStart())) { - AbortNode("Failed to write block"); + m_opts.notifications.fatalError("Failed to write block"); return FlatFilePos(); } } diff --git a/src/node/chainstate.cpp b/src/node/chainstate.cpp index 8f997b059477d..3a055c8e0bc71 100644 --- a/src/node/chainstate.cpp +++ b/src/node/chainstate.cpp @@ -207,7 +207,7 @@ ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSize } else if (snapshot_completion == SnapshotCompletionResult::SUCCESS) { LogPrintf("[snapshot] cleaning up unneeded background chainstate, then reinitializing\n"); if (!chainman.ValidatedSnapshotCleanup()) { - AbortNode("Background chainstate cleanup failed unexpectedly."); + chainman.GetNotifications().fatalError("Background chainstate cleanup failed unexpectedly."); } // Because ValidatedSnapshotCleanup() has torn down chainstates with diff --git a/src/node/kernel_notifications.cpp b/src/node/kernel_notifications.cpp index 926b157f3b131..1b8c9907470da 100644 --- a/src/node/kernel_notifications.cpp +++ b/src/node/kernel_notifications.cpp @@ -10,7 +10,9 @@ #include #include +#include #include +#include #include #include #include @@ -72,4 +74,12 @@ void KernelNotifications::warning(const bilingual_str& warning) DoWarning(warning); } +void KernelNotifications::fatalError(const std::string& debug_message, const bilingual_str& user_message) +{ + SetMiscWarning(Untranslated(debug_message)); + LogPrintf("*** %s\n", debug_message); + InitError(user_message.empty() ? _("A fatal internal error occurred, see debug.log for details") : user_message); + if (m_shutdown_on_fatal_error) StartShutdown(); +} + } // namespace node diff --git a/src/node/kernel_notifications.h b/src/node/kernel_notifications.h index 3e665bbf14e5c..84984a6759b50 100644 --- a/src/node/kernel_notifications.h +++ b/src/node/kernel_notifications.h @@ -25,6 +25,11 @@ class KernelNotifications : public kernel::Notifications void progress(const bilingual_str& title, int progress_percent, bool resume_possible) override; void warning(const bilingual_str& warning) override; + + void fatalError(const std::string& debug_message, const bilingual_str& user_message = {}) override; + + //! Useful for tests, can be set to false to avoid shutdown on fatal error. + bool m_shutdown_on_fatal_error{true}; }; } // namespace node diff --git a/src/shutdown.cpp b/src/shutdown.cpp index f3b0a7558b731..085b874660802 100644 --- a/src/shutdown.cpp +++ b/src/shutdown.cpp @@ -10,9 +10,7 @@ #endif #include -#include #include -#include #include #include @@ -20,18 +18,6 @@ #include #endif -bool AbortNode(const std::string& strMessage, bilingual_str user_message) -{ - SetMiscWarning(Untranslated(strMessage)); - LogPrintf("*** %s\n", strMessage); - if (user_message.empty()) { - user_message = _("A fatal internal error occurred, see debug.log for details"); - } - InitError(user_message); - StartShutdown(); - return false; -} - static std::atomic fRequestShutdown(false); #ifdef WIN32 /** On windows it is possible to simply use a condition variable. */ diff --git a/src/shutdown.h b/src/shutdown.h index b4721329fab90..1d8f57099f0fd 100644 --- a/src/shutdown.h +++ b/src/shutdown.h @@ -6,13 +6,8 @@ #ifndef BITCOIN_SHUTDOWN_H #define BITCOIN_SHUTDOWN_H -#include // For bilingual_str - #include -/** Abort with a message */ -bool AbortNode(const std::string& strMessage, bilingual_str user_message = bilingual_str{}); - /** Initialize shutdown state. This must be called before using either StartShutdown(), * AbortShutdown() or WaitForShutdown(). Calling ShutdownRequested() is always safe. */ diff --git a/src/test/blockmanager_tests.cpp b/src/test/blockmanager_tests.cpp index 7d905149afeae..1879866335a3a 100644 --- a/src/test/blockmanager_tests.cpp +++ b/src/test/blockmanager_tests.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -12,8 +13,9 @@ #include #include -using node::BlockManager; using node::BLOCK_SERIALIZATION_HEADER_SIZE; +using node::BlockManager; +using node::KernelNotifications; using node::MAX_BLOCKFILE_SIZE; // use BasicTestingSetup here for the data directory configuration, setup, and cleanup @@ -22,10 +24,12 @@ BOOST_FIXTURE_TEST_SUITE(blockmanager_tests, BasicTestingSetup) BOOST_AUTO_TEST_CASE(blockmanager_find_block_pos) { const auto params {CreateChainParams(ArgsManager{}, ChainType::MAIN)}; + KernelNotifications notifications{}; const BlockManager::Options blockman_opts{ .chainparams = *params, .blocks_dir = m_args.GetBlocksDirPath(), .shutdown_requested = GetRequestShutdownGlobal(), + .notifications = notifications, }; BlockManager blockman{blockman_opts}; CChain chain {}; diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index 383e99ee8c7d2..b5b438da4f735 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -198,6 +198,7 @@ ChainTestingSetup::ChainTestingSetup(const ChainType chainType, const std::vecto .chainparams = chainman_opts.chainparams, .blocks_dir = m_args.GetBlocksDirPath(), .shutdown_requested = chainman_opts.shutdown_requested, + .notifications = chainman_opts.notifications, }; m_node.chainman = std::make_unique(chainman_opts, blockman_opts); m_node.chainman->m_blockman.m_block_tree_db = std::make_unique(DBParams{ diff --git a/src/test/validation_chainstatemanager_tests.cpp b/src/test/validation_chainstatemanager_tests.cpp index 8dfe9dc7dbb3d..75fdfe407b960 100644 --- a/src/test/validation_chainstatemanager_tests.cpp +++ b/src/test/validation_chainstatemanager_tests.cpp @@ -392,6 +392,7 @@ struct SnapshotTestSetup : TestChain100Setup { .chainparams = chainman_opts.chainparams, .blocks_dir = m_args.GetBlocksDirPath(), .shutdown_requested = chainman_opts.shutdown_requested, + .notifications = chainman_opts.notifications, }; // For robustness, ensure the old manager is destroyed before creating a // new one. @@ -566,7 +567,7 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_snapshot_completion, SnapshotTestSetup auto db_cache_before_complete = active_cs.m_coinsdb_cache_size_bytes; SnapshotCompletionResult res; - auto mock_shutdown = [](bilingual_str msg) {}; + m_node.notifications->m_shutdown_on_fatal_error = false; fs::path snapshot_chainstate_dir = *node::FindSnapshotChainstateDir(); BOOST_CHECK(fs::exists(snapshot_chainstate_dir)); @@ -576,8 +577,7 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_snapshot_completion, SnapshotTestSetup const uint256 snapshot_tip_hash = WITH_LOCK(chainman.GetMutex(), return chainman.ActiveTip()->GetBlockHash()); - res = WITH_LOCK(::cs_main, - return chainman.MaybeCompleteSnapshotValidation(mock_shutdown)); + res = WITH_LOCK(::cs_main, return chainman.MaybeCompleteSnapshotValidation()); BOOST_CHECK_EQUAL(res, SnapshotCompletionResult::SUCCESS); WITH_LOCK(::cs_main, BOOST_CHECK(chainman.IsSnapshotValidated())); @@ -593,8 +593,7 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_snapshot_completion, SnapshotTestSetup BOOST_CHECK_EQUAL(all_chainstates[0], &active_cs); // Trying completion again should return false. - res = WITH_LOCK(::cs_main, - return chainman.MaybeCompleteSnapshotValidation(mock_shutdown)); + res = WITH_LOCK(::cs_main, return chainman.MaybeCompleteSnapshotValidation()); BOOST_CHECK_EQUAL(res, SnapshotCompletionResult::SKIPPED); // The invalid snapshot path should not have been used. @@ -647,7 +646,7 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_snapshot_completion_hash_mismatch, Sna Chainstate& validation_chainstate = *std::get<0>(chainstates); ChainstateManager& chainman = *Assert(m_node.chainman); SnapshotCompletionResult res; - auto mock_shutdown = [](bilingual_str msg) {}; + m_node.notifications->m_shutdown_on_fatal_error = false; // Test tampering with the IBD UTXO set with an extra coin to ensure it causes // snapshot completion to fail. @@ -663,8 +662,7 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_snapshot_completion_hash_mismatch, Sna fs::path snapshot_chainstate_dir = gArgs.GetDataDirNet() / "chainstate_snapshot"; BOOST_CHECK(fs::exists(snapshot_chainstate_dir)); - res = WITH_LOCK(::cs_main, - return chainman.MaybeCompleteSnapshotValidation(mock_shutdown)); + res = WITH_LOCK(::cs_main, return chainman.MaybeCompleteSnapshotValidation()); BOOST_CHECK_EQUAL(res, SnapshotCompletionResult::HASH_MISMATCH); auto all_chainstates = chainman.GetAll(); diff --git a/src/validation.cpp b/src/validation.cpp index 4f041e7b8fdea..76ac4815c374b 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -1842,9 +1842,9 @@ bool CheckInputScripts(const CTransaction& tx, TxValidationState& state, return true; } -bool AbortNode(BlockValidationState& state, const std::string& strMessage, const bilingual_str& userMessage) +bool FatalError(Notifications& notifications, BlockValidationState& state, const std::string& strMessage, const bilingual_str& userMessage) { - AbortNode(strMessage, userMessage); + notifications.fatalError(strMessage, userMessage); return state.Error(strMessage); } @@ -2081,7 +2081,7 @@ bool Chainstate::ConnectBlock(const CBlock& block, BlockValidationState& state, // We don't write down blocks to disk if they may have been // corrupted, so this should be impossible unless we're having hardware // problems. - return AbortNode(state, "Corrupt block found indicating potential hardware failure; shutting down"); + return FatalError(m_chainman.GetNotifications(), state, "Corrupt block found indicating potential hardware failure; shutting down"); } return error("%s: Consensus::CheckBlock: %s", __func__, state.ToString()); } @@ -2501,7 +2501,7 @@ bool Chainstate::FlushStateToDisk( if (fDoFullFlush || fPeriodicWrite) { // Ensure we can write block index if (!CheckDiskSpace(gArgs.GetBlocksDirPath())) { - return AbortNode(state, "Disk space is too low!", _("Disk space is too low!")); + return FatalError(m_chainman.GetNotifications(), state, "Disk space is too low!", _("Disk space is too low!")); } { LOG_TIME_MILLIS_WITH_CATEGORY("write block and undo data to disk", BCLog::BENCH); @@ -2515,7 +2515,7 @@ bool Chainstate::FlushStateToDisk( LOG_TIME_MILLIS_WITH_CATEGORY("write block index to disk", BCLog::BENCH); if (!m_blockman.WriteBlockIndexDB()) { - return AbortNode(state, "Failed to write to block index database"); + return FatalError(m_chainman.GetNotifications(), state, "Failed to write to block index database"); } } // Finally remove any pruned files @@ -2537,11 +2537,11 @@ bool Chainstate::FlushStateToDisk( // an overestimation, as most will delete an existing entry or // overwrite one. Still, use a conservative safety factor of 2. if (!CheckDiskSpace(gArgs.GetDataDirNet(), 48 * 2 * 2 * CoinsTip().GetCacheSize())) { - return AbortNode(state, "Disk space is too low!", _("Disk space is too low!")); + return FatalError(m_chainman.GetNotifications(), state, "Disk space is too low!", _("Disk space is too low!")); } // Flush the chainstate (which may refer to block index entries). if (!CoinsTip().Flush()) - return AbortNode(state, "Failed to write to coin database"); + return FatalError(m_chainman.GetNotifications(), state, "Failed to write to coin database"); m_last_flush = nNow; full_flush_completed = true; TRACE5(utxocache, flush, @@ -2557,7 +2557,7 @@ bool Chainstate::FlushStateToDisk( GetMainSignals().ChainStateFlushed(m_chain.GetLocator()); } } catch (const std::runtime_error& e) { - return AbortNode(state, std::string("System error while flushing: ") + e.what()); + return FatalError(m_chainman.GetNotifications(), state, std::string("System error while flushing: ") + e.what()); } return true; } @@ -2793,7 +2793,7 @@ bool Chainstate::ConnectTip(BlockValidationState& state, CBlockIndex* pindexNew, if (!pblock) { std::shared_ptr pblockNew = std::make_shared(); if (!m_blockman.ReadBlockFromDisk(*pblockNew, *pindexNew)) { - return AbortNode(state, "Failed to read block"); + return FatalError(m_chainman.GetNotifications(), state, "Failed to read block"); } pthisBlock = pblockNew; } else { @@ -2976,7 +2976,7 @@ bool Chainstate::ActivateBestChainStep(BlockValidationState& state, CBlockIndex* // If we're unable to disconnect a block during normal operation, // then that is a failure of our local system -- we should abort // rather than stay on a less work chain. - AbortNode(state, "Failed to disconnect block; see debug.log for details"); + FatalError(m_chainman.GetNotifications(), state, "Failed to disconnect block; see debug.log for details"); return false; } fBlocksDisconnected = true; @@ -3972,7 +3972,7 @@ bool Chainstate::AcceptBlock(const std::shared_ptr& pblock, BlockV } ReceivedBlockTransactions(block, pindex, blockPos); } catch (const std::runtime_error& e) { - return AbortNode(state, std::string("System error: ") + e.what()); + return FatalError(m_chainman.GetNotifications(), state, std::string("System error: ") + e.what()); } FlushStateToDisk(state, FlushStateMode::NONE); @@ -4663,7 +4663,7 @@ void Chainstate::LoadExternalBlockFile( } } } catch (const std::runtime_error& e) { - AbortNode(std::string("System error: ") + e.what()); + m_chainman.GetNotifications().fatalError(std::string("System error: ") + e.what()); } LogPrintf("Loaded %i blocks from external file in %dms\n", nLoaded, Ticks(SteadyClock::now() - start)); } @@ -5115,7 +5115,7 @@ bool ChainstateManager::ActivateSnapshot( snapshot_chainstate.reset(); bool removed = DeleteCoinsDBFromDisk(*snapshot_datadir, /*is_snapshot=*/true); if (!removed) { - AbortNode(strprintf("Failed to remove snapshot chainstate dir (%s). " + GetNotifications().fatalError(strprintf("Failed to remove snapshot chainstate dir (%s). " "Manually remove it before restarting.\n", fs::PathToString(*snapshot_datadir))); } } @@ -5380,8 +5380,7 @@ bool ChainstateManager::PopulateAndValidateSnapshot( // through IsUsable() checks, or // // (ii) giving each chainstate its own lock instead of using cs_main for everything. -SnapshotCompletionResult ChainstateManager::MaybeCompleteSnapshotValidation( - std::function shutdown_fnc) +SnapshotCompletionResult ChainstateManager::MaybeCompleteSnapshotValidation() { AssertLockHeld(cs_main); if (m_ibd_chainstate.get() == &this->ActiveChainstate() || @@ -5430,7 +5429,7 @@ SnapshotCompletionResult ChainstateManager::MaybeCompleteSnapshotValidation( m_snapshot_chainstate->InvalidateCoinsDBOnDisk(); - shutdown_fnc(user_error); + GetNotifications().fatalError(user_error.original, user_error); }; if (index_new.GetBlockHash() != snapshot_blockhash) { @@ -5664,7 +5663,7 @@ void Chainstate::InvalidateCoinsDBOnDisk() LogPrintf("%s: error renaming file '%s' -> '%s': %s\n", __func__, src_str, dest_str, e.what()); - AbortNode(strprintf( + m_chainman.GetNotifications().fatalError(strprintf( "Rename of '%s' -> '%s' failed. " "You should resolve this by manually moving or deleting the invalid " "snapshot directory %s, otherwise you will encounter the same error again " @@ -5730,13 +5729,13 @@ bool ChainstateManager::ValidatedSnapshotCleanup() fs::path tmp_old{ibd_chainstate_path + "_todelete"}; - auto rename_failed_abort = []( + auto rename_failed_abort = [this]( fs::path p_old, fs::path p_new, const fs::filesystem_error& err) { LogPrintf("%s: error renaming file (%s): %s\n", __func__, fs::PathToString(p_old), err.what()); - AbortNode(strprintf( + GetNotifications().fatalError(strprintf( "Rename of '%s' -> '%s' failed. " "Cannot clean up the background chainstate leveldb directory.", fs::PathToString(p_old), fs::PathToString(p_new))); diff --git a/src/validation.h b/src/validation.h index 652415633292b..1a8759787bd6c 100644 --- a/src/validation.h +++ b/src/validation.h @@ -103,7 +103,7 @@ void StopScriptCheckWorkerThreads(); CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams); -bool AbortNode(BlockValidationState& state, const std::string& strMessage, const bilingual_str& userMessage = bilingual_str{}); +bool FatalError(kernel::Notifications& notifications, BlockValidationState& state, const std::string& strMessage, const bilingual_str& userMessage = {}); /** Guess verification progress (as a fraction between 0.0=genesis and 1.0=current tip). */ double GuessVerificationProgress(const ChainTxData& data, const CBlockIndex* pindex); @@ -1045,10 +1045,7 @@ class ChainstateManager //! If the coins match (expected), then mark the validation chainstate for //! deletion and continue using the snapshot chainstate as active. //! Otherwise, revert to using the ibd chainstate and shutdown. - SnapshotCompletionResult MaybeCompleteSnapshotValidation( - std::function shutdown_fnc = - [](bilingual_str msg) { AbortNode(msg.original, msg); }) - EXCLUSIVE_LOCKS_REQUIRED(::cs_main); + SnapshotCompletionResult MaybeCompleteSnapshotValidation() EXCLUSIVE_LOCKS_REQUIRED(::cs_main); //! The most-work chain. Chainstate& ActiveChainstate() const;