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 }