From 9f3551d0439e45a538c46ed97a9dc5ec7dfe8ebe Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Sun, 21 Jun 2026 03:15:37 +0700 Subject: [PATCH 1/4] refactor: move const DEFAULT_MAX_RECOVERED_SIGS_AGE to llmq/options --- src/llmq/options.h | 3 +++ src/llmq/signing.h | 3 --- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/llmq/options.h b/src/llmq/options.h index 9973472d0e3b..8bb0b22b1f06 100644 --- a/src/llmq/options.h +++ b/src/llmq/options.h @@ -27,6 +27,9 @@ enum class QvvecSyncMode : int8_t { OnlyIfTypeMember = 1, }; +// Keep recovered signatures for a week. This is a "-maxrecsigsage" option default. +static constexpr int64_t DEFAULT_MAX_RECOVERED_SIGS_AGE{60 * 60 * 24 * 7}; + /** -llmq-data-recovery default */ static constexpr bool DEFAULT_ENABLE_QUORUM_DATA_RECOVERY{true}; /** -watchquorums default, if true, we will connect to all new quorums and watch their communication */ diff --git a/src/llmq/signing.h b/src/llmq/signing.h index a8175ef4d2e1..76b6c4326cb3 100644 --- a/src/llmq/signing.h +++ b/src/llmq/signing.h @@ -42,9 +42,6 @@ class CQuorumManager; class CSigSharesManager; class SignHash; -// Keep recovered signatures for a week. This is a "-maxrecsigsage" option default. -static constexpr int64_t DEFAULT_MAX_RECOVERED_SIGS_AGE{60 * 60 * 24 * 7}; - class CSigBase { protected: From 171e0361b95bec14ea4b995eb9fd3e130d362905 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Thu, 18 Jun 2026 22:08:53 +0700 Subject: [PATCH 2/4] refactor: inline methods DashChainstateSetupClose and DashChainstateSetup They are pretty trivial and simple but they are obstacle to implement bitcoin/bitcoin#25308 due to un-needed complexity --- src/bitcoin-chainstate.cpp | 6 +- src/init.cpp | 9 ++- src/node/chainstate.cpp | 69 +++++-------------- src/node/chainstate.h | 22 ------ src/test/util/setup_common.cpp | 28 ++------ src/test/util/setup_common.h | 5 -- .../validation_chainstatemanager_tests.cpp | 43 ++++++++++-- 7 files changed, 70 insertions(+), 112 deletions(-) diff --git a/src/bitcoin-chainstate.cpp b/src/bitcoin-chainstate.cpp index 07373cc00cbd..3ee059cbb670 100644 --- a/src/bitcoin-chainstate.cpp +++ b/src/bitcoin-chainstate.cpp @@ -283,7 +283,9 @@ int main(int argc, char* argv[]) } } GetMainSignals().UnregisterBackgroundSignalScheduler(); - // Tear down Dash kernel objects before kernel::~Context(). - node::DashChainstateSetupClose(chain_helper, dmnman, llmq_ctx, /*mempool=*/nullptr); + // Dash kernel objects before kernel::~Context(). + chain_helper.reset(); + llmq_ctx.reset(); + dmnman.reset(); evodb.reset(); } diff --git a/src/init.cpp b/src/init.cpp index 372347d54d2f..1a3156e06a89 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -157,7 +157,6 @@ using kernel::DumpMempool; using node::CacheSizes; using node::CalculateCacheSizes; -using node::DashChainstateSetupClose; using node::DEFAULT_PERSIST_MEMPOOL; using node::DEFAULT_PRINTPRIORITY; using node::DEFAULT_STOPAFTERBLOCKIMPORT; @@ -429,8 +428,12 @@ void PrepareShutdown(NodeContext& node) chainstate->ResetCoinsViews(); } } - DashChainstateSetupClose(node.chain_helper, node.dmnman, node.llmq_ctx, - Assert(node.mempool.get())); + node.chain_helper.reset(); + node.llmq_ctx.reset(); + if (node.mempool) { + node.mempool->DisconnectManagers(); + } + node.dmnman.reset(); node.evodb.reset(); } for (const auto& client : node.chain_clients) { diff --git a/src/node/chainstate.cpp b/src/node/chainstate.cpp index 79b7e353367d..fd47dfa91d36 100644 --- a/src/node/chainstate.cpp +++ b/src/node/chainstate.cpp @@ -57,6 +57,9 @@ ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, chainman.m_total_coinstip_cache = cache_sizes.coins; chainman.m_total_coinsdb_cache = cache_sizes.coins_db; + dmnman.reset(); + dmnman = std::make_unique(*evodb, mn_metaman); + // Load the fully validated chainstate. chainman.InitializeChainstate(options.mempool, *evodb, chain_helper); @@ -69,10 +72,20 @@ ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, pblocktree.reset(); pblocktree.reset(new CBlockTreeDB(cache_sizes.block_tree_db, options.block_tree_db_in_memory, options.reindex)); - DashChainstateSetup(chainman, mn_metaman, sporkman, chainlocks, mn_sync, chain_helper, - dmnman, *evodb, llmq_ctx, options.mempool, data_dir, options.dash_dbs_in_memory, - /*llmq_dbs_wipe=*/options.reindex || options.reindex_chainstate, options.bls_threads, options.worker_count, - options.max_recsigs_age); + // Initialize llmq_ctx and connection to mempool + llmq_ctx.reset(); + llmq_ctx = std::make_unique(*dmnman, *evodb, sporkman, chainman, + util::DbWrapperParams{.path = data_dir, .memory = dash_dbs_in_memory, .wipe = fReset || fReindexChainState}, + bls_threads, worker_count, max_recsigs_age); + if (mempool) { + mempool->ConnectManagers(dmnman.get(), llmq_ctx->isman.get()); + } + + // Initialize chain_helpper + chain_helper.reset(); + chain_helper = std::make_unique(*evodb, *dmnman, mn_sync, *(llmq_ctx->isman), *(llmq_ctx->quorum_block_processor), + *(llmq_ctx->qsnapman), chainman, chainman.GetConsensus(), chainlocks, + *(llmq_ctx->qman)); if (options.reindex) { pblocktree->WriteReindexing(true); @@ -189,54 +202,6 @@ ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, return {ChainstateLoadStatus::SUCCESS, {}}; } -void DashChainstateSetup(ChainstateManager& chainman, - CMasternodeMetaMan& mn_metaman, - CSporkManager& sporkman, - chainlock::Chainlocks& chainlocks, - const CMasternodeSync& mn_sync, - std::unique_ptr& chain_helper, - std::unique_ptr& dmnman, - CEvoDB& evodb, - std::unique_ptr& llmq_ctx, - CTxMemPool* mempool, - const fs::path& data_dir, - bool llmq_dbs_in_memory, - bool llmq_dbs_wipe, - int8_t bls_threads, - int16_t worker_count, - int64_t max_recsigs_age) -{ - // Same logic as pblocktree - dmnman.reset(); - dmnman = std::make_unique(evodb, mn_metaman); - - llmq_ctx.reset(); - llmq_ctx = std::make_unique(*dmnman, evodb, sporkman, chainman, - util::DbWrapperParams{.path = data_dir, .memory = llmq_dbs_in_memory, .wipe = llmq_dbs_wipe}, - bls_threads, worker_count, max_recsigs_age); - if (mempool) { - mempool->ConnectManagers(dmnman.get(), llmq_ctx->isman.get()); - } - chain_helper.reset(); - chain_helper = std::make_unique(evodb, *dmnman, mn_sync, *(llmq_ctx->isman), *(llmq_ctx->quorum_block_processor), - *(llmq_ctx->qsnapman), chainman, chainman.GetConsensus(), chainlocks, - *(llmq_ctx->qman)); -} - -void DashChainstateSetupClose(std::unique_ptr& chain_helper, - std::unique_ptr& dmnman, - std::unique_ptr& llmq_ctx, - CTxMemPool* mempool) - -{ - chain_helper.reset(); - llmq_ctx.reset(); - if (mempool) { - mempool->DisconnectManagers(); - } - dmnman.reset(); -} - ChainstateLoadResult VerifyLoadedChainstate(ChainstateManager& chainman, CEvoDB& evodb, const ChainstateLoadOptions& options) { diff --git a/src/node/chainstate.h b/src/node/chainstate.h index 36233b4e1aba..f6c88b72405a 100644 --- a/src/node/chainstate.h +++ b/src/node/chainstate.h @@ -84,28 +84,6 @@ ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSizes& cache_sizes, const ChainstateLoadOptions& options); -/** Initialize Dash-specific components during chainstate initialization */ -void DashChainstateSetup(ChainstateManager& chainman, - CMasternodeMetaMan& mn_metaman, - CSporkManager& sporkman, - chainlock::Chainlocks& chainlocks, - const CMasternodeSync& mn_sync, - std::unique_ptr& chain_helper, - std::unique_ptr& dmnman, - CEvoDB& evodb, - std::unique_ptr& llmq_ctx, - CTxMemPool* mempool, - const fs::path& data_dir, - bool llmq_dbs_in_memory, - bool llmq_dbs_wipe, - int8_t bls_threads, - int16_t worker_count, - int64_t max_recsigs_age); - -void DashChainstateSetupClose(std::unique_ptr& chain_helper, - std::unique_ptr& dmnman, - std::unique_ptr& llmq_ctx, - CTxMemPool* mempool); ChainstateLoadResult VerifyLoadedChainstate(ChainstateManager& chainman, CEvoDB& evodb, const ChainstateLoadOptions& options); diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index 5e20820fddd7..82c663f5a937 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -88,8 +88,6 @@ using node::BlockAssembler; using node::CalculateCacheSizes; -using node::DashChainstateSetup; -using node::DashChainstateSetupClose; using node::LoadChainstate; using node::NodeContext; using node::VerifyLoadedChainstate; @@ -141,23 +139,6 @@ std::unique_ptr MakePeerManager(CConnman& connman, node.llmq_ctx, ignore_incoming_txs); } -void DashChainstateSetup(ChainstateManager& chainman, - NodeContext& node, - bool llmq_dbs_in_memory, - bool llmq_dbs_wipe) -{ - DashChainstateSetup(chainman, *Assert(node.mn_metaman.get()), - *Assert(node.sporkman.get()), *Assert(node.chainlocks), *Assert(node.mn_sync), node.chain_helper, node.dmnman, *node.evodb, - node.llmq_ctx, Assert(node.mempool.get()), node.args->GetDataDirNet(), llmq_dbs_in_memory, llmq_dbs_wipe, - llmq::DEFAULT_BLSCHECK_THREADS, llmq::DEFAULT_WORKER_COUNT, llmq::DEFAULT_MAX_RECOVERED_SIGS_AGE); -} - -void DashChainstateSetupClose(NodeContext& node) -{ - DashChainstateSetupClose(node.chain_helper, node.dmnman, node.llmq_ctx, - Assert(node.mempool.get())); -} - struct NetworkSetup { NetworkSetup() @@ -428,9 +409,12 @@ TestingSetup::~TestingSetup() m_node.connman->Stop(); } - // DashChainstateSetup() is called by LoadChainstate() internally but - // winding them down is our responsibility - DashChainstateSetupClose(m_node); + m_node.chain_helper.reset(); + m_node.llmq_ctx.reset(); + if (m_node.mempool) { + m_node.mempool->DisconnectManagers(); + } + m_node.dmnman.reset(); } TestChain100Setup::TestChain100Setup( diff --git a/src/test/util/setup_common.h b/src/test/util/setup_common.h index 63f2a9d52a16..701f9569a6bf 100644 --- a/src/test/util/setup_common.h +++ b/src/test/util/setup_common.h @@ -84,11 +84,6 @@ std::unique_ptr MakePeerManager(CConnman& connman, node::NodeContext& node, BanMan* banman, bool ignore_incoming_txs); -void DashChainstateSetup(ChainstateManager& chainman, - node::NodeContext& node, - bool llmq_dbs_in_memory, - bool llmq_dbs_wipe); -void DashChainstateSetupClose(node::NodeContext& node); /** Basic testing setup. * This just configures logging, data dir and chain parameters. diff --git a/src/test/validation_chainstatemanager_tests.cpp b/src/test/validation_chainstatemanager_tests.cpp index 20450b60836a..35921ef10e01 100644 --- a/src/test/validation_chainstatemanager_tests.cpp +++ b/src/test/validation_chainstatemanager_tests.cpp @@ -4,6 +4,10 @@ // #include #include +#include +#include +#include +#include #include #include #include @@ -33,6 +37,38 @@ using node::SnapshotMetadata; BOOST_FIXTURE_TEST_SUITE(validation_chainstatemanager_tests, ChainTestingSetup) +static void DashChainstateSetup(ChainstateManager& chainman, + node::NodeContext& node, + bool llmq_dbs_in_memory, + bool llmq_dbs_wipe) +{ + node.llmq_ctx.reset(); + node.llmq_ctx = std::make_unique(*node.dmnman, *node.evodb, *Assert(node.sporkman.get()), chainman, + util::DbWrapperParams{.path = node.args->GetDataDirNet(), .memory = llmq_dbs_in_memory, .wipe = llmq_dbs_wipe}, + llmq::DEFAULT_BLSCHECK_THREADS, llmq::DEFAULT_WORKER_COUNT, llmq::DEFAULT_MAX_RECOVERED_SIGS_AGE); + if (node.mempool) { + node.mempool->ConnectManagers(node.dmnman.get(), node.llmq_ctx->isman.get()); + } + + // Initialize chain_helpper + node.chain_helper.reset(); + node.chain_helper = std::make_unique(*node.evodb, *node.dmnman, *Assert(node.mn_sync), *(node.llmq_ctx->isman), *(node.llmq_ctx->quorum_block_processor), + *(node.llmq_ctx->qsnapman), chainman, chainman.GetConsensus(), *Assert(node.chainlocks), + *(node.llmq_ctx->qman)); +} + +static void DashChainstateSetupClose(node::NodeContext& node) +{ + if (node.clhandler) { + node.clhandler->Stop(); + } + node.chain_helper.reset(); + if (node.mempool) { + node.mempool->DisconnectManagers(); + } + node.llmq_ctx.reset(); +} + //! Basic tests for ChainstateManager. //! //! First create a legacy (IBD) chainstate, then create a snapshot chainstate. @@ -41,6 +77,7 @@ BOOST_AUTO_TEST_CASE(chainstatemanager) ChainstateManager& manager = *m_node.chainman; CTxMemPool& mempool = *m_node.mempool; CEvoDB& evodb = *m_node.evodb; + m_node.dmnman = std::make_unique(evodb, *Assert(m_node.mn_metaman.get())); std::vector chainstates; BOOST_CHECK(!manager.SnapshotBlockhash().has_value()); @@ -71,9 +108,6 @@ BOOST_AUTO_TEST_CASE(chainstatemanager) BOOST_CHECK(!manager.SnapshotBlockhash().has_value()); - if (m_node.clhandler) { - m_node.clhandler->Stop(); - } DashChainstateSetupClose(m_node); // Create a snapshot-based chainstate. @@ -118,9 +152,6 @@ BOOST_AUTO_TEST_CASE(chainstatemanager) // Let scheduler events finish running to avoid accessing memory that is going to be unloaded SyncWithValidationInterfaceQueue(); - if (m_node.clhandler) { - m_node.clhandler->Stop(); - } DashChainstateSetupClose(m_node); } From 4a49ad2072560176511287c84e51003a057c803a Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Tue, 14 Jul 2026 18:05:22 +0700 Subject: [PATCH 3/4] fix: follow-up changes for #7451 [bitcoin#25308 and related] Revert changes that introduced by non-backported yet bitcoin/bitcoin#31061 Set default value for worker_count Move chainhelper, evodb, dmnman, llmq_ctx, data_dir, mn_metaman, sporkman, chainlocks, mn_sync to node::ChainstateLoadOptions same as mempool is done Pass notify_bls_state to VerifyLoadedChainstate only Added missing changes for strLoadError from bitcoin#25308 Added missing changes for node::fPruneMode node::fReindex from bitcoin#25308 Fixed duplicated entities in src/test/util/setup_common.cpp as follow-up for bitcoin/bitcoin#25290 --- src/bitcoin-chainstate.cpp | 36 ++++++++-------- src/init.cpp | 77 ++++++++++++++++------------------ src/node/chainstate.cpp | 74 ++++++++++++++++---------------- src/node/chainstate.h | 43 ++++++++++--------- src/test/util/setup_common.cpp | 48 +++++++++------------ 5 files changed, 128 insertions(+), 150 deletions(-) diff --git a/src/bitcoin-chainstate.cpp b/src/bitcoin-chainstate.cpp index 3ee059cbb670..7f14f4f8b687 100644 --- a/src/bitcoin-chainstate.cpp +++ b/src/bitcoin-chainstate.cpp @@ -102,32 +102,30 @@ int main(int argc, char* argv[]) std::unique_ptr llmq_ctx; std::unique_ptr chain_helper; + node::CacheSizes cache_sizes; cache_sizes.block_tree_db = 2 << 20; cache_sizes.coins_db = 2 << 22; cache_sizes.coins = (450 << 20) - (2 << 20) - (2 << 22); - node::ChainstateLoadOptions options; - options.bls_threads = 1; - options.worker_count = 1; - options.max_recsigs_age = 1; - options.check_interrupt = [] { return false; }; - auto [status, error] = node::LoadChainstate(chainman, - metaman, - sporkman, - chainlocks, - mn_sync, - chain_helper, - dmnman, - evodb, - llmq_ctx, - gArgs.GetDataDirNet(), - cache_sizes, - options); + node::ChainstateLoadOptions options{ + .chain_helper = chain_helper, + .evodb = evodb, + .dmnman = dmnman, + .llmq_ctx = llmq_ctx, + .data_dir = gArgs.GetDataDirNet(), + .check_interrupt = [] { return false; }, + .coins_error_cb =[] { return false; }, + }; + options.mn_metaman = &metaman; + options.sporkman = &sporkman; + options.chainlocks = &chainlocks; + options.mn_sync = &mn_sync; + auto [status, error] = node::LoadChainstate(chainman, cache_sizes, options); if (status != node::ChainstateLoadStatus::SUCCESS) { std::cerr << "Failed to load Chain state from your datadir." << std::endl; goto epilogue; } else { - std::tie(status, error) = node::VerifyLoadedChainstate(std::ref(chainman), *evodb, options); + std::tie(status, error) = node::VerifyLoadedChainstate(chainman, options); if (status != node::ChainstateLoadStatus::SUCCESS) { std::cerr << "Failed to verify loaded Chain state from your datadir." << std::endl; goto epilogue; @@ -283,7 +281,7 @@ int main(int argc, char* argv[]) } } GetMainSignals().UnregisterBackgroundSignalScheduler(); - // Dash kernel objects before kernel::~Context(). + // Tear down Dash kernel objects before kernel::~Context(). chain_helper.reset(); llmq_ctx.reset(); dmnman.reset(); diff --git a/src/init.cpp b/src/init.cpp index 1a3156e06a89..fe1ef3add661 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -2024,10 +2024,25 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) */ node.mn_sync = std::make_unique(std::make_unique(*node.connman, *node.netfulfilledman)); - bilingual_str strLoadError; - - node::ChainstateLoadOptions options; + node::ChainstateLoadOptions options{ + .chain_helper = node.chain_helper, + .evodb = node.evodb, + .dmnman = node.dmnman, + .llmq_ctx = node.llmq_ctx, + .data_dir = args.GetDataDirNet(), + .check_interrupt = ShutdownRequested, + .coins_error_cb = [] { + uiInterface.ThreadSafeMessageBox( + _("Error reading from database, shutting down."), + "", CClientUIInterface::MSG_ERROR); + } + }; options.mempool = Assert(node.mempool.get()); + options.mn_metaman = Assert(node.mn_metaman.get()); + options.sporkman = Assert(node.sporkman.get()); + options.chainlocks = Assert(node.chainlocks.get()); + options.mn_sync = Assert(node.mn_sync.get()); + options.reindex = node::fReindex; options.reindex_chainstate = fReindexChainState; options.prune = node::fPruneMode; @@ -2042,69 +2057,47 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) const int64_t adjusted_threads = std::clamp(threads, 1, int64_t{llmq::MAX_BLSCHECK_THREADS} + 1) - 1; return static_cast(adjusted_threads); }(); - options.worker_count = llmq::DEFAULT_WORKER_COUNT; options.max_recsigs_age = args.GetIntArg("-maxrecsigsage", llmq::DEFAULT_MAX_RECOVERED_SIGS_AGE); options.check_blocks = args.GetIntArg("-checkblocks", DEFAULT_CHECKBLOCKS); options.check_level = args.GetIntArg("-checklevel", DEFAULT_CHECKLEVEL); - options.check_interrupt = ShutdownRequested; - options.coins_error_cb = [] { - uiInterface.ThreadSafeMessageBox( - _("Error reading from database, shutting down."), - "", CClientUIInterface::MSG_ERROR); - }; - options.notify_bls_state = [](bool bls_state) { - LogPrintf("%s: bls_legacy_scheme=%d\n", __func__, bls_state); - }; + uiInterface.InitMessage(_("Loading block index…").translated); const auto load_block_index_start_time{SteadyClock::now()}; - auto catch_exceptions = [](auto&& fn) -> node::ChainstateLoadResult { + auto catch_exceptions = [](auto&& f) { try { - return fn(); + return f(); } catch (const std::exception& e) { LogPrintf("%s\n", e.what()); - return {node::ChainstateLoadStatus::FAILURE, _("Error opening block database")}; + return std::make_tuple(node::ChainstateLoadStatus::FAILURE, _("Error opening block database")); } }; - auto [status, error] = catch_exceptions([&] { - return LoadChainstate(chainman, - *node.mn_metaman, - *node.sporkman, - *node.chainlocks, - *node.mn_sync, - node.chain_helper, - node.dmnman, - node.evodb, - node.llmq_ctx, - args.GetDataDirNet(), - cache_sizes, - options); - }); + auto [status, error] = catch_exceptions([&]{ return LoadChainstate(chainman, cache_sizes, options); }); if (status == node::ChainstateLoadStatus::SUCCESS) { uiInterface.InitMessage(_("Verifying blocks…").translated); if (chainman.m_blockman.m_have_pruned && options.check_blocks > MIN_BLOCKS_TO_KEEP) { LogWarning("pruned datadir may not have more than %d blocks; only checking available blocks\n", MIN_BLOCKS_TO_KEEP); } - std::tie(status, error) = catch_exceptions([&] { - return VerifyLoadedChainstate(chainman, *Assert(node.evodb), options); - }); + std::tie(status, error) = catch_exceptions([&]{ return VerifyLoadedChainstate(chainman, options, [](bool bls_state) { + LogPrintf("%s: bls_legacy_scheme=%d\n", __func__, bls_state); + });}); + if (status == node::ChainstateLoadStatus::SUCCESS) { + fLoaded = true; + LogPrintf(" block index %15dms\n", Ticks(SteadyClock::now() - load_block_index_start_time)); + } } - if (status == node::ChainstateLoadStatus::SUCCESS) { - fLoaded = true; - LogPrintf(" block index %15dms\n", Ticks(SteadyClock::now() - load_block_index_start_time)); - } else if (status == node::ChainstateLoadStatus::FAILURE_INCOMPATIBLE_DB) { + + if (status == node::ChainstateLoadStatus::FAILURE_INCOMPATIBLE_DB) { return InitError(error); - } else { - strLoadError = error; } if (!fLoaded && !ShutdownRequested()) { // first suggest a reindex if (!options.reindex) { bool fRet = uiInterface.ThreadSafeQuestion( - strLoadError + Untranslated(".\n\n") + _("Do you want to rebuild the block database now?"), - strLoadError.original + ".\nPlease restart with -reindex or -reindex-chainstate to recover.", + error + Untranslated(".\n\n") + _("Do you want to rebuild the block database now?"), + error.original + ".\nPlease restart with -reindex or -reindex-chainstate to recover.", "", CClientUIInterface::MSG_ERROR | CClientUIInterface::BTN_ABORT); if (fRet) { fReindex = true; @@ -2114,7 +2107,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) return false; } } else { - return InitError(strLoadError); + return InitError(error); } } } diff --git a/src/node/chainstate.cpp b/src/node/chainstate.cpp index fd47dfa91d36..ab687b304a83 100644 --- a/src/node/chainstate.cpp +++ b/src/node/chainstate.cpp @@ -34,34 +34,28 @@ #include namespace node { -ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, - CMasternodeMetaMan& mn_metaman, - CSporkManager& sporkman, - chainlock::Chainlocks& chainlocks, - const CMasternodeSync& mn_sync, - std::unique_ptr& chain_helper, - std::unique_ptr& dmnman, - std::unique_ptr& evodb, - std::unique_ptr& llmq_ctx, - const fs::path& data_dir, - const CacheSizes& cache_sizes, - const ChainstateLoadOptions& options) +ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSizes& cache_sizes, + const ChainstateLoadOptions& options) { + const bool to_wipe_data = options.reindex || options.reindex_chainstate; auto is_coinsview_empty = [&](Chainstate* chainstate) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { - return options.reindex || options.reindex_chainstate || chainstate->CoinsTip().GetBestBlock().IsNull(); + return to_wipe_data || chainstate->CoinsTip().GetBestBlock().IsNull(); }; LOCK(cs_main); - evodb.reset(); - evodb = std::make_unique(util::DbWrapperParams{.path = data_dir, .memory = options.dash_dbs_in_memory, .wipe = options.reindex || options.reindex_chainstate}); + + options.evodb.reset(); + // TODO: pass DbWrapperParams as options instead multiple params + options.evodb = std::make_unique(util::DbWrapperParams{.path = options.data_dir, .memory = options.dash_dbs_in_memory, .wipe = to_wipe_data}); + + options.dmnman.reset(); + options.dmnman = std::make_unique(*options.evodb, *options.mn_metaman); + chainman.m_total_coinstip_cache = cache_sizes.coins; chainman.m_total_coinsdb_cache = cache_sizes.coins_db; - dmnman.reset(); - dmnman = std::make_unique(*evodb, mn_metaman); - // Load the fully validated chainstate. - chainman.InitializeChainstate(options.mempool, *evodb, chain_helper); + chainman.InitializeChainstate(options.mempool, *options.evodb, options.chain_helper); // Load a chain created from a UTXO snapshot, if any exist. chainman.DetectSnapshotChainstate(options.mempool); @@ -73,19 +67,19 @@ ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, pblocktree.reset(new CBlockTreeDB(cache_sizes.block_tree_db, options.block_tree_db_in_memory, options.reindex)); // Initialize llmq_ctx and connection to mempool - llmq_ctx.reset(); - llmq_ctx = std::make_unique(*dmnman, *evodb, sporkman, chainman, - util::DbWrapperParams{.path = data_dir, .memory = dash_dbs_in_memory, .wipe = fReset || fReindexChainState}, - bls_threads, worker_count, max_recsigs_age); - if (mempool) { - mempool->ConnectManagers(dmnman.get(), llmq_ctx->isman.get()); + options.llmq_ctx.reset(); + options.llmq_ctx = std::make_unique(*options.dmnman, *options.evodb, *options.sporkman, chainman, + util::DbWrapperParams{.path = options.data_dir, .memory = options.dash_dbs_in_memory, .wipe = to_wipe_data}, + options.bls_threads, options.worker_count, options.max_recsigs_age); + if (options.mempool) { + options.mempool->ConnectManagers(options.dmnman.get(), options.llmq_ctx->isman.get()); } - // Initialize chain_helpper - chain_helper.reset(); - chain_helper = std::make_unique(*evodb, *dmnman, mn_sync, *(llmq_ctx->isman), *(llmq_ctx->quorum_block_processor), - *(llmq_ctx->qsnapman), chainman, chainman.GetConsensus(), chainlocks, - *(llmq_ctx->qman)); + // Initialize chain_helper + options.chain_helper.reset(); + options.chain_helper = std::make_unique(*options.evodb, *options.dmnman, *options.mn_sync, *(options.llmq_ctx->isman), *(options.llmq_ctx->quorum_block_processor), + *(options.llmq_ctx->qsnapman), chainman, chainman.GetConsensus(), *options.chainlocks, + *(options.llmq_ctx->qman)); if (options.reindex) { pblocktree->WriteReindexing(true); @@ -108,6 +102,8 @@ ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, if (!chainman.BlockIndex().empty() && !chainman.m_blockman.LookupBlockIndex(chainman.GetConsensus().hashGenesisBlock)) { + // If the loaded chain has a wrong genesis, bail out immediately + // (we're likely using a testnet datadir, or the other way around). return {ChainstateLoadStatus::FAILURE_INCOMPATIBLE_DB, _("Incorrect or no genesis block found. Wrong datadir for network?")}; } @@ -172,7 +168,7 @@ ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, // TODO: CEvoDB instance should probably be a part of Chainstate // (for multiple chainstates to actually work in parallel) // and not a global - if (&chainman.ActiveChainstate() == chainstate && !evodb->CommitRootTransaction()) { + if (&chainman.ActiveChainstate() == chainstate && !options.evodb->CommitRootTransaction()) { return {ChainstateLoadStatus::FAILURE, _("Failed to commit Evo database")}; } @@ -185,12 +181,12 @@ ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, } } - if (!chain_helper->ehf_manager->ForceSignalDBUpdate()) { + if (!options.chain_helper->ehf_manager->ForceSignalDBUpdate()) { return {ChainstateLoadStatus::FAILURE, _("Error upgrading evo database for EHF")}; } // Check if nVersion-first migration is needed and perform it - if (dmnman->IsMigrationRequired() && !dmnman->MigrateLegacyDiffs(chainman.ActiveChainstate().m_chain.Tip())) { + if (options.dmnman->IsMigrationRequired() && !options.dmnman->MigrateLegacyDiffs(chainman.ActiveChainstate().m_chain.Tip())) { return {ChainstateLoadStatus::FAILURE, _("Failed to upgrade Evo database")}; } @@ -202,8 +198,7 @@ ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, return {ChainstateLoadStatus::SUCCESS, {}}; } -ChainstateLoadResult VerifyLoadedChainstate(ChainstateManager& chainman, CEvoDB& evodb, - const ChainstateLoadOptions& options) +ChainstateLoadResult VerifyLoadedChainstate(ChainstateManager& chainman, const ChainstateLoadOptions& options, std::function notify_bls_state) { auto is_coinsview_empty = [&](Chainstate* chainstate) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { return options.reindex || options.reindex_chainstate || chainstate->CoinsTip().GetBestBlock().IsNull(); @@ -222,12 +217,13 @@ ChainstateLoadResult VerifyLoadedChainstate(ChainstateManager& chainman, CEvoDB& const bool v19active{DeploymentActiveAfter(tip, chainman, Consensus::DEPLOYMENT_V19)}; if (v19active) { bls::bls_legacy_scheme.store(false); - if (options.notify_bls_state) options.notify_bls_state(bls::bls_legacy_scheme.load()); + // TODO: remove notify_bls_state, it's alien and irrelevant once v19 activated + if (notify_bls_state) notify_bls_state(bls::bls_legacy_scheme.load()); } if (!CVerifyDB().VerifyDB( *chainstate, chainman.GetConsensus(), chainstate->CoinsDB(), - evodb, + *options.evodb, options.check_level, options.check_blocks)) { return {ChainstateLoadStatus::FAILURE, _("Corrupted block database detected")}; @@ -237,7 +233,7 @@ ChainstateLoadResult VerifyLoadedChainstate(ChainstateManager& chainman, CEvoDB& // Make sure we use the right scheme. if (v19active && bls::bls_legacy_scheme.load()) { bls::bls_legacy_scheme.store(false); - if (options.notify_bls_state) options.notify_bls_state(bls::bls_legacy_scheme.load()); + if (notify_bls_state) notify_bls_state(bls::bls_legacy_scheme.load()); } if (options.check_level >= 3) { @@ -248,7 +244,7 @@ ChainstateLoadResult VerifyLoadedChainstate(ChainstateManager& chainman, CEvoDB& // TODO: CEvoDB instance should probably be a part of Chainstate // (for multiple chainstates to actually work in parallel) // and not a global - if (&chainman.ActiveChainstate() == chainstate && !evodb.IsEmpty()) { + if (&chainman.ActiveChainstate() == chainstate && !options.evodb->IsEmpty()) { // EvoDB processed some blocks earlier but we have no blocks anymore, something is wrong return {ChainstateLoadStatus::FAILURE, _("Error initializing block database")}; } diff --git a/src/node/chainstate.h b/src/node/chainstate.h index f6c88b72405a..c684de934fd6 100644 --- a/src/node/chainstate.h +++ b/src/node/chainstate.h @@ -5,13 +5,14 @@ #ifndef BITCOIN_NODE_CHAINSTATE_H #define BITCOIN_NODE_CHAINSTATE_H +#include +#include + #include #include #include #include #include -#include -#include class CChainstateHelper; class CDeterministicMNManager; @@ -29,24 +30,35 @@ class path; } // namespace fs namespace node { + struct CacheSizes; struct ChainstateLoadOptions { CTxMemPool* mempool{nullptr}; + std::unique_ptr& chain_helper; + std::unique_ptr& evodb; + std::unique_ptr& dmnman; + std::unique_ptr& llmq_ctx; + + CMasternodeMetaMan* mn_metaman{nullptr}; + CSporkManager* sporkman{nullptr}; + chainlock::Chainlocks* chainlocks{nullptr}; + const CMasternodeSync* mn_sync{nullptr}; + fs::path data_dir; + bool block_tree_db_in_memory{false}; bool coins_db_in_memory{false}; bool dash_dbs_in_memory{false}; bool reindex{false}; bool reindex_chainstate{false}; bool prune{false}; - int8_t bls_threads{0}; - int16_t worker_count{0}; - int64_t max_recsigs_age{0}; + int8_t bls_threads{llmq::DEFAULT_BLSCHECK_THREADS}; + int16_t worker_count{llmq::DEFAULT_WORKER_COUNT}; + int64_t max_recsigs_age{llmq::DEFAULT_MAX_RECOVERED_SIGS_AGE}; int64_t check_blocks{DEFAULT_CHECKBLOCKS}; int64_t check_level{DEFAULT_CHECKLEVEL}; std::function check_interrupt; std::function coins_error_cb; - std::function notify_bls_state; }; //! Chainstate load status. Simple applications can just check for the success @@ -71,22 +83,9 @@ using ChainstateLoadResult = std::tuple; * * LoadChainstate returns a (status code, error string) tuple. */ -ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, - CMasternodeMetaMan& mn_metaman, - CSporkManager& sporkman, - chainlock::Chainlocks& chainlocks, - const CMasternodeSync& mn_sync, - std::unique_ptr& chain_helper, - std::unique_ptr& dmnman, - std::unique_ptr& evodb, - std::unique_ptr& llmq_ctx, - const fs::path& data_dir, - const CacheSizes& cache_sizes, - const ChainstateLoadOptions& options); - - -ChainstateLoadResult VerifyLoadedChainstate(ChainstateManager& chainman, CEvoDB& evodb, - const ChainstateLoadOptions& options); +ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSizes& cache_sizes, + const ChainstateLoadOptions& options); +ChainstateLoadResult VerifyLoadedChainstate(ChainstateManager& chainman, const ChainstateLoadOptions& options, std::function notify_bls_state = nullptr); } // namespace node #endif // BITCOIN_NODE_CHAINSTATE_H diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index 82c663f5a937..9a22110d2828 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -91,11 +91,6 @@ using node::CalculateCacheSizes; using node::LoadChainstate; using node::NodeContext; using node::VerifyLoadedChainstate; -using node::fPruneMode; -using node::fReindex; -using node::LoadChainstate; -using node::NodeContext; -using node::VerifyLoadedChainstate; const std::function G_TRANSLATION_FUN = nullptr; UrlDecodeFn* const URL_DECODE = nullptr; @@ -302,40 +297,37 @@ ChainTestingSetup::~ChainTestingSetup() void ChainTestingSetup::LoadVerifyActivateChainstate() { auto& chainman{*Assert(m_node.chainman)}; - node::ChainstateLoadOptions options; + + node::ChainstateLoadOptions options{ + .chain_helper = m_node.chain_helper, + .evodb = m_node.evodb, + .dmnman = m_node.dmnman, + .llmq_ctx = m_node.llmq_ctx, + .data_dir = Assert(m_node.args)->GetDataDirNet(), + .check_interrupt = [] { return false; }, + .coins_error_cb =[] { return false; }, + }; + options.mempool = Assert(m_node.mempool.get()); + options.mn_metaman = Assert(m_node.mn_metaman.get()); + options.sporkman = Assert(m_node.sporkman.get()); + options.chainlocks = Assert(m_node.chainlocks.get()); + options.mn_sync = Assert(m_node.mn_sync.get()); + options.block_tree_db_in_memory = m_block_tree_db_in_memory; options.coins_db_in_memory = m_coins_db_in_memory; options.dash_dbs_in_memory = true; options.reindex = node::fReindex; options.reindex_chainstate = m_args.GetBoolArg("-reindex-chainstate", false); options.prune = node::fPruneMode; - options.bls_threads = llmq::DEFAULT_BLSCHECK_THREADS; - options.worker_count = llmq::DEFAULT_WORKER_COUNT; - options.max_recsigs_age = llmq::DEFAULT_MAX_RECOVERED_SIGS_AGE; options.check_blocks = m_args.GetIntArg("-checkblocks", DEFAULT_CHECKBLOCKS); options.check_level = m_args.GetIntArg("-checklevel", DEFAULT_CHECKLEVEL); - options.notify_bls_state = [](bool bls_state) { - LogPrintf("%s: bls_legacy_scheme=%d\n", __func__, bls_state); - }; - auto [status, error] = LoadChainstate(chainman, - *Assert(m_node.mn_metaman.get()), - *Assert(m_node.sporkman.get()), - *Assert(m_node.chainlocks.get()), - *Assert(m_node.mn_sync.get()), - m_node.chain_helper, - m_node.dmnman, - m_node.evodb, - m_node.llmq_ctx, - Assert(m_node.args)->GetDataDirNet(), - m_cache_sizes, - options); + auto [status, error] = LoadChainstate(*Assert(m_node.chainman), m_cache_sizes, options); assert(status == node::ChainstateLoadStatus::SUCCESS); - std::tie(status, error) = VerifyLoadedChainstate( - chainman, - *Assert(m_node.evodb.get()), - options); + std::tie(status, error) = VerifyLoadedChainstate(*Assert(m_node.chainman), options, [](bool bls_state) { + LogPrintf("%s: bls_legacy_scheme=%d\n", __func__, bls_state); + }); assert(status == node::ChainstateLoadStatus::SUCCESS); BlockValidationState state; From 4bde03ae2316f9989ec859f7dfa16f3b79437c6d Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Fri, 17 Jul 2026 14:50:27 +0700 Subject: [PATCH 4/4] fix: disconnect mempool from managers before dmnman and isman dtor --- src/init.cpp | 4 ++-- src/test/util/setup_common.cpp | 4 ++-- src/test/validation_chainstatemanager_tests.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index fe1ef3add661..92a8e840dc72 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -428,11 +428,11 @@ void PrepareShutdown(NodeContext& node) chainstate->ResetCoinsViews(); } } - node.chain_helper.reset(); - node.llmq_ctx.reset(); if (node.mempool) { node.mempool->DisconnectManagers(); } + node.chain_helper.reset(); + node.llmq_ctx.reset(); node.dmnman.reset(); node.evodb.reset(); } diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index 9a22110d2828..df5868ff5544 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -401,11 +401,11 @@ TestingSetup::~TestingSetup() m_node.connman->Stop(); } - m_node.chain_helper.reset(); - m_node.llmq_ctx.reset(); if (m_node.mempool) { m_node.mempool->DisconnectManagers(); } + m_node.chain_helper.reset(); + m_node.llmq_ctx.reset(); m_node.dmnman.reset(); } diff --git a/src/test/validation_chainstatemanager_tests.cpp b/src/test/validation_chainstatemanager_tests.cpp index 35921ef10e01..824857b680e4 100644 --- a/src/test/validation_chainstatemanager_tests.cpp +++ b/src/test/validation_chainstatemanager_tests.cpp @@ -62,10 +62,10 @@ static void DashChainstateSetupClose(node::NodeContext& node) if (node.clhandler) { node.clhandler->Stop(); } - node.chain_helper.reset(); if (node.mempool) { node.mempool->DisconnectManagers(); } + node.chain_helper.reset(); node.llmq_ctx.reset(); }