Skip to content

Commit c7b6eb8

Browse files
authored
Merge pull request #3389 from codablock/pr_concentrated_recovery
Implement "concentrated recovery" of LLMQ signatures
2 parents e518ce4 + 148bbdd commit c7b6eb8

File tree

10 files changed

+298
-16
lines changed

10 files changed

+298
-16
lines changed

src/chainparams.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ static Consensus::LLMQParams llmq_test = {
175175
.signingActiveQuorumCount = 2, // just a few ones to allow easier testing
176176

177177
.keepOldConnections = 3,
178+
.recoveryMembers = 3,
178179
};
179180

180181
// this one is for devnets only
@@ -194,6 +195,7 @@ static Consensus::LLMQParams llmq_devnet = {
194195
.signingActiveQuorumCount = 3, // just a few ones to allow easier testing
195196

196197
.keepOldConnections = 4,
198+
.recoveryMembers = 6,
197199
};
198200

199201
static Consensus::LLMQParams llmq50_60 = {
@@ -212,6 +214,7 @@ static Consensus::LLMQParams llmq50_60 = {
212214
.signingActiveQuorumCount = 24, // a full day worth of LLMQs
213215

214216
.keepOldConnections = 25,
217+
.recoveryMembers = 25,
215218
};
216219

217220
static Consensus::LLMQParams llmq400_60 = {
@@ -230,6 +233,7 @@ static Consensus::LLMQParams llmq400_60 = {
230233
.signingActiveQuorumCount = 4, // two days worth of LLMQs
231234

232235
.keepOldConnections = 5,
236+
.recoveryMembers = 100,
233237
};
234238

235239
// Used for deployment and min-proto-version signalling, so it needs a higher threshold
@@ -249,6 +253,7 @@ static Consensus::LLMQParams llmq400_85 = {
249253
.signingActiveQuorumCount = 4, // four days worth of LLMQs
250254

251255
.keepOldConnections = 5,
256+
.recoveryMembers = 100,
252257
};
253258

254259

src/consensus/params.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@ struct LLMQParams {
114114
// Used for inter-quorum communication. This is the number of quorums for which we should keep old connections. This
115115
// should be at least one more then the active quorums set.
116116
int keepOldConnections;
117+
118+
// How many members should we try to send all sigShares to before we give up.
119+
int recoveryMembers;
117120
};
118121

119122
/**

src/llmq/quorums_signing_shares.cpp

Lines changed: 168 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <init.h>
1212
#include <net_processing.h>
1313
#include <netmessagemaker.h>
14+
#include <spork.h>
1415
#include <validation.h>
1516

1617
#include <cxxtimer.hpp>
@@ -236,6 +237,23 @@ void CSigSharesManager::ProcessMessage(CNode* pfrom, const std::string& strComma
236237
return;
237238
}
238239

240+
if (sporkManager.IsSporkActive(SPORK_21_QUORUM_ALL_CONNECTED)) {
241+
if (strCommand == NetMsgType::QSIGSHARE) {
242+
std::vector<CSigShare> sigShares;
243+
vRecv >> sigShares;
244+
245+
if (sigShares.size() > MAX_MSGS_SIG_SHARES) {
246+
LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- too many sigs in QSIGSHARE message. cnt=%d, max=%d, node=%d\n", __func__, sigShares.size(), MAX_MSGS_SIG_SHARES, pfrom->GetId());
247+
BanNode(pfrom->GetId());
248+
return;
249+
}
250+
251+
for (auto& sigShare : sigShares) {
252+
ProcessMessageSigShare(pfrom->GetId(), sigShare, connman);
253+
}
254+
}
255+
}
256+
239257
if (strCommand == NetMsgType::QSIGSESANN) {
240258
std::vector<CSigSesAnn> msgs;
241259
vRecv >> msgs;
@@ -465,6 +483,57 @@ bool CSigSharesManager::ProcessMessageBatchedSigShares(CNode* pfrom, const CBatc
465483
return true;
466484
}
467485

486+
void CSigSharesManager::ProcessMessageSigShare(NodeId fromId, const CSigShare& sigShare, CConnman& connman)
487+
{
488+
auto quorum = quorumManager->GetQuorum(sigShare.llmqType, sigShare.quorumHash);
489+
if (!quorum) {
490+
return;
491+
}
492+
if (!CLLMQUtils::IsQuorumActive(sigShare.llmqType, quorum->qc.quorumHash)) {
493+
// quorum is too old
494+
return;
495+
}
496+
if (!quorum->IsMember(activeMasternodeInfo.proTxHash)) {
497+
// we're not a member so we can't verify it (we actually shouldn't have received it)
498+
return;
499+
}
500+
if (quorum->quorumVvec == nullptr) {
501+
// TODO we should allow to ask other nodes for the quorum vvec if we missed it in the DKG
502+
LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- we don't have the quorum vvec for %s, no verification possible. node=%d\n", __func__,
503+
quorum->qc.quorumHash.ToString(), fromId);
504+
return;
505+
}
506+
507+
if (sigShare.quorumMember >= quorum->members.size()) {
508+
LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- quorumMember out of bounds\n", __func__);
509+
BanNode(fromId);
510+
return;
511+
}
512+
if (!quorum->qc.validMembers[sigShare.quorumMember]) {
513+
LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- quorumMember not valid\n", __func__);
514+
BanNode(fromId);
515+
return;
516+
}
517+
518+
{
519+
LOCK(cs);
520+
521+
if (sigShares.Has(sigShare.GetKey())) {
522+
return;
523+
}
524+
525+
if (quorumSigningManager->HasRecoveredSigForId((Consensus::LLMQType)sigShare.llmqType, sigShare.id)) {
526+
return;
527+
}
528+
529+
auto& nodeState = nodeStates[fromId];
530+
nodeState.pendingIncomingSigShares.Add(sigShare.GetKey(), sigShare);
531+
}
532+
533+
LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- signHash=%s, id=%s, msgHash=%s, member=%d, node=%d\n", __func__,
534+
sigShare.GetSignHash().ToString(), sigShare.id.ToString(), sigShare.msgHash.ToString(), sigShare.quorumMember, fromId);
535+
}
536+
468537
bool CSigSharesManager::PreVerifyBatchedSigShares(NodeId nodeId, const CSigSharesNodeState::SessionInfo& session, const CBatchedSigShares& batchedSigShares, bool& retBan)
469538
{
470539
retBan = false;
@@ -668,8 +737,10 @@ void CSigSharesManager::ProcessSigShare(NodeId nodeId, const CSigShare& sigShare
668737

669738
// prepare node set for direct-push in case this is our sig share
670739
std::set<NodeId> quorumNodes;
671-
if (sigShare.quorumMember == quorum->GetMemberIndex(activeMasternodeInfo.proTxHash)) {
672-
quorumNodes = connman.GetMasternodeQuorumNodes((Consensus::LLMQType) sigShare.llmqType, sigShare.quorumHash);
740+
if (!sporkManager.IsSporkActive(SPORK_21_QUORUM_ALL_CONNECTED)) {
741+
if (sigShare.quorumMember == quorum->GetMemberIndex(activeMasternodeInfo.proTxHash)) {
742+
quorumNodes = connman.GetMasternodeQuorumNodes((Consensus::LLMQType) sigShare.llmqType, sigShare.quorumHash);
743+
}
673744
}
674745

675746
if (quorumSigningManager->HasRecoveredSigForId(llmqType, sigShare.id)) {
@@ -780,6 +851,21 @@ void CSigSharesManager::TryRecoverSig(const CQuorumCPtr& quorum, const uint256&
780851
quorumSigningManager->ProcessRecoveredSig(-1, rs, quorum, connman);
781852
}
782853

854+
CDeterministicMNCPtr CSigSharesManager::SelectMemberForRecovery(const CQuorumCPtr& quorum, const uint256 &id, int attempt)
855+
{
856+
assert(attempt < quorum->members.size());
857+
858+
std::vector<std::pair<uint256, CDeterministicMNCPtr>> v;
859+
v.reserve(quorum->members.size());
860+
for (const auto& dmn : quorum->members) {
861+
auto h = ::SerializeHash(std::make_pair(dmn->proTxHash, id));
862+
v.emplace_back(h, dmn);
863+
}
864+
std::sort(v.begin(), v.end());
865+
866+
return v[attempt].second;
867+
}
868+
783869
void CSigSharesManager::CollectSigSharesToRequest(std::unordered_map<NodeId, std::unordered_map<uint256, CSigSharesInv, StaticSaltedHasher>>& sigSharesToRequest)
784870
{
785871
AssertLockHeld(cs);
@@ -928,6 +1014,43 @@ void CSigSharesManager::CollectSigSharesToSend(std::unordered_map<NodeId, std::u
9281014
}
9291015
}
9301016

1017+
void CSigSharesManager::CollectSigSharesToSend(std::unordered_map<NodeId, std::vector<CSigShare>>& sigSharesToSend, const std::vector<CNode*>& vNodes)
1018+
{
1019+
AssertLockHeld(cs);
1020+
1021+
std::unordered_map<uint256, CNode*> proTxToNode;
1022+
for (const auto& pnode : vNodes) {
1023+
if (pnode->verifiedProRegTxHash.IsNull()) {
1024+
continue;
1025+
}
1026+
proTxToNode.emplace(pnode->verifiedProRegTxHash, pnode);
1027+
}
1028+
1029+
auto curTime = GetTime();
1030+
1031+
for (auto& p : signedSessions) {
1032+
if (p.second.attempt > p.second.quorum->params.recoveryMembers) {
1033+
continue;
1034+
}
1035+
1036+
if (curTime >= p.second.nextAttemptTime) {
1037+
p.second.nextAttemptTime = curTime + SEND_FOR_RECOVERY_TIMEOUT;
1038+
auto dmn = SelectMemberForRecovery(p.second.quorum, p.second.sigShare.id, p.second.attempt);
1039+
LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- sending to %s, signHash=%s\n", __func__,
1040+
dmn->proTxHash.ToString(), p.second.sigShare.GetSignHash().ToString());
1041+
p.second.attempt++;
1042+
1043+
auto it = proTxToNode.find(dmn->proTxHash);
1044+
if (it == proTxToNode.end()) {
1045+
continue;
1046+
}
1047+
1048+
auto& m = sigSharesToSend[it->second->GetId()];
1049+
m.emplace_back(p.second.sigShare);
1050+
}
1051+
}
1052+
}
1053+
9311054
void CSigSharesManager::CollectSigSharesToAnnounce(std::unordered_map<NodeId, std::unordered_map<uint256, CSigSharesInv, StaticSaltedHasher>>& sigSharesToAnnounce)
9321055
{
9331056
AssertLockHeld(cs);
@@ -983,7 +1106,8 @@ void CSigSharesManager::CollectSigSharesToAnnounce(std::unordered_map<NodeId, st
9831106
bool CSigSharesManager::SendMessages()
9841107
{
9851108
std::unordered_map<NodeId, std::unordered_map<uint256, CSigSharesInv, StaticSaltedHasher>> sigSharesToRequest;
986-
std::unordered_map<NodeId, std::unordered_map<uint256, CBatchedSigShares, StaticSaltedHasher>> sigSharesToSend;
1109+
std::unordered_map<NodeId, std::unordered_map<uint256, CBatchedSigShares, StaticSaltedHasher>> sigShareBatchesToSend;
1110+
std::unordered_map<NodeId, std::vector<CSigShare>> sigSharesToSend;
9871111
std::unordered_map<NodeId, std::unordered_map<uint256, CSigSharesInv, StaticSaltedHasher>> sigSharesToAnnounce;
9881112
std::unordered_map<NodeId, std::vector<CSigSesAnn>> sigSessionAnnouncements;
9891113

@@ -1006,18 +1130,24 @@ bool CSigSharesManager::SendMessages()
10061130
return session->sendSessionId;
10071131
};
10081132

1133+
std::vector<CNode*> vNodesCopy = g_connman->CopyNodeVector(CConnman::FullyConnectedOnly);
1134+
10091135
{
10101136
LOCK(cs);
1011-
CollectSigSharesToRequest(sigSharesToRequest);
1012-
CollectSigSharesToSend(sigSharesToSend);
1013-
CollectSigSharesToAnnounce(sigSharesToAnnounce);
1137+
if (!sporkManager.IsSporkActive(SPORK_21_QUORUM_ALL_CONNECTED)) {
1138+
CollectSigSharesToRequest(sigSharesToRequest);
1139+
CollectSigSharesToSend(sigShareBatchesToSend);
1140+
CollectSigSharesToAnnounce(sigSharesToAnnounce);
1141+
} else {
1142+
CollectSigSharesToSend(sigSharesToSend, vNodesCopy);
1143+
}
10141144

10151145
for (auto& p : sigSharesToRequest) {
10161146
for (auto& p2 : p.second) {
10171147
p2.second.sessionId = addSigSesAnnIfNeeded(p.first, p2.first);
10181148
}
10191149
}
1020-
for (auto& p : sigSharesToSend) {
1150+
for (auto& p : sigShareBatchesToSend) {
10211151
for (auto& p2 : p.second) {
10221152
p2.second.sessionId = addSigSesAnnIfNeeded(p.first, p2.first);
10231153
}
@@ -1031,8 +1161,6 @@ bool CSigSharesManager::SendMessages()
10311161

10321162
bool didSend = false;
10331163

1034-
std::vector<CNode*> vNodesCopy = g_connman->CopyNodeVector(CConnman::FullyConnectedOnly);
1035-
10361164
for (auto& pnode : vNodesCopy) {
10371165
CNetMsgMaker msgMaker(pnode->GetSendVersion());
10381166

@@ -1076,8 +1204,8 @@ bool CSigSharesManager::SendMessages()
10761204
}
10771205
}
10781206

1079-
auto jt = sigSharesToSend.find(pnode->GetId());
1080-
if (jt != sigSharesToSend.end()) {
1207+
auto jt = sigShareBatchesToSend.find(pnode->GetId());
1208+
if (jt != sigShareBatchesToSend.end()) {
10811209
size_t totalSigsCount = 0;
10821210
std::vector<CBatchedSigShares> msgs;
10831211
for (auto& p : jt->second) {
@@ -1119,6 +1247,25 @@ bool CSigSharesManager::SendMessages()
11191247
didSend = true;
11201248
}
11211249
}
1250+
1251+
auto lt = sigSharesToSend.find(pnode->GetId());
1252+
if (lt != sigSharesToSend.end()) {
1253+
std::vector<CSigShare> msgs;
1254+
for (auto& sigShare : lt->second) {
1255+
LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::SendMessages -- QSIGSHARE signHash=%s, node=%d\n",
1256+
sigShare.GetSignHash().ToString(), pnode->GetId());
1257+
msgs.emplace_back(std::move(sigShare));
1258+
if (msgs.size() == MAX_MSGS_SIG_SHARES) {
1259+
g_connman->PushMessage(pnode, msgMaker.Make(NetMsgType::QSIGSHARE, msgs), false);
1260+
msgs.clear();
1261+
didSend = true;
1262+
}
1263+
}
1264+
if (!msgs.empty()) {
1265+
g_connman->PushMessage(pnode, msgMaker.Make(NetMsgType::QSIGSHARE, msgs), false);
1266+
didSend = true;
1267+
}
1268+
}
11221269
}
11231270

11241271
// looped through all nodes, release them
@@ -1285,6 +1432,7 @@ void CSigSharesManager::RemoveSigSharesForSession(const uint256& signHash)
12851432
sigSharesRequested.EraseAllForSignHash(signHash);
12861433
sigSharesToAnnounce.EraseAllForSignHash(signHash);
12871434
sigShares.EraseAllForSignHash(signHash);
1435+
signedSessions.erase(signHash);
12881436
timeSeenForSessions.erase(signHash);
12891437
}
12901438

@@ -1431,6 +1579,15 @@ void CSigSharesManager::Sign(const CQuorumCPtr& quorum, const uint256& id, const
14311579
LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- signed sigShare. signHash=%s, id=%s, msgHash=%s, llmqType=%d, quorum=%s, time=%s\n", __func__,
14321580
signHash.ToString(), sigShare.id.ToString(), sigShare.msgHash.ToString(), quorum->params.type, quorum->qc.quorumHash.ToString(), t.count());
14331581
ProcessSigShare(-1, sigShare, *g_connman, quorum);
1582+
1583+
if (sporkManager.IsSporkActive(SPORK_21_QUORUM_ALL_CONNECTED)) {
1584+
LOCK(cs);
1585+
auto& session = signedSessions[sigShare.GetSignHash()];
1586+
session.sigShare = sigShare;
1587+
session.quorum = quorum;
1588+
session.nextAttemptTime = 0;
1589+
session.attempt = 0;
1590+
}
14341591
}
14351592

14361593
// causes all known sigShares to be re-announced

0 commit comments

Comments
 (0)