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