Line data Source code
1 : // Copyright (c) 2018-2025 The Dash Core developers
2 : // Distributed under the MIT software license, see the accompanying
3 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 :
5 : #include <llmq/net_dkg.h>
6 :
7 : #include <active/dkgsessionhandler.h>
8 : #include <chainparams.h>
9 : #include <evo/deterministicmns.h>
10 : #include <hash.h>
11 : #include <llmq/blockprocessor.h>
12 : #include <llmq/commitment.h>
13 : #include <llmq/debug.h>
14 : #include <llmq/dkgsession.h>
15 : #include <llmq/dkgsessionmgr.h>
16 : #include <llmq/net_quorum.h>
17 : #include <llmq/options.h>
18 : #include <llmq/quorumsman.h>
19 : #include <llmq/utils.h>
20 : #include <masternode/meta.h>
21 : #include <net.h>
22 : #include <netmessagemaker.h>
23 : #include <protocol.h>
24 : #include <span.h>
25 : #include <unordered_lru_cache.h>
26 : #include <util/std23.h>
27 : #include <util/thread.h>
28 : #include <validation.h>
29 :
30 : namespace llmq {
31 :
32 : namespace {
33 : // returns a set of NodeIds which sent invalid messages
34 : template <typename Message>
35 0 : std::unordered_set<NodeId> BatchVerifyMessageSigs(CDKGSession& session,
36 : const std::vector<std::pair<NodeId, std::shared_ptr<Message>>>& messages)
37 : {
38 0 : if (messages.empty()) {
39 0 : return {};
40 : }
41 :
42 0 : std::unordered_set<NodeId> ret;
43 0 : bool revertToSingleVerification = false;
44 :
45 0 : CBLSSignature aggSig;
46 0 : std::vector<CBLSPublicKey> pubKeys;
47 0 : std::vector<uint256> messageHashes;
48 0 : Uint256HashSet messageHashesSet;
49 0 : pubKeys.reserve(messages.size());
50 0 : messageHashes.reserve(messages.size());
51 0 : bool first = true;
52 0 : for (const auto& [nodeId, msg] : messages) {
53 0 : auto member = session.GetMember(msg->proTxHash);
54 0 : if (!member) {
55 : // should not happen as it was verified before
56 0 : ret.emplace(nodeId);
57 0 : continue;
58 : }
59 :
60 0 : if (first) {
61 0 : aggSig = msg->sig;
62 0 : } else {
63 0 : aggSig.AggregateInsecure(msg->sig);
64 : }
65 0 : first = false;
66 :
67 0 : auto msgHash = msg->GetSignHash();
68 0 : if (!messageHashesSet.emplace(msgHash).second) {
69 : // can only happen in 2 cases:
70 : // 1. Someone sent us the same message twice but with differing signature, meaning that at least one of them
71 : // must be invalid. In this case, we'd have to revert to single message verification nevertheless
72 : // 2. Someone managed to find a way to create two different binary representations of a message that deserializes
73 : // to the same object representation. This would be some form of malleability. However, this shouldn't be
74 : // possible as only deterministic/unique BLS signatures and very simple data types are involved
75 0 : revertToSingleVerification = true;
76 0 : break;
77 : }
78 :
79 0 : pubKeys.emplace_back(member->dmn->pdmnState->pubKeyOperator.Get());
80 0 : messageHashes.emplace_back(msgHash);
81 : }
82 0 : if (!revertToSingleVerification) {
83 0 : if (aggSig.VerifyInsecureAggregated(pubKeys, messageHashes)) {
84 : // all good
85 0 : return ret;
86 : }
87 :
88 : // are all messages from the same node?
89 0 : bool nodeIdsAllSame = std::adjacent_find(messages.begin(), messages.end(),
90 0 : [](const auto& first, const auto& second) {
91 0 : return first.first != second.first;
92 0 : }) == messages.end();
93 :
94 : // if yes, take a short path and return a set with only him
95 0 : if (nodeIdsAllSame) {
96 0 : ret.emplace(messages[0].first);
97 0 : return ret;
98 : }
99 : // different nodes, let's figure out who are the bad ones
100 0 : }
101 :
102 0 : for (const auto& [nodeId, msg] : messages) {
103 0 : if (ret.count(nodeId)) {
104 0 : continue;
105 : }
106 :
107 0 : auto member = session.GetMember(msg->proTxHash);
108 0 : bool valid = msg->sig.VerifyInsecure(member->dmn->pdmnState->pubKeyOperator.Get(), msg->GetSignHash());
109 0 : if (!valid) {
110 0 : ret.emplace(nodeId);
111 0 : }
112 : }
113 0 : return ret;
114 0 : }
115 :
116 0 : void RelayInvToParticipants(const CDKGSession& session, const CConnman& connman, PeerManagerInternal& peerman,
117 : const CInv& inv)
118 : {
119 0 : CDKGLogger logger(session, __func__, __LINE__);
120 0 : std::stringstream ss;
121 0 : const auto& relayMembers = session.RelayMembers();
122 0 : for (const auto& r : relayMembers) {
123 0 : ss << r.ToString().substr(0, 4) << " | ";
124 : }
125 0 : logger.Batch("RelayInvToParticipants inv[%s] relayMembers[%d] GetNodeCount[%d] GetNetworkActive[%d] "
126 : "HasMasternodeQuorumNodes[%d] for quorumHash[%s] forMember[%s] relayMembers[%s]",
127 0 : inv.ToString(), relayMembers.size(), connman.GetNodeCount(ConnectionDirection::Both),
128 0 : connman.GetNetworkActive(),
129 0 : connman.HasMasternodeQuorumNodes(session.GetType(), session.BlockIndex()->GetBlockHash()),
130 0 : session.BlockIndex()->GetBlockHash().ToString(), session.ProTx().ToString().substr(0, 4), ss.str());
131 :
132 0 : std::stringstream ss2;
133 0 : connman.ForEachNode([&](const CNode* pnode) {
134 0 : if (pnode->qwatch ||
135 0 : (!pnode->GetVerifiedProRegTxHash().IsNull() && (relayMembers.count(pnode->GetVerifiedProRegTxHash()) != 0))) {
136 0 : peerman.PeerPushInventory(pnode->GetId(), inv);
137 0 : }
138 :
139 0 : if (pnode->GetVerifiedProRegTxHash().IsNull()) {
140 0 : logger.Batch("node[%d:%s] not mn", pnode->GetId(), pnode->m_addr_name);
141 0 : } else if (relayMembers.count(pnode->GetVerifiedProRegTxHash()) == 0) {
142 0 : ss2 << pnode->GetVerifiedProRegTxHash().ToString().substr(0, 4) << " | ";
143 0 : }
144 0 : });
145 0 : logger.Batch("forMember[%s] NOTrelayMembers[%s]", session.ProTx().ToString().substr(0, 4), ss2.str());
146 0 : logger.Flush();
147 0 : }
148 :
149 : template <typename Message>
150 0 : void EnqueueOwn(CDKGPendingMessages& pending, const Message& msg)
151 : {
152 0 : CDataStream ds(SER_NETWORK, PROTOCOL_VERSION);
153 0 : ds << msg;
154 0 : auto pm = std::make_shared<CDataStream>(std::move(ds));
155 0 : CHashWriter hw(SER_GETHASH, 0);
156 0 : hw.write(AsWritableBytes(Span{*pm}));
157 0 : pending.PushPendingMessage(/*from=*/-1, std::move(pm), hw.GetHash());
158 0 : }
159 :
160 : template <typename Message>
161 0 : bool ProcessPendingMessageBatch(const CConnman& connman, CDKGSession& session, CDKGPendingMessages& pendingMessages,
162 : PeerManagerInternal& peerman, size_t maxCount)
163 : {
164 0 : auto msgs = pendingMessages.PopAndDeserializeMessages<Message>(maxCount);
165 0 : if (msgs.empty()) {
166 0 : return false;
167 : }
168 :
169 0 : std::vector<std::pair<NodeId, std::shared_ptr<Message>>> preverifiedMessages;
170 0 : preverifiedMessages.reserve(msgs.size());
171 :
172 0 : for (const auto& p : msgs) {
173 0 : const NodeId& nodeId = p.first;
174 0 : if (!p.second) {
175 0 : LogPrint(BCLog::LLMQ_DKG, "%s -- failed to deserialize message, peer=%d\n", __func__, nodeId);
176 0 : peerman.PeerMisbehaving(nodeId, 100);
177 0 : continue;
178 : }
179 0 : bool ban = false;
180 0 : if (!session.PreVerifyMessage(*p.second, ban)) {
181 0 : if (ban) {
182 0 : LogPrint(BCLog::LLMQ_DKG, "%s -- banning node due to failed preverification, peer=%d\n", __func__, nodeId);
183 0 : peerman.PeerMisbehaving(nodeId, 100);
184 0 : }
185 0 : LogPrint(BCLog::LLMQ_DKG, "%s -- skipping message due to failed preverification, peer=%d\n", __func__, nodeId);
186 0 : continue;
187 : }
188 0 : preverifiedMessages.emplace_back(p);
189 : }
190 0 : if (preverifiedMessages.empty()) {
191 0 : return true;
192 : }
193 :
194 0 : auto badNodes = BatchVerifyMessageSigs(session, preverifiedMessages);
195 0 : if (!badNodes.empty()) {
196 0 : for (auto nodeId : badNodes) {
197 0 : LogPrint(BCLog::LLMQ_DKG, "%s -- failed to verify signature, peer=%d\n", __func__, nodeId);
198 0 : peerman.PeerMisbehaving(nodeId, 100);
199 : }
200 0 : }
201 :
202 0 : for (const auto& p : preverifiedMessages) {
203 0 : const NodeId& nodeId = p.first;
204 0 : if (badNodes.count(nodeId)) {
205 0 : continue;
206 : }
207 0 : const std::optional<CInv> inv = session.ReceiveMessage(*p.second);
208 0 : if (inv) {
209 0 : RelayInvToParticipants(session, connman, peerman, *inv);
210 0 : }
211 : }
212 :
213 0 : return true;
214 0 : }
215 : } // namespace
216 :
217 :
218 0 : NetDKG::NetDKG(PeerManagerInternal* peer_manager, const CSporkManager& sporkman, CDKGSessionManager& qdkgsman,
219 : const ChainstateManager& chainman, CQuorumManager& qman, QuorumRole& role) :
220 0 : NetHandler(peer_manager),
221 0 : m_qdkgsman{qdkgsman},
222 0 : m_qman{qman},
223 0 : m_sporkman{sporkman},
224 0 : m_chainman{chainman},
225 0 : m_active{nullptr}
226 0 : {
227 0 : m_qdkgsman.InitializeHandlers([](const Consensus::LLMQParams& llmq_params,
228 : [[maybe_unused]] int quorum_idx) -> std::unique_ptr<CDKGSessionHandler> {
229 0 : return std::make_unique<CDKGSessionHandler>(llmq_params);
230 : });
231 0 : m_qman.ConnectManagers(&role, &m_qdkgsman);
232 0 : }
233 :
234 0 : NetDKG::NetDKG(PeerManagerInternal* peer_manager, const CSporkManager& sporkman, CDKGSessionManager& qdkgsman,
235 : const ChainstateManager& chainman, bool quorums_watch, CQuorumManager& qman, QuorumRole& role,
236 : CBLSWorker& bls_worker, CDeterministicMNManager& dmnman, CMasternodeMetaMan& mn_metaman,
237 : CDKGDebugManager& dkgdbgman, CQuorumBlockProcessor& qblockman, CQuorumSnapshotManager& qsnapman,
238 : const CActiveMasternodeManager& mn_activeman, CConnman& connman) :
239 0 : NetHandler(peer_manager),
240 0 : m_qdkgsman{qdkgsman},
241 0 : m_qman{qman},
242 0 : m_sporkman{sporkman},
243 0 : m_chainman{chainman},
244 0 : m_active{std::make_unique<ActiveDKG>(ActiveDKG{dmnman, mn_metaman, dkgdbgman, qblockman, qsnapman, connman})}
245 0 : {
246 0 : m_qdkgsman.InitializeHandlers(
247 0 : [&](const Consensus::LLMQParams& llmq_params, int quorum_idx) -> std::unique_ptr<ActiveDKGSessionHandler> {
248 0 : return std::make_unique<ActiveDKGSessionHandler>(bls_worker, dmnman, mn_metaman, dkgdbgman, qdkgsman,
249 0 : qblockman, qsnapman, mn_activeman, chainman, sporkman,
250 0 : llmq_params, quorums_watch, quorum_idx);
251 : });
252 0 : m_qman.ConnectManagers(&role, &m_qdkgsman);
253 0 : }
254 :
255 0 : NetDKG::~NetDKG()
256 0 : {
257 0 : Stop();
258 0 : m_qman.DisconnectManagers();
259 0 : }
260 :
261 0 : void NetDKG::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv)
262 : {
263 0 : if (!IsQuorumDKGEnabled(m_sporkman)) return;
264 :
265 0 : if (msg_type != NetMsgType::QCONTRIB && msg_type != NetMsgType::QCOMPLAINT && msg_type != NetMsgType::QJUSTIFICATION &&
266 0 : msg_type != NetMsgType::QPCOMMITMENT && msg_type != NetMsgType::QWATCH) {
267 0 : return;
268 : }
269 :
270 0 : const bool is_masternode = m_active != nullptr;
271 :
272 0 : if (msg_type == NetMsgType::QWATCH) {
273 0 : if (!is_masternode) {
274 : // non-masternodes should never receive this
275 0 : m_peer_manager->PeerMisbehaving(pfrom.GetId(), 10);
276 0 : return;
277 : }
278 0 : pfrom.qwatch = true;
279 0 : return;
280 : }
281 :
282 0 : if (vRecv.empty()) {
283 0 : m_peer_manager->PeerMisbehaving(pfrom.GetId(), 100);
284 0 : return;
285 : }
286 :
287 : Consensus::LLMQType llmqType;
288 0 : uint256 quorumHash;
289 0 : vRecv >> llmqType;
290 0 : vRecv >> quorumHash;
291 0 : vRecv.Rewind(sizeof(uint256));
292 0 : vRecv.Rewind(sizeof(uint8_t));
293 :
294 0 : const auto& llmq_params_opt = Params().GetLLMQ(llmqType);
295 0 : if (!llmq_params_opt.has_value()) {
296 0 : LogPrintf("NetDKG -- invalid llmqType [%d]\n", std23::to_underlying(llmqType));
297 0 : m_peer_manager->PeerMisbehaving(pfrom.GetId(), 100);
298 0 : return;
299 : }
300 0 : const auto& llmq_params = llmq_params_opt.value();
301 :
302 0 : int quorumIndex{-1};
303 : {
304 0 : LOCK(cs_indexed_quorums_cache);
305 0 : if (indexed_quorums_cache.empty()) {
306 0 : utils::InitQuorumsCache(indexed_quorums_cache, m_chainman.GetConsensus());
307 0 : }
308 0 : indexed_quorums_cache[llmqType].get(quorumHash, quorumIndex);
309 0 : }
310 :
311 0 : if (quorumIndex == -1) {
312 0 : const CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(::cs_main,
313 : return m_chainman.m_blockman.LookupBlockIndex(quorumHash));
314 0 : if (pQuorumBaseBlockIndex == nullptr) {
315 0 : LogPrintf("NetDKG -- unknown quorumHash %s\n", quorumHash.ToString());
316 : // NOTE: do not insta-ban for this, we might be lagging behind
317 0 : m_peer_manager->PeerMisbehaving(pfrom.GetId(), 10);
318 0 : return;
319 : }
320 0 : if (!m_chainman.IsQuorumTypeEnabled(llmqType, pQuorumBaseBlockIndex->pprev)) {
321 0 : LogPrintf("NetDKG -- llmqType [%d] quorums aren't active\n", std23::to_underlying(llmqType));
322 0 : m_peer_manager->PeerMisbehaving(pfrom.GetId(), 100);
323 0 : return;
324 : }
325 0 : quorumIndex = pQuorumBaseBlockIndex->nHeight % llmq_params.dkgInterval;
326 0 : const int quorumIndexMax = IsQuorumRotationEnabled(llmq_params, pQuorumBaseBlockIndex)
327 0 : ? llmq_params.signingActiveQuorumCount - 1
328 : : 0;
329 0 : if (quorumIndex > quorumIndexMax) {
330 0 : LogPrintf("NetDKG -- invalid quorumHash %s\n", quorumHash.ToString());
331 0 : m_peer_manager->PeerMisbehaving(pfrom.GetId(), 100);
332 0 : return;
333 : }
334 0 : }
335 :
336 0 : int inv_type = 0;
337 0 : if (msg_type == NetMsgType::QCONTRIB)
338 0 : inv_type = MSG_QUORUM_CONTRIB;
339 0 : else if (msg_type == NetMsgType::QCOMPLAINT)
340 0 : inv_type = MSG_QUORUM_COMPLAINT;
341 0 : else if (msg_type == NetMsgType::QJUSTIFICATION)
342 0 : inv_type = MSG_QUORUM_JUSTIFICATION;
343 0 : else if (msg_type == NetMsgType::QPCOMMITMENT)
344 0 : inv_type = MSG_QUORUM_PREMATURE_COMMITMENT;
345 0 : Assume(inv_type != 0); // guarded by the early-return above
346 :
347 0 : auto pm = std::make_shared<CDataStream>(std::move(vRecv));
348 0 : CHashWriter hw(SER_GETHASH, 0);
349 0 : hw.write(AsWritableBytes(Span{*pm}));
350 0 : const uint256 hash = hw.GetHash();
351 :
352 0 : const NodeId from = pfrom.GetId();
353 0 : const bool dispatched = m_qdkgsman.DoForHandler({llmqType, quorumIndex}, [&](CDKGSessionHandler& handler) {
354 0 : CDKGPendingMessages* pending = nullptr;
355 0 : switch (inv_type) {
356 : case MSG_QUORUM_CONTRIB:
357 0 : pending = &handler.pendingContributions;
358 0 : break;
359 : case MSG_QUORUM_COMPLAINT:
360 0 : pending = &handler.pendingComplaints;
361 0 : break;
362 : case MSG_QUORUM_JUSTIFICATION:
363 0 : pending = &handler.pendingJustifications;
364 0 : break;
365 : case MSG_QUORUM_PREMATURE_COMMITMENT:
366 0 : pending = &handler.pendingPrematureCommitments;
367 0 : break;
368 : }
369 0 : Assume(pending != nullptr);
370 0 : WITH_LOCK(::cs_main, m_peer_manager->PeerEraseObjectRequest(from, CInv{static_cast<uint32_t>(inv_type), hash}));
371 0 : pending->PushPendingMessage(from, std::move(pm), hash);
372 0 : });
373 0 : if (!dispatched) {
374 0 : LogPrintf("NetDKG -- no session handlers for quorumIndex [%d]\n", quorumIndex);
375 0 : m_peer_manager->PeerMisbehaving(pfrom.GetId(), 100);
376 0 : return;
377 : }
378 :
379 0 : WITH_LOCK(cs_indexed_quorums_cache, indexed_quorums_cache[llmqType].insert(quorumHash, quorumIndex));
380 0 : }
381 :
382 0 : bool NetDKG::AlreadyHave(const CInv& inv)
383 : {
384 0 : switch (inv.type) {
385 : case MSG_QUORUM_CONTRIB:
386 : case MSG_QUORUM_COMPLAINT:
387 : case MSG_QUORUM_JUSTIFICATION:
388 : case MSG_QUORUM_PREMATURE_COMMITMENT: {
389 0 : if (!IsQuorumDKGEnabled(m_sporkman)) return false;
390 0 : bool seen = false;
391 0 : m_qdkgsman.ForEachHandler([&](CDKGSessionHandler& h) {
392 0 : if (seen) return;
393 0 : if (h.pendingContributions.HasSeen(inv.hash) || h.pendingComplaints.HasSeen(inv.hash) ||
394 0 : h.pendingJustifications.HasSeen(inv.hash) || h.pendingPrematureCommitments.HasSeen(inv.hash)) {
395 0 : seen = true;
396 0 : }
397 0 : });
398 0 : return seen;
399 : }
400 : }
401 0 : return false;
402 0 : }
403 :
404 0 : bool NetDKG::ProcessGetData(CNode& pfrom, const CInv& inv, CConnman& connman, const CNetMsgMaker& msgMaker)
405 : {
406 : // Default implementations of GetContribution and the other virtual methods
407 : // return false in observer mode; m_active is only an early exit and does
408 : // not affect logic.
409 0 : if (m_active == nullptr) return false;
410 :
411 0 : switch (inv.type) {
412 : case MSG_QUORUM_CONTRIB: {
413 0 : CDKGContribution o;
414 0 : if (m_qdkgsman.GetContribution(inv.hash, o)) {
415 0 : connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::QCONTRIB, o));
416 0 : return true;
417 : }
418 0 : return false;
419 0 : }
420 : case MSG_QUORUM_COMPLAINT: {
421 0 : CDKGComplaint o;
422 0 : if (m_qdkgsman.GetComplaint(inv.hash, o)) {
423 0 : connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::QCOMPLAINT, o));
424 0 : return true;
425 : }
426 0 : return false;
427 0 : }
428 : case MSG_QUORUM_JUSTIFICATION: {
429 0 : CDKGJustification o;
430 0 : if (m_qdkgsman.GetJustification(inv.hash, o)) {
431 0 : connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::QJUSTIFICATION, o));
432 0 : return true;
433 : }
434 0 : return false;
435 0 : }
436 : case MSG_QUORUM_PREMATURE_COMMITMENT: {
437 0 : CDKGPrematureCommitment o;
438 0 : if (m_qdkgsman.GetPrematureCommitment(inv.hash, o)) {
439 0 : connman.PushMessage(&pfrom, msgMaker.Make(NetMsgType::QPCOMMITMENT, o));
440 0 : return true;
441 : }
442 0 : return false;
443 0 : }
444 : }
445 0 : return false;
446 0 : }
447 :
448 0 : void NetDKG::Start()
449 : {
450 0 : if (m_active == nullptr) return;
451 0 : if (!m_phase_threads.empty()) {
452 0 : throw std::runtime_error("Tried to start PhaseHandlerThreads again.");
453 : }
454 :
455 0 : m_qdkgsman.ForEachHandler([this](CDKGSessionHandler& base) {
456 0 : auto& handler = dynamic_cast<ActiveDKGSessionHandler&>(base);
457 0 : std::string thread_name = strprintf("llmq-%d-%d", std23::to_underlying(handler.params.type), handler.QuorumIndex());
458 0 : m_phase_threads.emplace_back([this, name = std::move(thread_name), &handler] {
459 0 : util::TraceThread(name.c_str(), [this, &handler] { PhaseHandlerThread(handler); });
460 0 : });
461 0 : });
462 0 : }
463 :
464 0 : void NetDKG::Stop()
465 : {
466 0 : Interrupt();
467 0 : for (auto& t : m_phase_threads) {
468 0 : if (t.joinable()) t.join();
469 : }
470 0 : m_phase_threads.clear();
471 0 : }
472 :
473 0 : void NetDKG::Interrupt()
474 : {
475 0 : if (m_active == nullptr) return;
476 0 : m_qdkgsman.ForEachHandler([](CDKGSessionHandler& base) {
477 0 : if (auto* handler = dynamic_cast<ActiveDKGSessionHandler*>(&base)) {
478 0 : handler->RequestStop();
479 0 : }
480 0 : });
481 0 : }
482 :
483 0 : void NetDKG::PhaseHandlerThread(ActiveDKGSessionHandler& handler)
484 : {
485 0 : while (!handler.IsStopRequested()) {
486 : try {
487 0 : LogPrint(BCLog::LLMQ_DKG, "NetDKG::%s -- %s qi[%d] - starting HandleDKGRound\n", __func__,
488 : handler.params.name, handler.QuorumIndex());
489 0 : HandleDKGRound(handler);
490 0 : } catch (AbortPhaseException& e) {
491 0 : m_active->dkgdbgman.MarkAborted(handler.params.type, handler.QuorumIndex());
492 0 : LogPrint(BCLog::LLMQ_DKG, "NetDKG::%s -- %s qi[%d] - aborted current DKG session\n", __func__,
493 : handler.params.name, handler.QuorumIndex());
494 0 : }
495 : }
496 0 : }
497 :
498 0 : static void AddQuorumProbeConnections(const Consensus::LLMQParams& llmqParams, CConnman& connman,
499 : CMasternodeMetaMan& mn_metaman, const CSporkManager& sporkman,
500 : const UtilParameters& util_params, const CDeterministicMNList& tip_mn_list,
501 : const uint256& myProTxHash)
502 : {
503 0 : assert(mn_metaman.IsValid());
504 :
505 0 : if (!IsQuorumPoseEnabled(llmqParams.type, sporkman)) {
506 0 : return;
507 : }
508 :
509 0 : auto members = utils::GetAllQuorumMembers(llmqParams.type, util_params);
510 0 : auto curTime = GetTime<std::chrono::seconds>().count();
511 :
512 0 : Uint256HashSet probeConnections;
513 0 : for (const auto& dmn : members) {
514 0 : if (dmn->proTxHash == myProTxHash) {
515 0 : continue;
516 : }
517 0 : auto lastOutbound = mn_metaman.GetLastOutboundSuccess(dmn->proTxHash);
518 0 : if (curTime - lastOutbound < 10 * 60) {
519 : // avoid re-probing nodes too often
520 0 : continue;
521 : }
522 0 : probeConnections.emplace(dmn->proTxHash);
523 : }
524 :
525 0 : if (!probeConnections.empty()) {
526 0 : if (LogAcceptDebug(BCLog::LLMQ)) {
527 0 : std::string debugMsg = strprintf("%s -- adding masternodes probes for quorum %s:\n", __func__,
528 0 : util_params.m_base_index->GetBlockHash().ToString());
529 0 : for (const auto& c : probeConnections) {
530 0 : auto dmn = tip_mn_list.GetValidMN(c);
531 0 : if (!dmn) {
532 0 : debugMsg += strprintf(" %s (not in valid MN set anymore)\n", c.ToString());
533 0 : } else {
534 0 : debugMsg += strprintf(" %s (%s)\n", c.ToString(),
535 0 : dmn->pdmnState->netInfo->GetPrimary().ToStringAddrPort());
536 : }
537 0 : }
538 0 : LogPrint(BCLog::NET_NETCONN, debugMsg.c_str()); /* Continued */
539 0 : }
540 0 : connman.AddPendingProbeConnections(probeConnections);
541 0 : }
542 0 : }
543 :
544 0 : void NetDKG::HandleDKGRound(ActiveDKGSessionHandler& handler)
545 : {
546 0 : auto& active = *Assert(m_active);
547 :
548 0 : handler.WaitForNextPhase(std::nullopt, QuorumPhase::Initialized);
549 :
550 0 : handler.ClearPendingMessages();
551 0 : uint256 curQuorumHash = handler.GetCurrentQuorumHash();
552 :
553 0 : const CBlockIndex* pQuorumBaseBlockIndex = WITH_LOCK(::cs_main,
554 : return m_chainman.m_blockman.LookupBlockIndex(curQuorumHash));
555 :
556 0 : if (!pQuorumBaseBlockIndex || !handler.InitNewQuorum(pQuorumBaseBlockIndex)) {
557 : // should actually never happen
558 0 : handler.WaitForNewQuorum(curQuorumHash);
559 0 : throw AbortPhaseException();
560 : }
561 :
562 0 : active.dkgdbgman.MarkPhaseAdvanced(handler.params.type, handler.QuorumIndex(), QuorumPhase::Initialized);
563 :
564 0 : auto* curSession = handler.GetCurSession();
565 0 : if (handler.params.is_single_member()) {
566 0 : auto finalCommitment = curSession->FinalizeSingleCommitment();
567 0 : if (!finalCommitment.IsNull()) { // it can be null only if we are not member
568 0 : if (auto inv_opt = active.qblockman.AddMineableCommitment(finalCommitment); inv_opt.has_value()) {
569 0 : m_peer_manager->PeerRelayInv(inv_opt.value());
570 0 : }
571 0 : }
572 0 : handler.WaitForNextPhase(QuorumPhase::Initialized, QuorumPhase::Contribute, curQuorumHash);
573 : return;
574 0 : }
575 :
576 0 : const auto tip_mn_list = active.dmnman.GetListAtChainTip();
577 0 : llmq::EnsureQuorumConnections(handler.params, active.connman, m_sporkman,
578 0 : {active.dmnman, active.qsnapman, m_chainman, pQuorumBaseBlockIndex}, tip_mn_list,
579 0 : curSession->ProTx(), /*is_masternode=*/true, handler.QuorumsWatch());
580 0 : if (curSession->AreWeMember()) {
581 0 : AddQuorumProbeConnections(handler.params, active.connman, active.mn_metaman, m_sporkman,
582 0 : {active.dmnman, active.qsnapman, m_chainman, pQuorumBaseBlockIndex}, tip_mn_list,
583 0 : curSession->ProTx());
584 0 : }
585 :
586 0 : handler.WaitForNextPhase(QuorumPhase::Initialized, QuorumPhase::Contribute, curQuorumHash);
587 :
588 : // Contribute
589 0 : auto fContributeStart = [curSession, &handler]() {
590 0 : if (auto qc = curSession->Contribute(); qc) {
591 0 : EnqueueOwn(handler.pendingContributions, *qc);
592 0 : }
593 0 : };
594 0 : auto fContributeWait = [this, curSession, &handler, &active] {
595 0 : return ProcessPendingMessageBatch<CDKGContribution>(active.connman, *curSession, handler.pendingContributions,
596 0 : *m_peer_manager, 8);
597 : };
598 0 : handler.HandlePhase(QuorumPhase::Contribute, QuorumPhase::Complain, curQuorumHash, 0.05, fContributeStart,
599 0 : fContributeWait);
600 :
601 : // Complain
602 0 : auto fComplainStart = [curSession, &handler, &active]() {
603 0 : if (auto qc = curSession->VerifyAndComplain(active.connman); qc) {
604 0 : EnqueueOwn(handler.pendingComplaints, *qc);
605 0 : }
606 0 : };
607 0 : auto fComplainWait = [this, curSession, &handler, &active] {
608 0 : return ProcessPendingMessageBatch<CDKGComplaint>(active.connman, *curSession, handler.pendingComplaints,
609 0 : *m_peer_manager, 8);
610 : };
611 0 : handler.HandlePhase(QuorumPhase::Complain, QuorumPhase::Justify, curQuorumHash, 0.05, fComplainStart, fComplainWait);
612 :
613 : // Justify
614 0 : auto fJustifyStart = [curSession, &handler]() {
615 0 : if (auto qj = curSession->VerifyAndJustify(); qj) {
616 0 : EnqueueOwn(handler.pendingJustifications, *qj);
617 0 : }
618 0 : };
619 0 : auto fJustifyWait = [this, curSession, &handler, &active] {
620 0 : return ProcessPendingMessageBatch<CDKGJustification>(active.connman, *curSession, handler.pendingJustifications,
621 0 : *m_peer_manager, 8);
622 : };
623 0 : handler.HandlePhase(QuorumPhase::Justify, QuorumPhase::Commit, curQuorumHash, 0.05, fJustifyStart, fJustifyWait);
624 :
625 : // Commit
626 0 : auto fCommitStart = [curSession, &handler]() {
627 0 : if (auto qc = curSession->VerifyAndCommit(); qc) {
628 0 : EnqueueOwn(handler.pendingPrematureCommitments, *qc);
629 0 : }
630 0 : };
631 0 : auto fCommitWait = [this, curSession, &handler, &active] {
632 0 : return ProcessPendingMessageBatch<CDKGPrematureCommitment>(active.connman, *curSession,
633 0 : handler.pendingPrematureCommitments, *m_peer_manager,
634 : 8);
635 : };
636 0 : handler.HandlePhase(QuorumPhase::Commit, QuorumPhase::Finalize, curQuorumHash, 0.1, fCommitStart, fCommitWait);
637 :
638 0 : auto finalCommitments = curSession->FinalizeCommitments();
639 0 : for (const auto& fqc : finalCommitments) {
640 0 : if (auto inv_opt = active.qblockman.AddMineableCommitment(fqc); inv_opt.has_value()) {
641 0 : m_peer_manager->PeerRelayInv(inv_opt.value());
642 0 : }
643 : }
644 0 : }
645 :
646 0 : void NetDKGStub::ProcessMessage(CNode& pfrom, const std::string& msg_type, CDataStream& vRecv)
647 : {
648 0 : if (msg_type == NetMsgType::QCONTRIB || msg_type == NetMsgType::QCOMPLAINT || msg_type == NetMsgType::QJUSTIFICATION ||
649 0 : msg_type == NetMsgType::QPCOMMITMENT || msg_type == NetMsgType::QWATCH) {
650 0 : m_peer_manager->PeerMisbehaving(pfrom.GetId(), 10);
651 0 : }
652 0 : }
653 :
654 : } // namespace llmq
|