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 }