fix: bound DKG pending message queue across NodeId reconnects - #7524
Draft
PastaPastaPasta wants to merge 1 commit into
Draft
fix: bound DKG pending message queue across NodeId reconnects#7524PastaPastaPasta wants to merge 1 commit into
PastaPastaPasta wants to merge 1 commit into
Conversation
PastaPastaPasta
force-pushed
the
sec/v017
branch
from
August 3, 2026 01:05
98712cf to
ee9c5e9
Compare
CDKGPendingMessages bounded retention only per NodeId, an ephemeral per-connection counter, and never erased the entry. A peer that disconnects and reconnects therefore got a fresh quota while its previously queued payloads and seen-hashes stayed resident, so the queue grew linearly in the number of connections with no global ceiling. For llmq_400_85 that is roughly 56 MB per connection cycle, repeatable indefinitely. Victims are not limited to masternodes: in observer mode the handler still enqueues, never pops and never clears, so retention there is permanent. Add a global cap across all NodeIds and release a disconnecting peer's payloads and seen-hashes via a finalize hook. Filed as a draft: the cap is still message-count-based rather than byte-based, and EvictGreediestNode runs before the duplicate check, so a malicious masternode can replay seen hashes to evict honest peers' queued messages without occupying a slot itself. Both are unresolved.
PastaPastaPasta
force-pushed
the
sec/v017
branch
from
August 3, 2026 15:01
ee9c5e9 to
b988249
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Issue being fixed or feature implemented
CDKGPendingMessagesbounds retention only withmessagesPerNode[from] >= maxMessagesPerNode. The key isNodeId, an ephemeral per-connection counter, and nothing ever erases the entry: there is noRemoveNodeand no finalize hook, andClear()runs only from the DKG round handler in active masternode mode.A peer that disconnects and reconnects therefore gets a fresh
NodeIdand a fresh full quota, while its previously queued payloads andseenMessageshashes stay resident.pendingMessagesandseenMessagesgrow linearly in the number of connections with no global ceiling.For
llmq_400_85the per-connection quota is 800 QCONTRIB messages at up to ~70 KB each, roughly 56 MB per connection cycle, repeatable indefinitely.The sender must have completed MNAuth with the operator key of a masternode in the current DMN list; quorum membership is not required. Importantly, victims are not limited to masternodes: in observer mode the handler still enqueues, never pops and never calls
Clear(), so retention on ordinary full nodes is permanent.What was done?
NodeIds, not just a per-node quota.RemoveNode, wired to a new finalize hook, so a disconnecting peer's queued payloads and seen-hashes are released.Known limitations - this is filed as a draft
Two issues are known and not yet resolved. Filing as a draft so the analysis is on the record and reviewers can weigh in on the direction before the work is finished.
The ceiling is too high and measured in the wrong unit.
maxMessagesPerNode * 8is 6400 QCONTRIB messages at ~70 KB for a single queue of a single handler, roughly 450 MB; summed over LLMQ types and rotation indexes the "bounded" state is still on the order of a gigabyte. The bound should be byte-based rather than message-count-based.The eviction policy can be abused.
EvictGreediestNode()runs before theseenMessagesduplicate check, and a duplicate returns without incrementing the per-node count. A malicious masternode can therefore replay already-seen hashes to evict honest peers' queued DKG messages while never occupying a slot itself, and refresh its own quota by reconnecting. That inverts the intent of the policy and is DKG griefing.Additionally,
seenMessagesentries for already-popped messages are still released only byClear(), which runs once per round in active mode only.How Has This Been Tested?
The first commit adds a regression test showing the pending queue grows across
NodeIdreconnects, ordered before the fix.Full build and test validation is delegated to CI on this PR; the changes were not built locally.
Breaking Changes
None.
Checklist: