[#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
// 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

View file

@ -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
}
}))

View file

@ -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
}

View file

@ -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

View file

@ -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
}

View file

@ -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

View file

@ -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.

View file

@ -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
}

View file

@ -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

View file

@ -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)

View file

@ -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.

View file

@ -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()

View file

@ -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

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
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{}
}

View file

@ -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())
})
}

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.
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

View file

@ -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)
}

View file

@ -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
}

View file

@ -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())

View file

@ -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
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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()

View file

@ -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 {

View file

@ -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]--
},
},
{

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
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
}

View file

@ -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())
})
}

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 (
"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

View file

@ -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])
}
}

View file

@ -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)
})
}

View file

@ -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())
}

View file

@ -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) {

View file

@ -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)
}

View file

@ -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

View file

@ -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) {

View file

@ -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()

View file

@ -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
}

View file

@ -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)
}
}

View file

@ -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)
}

View file

@ -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) {

View file

@ -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)

View file

@ -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
}

View file

@ -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
}
}

View file

@ -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())
}

View file

@ -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
}