github.com/arcology-network/consensus-engine@v1.9.0/consensus/reactor.go (about)

     1  package consensus
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"reflect"
     7  	"sync"
     8  	"time"
     9  
    10  	"github.com/gogo/protobuf/proto"
    11  
    12  	cstypes "github.com/arcology-network/consensus-engine/consensus/types"
    13  	"github.com/arcology-network/consensus-engine/libs/bits"
    14  	tmevents "github.com/arcology-network/consensus-engine/libs/events"
    15  	tmjson "github.com/arcology-network/consensus-engine/libs/json"
    16  	"github.com/arcology-network/consensus-engine/libs/log"
    17  	tmsync "github.com/arcology-network/consensus-engine/libs/sync"
    18  	"github.com/arcology-network/consensus-engine/monaco"
    19  	"github.com/arcology-network/consensus-engine/p2p"
    20  	tmcons "github.com/arcology-network/consensus-engine/proto/tendermint/consensus"
    21  	tmproto "github.com/arcology-network/consensus-engine/proto/tendermint/types"
    22  	sm "github.com/arcology-network/consensus-engine/state"
    23  	"github.com/arcology-network/consensus-engine/types"
    24  	tmtime "github.com/arcology-network/consensus-engine/types/time"
    25  )
    26  
    27  const (
    28  	StateChannel       = byte(0x20)
    29  	DataChannel        = byte(0x21)
    30  	VoteChannel        = byte(0x22)
    31  	VoteSetBitsChannel = byte(0x23)
    32  
    33  	maxMsgSize = 1048576 // 1MB; NOTE/TODO: keep in sync with types.PartSet sizes.
    34  
    35  	blocksToContributeToBecomeGoodPeer = 10000
    36  	votesToContributeToBecomeGoodPeer  = 10000
    37  )
    38  
    39  //-----------------------------------------------------------------------------
    40  
    41  // Reactor defines a reactor for the consensus service.
    42  type Reactor struct {
    43  	p2p.BaseReactor // BaseService + p2p.Switch
    44  
    45  	conS *State
    46  
    47  	mtx      tmsync.RWMutex
    48  	waitSync bool
    49  	eventBus *types.EventBus
    50  
    51  	Metrics *Metrics
    52  }
    53  
    54  type ReactorOption func(*Reactor)
    55  
    56  // NewReactor returns a new Reactor with the given
    57  // consensusState.
    58  func NewReactor(consensusState *State, waitSync bool, options ...ReactorOption) *Reactor {
    59  	conR := &Reactor{
    60  		conS:     consensusState,
    61  		waitSync: waitSync,
    62  		Metrics:  NopMetrics(),
    63  	}
    64  	conR.BaseReactor = *p2p.NewBaseReactor("Consensus", conR)
    65  
    66  	for _, option := range options {
    67  		option(conR)
    68  	}
    69  
    70  	return conR
    71  }
    72  
    73  func (conR *Reactor) SetBackendProxy(backend monaco.BackendProxy) {
    74  	conR.conS.SetBackendProxy(backend)
    75  }
    76  
    77  // OnStart implements BaseService by subscribing to events, which later will be
    78  // broadcasted to other peers and starting state if we're not in fast sync.
    79  func (conR *Reactor) OnStart() error {
    80  	conR.Logger.Info("Reactor ", "waitSync", conR.WaitSync())
    81  
    82  	// start routine that computes peer statistics for evaluating peer quality
    83  	go conR.peerStatsRoutine()
    84  
    85  	conR.subscribeToBroadcastEvents()
    86  
    87  	if !conR.WaitSync() {
    88  		err := conR.conS.Start()
    89  		if err != nil {
    90  			return err
    91  		}
    92  	}
    93  
    94  	return nil
    95  }
    96  
    97  // OnStop implements BaseService by unsubscribing from events and stopping
    98  // state.
    99  func (conR *Reactor) OnStop() {
   100  	conR.unsubscribeFromBroadcastEvents()
   101  	if err := conR.conS.Stop(); err != nil {
   102  		conR.Logger.Error("Error stopping consensus state", "err", err)
   103  	}
   104  	if !conR.WaitSync() {
   105  		conR.conS.Wait()
   106  	}
   107  }
   108  
   109  // SwitchToConsensus switches from fast_sync mode to consensus mode.
   110  // It resets the state, turns off fast_sync, and starts the consensus state-machine
   111  func (conR *Reactor) SwitchToConsensus(state sm.State, skipWAL bool) {
   112  	conR.Logger.Info("SwitchToConsensus")
   113  
   114  	// We have no votes, so reconstruct LastCommit from SeenCommit.
   115  	if state.LastBlockHeight > 0 {
   116  		conR.conS.reconstructLastCommit(state)
   117  	}
   118  
   119  	// NOTE: The line below causes broadcastNewRoundStepRoutine() to broadcast a
   120  	// NewRoundStepMessage.
   121  	conR.conS.updateToState(state)
   122  
   123  	conR.mtx.Lock()
   124  	conR.waitSync = false
   125  	conR.mtx.Unlock()
   126  	conR.Metrics.FastSyncing.Set(0)
   127  	conR.Metrics.StateSyncing.Set(0)
   128  
   129  	if skipWAL {
   130  		conR.conS.doWALCatchup = false
   131  	}
   132  	err := conR.conS.Start()
   133  	if err != nil {
   134  		panic(fmt.Sprintf(`Failed to start consensus state: %v
   135  
   136  conS:
   137  %+v
   138  
   139  conR:
   140  %+v`, err, conR.conS, conR))
   141  	}
   142  }
   143  
   144  // GetChannels implements Reactor
   145  func (conR *Reactor) GetChannels() []*p2p.ChannelDescriptor {
   146  	// TODO optimize
   147  	return []*p2p.ChannelDescriptor{
   148  		{
   149  			ID:                  StateChannel,
   150  			Priority:            6,
   151  			SendQueueCapacity:   100,
   152  			RecvMessageCapacity: maxMsgSize,
   153  		},
   154  		{
   155  			ID: DataChannel, // maybe split between gossiping current block and catchup stuff
   156  			// once we gossip the whole block there's nothing left to send until next height or round
   157  			Priority:            10,
   158  			SendQueueCapacity:   100,
   159  			RecvBufferCapacity:  50 * 4096,
   160  			RecvMessageCapacity: maxMsgSize,
   161  		},
   162  		{
   163  			ID:                  VoteChannel,
   164  			Priority:            7,
   165  			SendQueueCapacity:   100,
   166  			RecvBufferCapacity:  100 * 100,
   167  			RecvMessageCapacity: maxMsgSize,
   168  		},
   169  		{
   170  			ID:                  VoteSetBitsChannel,
   171  			Priority:            1,
   172  			SendQueueCapacity:   2,
   173  			RecvBufferCapacity:  1024,
   174  			RecvMessageCapacity: maxMsgSize,
   175  		},
   176  	}
   177  }
   178  
   179  // InitPeer implements Reactor by creating a state for the peer.
   180  func (conR *Reactor) InitPeer(peer p2p.Peer) p2p.Peer {
   181  	peerState := NewPeerState(peer).SetLogger(conR.Logger)
   182  	peer.Set(types.PeerStateKey, peerState)
   183  	return peer
   184  }
   185  
   186  // AddPeer implements Reactor by spawning multiple gossiping goroutines for the
   187  // peer.
   188  func (conR *Reactor) AddPeer(peer p2p.Peer) {
   189  	if !conR.IsRunning() {
   190  		return
   191  	}
   192  
   193  	peerState, ok := peer.Get(types.PeerStateKey).(*PeerState)
   194  	if !ok {
   195  		panic(fmt.Sprintf("peer %v has no state", peer))
   196  	}
   197  	// Begin routines for this peer.
   198  	go conR.gossipDataRoutine(peer, peerState)
   199  	go conR.gossipVotesRoutine(peer, peerState)
   200  	go conR.queryMaj23Routine(peer, peerState)
   201  
   202  	// Send our state to peer.
   203  	// If we're fast_syncing, broadcast a RoundStepMessage later upon SwitchToConsensus().
   204  	if !conR.WaitSync() {
   205  		conR.sendNewRoundStepMessage(peer)
   206  	}
   207  }
   208  
   209  // RemovePeer is a noop.
   210  func (conR *Reactor) RemovePeer(peer p2p.Peer, reason interface{}) {
   211  	if !conR.IsRunning() {
   212  		return
   213  	}
   214  	// TODO
   215  	// ps, ok := peer.Get(PeerStateKey).(*PeerState)
   216  	// if !ok {
   217  	// 	panic(fmt.Sprintf("Peer %v has no state", peer))
   218  	// }
   219  	// ps.Disconnect()
   220  }
   221  
   222  // Receive implements Reactor
   223  // NOTE: We process these messages even when we're fast_syncing.
   224  // Messages affect either a peer state or the consensus state.
   225  // Peer state updates can happen in parallel, but processing of
   226  // proposals, block parts, and votes are ordered by the receiveRoutine
   227  // NOTE: blocks on consensus state for proposals, block parts, and votes
   228  func (conR *Reactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) {
   229  	if !conR.IsRunning() {
   230  		conR.Logger.Debug("Receive", "src", src, "chId", chID, "bytes", msgBytes)
   231  		return
   232  	}
   233  
   234  	msg, err := decodeMsg(msgBytes)
   235  	if err != nil {
   236  		conR.Logger.Error("Error decoding message", "src", src, "chId", chID, "err", err)
   237  		conR.Switch.StopPeerForError(src, err)
   238  		return
   239  	}
   240  
   241  	if err = msg.ValidateBasic(); err != nil {
   242  		conR.Logger.Error("Peer sent us invalid msg", "peer", src, "msg", msg, "err", err)
   243  		conR.Switch.StopPeerForError(src, err)
   244  		return
   245  	}
   246  
   247  	conR.Logger.Debug("Receive", "src", src, "chId", chID, "msg", msg)
   248  
   249  	// Get peer states
   250  	ps, ok := src.Get(types.PeerStateKey).(*PeerState)
   251  	if !ok {
   252  		panic(fmt.Sprintf("Peer %v has no state", src))
   253  	}
   254  
   255  	switch chID {
   256  	case StateChannel:
   257  		switch msg := msg.(type) {
   258  		case *NewRoundStepMessage:
   259  			conR.conS.mtx.Lock()
   260  			initialHeight := conR.conS.state.InitialHeight
   261  			conR.conS.mtx.Unlock()
   262  			if err = msg.ValidateHeight(initialHeight); err != nil {
   263  				conR.Logger.Error("Peer sent us invalid msg", "peer", src, "msg", msg, "err", err)
   264  				conR.Switch.StopPeerForError(src, err)
   265  				return
   266  			}
   267  			ps.ApplyNewRoundStepMessage(msg)
   268  		case *NewValidBlockMessage:
   269  			ps.ApplyNewValidBlockMessage(msg)
   270  		case *HasVoteMessage:
   271  			ps.ApplyHasVoteMessage(msg)
   272  		case *VoteSetMaj23Message:
   273  			cs := conR.conS
   274  			cs.mtx.Lock()
   275  			height, votes := cs.Height, cs.Votes
   276  			cs.mtx.Unlock()
   277  			if height != msg.Height {
   278  				return
   279  			}
   280  			// Peer claims to have a maj23 for some BlockID at H,R,S,
   281  			err := votes.SetPeerMaj23(msg.Round, msg.Type, ps.peer.ID(), msg.BlockID)
   282  			if err != nil {
   283  				conR.Switch.StopPeerForError(src, err)
   284  				return
   285  			}
   286  			// Respond with a VoteSetBitsMessage showing which votes we have.
   287  			// (and consequently shows which we don't have)
   288  			var ourVotes *bits.BitArray
   289  			switch msg.Type {
   290  			case tmproto.PrevoteType:
   291  				ourVotes = votes.Prevotes(msg.Round).BitArrayByBlockID(msg.BlockID)
   292  			case tmproto.PrecommitType:
   293  				ourVotes = votes.Precommits(msg.Round).BitArrayByBlockID(msg.BlockID)
   294  			default:
   295  				panic("Bad VoteSetBitsMessage field Type. Forgot to add a check in ValidateBasic?")
   296  			}
   297  			src.TrySend(VoteSetBitsChannel, MustEncode(&VoteSetBitsMessage{
   298  				Height:  msg.Height,
   299  				Round:   msg.Round,
   300  				Type:    msg.Type,
   301  				BlockID: msg.BlockID,
   302  				Votes:   ourVotes,
   303  			}))
   304  		default:
   305  			conR.Logger.Error(fmt.Sprintf("Unknown message type %v", reflect.TypeOf(msg)))
   306  		}
   307  
   308  	case DataChannel:
   309  		if conR.WaitSync() {
   310  			conR.Logger.Info("Ignoring message received during sync", "msg", msg)
   311  			return
   312  		}
   313  		switch msg := msg.(type) {
   314  		case *ProposalMessage:
   315  			ps.SetHasProposal(msg.Proposal)
   316  			conR.conS.peerMsgQueue <- msgInfo{msg, src.ID()}
   317  		case *ProposalPOLMessage:
   318  			ps.ApplyProposalPOLMessage(msg)
   319  		case *BlockPartMessage:
   320  			ps.SetHasProposalBlockPart(msg.Height, msg.Round, int(msg.Part.Index))
   321  			conR.Metrics.BlockParts.With("peer_id", string(src.ID())).Add(1)
   322  			conR.conS.peerMsgQueue <- msgInfo{msg, src.ID()}
   323  		default:
   324  			conR.Logger.Error(fmt.Sprintf("Unknown message type %v", reflect.TypeOf(msg)))
   325  		}
   326  
   327  	case VoteChannel:
   328  		if conR.WaitSync() {
   329  			conR.Logger.Info("Ignoring message received during sync", "msg", msg)
   330  			return
   331  		}
   332  		switch msg := msg.(type) {
   333  		case *VoteMessage:
   334  			cs := conR.conS
   335  			cs.mtx.RLock()
   336  			height, valSize, lastCommitSize := cs.Height, cs.Validators.Size(), cs.LastCommit.Size()
   337  			cs.mtx.RUnlock()
   338  			ps.EnsureVoteBitArrays(height, valSize)
   339  			ps.EnsureVoteBitArrays(height-1, lastCommitSize)
   340  			ps.SetHasVote(msg.Vote)
   341  
   342  			cs.peerMsgQueue <- msgInfo{msg, src.ID()}
   343  
   344  		default:
   345  			// don't punish (leave room for soft upgrades)
   346  			conR.Logger.Error(fmt.Sprintf("Unknown message type %v", reflect.TypeOf(msg)))
   347  		}
   348  
   349  	case VoteSetBitsChannel:
   350  		if conR.WaitSync() {
   351  			conR.Logger.Info("Ignoring message received during sync", "msg", msg)
   352  			return
   353  		}
   354  		switch msg := msg.(type) {
   355  		case *VoteSetBitsMessage:
   356  			cs := conR.conS
   357  			cs.mtx.Lock()
   358  			height, votes := cs.Height, cs.Votes
   359  			cs.mtx.Unlock()
   360  
   361  			if height == msg.Height {
   362  				var ourVotes *bits.BitArray
   363  				switch msg.Type {
   364  				case tmproto.PrevoteType:
   365  					ourVotes = votes.Prevotes(msg.Round).BitArrayByBlockID(msg.BlockID)
   366  				case tmproto.PrecommitType:
   367  					ourVotes = votes.Precommits(msg.Round).BitArrayByBlockID(msg.BlockID)
   368  				default:
   369  					panic("Bad VoteSetBitsMessage field Type. Forgot to add a check in ValidateBasic?")
   370  				}
   371  				ps.ApplyVoteSetBitsMessage(msg, ourVotes)
   372  			} else {
   373  				ps.ApplyVoteSetBitsMessage(msg, nil)
   374  			}
   375  		default:
   376  			// don't punish (leave room for soft upgrades)
   377  			conR.Logger.Error(fmt.Sprintf("Unknown message type %v", reflect.TypeOf(msg)))
   378  		}
   379  
   380  	default:
   381  		conR.Logger.Error(fmt.Sprintf("Unknown chId %X", chID))
   382  	}
   383  }
   384  
   385  // SetEventBus sets event bus.
   386  func (conR *Reactor) SetEventBus(b *types.EventBus) {
   387  	conR.eventBus = b
   388  	conR.conS.SetEventBus(b)
   389  }
   390  
   391  // WaitSync returns whether the consensus reactor is waiting for state/fast sync.
   392  func (conR *Reactor) WaitSync() bool {
   393  	conR.mtx.RLock()
   394  	defer conR.mtx.RUnlock()
   395  	return conR.waitSync
   396  }
   397  
   398  //--------------------------------------
   399  
   400  // subscribeToBroadcastEvents subscribes for new round steps and votes
   401  // using internal pubsub defined on state to broadcast
   402  // them to peers upon receiving.
   403  func (conR *Reactor) subscribeToBroadcastEvents() {
   404  	const subscriber = "consensus-reactor"
   405  	if err := conR.conS.evsw.AddListenerForEvent(subscriber, types.EventNewRoundStep,
   406  		func(data tmevents.EventData) {
   407  			conR.broadcastNewRoundStepMessage(data.(*cstypes.RoundState))
   408  		}); err != nil {
   409  		conR.Logger.Error("Error adding listener for events", "err", err)
   410  	}
   411  
   412  	if err := conR.conS.evsw.AddListenerForEvent(subscriber, types.EventValidBlock,
   413  		func(data tmevents.EventData) {
   414  			conR.broadcastNewValidBlockMessage(data.(*cstypes.RoundState))
   415  		}); err != nil {
   416  		conR.Logger.Error("Error adding listener for events", "err", err)
   417  	}
   418  
   419  	if err := conR.conS.evsw.AddListenerForEvent(subscriber, types.EventVote,
   420  		func(data tmevents.EventData) {
   421  			conR.broadcastHasVoteMessage(data.(*types.Vote))
   422  		}); err != nil {
   423  		conR.Logger.Error("Error adding listener for events", "err", err)
   424  	}
   425  
   426  }
   427  
   428  func (conR *Reactor) unsubscribeFromBroadcastEvents() {
   429  	const subscriber = "consensus-reactor"
   430  	conR.conS.evsw.RemoveListener(subscriber)
   431  }
   432  
   433  func (conR *Reactor) broadcastNewRoundStepMessage(rs *cstypes.RoundState) {
   434  	nrsMsg := makeRoundStepMessage(rs)
   435  	conR.Switch.Broadcast(StateChannel, MustEncode(nrsMsg))
   436  }
   437  
   438  func (conR *Reactor) broadcastNewValidBlockMessage(rs *cstypes.RoundState) {
   439  	csMsg := &NewValidBlockMessage{
   440  		Height:             rs.Height,
   441  		Round:              rs.Round,
   442  		BlockPartSetHeader: rs.ProposalBlockParts.Header(),
   443  		BlockParts:         rs.ProposalBlockParts.BitArray(),
   444  		IsCommit:           rs.Step == cstypes.RoundStepCommit,
   445  	}
   446  	conR.Switch.Broadcast(StateChannel, MustEncode(csMsg))
   447  }
   448  
   449  // Broadcasts HasVoteMessage to peers that care.
   450  func (conR *Reactor) broadcastHasVoteMessage(vote *types.Vote) {
   451  	msg := &HasVoteMessage{
   452  		Height: vote.Height,
   453  		Round:  vote.Round,
   454  		Type:   vote.Type,
   455  		Index:  vote.ValidatorIndex,
   456  	}
   457  	conR.Switch.Broadcast(StateChannel, MustEncode(msg))
   458  	/*
   459  		// TODO: Make this broadcast more selective.
   460  		for _, peer := range conR.Switch.Peers().List() {
   461  			ps, ok := peer.Get(PeerStateKey).(*PeerState)
   462  			if !ok {
   463  				panic(fmt.Sprintf("Peer %v has no state", peer))
   464  			}
   465  			prs := ps.GetRoundState()
   466  			if prs.Height == vote.Height {
   467  				// TODO: Also filter on round?
   468  				peer.TrySend(StateChannel, struct{ ConsensusMessage }{msg})
   469  			} else {
   470  				// Height doesn't match
   471  				// TODO: check a field, maybe CatchupCommitRound?
   472  				// TODO: But that requires changing the struct field comment.
   473  			}
   474  		}
   475  	*/
   476  }
   477  
   478  func makeRoundStepMessage(rs *cstypes.RoundState) (nrsMsg *NewRoundStepMessage) {
   479  	nrsMsg = &NewRoundStepMessage{
   480  		Height:                rs.Height,
   481  		Round:                 rs.Round,
   482  		Step:                  rs.Step,
   483  		SecondsSinceStartTime: int64(time.Since(rs.StartTime).Seconds()),
   484  		LastCommitRound:       rs.LastCommit.GetRound(),
   485  	}
   486  	return
   487  }
   488  
   489  func (conR *Reactor) sendNewRoundStepMessage(peer p2p.Peer) {
   490  	rs := conR.conS.GetRoundState()
   491  	nrsMsg := makeRoundStepMessage(rs)
   492  	peer.Send(StateChannel, MustEncode(nrsMsg))
   493  }
   494  
   495  func (conR *Reactor) gossipDataRoutine(peer p2p.Peer, ps *PeerState) {
   496  	logger := conR.Logger.With("peer", peer)
   497  
   498  OUTER_LOOP:
   499  	for {
   500  		// Manage disconnects from self or peer.
   501  		if !peer.IsRunning() || !conR.IsRunning() {
   502  			logger.Info("Stopping gossipDataRoutine for peer")
   503  			return
   504  		}
   505  		rs := conR.conS.GetRoundState()
   506  		prs := ps.GetRoundState()
   507  
   508  		// Send proposal Block parts?
   509  		if rs.ProposalBlockParts.HasHeader(prs.ProposalBlockPartSetHeader) {
   510  			if index, ok := rs.ProposalBlockParts.BitArray().Sub(prs.ProposalBlockParts.Copy()).PickRandom(); ok {
   511  				part := rs.ProposalBlockParts.GetPart(index)
   512  				msg := &BlockPartMessage{
   513  					Height: rs.Height, // This tells peer that this part applies to us.
   514  					Round:  rs.Round,  // This tells peer that this part applies to us.
   515  					Part:   part,
   516  				}
   517  				logger.Debug("Sending block part", "height", prs.Height, "round", prs.Round)
   518  				if peer.Send(DataChannel, MustEncode(msg)) {
   519  					ps.SetHasProposalBlockPart(prs.Height, prs.Round, index)
   520  				}
   521  				continue OUTER_LOOP
   522  			}
   523  		}
   524  
   525  		// If the peer is on a previous height that we have, help catch up.
   526  		blockStoreBase := conR.conS.blockStore.Base()
   527  		if blockStoreBase > 0 && 0 < prs.Height && prs.Height < rs.Height && prs.Height >= blockStoreBase {
   528  			heightLogger := logger.With("height", prs.Height)
   529  
   530  			// if we never received the commit message from the peer, the block parts wont be initialized
   531  			if prs.ProposalBlockParts == nil {
   532  				blockMeta := conR.conS.blockStore.LoadBlockMeta(prs.Height)
   533  				if blockMeta == nil {
   534  					heightLogger.Error("Failed to load block meta",
   535  						"blockstoreBase", blockStoreBase, "blockstoreHeight", conR.conS.blockStore.Height())
   536  					time.Sleep(conR.conS.config.PeerGossipSleepDuration)
   537  				} else {
   538  					ps.InitProposalBlockParts(blockMeta.BlockID.PartSetHeader)
   539  				}
   540  				// continue the loop since prs is a copy and not effected by this initialization
   541  				continue OUTER_LOOP
   542  			}
   543  			conR.gossipDataForCatchup(heightLogger, rs, prs, ps, peer)
   544  			continue OUTER_LOOP
   545  		}
   546  
   547  		// If height and round don't match, sleep.
   548  		if (rs.Height != prs.Height) || (rs.Round != prs.Round) {
   549  			// logger.Info("Peer Height|Round mismatch, sleeping",
   550  			// "peerHeight", prs.Height, "peerRound", prs.Round, "peer", peer)
   551  			time.Sleep(conR.conS.config.PeerGossipSleepDuration)
   552  			continue OUTER_LOOP
   553  		}
   554  
   555  		// By here, height and round match.
   556  		// Proposal block parts were already matched and sent if any were wanted.
   557  		// (These can match on hash so the round doesn't matter)
   558  		// Now consider sending other things, like the Proposal itself.
   559  
   560  		// Send Proposal && ProposalPOL BitArray?
   561  		if rs.Proposal != nil && !prs.Proposal {
   562  			// Proposal: share the proposal metadata with peer.
   563  			{
   564  				msg := &ProposalMessage{Proposal: rs.Proposal}
   565  				logger.Debug("Sending proposal", "height", prs.Height, "round", prs.Round)
   566  				if peer.Send(DataChannel, MustEncode(msg)) {
   567  					// NOTE[ZM]: A peer might have received different proposal msg so this Proposal msg will be rejected!
   568  					ps.SetHasProposal(rs.Proposal)
   569  				}
   570  			}
   571  			// ProposalPOL: lets peer know which POL votes we have so far.
   572  			// Peer must receive ProposalMessage first.
   573  			// rs.Proposal was validated, so rs.Proposal.POLRound <= rs.Round,
   574  			// so we definitely have rs.Votes.Prevotes(rs.Proposal.POLRound).
   575  			if 0 <= rs.Proposal.POLRound {
   576  				msg := &ProposalPOLMessage{
   577  					Height:           rs.Height,
   578  					ProposalPOLRound: rs.Proposal.POLRound,
   579  					ProposalPOL:      rs.Votes.Prevotes(rs.Proposal.POLRound).BitArray(),
   580  				}
   581  				logger.Debug("Sending POL", "height", prs.Height, "round", prs.Round)
   582  				peer.Send(DataChannel, MustEncode(msg))
   583  			}
   584  			continue OUTER_LOOP
   585  		}
   586  
   587  		// Nothing to do. Sleep.
   588  		time.Sleep(conR.conS.config.PeerGossipSleepDuration)
   589  		continue OUTER_LOOP
   590  	}
   591  }
   592  
   593  func (conR *Reactor) gossipDataForCatchup(logger log.Logger, rs *cstypes.RoundState,
   594  	prs *cstypes.PeerRoundState, ps *PeerState, peer p2p.Peer) {
   595  
   596  	if index, ok := prs.ProposalBlockParts.Not().PickRandom(); ok {
   597  		// Ensure that the peer's PartSetHeader is correct
   598  		blockMeta := conR.conS.blockStore.LoadBlockMeta(prs.Height)
   599  		if blockMeta == nil {
   600  			logger.Error("Failed to load block meta", "ourHeight", rs.Height,
   601  				"blockstoreBase", conR.conS.blockStore.Base(), "blockstoreHeight", conR.conS.blockStore.Height())
   602  			time.Sleep(conR.conS.config.PeerGossipSleepDuration)
   603  			return
   604  		} else if !blockMeta.BlockID.PartSetHeader.Equals(prs.ProposalBlockPartSetHeader) {
   605  			logger.Info("Peer ProposalBlockPartSetHeader mismatch, sleeping",
   606  				"blockPartSetHeader", blockMeta.BlockID.PartSetHeader, "peerBlockPartSetHeader", prs.ProposalBlockPartSetHeader)
   607  			time.Sleep(conR.conS.config.PeerGossipSleepDuration)
   608  			return
   609  		}
   610  		// Load the part
   611  		part := conR.conS.blockStore.LoadBlockPart(prs.Height, index)
   612  		if part == nil {
   613  			logger.Error("Could not load part", "index", index,
   614  				"blockPartSetHeader", blockMeta.BlockID.PartSetHeader, "peerBlockPartSetHeader", prs.ProposalBlockPartSetHeader)
   615  			time.Sleep(conR.conS.config.PeerGossipSleepDuration)
   616  			return
   617  		}
   618  		// Send the part
   619  		msg := &BlockPartMessage{
   620  			Height: prs.Height, // Not our height, so it doesn't matter.
   621  			Round:  prs.Round,  // Not our height, so it doesn't matter.
   622  			Part:   part,
   623  		}
   624  		logger.Debug("Sending block part for catchup", "round", prs.Round, "index", index)
   625  		if peer.Send(DataChannel, MustEncode(msg)) {
   626  			ps.SetHasProposalBlockPart(prs.Height, prs.Round, index)
   627  		} else {
   628  			logger.Debug("Sending block part for catchup failed")
   629  		}
   630  		return
   631  	}
   632  	//  logger.Info("No parts to send in catch-up, sleeping")
   633  	time.Sleep(conR.conS.config.PeerGossipSleepDuration)
   634  }
   635  
   636  func (conR *Reactor) gossipVotesRoutine(peer p2p.Peer, ps *PeerState) {
   637  	logger := conR.Logger.With("peer", peer)
   638  
   639  	// Simple hack to throttle logs upon sleep.
   640  	var sleeping = 0
   641  
   642  OUTER_LOOP:
   643  	for {
   644  		// Manage disconnects from self or peer.
   645  		if !peer.IsRunning() || !conR.IsRunning() {
   646  			logger.Info("Stopping gossipVotesRoutine for peer")
   647  			return
   648  		}
   649  		rs := conR.conS.GetRoundStateCopy()
   650  		prs := ps.GetRoundState()
   651  
   652  		switch sleeping {
   653  		case 1: // First sleep
   654  			sleeping = 2
   655  		case 2: // No more sleep
   656  			sleeping = 0
   657  		}
   658  
   659  		// logger.Debug("gossipVotesRoutine", "rsHeight", rs.Height, "rsRound", rs.Round,
   660  		// 	"prsHeight", prs.Height, "prsRound", prs.Round, "prsStep", prs.Step)
   661  
   662  		// If height matches, then send LastCommit, Prevotes, Precommits.
   663  		if rs.Height == prs.Height {
   664  			heightLogger := logger.With("height", prs.Height)
   665  			if conR.gossipVotesForHeight(heightLogger, rs, prs, ps) {
   666  				continue OUTER_LOOP
   667  			}
   668  		}
   669  
   670  		// Special catchup logic.
   671  		// If peer is lagging by height 1, send LastCommit.
   672  		if prs.Height != 0 && rs.Height == prs.Height+1 {
   673  			if ps.PickSendVote(rs.LastCommit) {
   674  				logger.Debug("Picked rs.LastCommit to send", "height", prs.Height)
   675  				continue OUTER_LOOP
   676  			}
   677  		}
   678  
   679  		// Catchup logic
   680  		// If peer is lagging by more than 1, send Commit.
   681  		blockStoreBase := conR.conS.blockStore.Base()
   682  		if blockStoreBase > 0 && prs.Height != 0 && rs.Height >= prs.Height+2 && prs.Height >= blockStoreBase {
   683  			// Load the block commit for prs.Height,
   684  			// which contains precommit signatures for prs.Height.
   685  			if commit := conR.conS.blockStore.LoadBlockCommit(prs.Height); commit != nil {
   686  				if ps.PickSendVote(commit) {
   687  					logger.Debug("Picked Catchup commit to send", "height", prs.Height)
   688  					continue OUTER_LOOP
   689  				}
   690  			}
   691  		}
   692  
   693  		if sleeping == 0 {
   694  			// We sent nothing. Sleep...
   695  			sleeping = 1
   696  			logger.Debug("No votes to send, sleeping", "rs.Height", rs.Height, "prs.Height", prs.Height,
   697  				"localPV", rs.Votes.Prevotes(rs.Round).BitArray(), "peerPV", prs.Prevotes,
   698  				"localPC", rs.Votes.Precommits(rs.Round).BitArray(), "peerPC", prs.Precommits)
   699  		} else if sleeping == 2 {
   700  			// Continued sleep...
   701  			sleeping = 1
   702  		}
   703  
   704  		time.Sleep(conR.conS.config.PeerGossipSleepDuration)
   705  		continue OUTER_LOOP
   706  	}
   707  }
   708  
   709  func (conR *Reactor) gossipVotesForHeight(
   710  	logger log.Logger,
   711  	rs *cstypes.RoundState,
   712  	prs *cstypes.PeerRoundState,
   713  	ps *PeerState,
   714  ) bool {
   715  
   716  	// If there are lastCommits to send...
   717  	if prs.Step == cstypes.RoundStepNewHeight {
   718  		if ps.PickSendVote(rs.LastCommit) {
   719  			logger.Debug("Picked rs.LastCommit to send")
   720  			return true
   721  		}
   722  	}
   723  	// If there are POL prevotes to send...
   724  	if prs.Step <= cstypes.RoundStepPropose && prs.Round != -1 && prs.Round <= rs.Round && prs.ProposalPOLRound != -1 {
   725  		if polPrevotes := rs.Votes.Prevotes(prs.ProposalPOLRound); polPrevotes != nil {
   726  			if ps.PickSendVote(polPrevotes) {
   727  				logger.Debug("Picked rs.Prevotes(prs.ProposalPOLRound) to send",
   728  					"round", prs.ProposalPOLRound)
   729  				return true
   730  			}
   731  		}
   732  	}
   733  	// If there are prevotes to send...
   734  	if prs.Step <= cstypes.RoundStepPrevoteWait && prs.Round != -1 && prs.Round <= rs.Round {
   735  		if ps.PickSendVote(rs.Votes.Prevotes(prs.Round)) {
   736  			logger.Debug("Picked rs.Prevotes(prs.Round) to send", "round", prs.Round)
   737  			return true
   738  		}
   739  	}
   740  	// If there are precommits to send...
   741  	if prs.Step <= cstypes.RoundStepPrecommitWait && prs.Round != -1 && prs.Round <= rs.Round {
   742  		// precommits := rs.Votes.Precommits(prs.Round)
   743  		// logger.Debug("If there are precommits to send...", "precommits", precommits.String())
   744  		if ps.PickSendVote(rs.Votes.Precommits(prs.Round)) {
   745  			// if ps.PickSendVote(precommits) {
   746  			logger.Debug("Picked rs.Precommits(prs.Round) to send", "round", prs.Round)
   747  			return true
   748  		}
   749  	}
   750  	// If there are prevotes to send...Needed because of validBlock mechanism
   751  	if prs.Round != -1 && prs.Round <= rs.Round {
   752  		if ps.PickSendVote(rs.Votes.Prevotes(prs.Round)) {
   753  			logger.Debug("Picked rs.Prevotes(prs.Round) to send", "round", prs.Round)
   754  			return true
   755  		}
   756  	}
   757  	// If there are POLPrevotes to send...
   758  	if prs.ProposalPOLRound != -1 {
   759  		if polPrevotes := rs.Votes.Prevotes(prs.ProposalPOLRound); polPrevotes != nil {
   760  			if ps.PickSendVote(polPrevotes) {
   761  				logger.Debug("Picked rs.Prevotes(prs.ProposalPOLRound) to send",
   762  					"round", prs.ProposalPOLRound)
   763  				return true
   764  			}
   765  		}
   766  	}
   767  
   768  	return false
   769  }
   770  
   771  // NOTE: `queryMaj23Routine` has a simple crude design since it only comes
   772  // into play for liveness when there's a signature DDoS attack happening.
   773  func (conR *Reactor) queryMaj23Routine(peer p2p.Peer, ps *PeerState) {
   774  	logger := conR.Logger.With("peer", peer)
   775  
   776  OUTER_LOOP:
   777  	for {
   778  		// Manage disconnects from self or peer.
   779  		if !peer.IsRunning() || !conR.IsRunning() {
   780  			logger.Info("Stopping queryMaj23Routine for peer")
   781  			return
   782  		}
   783  
   784  		// Maybe send Height/Round/Prevotes
   785  		{
   786  			rs := conR.conS.GetRoundState()
   787  			prs := ps.GetRoundState()
   788  			if rs.Height == prs.Height {
   789  				if maj23, ok := rs.Votes.Prevotes(prs.Round).TwoThirdsMajority(); ok {
   790  					peer.TrySend(StateChannel, MustEncode(&VoteSetMaj23Message{
   791  						Height:  prs.Height,
   792  						Round:   prs.Round,
   793  						Type:    tmproto.PrevoteType,
   794  						BlockID: maj23,
   795  					}))
   796  					time.Sleep(conR.conS.config.PeerQueryMaj23SleepDuration)
   797  				}
   798  			}
   799  		}
   800  
   801  		// Maybe send Height/Round/Precommits
   802  		{
   803  			rs := conR.conS.GetRoundState()
   804  			prs := ps.GetRoundState()
   805  			if rs.Height == prs.Height {
   806  				if maj23, ok := rs.Votes.Precommits(prs.Round).TwoThirdsMajority(); ok {
   807  					peer.TrySend(StateChannel, MustEncode(&VoteSetMaj23Message{
   808  						Height:  prs.Height,
   809  						Round:   prs.Round,
   810  						Type:    tmproto.PrecommitType,
   811  						BlockID: maj23,
   812  					}))
   813  					time.Sleep(conR.conS.config.PeerQueryMaj23SleepDuration)
   814  				}
   815  			}
   816  		}
   817  
   818  		// Maybe send Height/Round/ProposalPOL
   819  		{
   820  			rs := conR.conS.GetRoundState()
   821  			prs := ps.GetRoundState()
   822  			if rs.Height == prs.Height && prs.ProposalPOLRound >= 0 {
   823  				if maj23, ok := rs.Votes.Prevotes(prs.ProposalPOLRound).TwoThirdsMajority(); ok {
   824  					peer.TrySend(StateChannel, MustEncode(&VoteSetMaj23Message{
   825  						Height:  prs.Height,
   826  						Round:   prs.ProposalPOLRound,
   827  						Type:    tmproto.PrevoteType,
   828  						BlockID: maj23,
   829  					}))
   830  					time.Sleep(conR.conS.config.PeerQueryMaj23SleepDuration)
   831  				}
   832  			}
   833  		}
   834  
   835  		// Little point sending LastCommitRound/LastCommit,
   836  		// These are fleeting and non-blocking.
   837  
   838  		// Maybe send Height/CatchupCommitRound/CatchupCommit.
   839  		{
   840  			prs := ps.GetRoundState()
   841  			if prs.CatchupCommitRound != -1 && prs.Height > 0 && prs.Height <= conR.conS.blockStore.Height() &&
   842  				prs.Height >= conR.conS.blockStore.Base() {
   843  				if commit := conR.conS.LoadCommit(prs.Height); commit != nil {
   844  					peer.TrySend(StateChannel, MustEncode(&VoteSetMaj23Message{
   845  						Height:  prs.Height,
   846  						Round:   commit.Round,
   847  						Type:    tmproto.PrecommitType,
   848  						BlockID: commit.BlockID,
   849  					}))
   850  					time.Sleep(conR.conS.config.PeerQueryMaj23SleepDuration)
   851  				}
   852  			}
   853  		}
   854  
   855  		time.Sleep(conR.conS.config.PeerQueryMaj23SleepDuration)
   856  
   857  		continue OUTER_LOOP
   858  	}
   859  }
   860  
   861  func (conR *Reactor) peerStatsRoutine() {
   862  	for {
   863  		if !conR.IsRunning() {
   864  			conR.Logger.Info("Stopping peerStatsRoutine")
   865  			return
   866  		}
   867  
   868  		select {
   869  		case msg := <-conR.conS.statsMsgQueue:
   870  			// Get peer
   871  			peer := conR.Switch.Peers().Get(msg.PeerID)
   872  			if peer == nil {
   873  				conR.Logger.Debug("Attempt to update stats for non-existent peer",
   874  					"peer", msg.PeerID)
   875  				continue
   876  			}
   877  			// Get peer state
   878  			ps, ok := peer.Get(types.PeerStateKey).(*PeerState)
   879  			if !ok {
   880  				panic(fmt.Sprintf("Peer %v has no state", peer))
   881  			}
   882  			switch msg.Msg.(type) {
   883  			case *VoteMessage:
   884  				if numVotes := ps.RecordVote(); numVotes%votesToContributeToBecomeGoodPeer == 0 {
   885  					conR.Switch.MarkPeerAsGood(peer)
   886  				}
   887  			case *BlockPartMessage:
   888  				if numParts := ps.RecordBlockPart(); numParts%blocksToContributeToBecomeGoodPeer == 0 {
   889  					conR.Switch.MarkPeerAsGood(peer)
   890  				}
   891  			}
   892  		case <-conR.conS.Quit():
   893  			return
   894  
   895  		case <-conR.Quit():
   896  			return
   897  		}
   898  	}
   899  }
   900  
   901  // String returns a string representation of the Reactor.
   902  // NOTE: For now, it is just a hard-coded string to avoid accessing unprotected shared variables.
   903  // TODO: improve!
   904  func (conR *Reactor) String() string {
   905  	// better not to access shared variables
   906  	return "ConsensusReactor" // conR.StringIndented("")
   907  }
   908  
   909  // StringIndented returns an indented string representation of the Reactor
   910  func (conR *Reactor) StringIndented(indent string) string {
   911  	s := "ConsensusReactor{\n"
   912  	s += indent + "  " + conR.conS.StringIndented(indent+"  ") + "\n"
   913  	for _, peer := range conR.Switch.Peers().List() {
   914  		ps, ok := peer.Get(types.PeerStateKey).(*PeerState)
   915  		if !ok {
   916  			panic(fmt.Sprintf("Peer %v has no state", peer))
   917  		}
   918  		s += indent + "  " + ps.StringIndented(indent+"  ") + "\n"
   919  	}
   920  	s += indent + "}"
   921  	return s
   922  }
   923  
   924  // ReactorMetrics sets the metrics
   925  func ReactorMetrics(metrics *Metrics) ReactorOption {
   926  	return func(conR *Reactor) { conR.Metrics = metrics }
   927  }
   928  
   929  //-----------------------------------------------------------------------------
   930  
   931  var (
   932  	ErrPeerStateHeightRegression = errors.New("error peer state height regression")
   933  	ErrPeerStateInvalidStartTime = errors.New("error peer state invalid startTime")
   934  )
   935  
   936  // PeerState contains the known state of a peer, including its connection and
   937  // threadsafe access to its PeerRoundState.
   938  // NOTE: THIS GETS DUMPED WITH rpc/core/consensus.go.
   939  // Be mindful of what you Expose.
   940  type PeerState struct {
   941  	peer   p2p.Peer
   942  	logger log.Logger
   943  
   944  	mtx   sync.Mutex             // NOTE: Modify below using setters, never directly.
   945  	PRS   cstypes.PeerRoundState `json:"round_state"` // Exposed.
   946  	Stats *peerStateStats        `json:"stats"`       // Exposed.
   947  }
   948  
   949  // peerStateStats holds internal statistics for a peer.
   950  type peerStateStats struct {
   951  	Votes      int `json:"votes"`
   952  	BlockParts int `json:"block_parts"`
   953  }
   954  
   955  func (pss peerStateStats) String() string {
   956  	return fmt.Sprintf("peerStateStats{votes: %d, blockParts: %d}",
   957  		pss.Votes, pss.BlockParts)
   958  }
   959  
   960  // NewPeerState returns a new PeerState for the given Peer
   961  func NewPeerState(peer p2p.Peer) *PeerState {
   962  	return &PeerState{
   963  		peer:   peer,
   964  		logger: log.NewNopLogger(),
   965  		PRS: cstypes.PeerRoundState{
   966  			Round:              -1,
   967  			ProposalPOLRound:   -1,
   968  			LastCommitRound:    -1,
   969  			CatchupCommitRound: -1,
   970  		},
   971  		Stats: &peerStateStats{},
   972  	}
   973  }
   974  
   975  // SetLogger allows to set a logger on the peer state. Returns the peer state
   976  // itself.
   977  func (ps *PeerState) SetLogger(logger log.Logger) *PeerState {
   978  	ps.logger = logger
   979  	return ps
   980  }
   981  
   982  // GetRoundState returns an shallow copy of the PeerRoundState.
   983  // There's no point in mutating it since it won't change PeerState.
   984  func (ps *PeerState) GetRoundState() *cstypes.PeerRoundState {
   985  	ps.mtx.Lock()
   986  	defer ps.mtx.Unlock()
   987  
   988  	prs := ps.PRS // copy
   989  	return &prs
   990  }
   991  
   992  // ToJSON returns a json of PeerState.
   993  func (ps *PeerState) ToJSON() ([]byte, error) {
   994  	ps.mtx.Lock()
   995  	defer ps.mtx.Unlock()
   996  
   997  	return tmjson.Marshal(ps)
   998  }
   999  
  1000  // GetHeight returns an atomic snapshot of the PeerRoundState's height
  1001  // used by the mempool to ensure peers are caught up before broadcasting new txs
  1002  func (ps *PeerState) GetHeight() int64 {
  1003  	ps.mtx.Lock()
  1004  	defer ps.mtx.Unlock()
  1005  	return ps.PRS.Height
  1006  }
  1007  
  1008  // SetHasProposal sets the given proposal as known for the peer.
  1009  func (ps *PeerState) SetHasProposal(proposal *types.Proposal) {
  1010  	ps.mtx.Lock()
  1011  	defer ps.mtx.Unlock()
  1012  
  1013  	if ps.PRS.Height != proposal.Height || ps.PRS.Round != proposal.Round {
  1014  		return
  1015  	}
  1016  
  1017  	if ps.PRS.Proposal {
  1018  		return
  1019  	}
  1020  
  1021  	ps.PRS.Proposal = true
  1022  
  1023  	// ps.PRS.ProposalBlockParts is set due to NewValidBlockMessage
  1024  	if ps.PRS.ProposalBlockParts != nil {
  1025  		return
  1026  	}
  1027  
  1028  	ps.PRS.ProposalBlockPartSetHeader = proposal.BlockID.PartSetHeader
  1029  	ps.PRS.ProposalBlockParts = bits.NewBitArray(int(proposal.BlockID.PartSetHeader.Total))
  1030  	ps.PRS.ProposalPOLRound = proposal.POLRound
  1031  	ps.PRS.ProposalPOL = nil // Nil until ProposalPOLMessage received.
  1032  }
  1033  
  1034  // InitProposalBlockParts initializes the peer's proposal block parts header and bit array.
  1035  func (ps *PeerState) InitProposalBlockParts(partSetHeader types.PartSetHeader) {
  1036  	ps.mtx.Lock()
  1037  	defer ps.mtx.Unlock()
  1038  
  1039  	if ps.PRS.ProposalBlockParts != nil {
  1040  		return
  1041  	}
  1042  
  1043  	ps.PRS.ProposalBlockPartSetHeader = partSetHeader
  1044  	ps.PRS.ProposalBlockParts = bits.NewBitArray(int(partSetHeader.Total))
  1045  }
  1046  
  1047  // SetHasProposalBlockPart sets the given block part index as known for the peer.
  1048  func (ps *PeerState) SetHasProposalBlockPart(height int64, round int32, index int) {
  1049  	ps.mtx.Lock()
  1050  	defer ps.mtx.Unlock()
  1051  
  1052  	if ps.PRS.Height != height || ps.PRS.Round != round {
  1053  		return
  1054  	}
  1055  
  1056  	ps.PRS.ProposalBlockParts.SetIndex(index, true)
  1057  }
  1058  
  1059  // PickSendVote picks a vote and sends it to the peer.
  1060  // Returns true if vote was sent.
  1061  func (ps *PeerState) PickSendVote(votes types.VoteSetReader) bool {
  1062  	if vote, ok := ps.PickVoteToSend(votes); ok {
  1063  		msg := &VoteMessage{vote}
  1064  		ps.logger.Debug("Sending vote message", "ps", ps, "vote", vote)
  1065  		if ps.peer.Send(VoteChannel, MustEncode(msg)) {
  1066  			ps.SetHasVote(vote)
  1067  			return true
  1068  		}
  1069  		return false
  1070  	}
  1071  	return false
  1072  }
  1073  
  1074  // PickVoteToSend picks a vote to send to the peer.
  1075  // Returns true if a vote was picked.
  1076  // NOTE: `votes` must be the correct Size() for the Height().
  1077  func (ps *PeerState) PickVoteToSend(votes types.VoteSetReader) (vote *types.Vote, ok bool) {
  1078  	ps.mtx.Lock()
  1079  	defer ps.mtx.Unlock()
  1080  
  1081  	if votes.Size() == 0 {
  1082  		return nil, false
  1083  	}
  1084  
  1085  	height, round, votesType, size :=
  1086  		votes.GetHeight(), votes.GetRound(), tmproto.SignedMsgType(votes.Type()), votes.Size()
  1087  
  1088  	// Lazily set data using 'votes'.
  1089  	if votes.IsCommit() {
  1090  		ps.ensureCatchupCommitRound(height, round, size)
  1091  	}
  1092  	ps.ensureVoteBitArrays(height, size)
  1093  
  1094  	psVotes := ps.getVoteBitArray(height, round, votesType)
  1095  	if psVotes == nil {
  1096  		return nil, false // Not something worth sending
  1097  	}
  1098  	if index, ok := votes.BitArray().Sub(psVotes).PickRandom(); ok {
  1099  		return votes.GetByIndex(int32(index)), true
  1100  	}
  1101  	return nil, false
  1102  }
  1103  
  1104  func (ps *PeerState) getVoteBitArray(height int64, round int32, votesType tmproto.SignedMsgType) *bits.BitArray {
  1105  	if !types.IsVoteTypeValid(votesType) {
  1106  		return nil
  1107  	}
  1108  
  1109  	if ps.PRS.Height == height {
  1110  		if ps.PRS.Round == round {
  1111  			switch votesType {
  1112  			case tmproto.PrevoteType:
  1113  				return ps.PRS.Prevotes
  1114  			case tmproto.PrecommitType:
  1115  				return ps.PRS.Precommits
  1116  			}
  1117  		}
  1118  		if ps.PRS.CatchupCommitRound == round {
  1119  			switch votesType {
  1120  			case tmproto.PrevoteType:
  1121  				return nil
  1122  			case tmproto.PrecommitType:
  1123  				return ps.PRS.CatchupCommit
  1124  			}
  1125  		}
  1126  		if ps.PRS.ProposalPOLRound == round {
  1127  			switch votesType {
  1128  			case tmproto.PrevoteType:
  1129  				return ps.PRS.ProposalPOL
  1130  			case tmproto.PrecommitType:
  1131  				return nil
  1132  			}
  1133  		}
  1134  		return nil
  1135  	}
  1136  	if ps.PRS.Height == height+1 {
  1137  		if ps.PRS.LastCommitRound == round {
  1138  			switch votesType {
  1139  			case tmproto.PrevoteType:
  1140  				return nil
  1141  			case tmproto.PrecommitType:
  1142  				return ps.PRS.LastCommit
  1143  			}
  1144  		}
  1145  		return nil
  1146  	}
  1147  	return nil
  1148  }
  1149  
  1150  // 'round': A round for which we have a +2/3 commit.
  1151  func (ps *PeerState) ensureCatchupCommitRound(height int64, round int32, numValidators int) {
  1152  	if ps.PRS.Height != height {
  1153  		return
  1154  	}
  1155  	/*
  1156  		NOTE: This is wrong, 'round' could change.
  1157  		e.g. if orig round is not the same as block LastCommit round.
  1158  		if ps.CatchupCommitRound != -1 && ps.CatchupCommitRound != round {
  1159  			panic(fmt.Sprintf(
  1160  				"Conflicting CatchupCommitRound. Height: %v,
  1161  				Orig: %v,
  1162  				New: %v",
  1163  				height,
  1164  				ps.CatchupCommitRound,
  1165  				round))
  1166  		}
  1167  	*/
  1168  	if ps.PRS.CatchupCommitRound == round {
  1169  		return // Nothing to do!
  1170  	}
  1171  	ps.PRS.CatchupCommitRound = round
  1172  	if round == ps.PRS.Round {
  1173  		ps.PRS.CatchupCommit = ps.PRS.Precommits
  1174  	} else {
  1175  		ps.PRS.CatchupCommit = bits.NewBitArray(numValidators)
  1176  	}
  1177  }
  1178  
  1179  // EnsureVoteBitArrays ensures the bit-arrays have been allocated for tracking
  1180  // what votes this peer has received.
  1181  // NOTE: It's important to make sure that numValidators actually matches
  1182  // what the node sees as the number of validators for height.
  1183  func (ps *PeerState) EnsureVoteBitArrays(height int64, numValidators int) {
  1184  	ps.mtx.Lock()
  1185  	defer ps.mtx.Unlock()
  1186  	ps.ensureVoteBitArrays(height, numValidators)
  1187  }
  1188  
  1189  func (ps *PeerState) ensureVoteBitArrays(height int64, numValidators int) {
  1190  	if ps.PRS.Height == height {
  1191  		if ps.PRS.Prevotes == nil {
  1192  			ps.PRS.Prevotes = bits.NewBitArray(numValidators)
  1193  		}
  1194  		if ps.PRS.Precommits == nil {
  1195  			ps.PRS.Precommits = bits.NewBitArray(numValidators)
  1196  		}
  1197  		if ps.PRS.CatchupCommit == nil {
  1198  			ps.PRS.CatchupCommit = bits.NewBitArray(numValidators)
  1199  		}
  1200  		if ps.PRS.ProposalPOL == nil {
  1201  			ps.PRS.ProposalPOL = bits.NewBitArray(numValidators)
  1202  		}
  1203  	} else if ps.PRS.Height == height+1 {
  1204  		if ps.PRS.LastCommit == nil {
  1205  			ps.PRS.LastCommit = bits.NewBitArray(numValidators)
  1206  		}
  1207  	}
  1208  }
  1209  
  1210  // RecordVote increments internal votes related statistics for this peer.
  1211  // It returns the total number of added votes.
  1212  func (ps *PeerState) RecordVote() int {
  1213  	ps.mtx.Lock()
  1214  	defer ps.mtx.Unlock()
  1215  
  1216  	ps.Stats.Votes++
  1217  
  1218  	return ps.Stats.Votes
  1219  }
  1220  
  1221  // VotesSent returns the number of blocks for which peer has been sending us
  1222  // votes.
  1223  func (ps *PeerState) VotesSent() int {
  1224  	ps.mtx.Lock()
  1225  	defer ps.mtx.Unlock()
  1226  
  1227  	return ps.Stats.Votes
  1228  }
  1229  
  1230  // RecordBlockPart increments internal block part related statistics for this peer.
  1231  // It returns the total number of added block parts.
  1232  func (ps *PeerState) RecordBlockPart() int {
  1233  	ps.mtx.Lock()
  1234  	defer ps.mtx.Unlock()
  1235  
  1236  	ps.Stats.BlockParts++
  1237  	return ps.Stats.BlockParts
  1238  }
  1239  
  1240  // BlockPartsSent returns the number of useful block parts the peer has sent us.
  1241  func (ps *PeerState) BlockPartsSent() int {
  1242  	ps.mtx.Lock()
  1243  	defer ps.mtx.Unlock()
  1244  
  1245  	return ps.Stats.BlockParts
  1246  }
  1247  
  1248  // SetHasVote sets the given vote as known by the peer
  1249  func (ps *PeerState) SetHasVote(vote *types.Vote) {
  1250  	ps.mtx.Lock()
  1251  	defer ps.mtx.Unlock()
  1252  
  1253  	ps.setHasVote(vote.Height, vote.Round, vote.Type, vote.ValidatorIndex)
  1254  }
  1255  
  1256  func (ps *PeerState) setHasVote(height int64, round int32, voteType tmproto.SignedMsgType, index int32) {
  1257  	logger := ps.logger.With(
  1258  		"peerH/R",
  1259  		fmt.Sprintf("%d/%d", ps.PRS.Height, ps.PRS.Round),
  1260  		"H/R",
  1261  		fmt.Sprintf("%d/%d", height, round))
  1262  	logger.Debug("setHasVote", "type", voteType, "index", index)
  1263  
  1264  	// NOTE: some may be nil BitArrays -> no side effects.
  1265  	psVotes := ps.getVoteBitArray(height, round, voteType)
  1266  	if psVotes != nil {
  1267  		psVotes.SetIndex(int(index), true)
  1268  	}
  1269  }
  1270  
  1271  // ApplyNewRoundStepMessage updates the peer state for the new round.
  1272  func (ps *PeerState) ApplyNewRoundStepMessage(msg *NewRoundStepMessage) {
  1273  	ps.mtx.Lock()
  1274  	defer ps.mtx.Unlock()
  1275  
  1276  	// Ignore duplicates or decreases
  1277  	if CompareHRS(msg.Height, msg.Round, msg.Step, ps.PRS.Height, ps.PRS.Round, ps.PRS.Step) <= 0 {
  1278  		return
  1279  	}
  1280  
  1281  	// Just remember these values.
  1282  	psHeight := ps.PRS.Height
  1283  	psRound := ps.PRS.Round
  1284  	psCatchupCommitRound := ps.PRS.CatchupCommitRound
  1285  	psCatchupCommit := ps.PRS.CatchupCommit
  1286  
  1287  	startTime := tmtime.Now().Add(-1 * time.Duration(msg.SecondsSinceStartTime) * time.Second)
  1288  	ps.PRS.Height = msg.Height
  1289  	ps.PRS.Round = msg.Round
  1290  	ps.PRS.Step = msg.Step
  1291  	ps.PRS.StartTime = startTime
  1292  	if psHeight != msg.Height || psRound != msg.Round {
  1293  		ps.PRS.Proposal = false
  1294  		ps.PRS.ProposalBlockPartSetHeader = types.PartSetHeader{}
  1295  		ps.PRS.ProposalBlockParts = nil
  1296  		ps.PRS.ProposalPOLRound = -1
  1297  		ps.PRS.ProposalPOL = nil
  1298  		// We'll update the BitArray capacity later.
  1299  		ps.PRS.Prevotes = nil
  1300  		ps.PRS.Precommits = nil
  1301  	}
  1302  	if psHeight == msg.Height && psRound != msg.Round && msg.Round == psCatchupCommitRound {
  1303  		// Peer caught up to CatchupCommitRound.
  1304  		// Preserve psCatchupCommit!
  1305  		// NOTE: We prefer to use prs.Precommits if
  1306  		// pr.Round matches pr.CatchupCommitRound.
  1307  		ps.PRS.Precommits = psCatchupCommit
  1308  	}
  1309  	if psHeight != msg.Height {
  1310  		// Shift Precommits to LastCommit.
  1311  		if psHeight+1 == msg.Height && psRound == msg.LastCommitRound {
  1312  			ps.PRS.LastCommitRound = msg.LastCommitRound
  1313  			ps.PRS.LastCommit = ps.PRS.Precommits
  1314  		} else {
  1315  			ps.PRS.LastCommitRound = msg.LastCommitRound
  1316  			ps.PRS.LastCommit = nil
  1317  		}
  1318  		// We'll update the BitArray capacity later.
  1319  		ps.PRS.CatchupCommitRound = -1
  1320  		ps.PRS.CatchupCommit = nil
  1321  	}
  1322  }
  1323  
  1324  // ApplyNewValidBlockMessage updates the peer state for the new valid block.
  1325  func (ps *PeerState) ApplyNewValidBlockMessage(msg *NewValidBlockMessage) {
  1326  	ps.mtx.Lock()
  1327  	defer ps.mtx.Unlock()
  1328  
  1329  	if ps.PRS.Height != msg.Height {
  1330  		return
  1331  	}
  1332  
  1333  	if ps.PRS.Round != msg.Round && !msg.IsCommit {
  1334  		return
  1335  	}
  1336  
  1337  	ps.PRS.ProposalBlockPartSetHeader = msg.BlockPartSetHeader
  1338  	ps.PRS.ProposalBlockParts = msg.BlockParts
  1339  }
  1340  
  1341  // ApplyProposalPOLMessage updates the peer state for the new proposal POL.
  1342  func (ps *PeerState) ApplyProposalPOLMessage(msg *ProposalPOLMessage) {
  1343  	ps.mtx.Lock()
  1344  	defer ps.mtx.Unlock()
  1345  
  1346  	if ps.PRS.Height != msg.Height {
  1347  		return
  1348  	}
  1349  	if ps.PRS.ProposalPOLRound != msg.ProposalPOLRound {
  1350  		return
  1351  	}
  1352  
  1353  	// TODO: Merge onto existing ps.PRS.ProposalPOL?
  1354  	// We might have sent some prevotes in the meantime.
  1355  	ps.PRS.ProposalPOL = msg.ProposalPOL
  1356  }
  1357  
  1358  // ApplyHasVoteMessage updates the peer state for the new vote.
  1359  func (ps *PeerState) ApplyHasVoteMessage(msg *HasVoteMessage) {
  1360  	ps.mtx.Lock()
  1361  	defer ps.mtx.Unlock()
  1362  
  1363  	if ps.PRS.Height != msg.Height {
  1364  		return
  1365  	}
  1366  
  1367  	ps.setHasVote(msg.Height, msg.Round, msg.Type, msg.Index)
  1368  }
  1369  
  1370  // ApplyVoteSetBitsMessage updates the peer state for the bit-array of votes
  1371  // it claims to have for the corresponding BlockID.
  1372  // `ourVotes` is a BitArray of votes we have for msg.BlockID
  1373  // NOTE: if ourVotes is nil (e.g. msg.Height < rs.Height),
  1374  // we conservatively overwrite ps's votes w/ msg.Votes.
  1375  func (ps *PeerState) ApplyVoteSetBitsMessage(msg *VoteSetBitsMessage, ourVotes *bits.BitArray) {
  1376  	ps.mtx.Lock()
  1377  	defer ps.mtx.Unlock()
  1378  
  1379  	votes := ps.getVoteBitArray(msg.Height, msg.Round, msg.Type)
  1380  	if votes != nil {
  1381  		if ourVotes == nil {
  1382  			votes.Update(msg.Votes)
  1383  		} else {
  1384  			otherVotes := votes.Sub(ourVotes)
  1385  			hasVotes := otherVotes.Or(msg.Votes)
  1386  			votes.Update(hasVotes)
  1387  		}
  1388  	}
  1389  }
  1390  
  1391  // String returns a string representation of the PeerState
  1392  func (ps *PeerState) String() string {
  1393  	return ps.StringIndented("")
  1394  }
  1395  
  1396  // StringIndented returns a string representation of the PeerState
  1397  func (ps *PeerState) StringIndented(indent string) string {
  1398  	ps.mtx.Lock()
  1399  	defer ps.mtx.Unlock()
  1400  	return fmt.Sprintf(`PeerState{
  1401  %s  Key        %v
  1402  %s  RoundState %v
  1403  %s  Stats      %v
  1404  %s}`,
  1405  		indent, ps.peer.ID(),
  1406  		indent, ps.PRS.StringIndented(indent+"  "),
  1407  		indent, ps.Stats,
  1408  		indent)
  1409  }
  1410  
  1411  //-----------------------------------------------------------------------------
  1412  // Messages
  1413  
  1414  // Message is a message that can be sent and received on the Reactor
  1415  type Message interface {
  1416  	ValidateBasic() error
  1417  }
  1418  
  1419  func init() {
  1420  	tmjson.RegisterType(&NewRoundStepMessage{}, "tendermint/NewRoundStepMessage")
  1421  	tmjson.RegisterType(&NewValidBlockMessage{}, "tendermint/NewValidBlockMessage")
  1422  	tmjson.RegisterType(&ProposalMessage{}, "tendermint/Proposal")
  1423  	tmjson.RegisterType(&ProposalPOLMessage{}, "tendermint/ProposalPOL")
  1424  	tmjson.RegisterType(&BlockPartMessage{}, "tendermint/BlockPart")
  1425  	tmjson.RegisterType(&VoteMessage{}, "tendermint/Vote")
  1426  	tmjson.RegisterType(&HasVoteMessage{}, "tendermint/HasVote")
  1427  	tmjson.RegisterType(&VoteSetMaj23Message{}, "tendermint/VoteSetMaj23")
  1428  	tmjson.RegisterType(&VoteSetBitsMessage{}, "tendermint/VoteSetBits")
  1429  }
  1430  
  1431  func decodeMsg(bz []byte) (msg Message, err error) {
  1432  	pb := &tmcons.Message{}
  1433  	if err = proto.Unmarshal(bz, pb); err != nil {
  1434  		return msg, err
  1435  	}
  1436  
  1437  	return MsgFromProto(pb)
  1438  }
  1439  
  1440  //-------------------------------------
  1441  
  1442  // NewRoundStepMessage is sent for every step taken in the ConsensusState.
  1443  // For every height/round/step transition
  1444  type NewRoundStepMessage struct {
  1445  	Height                int64
  1446  	Round                 int32
  1447  	Step                  cstypes.RoundStepType
  1448  	SecondsSinceStartTime int64
  1449  	LastCommitRound       int32
  1450  }
  1451  
  1452  // ValidateBasic performs basic validation.
  1453  func (m *NewRoundStepMessage) ValidateBasic() error {
  1454  	if m.Height < 0 {
  1455  		return errors.New("negative Height")
  1456  	}
  1457  	if m.Round < 0 {
  1458  		return errors.New("negative Round")
  1459  	}
  1460  	if !m.Step.IsValid() {
  1461  		return errors.New("invalid Step")
  1462  	}
  1463  
  1464  	// NOTE: SecondsSinceStartTime may be negative
  1465  
  1466  	// LastCommitRound will be -1 for the initial height, but we don't know what height this is
  1467  	// since it can be specified in genesis. The reactor will have to validate this via
  1468  	// ValidateHeight().
  1469  	if m.LastCommitRound < -1 {
  1470  		return errors.New("invalid LastCommitRound (cannot be < -1)")
  1471  	}
  1472  
  1473  	return nil
  1474  }
  1475  
  1476  // ValidateHeight validates the height given the chain's initial height.
  1477  func (m *NewRoundStepMessage) ValidateHeight(initialHeight int64) error {
  1478  	if m.Height < initialHeight {
  1479  		return fmt.Errorf("invalid Height %v (lower than initial height %v)",
  1480  			m.Height, initialHeight)
  1481  	}
  1482  	if m.Height == initialHeight && m.LastCommitRound != -1 {
  1483  		return fmt.Errorf("invalid LastCommitRound %v (must be -1 for initial height %v)",
  1484  			m.LastCommitRound, initialHeight)
  1485  	}
  1486  	if m.Height > initialHeight && m.LastCommitRound < 0 {
  1487  		return fmt.Errorf("LastCommitRound can only be negative for initial height %v", // nolint
  1488  			initialHeight)
  1489  	}
  1490  	return nil
  1491  }
  1492  
  1493  // String returns a string representation.
  1494  func (m *NewRoundStepMessage) String() string {
  1495  	return fmt.Sprintf("[NewRoundStep H:%v R:%v S:%v LCR:%v]",
  1496  		m.Height, m.Round, m.Step, m.LastCommitRound)
  1497  }
  1498  
  1499  //-------------------------------------
  1500  
  1501  // NewValidBlockMessage is sent when a validator observes a valid block B in some round r,
  1502  // i.e., there is a Proposal for block B and 2/3+ prevotes for the block B in the round r.
  1503  // In case the block is also committed, then IsCommit flag is set to true.
  1504  type NewValidBlockMessage struct {
  1505  	Height             int64
  1506  	Round              int32
  1507  	BlockPartSetHeader types.PartSetHeader
  1508  	BlockParts         *bits.BitArray
  1509  	IsCommit           bool
  1510  }
  1511  
  1512  // ValidateBasic performs basic validation.
  1513  func (m *NewValidBlockMessage) ValidateBasic() error {
  1514  	if m.Height < 0 {
  1515  		return errors.New("negative Height")
  1516  	}
  1517  	if m.Round < 0 {
  1518  		return errors.New("negative Round")
  1519  	}
  1520  	if err := m.BlockPartSetHeader.ValidateBasic(); err != nil {
  1521  		return fmt.Errorf("wrong BlockPartSetHeader: %v", err)
  1522  	}
  1523  	if m.BlockParts.Size() == 0 {
  1524  		return errors.New("empty blockParts")
  1525  	}
  1526  	if m.BlockParts.Size() != int(m.BlockPartSetHeader.Total) {
  1527  		return fmt.Errorf("blockParts bit array size %d not equal to BlockPartSetHeader.Total %d",
  1528  			m.BlockParts.Size(),
  1529  			m.BlockPartSetHeader.Total)
  1530  	}
  1531  	if m.BlockParts.Size() > int(types.MaxBlockPartsCount) {
  1532  		return fmt.Errorf("blockParts bit array is too big: %d, max: %d", m.BlockParts.Size(), types.MaxBlockPartsCount)
  1533  	}
  1534  	return nil
  1535  }
  1536  
  1537  // String returns a string representation.
  1538  func (m *NewValidBlockMessage) String() string {
  1539  	return fmt.Sprintf("[ValidBlockMessage H:%v R:%v BP:%v BA:%v IsCommit:%v]",
  1540  		m.Height, m.Round, m.BlockPartSetHeader, m.BlockParts, m.IsCommit)
  1541  }
  1542  
  1543  //-------------------------------------
  1544  
  1545  // ProposalMessage is sent when a new block is proposed.
  1546  type ProposalMessage struct {
  1547  	Proposal *types.Proposal
  1548  }
  1549  
  1550  // ValidateBasic performs basic validation.
  1551  func (m *ProposalMessage) ValidateBasic() error {
  1552  	return m.Proposal.ValidateBasic()
  1553  }
  1554  
  1555  // String returns a string representation.
  1556  func (m *ProposalMessage) String() string {
  1557  	return fmt.Sprintf("[Proposal %v]", m.Proposal)
  1558  }
  1559  
  1560  //-------------------------------------
  1561  
  1562  // ProposalPOLMessage is sent when a previous proposal is re-proposed.
  1563  type ProposalPOLMessage struct {
  1564  	Height           int64
  1565  	ProposalPOLRound int32
  1566  	ProposalPOL      *bits.BitArray
  1567  }
  1568  
  1569  // ValidateBasic performs basic validation.
  1570  func (m *ProposalPOLMessage) ValidateBasic() error {
  1571  	if m.Height < 0 {
  1572  		return errors.New("negative Height")
  1573  	}
  1574  	if m.ProposalPOLRound < 0 {
  1575  		return errors.New("negative ProposalPOLRound")
  1576  	}
  1577  	if m.ProposalPOL.Size() == 0 {
  1578  		return errors.New("empty ProposalPOL bit array")
  1579  	}
  1580  	if m.ProposalPOL.Size() > types.MaxVotesCount {
  1581  		return fmt.Errorf("proposalPOL bit array is too big: %d, max: %d", m.ProposalPOL.Size(), types.MaxVotesCount)
  1582  	}
  1583  	return nil
  1584  }
  1585  
  1586  // String returns a string representation.
  1587  func (m *ProposalPOLMessage) String() string {
  1588  	return fmt.Sprintf("[ProposalPOL H:%v POLR:%v POL:%v]", m.Height, m.ProposalPOLRound, m.ProposalPOL)
  1589  }
  1590  
  1591  //-------------------------------------
  1592  
  1593  // BlockPartMessage is sent when gossipping a piece of the proposed block.
  1594  type BlockPartMessage struct {
  1595  	Height int64
  1596  	Round  int32
  1597  	Part   *types.Part
  1598  }
  1599  
  1600  // ValidateBasic performs basic validation.
  1601  func (m *BlockPartMessage) ValidateBasic() error {
  1602  	if m.Height < 0 {
  1603  		return errors.New("negative Height")
  1604  	}
  1605  	if m.Round < 0 {
  1606  		return errors.New("negative Round")
  1607  	}
  1608  	if err := m.Part.ValidateBasic(); err != nil {
  1609  		return fmt.Errorf("wrong Part: %v", err)
  1610  	}
  1611  	return nil
  1612  }
  1613  
  1614  // String returns a string representation.
  1615  func (m *BlockPartMessage) String() string {
  1616  	return fmt.Sprintf("[BlockPart H:%v R:%v P:%v]", m.Height, m.Round, m.Part)
  1617  }
  1618  
  1619  //-------------------------------------
  1620  
  1621  // VoteMessage is sent when voting for a proposal (or lack thereof).
  1622  type VoteMessage struct {
  1623  	Vote *types.Vote
  1624  }
  1625  
  1626  // ValidateBasic performs basic validation.
  1627  func (m *VoteMessage) ValidateBasic() error {
  1628  	return m.Vote.ValidateBasic()
  1629  }
  1630  
  1631  // String returns a string representation.
  1632  func (m *VoteMessage) String() string {
  1633  	return fmt.Sprintf("[Vote %v]", m.Vote)
  1634  }
  1635  
  1636  //-------------------------------------
  1637  
  1638  // HasVoteMessage is sent to indicate that a particular vote has been received.
  1639  type HasVoteMessage struct {
  1640  	Height int64
  1641  	Round  int32
  1642  	Type   tmproto.SignedMsgType
  1643  	Index  int32
  1644  }
  1645  
  1646  // ValidateBasic performs basic validation.
  1647  func (m *HasVoteMessage) ValidateBasic() error {
  1648  	if m.Height < 0 {
  1649  		return errors.New("negative Height")
  1650  	}
  1651  	if m.Round < 0 {
  1652  		return errors.New("negative Round")
  1653  	}
  1654  	if !types.IsVoteTypeValid(m.Type) {
  1655  		return errors.New("invalid Type")
  1656  	}
  1657  	if m.Index < 0 {
  1658  		return errors.New("negative Index")
  1659  	}
  1660  	return nil
  1661  }
  1662  
  1663  // String returns a string representation.
  1664  func (m *HasVoteMessage) String() string {
  1665  	return fmt.Sprintf("[HasVote VI:%v V:{%v/%02d/%v}]", m.Index, m.Height, m.Round, m.Type)
  1666  }
  1667  
  1668  //-------------------------------------
  1669  
  1670  // VoteSetMaj23Message is sent to indicate that a given BlockID has seen +2/3 votes.
  1671  type VoteSetMaj23Message struct {
  1672  	Height  int64
  1673  	Round   int32
  1674  	Type    tmproto.SignedMsgType
  1675  	BlockID types.BlockID
  1676  }
  1677  
  1678  // ValidateBasic performs basic validation.
  1679  func (m *VoteSetMaj23Message) ValidateBasic() error {
  1680  	if m.Height < 0 {
  1681  		return errors.New("negative Height")
  1682  	}
  1683  	if m.Round < 0 {
  1684  		return errors.New("negative Round")
  1685  	}
  1686  	if !types.IsVoteTypeValid(m.Type) {
  1687  		return errors.New("invalid Type")
  1688  	}
  1689  	if err := m.BlockID.ValidateBasic(); err != nil {
  1690  		return fmt.Errorf("wrong BlockID: %v", err)
  1691  	}
  1692  	return nil
  1693  }
  1694  
  1695  // String returns a string representation.
  1696  func (m *VoteSetMaj23Message) String() string {
  1697  	return fmt.Sprintf("[VSM23 %v/%02d/%v %v]", m.Height, m.Round, m.Type, m.BlockID)
  1698  }
  1699  
  1700  //-------------------------------------
  1701  
  1702  // VoteSetBitsMessage is sent to communicate the bit-array of votes seen for the BlockID.
  1703  type VoteSetBitsMessage struct {
  1704  	Height  int64
  1705  	Round   int32
  1706  	Type    tmproto.SignedMsgType
  1707  	BlockID types.BlockID
  1708  	Votes   *bits.BitArray
  1709  }
  1710  
  1711  // ValidateBasic performs basic validation.
  1712  func (m *VoteSetBitsMessage) ValidateBasic() error {
  1713  	if m.Height < 0 {
  1714  		return errors.New("negative Height")
  1715  	}
  1716  	if !types.IsVoteTypeValid(m.Type) {
  1717  		return errors.New("invalid Type")
  1718  	}
  1719  	if err := m.BlockID.ValidateBasic(); err != nil {
  1720  		return fmt.Errorf("wrong BlockID: %v", err)
  1721  	}
  1722  	// NOTE: Votes.Size() can be zero if the node does not have any
  1723  	if m.Votes.Size() > types.MaxVotesCount {
  1724  		return fmt.Errorf("votes bit array is too big: %d, max: %d", m.Votes.Size(), types.MaxVotesCount)
  1725  	}
  1726  	return nil
  1727  }
  1728  
  1729  // String returns a string representation.
  1730  func (m *VoteSetBitsMessage) String() string {
  1731  	return fmt.Sprintf("[VSB %v/%02d/%v %v %v]", m.Height, m.Round, m.Type, m.BlockID, m.Votes)
  1732  }
  1733  
  1734  //-------------------------------------