github.com/adoriasoft/tendermint@v0.34.0-dev1.0.20200722151356-96d84601a75a/blockchain/v2/reactor.go (about)

     1  package v2
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"time"
     7  
     8  	"github.com/tendermint/tendermint/behaviour"
     9  	bc "github.com/tendermint/tendermint/blockchain"
    10  	"github.com/tendermint/tendermint/libs/log"
    11  	tmsync "github.com/tendermint/tendermint/libs/sync"
    12  	"github.com/tendermint/tendermint/p2p"
    13  	bcproto "github.com/tendermint/tendermint/proto/tendermint/blockchain"
    14  	"github.com/tendermint/tendermint/state"
    15  	"github.com/tendermint/tendermint/types"
    16  )
    17  
    18  const (
    19  	// chBufferSize is the buffer size of all event channels.
    20  	chBufferSize int = 1000
    21  )
    22  
    23  type blockStore interface {
    24  	LoadBlock(height int64) *types.Block
    25  	SaveBlock(*types.Block, *types.PartSet, *types.Commit)
    26  	Base() int64
    27  	Height() int64
    28  }
    29  
    30  // BlockchainReactor handles fast sync protocol.
    31  type BlockchainReactor struct {
    32  	p2p.BaseReactor
    33  
    34  	fastSync    bool // if true, enable fast sync on start
    35  	stateSynced bool // set to true when SwitchToFastSync is called by state sync
    36  	scheduler   *Routine
    37  	processor   *Routine
    38  	logger      log.Logger
    39  
    40  	mtx           tmsync.RWMutex
    41  	maxPeerHeight int64
    42  	syncHeight    int64
    43  	events        chan Event // non-nil during a fast sync
    44  
    45  	reporter behaviour.Reporter
    46  	io       iIO
    47  	store    blockStore
    48  }
    49  
    50  //nolint:unused,deadcode
    51  type blockVerifier interface {
    52  	VerifyCommit(chainID string, blockID types.BlockID, height int64, commit *types.Commit) error
    53  }
    54  
    55  type blockApplier interface {
    56  	ApplyBlock(state state.State, blockID types.BlockID, block *types.Block) (state.State, int64, error)
    57  }
    58  
    59  // XXX: unify naming in this package around tmState
    60  func newReactor(state state.State, store blockStore, reporter behaviour.Reporter,
    61  	blockApplier blockApplier, fastSync bool) *BlockchainReactor {
    62  	scheduler := newScheduler(state.LastBlockHeight, time.Now())
    63  	pContext := newProcessorContext(store, blockApplier, state)
    64  	// TODO: Fix naming to just newProcesssor
    65  	// newPcState requires a processorContext
    66  	processor := newPcState(pContext)
    67  
    68  	return &BlockchainReactor{
    69  		scheduler: newRoutine("scheduler", scheduler.handle, chBufferSize),
    70  		processor: newRoutine("processor", processor.handle, chBufferSize),
    71  		store:     store,
    72  		reporter:  reporter,
    73  		logger:    log.NewNopLogger(),
    74  		fastSync:  fastSync,
    75  	}
    76  }
    77  
    78  // NewBlockchainReactor creates a new reactor instance.
    79  func NewBlockchainReactor(
    80  	state state.State,
    81  	blockApplier blockApplier,
    82  	store blockStore,
    83  	fastSync bool) *BlockchainReactor {
    84  	reporter := behaviour.NewMockReporter()
    85  	return newReactor(state, store, reporter, blockApplier, fastSync)
    86  }
    87  
    88  // SetSwitch implements Reactor interface.
    89  func (r *BlockchainReactor) SetSwitch(sw *p2p.Switch) {
    90  	r.Switch = sw
    91  	if sw != nil {
    92  		r.io = newSwitchIo(sw)
    93  	} else {
    94  		r.io = nil
    95  	}
    96  }
    97  
    98  func (r *BlockchainReactor) setMaxPeerHeight(height int64) {
    99  	r.mtx.Lock()
   100  	defer r.mtx.Unlock()
   101  	if height > r.maxPeerHeight {
   102  		r.maxPeerHeight = height
   103  	}
   104  }
   105  
   106  func (r *BlockchainReactor) setSyncHeight(height int64) {
   107  	r.mtx.Lock()
   108  	defer r.mtx.Unlock()
   109  	r.syncHeight = height
   110  }
   111  
   112  // SyncHeight returns the height to which the BlockchainReactor has synced.
   113  func (r *BlockchainReactor) SyncHeight() int64 {
   114  	r.mtx.RLock()
   115  	defer r.mtx.RUnlock()
   116  	return r.syncHeight
   117  }
   118  
   119  // SetLogger sets the logger of the reactor.
   120  func (r *BlockchainReactor) SetLogger(logger log.Logger) {
   121  	r.logger = logger
   122  	r.scheduler.setLogger(logger)
   123  	r.processor.setLogger(logger)
   124  }
   125  
   126  // Start implements cmn.Service interface
   127  func (r *BlockchainReactor) Start() error {
   128  	r.reporter = behaviour.NewSwitchReporter(r.BaseReactor.Switch)
   129  	if r.fastSync {
   130  		err := r.startSync(nil)
   131  		if err != nil {
   132  			return fmt.Errorf("failed to start fast sync: %w", err)
   133  		}
   134  	}
   135  	return nil
   136  }
   137  
   138  // startSync begins a fast sync, signalled by r.events being non-nil. If state is non-nil,
   139  // the scheduler and processor is updated with this state on startup.
   140  func (r *BlockchainReactor) startSync(state *state.State) error {
   141  	r.mtx.Lock()
   142  	defer r.mtx.Unlock()
   143  	if r.events != nil {
   144  		return errors.New("fast sync already in progress")
   145  	}
   146  	r.events = make(chan Event, chBufferSize)
   147  	go r.scheduler.start()
   148  	go r.processor.start()
   149  	if state != nil {
   150  		<-r.scheduler.ready()
   151  		<-r.processor.ready()
   152  		r.scheduler.send(bcResetState{state: *state})
   153  		r.processor.send(bcResetState{state: *state})
   154  	}
   155  	go r.demux(r.events)
   156  	return nil
   157  }
   158  
   159  // endSync ends a fast sync
   160  func (r *BlockchainReactor) endSync() {
   161  	r.mtx.Lock()
   162  	defer r.mtx.Unlock()
   163  	if r.events != nil {
   164  		close(r.events)
   165  	}
   166  	r.events = nil
   167  	r.scheduler.stop()
   168  	r.processor.stop()
   169  }
   170  
   171  // SwitchToFastSync is called by the state sync reactor when switching to fast sync.
   172  func (r *BlockchainReactor) SwitchToFastSync(state state.State) error {
   173  	r.stateSynced = true
   174  	state = state.Copy()
   175  	return r.startSync(&state)
   176  }
   177  
   178  // reactor generated ticker events:
   179  // ticker for cleaning peers
   180  type rTryPrunePeer struct {
   181  	priorityHigh
   182  	time time.Time
   183  }
   184  
   185  func (e rTryPrunePeer) String() string {
   186  	return fmt.Sprintf(": %v", e.time)
   187  }
   188  
   189  // ticker event for scheduling block requests
   190  type rTrySchedule struct {
   191  	priorityHigh
   192  	time time.Time
   193  }
   194  
   195  func (e rTrySchedule) String() string {
   196  	return fmt.Sprintf(": %v", e.time)
   197  }
   198  
   199  // ticker for block processing
   200  type rProcessBlock struct {
   201  	priorityNormal
   202  }
   203  
   204  // reactor generated events based on blockchain related messages from peers:
   205  // blockResponse message received from a peer
   206  type bcBlockResponse struct {
   207  	priorityNormal
   208  	time   time.Time
   209  	peerID p2p.ID
   210  	size   int64
   211  	block  *types.Block
   212  }
   213  
   214  // blockNoResponse message received from a peer
   215  type bcNoBlockResponse struct {
   216  	priorityNormal
   217  	time   time.Time
   218  	peerID p2p.ID
   219  	height int64
   220  }
   221  
   222  // statusResponse message received from a peer
   223  type bcStatusResponse struct {
   224  	priorityNormal
   225  	time   time.Time
   226  	peerID p2p.ID
   227  	base   int64
   228  	height int64
   229  }
   230  
   231  // new peer is connected
   232  type bcAddNewPeer struct {
   233  	priorityNormal
   234  	peerID p2p.ID
   235  }
   236  
   237  // existing peer is removed
   238  type bcRemovePeer struct {
   239  	priorityHigh
   240  	peerID p2p.ID
   241  	reason interface{}
   242  }
   243  
   244  // resets the scheduler and processor state, e.g. following a switch from state syncing
   245  type bcResetState struct {
   246  	priorityHigh
   247  	state state.State
   248  }
   249  
   250  // Takes the channel as a parameter to avoid race conditions on r.events.
   251  func (r *BlockchainReactor) demux(events <-chan Event) {
   252  	var lastRate = 0.0
   253  	var lastHundred = time.Now()
   254  
   255  	var (
   256  		processBlockFreq = 20 * time.Millisecond
   257  		doProcessBlockCh = make(chan struct{}, 1)
   258  		doProcessBlockTk = time.NewTicker(processBlockFreq)
   259  	)
   260  	defer doProcessBlockTk.Stop()
   261  
   262  	var (
   263  		prunePeerFreq = 1 * time.Second
   264  		doPrunePeerCh = make(chan struct{}, 1)
   265  		doPrunePeerTk = time.NewTicker(prunePeerFreq)
   266  	)
   267  	defer doPrunePeerTk.Stop()
   268  
   269  	var (
   270  		scheduleFreq = 20 * time.Millisecond
   271  		doScheduleCh = make(chan struct{}, 1)
   272  		doScheduleTk = time.NewTicker(scheduleFreq)
   273  	)
   274  	defer doScheduleTk.Stop()
   275  
   276  	var (
   277  		statusFreq = 10 * time.Second
   278  		doStatusCh = make(chan struct{}, 1)
   279  		doStatusTk = time.NewTicker(statusFreq)
   280  	)
   281  	defer doStatusTk.Stop()
   282  	doStatusCh <- struct{}{} // immediately broadcast to get status of existing peers
   283  
   284  	// XXX: Extract timers to make testing atemporal
   285  	for {
   286  		select {
   287  		// Pacers: send at most per frequency but don't saturate
   288  		case <-doProcessBlockTk.C:
   289  			select {
   290  			case doProcessBlockCh <- struct{}{}:
   291  			default:
   292  			}
   293  		case <-doPrunePeerTk.C:
   294  			select {
   295  			case doPrunePeerCh <- struct{}{}:
   296  			default:
   297  			}
   298  		case <-doScheduleTk.C:
   299  			select {
   300  			case doScheduleCh <- struct{}{}:
   301  			default:
   302  			}
   303  		case <-doStatusTk.C:
   304  			select {
   305  			case doStatusCh <- struct{}{}:
   306  			default:
   307  			}
   308  
   309  		// Tickers: perform tasks periodically
   310  		case <-doScheduleCh:
   311  			r.scheduler.send(rTrySchedule{time: time.Now()})
   312  		case <-doPrunePeerCh:
   313  			r.scheduler.send(rTryPrunePeer{time: time.Now()})
   314  		case <-doProcessBlockCh:
   315  			r.processor.send(rProcessBlock{})
   316  		case <-doStatusCh:
   317  			r.io.broadcastStatusRequest()
   318  
   319  		// Events from peers. Closing the channel signals event loop termination.
   320  		case event, ok := <-events:
   321  			if !ok {
   322  				r.logger.Info("Stopping event processing")
   323  				return
   324  			}
   325  			switch event := event.(type) {
   326  			case bcStatusResponse:
   327  				r.setMaxPeerHeight(event.height)
   328  				r.scheduler.send(event)
   329  			case bcAddNewPeer, bcRemovePeer, bcBlockResponse, bcNoBlockResponse:
   330  				r.scheduler.send(event)
   331  			default:
   332  				r.logger.Error("Received unexpected event", "event", fmt.Sprintf("%T", event))
   333  			}
   334  
   335  		// Incremental events from scheduler
   336  		case event := <-r.scheduler.next():
   337  			switch event := event.(type) {
   338  			case scBlockReceived:
   339  				r.processor.send(event)
   340  			case scPeerError:
   341  				r.processor.send(event)
   342  				r.reporter.Report(behaviour.BadMessage(event.peerID, "scPeerError"))
   343  			case scBlockRequest:
   344  				r.io.sendBlockRequest(event.peerID, event.height)
   345  			case scFinishedEv:
   346  				r.processor.send(event)
   347  				r.scheduler.stop()
   348  			case scSchedulerFail:
   349  				r.logger.Error("Scheduler failure", "err", event.reason.Error())
   350  			case scPeersPruned:
   351  				r.logger.Debug("Pruned peers", "count", len(event.peers))
   352  			case noOpEvent:
   353  			default:
   354  				r.logger.Error("Received unexpected scheduler event", "event", fmt.Sprintf("%T", event))
   355  			}
   356  
   357  		// Incremental events from processor
   358  		case event := <-r.processor.next():
   359  			switch event := event.(type) {
   360  			case pcBlockProcessed:
   361  				r.setSyncHeight(event.height)
   362  				if r.syncHeight%100 == 0 {
   363  					lastRate = 0.9*lastRate + 0.1*(100/time.Since(lastHundred).Seconds())
   364  					r.logger.Info("Fast Sync Rate", "height", r.syncHeight,
   365  						"max_peer_height", r.maxPeerHeight, "blocks/s", lastRate)
   366  					lastHundred = time.Now()
   367  				}
   368  				r.scheduler.send(event)
   369  			case pcBlockVerificationFailure:
   370  				r.scheduler.send(event)
   371  			case pcFinished:
   372  				r.logger.Info("Fast sync complete, switching to consensus")
   373  				if !r.io.trySwitchToConsensus(event.tmState, event.blocksSynced > 0 || r.stateSynced) {
   374  					r.logger.Error("Failed to switch to consensus reactor")
   375  				}
   376  				r.endSync()
   377  				return
   378  			case noOpEvent:
   379  			default:
   380  				r.logger.Error("Received unexpected processor event", "event", fmt.Sprintf("%T", event))
   381  			}
   382  
   383  		// Terminal event from scheduler
   384  		case err := <-r.scheduler.final():
   385  			switch err {
   386  			case nil:
   387  				r.logger.Info("Scheduler stopped")
   388  			default:
   389  				r.logger.Error("Scheduler aborted with error", "err", err)
   390  			}
   391  
   392  		// Terminal event from processor
   393  		case err := <-r.processor.final():
   394  			switch err {
   395  			case nil:
   396  				r.logger.Info("Processor stopped")
   397  			default:
   398  				r.logger.Error("Processor aborted with error", "err", err)
   399  			}
   400  		}
   401  	}
   402  }
   403  
   404  // Stop implements cmn.Service interface.
   405  func (r *BlockchainReactor) Stop() error {
   406  	r.logger.Info("reactor stopping")
   407  	r.endSync()
   408  	r.logger.Info("reactor stopped")
   409  	return nil
   410  }
   411  
   412  // Receive implements Reactor by handling different message types.
   413  func (r *BlockchainReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) {
   414  	msg, err := bc.DecodeMsg(msgBytes)
   415  	if err != nil {
   416  		r.logger.Error("error decoding message",
   417  			"src", src.ID(), "chId", chID, "msg", msg, "err", err, "bytes", msgBytes)
   418  		_ = r.reporter.Report(behaviour.BadMessage(src.ID(), err.Error()))
   419  		return
   420  	}
   421  
   422  	if err = bc.ValidateMsg(msg); err != nil {
   423  		r.logger.Error("peer sent us invalid msg", "peer", src, "msg", msg, "err", err)
   424  		_ = r.reporter.Report(behaviour.BadMessage(src.ID(), err.Error()))
   425  		return
   426  	}
   427  
   428  	r.logger.Debug("Receive", "src", src.ID(), "chID", chID, "msg", msg)
   429  
   430  	switch msg := msg.(type) {
   431  	case *bcproto.StatusRequest:
   432  		if err := r.io.sendStatusResponse(r.store.Base(), r.store.Height(), src.ID()); err != nil {
   433  			r.logger.Error("Could not send status message to peer", "src", src)
   434  		}
   435  
   436  	case *bcproto.BlockRequest:
   437  		block := r.store.LoadBlock(msg.Height)
   438  		if block != nil {
   439  			if err = r.io.sendBlockToPeer(block, src.ID()); err != nil {
   440  				r.logger.Error("Could not send block message to peer: ", err)
   441  			}
   442  		} else {
   443  			r.logger.Info("peer asking for a block we don't have", "src", src, "height", msg.Height)
   444  			peerID := src.ID()
   445  			if err = r.io.sendBlockNotFound(msg.Height, peerID); err != nil {
   446  				r.logger.Error("Couldn't send block not found: ", err)
   447  			}
   448  		}
   449  
   450  	case *bcproto.StatusResponse:
   451  		r.mtx.RLock()
   452  		if r.events != nil {
   453  			r.events <- bcStatusResponse{peerID: src.ID(), base: msg.Base, height: msg.Height}
   454  		}
   455  		r.mtx.RUnlock()
   456  
   457  	case *bcproto.BlockResponse:
   458  		r.mtx.RLock()
   459  		bi, err := types.BlockFromProto(msg.Block)
   460  		if err != nil {
   461  			r.logger.Error("error transitioning block from protobuf", "err", err)
   462  			return
   463  		}
   464  		if r.events != nil {
   465  			r.events <- bcBlockResponse{
   466  				peerID: src.ID(),
   467  				block:  bi,
   468  				size:   int64(len(msgBytes)),
   469  				time:   time.Now(),
   470  			}
   471  		}
   472  		r.mtx.RUnlock()
   473  
   474  	case *bcproto.NoBlockResponse:
   475  		r.mtx.RLock()
   476  		if r.events != nil {
   477  			r.events <- bcNoBlockResponse{peerID: src.ID(), height: msg.Height, time: time.Now()}
   478  		}
   479  		r.mtx.RUnlock()
   480  	}
   481  }
   482  
   483  // AddPeer implements Reactor interface
   484  func (r *BlockchainReactor) AddPeer(peer p2p.Peer) {
   485  	err := r.io.sendStatusResponse(r.store.Base(), r.store.Height(), peer.ID())
   486  	if err != nil {
   487  		r.logger.Error("Could not send status message to peer new", "src", peer.ID, "height", r.SyncHeight())
   488  	}
   489  	r.mtx.RLock()
   490  	defer r.mtx.RUnlock()
   491  	if r.events != nil {
   492  		r.events <- bcAddNewPeer{peerID: peer.ID()}
   493  	}
   494  }
   495  
   496  // RemovePeer implements Reactor interface.
   497  func (r *BlockchainReactor) RemovePeer(peer p2p.Peer, reason interface{}) {
   498  	r.mtx.RLock()
   499  	defer r.mtx.RUnlock()
   500  	if r.events != nil {
   501  		r.events <- bcRemovePeer{
   502  			peerID: peer.ID(),
   503  			reason: reason,
   504  		}
   505  	}
   506  }
   507  
   508  // GetChannels implements Reactor
   509  func (r *BlockchainReactor) GetChannels() []*p2p.ChannelDescriptor {
   510  	return []*p2p.ChannelDescriptor{
   511  		{
   512  			ID:                  BlockchainChannel,
   513  			Priority:            10,
   514  			SendQueueCapacity:   2000,
   515  			RecvBufferCapacity:  50 * 4096,
   516  			RecvMessageCapacity: bc.MaxMsgSize,
   517  		},
   518  	}
   519  }