Skip to content

fix(net): clean recovered sig backlog after peer disconnect - #7483

Draft
thepastaclaw wants to merge 1 commit into
dashpay:developfrom
thepastaclaw:fix-recovered-sig-disconnect-cleanup
Draft

fix(net): clean recovered sig backlog after peer disconnect#7483
thepastaclaw wants to merge 1 commit into
dashpay:developfrom
thepastaclaw:fix-recovered-sig-disconnect-cleanup

Conversation

@thepastaclaw

Copy link
Copy Markdown

Issue being fixed or feature implemented

The recovered-signature cleanup added in #7402 checks PeerIsBanned(node_id) every five seconds. That query reads the peer's one-shot m_should_discourage flag, which is cleared during the next SendMessages pass before disconnection completes. A cleanup tick therefore usually misses the short-lived state, and once FinalizeNode() removes the peer, PeerIsBanned() returns false. Pending recovered signatures from the peer can consequently remain queued instead of being reclaimed after disconnection.

What was done?

  • Added PeerManagerInternal::PeerIsConnected() using the peer map as the authoritative connection-lifecycle state.
  • Changed the periodic pending recovered-signature sweep to remove entries whose originating peer is no longer connected.
  • Documented why disconnection is a durable cleanup predicate and why node-ID reuse cannot cause a later peer's queue to be removed.
  • Added focused tests for the one-shot discourage-state failure mode and for the PeerIsConnected() lifecycle across FinalizeNode().

The existing RemoveBannedNodeStates() path for signature shares is intentionally unchanged because it runs in the fast message-processing loop while the discourage flag is observable.

How Has This Been Tested?

  • ./src/test/test_dash --run_test=denialofservice_tests/peer_gone_recovered_sig_cleanup_predicate
  • ./src/test/test_dash --run_test=net_peer_connection_tests/peer_is_connected_tracks_finalize_node
  • ./src/test/test_dash --run_test='!validation_chainstate_tests' (762 test cases)
  • git diff --check 6d04c60ef36996f2d13662f806790de9fe732836..ea717326c77191cb1a1644a09a2cd39d60fed223
  • Exact-head independent code-review gate: 0 findings, recommendation ship

The predicate regression test was also verified to fail with the old PeerIsBanned() condition and pass with the new disconnected-peer condition.

Breaking Changes

None.

Checklist

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have made corresponding changes to the documentation
  • I have assigned this pull request to a milestone

…nned

NetSigning::WorkThreadSigning sweeps CSigningManager's pending (not-yet-
verified) recovered sigs every CLEANUP_INTERVAL{5s} so a flood's backlog does
not outlive the peer that sent it. That sweep was keyed on PeerIsBanned(),
which made it effectively a no-op.

PeerIsBanned() -> IsBanned() reads Peer::m_should_discourage, a one-shot flag
set by Misbehaving() and cleared by MaybeDiscourageAndDisconnect() on the very
next SendMessages pass (~100ms) just before the peer is disconnected. Once the
peer is finalized the Peer is erased from m_peer_map, so GetPeerRef() returns
nullptr and IsBanned() reports false again. The predicate could therefore only
match if a 5s tick happened to land inside a ~100ms window, so in practice the
backlog survived and the stated goal was never achieved.

Key the sweep on "no longer connected" instead. Disconnection is a durable,
terminal state -- node ids are never reused (CConnman::GetNewNodeId only
increments) -- so whenever the tick lands, it is observed. This also reclaims
the queues of peers that simply went away without misbehaving, and mirrors how
CSigSharesManager::Cleanup() already prunes its per-node state by disconnection.

A connected-state predicate was chosen over a FinalizeNode hook to keep the
change minimal: it preserves the existing throttled-sweep shape, adds no
cross-subsystem lifetime or ordering coupling, and is directly unit-testable.
The new PeerIsConnected() also needs a strictly smaller lock footprint than
PeerIsBanned() (m_peer_mutex only, no cs_main or m_misbehavior_mutex).

NetSigning::RemoveBannedNodeStates() is deliberately left on PeerIsBanned():
it runs from WorkThreadCleaning() on a 100ms loop that matches the flag's
lifetime, and its subsystem has its own disconnected-node sweep.

Locally reconstructed sigs are unaffected: they use NodeId -1 and live in
pendingReconstructedRecoveredSigs, a separate map keyed by sig hash that
RemoveNodesIf() does not touch.

Adds a regression test asserting the flag has already gone false by the time a
sweep could run, while the connection-keyed predicate stays true once the peer
is finalized, plus direct coverage of PeerIsConnected() across FinalizeNode().
@thepastaclaw

thepastaclaw commented Jul 25, 2026

Copy link
Copy Markdown
Author

✅ Final review complete — no blockers (commit ea71732)

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Final validation — Codex + Sonnet

The change correctly replaces the transient discouragement flag with durable peer-map membership when pruning pending recovered signatures. Peer lifecycle, locking, node-ID uniqueness, local reconstruction handling, and the focused regression tests all support the implementation; no in-scope correctness issues were found. GitHub does not permit self-approval, so the canonical APPROVE result is transported as a COMMENT review.

Review provenance

  • Codex reviewers: gpt-5.6-sol — general (completed), gpt-5.6-sol — dash-core-commit-history (completed)
  • Verifier: gpt-5.6-sol — final-verifier (fallback)
  • Sonnet reviewers: claude-sonnet-5 — general (failed), claude-sonnet-5 — dash-core-commit-history (failed), claude-sonnet-5 — general (completed), claude-sonnet-5 — dash-core-commit-history (completed)

@thepastaclaw thepastaclaw changed the title fix(llmq): clean recovered sig backlog after peer disconnect fix(net): clean recovered sig backlog after peer disconnect Jul 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant