Skip to content

Commit 717728b

Browse files
committed
Merge #2941: More Dash llmq backports
a29d294 Fix deadlock in CSigSharesManager::SendMessages (#2757) (Alexander Block) b4a4e09 Ignore sig share inv messages when we don't have the quorum vvec (#2733) (Alexander Block) a2fb276 On timeout, print members proTxHashes from members which did not send a share (#2731) (Alexander Block) d1084e0 Actually start the timers for sig share and recSig verification (#2730) (Alexander Block) 71092e0 Send/Receive multiple messages as part of one P2P message in CSigSharesManager (#2729) (Alexander Block) e73c238 Merge pull request #2726 from codablock/pr_llmq_sessionids (UdjinM6) 7ccd790 Merge pull request #2725 from codablock/pr_llmq_hashmaps (Alexander Block) a0084f5 Multiple fixes and optimizations for LLMQs and ChainLocks (#2724) (Alexander Block) 0613978 Cleanup successful sessions before doing timeout check (#2712) (Alexander Block) c9127e1 Avoid using ordered maps in LLMQ signing code (#2708) (Alexander Block) Pull request description: Follow up of #2921 each commit backports a PR. you can find the number of the PR in the commit description ACKs for top commit: a29d294 Duddino: utACK a29d294 Fuzzbawls: utACK a29d294 Tree-SHA512: 75483d543f39d85a2924606b1f7c359a45a52e0ebd84bdc06275080db2d07aa657d692461fbf22d23890d3a0394ebffae0c662a2def420d53ebcdb69c974ba6f
2 parents 67d2db1 + a29d294 commit 717728b

9 files changed

+879
-401
lines changed

src/llmq/quorums_chainlocks.cpp

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -77,17 +77,17 @@ void CChainLocksHandler::ProcessMessage(CNode* pfrom, const std::string& strComm
7777

7878
auto hash = ::SerializeHash(clsig);
7979

80-
{
81-
LOCK(cs_main);
82-
connman.RemoveAskFor(hash, MSG_CLSIG);
83-
}
84-
8580
ProcessNewChainLock(pfrom->GetId(), clsig, hash);
8681
}
8782
}
8883

8984
void CChainLocksHandler::ProcessNewChainLock(NodeId from, const llmq::CChainLockSig& clsig, const uint256& hash)
9085
{
86+
{
87+
LOCK(cs_main);
88+
g_connman->RemoveAskFor(hash, MSG_CLSIG);
89+
}
90+
9191
{
9292
LOCK(cs);
9393
if (!seenChainLocks.emplace(hash, GetTimeMillis()).second) {
@@ -190,6 +190,8 @@ void CChainLocksHandler::UpdatedBlockTip(const CBlockIndex* pindexNew, const CBl
190190
return;
191191
}
192192

193+
Cleanup();
194+
193195
// DIP8 defines a process called "Signing attempts" which should run before the CLSIG is finalized
194196
// To simplify the initial implementation, we skip this process and directly try to create a CLSIG
195197
// This will fail when multiple blocks compete, but we accept this for the initial implementation.
@@ -201,6 +203,12 @@ void CChainLocksHandler::UpdatedBlockTip(const CBlockIndex* pindexNew, const CBl
201203
{
202204
LOCK(cs);
203205

206+
if (bestChainLockBlockIndex == pindexNew) {
207+
// we first got the CLSIG, then the header, and then the block was connected.
208+
// In this case there is no need to continue here.
209+
return;
210+
}
211+
204212
if (InternalHasConflictingChainLock(pindexNew->nHeight, pindexNew->GetBlockHash())) {
205213
if (!inEnforceBestChainLock) {
206214
// we accepted this block when there was no lock yet, but now a conflicting lock appeared. Invalidate it.
@@ -226,8 +234,6 @@ void CChainLocksHandler::UpdatedBlockTip(const CBlockIndex* pindexNew, const CBl
226234
}
227235

228236
quorumSigningManager->AsyncSignIfMember(Params().GetConsensus().llmqChainLocks, requestId, msgHash);
229-
230-
Cleanup();
231237
}
232238

233239
// WARNING: cs_main and cs should not be held!

src/llmq/quorums_init.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ void StartLLMQSystem()
5454
quorumDKGSessionManager->StartThreads();
5555
}
5656
if (quorumSigSharesManager) {
57+
quorumSigSharesManager->RegisterAsRecoveredSigsListener();
5758
quorumSigSharesManager->StartWorkerThread();
5859
}
5960
if (chainLocksHandler) {
@@ -68,6 +69,7 @@ void StopLLMQSystem()
6869
}
6970
if (quorumSigSharesManager) {
7071
quorumSigSharesManager->StopWorkerThread();
72+
quorumSigSharesManager->UnregisterAsRecoveredSigsListener();
7173
}
7274
if (quorumDKGSessionManager) {
7375
quorumDKGSessionManager->StopThreads();

src/llmq/quorums_signing.cpp

Lines changed: 58 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#include <algorithm>
1919
#include <limits>
20+
#include <unordered_set>
2021

2122
namespace llmq
2223
{
@@ -79,8 +80,23 @@ bool CRecoveredSigsDb::HasRecoveredSigForSession(const uint256& signHash)
7980

8081
bool 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

86102
bool 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

356375
void 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

420438
bool 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
}

src/llmq/quorums_signing.h

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,11 @@
1010

1111
#include "chainparams.h"
1212
#include "net.h"
13+
#include "saltedhasher.h"
1314
#include "sync.h"
1415

1516
#include <unordered_map>
1617

17-
namespace std {
18-
template <>
19-
struct hash<std::pair<Consensus::LLMQType, uint256>>
20-
{
21-
std::size_t operator()(const std::pair<Consensus::LLMQType, uint256>& k) const
22-
{
23-
return (std::size_t)((k.first + 1) * k.second.GetCheapHash());
24-
}
25-
};
26-
}
27-
2818
namespace llmq
2919
{
3020

@@ -77,7 +67,6 @@ class CRecoveredSig
7767
}
7868
};
7969

80-
// TODO implement caching to speed things up
8170
class CRecoveredSigsDb
8271
{
8372
static const size_t MAX_CACHE_SIZE = 30000;
@@ -87,8 +76,9 @@ class CRecoveredSigsDb
8776
CDBWrapper db;
8877

8978
RecursiveMutex cs;
90-
std::unordered_map<std::pair<Consensus::LLMQType, uint256>, std::pair<bool, int64_t>> hasSigForIdCache;
91-
std::unordered_map<uint256, std::pair<bool, int64_t>> hasSigForSessionCache;
79+
std::unordered_map<std::pair<Consensus::LLMQType, uint256>, std::pair<bool, int64_t>, StaticSaltedHasher> hasSigForIdCache;
80+
std::unordered_map<uint256, std::pair<bool, int64_t>, StaticSaltedHasher> hasSigForSessionCache;
81+
std::unordered_map<uint256, std::pair<bool, int64_t>, StaticSaltedHasher> hasSigForHashCache;
9282

9383
public:
9484
CRecoveredSigsDb(bool fMemory);
@@ -136,7 +126,7 @@ class CSigningManager
136126
CRecoveredSigsDb db;
137127

138128
// Incoming and not verified yet
139-
std::map<NodeId, std::list<CRecoveredSig>> pendingRecoveredSigs;
129+
std::unordered_map<NodeId, std::list<CRecoveredSig>> pendingRecoveredSigs;
140130

141131
// must be protected by cs
142132
FastRandomContext rnd;
@@ -157,7 +147,9 @@ class CSigningManager
157147
void ProcessMessageRecoveredSig(CNode* pfrom, const CRecoveredSig& recoveredSig, CConnman& connman);
158148
bool PreVerifyRecoveredSig(NodeId nodeId, const CRecoveredSig& recoveredSig, bool& retBan);
159149

160-
void CollectPendingRecoveredSigsToVerify(size_t maxUniqueSessions, std::map<NodeId, std::list<CRecoveredSig>>& retSigShares, std::map<std::pair<Consensus::LLMQType, uint256>, CQuorumCPtr>& retQuorums);
150+
void CollectPendingRecoveredSigsToVerify(size_t maxUniqueSessions,
151+
std::unordered_map<NodeId, std::list<CRecoveredSig>>& retSigShares,
152+
std::unordered_map<std::pair<Consensus::LLMQType, uint256>, CQuorumCPtr, StaticSaltedHasher>& retQuorums);
161153
bool ProcessPendingRecoveredSigs(CConnman& connman); // called from the worker thread of CSigSharesManager
162154
void ProcessRecoveredSig(NodeId nodeId, const CRecoveredSig& recoveredSig, const CQuorumCPtr& quorum, CConnman& connman);
163155
void Cleanup(); // called from the worker thread of CSigSharesManager

0 commit comments

Comments
 (0)