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 }