github.com/yous1230/fabric@v2.0.0-beta.0.20191224111736-74345bee6ac2+incompatible/discovery/endorsement/endorsement_test.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  	"testing"
    12  
    13  	"github.com/golang/protobuf/proto"
    14  	discoveryprotos "github.com/hyperledger/fabric-protos-go/discovery"
    15  	"github.com/hyperledger/fabric-protos-go/gossip"
    16  	"github.com/hyperledger/fabric-protos-go/msp"
    17  	"github.com/hyperledger/fabric-protos-go/peer"
    18  	"github.com/hyperledger/fabric/common/chaincode"
    19  	"github.com/hyperledger/fabric/common/policies"
    20  	"github.com/hyperledger/fabric/common/policies/inquire"
    21  	"github.com/hyperledger/fabric/gossip/api"
    22  	"github.com/hyperledger/fabric/gossip/common"
    23  	"github.com/hyperledger/fabric/gossip/discovery"
    24  	"github.com/hyperledger/fabric/protoutil"
    25  	"github.com/pkg/errors"
    26  	"github.com/stretchr/testify/assert"
    27  	"github.com/stretchr/testify/mock"
    28  )
    29  
    30  var pkiID2MSPID = map[string]string{
    31  	"p0":  "Org0MSP",
    32  	"p1":  "Org1MSP",
    33  	"p2":  "Org2MSP",
    34  	"p3":  "Org3MSP",
    35  	"p4":  "Org4MSP",
    36  	"p5":  "Org5MSP",
    37  	"p6":  "Org6MSP",
    38  	"p7":  "Org7MSP",
    39  	"p8":  "Org8MSP",
    40  	"p9":  "Org9MSP",
    41  	"p10": "Org10MSP",
    42  	"p11": "Org11MSP",
    43  	"p12": "Org12MSP",
    44  	"p13": "Org13MSP",
    45  	"p14": "Org14MSP",
    46  	"p15": "Org15MSP",
    47  }
    48  
    49  func TestPeersForEndorsement(t *testing.T) {
    50  	extractPeers := func(desc *discoveryprotos.EndorsementDescriptor) map[string]struct{} {
    51  		res := map[string]struct{}{}
    52  		for _, endorsers := range desc.EndorsersByGroups {
    53  			for _, p := range endorsers.Peers {
    54  				res[string(p.Identity)] = struct{}{}
    55  				assert.Equal(t, string(p.Identity), string(p.MembershipInfo.Payload))
    56  				assert.Equal(t, string(p.Identity), string(p.StateInfo.Payload))
    57  			}
    58  		}
    59  		return res
    60  	}
    61  	cc := "chaincode"
    62  	mf := &metadataFetcher{}
    63  	g := &gossipMock{}
    64  	pf := &policyFetcherMock{}
    65  	ccWithMissingPolicy := "chaincodeWithMissingPolicy"
    66  	channel := common.ChannelID("test")
    67  	alivePeers := peerSet{
    68  		newPeer(0),
    69  		newPeer(2),
    70  		newPeer(4),
    71  		newPeer(6),
    72  		newPeer(8),
    73  		newPeer(10),
    74  		newPeer(11),
    75  		newPeer(12),
    76  	}
    77  
    78  	identities := identitySet(pkiID2MSPID)
    79  
    80  	chanPeers := peerSet{
    81  		newPeer(0).withChaincode(cc, "1.0"),
    82  		newPeer(3).withChaincode(cc, "1.0"),
    83  		newPeer(6).withChaincode(cc, "1.0"),
    84  		newPeer(9).withChaincode(cc, "1.0"),
    85  		newPeer(11).withChaincode(cc, "1.0"),
    86  		newPeer(12).withChaincode(cc, "1.0"),
    87  	}
    88  	g.On("Peers").Return(alivePeers.toMembers())
    89  	g.On("IdentityInfo").Return(identities)
    90  
    91  	// Scenario I: Policy isn't found
    92  	t.Run("PolicyNotFound", func(t *testing.T) {
    93  		pf.On("PolicyByChaincode", ccWithMissingPolicy).Return(nil).Once()
    94  		g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once()
    95  		mf.On("Metadata").Return(&chaincode.Metadata{
    96  			Name:    cc,
    97  			Version: "1.0",
    98  		}).Once()
    99  		analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf)
   100  		desc, err := analyzer.PeersForEndorsement(channel, &discoveryprotos.ChaincodeInterest{
   101  			Chaincodes: []*discoveryprotos.ChaincodeCall{
   102  				{
   103  					Name: ccWithMissingPolicy,
   104  				},
   105  			},
   106  		})
   107  		assert.Nil(t, desc)
   108  		assert.Equal(t, "policy not found", err.Error())
   109  	})
   110  
   111  	t.Run("NotEnoughPeers", func(t *testing.T) {
   112  		// Scenario II: Policy is found but not enough peers to satisfy the policy.
   113  		// The policy requires a signature from:
   114  		// p1 and p6, or
   115  		// p11 x2 (twice), but we only have a single peer in the alive view for p11
   116  		pb := principalBuilder{}
   117  		policy := pb.newSet().addPrincipal(peerRole("p1")).addPrincipal(peerRole("p6")).
   118  			newSet().addPrincipal(peerRole("p11")).addPrincipal(peerRole("p11")).buildPolicy()
   119  		g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once()
   120  		mf.On("Metadata").Return(&chaincode.Metadata{Name: cc, Version: "1.0"}).Once()
   121  		analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf)
   122  		pf.On("PolicyByChaincode", cc).Return(policy).Once()
   123  		desc, err := analyzer.PeersForEndorsement(channel, &discoveryprotos.ChaincodeInterest{
   124  			Chaincodes: []*discoveryprotos.ChaincodeCall{
   125  				{
   126  					Name: cc,
   127  				},
   128  			},
   129  		})
   130  		assert.Nil(t, desc)
   131  		assert.Equal(t, err.Error(), "cannot satisfy any principal combination")
   132  	})
   133  
   134  	t.Run("DisjointViews", func(t *testing.T) {
   135  		pb := principalBuilder{}
   136  		// Scenario III: Policy is found and there are enough peers to satisfy
   137  		// only 1 type of principal combination: p0 and p6.
   138  		// However, the combination of a signature from p10 and p12
   139  		// cannot be satisfied because p10 is not in the channel view but only in the alive view
   140  		policy := pb.newSet().addPrincipal(peerRole("p0")).addPrincipal(peerRole("p6")).
   141  			newSet().addPrincipal(peerRole("p10")).addPrincipal(peerRole("p12")).buildPolicy()
   142  		g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once()
   143  		mf.On("Metadata").Return(&chaincode.Metadata{
   144  			Name:    cc,
   145  			Version: "1.0",
   146  		}).Once()
   147  		analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf)
   148  		pf.On("PolicyByChaincode", cc).Return(policy).Once()
   149  		desc, err := analyzer.PeersForEndorsement(channel, &discoveryprotos.ChaincodeInterest{
   150  			Chaincodes: []*discoveryprotos.ChaincodeCall{
   151  				{
   152  					Name: cc,
   153  				},
   154  			},
   155  		})
   156  		assert.NoError(t, err)
   157  		assert.NotNil(t, desc)
   158  		assert.Len(t, desc.Layouts, 1)
   159  		assert.Len(t, desc.Layouts[0].QuantitiesByGroup, 2)
   160  		assert.Equal(t, map[string]struct{}{
   161  			peerIdentityString("p0"): {},
   162  			peerIdentityString("p6"): {},
   163  		}, extractPeers(desc))
   164  	})
   165  
   166  	t.Run("MultipleCombinations", func(t *testing.T) {
   167  		// Scenario IV: Policy is found and there are enough peers to satisfy
   168  		// 2 principal combinations:
   169  		// p0 and p6, or
   170  		// p12 alone
   171  		pb := principalBuilder{}
   172  		policy := pb.newSet().addPrincipal(peerRole("p0")).addPrincipal(peerRole("p6")).
   173  			newSet().addPrincipal(peerRole("p12")).buildPolicy()
   174  		g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once()
   175  		mf.On("Metadata").Return(&chaincode.Metadata{
   176  			Name:    cc,
   177  			Version: "1.0",
   178  		}).Once()
   179  		analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf)
   180  		pf.On("PolicyByChaincode", cc).Return(policy).Once()
   181  		desc, err := analyzer.PeersForEndorsement(channel, &discoveryprotos.ChaincodeInterest{
   182  			Chaincodes: []*discoveryprotos.ChaincodeCall{
   183  				{
   184  					Name: cc,
   185  				},
   186  			},
   187  		})
   188  		assert.NoError(t, err)
   189  		assert.NotNil(t, desc)
   190  		assert.Len(t, desc.Layouts, 2)
   191  		assert.Len(t, desc.Layouts[0].QuantitiesByGroup, 2)
   192  		assert.Len(t, desc.Layouts[1].QuantitiesByGroup, 1)
   193  		assert.Equal(t, map[string]struct{}{
   194  			peerIdentityString("p0"):  {},
   195  			peerIdentityString("p6"):  {},
   196  			peerIdentityString("p12"): {},
   197  		}, extractPeers(desc))
   198  	})
   199  
   200  	t.Run("WrongVersionInstalled", func(t *testing.T) {
   201  		// Scenario V: Policy is found, and there are enough peers to satisfy policy combinations,
   202  		// but all peers have the wrong version installed on them.
   203  		mf.On("Metadata").Return(&chaincode.Metadata{
   204  			Name:    cc,
   205  			Version: "1.1",
   206  		}).Once()
   207  		pb := principalBuilder{}
   208  		policy := pb.newSet().addPrincipal(peerRole("p0")).addPrincipal(peerRole("p6")).
   209  			newSet().addPrincipal(peerRole("p12")).buildPolicy()
   210  		g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once()
   211  		pf.On("PolicyByChaincode", cc).Return(policy).Once()
   212  		analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf)
   213  		desc, err := analyzer.PeersForEndorsement(channel, &discoveryprotos.ChaincodeInterest{
   214  			Chaincodes: []*discoveryprotos.ChaincodeCall{
   215  				{
   216  					Name: cc,
   217  				},
   218  			},
   219  		})
   220  		assert.Nil(t, desc)
   221  		assert.Equal(t, "cannot satisfy any principal combination", err.Error())
   222  
   223  		// Scenario VI: Policy is found, there are enough peers to satisfy policy combinations,
   224  		// but some peers have the wrong chaincode version, and some don't even have it installed.
   225  		chanPeers := peerSet{
   226  			newPeer(0).withChaincode(cc, "0.6"),
   227  			newPeer(3).withChaincode(cc, "1.0"),
   228  			newPeer(6).withChaincode(cc, "1.0"),
   229  			newPeer(9).withChaincode(cc, "1.0"),
   230  			newPeer(12),
   231  		}
   232  		g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once()
   233  		pf.On("PolicyByChaincode", cc).Return(policy).Once()
   234  		mf.On("Metadata").Return(&chaincode.Metadata{
   235  			Name:    cc,
   236  			Version: "1.0",
   237  		}).Once()
   238  		desc, err = analyzer.PeersForEndorsement(channel, &discoveryprotos.ChaincodeInterest{
   239  			Chaincodes: []*discoveryprotos.ChaincodeCall{
   240  				{
   241  					Name: cc,
   242  				},
   243  			},
   244  		})
   245  		assert.Nil(t, desc)
   246  		assert.Equal(t, "cannot satisfy any principal combination", err.Error())
   247  	})
   248  
   249  	t.Run("NoChaincodeMetadataFromLedger", func(t *testing.T) {
   250  		// Scenario VII: Policy is found, there are enough peers to satisfy the policy,
   251  		// but the chaincode metadata cannot be fetched from the ledger.
   252  		pb := principalBuilder{}
   253  		policy := pb.newSet().addPrincipal(peerRole("p0")).addPrincipal(peerRole("p6")).
   254  			newSet().addPrincipal(peerRole("p12")).buildPolicy()
   255  		g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once()
   256  		pf.On("PolicyByChaincode", cc).Return(policy).Once()
   257  		mf.On("Metadata").Return(nil).Once()
   258  		analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf)
   259  		desc, err := analyzer.PeersForEndorsement(channel, &discoveryprotos.ChaincodeInterest{
   260  			Chaincodes: []*discoveryprotos.ChaincodeCall{
   261  				{
   262  					Name: cc,
   263  				},
   264  			},
   265  		})
   266  		assert.Nil(t, desc)
   267  		assert.Equal(t, "No metadata was found for chaincode chaincode in channel test", err.Error())
   268  	})
   269  
   270  	t.Run("Collections", func(t *testing.T) {
   271  		// Scenario VIII: Policy is found and there are enough peers to satisfy
   272  		// 2 principal combinations: p0 and p6, or p12 alone.
   273  		// However, the query contains a collection which has a policy that permits only p0 and p12,
   274  		// and thus - the combination of p0 and p6 is filtered out and we're left with p12 only.
   275  		collectionOrgs := []*msp.MSPPrincipal{
   276  			peerRole("p0"),
   277  			peerRole("p12"),
   278  		}
   279  		col2principals := map[string][]*msp.MSPPrincipal{
   280  			"collection": collectionOrgs,
   281  		}
   282  		mf.On("Metadata").Return(&chaincode.Metadata{
   283  			Name:              cc,
   284  			Version:           "1.0",
   285  			CollectionsConfig: buildCollectionConfig(col2principals),
   286  		}).Once()
   287  		pb := principalBuilder{}
   288  		policy := pb.newSet().addPrincipal(peerRole("p0")).
   289  			addPrincipal(peerRole("p6")).newSet().
   290  			addPrincipal(peerRole("p12")).buildPolicy()
   291  		g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once()
   292  		pf.On("PolicyByChaincode", cc).Return(policy).Once()
   293  		analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf)
   294  		desc, err := analyzer.PeersForEndorsement(channel, &discoveryprotos.ChaincodeInterest{
   295  			Chaincodes: []*discoveryprotos.ChaincodeCall{
   296  				{
   297  					Name:            cc,
   298  					CollectionNames: []string{"collection"},
   299  				},
   300  			},
   301  		})
   302  		assert.NoError(t, err)
   303  		assert.NotNil(t, desc)
   304  		assert.Len(t, desc.Layouts, 1)
   305  		assert.Len(t, desc.Layouts[0].QuantitiesByGroup, 1)
   306  		assert.Equal(t, map[string]struct{}{
   307  			peerIdentityString("p12"): {},
   308  		}, extractPeers(desc))
   309  	})
   310  
   311  	t.Run("Chaincode2Chaincode I", func(t *testing.T) {
   312  		// Scenario IX: A chaincode-to-chaincode query is made.
   313  		// Total organizations are 0, 2, 4, 6, 10, 12
   314  		// and the endorsement policies of the chaincodes are as follows:
   315  		// cc1: OR(AND(0, 2), AND(6, 10))
   316  		// cc2: AND(6, 10, 12)
   317  		// cc3: AND(4, 12)
   318  		// Therefore, the result should be: 4, 6, 10, 12
   319  
   320  		chanPeers := peerSet{}
   321  		for _, id := range []int{0, 2, 4, 6, 10, 12} {
   322  			peer := newPeer(id).withChaincode("cc1", "1.0").withChaincode("cc2", "1.0").withChaincode("cc3", "1.0")
   323  			chanPeers = append(chanPeers, peer)
   324  		}
   325  
   326  		g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once()
   327  
   328  		mf.On("Metadata").Return(&chaincode.Metadata{
   329  			Name:    "cc1",
   330  			Version: "1.0",
   331  		}).Once()
   332  		mf.On("Metadata").Return(&chaincode.Metadata{
   333  			Name:    "cc2",
   334  			Version: "1.0",
   335  		}).Once()
   336  		mf.On("Metadata").Return(&chaincode.Metadata{
   337  			Name:    "cc3",
   338  			Version: "1.0",
   339  		}).Once()
   340  
   341  		pb := principalBuilder{}
   342  		cc1policy := pb.newSet().addPrincipal(peerRole("p0")).addPrincipal(peerRole("p2")).
   343  			newSet().addPrincipal(peerRole("p6")).addPrincipal(peerRole("p10")).buildPolicy()
   344  
   345  		pf.On("PolicyByChaincode", "cc1").Return(cc1policy).Once()
   346  
   347  		cc2policy := pb.newSet().addPrincipal(peerRole("p6")).
   348  			addPrincipal(peerRole("p10")).addPrincipal(peerRole("p12")).buildPolicy()
   349  		pf.On("PolicyByChaincode", "cc2").Return(cc2policy).Once()
   350  
   351  		cc3policy := pb.newSet().addPrincipal(peerRole("p4")).
   352  			addPrincipal(peerRole("p12")).buildPolicy()
   353  		pf.On("PolicyByChaincode", "cc3").Return(cc3policy).Once()
   354  
   355  		analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf)
   356  		desc, err := analyzer.PeersForEndorsement(channel, &discoveryprotos.ChaincodeInterest{
   357  			Chaincodes: []*discoveryprotos.ChaincodeCall{
   358  				{
   359  					Name: "cc1",
   360  				},
   361  				{
   362  					Name: "cc2",
   363  				},
   364  				{
   365  					Name: "cc3",
   366  				},
   367  			},
   368  		})
   369  		assert.NoError(t, err)
   370  		assert.NotNil(t, desc)
   371  		assert.Len(t, desc.Layouts, 1)
   372  		assert.Len(t, desc.Layouts[0].QuantitiesByGroup, 4)
   373  		assert.Equal(t, map[string]struct{}{
   374  			peerIdentityString("p4"):  {},
   375  			peerIdentityString("p6"):  {},
   376  			peerIdentityString("p10"): {},
   377  			peerIdentityString("p12"): {},
   378  		}, extractPeers(desc))
   379  	})
   380  
   381  	t.Run("Chaincode2Chaincode II", func(t *testing.T) {
   382  		// Scenario X: A chaincode-to-chaincode query is made.
   383  		// and the endorsement policies of the chaincodes are as follows:
   384  		// cc1: OR(0, 1)
   385  		// cc2: AND(0, 1)
   386  		// Therefore, the result should be: (0, 1)
   387  
   388  		cc1 := "cc1"
   389  		cc2 := "cc2"
   390  		chanPeers := peerSet{
   391  			newPeer(0).withChaincode(cc1, "1.0").withChaincode(cc2, "1.0"),
   392  			newPeer(1).withChaincode(cc1, "1.0").withChaincode(cc2, "1.0"),
   393  		}.toMembers()
   394  
   395  		alivePeers := peerSet{
   396  			newPeer(0),
   397  			newPeer(1),
   398  		}.toMembers()
   399  
   400  		g := &gossipMock{}
   401  		g.On("Peers").Return(alivePeers)
   402  		g.On("IdentityInfo").Return(identities)
   403  		g.On("PeersOfChannel").Return(chanPeers).Once()
   404  
   405  		mf.On("Metadata").Return(&chaincode.Metadata{
   406  			Name:    "cc1",
   407  			Version: "1.0",
   408  		})
   409  		mf.On("Metadata").Return(&chaincode.Metadata{
   410  			Name:    "cc2",
   411  			Version: "1.0",
   412  		})
   413  
   414  		pb := principalBuilder{}
   415  		cc1policy := pb.newSet().addPrincipal(peerRole("p0")).
   416  			newSet().addPrincipal(peerRole("p1")).buildPolicy()
   417  		pf.On("PolicyByChaincode", "cc1").Return(cc1policy).Once()
   418  
   419  		cc2policy := pb.newSet().addPrincipal(peerRole("p0")).
   420  			addPrincipal(peerRole("p1")).buildPolicy()
   421  		pf.On("PolicyByChaincode", "cc2").Return(cc2policy).Once()
   422  
   423  		analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf)
   424  		desc, err := analyzer.PeersForEndorsement(channel, &discoveryprotos.ChaincodeInterest{
   425  			Chaincodes: []*discoveryprotos.ChaincodeCall{
   426  				{
   427  					Name: "cc1",
   428  				},
   429  				{
   430  					Name: "cc2",
   431  				},
   432  			},
   433  		})
   434  		assert.NoError(t, err)
   435  		assert.NotNil(t, desc)
   436  		assert.Len(t, desc.Layouts, 1)
   437  		assert.Len(t, desc.Layouts[0].QuantitiesByGroup, 2)
   438  		assert.Equal(t, map[string]struct{}{
   439  			peerIdentityString("p0"): {},
   440  			peerIdentityString("p1"): {},
   441  		}, extractPeers(desc))
   442  	})
   443  }
   444  
   445  func TestPeersAuthorizedByCriteria(t *testing.T) {
   446  	cc1 := "cc1"
   447  	cc2 := "cc2"
   448  	members := peerSet{
   449  		newPeer(0).withChaincode(cc1, "1.0"),
   450  		newPeer(3).withChaincode(cc1, "1.0"),
   451  		newPeer(6).withChaincode(cc1, "1.0"),
   452  		newPeer(9).withChaincode(cc1, "1.0"),
   453  		newPeer(12).withChaincode(cc1, "1.0"),
   454  	}.toMembers()
   455  
   456  	members2 := append(discovery.Members{}, members...)
   457  	members2 = append(members2, peerSet{newPeer(13).withChaincode(cc1, "1.1").withChaincode(cc2, "1.0")}.toMembers()...)
   458  	members2 = append(members2, peerSet{newPeer(14).withChaincode(cc1, "1.1")}.toMembers()...)
   459  	members2 = append(members2, peerSet{newPeer(15).withChaincode(cc2, "1.0")}.toMembers()...)
   460  
   461  	alivePeers := peerSet{
   462  		newPeer(0),
   463  		newPeer(2),
   464  		newPeer(4),
   465  		newPeer(6),
   466  		newPeer(8),
   467  		newPeer(10),
   468  		newPeer(11),
   469  		newPeer(12),
   470  		newPeer(13),
   471  		newPeer(14),
   472  		newPeer(15),
   473  	}.toMembers()
   474  
   475  	identities := identitySet(pkiID2MSPID)
   476  
   477  	for _, tst := range []struct {
   478  		name                 string
   479  		arguments            *discoveryprotos.ChaincodeInterest
   480  		totalExistingMembers discovery.Members
   481  		metadata             []*chaincode.Metadata
   482  		expected             discovery.Members
   483  	}{
   484  		{
   485  			name:                 "Nil interest",
   486  			arguments:            nil,
   487  			totalExistingMembers: members,
   488  			expected:             members,
   489  		},
   490  		{
   491  			name:                 "Empty interest invocation chain",
   492  			arguments:            &discoveryprotos.ChaincodeInterest{},
   493  			totalExistingMembers: members,
   494  			expected:             members,
   495  		},
   496  		{
   497  			name: "Chaincodes only installed on some peers",
   498  			arguments: &discoveryprotos.ChaincodeInterest{
   499  				Chaincodes: []*discoveryprotos.ChaincodeCall{
   500  					{Name: cc1},
   501  					{Name: cc2},
   502  				},
   503  			},
   504  			totalExistingMembers: members2,
   505  			metadata: []*chaincode.Metadata{
   506  				{
   507  					Name:    "cc1",
   508  					Version: "1.1",
   509  				},
   510  				{
   511  					Name:    "cc2",
   512  					Version: "1.0",
   513  				},
   514  			},
   515  			expected: peerSet{newPeer(13).withChaincode(cc1, "1.1").withChaincode(cc2, "1.0")}.toMembers(),
   516  		},
   517  		{
   518  			name: "Only some peers authorized by collection",
   519  			arguments: &discoveryprotos.ChaincodeInterest{
   520  				Chaincodes: []*discoveryprotos.ChaincodeCall{
   521  					{Name: cc1, CollectionNames: []string{"collection"}},
   522  				},
   523  			},
   524  			totalExistingMembers: members,
   525  			metadata: []*chaincode.Metadata{
   526  				{
   527  					Name:    cc1,
   528  					Version: "1.0",
   529  					CollectionsConfig: buildCollectionConfig(map[string][]*msp.MSPPrincipal{
   530  						"collection": {
   531  							peerRole("p0"),
   532  							peerRole("p12"),
   533  						},
   534  					}),
   535  				},
   536  				{
   537  					Name:    cc1,
   538  					Version: "1.0",
   539  					CollectionsConfig: buildCollectionConfig(map[string][]*msp.MSPPrincipal{
   540  						"collection": {
   541  							peerRole("p3"),
   542  							peerRole("p9"),
   543  						},
   544  					}),
   545  				},
   546  			},
   547  			expected: peerSet{
   548  				newPeer(0).withChaincode(cc1, "1.0"),
   549  				newPeer(12).withChaincode(cc1, "1.0")}.toMembers(),
   550  		},
   551  	} {
   552  		t.Run(tst.name, func(t *testing.T) {
   553  			g := &gossipMock{}
   554  			pf := &policyFetcherMock{}
   555  			mf := &metadataFetcher{}
   556  			g.On("Peers").Return(alivePeers)
   557  			g.On("IdentityInfo").Return(identities)
   558  			g.On("PeersOfChannel").Return(tst.totalExistingMembers).Once()
   559  			for _, md := range tst.metadata {
   560  				mf.On("Metadata").Return(md).Once()
   561  			}
   562  
   563  			analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf)
   564  			actualMembers, err := analyzer.PeersAuthorizedByCriteria(common.ChannelID("mychannel"), tst.arguments)
   565  			assert.NoError(t, err)
   566  			assert.Equal(t, tst.expected, actualMembers)
   567  		})
   568  	}
   569  }
   570  
   571  func TestPop(t *testing.T) {
   572  	slice := []inquire.ComparablePrincipalSets{{}, {}}
   573  	assert.Len(t, slice, 2)
   574  	_, slice, err := popComparablePrincipalSets(slice)
   575  	assert.NoError(t, err)
   576  	assert.Len(t, slice, 1)
   577  	_, slice, err = popComparablePrincipalSets(slice)
   578  	assert.Len(t, slice, 0)
   579  	_, slice, err = popComparablePrincipalSets(slice)
   580  	assert.Error(t, err)
   581  	assert.Equal(t, "no principal sets remained after filtering", err.Error())
   582  }
   583  
   584  func TestMergePrincipalSetsNilInput(t *testing.T) {
   585  	_, err := mergePrincipalSets(nil)
   586  	assert.Error(t, err)
   587  	assert.Equal(t, "no principal sets remained after filtering", err.Error())
   588  }
   589  
   590  func TestComputePrincipalSetsNoPolicies(t *testing.T) {
   591  	// Tests a hypothetical case where no chaincodes populate the chaincode interest.
   592  
   593  	interest := &discoveryprotos.ChaincodeInterest{
   594  		Chaincodes: []*discoveryprotos.ChaincodeCall{},
   595  	}
   596  	ea := &endorsementAnalyzer{}
   597  	_, err := ea.computePrincipalSets(common.ChannelID("mychannel"), interest)
   598  	assert.Error(t, err)
   599  	assert.Contains(t, err.Error(), "no principal sets remained after filtering")
   600  }
   601  
   602  func TestLoadMetadataAndFiltersCollectionNotPresentInConfig(t *testing.T) {
   603  	interest := &discoveryprotos.ChaincodeInterest{
   604  		Chaincodes: []*discoveryprotos.ChaincodeCall{
   605  			{
   606  				Name:            "mycc",
   607  				CollectionNames: []string{"bar"},
   608  			},
   609  		},
   610  	}
   611  
   612  	org1AndOrg2 := []*msp.MSPPrincipal{
   613  		orgPrincipal("Org1MSP"),
   614  		orgPrincipal("Org2MSP"),
   615  	}
   616  	col2principals := map[string][]*msp.MSPPrincipal{
   617  		"foo": org1AndOrg2,
   618  	}
   619  	config := buildCollectionConfig(col2principals)
   620  
   621  	mdf := &metadataFetcher{}
   622  	mdf.On("Metadata").Return(&chaincode.Metadata{
   623  		Name:              "mycc",
   624  		CollectionsConfig: config,
   625  		Policy:            []byte{1, 2, 3},
   626  	})
   627  
   628  	_, err := loadMetadataAndFilters(metadataAndFilterContext{
   629  		identityInfoByID: nil,
   630  		evaluator:        nil,
   631  		chainID:          common.ChannelID("mychannel"),
   632  		fetch:            mdf,
   633  		interest:         interest,
   634  	})
   635  
   636  	assert.Equal(t, "collection bar doesn't exist in collection config for chaincode mycc", err.Error())
   637  }
   638  
   639  func TestLoadMetadataAndFiltersInvalidCollectionData(t *testing.T) {
   640  	interest := &discoveryprotos.ChaincodeInterest{
   641  		Chaincodes: []*discoveryprotos.ChaincodeCall{
   642  			{
   643  				Name:            "mycc",
   644  				CollectionNames: []string{"col1"},
   645  			},
   646  		},
   647  	}
   648  	mdf := &metadataFetcher{}
   649  	mdf.On("Metadata").Return(&chaincode.Metadata{
   650  		Name:              "mycc",
   651  		CollectionsConfig: &peer.CollectionConfigPackage{},
   652  		Policy:            []byte{1, 2, 3},
   653  	})
   654  
   655  	_, err := loadMetadataAndFilters(metadataAndFilterContext{
   656  		identityInfoByID: nil,
   657  		evaluator:        nil,
   658  		chainID:          common.ChannelID("mychannel"),
   659  		fetch:            mdf,
   660  		interest:         interest,
   661  	})
   662  	assert.Error(t, err)
   663  	assert.Contains(t, err.Error(), "collection col1 doesn't exist in collection config for chaincode mycc")
   664  }
   665  
   666  type peerSet []*peerInfo
   667  
   668  func (p peerSet) toMembers() discovery.Members {
   669  	var members discovery.Members
   670  	for _, peer := range p {
   671  		members = append(members, peer.NetworkMember)
   672  	}
   673  	return members
   674  }
   675  
   676  func identitySet(pkiID2MSPID map[string]string) api.PeerIdentitySet {
   677  	var res api.PeerIdentitySet
   678  	for pkiID, mspID := range pkiID2MSPID {
   679  		sID := &msp.SerializedIdentity{
   680  			Mspid:   pkiID2MSPID[pkiID],
   681  			IdBytes: []byte(pkiID),
   682  		}
   683  		res = append(res, api.PeerIdentityInfo{
   684  			Identity:     api.PeerIdentityType(protoutil.MarshalOrPanic(sID)),
   685  			PKIId:        common.PKIidType(pkiID),
   686  			Organization: api.OrgIdentityType(mspID),
   687  		})
   688  	}
   689  	return res
   690  }
   691  
   692  type peerInfo struct {
   693  	identity api.PeerIdentityType
   694  	pkiID    common.PKIidType
   695  	discovery.NetworkMember
   696  }
   697  
   698  func peerIdentityString(id string) string {
   699  	return string(protoutil.MarshalOrPanic(&msp.SerializedIdentity{
   700  		Mspid:   pkiID2MSPID[id],
   701  		IdBytes: []byte(id),
   702  	}))
   703  }
   704  
   705  func newPeer(i int) *peerInfo {
   706  	p := fmt.Sprintf("p%d", i)
   707  	identity := protoutil.MarshalOrPanic(&msp.SerializedIdentity{
   708  		Mspid:   pkiID2MSPID[p],
   709  		IdBytes: []byte(p),
   710  	})
   711  	return &peerInfo{
   712  		pkiID:    common.PKIidType(p),
   713  		identity: api.PeerIdentityType(identity),
   714  		NetworkMember: discovery.NetworkMember{
   715  			PKIid:            common.PKIidType(p),
   716  			Endpoint:         p,
   717  			InternalEndpoint: p,
   718  			Envelope: &gossip.Envelope{
   719  				Payload: []byte(identity),
   720  			},
   721  		},
   722  	}
   723  }
   724  
   725  func peerRole(pkiID string) *msp.MSPPrincipal {
   726  	return &msp.MSPPrincipal{
   727  		PrincipalClassification: msp.MSPPrincipal_ROLE,
   728  		Principal: protoutil.MarshalOrPanic(&msp.MSPRole{
   729  			MspIdentifier: pkiID2MSPID[pkiID],
   730  			Role:          msp.MSPRole_PEER,
   731  		}),
   732  	}
   733  }
   734  
   735  func (pi *peerInfo) withChaincode(name, version string) *peerInfo {
   736  	if pi.Properties == nil {
   737  		pi.Properties = &gossip.Properties{}
   738  	}
   739  	pi.Properties.Chaincodes = append(pi.Properties.Chaincodes, &gossip.Chaincode{
   740  		Name:    name,
   741  		Version: version,
   742  	})
   743  	return pi
   744  }
   745  
   746  type gossipMock struct {
   747  	mock.Mock
   748  }
   749  
   750  func (g *gossipMock) IdentityInfo() api.PeerIdentitySet {
   751  	return g.Called().Get(0).(api.PeerIdentitySet)
   752  }
   753  
   754  func (g *gossipMock) PeersOfChannel(_ common.ChannelID) discovery.Members {
   755  	members := g.Called().Get(0)
   756  	return members.(discovery.Members)
   757  }
   758  
   759  func (g *gossipMock) Peers() discovery.Members {
   760  	members := g.Called().Get(0)
   761  	return members.(discovery.Members)
   762  }
   763  
   764  type policyFetcherMock struct {
   765  	mock.Mock
   766  }
   767  
   768  func (pf *policyFetcherMock) PolicyByChaincode(channel string, chaincode string) policies.InquireablePolicy {
   769  	arg := pf.Called(chaincode)
   770  	if arg.Get(0) == nil {
   771  		return nil
   772  	}
   773  	return arg.Get(0).(policies.InquireablePolicy)
   774  }
   775  
   776  type principalBuilder struct {
   777  	ip inquireablePolicy
   778  }
   779  
   780  func (pb *principalBuilder) buildPolicy() inquireablePolicy {
   781  	defer func() {
   782  		pb.ip = nil
   783  	}()
   784  	return pb.ip
   785  }
   786  
   787  func (pb *principalBuilder) newSet() *principalBuilder {
   788  	pb.ip = append(pb.ip, make(policies.PrincipalSet, 0))
   789  	return pb
   790  }
   791  
   792  func (pb *principalBuilder) addPrincipal(principal *msp.MSPPrincipal) *principalBuilder {
   793  	pb.ip[len(pb.ip)-1] = append(pb.ip[len(pb.ip)-1], principal)
   794  	return pb
   795  }
   796  
   797  type inquireablePolicy []policies.PrincipalSet
   798  
   799  func (ip inquireablePolicy) SatisfiedBy() []policies.PrincipalSet {
   800  	return ip
   801  }
   802  
   803  type principalEvaluatorMock struct {
   804  }
   805  
   806  func (pe *principalEvaluatorMock) SatisfiesPrincipal(channel string, identity []byte, principal *msp.MSPPrincipal) error {
   807  	peerRole := &msp.MSPRole{}
   808  	if err := proto.Unmarshal(principal.Principal, peerRole); err != nil {
   809  		return err
   810  	}
   811  	sId := &msp.SerializedIdentity{}
   812  	if err := proto.Unmarshal(identity, sId); err != nil {
   813  		return err
   814  	}
   815  	if peerRole.MspIdentifier == sId.Mspid {
   816  		return nil
   817  	}
   818  	return errors.New("bingo")
   819  }
   820  
   821  type metadataFetcher struct {
   822  	mock.Mock
   823  }
   824  
   825  func (mf *metadataFetcher) Metadata(channel string, cc string, _ bool) *chaincode.Metadata {
   826  	arg := mf.Called().Get(0)
   827  	if arg == nil {
   828  		return nil
   829  	}
   830  	return arg.(*chaincode.Metadata)
   831  }