diff --git a/bearer/bearer.go b/bearer/bearer.go index 0c895c3..4d54ea2 100644 --- a/bearer/bearer.go +++ b/bearer/bearer.go @@ -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. diff --git a/bearer/bearer_test.go b/bearer/bearer_test.go index a131389..64d1df0 100644 --- a/bearer/bearer_test.go +++ b/bearer/bearer_test.go @@ -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)) }) } diff --git a/bearer/test/generate.go b/bearer/test/generate.go index 5f83258..406eff0 100644 --- a/bearer/test/generate.go +++ b/bearer/test/generate.go @@ -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 diff --git a/client/accounting.go b/client/accounting.go index 82aec79..acf4d84 100644 --- a/client/accounting.go +++ b/client/accounting.go @@ -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 diff --git a/client/container.go b/client/container.go index 2176585..080a62f 100644 --- a/client/container.go +++ b/client/container.go @@ -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 diff --git a/client/session.go b/client/session.go index ca490eb..8f41891 100644 --- a/client/session.go +++ b/client/session.go @@ -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 diff --git a/container/container.go b/container/container.go index 6512eff..1eef6e6 100644 --- a/container/container.go +++ b/container/container.go @@ -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. diff --git a/container/container_test.go b/container/container_test.go index ee4e938..fc1b971 100644 --- a/container/container_test.go +++ b/container/container_test.go @@ -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) diff --git a/container/opts.go b/container/opts.go index d6c589b..276aa8f 100644 --- a/container/opts.go +++ b/container/opts.go @@ -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) } } diff --git a/container/test/generate.go b/container/test/generate.go index b0bf5cd..6b26a3e 100644 --- a/container/test/generate.go +++ b/container/test/generate.go @@ -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()) diff --git a/eacl/record.go b/eacl/record.go index 935cd01..293755f 100644 --- a/eacl/record.go +++ b/eacl/record.go @@ -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) } diff --git a/eacl/record_test.go b/eacl/record_test.go index 1163bdf..e6dba8f 100644 --- a/eacl/record_test.go +++ b/eacl/record_test.go @@ -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) ) diff --git a/eacl/test/generate.go b/eacl/test/generate.go index f316024..8b9c88b 100644 --- a/eacl/test/generate.go +++ b/eacl/test/generate.go @@ -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 } diff --git a/object/object.go b/object/object.go index 191b217..a752724 100644 --- a/object/object.go +++ b/object/object.go @@ -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) }) } diff --git a/object/object_test.go b/object/object_test.go index 3e0dca9..6e7cfc9 100644 --- a/object/object_test.go +++ b/object/object_test.go @@ -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, diff --git a/object/raw_test.go b/object/raw_test.go index ea27a07..4b6a8e1 100644 --- a/object/raw_test.go +++ b/object/raw_test.go @@ -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) diff --git a/object/search.go b/object/search.go index b78cc82..2d4948c 100644 --- a/object/search.go +++ b/object/search.go @@ -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) } diff --git a/object/test/generate.go b/object/test/generate.go index 0f5df88..9bb1346 100644 --- a/object/test/generate.go +++ b/object/test/generate.go @@ -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) diff --git a/owner/convert_test.go b/owner/convert_test.go deleted file mode 100644 index 494285f..0000000 --- a/owner/convert_test.go +++ /dev/null @@ -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)) -} diff --git a/owner/id.go b/owner/id.go deleted file mode 100644 index 98c2de9..0000000 --- a/owner/id.go +++ /dev/null @@ -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 -} diff --git a/owner/id_test.go b/owner/id_test.go deleted file mode 100644 index a27aec8..0000000 --- a/owner/id_test.go +++ /dev/null @@ -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()) -} diff --git a/owner/test/id.go b/owner/test/id.go deleted file mode 100644 index f79ca91..0000000 --- a/owner/test/id.go +++ /dev/null @@ -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) -} diff --git a/pool/pool.go b/pool/pool.go index f8b864f..46d6106 100644 --- a/pool/pool.go +++ b/pool/pool.go @@ -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 diff --git a/pool/pool_test.go b/pool/pool_test.go index 41c0080..aa350e8 100644 --- a/pool/pool_test.go +++ b/pool/pool_test.go @@ -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) { diff --git a/session/session.go b/session/session.go index 681b024..672b070 100644 --- a/session/session.go +++ b/session/session.go @@ -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) }) } diff --git a/session/session_test.go b/session/session_test.go index b027114..5540ddd 100644 --- a/session/session_test.go +++ b/session/session_test.go @@ -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) diff --git a/session/test/token.go b/session/test/token.go index 95c8ca4..de516af 100644 --- a/session/test/token.go +++ b/session/test/token.go @@ -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) diff --git a/subnet/subnet.go b/subnet/subnet.go index f70f876..75c48c9 100644 --- a/subnet/subnet.go +++ b/subnet/subnet.go @@ -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. diff --git a/subnet/subnet_test.go b/subnet/subnet_test.go index 763319e..70590fd 100644 --- a/subnet/subnet_test.go +++ b/subnet/subnet_test.go @@ -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)) diff --git a/user/doc.go b/user/doc.go new file mode 100644 index 0000000..800677d --- /dev/null +++ b/user/doc.go @@ -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 diff --git a/user/id.go b/user/id.go new file mode 100644 index 0000000..08d851a --- /dev/null +++ b/user/id.go @@ -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) +} diff --git a/user/id_test.go b/user/id_test.go new file mode 100644 index 0000000..2d47d1a --- /dev/null +++ b/user/id_test.go @@ -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)) +} diff --git a/user/test/doc.go b/user/test/doc.go new file mode 100644 index 0000000..9584719 --- /dev/null +++ b/user/test/doc.go @@ -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 diff --git a/user/test/id.go b/user/test/id.go new file mode 100644 index 0000000..adf9d0c --- /dev/null +++ b/user/test/id.go @@ -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 +} diff --git a/user/util.go b/user/util.go new file mode 100644 index 0000000..ddd9657 --- /dev/null +++ b/user/util.go @@ -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()) +} diff --git a/user/util_test.go b/user/util_test.go new file mode 100644 index 0000000..437d449 --- /dev/null +++ b/user/util_test.go @@ -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()) +}