Skip to content

Commit bec6f84

Browse files
committed
[FAB-9479] Filter out MSP IDs before computing layouts
This change set makes the endorsement analysis logic to filter out principal sets that have MSP IDs which don't have peers with the chaincode installed at all. This is needed for implementing support for cc2cc calls, because when computing cc2cc endorsement policies we compare principal sets, and merge principal sets such that one set contains the other. However- even though a policy might have an MSP ID in it, doesn't mean the peers of that MSP ID have the chaincode installed on them. Therefore, we need to prune these principal sets beforehand in order for them no to appear as input to the combined endorsement policy evaluation which would be implemented in future change sets. The change set also prepares the ground for cc2cc and collection support by changing the signature of PeersForEndorsement to have a chaincode interest which aggregate several chaincodes and collections. Change-Id: If382b4254797684ffef20e182ae744f5c5f3dfa7 Signed-off-by: yacovm <yacovm@il.ibm.com>
1 parent b528fe8 commit bec6f84

File tree

12 files changed

+300
-43
lines changed

12 files changed

+300
-43
lines changed

common/policies/policy.go

+26
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,32 @@ var logger = flogging.MustGetLogger("policies")
5656
// PrincipalSet is a collection of MSPPrincipals
5757
type PrincipalSet []*msp.MSPPrincipal
5858

59+
// PrincipalSets aggregates PrincipalSets
60+
type PrincipalSets []PrincipalSet
61+
62+
// ContainingOnly returns PrincipalSets that contain only principals of the given predicate
63+
func (psSets PrincipalSets) ContainingOnly(f func(*msp.MSPPrincipal) bool) PrincipalSets {
64+
var res PrincipalSets
65+
for _, set := range psSets {
66+
if !set.ContainingOnly(f) {
67+
continue
68+
}
69+
res = append(res, set)
70+
}
71+
return res
72+
}
73+
74+
// ContainingOnly returns whether the given PrincipalSet contains only Principals
75+
// that satisfy the given predicate
76+
func (ps PrincipalSet) ContainingOnly(f func(*msp.MSPPrincipal) bool) bool {
77+
for _, principal := range ps {
78+
if !f(principal) {
79+
return false
80+
}
81+
}
82+
return true
83+
}
84+
5985
// UniqueSet returns a histogram that is induced by the PrincipalSet
6086
func (ps PrincipalSet) UniqueSet() map[*msp.MSPPrincipal]int {
6187
// Create a histogram that holds the MSPPrincipals and counts them

common/policies/policy_test.go

+27
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import (
1414
"fmt"
1515
"reflect"
1616

17+
"strconv"
18+
1719
"github.com/golang/protobuf/proto"
1820
"github.com/hyperledger/fabric/protos/msp"
1921
logging "github.com/op/go-logging"
@@ -212,3 +214,28 @@ func TestPrincipalUniqueSet(t *testing.T) {
212214
// This is essential for 'UniqueSet' to work properly
213215
assert.Equal(t, 2, v.NumField())
214216
}
217+
218+
func TestPrincipalSetContainingOnly(t *testing.T) {
219+
var principalSets PrincipalSets
220+
var principalSet PrincipalSet
221+
for j := 0; j < 3; j++ {
222+
for i := 0; i < 10; i++ {
223+
principalSet = append(principalSet, &msp.MSPPrincipal{
224+
PrincipalClassification: msp.MSPPrincipal_IDENTITY,
225+
Principal: []byte(fmt.Sprintf("%d", j*10+i)),
226+
})
227+
}
228+
principalSets = append(principalSets, principalSet)
229+
principalSet = nil
230+
}
231+
232+
between20And30 := func(principal *msp.MSPPrincipal) bool {
233+
n, _ := strconv.ParseInt(string(principal.Principal), 10, 32)
234+
return n >= 20 && n <= 29
235+
}
236+
237+
principalSets = principalSets.ContainingOnly(between20And30)
238+
239+
assert.Len(t, principalSets, 1)
240+
assert.True(t, principalSets[0].ContainingOnly(between20And30))
241+
}

discovery/api.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ type GossipSupport interface {
4848
// for chaincodes
4949
type EndorsementSupport interface {
5050
// PeersForEndorsement returns an EndorsementDescriptor for a given set of peers, channel, and chaincode
51-
PeersForEndorsement(chaincode string, channel common.ChainID) (*discovery2.EndorsementDescriptor, error)
51+
PeersForEndorsement(channel common.ChainID, interest *discovery2.ChaincodeInterest) (*discovery2.EndorsementDescriptor, error)
5252
}
5353

5454
// ConfigSupport provides access to channel configuration

discovery/client/client_test.go

+9-3
Original file line numberDiff line numberDiff line change
@@ -577,6 +577,12 @@ func (mdf *ccMetadataFetcher) Metadata(channel string, cc string) *chaincode.Met
577577
type principalEvaluator struct {
578578
}
579579

580+
func (pe *principalEvaluator) MSPOfPrincipal(principal *msp.MSPPrincipal) string {
581+
sID := &msp.SerializedIdentity{}
582+
proto.Unmarshal(principal.Principal, sID)
583+
return sID.Mspid
584+
}
585+
580586
func (pe *principalEvaluator) SatisfiesPrincipal(channel string, identity []byte, principal *msp.MSPPrincipal) error {
581587
sID := &msp.SerializedIdentity{}
582588
proto.Unmarshal(identity, sID)
@@ -597,7 +603,7 @@ func (pf *policyFetcher) PolicyByChaincode(channel string, cc string) policies.I
597603
}
598604

599605
type endorsementAnalyzer interface {
600-
PeersForEndorsement(chaincode string, chainID gossipcommon.ChainID) (*discovery.EndorsementDescriptor, error)
606+
PeersForEndorsement(chainID gossipcommon.ChainID, interest *discovery.ChaincodeInterest) (*discovery.EndorsementDescriptor, error)
601607
}
602608

603609
type inquireablePolicy struct {
@@ -722,8 +728,8 @@ func (ms *mockSupport) Peers() discovery3.Members {
722728
return ms.Called().Get(0).(discovery3.Members)
723729
}
724730

725-
func (ms *mockSupport) PeersForEndorsement(chaincode string, channel gossipcommon.ChainID) (*discovery.EndorsementDescriptor, error) {
726-
return ms.endorsementAnalyzer.PeersForEndorsement(chaincode, channel)
731+
func (ms *mockSupport) PeersForEndorsement(channel gossipcommon.ChainID, interest *discovery.ChaincodeInterest) (*discovery.EndorsementDescriptor, error) {
732+
return ms.endorsementAnalyzer.PeersForEndorsement(channel, interest)
727733
}
728734

729735
func (*mockSupport) EligibleForService(channel string, data common.SignedData) error {

discovery/endorsement/endorsement.go

+49-18
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ type principalEvaluator interface {
2929
// SatisfiesPrincipal returns whether a given peer identity satisfies a certain principal
3030
// on a given channel
3131
SatisfiesPrincipal(channel string, identity []byte, principal *msp.MSPPrincipal) error
32+
33+
// MSPOfPrincipal returns the MSP ID of the given principal
34+
MSPOfPrincipal(principal *msp.MSPPrincipal) string
3235
}
3336

3437
type chaincodeMetadataFetcher interface {
@@ -75,27 +78,41 @@ func NewEndorsementAnalyzer(gs gossipSupport, pf policyFetcher, pe principalEval
7578
type peerPrincipalEvaluator func(member discovery2.NetworkMember, principal *msp.MSPPrincipal) bool
7679

7780
// PeersForEndorsement returns an EndorsementDescriptor for a given set of peers, channel, and chaincode
78-
func (ea *endorsementAnalyzer) PeersForEndorsement(chaincode string, chainID common.ChainID) (*discovery.EndorsementDescriptor, error) {
81+
func (ea *endorsementAnalyzer) PeersForEndorsement(chainID common.ChainID, interest *discovery.ChaincodeInterest) (*discovery.EndorsementDescriptor, error) {
82+
chaincode := interest.ChaincodeNames[0]
7983
ccMD := ea.Metadata(string(chainID), chaincode)
8084
if ccMD == nil {
8185
return nil, errors.Errorf("No metadata was found for chaincode %s in channel %s", chaincode, string(chainID))
8286
}
83-
aliveMembership := ea.Peers()
87+
// Filter out peers that don't have the chaincode installed on them
8488
chanMembership := ea.PeersOfChannel(chainID).Filter(peersWithChaincode(ccMD))
85-
aliveMembership = aliveMembership.Intersect(chanMembership)
86-
membersById := aliveMembership.ByID()
8789
channelMembersById := chanMembership.ByID()
88-
identitiesOfMembers := computeIdentitiesOfMembers(ea.IdentityInfo(), membersById)
90+
// Choose only the alive messages of those that have joined the channel
91+
aliveMembership := ea.Peers().Intersect(chanMembership)
92+
membersById := aliveMembership.ByID()
93+
// Compute a mapping between the PKI-IDs of members to their identities
94+
identities := ea.IdentityInfo()
95+
identitiesOfMembers := computeIdentitiesOfMembers(identities, membersById)
8996

97+
// Retrieve the policy for the chaincode
9098
pol := ea.PolicyByChaincode(string(chainID), chaincode)
9199
if pol == nil {
92100
logger.Debug("Policy for chaincode '", chaincode, "'doesn't exist")
93101
return nil, errors.New("policy not found")
94102
}
95103

96-
// SatisfiedBy computes combinations of principals that each combination, if satisfied-
97-
// satisfies the endorsement policy on its own.
98-
principalsSets := pol.SatisfiedBy()
104+
// Compute the combinations of principals (principal sets) that satisfy the endorsement policy
105+
principalsSets := policies.PrincipalSets(pol.SatisfiedBy())
106+
107+
// Obtain the MSP IDs of the members of the channel that are alive
108+
mspIDsOfChannelPeers := mspIDsOfMembers(membersById, identities.ByID())
109+
// Filter out principal sets that contain MSP IDs for peers that don't have the chaincode(s) installed
110+
principalsSets = principalsSets.ContainingOnly(func(principal *msp.MSPPrincipal) bool {
111+
mspID := ea.MSPOfPrincipal(principal)
112+
_, exists := mspIDsOfChannelPeers[mspID]
113+
return mspID != "" && exists
114+
})
115+
99116
// mapPrincipalsToGroups returns a mapping from principals to their corresponding groups.
100117
// groups are just human readable representations that mask the principals behind them
101118
principalGroups := mapPrincipalsToGroups(principalsSets)
@@ -105,16 +122,7 @@ func (ea *endorsementAnalyzer) PeersForEndorsement(chaincode string, chainID com
105122
satGraph := principalsToPeersGraph(principalAndPeerData{
106123
members: aliveMembership,
107124
pGrps: principalGroups,
108-
}, func(member discovery2.NetworkMember, principal *msp.MSPPrincipal) bool {
109-
err := ea.SatisfiesPrincipal(string(chainID), identitiesOfMembers.identityByPKIID(member.PKIid), principal)
110-
if err == nil {
111-
// TODO: log the principals in a human readable form
112-
logger.Debug(member, "satisfies principal", principal)
113-
return true
114-
}
115-
logger.Debug(member, "doesn't satisfy principal", principal, ":", err)
116-
return false
117-
})
125+
}, ea.satisfiesPrincipal(string(chainID), identitiesOfMembers))
118126

119127
layouts := computeLayouts(principalsSets, principalGroups, satGraph)
120128
if len(layouts) == 0 {
@@ -135,6 +143,19 @@ func (ea *endorsementAnalyzer) PeersForEndorsement(chaincode string, chainID com
135143
}, nil
136144
}
137145

146+
func (ea *endorsementAnalyzer) satisfiesPrincipal(channel string, identitiesOfMembers memberIdentities) peerPrincipalEvaluator {
147+
return func(member discovery2.NetworkMember, principal *msp.MSPPrincipal) bool {
148+
err := ea.SatisfiesPrincipal(channel, identitiesOfMembers.identityByPKIID(member.PKIid), principal)
149+
if err == nil {
150+
// TODO: log the principals in a human readable form
151+
logger.Debug(member, "satisfies principal", principal)
152+
return true
153+
}
154+
logger.Debug(member, "doesn't satisfy principal", principal, ":", err)
155+
return false
156+
}
157+
}
158+
138159
type peerMembershipCriteria struct {
139160
satGraph *principalPeerGraph
140161
idOfMembers memberIdentities
@@ -351,3 +372,13 @@ func peersWithChaincode(ccMD *chaincode.Metadata) func(member discovery2.Network
351372
return false
352373
}
353374
}
375+
376+
func mspIDsOfMembers(membersById map[string]discovery2.NetworkMember, identitiesByID map[string]api.PeerIdentityInfo) map[string]struct{} {
377+
res := make(map[string]struct{})
378+
for pkiID := range membersById {
379+
if identity, exists := identitiesByID[string(pkiID)]; exists {
380+
res[string(identity.Organization)] = struct{}{}
381+
}
382+
}
383+
return res
384+
}

discovery/endorsement/endorsement_test.go

+40-18
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,23 @@ func TestPeersForEndorsement(t *testing.T) {
5555
newPeer(12),
5656
}
5757

58-
identities := alivePeers.toIdentitySet()
58+
pkiID2MSPID := map[string]string{
59+
"p0": "Org1MSP",
60+
"p1": "Org1MSP",
61+
"p2": "Org1MSP",
62+
"p3": "Org1MSP",
63+
"p4": "Org1MSP",
64+
"p5": "Org2MSP",
65+
"p6": "Org2MSP",
66+
"p7": "Org2MSP",
67+
"p8": "Org2MSP",
68+
"p9": "Org2MSP",
69+
"p10": "Org2MSP",
70+
"p11": "Org2MSP",
71+
"p12": "Org2MSP",
72+
}
73+
74+
identities := identitySet(pkiID2MSPID)
5975

6076
chanPeers := peerSet{
6177
newPeer(0).withChaincode(cc, "1.0"),
@@ -71,7 +87,7 @@ func TestPeersForEndorsement(t *testing.T) {
7187
// Scenario I: Policy isn't found
7288
pf.On("PolicyByChaincode", ccWithMissingPolicy).Return(nil).Once()
7389
analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf)
74-
desc, err := analyzer.PeersForEndorsement(ccWithMissingPolicy, channel)
90+
desc, err := analyzer.PeersForEndorsement(channel, &discovery2.ChaincodeInterest{ChaincodeNames: []string{ccWithMissingPolicy}})
7591
assert.Nil(t, desc)
7692
assert.Equal(t, "policy not found", err.Error())
7793

@@ -88,9 +104,9 @@ func TestPeersForEndorsement(t *testing.T) {
88104
Principal: []byte("p11"),
89105
}).buildPolicy()
90106

91-
analyzer = NewEndorsementAnalyzer(g, pf, policy.ToPrincipalEvaluator(), mf)
107+
analyzer = NewEndorsementAnalyzer(g, pf, policy.ToPrincipalEvaluator(pkiID2MSPID), mf)
92108
pf.On("PolicyByChaincode", cc).Return(policy).Once()
93-
desc, err = analyzer.PeersForEndorsement(cc, channel)
109+
desc, err = analyzer.PeersForEndorsement(channel, &discovery2.ChaincodeInterest{ChaincodeNames: []string{cc}})
94110
assert.Nil(t, desc)
95111
assert.Equal(t, err.Error(), "cannot satisfy any principal combination")
96112

@@ -108,9 +124,9 @@ func TestPeersForEndorsement(t *testing.T) {
108124
Principal: []byte("p12"),
109125
}).buildPolicy()
110126

111-
analyzer = NewEndorsementAnalyzer(g, pf, policy.ToPrincipalEvaluator(), mf)
127+
analyzer = NewEndorsementAnalyzer(g, pf, policy.ToPrincipalEvaluator(pkiID2MSPID), mf)
112128
pf.On("PolicyByChaincode", cc).Return(policy).Once()
113-
desc, err = analyzer.PeersForEndorsement(cc, channel)
129+
desc, err = analyzer.PeersForEndorsement(channel, &discovery2.ChaincodeInterest{ChaincodeNames: []string{cc}})
114130
assert.NoError(t, err)
115131
assert.NotNil(t, desc)
116132
assert.Len(t, desc.Layouts, 1)
@@ -132,9 +148,9 @@ func TestPeersForEndorsement(t *testing.T) {
132148
Principal: []byte("p12"),
133149
}).buildPolicy()
134150

135-
analyzer = NewEndorsementAnalyzer(g, pf, policy.ToPrincipalEvaluator(), mf)
151+
analyzer = NewEndorsementAnalyzer(g, pf, policy.ToPrincipalEvaluator(pkiID2MSPID), mf)
136152
pf.On("PolicyByChaincode", cc).Return(policy).Once()
137-
desc, err = analyzer.PeersForEndorsement(cc, channel)
153+
desc, err = analyzer.PeersForEndorsement(channel, &discovery2.ChaincodeInterest{ChaincodeNames: []string{cc}})
138154
assert.NoError(t, err)
139155
assert.NotNil(t, desc)
140156
assert.Len(t, desc.Layouts, 2)
@@ -153,7 +169,7 @@ func TestPeersForEndorsement(t *testing.T) {
153169
}).Once()
154170
g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once()
155171
pf.On("PolicyByChaincode", cc).Return(policy).Once()
156-
desc, err = analyzer.PeersForEndorsement(cc, channel)
172+
desc, err = analyzer.PeersForEndorsement(channel, &discovery2.ChaincodeInterest{ChaincodeNames: []string{cc}})
157173
assert.Nil(t, desc)
158174
assert.Equal(t, err.Error(), "cannot satisfy any principal combination")
159175

@@ -166,7 +182,7 @@ func TestPeersForEndorsement(t *testing.T) {
166182
mf.On("Metadata").Return(&chaincode.Metadata{
167183
Name: cc, Version: "1.0",
168184
}).Once()
169-
desc, err = analyzer.PeersForEndorsement(cc, channel)
185+
desc, err = analyzer.PeersForEndorsement(channel, &discovery2.ChaincodeInterest{ChaincodeNames: []string{cc}})
170186
assert.Nil(t, desc)
171187
assert.Equal(t, err.Error(), "cannot satisfy any principal combination")
172188

@@ -175,7 +191,7 @@ func TestPeersForEndorsement(t *testing.T) {
175191
g.On("PeersOfChannel").Return(chanPeers.toMembers()).Once()
176192
pf.On("PolicyByChaincode", cc).Return(policy).Once()
177193
mf.On("Metadata").Return(nil).Once()
178-
desc, err = analyzer.PeersForEndorsement(cc, channel)
194+
desc, err = analyzer.PeersForEndorsement(channel, &discovery2.ChaincodeInterest{ChaincodeNames: []string{cc}})
179195
assert.Nil(t, desc)
180196
assert.Equal(t, err.Error(), "No metadata was found for chaincode chaincode in channel test")
181197
}
@@ -190,12 +206,13 @@ func (p peerSet) toMembers() discovery.Members {
190206
return members
191207
}
192208

193-
func (p peerSet) toIdentitySet() api.PeerIdentitySet {
209+
func identitySet(pkiID2MSPID map[string]string) api.PeerIdentitySet {
194210
var res api.PeerIdentitySet
195-
for _, peer := range p {
211+
for pkiID, mspID := range pkiID2MSPID {
196212
res = append(res, api.PeerIdentityInfo{
197-
Identity: peer.identity,
198-
PKIId: peer.pkiID,
213+
Identity: api.PeerIdentityType(pkiID),
214+
PKIId: common.PKIidType(pkiID),
215+
Organization: api.OrgIdentityType(mspID),
199216
})
200217
}
201218
return res
@@ -291,12 +308,17 @@ func (ip inquireablePolicy) SatisfiedBy() []policies.PrincipalSet {
291308
return ip
292309
}
293310

294-
func (ip inquireablePolicy) ToPrincipalEvaluator() *principalEvaluatorMock {
295-
return &principalEvaluatorMock{ip: ip}
311+
func (ip inquireablePolicy) ToPrincipalEvaluator(pkiID2MSPID map[string]string) *principalEvaluatorMock {
312+
return &principalEvaluatorMock{ip: ip, pkiID2MSPID: pkiID2MSPID}
296313
}
297314

298315
type principalEvaluatorMock struct {
299-
ip []policies.PrincipalSet
316+
pkiID2MSPID map[string]string
317+
ip []policies.PrincipalSet
318+
}
319+
320+
func (pe *principalEvaluatorMock) MSPOfPrincipal(principal *msp.MSPPrincipal) string {
321+
return pe.pkiID2MSPID[string(principal.Principal)]
300322
}
301323

302324
func (pe *principalEvaluatorMock) SatisfiesPrincipal(channel string, identity []byte, principal *msp.MSPPrincipal) error {

discovery/service.go

+1-2
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,7 @@ func (s *service) chaincodeQuery(q *discovery.Query) *discovery.QueryResult {
124124
if len(interest.ChaincodeNames) == 0 {
125125
return wrapError(errors.Errorf("must include at least one chaincode"))
126126
}
127-
// Until we have cc2cc support, we just take the first chaincode
128-
desc, err := s.PeersForEndorsement(interest.ChaincodeNames[0], common2.ChainID(q.Channel))
127+
desc, err := s.PeersForEndorsement(common2.ChainID(q.Channel), interest)
129128
if err != nil {
130129
logger.Errorf("Failed constructing descriptor for chaincode %s,: %v", interest, err)
131130
return wrapError(errors.Errorf("failed constructing descriptor for %v", interest))

discovery/service_test.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,8 @@ func (ms *mockSupport) Peers() discovery2.Members {
445445
return ms.Called().Get(0).(discovery2.Members)
446446
}
447447

448-
func (ms *mockSupport) PeersForEndorsement(cc string, channel common2.ChainID) (*discovery.EndorsementDescriptor, error) {
448+
func (ms *mockSupport) PeersForEndorsement(channel common2.ChainID, interest *discovery.ChaincodeInterest) (*discovery.EndorsementDescriptor, error) {
449+
cc := interest.ChaincodeNames[0]
449450
args := ms.Called(cc)
450451
if args.Get(0) == nil {
451452
return nil, args.Error(1)

0 commit comments

Comments
 (0)