fix(consensus): Proposal Timeout-Leader Election Deadlock Under Byzantine Equivocation Attack #137 - #165
Merged
Conversation
…er Byzantine equivocation attack (VeriNode-Labs#137) Implements the full fix for the Byzantine equivocation-induced leader election deadlock described in issue VeriNode-Labs#137. What was changed: - src/consensus/proposal/equivocation_detector.rs (new) Stateful EquivocationDetector that stores the first valid proposal per (height, proposer) key. On receiving a second conflicting proposal at the same height from the same proposer it constructs and returns an EquivocationProof with both proposals. Callers must broadcast the proof to all peers so every honest replica can immediately advance its view. - src/consensus/leader_election/timeout_leader.rs (new) TimeoutLeader with exponential backoff (4s base, doubling each view, capped at 120s). on_equivocation() immediately advances the current view and resets the timeout without waiting for the normal timeout to expire, breaking the equivocation-induced deadlock. - src/consensus/recovery/fallback_sync.rs (new) FallbackSyncEngine tracking consecutive deadlocked views. After DEADLOCK_VIEW_THRESHOLD (5) consecutive views without a committed block, run_fallback() runs one round of synchronous PBFT-style agreement: replicas exchange locked values, the one with the highest lock_view number (tie-broken by block_hash) is chosen as the fallback proposal and committed. Deadlock counter resets on every commit. - src/consensus/engine/consensus_engine.rs (new) ConsensusEngine wiring all three components. on_proposal() feeds incoming proposals to the equivocation detector and triggers immediate view advance on equivocation. on_view_timeout() increments the deadlock counter and fires the synchronous fallback at threshold=5. - src/consensus/mod.rs (updated) Exports the four new submodules: proposal, leader_election, recovery, engine. - tests/consensus/byzantine_equivocation_recovery_test.rs (new) Chaos integration test suite: Byzantine primary sends 2 equivocating proposals → equivocation detected → immediate view advance → 5 deadlocked timeouts → fallback fires → recovery within 6 views. All invariants verified.
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.
Summary
Closes #137
Implements the complete fix for the Byzantine equivocation-induced leader election deadlock described in issue #137. A Byzantine primary sending two conflicting proposals at the same height causes honest replicas to lock on divergent blocks, preventing quorum and deadlocking timeout-based leader rotation indefinitely.
What was changed
New: src/consensus/proposal/equivocation_detector.rs
Stateful EquivocationDetector that stores the first valid proposal per (height, proposer) key. On receiving a second conflicting proposal at the same height from the same proposer, it constructs an EquivocationProof containing both proposals. Callers broadcast the proof to all peers. Invariants: zero-signature proposals are rejected; exact duplicates are deduplicated idempotently.
New: src/consensus/leader_election/timeout_leader.rs
TimeoutLeader with exponential backoff (4s base, doubling per view, capped at 120s: 4s to 8s to 16s to 120s). on_equivocation() immediately advances the current view and resets the timeout without waiting for the normal timer, breaking the equivocation deadlock. Leader rotation is round-robin over the ordered validator set.
New: src/consensus/recovery/fallback_sync.rs
FallbackSyncEngine counting consecutive views without a committed block. After DEADLOCK_VIEW_THRESHOLD = 5 consecutive deadlocked views, run_fallback() runs one round of synchronous PBFT-style agreement: replicas exchange their LockedValue { block_hash, lock_view }; the value with the highest lock_view wins (tie-broken lexicographically by block_hash). Deadlock counter resets on every successful commit.
New: src/consensus/engine/consensus_engine.rs
ConsensusEngine wiring all three components into the main consensus loop. on_proposal() feeds proposals to the equivocation detector and triggers immediate view advance on equivocation. on_commit() resets the deadlock counter. on_view_timeout() advances the view and increments the deadlock counter; at threshold=5 triggers synchronous fallback consensus.
Updated: src/consensus/mod.rs
Exports the four new submodules: proposal, leader_election, recovery, engine.
New: tests/consensus/byzantine_equivocation_recovery_test.rs
Chaos integration test suite (registered in Cargo.toml). Key scenarios: Byzantine primary sends 2 equivocating proposals, equivocation proof generated, view advances immediately; full end-to-end equivocation + 5 deadlocked timeouts + fallback fires + committed within 6 views; fallback selects highest-lock-view value; duplicate proposals do not trigger equivocation; engine resumes normal operation after fallback recovery.
Testing / Validation
Technical Invariants Satisfied