From 513261c43620baec89bf0dbeb7febf34219bf585 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Sun, 21 Jun 2026 03:15:37 +0700 Subject: [PATCH 1/5] 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 0774f56c81c81b25b16d17da4384e63cd728c366 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Thu, 18 Jun 2026 22:08:53 +0700 Subject: [PATCH 2/5] 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 | 45 ++++++++++-- 7 files changed, 72 insertions(+), 112 deletions(-) diff --git a/src/bitcoin-chainstate.cpp b/src/bitcoin-chainstate.cpp index 26a871bcda51..072afe7650f5 100644 --- a/src/bitcoin-chainstate.cpp +++ b/src/bitcoin-chainstate.cpp @@ -282,7 +282,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 34b43d4f8096..a5e8bb9432f5 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -158,7 +158,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; @@ -430,8 +429,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 f6c246cebc30..4f880a81d6fa 100644 --- a/src/node/chainstate.cpp +++ b/src/node/chainstate.cpp @@ -58,6 +58,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); @@ -70,10 +73,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); @@ -190,54 +203,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 de67c720d420..f85eb8b1265a 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() @@ -427,9 +408,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 b3ac553323a4..132a5a14b868 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 cd2f1d9cb5ce..58bef3a84185 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_helper + 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,10 +152,9 @@ 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); + // dmnman holds a reference to m_node.evodb, it mustn't outlive it + m_node.dmnman.reset(); } //! Test rebalancing the caches associated with each chainstate. From bf1608e414aba89111e2f84dbbbae3c350ceb851 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Tue, 14 Jul 2026 18:05:22 +0700 Subject: [PATCH 3/5] 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 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 | 27 ++++++--------- src/init.cpp | 56 ++++++++++++------------------ src/node/chainstate.cpp | 62 ++++++++++++++++++---------------- src/node/chainstate.h | 43 +++++++++++------------ src/test/util/setup_common.cpp | 41 ++++++++-------------- 5 files changed, 100 insertions(+), 129 deletions(-) diff --git a/src/bitcoin-chainstate.cpp b/src/bitcoin-chainstate.cpp index 072afe7650f5..24eb5965c26d 100644 --- a/src/bitcoin-chainstate.cpp +++ b/src/bitcoin-chainstate.cpp @@ -101,32 +101,25 @@ 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.mn_metaman = &metaman; + options.sporkman = &sporkman; + options.chainlocks = &chainlocks; + options.mn_sync = &mn_sync; + options.data_dir = gArgs.GetDataDirNet(); 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); + options.coins_error_cb = [] {}; + auto [status, error] = node::LoadChainstate(chainman, cache_sizes, options, evodb, dmnman, llmq_ctx, chain_helper); 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(chainman, *evodb, options); + std::tie(status, error) = node::VerifyLoadedChainstate(chainman, options, *evodb); if (status != node::ChainstateLoadStatus::SUCCESS) { std::cerr << "Failed to verify loaded Chain state from your datadir." << std::endl; goto epilogue; @@ -282,7 +275,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 a5e8bb9432f5..9f0dc7db27d5 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -2024,10 +2024,13 @@ 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; 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.data_dir = args.GetDataDirNet(); options.reindex = node::fReindex; options.reindex_chainstate = fReindexChainState; options.prune = node::fPruneMode; @@ -2042,7 +2045,6 @@ 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); @@ -2052,59 +2054,43 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) _("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, node.evodb, node.dmnman, node.llmq_ctx, node.chain_helper); }); 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, *Assert(node.evodb), [](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 +2100,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 4f880a81d6fa..c86f6173afad 100644 --- a/src/node/chainstate.cpp +++ b/src/node/chainstate.cpp @@ -35,31 +35,32 @@ #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, std::unique_ptr& evodb, + std::unique_ptr& dmnman, std::unique_ptr& llmq_ctx, + std::unique_ptr& chain_helper) { + assert(options.mn_metaman); + assert(options.sporkman); + assert(options.chainlocks); + assert(options.mn_sync); + + 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}); - chainman.m_total_coinstip_cache = cache_sizes.coins; - chainman.m_total_coinsdb_cache = cache_sizes.coins_db; + // TODO: pass DbWrapperParams as options instead multiple params + evodb = std::make_unique(util::DbWrapperParams{.path = options.data_dir, .memory = options.dash_dbs_in_memory, .wipe = to_wipe_data}); dmnman.reset(); - dmnman = std::make_unique(*evodb, mn_metaman); + dmnman = std::make_unique(*evodb, *options.mn_metaman); + + chainman.m_total_coinstip_cache = cache_sizes.coins; + chainman.m_total_coinsdb_cache = cache_sizes.coins_db; // Load the fully validated chainstate. chainman.InitializeChainstate(options.mempool, *evodb, chain_helper); @@ -75,17 +76,17 @@ ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, // 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()); + llmq_ctx = std::make_unique(*dmnman, *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(dmnman.get(), llmq_ctx->isman.get()); } - // Initialize chain_helpper + // Initialize chain_helper 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, + chain_helper = std::make_unique(*evodb, *dmnman, *options.mn_sync, *(llmq_ctx->isman), *(llmq_ctx->quorum_block_processor), + *(llmq_ctx->qsnapman), chainman, chainman.GetConsensus(), *options.chainlocks, *(llmq_ctx->qman)); if (options.reindex) { @@ -109,6 +110,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?")}; } @@ -203,8 +206,8 @@ ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, return {ChainstateLoadStatus::SUCCESS, {}}; } -ChainstateLoadResult VerifyLoadedChainstate(ChainstateManager& chainman, CEvoDB& evodb, - const ChainstateLoadOptions& options) +ChainstateLoadResult VerifyLoadedChainstate(ChainstateManager& chainman, const ChainstateLoadOptions& options, CEvoDB& evodb, + 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(); @@ -223,7 +226,8 @@ 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( @@ -238,7 +242,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) { diff --git a/src/node/chainstate.h b/src/node/chainstate.h index f6c88b72405a..678244970034 100644 --- a/src/node/chainstate.h +++ b/src/node/chainstate.h @@ -5,13 +5,15 @@ #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 +31,30 @@ class path; } // namespace fs namespace node { + struct CacheSizes; struct ChainstateLoadOptions { CTxMemPool* mempool{nullptr}; + 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 @@ -70,23 +78,16 @@ using ChainstateLoadResult = std::tuple; * - a failure that definitively cannot be recovered from with a reindex * * LoadChainstate returns a (status code, error string) tuple. + * + * The evodb, dmnman, llmq_ctx and chain_helper arguments are outputs: any + * instance they hold is destroyed and replaced with a freshly constructed one. */ -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, std::unique_ptr& evodb, + std::unique_ptr& dmnman, std::unique_ptr& llmq_ctx, + std::unique_ptr& chain_helper); +ChainstateLoadResult VerifyLoadedChainstate(ChainstateManager& chainman, const ChainstateLoadOptions& options, CEvoDB& evodb, + 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 f85eb8b1265a..955795ab2f39 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; @@ -301,40 +296,32 @@ ChainTestingSetup::~ChainTestingSetup() void ChainTestingSetup::LoadVerifyActivateChainstate() { auto& chainman{*Assert(m_node.chainman)}; + node::ChainstateLoadOptions options; 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.data_dir = Assert(m_node.args)->GetDataDirNet(); 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); + options.check_interrupt = [] { return false; }; + options.coins_error_cb = [] {}; + + auto [status, error] = LoadChainstate(chainman, m_cache_sizes, options, m_node.evodb, m_node.dmnman, m_node.llmq_ctx, + m_node.chain_helper); assert(status == node::ChainstateLoadStatus::SUCCESS); - std::tie(status, error) = VerifyLoadedChainstate( - chainman, - *Assert(m_node.evodb.get()), - options); + std::tie(status, error) = VerifyLoadedChainstate(chainman, options, *Assert(m_node.evodb), [](bool bls_state) { + LogPrintf("%s: bls_legacy_scheme=%d\n", __func__, bls_state); + }); assert(status == node::ChainstateLoadStatus::SUCCESS); BlockValidationState state; From 570a20283670d259a6477e1328ca7f0eb0bdc054 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Fri, 17 Jul 2026 14:50:27 +0700 Subject: [PATCH 4/5] 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 9f0dc7db27d5..5c7b664da737 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -429,11 +429,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 955795ab2f39..dbe97f79863b 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -395,11 +395,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 58bef3a84185..d26beca80fb6 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(); } From a3fdcd6c7f22f99459ed4d8eda5e2133e73e15cc Mon Sep 17 00:00:00 2001 From: UdjinM6 Date: Fri, 31 Jul 2026 23:53:46 +0300 Subject: [PATCH 5/5] refactor: address review feedback for #7471 - node/chainstate.h: include for the by-value fs::path member instead of relying on the transitive include from , and drop the now-unused forward declaration of fs::path - init.cpp, test/util/setup_common.cpp: __func__ inside a lambda expands to "operator()", so spell out the enclosing function name in the bls_legacy_scheme log line - init.cpp: note why the mempool has to release the managers before they are destroyed - validation_chainstatemanager_tests.cpp: drop the two vestigial clhandler->Stop() calls, and the include they needed The last one used to read `m_node.llmq_ctx->Stop()`, which stopped the LLMQ worker threads before `DashChainstateSetupClose()` destroyed the context. When clhandler moved out of LLMQContext into NodeContext those calls were mechanically retargeted at the chainlock handler, which is a different object with a different lifetime. `ChainlockHandler::Stop()` only stops the handler's own scheduler, and `ChainlockHandler::Start()` is never called outside of init.cpp, so in unit tests nothing is ever queued on it. The handler is also neither destroyed here nor registered with the validation interface in the test fixtures, and ~ChainlockHandler() already stops and joins its thread. --- src/init.cpp | 4 +++- src/node/chainstate.h | 4 +--- src/test/util/setup_common.cpp | 2 +- src/test/validation_chainstatemanager_tests.cpp | 4 ---- 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 5c7b664da737..49d91ff9203f 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -429,6 +429,8 @@ void PrepareShutdown(NodeContext& node) chainstate->ResetCoinsViews(); } } + // The mempool holds raw pointers to dmnman and llmq_ctx->isman, so it has to + // let go of them before either manager is destroyed. if (node.mempool) { node.mempool->DisconnectManagers(); } @@ -2073,7 +2075,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) MIN_BLOCKS_TO_KEEP); } std::tie(status, error) = catch_exceptions([&]{ return VerifyLoadedChainstate(chainman, options, *Assert(node.evodb), [](bool bls_state) { - LogPrintf("%s: bls_legacy_scheme=%d\n", __func__, bls_state); + LogPrintf("AppInitMain: bls_legacy_scheme=%d\n", bls_state); });}); if (status == node::ChainstateLoadStatus::SUCCESS) { fLoaded = true; diff --git a/src/node/chainstate.h b/src/node/chainstate.h index 678244970034..3f66cddd2ddd 100644 --- a/src/node/chainstate.h +++ b/src/node/chainstate.h @@ -6,6 +6,7 @@ #define BITCOIN_NODE_CHAINSTATE_H #include +#include #include #include @@ -26,9 +27,6 @@ class CTxMemPool; struct LLMQContext; namespace chainlock { class Chainlocks; } -namespace fs { -class path; -} // namespace fs namespace node { diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index dbe97f79863b..0a64da394f70 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -320,7 +320,7 @@ void ChainTestingSetup::LoadVerifyActivateChainstate() assert(status == node::ChainstateLoadStatus::SUCCESS); std::tie(status, error) = VerifyLoadedChainstate(chainman, options, *Assert(m_node.evodb), [](bool bls_state) { - LogPrintf("%s: bls_legacy_scheme=%d\n", __func__, bls_state); + LogPrintf("LoadVerifyActivateChainstate: bls_legacy_scheme=%d\n", bls_state); }); assert(status == node::ChainstateLoadStatus::SUCCESS); diff --git a/src/test/validation_chainstatemanager_tests.cpp b/src/test/validation_chainstatemanager_tests.cpp index d26beca80fb6..5b576b3bc58e 100644 --- a/src/test/validation_chainstatemanager_tests.cpp +++ b/src/test/validation_chainstatemanager_tests.cpp @@ -22,7 +22,6 @@ #include #include -#include #include #include #include @@ -59,9 +58,6 @@ static void DashChainstateSetup(ChainstateManager& chainman, static void DashChainstateSetupClose(node::NodeContext& node) { - if (node.clhandler) { - node.clhandler->Stop(); - } if (node.mempool) { node.mempool->DisconnectManagers(); }