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