Dash Core Source Documentation (0.16.0.1)

Find detailed information regarding the Dash Core source code.

quorums_signing_shares.h
Go to the documentation of this file.
1 // Copyright (c) 2018-2019 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 DASH_QUORUMS_SIGNING_SHARES_H
6 #define DASH_QUORUMS_SIGNING_SHARES_H
7 
8 #include <bls/bls.h>
9 #include <chainparams.h>
10 #include <net.h>
11 #include <random.h>
12 #include <saltedhasher.h>
13 #include <serialize.h>
14 #include <sync.h>
15 #include <tinyformat.h>
16 #include <uint256.h>
17 
18 #include <llmq/quorums.h>
19 
20 #include <thread>
21 #include <mutex>
22 #include <unordered_map>
23 #include <unordered_set>
24 
25 class CEvoDB;
26 class CScheduler;
27 
28 namespace llmq
29 {
30 // <signHash, quorumMember>
31 typedef std::pair<uint256, uint16_t> SigShareKey;
32 
33 class CSigShare
34 {
35 public:
38  uint16_t quorumMember;
42 
44 
45 public:
46  void UpdateKey();
47  const SigShareKey& GetKey() const
48  {
49  return key;
50  }
51  const uint256& GetSignHash() const
52  {
53  assert(!key.first.IsNull());
54  return key.first;
55  }
56 
58 
59  template<typename Stream, typename Operation>
60  inline void SerializationOp(Stream& s, Operation ser_action) {
64  READWRITE(id);
67 
68  if (ser_action.ForRead()) {
69  UpdateKey();
70  }
71  }
72 };
73 
74 // Nodes will first announce a signing session with a sessionId to be used in all future P2P messages related to that
75 // session. We locally keep track of the mapping for each node. We also assign new sessionIds for outgoing sessions
76 // and send QSIGSESANN messages appropriately. All values except the max value for uint32_t are valid as sessionId
78 {
79 public:
80  uint32_t sessionId{(uint32_t)-1};
85 
87 
88  template<typename Stream, typename Operation>
89  inline void SerializationOp(Stream& s, Operation ser_action) {
93  READWRITE(id);
95  }
96 
97  std::string ToString() const;
98 };
99 
101 {
102 public:
103  uint32_t sessionId{(uint32_t)-1};
104  std::vector<bool> inv;
105 
106 public:
108 
109  template<typename Stream, typename Operation>
110  inline void SerializationOp(Stream& s, Operation ser_action)
111  {
112  uint64_t invSize = inv.size();
113 
115  READWRITE(COMPACTSIZE(invSize));
116  READWRITE(AUTOBITSET(inv, (size_t)invSize));
117  }
118 
119  void Init(size_t size);
120  bool IsSet(uint16_t quorumMember) const;
121  void Set(uint16_t quorumMember, bool v);
122  void SetAll(bool v);
123  void Merge(const CSigSharesInv& inv2);
124 
125  size_t CountSet() const;
126  std::string ToString() const;
127 };
128 
129 // sent through the message QBSIGSHARES as a vector of multiple batches
131 {
132 public:
133  uint32_t sessionId{(uint32_t)-1};
134  std::vector<std::pair<uint16_t, CBLSLazySignature>> sigShares;
135 
136 public:
138 
139  template<typename Stream, typename Operation>
140  inline void SerializationOp(Stream& s, Operation ser_action)
141  {
144  }
145 
146  std::string ToInvString() const;
147 };
148 
149 template<typename T>
151 {
152 private:
153  std::unordered_map<uint256, std::unordered_map<uint16_t, T>, StaticSaltedHasher> internalMap;
154 
155 public:
156  bool Add(const SigShareKey& k, const T& v)
157  {
158  auto& m = internalMap[k.first];
159  return m.emplace(k.second, v).second;
160  }
161 
162  void Erase(const SigShareKey& k)
163  {
164  auto it = internalMap.find(k.first);
165  if (it == internalMap.end()) {
166  return;
167  }
168  it->second.erase(k.second);
169  if (it->second.empty()) {
170  internalMap.erase(it);
171  }
172  }
173 
174  void Clear()
175  {
176  internalMap.clear();
177  }
178 
179  bool Has(const SigShareKey& k) const
180  {
181  auto it = internalMap.find(k.first);
182  if (it == internalMap.end()) {
183  return false;
184  }
185  return it->second.count(k.second) != 0;
186  }
187 
188  T* Get(const SigShareKey& k)
189  {
190  auto it = internalMap.find(k.first);
191  if (it == internalMap.end()) {
192  return nullptr;
193  }
194 
195  auto jt = it->second.find(k.second);
196  if (jt == it->second.end()) {
197  return nullptr;
198  }
199 
200  return &jt->second;
201  }
202 
203  T& GetOrAdd(const SigShareKey& k)
204  {
205  T* v = Get(k);
206  if (!v) {
207  Add(k, T());
208  v = Get(k);
209  }
210  return *v;
211  }
212 
213  const T* GetFirst() const
214  {
215  if (internalMap.empty()) {
216  return nullptr;
217  }
218  return &internalMap.begin()->second.begin()->second;
219  }
220 
221  size_t Size() const
222  {
223  size_t s = 0;
224  for (auto& p : internalMap) {
225  s += p.second.size();
226  }
227  return s;
228  }
229 
230  size_t CountForSignHash(const uint256& signHash) const
231  {
232  auto it = internalMap.find(signHash);
233  if (it == internalMap.end()) {
234  return 0;
235  }
236  return it->second.size();
237  }
238 
239  bool Empty() const
240  {
241  return internalMap.empty();
242  }
243 
244  const std::unordered_map<uint16_t, T>* GetAllForSignHash(const uint256& signHash)
245  {
246  auto it = internalMap.find(signHash);
247  if (it == internalMap.end()) {
248  return nullptr;
249  }
250  return &it->second;
251  }
252 
253  void EraseAllForSignHash(const uint256& signHash)
254  {
255  internalMap.erase(signHash);
256  }
257 
258  template<typename F>
259  void EraseIf(F&& f)
260  {
261  for (auto it = internalMap.begin(); it != internalMap.end(); ) {
262  SigShareKey k;
263  k.first = it->first;
264  for (auto jt = it->second.begin(); jt != it->second.end(); ) {
265  k.second = jt->first;
266  if (f(k, jt->second)) {
267  jt = it->second.erase(jt);
268  } else {
269  ++jt;
270  }
271  }
272  if (it->second.empty()) {
273  it = internalMap.erase(it);
274  } else {
275  ++it;
276  }
277  }
278  }
279 
280  template<typename F>
281  void ForEach(F&& f)
282  {
283  for (auto& p : internalMap) {
284  SigShareKey k;
285  k.first = p.first;
286  for (auto& p2 : p.second) {
287  k.second = p2.first;
288  f(k, p2.second);
289  }
290  }
291  }
292 };
293 
295 {
296 public:
297  // Used to avoid holding locks too long
298  struct SessionInfo
299  {
305 
307  };
308 
309  struct Session {
310  uint32_t recvSessionId{(uint32_t)-1};
311  uint32_t sendSessionId{(uint32_t)-1};
312 
318 
320 
324  };
325  // TODO limit number of sessions per node
326  std::unordered_map<uint256, Session, StaticSaltedHasher> sessions;
327 
328  std::unordered_map<uint32_t, Session*> sessionByRecvId;
329  uint32_t nextSendSessionId{1};
330 
333 
334  bool banned{false};
335 
336  Session& GetOrCreateSessionFromShare(const CSigShare& sigShare);
337  Session& GetOrCreateSessionFromAnn(const CSigSesAnn& ann);
338  Session* GetSessionBySignHash(const uint256& signHash);
339  Session* GetSessionByRecvId(uint32_t sessionId);
340  bool GetSessionInfoByRecvId(uint32_t sessionId, SessionInfo& retInfo);
341 
342  void RemoveSession(const uint256& signHash);
343 };
344 
346 {
347 public:
350 
351  int64_t nextAttemptTime{0};
352  int attempt{0};
353 };
354 
356 {
357  static const int64_t SESSION_NEW_SHARES_TIMEOUT = 60;
358  static const int64_t SIG_SHARE_REQUEST_TIMEOUT = 5;
359 
360  // we try to keep total message size below 10k
361  const size_t MAX_MSGS_CNT_QSIGSESANN = 100;
362  const size_t MAX_MSGS_CNT_QGETSIGSHARES = 200;
363  const size_t MAX_MSGS_CNT_QSIGSHARESINV = 200;
364  // 400 is the maximum quorum size, so this is also the maximum number of sigs we need to support
365  const size_t MAX_MSGS_TOTAL_BATCHED_SIGS = 400;
366 
367  const int64_t EXP_SEND_FOR_RECOVERY_TIMEOUT = 2000;
368  const int64_t MAX_SEND_FOR_RECOVERY_TIMEOUT = 10000;
369  const size_t MAX_MSGS_SIG_SHARES = 32;
370 
371 private:
373 
374  std::thread workThread;
376 
378  std::unordered_map<uint256, CSignedSession, StaticSaltedHasher> signedSessions;
379 
380  // stores time of last receivedSigShare. Used to detect timeouts
381  std::unordered_map<uint256, int64_t, StaticSaltedHasher> timeSeenForSessions;
382 
383  std::unordered_map<NodeId, CSigSharesNodeState> nodeStates;
386 
387  std::vector<std::tuple<const CQuorumCPtr, uint256, uint256>> pendingSigns;
388 
389  // must be protected by cs
391 
392  int64_t lastCleanupTime{0};
393  std::atomic<uint32_t> recoveredSigsCounter{0};
394 
395 public:
398 
399  void StartWorkerThread();
400  void StopWorkerThread();
403  void InterruptWorkerThread();
404 
405 public:
406  void ProcessMessage(CNode* pnode, const std::string& strCommand, CDataStream& vRecv, CConnman& connman);
407 
408  void AsyncSign(const CQuorumCPtr& quorum, const uint256& id, const uint256& msgHash);
409  void Sign(const CQuorumCPtr& quorum, const uint256& id, const uint256& msgHash);
410  void ForceReAnnouncement(const CQuorumCPtr& quorum, Consensus::LLMQType llmqType, const uint256& id, const uint256& msgHash);
411 
412  void HandleNewRecoveredSig(const CRecoveredSig& recoveredSig);
413 
414  static CDeterministicMNCPtr SelectMemberForRecovery(const CQuorumCPtr& quorum, const uint256& id, int attempt);
415 
416 private:
417  // all of these return false when the currently processed message should be aborted (as each message actually contains multiple messages)
418  bool ProcessMessageSigSesAnn(CNode* pfrom, const CSigSesAnn& ann, CConnman& connman);
419  bool ProcessMessageSigSharesInv(CNode* pfrom, const CSigSharesInv& inv, CConnman& connman);
420  bool ProcessMessageGetSigShares(CNode* pfrom, const CSigSharesInv& inv, CConnman& connman);
421  bool ProcessMessageBatchedSigShares(CNode* pfrom, const CBatchedSigShares& batchedSigShares, CConnman& connman);
422  void ProcessMessageSigShare(NodeId fromId, const CSigShare& sigShare, CConnman& connman);
423 
424  bool VerifySigSharesInv(NodeId from, Consensus::LLMQType llmqType, const CSigSharesInv& inv);
425  bool PreVerifyBatchedSigShares(NodeId nodeId, const CSigSharesNodeState::SessionInfo& session, const CBatchedSigShares& batchedSigShares, bool& retBan);
426 
427  void CollectPendingSigSharesToVerify(size_t maxUniqueSessions,
428  std::unordered_map<NodeId, std::vector<CSigShare>>& retSigShares,
429  std::unordered_map<std::pair<Consensus::LLMQType, uint256>, CQuorumCPtr, StaticSaltedHasher>& retQuorums);
430  bool ProcessPendingSigShares(CConnman& connman);
431 
433  const std::vector<CSigShare>& sigShares,
434  const std::unordered_map<std::pair<Consensus::LLMQType, uint256>, CQuorumCPtr, StaticSaltedHasher>& quorums,
435  CConnman& connman);
436 
437  void ProcessSigShare(NodeId nodeId, const CSigShare& sigShare, CConnman& connman, const CQuorumCPtr& quorum);
438  void TryRecoverSig(const CQuorumCPtr& quorum, const uint256& id, const uint256& msgHash, CConnman& connman);
439 
440 private:
441  bool GetSessionInfoByRecvId(NodeId nodeId, uint32_t sessionId, CSigSharesNodeState::SessionInfo& retInfo);
442  CSigShare RebuildSigShare(const CSigSharesNodeState::SessionInfo& session, const CBatchedSigShares& batchedSigShares, size_t idx);
443 
444  void Cleanup();
445  void RemoveSigSharesForSession(const uint256& signHash);
446  void RemoveBannedNodeStates();
447 
448  void BanNode(NodeId nodeId);
449 
450  bool SendMessages();
451  void CollectSigSharesToRequest(std::unordered_map<NodeId, std::unordered_map<uint256, CSigSharesInv, StaticSaltedHasher>>& sigSharesToRequest);
452  void CollectSigSharesToSend(std::unordered_map<NodeId, std::unordered_map<uint256, CBatchedSigShares, StaticSaltedHasher>>& sigSharesToSend);
453  void CollectSigSharesToSendConcentrated(std::unordered_map<NodeId, std::vector<CSigShare>>& sigSharesToSend, const std::vector<CNode*>& vNodes);
454  void CollectSigSharesToAnnounce(std::unordered_map<NodeId, std::unordered_map<uint256, CSigSharesInv, StaticSaltedHasher>>& sigSharesToAnnounce);
455  bool SignPendingSigShares();
456  void WorkThreadMain();
457 };
458 
459 extern CSigSharesManager* quorumSigSharesManager;
460 
461 } // namespace llmq
462 
463 #endif //DASH_QUORUMS_SIGNING_SHARES_H
bool ProcessMessageBatchedSigShares(CNode *pfrom, const CBatchedSigShares &batchedSigShares, CConnman &connman)
std::string ToString() const
#define VARINT(obj)
Definition: serialize.h:375
void Set(uint16_t quorumMember, bool v)
std::unordered_map< uint32_t, Session * > sessionByRecvId
CSigSharesManager * quorumSigSharesManager
UniValue quorum(const JSONRPCRequest &request)
Definition: rpcquorums.cpp:492
std::unordered_map< uint256, std::unordered_map< uint16_t, T >, StaticSaltedHasher > internalMap
Definition: evodb.h:32
#define READWRITE(obj)
Definition: serialize.h:165
bool ProcessMessageGetSigShares(CNode *pfrom, const CSigSharesInv &inv, CConnman &connman)
static const int64_t SESSION_NEW_SHARES_TIMEOUT
Session * GetSessionBySignHash(const uint256 &signHash)
void RemoveSigSharesForSession(const uint256 &signHash)
std::atomic< uint32_t > recoveredSigsCounter
void Erase(const SigShareKey &k)
bool ProcessPendingSigShares(CConnman &connman)
#define COMPACTSIZE(obj)
Definition: serialize.h:376
void RemoveSession(const uint256 &signHash)
Session & GetOrCreateSessionFromShare(const CSigShare &sigShare)
bool ProcessMessageSigSharesInv(CNode *pfrom, const CSigSharesInv &inv, CConnman &connman)
std::vector< bool > inv
Consensus::LLMQType llmqType
#define AUTOBITSET(obj, size)
Definition: serialize.h:374
Double ended buffer combining vector and stream-like interfaces.
Definition: streams.h:103
std::shared_ptr< const CDeterministicMN > CDeterministicMNCPtr
void CollectSigSharesToAnnounce(std::unordered_map< NodeId, std::unordered_map< uint256, CSigSharesInv, StaticSaltedHasher >> &sigSharesToAnnounce)
const std::unordered_map< uint16_t, T > * GetAllForSignHash(const uint256 &signHash)
SigShareMap< bool > sigSharesToAnnounce
bool IsSet(uint16_t quorumMember) const
bool Has(const SigShareKey &k) const
SigShareMap< int64_t > requestedSigShares
bool PreVerifyBatchedSigShares(NodeId nodeId, const CSigSharesNodeState::SessionInfo &session, const CBatchedSigShares &batchedSigShares, bool &retBan)
static CDeterministicMNCPtr SelectMemberForRecovery(const CQuorumCPtr &quorum, const uint256 &id, int attempt)
std::unordered_map< uint256, CSignedSession, StaticSaltedHasher > signedSessions
ADD_SERIALIZE_METHODS void SerializationOp(Stream &s, Operation ser_action)
SigShareMap< std::pair< NodeId, int64_t > > sigSharesRequested
LLMQType
Definition: params.h:48
T * Get(const SigShareKey &k)
std::unordered_map< uint256, Session, StaticSaltedHasher > sessions
std::vector< std::tuple< const CQuorumCPtr, uint256, uint256 > > pendingSigns
void SerializationOp(Stream &s, Operation ser_action)
#define ADD_SERIALIZE_METHODS
Implement three methods for serializable objects.
Definition: serialize.h:174
void ForceReAnnouncement(const CQuorumCPtr &quorum, Consensus::LLMQType llmqType, const uint256 &id, const uint256 &msgHash)
Fast randomness source.
Definition: random.h:48
T & GetOrAdd(const SigShareKey &k)
void CollectSigSharesToSendConcentrated(std::unordered_map< NodeId, std::vector< CSigShare >> &sigSharesToSend, const std::vector< CNode *> &vNodes)
SigShareMap< CSigShare > pendingIncomingSigShares
int64_t NodeId
Definition: net.h:109
Definition: net.h:136
void ProcessMessage(CNode *pnode, const std::string &strCommand, CDataStream &vRecv, CConnman &connman)
CBLSLazySignature sigShare
std::pair< uint256, uint16_t > SigShareKey
static const int64_t SIG_SHARE_REQUEST_TIMEOUT
ADD_SERIALIZE_METHODS void SerializationOp(Stream &s, Operation ser_action)
size_t CountForSignHash(const uint256 &signHash) const
void Merge(const CSigSharesInv &inv2)
SigShareMap< CSigShare > sigShares
const int64_t EXP_SEND_FOR_RECOVERY_TIMEOUT
const uint256 & GetSignHash() const
256-bit opaque blob.
Definition: uint256.h:123
void CollectSigSharesToRequest(std::unordered_map< NodeId, std::unordered_map< uint256, CSigSharesInv, StaticSaltedHasher >> &sigSharesToRequest)
void AsyncSign(const CQuorumCPtr &quorum, const uint256 &id, const uint256 &msgHash)
void ProcessPendingSigSharesFromNode(NodeId nodeId, const std::vector< CSigShare > &sigShares, const std::unordered_map< std::pair< Consensus::LLMQType, uint256 >, CQuorumCPtr, StaticSaltedHasher > &quorums, CConnman &connman)
bool GetSessionInfoByRecvId(NodeId nodeId, uint32_t sessionId, CSigSharesNodeState::SessionInfo &retInfo)
std::shared_ptr< const CQuorum > CQuorumCPtr
Definition: quorums.h:73
bool Add(const SigShareKey &k, const T &v)
void TryRecoverSig(const CQuorumCPtr &quorum, const uint256 &id, const uint256 &msgHash, CConnman &connman)
std::unordered_map< uint256, int64_t, StaticSaltedHasher > timeSeenForSessions
const T * GetFirst() const
bool GetSessionInfoByRecvId(uint32_t sessionId, SessionInfo &retInfo)
Session * GetSessionByRecvId(uint32_t sessionId)
void EraseAllForSignHash(const uint256 &signHash)
void ProcessSigShare(NodeId nodeId, const CSigShare &sigShare, CConnman &connman, const CQuorumCPtr &quorum)
Session & GetOrCreateSessionFromAnn(const CSigSesAnn &ann)
std::vector< std::pair< uint16_t, CBLSLazySignature > > sigShares
ADD_SERIALIZE_METHODS void SerializationOp(Stream &s, Operation ser_action)
void HandleNewRecoveredSig(const CRecoveredSig &recoveredSig)
void CollectSigSharesToSend(std::unordered_map< NodeId, std::unordered_map< uint256, CBatchedSigShares, StaticSaltedHasher >> &sigSharesToSend)
const SigShareKey & GetKey() const
Information about a peer.
Definition: net.h:800
CSigShare RebuildSigShare(const CSigSharesNodeState::SessionInfo &session, const CBatchedSigShares &batchedSigShares, size_t idx)
std::string ToString() const
bool ProcessMessageSigSesAnn(CNode *pfrom, const CSigSesAnn &ann, CConnman &connman)
void CollectPendingSigSharesToVerify(size_t maxUniqueSessions, std::unordered_map< NodeId, std::vector< CSigShare >> &retSigShares, std::unordered_map< std::pair< Consensus::LLMQType, uint256 >, CQuorumCPtr, StaticSaltedHasher > &retQuorums)
Consensus::LLMQType llmqType
std::unordered_map< NodeId, CSigSharesNodeState > nodeStates
const int64_t MAX_SEND_FOR_RECOVERY_TIMEOUT
void ProcessMessageSigShare(NodeId fromId, const CSigShare &sigShare, CConnman &connman)
Wrapped mutex: supports recursive locking, but no waiting TODO: We should move away from using the re...
Definition: sync.h:94
void Sign(const CQuorumCPtr &quorum, const uint256 &id, const uint256 &msgHash)
bool VerifySigSharesInv(NodeId from, Consensus::LLMQType llmqType, const CSigSharesInv &inv)
Released under the MIT license