github.com/koko1123/flow-go-1@v0.29.6/model/flow/cluster.go (about)

     1  package flow
     2  
     3  import (
     4  	"fmt"
     5  	"math/big"
     6  )
     7  
     8  // AssignmentList is a list of identifier lists. Each list of identifiers lists the
     9  // identities that are part of the given cluster.
    10  type AssignmentList []IdentifierList
    11  
    12  // ClusterList is a list of identity lists. Each `IdentityList` represents the
    13  // nodes assigned to a specific cluster.
    14  type ClusterList []IdentityList
    15  
    16  func (al AssignmentList) EqualTo(other AssignmentList) bool {
    17  	if len(al) != len(other) {
    18  		return false
    19  	}
    20  	for i, a := range al {
    21  		if len(a) != len(other[i]) {
    22  			return false
    23  		}
    24  		for j, identifier := range a {
    25  			if identifier != other[i][j] {
    26  				return false
    27  			}
    28  		}
    29  	}
    30  	return true
    31  }
    32  
    33  // Assignments returns the assignment list for a cluster.
    34  func (clusters ClusterList) Assignments() AssignmentList {
    35  	assignments := make(AssignmentList, 0, len(clusters))
    36  	for _, cluster := range clusters {
    37  		assignment := make([]Identifier, 0, len(cluster))
    38  		for _, collector := range cluster {
    39  			assignment = append(assignment, collector.NodeID)
    40  		}
    41  		assignments = append(assignments, assignment)
    42  	}
    43  	return assignments
    44  }
    45  
    46  // NewClusterList creates a new cluster list based on the given cluster assignment
    47  // and the provided list of identities.
    48  func NewClusterList(assignments AssignmentList, collectors IdentityList) (ClusterList, error) {
    49  
    50  	// build a lookup for all the identities by node identifier
    51  	lookup := make(map[Identifier]*Identity)
    52  	for _, collector := range collectors {
    53  		_, ok := lookup[collector.NodeID]
    54  		if ok {
    55  			return nil, fmt.Errorf("duplicate collector in list %v", collector.NodeID)
    56  		}
    57  		lookup[collector.NodeID] = collector
    58  	}
    59  
    60  	// replicate the identifier list but use identities instead
    61  	clusters := make(ClusterList, 0, len(assignments))
    62  	for _, participants := range assignments {
    63  		cluster := make(IdentityList, 0, len(participants))
    64  		for _, participantID := range participants {
    65  			participant, found := lookup[participantID]
    66  			if !found {
    67  				return nil, fmt.Errorf("could not find collector identity (%x)", participantID)
    68  			}
    69  			cluster = append(cluster, participant)
    70  			delete(lookup, participantID)
    71  		}
    72  		clusters = append(clusters, cluster)
    73  	}
    74  
    75  	// check that every collector was assigned
    76  	if len(lookup) != 0 {
    77  		return nil, fmt.Errorf("missing collector assignments (%s)", lookup)
    78  	}
    79  
    80  	return clusters, nil
    81  }
    82  
    83  // ByIndex retrieves the list of identities that are part of the
    84  // given cluster.
    85  func (cl ClusterList) ByIndex(index uint) (IdentityList, bool) {
    86  	if index >= uint(len(cl)) {
    87  		return nil, false
    88  	}
    89  	return cl[int(index)], true
    90  }
    91  
    92  // ByTxID selects the cluster that should receive the transaction with the given
    93  // transaction ID.
    94  //
    95  // For evenly distributed transaction IDs, this will evenly distribute
    96  // transactions between clusters.
    97  func (cl ClusterList) ByTxID(txID Identifier) (IdentityList, bool) {
    98  	bigTxID := new(big.Int).SetBytes(txID[:])
    99  	bigIndex := new(big.Int).Mod(bigTxID, big.NewInt(int64(len(cl))))
   100  	return cl.ByIndex(uint(bigIndex.Uint64()))
   101  }
   102  
   103  // ByNodeID select the cluster that the node with the given ID is part of.
   104  //
   105  // Nodes will be divided into equally sized clusters as far as possible.
   106  // The last return value will indicate if the look up was successful
   107  func (cl ClusterList) ByNodeID(nodeID Identifier) (IdentityList, uint, bool) {
   108  	for index, cluster := range cl {
   109  		for _, participant := range cluster {
   110  			if participant.NodeID == nodeID {
   111  				return cluster, uint(index), true
   112  			}
   113  		}
   114  	}
   115  	return nil, 0, false
   116  }
   117  
   118  // IndexOf returns the index of the given cluster.
   119  func (cl ClusterList) IndexOf(cluster IdentityList) (uint, bool) {
   120  	clusterFingerprint := cluster.Fingerprint()
   121  	for index, other := range cl {
   122  		if other.Fingerprint() == clusterFingerprint {
   123  			return uint(index), true
   124  		}
   125  	}
   126  	return 0, false
   127  }