github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/bft/consensus/reactor.go (about)

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