github.com/bytom/bytom@v1.1.2-0.20221014091027-bbcba3df6075/netsync/peers/peer.go (about)

     1  package peers
     2  
     3  import (
     4  	"encoding/hex"
     5  	"net"
     6  	"reflect"
     7  	"sync"
     8  
     9  	log "github.com/sirupsen/logrus"
    10  	"github.com/tendermint/tmlibs/flowrate"
    11  	"gopkg.in/fatih/set.v0"
    12  
    13  	"github.com/bytom/bytom/consensus"
    14  	"github.com/bytom/bytom/errors"
    15  	msgs "github.com/bytom/bytom/netsync/messages"
    16  	"github.com/bytom/bytom/protocol/bc"
    17  	"github.com/bytom/bytom/protocol/bc/types"
    18  )
    19  
    20  const (
    21  	maxKnownTxs           = 32768 // Maximum transactions hashes to keep in the known list (prevent DOS)
    22  	maxKnownSignatures    = 1024  // Maximum block signatures to keep in the known list (prevent DOS)
    23  	maxKnownBlocks        = 1024  // Maximum block hashes to keep in the known list (prevent DOS)
    24  	maxFilterAddressSize  = 50
    25  	maxFilterAddressCount = 1000
    26  
    27  	logModule = "peers"
    28  )
    29  
    30  var (
    31  	errSendStatusMsg = errors.New("send status msg fail")
    32  	ErrPeerMisbehave = errors.New("peer is misbehave")
    33  	ErrNoValidPeer   = errors.New("Can't find valid fast sync peer")
    34  )
    35  
    36  //BasePeer is the interface for connection level peer
    37  type BasePeer interface {
    38  	Moniker() string
    39  	Addr() net.Addr
    40  	ID() string
    41  	RemoteAddrHost() string
    42  	ServiceFlag() consensus.ServiceFlag
    43  	TrafficStatus() (*flowrate.Status, *flowrate.Status)
    44  	TrySend(byte, interface{}) bool
    45  	IsLAN() bool
    46  }
    47  
    48  //BasePeerSet is the intergace for connection level peer manager
    49  type BasePeerSet interface {
    50  	StopPeerGracefully(string)
    51  	IsBanned(ip string, level byte, reason string) bool
    52  }
    53  
    54  type BroadcastMsg interface {
    55  	FilterTargetPeers(ps *PeerSet) []string
    56  	MarkSendRecord(ps *PeerSet, peers []string)
    57  	GetChan() byte
    58  	GetMsg() interface{}
    59  	MsgString() string
    60  }
    61  
    62  // PeerInfo indicate peer status snap
    63  type PeerInfo struct {
    64  	ID                  string `json:"peer_id"`
    65  	Moniker             string `json:"moniker"`
    66  	RemoteAddr          string `json:"remote_addr"`
    67  	Height              uint64 `json:"height"`
    68  	Ping                string `json:"ping"`
    69  	Duration            string `json:"duration"`
    70  	TotalSent           int64  `json:"total_sent"`
    71  	TotalReceived       int64  `json:"total_received"`
    72  	AverageSentRate     int64  `json:"average_sent_rate"`
    73  	AverageReceivedRate int64  `json:"average_received_rate"`
    74  	CurrentSentRate     int64  `json:"current_sent_rate"`
    75  	CurrentReceivedRate int64  `json:"current_received_rate"`
    76  }
    77  
    78  type Peer struct {
    79  	BasePeer
    80  	mtx             sync.RWMutex
    81  	services        consensus.ServiceFlag
    82  	bestHeight      uint64
    83  	bestHash        *bc.Hash
    84  	justifiedHeight uint64
    85  	justifiedHash   *bc.Hash
    86  	knownTxs        *set.Set // Set of transaction hashes known to be known by this peer
    87  	knownBlocks     *set.Set // Set of block hashes known to be known by this peer
    88  	knownSignatures *set.Set // Set of block signatures known to be known by this peer
    89  	knownStatus     uint64   // Set of chain status known to be known by this peer
    90  	filterAdds      *set.Set // Set of addresses that the spv node cares about.
    91  }
    92  
    93  func newPeer(basePeer BasePeer) *Peer {
    94  	return &Peer{
    95  		BasePeer:        basePeer,
    96  		services:        basePeer.ServiceFlag(),
    97  		knownTxs:        set.New(),
    98  		knownBlocks:     set.New(),
    99  		knownSignatures: set.New(),
   100  		filterAdds:      set.New(),
   101  	}
   102  }
   103  
   104  func (p *Peer) Height() uint64 {
   105  	p.mtx.RLock()
   106  	defer p.mtx.RUnlock()
   107  
   108  	return p.bestHeight
   109  }
   110  
   111  func (p *Peer) JustifiedHeight() uint64 {
   112  	p.mtx.RLock()
   113  	defer p.mtx.RUnlock()
   114  
   115  	return p.justifiedHeight
   116  }
   117  
   118  func (p *Peer) AddFilterAddress(address []byte) {
   119  	p.mtx.Lock()
   120  	defer p.mtx.Unlock()
   121  
   122  	if p.filterAdds.Size() >= maxFilterAddressCount {
   123  		log.WithField("module", logModule).Warn("the count of filter addresses is greater than limit")
   124  		return
   125  	}
   126  	if len(address) > maxFilterAddressSize {
   127  		log.WithField("module", logModule).Warn("the size of filter address is greater than limit")
   128  		return
   129  	}
   130  
   131  	p.filterAdds.Add(hex.EncodeToString(address))
   132  }
   133  
   134  func (p *Peer) AddFilterAddresses(addresses [][]byte) {
   135  	if !p.filterAdds.IsEmpty() {
   136  		p.filterAdds.Clear()
   137  	}
   138  	for _, address := range addresses {
   139  		p.AddFilterAddress(address)
   140  	}
   141  }
   142  
   143  func (p *Peer) FilterClear() {
   144  	p.filterAdds.Clear()
   145  }
   146  
   147  func (p *Peer) GetBlockByHeight(height uint64) bool {
   148  	msg := struct{ msgs.BlockchainMessage }{&msgs.GetBlockMessage{Height: height}}
   149  	return p.TrySend(msgs.BlockchainChannel, msg)
   150  }
   151  
   152  func (p *Peer) GetBlocks(locator []*bc.Hash, stopHash *bc.Hash) bool {
   153  	msg := struct{ msgs.BlockchainMessage }{msgs.NewGetBlocksMessage(locator, stopHash)}
   154  	return p.TrySend(msgs.BlockchainChannel, msg)
   155  }
   156  
   157  func (p *Peer) GetHeaders(locator []*bc.Hash, stopHash *bc.Hash, skip uint64) bool {
   158  	msg := struct{ msgs.BlockchainMessage }{msgs.NewGetHeadersMessage(locator, stopHash, skip)}
   159  	return p.TrySend(msgs.BlockchainChannel, msg)
   160  }
   161  
   162  func (p *Peer) GetPeerInfo() *PeerInfo {
   163  	p.mtx.RLock()
   164  	defer p.mtx.RUnlock()
   165  
   166  	sentStatus, receivedStatus := p.TrafficStatus()
   167  	ping := sentStatus.Idle - receivedStatus.Idle
   168  	if receivedStatus.Idle > sentStatus.Idle {
   169  		ping = -ping
   170  	}
   171  
   172  	return &PeerInfo{
   173  		ID:                  p.ID(),
   174  		Moniker:             p.BasePeer.Moniker(),
   175  		RemoteAddr:          p.Addr().String(),
   176  		Height:              p.bestHeight,
   177  		Ping:                ping.String(),
   178  		Duration:            sentStatus.Duration.String(),
   179  		TotalSent:           sentStatus.Bytes,
   180  		TotalReceived:       receivedStatus.Bytes,
   181  		AverageSentRate:     sentStatus.AvgRate,
   182  		AverageReceivedRate: receivedStatus.AvgRate,
   183  		CurrentSentRate:     sentStatus.CurRate,
   184  		CurrentReceivedRate: receivedStatus.CurRate,
   185  	}
   186  }
   187  
   188  func (p *Peer) getRelatedTxs(txs []*types.Tx) []*types.Tx {
   189  	var relatedTxs []*types.Tx
   190  	for _, tx := range txs {
   191  		if p.isRelatedTx(tx) {
   192  			relatedTxs = append(relatedTxs, tx)
   193  		}
   194  	}
   195  	return relatedTxs
   196  }
   197  
   198  func (p *Peer) isRelatedTx(tx *types.Tx) bool {
   199  	for _, input := range tx.Inputs {
   200  		switch inp := input.TypedInput.(type) {
   201  		case *types.SpendInput:
   202  			if p.filterAdds.Has(hex.EncodeToString(inp.ControlProgram)) {
   203  				return true
   204  			}
   205  		}
   206  	}
   207  	for _, output := range tx.Outputs {
   208  		if p.filterAdds.Has(hex.EncodeToString(output.ControlProgram)) {
   209  			return true
   210  		}
   211  	}
   212  	return false
   213  }
   214  
   215  func (p *Peer) isSPVNode() bool {
   216  	return !p.services.IsEnable(consensus.SFFullNode)
   217  }
   218  
   219  func (p *Peer) MarkBlock(hash *bc.Hash) {
   220  	p.mtx.Lock()
   221  	defer p.mtx.Unlock()
   222  
   223  	for p.knownBlocks.Size() >= maxKnownBlocks {
   224  		p.knownBlocks.Pop()
   225  	}
   226  	p.knownBlocks.Add(hash.String())
   227  }
   228  
   229  func (p *Peer) markNewStatus(height uint64) {
   230  	p.mtx.Lock()
   231  	defer p.mtx.Unlock()
   232  
   233  	p.knownStatus = height
   234  }
   235  
   236  func (p *Peer) markSign(signature []byte) {
   237  	p.mtx.Lock()
   238  	defer p.mtx.Unlock()
   239  
   240  	for p.knownSignatures.Size() >= maxKnownSignatures {
   241  		p.knownSignatures.Pop()
   242  	}
   243  	p.knownSignatures.Add(hex.EncodeToString(signature))
   244  }
   245  
   246  func (p *Peer) markTransaction(hash *bc.Hash) {
   247  	p.mtx.Lock()
   248  	defer p.mtx.Unlock()
   249  
   250  	for p.knownTxs.Size() >= maxKnownTxs {
   251  		p.knownTxs.Pop()
   252  	}
   253  	p.knownTxs.Add(hash.String())
   254  }
   255  
   256  func (p *Peer) SendBlock(block *types.Block) (bool, error) {
   257  	msg, err := msgs.NewBlockMessage(block)
   258  	if err != nil {
   259  		return false, errors.Wrap(err, "fail on NewBlockMessage")
   260  	}
   261  
   262  	ok := p.TrySend(msgs.BlockchainChannel, struct{ msgs.BlockchainMessage }{msg})
   263  	if ok {
   264  		blcokHash := block.Hash()
   265  		p.knownBlocks.Add(blcokHash.String())
   266  	}
   267  	return ok, nil
   268  }
   269  
   270  func (p *Peer) SendBlocks(blocks []*types.Block) (bool, error) {
   271  	msg, err := msgs.NewBlocksMessage(blocks)
   272  	if err != nil {
   273  		return false, errors.Wrap(err, "fail on NewBlocksMessage")
   274  	}
   275  
   276  	if ok := p.TrySend(msgs.BlockchainChannel, struct{ msgs.BlockchainMessage }{msg}); !ok {
   277  		return ok, nil
   278  	}
   279  
   280  	for _, block := range blocks {
   281  		blcokHash := block.Hash()
   282  		p.knownBlocks.Add(blcokHash.String())
   283  	}
   284  	return true, nil
   285  }
   286  
   287  func (p *Peer) SendHeaders(headers []*types.BlockHeader) (bool, error) {
   288  	msg, err := msgs.NewHeadersMessage(headers)
   289  	if err != nil {
   290  		return false, errors.New("fail on NewHeadersMessage")
   291  	}
   292  
   293  	ok := p.TrySend(msgs.BlockchainChannel, struct{ msgs.BlockchainMessage }{msg})
   294  	return ok, nil
   295  }
   296  
   297  func (p *Peer) SendMerkleBlock(block *types.Block) (bool, error) {
   298  	msg := msgs.NewMerkleBlockMessage()
   299  	if err := msg.SetRawBlockHeader(block.BlockHeader); err != nil {
   300  		return false, err
   301  	}
   302  
   303  	relatedTxs := p.getRelatedTxs(block.Transactions)
   304  
   305  	txHashes, txFlags := types.GetTxMerkleTreeProof(block.Transactions, relatedTxs)
   306  	if err := msg.SetTxInfo(txHashes, txFlags, relatedTxs); err != nil {
   307  		return false, nil
   308  	}
   309  
   310  	ok := p.TrySend(msgs.BlockchainChannel, struct{ msgs.BlockchainMessage }{msg})
   311  	return ok, nil
   312  }
   313  
   314  func (p *Peer) SendTransactions(txs []*types.Tx) error {
   315  	validTxs := make([]*types.Tx, 0, len(txs))
   316  	for i, tx := range txs {
   317  		if p.isSPVNode() && !p.isRelatedTx(tx) || p.knownTxs.Has(tx.ID.String()) {
   318  			continue
   319  		}
   320  
   321  		validTxs = append(validTxs, tx)
   322  		if len(validTxs) != msgs.TxsMsgMaxTxNum && i != len(txs)-1 {
   323  			continue
   324  		}
   325  
   326  		msg, err := msgs.NewTransactionsMessage(validTxs)
   327  		if err != nil {
   328  			return err
   329  		}
   330  
   331  		if ok := p.TrySend(msgs.BlockchainChannel, struct{ msgs.BlockchainMessage }{msg}); !ok {
   332  			return errors.New("failed to send txs msg")
   333  		}
   334  
   335  		for _, validTx := range validTxs {
   336  			p.knownTxs.Add(validTx.ID.String())
   337  		}
   338  
   339  		validTxs = make([]*types.Tx, 0, len(txs))
   340  	}
   341  
   342  	return nil
   343  }
   344  
   345  func (p *Peer) SendStatus(bestHeader, justifiedHeader *types.BlockHeader) error {
   346  	msg := msgs.NewStatusMessage(bestHeader, justifiedHeader)
   347  	if ok := p.TrySend(msgs.BlockchainChannel, struct{ msgs.BlockchainMessage }{msg}); !ok {
   348  		return errSendStatusMsg
   349  	}
   350  	p.markNewStatus(bestHeader.Height)
   351  	return nil
   352  }
   353  
   354  func (p *Peer) SetBestStatus(bestHeight uint64, bestHash *bc.Hash) {
   355  	p.mtx.Lock()
   356  	defer p.mtx.Unlock()
   357  
   358  	p.bestHeight = bestHeight
   359  	p.bestHash = bestHash
   360  }
   361  
   362  func (p *Peer) SetJustifiedStatus(justifiedHeight uint64, justifiedHash *bc.Hash) {
   363  	p.mtx.Lock()
   364  	defer p.mtx.Unlock()
   365  
   366  	p.justifiedHeight = justifiedHeight
   367  	p.justifiedHash = justifiedHash
   368  }
   369  
   370  type PeerSet struct {
   371  	BasePeerSet
   372  	mtx   sync.RWMutex
   373  	peers map[string]*Peer
   374  }
   375  
   376  // newPeerSet creates a new peer set to track the active participants.
   377  func NewPeerSet(basePeerSet BasePeerSet) *PeerSet {
   378  	return &PeerSet{
   379  		BasePeerSet: basePeerSet,
   380  		peers:       make(map[string]*Peer),
   381  	}
   382  }
   383  
   384  func (ps *PeerSet) ProcessIllegal(peerID string, level byte, reason string) {
   385  	ps.mtx.Lock()
   386  	peer := ps.peers[peerID]
   387  	ps.mtx.Unlock()
   388  
   389  	if peer == nil {
   390  		return
   391  	}
   392  
   393  	if banned := ps.IsBanned(peer.RemoteAddrHost(), level, reason); banned {
   394  		ps.RemovePeer(peerID)
   395  	}
   396  	return
   397  }
   398  
   399  func (ps *PeerSet) AddPeer(peer BasePeer) {
   400  	ps.mtx.Lock()
   401  	defer ps.mtx.Unlock()
   402  
   403  	if _, ok := ps.peers[peer.ID()]; !ok {
   404  		ps.peers[peer.ID()] = newPeer(peer)
   405  		return
   406  	}
   407  	log.WithField("module", logModule).Warning("add existing peer to blockKeeper")
   408  }
   409  
   410  func (ps *PeerSet) BestPeer(flag consensus.ServiceFlag) *Peer {
   411  	ps.mtx.RLock()
   412  	defer ps.mtx.RUnlock()
   413  
   414  	var bestPeer *Peer
   415  	for _, p := range ps.peers {
   416  		if !p.services.IsEnable(flag) {
   417  			continue
   418  		}
   419  		if bestPeer == nil || p.JustifiedHeight() > bestPeer.JustifiedHeight() ||
   420  			(p.JustifiedHeight() == bestPeer.JustifiedHeight() && p.bestHeight > bestPeer.bestHeight) ||
   421  			(p.JustifiedHeight() == bestPeer.JustifiedHeight() && p.bestHeight == bestPeer.bestHeight && p.IsLAN()) {
   422  			bestPeer = p
   423  		}
   424  	}
   425  	return bestPeer
   426  }
   427  
   428  //SendMsg send message to the target peer.
   429  func (ps *PeerSet) SendMsg(peerID string, msgChannel byte, msg interface{}) bool {
   430  	peer := ps.GetPeer(peerID)
   431  	if peer == nil {
   432  		return false
   433  	}
   434  
   435  	ok := peer.TrySend(msgChannel, msg)
   436  	if !ok {
   437  		ps.RemovePeer(peerID)
   438  	}
   439  	return ok
   440  }
   441  
   442  //BroadcastMsg Broadcast message to the target peers
   443  // and mark the message send record
   444  func (ps *PeerSet) BroadcastMsg(bm BroadcastMsg) error {
   445  	//filter target peers
   446  	peers := bm.FilterTargetPeers(ps)
   447  
   448  	//broadcast to target peers
   449  	peersSuccess := make([]string, 0)
   450  	for _, peer := range peers {
   451  		if ok := ps.SendMsg(peer, bm.GetChan(), bm.GetMsg()); !ok {
   452  			log.WithFields(log.Fields{"module": logModule, "peer": peer, "type": reflect.TypeOf(bm.GetMsg()), "message": bm.MsgString()}).Warning("send message to peer error")
   453  			continue
   454  		}
   455  		peersSuccess = append(peersSuccess, peer)
   456  	}
   457  
   458  	//mark the message send record
   459  	bm.MarkSendRecord(ps, peersSuccess)
   460  	return nil
   461  }
   462  
   463  func (ps *PeerSet) BroadcastNewStatus(bestHeader, justifiedHeader *types.BlockHeader) error {
   464  	msg := msgs.NewStatusMessage(bestHeader, justifiedHeader)
   465  	peers := ps.peersWithoutNewStatus(bestHeader.Height)
   466  	for _, peer := range peers {
   467  		if ok := peer.TrySend(msgs.BlockchainChannel, struct{ msgs.BlockchainMessage }{msg}); !ok {
   468  			ps.RemovePeer(peer.ID())
   469  			continue
   470  		}
   471  
   472  		peer.markNewStatus(bestHeader.Height)
   473  	}
   474  	return nil
   475  }
   476  
   477  func (ps *PeerSet) BroadcastTx(tx *types.Tx) error {
   478  	msg, err := msgs.NewTransactionMessage(tx)
   479  	if err != nil {
   480  		return errors.Wrap(err, "fail on broadcast tx")
   481  	}
   482  
   483  	peers := ps.peersWithoutTx(&tx.ID)
   484  	for _, peer := range peers {
   485  		if peer.isSPVNode() && !peer.isRelatedTx(tx) {
   486  			continue
   487  		}
   488  		if ok := peer.TrySend(msgs.BlockchainChannel, struct{ msgs.BlockchainMessage }{msg}); !ok {
   489  			log.WithFields(log.Fields{
   490  				"module":  logModule,
   491  				"peer":    peer.Addr(),
   492  				"type":    reflect.TypeOf(msg),
   493  				"message": msg.String(),
   494  			}).Warning("send message to peer error")
   495  			ps.RemovePeer(peer.ID())
   496  			continue
   497  		}
   498  		peer.markTransaction(&tx.ID)
   499  	}
   500  	return nil
   501  }
   502  
   503  // Peer retrieves the registered peer with the given id.
   504  func (ps *PeerSet) GetPeer(id string) *Peer {
   505  	ps.mtx.RLock()
   506  	defer ps.mtx.RUnlock()
   507  	return ps.peers[id]
   508  }
   509  
   510  func (ps *PeerSet) GetPeersByHeight(height uint64) []*Peer {
   511  	ps.mtx.RLock()
   512  	defer ps.mtx.RUnlock()
   513  
   514  	peers := []*Peer{}
   515  	for _, peer := range ps.peers {
   516  		if peer.Height() >= height {
   517  			peers = append(peers, peer)
   518  		}
   519  	}
   520  	return peers
   521  }
   522  
   523  func (ps *PeerSet) GetPeerInfos() []*PeerInfo {
   524  	ps.mtx.RLock()
   525  	defer ps.mtx.RUnlock()
   526  
   527  	result := []*PeerInfo{}
   528  	for _, peer := range ps.peers {
   529  		result = append(result, peer.GetPeerInfo())
   530  	}
   531  	return result
   532  }
   533  
   534  func (ps *PeerSet) MarkBlock(peerID string, hash *bc.Hash) {
   535  	peer := ps.GetPeer(peerID)
   536  	if peer == nil {
   537  		return
   538  	}
   539  	peer.MarkBlock(hash)
   540  }
   541  
   542  func (ps *PeerSet) MarkBlockVerification(peerID string, signature []byte) {
   543  	peer := ps.GetPeer(peerID)
   544  	if peer == nil {
   545  		return
   546  	}
   547  	peer.markSign(signature)
   548  }
   549  
   550  func (ps *PeerSet) MarkStatus(peerID string, height uint64) {
   551  	peer := ps.GetPeer(peerID)
   552  	if peer == nil {
   553  		return
   554  	}
   555  	peer.markNewStatus(height)
   556  }
   557  
   558  func (ps *PeerSet) MarkTx(peerID string, txHash bc.Hash) {
   559  	ps.mtx.Lock()
   560  	peer := ps.peers[peerID]
   561  	ps.mtx.Unlock()
   562  
   563  	if peer == nil {
   564  		return
   565  	}
   566  	peer.markTransaction(&txHash)
   567  }
   568  
   569  func (ps *PeerSet) PeersWithoutBlock(hash bc.Hash) []string {
   570  	ps.mtx.RLock()
   571  	defer ps.mtx.RUnlock()
   572  
   573  	var peers []string
   574  	for _, peer := range ps.peers {
   575  		if !peer.knownBlocks.Has(hash.String()) {
   576  			peers = append(peers, peer.ID())
   577  		}
   578  	}
   579  	return peers
   580  }
   581  
   582  func (ps *PeerSet) PeersWithoutSignature(signature []byte) []string {
   583  	ps.mtx.RLock()
   584  	defer ps.mtx.RUnlock()
   585  
   586  	var peers []string
   587  	for _, peer := range ps.peers {
   588  		if !peer.knownSignatures.Has(hex.EncodeToString(signature)) {
   589  			peers = append(peers, peer.ID())
   590  		}
   591  	}
   592  	return peers
   593  }
   594  
   595  func (ps *PeerSet) peersWithoutNewStatus(height uint64) []*Peer {
   596  	ps.mtx.RLock()
   597  	defer ps.mtx.RUnlock()
   598  
   599  	var peers []*Peer
   600  	for _, peer := range ps.peers {
   601  		if peer.knownStatus < height {
   602  			peers = append(peers, peer)
   603  		}
   604  	}
   605  	return peers
   606  }
   607  
   608  func (ps *PeerSet) peersWithoutTx(hash *bc.Hash) []*Peer {
   609  	ps.mtx.RLock()
   610  	defer ps.mtx.RUnlock()
   611  
   612  	peers := []*Peer{}
   613  	for _, peer := range ps.peers {
   614  		if !peer.knownTxs.Has(hash.String()) {
   615  			peers = append(peers, peer)
   616  		}
   617  	}
   618  	return peers
   619  }
   620  
   621  func (ps *PeerSet) RemovePeer(peerID string) {
   622  	ps.mtx.Lock()
   623  	delete(ps.peers, peerID)
   624  	ps.mtx.Unlock()
   625  	ps.StopPeerGracefully(peerID)
   626  }
   627  
   628  func (ps *PeerSet) SetStatus(peerID string, height uint64, hash *bc.Hash) {
   629  	peer := ps.GetPeer(peerID)
   630  	if peer == nil {
   631  		return
   632  	}
   633  
   634  	peer.SetBestStatus(height, hash)
   635  }
   636  
   637  func (ps *PeerSet) SetJustifiedStatus(peerID string, height uint64, hash *bc.Hash) {
   638  	peer := ps.GetPeer(peerID)
   639  	if peer == nil {
   640  		return
   641  	}
   642  
   643  	peer.SetJustifiedStatus(height, hash)
   644  }