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 }