@@ -101,8 +101,8 @@ func StepToType(step int8) cometproto.SignedMsgType {
101101// ConsensusLock represents a Tendermint consensus lock on a specific value
102102type ConsensusLock struct {
103103 Height int64 `json:"height"`
104- Round int64 `json:"round"`
105- Value []byte `json:"value,omitempty"` // nil for nil locked, non-nil for block locked
104+ Round int64 `json:"round"` // The round where we locked on this value (lockedRound)
105+ Value []byte `json:"value,omitempty"` // The value we're locked on (lockedValue)
106106}
107107
108108// IsLocked returns true if there is an active consensus lock
@@ -255,7 +255,7 @@ func (signState *SignState) blockDoubleSign(ssc SignStateConsensus) (*SignState,
255255 signState .VoteExtensionSignature = ssc .VoteExtensionSignature
256256
257257 // Handle consensus lock updates according to Tendermint rules
258- signState .ConsensusLock = updateConsensusLock (signState .ConsensusLock , ssc .HRSKey (), ssc .SignBytes )
258+ signState .ConsensusLock = nextConsensusLock (signState .ConsensusLock , ssc .HRSKey (), ssc .SignBytes )
259259
260260 return signState .lockedCopy (), nil
261261}
@@ -669,13 +669,13 @@ func (signState *SignState) ValidateConsensusLock(hrs HRSKey, signBytes []byte)
669669 return nil
670670 }
671671
672- // For PROPOSAL and PREVOTE messages in rounds R' >= R, only allow signing for the locked value V
672+ // For PROPOSAL and PREVOTE messages in rounds R' >= locked_round, only allow signing for the locked value V
673+ // We use the stored round from the consensus lock for validation
673674 if (hrs .Step == stepPropose || hrs .Step == stepPrevote ) && hrs .Round >= signState .ConsensusLock .Round {
674675 // Extract the block hash from the sign bytes to compare with the locked value
675- blockHash := extractBlockHashFromSignBytes (signBytes , hrs .Step )
676- if blockHash == nil {
677- // If we can't extract the block hash, allow signing (fallback to existing behavior)
678- return nil
676+ blockHash , err := extractBlockHashFromSignBytes (signBytes , hrs .Step )
677+ if err != nil {
678+ return fmt .Errorf ("failed to extract block hash from sign bytes: %w" , err )
679679 }
680680
681681 // Check if we're trying to sign a different value than what we're locked on
@@ -710,44 +710,44 @@ func (signState *SignState) ClearConsensusLock(hrs HRSKey) {
710710}
711711
712712// extractBlockHashFromSignBytes extracts the block hash from Tendermint sign bytes
713- func extractBlockHashFromSignBytes (signBytes []byte , step int8 ) []byte {
713+ func extractBlockHashFromSignBytes (signBytes []byte , step int8 ) ( []byte , error ) {
714714 if len (signBytes ) == 0 {
715- return nil
715+ return nil , fmt . Errorf ( "empty sign bytes" )
716716 }
717717
718718 switch step {
719719 case stepPropose :
720720 // For PROPOSAL, extract block hash from CanonicalProposal
721721 var proposal cometproto.CanonicalProposal
722722 if err := protoio .UnmarshalDelimited (signBytes , & proposal ); err != nil {
723- return nil
723+ return nil , fmt . Errorf ( "failed to unmarshal proposal: %w" , err )
724724 }
725725 blockID := proposal .GetBlockID ()
726726 if blockID == nil {
727- return nil
727+ return nil , fmt . Errorf ( "proposal has no block ID" )
728728 }
729- return blockID .GetHash ()
729+ return blockID .GetHash (), nil
730730
731731 case stepPrevote , stepPrecommit :
732732 // For PREVOTE/PRECOMMIT, extract block hash from CanonicalVote
733733 var vote cometproto.CanonicalVote
734734 if err := protoio .UnmarshalDelimited (signBytes , & vote ); err != nil {
735- return nil
735+ return nil , fmt . Errorf ( "failed to unmarshal vote: %w" , err )
736736 }
737737 blockID := vote .GetBlockID ()
738738 if blockID == nil {
739- return nil
739+ return nil , fmt . Errorf ( "vote has no block ID" )
740740 }
741- return blockID .GetHash ()
741+ return blockID .GetHash (), nil
742742
743743 default :
744- return nil
744+ return nil , fmt . Errorf ( "unknown step: %d" , step )
745745 }
746746}
747747
748- // updateConsensusLock updates the consensus lock based on Tendermint rules
748+ // nextConsensusLock updates the consensus lock based on Tendermint rules
749749// This is a helper function that can be used by both SignState and other components
750- func updateConsensusLock (existingLock ConsensusLock , hrs HRSKey , signBytes []byte ) ConsensusLock {
750+ func nextConsensusLock (existingLock ConsensusLock , hrs HRSKey , signBytes []byte ) ConsensusLock {
751751 // Only update lock for PRECOMMIT steps (step 3)
752752 if hrs .Step != stepPrecommit {
753753 // For non-PRECOMMIT steps, only clear lock if moving to different height
@@ -759,8 +759,9 @@ func updateConsensusLock(existingLock ConsensusLock, hrs HRSKey, signBytes []byt
759759 }
760760
761761 // Extract the block hash from the sign bytes
762- blockHash := extractBlockHashFromSignBytes (signBytes , hrs .Step )
763- if blockHash == nil {
762+ blockHash , err := extractBlockHashFromSignBytes (signBytes , hrs .Step )
763+ if err != nil {
764+ // If we can't extract the block hash, return existing lock unchanged
764765 return existingLock
765766 }
766767
@@ -770,17 +771,19 @@ func updateConsensusLock(existingLock ConsensusLock, hrs HRSKey, signBytes []byt
770771 existingLock .IsLocked () &&
771772 ! bytes .Equal (blockHash , existingLock .Value ) {
772773 // Release old lock and set new lock on V'
774+ // Round is where we locked on this value (lockedRound)
773775 return ConsensusLock {
774776 Height : hrs .Height ,
775- Round : hrs .Round ,
777+ Round : hrs .Round , // Round where we locked on this value
776778 Value : blockHash ,
777779 }
778780 }
779781 if ! existingLock .IsLocked () {
780782 // First lock for this height
783+ // Round is where we locked on this value (lockedRound)
781784 return ConsensusLock {
782785 Height : hrs .Height ,
783- Round : hrs .Round ,
786+ Round : hrs .Round , // Round where we locked on this value
784787 Value : blockHash ,
785788 }
786789 }
0 commit comments