github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/discovery/endorsement/endorsement.go (about)

     1  /*
     2  Copyright hechain. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package endorsement
     8  
     9  import (
    10  	"fmt"
    11  
    12  	"github.com/hyperledger/fabric-protos-go/peer"
    13  
    14  	"github.com/hechain20/hechain/common/chaincode"
    15  	"github.com/hechain20/hechain/common/flogging"
    16  	"github.com/hechain20/hechain/common/graph"
    17  	"github.com/hechain20/hechain/common/policies"
    18  	"github.com/hechain20/hechain/common/policies/inquire"
    19  	"github.com/hechain20/hechain/gossip/api"
    20  	"github.com/hechain20/hechain/gossip/common"
    21  	gossipdiscovery "github.com/hechain20/hechain/gossip/discovery"
    22  	"github.com/hyperledger/fabric-protos-go/discovery"
    23  	"github.com/hyperledger/fabric-protos-go/msp"
    24  	"github.com/pkg/errors"
    25  )
    26  
    27  var logger = flogging.MustGetLogger("discovery.endorsement")
    28  
    29  type principalEvaluator interface {
    30  	// SatisfiesPrincipal returns whether a given peer identity satisfies a certain principal
    31  	// on a given channel
    32  	SatisfiesPrincipal(channel string, identity []byte, principal *msp.MSPPrincipal) error
    33  }
    34  
    35  type chaincodeMetadataFetcher interface {
    36  	// ChaincodeMetadata returns the metadata of the chaincode as appears in the ledger,
    37  	// or nil if the channel doesn't exist, or the chaincode isn't found in the ledger
    38  	Metadata(channel string, cc string, collections ...string) *chaincode.Metadata
    39  }
    40  
    41  type policyFetcher interface {
    42  	// PoliciesByChaincode returns the chaincode policy or existing collection level policies that can be
    43  	// inquired for which identities satisfy them
    44  	PoliciesByChaincode(channel string, cc string, collections ...string) []policies.InquireablePolicy
    45  }
    46  
    47  type gossipSupport interface {
    48  	// IdentityInfo returns identity information about peers
    49  	IdentityInfo() api.PeerIdentitySet
    50  
    51  	// PeersOfChannel returns the NetworkMembers considered alive
    52  	// and also subscribed to the channel given
    53  	PeersOfChannel(common.ChannelID) gossipdiscovery.Members
    54  
    55  	// Peers returns the NetworkMembers considered alive
    56  	Peers() gossipdiscovery.Members
    57  }
    58  
    59  type membersChaincodeMapping struct {
    60  	members          gossipdiscovery.Members
    61  	chaincodeMapping map[string]gossipdiscovery.NetworkMember
    62  }
    63  
    64  type endorsementAnalyzer struct {
    65  	gossipSupport
    66  	principalEvaluator
    67  	policyFetcher
    68  	chaincodeMetadataFetcher
    69  }
    70  
    71  // NewEndorsementAnalyzer constructs an NewEndorsementAnalyzer out of the given support
    72  func NewEndorsementAnalyzer(gs gossipSupport, pf policyFetcher, pe principalEvaluator, mf chaincodeMetadataFetcher) *endorsementAnalyzer {
    73  	return &endorsementAnalyzer{
    74  		gossipSupport:            gs,
    75  		policyFetcher:            pf,
    76  		principalEvaluator:       pe,
    77  		chaincodeMetadataFetcher: mf,
    78  	}
    79  }
    80  
    81  type peerPrincipalEvaluator func(member gossipdiscovery.NetworkMember, principal *msp.MSPPrincipal) bool
    82  
    83  // PeersForEndorsement returns an EndorsementDescriptor for a given set of peers, channel, and chaincode
    84  func (ea *endorsementAnalyzer) PeersForEndorsement(channelID common.ChannelID, interest *peer.ChaincodeInterest) (*discovery.EndorsementDescriptor, error) {
    85  	membersAndCC, err := ea.peersByCriteria(channelID, interest, false)
    86  	if err != nil {
    87  		return nil, errors.WithStack(err)
    88  	}
    89  	channelMembersById := membersAndCC.members.ByID()
    90  	// Choose only the alive messages of those that have joined the channel
    91  	aliveMembership := ea.Peers().Intersect(membersAndCC.members)
    92  	membersById := aliveMembership.ByID()
    93  	// Compute a mapping between the PKI-IDs of members to their identities
    94  	identitiesOfMembers := computeIdentitiesOfMembers(ea.IdentityInfo(), membersById)
    95  	principalsSets, err := ea.computePrincipalSets(channelID, interest)
    96  	if err != nil {
    97  		logger.Warningf("Principal set computation failed: %v", err)
    98  		return nil, errors.WithStack(err)
    99  	}
   100  
   101  	return ea.computeEndorsementResponse(&context{
   102  		chaincode:           interest.Chaincodes[0].Name,
   103  		channel:             string(channelID),
   104  		principalsSets:      principalsSets,
   105  		channelMembersById:  channelMembersById,
   106  		aliveMembership:     aliveMembership,
   107  		identitiesOfMembers: identitiesOfMembers,
   108  		chaincodeMapping:    membersAndCC.chaincodeMapping,
   109  	})
   110  }
   111  
   112  func (ea *endorsementAnalyzer) PeersAuthorizedByCriteria(channelID common.ChannelID, interest *peer.ChaincodeInterest) (gossipdiscovery.Members, error) {
   113  	res, err := ea.peersByCriteria(channelID, interest, true)
   114  	return res.members, err
   115  }
   116  
   117  func (ea *endorsementAnalyzer) peersByCriteria(channelID common.ChannelID, interest *peer.ChaincodeInterest, excludePeersWithoutChaincode bool) (membersChaincodeMapping, error) {
   118  	peersOfChannel := ea.PeersOfChannel(channelID)
   119  	if interest == nil || len(interest.Chaincodes) == 0 {
   120  		return membersChaincodeMapping{members: peersOfChannel}, nil
   121  	}
   122  	identities := ea.IdentityInfo()
   123  	identitiesByID := identities.ByID()
   124  	metadataAndCollectionFilters, err := loadMetadataAndFilters(metadataAndFilterContext{
   125  		identityInfoByID: identitiesByID,
   126  		interest:         interest,
   127  		chainID:          channelID,
   128  		evaluator:        ea,
   129  		fetch:            ea,
   130  	})
   131  	if err != nil {
   132  		return membersChaincodeMapping{}, errors.WithStack(err)
   133  	}
   134  	metadata := metadataAndCollectionFilters.md
   135  	// Filter out peers that don't have the chaincode installed on them if required
   136  	peersWithChaincode := peersOfChannel.Filter(peersWithChaincode(metadata...))
   137  	chanMembership := peersOfChannel
   138  	if excludePeersWithoutChaincode {
   139  		chanMembership = peersWithChaincode
   140  	}
   141  
   142  	// Filter out peers that aren't authorized by the collection configs of the chaincode invocation chain
   143  	members := chanMembership.Filter(metadataAndCollectionFilters.isMemberAuthorized)
   144  	return membersChaincodeMapping{
   145  		members:          members,
   146  		chaincodeMapping: peersWithChaincode.ByID(),
   147  	}, nil
   148  }
   149  
   150  type context struct {
   151  	chaincode           string
   152  	channel             string
   153  	aliveMembership     gossipdiscovery.Members
   154  	principalsSets      []policies.PrincipalSet
   155  	channelMembersById  map[string]gossipdiscovery.NetworkMember
   156  	identitiesOfMembers memberIdentities
   157  	chaincodeMapping    map[string]gossipdiscovery.NetworkMember
   158  }
   159  
   160  func (ea *endorsementAnalyzer) computeEndorsementResponse(ctx *context) (*discovery.EndorsementDescriptor, error) {
   161  	// mapPrincipalsToGroups returns a mapping from principals to their corresponding groups.
   162  	// groups are just human readable representations that mask the principals behind them
   163  	principalGroups := mapPrincipalsToGroups(ctx.principalsSets)
   164  	// principalsToPeersGraph computes a bipartite graph (V1 U V2 , E)
   165  	// such that V1 is the peers, V2 are the principals,
   166  	// and each e=(peer,principal) is in E if the peer satisfies the principal
   167  	satGraph := principalsToPeersGraph(principalAndPeerData{
   168  		members: ctx.aliveMembership,
   169  		pGrps:   principalGroups,
   170  	}, ea.satisfiesPrincipal(ctx.channel, ctx.identitiesOfMembers))
   171  
   172  	layouts := computeLayouts(ctx.principalsSets, principalGroups, satGraph)
   173  	if len(layouts) == 0 {
   174  		return nil, errors.New("no peer combination can satisfy the endorsement policy")
   175  	}
   176  
   177  	criteria := &peerMembershipCriteria{
   178  		possibleLayouts:  layouts,
   179  		satGraph:         satGraph,
   180  		chanMemberById:   ctx.channelMembersById,
   181  		idOfMembers:      ctx.identitiesOfMembers,
   182  		chaincodeMapping: ctx.chaincodeMapping,
   183  	}
   184  
   185  	groupToEndorserListMapping := endorsersByGroup(criteria)
   186  	layouts = filterOutUnsatisfiedLayouts(groupToEndorserListMapping, layouts)
   187  
   188  	if len(layouts) == 0 {
   189  		return nil, errors.New("required chaincodes are not installed on sufficient peers")
   190  	}
   191  
   192  	return &discovery.EndorsementDescriptor{
   193  		Chaincode:         ctx.chaincode,
   194  		Layouts:           layouts,
   195  		EndorsersByGroups: groupToEndorserListMapping,
   196  	}, nil
   197  }
   198  
   199  func filterOutUnsatisfiedLayouts(endorsersByGroup map[string]*discovery.Peers, layouts []*discovery.Layout) []*discovery.Layout {
   200  	// Iterate once again over all layouts and ensure every layout has enough peers in the EndorsersByGroups
   201  	// as required by the quantity in the layout.
   202  	filteredLayouts := make([]*discovery.Layout, 0, len(layouts))
   203  	for _, layout := range layouts {
   204  		var layoutInvalid bool
   205  		for group, quantity := range layout.QuantitiesByGroup {
   206  			peerList := endorsersByGroup[group]
   207  			if peerList == nil || len(peerList.Peers) < int(quantity) {
   208  				layoutInvalid = true
   209  			}
   210  		}
   211  		if layoutInvalid {
   212  			continue
   213  		}
   214  		filteredLayouts = append(filteredLayouts, layout)
   215  	}
   216  	return filteredLayouts
   217  }
   218  
   219  func computeStateBasedPrincipalSets(chaincodes []*peer.ChaincodeCall, logger *flogging.FabricLogger) (inquire.ComparablePrincipalSets, error) {
   220  	var stateBasedCPS []inquire.ComparablePrincipalSets
   221  	for _, chaincode := range chaincodes {
   222  		if len(chaincode.KeyPolicies) == 0 {
   223  			continue
   224  		}
   225  
   226  		logger.Debugf("Chaincode call to %s is satisfied by %d state based policies of %v",
   227  			chaincode.Name, len(chaincode.KeyPolicies), chaincode.KeyPolicies)
   228  
   229  		for _, stateBasedPolicy := range chaincode.KeyPolicies {
   230  			var cmpsets inquire.ComparablePrincipalSets
   231  			stateBasedPolicy := inquire.NewInquireableSignaturePolicy(stateBasedPolicy)
   232  			for _, ps := range stateBasedPolicy.SatisfiedBy() {
   233  				cps := inquire.NewComparablePrincipalSet(ps)
   234  				if cps == nil {
   235  					return nil, errors.New("failed creating a comparable principal set for state based endorsement")
   236  				}
   237  				cmpsets = append(cmpsets, cps)
   238  			}
   239  			if len(cmpsets) == 0 {
   240  				return nil, errors.New("state based endorsement policy cannot be satisfied")
   241  			}
   242  			stateBasedCPS = append(stateBasedCPS, cmpsets)
   243  		}
   244  	}
   245  
   246  	if len(stateBasedCPS) > 0 {
   247  		stateBasedPrincipalSet, err := mergePrincipalSets(stateBasedCPS)
   248  		if err != nil {
   249  			return nil, errors.WithStack(err)
   250  		}
   251  
   252  		logger.Debugf("Merging state based policies: %v --> %v", stateBasedCPS, stateBasedPrincipalSet)
   253  
   254  		return stateBasedPrincipalSet, nil
   255  	}
   256  
   257  	logger.Debugf("No state based policies requested")
   258  
   259  	return nil, nil
   260  }
   261  
   262  func (ea *endorsementAnalyzer) computePrincipalSets(channelID common.ChannelID, interest *peer.ChaincodeInterest) (policies.PrincipalSets, error) {
   263  	sessionLogger := logger.With("channel", string(channelID))
   264  	var inquireablePoliciesForChaincodeAndCollections []policies.InquireablePolicy
   265  	for _, chaincode := range interest.Chaincodes {
   266  		policies := ea.PoliciesByChaincode(string(channelID), chaincode.Name, chaincode.CollectionNames...)
   267  		if len(policies) == 0 {
   268  			sessionLogger.Debug("Policy for chaincode '", chaincode, "'doesn't exist")
   269  			return nil, errors.New("policy not found")
   270  		}
   271  		if chaincode.DisregardNamespacePolicy && len(chaincode.KeyPolicies) == 0 && len(policies) == 1 {
   272  			sessionLogger.Warnf("Client requested to disregard chaincode %s's policy, but it did not specify any "+
   273  				"collection policies or key policies. This is probably a bug in the client side code, as the client should"+
   274  				"either not specify DisregardNamespacePolicy, or specify at least one key policy or at least one collection policy", chaincode.Name)
   275  			return nil, errors.Errorf("requested to disregard chaincode %s's policy but key and collection policies are missing, either "+
   276  				"disable DisregardNamespacePolicy or specify at least one key policy or at least one collection policy", chaincode.Name)
   277  		}
   278  		if chaincode.DisregardNamespacePolicy {
   279  			if len(policies) == 1 {
   280  				sessionLogger.Debugf("Client requested to disregard the namespace policy for chaincode %s,"+
   281  					" and no collection policies are present", chaincode.Name)
   282  				continue
   283  			}
   284  			sessionLogger.Debugf("Client requested to disregard the namespace policy for chaincode %s,"+
   285  				" however there exist %d collection policies taken into account", chaincode.Name, len(policies)-1)
   286  			policies = policies[1:]
   287  		}
   288  		inquireablePoliciesForChaincodeAndCollections = append(inquireablePoliciesForChaincodeAndCollections, policies...)
   289  	}
   290  
   291  	var cpss []inquire.ComparablePrincipalSets
   292  
   293  	for _, policy := range inquireablePoliciesForChaincodeAndCollections {
   294  		var cmpsets inquire.ComparablePrincipalSets
   295  		for _, ps := range policy.SatisfiedBy() {
   296  			cps := inquire.NewComparablePrincipalSet(ps)
   297  			if cps == nil {
   298  				return nil, errors.New("failed creating a comparable principal set")
   299  			}
   300  			cmpsets = append(cmpsets, cps)
   301  		}
   302  		if len(cmpsets) == 0 {
   303  			return nil, errors.New("endorsement policy cannot be satisfied")
   304  		}
   305  		cpss = append(cpss, cmpsets)
   306  	}
   307  
   308  	stateBasedCPS, err := computeStateBasedPrincipalSets(interest.Chaincodes, sessionLogger)
   309  	if err != nil {
   310  		return nil, errors.WithStack(err)
   311  	}
   312  
   313  	if len(stateBasedCPS) > 0 {
   314  		cpss = append(cpss, stateBasedCPS)
   315  	}
   316  
   317  	cps, err := mergePrincipalSets(cpss)
   318  	if err != nil {
   319  		return nil, errors.WithStack(err)
   320  	}
   321  
   322  	sessionLogger.Debugf("Merging principal sets: %v --> %v", cpss, cps)
   323  
   324  	return cps.ToPrincipalSets(), nil
   325  }
   326  
   327  type metadataAndFilterContext struct {
   328  	chainID          common.ChannelID
   329  	interest         *peer.ChaincodeInterest
   330  	fetch            chaincodeMetadataFetcher
   331  	identityInfoByID map[string]api.PeerIdentityInfo
   332  	evaluator        principalEvaluator
   333  }
   334  
   335  // metadataAndColFilter holds metadata and member filters
   336  type metadataAndColFilter struct {
   337  	md                 []*chaincode.Metadata
   338  	isMemberAuthorized memberFilter
   339  }
   340  
   341  func loadMetadataAndFilters(ctx metadataAndFilterContext) (*metadataAndColFilter, error) {
   342  	sessionLogger := logger.With("channel", string(ctx.chainID))
   343  	var metadata []*chaincode.Metadata
   344  	var filters []identityFilter
   345  
   346  	for _, chaincode := range ctx.interest.Chaincodes {
   347  		ccMD := ctx.fetch.Metadata(string(ctx.chainID), chaincode.Name, chaincode.CollectionNames...)
   348  		if ccMD == nil {
   349  			return nil, errors.Errorf("No metadata was found for chaincode %s in channel %s", chaincode.Name, string(ctx.chainID))
   350  		}
   351  		metadata = append(metadata, ccMD)
   352  		if len(chaincode.CollectionNames) == 0 {
   353  			sessionLogger.Debugf("No collections for %s, skipping", chaincode.Name)
   354  			continue
   355  		}
   356  		if chaincode.NoPrivateReads {
   357  			sessionLogger.Debugf("No private reads, skipping")
   358  			continue
   359  		}
   360  		principalSetByCollections, err := principalsFromCollectionConfig(ccMD.CollectionsConfig)
   361  		if err != nil {
   362  			sessionLogger.Warningf("Failed initializing collection filter for chaincode %s: %v", chaincode.Name, err)
   363  			return nil, errors.WithStack(err)
   364  		}
   365  		filter, err := principalSetByCollections.toIdentityFilter(string(ctx.chainID), ctx.evaluator, chaincode)
   366  		if err != nil {
   367  			sessionLogger.Warningf("Failed computing collection principal sets for chaincode %s due to %v", chaincode.Name, err)
   368  			return nil, errors.WithStack(err)
   369  		}
   370  		filters = append(filters, filter)
   371  	}
   372  
   373  	return computeFiltersWithMetadata(filters, metadata, ctx.identityInfoByID), nil
   374  }
   375  
   376  func computeFiltersWithMetadata(filters identityFilters, metadata []*chaincode.Metadata, identityInfoByID map[string]api.PeerIdentityInfo) *metadataAndColFilter {
   377  	if len(filters) == 0 {
   378  		return &metadataAndColFilter{
   379  			md:                 metadata,
   380  			isMemberAuthorized: noopMemberFilter,
   381  		}
   382  	}
   383  	filter := filters.combine().toMemberFilter(identityInfoByID)
   384  	return &metadataAndColFilter{
   385  		isMemberAuthorized: filter,
   386  		md:                 metadata,
   387  	}
   388  }
   389  
   390  // identityFilter accepts or rejects peer identities
   391  type identityFilter func(api.PeerIdentityType) bool
   392  
   393  // identityFilters aggregates multiple identityFilters
   394  type identityFilters []identityFilter
   395  
   396  // memberFilter accepts or rejects NetworkMembers
   397  type memberFilter func(member gossipdiscovery.NetworkMember) bool
   398  
   399  // noopMemberFilter accepts every NetworkMember
   400  func noopMemberFilter(_ gossipdiscovery.NetworkMember) bool {
   401  	return true
   402  }
   403  
   404  // combine combines all identityFilters into a single identityFilter which only accepts identities
   405  // which all the original filters accept
   406  func (filters identityFilters) combine() identityFilter {
   407  	return func(identity api.PeerIdentityType) bool {
   408  		for _, f := range filters {
   409  			if !f(identity) {
   410  				return false
   411  			}
   412  		}
   413  		return true
   414  	}
   415  }
   416  
   417  // toMemberFilter converts this identityFilter to a memberFilter based on the given mapping
   418  // from PKI-ID as strings, to PeerIdentityInfo which holds the peer identities
   419  func (idf identityFilter) toMemberFilter(identityInfoByID map[string]api.PeerIdentityInfo) memberFilter {
   420  	return func(member gossipdiscovery.NetworkMember) bool {
   421  		identity, exists := identityInfoByID[string(member.PKIid)]
   422  		if !exists {
   423  			return false
   424  		}
   425  		return idf(identity.Identity)
   426  	}
   427  }
   428  
   429  func (ea *endorsementAnalyzer) satisfiesPrincipal(channel string, identitiesOfMembers memberIdentities) peerPrincipalEvaluator {
   430  	return func(member gossipdiscovery.NetworkMember, principal *msp.MSPPrincipal) bool {
   431  		err := ea.SatisfiesPrincipal(channel, identitiesOfMembers.identityByPKIID(member.PKIid), principal)
   432  		if err == nil {
   433  			// TODO: log the principals in a human readable form
   434  			logger.Debug(member, "satisfies principal", principal)
   435  			return true
   436  		}
   437  		logger.Debug(member, "doesn't satisfy principal", principal, ":", err)
   438  		return false
   439  	}
   440  }
   441  
   442  type peerMembershipCriteria struct {
   443  	satGraph         *principalPeerGraph
   444  	idOfMembers      memberIdentities
   445  	chanMemberById   map[string]gossipdiscovery.NetworkMember
   446  	possibleLayouts  layouts
   447  	chaincodeMapping map[string]gossipdiscovery.NetworkMember
   448  }
   449  
   450  // endorsersByGroup computes a map from groups to peers.
   451  // Each group included, is found in some layout, which means
   452  // that there is some principal combination that includes the corresponding
   453  // group.
   454  // This means that if a group isn't included in the result, there is no
   455  // principal combination (that includes the principal corresponding to the group),
   456  // such that there are enough peers to satisfy the principal combination.
   457  func endorsersByGroup(criteria *peerMembershipCriteria) map[string]*discovery.Peers {
   458  	satGraph := criteria.satGraph
   459  	idOfMembers := criteria.idOfMembers
   460  	chanMemberById := criteria.chanMemberById
   461  	includedGroups := criteria.possibleLayouts.groupsSet()
   462  
   463  	res := make(map[string]*discovery.Peers)
   464  	// Map endorsers to their corresponding groups.
   465  	// Iterate the principals, and put the peers into each group that corresponds with a principal vertex
   466  	for grp, principalVertex := range satGraph.principalVertices {
   467  		if _, exists := includedGroups[grp]; !exists {
   468  			// If the current group is not found in any layout, skip the corresponding principal
   469  			continue
   470  		}
   471  		peerList := &discovery.Peers{}
   472  		for _, peerVertex := range principalVertex.Neighbors() {
   473  			member := peerVertex.Data.(gossipdiscovery.NetworkMember)
   474  			// Check if this peer has the chaincode installed
   475  			stateInfo := chanMemberById[string(member.PKIid)]
   476  			_, hasChaincodeInstalled := criteria.chaincodeMapping[string(stateInfo.PKIid)]
   477  			if !hasChaincodeInstalled {
   478  				continue
   479  			}
   480  			peerList.Peers = append(peerList.Peers, &discovery.Peer{
   481  				Identity:       idOfMembers.identityByPKIID(member.PKIid),
   482  				StateInfo:      stateInfo.Envelope,
   483  				MembershipInfo: member.Envelope,
   484  			})
   485  		}
   486  
   487  		if len(peerList.Peers) > 0 {
   488  			res[grp] = peerList
   489  		}
   490  	}
   491  	return res
   492  }
   493  
   494  // computeLayouts computes all possible principal combinations
   495  // that can be used to satisfy the endorsement policy, given a graph
   496  // of available peers that maps each peer to a principal it satisfies.
   497  // Each such a combination is called a layout, because it maps
   498  // a group (alias for a principal) to a threshold of peers that need to endorse,
   499  // and that satisfy the corresponding principal
   500  func computeLayouts(principalsSets []policies.PrincipalSet, principalGroups principalGroupMapper, satGraph *principalPeerGraph) []*discovery.Layout {
   501  	var layouts []*discovery.Layout
   502  	// principalsSets is a collection of combinations of principals,
   503  	// such that each combination (given enough peers) satisfies the endorsement policy.
   504  	for _, principalSet := range principalsSets {
   505  		layout := &discovery.Layout{
   506  			QuantitiesByGroup: make(map[string]uint32),
   507  		}
   508  		// Since principalsSet has repetitions, we first
   509  		// compute a mapping from the principal to repetitions in the set.
   510  		for principal, plurality := range principalSet.UniqueSet() {
   511  			key := principalKey{
   512  				cls:       int32(principal.PrincipalClassification),
   513  				principal: string(principal.Principal),
   514  			}
   515  			// We map the principal to a group, which is an alias for the principal.
   516  			layout.QuantitiesByGroup[principalGroups.group(key)] = uint32(plurality)
   517  		}
   518  		// Check that the layout can be satisfied with the current known peers
   519  		// This is done by iterating the current layout, and ensuring that
   520  		// each principal vertex is connected to at least <plurality> peer vertices.
   521  		if isLayoutSatisfied(layout.QuantitiesByGroup, satGraph) {
   522  			// If so, then add the layout to the layouts, since we have enough peers to satisfy the
   523  			// principal combination
   524  			layouts = append(layouts, layout)
   525  		}
   526  	}
   527  	return layouts
   528  }
   529  
   530  func isLayoutSatisfied(layout map[string]uint32, satGraph *principalPeerGraph) bool {
   531  	for grp, plurality := range layout {
   532  		// Do we have more than <plurality> peers connected to the principal?
   533  		if len(satGraph.principalVertices[grp].Neighbors()) < int(plurality) {
   534  			return false
   535  		}
   536  	}
   537  	return true
   538  }
   539  
   540  type principalPeerGraph struct {
   541  	peerVertices      []*graph.Vertex
   542  	principalVertices map[string]*graph.Vertex
   543  }
   544  
   545  type principalAndPeerData struct {
   546  	members gossipdiscovery.Members
   547  	pGrps   principalGroupMapper
   548  }
   549  
   550  func principalsToPeersGraph(data principalAndPeerData, satisfiesPrincipal peerPrincipalEvaluator) *principalPeerGraph {
   551  	// Create the peer vertices
   552  	peerVertices := make([]*graph.Vertex, len(data.members))
   553  	for i, member := range data.members {
   554  		peerVertices[i] = graph.NewVertex(string(member.PKIid), member)
   555  	}
   556  
   557  	// Create the principal vertices
   558  	principalVertices := make(map[string]*graph.Vertex)
   559  	for pKey, grp := range data.pGrps {
   560  		principalVertices[grp] = graph.NewVertex(grp, pKey.toPrincipal())
   561  	}
   562  
   563  	// Connect principals and peers
   564  	for _, principalVertex := range principalVertices {
   565  		for _, peerVertex := range peerVertices {
   566  			// If the current peer satisfies the principal, connect their corresponding vertices with an edge
   567  			principal := principalVertex.Data.(*msp.MSPPrincipal)
   568  			member := peerVertex.Data.(gossipdiscovery.NetworkMember)
   569  			if satisfiesPrincipal(member, principal) {
   570  				peerVertex.AddNeighbor(principalVertex)
   571  			}
   572  		}
   573  	}
   574  	return &principalPeerGraph{
   575  		peerVertices:      peerVertices,
   576  		principalVertices: principalVertices,
   577  	}
   578  }
   579  
   580  func mapPrincipalsToGroups(principalsSets []policies.PrincipalSet) principalGroupMapper {
   581  	groupMapper := make(principalGroupMapper)
   582  	totalPrincipals := make(map[principalKey]struct{})
   583  	for _, principalSet := range principalsSets {
   584  		for _, principal := range principalSet {
   585  			totalPrincipals[principalKey{
   586  				principal: string(principal.Principal),
   587  				cls:       int32(principal.PrincipalClassification),
   588  			}] = struct{}{}
   589  		}
   590  	}
   591  	for principal := range totalPrincipals {
   592  		groupMapper.group(principal)
   593  	}
   594  	return groupMapper
   595  }
   596  
   597  type memberIdentities map[string]api.PeerIdentityType
   598  
   599  func (m memberIdentities) identityByPKIID(id common.PKIidType) api.PeerIdentityType {
   600  	return m[string(id)]
   601  }
   602  
   603  func computeIdentitiesOfMembers(identitySet api.PeerIdentitySet, members map[string]gossipdiscovery.NetworkMember) memberIdentities {
   604  	identitiesByPKIID := make(map[string]api.PeerIdentityType)
   605  	identitiesOfMembers := make(map[string]api.PeerIdentityType, len(members))
   606  	for _, identity := range identitySet {
   607  		identitiesByPKIID[string(identity.PKIId)] = identity.Identity
   608  	}
   609  	for _, member := range members {
   610  		if identity, exists := identitiesByPKIID[string(member.PKIid)]; exists {
   611  			identitiesOfMembers[string(member.PKIid)] = identity
   612  		}
   613  	}
   614  	return identitiesOfMembers
   615  }
   616  
   617  // principalGroupMapper maps principals to names of groups
   618  type principalGroupMapper map[principalKey]string
   619  
   620  func (mapper principalGroupMapper) group(principal principalKey) string {
   621  	if grp, exists := mapper[principal]; exists {
   622  		return grp
   623  	}
   624  	grp := fmt.Sprintf("G%d", len(mapper))
   625  	mapper[principal] = grp
   626  	return grp
   627  }
   628  
   629  type principalKey struct {
   630  	cls       int32
   631  	principal string
   632  }
   633  
   634  func (pk principalKey) toPrincipal() *msp.MSPPrincipal {
   635  	return &msp.MSPPrincipal{
   636  		PrincipalClassification: msp.MSPPrincipal_Classification(pk.cls),
   637  		Principal:               []byte(pk.principal),
   638  	}
   639  }
   640  
   641  // layouts is an aggregation of several layouts
   642  type layouts []*discovery.Layout
   643  
   644  // groupsSet returns a set of groups that the layouts contain
   645  func (l layouts) groupsSet() map[string]struct{} {
   646  	m := make(map[string]struct{})
   647  	for _, layout := range l {
   648  		for grp := range layout.QuantitiesByGroup {
   649  			m[grp] = struct{}{}
   650  		}
   651  	}
   652  	return m
   653  }
   654  
   655  func peersWithChaincode(metadata ...*chaincode.Metadata) func(member gossipdiscovery.NetworkMember) bool {
   656  	return func(member gossipdiscovery.NetworkMember) bool {
   657  		if member.Properties == nil {
   658  			return false
   659  		}
   660  		for _, ccMD := range metadata {
   661  			var found bool
   662  			for _, cc := range member.Properties.Chaincodes {
   663  				if cc.Name == ccMD.Name && cc.Version == ccMD.Version {
   664  					found = true
   665  				}
   666  			}
   667  			if !found {
   668  				return false
   669  			}
   670  		}
   671  		return true
   672  	}
   673  }
   674  
   675  func mergePrincipalSets(cpss []inquire.ComparablePrincipalSets) (inquire.ComparablePrincipalSets, error) {
   676  	// Obtain the first ComparablePrincipalSet first
   677  	var cps inquire.ComparablePrincipalSets
   678  	cps, cpss, err := popComparablePrincipalSets(cpss)
   679  	if err != nil {
   680  		return nil, errors.WithStack(err)
   681  	}
   682  
   683  	for _, cps2 := range cpss {
   684  		cps = inquire.Merge(cps, cps2)
   685  	}
   686  	return cps, nil
   687  }
   688  
   689  func popComparablePrincipalSets(sets []inquire.ComparablePrincipalSets) (inquire.ComparablePrincipalSets, []inquire.ComparablePrincipalSets, error) {
   690  	if len(sets) == 0 {
   691  		return nil, nil, errors.New("no principal sets remained after filtering")
   692  	}
   693  	cps, cpss := sets[0], sets[1:]
   694  	return cps, cpss, nil
   695  }