github.com/hechain20/hechain@v0.0.0-20220316014945-b544036ba106/discovery/endorsement/endorsement_test.go (about)

     1  /*
     2  Copyright hechain. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package endorsement
     8  
     9  import (
    10  	"fmt"
    11  	"testing"
    12  
    13  	"github.com/hechain20/hechain/common/policydsl"
    14  	common2 "github.com/hyperledger/fabric-protos-go/common"
    15  
    16  	"github.com/golang/protobuf/proto"
    17  	"github.com/hechain20/hechain/common/chaincode"
    18  	"github.com/hechain20/hechain/common/policies"
    19  	"github.com/hechain20/hechain/common/policies/inquire"
    20  	"github.com/hechain20/hechain/gossip/api"
    21  	"github.com/hechain20/hechain/gossip/common"
    22  	"github.com/hechain20/hechain/gossip/discovery"
    23  	"github.com/hechain20/hechain/protoutil"
    24  	discoveryprotos "github.com/hyperledger/fabric-protos-go/discovery"
    25  	"github.com/hyperledger/fabric-protos-go/gossip"
    26  	"github.com/hyperledger/fabric-protos-go/msp"
    27  	"github.com/hyperledger/fabric-protos-go/peer"
    28  	"github.com/pkg/errors"
    29  	"github.com/stretchr/testify/mock"
    30  	"github.com/stretchr/testify/require"
    31  )
    32  
    33  var pkiID2MSPID = map[string]string{
    34  	"p0":  "Org0MSP",
    35  	"p1":  "Org1MSP",
    36  	"p2":  "Org2MSP",
    37  	"p3":  "Org3MSP",
    38  	"p4":  "Org4MSP",
    39  	"p5":  "Org5MSP",
    40  	"p6":  "Org6MSP",
    41  	"p7":  "Org7MSP",
    42  	"p8":  "Org8MSP",
    43  	"p9":  "Org9MSP",
    44  	"p10": "Org10MSP",
    45  	"p11": "Org11MSP",
    46  	"p12": "Org12MSP",
    47  	"p13": "Org13MSP",
    48  	"p14": "Org14MSP",
    49  	"p15": "Org15MSP",
    50  }
    51  
    52  func TestPeersForEndorsement(t *testing.T) {
    53  	extractPeers := func(desc *discoveryprotos.EndorsementDescriptor) map[string]struct{} {
    54  		res := map[string]struct{}{}
    55  		for _, endorsers := range desc.EndorsersByGroups {
    56  			for _, p := range endorsers.Peers {
    57  				res[string(p.Identity)] = struct{}{}
    58  				require.Equal(t, string(p.Identity), string(p.MembershipInfo.Payload))
    59  				require.Equal(t, string(p.Identity), string(p.StateInfo.Payload))
    60  			}
    61  		}
    62  		return res
    63  	}
    64  	cc := "chaincode"
    65  	g := &gossipMock{}
    66  	pf := &policyFetcherMock{}
    67  	ccWithMissingPolicy := "chaincodeWithMissingPolicy"
    68  	channel := common.ChannelID("test")
    69  	alivePeers := peerSet{
    70  		newPeer(0),
    71  		newPeer(2),
    72  		newPeer(4),
    73  		newPeer(6),
    74  		newPeer(8),
    75  		newPeer(10),
    76  		newPeer(11),
    77  		newPeer(12),
    78  	}
    79  
    80  	identities := identitySet(pkiID2MSPID)
    81  
    82  	chanPeers := peerSet{
    83  		newPeer(0).withChaincode(cc, "1.0"),
    84  		newPeer(3).withChaincode(cc, "1.0"),
    85  		newPeer(6).withChaincode(cc, "1.0"),
    86  		newPeer(9).withChaincode(cc, "1.0"),
    87  		newPeer(11).withChaincode(cc, "1.0"),
    88  		newPeer(12).withChaincode(cc, "1.0"),
    89  	}
    90  	g.On("Peers").Return(alivePeers.toMembers())
    91  	g.On("IdentityInfo").Return(identities)
    92  
    93  	// Scenario I: Policy isn't found
    94  	t.Run("PolicyNotFound", func(t *testing.T) {
    95  		pf.On("PoliciesByChaincode", ccWithMissingPolicy).Return(nil).Once()
    96  		g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once()
    97  		mf := &metadataFetcher{}
    98  		mf.On("Metadata").Return(&chaincode.Metadata{
    99  			Name:    cc,
   100  			Version: "1.0",
   101  		}).Once()
   102  		analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf)
   103  		desc, err := analyzer.PeersForEndorsement(channel, &peer.ChaincodeInterest{
   104  			Chaincodes: []*peer.ChaincodeCall{
   105  				{
   106  					Name: ccWithMissingPolicy,
   107  				},
   108  			},
   109  		})
   110  		require.Nil(t, desc)
   111  		require.Equal(t, "policy not found", err.Error())
   112  	})
   113  
   114  	t.Run("NotEnoughPeers", func(t *testing.T) {
   115  		// Scenario II: Policy is found but not enough peers to satisfy the policy.
   116  		// The policy requires a signature from:
   117  		// p1 and p6, or
   118  		// p11 x2 (twice), but we only have a single peer in the alive view for p11
   119  		pb := principalBuilder{}
   120  		policy := pb.newSet().addPrincipal(peerRole("p1")).addPrincipal(peerRole("p6")).
   121  			newSet().addPrincipal(peerRole("p11")).addPrincipal(peerRole("p11")).buildPolicy()
   122  		g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once()
   123  		mf := &metadataFetcher{}
   124  		mf.On("Metadata").Return(&chaincode.Metadata{Name: cc, Version: "1.0"}).Once()
   125  		analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf)
   126  		pf.On("PoliciesByChaincode", cc).Return(policy).Once()
   127  		desc, err := analyzer.PeersForEndorsement(channel, &peer.ChaincodeInterest{
   128  			Chaincodes: []*peer.ChaincodeCall{
   129  				{
   130  					Name: cc,
   131  				},
   132  			},
   133  		})
   134  		require.Nil(t, desc)
   135  		require.Equal(t, err.Error(), "no peer combination can satisfy the endorsement policy")
   136  	})
   137  
   138  	t.Run("DisjointViews", func(t *testing.T) {
   139  		pb := principalBuilder{}
   140  		// Scenario III: Policy is found and there are enough peers to satisfy
   141  		// only 1 type of principal combination: p0 and p6.
   142  		// However, the combination of a signature from p10 and p12
   143  		// cannot be satisfied because p10 is not in the channel view but only in the alive view
   144  		policy := pb.newSet().addPrincipal(peerRole("p0")).addPrincipal(peerRole("p6")).
   145  			newSet().addPrincipal(peerRole("p10")).addPrincipal(peerRole("p12")).buildPolicy()
   146  		g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once()
   147  		mf := &metadataFetcher{}
   148  		mf.On("Metadata").Return(&chaincode.Metadata{
   149  			Name:    cc,
   150  			Version: "1.0",
   151  		}).Once()
   152  		analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf)
   153  		pf.On("PoliciesByChaincode", cc).Return(policy).Once()
   154  		desc, err := analyzer.PeersForEndorsement(channel, &peer.ChaincodeInterest{
   155  			Chaincodes: []*peer.ChaincodeCall{
   156  				{
   157  					Name: cc,
   158  				},
   159  			},
   160  		})
   161  		require.NoError(t, err)
   162  		require.NotNil(t, desc)
   163  		require.Len(t, desc.Layouts, 1)
   164  		require.Len(t, desc.Layouts[0].QuantitiesByGroup, 2)
   165  		require.Equal(t, map[string]struct{}{
   166  			peerIdentityString("p0"): {},
   167  			peerIdentityString("p6"): {},
   168  		}, extractPeers(desc))
   169  	})
   170  
   171  	t.Run("MultipleCombinations", func(t *testing.T) {
   172  		// Scenario IV: Policy is found and there are enough peers to satisfy
   173  		// 2 principal combinations:
   174  		// p0 and p6, or
   175  		// p12 alone
   176  		pb := principalBuilder{}
   177  		policy := pb.newSet().addPrincipal(peerRole("p0")).addPrincipal(peerRole("p6")).
   178  			newSet().addPrincipal(peerRole("p12")).buildPolicy()
   179  		g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once()
   180  		mf := &metadataFetcher{}
   181  		mf.On("Metadata").Return(&chaincode.Metadata{
   182  			Name:    cc,
   183  			Version: "1.0",
   184  		}).Once()
   185  		analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf)
   186  		pf.On("PoliciesByChaincode", cc).Return(policy).Once()
   187  		desc, err := analyzer.PeersForEndorsement(channel, &peer.ChaincodeInterest{
   188  			Chaincodes: []*peer.ChaincodeCall{
   189  				{
   190  					Name: cc,
   191  				},
   192  			},
   193  		})
   194  		require.NoError(t, err)
   195  		require.NotNil(t, desc)
   196  		require.Len(t, desc.Layouts, 2)
   197  		require.Len(t, desc.Layouts[0].QuantitiesByGroup, 2)
   198  		require.Len(t, desc.Layouts[1].QuantitiesByGroup, 1)
   199  		require.Equal(t, map[string]struct{}{
   200  			peerIdentityString("p0"):  {},
   201  			peerIdentityString("p6"):  {},
   202  			peerIdentityString("p12"): {},
   203  		}, extractPeers(desc))
   204  	})
   205  
   206  	t.Run("WrongVersionInstalled", func(t *testing.T) {
   207  		// Scenario V: Policy is found, and there are enough peers to satisfy policy combinations,
   208  		// but all peers have the wrong version installed on them.
   209  		mf := &metadataFetcher{}
   210  		mf.On("Metadata").Return(&chaincode.Metadata{
   211  			Name:    cc,
   212  			Version: "1.1",
   213  		}).Once()
   214  		pb := principalBuilder{}
   215  		policy := pb.newSet().addPrincipal(peerRole("p0")).addPrincipal(peerRole("p6")).
   216  			newSet().addPrincipal(peerRole("p12")).buildPolicy()
   217  		g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once()
   218  		pf.On("PoliciesByChaincode", cc).Return(policy).Once()
   219  		analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf)
   220  		desc, err := analyzer.PeersForEndorsement(channel, &peer.ChaincodeInterest{
   221  			Chaincodes: []*peer.ChaincodeCall{
   222  				{
   223  					Name: cc,
   224  				},
   225  			},
   226  		})
   227  		require.Nil(t, desc)
   228  		require.Equal(t, "required chaincodes are not installed on sufficient peers", err.Error())
   229  
   230  		// Scenario VI: Policy is found, there are enough peers to satisfy policy combinations,
   231  		// but some peers have the wrong chaincode version, and some don't even have it installed.
   232  		chanPeers := peerSet{
   233  			newPeer(0).withChaincode(cc, "0.6"),
   234  			newPeer(3).withChaincode(cc, "1.0"),
   235  			newPeer(6).withChaincode(cc, "1.0"),
   236  			newPeer(9).withChaincode(cc, "1.0"),
   237  			newPeer(12),
   238  		}
   239  		g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once()
   240  		pf.On("PoliciesByChaincode", cc).Return(policy).Once()
   241  		mf.On("Metadata").Return(&chaincode.Metadata{
   242  			Name:    cc,
   243  			Version: "1.0",
   244  		}).Once()
   245  		desc, err = analyzer.PeersForEndorsement(channel, &peer.ChaincodeInterest{
   246  			Chaincodes: []*peer.ChaincodeCall{
   247  				{
   248  					Name: cc,
   249  				},
   250  			},
   251  		})
   252  		require.Nil(t, desc)
   253  		require.Equal(t, "required chaincodes are not installed on sufficient peers", err.Error())
   254  	})
   255  
   256  	t.Run("NoChaincodeMetadataFromLedger", func(t *testing.T) {
   257  		// Scenario VII: Policy is found, there are enough peers to satisfy the policy,
   258  		// but the chaincode metadata cannot be fetched from the ledger.
   259  		g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once()
   260  		mf := &metadataFetcher{}
   261  		mf.On("Metadata").Return(nil).Once()
   262  		analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf)
   263  		desc, err := analyzer.PeersForEndorsement(channel, &peer.ChaincodeInterest{
   264  			Chaincodes: []*peer.ChaincodeCall{
   265  				{
   266  					Name: cc,
   267  				},
   268  			},
   269  		})
   270  		require.Nil(t, desc)
   271  		require.Equal(t, "No metadata was found for chaincode chaincode in channel test", err.Error())
   272  	})
   273  
   274  	t.Run("Collections", func(t *testing.T) {
   275  		// Scenario VIII: Policy is found and there are enough peers to satisfy
   276  		// 2 principal combinations: p0 and p6, or p12 alone.
   277  		// However, the query contains a collection which has a policy that permits only p0 and p12,
   278  		// and thus - the combination of p0 and p6 is filtered out and we're left with p12 only.
   279  		collectionOrgs := []*msp.MSPPrincipal{
   280  			peerRole("p0"),
   281  			peerRole("p12"),
   282  		}
   283  		col2principals := map[string][]*msp.MSPPrincipal{
   284  			"collection": collectionOrgs,
   285  		}
   286  		mf := &metadataFetcher{}
   287  		mf.On("Metadata").Return(&chaincode.Metadata{
   288  			Name:              cc,
   289  			Version:           "1.0",
   290  			CollectionsConfig: buildCollectionConfig(col2principals),
   291  		}).Once()
   292  		pb := principalBuilder{}
   293  		policy := pb.newSet().addPrincipal(peerRole("p0")).
   294  			addPrincipal(peerRole("p6")).newSet().
   295  			addPrincipal(peerRole("p12")).buildPolicy()
   296  		g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once()
   297  		pf.On("PoliciesByChaincode", cc).Return(policy).Once()
   298  		analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf)
   299  		desc, err := analyzer.PeersForEndorsement(channel, &peer.ChaincodeInterest{
   300  			Chaincodes: []*peer.ChaincodeCall{
   301  				{
   302  					Name:            cc,
   303  					CollectionNames: []string{"collection"},
   304  				},
   305  			},
   306  		})
   307  		require.NoError(t, err)
   308  		require.NotNil(t, desc)
   309  		require.Len(t, desc.Layouts, 1)
   310  		require.Len(t, desc.Layouts[0].QuantitiesByGroup, 1)
   311  		require.Equal(t, map[string]struct{}{
   312  			peerIdentityString("p12"): {},
   313  		}, extractPeers(desc))
   314  	})
   315  
   316  	t.Run("Chaincode2Chaincode I", func(t *testing.T) {
   317  		// Scenario IX: A chaincode-to-chaincode query is made.
   318  		// Total organizations are 0, 2, 4, 6, 10, 12
   319  		// and the endorsement policies of the chaincodes are as follows:
   320  		// cc1: OR(AND(0, 2), AND(6, 10))
   321  		// cc2: AND(6, 10, 12)
   322  		// cc3: AND(4, 12)
   323  		// Therefore, the result should be: 4, 6, 10, 12
   324  
   325  		chanPeers := peerSet{}
   326  		for _, id := range []int{0, 2, 4, 6, 10, 12} {
   327  			peer := newPeer(id).withChaincode("cc1", "1.0").withChaincode("cc2", "1.0").withChaincode("cc3", "1.0")
   328  			chanPeers = append(chanPeers, peer)
   329  		}
   330  
   331  		g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once()
   332  
   333  		mf := &metadataFetcher{}
   334  		mf.On("Metadata").Return(&chaincode.Metadata{
   335  			Name:    "cc1",
   336  			Version: "1.0",
   337  		}).Once()
   338  		mf.On("Metadata").Return(&chaincode.Metadata{
   339  			Name:    "cc2",
   340  			Version: "1.0",
   341  		}).Once()
   342  		mf.On("Metadata").Return(&chaincode.Metadata{
   343  			Name:    "cc3",
   344  			Version: "1.0",
   345  		}).Once()
   346  
   347  		pb := principalBuilder{}
   348  		cc1policy := pb.newSet().addPrincipal(peerRole("p0")).addPrincipal(peerRole("p2")).
   349  			newSet().addPrincipal(peerRole("p6")).addPrincipal(peerRole("p10")).buildPolicy()
   350  
   351  		pf.On("PoliciesByChaincode", "cc1").Return(cc1policy).Once()
   352  
   353  		cc2policy := pb.newSet().addPrincipal(peerRole("p6")).
   354  			addPrincipal(peerRole("p10")).addPrincipal(peerRole("p12")).buildPolicy()
   355  		pf.On("PoliciesByChaincode", "cc2").Return(cc2policy).Once()
   356  
   357  		cc3policy := pb.newSet().addPrincipal(peerRole("p4")).
   358  			addPrincipal(peerRole("p12")).buildPolicy()
   359  		pf.On("PoliciesByChaincode", "cc3").Return(cc3policy).Once()
   360  
   361  		analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf)
   362  		desc, err := analyzer.PeersForEndorsement(channel, &peer.ChaincodeInterest{
   363  			Chaincodes: []*peer.ChaincodeCall{
   364  				{
   365  					Name: "cc1",
   366  				},
   367  				{
   368  					Name: "cc2",
   369  				},
   370  				{
   371  					Name: "cc3",
   372  				},
   373  			},
   374  		})
   375  		require.NoError(t, err)
   376  		require.NotNil(t, desc)
   377  		require.Len(t, desc.Layouts, 1)
   378  		require.Len(t, desc.Layouts[0].QuantitiesByGroup, 4)
   379  		require.Equal(t, map[string]struct{}{
   380  			peerIdentityString("p4"):  {},
   381  			peerIdentityString("p6"):  {},
   382  			peerIdentityString("p10"): {},
   383  			peerIdentityString("p12"): {},
   384  		}, extractPeers(desc))
   385  	})
   386  
   387  	t.Run("Chaincode2Chaincode II", func(t *testing.T) {
   388  		// Scenario X: A chaincode-to-chaincode query is made.
   389  		// and the endorsement policies of the chaincodes are as follows:
   390  		// cc1: OR(0, 1)
   391  		// cc2: AND(0, 1)
   392  		// Therefore, the result should be: (0, 1)
   393  
   394  		cc1 := "cc1"
   395  		cc2 := "cc2"
   396  		chanPeers := peerSet{
   397  			newPeer(0).withChaincode(cc1, "1.0").withChaincode(cc2, "1.0"),
   398  			newPeer(1).withChaincode(cc1, "1.0").withChaincode(cc2, "1.0"),
   399  		}.toMembers()
   400  
   401  		alivePeers := peerSet{
   402  			newPeer(0),
   403  			newPeer(1),
   404  		}.toMembers()
   405  
   406  		g := &gossipMock{}
   407  		g.On("Peers").Return(alivePeers)
   408  		g.On("IdentityInfo").Return(identities)
   409  		g.On("PeersOfChannel").Return(chanPeers).Once()
   410  
   411  		mf := &metadataFetcher{}
   412  		mf.On("Metadata").Return(&chaincode.Metadata{
   413  			Name:    "cc1",
   414  			Version: "1.0",
   415  		})
   416  		mf.On("Metadata").Return(&chaincode.Metadata{
   417  			Name:    "cc2",
   418  			Version: "1.0",
   419  		})
   420  
   421  		pb := principalBuilder{}
   422  		cc1policy := pb.newSet().addPrincipal(peerRole("p0")).
   423  			newSet().addPrincipal(peerRole("p1")).buildPolicy()
   424  		pf.On("PoliciesByChaincode", "cc1").Return(cc1policy).Once()
   425  
   426  		cc2policy := pb.newSet().addPrincipal(peerRole("p0")).
   427  			addPrincipal(peerRole("p1")).buildPolicy()
   428  		pf.On("PoliciesByChaincode", "cc2").Return(cc2policy).Once()
   429  
   430  		analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf)
   431  		desc, err := analyzer.PeersForEndorsement(channel, &peer.ChaincodeInterest{
   432  			Chaincodes: []*peer.ChaincodeCall{
   433  				{
   434  					Name: "cc1",
   435  				},
   436  				{
   437  					Name: "cc2",
   438  				},
   439  			},
   440  		})
   441  		require.NoError(t, err)
   442  		require.NotNil(t, desc)
   443  		require.Len(t, desc.Layouts, 1)
   444  		require.Len(t, desc.Layouts[0].QuantitiesByGroup, 2)
   445  		require.Equal(t, map[string]struct{}{
   446  			peerIdentityString("p0"): {},
   447  			peerIdentityString("p1"): {},
   448  		}, extractPeers(desc))
   449  	})
   450  
   451  	t.Run("Collection specific EP", func(t *testing.T) {
   452  		// Scenario XI: Policy is found and there are enough peers to satisfy
   453  		// 2 principal combinations: p0 and p6, or p12 alone.
   454  		// The collection has p0, p6, and p12 in it.
   455  		// The chaincode EP is (p0 and p6) or p12.
   456  		// However, the the chaincode has a collection level EP that requires p6 and p12.
   457  		// Thus, the only combination that can satisfy would be p6 and p12.
   458  		collectionOrgs := []*msp.MSPPrincipal{
   459  			peerRole("p0"),
   460  			peerRole("p6"),
   461  			peerRole("p12"),
   462  		}
   463  		col2principals := map[string][]*msp.MSPPrincipal{
   464  			"collection": collectionOrgs,
   465  		}
   466  
   467  		mf := &metadataFetcher{}
   468  		mf.On("Metadata").Return(&chaincode.Metadata{
   469  			Name:              cc,
   470  			Version:           "1.0",
   471  			CollectionsConfig: buildCollectionConfig(col2principals),
   472  		}).Once()
   473  		pb := principalBuilder{}
   474  		chaincodeEP := pb.newSet().addPrincipal(peerRole("p0")).
   475  			addPrincipal(peerRole("p6")).newSet().
   476  			addPrincipal(peerRole("p12")).buildPolicy()
   477  		collectionEP := pb.newSet().addPrincipal(peerRole("p6")).
   478  			addPrincipal(peerRole("p12")).buildPolicy()
   479  		g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once()
   480  		pf := &policyFetcherMock{}
   481  		pf.On("PoliciesByChaincode", cc).Return([]policies.InquireablePolicy{chaincodeEP, collectionEP}).Once()
   482  		analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf)
   483  		desc, err := analyzer.PeersForEndorsement(channel, &peer.ChaincodeInterest{
   484  			Chaincodes: []*peer.ChaincodeCall{
   485  				{
   486  					Name:            cc,
   487  					CollectionNames: []string{"collection"},
   488  				},
   489  			},
   490  		})
   491  		require.NoError(t, err)
   492  		require.NotNil(t, desc)
   493  		require.Len(t, desc.Layouts, 1)
   494  		require.Len(t, desc.Layouts[0].QuantitiesByGroup, 2)
   495  		require.Equal(t, map[string]struct{}{
   496  			peerIdentityString("p6"):  {},
   497  			peerIdentityString("p12"): {},
   498  		}, extractPeers(desc))
   499  	})
   500  
   501  	t.Run("Private data blind write", func(t *testing.T) {
   502  		// Scenario XII: The collection has only p0 in it
   503  		// The chaincode EP is p6 or p0.
   504  		// The collection endorsement policy is p0 and p6.
   505  		// However p6 is not in the collection at all (only p0),
   506  		// so it doesn't have the pre-images.
   507  		// To that end, the client indicates that it's a blind write
   508  		// by turning on the "noPrivateRead" field in the request.
   509  		// This might seem like a pathological case, but it's
   510  		// effective because it is in the intersection of
   511  		// several use cases.
   512  
   513  		collectionOrgs := []*msp.MSPPrincipal{
   514  			peerRole("p0"),
   515  		}
   516  		col2principals := map[string][]*msp.MSPPrincipal{
   517  			"collection": collectionOrgs,
   518  		}
   519  
   520  		mf := &metadataFetcher{}
   521  		mf.On("Metadata").Return(&chaincode.Metadata{
   522  			Name:              cc,
   523  			Version:           "1.0",
   524  			CollectionsConfig: buildCollectionConfig(col2principals),
   525  		}).Once()
   526  		pb := principalBuilder{}
   527  		chaincodeEP := pb.newSet().addPrincipal(peerRole("p0")).newSet(). // p0 or p6
   528  											addPrincipal(peerRole("p6")).buildPolicy()
   529  		collectionEP := pb.newSet().addPrincipal(peerRole("p0")). // p0 and p6
   530  										addPrincipal(peerRole("p6")).buildPolicy()
   531  		g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once()
   532  		pf := &policyFetcherMock{}
   533  		pf.On("PoliciesByChaincode", cc).Return([]policies.InquireablePolicy{chaincodeEP, collectionEP}).Once()
   534  		analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf)
   535  		desc, err := analyzer.PeersForEndorsement(channel, &peer.ChaincodeInterest{
   536  			Chaincodes: []*peer.ChaincodeCall{
   537  				{
   538  					Name:            cc,
   539  					CollectionNames: []string{"collection"},
   540  					NoPrivateReads:  true, // This means a blind write
   541  				},
   542  			},
   543  		})
   544  		require.NoError(t, err)
   545  		require.NotNil(t, desc)
   546  		require.Len(t, desc.Layouts, 1)
   547  		require.Len(t, desc.Layouts[0].QuantitiesByGroup, 2)
   548  		require.Equal(t, map[string]struct{}{
   549  			peerIdentityString("p0"): {},
   550  			peerIdentityString("p6"): {},
   551  		}, extractPeers(desc))
   552  	})
   553  
   554  	t.Run("Chaincode call with state based endorsement policy I", func(t *testing.T) {
   555  		// Scenario XIII: A chaincode call with a state based endorsement policy
   556  		// Total organizations are 0, 2, 4, 6, 10, 12
   557  		// and the endorsement policies of the chaincode is:
   558  		// cc1: OR(AND(0, 2), AND(6, 10))
   559  		// However the chaincode call is accompanied with a hint
   560  		// for a state based endorsement policy for organization 10
   561  		// Therefore, the result should be: 6, 10
   562  
   563  		chanPeers := peerSet{}
   564  		for _, id := range []int{0, 2, 4, 6, 10, 12} {
   565  			peer := newPeer(id).withChaincode("cc1", "1.0")
   566  			chanPeers = append(chanPeers, peer)
   567  		}
   568  
   569  		g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once()
   570  
   571  		mf := &metadataFetcher{}
   572  		mf.On("Metadata").Return(&chaincode.Metadata{
   573  			Name:    "cc1",
   574  			Version: "1.0",
   575  		}).Once()
   576  
   577  		pb := principalBuilder{}
   578  		cc1policy := pb.newSet().addPrincipal(peerRole("p0")).addPrincipal(peerRole("p2")).
   579  			newSet().addPrincipal(peerRole("p6")).addPrincipal(peerRole("p10")).buildPolicy()
   580  
   581  		pf.On("PoliciesByChaincode", "cc1").Return(cc1policy).Once()
   582  
   583  		analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf)
   584  		desc, err := analyzer.PeersForEndorsement(channel, &peer.ChaincodeInterest{
   585  			Chaincodes: []*peer.ChaincodeCall{
   586  				{
   587  					Name: "cc1",
   588  					KeyPolicies: []*common2.SignaturePolicyEnvelope{
   589  						{
   590  							Identities: []*msp.MSPPrincipal{peerRole("p10")},
   591  							Rule:       policydsl.SignedBy(0),
   592  						},
   593  					},
   594  				},
   595  			},
   596  		})
   597  		require.NoError(t, err)
   598  		require.NotNil(t, desc)
   599  		require.Len(t, desc.Layouts, 1)
   600  		require.Len(t, desc.Layouts[0].QuantitiesByGroup, 2)
   601  		require.Equal(t, map[string]struct{}{
   602  			peerIdentityString("p6"):  {},
   603  			peerIdentityString("p10"): {},
   604  		}, extractPeers(desc))
   605  	})
   606  
   607  	t.Run("Chaincode call with state based endorsement policy II", func(t *testing.T) {
   608  		// Scenario XIV: A chaincode call with a state based endorsement policy
   609  		// Total organizations are 0, 2, 4, 6, 10, 12
   610  		// and the endorsement policies of the chaincode is:
   611  		// cc1: OR(AND(0, 2), AND(6, 10))
   612  		// However the chaincode call is accompanied with a hint
   613  		// for a state based endorsement policy for organization 12
   614  		// Therefore, the result should be: {0, 2, 12} or {6, 10, 12}
   615  
   616  		chanPeers := peerSet{}
   617  		for _, id := range []int{0, 2, 4, 6, 10, 12} {
   618  			peer := newPeer(id).withChaincode("cc1", "1.0")
   619  			chanPeers = append(chanPeers, peer)
   620  		}
   621  
   622  		g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once()
   623  
   624  		mf := &metadataFetcher{}
   625  		mf.On("Metadata").Return(&chaincode.Metadata{
   626  			Name:    "cc1",
   627  			Version: "1.0",
   628  		}).Once()
   629  
   630  		pb := principalBuilder{}
   631  		cc1policy := pb.newSet().addPrincipal(peerRole("p0")).addPrincipal(peerRole("p2")).
   632  			newSet().addPrincipal(peerRole("p6")).addPrincipal(peerRole("p10")).buildPolicy()
   633  
   634  		pf.On("PoliciesByChaincode", "cc1").Return(cc1policy).Once()
   635  
   636  		analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf)
   637  		desc, err := analyzer.PeersForEndorsement(channel, &peer.ChaincodeInterest{
   638  			Chaincodes: []*peer.ChaincodeCall{
   639  				{
   640  					Name: "cc1",
   641  					KeyPolicies: []*common2.SignaturePolicyEnvelope{
   642  						{
   643  							Identities: []*msp.MSPPrincipal{peerRole("p12")},
   644  							Rule:       policydsl.SignedBy(0),
   645  						},
   646  					},
   647  				},
   648  			},
   649  		})
   650  		require.NoError(t, err)
   651  		require.NotNil(t, desc)
   652  		require.Len(t, desc.Layouts, 2)
   653  		require.Len(t, desc.Layouts[0].QuantitiesByGroup, 3)
   654  		require.Len(t, desc.Layouts[1].QuantitiesByGroup, 3)
   655  		require.Equal(t, map[string]struct{}{
   656  			peerIdentityString("p0"):  {},
   657  			peerIdentityString("p2"):  {},
   658  			peerIdentityString("p6"):  {},
   659  			peerIdentityString("p10"): {},
   660  			peerIdentityString("p12"): {},
   661  		}, extractPeers(desc))
   662  		// Find ID of org 12
   663  
   664  		// Ensure org 12 (and no other org) is found in both layouts
   665  		var intersectionSize int
   666  		for g1 := range desc.Layouts[0].QuantitiesByGroup {
   667  			for g2 := range desc.Layouts[1].QuantitiesByGroup {
   668  				if g1 == g2 {
   669  					require.Equal(t, intersectionSize, 0)
   670  					intersectionSize++
   671  					require.Equal(t, peerIdentityString("p12"), string(desc.EndorsersByGroups[g1].Peers[0].Identity))
   672  				}
   673  			}
   674  		}
   675  	})
   676  
   677  	t.Run("Chaincode call with state based endorsement policy III", func(t *testing.T) {
   678  		// Scenario XV: A chaincode call with a state based endorsement policy
   679  		// Total organizations are 0, 2, 4, 6, 10, 12
   680  		// and the endorsement policies of the chaincode is:
   681  		// cc1: OR(AND(0, 2), AND(6, 10))
   682  		// However the chaincode call is accompanied with a hint
   683  		// for a state based endorsement policy for both organizations 2 and 6
   684  		// Therefore, the result should be: {0, 2, 6} or {2, 6, 10}
   685  
   686  		chanPeers := peerSet{}
   687  		for _, id := range []int{0, 2, 4, 6, 10, 12} {
   688  			peer := newPeer(id).withChaincode("cc1", "1.0")
   689  			chanPeers = append(chanPeers, peer)
   690  		}
   691  
   692  		g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once()
   693  
   694  		mf := &metadataFetcher{}
   695  		mf.On("Metadata").Return(&chaincode.Metadata{
   696  			Name:    "cc1",
   697  			Version: "1.0",
   698  		}).Once()
   699  
   700  		pb := principalBuilder{}
   701  		cc1policy := pb.newSet().addPrincipal(peerRole("p0")).addPrincipal(peerRole("p2")).
   702  			newSet().addPrincipal(peerRole("p6")).addPrincipal(peerRole("p10")).buildPolicy()
   703  
   704  		pf.On("PoliciesByChaincode", "cc1").Return(cc1policy).Once()
   705  
   706  		analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf)
   707  		desc, err := analyzer.PeersForEndorsement(channel, &peer.ChaincodeInterest{
   708  			Chaincodes: []*peer.ChaincodeCall{
   709  				{
   710  					Name: "cc1",
   711  					KeyPolicies: []*common2.SignaturePolicyEnvelope{
   712  						{
   713  							Identities: []*msp.MSPPrincipal{peerRole("p2")},
   714  							Rule:       policydsl.SignedBy(0),
   715  						},
   716  						{
   717  							Identities: []*msp.MSPPrincipal{peerRole("p6")},
   718  							Rule:       policydsl.SignedBy(0),
   719  						},
   720  					},
   721  				},
   722  			},
   723  		})
   724  		require.NoError(t, err)
   725  		require.NotNil(t, desc)
   726  		require.Len(t, desc.Layouts, 2)
   727  		require.Len(t, desc.Layouts[0].QuantitiesByGroup, 3)
   728  		require.Equal(t, map[string]struct{}{
   729  			peerIdentityString("p0"):  {},
   730  			peerIdentityString("p2"):  {},
   731  			peerIdentityString("p6"):  {},
   732  			peerIdentityString("p10"): {},
   733  		}, extractPeers(desc))
   734  
   735  		// Ensure orgs 2, 6 are found in both layouts
   736  		intersection := make(map[string]struct{})
   737  		for g1 := range desc.Layouts[0].QuantitiesByGroup {
   738  			for g2 := range desc.Layouts[1].QuantitiesByGroup {
   739  				if g1 == g2 {
   740  					intersection[string(desc.EndorsersByGroups[g1].Peers[0].Identity)] = struct{}{}
   741  				}
   742  			}
   743  		}
   744  
   745  		require.Equal(t, map[string]struct{}{
   746  			peerIdentityString("p2"): {},
   747  			peerIdentityString("p6"): {},
   748  		}, intersection)
   749  	})
   750  
   751  	t.Run("Chaincode call with DisregardNamespacePolicy set but no key policies or collection policies present", func(t *testing.T) {
   752  		// Scenario XVI: A chaincode call with DisregardNamespacePolicy set
   753  		// Total organizations are 0, 2, 4, 6, 10, 12
   754  		// and there is a collection specified by the client but no collection policies exist
   755  		// We expect an error because since DisregardNamespacePolicy is specified, and no collection policies are defined,
   756  		// there is not a single endorsement policy to compute.
   757  
   758  		chanPeers := peerSet{}
   759  		for _, id := range []int{0, 2, 4, 6, 10, 12} {
   760  			peer := newPeer(id).withChaincode("cc1", "1.0")
   761  			chanPeers = append(chanPeers, peer)
   762  		}
   763  
   764  		g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once()
   765  
   766  		mf := &metadataFetcher{}
   767  		mf.On("Metadata").Return(&chaincode.Metadata{
   768  			Name:    "cc1",
   769  			Version: "1.0",
   770  		}).Once()
   771  
   772  		pb := principalBuilder{}
   773  		cc1policy := pb.newSet().addPrincipal(peerRole("p0")).addPrincipal(peerRole("p2")).
   774  			newSet().addPrincipal(peerRole("p6")).addPrincipal(peerRole("p10")).buildPolicy()
   775  
   776  		pf.On("PoliciesByChaincode", "cc1").Return(cc1policy).Once()
   777  
   778  		analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf)
   779  		desc, err := analyzer.PeersForEndorsement(channel, &peer.ChaincodeInterest{
   780  			Chaincodes: []*peer.ChaincodeCall{
   781  				{
   782  					Name:                     "cc1",
   783  					DisregardNamespacePolicy: true,
   784  				},
   785  			},
   786  		})
   787  		require.EqualError(t, err, "requested to disregard chaincode cc1's policy but key and collection policies are missing,"+
   788  			" either disable DisregardNamespacePolicy or specify at least one key policy or at least one collection policy")
   789  		require.Nil(t, desc)
   790  	})
   791  
   792  	t.Run("Chaincode call with state based endorsement policy and no chaincode namespace policy", func(t *testing.T) {
   793  		// Scenario XVII: A chaincode call with a state based endorsement policy and DisregardNamespacePolicy set
   794  		// Total organizations are 0, 2, 4, 6, 10, 12
   795  		// and the endorsement policies of the chaincode is:
   796  		// cc1: OR(AND(0, 2), AND(6, 10))
   797  		// However the chaincode call is accompanied with a hint
   798  		// for a state based endorsement policy for both organizations 2 and 6
   799  		// Therefore, the result should be: {2, 6}
   800  
   801  		chanPeers := peerSet{}
   802  		for _, id := range []int{0, 2, 4, 6, 10, 12} {
   803  			peer := newPeer(id).withChaincode("cc1", "1.0")
   804  			chanPeers = append(chanPeers, peer)
   805  		}
   806  
   807  		g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once()
   808  
   809  		mf := &metadataFetcher{}
   810  		mf.On("Metadata").Return(&chaincode.Metadata{
   811  			Name:    "cc1",
   812  			Version: "1.0",
   813  		}).Once()
   814  
   815  		pb := principalBuilder{}
   816  		cc1policy := pb.newSet().addPrincipal(peerRole("p0")).addPrincipal(peerRole("p2")).
   817  			newSet().addPrincipal(peerRole("p6")).addPrincipal(peerRole("p10")).buildPolicy()
   818  
   819  		pf.On("PoliciesByChaincode", "cc1").Return(cc1policy).Once()
   820  
   821  		analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf)
   822  		desc, err := analyzer.PeersForEndorsement(channel, &peer.ChaincodeInterest{
   823  			Chaincodes: []*peer.ChaincodeCall{
   824  				{
   825  					Name:                     "cc1",
   826  					DisregardNamespacePolicy: true,
   827  					KeyPolicies: []*common2.SignaturePolicyEnvelope{
   828  						{
   829  							Identities: []*msp.MSPPrincipal{peerRole("p2")},
   830  							Rule:       policydsl.SignedBy(0),
   831  						},
   832  						{
   833  							Identities: []*msp.MSPPrincipal{peerRole("p6")},
   834  							Rule:       policydsl.SignedBy(0),
   835  						},
   836  					},
   837  				},
   838  			},
   839  		})
   840  		require.NoError(t, err)
   841  		require.NotNil(t, desc)
   842  		require.Len(t, desc.Layouts, 1)
   843  		require.Len(t, desc.Layouts[0].QuantitiesByGroup, 2)
   844  		require.Equal(t, map[string]struct{}{
   845  			peerIdentityString("p2"): {},
   846  			peerIdentityString("p6"): {},
   847  		}, extractPeers(desc))
   848  	})
   849  
   850  	t.Run("Chaincode call with collection endorsement policy and no namespace endorsement policy", func(t *testing.T) {
   851  		// Scenario XVIII: A chaincode call with collection endorsement policy and DisregardNamespacePolicy set
   852  		// The chaincode EP is OR(AND(0, 2), AND(6, 10))
   853  		// The collection endorsement policy is p0 and p2.
   854  		// Additionally, the client sets DisregardNamespacePolicy which makes
   855  		// discovery only use the collection policy and not the namespace policy.
   856  
   857  		chanPeers := peerSet{}
   858  		for _, id := range []int{0, 2, 4, 6, 10, 12} {
   859  			peer := newPeer(id).withChaincode("cc1", "1.0")
   860  			chanPeers = append(chanPeers, peer)
   861  		}
   862  
   863  		g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once()
   864  
   865  		collectionOrgs := []*msp.MSPPrincipal{
   866  			peerRole("p0"),
   867  			peerRole("p2"),
   868  		}
   869  		col2principals := map[string][]*msp.MSPPrincipal{
   870  			"collection": collectionOrgs,
   871  		}
   872  		mf := &metadataFetcher{}
   873  		mf.On("Metadata").Return(&chaincode.Metadata{
   874  			Name:              "cc1",
   875  			Version:           "1.0",
   876  			CollectionsConfig: buildCollectionConfig(col2principals),
   877  		}).Once()
   878  
   879  		pb := principalBuilder{}
   880  		cc1policy := pb.newSet().addPrincipal(peerRole("p0")).addPrincipal(peerRole("p2")).
   881  			newSet().addPrincipal(peerRole("p6")).addPrincipal(peerRole("p10")).buildPolicy()
   882  
   883  		collectionEP := pb.newSet().addPrincipal(peerRole("p0")). // p0 and p6
   884  										addPrincipal(peerRole("p2")).buildPolicy()
   885  
   886  		pf.On("PoliciesByChaincode", "cc1").Return([]policies.InquireablePolicy{cc1policy, collectionEP}).Once()
   887  
   888  		analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf)
   889  		desc, err := analyzer.PeersForEndorsement(channel, &peer.ChaincodeInterest{
   890  			Chaincodes: []*peer.ChaincodeCall{
   891  				{
   892  					Name:                     "cc1",
   893  					DisregardNamespacePolicy: true,
   894  					CollectionNames:          []string{"collection"},
   895  				},
   896  			},
   897  		})
   898  		require.NoError(t, err)
   899  		require.NotNil(t, desc)
   900  		require.Len(t, desc.Layouts, 1)
   901  		require.Len(t, desc.Layouts[0].QuantitiesByGroup, 2)
   902  		require.Equal(t, map[string]struct{}{
   903  			peerIdentityString("p0"): {},
   904  			peerIdentityString("p2"): {},
   905  		}, extractPeers(desc))
   906  	})
   907  
   908  	t.Run("Identity based endorsement policy doesn't interfere with discovery", func(t *testing.T) {
   909  		// Scenario XIX: Policy is based on either identities or on organizations
   910  		// 2 principal combinations:
   911  		// p0 and p6 (organizations), or p7 (identity)
   912  
   913  		pb := principalBuilder{}
   914  		policy := pb.newSet().addPrincipal(peerRole("p0")).addPrincipal(peerRole("p6")).
   915  			newSet().addPrincipal(identity("p7")).buildPolicy()
   916  		g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once()
   917  		mf := &metadataFetcher{}
   918  		mf.On("Metadata").Return(&chaincode.Metadata{
   919  			Name:    cc,
   920  			Version: "1.0",
   921  		}).Once()
   922  		analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf)
   923  
   924  		pf.On("PoliciesByChaincode", cc).Return(policy).Once()
   925  		desc, err := analyzer.PeersForEndorsement(channel, &peer.ChaincodeInterest{
   926  			Chaincodes: []*peer.ChaincodeCall{
   927  				{
   928  					Name: cc,
   929  				},
   930  			},
   931  		})
   932  		require.NoError(t, err)
   933  		require.NotNil(t, desc)
   934  		require.Len(t, desc.Layouts, 1)
   935  		require.Len(t, desc.Layouts[0].QuantitiesByGroup, 2)
   936  		require.Equal(t, map[string]struct{}{
   937  			peerIdentityString("p0"): {},
   938  			peerIdentityString("p6"): {},
   939  		}, extractPeers(desc))
   940  	})
   941  }
   942  
   943  func TestPeersAuthorizedByCriteria(t *testing.T) {
   944  	cc1 := "cc1"
   945  	cc2 := "cc2"
   946  	members := peerSet{
   947  		newPeer(0).withChaincode(cc1, "1.0"),
   948  		newPeer(3).withChaincode(cc1, "1.0"),
   949  		newPeer(6).withChaincode(cc1, "1.0"),
   950  		newPeer(9).withChaincode(cc1, "1.0"),
   951  		newPeer(12).withChaincode(cc1, "1.0"),
   952  	}.toMembers()
   953  
   954  	members2 := append(discovery.Members{}, members...)
   955  	members2 = append(members2, peerSet{newPeer(13).withChaincode(cc1, "1.1").withChaincode(cc2, "1.0")}.toMembers()...)
   956  	members2 = append(members2, peerSet{newPeer(14).withChaincode(cc1, "1.1")}.toMembers()...)
   957  	members2 = append(members2, peerSet{newPeer(15).withChaincode(cc2, "1.0")}.toMembers()...)
   958  
   959  	alivePeers := peerSet{
   960  		newPeer(0),
   961  		newPeer(2),
   962  		newPeer(4),
   963  		newPeer(6),
   964  		newPeer(8),
   965  		newPeer(10),
   966  		newPeer(11),
   967  		newPeer(12),
   968  		newPeer(13),
   969  		newPeer(14),
   970  		newPeer(15),
   971  	}.toMembers()
   972  
   973  	identities := identitySet(pkiID2MSPID)
   974  
   975  	for _, tst := range []struct {
   976  		name                 string
   977  		arguments            *peer.ChaincodeInterest
   978  		totalExistingMembers discovery.Members
   979  		metadata             []*chaincode.Metadata
   980  		expected             discovery.Members
   981  	}{
   982  		{
   983  			name:                 "Nil interest",
   984  			arguments:            nil,
   985  			totalExistingMembers: members,
   986  			expected:             members,
   987  		},
   988  		{
   989  			name:                 "Empty interest invocation chain",
   990  			arguments:            &peer.ChaincodeInterest{},
   991  			totalExistingMembers: members,
   992  			expected:             members,
   993  		},
   994  		{
   995  			name: "Chaincodes only installed on some peers",
   996  			arguments: &peer.ChaincodeInterest{
   997  				Chaincodes: []*peer.ChaincodeCall{
   998  					{Name: cc1},
   999  					{Name: cc2},
  1000  				},
  1001  			},
  1002  			totalExistingMembers: members2,
  1003  			metadata: []*chaincode.Metadata{
  1004  				{
  1005  					Name:    "cc1",
  1006  					Version: "1.1",
  1007  				},
  1008  				{
  1009  					Name:    "cc2",
  1010  					Version: "1.0",
  1011  				},
  1012  			},
  1013  			expected: peerSet{newPeer(13).withChaincode(cc1, "1.1").withChaincode(cc2, "1.0")}.toMembers(),
  1014  		},
  1015  		{
  1016  			name: "Only some peers authorized by collection",
  1017  			arguments: &peer.ChaincodeInterest{
  1018  				Chaincodes: []*peer.ChaincodeCall{
  1019  					{Name: cc1, CollectionNames: []string{"collection"}},
  1020  				},
  1021  			},
  1022  			totalExistingMembers: members,
  1023  			metadata: []*chaincode.Metadata{
  1024  				{
  1025  					Name:    cc1,
  1026  					Version: "1.0",
  1027  					CollectionsConfig: buildCollectionConfig(map[string][]*msp.MSPPrincipal{
  1028  						"collection": {
  1029  							peerRole("p0"),
  1030  							peerRole("p12"),
  1031  						},
  1032  					}),
  1033  				},
  1034  				{
  1035  					Name:    cc1,
  1036  					Version: "1.0",
  1037  					CollectionsConfig: buildCollectionConfig(map[string][]*msp.MSPPrincipal{
  1038  						"collection": {
  1039  							peerRole("p3"),
  1040  							peerRole("p9"),
  1041  						},
  1042  					}),
  1043  				},
  1044  			},
  1045  			expected: peerSet{
  1046  				newPeer(0).withChaincode(cc1, "1.0"),
  1047  				newPeer(12).withChaincode(cc1, "1.0"),
  1048  			}.toMembers(),
  1049  		},
  1050  	} {
  1051  		t.Run(tst.name, func(t *testing.T) {
  1052  			g := &gossipMock{}
  1053  			pf := &policyFetcherMock{}
  1054  			mf := &metadataFetcher{}
  1055  			g.On("Peers").Return(alivePeers)
  1056  			g.On("IdentityInfo").Return(identities)
  1057  			g.On("PeersOfChannel").Return(tst.totalExistingMembers).Once()
  1058  			for _, md := range tst.metadata {
  1059  				mf.On("Metadata").Return(md).Once()
  1060  			}
  1061  
  1062  			analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf)
  1063  			actualMembers, err := analyzer.PeersAuthorizedByCriteria(common.ChannelID("mychannel"), tst.arguments)
  1064  			require.NoError(t, err)
  1065  			require.Equal(t, tst.expected, actualMembers)
  1066  		})
  1067  	}
  1068  }
  1069  
  1070  func TestPop(t *testing.T) {
  1071  	slice := []inquire.ComparablePrincipalSets{{}, {}}
  1072  	require.Len(t, slice, 2)
  1073  	_, slice, err := popComparablePrincipalSets(slice)
  1074  	require.NoError(t, err)
  1075  	require.Len(t, slice, 1)
  1076  	_, slice, err = popComparablePrincipalSets(slice)
  1077  	require.NoError(t, err)
  1078  	require.Len(t, slice, 0)
  1079  	_, _, err = popComparablePrincipalSets(slice)
  1080  	require.Error(t, err)
  1081  	require.Equal(t, "no principal sets remained after filtering", err.Error())
  1082  }
  1083  
  1084  func TestMergePrincipalSetsNilInput(t *testing.T) {
  1085  	_, err := mergePrincipalSets(nil)
  1086  	require.Error(t, err)
  1087  	require.Equal(t, "no principal sets remained after filtering", err.Error())
  1088  }
  1089  
  1090  func TestComputePrincipalSetsNoPolicies(t *testing.T) {
  1091  	// Tests a hypothetical case where no chaincodes populate the chaincode interest.
  1092  
  1093  	interest := &peer.ChaincodeInterest{
  1094  		Chaincodes: []*peer.ChaincodeCall{},
  1095  	}
  1096  	ea := &endorsementAnalyzer{}
  1097  	_, err := ea.computePrincipalSets(common.ChannelID("mychannel"), interest)
  1098  	require.Error(t, err)
  1099  	require.Contains(t, err.Error(), "no principal sets remained after filtering")
  1100  }
  1101  
  1102  func TestLoadMetadataAndFiltersCollectionNotPresentInConfig(t *testing.T) {
  1103  	interest := &peer.ChaincodeInterest{
  1104  		Chaincodes: []*peer.ChaincodeCall{
  1105  			{
  1106  				Name:            "mycc",
  1107  				CollectionNames: []string{"bar"},
  1108  			},
  1109  		},
  1110  	}
  1111  
  1112  	org1AndOrg2 := []*msp.MSPPrincipal{
  1113  		orgPrincipal("Org1MSP"),
  1114  		orgPrincipal("Org2MSP"),
  1115  	}
  1116  	col2principals := map[string][]*msp.MSPPrincipal{
  1117  		"foo": org1AndOrg2,
  1118  	}
  1119  	config := buildCollectionConfig(col2principals)
  1120  
  1121  	mdf := &metadataFetcher{}
  1122  	mdf.On("Metadata").Return(&chaincode.Metadata{
  1123  		Name:              "mycc",
  1124  		CollectionsConfig: config,
  1125  		Policy:            []byte{1, 2, 3},
  1126  	})
  1127  
  1128  	_, err := loadMetadataAndFilters(metadataAndFilterContext{
  1129  		identityInfoByID: nil,
  1130  		evaluator:        nil,
  1131  		chainID:          common.ChannelID("mychannel"),
  1132  		fetch:            mdf,
  1133  		interest:         interest,
  1134  	})
  1135  
  1136  	require.Equal(t, "collection bar doesn't exist in collection config for chaincode mycc", err.Error())
  1137  }
  1138  
  1139  func TestLoadMetadataAndFiltersInvalidCollectionData(t *testing.T) {
  1140  	interest := &peer.ChaincodeInterest{
  1141  		Chaincodes: []*peer.ChaincodeCall{
  1142  			{
  1143  				Name:            "mycc",
  1144  				CollectionNames: []string{"col1"},
  1145  			},
  1146  		},
  1147  	}
  1148  	mdf := &metadataFetcher{}
  1149  	mdf.On("Metadata").Return(&chaincode.Metadata{
  1150  		Name:              "mycc",
  1151  		CollectionsConfig: &peer.CollectionConfigPackage{},
  1152  		Policy:            []byte{1, 2, 3},
  1153  	})
  1154  
  1155  	_, err := loadMetadataAndFilters(metadataAndFilterContext{
  1156  		identityInfoByID: nil,
  1157  		evaluator:        nil,
  1158  		chainID:          common.ChannelID("mychannel"),
  1159  		fetch:            mdf,
  1160  		interest:         interest,
  1161  	})
  1162  	require.Error(t, err)
  1163  	require.Contains(t, err.Error(), "collection col1 doesn't exist in collection config for chaincode mycc")
  1164  }
  1165  
  1166  type peerSet []*peerInfo
  1167  
  1168  func (p peerSet) toMembers() discovery.Members {
  1169  	var members discovery.Members
  1170  	for _, peer := range p {
  1171  		members = append(members, peer.NetworkMember)
  1172  	}
  1173  	return members
  1174  }
  1175  
  1176  func identitySet(pkiID2MSPID map[string]string) api.PeerIdentitySet {
  1177  	var res api.PeerIdentitySet
  1178  	for pkiID, mspID := range pkiID2MSPID {
  1179  		sID := &msp.SerializedIdentity{
  1180  			Mspid:   pkiID2MSPID[pkiID],
  1181  			IdBytes: []byte(pkiID),
  1182  		}
  1183  		res = append(res, api.PeerIdentityInfo{
  1184  			Identity:     api.PeerIdentityType(protoutil.MarshalOrPanic(sID)),
  1185  			PKIId:        common.PKIidType(pkiID),
  1186  			Organization: api.OrgIdentityType(mspID),
  1187  		})
  1188  	}
  1189  	return res
  1190  }
  1191  
  1192  type peerInfo struct {
  1193  	identity api.PeerIdentityType
  1194  	pkiID    common.PKIidType
  1195  	discovery.NetworkMember
  1196  }
  1197  
  1198  func peerIdentityString(id string) string {
  1199  	return string(protoutil.MarshalOrPanic(&msp.SerializedIdentity{
  1200  		Mspid:   pkiID2MSPID[id],
  1201  		IdBytes: []byte(id),
  1202  	}))
  1203  }
  1204  
  1205  func newPeer(i int) *peerInfo {
  1206  	p := fmt.Sprintf("p%d", i)
  1207  	identity := protoutil.MarshalOrPanic(&msp.SerializedIdentity{
  1208  		Mspid:   pkiID2MSPID[p],
  1209  		IdBytes: []byte(p),
  1210  	})
  1211  	return &peerInfo{
  1212  		pkiID:    common.PKIidType(p),
  1213  		identity: api.PeerIdentityType(identity),
  1214  		NetworkMember: discovery.NetworkMember{
  1215  			PKIid:            common.PKIidType(p),
  1216  			Endpoint:         p,
  1217  			InternalEndpoint: p,
  1218  			Envelope: &gossip.Envelope{
  1219  				Payload: []byte(identity),
  1220  			},
  1221  		},
  1222  	}
  1223  }
  1224  
  1225  func peerRole(pkiID string) *msp.MSPPrincipal {
  1226  	return &msp.MSPPrincipal{
  1227  		PrincipalClassification: msp.MSPPrincipal_ROLE,
  1228  		Principal: protoutil.MarshalOrPanic(&msp.MSPRole{
  1229  			MspIdentifier: pkiID2MSPID[pkiID],
  1230  			Role:          msp.MSPRole_PEER,
  1231  		}),
  1232  	}
  1233  }
  1234  
  1235  func identity(pkiID string) *msp.MSPPrincipal {
  1236  	return &msp.MSPPrincipal{
  1237  		PrincipalClassification: msp.MSPPrincipal_IDENTITY,
  1238  		Principal: protoutil.MarshalOrPanic(&msp.SerializedIdentity{
  1239  			Mspid:   pkiID2MSPID[pkiID],
  1240  			IdBytes: []byte(pkiID),
  1241  		}),
  1242  	}
  1243  }
  1244  
  1245  func (pi *peerInfo) withChaincode(name, version string) *peerInfo {
  1246  	if pi.Properties == nil {
  1247  		pi.Properties = &gossip.Properties{}
  1248  	}
  1249  	pi.Properties.Chaincodes = append(pi.Properties.Chaincodes, &gossip.Chaincode{
  1250  		Name:    name,
  1251  		Version: version,
  1252  	})
  1253  	return pi
  1254  }
  1255  
  1256  type gossipMock struct {
  1257  	mock.Mock
  1258  }
  1259  
  1260  func (g *gossipMock) IdentityInfo() api.PeerIdentitySet {
  1261  	return g.Called().Get(0).(api.PeerIdentitySet)
  1262  }
  1263  
  1264  func (g *gossipMock) PeersOfChannel(_ common.ChannelID) discovery.Members {
  1265  	members := g.Called().Get(0)
  1266  	return members.(discovery.Members)
  1267  }
  1268  
  1269  func (g *gossipMock) Peers() discovery.Members {
  1270  	members := g.Called().Get(0)
  1271  	return members.(discovery.Members)
  1272  }
  1273  
  1274  type policyFetcherMock struct {
  1275  	mock.Mock
  1276  }
  1277  
  1278  func (pf *policyFetcherMock) PoliciesByChaincode(channel string, chaincode string, collections ...string) []policies.InquireablePolicy {
  1279  	arg := pf.Called(chaincode)
  1280  	if arg.Get(0) == nil {
  1281  		return nil
  1282  	}
  1283  
  1284  	singlePolicy, isSinglePolicy := arg.Get(0).(policies.InquireablePolicy)
  1285  	if isSinglePolicy {
  1286  		return []policies.InquireablePolicy{singlePolicy}
  1287  	}
  1288  
  1289  	return arg.Get(0).([]policies.InquireablePolicy)
  1290  }
  1291  
  1292  type principalBuilder struct {
  1293  	ip inquireablePolicy
  1294  }
  1295  
  1296  func (pb *principalBuilder) buildPolicy() inquireablePolicy {
  1297  	defer func() {
  1298  		pb.ip = nil
  1299  	}()
  1300  	return pb.ip
  1301  }
  1302  
  1303  func (pb *principalBuilder) newSet() *principalBuilder {
  1304  	pb.ip = append(pb.ip, make(policies.PrincipalSet, 0))
  1305  	return pb
  1306  }
  1307  
  1308  func (pb *principalBuilder) addPrincipal(principal *msp.MSPPrincipal) *principalBuilder {
  1309  	pb.ip[len(pb.ip)-1] = append(pb.ip[len(pb.ip)-1], principal)
  1310  	return pb
  1311  }
  1312  
  1313  type inquireablePolicy []policies.PrincipalSet
  1314  
  1315  func (ip inquireablePolicy) SatisfiedBy() []policies.PrincipalSet {
  1316  	return ip
  1317  }
  1318  
  1319  type principalEvaluatorMock struct{}
  1320  
  1321  func (pe *principalEvaluatorMock) SatisfiesPrincipal(_ string, identity []byte, principal *msp.MSPPrincipal) error {
  1322  	sId := &msp.SerializedIdentity{}
  1323  	if err := proto.Unmarshal(identity, sId); err != nil {
  1324  		return err
  1325  	}
  1326  	if principal.PrincipalClassification == msp.MSPPrincipal_IDENTITY {
  1327  		identityPrincipal := &msp.SerializedIdentity{}
  1328  		if err := proto.Unmarshal(principal.Principal, identityPrincipal); err != nil {
  1329  			return err
  1330  		}
  1331  		if proto.Equal(sId, identityPrincipal) {
  1332  			return nil
  1333  		}
  1334  		return fmt.Errorf("identities do not match")
  1335  	}
  1336  	// Else, it's either an OU type or a role type, so we only classify by MSP ID
  1337  	peerRole := &msp.MSPRole{}
  1338  	if err := proto.Unmarshal(principal.Principal, peerRole); err != nil {
  1339  		return err
  1340  	}
  1341  	if peerRole.MspIdentifier == sId.Mspid {
  1342  		return nil
  1343  	}
  1344  	return errors.New("bingo")
  1345  }
  1346  
  1347  type metadataFetcher struct {
  1348  	mock.Mock
  1349  }
  1350  
  1351  func (mf *metadataFetcher) Metadata(channel string, cc string, _ ...string) *chaincode.Metadata {
  1352  	arg := mf.Called().Get(0)
  1353  	if arg == nil {
  1354  		return nil
  1355  	}
  1356  	return arg.(*chaincode.Metadata)
  1357  }