Skip to content

Commit

Permalink
[FAB-17279] Discovery supports collection level endorsement policies (#…
Browse files Browse the repository at this point in the history
…445)

This change set adapts discovery to take into account collection level
endorsement policies.

Since discovery supports multiple endorsement policies in a single invocation
chain already (to support chaincode to chaincode writes), I changed the
method that retrieves a single endorsement policy by chaincode namespace
to retrieve a slice of several endorsement policies as well as extended the
parameter to take into account the collections of the said chaincode.

However, this change set has no effect on production since the discovery backend
needs to change in order to actually fetch the collection specific endorsement policies
if collections are specified.

Change-Id: Id93911c9560a9db8f896a06f71c2ac07e801784a
Signed-off-by: yacovm <yacovm@il.ibm.com>
  • Loading branch information
yacovm authored and C0rWin committed Jan 2, 2020
1 parent 65f8a01 commit 2a39779
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 28 deletions.
10 changes: 5 additions & 5 deletions discovery/client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ func createDiscoveryService(sup *mockSupport) discovery.DiscoveryServer {
Id: []byte{1, 2, 3},
})

pf.On("PolicyByChaincode", "mycc").Return(&inquireablePolicy{
pf.On("PoliciesByChaincode", "mycc").Return(&inquireablePolicy{
orgCombinations: orgCombinationsThatSatisfyPolicy,
})

Expand All @@ -342,7 +342,7 @@ func createDiscoveryService(sup *mockSupport) discovery.DiscoveryServer {
}),
})

pf.On("PolicyByChaincode", "mycc2").Return(&inquireablePolicy{
pf.On("PoliciesByChaincode", "mycc2").Return(&inquireablePolicy{
orgCombinations: orgCombinationsThatSatisfyPolicy2,
})

Expand All @@ -355,7 +355,7 @@ func createDiscoveryService(sup *mockSupport) discovery.DiscoveryServer {
Id: []byte{1, 2, 3},
})

pf.On("PolicyByChaincode", "mycc3").Return(&inquireablePolicy{
pf.On("PoliciesByChaincode", "mycc3").Return(&inquireablePolicy{
orgCombinations: [][]string{{"A", "B", "C", "D"}},
})

Expand Down Expand Up @@ -820,8 +820,8 @@ type policyFetcher struct {
mock.Mock
}

func (pf *policyFetcher) PolicyByChaincode(channel string, cc string) policies.InquireablePolicy {
return pf.Called(cc).Get(0).(policies.InquireablePolicy)
func (pf *policyFetcher) PoliciesByChaincode(channel string, cc string, collections ...string) []policies.InquireablePolicy {
return []policies.InquireablePolicy{pf.Called(cc).Get(0).(policies.InquireablePolicy)}
}

type endorsementAnalyzer interface {
Expand Down
11 changes: 7 additions & 4 deletions discovery/endorsement/endorsement.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ type chaincodeMetadataFetcher interface {
type policyFetcher interface {
// PolicyByChaincode returns a policy that can be inquired which identities
// satisfy it
PolicyByChaincode(channel string, cc string) policies.InquireablePolicy
PoliciesByChaincode(channel string, cc string, collections ...string) []policies.InquireablePolicy
}

type gossipSupport interface {
Expand Down Expand Up @@ -170,12 +170,15 @@ func (ea *endorsementAnalyzer) computeEndorsementResponse(ctx *context) (*discov
func (ea *endorsementAnalyzer) computePrincipalSets(channelID common.ChannelID, interest *discovery.ChaincodeInterest) (policies.PrincipalSets, error) {
var inquireablePolicies []policies.InquireablePolicy
for _, chaincode := range interest.Chaincodes {
pol := ea.PolicyByChaincode(string(channelID), chaincode.Name)
if pol == nil {
policies := ea.PoliciesByChaincode(string(channelID), chaincode.Name, chaincode.CollectionNames...)
if len(policies) == 0 {
logger.Debug("Policy for chaincode '", chaincode, "'doesn't exist")
return nil, errors.New("policy not found")
}
inquireablePolicies = append(inquireablePolicies, pol)

for _, pol := range policies {
inquireablePolicies = append(inquireablePolicies, pol)
}
}

var cpss []inquire.ComparablePrincipalSets
Expand Down
96 changes: 80 additions & 16 deletions discovery/endorsement/endorsement_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ func TestPeersForEndorsement(t *testing.T) {
return res
}
cc := "chaincode"
mf := &metadataFetcher{}
g := &gossipMock{}
pf := &policyFetcherMock{}
ccWithMissingPolicy := "chaincodeWithMissingPolicy"
Expand Down Expand Up @@ -90,8 +89,9 @@ func TestPeersForEndorsement(t *testing.T) {

// Scenario I: Policy isn't found
t.Run("PolicyNotFound", func(t *testing.T) {
pf.On("PolicyByChaincode", ccWithMissingPolicy).Return(nil).Once()
pf.On("PoliciesByChaincode", ccWithMissingPolicy).Return(nil).Once()
g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once()
mf := &metadataFetcher{}
mf.On("Metadata").Return(&chaincode.Metadata{
Name: cc,
Version: "1.0",
Expand All @@ -117,9 +117,10 @@ func TestPeersForEndorsement(t *testing.T) {
policy := pb.newSet().addPrincipal(peerRole("p1")).addPrincipal(peerRole("p6")).
newSet().addPrincipal(peerRole("p11")).addPrincipal(peerRole("p11")).buildPolicy()
g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once()
mf := &metadataFetcher{}
mf.On("Metadata").Return(&chaincode.Metadata{Name: cc, Version: "1.0"}).Once()
analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf)
pf.On("PolicyByChaincode", cc).Return(policy).Once()
pf.On("PoliciesByChaincode", cc).Return(policy).Once()
desc, err := analyzer.PeersForEndorsement(channel, &discoveryprotos.ChaincodeInterest{
Chaincodes: []*discoveryprotos.ChaincodeCall{
{
Expand All @@ -140,12 +141,13 @@ func TestPeersForEndorsement(t *testing.T) {
policy := pb.newSet().addPrincipal(peerRole("p0")).addPrincipal(peerRole("p6")).
newSet().addPrincipal(peerRole("p10")).addPrincipal(peerRole("p12")).buildPolicy()
g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once()
mf := &metadataFetcher{}
mf.On("Metadata").Return(&chaincode.Metadata{
Name: cc,
Version: "1.0",
}).Once()
analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf)
pf.On("PolicyByChaincode", cc).Return(policy).Once()
pf.On("PoliciesByChaincode", cc).Return(policy).Once()
desc, err := analyzer.PeersForEndorsement(channel, &discoveryprotos.ChaincodeInterest{
Chaincodes: []*discoveryprotos.ChaincodeCall{
{
Expand All @@ -172,12 +174,13 @@ func TestPeersForEndorsement(t *testing.T) {
policy := pb.newSet().addPrincipal(peerRole("p0")).addPrincipal(peerRole("p6")).
newSet().addPrincipal(peerRole("p12")).buildPolicy()
g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once()
mf := &metadataFetcher{}
mf.On("Metadata").Return(&chaincode.Metadata{
Name: cc,
Version: "1.0",
}).Once()
analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf)
pf.On("PolicyByChaincode", cc).Return(policy).Once()
pf.On("PoliciesByChaincode", cc).Return(policy).Once()
desc, err := analyzer.PeersForEndorsement(channel, &discoveryprotos.ChaincodeInterest{
Chaincodes: []*discoveryprotos.ChaincodeCall{
{
Expand All @@ -200,6 +203,7 @@ func TestPeersForEndorsement(t *testing.T) {
t.Run("WrongVersionInstalled", func(t *testing.T) {
// Scenario V: Policy is found, and there are enough peers to satisfy policy combinations,
// but all peers have the wrong version installed on them.
mf := &metadataFetcher{}
mf.On("Metadata").Return(&chaincode.Metadata{
Name: cc,
Version: "1.1",
Expand All @@ -208,7 +212,7 @@ func TestPeersForEndorsement(t *testing.T) {
policy := pb.newSet().addPrincipal(peerRole("p0")).addPrincipal(peerRole("p6")).
newSet().addPrincipal(peerRole("p12")).buildPolicy()
g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once()
pf.On("PolicyByChaincode", cc).Return(policy).Once()
pf.On("PoliciesByChaincode", cc).Return(policy).Once()
analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf)
desc, err := analyzer.PeersForEndorsement(channel, &discoveryprotos.ChaincodeInterest{
Chaincodes: []*discoveryprotos.ChaincodeCall{
Expand All @@ -230,7 +234,7 @@ func TestPeersForEndorsement(t *testing.T) {
newPeer(12),
}
g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once()
pf.On("PolicyByChaincode", cc).Return(policy).Once()
pf.On("PoliciesByChaincode", cc).Return(policy).Once()
mf.On("Metadata").Return(&chaincode.Metadata{
Name: cc,
Version: "1.0",
Expand All @@ -253,7 +257,8 @@ func TestPeersForEndorsement(t *testing.T) {
policy := pb.newSet().addPrincipal(peerRole("p0")).addPrincipal(peerRole("p6")).
newSet().addPrincipal(peerRole("p12")).buildPolicy()
g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once()
pf.On("PolicyByChaincode", cc).Return(policy).Once()
pf.On("PoliciesByChaincode", cc).Return(policy).Once()
mf := &metadataFetcher{}
mf.On("Metadata").Return(nil).Once()
analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf)
desc, err := analyzer.PeersForEndorsement(channel, &discoveryprotos.ChaincodeInterest{
Expand All @@ -279,6 +284,7 @@ func TestPeersForEndorsement(t *testing.T) {
col2principals := map[string][]*msp.MSPPrincipal{
"collection": collectionOrgs,
}
mf := &metadataFetcher{}
mf.On("Metadata").Return(&chaincode.Metadata{
Name: cc,
Version: "1.0",
Expand All @@ -289,7 +295,7 @@ func TestPeersForEndorsement(t *testing.T) {
addPrincipal(peerRole("p6")).newSet().
addPrincipal(peerRole("p12")).buildPolicy()
g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once()
pf.On("PolicyByChaincode", cc).Return(policy).Once()
pf.On("PoliciesByChaincode", cc).Return(policy).Once()
analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf)
desc, err := analyzer.PeersForEndorsement(channel, &discoveryprotos.ChaincodeInterest{
Chaincodes: []*discoveryprotos.ChaincodeCall{
Expand Down Expand Up @@ -325,6 +331,7 @@ func TestPeersForEndorsement(t *testing.T) {

g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once()

mf := &metadataFetcher{}
mf.On("Metadata").Return(&chaincode.Metadata{
Name: "cc1",
Version: "1.0",
Expand All @@ -342,15 +349,15 @@ func TestPeersForEndorsement(t *testing.T) {
cc1policy := pb.newSet().addPrincipal(peerRole("p0")).addPrincipal(peerRole("p2")).
newSet().addPrincipal(peerRole("p6")).addPrincipal(peerRole("p10")).buildPolicy()

pf.On("PolicyByChaincode", "cc1").Return(cc1policy).Once()
pf.On("PoliciesByChaincode", "cc1").Return(cc1policy).Once()

cc2policy := pb.newSet().addPrincipal(peerRole("p6")).
addPrincipal(peerRole("p10")).addPrincipal(peerRole("p12")).buildPolicy()
pf.On("PolicyByChaincode", "cc2").Return(cc2policy).Once()
pf.On("PoliciesByChaincode", "cc2").Return(cc2policy).Once()

cc3policy := pb.newSet().addPrincipal(peerRole("p4")).
addPrincipal(peerRole("p12")).buildPolicy()
pf.On("PolicyByChaincode", "cc3").Return(cc3policy).Once()
pf.On("PoliciesByChaincode", "cc3").Return(cc3policy).Once()

analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf)
desc, err := analyzer.PeersForEndorsement(channel, &discoveryprotos.ChaincodeInterest{
Expand Down Expand Up @@ -402,6 +409,7 @@ func TestPeersForEndorsement(t *testing.T) {
g.On("IdentityInfo").Return(identities)
g.On("PeersOfChannel").Return(chanPeers).Once()

mf := &metadataFetcher{}
mf.On("Metadata").Return(&chaincode.Metadata{
Name: "cc1",
Version: "1.0",
Expand All @@ -414,11 +422,11 @@ func TestPeersForEndorsement(t *testing.T) {
pb := principalBuilder{}
cc1policy := pb.newSet().addPrincipal(peerRole("p0")).
newSet().addPrincipal(peerRole("p1")).buildPolicy()
pf.On("PolicyByChaincode", "cc1").Return(cc1policy).Once()
pf.On("PoliciesByChaincode", "cc1").Return(cc1policy).Once()

cc2policy := pb.newSet().addPrincipal(peerRole("p0")).
addPrincipal(peerRole("p1")).buildPolicy()
pf.On("PolicyByChaincode", "cc2").Return(cc2policy).Once()
pf.On("PoliciesByChaincode", "cc2").Return(cc2policy).Once()

analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf)
desc, err := analyzer.PeersForEndorsement(channel, &discoveryprotos.ChaincodeInterest{
Expand All @@ -440,6 +448,56 @@ func TestPeersForEndorsement(t *testing.T) {
peerIdentityString("p1"): {},
}, extractPeers(desc))
})

t.Run("Collection specific EP", func(t *testing.T) {
// Scenario XI: Policy is found and there are enough peers to satisfy
// 2 principal combinations: p0 and p6, or p12 alone.
// The collection has p0, p6, and p12 in it.
// The chaincode EP is (p0 and p6) or p12.
// However, the the chaincode has a collection level EP that requires p6 and p12.
// Thus, the only combination that can satisfy would be p6 and p12.
collectionOrgs := []*msp.MSPPrincipal{
peerRole("p0"),
peerRole("p6"),
peerRole("p12"),
}
col2principals := map[string][]*msp.MSPPrincipal{
"collection": collectionOrgs,
}

mf := &metadataFetcher{}
mf.On("Metadata").Return(&chaincode.Metadata{
Name: cc,
Version: "1.0",
CollectionsConfig: buildCollectionConfig(col2principals),
}).Once()
pb := principalBuilder{}
chaincodeEP := pb.newSet().addPrincipal(peerRole("p0")).
addPrincipal(peerRole("p6")).newSet().
addPrincipal(peerRole("p12")).buildPolicy()
collectionEP := pb.newSet().addPrincipal(peerRole("p6")).
addPrincipal(peerRole("p12")).buildPolicy()
g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once()
pf := &policyFetcherMock{}
pf.On("PoliciesByChaincode", cc).Return([]policies.InquireablePolicy{chaincodeEP, collectionEP}).Once()
analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf)
desc, err := analyzer.PeersForEndorsement(channel, &discoveryprotos.ChaincodeInterest{
Chaincodes: []*discoveryprotos.ChaincodeCall{
{
Name: cc,
CollectionNames: []string{"collection"},
},
},
})
assert.NoError(t, err)
assert.NotNil(t, desc)
assert.Len(t, desc.Layouts, 1)
assert.Len(t, desc.Layouts[0].QuantitiesByGroup, 2)
assert.Equal(t, map[string]struct{}{
peerIdentityString("p6"): {},
peerIdentityString("p12"): {},
}, extractPeers(desc))
})
}

func TestPeersAuthorizedByCriteria(t *testing.T) {
Expand Down Expand Up @@ -765,12 +823,18 @@ type policyFetcherMock struct {
mock.Mock
}

func (pf *policyFetcherMock) PolicyByChaincode(channel string, chaincode string) policies.InquireablePolicy {
func (pf *policyFetcherMock) PoliciesByChaincode(channel string, chaincode string, collections ...string) []policies.InquireablePolicy {
arg := pf.Called(chaincode)
if arg.Get(0) == nil {
return nil
}
return arg.Get(0).(policies.InquireablePolicy)

singlePolicy, isSinglePolicy := arg.Get(0).(policies.InquireablePolicy)
if isSinglePolicy {
return []policies.InquireablePolicy{singlePolicy}
}

return arg.Get(0).([]policies.InquireablePolicy)
}

type principalBuilder struct {
Expand Down
4 changes: 2 additions & 2 deletions discovery/support/chaincode/support.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func NewDiscoverySupport(ci MetadataRetriever) *DiscoverySupport {
return s
}

func (s *DiscoverySupport) PolicyByChaincode(channel string, cc string) policies.InquireablePolicy {
func (s *DiscoverySupport) PoliciesByChaincode(channel string, cc string, _ ...string) []policies.InquireablePolicy {
chaincodeData := s.ci.Metadata(channel, cc, false)
if chaincodeData == nil {
logger.Info("Chaincode", cc, "wasn't found")
Expand All @@ -50,5 +50,5 @@ func (s *DiscoverySupport) PolicyByChaincode(channel string, cc string) policies
logger.Warningf("Invalid policy, either Identities(%v) or Rule(%v) are empty:", pol.Identities, pol.Rule)
return nil
}
return inquire.NewInquireableSignaturePolicy(pol)
return []policies.InquireablePolicy{inquire.NewInquireableSignaturePolicy(pol)}
}
2 changes: 1 addition & 1 deletion discovery/support/chaincode/support_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func TestSupport(t *testing.T) {
test := test
t.Run(test.name, func(t *testing.T) {
sup := NewDiscoverySupport(&mockMetadataRetriever{res: test.input})
res := sup.PolicyByChaincode("", "")
res := sup.PoliciesByChaincode("", "")
if test.shouldBeNil {
assert.Nil(t, res)
} else {
Expand Down

0 comments on commit 2a39779

Please sign in to comment.