1717
1818#include < algorithm>
1919#include < limits>
20+ #include < unordered_set>
2021
2122namespace llmq
2223{
@@ -79,8 +80,23 @@ bool CRecoveredSigsDb::HasRecoveredSigForSession(const uint256& signHash)
7980
8081bool CRecoveredSigsDb::HasRecoveredSigForHash (const uint256& hash)
8182{
83+ int64_t t = GetTimeMillis ();
84+
85+ {
86+ LOCK (cs);
87+ auto it = hasSigForHashCache.find (hash);
88+ if (it != hasSigForHashCache.end ()) {
89+ it->second .second = t;
90+ return it->second .first ;
91+ }
92+ }
93+
8294 auto k = std::make_tuple (' h' , hash);
83- return db.Exists (k);
95+ bool ret = db.Exists (k);
96+
97+ LOCK (cs);
98+ hasSigForHashCache.emplace (hash, std::make_pair (ret, t));
99+ return ret;
84100}
85101
86102bool CRecoveredSigsDb::ReadRecoveredSig (Consensus::LLMQType llmqType, const uint256& id, CRecoveredSig& ret)
@@ -152,13 +168,14 @@ void CRecoveredSigsDb::WriteRecoveredSig(const llmq::CRecoveredSig& recSig)
152168 LOCK (cs);
153169 hasSigForIdCache[std::make_pair ((Consensus::LLMQType)recSig.llmqType , recSig.id )] = std::make_pair (true , t);
154170 hasSigForSessionCache[signHash] = std::make_pair (true , t);
171+ hasSigForHashCache[recSig.GetHash ()] = std::make_pair (true , t);
155172 }
156173}
157174
158- template <typename K>
159- static void TruncateCacheMap (std::unordered_map<K, std::pair<bool , int64_t >>& m, size_t maxSize, size_t truncateThreshold)
175+ template <typename K, typename H >
176+ static void TruncateCacheMap (std::unordered_map<K, std::pair<bool , int64_t >, H >& m, size_t maxSize, size_t truncateThreshold)
160177{
161- typedef typename std::unordered_map<K, std::pair<bool , int64_t >> Map;
178+ typedef typename std::unordered_map<K, std::pair<bool , int64_t >, H > Map;
162179 typedef typename Map::iterator Iterator;
163180
164181 if (m.size () <= truncateThreshold) {
@@ -237,10 +254,12 @@ void CRecoveredSigsDb::CleanupOldRecoveredSigs(int64_t maxAge)
237254
238255 hasSigForIdCache.erase (std::make_pair ((Consensus::LLMQType)recSig.llmqType , recSig.id ));
239256 hasSigForSessionCache.erase (signHash);
257+ hasSigForHashCache.erase (recSig.GetHash ());
240258 }
241259
242260 TruncateCacheMap (hasSigForIdCache, MAX_CACHE_SIZE, MAX_CACHE_TRUNCATE_THRESHOLD);
243261 TruncateCacheMap (hasSigForSessionCache, MAX_CACHE_SIZE, MAX_CACHE_TRUNCATE_THRESHOLD);
262+ TruncateCacheMap (hasSigForHashCache, MAX_CACHE_SIZE, MAX_CACHE_TRUNCATE_THRESHOLD);
244263 }
245264
246265 for (auto & e : toDelete2) {
@@ -355,18 +374,17 @@ bool CSigningManager::PreVerifyRecoveredSig(NodeId nodeId, const CRecoveredSig&
355374
356375void CSigningManager::CollectPendingRecoveredSigsToVerify (
357376 size_t maxUniqueSessions,
358- std::map <NodeId, std::list<CRecoveredSig>>& retSigShares,
359- std::map <std::pair<Consensus::LLMQType, uint256>, CQuorumCPtr>& retQuorums)
377+ std::unordered_map <NodeId, std::list<CRecoveredSig>>& retSigShares,
378+ std::unordered_map <std::pair<Consensus::LLMQType, uint256>, CQuorumCPtr, StaticSaltedHasher >& retQuorums)
360379{
361380 {
362381 LOCK (cs);
363382 if (pendingRecoveredSigs.empty ()) {
364383 return ;
365384 }
366385
367- std::set<std::pair<NodeId, uint256>> uniqueSignHashes;
368- llmq::utils::IterateNodesRandom (
369- pendingRecoveredSigs, [&]() { return uniqueSignHashes.size () < maxUniqueSessions; }, [&](NodeId nodeId, std::list<CRecoveredSig>& ns) {
386+ std::unordered_set<std::pair<NodeId, uint256>, StaticSaltedHasher> uniqueSignHashes;
387+ llmq::utils::IterateNodesRandom (pendingRecoveredSigs, [&]() { return uniqueSignHashes.size () < maxUniqueSessions; }, [&](NodeId nodeId, std::list<CRecoveredSig>& ns) {
370388 if (ns.empty ()) {
371389 return false ;
372390 }
@@ -419,8 +437,8 @@ void CSigningManager::CollectPendingRecoveredSigsToVerify(
419437
420438bool CSigningManager::ProcessPendingRecoveredSigs (CConnman& connman)
421439{
422- std::map <NodeId, std::list<CRecoveredSig>> recSigsByNode;
423- std::map <std::pair<Consensus::LLMQType, uint256>, CQuorumCPtr> quorums;
440+ std::unordered_map <NodeId, std::list<CRecoveredSig>> recSigsByNode;
441+ std::unordered_map <std::pair<Consensus::LLMQType, uint256>, CQuorumCPtr, StaticSaltedHasher > quorums;
424442
425443 CollectPendingRecoveredSigsToVerify (32 , recSigsByNode, quorums);
426444 if (recSigsByNode.empty ()) {
@@ -443,13 +461,13 @@ bool CSigningManager::ProcessPendingRecoveredSigs(CConnman& connman)
443461 }
444462 }
445463
446- cxxtimer::Timer verifyTimer;
464+ cxxtimer::Timer verifyTimer ( true ) ;
447465 batchVerifier.Verify ();
448466 verifyTimer.stop ();
449467
450468 LogPrintf (" llmq" , " CSigningManager::%s -- verified recovered sig(s). count=%d, vt=%d, nodes=%d\n " , __func__, verifyCount, verifyTimer.count (), recSigsByNode.size ());
451469
452- std::set <uint256> processed;
470+ std::unordered_set <uint256, StaticSaltedHasher > processed;
453471 for (auto & p : recSigsByNode) {
454472 NodeId nodeId = p.first ;
455473 auto & v = p.second ;
@@ -494,11 +512,25 @@ void CSigningManager::ProcessRecoveredSig(NodeId nodeId, const CRecoveredSig& re
494512 signHash.ToString (), recoveredSig.id .ToString (), recoveredSig.msgHash .ToString (), nodeId);
495513
496514 if (db.HasRecoveredSigForId (llmqType, recoveredSig.id )) {
497- // this should really not happen, as each masternode is participating in only one vote,
498- // even if it's a member of multiple quorums. so a majority is only possible on one quorum and one msgHash per id
499- LogPrintf (" CSigningManager::%s -- conflicting recoveredSig for id=%s, msgHash=%s\n " , __func__,
500- recoveredSig.id .ToString (), recoveredSig.msgHash .ToString ());
501- return ;
515+ CRecoveredSig otherRecoveredSig;
516+ if (db.GetRecoveredSigById (llmqType, recoveredSig.id , otherRecoveredSig)) {
517+ auto otherSignHash = llmq::utils::BuildSignHash (recoveredSig);
518+ if (signHash != otherSignHash) {
519+ // this should really not happen, as each masternode is participating in only one vote,
520+ // even if it's a member of multiple quorums. so a majority is only possible on one quorum and one msgHash per id
521+ LogPrintf (" CSigningManager::%s -- conflicting recoveredSig for signHash=%s, id=%s, msgHash=%s, otherSignHash=%s\n " , __func__,
522+ signHash.ToString (), recoveredSig.id .ToString (), recoveredSig.msgHash .ToString (), otherSignHash.ToString ());
523+ } else {
524+ // Looks like we're trying to process a recSig that is already known. This might happen if the same
525+ // recSig comes in through regular QRECSIG messages and at the same time through some other message
526+ // which allowed to reconstruct a recSig (e.g. IXLOCK). In this case, just bail out.
527+ }
528+ return ;
529+ } else {
530+ // This case is very unlikely. It can only happen when cleanup caused this specific recSig to vanish
531+ // between the HasRecoveredSigForId and GetRecoveredSigById call. If that happens, treat it as if we
532+ // never had that recSig
533+ }
502534 }
503535
504536 db.WriteRecoveredSig (recoveredSig);
@@ -552,14 +584,19 @@ bool CSigningManager::AsyncSignIfMember(Consensus::LLMQType llmqType, const uint
552584 if (db.HasVotedOnId (llmqType, id)) {
553585 uint256 prevMsgHash;
554586 db.GetVoteForId (llmqType, id, prevMsgHash);
555- LogPrintf (" CSigningManager::%s -- already voted for id=%s and msgHash=%s. Not voting on conflicting msgHash=%s\n " , __func__,
556- id.ToString (), prevMsgHash.ToString (), msgHash.ToString ());
587+ if (msgHash != prevMsgHash) {
588+ LogPrintf (" CSigningManager::%s -- already voted for id=%s and msgHash=%s. Not voting on conflicting msgHash=%s\n " , __func__,
589+ id.ToString (), prevMsgHash.ToString (), msgHash.ToString ());
590+ } else {
591+ LogPrintf (" CSigningManager::%s -- already voted for id=%s and msgHash=%s. Not voting again.\n " , __func__,
592+ id.ToString (), prevMsgHash.ToString ());
593+ }
557594 return false ;
558595 }
559596
560597 if (db.HasRecoveredSigForId (llmqType, id)) {
561598 // no need to sign it if we already have a recovered sig
562- return false ;
599+ return true ;
563600 }
564601 db.WriteVoteForId (llmqType, id, msgHash);
565602 }
0 commit comments