2022-04-11 06:30:22 +00:00
|
|
|
package user
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
|
2023-03-07 11:20:03 +00:00
|
|
|
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
|
2022-04-11 06:30:22 +00:00
|
|
|
"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"
|
|
|
|
)
|
|
|
|
|
2023-11-21 08:35:10 +00:00
|
|
|
const idSize = 25
|
|
|
|
|
|
|
|
var zeroSlice = bytes.Repeat([]byte{0}, idSize)
|
|
|
|
|
2022-12-29 10:46:18 +00:00
|
|
|
// ID identifies users of the FrostFS system.
|
2022-04-11 06:30:22 +00:00
|
|
|
//
|
2023-03-07 11:20:03 +00:00
|
|
|
// ID is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs.OwnerID
|
2022-04-11 06:30:22 +00:00
|
|
|
// 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 []byte
|
|
|
|
}
|
|
|
|
|
|
|
|
// ReadFromV2 reads ID from the refs.OwnerID message. Returns an error if
|
2022-12-29 10:46:18 +00:00
|
|
|
// the message is malformed according to the FrostFS API V2 protocol.
|
2022-04-11 06:30:22 +00:00
|
|
|
//
|
|
|
|
// See also WriteToV2.
|
|
|
|
func (x *ID) ReadFromV2(m refs.OwnerID) error {
|
|
|
|
w := m.GetValue()
|
2023-11-21 08:35:10 +00:00
|
|
|
if len(w) != idSize {
|
|
|
|
return fmt.Errorf("invalid length %d, expected %d", len(w), idSize)
|
2022-04-11 06:30:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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")
|
|
|
|
}
|
|
|
|
|
|
|
|
x.w = w
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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.w)
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetScriptHash forms user ID from wallet address scripthash.
|
|
|
|
func (x *ID) SetScriptHash(scriptHash util.Uint160) {
|
2023-11-21 08:35:10 +00:00
|
|
|
if cap(x.w) < idSize {
|
|
|
|
x.w = make([]byte, idSize)
|
|
|
|
} else if len(x.w) < idSize {
|
|
|
|
x.w = x.w[:idSize]
|
2022-04-11 06:30:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
x.w[0] = address.Prefix
|
|
|
|
copy(x.w[1:], scriptHash.BytesBE())
|
|
|
|
copy(x.w[21:], hash.Checksum(x.w[:21]))
|
|
|
|
}
|
|
|
|
|
2024-01-26 11:41:55 +00:00
|
|
|
// ScriptHash calculates and returns script hash of ID.
|
|
|
|
func (x *ID) ScriptHash() (util.Uint160, error) {
|
|
|
|
return util.Uint160DecodeBytesBE(x.w[1:21])
|
|
|
|
}
|
|
|
|
|
2022-12-29 10:46:18 +00:00
|
|
|
// WalletBytes returns FrostFS user ID as Neo3 wallet address in a binary format.
|
2022-04-11 06:30:22 +00:00
|
|
|
//
|
|
|
|
// Return value MUST NOT be mutated: to do this, first make a copy.
|
|
|
|
//
|
|
|
|
// See also Neo3 wallet docs.
|
|
|
|
func (x ID) WalletBytes() []byte {
|
|
|
|
return x.w
|
|
|
|
}
|
|
|
|
|
2022-12-29 10:46:18 +00:00
|
|
|
// EncodeToString encodes ID into FrostFS API V2 protocol string.
|
2022-04-11 06:30:22 +00:00
|
|
|
//
|
|
|
|
// See also DecodeString.
|
|
|
|
func (x ID) EncodeToString() string {
|
|
|
|
return base58.Encode(x.w)
|
|
|
|
}
|
|
|
|
|
2022-12-29 10:46:18 +00:00
|
|
|
// DecodeString decodes FrostFS API V2 protocol string. Returns an error
|
2022-04-11 06:30:22 +00:00
|
|
|
// if s is malformed.
|
|
|
|
//
|
|
|
|
// DecodeString always changes the ID.
|
|
|
|
//
|
|
|
|
// See also EncodeToString.
|
|
|
|
func (x *ID) DecodeString(s string) error {
|
|
|
|
var err error
|
|
|
|
|
|
|
|
x.w, err = base58.Decode(s)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("decode base58: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
2022-12-29 10:46:18 +00:00
|
|
|
// be used to encode ID into FrostFS protocol string.
|
2022-04-11 06:30:22 +00:00
|
|
|
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 bytes.Equal(x.w, x2.w)
|
|
|
|
}
|
2023-11-21 08:35:10 +00:00
|
|
|
|
|
|
|
// IsEmpty returns True, if ID is empty value.
|
|
|
|
func (x ID) IsEmpty() bool {
|
|
|
|
return bytes.Equal(zeroSlice, x.w)
|
|
|
|
}
|