|
@ -223,6 +223,10 @@ func NewState( |
|
|
cs.doPrevote = cs.defaultDoPrevote |
|
|
cs.doPrevote = cs.defaultDoPrevote |
|
|
cs.setProposal = cs.defaultSetProposal |
|
|
cs.setProposal = cs.defaultSetProposal |
|
|
|
|
|
|
|
|
|
|
|
if err := cs.updateStateFromStore(ctx); err != nil { |
|
|
|
|
|
return nil, err |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// NOTE: we do not call scheduleRound0 yet, we do that upon Start()
|
|
|
// NOTE: we do not call scheduleRound0 yet, we do that upon Start()
|
|
|
cs.BaseService = *service.NewBaseService(logger, "State", cs) |
|
|
cs.BaseService = *service.NewBaseService(logger, "State", cs) |
|
|
for _, option := range options { |
|
|
for _, option := range options { |
|
@ -286,9 +290,9 @@ func (cs *State) GetRoundState() *cstypes.RoundState { |
|
|
cs.mtx.RLock() |
|
|
cs.mtx.RLock() |
|
|
defer cs.mtx.RUnlock() |
|
|
defer cs.mtx.RUnlock() |
|
|
|
|
|
|
|
|
// NOTE: this is probably be dodgy, as RoundState isn't thread
|
|
|
|
|
|
// safe as it contains a number of pointers which make the
|
|
|
|
|
|
// resulting object shared anyway
|
|
|
|
|
|
|
|
|
// NOTE: this might be dodgy, as RoundState itself isn't thread
|
|
|
|
|
|
// safe as it contains a number of pointers and is explicitly
|
|
|
|
|
|
// not thread safe.
|
|
|
rs := cs.RoundState // copy
|
|
|
rs := cs.RoundState // copy
|
|
|
return &rs |
|
|
return &rs |
|
|
} |
|
|
} |
|
@ -351,9 +355,8 @@ func (cs *State) SetPrivValidator(ctx context.Context, priv types.PrivValidator) |
|
|
// testing.
|
|
|
// testing.
|
|
|
func (cs *State) SetTimeoutTicker(timeoutTicker TimeoutTicker) { |
|
|
func (cs *State) SetTimeoutTicker(timeoutTicker TimeoutTicker) { |
|
|
cs.mtx.Lock() |
|
|
cs.mtx.Lock() |
|
|
defer cs.mtx.Unlock() |
|
|
|
|
|
cs.timeoutTicker = timeoutTicker |
|
|
cs.timeoutTicker = timeoutTicker |
|
|
|
|
|
|
|
|
|
|
|
cs.mtx.Unlock() |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// LoadCommit loads the commit for a given height.
|
|
|
// LoadCommit loads the commit for a given height.
|
|
@ -1116,6 +1119,7 @@ func (cs *State) handleTxsAvailable(ctx context.Context) { |
|
|
// NOTE: cs.StartTime was already set for height.
|
|
|
// NOTE: cs.StartTime was already set for height.
|
|
|
func (cs *State) enterNewRound(ctx context.Context, height int64, round int32) { |
|
|
func (cs *State) enterNewRound(ctx context.Context, height int64, round int32) { |
|
|
// TODO: remove panics in this function and return an error
|
|
|
// TODO: remove panics in this function and return an error
|
|
|
|
|
|
|
|
|
logger := cs.logger.With("height", height, "round", round) |
|
|
logger := cs.logger.With("height", height, "round", round) |
|
|
|
|
|
|
|
|
if cs.Height != height || round < cs.Round || (cs.Round == round && cs.Step != cstypes.RoundStepNewHeight) { |
|
|
if cs.Height != height || round < cs.Round || (cs.Round == round && cs.Step != cstypes.RoundStepNewHeight) { |
|
@ -1345,8 +1349,6 @@ func (cs *State) defaultDecideProposal(ctx context.Context, height int64, round |
|
|
// Returns true if the proposal block is complete &&
|
|
|
// Returns true if the proposal block is complete &&
|
|
|
// (if POLRound was proposed, we have +2/3 prevotes from there).
|
|
|
// (if POLRound was proposed, we have +2/3 prevotes from there).
|
|
|
func (cs *State) isProposalComplete() bool { |
|
|
func (cs *State) isProposalComplete() bool { |
|
|
cs.mtx.RLock() |
|
|
|
|
|
defer cs.mtx.RUnlock() |
|
|
|
|
|
if cs.Proposal == nil || cs.ProposalBlock == nil { |
|
|
if cs.Proposal == nil || cs.ProposalBlock == nil { |
|
|
return false |
|
|
return false |
|
|
} |
|
|
} |
|
@ -2056,27 +2058,19 @@ func (cs *State) RecordMetrics(height int64, block *types.Block) { |
|
|
func (cs *State) defaultSetProposal(proposal *types.Proposal, recvTime time.Time) error { |
|
|
func (cs *State) defaultSetProposal(proposal *types.Proposal, recvTime time.Time) error { |
|
|
// Already have one
|
|
|
// Already have one
|
|
|
// TODO: possibly catch double proposals
|
|
|
// TODO: possibly catch double proposals
|
|
|
if shouldReturn, err := func() (bool, error) { |
|
|
|
|
|
cs.mtx.RLock() |
|
|
|
|
|
defer cs.mtx.RUnlock() |
|
|
|
|
|
|
|
|
|
|
|
if cs.Proposal != nil || proposal == nil { |
|
|
|
|
|
return true, nil |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
if cs.Proposal != nil || proposal == nil { |
|
|
|
|
|
return nil |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// Does not apply
|
|
|
|
|
|
if proposal.Height != cs.Height || proposal.Round != cs.Round { |
|
|
|
|
|
return true, nil |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// Does not apply
|
|
|
|
|
|
if proposal.Height != cs.Height || proposal.Round != cs.Round { |
|
|
|
|
|
return nil |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
// Verify POLRound, which must be -1 or in range [0, proposal.Round).
|
|
|
|
|
|
if proposal.POLRound < -1 || |
|
|
|
|
|
(proposal.POLRound >= 0 && proposal.POLRound >= proposal.Round) { |
|
|
|
|
|
return true, ErrInvalidProposalPOLRound |
|
|
|
|
|
} |
|
|
|
|
|
return false, nil |
|
|
|
|
|
}(); shouldReturn { |
|
|
|
|
|
return err |
|
|
|
|
|
|
|
|
// Verify POLRound, which must be -1 or in range [0, proposal.Round).
|
|
|
|
|
|
if proposal.POLRound < -1 || |
|
|
|
|
|
(proposal.POLRound >= 0 && proposal.POLRound >= proposal.Round) { |
|
|
|
|
|
return ErrInvalidProposalPOLRound |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
p := proposal.ToProto() |
|
|
p := proposal.ToProto() |
|
@ -2088,21 +2082,15 @@ func (cs *State) defaultSetProposal(proposal *types.Proposal, recvTime time.Time |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
proposal.Signature = p.Signature |
|
|
proposal.Signature = p.Signature |
|
|
cs.mtx.Lock() |
|
|
|
|
|
cs.Proposal = proposal |
|
|
cs.Proposal = proposal |
|
|
cs.ProposalReceiveTime = recvTime |
|
|
cs.ProposalReceiveTime = recvTime |
|
|
cs.mtx.Unlock() |
|
|
|
|
|
cs.calculateProposalTimestampDifferenceMetric() |
|
|
cs.calculateProposalTimestampDifferenceMetric() |
|
|
// We don't update cs.ProposalBlockParts if it is already set.
|
|
|
// We don't update cs.ProposalBlockParts if it is already set.
|
|
|
// This happens if we're already in cstypes.RoundStepCommit or if there is a valid block in the current round.
|
|
|
// This happens if we're already in cstypes.RoundStepCommit or if there is a valid block in the current round.
|
|
|
// TODO: We can check if Proposal is for a different block as this is a sign of misbehavior!
|
|
|
// TODO: We can check if Proposal is for a different block as this is a sign of misbehavior!
|
|
|
if cs.ProposalBlockParts == nil { |
|
|
if cs.ProposalBlockParts == nil { |
|
|
cs.metrics.MarkBlockGossipStarted() |
|
|
cs.metrics.MarkBlockGossipStarted() |
|
|
func() { |
|
|
|
|
|
cs.mtx.Lock() |
|
|
|
|
|
defer cs.mtx.Unlock() |
|
|
|
|
|
cs.ProposalBlockParts = types.NewPartSetFromHeader(proposal.BlockID.PartSetHeader) |
|
|
|
|
|
}() |
|
|
|
|
|
|
|
|
cs.ProposalBlockParts = types.NewPartSetFromHeader(proposal.BlockID.PartSetHeader) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
cs.logger.Info("received proposal", "proposal", proposal) |
|
|
cs.logger.Info("received proposal", "proposal", proposal) |
|
|