[#170] oid, cid: Refactor and document package functionality

Signed-off-by: Pavel Karpy <carpawell@nspcc.ru>
This commit is contained in:
Pavel Karpy 2022-04-11 19:25:14 +03:00 committed by LeL
parent 24d6c2221f
commit f7172adf18
49 changed files with 831 additions and 439 deletions

View file

@ -75,20 +75,29 @@ func (r *Result) ForEpoch(epoch uint64) {
// Container returns identifier of the container with which the data audit Result // Container returns identifier of the container with which the data audit Result
// is associated. // is associated.
// //
// Returns nil if container is not specified. Zero Result has nil container. // Returns zero ID if container is not specified. Zero Result has zero container.
// Return value MUST NOT be mutated: to do this, first make a copy.
// //
// See also ForContainer. // See also ForContainer.
func (r Result) Container() *cid.ID { func (r Result) Container() cid.ID {
return cid.NewFromV2(r.v2.GetContainerID()) var cID cid.ID
cidV2 := r.v2.GetContainerID()
if cidV2 != nil {
_ = cID.ReadFromV2(*cidV2)
}
return cID
} }
// ForContainer returns identifier of the container with which the data audit Result // ForContainer sets identifier of the container with which the data audit Result
// is associated. // is associated.
// //
// See also Container. // See also Container.
func (r *Result) ForContainer(cnr cid.ID) { func (r *Result) ForContainer(cnr cid.ID) {
r.v2.SetContainerID(cnr.ToV2()) var cidV2 refs.ContainerID
cnr.WriteToV2(&cidV2)
r.v2.SetContainerID(&cidV2)
} }
// AuditorKey returns public key of the auditing NeoFS Inner Ring node in // AuditorKey returns public key of the auditing NeoFS Inner Ring node in
@ -174,8 +183,12 @@ func (r *Result) SetRetriesPoR(v uint32) {
func (r Result) IteratePassedStorageGroups(f func(oid.ID) bool) { func (r Result) IteratePassedStorageGroups(f func(oid.ID) bool) {
r2 := r.v2.GetPassSG() r2 := r.v2.GetPassSG()
var id oid.ID
for i := range r2 { for i := range r2 {
if !f(*oid.NewIDFromV2(&r2[i])) { _ = id.ReadFromV2(r2[i])
if !f(id) {
return return
} }
} }
@ -186,7 +199,10 @@ func (r Result) IteratePassedStorageGroups(f func(oid.ID) bool) {
// //
// See also IteratePassedStorageGroups. // See also IteratePassedStorageGroups.
func (r *Result) SubmitPassedStorageGroup(sg oid.ID) { func (r *Result) SubmitPassedStorageGroup(sg oid.ID) {
r.v2.SetPassSG(append(r.v2.GetPassSG(), *sg.ToV2())) var idV2 refs.ObjectID
sg.WriteToV2(&idV2)
r.v2.SetPassSG(append(r.v2.GetPassSG(), idV2))
} }
// IterateFailedStorageGroups is similar to IteratePassedStorageGroups but for failed groups. // IterateFailedStorageGroups is similar to IteratePassedStorageGroups but for failed groups.
@ -194,9 +210,11 @@ func (r *Result) SubmitPassedStorageGroup(sg oid.ID) {
// See also SubmitFailedStorageGroup. // See also SubmitFailedStorageGroup.
func (r Result) IterateFailedStorageGroups(f func(oid.ID) bool) { func (r Result) IterateFailedStorageGroups(f func(oid.ID) bool) {
v := r.v2.GetFailSG() v := r.v2.GetFailSG()
var id oid.ID
for i := range v { for i := range v {
if !f(*oid.NewIDFromV2(&v[i])) { _ = id.ReadFromV2(v[i])
if !f(id) {
return return
} }
} }
@ -206,7 +224,10 @@ func (r Result) IterateFailedStorageGroups(f func(oid.ID) bool) {
// //
// See also IterateFailedStorageGroups. // See also IterateFailedStorageGroups.
func (r *Result) SubmitFailedStorageGroup(sg oid.ID) { func (r *Result) SubmitFailedStorageGroup(sg oid.ID) {
r.v2.SetFailSG(append(r.v2.GetFailSG(), *sg.ToV2())) var idV2 refs.ObjectID
sg.WriteToV2(&idV2)
r.v2.SetFailSG(append(r.v2.GetFailSG(), idV2))
} }
// Hits returns number of sampled objects under audit placed // Hits returns number of sampled objects under audit placed

View file

@ -66,7 +66,7 @@ func TestResultData(t *testing.T) {
countFailNodes := func(f func([]byte)) int { return countNodes(false, f) } countFailNodes := func(f func([]byte)) int { return countNodes(false, f) }
require.Zero(t, r.Epoch()) require.Zero(t, r.Epoch())
require.Nil(t, r.Container()) require.True(t, r.Container().Empty())
require.Nil(t, r.AuditorKey()) require.Nil(t, r.AuditorKey())
require.False(t, r.Completed()) require.False(t, r.Completed())
require.Zero(t, r.RequestsPoR()) require.Zero(t, r.RequestsPoR())
@ -81,7 +81,7 @@ func TestResultData(t *testing.T) {
require.Equal(t, epoch, r.Epoch()) require.Equal(t, epoch, r.Epoch())
cnr := cidtest.ID() cnr := cidtest.ID()
r.ForContainer(*cnr) r.ForContainer(cnr)
require.Equal(t, cnr, r.Container()) require.Equal(t, cnr, r.Container())
key := []byte{1, 2, 3} key := []byte{1, 2, 3}
@ -99,32 +99,32 @@ func TestResultData(t *testing.T) {
r.SetRetriesPoR(retries) r.SetRetriesPoR(retries)
require.Equal(t, retries, r.RetriesPoR()) require.Equal(t, retries, r.RetriesPoR())
passSG1, passSG2 := *oidtest.ID(), *oidtest.ID() passSG1, passSG2 := oidtest.ID(), oidtest.ID()
r.SubmitPassedStorageGroup(passSG1) r.SubmitPassedStorageGroup(passSG1)
r.SubmitPassedStorageGroup(passSG2) r.SubmitPassedStorageGroup(passSG2)
called1, called2 := false, false called1, called2 := false, false
require.EqualValues(t, 2, countPassSG(func(id oid.ID) { require.EqualValues(t, 2, countPassSG(func(id oid.ID) {
if id.Equal(&passSG1) { if id.Equals(passSG1) {
called1 = true called1 = true
} else if id.Equal(&passSG2) { } else if id.Equals(passSG2) {
called2 = true called2 = true
} }
})) }))
require.True(t, called1) require.True(t, called1)
require.True(t, called2) require.True(t, called2)
failSG1, failSG2 := *oidtest.ID(), *oidtest.ID() failSG1, failSG2 := oidtest.ID(), oidtest.ID()
r.SubmitFailedStorageGroup(failSG1) r.SubmitFailedStorageGroup(failSG1)
r.SubmitFailedStorageGroup(failSG2) r.SubmitFailedStorageGroup(failSG2)
called1, called2 = false, false called1, called2 = false, false
require.EqualValues(t, 2, countFailSG(func(id oid.ID) { require.EqualValues(t, 2, countFailSG(func(id oid.ID) {
if id.Equal(&failSG1) { if id.Equals(failSG1) {
called1 = true called1 = true
} else if id.Equal(&failSG2) { } else if id.Equals(failSG2) {
called2 = true called2 = true
} }
})) }))

View file

@ -10,7 +10,7 @@ import (
func Result() *audit.Result { func Result() *audit.Result {
var x audit.Result var x audit.Result
x.ForContainer(*cidtest.ID()) x.ForContainer(cidtest.ID())
x.SetAuditorKey([]byte("key")) x.SetAuditorKey([]byte("key"))
x.Complete() x.Complete()
x.ForEpoch(44) x.ForEpoch(44)
@ -27,10 +27,10 @@ func Result() *audit.Result {
[]byte("node3"), []byte("node3"),
[]byte("node4"), []byte("node4"),
}) })
x.SubmitPassedStorageGroup(*oidtest.ID()) x.SubmitPassedStorageGroup(oidtest.ID())
x.SubmitPassedStorageGroup(*oidtest.ID()) x.SubmitPassedStorageGroup(oidtest.ID())
x.SubmitFailedStorageGroup(*oidtest.ID()) x.SubmitFailedStorageGroup(oidtest.ID())
x.SubmitFailedStorageGroup(*oidtest.ID()) x.SubmitFailedStorageGroup(oidtest.ID())
return &x return &x
} }

View file

@ -4,6 +4,7 @@ import (
"context" "context"
v2container "github.com/nspcc-dev/neofs-api-go/v2/container" v2container "github.com/nspcc-dev/neofs-api-go/v2/container"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
rpcapi "github.com/nspcc-dev/neofs-api-go/v2/rpc" 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-api-go/v2/rpc/client"
v2session "github.com/nspcc-dev/neofs-api-go/v2/session" v2session "github.com/nspcc-dev/neofs-api-go/v2/session"
@ -121,7 +122,17 @@ func (c *Client) ContainerPut(ctx context.Context, prm PrmContainerPut) (*ResCon
} }
cc.result = func(r responseV2) { cc.result = func(r responseV2) {
resp := r.(*v2container.PutResponse) resp := r.(*v2container.PutResponse)
res.setID(cid.NewFromV2(resp.GetBody().GetContainerID())) var cID *cid.ID
cidV2 := resp.GetBody().GetContainerID()
if cidV2 != nil {
var c cid.ID
_ = c.ReadFromV2(*cidV2)
cID = &c
}
res.setID(cID)
} }
// process call // process call
@ -187,9 +198,12 @@ func (c *Client) ContainerGet(ctx context.Context, prm PrmContainerGet) (*ResCon
panic(panicMsgMissingContainer) panic(panicMsgMissingContainer)
} }
var cidV2 refs.ContainerID
prm.id.WriteToV2(&cidV2)
// form request body // form request body
reqBody := new(v2container.GetRequestBody) reqBody := new(v2container.GetRequestBody)
reqBody.SetContainerID(prm.id.ToV2()) reqBody.SetContainerID(&cidV2)
// form request // form request
var req v2container.GetRequest var req v2container.GetRequest
@ -322,7 +336,7 @@ func (c *Client) ContainerList(ctx context.Context, prm PrmContainerList) (*ResC
ids := make([]cid.ID, len(resp.GetBody().GetContainerIDs())) ids := make([]cid.ID, len(resp.GetBody().GetContainerIDs()))
for i, cidV2 := range resp.GetBody().GetContainerIDs() { for i, cidV2 := range resp.GetBody().GetContainerIDs() {
ids[i] = *cid.NewFromV2(&cidV2) _ = ids[i].ReadFromV2(cidV2)
} }
res.setContainers(ids) res.setContainers(ids)
@ -400,9 +414,12 @@ func (c *Client) ContainerDelete(ctx context.Context, prm PrmContainerDelete) (*
panic(panicMsgMissingContainer) panic(panicMsgMissingContainer)
} }
var cidV2 refs.ContainerID
prm.id.WriteToV2(&cidV2)
// form request body // form request body
reqBody := new(v2container.DeleteRequestBody) reqBody := new(v2container.DeleteRequestBody)
reqBody.SetContainerID(prm.id.ToV2()) reqBody.SetContainerID(&cidV2)
signWrapper := delContainerSignWrapper{body: reqBody} signWrapper := delContainerSignWrapper{body: reqBody}
@ -504,9 +521,12 @@ func (c *Client) ContainerEACL(ctx context.Context, prm PrmContainerEACL) (*ResC
panic(panicMsgMissingContainer) panic(panicMsgMissingContainer)
} }
var cidV2 refs.ContainerID
prm.id.WriteToV2(&cidV2)
// form request body // form request body
reqBody := new(v2container.GetExtendedACLRequestBody) reqBody := new(v2container.GetExtendedACLRequestBody)
reqBody.SetContainerID(prm.id.ToV2()) reqBody.SetContainerID(&cidV2)
// form request // form request
var req v2container.GetExtendedACLRequest var req v2container.GetExtendedACLRequest

View file

@ -52,13 +52,19 @@ func (x *PrmObjectDelete) WithBearerToken(t bearer.Token) {
// FromContainer specifies NeoFS container of the object. // FromContainer specifies NeoFS container of the object.
// Required parameter. // Required parameter.
func (x *PrmObjectDelete) FromContainer(id cid.ID) { func (x *PrmObjectDelete) FromContainer(id cid.ID) {
x.addr.SetContainerID(id.ToV2()) var cidV2 v2refs.ContainerID
id.WriteToV2(&cidV2)
x.addr.SetContainerID(&cidV2)
} }
// ByID specifies identifier of the requested object. // ByID specifies identifier of the requested object.
// Required parameter. // Required parameter.
func (x *PrmObjectDelete) ByID(id oid.ID) { func (x *PrmObjectDelete) ByID(id oid.ID) {
x.addr.SetObjectID(id.ToV2()) var idV2 v2refs.ObjectID
id.WriteToV2(&idV2)
x.addr.SetObjectID(&idV2)
} }
// UseKey specifies private key to sign the requests. // UseKey specifies private key to sign the requests.
@ -91,7 +97,7 @@ type ResObjectDelete struct {
// Returns false if ID is missing (not read). // Returns false if ID is missing (not read).
func (x ResObjectDelete) ReadTombstoneID(dst *oid.ID) bool { func (x ResObjectDelete) ReadTombstoneID(dst *oid.ID) bool {
if x.idTomb != nil { if x.idTomb != nil {
*dst = *oid.NewIDFromV2(x.idTomb) // need smth better _ = dst.ReadFromV2(*x.idTomb)
return true return true
} }

View file

@ -36,10 +36,10 @@ type prmObjectRead struct {
bearer bearer.Token bearer bearer.Token
cnrSet bool cnrSet bool
cnr cid.ID cnrID cid.ID
objSet bool objSet bool
obj oid.ID objID oid.ID
} }
func (x prmObjectRead) writeToMetaHeader(h *v2session.RequestMetaHeader) { func (x prmObjectRead) writeToMetaHeader(h *v2session.RequestMetaHeader) {
@ -94,14 +94,14 @@ func (x *prmObjectRead) WithBearerToken(t bearer.Token) {
// FromContainer specifies NeoFS container of the object. // FromContainer specifies NeoFS container of the object.
// Required parameter. // Required parameter.
func (x *prmObjectRead) FromContainer(id cid.ID) { func (x *prmObjectRead) FromContainer(id cid.ID) {
x.cnr = id x.cnrID = id
x.cnrSet = true x.cnrSet = true
} }
// ByID specifies identifier of the requested object. // ByID specifies identifier of the requested object.
// Required parameter. // Required parameter.
func (x *prmObjectRead) ByID(id oid.ID) { func (x *prmObjectRead) ByID(id oid.ID) {
x.obj = id x.objID = id
x.objSet = true x.objSet = true
} }
@ -318,10 +318,17 @@ func (c *Client) ObjectGetInit(ctx context.Context, prm PrmObjectGet) (*ObjectRe
panic("missing object") panic("missing object")
} }
var addr v2refs.Address var (
addr v2refs.Address
oidV2 v2refs.ObjectID
cidV2 v2refs.ContainerID
)
addr.SetContainerID(prm.cnr.ToV2()) prm.objID.WriteToV2(&oidV2)
addr.SetObjectID(prm.obj.ToV2()) prm.cnrID.WriteToV2(&cidV2)
addr.SetContainerID(&cidV2)
addr.SetObjectID(&oidV2)
// form request body // form request body
var body v2object.GetRequestBody var body v2object.GetRequestBody
@ -411,7 +418,7 @@ func (x *ResObjectHead) ReadHeader(dst *object.Object) bool {
objv2.SetSignature(x.hdr.GetSignature()) objv2.SetSignature(x.hdr.GetSignature())
obj := object.NewFromV2(&objv2) obj := object.NewFromV2(&objv2)
obj.SetID(&x.idObj) obj.SetID(x.idObj)
*dst = *obj *dst = *obj
@ -449,16 +456,23 @@ func (c *Client) ObjectHead(ctx context.Context, prm PrmObjectHead) (*ResObjectH
panic("missing object") panic("missing object")
} }
var addr v2refs.Address var (
addrV2 v2refs.Address
oidV2 v2refs.ObjectID
cidV2 v2refs.ContainerID
)
addr.SetContainerID(prm.cnr.ToV2()) prm.objID.WriteToV2(&oidV2)
addr.SetObjectID(prm.obj.ToV2()) prm.cnrID.WriteToV2(&cidV2)
addrV2.SetContainerID(&cidV2)
addrV2.SetObjectID(&oidV2)
// form request body // form request body
var body v2object.HeadRequestBody var body v2object.HeadRequestBody
body.SetRaw(prm.raw) body.SetRaw(prm.raw)
body.SetAddress(&addr) body.SetAddress(&addrV2)
// form meta header // form meta header
var meta v2session.RequestMetaHeader var meta v2session.RequestMetaHeader
@ -478,7 +492,7 @@ func (c *Client) ObjectHead(ctx context.Context, prm PrmObjectHead) (*ResObjectH
res ResObjectHead res ResObjectHead
) )
res.idObj = prm.obj res.idObj = prm.objID
if prm.keySet { if prm.keySet {
c.initCallContextWithoutKey(&cc) c.initCallContextWithoutKey(&cc)
@ -715,10 +729,17 @@ func (c *Client) ObjectRangeInit(ctx context.Context, prm PrmObjectRange) (*Obje
panic("zero range length") panic("zero range length")
} }
var addr v2refs.Address var (
addrV2 v2refs.Address
oidV2 v2refs.ObjectID
cidV2 v2refs.ContainerID
)
addr.SetContainerID(prm.cnr.ToV2()) prm.objID.WriteToV2(&oidV2)
addr.SetObjectID(prm.obj.ToV2()) prm.cnrID.WriteToV2(&cidV2)
addrV2.SetContainerID(&cidV2)
addrV2.SetObjectID(&oidV2)
var rng v2object.Range var rng v2object.Range
@ -729,7 +750,7 @@ func (c *Client) ObjectRangeInit(ctx context.Context, prm PrmObjectRange) (*Obje
var body v2object.GetRangeRequestBody var body v2object.GetRangeRequestBody
body.SetRaw(prm.raw) body.SetRaw(prm.raw)
body.SetAddress(&addr) body.SetAddress(&addrV2)
body.SetRange(&rng) body.SetRange(&rng)
// form meta header // form meta header

View file

@ -55,13 +55,19 @@ func (x *PrmObjectHash) WithBearerToken(t bearer.Token) {
// FromContainer specifies NeoFS container of the object. // FromContainer specifies NeoFS container of the object.
// Required parameter. // Required parameter.
func (x *PrmObjectHash) FromContainer(id cid.ID) { func (x *PrmObjectHash) FromContainer(id cid.ID) {
x.addr.SetContainerID(id.ToV2()) var cidV2 v2refs.ContainerID
id.WriteToV2(&cidV2)
x.addr.SetContainerID(&cidV2)
} }
// ByID specifies identifier of the requested object. // ByID specifies identifier of the requested object.
// Required parameter. // Required parameter.
func (x *PrmObjectHash) ByID(id oid.ID) { func (x *PrmObjectHash) ByID(id oid.ID) {
x.addr.SetObjectID(id.ToV2()) var idV2 v2refs.ObjectID
id.WriteToV2(&idV2)
x.addr.SetObjectID(&idV2)
} }
// SetRangeList sets list of ranges in (offset, length) pair format. // SetRangeList sets list of ranges in (offset, length) pair format.

View file

@ -37,7 +37,7 @@ func (x *ResObjectPut) ReadStoredObjectID(id *oid.ID) bool {
return false return false
} }
*id = *oid.NewIDFromV2(idv2) // need smth better _ = id.ReadFromV2(*idv2)
return true return true
} }

View file

@ -34,7 +34,7 @@ type PrmObjectSearch struct {
bearer bearer.Token bearer bearer.Token
cnrSet bool cnrSet bool
cnr cid.ID cnrID cid.ID
filters object.SearchFilters filters object.SearchFilters
} }
@ -68,7 +68,7 @@ func (x *PrmObjectSearch) WithBearerToken(t bearer.Token) {
// InContainer specifies the container in which to look for objects. // InContainer specifies the container in which to look for objects.
// Required parameter. // Required parameter.
func (x *PrmObjectSearch) InContainer(id cid.ID) { func (x *PrmObjectSearch) InContainer(id cid.ID) {
x.cnr = id x.cnrID = id
x.cnrSet = true x.cnrSet = true
} }
@ -131,7 +131,7 @@ func (x *ObjectListReader) Read(buf []oid.ID) (int, bool) {
} }
for i := 0; i < read; i++ { for i := 0; i < read; i++ {
buf[i] = *oid.NewIDFromV2(&x.tail[i]) // need smth better _ = buf[i].ReadFromV2(x.tail[i])
} }
x.tail = x.tail[read:] x.tail = x.tail[read:]
@ -165,7 +165,7 @@ func (x *ObjectListReader) Read(buf []oid.ID) (int, bool) {
} }
for i = 0; i < ln; i++ { for i = 0; i < ln; i++ {
buf[read+i] = *oid.NewIDFromV2(&ids[i]) // need smth better _ = buf[read+i].ReadFromV2(ids[i])
} }
read += ln read += ln
@ -244,10 +244,15 @@ func (c *Client) ObjectSearchInit(ctx context.Context, prm PrmObjectSearch) (*Ob
} }
// form request body // form request body
var body v2object.SearchRequestBody var (
body v2object.SearchRequestBody
cidV2 v2refs.ContainerID
)
prm.cnrID.WriteToV2(&cidV2)
body.SetVersion(1) body.SetVersion(1)
body.SetContainerID(prm.cnr.ToV2()) body.SetContainerID(&cidV2)
body.SetFilters(prm.filters.ToV2()) body.SetFilters(prm.filters.ToV2())
// form meta header // form meta header

View file

@ -17,7 +17,7 @@ import (
func TestObjectSearch(t *testing.T) { func TestObjectSearch(t *testing.T) {
ids := make([]oid.ID, 20) ids := make([]oid.ID, 20)
for i := range ids { for i := range ids {
ids[i] = *oidtest.ID() ids[i] = oidtest.ID()
} }
resp, setID := testListReaderResponse(t) resp, setID := testListReaderResponse(t)
@ -88,7 +88,7 @@ func TestObjectSearch(t *testing.T) {
func TestObjectIterate(t *testing.T) { func TestObjectIterate(t *testing.T) {
ids := make([]oid.ID, 3) ids := make([]oid.ID, 3)
for i := range ids { for i := range ids {
ids[i] = *oidtest.ID() ids[i] = oidtest.ID()
} }
t.Run("iterate all sequence", func(t *testing.T) { t.Run("iterate all sequence", func(t *testing.T) {
@ -176,8 +176,11 @@ func testListReaderResponse(t *testing.T) (*ObjectListReader, func(id []oid.ID)
resp.SetBody(new(object.SearchResponseBody)) resp.SetBody(new(object.SearchResponseBody))
v2id := make([]refs.ObjectID, len(id)) v2id := make([]refs.ObjectID, len(id))
var oidV2 refs.ObjectID
for i := range id { for i := range id {
v2id[i] = *id[i].ToV2() id[i].WriteToV2(&oidV2)
v2id[i] = oidV2
} }
resp.GetBody().SetIDList(v2id) resp.GetBody().SetIDList(v2id)
err := signatureV2.SignServiceMessage(&p.PrivateKey, resp) err := signatureV2.SignServiceMessage(&p.PrivateKey, resp)

View file

@ -2,6 +2,7 @@ package container
import ( import (
"github.com/nspcc-dev/neofs-api-go/v2/container" "github.com/nspcc-dev/neofs-api-go/v2/container"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id" cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
) )
@ -38,15 +39,25 @@ func (a *UsedSpaceAnnouncement) SetEpoch(epoch uint64) {
} }
// ContainerID of the announcement. // ContainerID of the announcement.
func (a *UsedSpaceAnnouncement) ContainerID() *cid.ID { func (a *UsedSpaceAnnouncement) ContainerID() (cID cid.ID) {
return cid.NewFromV2( v2 := (*container.UsedSpaceAnnouncement)(a)
(*container.UsedSpaceAnnouncement)(a).GetContainerID(),
) cidV2 := v2.GetContainerID()
if cidV2 == nil {
return
}
_ = cID.ReadFromV2(*cidV2)
return
} }
// SetContainerID sets announcement container value. // SetContainerID sets announcement container value.
func (a *UsedSpaceAnnouncement) SetContainerID(cid *cid.ID) { func (a *UsedSpaceAnnouncement) SetContainerID(cnr cid.ID) {
(*container.UsedSpaceAnnouncement)(a).SetContainerID(cid.ToV2()) var cidV2 refs.ContainerID
cnr.WriteToV2(&cidV2)
(*container.UsedSpaceAnnouncement)(a).SetContainerID(&cidV2)
} }
// UsedSpace in container. // UsedSpace in container.

View file

@ -46,9 +46,12 @@ func TestAnnouncement(t *testing.T) {
newA := container.NewAnnouncementFromV2(v2) newA := container.NewAnnouncementFromV2(v2)
var cID cid.ID
_ = cID.ReadFromV2(*newCID)
require.Equal(t, newEpoch, newA.Epoch()) require.Equal(t, newEpoch, newA.Epoch())
require.Equal(t, newUsedSpace, newA.UsedSpace()) require.Equal(t, newUsedSpace, newA.UsedSpace())
require.Equal(t, cid.NewFromV2(newCID), newA.ContainerID()) require.Equal(t, cID, newA.ContainerID())
}) })
} }
@ -79,7 +82,7 @@ func TestUsedSpaceAnnouncement_ToV2(t *testing.T) {
// check initial values // check initial values
require.Zero(t, announcement.Epoch()) require.Zero(t, announcement.Epoch())
require.Zero(t, announcement.UsedSpace()) require.Zero(t, announcement.UsedSpace())
require.Nil(t, announcement.ContainerID()) require.True(t, announcement.ContainerID().Empty())
// convert to v2 message // convert to v2 message
announcementV2 := announcement.ToV2() announcementV2 := announcement.ToV2()

View file

@ -87,13 +87,13 @@ func NewContainerFromV2(c *container.Container) *Container {
// CalculateID calculates container identifier // CalculateID calculates container identifier
// based on its structure. // based on its structure.
func CalculateID(c *Container) *cid.ID { func CalculateID(c *Container) cid.ID {
data, err := c.ToV2().StableMarshal(nil) data, err := c.ToV2().StableMarshal(nil)
if err != nil { if err != nil {
panic(err) panic(err)
} }
id := cid.New() var id cid.ID
id.SetSHA256(sha256.Sum256(data)) id.SetSHA256(sha256.Sum256(data))
return id return id

8
container/id/doc.go Normal file
View file

@ -0,0 +1,8 @@
/*
Package cid provides primitives to work with container identification in NeoFS.
Using package types in an application is recommended to potentially work with
different protocol versions with which these types are compatible.
*/
package cid

View file

@ -1,90 +1,120 @@
package cid package cid
import ( import (
"bytes"
"crypto/sha256" "crypto/sha256"
"errors" "fmt"
"github.com/mr-tron/base58" "github.com/mr-tron/base58"
"github.com/nspcc-dev/neofs-api-go/v2/refs" "github.com/nspcc-dev/neofs-api-go/v2/refs"
) )
// ID represents v2-compatible container identifier. // ID represents NeoFS container identifier.
type ID refs.ContainerID
// NewFromV2 wraps v2 ContainerID message to ID.
// //
// Nil refs.ContainerID converts to nil. // ID is mutually compatible with github.com/nspcc-dev/neofs-api-go/v2/refs.ContainerID
func NewFromV2(idV2 *refs.ContainerID) *ID { // message. See ReadFromV2 / WriteToV2 methods.
return (*ID)(idV2)
}
// New creates and initializes blank ID.
// //
// Defaults: // Instances can be created using built-in var declaration.
// - value: nil.
func New() *ID {
return NewFromV2(new(refs.ContainerID))
}
// SetSHA256 sets container identifier value to SHA256 checksum of container body.
func (id *ID) SetSHA256(v [sha256.Size]byte) {
(*refs.ContainerID)(id).SetValue(v[:])
}
// ToV2 returns the v2 container ID message.
// //
// Nil ID converts to nil. // Note that direct typecast is not safe and may result in loss of compatibility:
func (id *ID) ToV2() *refs.ContainerID { // _ = ID([32]byte) // not recommended
return (*refs.ContainerID)(id) type ID [sha256.Size]byte
}
// Equal returns true if identifiers are identical. // ReadFromV2 reads ID from the refs.ContainerID message.
func (id *ID) Equal(id2 *ID) bool { // Returns an error if the message is malformed according
return bytes.Equal( // to the NeoFS API V2 protocol.
(*refs.ContainerID)(id).GetValue(),
(*refs.ContainerID)(id2).GetValue(),
)
}
// Parse parses string representation of ID.
// //
// Returns error if s is not a base58 encoded // See also WriteToV2.
// ID data. func (id *ID) ReadFromV2(m refs.ContainerID) error {
func (id *ID) Parse(s string) error { return id.Decode(m.GetValue())
data, err := base58.Decode(s) }
if err != nil {
return err // WriteToV2 writes ID to the refs.ContainerID message.
} else if len(data) != sha256.Size { // The message must not be nil.
return errors.New("incorrect format of the string container ID") //
// See also ReadFromV2.
func (id ID) WriteToV2(m *refs.ContainerID) {
m.SetValue(id[:])
}
// Encode encodes ID into 32 bytes of dst. Panics if
// dst length is less than 32.
//
// Zero ID is all zeros.
//
// See also Decode.
func (id ID) Encode(dst []byte) {
if l := len(dst); l < sha256.Size {
panic(fmt.Sprintf("destination length is less than %d bytes: %d", sha256.Size, l))
} }
(*refs.ContainerID)(id).SetValue(data) copy(dst, id[:])
}
// Decode decodes src bytes into ID.
//
// Decode expects that src has 32 bytes length. If the input is malformed,
// Decode returns an error describing format violation. In this case ID
// remains unchanged.
//
// Decode doesn't mutate src.
//
// See also Encode.
func (id *ID) Decode(src []byte) error {
if len(src) != sha256.Size {
return fmt.Errorf("invalid length %d", len(src))
}
copy(id[:], src)
return nil return nil
} }
// String returns base58 string representation of ID. // SetSHA256 sets container identifier value to SHA256 checksum of container structure.
func (id *ID) String() string { func (id *ID) SetSHA256(v [sha256.Size]byte) {
return base58.Encode((*refs.ContainerID)(id).GetValue()) copy(id[:], v[:])
} }
// Marshal marshals ID into a protobuf binary form. // Equals defines a comparison relation between two ID instances.
func (id *ID) Marshal() ([]byte, error) { //
return (*refs.ContainerID)(id).StableMarshal(nil) // Note that comparison using '==' operator is not recommended since it MAY result
// in loss of compatibility.
func (id ID) Equals(id2 ID) bool {
return id == id2
} }
// Unmarshal unmarshals protobuf binary representation of ID. // EncodeToString encodes ID into NeoFS API protocol string.
func (id *ID) Unmarshal(data []byte) error { //
return (*refs.ContainerID)(id).Unmarshal(data) // Zero ID is base58 encoding of 32 zeros.
//
// See also DecodeString.
func (id ID) EncodeToString() string {
return base58.Encode(id[:])
} }
// MarshalJSON encodes ID to protobuf JSON format. // DecodeString decodes string into ID according to NeoFS API protocol. Returns
func (id *ID) MarshalJSON() ([]byte, error) { // an error if s is malformed.
return (*refs.ContainerID)(id).MarshalJSON() //
// See also DecodeString.
func (id *ID) DecodeString(s string) error {
data, err := base58.Decode(s)
if err != nil {
return fmt.Errorf("decode base58: %w", err)
}
return id.Decode(data)
} }
// UnmarshalJSON decodes ID from protobuf JSON format. // String implements fmt.Stringer.
func (id *ID) UnmarshalJSON(data []byte) error { //
return (*refs.ContainerID)(id).UnmarshalJSON(data) // 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 (id ID) String() string {
return id.EncodeToString()
}
// Empty returns true if it is called on
// zero container ID.
func (id ID) Empty() bool {
return id == ID{}
} }

View file

@ -5,6 +5,7 @@ import (
"math/rand" "math/rand"
"testing" "testing"
"github.com/mr-tron/base58"
"github.com/nspcc-dev/neofs-api-go/v2/refs" "github.com/nspcc-dev/neofs-api-go/v2/refs"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id" cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test" cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test"
@ -16,30 +17,32 @@ func randSHA256Checksum() (cs [sha256.Size]byte) {
return return
} }
const emptyID = "11111111111111111111111111111111"
func TestID_ToV2(t *testing.T) { func TestID_ToV2(t *testing.T) {
t.Run("non-nil", func(t *testing.T) { t.Run("non-zero", func(t *testing.T) {
checksum := randSHA256Checksum() checksum := randSHA256Checksum()
id := cidtest.IDWithChecksum(checksum) id := cidtest.IDWithChecksum(checksum)
idV2 := id.ToV2() var idV2 refs.ContainerID
id.WriteToV2(&idV2)
require.Equal(t, id, cid.NewFromV2(idV2)) var newID cid.ID
require.NoError(t, newID.ReadFromV2(idV2))
require.Equal(t, id, newID)
require.Equal(t, checksum[:], idV2.GetValue()) require.Equal(t, checksum[:], idV2.GetValue())
}) })
t.Run("nil", func(t *testing.T) { t.Run("zero", func(t *testing.T) {
var x *cid.ID var (
x cid.ID
v2 refs.ContainerID
)
require.Nil(t, x.ToV2()) x.WriteToV2(&v2)
}) require.Equal(t, emptyID, base58.Encode(v2.GetValue()))
t.Run("default values", func(t *testing.T) {
id := cid.New()
// convert to v2 message
cidV2 := id.ToV2()
require.Nil(t, cidV2.GetValue())
}) })
} }
@ -49,57 +52,57 @@ func TestID_Equal(t *testing.T) {
id1 := cidtest.IDWithChecksum(cs) id1 := cidtest.IDWithChecksum(cs)
id2 := cidtest.IDWithChecksum(cs) id2 := cidtest.IDWithChecksum(cs)
require.True(t, id1.Equal(id2)) require.True(t, id1.Equals(id2))
id3 := cidtest.ID() id3 := cidtest.ID()
require.False(t, id1.Equal(id3)) require.False(t, id1.Equals(id3))
} }
func TestID_String(t *testing.T) { func TestID_String(t *testing.T) {
t.Run("Parse/String", func(t *testing.T) { t.Run("DecodeString/EncodeToString", func(t *testing.T) {
id := cidtest.ID() id := cidtest.ID()
id2 := cid.New() var id2 cid.ID
require.NoError(t, id2.Parse(id.String())) require.NoError(t, id2.DecodeString(id.String()))
require.Equal(t, id, id2) require.Equal(t, id, id2)
}) })
t.Run("nil", func(t *testing.T) { t.Run("zero", func(t *testing.T) {
id := cid.New() var id cid.ID
require.Empty(t, id.String()) require.Equal(t, emptyID, id.EncodeToString())
})
}
func TestContainerIDEncoding(t *testing.T) {
id := cidtest.ID()
t.Run("binary", func(t *testing.T) {
data, err := id.Marshal()
require.NoError(t, err)
id2 := cid.New()
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 := cid.New()
require.NoError(t, a2.UnmarshalJSON(data))
require.Equal(t, id, a2)
}) })
} }
func TestNewFromV2(t *testing.T) { func TestNewFromV2(t *testing.T) {
t.Run("from nil", func(t *testing.T) { t.Run("from zero", func(t *testing.T) {
var x *refs.ContainerID var (
x cid.ID
v2 refs.ContainerID
)
require.Nil(t, cid.NewFromV2(x)) require.Error(t, x.ReadFromV2(v2))
})
}
func TestID_Encode(t *testing.T) {
var id cid.ID
t.Run("panic", func(t *testing.T) {
dst := make([]byte, sha256.Size-1)
require.Panics(t, func() {
id.Encode(dst)
})
})
t.Run("correct", func(t *testing.T) {
dst := make([]byte, sha256.Size)
require.NotPanics(t, func() {
id.Encode(dst)
})
require.Equal(t, emptyID, id.EncodeToString())
}) })
} }

13
container/id/test/doc.go Normal file
View file

@ -0,0 +1,13 @@
/*
Package cidtest provides functions for convenient testing of cid 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 cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test"
cid := cidtest.ID()
// test the value
*/
package cidtest

View file

@ -8,7 +8,7 @@ import (
) )
// ID returns random cid.ID. // ID returns random cid.ID.
func ID() *cid.ID { func ID() cid.ID {
checksum := [sha256.Size]byte{} checksum := [sha256.Size]byte{}
rand.Read(checksum[:]) rand.Read(checksum[:])
@ -18,8 +18,8 @@ func ID() *cid.ID {
// IDWithChecksum returns cid.ID initialized // IDWithChecksum returns cid.ID initialized
// with specified checksum. // with specified checksum.
func IDWithChecksum(cs [sha256.Size]byte) *cid.ID { func IDWithChecksum(cs [sha256.Size]byte) cid.ID {
id := cid.New() var id cid.ID
id.SetSHA256(cs) id.SetSHA256(cs)
return id return id

View file

@ -112,12 +112,12 @@ func (r *Record) AddObjectVersionFilter(m Match, v *version.Version) {
} }
// AddObjectIDFilter adds filter by object ID. // AddObjectIDFilter adds filter by object ID.
func (r *Record) AddObjectIDFilter(m Match, id *oid.ID) { func (r *Record) AddObjectIDFilter(m Match, id oid.ID) {
r.addObjectReservedFilter(m, fKeyObjID, id) r.addObjectReservedFilter(m, fKeyObjID, id)
} }
// AddObjectContainerIDFilter adds filter by object container ID. // AddObjectContainerIDFilter adds filter by object container ID.
func (r *Record) AddObjectContainerIDFilter(m Match, id *cid.ID) { func (r *Record) AddObjectContainerIDFilter(m Match, id cid.ID) {
r.addObjectReservedFilter(m, fKeyObjContainerID, id) r.addObjectReservedFilter(m, fKeyObjContainerID, id)
} }

View file

@ -16,19 +16,19 @@ import (
// Table is compatible with v2 acl.EACLTable message. // Table is compatible with v2 acl.EACLTable message.
type Table struct { type Table struct {
version version.Version version version.Version
cid *cid.ID cid cid.ID
token *session.Token token *session.Token
sig *signature.Signature sig *signature.Signature
records []Record records []Record
} }
// CID returns identifier of the container that should use given access control rules. // CID returns identifier of the container that should use given access control rules.
func (t Table) CID() *cid.ID { func (t Table) CID() cid.ID {
return t.cid return t.cid
} }
// SetCID sets identifier of the container that should use given access control rules. // SetCID sets identifier of the container that should use given access control rules.
func (t *Table) SetCID(cid *cid.ID) { func (t *Table) SetCID(cid cid.ID) {
t.cid = cid t.cid = cid
} }
@ -85,9 +85,11 @@ func (t *Table) ToV2() *v2acl.Table {
} }
v2 := new(v2acl.Table) v2 := new(v2acl.Table)
var cidV2 refs.ContainerID
if t.cid != nil { if !t.cid.Empty() {
v2.SetContainerID(t.cid.ToV2()) t.cid.WriteToV2(&cidV2)
v2.SetContainerID(&cidV2)
} }
if t.records != nil { if t.records != nil {
@ -124,7 +126,7 @@ func NewTable() *Table {
// CreateTable creates, initializes with parameters and returns Table instance. // CreateTable creates, initializes with parameters and returns Table instance.
func CreateTable(cid cid.ID) *Table { func CreateTable(cid cid.ID) *Table {
t := NewTable() t := NewTable()
t.SetCID(&cid) t.SetCID(cid)
return t return t
} }
@ -148,8 +150,8 @@ func NewTableFromV2(table *v2acl.Table) *Table {
// set container id // set container id
if id := table.GetContainerID(); id != nil { if id := table.GetContainerID(); id != nil {
if t.cid == nil { if t.cid.Empty() {
t.cid = new(cid.ID) t.cid = cid.ID{}
} }
var h [sha256.Size]byte var h [sha256.Size]byte
@ -205,7 +207,7 @@ func (t *Table) UnmarshalJSON(data []byte) error {
// EqualTables compares Table with each other. // EqualTables compares Table with each other.
func EqualTables(t1, t2 Table) bool { func EqualTables(t1, t2 Table) bool {
if !t1.CID().Equal(t2.CID()) || if !t1.CID().Equals(t2.CID()) ||
!t1.Version().Equal(t2.Version()) { !t1.Version().Equal(t2.Version()) {
return false return false
} }

View file

@ -45,7 +45,7 @@ func TestTable(t *testing.T) {
t.Run("create table", func(t *testing.T) { t.Run("create table", func(t *testing.T) {
id := cidtest.ID() id := cidtest.ID()
table := eacl.CreateTable(*id) table := eacl.CreateTable(id)
require.Equal(t, id, table.CID()) require.Equal(t, id, table.CID())
require.Equal(t, version.Current(), table.Version()) require.Equal(t, version.Current(), table.Version())
}) })
@ -124,7 +124,7 @@ func TestTable_ToV2(t *testing.T) {
// check initial values // check initial values
require.Equal(t, version.Current(), table.Version()) require.Equal(t, version.Current(), table.Version())
require.Nil(t, table.Records()) require.Nil(t, table.Records())
require.Nil(t, table.CID()) require.True(t, table.CID().Empty())
require.Nil(t, table.SessionToken()) require.Nil(t, table.SessionToken())
require.Nil(t, table.Signature()) require.Nil(t, table.Signature())

View file

@ -16,20 +16,20 @@ type DNS struct{}
// Otherwise, returns an error. // Otherwise, returns an error.
// //
// See also net.LookupTXT. // See also net.LookupTXT.
func (x *DNS) ResolveContainerName(name string) (*cid.ID, error) { func (x *DNS) ResolveContainerName(name string) (id cid.ID, err error) {
records, err := net.LookupTXT(name) records, err := net.LookupTXT(name)
if err != nil { if err != nil {
return nil, err return
} }
var id cid.ID
for i := range records { for i := range records {
err = id.Parse(records[i]) err = id.DecodeString(records[i])
if err == nil { if err == nil {
return &id, nil return
} }
} }
return nil, errNotFound err = errNotFound
return
} }

View file

@ -114,7 +114,7 @@ func (n *NNS) Dial(address string) error {
// ResolveContainerName MUST NOT be called before successful Dial. // ResolveContainerName MUST NOT be called before successful Dial.
// //
// See also https://docs.neo.org/docs/en-us/reference/nns.html. // See also https://docs.neo.org/docs/en-us/reference/nns.html.
func (n *NNS) ResolveContainerName(name string) (*cid.ID, error) { func (n *NNS) ResolveContainerName(name string) (cid.ID, error) {
res, err := n.neoClient.call(n.nnsContract, "resolve", []smartcontract.Parameter{ res, err := n.neoClient.call(n.nnsContract, "resolve", []smartcontract.Parameter{
{ {
Type: smartcontract.StringType, Type: smartcontract.StringType,
@ -126,25 +126,25 @@ func (n *NNS) ResolveContainerName(name string) (*cid.ID, error) {
}, },
}) })
if err != nil { if err != nil {
return nil, fmt.Errorf("invoke NNS contract: %w", err) return cid.ID{}, fmt.Errorf("invoke NNS contract: %w", err)
} }
if res.State != vm.HaltState.String() { if res.State != vm.HaltState.String() {
return nil, fmt.Errorf("NNS contract fault exception: %s", res.FaultException) return cid.ID{}, fmt.Errorf("NNS contract fault exception: %s", res.FaultException)
} else if len(res.Stack) == 0 { } else if len(res.Stack) == 0 {
return nil, errors.New("empty stack in invocation result") return cid.ID{}, errors.New("empty stack in invocation result")
} }
itemArr, err := res.Stack[len(res.Stack)-1].Convert(stackitem.ArrayT) // top stack element is last in the array itemArr, err := res.Stack[len(res.Stack)-1].Convert(stackitem.ArrayT) // top stack element is last in the array
if err != nil { if err != nil {
return nil, fmt.Errorf("convert stack item to %s", stackitem.ArrayT) return cid.ID{}, fmt.Errorf("convert stack item to %s", stackitem.ArrayT)
} }
if _, ok := itemArr.(stackitem.Null); !ok { if _, ok := itemArr.(stackitem.Null); !ok {
arr, ok := itemArr.Value().([]stackitem.Item) arr, ok := itemArr.Value().([]stackitem.Item)
if !ok { if !ok {
// unexpected for types from stackitem package // unexpected for types from stackitem package
return nil, errors.New("invalid cast to stack item slice") return cid.ID{}, errors.New("invalid cast to stack item slice")
} }
var id cid.ID var id cid.ID
@ -152,15 +152,15 @@ func (n *NNS) ResolveContainerName(name string) (*cid.ID, error) {
for i := range arr { for i := range arr {
bs, err := arr[i].TryBytes() bs, err := arr[i].TryBytes()
if err != nil { if err != nil {
return nil, fmt.Errorf("convert array item to byte slice: %w", err) return cid.ID{}, fmt.Errorf("convert array item to byte slice: %w", err)
} }
err = id.Parse(string(bs)) err = id.DecodeString(string(bs))
if err == nil { if err == nil {
return &id, nil return id, nil
} }
} }
} }
return nil, errNotFound return cid.ID{}, errNotFound
} }

View file

@ -45,41 +45,61 @@ func (a *Address) ToV2() *refs.Address {
} }
// ContainerID returns container identifier. // ContainerID returns container identifier.
func (a *Address) ContainerID() *cid.ID { func (a *Address) ContainerID() (v cid.ID) {
return cid.NewFromV2( var cID cid.ID
(*refs.Address)(a).GetContainerID()) v2 := (*refs.Address)(a)
cidV2 := v2.GetContainerID()
if cidV2 != nil {
_ = cID.ReadFromV2(*cidV2)
}
return cID
} }
// SetContainerID sets container identifier. // SetContainerID sets container identifier.
func (a *Address) SetContainerID(id *cid.ID) { func (a *Address) SetContainerID(id cid.ID) {
(*refs.Address)(a).SetContainerID(id.ToV2()) var cidV2 refs.ContainerID
id.WriteToV2(&cidV2)
(*refs.Address)(a).SetContainerID(&cidV2)
} }
// ObjectID returns object identifier. // ObjectID returns object identifier.
func (a *Address) ObjectID() *oid.ID { func (a *Address) ObjectID() (v oid.ID) {
return oid.NewIDFromV2( var id oid.ID
(*refs.Address)(a).GetObjectID()) v2 := (*refs.Address)(a)
oidV2 := v2.GetObjectID()
if oidV2 != nil {
_ = id.ReadFromV2(*oidV2)
}
return id
} }
// SetObjectID sets object identifier. // SetObjectID sets object identifier.
func (a *Address) SetObjectID(id *oid.ID) { func (a *Address) SetObjectID(id oid.ID) {
(*refs.Address)(a).SetObjectID(id.ToV2()) var oidV2 refs.ObjectID
id.WriteToV2(&oidV2)
(*refs.Address)(a).SetObjectID(&oidV2)
} }
// Parse converts base58 string representation into Address. // Parse converts base58 string representation into Address.
func (a *Address) Parse(s string) error { func (a *Address) Parse(s string) error {
var ( var (
err error err error
oid = oid.NewID() oid oid.ID
id = cid.New() id cid.ID
parts = strings.Split(s, addressSeparator) parts = strings.Split(s, addressSeparator)
) )
if len(parts) != addressParts { if len(parts) != addressParts {
return errInvalidAddressString return errInvalidAddressString
} else if err = id.Parse(parts[0]); err != nil { } else if err = id.DecodeString(parts[0]); err != nil {
return err return err
} else if err = oid.Parse(parts[1]); err != nil { } else if err = oid.DecodeString(parts[1]); err != nil {
return err return err
} }

View file

@ -107,8 +107,8 @@ func TestNewAddress(t *testing.T) {
a := NewAddress() a := NewAddress()
// check initial values // check initial values
require.Nil(t, a.ContainerID()) require.True(t, a.ContainerID().Empty())
require.Nil(t, a.ObjectID()) require.True(t, a.ObjectID().Empty())
// convert to v2 message // convert to v2 message
aV2 := a.ToV2() aV2 := a.ToV2()

View file

@ -7,10 +7,10 @@ import (
"errors" "errors"
"fmt" "fmt"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
signatureV2 "github.com/nspcc-dev/neofs-api-go/v2/signature" signatureV2 "github.com/nspcc-dev/neofs-api-go/v2/signature"
"github.com/nspcc-dev/neofs-sdk-go/checksum" "github.com/nspcc-dev/neofs-sdk-go/checksum"
oid "github.com/nspcc-dev/neofs-sdk-go/object/id" oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
"github.com/nspcc-dev/neofs-sdk-go/signature"
sigutil "github.com/nspcc-dev/neofs-sdk-go/util/signature" sigutil "github.com/nspcc-dev/neofs-sdk-go/util/signature"
) )
@ -55,13 +55,13 @@ func VerifyPayloadChecksum(obj *Object) error {
} }
// CalculateID calculates identifier for the object. // CalculateID calculates identifier for the object.
func CalculateID(obj *Object) (*oid.ID, error) { func CalculateID(obj *Object) (oid.ID, error) {
data, err := obj.ToV2().GetHeader().StableMarshal(nil) data, err := obj.ToV2().GetHeader().StableMarshal(nil)
if err != nil { if err != nil {
return nil, err return oid.ID{}, err
} }
id := oid.NewID() var id oid.ID
id.SetSHA256(sha256.Sum256(data)) id.SetSHA256(sha256.Sum256(data))
return id, nil return id, nil
@ -88,43 +88,45 @@ func VerifyID(obj *Object) error {
return err return err
} }
if !id.Equal(obj.ID()) { if !id.Equals(obj.ID()) {
return errIncorrectID return errIncorrectID
} }
return nil return nil
} }
func CalculateIDSignature(key *ecdsa.PrivateKey, id *oid.ID) (*signature.Signature, error) { // CalculateAndSetSignature signs id with provided key and sets that signature to
return sigutil.SignData( // the object.
key, func CalculateAndSetSignature(key ecdsa.PrivateKey, obj *Object) error {
signatureV2.StableMarshalerWrapper{ sig, err := obj.ID().CalculateIDSignature(key)
SM: id.ToV2(),
})
}
func CalculateAndSetSignature(key *ecdsa.PrivateKey, obj *Object) error {
sig, err := CalculateIDSignature(key, obj.ID())
if err != nil { if err != nil {
return err return err
} }
obj.SetSignature(sig) obj.SetSignature(&sig)
return nil return nil
} }
func VerifyIDSignature(obj *Object) error { // VerifyIDSignature verifies object ID signature.
return sigutil.VerifyData( func (o *Object) VerifyIDSignature() bool {
var idV2 refs.ObjectID
o.ID().WriteToV2(&idV2)
sig := o.Signature()
err := sigutil.VerifyData(
signatureV2.StableMarshalerWrapper{ signatureV2.StableMarshalerWrapper{
SM: obj.ID().ToV2(), SM: &idV2,
}, },
obj.Signature(), sig,
) )
return err == nil
} }
// SetIDWithSignature sets object identifier and signature. // SetIDWithSignature sets object identifier and signature.
func SetIDWithSignature(key *ecdsa.PrivateKey, obj *Object) error { func SetIDWithSignature(key ecdsa.PrivateKey, obj *Object) error {
if err := CalculateAndSetID(obj); err != nil { if err := CalculateAndSetID(obj); err != nil {
return fmt.Errorf("could not set identifier: %w", err) return fmt.Errorf("could not set identifier: %w", err)
} }
@ -137,7 +139,7 @@ func SetIDWithSignature(key *ecdsa.PrivateKey, obj *Object) error {
} }
// SetVerificationFields calculates and sets all verification fields of the object. // SetVerificationFields calculates and sets all verification fields of the object.
func SetVerificationFields(key *ecdsa.PrivateKey, obj *Object) error { func SetVerificationFields(key ecdsa.PrivateKey, obj *Object) error {
CalculateAndSetPayloadChecksum(obj) CalculateAndSetPayloadChecksum(obj)
return SetIDWithSignature(key, obj) return SetIDWithSignature(key, obj)
@ -156,10 +158,12 @@ func CheckVerificationFields(obj *Object) error {
return nil return nil
} }
var errInvalidSignature = errors.New("invalid signature")
// CheckHeaderVerificationFields checks all verification fields except payload. // CheckHeaderVerificationFields checks all verification fields except payload.
func CheckHeaderVerificationFields(obj *Object) error { func CheckHeaderVerificationFields(obj *Object) error {
if err := VerifyIDSignature(obj); err != nil { if !obj.VerifyIDSignature() {
return fmt.Errorf("invalid signature: %w", err) return errInvalidSignature
} }
if err := VerifyID(obj); err != nil { if err := VerifyID(obj); err != nil {

View file

@ -19,7 +19,7 @@ func TestVerificationFields(t *testing.T) {
p, err := keys.NewPrivateKey() p, err := keys.NewPrivateKey()
require.NoError(t, err) require.NoError(t, err)
require.NoError(t, SetVerificationFields(&p.PrivateKey, obj)) require.NoError(t, SetVerificationFields(p.PrivateKey, obj))
require.NoError(t, CheckVerificationFields(obj)) require.NoError(t, CheckVerificationFields(obj))
@ -45,10 +45,10 @@ func TestVerificationFields(t *testing.T) {
}, },
{ {
corrupt: func() { corrupt: func() {
obj.ID().ToV2().GetValue()[0]++ obj.ToV2().GetObjectID().GetValue()[0]++
}, },
restore: func() { restore: func() {
obj.ID().ToV2().GetValue()[0]-- obj.ToV2().GetObjectID().GetValue()[0]--
}, },
}, },
{ {

8
object/id/doc.go Normal file
View file

@ -0,0 +1,8 @@
/*
Package oid provides primitives to work with object identification in NeoFS.
Using package types in an application is recommended to potentially work with
different protocol versions with which these types are compatible.
*/
package oid

View file

@ -1,92 +1,177 @@
package oid package oid
import ( import (
"bytes" "crypto/ecdsa"
"crypto/sha256" "crypto/sha256"
"errors"
"fmt" "fmt"
"github.com/mr-tron/base58" "github.com/mr-tron/base58"
"github.com/nspcc-dev/neofs-api-go/v2/refs" "github.com/nspcc-dev/neofs-api-go/v2/refs"
signatureV2 "github.com/nspcc-dev/neofs-api-go/v2/signature"
"github.com/nspcc-dev/neofs-sdk-go/signature"
sigutil "github.com/nspcc-dev/neofs-sdk-go/util/signature"
) )
// ID represents v2-compatible object identifier. // ID represents NeoFS object identifier in a container.
type ID refs.ObjectID
var errInvalidIDString = errors.New("incorrect format of the string object ID")
// NewIDFromV2 wraps v2 ObjectID message to ID.
// //
// Nil refs.ObjectID converts to nil. // ID is mutually compatible with github.com/nspcc-dev/neofs-api-go/v2/refs.ObjectID
func NewIDFromV2(idV2 *refs.ObjectID) *ID { // message. See ReadFromV2 / WriteToV2 methods.
return (*ID)(idV2)
}
// NewID creates and initializes blank ID.
// //
// Works similar as NewIDFromV2(new(ObjectID)). // Instances can be created using built-in var declaration.
// //
// Defaults: // Note that direct typecast is not safe and may result in loss of compatibility:
// - value: nil. // _ = ObjectID([32]byte{}) // not recommended
func NewID() *ID { type ID [sha256.Size]byte
return NewIDFromV2(new(refs.ObjectID))
}
// SetSHA256 sets object identifier value to SHA256 checksum. // ReadFromV2 reads ID from the refs.ObjectID message. Returns an error if
func (id *ID) SetSHA256(v [sha256.Size]byte) { // the message is malformed according to the NeoFS API V2 protocol.
(*refs.ObjectID)(id).SetValue(v[:])
}
// Equal returns true if identifiers are identical.
func (id *ID) Equal(id2 *ID) bool {
return bytes.Equal(
(*refs.ObjectID)(id).GetValue(),
(*refs.ObjectID)(id2).GetValue(),
)
}
// ToV2 converts ID to v2 ObjectID message.
// //
// Nil ID converts to nil. // See also WriteToV2.
func (id *ID) ToV2() *refs.ObjectID { func (id *ID) ReadFromV2(m refs.ObjectID) error {
return (*refs.ObjectID)(id) return id.Decode(m.GetValue())
} }
// Parse converts base58 string representation into ID. // WriteToV2 writes ID to the refs.ObjectID message.
func (id *ID) Parse(s string) error { // The message must not be nil.
data, err := base58.Decode(s) //
if err != nil { // See also ReadFromV2.
return fmt.Errorf("could not parse object.ID from string: %w", err) func (id ID) WriteToV2(m *refs.ObjectID) {
} else if len(data) != sha256.Size { m.SetValue(id[:])
return errInvalidIDString }
// Encode encodes ID into 32 bytes of dst. Panics if
// dst length is less than 32.
//
// Zero ID is all zeros.
//
// See also Decode.
func (id ID) Encode(dst []byte) {
if l := len(dst); l < sha256.Size {
panic(fmt.Sprintf("destination length is less than %d bytes: %d", sha256.Size, l))
} }
(*refs.ObjectID)(id).SetValue(data) copy(dst, id[:])
}
// Decode decodes src bytes into ID.
//
// Decode expects that src has 32 bytes length. If the input is malformed,
// Decode returns an error describing format violation. In this case ID
// remains unchanged.
//
// Decode doesn't mutate src.
//
// See also Encode.
func (id *ID) Decode(src []byte) error {
if len(src) != 32 {
return fmt.Errorf("invalid length %d", len(src))
}
copy(id[:], src)
return nil return nil
} }
// String returns base58 string representation of ID. // SetSHA256 sets object identifier value to SHA256 checksum.
func (id *ID) String() string { func (id *ID) SetSHA256(v [sha256.Size]byte) {
return base58.Encode((*refs.ObjectID)(id).GetValue()) copy(id[:], v[:])
}
// Equals defines a comparison relation between two ID instances.
//
// Note that comparison using '==' operator is not recommended since it MAY result
// in loss of compatibility.
func (id ID) Equals(id2 ID) bool {
return id == id2
}
// EncodeToString encodes ID into NeoFS API protocol string.
//
// Zero ID is base58 encoding of 32 zeros.
//
// See also DecodeString.
func (id ID) EncodeToString() string {
return base58.Encode(id[:])
}
// DecodeString decodes string into ID according to NeoFS API protocol. Returns
// an error if s is malformed.
//
// See also DecodeString.
func (id *ID) DecodeString(s string) error {
data, err := base58.Decode(s)
if err != nil {
return fmt.Errorf("decode base58: %w", err)
}
return id.Decode(data)
}
// 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 (id ID) String() string {
return id.EncodeToString()
}
// CalculateIDSignature signs object id with provided key.
func (id ID) CalculateIDSignature(key ecdsa.PrivateKey) (signature.Signature, error) {
var idV2 refs.ObjectID
id.WriteToV2(&idV2)
sign, err := sigutil.SignData(&key,
signatureV2.StableMarshalerWrapper{
SM: &idV2,
},
)
return *sign, err
}
// Empty returns true if it is called on
// zero object ID.
func (id ID) Empty() bool {
return id == ID{}
} }
// Marshal marshals ID into a protobuf binary form. // Marshal marshals ID into a protobuf binary form.
func (id *ID) Marshal() ([]byte, error) { func (id ID) Marshal() ([]byte, error) {
return (*refs.ObjectID)(id).StableMarshal(nil) var v2 refs.ObjectID
v2.SetValue(id[:])
return v2.StableMarshal(nil)
} }
// Unmarshal unmarshals protobuf binary representation of ID. // Unmarshal unmarshals protobuf binary representation of ID.
func (id *ID) Unmarshal(data []byte) error { func (id *ID) Unmarshal(data []byte) error {
return (*refs.ObjectID)(id).Unmarshal(data) var v2 refs.ObjectID
if err := v2.Unmarshal(data); err != nil {
return err
}
copy(id[:], v2.GetValue())
return nil
} }
// MarshalJSON encodes ID to protobuf JSON format. // MarshalJSON encodes ID to protobuf JSON format.
func (id *ID) MarshalJSON() ([]byte, error) { func (id ID) MarshalJSON() ([]byte, error) {
return (*refs.ObjectID)(id).MarshalJSON() var v2 refs.ObjectID
v2.SetValue(id[:])
return v2.MarshalJSON()
} }
// UnmarshalJSON decodes ID from protobuf JSON format. // UnmarshalJSON decodes ID from protobuf JSON format.
func (id *ID) UnmarshalJSON(data []byte) error { func (id *ID) UnmarshalJSON(data []byte) error {
return (*refs.ObjectID)(id).UnmarshalJSON(data) var v2 refs.ObjectID
if err := v2.UnmarshalJSON(data); err != nil {
return err
}
copy(id[:], v2.GetValue())
return nil
} }

View file

@ -11,8 +11,10 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func randID(t *testing.T) *ID { const emptyID = "11111111111111111111111111111111"
id := NewID()
func randID(t *testing.T) ID {
var id ID
id.SetSHA256(randSHA256Checksum(t)) id.SetSHA256(randSHA256Checksum(t))
return id return id
@ -26,7 +28,7 @@ func randSHA256Checksum(t *testing.T) (cs [sha256.Size]byte) {
} }
func TestIDV2(t *testing.T) { func TestIDV2(t *testing.T) {
id := NewID() var id ID
checksum := [sha256.Size]byte{} checksum := [sha256.Size]byte{}
@ -35,7 +37,8 @@ func TestIDV2(t *testing.T) {
id.SetSHA256(checksum) id.SetSHA256(checksum)
idV2 := id.ToV2() var idV2 refs.ObjectID
id.WriteToV2(&idV2)
require.Equal(t, checksum[:], idV2.GetValue()) require.Equal(t, checksum[:], idV2.GetValue())
} }
@ -43,17 +46,17 @@ func TestIDV2(t *testing.T) {
func TestID_Equal(t *testing.T) { func TestID_Equal(t *testing.T) {
cs := randSHA256Checksum(t) cs := randSHA256Checksum(t)
id1 := NewID() var id1 ID
id1.SetSHA256(cs) id1.SetSHA256(cs)
id2 := NewID() var id2 ID
id2.SetSHA256(cs) id2.SetSHA256(cs)
id3 := NewID() var id3 ID
id3.SetSHA256(randSHA256Checksum(t)) id3.SetSHA256(randSHA256Checksum(t))
require.True(t, id1.Equal(id2)) require.True(t, id1.Equals(id2))
require.False(t, id1.Equal(id3)) require.False(t, id1.Equals(id3))
} }
func TestID_Parse(t *testing.T) { func TestID_Parse(t *testing.T) {
@ -62,10 +65,14 @@ func TestID_Parse(t *testing.T) {
t.Run(strconv.Itoa(i), func(t *testing.T) { t.Run(strconv.Itoa(i), func(t *testing.T) {
cs := randSHA256Checksum(t) cs := randSHA256Checksum(t)
str := base58.Encode(cs[:]) str := base58.Encode(cs[:])
oid := NewID() var oid ID
require.NoError(t, oid.Parse(str)) require.NoError(t, oid.DecodeString(str))
require.Equal(t, cs[:], oid.ToV2().GetValue())
var oidV2 refs.ObjectID
oid.WriteToV2(&oidV2)
require.Equal(t, cs[:], oidV2.GetValue())
}) })
} }
}) })
@ -76,18 +83,18 @@ func TestID_Parse(t *testing.T) {
t.Run(strconv.Itoa(j), func(t *testing.T) { t.Run(strconv.Itoa(j), func(t *testing.T) {
cs := []byte{1, 2, 3, 4, 5, byte(j)} cs := []byte{1, 2, 3, 4, 5, byte(j)}
str := base58.Encode(cs) str := base58.Encode(cs)
oid := NewID() var oid ID
require.Error(t, oid.Parse(str)) require.Error(t, oid.DecodeString(str))
}) })
} }
}) })
} }
func TestID_String(t *testing.T) { func TestID_String(t *testing.T) {
t.Run("nil", func(t *testing.T) { t.Run("zero", func(t *testing.T) {
id := NewID() var id ID
require.Empty(t, id.String()) require.Equal(t, emptyID, id.EncodeToString())
}) })
t.Run("should be equal", func(t *testing.T) { t.Run("should be equal", func(t *testing.T) {
@ -95,9 +102,9 @@ func TestID_String(t *testing.T) {
t.Run(strconv.Itoa(i), func(t *testing.T) { t.Run(strconv.Itoa(i), func(t *testing.T) {
cs := randSHA256Checksum(t) cs := randSHA256Checksum(t)
str := base58.Encode(cs[:]) str := base58.Encode(cs[:])
oid := NewID() var oid ID
require.NoError(t, oid.Parse(str)) require.NoError(t, oid.DecodeString(str))
require.Equal(t, str, oid.String()) require.Equal(t, str, oid.String())
}) })
} }
@ -111,7 +118,7 @@ func TestObjectIDEncoding(t *testing.T) {
data, err := id.Marshal() data, err := id.Marshal()
require.NoError(t, err) require.NoError(t, err)
id2 := NewID() var id2 ID
require.NoError(t, id2.Unmarshal(data)) require.NoError(t, id2.Unmarshal(data))
require.Equal(t, id, id2) require.Equal(t, id, id2)
@ -121,36 +128,55 @@ func TestObjectIDEncoding(t *testing.T) {
data, err := id.MarshalJSON() data, err := id.MarshalJSON()
require.NoError(t, err) require.NoError(t, err)
a2 := NewID() var id2 ID
require.NoError(t, a2.UnmarshalJSON(data)) require.NoError(t, id2.UnmarshalJSON(data))
require.Equal(t, id, a2) require.Equal(t, id, id2)
}) })
} }
func TestNewIDFromV2(t *testing.T) { func TestNewIDFromV2(t *testing.T) {
t.Run("from nil", func(t *testing.T) { t.Run("from zero", func(t *testing.T) {
var x *refs.ObjectID var (
x ID
v2 refs.ObjectID
)
require.Nil(t, NewIDFromV2(x)) require.Error(t, x.ReadFromV2(v2))
}) })
} }
func TestID_ToV2(t *testing.T) { func TestID_ToV2(t *testing.T) {
t.Run("nil", func(t *testing.T) { t.Run("zero to v2", func(t *testing.T) {
var x *ID var (
x ID
v2 refs.ObjectID
)
require.Nil(t, x.ToV2()) x.WriteToV2(&v2)
require.Equal(t, sha256.Size, len(v2.GetValue()))
require.Equal(t, emptyID, base58.Encode(v2.GetValue()))
}) })
} }
func TestNewID(t *testing.T) { func TestID_Encode(t *testing.T) {
t.Run("default values", func(t *testing.T) { var id ID
id := NewID()
// convert to v2 message t.Run("panic", func(t *testing.T) {
idV2 := id.ToV2() dst := make([]byte, sha256.Size-1)
require.Nil(t, idV2.GetValue()) require.Panics(t, func() {
id.Encode(dst)
})
})
t.Run("correct", func(t *testing.T) {
dst := make([]byte, sha256.Size)
require.NotPanics(t, func() {
id.Encode(dst)
})
require.Equal(t, emptyID, id.EncodeToString())
}) })
} }

13
object/id/test/doc.go Normal file
View file

@ -0,0 +1,13 @@
/*
Package oidtest provides functions for convenient testing of oid 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 oidtest "github.com/nspcc-dev/neofs-sdk-go/object/id/test"
dec := oidtest.ID()
// test the value
*/
package oidtest

View file

@ -1,4 +1,4 @@
package test package oidtest
import ( import (
"crypto/sha256" "crypto/sha256"
@ -8,7 +8,7 @@ import (
) )
// ID returns random object.ID. // ID returns random object.ID.
func ID() *oid.ID { func ID() oid.ID {
checksum := [sha256.Size]byte{} checksum := [sha256.Size]byte{}
rand.Read(checksum[:]) rand.Read(checksum[:])
@ -18,8 +18,8 @@ func ID() *oid.ID {
// idWithChecksum returns object.ID initialized // idWithChecksum returns object.ID initialized
// with specified checksum. // with specified checksum.
func idWithChecksum(cs [sha256.Size]byte) *oid.ID { func idWithChecksum(cs [sha256.Size]byte) oid.ID {
id := oid.NewID() var id oid.ID
id.SetSHA256(cs) id.SetSHA256(cs)
return id return id

View file

@ -41,8 +41,8 @@ func (x Lock) NumberOfMembers() int {
func (x Lock) ReadMembers(buf []oid.ID) { func (x Lock) ReadMembers(buf []oid.ID) {
var i int var i int
(*v2object.Lock)(&x).IterateMembers(func(id refs.ObjectID) { (*v2object.Lock)(&x).IterateMembers(func(idV2 refs.ObjectID) {
buf[i] = *oid.NewIDFromV2(&id) // need smth better _ = buf[i].ReadFromV2(idV2)
i++ i++
}) })
} }
@ -55,7 +55,7 @@ func (x *Lock) WriteMembers(ids []oid.ID) {
members = make([]refs.ObjectID, len(ids)) members = make([]refs.ObjectID, len(ids))
for i := range ids { for i := range ids {
members[i] = *ids[i].ToV2() // need smth better ids[i].WriteToV2(&members[i])
} }
} }

View file

@ -34,7 +34,7 @@ type RequiredFields struct {
// InitCreation initializes the object instance with minimum set of required fields. // InitCreation initializes the object instance with minimum set of required fields.
// Object is expected (but not required) to be blank. Object must not be nil. // Object is expected (but not required) to be blank. Object must not be nil.
func InitCreation(dst *Object, rf RequiredFields) { func InitCreation(dst *Object, rf RequiredFields) {
dst.SetContainerID(&rf.Container) dst.SetContainerID(rf.Container)
dst.SetOwnerID(&rf.Owner) dst.SetOwnerID(&rf.Owner)
} }
@ -86,17 +86,24 @@ func (o *Object) setSplitFields(setter func(*object.SplitHeader)) {
} }
// ID returns object identifier. // ID returns object identifier.
func (o *Object) ID() *oid.ID { func (o *Object) ID() oid.ID {
return oid.NewIDFromV2( var v oid.ID
(*object.Object)(o).
GetObjectID(), v2 := (*object.Object)(o)
) if id := v2.GetObjectID(); id != nil {
_ = v.ReadFromV2(*v2.GetObjectID())
}
return v
} }
// SetID sets object identifier. // SetID sets object identifier.
func (o *Object) SetID(v *oid.ID) { func (o *Object) SetID(v oid.ID) {
var v2 refs.ObjectID
v.WriteToV2(&v2)
(*object.Object)(o). (*object.Object)(o).
SetObjectID(v.ToV2()) SetObjectID(&v2)
} }
// Signature returns signature of the object identifier. // Signature returns signature of the object identifier.
@ -154,18 +161,25 @@ func (o *Object) SetPayloadSize(v uint64) {
} }
// ContainerID returns identifier of the related container. // ContainerID returns identifier of the related container.
func (o *Object) ContainerID() *cid.ID { func (o *Object) ContainerID() cid.ID {
return cid.NewFromV2( var cID cid.ID
(*object.Object)(o). v2 := (*object.Object)(o)
GetHeader().
GetContainerID(), cidV2 := v2.GetHeader().GetContainerID()
) if cidV2 != nil {
_ = cID.ReadFromV2(*cidV2)
}
return cID
} }
// SetContainerID sets identifier of the related container. // SetContainerID sets identifier of the related container.
func (o *Object) SetContainerID(v *cid.ID) { func (o *Object) SetContainerID(v cid.ID) {
var cidV2 refs.ContainerID
v.WriteToV2(&cidV2)
o.setHeaderField(func(h *object.Header) { o.setHeaderField(func(h *object.Header) {
h.SetContainerID(v.ToV2()) h.SetContainerID(&cidV2)
}) })
} }
@ -290,33 +304,41 @@ func (o *Object) SetAttributes(v ...Attribute) {
} }
// PreviousID returns identifier of the previous sibling object. // PreviousID returns identifier of the previous sibling object.
func (o *Object) PreviousID() *oid.ID { func (o *Object) PreviousID() oid.ID {
return oid.NewIDFromV2( var v oid.ID
(*object.Object)(o). v2 := (*object.Object)(o)
GetHeader().
GetSplit(). v2Prev := v2.GetHeader().GetSplit().GetPrevious()
GetPrevious(), if v2Prev != nil {
) _ = v.ReadFromV2(*v2Prev)
}
return v
} }
// SetPreviousID sets identifier of the previous sibling object. // SetPreviousID sets identifier of the previous sibling object.
func (o *Object) SetPreviousID(v *oid.ID) { func (o *Object) SetPreviousID(v oid.ID) {
var v2 refs.ObjectID
v.WriteToV2(&v2)
o.setSplitFields(func(split *object.SplitHeader) { o.setSplitFields(func(split *object.SplitHeader) {
split.SetPrevious(v.ToV2()) split.SetPrevious(&v2)
}) })
} }
// Children return list of the identifiers of the child objects. // Children return list of the identifiers of the child objects.
func (o *Object) Children() []oid.ID { func (o *Object) Children() []oid.ID {
ids := (*object.Object)(o). v2 := (*object.Object)(o)
GetHeader(). ids := v2.GetHeader().GetSplit().GetChildren()
GetSplit().
GetChildren()
res := make([]oid.ID, len(ids)) var (
id oid.ID
res = make([]oid.ID, len(ids))
)
for i := range ids { for i := range ids {
res[i] = *oid.NewIDFromV2(&ids[i]) _ = id.ReadFromV2(ids[i])
res[i] = id
} }
return res return res
@ -324,10 +346,14 @@ func (o *Object) Children() []oid.ID {
// SetChildren sets list of the identifiers of the child objects. // SetChildren sets list of the identifiers of the child objects.
func (o *Object) SetChildren(v ...oid.ID) { func (o *Object) SetChildren(v ...oid.ID) {
ids := make([]refs.ObjectID, len(v)) var (
v2 refs.ObjectID
ids = make([]refs.ObjectID, len(v))
)
for i := range v { for i := range v {
ids[i] = *v[i].ToV2() v[i].WriteToV2(&v2)
ids[i] = v2
} }
o.setSplitFields(func(split *object.SplitHeader) { o.setSplitFields(func(split *object.SplitHeader) {
@ -406,19 +432,25 @@ func (o *Object) SetSplitID(id *SplitID) {
} }
// ParentID returns identifier of the parent object. // ParentID returns identifier of the parent object.
func (o *Object) ParentID() *oid.ID { func (o *Object) ParentID() oid.ID {
return oid.NewIDFromV2( var v oid.ID
(*object.Object)(o). v2 := (*object.Object)(o)
GetHeader().
GetSplit(). v2Par := v2.GetHeader().GetSplit().GetParent()
GetParent(), if v2Par != nil {
) _ = v.ReadFromV2(*v2Par)
}
return v
} }
// SetParentID sets identifier of the parent object. // SetParentID sets identifier of the parent object.
func (o *Object) SetParentID(v *oid.ID) { func (o *Object) SetParentID(v oid.ID) {
var v2 refs.ObjectID
v.WriteToV2(&v2)
o.setSplitFields(func(split *object.SplitHeader) { o.setSplitFields(func(split *object.SplitHeader) {
split.SetParent(v.ToV2()) split.SetParent(&v2)
}) })
} }

View file

@ -11,7 +11,7 @@ import (
func TestInitCreation(t *testing.T) { func TestInitCreation(t *testing.T) {
var o object.Object var o object.Object
cnr := *cidtest.ID() cnr := cidtest.ID()
own := *ownertest.ID() own := *ownertest.ID()
object.InitCreation(&o, object.RequiredFields{ object.InitCreation(&o, object.RequiredFields{
@ -19,6 +19,6 @@ func TestInitCreation(t *testing.T) {
Owner: own, Owner: own,
}) })
require.Equal(t, &cnr, o.ContainerID()) require.Equal(t, cnr, o.ContainerID())
require.Equal(t, &own, o.OwnerID()) require.Equal(t, &own, o.OwnerID())
} }

View file

@ -16,8 +16,8 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func randID(t *testing.T) *oid.ID { func randID(t *testing.T) oid.ID {
id := oid.NewID() var id oid.ID
id.SetSHA256(randSHA256Checksum(t)) id.SetSHA256(randSHA256Checksum(t))
return id return id
@ -177,9 +177,9 @@ func TestObject_SetChildren(t *testing.T) {
id1 := randID(t) id1 := randID(t)
id2 := randID(t) id2 := randID(t)
obj.SetChildren(*id1, *id2) obj.SetChildren(id1, id2)
require.Equal(t, []oid.ID{*id1, *id2}, obj.Children()) require.Equal(t, []oid.ID{id1, id2}, obj.Children())
} }
func TestObject_SetSplitID(t *testing.T) { func TestObject_SetSplitID(t *testing.T) {
@ -280,7 +280,7 @@ func TestObject_ResetRelations(t *testing.T) {
obj.ResetRelations() obj.ResetRelations()
require.Nil(t, obj.PreviousID()) require.True(t, obj.PreviousID().Empty())
} }
func TestObject_HasParent(t *testing.T) { func TestObject_HasParent(t *testing.T) {

View file

@ -220,7 +220,7 @@ func (f *SearchFilters) AddObjectVersionFilter(op SearchMatchType, v *version.Ve
f.addReservedFilter(op, fKeyVersion, v) f.addReservedFilter(op, fKeyVersion, v)
} }
func (f *SearchFilters) AddObjectContainerIDFilter(m SearchMatchType, id *cid.ID) { func (f *SearchFilters) AddObjectContainerIDFilter(m SearchMatchType, id cid.ID) {
f.addReservedFilter(m, fKeyContainerID, id) f.addReservedFilter(m, fKeyContainerID, id)
} }
@ -261,12 +261,12 @@ func (f *SearchFilters) AddPhyFilter() {
} }
// AddParentIDFilter adds filter by parent identifier. // AddParentIDFilter adds filter by parent identifier.
func (f *SearchFilters) AddParentIDFilter(m SearchMatchType, id *oid.ID) { func (f *SearchFilters) AddParentIDFilter(m SearchMatchType, id oid.ID) {
f.addReservedFilter(m, fKeyParent, id) f.addReservedFilter(m, fKeyParent, id)
} }
// AddObjectIDFilter adds filter by object identifier. // AddObjectIDFilter adds filter by object identifier.
func (f *SearchFilters) AddObjectIDFilter(m SearchMatchType, id *oid.ID) { func (f *SearchFilters) AddObjectIDFilter(m SearchMatchType, id oid.ID) {
f.addReservedFilter(m, fKeyObjectID, id) f.addReservedFilter(m, fKeyObjectID, id)
} }

View file

@ -100,12 +100,12 @@ func TestSearchFilters_AddPhyFilter(t *testing.T) {
require.Equal(t, "", f.Value()) require.Equal(t, "", f.Value())
} }
func testOID() *oid.ID { func testOID() oid.ID {
cs := [sha256.Size]byte{} cs := [sha256.Size]byte{}
rand.Read(cs[:]) rand.Read(cs[:])
id := oid.NewID() var id oid.ID
id.SetSHA256(cs) id.SetSHA256(cs)
return id return id

View file

@ -2,6 +2,7 @@ package object
import ( import (
"github.com/nspcc-dev/neofs-api-go/v2/object" "github.com/nspcc-dev/neofs-api-go/v2/object"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
oid "github.com/nspcc-dev/neofs-sdk-go/object/id" oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
) )
@ -40,22 +41,42 @@ func (s *SplitInfo) SetSplitID(v *SplitID) {
(*object.SplitInfo)(s).SetSplitID(v.ToV2()) (*object.SplitInfo)(s).SetSplitID(v.ToV2())
} }
func (s *SplitInfo) LastPart() *oid.ID { func (s SplitInfo) LastPart() oid.ID {
return oid.NewIDFromV2( var id oid.ID
(*object.SplitInfo)(s).GetLastPart()) v2 := (object.SplitInfo)(s)
lpV2 := v2.GetLastPart()
if lpV2 != nil {
_ = id.ReadFromV2(*lpV2)
}
return id
} }
func (s *SplitInfo) SetLastPart(v *oid.ID) { func (s *SplitInfo) SetLastPart(v oid.ID) {
(*object.SplitInfo)(s).SetLastPart(v.ToV2()) var idV2 refs.ObjectID
v.WriteToV2(&idV2)
(*object.SplitInfo)(s).SetLastPart(&idV2)
} }
func (s *SplitInfo) Link() *oid.ID { func (s SplitInfo) Link() oid.ID {
return oid.NewIDFromV2( var id oid.ID
(*object.SplitInfo)(s).GetLink()) v2 := (object.SplitInfo)(s)
linkV2 := v2.GetLink()
if linkV2 != nil {
_ = id.ReadFromV2(*linkV2)
}
return id
} }
func (s *SplitInfo) SetLink(v *oid.ID) { func (s *SplitInfo) SetLink(v oid.ID) {
(*object.SplitInfo)(s).SetLink(v.ToV2()) var idV2 refs.ObjectID
v.WriteToV2(&idV2)
(*object.SplitInfo)(s).SetLink(&idV2)
} }
func (s *SplitInfo) Marshal() ([]byte, error) { func (s *SplitInfo) Marshal() ([]byte, error) {

View file

@ -45,11 +45,11 @@ func TestSplitInfo(t *testing.T) {
}) })
} }
func generateID() *oid.ID { func generateID() oid.ID {
var buf [32]byte var buf [32]byte
_, _ = rand.Read(buf[:]) _, _ = rand.Read(buf[:])
id := oid.NewID() var id oid.ID
id.SetSHA256(buf) id.SetSHA256(buf)
return id return id
@ -77,8 +77,8 @@ func TestNewSplitInfo(t *testing.T) {
// check initial values // check initial values
require.Nil(t, si.SplitID()) require.Nil(t, si.SplitID())
require.Nil(t, si.LastPart()) require.True(t, si.LastPart().Empty())
require.Nil(t, si.Link()) require.True(t, si.Link().Empty())
// convert to v2 message // convert to v2 message
siV2 := si.ToV2() siV2 := si.ToV2()

View file

@ -57,7 +57,7 @@ func generate(withParent bool) *object.Object {
x.SetCreationEpoch(222) x.SetCreationEpoch(222)
x.SetPreviousID(oidtest.ID()) x.SetPreviousID(oidtest.ID())
x.SetParentID(oidtest.ID()) x.SetParentID(oidtest.ID())
x.SetChildren(*oidtest.ID(), *oidtest.ID()) x.SetChildren(oidtest.ID(), oidtest.ID())
x.SetAttributes(*Attribute(), *Attribute()) x.SetAttributes(*Attribute(), *Attribute())
x.SetSplitID(SplitID()) x.SetSplitID(SplitID())
x.SetPayloadChecksum(checksumtest.Checksum()) x.SetPayloadChecksum(checksumtest.Checksum())
@ -88,7 +88,7 @@ func Tombstone() *object.Tombstone {
x.SetSplitID(SplitID()) x.SetSplitID(SplitID())
x.SetExpirationEpoch(13) x.SetExpirationEpoch(13)
x.SetMembers([]oid.ID{*oidtest.ID(), *oidtest.ID()}) x.SetMembers([]oid.ID{oidtest.ID(), oidtest.ID()})
return x return x
} }
@ -117,7 +117,7 @@ func SearchFilters() object.SearchFilters {
// Lock returns random object.Lock. // Lock returns random object.Lock.
func Lock() *object.Lock { func Lock() *object.Lock {
var l object.Lock var l object.Lock
l.WriteMembers([]oid.ID{*oidtest.ID(), *oidtest.ID()}) l.WriteMembers([]oid.ID{oidtest.ID(), oidtest.ID()})
return &l return &l
} }

View file

@ -56,17 +56,21 @@ func (t *Tombstone) SetSplitID(v *SplitID) {
// Members returns list of objects to be deleted. // Members returns list of objects to be deleted.
func (t *Tombstone) Members() []oid.ID { func (t *Tombstone) Members() []oid.ID {
msV2 := (*tombstone.Tombstone)(t). v2 := (*tombstone.Tombstone)(t)
GetMembers() msV2 := v2.GetMembers()
if msV2 == nil { if msV2 == nil {
return nil return nil
} }
ms := make([]oid.ID, len(msV2)) var (
ms = make([]oid.ID, len(msV2))
id oid.ID
)
for i := range msV2 { for i := range msV2 {
ms[i] = *oid.NewIDFromV2(&msV2[i]) _ = id.ReadFromV2(msV2[i])
ms[i] = id
} }
return ms return ms
@ -86,8 +90,11 @@ func (t *Tombstone) SetMembers(v []oid.ID) {
ms = make([]refs.ObjectID, 0, ln) ms = make([]refs.ObjectID, 0, ln)
} }
var idV2 refs.ObjectID
for i := range v { for i := range v {
ms = append(ms, *v[i].ToV2()) v[i].WriteToV2(&idV2)
ms = append(ms, idV2)
} }
} }

View file

@ -15,7 +15,9 @@ func generateIDList(sz int) []oid.ID {
cs := [sha256.Size]byte{} cs := [sha256.Size]byte{}
for i := 0; i < sz; i++ { for i := 0; i < sz; i++ {
res[i] = *oid.NewID() var oID oid.ID
res[i] = oID
rand.Read(cs[:]) rand.Read(cs[:])
res[i].SetSHA256(cs) res[i].SetSHA256(cs)
} }

View file

@ -1,6 +1,7 @@
package session package session
import ( import (
"github.com/nspcc-dev/neofs-api-go/v2/refs"
"github.com/nspcc-dev/neofs-api-go/v2/session" "github.com/nspcc-dev/neofs-api-go/v2/session"
cid "github.com/nspcc-dev/neofs-sdk-go/container/id" cid "github.com/nspcc-dev/neofs-sdk-go/container/id"
) )
@ -43,8 +44,17 @@ func (x *ContainerContext) ToV2() *session.ContainerSessionContext {
func (x *ContainerContext) ApplyTo(id *cid.ID) { func (x *ContainerContext) ApplyTo(id *cid.ID) {
v2 := (*session.ContainerSessionContext)(x) v2 := (*session.ContainerSessionContext)(x)
var cidV2 *refs.ContainerID
if id != nil {
var c refs.ContainerID
id.WriteToV2(&c)
cidV2 = &c
}
v2.SetWildcard(id == nil) v2.SetWildcard(id == nil)
v2.SetContainerID(id.ToV2()) v2.SetContainerID(cidV2)
} }
// ApplyToAllContainers is a helper function that conveniently // ApplyToAllContainers is a helper function that conveniently
@ -65,7 +75,15 @@ func (x *ContainerContext) Container() *cid.ID {
return nil return nil
} }
return cid.NewFromV2(v2.ContainerID()) cidV2 := v2.ContainerID()
if cidV2 == nil {
return nil
}
var cID cid.ID
_ = cID.ReadFromV2(*cidV2)
return &cID
} }
func (x *ContainerContext) forVerb(v session.ContainerSessionVerb) { func (x *ContainerContext) forVerb(v session.ContainerSessionVerb) {

View file

@ -39,9 +39,9 @@ func TestContainerContext_ApplyTo(t *testing.T) {
id := cidtest.ID() id := cidtest.ID()
t.Run("method", func(t *testing.T) { t.Run("method", func(t *testing.T) {
c.ApplyTo(id) c.ApplyTo(&id)
require.Equal(t, id, c.Container()) require.Equal(t, id, *c.Container())
c.ApplyTo(nil) c.ApplyTo(nil)
@ -49,7 +49,7 @@ func TestContainerContext_ApplyTo(t *testing.T) {
}) })
t.Run("helper functions", func(t *testing.T) { t.Run("helper functions", func(t *testing.T) {
c.ApplyTo(id) c.ApplyTo(&id)
session.ApplyToAllContainers(c) session.ApplyToAllContainers(c)

View file

@ -20,7 +20,8 @@ func ContainerContext() *session.ContainerContext {
setters[rand.Uint32()%uint32(len(setters))]() setters[rand.Uint32()%uint32(len(setters))]()
c.ApplyTo(cidtest.ID()) cID := cidtest.ID()
c.ApplyTo(&cID)
return c return c
} }

View file

@ -93,7 +93,7 @@ func (sg *StorageGroup) Members() []oid.ID {
m := make([]oid.ID, len(mV2)) m := make([]oid.ID, len(mV2))
for i := range mV2 { for i := range mV2 {
m[i] = *oid.NewIDFromV2(&mV2[i]) _ = m[i].ReadFromV2(mV2[i])
} }
return m return m
@ -115,8 +115,11 @@ func (sg *StorageGroup) SetMembers(members []oid.ID) {
mV2 = make([]refs.ObjectID, ln) mV2 = make([]refs.ObjectID, ln)
} }
var oidV2 refs.ObjectID
for i := 0; i < ln; i++ { for i := 0; i < ln; i++ {
mV2[i] = *members[i].ToV2() members[i].WriteToV2(&oidV2)
mV2[i] = oidV2
} }
} }

View file

@ -30,7 +30,7 @@ func TestStorageGroup(t *testing.T) {
sg.SetExpirationEpoch(exp) sg.SetExpirationEpoch(exp)
require.Equal(t, exp, sg.ExpirationEpoch()) require.Equal(t, exp, sg.ExpirationEpoch())
members := []oid.ID{*oidtest.ID(), *oidtest.ID()} members := []oid.ID{oidtest.ID(), oidtest.ID()}
sg.SetMembers(members) sg.SetMembers(members)
require.Equal(t, members, sg.Members()) require.Equal(t, members, sg.Members())
} }

View file

@ -14,7 +14,7 @@ func StorageGroup() *storagegroup.StorageGroup {
x.SetExpirationEpoch(66) x.SetExpirationEpoch(66)
x.SetValidationDataSize(322) x.SetValidationDataSize(322)
x.SetValidationDataHash(checksumtest.Checksum()) x.SetValidationDataHash(checksumtest.Checksum())
x.SetMembers([]oid.ID{*oidtest.ID(), *oidtest.ID()}) x.SetMembers([]oid.ID{oidtest.ID(), oidtest.ID()})
return x return x
} }