-
Notifications
You must be signed in to change notification settings - Fork 1.2k
fix: limit signing share sessions per peer #7351
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
e5e8d1e
135f8ce
6595948
f6aa12a
03dddee
76a6fd7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,10 +22,21 @@ | |
|
|
||
| #include <cxxtimer.hpp> | ||
|
|
||
| #include <algorithm> | ||
| #include <ranges> | ||
|
|
||
| namespace llmq | ||
| { | ||
| namespace { | ||
| constexpr size_t MAX_SESSIONS_PER_PEER_FACTOR{4}; | ||
| constexpr size_t MIN_SESSIONS_PER_PEER{100}; | ||
|
|
||
| size_t GetMaxSessionsForPeer(const Consensus::LLMQParams& params) | ||
| { | ||
| return std::max<size_t>(size_t(params.size) * MAX_SESSIONS_PER_PEER_FACTOR, MIN_SESSIONS_PER_PEER); | ||
| } | ||
| } // namespace | ||
|
|
||
| void CSigShare::UpdateKey() | ||
| { | ||
| key.first = this->buildSignHash().Get(); | ||
|
|
@@ -130,9 +141,32 @@ CSigSharesNodeState::Session& CSigSharesNodeState::GetOrCreateSessionFromAnn(con | |
| if (s.announced.inv.empty()) { | ||
| InitSession(s, signHash, ann); | ||
| } | ||
| s.receivedAnnouncement = true; | ||
| return s; | ||
| } | ||
|
|
||
| bool CSigSharesNodeState::CanCreateSessionFromAnn(const llmq::CSigSesAnn& ann, size_t maxSessions) const | ||
| { | ||
| return sessions.count(ann.buildSignHash().Get()) != 0 || GetAnnouncementSessionCount(ann.getLlmqType()) < maxSessions; | ||
| } | ||
|
|
||
| size_t CSigSharesNodeState::GetSessionCount() const | ||
| { | ||
| return sessions.size(); | ||
| } | ||
|
|
||
| size_t CSigSharesNodeState::GetSessionCount(Consensus::LLMQType llmqType) const | ||
| { | ||
| return std::ranges::count_if(sessions, [&](const auto& kv) { return kv.second.llmqType == llmqType; }); | ||
| } | ||
|
|
||
| size_t CSigSharesNodeState::GetAnnouncementSessionCount(Consensus::LLMQType llmqType) const | ||
| { | ||
| return std::ranges::count_if(sessions, [&](const auto& kv) { | ||
| return kv.second.receivedAnnouncement && kv.second.llmqType == llmqType; | ||
| }); | ||
| } | ||
|
Comment on lines
+148
to
+168
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💬 Nitpick: Linear scan per announcement, recomputed on rejection
source: ['claude'] |
||
|
|
||
| CSigSharesNodeState::Session* CSigSharesNodeState::GetSessionBySignHash(const uint256& signHash) | ||
| { | ||
| auto it = sessions.find(signHash); | ||
|
|
@@ -206,7 +240,8 @@ void CSigSharesManager::UnregisterRecoveryInterface() | |
| bool CSigSharesManager::ProcessMessageSigSesAnn(const CNode& pfrom, const CSigSesAnn& ann) | ||
| { | ||
| auto llmqType = ann.getLlmqType(); | ||
| if (!Params().GetLLMQ(llmqType).has_value()) { | ||
| const auto& llmq_params_opt = Params().GetLLMQ(llmqType); | ||
| if (!llmq_params_opt.has_value()) { | ||
| return false; | ||
| } | ||
| if (ann.getSessionId() == UNINITIALIZED_SESSION_ID || ann.getQuorumHash().IsNull() || ann.getId().IsNull() || ann.getMsgHash().IsNull()) { | ||
|
|
@@ -225,7 +260,15 @@ bool CSigSharesManager::ProcessMessageSigSesAnn(const CNode& pfrom, const CSigSe | |
|
|
||
| LOCK(cs); | ||
| auto& nodeState = nodeStates[pfrom.GetId()]; | ||
| const size_t maxSessions = GetMaxSessionsForPeer(*llmq_params_opt); | ||
| if (!nodeState.CanCreateSessionFromAnn(ann, maxSessions)) { | ||
| LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- too many sessions. cnt=%d, max=%d, llmqType=%d, node=%d\n", | ||
| __func__, nodeState.GetAnnouncementSessionCount(llmqType), maxSessions, static_cast<int>(llmqType), pfrom.GetId()); | ||
| return true; | ||
| } | ||
|
Comment on lines
260
to
+268
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💬 Nitpick: Quorum lookup precedes the cap check
source: ['claude'] |
||
| const uint256 signHash = ann.buildSignHash().Get(); | ||
| auto& session = nodeState.GetOrCreateSessionFromAnn(ann); | ||
| timeSeenForSessions.try_emplace(signHash, GetTime<std::chrono::seconds>().count()); | ||
| nodeState.sessionByRecvId.erase(session.recvSessionId); | ||
| nodeState.sessionByRecvId.erase(ann.getSessionId()); | ||
| session.recvSessionId = ann.getSessionId(); | ||
|
|
@@ -1247,6 +1290,11 @@ void CSigSharesManager::Cleanup() | |
| doneSessions.emplace(sigShare.GetSignHash()); | ||
| } | ||
| }); | ||
| for (const auto& [signHash, _] : timeSeenForSessions) { | ||
| if (doneSessions.count(signHash) == 0 && sigman.HasRecoveredSigForSession(signHash)) { | ||
| doneSessions.emplace(signHash); | ||
| } | ||
| } | ||
| for (const auto& signHash : doneSessions) { | ||
| RemoveSigSharesForSession(signHash); | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💬 Nitpick: Document the cap factor and floor
MAX_SESSIONS_PER_PEER_FACTOR{4}andMIN_SESSIONS_PER_PEER{100}lack any rationale. With LLMQ_400_* (size=400) the per-peer cap is 1600 announcement-derived sessions; each Session carries three quorum-sized CSigSharesInv bitsets plus several uint256s and a CQuorumCPtr. A short comment tying these constants to the threat model in the PR description would help future maintainers reason about adjustments and explain why the 100 floor exists even though no current LLMQ size requires it.source: ['claude']