Skip to content

Commit

Permalink
Implement AddCommit in Consensus state
Browse files Browse the repository at this point in the history
  • Loading branch information
sergio-mena committed Jan 11, 2025
1 parent 2670544 commit b48a625
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 26 deletions.
4 changes: 4 additions & 0 deletions internal/consensus/reactor.go
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,7 @@ func (conR *Reactor) Receive(e p2p.Envelope) {
case types.PrevoteType:
ourVotes = votes.Prevotes(msg.Round).BitArrayByBlockID(msg.BlockID)
case types.PrecommitType:
// No need to check Votes.Commit, we are dealing with VoteSetMaj23Message.
ourVotes = votes.Precommits(msg.Round).BitArrayByBlockID(msg.BlockID)
default:
panic("Bad VoteSetBitsMessage field Type. Forgot to add a check in ValidateBasic?")
Expand Down Expand Up @@ -387,6 +388,7 @@ func (conR *Reactor) Receive(e p2p.Envelope) {
case types.PrevoteType:
ourVotes = votes.Prevotes(msg.Round).BitArrayByBlockID(msg.BlockID)
case types.PrecommitType:
// No need to check Votes.Commit, we are dealing with VoteSetBitsMessage.
ourVotes = votes.Precommits(msg.Round).BitArrayByBlockID(msg.BlockID)
default:
panic("Bad VoteSetBitsMessage field Type. Forgot to add a check in ValidateBasic?")
Expand Down Expand Up @@ -757,6 +759,7 @@ OUTER_LOOP:
rs := conR.getRoundState()
prs := ps.GetRoundState()
if rs.Height == prs.Height {
// No need to check rs.Votes.Commit, as this is for an ongoing consensus.
if maj23, ok := rs.Votes.Precommits(prs.Round).TwoThirdsMajority(); ok {
peer.TrySend(p2p.Envelope{
ChannelID: StateChannel,
Expand Down Expand Up @@ -1001,6 +1004,7 @@ func pickVoteCurrentHeight(
}
// If there are precommits to send...
if prs.Step <= cstypes.RoundStepPrecommitWait && prs.Round != -1 && prs.Round <= rs.Round {
// No need to check rs.Votes.Commit, as this is dealing with individual votes.
if vote := ps.PickVoteToSend(rs.Votes.Precommits(prs.Round), rng); vote != nil {
logger.Debug("Picked rs.Precommits(prs.Round) to send", "round", prs.Round)
return vote
Expand Down
133 changes: 109 additions & 24 deletions internal/consensus/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -694,14 +694,18 @@ func (cs *State) updateToState(state sm.State) {
case state.LastBlockHeight == 0: // Very first commit should be empty.
cs.LastCommit = (*types.VoteSet)(nil)
case cs.CommitRound > -1 && cs.Votes != nil: // Otherwise, use cs.Votes
if !cs.Votes.Precommits(cs.CommitRound).HasTwoThirdsMajority() {
panic(fmt.Sprintf(
"wanted to form a commit, but precommits (H/R: %d/%d) didn't have 2/3+: %v",
state.LastBlockHeight, cs.CommitRound, cs.Votes.Precommits(cs.CommitRound),
))
}
if commit := cs.Votes.GetCommit(cs.CommitRound); commit != nil {
cs.LastCommit = commit
} else {
if !cs.Votes.Precommits(cs.CommitRound).HasTwoThirdsMajority() {
panic(fmt.Sprintf(
"wanted to form a commit, but precommits (H/R: %d/%d) didn't have 2/3+: %v",
state.LastBlockHeight, cs.CommitRound, cs.Votes.Precommits(cs.CommitRound),
))
}

cs.LastCommit = cs.Votes.Precommits(cs.CommitRound)
cs.LastCommit = cs.Votes.Precommits(cs.CommitRound)
}

case cs.LastCommit == nil:
// NOTE: when consensus starts, it has no votes. reconstructLastCommit
Expand Down Expand Up @@ -1339,7 +1343,7 @@ func (cs *State) createProposalBlock(ctx context.Context) (*types.Block, error)
if !ok {
return nil, fmt.Errorf("last commit is neither a VoteSet nor a Commit")
}
// TODO This will need to be extended to support vote extensions.
// XXX This will need to be extended to support vote extensions.
lastExtCommit = lastCommitAsCommit.WrappedExtendedCommit()

default: // This shouldn't happen.
Expand Down Expand Up @@ -1749,7 +1753,7 @@ func (cs *State) enterPrecommitWait(height int64, round int32) {
return
}

if !cs.Votes.Precommits(round).HasTwoThirdsAny() {
if cs.Votes.GetCommit(round) == nil && !cs.Votes.Precommits(round).HasTwoThirdsAny() {
panic(fmt.Sprintf(
"entering precommit wait step (%v/%v), but precommits does not have any +2/3 votes",
height, round,
Expand Down Expand Up @@ -1794,9 +1798,15 @@ func (cs *State) enterCommit(height int64, commitRound int32) {
cs.tryFinalizeCommit(height)
}()

blockID, ok := cs.Votes.Precommits(commitRound).TwoThirdsMajority()
if !ok || blockID.IsNil() {
panic("RunActionCommit() expects +2/3 precommits")
var blockID types.BlockID
if commit := cs.Votes.GetCommit(commitRound); commit != nil {
blockID = commit.BlockID
} else {
var ok bool
blockID, ok = cs.Votes.Precommits(commitRound).TwoThirdsMajority()
if !ok || blockID.IsNil() {
panic("RunActionCommit() expects +2/3 precommits")
}
}

// The Locked* fields no longer matter.
Expand Down Expand Up @@ -1839,10 +1849,16 @@ func (cs *State) tryFinalizeCommit(height int64) {
panic(fmt.Sprintf("tryFinalizeCommit() cs.Height: %v vs height: %v", cs.Height, height))
}

blockID, ok := cs.Votes.Precommits(cs.CommitRound).TwoThirdsMajority()
if !ok || blockID.IsNil() {
logger.Error("Failed attempt to finalize commit; there was no +2/3 majority or +2/3 was for nil")
return
var blockID types.BlockID
if commit := cs.Votes.GetCommit(cs.CommitRound); commit != nil {
blockID = commit.BlockID
} else {
var ok bool
blockID, ok = cs.Votes.Precommits(cs.CommitRound).TwoThirdsMajority()
if !ok || blockID.IsNil() {
logger.Error("Failed attempt to finalize commit; there was no +2/3 majority or +2/3 was for nil")
return
}
}

if !cs.ProposalBlock.HashesTo(blockID.Hash) {
Expand Down Expand Up @@ -1871,14 +1887,20 @@ func (cs *State) finalizeCommit(height int64) {
return
}

cs.calculatePrevoteMessageDelayMetrics()
var blockID types.BlockID
if commit := cs.Votes.GetCommit(cs.CommitRound); commit != nil {
blockID = commit.BlockID
} else {
cs.calculatePrevoteMessageDelayMetrics()

blockID, ok := cs.Votes.Precommits(cs.CommitRound).TwoThirdsMajority()
var ok bool
blockID, ok = cs.Votes.Precommits(cs.CommitRound).TwoThirdsMajority()
if !ok {
panic("cannot finalize commit; commit does not have 2/3 majority")
}
}
block, blockParts := cs.ProposalBlock, cs.ProposalBlockParts

if !ok {
panic("cannot finalize commit; commit does not have 2/3 majority")
}
if !blockParts.HasHeader(blockID.PartSetHeader) {
panic("expected ProposalBlockParts header to be commit header")
}
Expand All @@ -1902,9 +1924,14 @@ func (cs *State) finalizeCommit(height int64) {

// Save to blockStore.
if cs.blockStore.Height() < block.Height {
// NOTE: the seenCommit is local justification to commit this block,
// but may differ from the LastCommit included in the next block
seenExtendedCommit := cs.Votes.Precommits(cs.CommitRound).MakeExtendedCommit(cs.state.ConsensusParams.Feature)
var seenExtendedCommit *types.ExtendedCommit
if commit := cs.Votes.GetCommit(cs.CommitRound); commit != nil {
seenExtendedCommit = commit.WrappedExtendedCommit()
} else {
// NOTE: the seenCommit is local justification to commit this block,
// but may differ from the LastCommit included in the next block
seenExtendedCommit = cs.Votes.Precommits(cs.CommitRound).MakeExtendedCommit(cs.state.ConsensusParams.Feature)
}
if cs.isVoteExtensionsEnabled(block.Height) {
cs.blockStore.SaveBlockWithExtendedCommit(block, blockParts, seenExtendedCommit)
} else {
Expand Down Expand Up @@ -2509,6 +2536,7 @@ func (cs *State) addVote(vote *types.Vote, peerID p2p.ID) (added bool, err error
}

case types.PrecommitType:
// No need to check cs.Votes.Commit() as this is about adding votes
precommits := cs.Votes.Precommits(vote.Round)
cs.Logger.Debug("Added vote to precommit",
"height", vote.Height,
Expand Down Expand Up @@ -2544,6 +2572,63 @@ func (cs *State) addVote(vote *types.Vote, peerID p2p.ID) (added bool, err error
return added, err
}

func (cs *State) AddCommit(commit *types.Commit, peerID p2p.ID) (added bool, err error) {
cs.Logger.Debug(
"Adding whole commit",
"commit_height", commit.Height,
"commit_round", commit.Round,
"commit_blockId", commit.BlockID,
"cs_height", cs.Height,
"cs_round", cs.Round,
)

if commit.Height < cs.Height {
// cs.metrics.MarkLateCommit() TODO Implement
}

// Height mismatch is ignored.
// Not necessarily a bad peer, but not favorable behavior.
if commit.Height != cs.Height {
cs.Logger.Debug("Commit ignored and not added",
"commit_height", commit.Height,
"cs_height", cs.Height,
"peer", peerID)
return added, err
}

// Check to see if the chain is configured to extend votes.
extEnabled := cs.isVoteExtensionsEnabled(commit.Height)
if extEnabled {
// We don't support receiving commits with vote extensions enabled ATM.
cs.Logger.Error("Received commit with vote extension for height %v (extensions disabled) from peer ID %s", commit.Height, peerID)
return added, err
}

// TODO: double check that we have fully validated the commit at this function's caller

height := cs.Height
cs.Votes.SetCommit(commit)

// TODO We need something similar for Commits
// if err := cs.eventBus.PublishEventVote(types.EventDataVote{Vote: vote}); err != nil {
// return added, err
// }
// cs.evsw.FireEvent(types.EventVote, vote)

// Executed as Commit could be from a higher round
cs.enterNewRound(height, commit.Round)
cs.enterPrecommit(height, commit.Round)

cs.enterCommit(height, commit.Round)
// TODO Enable back. Does Commit have something similar? If not implement HasAll() for Commit
// skipTimeoutCommit := cs.state.NextBlockDelay == 0 && cs.config.TimeoutCommit == 0
// if skipTimeoutCommit && commit.HasAll() {
// cs.enterNewRound(cs.Height, 0)
// }

return added, err
}

// CONTRACT: cs.privValidator is not nil.
func (cs *State) signVote(
msgType types.SignedMsgType,
Expand Down
20 changes: 19 additions & 1 deletion internal/consensus/types/height_vote_set.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ type HeightVoteSet struct {
mtx sync.Mutex
round int32 // max tracked round
roundVoteSets map[int32]RoundVoteSet // keys: [0...round]
peerCatchupRounds map[p2p.ID][]int32 // keys: peer.ID; values: at most 2 rounds
commit *types.Commit
peerCatchupRounds map[p2p.ID][]int32 // keys: peer.ID; values: at most 2 rounds
}

func NewHeightVoteSet(chainID string, height int64, valSet *types.ValidatorSet) *HeightVoteSet {
Expand Down Expand Up @@ -164,6 +165,23 @@ func (hvs *HeightVoteSet) Precommits(round int32) *types.VoteSet {
return hvs.getVoteSet(round, types.PrecommitType)
}

func (hvs *HeightVoteSet) GetCommit(round int32) *types.Commit {
hvs.mtx.Lock()
defer hvs.mtx.Unlock()
if hvs.commit == nil || hvs.commit.Round != round {
return nil
}

return hvs.commit
}

func (hvs *HeightVoteSet) SetCommit(commit *types.Commit) {
hvs.mtx.Lock()
defer hvs.mtx.Unlock()
// TODO: check if it's already set? Not sure
hvs.commit = commit
}

// Last round and blockID that has +2/3 prevotes for a particular block or nil.
// Returns -1 if no such round exists.
func (hvs *HeightVoteSet) POLInfo() (polRound int32, polBlockID types.BlockID) {
Expand Down
1 change: 0 additions & 1 deletion types/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -1031,7 +1031,6 @@ func (commit *Commit) Hash() cmtbytes.HexBytes {
// Wrapping a Commit as an ExtendedCommit is useful when an API
// requires an ExtendedCommit wire type but does not
// need the VoteExtension data.
// TODO probably remove this function
func (commit *Commit) WrappedExtendedCommit() *ExtendedCommit {
cs := make([]ExtendedCommitSig, len(commit.Signatures))
for idx, s := range commit.Signatures {
Expand Down

0 comments on commit b48a625

Please sign in to comment.