From b90e804ba6beaf38e205bf173a490023501ff687 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Sat, 2 May 2026 21:00:13 +0700 Subject: [PATCH 01/12] refactor: avoid including dkgsessionhandler in the dkgsessionmgr.h It shows the hidden circular dependency and tidy up list of includes --- src/llmq/dkgsessionmgr.h | 13 +++++++++---- src/llmq/observer.cpp | 1 + 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/src/llmq/dkgsessionmgr.h b/src/llmq/dkgsessionmgr.h index 6bd35cebc550..934e02503364 100644 --- a/src/llmq/dkgsessionmgr.h +++ b/src/llmq/dkgsessionmgr.h @@ -8,9 +8,10 @@ #include #include #include +#include #include -#include #include +#include #include #include @@ -23,16 +24,15 @@ class CBLSIESEncryptedObject; class CActiveMasternodeManager; class CBlockIndex; +class CConnman; class CDBWrapper; class CDeterministicMNManager; -class CDKGDebugManager; class ChainstateManager; +class CNode; class CMasternodeMetaMan; class CSporkManager; class PeerManager; class CInv; -class CNode; -class CConnman; struct MessageProcessingResult; namespace util { struct DbWrapperParams; @@ -48,6 +48,11 @@ class CDKGJustification; class CDKGPrematureCommitment; class CDKGSessionHandler; class CQuorumSnapshotManager; +class CDKGSessionHandler; +class CDKGContribution; +class CDKGComplaint; +class CDKGJustification; +class CDKGPrematureCommitment; class CDKGSessionManager { diff --git a/src/llmq/observer.cpp b/src/llmq/observer.cpp index bb9bc3f0bcd3..5adaa8326bf0 100644 --- a/src/llmq/observer.cpp +++ b/src/llmq/observer.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include From be26a68d23e0dfe89947484b0f8ebc1a9cb62b03 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Sun, 3 May 2026 03:50:52 +0700 Subject: [PATCH 02/12] refactor: make DkgSession's stages to return std::optional --- src/active/dkgsession.cpp | 64 ++++++++++++++++---------------- src/active/dkgsession.h | 19 ++++------ src/active/dkgsessionhandler.cpp | 22 +++++++++-- src/llmq/dkgsession.h | 28 ++++++++------ 4 files changed, 75 insertions(+), 58 deletions(-) diff --git a/src/active/dkgsession.cpp b/src/active/dkgsession.cpp index 00f037f8088b..8a66f74766bd 100644 --- a/src/active/dkgsession.cpp +++ b/src/active/dkgsession.cpp @@ -36,10 +36,10 @@ ActiveDKGSession::ActiveDKGSession(CBLSWorker& bls_worker, CDeterministicMNManag ActiveDKGSession::~ActiveDKGSession() = default; -void ActiveDKGSession::Contribute(CDKGPendingMessages& pendingMessages, PeerManager& peerman) +std::optional ActiveDKGSession::Contribute() { if (!AreWeMember()) { - return; + return std::nullopt; } assert(params.threshold > 1); // we should not get there with single-node-quorums @@ -51,15 +51,15 @@ void ActiveDKGSession::Contribute(CDKGPendingMessages& pendingMessages, PeerMana if (!blsWorker.GenerateContributions(params.threshold, memberIds, vvecContribution, m_sk_contributions)) { // this should never happen actually logger.Batch("GenerateContributions failed"); - return; + return std::nullopt; } logger.Batch("generated contributions. time=%d", t1.count()); logger.Flush(); - SendContributions(pendingMessages, peerman); + return SendContributions(); } -void ActiveDKGSession::SendContributions(CDKGPendingMessages& pendingMessages, PeerManager& peerman) +std::optional ActiveDKGSession::SendContributions() { CDKGLogger logger(*this, __func__, __LINE__); @@ -69,7 +69,7 @@ void ActiveDKGSession::SendContributions(CDKGPendingMessages& pendingMessages, P if (ShouldSimulateError(DKGError::type::CONTRIBUTION_OMIT)) { logger.Batch("omitting"); - return; + return std::nullopt; } CDKGContribution qc; @@ -93,7 +93,7 @@ void ActiveDKGSession::SendContributions(CDKGPendingMessages& pendingMessages, P 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; + return std::nullopt; } } @@ -108,7 +108,7 @@ void ActiveDKGSession::SendContributions(CDKGPendingMessages& pendingMessages, P return true; }); - pendingMessages.PushPendingMessage(-1, qc, peerman); + return qc; } // Verifies all pending secret key contributions in one batch @@ -170,10 +170,10 @@ void ActiveDKGSession::VerifyPendingContributions() pendingContributionVerifications.clear(); } -void ActiveDKGSession::VerifyAndComplain(CConnman& connman, CDKGPendingMessages& pendingMessages, PeerManager& peerman) +std::optional ActiveDKGSession::VerifyAndComplain(CConnman& connman) { if (!AreWeMember()) { - return; + return std::nullopt; } { @@ -208,7 +208,7 @@ void ActiveDKGSession::VerifyAndComplain(CConnman& connman, CDKGPendingMessages& VerifyConnectionAndMinProtoVersions(connman); - SendComplaint(pendingMessages, peerman); + return SendComplaint(); } void ActiveDKGSession::VerifyConnectionAndMinProtoVersions(CConnman& connman) const @@ -255,7 +255,7 @@ void ActiveDKGSession::VerifyConnectionAndMinProtoVersions(CConnman& connman) co } } -void ActiveDKGSession::SendComplaint(CDKGPendingMessages& pendingMessages, PeerManager& peerman) +std::optional ActiveDKGSession::SendComplaint() { CDKGLogger logger(*this, __func__, __LINE__); @@ -280,7 +280,7 @@ void ActiveDKGSession::SendComplaint(CDKGPendingMessages& pendingMessages, PeerM } if (badCount == 0 && complaintCount == 0) { - return; + return std::nullopt; } logger.Batch("sending complaint. badCount=%d, complaintCount=%d", badCount, complaintCount); @@ -294,13 +294,13 @@ void ActiveDKGSession::SendComplaint(CDKGPendingMessages& pendingMessages, PeerM return true; }); - pendingMessages.PushPendingMessage(-1, qc, peerman); + return qc; } -void ActiveDKGSession::VerifyAndJustify(CDKGPendingMessages& pendingMessages, PeerManager& peerman) +std::optional ActiveDKGSession::VerifyAndJustify() { if (!AreWeMember()) { - return; + return std::nullopt; } CDKGLogger logger(*this, __func__, __LINE__); @@ -333,13 +333,13 @@ void ActiveDKGSession::VerifyAndJustify(CDKGPendingMessages& pendingMessages, Pe } logger.Flush(); - if (!justifyFor.empty()) { - SendJustification(pendingMessages, peerman, justifyFor); + if (justifyFor.empty()) { + return std::nullopt; } + return SendJustification(justifyFor); } -void ActiveDKGSession::SendJustification(CDKGPendingMessages& pendingMessages, PeerManager& peerman, - const Uint256HashSet& forMembers) +std::optional ActiveDKGSession::SendJustification(const Uint256HashSet& forMembers) { CDKGLogger logger(*this, __func__, __LINE__); @@ -372,7 +372,7 @@ void ActiveDKGSession::SendJustification(CDKGPendingMessages& pendingMessages, P if (ShouldSimulateError(DKGError::type::JUSTIFY_OMIT)) { logger.Batch("omitting"); - return; + return std::nullopt; } qj.sig = m_mn_activeman.Sign(qj.GetSignHash(), m_use_legacy_bls); @@ -384,13 +384,13 @@ void ActiveDKGSession::SendJustification(CDKGPendingMessages& pendingMessages, P return true; }); - pendingMessages.PushPendingMessage(-1, qj, peerman); + return qj; } -void ActiveDKGSession::VerifyAndCommit(CDKGPendingMessages& pendingMessages, PeerManager& peerman) +std::optional ActiveDKGSession::VerifyAndCommit() { if (!AreWeMember()) { - return; + return std::nullopt; } CDKGLogger logger(*this, __func__, __LINE__); @@ -429,10 +429,10 @@ void ActiveDKGSession::VerifyAndCommit(CDKGPendingMessages& pendingMessages, Pee logger.Flush(); - SendCommitment(pendingMessages, peerman); + return SendCommitment(); } -void ActiveDKGSession::SendCommitment(CDKGPendingMessages& pendingMessages, PeerManager& peerman) +std::optional ActiveDKGSession::SendCommitment() { CDKGLogger logger(*this, __func__, __LINE__); @@ -454,12 +454,12 @@ void ActiveDKGSession::SendCommitment(CDKGPendingMessages& pendingMessages, Peer if (qc.CountValidMembers() < params.minSize) { logger.Batch("not enough valid members. not sending commitment"); - return; + return std::nullopt; } if (ShouldSimulateError(DKGError::type::COMMIT_OMIT)) { logger.Batch("omitting"); - return; + return std::nullopt; } cxxtimer::Timer timerTotal(true); @@ -470,13 +470,13 @@ void ActiveDKGSession::SendCommitment(CDKGPendingMessages& pendingMessages, Peer 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; + return std::nullopt; } BLSVerificationVectorPtr vvec = cache.BuildQuorumVerificationVector(::SerializeHash(memberIndexes), vvecs); if (vvec == nullptr) { logger.Batch("failed to build quorum verification vector"); - return; + return std::nullopt; } t1.stop(); @@ -484,7 +484,7 @@ void ActiveDKGSession::SendCommitment(CDKGPendingMessages& pendingMessages, Peer CBLSSecretKey skShare = cache.AggregateSecretKeys(::SerializeHash(memberIndexes), skContributions); if (!skShare.IsValid()) { logger.Batch("failed to build own secret share"); - return; + return std::nullopt; } t2.stop(); @@ -541,7 +541,7 @@ void ActiveDKGSession::SendCommitment(CDKGPendingMessages& pendingMessages, Peer return true; }); - pendingMessages.PushPendingMessage(-1, qc, peerman); + return qc; } std::vector ActiveDKGSession::FinalizeCommitments() diff --git a/src/active/dkgsession.h b/src/active/dkgsession.h index 3350a06c6b2d..f14d3239694d 100644 --- a/src/active/dkgsession.h +++ b/src/active/dkgsession.h @@ -29,25 +29,22 @@ class ActiveDKGSession final : public llmq::CDKGSession public: // Phase 1: contribution - void Contribute(CDKGPendingMessages& pendingMessages, PeerManager& peerman) override; - void SendContributions(CDKGPendingMessages& pendingMessages, PeerManager& peerman) override; + std::optional Contribute() override; + std::optional SendContributions() 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); + std::optional VerifyAndComplain(CConnman& connman) override EXCLUSIVE_LOCKS_REQUIRED(!cs_pending); void VerifyConnectionAndMinProtoVersions(CConnman& connman) const override; - void SendComplaint(CDKGPendingMessages& pendingMessages, PeerManager& peerman) override; + std::optional SendComplaint() override; // Phase 3: justification - void VerifyAndJustify(CDKGPendingMessages& pendingMessages, PeerManager& peerman) override - EXCLUSIVE_LOCKS_REQUIRED(!invCs); - void SendJustification(CDKGPendingMessages& pendingMessages, PeerManager& peerman, - const Uint256HashSet& forMembers) override; + std::optional VerifyAndJustify() override EXCLUSIVE_LOCKS_REQUIRED(!invCs); + std::optional SendJustification(const Uint256HashSet& forMembers) override; // Phase 4: commit - void VerifyAndCommit(CDKGPendingMessages& pendingMessages, PeerManager& peerman) override; - void SendCommitment(CDKGPendingMessages& pendingMessages, PeerManager& peerman) override; + std::optional VerifyAndCommit() override; + std::optional SendCommitment() override; // Phase 5: aggregate/finalize std::vector FinalizeCommitments() EXCLUSIVE_LOCKS_REQUIRED(!invCs) override; diff --git a/src/active/dkgsessionhandler.cpp b/src/active/dkgsessionhandler.cpp index 9b796d796516..65b756b2143d 100644 --- a/src/active/dkgsessionhandler.cpp +++ b/src/active/dkgsessionhandler.cpp @@ -471,7 +471,11 @@ void ActiveDKGSessionHandler::HandleDKGRound(CConnman& connman, PeerManager& pee WaitForNextPhase(QuorumPhase::Initialized, QuorumPhase::Contribute, curQuorumHash); // Contribute - auto fContributeStart = [this, &peerman]() { curSession->Contribute(pendingContributions, peerman); }; + auto fContributeStart = [this, &peerman]() { + if (auto qc = curSession->Contribute(); qc) { + pendingContributions.PushPendingMessage(-1, *qc, peerman); + } + }; auto fContributeWait = [this, &connman, &peerman] { return ProcessPendingMessageBatch(connman, *curSession, pendingContributions, peerman, 8); }; @@ -479,7 +483,9 @@ void ActiveDKGSessionHandler::HandleDKGRound(CConnman& connman, PeerManager& pee // Complain auto fComplainStart = [this, &connman, &peerman]() { - curSession->VerifyAndComplain(connman, pendingComplaints, peerman); + if (auto qc = curSession->VerifyAndComplain(connman); qc) { + pendingComplaints.PushPendingMessage(-1, *qc, peerman); + } }; auto fComplainWait = [this, &connman, &peerman] { return ProcessPendingMessageBatch(connman, *curSession, pendingComplaints, peerman, 8); @@ -487,14 +493,22 @@ void ActiveDKGSessionHandler::HandleDKGRound(CConnman& connman, PeerManager& pee HandlePhase(QuorumPhase::Complain, QuorumPhase::Justify, curQuorumHash, 0.05, fComplainStart, fComplainWait); // Justify - auto fJustifyStart = [this, &peerman]() { curSession->VerifyAndJustify(pendingJustifications, peerman); }; + auto fJustifyStart = [this, &peerman]() { + if (auto qj = curSession->VerifyAndJustify(); qj) { + pendingJustifications.PushPendingMessage(-1, *qj, 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 fCommitStart = [this, &peerman]() { + if (auto qc = curSession->VerifyAndCommit(); qc) { + pendingPrematureCommitments.PushPendingMessage(-1, *qc, peerman); + } + }; auto fCommitWait = [this, &connman, &peerman] { return ProcessPendingMessageBatch(connman, *curSession, pendingPrematureCommitments, peerman, 8); diff --git a/src/llmq/dkgsession.h b/src/llmq/dkgsession.h index abe65f6b5e84..d3ca9a934e1f 100644 --- a/src/llmq/dkgsession.h +++ b/src/llmq/dkgsession.h @@ -27,12 +27,10 @@ class CConnman; class CDeterministicMN; class CMasternodeMetaMan; class CSporkManager; -class PeerManager; namespace llmq { class ActiveDKGSession; class ActiveDKGSessionHandler; class CDKGDebugManager; -class CDKGPendingMessages; class CDKGSession; class CDKGSessionManager; class CFinalCommitment; @@ -361,29 +359,37 @@ class CDKGSession */ // Phase 1: contribution - virtual void Contribute(CDKGPendingMessages& pendingMessages, PeerManager& peerman) {} - virtual void SendContributions(CDKGPendingMessages& pendingMessages, PeerManager& peerman) {} + virtual std::optional Contribute() { return std::nullopt; } + virtual std::optional SendContributions() { return std::nullopt; } bool PreVerifyMessage(const CDKGContribution& qc, bool& retBan) const; std::optional ReceiveMessage(const CDKGContribution& qc) EXCLUSIVE_LOCKS_REQUIRED(!invCs, !cs_pending); virtual void VerifyPendingContributions() EXCLUSIVE_LOCKS_REQUIRED(cs_pending) {} // Phase 2: complaint - virtual void VerifyAndComplain(CConnman& connman, CDKGPendingMessages& pendingMessages, PeerManager& peerman) - EXCLUSIVE_LOCKS_REQUIRED(!cs_pending) {} + virtual std::optional VerifyAndComplain(CConnman& connman) EXCLUSIVE_LOCKS_REQUIRED(!cs_pending) + { + return std::nullopt; + } virtual void VerifyConnectionAndMinProtoVersions(CConnman& connman) const {} - virtual void SendComplaint(CDKGPendingMessages& pendingMessages, PeerManager& peerman) {} + virtual std::optional SendComplaint() { return std::nullopt; } bool PreVerifyMessage(const CDKGComplaint& qc, bool& retBan) const; std::optional ReceiveMessage(const CDKGComplaint& qc) EXCLUSIVE_LOCKS_REQUIRED(!invCs); // Phase 3: justification - virtual void VerifyAndJustify(CDKGPendingMessages& pendingMessages, PeerManager& peerman) EXCLUSIVE_LOCKS_REQUIRED(!invCs) {} - virtual void SendJustification(CDKGPendingMessages& pendingMessages, PeerManager& peerman, const Uint256HashSet& forMembers) {} + virtual std::optional VerifyAndJustify() EXCLUSIVE_LOCKS_REQUIRED(!invCs) + { + return std::nullopt; + } + virtual std::optional SendJustification(const Uint256HashSet& forMembers) + { + return std::nullopt; + } bool PreVerifyMessage(const CDKGJustification& qj, bool& retBan) const; std::optional ReceiveMessage(const CDKGJustification& qj) EXCLUSIVE_LOCKS_REQUIRED(!invCs); // Phase 4: commit - virtual void VerifyAndCommit(CDKGPendingMessages& pendingMessages, PeerManager& peerman) {} - virtual void SendCommitment(CDKGPendingMessages& pendingMessages, PeerManager& peerman) {} + virtual std::optional VerifyAndCommit() { return std::nullopt; } + virtual std::optional SendCommitment() { return std::nullopt; } bool PreVerifyMessage(const CDKGPrematureCommitment& qc, bool& retBan) const; std::optional ReceiveMessage(const CDKGPrematureCommitment& qc) EXCLUSIVE_LOCKS_REQUIRED(!invCs); From cebb20e11f843e4cfbc20a44cd5b3b1e25375bed Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Mon, 4 May 2026 14:35:01 +0700 Subject: [PATCH 03/12] feat: add a new helper PeerPushInventory to PeerManagerInternal --- src/net_processing.cpp | 6 ++++++ src/net_processing.h | 1 + 2 files changed, 7 insertions(+) diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 3f8ba2e11b48..74ff49313533 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -655,6 +655,7 @@ class PeerManagerImpl final : public PeerManager void PeerMisbehaving(const NodeId pnode, const int howmuch, const std::string& message = "") override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex); bool PeerIsBanned(const NodeId node_id) override EXCLUSIVE_LOCKS_REQUIRED(cs_main, !m_peer_mutex); void PeerEraseObjectRequest(const NodeId nodeid, const CInv& inv) override EXCLUSIVE_LOCKS_REQUIRED(::cs_main); + void PeerPushInventory(NodeId nodeid, const CInv& inv) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex); void PeerRelayInv(const CInv& inv) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex); void PeerRelayInvFiltered(const CInv& inv, const CTransaction& relatedTx) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex); void PeerRelayInvFiltered(const CInv& inv, const uint256& relatedTxHash) override EXCLUSIVE_LOCKS_REQUIRED(!m_peer_mutex); @@ -6641,6 +6642,11 @@ void PeerManagerImpl::PeerEraseObjectRequest(const NodeId nodeid, const CInv& in EraseObjectRequest(nodeid, inv); } +void PeerManagerImpl::PeerPushInventory(NodeId nodeid, const CInv& inv) +{ + PushInventory(nodeid, inv); +} + void PeerManagerImpl::PeerRelayInv(const CInv& inv) { RelayInv(inv); diff --git a/src/net_processing.h b/src/net_processing.h index 4967444fe346..285a660c164d 100644 --- a/src/net_processing.h +++ b/src/net_processing.h @@ -68,6 +68,7 @@ class PeerManagerInternal virtual void PeerMisbehaving(const NodeId pnode, const int howmuch, const std::string& message = "") = 0; virtual bool PeerIsBanned(const NodeId node_id) = 0; virtual void PeerEraseObjectRequest(const NodeId nodeid, const CInv& inv) = 0; + virtual void PeerPushInventory(NodeId nodeid, const CInv& inv) = 0; virtual void PeerRelayInv(const CInv& inv) = 0; virtual void PeerRelayInvFiltered(const CInv& inv, const CTransaction& relatedTx) = 0; virtual void PeerRelayInvFiltered(const CInv& inv, const uint256& relatedTxHash) = 0; From 1fc0a5007b1901c89f87e3aa97f97a46b453fb37 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Tue, 5 May 2026 18:11:11 +0700 Subject: [PATCH 04/12] refactor: drop MessageProcessingResult from DKG path - removed method CDKGPendingMessages::Misbehaving(NodeId, int, PeerManager&), ProcessPendingMessageBatch calls peerman.Misbehaving(...) directly - renamed PushPendingMessage(NodeId, Message&, PeerManager&) to PushOwnPendingMessage for clear distinction of path with node=-1 (self made) --- src/active/dkgsessionhandler.cpp | 26 +++++++++++--------------- src/llmq/dkgsessionhandler.cpp | 5 ++--- src/llmq/dkgsessionhandler.h | 19 ++++++++++++++++--- 3 files changed, 29 insertions(+), 21 deletions(-) diff --git a/src/active/dkgsessionhandler.cpp b/src/active/dkgsessionhandler.cpp index 65b756b2143d..f63431152cb0 100644 --- a/src/active/dkgsessionhandler.cpp +++ b/src/active/dkgsessionhandler.cpp @@ -379,18 +379,14 @@ bool ProcessPendingMessageBatch(const CConnman& connman, CDKGSession& session, C 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); - } + peerman.Misbehaving(nodeId, 100); 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); - } + peerman.Misbehaving(nodeId, 100); } LogPrint(BCLog::LLMQ_DKG, "%s -- skipping message due to failed preverification, peer=%d\n", __func__, nodeId); continue; @@ -405,7 +401,7 @@ bool ProcessPendingMessageBatch(const CConnman& connman, CDKGSession& session, C 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); + peerman.Misbehaving(nodeId, 100); } } @@ -471,9 +467,9 @@ void ActiveDKGSessionHandler::HandleDKGRound(CConnman& connman, PeerManager& pee WaitForNextPhase(QuorumPhase::Initialized, QuorumPhase::Contribute, curQuorumHash); // Contribute - auto fContributeStart = [this, &peerman]() { + auto fContributeStart = [this]() { if (auto qc = curSession->Contribute(); qc) { - pendingContributions.PushPendingMessage(-1, *qc, peerman); + pendingContributions.PushOwnPendingMessage(*qc); } }; auto fContributeWait = [this, &connman, &peerman] { @@ -482,9 +478,9 @@ void ActiveDKGSessionHandler::HandleDKGRound(CConnman& connman, PeerManager& pee HandlePhase(QuorumPhase::Contribute, QuorumPhase::Complain, curQuorumHash, 0.05, fContributeStart, fContributeWait); // Complain - auto fComplainStart = [this, &connman, &peerman]() { + auto fComplainStart = [this, &connman]() { if (auto qc = curSession->VerifyAndComplain(connman); qc) { - pendingComplaints.PushPendingMessage(-1, *qc, peerman); + pendingComplaints.PushOwnPendingMessage(*qc); } }; auto fComplainWait = [this, &connman, &peerman] { @@ -493,9 +489,9 @@ void ActiveDKGSessionHandler::HandleDKGRound(CConnman& connman, PeerManager& pee HandlePhase(QuorumPhase::Complain, QuorumPhase::Justify, curQuorumHash, 0.05, fComplainStart, fComplainWait); // Justify - auto fJustifyStart = [this, &peerman]() { + auto fJustifyStart = [this]() { if (auto qj = curSession->VerifyAndJustify(); qj) { - pendingJustifications.PushPendingMessage(-1, *qj, peerman); + pendingJustifications.PushOwnPendingMessage(*qj); } }; auto fJustifyWait = [this, &connman, &peerman] { @@ -504,9 +500,9 @@ void ActiveDKGSessionHandler::HandleDKGRound(CConnman& connman, PeerManager& pee HandlePhase(QuorumPhase::Justify, QuorumPhase::Commit, curQuorumHash, 0.05, fJustifyStart, fJustifyWait); // Commit - auto fCommitStart = [this, &peerman]() { + auto fCommitStart = [this]() { if (auto qc = curSession->VerifyAndCommit(); qc) { - pendingPrematureCommitments.PushPendingMessage(-1, *qc, peerman); + pendingPrematureCommitments.PushOwnPendingMessage(*qc); } }; auto fCommitWait = [this, &connman, &peerman] { diff --git a/src/llmq/dkgsessionhandler.cpp b/src/llmq/dkgsessionhandler.cpp index 2c83b07266e5..dce82138e6da 100644 --- a/src/llmq/dkgsessionhandler.cpp +++ b/src/llmq/dkgsessionhandler.cpp @@ -77,10 +77,9 @@ bool CDKGPendingMessages::HasSeen(const uint256& hash) const return seenMessages.count(hash) != 0; } -void CDKGPendingMessages::Misbehaving(const NodeId from, const int score, PeerManager& peerman) +void CDKGPendingMessages::PushOwnPendingMessage(CDataStream& vRecv) { - if (from == -1) return; - peerman.Misbehaving(from, score); + [[maybe_unused]] auto result = PushPendingMessage(/*from=*/-1, vRecv); } void CDKGPendingMessages::Clear() diff --git a/src/llmq/dkgsessionhandler.h b/src/llmq/dkgsessionhandler.h index 98b1cf7ac45e..b95b67296335 100644 --- a/src/llmq/dkgsessionhandler.h +++ b/src/llmq/dkgsessionhandler.h @@ -73,19 +73,32 @@ class CDKGPendingMessages explicit CDKGPendingMessages(size_t _maxMessagesPerNode, uint32_t _invType) : invType(_invType), maxMessagesPerNode(_maxMessagesPerNode) {}; + /** + * Real-peer path: enqueue a serialized DKG message received from a peer. + * The returned MessageProcessingResult conveys the erase request / misbehavior + * back up to PeerManager so DKG state stays peerman-free. + */ [[nodiscard]] MessageProcessingResult PushPendingMessage(NodeId from, CDataStream& vRecv) EXCLUSIVE_LOCKS_REQUIRED(!cs_messages); + + /** + * Self-inject path: enqueue a DKG message we just produced ourselves under + * the conventional from=-1 NodeId. No peer-side effects are possible, so + * the inner MessageProcessingResult is unconditionally discarded. + */ + void PushOwnPendingMessage(CDataStream& vRecv) EXCLUSIVE_LOCKS_REQUIRED(!cs_messages); + std::list PopPendingMessages(size_t maxCount) EXCLUSIVE_LOCKS_REQUIRED(!cs_messages); bool HasSeen(const uint256& hash) const EXCLUSIVE_LOCKS_REQUIRED(!cs_messages); - void Misbehaving(NodeId from, int score, PeerManager& peerman); void Clear() EXCLUSIVE_LOCKS_REQUIRED(!cs_messages); + /** Self-inject convenience overload: serialize @msg and route to the binary self-inject path. */ template - void PushPendingMessage(NodeId from, Message& msg, PeerManager& peerman) EXCLUSIVE_LOCKS_REQUIRED(!cs_messages) + void PushOwnPendingMessage(Message& msg) EXCLUSIVE_LOCKS_REQUIRED(!cs_messages) { CDataStream ds(SER_NETWORK, PROTOCOL_VERSION); ds << msg; - peerman.PostProcessMessage(PushPendingMessage(from, ds), from); + PushOwnPendingMessage(ds); } // Might return nullptr messages, which indicates that deserialization failed for some reason From 9eb501679bd31c0a021ce43530d598b532b6c6e5 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Tue, 5 May 2026 23:16:31 +0700 Subject: [PATCH 05/12] feat: new NetHandler for DKG to separate network and logic --- src/Makefile.am | 6 ++- src/init.cpp | 14 ++++++ src/llmq/net_dkg.cpp | 109 +++++++++++++++++++++++++++++++++++++++++ src/llmq/net_dkg.h | 95 +++++++++++++++++++++++++++++++++++ src/net_processing.cpp | 75 ++-------------------------- 5 files changed, 226 insertions(+), 73 deletions(-) create mode 100644 src/llmq/net_dkg.cpp create mode 100644 src/llmq/net_dkg.h diff --git a/src/Makefile.am b/src/Makefile.am index a0eca537b48d..52ffad3efff0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -282,6 +282,9 @@ BITCOIN_CORE_H = \ llmq/dkgsessionhandler.h \ llmq/dkgsessionmgr.h \ llmq/ehf_signals.h \ + llmq/net_dkg.h \ + llmq/net_quorum.h \ + llmq/net_signing.h \ llmq/observer.h \ llmq/options.h \ llmq/params.h \ @@ -289,8 +292,6 @@ BITCOIN_CORE_H = \ llmq/quorumsman.h \ llmq/signhash.h \ llmq/signing.h \ - llmq/net_quorum.h \ - llmq/net_signing.h \ llmq/signing_shares.h \ llmq/snapshot.h \ llmq/types.h \ @@ -553,6 +554,7 @@ libbitcoin_node_a_SOURCES = \ llmq/dkgsessionhandler.cpp \ llmq/dkgsessionmgr.cpp \ llmq/ehf_signals.cpp \ + llmq/net_dkg.cpp \ llmq/net_quorum.cpp \ llmq/net_signing.cpp \ llmq/observer.cpp \ diff --git a/src/init.cpp b/src/init.cpp index 113bd4c9f025..bc9a1cda3c43 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -98,6 +98,7 @@ #include #include #include +#include #include #include #include @@ -2228,6 +2229,19 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) node.peerman->AddExtraHandler(std::move(net_quorum)); } + if (node.active_ctx) { + node.peerman->AddExtraHandler(std::make_unique( + node.peerman.get(), *node.sporkman, *node.active_ctx->qdkgsman, + *node.llmq_ctx->bls_worker, *node.dmnman, *node.mn_metaman, + *node.active_ctx->dkgdbgman, *node.llmq_ctx->quorum_block_processor, *node.llmq_ctx->qsnapman, + *node.active_ctx->nodeman, chainman, *node.connman)); + } else if (node.observer_ctx) { + node.peerman->AddExtraHandler(std::make_unique( + node.peerman.get(), *node.sporkman, *node.observer_ctx->qdkgsman)); + } else { + node.peerman->AddExtraHandler(std::make_unique(node.peerman.get())); + } + 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); diff --git a/src/llmq/net_dkg.cpp b/src/llmq/net_dkg.cpp new file mode 100644 index 000000000000..e7d6a326a6ab --- /dev/null +++ b/src/llmq/net_dkg.cpp @@ -0,0 +1,109 @@ +// 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 + +namespace llmq { + +NetDKG::NetDKG(PeerManagerInternal* peer_manager, const CSporkManager& sporkman, CDKGSessionManager& qdkgsman) : + NetHandler(peer_manager), + m_qdkgsman{qdkgsman}, + m_sporkman{sporkman}, + m_active{nullptr} +{ +} + +NetDKG::NetDKG(PeerManagerInternal* peer_manager, const CSporkManager& sporkman, CDKGSessionManager& qdkgsman, + CBLSWorker& bls_worker, CDeterministicMNManager& dmnman, CMasternodeMetaMan& mn_metaman, + CDKGDebugManager& dkgdbgman, CQuorumBlockProcessor& qblockman, CQuorumSnapshotManager& qsnapman, + const CActiveMasternodeManager& mn_activeman, const ChainstateManager& chainman, CConnman& connman) : + NetHandler(peer_manager), + m_qdkgsman{qdkgsman}, + m_sporkman{sporkman}, + m_active{std::make_unique( + ActiveDKG{bls_worker, dmnman, mn_metaman, dkgdbgman, qblockman, qsnapman, mn_activeman, chainman, connman})} +{ +} + +void NetDKG::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv) +{ + auto result = m_qdkgsman.ProcessMessage(pfrom, /*is_masternode=*/m_active != nullptr, msg_type, vRecv); + if (result.m_error) { + m_peer_manager->PeerMisbehaving(pfrom.GetId(), result.m_error->score, result.m_error->message); + } + if (result.m_to_erase) { + WITH_LOCK(::cs_main, m_peer_manager->PeerEraseObjectRequest(pfrom.GetId(), *result.m_to_erase)); + } +} + +bool NetDKG::AlreadyHave(const CInv& inv) +{ + switch (inv.type) { + case MSG_QUORUM_CONTRIB: + case MSG_QUORUM_COMPLAINT: + case MSG_QUORUM_JUSTIFICATION: + case MSG_QUORUM_PREMATURE_COMMITMENT: + return m_qdkgsman.AlreadyHave(inv); + } + return false; +} + +bool NetDKG::ProcessGetData(CNode& pfrom, const CInv& inv, CConnman& connman, const CNetMsgMaker& msgMaker) +{ + // Default implementations of GetContribution and the other virtual methods + // return false in observer mode; m_active is only an early exit and does + // not affect logic. + if (m_active == nullptr) return false; + + switch (inv.type) { + case MSG_QUORUM_CONTRIB: { + CDKGContribution o; + if (m_qdkgsman.GetContribution(inv.hash, o)) { + connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::QCONTRIB, o)); + return true; + } + return false; + } + case MSG_QUORUM_COMPLAINT: { + CDKGComplaint o; + if (m_qdkgsman.GetComplaint(inv.hash, o)) { + connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::QCOMPLAINT, o)); + return true; + } + return false; + } + case MSG_QUORUM_JUSTIFICATION: { + CDKGJustification o; + if (m_qdkgsman.GetJustification(inv.hash, o)) { + connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::QJUSTIFICATION, o)); + return true; + } + return false; + } + case MSG_QUORUM_PREMATURE_COMMITMENT: { + CDKGPrematureCommitment o; + if (m_qdkgsman.GetPrematureCommitment(inv.hash, o)) { + connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::QPCOMMITMENT, o)); + return true; + } + return false; + } + } + return false; +} + +void NetDKGStub::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv) +{ + if (msg_type == NetMsgType::QCONTRIB || msg_type == NetMsgType::QCOMPLAINT || msg_type == NetMsgType::QJUSTIFICATION || + msg_type == NetMsgType::QPCOMMITMENT || msg_type == NetMsgType::QWATCH) { + m_peer_manager->PeerMisbehaving(pfrom.GetId(), 10); + } +} + +} // namespace llmq diff --git a/src/llmq/net_dkg.h b/src/llmq/net_dkg.h new file mode 100644 index 000000000000..ade0b72f3fc2 --- /dev/null +++ b/src/llmq/net_dkg.h @@ -0,0 +1,95 @@ +// 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_LLMQ_NET_DKG_H +#define BITCOIN_LLMQ_NET_DKG_H + +#include + +#include + +class CActiveMasternodeManager; +class CBLSWorker; +class CConnman; +class CDeterministicMNManager; +class ChainstateManager; +class CMasternodeMetaMan; +class CSporkManager; +namespace llmq { +class CDKGDebugManager; +class CDKGSessionManager; +class CQuorumBlockProcessor; +class CQuorumSnapshotManager; +} // namespace llmq + +namespace llmq { +/** + * NetHandler responsible for DKG networking: + * - QCONTRIB / QCOMPLAINT / QJUSTIFICATION / QPCOMMITMENT / QWATCH ProcessMessage + * routing into CDKGSessionManager. The resulting MessageProcessingResult is + * consumed locally via PeerManagerInternal and never propagated up. + * - AlreadyHave for the four MSG_QUORUM_* DKG inv types. + * - ProcessGetData for the four MSG_QUORUM_* DKG inv types (active mode only; + * in observer mode the underlying Get* calls return false by construction). + * + * Active-mode-only deps live in @ref ActiveDKG; @ref m_active is null in + * observer mode and non-null in active mode (all-or-none). + * + * On nodes that run neither active nor observer mode, register @ref NetDKGStub + * instead. + */ +class NetDKG final : public NetHandler +{ +public: + //! Observer-mode constructor. + NetDKG(PeerManagerInternal* peer_manager, const CSporkManager& sporkman, CDKGSessionManager& qdkgsman); + + //! Active-mode constructor: takes the masternode-only dep bundle as required references. + NetDKG(PeerManagerInternal* peer_manager, const CSporkManager& sporkman, CDKGSessionManager& qdkgsman, + CBLSWorker& bls_worker, CDeterministicMNManager& dmnman, CMasternodeMetaMan& mn_metaman, + CDKGDebugManager& dkgdbgman, CQuorumBlockProcessor& qblockman, CQuorumSnapshotManager& qsnapman, + const CActiveMasternodeManager& mn_activeman, const ChainstateManager& chainman, CConnman& connman); + + // NetHandler + void ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv) override; + bool AlreadyHave(const CInv& inv) override; + bool ProcessGetData(CNode& pfrom, const CInv& inv, CConnman& connman, const CNetMsgMaker& msgMaker) override; + +private: + //! Bundle of refs that exist only in active masternode mode. + struct ActiveDKG { + CBLSWorker& bls_worker; + CDeterministicMNManager& dmnman; + CMasternodeMetaMan& mn_metaman; + CDKGDebugManager& dkgdbgman; + CQuorumBlockProcessor& qblockman; + CQuorumSnapshotManager& qsnapman; + const CActiveMasternodeManager& mn_activeman; + const ChainstateManager& chainman; + CConnman& connman; + }; + + CDKGSessionManager& m_qdkgsman; + const CSporkManager& m_sporkman; + const std::unique_ptr m_active; //!< null in observer mode, non-null in active mode +}; + +/** + * Minimal NetHandler installed on nodes that run neither active nor observer + * DKG mode. Just punishes peers that push DKG messages we cannot serve. + */ +class NetDKGStub final : public NetHandler +{ +public: + explicit NetDKGStub(PeerManagerInternal* peer_manager) : + NetHandler(peer_manager) + { + } + + // NetHandler + void ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv) override; +}; +} // namespace llmq + +#endif // BITCOIN_LLMQ_NET_DKG_H diff --git a/src/net_processing.cpp b/src/net_processing.cpp index 74ff49313533..e05e4296b4d0 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -972,9 +972,6 @@ class PeerManagerImpl final : public PeerManager bool AlreadyHave(const CInv& inv) EXCLUSIVE_LOCKS_REQUIRED(cs_main, !m_recent_confirmed_transactions_mutex); - bool DKGSessionAlreadyHave(const CInv& inv); - MessageProcessingResult DKGSessionProcessMessage(CNode& pfrom, bool is_masternode, std::string_view msg_type, CDataStream& vRecv); - /** * Filter for transactions that were recently rejected by the mempool. * These are not rerequested until the chain tip changes, at which point @@ -2375,11 +2372,6 @@ bool PeerManagerImpl::AlreadyHave(const CInv& inv) case MSG_QUORUM_FINAL_COMMITMENT: return m_llmq_ctx->quorum_block_processor->HasMineableCommitment(inv.hash); - case MSG_QUORUM_CONTRIB: - case MSG_QUORUM_COMPLAINT: - case MSG_QUORUM_JUSTIFICATION: - case MSG_QUORUM_PREMATURE_COMMITMENT: - return DKGSessionAlreadyHave(inv); case MSG_QUORUM_RECOVERED_SIG: // TODO: move it to NetSigning return m_llmq_ctx->sigman->AlreadyHave(inv); @@ -2392,6 +2384,10 @@ bool PeerManagerImpl::AlreadyHave(const CInv& inv) return m_mn_metaman.AlreadyHavePlatformBan(inv.hash); // At the end inventories that are handled by NetHandler + case MSG_QUORUM_CONTRIB: + case MSG_QUORUM_COMPLAINT: + case MSG_QUORUM_JUSTIFICATION: + case MSG_QUORUM_PREMATURE_COMMITMENT: case MSG_DSQ: if (m_cj_walletman && m_cj_walletman->hasQueue(inv.hash)) return true; for (const auto& handler : m_handlers) { @@ -2404,16 +2400,6 @@ bool PeerManagerImpl::AlreadyHave(const CInv& inv) return true; } -bool PeerManagerImpl::DKGSessionAlreadyHave(const CInv& inv) -{ - if (m_observer_ctx) { - return m_observer_ctx->qdkgsman->AlreadyHave(inv); - } else if (m_active_ctx) { - return m_active_ctx->qdkgsman->AlreadyHave(inv); - } - return false; -} - bool PeerManagerImpl::AlreadyHaveBlock(const uint256& block_hash) { return m_chainman.m_blockman.LookupBlockIndex(block_hash) != nullptr; @@ -2977,38 +2963,6 @@ void PeerManagerImpl::ProcessGetData(CNode& pfrom, Peer& peer, const std::atomic } } - if (!push && (inv.type == MSG_QUORUM_CONTRIB)) { - llmq::CDKGContribution o; - if (m_active_ctx && m_active_ctx->qdkgsman->GetContribution(inv.hash, o)) { - m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::QCONTRIB, o)); - push = true; - } - } - - if (!push && (inv.type == MSG_QUORUM_COMPLAINT)) { - llmq::CDKGComplaint o; - if (m_active_ctx && m_active_ctx->qdkgsman->GetComplaint(inv.hash, o)) { - m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::QCOMPLAINT, o)); - push = true; - } - } - - if (!push && (inv.type == MSG_QUORUM_JUSTIFICATION)) { - llmq::CDKGJustification o; - if (m_active_ctx && m_active_ctx->qdkgsman->GetJustification(inv.hash, o)) { - m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::QJUSTIFICATION, o)); - push = true; - } - } - - if (!push && (inv.type == MSG_QUORUM_PREMATURE_COMMITMENT)) { - llmq::CDKGPrematureCommitment o; - if (m_active_ctx && m_active_ctx->qdkgsman->GetPrematureCommitment(inv.hash, o)) { - m_connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::QPCOMMITMENT, o)); - push = true; - } - } - if (!push && (inv.type == MSG_QUORUM_RECOVERED_SIG)) { llmq::CRecoveredSig o; if (m_llmq_ctx->sigman->GetRecoveredSigForGetData(inv.hash, o)) { @@ -5508,7 +5462,6 @@ void PeerManagerImpl::ProcessMessage( if (m_cj_walletman) { PostProcessMessage(m_cj_walletman->processMessage(pfrom, m_chainman.ActiveChainstate(), m_connman, m_mempool, msg_type, vRecv), pfrom.GetId()); } - PostProcessMessage(DKGSessionProcessMessage(pfrom, is_masternode, msg_type, vRecv), pfrom.GetId()); 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_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()); @@ -5537,26 +5490,6 @@ void PeerManagerImpl::ProcessMessage( return; } -MessageProcessingResult PeerManagerImpl::DKGSessionProcessMessage(CNode& pfrom, bool is_masternode, std::string_view msg_type, CDataStream& vRecv) -{ - if (m_active_ctx) { - assert(is_masternode); - return m_active_ctx->qdkgsman->ProcessMessage(pfrom, is_masternode, msg_type, vRecv); - } else if (m_observer_ctx) { - assert(!is_masternode); - return m_observer_ctx->qdkgsman->ProcessMessage(pfrom, is_masternode, msg_type, vRecv); - } - assert(!is_masternode); - if (msg_type == NetMsgType::QCONTRIB - || msg_type == NetMsgType::QCOMPLAINT - || msg_type == NetMsgType::QJUSTIFICATION - || msg_type == NetMsgType::QPCOMMITMENT - || msg_type == NetMsgType::QWATCH) { - return MisbehavingError{10}; - } - return {}; -} - bool PeerManagerImpl::MaybeDiscourageAndDisconnect(CNode& pnode, Peer& peer) { { From ac0258f161a50095b0823a356ef374028785cc41 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Wed, 6 May 2026 13:22:47 +0700 Subject: [PATCH 06/12] refactor: removed ActiveContext / ObserverContext members and usages from PeerManager Re-ordered initialization of PeerManager and ActiveContext / ObserverContext, PeerManager::make now takes nodeman raw ptr (or nullptr). It resolves several circular dependencies over net_processing and removes several unique_ptr work-arounds from PeerManager --- src/init.cpp | 25 +++++++++-------- src/net_processing.cpp | 37 ++++++++++--------------- src/net_processing.h | 13 ++++----- src/test/util/setup_common.cpp | 6 ++-- test/lint/lint-circular-dependencies.py | 4 +-- 5 files changed, 37 insertions(+), 48 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index bc9a1cda3c43..f6d45cb546b0 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -2174,18 +2174,6 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) node.clhandler = std::make_unique(*node.chainlocks, chainman, *node.mempool, *node.mn_sync); RegisterValidationInterface(node.clhandler.get()); - 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.chainlocks, *node.clhandler, node.active_ctx, node.dmnman, - node.cj_walletman, node.llmq_ctx, node.observer_ctx, ignores_incoming_txs); - RegisterValidationInterface(node.peerman.get()); - - g_ds_notification_interface = std::make_unique( - *node.connman, *node.dstxman, *node.mn_sync, *node.govman, chainman, node.dmnman // todo: replace unique_ptr for dmnman to reference - ); - RegisterValidationInterface(g_ds_notification_interface.get()); - // ********************************************************* Step 7c: Setup masternode mode or watch-only mode assert(!node.active_ctx); assert(!node.observer_ctx); @@ -2212,6 +2200,19 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) RegisterValidationInterface(node.observer_ctx.get()); } + 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.chainlocks, *node.clhandler, + node.active_ctx ? node.active_ctx->nodeman.get() : nullptr, + node.dmnman, node.cj_walletman, node.llmq_ctx, ignores_incoming_txs); + RegisterValidationInterface(node.peerman.get()); + + g_ds_notification_interface = std::make_unique( + *node.connman, *node.dstxman, *node.mn_sync, *node.govman, chainman, node.dmnman // todo: replace unique_ptr for dmnman to reference + ); + RegisterValidationInterface(g_ds_notification_interface.get()); + // ********************************************************* Step 7d: Setup other Dash services node.peerman->AddExtraHandler(std::make_unique(node.peerman.get(), *node.llmq_ctx->isman, node.active_ctx ? node.active_ctx->is_signer.get() : nullptr, *node.llmq_ctx->sigman, *node.llmq_ctx->qman, *node.chainlocks, chainman.ActiveChainstate(), *node.mempool, *node.mn_sync)); diff --git a/src/net_processing.cpp b/src/net_processing.cpp index e05e4296b4d0..5fe43196b6f3 100644 --- a/src/net_processing.cpp +++ b/src/net_processing.cpp @@ -40,7 +40,6 @@ #include #include -#include #include #include #include @@ -54,9 +53,6 @@ #include #include #include -#include -#include -#include #include #include #include @@ -589,11 +585,10 @@ class PeerManagerImpl final : public PeerManager CMasternodeMetaMan& mn_metaman, CMasternodeSync& mn_sync, CGovernanceManager& govman, CSporkManager& sporkman, const chainlock::Chainlocks& chainlocks, chainlock::ChainlockHandler& clhandler, - const std::unique_ptr& active_ctx, + CActiveMasternodeManager* nodeman, 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); + const std::unique_ptr& llmq_ctx, bool ignore_incoming_txs); ~PeerManagerImpl() { @@ -816,11 +811,10 @@ class PeerManagerImpl final : public PeerManager ChainstateManager& m_chainman; CTxMemPool& m_mempool; std::unique_ptr m_txreconciliation; - const std::unique_ptr& m_active_ctx; + CActiveMasternodeManager* const m_nodeman; //!< null if non-masternode mode; non-null implies masternode mode const std::unique_ptr& m_dmnman; const std::unique_ptr& m_cj_walletman; const std::unique_ptr& m_llmq_ctx; - const std::unique_ptr& m_observer_ctx; CMasternodeMetaMan& m_mn_metaman; CMasternodeSync& m_mn_sync; CGovernanceManager& m_govman; @@ -1652,7 +1646,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_active_ctx != nullptr, + std::chrono::microseconds process_time = CalculateObjectGetDataTime(inv, current_time, /*is_masternode=*/m_nodeman != nullptr, !state->fPreferredDownload); peer_download_state.m_object_process_time.emplace(process_time, inv); @@ -2064,13 +2058,12 @@ std::unique_ptr PeerManager::make(const CChainParams& chainparams, CMasternodeSync& mn_sync, CGovernanceManager& govman, CSporkManager& sporkman, const chainlock::Chainlocks& chainlocks, chainlock::ChainlockHandler& clhandler, - const std::unique_ptr& active_ctx, + CActiveMasternodeManager* nodeman, 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) + const std::unique_ptr& llmq_ctx, bool ignore_incoming_txs) { - return std::make_unique(chainparams, connman, addrman, banman, dstxman, chainman, pool, mn_metaman, mn_sync, govman, sporkman, chainlocks, clhandler, 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, chainlocks, clhandler, nodeman, dmnman, cj_walletman, llmq_ctx, ignore_incoming_txs); } PeerManagerImpl::PeerManagerImpl(const CChainParams& chainparams, CConnman& connman, AddrMan& addrman, BanMan* banman, @@ -2079,11 +2072,10 @@ PeerManagerImpl::PeerManagerImpl(const CChainParams& chainparams, CConnman& conn CSporkManager& sporkman, const chainlock::Chainlocks& chainlocks, chainlock::ChainlockHandler& clhandler, - const std::unique_ptr& active_ctx, + CActiveMasternodeManager* nodeman, 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) + const std::unique_ptr& llmq_ctx, bool ignore_incoming_txs) : m_chainparams(chainparams), m_connman(connman), m_addrman(addrman), @@ -2091,11 +2083,10 @@ PeerManagerImpl::PeerManagerImpl(const CChainParams& chainparams, CConnman& conn m_dstxman(dstxman), m_chainman(chainman), m_mempool(pool), - m_active_ctx(active_ctx), + m_nodeman(nodeman), m_dmnman(dmnman), m_cj_walletman(cj_walletman), m_llmq_ctx(llmq_ctx), - m_observer_ctx(observer_ctx), m_mn_metaman(mn_metaman), m_mn_sync(mn_sync), m_govman(govman), @@ -3714,7 +3705,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_active_ctx != nullptr; + const bool is_masternode = m_nodeman != nullptr; PeerRef peer = GetPeerRef(pfrom.GetId()); if (peer == nullptr) return; @@ -3988,7 +3979,7 @@ void PeerManagerImpl::ProcessMessage( } if (is_masternode && !pfrom.m_masternode_probe_connection) { - CMNAuth::PushMNAUTH(pfrom, m_connman, *m_active_ctx->nodeman); + CMNAuth::PushMNAUTH(pfrom, m_connman, *Assert(m_nodeman)); } // Tell our peer we prefer to receive headers rather than inv's @@ -5463,7 +5454,7 @@ void PeerManagerImpl::ProcessMessage( PostProcessMessage(m_cj_walletman->processMessage(pfrom, m_chainman.ActiveChainstate(), m_connman, m_mempool, msg_type, vRecv), pfrom.GetId()); } 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_active_ctx ? m_active_ctx->nodeman.get() : nullptr), m_mn_sync, m_dmnman->GetListAtChainTip(), msg_type, vRecv), pfrom.GetId()); + PostProcessMessage(CMNAuth::ProcessMessage(pfrom, peer->m_their_services, m_connman, m_mn_metaman, m_nodeman, m_mn_sync, m_dmnman->GetListAtChainTip(), msg_type, vRecv), pfrom.GetId()); PostProcessMessage(m_llmq_ctx->quorum_block_processor->ProcessMessage(pfrom, msg_type, vRecv), pfrom.GetId()); PostProcessMessage(ProcessPlatformBanMessage(pfrom.GetId(), msg_type, vRecv), pfrom.GetId()); @@ -5961,7 +5952,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto) assert(m_llmq_ctx); - const bool is_masternode = m_active_ctx != nullptr; + const bool is_masternode = m_nodeman != nullptr; PeerRef peer = GetPeerRef(pto->GetId()); if (!peer) return false; diff --git a/src/net_processing.h b/src/net_processing.h index 285a660c164d..b205a81b4a50 100644 --- a/src/net_processing.h +++ b/src/net_processing.h @@ -29,11 +29,7 @@ class CNetMsgMaker; class CSporkManager; class CTransaction; class CTxMemPool; -struct ActiveContext; struct LLMQContext; -namespace llmq { -struct ObserverContext; -} // namespace llmq namespace chainlock { class Chainlocks; class ChainlockHandler; @@ -109,17 +105,20 @@ class NetHandler class PeerManager : public CValidationInterface, public NetEventsInterface, public PeerManagerInternal { public: + /** + * @param nodeman Non-null iff masternode mode is on; null otherwise. Used both + * as the masternode-mode indicator and for direct access. + */ static std::unique_ptr make(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 chainlock::Chainlocks& chainlocks, chainlock::ChainlockHandler& clhandler, - const std::unique_ptr& active_ctx, + CActiveMasternodeManager* nodeman, 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); + const std::unique_ptr& llmq_ctx, bool ignore_incoming_txs); virtual ~PeerManager() { } /** diff --git a/src/test/util/setup_common.cpp b/src/test/util/setup_common.cpp index 8a96cb0c9ba4..5cbf4265c7c6 100644 --- a/src/test/util/setup_common.cpp +++ b/src/test/util/setup_common.cpp @@ -52,7 +52,6 @@ #include #include -#include #include #include #include @@ -134,8 +133,9 @@ std::unique_ptr MakePeerManager(CConnman& connman, 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, *node.chainlocks, *node.clhandler, node.active_ctx, node.dmnman, node.cj_walletman, - node.llmq_ctx, node.observer_ctx, ignore_incoming_txs); + *node.mn_sync, *node.govman, *node.sporkman, *node.chainlocks, *node.clhandler, + /*nodeman=*/nullptr, + node.dmnman, node.cj_walletman, node.llmq_ctx, ignore_incoming_txs); } void DashChainstateSetup(ChainstateManager& chainman, diff --git a/test/lint/lint-circular-dependencies.py b/test/lint/lint-circular-dependencies.py index 62e1e7741cad..96d83eddc73b 100755 --- a/test/lint/lint-circular-dependencies.py +++ b/test/lint/lint-circular-dependencies.py @@ -21,7 +21,6 @@ "wallet/wallet -> wallet/walletdb -> wallet/wallet", "kernel/coinstats -> validation -> kernel/coinstats", # Dash - "active/context -> active/dkgsessionhandler -> llmq/dkgsessionhandler -> net_processing -> active/context", "banman -> common/bloom -> evo/assetlocktx -> llmq/quorumsman -> llmq/blockprocessor -> net -> banman", "chainlock/chainlock -> spork -> net -> evo/deterministicmns -> evo/providertx -> validation -> chainlock/chainlock", "coinjoin/client -> coinjoin/util -> wallet/wallet -> psbt -> node/transaction -> net_processing -> coinjoin/walletman -> coinjoin/client", @@ -43,8 +42,7 @@ "instantsend/instantsend -> node/blockstorage -> validation -> txmempool -> instantsend/instantsend", "llmq/blockprocessor -> llmq/utils -> llmq/snapshot -> llmq/blockprocessor", "llmq/commitment -> llmq/utils -> llmq/snapshot -> llmq/commitment", - "llmq/dkgsessionhandler -> net_processing -> llmq/dkgsessionmgr -> llmq/dkgsessionhandler", - "llmq/dkgsessionhandler -> net_processing -> llmq/observer -> llmq/dkgsessionhandler", + "llmq/dkgsessionhandler -> net_processing -> llmq/quorumsman -> llmq/dkgsessionmgr -> llmq/dkgsessionhandler", "masternode/payments -> validation -> masternode/payments", "net -> netmessagemaker -> net", "netaddress -> netbase -> netaddress", From 90bf67c2c42fd3d4924f031f3f26c9db5213c9f9 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Wed, 6 May 2026 15:07:16 +0700 Subject: [PATCH 07/12] refactor: move DKG workers and relevant handlers inside NetDKG It helps to drop dependency of llmq/dkgsessionhandler on network code --- src/active/context.cpp | 4 +- src/active/context.h | 2 +- src/active/dkgsessionhandler.cpp | 310 +--------------------- src/active/dkgsessionhandler.h | 32 ++- src/init.cpp | 2 +- src/llmq/debug.cpp | 18 ++ src/llmq/debug.h | 6 + src/llmq/dkgsessionhandler.cpp | 8 + src/llmq/dkgsessionhandler.h | 15 +- src/llmq/dkgsessionmgr.cpp | 14 - src/llmq/dkgsessionmgr.h | 17 +- src/llmq/net_dkg.cpp | 335 ++++++++++++++++++++++++ src/llmq/net_dkg.h | 15 ++ test/lint/lint-circular-dependencies.py | 1 - 14 files changed, 423 insertions(+), 356 deletions(-) diff --git a/src/active/context.cpp b/src/active/context.cpp index c89162df6a7e..1660af9cd57c 100644 --- a/src/active/context.cpp +++ b/src/active/context.cpp @@ -61,9 +61,8 @@ ActiveContext::~ActiveContext() m_qman.DisconnectManagers(); } -void ActiveContext::Start(CConnman& connman, PeerManager& peerman) +void ActiveContext::Start() { - qdkgsman->StartThreads(connman, peerman); cl_signer->Start(); cl_signer->RegisterRecoveryInterface(); is_signer->RegisterRecoveryInterface(); @@ -80,7 +79,6 @@ void ActiveContext::Stop() is_signer->UnregisterRecoveryInterface(); cl_signer->UnregisterRecoveryInterface(); cl_signer->Stop(); - qdkgsman->StopThreads(); } CCoinJoinServer& ActiveContext::GetCJServer() const diff --git a/src/active/context.h b/src/active/context.h index 44b71abc8af7..61bd62ebb28c 100644 --- a/src/active/context.h +++ b/src/active/context.h @@ -64,7 +64,7 @@ struct ActiveContext final : public llmq::QuorumRole, public CValidationInterfac const util::DbWrapperParams& db_params, bool quorums_watch); ~ActiveContext(); - void Start(CConnman& connman, PeerManager& peerman); + void Start(); void Stop(); CCoinJoinServer& GetCJServer() const; diff --git a/src/active/dkgsessionhandler.cpp b/src/active/dkgsessionhandler.cpp index f63431152cb0..110d787466f7 100644 --- a/src/active/dkgsessionhandler.cpp +++ b/src/active/dkgsessionhandler.cpp @@ -7,15 +7,12 @@ #include #include #include -#include #include #include #include -#include #include #include -#include #include namespace llmq { @@ -69,23 +66,9 @@ void ActiveDKGSessionHandler::UpdatedBlockTip(const CBlockIndex* pindexNew) params.name, quorumIndex, currentHeight, pQuorumBaseBlockIndex->nHeight, std23::to_underlying(oldPhase), std23::to_underlying(phase)); } -void ActiveDKGSessionHandler::StartThread(CConnman& connman, PeerManager& peerman) +uint256 ActiveDKGSessionHandler::GetCurrentQuorumHash() const { - if (phaseHandlerThread.joinable()) { - throw std::runtime_error("Tried to start an already started ActiveDKGSessionHandler thread."); - } - - m_thread_name = strprintf("llmq-%d-%d", std23::to_underlying(params.type), quorumIndex); - phaseHandlerThread = std::thread(&util::TraceThread, m_thread_name.c_str(), - [this, &connman, &peerman] { PhaseHandlerThread(connman, peerman); }); -} - -void ActiveDKGSessionHandler::StopThread() -{ - stopRequested = true; - if (phaseHandlerThread.joinable()) { - phaseHandlerThread.join(); - } + return WITH_LOCK(cs_phase_qhash, return quorumHash); } std::pair ActiveDKGSessionHandler::GetPhaseAndQuorumHash() const @@ -114,9 +97,6 @@ bool ActiveDKGSessionHandler::InitNewQuorum(gsl::not_null pQ return true; } -class AbortPhaseException : public std::exception { -}; - void ActiveDKGSessionHandler::WaitForNextPhase(std::optional curPhase, QuorumPhase nextPhase, const uint256& expectedQuorumHash, const WhileWaitFunc& shouldNotWait) const { @@ -249,292 +229,6 @@ void ActiveDKGSessionHandler::HandlePhase(QuorumPhase curPhase, QuorumPhase next LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d] - done, curPhase=%d, nextPhase=%d\n", __func__, params.name, quorumIndex, std23::to_underlying(curPhase), std23::to_underlying(nextPhase)); } -// returns a set of NodeIds which sent invalid messages -template -std::unordered_set BatchVerifyMessageSigs(CDKGSession& session, const std::vector>>& messages) -{ - if (messages.empty()) { - return {}; - } - - std::unordered_set ret; - bool revertToSingleVerification = false; - - CBLSSignature aggSig; - std::vector pubKeys; - std::vector messageHashes; - Uint256HashSet 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.GetType(), 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); - peerman.Misbehaving(nodeId, 100); - 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); - peerman.Misbehaving(nodeId, 100); - } - 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); - peerman.Misbehaving(nodeId, 100); - } - } - - 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 ActiveDKGSessionHandler::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(); - } - - m_dkgdbgman.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 = m_qblockman.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->ProTx(), /*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->ProTx()); - } - - WaitForNextPhase(QuorumPhase::Initialized, QuorumPhase::Contribute, curQuorumHash); - - // Contribute - auto fContributeStart = [this]() { - if (auto qc = curSession->Contribute(); qc) { - pendingContributions.PushOwnPendingMessage(*qc); - } - }; - 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]() { - if (auto qc = curSession->VerifyAndComplain(connman); qc) { - pendingComplaints.PushOwnPendingMessage(*qc); - } - }; - 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]() { - if (auto qj = curSession->VerifyAndJustify(); qj) { - pendingJustifications.PushOwnPendingMessage(*qj); - } - }; - 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]() { - if (auto qc = curSession->VerifyAndCommit(); qc) { - pendingPrematureCommitments.PushOwnPendingMessage(*qc); - } - }; - 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 = m_qblockman.AddMineableCommitment(fqc); inv_opt.has_value()) { - peerman.RelayInv(inv_opt.value()); - } - } -} - -void ActiveDKGSessionHandler::PhaseHandlerThread(CConnman& connman, PeerManager& peerman) -{ - while (!stopRequested) { - try { - LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d] - starting HandleDKGRound\n", __func__, params.name, quorumIndex); - HandleDKGRound(connman, peerman); - } catch (AbortPhaseException& e) { - m_dkgdbgman.UpdateLocalSessionStatus(params.type, quorumIndex, [&](CDKGDebugSessionStatus& status) { - status.statusBits.aborted = true; - return true; - }); - LogPrint(BCLog::LLMQ_DKG, "ActiveDKGSessionHandler::%s -- %s qi[%d] - aborted current DKG session\n", __func__, params.name, quorumIndex); - } - } -} - bool ActiveDKGSessionHandler::GetContribution(const uint256& hash, CDKGContribution& ret) const { return curSession && curSession->GetContribution(hash, ret); diff --git a/src/active/dkgsessionhandler.h b/src/active/dkgsessionhandler.h index 845a50d74461..4e48369627a7 100644 --- a/src/active/dkgsessionhandler.h +++ b/src/active/dkgsessionhandler.h @@ -10,21 +10,18 @@ #include #include +#include #include #include #include -#include -#include class CActiveMasternodeManager; class CBLSWorker; class CBlockIndex; -class CConnman; class ChainstateManager; class CDeterministicMNManager; class CMasternodeMetaMan; class CSporkManager; -class PeerManager; namespace Consensus { struct LLMQParams; } // namespace Consensus @@ -37,6 +34,10 @@ class CQuorumSnapshotManager; } // namespace llmq namespace llmq { +//! Thrown by Wait*, Sleep* and HandlePhase to bail out of the current DKG round. +class AbortPhaseException : public std::exception { +}; + class ActiveDKGSessionHandler final : public llmq::CDKGSessionHandler { using StartPhaseFunc = std::function; @@ -59,8 +60,6 @@ class ActiveDKGSessionHandler final : public llmq::CDKGSessionHandler private: std::atomic stopRequested{false}; std::atomic currentHeight{-1}; - std::string m_thread_name; - std::thread phaseHandlerThread; std::unique_ptr curSession{nullptr}; mutable Mutex cs_phase_qhash; @@ -86,12 +85,20 @@ class ActiveDKGSessionHandler final : public llmq::CDKGSessionHandler 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); + /* + * Phase-thread interface, called by NetDKG. Wait/Sleep/HandlePhase throw + * AbortPhaseException when stopRequested is set or when an unexpected + * phase change is observed. + */ + int QuorumIndex() const { return quorumIndex; } + bool QuorumsWatch() const { return m_quorums_watch; } + uint256 GetCurrentQuorumHash() const EXCLUSIVE_LOCKS_REQUIRED(!cs_phase_qhash); + CDKGSession* GetCurSession() { return curSession.get(); } + + void RequestStop() { stopRequested = true; } + bool IsStopRequested() const { return stopRequested; } bool InitNewQuorum(gsl::not_null pQuorumBaseBlockIndex); @@ -110,8 +117,9 @@ class ActiveDKGSessionHandler final : public llmq::CDKGSessionHandler 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); + +private: + std::pair GetPhaseAndQuorumHash() const EXCLUSIVE_LOCKS_REQUIRED(!cs_phase_qhash); }; } // namespace llmq diff --git a/src/init.cpp b/src/init.cpp index f6d45cb546b0..2ef32d38f6ef 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -2361,7 +2361,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) node.peerman->ScheduleHandlers(*node.scheduler); if (node.active_ctx) { - node.active_ctx->Start(*node.connman, *node.peerman); + node.active_ctx->Start(); node.scheduler->scheduleEvery(std::bind(&llmq::CDKGSessionManager::CleanupOldContributions, std::ref(*node.active_ctx->qdkgsman)), std::chrono::hours{1}); } diff --git a/src/llmq/debug.cpp b/src/llmq/debug.cpp index cafa9b3e699c..d3678d61a6df 100644 --- a/src/llmq/debug.cpp +++ b/src/llmq/debug.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -209,4 +210,21 @@ void CDKGDebugManager::UpdateLocalMemberStatus(Consensus::LLMQType llmqType, int } } +void CDKGDebugManager::MarkPhaseAdvanced(Consensus::LLMQType llmqType, int quorumIndex, QuorumPhase newPhase) +{ + UpdateLocalSessionStatus(llmqType, quorumIndex, [&](CDKGDebugSessionStatus& status) { + bool changed = status.phase != newPhase; + status.phase = newPhase; + return changed; + }); +} + +void CDKGDebugManager::MarkAborted(Consensus::LLMQType llmqType, int quorumIndex) +{ + UpdateLocalSessionStatus(llmqType, quorumIndex, [&](CDKGDebugSessionStatus& status) { + status.statusBits.aborted = true; + return true; + }); +} + } // namespace llmq diff --git a/src/llmq/debug.h b/src/llmq/debug.h index 5fef407fa1b1..7ca0e566d313 100644 --- a/src/llmq/debug.h +++ b/src/llmq/debug.h @@ -116,6 +116,12 @@ class CDKGDebugManager std::function&& func) EXCLUSIVE_LOCKS_REQUIRED(!cs_lockStatus); + //! Set the locally tracked phase to @p newPhase if different. + void MarkPhaseAdvanced(Consensus::LLMQType llmqType, int quorumIndex, QuorumPhase newPhase) + EXCLUSIVE_LOCKS_REQUIRED(!cs_lockStatus); + //! Mark the local session as aborted. + void MarkAborted(Consensus::LLMQType llmqType, int quorumIndex) EXCLUSIVE_LOCKS_REQUIRED(!cs_lockStatus); + size_t GetSessionCount() const EXCLUSIVE_LOCKS_REQUIRED(!cs_lockStatus); [[nodiscard]] static RPCResult GetJsonHelp(const std::string& key, bool optional, bool inner_optional = false); diff --git a/src/llmq/dkgsessionhandler.cpp b/src/llmq/dkgsessionhandler.cpp index dce82138e6da..eab934219cb7 100644 --- a/src/llmq/dkgsessionhandler.cpp +++ b/src/llmq/dkgsessionhandler.cpp @@ -106,4 +106,12 @@ MessageProcessingResult CDKGSessionHandler::ProcessMessage(NodeId from, std::str } return {}; } + +void CDKGSessionHandler::ClearPendingMessages() +{ + pendingContributions.Clear(); + pendingComplaints.Clear(); + pendingJustifications.Clear(); + pendingPrematureCommitments.Clear(); +} } // namespace llmq diff --git a/src/llmq/dkgsessionhandler.h b/src/llmq/dkgsessionhandler.h index b95b67296335..bcb25583dc9d 100644 --- a/src/llmq/dkgsessionhandler.h +++ b/src/llmq/dkgsessionhandler.h @@ -8,7 +8,6 @@ #include #include // for NodeId -#include #include #include #include @@ -23,8 +22,6 @@ #include class CBlockIndex; -class CConnman; -class PeerManager; namespace Consensus { struct LLMQParams; @@ -129,16 +126,10 @@ class CDKGPendingMessages /** * Handles multiple sequential sessions of one specific LLMQ type. There is one instance of this class per LLMQ type. - * - * It internally starts the phase handler thread, which constantly loops and sequentially processes one session at a - * time and waiting for the next phase if necessary. */ class CDKGSessionHandler { -private: - friend class CDKGSessionManager; - -protected: +public: const Consensus::LLMQParams& params; // Do not guard these, they protect their internals themselves @@ -153,14 +144,14 @@ class CDKGSessionHandler [[nodiscard]] MessageProcessingResult ProcessMessage(NodeId from, std::string_view msg_type, CDataStream& vRecv); + void ClearPendingMessages(); + 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 diff --git a/src/llmq/dkgsessionmgr.cpp b/src/llmq/dkgsessionmgr.cpp index ae71e08d28ca..384d397c66ff 100644 --- a/src/llmq/dkgsessionmgr.cpp +++ b/src/llmq/dkgsessionmgr.cpp @@ -46,20 +46,6 @@ CDKGSessionManager::CDKGSessionManager(CDeterministicMNManager& dmnman, CQuorumS CDKGSessionManager::~CDKGSessionManager() = default; -void CDKGSessionManager::StartThreads(CConnman& connman, PeerManager& peerman) -{ - for (auto& [_, dkgType] : dkgSessionHandlers) { - Assert(dkgType)->StartThread(connman, peerman); - } -} - -void CDKGSessionManager::StopThreads() -{ - for (auto& [_, dkgType] : dkgSessionHandlers) { - Assert(dkgType)->StopThread(); - } -} - void CDKGSessionManager::UpdatedBlockTip(const CBlockIndex* pindexNew, bool fInitialDownload) { CleanupCache(); diff --git a/src/llmq/dkgsessionmgr.h b/src/llmq/dkgsessionmgr.h index 934e02503364..6e68f239654f 100644 --- a/src/llmq/dkgsessionmgr.h +++ b/src/llmq/dkgsessionmgr.h @@ -11,11 +11,13 @@ #include #include #include +#include #include #include #include #include +#include template class CBLSIESMultiRecipientObjects; @@ -24,14 +26,12 @@ class CBLSIESEncryptedObject; class CActiveMasternodeManager; class CBlockIndex; -class CConnman; class CDBWrapper; class CDeterministicMNManager; class ChainstateManager; class CNode; class CMasternodeMetaMan; class CSporkManager; -class PeerManager; class CInv; struct MessageProcessingResult; namespace util { @@ -121,8 +121,17 @@ class CDKGSessionManager } } - void StartThreads(CConnman& connman, PeerManager& peerman); - void StopThreads(); + /** + * Visit every registered handler with @p fn(CDKGSessionHandler&). Used by + * the DKG NetHandler to drive per-handler phase threads. + */ + template + void ForEachHandler(HandlerFn&& fn) + { + for (auto& [_, handler] : dkgSessionHandlers) { + fn(*Assert(handler)); + } + } void UpdatedBlockTip(const CBlockIndex* pindexNew, bool fInitialDownload) EXCLUSIVE_LOCKS_REQUIRED(!contributionsCacheCs); diff --git a/src/llmq/net_dkg.cpp b/src/llmq/net_dkg.cpp index e7d6a326a6ab..fefaa43c5d84 100644 --- a/src/llmq/net_dkg.cpp +++ b/src/llmq/net_dkg.cpp @@ -4,13 +4,197 @@ #include +#include +#include +#include +#include #include #include +#include +#include #include #include +#include +#include +#include + +#include namespace llmq { +namespace { +// returns a set of NodeIds which sent invalid messages +template +std::unordered_set BatchVerifyMessageSigs(CDKGSession& session, const std::vector>>& messages) +{ + if (messages.empty()) { + return {}; + } + + std::unordered_set ret; + bool revertToSingleVerification = false; + + CBLSSignature aggSig; + std::vector pubKeys; + std::vector messageHashes; + Uint256HashSet 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; +} + +void RelayInvToParticipants(const CDKGSession& session, const CConnman& connman, PeerManagerInternal& 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.GetType(), 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.PeerPushInventory(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, + PeerManagerInternal& 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); + peerman.PeerMisbehaving(nodeId, 100); + 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); + peerman.PeerMisbehaving(nodeId, 100); + } + 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); + peerman.PeerMisbehaving(nodeId, 100); + } + } + + 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; +} +} // namespace + + NetDKG::NetDKG(PeerManagerInternal* peer_manager, const CSporkManager& sporkman, CDKGSessionManager& qdkgsman) : NetHandler(peer_manager), m_qdkgsman{qdkgsman}, @@ -98,6 +282,157 @@ bool NetDKG::ProcessGetData(CNode& pfrom, const CInv& inv, CConnman& connman, co return false; } +void NetDKG::Start() +{ + if (m_active == nullptr) return; + if (!m_phase_threads.empty()) { + throw std::runtime_error("Tried to start PhaseHandlerThreads again."); + } + + m_qdkgsman.ForEachHandler([this](CDKGSessionHandler& base) { + auto& handler = dynamic_cast(base); + std::string thread_name = strprintf("llmq-%d-%d", std23::to_underlying(handler.params.type), handler.QuorumIndex()); + m_phase_threads.emplace_back([this, name = std::move(thread_name), &handler] { + util::TraceThread(name.c_str(), [this, &handler] { PhaseHandlerThread(handler); }); + }); + }); +} + +void NetDKG::Stop() +{ + Interrupt(); + for (auto& t : m_phase_threads) { + if (t.joinable()) t.join(); + } + m_phase_threads.clear(); +} + +void NetDKG::Interrupt() +{ + if (m_active == nullptr) return; + m_qdkgsman.ForEachHandler([](CDKGSessionHandler& base) { + if (auto* handler = dynamic_cast(&base)) { + handler->RequestStop(); + } + }); +} + +void NetDKG::PhaseHandlerThread(ActiveDKGSessionHandler& handler) +{ + while (!handler.IsStopRequested()) { + try { + LogPrint(BCLog::LLMQ_DKG, "NetDKG::%s -- %s qi[%d] - starting HandleDKGRound\n", __func__, + handler.params.name, handler.QuorumIndex()); + HandleDKGRound(handler); + } catch (AbortPhaseException& e) { + m_active->dkgdbgman.MarkAborted(handler.params.type, handler.QuorumIndex()); + LogPrint(BCLog::LLMQ_DKG, "NetDKG::%s -- %s qi[%d] - aborted current DKG session\n", __func__, + handler.params.name, handler.QuorumIndex()); + } + } +} + +void NetDKG::HandleDKGRound(ActiveDKGSessionHandler& handler) +{ + auto& active = *Assert(m_active); + + handler.WaitForNextPhase(std::nullopt, QuorumPhase::Initialized); + + handler.ClearPendingMessages(); + uint256 curQuorumHash = handler.GetCurrentQuorumHash(); + + const CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(::cs_main, + return active.chainman.m_blockman.LookupBlockIndex(curQuorumHash)); + + if (!pQuorumBaseBlockIndex || !handler.InitNewQuorum(pQuorumBaseBlockIndex)) { + // should actually never happen + handler.WaitForNewQuorum(curQuorumHash); + throw AbortPhaseException(); + } + + active.dkgdbgman.MarkPhaseAdvanced(handler.params.type, handler.QuorumIndex(), QuorumPhase::Initialized); + + auto* curSession = handler.GetCurSession(); + if (handler.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 = active.qblockman.AddMineableCommitment(finalCommitment); inv_opt.has_value()) { + m_peer_manager->PeerRelayInv(inv_opt.value()); + } + } + handler.WaitForNextPhase(QuorumPhase::Initialized, QuorumPhase::Contribute, curQuorumHash); + return; + } + + const auto tip_mn_list = active.dmnman.GetListAtChainTip(); + utils::EnsureQuorumConnections(handler.params, active.connman, m_sporkman, + {active.dmnman, active.qsnapman, active.chainman, pQuorumBaseBlockIndex}, + tip_mn_list, curSession->ProTx(), /*is_masternode=*/true, handler.QuorumsWatch()); + if (curSession->AreWeMember()) { + utils::AddQuorumProbeConnections(handler.params, active.connman, active.mn_metaman, m_sporkman, + {active.dmnman, active.qsnapman, active.chainman, pQuorumBaseBlockIndex}, + tip_mn_list, curSession->ProTx()); + } + + handler.WaitForNextPhase(QuorumPhase::Initialized, QuorumPhase::Contribute, curQuorumHash); + + // Contribute + auto fContributeStart = [curSession, &handler]() { + if (auto qc = curSession->Contribute(); qc) { + handler.pendingContributions.PushOwnPendingMessage(*qc); + } + }; + auto fContributeWait = [this, curSession, &handler, &active] { + return ProcessPendingMessageBatch(active.connman, *curSession, handler.pendingContributions, + *m_peer_manager, 8); + }; + handler.HandlePhase(QuorumPhase::Contribute, QuorumPhase::Complain, curQuorumHash, 0.05, fContributeStart, fContributeWait); + + // Complain + auto fComplainStart = [curSession, &handler, &active]() { + if (auto qc = curSession->VerifyAndComplain(active.connman); qc) { + handler.pendingComplaints.PushOwnPendingMessage(*qc); + } + }; + auto fComplainWait = [this, curSession, &handler, &active] { + return ProcessPendingMessageBatch(active.connman, *curSession, handler.pendingComplaints, + *m_peer_manager, 8); + }; + handler.HandlePhase(QuorumPhase::Complain, QuorumPhase::Justify, curQuorumHash, 0.05, fComplainStart, fComplainWait); + + // Justify + auto fJustifyStart = [curSession, &handler]() { + if (auto qj = curSession->VerifyAndJustify(); qj) { + handler.pendingJustifications.PushOwnPendingMessage(*qj); + } + }; + auto fJustifyWait = [this, curSession, &handler, &active] { + return ProcessPendingMessageBatch(active.connman, *curSession, handler.pendingJustifications, + *m_peer_manager, 8); + }; + handler.HandlePhase(QuorumPhase::Justify, QuorumPhase::Commit, curQuorumHash, 0.05, fJustifyStart, fJustifyWait); + + // Commit + auto fCommitStart = [curSession, &handler]() { + if (auto qc = curSession->VerifyAndCommit(); qc) { + handler.pendingPrematureCommitments.PushOwnPendingMessage(*qc); + } + }; + auto fCommitWait = [this, curSession, &handler, &active] { + return ProcessPendingMessageBatch(active.connman, *curSession, + handler.pendingPrematureCommitments, + *m_peer_manager, 8); + }; + handler.HandlePhase(QuorumPhase::Commit, QuorumPhase::Finalize, curQuorumHash, 0.1, fCommitStart, fCommitWait); + + auto finalCommitments = curSession->FinalizeCommitments(); + for (const auto& fqc : finalCommitments) { + if (auto inv_opt = active.qblockman.AddMineableCommitment(fqc); inv_opt.has_value()) { + m_peer_manager->PeerRelayInv(inv_opt.value()); + } + } +} + void NetDKGStub::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv) { if (msg_type == NetMsgType::QCONTRIB || msg_type == NetMsgType::QCOMPLAINT || msg_type == NetMsgType::QJUSTIFICATION || diff --git a/src/llmq/net_dkg.h b/src/llmq/net_dkg.h index ade0b72f3fc2..f98809f0a747 100644 --- a/src/llmq/net_dkg.h +++ b/src/llmq/net_dkg.h @@ -8,6 +8,8 @@ #include #include +#include +#include class CActiveMasternodeManager; class CBLSWorker; @@ -17,6 +19,7 @@ class ChainstateManager; class CMasternodeMetaMan; class CSporkManager; namespace llmq { +class ActiveDKGSessionHandler; class CDKGDebugManager; class CDKGSessionManager; class CQuorumBlockProcessor; @@ -55,6 +58,13 @@ class NetDKG final : public NetHandler void ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv) override; bool AlreadyHave(const CInv& inv) override; bool ProcessGetData(CNode& pfrom, const CInv& inv, CConnman& connman, const CNetMsgMaker& msgMaker) override; + /** + * Drives one phase-handler thread per ActiveDKGSessionHandler in active mode; + * no-op in observer mode (no curSession to drive). + */ + void Start() override; + void Stop() override; + void Interrupt() override; private: //! Bundle of refs that exist only in active masternode mode. @@ -70,9 +80,14 @@ class NetDKG final : public NetHandler CConnman& connman; }; + void PhaseHandlerThread(ActiveDKGSessionHandler& handler); + void HandleDKGRound(ActiveDKGSessionHandler& handler); + CDKGSessionManager& m_qdkgsman; const CSporkManager& m_sporkman; const std::unique_ptr m_active; //!< null in observer mode, non-null in active mode + + std::vector m_phase_threads; }; /** diff --git a/test/lint/lint-circular-dependencies.py b/test/lint/lint-circular-dependencies.py index 96d83eddc73b..d8b01a20b90e 100755 --- a/test/lint/lint-circular-dependencies.py +++ b/test/lint/lint-circular-dependencies.py @@ -42,7 +42,6 @@ "instantsend/instantsend -> node/blockstorage -> validation -> txmempool -> instantsend/instantsend", "llmq/blockprocessor -> llmq/utils -> llmq/snapshot -> llmq/blockprocessor", "llmq/commitment -> llmq/utils -> llmq/snapshot -> llmq/commitment", - "llmq/dkgsessionhandler -> net_processing -> llmq/quorumsman -> llmq/dkgsessionmgr -> llmq/dkgsessionhandler", "masternode/payments -> validation -> masternode/payments", "net -> netmessagemaker -> net", "netaddress -> netbase -> netaddress", From 361b911ce20ff1e71f519d6bc7ae8e54399417f2 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Fri, 8 May 2026 21:59:55 +0700 Subject: [PATCH 08/12] fmt: clang formatting for moved code --- src/active/dkgsessionhandler.cpp | 5 +---- src/active/dkgsessionhandler.h | 3 ++- src/llmq/net_dkg.cpp | 21 ++++++++++++--------- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/active/dkgsessionhandler.cpp b/src/active/dkgsessionhandler.cpp index 110d787466f7..28902bc41ac6 100644 --- a/src/active/dkgsessionhandler.cpp +++ b/src/active/dkgsessionhandler.cpp @@ -66,10 +66,7 @@ void ActiveDKGSessionHandler::UpdatedBlockTip(const CBlockIndex* pindexNew) params.name, quorumIndex, currentHeight, pQuorumBaseBlockIndex->nHeight, std23::to_underlying(oldPhase), std23::to_underlying(phase)); } -uint256 ActiveDKGSessionHandler::GetCurrentQuorumHash() const -{ - return WITH_LOCK(cs_phase_qhash, return quorumHash); -} +uint256 ActiveDKGSessionHandler::GetCurrentQuorumHash() const { return WITH_LOCK(cs_phase_qhash, return quorumHash); } std::pair ActiveDKGSessionHandler::GetPhaseAndQuorumHash() const { diff --git a/src/active/dkgsessionhandler.h b/src/active/dkgsessionhandler.h index 4e48369627a7..349cab171e35 100644 --- a/src/active/dkgsessionhandler.h +++ b/src/active/dkgsessionhandler.h @@ -35,7 +35,8 @@ class CQuorumSnapshotManager; namespace llmq { //! Thrown by Wait*, Sleep* and HandlePhase to bail out of the current DKG round. -class AbortPhaseException : public std::exception { +class AbortPhaseException : public std::exception +{ }; class ActiveDKGSessionHandler final : public llmq::CDKGSessionHandler diff --git a/src/llmq/net_dkg.cpp b/src/llmq/net_dkg.cpp index fefaa43c5d84..f782be211e87 100644 --- a/src/llmq/net_dkg.cpp +++ b/src/llmq/net_dkg.cpp @@ -25,7 +25,8 @@ namespace llmq { namespace { // returns a set of NodeIds which sent invalid messages template -std::unordered_set BatchVerifyMessageSigs(CDKGSession& session, const std::vector>>& messages) +std::unordered_set BatchVerifyMessageSigs(CDKGSession& session, + const std::vector>>& messages) { if (messages.empty()) { return {}; @@ -78,9 +79,10 @@ std::unordered_set BatchVerifyMessageSigs(CDKGSession& session, const st } // 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(); + 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) { @@ -341,8 +343,8 @@ void NetDKG::HandleDKGRound(ActiveDKGSessionHandler& handler) handler.ClearPendingMessages(); uint256 curQuorumHash = handler.GetCurrentQuorumHash(); - const CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(::cs_main, - return active.chainman.m_blockman.LookupBlockIndex(curQuorumHash)); + const CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(::cs_main, return active.chainman.m_blockman.LookupBlockIndex( + curQuorumHash)); if (!pQuorumBaseBlockIndex || !handler.InitNewQuorum(pQuorumBaseBlockIndex)) { // should actually never happen @@ -386,7 +388,8 @@ void NetDKG::HandleDKGRound(ActiveDKGSessionHandler& handler) return ProcessPendingMessageBatch(active.connman, *curSession, handler.pendingContributions, *m_peer_manager, 8); }; - handler.HandlePhase(QuorumPhase::Contribute, QuorumPhase::Complain, curQuorumHash, 0.05, fContributeStart, fContributeWait); + handler.HandlePhase(QuorumPhase::Contribute, QuorumPhase::Complain, curQuorumHash, 0.05, fContributeStart, + fContributeWait); // Complain auto fComplainStart = [curSession, &handler, &active]() { @@ -420,8 +423,8 @@ void NetDKG::HandleDKGRound(ActiveDKGSessionHandler& handler) }; auto fCommitWait = [this, curSession, &handler, &active] { return ProcessPendingMessageBatch(active.connman, *curSession, - handler.pendingPrematureCommitments, - *m_peer_manager, 8); + handler.pendingPrematureCommitments, *m_peer_manager, + 8); }; handler.HandlePhase(QuorumPhase::Commit, QuorumPhase::Finalize, curQuorumHash, 0.1, fCommitStart, fCommitWait); From 9bc5895f2e7f5a6d6ec41b929b605c084a5f8c4b Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Wed, 6 May 2026 15:24:18 +0700 Subject: [PATCH 09/12] refactor: clean up unused includes and forward declarations and add missing --- src/active/dkgsession.h | 6 ++++++ src/active/dkgsessionhandler.cpp | 4 ++-- src/llmq/dkgsession.h | 6 ------ src/llmq/dkgsessionmgr.cpp | 1 - src/llmq/dkgsessionmgr.h | 6 ------ src/llmq/net_dkg.cpp | 2 -- 6 files changed, 8 insertions(+), 17 deletions(-) diff --git a/src/active/dkgsession.h b/src/active/dkgsession.h index f14d3239694d..48ca59dd7429 100644 --- a/src/active/dkgsession.h +++ b/src/active/dkgsession.h @@ -7,6 +7,12 @@ #include +#include + +class CActiveMasternodeManager; +class CSporkManager; +class CMasternodeMetaMan; + namespace llmq { class ActiveDKGSession final : public llmq::CDKGSession { diff --git a/src/active/dkgsessionhandler.cpp b/src/active/dkgsessionhandler.cpp index 28902bc41ac6..b1227527a900 100644 --- a/src/active/dkgsessionhandler.cpp +++ b/src/active/dkgsessionhandler.cpp @@ -6,14 +6,14 @@ #include #include -#include #include #include #include +#include #include #include -#include +#include namespace llmq { ActiveDKGSessionHandler::ActiveDKGSessionHandler( diff --git a/src/llmq/dkgsession.h b/src/llmq/dkgsession.h index d3ca9a934e1f..1e790e12b165 100644 --- a/src/llmq/dkgsession.h +++ b/src/llmq/dkgsession.h @@ -22,14 +22,8 @@ #include #include -class CActiveMasternodeManager; class CConnman; -class CDeterministicMN; -class CMasternodeMetaMan; -class CSporkManager; namespace llmq { -class ActiveDKGSession; -class ActiveDKGSessionHandler; class CDKGDebugManager; class CDKGSession; class CDKGSessionManager; diff --git a/src/llmq/dkgsessionmgr.cpp b/src/llmq/dkgsessionmgr.cpp index 384d397c66ff..0dd4b4012142 100644 --- a/src/llmq/dkgsessionmgr.cpp +++ b/src/llmq/dkgsessionmgr.cpp @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include diff --git a/src/llmq/dkgsessionmgr.h b/src/llmq/dkgsessionmgr.h index 6e68f239654f..4fea1a303872 100644 --- a/src/llmq/dkgsessionmgr.h +++ b/src/llmq/dkgsessionmgr.h @@ -6,7 +6,6 @@ #define BITCOIN_LLMQ_DKGSESSIONMGR_H #include -#include #include #include #include @@ -17,20 +16,17 @@ #include #include #include -#include template class CBLSIESMultiRecipientObjects; template class CBLSIESEncryptedObject; -class CActiveMasternodeManager; class CBlockIndex; class CDBWrapper; class CDeterministicMNManager; class ChainstateManager; class CNode; -class CMasternodeMetaMan; class CSporkManager; class CInv; struct MessageProcessingResult; @@ -38,8 +34,6 @@ namespace util { struct DbWrapperParams; } // namespace util -class UniValue; - namespace llmq { class CDKGComplaint; diff --git a/src/llmq/net_dkg.cpp b/src/llmq/net_dkg.cpp index f782be211e87..fd1117c5a3af 100644 --- a/src/llmq/net_dkg.cpp +++ b/src/llmq/net_dkg.cpp @@ -18,8 +18,6 @@ #include #include -#include - namespace llmq { namespace { From 090365eb2c62160a362a3d0b5d292db56a061cac Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Thu, 7 May 2026 00:28:45 +0700 Subject: [PATCH 10/12] refactor: move DKG message routing from CDKGSessionManager into NetDKG - moved implementation of ProcessMessage and AlreadyHave to NetDKG - drop usages of MessageProcessingResult in CDKGSessionManager - introduced a new helper DoForHandler --- src/active/context.cpp | 2 +- src/active/context.h | 1 - src/init.cpp | 6 +- src/llmq/dkgsessionhandler.cpp | 52 ++-------- src/llmq/dkgsessionhandler.h | 42 ++------ src/llmq/dkgsessionmgr.cpp | 119 +--------------------- src/llmq/dkgsessionmgr.h | 25 +++-- src/llmq/net_dkg.cpp | 176 +++++++++++++++++++++++++++++---- src/llmq/net_dkg.h | 20 +++- src/llmq/observer.cpp | 3 +- src/llmq/options.cpp | 5 + src/llmq/options.h | 1 + 12 files changed, 219 insertions(+), 233 deletions(-) diff --git a/src/active/context.cpp b/src/active/context.cpp index 1660af9cd57c..cb3f2d7984bd 100644 --- a/src/active/context.cpp +++ b/src/active/context.cpp @@ -38,7 +38,7 @@ ActiveContext::ActiveContext(CBLSWorker& bls_worker, ChainstateManager& chainman m_quorums_watch{quorums_watch}, nodeman{std::make_unique(connman, dmnman, operator_sk)}, dkgdbgman{std::make_unique(dmnman, qsnapman, chainman)}, - qdkgsman{std::make_unique(dmnman, qsnapman, chainman, sporkman, db_params, quorums_watch)}, + qdkgsman{std::make_unique(dmnman, qsnapman, chainman, sporkman, db_params)}, shareman{std::make_unique(connman, chainman, sigman, *nodeman, qman, sporkman)}, gov_signer{std::make_unique(connman, dmnman, govman, *nodeman, chainman, mn_sync)}, ehf_sighandler{std::make_unique(chainman, sigman, *shareman, qman)}, diff --git a/src/active/context.h b/src/active/context.h index 61bd62ebb28c..c72a33f7f09f 100644 --- a/src/active/context.h +++ b/src/active/context.h @@ -25,7 +25,6 @@ class CMNHFManager; class CSporkManager; class CTxMemPool; class GovernanceSigner; -class PeerManager; namespace chainlock { class Chainlocks; class ChainlockHandler; diff --git a/src/init.cpp b/src/init.cpp index 2ef32d38f6ef..3cdff9cbe9e4 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -2232,13 +2232,13 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) if (node.active_ctx) { node.peerman->AddExtraHandler(std::make_unique( - node.peerman.get(), *node.sporkman, *node.active_ctx->qdkgsman, + node.peerman.get(), *node.sporkman, *node.active_ctx->qdkgsman, chainman, quorums_watch, *node.llmq_ctx->bls_worker, *node.dmnman, *node.mn_metaman, *node.active_ctx->dkgdbgman, *node.llmq_ctx->quorum_block_processor, *node.llmq_ctx->qsnapman, - *node.active_ctx->nodeman, chainman, *node.connman)); + *node.active_ctx->nodeman, *node.connman)); } else if (node.observer_ctx) { node.peerman->AddExtraHandler(std::make_unique( - node.peerman.get(), *node.sporkman, *node.observer_ctx->qdkgsman)); + node.peerman.get(), *node.sporkman, *node.observer_ctx->qdkgsman, chainman, /*quorums_watch=*/true)); } else { node.peerman->AddExtraHandler(std::make_unique(node.peerman.get())); } diff --git a/src/llmq/dkgsessionhandler.cpp b/src/llmq/dkgsessionhandler.cpp index eab934219cb7..c9258ff353f9 100644 --- a/src/llmq/dkgsessionhandler.cpp +++ b/src/llmq/dkgsessionhandler.cpp @@ -4,9 +4,8 @@ #include -#include #include -#include +#include #include @@ -14,10 +13,10 @@ namespace llmq { CDKGSessionHandler::CDKGSessionHandler(const Consensus::LLMQParams& _params) : params{_params}, // 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}, - pendingJustifications{(size_t)_params.size * 2, MSG_QUORUM_JUSTIFICATION}, - pendingPrematureCommitments{(size_t)_params.size * 2, MSG_QUORUM_PREMATURE_COMMITMENT} + pendingContributions{(size_t)_params.size * 2}, + pendingComplaints{(size_t)_params.size * 2}, + pendingJustifications{(size_t)_params.size * 2}, + pendingPrematureCommitments{(size_t)_params.size * 2} { if (params.type == Consensus::LLMQType::LLMQ_NONE) { throw std::runtime_error("Can't initialize CDKGSessionHandler with LLMQ_NONE type."); @@ -26,36 +25,23 @@ CDKGSessionHandler::CDKGSessionHandler(const Consensus::LLMQParams& _params) : CDKGSessionHandler::~CDKGSessionHandler() = default; -MessageProcessingResult CDKGPendingMessages::PushPendingMessage(NodeId from, CDataStream& vRecv) +void CDKGPendingMessages::PushPendingMessage(NodeId from, std::shared_ptr pm, const uint256& hash) { - // this will also consume the data, even if we bail out early - auto pm = std::make_shared(std::move(vRecv)); - - CHashWriter hw(SER_GETHASH, 0); - hw.write(AsWritableBytes(Span{*pm})); - uint256 hash = hw.GetHash(); - - MessageProcessingResult ret{}; - if (from != -1) { - ret.m_to_erase = CInv{invType, hash}; - } - LOCK(cs_messages); if (messagesPerNode[from] >= maxMessagesPerNode) { // TODO ban? LogPrint(BCLog::LLMQ_DKG, "CDKGPendingMessages::%s -- too many messages, peer=%d\n", __func__, from); - return ret; + return; } messagesPerNode[from]++; if (!seenMessages.emplace(hash).second) { LogPrint(BCLog::LLMQ_DKG, "CDKGPendingMessages::%s -- already seen %s, peer=%d\n", __func__, hash.ToString(), from); - return ret; + return; } pendingMessages.emplace_back(std::make_pair(from, std::move(pm))); - return ret; } std::list CDKGPendingMessages::PopPendingMessages(size_t maxCount) @@ -77,11 +63,6 @@ bool CDKGPendingMessages::HasSeen(const uint256& hash) const return seenMessages.count(hash) != 0; } -void CDKGPendingMessages::PushOwnPendingMessage(CDataStream& vRecv) -{ - [[maybe_unused]] auto result = PushPendingMessage(/*from=*/-1, vRecv); -} - void CDKGPendingMessages::Clear() { LOCK(cs_messages); @@ -90,23 +71,6 @@ void CDKGPendingMessages::Clear() seenMessages.clear(); } -////// - -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 - if (msg_type == NetMsgType::QCONTRIB) { - return pendingContributions.PushPendingMessage(from, vRecv); - } else if (msg_type == NetMsgType::QCOMPLAINT) { - return pendingComplaints.PushPendingMessage(from, vRecv); - } else if (msg_type == NetMsgType::QJUSTIFICATION) { - return pendingJustifications.PushPendingMessage(from, vRecv); - } else if (msg_type == NetMsgType::QPCOMMITMENT) { - return pendingPrematureCommitments.PushPendingMessage(from, vRecv); - } - return {}; -} - void CDKGSessionHandler::ClearPendingMessages() { pendingContributions.Clear(); diff --git a/src/llmq/dkgsessionhandler.h b/src/llmq/dkgsessionhandler.h index bcb25583dc9d..be55bfcbaa8a 100644 --- a/src/llmq/dkgsessionhandler.h +++ b/src/llmq/dkgsessionhandler.h @@ -5,23 +5,19 @@ #ifndef BITCOIN_LLMQ_DKGSESSIONHANDLER_H #define BITCOIN_LLMQ_DKGSESSIONHANDLER_H -#include - #include // for NodeId -#include -#include -#include #include -#include #include #include #include +#include #include -#include #include +class CDataStream; class CBlockIndex; +class uint256; namespace Consensus { struct LLMQParams; @@ -59,7 +55,6 @@ class CDKGPendingMessages using BinaryMessage = std::pair>; private: - const uint32_t invType; const size_t maxMessagesPerNode; mutable Mutex cs_messages; std::list pendingMessages GUARDED_BY(cs_messages); @@ -67,37 +62,22 @@ class CDKGPendingMessages Uint256HashSet seenMessages GUARDED_BY(cs_messages); public: - explicit CDKGPendingMessages(size_t _maxMessagesPerNode, uint32_t _invType) : - invType(_invType), maxMessagesPerNode(_maxMessagesPerNode) {}; + explicit CDKGPendingMessages(size_t _maxMessagesPerNode) : + maxMessagesPerNode(_maxMessagesPerNode) {}; /** - * Real-peer path: enqueue a serialized DKG message received from a peer. - * The returned MessageProcessingResult conveys the erase request / misbehavior - * back up to PeerManager so DKG state stays peerman-free. + * Enqueue a serialized DKG message under @p from with content hash @p hash. + * Caller is responsible for hashing the payload and (for real peers) + * routing the erase-request to PeerManager. Drops the message silently on + * per-node capacity overflow or duplicate hash. */ - [[nodiscard]] MessageProcessingResult PushPendingMessage(NodeId from, CDataStream& vRecv) + void PushPendingMessage(NodeId from, std::shared_ptr pm, const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(!cs_messages); - /** - * Self-inject path: enqueue a DKG message we just produced ourselves under - * the conventional from=-1 NodeId. No peer-side effects are possible, so - * the inner MessageProcessingResult is unconditionally discarded. - */ - void PushOwnPendingMessage(CDataStream& vRecv) EXCLUSIVE_LOCKS_REQUIRED(!cs_messages); - std::list PopPendingMessages(size_t maxCount) EXCLUSIVE_LOCKS_REQUIRED(!cs_messages); bool HasSeen(const uint256& hash) const EXCLUSIVE_LOCKS_REQUIRED(!cs_messages); void Clear() EXCLUSIVE_LOCKS_REQUIRED(!cs_messages); - /** Self-inject convenience overload: serialize @msg and route to the binary self-inject path. */ - template - void PushOwnPendingMessage(Message& msg) EXCLUSIVE_LOCKS_REQUIRED(!cs_messages) - { - CDataStream ds(SER_NETWORK, PROTOCOL_VERSION); - ds << msg; - PushOwnPendingMessage(ds); - } - // Might return nullptr messages, which indicates that deserialization failed for some reason template std::vector>> PopAndDeserializeMessages(size_t maxCount) @@ -142,8 +122,6 @@ class CDKGSessionHandler explicit CDKGSessionHandler(const Consensus::LLMQParams& _params); virtual ~CDKGSessionHandler(); - [[nodiscard]] MessageProcessingResult ProcessMessage(NodeId from, std::string_view msg_type, CDataStream& vRecv); - void ClearPendingMessages(); public: diff --git a/src/llmq/dkgsessionmgr.cpp b/src/llmq/dkgsessionmgr.cpp index 0dd4b4012142..7540a6a1b281 100644 --- a/src/llmq/dkgsessionmgr.cpp +++ b/src/llmq/dkgsessionmgr.cpp @@ -20,11 +20,6 @@ #include #include -static bool IsQuorumDKGEnabled(const CSporkManager& sporkman) -{ - return sporkman.IsSporkActive(SPORK_17_QUORUM_DKG_ENABLED); -} - namespace llmq { static const std::string DB_VVEC = "qdkg_V"; @@ -33,12 +28,11 @@ static const std::string DB_ENC_CONTRIB = "qdkg_E"; CDKGSessionManager::CDKGSessionManager(CDeterministicMNManager& dmnman, CQuorumSnapshotManager& qsnapman, const ChainstateManager& chainman, const CSporkManager& sporkman, - const util::DbWrapperParams& db_params, bool quorums_watch) : + const util::DbWrapperParams& db_params) : m_dmnman{dmnman}, m_qsnapman{qsnapman}, m_chainman{chainman}, 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})} { } @@ -61,117 +55,6 @@ void CDKGSessionManager::UpdatedBlockTip(const CBlockIndex* pindexNew, bool fIni } } -MessageProcessingResult CDKGSessionManager::ProcessMessage(CNode& pfrom, bool is_masternode, std::string_view msg_type, - CDataStream& vRecv) -{ - static Mutex cs_indexedQuorumsCache; - static std::map> indexedQuorumsCache GUARDED_BY(cs_indexedQuorumsCache); - - if (!IsQuorumDKGEnabled(m_sporkman)) - return {}; - - if (msg_type != NetMsgType::QCONTRIB - && msg_type != NetMsgType::QCOMPLAINT - && msg_type != NetMsgType::QJUSTIFICATION - && msg_type != NetMsgType::QPCOMMITMENT - && msg_type != NetMsgType::QWATCH) { - return {}; - } - - if (msg_type == NetMsgType::QWATCH) { - if (!is_masternode) { - // non-masternodes should never receive this - return MisbehavingError{10}; - } - pfrom.qwatch = true; - return {}; - } - - if (!is_masternode && !m_quorums_watch) { - // regular non-watching nodes should never receive any of these - return MisbehavingError{10}; - } - - if (vRecv.empty()) { - return MisbehavingError{100}; - } - - Consensus::LLMQType llmqType; - uint256 quorumHash; - vRecv >> llmqType; - vRecv >> quorumHash; - vRecv.Rewind(sizeof(uint256)); - vRecv.Rewind(sizeof(uint8_t)); - - const auto& llmq_params_opt = Params().GetLLMQ(llmqType); - if (!llmq_params_opt.has_value()) { - LogPrintf("CDKGSessionManager -- invalid llmqType [%d]\n", std23::to_underlying(llmqType)); - return MisbehavingError{100}; - } - const auto& llmq_params = llmq_params_opt.value(); - - int quorumIndex{-1}; - - // First check cache - { - LOCK(cs_indexedQuorumsCache); - if (indexedQuorumsCache.empty()) { - utils::InitQuorumsCache(indexedQuorumsCache, m_chainman.GetConsensus()); - } - indexedQuorumsCache[llmqType].get(quorumHash, quorumIndex); - } - - // No luck, try to compute - if (quorumIndex == -1) { - const CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(::cs_main, - return m_chainman.m_blockman.LookupBlockIndex(quorumHash)); - if (pQuorumBaseBlockIndex == nullptr) { - LogPrintf("CDKGSessionManager -- unknown quorumHash %s\n", quorumHash.ToString()); - // NOTE: do not insta-ban for this, we might be lagging behind - return MisbehavingError{10}; - } - - if (!m_chainman.IsQuorumTypeEnabled(llmqType, pQuorumBaseBlockIndex->pprev)) { - LogPrintf("CDKGSessionManager -- llmqType [%d] quorums aren't active\n", std23::to_underlying(llmqType)); - return MisbehavingError{100}; - } - - quorumIndex = pQuorumBaseBlockIndex->nHeight % llmq_params.dkgInterval; - int quorumIndexMax = IsQuorumRotationEnabled(llmq_params, pQuorumBaseBlockIndex) ? - llmq_params.signingActiveQuorumCount - 1 : 0; - - if (quorumIndex > quorumIndexMax) { - LogPrintf("CDKGSessionManager -- invalid quorumHash %s\n", quorumHash.ToString()); - return MisbehavingError{100}; - } - - if (!dkgSessionHandlers.count({llmqType, quorumIndex})) { - LogPrintf("CDKGSessionManager -- no session handlers for quorumIndex [%d]\n", quorumIndex); - return MisbehavingError{100}; - } - } - - assert(quorumIndex != -1); - WITH_LOCK(cs_indexedQuorumsCache, indexedQuorumsCache[llmqType].insert(quorumHash, quorumIndex)); - return Assert(dkgSessionHandlers.at({llmqType, quorumIndex}))->ProcessMessage(pfrom.GetId(), msg_type, vRecv); -} - -bool CDKGSessionManager::AlreadyHave(const CInv& inv) const -{ - if (!IsQuorumDKGEnabled(m_sporkman)) - return false; - - 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; - } - } - return false; -} - bool CDKGSessionManager::GetContribution(const uint256& hash, CDKGContribution& ret) const { if (!IsQuorumDKGEnabled(m_sporkman)) diff --git a/src/llmq/dkgsessionmgr.h b/src/llmq/dkgsessionmgr.h index 4fea1a303872..374a194a5a23 100644 --- a/src/llmq/dkgsessionmgr.h +++ b/src/llmq/dkgsessionmgr.h @@ -8,14 +8,12 @@ #include #include #include -#include #include #include #include #include #include -#include template class CBLSIESMultiRecipientObjects; @@ -26,9 +24,7 @@ class CBlockIndex; class CDBWrapper; class CDeterministicMNManager; class ChainstateManager; -class CNode; class CSporkManager; -class CInv; struct MessageProcessingResult; namespace util { struct DbWrapperParams; @@ -67,7 +63,6 @@ class CDKGSessionManager CQuorumSnapshotManager& m_qsnapman; const ChainstateManager& m_chainman; const CSporkManager& m_sporkman; - const bool m_quorums_watch{false}; private: std::unique_ptr db{nullptr}; @@ -99,7 +94,7 @@ class CDKGSessionManager CDKGSessionManager& operator=(const CDKGSessionManager&) = delete; explicit CDKGSessionManager(CDeterministicMNManager& dmnman, CQuorumSnapshotManager& qsnapman, const ChainstateManager& chainman, const CSporkManager& sporkman, - const util::DbWrapperParams& db_params, bool quorums_watch); + const util::DbWrapperParams& db_params); ~CDKGSessionManager(); template @@ -127,12 +122,24 @@ class CDKGSessionManager } } + /** + * Invoke @p fn(CDKGSessionHandler&) for the handler registered under @p key, + * if one exists. Returns true iff the handler was found (and the callback ran). + * Returning the handler by reference inside the callback prevents callers from + * keeping a dangling pointer outside the handler's lifetime. + */ + template + bool DoForHandler(const SessionHandlerKey& key, HandlerFn&& fn) + { + auto it = dkgSessionHandlers.find(key); + if (it == dkgSessionHandlers.end()) return false; + fn(*Assert(it->second)); + return true; + } + void UpdatedBlockTip(const CBlockIndex* pindexNew, bool fInitialDownload) EXCLUSIVE_LOCKS_REQUIRED(!contributionsCacheCs); - [[nodiscard]] MessageProcessingResult ProcessMessage(CNode& pfrom, bool is_masternode, std::string_view msg_type, - CDataStream& vRecv); - bool AlreadyHave(const CInv& inv) const; bool GetContribution(const uint256& hash, CDKGContribution& ret) const; bool GetComplaint(const uint256& hash, CDKGComplaint& ret) const; bool GetJustification(const uint256& hash, CDKGJustification& ret) const; diff --git a/src/llmq/net_dkg.cpp b/src/llmq/net_dkg.cpp index fd1117c5a3af..764281f84a66 100644 --- a/src/llmq/net_dkg.cpp +++ b/src/llmq/net_dkg.cpp @@ -5,15 +5,20 @@ #include #include +#include #include +#include #include #include #include #include +#include #include #include #include #include +#include +#include #include #include #include @@ -137,6 +142,17 @@ void RelayInvToParticipants(const CDKGSession& session, const CConnman& connman, logger.Flush(); } +template +void EnqueueOwn(CDKGPendingMessages& pending, const Message& msg) +{ + CDataStream ds(SER_NETWORK, PROTOCOL_VERSION); + ds << msg; + auto pm = std::make_shared(std::move(ds)); + CHashWriter hw(SER_GETHASH, 0); + hw.write(AsWritableBytes(Span{*pm})); + pending.PushPendingMessage(/*from=*/-1, std::move(pm), hw.GetHash()); +} + template bool ProcessPendingMessageBatch(const CConnman& connman, CDKGSession& session, CDKGPendingMessages& pendingMessages, PeerManagerInternal& peerman, size_t maxCount) @@ -195,35 +211,147 @@ bool ProcessPendingMessageBatch(const CConnman& connman, CDKGSession& session, C } // namespace -NetDKG::NetDKG(PeerManagerInternal* peer_manager, const CSporkManager& sporkman, CDKGSessionManager& qdkgsman) : +NetDKG::NetDKG(PeerManagerInternal* peer_manager, const CSporkManager& sporkman, CDKGSessionManager& qdkgsman, + const ChainstateManager& chainman, bool quorums_watch) : NetHandler(peer_manager), m_qdkgsman{qdkgsman}, m_sporkman{sporkman}, + m_chainman{chainman}, + m_quorums_watch{quorums_watch}, m_active{nullptr} { } NetDKG::NetDKG(PeerManagerInternal* peer_manager, const CSporkManager& sporkman, CDKGSessionManager& qdkgsman, + const ChainstateManager& chainman, bool quorums_watch, CBLSWorker& bls_worker, CDeterministicMNManager& dmnman, CMasternodeMetaMan& mn_metaman, CDKGDebugManager& dkgdbgman, CQuorumBlockProcessor& qblockman, CQuorumSnapshotManager& qsnapman, - const CActiveMasternodeManager& mn_activeman, const ChainstateManager& chainman, CConnman& connman) : + const CActiveMasternodeManager& mn_activeman, CConnman& connman) : NetHandler(peer_manager), m_qdkgsman{qdkgsman}, m_sporkman{sporkman}, - m_active{std::make_unique( - ActiveDKG{bls_worker, dmnman, mn_metaman, dkgdbgman, qblockman, qsnapman, mn_activeman, chainman, connman})} + m_chainman{chainman}, + m_quorums_watch{quorums_watch}, + m_active{std::make_unique(ActiveDKG{bls_worker, dmnman, mn_metaman, dkgdbgman, qblockman, qsnapman, + mn_activeman, connman})} { } void NetDKG::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv) { - auto result = m_qdkgsman.ProcessMessage(pfrom, /*is_masternode=*/m_active != nullptr, msg_type, vRecv); - if (result.m_error) { - m_peer_manager->PeerMisbehaving(pfrom.GetId(), result.m_error->score, result.m_error->message); + if (!IsQuorumDKGEnabled(m_sporkman)) return; + + if (msg_type != NetMsgType::QCONTRIB && msg_type != NetMsgType::QCOMPLAINT + && msg_type != NetMsgType::QJUSTIFICATION && msg_type != NetMsgType::QPCOMMITMENT + && msg_type != NetMsgType::QWATCH) { + return; + } + + const bool is_masternode = m_active != nullptr; + + if (msg_type == NetMsgType::QWATCH) { + if (!is_masternode) { + // non-masternodes should never receive this + m_peer_manager->PeerMisbehaving(pfrom.GetId(), 10); + return; + } + pfrom.qwatch = true; + return; + } + + if (!is_masternode && !m_quorums_watch) { + // regular non-watching nodes should never receive any of these + m_peer_manager->PeerMisbehaving(pfrom.GetId(), 10); + return; } - if (result.m_to_erase) { - WITH_LOCK(::cs_main, m_peer_manager->PeerEraseObjectRequest(pfrom.GetId(), *result.m_to_erase)); + + if (vRecv.empty()) { + m_peer_manager->PeerMisbehaving(pfrom.GetId(), 100); + return; } + + Consensus::LLMQType llmqType; + uint256 quorumHash; + vRecv >> llmqType; + vRecv >> quorumHash; + vRecv.Rewind(sizeof(uint256)); + vRecv.Rewind(sizeof(uint8_t)); + + const auto& llmq_params_opt = Params().GetLLMQ(llmqType); + if (!llmq_params_opt.has_value()) { + LogPrintf("NetDKG -- invalid llmqType [%d]\n", std23::to_underlying(llmqType)); + m_peer_manager->PeerMisbehaving(pfrom.GetId(), 100); + return; + } + const auto& llmq_params = llmq_params_opt.value(); + + int quorumIndex{-1}; + { + LOCK(cs_indexed_quorums_cache); + if (indexed_quorums_cache.empty()) { + utils::InitQuorumsCache(indexed_quorums_cache, m_chainman.GetConsensus()); + } + indexed_quorums_cache[llmqType].get(quorumHash, quorumIndex); + } + + if (quorumIndex == -1) { + const CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(::cs_main, + return m_chainman.m_blockman.LookupBlockIndex(quorumHash)); + if (pQuorumBaseBlockIndex == nullptr) { + LogPrintf("NetDKG -- unknown quorumHash %s\n", quorumHash.ToString()); + // NOTE: do not insta-ban for this, we might be lagging behind + m_peer_manager->PeerMisbehaving(pfrom.GetId(), 10); + return; + } + if (!m_chainman.IsQuorumTypeEnabled(llmqType, pQuorumBaseBlockIndex->pprev)) { + LogPrintf("NetDKG -- llmqType [%d] quorums aren't active\n", std23::to_underlying(llmqType)); + m_peer_manager->PeerMisbehaving(pfrom.GetId(), 100); + return; + } + quorumIndex = pQuorumBaseBlockIndex->nHeight % llmq_params.dkgInterval; + const int quorumIndexMax = IsQuorumRotationEnabled(llmq_params, pQuorumBaseBlockIndex) + ? llmq_params.signingActiveQuorumCount - 1 + : 0; + if (quorumIndex > quorumIndexMax) { + LogPrintf("NetDKG -- invalid quorumHash %s\n", quorumHash.ToString()); + m_peer_manager->PeerMisbehaving(pfrom.GetId(), 100); + return; + } + } + + int inv_type = 0; + if (msg_type == NetMsgType::QCONTRIB) inv_type = MSG_QUORUM_CONTRIB; + else if (msg_type == NetMsgType::QCOMPLAINT) inv_type = MSG_QUORUM_COMPLAINT; + else if (msg_type == NetMsgType::QJUSTIFICATION) inv_type = MSG_QUORUM_JUSTIFICATION; + else if (msg_type == NetMsgType::QPCOMMITMENT) inv_type = MSG_QUORUM_PREMATURE_COMMITMENT; + Assume(inv_type != 0); // guarded by the early-return above + + auto pm = std::make_shared(std::move(vRecv)); + CHashWriter hw(SER_GETHASH, 0); + hw.write(AsWritableBytes(Span{*pm})); + const uint256 hash = hw.GetHash(); + + const NodeId from = pfrom.GetId(); + const bool dispatched = m_qdkgsman.DoForHandler({llmqType, quorumIndex}, + [&](CDKGSessionHandler& handler) { + CDKGPendingMessages* pending = nullptr; + switch (inv_type) { + case MSG_QUORUM_CONTRIB: pending = &handler.pendingContributions; break; + case MSG_QUORUM_COMPLAINT: pending = &handler.pendingComplaints; break; + case MSG_QUORUM_JUSTIFICATION: pending = &handler.pendingJustifications; break; + case MSG_QUORUM_PREMATURE_COMMITMENT: pending = &handler.pendingPrematureCommitments; break; + } + Assume(pending != nullptr); + WITH_LOCK(::cs_main, m_peer_manager->PeerEraseObjectRequest(from, CInv{static_cast(inv_type), hash})); + pending->PushPendingMessage(from, std::move(pm), hash); + }); + if (!dispatched) { + LogPrintf("NetDKG -- no session handlers for quorumIndex [%d]\n", quorumIndex); + m_peer_manager->PeerMisbehaving(pfrom.GetId(), 100); + return; + } + + WITH_LOCK(cs_indexed_quorums_cache, indexed_quorums_cache[llmqType].insert(quorumHash, quorumIndex)); } bool NetDKG::AlreadyHave(const CInv& inv) @@ -232,8 +360,18 @@ bool NetDKG::AlreadyHave(const CInv& inv) case MSG_QUORUM_CONTRIB: case MSG_QUORUM_COMPLAINT: case MSG_QUORUM_JUSTIFICATION: - case MSG_QUORUM_PREMATURE_COMMITMENT: - return m_qdkgsman.AlreadyHave(inv); + case MSG_QUORUM_PREMATURE_COMMITMENT: { + if (!IsQuorumDKGEnabled(m_sporkman)) return false; + bool seen = false; + m_qdkgsman.ForEachHandler([&](CDKGSessionHandler& h) { + if (seen) return; + if (h.pendingContributions.HasSeen(inv.hash) || h.pendingComplaints.HasSeen(inv.hash) + || h.pendingJustifications.HasSeen(inv.hash) || h.pendingPrematureCommitments.HasSeen(inv.hash)) { + seen = true; + } + }); + return seen; + } } return false; } @@ -341,8 +479,8 @@ void NetDKG::HandleDKGRound(ActiveDKGSessionHandler& handler) handler.ClearPendingMessages(); uint256 curQuorumHash = handler.GetCurrentQuorumHash(); - const CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(::cs_main, return active.chainman.m_blockman.LookupBlockIndex( - curQuorumHash)); + const CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(::cs_main, + return m_chainman.m_blockman.LookupBlockIndex(curQuorumHash)); if (!pQuorumBaseBlockIndex || !handler.InitNewQuorum(pQuorumBaseBlockIndex)) { // should actually never happen @@ -366,11 +504,11 @@ void NetDKG::HandleDKGRound(ActiveDKGSessionHandler& handler) const auto tip_mn_list = active.dmnman.GetListAtChainTip(); utils::EnsureQuorumConnections(handler.params, active.connman, m_sporkman, - {active.dmnman, active.qsnapman, active.chainman, pQuorumBaseBlockIndex}, + {active.dmnman, active.qsnapman, m_chainman, pQuorumBaseBlockIndex}, tip_mn_list, curSession->ProTx(), /*is_masternode=*/true, handler.QuorumsWatch()); if (curSession->AreWeMember()) { utils::AddQuorumProbeConnections(handler.params, active.connman, active.mn_metaman, m_sporkman, - {active.dmnman, active.qsnapman, active.chainman, pQuorumBaseBlockIndex}, + {active.dmnman, active.qsnapman, m_chainman, pQuorumBaseBlockIndex}, tip_mn_list, curSession->ProTx()); } @@ -379,7 +517,7 @@ void NetDKG::HandleDKGRound(ActiveDKGSessionHandler& handler) // Contribute auto fContributeStart = [curSession, &handler]() { if (auto qc = curSession->Contribute(); qc) { - handler.pendingContributions.PushOwnPendingMessage(*qc); + EnqueueOwn(handler.pendingContributions, *qc); } }; auto fContributeWait = [this, curSession, &handler, &active] { @@ -392,7 +530,7 @@ void NetDKG::HandleDKGRound(ActiveDKGSessionHandler& handler) // Complain auto fComplainStart = [curSession, &handler, &active]() { if (auto qc = curSession->VerifyAndComplain(active.connman); qc) { - handler.pendingComplaints.PushOwnPendingMessage(*qc); + EnqueueOwn(handler.pendingComplaints, *qc); } }; auto fComplainWait = [this, curSession, &handler, &active] { @@ -404,7 +542,7 @@ void NetDKG::HandleDKGRound(ActiveDKGSessionHandler& handler) // Justify auto fJustifyStart = [curSession, &handler]() { if (auto qj = curSession->VerifyAndJustify(); qj) { - handler.pendingJustifications.PushOwnPendingMessage(*qj); + EnqueueOwn(handler.pendingJustifications, *qj); } }; auto fJustifyWait = [this, curSession, &handler, &active] { @@ -416,7 +554,7 @@ void NetDKG::HandleDKGRound(ActiveDKGSessionHandler& handler) // Commit auto fCommitStart = [curSession, &handler]() { if (auto qc = curSession->VerifyAndCommit(); qc) { - handler.pendingPrematureCommitments.PushOwnPendingMessage(*qc); + EnqueueOwn(handler.pendingPrematureCommitments, *qc); } }; auto fCommitWait = [this, curSession, &handler, &active] { diff --git a/src/llmq/net_dkg.h b/src/llmq/net_dkg.h index f98809f0a747..42e52ba367a1 100644 --- a/src/llmq/net_dkg.h +++ b/src/llmq/net_dkg.h @@ -5,8 +5,13 @@ #ifndef BITCOIN_LLMQ_NET_DKG_H #define BITCOIN_LLMQ_NET_DKG_H +#include #include +#include +#include +#include +#include #include #include #include @@ -46,16 +51,18 @@ class NetDKG final : public NetHandler { public: //! Observer-mode constructor. - NetDKG(PeerManagerInternal* peer_manager, const CSporkManager& sporkman, CDKGSessionManager& qdkgsman); + NetDKG(PeerManagerInternal* peer_manager, const CSporkManager& sporkman, CDKGSessionManager& qdkgsman, + const ChainstateManager& chainman, bool quorums_watch); //! Active-mode constructor: takes the masternode-only dep bundle as required references. NetDKG(PeerManagerInternal* peer_manager, const CSporkManager& sporkman, CDKGSessionManager& qdkgsman, + const ChainstateManager& chainman, bool quorums_watch, CBLSWorker& bls_worker, CDeterministicMNManager& dmnman, CMasternodeMetaMan& mn_metaman, CDKGDebugManager& dkgdbgman, CQuorumBlockProcessor& qblockman, CQuorumSnapshotManager& qsnapman, - const CActiveMasternodeManager& mn_activeman, const ChainstateManager& chainman, CConnman& connman); + const CActiveMasternodeManager& mn_activeman, CConnman& connman); // NetHandler - void ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv) override; + void ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv) override EXCLUSIVE_LOCKS_REQUIRED(!cs_indexed_quorums_cache); bool AlreadyHave(const CInv& inv) override; bool ProcessGetData(CNode& pfrom, const CInv& inv, CConnman& connman, const CNetMsgMaker& msgMaker) override; /** @@ -76,7 +83,6 @@ class NetDKG final : public NetHandler CQuorumBlockProcessor& qblockman; CQuorumSnapshotManager& qsnapman; const CActiveMasternodeManager& mn_activeman; - const ChainstateManager& chainman; CConnman& connman; }; @@ -85,8 +91,14 @@ class NetDKG final : public NetHandler CDKGSessionManager& m_qdkgsman; const CSporkManager& m_sporkman; + const ChainstateManager& m_chainman; + const bool m_quorums_watch; const std::unique_ptr m_active; //!< null in observer mode, non-null in active mode + /** Cache: quorum hash → quorum index, populated lazily by ProcessMessage. */ + mutable Mutex cs_indexed_quorums_cache; + mutable std::map> indexed_quorums_cache GUARDED_BY(cs_indexed_quorums_cache); + std::vector m_phase_threads; }; diff --git a/src/llmq/observer.cpp b/src/llmq/observer.cpp index 5adaa8326bf0..06ab70e2e209 100644 --- a/src/llmq/observer.cpp +++ b/src/llmq/observer.cpp @@ -20,8 +20,7 @@ ObserverContext::ObserverContext(CBLSWorker& bls_worker, CDeterministicMNManager const CSporkManager& sporkman, const util::DbWrapperParams& db_params) : QuorumRole{qman}, dkgdbgman{std::make_unique(dmnman, qsnapman, chainman)}, - qdkgsman{std::make_unique(dmnman, qsnapman, chainman, sporkman, db_params, - /*quorums_watch=*/true)} + qdkgsman{std::make_unique(dmnman, qsnapman, chainman, sporkman, db_params)} { qdkgsman->InitializeHandlers([&](const Consensus::LLMQParams& llmq_params, [[maybe_unused]] int quorum_idx) -> std::unique_ptr { diff --git a/src/llmq/options.cpp b/src/llmq/options.cpp index f6cad7966ba8..f9ad56e2b0ce 100644 --- a/src/llmq/options.cpp +++ b/src/llmq/options.cpp @@ -39,6 +39,11 @@ bool IsAllMembersConnectedEnabled(const Consensus::LLMQType llmqType, const CSpo return EvalSpork(llmqType, sporkman.GetSporkValue(SPORK_21_QUORUM_ALL_CONNECTED)); } +bool IsQuorumDKGEnabled(const CSporkManager& sporkman) +{ + return sporkman.IsSporkActive(SPORK_17_QUORUM_DKG_ENABLED); +} + bool IsQuorumPoseEnabled(const Consensus::LLMQType llmqType, const CSporkManager& sporkman) { return EvalSpork(llmqType, sporkman.GetSporkValue(SPORK_23_QUORUM_POSE)); diff --git a/src/llmq/options.h b/src/llmq/options.h index e546190301d2..9973472d0e3b 100644 --- a/src/llmq/options.h +++ b/src/llmq/options.h @@ -39,6 +39,7 @@ extern int16_t DEFAULT_WORKER_COUNT; static constexpr int8_t MAX_BLSCHECK_THREADS{33}; bool IsAllMembersConnectedEnabled(const Consensus::LLMQType llmqType, const CSporkManager& sporkman); +bool IsQuorumDKGEnabled(const CSporkManager& sporkman); bool IsQuorumPoseEnabled(const Consensus::LLMQType llmqType, const CSporkManager& sporkman); bool IsQuorumRotationEnabled(const Consensus::LLMQParams& llmqParams, gsl::not_null pindex); From 1cfb37fe469d7ed407aee83aee96e588700df636 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Thu, 7 May 2026 01:22:05 +0700 Subject: [PATCH 11/12] refactor: move DKG bootstrap from ActiveContext/ObserverContext into NetDKG --- src/active/context.cpp | 24 +++--------- src/active/context.h | 12 +++--- src/init.cpp | 11 +++--- src/llmq/net_dkg.cpp | 84 +++++++++++++++++++++++++++--------------- src/llmq/net_dkg.h | 13 ++++--- src/llmq/observer.cpp | 18 +-------- src/llmq/observer.h | 12 ++---- src/llmq/options.cpp | 5 +-- 8 files changed, 86 insertions(+), 93 deletions(-) diff --git a/src/active/context.cpp b/src/active/context.cpp index cb3f2d7984bd..5a05df672e22 100644 --- a/src/active/context.cpp +++ b/src/active/context.cpp @@ -4,7 +4,6 @@ #include -#include #include #include #include @@ -26,13 +25,12 @@ #include ActiveContext::ActiveContext(CBLSWorker& bls_worker, ChainstateManager& chainman, CConnman& connman, - CDeterministicMNManager& dmnman, CGovernanceManager& govman, CMasternodeMetaMan& mn_metaman, - CSporkManager& sporkman, const chainlock::Chainlocks& chainlocks, CTxMemPool& mempool, + CDeterministicMNManager& dmnman, CGovernanceManager& govman, CSporkManager& sporkman, + const chainlock::Chainlocks& chainlocks, CTxMemPool& mempool, chainlock::ChainlockHandler& clhandler, llmq::CInstantSendManager& isman, - llmq::CQuorumBlockProcessor& qblockman, llmq::CQuorumManager& qman, - llmq::CQuorumSnapshotManager& qsnapman, llmq::CSigningManager& sigman, - const CMasternodeSync& mn_sync, const CBLSSecretKey& operator_sk, - const util::DbWrapperParams& db_params, bool quorums_watch) : + llmq::CQuorumManager& qman, llmq::CQuorumSnapshotManager& qsnapman, + llmq::CSigningManager& sigman, const CMasternodeSync& mn_sync, + const CBLSSecretKey& operator_sk, const util::DbWrapperParams& db_params, bool quorums_watch) : llmq::QuorumRole{qman}, m_bls_worker{bls_worker}, m_quorums_watch{quorums_watch}, @@ -47,19 +45,9 @@ ActiveContext::ActiveContext(CBLSWorker& bls_worker, ChainstateManager& chainman is_signer{std::make_unique(chainman.ActiveChainstate(), chainlocks, isman, sigman, *shareman, qman, sporkman, mempool, mn_sync)} { - qdkgsman->InitializeHandlers([&](const Consensus::LLMQParams& llmq_params, - int quorum_idx) -> std::unique_ptr { - return std::make_unique(bls_worker, dmnman, mn_metaman, *dkgdbgman, *qdkgsman, - qblockman, qsnapman, *nodeman, chainman, sporkman, - llmq_params, quorums_watch, quorum_idx); - }); - m_qman.ConnectManagers(this, qdkgsman.get()); } -ActiveContext::~ActiveContext() -{ - m_qman.DisconnectManagers(); -} +ActiveContext::~ActiveContext() = default; void ActiveContext::Start() { diff --git a/src/active/context.h b/src/active/context.h index c72a33f7f09f..a7d55dc87e66 100644 --- a/src/active/context.h +++ b/src/active/context.h @@ -19,7 +19,6 @@ class CBLSWorker; class CCoinJoinServer; class CConnman; class CGovernanceManager; -class CMasternodeMetaMan; class CMasternodeSync; class CMNHFManager; class CSporkManager; @@ -54,13 +53,12 @@ struct ActiveContext final : public llmq::QuorumRole, public CValidationInterfac ActiveContext(const ActiveContext&) = delete; ActiveContext& operator=(const ActiveContext&) = delete; explicit ActiveContext(CBLSWorker& bls_worker, ChainstateManager& chainman, CConnman& connman, - CDeterministicMNManager& dmnman, CGovernanceManager& govman, CMasternodeMetaMan& mn_metaman, - CSporkManager& sporkman, const chainlock::Chainlocks& chainlocks, CTxMemPool& mempool, + CDeterministicMNManager& dmnman, CGovernanceManager& govman, CSporkManager& sporkman, + const chainlock::Chainlocks& chainlocks, CTxMemPool& mempool, chainlock::ChainlockHandler& clhandler, llmq::CInstantSendManager& isman, - llmq::CQuorumBlockProcessor& qblockman, llmq::CQuorumManager& qman, - llmq::CQuorumSnapshotManager& qsnapman, llmq::CSigningManager& sigman, - const CMasternodeSync& mn_sync, const CBLSSecretKey& operator_sk, - const util::DbWrapperParams& db_params, bool quorums_watch); + llmq::CQuorumManager& qman, llmq::CQuorumSnapshotManager& qsnapman, + llmq::CSigningManager& sigman, const CMasternodeSync& mn_sync, + const CBLSSecretKey& operator_sk, const util::DbWrapperParams& db_params, bool quorums_watch); ~ActiveContext(); void Start(); diff --git a/src/init.cpp b/src/init.cpp index 3cdff9cbe9e4..b31dfb9c426a 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -2188,14 +2188,13 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) return InitError(_("Invalid masternodeblsprivkey. Please see documentation.")); } // Will init later in ThreadImport - node.active_ctx = std::make_unique(*node.llmq_ctx->bls_worker, chainman, *node.connman, *node.dmnman, *node.govman, *node.mn_metaman, + node.active_ctx = std::make_unique(*node.llmq_ctx->bls_worker, chainman, *node.connman, *node.dmnman, *node.govman, *node.sporkman, *node.chainlocks, *node.mempool, *node.clhandler, *node.llmq_ctx->isman, - *node.llmq_ctx->quorum_block_processor, *node.llmq_ctx->qman, *node.llmq_ctx->qsnapman, *node.llmq_ctx->sigman, + *node.llmq_ctx->qman, *node.llmq_ctx->qsnapman, *node.llmq_ctx->sigman, *node.mn_sync, operator_sk, dash_db_params, quorums_watch); RegisterValidationInterface(node.active_ctx.get()); } else if (quorums_watch) { - node.observer_ctx = std::make_unique(*node.llmq_ctx->bls_worker, *node.dmnman, *node.mn_metaman, - *node.llmq_ctx->quorum_block_processor, *node.llmq_ctx->qman, *node.llmq_ctx->qsnapman, + node.observer_ctx = std::make_unique(*node.dmnman, *node.llmq_ctx->qman, *node.llmq_ctx->qsnapman, chainman, *node.sporkman, dash_db_params); RegisterValidationInterface(node.observer_ctx.get()); } @@ -2233,12 +2232,14 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) if (node.active_ctx) { node.peerman->AddExtraHandler(std::make_unique( node.peerman.get(), *node.sporkman, *node.active_ctx->qdkgsman, chainman, quorums_watch, + *node.llmq_ctx->qman, *node.active_ctx, *node.llmq_ctx->bls_worker, *node.dmnman, *node.mn_metaman, *node.active_ctx->dkgdbgman, *node.llmq_ctx->quorum_block_processor, *node.llmq_ctx->qsnapman, *node.active_ctx->nodeman, *node.connman)); } else if (node.observer_ctx) { node.peerman->AddExtraHandler(std::make_unique( - node.peerman.get(), *node.sporkman, *node.observer_ctx->qdkgsman, chainman, /*quorums_watch=*/true)); + node.peerman.get(), *node.sporkman, *node.observer_ctx->qdkgsman, chainman, /*quorums_watch=*/true, + *node.llmq_ctx->qman, *node.observer_ctx)); } else { node.peerman->AddExtraHandler(std::make_unique(node.peerman.get())); } diff --git a/src/llmq/net_dkg.cpp b/src/llmq/net_dkg.cpp index 764281f84a66..0f186379f37a 100644 --- a/src/llmq/net_dkg.cpp +++ b/src/llmq/net_dkg.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -212,38 +213,52 @@ bool ProcessPendingMessageBatch(const CConnman& connman, CDKGSession& session, C NetDKG::NetDKG(PeerManagerInternal* peer_manager, const CSporkManager& sporkman, CDKGSessionManager& qdkgsman, - const ChainstateManager& chainman, bool quorums_watch) : + const ChainstateManager& chainman, bool quorums_watch, CQuorumManager& qman, QuorumRole& role) : NetHandler(peer_manager), m_qdkgsman{qdkgsman}, + m_qman{qman}, m_sporkman{sporkman}, m_chainman{chainman}, m_quorums_watch{quorums_watch}, m_active{nullptr} { + m_qdkgsman.InitializeHandlers([](const Consensus::LLMQParams& llmq_params, + [[maybe_unused]] int quorum_idx) -> std::unique_ptr { + return std::make_unique(llmq_params); + }); + m_qman.ConnectManagers(&role, &m_qdkgsman); } NetDKG::NetDKG(PeerManagerInternal* peer_manager, const CSporkManager& sporkman, CDKGSessionManager& qdkgsman, - const ChainstateManager& chainman, bool quorums_watch, + const ChainstateManager& chainman, bool quorums_watch, CQuorumManager& qman, QuorumRole& role, CBLSWorker& bls_worker, CDeterministicMNManager& dmnman, CMasternodeMetaMan& mn_metaman, CDKGDebugManager& dkgdbgman, CQuorumBlockProcessor& qblockman, CQuorumSnapshotManager& qsnapman, const CActiveMasternodeManager& mn_activeman, CConnman& connman) : NetHandler(peer_manager), m_qdkgsman{qdkgsman}, + m_qman{qman}, m_sporkman{sporkman}, m_chainman{chainman}, m_quorums_watch{quorums_watch}, - m_active{std::make_unique(ActiveDKG{bls_worker, dmnman, mn_metaman, dkgdbgman, qblockman, qsnapman, - mn_activeman, connman})} + m_active{std::make_unique(ActiveDKG{dmnman, mn_metaman, dkgdbgman, qblockman, qsnapman, connman})} { + m_qdkgsman.InitializeHandlers( + [&](const Consensus::LLMQParams& llmq_params, int quorum_idx) -> std::unique_ptr { + return std::make_unique(bls_worker, dmnman, mn_metaman, dkgdbgman, qdkgsman, + qblockman, qsnapman, mn_activeman, chainman, sporkman, + llmq_params, quorums_watch, quorum_idx); + }); + m_qman.ConnectManagers(&role, &m_qdkgsman); } +NetDKG::~NetDKG() { m_qman.DisconnectManagers(); } + void NetDKG::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv) { if (!IsQuorumDKGEnabled(m_sporkman)) return; - if (msg_type != NetMsgType::QCONTRIB && msg_type != NetMsgType::QCOMPLAINT - && msg_type != NetMsgType::QJUSTIFICATION && msg_type != NetMsgType::QPCOMMITMENT - && msg_type != NetMsgType::QWATCH) { + if (msg_type != NetMsgType::QCONTRIB && msg_type != NetMsgType::QCOMPLAINT && msg_type != NetMsgType::QJUSTIFICATION && + msg_type != NetMsgType::QPCOMMITMENT && msg_type != NetMsgType::QWATCH) { return; } @@ -296,7 +311,7 @@ void NetDKG::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStre if (quorumIndex == -1) { const CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(::cs_main, - return m_chainman.m_blockman.LookupBlockIndex(quorumHash)); + return m_chainman.m_blockman.LookupBlockIndex(quorumHash)); if (pQuorumBaseBlockIndex == nullptr) { LogPrintf("NetDKG -- unknown quorumHash %s\n", quorumHash.ToString()); // NOTE: do not insta-ban for this, we might be lagging behind @@ -320,10 +335,14 @@ void NetDKG::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStre } int inv_type = 0; - if (msg_type == NetMsgType::QCONTRIB) inv_type = MSG_QUORUM_CONTRIB; - else if (msg_type == NetMsgType::QCOMPLAINT) inv_type = MSG_QUORUM_COMPLAINT; - else if (msg_type == NetMsgType::QJUSTIFICATION) inv_type = MSG_QUORUM_JUSTIFICATION; - else if (msg_type == NetMsgType::QPCOMMITMENT) inv_type = MSG_QUORUM_PREMATURE_COMMITMENT; + if (msg_type == NetMsgType::QCONTRIB) + inv_type = MSG_QUORUM_CONTRIB; + else if (msg_type == NetMsgType::QCOMPLAINT) + inv_type = MSG_QUORUM_COMPLAINT; + else if (msg_type == NetMsgType::QJUSTIFICATION) + inv_type = MSG_QUORUM_JUSTIFICATION; + else if (msg_type == NetMsgType::QPCOMMITMENT) + inv_type = MSG_QUORUM_PREMATURE_COMMITMENT; Assume(inv_type != 0); // guarded by the early-return above auto pm = std::make_shared(std::move(vRecv)); @@ -332,19 +351,26 @@ void NetDKG::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStre const uint256 hash = hw.GetHash(); const NodeId from = pfrom.GetId(); - const bool dispatched = m_qdkgsman.DoForHandler({llmqType, quorumIndex}, - [&](CDKGSessionHandler& handler) { - CDKGPendingMessages* pending = nullptr; - switch (inv_type) { - case MSG_QUORUM_CONTRIB: pending = &handler.pendingContributions; break; - case MSG_QUORUM_COMPLAINT: pending = &handler.pendingComplaints; break; - case MSG_QUORUM_JUSTIFICATION: pending = &handler.pendingJustifications; break; - case MSG_QUORUM_PREMATURE_COMMITMENT: pending = &handler.pendingPrematureCommitments; break; - } - Assume(pending != nullptr); - WITH_LOCK(::cs_main, m_peer_manager->PeerEraseObjectRequest(from, CInv{static_cast(inv_type), hash})); - pending->PushPendingMessage(from, std::move(pm), hash); - }); + const bool dispatched = m_qdkgsman.DoForHandler({llmqType, quorumIndex}, [&](CDKGSessionHandler& handler) { + CDKGPendingMessages* pending = nullptr; + switch (inv_type) { + case MSG_QUORUM_CONTRIB: + pending = &handler.pendingContributions; + break; + case MSG_QUORUM_COMPLAINT: + pending = &handler.pendingComplaints; + break; + case MSG_QUORUM_JUSTIFICATION: + pending = &handler.pendingJustifications; + break; + case MSG_QUORUM_PREMATURE_COMMITMENT: + pending = &handler.pendingPrematureCommitments; + break; + } + Assume(pending != nullptr); + WITH_LOCK(::cs_main, m_peer_manager->PeerEraseObjectRequest(from, CInv{static_cast(inv_type), hash})); + pending->PushPendingMessage(from, std::move(pm), hash); + }); if (!dispatched) { LogPrintf("NetDKG -- no session handlers for quorumIndex [%d]\n", quorumIndex); m_peer_manager->PeerMisbehaving(pfrom.GetId(), 100); @@ -365,8 +391,8 @@ bool NetDKG::AlreadyHave(const CInv& inv) bool seen = false; m_qdkgsman.ForEachHandler([&](CDKGSessionHandler& h) { if (seen) return; - if (h.pendingContributions.HasSeen(inv.hash) || h.pendingComplaints.HasSeen(inv.hash) - || h.pendingJustifications.HasSeen(inv.hash) || h.pendingPrematureCommitments.HasSeen(inv.hash)) { + if (h.pendingContributions.HasSeen(inv.hash) || h.pendingComplaints.HasSeen(inv.hash) || + h.pendingJustifications.HasSeen(inv.hash) || h.pendingPrematureCommitments.HasSeen(inv.hash)) { seen = true; } }); @@ -504,8 +530,8 @@ void NetDKG::HandleDKGRound(ActiveDKGSessionHandler& handler) const auto tip_mn_list = active.dmnman.GetListAtChainTip(); utils::EnsureQuorumConnections(handler.params, active.connman, m_sporkman, - {active.dmnman, active.qsnapman, m_chainman, pQuorumBaseBlockIndex}, - tip_mn_list, curSession->ProTx(), /*is_masternode=*/true, handler.QuorumsWatch()); + {active.dmnman, active.qsnapman, m_chainman, pQuorumBaseBlockIndex}, tip_mn_list, + curSession->ProTx(), /*is_masternode=*/true, handler.QuorumsWatch()); if (curSession->AreWeMember()) { utils::AddQuorumProbeConnections(handler.params, active.connman, active.mn_metaman, m_sporkman, {active.dmnman, active.qsnapman, m_chainman, pQuorumBaseBlockIndex}, diff --git a/src/llmq/net_dkg.h b/src/llmq/net_dkg.h index 42e52ba367a1..3da4364256d5 100644 --- a/src/llmq/net_dkg.h +++ b/src/llmq/net_dkg.h @@ -8,8 +8,8 @@ #include #include #include -#include #include +#include #include #include @@ -28,7 +28,9 @@ class ActiveDKGSessionHandler; class CDKGDebugManager; class CDKGSessionManager; class CQuorumBlockProcessor; +class CQuorumManager; class CQuorumSnapshotManager; +class QuorumRole; } // namespace llmq namespace llmq { @@ -52,15 +54,17 @@ class NetDKG final : public NetHandler public: //! Observer-mode constructor. NetDKG(PeerManagerInternal* peer_manager, const CSporkManager& sporkman, CDKGSessionManager& qdkgsman, - const ChainstateManager& chainman, bool quorums_watch); + const ChainstateManager& chainman, bool quorums_watch, CQuorumManager& qman, QuorumRole& role); //! Active-mode constructor: takes the masternode-only dep bundle as required references. NetDKG(PeerManagerInternal* peer_manager, const CSporkManager& sporkman, CDKGSessionManager& qdkgsman, - const ChainstateManager& chainman, bool quorums_watch, + const ChainstateManager& chainman, bool quorums_watch, CQuorumManager& qman, QuorumRole& role, CBLSWorker& bls_worker, CDeterministicMNManager& dmnman, CMasternodeMetaMan& mn_metaman, CDKGDebugManager& dkgdbgman, CQuorumBlockProcessor& qblockman, CQuorumSnapshotManager& qsnapman, const CActiveMasternodeManager& mn_activeman, CConnman& connman); + ~NetDKG(); + // NetHandler void ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv) override EXCLUSIVE_LOCKS_REQUIRED(!cs_indexed_quorums_cache); bool AlreadyHave(const CInv& inv) override; @@ -76,13 +80,11 @@ class NetDKG final : public NetHandler private: //! Bundle of refs that exist only in active masternode mode. struct ActiveDKG { - CBLSWorker& bls_worker; CDeterministicMNManager& dmnman; CMasternodeMetaMan& mn_metaman; CDKGDebugManager& dkgdbgman; CQuorumBlockProcessor& qblockman; CQuorumSnapshotManager& qsnapman; - const CActiveMasternodeManager& mn_activeman; CConnman& connman; }; @@ -90,6 +92,7 @@ class NetDKG final : public NetHandler void HandleDKGRound(ActiveDKGSessionHandler& handler); CDKGSessionManager& m_qdkgsman; + CQuorumManager& m_qman; const CSporkManager& m_sporkman; const ChainstateManager& m_chainman; const bool m_quorums_watch; diff --git a/src/llmq/observer.cpp b/src/llmq/observer.cpp index 06ab70e2e209..2fe9de078590 100644 --- a/src/llmq/observer.cpp +++ b/src/llmq/observer.cpp @@ -5,34 +5,20 @@ #include #include -#include #include #include -#include -#include - namespace llmq { -ObserverContext::ObserverContext(CBLSWorker& bls_worker, CDeterministicMNManager& dmnman, - CMasternodeMetaMan& mn_metaman, - llmq::CQuorumBlockProcessor& qblockman, llmq::CQuorumManager& qman, +ObserverContext::ObserverContext(CDeterministicMNManager& dmnman, llmq::CQuorumManager& qman, llmq::CQuorumSnapshotManager& qsnapman, const ChainstateManager& chainman, const CSporkManager& sporkman, const util::DbWrapperParams& db_params) : QuorumRole{qman}, dkgdbgman{std::make_unique(dmnman, qsnapman, chainman)}, qdkgsman{std::make_unique(dmnman, qsnapman, chainman, sporkman, db_params)} { - qdkgsman->InitializeHandlers([&](const Consensus::LLMQParams& llmq_params, - [[maybe_unused]] int quorum_idx) -> std::unique_ptr { - return std::make_unique(llmq_params); - }); - m_qman.ConnectManagers(this, qdkgsman.get()); } -ObserverContext::~ObserverContext() -{ - m_qman.DisconnectManagers(); -} +ObserverContext::~ObserverContext() = default; void ObserverContext::InitializeCurrentBlockTip(const CBlockIndex* tip, bool ibd) { diff --git a/src/llmq/observer.h b/src/llmq/observer.h index 52a820327aaf..d0fb60b2554e 100644 --- a/src/llmq/observer.h +++ b/src/llmq/observer.h @@ -9,22 +9,17 @@ #include -#include #include #include -class CBLSWorker; class CBlockIndex; class CDeterministicMNManager; -class CMasternodeMetaMan; class CSporkManager; namespace llmq { class CDKGDebugManager; class CDKGSessionManager; class CQuorum; -class CQuorumBlockProcessor; -class CQuorumDataRequest; class CQuorumSnapshotManager; } // namespace llmq namespace util { @@ -37,10 +32,9 @@ struct ObserverContext final : public QuorumRole, public CValidationInterface { ObserverContext() = delete; ObserverContext(const ObserverContext&) = delete; ObserverContext& operator=(const ObserverContext&) = delete; - ObserverContext(CBLSWorker& bls_worker, CDeterministicMNManager& dmnman, - CMasternodeMetaMan& mn_metaman, llmq::CQuorumBlockProcessor& qblockman, - llmq::CQuorumManager& qman, llmq::CQuorumSnapshotManager& qsnapman, const ChainstateManager& chainman, - const CSporkManager& sporkman, const util::DbWrapperParams& db_params); + ObserverContext(CDeterministicMNManager& dmnman, llmq::CQuorumManager& qman, llmq::CQuorumSnapshotManager& qsnapman, + const ChainstateManager& chainman, const CSporkManager& sporkman, + const util::DbWrapperParams& db_params); ~ObserverContext(); // QuorumRole diff --git a/src/llmq/options.cpp b/src/llmq/options.cpp index f9ad56e2b0ce..dc6330d087f0 100644 --- a/src/llmq/options.cpp +++ b/src/llmq/options.cpp @@ -39,10 +39,7 @@ bool IsAllMembersConnectedEnabled(const Consensus::LLMQType llmqType, const CSpo return EvalSpork(llmqType, sporkman.GetSporkValue(SPORK_21_QUORUM_ALL_CONNECTED)); } -bool IsQuorumDKGEnabled(const CSporkManager& sporkman) -{ - return sporkman.IsSporkActive(SPORK_17_QUORUM_DKG_ENABLED); -} +bool IsQuorumDKGEnabled(const CSporkManager& sporkman) { return sporkman.IsSporkActive(SPORK_17_QUORUM_DKG_ENABLED); } bool IsQuorumPoseEnabled(const Consensus::LLMQType llmqType, const CSporkManager& sporkman) { From c1c4e2ac840a92e9390ee19b1cdf0b378bff4fe0 Mon Sep 17 00:00:00 2001 From: Konstantin Akimov Date: Mon, 11 May 2026 00:59:25 +0700 Subject: [PATCH 12/12] refactor: drop heavy include llmq/commitment.h from CDKGSession header --- src/active/dkgsession.cpp | 1 + src/llmq/dkgsession.cpp | 10 ++++++++++ src/llmq/dkgsession.h | 16 ++++++++-------- src/llmq/net_dkg.cpp | 1 + 4 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/active/dkgsession.cpp b/src/active/dkgsession.cpp index 8a66f74766bd..6b28bfcc755a 100644 --- a/src/active/dkgsession.cpp +++ b/src/active/dkgsession.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include diff --git a/src/llmq/dkgsession.cpp b/src/llmq/dkgsession.cpp index 8a35a5f75626..8e050afac5d8 100644 --- a/src/llmq/dkgsession.cpp +++ b/src/llmq/dkgsession.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -68,6 +69,11 @@ CDKGMember::CDKGMember(const CDeterministicMNCPtr& _dmn, size_t _idx) : { } +uint256 CDKGPrematureCommitment::GetSignHash() const +{ + return BuildCommitmentHash(llmqType, quorumHash, validMembers, quorumPublicKey, quorumVvecHash); +} + CDKGSession::CDKGSession(CBLSWorker& _blsWorker, CDeterministicMNManager& dmnman, CDKGDebugManager& _dkgDebugManager, CDKGSessionManager& _dkgManager, CQuorumSnapshotManager& qsnapman, const ChainstateManager& chainman, const CBlockIndex* pQuorumBaseBlockIndex, @@ -618,6 +624,10 @@ CDKGMember* CDKGSession::GetMemberAtIndex(size_t index) const return members[index].get(); } +std::vector CDKGSession::FinalizeCommitments() { return {}; } + +CFinalCommitment CDKGSession::FinalizeSingleCommitment() { return {}; } + bool CDKGSession::GetContribution(const uint256& hash, CDKGContribution& ret) const { LOCK(invCs); diff --git a/src/llmq/dkgsession.h b/src/llmq/dkgsession.h index 1e790e12b165..ae151abb07d1 100644 --- a/src/llmq/dkgsession.h +++ b/src/llmq/dkgsession.h @@ -5,7 +5,6 @@ #ifndef BITCOIN_LLMQ_DKGSESSION_H #define BITCOIN_LLMQ_DKGSESSION_H -#include #include #include @@ -13,6 +12,7 @@ #include #include #include +#include #include #include @@ -20,9 +20,12 @@ #include #include -#include class CConnman; +class CBLSWorker; +class CDeterministicMNManager; +class ChainstateManager; +class CBlockIndex; namespace llmq { class CDKGDebugManager; class CDKGSession; @@ -193,10 +196,7 @@ class CDKGPrematureCommitment ); } - [[nodiscard]] uint256 GetSignHash() const - { - return BuildCommitmentHash(llmqType, quorumHash, validMembers, quorumPublicKey, quorumVvecHash); - } + [[nodiscard]] uint256 GetSignHash() const; }; class CDKGMember @@ -388,10 +388,10 @@ class CDKGSession std::optional ReceiveMessage(const CDKGPrematureCommitment& qc) EXCLUSIVE_LOCKS_REQUIRED(!invCs); // Phase 5: aggregate/finalize - virtual std::vector FinalizeCommitments() EXCLUSIVE_LOCKS_REQUIRED(!invCs) { return {}; } + virtual std::vector FinalizeCommitments() EXCLUSIVE_LOCKS_REQUIRED(!invCs); // All Phases 5-in-1 for single-node-quorum - virtual CFinalCommitment FinalizeSingleCommitment() { return {}; } + virtual CFinalCommitment FinalizeSingleCommitment(); //! Look up a received message by hash. Used by CDKGSessionHandler subclasses to implement their Get* virtuals. [[nodiscard]] bool GetContribution(const uint256& hash, CDKGContribution& ret) const EXCLUSIVE_LOCKS_REQUIRED(!invCs); diff --git a/src/llmq/net_dkg.cpp b/src/llmq/net_dkg.cpp index 0f186379f37a..a5682440aa27 100644 --- a/src/llmq/net_dkg.cpp +++ b/src/llmq/net_dkg.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include