github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/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 []IdentitySkeletonList
    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 (cl ClusterList) Assignments() AssignmentList {
    35  	assignments := make(AssignmentList, 0, len(cl))
    36  	for _, cluster := range cl {
    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 IdentitySkeletonList) (ClusterList, error) {
    49  
    50  	// build a lookup for all the identities by node identifier
    51  	lookup := make(map[Identifier]*IdentitySkeleton)
    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(IdentitySkeletonList, 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 given cluster.
    84  func (cl ClusterList) ByIndex(index uint) (IdentitySkeletonList, bool) {
    85  	if index >= uint(len(cl)) {
    86  		return nil, false
    87  	}
    88  	return cl[int(index)], true
    89  }
    90  
    91  // ByTxID selects the cluster that should receive the transaction with the given
    92  // transaction ID.
    93  //
    94  // For evenly distributed transaction IDs, this will evenly distribute
    95  // transactions between clusters.
    96  func (cl ClusterList) ByTxID(txID Identifier) (IdentitySkeletonList, bool) {
    97  	bigTxID := new(big.Int).SetBytes(txID[:])
    98  	bigIndex := new(big.Int).Mod(bigTxID, big.NewInt(int64(len(cl))))
    99  	return cl.ByIndex(uint(bigIndex.Uint64()))
   100  }
   101  
   102  // ByNodeID select the cluster that the node with the given ID is part of.
   103  //
   104  // Nodes will be divided into equally sized clusters as far as possible.
   105  // The last return value will indicate if the look up was successful
   106  func (cl ClusterList) ByNodeID(nodeID Identifier) (IdentitySkeletonList, uint, bool) {
   107  	for index, cluster := range cl {
   108  		for _, participant := range cluster {
   109  			if participant.NodeID == nodeID {
   110  				return cluster, uint(index), true
   111  			}
   112  		}
   113  	}
   114  	return nil, 0, false
   115  }
   116  
   117  // IndexOf returns the index of the given cluster.
   118  func (cl ClusterList) IndexOf(cluster IdentitySkeletonList) (uint, bool) {
   119  	clusterFingerprint := cluster.ID()
   120  	for index, other := range cl {
   121  		if other.ID() == clusterFingerprint {
   122  			return uint(index), true
   123  		}
   124  	}
   125  	return 0, false
   126  }