git.frostfs.info/TrueCloudLab/frostfs-sdk-go@v0.0.0-20241022124111-5361f0ecebd3/user/id.go (about)

     1  package user
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  
     8  	"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
     9  	"github.com/mr-tron/base58"
    10  	"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
    11  	"github.com/nspcc-dev/neo-go/pkg/encoding/address"
    12  	"github.com/nspcc-dev/neo-go/pkg/util"
    13  )
    14  
    15  const idSize = 25
    16  
    17  var zeroSlice = bytes.Repeat([]byte{0}, idSize)
    18  
    19  // ID identifies users of the FrostFS system.
    20  //
    21  // ID is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs.OwnerID
    22  // message. See ReadFromV2 / WriteToV2 methods.
    23  //
    24  // Instances can be created using built-in var declaration. Zero ID is not valid,
    25  // so it MUST be initialized using some modifying function (e.g. SetScriptHash,
    26  // IDFromKey, etc.).
    27  type ID struct {
    28  	w []byte
    29  }
    30  
    31  // ReadFromV2 reads ID from the refs.OwnerID message. Returns an error if
    32  // the message is malformed according to the FrostFS API V2 protocol.
    33  //
    34  // See also WriteToV2.
    35  func (x *ID) ReadFromV2(m refs.OwnerID) error {
    36  	w := m.GetValue()
    37  	if len(w) != idSize {
    38  		return fmt.Errorf("invalid length %d, expected %d", len(w), idSize)
    39  	}
    40  
    41  	if w[0] != address.NEO3Prefix {
    42  		return fmt.Errorf("invalid prefix byte 0x%X, expected 0x%X", w[0], address.NEO3Prefix)
    43  	}
    44  
    45  	if !bytes.Equal(w[21:], hash.Checksum(w[:21])) {
    46  		return errors.New("checksum mismatch")
    47  	}
    48  
    49  	x.w = w
    50  
    51  	return nil
    52  }
    53  
    54  // WriteToV2 writes ID to the refs.OwnerID message.
    55  // The message must not be nil.
    56  //
    57  // See also ReadFromV2.
    58  func (x ID) WriteToV2(m *refs.OwnerID) {
    59  	m.SetValue(x.w)
    60  }
    61  
    62  // SetScriptHash forms user ID from wallet address scripthash.
    63  func (x *ID) SetScriptHash(scriptHash util.Uint160) {
    64  	if cap(x.w) < idSize {
    65  		x.w = make([]byte, idSize)
    66  	} else if len(x.w) < idSize {
    67  		x.w = x.w[:idSize]
    68  	}
    69  
    70  	x.w[0] = address.Prefix
    71  	copy(x.w[1:], scriptHash.BytesBE())
    72  	copy(x.w[21:], hash.Checksum(x.w[:21]))
    73  }
    74  
    75  // ScriptHash calculates and returns script hash of ID.
    76  func (x *ID) ScriptHash() (util.Uint160, error) {
    77  	return util.Uint160DecodeBytesBE(x.w[1:21])
    78  }
    79  
    80  // WalletBytes returns FrostFS user ID as Neo3 wallet address in a binary format.
    81  //
    82  // Return value MUST NOT be mutated: to do this, first make a copy.
    83  //
    84  // See also Neo3 wallet docs.
    85  func (x ID) WalletBytes() []byte {
    86  	return x.w
    87  }
    88  
    89  // EncodeToString encodes ID into FrostFS API V2 protocol string.
    90  //
    91  // See also DecodeString.
    92  func (x ID) EncodeToString() string {
    93  	return base58.Encode(x.w)
    94  }
    95  
    96  // DecodeString decodes FrostFS API V2 protocol string. Returns an error
    97  // if s is malformed.
    98  //
    99  // DecodeString always changes the ID.
   100  //
   101  // See also EncodeToString.
   102  func (x *ID) DecodeString(s string) error {
   103  	var err error
   104  
   105  	x.w, err = base58.Decode(s)
   106  	if err != nil {
   107  		return fmt.Errorf("decode base58: %w", err)
   108  	}
   109  
   110  	return nil
   111  }
   112  
   113  // String implements fmt.Stringer.
   114  //
   115  // String is designed to be human-readable, and its format MAY differ between
   116  // SDK versions. String MAY return same result as EncodeToString. String MUST NOT
   117  // be used to encode ID into FrostFS protocol string.
   118  func (x ID) String() string {
   119  	return x.EncodeToString()
   120  }
   121  
   122  // Equals defines a comparison relation between two ID instances.
   123  func (x ID) Equals(x2 ID) bool {
   124  	return bytes.Equal(x.w, x2.w)
   125  }
   126  
   127  // IsEmpty returns True, if ID is empty value.
   128  func (x ID) IsEmpty() bool {
   129  	return bytes.Equal(zeroSlice, x.w)
   130  }