github.com/yous1230/fabric@v2.0.0-beta.0.20191224111736-74345bee6ac2+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  	PolicyByChaincode(channel string, cc 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  		pol := ea.PolicyByChaincode(string(channelID), chaincode.Name)
   174  		if pol == nil {
   175  			logger.Debug("Policy for chaincode '", chaincode, "'doesn't exist")
   176  			return nil, errors.New("policy not found")
   177  		}
   178  		inquireablePolicies = append(inquireablePolicies, pol)
   179  	}
   180  
   181  	var cpss []inquire.ComparablePrincipalSets
   182  
   183  	for _, policy := range inquireablePolicies {
   184  		var cmpsets inquire.ComparablePrincipalSets
   185  		for _, ps := range policy.SatisfiedBy() {
   186  			cps := inquire.NewComparablePrincipalSet(ps)
   187  			if cps == nil {
   188  				return nil, errors.New("failed creating a comparable principal set")
   189  			}
   190  			cmpsets = append(cmpsets, cps)
   191  		}
   192  		if len(cmpsets) == 0 {
   193  			return nil, errors.New("chaincode isn't installed on sufficient organizations required by the endorsement policy")
   194  		}
   195  		cpss = append(cpss, cmpsets)
   196  	}
   197  
   198  	cps, err := mergePrincipalSets(cpss)
   199  	if err != nil {
   200  		return nil, errors.WithStack(err)
   201  	}
   202  
   203  	return cps.ToPrincipalSets(), nil
   204  }
   205  
   206  type metadataAndFilterContext struct {
   207  	chainID          common.ChannelID
   208  	interest         *discovery.ChaincodeInterest
   209  	fetch            chaincodeMetadataFetcher
   210  	identityInfoByID map[string]api.PeerIdentityInfo
   211  	evaluator        principalEvaluator
   212  }
   213  
   214  // metadataAndColFilter holds metadata and member filters
   215  type metadataAndColFilter struct {
   216  	md                 []*chaincode.Metadata
   217  	isMemberAuthorized memberFilter
   218  }
   219  
   220  func loadMetadataAndFilters(ctx metadataAndFilterContext) (*metadataAndColFilter, error) {
   221  	var metadata []*chaincode.Metadata
   222  	var filters []identityFilter
   223  
   224  	for _, chaincode := range ctx.interest.Chaincodes {
   225  		ccMD := ctx.fetch.Metadata(string(ctx.chainID), chaincode.Name, len(chaincode.CollectionNames) > 0)
   226  		if ccMD == nil {
   227  			return nil, errors.Errorf("No metadata was found for chaincode %s in channel %s", chaincode.Name, string(ctx.chainID))
   228  		}
   229  		metadata = append(metadata, ccMD)
   230  		if len(chaincode.CollectionNames) == 0 {
   231  			continue
   232  		}
   233  		principalSetByCollections, err := principalsFromCollectionConfig(ccMD.CollectionsConfig)
   234  		if err != nil {
   235  			logger.Warningf("Failed initializing collection filter for chaincode %s: %v", chaincode.Name, err)
   236  			return nil, errors.WithStack(err)
   237  		}
   238  		filter, err := principalSetByCollections.toIdentityFilter(string(ctx.chainID), ctx.evaluator, chaincode)
   239  		if err != nil {
   240  			logger.Warningf("Failed computing collection principal sets for chaincode %s due to %v", chaincode.Name, err)
   241  			return nil, errors.WithStack(err)
   242  		}
   243  		filters = append(filters, filter)
   244  	}
   245  
   246  	return computeFiltersWithMetadata(filters, metadata, ctx.identityInfoByID), nil
   247  }
   248  
   249  func computeFiltersWithMetadata(filters identityFilters, metadata []*chaincode.Metadata, identityInfoByID map[string]api.PeerIdentityInfo) *metadataAndColFilter {
   250  	if len(filters) == 0 {
   251  		return &metadataAndColFilter{
   252  			md:                 metadata,
   253  			isMemberAuthorized: noopMemberFilter,
   254  		}
   255  	}
   256  	filter := filters.combine().toMemberFilter(identityInfoByID)
   257  	return &metadataAndColFilter{
   258  		isMemberAuthorized: filter,
   259  		md:                 metadata,
   260  	}
   261  }
   262  
   263  // identityFilter accepts or rejects peer identities
   264  type identityFilter func(api.PeerIdentityType) bool
   265  
   266  // identityFilters aggregates multiple identityFilters
   267  type identityFilters []identityFilter
   268  
   269  // memberFilter accepts or rejects NetworkMembers
   270  type memberFilter func(member NetworkMember) bool
   271  
   272  // noopMemberFilter accepts every NetworkMember
   273  func noopMemberFilter(_ NetworkMember) bool {
   274  	return true
   275  }
   276  
   277  // combine combines all identityFilters into a single identityFilter which only accepts identities
   278  // which all the original filters accept
   279  func (filters identityFilters) combine() identityFilter {
   280  	return func(identity api.PeerIdentityType) bool {
   281  		for _, f := range filters {
   282  			if !f(identity) {
   283  				return false
   284  			}
   285  		}
   286  		return true
   287  	}
   288  }
   289  
   290  // toMemberFilter converts this identityFilter to a memberFilter based on the given mapping
   291  // from PKI-ID as strings, to PeerIdentityInfo which holds the peer identities
   292  func (idf identityFilter) toMemberFilter(identityInfoByID map[string]api.PeerIdentityInfo) memberFilter {
   293  	return func(member NetworkMember) bool {
   294  		identity, exists := identityInfoByID[string(member.PKIid)]
   295  		if !exists {
   296  			return false
   297  		}
   298  		return idf(identity.Identity)
   299  	}
   300  }
   301  
   302  func (ea *endorsementAnalyzer) satisfiesPrincipal(channel string, identitiesOfMembers memberIdentities) peerPrincipalEvaluator {
   303  	return func(member NetworkMember, principal *msp.MSPPrincipal) bool {
   304  		err := ea.SatisfiesPrincipal(channel, identitiesOfMembers.identityByPKIID(member.PKIid), principal)
   305  		if err == nil {
   306  			// TODO: log the principals in a human readable form
   307  			logger.Debug(member, "satisfies principal", principal)
   308  			return true
   309  		}
   310  		logger.Debug(member, "doesn't satisfy principal", principal, ":", err)
   311  		return false
   312  	}
   313  }
   314  
   315  type peerMembershipCriteria struct {
   316  	satGraph        *principalPeerGraph
   317  	idOfMembers     memberIdentities
   318  	chanMemberById  map[string]NetworkMember
   319  	possibleLayouts layouts
   320  }
   321  
   322  // endorsersByGroup computes a map from groups to peers.
   323  // Each group included, is found in some layout, which means
   324  // that there is some principal combination that includes the corresponding
   325  // group.
   326  // This means that if a group isn't included in the result, there is no
   327  // principal combination (that includes the principal corresponding to the group),
   328  // such that there are enough peers to satisfy the principal combination.
   329  func endorsersByGroup(criteria *peerMembershipCriteria) map[string]*discovery.Peers {
   330  	satGraph := criteria.satGraph
   331  	idOfMembers := criteria.idOfMembers
   332  	chanMemberById := criteria.chanMemberById
   333  	includedGroups := criteria.possibleLayouts.groupsSet()
   334  
   335  	res := make(map[string]*discovery.Peers)
   336  	// Map endorsers to their corresponding groups.
   337  	// Iterate the principals, and put the peers into each group that corresponds with a principal vertex
   338  	for grp, principalVertex := range satGraph.principalVertices {
   339  		if _, exists := includedGroups[grp]; !exists {
   340  			// If the current group is not found in any layout, skip the corresponding principal
   341  			continue
   342  		}
   343  		peerList := &discovery.Peers{}
   344  		res[grp] = peerList
   345  		for _, peerVertex := range principalVertex.Neighbors() {
   346  			member := peerVertex.Data.(NetworkMember)
   347  			peerList.Peers = append(peerList.Peers, &discovery.Peer{
   348  				Identity:       idOfMembers.identityByPKIID(member.PKIid),
   349  				StateInfo:      chanMemberById[string(member.PKIid)].Envelope,
   350  				MembershipInfo: member.Envelope,
   351  			})
   352  		}
   353  	}
   354  	return res
   355  }
   356  
   357  // computeLayouts computes all possible principal combinations
   358  // that can be used to satisfy the endorsement policy, given a graph
   359  // of available peers that maps each peer to a principal it satisfies.
   360  // Each such a combination is called a layout, because it maps
   361  // a group (alias for a principal) to a threshold of peers that need to endorse,
   362  // and that satisfy the corresponding principal.
   363  func computeLayouts(principalsSets []policies.PrincipalSet, principalGroups principalGroupMapper, satGraph *principalPeerGraph) []*discovery.Layout {
   364  	var layouts []*discovery.Layout
   365  	// principalsSets is a collection of combinations of principals,
   366  	// such that each combination (given enough peers) satisfies the endorsement policy.
   367  	for _, principalSet := range principalsSets {
   368  		layout := &discovery.Layout{
   369  			QuantitiesByGroup: make(map[string]uint32),
   370  		}
   371  		// Since principalsSet has repetitions, we first
   372  		// compute a mapping from the principal to repetitions in the set.
   373  		for principal, plurality := range principalSet.UniqueSet() {
   374  			key := principalKey{
   375  				cls:       int32(principal.PrincipalClassification),
   376  				principal: string(principal.Principal),
   377  			}
   378  			// We map the principal to a group, which is an alias for the principal.
   379  			layout.QuantitiesByGroup[principalGroups.group(key)] = uint32(plurality)
   380  		}
   381  		// Check that the layout can be satisfied with the current known peers
   382  		// This is done by iterating the current layout, and ensuring that
   383  		// each principal vertex is connected to at least <plurality> peer vertices.
   384  		if isLayoutSatisfied(layout.QuantitiesByGroup, satGraph) {
   385  			// If so, then add the layout to the layouts, since we have enough peers to satisfy the
   386  			// principal combination
   387  			layouts = append(layouts, layout)
   388  		}
   389  	}
   390  	return layouts
   391  }
   392  
   393  func isLayoutSatisfied(layout map[string]uint32, satGraph *principalPeerGraph) bool {
   394  	for grp, plurality := range layout {
   395  		// Do we have more than <plurality> peers connected to the principal?
   396  		if len(satGraph.principalVertices[grp].Neighbors()) < int(plurality) {
   397  			return false
   398  		}
   399  	}
   400  	return true
   401  }
   402  
   403  type principalPeerGraph struct {
   404  	peerVertices      []*graph.Vertex
   405  	principalVertices map[string]*graph.Vertex
   406  }
   407  
   408  type principalAndPeerData struct {
   409  	members Members
   410  	pGrps   principalGroupMapper
   411  }
   412  
   413  func principalsToPeersGraph(data principalAndPeerData, satisfiesPrincipal peerPrincipalEvaluator) *principalPeerGraph {
   414  	// Create the peer vertices
   415  	peerVertices := make([]*graph.Vertex, len(data.members))
   416  	for i, member := range data.members {
   417  		peerVertices[i] = graph.NewVertex(string(member.PKIid), member)
   418  	}
   419  
   420  	// Create the principal vertices
   421  	principalVertices := make(map[string]*graph.Vertex)
   422  	for pKey, grp := range data.pGrps {
   423  		principalVertices[grp] = graph.NewVertex(grp, pKey.toPrincipal())
   424  	}
   425  
   426  	// Connect principals and peers
   427  	for _, principalVertex := range principalVertices {
   428  		for _, peerVertex := range peerVertices {
   429  			// If the current peer satisfies the principal, connect their corresponding vertices with an edge
   430  			principal := principalVertex.Data.(*msp.MSPPrincipal)
   431  			member := peerVertex.Data.(NetworkMember)
   432  			if satisfiesPrincipal(member, principal) {
   433  				peerVertex.AddNeighbor(principalVertex)
   434  			}
   435  		}
   436  	}
   437  	return &principalPeerGraph{
   438  		peerVertices:      peerVertices,
   439  		principalVertices: principalVertices,
   440  	}
   441  }
   442  
   443  func mapPrincipalsToGroups(principalsSets []policies.PrincipalSet) principalGroupMapper {
   444  	groupMapper := make(principalGroupMapper)
   445  	totalPrincipals := make(map[principalKey]struct{})
   446  	for _, principalSet := range principalsSets {
   447  		for _, principal := range principalSet {
   448  			totalPrincipals[principalKey{
   449  				principal: string(principal.Principal),
   450  				cls:       int32(principal.PrincipalClassification),
   451  			}] = struct{}{}
   452  		}
   453  	}
   454  	for principal := range totalPrincipals {
   455  		groupMapper.group(principal)
   456  	}
   457  	return groupMapper
   458  }
   459  
   460  type memberIdentities map[string]api.PeerIdentityType
   461  
   462  func (m memberIdentities) identityByPKIID(id common.PKIidType) api.PeerIdentityType {
   463  	return m[string(id)]
   464  }
   465  
   466  func computeIdentitiesOfMembers(identitySet api.PeerIdentitySet, members map[string]NetworkMember) memberIdentities {
   467  	identitiesByPKIID := make(map[string]api.PeerIdentityType)
   468  	identitiesOfMembers := make(map[string]api.PeerIdentityType, len(members))
   469  	for _, identity := range identitySet {
   470  		identitiesByPKIID[string(identity.PKIId)] = identity.Identity
   471  	}
   472  	for _, member := range members {
   473  		if identity, exists := identitiesByPKIID[string(member.PKIid)]; exists {
   474  			identitiesOfMembers[string(member.PKIid)] = identity
   475  		}
   476  	}
   477  	return identitiesOfMembers
   478  }
   479  
   480  // principalGroupMapper maps principals to names of groups
   481  type principalGroupMapper map[principalKey]string
   482  
   483  func (mapper principalGroupMapper) group(principal principalKey) string {
   484  	if grp, exists := mapper[principal]; exists {
   485  		return grp
   486  	}
   487  	grp := fmt.Sprintf("G%d", len(mapper))
   488  	mapper[principal] = grp
   489  	return grp
   490  }
   491  
   492  type principalKey struct {
   493  	cls       int32
   494  	principal string
   495  }
   496  
   497  func (pk principalKey) toPrincipal() *msp.MSPPrincipal {
   498  	return &msp.MSPPrincipal{
   499  		PrincipalClassification: msp.MSPPrincipal_Classification(pk.cls),
   500  		Principal:               []byte(pk.principal),
   501  	}
   502  }
   503  
   504  // layouts is an aggregation of several layouts
   505  type layouts []*discovery.Layout
   506  
   507  // groupsSet returns a set of groups that the layouts contain
   508  func (l layouts) groupsSet() map[string]struct{} {
   509  	m := make(map[string]struct{})
   510  	for _, layout := range l {
   511  		for grp := range layout.QuantitiesByGroup {
   512  			m[grp] = struct{}{}
   513  		}
   514  	}
   515  	return m
   516  }
   517  
   518  func peersWithChaincode(metadata ...*chaincode.Metadata) func(member NetworkMember) bool {
   519  	return func(member NetworkMember) bool {
   520  		if member.Properties == nil {
   521  			return false
   522  		}
   523  		for _, ccMD := range metadata {
   524  			var found bool
   525  			for _, cc := range member.Properties.Chaincodes {
   526  				if cc.Name == ccMD.Name && cc.Version == ccMD.Version {
   527  					found = true
   528  				}
   529  			}
   530  			if !found {
   531  				return false
   532  			}
   533  		}
   534  		return true
   535  	}
   536  }
   537  
   538  func mergePrincipalSets(cpss []inquire.ComparablePrincipalSets) (inquire.ComparablePrincipalSets, error) {
   539  	// Obtain the first ComparablePrincipalSet first
   540  	var cps inquire.ComparablePrincipalSets
   541  	cps, cpss, err := popComparablePrincipalSets(cpss)
   542  	if err != nil {
   543  		return nil, errors.WithStack(err)
   544  	}
   545  
   546  	for _, cps2 := range cpss {
   547  		cps = inquire.Merge(cps, cps2)
   548  	}
   549  	return cps, nil
   550  }
   551  
   552  func popComparablePrincipalSets(sets []inquire.ComparablePrincipalSets) (inquire.ComparablePrincipalSets, []inquire.ComparablePrincipalSets, error) {
   553  	if len(sets) == 0 {
   554  		return nil, nil, errors.New("no principal sets remained after filtering")
   555  	}
   556  	cps, cpss := sets[0], sets[1:]
   557  	return cps, cpss, nil
   558  }