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

     1  package v1
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"time"
     7  
     8  	"github.com/tendermint/tendermint/behaviour"
     9  	bc "github.com/tendermint/tendermint/blockchain"
    10  	"github.com/tendermint/tendermint/libs/log"
    11  	"github.com/tendermint/tendermint/p2p"
    12  	bcproto "github.com/tendermint/tendermint/proto/tendermint/blockchain"
    13  	sm "github.com/tendermint/tendermint/state"
    14  	"github.com/tendermint/tendermint/store"
    15  	"github.com/tendermint/tendermint/types"
    16  )
    17  
    18  const (
    19  	// BlockchainChannel is a channel for blocks and status updates (`BlockStore` height)
    20  	BlockchainChannel = byte(0x40)
    21  	trySyncIntervalMS = 10
    22  	trySendIntervalMS = 10
    23  
    24  	// ask for best height every 10s
    25  	statusUpdateIntervalSeconds = 10
    26  )
    27  
    28  var (
    29  	// Maximum number of requests that can be pending per peer, i.e. for which requests have been sent but blocks
    30  	// have not been received.
    31  	maxRequestsPerPeer = 20
    32  	// Maximum number of block requests for the reactor, pending or for which blocks have been received.
    33  	maxNumRequests = 64
    34  )
    35  
    36  type consensusReactor interface {
    37  	// for when we switch from blockchain reactor and fast sync to
    38  	// the consensus machine
    39  	SwitchToConsensus(state sm.State, skipWAL bool)
    40  }
    41  
    42  // BlockchainReactor handles long-term catchup syncing.
    43  type BlockchainReactor struct {
    44  	p2p.BaseReactor
    45  
    46  	initialState sm.State // immutable
    47  	state        sm.State
    48  
    49  	blockExec *sm.BlockExecutor
    50  	store     *store.BlockStore
    51  
    52  	fastSync    bool
    53  	stateSynced bool
    54  
    55  	fsm          *BcReactorFSM
    56  	blocksSynced uint64
    57  
    58  	// Receive goroutine forwards messages to this channel to be processed in the context of the poolRoutine.
    59  	messagesForFSMCh chan bcReactorMessage
    60  
    61  	// Switch goroutine may send RemovePeer to the blockchain reactor. This is an error message that is relayed
    62  	// to this channel to be processed in the context of the poolRoutine.
    63  	errorsForFSMCh chan bcReactorMessage
    64  
    65  	// This channel is used by the FSM and indirectly the block pool to report errors to the blockchain reactor and
    66  	// the switch.
    67  	eventsFromFSMCh chan bcFsmMessage
    68  
    69  	swReporter *behaviour.SwitchReporter
    70  }
    71  
    72  // NewBlockchainReactor returns new reactor instance.
    73  func NewBlockchainReactor(state sm.State, blockExec *sm.BlockExecutor, store *store.BlockStore,
    74  	fastSync bool) *BlockchainReactor {
    75  
    76  	if state.LastBlockHeight != store.Height() {
    77  		panic(fmt.Sprintf("state (%v) and store (%v) height mismatch", state.LastBlockHeight,
    78  			store.Height()))
    79  	}
    80  
    81  	const capacity = 1000
    82  	eventsFromFSMCh := make(chan bcFsmMessage, capacity)
    83  	messagesForFSMCh := make(chan bcReactorMessage, capacity)
    84  	errorsForFSMCh := make(chan bcReactorMessage, capacity)
    85  
    86  	startHeight := store.Height() + 1
    87  	bcR := &BlockchainReactor{
    88  		initialState:     state,
    89  		state:            state,
    90  		blockExec:        blockExec,
    91  		fastSync:         fastSync,
    92  		store:            store,
    93  		messagesForFSMCh: messagesForFSMCh,
    94  		eventsFromFSMCh:  eventsFromFSMCh,
    95  		errorsForFSMCh:   errorsForFSMCh,
    96  	}
    97  	fsm := NewFSM(startHeight, bcR)
    98  	bcR.fsm = fsm
    99  	bcR.BaseReactor = *p2p.NewBaseReactor("BlockchainReactor", bcR)
   100  	//bcR.swReporter = behaviour.NewSwitchReporter(bcR.BaseReactor.Switch)
   101  
   102  	return bcR
   103  }
   104  
   105  // bcReactorMessage is used by the reactor to send messages to the FSM.
   106  type bcReactorMessage struct {
   107  	event bReactorEvent
   108  	data  bReactorEventData
   109  }
   110  
   111  type bFsmEvent uint
   112  
   113  const (
   114  	// message type events
   115  	peerErrorEv = iota + 1
   116  	syncFinishedEv
   117  )
   118  
   119  type bFsmEventData struct {
   120  	peerID p2p.ID
   121  	err    error
   122  }
   123  
   124  // bcFsmMessage is used by the FSM to send messages to the reactor
   125  type bcFsmMessage struct {
   126  	event bFsmEvent
   127  	data  bFsmEventData
   128  }
   129  
   130  // SetLogger implements service.Service by setting the logger on reactor and pool.
   131  func (bcR *BlockchainReactor) SetLogger(l log.Logger) {
   132  	bcR.BaseService.Logger = l
   133  	bcR.fsm.SetLogger(l)
   134  }
   135  
   136  // OnStart implements service.Service.
   137  func (bcR *BlockchainReactor) OnStart() error {
   138  	bcR.swReporter = behaviour.NewSwitchReporter(bcR.BaseReactor.Switch)
   139  	if bcR.fastSync {
   140  		go bcR.poolRoutine()
   141  	}
   142  	return nil
   143  }
   144  
   145  // OnStop implements service.Service.
   146  func (bcR *BlockchainReactor) OnStop() {
   147  	_ = bcR.Stop()
   148  }
   149  
   150  // SwitchToFastSync is called by the state sync reactor when switching to fast sync.
   151  func (bcR *BlockchainReactor) SwitchToFastSync(state sm.State) error {
   152  	bcR.fastSync = true
   153  	bcR.initialState = state
   154  	bcR.state = state
   155  	bcR.stateSynced = true
   156  
   157  	bcR.fsm = NewFSM(state.LastBlockHeight+1, bcR)
   158  	bcR.fsm.SetLogger(bcR.Logger)
   159  	go bcR.poolRoutine()
   160  	return nil
   161  }
   162  
   163  // GetChannels implements Reactor
   164  func (bcR *BlockchainReactor) GetChannels() []*p2p.ChannelDescriptor {
   165  	return []*p2p.ChannelDescriptor{
   166  		{
   167  			ID:                  BlockchainChannel,
   168  			Priority:            10,
   169  			SendQueueCapacity:   2000,
   170  			RecvBufferCapacity:  50 * 4096,
   171  			RecvMessageCapacity: bc.MaxMsgSize,
   172  		},
   173  	}
   174  }
   175  
   176  // AddPeer implements Reactor by sending our state to peer.
   177  func (bcR *BlockchainReactor) AddPeer(peer p2p.Peer) {
   178  	msgBytes, err := bc.EncodeMsg(&bcproto.StatusResponse{
   179  		Base:   bcR.store.Base(),
   180  		Height: bcR.store.Height(),
   181  	})
   182  	if err != nil {
   183  		bcR.Logger.Error("could not convert msg to protobuf", "err", err)
   184  		return
   185  	}
   186  	peer.Send(BlockchainChannel, msgBytes)
   187  	// it's OK if send fails. will try later in poolRoutine
   188  
   189  	// peer is added to the pool once we receive the first
   190  	// bcStatusResponseMessage from the peer and call pool.updatePeer()
   191  }
   192  
   193  // sendBlockToPeer loads a block and sends it to the requesting peer.
   194  // If the block doesn't exist a bcNoBlockResponseMessage is sent.
   195  // If all nodes are honest, no node should be requesting for a block that doesn't exist.
   196  func (bcR *BlockchainReactor) sendBlockToPeer(msg *bcproto.BlockRequest,
   197  	src p2p.Peer) (queued bool) {
   198  
   199  	block := bcR.store.LoadBlock(msg.Height)
   200  	if block != nil {
   201  		pbbi, err := block.ToProto()
   202  		if err != nil {
   203  			bcR.Logger.Error("Could not send block message to peer", "err", err)
   204  			return false
   205  		}
   206  		msgBytes, err := bc.EncodeMsg(&bcproto.BlockResponse{Block: pbbi})
   207  		if err != nil {
   208  			bcR.Logger.Error("unable to marshal msg", "err", err)
   209  			return false
   210  		}
   211  		return src.TrySend(BlockchainChannel, msgBytes)
   212  	}
   213  
   214  	bcR.Logger.Info("peer asking for a block we don't have", "src", src, "height", msg.Height)
   215  
   216  	msgBytes, err := bc.EncodeMsg(&bcproto.NoBlockResponse{Height: msg.Height})
   217  	if err != nil {
   218  		bcR.Logger.Error("unable to marshal msg", "err", err)
   219  		return false
   220  	}
   221  	return src.TrySend(BlockchainChannel, msgBytes)
   222  }
   223  
   224  func (bcR *BlockchainReactor) sendStatusResponseToPeer(msg *bcproto.StatusRequest, src p2p.Peer) (queued bool) {
   225  	msgBytes, err := bc.EncodeMsg(&bcproto.StatusResponse{
   226  		Base:   bcR.store.Base(),
   227  		Height: bcR.store.Height(),
   228  	})
   229  	if err != nil {
   230  		bcR.Logger.Error("unable to marshal msg", "err", err)
   231  		return false
   232  	}
   233  
   234  	return src.TrySend(BlockchainChannel, msgBytes)
   235  }
   236  
   237  // RemovePeer implements Reactor by removing peer from the pool.
   238  func (bcR *BlockchainReactor) RemovePeer(peer p2p.Peer, reason interface{}) {
   239  	msgData := bcReactorMessage{
   240  		event: peerRemoveEv,
   241  		data: bReactorEventData{
   242  			peerID: peer.ID(),
   243  			err:    errSwitchRemovesPeer,
   244  		},
   245  	}
   246  	bcR.errorsForFSMCh <- msgData
   247  }
   248  
   249  // Receive implements Reactor by handling 4 types of messages (look below).
   250  func (bcR *BlockchainReactor) Receive(chID byte, src p2p.Peer, msgBytes []byte) {
   251  	msg, err := bc.DecodeMsg(msgBytes)
   252  	if err != nil {
   253  		bcR.Logger.Error("error decoding message",
   254  			"src", src, "chId", chID, "msg", msg, "err", err, "bytes", msgBytes)
   255  		_ = bcR.swReporter.Report(behaviour.BadMessage(src.ID(), err.Error()))
   256  		return
   257  	}
   258  
   259  	if err = bc.ValidateMsg(msg); err != nil {
   260  		bcR.Logger.Error("peer sent us invalid msg", "peer", src, "msg", msg, "err", err)
   261  		_ = bcR.swReporter.Report(behaviour.BadMessage(src.ID(), err.Error()))
   262  		return
   263  	}
   264  
   265  	bcR.Logger.Debug("Receive", "src", src, "chID", chID, "msg", msg)
   266  
   267  	switch msg := msg.(type) {
   268  	case *bcproto.BlockRequest:
   269  		if queued := bcR.sendBlockToPeer(msg, src); !queued {
   270  			// Unfortunately not queued since the queue is full.
   271  			bcR.Logger.Error("Could not send block message to peer", "src", src, "height", msg.Height)
   272  		}
   273  
   274  	case *bcproto.StatusRequest:
   275  		// Send peer our state.
   276  		if queued := bcR.sendStatusResponseToPeer(msg, src); !queued {
   277  			// Unfortunately not queued since the queue is full.
   278  			bcR.Logger.Error("Could not send status message to peer", "src", src)
   279  		}
   280  
   281  	case *bcproto.BlockResponse:
   282  		bi, err := types.BlockFromProto(msg.Block)
   283  		if err != nil {
   284  			bcR.Logger.Error("error transition block from protobuf", "err", err)
   285  			return
   286  		}
   287  		msgForFSM := bcReactorMessage{
   288  			event: blockResponseEv,
   289  			data: bReactorEventData{
   290  				peerID: src.ID(),
   291  				height: bi.Height,
   292  				block:  bi,
   293  				length: len(msgBytes),
   294  			},
   295  		}
   296  		bcR.Logger.Info("Received", "src", src, "height", bi.Height)
   297  		bcR.messagesForFSMCh <- msgForFSM
   298  
   299  	case *bcproto.StatusResponse:
   300  		// Got a peer status. Unverified.
   301  		msgForFSM := bcReactorMessage{
   302  			event: statusResponseEv,
   303  			data: bReactorEventData{
   304  				peerID: src.ID(),
   305  				height: msg.Height,
   306  				length: len(msgBytes),
   307  			},
   308  		}
   309  		bcR.messagesForFSMCh <- msgForFSM
   310  
   311  	default:
   312  		bcR.Logger.Error(fmt.Sprintf("unknown message type %v", reflect.TypeOf(msg)))
   313  	}
   314  }
   315  
   316  // processBlocksRoutine processes blocks until signlaed to stop over the stopProcessing channel
   317  func (bcR *BlockchainReactor) processBlocksRoutine(stopProcessing chan struct{}) {
   318  
   319  	processReceivedBlockTicker := time.NewTicker(trySyncIntervalMS * time.Millisecond)
   320  	doProcessBlockCh := make(chan struct{}, 1)
   321  
   322  	lastHundred := time.Now()
   323  	lastRate := 0.0
   324  
   325  ForLoop:
   326  	for {
   327  		select {
   328  		case <-stopProcessing:
   329  			bcR.Logger.Info("finishing block execution")
   330  			break ForLoop
   331  		case <-processReceivedBlockTicker.C: // try to execute blocks
   332  			select {
   333  			case doProcessBlockCh <- struct{}{}:
   334  			default:
   335  			}
   336  		case <-doProcessBlockCh:
   337  			for {
   338  				err := bcR.processBlock()
   339  				if err == errMissingBlock {
   340  					break
   341  				}
   342  				// Notify FSM of block processing result.
   343  				msgForFSM := bcReactorMessage{
   344  					event: processedBlockEv,
   345  					data: bReactorEventData{
   346  						err: err,
   347  					},
   348  				}
   349  				_ = bcR.fsm.Handle(&msgForFSM)
   350  
   351  				if err != nil {
   352  					break
   353  				}
   354  
   355  				bcR.blocksSynced++
   356  				if bcR.blocksSynced%100 == 0 {
   357  					lastRate = 0.9*lastRate + 0.1*(100/time.Since(lastHundred).Seconds())
   358  					height, maxPeerHeight := bcR.fsm.Status()
   359  					bcR.Logger.Info("Fast Sync Rate", "height", height,
   360  						"max_peer_height", maxPeerHeight, "blocks/s", lastRate)
   361  					lastHundred = time.Now()
   362  				}
   363  			}
   364  		}
   365  	}
   366  }
   367  
   368  // poolRoutine receives and handles messages from the Receive() routine and from the FSM.
   369  func (bcR *BlockchainReactor) poolRoutine() {
   370  
   371  	bcR.fsm.Start()
   372  
   373  	sendBlockRequestTicker := time.NewTicker(trySendIntervalMS * time.Millisecond)
   374  	statusUpdateTicker := time.NewTicker(statusUpdateIntervalSeconds * time.Second)
   375  
   376  	stopProcessing := make(chan struct{}, 1)
   377  	go bcR.processBlocksRoutine(stopProcessing)
   378  
   379  ForLoop:
   380  	for {
   381  		select {
   382  
   383  		case <-sendBlockRequestTicker.C:
   384  			if !bcR.fsm.NeedsBlocks() {
   385  				continue
   386  			}
   387  			_ = bcR.fsm.Handle(&bcReactorMessage{
   388  				event: makeRequestsEv,
   389  				data: bReactorEventData{
   390  					maxNumRequests: maxNumRequests}})
   391  
   392  		case <-statusUpdateTicker.C:
   393  			// Ask for status updates.
   394  			go bcR.sendStatusRequest()
   395  
   396  		case msg := <-bcR.messagesForFSMCh:
   397  			// Sent from the Receive() routine when status (statusResponseEv) and
   398  			// block (blockResponseEv) response events are received
   399  			_ = bcR.fsm.Handle(&msg)
   400  
   401  		case msg := <-bcR.errorsForFSMCh:
   402  			// Sent from the switch.RemovePeer() routine (RemovePeerEv) and
   403  			// FSM state timer expiry routine (stateTimeoutEv).
   404  			_ = bcR.fsm.Handle(&msg)
   405  
   406  		case msg := <-bcR.eventsFromFSMCh:
   407  			switch msg.event {
   408  			case syncFinishedEv:
   409  				stopProcessing <- struct{}{}
   410  				// Sent from the FSM when it enters finished state.
   411  				break ForLoop
   412  			case peerErrorEv:
   413  				// Sent from the FSM when it detects peer error
   414  				bcR.reportPeerErrorToSwitch(msg.data.err, msg.data.peerID)
   415  				if msg.data.err == errNoPeerResponse {
   416  					// Sent from the peer timeout handler routine
   417  					_ = bcR.fsm.Handle(&bcReactorMessage{
   418  						event: peerRemoveEv,
   419  						data: bReactorEventData{
   420  							peerID: msg.data.peerID,
   421  							err:    msg.data.err,
   422  						},
   423  					})
   424  				}
   425  				// else {
   426  				// For slow peers, or errors due to blocks received from wrong peer
   427  				// the FSM had already removed the peers
   428  				// }
   429  			default:
   430  				bcR.Logger.Error("Event from FSM not supported", "type", msg.event)
   431  			}
   432  
   433  		case <-bcR.Quit():
   434  			break ForLoop
   435  		}
   436  	}
   437  }
   438  
   439  func (bcR *BlockchainReactor) reportPeerErrorToSwitch(err error, peerID p2p.ID) {
   440  	peer := bcR.Switch.Peers().Get(peerID)
   441  	if peer != nil {
   442  		_ = bcR.swReporter.Report(behaviour.BadMessage(peerID, err.Error()))
   443  	}
   444  }
   445  
   446  func (bcR *BlockchainReactor) processBlock() error {
   447  
   448  	first, second, err := bcR.fsm.FirstTwoBlocks()
   449  	if err != nil {
   450  		// We need both to sync the first block.
   451  		return err
   452  	}
   453  
   454  	chainID := bcR.initialState.ChainID
   455  
   456  	firstParts := first.MakePartSet(types.BlockPartSizeBytes)
   457  	firstPartSetHeader := firstParts.Header()
   458  	firstID := types.BlockID{Hash: first.Hash(), PartSetHeader: firstPartSetHeader}
   459  	// Finally, verify the first block using the second's commit
   460  	// NOTE: we can probably make this more efficient, but note that calling
   461  	// first.Hash() doesn't verify the tx contents, so MakePartSet() is
   462  	// currently necessary.
   463  	err = bcR.state.Validators.VerifyCommit(chainID, firstID, first.Height, second.LastCommit)
   464  	if err != nil {
   465  		bcR.Logger.Error("error during commit verification", "err", err,
   466  			"first", first.Height, "second", second.Height)
   467  		return errBlockVerificationFailure
   468  	}
   469  
   470  	bcR.store.SaveBlock(first, firstParts, second.LastCommit)
   471  
   472  	bcR.state, _, err = bcR.blockExec.ApplyBlock(bcR.state, firstID, first)
   473  	if err != nil {
   474  		panic(fmt.Sprintf("failed to process committed block (%d:%X): %v", first.Height, first.Hash(), err))
   475  	}
   476  
   477  	return nil
   478  }
   479  
   480  // Implements bcRNotifier
   481  // sendStatusRequest broadcasts `BlockStore` height.
   482  func (bcR *BlockchainReactor) sendStatusRequest() {
   483  	msgBytes, err := bc.EncodeMsg(&bcproto.StatusRequest{})
   484  	if err != nil {
   485  		panic(err)
   486  	}
   487  	bcR.Switch.Broadcast(BlockchainChannel, msgBytes)
   488  }
   489  
   490  // Implements bcRNotifier
   491  // BlockRequest sends `BlockRequest` height.
   492  func (bcR *BlockchainReactor) sendBlockRequest(peerID p2p.ID, height int64) error {
   493  	peer := bcR.Switch.Peers().Get(peerID)
   494  	if peer == nil {
   495  		return errNilPeerForBlockRequest
   496  	}
   497  
   498  	msgBytes, err := bc.EncodeMsg(&bcproto.BlockRequest{Height: height})
   499  	if err != nil {
   500  		return err
   501  	}
   502  	queued := peer.TrySend(BlockchainChannel, msgBytes)
   503  	if !queued {
   504  		return errSendQueueFull
   505  	}
   506  	return nil
   507  }
   508  
   509  // Implements bcRNotifier
   510  func (bcR *BlockchainReactor) switchToConsensus() {
   511  	conR, ok := bcR.Switch.Reactor("CONSENSUS").(consensusReactor)
   512  	if ok {
   513  		conR.SwitchToConsensus(bcR.state, bcR.blocksSynced > 0 || bcR.stateSynced)
   514  		bcR.eventsFromFSMCh <- bcFsmMessage{event: syncFinishedEv}
   515  	}
   516  	// else {
   517  	// Should only happen during testing.
   518  	// }
   519  }
   520  
   521  // Implements bcRNotifier
   522  // Called by FSM and pool:
   523  // - pool calls when it detects slow peer or when peer times out
   524  // - FSM calls when:
   525  //    - adding a block (addBlock) fails
   526  //    - reactor processing of a block reports failure and FSM sends back the peers of first and second blocks
   527  func (bcR *BlockchainReactor) sendPeerError(err error, peerID p2p.ID) {
   528  	bcR.Logger.Info("sendPeerError:", "peer", peerID, "error", err)
   529  	msgData := bcFsmMessage{
   530  		event: peerErrorEv,
   531  		data: bFsmEventData{
   532  			peerID: peerID,
   533  			err:    err,
   534  		},
   535  	}
   536  	bcR.eventsFromFSMCh <- msgData
   537  }
   538  
   539  // Implements bcRNotifier
   540  func (bcR *BlockchainReactor) resetStateTimer(name string, timer **time.Timer, timeout time.Duration) {
   541  	if timer == nil {
   542  		panic("nil timer pointer parameter")
   543  	}
   544  	if *timer == nil {
   545  		*timer = time.AfterFunc(timeout, func() {
   546  			msg := bcReactorMessage{
   547  				event: stateTimeoutEv,
   548  				data: bReactorEventData{
   549  					stateName: name,
   550  				},
   551  			}
   552  			bcR.errorsForFSMCh <- msg
   553  		})
   554  	} else {
   555  		(*timer).Reset(timeout)
   556  	}
   557  }