From 513261c43620baec89bf0dbeb7febf34219bf585 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 0774f56c81c81b25b16d17da4384e63cd728c366 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 | 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/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 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/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 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(); }