551 lines
20 KiB
Go
551 lines
20 KiB
Go
/*
|
|
Copyright IBM Corp. All Rights Reserved.
|
|
|
|
SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
package service
|
|
|
|
import (
|
|
"fmt"
|
|
"sync"
|
|
|
|
"github.com/hyperledger/fabric/bccsp"
|
|
|
|
cb "github.com/hyperledger/fabric-protos-go/common"
|
|
gproto "github.com/hyperledger/fabric-protos-go/gossip"
|
|
tspb "github.com/hyperledger/fabric-protos-go/transientstore"
|
|
"github.com/hyperledger/fabric/common/flogging"
|
|
"github.com/hyperledger/fabric/core/committer"
|
|
"github.com/hyperledger/fabric/core/committer/txvalidator"
|
|
"github.com/hyperledger/fabric/core/common/privdata"
|
|
"github.com/hyperledger/fabric/core/deliverservice"
|
|
"github.com/hyperledger/fabric/core/transientstore"
|
|
"github.com/hyperledger/fabric/gossip/api"
|
|
"github.com/hyperledger/fabric/gossip/comm"
|
|
"github.com/hyperledger/fabric/gossip/common"
|
|
"github.com/hyperledger/fabric/gossip/discovery"
|
|
"github.com/hyperledger/fabric/gossip/election"
|
|
"github.com/hyperledger/fabric/gossip/filter"
|
|
"github.com/hyperledger/fabric/gossip/gossip"
|
|
gossipmetrics "github.com/hyperledger/fabric/gossip/metrics"
|
|
gossipprivdata "github.com/hyperledger/fabric/gossip/privdata"
|
|
"github.com/hyperledger/fabric/gossip/protoext"
|
|
"github.com/hyperledger/fabric/gossip/state"
|
|
"github.com/hyperledger/fabric/gossip/util"
|
|
corecomm "github.com/hyperledger/fabric/internal/pkg/comm"
|
|
"github.com/hyperledger/fabric/internal/pkg/identity"
|
|
"github.com/hyperledger/fabric/internal/pkg/peer/blocksprovider"
|
|
"github.com/hyperledger/fabric/internal/pkg/peer/orderers"
|
|
"github.com/hyperledger/fabric/protoutil"
|
|
"github.com/pkg/errors"
|
|
"google.golang.org/grpc"
|
|
)
|
|
|
|
// gossipSvc is the interface of the gossip component.
|
|
type gossipSvc interface {
|
|
// SelfMembershipInfo returns the peer's membership information
|
|
SelfMembershipInfo() discovery.NetworkMember
|
|
|
|
// SelfChannelInfo returns the peer's latest StateInfo message of a given channel
|
|
SelfChannelInfo(common.ChannelID) *protoext.SignedGossipMessage
|
|
|
|
// Send sends a message to remote peers
|
|
Send(msg *gproto.GossipMessage, peers ...*comm.RemotePeer)
|
|
|
|
// SendByCriteria sends a given message to all peers that match the given SendCriteria
|
|
SendByCriteria(*protoext.SignedGossipMessage, gossip.SendCriteria) error
|
|
|
|
// GetPeers returns the NetworkMembers considered alive
|
|
Peers() []discovery.NetworkMember
|
|
|
|
// PeersOfChannel returns the NetworkMembers considered alive
|
|
// and also subscribed to the channel given
|
|
PeersOfChannel(common.ChannelID) []discovery.NetworkMember
|
|
|
|
// UpdateMetadata updates the self metadata of the discovery layer
|
|
// the peer publishes to other peers
|
|
UpdateMetadata(metadata []byte)
|
|
|
|
// UpdateLedgerHeight updates the ledger height the peer
|
|
// publishes to other peers in the channel
|
|
UpdateLedgerHeight(height uint64, channelID common.ChannelID)
|
|
|
|
// UpdateChaincodes updates the chaincodes the peer publishes
|
|
// to other peers in the channel
|
|
UpdateChaincodes(chaincode []*gproto.Chaincode, channelID common.ChannelID)
|
|
|
|
// Gossip sends a message to other peers to the network
|
|
Gossip(msg *gproto.GossipMessage)
|
|
|
|
// PeerFilter receives a SubChannelSelectionCriteria and returns a RoutingFilter that selects
|
|
// only peer identities that match the given criteria, and that they published their channel participation
|
|
PeerFilter(channel common.ChannelID, messagePredicate api.SubChannelSelectionCriteria) (filter.RoutingFilter, error)
|
|
|
|
// Accept returns a dedicated read-only channel for messages sent by other nodes that match a certain predicate.
|
|
// If passThrough is false, the messages are processed by the gossip layer beforehand.
|
|
// If passThrough is true, the gossip layer doesn't intervene and the messages
|
|
// can be used to send a reply back to the sender
|
|
Accept(acceptor common.MessageAcceptor, passThrough bool) (<-chan *gproto.GossipMessage, <-chan protoext.ReceivedMessage)
|
|
|
|
// JoinChan makes the Gossip instance join a channel
|
|
JoinChan(joinMsg api.JoinChannelMessage, channelID common.ChannelID)
|
|
|
|
// LeaveChan makes the Gossip instance leave a channel.
|
|
// It still disseminates stateInfo message, but doesn't participate
|
|
// in block pulling anymore, and can't return anymore a list of peers
|
|
// in the channel.
|
|
LeaveChan(channelID common.ChannelID)
|
|
|
|
// SuspectPeers makes the gossip instance validate identities of suspected peers, and close
|
|
// any connections to peers with identities that are found invalid
|
|
SuspectPeers(s api.PeerSuspector)
|
|
|
|
// IdentityInfo returns information known peer identities
|
|
IdentityInfo() api.PeerIdentitySet
|
|
|
|
// IsInMyOrg checks whether a network member is in this peer's org
|
|
IsInMyOrg(member discovery.NetworkMember) bool
|
|
|
|
// Stop stops the gossip component
|
|
Stop()
|
|
}
|
|
|
|
// GossipServiceAdapter serves to provide basic functionality
|
|
// required from gossip service by delivery service
|
|
type GossipServiceAdapter interface {
|
|
// PeersOfChannel returns slice with members of specified channel
|
|
PeersOfChannel(common.ChannelID) []discovery.NetworkMember
|
|
|
|
// AddPayload adds payload to the local state sync buffer
|
|
AddPayload(channelID string, payload *gproto.Payload) error
|
|
|
|
// Gossip the message across the peers
|
|
Gossip(msg *gproto.GossipMessage)
|
|
}
|
|
|
|
// DeliveryServiceFactory factory to create and initialize delivery service instance
|
|
type DeliveryServiceFactory interface {
|
|
// Returns an instance of delivery client
|
|
Service(g GossipServiceAdapter, ordererSource *orderers.ConnectionSource, mcs api.MessageCryptoService, isStaticLead bool, channelConfig *cb.Config, cryptoProvider bccsp.BCCSP) deliverservice.DeliverService
|
|
}
|
|
|
|
type deliveryFactoryImpl struct {
|
|
signer identity.SignerSerializer
|
|
credentialSupport *corecomm.CredentialSupport
|
|
deliverServiceConfig *deliverservice.DeliverServiceConfig
|
|
}
|
|
|
|
// Returns an instance of delivery service
|
|
func (df *deliveryFactoryImpl) Service(
|
|
g GossipServiceAdapter,
|
|
ordererSource *orderers.ConnectionSource,
|
|
mcs api.MessageCryptoService, isStaticLead bool,
|
|
channelConfig *cb.Config,
|
|
cryptoProvider bccsp.BCCSP,
|
|
) deliverservice.DeliverService {
|
|
return deliverservice.NewDeliverService(
|
|
&deliverservice.Config{
|
|
IsStaticLeader: isStaticLead,
|
|
CryptoSvc: mcs,
|
|
Gossip: g,
|
|
Signer: df.signer,
|
|
DeliverServiceConfig: df.deliverServiceConfig,
|
|
OrdererSource: ordererSource,
|
|
ChannelConfig: channelConfig,
|
|
CryptoProvider: cryptoProvider,
|
|
})
|
|
}
|
|
|
|
type privateHandler struct {
|
|
support Support
|
|
coordinator gossipprivdata.Coordinator
|
|
distributor gossipprivdata.PvtDataDistributor
|
|
reconciler gossipprivdata.PvtDataReconciler
|
|
}
|
|
|
|
func (p privateHandler) close() {
|
|
p.coordinator.Close()
|
|
p.reconciler.Stop()
|
|
}
|
|
|
|
// GossipService handles the interaction between gossip service and peer
|
|
type GossipService struct {
|
|
gossipSvc
|
|
privateHandlers map[string]privateHandler
|
|
chains map[string]state.GossipStateProvider
|
|
leaderElection map[string]election.LeaderElectionService
|
|
deliveryService map[string]deliverservice.DeliverService
|
|
deliveryFactory DeliveryServiceFactory
|
|
lock sync.RWMutex
|
|
mcs api.MessageCryptoService
|
|
peerIdentity []byte
|
|
secAdv api.SecurityAdvisor
|
|
metrics *gossipmetrics.GossipMetrics
|
|
serviceConfig *ServiceConfig
|
|
privdataConfig *gossipprivdata.PrivdataConfig
|
|
anchorPeerTracker *anchorPeerTracker
|
|
}
|
|
|
|
// This is an implementation of api.JoinChannelMessage.
|
|
type joinChannelMessage struct {
|
|
seqNum uint64
|
|
members2AnchorPeers map[string][]api.AnchorPeer
|
|
}
|
|
|
|
func (jcm *joinChannelMessage) SequenceNumber() uint64 {
|
|
return jcm.seqNum
|
|
}
|
|
|
|
// Members returns the organizations of the channel
|
|
func (jcm *joinChannelMessage) Members() []api.OrgIdentityType {
|
|
members := make([]api.OrgIdentityType, 0, len(jcm.members2AnchorPeers))
|
|
for org := range jcm.members2AnchorPeers {
|
|
members = append(members, api.OrgIdentityType(org))
|
|
}
|
|
return members
|
|
}
|
|
|
|
// AnchorPeersOf returns the anchor peers of the given organization
|
|
func (jcm *joinChannelMessage) AnchorPeersOf(org api.OrgIdentityType) []api.AnchorPeer {
|
|
return jcm.members2AnchorPeers[string(org)]
|
|
}
|
|
|
|
// anchorPeerTracker maintains anchor peer endpoints for all the channels.
|
|
type anchorPeerTracker struct {
|
|
// allEndpoints contains anchor peer endpoints for all the channels,
|
|
// its key is channel name, value is map of anchor peer endpoints
|
|
allEndpoints map[string]map[string]struct{}
|
|
mutex sync.RWMutex
|
|
}
|
|
|
|
// update overwrites the anchor peer endpoints for the channel
|
|
func (t *anchorPeerTracker) update(channelName string, endpoints map[string]struct{}) {
|
|
t.mutex.Lock()
|
|
defer t.mutex.Unlock()
|
|
t.allEndpoints[channelName] = endpoints
|
|
}
|
|
|
|
// IsAnchorPeer checks if an endpoint is an anchor peer in any channel
|
|
func (t *anchorPeerTracker) IsAnchorPeer(endpoint string) bool {
|
|
t.mutex.RLock()
|
|
defer t.mutex.RUnlock()
|
|
for _, endpointsForChannel := range t.allEndpoints {
|
|
if _, ok := endpointsForChannel[endpoint]; ok {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
var logger = util.GetLogger(util.ServiceLogger, "")
|
|
|
|
// New creates the gossip service.
|
|
func New(
|
|
peerIdentity identity.SignerSerializer,
|
|
gossipMetrics *gossipmetrics.GossipMetrics,
|
|
endpoint string,
|
|
s *grpc.Server,
|
|
mcs api.MessageCryptoService,
|
|
secAdv api.SecurityAdvisor,
|
|
secureDialOpts api.PeerSecureDialOpts,
|
|
credSupport *corecomm.CredentialSupport,
|
|
gossipConfig *gossip.Config,
|
|
serviceConfig *ServiceConfig,
|
|
privdataConfig *gossipprivdata.PrivdataConfig,
|
|
deliverServiceConfig *deliverservice.DeliverServiceConfig,
|
|
) (*GossipService, error) {
|
|
serializedIdentity, err := peerIdentity.Serialize()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
logger.Infof("Initialize gossip with endpoint %s", endpoint)
|
|
|
|
anchorPeerTracker := &anchorPeerTracker{allEndpoints: map[string]map[string]struct{}{}}
|
|
gossipComponent := gossip.New(
|
|
gossipConfig,
|
|
s,
|
|
secAdv,
|
|
mcs,
|
|
serializedIdentity,
|
|
secureDialOpts,
|
|
gossipMetrics,
|
|
anchorPeerTracker,
|
|
)
|
|
|
|
return &GossipService{
|
|
gossipSvc: gossipComponent,
|
|
mcs: mcs,
|
|
privateHandlers: make(map[string]privateHandler),
|
|
chains: make(map[string]state.GossipStateProvider),
|
|
leaderElection: make(map[string]election.LeaderElectionService),
|
|
deliveryService: make(map[string]deliverservice.DeliverService),
|
|
deliveryFactory: &deliveryFactoryImpl{
|
|
signer: peerIdentity,
|
|
credentialSupport: credSupport,
|
|
deliverServiceConfig: deliverServiceConfig,
|
|
},
|
|
peerIdentity: serializedIdentity,
|
|
secAdv: secAdv,
|
|
metrics: gossipMetrics,
|
|
serviceConfig: serviceConfig,
|
|
privdataConfig: privdataConfig,
|
|
anchorPeerTracker: anchorPeerTracker,
|
|
}, nil
|
|
}
|
|
|
|
// DistributePrivateData distribute private read write set inside the channel based on the collections policies
|
|
func (g *GossipService) DistributePrivateData(channelID string, txID string, privData *tspb.TxPvtReadWriteSetWithConfigInfo, blkHt uint64) error {
|
|
g.lock.RLock()
|
|
handler, exists := g.privateHandlers[channelID]
|
|
g.lock.RUnlock()
|
|
if !exists {
|
|
return errors.Errorf("No private data handler for %s", channelID)
|
|
}
|
|
|
|
if err := handler.distributor.Distribute(txID, privData, blkHt); err != nil {
|
|
err := errors.WithMessagef(err, "failed to distribute private collection, txID %s, channel %s", txID, channelID)
|
|
logger.Error(err)
|
|
return err
|
|
}
|
|
|
|
if err := handler.coordinator.StorePvtData(txID, privData, blkHt); err != nil {
|
|
logger.Error("Failed to store private data into transient store, txID",
|
|
txID, "channel", channelID, "due to", err)
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// NewConfigEventer creates a ConfigProcessor which the channelconfig.BundleSource can ultimately route config updates to
|
|
func (g *GossipService) NewConfigEventer() ConfigProcessor {
|
|
return newConfigEventer(g)
|
|
}
|
|
|
|
// Support aggregates functionality of several
|
|
// interfaces required by gossip service
|
|
type Support struct {
|
|
Validator txvalidator.Validator
|
|
Committer committer.Committer
|
|
CollectionStore privdata.CollectionStore
|
|
IdDeserializeFactory gossipprivdata.IdentityDeserializerFactory
|
|
CapabilityProvider gossipprivdata.CapabilityProvider
|
|
}
|
|
|
|
// InitializeChannel allocates the state provider and should be invoked once per channel per execution
|
|
func (g *GossipService) InitializeChannel(channelID string, ordererSource *orderers.ConnectionSource, store *transientstore.Store, support Support, channelConfig *cb.Config, cryptoProvider bccsp.BCCSP) {
|
|
g.lock.Lock()
|
|
defer g.lock.Unlock()
|
|
// Initialize new state provider for given committer
|
|
logger.Debug("Creating state provider for channelID", channelID)
|
|
servicesAdapter := &state.ServicesMediator{GossipAdapter: g, MCSAdapter: g.mcs}
|
|
|
|
// Initialize private data fetcher
|
|
dataRetriever := gossipprivdata.NewDataRetriever(channelID, store, support.Committer)
|
|
collectionAccessFactory := gossipprivdata.NewCollectionAccessFactory(support.IdDeserializeFactory)
|
|
fetcher := gossipprivdata.NewPuller(g.metrics.PrivdataMetrics, support.CollectionStore, g.gossipSvc, dataRetriever,
|
|
collectionAccessFactory, channelID, g.serviceConfig.BtlPullMargin)
|
|
|
|
coordinatorConfig := gossipprivdata.CoordinatorConfig{
|
|
TransientBlockRetention: g.serviceConfig.TransientstoreMaxBlockRetention,
|
|
PullRetryThreshold: g.serviceConfig.PvtDataPullRetryThreshold,
|
|
SkipPullingInvalidTransactions: g.serviceConfig.SkipPullingInvalidTransactionsDuringCommit,
|
|
}
|
|
selfSignedData := g.createSelfSignedData()
|
|
mspID := string(g.secAdv.OrgByPeerIdentity(selfSignedData.Identity))
|
|
coordinator := gossipprivdata.NewCoordinator(mspID, gossipprivdata.Support{
|
|
ChainID: channelID,
|
|
CollectionStore: support.CollectionStore,
|
|
Validator: support.Validator,
|
|
Committer: support.Committer,
|
|
Fetcher: fetcher,
|
|
CapabilityProvider: support.CapabilityProvider,
|
|
}, store, selfSignedData, g.metrics.PrivdataMetrics, coordinatorConfig,
|
|
support.IdDeserializeFactory)
|
|
|
|
var reconciler gossipprivdata.PvtDataReconciler
|
|
|
|
if g.privdataConfig.ReconciliationEnabled {
|
|
reconciler = gossipprivdata.NewReconciler(channelID, g.metrics.PrivdataMetrics,
|
|
support.Committer, fetcher, g.privdataConfig)
|
|
} else {
|
|
reconciler = &gossipprivdata.NoOpReconciler{}
|
|
}
|
|
|
|
pushAckTimeout := g.serviceConfig.PvtDataPushAckTimeout
|
|
g.privateHandlers[channelID] = privateHandler{
|
|
support: support,
|
|
coordinator: coordinator,
|
|
distributor: gossipprivdata.NewDistributor(channelID, g, collectionAccessFactory, g.metrics.PrivdataMetrics, pushAckTimeout),
|
|
reconciler: reconciler,
|
|
}
|
|
g.privateHandlers[channelID].reconciler.Start()
|
|
|
|
blockingMode := !g.serviceConfig.NonBlockingCommitMode
|
|
stateConfig := state.GlobalConfig()
|
|
g.chains[channelID] = state.NewGossipStateProvider(
|
|
flogging.MustGetLogger(util.StateLogger),
|
|
channelID,
|
|
servicesAdapter,
|
|
coordinator,
|
|
g.metrics.StateMetrics,
|
|
blockingMode,
|
|
stateConfig)
|
|
if g.deliveryService[channelID] == nil {
|
|
g.deliveryService[channelID] = g.deliveryFactory.Service(g, ordererSource, g.mcs, g.serviceConfig.OrgLeader, channelConfig, cryptoProvider)
|
|
}
|
|
|
|
// Delivery service might be nil only if it was not able to get connected
|
|
// to the ordering service
|
|
if g.deliveryService[channelID] != nil {
|
|
// Parameters:
|
|
// - peer.gossip.useLeaderElection
|
|
// - peer.gossip.orgLeader
|
|
//
|
|
// are mutual exclusive, setting both to true is not defined, hence
|
|
// peer will panic and terminate
|
|
leaderElection := g.serviceConfig.UseLeaderElection
|
|
isStaticOrgLeader := g.serviceConfig.OrgLeader
|
|
|
|
if leaderElection && isStaticOrgLeader {
|
|
logger.Panic("Setting both orgLeader and useLeaderElection to true isn't supported, aborting execution")
|
|
}
|
|
|
|
if leaderElection {
|
|
logger.Debug("Delivery uses dynamic leader election mechanism, channel", channelID)
|
|
g.leaderElection[channelID] = g.newLeaderElectionComponent(channelID, g.onStatusChangeFactory(channelID,
|
|
support.Committer), g.metrics.ElectionMetrics)
|
|
} else if isStaticOrgLeader {
|
|
logger.Debug("This peer is configured to connect to ordering service for blocks delivery, channel", channelID)
|
|
g.deliveryService[channelID].StartDeliverForChannel(channelID, support.Committer, func() {})
|
|
} else {
|
|
logger.Debug("This peer is not configured to connect to ordering service for blocks delivery, channel", channelID)
|
|
}
|
|
} else {
|
|
logger.Warning("Delivery client is down won't be able to pull blocks for chain", channelID)
|
|
}
|
|
}
|
|
|
|
func (g *GossipService) createSelfSignedData() protoutil.SignedData {
|
|
msg := make([]byte, 32)
|
|
sig, err := g.mcs.Sign(msg)
|
|
if err != nil {
|
|
logger.Panicf("Failed creating self signed data because message signing failed: %v", err)
|
|
}
|
|
return protoutil.SignedData{
|
|
Data: msg,
|
|
Signature: sig,
|
|
Identity: g.peerIdentity,
|
|
}
|
|
}
|
|
|
|
// updateAnchors constructs a joinChannelMessage and sends it to the gossipSvc
|
|
func (g *GossipService) updateAnchors(configUpdate ConfigUpdate) {
|
|
myOrg := string(g.secAdv.OrgByPeerIdentity(api.PeerIdentityType(g.peerIdentity)))
|
|
if !g.amIinChannel(myOrg, configUpdate) {
|
|
logger.Error("Tried joining channel", configUpdate.ChannelID, "but our org(", myOrg, "), isn't "+
|
|
"among the orgs of the channel:", orgListFromConfigUpdate(configUpdate), ", aborting.")
|
|
return
|
|
}
|
|
jcm := &joinChannelMessage{seqNum: configUpdate.Sequence, members2AnchorPeers: map[string][]api.AnchorPeer{}}
|
|
anchorPeerEndpoints := map[string]struct{}{}
|
|
for _, appOrg := range configUpdate.Organizations {
|
|
logger.Debug(appOrg.MSPID(), "anchor peers:", appOrg.AnchorPeers())
|
|
jcm.members2AnchorPeers[appOrg.MSPID()] = []api.AnchorPeer{}
|
|
for _, ap := range appOrg.AnchorPeers() {
|
|
anchorPeer := api.AnchorPeer{
|
|
Host: ap.Host,
|
|
Port: int(ap.Port),
|
|
}
|
|
jcm.members2AnchorPeers[appOrg.MSPID()] = append(jcm.members2AnchorPeers[appOrg.MSPID()], anchorPeer)
|
|
anchorPeerEndpoints[fmt.Sprintf("%s:%d", ap.Host, ap.Port)] = struct{}{}
|
|
}
|
|
}
|
|
g.anchorPeerTracker.update(configUpdate.ChannelID, anchorPeerEndpoints)
|
|
|
|
// Initialize new state provider for given committer
|
|
logger.Debug("Creating state provider for channelID", configUpdate.ChannelID)
|
|
g.JoinChan(jcm, common.ChannelID(configUpdate.ChannelID))
|
|
}
|
|
|
|
// AddPayload appends message payload to for given chain
|
|
func (g *GossipService) AddPayload(channelID string, payload *gproto.Payload) error {
|
|
g.lock.RLock()
|
|
defer g.lock.RUnlock()
|
|
return g.chains[channelID].AddPayload(payload)
|
|
}
|
|
|
|
// Stop stops the gossip component
|
|
func (g *GossipService) Stop() {
|
|
g.lock.Lock()
|
|
defer g.lock.Unlock()
|
|
|
|
for chainID := range g.chains {
|
|
logger.Info("Stopping chain", chainID)
|
|
if le, exists := g.leaderElection[chainID]; exists {
|
|
logger.Infof("Stopping leader election for %s", chainID)
|
|
le.Stop()
|
|
}
|
|
g.chains[chainID].Stop()
|
|
g.privateHandlers[chainID].close()
|
|
|
|
if g.deliveryService[chainID] != nil {
|
|
g.deliveryService[chainID].Stop()
|
|
}
|
|
}
|
|
g.gossipSvc.Stop()
|
|
}
|
|
|
|
func (g *GossipService) newLeaderElectionComponent(channelID string, callback func(bool),
|
|
electionMetrics *gossipmetrics.ElectionMetrics) election.LeaderElectionService {
|
|
PKIid := g.mcs.GetPKIidOfCert(g.peerIdentity)
|
|
adapter := election.NewAdapter(g, PKIid, common.ChannelID(channelID), electionMetrics)
|
|
config := election.ElectionConfig{
|
|
StartupGracePeriod: g.serviceConfig.ElectionStartupGracePeriod,
|
|
MembershipSampleInterval: g.serviceConfig.ElectionMembershipSampleInterval,
|
|
LeaderAliveThreshold: g.serviceConfig.ElectionLeaderAliveThreshold,
|
|
LeaderElectionDuration: g.serviceConfig.ElectionLeaderElectionDuration,
|
|
}
|
|
return election.NewLeaderElectionService(adapter, string(PKIid), callback, config)
|
|
}
|
|
|
|
func (g *GossipService) amIinChannel(myOrg string, configUpdate ConfigUpdate) bool {
|
|
for _, orgName := range orgListFromConfigUpdate(configUpdate) {
|
|
if orgName == myOrg {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func (g *GossipService) onStatusChangeFactory(channelID string, committer blocksprovider.LedgerInfo) func(bool) {
|
|
return func(isLeader bool) {
|
|
if isLeader {
|
|
yield := func() {
|
|
g.lock.RLock()
|
|
le := g.leaderElection[channelID]
|
|
g.lock.RUnlock()
|
|
le.Yield()
|
|
}
|
|
logger.Info("Elected as a leader, starting delivery service for channel", channelID)
|
|
if err := g.deliveryService[channelID].StartDeliverForChannel(channelID, committer, yield); err != nil {
|
|
logger.Errorf("Delivery service is not able to start blocks delivery for chain, due to %+v", err)
|
|
}
|
|
} else {
|
|
logger.Info("Renounced leadership, stopping delivery service for channel", channelID)
|
|
if err := g.deliveryService[channelID].StopDeliverForChannel(); err != nil {
|
|
logger.Errorf("Delivery service is not able to stop blocks delivery for chain, due to %+v", err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func orgListFromConfigUpdate(config ConfigUpdate) []string {
|
|
var orgList []string
|
|
for _, appOrg := range config.Organizations {
|
|
orgList = append(orgList, appOrg.MSPID())
|
|
}
|
|
return orgList
|
|
}
|