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