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 }