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 }