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  }