Line data Source code
1 : // Copyright (c) 2018-2025 The Dash Core developers
2 : // Distributed under the MIT/X11 software license, see the accompanying
3 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 :
5 : #ifndef BITCOIN_LLMQ_SIGNING_SHARES_H
6 : #define BITCOIN_LLMQ_SIGNING_SHARES_H
7 :
8 : #include <bls/bls.h>
9 : #include <evo/types.h>
10 : #include <llmq/signhash.h>
11 : #include <llmq/signing.h>
12 : #include <util/std23.h>
13 :
14 : #include <random.h>
15 : #include <saltedhasher.h>
16 : #include <serialize.h>
17 : #include <sync.h>
18 : #include <uint256.h>
19 : #include <util/time.h>
20 :
21 : #include <atomic>
22 : #include <functional>
23 : #include <limits>
24 : #include <memory>
25 : #include <optional>
26 : #include <string>
27 : #include <unordered_map>
28 : #include <utility>
29 : #include <vector>
30 :
31 : class CActiveMasternodeManager;
32 : class ChainstateManager;
33 : class CNode;
34 : class CConnman;
35 : class CSporkManager;
36 :
37 : namespace llmq
38 : {
39 : class CSigningManager;
40 :
41 : // <signHash, quorumMember>
42 : using SigShareKey = std::pair<uint256, uint16_t>;
43 :
44 : constexpr uint32_t UNINITIALIZED_SESSION_ID{std::numeric_limits<uint32_t>::max()};
45 :
46 : class CSigShare : virtual public CSigBase
47 : {
48 : protected:
49 13462 : uint16_t quorumMember{std::numeric_limits<uint16_t>::max()};
50 : public:
51 : CBLSLazySignature sigShare;
52 :
53 : SigShareKey key;
54 :
55 134179 : [[nodiscard]] auto getQuorumMember() const {
56 134179 : return quorumMember;
57 : }
58 :
59 73978 : CSigShare(Consensus::LLMQType _llmqType, const uint256& _quorumHash, const uint256& _id, const uint256& _msgHash,
60 : uint16_t _quorumMember, const CBLSLazySignature& _sigShare) :
61 36989 : CSigBase(_llmqType, _quorumHash, _id, _msgHash),
62 36989 : quorumMember(_quorumMember),
63 110967 : sigShare(_sigShare) {};
64 :
65 : // This should only be used for serialization
66 26924 : CSigShare() = default;
67 :
68 :
69 : public:
70 : void UpdateKey();
71 202506 : const SigShareKey& GetKey() const
72 : {
73 202506 : return key;
74 : }
75 254347 : const uint256& GetSignHash() const
76 : {
77 254347 : assert(!key.first.IsNull());
78 254347 : return key.first;
79 : }
80 :
81 38361 : SERIALIZE_METHODS(CSigShare, obj)
82 : {
83 12787 : READWRITE(obj.llmqType, obj.quorumHash, obj.quorumMember, obj.id, obj.msgHash, obj.sigShare);
84 19166 : SER_READ(obj, obj.UpdateKey());
85 12787 : }
86 : };
87 :
88 : // Nodes will first announce a signing session with a sessionId to be used in all future P2P messages related to that
89 : // session. We locally keep track of the mapping for each node. We also assign new sessionIds for outgoing sessions
90 : // and send QSIGSESANN messages appropriately. All values except the max value for uint32_t are valid as sessionId
91 : class CSigSesAnn : virtual public CSigBase
92 : {
93 : private:
94 21816 : uint32_t sessionId{UNINITIALIZED_SESSION_ID};
95 :
96 : public:
97 21836 : CSigSesAnn(uint32_t _sessionId, Consensus::LLMQType _llmqType, const uint256& _quorumHash, const uint256& _id,
98 21836 : const uint256& _msgHash) : CSigBase(_llmqType, _quorumHash, _id, _msgHash), sessionId(_sessionId) {};
99 : // ONLY FOR SERIALIZATION
100 43632 : CSigSesAnn() = default;
101 :
102 :
103 :
104 109094 : [[nodiscard]] auto getSessionId() const {
105 109094 : return sessionId;
106 : }
107 :
108 130941 : SERIALIZE_METHODS(CSigSesAnn, obj)
109 : {
110 43647 : READWRITE(VARINT(obj.sessionId), obj.llmqType, obj.quorumHash, obj.id, obj.msgHash);
111 43647 : }
112 :
113 : [[nodiscard]] std::string ToString() const;
114 : };
115 :
116 166025 : class CSigSharesInv
117 : {
118 : public:
119 166025 : uint32_t sessionId{UNINITIALIZED_SESSION_ID};
120 : std::vector<bool> inv;
121 :
122 : public:
123 92037 : SERIALIZE_METHODS(CSigSharesInv, obj)
124 : {
125 30679 : uint64_t invSize = obj.inv.size();
126 30679 : READWRITE(VARINT(obj.sessionId), COMPACTSIZE(invSize));
127 30679 : autobitset_t bitset = std::make_pair(obj.inv, (size_t)invSize);
128 30679 : READWRITE(AUTOBITSET(bitset));
129 46012 : SER_READ(obj, obj.inv = bitset.first);
130 30679 : }
131 :
132 : void Init(size_t size);
133 : void Set(uint16_t quorumMember, bool v);
134 : void SetAll(bool v);
135 : void Merge(const CSigSharesInv& inv2);
136 :
137 : [[nodiscard]] size_t CountSet() const;
138 : [[nodiscard]] std::string ToString() const;
139 : };
140 :
141 : // sent through the message QBSIGSHARES as a vector of multiple batches
142 689638 : class CBatchedSigShares
143 : {
144 : public:
145 689638 : uint32_t sessionId{UNINITIALIZED_SESSION_ID};
146 : std::vector<std::pair<uint16_t, CBLSLazySignature>> sigShares;
147 :
148 : public:
149 113427 : SERIALIZE_METHODS(CBatchedSigShares, obj)
150 : {
151 37809 : READWRITE(VARINT(obj.sessionId), obj.sigShares);
152 37809 : }
153 :
154 : [[nodiscard]] std::string ToInvString() const;
155 : };
156 :
157 : template<typename T>
158 : class SigShareMap
159 : {
160 : private:
161 : Uint256HashMap<std::unordered_map<uint16_t, T>> internalMap;
162 :
163 : public:
164 97752 : bool Add(const SigShareKey& k, const T& v)
165 : {
166 97752 : auto& m = internalMap[k.first];
167 97752 : return m.emplace(k.second, v).second;
168 : }
169 :
170 41492 : void Erase(const SigShareKey& k)
171 : {
172 41492 : auto it = internalMap.find(k.first);
173 41492 : if (it == internalMap.end()) {
174 16702 : return;
175 : }
176 24790 : it->second.erase(k.second);
177 24790 : if (it->second.empty()) {
178 24755 : internalMap.erase(it);
179 24755 : }
180 41492 : }
181 :
182 216243 : void Clear()
183 : {
184 216243 : internalMap.clear();
185 216243 : }
186 :
187 58752 : [[nodiscard]] bool Has(const SigShareKey& k) const
188 : {
189 58752 : auto it = internalMap.find(k.first);
190 58752 : if (it == internalMap.end()) {
191 8866 : return false;
192 : }
193 49886 : return it->second.count(k.second) != 0;
194 58752 : }
195 :
196 48249 : T* Get(const SigShareKey& k)
197 : {
198 48249 : auto it = internalMap.find(k.first);
199 48249 : if (it == internalMap.end()) {
200 6230 : return nullptr;
201 : }
202 :
203 42019 : auto jt = it->second.find(k.second);
204 42019 : if (jt == it->second.end()) {
205 146 : return nullptr;
206 : }
207 :
208 41873 : return &jt->second;
209 48249 : }
210 :
211 3185 : T& GetOrAdd(const SigShareKey& k)
212 : {
213 3185 : T* v = Get(k);
214 3185 : if (!v) {
215 3183 : Add(k, T());
216 3183 : v = Get(k);
217 3183 : }
218 3185 : return *v;
219 : }
220 :
221 22840 : const T* GetFirst() const
222 : {
223 22840 : if (internalMap.empty()) {
224 0 : return nullptr;
225 : }
226 22840 : return &internalMap.begin()->second.begin()->second;
227 22840 : }
228 :
229 5681 : [[nodiscard]] size_t Size() const
230 : {
231 5681 : return std23::ranges::fold_left(internalMap, size_t{0},
232 3255 : [](size_t s, const auto& p) { return s + p.second.size(); });
233 : }
234 :
235 41470 : [[nodiscard]] size_t CountForSignHash(const uint256& signHash) const
236 : {
237 41470 : auto it = internalMap.find(signHash);
238 41470 : if (it == internalMap.end()) {
239 0 : return 0;
240 : }
241 41470 : return it->second.size();
242 41470 : }
243 :
244 987177 : [[nodiscard]] bool Empty() const
245 : {
246 987177 : return internalMap.empty();
247 : }
248 :
249 11463 : const std::unordered_map<uint16_t, T>* GetAllForSignHash(const uint256& signHash) const
250 : {
251 11463 : auto it = internalMap.find(signHash);
252 11463 : if (it == internalMap.end()) {
253 839 : return nullptr;
254 : }
255 10624 : return &it->second;
256 11463 : }
257 :
258 267466 : void EraseAllForSignHash(const uint256& signHash)
259 : {
260 267466 : internalMap.erase(signHash);
261 267466 : }
262 :
263 : template<typename F>
264 105697 : void EraseIf(F&& f)
265 : {
266 107164 : for (auto it = internalMap.begin(); it != internalMap.end(); ) {
267 1467 : SigShareKey k;
268 1467 : k.first = it->first;
269 3258 : for (auto jt = it->second.begin(); jt != it->second.end(); ) {
270 1791 : k.second = jt->first;
271 1791 : if (f(k, jt->second)) {
272 25 : jt = it->second.erase(jt);
273 25 : } else {
274 1766 : ++jt;
275 : }
276 : }
277 1467 : if (it->second.empty()) {
278 25 : it = internalMap.erase(it);
279 25 : } else {
280 1442 : ++it;
281 : }
282 : }
283 105697 : }
284 :
285 : template<typename F>
286 246509 : void ForEach(F&& f)
287 : {
288 306826 : for (auto& p : internalMap) {
289 60317 : SigShareKey k;
290 60317 : k.first = p.first;
291 151191 : for (auto& p2 : p.second) {
292 90874 : k.second = p2.first;
293 90874 : f(k, p2.second);
294 : }
295 : }
296 246509 : }
297 : };
298 :
299 1770 : class CSigSharesNodeState
300 : {
301 : public:
302 : // Used to avoid holding locks too long
303 34226 : struct SessionInfo
304 : {
305 34226 : Consensus::LLMQType llmqType{Consensus::LLMQType::LLMQ_NONE};
306 : uint256 quorumHash;
307 : uint256 id;
308 : uint256 msgHash;
309 : llmq::SignHash signHash;
310 :
311 : CQuorumCPtr quorum;
312 : };
313 :
314 32599 : struct Session {
315 32599 : uint32_t recvSessionId{UNINITIALIZED_SESSION_ID};
316 32599 : uint32_t sendSessionId{UNINITIALIZED_SESSION_ID};
317 :
318 : Consensus::LLMQType llmqType;
319 : uint256 quorumHash;
320 : uint256 id;
321 : uint256 msgHash;
322 : llmq::SignHash signHash;
323 :
324 : CQuorumCPtr quorum;
325 :
326 : CSigSharesInv announced;
327 : CSigSharesInv requested;
328 : CSigSharesInv knows;
329 : };
330 : // TODO limit number of sessions per node
331 : Uint256HashMap<Session> sessions;
332 :
333 : std::unordered_map<uint32_t, Session*> sessionByRecvId;
334 :
335 : SigShareMap<CSigShare> pendingIncomingSigShares;
336 : SigShareMap<int64_t> requestedSigShares;
337 :
338 1770 : bool banned{false};
339 :
340 : Session& GetOrCreateSessionFromShare(const CSigShare& sigShare);
341 : Session& GetOrCreateSessionFromAnn(const CSigSesAnn& ann);
342 : Session* GetSessionBySignHash(const uint256& signHash);
343 : Session* GetSessionByRecvId(uint32_t sessionId);
344 : bool GetSessionInfoByRecvId(uint32_t sessionId, SessionInfo& retInfo);
345 :
346 : void RemoveSession(const uint256& signHash);
347 : };
348 :
349 7083 : class CSignedSession
350 : {
351 : public:
352 : CSigShare sigShare;
353 : CQuorumCPtr quorum;
354 :
355 7083 : int64_t nextAttemptTime{0};
356 7083 : int attempt{0};
357 : };
358 :
359 : struct PendingSignatureData {
360 : const CQuorumCPtr quorum;
361 : const uint256 id;
362 : const uint256 msgHash;
363 :
364 36838 : PendingSignatureData(CQuorumCPtr quorum, const uint256& id, const uint256& msgHash) :
365 18419 : quorum(std::move(quorum)),
366 18419 : id(id),
367 18419 : msgHash(msgHash)
368 18419 : {
369 36838 : }
370 : };
371 :
372 : class CSigSharesManager : public llmq::CRecoveredSigsListener
373 : {
374 : private:
375 : static constexpr int64_t SESSION_NEW_SHARES_TIMEOUT{60};
376 : static constexpr int64_t SIG_SHARE_REQUEST_TIMEOUT{5};
377 :
378 : public:
379 : // we try to keep total message size below 10k
380 : static constexpr size_t MAX_MSGS_CNT_QSIGSESANN{100};
381 : static constexpr size_t MAX_MSGS_CNT_QSIGSHARES{200};
382 : // 400 is the maximum quorum size, so this is also the maximum number of sigs we need to support
383 : static constexpr size_t MAX_MSGS_TOTAL_BATCHED_SIGS{400};
384 : static constexpr size_t MAX_MSGS_SIG_SHARES{32};
385 :
386 : private:
387 : static constexpr int64_t EXP_SEND_FOR_RECOVERY_TIMEOUT{2000};
388 : static constexpr int64_t MAX_SEND_FOR_RECOVERY_TIMEOUT{10000};
389 :
390 : mutable Mutex cs;
391 :
392 : SigShareMap<CSigShare> sigShares GUARDED_BY(cs);
393 : Uint256HashMap<CSignedSession> signedSessions GUARDED_BY(cs);
394 :
395 : // stores time of last receivedSigShare. Used to detect timeouts
396 : Uint256HashMap<int64_t> timeSeenForSessions GUARDED_BY(cs);
397 :
398 : std::unordered_map<NodeId, CSigSharesNodeState> nodeStates GUARDED_BY(cs);
399 : SigShareMap<std::pair<NodeId, int64_t>> sigSharesRequested GUARDED_BY(cs);
400 : SigShareMap<bool> sigSharesQueuedToAnnounce GUARDED_BY(cs);
401 :
402 : Mutex cs_pendingSigns;
403 : std::vector<PendingSignatureData> pendingSigns GUARDED_BY(cs_pendingSigns);
404 :
405 : FastRandomContext rnd GUARDED_BY(cs);
406 :
407 : CConnman& m_connman;
408 : const ChainstateManager& m_chainman;
409 : CSigningManager& sigman;
410 : const CActiveMasternodeManager& m_mn_activeman;
411 : const CQuorumManager& qman;
412 : const CSporkManager& m_sporkman;
413 :
414 : CleanupThrottler<NodeClock> cleanupThrottler;
415 : std::atomic<uint32_t> recoveredSigsCounter{0};
416 :
417 : public:
418 : CSigSharesManager() = delete;
419 : CSigSharesManager(const CSigSharesManager&) = delete;
420 : CSigSharesManager& operator=(const CSigSharesManager&) = delete;
421 : explicit CSigSharesManager(CConnman& connman, const ChainstateManager& chainman, CSigningManager& _sigman,
422 : const CActiveMasternodeManager& mn_activeman, const CQuorumManager& _qman,
423 : const CSporkManager& sporkman);
424 : ~CSigSharesManager() override;
425 :
426 : void RegisterRecoveryInterface() EXCLUSIVE_LOCKS_REQUIRED(!cs);
427 : void UnregisterRecoveryInterface() EXCLUSIVE_LOCKS_REQUIRED(!cs);
428 :
429 : void AsyncSign(CQuorumCPtr quorum, const uint256& id, const uint256& msgHash)
430 : EXCLUSIVE_LOCKS_REQUIRED(!cs_pendingSigns, !cs);
431 : std::optional<CSigShare> CreateSigShare(const CQuorum& quorum, const uint256& id, const uint256& msgHash) const
432 : EXCLUSIVE_LOCKS_REQUIRED(!cs);
433 : void ForceReAnnouncement(const CQuorum& quorum, Consensus::LLMQType llmqType, const uint256& id,
434 : const uint256& msgHash) EXCLUSIVE_LOCKS_REQUIRED(!cs);
435 :
436 : [[nodiscard]] RecoveredSigResult HandleNewRecoveredSig(const CRecoveredSig& recoveredSig) override
437 : EXCLUSIVE_LOCKS_REQUIRED(!cs);
438 :
439 : static CDeterministicMNCPtr SelectMemberForRecovery(const CQuorum& quorum, const uint256& id, int attempt);
440 :
441 : bool AsyncSignIfMember(Consensus::LLMQType llmqType, CSigningManager& sigman, const uint256& id,
442 : const uint256& msgHash, const uint256& quorumHash = uint256(), bool allowReSign = false,
443 : bool allowDiffMsgHashSigning = false) EXCLUSIVE_LOCKS_REQUIRED(!cs_pendingSigns, !cs);
444 : private:
445 : std::optional<CSigShare> CreateSigShareForSingleMember(const CQuorum& quorum, const uint256& id, const uint256& msgHash) const;
446 :
447 : public:
448 : // all of these return false when the currently processed message should be aborted (as each message actually contains multiple messages) and ban node
449 : bool ProcessMessageSigSesAnn(const CNode& pfrom, const CSigSesAnn& ann) EXCLUSIVE_LOCKS_REQUIRED(!cs);
450 : bool ProcessMessageSigShares(const CNode& pfrom, const CSigSharesInv& inv, const std::string& msg_type)
451 : EXCLUSIVE_LOCKS_REQUIRED(!cs);
452 : bool ProcessMessageBatchedSigShares(const CNode& pfrom, const CBatchedSigShares& batchedSigShares)
453 : EXCLUSIVE_LOCKS_REQUIRED(!cs);
454 :
455 : // if ProcessMessageSigShare returns false the node should be banned
456 : bool ProcessMessageSigShare(NodeId fromId, const CSigShare& sigShare) EXCLUSIVE_LOCKS_REQUIRED(!cs);
457 :
458 : // CollectPendingSigSharesToVerify returns true if there's more work to do
459 : bool CollectPendingSigSharesToVerify(
460 : size_t maxUniqueSessions, std::unordered_map<NodeId, std::vector<CSigShare>>& retSigShares,
461 : std::unordered_map<std::pair<Consensus::LLMQType, uint256>, CQuorumCPtr, StaticSaltedHasher>& retQuorums)
462 : EXCLUSIVE_LOCKS_REQUIRED(!cs);
463 :
464 : std::vector<std::shared_ptr<CRecoveredSig>> ProcessPendingSigShares(
465 : const std::vector<CSigShare>& sigSharesToProcess,
466 : const std::unordered_map<std::pair<Consensus::LLMQType, uint256>, CQuorumCPtr, StaticSaltedHasher>& quorums)
467 : EXCLUSIVE_LOCKS_REQUIRED(!cs);
468 :
469 : private:
470 : [[nodiscard]] std::shared_ptr<CRecoveredSig> ProcessSigShare(const CSigShare& sigShare, const CQuorumCPtr& quorum)
471 : EXCLUSIVE_LOCKS_REQUIRED(!cs);
472 : [[nodiscard]] std::shared_ptr<CRecoveredSig> TryRecoverSig(const CQuorum& quorum, const uint256& id,
473 : const uint256& msgHash) EXCLUSIVE_LOCKS_REQUIRED(!cs);
474 :
475 : bool GetSessionInfoByRecvId(NodeId nodeId, uint32_t sessionId, CSigSharesNodeState::SessionInfo& retInfo)
476 : EXCLUSIVE_LOCKS_REQUIRED(!cs);
477 : static CSigShare RebuildSigShare(const CSigSharesNodeState::SessionInfo& session, const std::pair<uint16_t, CBLSLazySignature>& in);
478 :
479 : void RemoveSigSharesForSession(const uint256& signHash) EXCLUSIVE_LOCKS_REQUIRED(cs);
480 :
481 : public:
482 : void RemoveNodesIf(std::function<bool(NodeId)> predicate) EXCLUSIVE_LOCKS_REQUIRED(!cs);
483 : void MarkAsBanned(NodeId nodeId) EXCLUSIVE_LOCKS_REQUIRED(!cs);
484 :
485 : private:
486 : void CollectSigSharesToRequest(std::unordered_map<NodeId, Uint256HashMap<CSigSharesInv>>& sigSharesToRequest)
487 : EXCLUSIVE_LOCKS_REQUIRED(cs);
488 : void CollectSigSharesToSend(std::unordered_map<NodeId, Uint256HashMap<CBatchedSigShares>>& sigSharesToSend)
489 : EXCLUSIVE_LOCKS_REQUIRED(cs);
490 : void CollectSigSharesToSendConcentrated(std::unordered_map<NodeId, std::vector<CSigShare>>& sigSharesToSend, const std::vector<CNode*>& vNodes) EXCLUSIVE_LOCKS_REQUIRED(cs);
491 : void CollectSigSharesToAnnounce(std::unordered_map<NodeId, Uint256HashMap<CSigSharesInv>>& sigSharesToAnnounce)
492 : EXCLUSIVE_LOCKS_REQUIRED(cs);
493 :
494 : public:
495 : void Cleanup() EXCLUSIVE_LOCKS_REQUIRED(!cs);
496 : bool SendMessages() EXCLUSIVE_LOCKS_REQUIRED(!cs);
497 :
498 : // Dispatcher functions
499 : std::vector<PendingSignatureData> DispatchPendingSigns() EXCLUSIVE_LOCKS_REQUIRED(!cs_pendingSigns);
500 : // Worker pool task functions
501 : bool IsAnyPendingProcessing() const EXCLUSIVE_LOCKS_REQUIRED(!cs);
502 : [[nodiscard]] std::shared_ptr<CRecoveredSig> SignAndProcessSingleShare(PendingSignatureData work)
503 : EXCLUSIVE_LOCKS_REQUIRED(!cs_pendingSigns, !cs);
504 : };
505 : } // namespace llmq
506 :
507 : #endif // BITCOIN_LLMQ_SIGNING_SHARES_H
|