github.com/franono/tendermint@v0.32.2-0.20200527150959-749313264ce9/blockchain/v2/reactor.go (about)

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