github.com/darrenli6/fabric-sdk-example@v0.0.0-20220109053535-94b13b56df8c/gossip/service/gossip_service.go (about)

     1  /*
     2  Copyright IBM Corp. 2016 All Rights Reserved.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8                   http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package service
    18  
    19  import (
    20  	"sync"
    21  
    22  	"github.com/hyperledger/fabric/core/committer"
    23  	"github.com/hyperledger/fabric/core/deliverservice"
    24  	"github.com/hyperledger/fabric/core/deliverservice/blocksprovider"
    25  	"github.com/hyperledger/fabric/gossip/api"
    26  	gossipCommon "github.com/hyperledger/fabric/gossip/common"
    27  	"github.com/hyperledger/fabric/gossip/election"
    28  	"github.com/hyperledger/fabric/gossip/gossip"
    29  	"github.com/hyperledger/fabric/gossip/identity"
    30  	"github.com/hyperledger/fabric/gossip/integration"
    31  	"github.com/hyperledger/fabric/gossip/state"
    32  	"github.com/hyperledger/fabric/gossip/util"
    33  	"github.com/hyperledger/fabric/protos/common"
    34  	proto "github.com/hyperledger/fabric/protos/gossip"
    35  	"github.com/spf13/viper"
    36  	"google.golang.org/grpc"
    37  )
    38  
    39  var (
    40  	gossipServiceInstance *gossipServiceImpl
    41  	once                  sync.Once
    42  )
    43  
    44  type gossipSvc gossip.Gossip
    45  
    46  // GossipService encapsulates gossip and state capabilities into single interface
    47  type GossipService interface {
    48  	gossip.Gossip
    49  
    50  	// NewConfigEventer creates a ConfigProcessor which the configtx.Manager can ultimately route config updates to
    51  	NewConfigEventer() ConfigProcessor
    52  	// InitializeChannel allocates the state provider and should be invoked once per channel per execution
    53  	InitializeChannel(chainID string, committer committer.Committer, endpoints []string)
    54  	// GetBlock returns block for given chain
    55  	GetBlock(chainID string, index uint64) *common.Block
    56  	// AddPayload appends message payload to for given chain
    57  	AddPayload(chainID string, payload *proto.Payload) error
    58  }
    59  
    60  // DeliveryServiceFactory factory to create and initialize delivery service instance
    61  type DeliveryServiceFactory interface {
    62  	// Returns an instance of delivery client
    63  	Service(g GossipService, endpoints []string, msc api.MessageCryptoService) (deliverclient.DeliverService, error)
    64  }
    65  
    66  type deliveryFactoryImpl struct {
    67  }
    68  
    69  // Returns an instance of delivery client
    70  func (*deliveryFactoryImpl) Service(g GossipService, endpoints []string, mcs api.MessageCryptoService) (deliverclient.DeliverService, error) {
    71  	return deliverclient.NewDeliverService(&deliverclient.Config{
    72  		CryptoSvc:   mcs,
    73  		Gossip:      g,
    74  		Endpoints:   endpoints,
    75  		ConnFactory: deliverclient.DefaultConnectionFactory,
    76  		ABCFactory:  deliverclient.DefaultABCFactory,
    77  	})
    78  }
    79  
    80  type gossipServiceImpl struct {
    81  	gossipSvc
    82  	chains          map[string]state.GossipStateProvider
    83  	leaderElection  map[string]election.LeaderElectionService
    84  	deliveryService deliverclient.DeliverService
    85  	deliveryFactory DeliveryServiceFactory
    86  	lock            sync.RWMutex
    87  	idMapper        identity.Mapper
    88  	mcs             api.MessageCryptoService
    89  	peerIdentity    []byte
    90  	secAdv          api.SecurityAdvisor
    91  }
    92  
    93  // This is an implementation of api.JoinChannelMessage.
    94  type joinChannelMessage struct {
    95  	seqNum              uint64
    96  	members2AnchorPeers map[string][]api.AnchorPeer
    97  }
    98  
    99  func (jcm *joinChannelMessage) SequenceNumber() uint64 {
   100  	return jcm.seqNum
   101  }
   102  
   103  // Members returns the organizations of the channel
   104  func (jcm *joinChannelMessage) Members() []api.OrgIdentityType {
   105  	members := make([]api.OrgIdentityType, len(jcm.members2AnchorPeers))
   106  	i := 0
   107  	for org := range jcm.members2AnchorPeers {
   108  		members[i] = api.OrgIdentityType(org)
   109  		i++
   110  	}
   111  	return members
   112  }
   113  
   114  // AnchorPeersOf returns the anchor peers of the given organization
   115  func (jcm *joinChannelMessage) AnchorPeersOf(org api.OrgIdentityType) []api.AnchorPeer {
   116  	return jcm.members2AnchorPeers[string(org)]
   117  }
   118  
   119  var logger = util.GetLogger(util.LoggingServiceModule, "")
   120  
   121  // InitGossipService initialize gossip service
   122  func InitGossipService(peerIdentity []byte, endpoint string, s *grpc.Server, mcs api.MessageCryptoService,
   123  	secAdv api.SecurityAdvisor, secureDialOpts api.PeerSecureDialOpts, bootPeers ...string) error {
   124  	// TODO: Remove this.
   125  	// TODO: This is a temporary work-around to make the gossip leader election module load its logger at startup
   126  	// TODO: in order for the flogging package to register this logger in time so it can set the log levels as requested in the config
   127  	util.GetLogger(util.LoggingElectionModule, "")
   128  	return InitGossipServiceCustomDeliveryFactory(peerIdentity, endpoint, s, &deliveryFactoryImpl{},
   129  		mcs, secAdv, secureDialOpts, bootPeers...)
   130  }
   131  
   132  // InitGossipServiceCustomDeliveryFactory initialize gossip service with customize delivery factory
   133  // implementation, might be useful for testing and mocking purposes
   134  func InitGossipServiceCustomDeliveryFactory(peerIdentity []byte, endpoint string, s *grpc.Server,
   135  	factory DeliveryServiceFactory, mcs api.MessageCryptoService, secAdv api.SecurityAdvisor,
   136  	secureDialOpts api.PeerSecureDialOpts, bootPeers ...string) error {
   137  	var err error
   138  	var gossip gossip.Gossip
   139  	once.Do(func() {
   140  		if overrideEndpoint := viper.GetString("peer.gossip.endpoint"); overrideEndpoint != "" {
   141  			endpoint = overrideEndpoint
   142  		}
   143  
   144  		logger.Info("Initialize gossip with endpoint", endpoint, "and bootstrap set", bootPeers)
   145  
   146  		idMapper := identity.NewIdentityMapper(mcs, peerIdentity)
   147  		gossip, err = integration.NewGossipComponent(peerIdentity, endpoint, s, secAdv,
   148  			mcs, idMapper, secureDialOpts, bootPeers...)
   149  		gossipServiceInstance = &gossipServiceImpl{
   150  			mcs:             mcs,
   151  			gossipSvc:       gossip,
   152  			chains:          make(map[string]state.GossipStateProvider),
   153  			leaderElection:  make(map[string]election.LeaderElectionService),
   154  			deliveryFactory: factory,
   155  			idMapper:        idMapper,
   156  			peerIdentity:    peerIdentity,
   157  			secAdv:          secAdv,
   158  		}
   159  	})
   160  	return err
   161  }
   162  
   163  // GetGossipService returns an instance of gossip service
   164  func GetGossipService() GossipService {
   165  	return gossipServiceInstance
   166  }
   167  
   168  // NewConfigEventer creates a ConfigProcessor which the configtx.Manager can ultimately route config updates to
   169  func (g *gossipServiceImpl) NewConfigEventer() ConfigProcessor {
   170  	return newConfigEventer(g)
   171  }
   172  
   173  // InitializeChannel allocates the state provider and should be invoked once per channel per execution
   174  func (g *gossipServiceImpl) InitializeChannel(chainID string, committer committer.Committer, endpoints []string) {
   175  	g.lock.Lock()
   176  	defer g.lock.Unlock()
   177  	// Initialize new state provider for given committer
   178  	logger.Debug("Creating state provider for chainID", chainID)
   179  	g.chains[chainID] = state.NewGossipStateProvider(chainID, g, committer, g.mcs)
   180  	if g.deliveryService == nil {
   181  		var err error
   182  		g.deliveryService, err = g.deliveryFactory.Service(gossipServiceInstance, endpoints, g.mcs)
   183  		if err != nil {
   184  			logger.Warning("Cannot create delivery client, due to", err)
   185  		}
   186  	}
   187  
   188  	// Delivery service might be nil only if it was not able to get connected
   189  	// to the ordering service
   190  	if g.deliveryService != nil {
   191  		// Parameters:
   192  		//              - peer.gossip.useLeaderElection
   193  		//              - peer.gossip.orgLeader
   194  		//
   195  		// are mutual exclusive, setting both to true is not defined, hence
   196  		// peer will panic and terminate
   197  		leaderElection := viper.GetBool("peer.gossip.useLeaderElection")
   198  		isStaticOrgLeader := viper.GetBool("peer.gossip.orgLeader")
   199  
   200  		if leaderElection && isStaticOrgLeader {
   201  			logger.Panic("Setting both orgLeader and useLeaderElection to true isn't supported, aborting execution")
   202  		}
   203  
   204  		if leaderElection {
   205  			logger.Debug("Delivery uses dynamic leader election mechanism, channel", chainID)
   206  			g.leaderElection[chainID] = g.newLeaderElectionComponent(chainID, g.onStatusChangeFactory(chainID, committer))
   207  		} else if isStaticOrgLeader {
   208  			logger.Debug("This peer is configured to connect to ordering service for blocks delivery, channel", chainID)
   209  			g.deliveryService.StartDeliverForChannel(chainID, committer, func() {})
   210  		} else {
   211  			logger.Debug("This peer is not configured to connect to ordering service for blocks delivery, channel", chainID)
   212  		}
   213  	} else {
   214  		logger.Warning("Delivery client is down won't be able to pull blocks for chain", chainID)
   215  	}
   216  }
   217  
   218  // configUpdated constructs a joinChannelMessage and sends it to the gossipSvc
   219  func (g *gossipServiceImpl) configUpdated(config Config) {
   220  	myOrg := string(g.secAdv.OrgByPeerIdentity(api.PeerIdentityType(g.peerIdentity)))
   221  	if !g.amIinChannel(myOrg, config) {
   222  		logger.Error("Tried joining channel", config.ChainID(), "but our org(", myOrg, "), isn't "+
   223  			"among the orgs of the channel:", orgListFromConfig(config), ", aborting.")
   224  		return
   225  	}
   226  	jcm := &joinChannelMessage{seqNum: config.Sequence(), members2AnchorPeers: map[string][]api.AnchorPeer{}}
   227  	for _, appOrg := range config.Organizations() {
   228  		logger.Debug(appOrg.MSPID(), "anchor peers:", appOrg.AnchorPeers())
   229  		jcm.members2AnchorPeers[appOrg.MSPID()] = []api.AnchorPeer{}
   230  		for _, ap := range appOrg.AnchorPeers() {
   231  			anchorPeer := api.AnchorPeer{
   232  				Host: ap.Host,
   233  				Port: int(ap.Port),
   234  			}
   235  			jcm.members2AnchorPeers[appOrg.MSPID()] = append(jcm.members2AnchorPeers[appOrg.MSPID()], anchorPeer)
   236  		}
   237  	}
   238  
   239  	// Initialize new state provider for given committer
   240  	logger.Debug("Creating state provider for chainID", config.ChainID())
   241  	g.JoinChan(jcm, gossipCommon.ChainID(config.ChainID()))
   242  }
   243  
   244  // GetBlock returns block for given chain
   245  func (g *gossipServiceImpl) GetBlock(chainID string, index uint64) *common.Block {
   246  	g.lock.RLock()
   247  	defer g.lock.RUnlock()
   248  	return g.chains[chainID].GetBlock(index)
   249  }
   250  
   251  // AddPayload appends message payload to for given chain
   252  func (g *gossipServiceImpl) AddPayload(chainID string, payload *proto.Payload) error {
   253  	g.lock.RLock()
   254  	defer g.lock.RUnlock()
   255  	return g.chains[chainID].AddPayload(payload)
   256  }
   257  
   258  // Stop stops the gossip component
   259  func (g *gossipServiceImpl) Stop() {
   260  	g.lock.Lock()
   261  	defer g.lock.Unlock()
   262  	for _, ch := range g.chains {
   263  		logger.Info("Stopping chain", ch)
   264  		ch.Stop()
   265  	}
   266  
   267  	for chainID, electionService := range g.leaderElection {
   268  		logger.Infof("Stopping leader election for %s", chainID)
   269  		electionService.Stop()
   270  	}
   271  	g.gossipSvc.Stop()
   272  	if g.deliveryService != nil {
   273  		g.deliveryService.Stop()
   274  	}
   275  }
   276  
   277  func (g *gossipServiceImpl) newLeaderElectionComponent(chainID string, callback func(bool)) election.LeaderElectionService {
   278  	PKIid := g.idMapper.GetPKIidOfCert(g.peerIdentity)
   279  	adapter := election.NewAdapter(g, PKIid, gossipCommon.ChainID(chainID))
   280  	return election.NewLeaderElectionService(adapter, string(PKIid), callback)
   281  }
   282  
   283  func (g *gossipServiceImpl) amIinChannel(myOrg string, config Config) bool {
   284  	for _, orgName := range orgListFromConfig(config) {
   285  		if orgName == myOrg {
   286  			return true
   287  		}
   288  	}
   289  	return false
   290  }
   291  
   292  func (g *gossipServiceImpl) onStatusChangeFactory(chainID string, committer blocksprovider.LedgerInfo) func(bool) {
   293  	return func(isLeader bool) {
   294  		if isLeader {
   295  			yield := func() {
   296  				g.lock.RLock()
   297  				le := g.leaderElection[chainID]
   298  				g.lock.RUnlock()
   299  				le.Yield()
   300  			}
   301  			logger.Info("Elected as a leader, starting delivery service for channel", chainID)
   302  			if err := g.deliveryService.StartDeliverForChannel(chainID, committer, yield); err != nil {
   303  				logger.Error("Delivery service is not able to start blocks delivery for chain, due to", err)
   304  			}
   305  		} else {
   306  			logger.Info("Renounced leadership, stopping delivery service for channel", chainID)
   307  			if err := g.deliveryService.StopDeliverForChannel(chainID); err != nil {
   308  				logger.Error("Delivery service is not able to stop blocks delivery for chain, due to", err)
   309  			}
   310  
   311  		}
   312  
   313  	}
   314  }
   315  
   316  func orgListFromConfig(config Config) []string {
   317  	var orgList []string
   318  	for _, appOrg := range config.Organizations() {
   319  		orgList = append(orgList, appOrg.MSPID())
   320  	}
   321  	return orgList
   322  }