diff --git a/audit/result.go b/audit/result.go index 744ed7a4..c89dbfd3 100644 --- a/audit/result.go +++ b/audit/result.go @@ -75,20 +75,29 @@ func (r *Result) ForEpoch(epoch uint64) { // Container returns identifier of the container with which the data audit Result // is associated. // -// Returns nil if container is not specified. Zero Result has nil container. -// Return value MUST NOT be mutated: to do this, first make a copy. +// Returns zero ID if container is not specified. Zero Result has zero container. // // See also ForContainer. -func (r Result) Container() *cid.ID { - return cid.NewFromV2(r.v2.GetContainerID()) +func (r Result) Container() cid.ID { + 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. // // See also Container. 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 @@ -174,8 +183,12 @@ func (r *Result) SetRetriesPoR(v uint32) { func (r Result) IteratePassedStorageGroups(f func(oid.ID) bool) { r2 := r.v2.GetPassSG() + var id oid.ID + for i := range r2 { - if !f(*oid.NewIDFromV2(&r2[i])) { + _ = id.ReadFromV2(r2[i]) + + if !f(id) { return } } @@ -186,7 +199,10 @@ func (r Result) IteratePassedStorageGroups(f func(oid.ID) bool) { // // See also IteratePassedStorageGroups. 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. @@ -194,9 +210,11 @@ func (r *Result) SubmitPassedStorageGroup(sg oid.ID) { // See also SubmitFailedStorageGroup. func (r Result) IterateFailedStorageGroups(f func(oid.ID) bool) { v := r.v2.GetFailSG() + var id oid.ID for i := range v { - if !f(*oid.NewIDFromV2(&v[i])) { + _ = id.ReadFromV2(v[i]) + if !f(id) { return } } @@ -206,7 +224,10 @@ func (r Result) IterateFailedStorageGroups(f func(oid.ID) bool) { // // See also IterateFailedStorageGroups. 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 diff --git a/audit/result_test.go b/audit/result_test.go index 736905cf..e41cb403 100644 --- a/audit/result_test.go +++ b/audit/result_test.go @@ -66,7 +66,7 @@ func TestResultData(t *testing.T) { countFailNodes := func(f func([]byte)) int { return countNodes(false, f) } require.Zero(t, r.Epoch()) - require.Nil(t, r.Container()) + require.True(t, r.Container().Empty()) require.Nil(t, r.AuditorKey()) require.False(t, r.Completed()) require.Zero(t, r.RequestsPoR()) @@ -81,7 +81,7 @@ func TestResultData(t *testing.T) { require.Equal(t, epoch, r.Epoch()) cnr := cidtest.ID() - r.ForContainer(*cnr) + r.ForContainer(cnr) require.Equal(t, cnr, r.Container()) key := []byte{1, 2, 3} @@ -99,32 +99,32 @@ func TestResultData(t *testing.T) { r.SetRetriesPoR(retries) require.Equal(t, retries, r.RetriesPoR()) - passSG1, passSG2 := *oidtest.ID(), *oidtest.ID() + passSG1, passSG2 := oidtest.ID(), oidtest.ID() r.SubmitPassedStorageGroup(passSG1) r.SubmitPassedStorageGroup(passSG2) called1, called2 := false, false require.EqualValues(t, 2, countPassSG(func(id oid.ID) { - if id.Equal(&passSG1) { + if id.Equals(passSG1) { called1 = true - } else if id.Equal(&passSG2) { + } else if id.Equals(passSG2) { called2 = true } })) require.True(t, called1) require.True(t, called2) - failSG1, failSG2 := *oidtest.ID(), *oidtest.ID() + failSG1, failSG2 := oidtest.ID(), oidtest.ID() r.SubmitFailedStorageGroup(failSG1) r.SubmitFailedStorageGroup(failSG2) called1, called2 = false, false require.EqualValues(t, 2, countFailSG(func(id oid.ID) { - if id.Equal(&failSG1) { + if id.Equals(failSG1) { called1 = true - } else if id.Equal(&failSG2) { + } else if id.Equals(failSG2) { called2 = true } })) diff --git a/audit/test/generate.go b/audit/test/generate.go index 694da4d2..2e2b9565 100644 --- a/audit/test/generate.go +++ b/audit/test/generate.go @@ -10,7 +10,7 @@ import ( func Result() *audit.Result { var x audit.Result - x.ForContainer(*cidtest.ID()) + x.ForContainer(cidtest.ID()) x.SetAuditorKey([]byte("key")) x.Complete() x.ForEpoch(44) @@ -27,10 +27,10 @@ func Result() *audit.Result { []byte("node3"), []byte("node4"), }) - x.SubmitPassedStorageGroup(*oidtest.ID()) - x.SubmitPassedStorageGroup(*oidtest.ID()) - x.SubmitFailedStorageGroup(*oidtest.ID()) - x.SubmitFailedStorageGroup(*oidtest.ID()) + x.SubmitPassedStorageGroup(oidtest.ID()) + x.SubmitPassedStorageGroup(oidtest.ID()) + x.SubmitFailedStorageGroup(oidtest.ID()) + x.SubmitFailedStorageGroup(oidtest.ID()) return &x } diff --git a/client/container.go b/client/container.go index d86dd544..58428d15 100644 --- a/client/container.go +++ b/client/container.go @@ -4,6 +4,7 @@ import ( "context" 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" "github.com/nspcc-dev/neofs-api-go/v2/rpc/client" 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) { 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 @@ -187,9 +198,12 @@ func (c *Client) ContainerGet(ctx context.Context, prm PrmContainerGet) (*ResCon panic(panicMsgMissingContainer) } + var cidV2 refs.ContainerID + prm.id.WriteToV2(&cidV2) + // form request body reqBody := new(v2container.GetRequestBody) - reqBody.SetContainerID(prm.id.ToV2()) + reqBody.SetContainerID(&cidV2) // form request 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())) for i, cidV2 := range resp.GetBody().GetContainerIDs() { - ids[i] = *cid.NewFromV2(&cidV2) + _ = ids[i].ReadFromV2(cidV2) } res.setContainers(ids) @@ -400,9 +414,12 @@ func (c *Client) ContainerDelete(ctx context.Context, prm PrmContainerDelete) (* panic(panicMsgMissingContainer) } + var cidV2 refs.ContainerID + prm.id.WriteToV2(&cidV2) + // form request body reqBody := new(v2container.DeleteRequestBody) - reqBody.SetContainerID(prm.id.ToV2()) + reqBody.SetContainerID(&cidV2) signWrapper := delContainerSignWrapper{body: reqBody} @@ -504,9 +521,12 @@ func (c *Client) ContainerEACL(ctx context.Context, prm PrmContainerEACL) (*ResC panic(panicMsgMissingContainer) } + var cidV2 refs.ContainerID + prm.id.WriteToV2(&cidV2) + // form request body reqBody := new(v2container.GetExtendedACLRequestBody) - reqBody.SetContainerID(prm.id.ToV2()) + reqBody.SetContainerID(&cidV2) // form request var req v2container.GetExtendedACLRequest diff --git a/client/object_delete.go b/client/object_delete.go index 08779b17..4f2eb75f 100644 --- a/client/object_delete.go +++ b/client/object_delete.go @@ -52,13 +52,19 @@ func (x *PrmObjectDelete) WithBearerToken(t bearer.Token) { // FromContainer specifies NeoFS container of the object. // Required parameter. 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. // Required parameter. 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. @@ -91,7 +97,7 @@ type ResObjectDelete struct { // Returns false if ID is missing (not read). func (x ResObjectDelete) ReadTombstoneID(dst *oid.ID) bool { if x.idTomb != nil { - *dst = *oid.NewIDFromV2(x.idTomb) // need smth better + _ = dst.ReadFromV2(*x.idTomb) return true } diff --git a/client/object_get.go b/client/object_get.go index 2f1221fa..ac54ddd0 100644 --- a/client/object_get.go +++ b/client/object_get.go @@ -36,10 +36,10 @@ type prmObjectRead struct { bearer bearer.Token cnrSet bool - cnr cid.ID + cnrID cid.ID objSet bool - obj oid.ID + objID oid.ID } 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. // Required parameter. func (x *prmObjectRead) FromContainer(id cid.ID) { - x.cnr = id + x.cnrID = id x.cnrSet = true } // ByID specifies identifier of the requested object. // Required parameter. func (x *prmObjectRead) ByID(id oid.ID) { - x.obj = id + x.objID = id x.objSet = true } @@ -318,10 +318,17 @@ func (c *Client) ObjectGetInit(ctx context.Context, prm PrmObjectGet) (*ObjectRe panic("missing object") } - var addr v2refs.Address + var ( + addr v2refs.Address + oidV2 v2refs.ObjectID + cidV2 v2refs.ContainerID + ) - addr.SetContainerID(prm.cnr.ToV2()) - addr.SetObjectID(prm.obj.ToV2()) + prm.objID.WriteToV2(&oidV2) + prm.cnrID.WriteToV2(&cidV2) + + addr.SetContainerID(&cidV2) + addr.SetObjectID(&oidV2) // form request body var body v2object.GetRequestBody @@ -411,7 +418,7 @@ func (x *ResObjectHead) ReadHeader(dst *object.Object) bool { objv2.SetSignature(x.hdr.GetSignature()) obj := object.NewFromV2(&objv2) - obj.SetID(&x.idObj) + obj.SetID(x.idObj) *dst = *obj @@ -449,16 +456,23 @@ func (c *Client) ObjectHead(ctx context.Context, prm PrmObjectHead) (*ResObjectH panic("missing object") } - var addr v2refs.Address + var ( + addrV2 v2refs.Address + oidV2 v2refs.ObjectID + cidV2 v2refs.ContainerID + ) - addr.SetContainerID(prm.cnr.ToV2()) - addr.SetObjectID(prm.obj.ToV2()) + prm.objID.WriteToV2(&oidV2) + prm.cnrID.WriteToV2(&cidV2) + + addrV2.SetContainerID(&cidV2) + addrV2.SetObjectID(&oidV2) // form request body var body v2object.HeadRequestBody body.SetRaw(prm.raw) - body.SetAddress(&addr) + body.SetAddress(&addrV2) // form meta header var meta v2session.RequestMetaHeader @@ -478,7 +492,7 @@ func (c *Client) ObjectHead(ctx context.Context, prm PrmObjectHead) (*ResObjectH res ResObjectHead ) - res.idObj = prm.obj + res.idObj = prm.objID if prm.keySet { c.initCallContextWithoutKey(&cc) @@ -715,10 +729,17 @@ func (c *Client) ObjectRangeInit(ctx context.Context, prm PrmObjectRange) (*Obje panic("zero range length") } - var addr v2refs.Address + var ( + addrV2 v2refs.Address + oidV2 v2refs.ObjectID + cidV2 v2refs.ContainerID + ) - addr.SetContainerID(prm.cnr.ToV2()) - addr.SetObjectID(prm.obj.ToV2()) + prm.objID.WriteToV2(&oidV2) + prm.cnrID.WriteToV2(&cidV2) + + addrV2.SetContainerID(&cidV2) + addrV2.SetObjectID(&oidV2) var rng v2object.Range @@ -729,7 +750,7 @@ func (c *Client) ObjectRangeInit(ctx context.Context, prm PrmObjectRange) (*Obje var body v2object.GetRangeRequestBody body.SetRaw(prm.raw) - body.SetAddress(&addr) + body.SetAddress(&addrV2) body.SetRange(&rng) // form meta header diff --git a/client/object_hash.go b/client/object_hash.go index c4f86c97..29004d7c 100644 --- a/client/object_hash.go +++ b/client/object_hash.go @@ -55,13 +55,19 @@ func (x *PrmObjectHash) WithBearerToken(t bearer.Token) { // FromContainer specifies NeoFS container of the object. // Required parameter. 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. // Required parameter. 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. diff --git a/client/object_put.go b/client/object_put.go index 1c0e4ca9..69c9bf2a 100644 --- a/client/object_put.go +++ b/client/object_put.go @@ -37,7 +37,7 @@ func (x *ResObjectPut) ReadStoredObjectID(id *oid.ID) bool { return false } - *id = *oid.NewIDFromV2(idv2) // need smth better + _ = id.ReadFromV2(*idv2) return true } diff --git a/client/object_search.go b/client/object_search.go index a9ee9297..76adceab 100644 --- a/client/object_search.go +++ b/client/object_search.go @@ -34,7 +34,7 @@ type PrmObjectSearch struct { bearer bearer.Token cnrSet bool - cnr cid.ID + cnrID cid.ID filters object.SearchFilters } @@ -68,7 +68,7 @@ func (x *PrmObjectSearch) WithBearerToken(t bearer.Token) { // InContainer specifies the container in which to look for objects. // Required parameter. func (x *PrmObjectSearch) InContainer(id cid.ID) { - x.cnr = id + x.cnrID = id x.cnrSet = true } @@ -131,7 +131,7 @@ func (x *ObjectListReader) Read(buf []oid.ID) (int, bool) { } 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:] @@ -165,7 +165,7 @@ func (x *ObjectListReader) Read(buf []oid.ID) (int, bool) { } for i = 0; i < ln; i++ { - buf[read+i] = *oid.NewIDFromV2(&ids[i]) // need smth better + _ = buf[read+i].ReadFromV2(ids[i]) } read += ln @@ -244,10 +244,15 @@ func (c *Client) ObjectSearchInit(ctx context.Context, prm PrmObjectSearch) (*Ob } // form request body - var body v2object.SearchRequestBody + var ( + body v2object.SearchRequestBody + cidV2 v2refs.ContainerID + ) + + prm.cnrID.WriteToV2(&cidV2) body.SetVersion(1) - body.SetContainerID(prm.cnr.ToV2()) + body.SetContainerID(&cidV2) body.SetFilters(prm.filters.ToV2()) // form meta header diff --git a/client/object_search_test.go b/client/object_search_test.go index 7809729c..6fb885c3 100644 --- a/client/object_search_test.go +++ b/client/object_search_test.go @@ -17,7 +17,7 @@ import ( func TestObjectSearch(t *testing.T) { ids := make([]oid.ID, 20) for i := range ids { - ids[i] = *oidtest.ID() + ids[i] = oidtest.ID() } resp, setID := testListReaderResponse(t) @@ -88,7 +88,7 @@ func TestObjectSearch(t *testing.T) { func TestObjectIterate(t *testing.T) { ids := make([]oid.ID, 3) for i := range ids { - ids[i] = *oidtest.ID() + ids[i] = oidtest.ID() } 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)) v2id := make([]refs.ObjectID, len(id)) + var oidV2 refs.ObjectID + for i := range id { - v2id[i] = *id[i].ToV2() + id[i].WriteToV2(&oidV2) + v2id[i] = oidV2 } resp.GetBody().SetIDList(v2id) err := signatureV2.SignServiceMessage(&p.PrivateKey, resp) diff --git a/container/announcement.go b/container/announcement.go index 1e41cf84..4fc35388 100644 --- a/container/announcement.go +++ b/container/announcement.go @@ -2,6 +2,7 @@ package container import ( "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" ) @@ -38,15 +39,25 @@ func (a *UsedSpaceAnnouncement) SetEpoch(epoch uint64) { } // ContainerID of the announcement. -func (a *UsedSpaceAnnouncement) ContainerID() *cid.ID { - return cid.NewFromV2( - (*container.UsedSpaceAnnouncement)(a).GetContainerID(), - ) +func (a *UsedSpaceAnnouncement) ContainerID() (cID cid.ID) { + v2 := (*container.UsedSpaceAnnouncement)(a) + + cidV2 := v2.GetContainerID() + if cidV2 == nil { + return + } + + _ = cID.ReadFromV2(*cidV2) + + return } // SetContainerID sets announcement container value. -func (a *UsedSpaceAnnouncement) SetContainerID(cid *cid.ID) { - (*container.UsedSpaceAnnouncement)(a).SetContainerID(cid.ToV2()) +func (a *UsedSpaceAnnouncement) SetContainerID(cnr cid.ID) { + var cidV2 refs.ContainerID + cnr.WriteToV2(&cidV2) + + (*container.UsedSpaceAnnouncement)(a).SetContainerID(&cidV2) } // UsedSpace in container. diff --git a/container/announcement_test.go b/container/announcement_test.go index dcc40981..afc3bd35 100644 --- a/container/announcement_test.go +++ b/container/announcement_test.go @@ -46,9 +46,12 @@ func TestAnnouncement(t *testing.T) { newA := container.NewAnnouncementFromV2(v2) + var cID cid.ID + _ = cID.ReadFromV2(*newCID) + require.Equal(t, newEpoch, newA.Epoch()) 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 require.Zero(t, announcement.Epoch()) require.Zero(t, announcement.UsedSpace()) - require.Nil(t, announcement.ContainerID()) + require.True(t, announcement.ContainerID().Empty()) // convert to v2 message announcementV2 := announcement.ToV2() diff --git a/container/container.go b/container/container.go index d2b9bb6c..47bed79f 100644 --- a/container/container.go +++ b/container/container.go @@ -87,13 +87,13 @@ func NewContainerFromV2(c *container.Container) *Container { // CalculateID calculates container identifier // based on its structure. -func CalculateID(c *Container) *cid.ID { +func CalculateID(c *Container) cid.ID { data, err := c.ToV2().StableMarshal(nil) if err != nil { panic(err) } - id := cid.New() + var id cid.ID id.SetSHA256(sha256.Sum256(data)) return id diff --git a/container/id/doc.go b/container/id/doc.go new file mode 100644 index 00000000..e0141b97 --- /dev/null +++ b/container/id/doc.go @@ -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 diff --git a/container/id/id.go b/container/id/id.go index 32f11998..e064cfcc 100644 --- a/container/id/id.go +++ b/container/id/id.go @@ -1,90 +1,120 @@ package cid import ( - "bytes" "crypto/sha256" - "errors" + "fmt" "github.com/mr-tron/base58" "github.com/nspcc-dev/neofs-api-go/v2/refs" ) -// ID represents v2-compatible container identifier. -type ID refs.ContainerID - -// NewFromV2 wraps v2 ContainerID message to ID. +// ID represents NeoFS container identifier. // -// Nil refs.ContainerID converts to nil. -func NewFromV2(idV2 *refs.ContainerID) *ID { - return (*ID)(idV2) -} - -// New creates and initializes blank ID. +// ID is mutually compatible with github.com/nspcc-dev/neofs-api-go/v2/refs.ContainerID +// message. See ReadFromV2 / WriteToV2 methods. // -// Defaults: -// - 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. +// Instances can be created using built-in var declaration. // -// Nil ID converts to nil. -func (id *ID) ToV2() *refs.ContainerID { - return (*refs.ContainerID)(id) -} +// Note that direct typecast is not safe and may result in loss of compatibility: +// _ = ID([32]byte) // not recommended +type ID [sha256.Size]byte -// Equal returns true if identifiers are identical. -func (id *ID) Equal(id2 *ID) bool { - return bytes.Equal( - (*refs.ContainerID)(id).GetValue(), - (*refs.ContainerID)(id2).GetValue(), - ) -} - -// Parse parses string representation of ID. +// ReadFromV2 reads ID from the refs.ContainerID message. +// Returns an error if the message is malformed according +// to the NeoFS API V2 protocol. // -// Returns error if s is not a base58 encoded -// ID data. -func (id *ID) Parse(s string) error { - data, err := base58.Decode(s) - if err != nil { - return err - } else if len(data) != sha256.Size { - return errors.New("incorrect format of the string container ID") +// See also WriteToV2. +func (id *ID) ReadFromV2(m refs.ContainerID) error { + return id.Decode(m.GetValue()) +} + +// WriteToV2 writes ID to the refs.ContainerID message. +// The message must not be nil. +// +// 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 } -// String returns base58 string representation of ID. -func (id *ID) String() string { - return base58.Encode((*refs.ContainerID)(id).GetValue()) +// SetSHA256 sets container identifier value to SHA256 checksum of container structure. +func (id *ID) SetSHA256(v [sha256.Size]byte) { + copy(id[:], v[:]) } -// Marshal marshals ID into a protobuf binary form. -func (id *ID) Marshal() ([]byte, error) { - return (*refs.ContainerID)(id).StableMarshal(nil) +// 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 } -// Unmarshal unmarshals protobuf binary representation of ID. -func (id *ID) Unmarshal(data []byte) error { - return (*refs.ContainerID)(id).Unmarshal(data) +// 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[:]) } -// MarshalJSON encodes ID to protobuf JSON format. -func (id *ID) MarshalJSON() ([]byte, error) { - return (*refs.ContainerID)(id).MarshalJSON() +// 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) } -// UnmarshalJSON decodes ID from protobuf JSON format. -func (id *ID) UnmarshalJSON(data []byte) error { - return (*refs.ContainerID)(id).UnmarshalJSON(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() +} + +// Empty returns true if it is called on +// zero container ID. +func (id ID) Empty() bool { + return id == ID{} } diff --git a/container/id/id_test.go b/container/id/id_test.go index 02d1d75f..8a08f6ba 100644 --- a/container/id/id_test.go +++ b/container/id/id_test.go @@ -5,6 +5,7 @@ import ( "math/rand" "testing" + "github.com/mr-tron/base58" "github.com/nspcc-dev/neofs-api-go/v2/refs" cid "github.com/nspcc-dev/neofs-sdk-go/container/id" cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test" @@ -16,30 +17,32 @@ func randSHA256Checksum() (cs [sha256.Size]byte) { return } +const emptyID = "11111111111111111111111111111111" + func TestID_ToV2(t *testing.T) { - t.Run("non-nil", func(t *testing.T) { + t.Run("non-zero", func(t *testing.T) { checksum := randSHA256Checksum() 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()) }) - t.Run("nil", func(t *testing.T) { - var x *cid.ID + t.Run("zero", func(t *testing.T) { + var ( + x cid.ID + v2 refs.ContainerID + ) - require.Nil(t, x.ToV2()) - }) - - t.Run("default values", func(t *testing.T) { - id := cid.New() - - // convert to v2 message - cidV2 := id.ToV2() - require.Nil(t, cidV2.GetValue()) + x.WriteToV2(&v2) + require.Equal(t, emptyID, base58.Encode(v2.GetValue())) }) } @@ -49,57 +52,57 @@ func TestID_Equal(t *testing.T) { id1 := cidtest.IDWithChecksum(cs) id2 := cidtest.IDWithChecksum(cs) - require.True(t, id1.Equal(id2)) + require.True(t, id1.Equals(id2)) id3 := cidtest.ID() - require.False(t, id1.Equal(id3)) + require.False(t, id1.Equals(id3)) } 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() - 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) }) - t.Run("nil", func(t *testing.T) { - id := cid.New() + t.Run("zero", func(t *testing.T) { + var id cid.ID - require.Empty(t, id.String()) - }) -} - -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) + require.Equal(t, emptyID, id.EncodeToString()) }) } func TestNewFromV2(t *testing.T) { - t.Run("from nil", func(t *testing.T) { - var x *refs.ContainerID + t.Run("from zero", func(t *testing.T) { + 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()) }) } diff --git a/container/id/test/doc.go b/container/id/test/doc.go new file mode 100644 index 00000000..f2d4d327 --- /dev/null +++ b/container/id/test/doc.go @@ -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 diff --git a/container/id/test/id.go b/container/id/test/id.go index cf26feaf..c8e7deb5 100644 --- a/container/id/test/id.go +++ b/container/id/test/id.go @@ -8,7 +8,7 @@ import ( ) // ID returns random cid.ID. -func ID() *cid.ID { +func ID() cid.ID { checksum := [sha256.Size]byte{} rand.Read(checksum[:]) @@ -18,8 +18,8 @@ func ID() *cid.ID { // IDWithChecksum returns cid.ID initialized // with specified checksum. -func IDWithChecksum(cs [sha256.Size]byte) *cid.ID { - id := cid.New() +func IDWithChecksum(cs [sha256.Size]byte) cid.ID { + var id cid.ID id.SetSHA256(cs) return id diff --git a/eacl/record.go b/eacl/record.go index 430ed22f..935cd014 100644 --- a/eacl/record.go +++ b/eacl/record.go @@ -112,12 +112,12 @@ func (r *Record) AddObjectVersionFilter(m Match, v *version.Version) { } // 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) } // 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) } diff --git a/eacl/table.go b/eacl/table.go index 75c07552..ba090f91 100644 --- a/eacl/table.go +++ b/eacl/table.go @@ -16,19 +16,19 @@ import ( // Table is compatible with v2 acl.EACLTable message. type Table struct { version version.Version - cid *cid.ID + cid cid.ID token *session.Token sig *signature.Signature records []Record } // 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 } // 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 } @@ -85,9 +85,11 @@ func (t *Table) ToV2() *v2acl.Table { } v2 := new(v2acl.Table) + var cidV2 refs.ContainerID - if t.cid != nil { - v2.SetContainerID(t.cid.ToV2()) + if !t.cid.Empty() { + t.cid.WriteToV2(&cidV2) + v2.SetContainerID(&cidV2) } if t.records != nil { @@ -124,7 +126,7 @@ func NewTable() *Table { // CreateTable creates, initializes with parameters and returns Table instance. func CreateTable(cid cid.ID) *Table { t := NewTable() - t.SetCID(&cid) + t.SetCID(cid) return t } @@ -148,8 +150,8 @@ func NewTableFromV2(table *v2acl.Table) *Table { // set container id if id := table.GetContainerID(); id != nil { - if t.cid == nil { - t.cid = new(cid.ID) + if t.cid.Empty() { + t.cid = cid.ID{} } var h [sha256.Size]byte @@ -205,7 +207,7 @@ func (t *Table) UnmarshalJSON(data []byte) error { // EqualTables compares Table with each other. func EqualTables(t1, t2 Table) bool { - if !t1.CID().Equal(t2.CID()) || + if !t1.CID().Equals(t2.CID()) || !t1.Version().Equal(t2.Version()) { return false } diff --git a/eacl/table_test.go b/eacl/table_test.go index a9737cb6..6681761c 100644 --- a/eacl/table_test.go +++ b/eacl/table_test.go @@ -45,7 +45,7 @@ func TestTable(t *testing.T) { t.Run("create table", func(t *testing.T) { id := cidtest.ID() - table := eacl.CreateTable(*id) + table := eacl.CreateTable(id) require.Equal(t, id, table.CID()) require.Equal(t, version.Current(), table.Version()) }) @@ -124,7 +124,7 @@ func TestTable_ToV2(t *testing.T) { // check initial values require.Equal(t, version.Current(), table.Version()) 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.Signature()) diff --git a/ns/dns.go b/ns/dns.go index 56a6f09a..065eef90 100644 --- a/ns/dns.go +++ b/ns/dns.go @@ -16,20 +16,20 @@ type DNS struct{} // Otherwise, returns an error. // // 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) if err != nil { - return nil, err + return } - var id cid.ID - for i := range records { - err = id.Parse(records[i]) + err = id.DecodeString(records[i]) if err == nil { - return &id, nil + return } } - return nil, errNotFound + err = errNotFound + + return } diff --git a/ns/nns.go b/ns/nns.go index 3e7846d1..0f178fa3 100644 --- a/ns/nns.go +++ b/ns/nns.go @@ -114,7 +114,7 @@ func (n *NNS) Dial(address string) error { // ResolveContainerName MUST NOT be called before successful Dial. // // 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{ { Type: smartcontract.StringType, @@ -126,25 +126,25 @@ func (n *NNS) ResolveContainerName(name string) (*cid.ID, error) { }, }) 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() { - 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 { - 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 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 { arr, ok := itemArr.Value().([]stackitem.Item) if !ok { // 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 @@ -152,15 +152,15 @@ func (n *NNS) ResolveContainerName(name string) (*cid.ID, error) { for i := range arr { bs, err := arr[i].TryBytes() 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 { - return &id, nil + return id, nil } } } - return nil, errNotFound + return cid.ID{}, errNotFound } diff --git a/object/address/address.go b/object/address/address.go index b7cb0ad4..cb74a2a8 100644 --- a/object/address/address.go +++ b/object/address/address.go @@ -45,41 +45,61 @@ func (a *Address) ToV2() *refs.Address { } // ContainerID returns container identifier. -func (a *Address) ContainerID() *cid.ID { - return cid.NewFromV2( - (*refs.Address)(a).GetContainerID()) +func (a *Address) ContainerID() (v cid.ID) { + var cID cid.ID + v2 := (*refs.Address)(a) + + cidV2 := v2.GetContainerID() + if cidV2 != nil { + _ = cID.ReadFromV2(*cidV2) + } + + return cID } // SetContainerID sets container identifier. -func (a *Address) SetContainerID(id *cid.ID) { - (*refs.Address)(a).SetContainerID(id.ToV2()) +func (a *Address) SetContainerID(id cid.ID) { + var cidV2 refs.ContainerID + id.WriteToV2(&cidV2) + + (*refs.Address)(a).SetContainerID(&cidV2) } // ObjectID returns object identifier. -func (a *Address) ObjectID() *oid.ID { - return oid.NewIDFromV2( - (*refs.Address)(a).GetObjectID()) +func (a *Address) ObjectID() (v oid.ID) { + var id oid.ID + v2 := (*refs.Address)(a) + + oidV2 := v2.GetObjectID() + if oidV2 != nil { + _ = id.ReadFromV2(*oidV2) + } + + return id } // SetObjectID sets object identifier. -func (a *Address) SetObjectID(id *oid.ID) { - (*refs.Address)(a).SetObjectID(id.ToV2()) +func (a *Address) SetObjectID(id oid.ID) { + var oidV2 refs.ObjectID + id.WriteToV2(&oidV2) + + (*refs.Address)(a).SetObjectID(&oidV2) } // Parse converts base58 string representation into Address. func (a *Address) Parse(s string) error { var ( err error - oid = oid.NewID() - id = cid.New() + oid oid.ID + id cid.ID parts = strings.Split(s, addressSeparator) ) if len(parts) != addressParts { return errInvalidAddressString - } else if err = id.Parse(parts[0]); err != nil { + } else if err = id.DecodeString(parts[0]); err != nil { return err - } else if err = oid.Parse(parts[1]); err != nil { + } else if err = oid.DecodeString(parts[1]); err != nil { return err } diff --git a/object/address/address_test.go b/object/address/address_test.go index 76c446e9..428a47a1 100644 --- a/object/address/address_test.go +++ b/object/address/address_test.go @@ -107,8 +107,8 @@ func TestNewAddress(t *testing.T) { a := NewAddress() // check initial values - require.Nil(t, a.ContainerID()) - require.Nil(t, a.ObjectID()) + require.True(t, a.ContainerID().Empty()) + require.True(t, a.ObjectID().Empty()) // convert to v2 message aV2 := a.ToV2() diff --git a/object/fmt.go b/object/fmt.go index 05f3ca9c..fe5040a6 100644 --- a/object/fmt.go +++ b/object/fmt.go @@ -7,10 +7,10 @@ import ( "errors" "fmt" + "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/checksum" 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" ) @@ -55,13 +55,13 @@ func VerifyPayloadChecksum(obj *Object) error { } // 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) if err != nil { - return nil, err + return oid.ID{}, err } - id := oid.NewID() + var id oid.ID id.SetSHA256(sha256.Sum256(data)) return id, nil @@ -88,43 +88,45 @@ func VerifyID(obj *Object) error { return err } - if !id.Equal(obj.ID()) { + if !id.Equals(obj.ID()) { return errIncorrectID } return nil } -func CalculateIDSignature(key *ecdsa.PrivateKey, id *oid.ID) (*signature.Signature, error) { - return sigutil.SignData( - key, - signatureV2.StableMarshalerWrapper{ - SM: id.ToV2(), - }) -} - -func CalculateAndSetSignature(key *ecdsa.PrivateKey, obj *Object) error { - sig, err := CalculateIDSignature(key, obj.ID()) +// CalculateAndSetSignature signs id with provided key and sets that signature to +// the object. +func CalculateAndSetSignature(key ecdsa.PrivateKey, obj *Object) error { + sig, err := obj.ID().CalculateIDSignature(key) if err != nil { return err } - obj.SetSignature(sig) + obj.SetSignature(&sig) return nil } -func VerifyIDSignature(obj *Object) error { - return sigutil.VerifyData( +// VerifyIDSignature verifies object ID signature. +func (o *Object) VerifyIDSignature() bool { + var idV2 refs.ObjectID + o.ID().WriteToV2(&idV2) + + sig := o.Signature() + + err := sigutil.VerifyData( signatureV2.StableMarshalerWrapper{ - SM: obj.ID().ToV2(), + SM: &idV2, }, - obj.Signature(), + sig, ) + + return err == nil } // 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 { 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. -func SetVerificationFields(key *ecdsa.PrivateKey, obj *Object) error { +func SetVerificationFields(key ecdsa.PrivateKey, obj *Object) error { CalculateAndSetPayloadChecksum(obj) return SetIDWithSignature(key, obj) @@ -156,10 +158,12 @@ func CheckVerificationFields(obj *Object) error { return nil } +var errInvalidSignature = errors.New("invalid signature") + // CheckHeaderVerificationFields checks all verification fields except payload. func CheckHeaderVerificationFields(obj *Object) error { - if err := VerifyIDSignature(obj); err != nil { - return fmt.Errorf("invalid signature: %w", err) + if !obj.VerifyIDSignature() { + return errInvalidSignature } if err := VerifyID(obj); err != nil { diff --git a/object/fmt_test.go b/object/fmt_test.go index 500d74aa..aa22e755 100644 --- a/object/fmt_test.go +++ b/object/fmt_test.go @@ -19,7 +19,7 @@ func TestVerificationFields(t *testing.T) { p, err := keys.NewPrivateKey() require.NoError(t, err) - require.NoError(t, SetVerificationFields(&p.PrivateKey, obj)) + require.NoError(t, SetVerificationFields(p.PrivateKey, obj)) require.NoError(t, CheckVerificationFields(obj)) @@ -45,10 +45,10 @@ func TestVerificationFields(t *testing.T) { }, { corrupt: func() { - obj.ID().ToV2().GetValue()[0]++ + obj.ToV2().GetObjectID().GetValue()[0]++ }, restore: func() { - obj.ID().ToV2().GetValue()[0]-- + obj.ToV2().GetObjectID().GetValue()[0]-- }, }, { diff --git a/object/id/doc.go b/object/id/doc.go new file mode 100644 index 00000000..89f15858 --- /dev/null +++ b/object/id/doc.go @@ -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 diff --git a/object/id/id.go b/object/id/id.go index 4c3d0e47..c0426c0a 100644 --- a/object/id/id.go +++ b/object/id/id.go @@ -1,92 +1,177 @@ package oid import ( - "bytes" + "crypto/ecdsa" "crypto/sha256" - "errors" "fmt" "github.com/mr-tron/base58" "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. -type ID refs.ObjectID - -var errInvalidIDString = errors.New("incorrect format of the string object ID") - -// NewIDFromV2 wraps v2 ObjectID message to ID. +// ID represents NeoFS object identifier in a container. // -// Nil refs.ObjectID converts to nil. -func NewIDFromV2(idV2 *refs.ObjectID) *ID { - return (*ID)(idV2) -} - -// NewID creates and initializes blank ID. +// ID is mutually compatible with github.com/nspcc-dev/neofs-api-go/v2/refs.ObjectID +// message. See ReadFromV2 / WriteToV2 methods. // -// Works similar as NewIDFromV2(new(ObjectID)). +// Instances can be created using built-in var declaration. // -// Defaults: -// - value: nil. -func NewID() *ID { - return NewIDFromV2(new(refs.ObjectID)) -} +// Note that direct typecast is not safe and may result in loss of compatibility: +// _ = ObjectID([32]byte{}) // not recommended +type ID [sha256.Size]byte -// SetSHA256 sets object identifier value to SHA256 checksum. -func (id *ID) SetSHA256(v [sha256.Size]byte) { - (*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. +// ReadFromV2 reads ID from the refs.ObjectID message. Returns an error if +// the message is malformed according to the NeoFS API V2 protocol. // -// Nil ID converts to nil. -func (id *ID) ToV2() *refs.ObjectID { - return (*refs.ObjectID)(id) +// See also WriteToV2. +func (id *ID) ReadFromV2(m refs.ObjectID) error { + return id.Decode(m.GetValue()) } -// 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 object.ID from string: %w", err) - } else if len(data) != sha256.Size { - return errInvalidIDString +// WriteToV2 writes ID to the refs.ObjectID message. +// The message must not be nil. +// +// See also ReadFromV2. +func (id ID) WriteToV2(m *refs.ObjectID) { + 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.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 } -// String returns base58 string representation of ID. -func (id *ID) String() string { - return base58.Encode((*refs.ObjectID)(id).GetValue()) +// SetSHA256 sets object identifier value to SHA256 checksum. +func (id *ID) SetSHA256(v [sha256.Size]byte) { + 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. -func (id *ID) Marshal() ([]byte, error) { - return (*refs.ObjectID)(id).StableMarshal(nil) +func (id ID) Marshal() ([]byte, error) { + var v2 refs.ObjectID + v2.SetValue(id[:]) + + return v2.StableMarshal(nil) } // Unmarshal unmarshals protobuf binary representation of ID. 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. -func (id *ID) MarshalJSON() ([]byte, error) { - return (*refs.ObjectID)(id).MarshalJSON() +func (id ID) MarshalJSON() ([]byte, error) { + var v2 refs.ObjectID + v2.SetValue(id[:]) + + return v2.MarshalJSON() } // UnmarshalJSON decodes ID from protobuf JSON format. 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 } diff --git a/object/id/id_test.go b/object/id/id_test.go index 336ba734..aebf159e 100644 --- a/object/id/id_test.go +++ b/object/id/id_test.go @@ -11,8 +11,10 @@ import ( "github.com/stretchr/testify/require" ) -func randID(t *testing.T) *ID { - id := NewID() +const emptyID = "11111111111111111111111111111111" + +func randID(t *testing.T) ID { + var id ID id.SetSHA256(randSHA256Checksum(t)) return id @@ -26,7 +28,7 @@ func randSHA256Checksum(t *testing.T) (cs [sha256.Size]byte) { } func TestIDV2(t *testing.T) { - id := NewID() + var id ID checksum := [sha256.Size]byte{} @@ -35,7 +37,8 @@ func TestIDV2(t *testing.T) { id.SetSHA256(checksum) - idV2 := id.ToV2() + var idV2 refs.ObjectID + id.WriteToV2(&idV2) require.Equal(t, checksum[:], idV2.GetValue()) } @@ -43,17 +46,17 @@ func TestIDV2(t *testing.T) { func TestID_Equal(t *testing.T) { cs := randSHA256Checksum(t) - id1 := NewID() + var id1 ID id1.SetSHA256(cs) - id2 := NewID() + var id2 ID id2.SetSHA256(cs) - id3 := NewID() + var id3 ID id3.SetSHA256(randSHA256Checksum(t)) - require.True(t, id1.Equal(id2)) - require.False(t, id1.Equal(id3)) + require.True(t, id1.Equals(id2)) + require.False(t, id1.Equals(id3)) } 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) { cs := randSHA256Checksum(t) str := base58.Encode(cs[:]) - oid := NewID() + var oid ID - require.NoError(t, oid.Parse(str)) - require.Equal(t, cs[:], oid.ToV2().GetValue()) + require.NoError(t, oid.DecodeString(str)) + + 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) { cs := []byte{1, 2, 3, 4, 5, byte(j)} 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) { - t.Run("nil", func(t *testing.T) { - id := NewID() - require.Empty(t, id.String()) + t.Run("zero", func(t *testing.T) { + var id ID + require.Equal(t, emptyID, id.EncodeToString()) }) 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) { cs := randSHA256Checksum(t) 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()) }) } @@ -111,7 +118,7 @@ func TestObjectIDEncoding(t *testing.T) { data, err := id.Marshal() require.NoError(t, err) - id2 := NewID() + var id2 ID require.NoError(t, id2.Unmarshal(data)) require.Equal(t, id, id2) @@ -121,36 +128,55 @@ func TestObjectIDEncoding(t *testing.T) { data, err := id.MarshalJSON() require.NoError(t, err) - a2 := NewID() - require.NoError(t, a2.UnmarshalJSON(data)) + var id2 ID + require.NoError(t, id2.UnmarshalJSON(data)) - require.Equal(t, id, a2) + require.Equal(t, id, id2) }) } func TestNewIDFromV2(t *testing.T) { - t.Run("from nil", func(t *testing.T) { - var x *refs.ObjectID + t.Run("from zero", func(t *testing.T) { + var ( + x ID + v2 refs.ObjectID + ) - require.Nil(t, NewIDFromV2(x)) + require.Error(t, x.ReadFromV2(v2)) }) } func TestID_ToV2(t *testing.T) { - t.Run("nil", func(t *testing.T) { - var x *ID + t.Run("zero to v2", func(t *testing.T) { + 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) { - t.Run("default values", func(t *testing.T) { - id := NewID() +func TestID_Encode(t *testing.T) { + var id ID - // convert to v2 message - idV2 := id.ToV2() + t.Run("panic", func(t *testing.T) { + 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()) }) } diff --git a/object/id/test/doc.go b/object/id/test/doc.go new file mode 100644 index 00000000..22d5e2e9 --- /dev/null +++ b/object/id/test/doc.go @@ -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 diff --git a/object/id/test/generate.go b/object/id/test/generate.go index 7bd5bbe4..8bb80328 100644 --- a/object/id/test/generate.go +++ b/object/id/test/generate.go @@ -1,4 +1,4 @@ -package test +package oidtest import ( "crypto/sha256" @@ -8,7 +8,7 @@ import ( ) // ID returns random object.ID. -func ID() *oid.ID { +func ID() oid.ID { checksum := [sha256.Size]byte{} rand.Read(checksum[:]) @@ -18,8 +18,8 @@ func ID() *oid.ID { // idWithChecksum returns object.ID initialized // with specified checksum. -func idWithChecksum(cs [sha256.Size]byte) *oid.ID { - id := oid.NewID() +func idWithChecksum(cs [sha256.Size]byte) oid.ID { + var id oid.ID id.SetSHA256(cs) return id diff --git a/object/lock.go b/object/lock.go index 952822bb..4daff7ee 100644 --- a/object/lock.go +++ b/object/lock.go @@ -41,8 +41,8 @@ func (x Lock) NumberOfMembers() int { func (x Lock) ReadMembers(buf []oid.ID) { var i int - (*v2object.Lock)(&x).IterateMembers(func(id refs.ObjectID) { - buf[i] = *oid.NewIDFromV2(&id) // need smth better + (*v2object.Lock)(&x).IterateMembers(func(idV2 refs.ObjectID) { + _ = buf[i].ReadFromV2(idV2) i++ }) } @@ -55,7 +55,7 @@ func (x *Lock) WriteMembers(ids []oid.ID) { members = make([]refs.ObjectID, len(ids)) for i := range ids { - members[i] = *ids[i].ToV2() // need smth better + ids[i].WriteToV2(&members[i]) } } diff --git a/object/object.go b/object/object.go index 9c509d49..c71239c1 100644 --- a/object/object.go +++ b/object/object.go @@ -34,7 +34,7 @@ type RequiredFields struct { // 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. func InitCreation(dst *Object, rf RequiredFields) { - dst.SetContainerID(&rf.Container) + dst.SetContainerID(rf.Container) dst.SetOwnerID(&rf.Owner) } @@ -86,17 +86,24 @@ func (o *Object) setSplitFields(setter func(*object.SplitHeader)) { } // ID returns object identifier. -func (o *Object) ID() *oid.ID { - return oid.NewIDFromV2( - (*object.Object)(o). - GetObjectID(), - ) +func (o *Object) ID() oid.ID { + var v oid.ID + + v2 := (*object.Object)(o) + if id := v2.GetObjectID(); id != nil { + _ = v.ReadFromV2(*v2.GetObjectID()) + } + + return v } // 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). - SetObjectID(v.ToV2()) + SetObjectID(&v2) } // Signature returns signature of the object identifier. @@ -154,18 +161,25 @@ func (o *Object) SetPayloadSize(v uint64) { } // ContainerID returns identifier of the related container. -func (o *Object) ContainerID() *cid.ID { - return cid.NewFromV2( - (*object.Object)(o). - GetHeader(). - GetContainerID(), - ) +func (o *Object) ContainerID() cid.ID { + var cID cid.ID + v2 := (*object.Object)(o) + + cidV2 := v2.GetHeader().GetContainerID() + if cidV2 != nil { + _ = cID.ReadFromV2(*cidV2) + } + + return cID } // 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) { - 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. -func (o *Object) PreviousID() *oid.ID { - return oid.NewIDFromV2( - (*object.Object)(o). - GetHeader(). - GetSplit(). - GetPrevious(), - ) +func (o *Object) PreviousID() oid.ID { + var v oid.ID + v2 := (*object.Object)(o) + + v2Prev := v2.GetHeader().GetSplit().GetPrevious() + if v2Prev != nil { + _ = v.ReadFromV2(*v2Prev) + } + + return v } // 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) { - split.SetPrevious(v.ToV2()) + split.SetPrevious(&v2) }) } // Children return list of the identifiers of the child objects. func (o *Object) Children() []oid.ID { - ids := (*object.Object)(o). - GetHeader(). - GetSplit(). - GetChildren() + v2 := (*object.Object)(o) + ids := v2.GetHeader().GetSplit().GetChildren() - res := make([]oid.ID, len(ids)) + var ( + id oid.ID + res = make([]oid.ID, len(ids)) + ) for i := range ids { - res[i] = *oid.NewIDFromV2(&ids[i]) + _ = id.ReadFromV2(ids[i]) + res[i] = id } return res @@ -324,10 +346,14 @@ func (o *Object) Children() []oid.ID { // SetChildren sets list of the identifiers of the child objects. 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 { - ids[i] = *v[i].ToV2() + v[i].WriteToV2(&v2) + ids[i] = v2 } o.setSplitFields(func(split *object.SplitHeader) { @@ -406,19 +432,25 @@ func (o *Object) SetSplitID(id *SplitID) { } // ParentID returns identifier of the parent object. -func (o *Object) ParentID() *oid.ID { - return oid.NewIDFromV2( - (*object.Object)(o). - GetHeader(). - GetSplit(). - GetParent(), - ) +func (o *Object) ParentID() oid.ID { + var v oid.ID + v2 := (*object.Object)(o) + + v2Par := v2.GetHeader().GetSplit().GetParent() + if v2Par != nil { + _ = v.ReadFromV2(*v2Par) + } + + return v } // 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) { - split.SetParent(v.ToV2()) + split.SetParent(&v2) }) } diff --git a/object/object_test.go b/object/object_test.go index 286ca4ba..e489154b 100644 --- a/object/object_test.go +++ b/object/object_test.go @@ -11,7 +11,7 @@ import ( func TestInitCreation(t *testing.T) { var o object.Object - cnr := *cidtest.ID() + cnr := cidtest.ID() own := *ownertest.ID() object.InitCreation(&o, object.RequiredFields{ @@ -19,6 +19,6 @@ func TestInitCreation(t *testing.T) { Owner: own, }) - require.Equal(t, &cnr, o.ContainerID()) + require.Equal(t, cnr, o.ContainerID()) require.Equal(t, &own, o.OwnerID()) } diff --git a/object/raw_test.go b/object/raw_test.go index cd7197d0..b1675c4e 100644 --- a/object/raw_test.go +++ b/object/raw_test.go @@ -16,8 +16,8 @@ import ( "github.com/stretchr/testify/require" ) -func randID(t *testing.T) *oid.ID { - id := oid.NewID() +func randID(t *testing.T) oid.ID { + var id oid.ID id.SetSHA256(randSHA256Checksum(t)) return id @@ -177,9 +177,9 @@ func TestObject_SetChildren(t *testing.T) { id1 := 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) { @@ -280,7 +280,7 @@ func TestObject_ResetRelations(t *testing.T) { obj.ResetRelations() - require.Nil(t, obj.PreviousID()) + require.True(t, obj.PreviousID().Empty()) } func TestObject_HasParent(t *testing.T) { diff --git a/object/search.go b/object/search.go index 481e11c2..b78cc82d 100644 --- a/object/search.go +++ b/object/search.go @@ -220,7 +220,7 @@ func (f *SearchFilters) AddObjectVersionFilter(op SearchMatchType, v *version.Ve 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) } @@ -261,12 +261,12 @@ func (f *SearchFilters) AddPhyFilter() { } // 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) } // 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) } diff --git a/object/search_test.go b/object/search_test.go index debaff79..55028a90 100644 --- a/object/search_test.go +++ b/object/search_test.go @@ -100,12 +100,12 @@ func TestSearchFilters_AddPhyFilter(t *testing.T) { require.Equal(t, "", f.Value()) } -func testOID() *oid.ID { +func testOID() oid.ID { cs := [sha256.Size]byte{} rand.Read(cs[:]) - id := oid.NewID() + var id oid.ID id.SetSHA256(cs) return id diff --git a/object/splitinfo.go b/object/splitinfo.go index 4a2fcfad..2201e467 100644 --- a/object/splitinfo.go +++ b/object/splitinfo.go @@ -2,6 +2,7 @@ package object import ( "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" ) @@ -40,22 +41,42 @@ func (s *SplitInfo) SetSplitID(v *SplitID) { (*object.SplitInfo)(s).SetSplitID(v.ToV2()) } -func (s *SplitInfo) LastPart() *oid.ID { - return oid.NewIDFromV2( - (*object.SplitInfo)(s).GetLastPart()) +func (s SplitInfo) LastPart() oid.ID { + var id oid.ID + v2 := (object.SplitInfo)(s) + + lpV2 := v2.GetLastPart() + if lpV2 != nil { + _ = id.ReadFromV2(*lpV2) + } + + return id } -func (s *SplitInfo) SetLastPart(v *oid.ID) { - (*object.SplitInfo)(s).SetLastPart(v.ToV2()) +func (s *SplitInfo) SetLastPart(v oid.ID) { + var idV2 refs.ObjectID + v.WriteToV2(&idV2) + + (*object.SplitInfo)(s).SetLastPart(&idV2) } -func (s *SplitInfo) Link() *oid.ID { - return oid.NewIDFromV2( - (*object.SplitInfo)(s).GetLink()) +func (s SplitInfo) Link() oid.ID { + var id oid.ID + v2 := (object.SplitInfo)(s) + + linkV2 := v2.GetLink() + if linkV2 != nil { + _ = id.ReadFromV2(*linkV2) + } + + return id } -func (s *SplitInfo) SetLink(v *oid.ID) { - (*object.SplitInfo)(s).SetLink(v.ToV2()) +func (s *SplitInfo) SetLink(v oid.ID) { + var idV2 refs.ObjectID + v.WriteToV2(&idV2) + + (*object.SplitInfo)(s).SetLink(&idV2) } func (s *SplitInfo) Marshal() ([]byte, error) { diff --git a/object/splitinfo_test.go b/object/splitinfo_test.go index 1c42a4d2..f89cb524 100644 --- a/object/splitinfo_test.go +++ b/object/splitinfo_test.go @@ -45,11 +45,11 @@ func TestSplitInfo(t *testing.T) { }) } -func generateID() *oid.ID { +func generateID() oid.ID { var buf [32]byte _, _ = rand.Read(buf[:]) - id := oid.NewID() + var id oid.ID id.SetSHA256(buf) return id @@ -77,8 +77,8 @@ func TestNewSplitInfo(t *testing.T) { // check initial values require.Nil(t, si.SplitID()) - require.Nil(t, si.LastPart()) - require.Nil(t, si.Link()) + require.True(t, si.LastPart().Empty()) + require.True(t, si.Link().Empty()) // convert to v2 message siV2 := si.ToV2() diff --git a/object/test/generate.go b/object/test/generate.go index 3df931ed..3ec9e007 100644 --- a/object/test/generate.go +++ b/object/test/generate.go @@ -57,7 +57,7 @@ func generate(withParent bool) *object.Object { x.SetCreationEpoch(222) x.SetPreviousID(oidtest.ID()) x.SetParentID(oidtest.ID()) - x.SetChildren(*oidtest.ID(), *oidtest.ID()) + x.SetChildren(oidtest.ID(), oidtest.ID()) x.SetAttributes(*Attribute(), *Attribute()) x.SetSplitID(SplitID()) x.SetPayloadChecksum(checksumtest.Checksum()) @@ -88,7 +88,7 @@ func Tombstone() *object.Tombstone { x.SetSplitID(SplitID()) x.SetExpirationEpoch(13) - x.SetMembers([]oid.ID{*oidtest.ID(), *oidtest.ID()}) + x.SetMembers([]oid.ID{oidtest.ID(), oidtest.ID()}) return x } @@ -117,7 +117,7 @@ func SearchFilters() object.SearchFilters { // Lock returns random object.Lock. func Lock() *object.Lock { var l object.Lock - l.WriteMembers([]oid.ID{*oidtest.ID(), *oidtest.ID()}) + l.WriteMembers([]oid.ID{oidtest.ID(), oidtest.ID()}) return &l } diff --git a/object/tombstone.go b/object/tombstone.go index 03bb05e9..4f4fface 100644 --- a/object/tombstone.go +++ b/object/tombstone.go @@ -56,17 +56,21 @@ func (t *Tombstone) SetSplitID(v *SplitID) { // Members returns list of objects to be deleted. func (t *Tombstone) Members() []oid.ID { - msV2 := (*tombstone.Tombstone)(t). - GetMembers() + v2 := (*tombstone.Tombstone)(t) + msV2 := v2.GetMembers() if msV2 == nil { return nil } - ms := make([]oid.ID, len(msV2)) + var ( + ms = make([]oid.ID, len(msV2)) + id oid.ID + ) for i := range msV2 { - ms[i] = *oid.NewIDFromV2(&msV2[i]) + _ = id.ReadFromV2(msV2[i]) + ms[i] = id } return ms @@ -86,8 +90,11 @@ func (t *Tombstone) SetMembers(v []oid.ID) { ms = make([]refs.ObjectID, 0, ln) } + var idV2 refs.ObjectID + for i := range v { - ms = append(ms, *v[i].ToV2()) + v[i].WriteToV2(&idV2) + ms = append(ms, idV2) } } diff --git a/object/tombstone_test.go b/object/tombstone_test.go index f156353a..62d50505 100644 --- a/object/tombstone_test.go +++ b/object/tombstone_test.go @@ -15,7 +15,9 @@ func generateIDList(sz int) []oid.ID { cs := [sha256.Size]byte{} for i := 0; i < sz; i++ { - res[i] = *oid.NewID() + var oID oid.ID + + res[i] = oID rand.Read(cs[:]) res[i].SetSHA256(cs) } diff --git a/session/container.go b/session/container.go index ed754560..809c706f 100644 --- a/session/container.go +++ b/session/container.go @@ -1,6 +1,7 @@ package session import ( + "github.com/nspcc-dev/neofs-api-go/v2/refs" "github.com/nspcc-dev/neofs-api-go/v2/session" 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) { 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.SetContainerID(id.ToV2()) + v2.SetContainerID(cidV2) } // ApplyToAllContainers is a helper function that conveniently @@ -65,7 +75,15 @@ func (x *ContainerContext) Container() *cid.ID { 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) { diff --git a/session/container_test.go b/session/container_test.go index 800025f2..8456f842 100644 --- a/session/container_test.go +++ b/session/container_test.go @@ -39,9 +39,9 @@ func TestContainerContext_ApplyTo(t *testing.T) { id := cidtest.ID() 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) @@ -49,7 +49,7 @@ func TestContainerContext_ApplyTo(t *testing.T) { }) t.Run("helper functions", func(t *testing.T) { - c.ApplyTo(id) + c.ApplyTo(&id) session.ApplyToAllContainers(c) diff --git a/session/test/container.go b/session/test/container.go index 2a0e2024..1a750961 100644 --- a/session/test/container.go +++ b/session/test/container.go @@ -20,7 +20,8 @@ func ContainerContext() *session.ContainerContext { setters[rand.Uint32()%uint32(len(setters))]() - c.ApplyTo(cidtest.ID()) + cID := cidtest.ID() + c.ApplyTo(&cID) return c } diff --git a/storagegroup/storagegroup.go b/storagegroup/storagegroup.go index 2db0bd23..fae8dd0c 100644 --- a/storagegroup/storagegroup.go +++ b/storagegroup/storagegroup.go @@ -93,7 +93,7 @@ func (sg *StorageGroup) Members() []oid.ID { m := make([]oid.ID, len(mV2)) for i := range mV2 { - m[i] = *oid.NewIDFromV2(&mV2[i]) + _ = m[i].ReadFromV2(mV2[i]) } return m @@ -115,8 +115,11 @@ func (sg *StorageGroup) SetMembers(members []oid.ID) { mV2 = make([]refs.ObjectID, ln) } + var oidV2 refs.ObjectID + for i := 0; i < ln; i++ { - mV2[i] = *members[i].ToV2() + members[i].WriteToV2(&oidV2) + mV2[i] = oidV2 } } diff --git a/storagegroup/storagegroup_test.go b/storagegroup/storagegroup_test.go index 9afe8b8e..6a197d58 100644 --- a/storagegroup/storagegroup_test.go +++ b/storagegroup/storagegroup_test.go @@ -30,7 +30,7 @@ func TestStorageGroup(t *testing.T) { sg.SetExpirationEpoch(exp) require.Equal(t, exp, sg.ExpirationEpoch()) - members := []oid.ID{*oidtest.ID(), *oidtest.ID()} + members := []oid.ID{oidtest.ID(), oidtest.ID()} sg.SetMembers(members) require.Equal(t, members, sg.Members()) } diff --git a/storagegroup/test/generate.go b/storagegroup/test/generate.go index 76415f01..a5c1756b 100644 --- a/storagegroup/test/generate.go +++ b/storagegroup/test/generate.go @@ -14,7 +14,7 @@ func StorageGroup() *storagegroup.StorageGroup { x.SetExpirationEpoch(66) x.SetValidationDataSize(322) x.SetValidationDataHash(checksumtest.Checksum()) - x.SetMembers([]oid.ID{*oidtest.ID(), *oidtest.ID()}) + x.SetMembers([]oid.ID{oidtest.ID(), oidtest.ID()}) return x }