Repository navigation
fix(consensus): stop a previous-height precommit marking the peer's current-height votes - #1601
Draft
Claudius-Maginificent wants to merge 2 commits into
Draft
Claudius-Maginificent wants to merge 2 commits into
Claudius-Maginificent wants to merge 2 commits into
Conversation
…urrent-height bit getVoteBitArray resolved (PRS.Height-1, LastCommitRound, Precommit) to PRS.Precommits, the bit array of the peer's current height, after #751 removed the dedicated LastPrecommits array. A late precommit of the height the peer had just left (reactor Vote handler -> SetHasVote) therefore marked the same validator index as already held for the current height, and that validator's current-height precommit was never offered to the peer. A VoteSetBits answer about the previous height's commit (ApplyVoteSetBitsMessage with ourVotes == nil) overwrote the whole current-height precommit bit array the same way. Previous-height votes are not gossiped individually (the commit is sent whole), so the lookup now returns nil for them: no tracking, no side effects. LastCommitRound bookkeeping in ApplyNewRoundStepMessage is unchanged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ommits branch ApplyVoteSetBitsMessage has two branches (ourVotes nil or not); the previous-height regression test now exercises both. ApplyNewRoundStepMessage kept an if/else with identical branches under a "Shift Precommits to LastPrecommits" comment from before #751 removed that array; collapsed to the single assignment it performs. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Contributor
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the
|
|
🕓 Review not started yet because this PR is a draft.
Commit 47b9439. Normal review starts when eligible; priority review starts as soon as a slot is available. |
This branch has not been deployed
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.
TL;DR: A node could silently stop sending some validators' votes to one of its peers for the rest of a round. This happened when a late message about the previous block arrived just after both nodes moved to the next block. It can slow a round down or, on poorly connected networks, stall it.
User story
As a validator operator, I want every peer to receive the final-stage votes it still needs, so that rounds finish quickly after network hiccups.
Scenario
Base flow
Each node remembers which votes each peer already has, so it does not send the same vote twice. When both nodes finish one block and move to the next, that memory starts fresh for the new block.
Actual behavior
A message about the block the nodes just finished sometimes arrives after both have moved on. The node then wrongly recorded the peer as already holding the matching votes for the new block. There are two ways in:
Those votes were never sent to that peer. If the peer cannot get them from anyone else, it waits, and the round can stall.
Expected behavior
Messages about the previous block never change what the node thinks a peer has for the current block. Current-block votes are sent as usual.
Detailed discussion
What was done
internal/consensus/peer_state.go,getVoteBitArray):PRS.Height == height+1 && PRS.LastCommitRound == round && Precommit, it returnedps.PRS.Precommits, which is the peer's current-height array.PRS.LastPrecommitsand left that branch pointing at the current array.reactor.goVoteChannel handler →SetHasVotefor a late height-1 precommit (sets one bit);reactor.goVoteSetBits handler →ApplyVoteSetBitsMessagefor an old height. This overwrote the whole array with the peer's previous commit bits, for bothourVotes == niland non-nil;gossiper.goupdatePeerVote→SetHasVotewhen our own send finishes after the peer's state has advanced.PickVoteToSendsubtracts those bits, so the affected current-height precommits are never offered to that peer. Vote redelivery (fix(consensus): resend same-round votes the peer never acknowledged #1599) cannot repair this, because a vote that was never sent is never tracked as unacknowledged.getVoteBitArrayreturns nil ("not tracked") for any height other thanPRS.Height.pickVoteForGossipworks only atrs.Height, andGossipCommitsends the wholeCommit, so there is nothing to track.ApplyNewRoundStepMessage, anif/elsewith identical branches and a stale "Shift Precommits to LastPrecommits" comment is collapsed into a single assignment. Behavior is unchanged.Testing
New tests in
internal/consensus/peer_state_lastcommit_test.go:TestSetHasVoteLastHeightPrecommitDoesNotMarkCurrentHeightPickVoteToSend.TestApplyVoteSetBitsLastHeightPrecommitsDoNotMarkCurrentHeight(ourVotesnil and non-nil)TestApplyNewRoundStepMessageKeepsLastCommitRoundLastCommitRoundcarry-over.The neighbouring PeerState, VoteSetBits, Maj23, gossiper and reactor tests pass, and
make lintreports 0 issues.An independent review traced every caller of
getVoteBitArrayand found none that needed the removed branch.Breaking changes
None.
Prior work
peer_state.go, so no conflict is expected. This fix also keeps fix(consensus): resend same-round votes the peer never acknowledged #1599's re-offer from resolving a stale height to the current array.🤖 Generated with Claude Code
https://claude.ai/code/session_01WunYYgcoPDNh3uebhUCwig
🤖 Co-authored by Claudius the Magnificent AI Agent