Skip to content

Commit

Permalink
[FAB-18527] Discovery supports DisregardNamespacePolicy hint from cli…
Browse files Browse the repository at this point in the history
…ent (#2768)

This commit adds support for a boolean flag in the chaincode call: DisregardNamespacePolicy
Its role is to hint to discovery that the client wishes to ask about endorsement policies
of either state based endorsement policies of given keys, or of collection endorsement policies
that exist for given collection names.

In case no state based endorsement policies are given in the bespoken chaincode call,
and also no collection level endorsement policies of the given collection names are found,
specifying the DisregardNamespacePolicy hint for that chaincode call will result in an error,
as there is no endorsement policy to compute on.

Change-Id: I299afd40063a0cf1cee34d6034eaa02fe6fc6707
Signed-off-by: Yacov Manevich <yacovm@il.ibm.com>
  • Loading branch information
yacovm authored Jul 21, 2021
1 parent 9a922fd commit e5e623d
Show file tree
Hide file tree
Showing 6 changed files with 229 additions and 45 deletions.
17 changes: 17 additions & 0 deletions discovery/endorsement/endorsement.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,23 @@ func (ea *endorsementAnalyzer) computePrincipalSets(channelID common.ChannelID,
sessionLogger.Debug("Policy for chaincode '", chaincode, "'doesn't exist")
return nil, errors.New("policy not found")
}
if chaincode.DisregardNamespacePolicy && len(chaincode.KeyPolicies) == 0 && len(policies) == 1 {
sessionLogger.Warnf("Client requested to disregard chaincode %s's policy, but it did not specify any "+
"collection policies or key policies. This is probably a bug in the client side code, as the client should"+
"either not specify DisregardNamespacePolicy, or specify at least one key policy or at least one collection policy", chaincode.Name)
return nil, errors.Errorf("requested to disregard chaincode %s's policy but key and collection policies are missing, either "+
"disable DisregardNamespacePolicy or specify at least one key policy or at least one collection policy", chaincode.Name)
}
if chaincode.DisregardNamespacePolicy {
if len(policies) == 1 {
sessionLogger.Debugf("Client requested to disregard the namespace policy for chaincode %s,"+
" and no collection policies are present", chaincode.Name)
continue
}
sessionLogger.Debugf("Client requested to disregard the namespace policy for chaincode %s,"+
" however there exist %d collection policies taken into account", chaincode.Name, len(policies)-1)
policies = policies[1:]
}
inquireablePoliciesForChaincodeAndCollections = append(inquireablePoliciesForChaincodeAndCollections, policies...)
}

Expand Down
157 changes: 157 additions & 0 deletions discovery/endorsement/endorsement_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -751,6 +751,163 @@ func TestPeersForEndorsement(t *testing.T) {
peerIdentityString("p6"): {},
}, intersection)
})

t.Run("Chaincode call with DisregardNamespacePolicy set but no key policies or collection policies present", func(t *testing.T) {
// Scenario XVI: A chaincode call with DisregardNamespacePolicy set
// Total organizations are 0, 2, 4, 6, 10, 12
// and there is a collection specified by the client but no collection policies exist
// We expect an error because since DisregardNamespacePolicy is specified, and no collection policies are defined,
// there is not a single endorsement policy to compute.

chanPeers := peerSet{}
for _, id := range []int{0, 2, 4, 6, 10, 12} {
peer := newPeer(id).withChaincode("cc1", "1.0")
chanPeers = append(chanPeers, peer)
}

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

mf := &metadataFetcher{}
mf.On("Metadata").Return(&chaincode.Metadata{
Name: "cc1",
Version: "1.0",
}).Once()

pb := principalBuilder{}
cc1policy := pb.newSet().addPrincipal(peerRole("p0")).addPrincipal(peerRole("p2")).
newSet().addPrincipal(peerRole("p6")).addPrincipal(peerRole("p10")).buildPolicy()

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

analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf)
desc, err := analyzer.PeersForEndorsement(channel, &peer.ChaincodeInterest{
Chaincodes: []*peer.ChaincodeCall{
{
Name: "cc1",
DisregardNamespacePolicy: true,
},
},
})
require.EqualError(t, err, "requested to disregard chaincode cc1's policy but key and collection policies are missing,"+
" either disable DisregardNamespacePolicy or specify at least one key policy or at least one collection policy")
require.Nil(t, desc)
})

t.Run("Chaincode call with state based endorsement policy and no chaincode namespace policy", func(t *testing.T) {
// Scenario XVII: A chaincode call with a state based endorsement policy and DisregardNamespacePolicy set
// Total organizations are 0, 2, 4, 6, 10, 12
// and the endorsement policies of the chaincode is:
// cc1: OR(AND(0, 2), AND(6, 10))
// However the chaincode call is accompanied with a hint
// for a state based endorsement policy for both organizations 2 and 6
// Therefore, the result should be: {2, 6}

chanPeers := peerSet{}
for _, id := range []int{0, 2, 4, 6, 10, 12} {
peer := newPeer(id).withChaincode("cc1", "1.0")
chanPeers = append(chanPeers, peer)
}

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

mf := &metadataFetcher{}
mf.On("Metadata").Return(&chaincode.Metadata{
Name: "cc1",
Version: "1.0",
}).Once()

pb := principalBuilder{}
cc1policy := pb.newSet().addPrincipal(peerRole("p0")).addPrincipal(peerRole("p2")).
newSet().addPrincipal(peerRole("p6")).addPrincipal(peerRole("p10")).buildPolicy()

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

analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf)
desc, err := analyzer.PeersForEndorsement(channel, &peer.ChaincodeInterest{
Chaincodes: []*peer.ChaincodeCall{
{
Name: "cc1",
DisregardNamespacePolicy: true,
KeyPolicies: []*common2.SignaturePolicyEnvelope{
{
Identities: []*msp.MSPPrincipal{peerRole("p2")},
Rule: policydsl.SignedBy(0),
},
{
Identities: []*msp.MSPPrincipal{peerRole("p6")},
Rule: policydsl.SignedBy(0),
},
},
},
},
})
require.NoError(t, err)
require.NotNil(t, desc)
require.Len(t, desc.Layouts, 1)
require.Len(t, desc.Layouts[0].QuantitiesByGroup, 2)
require.Equal(t, map[string]struct{}{
peerIdentityString("p2"): {},
peerIdentityString("p6"): {},
}, extractPeers(desc))
})

t.Run("Chaincode call with collection endorsement policy and no namespace endorsement policy", func(t *testing.T) {
// Scenario XVIII: A chaincode call with collection endorsement policy and DisregardNamespacePolicy set
// The chaincode EP is OR(AND(0, 2), AND(6, 10))
// The collection endorsement policy is p0 and p2.
// Additionally, the client sets DisregardNamespacePolicy which makes
// discovery only use the collection policy and not the namespace policy.

chanPeers := peerSet{}
for _, id := range []int{0, 2, 4, 6, 10, 12} {
peer := newPeer(id).withChaincode("cc1", "1.0")
chanPeers = append(chanPeers, peer)
}

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

collectionOrgs := []*msp.MSPPrincipal{
peerRole("p0"),
peerRole("p2"),
}
col2principals := map[string][]*msp.MSPPrincipal{
"collection": collectionOrgs,
}
mf := &metadataFetcher{}
mf.On("Metadata").Return(&chaincode.Metadata{
Name: "cc1",
Version: "1.0",
CollectionsConfig: buildCollectionConfig(col2principals),
}).Once()

pb := principalBuilder{}
cc1policy := pb.newSet().addPrincipal(peerRole("p0")).addPrincipal(peerRole("p2")).
newSet().addPrincipal(peerRole("p6")).addPrincipal(peerRole("p10")).buildPolicy()

collectionEP := pb.newSet().addPrincipal(peerRole("p0")). // p0 and p6
addPrincipal(peerRole("p2")).buildPolicy()

pf.On("PoliciesByChaincode", "cc1").Return([]policies.InquireablePolicy{cc1policy, collectionEP}).Once()

analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf)
desc, err := analyzer.PeersForEndorsement(channel, &peer.ChaincodeInterest{
Chaincodes: []*peer.ChaincodeCall{
{
Name: "cc1",
DisregardNamespacePolicy: true,
CollectionNames: []string{"collection"},
},
},
})
require.NoError(t, err)
require.NotNil(t, desc)
require.Len(t, desc.Layouts, 1)
require.Len(t, desc.Layouts[0].QuantitiesByGroup, 2)
require.Equal(t, map[string]struct{}{
peerIdentityString("p0"): {},
peerIdentityString("p2"): {},
}, extractPeers(desc))
})
}

func TestPeersAuthorizedByCriteria(t *testing.T) {
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ require (
github.com/hyperledger/fabric-chaincode-go v0.0.0-20201119163726-f8ef75b17719
github.com/hyperledger/fabric-config v0.1.0
github.com/hyperledger/fabric-lib-go v1.0.0
github.com/hyperledger/fabric-protos-go v0.0.0-20210717172449-368ac8b7bc5b
github.com/hyperledger/fabric-protos-go v0.0.0-20210720123151-f0dc3e2a0871
github.com/kr/pretty v0.2.1
github.com/magiconair/properties v1.8.1 // indirect
github.com/mattn/go-runewidth v0.0.4 // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,8 @@ github.com/hyperledger/fabric-lib-go v1.0.0 h1:UL1w7c9LvHZUSkIvHTDGklxFv2kTeva1Q
github.com/hyperledger/fabric-lib-go v1.0.0/go.mod h1:H362nMlunurmHwkYqR5uHL2UDWbQdbfz74n8kbCFsqc=
github.com/hyperledger/fabric-protos-go v0.0.0-20190919234611-2a87503ac7c9/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
github.com/hyperledger/fabric-protos-go v0.0.0-20200424173316-dd554ba3746e/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
github.com/hyperledger/fabric-protos-go v0.0.0-20210717172449-368ac8b7bc5b h1:F3UmdNp9Hl6OCw3NwmEjrkJzG08X0+Xmt3NhmPnueE8=
github.com/hyperledger/fabric-protos-go v0.0.0-20210717172449-368ac8b7bc5b/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
github.com/hyperledger/fabric-protos-go v0.0.0-20210720123151-f0dc3e2a0871 h1:d7do07Q4LaOFAEWceRwUwVDdcfx3BdLeZYyUGtbHfRk=
github.com/hyperledger/fabric-protos-go v0.0.0-20210720123151-f0dc3e2a0871/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion vendor/modules.txt
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ github.com/hyperledger/fabric-config/protolator/protoext/peerext
# github.com/hyperledger/fabric-lib-go v1.0.0
## explicit
github.com/hyperledger/fabric-lib-go/healthz
# github.com/hyperledger/fabric-protos-go v0.0.0-20210717172449-368ac8b7bc5b
# github.com/hyperledger/fabric-protos-go v0.0.0-20210720123151-f0dc3e2a0871
## explicit
github.com/hyperledger/fabric-protos-go/common
github.com/hyperledger/fabric-protos-go/discovery
Expand Down

0 comments on commit e5e623d

Please sign in to comment.