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 }