mirror of
https://github.com/luxfi/ids.git
synced 2026-10-30 16:22:36 +00:00
UnmarshalText delegated to UnmarshalJSON, so it demanded quotes around a value that was never JSON. Every caller that keeps the TextUnmarshaler contract met "first and last characters should be quotes": encoding/json handing over a map KEY, encoding/xml, flag.Value, and a URL binder reading a query value — where the failure is silent and the node id arrives as the zero node with a 200 on the answer. ID.UnmarshalText was corrected this way already, and its comment records the same symptom on --chain-aliases-file. This is that correction on the other two, with the reading in ONE place: UnmarshalText holds it and UnmarshalJSON unquotes and calls it, so the two spellings cannot drift.
131 lines
3.4 KiB
Go
131 lines
3.4 KiB
Go
// Copyright (C) 2020-2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package ids
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/luxfi/crypto/cb58"
|
|
"github.com/luxfi/crypto/hash"
|
|
)
|
|
|
|
const ShortIDLen = 20
|
|
|
|
// ShortEmpty is a useful all zero value
|
|
var (
|
|
ShortEmpty = ShortID{}
|
|
|
|
_ Sortable[ShortID] = ShortID{}
|
|
)
|
|
|
|
// ShortID wraps a 20 byte hash as an identifier
|
|
type ShortID [ShortIDLen]byte
|
|
|
|
// ToShortID attempt to convert a byte slice into an id
|
|
func ToShortID(bytes []byte) (ShortID, error) {
|
|
return hash.ToHash160(bytes)
|
|
}
|
|
|
|
// ShortFromString is the inverse of ShortID.String()
|
|
func ShortFromString(idStr string) (ShortID, error) {
|
|
bytes, err := cb58.Decode(idStr)
|
|
if err != nil {
|
|
return ShortID{}, err
|
|
}
|
|
return ToShortID(bytes)
|
|
}
|
|
|
|
// ShortFromPrefixedString returns a ShortID assuming the cb58 format is
|
|
// prefixed
|
|
func ShortFromPrefixedString(idStr, prefix string) (ShortID, error) {
|
|
if !strings.HasPrefix(idStr, prefix) {
|
|
return ShortID{}, fmt.Errorf("ID: %s is missing the prefix: %s", idStr, prefix)
|
|
}
|
|
return ShortFromString(strings.TrimPrefix(idStr, prefix))
|
|
}
|
|
|
|
func (id ShortID) MarshalJSON() ([]byte, error) {
|
|
str, err := cb58.Encode(id[:])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return []byte(`"` + str + `"`), nil
|
|
}
|
|
|
|
// UnmarshalJSON reads the same cb58 word, quoted. The reading itself is in
|
|
// UnmarshalText and is not written twice.
|
|
func (id *ShortID) UnmarshalJSON(b []byte) error {
|
|
str := string(b)
|
|
if str == nullStr { // If "null", do nothing
|
|
return nil
|
|
}
|
|
if len(str) < 2 || str[0] != '"' || str[len(str)-1] != '"' {
|
|
return errMissingQuotes
|
|
}
|
|
return id.UnmarshalText([]byte(str[1 : len(str)-1]))
|
|
}
|
|
|
|
// UnmarshalText reads the cb58 word ITSELF, unquoted — which is what a
|
|
// TextUnmarshaler is handed, by encoding/json for a map key, by encoding/xml,
|
|
// by flag.Value and by a URL binder. It delegated to UnmarshalJSON, so every
|
|
// one of those callers met "first and last characters should be quotes" for a
|
|
// value that was never JSON. [ID.UnmarshalText] was already corrected this way;
|
|
// this is the same correction on the other two.
|
|
func (id *ShortID) UnmarshalText(text []byte) error {
|
|
str := string(text)
|
|
if str == nullStr || str == "" {
|
|
return nil
|
|
}
|
|
bytes, err := cb58.Decode(str)
|
|
if err != nil {
|
|
return fmt.Errorf("couldn't decode ID to bytes: %w", err)
|
|
}
|
|
*id, err = ToShortID(bytes)
|
|
return err
|
|
}
|
|
|
|
// Bytes returns the 20 byte hash as a slice. It is assumed this slice is not
|
|
// modified.
|
|
func (id ShortID) Bytes() []byte {
|
|
return id[:]
|
|
}
|
|
|
|
// Hex returns a hex encoded string of this id.
|
|
func (id ShortID) Hex() string {
|
|
return hex.EncodeToString(id.Bytes())
|
|
}
|
|
|
|
func (id ShortID) String() string {
|
|
// We assume that the maximum size of a byte slice that
|
|
// can be stringified is at least the length of an ID
|
|
str, _ := cb58.Encode(id.Bytes())
|
|
return str
|
|
}
|
|
|
|
// PrefixedString returns the String representation with a prefix added
|
|
func (id ShortID) PrefixedString(prefix string) string {
|
|
return prefix + id.String()
|
|
}
|
|
|
|
func (id ShortID) MarshalText() ([]byte, error) {
|
|
return []byte(id.String()), nil
|
|
}
|
|
|
|
func (id ShortID) Compare(other ShortID) int {
|
|
return bytes.Compare(id[:], other[:])
|
|
}
|
|
|
|
// ShortIDsToStrings converts an array of shortIDs to an array of their string
|
|
// representations
|
|
func ShortIDsToStrings(ids []ShortID) []string {
|
|
idStrs := make([]string, len(ids))
|
|
for i, id := range ids {
|
|
idStrs[i] = id.String()
|
|
}
|
|
return idStrs
|
|
}
|