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  }