github.com/myafeier/fabric@v1.0.1-0.20170722181825-3a4b1f2bce86/gossip/service/gossip_service.go (about)

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