func (r *Reactor) SwitchToBlockSync(state sm.State) error {
	r.blockSync.Set()
	r.initialState = state
	r.pool.height = state.LastBlockHeight + 1
	if err := r.pool.Start(); err != nil {
		return err
	}
	r.syncStartTime = time.Now()
	r.poolWG.Add(1)
	go r.requestRoutine()
	r.poolWG.Add(1)
	go r.poolRoutine(true)
	return nil
}
SwitchToBlockSync is called by the state sync reactor when switching to fast
sync.
package blocksync
import (
	"fmt"
	"runtime/debug"
	"sync"
	"time"
	"github.com/tendermint/tendermint/internal/consensus"
	"github.com/tendermint/tendermint/internal/p2p"
	sm "github.com/tendermint/tendermint/internal/state"
	"github.com/tendermint/tendermint/internal/store"
	"github.com/tendermint/tendermint/libs/log"
	"github.com/tendermint/tendermint/libs/service"
	tmsync "github.com/tendermint/tendermint/libs/sync"
	bcproto "github.com/tendermint/tendermint/proto/tendermint/blocksync"
	"github.com/tendermint/tendermint/types"
)
var _ service.Service = (*Reactor)(nil)
const (
	BlockSyncChannel = p2p.ChannelID(0x40)
	trySyncIntervalMS = 10
	statusUpdateIntervalSeconds = 10
	switchToConsensusIntervalSeconds = 1
	syncTimeout = 60 * time.Second
)
func GetChannelDescriptor() *p2p.ChannelDescriptor {
	return &p2p.ChannelDescriptor{
		ID:                  BlockSyncChannel,
		MessageType:         new(bcproto.Message),
		Priority:            5,
		SendQueueCapacity:   1000,
		RecvBufferCapacity:  1024,
		RecvMessageCapacity: MaxMsgSize,
	}
}
type consensusReactor interface {
	SwitchToConsensus(state sm.State, skipWAL bool)
}
type peerError struct {
	err    error
	peerID types.NodeID
}
func (e peerError) Error() string {
	return fmt.Sprintf("error with peer %v: %s", e.peerID, e.err.Error())
}
type Reactor struct {
	service.BaseService
	initialState sm.State
	blockExec   *sm.BlockExecutor
	store       *store.BlockStore
	pool        *BlockPool
	consReactor consensusReactor
	blockSync   *tmsync.AtomicBool
	blockSyncCh *p2p.Channel
	blockSyncOutBridgeCh chan p2p.Envelope
	peerUpdates          *p2p.PeerUpdates
	closeCh              chan struct{}
	requestsCh <-chan BlockRequest
	errorsCh   <-chan peerError
	poolWG sync.WaitGroup
	metrics *consensus.Metrics
	syncStartTime time.Time
}
func NewReactor(
	logger log.Logger,
	state sm.State,
	blockExec *sm.BlockExecutor,
	store *store.BlockStore,
	consReactor consensusReactor,
	blockSyncCh *p2p.Channel,
	peerUpdates *p2p.PeerUpdates,
	blockSync bool,
	metrics *consensus.Metrics,
) (*Reactor, error) {
	if state.LastBlockHeight != store.Height() {
		return nil, fmt.Errorf("state (%v) and store (%v) height mismatch", state.LastBlockHeight, store.Height())
	}
	startHeight := store.Height() + 1
	if startHeight == 1 {
		startHeight = state.InitialHeight
	}
	requestsCh := make(chan BlockRequest, maxTotalRequesters)
	errorsCh := make(chan peerError, maxPeerErrBuffer)  
	r := &Reactor{
		initialState:         state,
		blockExec:            blockExec,
		store:                store,
		pool:                 NewBlockPool(startHeight, requestsCh, errorsCh),
		consReactor:          consReactor,
		blockSync:            tmsync.NewBool(blockSync),
		requestsCh:           requestsCh,
		errorsCh:             errorsCh,
		blockSyncCh:          blockSyncCh,
		blockSyncOutBridgeCh: make(chan p2p.Envelope),
		peerUpdates:          peerUpdates,
		closeCh:              make(chan struct{}),
		metrics:              metrics,
		syncStartTime:        time.Time{},
	}
	r.BaseService = *service.NewBaseService(logger, "BlockSync", r)
	return r, nil
}
func (r *Reactor) OnStart() error {
	if r.blockSync.IsSet() {
		if err := r.pool.Start(); err != nil {
			return err
		}
		r.poolWG.Add(1)
		go r.requestRoutine()
		r.poolWG.Add(1)
		go r.poolRoutine(false)
	}
	go r.processBlockSyncCh()
	go r.processPeerUpdates()
	return nil
}
func (r *Reactor) OnStop() {
	if r.blockSync.IsSet() {
		if err := r.pool.Stop(); err != nil {
			r.Logger.Error("failed to stop pool", "err", err)
		}
	}
	r.poolWG.Wait()
	close(r.closeCh)
	<-r.blockSyncCh.Done()
	<-r.peerUpdates.Done()
}
func (r *Reactor) respondToPeer(msg *bcproto.BlockRequest, peerID types.NodeID) {
	block := r.store.LoadBlock(msg.Height)
	if block != nil {
		blockProto, err := block.ToProto()
		if err != nil {
			r.Logger.Error("failed to convert msg to protobuf", "err", err)
			return
		}
		r.blockSyncCh.Out <- p2p.Envelope{
			To:      peerID,
			Message: &bcproto.BlockResponse{Block: blockProto},
		}
		return
	}
	r.Logger.Info("peer requesting a block we do not have", "peer", peerID, "height", msg.Height)
	r.blockSyncCh.Out <- p2p.Envelope{
		To:      peerID,
		Message: &bcproto.NoBlockResponse{Height: msg.Height},
	}
}
func (r *Reactor) handleBlockSyncMessage(envelope p2p.Envelope) error {
	logger := r.Logger.With("peer", envelope.From)
	switch msg := envelope.Message.(type) {
	case *bcproto.BlockRequest:
		r.respondToPeer(msg, envelope.From)
	case *bcproto.BlockResponse:
		block, err := types.BlockFromProto(msg.Block)
		if err != nil {
			logger.Error("failed to convert block from proto", "err", err)
			return err
		}
		r.pool.AddBlock(envelope.From, block, block.Size())
	case *bcproto.StatusRequest:
		r.blockSyncCh.Out <- p2p.Envelope{
			To: envelope.From,
			Message: &bcproto.StatusResponse{
				Height: r.store.Height(),
				Base:   r.store.Base(),
			},
		}
	case *bcproto.StatusResponse:
		r.pool.SetPeerRange(envelope.From, msg.Base, msg.Height)
	case *bcproto.NoBlockResponse:
		logger.Debug("peer does not have the requested block", "height", msg.Height)
	default:
		return fmt.Errorf("received unknown message: %T", msg)
	}
	return nil
}
func (r *Reactor) handleMessage(chID p2p.ChannelID, envelope p2p.Envelope) (err error) {
	defer func() {
		if e := recover(); e != nil {
			err = fmt.Errorf("panic in processing message: %v", e)
			r.Logger.Error(
				"recovering from processing message panic",
				"err", err,
				"stack", string(debug.Stack()),
			)
		}
	}()
	r.Logger.Debug("received message", "message", envelope.Message, "peer", envelope.From)
	switch chID {
	case BlockSyncChannel:
		err = r.handleBlockSyncMessage(envelope)
	default:
		err = fmt.Errorf("unknown channel ID (%d) for envelope (%v)", chID, envelope)
	}
	return err
}
func (r *Reactor) processBlockSyncCh() {
	defer r.blockSyncCh.Close()
	for {
		select {
		case envelope := <-r.blockSyncCh.In:
			if err := r.handleMessage(r.blockSyncCh.ID, envelope); err != nil {
				r.Logger.Error("failed to process message", "ch_id", r.blockSyncCh.ID, "envelope", envelope, "err", err)
				r.blockSyncCh.Error <- p2p.PeerError{
					NodeID: envelope.From,
					Err:    err,
				}
			}
		case envelope := <-r.blockSyncOutBridgeCh:
			r.blockSyncCh.Out <- envelope
		case <-r.closeCh:
			r.Logger.Debug("stopped listening on block sync channel; closing...")
			return
		}
	}
}
func (r *Reactor) processPeerUpdate(peerUpdate p2p.PeerUpdate) {
	r.Logger.Debug("received peer update", "peer", peerUpdate.NodeID, "status", peerUpdate.Status)
	if len(peerUpdate.NodeID) == 0 {
		return
	}
	switch peerUpdate.Status {
	case p2p.PeerStatusUp:
		r.blockSyncOutBridgeCh <- p2p.Envelope{
			To: peerUpdate.NodeID,
			Message: &bcproto.StatusResponse{
				Base:   r.store.Base(),
				Height: r.store.Height(),
			},
		}
	case p2p.PeerStatusDown:
		r.pool.RemovePeer(peerUpdate.NodeID)
	}
}
func (r *Reactor) processPeerUpdates() {
	defer r.peerUpdates.Close()
	for {
		select {
		case peerUpdate := <-r.peerUpdates.Updates():
			r.processPeerUpdate(peerUpdate)
		case <-r.closeCh:
			r.Logger.Debug("stopped listening on peer updates channel; closing...")
			return
		}
	}
}