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