From c43e3e4bea1a4d5ba1946e09b8223fa958fb8da3 Mon Sep 17 00:00:00 2001 From: pasta Date: Wed, 19 Aug 2026 22:46:11 -0500 Subject: [PATCH] test: rebuild peerman and cj_walletman across chainstate reloads PeerManagerImpl holds the mempool, dmnman, isman, clhandler and llmq_ctx by reference; CJWalletManagerImpl holds the chainman, dmnman, mempool and isman. LoadVerifyActivateChainstate() always recreates llmq_ctx (via LoadChainstate) and, since dash#7603, its reindex path also replaces the mempool and those managers, so a fixture that already built peerman or cj_walletman (any SnapshotTestSetup test reloading after SimulateNodeRestart) was left holding dangling references for the rest of the test. Nothing dereferences them today, but any future use would silently read freed memory. Tear both down before the reload and rebuild them afterwards, mirroring AppInitMain, which constructs them only after the chainstate is loaded. connman's raw m_msgproc pointer is cleared for the duration of the reload and wired back up to the new peerman at the end. Co-Authored-By: Claude Fable 5 --- src/test/util/setup_common.cpp | 39 ++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index e1f6a8efcd52..92c68a97633a 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -326,6 +326,26 @@ void ChainTestingSetup::LoadVerifyActivateChainstate() { auto& chainman{*Assert(m_node.chainman)}; + // peerman and cj_walletman hold references to llmq_ctx, which + // LoadChainstate() recreates, and to the mempool and Dash managers, which + // the reindex path below replaces. Tear them down first and rebuild them + // afterwards, like AppInitMain constructs them only after the chainstate + // is loaded. Fixtures that never built them (e.g. plain ChainTestingSetup) + // are unaffected. + const bool rebuild_peerman{m_node.peerman != nullptr}; + if (rebuild_peerman) { + // Drop connman's raw m_msgproc pointer to the PeerManager destroyed + // below; it is wired back up after the rebuild. + CConnman::Options connman_options; + connman_options.socketEventsMode = ::g_socket_events_mode; + m_node.connman->Init(connman_options); + } + m_node.peerman.reset(); +#ifdef ENABLE_WALLET + const bool rebuild_cj_walletman{m_node.cj_walletman != nullptr}; + m_node.cj_walletman.reset(); +#endif // ENABLE_WALLET + node::ChainstateLoadOptions options{ChainstateLoadOptionsForTest()}; if (options.reindex || options.reindex_chainstate) { @@ -358,6 +378,25 @@ void ChainTestingSetup::LoadVerifyActivateChainstate() if (!chainman.ActiveChainstate().ActivateBestChain(state)) { throw std::runtime_error(strprintf("ActivateBestChain failed. (%s)", state.ToString())); } + +#ifdef ENABLE_WALLET + if (rebuild_cj_walletman) { + // The rebuilt manager starts with an empty wallet map. No present + // fixture has wallets loaded when the chainstate is reloaded; a future + // test that does must re-register them with the new manager (see + // CoinJoinLoaderImpl::AddWallet). + m_node.cj_walletman = CJWalletManager::make(chainman, *m_node.dmnman, *m_node.mn_metaman, *m_node.mempool, + *m_node.mn_sync, *m_node.isman, /*relay_txes=*/true); + } +#endif // ENABLE_WALLET + if (rebuild_peerman) { + m_node.peerman = MakePeerManager(*m_node.connman, m_node, m_node.banman.get(), + /*ignore_incoming_txs=*/false); + CConnman::Options connman_options; + connman_options.m_msgproc = m_node.peerman.get(); + connman_options.socketEventsMode = ::g_socket_events_mode; + m_node.connman->Init(connman_options); + } } TestingSetup::TestingSetup(