From 394c66b98afbc8a1f8056af0a7941990d8a9aa59 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Mon, 5 Jan 2026 00:32:31 +0530 Subject: [PATCH 01/14] refactor: use `unique_ptr` in DKG session handler map Required for upcoming commits. --- src/llmq/dkgsessionmgr.cpp | 82 ++++++++++++++++++++------------------ src/llmq/dkgsessionmgr.h | 18 +++++++-- 2 files changed, 57 insertions(+), 43 deletions(-) diff --git a/src/llmq/dkgsessionmgr.cpp b/src/llmq/dkgsessionmgr.cpp index 7397c3ebe792..679cb3974d76 100644 --- a/src/llmq/dkgsessionmgr.cpp +++ b/src/llmq/dkgsessionmgr.cpp @@ -35,7 +35,6 @@ CDKGSessionManager::CDKGSessionManager(CBLSWorker& _blsWorker, CDeterministicMNM const CActiveMasternodeManager* const mn_activeman, const ChainstateManager& chainman, const CSporkManager& sporkman, const util::DbWrapperParams& db_params, bool quorums_watch) : - db{util::MakeDbWrapper({db_params.path / "llmq" / "dkgdb", db_params.memory, db_params.wipe, /*cache_size=*/1 << 20})}, blsWorker{_blsWorker}, m_dmnman{dmnman}, dkgDebugManager{_dkgDebugManager}, @@ -43,16 +42,18 @@ CDKGSessionManager::CDKGSessionManager(CBLSWorker& _blsWorker, CDeterministicMNM m_qsnapman{qsnapman}, m_chainman{chainman}, spork_manager{sporkman}, - m_quorums_watch{quorums_watch} + m_quorums_watch{quorums_watch}, + db{util::MakeDbWrapper({db_params.path / "llmq" / "dkgdb", db_params.memory, db_params.wipe, /*cache_size=*/1 << 20})} { const Consensus::Params& consensus_params = Params().GetConsensus(); for (const auto& params : consensus_params.llmqs) { auto session_count = (params.useRotation) ? params.signingActiveQuorumCount : 1; for (const auto i : irange::range(session_count)) { dkgSessionHandlers.emplace(std::piecewise_construct, std::forward_as_tuple(params.type, i), - std::forward_as_tuple(blsWorker, m_dmnman, dkgDebugManager, *this, mn_metaman, - quorumBlockProcessor, m_qsnapman, mn_activeman, m_chainman, - spork_manager, params, m_quorums_watch, i)); + std::forward_as_tuple(std::make_unique( + blsWorker, m_dmnman, dkgDebugManager, *this, mn_metaman, + quorumBlockProcessor, m_qsnapman, mn_activeman, m_chainman, spork_manager, + params, m_quorums_watch, i))); } } } @@ -61,15 +62,15 @@ CDKGSessionManager::~CDKGSessionManager() = default; void CDKGSessionManager::StartThreads(CConnman& connman, PeerManager& peerman) { - for (auto& it : dkgSessionHandlers) { - it.second.StartThread(connman, peerman); + for (auto& [_, dkgType] : dkgSessionHandlers) { + Assert(dkgType)->StartThread(connman, peerman); } } void CDKGSessionManager::StopThreads() { - for (auto& it : dkgSessionHandlers) { - it.second.StopThread(); + for (auto& [_, dkgType] : dkgSessionHandlers) { + Assert(dkgType)->StopThread(); } } @@ -84,8 +85,8 @@ void CDKGSessionManager::UpdatedBlockTip(const CBlockIndex* pindexNew, bool fIni if (!IsQuorumDKGEnabled(spork_manager)) return; - for (auto& qt : dkgSessionHandlers) { - qt.second.UpdatedBlockTip(pindexNew); + for (auto& [_, dkgType] : dkgSessionHandlers) { + Assert(dkgType)->UpdatedBlockTip(pindexNew); } } @@ -173,7 +174,7 @@ MessageProcessingResult CDKGSessionManager::ProcessMessage(CNode& pfrom, bool is return MisbehavingError{100}; } - if (!dkgSessionHandlers.count(std::make_pair(llmqType, quorumIndex))) { + if (!dkgSessionHandlers.count({llmqType, quorumIndex})) { LogPrintf("CDKGSessionManager -- no session handlers for quorumIndex [%d]\n", quorumIndex); return MisbehavingError{100}; } @@ -181,7 +182,7 @@ MessageProcessingResult CDKGSessionManager::ProcessMessage(CNode& pfrom, bool is assert(quorumIndex != -1); WITH_LOCK(cs_indexedQuorumsCache, indexedQuorumsCache[llmqType].insert(quorumHash, quorumIndex)); - return dkgSessionHandlers.at(std::make_pair(llmqType, quorumIndex)).ProcessMessage(pfrom.GetId(), msg_type, vRecv); + return Assert(dkgSessionHandlers.at({llmqType, quorumIndex}))->ProcessMessage(pfrom.GetId(), msg_type, vRecv); } bool CDKGSessionManager::AlreadyHave(const CInv& inv) const @@ -189,12 +190,11 @@ bool CDKGSessionManager::AlreadyHave(const CInv& inv) const if (!IsQuorumDKGEnabled(spork_manager)) return false; - for (const auto& p : dkgSessionHandlers) { - const auto& dkgType = p.second; - if (dkgType.pendingContributions.HasSeen(inv.hash) - || dkgType.pendingComplaints.HasSeen(inv.hash) - || dkgType.pendingJustifications.HasSeen(inv.hash) - || dkgType.pendingPrematureCommitments.HasSeen(inv.hash)) { + for (const auto& [_, dkgType] : dkgSessionHandlers) { + if (Assert(dkgType)->pendingContributions.HasSeen(inv.hash) + || dkgType->pendingComplaints.HasSeen(inv.hash) + || dkgType->pendingJustifications.HasSeen(inv.hash) + || dkgType->pendingPrematureCommitments.HasSeen(inv.hash)) { return true; } } @@ -206,13 +206,14 @@ bool CDKGSessionManager::GetContribution(const uint256& hash, CDKGContribution& if (!IsQuorumDKGEnabled(spork_manager)) return false; - for (const auto& p : dkgSessionHandlers) { - const auto& dkgType = p.second; - LOCK(dkgType.cs_phase_qhash); - if (dkgType.phase < QuorumPhase::Initialized || dkgType.phase > QuorumPhase::Contribute) { + for (const auto& [_, dkgType] : dkgSessionHandlers) { + assert(dkgType); + LOCK(dkgType->cs_phase_qhash); + const auto dkgPhase = dkgType->phase; + if (dkgPhase < QuorumPhase::Initialized || dkgPhase > QuorumPhase::Contribute) { continue; } - if (dkgType.GetContribution(hash, ret)) { + if (dkgType->GetContribution(hash, ret)) { return true; } } @@ -224,13 +225,14 @@ bool CDKGSessionManager::GetComplaint(const uint256& hash, CDKGComplaint& ret) c if (!IsQuorumDKGEnabled(spork_manager)) return false; - for (const auto& p : dkgSessionHandlers) { - const auto& dkgType = p.second; - LOCK(dkgType.cs_phase_qhash); - if (dkgType.phase < QuorumPhase::Contribute || dkgType.phase > QuorumPhase::Complain) { + for (const auto& [_, dkgType] : dkgSessionHandlers) { + assert(dkgType); + LOCK(dkgType->cs_phase_qhash); + const auto dkgPhase = dkgType->phase; + if (dkgPhase < QuorumPhase::Contribute || dkgPhase > QuorumPhase::Complain) { continue; } - if (dkgType.GetComplaint(hash, ret)) { + if (dkgType->GetComplaint(hash, ret)) { return true; } } @@ -242,13 +244,14 @@ bool CDKGSessionManager::GetJustification(const uint256& hash, CDKGJustification if (!IsQuorumDKGEnabled(spork_manager)) return false; - for (const auto& p : dkgSessionHandlers) { - const auto& dkgType = p.second; - LOCK(dkgType.cs_phase_qhash); - if (dkgType.phase < QuorumPhase::Complain || dkgType.phase > QuorumPhase::Justify) { + for (const auto& [_, dkgType] : dkgSessionHandlers) { + assert(dkgType); + LOCK(dkgType->cs_phase_qhash); + const auto dkgPhase = dkgType->phase; + if (dkgPhase < QuorumPhase::Complain || dkgPhase > QuorumPhase::Justify) { continue; } - if (dkgType.GetJustification(hash, ret)) { + if (dkgType->GetJustification(hash, ret)) { return true; } } @@ -260,13 +263,14 @@ bool CDKGSessionManager::GetPrematureCommitment(const uint256& hash, CDKGPrematu if (!IsQuorumDKGEnabled(spork_manager)) return false; - for (const auto& p : dkgSessionHandlers) { - const auto& dkgType = p.second; - LOCK(dkgType.cs_phase_qhash); - if (dkgType.phase < QuorumPhase::Justify || dkgType.phase > QuorumPhase::Commit) { + for (const auto& [_, dkgType] : dkgSessionHandlers) { + assert(dkgType); + LOCK(dkgType->cs_phase_qhash); + const auto dkgPhase = dkgType->phase; + if (dkgPhase < QuorumPhase::Justify || dkgPhase > QuorumPhase::Commit) { continue; } - if (dkgType.GetPrematureCommitment(hash, ret)) { + if (dkgType->GetPrematureCommitment(hash, ret)) { return true; } } diff --git a/src/llmq/dkgsessionmgr.h b/src/llmq/dkgsessionmgr.h index 72f884c5285d..75a6d4546b18 100644 --- a/src/llmq/dkgsessionmgr.h +++ b/src/llmq/dkgsessionmgr.h @@ -45,11 +45,19 @@ class CQuorumSnapshotManager; class CDKGSessionManager { - static constexpr int64_t MAX_CONTRIBUTION_CACHE_TIME = 60 * 1000; +public: + struct SessionHandlerKey { + Consensus::LLMQType llmq_type; + int quorum_idx; + auto operator<=>(const SessionHandlerKey&) const = default; + }; + + using SessionHandlerMap = std::map>; private: - std::unique_ptr db{nullptr}; + static constexpr int64_t MAX_CONTRIBUTION_CACHE_TIME = 60 * 1000; +private: CBLSWorker& blsWorker; CDeterministicMNManager& m_dmnman; CDKGDebugManager& dkgDebugManager; @@ -59,8 +67,10 @@ class CDKGSessionManager const CSporkManager& spork_manager; const bool m_quorums_watch{false}; - //TODO name struct instead of std::pair - std::map, CDKGSessionHandler> dkgSessionHandlers; +private: + std::unique_ptr db{nullptr}; + + SessionHandlerMap dkgSessionHandlers; mutable Mutex contributionsCacheCs; struct ContributionsCacheKey { From 831dbfe78df88f0900b7aa583fe38b52a1289241 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Tue, 9 Dec 2025 04:22:47 +0530 Subject: [PATCH 02/14] refactor: move DKG session handler init out of ctor --- src/llmq/dkgsessionmgr.cpp | 35 ++++++++----------------------- src/llmq/dkgsessionmgr.h | 27 ++++++++++++++++-------- src/llmq/observer/context.cpp | 11 ++++++++-- src/masternode/active/context.cpp | 12 ++++++++--- 4 files changed, 45 insertions(+), 40 deletions(-) diff --git a/src/llmq/dkgsessionmgr.cpp b/src/llmq/dkgsessionmgr.cpp index 679cb3974d76..b879ef8f0b61 100644 --- a/src/llmq/dkgsessionmgr.cpp +++ b/src/llmq/dkgsessionmgr.cpp @@ -29,33 +29,16 @@ static const std::string DB_VVEC = "qdkg_V"; static const std::string DB_SKCONTRIB = "qdkg_S"; static const std::string DB_ENC_CONTRIB = "qdkg_E"; -CDKGSessionManager::CDKGSessionManager(CBLSWorker& _blsWorker, CDeterministicMNManager& dmnman, - CDKGDebugManager& _dkgDebugManager, CMasternodeMetaMan& mn_metaman, - CQuorumBlockProcessor& _quorumBlockProcessor, CQuorumSnapshotManager& qsnapman, - const CActiveMasternodeManager* const mn_activeman, +CDKGSessionManager::CDKGSessionManager(CDeterministicMNManager& dmnman, CQuorumSnapshotManager& qsnapman, const ChainstateManager& chainman, const CSporkManager& sporkman, const util::DbWrapperParams& db_params, bool quorums_watch) : - blsWorker{_blsWorker}, m_dmnman{dmnman}, - dkgDebugManager{_dkgDebugManager}, - quorumBlockProcessor{_quorumBlockProcessor}, m_qsnapman{qsnapman}, m_chainman{chainman}, - spork_manager{sporkman}, + m_sporkman{sporkman}, m_quorums_watch{quorums_watch}, db{util::MakeDbWrapper({db_params.path / "llmq" / "dkgdb", db_params.memory, db_params.wipe, /*cache_size=*/1 << 20})} { - const Consensus::Params& consensus_params = Params().GetConsensus(); - for (const auto& params : consensus_params.llmqs) { - auto session_count = (params.useRotation) ? params.signingActiveQuorumCount : 1; - for (const auto i : irange::range(session_count)) { - dkgSessionHandlers.emplace(std::piecewise_construct, std::forward_as_tuple(params.type, i), - std::forward_as_tuple(std::make_unique( - blsWorker, m_dmnman, dkgDebugManager, *this, mn_metaman, - quorumBlockProcessor, m_qsnapman, mn_activeman, m_chainman, spork_manager, - params, m_quorums_watch, i))); - } - } } CDKGSessionManager::~CDKGSessionManager() = default; @@ -82,7 +65,7 @@ void CDKGSessionManager::UpdatedBlockTip(const CBlockIndex* pindexNew, bool fIni return; if (!DeploymentDIP0003Enforced(pindexNew->nHeight, Params().GetConsensus())) return; - if (!IsQuorumDKGEnabled(spork_manager)) + if (!IsQuorumDKGEnabled(m_sporkman)) return; for (auto& [_, dkgType] : dkgSessionHandlers) { @@ -96,7 +79,7 @@ MessageProcessingResult CDKGSessionManager::ProcessMessage(CNode& pfrom, bool is static Mutex cs_indexedQuorumsCache; static std::map> indexedQuorumsCache GUARDED_BY(cs_indexedQuorumsCache); - if (!IsQuorumDKGEnabled(spork_manager)) + if (!IsQuorumDKGEnabled(m_sporkman)) return {}; if (msg_type != NetMsgType::QCONTRIB @@ -187,7 +170,7 @@ MessageProcessingResult CDKGSessionManager::ProcessMessage(CNode& pfrom, bool is bool CDKGSessionManager::AlreadyHave(const CInv& inv) const { - if (!IsQuorumDKGEnabled(spork_manager)) + if (!IsQuorumDKGEnabled(m_sporkman)) return false; for (const auto& [_, dkgType] : dkgSessionHandlers) { @@ -203,7 +186,7 @@ bool CDKGSessionManager::AlreadyHave(const CInv& inv) const bool CDKGSessionManager::GetContribution(const uint256& hash, CDKGContribution& ret) const { - if (!IsQuorumDKGEnabled(spork_manager)) + if (!IsQuorumDKGEnabled(m_sporkman)) return false; for (const auto& [_, dkgType] : dkgSessionHandlers) { @@ -222,7 +205,7 @@ bool CDKGSessionManager::GetContribution(const uint256& hash, CDKGContribution& bool CDKGSessionManager::GetComplaint(const uint256& hash, CDKGComplaint& ret) const { - if (!IsQuorumDKGEnabled(spork_manager)) + if (!IsQuorumDKGEnabled(m_sporkman)) return false; for (const auto& [_, dkgType] : dkgSessionHandlers) { @@ -241,7 +224,7 @@ bool CDKGSessionManager::GetComplaint(const uint256& hash, CDKGComplaint& ret) c bool CDKGSessionManager::GetJustification(const uint256& hash, CDKGJustification& ret) const { - if (!IsQuorumDKGEnabled(spork_manager)) + if (!IsQuorumDKGEnabled(m_sporkman)) return false; for (const auto& [_, dkgType] : dkgSessionHandlers) { @@ -260,7 +243,7 @@ bool CDKGSessionManager::GetJustification(const uint256& hash, CDKGJustification bool CDKGSessionManager::GetPrematureCommitment(const uint256& hash, CDKGPrematureCommitment& ret) const { - if (!IsQuorumDKGEnabled(spork_manager)) + if (!IsQuorumDKGEnabled(m_sporkman)) return false; for (const auto& [_, dkgType] : dkgSessionHandlers) { diff --git a/src/llmq/dkgsessionmgr.h b/src/llmq/dkgsessionmgr.h index 75a6d4546b18..56a13e7be285 100644 --- a/src/llmq/dkgsessionmgr.h +++ b/src/llmq/dkgsessionmgr.h @@ -9,6 +9,8 @@ #include #include #include +#include + #include #include @@ -58,13 +60,10 @@ class CDKGSessionManager static constexpr int64_t MAX_CONTRIBUTION_CACHE_TIME = 60 * 1000; private: - CBLSWorker& blsWorker; CDeterministicMNManager& m_dmnman; - CDKGDebugManager& dkgDebugManager; - CQuorumBlockProcessor& quorumBlockProcessor; CQuorumSnapshotManager& m_qsnapman; const ChainstateManager& m_chainman; - const CSporkManager& spork_manager; + const CSporkManager& m_sporkman; const bool m_quorums_watch{false}; private: @@ -95,13 +94,23 @@ class CDKGSessionManager CDKGSessionManager() = delete; CDKGSessionManager(const CDKGSessionManager&) = delete; CDKGSessionManager& operator=(const CDKGSessionManager&) = delete; - explicit CDKGSessionManager(CBLSWorker& _blsWorker, CDeterministicMNManager& dmnman, - CDKGDebugManager& _dkgDebugManager, CMasternodeMetaMan& mn_metaman, - CQuorumBlockProcessor& _quorumBlockProcessor, CQuorumSnapshotManager& qsnapman, - const CActiveMasternodeManager* const mn_activeman, const ChainstateManager& chainman, - const CSporkManager& sporkman, const util::DbWrapperParams& db_params, bool quorums_watch); + explicit CDKGSessionManager(CDeterministicMNManager& dmnman, CQuorumSnapshotManager& qsnapman, + const ChainstateManager& chainman, const CSporkManager& sporkman, + const util::DbWrapperParams& db_params, bool quorums_watch); ~CDKGSessionManager(); + template + void InitializeHandlers(HandlerFn&& handler_fn) + { + const Consensus::Params& consensus_params = Params().GetConsensus(); + for (const auto& params : consensus_params.llmqs) { + auto session_count = (params.useRotation) ? params.signingActiveQuorumCount : 1; + for (const auto i : irange::range(session_count)) { + dkgSessionHandlers.emplace(SessionHandlerKey{params.type, i}, handler_fn(params, i)); + } + } + } + void StartThreads(CConnman& connman, PeerManager& peerman); void StopThreads(); diff --git a/src/llmq/observer/context.cpp b/src/llmq/observer/context.cpp index bc111c068a3d..ffce306bb3cf 100644 --- a/src/llmq/observer/context.cpp +++ b/src/llmq/observer/context.cpp @@ -9,6 +9,8 @@ #include #include +#include + namespace llmq { ObserverContext::ObserverContext(CBLSWorker& bls_worker, CConnman& connman, CDeterministicMNManager& dmnman, CMasternodeMetaMan& mn_metaman, CMasternodeSync& mn_sync, @@ -18,12 +20,17 @@ ObserverContext::ObserverContext(CBLSWorker& bls_worker, CConnman& connman, CDet const util::DbWrapperParams& db_params, bool quorums_recovery) : m_qman{qman}, dkgdbgman{std::make_unique()}, - qdkgsman{std::make_unique(bls_worker, dmnman, *dkgdbgman, mn_metaman, qblockman, qsnapman, - /*mn_activeman=*/nullptr, chainman, sporkman, db_params, + qdkgsman{std::make_unique(dmnman, qsnapman, chainman, sporkman, db_params, /*quorums_watch=*/true)}, qman_handler{std::make_unique(connman, dmnman, qman, qsnapman, chainman, mn_sync, sporkman, sync_map, quorums_recovery)} { + qdkgsman->InitializeHandlers( + [&](const Consensus::LLMQParams& llmq_params, int quorum_idx) -> std::unique_ptr { + return std::make_unique(bls_worker, dmnman, *dkgdbgman, *qdkgsman, mn_metaman, + qblockman, qsnapman, /*mn_activeman=*/nullptr, chainman, + sporkman, llmq_params, /*quorums_watch=*/true, quorum_idx); + }); m_qman.ConnectManagers(qman_handler.get(), qdkgsman.get()); } diff --git a/src/masternode/active/context.cpp b/src/masternode/active/context.cpp index 84132b99ffa0..f6c500b83972 100644 --- a/src/masternode/active/context.cpp +++ b/src/masternode/active/context.cpp @@ -29,9 +29,8 @@ ActiveContext::ActiveContext(CCoinJoinServer& cj_server, CConnman& connman, CDet m_cj_server(cj_server), gov_signer{std::make_unique(connman, dmnman, govman, mn_activeman, chainman, mn_sync)}, dkgdbgman{std::make_unique()}, - qdkgsman{std::make_unique(*llmq_ctx.bls_worker, dmnman, *dkgdbgman, mn_metaman, - *llmq_ctx.quorum_block_processor, *llmq_ctx.qsnapman, - &mn_activeman, chainman, sporkman, db_params, quorums_watch)}, + qdkgsman{std::make_unique(dmnman, *llmq_ctx.qsnapman, chainman, sporkman, db_params, + quorums_watch)}, shareman{std::make_unique(connman, chainman.ActiveChainstate(), *llmq_ctx.sigman, peerman, mn_activeman, *llmq_ctx.qman, sporkman)}, ehf_sighandler{ @@ -45,6 +44,13 @@ ActiveContext::ActiveContext(CCoinJoinServer& cj_server, CConnman& connman, CDet *llmq_ctx.isman, *llmq_ctx.sigman, *shareman, *llmq_ctx.qman, sporkman, mempool, mn_sync)} { + qdkgsman->InitializeHandlers( + [&](const Consensus::LLMQParams& llmq_params, int quorum_idx) -> std::unique_ptr { + return std::make_unique(*llmq_ctx.bls_worker, dmnman, *dkgdbgman, *qdkgsman, + mn_metaman, *llmq_ctx.quorum_block_processor, + *llmq_ctx.qsnapman, &mn_activeman, chainman, sporkman, + llmq_params, quorums_watch, quorum_idx); + }); m_llmq_ctx.clhandler->ConnectSigner(cl_signer.get()); m_llmq_ctx.isman->ConnectSigner(is_signer.get()); m_llmq_ctx.qman->ConnectManagers(qman_handler.get(), qdkgsman.get()); From 1bdf8366e17f2f6f7c1e567c55a7cb12faceb97f Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Tue, 6 Jan 2026 22:45:16 +0530 Subject: [PATCH 03/14] move-only: move DKG session handler participant logic to `llmq/active` Review with `git log -p -n1 --color-moved=dimmed_zebra`. --- src/Makefile.am | 1 + src/active/dkgsessionhandler.cpp | 553 +++++++++++++++++++++++++++++++ src/llmq/dkgsessionhandler.cpp | 534 ----------------------------- 3 files changed, 554 insertions(+), 534 deletions(-) create mode 100644 src/active/dkgsessionhandler.cpp diff --git a/src/Makefile.am b/src/Makefile.am index a7a0819ce088..77454ba65c74 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -482,6 +482,7 @@ libbitcoin_util_a-clientversion.$(OBJEXT): obj/build.h libbitcoin_node_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BOOST_CPPFLAGS) $(MINIUPNPC_CPPFLAGS) $(NATPMP_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS) libbitcoin_node_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_node_a_SOURCES = \ + active/dkgsessionhandler.cpp \ active/quorums.cpp \ addrdb.cpp \ addressindex.cpp \ diff --git a/src/active/dkgsessionhandler.cpp b/src/active/dkgsessionhandler.cpp new file mode 100644 index 000000000000..1c1cf356381d --- /dev/null +++ b/src/active/dkgsessionhandler.cpp @@ -0,0 +1,553 @@ +// Copyright (c) 2018-2025 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace llmq { +void CDKGSessionHandler::UpdatedBlockTip(const CBlockIndex* pindexNew) +{ + //AssertLockNotHeld(cs_main); + //Indexed quorums (greater than 0) are enabled with Quorum Rotation + if (quorumIndex > 0 && !IsQuorumRotationEnabled(params, pindexNew)) { + return; + } + LOCK(cs_phase_qhash); + + int quorumStageInt = (pindexNew->nHeight - quorumIndex) % params.dkgInterval; + + const CBlockIndex* pQuorumBaseBlockIndex = pindexNew->GetAncestor(pindexNew->nHeight - quorumStageInt); + + currentHeight = pindexNew->nHeight; + quorumHash = pQuorumBaseBlockIndex->GetBlockHash(); + + bool fNewPhase = (quorumStageInt % params.dkgPhaseBlocks) == 0; + int phaseInt = quorumStageInt / params.dkgPhaseBlocks + 1; + QuorumPhase oldPhase = phase; + if (fNewPhase && phaseInt >= ToUnderlying(QuorumPhase::Initialized) && phaseInt <= ToUnderlying(QuorumPhase::Idle)) { + phase = static_cast(phaseInt); + } + + LogPrint(BCLog::LLMQ_DKG, "CDKGSessionHandler::%s -- %s qi[%d] currentHeight=%d, pQuorumBaseBlockIndex->nHeight=%d, oldPhase=%d, newPhase=%d\n", __func__, + params.name, quorumIndex, currentHeight, pQuorumBaseBlockIndex->nHeight, ToUnderlying(oldPhase), ToUnderlying(phase)); +} + +void CDKGSessionHandler::StartThread(CConnman& connman, PeerManager& peerman) +{ + if (phaseHandlerThread.joinable()) { + throw std::runtime_error("Tried to start an already started CDKGSessionHandler thread."); + } + + m_thread_name = strprintf("llmq-%d-%d", ToUnderlying(params.type), quorumIndex); + phaseHandlerThread = std::thread(&util::TraceThread, m_thread_name.c_str(), + [this, &connman, &peerman] { PhaseHandlerThread(connman, peerman); }); +} + +void CDKGSessionHandler::StopThread() +{ + stopRequested = true; + if (phaseHandlerThread.joinable()) { + phaseHandlerThread.join(); + } +} + +std::pair CDKGSessionHandler::GetPhaseAndQuorumHash() const +{ + LOCK(cs_phase_qhash); + return std::make_pair(phase, quorumHash); +} + +bool CDKGSessionHandler::InitNewQuorum(const CBlockIndex* pQuorumBaseBlockIndex) +{ + if (!DeploymentDIP0003Enforced(pQuorumBaseBlockIndex->nHeight, Params().GetConsensus())) { + return false; + } + + curSession = std::make_unique(pQuorumBaseBlockIndex, params, blsWorker, m_dmnman, dkgManager, + dkgDebugManager, m_mn_metaman, m_qsnapman, m_mn_activeman, m_chainman, + m_sporkman); + + if (!curSession->Init(m_mn_activeman->GetProTxHash(), quorumIndex)) { + LogPrintf("CDKGSessionManager::%s -- height[%d] quorum initialization failed for %s qi[%d]\n", __func__, + pQuorumBaseBlockIndex->nHeight, curSession->params.name, quorumIndex); + return false; + } + + LogPrintf("CDKGSessionManager::%s -- height[%d] quorum initialization OK for %s qi[%d]\n", __func__, pQuorumBaseBlockIndex->nHeight, curSession->params.name, quorumIndex); + return true; +} + +class AbortPhaseException : public std::exception { +}; + +void CDKGSessionHandler::WaitForNextPhase(std::optional curPhase, + QuorumPhase nextPhase, + const uint256& expectedQuorumHash, + const WhileWaitFunc& shouldNotWait) const +{ + LogPrint(BCLog::LLMQ_DKG, "CDKGSessionManager::%s -- %s qi[%d] - starting, curPhase=%d, nextPhase=%d\n", __func__, params.name, quorumIndex, curPhase.has_value() ? ToUnderlying(*curPhase) : -1, ToUnderlying(nextPhase)); + + while (true) { + if (stopRequested) { + LogPrint(BCLog::LLMQ_DKG, "CDKGSessionManager::%s -- %s qi[%d] - aborting due to stop/shutdown requested\n", __func__, params.name, quorumIndex); + throw AbortPhaseException(); + } + auto [_phase, _quorumHash] = GetPhaseAndQuorumHash(); + if (!expectedQuorumHash.IsNull() && _quorumHash != expectedQuorumHash) { + LogPrint(BCLog::LLMQ_DKG, "CDKGSessionManager::%s -- %s qi[%d] - aborting due unexpected expectedQuorumHash change\n", __func__, params.name, quorumIndex); + throw AbortPhaseException(); + } + if (_phase == nextPhase) { + break; + } + if (curPhase.has_value() && _phase != curPhase) { + LogPrint(BCLog::LLMQ_DKG, "CDKGSessionManager::%s -- %s qi[%d] - aborting due unexpected phase change, _phase=%d, curPhase=%d\n", __func__, params.name, quorumIndex, ToUnderlying(_phase), curPhase.has_value() ? ToUnderlying(*curPhase) : -1); + throw AbortPhaseException(); + } + if (!shouldNotWait()) { + UninterruptibleSleep(std::chrono::milliseconds{100}); + } + } + + LogPrint(BCLog::LLMQ_DKG, "CDKGSessionManager::%s -- %s qi[%d] - done, curPhase=%d, nextPhase=%d\n", __func__, params.name, quorumIndex, curPhase.has_value() ? ToUnderlying(*curPhase) : -1, ToUnderlying(nextPhase)); + + if (nextPhase == QuorumPhase::Initialized) { + dkgDebugManager.ResetLocalSessionStatus(params.type, quorumIndex); + } else { + dkgDebugManager.UpdateLocalSessionStatus(params.type, quorumIndex, [&](CDKGDebugSessionStatus& status) { + bool changed = status.phase != nextPhase; + status.phase = nextPhase; + return changed; + }); + } +} + +void CDKGSessionHandler::WaitForNewQuorum(const uint256& oldQuorumHash) const +{ + LogPrint(BCLog::LLMQ_DKG, "CDKGSessionManager::%s -- %s qi[%d]- starting\n", __func__, params.name, quorumIndex); + + while (true) { + if (stopRequested) { + LogPrint(BCLog::LLMQ_DKG, "CDKGSessionManager::%s -- %s qi[%d] - aborting due to stop/shutdown requested\n", __func__, params.name, quorumIndex); + throw AbortPhaseException(); + } + auto [_, _quorumHash] = GetPhaseAndQuorumHash(); + if (_quorumHash != oldQuorumHash) { + break; + } + UninterruptibleSleep(std::chrono::milliseconds{100}); + } + + LogPrint(BCLog::LLMQ_DKG, "CDKGSessionManager::%s -- %s qi[%d] - done\n", __func__, params.name, quorumIndex); +} + +// Sleep some time to not fully overload the whole network +void CDKGSessionHandler::SleepBeforePhase(QuorumPhase curPhase, + const uint256& expectedQuorumHash, + double randomSleepFactor, + const WhileWaitFunc& runWhileWaiting) const +{ + if (!curSession->AreWeMember()) { + // Non-members do not participate and do not create any network load, no need to sleep. + return; + } + + if (Params().MineBlocksOnDemand()) { + // On regtest, blocks can be mined on demand without any significant time passing between these. + // We shouldn't wait before phases in this case. + return; + } + + // Two blocks can come very close to each other, this happens pretty regularly. We don't want to be + // left behind and marked as a bad member. This means that we should not count the last block of the + // phase as a safe one to keep sleeping, that's why we calculate the phase sleep time as a time of + // the full phase minus one block here. + double phaseSleepTime = (params.dkgPhaseBlocks - 1) * Params().GetConsensus().nPowTargetSpacing * 1000; + // Expected phase sleep time per member + double phaseSleepTimePerMember = phaseSleepTime / params.size; + // Don't expect perfect block times and thus reduce the phase time to be on the secure side (caller chooses factor) + double adjustedPhaseSleepTimePerMember = phaseSleepTimePerMember * randomSleepFactor; + + int64_t sleepTime = (int64_t)(adjustedPhaseSleepTimePerMember * curSession->GetMyMemberIndex().value_or(0)); + int64_t endTime = TicksSinceEpoch(SystemClock::now()) + sleepTime; + int heightTmp{currentHeight.load()}; + int heightStart{heightTmp}; + + LogPrint(BCLog::LLMQ_DKG, "CDKGSessionManager::%s -- %s qi[%d] - starting sleep for %d ms, curPhase=%d\n", __func__, params.name, quorumIndex, sleepTime, ToUnderlying(curPhase)); + + while (TicksSinceEpoch(SystemClock::now()) < endTime) { + if (stopRequested) { + LogPrint(BCLog::LLMQ_DKG, "CDKGSessionManager::%s -- %s qi[%d] - aborting due to stop/shutdown requested\n", __func__, params.name, quorumIndex); + throw AbortPhaseException(); + } + auto cur_height = currentHeight.load(); + if (cur_height > heightTmp) { + // New block(s) just came in + int64_t expectedBlockTime = (cur_height - heightStart) * Params().GetConsensus().nPowTargetSpacing * 1000; + if (expectedBlockTime > sleepTime) { + // Blocks came faster than we expected, jump into the phase func asap + break; + } + heightTmp = cur_height; + } + if (WITH_LOCK(cs_phase_qhash, return phase != curPhase || quorumHash != expectedQuorumHash)) { + // Something went wrong and/or we missed quite a few blocks and it's just too late now + LogPrint(BCLog::LLMQ_DKG, "CDKGSessionManager::%s -- %s qi[%d] - aborting due unexpected phase/expectedQuorumHash change\n", __func__, params.name, quorumIndex); + throw AbortPhaseException(); + } + if (!runWhileWaiting()) { + UninterruptibleSleep(std::chrono::milliseconds{100}); + } + } + + LogPrint(BCLog::LLMQ_DKG, "CDKGSessionManager::%s -- %s qi[%d] - done, curPhase=%d\n", __func__, params.name, quorumIndex, ToUnderlying(curPhase)); +} + +void CDKGSessionHandler::HandlePhase(QuorumPhase curPhase, + QuorumPhase nextPhase, + const uint256& expectedQuorumHash, + double randomSleepFactor, + const StartPhaseFunc& startPhaseFunc, + const WhileWaitFunc& runWhileWaiting) +{ + LogPrint(BCLog::LLMQ_DKG, "CDKGSessionManager::%s -- %s qi[%d] - starting, curPhase=%d, nextPhase=%d\n", __func__, params.name, quorumIndex, ToUnderlying(curPhase), ToUnderlying(nextPhase)); + + SleepBeforePhase(curPhase, expectedQuorumHash, randomSleepFactor, runWhileWaiting); + startPhaseFunc(); + WaitForNextPhase(curPhase, nextPhase, expectedQuorumHash, runWhileWaiting); + + LogPrint(BCLog::LLMQ_DKG, "CDKGSessionManager::%s -- %s qi[%d] - done, curPhase=%d, nextPhase=%d\n", __func__, params.name, quorumIndex, ToUnderlying(curPhase), ToUnderlying(nextPhase)); +} + +// returns a set of NodeIds which sent invalid messages +template +std::set BatchVerifyMessageSigs(CDKGSession& session, const std::vector>>& messages) +{ + if (messages.empty()) { + return {}; + } + + std::set ret; + bool revertToSingleVerification = false; + + CBLSSignature aggSig; + std::vector pubKeys; + std::vector messageHashes; + std::set messageHashesSet; + pubKeys.reserve(messages.size()); + messageHashes.reserve(messages.size()); + bool first = true; + for (const auto& [nodeId, msg] : messages) { + auto member = session.GetMember(msg->proTxHash); + if (!member) { + // should not happen as it was verified before + ret.emplace(nodeId); + continue; + } + + if (first) { + aggSig = msg->sig; + } else { + aggSig.AggregateInsecure(msg->sig); + } + first = false; + + auto msgHash = msg->GetSignHash(); + if (!messageHashesSet.emplace(msgHash).second) { + // can only happen in 2 cases: + // 1. Someone sent us the same message twice but with differing signature, meaning that at least one of them + // must be invalid. In this case, we'd have to revert to single message verification nevertheless + // 2. Someone managed to find a way to create two different binary representations of a message that deserializes + // to the same object representation. This would be some form of malleability. However, this shouldn't be + // possible as only deterministic/unique BLS signatures and very simple data types are involved + revertToSingleVerification = true; + break; + } + + pubKeys.emplace_back(member->dmn->pdmnState->pubKeyOperator.Get()); + messageHashes.emplace_back(msgHash); + } + if (!revertToSingleVerification) { + if (aggSig.VerifyInsecureAggregated(pubKeys, messageHashes)) { + // all good + return ret; + } + + // are all messages from the same node? + bool nodeIdsAllSame = std::adjacent_find( messages.begin(), messages.end(), [](const auto& first, const auto& second){ + return first.first != second.first; + }) == messages.end(); + + // if yes, take a short path and return a set with only him + if (nodeIdsAllSame) { + ret.emplace(messages[0].first); + return ret; + } + // different nodes, let's figure out who are the bad ones + } + + for (const auto& [nodeId, msg] : messages) { + if (ret.count(nodeId)) { + continue; + } + + auto member = session.GetMember(msg->proTxHash); + bool valid = msg->sig.VerifyInsecure(member->dmn->pdmnState->pubKeyOperator.Get(), msg->GetSignHash()); + if (!valid) { + ret.emplace(nodeId); + } + } + return ret; +} + +static void RelayInvToParticipants(const CDKGSession& session, const CConnman& connman, PeerManager& peerman, + const CInv& inv) +{ + CDKGLogger logger(session, __func__, __LINE__); + std::stringstream ss; + const auto& relayMembers = session.RelayMembers(); + for (const auto& r : relayMembers) { + ss << r.ToString().substr(0, 4) << " | "; + } + logger.Batch("RelayInvToParticipants inv[%s] relayMembers[%d] GetNodeCount[%d] GetNetworkActive[%d] " + "HasMasternodeQuorumNodes[%d] for quorumHash[%s] forMember[%s] relayMembers[%s]", + inv.ToString(), relayMembers.size(), connman.GetNodeCount(ConnectionDirection::Both), + connman.GetNetworkActive(), + connman.HasMasternodeQuorumNodes(session.GetParams().type, session.BlockIndex()->GetBlockHash()), + session.BlockIndex()->GetBlockHash().ToString(), session.ProTx().ToString().substr(0, 4), ss.str()); + + std::stringstream ss2; + connman.ForEachNode([&](const CNode* pnode) { + if (pnode->qwatch || + (!pnode->GetVerifiedProRegTxHash().IsNull() && (relayMembers.count(pnode->GetVerifiedProRegTxHash()) != 0))) { + peerman.PushInventory(pnode->GetId(), inv); + } + + if (pnode->GetVerifiedProRegTxHash().IsNull()) { + logger.Batch("node[%d:%s] not mn", pnode->GetId(), pnode->m_addr_name); + } else if (relayMembers.count(pnode->GetVerifiedProRegTxHash()) == 0) { + ss2 << pnode->GetVerifiedProRegTxHash().ToString().substr(0, 4) << " | "; + } + }); + logger.Batch("forMember[%s] NOTrelayMembers[%s]", session.ProTx().ToString().substr(0, 4), ss2.str()); + logger.Flush(); +} + +template +bool ProcessPendingMessageBatch(const CConnman& connman, CDKGSession& session, CDKGPendingMessages& pendingMessages, + PeerManager& peerman, size_t maxCount) +{ + auto msgs = pendingMessages.PopAndDeserializeMessages(maxCount); + if (msgs.empty()) { + return false; + } + + std::vector>> preverifiedMessages; + preverifiedMessages.reserve(msgs.size()); + + for (const auto& p : msgs) { + const NodeId &nodeId = p.first; + if (!p.second) { + LogPrint(BCLog::LLMQ_DKG, "%s -- failed to deserialize message, peer=%d\n", __func__, nodeId); + { + pendingMessages.Misbehaving(nodeId, 100, peerman); + } + continue; + } + bool ban = false; + if (!session.PreVerifyMessage(*p.second, ban)) { + if (ban) { + LogPrint(BCLog::LLMQ_DKG, "%s -- banning node due to failed preverification, peer=%d\n", __func__, nodeId); + { + pendingMessages.Misbehaving(nodeId, 100, peerman); + } + } + LogPrint(BCLog::LLMQ_DKG, "%s -- skipping message due to failed preverification, peer=%d\n", __func__, nodeId); + continue; + } + preverifiedMessages.emplace_back(p); + } + if (preverifiedMessages.empty()) { + return true; + } + + auto badNodes = BatchVerifyMessageSigs(session, preverifiedMessages); + if (!badNodes.empty()) { + for (auto nodeId : badNodes) { + LogPrint(BCLog::LLMQ_DKG, "%s -- failed to verify signature, peer=%d\n", __func__, nodeId); + pendingMessages.Misbehaving(nodeId, 100, peerman); + } + } + + for (const auto& p : preverifiedMessages) { + const NodeId &nodeId = p.first; + if (badNodes.count(nodeId)) { + continue; + } + const std::optional inv = session.ReceiveMessage(*p.second); + if (inv) { + RelayInvToParticipants(session, connman, peerman, *inv); + } + } + + return true; +} + +void CDKGSessionHandler::HandleDKGRound(CConnman& connman, PeerManager& peerman) +{ + WaitForNextPhase(std::nullopt, QuorumPhase::Initialized); + + pendingContributions.Clear(); + pendingComplaints.Clear(); + pendingJustifications.Clear(); + pendingPrematureCommitments.Clear(); + uint256 curQuorumHash = WITH_LOCK(cs_phase_qhash, return quorumHash); + + const CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(::cs_main, + return m_chainman.m_blockman.LookupBlockIndex(curQuorumHash)); + + if (!pQuorumBaseBlockIndex || !InitNewQuorum(pQuorumBaseBlockIndex)) { + // should actually never happen + WaitForNewQuorum(curQuorumHash); + throw AbortPhaseException(); + } + + dkgDebugManager.UpdateLocalSessionStatus(params.type, quorumIndex, [&](CDKGDebugSessionStatus& status) { + bool changed = status.phase != QuorumPhase::Initialized; + status.phase = QuorumPhase::Initialized; + return changed; + }); + + if (params.is_single_member()) { + auto finalCommitment = curSession->FinalizeSingleCommitment(); + if (!finalCommitment.IsNull()) { // it can be null only if we are not member + if (auto inv_opt = quorumBlockProcessor.AddMineableCommitment(finalCommitment); inv_opt.has_value()) { + peerman.RelayInv(inv_opt.value()); + } + } + WaitForNextPhase(QuorumPhase::Initialized, QuorumPhase::Contribute, curQuorumHash); + return; + } + + const auto tip_mn_list = m_dmnman.GetListAtChainTip(); + utils::EnsureQuorumConnections(params, connman, m_sporkman, {m_dmnman, m_qsnapman, m_chainman, pQuorumBaseBlockIndex}, tip_mn_list, + curSession->myProTxHash, /*is_masternode=*/m_mn_activeman != nullptr, m_quorums_watch); + if (curSession->AreWeMember()) { + utils::AddQuorumProbeConnections(params, connman, m_mn_metaman, m_sporkman, {m_dmnman, m_qsnapman, m_chainman, + pQuorumBaseBlockIndex}, tip_mn_list, curSession->myProTxHash); + } + + WaitForNextPhase(QuorumPhase::Initialized, QuorumPhase::Contribute, curQuorumHash); + + // Contribute + auto fContributeStart = [this, &peerman]() { curSession->Contribute(pendingContributions, peerman); }; + auto fContributeWait = [this, &connman, &peerman] { + return ProcessPendingMessageBatch(connman, *curSession, pendingContributions, peerman, 8); + }; + HandlePhase(QuorumPhase::Contribute, QuorumPhase::Complain, curQuorumHash, 0.05, fContributeStart, fContributeWait); + + // Complain + auto fComplainStart = [this, &connman, &peerman]() { + curSession->VerifyAndComplain(connman, pendingComplaints, peerman); + }; + auto fComplainWait = [this, &connman, &peerman] { + return ProcessPendingMessageBatch(connman, *curSession, pendingComplaints, peerman, 8); + }; + HandlePhase(QuorumPhase::Complain, QuorumPhase::Justify, curQuorumHash, 0.05, fComplainStart, fComplainWait); + + // Justify + auto fJustifyStart = [this, &peerman]() { curSession->VerifyAndJustify(pendingJustifications, peerman); }; + auto fJustifyWait = [this, &connman, &peerman] { + return ProcessPendingMessageBatch(connman, *curSession, pendingJustifications, peerman, 8); + }; + HandlePhase(QuorumPhase::Justify, QuorumPhase::Commit, curQuorumHash, 0.05, fJustifyStart, fJustifyWait); + + // Commit + auto fCommitStart = [this, &peerman]() { curSession->VerifyAndCommit(pendingPrematureCommitments, peerman); }; + auto fCommitWait = [this, &connman, &peerman] { + return ProcessPendingMessageBatch(connman, *curSession, pendingPrematureCommitments, + peerman, 8); + }; + HandlePhase(QuorumPhase::Commit, QuorumPhase::Finalize, curQuorumHash, 0.1, fCommitStart, fCommitWait); + + auto finalCommitments = curSession->FinalizeCommitments(); + for (const auto& fqc : finalCommitments) { + if (auto inv_opt = quorumBlockProcessor.AddMineableCommitment(fqc); inv_opt.has_value()) { + peerman.RelayInv(inv_opt.value()); + } + } +} + +void CDKGSessionHandler::PhaseHandlerThread(CConnman& connman, PeerManager& peerman) +{ + while (!stopRequested) { + try { + LogPrint(BCLog::LLMQ_DKG, "CDKGSessionHandler::%s -- %s qi[%d] - starting HandleDKGRound\n", __func__, params.name, quorumIndex); + HandleDKGRound(connman, peerman); + } catch (AbortPhaseException& e) { + dkgDebugManager.UpdateLocalSessionStatus(params.type, quorumIndex, [&](CDKGDebugSessionStatus& status) { + status.statusBits.aborted = true; + return true; + }); + LogPrint(BCLog::LLMQ_DKG, "CDKGSessionHandler::%s -- %s qi[%d] - aborted current DKG session\n", __func__, params.name, quorumIndex); + } + } +} + +bool CDKGSessionHandler::GetContribution(const uint256& hash, CDKGContribution& ret) const +{ + LOCK(curSession->invCs); + auto it = curSession->contributions.find(hash); + if (it != curSession->contributions.end()) { + ret = it->second; + return true; + } + return false; +} + +bool CDKGSessionHandler::GetComplaint(const uint256& hash, CDKGComplaint& ret) const +{ + LOCK(curSession->invCs); + auto it = curSession->complaints.find(hash); + if (it != curSession->complaints.end()) { + ret = it->second; + return true; + } + return false; +} + +bool CDKGSessionHandler::GetJustification(const uint256& hash, CDKGJustification& ret) const +{ + LOCK(curSession->invCs); + auto it = curSession->justifications.find(hash); + if (it != curSession->justifications.end()) { + ret = it->second; + return true; + } + return false; +} + +bool CDKGSessionHandler::GetPrematureCommitment(const uint256& hash, CDKGPrematureCommitment& ret) const +{ + LOCK(curSession->invCs); + auto it = curSession->prematureCommitments.find(hash); + if (it != curSession->prematureCommitments.end() && curSession->validCommitments.count(hash)) { + ret = it->second; + return true; + } + return false; +} +} // namespace llmq diff --git a/src/llmq/dkgsessionhandler.cpp b/src/llmq/dkgsessionhandler.cpp index e90979c44272..0f57c180b73e 100644 --- a/src/llmq/dkgsessionhandler.cpp +++ b/src/llmq/dkgsessionhandler.cpp @@ -125,33 +125,6 @@ void CDKGPendingMessages::Clear() ////// -void CDKGSessionHandler::UpdatedBlockTip(const CBlockIndex* pindexNew) -{ - //AssertLockNotHeld(cs_main); - //Indexed quorums (greater than 0) are enabled with Quorum Rotation - if (quorumIndex > 0 && !IsQuorumRotationEnabled(params, pindexNew)) { - return; - } - LOCK(cs_phase_qhash); - - int quorumStageInt = (pindexNew->nHeight - quorumIndex) % params.dkgInterval; - - const CBlockIndex* pQuorumBaseBlockIndex = pindexNew->GetAncestor(pindexNew->nHeight - quorumStageInt); - - currentHeight = pindexNew->nHeight; - quorumHash = pQuorumBaseBlockIndex->GetBlockHash(); - - bool fNewPhase = (quorumStageInt % params.dkgPhaseBlocks) == 0; - int phaseInt = quorumStageInt / params.dkgPhaseBlocks + 1; - QuorumPhase oldPhase = phase; - if (fNewPhase && phaseInt >= ToUnderlying(QuorumPhase::Initialized) && phaseInt <= ToUnderlying(QuorumPhase::Idle)) { - phase = static_cast(phaseInt); - } - - LogPrint(BCLog::LLMQ_DKG, "CDKGSessionHandler::%s -- %s qi[%d] currentHeight=%d, pQuorumBaseBlockIndex->nHeight=%d, oldPhase=%d, newPhase=%d\n", __func__, - params.name, quorumIndex, currentHeight, pQuorumBaseBlockIndex->nHeight, ToUnderlying(oldPhase), ToUnderlying(phase)); -} - MessageProcessingResult CDKGSessionHandler::ProcessMessage(NodeId from, std::string_view msg_type, CDataStream& vRecv) { // We don't handle messages in the calling thread as deserialization/processing of these would block everything @@ -166,511 +139,4 @@ MessageProcessingResult CDKGSessionHandler::ProcessMessage(NodeId from, std::str } return {}; } - -void CDKGSessionHandler::StartThread(CConnman& connman, PeerManager& peerman) -{ - if (phaseHandlerThread.joinable()) { - throw std::runtime_error("Tried to start an already started CDKGSessionHandler thread."); - } - - m_thread_name = strprintf("llmq-%d-%d", ToUnderlying(params.type), quorumIndex); - phaseHandlerThread = std::thread(&util::TraceThread, m_thread_name.c_str(), - [this, &connman, &peerman] { PhaseHandlerThread(connman, peerman); }); -} - -void CDKGSessionHandler::StopThread() -{ - stopRequested = true; - if (phaseHandlerThread.joinable()) { - phaseHandlerThread.join(); - } -} - -bool CDKGSessionHandler::InitNewQuorum(const CBlockIndex* pQuorumBaseBlockIndex) -{ - if (!DeploymentDIP0003Enforced(pQuorumBaseBlockIndex->nHeight, Params().GetConsensus())) { - return false; - } - - curSession = std::make_unique(pQuorumBaseBlockIndex, params, blsWorker, m_dmnman, dkgManager, - dkgDebugManager, m_mn_metaman, m_qsnapman, m_mn_activeman, m_chainman, - m_sporkman); - - if (!curSession->Init(m_mn_activeman->GetProTxHash(), quorumIndex)) { - LogPrintf("CDKGSessionManager::%s -- height[%d] quorum initialization failed for %s qi[%d]\n", __func__, - pQuorumBaseBlockIndex->nHeight, curSession->params.name, quorumIndex); - return false; - } - - LogPrintf("CDKGSessionManager::%s -- height[%d] quorum initialization OK for %s qi[%d]\n", __func__, pQuorumBaseBlockIndex->nHeight, curSession->params.name, quorumIndex); - return true; -} - -std::pair CDKGSessionHandler::GetPhaseAndQuorumHash() const -{ - LOCK(cs_phase_qhash); - return std::make_pair(phase, quorumHash); -} - -class AbortPhaseException : public std::exception { -}; - -void CDKGSessionHandler::WaitForNextPhase(std::optional curPhase, - QuorumPhase nextPhase, - const uint256& expectedQuorumHash, - const WhileWaitFunc& shouldNotWait) const -{ - LogPrint(BCLog::LLMQ_DKG, "CDKGSessionManager::%s -- %s qi[%d] - starting, curPhase=%d, nextPhase=%d\n", __func__, params.name, quorumIndex, curPhase.has_value() ? ToUnderlying(*curPhase) : -1, ToUnderlying(nextPhase)); - - while (true) { - if (stopRequested) { - LogPrint(BCLog::LLMQ_DKG, "CDKGSessionManager::%s -- %s qi[%d] - aborting due to stop/shutdown requested\n", __func__, params.name, quorumIndex); - throw AbortPhaseException(); - } - auto [_phase, _quorumHash] = GetPhaseAndQuorumHash(); - if (!expectedQuorumHash.IsNull() && _quorumHash != expectedQuorumHash) { - LogPrint(BCLog::LLMQ_DKG, "CDKGSessionManager::%s -- %s qi[%d] - aborting due unexpected expectedQuorumHash change\n", __func__, params.name, quorumIndex); - throw AbortPhaseException(); - } - if (_phase == nextPhase) { - break; - } - if (curPhase.has_value() && _phase != curPhase) { - LogPrint(BCLog::LLMQ_DKG, "CDKGSessionManager::%s -- %s qi[%d] - aborting due unexpected phase change, _phase=%d, curPhase=%d\n", __func__, params.name, quorumIndex, ToUnderlying(_phase), curPhase.has_value() ? ToUnderlying(*curPhase) : -1); - throw AbortPhaseException(); - } - if (!shouldNotWait()) { - UninterruptibleSleep(std::chrono::milliseconds{100}); - } - } - - LogPrint(BCLog::LLMQ_DKG, "CDKGSessionManager::%s -- %s qi[%d] - done, curPhase=%d, nextPhase=%d\n", __func__, params.name, quorumIndex, curPhase.has_value() ? ToUnderlying(*curPhase) : -1, ToUnderlying(nextPhase)); - - if (nextPhase == QuorumPhase::Initialized) { - dkgDebugManager.ResetLocalSessionStatus(params.type, quorumIndex); - } else { - dkgDebugManager.UpdateLocalSessionStatus(params.type, quorumIndex, [&](CDKGDebugSessionStatus& status) { - bool changed = status.phase != nextPhase; - status.phase = nextPhase; - return changed; - }); - } -} - -void CDKGSessionHandler::WaitForNewQuorum(const uint256& oldQuorumHash) const -{ - LogPrint(BCLog::LLMQ_DKG, "CDKGSessionManager::%s -- %s qi[%d]- starting\n", __func__, params.name, quorumIndex); - - while (true) { - if (stopRequested) { - LogPrint(BCLog::LLMQ_DKG, "CDKGSessionManager::%s -- %s qi[%d] - aborting due to stop/shutdown requested\n", __func__, params.name, quorumIndex); - throw AbortPhaseException(); - } - auto [_, _quorumHash] = GetPhaseAndQuorumHash(); - if (_quorumHash != oldQuorumHash) { - break; - } - UninterruptibleSleep(std::chrono::milliseconds{100}); - } - - LogPrint(BCLog::LLMQ_DKG, "CDKGSessionManager::%s -- %s qi[%d] - done\n", __func__, params.name, quorumIndex); -} - -// Sleep some time to not fully overload the whole network -void CDKGSessionHandler::SleepBeforePhase(QuorumPhase curPhase, - const uint256& expectedQuorumHash, - double randomSleepFactor, - const WhileWaitFunc& runWhileWaiting) const -{ - if (!curSession->AreWeMember()) { - // Non-members do not participate and do not create any network load, no need to sleep. - return; - } - - if (Params().MineBlocksOnDemand()) { - // On regtest, blocks can be mined on demand without any significant time passing between these. - // We shouldn't wait before phases in this case. - return; - } - - // Two blocks can come very close to each other, this happens pretty regularly. We don't want to be - // left behind and marked as a bad member. This means that we should not count the last block of the - // phase as a safe one to keep sleeping, that's why we calculate the phase sleep time as a time of - // the full phase minus one block here. - double phaseSleepTime = (params.dkgPhaseBlocks - 1) * Params().GetConsensus().nPowTargetSpacing * 1000; - // Expected phase sleep time per member - double phaseSleepTimePerMember = phaseSleepTime / params.size; - // Don't expect perfect block times and thus reduce the phase time to be on the secure side (caller chooses factor) - double adjustedPhaseSleepTimePerMember = phaseSleepTimePerMember * randomSleepFactor; - - int64_t sleepTime = (int64_t)(adjustedPhaseSleepTimePerMember * curSession->GetMyMemberIndex().value_or(0)); - int64_t endTime = TicksSinceEpoch(SystemClock::now()) + sleepTime; - int heightTmp{currentHeight.load()}; - int heightStart{heightTmp}; - - LogPrint(BCLog::LLMQ_DKG, "CDKGSessionManager::%s -- %s qi[%d] - starting sleep for %d ms, curPhase=%d\n", __func__, params.name, quorumIndex, sleepTime, ToUnderlying(curPhase)); - - while (TicksSinceEpoch(SystemClock::now()) < endTime) { - if (stopRequested) { - LogPrint(BCLog::LLMQ_DKG, "CDKGSessionManager::%s -- %s qi[%d] - aborting due to stop/shutdown requested\n", __func__, params.name, quorumIndex); - throw AbortPhaseException(); - } - auto cur_height = currentHeight.load(); - if (cur_height > heightTmp) { - // New block(s) just came in - int64_t expectedBlockTime = (cur_height - heightStart) * Params().GetConsensus().nPowTargetSpacing * 1000; - if (expectedBlockTime > sleepTime) { - // Blocks came faster than we expected, jump into the phase func asap - break; - } - heightTmp = cur_height; - } - if (WITH_LOCK(cs_phase_qhash, return phase != curPhase || quorumHash != expectedQuorumHash)) { - // Something went wrong and/or we missed quite a few blocks and it's just too late now - LogPrint(BCLog::LLMQ_DKG, "CDKGSessionManager::%s -- %s qi[%d] - aborting due unexpected phase/expectedQuorumHash change\n", __func__, params.name, quorumIndex); - throw AbortPhaseException(); - } - if (!runWhileWaiting()) { - UninterruptibleSleep(std::chrono::milliseconds{100}); - } - } - - LogPrint(BCLog::LLMQ_DKG, "CDKGSessionManager::%s -- %s qi[%d] - done, curPhase=%d\n", __func__, params.name, quorumIndex, ToUnderlying(curPhase)); -} - -void CDKGSessionHandler::HandlePhase(QuorumPhase curPhase, - QuorumPhase nextPhase, - const uint256& expectedQuorumHash, - double randomSleepFactor, - const StartPhaseFunc& startPhaseFunc, - const WhileWaitFunc& runWhileWaiting) -{ - LogPrint(BCLog::LLMQ_DKG, "CDKGSessionManager::%s -- %s qi[%d] - starting, curPhase=%d, nextPhase=%d\n", __func__, params.name, quorumIndex, ToUnderlying(curPhase), ToUnderlying(nextPhase)); - - SleepBeforePhase(curPhase, expectedQuorumHash, randomSleepFactor, runWhileWaiting); - startPhaseFunc(); - WaitForNextPhase(curPhase, nextPhase, expectedQuorumHash, runWhileWaiting); - - LogPrint(BCLog::LLMQ_DKG, "CDKGSessionManager::%s -- %s qi[%d] - done, curPhase=%d, nextPhase=%d\n", __func__, params.name, quorumIndex, ToUnderlying(curPhase), ToUnderlying(nextPhase)); -} - -// returns a set of NodeIds which sent invalid messages -template -std::set BatchVerifyMessageSigs(CDKGSession& session, const std::vector>>& messages) -{ - if (messages.empty()) { - return {}; - } - - std::set ret; - bool revertToSingleVerification = false; - - CBLSSignature aggSig; - std::vector pubKeys; - std::vector messageHashes; - std::set messageHashesSet; - pubKeys.reserve(messages.size()); - messageHashes.reserve(messages.size()); - bool first = true; - for (const auto& [nodeId, msg] : messages) { - auto member = session.GetMember(msg->proTxHash); - if (!member) { - // should not happen as it was verified before - ret.emplace(nodeId); - continue; - } - - if (first) { - aggSig = msg->sig; - } else { - aggSig.AggregateInsecure(msg->sig); - } - first = false; - - auto msgHash = msg->GetSignHash(); - if (!messageHashesSet.emplace(msgHash).second) { - // can only happen in 2 cases: - // 1. Someone sent us the same message twice but with differing signature, meaning that at least one of them - // must be invalid. In this case, we'd have to revert to single message verification nevertheless - // 2. Someone managed to find a way to create two different binary representations of a message that deserializes - // to the same object representation. This would be some form of malleability. However, this shouldn't be - // possible as only deterministic/unique BLS signatures and very simple data types are involved - revertToSingleVerification = true; - break; - } - - pubKeys.emplace_back(member->dmn->pdmnState->pubKeyOperator.Get()); - messageHashes.emplace_back(msgHash); - } - if (!revertToSingleVerification) { - if (aggSig.VerifyInsecureAggregated(pubKeys, messageHashes)) { - // all good - return ret; - } - - // are all messages from the same node? - bool nodeIdsAllSame = std::adjacent_find( messages.begin(), messages.end(), [](const auto& first, const auto& second){ - return first.first != second.first; - }) == messages.end(); - - // if yes, take a short path and return a set with only him - if (nodeIdsAllSame) { - ret.emplace(messages[0].first); - return ret; - } - // different nodes, let's figure out who are the bad ones - } - - for (const auto& [nodeId, msg] : messages) { - if (ret.count(nodeId)) { - continue; - } - - auto member = session.GetMember(msg->proTxHash); - bool valid = msg->sig.VerifyInsecure(member->dmn->pdmnState->pubKeyOperator.Get(), msg->GetSignHash()); - if (!valid) { - ret.emplace(nodeId); - } - } - return ret; -} - -static void RelayInvToParticipants(const CDKGSession& session, const CConnman& connman, PeerManager& peerman, - const CInv& inv) -{ - CDKGLogger logger(session, __func__, __LINE__); - std::stringstream ss; - const auto& relayMembers = session.RelayMembers(); - for (const auto& r : relayMembers) { - ss << r.ToString().substr(0, 4) << " | "; - } - logger.Batch("RelayInvToParticipants inv[%s] relayMembers[%d] GetNodeCount[%d] GetNetworkActive[%d] " - "HasMasternodeQuorumNodes[%d] for quorumHash[%s] forMember[%s] relayMembers[%s]", - inv.ToString(), relayMembers.size(), connman.GetNodeCount(ConnectionDirection::Both), - connman.GetNetworkActive(), - connman.HasMasternodeQuorumNodes(session.GetParams().type, session.BlockIndex()->GetBlockHash()), - session.BlockIndex()->GetBlockHash().ToString(), session.ProTx().ToString().substr(0, 4), ss.str()); - - std::stringstream ss2; - connman.ForEachNode([&](const CNode* pnode) { - if (pnode->qwatch || - (!pnode->GetVerifiedProRegTxHash().IsNull() && (relayMembers.count(pnode->GetVerifiedProRegTxHash()) != 0))) { - peerman.PushInventory(pnode->GetId(), inv); - } - - if (pnode->GetVerifiedProRegTxHash().IsNull()) { - logger.Batch("node[%d:%s] not mn", pnode->GetId(), pnode->m_addr_name); - } else if (relayMembers.count(pnode->GetVerifiedProRegTxHash()) == 0) { - ss2 << pnode->GetVerifiedProRegTxHash().ToString().substr(0, 4) << " | "; - } - }); - logger.Batch("forMember[%s] NOTrelayMembers[%s]", session.ProTx().ToString().substr(0, 4), ss2.str()); - logger.Flush(); -} - -template -bool ProcessPendingMessageBatch(const CConnman& connman, CDKGSession& session, CDKGPendingMessages& pendingMessages, - PeerManager& peerman, size_t maxCount) -{ - auto msgs = pendingMessages.PopAndDeserializeMessages(maxCount); - if (msgs.empty()) { - return false; - } - - std::vector>> preverifiedMessages; - preverifiedMessages.reserve(msgs.size()); - - for (const auto& p : msgs) { - const NodeId &nodeId = p.first; - if (!p.second) { - LogPrint(BCLog::LLMQ_DKG, "%s -- failed to deserialize message, peer=%d\n", __func__, nodeId); - { - pendingMessages.Misbehaving(nodeId, 100, peerman); - } - continue; - } - bool ban = false; - if (!session.PreVerifyMessage(*p.second, ban)) { - if (ban) { - LogPrint(BCLog::LLMQ_DKG, "%s -- banning node due to failed preverification, peer=%d\n", __func__, nodeId); - { - pendingMessages.Misbehaving(nodeId, 100, peerman); - } - } - LogPrint(BCLog::LLMQ_DKG, "%s -- skipping message due to failed preverification, peer=%d\n", __func__, nodeId); - continue; - } - preverifiedMessages.emplace_back(p); - } - if (preverifiedMessages.empty()) { - return true; - } - - auto badNodes = BatchVerifyMessageSigs(session, preverifiedMessages); - if (!badNodes.empty()) { - for (auto nodeId : badNodes) { - LogPrint(BCLog::LLMQ_DKG, "%s -- failed to verify signature, peer=%d\n", __func__, nodeId); - pendingMessages.Misbehaving(nodeId, 100, peerman); - } - } - - for (const auto& p : preverifiedMessages) { - const NodeId &nodeId = p.first; - if (badNodes.count(nodeId)) { - continue; - } - const std::optional inv = session.ReceiveMessage(*p.second); - if (inv) { - RelayInvToParticipants(session, connman, peerman, *inv); - } - } - - return true; -} - -void CDKGSessionHandler::HandleDKGRound(CConnman& connman, PeerManager& peerman) -{ - WaitForNextPhase(std::nullopt, QuorumPhase::Initialized); - - pendingContributions.Clear(); - pendingComplaints.Clear(); - pendingJustifications.Clear(); - pendingPrematureCommitments.Clear(); - uint256 curQuorumHash = WITH_LOCK(cs_phase_qhash, return quorumHash); - - const CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(::cs_main, - return m_chainman.m_blockman.LookupBlockIndex(curQuorumHash)); - - if (!pQuorumBaseBlockIndex || !InitNewQuorum(pQuorumBaseBlockIndex)) { - // should actually never happen - WaitForNewQuorum(curQuorumHash); - throw AbortPhaseException(); - } - - dkgDebugManager.UpdateLocalSessionStatus(params.type, quorumIndex, [&](CDKGDebugSessionStatus& status) { - bool changed = status.phase != QuorumPhase::Initialized; - status.phase = QuorumPhase::Initialized; - return changed; - }); - - if (params.is_single_member()) { - auto finalCommitment = curSession->FinalizeSingleCommitment(); - if (!finalCommitment.IsNull()) { // it can be null only if we are not member - if (auto inv_opt = quorumBlockProcessor.AddMineableCommitment(finalCommitment); inv_opt.has_value()) { - peerman.RelayInv(inv_opt.value()); - } - } - WaitForNextPhase(QuorumPhase::Initialized, QuorumPhase::Contribute, curQuorumHash); - return; - } - - const auto tip_mn_list = m_dmnman.GetListAtChainTip(); - utils::EnsureQuorumConnections(params, connman, m_sporkman, {m_dmnman, m_qsnapman, m_chainman, pQuorumBaseBlockIndex}, tip_mn_list, - curSession->myProTxHash, /*is_masternode=*/m_mn_activeman != nullptr, m_quorums_watch); - if (curSession->AreWeMember()) { - utils::AddQuorumProbeConnections(params, connman, m_mn_metaman, m_sporkman, {m_dmnman, m_qsnapman, m_chainman, - pQuorumBaseBlockIndex}, tip_mn_list, curSession->myProTxHash); - } - - WaitForNextPhase(QuorumPhase::Initialized, QuorumPhase::Contribute, curQuorumHash); - - // Contribute - auto fContributeStart = [this, &peerman]() { curSession->Contribute(pendingContributions, peerman); }; - auto fContributeWait = [this, &connman, &peerman] { - return ProcessPendingMessageBatch(connman, *curSession, pendingContributions, peerman, 8); - }; - HandlePhase(QuorumPhase::Contribute, QuorumPhase::Complain, curQuorumHash, 0.05, fContributeStart, fContributeWait); - - // Complain - auto fComplainStart = [this, &connman, &peerman]() { - curSession->VerifyAndComplain(connman, pendingComplaints, peerman); - }; - auto fComplainWait = [this, &connman, &peerman] { - return ProcessPendingMessageBatch(connman, *curSession, pendingComplaints, peerman, 8); - }; - HandlePhase(QuorumPhase::Complain, QuorumPhase::Justify, curQuorumHash, 0.05, fComplainStart, fComplainWait); - - // Justify - auto fJustifyStart = [this, &peerman]() { curSession->VerifyAndJustify(pendingJustifications, peerman); }; - auto fJustifyWait = [this, &connman, &peerman] { - return ProcessPendingMessageBatch(connman, *curSession, pendingJustifications, peerman, 8); - }; - HandlePhase(QuorumPhase::Justify, QuorumPhase::Commit, curQuorumHash, 0.05, fJustifyStart, fJustifyWait); - - // Commit - auto fCommitStart = [this, &peerman]() { curSession->VerifyAndCommit(pendingPrematureCommitments, peerman); }; - auto fCommitWait = [this, &connman, &peerman] { - return ProcessPendingMessageBatch(connman, *curSession, pendingPrematureCommitments, - peerman, 8); - }; - HandlePhase(QuorumPhase::Commit, QuorumPhase::Finalize, curQuorumHash, 0.1, fCommitStart, fCommitWait); - - auto finalCommitments = curSession->FinalizeCommitments(); - for (const auto& fqc : finalCommitments) { - if (auto inv_opt = quorumBlockProcessor.AddMineableCommitment(fqc); inv_opt.has_value()) { - peerman.RelayInv(inv_opt.value()); - } - } -} - -void CDKGSessionHandler::PhaseHandlerThread(CConnman& connman, PeerManager& peerman) -{ - while (!stopRequested) { - try { - LogPrint(BCLog::LLMQ_DKG, "CDKGSessionHandler::%s -- %s qi[%d] - starting HandleDKGRound\n", __func__, params.name, quorumIndex); - HandleDKGRound(connman, peerman); - } catch (AbortPhaseException& e) { - dkgDebugManager.UpdateLocalSessionStatus(params.type, quorumIndex, [&](CDKGDebugSessionStatus& status) { - status.statusBits.aborted = true; - return true; - }); - LogPrint(BCLog::LLMQ_DKG, "CDKGSessionHandler::%s -- %s qi[%d] - aborted current DKG session\n", __func__, params.name, quorumIndex); - } - } -} - -bool CDKGSessionHandler::GetContribution(const uint256& hash, CDKGContribution& ret) const -{ - LOCK(curSession->invCs); - auto it = curSession->contributions.find(hash); - if (it != curSession->contributions.end()) { - ret = it->second; - return true; - } - return false; -} - -bool CDKGSessionHandler::GetComplaint(const uint256& hash, CDKGComplaint& ret) const -{ - LOCK(curSession->invCs); - auto it = curSession->complaints.find(hash); - if (it != curSession->complaints.end()) { - ret = it->second; - return true; - } - return false; -} - -bool CDKGSessionHandler::GetJustification(const uint256& hash, CDKGJustification& ret) const -{ - LOCK(curSession->invCs); - auto it = curSession->justifications.find(hash); - if (it != curSession->justifications.end()) { - ret = it->second; - return true; - } - return false; -} - -bool CDKGSessionHandler::GetPrematureCommitment(const uint256& hash, CDKGPrematureCommitment& ret) const -{ - LOCK(curSession->invCs); - auto it = curSession->prematureCommitments.find(hash); - if (it != curSession->prematureCommitments.end() && curSession->validCommitments.count(hash)) { - ret = it->second; - return true; - } - return false; -} - } // namespace llmq From b59baac42d4b0fcf4ea97b0438184efb2c603cbd Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Tue, 6 Jan 2026 23:02:01 +0530 Subject: [PATCH 04/14] refactor: separate active session handler routines into dedicated class Co-authored-by: UdjinM6 --- src/Makefile.am | 1 + src/active/dkgsessionhandler.cpp | 142 +++++++++++++++++------------- src/active/dkgsessionhandler.h | 87 ++++++++++++++++++ src/llmq/dkgsession.h | 13 +-- src/llmq/dkgsessionhandler.cpp | 13 +-- src/llmq/dkgsessionhandler.h | 67 +++----------- src/llmq/dkgsessionmgr.cpp | 17 +--- src/llmq/observer/context.cpp | 12 +-- src/masternode/active/context.cpp | 15 ++-- src/net_processing.cpp | 12 +-- 10 files changed, 211 insertions(+), 168 deletions(-) create mode 100644 src/active/dkgsessionhandler.h diff --git a/src/Makefile.am b/src/Makefile.am index 77454ba65c74..0f5a40f29cd6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -151,6 +151,7 @@ endif .PHONY: FORCE check-symbols check-security # dash core # BITCOIN_CORE_H = \ + active/dkgsessionhandler.h \ active/quorums.h \ addrdb.h \ addressindex.h \ diff --git a/src/active/dkgsessionhandler.cpp b/src/active/dkgsessionhandler.cpp index 1c1cf356381d..770290902f86 100644 --- a/src/active/dkgsessionhandler.cpp +++ b/src/active/dkgsessionhandler.cpp @@ -2,7 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include +#include #include #include @@ -18,7 +18,30 @@ #include namespace llmq { -void CDKGSessionHandler::UpdatedBlockTip(const CBlockIndex* pindexNew) +ActiveDKGSessionHandler::ActiveDKGSessionHandler( + CBLSWorker& bls_worker, CDeterministicMNManager& dmnman, CMasternodeMetaMan& mn_metaman, + llmq::CDKGDebugManager& dkgdbgman, llmq::CDKGSessionManager& qdkgsman, llmq::CQuorumBlockProcessor& qblockman, + llmq::CQuorumSnapshotManager& qsnapman, const CActiveMasternodeManager& mn_activeman, const ChainstateManager& chainman, + const CSporkManager& sporkman, const Consensus::LLMQParams& llmq_params, bool quorums_watch, int quorums_idx) : + llmq::CDKGSessionHandler(bls_worker, dmnman, dkgdbgman, qdkgsman, mn_metaman, qblockman, qsnapman, &mn_activeman, + chainman, sporkman, llmq_params, quorums_watch, quorums_idx), + m_bls_worker{bls_worker}, + m_dmnman{dmnman}, + m_mn_metaman{mn_metaman}, + m_dkgdbgman{dkgdbgman}, + m_qdkgsman{qdkgsman}, + m_qblockman{qblockman}, + m_qsnapman{qsnapman}, + m_mn_activeman{mn_activeman}, + m_chainman{chainman}, + m_sporkman{sporkman}, + m_quorums_watch{quorums_watch} +{ +} + +ActiveDKGSessionHandler::~ActiveDKGSessionHandler() = default; + +void ActiveDKGSessionHandler::UpdatedBlockTip(const CBlockIndex* pindexNew) { //AssertLockNotHeld(cs_main); //Indexed quorums (greater than 0) are enabled with Quorum Rotation @@ -41,14 +64,14 @@ void CDKGSessionHandler::UpdatedBlockTip(const CBlockIndex* pindexNew) phase = static_cast(phaseInt); } - LogPrint(BCLog::LLMQ_DKG, "CDKGSessionHandler::%s -- %s qi[%d] currentHeight=%d, pQuorumBaseBlockIndex->nHeight=%d, oldPhase=%d, newPhase=%d\n", __func__, + LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d] currentHeight=%d, pQuorumBaseBlockIndex->nHeight=%d, oldPhase=%d, newPhase=%d\n", __func__, params.name, quorumIndex, currentHeight, pQuorumBaseBlockIndex->nHeight, ToUnderlying(oldPhase), ToUnderlying(phase)); } -void CDKGSessionHandler::StartThread(CConnman& connman, PeerManager& peerman) +void ActiveDKGSessionHandler::StartThread(CConnman& connman, PeerManager& peerman) { if (phaseHandlerThread.joinable()) { - throw std::runtime_error("Tried to start an already started CDKGSessionHandler thread."); + throw std::runtime_error("Tried to start an already started ActiveDKGSessionHandler thread."); } m_thread_name = strprintf("llmq-%d-%d", ToUnderlying(params.type), quorumIndex); @@ -56,7 +79,7 @@ void CDKGSessionHandler::StartThread(CConnman& connman, PeerManager& peerman) [this, &connman, &peerman] { PhaseHandlerThread(connman, peerman); }); } -void CDKGSessionHandler::StopThread() +void ActiveDKGSessionHandler::StopThread() { stopRequested = true; if (phaseHandlerThread.joinable()) { @@ -64,57 +87,55 @@ void CDKGSessionHandler::StopThread() } } -std::pair CDKGSessionHandler::GetPhaseAndQuorumHash() const +std::pair ActiveDKGSessionHandler::GetPhaseAndQuorumHash() const { LOCK(cs_phase_qhash); return std::make_pair(phase, quorumHash); } -bool CDKGSessionHandler::InitNewQuorum(const CBlockIndex* pQuorumBaseBlockIndex) +bool ActiveDKGSessionHandler::InitNewQuorum(const CBlockIndex* pQuorumBaseBlockIndex) { if (!DeploymentDIP0003Enforced(pQuorumBaseBlockIndex->nHeight, Params().GetConsensus())) { return false; } - curSession = std::make_unique(pQuorumBaseBlockIndex, params, blsWorker, m_dmnman, dkgManager, - dkgDebugManager, m_mn_metaman, m_qsnapman, m_mn_activeman, m_chainman, + curSession = std::make_unique(pQuorumBaseBlockIndex, params, m_bls_worker, m_dmnman, m_qdkgsman, + m_dkgdbgman, m_mn_metaman, m_qsnapman, &m_mn_activeman, m_chainman, m_sporkman); - if (!curSession->Init(m_mn_activeman->GetProTxHash(), quorumIndex)) { - LogPrintf("CDKGSessionManager::%s -- height[%d] quorum initialization failed for %s qi[%d]\n", __func__, + if (!curSession->Init(m_mn_activeman.GetProTxHash(), quorumIndex)) { + LogPrintf("ActiveDKGSessionHandler::%s -- height[%d] quorum initialization failed for %s qi[%d]\n", __func__, pQuorumBaseBlockIndex->nHeight, curSession->params.name, quorumIndex); return false; } - LogPrintf("CDKGSessionManager::%s -- height[%d] quorum initialization OK for %s qi[%d]\n", __func__, pQuorumBaseBlockIndex->nHeight, curSession->params.name, quorumIndex); + LogPrintf("ActiveDKGSessionHandler::%s -- height[%d] quorum initialization OK for %s qi[%d]\n", __func__, pQuorumBaseBlockIndex->nHeight, curSession->params.name, quorumIndex); return true; } class AbortPhaseException : public std::exception { }; -void CDKGSessionHandler::WaitForNextPhase(std::optional curPhase, - QuorumPhase nextPhase, - const uint256& expectedQuorumHash, - const WhileWaitFunc& shouldNotWait) const +void ActiveDKGSessionHandler::WaitForNextPhase(std::optional curPhase, QuorumPhase nextPhase, + const uint256& expectedQuorumHash, const WhileWaitFunc& shouldNotWait) const { - LogPrint(BCLog::LLMQ_DKG, "CDKGSessionManager::%s -- %s qi[%d] - starting, curPhase=%d, nextPhase=%d\n", __func__, params.name, quorumIndex, curPhase.has_value() ? ToUnderlying(*curPhase) : -1, ToUnderlying(nextPhase)); + LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d] - starting, curPhase=%d, nextPhase=%d\n", __func__, params.name, quorumIndex, curPhase.has_value() ? ToUnderlying(*curPhase) : -1, ToUnderlying(nextPhase)); while (true) { if (stopRequested) { - LogPrint(BCLog::LLMQ_DKG, "CDKGSessionManager::%s -- %s qi[%d] - aborting due to stop/shutdown requested\n", __func__, params.name, quorumIndex); + LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d] - aborting due to stop/shutdown requested\n", __func__, params.name, quorumIndex); throw AbortPhaseException(); } auto [_phase, _quorumHash] = GetPhaseAndQuorumHash(); if (!expectedQuorumHash.IsNull() && _quorumHash != expectedQuorumHash) { - LogPrint(BCLog::LLMQ_DKG, "CDKGSessionManager::%s -- %s qi[%d] - aborting due unexpected expectedQuorumHash change\n", __func__, params.name, quorumIndex); + LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d] - aborting due unexpected expectedQuorumHash change\n", __func__, params.name, quorumIndex); throw AbortPhaseException(); } if (_phase == nextPhase) { break; } if (curPhase.has_value() && _phase != curPhase) { - LogPrint(BCLog::LLMQ_DKG, "CDKGSessionManager::%s -- %s qi[%d] - aborting due unexpected phase change, _phase=%d, curPhase=%d\n", __func__, params.name, quorumIndex, ToUnderlying(_phase), curPhase.has_value() ? ToUnderlying(*curPhase) : -1); + LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d] - aborting due unexpected phase change, _phase=%d, curPhase=%d\n", __func__, params.name, quorumIndex, ToUnderlying(_phase), curPhase.has_value() ? ToUnderlying(*curPhase) : -1); throw AbortPhaseException(); } if (!shouldNotWait()) { @@ -122,12 +143,12 @@ void CDKGSessionHandler::WaitForNextPhase(std::optional curPhase, } } - LogPrint(BCLog::LLMQ_DKG, "CDKGSessionManager::%s -- %s qi[%d] - done, curPhase=%d, nextPhase=%d\n", __func__, params.name, quorumIndex, curPhase.has_value() ? ToUnderlying(*curPhase) : -1, ToUnderlying(nextPhase)); + LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d] - done, curPhase=%d, nextPhase=%d\n", __func__, params.name, quorumIndex, curPhase.has_value() ? ToUnderlying(*curPhase) : -1, ToUnderlying(nextPhase)); if (nextPhase == QuorumPhase::Initialized) { - dkgDebugManager.ResetLocalSessionStatus(params.type, quorumIndex); + m_dkgdbgman.ResetLocalSessionStatus(params.type, quorumIndex); } else { - dkgDebugManager.UpdateLocalSessionStatus(params.type, quorumIndex, [&](CDKGDebugSessionStatus& status) { + m_dkgdbgman.UpdateLocalSessionStatus(params.type, quorumIndex, [&](CDKGDebugSessionStatus& status) { bool changed = status.phase != nextPhase; status.phase = nextPhase; return changed; @@ -135,13 +156,13 @@ void CDKGSessionHandler::WaitForNextPhase(std::optional curPhase, } } -void CDKGSessionHandler::WaitForNewQuorum(const uint256& oldQuorumHash) const +void ActiveDKGSessionHandler::WaitForNewQuorum(const uint256& oldQuorumHash) const { - LogPrint(BCLog::LLMQ_DKG, "CDKGSessionManager::%s -- %s qi[%d]- starting\n", __func__, params.name, quorumIndex); + LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d]- starting\n", __func__, params.name, quorumIndex); while (true) { if (stopRequested) { - LogPrint(BCLog::LLMQ_DKG, "CDKGSessionManager::%s -- %s qi[%d] - aborting due to stop/shutdown requested\n", __func__, params.name, quorumIndex); + LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d] - aborting due to stop/shutdown requested\n", __func__, params.name, quorumIndex); throw AbortPhaseException(); } auto [_, _quorumHash] = GetPhaseAndQuorumHash(); @@ -151,14 +172,12 @@ void CDKGSessionHandler::WaitForNewQuorum(const uint256& oldQuorumHash) const UninterruptibleSleep(std::chrono::milliseconds{100}); } - LogPrint(BCLog::LLMQ_DKG, "CDKGSessionManager::%s -- %s qi[%d] - done\n", __func__, params.name, quorumIndex); + LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d] - done\n", __func__, params.name, quorumIndex); } // Sleep some time to not fully overload the whole network -void CDKGSessionHandler::SleepBeforePhase(QuorumPhase curPhase, - const uint256& expectedQuorumHash, - double randomSleepFactor, - const WhileWaitFunc& runWhileWaiting) const +void ActiveDKGSessionHandler::SleepBeforePhase(QuorumPhase curPhase, const uint256& expectedQuorumHash, + double randomSleepFactor, const WhileWaitFunc& runWhileWaiting) const { if (!curSession->AreWeMember()) { // Non-members do not participate and do not create any network load, no need to sleep. @@ -186,11 +205,11 @@ void CDKGSessionHandler::SleepBeforePhase(QuorumPhase curPhase, int heightTmp{currentHeight.load()}; int heightStart{heightTmp}; - LogPrint(BCLog::LLMQ_DKG, "CDKGSessionManager::%s -- %s qi[%d] - starting sleep for %d ms, curPhase=%d\n", __func__, params.name, quorumIndex, sleepTime, ToUnderlying(curPhase)); + LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d] - starting sleep for %d ms, curPhase=%d\n", __func__, params.name, quorumIndex, sleepTime, ToUnderlying(curPhase)); while (TicksSinceEpoch(SystemClock::now()) < endTime) { if (stopRequested) { - LogPrint(BCLog::LLMQ_DKG, "CDKGSessionManager::%s -- %s qi[%d] - aborting due to stop/shutdown requested\n", __func__, params.name, quorumIndex); + LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d] - aborting due to stop/shutdown requested\n", __func__, params.name, quorumIndex); throw AbortPhaseException(); } auto cur_height = currentHeight.load(); @@ -205,7 +224,7 @@ void CDKGSessionHandler::SleepBeforePhase(QuorumPhase curPhase, } if (WITH_LOCK(cs_phase_qhash, return phase != curPhase || quorumHash != expectedQuorumHash)) { // Something went wrong and/or we missed quite a few blocks and it's just too late now - LogPrint(BCLog::LLMQ_DKG, "CDKGSessionManager::%s -- %s qi[%d] - aborting due unexpected phase/expectedQuorumHash change\n", __func__, params.name, quorumIndex); + LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d] - aborting due unexpected phase/expectedQuorumHash change\n", __func__, params.name, quorumIndex); throw AbortPhaseException(); } if (!runWhileWaiting()) { @@ -213,23 +232,20 @@ void CDKGSessionHandler::SleepBeforePhase(QuorumPhase curPhase, } } - LogPrint(BCLog::LLMQ_DKG, "CDKGSessionManager::%s -- %s qi[%d] - done, curPhase=%d\n", __func__, params.name, quorumIndex, ToUnderlying(curPhase)); + LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d] - done, curPhase=%d\n", __func__, params.name, quorumIndex, ToUnderlying(curPhase)); } -void CDKGSessionHandler::HandlePhase(QuorumPhase curPhase, - QuorumPhase nextPhase, - const uint256& expectedQuorumHash, - double randomSleepFactor, - const StartPhaseFunc& startPhaseFunc, - const WhileWaitFunc& runWhileWaiting) +void ActiveDKGSessionHandler::HandlePhase(QuorumPhase curPhase, QuorumPhase nextPhase, + const uint256& expectedQuorumHash, double randomSleepFactor, + const StartPhaseFunc& startPhaseFunc, const WhileWaitFunc& runWhileWaiting) { - LogPrint(BCLog::LLMQ_DKG, "CDKGSessionManager::%s -- %s qi[%d] - starting, curPhase=%d, nextPhase=%d\n", __func__, params.name, quorumIndex, ToUnderlying(curPhase), ToUnderlying(nextPhase)); + LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d] - starting, curPhase=%d, nextPhase=%d\n", __func__, params.name, quorumIndex, ToUnderlying(curPhase), ToUnderlying(nextPhase)); SleepBeforePhase(curPhase, expectedQuorumHash, randomSleepFactor, runWhileWaiting); startPhaseFunc(); WaitForNextPhase(curPhase, nextPhase, expectedQuorumHash, runWhileWaiting); - LogPrint(BCLog::LLMQ_DKG, "CDKGSessionManager::%s -- %s qi[%d] - done, curPhase=%d, nextPhase=%d\n", __func__, params.name, quorumIndex, ToUnderlying(curPhase), ToUnderlying(nextPhase)); + LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d] - done, curPhase=%d, nextPhase=%d\n", __func__, params.name, quorumIndex, ToUnderlying(curPhase), ToUnderlying(nextPhase)); } // returns a set of NodeIds which sent invalid messages @@ -406,7 +422,7 @@ bool ProcessPendingMessageBatch(const CConnman& connman, CDKGSession& session, C return true; } -void CDKGSessionHandler::HandleDKGRound(CConnman& connman, PeerManager& peerman) +void ActiveDKGSessionHandler::HandleDKGRound(CConnman& connman, PeerManager& peerman) { WaitForNextPhase(std::nullopt, QuorumPhase::Initialized); @@ -425,7 +441,7 @@ void CDKGSessionHandler::HandleDKGRound(CConnman& connman, PeerManager& peerman) throw AbortPhaseException(); } - dkgDebugManager.UpdateLocalSessionStatus(params.type, quorumIndex, [&](CDKGDebugSessionStatus& status) { + m_dkgdbgman.UpdateLocalSessionStatus(params.type, quorumIndex, [&](CDKGDebugSessionStatus& status) { bool changed = status.phase != QuorumPhase::Initialized; status.phase = QuorumPhase::Initialized; return changed; @@ -434,7 +450,7 @@ void CDKGSessionHandler::HandleDKGRound(CConnman& connman, PeerManager& peerman) if (params.is_single_member()) { auto finalCommitment = curSession->FinalizeSingleCommitment(); if (!finalCommitment.IsNull()) { // it can be null only if we are not member - if (auto inv_opt = quorumBlockProcessor.AddMineableCommitment(finalCommitment); inv_opt.has_value()) { + if (auto inv_opt = m_qblockman.AddMineableCommitment(finalCommitment); inv_opt.has_value()) { peerman.RelayInv(inv_opt.value()); } } @@ -443,11 +459,12 @@ void CDKGSessionHandler::HandleDKGRound(CConnman& connman, PeerManager& peerman) } const auto tip_mn_list = m_dmnman.GetListAtChainTip(); - utils::EnsureQuorumConnections(params, connman, m_sporkman, {m_dmnman, m_qsnapman, m_chainman, pQuorumBaseBlockIndex}, tip_mn_list, - curSession->myProTxHash, /*is_masternode=*/m_mn_activeman != nullptr, m_quorums_watch); + utils::EnsureQuorumConnections(params, connman, m_sporkman, {m_dmnman, m_qsnapman, m_chainman, pQuorumBaseBlockIndex}, + tip_mn_list, curSession->myProTxHash, /*is_masternode=*/true, m_quorums_watch); if (curSession->AreWeMember()) { - utils::AddQuorumProbeConnections(params, connman, m_mn_metaman, m_sporkman, {m_dmnman, m_qsnapman, m_chainman, - pQuorumBaseBlockIndex}, tip_mn_list, curSession->myProTxHash); + utils::AddQuorumProbeConnections(params, connman, m_mn_metaman, m_sporkman, + {m_dmnman, m_qsnapman, m_chainman, pQuorumBaseBlockIndex}, tip_mn_list, + curSession->myProTxHash); } WaitForNextPhase(QuorumPhase::Initialized, QuorumPhase::Contribute, curQuorumHash); @@ -485,29 +502,29 @@ void CDKGSessionHandler::HandleDKGRound(CConnman& connman, PeerManager& peerman) auto finalCommitments = curSession->FinalizeCommitments(); for (const auto& fqc : finalCommitments) { - if (auto inv_opt = quorumBlockProcessor.AddMineableCommitment(fqc); inv_opt.has_value()) { + if (auto inv_opt = m_qblockman.AddMineableCommitment(fqc); inv_opt.has_value()) { peerman.RelayInv(inv_opt.value()); } } } -void CDKGSessionHandler::PhaseHandlerThread(CConnman& connman, PeerManager& peerman) +void ActiveDKGSessionHandler::PhaseHandlerThread(CConnman& connman, PeerManager& peerman) { while (!stopRequested) { try { - LogPrint(BCLog::LLMQ_DKG, "CDKGSessionHandler::%s -- %s qi[%d] - starting HandleDKGRound\n", __func__, params.name, quorumIndex); + LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d] - starting HandleDKGRound\n", __func__, params.name, quorumIndex); HandleDKGRound(connman, peerman); } catch (AbortPhaseException& e) { - dkgDebugManager.UpdateLocalSessionStatus(params.type, quorumIndex, [&](CDKGDebugSessionStatus& status) { + m_dkgdbgman.UpdateLocalSessionStatus(params.type, quorumIndex, [&](CDKGDebugSessionStatus& status) { status.statusBits.aborted = true; return true; }); - LogPrint(BCLog::LLMQ_DKG, "CDKGSessionHandler::%s -- %s qi[%d] - aborted current DKG session\n", __func__, params.name, quorumIndex); + LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d] - aborted current DKG session\n", __func__, params.name, quorumIndex); } } } -bool CDKGSessionHandler::GetContribution(const uint256& hash, CDKGContribution& ret) const +bool ActiveDKGSessionHandler::GetContribution(const uint256& hash, CDKGContribution& ret) const { LOCK(curSession->invCs); auto it = curSession->contributions.find(hash); @@ -518,7 +535,7 @@ bool CDKGSessionHandler::GetContribution(const uint256& hash, CDKGContribution& return false; } -bool CDKGSessionHandler::GetComplaint(const uint256& hash, CDKGComplaint& ret) const +bool ActiveDKGSessionHandler::GetComplaint(const uint256& hash, CDKGComplaint& ret) const { LOCK(curSession->invCs); auto it = curSession->complaints.find(hash); @@ -529,7 +546,7 @@ bool CDKGSessionHandler::GetComplaint(const uint256& hash, CDKGComplaint& ret) c return false; } -bool CDKGSessionHandler::GetJustification(const uint256& hash, CDKGJustification& ret) const +bool ActiveDKGSessionHandler::GetJustification(const uint256& hash, CDKGJustification& ret) const { LOCK(curSession->invCs); auto it = curSession->justifications.find(hash); @@ -540,7 +557,7 @@ bool CDKGSessionHandler::GetJustification(const uint256& hash, CDKGJustification return false; } -bool CDKGSessionHandler::GetPrematureCommitment(const uint256& hash, CDKGPrematureCommitment& ret) const +bool ActiveDKGSessionHandler::GetPrematureCommitment(const uint256& hash, CDKGPrematureCommitment& ret) const { LOCK(curSession->invCs); auto it = curSession->prematureCommitments.find(hash); @@ -550,4 +567,9 @@ bool CDKGSessionHandler::GetPrematureCommitment(const uint256& hash, CDKGPrematu } return false; } + +QuorumPhase ActiveDKGSessionHandler::GetPhase() const +{ + return WITH_LOCK(cs_phase_qhash, return phase); +} } // namespace llmq diff --git a/src/active/dkgsessionhandler.h b/src/active/dkgsessionhandler.h new file mode 100644 index 000000000000..a7b009209564 --- /dev/null +++ b/src/active/dkgsessionhandler.h @@ -0,0 +1,87 @@ +// Copyright (c) 2018-2025 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_ACTIVE_DKGSESSIONHANDLER_H +#define BITCOIN_ACTIVE_DKGSESSIONHANDLER_H + +#include + +namespace llmq { +class ActiveDKGSessionHandler final : public llmq::CDKGSessionHandler +{ + using StartPhaseFunc = std::function; + using WhileWaitFunc = std::function; + +private: + CBLSWorker& m_bls_worker; + CDeterministicMNManager& m_dmnman; + CMasternodeMetaMan& m_mn_metaman; + llmq::CDKGDebugManager& m_dkgdbgman; + llmq::CDKGSessionManager& m_qdkgsman; + llmq::CQuorumBlockProcessor& m_qblockman; + llmq::CQuorumSnapshotManager& m_qsnapman; + const CActiveMasternodeManager& m_mn_activeman; + const ChainstateManager& m_chainman; + const CSporkManager& m_sporkman; + const bool m_quorums_watch{false}; + +private: + std::atomic stopRequested{false}; + std::atomic currentHeight{-1}; + std::string m_thread_name; + std::thread phaseHandlerThread; + + mutable Mutex cs_phase_qhash; + QuorumPhase phase GUARDED_BY(cs_phase_qhash){QuorumPhase::Idle}; + uint256 quorumHash GUARDED_BY(cs_phase_qhash); + +public: + ActiveDKGSessionHandler() = delete; + ActiveDKGSessionHandler(const ActiveDKGSessionHandler&) = delete; + ActiveDKGSessionHandler& operator=(const ActiveDKGSessionHandler&) = delete; + ActiveDKGSessionHandler(CBLSWorker& bls_worker, CDeterministicMNManager& dmnman, CMasternodeMetaMan& mn_metaman, + llmq::CDKGDebugManager& dkgdbgman, llmq::CDKGSessionManager& qdkgsman, + llmq::CQuorumBlockProcessor& qblockman, llmq::CQuorumSnapshotManager& qsnapman, + const CActiveMasternodeManager& mn_activeman, const ChainstateManager& chainman, + const CSporkManager& sporkman, const Consensus::LLMQParams& llmq_params, bool quorums_watch, + int quorums_idx); + ~ActiveDKGSessionHandler(); + +public: + //! CDKGSessionHandler + bool GetContribution(const uint256& hash, CDKGContribution& ret) const override; + bool GetComplaint(const uint256& hash, CDKGComplaint& ret) const override; + bool GetJustification(const uint256& hash, CDKGJustification& ret) const override; + bool GetPrematureCommitment(const uint256& hash, CDKGPrematureCommitment& ret) const override; + QuorumPhase GetPhase() const override EXCLUSIVE_LOCKS_REQUIRED(!cs_phase_qhash); + void StartThread(CConnman& connman, PeerManager& peerman) override; + void StopThread() override; + void UpdatedBlockTip(const CBlockIndex* pindexNew) override EXCLUSIVE_LOCKS_REQUIRED(!cs_phase_qhash); + +private: + std::pair GetPhaseAndQuorumHash() const EXCLUSIVE_LOCKS_REQUIRED(!cs_phase_qhash); + + bool InitNewQuorum(const CBlockIndex* pQuorumBaseBlockIndex); + + /** + * @param curPhase current QuorumPhase + * @param nextPhase next QuorumPhase + * @param expectedQuorumHash expected QuorumHash, defaults to null + * @param shouldNotWait function that returns bool, defaults to function that returns false. If the function returns false, we will wait in the loop, if true, we don't wait + */ + void WaitForNextPhase( + std::optional curPhase, QuorumPhase nextPhase, const uint256& expectedQuorumHash = uint256(), + const WhileWaitFunc& shouldNotWait = [] { return false; }) const EXCLUSIVE_LOCKS_REQUIRED(!cs_phase_qhash); + void WaitForNewQuorum(const uint256& oldQuorumHash) const EXCLUSIVE_LOCKS_REQUIRED(!cs_phase_qhash); + void SleepBeforePhase(QuorumPhase curPhase, const uint256& expectedQuorumHash, double randomSleepFactor, + const WhileWaitFunc& runWhileWaiting) const EXCLUSIVE_LOCKS_REQUIRED(!cs_phase_qhash); + void HandlePhase(QuorumPhase curPhase, QuorumPhase nextPhase, const uint256& expectedQuorumHash, + double randomSleepFactor, const StartPhaseFunc& startPhaseFunc, + const WhileWaitFunc& runWhileWaiting) EXCLUSIVE_LOCKS_REQUIRED(!cs_phase_qhash); + void HandleDKGRound(CConnman& connman, PeerManager& peerman) EXCLUSIVE_LOCKS_REQUIRED(!cs_phase_qhash); + void PhaseHandlerThread(CConnman& connman, PeerManager& peerman) EXCLUSIVE_LOCKS_REQUIRED(!cs_phase_qhash); +}; +} // namespace llmq + +#endif // BITCOIN_ACTIVE_DKGSESSIONHANDLER_H diff --git a/src/llmq/dkgsession.h b/src/llmq/dkgsession.h index fe7cf8cbf818..df8b45c32ad2 100644 --- a/src/llmq/dkgsession.h +++ b/src/llmq/dkgsession.h @@ -27,17 +27,17 @@ class CDeterministicMN; class CMasternodeMetaMan; class CSporkManager; class PeerManager; - -namespace llmq -{ - -class CFinalCommitment; +namespace llmq { +class ActiveDKGSessionHandler; class CDKGDebugManager; +class CDKGPendingMessages; class CDKGSession; class CDKGSessionManager; -class CDKGPendingMessages; +class CFinalCommitment; class CQuorumSnapshotManager; +} // namespace llmq +namespace llmq { class CDKGContribution { public: @@ -274,6 +274,7 @@ class CDKGLogger : public CBatchedLogger */ class CDKGSession { + friend class ActiveDKGSessionHandler; friend class CDKGSessionHandler; friend class CDKGSessionManager; friend class CDKGLogger; diff --git a/src/llmq/dkgsessionhandler.cpp b/src/llmq/dkgsessionhandler.cpp index 0f57c180b73e..71aac74b167d 100644 --- a/src/llmq/dkgsessionhandler.cpp +++ b/src/llmq/dkgsessionhandler.cpp @@ -30,21 +30,10 @@ CDKGSessionHandler::CDKGSessionHandler(CBLSWorker& _blsWorker, CDeterministicMNM CQuorumSnapshotManager& qsnapman, const CActiveMasternodeManager* const mn_activeman, const ChainstateManager& chainman, const CSporkManager& sporkman, const Consensus::LLMQParams& _params, bool quorums_watch, int _quorumIndex) : - blsWorker{_blsWorker}, - m_dmnman{dmnman}, - dkgDebugManager{_dkgDebugManager}, - dkgManager{_dkgManager}, - m_mn_metaman{mn_metaman}, - quorumBlockProcessor{_quorumBlockProcessor}, - m_qsnapman{qsnapman}, - m_mn_activeman{mn_activeman}, - m_chainman{chainman}, - m_sporkman{sporkman}, params{_params}, - m_quorums_watch{quorums_watch}, quorumIndex{_quorumIndex}, curSession{std::make_unique(nullptr, _params, _blsWorker, dmnman, _dkgManager, _dkgDebugManager, - m_mn_metaman, m_qsnapman, m_mn_activeman, m_chainman, m_sporkman)}, + mn_metaman, qsnapman, mn_activeman, chainman, sporkman)}, // we allow size*2 messages as we need to make sure we see bad behavior (double messages) pendingContributions{(size_t)_params.size * 2, MSG_QUORUM_CONTRIB}, pendingComplaints{(size_t)_params.size * 2, MSG_QUORUM_COMPLAINT}, diff --git a/src/llmq/dkgsessionhandler.h b/src/llmq/dkgsessionhandler.h index 0a14642c4ea8..ccede6cce8dc 100644 --- a/src/llmq/dkgsessionhandler.h +++ b/src/llmq/dkgsessionhandler.h @@ -130,31 +130,11 @@ class CDKGSessionHandler private: friend class CDKGSessionManager; -private: - std::atomic stopRequested{false}; - - CBLSWorker& blsWorker; - CDeterministicMNManager& m_dmnman; - CDKGDebugManager& dkgDebugManager; - CDKGSessionManager& dkgManager; - CMasternodeMetaMan& m_mn_metaman; - CQuorumBlockProcessor& quorumBlockProcessor; - CQuorumSnapshotManager& m_qsnapman; - const CActiveMasternodeManager* const m_mn_activeman; - const ChainstateManager& m_chainman; - const CSporkManager& m_sporkman; +protected: const Consensus::LLMQParams params; - const bool m_quorums_watch{false}; const int quorumIndex; - std::atomic currentHeight {-1}; - mutable Mutex cs_phase_qhash; - QuorumPhase phase GUARDED_BY(cs_phase_qhash) {QuorumPhase::Idle}; - uint256 quorumHash GUARDED_BY(cs_phase_qhash); - std::unique_ptr curSession; - std::thread phaseHandlerThread; - std::string m_thread_name; // Do not guard these, they protect their internals themselves CDKGPendingMessages pendingContributions; @@ -169,45 +149,20 @@ class CDKGSessionHandler const CActiveMasternodeManager* const mn_activeman, const ChainstateManager& chainman, const CSporkManager& sporkman, const Consensus::LLMQParams& _params, bool quorums_watch, int _quorumIndex); - ~CDKGSessionHandler(); + virtual ~CDKGSessionHandler(); - void UpdatedBlockTip(const CBlockIndex* pindexNew) EXCLUSIVE_LOCKS_REQUIRED(!cs_phase_qhash); [[nodiscard]] MessageProcessingResult ProcessMessage(NodeId from, std::string_view msg_type, CDataStream& vRecv); - void StartThread(CConnman& connman, PeerManager& peerman); - void StopThread(); - - bool GetContribution(const uint256& hash, CDKGContribution& ret) const; - bool GetComplaint(const uint256& hash, CDKGComplaint& ret) const; - bool GetJustification(const uint256& hash, CDKGJustification& ret) const; - bool GetPrematureCommitment(const uint256& hash, CDKGPrematureCommitment& ret) const; - -private: - bool InitNewQuorum(const CBlockIndex* pQuorumBaseBlockIndex); - - std::pair GetPhaseAndQuorumHash() const EXCLUSIVE_LOCKS_REQUIRED(!cs_phase_qhash); - - using StartPhaseFunc = std::function; - using WhileWaitFunc = std::function; - /** - * @param curPhase current QuorumPhase - * @param nextPhase next QuorumPhase - * @param expectedQuorumHash expected QuorumHash, defaults to null - * @param shouldNotWait function that returns bool, defaults to function that returns false. If the function returns false, we will wait in the loop, if true, we don't wait - */ - void WaitForNextPhase( - std::optional curPhase, QuorumPhase nextPhase, const uint256& expectedQuorumHash = uint256(), - const WhileWaitFunc& shouldNotWait = [] { return false; }) const EXCLUSIVE_LOCKS_REQUIRED(!cs_phase_qhash); - void WaitForNewQuorum(const uint256& oldQuorumHash) const EXCLUSIVE_LOCKS_REQUIRED(!cs_phase_qhash); - void SleepBeforePhase(QuorumPhase curPhase, const uint256& expectedQuorumHash, double randomSleepFactor, - const WhileWaitFunc& runWhileWaiting) const EXCLUSIVE_LOCKS_REQUIRED(!cs_phase_qhash); - void HandlePhase(QuorumPhase curPhase, QuorumPhase nextPhase, const uint256& expectedQuorumHash, - double randomSleepFactor, const StartPhaseFunc& startPhaseFunc, - const WhileWaitFunc& runWhileWaiting) EXCLUSIVE_LOCKS_REQUIRED(!cs_phase_qhash); - void HandleDKGRound(CConnman& connman, PeerManager& peerman) EXCLUSIVE_LOCKS_REQUIRED(!cs_phase_qhash); - void PhaseHandlerThread(CConnman& connman, PeerManager& peerman) EXCLUSIVE_LOCKS_REQUIRED(!cs_phase_qhash); +public: + virtual bool GetContribution(const uint256& hash, CDKGContribution& ret) const { return false; } + virtual bool GetComplaint(const uint256& hash, CDKGComplaint& ret) const { return false; } + virtual bool GetJustification(const uint256& hash, CDKGJustification& ret) const { return false; } + virtual bool GetPrematureCommitment(const uint256& hash, CDKGPrematureCommitment& ret) const { return false; } + virtual QuorumPhase GetPhase() const { return QuorumPhase::Idle; } + virtual void StartThread(CConnman& connman, PeerManager& peerman) {} + virtual void StopThread() {} + virtual void UpdatedBlockTip(const CBlockIndex* pindexNew) {} }; - } // namespace llmq #endif // BITCOIN_LLMQ_DKGSESSIONHANDLER_H diff --git a/src/llmq/dkgsessionmgr.cpp b/src/llmq/dkgsessionmgr.cpp index b879ef8f0b61..0c9a74f0570b 100644 --- a/src/llmq/dkgsessionmgr.cpp +++ b/src/llmq/dkgsessionmgr.cpp @@ -190,9 +190,7 @@ bool CDKGSessionManager::GetContribution(const uint256& hash, CDKGContribution& return false; for (const auto& [_, dkgType] : dkgSessionHandlers) { - assert(dkgType); - LOCK(dkgType->cs_phase_qhash); - const auto dkgPhase = dkgType->phase; + const auto dkgPhase = Assert(dkgType)->GetPhase(); if (dkgPhase < QuorumPhase::Initialized || dkgPhase > QuorumPhase::Contribute) { continue; } @@ -209,9 +207,7 @@ bool CDKGSessionManager::GetComplaint(const uint256& hash, CDKGComplaint& ret) c return false; for (const auto& [_, dkgType] : dkgSessionHandlers) { - assert(dkgType); - LOCK(dkgType->cs_phase_qhash); - const auto dkgPhase = dkgType->phase; + const auto dkgPhase = Assert(dkgType)->GetPhase(); if (dkgPhase < QuorumPhase::Contribute || dkgPhase > QuorumPhase::Complain) { continue; } @@ -228,9 +224,7 @@ bool CDKGSessionManager::GetJustification(const uint256& hash, CDKGJustification return false; for (const auto& [_, dkgType] : dkgSessionHandlers) { - assert(dkgType); - LOCK(dkgType->cs_phase_qhash); - const auto dkgPhase = dkgType->phase; + const auto dkgPhase = Assert(dkgType)->GetPhase(); if (dkgPhase < QuorumPhase::Complain || dkgPhase > QuorumPhase::Justify) { continue; } @@ -247,9 +241,7 @@ bool CDKGSessionManager::GetPrematureCommitment(const uint256& hash, CDKGPrematu return false; for (const auto& [_, dkgType] : dkgSessionHandlers) { - assert(dkgType); - LOCK(dkgType->cs_phase_qhash); - const auto dkgPhase = dkgType->phase; + const auto dkgPhase = Assert(dkgType)->GetPhase(); if (dkgPhase < QuorumPhase::Justify || dkgPhase > QuorumPhase::Commit) { continue; } @@ -418,5 +410,4 @@ void CDKGSessionManager::CleanupOldContributions() const } } } - } // namespace llmq diff --git a/src/llmq/observer/context.cpp b/src/llmq/observer/context.cpp index ffce306bb3cf..7a2ae6f29f76 100644 --- a/src/llmq/observer/context.cpp +++ b/src/llmq/observer/context.cpp @@ -25,12 +25,12 @@ ObserverContext::ObserverContext(CBLSWorker& bls_worker, CConnman& connman, CDet qman_handler{std::make_unique(connman, dmnman, qman, qsnapman, chainman, mn_sync, sporkman, sync_map, quorums_recovery)} { - qdkgsman->InitializeHandlers( - [&](const Consensus::LLMQParams& llmq_params, int quorum_idx) -> std::unique_ptr { - return std::make_unique(bls_worker, dmnman, *dkgdbgman, *qdkgsman, mn_metaman, - qblockman, qsnapman, /*mn_activeman=*/nullptr, chainman, - sporkman, llmq_params, /*quorums_watch=*/true, quorum_idx); - }); + qdkgsman->InitializeHandlers([&](const Consensus::LLMQParams& llmq_params, + int quorum_idx) -> std::unique_ptr { + return std::make_unique(bls_worker, dmnman, *dkgdbgman, *qdkgsman, mn_metaman, + qblockman, qsnapman, /*mn_activeman=*/nullptr, chainman, + sporkman, llmq_params, /*quorums_watch=*/true, quorum_idx); + }); m_qman.ConnectManagers(qman_handler.get(), qdkgsman.get()); } diff --git a/src/masternode/active/context.cpp b/src/masternode/active/context.cpp index f6c500b83972..43cd7c90a7e6 100644 --- a/src/masternode/active/context.cpp +++ b/src/masternode/active/context.cpp @@ -4,6 +4,7 @@ #include +#include #include #include #include @@ -44,13 +45,13 @@ ActiveContext::ActiveContext(CCoinJoinServer& cj_server, CConnman& connman, CDet *llmq_ctx.isman, *llmq_ctx.sigman, *shareman, *llmq_ctx.qman, sporkman, mempool, mn_sync)} { - qdkgsman->InitializeHandlers( - [&](const Consensus::LLMQParams& llmq_params, int quorum_idx) -> std::unique_ptr { - return std::make_unique(*llmq_ctx.bls_worker, dmnman, *dkgdbgman, *qdkgsman, - mn_metaman, *llmq_ctx.quorum_block_processor, - *llmq_ctx.qsnapman, &mn_activeman, chainman, sporkman, - llmq_params, quorums_watch, quorum_idx); - }); + qdkgsman->InitializeHandlers([&](const Consensus::LLMQParams& llmq_params, + int quorum_idx) -> std::unique_ptr { + return std::make_unique(*llmq_ctx.bls_worker, dmnman, mn_metaman, *dkgdbgman, + *qdkgsman, *llmq_ctx.quorum_block_processor, + *llmq_ctx.qsnapman, mn_activeman, chainman, sporkman, + llmq_params, quorums_watch, quorum_idx); + }); m_llmq_ctx.clhandler->ConnectSigner(cl_signer.get()); m_llmq_ctx.isman->ConnectSigner(is_signer.get()); m_llmq_ctx.qman->ConnectManagers(qman_handler.get(), qdkgsman.get()); diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 851848a60d5d..e7598ace940b 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -2937,8 +2937,7 @@ void PeerManagerImpl::ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic if (!push && (inv.type == MSG_QUORUM_CONTRIB)) { llmq::CDKGContribution o; - if ((m_observer_ctx && m_observer_ctx->qdkgsman->GetContribution(inv.hash, o)) - || (m_active_ctx && m_active_ctx->qdkgsman->GetContribution(inv.hash, o))) { + if (m_active_ctx && m_active_ctx->qdkgsman->GetContribution(inv.hash, o)) { m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::QCONTRIB, o)); push = true; } @@ -2946,8 +2945,7 @@ void PeerManagerImpl::ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic if (!push && (inv.type == MSG_QUORUM_COMPLAINT)) { llmq::CDKGComplaint o; - if ((m_observer_ctx && m_observer_ctx->qdkgsman->GetComplaint(inv.hash, o)) - || (m_active_ctx && m_active_ctx->qdkgsman->GetComplaint(inv.hash, o))) { + if (m_active_ctx && m_active_ctx->qdkgsman->GetComplaint(inv.hash, o)) { m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::QCOMPLAINT, o)); push = true; } @@ -2955,8 +2953,7 @@ void PeerManagerImpl::ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic if (!push && (inv.type == MSG_QUORUM_JUSTIFICATION)) { llmq::CDKGJustification o; - if ((m_observer_ctx && m_observer_ctx->qdkgsman->GetJustification(inv.hash, o)) - || (m_active_ctx && m_active_ctx->qdkgsman->GetJustification(inv.hash, o))) { + if (m_active_ctx && m_active_ctx->qdkgsman->GetJustification(inv.hash, o)) { m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::QJUSTIFICATION, o)); push = true; } @@ -2964,8 +2961,7 @@ void PeerManagerImpl::ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic if (!push && (inv.type == MSG_QUORUM_PREMATURE_COMMITMENT)) { llmq::CDKGPrematureCommitment o; - if ((m_observer_ctx && m_observer_ctx->qdkgsman->GetPrematureCommitment(inv.hash, o)) - || (m_active_ctx && m_active_ctx->qdkgsman->GetPrematureCommitment(inv.hash, o))) { + if (m_active_ctx && m_active_ctx->qdkgsman->GetPrematureCommitment(inv.hash, o)) { m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::QPCOMMITMENT, o)); push = true; } From bb90306129e68d35f9971f037eb2c86ed12b1280 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Sun, 7 Dec 2025 07:23:39 +0530 Subject: [PATCH 05/14] move-only: move DKG session participant logic to `llmq/active` Review with `git log -p -n1 --color-moved=dimmed_zebra`. --- src/Makefile.am | 1 + src/active/dkgsession.cpp | 697 ++++++++++++++++++++++++++++++++++++++ src/llmq/dkgsession.cpp | 682 +------------------------------------ 3 files changed, 699 insertions(+), 681 deletions(-) create mode 100644 src/active/dkgsession.cpp diff --git a/src/Makefile.am b/src/Makefile.am index 0f5a40f29cd6..2706a31ddf90 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -483,6 +483,7 @@ libbitcoin_util_a-clientversion.$(OBJEXT): obj/build.h libbitcoin_node_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(BOOST_CPPFLAGS) $(MINIUPNPC_CPPFLAGS) $(NATPMP_CPPFLAGS) $(EVENT_CFLAGS) $(EVENT_PTHREADS_CFLAGS) libbitcoin_node_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) libbitcoin_node_a_SOURCES = \ + active/dkgsession.cpp \ active/dkgsessionhandler.cpp \ active/quorums.cpp \ addrdb.cpp \ diff --git a/src/active/dkgsession.cpp b/src/active/dkgsession.cpp new file mode 100644 index 000000000000..cb7e5986b762 --- /dev/null +++ b/src/active/dkgsession.cpp @@ -0,0 +1,697 @@ +// Copyright (c) 2018-2025 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +namespace llmq { +void CDKGSession::Contribute(CDKGPendingMessages& pendingMessages, PeerManager& peerman) +{ + CDKGLogger logger(*this, __func__, __LINE__); + + if (!AreWeMember()) { + return; + } + + assert(params.threshold > 1); // we should not get there with single-node-quorums + + cxxtimer::Timer t1(true); + logger.Batch("generating contributions"); + if (!blsWorker.GenerateContributions(params.threshold, memberIds, vvecContribution, m_sk_contributions)) { + // this should never happen actually + logger.Batch("GenerateContributions failed"); + return; + } + logger.Batch("generated contributions. time=%d", t1.count()); + logger.Flush(); + + SendContributions(pendingMessages, peerman); +} + +void CDKGSession::SendContributions(CDKGPendingMessages& pendingMessages, PeerManager& peerman) +{ + CDKGLogger logger(*this, __func__, __LINE__); + + assert(AreWeMember()); + + logger.Batch("sending contributions"); + + if (ShouldSimulateError(DKGError::type::CONTRIBUTION_OMIT)) { + logger.Batch("omitting"); + return; + } + + CDKGContribution qc; + qc.llmqType = params.type; + qc.quorumHash = m_quorum_base_block_index->GetBlockHash(); + qc.proTxHash = myProTxHash; + qc.vvec = vvecContribution; + + cxxtimer::Timer t1(true); + qc.contributions = std::make_shared>(); + qc.contributions->InitEncrypt(members.size()); + + for (const auto i : irange::range(members.size())) { + const auto& m = members[i]; + CBLSSecretKey skContrib = m_sk_contributions[i]; + + if (i != myIdx && ShouldSimulateError(DKGError::type::CONTRIBUTION_LIE)) { + logger.Batch("lying for %s", m->dmn->proTxHash.ToString()); + skContrib.MakeNewKey(); + } + + if (!qc.contributions->Encrypt(i, m->dmn->pdmnState->pubKeyOperator.Get(), skContrib, PROTOCOL_VERSION)) { + logger.Batch("failed to encrypt contribution for %s", m->dmn->proTxHash.ToString()); + return; + } + } + + logger.Batch("encrypted contributions. time=%d", t1.count()); + + qc.sig = m_mn_activeman->Sign(qc.GetSignHash(), m_use_legacy_bls); + + logger.Flush(); + + dkgDebugManager.UpdateLocalSessionStatus(params.type, quorumIndex, [&](CDKGDebugSessionStatus& status) { + status.statusBits.sentContributions = true; + return true; + }); + + pendingMessages.PushPendingMessage(-1, qc, peerman); +} + +// Verifies all pending secret key contributions in one batch +// This is done by aggregating the verification vectors belonging to the secret key contributions +// The resulting aggregated vvec is then used to recover a public key share +// The public key share must match the public key belonging to the aggregated secret key contributions +// See CBLSWorker::VerifyContributionShares for more details. +void CDKGSession::VerifyPendingContributions() +{ + AssertLockHeld(cs_pending); + + CDKGLogger logger(*this, __func__, __LINE__); + + cxxtimer::Timer t1(true); + + if (pendingContributionVerifications.empty()) { + return; + } + + std::vector memberIndexes; + std::vector vvecs; + std::vector skContributions; + + for (const auto& idx : pendingContributionVerifications) { + const auto& m = members[idx]; + if (m->bad || m->weComplain) { + continue; + } + memberIndexes.emplace_back(idx); + vvecs.emplace_back(receivedVvecs[idx]); + skContributions.emplace_back(receivedSkContributions[idx]); + // Write here to definitely store one contribution for each member no matter if + // our share is valid or not, could be that others are still correct + dkgManager.WriteEncryptedContributions(params.type, m_quorum_base_block_index, m->dmn->proTxHash, *vecEncryptedContributions[idx]); + } + + auto result = blsWorker.VerifyContributionShares(myId, vvecs, skContributions); + if (result.size() != memberIndexes.size()) { + logger.Batch("VerifyContributionShares returned result of size %d but size %d was expected, something is wrong", result.size(), memberIndexes.size()); + return; + } + + for (const auto i : irange::range(memberIndexes.size())) { + if (!result[i]) { + const auto& m = members[memberIndexes[i]]; + logger.Batch("invalid contribution from %s. will complain later", m->dmn->proTxHash.ToString()); + m->weComplain = true; + dkgDebugManager.UpdateLocalMemberStatus(params.type, quorumIndex, m->idx, [&](CDKGDebugMemberStatus& status) { + status.statusBits.weComplain = true; + return true; + }); + } else { + size_t memberIdx = memberIndexes[i]; + dkgManager.WriteVerifiedSkContribution(params.type, m_quorum_base_block_index, members[memberIdx]->dmn->proTxHash, skContributions[i]); + } + } + + logger.Batch("verified %d pending contributions. time=%d", pendingContributionVerifications.size(), t1.count()); + pendingContributionVerifications.clear(); +} + +void CDKGSession::VerifyAndComplain(CConnman& connman, CDKGPendingMessages& pendingMessages, PeerManager& peerman) +{ + if (!AreWeMember()) { + return; + } + + { + LOCK(cs_pending); + VerifyPendingContributions(); + } + + CDKGLogger logger(*this, __func__, __LINE__); + + // we check all members if they sent us their contributions + // we consider members as bad if they missed to send anything or if they sent multiple + // in both cases we won't give them a second chance as they might be either down, buggy or an adversary + // we assume that such a participant will be marked as bad by the whole network in most cases, + // as propagation will ensure that all nodes see the same vvecs/contributions. In case nodes come to + // different conclusions, the aggregation phase will handle this (most voted quorum key wins). + + cxxtimer::Timer t1(true); + + for (const auto& m : members) { + if (m->bad) { + continue; + } + if (m->contributions.empty()) { + logger.Batch("%s did not send any contribution", m->dmn->proTxHash.ToString()); + MarkBadMember(m->idx); + continue; + } + } + + logger.Batch("verified contributions. time=%d", t1.count()); + logger.Flush(); + + VerifyConnectionAndMinProtoVersions(connman); + + SendComplaint(pendingMessages, peerman); +} + +void CDKGSession::VerifyConnectionAndMinProtoVersions(CConnman& connman) const +{ + assert(m_mn_metaman.IsValid()); + + if (!IsQuorumPoseEnabled(params.type, m_sporkman)) { + return; + } + + CDKGLogger logger(*this, __func__, __LINE__); + + Uint256HashMap protoMap; + connman.ForEachNode([&](const CNode* pnode) { + auto verifiedProRegTxHash = pnode->GetVerifiedProRegTxHash(); + if (verifiedProRegTxHash.IsNull()) { + return; + } + protoMap.emplace(verifiedProRegTxHash, pnode->nVersion); + }); + + bool fShouldAllMembersBeConnected = IsAllMembersConnectedEnabled(params.type, m_sporkman); + for (const auto& m : members) { + if (m->dmn->proTxHash == myProTxHash) { + continue; + } + if (auto it = protoMap.find(m->dmn->proTxHash); it == protoMap.end()) { + m->badConnection = fShouldAllMembersBeConnected; + if (m->badConnection) { + logger.Batch("%s is not connected to us, badConnection=1", m->dmn->proTxHash.ToString()); + } + } else if (it->second < MIN_MASTERNODE_PROTO_VERSION) { + m->badConnection = true; + logger.Batch("%s does not have min proto version %d (has %d)", m->dmn->proTxHash.ToString(), MIN_MASTERNODE_PROTO_VERSION, it->second); + } + if (m_mn_metaman.OutboundFailedTooManyTimes(m->dmn->proTxHash)) { + m->badConnection = true; + logger.Batch("%s failed to connect to it too many times", m->dmn->proTxHash.ToString()); + } + if (m_mn_metaman.IsPlatformBanned(m->dmn->proTxHash)) { + m->badConnection = true; + logger.Batch("%s is Platform PoSe banned", m->dmn->proTxHash.ToString()); + } + } +} + +void CDKGSession::SendComplaint(CDKGPendingMessages& pendingMessages, PeerManager& peerman) +{ + CDKGLogger logger(*this, __func__, __LINE__); + + assert(AreWeMember()); + + CDKGComplaint qc(params); + qc.llmqType = params.type; + qc.quorumHash = m_quorum_base_block_index->GetBlockHash(); + qc.proTxHash = myProTxHash; + + int badCount = 0; + int complaintCount = 0; + for (const auto i : irange::range(members.size())) { + const auto& m = members[i]; + if (m->bad || m->badConnection) { + qc.badMembers[i] = true; + badCount++; + } else if (m->weComplain) { + qc.complainForMembers[i] = true; + complaintCount++; + } + } + + if (badCount == 0 && complaintCount == 0) { + return; + } + + logger.Batch("sending complaint. badCount=%d, complaintCount=%d", badCount, complaintCount); + + qc.sig = m_mn_activeman->Sign(qc.GetSignHash(), m_use_legacy_bls); + + logger.Flush(); + + dkgDebugManager.UpdateLocalSessionStatus(params.type, quorumIndex, [&](CDKGDebugSessionStatus& status) { + status.statusBits.sentComplaint = true; + return true; + }); + + pendingMessages.PushPendingMessage(-1, qc, peerman); +} + +void CDKGSession::VerifyAndJustify(CDKGPendingMessages& pendingMessages, PeerManager& peerman) +{ + if (!AreWeMember()) { + return; + } + + CDKGLogger logger(*this, __func__, __LINE__); + + std::set justifyFor; + + for (const auto& m : members) { + if (m->bad) { + continue; + } + if (m->badMemberVotes.size() >= size_t(params.dkgBadVotesThreshold)) { + logger.Batch("%s marked as bad as %d other members voted for this", m->dmn->proTxHash.ToString(), m->badMemberVotes.size()); + MarkBadMember(m->idx); + continue; + } + if (m->complaints.empty()) { + continue; + } + if (m->complaints.size() != 1) { + logger.Batch("%s sent multiple complaints", m->dmn->proTxHash.ToString()); + MarkBadMember(m->idx); + continue; + } + + LOCK(invCs); + if (const auto& qc = complaints.at(*m->complaints.begin()); + qc.complainForMembers[*myIdx]) { + justifyFor.emplace(qc.proTxHash); + } + } + + logger.Flush(); + if (!justifyFor.empty()) { + SendJustification(pendingMessages, peerman, justifyFor); + } +} + +void CDKGSession::SendJustification(CDKGPendingMessages& pendingMessages, PeerManager& peerman, + const std::set& forMembers) +{ + CDKGLogger logger(*this, __func__, __LINE__); + + assert(AreWeMember()); + + logger.Batch("sending justification for %d members", forMembers.size()); + + CDKGJustification qj; + qj.llmqType = params.type; + qj.quorumHash = m_quorum_base_block_index->GetBlockHash(); + qj.proTxHash = myProTxHash; + qj.contributions.reserve(forMembers.size()); + + for (const uint32_t i : irange::range(members.size())) { + const auto& m = members[i]; + if (forMembers.count(m->dmn->proTxHash) == 0) { + continue; + } + logger.Batch("justifying for %s", m->dmn->proTxHash.ToString()); + + CBLSSecretKey skContribution = m_sk_contributions[i]; + + if (i != myIdx && ShouldSimulateError(DKGError::type::JUSTIFY_LIE)) { + logger.Batch("lying for %s", m->dmn->proTxHash.ToString()); + skContribution.MakeNewKey(); + } + + qj.contributions.emplace_back(CDKGJustification::Contribution{i, skContribution}); + } + + if (ShouldSimulateError(DKGError::type::JUSTIFY_OMIT)) { + logger.Batch("omitting"); + return; + } + + qj.sig = m_mn_activeman->Sign(qj.GetSignHash(), m_use_legacy_bls); + + logger.Flush(); + + dkgDebugManager.UpdateLocalSessionStatus(params.type, quorumIndex, [&](CDKGDebugSessionStatus& status) { + status.statusBits.sentJustification = true; + return true; + }); + + pendingMessages.PushPendingMessage(-1, qj, peerman); +} + +void CDKGSession::VerifyAndCommit(CDKGPendingMessages& pendingMessages, PeerManager& peerman) +{ + if (!AreWeMember()) { + return; + } + + CDKGLogger logger(*this, __func__, __LINE__); + + std::vector badMembers; + badMembers.reserve(members.size()); + std::vector openComplaintMembers; + openComplaintMembers.reserve(members.size()); + + for (const auto& m : members) { + if (m->bad) { + badMembers.emplace_back(m->idx); + continue; + } + if (!m->complaintsFromOthers.empty()) { + MarkBadMember(m->idx); + openComplaintMembers.emplace_back(m->idx); + } + } + + if (!badMembers.empty() || !openComplaintMembers.empty()) { + logger.Batch("verification result:"); + } + if (!badMembers.empty()) { + logger.Batch(" members previously determined as bad:"); + for (const auto& idx : badMembers) { + logger.Batch(" %s", members[idx]->dmn->proTxHash.ToString()); + } + } + if (!openComplaintMembers.empty()) { + logger.Batch(" members with open complaints and now marked as bad:"); + for (const auto& idx : openComplaintMembers) { + logger.Batch(" %s", members[idx]->dmn->proTxHash.ToString()); + } + } + + logger.Flush(); + + SendCommitment(pendingMessages, peerman); +} + +void CDKGSession::SendCommitment(CDKGPendingMessages& pendingMessages, PeerManager& peerman) +{ + CDKGLogger logger(*this, __func__, __LINE__); + + assert(AreWeMember()); + + logger.Batch("sending commitment"); + + CDKGPrematureCommitment qc(params); + qc.llmqType = params.type; + qc.quorumHash = m_quorum_base_block_index->GetBlockHash(); + qc.proTxHash = myProTxHash; + + for (const auto i : irange::range(members.size())) { + const auto& m = members[i]; + if (!m->bad) { + qc.validMembers[i] = true; + } + } + + if (qc.CountValidMembers() < params.minSize) { + logger.Batch("not enough valid members. not sending commitment"); + return; + } + + if (ShouldSimulateError(DKGError::type::COMMIT_OMIT)) { + logger.Batch("omitting"); + return; + } + + cxxtimer::Timer timerTotal(true); + + cxxtimer::Timer t1(true); + std::vector memberIndexes; + std::vector vvecs; + std::vector skContributions; + if (!dkgManager.GetVerifiedContributions(params.type, m_quorum_base_block_index, qc.validMembers, memberIndexes, vvecs, skContributions)) { + logger.Batch("failed to get valid contributions"); + return; + } + + BLSVerificationVectorPtr vvec = cache.BuildQuorumVerificationVector(::SerializeHash(memberIndexes), vvecs); + if (vvec == nullptr) { + logger.Batch("failed to build quorum verification vector"); + return; + } + t1.stop(); + + cxxtimer::Timer t2(true); + CBLSSecretKey skShare = cache.AggregateSecretKeys(::SerializeHash(memberIndexes), skContributions); + if (!skShare.IsValid()) { + logger.Batch("failed to build own secret share"); + return; + } + t2.stop(); + + logger.Batch("pubKeyShare=%s", skShare.GetPublicKey().ToString()); + + cxxtimer::Timer t3(true); + qc.quorumPublicKey = (*vvec)[0]; + qc.quorumVvecHash = ::SerializeHash(*vvec); + + int lieType = -1; + if (ShouldSimulateError(DKGError::type::COMMIT_LIE)) { + lieType = GetRand(/*nMax=*/5); + logger.Batch("lying on commitment. lieType=%d", lieType); + } + + if (lieType == 0) { + CBLSSecretKey k; + k.MakeNewKey(); + qc.quorumPublicKey = k.GetPublicKey(); + } else if (lieType == 1) { + (*qc.quorumVvecHash.begin())++; + } + + uint256 commitmentHash = BuildCommitmentHash(qc.llmqType, qc.quorumHash, qc.validMembers, qc.quorumPublicKey, qc.quorumVvecHash); + + if (lieType == 2) { + (*commitmentHash.begin())++; + } + + qc.sig = m_mn_activeman->Sign(commitmentHash, m_use_legacy_bls); + qc.quorumSig = skShare.Sign(commitmentHash, m_use_legacy_bls); + + if (lieType == 3) { + auto buf = qc.sig.ToBytes(m_use_legacy_bls); + buf[5]++; + qc.sig.SetBytes(buf, m_use_legacy_bls); + } else if (lieType == 4) { + auto buf = qc.quorumSig.ToBytes(m_use_legacy_bls); + buf[5]++; + qc.quorumSig.SetBytes(buf, m_use_legacy_bls); + } + + t3.stop(); + timerTotal.stop(); + + logger.Batch("built premature commitment. time1=%d, time2=%d, time3=%d, totalTime=%d", + t1.count(), t2.count(), t3.count(), timerTotal.count()); + + + logger.Flush(); + + dkgDebugManager.UpdateLocalSessionStatus(params.type, quorumIndex, [&](CDKGDebugSessionStatus& status) { + status.statusBits.sentPrematureCommitment = true; + return true; + }); + + pendingMessages.PushPendingMessage(-1, qc, peerman); +} + +std::vector CDKGSession::FinalizeCommitments() +{ + if (!AreWeMember()) { + return {}; + } + + CDKGLogger logger(*this, __func__, __LINE__); + + using Key = std::vector; + std::map> commitmentsMap; + + { + LOCK(invCs); + + for (const auto& p : prematureCommitments) { + const auto& qc = p.second; + if (validCommitments.count(p.first) == 0) { + continue; + } + + // should have been verified before + assert(qc.CountValidMembers() >= params.minSize); + + auto it = commitmentsMap.find(qc.validMembers); + if (it == commitmentsMap.end()) { + it = commitmentsMap.emplace(qc.validMembers, std::vector()).first; + } + + it->second.emplace_back(qc); + } + } + + std::vector finalCommitments; + for (const auto& p : commitmentsMap) { + const auto& cvec = p.second; + if (cvec.size() < size_t(params.minSize)) { + // commitment was signed by a minority + continue; + } + + std::vector signerIds; + std::vector thresholdSigs; + + const auto& first = cvec[0]; + + CFinalCommitment fqc(params, first.quorumHash); + fqc.validMembers = first.validMembers; + fqc.quorumPublicKey = first.quorumPublicKey; + fqc.quorumVvecHash = first.quorumVvecHash; + + const bool isQuorumRotationEnabled{IsQuorumRotationEnabled(params, m_quorum_base_block_index)}; + // TODO: always put `true` here: so far as v19 is activated, we always write BASIC now + fqc.nVersion = CFinalCommitment::GetVersion(isQuorumRotationEnabled, DeploymentActiveAfter(m_quorum_base_block_index, m_chainman.GetConsensus(), Consensus::DEPLOYMENT_V19)); + fqc.quorumIndex = isQuorumRotationEnabled ? quorumIndex : 0; + + uint256 commitmentHash = BuildCommitmentHash(fqc.llmqType, fqc.quorumHash, fqc.validMembers, fqc.quorumPublicKey, fqc.quorumVvecHash); + + std::vector aggSigs; + std::vector aggPks; + aggSigs.reserve(cvec.size()); + aggPks.reserve(cvec.size()); + + for (const auto& qc : cvec) { + if (qc.quorumPublicKey != first.quorumPublicKey || qc.quorumVvecHash != first.quorumVvecHash) { + logger.Batch("quorumPublicKey or quorumVvecHash does not match, skipping"); + continue; + } + + size_t signerIndex = membersMap[qc.proTxHash]; + const auto& m = members[signerIndex]; + + fqc.signers[signerIndex] = true; + aggSigs.emplace_back(qc.sig); + aggPks.emplace_back(m->dmn->pdmnState->pubKeyOperator.Get()); + + signerIds.emplace_back(m->id); + thresholdSigs.emplace_back(qc.quorumSig); + } + + cxxtimer::Timer t1(true); + fqc.membersSig = CBLSSignature::AggregateSecure(aggSigs, aggPks, commitmentHash); + t1.stop(); + + cxxtimer::Timer t2(true); + if (!fqc.quorumSig.Recover(thresholdSigs, signerIds)) { + logger.Batch("failed to recover quorum sig"); + continue; + } + t2.stop(); + + cxxtimer::Timer t3(true); + if (!fqc.Verify({m_dmnman, m_qsnapman, m_chainman, m_quorum_base_block_index}, true)) { + logger.Batch("failed to verify final commitment"); + continue; + } + t3.stop(); + + finalCommitments.emplace_back(fqc); + + logger.Batch("final commitment: validMembers=%d, signers=%d, quorumPublicKey=%s, time1=%d, time2=%d, time3=%d", + fqc.CountValidMembers(), fqc.CountSigners(), fqc.quorumPublicKey.ToString(), + t1.count(), t2.count(), t3.count()); + } + + logger.Flush(); + + return finalCommitments; +} + +CFinalCommitment CDKGSession::FinalizeSingleCommitment() +{ + if (!AreWeMember()) { + return {}; + } + + CDKGLogger logger(*this, __func__, __LINE__); + + std::vector signerIds; + std::vector thresholdSigs; + + CFinalCommitment fqc(params, m_quorum_base_block_index->GetBlockHash()); + + + fqc.signers = {true}; + fqc.validMembers = {true}; + + CBLSSecretKey sk1; + sk1.MakeNewKey(); + + fqc.quorumPublicKey = sk1.GetPublicKey(); + fqc.quorumVvecHash = {}; + + // use just MN's operator public key as quorum pubkey. + // TODO: use sk1 here instead and use recovery mechanism from shares, but that's not trivial to do + const bool workaround_qpublic_key = true; + if (workaround_qpublic_key) { + fqc.quorumPublicKey = m_mn_activeman->GetPubKey(); + } + const bool isQuorumRotationEnabled{false}; + fqc.nVersion = CFinalCommitment::GetVersion(isQuorumRotationEnabled, + DeploymentActiveAfter(m_quorum_base_block_index, m_chainman.GetConsensus(), + Consensus::DEPLOYMENT_V19)); + fqc.quorumIndex = 0; + + uint256 commitmentHash = BuildCommitmentHash(fqc.llmqType, fqc.quorumHash, fqc.validMembers, fqc.quorumPublicKey, + fqc.quorumVvecHash); + fqc.quorumSig = sk1.Sign(commitmentHash, m_use_legacy_bls); + + fqc.membersSig = m_mn_activeman->Sign(commitmentHash, m_use_legacy_bls); + + if (workaround_qpublic_key) { + fqc.quorumSig = fqc.membersSig; + } + + if (!fqc.Verify({m_dmnman, m_qsnapman, m_chainman, m_quorum_base_block_index}, true)) { + logger.Batch("failed to verify final commitment"); + assert(false); + } + + logger.Batch("final commitment: validMembers=%d, signers=%d, quorumPublicKey=%s", fqc.CountValidMembers(), + fqc.CountSigners(), fqc.quorumPublicKey.ToString()); + + logger.Flush(); + + return fqc; +} +} // namespace llmq diff --git a/src/llmq/dkgsession.cpp b/src/llmq/dkgsession.cpp index 77725529fd8d..d7f770393369 100644 --- a/src/llmq/dkgsession.cpp +++ b/src/llmq/dkgsession.cpp @@ -27,9 +27,7 @@ #include #include -namespace llmq -{ - +namespace llmq { CDKGLogger::CDKGLogger(const CDKGSession& _quorumDkg, std::string_view _func, int source_line) : CBatchedLogger(BCLog::LLMQ_DKG, BCLog::Level::Debug, strprintf("QuorumDKG(type=%s, qIndex=%d, h=%d, member=%d)", _quorumDkg.params.name, _quorumDkg.quorumIndex, @@ -66,7 +64,6 @@ CDKGMember::CDKGMember(const CDeterministicMNCPtr& _dmn, size_t _idx) : idx(_idx), id(_dmn->proTxHash) { - } CDKGSession::CDKGSession(const CBlockIndex* pQuorumBaseBlockIndex, const Consensus::LLMQParams& _params, @@ -157,81 +154,6 @@ bool CDKGSession::Init(const uint256& _myProTxHash, int _quorumIndex) return true; } -void CDKGSession::Contribute(CDKGPendingMessages& pendingMessages, PeerManager& peerman) -{ - CDKGLogger logger(*this, __func__, __LINE__); - - if (!AreWeMember()) { - return; - } - - assert(params.threshold > 1); // we should not get there with single-node-quorums - - cxxtimer::Timer t1(true); - logger.Batch("generating contributions"); - if (!blsWorker.GenerateContributions(params.threshold, memberIds, vvecContribution, m_sk_contributions)) { - // this should never happen actually - logger.Batch("GenerateContributions failed"); - return; - } - logger.Batch("generated contributions. time=%d", t1.count()); - logger.Flush(); - - SendContributions(pendingMessages, peerman); -} - -void CDKGSession::SendContributions(CDKGPendingMessages& pendingMessages, PeerManager& peerman) -{ - CDKGLogger logger(*this, __func__, __LINE__); - - assert(AreWeMember()); - - logger.Batch("sending contributions"); - - if (ShouldSimulateError(DKGError::type::CONTRIBUTION_OMIT)) { - logger.Batch("omitting"); - return; - } - - CDKGContribution qc; - qc.llmqType = params.type; - qc.quorumHash = m_quorum_base_block_index->GetBlockHash(); - qc.proTxHash = myProTxHash; - qc.vvec = vvecContribution; - - cxxtimer::Timer t1(true); - qc.contributions = std::make_shared>(); - qc.contributions->InitEncrypt(members.size()); - - for (const auto i : irange::range(members.size())) { - const auto& m = members[i]; - CBLSSecretKey skContrib = m_sk_contributions[i]; - - if (i != myIdx && ShouldSimulateError(DKGError::type::CONTRIBUTION_LIE)) { - logger.Batch("lying for %s", m->dmn->proTxHash.ToString()); - skContrib.MakeNewKey(); - } - - if (!qc.contributions->Encrypt(i, m->dmn->pdmnState->pubKeyOperator.Get(), skContrib, PROTOCOL_VERSION)) { - logger.Batch("failed to encrypt contribution for %s", m->dmn->proTxHash.ToString()); - return; - } - } - - logger.Batch("encrypted contributions. time=%d", t1.count()); - - qc.sig = m_mn_activeman->Sign(qc.GetSignHash(), m_use_legacy_bls); - - logger.Flush(); - - dkgDebugManager.UpdateLocalSessionStatus(params.type, quorumIndex, [&](CDKGDebugSessionStatus& status) { - status.statusBits.sentContributions = true; - return true; - }); - - pendingMessages.PushPendingMessage(-1, qc, peerman); -} - // only performs cheap verifications, but not the signature of the message. this is checked with batched verification bool CDKGSession::PreVerifyMessage(const CDKGContribution& qc, bool& retBan) const { @@ -362,192 +284,6 @@ std::optional CDKGSession::ReceiveMessage(const CDKGContribution& qc) return inv; } -// Verifies all pending secret key contributions in one batch -// This is done by aggregating the verification vectors belonging to the secret key contributions -// The resulting aggregated vvec is then used to recover a public key share -// The public key share must match the public key belonging to the aggregated secret key contributions -// See CBLSWorker::VerifyContributionShares for more details. -void CDKGSession::VerifyPendingContributions() -{ - AssertLockHeld(cs_pending); - - CDKGLogger logger(*this, __func__, __LINE__); - - cxxtimer::Timer t1(true); - - if (pendingContributionVerifications.empty()) { - return; - } - - std::vector memberIndexes; - std::vector vvecs; - std::vector skContributions; - - for (const auto& idx : pendingContributionVerifications) { - const auto& m = members[idx]; - if (m->bad || m->weComplain) { - continue; - } - memberIndexes.emplace_back(idx); - vvecs.emplace_back(receivedVvecs[idx]); - skContributions.emplace_back(receivedSkContributions[idx]); - // Write here to definitely store one contribution for each member no matter if - // our share is valid or not, could be that others are still correct - dkgManager.WriteEncryptedContributions(params.type, m_quorum_base_block_index, m->dmn->proTxHash, *vecEncryptedContributions[idx]); - } - - auto result = blsWorker.VerifyContributionShares(myId, vvecs, skContributions); - if (result.size() != memberIndexes.size()) { - logger.Batch("VerifyContributionShares returned result of size %d but size %d was expected, something is wrong", result.size(), memberIndexes.size()); - return; - } - - for (const auto i : irange::range(memberIndexes.size())) { - if (!result[i]) { - const auto& m = members[memberIndexes[i]]; - logger.Batch("invalid contribution from %s. will complain later", m->dmn->proTxHash.ToString()); - m->weComplain = true; - dkgDebugManager.UpdateLocalMemberStatus(params.type, quorumIndex, m->idx, [&](CDKGDebugMemberStatus& status) { - status.statusBits.weComplain = true; - return true; - }); - } else { - size_t memberIdx = memberIndexes[i]; - dkgManager.WriteVerifiedSkContribution(params.type, m_quorum_base_block_index, members[memberIdx]->dmn->proTxHash, skContributions[i]); - } - } - - logger.Batch("verified %d pending contributions. time=%d", pendingContributionVerifications.size(), t1.count()); - pendingContributionVerifications.clear(); -} - -void CDKGSession::VerifyAndComplain(CConnman& connman, CDKGPendingMessages& pendingMessages, PeerManager& peerman) -{ - if (!AreWeMember()) { - return; - } - - { - LOCK(cs_pending); - VerifyPendingContributions(); - } - - CDKGLogger logger(*this, __func__, __LINE__); - - // we check all members if they sent us their contributions - // we consider members as bad if they missed to send anything or if they sent multiple - // in both cases we won't give them a second chance as they might be either down, buggy or an adversary - // we assume that such a participant will be marked as bad by the whole network in most cases, - // as propagation will ensure that all nodes see the same vvecs/contributions. In case nodes come to - // different conclusions, the aggregation phase will handle this (most voted quorum key wins). - - cxxtimer::Timer t1(true); - - for (const auto& m : members) { - if (m->bad) { - continue; - } - if (m->contributions.empty()) { - logger.Batch("%s did not send any contribution", m->dmn->proTxHash.ToString()); - MarkBadMember(m->idx); - continue; - } - } - - logger.Batch("verified contributions. time=%d", t1.count()); - logger.Flush(); - - VerifyConnectionAndMinProtoVersions(connman); - - SendComplaint(pendingMessages, peerman); -} - -void CDKGSession::VerifyConnectionAndMinProtoVersions(CConnman& connman) const -{ - assert(m_mn_metaman.IsValid()); - - if (!IsQuorumPoseEnabled(params.type, m_sporkman)) { - return; - } - - CDKGLogger logger(*this, __func__, __LINE__); - - Uint256HashMap protoMap; - connman.ForEachNode([&](const CNode* pnode) { - auto verifiedProRegTxHash = pnode->GetVerifiedProRegTxHash(); - if (verifiedProRegTxHash.IsNull()) { - return; - } - protoMap.emplace(verifiedProRegTxHash, pnode->nVersion); - }); - - bool fShouldAllMembersBeConnected = IsAllMembersConnectedEnabled(params.type, m_sporkman); - for (const auto& m : members) { - if (m->dmn->proTxHash == myProTxHash) { - continue; - } - if (auto it = protoMap.find(m->dmn->proTxHash); it == protoMap.end()) { - m->badConnection = fShouldAllMembersBeConnected; - if (m->badConnection) { - logger.Batch("%s is not connected to us, badConnection=1", m->dmn->proTxHash.ToString()); - } - } else if (it->second < MIN_MASTERNODE_PROTO_VERSION) { - m->badConnection = true; - logger.Batch("%s does not have min proto version %d (has %d)", m->dmn->proTxHash.ToString(), MIN_MASTERNODE_PROTO_VERSION, it->second); - } - if (m_mn_metaman.OutboundFailedTooManyTimes(m->dmn->proTxHash)) { - m->badConnection = true; - logger.Batch("%s failed to connect to it too many times", m->dmn->proTxHash.ToString()); - } - if (m_mn_metaman.IsPlatformBanned(m->dmn->proTxHash)) { - m->badConnection = true; - logger.Batch("%s is Platform PoSe banned", m->dmn->proTxHash.ToString()); - } - } -} - -void CDKGSession::SendComplaint(CDKGPendingMessages& pendingMessages, PeerManager& peerman) -{ - CDKGLogger logger(*this, __func__, __LINE__); - - assert(AreWeMember()); - - CDKGComplaint qc(params); - qc.llmqType = params.type; - qc.quorumHash = m_quorum_base_block_index->GetBlockHash(); - qc.proTxHash = myProTxHash; - - int badCount = 0; - int complaintCount = 0; - for (const auto i : irange::range(members.size())) { - const auto& m = members[i]; - if (m->bad || m->badConnection) { - qc.badMembers[i] = true; - badCount++; - } else if (m->weComplain) { - qc.complainForMembers[i] = true; - complaintCount++; - } - } - - if (badCount == 0 && complaintCount == 0) { - return; - } - - logger.Batch("sending complaint. badCount=%d, complaintCount=%d", badCount, complaintCount); - - qc.sig = m_mn_activeman->Sign(qc.GetSignHash(), m_use_legacy_bls); - - logger.Flush(); - - dkgDebugManager.UpdateLocalSessionStatus(params.type, quorumIndex, [&](CDKGDebugSessionStatus& status) { - status.statusBits.sentComplaint = true; - return true; - }); - - pendingMessages.PushPendingMessage(-1, qc, peerman); -} - // only performs cheap verifications, but not the signature of the message. this is checked with batched verification bool CDKGSession::PreVerifyMessage(const CDKGComplaint& qc, bool& retBan) const { @@ -652,96 +388,6 @@ std::optional CDKGSession::ReceiveMessage(const CDKGComplaint& qc) return inv; } -void CDKGSession::VerifyAndJustify(CDKGPendingMessages& pendingMessages, PeerManager& peerman) -{ - if (!AreWeMember()) { - return; - } - - CDKGLogger logger(*this, __func__, __LINE__); - - std::set justifyFor; - - for (const auto& m : members) { - if (m->bad) { - continue; - } - if (m->badMemberVotes.size() >= size_t(params.dkgBadVotesThreshold)) { - logger.Batch("%s marked as bad as %d other members voted for this", m->dmn->proTxHash.ToString(), m->badMemberVotes.size()); - MarkBadMember(m->idx); - continue; - } - if (m->complaints.empty()) { - continue; - } - if (m->complaints.size() != 1) { - logger.Batch("%s sent multiple complaints", m->dmn->proTxHash.ToString()); - MarkBadMember(m->idx); - continue; - } - - LOCK(invCs); - if (const auto& qc = complaints.at(*m->complaints.begin()); - qc.complainForMembers[*myIdx]) { - justifyFor.emplace(qc.proTxHash); - } - } - - logger.Flush(); - if (!justifyFor.empty()) { - SendJustification(pendingMessages, peerman, justifyFor); - } -} - -void CDKGSession::SendJustification(CDKGPendingMessages& pendingMessages, PeerManager& peerman, - const std::set& forMembers) -{ - CDKGLogger logger(*this, __func__, __LINE__); - - assert(AreWeMember()); - - logger.Batch("sending justification for %d members", forMembers.size()); - - CDKGJustification qj; - qj.llmqType = params.type; - qj.quorumHash = m_quorum_base_block_index->GetBlockHash(); - qj.proTxHash = myProTxHash; - qj.contributions.reserve(forMembers.size()); - - for (const uint32_t i : irange::range(members.size())) { - const auto& m = members[i]; - if (forMembers.count(m->dmn->proTxHash) == 0) { - continue; - } - logger.Batch("justifying for %s", m->dmn->proTxHash.ToString()); - - CBLSSecretKey skContribution = m_sk_contributions[i]; - - if (i != myIdx && ShouldSimulateError(DKGError::type::JUSTIFY_LIE)) { - logger.Batch("lying for %s", m->dmn->proTxHash.ToString()); - skContribution.MakeNewKey(); - } - - qj.contributions.emplace_back(CDKGJustification::Contribution{i, skContribution}); - } - - if (ShouldSimulateError(DKGError::type::JUSTIFY_OMIT)) { - logger.Batch("omitting"); - return; - } - - qj.sig = m_mn_activeman->Sign(qj.GetSignHash(), m_use_legacy_bls); - - logger.Flush(); - - dkgDebugManager.UpdateLocalSessionStatus(params.type, quorumIndex, [&](CDKGDebugSessionStatus& status) { - status.statusBits.sentJustification = true; - return true; - }); - - pendingMessages.PushPendingMessage(-1, qj, peerman); -} - // only performs cheap verifications, but not the signature of the message. this is checked with batched verification bool CDKGSession::PreVerifyMessage(const CDKGJustification& qj, bool& retBan) const { @@ -893,163 +539,6 @@ std::optional CDKGSession::ReceiveMessage(const CDKGJustification& qj) return inv; } -void CDKGSession::VerifyAndCommit(CDKGPendingMessages& pendingMessages, PeerManager& peerman) -{ - if (!AreWeMember()) { - return; - } - - CDKGLogger logger(*this, __func__, __LINE__); - - std::vector badMembers; - badMembers.reserve(members.size()); - std::vector openComplaintMembers; - openComplaintMembers.reserve(members.size()); - - for (const auto& m : members) { - if (m->bad) { - badMembers.emplace_back(m->idx); - continue; - } - if (!m->complaintsFromOthers.empty()) { - MarkBadMember(m->idx); - openComplaintMembers.emplace_back(m->idx); - } - } - - if (!badMembers.empty() || !openComplaintMembers.empty()) { - logger.Batch("verification result:"); - } - if (!badMembers.empty()) { - logger.Batch(" members previously determined as bad:"); - for (const auto& idx : badMembers) { - logger.Batch(" %s", members[idx]->dmn->proTxHash.ToString()); - } - } - if (!openComplaintMembers.empty()) { - logger.Batch(" members with open complaints and now marked as bad:"); - for (const auto& idx : openComplaintMembers) { - logger.Batch(" %s", members[idx]->dmn->proTxHash.ToString()); - } - } - - logger.Flush(); - - SendCommitment(pendingMessages, peerman); -} - -void CDKGSession::SendCommitment(CDKGPendingMessages& pendingMessages, PeerManager& peerman) -{ - CDKGLogger logger(*this, __func__, __LINE__); - - assert(AreWeMember()); - - logger.Batch("sending commitment"); - - CDKGPrematureCommitment qc(params); - qc.llmqType = params.type; - qc.quorumHash = m_quorum_base_block_index->GetBlockHash(); - qc.proTxHash = myProTxHash; - - for (const auto i : irange::range(members.size())) { - const auto& m = members[i]; - if (!m->bad) { - qc.validMembers[i] = true; - } - } - - if (qc.CountValidMembers() < params.minSize) { - logger.Batch("not enough valid members. not sending commitment"); - return; - } - - if (ShouldSimulateError(DKGError::type::COMMIT_OMIT)) { - logger.Batch("omitting"); - return; - } - - cxxtimer::Timer timerTotal(true); - - cxxtimer::Timer t1(true); - std::vector memberIndexes; - std::vector vvecs; - std::vector skContributions; - if (!dkgManager.GetVerifiedContributions(params.type, m_quorum_base_block_index, qc.validMembers, memberIndexes, vvecs, skContributions)) { - logger.Batch("failed to get valid contributions"); - return; - } - - BLSVerificationVectorPtr vvec = cache.BuildQuorumVerificationVector(::SerializeHash(memberIndexes), vvecs); - if (vvec == nullptr) { - logger.Batch("failed to build quorum verification vector"); - return; - } - t1.stop(); - - cxxtimer::Timer t2(true); - CBLSSecretKey skShare = cache.AggregateSecretKeys(::SerializeHash(memberIndexes), skContributions); - if (!skShare.IsValid()) { - logger.Batch("failed to build own secret share"); - return; - } - t2.stop(); - - logger.Batch("pubKeyShare=%s", skShare.GetPublicKey().ToString()); - - cxxtimer::Timer t3(true); - qc.quorumPublicKey = (*vvec)[0]; - qc.quorumVvecHash = ::SerializeHash(*vvec); - - int lieType = -1; - if (ShouldSimulateError(DKGError::type::COMMIT_LIE)) { - lieType = GetRand(/*nMax=*/5); - logger.Batch("lying on commitment. lieType=%d", lieType); - } - - if (lieType == 0) { - CBLSSecretKey k; - k.MakeNewKey(); - qc.quorumPublicKey = k.GetPublicKey(); - } else if (lieType == 1) { - (*qc.quorumVvecHash.begin())++; - } - - uint256 commitmentHash = BuildCommitmentHash(qc.llmqType, qc.quorumHash, qc.validMembers, qc.quorumPublicKey, qc.quorumVvecHash); - - if (lieType == 2) { - (*commitmentHash.begin())++; - } - - qc.sig = m_mn_activeman->Sign(commitmentHash, m_use_legacy_bls); - qc.quorumSig = skShare.Sign(commitmentHash, m_use_legacy_bls); - - if (lieType == 3) { - auto buf = qc.sig.ToBytes(m_use_legacy_bls); - buf[5]++; - qc.sig.SetBytes(buf, m_use_legacy_bls); - } else if (lieType == 4) { - auto buf = qc.quorumSig.ToBytes(m_use_legacy_bls); - buf[5]++; - qc.quorumSig.SetBytes(buf, m_use_legacy_bls); - } - - t3.stop(); - timerTotal.stop(); - - logger.Batch("built premature commitment. time1=%d, time2=%d, time3=%d, totalTime=%d", - t1.count(), t2.count(), t3.count(), timerTotal.count()); - - - logger.Flush(); - - dkgDebugManager.UpdateLocalSessionStatus(params.type, quorumIndex, [&](CDKGDebugSessionStatus& status) { - status.statusBits.sentPrematureCommitment = true; - return true; - }); - - pendingMessages.PushPendingMessage(-1, qc, peerman); -} - // only performs cheap verifications, but not the signature of the message. this is checked with batched verification bool CDKGSession::PreVerifyMessage(const CDKGPrematureCommitment& qc, bool& retBan) const { @@ -1189,173 +678,6 @@ std::optional CDKGSession::ReceiveMessage(const CDKGPrematureCommitment& q return inv; } -std::vector CDKGSession::FinalizeCommitments() -{ - if (!AreWeMember()) { - return {}; - } - - CDKGLogger logger(*this, __func__, __LINE__); - - using Key = std::vector; - std::map> commitmentsMap; - - { - LOCK(invCs); - - for (const auto& p : prematureCommitments) { - const auto& qc = p.second; - if (validCommitments.count(p.first) == 0) { - continue; - } - - // should have been verified before - assert(qc.CountValidMembers() >= params.minSize); - - auto it = commitmentsMap.find(qc.validMembers); - if (it == commitmentsMap.end()) { - it = commitmentsMap.emplace(qc.validMembers, std::vector()).first; - } - - it->second.emplace_back(qc); - } - } - - std::vector finalCommitments; - for (const auto& p : commitmentsMap) { - const auto& cvec = p.second; - if (cvec.size() < size_t(params.minSize)) { - // commitment was signed by a minority - continue; - } - - std::vector signerIds; - std::vector thresholdSigs; - - const auto& first = cvec[0]; - - CFinalCommitment fqc(params, first.quorumHash); - fqc.validMembers = first.validMembers; - fqc.quorumPublicKey = first.quorumPublicKey; - fqc.quorumVvecHash = first.quorumVvecHash; - - const bool isQuorumRotationEnabled{IsQuorumRotationEnabled(params, m_quorum_base_block_index)}; - // TODO: always put `true` here: so far as v19 is activated, we always write BASIC now - fqc.nVersion = CFinalCommitment::GetVersion(isQuorumRotationEnabled, DeploymentActiveAfter(m_quorum_base_block_index, m_chainman.GetConsensus(), Consensus::DEPLOYMENT_V19)); - fqc.quorumIndex = isQuorumRotationEnabled ? quorumIndex : 0; - - uint256 commitmentHash = BuildCommitmentHash(fqc.llmqType, fqc.quorumHash, fqc.validMembers, fqc.quorumPublicKey, fqc.quorumVvecHash); - - std::vector aggSigs; - std::vector aggPks; - aggSigs.reserve(cvec.size()); - aggPks.reserve(cvec.size()); - - for (const auto& qc : cvec) { - if (qc.quorumPublicKey != first.quorumPublicKey || qc.quorumVvecHash != first.quorumVvecHash) { - logger.Batch("quorumPublicKey or quorumVvecHash does not match, skipping"); - continue; - } - - size_t signerIndex = membersMap[qc.proTxHash]; - const auto& m = members[signerIndex]; - - fqc.signers[signerIndex] = true; - aggSigs.emplace_back(qc.sig); - aggPks.emplace_back(m->dmn->pdmnState->pubKeyOperator.Get()); - - signerIds.emplace_back(m->id); - thresholdSigs.emplace_back(qc.quorumSig); - } - - cxxtimer::Timer t1(true); - fqc.membersSig = CBLSSignature::AggregateSecure(aggSigs, aggPks, commitmentHash); - t1.stop(); - - cxxtimer::Timer t2(true); - if (!fqc.quorumSig.Recover(thresholdSigs, signerIds)) { - logger.Batch("failed to recover quorum sig"); - continue; - } - t2.stop(); - - cxxtimer::Timer t3(true); - if (!fqc.Verify({m_dmnman, m_qsnapman, m_chainman, m_quorum_base_block_index}, true)) { - logger.Batch("failed to verify final commitment"); - continue; - } - t3.stop(); - - finalCommitments.emplace_back(fqc); - - logger.Batch("final commitment: validMembers=%d, signers=%d, quorumPublicKey=%s, time1=%d, time2=%d, time3=%d", - fqc.CountValidMembers(), fqc.CountSigners(), fqc.quorumPublicKey.ToString(), - t1.count(), t2.count(), t3.count()); - } - - logger.Flush(); - - return finalCommitments; -} - -CFinalCommitment CDKGSession::FinalizeSingleCommitment() -{ - if (!AreWeMember()) { - return {}; - } - - CDKGLogger logger(*this, __func__, __LINE__); - - std::vector signerIds; - std::vector thresholdSigs; - - CFinalCommitment fqc(params, m_quorum_base_block_index->GetBlockHash()); - - - fqc.signers = {true}; - fqc.validMembers = {true}; - - CBLSSecretKey sk1; - sk1.MakeNewKey(); - - fqc.quorumPublicKey = sk1.GetPublicKey(); - fqc.quorumVvecHash = {}; - - // use just MN's operator public key as quorum pubkey. - // TODO: use sk1 here instead and use recovery mechanism from shares, but that's not trivial to do - const bool workaround_qpublic_key = true; - if (workaround_qpublic_key) { - fqc.quorumPublicKey = m_mn_activeman->GetPubKey(); - } - const bool isQuorumRotationEnabled{false}; - fqc.nVersion = CFinalCommitment::GetVersion(isQuorumRotationEnabled, - DeploymentActiveAfter(m_quorum_base_block_index, m_chainman.GetConsensus(), - Consensus::DEPLOYMENT_V19)); - fqc.quorumIndex = 0; - - uint256 commitmentHash = BuildCommitmentHash(fqc.llmqType, fqc.quorumHash, fqc.validMembers, fqc.quorumPublicKey, - fqc.quorumVvecHash); - fqc.quorumSig = sk1.Sign(commitmentHash, m_use_legacy_bls); - - fqc.membersSig = m_mn_activeman->Sign(commitmentHash, m_use_legacy_bls); - - if (workaround_qpublic_key) { - fqc.quorumSig = fqc.membersSig; - } - - if (!fqc.Verify({m_dmnman, m_qsnapman, m_chainman, m_quorum_base_block_index}, true)) { - logger.Batch("failed to verify final commitment"); - assert(false); - } - - logger.Batch("final commitment: validMembers=%d, signers=%d, quorumPublicKey=%s", fqc.CountValidMembers(), - fqc.CountSigners(), fqc.quorumPublicKey.ToString()); - - logger.Flush(); - - return fqc; -} - CDKGMember* CDKGSession::GetMember(const uint256& proTxHash) const { auto it = membersMap.find(proTxHash); @@ -1377,6 +699,4 @@ void CDKGSession::MarkBadMember(size_t idx) }); member->bad = true; } - - } // namespace llmq From 01c0b9458a77dc88f08577337eb24816845343f1 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Sat, 13 Dec 2025 23:30:33 +0530 Subject: [PATCH 06/14] refactor: separate active session routines into dedicated class --- src/Makefile.am | 1 + src/active/dkgsession.cpp | 61 ++++++++++++++++++++---------- src/active/dkgsession.h | 65 ++++++++++++++++++++++++++++++++ src/active/dkgsessionhandler.cpp | 16 +++++--- src/llmq/dkgsession.cpp | 23 +++++------ src/llmq/dkgsession.h | 64 +++++++++++++++---------------- src/llmq/dkgsessionhandler.cpp | 12 ++---- src/llmq/dkgsessionhandler.h | 12 ++---- src/llmq/observer/context.cpp | 5 +-- 9 files changed, 170 insertions(+), 89 deletions(-) create mode 100644 src/active/dkgsession.h diff --git a/src/Makefile.am b/src/Makefile.am index 2706a31ddf90..a3d68339feea 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -151,6 +151,7 @@ endif .PHONY: FORCE check-symbols check-security # dash core # BITCOIN_CORE_H = \ + active/dkgsession.h \ active/dkgsessionhandler.h \ active/quorums.h \ addrdb.h \ diff --git a/src/active/dkgsession.cpp b/src/active/dkgsession.cpp index cb7e5986b762..c302c7ca26b1 100644 --- a/src/active/dkgsession.cpp +++ b/src/active/dkgsession.cpp @@ -2,7 +2,7 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include +#include #include #include @@ -20,7 +20,22 @@ #include namespace llmq { -void CDKGSession::Contribute(CDKGPendingMessages& pendingMessages, PeerManager& peerman) +ActiveDKGSession::ActiveDKGSession(CBLSWorker& bls_worker, CDeterministicMNManager& dmnman, CDKGDebugManager& dkgdbgman, + CDKGSessionManager& qdkgsman, CMasternodeMetaMan& mn_metaman, + CQuorumSnapshotManager& qsnapman, const CActiveMasternodeManager& mn_activeman, + const ChainstateManager& chainman, const CSporkManager& sporkman, + const CBlockIndex* base_block_index, const Consensus::LLMQParams& params) : + CDKGSession(bls_worker, dmnman, dkgdbgman, qdkgsman, qsnapman, chainman, base_block_index, params), + m_mn_metaman{mn_metaman}, + m_mn_activeman{mn_activeman}, + m_sporkman{sporkman}, + m_use_legacy_bls{!DeploymentActiveAfter(m_quorum_base_block_index, Params().GetConsensus(), Consensus::DEPLOYMENT_V19)} +{ +} + +ActiveDKGSession::~ActiveDKGSession() = default; + +void ActiveDKGSession::Contribute(CDKGPendingMessages& pendingMessages, PeerManager& peerman) { CDKGLogger logger(*this, __func__, __LINE__); @@ -43,7 +58,7 @@ void CDKGSession::Contribute(CDKGPendingMessages& pendingMessages, PeerManager& SendContributions(pendingMessages, peerman); } -void CDKGSession::SendContributions(CDKGPendingMessages& pendingMessages, PeerManager& peerman) +void ActiveDKGSession::SendContributions(CDKGPendingMessages& pendingMessages, PeerManager& peerman) { CDKGLogger logger(*this, __func__, __LINE__); @@ -83,7 +98,7 @@ void CDKGSession::SendContributions(CDKGPendingMessages& pendingMessages, PeerMa logger.Batch("encrypted contributions. time=%d", t1.count()); - qc.sig = m_mn_activeman->Sign(qc.GetSignHash(), m_use_legacy_bls); + qc.sig = m_mn_activeman.Sign(qc.GetSignHash(), m_use_legacy_bls); logger.Flush(); @@ -100,7 +115,7 @@ void CDKGSession::SendContributions(CDKGPendingMessages& pendingMessages, PeerMa // The resulting aggregated vvec is then used to recover a public key share // The public key share must match the public key belonging to the aggregated secret key contributions // See CBLSWorker::VerifyContributionShares for more details. -void CDKGSession::VerifyPendingContributions() +void ActiveDKGSession::VerifyPendingContributions() { AssertLockHeld(cs_pending); @@ -154,7 +169,7 @@ void CDKGSession::VerifyPendingContributions() pendingContributionVerifications.clear(); } -void CDKGSession::VerifyAndComplain(CConnman& connman, CDKGPendingMessages& pendingMessages, PeerManager& peerman) +void ActiveDKGSession::VerifyAndComplain(CConnman& connman, CDKGPendingMessages& pendingMessages, PeerManager& peerman) { if (!AreWeMember()) { return; @@ -195,7 +210,7 @@ void CDKGSession::VerifyAndComplain(CConnman& connman, CDKGPendingMessages& pend SendComplaint(pendingMessages, peerman); } -void CDKGSession::VerifyConnectionAndMinProtoVersions(CConnman& connman) const +void ActiveDKGSession::VerifyConnectionAndMinProtoVersions(CConnman& connman) const { assert(m_mn_metaman.IsValid()); @@ -239,7 +254,7 @@ void CDKGSession::VerifyConnectionAndMinProtoVersions(CConnman& connman) const } } -void CDKGSession::SendComplaint(CDKGPendingMessages& pendingMessages, PeerManager& peerman) +void ActiveDKGSession::SendComplaint(CDKGPendingMessages& pendingMessages, PeerManager& peerman) { CDKGLogger logger(*this, __func__, __LINE__); @@ -269,7 +284,7 @@ void CDKGSession::SendComplaint(CDKGPendingMessages& pendingMessages, PeerManage logger.Batch("sending complaint. badCount=%d, complaintCount=%d", badCount, complaintCount); - qc.sig = m_mn_activeman->Sign(qc.GetSignHash(), m_use_legacy_bls); + qc.sig = m_mn_activeman.Sign(qc.GetSignHash(), m_use_legacy_bls); logger.Flush(); @@ -281,7 +296,7 @@ void CDKGSession::SendComplaint(CDKGPendingMessages& pendingMessages, PeerManage pendingMessages.PushPendingMessage(-1, qc, peerman); } -void CDKGSession::VerifyAndJustify(CDKGPendingMessages& pendingMessages, PeerManager& peerman) +void ActiveDKGSession::VerifyAndJustify(CDKGPendingMessages& pendingMessages, PeerManager& peerman) { if (!AreWeMember()) { return; @@ -322,8 +337,8 @@ void CDKGSession::VerifyAndJustify(CDKGPendingMessages& pendingMessages, PeerMan } } -void CDKGSession::SendJustification(CDKGPendingMessages& pendingMessages, PeerManager& peerman, - const std::set& forMembers) +void ActiveDKGSession::SendJustification(CDKGPendingMessages& pendingMessages, PeerManager& peerman, + const std::set& forMembers) { CDKGLogger logger(*this, __func__, __LINE__); @@ -359,7 +374,7 @@ void CDKGSession::SendJustification(CDKGPendingMessages& pendingMessages, PeerMa return; } - qj.sig = m_mn_activeman->Sign(qj.GetSignHash(), m_use_legacy_bls); + qj.sig = m_mn_activeman.Sign(qj.GetSignHash(), m_use_legacy_bls); logger.Flush(); @@ -371,7 +386,7 @@ void CDKGSession::SendJustification(CDKGPendingMessages& pendingMessages, PeerMa pendingMessages.PushPendingMessage(-1, qj, peerman); } -void CDKGSession::VerifyAndCommit(CDKGPendingMessages& pendingMessages, PeerManager& peerman) +void ActiveDKGSession::VerifyAndCommit(CDKGPendingMessages& pendingMessages, PeerManager& peerman) { if (!AreWeMember()) { return; @@ -416,7 +431,7 @@ void CDKGSession::VerifyAndCommit(CDKGPendingMessages& pendingMessages, PeerMana SendCommitment(pendingMessages, peerman); } -void CDKGSession::SendCommitment(CDKGPendingMessages& pendingMessages, PeerManager& peerman) +void ActiveDKGSession::SendCommitment(CDKGPendingMessages& pendingMessages, PeerManager& peerman) { CDKGLogger logger(*this, __func__, __LINE__); @@ -498,7 +513,7 @@ void CDKGSession::SendCommitment(CDKGPendingMessages& pendingMessages, PeerManag (*commitmentHash.begin())++; } - qc.sig = m_mn_activeman->Sign(commitmentHash, m_use_legacy_bls); + qc.sig = m_mn_activeman.Sign(commitmentHash, m_use_legacy_bls); qc.quorumSig = skShare.Sign(commitmentHash, m_use_legacy_bls); if (lieType == 3) { @@ -528,7 +543,7 @@ void CDKGSession::SendCommitment(CDKGPendingMessages& pendingMessages, PeerManag pendingMessages.PushPendingMessage(-1, qc, peerman); } -std::vector CDKGSession::FinalizeCommitments() +std::vector ActiveDKGSession::FinalizeCommitments() { if (!AreWeMember()) { return {}; @@ -637,7 +652,7 @@ std::vector CDKGSession::FinalizeCommitments() return finalCommitments; } -CFinalCommitment CDKGSession::FinalizeSingleCommitment() +CFinalCommitment ActiveDKGSession::FinalizeSingleCommitment() { if (!AreWeMember()) { return {}; @@ -664,7 +679,7 @@ CFinalCommitment CDKGSession::FinalizeSingleCommitment() // TODO: use sk1 here instead and use recovery mechanism from shares, but that's not trivial to do const bool workaround_qpublic_key = true; if (workaround_qpublic_key) { - fqc.quorumPublicKey = m_mn_activeman->GetPubKey(); + fqc.quorumPublicKey = m_mn_activeman.GetPubKey(); } const bool isQuorumRotationEnabled{false}; fqc.nVersion = CFinalCommitment::GetVersion(isQuorumRotationEnabled, @@ -676,7 +691,7 @@ CFinalCommitment CDKGSession::FinalizeSingleCommitment() fqc.quorumVvecHash); fqc.quorumSig = sk1.Sign(commitmentHash, m_use_legacy_bls); - fqc.membersSig = m_mn_activeman->Sign(commitmentHash, m_use_legacy_bls); + fqc.membersSig = m_mn_activeman.Sign(commitmentHash, m_use_legacy_bls); if (workaround_qpublic_key) { fqc.quorumSig = fqc.membersSig; @@ -694,4 +709,10 @@ CFinalCommitment CDKGSession::FinalizeSingleCommitment() return fqc; } + +bool ActiveDKGSession::MaybeDecrypt(const CBLSIESMultiRecipientObjects& obj, size_t idx, + CBLSSecretKey& ret_obj, int version) +{ + return m_mn_activeman.Decrypt(obj, idx, ret_obj, version); +} } // namespace llmq diff --git a/src/active/dkgsession.h b/src/active/dkgsession.h new file mode 100644 index 000000000000..412d69e7d84b --- /dev/null +++ b/src/active/dkgsession.h @@ -0,0 +1,65 @@ +// Copyright (c) 2018-2025 The Dash Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#ifndef BITCOIN_ACTIVE_DKGSESSION_H +#define BITCOIN_ACTIVE_DKGSESSION_H + +#include + +namespace llmq { +class ActiveDKGSession final : public llmq::CDKGSession +{ +private: + CMasternodeMetaMan& m_mn_metaman; + const CActiveMasternodeManager& m_mn_activeman; + const CSporkManager& m_sporkman; + const bool m_use_legacy_bls{false}; + +public: + ActiveDKGSession() = delete; + ActiveDKGSession(const ActiveDKGSession&) = delete; + ActiveDKGSession& operator=(const ActiveDKGSession&) = delete; + ActiveDKGSession(CBLSWorker& bls_worker, CDeterministicMNManager& dmnman, CDKGDebugManager& dkgdbgman, + CDKGSessionManager& qdkgsman, CMasternodeMetaMan& mn_metaman, CQuorumSnapshotManager& qsnapman, + const CActiveMasternodeManager& mn_activeman, const ChainstateManager& chainman, + const CSporkManager& sporkman, const CBlockIndex* base_block_index, + const Consensus::LLMQParams& params); + ~ActiveDKGSession(); + +public: + // Phase 1: contribution + void Contribute(CDKGPendingMessages& pendingMessages, PeerManager& peerman) override; + void SendContributions(CDKGPendingMessages& pendingMessages, PeerManager& peerman) override; + void VerifyPendingContributions() override EXCLUSIVE_LOCKS_REQUIRED(cs_pending); + + // Phase 2: complaint + void VerifyAndComplain(CConnman& connman, CDKGPendingMessages& pendingMessages, PeerManager& peerman) override + EXCLUSIVE_LOCKS_REQUIRED(!cs_pending); + void VerifyConnectionAndMinProtoVersions(CConnman& connman) const override; + void SendComplaint(CDKGPendingMessages& pendingMessages, PeerManager& peerman) override; + + // Phase 3: justification + void VerifyAndJustify(CDKGPendingMessages& pendingMessages, PeerManager& peerman) override + EXCLUSIVE_LOCKS_REQUIRED(!invCs); + void SendJustification(CDKGPendingMessages& pendingMessages, PeerManager& peerman, + const std::set& forMembers) override; + + // Phase 4: commit + void VerifyAndCommit(CDKGPendingMessages& pendingMessages, PeerManager& peerman) override; + void SendCommitment(CDKGPendingMessages& pendingMessages, PeerManager& peerman) override; + + // Phase 5: aggregate/finalize + std::vector FinalizeCommitments() EXCLUSIVE_LOCKS_REQUIRED(!invCs) override; + + // All Phases 5-in-1 for single-node-quorum + CFinalCommitment FinalizeSingleCommitment() override; + +private: + //! CDKGSession + bool MaybeDecrypt(const CBLSIESMultiRecipientObjects& obj, size_t idx, CBLSSecretKey& ret_obj, + int version) override; +}; +} // namespace llmq + +#endif // BITCOIN_ACTIVE_DKGSESSION_H diff --git a/src/active/dkgsessionhandler.cpp b/src/active/dkgsessionhandler.cpp index 770290902f86..9a5e79b4b34f 100644 --- a/src/active/dkgsessionhandler.cpp +++ b/src/active/dkgsessionhandler.cpp @@ -4,6 +4,7 @@ #include +#include #include #include #include @@ -23,8 +24,8 @@ ActiveDKGSessionHandler::ActiveDKGSessionHandler( llmq::CDKGDebugManager& dkgdbgman, llmq::CDKGSessionManager& qdkgsman, llmq::CQuorumBlockProcessor& qblockman, llmq::CQuorumSnapshotManager& qsnapman, const CActiveMasternodeManager& mn_activeman, const ChainstateManager& chainman, const CSporkManager& sporkman, const Consensus::LLMQParams& llmq_params, bool quorums_watch, int quorums_idx) : - llmq::CDKGSessionHandler(bls_worker, dmnman, dkgdbgman, qdkgsman, mn_metaman, qblockman, qsnapman, &mn_activeman, - chainman, sporkman, llmq_params, quorums_watch, quorums_idx), + llmq::CDKGSessionHandler(bls_worker, dmnman, dkgdbgman, qdkgsman, qsnapman, chainman, llmq_params, quorums_watch, + quorums_idx), m_bls_worker{bls_worker}, m_dmnman{dmnman}, m_mn_metaman{mn_metaman}, @@ -37,6 +38,11 @@ ActiveDKGSessionHandler::ActiveDKGSessionHandler( m_sporkman{sporkman}, m_quorums_watch{quorums_watch} { + // Overwrite session initialized in parent + curSession.reset(); + curSession = std::make_unique(m_bls_worker, m_dmnman, m_dkgdbgman, m_qdkgsman, m_mn_metaman, + m_qsnapman, m_mn_activeman, m_chainman, m_sporkman, + /*pQuorumBaseBlockIndex=*/nullptr, llmq_params); } ActiveDKGSessionHandler::~ActiveDKGSessionHandler() = default; @@ -99,9 +105,9 @@ bool ActiveDKGSessionHandler::InitNewQuorum(const CBlockIndex* pQuorumBaseBlockI return false; } - curSession = std::make_unique(pQuorumBaseBlockIndex, params, m_bls_worker, m_dmnman, m_qdkgsman, - m_dkgdbgman, m_mn_metaman, m_qsnapman, &m_mn_activeman, m_chainman, - m_sporkman); + curSession = std::make_unique(m_bls_worker, m_dmnman, m_dkgdbgman, m_qdkgsman, m_mn_metaman, + m_qsnapman, m_mn_activeman, m_chainman, m_sporkman, + pQuorumBaseBlockIndex, params); if (!curSession->Init(m_mn_activeman.GetProTxHash(), quorumIndex)) { LogPrintf("ActiveDKGSessionHandler::%s -- height[%d] quorum initialization failed for %s qi[%d]\n", __func__, diff --git a/src/llmq/dkgsession.cpp b/src/llmq/dkgsession.cpp index d7f770393369..23fe49c3be08 100644 --- a/src/llmq/dkgsession.cpp +++ b/src/llmq/dkgsession.cpp @@ -66,27 +66,24 @@ CDKGMember::CDKGMember(const CDeterministicMNCPtr& _dmn, size_t _idx) : { } -CDKGSession::CDKGSession(const CBlockIndex* pQuorumBaseBlockIndex, const Consensus::LLMQParams& _params, - CBLSWorker& _blsWorker, CDeterministicMNManager& dmnman, CDKGSessionManager& _dkgManager, - CDKGDebugManager& _dkgDebugManager, CMasternodeMetaMan& mn_metaman, - CQuorumSnapshotManager& qsnapman, const CActiveMasternodeManager* const mn_activeman, - const ChainstateManager& chainman, const CSporkManager& sporkman) : - params{_params}, +CDKGSession::CDKGSession(CBLSWorker& _blsWorker, CDeterministicMNManager& dmnman, CDKGDebugManager& _dkgDebugManager, + CDKGSessionManager& _dkgManager, CQuorumSnapshotManager& qsnapman, + const ChainstateManager& chainman, const CBlockIndex* pQuorumBaseBlockIndex, + const Consensus::LLMQParams& _params) : blsWorker{_blsWorker}, cache{_blsWorker}, m_dmnman{dmnman}, - dkgManager{_dkgManager}, dkgDebugManager{_dkgDebugManager}, - m_mn_metaman{mn_metaman}, + dkgManager{_dkgManager}, m_qsnapman{qsnapman}, - m_mn_activeman{mn_activeman}, m_chainman{chainman}, - m_sporkman{sporkman}, - m_quorum_base_block_index{pQuorumBaseBlockIndex}, - m_use_legacy_bls{!DeploymentActiveAfter(m_quorum_base_block_index, chainman.GetConsensus(), Consensus::DEPLOYMENT_V19)} + params{_params}, + m_quorum_base_block_index{pQuorumBaseBlockIndex} { } +CDKGSession::~CDKGSession() = default; + bool CDKGSession::Init(const uint256& _myProTxHash, int _quorumIndex) { const auto mns = utils::GetAllQuorumMembers(params.type, {m_dmnman, m_qsnapman, m_chainman, m_quorum_base_block_index}); @@ -255,7 +252,7 @@ std::optional CDKGSession::ReceiveMessage(const CDKGContribution& qc) bool complain = false; CBLSSecretKey skContribution; - if (!m_mn_activeman->Decrypt(*qc.contributions, *myIdx, skContribution, PROTOCOL_VERSION)) { + if (!MaybeDecrypt(*qc.contributions, *myIdx, skContribution, PROTOCOL_VERSION)) { logger.Batch("contribution from %s could not be decrypted", member->dmn->proTxHash.ToString()); complain = true; } else if (member->idx != myIdx && ShouldSimulateError(DKGError::type::COMPLAIN_LIE)) { diff --git a/src/llmq/dkgsession.h b/src/llmq/dkgsession.h index df8b45c32ad2..16b2e1416653 100644 --- a/src/llmq/dkgsession.h +++ b/src/llmq/dkgsession.h @@ -28,6 +28,7 @@ class CMasternodeMetaMan; class CSporkManager; class PeerManager; namespace llmq { +class ActiveDKGSession; class ActiveDKGSessionHandler; class CDKGDebugManager; class CDKGPendingMessages; @@ -274,30 +275,25 @@ class CDKGLogger : public CBatchedLogger */ class CDKGSession { + friend class ActiveDKGSession; friend class ActiveDKGSessionHandler; friend class CDKGSessionHandler; friend class CDKGSessionManager; friend class CDKGLogger; private: - const Consensus::LLMQParams params; - CBLSWorker& blsWorker; CBLSWorkerCache cache; CDeterministicMNManager& m_dmnman; - CDKGSessionManager& dkgManager; CDKGDebugManager& dkgDebugManager; - CMasternodeMetaMan& m_mn_metaman; + CDKGSessionManager& dkgManager; CQuorumSnapshotManager& m_qsnapman; - const CActiveMasternodeManager* const m_mn_activeman; const ChainstateManager& m_chainman; - const CSporkManager& m_sporkman; - + const Consensus::LLMQParams& params; const CBlockIndex* const m_quorum_base_block_index; - bool m_use_legacy_bls; - int quorumIndex{0}; private: + int quorumIndex{0}; std::vector> members; std::map membersMap; Uint256HashSet relayMembers; @@ -332,17 +328,14 @@ class CDKGSession std::set validCommitments GUARDED_BY(invCs); public: - CDKGSession(const CBlockIndex* pQuorumBaseBlockIndex, const Consensus::LLMQParams& _params, CBLSWorker& _blsWorker, - CDeterministicMNManager& dmnman, CDKGSessionManager& _dkgManager, CDKGDebugManager& _dkgDebugManager, - CMasternodeMetaMan& mn_metaman, CQuorumSnapshotManager& qsnapman, - const CActiveMasternodeManager* const mn_activeman, const ChainstateManager& chainman, - const CSporkManager& sporkman); + CDKGSession(CBLSWorker& _blsWorker, CDeterministicMNManager& dmnman, CDKGDebugManager& _dkgDebugManager, + CDKGSessionManager& _dkgManager, CQuorumSnapshotManager& qsnapman, const ChainstateManager& chainman, + const CBlockIndex* pQuorumBaseBlockIndex, const Consensus::LLMQParams& _params); + virtual ~CDKGSession(); // TODO: remove Init completely bool Init(const uint256& _myProTxHash, int _quorumIndex); - [[nodiscard]] std::optional GetMyMemberIndex() const { return myIdx; } - /** * The following sets of methods are for the first 4 phases handled in the session. The flow of message calls * is identical for all phases: @@ -357,50 +350,57 @@ class CDKGSession */ // Phase 1: contribution - void Contribute(CDKGPendingMessages& pendingMessages, PeerManager& peerman); - void SendContributions(CDKGPendingMessages& pendingMessages, PeerManager& peerman); + virtual void Contribute(CDKGPendingMessages& pendingMessages, PeerManager& peerman) {} + virtual void SendContributions(CDKGPendingMessages& pendingMessages, PeerManager& peerman) {} bool PreVerifyMessage(const CDKGContribution& qc, bool& retBan) const; std::optional ReceiveMessage(const CDKGContribution& qc) EXCLUSIVE_LOCKS_REQUIRED(!invCs, !cs_pending); - void VerifyPendingContributions() EXCLUSIVE_LOCKS_REQUIRED(cs_pending); + virtual void VerifyPendingContributions() EXCLUSIVE_LOCKS_REQUIRED(cs_pending) {} // Phase 2: complaint - void VerifyAndComplain(CConnman& connman, CDKGPendingMessages& pendingMessages, PeerManager& peerman) - EXCLUSIVE_LOCKS_REQUIRED(!cs_pending); - void VerifyConnectionAndMinProtoVersions(CConnman& connman) const; - void SendComplaint(CDKGPendingMessages& pendingMessages, PeerManager& peerman); + virtual void VerifyAndComplain(CConnman& connman, CDKGPendingMessages& pendingMessages, PeerManager& peerman) + EXCLUSIVE_LOCKS_REQUIRED(!cs_pending) {} + virtual void VerifyConnectionAndMinProtoVersions(CConnman& connman) const {} + virtual void SendComplaint(CDKGPendingMessages& pendingMessages, PeerManager& peerman) {} bool PreVerifyMessage(const CDKGComplaint& qc, bool& retBan) const; std::optional ReceiveMessage(const CDKGComplaint& qc) EXCLUSIVE_LOCKS_REQUIRED(!invCs); // Phase 3: justification - void VerifyAndJustify(CDKGPendingMessages& pendingMessages, PeerManager& peerman) EXCLUSIVE_LOCKS_REQUIRED(!invCs); - void SendJustification(CDKGPendingMessages& pendingMessages, PeerManager& peerman, const std::set& forMembers); + virtual void VerifyAndJustify(CDKGPendingMessages& pendingMessages, PeerManager& peerman) EXCLUSIVE_LOCKS_REQUIRED(!invCs) {} + virtual void SendJustification(CDKGPendingMessages& pendingMessages, PeerManager& peerman, const std::set& forMembers) {} bool PreVerifyMessage(const CDKGJustification& qj, bool& retBan) const; std::optional ReceiveMessage(const CDKGJustification& qj) EXCLUSIVE_LOCKS_REQUIRED(!invCs); // Phase 4: commit - void VerifyAndCommit(CDKGPendingMessages& pendingMessages, PeerManager& peerman); - void SendCommitment(CDKGPendingMessages& pendingMessages, PeerManager& peerman); + virtual void VerifyAndCommit(CDKGPendingMessages& pendingMessages, PeerManager& peerman) {} + virtual void SendCommitment(CDKGPendingMessages& pendingMessages, PeerManager& peerman) {} bool PreVerifyMessage(const CDKGPrematureCommitment& qc, bool& retBan) const; std::optional ReceiveMessage(const CDKGPrematureCommitment& qc) EXCLUSIVE_LOCKS_REQUIRED(!invCs); // Phase 5: aggregate/finalize - std::vector FinalizeCommitments() EXCLUSIVE_LOCKS_REQUIRED(!invCs); + virtual std::vector FinalizeCommitments() EXCLUSIVE_LOCKS_REQUIRED(!invCs) { return {}; } // All Phases 5-in-1 for single-node-quorum - CFinalCommitment FinalizeSingleCommitment(); - - [[nodiscard]] bool AreWeMember() const { return !myProTxHash.IsNull(); } - void MarkBadMember(size_t idx); + virtual CFinalCommitment FinalizeSingleCommitment() { return {}; } public: + [[nodiscard]] bool AreWeMember() const { return !myProTxHash.IsNull(); } [[nodiscard]] CDKGMember* GetMember(const uint256& proTxHash) const; + [[nodiscard]] std::optional GetMyMemberIndex() const { return myIdx; } [[nodiscard]] const Uint256HashSet& RelayMembers() const { return relayMembers; } [[nodiscard]] const CBlockIndex* BlockIndex() const { return m_quorum_base_block_index; } [[nodiscard]] const uint256& ProTx() const { return myProTxHash; } [[nodiscard]] const Consensus::LLMQParams GetParams() const { return params; } +protected: + virtual bool MaybeDecrypt(const CBLSIESMultiRecipientObjects& obj, size_t idx, + CBLSSecretKey& ret_obj, int version) + { + return false; + } + private: [[nodiscard]] bool ShouldSimulateError(DKGError::type type) const; + void MarkBadMember(size_t idx); }; void SetSimulatedDKGErrorRate(DKGError::type type, double rate); diff --git a/src/llmq/dkgsessionhandler.cpp b/src/llmq/dkgsessionhandler.cpp index 71aac74b167d..29c303e76889 100644 --- a/src/llmq/dkgsessionhandler.cpp +++ b/src/llmq/dkgsessionhandler.cpp @@ -21,19 +21,15 @@ #include #include -namespace llmq -{ - +namespace llmq { CDKGSessionHandler::CDKGSessionHandler(CBLSWorker& _blsWorker, CDeterministicMNManager& dmnman, CDKGDebugManager& _dkgDebugManager, CDKGSessionManager& _dkgManager, - CMasternodeMetaMan& mn_metaman, CQuorumBlockProcessor& _quorumBlockProcessor, - CQuorumSnapshotManager& qsnapman, const CActiveMasternodeManager* const mn_activeman, - const ChainstateManager& chainman, const CSporkManager& sporkman, + CQuorumSnapshotManager& qsnapman, const ChainstateManager& chainman, const Consensus::LLMQParams& _params, bool quorums_watch, int _quorumIndex) : + curSession{std::make_unique(_blsWorker, dmnman, _dkgDebugManager, _dkgManager, qsnapman, chainman, + /*pQuorumBaseBlockIndex=*/nullptr, _params)}, params{_params}, quorumIndex{_quorumIndex}, - curSession{std::make_unique(nullptr, _params, _blsWorker, dmnman, _dkgManager, _dkgDebugManager, - mn_metaman, qsnapman, mn_activeman, chainman, sporkman)}, // we allow size*2 messages as we need to make sure we see bad behavior (double messages) pendingContributions{(size_t)_params.size * 2, MSG_QUORUM_CONTRIB}, pendingComplaints{(size_t)_params.size * 2, MSG_QUORUM_COMPLAINT}, diff --git a/src/llmq/dkgsessionhandler.h b/src/llmq/dkgsessionhandler.h index ccede6cce8dc..3a3d4416ce94 100644 --- a/src/llmq/dkgsessionhandler.h +++ b/src/llmq/dkgsessionhandler.h @@ -131,10 +131,9 @@ class CDKGSessionHandler friend class CDKGSessionManager; protected: - const Consensus::LLMQParams params; - const int quorumIndex; - std::unique_ptr curSession; + const Consensus::LLMQParams& params; + const int quorumIndex; // Do not guard these, they protect their internals themselves CDKGPendingMessages pendingContributions; @@ -144,11 +143,8 @@ class CDKGSessionHandler public: CDKGSessionHandler(CBLSWorker& _blsWorker, CDeterministicMNManager& dmnman, CDKGDebugManager& _dkgDebugManager, - CDKGSessionManager& _dkgManager, CMasternodeMetaMan& mn_metaman, - CQuorumBlockProcessor& _quorumBlockProcessor, CQuorumSnapshotManager& qsnapman, - const CActiveMasternodeManager* const mn_activeman, const ChainstateManager& chainman, - const CSporkManager& sporkman, const Consensus::LLMQParams& _params, bool quorums_watch, - int _quorumIndex); + CDKGSessionManager& _dkgManager, CQuorumSnapshotManager& qsnapman, const ChainstateManager& chainman, + const Consensus::LLMQParams& _params, bool quorums_watch, int _quorumIndex); virtual ~CDKGSessionHandler(); [[nodiscard]] MessageProcessingResult ProcessMessage(NodeId from, std::string_view msg_type, CDataStream& vRecv); diff --git a/src/llmq/observer/context.cpp b/src/llmq/observer/context.cpp index 7a2ae6f29f76..3f7313acb6cf 100644 --- a/src/llmq/observer/context.cpp +++ b/src/llmq/observer/context.cpp @@ -27,9 +27,8 @@ ObserverContext::ObserverContext(CBLSWorker& bls_worker, CConnman& connman, CDet { qdkgsman->InitializeHandlers([&](const Consensus::LLMQParams& llmq_params, int quorum_idx) -> std::unique_ptr { - return std::make_unique(bls_worker, dmnman, *dkgdbgman, *qdkgsman, mn_metaman, - qblockman, qsnapman, /*mn_activeman=*/nullptr, chainman, - sporkman, llmq_params, /*quorums_watch=*/true, quorum_idx); + return std::make_unique(bls_worker, dmnman, *dkgdbgman, *qdkgsman, qsnapman, chainman, + llmq_params, /*quorums_watch=*/true, quorum_idx); }); m_qman.ConnectManagers(qman_handler.get(), qdkgsman.get()); } From 004b3b7afc415238c6c9c2bf62227c163cfd4b6c Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Mon, 5 Jan 2026 00:39:26 +0530 Subject: [PATCH 07/14] refactor: drop `CActiveMasternodeManager` from chainstate init --- src/init.cpp | 1 - src/llmq/context.cpp | 4 ++-- src/llmq/context.h | 4 ++-- src/node/chainstate.cpp | 6 ++---- src/node/chainstate.h | 2 -- src/test/util/setup_common.cpp | 7 +++---- 6 files changed, 9 insertions(+), 15 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 364af98cb4d5..11b214744ff1 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -2012,7 +2012,6 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) *node.mn_metaman, *node.mn_sync, *node.sporkman, - node.mn_activeman, node.chain_helper, node.cpoolman, node.dmnman, diff --git a/src/llmq/context.cpp b/src/llmq/context.cpp index b2f3f5288a09..d22a8de1156a 100644 --- a/src/llmq/context.cpp +++ b/src/llmq/context.cpp @@ -13,8 +13,8 @@ #include #include -LLMQContext::LLMQContext(ChainstateManager& chainman, CDeterministicMNManager& dmnman, CEvoDB& evo_db, - CSporkManager& sporkman, CTxMemPool& mempool, const CMasternodeSync& mn_sync, +LLMQContext::LLMQContext(CDeterministicMNManager& dmnman, CEvoDB& evo_db, CSporkManager& sporkman, CTxMemPool& mempool, + const ChainstateManager& chainman, const CMasternodeSync& mn_sync, const util::DbWrapperParams& db_params, int8_t bls_threads, int64_t max_recsigs_age) : bls_worker{std::make_shared()}, qsnapman{std::make_unique(evo_db)}, diff --git a/src/llmq/context.h b/src/llmq/context.h index a31f1071f5d1..1e2e84e25a0e 100644 --- a/src/llmq/context.h +++ b/src/llmq/context.h @@ -36,8 +36,8 @@ struct LLMQContext { LLMQContext() = delete; LLMQContext(const LLMQContext&) = delete; LLMQContext& operator=(const LLMQContext&) = delete; - explicit LLMQContext(ChainstateManager& chainman, CDeterministicMNManager& dmnman, CEvoDB& evo_db, - CSporkManager& sporkman, CTxMemPool& mempool, const CMasternodeSync& mn_sync, + explicit LLMQContext(CDeterministicMNManager& dmnman, CEvoDB& evo_db, CSporkManager& sporkman, CTxMemPool& mempool, + const ChainstateManager& chainman, const CMasternodeSync& mn_sync, const util::DbWrapperParams& db_params, int8_t bls_threads, int64_t max_recsigs_age); ~LLMQContext(); diff --git a/src/node/chainstate.cpp b/src/node/chainstate.cpp index 032e47318f90..e60eaace5630 100644 --- a/src/node/chainstate.cpp +++ b/src/node/chainstate.cpp @@ -40,7 +40,6 @@ std::optional LoadChainstate(bool fReset, CMasternodeMetaMan& mn_metaman, CMasternodeSync& mn_sync, CSporkManager& sporkman, - std::unique_ptr& mn_activeman, std::unique_ptr& chain_helper, std::unique_ptr& cpoolman, std::unique_ptr& dmnman, @@ -88,7 +87,7 @@ std::optional LoadChainstate(bool fReset, pblocktree.reset(); pblocktree.reset(new CBlockTreeDB(nBlockTreeDBCache, block_tree_db_in_memory, fReset)); - DashChainstateSetup(chainman, govman, mn_metaman, mn_sync, sporkman, mn_activeman, chain_helper, cpoolman, + DashChainstateSetup(chainman, govman, mn_metaman, mn_sync, sporkman, chain_helper, cpoolman, dmnman, evodb, mnhf_manager, llmq_ctx, mempool, data_dir, dash_dbs_in_memory, /*llmq_dbs_wipe=*/fReset || fReindexChainState, bls_threads, max_recsigs_age, consensus_params); @@ -215,7 +214,6 @@ void DashChainstateSetup(ChainstateManager& chainman, CMasternodeMetaMan& mn_metaman, CMasternodeSync& mn_sync, CSporkManager& sporkman, - std::unique_ptr& mn_activeman, std::unique_ptr& chain_helper, std::unique_ptr& cpoolman, std::unique_ptr& dmnman, @@ -241,7 +239,7 @@ void DashChainstateSetup(ChainstateManager& chainman, llmq_ctx->Stop(); } llmq_ctx.reset(); - llmq_ctx = std::make_unique(chainman, *dmnman, *evodb, sporkman, *mempool, mn_sync, + llmq_ctx = std::make_unique(*dmnman, *evodb, sporkman, *mempool, chainman, mn_sync, util::DbWrapperParams{.path = data_dir, .memory = llmq_dbs_in_memory, .wipe = llmq_dbs_wipe}, bls_threads, max_recsigs_age); mempool->ConnectManagers(dmnman.get(), llmq_ctx->isman.get()); diff --git a/src/node/chainstate.h b/src/node/chainstate.h index f5c456c2abbe..0022b7637d87 100644 --- a/src/node/chainstate.h +++ b/src/node/chainstate.h @@ -84,7 +84,6 @@ std::optional LoadChainstate(bool fReset, CMasternodeMetaMan& mn_metaman, CMasternodeSync& mn_sync, CSporkManager& sporkman, - std::unique_ptr& mn_activeman, std::unique_ptr& chain_helper, std::unique_ptr& cpoolman, std::unique_ptr& dmnman, @@ -116,7 +115,6 @@ void DashChainstateSetup(ChainstateManager& chainman, CMasternodeMetaMan& mn_metaman, CMasternodeSync& mn_sync, CSporkManager& sporkman, - std::unique_ptr& mn_activeman, std::unique_ptr& chain_helper, std::unique_ptr& cpoolman, std::unique_ptr& dmnman, diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index cd999067b278..e9bf4b28d03b 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -145,9 +145,9 @@ void DashChainstateSetup(ChainstateManager& chainman, const Consensus::Params& consensus_params) { DashChainstateSetup(chainman, *Assert(node.govman.get()), *Assert(node.mn_metaman.get()), *Assert(node.mn_sync.get()), - *Assert(node.sporkman.get()), node.mn_activeman, node.chain_helper, node.cpoolman, node.dmnman, - node.evodb, node.mnhf_manager, node.llmq_ctx, Assert(node.mempool.get()), node.args->GetDataDirNet(), - llmq_dbs_in_memory, llmq_dbs_wipe, llmq::DEFAULT_BLSCHECK_THREADS, llmq::DEFAULT_MAX_RECOVERED_SIGS_AGE, consensus_params); + *Assert(node.sporkman.get()), node.chain_helper, node.cpoolman, node.dmnman, node.evodb, node.mnhf_manager, + node.llmq_ctx, Assert(node.mempool.get()), node.args->GetDataDirNet(), llmq_dbs_in_memory, llmq_dbs_wipe, + llmq::DEFAULT_BLSCHECK_THREADS, llmq::DEFAULT_MAX_RECOVERED_SIGS_AGE, consensus_params); } void DashChainstateSetupClose(NodeContext& node) @@ -324,7 +324,6 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector Date: Mon, 5 Jan 2026 15:27:11 +0530 Subject: [PATCH 08/14] refactor: move `CActiveMasternodeManager` to `ActiveContext` We also need to move CCoinJoinServer creation back into ActiveContext --- src/init.cpp | 52 ++++++++----------- src/masternode/active/context.cpp | 39 +++++++++----- src/masternode/active/context.h | 24 ++++++--- .../active/notificationinterface.cpp | 7 ++- src/masternode/active/notificationinterface.h | 3 +- src/masternode/node.cpp | 14 ++--- src/masternode/node.h | 12 ++--- src/net_processing.cpp | 18 +++---- src/net_processing.h | 1 - src/node/context.h | 1 - src/node/interfaces.cpp | 2 +- src/rpc/coinjoin.cpp | 12 ++--- src/rpc/governance.cpp | 9 ++-- src/rpc/masternode.cpp | 14 ++--- src/rpc/quorums.cpp | 6 +-- src/test/coinjoin_queue_tests.cpp | 4 +- src/test/denialofservice_tests.cpp | 8 +-- src/test/net_peer_connection_tests.cpp | 2 +- src/test/util/setup_common.cpp | 5 +- src/test/util/setup_common.h | 1 - 20 files changed, 121 insertions(+), 113 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index 11b214744ff1..1ade19212871 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -419,8 +419,6 @@ void PrepareShutdown(NodeContext& node) } #endif - node.mn_activeman.reset(); - node.chain_clients.clear(); // After all wallets are removed, destroy all CoinJoin objects @@ -1730,16 +1728,6 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) } } - std::string strMasterNodeBLSPrivKey = args.GetArg("-masternodeblsprivkey", ""); - if (!strMasterNodeBLSPrivKey.empty()) { - CBLSSecretKey keyOperator(ParseHex(strMasterNodeBLSPrivKey)); - if (!keyOperator.IsValid()) { - return InitError(_("Invalid masternodeblsprivkey. Please see documentation.")); - } - // Create and register mn_activeman, will init later in ThreadImport - node.mn_activeman = std::make_unique(keyOperator, *node.connman, node.dmnman); - } - // Check port numbers for (const std::string port_option : { "-port", @@ -2191,7 +2179,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) assert(!node.peerman); node.peerman = PeerManager::make(chainparams, *node.connman, *node.addrman, node.banman.get(), *node.dstxman, chainman, *node.mempool, *node.mn_metaman, *node.mn_sync, - *node.govman, *node.sporkman, node.mn_activeman.get(), node.active_ctx, node.dmnman, + *node.govman, *node.sporkman, node.active_ctx, node.dmnman, node.cj_walletman, node.llmq_ctx, node.observer_ctx, ignores_incoming_txs); RegisterValidationInterface(node.peerman.get()); @@ -2209,14 +2197,16 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) const bool quorums_watch = args.GetBoolArg("-watchquorums", llmq::DEFAULT_WATCH_QUORUMS); const llmq::QvvecSyncModeMap sync_map{llmq::GetEnabledQuorumVvecSyncEntries(args)}; const util::DbWrapperParams dash_db_params{.path = args.GetDataDirNet(), .memory = false, .wipe = (fReindex || fReindexChainState)}; - if (node.mn_activeman) { - auto cj_server = std::make_unique(node.peerman.get(), chainman, *node.connman, *node.dmnman, *node.dstxman, *node.mn_metaman, - *node.mempool, *node.mn_activeman, *node.mn_sync, *node.llmq_ctx->isman); - node.active_ctx = std::make_unique(*cj_server, *node.connman, *node.dmnman, *node.govman, chainman, *node.mn_metaman, - *node.mnhf_manager, *node.sporkman, *node.mempool, *node.llmq_ctx, *node.peerman, - *node.mn_activeman, *node.mn_sync, sync_map, dash_db_params, quorums_recovery, quorums_watch); - node.peerman->AddExtraHandler(std::move(cj_server)); - g_active_notification_interface = std::make_unique(*node.active_ctx, *node.mn_activeman); + if (const auto operator_sk_str = args.GetArg("-masternodeblsprivkey", ""); !operator_sk_str.empty()) { + const CBLSSecretKey operator_sk{ParseHex(operator_sk_str)}; + if (!operator_sk.IsValid()) { + return InitError(_("Invalid masternodeblsprivkey. Please see documentation.")); + } + // Will init later in ThreadImport + node.active_ctx = std::make_unique(*node.connman, *node.dmnman, *node.govman, chainman, *node.mn_metaman, *node.mnhf_manager, + *node.sporkman, *node.mempool, *node.llmq_ctx, *node.peerman, *node.mn_sync, operator_sk, + sync_map, dash_db_params, quorums_recovery, quorums_watch); + g_active_notification_interface = std::make_unique(*node.active_ctx); RegisterValidationInterface(g_active_notification_interface.get()); } else if (quorums_watch) { node.observer_ctx = std::make_unique(*node.llmq_ctx->bls_worker, *node.connman, *node.dmnman, *node.mn_metaman, *node.mn_sync, @@ -2230,8 +2220,13 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) node.peerman->AddExtraHandler(std::make_unique(node.peerman.get(), *node.llmq_ctx->isman, *node.llmq_ctx->qman, chainman.ActiveChainstate())); node.peerman->AddExtraHandler(std::make_unique(node.peerman.get(), *node.llmq_ctx->sigman)); - assert(!node.cj_walletman); - if (!node.active_ctx) { + 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.active_ctx->SetCJServer(cj_server.get()); + node.peerman->AddExtraHandler(std::move(cj_server)); + } else { + assert(!node.cj_walletman); // Can return nullptr if built without wallet support, must check before use node.cj_walletman = CJWalletManager::make(chainman, *node.dmnman, *node.mn_metaman, *node.mempool, *node.mn_sync, *node.llmq_ctx->isman, !ignores_incoming_txs); @@ -2336,7 +2331,6 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) node.llmq_ctx->Start(); node.peerman->StartHandlers(); - if (node.active_ctx) node.active_ctx->Start(*node.connman, *node.peerman); if (node.observer_ctx) node.observer_ctx->Start(); node.scheduler->scheduleEvery(std::bind(&CNetFulfilledRequestManager::DoMaintenance, std::ref(*node.netfulfilledman)), std::chrono::minutes{1}); @@ -2344,7 +2338,8 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) node.scheduler->scheduleEvery(std::bind(&CDeterministicMNManager::DoMaintenance, std::ref(*node.dmnman)), std::chrono::seconds{10}); node.peerman->ScheduleHandlers(*node.scheduler); - if (node.mn_activeman) { + if (node.active_ctx) { + node.active_ctx->Start(*node.connman, *node.peerman); node.scheduler->scheduleEvery(std::bind(&llmq::CDKGSessionManager::CleanupOldContributions, std::ref(*node.active_ctx->qdkgsman)), std::chrono::hours{1}); } @@ -2491,10 +2486,9 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) } } - if (node.mn_activeman != nullptr) { - node.mn_activeman->Init(chainman.ActiveTip()); + if (node.active_ctx) { + node.active_ctx->nodeman->Init(chainman.ActiveTip()); } - }); #ifdef ENABLE_WALLET if (!args.GetBoolArg("-disablewallet", DEFAULT_DISABLE_WALLET)) { @@ -2562,7 +2556,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) connOptions.nMaxOutboundLimit = *opt_max_upload; connOptions.m_peer_connect_timeout = peer_connect_timeout; connOptions.socketEventsMode = ::g_socket_events_mode; - connOptions.m_active_masternode = node.mn_activeman != nullptr; + connOptions.m_active_masternode = node.active_ctx != nullptr; // Port to bind to if `-bind=addr` is provided without a `:port` suffix. const uint16_t default_bind_port = diff --git a/src/masternode/active/context.cpp b/src/masternode/active/context.cpp index 43cd7c90a7e6..19cccffb26f7 100644 --- a/src/masternode/active/context.cpp +++ b/src/masternode/active/context.cpp @@ -18,27 +18,30 @@ #include #include #include +#include + +#include #include -ActiveContext::ActiveContext(CCoinJoinServer& cj_server, CConnman& connman, CDeterministicMNManager& dmnman, - CGovernanceManager& govman, ChainstateManager& chainman, CMasternodeMetaMan& mn_metaman, - CMNHFManager& mnhfman, CSporkManager& sporkman, CTxMemPool& mempool, LLMQContext& llmq_ctx, - PeerManager& peerman, const CActiveMasternodeManager& mn_activeman, - const CMasternodeSync& mn_sync, const llmq::QvvecSyncModeMap& sync_map, - const util::DbWrapperParams& db_params, bool quorums_recovery, bool quorums_watch) : +ActiveContext::ActiveContext(CConnman& connman, CDeterministicMNManager& dmnman, CGovernanceManager& govman, + ChainstateManager& chainman, CMasternodeMetaMan& mn_metaman, CMNHFManager& mnhfman, + CSporkManager& sporkman, CTxMemPool& mempool, LLMQContext& llmq_ctx, PeerManager& peerman, + const CMasternodeSync& mn_sync, const CBLSSecretKey& operator_sk, + const llmq::QvvecSyncModeMap& sync_map, const util::DbWrapperParams& db_params, + bool quorums_recovery, bool quorums_watch) : m_llmq_ctx{llmq_ctx}, - m_cj_server(cj_server), - gov_signer{std::make_unique(connman, dmnman, govman, mn_activeman, chainman, mn_sync)}, + nodeman{std::make_unique(connman, dmnman, operator_sk)}, + gov_signer{std::make_unique(connman, dmnman, govman, *nodeman, chainman, mn_sync)}, dkgdbgman{std::make_unique()}, qdkgsman{std::make_unique(dmnman, *llmq_ctx.qsnapman, chainman, sporkman, db_params, quorums_watch)}, shareman{std::make_unique(connman, chainman.ActiveChainstate(), *llmq_ctx.sigman, peerman, - mn_activeman, *llmq_ctx.qman, sporkman)}, + *nodeman, *llmq_ctx.qman, sporkman)}, ehf_sighandler{ std::make_unique(chainman, mnhfman, *llmq_ctx.sigman, *shareman, *llmq_ctx.qman)}, qman_handler{std::make_unique(*llmq_ctx.bls_worker, connman, dmnman, *llmq_ctx.qman, - *llmq_ctx.qsnapman, mn_activeman, chainman, mn_sync, - sporkman, sync_map, quorums_recovery, quorums_watch)}, + *llmq_ctx.qsnapman, *nodeman, chainman, mn_sync, sporkman, + sync_map, quorums_recovery, quorums_watch)}, cl_signer{std::make_unique(chainman.ActiveChainstate(), *llmq_ctx.clhandler, *llmq_ctx.sigman, *shareman, sporkman, mn_sync)}, is_signer{std::make_unique(chainman.ActiveChainstate(), *llmq_ctx.clhandler, @@ -49,7 +52,7 @@ ActiveContext::ActiveContext(CCoinJoinServer& cj_server, CConnman& connman, CDet int quorum_idx) -> std::unique_ptr { return std::make_unique(*llmq_ctx.bls_worker, dmnman, mn_metaman, *dkgdbgman, *qdkgsman, *llmq_ctx.quorum_block_processor, - *llmq_ctx.qsnapman, mn_activeman, chainman, sporkman, + *llmq_ctx.qsnapman, *nodeman, chainman, sporkman, llmq_params, quorums_watch, quorum_idx); }); m_llmq_ctx.clhandler->ConnectSigner(cl_signer.get()); @@ -88,3 +91,15 @@ void ActiveContext::Stop() qdkgsman->StopThreads(); qman_handler->Stop(); } + +CCoinJoinServer& ActiveContext::GetCJServer() const +{ + return *Assert(m_cj_server); +} + +void ActiveContext::SetCJServer(gsl::not_null cj_server) +{ + // Prohibit double initialization + assert(m_cj_server == nullptr); + m_cj_server = cj_server; +} diff --git a/src/masternode/active/context.h b/src/masternode/active/context.h index 8896a7f7e86f..94f30c495408 100644 --- a/src/masternode/active/context.h +++ b/src/masternode/active/context.h @@ -7,9 +7,12 @@ #include +#include + #include class CActiveMasternodeManager; +class CBLSSecretKey; class ChainstateManager; class CCoinJoinServer; class CConnman; @@ -49,24 +52,26 @@ struct ActiveContext { ActiveContext() = delete; ActiveContext(const ActiveContext&) = delete; ActiveContext& operator=(const ActiveContext&) = delete; - explicit ActiveContext(CCoinJoinServer& cj_server, CConnman& connman, CDeterministicMNManager& dmnman, - CGovernanceManager& govman, ChainstateManager& chainman, CMasternodeMetaMan& mn_metaman, - CMNHFManager& mnhfman, CSporkManager& sporkman, CTxMemPool& mempool, LLMQContext& llmq_ctx, - PeerManager& peerman, const CActiveMasternodeManager& mn_activeman, - const CMasternodeSync& mn_sync, const llmq::QvvecSyncModeMap& sync_map, - const util::DbWrapperParams& db_params, bool quorums_recovery, bool quorums_watch); + explicit ActiveContext(CConnman& connman, CDeterministicMNManager& dmnman, CGovernanceManager& govman, + ChainstateManager& chainman, CMasternodeMetaMan& mn_metaman, CMNHFManager& mnhfman, + CSporkManager& sporkman, CTxMemPool& mempool, LLMQContext& llmq_ctx, PeerManager& peerman, + const CMasternodeSync& mn_sync, const CBLSSecretKey& operator_sk, + const llmq::QvvecSyncModeMap& sync_map, const util::DbWrapperParams& db_params, + bool quorums_recovery, bool quorums_watch); ~ActiveContext(); void Interrupt(); void Start(CConnman& connman, PeerManager& peerman); void Stop(); + CCoinJoinServer& GetCJServer() const; + void SetCJServer(gsl::not_null cj_server); + /* * Entities that are only utilized when masternode mode is enabled * and are accessible in their own right - * TODO: Move CActiveMasternodeManager here when dependents have been migrated */ - CCoinJoinServer& m_cj_server; + const std::unique_ptr nodeman; const std::unique_ptr gov_signer; const std::unique_ptr dkgdbgman; const std::unique_ptr qdkgsman; @@ -81,6 +86,9 @@ struct ActiveContext { */ const std::unique_ptr cl_signer; const std::unique_ptr is_signer; + + /** Owned by PeerManager, use GetCJServer() */ + CCoinJoinServer* m_cj_server{nullptr}; }; #endif // BITCOIN_MASTERNODE_ACTIVE_CONTEXT_H diff --git a/src/masternode/active/notificationinterface.cpp b/src/masternode/active/notificationinterface.cpp index fa01867b17ff..83a9f100aac3 100644 --- a/src/masternode/active/notificationinterface.cpp +++ b/src/masternode/active/notificationinterface.cpp @@ -12,9 +12,8 @@ #include #include -ActiveNotificationInterface::ActiveNotificationInterface(ActiveContext& active_ctx, CActiveMasternodeManager& mn_activeman) : - m_active_ctx{active_ctx}, - m_mn_activeman{mn_activeman} +ActiveNotificationInterface::ActiveNotificationInterface(ActiveContext& active_ctx) : + m_active_ctx{active_ctx} { } @@ -26,7 +25,7 @@ void ActiveNotificationInterface::UpdatedBlockTip(const CBlockIndex* pindexNew, if (fInitialDownload || pindexNew == pindexFork) // In IBD or blocks were disconnected without any new ones return; - m_mn_activeman.UpdatedBlockTip(pindexNew, pindexFork, fInitialDownload); + m_active_ctx.nodeman->UpdatedBlockTip(pindexNew, pindexFork, fInitialDownload); m_active_ctx.ehf_sighandler->UpdatedBlockTip(pindexNew); m_active_ctx.gov_signer->UpdatedBlockTip(pindexNew); m_active_ctx.qdkgsman->UpdatedBlockTip(pindexNew, fInitialDownload); diff --git a/src/masternode/active/notificationinterface.h b/src/masternode/active/notificationinterface.h index fade9ede4130..b39023d5c76e 100644 --- a/src/masternode/active/notificationinterface.h +++ b/src/masternode/active/notificationinterface.h @@ -21,7 +21,7 @@ class ActiveNotificationInterface final : public CValidationInterface ActiveNotificationInterface() = delete; ActiveNotificationInterface(const ActiveNotificationInterface&) = delete; ActiveNotificationInterface& operator=(const ActiveNotificationInterface&) = delete; - explicit ActiveNotificationInterface(ActiveContext& active_ctx, CActiveMasternodeManager& mn_activeman); + explicit ActiveNotificationInterface(ActiveContext& active_ctx); virtual ~ActiveNotificationInterface(); protected: @@ -31,7 +31,6 @@ class ActiveNotificationInterface final : public CValidationInterface private: ActiveContext& m_active_ctx; - CActiveMasternodeManager& m_mn_activeman; }; extern std::unique_ptr g_active_notification_interface; diff --git a/src/masternode/node.cpp b/src/masternode/node.cpp index 7c48ce7f4fba..6ff91fe37e88 100644 --- a/src/masternode/node.cpp +++ b/src/masternode/node.cpp @@ -49,11 +49,11 @@ bool GetLocal(CService& addr, const CNetAddr* paddrPeer) } } // anonymous namespace -CActiveMasternodeManager::CActiveMasternodeManager(const CBLSSecretKey& sk, CConnman& connman, - const std::unique_ptr& dmnman) : - m_info{sk, sk.GetPublicKey()}, +CActiveMasternodeManager::CActiveMasternodeManager(CConnman& connman, CDeterministicMNManager& dmnman, + const CBLSSecretKey& sk) : m_connman{connman}, - m_dmnman{dmnman} + m_dmnman{dmnman}, + m_info{sk, sk.GetPublicKey()} { assert(sk.IsValid()); /* We can assume pk is valid if sk is valid */ LogPrintf("MASTERNODE:\n blsPubKeyOperator legacy: %s\n blsPubKeyOperator basic: %s\n", @@ -128,7 +128,7 @@ void CActiveMasternodeManager::InitInternal(const CBlockIndex* pindex) return; } - CDeterministicMNList mnList = Assert(m_dmnman)->GetListForBlock(pindex); + CDeterministicMNList mnList = m_dmnman.GetListForBlock(pindex); auto dmn = mnList.GetMNByOperatorKey(m_info.blsPubKeyOperator); if (!dmn) { @@ -177,8 +177,8 @@ void CActiveMasternodeManager::UpdatedBlockTip(const CBlockIndex* pindexNew, con const auto [cur_state, cur_protx_hash] = WITH_READ_LOCK(cs, return std::make_pair(m_state, m_info.proTxHash)); if (cur_state == MasternodeState::READY) { - auto oldMNList = Assert(m_dmnman)->GetListForBlock(pindexNew->pprev); - auto newMNList = m_dmnman->GetListForBlock(pindexNew); + auto oldMNList = m_dmnman.GetListForBlock(pindexNew->pprev); + auto newMNList = m_dmnman.GetListForBlock(pindexNew); auto reset = [this, pindexNew](MasternodeState state) -> void { LOCK(cs); m_state = state; diff --git a/src/masternode/node.h b/src/masternode/node.h index 980a2f96814d..9f8ddbaac5b2 100644 --- a/src/masternode/node.h +++ b/src/masternode/node.h @@ -30,7 +30,11 @@ struct CActiveMasternodeInfo { class CActiveMasternodeManager { -public: +private: + CConnman& m_connman; + CDeterministicMNManager& m_dmnman; + +private: enum class MasternodeState { WAITING_FOR_PROTX, POSE_BANNED, @@ -41,20 +45,16 @@ class CActiveMasternodeManager SOME_ERROR, }; -private: mutable SharedMutex cs; MasternodeState m_state GUARDED_BY(cs){MasternodeState::WAITING_FOR_PROTX}; CActiveMasternodeInfo m_info GUARDED_BY(cs); std::string m_error GUARDED_BY(cs); - CConnman& m_connman; - const std::unique_ptr& m_dmnman; - public: CActiveMasternodeManager() = delete; CActiveMasternodeManager(const CActiveMasternodeManager&) = delete; CActiveMasternodeManager& operator=(const CActiveMasternodeManager&) = delete; - explicit CActiveMasternodeManager(const CBLSSecretKey& sk, CConnman& connman, const std::unique_ptr& dmnman); + explicit CActiveMasternodeManager(CConnman& connman, CDeterministicMNManager& dmnman, const CBLSSecretKey& sk); ~CActiveMasternodeManager(); void UpdatedBlockTip(const CBlockIndex* pindexNew, const CBlockIndex* pindexFork, bool fInitialDownload) diff --git a/src/net_processing.cpp b/src/net_processing.cpp index e7598ace940b..092fed1d1624 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -587,7 +587,6 @@ class PeerManagerImpl final : public PeerManager CDSTXManager& dstxman, ChainstateManager& chainman, CTxMemPool& pool, CMasternodeMetaMan& mn_metaman, CMasternodeSync& mn_sync, CGovernanceManager& govman, CSporkManager& sporkman, - const CActiveMasternodeManager* const mn_activeman, const std::unique_ptr& active_ctx, const std::unique_ptr& dmnman, const std::unique_ptr& cj_walletman, @@ -821,8 +820,6 @@ class PeerManagerImpl final : public PeerManager CMasternodeSync& m_mn_sync; CGovernanceManager& m_govman; CSporkManager& m_sporkman; - /** Pointer to this node's CActiveMasternodeManager. May be nullptr - check existence before dereferencing. */ - const CActiveMasternodeManager* const m_mn_activeman; /** The height of the best chain */ std::atomic m_best_height{-1}; @@ -1635,7 +1632,7 @@ void PeerManagerImpl::RequestObject(NodeId nodeid, const CInv& inv, std::chrono: // Calculate the time to try requesting this transaction. Use // fPreferredDownload as a proxy for outbound peers. - std::chrono::microseconds process_time = CalculateObjectGetDataTime(inv, current_time, /*is_masternode=*/m_mn_activeman != nullptr, + std::chrono::microseconds process_time = CalculateObjectGetDataTime(inv, current_time, /*is_masternode=*/m_active_ctx != nullptr, !state->fPreferredDownload); peer_download_state.m_object_process_time.emplace(process_time, inv); @@ -2046,21 +2043,19 @@ std::unique_ptr PeerManager::make(const CChainParams& chainparams, CTxMemPool& pool, CMasternodeMetaMan& mn_metaman, CMasternodeSync& mn_sync, CGovernanceManager& govman, CSporkManager& sporkman, - const CActiveMasternodeManager* const mn_activeman, const std::unique_ptr& active_ctx, const std::unique_ptr& dmnman, const std::unique_ptr& cj_walletman, const std::unique_ptr& llmq_ctx, const std::unique_ptr& observer_ctx, bool ignore_incoming_txs) { - return std::make_unique(chainparams, connman, addrman, banman, dstxman, chainman, pool, mn_metaman, mn_sync, govman, sporkman, mn_activeman, active_ctx, dmnman, cj_walletman, llmq_ctx, observer_ctx, ignore_incoming_txs); + return std::make_unique(chainparams, connman, addrman, banman, dstxman, chainman, pool, mn_metaman, mn_sync, govman, sporkman, active_ctx, dmnman, cj_walletman, llmq_ctx, observer_ctx, ignore_incoming_txs); } PeerManagerImpl::PeerManagerImpl(const CChainParams& chainparams, CConnman& connman, AddrMan& addrman, BanMan* banman, CDSTXManager& dstxman, ChainstateManager& chainman, CTxMemPool& pool, CMasternodeMetaMan& mn_metaman, CMasternodeSync& mn_sync, CGovernanceManager& govman, CSporkManager& sporkman, - const CActiveMasternodeManager* const mn_activeman, const std::unique_ptr& active_ctx, const std::unique_ptr& dmnman, const std::unique_ptr& cj_walletman, @@ -2082,7 +2077,6 @@ PeerManagerImpl::PeerManagerImpl(const CChainParams& chainparams, CConnman& conn m_mn_sync(mn_sync), m_govman(govman), m_sporkman(sporkman), - m_mn_activeman(mn_activeman), m_ignore_incoming_txs(ignore_incoming_txs) { // While Erlay support is incomplete, it must be enabled explicitly via -txreconciliation. @@ -3714,7 +3708,7 @@ void PeerManagerImpl::ProcessMessage( LogPrint(BCLog::NET, "received: %s (%u bytes) peer=%d\n", SanitizeString(msg_type), vRecv.size(), pfrom.GetId()); ::g_stats_client->inc("message.received." + SanitizeString(msg_type), 1.0f); - const bool is_masternode = m_mn_activeman != nullptr; + const bool is_masternode = m_active_ctx != nullptr; PeerRef peer = GetPeerRef(pfrom.GetId()); if (peer == nullptr) return; @@ -3985,7 +3979,7 @@ void PeerManagerImpl::ProcessMessage( } if (is_masternode && !pfrom.m_masternode_probe_connection) { - CMNAuth::PushMNAUTH(pfrom, m_connman, *m_mn_activeman); + CMNAuth::PushMNAUTH(pfrom, m_connman, *m_active_ctx->nodeman); } // Tell our peer we prefer to receive headers rather than inv's @@ -5479,7 +5473,7 @@ void PeerManagerImpl::ProcessMessage( } } PostProcessMessage(m_sporkman.ProcessMessage(pfrom, m_connman, msg_type, vRecv), pfrom.GetId()); - PostProcessMessage(CMNAuth::ProcessMessage(pfrom, peer->m_their_services, m_connman, m_mn_metaman, m_mn_activeman, 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_active_ctx ? m_active_ctx->nodeman.get() : nullptr), m_mn_sync, m_dmnman->GetListAtChainTip(), msg_type, vRecv), pfrom.GetId()); PostProcessMessage(m_llmq_ctx->quorum_block_processor->ProcessMessage(pfrom, msg_type, vRecv), pfrom.GetId()); PostProcessMessage(m_llmq_ctx->qman->ProcessMessage(pfrom, m_connman, msg_type, vRecv), pfrom.GetId()); PostProcessMessage(ProcessPlatformBanMessage(pfrom.GetId(), msg_type, vRecv), pfrom.GetId()); @@ -5978,7 +5972,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto) assert(m_llmq_ctx); - const bool is_masternode = m_mn_activeman != nullptr; + const bool is_masternode = m_active_ctx != nullptr; PeerRef peer = GetPeerRef(pto->GetId()); if (!peer) return false; diff --git a/src/net_processing.h b/src/net_processing.h index ef10b01b4233..54795ab803c0 100644 --- a/src/net_processing.h +++ b/src/net_processing.h @@ -106,7 +106,6 @@ class PeerManager : public CValidationInterface, public NetEventsInterface, publ BanMan* banman, CDSTXManager& dstxman, ChainstateManager& chainman, CTxMemPool& pool, CMasternodeMetaMan& mn_metaman, CMasternodeSync& mn_sync, CGovernanceManager& govman, CSporkManager& sporkman, - const CActiveMasternodeManager* const mn_activeman, const std::unique_ptr& active_ctx, const std::unique_ptr& dmnman, const std::unique_ptr& cj_walletman, diff --git a/src/node/context.h b/src/node/context.h index 57710d75ba67..f2d1bdbb9722 100644 --- a/src/node/context.h +++ b/src/node/context.h @@ -82,7 +82,6 @@ struct NodeContext { std::unique_ptr scheduler; std::function rpc_interruption_point = [] {}; //! Dash managers - std::unique_ptr mn_activeman; std::unique_ptr cpoolman; std::unique_ptr cj_walletman; std::unique_ptr dstxman; diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index d3e6283eb5a9..88b7edf3bb56 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -756,7 +756,7 @@ class NodeImpl : public Node } bool isMasternode() override { - return m_context->mn_activeman != nullptr; + return m_context->active_ctx != nullptr; } bool isLoadingBlocks() override { return node::fReindex || node::fImporting; } void setNetworkActive(bool active) override diff --git a/src/rpc/coinjoin.cpp b/src/rpc/coinjoin.cpp index 3a640a4983b8..06ac8bf92cb7 100644 --- a/src/rpc/coinjoin.cpp +++ b/src/rpc/coinjoin.cpp @@ -86,7 +86,7 @@ static RPCHelpMan coinjoin_reset() const NodeContext& node = EnsureAnyNodeContext(request.context); - if (node.mn_activeman) { + if (node.active_ctx) { throw JSONRPCError(RPC_INTERNAL_ERROR, "Client-side mixing is not supported on masternodes"); } @@ -120,7 +120,7 @@ static RPCHelpMan coinjoin_start() const NodeContext& node = EnsureAnyNodeContext(request.context); - if (node.mn_activeman) { + if (node.active_ctx) { throw JSONRPCError(RPC_INTERNAL_ERROR, "Client-side mixing is not supported on masternodes"); } @@ -161,7 +161,7 @@ static RPCHelpMan coinjoin_status() const NodeContext& node = EnsureAnyNodeContext(request.context); - if (node.mn_activeman) { + if (node.active_ctx) { throw JSONRPCError(RPC_INTERNAL_ERROR, "Client-side mixing is not supported on masternodes"); } @@ -200,7 +200,7 @@ static RPCHelpMan coinjoin_stop() const NodeContext& node = EnsureAnyNodeContext(request.context); - if (node.mn_activeman) { + if (node.active_ctx) { throw JSONRPCError(RPC_INTERNAL_ERROR, "Client-side mixing is not supported on masternodes"); } @@ -464,8 +464,8 @@ static RPCHelpMan getcoinjoininfo() UniValue obj(UniValue::VOBJ); const NodeContext& node = EnsureAnyNodeContext(request.context); - if (node.mn_activeman) { - node.active_ctx->m_cj_server.GetJsonInfo(obj); + if (node.active_ctx) { + node.active_ctx->GetCJServer().GetJsonInfo(obj); return obj; } diff --git a/src/rpc/governance.cpp b/src/rpc/governance.cpp index 2ef7fa59571e..68944699468c 100644 --- a/src/rpc/governance.cpp +++ b/src/rpc/governance.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -330,12 +331,12 @@ static RPCHelpMan gobject_submit() auto mnList = CHECK_NONFATAL(node.dmnman)->GetListAtChainTip(); - if (node.mn_activeman) { - const bool fMnFound = mnList.HasValidMNByCollateral(node.mn_activeman->GetOutPoint()); - + if (node.active_ctx) { + const auto& mn_activeman{*node.active_ctx->nodeman}; + const bool fMnFound = mnList.HasValidMNByCollateral(mn_activeman.GetOutPoint()); LogPrint(BCLog::GOBJECT, /* Continued */ "gobject_submit -- pubKeyOperator = %s, outpoint = %s, params.size() = %lld, fMnFound = %d\n", - node.mn_activeman->GetPubKey().ToString(false), node.mn_activeman->GetOutPoint().ToStringShort(), + mn_activeman.GetPubKey().ToString(false), mn_activeman.GetOutPoint().ToStringShort(), request.params.size(), fMnFound); } else { LogPrint(BCLog::GOBJECT, "gobject_submit -- pubKeyOperator = N/A, outpoint = N/A, params.size() = %lld, fMnFound = %d\n", diff --git a/src/rpc/masternode.cpp b/src/rpc/masternode.cpp index 7d883177fdcc..43e3efbe54a3 100644 --- a/src/rpc/masternode.cpp +++ b/src/rpc/masternode.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -198,15 +199,16 @@ static RPCHelpMan masternode_status() { const NodeContext& node = EnsureAnyNodeContext(request.context); - if (!node.mn_activeman) { + if (!node.active_ctx) { throw JSONRPCError(RPC_INTERNAL_ERROR, "This node does not run an active masternode."); } + const auto& mn_activeman{*node.active_ctx->nodeman}; UniValue mnObj(UniValue::VOBJ); // keep compatibility with legacy status for now (TODO: get deprecated/removed later) - mnObj.pushKV("outpoint", node.mn_activeman->GetOutPoint().ToStringShort()); - mnObj.pushKV("service", node.mn_activeman->GetService().ToStringAddrPort()); - auto dmn = CHECK_NONFATAL(node.dmnman)->GetListAtChainTip().GetMN(node.mn_activeman->GetProTxHash()); + mnObj.pushKV("outpoint", mn_activeman.GetOutPoint().ToStringShort()); + mnObj.pushKV("service", mn_activeman.GetService().ToStringAddrPort()); + auto dmn = CHECK_NONFATAL(node.dmnman)->GetListAtChainTip().GetMN(mn_activeman.GetProTxHash()); if (dmn) { mnObj.pushKV("proTxHash", dmn->proTxHash.ToString()); mnObj.pushKV("type", std::string(GetMnType(dmn->nType).description)); @@ -214,8 +216,8 @@ static RPCHelpMan masternode_status() mnObj.pushKV("collateralIndex", dmn->collateralOutpoint.n); mnObj.pushKV("dmnState", dmn->pdmnState->ToJson(dmn->nType)); } - mnObj.pushKV("state", node.mn_activeman->GetStateString()); - mnObj.pushKV("status", node.mn_activeman->GetStatus()); + mnObj.pushKV("state", mn_activeman.GetStateString()); + mnObj.pushKV("status", mn_activeman.GetStatus()); return mnObj; }, diff --git a/src/rpc/quorums.cpp b/src/rpc/quorums.cpp index 23474c50f6b4..d0b0be2103f5 100644 --- a/src/rpc/quorums.cpp +++ b/src/rpc/quorums.cpp @@ -359,7 +359,7 @@ static RPCHelpMan quorum_dkgstatus() const CConnman& connman = EnsureConnman(node); const CBlockIndex* const pindexTip = WITH_LOCK(cs_main, return chainman.ActiveChain().Tip()); const int tipHeight = pindexTip->nHeight; - const uint256 proTxHash = node.mn_activeman ? node.mn_activeman->GetProTxHash() : uint256{}; + const uint256 proTxHash = node.active_ctx ? node.active_ctx->nodeman->GetProTxHash() : uint256{}; for (const auto& type : llmq::GetEnabledQuorumTypes(chainman, pindexTip)) { const auto llmq_params_opt = Params().GetLLMQ(type); CHECK_NONFATAL(llmq_params_opt.has_value()); @@ -372,7 +372,7 @@ static RPCHelpMan quorum_dkgstatus() obj.pushKV("llmqType", std::string(llmq_params.name)); obj.pushKV("quorumIndex", quorumIndex); - if (node.mn_activeman) { + if (node.active_ctx) { int quorumHeight = tipHeight - (tipHeight % llmq_params.dkgInterval) + quorumIndex; if (quorumHeight <= tipHeight) { const CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(cs_main, return chainman.ActiveChain()[quorumHeight]); @@ -501,7 +501,7 @@ static RPCHelpMan quorum_memberof() static UniValue quorum_sign_helper(const JSONRPCRequest& request, Consensus::LLMQType llmqType) { const NodeContext& node = EnsureAnyNodeContext(request.context); - if (!node.mn_activeman) { + if (!node.active_ctx) { throw JSONRPCError(RPC_INTERNAL_ERROR, "Only available in masternode mode."); } diff --git a/src/test/coinjoin_queue_tests.cpp b/src/test/coinjoin_queue_tests.cpp index fda668146477..350dd3889861 100644 --- a/src/test/coinjoin_queue_tests.cpp +++ b/src/test/coinjoin_queue_tests.cpp @@ -11,7 +11,7 @@ #include -BOOST_FIXTURE_TEST_SUITE(coinjoin_queue_tests, BasicTestingSetup) +BOOST_FIXTURE_TEST_SUITE(coinjoin_queue_tests, TestingSetup) static CBLSSecretKey MakeSecretKey() { @@ -24,7 +24,7 @@ static CBLSSecretKey MakeSecretKey() BOOST_AUTO_TEST_CASE(queue_sign_and_verify) { // Build active MN manager with operator key using node context wiring - CActiveMasternodeManager mn_activeman(MakeSecretKey(), *Assert(m_node.connman), m_node.dmnman); + CActiveMasternodeManager mn_activeman(*Assert(m_node.connman), *Assert(m_node.dmnman), MakeSecretKey()); CCoinJoinQueue q; q.nDenom = CoinJoin::AmountToDenomination(CoinJoin::GetSmallestDenomination()); diff --git a/src/test/denialofservice_tests.cpp b/src/test/denialofservice_tests.cpp index cc45a34c8ca8..ae2326cbef10 100644 --- a/src/test/denialofservice_tests.cpp +++ b/src/test/denialofservice_tests.cpp @@ -148,7 +148,7 @@ BOOST_AUTO_TEST_CASE(stale_tip_peer_management) NodeId id{0}; const CChainParams& chainparams = Params(); auto connman = std::make_unique(0x1337, 0x1337, *m_node.addrman, *m_node.netgroupman); - auto peerLogic = MakePeerManager(*connman, m_node, /*banman=*/nullptr, /*mn_activeman=*/nullptr, chainparams, /*ignore_incoming_txs=*/false); + auto peerLogic = MakePeerManager(*connman, m_node, /*banman=*/nullptr, chainparams, /*ignore_incoming_txs=*/false); constexpr int max_outbound_full_relay = MAX_OUTBOUND_FULL_RELAY_CONNECTIONS; CConnman::Options options; @@ -249,7 +249,7 @@ BOOST_AUTO_TEST_CASE(block_relay_only_eviction) NodeId id{0}; const CChainParams& chainparams = Params(); auto connman = std::make_unique(0x1337, 0x1337, *m_node.addrman, *m_node.netgroupman); - auto peerLogic = MakePeerManager(*connman, m_node, /*banman=*/nullptr, /*mn_activeman=*/nullptr, chainparams, /*ignore_incoming_txs=*/false); + auto peerLogic = MakePeerManager(*connman, m_node, /*banman=*/nullptr, chainparams, /*ignore_incoming_txs=*/false); constexpr int max_outbound_block_relay{MAX_BLOCK_RELAY_ONLY_CONNECTIONS}; constexpr int64_t MINIMUM_CONNECT_TIME{30}; @@ -313,7 +313,7 @@ BOOST_AUTO_TEST_CASE(peer_discouragement) const CChainParams& chainparams = Params(); auto banman = std::make_unique(m_args.GetDataDirBase() / "banlist", nullptr, DEFAULT_MISBEHAVING_BANTIME); auto connman = std::make_unique(0x1337, 0x1337, *m_node.addrman, *m_node.netgroupman); - auto peerLogic = MakePeerManager(*connman, m_node, banman.get(), /*mn_activeman=*/nullptr, chainparams, /*ignore_incoming_txs=*/false); + auto peerLogic = MakePeerManager(*connman, m_node, banman.get(), chainparams, /*ignore_incoming_txs=*/false); CNetAddr tor_netaddr; BOOST_REQUIRE( @@ -415,7 +415,7 @@ BOOST_AUTO_TEST_CASE(DoS_bantime) const CChainParams& chainparams = Params(); auto banman = std::make_unique(m_args.GetDataDirBase() / "banlist", nullptr, DEFAULT_MISBEHAVING_BANTIME); auto connman = std::make_unique(0x1337, 0x1337, *m_node.addrman, *m_node.netgroupman); - auto peerLogic = MakePeerManager(*connman, m_node, banman.get(), /*mn_activeman=*/nullptr, chainparams, /*ignore_incoming_txs=*/false); + auto peerLogic = MakePeerManager(*connman, m_node, banman.get(), chainparams, /*ignore_incoming_txs=*/false); banman->ClearBanned(); int64_t nStartTime = GetTime(); diff --git a/src/test/net_peer_connection_tests.cpp b/src/test/net_peer_connection_tests.cpp index 5c981fb2ebb1..f5e5001e822c 100644 --- a/src/test/net_peer_connection_tests.cpp +++ b/src/test/net_peer_connection_tests.cpp @@ -85,7 +85,7 @@ BOOST_AUTO_TEST_CASE(test_addnode_getaddednodeinfo_and_connection_detection) { const auto& chainparams = Params(); auto connman = std::make_unique(0x1337, 0x1337, *m_node.addrman, *m_node.netgroupman); - auto peerman = MakePeerManager(*connman, m_node, /*banman=*/nullptr, /*mn_activeman=*/nullptr, chainparams, /*ignore_incoming_txs=*/false); + auto peerman = MakePeerManager(*connman, m_node, /*banman=*/nullptr, chainparams, /*ignore_incoming_txs=*/false); NodeId id{0}; std::vector nodes; diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index e9bf4b28d03b..53ad7bc31f23 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -129,12 +129,11 @@ std::ostream& operator<<(std::ostream& os, const uint256& num) std::unique_ptr MakePeerManager(CConnman& connman, NodeContext& node, BanMan* banman, - CActiveMasternodeManager* mn_activeman, const CChainParams& chainparams, bool ignore_incoming_txs) { return PeerManager::make(chainparams, connman, *node.addrman, banman, *node.dstxman, *node.chainman, *node.mempool, *node.mn_metaman, - *node.mn_sync, *node.govman, *node.sporkman, mn_activeman, node.active_ctx, node.dmnman, node.cj_walletman, + *node.mn_sync, *node.govman, *node.sporkman, node.active_ctx, node.dmnman, node.cj_walletman, node.llmq_ctx, node.observer_ctx, ignore_incoming_txs); } @@ -380,7 +379,7 @@ TestingSetup::TestingSetup(const std::string& chainName, const std::vector(m_args.GetDataDirBase() / "banlist", nullptr, DEFAULT_MISBEHAVING_BANTIME); - m_node.peerman = MakePeerManager(*m_node.connman, m_node, m_node.banman.get(), /*mn_activeman=*/nullptr, chainparams, + m_node.peerman = MakePeerManager(*m_node.connman, m_node, m_node.banman.get(), chainparams, /*ignore_incoming_txs=*/false); { CConnman::Options options; diff --git a/src/test/util/setup_common.h b/src/test/util/setup_common.h index 114699d68a72..74f45605078a 100644 --- a/src/test/util/setup_common.h +++ b/src/test/util/setup_common.h @@ -89,7 +89,6 @@ static constexpr CAmount CENT{1000000}; std::unique_ptr MakePeerManager(CConnman& connman, node::NodeContext& node, BanMan* banman, - CActiveMasternodeManager* mn_activeman, const CChainParams& chainparams, bool ignore_incoming_txs); void DashChainstateSetup(ChainstateManager& chainman, From f0468e6b805fe61cb3e9eae586f8fcb8f7a407a7 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Sun, 14 Dec 2025 01:03:52 +0530 Subject: [PATCH 09/14] refactor: merge `CActiveMasternodeInfo` into `CActiveMasternodeManager` Also: - Drop stale TODO, remove headers in code that no longer refer to masternode capabilities --- src/llmq/dkgsession.cpp | 1 - src/masternode/node.cpp | 35 ++++++++++++++++++----------------- src/masternode/node.h | 28 +++++++++------------------- src/node/context.cpp | 1 - 4 files changed, 27 insertions(+), 38 deletions(-) diff --git a/src/llmq/dkgsession.cpp b/src/llmq/dkgsession.cpp index 23fe49c3be08..fba7e8b38d39 100644 --- a/src/llmq/dkgsession.cpp +++ b/src/llmq/dkgsession.cpp @@ -12,7 +12,6 @@ #include #include #include -#include #include #include diff --git a/src/masternode/node.cpp b/src/masternode/node.cpp index 6ff91fe37e88..9d3b298a8f26 100644 --- a/src/masternode/node.cpp +++ b/src/masternode/node.cpp @@ -53,12 +53,13 @@ CActiveMasternodeManager::CActiveMasternodeManager(CConnman& connman, CDetermini const CBLSSecretKey& sk) : m_connman{connman}, m_dmnman{dmnman}, - m_info{sk, sk.GetPublicKey()} + m_operator_pk{sk.GetPublicKey()}, + m_operator_sk{sk} { assert(sk.IsValid()); /* We can assume pk is valid if sk is valid */ LogPrintf("MASTERNODE:\n blsPubKeyOperator legacy: %s\n blsPubKeyOperator basic: %s\n", - m_info.blsPubKeyOperator.ToString(/*specificLegacyScheme=*/ true), - m_info.blsPubKeyOperator.ToString(/*specificLegacyScheme=*/ false)); + m_operator_pk.ToString(/*specificLegacyScheme=*/true), + m_operator_pk.ToString(/*specificLegacyScheme=*/false)); } CActiveMasternodeManager::~CActiveMasternodeManager() = default; @@ -123,14 +124,14 @@ void CActiveMasternodeManager::InitInternal(const CBlockIndex* pindex) return; } - if (!GetLocalAddress(m_info.service)) { + if (!GetLocalAddress(m_service)) { m_state = MasternodeState::SOME_ERROR; return; } CDeterministicMNList mnList = m_dmnman.GetListForBlock(pindex); - auto dmn = mnList.GetMNByOperatorKey(m_info.blsPubKeyOperator); + auto dmn = mnList.GetMNByOperatorKey(m_operator_pk); if (!dmn) { // MN not appeared on the chain yet return; @@ -147,7 +148,7 @@ void CActiveMasternodeManager::InitInternal(const CBlockIndex* pindex) LogPrintf("CActiveMasternodeManager::Init -- proTxHash=%s, proTx=%s\n", dmn->proTxHash.ToString(), dmn->ToString()); - if (m_info.service != dmn->pdmnState->netInfo->GetPrimary()) { + if (m_service != dmn->pdmnState->netInfo->GetPrimary()) { m_state = MasternodeState::SOME_ERROR; m_error = "Local address does not match the address from ProTx"; LogPrintf("CActiveMasternodeManager::Init -- ERROR: %s\n", m_error); @@ -155,19 +156,19 @@ void CActiveMasternodeManager::InitInternal(const CBlockIndex* pindex) } // Check socket connectivity - LogPrintf("CActiveMasternodeManager::Init -- Checking inbound connection to '%s'\n", m_info.service.ToStringAddrPort()); - std::unique_ptr sock{ConnectDirectly(m_info.service, /*manual_connection=*/true)}; + LogPrintf("CActiveMasternodeManager::Init -- Checking inbound connection to '%s'\n", m_service.ToStringAddrPort()); + std::unique_ptr sock{ConnectDirectly(m_service, /*manual_connection=*/true)}; bool fConnected{sock && sock->IsSelectable(/*is_select=*/::g_socket_events_mode == SocketEventsMode::Select)}; sock = std::make_unique(INVALID_SOCKET); if (!fConnected && Params().RequireRoutableExternalIP()) { m_state = MasternodeState::SOME_ERROR; - m_error = "Could not connect to " + m_info.service.ToStringAddrPort(); + m_error = "Could not connect to " + m_service.ToStringAddrPort(); LogPrintf("CActiveMasternodeManager::Init -- ERROR: %s\n", m_error); return; } - m_info.proTxHash = dmn->proTxHash; - m_info.outpoint = dmn->collateralOutpoint; + m_protx_hash = dmn->proTxHash; + m_outpoint = dmn->collateralOutpoint; m_state = MasternodeState::READY; } @@ -175,15 +176,15 @@ void CActiveMasternodeManager::UpdatedBlockTip(const CBlockIndex* pindexNew, con { if (!DeploymentDIP0003Enforced(pindexNew->nHeight, Params().GetConsensus())) return; - const auto [cur_state, cur_protx_hash] = WITH_READ_LOCK(cs, return std::make_pair(m_state, m_info.proTxHash)); + const auto [cur_state, cur_protx_hash] = WITH_READ_LOCK(cs, return std::make_pair(m_state, m_protx_hash)); if (cur_state == MasternodeState::READY) { auto oldMNList = m_dmnman.GetListForBlock(pindexNew->pprev); auto newMNList = m_dmnman.GetListForBlock(pindexNew); auto reset = [this, pindexNew](MasternodeState state) -> void { LOCK(cs); m_state = state; - m_info.proTxHash = uint256(); - m_info.outpoint.SetNull(); + m_protx_hash = uint256(); + m_outpoint.SetNull(); // MN might have reappeared in same block with a new ProTx InitInternal(pindexNew); }; @@ -266,7 +267,7 @@ template