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  }