From 46308fc960686320f15017eeda35470f3ee42463 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Fri, 7 Aug 2026 00:38:00 +0700 Subject: [PATCH 01/12] refactor: move evodb initialization out from LoadChainState --- src/bitcoin-chainstate.cpp | 9 ++-- src/init.cpp | 10 +++- src/node/chainstate.cpp | 48 ++++++++----------- src/node/chainstate.h | 10 ++-- src/test/util/setup_common.cpp | 17 +++++-- .../validation_chainstatemanager_tests.cpp | 2 +- 6 files changed, 49 insertions(+), 47 deletions(-) diff --git a/src/bitcoin-chainstate.cpp b/src/bitcoin-chainstate.cpp index 24eb5965c26d..7d68d122b7ff 100644 --- a/src/bitcoin-chainstate.cpp +++ b/src/bitcoin-chainstate.cpp @@ -93,8 +93,8 @@ int main(int argc, char* argv[]) ChainstateManager chainman{chainman_opts}; CMasternodeMetaMan metaman; - std::unique_ptr evodb; - std::unique_ptr dmnman; + CEvoDB evodb{util::DbWrapperParams{.path = gArgs.GetDataDirNet(), .memory = false, .wipe = false}}; + CDeterministicMNManager dmnman{evodb, metaman}; CMasternodeSync mn_sync{std::make_unique()}; CSporkManager sporkman; chainlock::Chainlocks chainlocks(sporkman); @@ -107,7 +107,6 @@ int main(int argc, char* argv[]) cache_sizes.coins_db = 2 << 22; cache_sizes.coins = (450 << 20) - (2 << 20) - (2 << 22); node::ChainstateLoadOptions options; - options.mn_metaman = &metaman; options.sporkman = &sporkman; options.chainlocks = &chainlocks; options.mn_sync = &mn_sync; @@ -119,7 +118,7 @@ int main(int argc, char* argv[]) std::cerr << "Failed to load Chain state from your datadir." << std::endl; goto epilogue; } else { - std::tie(status, error) = node::VerifyLoadedChainstate(chainman, options, *evodb); + 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; @@ -278,6 +277,4 @@ int main(int argc, char* argv[]) // Tear down 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 61b8ed7501ef..d1a33fd5a190 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1949,6 +1949,13 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) LogPrintf("* Using %.1f MiB for in-memory UTXO set (plus up to %.1f MiB of unused mempool space)\n", cache_sizes.coins * (1.0 / 1024 / 1024), mempool_opts.max_size_bytes * (1.0 / 1024 / 1024)); for (bool fLoaded = false; !fLoaded && !ShutdownRequested();) { + // On a retry iteration the previous instances still hold the on-disk + // database locks, so release them before opening the databases again. + node.dmnman.reset(); + node.evodb.reset(); + node.evodb = std::make_unique(util::DbWrapperParams{.path = args.GetDataDirNet(), .memory = false, .wipe = node::fReindex || fReindexChainState}); + node.dmnman = std::make_unique(*node.evodb, *node.mn_metaman); + node.mempool = std::make_unique(mempool_opts); const ChainstateManager::Options chainman_opts{ @@ -1969,7 +1976,6 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) 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()); @@ -2008,7 +2014,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) return std::make_tuple(node::ChainstateLoadStatus::FAILURE, _("Error opening block database")); } }; - auto [status, error] = catch_exceptions([&]{ return LoadChainstate(chainman, cache_sizes, options, node.evodb, node.dmnman, node.llmq_ctx, node.chain_helper); }); + 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) { diff --git a/src/node/chainstate.cpp b/src/node/chainstate.cpp index 87c91b2dcfa6..a2f7e4c6a069 100644 --- a/src/node/chainstate.cpp +++ b/src/node/chainstate.cpp @@ -139,16 +139,13 @@ static bool RecoverSnapshotCleanup(CEvoDB& evodb, const fs::path& data_dir, bili // to ChainstateManager::InitializeChainstate(). static ChainstateLoadResult CompleteChainstateInitialization(ChainstateManager& chainman, const CacheSizes& cache_sizes, const ChainstateLoadOptions& options, CEvoDB& evodb, - std::unique_ptr& dmnman, + CDeterministicMNManager& dmnman, std::unique_ptr& llmq_ctx, std::unique_ptr& chain_helper) EXCLUSIVE_LOCKS_REQUIRED(::cs_main) { const bool to_wipe_data = options.reindex || options.reindex_chainstate; - dmnman.reset(); - dmnman = std::make_unique(evodb, *options.mn_metaman); - auto& pblocktree{chainman.m_blockman.m_block_tree_db}; // new CBlockTreeDB tries to delete the existing file, which // fails if it's still open from the previous loop. Close it first: @@ -157,16 +154,16 @@ static ChainstateLoadResult CompleteChainstateInitialization(ChainstateManager& // Initialize llmq_ctx and connection to mempool llmq_ctx.reset(); - llmq_ctx = std::make_unique(*dmnman, evodb, *options.sporkman, chainman, + 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()); + options.mempool->ConnectManagers(&dmnman, llmq_ctx->isman.get()); } // Initialize chain_helper chain_helper.reset(); - chain_helper = std::make_unique(evodb, *dmnman, *options.mn_sync, *(llmq_ctx->isman), *(llmq_ctx->quorum_block_processor), + 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)); @@ -287,7 +284,7 @@ static ChainstateLoadResult CompleteChainstateInitialization(ChainstateManager& } // Check if nVersion-first migration is needed and perform it - if (dmnman->IsMigrationRequired() && !dmnman->MigrateLegacyDiffs(chainman.ActiveChainstate().m_chain.Tip())) { + if (dmnman.IsMigrationRequired() && !dmnman.MigrateLegacyDiffs(chainman.ActiveChainstate().m_chain.Tip())) { return {ChainstateLoadStatus::FAILURE, _("Failed to upgrade Evo database")}; } @@ -300,11 +297,10 @@ static ChainstateLoadResult CompleteChainstateInitialization(ChainstateManager& } ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSizes& cache_sizes, - const ChainstateLoadOptions& options, std::unique_ptr& evodb, - std::unique_ptr& dmnman, std::unique_ptr& llmq_ctx, + const ChainstateLoadOptions& options, CEvoDB& evodb, + CDeterministicMNManager& 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); @@ -326,15 +322,9 @@ ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSize LOCK(cs_main); - evodb.reset(); - // 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 = options.reindex || options.reindex_chainstate}); if (!options.dash_dbs_in_memory && !options.reindex && !options.reindex_chainstate) { bilingual_str recovery_error; - if (!RecoverSnapshotCleanup(*evodb, options.data_dir, recovery_error)) { + if (!RecoverSnapshotCleanup(evodb, options.data_dir, recovery_error)) { return {ChainstateLoadStatus::FAILURE, recovery_error}; } } @@ -342,12 +332,13 @@ ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSize chainman.m_total_coinsdb_cache = cache_sizes.coins_db; // Load the fully validated chainstate. - chainman.InitializeChainstate(options.mempool, *evodb, chain_helper); + chainman.InitializeChainstate(options.mempool, evodb, chain_helper); - // Wiping the shared EvoDB above erased the SNAPSHOT best-block marker that - // ActivateExistingSnapshot() requires, so a persisted snapshot chainstate can - // no longer be revived. Discard it here rather than letting startup fail with - // advice ("reindex") the user has just followed, which would never recover. + // On a reindex the caller hands us a freshly wiped EvoDB, without the + // SNAPSHOT best-block marker that ActivateExistingSnapshot() requires, so a + // persisted snapshot chainstate can no longer be revived. Discard it here + // rather than letting startup fail with advice ("reindex") the user has + // just followed, which would never recover. if ((options.reindex || options.reindex_chainstate) && !DeleteSnapshotChainstateFromDisk()) { return {ChainstateLoadStatus::FAILURE, _("Failed to remove the snapshot chainstate directory. Remove it manually before restarting.")}; @@ -359,7 +350,7 @@ ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSize return {ChainstateLoadStatus::FAILURE, snapshot_error}; } - auto [init_status, init_error] = CompleteChainstateInitialization(chainman, cache_sizes, options, *evodb, dmnman, + auto [init_status, init_error] = CompleteChainstateInitialization(chainman, cache_sizes, options, evodb, dmnman, llmq_ctx, chain_helper); if (init_status != ChainstateLoadStatus::SUCCESS) { return {init_status, init_error}; @@ -379,14 +370,13 @@ ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSize // Do nothing; expected case. } else if (snapshot_completion == SnapshotCompletionResult::SUCCESS) { LogPrintf("[snapshot] cleaning up unneeded background chainstate, then reinitializing\n"); - // The mempool holds raw pointers to dmnman and llmq_ctx->isman, so it has to - // let go of them before either manager is destroyed. + // The mempool holds a raw pointer to llmq_ctx->isman, so it has to + // let go of it before the LLMQ context is destroyed. if (options.mempool) { options.mempool->DisconnectManagers(); } chain_helper.reset(); llmq_ctx.reset(); - dmnman.reset(); if (!chainman.ValidatedSnapshotCleanup()) { return {ChainstateLoadStatus::FAILURE_FATAL, Untranslated("Background chainstate cleanup failed unexpectedly.")}; } @@ -398,13 +388,13 @@ ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSize assert(!chainman.IsSnapshotActive()); assert(!chainman.IsSnapshotValidated()); - chainman.InitializeChainstate(options.mempool, *evodb, chain_helper); + chainman.InitializeChainstate(options.mempool, evodb, chain_helper); // A reload of the block index is required to recompute setBlockIndexCandidates // for the fully validated chainstate. chainman.ActiveChainstate().ClearBlockIndexCandidates(); - std::tie(init_status, init_error) = CompleteChainstateInitialization(chainman, cache_sizes, options, *evodb, + std::tie(init_status, init_error) = CompleteChainstateInitialization(chainman, cache_sizes, options, evodb, dmnman, llmq_ctx, chain_helper); if (init_status != ChainstateLoadStatus::SUCCESS) { return {init_status, init_error}; diff --git a/src/node/chainstate.h b/src/node/chainstate.h index 98c0ea9f7511..e582353caab0 100644 --- a/src/node/chainstate.h +++ b/src/node/chainstate.h @@ -21,7 +21,6 @@ class CDeterministicMNManager; class CEvoDB; class ChainstateManager; class CMasternodeSync; -class CMasternodeMetaMan; class CSporkManager; class CTxMemPool; struct LLMQContext; @@ -34,7 +33,6 @@ struct CacheSizes; struct ChainstateLoadOptions { CTxMemPool* mempool{nullptr}; - CMasternodeMetaMan* mn_metaman{nullptr}; CSporkManager* sporkman{nullptr}; chainlock::Chainlocks* chainlocks{nullptr}; const CMasternodeSync* mn_sync{nullptr}; @@ -83,12 +81,12 @@ using ChainstateLoadResult = std::tuple; * * 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. + * The 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, const CacheSizes& cache_sizes, - const ChainstateLoadOptions& options, std::unique_ptr& evodb, - std::unique_ptr& dmnman, std::unique_ptr& llmq_ctx, + const ChainstateLoadOptions& options, CEvoDB& evodb, + CDeterministicMNManager& 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); diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index a370cb1072ea..db2ef0df95a8 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -221,6 +221,7 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName, const std::ve m_node.sporkman = std::make_unique(); m_node.chainlocks = std::make_unique(*m_node.sporkman); m_node.evodb = std::make_unique(util::DbWrapperParams{.path = m_node.args->GetDataDirNet(), .memory = m_dash_dbs_in_memory, .wipe = true}); + m_node.dmnman = std::make_unique(*m_node.evodb, *m_node.mn_metaman); static bool noui_connected = false; if (!noui_connected) { @@ -236,6 +237,7 @@ BasicTestingSetup::~BasicTestingSetup() SetMockTime(0s); // Reset mocktime for following tests LogInstance().DisconnectTestLogger(); // Close disk-backed EvoDB before deleting its data directory. + m_node.dmnman.reset(); m_node.evodb.reset(); fs::remove_all(m_path_root); gArgs.ClearArgs(); @@ -300,7 +302,6 @@ node::ChainstateLoadOptions ChainTestingSetup::ChainstateLoadOptionsForTest() { 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()); @@ -322,9 +323,19 @@ void ChainTestingSetup::LoadVerifyActivateChainstate() { auto& chainman{*Assert(m_node.chainman)}; - const node::ChainstateLoadOptions options{ChainstateLoadOptionsForTest()}; + node::ChainstateLoadOptions options{ChainstateLoadOptionsForTest()}; + + if (options.reindex || options.reindex_chainstate) { + // A reindex wipes the Dash databases at open, which AppInitMain does by + // recreating them. Mirror that here. + m_node.dmnman.reset(); + m_node.evodb.reset(); + m_node.evodb = std::make_unique(util::DbWrapperParams{.path = m_node.args->GetDataDirNet(), .memory = m_dash_dbs_in_memory, .wipe = true}); + m_node.dmnman = std::make_unique(*m_node.evodb, *m_node.mn_metaman); + options = ChainstateLoadOptionsForTest(); + } - auto [status, error] = LoadChainstate(chainman, m_cache_sizes, options, m_node.evodb, m_node.dmnman, m_node.llmq_ctx, + 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); diff --git a/src/test/validation_chainstatemanager_tests.cpp b/src/test/validation_chainstatemanager_tests.cpp index 901a3f094f65..f02f3a98d00d 100644 --- a/src/test/validation_chainstatemanager_tests.cpp +++ b/src/test/validation_chainstatemanager_tests.cpp @@ -1223,7 +1223,7 @@ BOOST_FIXTURE_TEST_CASE(chainstatemanager_snapshot_missing_base_fails_load, Snap { ASSERT_DEBUG_LOG("missing from the block index"); std::tie(status, error) = node::LoadChainstate(chainman, m_cache_sizes, ChainstateLoadOptionsForTest(), - m_node.evodb, m_node.dmnman, m_node.llmq_ctx, + *m_node.evodb, *m_node.dmnman, m_node.llmq_ctx, m_node.chain_helper); } BOOST_CHECK(status == node::ChainstateLoadStatus::FAILURE); From aa8151f732e02bfb6c07be35a5a6b937bf5a792f Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Thu, 13 Aug 2026 03:24:09 +0700 Subject: [PATCH 02/12] refactor: move CInstantSendManager out of LLMQContext The mempool must be able to hold CInstantSendManager for its whole lifetime, but as an LLMQContext member isman was destroyed and recreated together with the LLMQ subsystem (chainstate reload, snapshot completion), enforcing the work-around with ConnectManagers/DisconnectManagers. CInstantSendManager only needs CSporkManager and its own database, so nothing ties it to the LLMQ context's lifetime. --- src/bench/rpc_blockchain.cpp | 9 +++---- src/bitcoin-chainstate.cpp | 4 ++- src/init.cpp | 19 +++++++------ src/llmq/context.cpp | 5 ++-- src/llmq/context.h | 5 ++-- src/net_processing.cpp | 16 +++++------ src/node/chainstate.cpp | 12 ++++----- src/node/chainstate.h | 4 +-- src/node/context.cpp | 1 + src/node/context.h | 2 ++ src/node/interfaces.cpp | 8 +++--- src/node/miner.cpp | 2 +- src/rest.cpp | 27 +++++++++---------- src/rpc/blockchain.cpp | 9 +++---- src/rpc/mempool.cpp | 21 +++++++-------- src/rpc/rawtransaction.cpp | 13 +++++---- src/rpc/server_util.cpp | 8 ++++++ src/rpc/server_util.h | 4 +++ src/test/coinjoin_inouts_tests.cpp | 4 +-- src/test/evo_deterministicmns_tests.cpp | 6 ++--- src/test/util/setup_common.cpp | 11 +++++--- .../validation_chainstatemanager_tests.cpp | 6 ++--- 22 files changed, 107 insertions(+), 89 deletions(-) diff --git a/src/bench/rpc_blockchain.cpp b/src/bench/rpc_blockchain.cpp index 5a39e3393411..cb6b058a7b90 100644 --- a/src/bench/rpc_blockchain.cpp +++ b/src/bench/rpc_blockchain.cpp @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include @@ -43,9 +42,9 @@ struct TestBlockAndIndex { static void BlockToJsonVerbose(benchmark::Bench& bench) { TestBlockAndIndex data; - const LLMQContext& llmq_ctx = *data.testing_setup->m_node.llmq_ctx; + const llmq::CInstantSendManager& isman = *data.testing_setup->m_node.isman; bench.run([&] { - auto univalue = blockToJSON(data.testing_setup->m_node.chainman->m_blockman, data.block, &data.blockindex, &data.blockindex, *data.testing_setup->m_node.chainlocks, *llmq_ctx.isman, TxVerbosity::SHOW_DETAILS_AND_PREVOUT); + auto univalue = blockToJSON(data.testing_setup->m_node.chainman->m_blockman, data.block, &data.blockindex, &data.blockindex, *data.testing_setup->m_node.chainlocks, isman, TxVerbosity::SHOW_DETAILS_AND_PREVOUT); ankerl::nanobench::doNotOptimizeAway(univalue); }); } @@ -55,8 +54,8 @@ BENCHMARK(BlockToJsonVerbose, benchmark::PriorityLevel::HIGH); static void BlockToJsonVerboseWrite(benchmark::Bench& bench) { TestBlockAndIndex data; - const LLMQContext& llmq_ctx = *data.testing_setup->m_node.llmq_ctx; - auto univalue = blockToJSON(data.testing_setup->m_node.chainman->m_blockman, data.block, &data.blockindex, &data.blockindex, *data.testing_setup->m_node.chainlocks, *llmq_ctx.isman, TxVerbosity::SHOW_DETAILS_AND_PREVOUT); + const llmq::CInstantSendManager& isman = *data.testing_setup->m_node.isman; + auto univalue = blockToJSON(data.testing_setup->m_node.chainman->m_blockman, data.block, &data.blockindex, &data.blockindex, *data.testing_setup->m_node.chainlocks, isman, TxVerbosity::SHOW_DETAILS_AND_PREVOUT); bench.run([&] { auto str = univalue.write(); ankerl::nanobench::doNotOptimizeAway(str); diff --git a/src/bitcoin-chainstate.cpp b/src/bitcoin-chainstate.cpp index 7d68d122b7ff..ff6750339987 100644 --- a/src/bitcoin-chainstate.cpp +++ b/src/bitcoin-chainstate.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -98,6 +99,7 @@ int main(int argc, char* argv[]) CMasternodeSync mn_sync{std::make_unique()}; CSporkManager sporkman; chainlock::Chainlocks chainlocks(sporkman); + llmq::CInstantSendManager isman{sporkman, util::DbWrapperParams{.path = gArgs.GetDataDirNet(), .memory = false, .wipe = false}}; std::unique_ptr llmq_ctx; std::unique_ptr chain_helper; @@ -107,7 +109,7 @@ int main(int argc, char* argv[]) cache_sizes.coins_db = 2 << 22; cache_sizes.coins = (450 << 20) - (2 << 20) - (2 << 22); node::ChainstateLoadOptions options; - options.sporkman = &sporkman; + options.isman = &isman; options.chainlocks = &chainlocks; options.mn_sync = &mn_sync; options.data_dir = gArgs.GetDataDirNet(); diff --git a/src/init.cpp b/src/init.cpp index d1a33fd5a190..472c5d31188d 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -429,13 +429,14 @@ void PrepareShutdown(NodeContext& node) chainstate->ResetCoinsViews(); } } - // The mempool holds raw pointers to dmnman and llmq_ctx->isman, so it has to + // The mempool holds raw pointers to dmnman and isman, so it has to // let go of them before either manager is destroyed. if (node.mempool) { node.mempool->DisconnectManagers(); } node.chain_helper.reset(); node.llmq_ctx.reset(); + node.isman.reset(); node.dmnman.reset(); node.evodb.reset(); } @@ -900,7 +901,7 @@ static void PeriodicStats(NodeContext& node) assert(::g_stats_client->active()); ChainstateManager& chainman = *Assert(node.chainman); const CTxMemPool& mempool = *Assert(node.mempool); - const llmq::CInstantSendManager& isman = *Assert(node.llmq_ctx->isman); + const llmq::CInstantSendManager& isman = *Assert(node.isman); chainman.ActiveChainstate().ForceFlushStateToDisk(); const auto maybe_stats = WITH_LOCK(::cs_main, return GetUTXOStats(&chainman.ActiveChainstate().CoinsDB(), chainman.m_blockman, /*hash_type=*/CoinStatsHashType::NONE, node.rpc_interruption_point, chainman.ActiveChain().Tip(), /*index_requested=*/true)); if (maybe_stats.has_value()) { @@ -1951,10 +1952,12 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) for (bool fLoaded = false; !fLoaded && !ShutdownRequested();) { // On a retry iteration the previous instances still hold the on-disk // database locks, so release them before opening the databases again. + node.isman.reset(); node.dmnman.reset(); node.evodb.reset(); node.evodb = std::make_unique(util::DbWrapperParams{.path = args.GetDataDirNet(), .memory = false, .wipe = node::fReindex || fReindexChainState}); node.dmnman = std::make_unique(*node.evodb, *node.mn_metaman); + node.isman = std::make_unique(*node.sporkman, util::DbWrapperParams{.path = args.GetDataDirNet(), .memory = false, .wipe = node::fReindex || fReindexChainState}); node.mempool = std::make_unique(mempool_opts); @@ -1976,7 +1979,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) node::ChainstateLoadOptions options; options.mempool = Assert(node.mempool.get()); - options.sporkman = Assert(node.sporkman.get()); + options.isman = Assert(node.isman.get()); options.chainlocks = Assert(node.chainlocks.get()); options.mn_sync = Assert(node.mn_sync.get()); options.data_dir = args.GetDataDirNet(); @@ -2089,7 +2092,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) // Will init later in ThreadImport node.active_ctx = std::make_unique(*node.llmq_ctx->bls_worker, chainman, *node.connman, *node.dmnman, *node.govman, *node.chain_helper->superblocks, - *node.sporkman, *node.chainlocks, *node.mempool, *node.clhandler, *node.llmq_ctx->isman, + *node.sporkman, *node.chainlocks, *node.mempool, *node.clhandler, *node.isman, *node.llmq_ctx->qman, *node.llmq_ctx->qsnapman, *node.llmq_ctx->sigman, *node.mn_sync, operator_sk, dash_db_params, quorums_watch); RegisterValidationInterface(node.active_ctx.get()); @@ -2114,7 +2117,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) // ********************************************************* Step 7d: Setup other Dash services - node.peerman->AddExtraHandler(std::make_unique(node.peerman.get(), *node.llmq_ctx->isman, node.active_ctx ? node.active_ctx->is_signer.get() : nullptr, *node.llmq_ctx->sigman, *node.llmq_ctx->qman, *node.chainlocks, chainman, *node.mempool, *node.mn_sync)); + node.peerman->AddExtraHandler(std::make_unique(node.peerman.get(), *node.isman, node.active_ctx ? node.active_ctx->is_signer.get() : nullptr, *node.llmq_ctx->sigman, *node.llmq_ctx->qman, *node.chainlocks, chainman, *node.mempool, *node.mn_sync)); node.peerman->AddExtraHandler(std::make_unique(node.peerman.get(), *node.llmq_ctx->sigman, node.active_ctx ? node.active_ctx->shareman.get() : nullptr, *node.sporkman)); { @@ -2146,7 +2149,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) if (node.active_ctx) { auto cj_server = std::make_unique(node.peerman.get(), chainman, *node.connman, *node.dmnman, *node.dstxman, *node.mn_metaman, - *node.mempool, *node.active_ctx->nodeman, *node.mn_sync, *node.llmq_ctx->isman); + *node.mempool, *node.active_ctx->nodeman, *node.mn_sync, *node.isman); node.active_ctx->SetCJServer(cj_server.get()); node.peerman->AddExtraHandler(std::move(cj_server)); } else { @@ -2154,7 +2157,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) // Only constructed in wallet-enabled builds; stays null otherwise, must check before use #ifdef ENABLE_WALLET node.cj_walletman = CJWalletManager::make(chainman, *node.dmnman, *node.mn_metaman, *node.mempool, *node.mn_sync, - *node.llmq_ctx->isman, !ignores_incoming_txs); + *node.isman, !ignores_incoming_txs); #endif } @@ -2392,7 +2395,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) // Seed InstantSend tip-height cache; NetInstantSend receives future // updates via CValidationInterface but misses InitializeCurrentBlockTip. // TODO: move cache updates from NetInstantSend to g_ds_notification due to specific of Tip's processing - node.llmq_ctx->isman->CacheTipHeight(WITH_LOCK(::cs_main, return chainman.ActiveChain().Tip())); + node.isman->CacheTipHeight(WITH_LOCK(::cs_main, return chainman.ActiveChain().Tip())); { // Get all UTXOs for each MN collateral in one go so that we can fill coin cache early diff --git a/src/llmq/context.cpp b/src/llmq/context.cpp index f144f5e5db84..ca57202dd3f0 100644 --- a/src/llmq/context.cpp +++ b/src/llmq/context.cpp @@ -5,14 +5,13 @@ #include #include -#include #include #include #include #include #include -LLMQContext::LLMQContext(CDeterministicMNManager& dmnman, CEvoDB& evo_db, CSporkManager& sporkman, +LLMQContext::LLMQContext(CDeterministicMNManager& dmnman, CEvoDB& evo_db, llmq::CInstantSendManager& isman, ChainstateManager& chainman, const util::DbWrapperParams& db_params, int8_t bls_threads, int16_t worker_count, int64_t max_recsigs_age) : bls_worker{std::make_shared()}, @@ -22,7 +21,7 @@ LLMQContext::LLMQContext(CDeterministicMNManager& dmnman, CEvoDB& evo_db, CSpork qman{std::make_unique(*bls_worker, dmnman, evo_db, *quorum_block_processor, *qsnapman, chainman, db_params)}, sigman{std::make_unique(*qman, db_params, max_recsigs_age)}, - isman{std::make_unique(sporkman, db_params)} + isman{isman} { // Have to start it early to let VerifyDB check ChainLock signatures in coinbase bls_worker->Start(worker_count); diff --git a/src/llmq/context.h b/src/llmq/context.h index 8ffc38dfa529..17c598e36896 100644 --- a/src/llmq/context.h +++ b/src/llmq/context.h @@ -13,7 +13,6 @@ class CBLSWorker; class ChainstateManager; class CDeterministicMNManager; class CEvoDB; -class CSporkManager; class PeerManager; namespace llmq { @@ -32,7 +31,7 @@ struct LLMQContext { LLMQContext() = delete; LLMQContext(const LLMQContext&) = delete; LLMQContext& operator=(const LLMQContext&) = delete; - explicit LLMQContext(CDeterministicMNManager& dmnman, CEvoDB& evo_db, CSporkManager& sporkman, + explicit LLMQContext(CDeterministicMNManager& dmnman, CEvoDB& evo_db, llmq::CInstantSendManager& isman, ChainstateManager& chainman, const util::DbWrapperParams& db_params, int8_t bls_threads, int16_t worker_count, int64_t max_recsigs_age); ~LLMQContext(); @@ -48,7 +47,7 @@ struct LLMQContext { const std::unique_ptr quorum_block_processor; const std::unique_ptr qman; const std::unique_ptr sigman; - const std::unique_ptr isman; + llmq::CInstantSendManager& isman; }; #endif // BITCOIN_LLMQ_CONTEXT_H diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 0e265c36bb2c..acdf8a8b1b8a 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -2319,8 +2319,8 @@ bool PeerManagerImpl::AlreadyHave(const CInv& inv) // crafted invalid DSTX-es and potentially cause high load cheaply, because // corresponding checks in ProcessMessage won't let it to send DSTX-es too often. bool fIgnoreRecentRejects = inv.IsMsgDstx() || - m_llmq_ctx->isman->IsWaitingForTx(inv.hash) || - m_llmq_ctx->isman->IsLocked(inv.hash); + m_llmq_ctx->isman.IsWaitingForTx(inv.hash) || + m_llmq_ctx->isman.IsLocked(inv.hash); return (!fIgnoreRecentRejects && m_recent_rejects.contains(inv.hash)) || (inv.IsMsgDstx() && static_cast(m_dstxman.GetDSTX(inv.hash))) || @@ -2359,7 +2359,7 @@ bool PeerManagerImpl::AlreadyHave(const CInv& inv) return m_clhandler.AlreadyHave(inv); // TODO: move it to NetInstantSend case MSG_ISDLOCK: - return m_llmq_ctx->isman->AlreadyHave(inv); + return m_llmq_ctx->isman.AlreadyHave(inv); case MSG_PLATFORM_BAN: return m_mn_metaman.AlreadyHavePlatformBan(inv.hash); @@ -2961,7 +2961,7 @@ void PeerManagerImpl::ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic if (!push && inv.type == MSG_ISDLOCK) { instantsend::InstantSendLock o; - if (m_llmq_ctx->isman->GetInstantSendLockByHash(inv.hash, o)) { + if (m_llmq_ctx->isman.GetInstantSendLockByHash(inv.hash, o)) { m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::ISDLOCK, o)); push = true; } @@ -2996,7 +2996,7 @@ void PeerManagerImpl::ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic if (it != peer.m_getdata_requests.end() && !pfrom.fPauseSend) { const CInv &inv = *it++; if (inv.IsGenBlkMsg()) { - ProcessGetBlockData(pfrom, peer, inv, *m_llmq_ctx->isman); + ProcessGetBlockData(pfrom, peer, inv, m_llmq_ctx->isman); } // else: If the first item on the queue is an unknown type, we erase it // and continue processing the queue on the next call. @@ -4859,7 +4859,7 @@ void PeerManagerImpl::ProcessMessage( // parents so avoid re-requesting it from other peers. m_recent_rejects.insert(tx.GetHash()); ForgetTx(tx.GetHash()); - m_llmq_ctx->isman->TransactionIsRemoved(ptx); + m_llmq_ctx->isman.TransactionIsRemoved(ptx); } } else { m_recent_rejects.insert(tx.GetHash()); @@ -4891,7 +4891,7 @@ void PeerManagerImpl::ProcessMessage( pfrom.GetId(), state.ToString()); MaybePunishNodeForTx(pfrom.GetId(), state); - m_llmq_ctx->isman->TransactionIsRemoved(ptx); + m_llmq_ctx->isman.TransactionIsRemoved(ptx); } return; } @@ -6468,7 +6468,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto) tx_relay->m_tx_inventory_known_filter.insert(hash); queueAndMaybePushInv(CInv(nInvType, hash)); - const auto islock = m_llmq_ctx->isman->GetInstantSendLockByTxid(hash); + const auto islock = m_llmq_ctx->isman.GetInstantSendLockByTxid(hash); if (islock == nullptr) continue; uint256 isLockHash{::SerializeHash(*islock)}; tx_relay->m_tx_inventory_known_filter.insert(isLockHash); diff --git a/src/node/chainstate.cpp b/src/node/chainstate.cpp index a2f7e4c6a069..f5a2eb393291 100644 --- a/src/node/chainstate.cpp +++ b/src/node/chainstate.cpp @@ -154,16 +154,16 @@ static ChainstateLoadResult CompleteChainstateInitialization(ChainstateManager& // Initialize llmq_ctx and connection to mempool llmq_ctx.reset(); - llmq_ctx = std::make_unique(dmnman, evodb, *options.sporkman, chainman, + llmq_ctx = std::make_unique(dmnman, evodb, *options.isman, 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, llmq_ctx->isman.get()); + options.mempool->ConnectManagers(&dmnman, options.isman); } // Initialize chain_helper chain_helper.reset(); - chain_helper = std::make_unique(evodb, dmnman, *options.mn_sync, *(llmq_ctx->isman), *(llmq_ctx->quorum_block_processor), + 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)); @@ -301,7 +301,7 @@ ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSize CDeterministicMNManager& dmnman, std::unique_ptr& llmq_ctx, std::unique_ptr& chain_helper) { - assert(options.sporkman); + assert(options.isman); assert(options.chainlocks); assert(options.mn_sync); @@ -370,8 +370,8 @@ ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSize // Do nothing; expected case. } else if (snapshot_completion == SnapshotCompletionResult::SUCCESS) { LogPrintf("[snapshot] cleaning up unneeded background chainstate, then reinitializing\n"); - // The mempool holds a raw pointer to llmq_ctx->isman, so it has to - // let go of it before the LLMQ context is destroyed. + // ConnectManagers() in the reinitialization below forbids + // double-initialization, so detach the mempool's managers first. if (options.mempool) { options.mempool->DisconnectManagers(); } diff --git a/src/node/chainstate.h b/src/node/chainstate.h index e582353caab0..675afe3cc1b4 100644 --- a/src/node/chainstate.h +++ b/src/node/chainstate.h @@ -21,11 +21,11 @@ class CDeterministicMNManager; class CEvoDB; class ChainstateManager; class CMasternodeSync; -class CSporkManager; class CTxMemPool; struct LLMQContext; namespace chainlock { class Chainlocks; } +namespace llmq { class CInstantSendManager; } namespace node { @@ -33,7 +33,7 @@ struct CacheSizes; struct ChainstateLoadOptions { CTxMemPool* mempool{nullptr}; - CSporkManager* sporkman{nullptr}; + llmq::CInstantSendManager* isman{nullptr}; chainlock::Chainlocks* chainlocks{nullptr}; const CMasternodeSync* mn_sync{nullptr}; fs::path data_dir; diff --git a/src/node/context.cpp b/src/node/context.cpp index 335f34a22947..24687782ee5b 100644 --- a/src/node/context.cpp +++ b/src/node/context.cpp @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include diff --git a/src/node/context.h b/src/node/context.h index b0d839603418..ce9a384ecdb6 100644 --- a/src/node/context.h +++ b/src/node/context.h @@ -58,6 +58,7 @@ class Loader; } // namespace interfaces namespace llmq { +class CInstantSendManager; struct ObserverContext; } // namespace llmq @@ -102,6 +103,7 @@ struct NodeContext { std::unique_ptr evodb; std::unique_ptr chain_helper; std::unique_ptr dmnman; + std::unique_ptr isman; std::unique_ptr govman; std::unique_ptr mn_metaman; std::unique_ptr mn_sync; diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index 14b9fd4a204b..99d61215aa59 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -546,10 +546,10 @@ class LLMQImpl : public LLMQ } InstantSendCounts getInstantSendCounts() override { - if (!context().llmq_ctx || !context().llmq_ctx->isman) { + if (!context().isman) { return {}; } - const auto counts{context().llmq_ctx->isman->GetCounts()}; + const auto counts{context().isman->GetCounts()}; return { .m_verified = counts.m_verified, .m_unverified = counts.m_unverified, @@ -1383,8 +1383,8 @@ class ChainImpl : public Chain } bool isInstantSendLockedTx(const uint256& hash) override { - if (m_node.llmq_ctx == nullptr || m_node.llmq_ctx->isman == nullptr) return false; - return m_node.llmq_ctx->isman->IsLocked(hash); + if (m_node.isman == nullptr) return false; + return m_node.isman->IsLocked(hash); } bool hasChainLock(int height, const uint256& hash) override { diff --git a/src/node/miner.cpp b/src/node/miner.cpp index 34225a6d0daa..9146af7bf3f7 100644 --- a/src/node/miner.cpp +++ b/src/node/miner.cpp @@ -76,7 +76,7 @@ BlockAssembler::BlockAssembler(Chainstate& chainstate, const NodeContext& node, m_evoDb(*Assert(node.evodb)), m_chainlocks(*Assert(node.chainlocks)), m_clhandler(*Assert(node.clhandler)), - m_isman(*Assert(Assert(node.llmq_ctx)->isman)), + m_isman(*Assert(node.isman)), chainparams(chainstate.m_chainman.GetParams()), m_mempool(mempool), m_quorum_block_processor(*Assert(Assert(node.llmq_ctx)->quorum_block_processor)), diff --git a/src/rest.cpp b/src/rest.cpp index 90a8dc7bca6a..4cb2859b360b 100644 --- a/src/rest.cpp +++ b/src/rest.cpp @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -138,21 +137,21 @@ static ChainstateManager* GetChainman(const CoreContext& context, HTTPRequest* r * Get the node context LLMQContext. * * @param[in] req The HTTP request, whose status code will be set if node - * context LLMQContext is not found. - * @returns Pointer to the LLMQContext or nullptr if none found. + * context InstantSend manager is not found. + * @returns Pointer to the InstantSend manager or nullptr if none found. */ -static LLMQContext* GetLLMQContext(const CoreContext& context, HTTPRequest* req) +static llmq::CInstantSendManager* GetInstantSendManager(const CoreContext& context, HTTPRequest* req) { auto node_context = GetContext(context); - if (!node_context || !node_context->llmq_ctx) { + if (!node_context || !node_context->isman) { RESTERR(req, HTTP_INTERNAL_SERVER_ERROR, strprintf("%s:%d (%s)\n" - "Internal bug detected: LLMQ context not found!\n" + "Internal bug detected: InstantSend manager not found!\n" "You may report this issue here: %s\n", __FILE__, __LINE__, __func__, PACKAGE_BUGREPORT)); return nullptr; } - return node_context->llmq_ctx.get(); + return node_context->isman.get(); } RESTResponseFormat ParseDataFormat(std::string& param, const std::string& strReq) @@ -364,10 +363,10 @@ static bool rest_block(const CoreContext& context, const NodeContext* const node = GetNodeContext(context, req); if (!node || !node->chainlocks) return false; - const LLMQContext* llmq_ctx = GetLLMQContext(context, req); - if (!llmq_ctx) return false; + const llmq::CInstantSendManager* isman = GetInstantSendManager(context, req); + if (!isman) return false; - UniValue objBlock = blockToJSON(chainman.m_blockman, block, tip, pblockindex, *node->chainlocks, *llmq_ctx->isman, tx_verbosity); + UniValue objBlock = blockToJSON(chainman.m_blockman, block, tip, pblockindex, *node->chainlocks, *isman, tx_verbosity); std::string strJSON = objBlock.write() + "\n"; req->WriteHeader("Content-Type", "application/json"); req->WriteReply(HTTP_OK, strJSON); @@ -688,8 +687,8 @@ static bool rest_mempool(const CoreContext& context, HTTPRequest* req, const std switch (rf) { case RESTResponseFormat::JSON: { - const LLMQContext* llmq_ctx = GetLLMQContext(context, req); - if (!llmq_ctx) return false; + const llmq::CInstantSendManager* isman = GetInstantSendManager(context, req); + if (!isman) return false; std::string str_json; if (param == "contents") { @@ -716,9 +715,9 @@ static bool rest_mempool(const CoreContext& context, HTTPRequest* req, const std if (verbose && mempool_sequence) { return RESTERR(req, HTTP_BAD_REQUEST, "Verbose results cannot contain mempool sequence values. (hint: set \"verbose=false\")"); } - str_json = MempoolToJSON(*mempool, llmq_ctx->isman.get(), verbose, mempool_sequence).write() + "\n"; + str_json = MempoolToJSON(*mempool, isman, verbose, mempool_sequence).write() + "\n"; } else { - str_json = MempoolInfoToJSON(*mempool, *llmq_ctx->isman).write() + "\n"; + str_json = MempoolInfoToJSON(*mempool, *isman).write() + "\n"; } req->WriteHeader("Content-Type", "application/json"); diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index bb0be4e11df4..34f41736bbaf 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -57,7 +57,6 @@ #include #include #include -#include #include @@ -1061,7 +1060,7 @@ static RPCHelpMan getblock() return strHex; } - const LLMQContext& llmq_ctx = EnsureLLMQContext(node); + const llmq::CInstantSendManager& isman = EnsureInstantSendManager(node); CHECK_NONFATAL(node.chainlocks); TxVerbosity tx_verbosity; if (verbosity == 1) { @@ -1072,7 +1071,7 @@ static RPCHelpMan getblock() tx_verbosity = TxVerbosity::SHOW_DETAILS_AND_PREVOUT; } - return blockToJSON(chainman.m_blockman, block, tip, pblockindex, *node.chainlocks, *llmq_ctx.isman, tx_verbosity); + return blockToJSON(chainman.m_blockman, block, tip, pblockindex, *node.chainlocks, isman, tx_verbosity); }, }; } @@ -2414,7 +2413,7 @@ static RPCHelpMan getspecialtxes() LOCK(cs_main); const CTxMemPool& mempool = EnsureMemPool(node); - const LLMQContext& llmq_ctx = EnsureLLMQContext(node); + const llmq::CInstantSendManager& isman = EnsureInstantSendManager(node); CHECK_NONFATAL(node.chainlocks); const uint256 blockhash(ParseHashV(request.params[0], "blockhash")); @@ -2474,7 +2473,7 @@ static RPCHelpMan getspecialtxes() case 2 : { UniValue objTx(UniValue::VOBJ); - TxToJSON(*tx, blockhash, mempool, chainman.ActiveChainstate(), *node.chainlocks, *llmq_ctx.isman, node.spent_index.get(), objTx); + TxToJSON(*tx, blockhash, mempool, chainman.ActiveChainstate(), *node.chainlocks, isman, node.spent_index.get(), objTx); result.push_back(objTx); break; } diff --git a/src/rpc/mempool.cpp b/src/rpc/mempool.cpp index 5f8bca5db3ef..d433c04f13e7 100644 --- a/src/rpc/mempool.cpp +++ b/src/rpc/mempool.cpp @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -402,9 +401,9 @@ static RPCHelpMan getrawmempool() const NodeContext& node = EnsureAnyNodeContext(request.context); const CTxMemPool& mempool = EnsureMemPool(node); - const LLMQContext& llmq_ctx = EnsureLLMQContext(node); + const llmq::CInstantSendManager& isman = EnsureInstantSendManager(node); - return MempoolToJSON(mempool, llmq_ctx.isman.get(), fVerbose, include_mempool_sequence); + return MempoolToJSON(mempool, &isman, fVerbose, include_mempool_sequence); }, }; } @@ -461,12 +460,12 @@ static RPCHelpMan getmempoolancestors() return o; } else { UniValue o(UniValue::VOBJ); - const LLMQContext& llmq_ctx = EnsureLLMQContext(node); + const llmq::CInstantSendManager& isman = EnsureInstantSendManager(node); for (CTxMemPool::txiter ancestorIt : setAncestors) { const CTxMemPoolEntry &e = *ancestorIt; const uint256& _hash = e.GetTx().GetHash(); UniValue info(UniValue::VOBJ); - entryToJSON(mempool, info, e, llmq_ctx.isman.get()); + entryToJSON(mempool, info, e, &isman); o.pushKV(_hash.ToString(), info); } return o; @@ -529,12 +528,12 @@ static RPCHelpMan getmempooldescendants() return o; } else { UniValue o(UniValue::VOBJ); - const LLMQContext& llmq_ctx = EnsureLLMQContext(node); + const llmq::CInstantSendManager& isman = EnsureInstantSendManager(node); for (CTxMemPool::txiter descendantIt : setDescendants) { const CTxMemPoolEntry &e = *descendantIt; const uint256& _hash = e.GetTx().GetHash(); UniValue info(UniValue::VOBJ); - entryToJSON(mempool, info, e, llmq_ctx.isman.get()); + entryToJSON(mempool, info, e, &isman); o.pushKV(_hash.ToString(), info); } return o; @@ -573,8 +572,8 @@ static RPCHelpMan getmempoolentry() const CTxMemPoolEntry &e = *it; UniValue info(UniValue::VOBJ); - const LLMQContext& llmq_ctx = EnsureLLMQContext(node); - entryToJSON(mempool, info, e, llmq_ctx.isman.get()); + const llmq::CInstantSendManager& isman = EnsureInstantSendManager(node); + entryToJSON(mempool, info, e, &isman); return info; }, }; @@ -707,8 +706,8 @@ static RPCHelpMan getmempoolinfo() { const NodeContext& node = EnsureAnyNodeContext(request.context); const CTxMemPool& mempool = EnsureMemPool(node); - const LLMQContext& llmq_ctx = EnsureLLMQContext(node); - return MempoolInfoToJSON(mempool, *llmq_ctx.isman); + const llmq::CInstantSendManager& isman = EnsureInstantSendManager(node); + return MempoolInfoToJSON(mempool, isman); }, }; } diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 4a003a8d6593..9d55adcc00cb 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -57,7 +57,6 @@ #include #include #include -#include #include #include @@ -434,13 +433,13 @@ static RPCHelpMan getrawtransaction() return EncodeHexTx(*tx); } - const LLMQContext& llmq_ctx = EnsureLLMQContext(node); + const llmq::CInstantSendManager& isman = EnsureInstantSendManager(node); const CTxMemPool& mempool = EnsureMemPool(node); CHECK_NONFATAL(node.chainlocks); UniValue result(UniValue::VOBJ); if (blockindex) result.pushKV("in_active_chain", in_active_chain); - TxToJSON(*tx, hash_block, mempool, chainman.ActiveChainstate(), *node.chainlocks, *llmq_ctx.isman, node.spent_index.get(), result); + TxToJSON(*tx, hash_block, mempool, chainman.ActiveChainstate(), *node.chainlocks, isman, node.spent_index.get(), result); return result; }, }; @@ -495,7 +494,7 @@ static RPCHelpMan getrawtransactionmulti() { const NodeContext& node{EnsureAnyNodeContext(request.context)}; const ChainstateManager& chainman{EnsureChainman(node)}; - const LLMQContext& llmq_ctx{EnsureLLMQContext(node)}; + const llmq::CInstantSendManager& isman{EnsureInstantSendManager(node)}; CHECK_NONFATAL(node.chainlocks); CTxMemPool& mempool{EnsureMemPool(node)}; @@ -531,7 +530,7 @@ static RPCHelpMan getrawtransactionmulti() { result.pushKV(txid_str, "None"); } else if (fVerbose) { UniValue tx_data{UniValue::VOBJ}; - TxToJSON(*tx, hash_block, mempool, chainman.ActiveChainstate(), *node.chainlocks, *llmq_ctx.isman, node.spent_index.get(), tx_data); + TxToJSON(*tx, hash_block, mempool, chainman.ActiveChainstate(), *node.chainlocks, isman, node.spent_index.get(), tx_data); result.pushKV(txid_str, tx_data); } else { result.pushKV(txid_str, EncodeHexTx(*tx)); @@ -591,11 +590,11 @@ static RPCHelpMan getislocks() throw JSONRPCError(RPC_INVALID_PARAMETER, "Up to 100 txids only"); } - const LLMQContext& llmq_ctx = EnsureLLMQContext(node); + const llmq::CInstantSendManager& isman = EnsureInstantSendManager(node); for (const auto idx : util::irange(txids.size())) { const uint256 txid(ParseHashV(txids[idx], "txid")); - if (const instantsend::InstantSendLockPtr islock = llmq_ctx.isman->GetInstantSendLockByTxid(txid); islock != nullptr) { + if (const instantsend::InstantSendLockPtr islock = isman.GetInstantSendLockByTxid(txid); islock != nullptr) { UniValue objIS(UniValue::VOBJ); objIS.pushKV("txid", islock->txid.ToString()); UniValue inputs(UniValue::VARR); diff --git a/src/rpc/server_util.cpp b/src/rpc/server_util.cpp index 60d24077736e..8c041ee20993 100644 --- a/src/rpc/server_util.cpp +++ b/src/rpc/server_util.cpp @@ -123,6 +123,14 @@ LLMQContext& EnsureAnyLLMQContext(const CoreContext& context) return EnsureLLMQContext(EnsureAnyNodeContext(context)); } +llmq::CInstantSendManager& EnsureInstantSendManager(const NodeContext& node) +{ + if (!node.isman) { + throw JSONRPCError(RPC_INTERNAL_ERROR, "Node InstantSend manager not found"); + } + return *node.isman; +} + CConnman& EnsureConnman(const NodeContext& node) { if (!node.connman) { diff --git a/src/rpc/server_util.h b/src/rpc/server_util.h index 81e330817f7c..4b50bc8dde3a 100644 --- a/src/rpc/server_util.h +++ b/src/rpc/server_util.h @@ -15,6 +15,9 @@ class ChainstateManager; class PeerManager; class BanMan; struct LLMQContext; +namespace llmq { +class CInstantSendManager; +} // namespace llmq namespace node { struct NodeContext; } // namespace node @@ -32,6 +35,7 @@ CBlockPolicyEstimator& EnsureFeeEstimator(const node::NodeContext& node); CBlockPolicyEstimator& EnsureAnyFeeEstimator(const CoreContext& context); LLMQContext& EnsureLLMQContext(const node::NodeContext& node); LLMQContext& EnsureAnyLLMQContext(const CoreContext& context); +llmq::CInstantSendManager& EnsureInstantSendManager(const node::NodeContext& node); CConnman& EnsureConnman(const node::NodeContext& node); PeerManager& EnsurePeerman(const node::NodeContext& node); diff --git a/src/test/coinjoin_inouts_tests.cpp b/src/test/coinjoin_inouts_tests.cpp index 0753a9f56d88..ac6c2c233569 100644 --- a/src/test/coinjoin_inouts_tests.cpp +++ b/src/test/coinjoin_inouts_tests.cpp @@ -219,7 +219,7 @@ BOOST_AUTO_TEST_CASE(server_signfinaltx_nonparticipant_cannot_abort_session) TestableCoinJoinServer server(m_node.peerman.get(), *Assert(m_node.chainman), *Assert(m_node.connman), *Assert(m_node.dmnman), *Assert(m_node.dstxman), *Assert(m_node.mn_metaman), *Assert(m_node.mempool), mn_activeman, *Assert(m_node.mn_sync), - *Assert(m_node.llmq_ctx->isman)); + *Assert(m_node.isman)); // Seed an active signing session with one participant. That participant's // addr is deliberately not registered with connman -- a session-wide @@ -257,7 +257,7 @@ BOOST_AUTO_TEST_CASE(server_signfinaltx_participant_oversized_count_is_rejected_ TestableCoinJoinServer server(m_node.peerman.get(), *Assert(m_node.chainman), *Assert(m_node.connman), *Assert(m_node.dmnman), *Assert(m_node.dstxman), *Assert(m_node.mn_metaman), *Assert(m_node.mempool), mn_activeman, *Assert(m_node.mn_sync), - *Assert(m_node.llmq_ctx->isman)); + *Assert(m_node.isman)); // Same setup, but this time the oversized DSSIGNFINALTX comes from the // session participant itself. It must still be rejected without materializing diff --git a/src/test/evo_deterministicmns_tests.cpp b/src/test/evo_deterministicmns_tests.cpp index 2e4d117ab182..bdc4b4134a0d 100644 --- a/src/test/evo_deterministicmns_tests.cpp +++ b/src/test/evo_deterministicmns_tests.cpp @@ -1286,7 +1286,7 @@ void FuncTestMempoolReorg(TestChainSetup& setup) CTxMemPool testPool{MemPoolOptionsForTest(setup.m_node)}; if (setup.m_node.dmnman) { - testPool.ConnectManagers(setup.m_node.dmnman.get(), setup.m_node.llmq_ctx->isman.get()); + testPool.ConnectManagers(setup.m_node.dmnman.get(), setup.m_node.isman.get()); } TestMemPoolEntryHelper entry; LOCK2(cs_main, testPool.cs); @@ -1373,7 +1373,7 @@ void FuncTestMempoolProTxKeyChangedConflictChain(TestChainSetup& setup) CTxMemPool testPool{MemPoolOptionsForTest(setup.m_node)}; BOOST_REQUIRE(setup.m_node.dmnman); - testPool.ConnectManagers(setup.m_node.dmnman.get(), setup.m_node.llmq_ctx->isman.get()); + testPool.ConnectManagers(setup.m_node.dmnman.get(), setup.m_node.isman.get()); TestMemPoolEntryHelper entry; LOCK2(cs_main, testPool.cs); @@ -1416,7 +1416,7 @@ void FuncTestMempoolDualProregtx(TestChainSetup& setup) CTxMemPool testPool{MemPoolOptionsForTest(setup.m_node)}; if (setup.m_node.dmnman) { - testPool.ConnectManagers(setup.m_node.dmnman.get(), setup.m_node.llmq_ctx->isman.get()); + testPool.ConnectManagers(setup.m_node.dmnman.get(), setup.m_node.isman.get()); } TestMemPoolEntryHelper entry; LOCK2(cs_main, testPool.cs); diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index db2ef0df95a8..a346513a365b 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -70,6 +70,7 @@ #include #include #include +#include #include #include #include @@ -222,6 +223,7 @@ BasicTestingSetup::BasicTestingSetup(const std::string& chainName, const std::ve m_node.chainlocks = std::make_unique(*m_node.sporkman); m_node.evodb = std::make_unique(util::DbWrapperParams{.path = m_node.args->GetDataDirNet(), .memory = m_dash_dbs_in_memory, .wipe = true}); m_node.dmnman = std::make_unique(*m_node.evodb, *m_node.mn_metaman); + m_node.isman = std::make_unique(*m_node.sporkman, util::DbWrapperParams{.path = m_node.args->GetDataDirNet(), .memory = m_dash_dbs_in_memory, .wipe = true}); static bool noui_connected = false; if (!noui_connected) { @@ -236,7 +238,8 @@ BasicTestingSetup::~BasicTestingSetup() { SetMockTime(0s); // Reset mocktime for following tests LogInstance().DisconnectTestLogger(); - // Close disk-backed EvoDB before deleting its data directory. + // Close the disk-backed databases before deleting their data directory. + m_node.isman.reset(); m_node.dmnman.reset(); m_node.evodb.reset(); fs::remove_all(m_path_root); @@ -302,7 +305,7 @@ node::ChainstateLoadOptions ChainTestingSetup::ChainstateLoadOptionsForTest() { node::ChainstateLoadOptions options; options.mempool = Assert(m_node.mempool.get()); - options.sporkman = Assert(m_node.sporkman.get()); + options.isman = Assert(m_node.isman.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(); @@ -328,10 +331,12 @@ void ChainTestingSetup::LoadVerifyActivateChainstate() if (options.reindex || options.reindex_chainstate) { // A reindex wipes the Dash databases at open, which AppInitMain does by // recreating them. Mirror that here. + m_node.isman.reset(); m_node.dmnman.reset(); m_node.evodb.reset(); m_node.evodb = std::make_unique(util::DbWrapperParams{.path = m_node.args->GetDataDirNet(), .memory = m_dash_dbs_in_memory, .wipe = true}); m_node.dmnman = std::make_unique(*m_node.evodb, *m_node.mn_metaman); + m_node.isman = std::make_unique(*m_node.sporkman, util::DbWrapperParams{.path = m_node.args->GetDataDirNet(), .memory = m_dash_dbs_in_memory, .wipe = true}); options = ChainstateLoadOptionsForTest(); } @@ -369,7 +374,7 @@ TestingSetup::TestingSetup( #ifdef ENABLE_WALLET // The test suite doesn't use masternode mode, so we may initialize it m_node.cj_walletman = CJWalletManager::make(*m_node.chainman, *m_node.dmnman, *m_node.mn_metaman, *m_node.mempool, - *m_node.mn_sync, *m_node.llmq_ctx->isman, /*relay_txes=*/true); + *m_node.mn_sync, *m_node.isman, /*relay_txes=*/true); assert(m_node.cj_walletman); // WalletInit::Construct()-like logic needed for wallet tests that run on diff --git a/src/test/validation_chainstatemanager_tests.cpp b/src/test/validation_chainstatemanager_tests.cpp index f02f3a98d00d..87955e1d7fce 100644 --- a/src/test/validation_chainstatemanager_tests.cpp +++ b/src/test/validation_chainstatemanager_tests.cpp @@ -58,16 +58,16 @@ static void DashChainstateSetup(ChainstateManager& chainman, bool llmq_dbs_wipe) { node.llmq_ctx.reset(); - node.llmq_ctx = std::make_unique(*node.dmnman, *node.evodb, *Assert(node.sporkman.get()), chainman, + node.llmq_ctx = std::make_unique(*node.dmnman, *node.evodb, *Assert(node.isman.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()); + node.mempool->ConnectManagers(node.dmnman.get(), node.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.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)); } From e5cee0440248239110de47ed17e0c421fd97615c Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Thu, 13 Aug 2026 03:28:18 +0700 Subject: [PATCH 03/12] refactor: pass dmnman and isman to CTxMemPool at construction ConnectManagers/DisconnectManagers existed only because the mempool was constructed before isman / dmnman. Now that dmnman and isman are alive before the mempool and ConnectManagers could be just removed. --- src/init.cpp | 11 ++++--- src/kernel/mempool_options.h | 9 +++++ src/node/chainstate.cpp | 11 +------ src/test/evo_deterministicmns_tests.cpp | 8 ----- src/test/util/setup_common.cpp | 11 ++++--- src/test/util/txmempool.cpp | 2 ++ .../validation_chainstatemanager_tests.cpp | 8 ----- src/txmempool.cpp | 33 ++++++------------- src/txmempool.h | 28 ++++------------ 9 files changed, 40 insertions(+), 81 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 472c5d31188d..ec33682e7bab 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -429,11 +429,9 @@ void PrepareShutdown(NodeContext& node) chainstate->ResetCoinsViews(); } } - // The mempool holds raw pointers to dmnman and isman, so it has to - // let go of them before either manager is destroyed. - if (node.mempool) { - node.mempool->DisconnectManagers(); - } + // The mempool holds raw pointers to dmnman and isman, so it must be + // destroyed before either manager. + node.mempool.reset(); node.chain_helper.reset(); node.llmq_ctx.reset(); node.isman.reset(); @@ -1952,6 +1950,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) for (bool fLoaded = false; !fLoaded && !ShutdownRequested();) { // On a retry iteration the previous instances still hold the on-disk // database locks, so release them before opening the databases again. + node.mempool.reset(); node.isman.reset(); node.dmnman.reset(); node.evodb.reset(); @@ -1959,6 +1958,8 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) node.dmnman = std::make_unique(*node.evodb, *node.mn_metaman); node.isman = std::make_unique(*node.sporkman, util::DbWrapperParams{.path = args.GetDataDirNet(), .memory = false, .wipe = node::fReindex || fReindexChainState}); + mempool_opts.dmnman = node.dmnman.get(); + mempool_opts.isman = node.isman.get(); node.mempool = std::make_unique(mempool_opts); const ChainstateManager::Options chainman_opts{ diff --git a/src/kernel/mempool_options.h b/src/kernel/mempool_options.h index fe2fb0543ba3..11b731bd5459 100644 --- a/src/kernel/mempool_options.h +++ b/src/kernel/mempool_options.h @@ -15,6 +15,11 @@ #include class CBlockPolicyEstimator; +class CDeterministicMNManager; + +namespace llmq { +class CInstantSendManager; +} // namespace llmq /** Default for -maxmempool, maximum megabytes of mempool memory usage */ static constexpr unsigned int DEFAULT_MAX_MEMPOOL_SIZE_MB{300}; @@ -32,6 +37,10 @@ namespace kernel { struct MemPoolOptions { /* Used to estimate appropriate transaction fees. */ CBlockPolicyEstimator* estimator{nullptr}; + /* Used to validate special transactions; may be unset in tests that do not need them. */ + CDeterministicMNManager* dmnman{nullptr}; + /* Used to protect InstantSend-locked transactions; may be unset in tests that do not need them. */ + llmq::CInstantSendManager* isman{nullptr}; /* The ratio used to determine how often sanity checks will run. */ int check_ratio{0}; int64_t max_size_bytes{DEFAULT_MAX_MEMPOOL_SIZE_MB * 1'000'000}; diff --git a/src/node/chainstate.cpp b/src/node/chainstate.cpp index f5a2eb393291..47b17f947edb 100644 --- a/src/node/chainstate.cpp +++ b/src/node/chainstate.cpp @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -152,14 +151,11 @@ static ChainstateLoadResult CompleteChainstateInitialization(ChainstateManager& pblocktree.reset(); pblocktree.reset(new CBlockTreeDB(cache_sizes.block_tree_db, options.block_tree_db_in_memory, options.reindex)); - // Initialize llmq_ctx and connection to mempool + // Initialize llmq_ctx llmq_ctx.reset(); llmq_ctx = std::make_unique(dmnman, evodb, *options.isman, 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, options.isman); - } // Initialize chain_helper chain_helper.reset(); @@ -370,11 +366,6 @@ ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSize // Do nothing; expected case. } else if (snapshot_completion == SnapshotCompletionResult::SUCCESS) { LogPrintf("[snapshot] cleaning up unneeded background chainstate, then reinitializing\n"); - // ConnectManagers() in the reinitialization below forbids - // double-initialization, so detach the mempool's managers first. - if (options.mempool) { - options.mempool->DisconnectManagers(); - } chain_helper.reset(); llmq_ctx.reset(); if (!chainman.ValidatedSnapshotCleanup()) { diff --git a/src/test/evo_deterministicmns_tests.cpp b/src/test/evo_deterministicmns_tests.cpp index bdc4b4134a0d..6e9339bd0c7a 100644 --- a/src/test/evo_deterministicmns_tests.cpp +++ b/src/test/evo_deterministicmns_tests.cpp @@ -1285,9 +1285,6 @@ void FuncTestMempoolReorg(TestChainSetup& setup) auto tx_reg = CreateProRegTxExternalCollateral(chainman, utxos, 1, collateralOutpoint, scriptPayout, ownerKey, operatorKey, collateralKey, setup.coinbaseKey); CTxMemPool testPool{MemPoolOptionsForTest(setup.m_node)}; - if (setup.m_node.dmnman) { - testPool.ConnectManagers(setup.m_node.dmnman.get(), setup.m_node.isman.get()); - } TestMemPoolEntryHelper entry; LOCK2(cs_main, testPool.cs); @@ -1372,8 +1369,6 @@ void FuncTestMempoolProTxKeyChangedConflictChain(TestChainSetup& setup) auto tx_revoke = CreateProUpRevTx(chainman, utxos, proTxHash, operatorKey, setup.coinbaseKey); CTxMemPool testPool{MemPoolOptionsForTest(setup.m_node)}; - BOOST_REQUIRE(setup.m_node.dmnman); - testPool.ConnectManagers(setup.m_node.dmnman.get(), setup.m_node.isman.get()); TestMemPoolEntryHelper entry; LOCK2(cs_main, testPool.cs); @@ -1415,9 +1410,6 @@ void FuncTestMempoolDualProregtx(TestChainSetup& setup) auto tx_reg2 = CreateProRegTxExternalCollateral(chainman, utxos, 2, collateralOutpoint, scriptPayout, ownerKey, operatorKey, collateralKey, setup.coinbaseKey); CTxMemPool testPool{MemPoolOptionsForTest(setup.m_node)}; - if (setup.m_node.dmnman) { - testPool.ConnectManagers(setup.m_node.dmnman.get(), setup.m_node.isman.get()); - } TestMemPoolEntryHelper entry; LOCK2(cs_main, testPool.cs); diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index a346513a365b..0b951ad3a0f6 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -330,13 +330,18 @@ void ChainTestingSetup::LoadVerifyActivateChainstate() if (options.reindex || options.reindex_chainstate) { // A reindex wipes the Dash databases at open, which AppInitMain does by - // recreating them. Mirror that here. + // recreating them together with the mempool bound to them. Mirror that + // here, including the chainlock handler that references the mempool. + m_node.clhandler.reset(); + m_node.mempool.reset(); m_node.isman.reset(); m_node.dmnman.reset(); m_node.evodb.reset(); m_node.evodb = std::make_unique(util::DbWrapperParams{.path = m_node.args->GetDataDirNet(), .memory = m_dash_dbs_in_memory, .wipe = true}); m_node.dmnman = std::make_unique(*m_node.evodb, *m_node.mn_metaman); m_node.isman = std::make_unique(*m_node.sporkman, util::DbWrapperParams{.path = m_node.args->GetDataDirNet(), .memory = m_dash_dbs_in_memory, .wipe = true}); + m_node.mempool = std::make_unique(MemPoolOptionsForTest(m_node)); + m_node.clhandler = std::make_unique(*m_node.chainlocks, chainman, *m_node.mempool, *m_node.mn_sync); options = ChainstateLoadOptionsForTest(); } @@ -426,12 +431,8 @@ TestingSetup::~TestingSetup() // in init.cpp). Keep this defensive for fixtures that construct govman. m_node.govman.reset(); - if (m_node.mempool) { - m_node.mempool->DisconnectManagers(); - } m_node.chain_helper.reset(); m_node.llmq_ctx.reset(); - m_node.dmnman.reset(); } TestChain100Setup::TestChain100Setup( diff --git a/src/test/util/txmempool.cpp b/src/test/util/txmempool.cpp index ce3d85e83d57..23aff3f2e2d4 100644 --- a/src/test/util/txmempool.cpp +++ b/src/test/util/txmempool.cpp @@ -18,6 +18,8 @@ CTxMemPool::Options MemPoolOptionsForTest(const NodeContext& node) { CTxMemPool::Options mempool_opts{ .estimator = node.fee_estimator.get(), + .dmnman = node.dmnman.get(), + .isman = node.isman.get(), // Default to always checking mempool regardless of // chainparams.DefaultConsistencyChecks for tests .check_ratio = 1, diff --git a/src/test/validation_chainstatemanager_tests.cpp b/src/test/validation_chainstatemanager_tests.cpp index 87955e1d7fce..5531b7116091 100644 --- a/src/test/validation_chainstatemanager_tests.cpp +++ b/src/test/validation_chainstatemanager_tests.cpp @@ -61,10 +61,6 @@ static void DashChainstateSetup(ChainstateManager& chainman, node.llmq_ctx = std::make_unique(*node.dmnman, *node.evodb, *Assert(node.isman.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.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), @@ -74,9 +70,6 @@ static void DashChainstateSetup(ChainstateManager& chainman, static void DashChainstateSetupClose(node::NodeContext& node) { - if (node.mempool) { - node.mempool->DisconnectManagers(); - } node.chain_helper.reset(); node.llmq_ctx.reset(); } @@ -89,7 +82,6 @@ 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()); diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 58c03b3baa6f..90a0d6d07d2b 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -423,6 +423,8 @@ void CTxMemPoolEntry::UpdateAncestorState(int64_t modifySize, CAmount modifyFee, CTxMemPool::CTxMemPool(const Options& opts) : m_check_ratio{opts.check_ratio}, minerPolicyEstimator{opts.estimator}, + m_dmnman{opts.dmnman}, + m_isman{opts.isman}, m_max_size_bytes{opts.max_size_bytes}, m_expiry{opts.expiry}, m_incremental_relay_feerate{opts.incremental_relay_feerate}, @@ -438,21 +440,6 @@ CTxMemPool::CTxMemPool(const Options& opts) _clear(); //lock free clear } -void CTxMemPool::ConnectManagers(gsl::not_null dmnman, gsl::not_null isman) -{ - // Do not allow double-initialization - assert(m_dmnman.load(std::memory_order_acquire) == nullptr); - m_dmnman.store(dmnman, std::memory_order_release); - assert(m_isman.load(std::memory_order_acquire) == nullptr); - m_isman.store(isman, std::memory_order_release); -} - -void CTxMemPool::DisconnectManagers() -{ - m_dmnman.store(nullptr, std::memory_order_release); - m_isman.store(nullptr, std::memory_order_release); -} - bool CTxMemPool::isSpent(const COutPoint& outpoint) const { LOCK(cs); @@ -523,8 +510,8 @@ void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry, setEntries &setAnces // Invalid ProTxes should never get this far because transactions should be // fully checked by AcceptToMemoryPool() at this point, so we just assume that // everything is fine here. - if (auto dmnman = m_dmnman.load(std::memory_order_acquire); dmnman) { - addUncheckedProTx(*dmnman, newit, tx); + if (m_dmnman) { + addUncheckedProTx(*m_dmnman, newit, tx); } } @@ -736,7 +723,7 @@ void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason) } else vTxHashes.clear(); - if (m_dmnman.load(std::memory_order_acquire)) { + if (m_dmnman) { removeUncheckedProTx(it->GetTx()); } @@ -957,7 +944,7 @@ void CTxMemPool::removeProTxCollateralConflicts(const CTransaction &tx, const CO void CTxMemPool::removeProTxSpentCollateralConflicts(const CTransaction &tx) { - auto dmnman = Assert(m_dmnman.load(std::memory_order_acquire)); + auto dmnman = Assert(m_dmnman); // Remove TXs that refer to a MN for which the collateral was spent auto removeSpentCollateralConflict = [&](const uint256& proTxHash) EXCLUSIVE_LOCKS_REQUIRED(cs) { @@ -1114,7 +1101,7 @@ void CTxMemPool::removeForBlock(const std::vector& vtx, unsigne RemoveStaged(stage, true, MemPoolRemovalReason::BLOCK); } removeConflicts(*tx); - if (m_dmnman.load(std::memory_order_acquire)) { + if (m_dmnman) { removeProTxConflicts(*tx); } ClearPrioritisation(tx->GetHash()); @@ -1418,7 +1405,7 @@ bool CTxMemPool::existsProviderTxCrossSchemeConflict(const CTransaction& tx) con // encoding the masternode already holds in the list fails CheckSpecialTx // before reaching the mempool. Probing here would wrongly block updates // for one member of a pre-activation cross-scheme pair. - auto dmnman = Assert(m_dmnman.load(std::memory_order_acquire)); + auto dmnman = Assert(m_dmnman); if (auto dmn = dmnman->GetListAtChainTip().GetMN(opt_proTx->proTxHash); dmn && opt_proTx->pubKeyOperator == dmn->pdmnState->pubKeyOperator) { return false; @@ -1429,7 +1416,7 @@ bool CTxMemPool::existsProviderTxCrossSchemeConflict(const CTransaction& tx) con } bool CTxMemPool::existsProviderTxConflict(const CTransaction &tx) const { - auto dmnman = Assert(m_dmnman.load(std::memory_order_acquire)); + auto dmnman = Assert(m_dmnman); LOCK(cs); @@ -1675,7 +1662,7 @@ void CTxMemPool::RemoveStaged(setEntries &stage, bool updateDescendants, MemPool int CTxMemPool::Expire(std::chrono::seconds time) { AssertLockHeld(cs); - auto isman = Assert(m_isman.load(std::memory_order_acquire)); + auto isman = Assert(m_isman); indexed_transaction_set::index::type::iterator it = mapTx.get().begin(); setEntries toremove; while (it != mapTx.get().end() && it->GetTime() < time) { diff --git a/src/txmempool.h b/src/txmempool.h index 564aa5c0ef15..c2d9789dd214 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -448,8 +447,8 @@ class CTxMemPool const int m_check_ratio; //!< Value n means that 1 times in n we check. std::atomic nTransactionsUpdated{0}; //!< Used by getblocktemplate to trigger CreateNewBlock() invocation CBlockPolicyEstimator* const minerPolicyEstimator; - std::atomic m_dmnman{nullptr}; - std::atomic m_isman{nullptr}; + CDeterministicMNManager* const m_dmnman; + llmq::CInstantSendManager* const m_isman; uint64_t totalTxSize GUARDED_BY(cs); //!< sum of all mempool tx' byte sizes CAmount m_total_fee GUARDED_BY(cs); //!< sum of all mempool tx's fees (NOT modified fee) @@ -610,21 +609,6 @@ class CTxMemPool */ explicit CTxMemPool(const Options& opts); - /** - * Set CDeterministicMNManager and CInstantSendManager pointers. - * - * Separated from constructor as it's initialized after CTxMemPool - * is created. Required for ProTx processing. - */ - void ConnectManagers(gsl::not_null dmnman, gsl::not_null isman); - - /** - * Reset CDeterministicMNManager and CInstantSendManager pointers. - * - * @pre Must be called before CDeterministicMNManager and CInstantSendManager are destroyed. - */ - void DisconnectManagers(); - /** * If sanity-checking is turned on, check makes sure the pool is * consistent (does not contain two transactions that spend the same inputs, @@ -782,8 +766,8 @@ class CTxMemPool void TrimToSize(size_t sizelimit, std::vector* pvNoSpendsRemaining = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs); /** Expire all transaction (and their dependencies) in the mempool older than time. Return the number of removed transactions. - * @pre Caller must ensure that CInstantSendManager exists and has been set using - * ConnectManagers() for InstantSend awareness + * @pre Requires a CInstantSendManager passed via Options at construction + * for InstantSend awareness */ int Expire(std::chrono::seconds time) EXCLUSIVE_LOCKS_REQUIRED(cs); @@ -836,8 +820,8 @@ class CTxMemPool std::vector infoAll() const; /** - * @pre Caller must ensure that CDeterministicMNManager exists and has been - * set using ConnectManagers() for the CTxMemPool instance. + * @pre Requires a CDeterministicMNManager passed via Options at + * construction of the CTxMemPool instance. */ bool existsProviderTxConflict(const CTransaction &tx) const; From 213c60ad8f312ecafbcc708f0c79f83758a603c9 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Thu, 13 Aug 2026 14:04:43 +0700 Subject: [PATCH 04/12] chore: add todo to make isman optional for bitcoin chainstate --- src/bitcoin-chainstate.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bitcoin-chainstate.cpp b/src/bitcoin-chainstate.cpp index ff6750339987..1ebd86bae1c5 100644 --- a/src/bitcoin-chainstate.cpp +++ b/src/bitcoin-chainstate.cpp @@ -99,6 +99,7 @@ int main(int argc, char* argv[]) CMasternodeSync mn_sync{std::make_unique()}; CSporkManager sporkman; chainlock::Chainlocks chainlocks(sporkman); + // TODO: remove isman from bitcoin-chainstate and make it nullable for node::ChainstateLoadOptions same as mempool llmq::CInstantSendManager isman{sporkman, util::DbWrapperParams{.path = gArgs.GetDataDirNet(), .memory = false, .wipe = false}}; std::unique_ptr llmq_ctx; From 0447697e623c0cbc85854855440cbf30fc1b32ae Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Thu, 13 Aug 2026 04:23:20 +0700 Subject: [PATCH 05/12] refactor: make CTxMemPool manager members references Every CTxMemPool construction now provides dmnman and isman (init's retry loop builds them first; MemPoolOptionsForTest fills them from the fixture, which always creates both), so the null checks and per-use Asserts inherited from the ConnectManagers era guard a state that can no longer occur. Turn the members into references, asserted once at construction, and drop the dead branches along with the @pre comments and the redundant dmnman parameter of addUncheckedProTx. The Options fields stay pointers with a nullptr default because the options struct is an aggregate initialized field-by-field; the requirement is therefore enforced by the constructor assert rather than the type, and a future construction site that forgets the managers fails loudly on startup instead of silently losing ProTx/InstantSend handling. --- src/kernel/mempool_options.h | 4 ++-- src/txmempool.cpp | 39 ++++++++++++------------------------ src/txmempool.h | 16 ++++----------- 3 files changed, 19 insertions(+), 40 deletions(-) diff --git a/src/kernel/mempool_options.h b/src/kernel/mempool_options.h index 11b731bd5459..93384bb8e7c1 100644 --- a/src/kernel/mempool_options.h +++ b/src/kernel/mempool_options.h @@ -37,9 +37,9 @@ namespace kernel { struct MemPoolOptions { /* Used to estimate appropriate transaction fees. */ CBlockPolicyEstimator* estimator{nullptr}; - /* Used to validate special transactions; may be unset in tests that do not need them. */ + /* Used to validate special transactions; required, must outlive the mempool. */ CDeterministicMNManager* dmnman{nullptr}; - /* Used to protect InstantSend-locked transactions; may be unset in tests that do not need them. */ + /* Used to protect InstantSend-locked transactions; required, must outlive the mempool. */ llmq::CInstantSendManager* isman{nullptr}; /* The ratio used to determine how often sanity checks will run. */ int check_ratio{0}; diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 90a0d6d07d2b..001d4de0500a 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -423,8 +423,8 @@ void CTxMemPoolEntry::UpdateAncestorState(int64_t modifySize, CAmount modifyFee, CTxMemPool::CTxMemPool(const Options& opts) : m_check_ratio{opts.check_ratio}, minerPolicyEstimator{opts.estimator}, - m_dmnman{opts.dmnman}, - m_isman{opts.isman}, + m_dmnman{*Assert(opts.dmnman)}, + m_isman{*Assert(opts.isman)}, m_max_size_bytes{opts.max_size_bytes}, m_expiry{opts.expiry}, m_incremental_relay_feerate{opts.incremental_relay_feerate}, @@ -510,9 +510,7 @@ void CTxMemPool::addUnchecked(const CTxMemPoolEntry &entry, setEntries &setAnces // Invalid ProTxes should never get this far because transactions should be // fully checked by AcceptToMemoryPool() at this point, so we just assume that // everything is fine here. - if (m_dmnman) { - addUncheckedProTx(*m_dmnman, newit, tx); - } + addUncheckedProTx(newit, tx); } void CTxMemPool::addAddressIndex(const CTxMemPoolEntry& entry, const CCoinsViewCache& view) @@ -638,8 +636,7 @@ void CTxMemPool::removeSpentIndex(const uint256 txhash) } } -void CTxMemPool::addUncheckedProTx(CDeterministicMNManager& dmnman, indexed_transaction_set::iterator& newit, - const CTransaction& tx) +void CTxMemPool::addUncheckedProTx(indexed_transaction_set::iterator& newit, const CTransaction& tx) { const uint256 tx_hash{tx.GetHash()}; if (tx.nType == TRANSACTION_PROVIDER_REGISTER) { @@ -673,7 +670,7 @@ void CTxMemPool::addUncheckedProTx(CDeterministicMNManager& dmnman, indexed_tran auto proTx = *Assert(GetTxPayload(tx)); mapProTxRefs.emplace(proTx.proTxHash, tx_hash); mapProTxBlsPubKeyHashes.emplace(proTx.pubKeyOperator.GetHash(), tx_hash); - auto dmn = Assert(dmnman.GetListAtChainTip().GetMN(proTx.proTxHash)); + auto dmn = Assert(m_dmnman.GetListAtChainTip().GetMN(proTx.proTxHash)); newit->validForProTxKey = ::SerializeHash(dmn->pdmnState->pubKeyOperator); if (dmn->pdmnState->pubKeyOperator != proTx.pubKeyOperator) { newit->isKeyChangeProTx = true; @@ -681,7 +678,7 @@ void CTxMemPool::addUncheckedProTx(CDeterministicMNManager& dmnman, indexed_tran } else if (tx.nType == TRANSACTION_PROVIDER_UPDATE_REVOKE) { auto proTx = *Assert(GetTxPayload(tx)); mapProTxRefs.emplace(proTx.proTxHash, tx_hash); - auto dmn = Assert(dmnman.GetListAtChainTip().GetMN(proTx.proTxHash)); + auto dmn = Assert(m_dmnman.GetListAtChainTip().GetMN(proTx.proTxHash)); newit->validForProTxKey = ::SerializeHash(dmn->pdmnState->pubKeyOperator); if (dmn->pdmnState->pubKeyOperator.Get() != CBLSPublicKey()) { newit->isKeyChangeProTx = true; @@ -723,9 +720,7 @@ void CTxMemPool::removeUnchecked(txiter it, MemPoolRemovalReason reason) } else vTxHashes.clear(); - if (m_dmnman) { - removeUncheckedProTx(it->GetTx()); - } + removeUncheckedProTx(it->GetTx()); totalTxSize -= it->GetTxSize(); m_total_fee -= it->GetFee(); @@ -944,8 +939,6 @@ void CTxMemPool::removeProTxCollateralConflicts(const CTransaction &tx, const CO void CTxMemPool::removeProTxSpentCollateralConflicts(const CTransaction &tx) { - auto dmnman = Assert(m_dmnman); - // Remove TXs that refer to a MN for which the collateral was spent auto removeSpentCollateralConflict = [&](const uint256& proTxHash) EXCLUSIVE_LOCKS_REQUIRED(cs) { // Can't use equal_range here as every call to removeRecursive might invalidate iterators @@ -966,7 +959,7 @@ void CTxMemPool::removeProTxSpentCollateralConflicts(const CTransaction &tx) } } }; - auto mnList = dmnman->GetListAtChainTip(); + auto mnList = m_dmnman.GetListAtChainTip(); for (const auto& in : tx.vin) { auto collateralIt = mapProTxCollaterals.find(in.prevout); if (collateralIt != mapProTxCollaterals.end()) { @@ -1101,9 +1094,7 @@ void CTxMemPool::removeForBlock(const std::vector& vtx, unsigne RemoveStaged(stage, true, MemPoolRemovalReason::BLOCK); } removeConflicts(*tx); - if (m_dmnman) { - removeProTxConflicts(*tx); - } + removeProTxConflicts(*tx); ClearPrioritisation(tx->GetHash()); } lastRollingFeeUpdate = GetTime(); @@ -1405,8 +1396,7 @@ bool CTxMemPool::existsProviderTxCrossSchemeConflict(const CTransaction& tx) con // encoding the masternode already holds in the list fails CheckSpecialTx // before reaching the mempool. Probing here would wrongly block updates // for one member of a pre-activation cross-scheme pair. - auto dmnman = Assert(m_dmnman); - if (auto dmn = dmnman->GetListAtChainTip().GetMN(opt_proTx->proTxHash); + if (auto dmn = m_dmnman.GetListAtChainTip().GetMN(opt_proTx->proTxHash); dmn && opt_proTx->pubKeyOperator == dmn->pdmnState->pubKeyOperator) { return false; } @@ -1416,8 +1406,6 @@ bool CTxMemPool::existsProviderTxCrossSchemeConflict(const CTransaction& tx) con } bool CTxMemPool::existsProviderTxConflict(const CTransaction &tx) const { - auto dmnman = Assert(m_dmnman); - LOCK(cs); auto hasKeyChangeInMempool = [&](const uint256& proTxHash) EXCLUSIVE_LOCKS_REQUIRED(cs) { @@ -1491,7 +1479,7 @@ bool CTxMemPool::existsProviderTxConflict(const CTransaction &tx) const { auto& proTx = *opt_proTx; // this method should only be called with validated ProTxs - auto dmn = dmnman->GetListAtChainTip().GetMN(proTx.proTxHash); + auto dmn = m_dmnman.GetListAtChainTip().GetMN(proTx.proTxHash); if (!dmn) { LogPrint(BCLog::MEMPOOL, "%s: ERROR: Masternode is not in the list, proTxHash: %s\n", __func__, proTx.proTxHash.ToString()); return true; // i.e. failed to find validated ProTx == conflict @@ -1513,7 +1501,7 @@ bool CTxMemPool::existsProviderTxConflict(const CTransaction &tx) const { } auto& proTx = *opt_proTx; // this method should only be called with validated ProTxs - auto dmn = dmnman->GetListAtChainTip().GetMN(proTx.proTxHash); + auto dmn = m_dmnman.GetListAtChainTip().GetMN(proTx.proTxHash); if (!dmn) { LogPrint(BCLog::MEMPOOL, "%s: ERROR: Masternode is not in the list, proTxHash: %s\n", __func__, proTx.proTxHash.ToString()); return true; // i.e. failed to find validated ProTx == conflict @@ -1662,12 +1650,11 @@ void CTxMemPool::RemoveStaged(setEntries &stage, bool updateDescendants, MemPool int CTxMemPool::Expire(std::chrono::seconds time) { AssertLockHeld(cs); - auto isman = Assert(m_isman); indexed_transaction_set::index::type::iterator it = mapTx.get().begin(); setEntries toremove; while (it != mapTx.get().end() && it->GetTime() < time) { // locked txes do not expire until mined and have sufficient confirmations - if (isman->IsLocked(it->GetTx().GetHash())) { + if (m_isman.IsLocked(it->GetTx().GetHash())) { it++; continue; } diff --git a/src/txmempool.h b/src/txmempool.h index c2d9789dd214..c0dcd0f1a360 100644 --- a/src/txmempool.h +++ b/src/txmempool.h @@ -447,8 +447,8 @@ class CTxMemPool const int m_check_ratio; //!< Value n means that 1 times in n we check. std::atomic nTransactionsUpdated{0}; //!< Used by getblocktemplate to trigger CreateNewBlock() invocation CBlockPolicyEstimator* const minerPolicyEstimator; - CDeterministicMNManager* const m_dmnman; - llmq::CInstantSendManager* const m_isman; + CDeterministicMNManager& m_dmnman; + llmq::CInstantSendManager& m_isman; uint64_t totalTxSize GUARDED_BY(cs); //!< sum of all mempool tx' byte sizes CAmount m_total_fee GUARDED_BY(cs); //!< sum of all mempool tx's fees (NOT modified fee) @@ -765,10 +765,7 @@ class CTxMemPool */ void TrimToSize(size_t sizelimit, std::vector* pvNoSpendsRemaining = nullptr) EXCLUSIVE_LOCKS_REQUIRED(cs); - /** Expire all transaction (and their dependencies) in the mempool older than time. Return the number of removed transactions. - * @pre Requires a CInstantSendManager passed via Options at construction - * for InstantSend awareness - */ + /** Expire all transaction (and their dependencies) in the mempool older than time. Return the number of removed transactions. */ int Expire(std::chrono::seconds time) EXCLUSIVE_LOCKS_REQUIRED(cs); /** @@ -819,10 +816,6 @@ class CTxMemPool TxMempoolInfo info(const uint256& hash) const; std::vector infoAll() const; - /** - * @pre Requires a CDeterministicMNManager passed via Options at - * construction of the CTxMemPool instance. - */ bool existsProviderTxConflict(const CTransaction &tx) const; /** @@ -923,8 +916,7 @@ class CTxMemPool /** * addUnchecked extension for Dash-specific transactions (ProTx). */ - void addUncheckedProTx(CDeterministicMNManager& dmnman, indexed_transaction_set::iterator& newit, - const CTransaction& tx); + void addUncheckedProTx(indexed_transaction_set::iterator& newit, const CTransaction& tx); /** Before calling removeUnchecked for a given transaction, * UpdateForRemoveFromMempool must be called on the entire (dependent) set From 75bf59489fdd586723062a8f86344af634b833f0 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Thu, 13 Aug 2026 04:06:49 +0700 Subject: [PATCH 06/12] refactor: query InstantSend via CChainstateHelper in BlockAssembler BlockAssembler carried its own CInstantSendManager reference for a single check even though its CChainstateHelper already holds the manager and has a dedicated passthrough section for it. Route the check through two new passthroughs and drop the extra member and the NodeContext::isman dependency from the miner. --- src/evo/chainhelper.cpp | 4 ++++ src/evo/chainhelper.h | 2 ++ src/node/miner.cpp | 4 +--- src/node/miner.h | 2 -- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/evo/chainhelper.cpp b/src/evo/chainhelper.cpp index 9412607e93a0..7bcd66d40c6e 100644 --- a/src/evo/chainhelper.cpp +++ b/src/evo/chainhelper.cpp @@ -84,6 +84,10 @@ std::optional> CChainstateH return std::make_pair(::SerializeHash(*islock), islock->txid); } +bool CChainstateHelper::IsInstantSendEnabled() const { return isman.IsInstantSendEnabled(); } + +bool CChainstateHelper::IsInstantSendLocked(const uint256& hash) const { return isman.IsLocked(hash); } + bool CChainstateHelper::IsInstantSendWaitingForTx(const uint256& hash) const { return isman.IsWaitingForTx(hash); } bool CChainstateHelper::RemoveConflictingISLockByTx(const CTransaction& tx) diff --git a/src/evo/chainhelper.h b/src/evo/chainhelper.h index eac183777ba1..0036475fd282 100644 --- a/src/evo/chainhelper.h +++ b/src/evo/chainhelper.h @@ -78,6 +78,8 @@ class CChainstateHelper /** Passthrough functions to CInstantSendManager */ std::optional> ConflictingISLockIfAny(const CTransaction& tx) const; + bool IsInstantSendEnabled() const; + bool IsInstantSendLocked(const uint256& hash) const; bool IsInstantSendWaitingForTx(const uint256& hash) const; bool RemoveConflictingISLockByTx(const CTransaction& tx); diff --git a/src/node/miner.cpp b/src/node/miner.cpp index 9146af7bf3f7..c4ad5451d76b 100644 --- a/src/node/miner.cpp +++ b/src/node/miner.cpp @@ -35,7 +35,6 @@ #include #include #include -#include #include #include #include @@ -76,7 +75,6 @@ BlockAssembler::BlockAssembler(Chainstate& chainstate, const NodeContext& node, m_evoDb(*Assert(node.evodb)), m_chainlocks(*Assert(node.chainlocks)), m_clhandler(*Assert(node.clhandler)), - m_isman(*Assert(node.isman)), chainparams(chainstate.m_chainman.GetParams()), m_mempool(mempool), m_quorum_block_processor(*Assert(Assert(node.llmq_ctx)->quorum_block_processor)), @@ -396,7 +394,7 @@ bool BlockAssembler::TestPackageTransactions(const CTxMemPool::setEntries& packa } const auto& txid = it->GetTx().GetHash(); - if (!m_isman.IsInstantSendEnabled() || m_isman.IsLocked(txid)) { + if (!m_chain_helper.IsInstantSendEnabled() || m_chain_helper.IsInstantSendLocked(txid)) { continue; } diff --git a/src/node/miner.h b/src/node/miner.h index 465929be17dc..dfa24cfcb55d 100644 --- a/src/node/miner.h +++ b/src/node/miner.h @@ -34,7 +34,6 @@ class ChainlockHandler; } // namespace chainlock namespace Consensus { struct Params; }; namespace llmq { -class CInstantSendManager; class CQuorumBlockProcessor; class CQuorumManager; } // namespace llmq @@ -174,7 +173,6 @@ class BlockAssembler CEvoDB& m_evoDb; const chainlock::Chainlocks& m_chainlocks; chainlock::ChainlockHandler& m_clhandler; - llmq::CInstantSendManager& m_isman; const CChainParams& chainparams; const CTxMemPool* const m_mempool; const llmq::CQuorumBlockProcessor& m_quorum_block_processor; From 65b5bb8014a192ec8f97a23c92db04c76bc29481 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Thu, 13 Aug 2026 18:19:44 +0700 Subject: [PATCH 07/12] refactor: drop the isman reference member from LLMQContext LLMQContext never used the InstantSend manager itself; the reference member existed only so consumers could reach isman through the context. With NodeContext owning isman that indirection is gone: PeerManagerImpl receives its own reference (making ProcessGetBlockData's isman parameter redundant), the chainstate helper is constructed from options.isman directly, and the LLMQContext constructor loses the parameter. --- src/init.cpp | 2 +- src/llmq/context.cpp | 9 +++--- src/llmq/context.h | 8 ++--- src/net_processing.cpp | 29 +++++++++++-------- src/net_processing.h | 4 +++ src/node/chainstate.cpp | 4 +-- src/node/interfaces.cpp | 4 +-- src/test/coinjoin_inouts_tests.cpp | 2 +- src/test/interfaces_tests.cpp | 2 +- src/test/util/setup_common.cpp | 2 +- .../validation_chainstatemanager_tests.cpp | 4 +-- 11 files changed, 38 insertions(+), 32 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index ec33682e7bab..f56248d00243 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -2108,7 +2108,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) chainman, *node.mempool, *node.mn_metaman, *node.mn_sync, *node.sporkman, *node.chainlocks, *node.clhandler, node.active_ctx ? node.active_ctx->nodeman.get() : nullptr, - node.dmnman, node.cj_walletman, node.llmq_ctx, ignores_incoming_txs); + node.dmnman, node.cj_walletman, *node.isman, node.llmq_ctx, ignores_incoming_txs); RegisterValidationInterface(node.peerman.get()); node.ds_notification_interface = std::make_unique( diff --git a/src/llmq/context.cpp b/src/llmq/context.cpp index ca57202dd3f0..b961a0a7ec89 100644 --- a/src/llmq/context.cpp +++ b/src/llmq/context.cpp @@ -11,17 +11,16 @@ #include #include -LLMQContext::LLMQContext(CDeterministicMNManager& dmnman, CEvoDB& evo_db, llmq::CInstantSendManager& isman, - ChainstateManager& chainman, const util::DbWrapperParams& db_params, int8_t bls_threads, - int16_t worker_count, int64_t max_recsigs_age) : +LLMQContext::LLMQContext(CDeterministicMNManager& dmnman, CEvoDB& evo_db, ChainstateManager& chainman, + const util::DbWrapperParams& db_params, int8_t bls_threads, int16_t worker_count, + int64_t max_recsigs_age) : bls_worker{std::make_shared()}, qsnapman{std::make_unique(evo_db)}, quorum_block_processor{ std::make_unique(chainman, dmnman, evo_db, *qsnapman, bls_threads)}, qman{std::make_unique(*bls_worker, dmnman, evo_db, *quorum_block_processor, *qsnapman, chainman, db_params)}, - sigman{std::make_unique(*qman, db_params, max_recsigs_age)}, - isman{isman} + sigman{std::make_unique(*qman, db_params, max_recsigs_age)} { // Have to start it early to let VerifyDB check ChainLock signatures in coinbase bls_worker->Start(worker_count); diff --git a/src/llmq/context.h b/src/llmq/context.h index 17c598e36896..9033967bfc95 100644 --- a/src/llmq/context.h +++ b/src/llmq/context.h @@ -16,7 +16,6 @@ class CEvoDB; class PeerManager; namespace llmq { -class CInstantSendManager; class CQuorumBlockProcessor; class CQuorumManager; class CQuorumSnapshotManager; @@ -31,9 +30,9 @@ struct LLMQContext { LLMQContext() = delete; LLMQContext(const LLMQContext&) = delete; LLMQContext& operator=(const LLMQContext&) = delete; - explicit LLMQContext(CDeterministicMNManager& dmnman, CEvoDB& evo_db, llmq::CInstantSendManager& isman, - ChainstateManager& chainman, const util::DbWrapperParams& db_params, int8_t bls_threads, - int16_t worker_count, int64_t max_recsigs_age); + explicit LLMQContext(CDeterministicMNManager& dmnman, CEvoDB& evo_db, ChainstateManager& chainman, + const util::DbWrapperParams& db_params, int8_t bls_threads, int16_t worker_count, + int64_t max_recsigs_age); ~LLMQContext(); /** Guaranteed if LLMQContext is initialized then all members are valid too @@ -47,7 +46,6 @@ struct LLMQContext { const std::unique_ptr quorum_block_processor; const std::unique_ptr qman; const std::unique_ptr sigman; - llmq::CInstantSendManager& isman; }; #endif // BITCOIN_LLMQ_CONTEXT_H diff --git a/src/net_processing.cpp b/src/net_processing.cpp index acdf8a8b1b8a..c48fdc29394c 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -572,6 +572,7 @@ class PeerManagerImpl final : public PeerManager CActiveMasternodeManager* nodeman, const std::unique_ptr& dmnman, const std::unique_ptr& cj_walletman, + llmq::CInstantSendManager& isman, const std::unique_ptr& llmq_ctx, bool ignore_incoming_txs); ~PeerManagerImpl() @@ -809,6 +810,7 @@ class PeerManagerImpl final : public PeerManager CActiveMasternodeManager* const m_nodeman; //!< null if non-masternode mode; non-null implies masternode mode const std::unique_ptr& m_dmnman; const std::unique_ptr& m_cj_walletman; + llmq::CInstantSendManager& m_isman; const std::unique_ptr& m_llmq_ctx; CMasternodeMetaMan& m_mn_metaman; CMasternodeSync& m_mn_sync; @@ -867,7 +869,7 @@ class PeerManagerImpl final : public PeerManager */ bool BlockRequestAllowed(const CBlockIndex* pindex) EXCLUSIVE_LOCKS_REQUIRED(cs_main); bool AlreadyHaveBlock(const uint256& block_hash) EXCLUSIVE_LOCKS_REQUIRED(cs_main); - void ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv& inv, llmq::CInstantSendManager& isman) EXCLUSIVE_LOCKS_REQUIRED(!m_most_recent_block_mutex); + void ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(!m_most_recent_block_mutex); /** * Validation logic for compact filters request handling. @@ -2041,9 +2043,10 @@ std::unique_ptr PeerManager::make(CConnman& connman, AddrMan& addrm CActiveMasternodeManager* nodeman, const std::unique_ptr& dmnman, const std::unique_ptr& cj_walletman, + llmq::CInstantSendManager& isman, const std::unique_ptr& llmq_ctx, bool ignore_incoming_txs) { - return std::make_unique(connman, addrman, banman, dstxman, chainman, pool, mn_metaman, mn_sync, sporkman, chainlocks, clhandler, nodeman, dmnman, cj_walletman, llmq_ctx, ignore_incoming_txs); + return std::make_unique(connman, addrman, banman, dstxman, chainman, pool, mn_metaman, mn_sync, sporkman, chainlocks, clhandler, nodeman, dmnman, cj_walletman, isman, llmq_ctx, ignore_incoming_txs); } PeerManagerImpl::PeerManagerImpl(CConnman& connman, AddrMan& addrman, BanMan* banman, @@ -2055,6 +2058,7 @@ PeerManagerImpl::PeerManagerImpl(CConnman& connman, AddrMan& addrman, BanMan* ba CActiveMasternodeManager* nodeman, const std::unique_ptr& dmnman, const std::unique_ptr& cj_walletman, + llmq::CInstantSendManager& isman, const std::unique_ptr& llmq_ctx, bool ignore_incoming_txs) : m_chainparams(chainman.GetParams()), m_connman(connman), @@ -2066,6 +2070,7 @@ PeerManagerImpl::PeerManagerImpl(CConnman& connman, AddrMan& addrman, BanMan* ba m_nodeman(nodeman), m_dmnman(dmnman), m_cj_walletman(cj_walletman), + m_isman(isman), m_llmq_ctx(llmq_ctx), m_mn_metaman(mn_metaman), m_mn_sync(mn_sync), @@ -2319,8 +2324,8 @@ bool PeerManagerImpl::AlreadyHave(const CInv& inv) // crafted invalid DSTX-es and potentially cause high load cheaply, because // corresponding checks in ProcessMessage won't let it to send DSTX-es too often. bool fIgnoreRecentRejects = inv.IsMsgDstx() || - m_llmq_ctx->isman.IsWaitingForTx(inv.hash) || - m_llmq_ctx->isman.IsLocked(inv.hash); + m_isman.IsWaitingForTx(inv.hash) || + m_isman.IsLocked(inv.hash); return (!fIgnoreRecentRejects && m_recent_rejects.contains(inv.hash)) || (inv.IsMsgDstx() && static_cast(m_dstxman.GetDSTX(inv.hash))) || @@ -2359,7 +2364,7 @@ bool PeerManagerImpl::AlreadyHave(const CInv& inv) return m_clhandler.AlreadyHave(inv); // TODO: move it to NetInstantSend case MSG_ISDLOCK: - return m_llmq_ctx->isman.AlreadyHave(inv); + return m_isman.AlreadyHave(inv); case MSG_PLATFORM_BAN: return m_mn_metaman.AlreadyHavePlatformBan(inv.hash); @@ -2652,7 +2657,7 @@ void PeerManagerImpl::RelayAddress(NodeId originator, } } -void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv& inv, llmq::CInstantSendManager& isman) +void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv& inv) { std::shared_ptr a_recent_block; std::shared_ptr a_recent_compact_block; @@ -2773,7 +2778,7 @@ void PeerManagerImpl::ProcessGetBlockData(CNode& pfrom, Peer& peer, const CInv& m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::TX, *pblock->vtx[pair.first])); } for (PairType &pair : merkleBlock.vMatchedTxn) { - auto islock = isman.GetInstantSendLockByTxid(pair.second); + auto islock = m_isman.GetInstantSendLockByTxid(pair.second); if (islock != nullptr) { m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::ISDLOCK, *islock)); } @@ -2961,7 +2966,7 @@ void PeerManagerImpl::ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic if (!push && inv.type == MSG_ISDLOCK) { instantsend::InstantSendLock o; - if (m_llmq_ctx->isman.GetInstantSendLockByHash(inv.hash, o)) { + if (m_isman.GetInstantSendLockByHash(inv.hash, o)) { m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::ISDLOCK, o)); push = true; } @@ -2996,7 +3001,7 @@ void PeerManagerImpl::ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic if (it != peer.m_getdata_requests.end() && !pfrom.fPauseSend) { const CInv &inv = *it++; if (inv.IsGenBlkMsg()) { - ProcessGetBlockData(pfrom, peer, inv, m_llmq_ctx->isman); + ProcessGetBlockData(pfrom, peer, inv); } // else: If the first item on the queue is an unknown type, we erase it // and continue processing the queue on the next call. @@ -4859,7 +4864,7 @@ void PeerManagerImpl::ProcessMessage( // parents so avoid re-requesting it from other peers. m_recent_rejects.insert(tx.GetHash()); ForgetTx(tx.GetHash()); - m_llmq_ctx->isman.TransactionIsRemoved(ptx); + m_isman.TransactionIsRemoved(ptx); } } else { m_recent_rejects.insert(tx.GetHash()); @@ -4891,7 +4896,7 @@ void PeerManagerImpl::ProcessMessage( pfrom.GetId(), state.ToString()); MaybePunishNodeForTx(pfrom.GetId(), state); - m_llmq_ctx->isman.TransactionIsRemoved(ptx); + m_isman.TransactionIsRemoved(ptx); } return; } @@ -6468,7 +6473,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto) tx_relay->m_tx_inventory_known_filter.insert(hash); queueAndMaybePushInv(CInv(nInvType, hash)); - const auto islock = m_llmq_ctx->isman.GetInstantSendLockByTxid(hash); + const auto islock = m_isman.GetInstantSendLockByTxid(hash); if (islock == nullptr) continue; uint256 isLockHash{::SerializeHash(*islock)}; tx_relay->m_tx_inventory_known_filter.insert(isLockHash); diff --git a/src/net_processing.h b/src/net_processing.h index 761da8001c07..282f96eafbf8 100644 --- a/src/net_processing.h +++ b/src/net_processing.h @@ -33,6 +33,9 @@ namespace chainlock { class Chainlocks; class ChainlockHandler; } // namespace chainlock +namespace llmq { +class CInstantSendManager; +} // namespace llmq /** Default for -maxorphantxsize, maximum size in megabytes the orphan map can grow before entries are removed */ static const unsigned int DEFAULT_MAX_ORPHAN_TRANSACTIONS_SIZE = 10; // this allows around 100 TXs of max size (and many more of normal size) @@ -158,6 +161,7 @@ class PeerManager : public CValidationInterface, public NetEventsInterface, publ CActiveMasternodeManager* nodeman, const std::unique_ptr& dmnman, const std::unique_ptr& cj_walletman, + llmq::CInstantSendManager& isman, const std::unique_ptr& llmq_ctx, bool ignore_incoming_txs); virtual ~PeerManager() { } diff --git a/src/node/chainstate.cpp b/src/node/chainstate.cpp index 47b17f947edb..b0ceaf9dea67 100644 --- a/src/node/chainstate.cpp +++ b/src/node/chainstate.cpp @@ -153,13 +153,13 @@ static ChainstateLoadResult CompleteChainstateInitialization(ChainstateManager& // Initialize llmq_ctx llmq_ctx.reset(); - llmq_ctx = std::make_unique(dmnman, evodb, *options.isman, chainman, + llmq_ctx = std::make_unique(dmnman, evodb, 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); // Initialize chain_helper chain_helper.reset(); - chain_helper = std::make_unique(evodb, dmnman, *options.mn_sync, llmq_ctx->isman, *(llmq_ctx->quorum_block_processor), + chain_helper = std::make_unique(evodb, dmnman, *options.mn_sync, *options.isman, *(llmq_ctx->quorum_block_processor), *(llmq_ctx->qsnapman), chainman, chainman.GetConsensus(), *options.chainlocks, *(llmq_ctx->qman)); diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index 99d61215aa59..4900dcf785af 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -648,10 +648,10 @@ class LLMQImpl : public LLMQ } std::vector getInstantSendLock(const uint256& txid) override { - if (!context().llmq_ctx || !context().llmq_ctx->isman) { + if (!context().isman) { return {}; } - const auto islock{context().llmq_ctx->isman->GetInstantSendLockByTxid(txid)}; + const auto islock{context().isman->GetInstantSendLockByTxid(txid)}; if (!islock) { return {}; } diff --git a/src/test/coinjoin_inouts_tests.cpp b/src/test/coinjoin_inouts_tests.cpp index ac6c2c233569..5cd3c8921474 100644 --- a/src/test/coinjoin_inouts_tests.cpp +++ b/src/test/coinjoin_inouts_tests.cpp @@ -298,7 +298,7 @@ struct InOutsChecker : CCoinJoinBaseSession BOOST_AUTO_TEST_CASE(validation_uses_session_denom_snapshot) { Chainstate& chainstate{Assert(m_node.chainman)->ActiveChainstate()}; - const auto& isman{*Assert(m_node.llmq_ctx->isman)}; + const auto& isman{*Assert(m_node.isman)}; const auto& mempool{*Assert(m_node.mempool)}; const int session_denom{CoinJoin::AmountToDenomination(CoinJoin::GetSmallestDenomination())}; diff --git a/src/test/interfaces_tests.cpp b/src/test/interfaces_tests.cpp index d96112dbca1e..2e3f1e414ff8 100644 --- a/src/test/interfaces_tests.cpp +++ b/src/test/interfaces_tests.cpp @@ -358,7 +358,7 @@ BOOST_AUTO_TEST_CASE(getInstantSendLock) constexpr const char* REGTEST_SPORK_PRIVKEY{"cP4EKFyJsHT39LDqgdcB43Y3YXjNyjb5Fuas1GQSeAtjnZWmZEQK"}; auto node{interfaces::MakeNode(m_node)}; - auto& isman{*Assert(m_node.llmq_ctx)->isman}; + auto& isman{*Assert(m_node.isman)}; auto islock{std::make_shared()}; islock->inputs.emplace_back(uint256::ONE, 0); diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index 0b951ad3a0f6..30371e416a9e 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -133,7 +133,7 @@ std::unique_ptr MakePeerManager(CConnman& connman, { return PeerManager::make(connman, *node.addrman, banman, *node.dstxman, *node.chainman, *node.mempool, *node.mn_metaman, *node.mn_sync, *node.sporkman, *node.chainlocks, *node.clhandler, /*nodeman=*/nullptr, node.dmnman, node.cj_walletman, - node.llmq_ctx, ignore_incoming_txs); + *node.isman, node.llmq_ctx, ignore_incoming_txs); } struct NetworkSetup diff --git a/src/test/validation_chainstatemanager_tests.cpp b/src/test/validation_chainstatemanager_tests.cpp index 5531b7116091..50a8f8dc1145 100644 --- a/src/test/validation_chainstatemanager_tests.cpp +++ b/src/test/validation_chainstatemanager_tests.cpp @@ -58,12 +58,12 @@ static void DashChainstateSetup(ChainstateManager& chainman, bool llmq_dbs_wipe) { node.llmq_ctx.reset(); - node.llmq_ctx = std::make_unique(*node.dmnman, *node.evodb, *Assert(node.isman.get()), chainman, + node.llmq_ctx = std::make_unique(*node.dmnman, *node.evodb, 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); // 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.chain_helper = std::make_unique(*node.evodb, *node.dmnman, *Assert(node.mn_sync), *Assert(node.isman), *(node.llmq_ctx->quorum_block_processor), *(node.llmq_ctx->qsnapman), chainman, chainman.GetConsensus(), *Assert(node.chainlocks), *(node.llmq_ctx->qman)); } From 039b51be74a556d29807479b080a80eb6f5c1477 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Tue, 18 Aug 2026 17:43:46 +0700 Subject: [PATCH 08/12] refactor: code-move only: catch_exceptions to the top of the chainstate load loop It is required to load CEvoDB and CInstantSendManager safely and trigger re-index in case if exception is thrown --- src/init.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index f56248d00243..2a17d3a06b61 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1954,6 +1954,14 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) node.isman.reset(); node.dmnman.reset(); node.evodb.reset(); + auto catch_exceptions = [](auto&& f) { + try { + return f(); + } catch (const std::exception& e) { + LogPrintf("%s\n", e.what()); + return std::make_tuple(node::ChainstateLoadStatus::FAILURE, _("Error opening block database")); + } + }; node.evodb = std::make_unique(util::DbWrapperParams{.path = args.GetDataDirNet(), .memory = false, .wipe = node::fReindex || fReindexChainState}); node.dmnman = std::make_unique(*node.evodb, *node.mn_metaman); node.isman = std::make_unique(*node.sporkman, util::DbWrapperParams{.path = args.GetDataDirNet(), .memory = false, .wipe = node::fReindex || fReindexChainState}); @@ -2010,14 +2018,6 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) uiInterface.InitMessage(_("Loading block index…").translated); const auto load_block_index_start_time{SteadyClock::now()}; - auto catch_exceptions = [](auto&& f) { - try { - return f(); - } catch (const std::exception& e) { - LogPrintf("%s\n", e.what()); - return std::make_tuple(node::ChainstateLoadStatus::FAILURE, _("Error opening block database")); - } - }; 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); From 4ee18f5c999849edaf084b723b1bebc1fe09e4ea Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Tue, 18 Aug 2026 18:09:51 +0700 Subject: [PATCH 09/12] fix: load CEvoDB and CInstantSendManager safely before chainstate and trigger re-index in case if exception is thrown --- src/init.cpp | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 2a17d3a06b61..53f159d7586e 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -1962,13 +1962,16 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) return std::make_tuple(node::ChainstateLoadStatus::FAILURE, _("Error opening block database")); } }; - node.evodb = std::make_unique(util::DbWrapperParams{.path = args.GetDataDirNet(), .memory = false, .wipe = node::fReindex || fReindexChainState}); - node.dmnman = std::make_unique(*node.evodb, *node.mn_metaman); - node.isman = std::make_unique(*node.sporkman, util::DbWrapperParams{.path = args.GetDataDirNet(), .memory = false, .wipe = node::fReindex || fReindexChainState}); - - mempool_opts.dmnman = node.dmnman.get(); - mempool_opts.isman = node.isman.get(); - node.mempool = std::make_unique(mempool_opts); + auto [status, error] = catch_exceptions([&]() -> node::ChainstateLoadResult { + node.evodb = std::make_unique(util::DbWrapperParams{.path = args.GetDataDirNet(), .memory = false, .wipe = node::fReindex || fReindexChainState}); + node.dmnman = std::make_unique(*node.evodb, *node.mn_metaman); + node.isman = std::make_unique(*node.sporkman, util::DbWrapperParams{.path = args.GetDataDirNet(), .memory = false, .wipe = node::fReindex || fReindexChainState}); + + mempool_opts.dmnman = node.dmnman.get(); + mempool_opts.isman = node.isman.get(); + node.mempool = std::make_unique(mempool_opts); + return {node::ChainstateLoadStatus::SUCCESS, {}}; + }); const ChainstateManager::Options chainman_opts{ .chainparams = chainparams, @@ -1987,8 +1990,6 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) node.mn_sync = std::make_unique(std::make_unique(*node.connman, *node.netfulfilledman)); node::ChainstateLoadOptions options; - options.mempool = Assert(node.mempool.get()); - options.isman = Assert(node.isman.get()); options.chainlocks = Assert(node.chainlocks.get()); options.mn_sync = Assert(node.mn_sync.get()); options.data_dir = args.GetDataDirNet(); @@ -2018,7 +2019,11 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) uiInterface.InitMessage(_("Loading block index…").translated); const auto load_block_index_start_time{SteadyClock::now()}; - 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) { + options.mempool = Assert(node.mempool.get()); + options.isman = Assert(node.isman.get()); + std::tie(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) { From b5b7ab780c95abde1e14edb685d1913890df00a3 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Wed, 19 Aug 2026 05:14:28 +0700 Subject: [PATCH 10/12] refactor: pass CDeterministicMNManager to PeerManager by reference PeerManager reaches dmnman through a reference to init's unique_ptr and pays an Assert on every use, but the manager is constructed before PeerManager::make and Shutdown() destroys it only after peerman is reset, so the pointer can never be null while PeerManager is alive. --- src/init.cpp | 2 +- src/net_processing.cpp | 20 ++++++++++---------- src/net_processing.h | 2 +- src/test/util/setup_common.cpp | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 53f159d7586e..ddbf9b53eda3 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -2113,7 +2113,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) chainman, *node.mempool, *node.mn_metaman, *node.mn_sync, *node.sporkman, *node.chainlocks, *node.clhandler, node.active_ctx ? node.active_ctx->nodeman.get() : nullptr, - node.dmnman, node.cj_walletman, *node.isman, node.llmq_ctx, ignores_incoming_txs); + *node.dmnman, node.cj_walletman, *node.isman, node.llmq_ctx, ignores_incoming_txs); RegisterValidationInterface(node.peerman.get()); node.ds_notification_interface = std::make_unique( diff --git a/src/net_processing.cpp b/src/net_processing.cpp index c48fdc29394c..7ea1174aefe4 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -570,7 +570,7 @@ class PeerManagerImpl final : public PeerManager CSporkManager& sporkman, const chainlock::Chainlocks& chainlocks, chainlock::ChainlockHandler& clhandler, CActiveMasternodeManager* nodeman, - const std::unique_ptr& dmnman, + CDeterministicMNManager& dmnman, const std::unique_ptr& cj_walletman, llmq::CInstantSendManager& isman, const std::unique_ptr& llmq_ctx, bool ignore_incoming_txs); @@ -808,7 +808,7 @@ class PeerManagerImpl final : public PeerManager CTxMemPool& m_mempool; std::unique_ptr m_txreconciliation; CActiveMasternodeManager* const m_nodeman; //!< null if non-masternode mode; non-null implies masternode mode - const std::unique_ptr& m_dmnman; + CDeterministicMNManager& m_dmnman; const std::unique_ptr& m_cj_walletman; llmq::CInstantSendManager& m_isman; const std::unique_ptr& m_llmq_ctx; @@ -2041,7 +2041,7 @@ std::unique_ptr PeerManager::make(CConnman& connman, AddrMan& addrm CSporkManager& sporkman, const chainlock::Chainlocks& chainlocks, chainlock::ChainlockHandler& clhandler, CActiveMasternodeManager* nodeman, - const std::unique_ptr& dmnman, + CDeterministicMNManager& dmnman, const std::unique_ptr& cj_walletman, llmq::CInstantSendManager& isman, const std::unique_ptr& llmq_ctx, bool ignore_incoming_txs) @@ -2056,7 +2056,7 @@ PeerManagerImpl::PeerManagerImpl(CConnman& connman, AddrMan& addrman, BanMan* ba const chainlock::Chainlocks& chainlocks, chainlock::ChainlockHandler& clhandler, CActiveMasternodeManager* nodeman, - const std::unique_ptr& dmnman, + CDeterministicMNManager& dmnman, const std::unique_ptr& cj_walletman, llmq::CInstantSendManager& isman, const std::unique_ptr& llmq_ctx, bool ignore_incoming_txs) @@ -3742,7 +3742,7 @@ MessageProcessingResult PeerManagerImpl::ProcessPlatformBanMessage(NodeId node, MessageProcessingResult ret{}; ret.m_to_erase = CInv{MSG_PLATFORM_BAN, hash}; - const auto list = Assert(m_dmnman)->GetListAtChainTip(); + const auto list = m_dmnman.GetListAtChainTip(); auto dmn = list.GetMN(ban_msg.m_protx_hash); if (!dmn) { // small P2P penalty (1), as the evonode may have very recently been removed @@ -4106,7 +4106,7 @@ void PeerManagerImpl::ProcessMessage( // Tell our peer that he should send us CoinJoin queue messages m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDDSQUEUE, true)); // Tell our peer that he should send us intra-quorum messages - const auto tip_mn_list = Assert(m_dmnman)->GetListAtChainTip(); + const auto tip_mn_list = m_dmnman.GetListAtChainTip(); if (m_llmq_ctx->qman->IsWatching() && m_connman.IsMasternodeQuorumNode(&pfrom, tip_mn_list)) { m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::QWATCH)); } @@ -4762,7 +4762,7 @@ void PeerManagerImpl::ProcessMessage( // Process custom logic, no matter if tx will be accepted to mempool later or not if (nInvType == MSG_DSTX) { uint256 hashTx = tx.GetHash(); - const auto result = ValidateDSTX(*m_dmnman, m_dstxman, m_chainman, m_mn_metaman, m_mempool, dstx, hashTx); + const auto result = ValidateDSTX(m_dmnman, m_dstxman, m_chainman, m_mn_metaman, m_mempool, dstx, hashTx); if (result.do_return) { if (result.score != DSTXValidationScore::NONE) { Misbehaving(*peer, static_cast(result.score), "invalid dstx"); @@ -5468,7 +5468,7 @@ void PeerManagerImpl::ProcessMessage( CSimplifiedMNListDiff mnListDiff; std::string strError; - if (BuildSimplifiedMNListDiff(*m_dmnman, m_chainman, *m_llmq_ctx->quorum_block_processor, *m_llmq_ctx->qman, + if (BuildSimplifiedMNListDiff(m_dmnman, m_chainman, *m_llmq_ctx->quorum_block_processor, *m_llmq_ctx->qman, cmd.baseBlockHash, cmd.blockHash, mnListDiff, strError)) { m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::MNLISTDIFF, mnListDiff)); @@ -5518,7 +5518,7 @@ void PeerManagerImpl::ProcessMessage( llmq::CQuorumRotationInfo quorumRotationInfoRet; std::string strError; bool use_legacy_construction = pfrom.GetCommonVersion() < EFFICIENT_QRINFO_VERSION;; - if (BuildQuorumRotationInfo(*m_dmnman, *m_llmq_ctx->qsnapman, m_chainman, *m_llmq_ctx->qman, *m_llmq_ctx->quorum_block_processor, cmd, use_legacy_construction, quorumRotationInfoRet, strError)) { + if (BuildQuorumRotationInfo(m_dmnman, *m_llmq_ctx->qsnapman, m_chainman, *m_llmq_ctx->qman, *m_llmq_ctx->quorum_block_processor, cmd, use_legacy_construction, quorumRotationInfoRet, strError)) { m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::QUORUMROTATIONINFO, quorumRotationInfoRet)); } else { strError = strprintf("getquorumrotationinfo failed for size(baseBlockHashes)=%d, blockRequestHash=%s. error=%s", cmd.baseBlockHashes.size(), cmd.blockRequestHash.ToString(), strError); @@ -5623,7 +5623,7 @@ void PeerManagerImpl::ProcessMessage( if (m_cj_walletman) { PostProcessMessage(m_cj_walletman->processMessage(pfrom, m_chainman.ActiveChainstate(), m_connman, m_mempool, msg_type, vRecv), pfrom.GetId()); } - PostProcessMessage(CMNAuth::ProcessMessage(pfrom, peer->m_their_services, m_connman, m_mn_metaman, m_nodeman, m_mn_sync, m_dmnman->GetListAtChainTip(), msg_type, vRecv), pfrom.GetId()); + PostProcessMessage(CMNAuth::ProcessMessage(pfrom, peer->m_their_services, m_connman, m_mn_metaman, m_nodeman, m_mn_sync, m_dmnman.GetListAtChainTip(), msg_type, vRecv), pfrom.GetId()); PostProcessMessage(m_llmq_ctx->quorum_block_processor->ProcessMessage( pfrom, msg_type, vRecv, [this, &pfrom](const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(!::cs_main) { diff --git a/src/net_processing.h b/src/net_processing.h index 282f96eafbf8..03ca4855b707 100644 --- a/src/net_processing.h +++ b/src/net_processing.h @@ -159,7 +159,7 @@ class PeerManager : public CValidationInterface, public NetEventsInterface, publ const chainlock::Chainlocks& chainlocks, chainlock::ChainlockHandler& clhandler, CActiveMasternodeManager* nodeman, - const std::unique_ptr& dmnman, + CDeterministicMNManager& dmnman, const std::unique_ptr& cj_walletman, llmq::CInstantSendManager& isman, const std::unique_ptr& llmq_ctx, bool ignore_incoming_txs); diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index 30371e416a9e..79986a675b25 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -132,7 +132,7 @@ std::unique_ptr MakePeerManager(CConnman& connman, bool ignore_incoming_txs) { return PeerManager::make(connman, *node.addrman, banman, *node.dstxman, *node.chainman, *node.mempool, *node.mn_metaman, - *node.mn_sync, *node.sporkman, *node.chainlocks, *node.clhandler, /*nodeman=*/nullptr, node.dmnman, node.cj_walletman, + *node.mn_sync, *node.sporkman, *node.chainlocks, *node.clhandler, /*nodeman=*/nullptr, *node.dmnman, node.cj_walletman, *node.isman, node.llmq_ctx, ignore_incoming_txs); } From bba42f01270e46ffb8541d937522376eb438af0e Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Wed, 19 Aug 2026 05:16:35 +0700 Subject: [PATCH 11/12] refactor: pass LLMQContext to PeerManager by reference Same situation as dmnman: LLMQContext is constructed during chainstate load, before PeerManager::make, and Shutdown() destroys it only after peerman is reset, so PeerManager's unique_ptr indirection and the defensive assert in SendMessages() guard a case that cannot happen. --- src/init.cpp | 2 +- src/net_processing.cpp | 30 ++++++++++++++---------------- src/net_processing.h | 2 +- src/test/util/setup_common.cpp | 2 +- 4 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index ddbf9b53eda3..05a708b2528b 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -2113,7 +2113,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) chainman, *node.mempool, *node.mn_metaman, *node.mn_sync, *node.sporkman, *node.chainlocks, *node.clhandler, node.active_ctx ? node.active_ctx->nodeman.get() : nullptr, - *node.dmnman, node.cj_walletman, *node.isman, node.llmq_ctx, ignores_incoming_txs); + *node.dmnman, node.cj_walletman, *node.isman, *node.llmq_ctx, ignores_incoming_txs); RegisterValidationInterface(node.peerman.get()); node.ds_notification_interface = std::make_unique( diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 7ea1174aefe4..d421b0c68595 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -573,7 +573,7 @@ class PeerManagerImpl final : public PeerManager CDeterministicMNManager& dmnman, const std::unique_ptr& cj_walletman, llmq::CInstantSendManager& isman, - const std::unique_ptr& llmq_ctx, bool ignore_incoming_txs); + LLMQContext& llmq_ctx, bool ignore_incoming_txs); ~PeerManagerImpl() { @@ -811,7 +811,7 @@ class PeerManagerImpl final : public PeerManager CDeterministicMNManager& m_dmnman; const std::unique_ptr& m_cj_walletman; llmq::CInstantSendManager& m_isman; - const std::unique_ptr& m_llmq_ctx; + LLMQContext& m_llmq_ctx; CMasternodeMetaMan& m_mn_metaman; CMasternodeSync& m_mn_sync; CSporkManager& m_sporkman; @@ -2044,7 +2044,7 @@ std::unique_ptr PeerManager::make(CConnman& connman, AddrMan& addrm CDeterministicMNManager& dmnman, const std::unique_ptr& cj_walletman, llmq::CInstantSendManager& isman, - const std::unique_ptr& llmq_ctx, bool ignore_incoming_txs) + LLMQContext& llmq_ctx, bool ignore_incoming_txs) { return std::make_unique(connman, addrman, banman, dstxman, chainman, pool, mn_metaman, mn_sync, sporkman, chainlocks, clhandler, nodeman, dmnman, cj_walletman, isman, llmq_ctx, ignore_incoming_txs); } @@ -2059,7 +2059,7 @@ PeerManagerImpl::PeerManagerImpl(CConnman& connman, AddrMan& addrman, BanMan* ba CDeterministicMNManager& dmnman, const std::unique_ptr& cj_walletman, llmq::CInstantSendManager& isman, - const std::unique_ptr& llmq_ctx, bool ignore_incoming_txs) + LLMQContext& llmq_ctx, bool ignore_incoming_txs) : m_chainparams(chainman.GetParams()), m_connman(connman), m_addrman(addrman), @@ -2356,10 +2356,10 @@ bool PeerManagerImpl::AlreadyHave(const CInv& inv) return false; case MSG_QUORUM_FINAL_COMMITMENT: - return m_llmq_ctx->quorum_block_processor->HasMineableCommitment(inv.hash); + return m_llmq_ctx.quorum_block_processor->HasMineableCommitment(inv.hash); case MSG_QUORUM_RECOVERED_SIG: // TODO: move it to NetSigning - return m_llmq_ctx->sigman->AlreadyHave(inv); + return m_llmq_ctx.sigman->AlreadyHave(inv); case MSG_CLSIG: return m_clhandler.AlreadyHave(inv); // TODO: move it to NetInstantSend @@ -2941,7 +2941,7 @@ void PeerManagerImpl::ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic if (!push && (inv.type == MSG_QUORUM_FINAL_COMMITMENT)) { llmq::CFinalCommitment o; - if (m_llmq_ctx->quorum_block_processor->GetMineableCommitmentByHash( + if (m_llmq_ctx.quorum_block_processor->GetMineableCommitmentByHash( inv.hash, o)) { m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::QFCOMMITMENT, o)); push = true; @@ -2950,7 +2950,7 @@ void PeerManagerImpl::ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic if (!push && (inv.type == MSG_QUORUM_RECOVERED_SIG)) { llmq::CRecoveredSig o; - if (m_llmq_ctx->sigman->GetRecoveredSigForGetData(inv.hash, o)) { + if (m_llmq_ctx.sigman->GetRecoveredSigForGetData(inv.hash, o)) { m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::QSIGREC, o)); push = true; } @@ -3770,7 +3770,7 @@ MessageProcessingResult PeerManagerImpl::ProcessPlatformBanMessage(NodeId node, } Consensus::LLMQType llmq_type = m_chainparams.GetConsensus().llmqTypePlatform; - auto quorum = m_llmq_ctx->qman->GetQuorum(llmq_type, ban_msg.m_quorum_hash); + auto quorum = m_llmq_ctx.qman->GetQuorum(llmq_type, ban_msg.m_quorum_hash); if (!quorum) { LogPrintf("PLATFORMBAN -- hash: %s protx_hash: %s missing quorum_hash: %s llmq_type: %d\n", hash.ToString(), ban_msg.m_protx_hash.ToString(), ban_msg.m_quorum_hash.ToString(), std23::to_underlying(llmq_type)); ret.m_error = MisbehavingError{100}; @@ -4107,7 +4107,7 @@ void PeerManagerImpl::ProcessMessage( m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::SENDDSQUEUE, true)); // Tell our peer that he should send us intra-quorum messages const auto tip_mn_list = m_dmnman.GetListAtChainTip(); - if (m_llmq_ctx->qman->IsWatching() && m_connman.IsMasternodeQuorumNode(&pfrom, tip_mn_list)) { + if (m_llmq_ctx.qman->IsWatching() && m_connman.IsMasternodeQuorumNode(&pfrom, tip_mn_list)) { m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::QWATCH)); } } @@ -5468,7 +5468,7 @@ void PeerManagerImpl::ProcessMessage( CSimplifiedMNListDiff mnListDiff; std::string strError; - if (BuildSimplifiedMNListDiff(m_dmnman, m_chainman, *m_llmq_ctx->quorum_block_processor, *m_llmq_ctx->qman, + if (BuildSimplifiedMNListDiff(m_dmnman, m_chainman, *m_llmq_ctx.quorum_block_processor, *m_llmq_ctx.qman, cmd.baseBlockHash, cmd.blockHash, mnListDiff, strError)) { m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::MNLISTDIFF, mnListDiff)); @@ -5518,7 +5518,7 @@ void PeerManagerImpl::ProcessMessage( llmq::CQuorumRotationInfo quorumRotationInfoRet; std::string strError; bool use_legacy_construction = pfrom.GetCommonVersion() < EFFICIENT_QRINFO_VERSION;; - if (BuildQuorumRotationInfo(m_dmnman, *m_llmq_ctx->qsnapman, m_chainman, *m_llmq_ctx->qman, *m_llmq_ctx->quorum_block_processor, cmd, use_legacy_construction, quorumRotationInfoRet, strError)) { + if (BuildQuorumRotationInfo(m_dmnman, *m_llmq_ctx.qsnapman, m_chainman, *m_llmq_ctx.qman, *m_llmq_ctx.quorum_block_processor, cmd, use_legacy_construction, quorumRotationInfoRet, strError)) { m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::QUORUMROTATIONINFO, quorumRotationInfoRet)); } else { strError = strprintf("getquorumrotationinfo failed for size(baseBlockHashes)=%d, blockRequestHash=%s. error=%s", cmd.baseBlockHashes.size(), cmd.blockRequestHash.ToString(), strError); @@ -5624,7 +5624,7 @@ void PeerManagerImpl::ProcessMessage( PostProcessMessage(m_cj_walletman->processMessage(pfrom, m_chainman.ActiveChainstate(), m_connman, m_mempool, msg_type, vRecv), pfrom.GetId()); } PostProcessMessage(CMNAuth::ProcessMessage(pfrom, peer->m_their_services, m_connman, m_mn_metaman, m_nodeman, m_mn_sync, m_dmnman.GetListAtChainTip(), msg_type, vRecv), pfrom.GetId()); - PostProcessMessage(m_llmq_ctx->quorum_block_processor->ProcessMessage( + PostProcessMessage(m_llmq_ctx.quorum_block_processor->ProcessMessage( pfrom, msg_type, vRecv, [this, &pfrom](const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(!::cs_main) { return WITH_LOCK(::cs_main, @@ -5654,7 +5654,7 @@ void PeerManagerImpl::ProcessMessage( Misbehaving(*peer, UNREQUESTED_OBJECT_MISBEHAVIOR_SCORE, "unrequested clsig"); return; } - PostProcessMessage(m_clhandler.ProcessNewChainLock(pfrom.GetId(), clsig, *m_llmq_ctx->qman, + PostProcessMessage(m_clhandler.ProcessNewChainLock(pfrom.GetId(), clsig, *m_llmq_ctx.qman, clsig_inv.hash), pfrom.GetId()); } @@ -6142,8 +6142,6 @@ bool PeerManagerImpl::SendMessages(CNode* pto) { AssertLockHeld(g_msgproc_mutex); - assert(m_llmq_ctx); - const bool is_masternode = m_nodeman != nullptr; PeerRef peer = GetPeerRef(pto->GetId()); diff --git a/src/net_processing.h b/src/net_processing.h index 03ca4855b707..e950a01bccb8 100644 --- a/src/net_processing.h +++ b/src/net_processing.h @@ -162,7 +162,7 @@ class PeerManager : public CValidationInterface, public NetEventsInterface, publ CDeterministicMNManager& dmnman, const std::unique_ptr& cj_walletman, llmq::CInstantSendManager& isman, - const std::unique_ptr& llmq_ctx, bool ignore_incoming_txs); + LLMQContext& llmq_ctx, bool ignore_incoming_txs); virtual ~PeerManager() { } /** diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index 79986a675b25..e270bdcbc35b 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -133,7 +133,7 @@ std::unique_ptr MakePeerManager(CConnman& connman, { return PeerManager::make(connman, *node.addrman, banman, *node.dstxman, *node.chainman, *node.mempool, *node.mn_metaman, *node.mn_sync, *node.sporkman, *node.chainlocks, *node.clhandler, /*nodeman=*/nullptr, *node.dmnman, node.cj_walletman, - *node.isman, node.llmq_ctx, ignore_incoming_txs); + *node.isman, *node.llmq_ctx, ignore_incoming_txs); } struct NetworkSetup From b684c2ad8410f790d915a38846bbe88d71f2f639 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Wed, 19 Aug 2026 05:19:18 +0700 Subject: [PATCH 12/12] refactor: construct CJWalletManager before PeerManager, pass plain pointer CJWalletManager was the only PeerManager dependency created after PeerManager::make, which forced the unique_ptr-reference indirection to observe the late construction. Its constructor needs nothing from peerman, so create it first (tests already do) and pass a plain nullable pointer like nodeman and banman; it stays null in masternode mode and in wallet-disabled builds. --- src/init.cpp | 18 ++++++++++-------- src/net_processing.cpp | 8 ++++---- src/net_processing.h | 2 +- src/test/util/setup_common.cpp | 2 +- 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 05a708b2528b..d083d168270f 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -2108,12 +2108,21 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) RegisterValidationInterface(node.observer_ctx.get()); } + assert(!node.cj_walletman); +#ifdef ENABLE_WALLET + if (!node.active_ctx) { + // Only constructed in wallet-enabled builds; stays null otherwise, must check before use + node.cj_walletman = CJWalletManager::make(chainman, *node.dmnman, *node.mn_metaman, *node.mempool, *node.mn_sync, + *node.isman, !ignores_incoming_txs); + } +#endif + assert(!node.peerman); node.peerman = PeerManager::make(*node.connman, *node.addrman, node.banman.get(), *node.dstxman, chainman, *node.mempool, *node.mn_metaman, *node.mn_sync, *node.sporkman, *node.chainlocks, *node.clhandler, node.active_ctx ? node.active_ctx->nodeman.get() : nullptr, - *node.dmnman, node.cj_walletman, *node.isman, *node.llmq_ctx, ignores_incoming_txs); + *node.dmnman, node.cj_walletman.get(), *node.isman, *node.llmq_ctx, ignores_incoming_txs); RegisterValidationInterface(node.peerman.get()); node.ds_notification_interface = std::make_unique( @@ -2158,13 +2167,6 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) *node.mempool, *node.active_ctx->nodeman, *node.mn_sync, *node.isman); node.active_ctx->SetCJServer(cj_server.get()); node.peerman->AddExtraHandler(std::move(cj_server)); - } else { - assert(!node.cj_walletman); - // Only constructed in wallet-enabled builds; stays null otherwise, must check before use -#ifdef ENABLE_WALLET - node.cj_walletman = CJWalletManager::make(chainman, *node.dmnman, *node.mn_metaman, *node.mempool, *node.mn_sync, - *node.isman, !ignores_incoming_txs); -#endif } if (node.cj_walletman) { diff --git a/src/net_processing.cpp b/src/net_processing.cpp index d421b0c68595..3c164a07de41 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -571,7 +571,7 @@ class PeerManagerImpl final : public PeerManager chainlock::ChainlockHandler& clhandler, CActiveMasternodeManager* nodeman, CDeterministicMNManager& dmnman, - const std::unique_ptr& cj_walletman, + CJWalletManager* cj_walletman, llmq::CInstantSendManager& isman, LLMQContext& llmq_ctx, bool ignore_incoming_txs); @@ -809,7 +809,7 @@ class PeerManagerImpl final : public PeerManager std::unique_ptr m_txreconciliation; CActiveMasternodeManager* const m_nodeman; //!< null if non-masternode mode; non-null implies masternode mode CDeterministicMNManager& m_dmnman; - const std::unique_ptr& m_cj_walletman; + CJWalletManager* const m_cj_walletman; //!< null in masternode mode and in wallet-disabled builds llmq::CInstantSendManager& m_isman; LLMQContext& m_llmq_ctx; CMasternodeMetaMan& m_mn_metaman; @@ -2042,7 +2042,7 @@ std::unique_ptr PeerManager::make(CConnman& connman, AddrMan& addrm chainlock::ChainlockHandler& clhandler, CActiveMasternodeManager* nodeman, CDeterministicMNManager& dmnman, - const std::unique_ptr& cj_walletman, + CJWalletManager* cj_walletman, llmq::CInstantSendManager& isman, LLMQContext& llmq_ctx, bool ignore_incoming_txs) { @@ -2057,7 +2057,7 @@ PeerManagerImpl::PeerManagerImpl(CConnman& connman, AddrMan& addrman, BanMan* ba chainlock::ChainlockHandler& clhandler, CActiveMasternodeManager* nodeman, CDeterministicMNManager& dmnman, - const std::unique_ptr& cj_walletman, + CJWalletManager* cj_walletman, llmq::CInstantSendManager& isman, LLMQContext& llmq_ctx, bool ignore_incoming_txs) : m_chainparams(chainman.GetParams()), diff --git a/src/net_processing.h b/src/net_processing.h index e950a01bccb8..8fceb9bcd295 100644 --- a/src/net_processing.h +++ b/src/net_processing.h @@ -160,7 +160,7 @@ class PeerManager : public CValidationInterface, public NetEventsInterface, publ chainlock::ChainlockHandler& clhandler, CActiveMasternodeManager* nodeman, CDeterministicMNManager& dmnman, - const std::unique_ptr& cj_walletman, + CJWalletManager* cj_walletman, llmq::CInstantSendManager& isman, LLMQContext& llmq_ctx, bool ignore_incoming_txs); virtual ~PeerManager() { } diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index e270bdcbc35b..e1f6a8efcd52 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -132,7 +132,7 @@ std::unique_ptr MakePeerManager(CConnman& connman, bool ignore_incoming_txs) { return PeerManager::make(connman, *node.addrman, banman, *node.dstxman, *node.chainman, *node.mempool, *node.mn_metaman, - *node.mn_sync, *node.sporkman, *node.chainlocks, *node.clhandler, /*nodeman=*/nullptr, *node.dmnman, node.cj_walletman, + *node.mn_sync, *node.sporkman, *node.chainlocks, *node.clhandler, /*nodeman=*/nullptr, *node.dmnman, node.cj_walletman.get(), *node.isman, *node.llmq_ctx, ignore_incoming_txs); }