github.com/true-sqn/fabric@v2.1.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  }