github.com/MetalBlockchain/metalgo@v1.11.9/ids/aliases.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package ids 5 6 import ( 7 "errors" 8 "fmt" 9 "sync" 10 ) 11 12 var ( 13 ErrNoIDWithAlias = errors.New("there is no ID with alias") 14 errNoAliasForID = errors.New("there is no alias for ID") 15 errAliasAlreadyMapped = errors.New("alias already mapped to an ID") 16 ) 17 18 // AliaserReader allows one to lookup the aliases given to an ID. 19 type AliaserReader interface { 20 // Lookup returns the ID associated with alias 21 Lookup(alias string) (ID, error) 22 23 // PrimaryAlias returns the first alias of [id] 24 PrimaryAlias(id ID) (string, error) 25 26 // Aliases returns the aliases of an ID 27 Aliases(id ID) ([]string, error) 28 } 29 30 // AliaserWriter allows one to give an ID aliases. An ID can have arbitrarily 31 // many aliases; two IDs may not have the same alias. 32 type AliaserWriter interface { 33 // Alias gives [id] the alias [alias] 34 Alias(id ID, alias string) error 35 36 // RemoveAliases of the provided ID 37 RemoveAliases(id ID) 38 } 39 40 // Aliaser allows one to give an ID aliases and lookup the aliases given to an 41 // ID. 42 type Aliaser interface { 43 AliaserReader 44 AliaserWriter 45 46 // PrimaryAliasOrDefault returns the first alias of [id], or ID string as a 47 // default if no alias exists 48 PrimaryAliasOrDefault(id ID) string 49 } 50 51 type aliaser struct { 52 lock sync.RWMutex 53 dealias map[string]ID 54 aliases map[ID][]string 55 } 56 57 func NewAliaser() Aliaser { 58 return &aliaser{ 59 dealias: make(map[string]ID), 60 aliases: make(map[ID][]string), 61 } 62 } 63 64 func (a *aliaser) Lookup(alias string) (ID, error) { 65 a.lock.RLock() 66 defer a.lock.RUnlock() 67 68 if id, ok := a.dealias[alias]; ok { 69 return id, nil 70 } 71 return ID{}, fmt.Errorf("%w: %s", ErrNoIDWithAlias, alias) 72 } 73 74 func (a *aliaser) PrimaryAlias(id ID) (string, error) { 75 a.lock.RLock() 76 defer a.lock.RUnlock() 77 78 aliases := a.aliases[id] 79 if len(aliases) == 0 { 80 return "", fmt.Errorf("%w: %s", errNoAliasForID, id) 81 } 82 return aliases[0], nil 83 } 84 85 func (a *aliaser) PrimaryAliasOrDefault(id ID) string { 86 alias, err := a.PrimaryAlias(id) 87 if err != nil { 88 return id.String() 89 } 90 return alias 91 } 92 93 func (a *aliaser) Aliases(id ID) ([]string, error) { 94 a.lock.RLock() 95 defer a.lock.RUnlock() 96 97 return a.aliases[id], nil 98 } 99 100 func (a *aliaser) Alias(id ID, alias string) error { 101 a.lock.Lock() 102 defer a.lock.Unlock() 103 104 if _, exists := a.dealias[alias]; exists { 105 return fmt.Errorf("%w: %s", errAliasAlreadyMapped, alias) 106 } 107 108 a.dealias[alias] = id 109 a.aliases[id] = append(a.aliases[id], alias) 110 return nil 111 } 112 113 func (a *aliaser) RemoveAliases(id ID) { 114 a.lock.Lock() 115 defer a.lock.Unlock() 116 117 aliases := a.aliases[id] 118 delete(a.aliases, id) 119 for _, alias := range aliases { 120 delete(a.dealias, alias) 121 } 122 } 123 124 // GetRelevantAliases returns the aliases with the redundant identity alias 125 // removed (each id is aliased to at least itself). 126 func GetRelevantAliases(aliaser Aliaser, ids []ID) (map[ID][]string, error) { 127 result := make(map[ID][]string, len(ids)) 128 for _, id := range ids { 129 aliases, err := aliaser.Aliases(id) 130 if err != nil { 131 return nil, err 132 } 133 134 // remove the redundant alias where alias = id. 135 relevantAliases := make([]string, 0, len(aliases)-1) 136 for _, alias := range aliases { 137 if alias != id.String() { 138 relevantAliases = append(relevantAliases, alias) 139 } 140 } 141 result[id] = relevantAliases 142 } 143 return result, nil 144 }