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

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