github.com/MetalBlockchain/metalgo@v1.11.9/ids/short.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 "bytes" 8 "encoding/hex" 9 "fmt" 10 "strings" 11 12 "github.com/MetalBlockchain/metalgo/utils" 13 "github.com/MetalBlockchain/metalgo/utils/cb58" 14 "github.com/MetalBlockchain/metalgo/utils/hashing" 15 ) 16 17 const ShortIDLen = 20 18 19 // ShortEmpty is a useful all zero value 20 var ( 21 ShortEmpty = ShortID{} 22 23 _ utils.Sortable[ShortID] = ShortID{} 24 ) 25 26 // ShortID wraps a 20 byte hash as an identifier 27 type ShortID [ShortIDLen]byte 28 29 // ToShortID attempt to convert a byte slice into an id 30 func ToShortID(bytes []byte) (ShortID, error) { 31 return hashing.ToHash160(bytes) 32 } 33 34 // ShortFromString is the inverse of ShortID.String() 35 func ShortFromString(idStr string) (ShortID, error) { 36 bytes, err := cb58.Decode(idStr) 37 if err != nil { 38 return ShortID{}, err 39 } 40 return ToShortID(bytes) 41 } 42 43 // ShortFromPrefixedString returns a ShortID assuming the cb58 format is 44 // prefixed 45 func ShortFromPrefixedString(idStr, prefix string) (ShortID, error) { 46 if !strings.HasPrefix(idStr, prefix) { 47 return ShortID{}, fmt.Errorf("ID: %s is missing the prefix: %s", idStr, prefix) 48 } 49 return ShortFromString(strings.TrimPrefix(idStr, prefix)) 50 } 51 52 func (id ShortID) MarshalJSON() ([]byte, error) { 53 str, err := cb58.Encode(id[:]) 54 if err != nil { 55 return nil, err 56 } 57 return []byte(`"` + str + `"`), nil 58 } 59 60 func (id *ShortID) UnmarshalJSON(b []byte) error { 61 str := string(b) 62 if str == nullStr { // If "null", do nothing 63 return nil 64 } else if len(str) < 2 { 65 return errMissingQuotes 66 } 67 68 lastIndex := len(str) - 1 69 if str[0] != '"' || str[lastIndex] != '"' { 70 return errMissingQuotes 71 } 72 73 // Parse CB58 formatted string to bytes 74 bytes, err := cb58.Decode(str[1:lastIndex]) 75 if err != nil { 76 return fmt.Errorf("couldn't decode ID to bytes: %w", err) 77 } 78 *id, err = ToShortID(bytes) 79 return err 80 } 81 82 func (id *ShortID) UnmarshalText(text []byte) error { 83 return id.UnmarshalJSON(text) 84 } 85 86 // Bytes returns the 20 byte hash as a slice. It is assumed this slice is not 87 // modified. 88 func (id ShortID) Bytes() []byte { 89 return id[:] 90 } 91 92 // Hex returns a hex encoded string of this id. 93 func (id ShortID) Hex() string { 94 return hex.EncodeToString(id.Bytes()) 95 } 96 97 func (id ShortID) String() string { 98 // We assume that the maximum size of a byte slice that 99 // can be stringified is at least the length of an ID 100 str, _ := cb58.Encode(id.Bytes()) 101 return str 102 } 103 104 // PrefixedString returns the String representation with a prefix added 105 func (id ShortID) PrefixedString(prefix string) string { 106 return prefix + id.String() 107 } 108 109 func (id ShortID) MarshalText() ([]byte, error) { 110 return []byte(id.String()), nil 111 } 112 113 func (id ShortID) Compare(other ShortID) int { 114 return bytes.Compare(id[:], other[:]) 115 } 116 117 // ShortIDsToStrings converts an array of shortIDs to an array of their string 118 // representations 119 func ShortIDsToStrings(ids []ShortID) []string { 120 idStrs := make([]string, len(ids)) 121 for i, id := range ids { 122 idStrs[i] = id.String() 123 } 124 return idStrs 125 }