[#199] owner: Rename to user, refactor and doc

Signed-off-by: Leonard Lyubich <leonard@nspcc.ru>
This commit is contained in:
Leonard Lyubich 2022-04-11 09:30:22 +03:00 committed by LeL
parent d20999113a
commit 1ed426b8a6
36 changed files with 546 additions and 557 deletions

View file

@ -12,7 +12,7 @@ import (
neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
neofsecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa"
"github.com/nspcc-dev/neofs-sdk-go/eacl"
"github.com/nspcc-dev/neofs-sdk-go/owner"
"github.com/nspcc-dev/neofs-sdk-go/user"
)
var (
@ -175,11 +175,11 @@ func (b Token) EACLTable() eacl.Table {
return *eacl.NewTableFromV2(v2.GetBody().GetEACL())
}
// SetOwnerID sets owner.ID value of the user who can attach bearer token to
// SetOwnerID sets user.ID value of the user who can attach bearer token to
// its requests.
//
// See also OwnerID.
func (b *Token) SetOwnerID(id owner.ID) {
func (b *Token) SetOwnerID(id user.ID) {
v2 := (*acl.BearerToken)(b)
body := v2.GetBody()
@ -187,17 +187,28 @@ func (b *Token) SetOwnerID(id owner.ID) {
body = new(acl.BearerTokenBody)
}
body.SetOwnerID(id.ToV2())
var idV2 refs.OwnerID
id.WriteToV2(&idV2)
body.SetOwnerID(&idV2)
v2.SetBody(body)
}
// OwnerID returns owner.ID value of the user who can attach bearer token to
// OwnerID returns user.ID value of the user who can attach bearer token to
// its requests.
//
// See also SetOwnerID.
func (b Token) OwnerID() owner.ID {
func (b Token) OwnerID() user.ID {
v2 := (acl.BearerToken)(b)
return *owner.NewIDFromV2(v2.GetBody().GetOwnerID())
var id user.ID
idV2 := v2.GetBody().GetOwnerID()
if idV2 != nil {
_ = id.ReadFromV2(*idV2)
}
return id
}
// Sign signs bearer token. This method should be invoked with the private
@ -261,21 +272,23 @@ func (b Token) VerifySignature() error {
return nil
}
// Issuer returns owner.ID associated with the key that signed bearer token.
// Issuer returns user.ID associated with the key that signed bearer token.
// To pass node validation it should be owner of requested container.
//
// If token is not signed, Issuer returns empty owner ID and false `ok` flag.
//
// See also Sign.
func (b Token) Issuer() (id owner.ID, ok bool) {
func (b Token) Issuer() (id user.ID, ok bool) {
v2 := (acl.BearerToken)(b)
pub, _ := keys.NewPublicKeyFromBytes(v2.GetSignature().GetKey(), elliptic.P256())
if pub == nil {
return id, false
ok = pub != nil
if ok {
user.IDFromKey(&id, (ecdsa.PublicKey)(*pub))
}
return *owner.NewIDFromPublicKey((*ecdsa.PublicKey)(pub)), true
return
}
// sanityCheck if bearer token is ready to be issued.

View file

@ -1,14 +1,13 @@
package bearer_test
import (
"crypto/ecdsa"
"testing"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neofs-sdk-go/bearer"
tokentest "github.com/nspcc-dev/neofs-sdk-go/bearer/test"
"github.com/nspcc-dev/neofs-sdk-go/eacl"
"github.com/nspcc-dev/neofs-sdk-go/owner"
"github.com/nspcc-dev/neofs-sdk-go/user"
"github.com/stretchr/testify/require"
)
@ -24,13 +23,14 @@ func TestBearerToken_Issuer(t *testing.T) {
p, err := keys.NewPrivateKey()
require.NoError(t, err)
ownerID := owner.NewIDFromPublicKey((*ecdsa.PublicKey)(p.PublicKey()))
var id user.ID
user.IDFromKey(&id, p.PrivateKey.PublicKey)
bearerToken.SetEACLTable(*eacl.NewTable())
require.NoError(t, bearerToken.Sign(p.PrivateKey))
issuer, ok := bearerToken.Issuer()
require.True(t, ok)
require.True(t, ownerID.Equal(&issuer))
require.True(t, id.Equals(issuer))
})
}

View file

@ -3,7 +3,7 @@ package bearertest
import (
"github.com/nspcc-dev/neofs-sdk-go/bearer"
eacltest "github.com/nspcc-dev/neofs-sdk-go/eacl/test"
ownertest "github.com/nspcc-dev/neofs-sdk-go/owner/test"
usertest "github.com/nspcc-dev/neofs-sdk-go/user/test"
)
// Token returns random bearer.Token.
@ -13,7 +13,7 @@ func Token() (t bearer.Token) {
t.SetExpiration(3)
t.SetNotBefore(2)
t.SetIssuedAt(1)
t.SetOwnerID(*ownertest.ID())
t.SetOwnerID(*usertest.ID())
t.SetEACLTable(*eacltest.Table())
return t

View file

@ -4,25 +4,26 @@ import (
"context"
v2accounting "github.com/nspcc-dev/neofs-api-go/v2/accounting"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc"
"github.com/nspcc-dev/neofs-api-go/v2/rpc/client"
"github.com/nspcc-dev/neofs-sdk-go/accounting"
"github.com/nspcc-dev/neofs-sdk-go/owner"
"github.com/nspcc-dev/neofs-sdk-go/user"
)
// PrmBalanceGet groups parameters of BalanceGet operation.
type PrmBalanceGet struct {
prmCommonMeta
ownerSet bool
ownerID owner.ID
accountSet bool
account user.ID
}
// SetAccount sets identifier of the NeoFS account for which the balance is requested.
// Required parameter. Must be a valid ID according to NeoFS API protocol.
func (x *PrmBalanceGet) SetAccount(id owner.ID) {
x.ownerID = id
x.ownerSet = true
// Required parameter.
func (x *PrmBalanceGet) SetAccount(id user.ID) {
x.account = id
x.accountSet = true
}
// ResBalanceGet groups resulting values of BalanceGet operation.
@ -60,16 +61,16 @@ func (c *Client) BalanceGet(ctx context.Context, prm PrmBalanceGet) (*ResBalance
switch {
case ctx == nil:
panic(panicMsgMissingContext)
case !prm.ownerSet:
case !prm.accountSet:
panic("account not set")
case !prm.ownerID.Valid():
panic("invalid account ID")
}
// form request body
var body v2accounting.BalanceRequestBody
var accountV2 refs.OwnerID
prm.account.WriteToV2(&accountV2)
body.SetOwnerID(prm.ownerID.ToV2())
var body v2accounting.BalanceRequestBody
body.SetOwnerID(&accountV2)
// form request
var req v2accounting.BalanceRequest

View file

@ -14,8 +14,8 @@ import (
neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
neofsecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa"
"github.com/nspcc-dev/neofs-sdk-go/eacl"
"github.com/nspcc-dev/neofs-sdk-go/owner"
"github.com/nspcc-dev/neofs-sdk-go/session"
"github.com/nspcc-dev/neofs-sdk-go/user"
)
// PrmContainerPut groups parameters of ContainerPut operation.
@ -269,12 +269,12 @@ type PrmContainerList struct {
prmCommonMeta
ownerSet bool
ownerID owner.ID
ownerID user.ID
}
// SetAccount sets identifier of the NeoFS account to list the containers.
// Required parameter. Must be a valid ID according to NeoFS API protocol.
func (x *PrmContainerList) SetAccount(id owner.ID) {
// Required parameter.
func (x *PrmContainerList) SetAccount(id user.ID) {
x.ownerID = id
x.ownerSet = true
}
@ -317,13 +317,14 @@ func (c *Client) ContainerList(ctx context.Context, prm PrmContainerList) (*ResC
panic(panicMsgMissingContext)
case !prm.ownerSet:
panic("account not set")
case !prm.ownerID.Valid():
panic("invalid account")
}
// form request body
var ownerV2 refs.OwnerID
prm.ownerID.WriteToV2(&ownerV2)
reqBody := new(v2container.ListRequestBody)
reqBody.SetOwnerID(prm.ownerID.ToV2())
reqBody.SetOwnerID(&ownerV2)
// form request
var req v2container.ListRequest

View file

@ -3,10 +3,11 @@ package client
import (
"context"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc"
"github.com/nspcc-dev/neofs-api-go/v2/rpc/client"
v2session "github.com/nspcc-dev/neofs-api-go/v2/session"
"github.com/nspcc-dev/neofs-sdk-go/owner"
"github.com/nspcc-dev/neofs-sdk-go/user"
)
// PrmSessionCreate groups parameters of SessionCreate operation.
@ -71,11 +72,15 @@ func (c *Client) SessionCreate(ctx context.Context, prm PrmSessionCreate) (*ResS
panic(panicMsgMissingContext)
}
ownerID := owner.NewIDFromPublicKey(&c.prm.key.PublicKey)
var ownerID user.ID
user.IDFromKey(&ownerID, c.prm.key.PublicKey)
var ownerIDV2 refs.OwnerID
ownerID.WriteToV2(&ownerIDV2)
// form request body
reqBody := new(v2session.CreateRequestBody)
reqBody.SetOwnerID(ownerID.ToV2())
reqBody.SetOwnerID(&ownerIDV2)
reqBody.SetExpiration(prm.exp)
// for request

View file

@ -10,8 +10,8 @@ import (
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
"github.com/nspcc-dev/neofs-sdk-go/netmap"
"github.com/nspcc-dev/neofs-sdk-go/owner"
"github.com/nspcc-dev/neofs-sdk-go/session"
"github.com/nspcc-dev/neofs-sdk-go/user"
"github.com/nspcc-dev/neofs-sdk-go/version"
)
@ -113,12 +113,24 @@ func (c *Container) SetVersion(v *version.Version) {
c.v2.SetVersion(&verV2)
}
func (c *Container) OwnerID() *owner.ID {
return owner.NewIDFromV2(c.v2.GetOwnerID())
func (c *Container) OwnerID() *user.ID {
m := c.v2.GetOwnerID()
if m == nil {
return nil
}
var id user.ID
_ = id.ReadFromV2(*m)
return &id
}
func (c *Container) SetOwnerID(v *owner.ID) {
c.v2.SetOwnerID(v.ToV2())
func (c *Container) SetOwnerID(v *user.ID) {
var m refs.OwnerID
v.WriteToV2(&m)
c.v2.SetOwnerID(&m)
}
// Returns container nonce in UUID format.

View file

@ -9,8 +9,8 @@ import (
"github.com/nspcc-dev/neofs-sdk-go/container"
containertest "github.com/nspcc-dev/neofs-sdk-go/container/test"
netmaptest "github.com/nspcc-dev/neofs-sdk-go/netmap/test"
ownertest "github.com/nspcc-dev/neofs-sdk-go/owner/test"
sessiontest "github.com/nspcc-dev/neofs-sdk-go/session/test"
usertest "github.com/nspcc-dev/neofs-sdk-go/user/test"
"github.com/nspcc-dev/neofs-sdk-go/version"
versiontest "github.com/nspcc-dev/neofs-sdk-go/version/test"
"github.com/stretchr/testify/require"
@ -21,7 +21,7 @@ func TestNewContainer(t *testing.T) {
nonce := uuid.New()
ownerID := ownertest.ID()
ownerID := usertest.ID()
policy := netmaptest.PlacementPolicy()
c.SetBasicACL(acl.PublicBasicRule)

View file

@ -6,7 +6,7 @@ import (
"github.com/google/uuid"
"github.com/nspcc-dev/neofs-sdk-go/acl"
"github.com/nspcc-dev/neofs-sdk-go/netmap"
"github.com/nspcc-dev/neofs-sdk-go/owner"
"github.com/nspcc-dev/neofs-sdk-go/user"
)
type (
@ -16,7 +16,7 @@ type (
acl acl.BasicACL
policy *netmap.PlacementPolicy
attributes Attributes
owner *owner.ID
owner *user.ID
nonce uuid.UUID
}
)
@ -57,7 +57,7 @@ func WithNonce(nonce uuid.UUID) Option {
}
}
func WithOwnerID(id *owner.ID) Option {
func WithOwnerID(id *user.ID) Option {
return func(option *containerOptions) {
option.owner = id
}
@ -66,10 +66,10 @@ func WithOwnerID(id *owner.ID) Option {
func WithOwnerPublicKey(pub *ecdsa.PublicKey) Option {
return func(option *containerOptions) {
if option.owner == nil {
option.owner = new(owner.ID)
option.owner = new(user.ID)
}
option.owner.SetPublicKey(pub)
user.IDFromKey(option.owner, *pub)
}
}

View file

@ -4,7 +4,7 @@ import (
"github.com/nspcc-dev/neofs-sdk-go/container"
cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test"
netmaptest "github.com/nspcc-dev/neofs-sdk-go/netmap/test"
ownertest "github.com/nspcc-dev/neofs-sdk-go/owner/test"
usertest "github.com/nspcc-dev/neofs-sdk-go/user/test"
versiontest "github.com/nspcc-dev/neofs-sdk-go/version/test"
)
@ -30,7 +30,7 @@ func Container() *container.Container {
x.SetVersion(&ver)
x.SetAttributes(Attributes())
x.SetOwnerID(ownertest.ID())
x.SetOwnerID(usertest.ID())
x.SetBasicACL(123)
x.SetPlacementPolicy(netmaptest.PlacementPolicy())

View file

@ -9,7 +9,7 @@ import (
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
"github.com/nspcc-dev/neofs-sdk-go/object"
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
"github.com/nspcc-dev/neofs-sdk-go/owner"
"github.com/nspcc-dev/neofs-sdk-go/user"
"github.com/nspcc-dev/neofs-sdk-go/version"
)
@ -122,7 +122,7 @@ func (r *Record) AddObjectContainerIDFilter(m Match, id cid.ID) {
}
// AddObjectOwnerIDFilter adds filter by object owner ID.
func (r *Record) AddObjectOwnerIDFilter(m Match, id *owner.ID) {
func (r *Record) AddObjectOwnerIDFilter(m Match, id *user.ID) {
r.addObjectReservedFilter(m, fKeyObjOwnerID, id)
}

View file

@ -11,7 +11,7 @@ import (
cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test"
"github.com/nspcc-dev/neofs-sdk-go/object"
oidtest "github.com/nspcc-dev/neofs-sdk-go/object/id/test"
ownertest "github.com/nspcc-dev/neofs-sdk-go/owner/test"
usertest "github.com/nspcc-dev/neofs-sdk-go/user/test"
versiontest "github.com/nspcc-dev/neofs-sdk-go/version/test"
"github.com/stretchr/testify/require"
)
@ -154,7 +154,7 @@ func TestReservedRecords(t *testing.T) {
v = versiontest.Version()
oid = oidtest.ID()
cid = cidtest.ID()
ownerid = ownertest.ID()
ownerid = usertest.ID()
h = checksumtest.Checksum()
typ = new(object.Type)
)

View file

@ -3,7 +3,7 @@ package eacltest
import (
cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test"
"github.com/nspcc-dev/neofs-sdk-go/eacl"
ownertest "github.com/nspcc-dev/neofs-sdk-go/owner/test"
usertest "github.com/nspcc-dev/neofs-sdk-go/user/test"
versiontest "github.com/nspcc-dev/neofs-sdk-go/version/test"
)
@ -28,7 +28,7 @@ func Record() *eacl.Record {
x.SetOperation(eacl.OperationRangeHash)
x.SetTargets(*Target(), *Target())
x.AddObjectContainerIDFilter(eacl.MatchStringEqual, cidtest.ID())
x.AddObjectOwnerIDFilter(eacl.MatchStringNotEqual, ownertest.ID())
x.AddObjectOwnerIDFilter(eacl.MatchStringNotEqual, usertest.ID())
return x
}

View file

@ -10,8 +10,8 @@ import (
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
"github.com/nspcc-dev/neofs-sdk-go/owner"
"github.com/nspcc-dev/neofs-sdk-go/session"
"github.com/nspcc-dev/neofs-sdk-go/user"
"github.com/nspcc-dev/neofs-sdk-go/version"
)
@ -30,8 +30,8 @@ type RequiredFields struct {
// Identifier of the NeoFS container associated with the object.
Container cid.ID
// Object owner ID in the NeoFS system.
Owner owner.ID
// Object owner's user ID in the NeoFS system.
Owner user.ID
}
// InitCreation initializes the object instance with minimum set of required fields.
@ -201,18 +201,24 @@ func (o *Object) SetContainerID(v cid.ID) {
}
// OwnerID returns identifier of the object owner.
func (o *Object) OwnerID() *owner.ID {
return owner.NewIDFromV2(
(*object.Object)(o).
GetHeader().
GetOwnerID(),
)
func (o *Object) OwnerID() *user.ID {
var id user.ID
m := (*object.Object)(o).GetHeader().GetOwnerID()
if m != nil {
_ = id.ReadFromV2(*m)
}
return &id
}
// SetOwnerID sets identifier of the object owner.
func (o *Object) SetOwnerID(v *owner.ID) {
func (o *Object) SetOwnerID(v *user.ID) {
o.setHeaderField(func(h *object.Header) {
h.SetOwnerID(v.ToV2())
var m refs.OwnerID
v.WriteToV2(&m)
h.SetOwnerID(&m)
})
}

View file

@ -5,14 +5,14 @@ import (
cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test"
"github.com/nspcc-dev/neofs-sdk-go/object"
ownertest "github.com/nspcc-dev/neofs-sdk-go/owner/test"
usertest "github.com/nspcc-dev/neofs-sdk-go/user/test"
"github.com/stretchr/testify/require"
)
func TestInitCreation(t *testing.T) {
var o object.Object
cnr := cidtest.ID()
own := *ownertest.ID()
own := *usertest.ID()
object.InitCreation(&o, object.RequiredFields{
Container: cnr,

View file

@ -9,8 +9,8 @@ import (
"github.com/nspcc-dev/neofs-sdk-go/checksum"
cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test"
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
ownertest "github.com/nspcc-dev/neofs-sdk-go/owner/test"
sessiontest "github.com/nspcc-dev/neofs-sdk-go/session/test"
usertest "github.com/nspcc-dev/neofs-sdk-go/user/test"
"github.com/nspcc-dev/neofs-sdk-go/version"
"github.com/stretchr/testify/require"
)
@ -95,7 +95,7 @@ func TestObject_SetContainerID(t *testing.T) {
func TestObject_SetOwnerID(t *testing.T) {
obj := New()
ownerID := ownertest.ID()
ownerID := usertest.ID()
obj.SetOwnerID(ownerID)

View file

@ -8,7 +8,7 @@ import (
v2object "github.com/nspcc-dev/neofs-api-go/v2/object"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
"github.com/nspcc-dev/neofs-sdk-go/owner"
"github.com/nspcc-dev/neofs-sdk-go/user"
"github.com/nspcc-dev/neofs-sdk-go/version"
)
@ -224,7 +224,7 @@ func (f *SearchFilters) AddObjectContainerIDFilter(m SearchMatchType, id cid.ID)
f.addReservedFilter(m, fKeyContainerID, id)
}
func (f *SearchFilters) AddObjectOwnerIDFilter(m SearchMatchType, id *owner.ID) {
func (f *SearchFilters) AddObjectOwnerIDFilter(m SearchMatchType, id *user.ID) {
f.addReservedFilter(m, fKeyOwnerID, id)
}

View file

@ -7,8 +7,8 @@ import (
"github.com/nspcc-dev/neofs-sdk-go/object"
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
oidtest "github.com/nspcc-dev/neofs-sdk-go/object/id/test"
ownertest "github.com/nspcc-dev/neofs-sdk-go/owner/test"
sessiontest "github.com/nspcc-dev/neofs-sdk-go/session/test"
usertest "github.com/nspcc-dev/neofs-sdk-go/user/test"
"github.com/nspcc-dev/neofs-sdk-go/version"
)
@ -48,7 +48,7 @@ func generate(withParent bool) *object.Object {
x.SetID(oidtest.ID())
x.SetSessionToken(sessiontest.Token())
x.SetPayload([]byte{1, 2, 3})
x.SetOwnerID(ownertest.ID())
x.SetOwnerID(usertest.ID())
x.SetContainerID(cidtest.ID())
x.SetType(object.TypeTombstone)
x.SetVersion(&ver)

View file

@ -1,46 +0,0 @@
package owner
import (
"crypto/ecdsa"
"crypto/elliptic"
"encoding/hex"
"testing"
"github.com/mr-tron/base58"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/stretchr/testify/require"
)
func TestNEO3WalletFromPublicKey(t *testing.T) {
rawPub, _ := hex.DecodeString("0369b7b6c49fb937f3de52af189b91069767679c2739798d85f2ed69c079940680")
x, y := elliptic.UnmarshalCompressed(elliptic.P256(), rawPub)
require.True(t, x != nil && y != nil)
expected := "35ee628f21922d7308f1bd71f03a0d8ba89c4e7372fca1442c"
actual := PublicKeyToIDBytes(&ecdsa.PublicKey{Curve: elliptic.P256(), X: x, Y: y})
require.Equal(t, expected, hex.EncodeToString(actual))
}
func TestPublicKeyToBytes(t *testing.T) {
p, err := keys.NewPrivateKey()
require.NoError(t, err)
expected, err := base58.Decode(p.PublicKey().Address())
require.NoError(t, err)
actual := PublicKeyToIDBytes((*ecdsa.PublicKey)(p.PublicKey()))
require.Equal(t, expected, actual)
require.Equal(t, NEO3WalletSize, len(actual))
}
func TestScriptHashToIDBytes(t *testing.T) {
p, err := keys.NewPrivateKey()
require.NoError(t, err)
expected, err := base58.Decode(p.PublicKey().Address())
require.NoError(t, err)
actual := ScriptHashToIDBytes(p.GetScriptHash())
require.Equal(t, expected, actual)
require.Equal(t, NEO3WalletSize, len(actual))
}

View file

@ -1,163 +0,0 @@
package owner
import (
"bytes"
"crypto/ecdsa"
"errors"
"fmt"
"github.com/mr-tron/base58"
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
)
// ID represents v2-compatible owner identifier.
type ID refs.OwnerID
var errInvalidIDString = errors.New("incorrect format of the string owner ID")
// ErrEmptyPublicKey when public key passed to Verify method is nil.
var ErrEmptyPublicKey = errors.New("empty public key")
// NEO3WalletSize contains size of neo3 wallet.
const NEO3WalletSize = 25
// NewIDFromV2 wraps v2 OwnerID message to ID.
//
// Nil refs.OwnerID converts to nil.
func NewIDFromV2(idV2 *refs.OwnerID) *ID {
return (*ID)(idV2)
}
// NewID creates and initializes blank ID.
//
// Works similar as NewIDFromV2(new(OwnerID)).
//
// Defaults:
// - value: nil.
func NewID() *ID {
return NewIDFromV2(new(refs.OwnerID))
}
// SetPublicKey sets owner identifier value to the provided NEO3 public key.
func (id *ID) SetPublicKey(pub *ecdsa.PublicKey) {
(*refs.OwnerID)(id).SetValue(PublicKeyToIDBytes(pub))
}
// SetScriptHash sets owner identifier value to the provided NEO3 script hash.
func (id *ID) SetScriptHash(u util.Uint160) {
(*refs.OwnerID)(id).SetValue(ScriptHashToIDBytes(u))
}
// ToV2 returns the v2 owner ID message.
//
// Nil ID converts to nil.
func (id *ID) ToV2() *refs.OwnerID {
return (*refs.OwnerID)(id)
}
// String implements fmt.Stringer.
func (id *ID) String() string {
return base58.Encode((*refs.OwnerID)(id).GetValue())
}
// Equal defines a comparison relation on ID's.
//
// ID's are equal if they have the same binary representation.
func (id *ID) Equal(id2 *ID) bool {
return bytes.Equal(
(*refs.ObjectID)(id).GetValue(),
(*refs.ObjectID)(id2).GetValue(),
)
}
// NewIDFromPublicKey creates new owner identity from ECDSA public key.
func NewIDFromPublicKey(pub *ecdsa.PublicKey) *ID {
id := NewID()
id.SetPublicKey(pub)
return id
}
// NewIDFromN3Account creates new owner identity from N3 wallet account.
func NewIDFromN3Account(acc *wallet.Account) *ID {
return NewIDFromPublicKey(
(*ecdsa.PublicKey)(acc.PrivateKey().PublicKey()))
}
// Parse converts base58 string representation into ID.
func (id *ID) Parse(s string) error {
data, err := base58.Decode(s)
if err != nil {
return fmt.Errorf("could not parse owner.ID from string: %w", err)
} else if !valid(data) {
return errInvalidIDString
}
(*refs.OwnerID)(id).SetValue(data)
return nil
}
// Valid returns true if id is a valid owner id.
// The rules for v2 are the following:
// 1. Must be 25 bytes in length.
// 2. Must have N3 address prefix-byte.
// 3. Last 4 bytes must contain the address checksum.
func (id *ID) Valid() bool {
rawID := id.ToV2().GetValue()
return valid(rawID)
}
func valid(rawID []byte) bool {
if len(rawID) != NEO3WalletSize {
return false
}
if rawID[0] != address.NEO3Prefix {
return false
}
const boundIndex = NEO3WalletSize - 4
return bytes.Equal(rawID[boundIndex:], hash.Checksum(rawID[:boundIndex]))
}
// Marshal marshals ID into a protobuf binary form.
func (id *ID) Marshal() ([]byte, error) {
return (*refs.OwnerID)(id).StableMarshal(nil)
}
// Unmarshal unmarshals protobuf binary representation of ID.
func (id *ID) Unmarshal(data []byte) error {
return (*refs.OwnerID)(id).Unmarshal(data)
}
// MarshalJSON encodes ID to protobuf JSON format.
func (id *ID) MarshalJSON() ([]byte, error) {
return (*refs.OwnerID)(id).MarshalJSON()
}
// UnmarshalJSON decodes ID from protobuf JSON format.
func (id *ID) UnmarshalJSON(data []byte) error {
return (*refs.OwnerID)(id).UnmarshalJSON(data)
}
// PublicKeyToIDBytes converts public key to a byte slice of NEO3WalletSize length.
// It is similar to decoding a NEO3 address but is inlined to skip base58 encoding-decoding step
// make it clear that no errors can occur.
func PublicKeyToIDBytes(pub *ecdsa.PublicKey) []byte {
sh := (*keys.PublicKey)(pub).GetScriptHash()
return ScriptHashToIDBytes(sh)
}
// ScriptHashToIDBytes converts NEO3 script hash to a byte slice of NEO3WalletSize length.
func ScriptHashToIDBytes(sh util.Uint160) []byte {
b := make([]byte, NEO3WalletSize)
b[0] = address.Prefix
copy(b[1:], sh.BytesBE())
copy(b[21:], hash.Checksum(b[:21]))
return b
}

View file

@ -1,174 +0,0 @@
package owner_test
import (
"crypto/ecdsa"
"testing"
"github.com/mr-tron/base58"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neo-go/pkg/util/slice"
"github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
. "github.com/nspcc-dev/neofs-sdk-go/owner"
ownertest "github.com/nspcc-dev/neofs-sdk-go/owner/test"
"github.com/stretchr/testify/require"
)
func TestIDV2(t *testing.T) {
id := ownertest.ID()
idV2 := id.ToV2()
require.Equal(t, id, NewIDFromV2(idV2))
}
func TestID_Valid(t *testing.T) {
id := ownertest.ID()
require.True(t, id.Valid())
val := id.ToV2().GetValue()
t.Run("invalid prefix", func(t *testing.T) {
val := slice.Copy(val)
val[0] ^= 0xFF
id := ownertest.IDFromBytes(val)
require.False(t, id.Valid())
})
t.Run("invalid size", func(t *testing.T) {
val := val[:NEO3WalletSize-1]
id := ownertest.IDFromBytes(val)
require.False(t, id.Valid())
})
t.Run("invalid checksum", func(t *testing.T) {
val := slice.Copy(val)
val[NEO3WalletSize-1] ^= 0xFF
id := ownertest.IDFromBytes(val)
require.False(t, id.Valid())
})
}
func TestNewIDFromN3Account(t *testing.T) {
acc, err := wallet.NewAccount()
require.NoError(t, err)
id := NewIDFromN3Account(acc)
require.Equal(t, id.String(), acc.Address)
}
func TestNewIDFromPublicKey(t *testing.T) {
p, err := keys.NewPrivateKey()
require.NoError(t, err)
id := NewIDFromPublicKey((*ecdsa.PublicKey)(p.PublicKey()))
require.Equal(t, id.String(), p.Address())
}
func TestID_Parse(t *testing.T) {
t.Run("should parse successful", func(t *testing.T) {
acc, err := wallet.NewAccount()
require.NoError(t, err)
eid := NewIDFromN3Account(acc)
aid := NewID()
require.NoError(t, aid.Parse(eid.String()))
require.Equal(t, eid, aid)
})
t.Run("should failure on parse", func(t *testing.T) {
cs := []byte{1, 2, 3, 4, 5, 6}
str := base58.Encode(cs)
cid := NewID()
require.Error(t, cid.Parse(str))
})
}
func TestIDEncoding(t *testing.T) {
id := ownertest.ID()
t.Run("binary", func(t *testing.T) {
data, err := id.Marshal()
require.NoError(t, err)
id2 := NewID()
require.NoError(t, id2.Unmarshal(data))
require.Equal(t, id, id2)
})
t.Run("json", func(t *testing.T) {
data, err := id.MarshalJSON()
require.NoError(t, err)
a2 := NewID()
require.NoError(t, a2.UnmarshalJSON(data))
require.Equal(t, id, a2)
})
}
func TestID_Equal(t *testing.T) {
var (
data1 = []byte{1, 2, 3}
data2 = data1
data3 = append(data1, 255)
)
id1 := ownertest.IDFromBytes(data1)
require.True(t, id1.Equal(
ownertest.IDFromBytes(data2),
))
require.False(t, id1.Equal(
ownertest.IDFromBytes(data3),
))
}
func TestNewIDFromV2(t *testing.T) {
t.Run("from nil", func(t *testing.T) {
var x *refs.OwnerID
require.Nil(t, NewIDFromV2(x))
})
}
func TestID_ToV2(t *testing.T) {
t.Run("nil", func(t *testing.T) {
var x *ID
require.Nil(t, x.ToV2())
})
}
func TestID_String(t *testing.T) {
t.Run("nil", func(t *testing.T) {
id := NewID()
require.Empty(t, id.String())
})
}
func TestNewID(t *testing.T) {
t.Run("default values", func(t *testing.T) {
id := NewID()
// convert to v2 message
idV2 := id.ToV2()
require.Nil(t, idV2.GetValue())
})
}
func TestID_SetScriptHash(t *testing.T) {
p, err := keys.NewPrivateKey()
require.NoError(t, err)
id := NewID()
id.SetScriptHash(p.GetScriptHash())
require.Equal(t, p.Address(), id.String())
}

View file

@ -1,34 +0,0 @@
package ownertest
import (
"math/rand"
"github.com/mr-tron/base58"
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
"github.com/nspcc-dev/neofs-sdk-go/owner"
)
// ID returns owner.ID calculated
// from a random owner.NEO3Wallet.
func ID() *owner.ID {
u := util.Uint160{}
rand.Read(u[:])
addr := address.Uint160ToString(u)
data, err := base58.Decode(addr)
if err != nil {
panic(err)
}
return IDFromBytes(data)
}
// IDFromBytes returns owner.ID generated
// from a passed byte slice.
func IDFromBytes(val []byte) *owner.ID {
idV2 := new(refs.OwnerID)
idV2.SetValue(val)
return owner.NewIDFromV2(idV2)
}

View file

@ -26,8 +26,8 @@ import (
"github.com/nspcc-dev/neofs-sdk-go/object"
"github.com/nspcc-dev/neofs-sdk-go/object/address"
oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
"github.com/nspcc-dev/neofs-sdk-go/owner"
"github.com/nspcc-dev/neofs-sdk-go/session"
"github.com/nspcc-dev/neofs-sdk-go/user"
"go.uber.org/zap"
)
@ -104,7 +104,7 @@ func newWrapper(prm wrapperPrm) (*clientWrapper, error) {
func (c *clientWrapper) balanceGet(ctx context.Context, prm PrmBalanceGet) (*accounting.Decimal, error) {
var cliPrm sdkClient.PrmBalanceGet
cliPrm.SetAccount(prm.ownerID)
cliPrm.SetAccount(prm.account)
res, err := c.client.BalanceGet(ctx, cliPrm)
if err != nil {
@ -775,11 +775,11 @@ func (x *PrmContainerGet) SetContainerID(cnrID cid.ID) {
// PrmContainerList groups parameters of ListContainers operation.
type PrmContainerList struct {
ownerID owner.ID
ownerID user.ID
}
// SetOwnerID specifies identifier of the NeoFS account to list the containers.
func (x *PrmContainerList) SetOwnerID(ownerID owner.ID) {
func (x *PrmContainerList) SetOwnerID(ownerID user.ID) {
x.ownerID = ownerID
}
@ -848,12 +848,12 @@ func (x *PrmContainerSetEACL) SetWaitParams(waitParams WaitParams) {
// PrmBalanceGet groups parameters of Balance operation.
type PrmBalanceGet struct {
ownerID owner.ID
account user.ID
}
// SetOwnerID specifies identifier of the NeoFS account for which the balance is requested.
func (x *PrmBalanceGet) SetOwnerID(ownerID owner.ID) {
x.ownerID = ownerID
// SetAccount specifies identifier of the NeoFS account for which the balance is requested.
func (x *PrmBalanceGet) SetAccount(id user.ID) {
x.account = id
}
// prmEndpointInfo groups parameters of sessionCreate operation.
@ -903,7 +903,7 @@ type resCreateSession struct {
type Pool struct {
innerPools []*innerPool
key *ecdsa.PrivateKey
owner *owner.ID
sessionOwner user.ID
cancel context.CancelFunc
closedCh chan struct{}
cache *sessionCache
@ -946,7 +946,6 @@ func NewPool(options InitParameters) (*Pool, error) {
pool := &Pool{
key: options.key,
owner: owner.NewIDFromPublicKey(&options.key.PublicKey),
cache: cache,
logger: options.logger,
stokenDuration: options.sessionExpirationDuration,
@ -959,6 +958,8 @@ func NewPool(options InitParameters) (*Pool, error) {
clientBuilder: options.clientBuilder,
}
user.IDFromKey(&pool.sessionOwner, options.key.PublicKey)
return pool, nil
}
@ -982,7 +983,7 @@ func (p *Pool) Dial(ctx context.Context) error {
return err
}
var healthy bool
st, err := createSessionTokenForDuration(ctx, c, p.owner, p.rebalanceParams.sessionExpirationDuration)
st, err := createSessionTokenForDuration(ctx, c, p.sessionOwner, p.rebalanceParams.sessionExpirationDuration)
if err != nil && p.logger != nil {
p.logger.Warn("failed to create neofs session token for client",
zap.String("Address", addr),
@ -1205,8 +1206,8 @@ func (p *innerPool) connection() (*clientPack, error) {
return nil, errors.New("no healthy client")
}
func (p *Pool) OwnerID() *owner.ID {
return p.owner
func (p *Pool) OwnerID() *user.ID {
return &p.sessionOwner
}
func formCacheKey(address string, key *ecdsa.PrivateKey) string {
@ -1228,7 +1229,7 @@ func (p *Pool) checkSessionTokenErr(err error, address string) bool {
return false
}
func createSessionTokenForDuration(ctx context.Context, c client, ownerID *owner.ID, dur uint64) (*session.Token, error) {
func createSessionTokenForDuration(ctx context.Context, c client, ownerID user.ID, dur uint64) (*session.Token, error) {
ni, err := c.networkInfo(ctx, prmNetworkInfo{})
if err != nil {
return nil, err
@ -1309,8 +1310,12 @@ func (p *Pool) openDefaultSession(ctx *callContext) error {
tok := p.cache.Get(cacheKey)
if tok == nil {
var err error
var sessionOwner user.ID
user.IDFromKey(&sessionOwner, ctx.key.PublicKey)
// open new session
tok, err = createSessionTokenForDuration(ctx, ctx.client, owner.NewIDFromPublicKey(&ctx.key.PublicKey), p.stokenDuration)
tok, err = createSessionTokenForDuration(ctx, ctx.client, sessionOwner, p.stokenDuration)
if err != nil {
return fmt.Errorf("session API client: %w", err)
}
@ -1784,9 +1789,9 @@ func (p *Pool) Close() {
}
// creates new session token with specified owner from SessionCreate call result.
func sessionTokenForOwner(id *owner.ID, cliRes *resCreateSession, exp uint64) *session.Token {
func sessionTokenForOwner(id user.ID, cliRes *resCreateSession, exp uint64) *session.Token {
st := session.NewToken()
st.SetOwnerID(id)
st.SetOwnerID(&id)
st.SetID(cliRes.id)
st.SetSessionKey(cliRes.sessionKey)
st.SetExp(exp)
@ -1815,15 +1820,7 @@ func copySessionTokenWithoutSignatureAndContext(from session.Token) (to session.
copy(sessionTokenKey, from.SessionKey())
to.SetSessionKey(sessionTokenKey)
var sessionTokenOwner owner.ID
buf, err := from.OwnerID().Marshal()
if err != nil {
panic(err) // should never happen
}
err = sessionTokenOwner.Unmarshal(buf)
if err != nil {
panic(err) // should never happen
}
sessionTokenOwner := *from.OwnerID()
to.SetOwnerID(&sessionTokenOwner)
return to

View file

@ -17,9 +17,9 @@ import (
"github.com/nspcc-dev/neofs-sdk-go/netmap"
"github.com/nspcc-dev/neofs-sdk-go/object"
"github.com/nspcc-dev/neofs-sdk-go/object/address"
"github.com/nspcc-dev/neofs-sdk-go/owner"
"github.com/nspcc-dev/neofs-sdk-go/session"
sessiontest "github.com/nspcc-dev/neofs-sdk-go/session/test"
"github.com/nspcc-dev/neofs-sdk-go/user"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
)
@ -594,7 +594,9 @@ func TestSessionTokenOwner(t *testing.T) {
t.Cleanup(p.Close)
anonKey := newPrivateKey(t)
anonOwner := owner.NewIDFromPublicKey(&anonKey.PublicKey)
var anonOwner user.ID
user.IDFromKey(&anonOwner, anonKey.PublicKey)
var prm prmCommon
prm.UseKey(anonKey)
@ -611,7 +613,7 @@ func TestSessionTokenOwner(t *testing.T) {
require.NoError(t, err)
tkn := p.cache.Get(formCacheKey("peer0", anonKey))
require.True(t, anonOwner.Equal(tkn.OwnerID()))
require.True(t, anonOwner.Equals(*tkn.OwnerID()))
}
func TestWaitPresence(t *testing.T) {

View file

@ -9,7 +9,7 @@ import (
"github.com/nspcc-dev/neofs-api-go/v2/session"
neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
neofsecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa"
"github.com/nspcc-dev/neofs-sdk-go/owner"
"github.com/nspcc-dev/neofs-sdk-go/user"
)
// Token represents NeoFS API v2-compatible
@ -72,18 +72,27 @@ func (t *Token) SetID(v []byte) {
}
// OwnerID returns Token's owner identifier.
func (t *Token) OwnerID() *owner.ID {
return owner.NewIDFromV2(
(*session.Token)(t).
GetBody().
GetOwnerID(),
)
func (t *Token) OwnerID() *user.ID {
m := (*session.Token)(t).GetBody().GetOwnerID()
if m == nil {
return nil
}
var res user.ID
_ = res.ReadFromV2(*m)
return &res
}
// SetOwnerID sets Token's owner identifier.
func (t *Token) SetOwnerID(v *owner.ID) {
func (t *Token) SetOwnerID(v *user.ID) {
t.setBodyField(func(body *session.TokenBody) {
body.SetOwnerID(v.ToV2())
var m refs.OwnerID
v.WriteToV2(&m)
body.SetOwnerID(&m)
})
}

View file

@ -4,9 +4,9 @@ import (
"testing"
sessionv2 "github.com/nspcc-dev/neofs-api-go/v2/session"
ownertest "github.com/nspcc-dev/neofs-sdk-go/owner/test"
"github.com/nspcc-dev/neofs-sdk-go/session"
sessiontest "github.com/nspcc-dev/neofs-sdk-go/session/test"
usertest "github.com/nspcc-dev/neofs-sdk-go/user/test"
"github.com/stretchr/testify/require"
)
@ -22,7 +22,7 @@ func TestSessionToken_SetID(t *testing.T) {
func TestSessionToken_SetOwnerID(t *testing.T) {
token := session.NewToken()
ownerID := ownertest.ID()
ownerID := usertest.ID()
token.SetOwnerID(ownerID)

View file

@ -7,8 +7,8 @@ import (
"github.com/google/uuid"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neofs-sdk-go/owner"
"github.com/nspcc-dev/neofs-sdk-go/session"
"github.com/nspcc-dev/neofs-sdk-go/user"
)
var p *keys.PrivateKey
@ -38,13 +38,14 @@ func Token() *session.Token {
panic(err)
}
ownerID := owner.NewID()
ownerID.SetPublicKey(&priv.PublicKey)
var ownerID user.ID
user.IDFromKey(&ownerID, priv.PublicKey)
keyBin := p.PublicKey().Bytes()
tok.SetID(uid)
tok.SetOwnerID(ownerID)
tok.SetOwnerID(&ownerID)
tok.SetSessionKey(keyBin)
tok.SetExp(11)
tok.SetNbf(22)

View file

@ -3,8 +3,8 @@ package subnet
import (
"github.com/nspcc-dev/neofs-api-go/v2/refs"
"github.com/nspcc-dev/neofs-api-go/v2/subnet"
"github.com/nspcc-dev/neofs-sdk-go/owner"
subnetid "github.com/nspcc-dev/neofs-sdk-go/subnet/id"
"github.com/nspcc-dev/neofs-sdk-go/user"
)
// Info represents information about NeoFS subnet.
@ -63,7 +63,7 @@ func (x Info) ReadID(id *subnetid.ID) {
}
// SetOwner sets subnet owner ID.
func (x *Info) SetOwner(id owner.ID) {
func (x *Info) SetOwner(id user.ID) {
infov2 := (*subnet.Info)(x)
idv2 := infov2.Owner()
@ -72,33 +72,32 @@ func (x *Info) SetOwner(id owner.ID) {
infov2.SetOwner(idv2)
}
// FIXME: we need to implement and use owner.ID.WriteToV2() method
*idv2 = *id.ToV2()
id.WriteToV2(idv2)
}
// ReadOwner reads the identifier of the subnet that Info describes.
// Must be called only if owner is set (see HasOwner). Arg must not be nil.
func (x Info) ReadOwner(id *owner.ID) {
func (x Info) ReadOwner(id *user.ID) {
infov2 := (subnet.Info)(x)
id2 := infov2.Owner()
if id2 == nil {
// TODO: implement owner.ID.Reset
*id = owner.ID{}
*id = user.ID{}
return
}
// TODO: we need to implement and use owner.ID.FromV2 method
*id = *owner.NewIDFromV2(infov2.Owner())
if ownerV2 := infov2.Owner(); ownerV2 != nil {
_ = id.ReadFromV2(*ownerV2)
}
}
// IsOwner checks subnet ownership.
func IsOwner(info Info, id owner.ID) bool {
id2 := new(owner.ID)
func IsOwner(info Info, id user.ID) bool {
var id2 user.ID
info.ReadOwner(id2)
info.ReadOwner(&id2)
return id.Equal(id2)
return id.Equals(id2)
}
// IDEquals checks if ID refers to subnet that Info describes.

View file

@ -5,10 +5,10 @@ import (
subnetv2 "github.com/nspcc-dev/neofs-api-go/v2/subnet"
subnettest "github.com/nspcc-dev/neofs-api-go/v2/subnet/test"
"github.com/nspcc-dev/neofs-sdk-go/owner"
ownertest "github.com/nspcc-dev/neofs-sdk-go/owner/test"
. "github.com/nspcc-dev/neofs-sdk-go/subnet"
subnetid "github.com/nspcc-dev/neofs-sdk-go/subnet/id"
"github.com/nspcc-dev/neofs-sdk-go/user"
usertest "github.com/nspcc-dev/neofs-sdk-go/user/test"
"github.com/stretchr/testify/require"
)
@ -36,11 +36,11 @@ func TestInfo_SetID(t *testing.T) {
func TestInfo_SetOwner(t *testing.T) {
var (
id owner.ID
id user.ID
info Info
)
id = *ownertest.ID()
id = *usertest.ID()
require.False(t, IsOwner(info, id))

55
user/doc.go Normal file
View file

@ -0,0 +1,55 @@
/*
Package user provides functionality related to NeoFS users.
User identity is reflected in ID type. Each user has its own unique identifier
within the same network.
NeoFS user identification is compatible with Neo accounts:
import "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
import "github.com/nspcc-dev/neo-go/pkg/crypto/hash"
var id user.ID
var scriptHash util.Uint160 // user account in NeoFS
id.SetScriptHash(scriptHash)
var key keys.PublicKey // user's public key
user.IDFromKey(&id, k.PrivateKey.PublicKey)
ID is compatible with the NeoFS Smart Contract API:
var id user.ID
// ...
wallet := id.WalletBytes()
// use wallet in call
Encoding/decoding mechanisms are used to transfer identifiers:
var id user.ID
// ...
s := id.EncodeToString() // on transmitter
err = id.DecodeString(s) // on receiver
Instances can be also used to process NeoFS API protocol messages
(see neo.fs.v2.refs package in https://github.com/nspcc-dev/neofs-api).
On client side:
import "github.com/nspcc-dev/neofs-api-go/v2/refs"
var msg refs.OwnerID
id.WriteToV2(&msg)
// send msg
On server side:
// recv msg
var id user.ID
err := id.ReadFromV2(msg)
// ...
// process id
*/
package user

116
user/id.go Normal file
View file

@ -0,0 +1,116 @@
package user
import (
"bytes"
"errors"
"fmt"
"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"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
)
// ID identifies users of the NeoFS system.
//
// ID is mutually compatible with github.com/nspcc-dev/neofs-api-go/v2/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 []byte
}
// ReadFromV2 reads ID from the refs.OwnerID message. Returns an error if
// the message is malformed according to the NeoFS API V2 protocol.
//
// See also WriteToV2.
func (x *ID) ReadFromV2(m refs.OwnerID) error {
w := m.GetValue()
if len(w) != 25 {
return fmt.Errorf("invalid length %d, expected 25", len(w))
}
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) {
if cap(x.w) < 25 {
x.w = make([]byte, 25)
} else if len(x.w) < 25 {
x.w = x.w[:25]
}
x.w[0] = address.Prefix
copy(x.w[1:], scriptHash.BytesBE())
copy(x.w[21:], hash.Checksum(x.w[:21]))
}
// WalletBytes returns NeoFS 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 {
return x.w
}
// EncodeToString encodes ID into NeoFS API V2 protocol string.
//
// See also DecodeString.
func (x ID) EncodeToString() string {
return base58.Encode(x.w)
}
// DecodeString decodes NeoFS 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 {
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
// be used to encode ID into NeoFS 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 bytes.Equal(x.w, x2.w)
}

121
user/id_test.go Normal file
View file

@ -0,0 +1,121 @@
package user_test
import (
"math/rand"
"testing"
"github.com/mr-tron/base58"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/util/slice"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
. "github.com/nspcc-dev/neofs-sdk-go/user"
usertest "github.com/nspcc-dev/neofs-sdk-go/user/test"
"github.com/stretchr/testify/require"
)
func TestID_WalletBytes(t *testing.T) {
var scriptHash util.Uint160
rand.Read(scriptHash[:])
var id ID
id.SetScriptHash(scriptHash)
w := id.WalletBytes()
var m refs.OwnerID
m.SetValue(w)
err := id.ReadFromV2(m)
require.NoError(t, err)
}
func TestID_SetScriptHash(t *testing.T) {
var scriptHash util.Uint160
rand.Read(scriptHash[:])
var id ID
id.SetScriptHash(scriptHash)
var m refs.OwnerID
id.WriteToV2(&m)
var id2 ID
err := id2.ReadFromV2(m)
require.NoError(t, err)
require.True(t, id2.Equals(id))
}
func TestV2_ID(t *testing.T) {
id := *usertest.ID()
var m refs.OwnerID
var id2 ID
t.Run("OK", func(t *testing.T) {
id.WriteToV2(&m)
err := id2.ReadFromV2(m)
require.NoError(t, err)
require.True(t, id2.Equals(id))
})
val := m.GetValue()
t.Run("invalid size", func(t *testing.T) {
m.SetValue(val[:24])
err := id2.ReadFromV2(m)
require.Error(t, err)
})
t.Run("invalid prefix", func(t *testing.T) {
val := slice.Copy(val)
val[0]++
m.SetValue(val)
err := id2.ReadFromV2(m)
require.Error(t, err)
})
t.Run("invalid checksum", func(t *testing.T) {
val := slice.Copy(val)
val[21]++
m.SetValue(val)
err := id2.ReadFromV2(m)
require.Error(t, err)
})
}
func TestID_EncodeToString(t *testing.T) {
id := *usertest.ID()
s := id.EncodeToString()
_, err := base58.Decode(s)
require.NoError(t, err)
var id2 ID
err = id2.DecodeString(s)
require.NoError(t, err)
require.Equal(t, id, id2)
err = id2.DecodeString("_") // any invalid bas58 string
require.Error(t, err)
}
func TestID_Equal(t *testing.T) {
id1 := *usertest.ID()
id2 := *usertest.ID()
id3 := id1
require.True(t, id1.Equals(id1)) // self-equality
require.True(t, id1.Equals(id3))
require.True(t, id3.Equals(id1)) // commutativity
require.False(t, id1.Equals(id2))
}

13
user/test/doc.go Normal file
View file

@ -0,0 +1,13 @@
/*
Package usertest provides functions for convenient testing of user package API.
Note that importing the package into source files is highly discouraged.
Random instance generation functions can be useful when testing expects any value, e.g.:
import usertest "github.com/nspcc-dev/neofs-sdk-go/user/test"
id := usertest.ID()
// test the value
*/
package usertest

19
user/test/id.go Normal file
View file

@ -0,0 +1,19 @@
package usertest
import (
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neofs-sdk-go/user"
)
// ID returns random user.ID.
func ID() *user.ID {
key, err := keys.NewPrivateKey()
if err != nil {
panic(err)
}
var x user.ID
user.IDFromKey(&x, key.PrivateKey.PublicKey)
return &x
}

12
user/util.go Normal file
View file

@ -0,0 +1,12 @@
package user
import (
"crypto/ecdsa"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
)
// IDFromKey forms the ID using script hash calculated for the given key.
func IDFromKey(id *ID, key ecdsa.PublicKey) {
id.SetScriptHash((*keys.PublicKey)(&key).GetScriptHash())
}

24
user/util_test.go Normal file
View file

@ -0,0 +1,24 @@
package user_test
import (
"crypto/ecdsa"
"crypto/elliptic"
"encoding/hex"
"testing"
"github.com/nspcc-dev/neofs-sdk-go/user"
"github.com/stretchr/testify/require"
)
func TestIDFromKey(t *testing.T) {
// examples are taken from https://docs.neo.org/docs/en-us/basic/concept/wallets.html
rawPub, _ := hex.DecodeString("03cdb067d930fd5adaa6c68545016044aaddec64ba39e548250eaea551172e535c")
x, y := elliptic.UnmarshalCompressed(elliptic.P256(), rawPub)
require.True(t, x != nil && y != nil)
var id user.ID
user.IDFromKey(&id, ecdsa.PublicKey{Curve: elliptic.P256(), X: x, Y: y})
require.Equal(t, "NNLi44dJNXtDNSBkofB48aTVYtb1zZrNEs", id.EncodeToString())
}