Skip to content

Commit fea7ac6

Browse files
committed
Fix false-positive double-sign slashing on delayed proposals
View numbers are reused across blocks (StartRound sets view = blockNumber), so delayed proposals from previous rounds can arrive after _proposalsByView is cleared and collide with current-round entries. Guard double-sign detection with a block number check to prevent stale proposals from corrupting the detection state and incorrectly slashing validators.
1 parent e2bf00a commit fea7ac6

File tree

1 file changed

+20
-12
lines changed

1 file changed

+20
-12
lines changed

src/node/Basalt.Node/NodeCoordinator.cs

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -949,24 +949,32 @@ private void HandleSyncResponse(PeerId sender, SyncResponseMessage response)
949949

950950
private void HandleConsensusProposal(PeerId sender, ConsensusProposalMessage proposal)
951951
{
952-
// Double-sign detection: check if the SAME proposer already sent a DIFFERENT
953-
// block hash for this view. Keying by (view, proposer) ensures that different
954-
// proposers at the same view (e.g. after a view change) don't trigger false positives.
955-
var proposalKey = (proposal.ViewNumber, proposal.SenderId);
956-
if (_slashingEngine != null && _proposalsByView.TryGetValue(proposalKey, out var existingHash))
952+
// Double-sign detection: only for proposals matching the current block number.
953+
// View numbers are reused across blocks (StartRound sets view = blockNumber),
954+
// so delayed proposals from previous blocks can arrive after _proposalsByView
955+
// was cleared and collide with current-round entries, causing false positives.
956+
var currentBlock = _config.UsePipelining
957+
? _pipelinedConsensus!.LastFinalizedBlock + 1
958+
: _consensus!.CurrentBlockNumber;
959+
960+
if (proposal.BlockNumber == currentBlock)
957961
{
958-
if (existingHash != proposal.BlockHash)
962+
var proposalKey = (proposal.ViewNumber, proposal.SenderId);
963+
if (_slashingEngine != null && _proposalsByView.TryGetValue(proposalKey, out var existingHash))
959964
{
960-
var proposerInfo = _validatorSet?.GetByPeerId(proposal.SenderId);
961-
if (proposerInfo != null)
965+
if (existingHash != proposal.BlockHash)
962966
{
963-
_slashingEngine.SlashDoubleSign(proposerInfo.Address, proposal.BlockNumber, existingHash, proposal.BlockHash);
964-
_logger.LogWarning("Double-sign detected from validator {Address} at view {View}",
965-
proposerInfo.Address, proposal.ViewNumber);
967+
var proposerInfo = _validatorSet?.GetByPeerId(proposal.SenderId);
968+
if (proposerInfo != null)
969+
{
970+
_slashingEngine.SlashDoubleSign(proposerInfo.Address, proposal.BlockNumber, existingHash, proposal.BlockHash);
971+
_logger.LogWarning("Double-sign detected from validator {Address} at view {View}",
972+
proposerInfo.Address, proposal.ViewNumber);
973+
}
966974
}
967975
}
976+
_proposalsByView[proposalKey] = proposal.BlockHash;
968977
}
969-
_proposalsByView[proposalKey] = proposal.BlockHash;
970978

971979
ConsensusVoteMessage? vote;
972980
if (_config.UsePipelining)

0 commit comments

Comments
 (0)