github.com/kaituanwang/hyperledger@v2.0.1+incompatible/discovery/endorsement/endorsement.go (about)

     1  /*
     2  Copyright IBM Corp. 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/discovery"
    13  	"github.com/hyperledger/fabric-protos-go/msp"
    14  	"github.com/hyperledger/fabric/common/chaincode"
    15  	"github.com/hyperledger/fabric/common/flogging"
    16  	"github.com/hyperledger/fabric/common/graph"
    17  	"github.com/hyperledger/fabric/common/policies"
    18  	"github.com/hyperledger/fabric/common/policies/inquire"
    19  	"github.com/hyperledger/fabric/gossip/api"
    20  	"github.com/hyperledger/fabric/gossip/common"
    21  	. "github.com/hyperledger/fabric/gossip/discovery"
    22  	"github.com/pkg/errors"
    23  )
    24  
    25  var (
    26  	logger = flogging.MustGetLogger("discovery.endorsement")
    27  )
    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, loadCollections bool) *chaincode.Metadata
    39  }
    40  
    41  type policyFetcher interface {
    42  	// PolicyByChaincode returns a policy that can be inquired which identities
    43  	// satisfy it
    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) Members
    54  
    55  	// Peers returns the NetworkMembers considered alive
    56  	Peers() Members
    57  }
    58  
    59  type endorsementAnalyzer struct {
    60  	gossipSupport
    61  	principalEvaluator
    62  	policyFetcher
    63  	chaincodeMetadataFetcher
    64  }
    65  
    66  // NewEndorsementAnalyzer constructs an NewEndorsementAnalyzer out of the given support
    67  func NewEndorsementAnalyzer(gs gossipSupport, pf policyFetcher, pe principalEvaluator, mf chaincodeMetadataFetcher) *endorsementAnalyzer {
    68  	return &endorsementAnalyzer{
    69  		gossipSupport:            gs,
    70  		policyFetcher:            pf,
    71  		principalEvaluator:       pe,
    72  		chaincodeMetadataFetcher: mf,
    73  	}
    74  }
    75  
    76  type peerPrincipalEvaluator func(member NetworkMember, principal *msp.MSPPrincipal) bool
    77  
    78  // PeersForEndorsement returns an EndorsementDescriptor for a given set of peers, channel, and chaincode
    79  func (ea *endorsementAnalyzer) PeersForEndorsement(channelID common.ChannelID, interest *discovery.ChaincodeInterest) (*discovery.EndorsementDescriptor, error) {
    80  	chanMembership, err := ea.PeersAuthorizedByCriteria(channelID, interest)
    81  	if err != nil {
    82  		return nil, errors.WithStack(err)
    83  	}
    84  	channelMembersById := chanMembership.ByID()
    85  	// Choose only the alive messages of those that have joined the channel
    86  	aliveMembership := ea.Peers().Intersect(chanMembership)
    87  	membersById := aliveMembership.ByID()
    88  	// Compute a mapping between the PKI-IDs of members to their identities
    89  	identitiesOfMembers := computeIdentitiesOfMembers(ea.IdentityInfo(), membersById)
    90  	principalsSets, err := ea.computePrincipalSets(channelID, interest)
    91  	if err != nil {
    92  		logger.Warningf("Principal set computation failed: %v", err)
    93  		return nil, errors.WithStack(err)
    94  	}
    95  
    96  	return ea.computeEndorsementResponse(&context{
    97  		chaincode:           interest.Chaincodes[0].Name,
    98  		channel:             string(channelID),
    99  		principalsSets:      principalsSets,
   100  		channelMembersById:  channelMembersById,
   101  		aliveMembership:     aliveMembership,
   102  		identitiesOfMembers: identitiesOfMembers,
   103  	})
   104  }
   105  
   106  func (ea *endorsementAnalyzer) PeersAuthorizedByCriteria(channelID common.ChannelID, interest *discovery.ChaincodeInterest) (Members, error) {
   107  	peersOfChannel := ea.PeersOfChannel(channelID)
   108  	if interest == nil || len(interest.Chaincodes) == 0 {
   109  		return peersOfChannel, nil
   110  	}
   111  	identities := ea.IdentityInfo()
   112  	identitiesByID := identities.ByID()
   113  	metadataAndCollectionFilters, err := loadMetadataAndFilters(metadataAndFilterContext{
   114  		identityInfoByID: identitiesByID,
   115  		interest:         interest,
   116  		chainID:          channelID,
   117  		evaluator:        ea,
   118  		fetch:            ea,
   119  	})
   120  	if err != nil {
   121  		return nil, errors.WithStack(err)
   122  	}
   123  	metadata := metadataAndCollectionFilters.md
   124  	// Filter out peers that don't have the chaincode installed on them
   125  	chanMembership := peersOfChannel.Filter(peersWithChaincode(metadata...))
   126  	// Filter out peers that aren't authorized by the collection configs of the chaincode invocation chain
   127  	return chanMembership.Filter(metadataAndCollectionFilters.isMemberAuthorized), nil
   128  }
   129  
   130  type context struct {
   131  	chaincode           string
   132  	channel             string
   133  	aliveMembership     Members
   134  	principalsSets      []policies.PrincipalSet
   135  	channelMembersById  map[string]NetworkMember
   136  	identitiesOfMembers memberIdentities
   137  }
   138  
   139  func (ea *endorsementAnalyzer) computeEndorsementResponse(ctx *context) (*discovery.EndorsementDescriptor, error) {
   140  	// mapPrincipalsToGroups returns a mapping from principals to their corresponding groups.
   141  	// groups are just human readable representations that mask the principals behind them
   142  	principalGroups := mapPrincipalsToGroups(ctx.principalsSets)
   143  	// principalsToPeersGraph computes a bipartite graph (V1 U V2 , E)
   144  	// such that V1 is the peers, V2 are the principals,
   145  	// and each e=(peer,principal) is in E if the peer satisfies the principal
   146  	satGraph := principalsToPeersGraph(principalAndPeerData{
   147  		members: ctx.aliveMembership,
   148  		pGrps:   principalGroups,
   149  	}, ea.satisfiesPrincipal(ctx.channel, ctx.identitiesOfMembers))
   150  
   151  	layouts := computeLayouts(ctx.principalsSets, principalGroups, satGraph)
   152  	if len(layouts) == 0 {
   153  		return nil, errors.New("cannot satisfy any principal combination")
   154  	}
   155  
   156  	criteria := &peerMembershipCriteria{
   157  		possibleLayouts: layouts,
   158  		satGraph:        satGraph,
   159  		chanMemberById:  ctx.channelMembersById,
   160  		idOfMembers:     ctx.identitiesOfMembers,
   161  	}
   162  
   163  	return &discovery.EndorsementDescriptor{
   164  		Chaincode:         ctx.chaincode,
   165  		Layouts:           layouts,
   166  		EndorsersByGroups: endorsersByGroup(criteria),
   167  	}, nil
   168  }
   169  
   170  func (ea *endorsementAnalyzer) computePrincipalSets(channelID common.ChannelID, interest *discovery.ChaincodeInterest) (policies.PrincipalSets, error) {
   171  	var inquireablePolicies []policies.InquireablePolicy
   172  	for _, chaincode := range interest.Chaincodes {
   173  		policies := ea.PoliciesByChaincode(string(channelID), chaincode.Name, chaincode.CollectionNames...)
   174  		if len(policies) == 0 {
   175  			logger.Debug("Policy for chaincode '", chaincode, "'doesn't exist")
   176  			return nil, errors.New("policy not found")
   177  		}
   178  
   179  		for _, pol := range policies {
   180  			inquireablePolicies = append(inquireablePolicies, pol)
   181  		}
   182  	}
   183  
   184  	var cpss []inquire.ComparablePrincipalSets
   185  
   186  	for _, policy := range inquireablePolicies {
   187  		var cmpsets inquire.ComparablePrincipalSets
   188  		for _, ps := range policy.SatisfiedBy() {
   189  			cps := inquire.NewComparablePrincipalSet(ps)
   190  			if cps == nil {
   191  				return nil, errors.New("failed creating a comparable principal set")
   192  			}
   193  			cmpsets = append(cmpsets, cps)
   194  		}
   195  		if len(cmpsets) == 0 {
   196  			return nil, errors.New("chaincode isn't installed on sufficient organizations required by the endorsement policy")
   197  		}
   198  		cpss = append(cpss, cmpsets)
   199  	}
   200  
   201  	cps, err := mergePrincipalSets(cpss)
   202  	if err != nil {
   203  		return nil, errors.WithStack(err)
   204  	}
   205  
   206  	return cps.ToPrincipalSets(), nil
   207  }
   208  
   209  type metadataAndFilterContext struct {
   210  	chainID          common.ChannelID
   211  	interest         *discovery.ChaincodeInterest
   212  	fetch            chaincodeMetadataFetcher
   213  	identityInfoByID map[string]api.PeerIdentityInfo
   214  	evaluator        principalEvaluator
   215  }
   216  
   217  // metadataAndColFilter holds metadata and member filters
   218  type metadataAndColFilter struct {
   219  	md                 []*chaincode.Metadata
   220  	isMemberAuthorized memberFilter
   221  }
   222  
   223  func loadMetadataAndFilters(ctx metadataAndFilterContext) (*metadataAndColFilter, error) {
   224  	var metadata []*chaincode.Metadata
   225  	var filters []identityFilter
   226  
   227  	for _, chaincode := range ctx.interest.Chaincodes {
   228  		ccMD := ctx.fetch.Metadata(string(ctx.chainID), chaincode.Name, len(chaincode.CollectionNames) > 0)
   229  		if ccMD == nil {
   230  			return nil, errors.Errorf("No metadata was found for chaincode %s in channel %s", chaincode.Name, string(ctx.chainID))
   231  		}
   232  		metadata = append(metadata, ccMD)
   233  		if len(chaincode.CollectionNames) == 0 {
   234  			continue
   235  		}
   236  		principalSetByCollections, err := principalsFromCollectionConfig(ccMD.CollectionsConfig)
   237  		if err != nil {
   238  			logger.Warningf("Failed initializing collection filter for chaincode %s: %v", chaincode.Name, err)
   239  			return nil, errors.WithStack(err)
   240  		}
   241  		filter, err := principalSetByCollections.toIdentityFilter(string(ctx.chainID), ctx.evaluator, chaincode)
   242  		if err != nil {
   243  			logger.Warningf("Failed computing collection principal sets for chaincode %s due to %v", chaincode.Name, err)
   244  			return nil, errors.WithStack(err)
   245  		}
   246  		filters = append(filters, filter)
   247  	}
   248  
   249  	return computeFiltersWithMetadata(filters, metadata, ctx.identityInfoByID), nil
   250  }
   251  
   252  func computeFiltersWithMetadata(filters identityFilters, metadata []*chaincode.Metadata, identityInfoByID map[string]api.PeerIdentityInfo) *metadataAndColFilter {
   253  	if len(filters) == 0 {
   254  		return &metadataAndColFilter{
   255  			md:                 metadata,
   256  			isMemberAuthorized: noopMemberFilter,
   257  		}
   258  	}
   259  	filter := filters.combine().toMemberFilter(identityInfoByID)
   260  	return &metadataAndColFilter{
   261  		isMemberAuthorized: filter,
   262  		md:                 metadata,
   263  	}
   264  }
   265  
   266  // identityFilter accepts or rejects peer identities
   267  type identityFilter func(api.PeerIdentityType) bool
   268  
   269  // identityFilters aggregates multiple identityFilters
   270  type identityFilters []identityFilter
   271  
   272  // memberFilter accepts or rejects NetworkMembers
   273  type memberFilter func(member NetworkMember) bool
   274  
   275  // noopMemberFilter accepts every NetworkMember
   276  func noopMemberFilter(_ NetworkMember) bool {
   277  	return true
   278  }
   279  
   280  // combine combines all identityFilters into a single identityFilter which only accepts identities
   281  // which all the original filters accept
   282  func (filters identityFilters) combine() identityFilter {
   283  	return func(identity api.PeerIdentityType) bool {
   284  		for _, f := range filters {
   285  			if !f(identity) {
   286  				return false
   287  			}
   288  		}
   289  		return true
   290  	}
   291  }
   292  
   293  // toMemberFilter converts this identityFilter to a memberFilter based on the given mapping
   294  // from PKI-ID as strings, to PeerIdentityInfo which holds the peer identities
   295  func (idf identityFilter) toMemberFilter(identityInfoByID map[string]api.PeerIdentityInfo) memberFilter {
   296  	return func(member NetworkMember) bool {
   297  		identity, exists := identityInfoByID[string(member.PKIid)]
   298  		if !exists {
   299  			return false
   300  		}
   301  		return idf(identity.Identity)
   302  	}
   303  }
   304  
   305  func (ea *endorsementAnalyzer) satisfiesPrincipal(channel string, identitiesOfMembers memberIdentities) peerPrincipalEvaluator {
   306  	return func(member NetworkMember, principal *msp.MSPPrincipal) bool {
   307  		err := ea.SatisfiesPrincipal(channel, identitiesOfMembers.identityByPKIID(member.PKIid), principal)
   308  		if err == nil {
   309  			// TODO: log the principals in a human readable form
   310  			logger.Debug(member, "satisfies principal", principal)
   311  			return true
   312  		}
   313  		logger.Debug(member, "doesn't satisfy principal", principal, ":", err)
   314  		return false
   315  	}
   316  }
   317  
   318  type peerMembershipCriteria struct {
   319  	satGraph        *principalPeerGraph
   320  	idOfMembers     memberIdentities
   321  	chanMemberById  map[string]NetworkMember
   322  	possibleLayouts layouts
   323  }
   324  
   325  // endorsersByGroup computes a map from groups to peers.
   326  // Each group included, is found in some layout, which means
   327  // that there is some principal combination that includes the corresponding
   328  // group.
   329  // This means that if a group isn't included in the result, there is no
   330  // principal combination (that includes the principal corresponding to the group),
   331  // such that there are enough peers to satisfy the principal combination.
   332  func endorsersByGroup(criteria *peerMembershipCriteria) map[string]*discovery.Peers {
   333  	satGraph := criteria.satGraph
   334  	idOfMembers := criteria.idOfMembers
   335  	chanMemberById := criteria.chanMemberById
   336  	includedGroups := criteria.possibleLayouts.groupsSet()
   337  
   338  	res := make(map[string]*discovery.Peers)
   339  	// Map endorsers to their corresponding groups.
   340  	// Iterate the principals, and put the peers into each group that corresponds with a principal vertex
   341  	for grp, principalVertex := range satGraph.principalVertices {
   342  		if _, exists := includedGroups[grp]; !exists {
   343  			// If the current group is not found in any layout, skip the corresponding principal
   344  			continue
   345  		}
   346  		peerList := &discovery.Peers{}
   347  		res[grp] = peerList
   348  		for _, peerVertex := range principalVertex.Neighbors() {
   349  			member := peerVertex.Data.(NetworkMember)
   350  			peerList.Peers = append(peerList.Peers, &discovery.Peer{
   351  				Identity:       idOfMembers.identityByPKIID(member.PKIid),
   352  				StateInfo:      chanMemberById[string(member.PKIid)].Envelope,
   353  				MembershipInfo: member.Envelope,
   354  			})
   355  		}
   356  	}
   357  	return res
   358  }
   359  
   360  // computeLayouts computes all possible principal combinations
   361  // that can be used to satisfy the endorsement policy, given a graph
   362  // of available peers that maps each peer to a principal it satisfies.
   363  // Each such a combination is called a layout, because it maps
   364  // a group (alias for a principal) to a threshold of peers that need to endorse,
   365  // and that satisfy the corresponding principal.
   366  func computeLayouts(principalsSets []policies.PrincipalSet, principalGroups principalGroupMapper, satGraph *principalPeerGraph) []*discovery.Layout {
   367  	var layouts []*discovery.Layout
   368  	// principalsSets is a collection of combinations of principals,
   369  	// such that each combination (given enough peers) satisfies the endorsement policy.
   370  	for _, principalSet := range principalsSets {
   371  		layout := &discovery.Layout{
   372  			QuantitiesByGroup: make(map[string]uint32),
   373  		}
   374  		// Since principalsSet has repetitions, we first
   375  		// compute a mapping from the principal to repetitions in the set.
   376  		for principal, plurality := range principalSet.UniqueSet() {
   377  			key := principalKey{
   378  				cls:       int32(principal.PrincipalClassification),
   379  				principal: string(principal.Principal),
   380  			}
   381  			// We map the principal to a group, which is an alias for the principal.
   382  			layout.QuantitiesByGroup[principalGroups.group(key)] = uint32(plurality)
   383  		}
   384  		// Check that the layout can be satisfied with the current known peers
   385  		// This is done by iterating the current layout, and ensuring that
   386  		// each principal vertex is connected to at least <plurality> peer vertices.
   387  		if isLayoutSatisfied(layout.QuantitiesByGroup, satGraph) {
   388  			// If so, then add the layout to the layouts, since we have enough peers to satisfy the
   389  			// principal combination
   390  			layouts = append(layouts, layout)
   391  		}
   392  	}
   393  	return layouts
   394  }
   395  
   396  func isLayoutSatisfied(layout map[string]uint32, satGraph *principalPeerGraph) bool {
   397  	for grp, plurality := range layout {
   398  		// Do we have more than <plurality> peers connected to the principal?
   399  		if len(satGraph.principalVertices[grp].Neighbors()) < int(plurality) {
   400  			return false
   401  		}
   402  	}
   403  	return true
   404  }
   405  
   406  type principalPeerGraph struct {
   407  	peerVertices      []*graph.Vertex
   408  	principalVertices map[string]*graph.Vertex
   409  }
   410  
   411  type principalAndPeerData struct {
   412  	members Members
   413  	pGrps   principalGroupMapper
   414  }
   415  
   416  func principalsToPeersGraph(data principalAndPeerData, satisfiesPrincipal peerPrincipalEvaluator) *principalPeerGraph {
   417  	// Create the peer vertices
   418  	peerVertices := make([]*graph.Vertex, len(data.members))
   419  	for i, member := range data.members {
   420  		peerVertices[i] = graph.NewVertex(string(member.PKIid), member)
   421  	}
   422  
   423  	// Create the principal vertices
   424  	principalVertices := make(map[string]*graph.Vertex)
   425  	for pKey, grp := range data.pGrps {
   426  		principalVertices[grp] = graph.NewVertex(grp, pKey.toPrincipal())
   427  	}
   428  
   429  	// Connect principals and peers
   430  	for _, principalVertex := range principalVertices {
   431  		for _, peerVertex := range peerVertices {
   432  			// If the current peer satisfies the principal, connect their corresponding vertices with an edge
   433  			principal := principalVertex.Data.(*msp.MSPPrincipal)
   434  			member := peerVertex.Data.(NetworkMember)
   435  			if satisfiesPrincipal(member, principal) {
   436  				peerVertex.AddNeighbor(principalVertex)
   437  			}
   438  		}
   439  	}
   440  	return &principalPeerGraph{
   441  		peerVertices:      peerVertices,
   442  		principalVertices: principalVertices,
   443  	}
   444  }
   445  
   446  func mapPrincipalsToGroups(principalsSets []policies.PrincipalSet) principalGroupMapper {
   447  	groupMapper := make(principalGroupMapper)
   448  	totalPrincipals := make(map[principalKey]struct{})
   449  	for _, principalSet := range principalsSets {
   450  		for _, principal := range principalSet {
   451  			totalPrincipals[principalKey{
   452  				principal: string(principal.Principal),
   453  				cls:       int32(principal.PrincipalClassification),
   454  			}] = struct{}{}
   455  		}
   456  	}
   457  	for principal := range totalPrincipals {
   458  		groupMapper.group(principal)
   459  	}
   460  	return groupMapper
   461  }
   462  
   463  type memberIdentities map[string]api.PeerIdentityType
   464  
   465  func (m memberIdentities) identityByPKIID(id common.PKIidType) api.PeerIdentityType {
   466  	return m[string(id)]
   467  }
   468  
   469  func computeIdentitiesOfMembers(identitySet api.PeerIdentitySet, members map[string]NetworkMember) memberIdentities {
   470  	identitiesByPKIID := make(map[string]api.PeerIdentityType)
   471  	identitiesOfMembers := make(map[string]api.PeerIdentityType, len(members))
   472  	for _, identity := range identitySet {
   473  		identitiesByPKIID[string(identity.PKIId)] = identity.Identity
   474  	}
   475  	for _, member := range members {
   476  		if identity, exists := identitiesByPKIID[string(member.PKIid)]; exists {
   477  			identitiesOfMembers[string(member.PKIid)] = identity
   478  		}
   479  	}
   480  	return identitiesOfMembers
   481  }
   482  
   483  // principalGroupMapper maps principals to names of groups
   484  type principalGroupMapper map[principalKey]string
   485  
   486  func (mapper principalGroupMapper) group(principal principalKey) string {
   487  	if grp, exists := mapper[principal]; exists {
   488  		return grp
   489  	}
   490  	grp := fmt.Sprintf("G%d", len(mapper))
   491  	mapper[principal] = grp
   492  	return grp
   493  }
   494  
   495  type principalKey struct {
   496  	cls       int32
   497  	principal string
   498  }
   499  
   500  func (pk principalKey) toPrincipal() *msp.MSPPrincipal {
   501  	return &msp.MSPPrincipal{
   502  		PrincipalClassification: msp.MSPPrincipal_Classification(pk.cls),
   503  		Principal:               []byte(pk.principal),
   504  	}
   505  }
   506  
   507  // layouts is an aggregation of several layouts
   508  type layouts []*discovery.Layout
   509  
   510  // groupsSet returns a set of groups that the layouts contain
   511  func (l layouts) groupsSet() map[string]struct{} {
   512  	m := make(map[string]struct{})
   513  	for _, layout := range l {
   514  		for grp := range layout.QuantitiesByGroup {
   515  			m[grp] = struct{}{}
   516  		}
   517  	}
   518  	return m
   519  }
   520  
   521  func peersWithChaincode(metadata ...*chaincode.Metadata) func(member NetworkMember) bool {
   522  	return func(member NetworkMember) bool {
   523  		if member.Properties == nil {
   524  			return false
   525  		}
   526  		for _, ccMD := range metadata {
   527  			var found bool
   528  			for _, cc := range member.Properties.Chaincodes {
   529  				if cc.Name == ccMD.Name && cc.Version == ccMD.Version {
   530  					found = true
   531  				}
   532  			}
   533  			if !found {
   534  				return false
   535  			}
   536  		}
   537  		return true
   538  	}
   539  }
   540  
   541  func mergePrincipalSets(cpss []inquire.ComparablePrincipalSets) (inquire.ComparablePrincipalSets, error) {
   542  	// Obtain the first ComparablePrincipalSet first
   543  	var cps inquire.ComparablePrincipalSets
   544  	cps, cpss, err := popComparablePrincipalSets(cpss)
   545  	if err != nil {
   546  		return nil, errors.WithStack(err)
   547  	}
   548  
   549  	for _, cps2 := range cpss {
   550  		cps = inquire.Merge(cps, cps2)
   551  	}
   552  	return cps, nil
   553  }
   554  
   555  func popComparablePrincipalSets(sets []inquire.ComparablePrincipalSets) (inquire.ComparablePrincipalSets, []inquire.ComparablePrincipalSets, error) {
   556  	if len(sets) == 0 {
   557  		return nil, nil, errors.New("no principal sets remained after filtering")
   558  	}
   559  	cps, cpss := sets[0], sets[1:]
   560  	return cps, cpss, nil
   561  }