func (cs *ConsensusState) SetProposal(proposal *Proposal) error { cs.mtx.Lock() defer cs.mtx.Unlock() // Already have one if cs.Proposal != nil { return nil } // Does not apply if proposal.Height != cs.Height || proposal.Round != cs.Round { return nil } // We don't care about the proposal if we're already in RoundStepCommit. if RoundStepCommit <= cs.Step { return nil } // Verify POLRound, which must be -1 or between 0 and proposal.Round exclusive. if proposal.POLRound != -1 && (proposal.POLRound < 0 || proposal.Round <= proposal.POLRound) { return ErrInvalidProposalPOLRound } // Verify signature if !cs.Validators.Proposer().PubKey.VerifyBytes(account.SignBytes(cs.state.ChainID, proposal), proposal.Signature) { return ErrInvalidProposalSignature } cs.Proposal = proposal cs.ProposalBlockParts = types.NewPartSetFromHeader(proposal.BlockPartsHeader) return nil }
// Enter: +2/3 precommits for block func (cs *ConsensusState) EnterCommit(height int) { cs.mtx.Lock() defer cs.mtx.Unlock() if cs.Height != height || RoundStepCommit <= cs.Step { log.Debug(Fmt("EnterCommit(%v): Invalid args. Current step: %v/%v/%v", height, cs.Height, cs.Round, cs.Step)) return } log.Debug(Fmt("EnterCommit(%v). Current: %v/%v/%v", height, cs.Height, cs.Round, cs.Step)) defer func() { // Done Entercommit: // keep ca.Round the same, it points to the right Precommits set. cs.Step = RoundStepCommit cs.newStepCh <- cs.getRoundState() // Maybe finalize immediately. cs.tryFinalizeCommit(height) }() // SANITY CHECK hash, partsHeader, ok := cs.Votes.Precommits(cs.Round).TwoThirdsMajority() if !ok { panic("RunActionCommit() expects +2/3 precommits") } // END SANITY CHECK // The Locked* fields no longer matter. // Move them over to ProposalBlock if they match the commit hash, // otherwise they can now be cleared. if cs.LockedBlock.HashesTo(hash) { cs.ProposalBlock = cs.LockedBlock cs.ProposalBlockParts = cs.LockedBlockParts cs.LockedRound = 0 cs.LockedBlock = nil cs.LockedBlockParts = nil } else { cs.LockedRound = 0 cs.LockedBlock = nil cs.LockedBlockParts = nil } // If we don't have the block being committed, set up to get it. if !cs.ProposalBlock.HashesTo(hash) { if !cs.ProposalBlockParts.HasHeader(partsHeader) { // We're getting the wrong block. // Set up ProposalBlockParts and keep waiting. cs.ProposalBlock = nil cs.ProposalBlockParts = types.NewPartSetFromHeader(partsHeader) } else { // We just need to keep waiting. } } }
// Enter: +2/3 precomits for block or nil. // Enter: `timeoutPrevote` after any +2/3 prevotes. // Enter: any +2/3 precommits for next round. // Lock & precommit the ProposalBlock if we have enough prevotes for it, // else, unlock an existing lock and precommit nil if +2/3 of prevotes were nil, // else, precommit locked block or nil otherwise. func (cs *ConsensusState) EnterPrecommit(height int, round int) { cs.mtx.Lock() defer cs.mtx.Unlock() if cs.Height != height || round < cs.Round || (cs.Round == round && RoundStepPrecommit <= cs.Step) { log.Debug(Fmt("EnterPrecommit(%v/%v): Invalid args. Current step: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step)) return } log.Debug(Fmt("EnterPrecommit(%v/%v). Current: %v/%v/%v", height, round, cs.Height, cs.Round, cs.Step)) defer func() { // Done EnterPrecommit: cs.Round = round cs.Step = RoundStepPrecommit cs.newStepCh <- cs.getRoundState() /* This isn't necessary because addVote() does it for us. if cs.Votes.Precommits(round).HasTwoThirdsAny() { go cs.EnterPrecommitWait(height, round) }*/ }() hash, partsHeader, ok := cs.Votes.Prevotes(round).TwoThirdsMajority() // If we don't have two thirds of prevotes, just precommit locked block or nil if !ok { if cs.LockedBlock != nil { log.Debug("EnterPrecommit: No +2/3 prevotes during EnterPrecommit. Precommitting lock.") cs.signAddVote(types.VoteTypePrecommit, cs.LockedBlock.Hash(), cs.LockedBlockParts.Header()) } else { log.Debug("EnterPrecommit: No +2/3 prevotes during EnterPrecommit. Precommitting nil.") cs.signAddVote(types.VoteTypePrecommit, nil, types.PartSetHeader{}) } return } // +2/3 prevoted nil. Unlock and precommit nil. if len(hash) == 0 { if cs.LockedBlock == nil { log.Debug("EnterPrecommit: +2/3 prevoted for nil.") } else { log.Debug("EnterPrecommit: +2/3 prevoted for nil. Unlocking") cs.LockedRound = 0 cs.LockedBlock = nil cs.LockedBlockParts = nil } cs.signAddVote(types.VoteTypePrecommit, nil, types.PartSetHeader{}) return } // At this point, +2/3 prevoted for a particular block. // If +2/3 prevoted for already locked block, precommit it. if cs.LockedBlock.HashesTo(hash) { log.Debug("EnterPrecommit: +2/3 prevoted locked block.") cs.signAddVote(types.VoteTypePrecommit, hash, partsHeader) return } // If +2/3 prevoted for proposal block, stage and precommit it if cs.ProposalBlock.HashesTo(hash) { log.Debug("EnterPrecommit: +2/3 prevoted proposal block.") // Validate the block. if err := cs.stageBlock(cs.ProposalBlock, cs.ProposalBlockParts); err != nil { panic(Fmt("EnterPrecommit: +2/3 prevoted for an invalid block: %v", err)) } cs.LockedRound = round cs.LockedBlock = cs.ProposalBlock cs.LockedBlockParts = cs.ProposalBlockParts cs.signAddVote(types.VoteTypePrecommit, hash, partsHeader) return } // Otherwise, we need to fetch the +2/3 prevoted block. // Unlock and precommit nil. // The +2/3 prevotes for this round is the POL for our unlock. if cs.Votes.POLRound() < round { panic(Fmt("This POLRound shold be %v but got %", round, cs.Votes.POLRound())) } cs.LockedRound = 0 cs.LockedBlock = nil cs.LockedBlockParts = nil if !cs.ProposalBlockParts.HasHeader(partsHeader) { cs.ProposalBlock = nil cs.ProposalBlockParts = types.NewPartSetFromHeader(partsHeader) } cs.signAddVote(types.VoteTypePrecommit, nil, types.PartSetHeader{}) return }