func (cs *ConsensusState) scheduleTimeout(duration time.Duration, height, round agtypes.INT, step csspb.RoundStepType) {
	cs.timeoutTicker.ScheduleTimeout(timeoutInfo{
		timeoutInfoJson: timeoutInfoJson{
			Duration: duration,
			Height:   height,
			Round:    round,
			Step:     step,
		},
	})
}
Attempt to schedule a timeout (by sending timeoutInfo on the tickChan)
package consensus
import (
	"bytes"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"math/big"
	"reflect"
	"sync"
	"time"
	"unsafe"
	bc "github.com/Baptist-Publication/angine/blockchain"
	mempl "github.com/Baptist-Publication/angine/mempool"
	csspb "github.com/Baptist-Publication/angine/protos/consensus"
	pbtypes "github.com/Baptist-Publication/angine/protos/types"
	sm "github.com/Baptist-Publication/angine/state"
	agtypes "github.com/Baptist-Publication/angine/types"
	. "github.com/Baptist-Publication/chorus-module/lib/go-common"
	"github.com/Baptist-Publication/chorus-module/lib/go-crypto"
	"github.com/spf13/viper"
	"go.uber.org/zap"
)
type TimeoutParams struct {
	Propose0          agtypes.INT
	ProposeDelta      agtypes.INT
	Prevote0          agtypes.INT
	PrevoteDelta      agtypes.INT
	Precommit0        agtypes.INT
	PrecommitDelta    agtypes.INT
	Commit0           agtypes.INT
	SkipTimeoutCommit bool
}
func (tp *TimeoutParams) Propose(round agtypes.INT) time.Duration {
	return time.Duration(tp.Propose0+tp.ProposeDelta*round) * time.Millisecond
}
func (tp *TimeoutParams) Prevote(round agtypes.INT) time.Duration {
	return time.Duration(tp.Prevote0+tp.PrevoteDelta*round) * time.Millisecond
}
func (tp *TimeoutParams) Precommit(round agtypes.INT) time.Duration {
	return time.Duration(tp.Precommit0+tp.PrecommitDelta*round) * time.Millisecond
}
func (tp *TimeoutParams) Commit(t time.Time) time.Time {
	return t.Add(time.Duration(tp.Commit0) * time.Millisecond)
}
func InitTimeoutParamsFromConfig(config *viper.Viper) *TimeoutParams {
	return &TimeoutParams{
		Propose0:          agtypes.INT(config.GetInt("timeout_propose")),
		ProposeDelta:      agtypes.INT(config.GetInt("timeout_propose_delta")),
		Prevote0:          agtypes.INT(config.GetInt("timeout_prevote")),
		PrevoteDelta:      agtypes.INT(config.GetInt("timeout_prevote_delta")),
		Precommit0:        agtypes.INT(config.GetInt("timeout_precommit")),
		PrecommitDelta:    agtypes.INT(config.GetInt("timeout_precommit_delta")),
		Commit0:           agtypes.INT(config.GetInt("timeout_commit")),
		SkipTimeoutCommit: config.GetBool("skip_timeout_commit"),
	}
}
var (
	ErrInvalidProposalSignature = errors.New("Error invalid proposal signature")
	ErrInvalidProposalPOLRound  = errors.New("Error invalid proposal POL round")
	ErrAddingVote               = errors.New("Error adding vote")
	ErrVoteHeightMismatch       = errors.New("Error vote height mismatch")
)
type RoundState struct {
	Height             agtypes.INT  
	Round              agtypes.INT
	Step               csspb.RoundStepType
	StartTime          time.Time
	CommitTime         time.Time  
	Validators         *agtypes.ValidatorSet
	Proposal           *pbtypes.Proposal
	ProposalBlock      *agtypes.BlockCache
	ProposalBlockParts *agtypes.PartSet
	LockedRound        agtypes.INT
	LockedBlock        *agtypes.BlockCache
	LockedBlockParts   *agtypes.PartSet
	Votes              *HeightVoteSet
	CommitRound        agtypes.INT       
	LastCommit         *agtypes.VoteSet  
	LastValidators     *agtypes.ValidatorSet
}
func (rs *RoundState) RoundStateEvent() agtypes.EventDataRoundState {
	edrs := agtypes.EventDataRoundState{
		EventDataRoundStateJson: agtypes.EventDataRoundStateJson{
			Height:     rs.Height,
			Round:      rs.Round,
			Step:       rs.Step.CString(),
			RoundState: rs,
		},
	}
	return edrs
}
func (rs *RoundState) String() string {
	return rs.StringIndented("")
}
func (rs *RoundState) StringIndented(indent string) string {
	return fmt.Sprintf(`RoundState{
%s  H:%v R:%v S:%v
%s  StartTime:     %v
%s  CommitTime:    %v
%s  Validators:    %v
%s  Proposal:      %v
%s  ProposalBlock: %v %v
%s  LockedRound:   %v
%s  LockedBlock:   %v %v
%s  Votes:         %v
%s  LastCommit: %v
%s  LastValidators:    %v
%s}`,
		indent, rs.Height, rs.Round, rs.Step,
		indent, rs.StartTime,
		indent, rs.CommitTime,
		indent, rs.Validators.StringIndented(indent+"    "),
		indent, rs.Proposal,
		indent, rs.ProposalBlockParts.StringShort(), rs.ProposalBlock.StringShort(),
		indent, rs.LockedRound,
		indent, rs.LockedBlockParts.StringShort(), rs.LockedBlock.StringShort(),
		indent, rs.Votes.StringIndented(indent+"    "),
		indent, rs.LastCommit.StringShort(),
		indent, rs.LastValidators.StringIndented(indent+"    "),
		indent)
}
func (rs *RoundState) StringShort() string {
	return fmt.Sprintf(`RoundState{H:%v R:%v S:%v ST:%v}`,
		rs.Height, rs.Round, rs.Step, rs.StartTime)
}
var (
	msgQueueSize = 1000
	int0            agtypes.INT
	sizeOfInt       = unsafe.Sizeof(int0)
	emptyRoundLimit = 1 << (*(*uint)(unsafe.Pointer(&sizeOfInt))*8 - 2)
)
const (
	CssMsgTypeProposal  = byte(0x01)
	CssMsgTypeBlockPart = byte(0x02)
	CssMsgTypeVote      = byte(0x03)
)
func cssMsgType(msg csspb.ConsensusMsgItfc) byte {
	switch csspb.GetMessageType(msg) {
	case csspb.MsgType_Proposal:
		return CssMsgTypeProposal
	case csspb.MsgType_BlockPart:
		return CssMsgTypeBlockPart
	case csspb.MsgType_Vote:
		return CssMsgTypeVote
	}
	return 0
}
type msgInfoJson struct {
	Msg     csspb.StConsensusMsg `json:"msg"`
	PeerKey string               `json:"peer_key"`
}
type msgInfo struct {
	msgInfoJson
}
func (mi *msgInfo) GetMsg() csspb.ConsensusMsgItfc {
	return mi.Msg.ConsensusMsgItfc
}
func genMsgInfo(msg csspb.ConsensusMsgItfc, pk string) (retMsg msgInfo) {
	retMsg.Msg = csspb.StConsensusMsg{msg}
	retMsg.PeerKey = pk
	return
}
func (mi msgInfo) MarshalJSON() ([]byte, error) {
	return json.Marshal(mi.msgInfoJson)
}
func (mi *msgInfo) UnmarshalJSON(data []byte) error {
	var mij msgInfoJson
	err := json.Unmarshal(data, &mij)
	if err != nil {
		return err
	}
	mi.msgInfoJson = mij
	return nil
}
type timeoutInfo struct {
	timeoutInfoJson
}
type timeoutInfoJson struct {
	Duration time.Duration       `json:"duration"`
	Height   agtypes.INT         `json:"height"`
	Round    agtypes.INT         `json:"round"`
	Step     csspb.RoundStepType `json:"step"`
}
func (ti timeoutInfo) MarshalJSON() ([]byte, error) {
	return json.Marshal(&ti.timeoutInfoJson)
}
func (ti *timeoutInfo) UnmarshalJSON(data []byte) error {
	var tij timeoutInfoJson
	err := json.Unmarshal(data, &tij)
	if err != nil {
		return err
	}
	ti.timeoutInfoJson = tij
	return nil
}
func (ti *timeoutInfo) String() string {
	return fmt.Sprintf("%v ; %d/%d %v", ti.Duration, ti.Height, ti.Round, ti.Step)
}
type PrivValidator interface {
	GetAddress() []byte
	GetCoinbase() []byte
	SignVote(chainID string, vote *pbtypes.Vote) error
	SignProposal(chainID string, proposal *pbtypes.Proposal) error
}
type ConsensusState struct {
	BaseService
	config     *viper.Viper
	blockStore *bc.BlockStore
	mempool    *mempl.Mempool
	conR *ConsensusReactor
	privValidator PrivValidator  
	mtx sync.Mutex
	RoundState
	state *sm.State  
	peerMsgQueue     chan msgInfo    
	internalMsgQueue chan msgInfo    
	timeoutTicker    TimeoutTicker   
	timeoutParams    *TimeoutParams  
	evsw agtypes.EventSwitch
	wal        *WAL
	replayMode bool  
	nSteps int  
	decideProposal func(height, round agtypes.INT)
	doPrevote      func(height, round agtypes.INT)
	setProposal    func(proposal *pbtypes.Proposal) error
	done chan struct{}
	logger  *zap.Logger
	slogger *zap.SugaredLogger
	badvoteCollector agtypes.IBadVoteCollector
}
func NewConsensusState(logger *zap.Logger, config *viper.Viper, state *sm.State, blockStore *bc.BlockStore, mempool *mempl.Mempool) *ConsensusState {
	cs := &ConsensusState{
		config:           config,
		blockStore:       blockStore,
		mempool:          mempool,
		peerMsgQueue:     make(chan msgInfo, msgQueueSize),
		internalMsgQueue: make(chan msgInfo, msgQueueSize),
		timeoutTicker:    NewTimeoutTicker(logger),
		timeoutParams:    InitTimeoutParamsFromConfig(config),
		done:             make(chan struct{}),
		logger:           logger,
		slogger:          logger.Sugar(),
	}
	cs.decideProposal = cs.defaultDecideProposal
	cs.doPrevote = cs.defaultDoPrevote
	cs.setProposal = cs.defaultSetProposal
	cs.updateToState(state)
	cs.reconstructLastCommit(state)
	cs.BaseService = *NewBaseService(logger, "ConsensusState", cs)
	walDir := cs.config.GetString("cs_wal_dir")
	err := EnsureDir(walDir, 0700)
	if err != nil {
		logger.Error("Error ensuring ConsensusState wal dir", zap.String("error", err.Error()))
		return nil
	}
	err = cs.OpenWAL(walDir)
	if err != nil {
		logger.Error("Error loading ConsensusState wal", zap.String("error", err.Error()))
		return nil
	}
	return cs
}
func (cs *ConsensusState) SetEventSwitch(evsw agtypes.EventSwitch) {
	cs.evsw = evsw
}
func (cs *ConsensusState) BindReactor(r *ConsensusReactor) {
	cs.conR = r
}
func (cs *ConsensusState) SetValSetLoader(f agtypes.ValSetLoaderFunc) {
	if cs == nil || cs.state == nil {
		return
	}
	cs.state.SetValSetLoader(f)
}
func (cs *ConsensusState) String() string {
	return Fmt("ConsensusState")  
}
func (cs *ConsensusState) GetState() *sm.State {
	cs.mtx.Lock()
	defer cs.mtx.Unlock()
	return cs.state.Copy()
}
func (cs *ConsensusState) GetRoundState() *RoundState {
	cs.mtx.Lock()
	defer cs.mtx.Unlock()
	return cs.getRoundState()
}
func (cs *ConsensusState) getRoundState() *RoundState {
	rs := cs.RoundState  
	return &rs
}
func (cs *ConsensusState) GetTotalVotingPower() int64 {
	cs.mtx.Lock()
	defer cs.mtx.Unlock()
	return cs.Validators.TotalVotingPower()
}
func (cs *ConsensusState) GetValidators() (agtypes.INT, []*agtypes.Validator) {
	cs.mtx.Lock()
	defer cs.mtx.Unlock()
	return cs.state.LastBlockHeight, cs.state.Validators.Copy().Validators
}
func (cs *ConsensusState) SetPrivValidator(priv PrivValidator) {
	cs.mtx.Lock()
	defer cs.mtx.Unlock()
	cs.privValidator = priv
}
func (cs *ConsensusState) SetTimeoutTicker(timeoutTicker TimeoutTicker) {
	cs.mtx.Lock()
	defer cs.mtx.Unlock()
	cs.timeoutTicker = timeoutTicker
}
func (cs *ConsensusState) SetBadVoteCollector(c agtypes.IBadVoteCollector) {
	cs.badvoteCollector = c
}
func (cs *ConsensusState) LoadCommit(height agtypes.INT) *agtypes.CommitCache {
	cs.mtx.Lock()
	defer cs.mtx.Unlock()
	if height == cs.blockStore.Height() {
		return cs.blockStore.LoadSeenCommit(height)
	}
	return cs.blockStore.LoadBlockCommit(height)
}
func (cs *ConsensusState) OnStart() error {
	cs.BaseService.OnStart()
	gr, found, err := cs.wal.group.Search("#HEIGHT: ", makeHeightSearchFunc(cs.Height))
	if (err == io.EOF || !found) && cs.Step == csspb.RoundStepType_NewHeight {
		cs.logger.Warn("Height not found in wal. Writing new height", zap.Int64("height", cs.Height))
		rs := cs.RoundStateEvent()
		cs.wal.Save(&rs)
	} else if err != nil {
		return err
	}
	if gr != nil {
		gr.Close()
	}
	cs.timeoutTicker.Start()
	if err := cs.catchupReplay(cs.Height); err != nil {
		cs.logger.Error("Error on catchup replay", zap.String("error", err.Error()))
	}
	go cs.receiveRoutine(0)
	cs.scheduleRound0(cs.GetRoundState())
	return nil
}
func (cs *ConsensusState) startRoutines(maxSteps int) {
	cs.timeoutTicker.Start()
	go cs.receiveRoutine(maxSteps)
}
func (cs *ConsensusState) OnStop() {
	cs.BaseService.OnStop()
	cs.timeoutTicker.Stop()
	if cs.wal != nil && cs.IsRunning() {
		cs.wal.Wait()
	}
}
func (cs *ConsensusState) Wait() {
	<-cs.done
}
func (cs *ConsensusState) OpenWAL(walDir string) (err error) {
	cs.mtx.Lock()
	defer cs.mtx.Unlock()
	wal, err := NewWAL(cs.logger, walDir, cs.config.GetBool("cs_wal_light"))
	if err != nil {
		return err
	}
	cs.wal = wal
	return nil
}
func (cs *ConsensusState) AddVote(vote *pbtypes.Vote, peerKey string) (added bool, err error) {
	if peerKey == "" {
		cs.internalMsgQueue <- genMsgInfo(&csspb.VoteMessage{vote}, "")
	} else {
		cs.peerMsgQueue <- genMsgInfo(&csspb.VoteMessage{vote}, peerKey)
	}
	return false, nil
}
func (cs *ConsensusState) SetProposal(proposal *pbtypes.Proposal, peerKey string) error {
	if peerKey == "" {
		cs.internalMsgQueue <- genMsgInfo(&csspb.ProposalMessage{proposal}, "")
	} else {
		cs.peerMsgQueue <- genMsgInfo(&csspb.ProposalMessage{proposal}, peerKey)
	}
	return nil
}
func (cs *ConsensusState) AddProposalBlockPart(height, round agtypes.INT, part *pbtypes.Part, peerKey string) error {
	if peerKey == "" {
		cs.internalMsgQueue <- genMsgInfo(&csspb.BlockPartMessage{height, round, part}, "")
	} else {
		cs.peerMsgQueue <- genMsgInfo(&csspb.BlockPartMessage{height, round, part}, peerKey)
	}
	return nil
}
func (cs *ConsensusState) SetProposalAndBlock(proposal *pbtypes.Proposal, block *pbtypes.Block, parts *agtypes.PartSet, peerKey string) error {
	cs.SetProposal(proposal, peerKey)
	pdata := proposal.GetData()
	for i := 0; i < int(parts.Total()); i++ {
		part := parts.GetPart(i)
		cs.AddProposalBlockPart(pdata.Height, pdata.Round, part, peerKey)
	}
	return nil  
}
func (cs *ConsensusState) updateHeight(height agtypes.INT) {
	cs.Height = height
}
func (cs *ConsensusState) updateRoundStep(round agtypes.INT, step csspb.RoundStepType) {
	cs.Round = round
	cs.Step = step
}
func (cs *ConsensusState) scheduleRound0(rs *RoundState) {
	sleepDuration := rs.StartTime.Sub(time.Now())
	cs.scheduleTimeout(sleepDuration, rs.Height, 0, csspb.RoundStepType_NewHeight)
}