frostfs-sdk-go/user/id.go
Ekaterina Lebedeva 00cebd297f [#303] user: Make user.ID a util.Uint160
Signed-off-by: Ekaterina Lebedeva <ekaterina.lebedeva@yadro.com>
2025-01-28 18:01:21 +03:00

125 lines
3.2 KiB
Go

package user
import (
"bytes"
"errors"
"fmt"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/refs"
"github.com/mr-tron/base58"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/util"
)
// idFullSize is the size of ID in bytes, including prefix and checksum.
const idFullSize = util.Uint160Size + 5
// ID identifies users of the FrostFS system.
//
// ID is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-sdk-go/api/refs.OwnerID
// message. See ReadFromV2 / WriteToV2 methods.
//
// Instances can be created using built-in var declaration. Zero ID is not valid,
// so it MUST be initialized using some modifying function (e.g. SetScriptHash,
// IDFromKey, etc.).
type ID struct {
w util.Uint160
}
// ReadFromV2 reads ID from the refs.OwnerID message. Returns an error if
// the message is malformed according to the FrostFS API V2 protocol.
//
// See also WriteToV2.
func (x *ID) ReadFromV2(m refs.OwnerID) error {
return x.setUserID(m.GetValue())
}
// WriteToV2 writes ID to the refs.OwnerID message.
// The message must not be nil.
//
// See also ReadFromV2.
func (x ID) WriteToV2(m *refs.OwnerID) {
m.SetValue(x.WalletBytes())
}
// SetScriptHash forms user ID from wallet address scripthash.
func (x *ID) SetScriptHash(scriptHash util.Uint160) {
x.w = scriptHash
}
// ScriptHash calculates and returns script hash of ID.
func (x *ID) ScriptHash() util.Uint160 {
return x.w
}
// WalletBytes returns FrostFS user ID as Neo3 wallet address in a binary format.
//
// Return value MUST NOT be mutated: to do this, first make a copy.
//
// See also Neo3 wallet docs.
func (x ID) WalletBytes() []byte {
v := make([]byte, idFullSize)
v[0] = address.Prefix
copy(v[1:], x.w[:])
copy(v[21:], hash.Checksum(v[:21]))
return v
}
// EncodeToString encodes ID into FrostFS API V2 protocol string.
//
// See also DecodeString.
func (x ID) EncodeToString() string {
return base58.Encode(x.WalletBytes())
}
// DecodeString decodes FrostFS API V2 protocol string. Returns an error
// if s is malformed.
//
// DecodeString always changes the ID.
//
// See also EncodeToString.
func (x *ID) DecodeString(s string) error {
w, err := base58.Decode(s)
if err != nil {
return fmt.Errorf("decode base58: %w", err)
}
return x.setUserID(w)
}
// String implements fmt.Stringer.
//
// String is designed to be human-readable, and its format MAY differ between
// SDK versions. String MAY return same result as EncodeToString. String MUST NOT
// be used to encode ID into FrostFS protocol string.
func (x ID) String() string {
return x.EncodeToString()
}
// Equals defines a comparison relation between two ID instances.
func (x ID) Equals(x2 ID) bool {
return x.w == x2.w
}
// IsEmpty returns True, if ID is empty value.
func (x ID) IsEmpty() bool {
return x.w == util.Uint160{}
}
func (x *ID) setUserID(w []byte) error {
if len(w) != idFullSize {
return fmt.Errorf("invalid length %d, expected %d", len(w), idFullSize)
}
if w[0] != address.NEO3Prefix {
return fmt.Errorf("invalid prefix byte 0x%X, expected 0x%X", w[0], address.NEO3Prefix)
}
if !bytes.Equal(w[21:], hash.Checksum(w[:21])) {
return errors.New("checksum mismatch")
}
copy(x.w[:], w[1:21])
return nil
}