Skip to content

Commit 35d2356

Browse files
committed
[FAB-10023] Principal set merging
This change set implements merging of principal sets sets. More specifically, if 2 or more sets of principal sets are given, such that each principal set set - satisfies an endorsement policy, we are able to compute sets of principal sets such that each principal set satisfies all of the endorsement policies. This is to be used for cc2cc queries for service discovery. Change-Id: I4b6e63f07b1b5832667475b7876df754007d9c2f Signed-off-by: yacovm <yacovm@il.ibm.com>
1 parent 6be509a commit 35d2356

File tree

3 files changed

+463
-0
lines changed

3 files changed

+463
-0
lines changed

common/policies/inquire/compare.go

+32
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ package inquire
99
import (
1010
"bytes"
1111

12+
"fmt"
13+
1214
"github.com/golang/protobuf/proto"
1315
"github.com/hyperledger/fabric/common/policies"
1416
"github.com/hyperledger/fabric/protos/msp"
@@ -128,6 +130,27 @@ func (cp *ComparablePrincipal) ToRole() *ComparablePrincipal {
128130
// ComparablePrincipalSet aggregates ComparablePrincipals
129131
type ComparablePrincipalSet []*ComparablePrincipal
130132

133+
// String returns a string representation of this ComparablePrincipalSet
134+
func (cps ComparablePrincipalSet) String() string {
135+
buff := bytes.Buffer{}
136+
buff.WriteString("[")
137+
for i, cp := range cps {
138+
buff.WriteString(cp.mspID)
139+
buff.WriteString(".")
140+
if cp.role != nil {
141+
buff.WriteString(fmt.Sprintf("%v", cp.role.Role))
142+
}
143+
if cp.ou != nil {
144+
buff.WriteString(fmt.Sprintf("%v", cp.ou.OrganizationalUnitIdentifier))
145+
}
146+
if i < len(cps)-1 {
147+
buff.WriteString(", ")
148+
}
149+
}
150+
buff.WriteString("]")
151+
return buff.String()
152+
}
153+
131154
// NewComparablePrincipalSet constructs a ComparablePrincipalSet out of the given PrincipalSet
132155
func NewComparablePrincipalSet(set policies.PrincipalSet) ComparablePrincipalSet {
133156
var res ComparablePrincipalSet
@@ -141,6 +164,15 @@ func NewComparablePrincipalSet(set policies.PrincipalSet) ComparablePrincipalSet
141164
return res
142165
}
143166

167+
// Clone returns a copy of this ComparablePrincipalSet
168+
func (cps ComparablePrincipalSet) Clone() ComparablePrincipalSet {
169+
res := make(ComparablePrincipalSet, len(cps))
170+
for i, cp := range cps {
171+
res[i] = cp
172+
}
173+
return res
174+
}
175+
144176
// IsCoveredBy returns whether the ComparablePrincipalSet is covered by the given ComparablePrincipalSet.
145177
// A ComparablePrincipalSet X is covered by another ComparablePrincipalSet Y if:
146178
// for each ComparablePrincipal x in X there is a ComparablePrincipal y in Y,

common/policies/inquire/merge.go

+251
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
/*
2+
Copyright IBM Corp. All Rights Reserved.
3+
4+
SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
package inquire
8+
9+
import (
10+
"reflect"
11+
)
12+
13+
// ComparablePrincipalSets aggregate ComparablePrincipalSets
14+
type ComparablePrincipalSets []ComparablePrincipalSet
15+
16+
// Merge returns ComparablePrincipalSets that the underlying PrincipalSets consist of
17+
// PrincipalSets that satisfy the endorsement policies that both ComparablePrincipalSets were derived of.
18+
// More formally speaking, let EP1 and EP2 be endorsement policies, and
19+
// P1 and P2 be the principal sets that each principal set p in P1 satisfies EP1,
20+
// and each principal set p in P2 satisfies EP2.
21+
// Denote as S1 and S2 the ComparablePrincipalSets derived from EP1 and EP2 respectively.
22+
// Then, S = Merge(S1, S2) wields ComparablePrincipalSets
23+
// such that every ComparablePrincipalSet s in S, satisfies both EP1 and EP2.
24+
func Merge(s1, s2 ComparablePrincipalSets) ComparablePrincipalSets {
25+
var res ComparablePrincipalSets
26+
setsIn1ToTheContainingSetsIn2 := computeContainedInMapping(s1, s2)
27+
setsIn1ThatAreIn2 := s1.OfMapping(setsIn1ToTheContainingSetsIn2, s2)
28+
// Without loss of generality, remove all principal sets in s1 that
29+
// are contained by principal sets in s2, in order not to have duplicates
30+
s1 = s1.ExcludeIndices(setsIn1ToTheContainingSetsIn2)
31+
setsIn2ToTheContainingSetsIn1 := computeContainedInMapping(s2, s1)
32+
setsIn2ThatAreIn1 := s2.OfMapping(setsIn2ToTheContainingSetsIn1, s1)
33+
s2 = s2.ExcludeIndices(setsIn2ToTheContainingSetsIn1)
34+
35+
// In the interim, the result contains sets from either the first of the second
36+
// set, that also contain some other set(s) in the other set
37+
res = append(res, setsIn1ThatAreIn2.ToMergedPrincipalSets()...)
38+
res = append(res, setsIn2ThatAreIn1.ToMergedPrincipalSets()...)
39+
40+
// Now, purge the principal sets from the original groups s1 and s2
41+
// that are found to contain sets from the other group.
42+
// The motivation is to be left with s1 and s2 that only contain sets that aren't
43+
// contained or contain any set of the other group.
44+
s1 = s1.ExcludeIndices(setsIn2ToTheContainingSetsIn1.invert())
45+
s2 = s2.ExcludeIndices(setsIn1ToTheContainingSetsIn2.invert())
46+
47+
// We're left with principal sets either in both or in one of the sets
48+
// that are entirely contained by the other sets, so there is nothing more to be done
49+
if len(s1) == 0 || len(s2) == 0 {
50+
return res.Reduce()
51+
}
52+
53+
// We're only left with sets that are not contained in the other sets,
54+
// in both sets. Therefore we should combine them to form principal sets
55+
// that contain both sets.
56+
combinedPairs := CartesianProduct(s1, s2)
57+
res = append(res, combinedPairs.ToMergedPrincipalSets()...)
58+
return res.Reduce()
59+
}
60+
61+
// CartesianProduct returns a comparablePrincipalSetPairs that is comprised of the combination
62+
// of every possible pair of ComparablePrincipalSet such that the first element is in s1,
63+
// and the second element is in s2.
64+
func CartesianProduct(s1, s2 ComparablePrincipalSets) comparablePrincipalSetPairs {
65+
var res comparablePrincipalSetPairs
66+
for _, x := range s1 {
67+
var set comparablePrincipalSetPairs
68+
// For every set in the first sets,
69+
// combine it with every set in the second sets
70+
for _, y := range s2 {
71+
set = append(set, comparablePrincipalSetPair{
72+
contained: x,
73+
containing: y,
74+
})
75+
}
76+
res = append(res, set...)
77+
}
78+
return res
79+
}
80+
81+
// comparablePrincipalSetPair is a tuple of 2 ComparablePrincipalSets
82+
type comparablePrincipalSetPair struct {
83+
contained ComparablePrincipalSet
84+
containing ComparablePrincipalSet
85+
}
86+
87+
// EnsurePlurality returns a ComparablePrincipalSet such that plurality requirements over
88+
// the contained ComparablePrincipalSet in the comparablePrincipalSetPair hold
89+
func (pair comparablePrincipalSetPair) MergeWithPlurality() ComparablePrincipalSet {
90+
var principalsToAdd []*ComparablePrincipal
91+
used := make(map[int]struct{})
92+
// Iterate over the contained set and for each principal
93+
for _, principal := range pair.contained {
94+
var covered bool
95+
// Search a principal in the containing set to cover the principal in the contained set
96+
for i, coveringPrincipal := range pair.containing {
97+
// The principal found shouldn't be used twice
98+
if _, isUsed := used[i]; isUsed {
99+
continue
100+
}
101+
// All identities that satisfy the found principal, should satisfy the covered principal as well.
102+
if coveringPrincipal.IsA(principal) {
103+
used[i] = struct{}{}
104+
covered = true
105+
break
106+
}
107+
}
108+
// If we haven't found a cover to the principal, it's because we already used up all the potential candidates
109+
// among the containing set, so just add it to the principals set to be added later.
110+
if !covered {
111+
principalsToAdd = append(principalsToAdd, principal)
112+
}
113+
}
114+
115+
res := pair.containing.Clone()
116+
res = append(res, principalsToAdd...)
117+
return res
118+
}
119+
120+
// comparablePrincipalSetPairs aggregates []comparablePrincipalSetPairs
121+
type comparablePrincipalSetPairs []comparablePrincipalSetPair
122+
123+
// ToPrincipalSets converts the comparablePrincipalSetPairs to ComparablePrincipalSets
124+
// while taking into account plurality of each pair
125+
func (pairs comparablePrincipalSetPairs) ToMergedPrincipalSets() ComparablePrincipalSets {
126+
var res ComparablePrincipalSets
127+
for _, pair := range pairs {
128+
res = append(res, pair.MergeWithPlurality())
129+
}
130+
return res
131+
}
132+
133+
// OfMapping returns comparablePrincipalSetPairs comprising only of the indices found in the given keys
134+
func (cps ComparablePrincipalSets) OfMapping(mapping map[int][]int, sets2 ComparablePrincipalSets) comparablePrincipalSetPairs {
135+
var res []comparablePrincipalSetPair
136+
for i, js := range mapping {
137+
for _, j := range js {
138+
res = append(res, comparablePrincipalSetPair{
139+
contained: cps[i],
140+
containing: sets2[j],
141+
})
142+
}
143+
}
144+
return res
145+
}
146+
147+
// Reduce returns the ComparablePrincipalSets in a form such that no element contains another element.
148+
// Every element that contains some other element is omitted from the result.
149+
func (cps ComparablePrincipalSets) Reduce() ComparablePrincipalSets {
150+
var res ComparablePrincipalSets
151+
for i, s1 := range cps {
152+
var isContaining bool
153+
for j, s2 := range cps {
154+
if i == j {
155+
continue
156+
}
157+
if s2.IsSubset(s1) {
158+
isContaining = true
159+
}
160+
}
161+
if !isContaining {
162+
res = append(res, s1)
163+
}
164+
}
165+
return res
166+
}
167+
168+
// ExcludeIndices returns a ComparablePrincipalSets without the given indices found in the keys
169+
func (cps ComparablePrincipalSets) ExcludeIndices(mapping map[int][]int) ComparablePrincipalSets {
170+
var res ComparablePrincipalSets
171+
for i, set := range cps {
172+
if _, exists := mapping[i]; exists {
173+
continue
174+
}
175+
res = append(res, set)
176+
}
177+
return res
178+
}
179+
180+
// Contains returns whether this ComparablePrincipalSet contains the given ComparablePrincipal.
181+
// A ComparablePrincipalSet X contains a ComparablePrincipal y if
182+
// there is a ComparablePrincipal x in X such that x.IsA(y).
183+
// From here it follows that every signature set that satisfies X, also satisfies y.
184+
func (cps ComparablePrincipalSet) Contains(s *ComparablePrincipal) bool {
185+
for _, cp := range cps {
186+
if cp.IsA(s) {
187+
return true
188+
}
189+
}
190+
return false
191+
}
192+
193+
// IsContainedIn returns whether this ComparablePrincipalSet is contained in the given ComparablePrincipalSet.
194+
// More formally- a ComparablePrincipalSet X is said to be contained in ComparablePrincipalSet Y
195+
// if for each ComparablePrincipalSet x in X there is a ComparablePrincipalSet y in Y such that y.IsA(x) is true.
196+
// If a ComparablePrincipalSet X is contained by a ComparablePrincipalSet Y then if a signature set satisfies Y,
197+
// it also satisfies X, because for each x in X there is a y in Y such that there exists a signature of a corresponding
198+
// identity such that the identity satisfies y, and therefore satisfies x too.
199+
func (cps ComparablePrincipalSet) IsContainedIn(set ComparablePrincipalSet) bool {
200+
for _, cp := range cps {
201+
if !set.Contains(cp) {
202+
return false
203+
}
204+
}
205+
return true
206+
}
207+
208+
// computeContainedInMapping returns a mapping from the indices in the first ComparablePrincipalSets
209+
// to the indices in the second ComparablePrincipalSets that the corresponding ComparablePrincipalSets in
210+
// the first ComparablePrincipalSets are contained in the second ComparablePrincipalSets given.
211+
func computeContainedInMapping(s1, s2 []ComparablePrincipalSet) intMapping {
212+
mapping := make(map[int][]int)
213+
for i, ps1 := range s1 {
214+
for j, ps2 := range s2 {
215+
if !ps1.IsContainedIn(ps2) {
216+
continue
217+
}
218+
mapping[i] = append(mapping[i], j)
219+
}
220+
}
221+
return mapping
222+
}
223+
224+
// intMapping maps integers to sets of integers
225+
type intMapping map[int][]int
226+
227+
func (im intMapping) invert() intMapping {
228+
res := make(intMapping)
229+
for i, js := range im {
230+
for _, j := range js {
231+
res[j] = append(res[j], i)
232+
}
233+
}
234+
return res
235+
}
236+
237+
// IsSubset returns whether this ComparablePrincipalSet is a subset of the given ComparablePrincipalSet
238+
func (cps ComparablePrincipalSet) IsSubset(sets ComparablePrincipalSet) bool {
239+
for _, p1 := range cps {
240+
var found bool
241+
for _, p2 := range sets {
242+
if reflect.DeepEqual(p1, p2) {
243+
found = true
244+
}
245+
}
246+
if !found {
247+
return false
248+
}
249+
}
250+
return true
251+
}

0 commit comments

Comments
 (0)