github.com/Hnampk/my-fabric@v0.0.0-20201028083322-75069da399c0/gossip/gossip/channel/channel.go (about)

     1  /*
     2  Copyright IBM Corp. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package channel
     8  
     9  import (
    10  	"bytes"
    11  	"fmt"
    12  	"reflect"
    13  	"strconv"
    14  	"sync"
    15  	"sync/atomic"
    16  	"time"
    17  
    18  	proto "github.com/hyperledger/fabric-protos-go/gossip"
    19  	common_utils "github.com/hyperledger/fabric/common/util"
    20  	"github.com/hyperledger/fabric/gossip/api"
    21  	"github.com/hyperledger/fabric/gossip/comm"
    22  	"github.com/hyperledger/fabric/gossip/common"
    23  	"github.com/hyperledger/fabric/gossip/discovery"
    24  	"github.com/hyperledger/fabric/gossip/election"
    25  	"github.com/hyperledger/fabric/gossip/filter"
    26  	"github.com/hyperledger/fabric/gossip/gossip/algo"
    27  	"github.com/hyperledger/fabric/gossip/gossip/msgstore"
    28  	"github.com/hyperledger/fabric/gossip/gossip/pull"
    29  	"github.com/hyperledger/fabric/gossip/metrics"
    30  	"github.com/hyperledger/fabric/gossip/protoext"
    31  	"github.com/hyperledger/fabric/gossip/util"
    32  	"github.com/hyperledger/fabric/protoutil"
    33  )
    34  
    35  const DefMsgExpirationTimeout = election.DefLeaderAliveThreshold * 10
    36  
    37  // Config is a configuration item
    38  // of the channel store
    39  type Config struct {
    40  	ID                          string
    41  	PublishStateInfoInterval    time.Duration
    42  	MaxBlockCountToStore        int
    43  	PullPeerNum                 int
    44  	PullInterval                time.Duration
    45  	RequestStateInfoInterval    time.Duration
    46  	BlockExpirationInterval     time.Duration
    47  	StateInfoCacheSweepInterval time.Duration
    48  	TimeForMembershipTracker    time.Duration
    49  	DigestWaitTime              time.Duration
    50  	RequestWaitTime             time.Duration
    51  	ResponseWaitTime            time.Duration
    52  	MsgExpirationTimeout        time.Duration
    53  }
    54  
    55  // GossipChannel defines an object that deals with all channel-related messages
    56  type GossipChannel interface {
    57  	// Self returns a StateInfoMessage about the peer
    58  	Self() *protoext.SignedGossipMessage
    59  
    60  	// GetPeers returns a list of peers with metadata as published by them
    61  	GetPeers() []discovery.NetworkMember
    62  
    63  	// PeerFilter receives a SubChannelSelectionCriteria and returns a RoutingFilter that selects
    64  	// only peer identities that match the given criteria
    65  	PeerFilter(api.SubChannelSelectionCriteria) filter.RoutingFilter
    66  
    67  	// IsMemberInChan checks whether the given member is eligible to be in the channel
    68  	IsMemberInChan(member discovery.NetworkMember) bool
    69  
    70  	// UpdateLedgerHeight updates the ledger height the peer
    71  	// publishes to other peers in the channel
    72  	UpdateLedgerHeight(height uint64)
    73  
    74  	// UpdateChaincodes updates the chaincodes the peer publishes
    75  	// to other peers in the channel
    76  	UpdateChaincodes(chaincode []*proto.Chaincode)
    77  
    78  	// IsOrgInChannel returns whether the given organization is in the channel
    79  	IsOrgInChannel(membersOrg api.OrgIdentityType) bool
    80  
    81  	// EligibleForChannel returns whether the given member should get blocks
    82  	// for this channel
    83  	EligibleForChannel(member discovery.NetworkMember) bool
    84  
    85  	// HandleMessage processes a message sent by a remote peer
    86  	HandleMessage(protoext.ReceivedMessage)
    87  
    88  	// AddToMsgStore adds a given GossipMessage to the message store
    89  	AddToMsgStore(msg *protoext.SignedGossipMessage)
    90  
    91  	// ConfigureChannel (re)configures the list of organizations
    92  	// that are eligible to be in the channel
    93  	ConfigureChannel(joinMsg api.JoinChannelMessage)
    94  
    95  	// LeaveChannel makes the peer leave the channel
    96  	LeaveChannel()
    97  
    98  	// Stop stops the channel's activity
    99  	Stop()
   100  }
   101  
   102  // Adapter enables the gossipChannel
   103  // to communicate with gossipServiceImpl.
   104  type Adapter interface {
   105  	Sign(msg *proto.GossipMessage) (*protoext.SignedGossipMessage, error)
   106  
   107  	// GetConf returns the configuration that this GossipChannel will posses
   108  	GetConf() Config
   109  
   110  	// Gossip gossips a message in the channel
   111  	Gossip(message *protoext.SignedGossipMessage)
   112  
   113  	// Forward sends a message to the next hops
   114  	Forward(message protoext.ReceivedMessage)
   115  
   116  	// DeMultiplex de-multiplexes an item to subscribers
   117  	DeMultiplex(interface{})
   118  
   119  	// GetMembership returns the known alive peers and their information
   120  	GetMembership() []discovery.NetworkMember
   121  
   122  	// Lookup returns a network member, or nil if not found
   123  	Lookup(PKIID common.PKIidType) *discovery.NetworkMember
   124  
   125  	// Send sends a message to a list of peers
   126  	Send(msg *protoext.SignedGossipMessage, peers ...*comm.RemotePeer)
   127  
   128  	// ValidateStateInfoMessage returns an error if a message
   129  	// hasn't been signed correctly, nil otherwise.
   130  	ValidateStateInfoMessage(message *protoext.SignedGossipMessage) error
   131  
   132  	// GetOrgOfPeer returns the organization ID of a given peer PKI-ID
   133  	GetOrgOfPeer(pkiID common.PKIidType) api.OrgIdentityType
   134  
   135  	// GetIdentityByPKIID returns an identity of a peer with a certain
   136  	// pkiID, or nil if not found
   137  	GetIdentityByPKIID(pkiID common.PKIidType) api.PeerIdentityType
   138  }
   139  
   140  type gossipChannel struct {
   141  	Adapter
   142  	sync.RWMutex
   143  	shouldGossipStateInfo     int32
   144  	mcs                       api.MessageCryptoService
   145  	pkiID                     common.PKIidType
   146  	selfOrg                   api.OrgIdentityType
   147  	stopChan                  chan struct{}
   148  	stateInfoMsg              *protoext.SignedGossipMessage
   149  	orgs                      []api.OrgIdentityType
   150  	joinMsg                   api.JoinChannelMessage
   151  	blockMsgStore             msgstore.MessageStore
   152  	stateInfoMsgStore         *stateInfoCache
   153  	leaderMsgStore            msgstore.MessageStore
   154  	chainID                   common.ChannelID
   155  	blocksPuller              pull.Mediator
   156  	logger                    util.Logger
   157  	stateInfoPublishScheduler *time.Ticker
   158  	stateInfoRequestScheduler *time.Ticker
   159  	memFilter                 *membershipFilter
   160  	ledgerHeight              uint64
   161  	incTime                   uint64
   162  	leftChannel               int32
   163  	membershipTracker         *membershipTracker
   164  }
   165  
   166  type membershipFilter struct {
   167  	adapter Adapter
   168  	*gossipChannel
   169  }
   170  
   171  // GetMembership returns the known alive peers and their information
   172  func (mf *membershipFilter) GetMembership() []discovery.NetworkMember {
   173  	if mf.hasLeftChannel() {
   174  		return nil
   175  	}
   176  
   177  	var members []discovery.NetworkMember
   178  	for _, mem := range mf.adapter.GetMembership() {
   179  		if mf.eligibleForChannelAndSameOrg(mem) {
   180  			members = append(members, mem)
   181  		}
   182  	}
   183  	return members
   184  }
   185  
   186  // NewGossipChannel creates a new GossipChannel
   187  func NewGossipChannel(pkiID common.PKIidType, org api.OrgIdentityType, mcs api.MessageCryptoService,
   188  	channelID common.ChannelID, adapter Adapter, joinMsg api.JoinChannelMessage,
   189  	metrics *metrics.MembershipMetrics, logger util.Logger) GossipChannel {
   190  	gc := &gossipChannel{
   191  		incTime:                   uint64(time.Now().UnixNano()),
   192  		selfOrg:                   org,
   193  		pkiID:                     pkiID,
   194  		mcs:                       mcs,
   195  		Adapter:                   adapter,
   196  		stopChan:                  make(chan struct{}, 1),
   197  		shouldGossipStateInfo:     int32(0),
   198  		stateInfoPublishScheduler: time.NewTicker(adapter.GetConf().PublishStateInfoInterval),
   199  		stateInfoRequestScheduler: time.NewTicker(adapter.GetConf().RequestStateInfoInterval),
   200  		orgs:                      []api.OrgIdentityType{},
   201  		chainID:                   channelID,
   202  	}
   203  
   204  	if logger == nil {
   205  		gc.logger = util.GetLogger(util.ChannelLogger, adapter.GetConf().ID)
   206  	} else {
   207  		gc.logger = logger
   208  	}
   209  
   210  	gc.memFilter = &membershipFilter{adapter: gc.Adapter, gossipChannel: gc}
   211  
   212  	comparator := protoext.NewGossipMessageComparator(adapter.GetConf().MaxBlockCountToStore)
   213  
   214  	gc.blocksPuller = gc.createBlockPuller()
   215  
   216  	seqNumFromMsg := func(m interface{}) string {
   217  		return fmt.Sprintf("%d", m.(*protoext.SignedGossipMessage).GetDataMsg().Payload.SeqNum)
   218  	}
   219  	gc.blockMsgStore = msgstore.NewMessageStoreExpirable(comparator, func(m interface{}) {
   220  		gc.logger.Debugf("Removing %s from the message store", seqNumFromMsg(m))
   221  		gc.blocksPuller.Remove(seqNumFromMsg(m))
   222  	}, gc.GetConf().BlockExpirationInterval, nil, nil, func(m interface{}) {
   223  		gc.logger.Debugf("Removing %s from the message store", seqNumFromMsg(m))
   224  		gc.blocksPuller.Remove(seqNumFromMsg(m))
   225  	})
   226  
   227  	hashPeerExpiredInMembership := func(o interface{}) bool {
   228  		pkiID := o.(*protoext.SignedGossipMessage).GetStateInfo().PkiId
   229  		return gc.Lookup(pkiID) == nil
   230  	}
   231  	verifyStateInfoMsg := func(msg *protoext.SignedGossipMessage, orgs ...api.OrgIdentityType) bool {
   232  		si := msg.GetStateInfo()
   233  		// No point in verifying ourselves
   234  		if bytes.Equal(gc.pkiID, si.PkiId) {
   235  			return true
   236  		}
   237  		peerIdentity := adapter.GetIdentityByPKIID(si.PkiId)
   238  		if len(peerIdentity) == 0 {
   239  			gc.logger.Warning("Identity for peer", si.PkiId, "doesn't exist")
   240  			return false
   241  		}
   242  		isOrgInChan := func(org api.OrgIdentityType) bool {
   243  			if len(orgs) == 0 {
   244  				if !gc.IsOrgInChannel(org) {
   245  					return false
   246  				}
   247  			} else {
   248  				found := false
   249  				for _, chanMember := range orgs {
   250  					if bytes.Equal(chanMember, org) {
   251  						found = true
   252  						break
   253  					}
   254  				}
   255  				if !found {
   256  					return false
   257  				}
   258  			}
   259  			return true
   260  		}
   261  
   262  		org := gc.GetOrgOfPeer(si.PkiId)
   263  		if !isOrgInChan(org) {
   264  			gc.logger.Warning("peer", peerIdentity, "'s organization(", string(org), ") isn't in the channel", string(channelID))
   265  			return false
   266  		}
   267  		if err := gc.mcs.VerifyByChannel(channelID, peerIdentity, msg.Signature, msg.Payload); err != nil {
   268  			gc.logger.Warningf("Peer %v isn't eligible for channel %s : %+v", peerIdentity, string(channelID), err)
   269  			return false
   270  		}
   271  		return true
   272  	}
   273  	gc.stateInfoMsgStore = newStateInfoCache(gc.GetConf().StateInfoCacheSweepInterval, hashPeerExpiredInMembership, verifyStateInfoMsg)
   274  
   275  	ttl := adapter.GetConf().MsgExpirationTimeout
   276  	pol := protoext.NewGossipMessageComparator(0)
   277  
   278  	gc.leaderMsgStore = msgstore.NewMessageStoreExpirable(pol, msgstore.Noop, ttl, nil, nil, nil)
   279  
   280  	gc.ConfigureChannel(joinMsg)
   281  
   282  	// Periodically publish state info
   283  	go gc.periodicalInvocation(gc.publishStateInfo, gc.stateInfoPublishScheduler.C)
   284  	// Periodically request state info
   285  	go gc.periodicalInvocation(gc.requestStateInfo, gc.stateInfoRequestScheduler.C)
   286  
   287  	ticker := time.NewTicker(gc.GetConf().TimeForMembershipTracker)
   288  	gc.membershipTracker = &membershipTracker{
   289  		getPeersToTrack: gc.GetPeers,
   290  		report:          gc.reportMembershipChanges,
   291  		stopChan:        make(chan struct{}, 1),
   292  		tickerChannel:   ticker.C,
   293  		metrics:         metrics,
   294  		chainID:         channelID,
   295  	}
   296  
   297  	go gc.membershipTracker.trackMembershipChanges()
   298  	return gc
   299  }
   300  
   301  func (gc *gossipChannel) reportMembershipChanges(input ...interface{}) {
   302  	args := []interface{}{fmt.Sprintf("[%s]", string(gc.chainID))}
   303  	args = append(args, input...)
   304  	gc.logger.Info(args)
   305  }
   306  
   307  // Stop stop the channel operations
   308  func (gc *gossipChannel) Stop() {
   309  	close(gc.stopChan)
   310  	close(gc.membershipTracker.stopChan)
   311  	gc.blocksPuller.Stop()
   312  	gc.stateInfoPublishScheduler.Stop()
   313  	gc.stateInfoRequestScheduler.Stop()
   314  	gc.leaderMsgStore.Stop()
   315  	gc.stateInfoMsgStore.Stop()
   316  	gc.blockMsgStore.Stop()
   317  }
   318  
   319  func (gc *gossipChannel) periodicalInvocation(fn func(), c <-chan time.Time) {
   320  	for {
   321  		select {
   322  		case <-c:
   323  			fn()
   324  		case <-gc.stopChan:
   325  			return
   326  		}
   327  	}
   328  }
   329  
   330  // Self returns a StateInfoMessage about the peer
   331  func (gc *gossipChannel) Self() *protoext.SignedGossipMessage {
   332  	gc.RLock()
   333  	defer gc.RUnlock()
   334  	return gc.stateInfoMsg
   335  }
   336  
   337  // LeaveChannel makes the peer leave the channel
   338  func (gc *gossipChannel) LeaveChannel() {
   339  	gc.Lock()
   340  	defer gc.Unlock()
   341  
   342  	atomic.StoreInt32(&gc.leftChannel, 1)
   343  
   344  	var chaincodes []*proto.Chaincode
   345  	var height uint64
   346  	if prevMsg := gc.stateInfoMsg; prevMsg != nil {
   347  		chaincodes = prevMsg.GetStateInfo().Properties.Chaincodes
   348  		height = prevMsg.GetStateInfo().Properties.LedgerHeight
   349  	}
   350  	gc.updateProperties(height, chaincodes, true)
   351  }
   352  
   353  func (gc *gossipChannel) hasLeftChannel() bool {
   354  	return atomic.LoadInt32(&gc.leftChannel) == 1
   355  }
   356  
   357  // GetPeers returns a list of peers with metadata as published by them
   358  func (gc *gossipChannel) GetPeers() []discovery.NetworkMember {
   359  	var members []discovery.NetworkMember
   360  	if gc.hasLeftChannel() {
   361  		return members
   362  	}
   363  
   364  	for _, member := range gc.GetMembership() {
   365  		if !gc.EligibleForChannel(member) {
   366  			continue
   367  		}
   368  		stateInf := gc.stateInfoMsgStore.MsgByID(member.PKIid)
   369  		if stateInf == nil {
   370  			continue
   371  		}
   372  		props := stateInf.GetStateInfo().Properties
   373  		if props != nil && props.LeftChannel {
   374  			continue
   375  		}
   376  		member.Properties = stateInf.GetStateInfo().Properties
   377  		member.Envelope = stateInf.Envelope
   378  		members = append(members, member)
   379  	}
   380  	return members
   381  }
   382  
   383  func (gc *gossipChannel) requestStateInfo() {
   384  	req, err := gc.createStateInfoRequest()
   385  	if err != nil {
   386  		gc.logger.Warningf("Failed creating SignedGossipMessage: %+v", err)
   387  		return
   388  	}
   389  	endpoints := filter.SelectPeers(gc.GetConf().PullPeerNum, gc.GetMembership(), gc.IsMemberInChan)
   390  	gc.Send(req, endpoints...)
   391  }
   392  
   393  func (gc *gossipChannel) eligibleForChannelAndSameOrg(member discovery.NetworkMember) bool {
   394  	sameOrg := func(networkMember discovery.NetworkMember) bool {
   395  		return bytes.Equal(gc.GetOrgOfPeer(networkMember.PKIid), gc.selfOrg)
   396  	}
   397  	return filter.CombineRoutingFilters(gc.EligibleForChannel, sameOrg)(member)
   398  }
   399  
   400  func (gc *gossipChannel) publishStateInfo() {
   401  	if atomic.LoadInt32(&gc.shouldGossipStateInfo) == int32(0) {
   402  		return
   403  	}
   404  	gc.RLock()
   405  	stateInfoMsg := gc.stateInfoMsg
   406  	gc.RUnlock()
   407  	gc.Gossip(stateInfoMsg)
   408  	if len(gc.GetMembership()) > 0 {
   409  		atomic.StoreInt32(&gc.shouldGossipStateInfo, int32(0))
   410  	}
   411  }
   412  
   413  func (gc *gossipChannel) createBlockPuller() pull.Mediator {
   414  	conf := pull.Config{
   415  		MsgType:           proto.PullMsgType_BLOCK_MSG,
   416  		Channel:           []byte(gc.chainID),
   417  		ID:                gc.GetConf().ID,
   418  		PeerCountToSelect: gc.GetConf().PullPeerNum,
   419  		PullInterval:      gc.GetConf().PullInterval,
   420  		Tag:               proto.GossipMessage_CHAN_AND_ORG,
   421  		PullEngineConfig: algo.PullEngineConfig{
   422  			DigestWaitTime:   gc.GetConf().DigestWaitTime,
   423  			RequestWaitTime:  gc.GetConf().RequestWaitTime,
   424  			ResponseWaitTime: gc.GetConf().ResponseWaitTime,
   425  		},
   426  	}
   427  	seqNumFromMsg := func(msg *protoext.SignedGossipMessage) string {
   428  		dataMsg := msg.GetDataMsg()
   429  		if dataMsg == nil || dataMsg.Payload == nil {
   430  			gc.logger.Warning("Non-data block or with no payload")
   431  			return ""
   432  		}
   433  		return fmt.Sprintf("%d", dataMsg.Payload.SeqNum)
   434  	}
   435  	adapter := &pull.PullAdapter{
   436  		Sndr:        gc,
   437  		MemSvc:      gc.memFilter,
   438  		IdExtractor: seqNumFromMsg,
   439  		MsgCons: func(msg *protoext.SignedGossipMessage) {
   440  			gc.DeMultiplex(msg)
   441  		},
   442  	}
   443  
   444  	adapter.IngressDigFilter = func(digestMsg *proto.DataDigest) *proto.DataDigest {
   445  		gc.RLock()
   446  		height := gc.ledgerHeight
   447  		gc.RUnlock()
   448  		digests := digestMsg.Digests
   449  		digestMsg.Digests = nil
   450  		for i := range digests {
   451  			seqNum, err := strconv.ParseUint(string(digests[i]), 10, 64)
   452  			if err != nil {
   453  				gc.logger.Warningf("Can't parse digest %s : %+v", digests[i], err)
   454  				continue
   455  			}
   456  			if seqNum >= height {
   457  				digestMsg.Digests = append(digestMsg.Digests, digests[i])
   458  			}
   459  
   460  		}
   461  		return digestMsg
   462  	}
   463  
   464  	return pull.NewPullMediator(conf, adapter)
   465  }
   466  
   467  // IsMemberInChan checks whether the given member is eligible to be in the channel
   468  func (gc *gossipChannel) IsMemberInChan(member discovery.NetworkMember) bool {
   469  	org := gc.GetOrgOfPeer(member.PKIid)
   470  	if org == nil {
   471  		return false
   472  	}
   473  
   474  	return gc.IsOrgInChannel(org)
   475  }
   476  
   477  // PeerFilter receives a SubChannelSelectionCriteria and returns a RoutingFilter that selects
   478  // only peer identities that match the given criteria
   479  func (gc *gossipChannel) PeerFilter(messagePredicate api.SubChannelSelectionCriteria) filter.RoutingFilter {
   480  	return func(member discovery.NetworkMember) bool {
   481  		peerIdentity := gc.GetIdentityByPKIID(member.PKIid)
   482  		if len(peerIdentity) == 0 {
   483  			return false
   484  		}
   485  		msg := gc.stateInfoMsgStore.MembershipStore.MsgByID(member.PKIid)
   486  		if msg == nil {
   487  			return false
   488  		}
   489  
   490  		return messagePredicate(api.PeerSignature{
   491  			Message:      msg.Payload,
   492  			Signature:    msg.Signature,
   493  			PeerIdentity: peerIdentity,
   494  		})
   495  	}
   496  }
   497  
   498  // IsOrgInChannel returns whether the given organization is in the channel
   499  func (gc *gossipChannel) IsOrgInChannel(membersOrg api.OrgIdentityType) bool {
   500  	gc.RLock()
   501  	defer gc.RUnlock()
   502  	for _, orgOfChan := range gc.orgs {
   503  		if bytes.Equal(orgOfChan, membersOrg) {
   504  			return true
   505  		}
   506  	}
   507  	return false
   508  }
   509  
   510  // EligibleForChannel returns whether the given member should get blocks
   511  // for this channel
   512  func (gc *gossipChannel) EligibleForChannel(member discovery.NetworkMember) bool {
   513  	peerIdentity := gc.GetIdentityByPKIID(member.PKIid)
   514  	if len(peerIdentity) == 0 {
   515  		gc.logger.Warning("Identity for peer", member.PKIid, "doesn't exist")
   516  		return false
   517  	}
   518  	msg := gc.stateInfoMsgStore.MsgByID(member.PKIid)
   519  	if msg == nil {
   520  		return false
   521  	}
   522  	return true
   523  }
   524  
   525  // AddToMsgStore adds a given GossipMessage to the message store
   526  func (gc *gossipChannel) AddToMsgStore(msg *protoext.SignedGossipMessage) {
   527  	if protoext.IsDataMsg(msg.GossipMessage) {
   528  		gc.Lock()
   529  		defer gc.Unlock()
   530  		added := gc.blockMsgStore.Add(msg)
   531  		if added {
   532  			gc.logger.Debugf("Adding %v to the block puller", msg)
   533  			gc.blocksPuller.Add(msg)
   534  		}
   535  	}
   536  
   537  	if protoext.IsStateInfoMsg(msg.GossipMessage) {
   538  		gc.stateInfoMsgStore.Add(msg)
   539  	}
   540  }
   541  
   542  // ConfigureChannel (re)configures the list of organizations
   543  // that are eligible to be in the channel
   544  func (gc *gossipChannel) ConfigureChannel(joinMsg api.JoinChannelMessage) {
   545  	gc.Lock()
   546  	defer gc.Unlock()
   547  
   548  	if len(joinMsg.Members()) == 0 {
   549  		gc.logger.Warning("Received join channel message with empty set of members")
   550  		return
   551  	}
   552  
   553  	if gc.joinMsg == nil {
   554  		gc.joinMsg = joinMsg
   555  	}
   556  
   557  	if gc.joinMsg.SequenceNumber() > (joinMsg.SequenceNumber()) {
   558  		gc.logger.Warning("Already have a more updated JoinChannel message(", gc.joinMsg.SequenceNumber(), ") than", joinMsg.SequenceNumber())
   559  		return
   560  	}
   561  
   562  	gc.orgs = joinMsg.Members()
   563  	gc.joinMsg = joinMsg
   564  	gc.stateInfoMsgStore.validate(joinMsg.Members())
   565  }
   566  
   567  // HandleMessage processes a message sent by a remote peer
   568  func (gc *gossipChannel) HandleMessage(msg protoext.ReceivedMessage) {
   569  	if !gc.verifyMsg(msg) {
   570  		gc.logger.Warning("Failed verifying message:", msg.GetGossipMessage().GossipMessage)
   571  		return
   572  	}
   573  	m := msg.GetGossipMessage()
   574  	if !protoext.IsChannelRestricted(m.GossipMessage) {
   575  		gc.logger.Warning("Got message", msg.GetGossipMessage(), "but it's not a per-channel message, discarding it")
   576  		return
   577  	}
   578  	orgID := gc.GetOrgOfPeer(msg.GetConnectionInfo().ID)
   579  	if len(orgID) == 0 {
   580  		gc.logger.Debug("Couldn't find org identity of peer", msg.GetConnectionInfo())
   581  		return
   582  	}
   583  	if !gc.IsOrgInChannel(orgID) {
   584  		gc.logger.Warning("Point to point message came from", msg.GetConnectionInfo(),
   585  			", org(", string(orgID), ") but it's not eligible for the channel", string(gc.chainID))
   586  		return
   587  	}
   588  
   589  	if protoext.IsStateInfoPullRequestMsg(m.GossipMessage) {
   590  		msg.Respond(gc.createStateInfoSnapshot(orgID))
   591  		return
   592  	}
   593  
   594  	if protoext.IsStateInfoSnapshot(m.GossipMessage) {
   595  		gc.handleStateInfSnapshot(m.GossipMessage, msg.GetConnectionInfo().ID)
   596  		return
   597  	}
   598  
   599  	if protoext.IsDataMsg(m.GossipMessage) || protoext.IsStateInfoMsg(m.GossipMessage) {
   600  		added := false
   601  
   602  		if protoext.IsDataMsg(m.GossipMessage) {
   603  			if m.GetDataMsg().Payload == nil {
   604  				gc.logger.Warning("Payload is empty, got it from", msg.GetConnectionInfo().ID)
   605  				return
   606  			}
   607  			// Would this block go into the message store if it was verified?
   608  			if !gc.blockMsgStore.CheckValid(msg.GetGossipMessage()) {
   609  				return
   610  			}
   611  			if !gc.verifyBlock(m.GossipMessage, msg.GetConnectionInfo().ID) {
   612  				gc.logger.Warning("Failed verifying block", m.GetDataMsg().Payload.SeqNum)
   613  				return
   614  			}
   615  			gc.Lock()
   616  			added = gc.blockMsgStore.Add(msg.GetGossipMessage())
   617  			if added {
   618  				gc.logger.Debugf("Adding %v to the block puller", msg.GetGossipMessage())
   619  				gc.blocksPuller.Add(msg.GetGossipMessage())
   620  			}
   621  			gc.Unlock()
   622  		} else { // StateInfoMsg verification should be handled in a layer above
   623  			//  since we don't have access to the id mapper here
   624  			added = gc.stateInfoMsgStore.Add(msg.GetGossipMessage())
   625  		}
   626  
   627  		if added {
   628  			// Forward the message
   629  			gc.Forward(msg)
   630  			// DeMultiplex to local subscribers
   631  			gc.DeMultiplex(m)
   632  		}
   633  		return
   634  	}
   635  
   636  	if protoext.IsPullMsg(m.GossipMessage) && protoext.GetPullMsgType(m.GossipMessage) == proto.PullMsgType_BLOCK_MSG {
   637  		if gc.hasLeftChannel() {
   638  			gc.logger.Info("Received Pull message from", msg.GetConnectionInfo().Endpoint, "but left the channel", string(gc.chainID))
   639  			return
   640  		}
   641  		// If we don't have a StateInfo message from the peer,
   642  		// no way of validating its eligibility in the channel.
   643  		if gc.stateInfoMsgStore.MsgByID(msg.GetConnectionInfo().ID) == nil {
   644  			gc.logger.Debug("Don't have StateInfo message of peer", msg.GetConnectionInfo())
   645  			return
   646  		}
   647  		if !gc.eligibleForChannelAndSameOrg(discovery.NetworkMember{PKIid: msg.GetConnectionInfo().ID}) {
   648  			gc.logger.Warning(msg.GetConnectionInfo(), "isn't eligible for pulling blocks of", string(gc.chainID))
   649  			return
   650  		}
   651  		if protoext.IsDataUpdate(m.GossipMessage) {
   652  			// Iterate over the envelopes, and filter out blocks
   653  			// that we already have in the blockMsgStore, or blocks that
   654  			// are too far in the past.
   655  			var msgs []*protoext.SignedGossipMessage
   656  			var items []*proto.Envelope
   657  			filteredEnvelopes := []*proto.Envelope{}
   658  			for _, item := range m.GetDataUpdate().Data {
   659  				gMsg, err := protoext.EnvelopeToGossipMessage(item)
   660  				if err != nil {
   661  					gc.logger.Warningf("Data update contains an invalid message: %+v", err)
   662  					return
   663  				}
   664  				if !bytes.Equal(gMsg.Channel, []byte(gc.chainID)) {
   665  					gc.logger.Warning("DataUpdate message contains item with channel", gMsg.Channel, "but should be", gc.chainID)
   666  					return
   667  				}
   668  				// Would this block go into the message store if it was verified?
   669  				if !gc.blockMsgStore.CheckValid(gMsg) {
   670  					continue
   671  				}
   672  				if !gc.verifyBlock(gMsg.GossipMessage, msg.GetConnectionInfo().ID) {
   673  					return
   674  				}
   675  				msgs = append(msgs, gMsg)
   676  				items = append(items, item)
   677  			}
   678  
   679  			gc.Lock()
   680  			defer gc.Unlock()
   681  
   682  			for i, gMsg := range msgs {
   683  				item := items[i]
   684  				added := gc.blockMsgStore.Add(gMsg)
   685  				if !added {
   686  					// If this block doesn't need to be added, it means it either already
   687  					// exists in memory or that it is too far in the past
   688  					continue
   689  				}
   690  				filteredEnvelopes = append(filteredEnvelopes, item)
   691  			}
   692  
   693  			// Replace the update message with just the blocks that should be processed
   694  			m.GetDataUpdate().Data = filteredEnvelopes
   695  		}
   696  		gc.blocksPuller.HandleMessage(msg)
   697  	}
   698  
   699  	if protoext.IsLeadershipMsg(m.GossipMessage) {
   700  		connInfo := msg.GetConnectionInfo()
   701  		senderOrg := gc.GetOrgOfPeer(connInfo.ID)
   702  		if !bytes.Equal(gc.selfOrg, senderOrg) {
   703  			gc.logger.Warningf("Received leadership message from %s that belongs to a foreign organization %s",
   704  				connInfo.Endpoint, string(senderOrg))
   705  			return
   706  		}
   707  		msgCreatorOrg := gc.GetOrgOfPeer(m.GetLeadershipMsg().PkiId)
   708  		if !bytes.Equal(gc.selfOrg, msgCreatorOrg) {
   709  			gc.logger.Warningf("Received leadership message created by a foreign organization %s", string(msgCreatorOrg))
   710  			return
   711  		}
   712  		// Handling leadership message
   713  		added := gc.leaderMsgStore.Add(m)
   714  		if added {
   715  			gc.DeMultiplex(m)
   716  		}
   717  	}
   718  }
   719  
   720  func (gc *gossipChannel) handleStateInfSnapshot(m *proto.GossipMessage, sender common.PKIidType) {
   721  	chanName := string(gc.chainID)
   722  	for _, envelope := range m.GetStateSnapshot().Elements {
   723  		stateInf, err := protoext.EnvelopeToGossipMessage(envelope)
   724  		if err != nil {
   725  			gc.logger.Warningf("Channel %s : StateInfo snapshot contains an invalid message: %+v", chanName, err)
   726  			return
   727  		}
   728  		if !protoext.IsStateInfoMsg(stateInf.GossipMessage) {
   729  			gc.logger.Warning("Channel", chanName, ": Element of StateInfoSnapshot isn't a StateInfoMessage:",
   730  				stateInf, "message sent from", sender)
   731  			return
   732  		}
   733  		si := stateInf.GetStateInfo()
   734  		orgID := gc.GetOrgOfPeer(si.PkiId)
   735  		if orgID == nil {
   736  			gc.logger.Debug("Channel", chanName, ": Couldn't find org identity of peer",
   737  				string(si.PkiId), "message sent from", string(sender))
   738  			return
   739  		}
   740  
   741  		if !gc.IsOrgInChannel(orgID) {
   742  			gc.logger.Warning("Channel", chanName, ": Peer", stateInf.GetStateInfo().PkiId,
   743  				"is not in an eligible org, can't process a stateInfo from it, sent from", sender)
   744  			return
   745  		}
   746  
   747  		expectedMAC := GenerateMAC(si.PkiId, gc.chainID)
   748  		if !bytes.Equal(si.Channel_MAC, expectedMAC) {
   749  			gc.logger.Warning("Channel", chanName, ": StateInfo message", stateInf,
   750  				", has an invalid MAC. Expected", expectedMAC, ", got", si.Channel_MAC, ", sent from", sender)
   751  			return
   752  		}
   753  		err = gc.ValidateStateInfoMessage(stateInf)
   754  		if err != nil {
   755  			gc.logger.Warningf("Channel %s: Failed validating state info message: %v sent from %v : %+v", chanName, stateInf, sender, err)
   756  			return
   757  		}
   758  
   759  		if gc.Lookup(si.PkiId) == nil {
   760  			// Skip StateInfo messages that belong to peers
   761  			// that have been expired
   762  			continue
   763  		}
   764  
   765  		gc.stateInfoMsgStore.Add(stateInf)
   766  	}
   767  }
   768  
   769  func (gc *gossipChannel) verifyBlock(msg *proto.GossipMessage, sender common.PKIidType) bool {
   770  	if !protoext.IsDataMsg(msg) {
   771  		gc.logger.Warning("Received from ", sender, "a DataUpdate message that contains a non-block GossipMessage:", msg)
   772  		return false
   773  	}
   774  	payload := msg.GetDataMsg().Payload
   775  	if payload == nil {
   776  		gc.logger.Warning("Received empty payload from", sender)
   777  		return false
   778  	}
   779  	seqNum := payload.SeqNum
   780  	rawBlock := payload.Data
   781  	block, err := protoutil.UnmarshalBlock(rawBlock)
   782  	if err != nil {
   783  		gc.logger.Warningf("Received improperly encoded block from %v in DataUpdate: %+v", sender, err)
   784  		return false
   785  	}
   786  
   787  	err = gc.mcs.VerifyBlock(msg.Channel, seqNum, block)
   788  	if err != nil {
   789  		gc.logger.Warningf("Received fabricated block from %v in DataUpdate: %+v", sender, err)
   790  		return false
   791  	}
   792  	return true
   793  }
   794  
   795  func (gc *gossipChannel) createStateInfoSnapshot(requestersOrg api.OrgIdentityType) *proto.GossipMessage {
   796  	sameOrg := bytes.Equal(gc.selfOrg, requestersOrg)
   797  	rawElements := gc.stateInfoMsgStore.Get()
   798  	elements := []*proto.Envelope{}
   799  	for _, rawEl := range rawElements {
   800  		msg := rawEl.(*protoext.SignedGossipMessage)
   801  		orgOfCurrentMsg := gc.GetOrgOfPeer(msg.GetStateInfo().PkiId)
   802  		// If we're in the same org as the requester, or the message belongs to a foreign org
   803  		// don't do any filtering
   804  		if sameOrg || !bytes.Equal(orgOfCurrentMsg, gc.selfOrg) {
   805  			elements = append(elements, msg.Envelope)
   806  			continue
   807  		}
   808  		// Else, the requester is in a different org, so disclose only StateInfo messages that their
   809  		// corresponding AliveMessages have external endpoints
   810  		if netMember := gc.Lookup(msg.GetStateInfo().PkiId); netMember == nil || netMember.Endpoint == "" {
   811  			continue
   812  		}
   813  		elements = append(elements, msg.Envelope)
   814  	}
   815  
   816  	return &proto.GossipMessage{
   817  		Channel: gc.chainID,
   818  		Tag:     proto.GossipMessage_CHAN_OR_ORG,
   819  		Nonce:   0,
   820  		Content: &proto.GossipMessage_StateSnapshot{
   821  			StateSnapshot: &proto.StateInfoSnapshot{
   822  				Elements: elements,
   823  			},
   824  		},
   825  	}
   826  }
   827  
   828  func (gc *gossipChannel) verifyMsg(msg protoext.ReceivedMessage) bool {
   829  	if msg == nil {
   830  		gc.logger.Warning("Messsage is nil")
   831  		return false
   832  	}
   833  	m := msg.GetGossipMessage()
   834  	if m == nil {
   835  		gc.logger.Warning("Message content is empty")
   836  		return false
   837  	}
   838  
   839  	if msg.GetConnectionInfo().ID == nil {
   840  		gc.logger.Warning("Message has nil PKI-ID")
   841  		return false
   842  	}
   843  
   844  	if protoext.IsStateInfoMsg(m.GossipMessage) {
   845  		si := m.GetStateInfo()
   846  		expectedMAC := GenerateMAC(si.PkiId, gc.chainID)
   847  		if !bytes.Equal(expectedMAC, si.Channel_MAC) {
   848  			gc.logger.Warning("Message contains wrong channel MAC(", si.Channel_MAC, "), expected", expectedMAC)
   849  			return false
   850  		}
   851  		return true
   852  	}
   853  
   854  	if protoext.IsStateInfoPullRequestMsg(m.GossipMessage) {
   855  		sipr := m.GetStateInfoPullReq()
   856  		expectedMAC := GenerateMAC(msg.GetConnectionInfo().ID, gc.chainID)
   857  		if !bytes.Equal(expectedMAC, sipr.Channel_MAC) {
   858  			gc.logger.Warning("Message contains wrong channel MAC(", sipr.Channel_MAC, "), expected", expectedMAC)
   859  			return false
   860  		}
   861  		return true
   862  	}
   863  
   864  	if !bytes.Equal(m.Channel, []byte(gc.chainID)) {
   865  		gc.logger.Warning("Message contains wrong channel(", m.Channel, "), expected", gc.chainID)
   866  		return false
   867  	}
   868  	return true
   869  }
   870  
   871  func (gc *gossipChannel) createStateInfoRequest() (*protoext.SignedGossipMessage, error) {
   872  	return protoext.NoopSign(&proto.GossipMessage{
   873  		Tag:   proto.GossipMessage_CHAN_OR_ORG,
   874  		Nonce: 0,
   875  		Content: &proto.GossipMessage_StateInfoPullReq{
   876  			StateInfoPullReq: &proto.StateInfoPullRequest{
   877  				Channel_MAC: GenerateMAC(gc.pkiID, gc.chainID),
   878  			},
   879  		},
   880  	})
   881  }
   882  
   883  // UpdateLedgerHeight updates the ledger height the peer
   884  // publishes to other peers in the channel
   885  func (gc *gossipChannel) UpdateLedgerHeight(height uint64) {
   886  	gc.Lock()
   887  	defer gc.Unlock()
   888  
   889  	var chaincodes []*proto.Chaincode
   890  	var leftChannel bool
   891  	if prevMsg := gc.stateInfoMsg; prevMsg != nil {
   892  		leftChannel = prevMsg.GetStateInfo().Properties.LeftChannel
   893  		chaincodes = prevMsg.GetStateInfo().Properties.Chaincodes
   894  	}
   895  	gc.updateProperties(height, chaincodes, leftChannel)
   896  }
   897  
   898  // UpdateChaincodes updates the chaincodes the peer publishes
   899  // to other peers in the channel
   900  func (gc *gossipChannel) UpdateChaincodes(chaincodes []*proto.Chaincode) {
   901  	gc.Lock()
   902  	defer gc.Unlock()
   903  
   904  	var ledgerHeight uint64 = 1
   905  	var leftChannel bool
   906  	if prevMsg := gc.stateInfoMsg; prevMsg != nil {
   907  		ledgerHeight = prevMsg.GetStateInfo().Properties.LedgerHeight
   908  		leftChannel = prevMsg.GetStateInfo().Properties.LeftChannel
   909  	}
   910  	gc.updateProperties(ledgerHeight, chaincodes, leftChannel)
   911  }
   912  
   913  // UpdateStateInfo updates this channel's StateInfo message
   914  // that is periodically published
   915  func (gc *gossipChannel) updateStateInfo(msg *protoext.SignedGossipMessage) {
   916  	gc.stateInfoMsgStore.Add(msg)
   917  	gc.ledgerHeight = msg.GetStateInfo().Properties.LedgerHeight
   918  	gc.stateInfoMsg = msg
   919  	atomic.StoreInt32(&gc.shouldGossipStateInfo, int32(1))
   920  }
   921  
   922  func (gc *gossipChannel) updateProperties(ledgerHeight uint64, chaincodes []*proto.Chaincode, leftChannel bool) {
   923  	stateInfMsg := &proto.StateInfo{
   924  		Channel_MAC: GenerateMAC(gc.pkiID, gc.chainID),
   925  		PkiId:       gc.pkiID,
   926  		Timestamp: &proto.PeerTime{
   927  			IncNum: gc.incTime,
   928  			SeqNum: uint64(time.Now().UnixNano()),
   929  		},
   930  		Properties: &proto.Properties{
   931  			LeftChannel:  leftChannel,
   932  			LedgerHeight: ledgerHeight,
   933  			Chaincodes:   chaincodes,
   934  		},
   935  	}
   936  	m := &proto.GossipMessage{
   937  		Nonce: 0,
   938  		Tag:   proto.GossipMessage_CHAN_OR_ORG,
   939  		Content: &proto.GossipMessage_StateInfo{
   940  			StateInfo: stateInfMsg,
   941  		},
   942  	}
   943  
   944  	msg, err := gc.Sign(m)
   945  	if err != nil {
   946  		gc.logger.Error("Failed signing message:", err)
   947  		return
   948  	}
   949  	gc.updateStateInfo(msg)
   950  }
   951  
   952  func newStateInfoCache(sweepInterval time.Duration, hasExpired func(interface{}) bool, verifyFunc membershipPredicate) *stateInfoCache {
   953  	membershipStore := util.NewMembershipStore()
   954  	pol := protoext.NewGossipMessageComparator(0)
   955  
   956  	s := &stateInfoCache{
   957  		verify:          verifyFunc,
   958  		MembershipStore: membershipStore,
   959  		stopChan:        make(chan struct{}),
   960  	}
   961  	invalidationTrigger := func(m interface{}) {
   962  		pkiID := m.(*protoext.SignedGossipMessage).GetStateInfo().PkiId
   963  		membershipStore.Remove(pkiID)
   964  	}
   965  	s.MessageStore = msgstore.NewMessageStore(pol, invalidationTrigger)
   966  
   967  	go func() {
   968  		for {
   969  			select {
   970  			case <-s.stopChan:
   971  				return
   972  			case <-time.After(sweepInterval):
   973  				s.Purge(hasExpired)
   974  			}
   975  		}
   976  	}()
   977  	return s
   978  }
   979  
   980  // membershipPredicate receives a StateInfoMessage and optionally a slice of organization identifiers
   981  // and returns whether the peer that signed the given StateInfoMessage is eligible
   982  // to the channel or not
   983  type membershipPredicate func(msg *protoext.SignedGossipMessage, orgs ...api.OrgIdentityType) bool
   984  
   985  // stateInfoCache is actually a messageStore
   986  // that also indexes messages that are added
   987  // so that they could be extracted later
   988  type stateInfoCache struct {
   989  	verify membershipPredicate
   990  	*util.MembershipStore
   991  	msgstore.MessageStore
   992  	stopChan chan struct{}
   993  }
   994  
   995  func (cache *stateInfoCache) validate(orgs []api.OrgIdentityType) {
   996  	for _, m := range cache.Get() {
   997  		msg := m.(*protoext.SignedGossipMessage)
   998  		if !cache.verify(msg, orgs...) {
   999  			cache.delete(msg)
  1000  		}
  1001  	}
  1002  }
  1003  
  1004  // Add attempts to add the given message to the stateInfoCache,
  1005  // and if the message was added, also indexes it.
  1006  // Message must be a StateInfo message.
  1007  func (cache *stateInfoCache) Add(msg *protoext.SignedGossipMessage) bool {
  1008  	if !cache.MessageStore.CheckValid(msg) {
  1009  		return false
  1010  	}
  1011  	if !cache.verify(msg) {
  1012  		return false
  1013  	}
  1014  	added := cache.MessageStore.Add(msg)
  1015  	if added {
  1016  		pkiID := msg.GetStateInfo().PkiId
  1017  		cache.MembershipStore.Put(pkiID, msg)
  1018  	}
  1019  	return added
  1020  }
  1021  
  1022  func (cache *stateInfoCache) delete(msg *protoext.SignedGossipMessage) {
  1023  	cache.Purge(func(o interface{}) bool {
  1024  		pkiID := o.(*protoext.SignedGossipMessage).GetStateInfo().PkiId
  1025  		return bytes.Equal(pkiID, msg.GetStateInfo().PkiId)
  1026  	})
  1027  	cache.Remove(msg.GetStateInfo().PkiId)
  1028  }
  1029  
  1030  func (cache *stateInfoCache) Stop() {
  1031  	cache.stopChan <- struct{}{}
  1032  }
  1033  
  1034  // GenerateMAC returns a byte slice that is derived from the peer's PKI-ID
  1035  // and a channel name
  1036  func GenerateMAC(pkiID common.PKIidType, channelID common.ChannelID) []byte {
  1037  	// Hash is computed on (PKI-ID || channel ID)
  1038  	var preImage []byte
  1039  	preImage = append(preImage, []byte(pkiID)...)
  1040  	preImage = append(preImage, []byte(channelID)...)
  1041  	return common_utils.ComputeSHA256(preImage)
  1042  }
  1043  
  1044  //membershipTracker is a struct for tracking changes in peers of the channel
  1045  type membershipTracker struct {
  1046  	getPeersToTrack func() []discovery.NetworkMember
  1047  	report          func(...interface{})
  1048  	stopChan        chan struct{}
  1049  	tickerChannel   <-chan time.Time
  1050  	metrics         *metrics.MembershipMetrics
  1051  	chainID         common.ChannelID
  1052  }
  1053  
  1054  //endpoints return all peers by their endpoints
  1055  func endpoints(members discovery.Members) [][]string {
  1056  	var currView [][]string
  1057  	for _, member := range members {
  1058  		ep := member.Endpoint
  1059  		epi := member.InternalEndpoint
  1060  		var endPoints []string
  1061  		if ep != epi {
  1062  			endPoints = append(endPoints, ep, epi)
  1063  		} else {
  1064  			endPoints = append(endPoints, ep)
  1065  		}
  1066  		currView = append(currView, endPoints)
  1067  	}
  1068  	return currView
  1069  }
  1070  
  1071  //checkIfPeersChanged checks which peers are offline and which are online for channel
  1072  func (mt *membershipTracker) checkIfPeersChanged(prevPeers discovery.Members, currPeers discovery.Members,
  1073  	prevSetPeers map[string]struct{}, currSetPeers map[string]struct{}) {
  1074  	var currView [][]string
  1075  
  1076  	wereInPrev := endpoints(prevPeers.Filter(func(member discovery.NetworkMember) bool {
  1077  		_, exists := currSetPeers[string(member.PKIid)]
  1078  		return !exists
  1079  	}))
  1080  	newInCurr := endpoints(currPeers.Filter(func(member discovery.NetworkMember) bool {
  1081  		_, exists := prevSetPeers[string(member.PKIid)]
  1082  		return !exists
  1083  	}))
  1084  	currView = endpoints(currPeers)
  1085  
  1086  	if !reflect.DeepEqual(wereInPrev, newInCurr) {
  1087  		if len(wereInPrev) == 0 {
  1088  			mt.report("Membership view has changed. peers went online: ", newInCurr, ", current view: ", currView)
  1089  		} else if len(newInCurr) == 0 {
  1090  			mt.report("Membership view has changed. peers went offline: ", wereInPrev, ", current view: ", currView)
  1091  		} else {
  1092  			mt.report("Membership view has changed. peers went offline: ", wereInPrev, ", peers went online: ", newInCurr, ", current view: ", currView)
  1093  		}
  1094  	}
  1095  }
  1096  
  1097  func (mt *membershipTracker) createSetOfPeers(peersToMakeSet []discovery.NetworkMember) map[string]struct{} {
  1098  	setPeers := make(map[string]struct{})
  1099  	for _, prevPeer := range peersToMakeSet {
  1100  		prevPeerID := string(prevPeer.PKIid)
  1101  		setPeers[prevPeerID] = struct{}{}
  1102  	}
  1103  	return setPeers
  1104  }
  1105  
  1106  func (mt *membershipTracker) trackMembershipChanges() {
  1107  	prev := mt.getPeersToTrack()
  1108  	prevSetPeers := mt.createSetOfPeers(prev)
  1109  	for {
  1110  		//timeout to check changes in peers
  1111  		select {
  1112  		case <-mt.stopChan:
  1113  			return
  1114  		case <-mt.tickerChannel:
  1115  			currPeers := mt.getPeersToTrack()
  1116  			mt.metrics.Total.With("channel", string(mt.chainID)).Set(float64(len(currPeers)))
  1117  			currSetPeers := mt.createSetOfPeers(currPeers)
  1118  			mt.checkIfPeersChanged(prev, currPeers, prevSetPeers, currSetPeers)
  1119  			prev = currPeers
  1120  			prevSetPeers = mt.createSetOfPeers(prev)
  1121  		}
  1122  	}
  1123  }