|
|
@ -153,6 +153,7 @@ type StateOption func(*State) |
|
|
|
|
|
|
|
// NewState returns a new State.
|
|
|
|
func NewState( |
|
|
|
ctx context.Context, |
|
|
|
logger log.Logger, |
|
|
|
cfg *config.ConsensusConfig, |
|
|
|
state sm.State, |
|
|
@ -190,7 +191,7 @@ func NewState( |
|
|
|
cs.reconstructLastCommit(state) |
|
|
|
} |
|
|
|
|
|
|
|
cs.updateToState(state) |
|
|
|
cs.updateToState(ctx, state) |
|
|
|
|
|
|
|
// NOTE: we do not call scheduleRound0 yet, we do that upon Start()
|
|
|
|
|
|
|
@ -345,7 +346,7 @@ func (cs *State) OnStart(ctx context.Context) error { |
|
|
|
|
|
|
|
LOOP: |
|
|
|
for { |
|
|
|
err := cs.catchupReplay(cs.Height) |
|
|
|
err := cs.catchupReplay(ctx, cs.Height) |
|
|
|
switch { |
|
|
|
case err == nil: |
|
|
|
break LOOP |
|
|
@ -409,7 +410,7 @@ func (cs *State) OnStart(ctx context.Context) error { |
|
|
|
} |
|
|
|
|
|
|
|
// now start the receiveRoutine
|
|
|
|
go cs.receiveRoutine(0) |
|
|
|
go cs.receiveRoutine(ctx, 0) |
|
|
|
|
|
|
|
// schedule the first round!
|
|
|
|
// use GetRoundState so we don't race the receiveRoutine for access
|
|
|
@ -427,7 +428,7 @@ func (cs *State) startRoutines(ctx context.Context, maxSteps int) { |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
go cs.receiveRoutine(maxSteps) |
|
|
|
go cs.receiveRoutine(ctx, maxSteps) |
|
|
|
} |
|
|
|
|
|
|
|
// loadWalFile loads WAL data from file. It overwrites cs.wal.
|
|
|
@ -625,7 +626,7 @@ func (cs *State) reconstructLastCommit(state sm.State) { |
|
|
|
|
|
|
|
// Updates State and increments height to match that of state.
|
|
|
|
// The round becomes 0 and cs.Step becomes cstypes.RoundStepNewHeight.
|
|
|
|
func (cs *State) updateToState(state sm.State) { |
|
|
|
func (cs *State) updateToState(ctx context.Context, state sm.State) { |
|
|
|
if cs.CommitRound > -1 && 0 < cs.Height && cs.Height != state.LastBlockHeight { |
|
|
|
panic(fmt.Sprintf( |
|
|
|
"updateToState() expected state height of %v but found %v", |
|
|
@ -660,7 +661,7 @@ func (cs *State) updateToState(state sm.State) { |
|
|
|
"new_height", state.LastBlockHeight+1, |
|
|
|
"old_height", cs.state.LastBlockHeight+1, |
|
|
|
) |
|
|
|
cs.newStep() |
|
|
|
cs.newStep(ctx) |
|
|
|
return |
|
|
|
} |
|
|
|
} |
|
|
@ -729,10 +730,10 @@ func (cs *State) updateToState(state sm.State) { |
|
|
|
cs.state = state |
|
|
|
|
|
|
|
// Finally, broadcast RoundState
|
|
|
|
cs.newStep() |
|
|
|
cs.newStep(ctx) |
|
|
|
} |
|
|
|
|
|
|
|
func (cs *State) newStep() { |
|
|
|
func (cs *State) newStep(ctx context.Context) { |
|
|
|
rs := cs.RoundStateEvent() |
|
|
|
if err := cs.wal.Write(rs); err != nil { |
|
|
|
cs.Logger.Error("failed writing to WAL", "err", err) |
|
|
@ -742,7 +743,7 @@ func (cs *State) newStep() { |
|
|
|
|
|
|
|
// newStep is called by updateToState in NewState before the eventBus is set!
|
|
|
|
if cs.eventBus != nil { |
|
|
|
if err := cs.eventBus.PublishEventNewRoundStep(rs); err != nil { |
|
|
|
if err := cs.eventBus.PublishEventNewRoundStep(ctx, rs); err != nil { |
|
|
|
cs.Logger.Error("failed publishing new round step", "err", err) |
|
|
|
} |
|
|
|
|
|
|
@ -758,7 +759,7 @@ func (cs *State) newStep() { |
|
|
|
// It keeps the RoundState and is the only thing that updates it.
|
|
|
|
// Updates (state transitions) happen on timeouts, complete proposals, and 2/3 majorities.
|
|
|
|
// State must be locked before any internal state is updated.
|
|
|
|
func (cs *State) receiveRoutine(maxSteps int) { |
|
|
|
func (cs *State) receiveRoutine(ctx context.Context, maxSteps int) { |
|
|
|
onExit := func(cs *State) { |
|
|
|
// NOTE: the internalMsgQueue may have signed messages from our
|
|
|
|
// priv_val that haven't hit the WAL, but its ok because
|
|
|
@ -804,7 +805,7 @@ func (cs *State) receiveRoutine(maxSteps int) { |
|
|
|
|
|
|
|
select { |
|
|
|
case <-cs.txNotifier.TxsAvailable(): |
|
|
|
cs.handleTxsAvailable() |
|
|
|
cs.handleTxsAvailable(ctx) |
|
|
|
|
|
|
|
case mi = <-cs.peerMsgQueue: |
|
|
|
if err := cs.wal.Write(mi); err != nil { |
|
|
@ -813,7 +814,7 @@ func (cs *State) receiveRoutine(maxSteps int) { |
|
|
|
|
|
|
|
// handles proposals, block parts, votes
|
|
|
|
// may generate internal events (votes, complete proposals, 2/3 majorities)
|
|
|
|
cs.handleMsg(mi) |
|
|
|
cs.handleMsg(ctx, mi) |
|
|
|
|
|
|
|
case mi = <-cs.internalMsgQueue: |
|
|
|
err := cs.wal.WriteSync(mi) // NOTE: fsync
|
|
|
@ -833,7 +834,7 @@ func (cs *State) receiveRoutine(maxSteps int) { |
|
|
|
} |
|
|
|
|
|
|
|
// handles proposals, block parts, votes
|
|
|
|
cs.handleMsg(mi) |
|
|
|
cs.handleMsg(ctx, mi) |
|
|
|
|
|
|
|
case ti := <-cs.timeoutTicker.Chan(): // tockChan:
|
|
|
|
if err := cs.wal.Write(ti); err != nil { |
|
|
@ -842,17 +843,18 @@ func (cs *State) receiveRoutine(maxSteps int) { |
|
|
|
|
|
|
|
// if the timeout is relevant to the rs
|
|
|
|
// go to the next step
|
|
|
|
cs.handleTimeout(ti, rs) |
|
|
|
cs.handleTimeout(ctx, ti, rs) |
|
|
|
|
|
|
|
case <-cs.Quit(): |
|
|
|
onExit(cs) |
|
|
|
return |
|
|
|
} |
|
|
|
// TODO should we handle context cancels here?
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// state transitions on complete-proposal, 2/3-any, 2/3-one
|
|
|
|
func (cs *State) handleMsg(mi msgInfo) { |
|
|
|
func (cs *State) handleMsg(ctx context.Context, mi msgInfo) { |
|
|
|
cs.mtx.Lock() |
|
|
|
defer cs.mtx.Unlock() |
|
|
|
|
|
|
@ -871,7 +873,7 @@ func (cs *State) handleMsg(mi msgInfo) { |
|
|
|
|
|
|
|
case *BlockPartMessage: |
|
|
|
// if the proposal is complete, we'll enterPrevote or tryFinalizeCommit
|
|
|
|
added, err = cs.addProposalBlockPart(msg, peerID) |
|
|
|
added, err = cs.addProposalBlockPart(ctx, msg, peerID) |
|
|
|
if added { |
|
|
|
cs.statsMsgQueue <- mi |
|
|
|
} |
|
|
@ -889,7 +891,7 @@ func (cs *State) handleMsg(mi msgInfo) { |
|
|
|
case *VoteMessage: |
|
|
|
// attempt to add the vote and dupeout the validator if its a duplicate signature
|
|
|
|
// if the vote gives us a 2/3-any or 2/3-one, we transition
|
|
|
|
added, err = cs.tryAddVote(msg.Vote, peerID) |
|
|
|
added, err = cs.tryAddVote(ctx, msg.Vote, peerID) |
|
|
|
if added { |
|
|
|
cs.statsMsgQueue <- mi |
|
|
|
} |
|
|
@ -926,7 +928,11 @@ func (cs *State) handleMsg(mi msgInfo) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
func (cs *State) handleTimeout(ti timeoutInfo, rs cstypes.RoundState) { |
|
|
|
func (cs *State) handleTimeout( |
|
|
|
ctx context.Context, |
|
|
|
ti timeoutInfo, |
|
|
|
rs cstypes.RoundState, |
|
|
|
) { |
|
|
|
cs.Logger.Debug("received tock", "timeout", ti.Duration, "height", ti.Height, "round", ti.Round, "step", ti.Step) |
|
|
|
|
|
|
|
// timeouts must be for current height, round, step
|
|
|
@ -943,32 +949,32 @@ func (cs *State) handleTimeout(ti timeoutInfo, rs cstypes.RoundState) { |
|
|
|
case cstypes.RoundStepNewHeight: |
|
|
|
// NewRound event fired from enterNewRound.
|
|
|
|
// XXX: should we fire timeout here (for timeout commit)?
|
|
|
|
cs.enterNewRound(ti.Height, 0) |
|
|
|
cs.enterNewRound(ctx, ti.Height, 0) |
|
|
|
|
|
|
|
case cstypes.RoundStepNewRound: |
|
|
|
cs.enterPropose(ti.Height, 0) |
|
|
|
cs.enterPropose(ctx, ti.Height, 0) |
|
|
|
|
|
|
|
case cstypes.RoundStepPropose: |
|
|
|
if err := cs.eventBus.PublishEventTimeoutPropose(cs.RoundStateEvent()); err != nil { |
|
|
|
if err := cs.eventBus.PublishEventTimeoutPropose(ctx, cs.RoundStateEvent()); err != nil { |
|
|
|
cs.Logger.Error("failed publishing timeout propose", "err", err) |
|
|
|
} |
|
|
|
|
|
|
|
cs.enterPrevote(ti.Height, ti.Round) |
|
|
|
cs.enterPrevote(ctx, ti.Height, ti.Round) |
|
|
|
|
|
|
|
case cstypes.RoundStepPrevoteWait: |
|
|
|
if err := cs.eventBus.PublishEventTimeoutWait(cs.RoundStateEvent()); err != nil { |
|
|
|
if err := cs.eventBus.PublishEventTimeoutWait(ctx, cs.RoundStateEvent()); err != nil { |
|
|
|
cs.Logger.Error("failed publishing timeout wait", "err", err) |
|
|
|
} |
|
|
|
|
|
|
|
cs.enterPrecommit(ti.Height, ti.Round) |
|
|
|
cs.enterPrecommit(ctx, ti.Height, ti.Round) |
|
|
|
|
|
|
|
case cstypes.RoundStepPrecommitWait: |
|
|
|
if err := cs.eventBus.PublishEventTimeoutWait(cs.RoundStateEvent()); err != nil { |
|
|
|
if err := cs.eventBus.PublishEventTimeoutWait(ctx, cs.RoundStateEvent()); err != nil { |
|
|
|
cs.Logger.Error("failed publishing timeout wait", "err", err) |
|
|
|
} |
|
|
|
|
|
|
|
cs.enterPrecommit(ti.Height, ti.Round) |
|
|
|
cs.enterNewRound(ti.Height, ti.Round+1) |
|
|
|
cs.enterPrecommit(ctx, ti.Height, ti.Round) |
|
|
|
cs.enterNewRound(ctx, ti.Height, ti.Round+1) |
|
|
|
|
|
|
|
default: |
|
|
|
panic(fmt.Sprintf("invalid timeout step: %v", ti.Step)) |
|
|
@ -976,7 +982,7 @@ func (cs *State) handleTimeout(ti timeoutInfo, rs cstypes.RoundState) { |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
func (cs *State) handleTxsAvailable() { |
|
|
|
func (cs *State) handleTxsAvailable(ctx context.Context) { |
|
|
|
cs.mtx.Lock() |
|
|
|
defer cs.mtx.Unlock() |
|
|
|
|
|
|
@ -997,7 +1003,7 @@ func (cs *State) handleTxsAvailable() { |
|
|
|
cs.scheduleTimeout(timeoutCommit, cs.Height, 0, cstypes.RoundStepNewRound) |
|
|
|
|
|
|
|
case cstypes.RoundStepNewRound: // after timeoutCommit
|
|
|
|
cs.enterPropose(cs.Height, 0) |
|
|
|
cs.enterPropose(ctx, cs.Height, 0) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -1011,7 +1017,7 @@ func (cs *State) handleTxsAvailable() { |
|
|
|
// Enter: +2/3 precommits for nil at (height,round-1)
|
|
|
|
// Enter: +2/3 prevotes any or +2/3 precommits for block or any from (height, round)
|
|
|
|
// NOTE: cs.StartTime was already set for height.
|
|
|
|
func (cs *State) enterNewRound(height int64, round int32) { |
|
|
|
func (cs *State) enterNewRound(ctx context.Context, height int64, round int32) { |
|
|
|
logger := cs.Logger.With("height", height, "round", round) |
|
|
|
|
|
|
|
if cs.Height != height || round < cs.Round || (cs.Round == round && cs.Step != cstypes.RoundStepNewHeight) { |
|
|
@ -1054,7 +1060,7 @@ func (cs *State) enterNewRound(height int64, round int32) { |
|
|
|
cs.Votes.SetRound(tmmath.SafeAddInt32(round, 1)) // also track next round (round+1) to allow round-skipping
|
|
|
|
cs.TriggeredTimeoutPrecommit = false |
|
|
|
|
|
|
|
if err := cs.eventBus.PublishEventNewRound(cs.NewRoundEvent()); err != nil { |
|
|
|
if err := cs.eventBus.PublishEventNewRound(ctx, cs.NewRoundEvent()); err != nil { |
|
|
|
cs.Logger.Error("failed publishing new round", "err", err) |
|
|
|
} |
|
|
|
|
|
|
@ -1070,7 +1076,7 @@ func (cs *State) enterNewRound(height int64, round int32) { |
|
|
|
cstypes.RoundStepNewRound) |
|
|
|
} |
|
|
|
} else { |
|
|
|
cs.enterPropose(height, round) |
|
|
|
cs.enterPropose(ctx, height, round) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -1093,7 +1099,7 @@ func (cs *State) needProofBlock(height int64) bool { |
|
|
|
// Enter (CreateEmptyBlocks, CreateEmptyBlocksInterval > 0 ):
|
|
|
|
// after enterNewRound(height,round), after timeout of CreateEmptyBlocksInterval
|
|
|
|
// Enter (!CreateEmptyBlocks) : after enterNewRound(height,round), once txs are in the mempool
|
|
|
|
func (cs *State) enterPropose(height int64, round int32) { |
|
|
|
func (cs *State) enterPropose(ctx context.Context, height int64, round int32) { |
|
|
|
logger := cs.Logger.With("height", height, "round", round) |
|
|
|
|
|
|
|
if cs.Height != height || round < cs.Round || (cs.Round == round && cstypes.RoundStepPropose <= cs.Step) { |
|
|
@ -1109,13 +1115,13 @@ func (cs *State) enterPropose(height int64, round int32) { |
|
|
|
defer func() { |
|
|
|
// Done enterPropose:
|
|
|
|
cs.updateRoundStep(round, cstypes.RoundStepPropose) |
|
|
|
cs.newStep() |
|
|
|
cs.newStep(ctx) |
|
|
|
|
|
|
|
// If we have the whole proposal + POL, then goto Prevote now.
|
|
|
|
// else, we'll enterPrevote when the rest of the proposal is received (in AddProposalBlockPart),
|
|
|
|
// or else after timeoutPropose
|
|
|
|
if cs.isProposalComplete() { |
|
|
|
cs.enterPrevote(height, cs.Round) |
|
|
|
cs.enterPrevote(ctx, height, cs.Round) |
|
|
|
} |
|
|
|
}() |
|
|
|
|
|
|
@ -1271,7 +1277,7 @@ func (cs *State) createProposalBlock() (block *types.Block, blockParts *types.Pa |
|
|
|
// Enter: proposal block and POL is ready.
|
|
|
|
// Prevote for LockedBlock if we're locked, or ProposalBlock if valid.
|
|
|
|
// Otherwise vote nil.
|
|
|
|
func (cs *State) enterPrevote(height int64, round int32) { |
|
|
|
func (cs *State) enterPrevote(ctx context.Context, height int64, round int32) { |
|
|
|
logger := cs.Logger.With("height", height, "round", round) |
|
|
|
|
|
|
|
if cs.Height != height || round < cs.Round || (cs.Round == round && cstypes.RoundStepPrevote <= cs.Step) { |
|
|
@ -1285,7 +1291,7 @@ func (cs *State) enterPrevote(height int64, round int32) { |
|
|
|
defer func() { |
|
|
|
// Done enterPrevote:
|
|
|
|
cs.updateRoundStep(round, cstypes.RoundStepPrevote) |
|
|
|
cs.newStep() |
|
|
|
cs.newStep(ctx) |
|
|
|
}() |
|
|
|
|
|
|
|
logger.Debug("entering prevote step", "current", fmt.Sprintf("%v/%v/%v", cs.Height, cs.Round, cs.Step)) |
|
|
@ -1331,7 +1337,7 @@ func (cs *State) defaultDoPrevote(height int64, round int32) { |
|
|
|
} |
|
|
|
|
|
|
|
// Enter: any +2/3 prevotes at next round.
|
|
|
|
func (cs *State) enterPrevoteWait(height int64, round int32) { |
|
|
|
func (cs *State) enterPrevoteWait(ctx context.Context, height int64, round int32) { |
|
|
|
logger := cs.Logger.With("height", height, "round", round) |
|
|
|
|
|
|
|
if cs.Height != height || round < cs.Round || (cs.Round == round && cstypes.RoundStepPrevoteWait <= cs.Step) { |
|
|
@ -1354,7 +1360,7 @@ func (cs *State) enterPrevoteWait(height int64, round int32) { |
|
|
|
defer func() { |
|
|
|
// Done enterPrevoteWait:
|
|
|
|
cs.updateRoundStep(round, cstypes.RoundStepPrevoteWait) |
|
|
|
cs.newStep() |
|
|
|
cs.newStep(ctx) |
|
|
|
}() |
|
|
|
|
|
|
|
// Wait for some more prevotes; enterPrecommit
|
|
|
@ -1367,7 +1373,7 @@ func (cs *State) enterPrevoteWait(height int64, round int32) { |
|
|
|
// Lock & precommit the ProposalBlock if we have enough prevotes for it (a POL in this round)
|
|
|
|
// else, unlock an existing lock and precommit nil if +2/3 of prevotes were nil,
|
|
|
|
// else, precommit nil otherwise.
|
|
|
|
func (cs *State) enterPrecommit(height int64, round int32) { |
|
|
|
func (cs *State) enterPrecommit(ctx context.Context, height int64, round int32) { |
|
|
|
logger := cs.Logger.With("height", height, "round", round) |
|
|
|
|
|
|
|
if cs.Height != height || round < cs.Round || (cs.Round == round && cstypes.RoundStepPrecommit <= cs.Step) { |
|
|
@ -1383,7 +1389,7 @@ func (cs *State) enterPrecommit(height int64, round int32) { |
|
|
|
defer func() { |
|
|
|
// Done enterPrecommit:
|
|
|
|
cs.updateRoundStep(round, cstypes.RoundStepPrecommit) |
|
|
|
cs.newStep() |
|
|
|
cs.newStep(ctx) |
|
|
|
}() |
|
|
|
|
|
|
|
// check for a polka
|
|
|
@ -1402,7 +1408,7 @@ func (cs *State) enterPrecommit(height int64, round int32) { |
|
|
|
} |
|
|
|
|
|
|
|
// At this point +2/3 prevoted for a particular block or nil.
|
|
|
|
if err := cs.eventBus.PublishEventPolka(cs.RoundStateEvent()); err != nil { |
|
|
|
if err := cs.eventBus.PublishEventPolka(ctx, cs.RoundStateEvent()); err != nil { |
|
|
|
logger.Error("failed publishing polka", "err", err) |
|
|
|
} |
|
|
|
|
|
|
@ -1422,7 +1428,7 @@ func (cs *State) enterPrecommit(height int64, round int32) { |
|
|
|
cs.LockedBlock = nil |
|
|
|
cs.LockedBlockParts = nil |
|
|
|
|
|
|
|
if err := cs.eventBus.PublishEventUnlock(cs.RoundStateEvent()); err != nil { |
|
|
|
if err := cs.eventBus.PublishEventUnlock(ctx, cs.RoundStateEvent()); err != nil { |
|
|
|
logger.Error("failed publishing event unlock", "err", err) |
|
|
|
} |
|
|
|
} |
|
|
@ -1438,7 +1444,7 @@ func (cs *State) enterPrecommit(height int64, round int32) { |
|
|
|
logger.Debug("precommit step; +2/3 prevoted locked block; relocking") |
|
|
|
cs.LockedRound = round |
|
|
|
|
|
|
|
if err := cs.eventBus.PublishEventRelock(cs.RoundStateEvent()); err != nil { |
|
|
|
if err := cs.eventBus.PublishEventRelock(ctx, cs.RoundStateEvent()); err != nil { |
|
|
|
logger.Error("failed publishing event relock", "err", err) |
|
|
|
} |
|
|
|
|
|
|
@ -1459,7 +1465,7 @@ func (cs *State) enterPrecommit(height int64, round int32) { |
|
|
|
cs.LockedBlock = cs.ProposalBlock |
|
|
|
cs.LockedBlockParts = cs.ProposalBlockParts |
|
|
|
|
|
|
|
if err := cs.eventBus.PublishEventLock(cs.RoundStateEvent()); err != nil { |
|
|
|
if err := cs.eventBus.PublishEventLock(ctx, cs.RoundStateEvent()); err != nil { |
|
|
|
logger.Error("failed publishing event lock", "err", err) |
|
|
|
} |
|
|
|
|
|
|
@ -1481,7 +1487,7 @@ func (cs *State) enterPrecommit(height int64, round int32) { |
|
|
|
cs.ProposalBlockParts = types.NewPartSetFromHeader(blockID.PartSetHeader) |
|
|
|
} |
|
|
|
|
|
|
|
if err := cs.eventBus.PublishEventUnlock(cs.RoundStateEvent()); err != nil { |
|
|
|
if err := cs.eventBus.PublishEventUnlock(ctx, cs.RoundStateEvent()); err != nil { |
|
|
|
logger.Error("failed publishing event unlock", "err", err) |
|
|
|
} |
|
|
|
|
|
|
@ -1489,7 +1495,7 @@ func (cs *State) enterPrecommit(height int64, round int32) { |
|
|
|
} |
|
|
|
|
|
|
|
// Enter: any +2/3 precommits for next round.
|
|
|
|
func (cs *State) enterPrecommitWait(height int64, round int32) { |
|
|
|
func (cs *State) enterPrecommitWait(ctx context.Context, height int64, round int32) { |
|
|
|
logger := cs.Logger.With("height", height, "round", round) |
|
|
|
|
|
|
|
if cs.Height != height || round < cs.Round || (cs.Round == round && cs.TriggeredTimeoutPrecommit) { |
|
|
@ -1513,7 +1519,7 @@ func (cs *State) enterPrecommitWait(height int64, round int32) { |
|
|
|
defer func() { |
|
|
|
// Done enterPrecommitWait:
|
|
|
|
cs.TriggeredTimeoutPrecommit = true |
|
|
|
cs.newStep() |
|
|
|
cs.newStep(ctx) |
|
|
|
}() |
|
|
|
|
|
|
|
// wait for some more precommits; enterNewRound
|
|
|
@ -1521,7 +1527,7 @@ func (cs *State) enterPrecommitWait(height int64, round int32) { |
|
|
|
} |
|
|
|
|
|
|
|
// Enter: +2/3 precommits for block
|
|
|
|
func (cs *State) enterCommit(height int64, commitRound int32) { |
|
|
|
func (cs *State) enterCommit(ctx context.Context, height int64, commitRound int32) { |
|
|
|
logger := cs.Logger.With("height", height, "commit_round", commitRound) |
|
|
|
|
|
|
|
if cs.Height != height || cstypes.RoundStepCommit <= cs.Step { |
|
|
@ -1540,10 +1546,10 @@ func (cs *State) enterCommit(height int64, commitRound int32) { |
|
|
|
cs.updateRoundStep(cs.Round, cstypes.RoundStepCommit) |
|
|
|
cs.CommitRound = commitRound |
|
|
|
cs.CommitTime = tmtime.Now() |
|
|
|
cs.newStep() |
|
|
|
cs.newStep(ctx) |
|
|
|
|
|
|
|
// Maybe finalize immediately.
|
|
|
|
cs.tryFinalizeCommit(height) |
|
|
|
cs.tryFinalizeCommit(ctx, height) |
|
|
|
}() |
|
|
|
|
|
|
|
blockID, ok := cs.Votes.Precommits(commitRound).TwoThirdsMajority() |
|
|
@ -1574,7 +1580,7 @@ func (cs *State) enterCommit(height int64, commitRound int32) { |
|
|
|
cs.ProposalBlock = nil |
|
|
|
cs.ProposalBlockParts = types.NewPartSetFromHeader(blockID.PartSetHeader) |
|
|
|
|
|
|
|
if err := cs.eventBus.PublishEventValidBlock(cs.RoundStateEvent()); err != nil { |
|
|
|
if err := cs.eventBus.PublishEventValidBlock(ctx, cs.RoundStateEvent()); err != nil { |
|
|
|
logger.Error("failed publishing valid block", "err", err) |
|
|
|
} |
|
|
|
|
|
|
@ -1584,7 +1590,7 @@ func (cs *State) enterCommit(height int64, commitRound int32) { |
|
|
|
} |
|
|
|
|
|
|
|
// If we have the block AND +2/3 commits for it, finalize.
|
|
|
|
func (cs *State) tryFinalizeCommit(height int64) { |
|
|
|
func (cs *State) tryFinalizeCommit(ctx context.Context, height int64) { |
|
|
|
logger := cs.Logger.With("height", height) |
|
|
|
|
|
|
|
if cs.Height != height { |
|
|
@ -1608,11 +1614,11 @@ func (cs *State) tryFinalizeCommit(height int64) { |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
cs.finalizeCommit(height) |
|
|
|
cs.finalizeCommit(ctx, height) |
|
|
|
} |
|
|
|
|
|
|
|
// Increment height and goto cstypes.RoundStepNewHeight
|
|
|
|
func (cs *State) finalizeCommit(height int64) { |
|
|
|
func (cs *State) finalizeCommit(ctx context.Context, height int64) { |
|
|
|
logger := cs.Logger.With("height", height) |
|
|
|
|
|
|
|
if cs.Height != height || cs.Step != cstypes.RoundStepCommit { |
|
|
@ -1692,7 +1698,7 @@ func (cs *State) finalizeCommit(height int64) { |
|
|
|
|
|
|
|
// Execute and commit the block, update and save the state, and update the mempool.
|
|
|
|
// NOTE The block.AppHash wont reflect these txs until the next block.
|
|
|
|
stateCopy, err := cs.blockExec.ApplyBlock( |
|
|
|
stateCopy, err := cs.blockExec.ApplyBlock(ctx, |
|
|
|
stateCopy, |
|
|
|
types.BlockID{ |
|
|
|
Hash: block.Hash(), |
|
|
@ -1711,7 +1717,7 @@ func (cs *State) finalizeCommit(height int64) { |
|
|
|
cs.RecordMetrics(height, block) |
|
|
|
|
|
|
|
// NewHeightStep!
|
|
|
|
cs.updateToState(stateCopy) |
|
|
|
cs.updateToState(ctx, stateCopy) |
|
|
|
|
|
|
|
fail.Fail() // XXX
|
|
|
|
|
|
|
@ -1864,7 +1870,11 @@ func (cs *State) defaultSetProposal(proposal *types.Proposal) error { |
|
|
|
// NOTE: block is not necessarily valid.
|
|
|
|
// Asynchronously triggers either enterPrevote (before we timeout of propose) or tryFinalizeCommit,
|
|
|
|
// once we have the full block.
|
|
|
|
func (cs *State) addProposalBlockPart(msg *BlockPartMessage, peerID types.NodeID) (added bool, err error) { |
|
|
|
func (cs *State) addProposalBlockPart( |
|
|
|
ctx context.Context, |
|
|
|
msg *BlockPartMessage, |
|
|
|
peerID types.NodeID, |
|
|
|
) (added bool, err error) { |
|
|
|
height, round, part := msg.Height, msg.Round, msg.Part |
|
|
|
|
|
|
|
// Blocks might be reused, so round mismatch is OK
|
|
|
@ -1918,7 +1928,7 @@ func (cs *State) addProposalBlockPart(msg *BlockPartMessage, peerID types.NodeID |
|
|
|
// NOTE: it's possible to receive complete proposal blocks for future rounds without having the proposal
|
|
|
|
cs.Logger.Info("received complete proposal block", "height", cs.ProposalBlock.Height, "hash", cs.ProposalBlock.Hash()) |
|
|
|
|
|
|
|
if err := cs.eventBus.PublishEventCompleteProposal(cs.CompleteProposalEvent()); err != nil { |
|
|
|
if err := cs.eventBus.PublishEventCompleteProposal(ctx, cs.CompleteProposalEvent()); err != nil { |
|
|
|
cs.Logger.Error("failed publishing event complete proposal", "err", err) |
|
|
|
} |
|
|
|
|
|
|
@ -1946,13 +1956,13 @@ func (cs *State) addProposalBlockPart(msg *BlockPartMessage, peerID types.NodeID |
|
|
|
|
|
|
|
if cs.Step <= cstypes.RoundStepPropose && cs.isProposalComplete() { |
|
|
|
// Move onto the next step
|
|
|
|
cs.enterPrevote(height, cs.Round) |
|
|
|
cs.enterPrevote(ctx, height, cs.Round) |
|
|
|
if hasTwoThirds { // this is optimisation as this will be triggered when prevote is added
|
|
|
|
cs.enterPrecommit(height, cs.Round) |
|
|
|
cs.enterPrecommit(ctx, height, cs.Round) |
|
|
|
} |
|
|
|
} else if cs.Step == cstypes.RoundStepCommit { |
|
|
|
// If we're waiting on the proposal block...
|
|
|
|
cs.tryFinalizeCommit(height) |
|
|
|
cs.tryFinalizeCommit(ctx, height) |
|
|
|
} |
|
|
|
|
|
|
|
return added, nil |
|
|
@ -1962,8 +1972,8 @@ func (cs *State) addProposalBlockPart(msg *BlockPartMessage, peerID types.NodeID |
|
|
|
} |
|
|
|
|
|
|
|
// Attempt to add the vote. if its a duplicate signature, dupeout the validator
|
|
|
|
func (cs *State) tryAddVote(vote *types.Vote, peerID types.NodeID) (bool, error) { |
|
|
|
added, err := cs.addVote(vote, peerID) |
|
|
|
func (cs *State) tryAddVote(ctx context.Context, vote *types.Vote, peerID types.NodeID) (bool, error) { |
|
|
|
added, err := cs.addVote(ctx, vote, peerID) |
|
|
|
if err != nil { |
|
|
|
// If the vote height is off, we'll just ignore it,
|
|
|
|
// But if it's a conflicting sig, add it to the cs.evpool.
|
|
|
@ -2010,7 +2020,11 @@ func (cs *State) tryAddVote(vote *types.Vote, peerID types.NodeID) (bool, error) |
|
|
|
return added, nil |
|
|
|
} |
|
|
|
|
|
|
|
func (cs *State) addVote(vote *types.Vote, peerID types.NodeID) (added bool, err error) { |
|
|
|
func (cs *State) addVote( |
|
|
|
ctx context.Context, |
|
|
|
vote *types.Vote, |
|
|
|
peerID types.NodeID, |
|
|
|
) (added bool, err error) { |
|
|
|
cs.Logger.Debug( |
|
|
|
"adding vote", |
|
|
|
"vote_height", vote.Height, |
|
|
@ -2034,7 +2048,7 @@ func (cs *State) addVote(vote *types.Vote, peerID types.NodeID) (added bool, err |
|
|
|
} |
|
|
|
|
|
|
|
cs.Logger.Debug("added vote to last precommits", "last_commit", cs.LastCommit.StringShort()) |
|
|
|
if err := cs.eventBus.PublishEventVote(types.EventDataVote{Vote: vote}); err != nil { |
|
|
|
if err := cs.eventBus.PublishEventVote(ctx, types.EventDataVote{Vote: vote}); err != nil { |
|
|
|
return added, err |
|
|
|
} |
|
|
|
|
|
|
@ -2044,7 +2058,7 @@ func (cs *State) addVote(vote *types.Vote, peerID types.NodeID) (added bool, err |
|
|
|
if cs.config.SkipTimeoutCommit && cs.LastCommit.HasAll() { |
|
|
|
// go straight to new round (skip timeout commit)
|
|
|
|
// cs.scheduleTimeout(time.Duration(0), cs.Height, 0, cstypes.RoundStepNewHeight)
|
|
|
|
cs.enterNewRound(cs.Height, 0) |
|
|
|
cs.enterNewRound(ctx, cs.Height, 0) |
|
|
|
} |
|
|
|
|
|
|
|
return |
|
|
@ -2064,7 +2078,7 @@ func (cs *State) addVote(vote *types.Vote, peerID types.NodeID) (added bool, err |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
if err := cs.eventBus.PublishEventVote(types.EventDataVote{Vote: vote}); err != nil { |
|
|
|
if err := cs.eventBus.PublishEventVote(ctx, types.EventDataVote{Vote: vote}); err != nil { |
|
|
|
return added, err |
|
|
|
} |
|
|
|
cs.evsw.FireEvent(types.EventVoteValue, vote) |
|
|
@ -2093,7 +2107,7 @@ func (cs *State) addVote(vote *types.Vote, peerID types.NodeID) (added bool, err |
|
|
|
cs.LockedBlock = nil |
|
|
|
cs.LockedBlockParts = nil |
|
|
|
|
|
|
|
if err := cs.eventBus.PublishEventUnlock(cs.RoundStateEvent()); err != nil { |
|
|
|
if err := cs.eventBus.PublishEventUnlock(ctx, cs.RoundStateEvent()); err != nil { |
|
|
|
return added, err |
|
|
|
} |
|
|
|
} |
|
|
@ -2122,7 +2136,7 @@ func (cs *State) addVote(vote *types.Vote, peerID types.NodeID) (added bool, err |
|
|
|
} |
|
|
|
|
|
|
|
cs.evsw.FireEvent(types.EventValidBlockValue, &cs.RoundState) |
|
|
|
if err := cs.eventBus.PublishEventValidBlock(cs.RoundStateEvent()); err != nil { |
|
|
|
if err := cs.eventBus.PublishEventValidBlock(ctx, cs.RoundStateEvent()); err != nil { |
|
|
|
return added, err |
|
|
|
} |
|
|
|
} |
|
|
@ -2132,20 +2146,20 @@ func (cs *State) addVote(vote *types.Vote, peerID types.NodeID) (added bool, err |
|
|
|
switch { |
|
|
|
case cs.Round < vote.Round && prevotes.HasTwoThirdsAny(): |
|
|
|
// Round-skip if there is any 2/3+ of votes ahead of us
|
|
|
|
cs.enterNewRound(height, vote.Round) |
|
|
|
cs.enterNewRound(ctx, height, vote.Round) |
|
|
|
|
|
|
|
case cs.Round == vote.Round && cstypes.RoundStepPrevote <= cs.Step: // current round
|
|
|
|
blockID, ok := prevotes.TwoThirdsMajority() |
|
|
|
if ok && (cs.isProposalComplete() || len(blockID.Hash) == 0) { |
|
|
|
cs.enterPrecommit(height, vote.Round) |
|
|
|
cs.enterPrecommit(ctx, height, vote.Round) |
|
|
|
} else if prevotes.HasTwoThirdsAny() { |
|
|
|
cs.enterPrevoteWait(height, vote.Round) |
|
|
|
cs.enterPrevoteWait(ctx, height, vote.Round) |
|
|
|
} |
|
|
|
|
|
|
|
case cs.Proposal != nil && 0 <= cs.Proposal.POLRound && cs.Proposal.POLRound == vote.Round: |
|
|
|
// If the proposal is now complete, enter prevote of cs.Round.
|
|
|
|
if cs.isProposalComplete() { |
|
|
|
cs.enterPrevote(height, cs.Round) |
|
|
|
cs.enterPrevote(ctx, height, cs.Round) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -2161,20 +2175,20 @@ func (cs *State) addVote(vote *types.Vote, peerID types.NodeID) (added bool, err |
|
|
|
blockID, ok := precommits.TwoThirdsMajority() |
|
|
|
if ok { |
|
|
|
// Executed as TwoThirdsMajority could be from a higher round
|
|
|
|
cs.enterNewRound(height, vote.Round) |
|
|
|
cs.enterPrecommit(height, vote.Round) |
|
|
|
cs.enterNewRound(ctx, height, vote.Round) |
|
|
|
cs.enterPrecommit(ctx, height, vote.Round) |
|
|
|
|
|
|
|
if len(blockID.Hash) != 0 { |
|
|
|
cs.enterCommit(height, vote.Round) |
|
|
|
cs.enterCommit(ctx, height, vote.Round) |
|
|
|
if cs.config.SkipTimeoutCommit && precommits.HasAll() { |
|
|
|
cs.enterNewRound(cs.Height, 0) |
|
|
|
cs.enterNewRound(ctx, cs.Height, 0) |
|
|
|
} |
|
|
|
} else { |
|
|
|
cs.enterPrecommitWait(height, vote.Round) |
|
|
|
cs.enterPrecommitWait(ctx, height, vote.Round) |
|
|
|
} |
|
|
|
} else if cs.Round <= vote.Round && precommits.HasTwoThirdsAny() { |
|
|
|
cs.enterNewRound(height, vote.Round) |
|
|
|
cs.enterPrecommitWait(height, vote.Round) |
|
|
|
cs.enterNewRound(ctx, height, vote.Round) |
|
|
|
cs.enterPrecommitWait(ctx, height, vote.Round) |
|
|
|
} |
|
|
|
|
|
|
|
default: |
|
|
|