github.com/tenywen/fabric@v1.0.0-beta.0.20170620030522-a5b1ed380643/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) {
   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  	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) {
   137  	once.Do(func() {
   138  		if overrideEndpoint := viper.GetString("peer.gossip.endpoint"); overrideEndpoint != "" {
   139  			endpoint = overrideEndpoint
   140  		}
   141  
   142  		logger.Info("Initialize gossip with endpoint", endpoint, "and bootstrap set", bootPeers)
   143  
   144  		idMapper := identity.NewIdentityMapper(mcs, peerIdentity)
   145  		gossip := integration.NewGossipComponent(peerIdentity, endpoint, s, secAdv,
   146  			mcs, idMapper, secureDialOpts, bootPeers...)
   147  		gossipServiceInstance = &gossipServiceImpl{
   148  			mcs:             mcs,
   149  			gossipSvc:       gossip,
   150  			chains:          make(map[string]state.GossipStateProvider),
   151  			leaderElection:  make(map[string]election.LeaderElectionService),
   152  			deliveryFactory: factory,
   153  			idMapper:        idMapper,
   154  			peerIdentity:    peerIdentity,
   155  			secAdv:          secAdv,
   156  		}
   157  	})
   158  }
   159  
   160  // GetGossipService returns an instance of gossip service
   161  func GetGossipService() GossipService {
   162  	return gossipServiceInstance
   163  }
   164  
   165  // NewConfigEventer creates a ConfigProcessor which the configtx.Manager can ultimately route config updates to
   166  func (g *gossipServiceImpl) NewConfigEventer() ConfigProcessor {
   167  	return newConfigEventer(g)
   168  }
   169  
   170  // InitializeChannel allocates the state provider and should be invoked once per channel per execution
   171  func (g *gossipServiceImpl) InitializeChannel(chainID string, committer committer.Committer, endpoints []string) {
   172  	g.lock.Lock()
   173  	defer g.lock.Unlock()
   174  	// Initialize new state provider for given committer
   175  	logger.Debug("Creating state provider for chainID", chainID)
   176  	g.chains[chainID] = state.NewGossipStateProvider(chainID, g, committer, g.mcs)
   177  	if g.deliveryService == nil {
   178  		var err error
   179  		g.deliveryService, err = g.deliveryFactory.Service(gossipServiceInstance, endpoints, g.mcs)
   180  		if err != nil {
   181  			logger.Warning("Cannot create delivery client, due to", err)
   182  		}
   183  	}
   184  
   185  	// Delivery service might be nil only if it was not able to get connected
   186  	// to the ordering service
   187  	if g.deliveryService != nil {
   188  		// Parameters:
   189  		//              - peer.gossip.useLeaderElection
   190  		//              - peer.gossip.orgLeader
   191  		//
   192  		// are mutual exclusive, setting both to true is not defined, hence
   193  		// peer will panic and terminate
   194  		leaderElection := viper.GetBool("peer.gossip.useLeaderElection")
   195  		isStaticOrgLeader := viper.GetBool("peer.gossip.orgLeader")
   196  
   197  		if leaderElection && isStaticOrgLeader {
   198  			logger.Panic("Setting both orgLeader and useLeaderElection to true isn't supported, aborting execution")
   199  		}
   200  
   201  		if leaderElection {
   202  			logger.Debug("Delivery uses dynamic leader election mechanism, channel", chainID)
   203  			g.leaderElection[chainID] = g.newLeaderElectionComponent(chainID, g.onStatusChangeFactory(chainID, committer))
   204  		} else if isStaticOrgLeader {
   205  			logger.Debug("This peer is configured to connect to ordering service for blocks delivery, channel", chainID)
   206  			g.deliveryService.StartDeliverForChannel(chainID, committer)
   207  		} else {
   208  			logger.Debug("This peer is not configured to connect to ordering service for blocks delivery, channel", chainID)
   209  		}
   210  	} else {
   211  		logger.Warning("Delivery client is down won't be able to pull blocks for chain", chainID)
   212  	}
   213  }
   214  
   215  // configUpdated constructs a joinChannelMessage and sends it to the gossipSvc
   216  func (g *gossipServiceImpl) configUpdated(config Config) {
   217  	myOrg := string(g.secAdv.OrgByPeerIdentity(api.PeerIdentityType(g.peerIdentity)))
   218  	if !g.amIinChannel(myOrg, config) {
   219  		logger.Error("Tried joining channel", config.ChainID(), "but our org(", myOrg, "), isn't "+
   220  			"among the orgs of the channel:", orgListFromConfig(config), ", aborting.")
   221  		return
   222  	}
   223  	jcm := &joinChannelMessage{seqNum: config.Sequence(), members2AnchorPeers: map[string][]api.AnchorPeer{}}
   224  	for _, appOrg := range config.Organizations() {
   225  		logger.Debug(appOrg.MSPID(), "anchor peers:", appOrg.AnchorPeers())
   226  		jcm.members2AnchorPeers[appOrg.MSPID()] = []api.AnchorPeer{}
   227  		for _, ap := range appOrg.AnchorPeers() {
   228  			anchorPeer := api.AnchorPeer{
   229  				Host: ap.Host,
   230  				Port: int(ap.Port),
   231  			}
   232  			jcm.members2AnchorPeers[appOrg.MSPID()] = append(jcm.members2AnchorPeers[appOrg.MSPID()], anchorPeer)
   233  		}
   234  	}
   235  
   236  	// Initialize new state provider for given committer
   237  	logger.Debug("Creating state provider for chainID", config.ChainID())
   238  	g.JoinChan(jcm, gossipCommon.ChainID(config.ChainID()))
   239  }
   240  
   241  // GetBlock returns block for given chain
   242  func (g *gossipServiceImpl) GetBlock(chainID string, index uint64) *common.Block {
   243  	g.lock.RLock()
   244  	defer g.lock.RUnlock()
   245  	return g.chains[chainID].GetBlock(index)
   246  }
   247  
   248  // AddPayload appends message payload to for given chain
   249  func (g *gossipServiceImpl) AddPayload(chainID string, payload *proto.Payload) error {
   250  	g.lock.RLock()
   251  	defer g.lock.RUnlock()
   252  	return g.chains[chainID].AddPayload(payload)
   253  }
   254  
   255  // Stop stops the gossip component
   256  func (g *gossipServiceImpl) Stop() {
   257  	g.lock.Lock()
   258  	defer g.lock.Unlock()
   259  	for _, ch := range g.chains {
   260  		logger.Info("Stopping chain", ch)
   261  		ch.Stop()
   262  	}
   263  
   264  	for chainID, electionService := range g.leaderElection {
   265  		logger.Infof("Stopping leader election for %s", chainID)
   266  		electionService.Stop()
   267  	}
   268  	g.gossipSvc.Stop()
   269  	if g.deliveryService != nil {
   270  		g.deliveryService.Stop()
   271  	}
   272  }
   273  
   274  func (g *gossipServiceImpl) newLeaderElectionComponent(chainID string, callback func(bool)) election.LeaderElectionService {
   275  	PKIid := g.idMapper.GetPKIidOfCert(g.peerIdentity)
   276  	adapter := election.NewAdapter(g, PKIid, gossipCommon.ChainID(chainID))
   277  	return election.NewLeaderElectionService(adapter, string(PKIid), callback)
   278  }
   279  
   280  func (g *gossipServiceImpl) amIinChannel(myOrg string, config Config) bool {
   281  	for _, orgName := range orgListFromConfig(config) {
   282  		if orgName == myOrg {
   283  			return true
   284  		}
   285  	}
   286  	return false
   287  }
   288  
   289  func (g *gossipServiceImpl) onStatusChangeFactory(chainID string, committer blocksprovider.LedgerInfo) func(bool) {
   290  	return func(isLeader bool) {
   291  		if isLeader {
   292  			logger.Info("Elected as a leader, starting delivery service for channel", chainID)
   293  			if err := g.deliveryService.StartDeliverForChannel(chainID, committer); err != nil {
   294  				logger.Error("Delivery service is not able to start blocks delivery for chain, due to", err)
   295  			}
   296  		} else {
   297  			logger.Info("Renounced leadership, stopping delivery service for channel", chainID)
   298  			if err := g.deliveryService.StopDeliverForChannel(chainID); err != nil {
   299  				logger.Error("Delivery service is not able to stop blocks delivery for chain, due to", err)
   300  			}
   301  
   302  		}
   303  
   304  	}
   305  }
   306  
   307  func orgListFromConfig(config Config) []string {
   308  	var orgList []string
   309  	for _, appOrg := range config.Organizations() {
   310  		orgList = append(orgList, appOrg.MSPID())
   311  	}
   312  	return orgList
   313  }