Skip to content

Commit e5e8d1e

Browse files
committed
fix: limit signing share sessions per peer
1 parent 317917a commit e5e8d1e

4 files changed

Lines changed: 86 additions & 2 deletions

File tree

src/Makefile.test.include

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ BITCOIN_TESTS =\
136136
test/llmq_commitment_tests.cpp \
137137
test/llmq_hash_tests.cpp \
138138
test/llmq_params_tests.cpp \
139+
test/llmq_signing_shares_tests.cpp \
139140
test/llmq_snapshot_tests.cpp \
140141
test/llmq_utils_tests.cpp \
141142
test/logging_tests.cpp \

src/llmq/signing_shares.cpp

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,21 @@
2222

2323
#include <cxxtimer.hpp>
2424

25+
#include <algorithm>
2526
#include <ranges>
2627

2728
namespace llmq
2829
{
30+
namespace {
31+
constexpr size_t MAX_SESSIONS_PER_PEER_FACTOR{4};
32+
constexpr size_t MIN_SESSIONS_PER_PEER{100};
33+
34+
size_t GetMaxSessionsForPeer(const Consensus::LLMQParams& params)
35+
{
36+
return std::max<size_t>(size_t(params.size) * MAX_SESSIONS_PER_PEER_FACTOR, MIN_SESSIONS_PER_PEER);
37+
}
38+
} // namespace
39+
2940
void CSigShare::UpdateKey()
3041
{
3142
key.first = this->buildSignHash().Get();
@@ -133,6 +144,16 @@ CSigSharesNodeState::Session& CSigSharesNodeState::GetOrCreateSessionFromAnn(con
133144
return s;
134145
}
135146

147+
bool CSigSharesNodeState::CanCreateSessionFromAnn(const llmq::CSigSesAnn& ann, size_t maxSessions) const
148+
{
149+
return sessions.count(ann.buildSignHash().Get()) != 0 || sessions.size() < maxSessions;
150+
}
151+
152+
size_t CSigSharesNodeState::GetSessionCount() const
153+
{
154+
return sessions.size();
155+
}
156+
136157
CSigSharesNodeState::Session* CSigSharesNodeState::GetSessionBySignHash(const uint256& signHash)
137158
{
138159
auto it = sessions.find(signHash);
@@ -206,7 +227,8 @@ void CSigSharesManager::UnregisterRecoveryInterface()
206227
bool CSigSharesManager::ProcessMessageSigSesAnn(const CNode& pfrom, const CSigSesAnn& ann)
207228
{
208229
auto llmqType = ann.getLlmqType();
209-
if (!Params().GetLLMQ(llmqType).has_value()) {
230+
const auto& llmq_params_opt = Params().GetLLMQ(llmqType);
231+
if (!llmq_params_opt.has_value()) {
210232
return false;
211233
}
212234
if (ann.getSessionId() == UNINITIALIZED_SESSION_ID || ann.getQuorumHash().IsNull() || ann.getId().IsNull() || ann.getMsgHash().IsNull()) {
@@ -225,7 +247,14 @@ bool CSigSharesManager::ProcessMessageSigSesAnn(const CNode& pfrom, const CSigSe
225247

226248
LOCK(cs);
227249
auto& nodeState = nodeStates[pfrom.GetId()];
250+
const size_t maxSessions = GetMaxSessionsForPeer(*llmq_params_opt);
251+
if (!nodeState.CanCreateSessionFromAnn(ann, maxSessions)) {
252+
LogPrint(BCLog::LLMQ_SIGS, "CSigSharesManager::%s -- too many sessions. cnt=%d, max=%d, node=%d\n",
253+
__func__, nodeState.GetSessionCount(), maxSessions, pfrom.GetId());
254+
return false;
255+
}
228256
auto& session = nodeState.GetOrCreateSessionFromAnn(ann);
257+
timeSeenForSessions.try_emplace(ann.buildSignHash().Get(), GetTime<std::chrono::seconds>().count());
229258
nodeState.sessionByRecvId.erase(session.recvSessionId);
230259
nodeState.sessionByRecvId.erase(ann.getSessionId());
231260
session.recvSessionId = ann.getSessionId();
@@ -1247,6 +1276,11 @@ void CSigSharesManager::Cleanup()
12471276
doneSessions.emplace(sigShare.GetSignHash());
12481277
}
12491278
});
1279+
for (const auto& [signHash, _] : timeSeenForSessions) {
1280+
if (doneSessions.count(signHash) == 0 && sigman.HasRecoveredSigForSession(signHash)) {
1281+
doneSessions.emplace(signHash);
1282+
}
1283+
}
12501284
for (const auto& signHash : doneSessions) {
12511285
RemoveSigSharesForSession(signHash);
12521286
}

src/llmq/signing_shares.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,7 +327,6 @@ class CSigSharesNodeState
327327
CSigSharesInv requested;
328328
CSigSharesInv knows;
329329
};
330-
// TODO limit number of sessions per node
331330
Uint256HashMap<Session> sessions;
332331

333332
std::unordered_map<uint32_t, Session*> sessionByRecvId;
@@ -339,6 +338,8 @@ class CSigSharesNodeState
339338

340339
Session& GetOrCreateSessionFromShare(const CSigShare& sigShare);
341340
Session& GetOrCreateSessionFromAnn(const CSigSesAnn& ann);
341+
[[nodiscard]] bool CanCreateSessionFromAnn(const CSigSesAnn& ann, size_t maxSessions) const;
342+
[[nodiscard]] size_t GetSessionCount() const;
342343
Session* GetSessionBySignHash(const uint256& signHash);
343344
Session* GetSessionByRecvId(uint32_t sessionId);
344345
bool GetSessionInfoByRecvId(uint32_t sessionId, SessionInfo& retInfo);
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Copyright (c) 2026 The Dash Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#include <test/util/llmq_tests.h>
6+
#include <test/util/setup_common.h>
7+
8+
#include <consensus/params.h>
9+
#include <llmq/signing_shares.h>
10+
11+
#include <boost/test/unit_test.hpp>
12+
13+
using namespace llmq;
14+
using namespace llmq::testutils;
15+
16+
BOOST_FIXTURE_TEST_SUITE(llmq_signing_shares_tests, BasicTestingSetup)
17+
18+
static CSigSesAnn MakeAnn(uint32_t session_id, uint32_t nonce)
19+
{
20+
return CSigSesAnn{session_id, Consensus::LLMQType::LLMQ_50_60, GetTestQuorumHash(1), GetTestQuorumHash(2), GetTestQuorumHash(nonce)};
21+
}
22+
23+
BOOST_AUTO_TEST_CASE(sig_ses_ann_respects_session_limit_but_allows_refresh)
24+
{
25+
CSigSharesNodeState node_state;
26+
27+
const CSigSesAnn ann1{MakeAnn(1, 1)};
28+
const CSigSesAnn ann2{MakeAnn(2, 2)};
29+
const CSigSesAnn ann3{MakeAnn(3, 3)};
30+
constexpr size_t max_sessions{2};
31+
32+
BOOST_CHECK(node_state.CanCreateSessionFromAnn(ann1, max_sessions));
33+
node_state.GetOrCreateSessionFromAnn(ann1);
34+
BOOST_CHECK_EQUAL(node_state.GetSessionCount(), 1U);
35+
36+
BOOST_CHECK(node_state.CanCreateSessionFromAnn(ann2, max_sessions));
37+
node_state.GetOrCreateSessionFromAnn(ann2);
38+
BOOST_CHECK_EQUAL(node_state.GetSessionCount(), max_sessions);
39+
40+
BOOST_CHECK(!node_state.CanCreateSessionFromAnn(ann3, max_sessions));
41+
42+
const CSigSesAnn ann1_refresh{4, Consensus::LLMQType::LLMQ_50_60, ann1.getQuorumHash(), ann1.getId(), ann1.getMsgHash()};
43+
BOOST_CHECK(node_state.CanCreateSessionFromAnn(ann1_refresh, max_sessions));
44+
node_state.GetOrCreateSessionFromAnn(ann1_refresh);
45+
BOOST_CHECK_EQUAL(node_state.GetSessionCount(), max_sessions);
46+
}
47+
48+
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)