From 3c47093ae4c28b62afba3e3f34bb270d39cf3137 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Fri, 25 Oct 2024 13:07:23 +0300 Subject: [PATCH 1/9] object: Verify format of `Object` message fields Follow 80da1c65b3ba6b3cd909ace03deb39c632ee5d6a. Signed-off-by: Leonard Lyubich --- client/object_get.go | 14 +-- object/object.go | 251 ++++++++++++++++++++++++++++++++----------- 2 files changed, 197 insertions(+), 68 deletions(-) diff --git a/client/object_get.go b/client/object_get.go index 18633d7bc..7efbd0c0f 100644 --- a/client/object_get.go +++ b/client/object_get.go @@ -124,9 +124,8 @@ func (x *PayloadReader) readHeader(dst *object.Object) bool { x.remainingPayloadLen = int(objv2.GetHeader().GetPayloadLength()) - *dst = *object.NewFromV2(&objv2) // need smth better - - return true + x.err = dst.ReadFromV2(objv2) + return x.err == nil } func (x *PayloadReader) readChunk(buf []byte) (int, bool) { @@ -404,10 +403,11 @@ func (c *Client) ObjectHead(ctx context.Context, containerID cid.ID, objectID oi objv2.SetHeader(v.GetHeader()) objv2.SetSignature(v.GetSignature()) - obj := object.NewFromV2(&objv2) - obj.SetID(objectID) - - return obj, nil + var obj object.Object + if err = obj.ReadFromV2(objv2); err != nil { + return nil, fmt.Errorf("invalid header response: %w", err) + } + return &obj, nil } } diff --git a/object/object.go b/object/object.go index c0dc8753e..af82eb1a7 100644 --- a/object/object.go +++ b/object/object.go @@ -2,10 +2,11 @@ package object import ( "bytes" - "errors" "fmt" + "strconv" "strings" + "github.com/google/uuid" "github.com/nspcc-dev/neofs-api-go/v2/object" "github.com/nspcc-dev/neofs-api-go/v2/refs" v2session "github.com/nspcc-dev/neofs-api-go/v2/session" @@ -24,7 +25,7 @@ import ( // Instance can be created depending on scenario: // - [Object.InitCreation] (an object to be placed in container); // - New (blank instance, usually needed for decoding); -// - NewFromV2 (when working under NeoFS API V2 protocol). +// - [Object.ReadFromV2] (when working under NeoFS API V2 protocol). type Object object.Object // RequiredFields contains the minimum set of object data that must be set @@ -44,15 +45,166 @@ func (o *Object) InitCreation(rf RequiredFields) { } // NewFromV2 wraps v2 [object.Object] message to [Object]. +// +// Deprecated: BUG: fields' format is not checked. Use [Object.ReadFromV2] +// instead. func NewFromV2(oV2 *object.Object) *Object { return (*Object)(oV2) } // New creates and initializes blank [Object]. -// -// Works similar as NewFromV2(new(Object)). func New() *Object { - return NewFromV2(new(object.Object)) + return new(Object) +} + +func verifySplitHeaderV2(m object.SplitHeader) error { + // parent ID + if mID := m.GetParent(); mID != nil { + if err := new(oid.ID).ReadFromV2(*mID); err != nil { + return fmt.Errorf("invalid parent split member ID: %w", err) + } + } + // previous + if mID := m.GetPrevious(); mID != nil { + if err := new(oid.ID).ReadFromV2(*mID); err != nil { + return fmt.Errorf("invalid previous split member ID: %w", err) + } + } + // first + if mID := m.GetFirst(); mID != nil { + if err := new(oid.ID).ReadFromV2(*mID); err != nil { + return fmt.Errorf("invalid first split member ID: %w", err) + } + } + // split ID + if b := m.GetSplitID(); len(b) > 0 { + var uid uuid.UUID + if err := uid.UnmarshalBinary(b); err != nil { + return fmt.Errorf("invalid split UUID: %w", err) + } else if ver := uid.Version(); ver != 4 { + return fmt.Errorf("invalid split UUID version %d", ver) + } + } + // children + if mc := m.GetChildren(); len(mc) > 0 { + for i := range mc { + if err := new(oid.ID).ReadFromV2(mc[i]); err != nil { + return fmt.Errorf("invalid child split member ID #%d: %w", i, err) + } + } + } + // parent signature + if ms := m.GetParentSignature(); ms != nil { + if err := new(neofscrypto.Signature).ReadFromV2(*ms); err != nil { + return fmt.Errorf("invalid parent signature: %w", err) + } + } + // parent header + if mh := m.GetParentHeader(); mh != nil { + if err := verifyHeaderV2(*mh); err != nil { + return fmt.Errorf("invalid parent header: %w", err) + } + } + return nil +} + +func verifyHeaderV2(m object.Header) error { + // version + if mv := m.GetVersion(); mv != nil { + if err := new(version.Version).ReadFromV2(*mv); err != nil { + return fmt.Errorf("invalid version: %w", err) + } + } + // owner + if mu := m.GetOwnerID(); mu != nil { + if err := new(user.ID).ReadFromV2(*mu); err != nil { + return fmt.Errorf("invalid owner: %w", err) + } + } + // container + if mc := m.GetContainerID(); mc != nil { + if err := new(cid.ID).ReadFromV2(*mc); err != nil { + return fmt.Errorf("invalid container: %w", err) + } + } + // payload checksum + if mc := m.GetPayloadHash(); mc != nil { + if err := new(checksum.Checksum).ReadFromV2(*mc); err != nil { + return fmt.Errorf("invalid payload checksum: %w", err) + } + } + // payload homomorphic checksum + if mc := m.GetHomomorphicHash(); mc != nil { + if err := new(checksum.Checksum).ReadFromV2(*mc); err != nil { + return fmt.Errorf("invalid payload homomorphic checksum: %w", err) + } + } + // session token + if ms := m.GetSessionToken(); ms != nil { + if err := new(session.Object).ReadFromV2(*ms); err != nil { + return fmt.Errorf("invalid session token: %w", err) + } + } + // split header + if ms := m.GetSplit(); ms != nil { + if err := verifySplitHeaderV2(*ms); err != nil { + return fmt.Errorf("invalid split header: %w", err) + } + } + // attributes + if ma := m.GetAttributes(); len(ma) > 0 { + done := make(map[string]struct{}, len(ma)) + for i := range ma { + key := ma[i].GetKey() + if key == "" { + return fmt.Errorf("empty key of the attribute #%d", i) + } + if _, ok := done[key]; ok { + return fmt.Errorf("duplicated attribute %s", key) + } + val := ma[i].GetValue() + if val == "" { + return fmt.Errorf("empty value of the attribute #%d (%s)", i, key) + } + switch key { + case AttributeExpirationEpoch: + if _, err := strconv.ParseUint(val, 10, 64); err != nil { + return fmt.Errorf("invalid expiration attribute (must be a uint): %w", err) + } + } + done[key] = struct{}{} + } + } + return nil +} + +// ReadFromV2 reads Object from the [object.Object] message. Returns an error if +// the message is malformed according to the NeoFS API V2 protocol. The message +// must not be nil. +// +// ReadFromV2 is intended to be used by the NeoFS API V2 client/server +// implementation only and is not expected to be directly used by applications. +func (o *Object) ReadFromV2(m object.Object) error { + // ID + if mID := m.GetObjectID(); mID != nil { + if err := new(oid.ID).ReadFromV2(*mID); err != nil { + return fmt.Errorf("invalid ID: %w", err) + } + } + // signature + if ms := m.GetSignature(); ms != nil { + if err := new(neofscrypto.Signature).ReadFromV2(*ms); err != nil { + return fmt.Errorf("invalid signature: %w", err) + } + } + // header + if mh := m.GetHeader(); mh != nil { + if err := verifyHeaderV2(*mh); err != nil { + return fmt.Errorf("invalid header: %w", err) + } + } + *o = Object(m) + return nil } // ToV2 converts [Object] to v2 [object.Object] message. @@ -122,7 +274,9 @@ func (o *Object) GetID() oid.ID { var res oid.ID m := (*object.Object)(o) if id := m.GetObjectID(); id != nil { - _ = res.ReadFromV2(*m.GetObjectID()) + if err := res.ReadFromV2(*id); err != nil { + panic(fmt.Errorf("unexpected ID decoding failure: %w", err)) + } } return res @@ -267,7 +421,9 @@ func (o *Object) SetContainerID(v cid.ID) { func (o *Object) GetContainerID() cid.ID { var cnr cid.ID if m := (*object.Object)(o).GetHeader().GetContainerID(); m != nil { - _ = cnr.ReadFromV2(*m) + if err := cnr.ReadFromV2(*m); err != nil { + panic(fmt.Errorf("unexpected container ID decoding failure: %w", err)) + } } return cnr } @@ -280,7 +436,15 @@ func (o *Object) OwnerID() *user.ID { m := (*object.Object)(o).GetHeader().GetOwnerID() if m != nil { - _ = id.ReadFromV2(*m) + // unlike other IDs, user.ID.ReadFromV2 also expects correct prefix and checksum + // suffix. So, we cannot call it and panic on error here because nothing + // prevents user from setting incorrect owner ID (Object.SetOwnerID accepts it). + // At the same time, we always expect correct length. + b := m.GetValue() + if len(b) != user.IDSize { + panic(fmt.Errorf("unexpected user ID decoding failure: invalid length %d, expected %d", len(b), user.IDSize)) + } + copy(id[:], b) } return &id @@ -447,7 +611,9 @@ func (o *Object) PreviousID() (oid.ID, bool) { func (o *Object) GetPreviousID() oid.ID { var id oid.ID if m := (*object.Object)(o).GetHeader().GetSplit().GetPrevious(); m != nil { - _ = id.ReadFromV2(*m) + if err := id.ReadFromV2(*m); err != nil { + panic(fmt.Errorf("unexpected ID decoding failure: %w", err)) + } } return id } @@ -486,7 +652,9 @@ func (o *Object) Children() []oid.ID { ) for i := range ids { - _ = id.ReadFromV2(ids[i]) + if err := id.ReadFromV2(ids[i]); err != nil { + panic(fmt.Errorf("unexpected child#%d decoding failure: %w", i, err)) + } res[i] = id } @@ -539,7 +707,9 @@ func (o *Object) FirstID() (oid.ID, bool) { func (o *Object) GetFirstID() oid.ID { var id oid.ID if m := (*object.Object)(o).GetHeader().GetSplit().GetFirst(); m != nil { - _ = id.ReadFromV2(*m) + if err := id.ReadFromV2(*m); err != nil { + panic(fmt.Errorf("unexpected ID decoding failure: %w", err)) + } } return id } @@ -581,7 +751,9 @@ func (o *Object) ParentID() (oid.ID, bool) { func (o *Object) GetParentID() oid.ID { var id oid.ID if m := (*object.Object)(o).GetHeader().GetSplit().GetParent(); m != nil { - _ = id.ReadFromV2(*m) + if err := id.ReadFromV2(*m); err != nil { + panic(fmt.Errorf("unexpected ID decoding failure: %w", err)) + } } return id } @@ -743,12 +915,13 @@ func (o *Object) Marshal() []byte { // // See also [Object.Marshal]. func (o *Object) Unmarshal(data []byte) error { - err := (*object.Object)(o).Unmarshal(data) + var m object.Object + err := m.Unmarshal(data) if err != nil { return err } - return formatCheck((*object.Object)(o)) + return o.ReadFromV2(m) } // MarshalJSON encodes object to protobuf JSON format. @@ -762,57 +935,13 @@ func (o *Object) MarshalJSON() ([]byte, error) { // // See also [Object.MarshalJSON]. func (o *Object) UnmarshalJSON(data []byte) error { - err := (*object.Object)(o).UnmarshalJSON(data) + var m object.Object + err := m.UnmarshalJSON(data) if err != nil { return err } - return formatCheck((*object.Object)(o)) -} - -var errCIDNotSet = errors.New("container ID is not set") - -func formatCheck(v2 *object.Object) error { - var ( - oID oid.ID - cID cid.ID - ) - - oidV2 := v2.GetObjectID() - if oidV2 == nil { - return oid.ErrZero - } - - err := oID.ReadFromV2(*oidV2) - if err != nil { - return fmt.Errorf("could not convert V2 object ID: %w", err) - } - - cidV2 := v2.GetHeader().GetContainerID() - if cidV2 == nil { - return errCIDNotSet - } - - err = cID.ReadFromV2(*cidV2) - if err != nil { - return fmt.Errorf("could not convert V2 container ID: %w", err) - } - - if prev := v2.GetHeader().GetSplit().GetPrevious(); prev != nil { - err = oID.ReadFromV2(*prev) - if err != nil { - return fmt.Errorf("could not convert previous object ID: %w", err) - } - } - - if parent := v2.GetHeader().GetSplit().GetParent(); parent != nil { - err = oID.ReadFromV2(*parent) - if err != nil { - return fmt.Errorf("could not convert parent object ID: %w", err) - } - } - - return nil + return o.ReadFromV2(m) } // HeaderLen returns length of the binary header. From cc4d4a16e34f35413a858274186f9d188c70cde3 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Fri, 25 Oct 2024 13:28:18 +0300 Subject: [PATCH 2/9] object: Do not declare stateless `Object` methods on a pointer Follow 8e8ebc7260a3a2d86538c378c8f2ca470aedb936. Signed-off-by: Leonard Lyubich --- object/fmt.go | 16 +++--- object/link.go | 2 +- object/lock.go | 2 +- object/object.go | 102 ++++++++++++++++----------------- object/object_internal_test.go | 47 ++++++++------- 5 files changed, 84 insertions(+), 85 deletions(-) diff --git a/object/fmt.go b/object/fmt.go index ead434ba7..135766764 100644 --- a/object/fmt.go +++ b/object/fmt.go @@ -38,7 +38,7 @@ func (o *Object) CalculateAndSetPayloadChecksum() { // VerifyPayloadChecksum checks if payload checksum in the object // corresponds to its payload. -func (o *Object) VerifyPayloadChecksum() error { +func (o Object) VerifyPayloadChecksum() error { actual := CalculatePayloadChecksum(o.Payload()) cs, set := o.PayloadChecksum() @@ -54,7 +54,7 @@ func (o *Object) VerifyPayloadChecksum() error { } // CalculateID calculates identifier for the object. -func (o *Object) CalculateID() (oid.ID, error) { +func (o Object) CalculateID() (oid.ID, error) { return sha256.Sum256(o.ToV2().GetHeader().StableMarshal(nil)), nil } @@ -73,7 +73,7 @@ func (o *Object) CalculateAndSetID() error { // VerifyID checks if identifier in the object corresponds to // its structure. -func (o *Object) VerifyID() error { +func (o Object) VerifyID() error { id, err := o.CalculateID() if err != nil { return err @@ -113,13 +113,13 @@ func (o *Object) Sign(signer neofscrypto.Signer) error { // SignedData returns actual payload to sign. // // See also [Object.Sign]. -func (o *Object) SignedData() []byte { +func (o Object) SignedData() []byte { return o.GetID().Marshal() } // VerifySignature verifies object ID signature. -func (o *Object) VerifySignature() bool { - m := (*object.Object)(o) +func (o Object) VerifySignature() bool { + m := (*object.Object)(&o) sigV2 := m.GetSignature() if sigV2 == nil { @@ -157,7 +157,7 @@ func (o *Object) SetVerificationFields(signer neofscrypto.Signer) error { } // CheckVerificationFields checks all verification fields of the object. -func (o *Object) CheckVerificationFields() error { +func (o Object) CheckVerificationFields() error { if err := o.CheckHeaderVerificationFields(); err != nil { return fmt.Errorf("invalid header structure: %w", err) } @@ -172,7 +172,7 @@ func (o *Object) CheckVerificationFields() error { var errInvalidSignature = errors.New("invalid signature") // CheckHeaderVerificationFields checks all verification fields except payload. -func (o *Object) CheckHeaderVerificationFields() error { +func (o Object) CheckHeaderVerificationFields() error { if !o.VerifySignature() { return errInvalidSignature } diff --git a/object/link.go b/object/link.go index 3bf69518b..4c775e6ed 100644 --- a/object/link.go +++ b/object/link.go @@ -28,7 +28,7 @@ func (o *Object) WriteLink(l Link) { // if the object has [TypeLink] type. // // See also [Object.WriteLink]. -func (o *Object) ReadLink(l *Link) error { +func (o Object) ReadLink(l *Link) error { return l.Unmarshal(o.Payload()) } diff --git a/object/lock.go b/object/lock.go index 06139b275..c153adb79 100644 --- a/object/lock.go +++ b/object/lock.go @@ -27,7 +27,7 @@ func (o *Object) WriteLock(l Lock) { // if object has [TypeLock] type. // // See also [Object.WriteLock]. -func (o *Object) ReadLock(l *Lock) error { +func (o Object) ReadLock(l *Lock) error { return l.Unmarshal(o.Payload()) } diff --git a/object/object.go b/object/object.go index af82eb1a7..635205259 100644 --- a/object/object.go +++ b/object/object.go @@ -211,8 +211,8 @@ func (o *Object) ReadFromV2(m object.Object) error { // // The value returned shares memory with the structure itself, so changing it can lead to data corruption. // Make a copy if you need to change it. -func (o *Object) ToV2() *object.Object { - return (*object.Object)(o) +func (o Object) ToV2() *object.Object { + return (*object.Object)(&o) } // CopyTo writes deep copy of the [Object] to dst. @@ -230,8 +230,8 @@ func (o Object) CopyTo(dst *Object) { } // MarshalHeaderJSON marshals object's header into JSON format. -func (o *Object) MarshalHeaderJSON() ([]byte, error) { - return (*object.Object)(o).GetHeader().MarshalJSON() +func (o Object) MarshalHeaderJSON() ([]byte, error) { + return (*object.Object)(&o).GetHeader().MarshalJSON() } func (o *Object) setHeaderField(setter func(*object.Header)) { @@ -270,9 +270,9 @@ func (o *Object) ID() (oid.ID, bool) { // GetID returns identifier of the object. Zero return means unset ID. // // See also [Object.SetID]. -func (o *Object) GetID() oid.ID { +func (o Object) GetID() oid.ID { var res oid.ID - m := (*object.Object)(o) + m := (*object.Object)(&o) if id := m.GetObjectID(); id != nil { if err := res.ReadFromV2(*id); err != nil { panic(fmt.Errorf("unexpected ID decoding failure: %w", err)) @@ -304,8 +304,8 @@ func (o *Object) ResetID() { // Signature returns signature of the object identifier. // // See also [Object.SetSignature]. -func (o *Object) Signature() *neofscrypto.Signature { - sigv2 := (*object.Object)(o).GetSignature() +func (o Object) Signature() *neofscrypto.Signature { + sigv2 := (*object.Object)(&o).GetSignature() if sigv2 == nil { return nil } @@ -339,8 +339,8 @@ func (o *Object) SetSignature(v *neofscrypto.Signature) { // Make a copy if you need to change it. // // See also [Object.SetPayload]. -func (o *Object) Payload() []byte { - return (*object.Object)(o).GetPayload() +func (o Object) Payload() []byte { + return (*object.Object)(&o).GetPayload() } // SetPayload sets payload bytes. @@ -353,9 +353,9 @@ func (o *Object) SetPayload(v []byte) { // Version returns version of the object. // // See also [Object.SetVersion]. -func (o *Object) Version() *version.Version { +func (o Object) Version() *version.Version { var ver version.Version - if verV2 := (*object.Object)(o).GetHeader().GetVersion(); verV2 != nil { + if verV2 := (*object.Object)(&o).GetHeader().GetVersion(); verV2 != nil { if err := ver.ReadFromV2(*verV2); err != nil { return nil } @@ -378,8 +378,8 @@ func (o *Object) SetVersion(v *version.Version) { // PayloadSize returns payload length of the object. // // See also [Object.SetPayloadSize]. -func (o *Object) PayloadSize() uint64 { - return (*object.Object)(o). +func (o Object) PayloadSize() uint64 { + return (*object.Object)(&o). GetHeader(). GetPayloadLength() } @@ -418,9 +418,9 @@ func (o *Object) SetContainerID(v cid.ID) { // binding. // // See also [Object.SetContainerID]. -func (o *Object) GetContainerID() cid.ID { +func (o Object) GetContainerID() cid.ID { var cnr cid.ID - if m := (*object.Object)(o).GetHeader().GetContainerID(); m != nil { + if m := (*object.Object)(&o).GetHeader().GetContainerID(); m != nil { if err := cnr.ReadFromV2(*m); err != nil { panic(fmt.Errorf("unexpected container ID decoding failure: %w", err)) } @@ -465,8 +465,8 @@ func (o *Object) SetOwnerID(v *user.ID) { // CreationEpoch returns epoch number in which object was created. // // See also [Object.SetCreationEpoch]. -func (o *Object) CreationEpoch() uint64 { - return (*object.Object)(o). +func (o Object) CreationEpoch() uint64 { + return (*object.Object)(&o). GetHeader(). GetCreationEpoch() } @@ -486,9 +486,9 @@ func (o *Object) SetCreationEpoch(v uint64) { // Zero [Object] does not have payload checksum. // // See also [Object.SetPayloadChecksum]. -func (o *Object) PayloadChecksum() (checksum.Checksum, bool) { +func (o Object) PayloadChecksum() (checksum.Checksum, bool) { var v checksum.Checksum - v2 := (*object.Object)(o) + v2 := (*object.Object)(&o) if hash := v2.GetHeader().GetPayloadHash(); hash != nil { err := v.ReadFromV2(*hash) @@ -516,9 +516,9 @@ func (o *Object) SetPayloadChecksum(v checksum.Checksum) { // Zero [Object] does not have payload homomorphic checksum. // // See also [Object.SetPayloadHomomorphicHash]. -func (o *Object) PayloadHomomorphicHash() (checksum.Checksum, bool) { +func (o Object) PayloadHomomorphicHash() (checksum.Checksum, bool) { var v checksum.Checksum - v2 := (*object.Object)(o) + v2 := (*object.Object)(&o) if hash := v2.GetHeader().GetHomomorphicHash(); hash != nil { err := v.ReadFromV2(*hash) @@ -546,8 +546,8 @@ func (o *Object) SetPayloadHomomorphicHash(v checksum.Checksum) { // Make a copy if you need to change it. // // See also [Object.SetAttributes], [Object.UserAttributes]. -func (o *Object) Attributes() []Attribute { - attrs := (*object.Object)(o). +func (o Object) Attributes() []Attribute { + attrs := (*object.Object)(&o). GetHeader(). GetAttributes() @@ -566,8 +566,8 @@ func (o *Object) Attributes() []Attribute { // Make a copy if you need to change it. // // See also [Object.SetAttributes], [Object.Attributes]. -func (o *Object) UserAttributes() []Attribute { - attrs := (*object.Object)(o). +func (o Object) UserAttributes() []Attribute { + attrs := (*object.Object)(&o). GetHeader(). GetAttributes() @@ -608,9 +608,9 @@ func (o *Object) PreviousID() (oid.ID, bool) { // means unset ID. // // See also [Object.SetPreviousID]. -func (o *Object) GetPreviousID() oid.ID { +func (o Object) GetPreviousID() oid.ID { var id oid.ID - if m := (*object.Object)(o).GetHeader().GetSplit().GetPrevious(); m != nil { + if m := (*object.Object)(&o).GetHeader().GetSplit().GetPrevious(); m != nil { if err := id.ReadFromV2(*m); err != nil { panic(fmt.Errorf("unexpected ID decoding failure: %w", err)) } @@ -642,8 +642,8 @@ func (o *Object) SetPreviousID(v oid.ID) { // Children return list of the identifiers of the child objects. // // See also [Object.SetChildren]. -func (o *Object) Children() []oid.ID { - v2 := (*object.Object)(o) +func (o Object) Children() []oid.ID { + v2 := (*object.Object)(&o) ids := v2.GetHeader().GetSplit().GetChildren() var ( @@ -696,7 +696,7 @@ func (o *Object) SetFirstID(id oid.ID) { // FirstID returns the first part of the object's split chain. // // See also [Object.SetFirstID]. -func (o *Object) FirstID() (oid.ID, bool) { +func (o Object) FirstID() (oid.ID, bool) { id := o.GetFirstID() return id, !id.IsZero() } @@ -704,9 +704,9 @@ func (o *Object) FirstID() (oid.ID, bool) { // GetFirstID returns the first part of the object's split chain. Zero return means unset ID. // // See also [Object.SetFirstID]. -func (o *Object) GetFirstID() oid.ID { +func (o Object) GetFirstID() oid.ID { var id oid.ID - if m := (*object.Object)(o).GetHeader().GetSplit().GetFirst(); m != nil { + if m := (*object.Object)(&o).GetHeader().GetSplit().GetFirst(); m != nil { if err := id.ReadFromV2(*m); err != nil { panic(fmt.Errorf("unexpected ID decoding failure: %w", err)) } @@ -717,9 +717,9 @@ func (o *Object) GetFirstID() oid.ID { // SplitID return split identity of split object. If object is not split returns nil. // // See also [Object.SetSplitID]. -func (o *Object) SplitID() *SplitID { +func (o Object) SplitID() *SplitID { return NewSplitIDFromV2( - (*object.Object)(o). + (*object.Object)(&o). GetHeader(). GetSplit(). GetSplitID(), @@ -748,9 +748,9 @@ func (o *Object) ParentID() (oid.ID, bool) { // ID. // // See also [Object.SetParentID]. -func (o *Object) GetParentID() oid.ID { +func (o Object) GetParentID() oid.ID { var id oid.ID - if m := (*object.Object)(o).GetHeader().GetSplit().GetParent(); m != nil { + if m := (*object.Object)(&o).GetHeader().GetSplit().GetParent(); m != nil { if err := id.ReadFromV2(*m); err != nil { panic(fmt.Errorf("unexpected ID decoding failure: %w", err)) } @@ -782,8 +782,8 @@ func (o *Object) ResetParentID() { // Parent returns parent object w/o payload. // // See also [Object.SetParent]. -func (o *Object) Parent() *Object { - h := (*object.Object)(o). +func (o Object) Parent() *Object { + h := (*object.Object)(&o). GetHeader(). GetSplit() @@ -828,8 +828,8 @@ func (o *Object) resetRelations() { // SessionToken returns token of the session within which object was created. // // See also [Object.SetSessionToken]. -func (o *Object) SessionToken() *session.Object { - tokv2 := (*object.Object)(o).GetHeader().GetSessionToken() +func (o Object) SessionToken() *session.Object { + tokv2 := (*object.Object)(&o).GetHeader().GetSessionToken() if tokv2 == nil { return nil } @@ -860,8 +860,8 @@ func (o *Object) SetSessionToken(v *session.Object) { // Type returns type of the object. // // See also [Object.SetType]. -func (o *Object) Type() Type { - return Type((*object.Object)(o). +func (o Object) Type() Type { + return Type((*object.Object)(&o). GetHeader(). GetObjectType()) } @@ -888,8 +888,8 @@ func (o *Object) CutPayload() *Object { } // HasParent checks if parent (split ID) is present. -func (o *Object) HasParent() bool { - return (*object.Object)(o). +func (o Object) HasParent() bool { + return (*object.Object)(&o). GetHeader(). GetSplit() != nil } @@ -907,8 +907,8 @@ func (o *Object) InitRelations() { // Marshal marshals object into a protobuf binary form. // // See also [Object.Unmarshal]. -func (o *Object) Marshal() []byte { - return (*object.Object)(o).StableMarshal(nil) +func (o Object) Marshal() []byte { + return (*object.Object)(&o).StableMarshal(nil) } // Unmarshal unmarshals protobuf binary representation of object. @@ -927,8 +927,8 @@ func (o *Object) Unmarshal(data []byte) error { // MarshalJSON encodes object to protobuf JSON format. // // See also [Object.UnmarshalJSON]. -func (o *Object) MarshalJSON() ([]byte, error) { - return (*object.Object)(o).MarshalJSON() +func (o Object) MarshalJSON() ([]byte, error) { + return (*object.Object)(&o).MarshalJSON() } // UnmarshalJSON decodes object from protobuf JSON format. @@ -945,6 +945,6 @@ func (o *Object) UnmarshalJSON(data []byte) error { } // HeaderLen returns length of the binary header. -func (o *Object) HeaderLen() int { - return (*object.Object)(o).GetHeader().StableSize() +func (o Object) HeaderLen() int { + return (*object.Object)(&o).GetHeader().StableSize() } diff --git a/object/object_internal_test.go b/object/object_internal_test.go index a8d994914..33b549845 100644 --- a/object/object_internal_test.go +++ b/object/object_internal_test.go @@ -181,31 +181,30 @@ func TestObject_CopyTo(t *testing.T) { }) t.Run("header, change container id", func(t *testing.T) { - var localContID refs.ContainerID - localContID.SetValue([]byte{1}) - - var localHeader object.Header - localHeader.SetContainerID(&localContID) - - var local Object - local.ToV2().SetHeader(&localHeader) - - var dstContID refs.ContainerID - dstContID.SetValue([]byte{2}) - - var dstHeader object.Header - dstHeader.SetContainerID(&dstContID) - - var dst Object - dst.ToV2().SetHeader(&dstHeader) - - require.NotEqual(t, local.ToV2().GetHeader().GetContainerID(), dst.ToV2().GetHeader().GetContainerID()) - + c := cidtest.ID() + b := c[:] + var mc refs.ContainerID + mc.SetValue(b) + var mh object.Header + mh.SetContainerID(&mc) + var mo object.Object + mo.SetHeader(&mh) + + var local, dst Object + require.NoError(t, local.ReadFromV2(mo)) + require.NoError(t, dst.ReadFromV2(mo)) + require.Equal(t, local.GetContainerID(), dst.GetContainerID()) + + b[0]++ + require.Equal(t, c, local.GetContainerID()) + require.Equal(t, local.GetContainerID(), dst.GetContainerID()) + + cp := c local.CopyTo(&dst) - checkObjectEquals(t, local, dst) - - local.ToV2().GetHeader().GetContainerID().SetValue([]byte{3}) - require.NotEqual(t, local.ToV2().GetHeader().GetContainerID(), dst.ToV2().GetHeader().GetContainerID()) + b[0]++ + require.NotEqual(t, local.GetContainerID(), dst.GetContainerID()) + require.Equal(t, c, local.GetContainerID()) + require.Equal(t, cp, dst.GetContainerID()) }) t.Run("header, rewrite payload hash", func(t *testing.T) { From b20b16221f149cb5f73eadc4bb67fef9566b88f0 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Fri, 25 Oct 2024 14:43:06 +0300 Subject: [PATCH 3/9] object/test: Assert that generated instance follows the protocol Signed-off-by: Leonard Lyubich --- object/test/generate_test.go | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 object/test/generate_test.go diff --git a/object/test/generate_test.go b/object/test/generate_test.go new file mode 100644 index 000000000..b89f26f27 --- /dev/null +++ b/object/test/generate_test.go @@ -0,0 +1,28 @@ +package objecttest_test + +import ( + "testing" + + "github.com/nspcc-dev/neofs-sdk-go/object" + objecttest "github.com/nspcc-dev/neofs-sdk-go/object/test" + "github.com/stretchr/testify/require" +) + +func TestObject(t *testing.T) { + obj := objecttest.Object() + require.NotEqual(t, obj, objecttest.Object()) + + var dst1 object.Object + require.NoError(t, dst1.Unmarshal(obj.Marshal())) + require.Equal(t, obj, dst1) + + var dst2 object.Object + require.NoError(t, dst2.ReadFromV2(*obj.ToV2())) + require.Equal(t, obj, dst2) + + j, err := obj.MarshalJSON() + require.NoError(t, err) + var dst3 object.Object + require.NoError(t, dst3.UnmarshalJSON(j)) + require.Equal(t, obj, dst3) +} From 566bbee60abe01045a42a7f25ecf29ee78b6c160 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Fri, 25 Oct 2024 14:46:42 +0300 Subject: [PATCH 4/9] object/test: Make randomizer to return valid protocol instance Session token is now expected to be signed, and attributes must be unique. Signed-off-by: Leonard Lyubich --- object/test/generate.go | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/object/test/generate.go b/object/test/generate.go index 81bd1be46..934b2f8c0 100644 --- a/object/test/generate.go +++ b/object/test/generate.go @@ -1,6 +1,9 @@ package objecttest import ( + "math/rand" + "strconv" + "github.com/google/uuid" objecttest "github.com/nspcc-dev/neofs-api-go/v2/object/test" checksumtest "github.com/nspcc-dev/neofs-sdk-go/checksum/test" @@ -44,7 +47,7 @@ func generate(withParent bool) object.Object { ver := version.Current() x.SetID(oidtest.ID()) - tok := sessiontest.Object() + tok := sessiontest.ObjectSigned(usertest.User()) x.SetSessionToken(&tok) x.SetPayload([]byte{1, 2, 3}) owner := usertest.ID() @@ -57,7 +60,13 @@ func generate(withParent bool) object.Object { x.SetPreviousID(oidtest.ID()) x.SetParentID(oidtest.ID()) x.SetChildren(oidtest.ID(), oidtest.ID()) - x.SetAttributes(Attribute(), Attribute()) + as := make([]object.Attribute, 1+rand.Int()%4) + for i := range as { + si := strconv.Itoa(i) + as[i].SetKey("key_" + si) + as[i].SetValue("val_" + si) + } + x.SetAttributes(as...) splitID := SplitID() x.SetSplitID(&splitID) x.SetPayloadChecksum(checksumtest.Checksum()) From 5110db2436794c8eddfc053230e5fab1707ef919 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Tue, 29 Oct 2024 15:20:09 +0300 Subject: [PATCH 5/9] object: Unify ID-related methods With 340c3690cfd1dc91ced08cf655ced8586f375044, container/object ID setter/getter accepts/returns corresponding ID type by value. in a structural sense, the owner ID of `user.ID` type is no different. However, its setter and getter were built over pointer to the value. Now the interface of all identifiers in the object is uniform: - setter accepts ID by value; - getter returns ID by value; - if unset, getter returns zero instance. Signed-off-by: Leonard Lyubich --- client/container_statistic_test.go | 2 +- object/object.go | 22 +++++++++++++++++----- object/object_test.go | 2 +- object/slicer/slicer.go | 14 +++++++------- object/slicer/slicer_test.go | 19 +++++++++---------- object/test/generate.go | 3 +-- pool/example_pool_test.go | 4 ++-- pool/pool_aio_test.go | 12 ++++++------ 8 files changed, 44 insertions(+), 34 deletions(-) diff --git a/client/container_statistic_test.go b/client/container_statistic_test.go index e96ccc9cd..d9e56def4 100644 --- a/client/container_statistic_test.go +++ b/client/container_statistic_test.go @@ -580,7 +580,7 @@ func TestClientStatistic_ObjectPut(t *testing.T) { prm.WithinSession(tokenSession) var hdr object.Object - hdr.SetOwnerID(&usr.ID) + hdr.SetOwner(usr.ID) hdr.SetContainerID(containerID) writer, err := c.ObjectPutInit(ctx, hdr, usr, prm) diff --git a/object/object.go b/object/object.go index 635205259..d798a2b7c 100644 --- a/object/object.go +++ b/object/object.go @@ -41,7 +41,7 @@ type RequiredFields struct { // InitCreation initializes the object instance with minimum set of required fields. func (o *Object) InitCreation(rf RequiredFields) { o.SetContainerID(rf.Container) - o.SetOwnerID(&rf.Owner) + o.SetOwner(rf.Owner) } // NewFromV2 wraps v2 [object.Object] message to [Object]. @@ -431,10 +431,16 @@ func (o Object) GetContainerID() cid.ID { // OwnerID returns identifier of the object owner. // // See also [Object.SetOwnerID]. -func (o *Object) OwnerID() *user.ID { +// Deprecated: use [Object.Owner] instead. +func (o Object) OwnerID() *user.ID { res := o.Owner(); return &res } + +// Owner returns user ID of the object owner. Zero return means unset ID. +// +// See also [Object.SetOwner]. +func (o Object) Owner() user.ID { var id user.ID - m := (*object.Object)(o).GetHeader().GetOwnerID() + m := (*object.Object)(&o).GetHeader().GetOwnerID() if m != nil { // unlike other IDs, user.ID.ReadFromV2 also expects correct prefix and checksum // suffix. So, we cannot call it and panic on error here because nothing @@ -447,13 +453,19 @@ func (o *Object) OwnerID() *user.ID { copy(id[:], b) } - return &id + return id } // SetOwnerID sets identifier of the object owner. // // See also [Object.OwnerID]. -func (o *Object) SetOwnerID(v *user.ID) { +// Deprecated: use [Object.SetOwner] accepting value instead. +func (o *Object) SetOwnerID(v *user.ID) { o.SetOwner(*v) } + +// SetOwner sets identifier of the object owner. +// +// See also [Object.GetOwner]. +func (o *Object) SetOwner(v user.ID) { o.setHeaderField(func(h *object.Header) { var m refs.OwnerID v.WriteToV2(&m) diff --git a/object/object_test.go b/object/object_test.go index 41e49def6..8cadd99b0 100644 --- a/object/object_test.go +++ b/object/object_test.go @@ -22,7 +22,7 @@ func TestInitCreation(t *testing.T) { cID := o.GetContainerID() require.Equal(t, cnr, cID) - require.Equal(t, &own, o.OwnerID()) + require.Equal(t, own, o.Owner()) } func TestObject_UserAttributes(t *testing.T) { diff --git a/object/slicer/slicer.go b/object/slicer/slicer.go index 1373a7b86..9bac84c2e 100644 --- a/object/slicer/slicer.go +++ b/object/slicer/slicer.go @@ -101,7 +101,7 @@ func New(ctx context.Context, nw NetworkedClient, signer user.Signer, cnr cid.ID var hdr object.Object hdr.SetContainerID(cnr) hdr.SetType(object.TypeRegular) - hdr.SetOwnerID(&owner) + hdr.SetOwner(owner) hdr.SetCreationEpoch(ni.CurrentEpoch()) hdr.SetSessionToken(sessionToken) return &Slicer{ @@ -236,12 +236,12 @@ func headerData(header object.Object) (cid.ID, user.ID, error) { return cid.ID{}, user.ID{}, fmt.Errorf("container-id: %w", ErrIncompleteHeader) } - owner := header.OwnerID() - if owner == nil { + owner := header.Owner() + if owner.IsZero() { return cid.ID{}, user.ID{}, fmt.Errorf("owner: %w", ErrIncompleteHeader) } - return containerID, *owner, nil + return containerID, owner, nil } func initPayloadStream(ctx context.Context, ow ObjectWriter, header object.Object, signer user.Signer, opts Options) (*PayloadWriter, error) { @@ -259,13 +259,13 @@ func initPayloadStream(ctx context.Context, ow ObjectWriter, header object.Objec // session issuer is a container owner. issuer := opts.sessionToken.Issuer() owner = issuer - header.SetOwnerID(&owner) + header.SetOwner(owner) } else if opts.bearerToken != nil { prm.WithBearerToken(*opts.bearerToken) // token issuer is a container owner. issuer := opts.bearerToken.Issuer() owner = issuer - header.SetOwnerID(&owner) + header.SetOwner(owner) } header.SetCreationEpoch(opts.currentNeoFSEpoch) @@ -277,7 +277,7 @@ func initPayloadStream(ctx context.Context, ow ObjectWriter, header object.Objec stubObject.SetContainerID(containerID) stubObject.SetCreationEpoch(opts.currentNeoFSEpoch) stubObject.SetType(object.TypeRegular) - stubObject.SetOwnerID(&owner) + stubObject.SetOwner(owner) stubObject.SetSessionToken(opts.sessionToken) res := &PayloadWriter{ diff --git a/object/slicer/slicer_test.go b/object/slicer/slicer_test.go index 74e98550a..b97d270ae 100644 --- a/object/slicer/slicer_test.go +++ b/object/slicer/slicer_test.go @@ -334,7 +334,7 @@ func testSlicerByHeaderType(t *testing.T, checker *slicedObjectChecker, in input var hdr object.Object hdr.SetSessionToken(opts.Session()) hdr.SetContainerID(in.container) - hdr.SetOwnerID(&in.owner) + hdr.SetOwner(in.owner) hdr.SetAttributes(in.attributes...) rootID, err := slicer.Put(ctx, checker, hdr, checker.input.signer, bytes.NewReader(in.payload), opts) @@ -381,7 +381,7 @@ func testSlicerByHeaderType(t *testing.T, checker *slicedObjectChecker, in input var hdr object.Object hdr.SetSessionToken(opts.Session()) hdr.SetContainerID(in.container) - hdr.SetOwnerID(&in.owner) + hdr.SetOwner(in.owner) hdr.SetAttributes(in.attributes...) // check writer with random written chunk's size @@ -417,7 +417,7 @@ func testSlicerByHeaderType(t *testing.T, checker *slicedObjectChecker, in input var hdr object.Object hdr.SetSessionToken(opts.Session()) hdr.SetContainerID(in.container) - hdr.SetOwnerID(&in.owner) + hdr.SetOwner(in.owner) hdr.SetAttributes(in.attributes...) rootID, err := slicer.Put(ctx, checker, hdr, checker.input.signer, &eofOnLastChunkReader{payload: in.payload, isZeroNilShowed: true}, opts) @@ -431,7 +431,7 @@ func testSlicerByHeaderType(t *testing.T, checker *slicedObjectChecker, in input var hdr object.Object hdr.SetSessionToken(opts.Session()) hdr.SetContainerID(in.container) - hdr.SetOwnerID(&in.owner) + hdr.SetOwner(in.owner) hdr.SetAttributes(in.attributes...) rootID, err := slicer.Put(ctx, checker, hdr, checker.input.signer, &eofOnLastChunkReader{payload: in.payload}, opts) @@ -445,7 +445,7 @@ func testSlicerByHeaderType(t *testing.T, checker *slicedObjectChecker, in input var hdr object.Object hdr.SetSessionToken(opts.Session()) hdr.SetContainerID(in.container) - hdr.SetOwnerID(&in.owner) + hdr.SetOwner(in.owner) hdr.SetAttributes(in.attributes...) rootID, err := slicer.Put(ctx, checker, hdr, checker.input.signer, &eofOnLastChunkReader{payload: in.payload, isZeroOnEOF: true, isZeroNilShowed: true}, opts) @@ -567,12 +567,11 @@ func checkStaticMetadata(tb testing.TB, header object.Object, in input) { require.False(tb, cnr.IsZero(), "all objects must be bound to some container") require.True(tb, cnr == in.container, "the container must be set to the configured one") - owner := header.OwnerID() - require.NotNil(tb, owner, "any object must be owned by somebody") + owner := header.Owner() if in.sessionToken != nil { - require.True(tb, in.sessionToken.Issuer() == *owner, "owner must be set to the session issuer") + require.True(tb, in.sessionToken.Issuer() == owner, "owner must be set to the session issuer") } else { - require.True(tb, *owner == in.owner, "owner must be set to the particular user") + require.True(tb, owner == in.owner, "owner must be set to the particular user") } ver := header.Version() @@ -638,7 +637,7 @@ func (x *chainCollector) handleOutgoingObject(headerOriginal object.Object, payl parentNoPayloadInfo.SetContainerID(cID) parentNoPayloadInfo.SetCreationEpoch(x.parentHeader.CreationEpoch()) parentNoPayloadInfo.SetType(x.parentHeader.Type()) - parentNoPayloadInfo.SetOwnerID(x.parentHeader.OwnerID()) + parentNoPayloadInfo.SetOwner(x.parentHeader.Owner()) parentNoPayloadInfo.SetSessionToken(x.parentHeader.SessionToken()) parentNoPayloadInfo.SetAttributes(x.parentHeader.Attributes()...) diff --git a/object/test/generate.go b/object/test/generate.go index 934b2f8c0..fb5010df9 100644 --- a/object/test/generate.go +++ b/object/test/generate.go @@ -50,8 +50,7 @@ func generate(withParent bool) object.Object { tok := sessiontest.ObjectSigned(usertest.User()) x.SetSessionToken(&tok) x.SetPayload([]byte{1, 2, 3}) - owner := usertest.ID() - x.SetOwnerID(&owner) + x.SetOwner(usertest.ID()) x.SetContainerID(cidtest.ID()) x.SetType(object.TypeTombstone) x.SetVersion(&ver) diff --git a/pool/example_pool_test.go b/pool/example_pool_test.go index 5dac03aa7..768f85355 100644 --- a/pool/example_pool_test.go +++ b/pool/example_pool_test.go @@ -64,7 +64,7 @@ func ExamplePool_ObjectGetInit_explicitAutoSessionDisabling() { var ownerID user.ID var hdr object.Object hdr.SetContainerID(cid.ID{}) - hdr.SetOwnerID(&ownerID) + hdr.SetOwner(ownerID) var containerID cid.ID // fill containerID @@ -108,7 +108,7 @@ func ExamplePool_ObjectPutInit_autoSessionDisabling() { var ownerID user.ID var hdr object.Object hdr.SetContainerID(cid.ID{}) - hdr.SetOwnerID(&ownerID) + hdr.SetOwner(ownerID) // In case of a session wasn't provided with prm.WithinSession, the signer must be for account which is a container // owner, otherwise there will be an error. diff --git a/pool/pool_aio_test.go b/pool/pool_aio_test.go index 66a0d3fd2..c06e5d698 100644 --- a/pool/pool_aio_test.go +++ b/pool/pool_aio_test.go @@ -346,8 +346,8 @@ func testPoolInterfaceWithAIO(t *testing.T, nodeAddr string) { }() require.NoError(t, err) - require.NotNil(t, hdr.OwnerID()) - require.True(t, *hdr.OwnerID() == account) + require.False(t, hdr.Owner().IsZero()) + require.True(t, hdr.Owner() == account) downloadedPayload := make([]byte, len(payload)) @@ -503,8 +503,8 @@ func testPoolWaiterWithAIO(t *testing.T, nodeAddr string) { }() require.NoError(t, err) - require.NotNil(t, hdr.OwnerID()) - require.True(t, *hdr.OwnerID() == account) + require.False(t, hdr.Owner().IsZero()) + require.True(t, hdr.Owner() == account) downloadedPayload := make([]byte, len(payload)) @@ -659,8 +659,8 @@ func testClientWaiterWithAIO(t *testing.T, nodeAddr string) { }() require.NoError(t, err) - require.NotNil(t, hdr.OwnerID()) - require.True(t, *hdr.OwnerID() == account) + require.False(t, hdr.Owner().IsZero()) + require.True(t, hdr.Owner() == account) downloadedPayload := make([]byte, len(payload)) From 0c33fbcb2258bad6ebc2d0833b0b13c0b3879350 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Tue, 29 Oct 2024 15:21:43 +0300 Subject: [PATCH 6/9] object/slicer: Fix missing object owner in test input The owner is mandatory and must never be zero. Signed-off-by: Leonard Lyubich --- object/slicer/slicer_test.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/object/slicer/slicer_test.go b/object/slicer/slicer_test.go index b97d270ae..b58dfe7f7 100644 --- a/object/slicer/slicer_test.go +++ b/object/slicer/slicer_test.go @@ -233,14 +233,13 @@ func randomInput(size, sizeLimit uint64) (input, slicer.Options) { } in.payload = randomData(size) in.attributes = attrs + in.owner = usertest.ID() var opts slicer.Options if rand.Int()%2 == 0 { tok := sessiontest.ObjectSigned(usertest.User()) in.sessionToken = &tok opts.SetSession(*in.sessionToken) - } else { - in.owner = usertest.ID() } in.withHomo = rand.Int()%2 == 0 From ebb37bc98a3b72cf3ced92dc52b8114ce2fa3e42 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Tue, 29 Oct 2024 16:31:13 +0300 Subject: [PATCH 7/9] object: Fix version return when unset Some kind of regression from ade8822a2f6b082291cbcba3bf3611d4a7cf7e73: `Version` method started to return pointer to zero instance instead of nil. Since the absence of a field and an explicitly set zero are different cases, the behavior was initially more correct, so this returns it. Signed-off-by: Leonard Lyubich --- object/object.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/object/object.go b/object/object.go index d798a2b7c..e44b70254 100644 --- a/object/object.go +++ b/object/object.go @@ -350,15 +350,17 @@ func (o *Object) SetPayload(v []byte) { (*object.Object)(o).SetPayload(v) } -// Version returns version of the object. +// Version returns version of the object. Returns nil if the version is unset. // // See also [Object.SetVersion]. func (o Object) Version() *version.Version { + verV2 := (*object.Object)(&o).GetHeader().GetVersion() + if verV2 == nil { + return nil + } var ver version.Version - if verV2 := (*object.Object)(&o).GetHeader().GetVersion(); verV2 != nil { - if err := ver.ReadFromV2(*verV2); err != nil { - return nil - } + if err := ver.ReadFromV2(*verV2); err != nil { + return nil } return &ver } From 0422113c4d56aa967df2651ed70c8a13ae4bdbbf Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Tue, 29 Oct 2024 18:15:44 +0300 Subject: [PATCH 8/9] object: Inline redundant unexported methods of `Object` Signed-off-by: Leonard Lyubich --- object/object.go | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/object/object.go b/object/object.go index e44b70254..5e1e39893 100644 --- a/object/object.go +++ b/object/object.go @@ -827,18 +827,6 @@ func (o *Object) SetParent(v *Object) { }) } -func (o *Object) initRelations() { - o.setHeaderField(func(h *object.Header) { - h.SetSplit(new(object.SplitHeader)) - }) -} - -func (o *Object) resetRelations() { - o.setHeaderField(func(h *object.Header) { - h.SetSplit(nil) - }) -} - // SessionToken returns token of the session within which object was created. // // See also [Object.SetSessionToken]. @@ -910,12 +898,16 @@ func (o Object) HasParent() bool { // ResetRelations removes all fields of links with other objects. func (o *Object) ResetRelations() { - o.resetRelations() + o.setHeaderField(func(h *object.Header) { + h.SetSplit(nil) + }) } // InitRelations initializes relation field. func (o *Object) InitRelations() { - o.initRelations() + o.setHeaderField(func(h *object.Header) { + h.SetSplit(new(object.SplitHeader)) + }) } // Marshal marshals object into a protobuf binary form. From 7d16474b3405dd53153f1e0233c7ab786c610f0d Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Tue, 29 Oct 2024 18:48:11 +0300 Subject: [PATCH 9/9] object: Improve testing and increase coverage for `Object` Signed-off-by: Leonard Lyubich --- object/fmt_test.go | 396 +++++- object/lock_test.go | 2 +- object/object_test.go | 2634 +++++++++++++++++++++++++++++++++++++- object/search_test.go | 12 +- object/tombstone_test.go | 4 +- object/util_test.go | 66 +- 6 files changed, 3018 insertions(+), 96 deletions(-) diff --git a/object/fmt_test.go b/object/fmt_test.go index 5f9398774..8c7141991 100644 --- a/object/fmt_test.go +++ b/object/fmt_test.go @@ -1,82 +1,372 @@ -package object +package object_test import ( - "crypto/rand" + "bytes" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/sha256" + "crypto/sha512" + "encoding/base64" + "encoding/hex" + "math/big" "testing" - cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test" + "github.com/nspcc-dev/neo-go/pkg/io" + "github.com/nspcc-dev/neofs-sdk-go/checksum" + neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto" + neofsecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa" neofscryptotest "github.com/nspcc-dev/neofs-sdk-go/crypto/test" - usertest "github.com/nspcc-dev/neofs-sdk-go/user/test" + "github.com/nspcc-dev/neofs-sdk-go/object" + oid "github.com/nspcc-dev/neofs-sdk-go/object/id" + oidtest "github.com/nspcc-dev/neofs-sdk-go/object/id/test" + "github.com/nspcc-dev/neofs-sdk-go/version" "github.com/stretchr/testify/require" ) -func TestVerificationFields(t *testing.T) { - obj := New() +// corresponds to validObject. +var validSignedObject = []byte{10, 32, 178, 74, 58, 219, 46, 3, 110, 125, 220, 81, 238, 35, 27, 6, 228, 193, 190, 224, 77, 44, + 18, 56, 117, 173, 70, 246, 8, 139, 247, 174, 53, 60} - payload := make([]byte, 10) - _, _ = rand.Read(payload) +func TestObject_CalculateAndSetPayloadChecksum(t *testing.T) { + var obj object.Object + _, ok := obj.PayloadChecksum() + require.False(t, ok) - obj.SetPayload(payload) - obj.SetPayloadSize(uint64(len(payload))) + obj.CalculateAndSetPayloadChecksum() + cs, ok := obj.PayloadChecksum() + require.True(t, ok) + require.Equal(t, checksum.SHA256, cs.Type()) + require.Equal(t, emptySHA256Hash[:], cs.Value()) + require.NoError(t, obj.VerifyPayloadChecksum()) - require.NoError(t, obj.SetVerificationFields(neofscryptotest.Signer())) + obj.SetPayload(anyValidRegularPayload) + obj.CalculateAndSetPayloadChecksum() + cs, ok = obj.PayloadChecksum() + require.True(t, ok) + require.Equal(t, checksum.SHA256, cs.Type()) + require.Equal(t, anyValidPayloadChecksum[:], cs.Value()) + require.NoError(t, obj.VerifyPayloadChecksum()) +} - require.NoError(t, obj.CheckVerificationFields()) +func TestObject_VerifyPayloadChecksum(t *testing.T) { + var obj object.Object + require.EqualError(t, obj.VerifyPayloadChecksum(), "payload checksum is not set") + + obj.SetPayload(anyValidRegularPayload) + obj.SetPayloadChecksum(checksum.NewSHA256(anyValidPayloadChecksum)) + require.NoError(t, obj.VerifyPayloadChecksum()) + + // mutate payload + for i := range anyValidRegularPayload { + b := bytes.Clone(anyValidRegularPayload) + b[i]++ + obj.SetPayload(b) + require.EqualError(t, obj.VerifyPayloadChecksum(), "payload checksum mismatch") + } + + obj.SetPayload(anyValidRegularPayload) + + // mutate checksum + for i := range anyValidPayloadChecksum { + b := bytes.Clone(anyValidPayloadChecksum[:]) + b[i]++ + obj.SetPayloadChecksum(checksum.NewSHA256([sha256.Size]byte(b))) + require.EqualError(t, obj.VerifyPayloadChecksum(), "payload checksum mismatch") + } +} + +func TestObject_CalculateAndSetID(t *testing.T) { + var obj object.Object + id, err := obj.CalculateID() + require.NoError(t, err) + require.EqualValues(t, emptySHA256Hash, id) - items := []struct { - corrupt func() - restore func() + require.NoError(t, obj.CalculateAndSetID()) + require.EqualValues(t, emptySHA256Hash, obj.GetID()) + require.NoError(t, obj.VerifyID()) + + id, err = validObject.CalculateID() + require.NoError(t, err) + require.Equal(t, validObjectID, id) + + // any header field affects ID which is essentially a checksum. Therefore, we + // set each field individually - it must be taken into account when calculating. + for _, tc := range []struct { + name string + id oid.ID + setHdr func(*object.Object) }{ - { - corrupt: func() { - payload[0]++ - }, - restore: func() { - payload[0]-- - }, - }, - { - corrupt: func() { - obj.SetPayloadSize(obj.PayloadSize() + 1) - }, - restore: func() { - obj.SetPayloadSize(obj.PayloadSize() - 1) - }, - }, - { - corrupt: func() { - obj.ToV2().GetObjectID().GetValue()[0]++ - }, - restore: func() { - obj.ToV2().GetObjectID().GetValue()[0]-- - }, - }, + {name: "version", id: oid.ID{149, 211, 24, 216, 96, 10, 95, 80, 221, 0, 122, 130, 195, 255, 143, 90, 65, 138, 234, 225, + 187, 5, 124, 176, 203, 167, 171, 64, 28, 105, 36, 19}, setHdr: func(obj *object.Object) { + v := version.New(1817223862, 2425735897) + obj.SetVersion(&v) + }}, + {name: "container", id: oid.ID{189, 201, 250, 161, 127, 136, 1, 171, 38, 55, 162, 94, 244, 236, 25, 194, 249, 76, 108, 157, + 106, 207, 230, 103, 205, 172, 108, 95, 245, 216, 87, 233}, setHdr: func(obj *object.Object) { + obj.SetContainerID(anyValidContainers[0]) + }}, + {name: "owner", id: oid.ID{121, 153, 185, 254, 28, 165, 125, 149, 195, 153, 23, 170, 202, 220, 131, 60, 228, 232, 183, + 213, 78, 101, 149, 77, 60, 167, 17, 217, 224, 176, 209, 117}, setHdr: func(obj *object.Object) { + obj.SetOwner(anyValidUsers[0]) + }}, + {name: "creation epoch", id: oid.ID{233, 231, 109, 191, 11, 224, 100, 8, 141, 85, 178, 69, 64, 34, 122, 227, 68, 39, 55, + 146, 77, 191, 134, 0, 208, 181, 136, 128, 253, 69, 244, 209}, setHdr: func(obj *object.Object) { + obj.SetCreationEpoch(11023562854130131584) + }}, + {name: "payload size", id: oid.ID{43, 209, 94, 92, 255, 43, 251, 30, 12, 184, 202, 9, 81, 120, 168, 105, 118, 131, 49, + 205, 145, 52, 122, 54, 223, 189, 165, 14, 156, 143, 206, 42}, setHdr: func(obj *object.Object) { + obj.SetPayloadSize(14301110394027098694) + }}, + {name: "payload checksum", id: oid.ID{222, 91, 43, 92, 236, 51, 230, 199, 141, 85, 161, 148, 4, 137, 203, 159, 207, 146, + 34, 106, 193, 97, 113, 133, 170, 215, 207, 130, 160, 197, 119, 41}, setHdr: func(obj *object.Object) { + obj.SetPayloadChecksum(checksum.NewSHA256(anyValidPayloadChecksum)) + }}, + {name: "type", id: oid.ID{88, 19, 117, 205, 15, 152, 61, 10, 161, 139, 239, 17, 173, 95, 115, 67, 244, 161, 72, 65, 192, 31, + 214, 201, 193, 121, 18, 157, 137, 131, 232, 101}, setHdr: func(obj *object.Object) { + obj.SetType(273597346) + }}, + {name: "homomorphic checksum", id: oid.ID{169, 103, 36, 160, 165, 242, 215, 67, 43, 37, 115, 178, 199, 253, 211, 68, 204, + 76, 225, 188, 194, 180, 249, 109, 92, 82, 173, 253, 9, 131, 86, 55}, setHdr: func(obj *object.Object) { + cs, err := checksum.NewFromData(checksum.TillichZemor, anyValidRegularPayload) + require.NoError(t, err) + obj.SetPayloadHomomorphicHash(cs) + }}, + {name: "session token", id: oid.ID{198, 180, 102, 30, 21, 150, 211, 56, 197, 91, 91, 223, 10, 18, 156, 171, 238, 183, 219, + 184, 181, 198, 152, 220, 242, 212, 20, 196, 32, 183, 246, 91}, setHdr: func(obj *object.Object) { + obj.SetSessionToken(&anyValidObjectToken) + }}, + {name: "attributes", id: oid.ID{111, 113, 39, 89, 222, 193, 249, 95, 9, 92, 207, 177, 208, 184, 181, 55, 122, 93, 42, + 237, 171, 27, 5, 85, 61, 78, 14, 57, 139, 11, 0, 113}, setHdr: func(obj *object.Object) { + obj.SetAttributes(*object.NewAttribute("k1", "v1"), *object.NewAttribute("k2", "v2")) + }}, + {name: "parent ID", id: oid.ID{251, 8, 196, 121, 53, 217, 66, 125, 74, 220, 6, 136, 236, 147, 196, 32, 129, 176, 8, 252, + 205, 111, 44, 93, 229, 164, 15, 195, 239, 148, 174, 17}, setHdr: func(obj *object.Object) { + obj.SetParentID(anyValidIDs[0]) + }}, + {name: "previous ID", id: oid.ID{41, 248, 197, 11, 1, 21, 230, 178, 41, 223, 168, 32, 126, 251, 218, 203, 247, 20, 170, + 115, 225, 81, 196, 171, 228, 226, 110, 237, 114, 117, 86, 8}, setHdr: func(obj *object.Object) { + obj.SetPreviousID(anyValidIDs[0]) + }}, + {name: "parent signature", id: oid.ID{130, 200, 147, 200, 38, 182, 90, 133, 14, 221, 74, 69, 94, 207, 16, 27, 34, 26, + 215, 171, 97, 13, 103, 237, 19, 30, 107, 199, 83, 24, 190, 211}, setHdr: func(obj *object.Object) { + var par object.Object + par.SetSignature(&anyValidSignatures[0]) + obj.SetParent(&par) + }}, + {name: "parent header", id: oid.ID{51, 4, 113, 101, 21, 249, 152, 60, 58, 17, 23, 29, 114, 211, 60, 101, 150, 13, 1, 159, + 134, 91, 182, 175, 70, 115, 15, 139, 189, 99, 120, 130}, setHdr: func(obj *object.Object) { + var par object.Object + par.SetContainerID(anyValidContainers[0]) + par.SetOwner(anyValidUsers[0]) + obj.SetParent(&par) + }}, + {name: "children", id: oid.ID{101, 182, 100, 255, 18, 42, 237, 120, 194, 67, 161, 195, 147, 203, 57, 106, 232, 132, 127, + 151, 1, 232, 4, 231, 71, 112, 124, 55, 97, 224, 119, 11}, setHdr: func(obj *object.Object) { + obj.SetChildren(anyValidIDs[0], anyValidIDs[1]) + }}, + {name: "split ID", id: oid.ID{229, 194, 131, 2, 215, 171, 31, 69, 103, 30, 215, 27, 18, 139, 167, 113, 183, 37, 24, 48, 17, + 209, 49, 62, 179, 240, 146, 244, 21, 132, 61, 102}, setHdr: func(obj *object.Object) { + obj.SetSplitID(anyValidSplitID) + }}, + {name: "first ID", id: oid.ID{55, 115, 112, 34, 102, 135, 159, 161, 3, 119, 234, 68, 54, 181, 49, 40, 151, 106, 147, 223, 35, + 60, 204, 90, 255, 113, 199, 189, 11, 120, 132, 75}, setHdr: func(obj *object.Object) { + obj.SetFirstID(anyValidIDs[0]) + }}, + } { + t.Run(tc.name, func(t *testing.T) { + var obj object.Object + tc.setHdr(&obj) + + id, err := obj.CalculateID() + require.NoError(t, err) + require.Equal(t, tc.id, id) + + require.NoError(t, obj.CalculateAndSetID()) + require.Equal(t, tc.id, obj.GetID()) + require.NoError(t, obj.VerifyID()) + }) } +} - for _, item := range items { - item.corrupt() +func TestObject_VerifyID(t *testing.T) { + var obj object.Object + require.ErrorIs(t, obj.VerifyID(), oid.ErrZero) - require.Error(t, obj.CheckVerificationFields()) + validObject.CopyTo(&obj) - item.restore() + id, err := obj.CalculateID() + require.NoError(t, err) + obj.SetID(id) + require.NoError(t, obj.VerifyID()) + + obj.SetID(oidtest.OtherID(id)) + require.EqualError(t, obj.VerifyID(), "incorrect object identifier") +} - require.NoError(t, obj.CheckVerificationFields()) +func TestContainer_Sign(t *testing.T) { + t.Run("failure", func(t *testing.T) { + anySigner := neofscryptotest.Signer() + require.ErrorIs(t, new(object.Object).Sign(anySigner), oid.ErrZero) + + var objWithID object.Object + objWithID.SetID(oidtest.ID()) + require.Error(t, objWithID.Sign(neofscryptotest.FailSigner(anySigner))) + }) + + ecdsaPriv := ecdsa.PrivateKey{ + PublicKey: ecdsa.PublicKey{Curve: elliptic.P256(), + X: new(big.Int).SetBytes([]byte{244, 235, 150, 254, 16, 223, 121, 92, 82, 95, 93, 0, 218, 75, 97, + 182, 224, 29, 29, 126, 136, 127, 95, 227, 148, 120, 101, 174, 116, 191, 113, 56}), + Y: new(big.Int).SetBytes([]byte{162, 142, 254, 167, 43, 228, 23, 134, 112, 148, 125, 252, 40, 205, + 120, 74, 50, 155, 194, 180, 37, 229, 18, 105, 143, 250, 110, 254, 3, 20, 159, 152}), + }, + D: new(big.Int).SetBytes([]byte{37, 38, 152, 197, 254, 145, 122, 170, 199, 181, 85, 225, 135, 215, + 58, 94, 65, 111, 216, 11, 91, 240, 13, 191, 233, 192, 59, 95, 242, 32, 142, 145}), + } + + var obj object.Object + validObject.CopyTo(&obj) + + /* non-deterministic schemes */ + assertECDSACommon := func(signer neofscrypto.Signer) []byte { + require.NoError(t, obj.Sign(signer)) + sig := obj.Signature() + require.NotNil(t, sig) + require.Equal(t, signer.Scheme(), sig.Scheme()) + x, y := elliptic.UnmarshalCompressed(elliptic.P256(), sig.PublicKeyBytes()) + require.NotNil(t, x) + require.Equal(t, ecdsaPriv.X, x) + require.Equal(t, ecdsaPriv.Y, y) + return sig.Value() } + + // SHA-512 + sig := assertECDSACommon(neofsecdsa.Signer(ecdsaPriv)) + require.Len(t, sig, 65) + require.EqualValues(t, 0x4, sig[0]) + h512 := sha512.Sum512(validSignedObject) + r, s := new(big.Int).SetBytes(sig[1:33]), new(big.Int).SetBytes(sig[33:]) + require.True(t, ecdsa.Verify(&ecdsaPriv.PublicKey, h512[:], r, s)) + + // WalletConnect + sig = assertECDSACommon(neofsecdsa.SignerWalletConnect(ecdsaPriv)) + require.Len(t, sig, 80) + b64 := make([]byte, base64.StdEncoding.EncodedLen(len(validSignedObject))) + base64.StdEncoding.Encode(b64, validSignedObject) + payloadLen := 2*16 + len(b64) + b := make([]byte, 4+io.GetVarSize(payloadLen)+payloadLen+2) + n := copy(b, []byte{0x01, 0x00, 0x01, 0xf0}) + n += io.PutVarUint(b[n:], uint64(payloadLen)) + n += hex.Encode(b[n:], sig[64:]) + n += copy(b[n:], b64) + copy(b[n:], []byte{0x00, 0x00}) + h256 := sha256.Sum256(b) + r, s = new(big.Int).SetBytes(sig[:32]), new(big.Int).SetBytes(sig[32:][:32]) + require.True(t, ecdsa.Verify(&ecdsaPriv.PublicKey, h256[:], r, s)) + + /* deterministic schemes */ + // deterministic ECDSA with SHA-256 hashing (RFC 6979) + sig = assertECDSACommon(neofsecdsa.SignerRFC6979(ecdsaPriv)) + require.Equal(t, []byte{ + 117, 254, 164, 56, 148, 113, 8, 171, 216, 251, 102, 211, 37, 52, 181, 63, 206, 226, 22, 24, 36, 90, 249, 59, 247, 3, 213, + 46, 70, 16, 128, 211, 34, 165, 212, 22, 129, 61, 103, 36, 0, 132, 171, 27, 209, 184, 243, 123, 105, 96, 152, 47, 12, 93, + 33, 155, 177, 252, 161, 219, 83, 95, 102, 213, + }, sig) + h256 = sha256.Sum256(validSignedObject) + r, s = new(big.Int).SetBytes(sig[:32]), new(big.Int).SetBytes(sig[32:][:32]) + require.True(t, ecdsa.Verify(&ecdsaPriv.PublicKey, h256[:], r, s)) } func TestObject_SignedData(t *testing.T) { - signer := neofscryptotest.Signer() - uid := usertest.ID() + require.Equal(t, validSignedObject, validObject.SignedData()) +} - rf := RequiredFields{ - Container: cidtest.ID(), - Owner: uid, +func TestObject_VerifySignature(t *testing.T) { + // keys used for this test + // ecdsa.PrivateKey{ + // PublicKey: ecdsa.PublicKey{Curve: elliptic.P256(), + // X: new(big.Int).SetBytes([]byte{34, 204, 96, 183, 108, 209, 95, 61, 67, 216, 229, 8, 26, 112, 174, 164, 239, + // 94, 128, 115, 198, 56, 227, 185, 129, 205, 101, 244, 163, 157, 172, 116}), + // Y: new(big.Int).SetBytes([]byte{187, 225, 7, 20, 148, 140, 234, 98, 202, 109, 145, 126, 126, 62, 188, 15, 56, + // 195, 237, 150, 247, 93, 101, 231, 140, 240, 19, 72, 16, 99, 6, 99}), + // }, + // D: new(big.Int).SetBytes([]byte{125, 34, 9, 148, 39, 247, 116, 124, 27, 11, 166, 201, 232, 182, 153, 32, 117, 126, + // 24, 47, 85, 107, 215, 199, 26, 166, 96, 87, 234, 110, 151, 114}), + // } + pub := []byte{3, 34, 204, 96, 183, 108, 209, 95, 61, 67, 216, 229, 8, 26, 112, 174, 164, 239, 94, 128, 115, + 198, 56, 227, 185, 129, 205, 101, 244, 163, 157, 172, 116} + + var obj object.Object + require.False(t, obj.VerifySignature()) + obj.SetSignature(new(neofscrypto.Signature)) + require.False(t, obj.VerifySignature()) + obj.SetID(validObjectID) + require.False(t, obj.VerifySignature()) + + var sig neofscrypto.Signature + for i, tc := range []struct { + scheme neofscrypto.Scheme + sig []byte // of validObject + }{ + {scheme: neofscrypto.ECDSA_SHA512, sig: []byte{4, 206, 167, 138, 255, 141, 241, 206, 240, 218, 17, 41, 57, 180, 37, 170, + 36, 201, 199, 112, 111, 2, 1, 73, 31, 206, 255, 101, 116, 114, 232, 98, 140, 138, 197, 59, 179, 102, 49, 137, 252, 230, + 176, 155, 116, 39, 203, 232, 51, 238, 172, 112, 42, 227, 113, 203, 177, 53, 101, 116, 97, 29, 121, 22, 183}}, + {scheme: neofscrypto.ECDSA_DETERMINISTIC_SHA256, sig: []byte{47, 239, 122, 255, 209, 184, 122, 77, 65, 84, 185, 0, + 73, 8, 244, 138, 253, 226, 200, 127, 47, 220, 119, 224, 57, 96, 95, 156, 168, 84, 237, 156, 67, 138, 42, 138, 47, 52, + 165, 244, 234, 135, 8, 196, 55, 190, 51, 55, 38, 229, 192, 68, 143, 161, 236, 27, 179, 68, 160, 12, 200, 176, 10, 245}}, + {scheme: neofscrypto.ECDSA_WALLETCONNECT, sig: []byte{7, 96, 184, 28, 216, 37, 138, 11, 78, 152, 47, 148, 183, 72, 211, + 189, 38, 204, 64, 171, 102, 52, 129, 180, 113, 228, 91, 29, 97, 63, 5, 73, 184, 219, 193, 178, 174, 40, 56, 232, 143, + 4, 52, 66, 130, 124, 232, 106, 120, 14, 7, 29, 244, 145, 10, 253, 209, 50, 113, 252, 134, 180, 49, 175, 183, 21, 69, + 4, 29, 124, 74, 160, 152, 155, 249, 216, 146, 80, 125, 194}}, + } { + sig.SetScheme(tc.scheme) + sig.SetPublicKeyBytes(pub) + sig.SetValue(tc.sig) + obj.SetSignature(&sig) + require.True(t, obj.VerifySignature(), i) + // corrupt public key + for k := range pub { + pubCp := bytes.Clone(pub) + pubCp[k]++ + sig.SetPublicKeyBytes(pubCp) + obj.SetSignature(&sig) + require.False(t, obj.VerifySignature(), i) + } + // corrupt signature + for k := range tc.sig { + sigBytesCp := bytes.Clone(tc.sig) + sigBytesCp[k]++ + sig.SetValue(sigBytesCp) + obj.SetSignature(&sig) + require.False(t, obj.VerifySignature(), i) + } } - var val Object +} - val.InitCreation(rf) +func TestObject_SetIDWithSignature(t *testing.T) { + anySigner := neofscryptotest.Signer() + var obj object.Object + validObject.CopyTo(&obj) - require.NoError(t, val.SetVerificationFields(signer)) + require.Error(t, obj.SetIDWithSignature(neofscryptotest.FailSigner(anySigner))) - neofscryptotest.TestSignedData(t, signer, &val) + require.NoError(t, obj.SetIDWithSignature(anySigner)) + require.Equal(t, validObjectID, obj.GetID()) + require.NoError(t, obj.VerifyID()) + require.True(t, obj.VerifySignature()) +} + +func TestObject_SetVerificationFields(t *testing.T) { + anySigner := neofscryptotest.Signer() + + var obj object.Object + validObject.CopyTo(&obj) + require.Error(t, obj.SetVerificationFields(neofscryptotest.FailSigner(anySigner))) + require.NoError(t, obj.SetVerificationFields(anySigner)) + + require.NoError(t, obj.VerifyID()) + require.NoError(t, obj.VerifyPayloadChecksum()) + require.True(t, obj.VerifySignature()) + require.NoError(t, obj.CheckHeaderVerificationFields()) + require.NoError(t, obj.CheckVerificationFields()) } diff --git a/object/lock_test.go b/object/lock_test.go index b7ddc058c..4b4669607 100644 --- a/object/lock_test.go +++ b/object/lock_test.go @@ -11,7 +11,7 @@ import ( var validLock object.Lock // set by init. func init() { - validLock.WriteMembers(anyValidIDs) + validLock.WriteMembers(anyValidIDs[:3]) } var validBinLock = []byte{ diff --git a/object/object_test.go b/object/object_test.go index 8cadd99b0..11f3e0e12 100644 --- a/object/object_test.go +++ b/object/object_test.go @@ -1,15 +1,301 @@ package object_test import ( - "strconv" + "bytes" + "crypto/elliptic" + "encoding/json" + "math" + "math/big" "testing" + "github.com/google/uuid" + apiobject "github.com/nspcc-dev/neofs-api-go/v2/object" + "github.com/nspcc-dev/neofs-api-go/v2/refs" + apisession "github.com/nspcc-dev/neofs-api-go/v2/session" cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test" + neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto" + neofsecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa" "github.com/nspcc-dev/neofs-sdk-go/object" + oid "github.com/nspcc-dev/neofs-sdk-go/object/id" + oidtest "github.com/nspcc-dev/neofs-sdk-go/object/id/test" + objecttest "github.com/nspcc-dev/neofs-sdk-go/object/test" + "github.com/nspcc-dev/neofs-sdk-go/session" + sessiontest "github.com/nspcc-dev/neofs-sdk-go/session/test" usertest "github.com/nspcc-dev/neofs-sdk-go/user/test" "github.com/stretchr/testify/require" ) +var ( + anySessionIssuerPubKey = &neofsecdsa.PublicKey{ + Curve: elliptic.P256(), + X: new(big.Int).SetBytes([]byte{154, 144, 131, 197, 214, 154, 163, 35, 93, 133, 107, 35, 109, 3, 218, 20, 0, 255, 26, + 192, 33, 236, 0, 240, 183, 253, 76, 187, 136, 215, 164, 217}), + Y: new(big.Int).SetBytes([]byte{94, 32, 107, 98, 243, 3, 170, 187, 6, 229, 38, 125, 17, 208, 247, 106, 3, 209, 115, + 174, 180, 192, 102, 190, 151, 10, 215, 157, 164, 219, 74, 40}), + } + // corresponds to anySessionIssuerPubKey. + anySessionIssuerPubKeyBytes = []byte{2, 154, 144, 131, 197, 214, 154, 163, 35, 93, 133, 107, 35, 109, 3, 218, 20, 0, 255, 26, + 192, 33, 236, 0, 240, 183, 253, 76, 187, 136, 215, 164, 217} + anyValidSessionID = uuid.UUID{118, 23, 219, 249, 117, 70, 64, 33, 157, 229, 102, 253, 142, 52, 17, 144} + anyValidObjectToken session.Object // set by init. +) + +var validObject object.Object // set by init. + +func init() { + anyValidObjectToken.SetID(anyValidSessionID) + anyValidObjectToken.SetIssuer(anyValidUsers[2]) + anyValidObjectToken.SetExp(16429376563136800338) + anyValidObjectToken.SetIat(7956510363313998522) + anyValidObjectToken.SetNbf(17237208928641773338) + anyValidObjectToken.SetAuthKey(anySessionIssuerPubKey) + anyValidObjectToken.ForVerb(1047242055) + anyValidObjectToken.BindContainer(anyValidContainers[2]) + anyValidObjectToken.LimitByObjects(anyValidIDs[8], anyValidIDs[9]) + anyValidObjectToken.AttachSignature(neofscrypto.NewSignatureFromRawKey(1134494890, []byte("session_signer"), []byte("session_signature"))) + + var par object.Object + par.SetID(anyValidIDs[1]) + par.SetSignature(&anyValidSignatures[0]) + par.SetVersion(&anyValidVersions[0]) + par.SetContainerID(anyValidContainers[0]) + par.SetOwner(anyValidUsers[0]) + par.SetCreationEpoch(anyValidCreationEpoch) + par.SetPayloadSize(anyValidPayloadSize) + par.SetPayloadChecksum(anyValidChecksums[0]) + par.SetType(anyValidType) + par.SetPayloadHomomorphicHash(anyValidChecksums[1]) + par.SetAttributes( + *object.NewAttribute("par_attr_key1", "par_attr_val1"), + *object.NewAttribute("__NEOFS__EXPIRATION_EPOCH", "14208497712700580130"), + *object.NewAttribute("par_attr_key2", "par_attr_val2"), + ) + + validObject.SetID(anyValidIDs[0]) + validObject.SetSignature(&anyValidSignatures[1]) + validObject.SetPayload(anyValidRegularPayload) + validObject.SetVersion(&anyValidVersions[1]) + validObject.SetContainerID(anyValidContainers[1]) + validObject.SetOwner(anyValidUsers[1]) + validObject.SetCreationEpoch(anyValidCreationEpoch + 1) + validObject.SetPayloadSize(anyValidPayloadSize + 1) + validObject.SetPayloadChecksum(anyValidChecksums[2]) + validObject.SetType(anyValidType + 1) + validObject.SetPayloadHomomorphicHash(anyValidChecksums[3]) + validObject.SetSessionToken(&anyValidObjectToken) + validObject.SetAttributes( + *object.NewAttribute("attr_key1", "attr_val1"), + *object.NewAttribute("__NEOFS__EXPIRATION_EPOCH", "8516691293958955670"), + *object.NewAttribute("attr_key2", "attr_val2"), + ) + validObject.SetPreviousID(anyValidIDs[2]) + validObject.SetParent(&par) + validObject.SetChildren(anyValidIDs[3], anyValidIDs[4], anyValidIDs[5]) + validObject.SetSplitID(anyValidSplitID) + validObject.SetFirstID(anyValidIDs[6]) +} + +// corresponds to validObject. +var validObjectID = oid.ID{104, 79, 12, 22, 52, 73, 44, 178, 246, 55, 17, 33, 163, 133, 252, 128, 121, 232, 127, 223, 91, 227, + 7, 83, 223, 223, 186, 153, 69, 172, 80, 230} + +// corresponds to validObject. +var validBinObject = []byte{ + 10, 34, 10, 32, 178, 74, 58, 219, 46, 3, 110, 125, 220, 81, 238, 35, 27, 6, 228, 193, 190, 224, 77, 44, 18, 56, 117, 173, 70, + 246, 8, 139, 247, 174, 53, 60, 18, 20, 10, 5, 112, 117, 98, 95, 50, 18, 5, 115, 105, 103, 95, 50, 24, 171, 178, 212, 208, 4, 26, + 151, 8, 10, 11, 8, 209, 134, 217, 250, 1, 16, 202, 208, 129, 82, 18, 34, 10, 32, 217, 213, 19, 152, 91, 248, 2, 180, 17, 177, + 248, 226, 163, 200, 56, 31, 123, 24, 182, 144, 148, 180, 248, 192, 155, 253, 104, 220, 69, 102, 174, 5, 26, 27, 10, 25, 53, + 214, 113, 220, 69, 70, 98, 242, 115, 99, 188, 86, 53, 223, 243, 238, 11, 245, 251, 169, 115, 202, 247, 184, 221, 32, 158, + 188, 250, 184, 255, 160, 255, 210, 183, 1, 40, 189, 238, 172, 200, 143, 221, 203, 248, 76, 50, 17, 8, 193, 243, 161, 60, 18, + 10, 99, 104, 101, 99, 107, 115, 117, 109, 95, 51, 56, 224, 137, 251, 224, 7, 66, 18, 8, 229, 198, 224, 221, 3, 18, 10, 99, 104, + 101, 99, 107, 115, 117, 109, 95, 52, 74, 152, 2, 10, 234, 1, 10, 16, 118, 23, 219, 249, 117, 70, 64, 33, 157, 229, 102, 253, 142, + 52, 17, 144, 18, 27, 10, 25, 53, 248, 195, 15, 196, 254, 124, 23, 169, 198, 208, 15, 219, 229, 62, 150, 151, 159, 221, 73, 224, + 229, 106, 42, 222, 26, 32, 8, 210, 204, 150, 183, 128, 222, 183, 128, 228, 1, 16, 154, 222, 137, 183, 154, 198, 183, 155, + 239, 1, 24, 186, 149, 174, 136, 210, 128, 205, 181, 110, 34, 33, 2, 154, 144, 131, 197, 214, 154, 163, 35, 93, 133, 107, 35, 109, + 3, 218, 20, 0, 255, 26, 192, 33, 236, 0, 240, 183, 253, 76, 187, 136, 215, 164, 217, 42, 116, 8, 199, 202, 174, 243, 3, 18, + 108, 10, 34, 10, 32, 135, 89, 149, 219, 185, 209, 233, 137, 224, 211, 141, 70, 193, 205, 248, 254, 226, 30, 114, 177, 245, 171, + 29, 90, 212, 15, 51, 86, 142, 101, 155, 141, 18, 34, 10, 32, 57, 171, 109, 41, 105, 25, 146, 224, 164, 89, 61, 178, 179, 158, 67, 40, + 32, 154, 122, 174, 117, 221, 138, 168, 135, 149, 238, 61, 68, 58, 34, 189, 18, 34, 10, 32, 110, 233, 102, 232, 136, 68, 233, + 22, 158, 100, 49, 20, 181, 95, 219, 143, 53, 250, 237, 113, 64, 25, 48, 11, 54, 207, 56, 98, 99, 136, 207, 21, 18, 41, 10, 14, + 115, 101, 115, 115, 105, 111, 110, 95, 115, 105, 103, 110, 101, 114, 18, 17, 115, 101, 115, 115, 105, 111, 110, 95, 115, 105, 103, 110, 97, 116, + 117, 114, 101, 24, 170, 137, 252, 156, 4, 82, 22, 10, 9, 97, 116, 116, 114, 95, 107, 101, 121, 49, 18, 9, 97, 116, 116, 114, 95, 118, + 97, 108, 49, 82, 48, 10, 25, 95, 95, 78, 69, 79, 70, 83, 95, 95, 69, 88, 80, 73, 82, 65, 84, 73, 79, 78, 95, 69, 80, 79, 67, 72, + 18, 19, 56, 53, 49, 54, 54, 57, 49, 50, 57, 51, 57, 53, 56, 57, 53, 53, 54, 55, 48, 82, 22, 10, 9, 97, 116, 116, 114, 95, 107, 101, + 121, 50, 18, 9, 97, 116, 116, 114, 95, 118, 97, 108, 50, 90, 135, 4, 10, 34, 10, 32, 229, 77, 63, 235, 2, 9, 165, 123, 116, 123, 47, + 65, 22, 34, 214, 76, 45, 225, 21, 46, 135, 32, 116, 172, 67, 213, 243, 57, 253, 127, 179, 235, 18, 34, 10, 32, 206, 228, 247, + 217, 41, 247, 159, 215, 79, 226, 53, 153, 133, 16, 102, 104, 2, 234, 35, 220, 236, 112, 101, 24, 235, 126, 173, 229, 161, + 202, 197, 242, 26, 20, 10, 5, 112, 117, 98, 95, 49, 18, 5, 115, 105, 103, 95, 49, 24, 184, 132, 246, 224, 4, 34, 132, 2, 10, + 11, 8, 167, 167, 171, 42, 16, 221, 138, 221, 194, 7, 18, 34, 10, 32, 245, 94, 164, 207, 217, 233, 175, 75, 123, 153, 174, 8, 20, + 135, 96, 204, 179, 93, 183, 250, 180, 255, 162, 182, 222, 220, 99, 125, 136, 117, 206, 34, 26, 27, 10, 25, 53, 59, 15, 5, 52, + 131, 255, 198, 8, 98, 41, 184, 229, 237, 140, 215, 52, 129, 211, 214, 90, 145, 237, 137, 153, 32, 157, 188, 250, 184, 255, 160, + 255, 210, 183, 1, 40, 188, 238, 172, 200, 143, 221, 203, 248, 76, 50, 18, 8, 222, 213, 182, 173, 7, 18, 10, 99, 104, 101, 99, + 107, 115, 117, 109, 95, 49, 56, 223, 137, 251, 224, 7, 66, 18, 8, 240, 184, 222, 148, 7, 18, 10, 99, 104, 101, 99, 107, 115, 117, + 109, 95, 50, 82, 30, 10, 13, 112, 97, 114, 95, 97, 116, 116, 114, 95, 107, 101, 121, 49, 18, 13, 112, 97, 114, 95, 97, 116, 116, 114, 95, + 118, 97, 108, 49, 82, 49, 10, 25, 95, 95, 78, 69, 79, 70, 83, 95, 95, 69, 88, 80, 73, 82, 65, 84, 73, 79, 78, 95, 69, 80, 79, 67, + 72, 18, 20, 49, 52, 50, 48, 56, 52, 57, 55, 55, 49, 50, 55, 48, 48, 53, 56, 48, 49, 51, 48, 82, 30, 10, 13, 112, 97, 114, 95, 97, + 116, 116, 114, 95, 107, 101, 121, 50, 18, 13, 112, 97, 114, 95, 97, 116, 116, 114, 95, 118, 97, 108, 50, 42, 34, 10, 32, 173, 160, 45, + 58, 200, 168, 116, 142, 235, 209, 231, 80, 235, 186, 6, 132, 99, 95, 14, 39, 237, 139, 87, 66, 244, 72, 96, 69, 13, 83, 81, 172, + 42, 34, 10, 32, 238, 167, 85, 68, 91, 254, 165, 81, 182, 145, 16, 91, 35, 224, 17, 46, 164, 138, 86, 50, 196, 148, 215, 210, 247, + 29, 44, 153, 203, 20, 137, 169, 42, 34, 10, 32, 226, 165, 123, 249, 146, 166, 187, 202, 244, 12, 156, 43, 207, 204, 40, 230, + 145, 34, 212, 152, 148, 112, 44, 21, 195, 207, 249, 112, 34, 81, 145, 194, 50, 16, 224, 132, 3, 80, 32, 44, 69, 184, 185, 32, + 226, 201, 206, 196, 147, 41, 58, 34, 10, 32, 119, 231, 221, 167, 7, 141, 50, 77, 49, 23, 194, 169, 82, 56, 150, 162, 103, 20, + 124, 174, 16, 64, 169, 172, 79, 238, 242, 146, 87, 88, 5, 147, 34, 13, 72, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 100, 33, +} + +// corresponds to validObject. +var validJSONObject = ` +{ + "objectID": { + "value": "sko62y4Dbn3cUe4jGwbkwb7gTSwSOHWtRvYIi/euNTw=" + }, + "signature": { + "key": "cHViXzI=", + "signature": "c2lnXzI=", + "scheme": 1242896683 + }, + "header": { + "version": { + "major": 525747025, + "minor": 171993162 + }, + "containerID": { + "value": "2dUTmFv4ArQRsfjio8g4H3sYtpCUtPjAm/1o3EVmrgU=" + }, + "ownerID": { + "value": "NdZx3EVGYvJzY7xWNd/z7gv1+6lzyve43Q==" + }, + "creationEpoch": "13233261290750647838", + "payloadLength": "5544264194415343421", + "payloadHash": { + "type": 126384577, + "sum": "Y2hlY2tzdW1fMw==" + }, + "objectType": 2082391264, + "homomorphicHash": { + "type": 1001923429, + "sum": "Y2hlY2tzdW1fNA==" + }, + "sessionToken": { + "body": { + "id": "dhfb+XVGQCGd5Wb9jjQRkA==", + "ownerID": { + "value": "NfjDD8T+fBepxtAP2+U+lpef3Ung5Woq3g==" + }, + "lifetime": { + "exp": "16429376563136800338", + "nbf": "17237208928641773338", + "iat": "7956510363313998522" + }, + "sessionKey": "ApqQg8XWmqMjXYVrI20D2hQA/xrAIewA8Lf9TLuI16TZ", + "object": { + "verb": "VERB_UNSPECIFIED", + "target": { + "container": { + "value": "h1mV27nR6Yng041Gwc34/uIecrH1qx1a1A8zVo5lm40=" + }, + "objects": [ + { + "value": "OattKWkZkuCkWT2ys55DKCCaeq513Yqoh5XuPUQ6Ir0=" + }, + { + "value": "bulm6IhE6RaeZDEUtV/bjzX67XFAGTALNs84YmOIzxU=" + } + ] + } + } + }, + "signature": { + "key": "c2Vzc2lvbl9zaWduZXI=", + "signature": "c2Vzc2lvbl9zaWduYXR1cmU=", + "scheme": 1134494890 + } + }, + "attributes": [ + { + "key": "attr_key1", + "value": "attr_val1" + }, + { + "key": "__NEOFS__EXPIRATION_EPOCH", + "value": "8516691293958955670" + }, + { + "key": "attr_key2", + "value": "attr_val2" + } + ], + "split": { + "parent": { + "value": "5U0/6wIJpXt0ey9BFiLWTC3hFS6HIHSsQ9XzOf1/s+s=" + }, + "previous": { + "value": "zuT32Sn3n9dP4jWZhRBmaALqI9zscGUY636t5aHKxfI=" + }, + "parentSignature": { + "key": "cHViXzE=", + "signature": "c2lnXzE=", + "scheme": 1277002296 + }, + "parentHeader": { + "version": { + "major": 88789927, + "minor": 2018985309 + }, + "containerID": { + "value": "9V6kz9npr0t7ma4IFIdgzLNdt/q0/6K23txjfYh1ziI=" + }, + "ownerID": { + "value": "NTsPBTSD/8YIYim45e2M1zSB09Zake2JmQ==" + }, + "creationEpoch": "13233261290750647837", + "payloadLength": "5544264194415343420", + "payloadHash": { + "type": 1974315742, + "sum": "Y2hlY2tzdW1fMQ==" + }, + "objectType": 2082391263, + "homomorphicHash": { + "type": 1922538608, + "sum": "Y2hlY2tzdW1fMg==" + }, + "sessionToken": null, + "attributes": [ + { + "key": "par_attr_key1", + "value": "par_attr_val1" + }, + { + "key": "__NEOFS__EXPIRATION_EPOCH", + "value": "14208497712700580130" + }, + { + "key": "par_attr_key2", + "value": "par_attr_val2" + } + ], + "split": null + }, + "children": [ + { + "value": "raAtOsiodI7r0edQ67oGhGNfDifti1dC9EhgRQ1TUaw=" + }, + { + "value": "7qdVRFv+pVG2kRBbI+ARLqSKVjLElNfS9x0smcsUiak=" + }, + { + "value": "4qV7+ZKmu8r0DJwrz8wo5pEi1JiUcCwVw8/5cCJRkcI=" + } + ], + "splitID": "4IQDUCAsRbi5IOLJzsSTKQ==", + "first": { + "value": "d+fdpweNMk0xF8KpUjiWomcUfK4QQKmsT+7ykldYBZM=" + } + } + }, + "payload": "SGVsbG8sIHdvcmxkIQ==" +} +` + func TestInitCreation(t *testing.T) { var o object.Object cnr := cidtest.ID() @@ -25,43 +311,2333 @@ func TestInitCreation(t *testing.T) { require.Equal(t, own, o.Owner()) } -func TestObject_UserAttributes(t *testing.T) { +func TestObject_SetID(t *testing.T) { var obj object.Object - var attrs []object.Attribute - mSys := make(map[string]string) - mUsr := make(map[string]string) + require.True(t, obj.GetID().IsZero()) + _, ok := obj.ID() + require.False(t, ok) - for i := range 10 { - si := strconv.Itoa(i) + id1 := oidtest.ID() + obj.SetID(id1) + require.Equal(t, id1, obj.GetID()) + res, ok := obj.ID() + require.True(t, ok) + require.Equal(t, id1, res) - keyUsr := "key" + si - valUsr := "val" + si - keySys := "__NEOFS__" + si - valSys := "sys-val" + si + id2 := oidtest.OtherID(id1) + obj.SetID(id2) + require.Equal(t, id2, obj.GetID()) + res, ok = obj.ID() + require.True(t, ok) + require.Equal(t, id2, res) - mUsr[keyUsr] = valUsr - mSys[keySys] = valSys + // reset + require.False(t, obj.GetID().IsZero()) + obj.ResetID() + require.True(t, obj.GetID().IsZero()) + _, ok = obj.ID() + require.False(t, ok) +} - var aUsr object.Attribute - aUsr.SetKey(keyUsr) - aUsr.SetValue(valUsr) +func TestObject_SetSignature(t *testing.T) { + var obj object.Object + require.Zero(t, obj.Signature()) - var aSys object.Attribute - aSys.SetKey(keySys) - aSys.SetValue(valSys) + obj.SetSignature(&anyValidSignatures[0]) + sig := obj.Signature() + require.NotNil(t, sig) + require.Equal(t, anyValidSignatures[0], *sig) - attrs = append(attrs, aSys, aUsr) - } + obj.SetSignature(&anyValidSignatures[1]) + require.Equal(t, anyValidSignatures[1], *obj.Signature()) +} + +func TestObject_SetPayload(t *testing.T) { + var obj object.Object + require.Zero(t, obj.Payload()) + + obj.SetPayload(anyValidRegularPayload) + require.Equal(t, anyValidRegularPayload, obj.Payload()) + + otherPayload := append(bytes.Clone(anyValidRegularPayload), "_other"...) + obj.SetPayload(otherPayload) + require.Equal(t, otherPayload, obj.Payload()) +} + +func TestObject_SetVersion(t *testing.T) { + var obj object.Object + require.Zero(t, obj.Version()) + + obj.SetVersion(&anyValidVersions[0]) + require.Equal(t, anyValidVersions[0], *obj.Version()) + + obj.SetVersion(&anyValidVersions[1]) + require.Equal(t, anyValidVersions[1], *obj.Version()) +} + +func TestObject_SetPayloadSize(t *testing.T) { + var obj object.Object + require.Zero(t, obj.PayloadSize()) + + obj.SetPayloadSize(anyValidPayloadSize) + require.EqualValues(t, anyValidPayloadSize, obj.PayloadSize()) + + obj.SetPayloadSize(anyValidPayloadSize + 1) + require.EqualValues(t, anyValidPayloadSize+1, obj.PayloadSize()) +} + +func TestObject_SetContainerID(t *testing.T) { + var obj object.Object + require.True(t, obj.GetContainerID().IsZero()) + _, ok := obj.ContainerID() + require.False(t, ok) + + cnr1 := cidtest.ID() + obj.SetContainerID(cnr1) + require.Equal(t, cnr1, obj.GetContainerID()) + res, ok := obj.ContainerID() + require.True(t, ok) + require.Equal(t, cnr1, res) + + cnr2 := cidtest.OtherID(cnr1) + obj.SetContainerID(cnr2) + require.Equal(t, cnr2, obj.GetContainerID()) + res, ok = obj.ContainerID() + require.True(t, ok) + require.Equal(t, cnr2, res) +} + +func TestObject_SetOwner(t *testing.T) { + var obj object.Object + require.True(t, obj.Owner().IsZero()) + + usr1 := usertest.ID() + obj.SetOwner(usr1) + require.Equal(t, usr1, obj.Owner()) + + usr2 := usertest.OtherID(usr1) + obj.SetOwner(usr2) + require.Equal(t, usr2, obj.Owner()) +} + +func TestObject_SetOwnerID(t *testing.T) { + var obj object.Object + require.True(t, obj.OwnerID().IsZero()) + + usr1 := usertest.ID() + obj.SetOwnerID(&usr1) + require.Equal(t, usr1, *obj.OwnerID()) + + usr2 := usertest.OtherID(usr1) + obj.SetOwnerID(&usr2) + require.Equal(t, usr2, *obj.OwnerID()) +} + +func TestObject_SetCreationEpoch(t *testing.T) { + var obj object.Object + require.Zero(t, obj.CreationEpoch()) + + obj.SetCreationEpoch(anyValidCreationEpoch) + require.EqualValues(t, anyValidCreationEpoch, obj.CreationEpoch()) + + obj.SetCreationEpoch(anyValidCreationEpoch + 1) + require.EqualValues(t, anyValidCreationEpoch+1, obj.CreationEpoch()) +} + +func TestObject_SetPayloadChecksum(t *testing.T) { + var obj object.Object + _, ok := obj.PayloadChecksum() + require.False(t, ok) + + obj.SetPayloadChecksum(anyValidChecksums[0]) + res, ok := obj.PayloadChecksum() + require.True(t, ok) + require.Equal(t, anyValidChecksums[0], res) + + obj.SetPayloadChecksum(anyValidChecksums[1]) + res, ok = obj.PayloadChecksum() + require.True(t, ok) + require.Equal(t, anyValidChecksums[1], res) +} + +func TestObject_SetPayloadHomomorphicHash(t *testing.T) { + var obj object.Object + _, ok := obj.PayloadHomomorphicHash() + require.False(t, ok) + + obj.SetPayloadHomomorphicHash(anyValidChecksums[0]) + res, ok := obj.PayloadHomomorphicHash() + require.True(t, ok) + require.Equal(t, anyValidChecksums[0], res) + + obj.SetPayloadHomomorphicHash(anyValidChecksums[1]) + res, ok = obj.PayloadHomomorphicHash() + require.True(t, ok) + require.Equal(t, anyValidChecksums[1], res) +} + +func TestObject_SetAttributes(t *testing.T) { + var obj object.Object + require.Empty(t, obj.Attributes()) + require.Empty(t, obj.UserAttributes()) + + a1 := *object.NewAttribute("k1", "v1") + sa1 := *object.NewAttribute("__NEOFS__sk1", "sv1") + a2 := *object.NewAttribute("k2", "v2") + sa2 := *object.NewAttribute("__NEOFS__sk2", "sv2") + + obj.SetAttributes(a1, sa1, a2, sa2) + require.Equal(t, []object.Attribute{a1, sa1, a2, sa2}, obj.Attributes()) + require.Equal(t, []object.Attribute{a1, a2}, obj.UserAttributes()) +} + +func TestObject_SetPreviousID(t *testing.T) { + var obj object.Object + require.True(t, obj.GetPreviousID().IsZero()) + _, ok := obj.PreviousID() + require.False(t, ok) + + id1 := oidtest.ID() + obj.SetPreviousID(id1) + require.Equal(t, id1, obj.GetPreviousID()) + res, ok := obj.PreviousID() + require.True(t, ok) + require.Equal(t, id1, res) + + id2 := oidtest.OtherID(id1) + obj.SetPreviousID(id2) + require.Equal(t, id2, obj.GetPreviousID()) + res, ok = obj.PreviousID() + require.True(t, ok) + require.Equal(t, id2, res) + + // reset + require.False(t, obj.GetPreviousID().IsZero()) + obj.ResetPreviousID() + require.True(t, obj.GetPreviousID().IsZero()) + _, ok = obj.PreviousID() + require.False(t, ok) +} + +func TestObject_SetChildren(t *testing.T) { + var obj object.Object + require.Empty(t, obj.Children()) + + ids := oidtest.IDs(5) + obj.SetChildren(ids...) + require.Equal(t, ids, obj.Children()) + + idsOther := oidtest.IDs(7) + obj.SetChildren(idsOther...) + require.Equal(t, idsOther, obj.Children()) +} + +func TestObject_SetFirstID(t *testing.T) { + var obj object.Object + require.True(t, obj.GetFirstID().IsZero()) + _, ok := obj.FirstID() + require.False(t, ok) + + id1 := oidtest.ID() + obj.SetFirstID(id1) + require.Equal(t, id1, obj.GetFirstID()) + res, ok := obj.FirstID() + require.True(t, ok) + require.Equal(t, id1, res) + + id2 := oidtest.OtherID(id1) + obj.SetFirstID(id2) + require.Equal(t, id2, obj.GetFirstID()) + res, ok = obj.FirstID() + require.True(t, ok) + require.Equal(t, id2, res) +} + +func TestObject_SetSplitID(t *testing.T) { + var obj object.Object + require.Zero(t, obj.SplitID()) - obj.SetAttributes(attrs...) + id1 := objecttest.SplitID() + obj.SetSplitID(&id1) + require.Equal(t, id1, *obj.SplitID()) - for _, a := range obj.UserAttributes() { - key, val := a.Key(), a.Value() - _, isSys := mSys[key] - require.False(t, isSys, key) - require.Equal(t, mUsr[key], val, key) - delete(mUsr, key) + id2 := objecttest.SplitID() + obj.SetSplitID(&id2) + require.Equal(t, id2, *obj.SplitID()) +} + +func TestObject_SetParentID(t *testing.T) { + var obj object.Object + require.True(t, obj.GetParentID().IsZero()) + _, ok := obj.ParentID() + require.False(t, ok) + + id1 := oidtest.ID() + obj.SetParentID(id1) + require.Equal(t, id1, obj.GetParentID()) + res, ok := obj.ParentID() + require.True(t, ok) + require.Equal(t, id1, res) + + id2 := oidtest.OtherID(id1) + obj.SetParentID(id2) + require.Equal(t, id2, obj.GetParentID()) + res, ok = obj.ParentID() + require.True(t, ok) + require.Equal(t, id2, res) + + // reset + require.False(t, obj.GetParentID().IsZero()) + obj.ResetParentID() + require.True(t, obj.GetParentID().IsZero()) + _, ok = obj.ParentID() + require.False(t, ok) +} + +func TestObject_SetParent(t *testing.T) { + var obj object.Object + require.Nil(t, obj.Parent()) + + par := objecttest.Object() + parHdr := *par.CutPayload() + obj.SetParent(&parHdr) + require.Equal(t, parHdr, *obj.Parent()) + + parOther := objecttest.Object() + parHdrOther := *parOther.CutPayload() + obj.SetParent(&parOther) + require.Equal(t, parHdrOther, *obj.Parent()) +} + +func TestObject_SetSessionToken(t *testing.T) { + var obj object.Object + require.Nil(t, obj.SessionToken()) + + s := sessiontest.ObjectSigned(usertest.User()) + obj.SetSessionToken(&s) + require.Equal(t, s, *obj.SessionToken()) + + sOther := sessiontest.ObjectSigned(usertest.User()) + obj.SetSessionToken(&sOther) + require.Equal(t, sOther, *obj.SessionToken()) +} + +func TestObject_Attributes(t *testing.T) { + var obj object.Object + require.Zero(t, obj.Type()) + + obj.SetType(anyValidType) + require.Equal(t, anyValidType, obj.Type()) + + obj.SetType(anyValidType + 1) + require.Equal(t, anyValidType+1, obj.Type()) +} + +func TestObject_CutPayload(t *testing.T) { + obj := objecttest.Object() + payload := obj.Payload() + + cut := obj.CutPayload() + require.Zero(t, cut.Payload()) + cut.SetPayload(payload) + require.Equal(t, obj, *cut) +} + +func TestObject_HasParent(t *testing.T) { + for i, tc := range []func(*object.Object){ + func(obj *object.Object) { obj.SetParentID(oidtest.ID()) }, + func(obj *object.Object) { obj.SetPreviousID(oidtest.ID()) }, + func(obj *object.Object) { obj.SetChildren(oidtest.IDs(3)...) }, + func(obj *object.Object) { obj.SetSplitID(anyValidSplitID) }, + func(obj *object.Object) { obj.SetFirstID(oidtest.ID()) }, + func(obj *object.Object) { par := objecttest.Object(); obj.SetParent(&par) }, + } { + var obj object.Object + require.False(t, obj.HasParent()) + tc(&obj) + require.True(t, obj.HasParent(), i) } +} + +func assertNoSplitFields(t testing.TB, obj object.Object) { + require.True(t, obj.GetParentID().IsZero()) + require.True(t, obj.GetPreviousID().IsZero()) + require.Zero(t, obj.Parent()) + require.Empty(t, obj.Children()) + require.Zero(t, obj.SplitID()) + require.Zero(t, obj.SplitID()) + require.True(t, obj.GetFirstID().IsZero()) +} + +func TestObject_ResetRelations(t *testing.T) { + var obj object.Object + obj.SetParentID(oidtest.ID()) + obj.SetPreviousID(oidtest.ID()) + par := objecttest.Object() + obj.SetParent(&par) + obj.SetChildren(oidtest.IDs(3)...) + obj.SetSplitID(anyValidSplitID) + obj.SetFirstID(oidtest.ID()) + + require.False(t, obj.GetParentID().IsZero()) + require.False(t, obj.GetPreviousID().IsZero()) + require.NotZero(t, obj.Parent()) + require.NotEmpty(t, obj.Children()) + require.NotZero(t, obj.SplitID()) + require.NotZero(t, obj.SplitID()) + require.False(t, obj.GetFirstID().IsZero()) + + obj.ResetRelations() + assertNoSplitFields(t, obj) +} + +func TestObject_InitRelations(t *testing.T) { + var obj object.Object + assertNoSplitFields(t, obj) + require.False(t, obj.HasParent()) + + obj.InitRelations() + assertNoSplitFields(t, obj) + require.True(t, obj.HasParent()) +} + +func TestObject_ReadFromV2(t *testing.T) { + var pv refs.Version + pv.SetMajor(88789927) + pv.SetMinor(2018985309) + var pcs refs.Checksum + pcs.SetType(1974315742) + pcs.SetSum([]byte("checksum_1")) + var phcs refs.Checksum + phcs.SetType(1922538608) + phcs.SetSum([]byte("checksum_2")) + pas := make([]apiobject.Attribute, 3) + pas[0].SetKey("par_attr_key1") + pas[0].SetValue("par_attr_val1") + pas[1].SetKey("__NEOFS__EXPIRATION_EPOCH") + pas[1].SetValue("14208497712700580130") + pas[2].SetKey("par_attr_key2") + pas[2].SetValue("par_attr_val2") + var ph apiobject.Header + ph.SetVersion(&pv) + ph.SetContainerID(protoContainerIDFromBytes(anyValidContainers[0][:])) + ph.SetOwnerID(protoUserIDFromBytes(anyValidUsers[0][:])) + ph.SetCreationEpoch(anyValidCreationEpoch) + ph.SetPayloadLength(anyValidPayloadSize) + ph.SetPayloadHash(&pcs) + ph.SetObjectType(apiobject.Type(anyValidType)) + ph.SetHomomorphicHash(&phcs) + ph.SetAttributes(pas) + + var mtl apisession.TokenLifetime + mtl.SetExp(16429376563136800338) + mtl.SetIat(7956510363313998522) + mtl.SetNbf(17237208928641773338) + var mtc apisession.ObjectSessionContext + mtc.SetVerb(1047242055) + mtc.SetTarget(protoContainerIDFromBytes(anyValidContainers[2][:]), + *protoIDFromBytes(anyValidIDs[8][:]), *protoIDFromBytes(anyValidIDs[9][:])) + var mtb apisession.TokenBody + mtb.SetID(anyValidSessionID[:]) + mtb.SetOwnerID(protoUserIDFromBytes(anyValidUsers[2][:])) + mtb.SetLifetime(&mtl) + mtb.SetSessionKey(anySessionIssuerPubKeyBytes) + mtb.SetContext(&mtc) + var mts refs.Signature + mts.SetScheme(1134494890) + mts.SetKey([]byte("session_signer")) + mts.SetSign([]byte("session_signature")) + var mt apisession.Token + mt.SetBody(&mtb) + mt.SetSignature(&mts) + + var mv refs.Version + mv.SetMajor(525747025) + mv.SetMinor(171993162) + var cs refs.Checksum + cs.SetType(126384577) + cs.SetSum([]byte("checksum_3")) + var hcs refs.Checksum + hcs.SetType(1001923429) + hcs.SetSum([]byte("checksum_4")) + as := make([]apiobject.Attribute, 3) + as[0].SetKey("attr_key1") + as[0].SetValue("attr_val1") + as[1].SetKey("__NEOFS__EXPIRATION_EPOCH") + as[1].SetValue("8516691293958955670") + as[2].SetKey("attr_key2") + as[2].SetValue("attr_val2") + var ps refs.Signature + ps.SetKey([]byte("pub_1")) + ps.SetSign([]byte("sig_1")) + ps.SetScheme(1277002296) + var sh apiobject.SplitHeader + sh.SetParent(protoIDFromBytes(anyValidIDs[1][:])) + sh.SetPrevious(protoIDFromBytes(anyValidIDs[2][:])) + sh.SetParentSignature(&ps) + sh.SetParentHeader(&ph) + sh.SetChildren([]refs.ObjectID{ + *protoIDFromBytes(anyValidIDs[3][:]), + *protoIDFromBytes(anyValidIDs[4][:]), + *protoIDFromBytes(anyValidIDs[5][:]), + }) + sh.SetSplitID(anyValidSplitIDBytes) + sh.SetFirst(protoIDFromBytes(anyValidIDs[6][:])) + + var mh apiobject.Header + mh.SetVersion(&mv) + mh.SetContainerID(protoContainerIDFromBytes(anyValidContainers[1][:])) + mh.SetOwnerID(protoUserIDFromBytes(anyValidUsers[1][:])) + mh.SetCreationEpoch(anyValidCreationEpoch + 1) + mh.SetPayloadLength(anyValidPayloadSize + 1) + mh.SetPayloadHash(&cs) + mh.SetObjectType(apiobject.Type(anyValidType) + 1) + mh.SetHomomorphicHash(&hcs) + mh.SetSessionToken(&mt) + mh.SetAttributes(as) + mh.SetSplit(&sh) + + var ms refs.Signature + ms.SetKey([]byte("pub_2")) + ms.SetSign([]byte("sig_2")) + ms.SetScheme(1242896683) + + var m apiobject.Object + m.SetObjectID(protoIDFromBytes(anyValidIDs[0][:])) + m.SetSignature(&ms) + m.SetHeader(&mh) + m.SetPayload(anyValidRegularPayload) + + var obj object.Object + require.NoError(t, obj.ReadFromV2(m)) + require.Equal(t, validObject, obj) + + // reset optional fields + m.SetObjectID(nil) + m.SetSignature(nil) + m.SetHeader(nil) + m.SetPayload(nil) + obj2 := obj + require.NoError(t, obj2.ReadFromV2(m)) + require.Zero(t, obj2) + + t.Run("invalid", func(t *testing.T) { + for _, tc := range []struct { + name, err string + corrupt func(*apiobject.Object) + }{ + {name: "id/nil value", err: "invalid ID: invalid length 0", + corrupt: func(m *apiobject.Object) { m.SetObjectID(protoIDFromBytes(nil)) }}, + {name: "id/empty value", err: "invalid ID: invalid length 0", + corrupt: func(m *apiobject.Object) { m.SetObjectID(protoIDFromBytes([]byte{})) }}, + {name: "id/undersize", err: "invalid ID: invalid length 31", + corrupt: func(m *apiobject.Object) { m.SetObjectID(protoIDFromBytes(anyValidIDs[0][:31])) }}, + {name: "id/oversize", err: "invalid ID: invalid length 33", + corrupt: func(m *apiobject.Object) { m.SetObjectID(protoIDFromBytes(append(anyValidIDs[0][:], 1))) }}, + {name: "id/zero", err: "invalid ID: zero object ID", + corrupt: func(m *apiobject.Object) { m.SetObjectID(protoIDFromBytes(make([]byte, 32))) }}, + {name: "signature/scheme/overflow", err: "invalid signature: scheme 2147483648 overflows int32", + corrupt: func(m *apiobject.Object) { + var s refs.Signature + s.SetScheme(math.MaxInt32 + 1) + m.SetSignature(&s) + }}, + {name: "header/owner/value/nil", err: "invalid header: invalid owner: invalid length 0, expected 25", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + h.SetOwnerID(protoUserIDFromBytes(nil)) + m.SetHeader(&h) + }}, + {name: "header/owner/value/empty", err: "invalid header: invalid owner: invalid length 0, expected 25", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + h.SetOwnerID(protoUserIDFromBytes([]byte{})) + m.SetHeader(&h) + }}, + {name: "header/owner/value/undersize", err: "invalid header: invalid owner: invalid length 24, expected 25", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + h.SetOwnerID(protoUserIDFromBytes(anyValidUsers[0][:24])) + m.SetHeader(&h) + }}, + {name: "header/owner/value/oversize", err: "invalid header: invalid owner: invalid length 26, expected 25", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + h.SetOwnerID(protoUserIDFromBytes(append(anyValidUsers[0][:], 1))) + m.SetHeader(&h) + }}, + {name: "header/owner/value/wrong prefix", err: "invalid header: invalid owner: invalid prefix byte 0x42, expected 0x35", + corrupt: func(m *apiobject.Object) { + id := anyValidUsers[0] + id[0] = 0x42 + h := *m.GetHeader() + h.SetOwnerID(protoUserIDFromBytes(id[:])) + m.SetHeader(&h) + }}, + {name: "header/owner/value/checksum mismatch", err: "invalid header: invalid owner: checksum mismatch", + corrupt: func(m *apiobject.Object) { + id := anyValidUsers[0] + id[len(id)-1]++ + h := *m.GetHeader() + h.SetOwnerID(protoUserIDFromBytes(id[:])) + m.SetHeader(&h) + }}, + {name: "header/container/nil value", err: "invalid header: invalid container: invalid length 0", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + h.SetContainerID(protoContainerIDFromBytes(nil)) + m.SetHeader(&h) + }}, + {name: "header/container/empty value", err: "invalid header: invalid container: invalid length 0", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + h.SetContainerID(protoContainerIDFromBytes([]byte{})) + m.SetHeader(&h) + }}, + {name: "header/container/undersize", err: "invalid header: invalid container: invalid length 31", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + h.SetContainerID(protoContainerIDFromBytes(anyValidContainers[0][:31])) + m.SetHeader(&h) + }}, + {name: "header/container/oversize", err: "invalid header: invalid container: invalid length 33", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + h.SetContainerID(protoContainerIDFromBytes(append(anyValidContainers[0][:], 1))) + m.SetHeader(&h) + }}, + {name: "header/container/zero", err: "invalid header: invalid container: zero container ID", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + h.SetContainerID(protoContainerIDFromBytes(make([]byte, 32))) + m.SetHeader(&h) + }}, + {name: "header/payload checksum/missing value", err: "invalid header: invalid payload checksum: missing value", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + h.SetPayloadHash(new(refs.Checksum)) + m.SetHeader(&h) + }}, + {name: "header/payload homomorphic checksum/missing value", err: "invalid header: invalid payload homomorphic checksum: missing value", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + h.SetHomomorphicHash(new(refs.Checksum)) + m.SetHeader(&h) + }}, + {name: "header/session/body/ID/undersize", err: "invalid header: invalid session token: invalid session ID: invalid UUID (got 15 bytes)", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + mt := *h.GetSessionToken() + mtb := *mt.GetBody() + mtb.SetID(anyValidSessionID[:15]) + mt.SetBody(&mtb) + h.SetSessionToken(&mt) + m.SetHeader(&h) + }}, + {name: "header/session/body/ID/oversize", err: "invalid header: invalid session token: invalid session ID: invalid UUID (got 17 bytes)", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + mt := *h.GetSessionToken() + mtb := *mt.GetBody() + mtb.SetID(append(anyValidSessionID[:], 1)) + mt.SetBody(&mtb) + h.SetSessionToken(&mt) + m.SetHeader(&h) + }}, + {name: "header/session/body/ID/wrong UUID version", err: "invalid header: invalid session token: invalid session UUID version 3", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + mt := *h.GetSessionToken() + mtb := *mt.GetBody() + b := bytes.Clone(anyValidSessionID[:]) + b[6] = 3 << 4 + mtb.SetID(b) + mt.SetBody(&mtb) + h.SetSessionToken(&mt) + m.SetHeader(&h) + }}, + {name: "header/session/body/issuer/value/empty", err: "invalid header: invalid session token: invalid session issuer: invalid length 0, expected 25", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + mt := *h.GetSessionToken() + mtb := *mt.GetBody() + mtb.SetOwnerID(protoUserIDFromBytes(nil)) + mt.SetBody(&mtb) + h.SetSessionToken(&mt) + m.SetHeader(&h) + }}, + {name: "header/session/body/issuer/value/undersize", err: "invalid header: invalid session token: invalid session issuer: invalid length 24, expected 25", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + mt := *h.GetSessionToken() + mtb := *mt.GetBody() + mtb.SetOwnerID(protoUserIDFromBytes(anyValidUsers[0][:24])) + mt.SetBody(&mtb) + h.SetSessionToken(&mt) + m.SetHeader(&h) + }}, + {name: "header/session/body/issuer/value/oversize", err: "invalid header: invalid session token: invalid session issuer: invalid length 26, expected 25", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + mt := *h.GetSessionToken() + mtb := *mt.GetBody() + mtb.SetOwnerID(protoUserIDFromBytes(append(anyValidUsers[0][:], 1))) + mt.SetBody(&mtb) + h.SetSessionToken(&mt) + m.SetHeader(&h) + }}, + {name: "header/session/body/issuer/value/wrong prefix", err: "invalid header: invalid session token: invalid session issuer: invalid prefix byte 0x42, expected 0x35", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + mt := *h.GetSessionToken() + mtb := *mt.GetBody() + b := bytes.Clone(anyValidUsers[0][:]) + b[0] = 0x42 + mtb.SetOwnerID(protoUserIDFromBytes(b)) + mt.SetBody(&mtb) + h.SetSessionToken(&mt) + m.SetHeader(&h) + }}, + {name: "header/session/body/issuer/value/checksum mismatch", err: "invalid header: invalid session token: invalid session issuer: checksum mismatch", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + mt := *h.GetSessionToken() + mtb := *mt.GetBody() + b := bytes.Clone(anyValidUsers[0][:]) + b[len(b)-1]++ + mtb.SetOwnerID(protoUserIDFromBytes(b)) + mt.SetBody(&mtb) + h.SetSessionToken(&mt) + m.SetHeader(&h) + }}, + {name: "header/session/body/context/wrong oneof", err: "invalid header: invalid session token: invalid context: invalid context *session.ContainerSessionContext", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + mt := *h.GetSessionToken() + mtb := *mt.GetBody() + mtb.SetContext(new(apisession.ContainerSessionContext)) + mt.SetBody(&mtb) + h.SetSessionToken(&mt) + m.SetHeader(&h) + }}, + {name: "header/session/body/context/container/empty value", err: "invalid header: invalid session token: invalid context: invalid container ID: invalid length 0", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + mt := *h.GetSessionToken() + mtb := *mt.GetBody() + mtc := *mtb.GetContext().(*apisession.ObjectSessionContext) + mtc.SetTarget(protoContainerIDFromBytes(nil), mtc.GetObjects()...) + mtb.SetContext(&mtc) + mt.SetBody(&mtb) + h.SetSessionToken(&mt) + m.SetHeader(&h) + }}, + {name: "header/session/body/context/container/undersize", err: "invalid header: invalid session token: invalid context: invalid container ID: invalid length 31", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + mt := *h.GetSessionToken() + mtb := *mt.GetBody() + mtc := *mtb.GetContext().(*apisession.ObjectSessionContext) + mtc.SetTarget(protoContainerIDFromBytes(anyValidContainers[0][:31]), mtc.GetObjects()...) + mtb.SetContext(&mtc) + mt.SetBody(&mtb) + h.SetSessionToken(&mt) + m.SetHeader(&h) + }}, + {name: "header/session/body/context/container/oversize", err: "invalid header: invalid session token: invalid context: invalid container ID: invalid length 33", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + mt := *h.GetSessionToken() + mtb := *mt.GetBody() + mtc := *mtb.GetContext().(*apisession.ObjectSessionContext) + mtc.SetTarget(protoContainerIDFromBytes(append(anyValidContainers[0][:], 1)), mtc.GetObjects()...) + mtb.SetContext(&mtc) + mt.SetBody(&mtb) + h.SetSessionToken(&mt) + m.SetHeader(&h) + }}, + {name: "header/session/body/context/object/empty value", err: "invalid header: invalid session token: invalid context: invalid target object: invalid length 0", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + mt := *h.GetSessionToken() + mtb := *mt.GetBody() + mtc := *mtb.GetContext().(*apisession.ObjectSessionContext) + objs := make([]refs.ObjectID, 2) + anyValidIDs[0].WriteToV2(&objs[0]) + mtc.SetTarget(protoContainerIDFromBytes(anyValidContainers[0][:]), objs...) + mtb.SetContext(&mtc) + mt.SetBody(&mtb) + h.SetSessionToken(&mt) + m.SetHeader(&h) + }}, + {name: "header/session/body/context/object/undersize", err: "invalid header: invalid session token: invalid context: invalid target object: invalid length 31", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + mt := *h.GetSessionToken() + mtb := *mt.GetBody() + mtc := *mtb.GetContext().(*apisession.ObjectSessionContext) + objs := make([]refs.ObjectID, 2) + anyValidIDs[0].WriteToV2(&objs[0]) + objs[1].SetValue(anyValidIDs[1][:31]) + mtc.SetTarget(protoContainerIDFromBytes(anyValidContainers[0][:]), objs...) + mtb.SetContext(&mtc) + mt.SetBody(&mtb) + h.SetSessionToken(&mt) + m.SetHeader(&h) + }}, + {name: "header/session/body/context/object/oversize", err: "invalid header: invalid session token: invalid context: invalid target object: invalid length 33", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + mt := *h.GetSessionToken() + mtb := *mt.GetBody() + mtc := *mtb.GetContext().(*apisession.ObjectSessionContext) + objs := make([]refs.ObjectID, 2) + anyValidIDs[0].WriteToV2(&objs[0]) + objs[1].SetValue(append(anyValidIDs[1][:], 1)) + mtc.SetTarget(protoContainerIDFromBytes(anyValidContainers[0][:]), objs...) + mtb.SetContext(&mtc) + mt.SetBody(&mtb) + h.SetSessionToken(&mt) + m.SetHeader(&h) + }}, + {name: "header/session/signature/scheme/overflow", err: "invalid header: invalid session token: invalid body signature: scheme 2147483648 overflows int32", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + mt := *h.GetSessionToken() + mts := *mt.GetSignature() + mts.SetScheme(math.MaxInt32 + 1) + mt.SetSignature(&mts) + h.SetSessionToken(&mt) + m.SetHeader(&h) + }}, + {name: "attributes/no key", err: "invalid header: empty key of the attribute #1", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + as := make([]apiobject.Attribute, 3) + as[0].SetKey("k1") + as[0].SetValue("v1") + as[1].SetValue("v2") + as[2].SetKey("k3") + as[2].SetValue("v3") + h.SetAttributes(as) + m.SetHeader(&h) + }}, + {name: "attributes/no value", err: "invalid header: empty value of the attribute #1 (k2)", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + as := make([]apiobject.Attribute, 3) + as[0].SetKey("k1") + as[0].SetValue("v1") + as[1].SetKey("k2") + as[2].SetKey("k3") + as[2].SetValue("v3") + h.SetAttributes(as) + m.SetHeader(&h) + }}, + {name: "attributes/duplicated", err: "invalid header: duplicated attribute k1", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + as := make([]apiobject.Attribute, 3) + as[0].SetKey("k1") + as[0].SetValue("v1") + as[1].SetKey("k2") + as[1].SetValue("v2") + as[2].SetKey("k1") + as[2].SetValue("v3") + h.SetAttributes(as) + m.SetHeader(&h) + }}, + {name: "attributes/expiration", err: "invalid header: invalid expiration attribute (must be a uint): strconv.ParseUint: parsing \"foo\": invalid syntax", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + as := make([]apiobject.Attribute, 3) + as[0].SetKey("k1") + as[0].SetValue("v1") + as[1].SetKey("__NEOFS__EXPIRATION_EPOCH") + as[1].SetValue("foo") + as[2].SetKey("k3") + as[2].SetValue("v3") + h.SetAttributes(as) + m.SetHeader(&h) + }}, + {name: "header/split/parent ID/empty value", err: "invalid header: invalid split header: invalid parent split member ID: invalid length 0", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + sh := *h.GetSplit() + sh.SetParent(protoIDFromBytes(nil)) + h.SetSplit(&sh) + m.SetHeader(&h) + }}, + {name: "header/split/parent ID/undersize", err: "invalid header: invalid split header: invalid parent split member ID: invalid length 31", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + sh := *h.GetSplit() + sh.SetParent(protoIDFromBytes(anyValidIDs[0][:31])) + h.SetSplit(&sh) + m.SetHeader(&h) + }}, + {name: "header/split/parent ID/oversize", err: "invalid header: invalid split header: invalid parent split member ID: invalid length 33", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + sh := *h.GetSplit() + sh.SetParent(protoIDFromBytes(append(anyValidIDs[0][:], 1))) + h.SetSplit(&sh) + m.SetHeader(&h) + }}, + {name: "header/split/parent ID/zero", err: "invalid header: invalid split header: invalid parent split member ID: zero object ID", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + sh := *h.GetSplit() + sh.SetParent(protoIDFromBytes(make([]byte, 32))) + h.SetSplit(&sh) + m.SetHeader(&h) + }}, + {name: "header/split/previous/empty value", err: "invalid header: invalid split header: invalid previous split member ID: invalid length 0", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + sh := *h.GetSplit() + sh.SetPrevious(protoIDFromBytes(nil)) + h.SetSplit(&sh) + m.SetHeader(&h) + }}, + {name: "header/split/previous/undersize", err: "invalid header: invalid split header: invalid previous split member ID: invalid length 31", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + sh := *h.GetSplit() + sh.SetPrevious(protoIDFromBytes(anyValidIDs[0][:31])) + h.SetSplit(&sh) + m.SetHeader(&h) + }}, + {name: "header/split/previous/oversize", err: "invalid header: invalid split header: invalid previous split member ID: invalid length 33", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + sh := *h.GetSplit() + sh.SetPrevious(protoIDFromBytes(append(anyValidIDs[0][:], 1))) + h.SetSplit(&sh) + m.SetHeader(&h) + }}, + {name: "header/split/previous/zero", err: "invalid header: invalid split header: invalid previous split member ID: zero object ID", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + sh := *h.GetSplit() + sh.SetPrevious(protoIDFromBytes(make([]byte, 32))) + h.SetSplit(&sh) + m.SetHeader(&h) + }}, + {name: "header/split/first/empty value", err: "invalid header: invalid split header: invalid first split member ID: invalid length 0", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + sh := *h.GetSplit() + sh.SetFirst(protoIDFromBytes(nil)) + h.SetSplit(&sh) + m.SetHeader(&h) + }}, + {name: "header/split/first/undersize", err: "invalid header: invalid split header: invalid first split member ID: invalid length 31", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + sh := *h.GetSplit() + sh.SetFirst(protoIDFromBytes(anyValidIDs[0][:31])) + h.SetSplit(&sh) + m.SetHeader(&h) + }}, + {name: "header/split/first/oversize", err: "invalid header: invalid split header: invalid first split member ID: invalid length 33", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + sh := *h.GetSplit() + sh.SetFirst(protoIDFromBytes(append(anyValidIDs[0][:], 1))) + h.SetSplit(&sh) + m.SetHeader(&h) + }}, + {name: "header/split/first/zero", err: "invalid header: invalid split header: invalid first split member ID: zero object ID", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + sh := *h.GetSplit() + sh.SetFirst(protoIDFromBytes(make([]byte, 32))) + h.SetSplit(&sh) + m.SetHeader(&h) + }}, + {name: "header/split/ID/undersize", err: "invalid header: invalid split header: invalid split UUID: invalid UUID (got 15 bytes)", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + sh := *h.GetSplit() + sh.SetSplitID(anyValidSplitIDBytes[:15]) + h.SetSplit(&sh) + m.SetHeader(&h) + }}, + {name: "header/split/ID/oversize", err: "invalid header: invalid split header: invalid split UUID: invalid UUID (got 17 bytes)", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + sh := *h.GetSplit() + sh.SetSplitID(append(anyValidSplitIDBytes, 1)) + h.SetSplit(&sh) + m.SetHeader(&h) + }}, + {name: "header/split/ID/wrong UUID version", err: "invalid header: invalid split header: invalid split UUID version 3", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + sh := *h.GetSplit() + b := bytes.Clone(anyValidSplitIDBytes) + b[6] = 3 << 4 + sh.SetSplitID(b) + h.SetSplit(&sh) + m.SetHeader(&h) + }}, + {name: "header/split/children/empty value", err: "invalid header: invalid split header: invalid child split member ID #1: invalid length 0", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + sh := *h.GetSplit() + c := make([]refs.ObjectID, 3) + anyValidIDs[0].WriteToV2(&c[0]) + anyValidIDs[2].WriteToV2(&c[2]) + sh.SetChildren(c) + h.SetSplit(&sh) + m.SetHeader(&h) + }}, + {name: "header/split/children/undersize", err: "invalid header: invalid split header: invalid child split member ID #1: invalid length 31", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + sh := *h.GetSplit() + c := make([]refs.ObjectID, 3) + anyValidIDs[0].WriteToV2(&c[0]) + c[1].SetValue(anyValidIDs[1][:31]) + anyValidIDs[2].WriteToV2(&c[2]) + sh.SetChildren(c) + h.SetSplit(&sh) + m.SetHeader(&h) + }}, + {name: "header/split/children/oversize", err: "invalid header: invalid split header: invalid child split member ID #1: invalid length 33", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + sh := *h.GetSplit() + c := make([]refs.ObjectID, 3) + anyValidIDs[0].WriteToV2(&c[0]) + c[1].SetValue(append(anyValidIDs[1][:], 1)) + anyValidIDs[2].WriteToV2(&c[2]) + sh.SetChildren(c) + h.SetSplit(&sh) + m.SetHeader(&h) + }}, + {name: "header/split/children/zero", err: "invalid header: invalid split header: invalid child split member ID #1: zero object ID", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + sh := *h.GetSplit() + c := make([]refs.ObjectID, 3) + anyValidIDs[0].WriteToV2(&c[0]) + c[1].SetValue(make([]byte, 32)) + anyValidIDs[2].WriteToV2(&c[2]) + sh.SetChildren(c) + h.SetSplit(&sh) + m.SetHeader(&h) + }}, + {name: "header/split/parent signature/scheme/overflow", err: "invalid header: invalid split header: invalid parent signature: scheme 2147483648 overflows int32", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + sh := *h.GetSplit() + var s refs.Signature + anyValidSignatures[0].WriteToV2(&s) + s.SetScheme(math.MaxInt32 + 1) + sh.SetParentSignature(&s) + h.SetSplit(&sh) + m.SetHeader(&h) + }}, + {name: "header/split/parent/owner/value/empty", err: "invalid header: invalid split header: invalid parent header: invalid owner: invalid length 0, expected 25", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + sh := *h.GetSplit() + ph := *sh.GetParentHeader() + ph.SetOwnerID(protoUserIDFromBytes(nil)) + sh.SetParentHeader(&ph) + h.SetSplit(&sh) + m.SetHeader(&h) + }}, + {name: "header/split/parent/owner/value/undersize", err: "invalid header: invalid split header: invalid parent header: invalid owner: invalid length 24, expected 25", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + sh := *h.GetSplit() + ph := *sh.GetParentHeader() + ph.SetOwnerID(protoUserIDFromBytes(anyValidUsers[0][:24])) + sh.SetParentHeader(&ph) + h.SetSplit(&sh) + m.SetHeader(&h) + }}, + {name: "header/split/parent/owner/value/oversize", err: "invalid header: invalid split header: invalid parent header: invalid owner: invalid length 26, expected 25", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + sh := *h.GetSplit() + ph := *sh.GetParentHeader() + ph.SetOwnerID(protoUserIDFromBytes(append(anyValidUsers[0][:], 1))) + sh.SetParentHeader(&ph) + h.SetSplit(&sh) + m.SetHeader(&h) + }}, + {name: "header/split/parent/owner/value/wrong prefix", err: "invalid header: invalid split header: invalid parent header: invalid owner: invalid prefix byte 0x42, expected 0x35", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + sh := *h.GetSplit() + ph := *sh.GetParentHeader() + b := bytes.Clone(anyValidUsers[0][:]) + b[0] = 0x42 + ph.SetOwnerID(protoUserIDFromBytes(b)) + sh.SetParentHeader(&ph) + h.SetSplit(&sh) + m.SetHeader(&h) + }}, + {name: "header/split/parent/owner/value/checksum mismatch", err: "invalid header: invalid split header: invalid parent header: invalid owner: checksum mismatch", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + sh := *h.GetSplit() + ph := *sh.GetParentHeader() + b := bytes.Clone(anyValidUsers[0][:]) + b[len(b)-1]++ + ph.SetOwnerID(protoUserIDFromBytes(b)) + sh.SetParentHeader(&ph) + h.SetSplit(&sh) + m.SetHeader(&h) + }}, + {name: "header/split/parent/container/empty value", err: "invalid header: invalid split header: invalid parent header: invalid container: invalid length 0", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + sh := *h.GetSplit() + ph := *sh.GetParentHeader() + ph.SetContainerID(protoContainerIDFromBytes(nil)) + sh.SetParentHeader(&ph) + h.SetSplit(&sh) + m.SetHeader(&h) + }}, + {name: "header/split/parent/container/undersize", err: "invalid header: invalid split header: invalid parent header: invalid container: invalid length 31", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + sh := *h.GetSplit() + ph := *sh.GetParentHeader() + ph.SetContainerID(protoContainerIDFromBytes(anyValidContainers[0][:31])) + sh.SetParentHeader(&ph) + h.SetSplit(&sh) + m.SetHeader(&h) + }}, + {name: "header/split/parent/container/oversize", err: "invalid header: invalid split header: invalid parent header: invalid container: invalid length 33", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + sh := *h.GetSplit() + ph := *sh.GetParentHeader() + ph.SetContainerID(protoContainerIDFromBytes(append(anyValidContainers[0][:], 1))) + sh.SetParentHeader(&ph) + h.SetSplit(&sh) + m.SetHeader(&h) + }}, + {name: "header/split/parent/container/zero", err: "invalid header: invalid split header: invalid parent header: invalid container: zero container ID", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + sh := *h.GetSplit() + ph := *sh.GetParentHeader() + ph.SetContainerID(protoContainerIDFromBytes(make([]byte, 32))) + sh.SetParentHeader(&ph) + h.SetSplit(&sh) + m.SetHeader(&h) + }}, + {name: "header/split/parent/payload checksum/missing value", err: "invalid header: invalid split header: invalid parent header: invalid payload checksum: missing value", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + sh := *h.GetSplit() + ph := *sh.GetParentHeader() + ph.SetPayloadHash(new(refs.Checksum)) + sh.SetParentHeader(&ph) + h.SetSplit(&sh) + m.SetHeader(&h) + }}, + {name: "header/split/parent/payload homomorphic checksum/missing value", err: "invalid header: invalid split header: invalid parent header: invalid payload homomorphic checksum: missing value", + corrupt: func(m *apiobject.Object) { + h := *m.GetHeader() + sh := *h.GetSplit() + ph := *sh.GetParentHeader() + ph.SetHomomorphicHash(new(refs.Checksum)) + sh.SetParentHeader(&ph) + h.SetSplit(&sh) + m.SetHeader(&h) + }}, + } { + t.Run(tc.name, func(t *testing.T) { + m := obj.ToV2() + tc.corrupt(m) + require.EqualError(t, new(object.Object).ReadFromV2(*m), tc.err) + }) + } + }) +} + +func TestObject_ToV2(t *testing.T) { + // zero + m := object.Object{}.ToV2() + require.Zero(t, m.GetObjectID()) + require.Zero(t, m.GetSignature()) + require.Zero(t, m.GetHeader()) + require.Zero(t, m.GetPayload()) + + // filled + m = validObject.ToV2() + require.Equal(t, anyValidIDs[0][:], m.GetObjectID().GetValue()) + require.Equal(t, anyValidRegularPayload, m.GetPayload()) + msig := m.GetSignature() + require.Equal(t, anyValidSignatures[1].PublicKeyBytes(), msig.GetKey()) + require.Equal(t, anyValidSignatures[1].Value(), msig.GetSign()) + require.EqualValues(t, 1242896683, msig.GetScheme()) + + mh := m.GetHeader() + require.EqualValues(t, 525747025, mh.GetVersion().GetMajor()) + require.EqualValues(t, 171993162, mh.GetVersion().GetMinor()) + require.Equal(t, anyValidContainers[1][:], mh.GetContainerID().GetValue()) + require.Equal(t, anyValidUsers[1][:], mh.GetOwnerID().GetValue()) + require.EqualValues(t, anyValidCreationEpoch+1, mh.GetCreationEpoch()) + require.EqualValues(t, anyValidPayloadSize+1, mh.GetPayloadLength()) + require.EqualValues(t, 126384577, mh.GetPayloadHash().GetType()) + require.EqualValues(t, "checksum_3", mh.GetPayloadHash().GetSum()) + require.EqualValues(t, anyValidType+1, mh.GetObjectType()) + require.EqualValues(t, 1001923429, mh.GetHomomorphicHash().GetType()) + require.EqualValues(t, "checksum_4", mh.GetHomomorphicHash().GetSum()) + + mt := mh.GetSessionToken() + mb := mt.GetBody() + require.Equal(t, anyValidSessionID[:], mb.GetID()) + require.Equal(t, anyValidUsers[2][:], mb.GetOwnerID().GetValue()) + require.Equal(t, anySessionIssuerPubKeyBytes, mb.GetSessionKey()) + require.EqualValues(t, uint64(16429376563136800338), mb.GetLifetime().GetExp()) + require.EqualValues(t, 7956510363313998522, mb.GetLifetime().GetIat()) + require.EqualValues(t, uint64(17237208928641773338), mb.GetLifetime().GetNbf()) + c := mb.GetContext() + require.IsType(t, new(apisession.ObjectSessionContext), c) + co := c.(*apisession.ObjectSessionContext) + require.EqualValues(t, 1047242055, co.GetVerb()) + require.Equal(t, anyValidContainers[2][:], co.GetContainer().GetValue()) + objs := co.GetObjects() + require.Len(t, objs, 2) + require.Equal(t, anyValidIDs[8][:], objs[0].GetValue()) + require.Equal(t, anyValidIDs[9][:], objs[1].GetValue()) + ms := mt.GetSignature() + require.EqualValues(t, 1134494890, ms.GetScheme()) + require.EqualValues(t, "session_signer", ms.GetKey()) + require.EqualValues(t, "session_signature", ms.GetSign()) + + as := mh.GetAttributes() + require.Len(t, as, 3) + require.Equal(t, "attr_key1", as[0].GetKey()) + require.Equal(t, "attr_val1", as[0].GetValue()) + require.Equal(t, "__NEOFS__EXPIRATION_EPOCH", as[1].GetKey()) + require.Equal(t, "8516691293958955670", as[1].GetValue()) + require.Equal(t, "attr_key2", as[2].GetKey()) + require.Equal(t, "attr_val2", as[2].GetValue()) + + sh := mh.GetSplit() + require.Equal(t, anyValidIDs[1][:], sh.GetParent().GetValue()) + require.Equal(t, anyValidIDs[2][:], sh.GetPrevious().GetValue()) + require.Equal(t, anyValidSplitIDBytes, sh.GetSplitID()) + require.Equal(t, anyValidIDs[6][:], sh.GetFirst().GetValue()) + ch := sh.GetChildren() + require.Len(t, ch, 3) + require.Equal(t, anyValidIDs[3][:], ch[0].GetValue()) + require.Equal(t, anyValidIDs[4][:], ch[1].GetValue()) + require.Equal(t, anyValidIDs[5][:], ch[2].GetValue()) + ms = sh.GetParentSignature() + require.EqualValues(t, 1277002296, ms.GetScheme()) + require.EqualValues(t, "pub_1", ms.GetKey()) + require.EqualValues(t, "sig_1", ms.GetSign()) + + ph := sh.GetParentHeader() + require.NotNil(t, ph) + require.Zero(t, ph.GetSessionToken()) + require.Zero(t, ph.GetSplit()) + require.EqualValues(t, 88789927, ph.GetVersion().GetMajor()) + require.EqualValues(t, 2018985309, ph.GetVersion().GetMinor()) + require.Equal(t, anyValidContainers[0][:], ph.GetContainerID().GetValue()) + require.Equal(t, anyValidUsers[0][:], ph.GetOwnerID().GetValue()) + require.EqualValues(t, anyValidCreationEpoch, ph.GetCreationEpoch()) + require.EqualValues(t, anyValidPayloadSize, ph.GetPayloadLength()) + require.EqualValues(t, 1974315742, ph.GetPayloadHash().GetType()) + require.EqualValues(t, "checksum_1", ph.GetPayloadHash().GetSum()) + require.EqualValues(t, anyValidType, ph.GetObjectType()) + require.EqualValues(t, 1922538608, ph.GetHomomorphicHash().GetType()) + require.EqualValues(t, "checksum_2", ph.GetHomomorphicHash().GetSum()) + + as = ph.GetAttributes() + require.Len(t, as, 3) + require.Equal(t, "par_attr_key1", as[0].GetKey()) + require.Equal(t, "par_attr_val1", as[0].GetValue()) + require.Equal(t, "__NEOFS__EXPIRATION_EPOCH", as[1].GetKey()) + require.Equal(t, "14208497712700580130", as[1].GetValue()) + require.Equal(t, "par_attr_key2", as[2].GetKey()) + require.Equal(t, "par_attr_val2", as[2].GetValue()) +} + +func TestObject_Marshal(t *testing.T) { + require.Empty(t, object.Object{}.Marshal()) + require.Equal(t, validBinObject, validObject.Marshal()) +} + +func TestObject_Unmarshal(t *testing.T) { + t.Run("invalid", func(t *testing.T) { + t.Run("protobuf", func(t *testing.T) { + err := new(object.Object).Unmarshal([]byte("Hello, world!")) + require.ErrorContains(t, err, "proto") + require.ErrorContains(t, err, "cannot parse invalid wire-format data") + }) + for _, tc := range []struct { + name, err string + b []byte + }{ + {name: "id/empty value", err: "invalid ID: invalid length 0", + b: []byte{10, 0}}, + {name: "id/undersize", err: "invalid ID: invalid length 31", + b: []byte{10, 33, 10, 31, 178, 74, 58, 219, 46, 3, 110, 125, 220, 81, 238, 35, 27, 6, 228, 193, 190, 224, 77, 44, + 18, 56, 117, 173, 70, 246, 8, 139, 247, 174, 53}}, + {name: "id/oversize", err: "invalid ID: invalid length 33", + b: []byte{10, 35, 10, 33, 178, 74, 58, 219, 46, 3, 110, 125, 220, 81, 238, 35, 27, 6, 228, 193, 190, 224, 77, 44, + 18, 56, 117, 173, 70, 246, 8, 139, 247, 174, 53, 60, 1}}, + {name: "id/zero", err: "invalid ID: zero object ID", + b: []byte{10, 34, 10, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, + {name: "signature/scheme/overflow", err: "invalid signature: scheme 2147483648 overflows int32", + b: []byte{18, 11, 24, 128, 128, 128, 128, 248, 255, 255, 255, 255, 1}}, + {name: "header/owner/value/empty", err: "invalid header: invalid owner: invalid length 0, expected 25", + b: []byte{26, 2, 26, 0}}, + {name: "header/owner/value/undersize", err: "invalid header: invalid owner: invalid length 24, expected 25", + b: []byte{26, 28, 26, 26, 10, 24, 53, 59, 15, 5, 52, 131, 255, 198, 8, 98, 41, 184, 229, 237, 140, 215, 52, 129, + 211, 214, 90, 145, 237, 137}}, + {name: "header/owner/value/oversize", err: "invalid header: invalid owner: invalid length 26, expected 25", + b: []byte{26, 30, 26, 28, 10, 26, 53, 59, 15, 5, 52, 131, 255, 198, 8, 98, 41, 184, 229, 237, 140, 215, 52, 129, + 211, 214, 90, 145, 237, 137, 153, 1}}, + {name: "header/owner/value/wrong prefix", err: "invalid header: invalid owner: invalid prefix byte 0x42, expected 0x35", + b: []byte{26, 29, 26, 27, 10, 25, 66, 59, 15, 5, 52, 131, 255, 198, 8, 98, 41, 184, 229, 237, 140, 215, 52, 129, + 211, 214, 90, 145, 237, 137, 153}}, + {name: "header/owner/value/checksum mismatch", err: "invalid header: invalid owner: checksum mismatch", + b: []byte{26, 29, 26, 27, 10, 25, 53, 59, 15, 5, 52, 131, 255, 198, 8, 98, 41, 184, 229, 237, 140, 215, 52, 129, + 211, 214, 90, 145, 237, 137, 154}}, + {name: "header/container/empty value", err: "invalid header: invalid container: invalid length 0", + b: []byte{26, 2, 18, 0}}, + {name: "header/container/undersize", err: "invalid header: invalid container: invalid length 31", + b: []byte{26, 35, 18, 33, 10, 31, 245, 94, 164, 207, 217, 233, 175, 75, 123, 153, 174, 8, 20, 135, 96, 204, 179, + 93, 183, 250, 180, 255, 162, 182, 222, 220, 99, 125, 136, 117, 206}}, + {name: "header/container/oversize", err: "invalid header: invalid container: invalid length 33", + b: []byte{26, 37, 18, 35, 10, 33, 245, 94, 164, 207, 217, 233, 175, 75, 123, 153, 174, 8, 20, 135, 96, 204, 179, + 93, 183, 250, 180, 255, 162, 182, 222, 220, 99, 125, 136, 117, 206, 34, 1}}, + {name: "header/container/zero", err: "invalid header: invalid container: zero container ID", + b: []byte{26, 36, 18, 34, 10, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, + {name: "header/payload checksum/missing value", err: "invalid header: invalid payload checksum: missing value", + b: []byte{26, 2, 50, 0}}, + {name: "header/payload homomorphic checksum/missing value", err: "invalid header: invalid payload homomorphic checksum: missing value", + b: []byte{26, 2, 66, 0}}, + {name: "header/session/body/ID/undersize", err: "invalid header: invalid session token: invalid session ID: invalid UUID (got 15 bytes)", + b: []byte{26, 154, 2, 74, 151, 2, 10, 233, 1, 10, 15, 118, 23, 219, 249, 117, 70, 64, 33, 157, 229, 102, 253, 142, + 52, 17, 18, 27, 10, 25, 53, 248, 195, 15, 196, 254, 124, 23, 169, 198, 208, 15, 219, 229, 62, 150, 151, 159, + 221, 73, 224, 229, 106, 42, 222, 26, 32, 8, 210, 204, 150, 183, 128, 222, 183, 128, 228, 1, 16, 154, + 222, 137, 183, 154, 198, 183, 155, 239, 1, 24, 186, 149, 174, 136, 210, 128, 205, 181, 110, 34, 33, 2, 154, + 144, 131, 197, 214, 154, 163, 35, 93, 133, 107, 35, 109, 3, 218, 20, 0, 255, 26, 192, 33, 236, 0, 240, 183, + 253, 76, 187, 136, 215, 164, 217, 42, 116, 8, 199, 202, 174, 243, 3, 18, 108, 10, 34, 10, 32, 135, 89, 149, + 219, 185, 209, 233, 137, 224, 211, 141, 70, 193, 205, 248, 254, 226, 30, 114, 177, 245, 171, 29, 90, 212, + 15, 51, 86, 142, 101, 155, 141, 18, 34, 10, 32, 57, 171, 109, 41, 105, 25, 146, 224, 164, 89, 61, 178, 179, 158, + 67, 40, 32, 154, 122, 174, 117, 221, 138, 168, 135, 149, 238, 61, 68, 58, 34, 189, 18, 34, 10, 32, 110, 233, + 102, 232, 136, 68, 233, 22, 158, 100, 49, 20, 181, 95, 219, 143, 53, 250, 237, 113, 64, 25, 48, 11, 54, + 207, 56, 98, 99, 136, 207, 21, 18, 41, 10, 14, 115, 101, 115, 115, 105, 111, 110, 95, 115, 105, 103, 110, 101, 114, + 18, 17, 115, 101, 115, 115, 105, 111, 110, 32, 115, 105, 103, 110, 97, 116, 117, 114, 101, 24, 170, 137, 252, 156, 4}}, + {name: "header/session/body/ID/oversize", err: "invalid header: invalid session token: invalid session ID: invalid UUID (got 17 bytes)", + b: []byte{26, 156, 2, 74, 153, 2, 10, 235, 1, 10, 17, 118, 23, 219, 249, 117, 70, 64, 33, 157, 229, 102, 253, 142, + 52, 17, 144, 1, 18, 27, 10, 25, 53, 248, 195, 15, 196, 254, 124, 23, 169, 198, 208, 15, 219, 229, 62, 150, + 151, 159, 221, 73, 224, 229, 106, 42, 222, 26, 32, 8, 210, 204, 150, 183, 128, 222, 183, 128, 228, 1, + 16, 154, 222, 137, 183, 154, 198, 183, 155, 239, 1, 24, 186, 149, 174, 136, 210, 128, 205, 181, 110, 34, 33, 2, + 154, 144, 131, 197, 214, 154, 163, 35, 93, 133, 107, 35, 109, 3, 218, 20, 0, 255, 26, 192, 33, 236, 0, 240, + 183, 253, 76, 187, 136, 215, 164, 217, 42, 116, 8, 199, 202, 174, 243, 3, 18, 108, 10, 34, 10, 32, 135, 89, + 149, 219, 185, 209, 233, 137, 224, 211, 141, 70, 193, 205, 248, 254, 226, 30, 114, 177, 245, 171, 29, 90, + 212, 15, 51, 86, 142, 101, 155, 141, 18, 34, 10, 32, 57, 171, 109, 41, 105, 25, 146, 224, 164, 89, 61, 178, 179, + 158, 67, 40, 32, 154, 122, 174, 117, 221, 138, 168, 135, 149, 238, 61, 68, 58, 34, 189, 18, 34, 10, 32, 110, + 233, 102, 232, 136, 68, 233, 22, 158, 100, 49, 20, 181, 95, 219, 143, 53, 250, 237, 113, 64, 25, 48, 11, + 54, 207, 56, 98, 99, 136, 207, 21, 18, 41, 10, 14, 115, 101, 115, 115, 105, 111, 110, 95, 115, 105, 103, 110, 101, + 114, 18, 17, 115, 101, 115, 115, 105, 111, 110, 32, 115, 105, 103, 110, 97, 116, 117, 114, 101, 24, 170, 137, 252, 156, 4}}, + {name: "header/session/body/ID/wrong UUID version", err: "invalid header: invalid session token: invalid session UUID version 3", + b: []byte{26, 155, 2, 74, 152, 2, 10, 234, 1, 10, 16, 118, 23, 219, 249, 117, 70, 48, 33, 157, 229, 102, 253, 142, + 52, 17, 144, 18, 27, 10, 25, 53, 248, 195, 15, 196, 254, 124, 23, 169, 198, 208, 15, 219, 229, 62, 150, 151, + 159, 221, 73, 224, 229, 106, 42, 222, 26, 32, 8, 210, 204, 150, 183, 128, 222, 183, 128, 228, 1, 16, + 154, 222, 137, 183, 154, 198, 183, 155, 239, 1, 24, 186, 149, 174, 136, 210, 128, 205, 181, 110, 34, 33, 2, + 154, 144, 131, 197, 214, 154, 163, 35, 93, 133, 107, 35, 109, 3, 218, 20, 0, 255, 26, 192, 33, 236, 0, 240, + 183, 253, 76, 187, 136, 215, 164, 217, 42, 116, 8, 199, 202, 174, 243, 3, 18, 108, 10, 34, 10, 32, 135, 89, + 149, 219, 185, 209, 233, 137, 224, 211, 141, 70, 193, 205, 248, 254, 226, 30, 114, 177, 245, 171, 29, 90, + 212, 15, 51, 86, 142, 101, 155, 141, 18, 34, 10, 32, 57, 171, 109, 41, 105, 25, 146, 224, 164, 89, 61, 178, 179, + 158, 67, 40, 32, 154, 122, 174, 117, 221, 138, 168, 135, 149, 238, 61, 68, 58, 34, 189, 18, 34, 10, 32, 110, + 233, 102, 232, 136, 68, 233, 22, 158, 100, 49, 20, 181, 95, 219, 143, 53, 250, 237, 113, 64, 25, 48, 11, 54, + 207, 56, 98, 99, 136, 207, 21, 18, 41, 10, 14, 115, 101, 115, 115, 105, 111, 110, 95, 115, 105, 103, 110, 101, 114, 18, + 17, 115, 101, 115, 115, 105, 111, 110, 32, 115, 105, 103, 110, 97, 116, 117, 114, 101, 24, 170, 137, 252, 156, 4}}, + {name: "header/session/body/issuer/value/empty", err: "invalid header: invalid session token: invalid session issuer: invalid length 0, expected 25", + b: []byte{26, 128, 2, 74, 253, 1, 10, 207, 1, 10, 16, 118, 23, 219, 249, 117, 70, 64, 33, 157, 229, 102, 253, 142, + 52, 17, 144, 18, 0, 26, 32, 8, 210, 204, 150, 183, 128, 222, 183, 128, 228, 1, 16, 154, 222, 137, 183, 154, + 198, 183, 155, 239, 1, 24, 186, 149, 174, 136, 210, 128, 205, 181, 110, 34, 33, 2, 154, 144, 131, 197, 214, 154, + 163, 35, 93, 133, 107, 35, 109, 3, 218, 20, 0, 255, 26, 192, 33, 236, 0, 240, 183, 253, 76, 187, 136, 215, + 164, 217, 42, 116, 8, 199, 202, 174, 243, 3, 18, 108, 10, 34, 10, 32, 135, 89, 149, 219, 185, 209, 233, 137, + 224, 211, 141, 70, 193, 205, 248, 254, 226, 30, 114, 177, 245, 171, 29, 90, 212, 15, 51, 86, 142, 101, 155, + 141, 18, 34, 10, 32, 57, 171, 109, 41, 105, 25, 146, 224, 164, 89, 61, 178, 179, 158, 67, 40, 32, 154, 122, 174, + 117, 221, 138, 168, 135, 149, 238, 61, 68, 58, 34, 189, 18, 34, 10, 32, 110, 233, 102, 232, 136, 68, 233, 22, + 158, 100, 49, 20, 181, 95, 219, 143, 53, 250, 237, 113, 64, 25, 48, 11, 54, 207, 56, 98, 99, 136, 207, 21, 18, + 41, 10, 14, 115, 101, 115, 115, 105, 111, 110, 95, 115, 105, 103, 110, 101, 114, 18, 17, 115, 101, 115, 115, 105, 111, 110, + 32, 115, 105, 103, 110, 97, 116, 117, 114, 101, 24, 170, 137, 252, 156, 4}}, + {name: "header/session/body/issuer/value/undersize", err: "invalid header: invalid session token: invalid session issuer: invalid length 24, expected 25", + b: []byte{26, 154, 2, 74, 151, 2, 10, 233, 1, 10, 16, 118, 23, 219, 249, 117, 70, 64, 33, 157, 229, 102, 253, 142, + 52, 17, 144, 18, 26, 10, 24, 53, 59, 15, 5, 52, 131, 255, 198, 8, 98, 41, 184, 229, 237, 140, 215, 52, 129, + 211, 214, 90, 145, 237, 137, 26, 32, 8, 210, 204, 150, 183, 128, 222, 183, 128, 228, 1, 16, 154, 222, 137, + 183, 154, 198, 183, 155, 239, 1, 24, 186, 149, 174, 136, 210, 128, 205, 181, 110, 34, 33, 2, 154, 144, 131, 197, + 214, 154, 163, 35, 93, 133, 107, 35, 109, 3, 218, 20, 0, 255, 26, 192, 33, 236, 0, 240, 183, 253, 76, 187, + 136, 215, 164, 217, 42, 116, 8, 199, 202, 174, 243, 3, 18, 108, 10, 34, 10, 32, 135, 89, 149, 219, 185, 209, + 233, 137, 224, 211, 141, 70, 193, 205, 248, 254, 226, 30, 114, 177, 245, 171, 29, 90, 212, 15, 51, 86, 142, + 101, 155, 141, 18, 34, 10, 32, 57, 171, 109, 41, 105, 25, 146, 224, 164, 89, 61, 178, 179, 158, 67, 40, 32, 154, + 122, 174, 117, 221, 138, 168, 135, 149, 238, 61, 68, 58, 34, 189, 18, 34, 10, 32, 110, 233, 102, 232, 136, 68, + 233, 22, 158, 100, 49, 20, 181, 95, 219, 143, 53, 250, 237, 113, 64, 25, 48, 11, 54, 207, 56, 98, 99, 136, + 207, 21, 18, 41, 10, 14, 115, 101, 115, 115, 105, 111, 110, 95, 115, 105, 103, 110, 101, 114, 18, 17, 115, 101, 115, 115, + 105, 111, 110, 32, 115, 105, 103, 110, 97, 116, 117, 114, 101, 24, 170, 137, 252, 156, 4}}, + {name: "header/session/body/issuer/value/oversize", err: "invalid header: invalid session token: invalid session issuer: invalid length 26, expected 25", + b: []byte{26, 156, 2, 74, 153, 2, 10, 235, 1, 10, 16, 118, 23, 219, 249, 117, 70, 64, 33, 157, 229, 102, 253, 142, + 52, 17, 144, 18, 28, 10, 26, 53, 59, 15, 5, 52, 131, 255, 198, 8, 98, 41, 184, 229, 237, 140, 215, 52, 129, + 211, 214, 90, 145, 237, 137, 153, 1, 26, 32, 8, 210, 204, 150, 183, 128, 222, 183, 128, 228, 1, 16, 154, + 222, 137, 183, 154, 198, 183, 155, 239, 1, 24, 186, 149, 174, 136, 210, 128, 205, 181, 110, 34, 33, 2, 154, + 144, 131, 197, 214, 154, 163, 35, 93, 133, 107, 35, 109, 3, 218, 20, 0, 255, 26, 192, 33, 236, 0, 240, 183, + 253, 76, 187, 136, 215, 164, 217, 42, 116, 8, 199, 202, 174, 243, 3, 18, 108, 10, 34, 10, 32, 135, 89, 149, + 219, 185, 209, 233, 137, 224, 211, 141, 70, 193, 205, 248, 254, 226, 30, 114, 177, 245, 171, 29, 90, 212, + 15, 51, 86, 142, 101, 155, 141, 18, 34, 10, 32, 57, 171, 109, 41, 105, 25, 146, 224, 164, 89, 61, 178, 179, 158, + 67, 40, 32, 154, 122, 174, 117, 221, 138, 168, 135, 149, 238, 61, 68, 58, 34, 189, 18, 34, 10, 32, 110, 233, + 102, 232, 136, 68, 233, 22, 158, 100, 49, 20, 181, 95, 219, 143, 53, 250, 237, 113, 64, 25, 48, 11, 54, + 207, 56, 98, 99, 136, 207, 21, 18, 41, 10, 14, 115, 101, 115, 115, 105, 111, 110, 95, 115, 105, 103, 110, 101, 114, + 18, 17, 115, 101, 115, 115, 105, 111, 110, 32, 115, 105, 103, 110, 97, 116, 117, 114, 101, 24, 170, 137, 252, 156, 4}}, + {name: "header/session/body/issuer/value/wrong prefix", err: "invalid header: invalid session token: invalid session issuer: invalid prefix byte 0x42, expected 0x35", + b: []byte{26, 155, 2, 74, 152, 2, 10, 234, 1, 10, 16, 118, 23, 219, 249, 117, 70, 64, 33, 157, 229, 102, 253, 142, + 52, 17, 144, 18, 27, 10, 25, 66, 59, 15, 5, 52, 131, 255, 198, 8, 98, 41, 184, 229, 237, 140, 215, 52, 129, + 211, 214, 90, 145, 237, 137, 153, 26, 32, 8, 210, 204, 150, 183, 128, 222, 183, 128, 228, 1, 16, 154, 222, + 137, 183, 154, 198, 183, 155, 239, 1, 24, 186, 149, 174, 136, 210, 128, 205, 181, 110, 34, 33, 2, 154, 144, 131, + 197, 214, 154, 163, 35, 93, 133, 107, 35, 109, 3, 218, 20, 0, 255, 26, 192, 33, 236, 0, 240, 183, 253, 76, + 187, 136, 215, 164, 217, 42, 116, 8, 199, 202, 174, 243, 3, 18, 108, 10, 34, 10, 32, 135, 89, 149, 219, 185, + 209, 233, 137, 224, 211, 141, 70, 193, 205, 248, 254, 226, 30, 114, 177, 245, 171, 29, 90, 212, 15, 51, 86, + 142, 101, 155, 141, 18, 34, 10, 32, 57, 171, 109, 41, 105, 25, 146, 224, 164, 89, 61, 178, 179, 158, 67, 40, 32, + 154, 122, 174, 117, 221, 138, 168, 135, 149, 238, 61, 68, 58, 34, 189, 18, 34, 10, 32, 110, 233, 102, 232, + 136, 68, 233, 22, 158, 100, 49, 20, 181, 95, 219, 143, 53, 250, 237, 113, 64, 25, 48, 11, 54, 207, 56, 98, + 99, 136, 207, 21, 18, 41, 10, 14, 115, 101, 115, 115, 105, 111, 110, 95, 115, 105, 103, 110, 101, 114, 18, 17, 115, 101, + 115, 115, 105, 111, 110, 32, 115, 105, 103, 110, 97, 116, 117, 114, 101, 24, 170, 137, 252, 156, 4}}, + {name: "header/session/body/issuer/value/checksum mismatch", err: "invalid header: invalid session token: invalid session issuer: checksum mismatch", + b: []byte{26, 155, 2, 74, 152, 2, 10, 234, 1, 10, 16, 118, 23, 219, 249, 117, 70, 64, 33, 157, 229, 102, 253, 142, + 52, 17, 144, 18, 27, 10, 25, 53, 59, 15, 5, 52, 131, 255, 198, 8, 98, 41, 184, 229, 237, 140, 215, 52, 129, + 211, 214, 90, 145, 237, 137, 154, 26, 32, 8, 210, 204, 150, 183, 128, 222, 183, 128, 228, 1, 16, 154, 222, + 137, 183, 154, 198, 183, 155, 239, 1, 24, 186, 149, 174, 136, 210, 128, 205, 181, 110, 34, 33, 2, 154, 144, 131, + 197, 214, 154, 163, 35, 93, 133, 107, 35, 109, 3, 218, 20, 0, 255, 26, 192, 33, 236, 0, 240, 183, 253, 76, + 187, 136, 215, 164, 217, 42, 116, 8, 199, 202, 174, 243, 3, 18, 108, 10, 34, 10, 32, 135, 89, 149, 219, 185, + 209, 233, 137, 224, 211, 141, 70, 193, 205, 248, 254, 226, 30, 114, 177, 245, 171, 29, 90, 212, 15, 51, 86, + 142, 101, 155, 141, 18, 34, 10, 32, 57, 171, 109, 41, 105, 25, 146, 224, 164, 89, 61, 178, 179, 158, 67, 40, 32, + 154, 122, 174, 117, 221, 138, 168, 135, 149, 238, 61, 68, 58, 34, 189, 18, 34, 10, 32, 110, 233, 102, 232, 136, + 68, 233, 22, 158, 100, 49, 20, 181, 95, 219, 143, 53, 250, 237, 113, 64, 25, 48, 11, 54, 207, 56, 98, 99, 136, + 207, 21, 18, 41, 10, 14, 115, 101, 115, 115, 105, 111, 110, 95, 115, 105, 103, 110, 101, 114, 18, 17, 115, 101, 115, 115, + 105, 111, 110, 32, 115, 105, 103, 110, 97, 116, 117, 114, 101, 24, 170, 137, 252, 156, 4}}, + {name: "header/session/body/context/wrong oneof", err: "invalid header: invalid session token: invalid context: invalid context *session.ContainerSessionContext", + b: []byte{26, 166, 1, 74, 163, 1, 10, 118, 10, 16, 118, 23, 219, 249, 117, 70, 64, 33, 157, 229, 102, 253, 142, 52, + 17, 144, 18, 27, 10, 25, 53, 248, 195, 15, 196, 254, 124, 23, 169, 198, 208, 15, 219, 229, 62, 150, 151, 159, + 221, 73, 224, 229, 106, 42, 222, 26, 32, 8, 210, 204, 150, 183, 128, 222, 183, 128, 228, 1, 16, 154, + 222, 137, 183, 154, 198, 183, 155, 239, 1, 24, 186, 149, 174, 136, 210, 128, 205, 181, 110, 34, 33, 2, 154, 144, + 131, 197, 214, 154, 163, 35, 93, 133, 107, 35, 109, 3, 218, 20, 0, 255, 26, 192, 33, 236, 0, 240, 183, 253, + 76, 187, 136, 215, 164, 217, 50, 0, 18, 41, 10, 14, 115, 101, 115, 115, 105, 111, 110, 95, 115, 105, 103, 110, 101, 114, + 18, 17, 115, 101, 115, 115, 105, 111, 110, 32, 115, 105, 103, 110, 97, 116, 117, 114, 101, 24, 170, 137, 252, 156, 4}}, + {name: "header/session/body/context/container/empty value", err: "invalid header: invalid session token: invalid context: invalid container ID: invalid length 0", + b: []byte{26, 249, 1, 74, 246, 1, 10, 200, 1, 10, 16, 118, 23, 219, 249, 117, 70, 64, 33, 157, 229, 102, 253, 142, + 52, 17, 144, 18, 27, 10, 25, 53, 248, 195, 15, 196, 254, 124, 23, 169, 198, 208, 15, 219, 229, 62, 150, 151, + 159, 221, 73, 224, 229, 106, 42, 222, 26, 32, 8, 210, 204, 150, 183, 128, 222, 183, 128, 228, 1, 16, + 154, 222, 137, 183, 154, 198, 183, 155, 239, 1, 24, 186, 149, 174, 136, 210, 128, 205, 181, 110, 34, 33, 2, 154, + 144, 131, 197, 214, 154, 163, 35, 93, 133, 107, 35, 109, 3, 218, 20, 0, 255, 26, 192, 33, 236, 0, 240, 183, + 253, 76, 187, 136, 215, 164, 217, 42, 82, 8, 199, 202, 174, 243, 3, 18, 74, 10, 0, 18, 34, 10, 32, 57, 171, 109, + 41, 105, 25, 146, 224, 164, 89, 61, 178, 179, 158, 67, 40, 32, 154, 122, 174, 117, 221, 138, 168, 135, 149, 238, + 61, 68, 58, 34, 189, 18, 34, 10, 32, 110, 233, 102, 232, 136, 68, 233, 22, 158, 100, 49, 20, 181, 95, 219, + 143, 53, 250, 237, 113, 64, 25, 48, 11, 54, 207, 56, 98, 99, 136, 207, 21, 18, 41, 10, 14, 115, 101, 115, 115, 105, + 111, 110, 95, 115, 105, 103, 110, 101, 114, 18, 17, 115, 101, 115, 115, 105, 111, 110, 32, 115, 105, 103, 110, 97, 116, 117, + 114, 101, 24, 170, 137, 252, 156, 4}}, + {name: "header/session/body/context/container/undersize", err: "invalid header: invalid session token: invalid context: invalid container ID: invalid length 31", + b: []byte{26, 154, 2, 74, 151, 2, 10, 233, 1, 10, 16, 118, 23, 219, 249, 117, 70, 64, 33, 157, 229, 102, 253, 142, + 52, 17, 144, 18, 27, 10, 25, 53, 248, 195, 15, 196, 254, 124, 23, 169, 198, 208, 15, 219, 229, 62, 150, 151, + 159, 221, 73, 224, 229, 106, 42, 222, 26, 32, 8, 210, 204, 150, 183, 128, 222, 183, 128, 228, 1, 16, + 154, 222, 137, 183, 154, 198, 183, 155, 239, 1, 24, 186, 149, 174, 136, 210, 128, 205, 181, 110, 34, 33, 2, 154, + 144, 131, 197, 214, 154, 163, 35, 93, 133, 107, 35, 109, 3, 218, 20, 0, 255, 26, 192, 33, 236, 0, 240, 183, + 253, 76, 187, 136, 215, 164, 217, 42, 115, 8, 199, 202, 174, 243, 3, 18, 107, 10, 33, 10, 31, 245, 94, 164, 207, + 217, 233, 175, 75, 123, 153, 174, 8, 20, 135, 96, 204, 179, 93, 183, 250, 180, 255, 162, 182, 222, 220, 99, + 125, 136, 117, 206, 18, 34, 10, 32, 57, 171, 109, 41, 105, 25, 146, 224, 164, 89, 61, 178, 179, 158, 67, 40, 32, + 154, 122, 174, 117, 221, 138, 168, 135, 149, 238, 61, 68, 58, 34, 189, 18, 34, 10, 32, 110, 233, 102, 232, 136, + 68, 233, 22, 158, 100, 49, 20, 181, 95, 219, 143, 53, 250, 237, 113, 64, 25, 48, 11, 54, 207, 56, 98, 99, 136, + 207, 21, 18, 41, 10, 14, 115, 101, 115, 115, 105, 111, 110, 95, 115, 105, 103, 110, 101, 114, 18, 17, 115, 101, 115, 115, + 105, 111, 110, 32, 115, 105, 103, 110, 97, 116, 117, 114, 101, 24, 170, 137, 252, 156, 4}}, + {name: "header/session/body/context/container/oversize", err: "invalid header: invalid session token: invalid context: invalid container ID: invalid length 33", + b: []byte{26, 156, 2, 74, 153, 2, 10, 235, 1, 10, 16, 118, 23, 219, 249, 117, 70, 64, 33, 157, 229, 102, 253, 142, + 52, 17, 144, 18, 27, 10, 25, 53, 248, 195, 15, 196, 254, 124, 23, 169, 198, 208, 15, 219, 229, 62, 150, 151, + 159, 221, 73, 224, 229, 106, 42, 222, 26, 32, 8, 210, 204, 150, 183, 128, 222, 183, 128, 228, 1, 16, + 154, 222, 137, 183, 154, 198, 183, 155, 239, 1, 24, 186, 149, 174, 136, 210, 128, 205, 181, 110, 34, 33, 2, 154, + 144, 131, 197, 214, 154, 163, 35, 93, 133, 107, 35, 109, 3, 218, 20, 0, 255, 26, 192, 33, 236, 0, 240, 183, + 253, 76, 187, 136, 215, 164, 217, 42, 117, 8, 199, 202, 174, 243, 3, 18, 109, 10, 35, 10, 33, 245, 94, 164, 207, + 217, 233, 175, 75, 123, 153, 174, 8, 20, 135, 96, 204, 179, 93, 183, 250, 180, 255, 162, 182, 222, 220, 99, + 125, 136, 117, 206, 34, 1, 18, 34, 10, 32, 57, 171, 109, 41, 105, 25, 146, 224, 164, 89, 61, 178, 179, 158, 67, 40, + 32, 154, 122, 174, 117, 221, 138, 168, 135, 149, 238, 61, 68, 58, 34, 189, 18, 34, 10, 32, 110, 233, 102, 232, + 136, 68, 233, 22, 158, 100, 49, 20, 181, 95, 219, 143, 53, 250, 237, 113, 64, 25, 48, 11, 54, 207, 56, 98, 99, + 136, 207, 21, 18, 41, 10, 14, 115, 101, 115, 115, 105, 111, 110, 95, 115, 105, 103, 110, 101, 114, 18, 17, 115, 101, 115, + 115, 105, 111, 110, 32, 115, 105, 103, 110, 97, 116, 117, 114, 101, 24, 170, 137, 252, 156, 4}}, + {name: "header/session/body/context/object/empty value", err: "invalid header: invalid session token: invalid context: invalid target object: invalid length 0", + b: []byte{26, 249, 1, 74, 246, 1, 10, 200, 1, 10, 16, 118, 23, 219, 249, 117, 70, 64, 33, 157, 229, 102, 253, 142, + 52, 17, 144, 18, 27, 10, 25, 53, 248, 195, 15, 196, 254, 124, 23, 169, 198, 208, 15, 219, 229, 62, 150, 151, + 159, 221, 73, 224, 229, 106, 42, 222, 26, 32, 8, 210, 204, 150, 183, 128, 222, 183, 128, 228, 1, 16, + 154, 222, 137, 183, 154, 198, 183, 155, 239, 1, 24, 186, 149, 174, 136, 210, 128, 205, 181, 110, 34, 33, 2, 154, + 144, 131, 197, 214, 154, 163, 35, 93, 133, 107, 35, 109, 3, 218, 20, 0, 255, 26, 192, 33, 236, 0, 240, 183, + 253, 76, 187, 136, 215, 164, 217, 42, 82, 8, 199, 202, 174, 243, 3, 18, 74, 10, 34, 10, 32, 135, 89, 149, 219, + 185, 209, 233, 137, 224, 211, 141, 70, 193, 205, 248, 254, 226, 30, 114, 177, 245, 171, 29, 90, 212, 15, 51, + 86, 142, 101, 155, 141, 18, 0, 18, 34, 10, 32, 110, 233, 102, 232, 136, 68, 233, 22, 158, 100, 49, 20, 181, + 95, 219, 143, 53, 250, 237, 113, 64, 25, 48, 11, 54, 207, 56, 98, 99, 136, 207, 21, 18, 41, 10, 14, 115, 101, 115, + 115, 105, 111, 110, 95, 115, 105, 103, 110, 101, 114, 18, 17, 115, 101, 115, 115, 105, 111, 110, 32, 115, 105, 103, 110, + 97, 116, 117, 114, 101, 24, 170, 137, 252, 156, 4}}, + {name: "header/session/body/context/object/undersize", err: "invalid header: invalid session token: invalid context: invalid target object: invalid length 31", + b: []byte{26, 154, 2, 74, 151, 2, 10, 233, 1, 10, 16, 118, 23, 219, 249, 117, 70, 64, 33, 157, 229, 102, 253, 142, + 52, 17, 144, 18, 27, 10, 25, 53, 248, 195, 15, 196, 254, 124, 23, 169, 198, 208, 15, 219, 229, 62, 150, 151, + 159, 221, 73, 224, 229, 106, 42, 222, 26, 32, 8, 210, 204, 150, 183, 128, 222, 183, 128, 228, 1, 16, + 154, 222, 137, 183, 154, 198, 183, 155, 239, 1, 24, 186, 149, 174, 136, 210, 128, 205, 181, 110, 34, 33, 2, 154, + 144, 131, 197, 214, 154, 163, 35, 93, 133, 107, 35, 109, 3, 218, 20, 0, 255, 26, 192, 33, 236, 0, 240, 183, + 253, 76, 187, 136, 215, 164, 217, 42, 115, 8, 199, 202, 174, 243, 3, 18, 107, 10, 34, 10, 32, 135, 89, 149, 219, + 185, 209, 233, 137, 224, 211, 141, 70, 193, 205, 248, 254, 226, 30, 114, 177, 245, 171, 29, 90, 212, 15, 51, + 86, 142, 101, 155, 141, 18, 33, 10, 31, 229, 77, 63, 235, 2, 9, 165, 123, 116, 123, 47, 65, 22, 34, 214, 76, 45, + 225, 21, 46, 135, 32, 116, 172, 67, 213, 243, 57, 253, 127, 179, 18, 34, 10, 32, 110, 233, 102, 232, 136, 68, + 233, 22, 158, 100, 49, 20, 181, 95, 219, 143, 53, 250, 237, 113, 64, 25, 48, 11, 54, 207, 56, 98, 99, 136, + 207, 21, 18, 41, 10, 14, 115, 101, 115, 115, 105, 111, 110, 95, 115, 105, 103, 110, 101, 114, 18, 17, 115, 101, 115, 115, + 105, 111, 110, 32, 115, 105, 103, 110, 97, 116, 117, 114, 101, 24, 170, 137, 252, 156, 4}}, + {name: "header/session/body/context/object/oversize", err: "invalid header: invalid session token: invalid context: invalid target object: invalid length 33", + b: []byte{26, 156, 2, 74, 153, 2, 10, 235, 1, 10, 16, 118, 23, 219, 249, 117, 70, 64, 33, 157, 229, 102, 253, 142, + 52, 17, 144, 18, 27, 10, 25, 53, 248, 195, 15, 196, 254, 124, 23, 169, 198, 208, 15, 219, 229, 62, 150, 151, + 159, 221, 73, 224, 229, 106, 42, 222, 26, 32, 8, 210, 204, 150, 183, 128, 222, 183, 128, 228, 1, 16, + 154, 222, 137, 183, 154, 198, 183, 155, 239, 1, 24, 186, 149, 174, 136, 210, 128, 205, 181, 110, 34, 33, 2, 154, + 144, 131, 197, 214, 154, 163, 35, 93, 133, 107, 35, 109, 3, 218, 20, 0, 255, 26, 192, 33, 236, 0, 240, 183, + 253, 76, 187, 136, 215, 164, 217, 42, 117, 8, 199, 202, 174, 243, 3, 18, 109, 10, 34, 10, 32, 135, 89, 149, 219, + 185, 209, 233, 137, 224, 211, 141, 70, 193, 205, 248, 254, 226, 30, 114, 177, 245, 171, 29, 90, 212, 15, 51, + 86, 142, 101, 155, 141, 18, 35, 10, 33, 229, 77, 63, 235, 2, 9, 165, 123, 116, 123, 47, 65, 22, 34, 214, 76, 45, + 225, 21, 46, 135, 32, 116, 172, 67, 213, 243, 57, 253, 127, 179, 235, 1, 18, 34, 10, 32, 110, 233, 102, 232, + 136, 68, 233, 22, 158, 100, 49, 20, 181, 95, 219, 143, 53, 250, 237, 113, 64, 25, 48, 11, 54, 207, 56, 98, 99, + 136, 207, 21, 18, 41, 10, 14, 115, 101, 115, 115, 105, 111, 110, 95, 115, 105, 103, 110, 101, 114, 18, 17, 115, 101, 115, + 115, 105, 111, 110, 32, 115, 105, 103, 110, 97, 116, 117, 114, 101, 24, 170, 137, 252, 156, 4}}, + {name: "header/session/signature/scheme/overflow", err: "invalid header: invalid session token: invalid body signature: scheme 2147483648 overflows int32", + b: []byte{26, 253, 1, 74, 250, 1, 10, 234, 1, 10, 16, 118, 23, 219, 249, 117, 70, 64, 33, 157, 229, 102, 253, 142, + 52, 17, 144, 18, 27, 10, 25, 53, 248, 195, 15, 196, 254, 124, 23, 169, 198, 208, 15, 219, 229, 62, 150, 151, + 159, 221, 73, 224, 229, 106, 42, 222, 26, 32, 8, 210, 204, 150, 183, 128, 222, 183, 128, 228, 1, 16, + 154, 222, 137, 183, 154, 198, 183, 155, 239, 1, 24, 186, 149, 174, 136, 210, 128, 205, 181, 110, 34, 33, 2, 154, + 144, 131, 197, 214, 154, 163, 35, 93, 133, 107, 35, 109, 3, 218, 20, 0, 255, 26, 192, 33, 236, 0, 240, 183, + 253, 76, 187, 136, 215, 164, 217, 42, 116, 8, 199, 202, 174, 243, 3, 18, 108, 10, 34, 10, 32, 135, 89, 149, 219, + 185, 209, 233, 137, 224, 211, 141, 70, 193, 205, 248, 254, 226, 30, 114, 177, 245, 171, 29, 90, 212, 15, 51, + 86, 142, 101, 155, 141, 18, 34, 10, 32, 57, 171, 109, 41, 105, 25, 146, 224, 164, 89, 61, 178, 179, 158, 67, 40, + 32, 154, 122, 174, 117, 221, 138, 168, 135, 149, 238, 61, 68, 58, 34, 189, 18, 34, 10, 32, 110, 233, 102, 232, + 136, 68, 233, 22, 158, 100, 49, 20, 181, 95, 219, 143, 53, 250, 237, 113, 64, 25, 48, 11, 54, 207, 56, 98, 99, + 136, 207, 21, 18, 11, 24, 128, 128, 128, 128, 248, 255, 255, 255, 255, 1}}, + {name: "attributes/no key", err: "invalid header: empty key of the attribute #1", + b: []byte{26, 26, 82, 8, 10, 2, 107, 49, 18, 2, 118, 49, 82, 4, 18, 2, 118, 49, 82, 8, 10, 2, 107, 51, 18, 2, 118, 51}}, + {name: "attributes/no value", err: "invalid header: empty value of the attribute #1 (k2)", + b: []byte{26, 26, 82, 8, 10, 2, 107, 49, 18, 2, 118, 49, 82, 4, 10, 2, 107, 50, 82, 8, 10, 2, 107, 51, 18, 2, 118, 51}}, + {name: "attributes/duplicated", err: "invalid header: duplicated attribute k1", + b: []byte{26, 30, 82, 8, 10, 2, 107, 49, 18, 2, 118, 49, 82, 8, 10, 2, 107, 50, 18, 2, 118, 50, 82, 8, 10, 2, 107, + 49, 18, 2, 118, 51}}, + {name: "attributes/expiration", err: "invalid header: invalid expiration attribute (must be a uint): strconv.ParseUint: parsing \"foo\": invalid syntax", + b: []byte{26, 54, 82, 8, 10, 2, 107, 49, 18, 2, 118, 49, 82, 32, 10, 25, 95, 95, 78, 69, 79, 70, 83, 95, 95, 69, + 88, 80, 73, 82, 65, 84, 73, 79, 78, 95, 69, 80, 79, 67, 72, 18, 3, 102, 111, 111, 82, 8, 10, 2, 107, 51, 18, 2, 118, 51}}, + {name: "header/split/parent ID/empty value", err: "invalid header: invalid split header: invalid parent split member ID: invalid length 0", + b: []byte{26, 4, 90, 2, 10, 0}}, + {name: "header/split/parent ID/undersize", err: "invalid header: invalid split header: invalid parent split member ID: invalid length 31", + b: []byte{26, 37, 90, 35, 10, 33, 10, 31, 178, 74, 58, 219, 46, 3, 110, 125, 220, 81, 238, 35, 27, 6, 228, 193, + 190, 224, 77, 44, 18, 56, 117, 173, 70, 246, 8, 139, 247, 174, 53}}, + {name: "header/split/parent ID/oversize", err: "invalid header: invalid split header: invalid parent split member ID: invalid length 33", + b: []byte{26, 39, 90, 37, 10, 35, 10, 33, 178, 74, 58, 219, 46, 3, 110, 125, 220, 81, 238, 35, 27, 6, 228, 193, + 190, 224, 77, 44, 18, 56, 117, 173, 70, 246, 8, 139, 247, 174, 53, 60, 1}}, + {name: "header/split/parent ID/zero", err: "invalid header: invalid split header: invalid parent split member ID: zero object ID", + b: []byte{26, 38, 90, 36, 10, 34, 10, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0}}, + {name: "header/split/previous/empty value", err: "invalid header: invalid split header: invalid previous split member ID: invalid length 0", + b: []byte{26, 4, 90, 2, 18, 0}}, + {name: "header/split/previous/undersize", err: "invalid header: invalid split header: invalid previous split member ID: invalid length 31", + b: []byte{26, 37, 90, 35, 18, 33, 10, 31, 178, 74, 58, 219, 46, 3, 110, 125, 220, 81, 238, 35, 27, 6, 228, 193, + 190, 224, 77, 44, 18, 56, 117, 173, 70, 246, 8, 139, 247, 174, 53}}, + {name: "header/split/previous/oversize", err: "invalid header: invalid split header: invalid previous split member ID: invalid length 33", + b: []byte{26, 39, 90, 37, 18, 35, 10, 33, 178, 74, 58, 219, 46, 3, 110, 125, 220, 81, 238, 35, 27, 6, 228, 193, + 190, 224, 77, 44, 18, 56, 117, 173, 70, 246, 8, 139, 247, 174, 53, 60, 1}}, + {name: "header/split/previous/zero", err: "invalid header: invalid split header: invalid previous split member ID: zero object ID", + b: []byte{26, 38, 90, 36, 18, 34, 10, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0}}, + {name: "header/split/first/empty value", err: "invalid header: invalid split header: invalid first split member ID: invalid length 0", + b: []byte{26, 4, 90, 2, 58, 0}}, + {name: "header/split/first/undersize", err: "invalid header: invalid split header: invalid first split member ID: invalid length 31", + b: []byte{26, 37, 90, 35, 58, 33, 10, 31, 178, 74, 58, 219, 46, 3, 110, 125, 220, 81, 238, 35, 27, 6, 228, 193, + 190, 224, 77, 44, 18, 56, 117, 173, 70, 246, 8, 139, 247, 174, 53}}, + {name: "header/split/first/oversize", err: "invalid header: invalid split header: invalid first split member ID: invalid length 33", + b: []byte{26, 39, 90, 37, 58, 35, 10, 33, 178, 74, 58, 219, 46, 3, 110, 125, 220, 81, 238, 35, 27, 6, 228, 193, + 190, 224, 77, 44, 18, 56, 117, 173, 70, 246, 8, 139, 247, 174, 53, 60, 1}}, + {name: "header/split/first/zero", err: "invalid header: invalid split header: invalid first split member ID: zero object ID", + b: []byte{26, 38, 90, 36, 58, 34, 10, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0}}, + {name: "header/split/ID/undersize", err: "invalid header: invalid split header: invalid split UUID: invalid UUID (got 15 bytes)", + b: []byte{26, 19, 90, 17, 50, 15, 224, 132, 3, 80, 32, 44, 69, 184, 185, 32, 226, 201, 206, 196, 147}}, + {name: "header/split/ID/oversize", err: "invalid header: invalid split header: invalid split UUID: invalid UUID (got 17 bytes)", + b: []byte{26, 21, 90, 19, 50, 17, 224, 132, 3, 80, 32, 44, 69, 184, 185, 32, 226, 201, 206, 196, 147, 41, 1}}, + {name: "header/split/ID/wrong UUID version", err: "invalid header: invalid split header: invalid split UUID version 3", + b: []byte{26, 20, 90, 18, 50, 16, 224, 132, 3, 80, 32, 44, 48, 184, 185, 32, 226, 201, 206, 196, 147, 41}}, + {name: "header/split/children/empty value", err: "invalid header: invalid split header: invalid child split member ID #1: invalid length 0", + b: []byte{26, 40, 90, 38, 42, 34, 10, 32, 178, 74, 58, 219, 46, 3, 110, 125, 220, 81, 238, 35, 27, 6, 228, 193, + 190, 224, 77, 44, 18, 56, 117, 173, 70, 246, 8, 139, 247, 174, 53, 60, 42, 0}}, + {name: "header/split/children/undersize", err: "invalid header: invalid split header: invalid child split member ID #1: invalid length 31", + b: []byte{26, 73, 90, 71, 42, 34, 10, 32, 178, 74, 58, 219, 46, 3, 110, 125, 220, 81, 238, 35, 27, 6, 228, 193, + 190, 224, 77, 44, 18, 56, 117, 173, 70, 246, 8, 139, 247, 174, 53, 60, 42, 33, 10, 31, 229, 77, 63, 235, 2, + 9, 165, 123, 116, 123, 47, 65, 22, 34, 214, 76, 45, 225, 21, 46, 135, 32, 116, 172, 67, 213, 243, 57, 253, + 127, 179}}, + {name: "header/split/children/oversize", err: "invalid header: invalid split header: invalid child split member ID #1: invalid length 33", + b: []byte{26, 75, 90, 73, 42, 34, 10, 32, 178, 74, 58, 219, 46, 3, 110, 125, 220, 81, 238, 35, 27, 6, 228, 193, + 190, 224, 77, 44, 18, 56, 117, 173, 70, 246, 8, 139, 247, 174, 53, 60, 42, 35, 10, 33, 229, 77, 63, 235, 2, + 9, 165, 123, 116, 123, 47, 65, 22, 34, 214, 76, 45, 225, 21, 46, 135, 32, 116, 172, 67, 213, 243, 57, 253, + 127, 179, 235, 1}}, + {name: "header/split/children/zero", err: "invalid header: invalid split header: invalid child split member ID #1: zero object ID", + b: []byte{26, 74, 90, 72, 42, 34, 10, 32, 178, 74, 58, 219, 46, 3, 110, 125, 220, 81, 238, 35, 27, 6, 228, 193, + 190, 224, 77, 44, 18, 56, 117, 173, 70, 246, 8, 139, 247, 174, 53, 60, 42, 34, 10, 32, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, + {name: "header/split/parent signature/scheme/overflow", err: "invalid header: invalid split header: invalid parent signature: scheme 2147483648 overflows int32", + b: []byte{26, 15, 90, 13, 26, 11, 24, 128, 128, 128, 128, 248, 255, 255, 255, 255, 1}}, + {name: "header/split/parent/owner/value/empty", err: "invalid header: invalid split header: invalid parent header: invalid owner: invalid length 0, expected 25", + b: []byte{26, 6, 90, 4, 34, 2, 26, 0}}, + {name: "header/split/parent/owner/value/undersize", err: "invalid header: invalid split header: invalid parent header: invalid owner: invalid length 24, expected 25", + b: []byte{26, 32, 90, 30, 34, 28, 26, 26, 10, 24, 53, 59, 15, 5, 52, 131, 255, 198, 8, 98, 41, 184, 229, 237, + 140, 215, 52, 129, 211, 214, 90, 145, 237, 137}}, + {name: "header/split/parent/owner/value/oversize", err: "invalid header: invalid split header: invalid parent header: invalid owner: invalid length 26, expected 25", + b: []byte{26, 34, 90, 32, 34, 30, 26, 28, 10, 26, 53, 59, 15, 5, 52, 131, 255, 198, 8, 98, 41, 184, 229, 237, + 140, 215, 52, 129, 211, 214, 90, 145, 237, 137, 153, 1}}, + {name: "header/split/parent/owner/value/wrong prefix", err: "invalid header: invalid split header: invalid parent header: invalid owner: invalid prefix byte 0x42, expected 0x35", + b: []byte{26, 33, 90, 31, 34, 29, 26, 27, 10, 25, 66, 59, 15, 5, 52, 131, 255, 198, 8, 98, 41, 184, 229, 237, + 140, 215, 52, 129, 211, 214, 90, 145, 237, 137, 153}}, + {name: "header/split/parent/owner/value/checksum mismatch", err: "invalid header: invalid split header: invalid parent header: invalid owner: checksum mismatch", + b: []byte{26, 33, 90, 31, 34, 29, 26, 27, 10, 25, 53, 59, 15, 5, 52, 131, 255, 198, 8, 98, 41, 184, 229, 237, + 140, 215, 52, 129, 211, 214, 90, 145, 237, 137, 154}}, + {name: "header/split/parent/container/empty value", err: "invalid header: invalid split header: invalid parent header: invalid container: invalid length 0", + b: []byte{26, 6, 90, 4, 34, 2, 18, 0}}, + {name: "header/split/parent/container/undersize", err: "invalid header: invalid split header: invalid parent header: invalid container: invalid length 31", + b: []byte{26, 39, 90, 37, 34, 35, 18, 33, 10, 31, 245, 94, 164, 207, 217, 233, 175, 75, 123, 153, 174, 8, 20, 135, + 96, 204, 179, 93, 183, 250, 180, 255, 162, 182, 222, 220, 99, 125, 136, 117, 206}}, + {name: "header/split/parent/container/oversize", err: "invalid header: invalid split header: invalid parent header: invalid container: invalid length 33", + b: []byte{26, 41, 90, 39, 34, 37, 18, 35, 10, 33, 245, 94, 164, 207, 217, 233, 175, 75, 123, 153, 174, 8, 20, 135, + 96, 204, 179, 93, 183, 250, 180, 255, 162, 182, 222, 220, 99, 125, 136, 117, 206, 34, 1}}, + {name: "header/split/parent/container/zero", err: "invalid header: invalid split header: invalid parent header: invalid container: zero container ID", + b: []byte{26, 40, 90, 38, 34, 36, 18, 34, 10, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, + {name: "header/split/parent/payload checksum/missing value", err: "invalid header: invalid split header: invalid parent header: invalid payload checksum: missing value", + b: []byte{26, 6, 90, 4, 34, 2, 50, 0}}, + {name: "header/split/parent/payload homomorphic checksum/missing value", err: "invalid header: invalid split header: invalid parent header: invalid payload homomorphic checksum: missing value", + b: []byte{26, 6, 90, 4, 34, 2, 66, 0}}, + {name: "header/split/parent/session/body/ID/undersize", err: "invalid header: invalid split header: invalid parent header: invalid session token: invalid session ID: invalid UUID (got 15 bytes)", + b: []byte{26, 160, 2, 90, 157, 2, 34, 154, 2, 74, 151, 2, 10, 233, 1, 10, 15, 118, 23, 219, 249, 117, 70, 64, 33, + 157, 229, 102, 253, 142, 52, 17, 18, 27, 10, 25, 53, 248, 195, 15, 196, 254, 124, 23, 169, 198, 208, 15, 219, + 229, 62, 150, 151, 159, 221, 73, 224, 229, 106, 42, 222, 26, 32, 8, 210, 204, 150, 183, 128, 222, 183, + 128, 228, 1, 16, 154, 222, 137, 183, 154, 198, 183, 155, 239, 1, 24, 186, 149, 174, 136, 210, 128, 205, 181, + 110, 34, 33, 2, 154, 144, 131, 197, 214, 154, 163, 35, 93, 133, 107, 35, 109, 3, 218, 20, 0, 255, 26, 192, 33, + 236, 0, 240, 183, 253, 76, 187, 136, 215, 164, 217, 42, 116, 8, 199, 202, 174, 243, 3, 18, 108, 10, 34, 10, + 32, 135, 89, 149, 219, 185, 209, 233, 137, 224, 211, 141, 70, 193, 205, 248, 254, 226, 30, 114, 177, 245, + 171, 29, 90, 212, 15, 51, 86, 142, 101, 155, 141, 18, 34, 10, 32, 57, 171, 109, 41, 105, 25, 146, 224, 164, 89, + 61, 178, 179, 158, 67, 40, 32, 154, 122, 174, 117, 221, 138, 168, 135, 149, 238, 61, 68, 58, 34, 189, 18, 34, 10, + 32, 110, 233, 102, 232, 136, 68, 233, 22, 158, 100, 49, 20, 181, 95, 219, 143, 53, 250, 237, 113, 64, 25, + 48, 11, 54, 207, 56, 98, 99, 136, 207, 21, 18, 41, 10, 14, 115, 101, 115, 115, 105, 111, 110, 95, 115, 105, 103, 110, + 101, 114, 18, 17, 115, 101, 115, 115, 105, 111, 110, 32, 115, 105, 103, 110, 97, 116, 117, 114, 101, 24, 170, 137, 252, 156, 4}}, + {name: "header/split/parent/session/body/ID/oversize", err: "invalid header: invalid split header: invalid parent header: invalid session token: invalid session ID: invalid UUID (got 17 bytes)", + b: []byte{26, 162, 2, 90, 159, 2, 34, 156, 2, 74, 153, 2, 10, 235, 1, 10, 17, 118, 23, 219, 249, 117, 70, 64, 33, + 157, 229, 102, 253, 142, 52, 17, 144, 1, 18, 27, 10, 25, 53, 248, 195, 15, 196, 254, 124, 23, 169, 198, 208, + 15, 219, 229, 62, 150, 151, 159, 221, 73, 224, 229, 106, 42, 222, 26, 32, 8, 210, 204, 150, 183, 128, + 222, 183, 128, 228, 1, 16, 154, 222, 137, 183, 154, 198, 183, 155, 239, 1, 24, 186, 149, 174, 136, 210, 128, + 205, 181, 110, 34, 33, 2, 154, 144, 131, 197, 214, 154, 163, 35, 93, 133, 107, 35, 109, 3, 218, 20, 0, 255, 26, + 192, 33, 236, 0, 240, 183, 253, 76, 187, 136, 215, 164, 217, 42, 116, 8, 199, 202, 174, 243, 3, 18, 108, 10, + 34, 10, 32, 135, 89, 149, 219, 185, 209, 233, 137, 224, 211, 141, 70, 193, 205, 248, 254, 226, 30, 114, 177, + 245, 171, 29, 90, 212, 15, 51, 86, 142, 101, 155, 141, 18, 34, 10, 32, 57, 171, 109, 41, 105, 25, 146, 224, 164, + 89, 61, 178, 179, 158, 67, 40, 32, 154, 122, 174, 117, 221, 138, 168, 135, 149, 238, 61, 68, 58, 34, 189, 18, 34, + 10, 32, 110, 233, 102, 232, 136, 68, 233, 22, 158, 100, 49, 20, 181, 95, 219, 143, 53, 250, 237, 113, 64, + 25, 48, 11, 54, 207, 56, 98, 99, 136, 207, 21, 18, 41, 10, 14, 115, 101, 115, 115, 105, 111, 110, 95, 115, 105, 103, + 110, 101, 114, 18, 17, 115, 101, 115, 115, 105, 111, 110, 32, 115, 105, 103, 110, 97, 116, 117, 114, 101, 24, 170, 137, + 252, 156, 4}}, + {name: "header/split/parent/session/body/ID/wrong UUID version", err: "invalid header: invalid split header: invalid parent header: invalid session token: invalid session UUID version 3", + b: []byte{26, 161, 2, 90, 158, 2, 34, 155, 2, 74, 152, 2, 10, 234, 1, 10, 16, 118, 23, 219, 249, 117, 70, 48, 33, + 157, 229, 102, 253, 142, 52, 17, 144, 18, 27, 10, 25, 53, 248, 195, 15, 196, 254, 124, 23, 169, 198, 208, 15, + 219, 229, 62, 150, 151, 159, 221, 73, 224, 229, 106, 42, 222, 26, 32, 8, 210, 204, 150, 183, 128, 222, + 183, 128, 228, 1, 16, 154, 222, 137, 183, 154, 198, 183, 155, 239, 1, 24, 186, 149, 174, 136, 210, 128, 205, + 181, 110, 34, 33, 2, 154, 144, 131, 197, 214, 154, 163, 35, 93, 133, 107, 35, 109, 3, 218, 20, 0, 255, 26, 192, + 33, 236, 0, 240, 183, 253, 76, 187, 136, 215, 164, 217, 42, 116, 8, 199, 202, 174, 243, 3, 18, 108, 10, 34, 10, + 32, 135, 89, 149, 219, 185, 209, 233, 137, 224, 211, 141, 70, 193, 205, 248, 254, 226, 30, 114, 177, 245, 171, + 29, 90, 212, 15, 51, 86, 142, 101, 155, 141, 18, 34, 10, 32, 57, 171, 109, 41, 105, 25, 146, 224, 164, 89, 61, 178, + 179, 158, 67, 40, 32, 154, 122, 174, 117, 221, 138, 168, 135, 149, 238, 61, 68, 58, 34, 189, 18, 34, 10, 32, 110, + 233, 102, 232, 136, 68, 233, 22, 158, 100, 49, 20, 181, 95, 219, 143, 53, 250, 237, 113, 64, 25, 48, 11, 54, + 207, 56, 98, 99, 136, 207, 21, 18, 41, 10, 14, 115, 101, 115, 115, 105, 111, 110, 95, 115, 105, 103, 110, 101, 114, 18, + 17, 115, 101, 115, 115, 105, 111, 110, 32, 115, 105, 103, 110, 97, 116, 117, 114, 101, 24, 170, 137, 252, 156, 4}}, + {name: "header/split/parent/session/body/issuer/value/empty", err: "invalid header: invalid split header: invalid parent header: invalid session token: invalid session issuer: invalid length 0, expected 25", + b: []byte{26, 134, 2, 90, 131, 2, 34, 128, 2, 74, 253, 1, 10, 207, 1, 10, 16, 118, 23, 219, 249, 117, 70, 64, 33, + 157, 229, 102, 253, 142, 52, 17, 144, 18, 0, 26, 32, 8, 210, 204, 150, 183, 128, 222, 183, 128, 228, 1, + 16, 154, 222, 137, 183, 154, 198, 183, 155, 239, 1, 24, 186, 149, 174, 136, 210, 128, 205, 181, 110, 34, 33, 2, + 154, 144, 131, 197, 214, 154, 163, 35, 93, 133, 107, 35, 109, 3, 218, 20, 0, 255, 26, 192, 33, 236, 0, 240, + 183, 253, 76, 187, 136, 215, 164, 217, 42, 116, 8, 199, 202, 174, 243, 3, 18, 108, 10, 34, 10, 32, 135, 89, 149, + 219, 185, 209, 233, 137, 224, 211, 141, 70, 193, 205, 248, 254, 226, 30, 114, 177, 245, 171, 29, 90, 212, 15, + 51, 86, 142, 101, 155, 141, 18, 34, 10, 32, 57, 171, 109, 41, 105, 25, 146, 224, 164, 89, 61, 178, 179, 158, 67, 40, + 32, 154, 122, 174, 117, 221, 138, 168, 135, 149, 238, 61, 68, 58, 34, 189, 18, 34, 10, 32, 110, 233, 102, 232, + 136, 68, 233, 22, 158, 100, 49, 20, 181, 95, 219, 143, 53, 250, 237, 113, 64, 25, 48, 11, 54, 207, 56, 98, 99, + 136, 207, 21, 18, 41, 10, 14, 115, 101, 115, 115, 105, 111, 110, 95, 115, 105, 103, 110, 101, 114, 18, 17, 115, 101, 115, + 115, 105, 111, 110, 32, 115, 105, 103, 110, 97, 116, 117, 114, 101, 24, 170, 137, 252, 156, 4}}, + {name: "header/split/parent/session/body/issuer/value/undersize", err: "invalid header: invalid split header: invalid parent header: invalid session token: invalid session issuer: invalid length 24, expected 25", + b: []byte{26, 160, 2, 90, 157, 2, 34, 154, 2, 74, 151, 2, 10, 233, 1, 10, 16, 118, 23, 219, 249, 117, 70, 64, 33, 157, + 229, 102, 253, 142, 52, 17, 144, 18, 26, 10, 24, 53, 59, 15, 5, 52, 131, 255, 198, 8, 98, 41, 184, 229, 237, + 140, 215, 52, 129, 211, 214, 90, 145, 237, 137, 26, 32, 8, 210, 204, 150, 183, 128, 222, 183, 128, 228, 1, + 16, 154, 222, 137, 183, 154, 198, 183, 155, 239, 1, 24, 186, 149, 174, 136, 210, 128, 205, 181, 110, 34, 33, 2, + 154, 144, 131, 197, 214, 154, 163, 35, 93, 133, 107, 35, 109, 3, 218, 20, 0, 255, 26, 192, 33, 236, 0, 240, 183, + 253, 76, 187, 136, 215, 164, 217, 42, 116, 8, 199, 202, 174, 243, 3, 18, 108, 10, 34, 10, 32, 135, 89, 149, 219, + 185, 209, 233, 137, 224, 211, 141, 70, 193, 205, 248, 254, 226, 30, 114, 177, 245, 171, 29, 90, 212, 15, 51, + 86, 142, 101, 155, 141, 18, 34, 10, 32, 57, 171, 109, 41, 105, 25, 146, 224, 164, 89, 61, 178, 179, 158, 67, 40, 32, + 154, 122, 174, 117, 221, 138, 168, 135, 149, 238, 61, 68, 58, 34, 189, 18, 34, 10, 32, 110, 233, 102, 232, 136, + 68, 233, 22, 158, 100, 49, 20, 181, 95, 219, 143, 53, 250, 237, 113, 64, 25, 48, 11, 54, 207, 56, 98, 99, 136, + 207, 21, 18, 41, 10, 14, 115, 101, 115, 115, 105, 111, 110, 95, 115, 105, 103, 110, 101, 114, 18, 17, 115, 101, 115, 115, + 105, 111, 110, 32, 115, 105, 103, 110, 97, 116, 117, 114, 101, 24, 170, 137, 252, 156, 4}}, + {name: "header/split/parent/session/body/issuer/value/oversize", err: "invalid header: invalid split header: invalid parent header: invalid session token: invalid session issuer: invalid length 26, expected 25", + b: []byte{26, 162, 2, 90, 159, 2, 34, 156, 2, 74, 153, 2, 10, 235, 1, 10, 16, 118, 23, 219, 249, 117, 70, 64, 33, + 157, 229, 102, 253, 142, 52, 17, 144, 18, 28, 10, 26, 53, 59, 15, 5, 52, 131, 255, 198, 8, 98, 41, 184, 229, + 237, 140, 215, 52, 129, 211, 214, 90, 145, 237, 137, 153, 1, 26, 32, 8, 210, 204, 150, 183, 128, 222, 183, + 128, 228, 1, 16, 154, 222, 137, 183, 154, 198, 183, 155, 239, 1, 24, 186, 149, 174, 136, 210, 128, 205, 181, + 110, 34, 33, 2, 154, 144, 131, 197, 214, 154, 163, 35, 93, 133, 107, 35, 109, 3, 218, 20, 0, 255, 26, 192, 33, + 236, 0, 240, 183, 253, 76, 187, 136, 215, 164, 217, 42, 116, 8, 199, 202, 174, 243, 3, 18, 108, 10, 34, 10, + 32, 135, 89, 149, 219, 185, 209, 233, 137, 224, 211, 141, 70, 193, 205, 248, 254, 226, 30, 114, 177, 245, + 171, 29, 90, 212, 15, 51, 86, 142, 101, 155, 141, 18, 34, 10, 32, 57, 171, 109, 41, 105, 25, 146, 224, 164, 89, + 61, 178, 179, 158, 67, 40, 32, 154, 122, 174, 117, 221, 138, 168, 135, 149, 238, 61, 68, 58, 34, 189, 18, 34, 10, + 32, 110, 233, 102, 232, 136, 68, 233, 22, 158, 100, 49, 20, 181, 95, 219, 143, 53, 250, 237, 113, 64, 25, + 48, 11, 54, 207, 56, 98, 99, 136, 207, 21, 18, 41, 10, 14, 115, 101, 115, 115, 105, 111, 110, 95, 115, 105, 103, 110, + 101, 114, 18, 17, 115, 101, 115, 115, 105, 111, 110, 32, 115, 105, 103, 110, 97, 116, 117, 114, 101, 24, 170, 137, 252, + 156, 4}}, + {name: "header/split/parent/session/body/issuer/value/wrong prefix", err: "invalid header: invalid split header: invalid parent header: invalid session token: invalid session issuer: invalid prefix byte 0x42, expected 0x35", + b: []byte{26, 161, 2, 90, 158, 2, 34, 155, 2, 74, 152, 2, 10, 234, 1, 10, 16, 118, 23, 219, 249, 117, 70, 64, 33, + 157, 229, 102, 253, 142, 52, 17, 144, 18, 27, 10, 25, 66, 248, 195, 15, 196, 254, 124, 23, 169, 198, 208, 15, + 219, 229, 62, 150, 151, 159, 221, 73, 224, 229, 106, 42, 222, 26, 32, 8, 210, 204, 150, 183, 128, 222, + 183, 128, 228, 1, 16, 154, 222, 137, 183, 154, 198, 183, 155, 239, 1, 24, 186, 149, 174, 136, 210, 128, 205, + 181, 110, 34, 33, 2, 154, 144, 131, 197, 214, 154, 163, 35, 93, 133, 107, 35, 109, 3, 218, 20, 0, 255, 26, 192, + 33, 236, 0, 240, 183, 253, 76, 187, 136, 215, 164, 217, 42, 116, 8, 199, 202, 174, 243, 3, 18, 108, 10, 34, 10, + 32, 135, 89, 149, 219, 185, 209, 233, 137, 224, 211, 141, 70, 193, 205, 248, 254, 226, 30, 114, 177, 245, 171, + 29, 90, 212, 15, 51, 86, 142, 101, 155, 141, 18, 34, 10, 32, 57, 171, 109, 41, 105, 25, 146, 224, 164, 89, 61, 178, + 179, 158, 67, 40, 32, 154, 122, 174, 117, 221, 138, 168, 135, 149, 238, 61, 68, 58, 34, 189, 18, 34, 10, 32, 110, + 233, 102, 232, 136, 68, 233, 22, 158, 100, 49, 20, 181, 95, 219, 143, 53, 250, 237, 113, 64, 25, 48, 11, 54, + 207, 56, 98, 99, 136, 207, 21, 18, 41, 10, 14, 115, 101, 115, 115, 105, 111, 110, 95, 115, 105, 103, 110, 101, 114, 18, + 17, 115, 101, 115, 115, 105, 111, 110, 32, 115, 105, 103, 110, 97, 116, 117, 114, 101, 24, 170, 137, 252, 156, 4}}, + {name: "header/split/parent/session/body/issuer/value/checksum mismatch", err: "invalid header: invalid split header: invalid parent header: invalid session token: invalid session issuer: checksum mismatch", + b: []byte{26, 161, 2, 90, 158, 2, 34, 155, 2, 74, 152, 2, 10, 234, 1, 10, 16, 118, 23, 219, 249, 117, 70, 64, 33, + 157, 229, 102, 253, 142, 52, 17, 144, 18, 27, 10, 25, 53, 248, 195, 15, 196, 254, 124, 23, 169, 198, 208, 15, + 219, 229, 62, 150, 151, 159, 221, 73, 224, 229, 106, 42, 223, 26, 32, 8, 210, 204, 150, 183, 128, 222, + 183, 128, 228, 1, 16, 154, 222, 137, 183, 154, 198, 183, 155, 239, 1, 24, 186, 149, 174, 136, 210, 128, 205, + 181, 110, 34, 33, 2, 154, 144, 131, 197, 214, 154, 163, 35, 93, 133, 107, 35, 109, 3, 218, 20, 0, 255, 26, 192, + 33, 236, 0, 240, 183, 253, 76, 187, 136, 215, 164, 217, 42, 116, 8, 199, 202, 174, 243, 3, 18, 108, 10, 34, 10, + 32, 135, 89, 149, 219, 185, 209, 233, 137, 224, 211, 141, 70, 193, 205, 248, 254, 226, 30, 114, 177, 245, 171, + 29, 90, 212, 15, 51, 86, 142, 101, 155, 141, 18, 34, 10, 32, 57, 171, 109, 41, 105, 25, 146, 224, 164, 89, 61, 178, + 179, 158, 67, 40, 32, 154, 122, 174, 117, 221, 138, 168, 135, 149, 238, 61, 68, 58, 34, 189, 18, 34, 10, 32, 110, + 233, 102, 232, 136, 68, 233, 22, 158, 100, 49, 20, 181, 95, 219, 143, 53, 250, 237, 113, 64, 25, 48, 11, 54, + 207, 56, 98, 99, 136, 207, 21, 18, 41, 10, 14, 115, 101, 115, 115, 105, 111, 110, 95, 115, 105, 103, 110, 101, 114, 18, + 17, 115, 101, 115, 115, 105, 111, 110, 32, 115, 105, 103, 110, 97, 116, 117, 114, 101, 24, 170, 137, 252, 156, 4}}, + {name: "header/split/parent/session/body/context/wrong oneof", err: "invalid header: invalid split header: invalid parent header: invalid session token: invalid context: invalid context *session.ContainerSessionContext", + b: []byte{26, 172, 1, 90, 169, 1, 34, 166, 1, 74, 163, 1, 10, 118, 10, 16, 118, 23, 219, 249, 117, 70, 64, 33, 157, 229, + 102, 253, 142, 52, 17, 144, 18, 27, 10, 25, 53, 248, 195, 15, 196, 254, 124, 23, 169, 198, 208, 15, 219, 229, + 62, 150, 151, 159, 221, 73, 224, 229, 106, 42, 222, 26, 32, 8, 210, 204, 150, 183, 128, 222, 183, 128, + 228, 1, 16, 154, 222, 137, 183, 154, 198, 183, 155, 239, 1, 24, 186, 149, 174, 136, 210, 128, 205, 181, 110, 34, + 33, 2, 154, 144, 131, 197, 214, 154, 163, 35, 93, 133, 107, 35, 109, 3, 218, 20, 0, 255, 26, 192, 33, 236, 0, + 240, 183, 253, 76, 187, 136, 215, 164, 217, 50, 0, 18, 41, 10, 14, 115, 101, 115, 115, 105, 111, 110, 95, 115, 105, + 103, 110, 101, 114, 18, 17, 115, 101, 115, 115, 105, 111, 110, 32, 115, 105, 103, 110, 97, 116, 117, 114, 101, 24, 170, + 137, 252, 156, 4}}, + {name: "header/split/parent/session/body/context/container/empty value", err: "invalid header: invalid split header: invalid parent header: invalid session token: invalid context: invalid container ID: invalid length 0", + b: []byte{26, 255, 1, 90, 252, 1, 34, 249, 1, 74, 246, 1, 10, 200, 1, 10, 16, 118, 23, 219, 249, 117, 70, 64, 33, + 157, 229, 102, 253, 142, 52, 17, 144, 18, 27, 10, 25, 53, 248, 195, 15, 196, 254, 124, 23, 169, 198, 208, 15, + 219, 229, 62, 150, 151, 159, 221, 73, 224, 229, 106, 42, 222, 26, 32, 8, 210, 204, 150, 183, 128, 222, + 183, 128, 228, 1, 16, 154, 222, 137, 183, 154, 198, 183, 155, 239, 1, 24, 186, 149, 174, 136, 210, 128, 205, + 181, 110, 34, 33, 2, 154, 144, 131, 197, 214, 154, 163, 35, 93, 133, 107, 35, 109, 3, 218, 20, 0, 255, 26, 192, + 33, 236, 0, 240, 183, 253, 76, 187, 136, 215, 164, 217, 42, 82, 8, 199, 202, 174, 243, 3, 18, 74, 10, 0, 18, + 34, 10, 32, 57, 171, 109, 41, 105, 25, 146, 224, 164, 89, 61, 178, 179, 158, 67, 40, 32, 154, 122, 174, 117, 221, + 138, 168, 135, 149, 238, 61, 68, 58, 34, 189, 18, 34, 10, 32, 110, 233, 102, 232, 136, 68, 233, 22, 158, 100, + 49, 20, 181, 95, 219, 143, 53, 250, 237, 113, 64, 25, 48, 11, 54, 207, 56, 98, 99, 136, 207, 21, 18, 41, 10, 14, + 115, 101, 115, 115, 105, 111, 110, 95, 115, 105, 103, 110, 101, 114, 18, 17, 115, 101, 115, 115, 105, 111, 110, 32, 115, 105, + 103, 110, 97, 116, 117, 114, 101, 24, 170, 137, 252, 156, 4}}, + {name: "header/split/parent/session/body/context/container/undersize", err: "invalid header: invalid split header: invalid parent header: invalid session token: invalid context: invalid container ID: invalid length 31", + b: []byte{26, 160, 2, 90, 157, 2, 34, 154, 2, 74, 151, 2, 10, 233, 1, 10, 16, 118, 23, 219, 249, 117, 70, 64, 33, 157, + 229, 102, 253, 142, 52, 17, 144, 18, 27, 10, 25, 53, 248, 195, 15, 196, 254, 124, 23, 169, 198, 208, 15, 219, + 229, 62, 150, 151, 159, 221, 73, 224, 229, 106, 42, 222, 26, 32, 8, 210, 204, 150, 183, 128, 222, 183, + 128, 228, 1, 16, 154, 222, 137, 183, 154, 198, 183, 155, 239, 1, 24, 186, 149, 174, 136, 210, 128, 205, 181, + 110, 34, 33, 2, 154, 144, 131, 197, 214, 154, 163, 35, 93, 133, 107, 35, 109, 3, 218, 20, 0, 255, 26, 192, 33, + 236, 0, 240, 183, 253, 76, 187, 136, 215, 164, 217, 42, 115, 8, 199, 202, 174, 243, 3, 18, 107, 10, 33, 10, 31, + 245, 94, 164, 207, 217, 233, 175, 75, 123, 153, 174, 8, 20, 135, 96, 204, 179, 93, 183, 250, 180, 255, 162, + 182, 222, 220, 99, 125, 136, 117, 206, 18, 34, 10, 32, 57, 171, 109, 41, 105, 25, 146, 224, 164, 89, 61, 178, + 179, 158, 67, 40, 32, 154, 122, 174, 117, 221, 138, 168, 135, 149, 238, 61, 68, 58, 34, 189, 18, 34, 10, 32, 110, + 233, 102, 232, 136, 68, 233, 22, 158, 100, 49, 20, 181, 95, 219, 143, 53, 250, 237, 113, 64, 25, 48, 11, 54, + 207, 56, 98, 99, 136, 207, 21, 18, 41, 10, 14, 115, 101, 115, 115, 105, 111, 110, 95, 115, 105, 103, 110, 101, 114, 18, + 17, 115, 101, 115, 115, 105, 111, 110, 32, 115, 105, 103, 110, 97, 116, 117, 114, 101, 24, 170, 137, 252, 156, 4}}, + {name: "header/split/parent/session/body/context/container/oversize", err: "invalid header: invalid split header: invalid parent header: invalid session token: invalid context: invalid container ID: invalid length 33", + b: []byte{26, 162, 2, 90, 159, 2, 34, 156, 2, 74, 153, 2, 10, 235, 1, 10, 16, 118, 23, 219, 249, 117, 70, 64, 33, + 157, 229, 102, 253, 142, 52, 17, 144, 18, 27, 10, 25, 53, 248, 195, 15, 196, 254, 124, 23, 169, 198, 208, 15, + 219, 229, 62, 150, 151, 159, 221, 73, 224, 229, 106, 42, 222, 26, 32, 8, 210, 204, 150, 183, 128, 222, + 183, 128, 228, 1, 16, 154, 222, 137, 183, 154, 198, 183, 155, 239, 1, 24, 186, 149, 174, 136, 210, 128, 205, + 181, 110, 34, 33, 2, 154, 144, 131, 197, 214, 154, 163, 35, 93, 133, 107, 35, 109, 3, 218, 20, 0, 255, 26, 192, + 33, 236, 0, 240, 183, 253, 76, 187, 136, 215, 164, 217, 42, 117, 8, 199, 202, 174, 243, 3, 18, 109, 10, 35, 10, + 33, 245, 94, 164, 207, 217, 233, 175, 75, 123, 153, 174, 8, 20, 135, 96, 204, 179, 93, 183, 250, 180, 255, 162, + 182, 222, 220, 99, 125, 136, 117, 206, 34, 1, 18, 34, 10, 32, 57, 171, 109, 41, 105, 25, 146, 224, 164, 89, 61, + 178, 179, 158, 67, 40, 32, 154, 122, 174, 117, 221, 138, 168, 135, 149, 238, 61, 68, 58, 34, 189, 18, 34, 10, 32, + 110, 233, 102, 232, 136, 68, 233, 22, 158, 100, 49, 20, 181, 95, 219, 143, 53, 250, 237, 113, 64, 25, 48, 11, + 54, 207, 56, 98, 99, 136, 207, 21, 18, 41, 10, 14, 115, 101, 115, 115, 105, 111, 110, 95, 115, 105, 103, 110, 101, 114, + 18, 17, 115, 101, 115, 115, 105, 111, 110, 32, 115, 105, 103, 110, 97, 116, 117, 114, 101, 24, 170, 137, 252, 156, 4}}, + {name: "header/split/parent/session/body/context/object/empty value", err: "invalid header: invalid split header: invalid parent header: invalid session token: invalid context: invalid target object: invalid length 0", + b: []byte{26, 255, 1, 90, 252, 1, 34, 249, 1, 74, 246, 1, 10, 200, 1, 10, 16, 118, 23, 219, 249, 117, 70, 64, 33, + 157, 229, 102, 253, 142, 52, 17, 144, 18, 27, 10, 25, 53, 248, 195, 15, 196, 254, 124, 23, 169, 198, 208, 15, + 219, 229, 62, 150, 151, 159, 221, 73, 224, 229, 106, 42, 222, 26, 32, 8, 210, 204, 150, 183, 128, 222, + 183, 128, 228, 1, 16, 154, 222, 137, 183, 154, 198, 183, 155, 239, 1, 24, 186, 149, 174, 136, 210, 128, 205, + 181, 110, 34, 33, 2, 154, 144, 131, 197, 214, 154, 163, 35, 93, 133, 107, 35, 109, 3, 218, 20, 0, 255, 26, 192, + 33, 236, 0, 240, 183, 253, 76, 187, 136, 215, 164, 217, 42, 82, 8, 199, 202, 174, 243, 3, 18, 74, 10, 34, 10, + 32, 135, 89, 149, 219, 185, 209, 233, 137, 224, 211, 141, 70, 193, 205, 248, 254, 226, 30, 114, 177, 245, 171, + 29, 90, 212, 15, 51, 86, 142, 101, 155, 141, 18, 34, 10, 32, 57, 171, 109, 41, 105, 25, 146, 224, 164, 89, 61, 178, + 179, 158, 67, 40, 32, 154, 122, 174, 117, 221, 138, 168, 135, 149, 238, 61, 68, 58, 34, 189, 18, 0, 18, 41, 10, 14, + 115, 101, 115, 115, 105, 111, 110, 95, 115, 105, 103, 110, 101, 114, 18, 17, 115, 101, 115, 115, 105, 111, 110, 32, 115, 105, + 103, 110, 97, 116, 117, 114, 101, 24, 170, 137, 252, 156, 4}}, + {name: "header/split/parent/session/body/context/object/undersize", err: "invalid header: invalid split header: invalid parent header: invalid session token: invalid context: invalid target object: invalid length 31", + b: []byte{26, 160, 2, 90, 157, 2, 34, 154, 2, 74, 151, 2, 10, 233, 1, 10, 16, 118, 23, 219, 249, 117, 70, 64, 33, 157, + 229, 102, 253, 142, 52, 17, 144, 18, 27, 10, 25, 53, 248, 195, 15, 196, 254, 124, 23, 169, 198, 208, 15, 219, + 229, 62, 150, 151, 159, 221, 73, 224, 229, 106, 42, 222, 26, 32, 8, 210, 204, 150, 183, 128, 222, 183, + 128, 228, 1, 16, 154, 222, 137, 183, 154, 198, 183, 155, 239, 1, 24, 186, 149, 174, 136, 210, 128, 205, 181, 110, + 34, 33, 2, 154, 144, 131, 197, 214, 154, 163, 35, 93, 133, 107, 35, 109, 3, 218, 20, 0, 255, 26, 192, 33, 236, + 0, 240, 183, 253, 76, 187, 136, 215, 164, 217, 42, 115, 8, 199, 202, 174, 243, 3, 18, 107, 10, 34, 10, 32, 135, + 89, 149, 219, 185, 209, 233, 137, 224, 211, 141, 70, 193, 205, 248, 254, 226, 30, 114, 177, 245, 171, 29, 90, + 212, 15, 51, 86, 142, 101, 155, 141, 18, 34, 10, 32, 57, 171, 109, 41, 105, 25, 146, 224, 164, 89, 61, 178, 179, + 158, 67, 40, 32, 154, 122, 174, 117, 221, 138, 168, 135, 149, 238, 61, 68, 58, 34, 189, 18, 33, 10, 31, 178, 74, + 58, 219, 46, 3, 110, 125, 220, 81, 238, 35, 27, 6, 228, 193, 190, 224, 77, 44, 18, 56, 117, 173, 70, 246, 8, + 139, 247, 174, 53, 18, 41, 10, 14, 115, 101, 115, 115, 105, 111, 110, 95, 115, 105, 103, 110, 101, 114, 18, 17, 115, 101, + 115, 115, 105, 111, 110, 32, 115, 105, 103, 110, 97, 116, 117, 114, 101, 24, 170, 137, 252, 156, 4}}, + {name: "header/split/parent/session/body/context/object/oversize", err: "invalid header: invalid split header: invalid parent header: invalid session token: invalid context: invalid target object: invalid length 33", + b: []byte{26, 162, 2, 90, 159, 2, 34, 156, 2, 74, 153, 2, 10, 235, 1, 10, 16, 118, 23, 219, 249, 117, 70, 64, 33, + 157, 229, 102, 253, 142, 52, 17, 144, 18, 27, 10, 25, 53, 248, 195, 15, 196, 254, 124, 23, 169, 198, 208, 15, + 219, 229, 62, 150, 151, 159, 221, 73, 224, 229, 106, 42, 222, 26, 32, 8, 210, 204, 150, 183, 128, 222, + 183, 128, 228, 1, 16, 154, 222, 137, 183, 154, 198, 183, 155, 239, 1, 24, 186, 149, 174, 136, 210, 128, 205, + 181, 110, 34, 33, 2, 154, 144, 131, 197, 214, 154, 163, 35, 93, 133, 107, 35, 109, 3, 218, 20, 0, 255, 26, 192, + 33, 236, 0, 240, 183, 253, 76, 187, 136, 215, 164, 217, 42, 117, 8, 199, 202, 174, 243, 3, 18, 109, 10, 34, 10, + 32, 135, 89, 149, 219, 185, 209, 233, 137, 224, 211, 141, 70, 193, 205, 248, 254, 226, 30, 114, 177, 245, 171, + 29, 90, 212, 15, 51, 86, 142, 101, 155, 141, 18, 34, 10, 32, 57, 171, 109, 41, 105, 25, 146, 224, 164, 89, 61, 178, + 179, 158, 67, 40, 32, 154, 122, 174, 117, 221, 138, 168, 135, 149, 238, 61, 68, 58, 34, 189, 18, 35, 10, 33, 178, + 74, 58, 219, 46, 3, 110, 125, 220, 81, 238, 35, 27, 6, 228, 193, 190, 224, 77, 44, 18, 56, 117, 173, 70, 246, + 8, 139, 247, 174, 53, 60, 1, 18, 41, 10, 14, 115, 101, 115, 115, 105, 111, 110, 95, 115, 105, 103, 110, 101, 114, 18, 17, + 115, 101, 115, 115, 105, 111, 110, 32, 115, 105, 103, 110, 97, 116, 117, 114, 101, 24, 170, 137, 252, 156, 4}}, + {name: "header/split/parent/session/signature/scheme/overflow", err: "invalid header: invalid split header: invalid parent header: invalid session token: invalid body signature: scheme 2147483648 overflows int32", + b: []byte{26, 166, 2, 90, 163, 2, 34, 160, 2, 74, 157, 2, 10, 234, 1, 10, 16, 118, 23, 219, 249, 117, 70, 64, 33, + 157, 229, 102, 253, 142, 52, 17, 144, 18, 27, 10, 25, 53, 248, 195, 15, 196, 254, 124, 23, 169, 198, 208, 15, + 219, 229, 62, 150, 151, 159, 221, 73, 224, 229, 106, 42, 222, 26, 32, 8, 210, 204, 150, 183, 128, 222, + 183, 128, 228, 1, 16, 154, 222, 137, 183, 154, 198, 183, 155, 239, 1, 24, 186, 149, 174, 136, 210, 128, 205, + 181, 110, 34, 33, 2, 154, 144, 131, 197, 214, 154, 163, 35, 93, 133, 107, 35, 109, 3, 218, 20, 0, 255, 26, 192, + 33, 236, 0, 240, 183, 253, 76, 187, 136, 215, 164, 217, 42, 116, 8, 199, 202, 174, 243, 3, 18, 108, 10, 34, 10, + 32, 135, 89, 149, 219, 185, 209, 233, 137, 224, 211, 141, 70, 193, 205, 248, 254, 226, 30, 114, 177, 245, 171, + 29, 90, 212, 15, 51, 86, 142, 101, 155, 141, 18, 34, 10, 32, 57, 171, 109, 41, 105, 25, 146, 224, 164, 89, 61, 178, + 179, 158, 67, 40, 32, 154, 122, 174, 117, 221, 138, 168, 135, 149, 238, 61, 68, 58, 34, 189, 18, 34, 10, 32, 110, + 233, 102, 232, 136, 68, 233, 22, 158, 100, 49, 20, 181, 95, 219, 143, 53, 250, 237, 113, 64, 25, 48, 11, 54, + 207, 56, 98, 99, 136, 207, 21, 18, 46, 10, 14, 115, 101, 115, 115, 105, 111, 110, 95, 115, 105, 103, 110, 101, 114, 18, + 17, 115, 101, 115, 115, 105, 111, 110, 32, 115, 105, 103, 110, 97, 116, 117, 114, 101, 24, 128, 128, 128, 128, 248, + 255, 255, 255, 255, 1}}, + {name: "header/split/parent/split/parent ID/empty value", err: "invalid header: invalid split header: invalid parent header: invalid split header: invalid parent split member ID: invalid length 0", + b: []byte{26, 8, 90, 6, 34, 4, 90, 2, 10, 0}}, + {name: "header/split/parent/split/parent ID/undersize", err: "invalid header: invalid split header: invalid parent header: invalid split header: invalid parent split member ID: invalid length 31", + b: []byte{26, 41, 90, 39, 34, 37, 90, 35, 10, 33, 10, 31, 178, 74, 58, 219, 46, 3, 110, 125, 220, 81, 238, 35, 27, + 6, 228, 193, 190, 224, 77, 44, 18, 56, 117, 173, 70, 246, 8, 139, 247, 174, 53}}, + {name: "header/split/parent/split/parent ID/oversize", err: "invalid header: invalid split header: invalid parent header: invalid split header: invalid parent split member ID: invalid length 33", + b: []byte{26, 43, 90, 41, 34, 39, 90, 37, 10, 35, 10, 33, 178, 74, 58, 219, 46, 3, 110, 125, 220, 81, 238, 35, 27, + 6, 228, 193, 190, 224, 77, 44, 18, 56, 117, 173, 70, 246, 8, 139, 247, 174, 53, 60, 1}}, + {name: "header/split/parent/split/parent ID/zero", err: "invalid header: invalid split header: invalid parent header: invalid split header: invalid parent split member ID: zero object ID", + b: []byte{26, 42, 90, 40, 34, 38, 90, 36, 10, 34, 10, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, + {name: "header/split/parent/split/previous/empty value", err: "invalid header: invalid split header: invalid parent header: invalid split header: invalid previous split member ID: invalid length 0", + b: []byte{26, 8, 90, 6, 34, 4, 90, 2, 18, 0}}, + {name: "header/split/parent/split/previous/undersize", err: "invalid header: invalid split header: invalid parent header: invalid split header: invalid previous split member ID: invalid length 31", + b: []byte{26, 41, 90, 39, 34, 37, 90, 35, 18, 33, 10, 31, 178, 74, 58, 219, 46, 3, 110, 125, 220, 81, 238, 35, 27, + 6, 228, 193, 190, 224, 77, 44, 18, 56, 117, 173, 70, 246, 8, 139, 247, 174, 53}}, + {name: "header/split/parent/split/previous/oversize", err: "invalid header: invalid split header: invalid parent header: invalid split header: invalid previous split member ID: invalid length 33", + b: []byte{26, 43, 90, 41, 34, 39, 90, 37, 18, 35, 10, 33, 178, 74, 58, 219, 46, 3, 110, 125, 220, 81, 238, 35, 27, + 6, 228, 193, 190, 224, 77, 44, 18, 56, 117, 173, 70, 246, 8, 139, 247, 174, 53, 60, 1}}, + {name: "header/split/parent/split/previous/zero", err: "invalid header: invalid split header: invalid parent header: invalid split header: invalid previous split member ID: zero object ID", + b: []byte{26, 42, 90, 40, 34, 38, 90, 36, 18, 34, 10, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, + {name: "header/split/parent/split/first/empty value", err: "invalid header: invalid split header: invalid parent header: invalid split header: invalid first split member ID: invalid length 0", + b: []byte{26, 8, 90, 6, 34, 4, 90, 2, 58, 0}}, + {name: "header/split/parent/split/first/undersize", err: "invalid header: invalid split header: invalid parent header: invalid split header: invalid first split member ID: invalid length 31", + b: []byte{26, 41, 90, 39, 34, 37, 90, 35, 58, 33, 10, 31, 178, 74, 58, 219, 46, 3, 110, 125, 220, 81, 238, 35, 27, + 6, 228, 193, 190, 224, 77, 44, 18, 56, 117, 173, 70, 246, 8, 139, 247, 174, 53}}, + {name: "header/split/parent/split/first/oversize", err: "invalid header: invalid split header: invalid parent header: invalid split header: invalid first split member ID: invalid length 33", + b: []byte{26, 43, 90, 41, 34, 39, 90, 37, 58, 35, 10, 33, 178, 74, 58, 219, 46, 3, 110, 125, 220, 81, 238, 35, 27, + 6, 228, 193, 190, 224, 77, 44, 18, 56, 117, 173, 70, 246, 8, 139, 247, 174, 53, 60, 1}}, + {name: "header/split/parent/split/first/zero", err: "invalid header: invalid split header: invalid parent header: invalid split header: invalid first split member ID: zero object ID", + b: []byte{26, 42, 90, 40, 34, 38, 90, 36, 58, 34, 10, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}, + {name: "header/split/parent/split/ID/undersize", err: "invalid header: invalid split header: invalid parent header: invalid split header: invalid split UUID: invalid UUID (got 15 bytes)", + b: []byte{26, 23, 90, 21, 34, 19, 90, 17, 50, 15, 224, 132, 3, 80, 32, 44, 69, 184, 185, 32, 226, 201, 206, 196, 147}}, + {name: "header/split/parent/split/ID/oversize", err: "invalid header: invalid split header: invalid parent header: invalid split header: invalid split UUID: invalid UUID (got 17 bytes)", + b: []byte{26, 25, 90, 23, 34, 21, 90, 19, 50, 17, 224, 132, 3, 80, 32, 44, 69, 184, 185, 32, 226, 201, 206, 196, 147, 41, 1}}, + {name: "header/split/parent/split/ID/wrong UUID version", err: "invalid header: invalid split header: invalid parent header: invalid split header: invalid split UUID version 3", + b: []byte{26, 24, 90, 22, 34, 20, 90, 18, 50, 16, 224, 132, 3, 80, 32, 44, 48, 184, 185, 32, 226, 201, 206, 196, 147, 41}}, + {name: "header/split/parent/split/children/empty value", err: "invalid header: invalid split header: invalid parent header: invalid split header: invalid child split member ID #1: invalid length 0", + b: []byte{26, 80, 90, 78, 34, 76, 90, 74, 42, 34, 10, 32, 178, 74, 58, 219, 46, 3, 110, 125, 220, 81, 238, 35, 27, + 6, 228, 193, 190, 224, 77, 44, 18, 56, 117, 173, 70, 246, 8, 139, 247, 174, 53, 60, 42, 0, 42, 34, 10, 32, + 206, 228, 247, 217, 41, 247, 159, 215, 79, 226, 53, 153, 133, 16, 102, 104, 2, 234, 35, 220, 236, 112, 101, + 24, 235, 126, 173, 229, 161, 202, 197, 242}}, + {name: "header/split/parent/split/children/undersize", err: "invalid header: invalid split header: invalid parent header: invalid split header: invalid child split member ID #1: invalid length 31", + b: []byte{26, 113, 90, 111, 34, 109, 90, 107, 42, 34, 10, 32, 178, 74, 58, 219, 46, 3, 110, 125, 220, 81, 238, 35, + 27, 6, 228, 193, 190, 224, 77, 44, 18, 56, 117, 173, 70, 246, 8, 139, 247, 174, 53, 60, 42, 33, 10, 31, 229, + 77, 63, 235, 2, 9, 165, 123, 116, 123, 47, 65, 22, 34, 214, 76, 45, 225, 21, 46, 135, 32, 116, 172, 67, 213, + 243, 57, 253, 127, 179, 42, 34, 10, 32, 206, 228, 247, 217, 41, 247, 159, 215, 79, 226, 53, 153, 133, 16, + 102, 104, 2, 234, 35, 220, 236, 112, 101, 24, 235, 126, 173, 229, 161, 202, 197, 242}}, + {name: "header/split/parent/split/children/oversize", err: "invalid header: invalid split header: invalid parent header: invalid split header: invalid child split member ID #1: invalid length 33", + b: []byte{26, 115, 90, 113, 34, 111, 90, 109, 42, 34, 10, 32, 178, 74, 58, 219, 46, 3, 110, 125, 220, 81, 238, 35, + 27, 6, 228, 193, 190, 224, 77, 44, 18, 56, 117, 173, 70, 246, 8, 139, 247, 174, 53, 60, 42, 35, 10, 33, 229, + 77, 63, 235, 2, 9, 165, 123, 116, 123, 47, 65, 22, 34, 214, 76, 45, 225, 21, 46, 135, 32, 116, 172, 67, 213, + 243, 57, 253, 127, 179, 235, 1, 42, 34, 10, 32, 206, 228, 247, 217, 41, 247, 159, 215, 79, 226, 53, 153, + 133, 16, 102, 104, 2, 234, 35, 220, 236, 112, 101, 24, 235, 126, 173, 229, 161, 202, 197, 242}}, + {name: "header/split/parent/split/children/zero", err: "invalid header: invalid split header: invalid parent header: invalid split header: invalid child split member ID #1: zero object ID", + b: []byte{26, 114, 90, 112, 34, 110, 90, 108, 42, 34, 10, 32, 178, 74, 58, 219, 46, 3, 110, 125, 220, 81, 238, 35, + 27, 6, 228, 193, 190, 224, 77, 44, 18, 56, 117, 173, 70, 246, 8, 139, 247, 174, 53, 60, 42, 34, 10, 32, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 34, 10, 32, + 206, 228, 247, 217, 41, 247, 159, 215, 79, 226, 53, 153, 133, 16, 102, 104, 2, 234, 35, 220, 236, 112, 101, + 24, 235, 126, 173, 229, 161, 202, 197, 242}}, + {name: "header/split/parent/split/parent signature/scheme/overflow", err: "invalid header: invalid split header: invalid parent header: invalid split header: invalid parent signature: scheme 2147483648 overflows int32", + b: []byte{26, 19, 90, 17, 34, 15, 90, 13, 26, 11, 24, 128, 128, 128, 128, 248, 255, 255, 255, 255, 1}}, + } { + t.Run(tc.name, func(t *testing.T) { + require.EqualError(t, new(object.Object).Unmarshal(tc.b), tc.err) + }) + } + }) + + var obj object.Object + // zero + require.NoError(t, obj.Unmarshal(nil)) + require.Zero(t, obj) + + // filled + require.NoError(t, obj.Unmarshal(validBinObject)) + t.Skip("https://github.com/nspcc-dev/neofs-sdk-go/issues/606") + require.Equal(t, validObject, obj) +} + +func TestObject_MarshalJSON(t *testing.T) { + b, err := json.MarshalIndent(validObject, "", " ") + require.NoError(t, err) + require.JSONEq(t, validJSONObject, string(b)) +} + +func TestObject_UnmarshalJSON(t *testing.T) { + t.Run("invalid", func(t *testing.T) { + t.Run("JSON", func(t *testing.T) { + err := new(object.Object).UnmarshalJSON([]byte("Hello, world!")) + require.ErrorContains(t, err, "proto") + require.ErrorContains(t, err, "syntax error") + }) + for _, tc := range []struct{ name, err, j string }{ + {name: "id/empty value", err: "invalid ID: invalid length 0", + j: `{"objectID":{}}`}, + {name: "id/undersize", err: "invalid ID: invalid length 31", + j: `{"objectID":{"value":"sko62y4Dbn3cUe4jGwbkwb7gTSwSOHWtRvYIi/euNQ=="}}`}, + {name: "id/oversize", err: "invalid ID: invalid length 33", + j: `{"objectID":{"value":"sko62y4Dbn3cUe4jGwbkwb7gTSwSOHWtRvYIi/euNTwB"}}`}, + {name: "id/zero", err: "invalid ID: zero object ID", + j: `{"objectID":{"value":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="}}`}, + {name: "signature/scheme/overflow", err: "invalid signature: scheme 2147483648 overflows int32", + j: `{"signature":{"scheme":-2147483648}}`}, + {name: "header/owner/value/empty", err: "invalid header: invalid owner: invalid length 0, expected 25", + j: `{"header":{"ownerID":{}}}`}, + {name: "header/owner/value/undersize", err: "invalid header: invalid owner: invalid length 24, expected 25", + j: `{"header":{"ownerID":{"value":"NTsPBTSD/8YIYim45e2M1zSB09Zake2J"}}}`}, + {name: "header/owner/value/oversize", err: "invalid header: invalid owner: invalid length 26, expected 25", + j: `{"header":{"ownerID":{"value":"NTsPBTSD/8YIYim45e2M1zSB09Zake2JmQE="}}}`}, + {name: "header/owner/value/wrong prefix", err: "invalid header: invalid owner: invalid prefix byte 0x42, expected 0x35", + j: `{"header":{"ownerID":{"value":"QjsPBTSD/8YIYim45e2M1zSB09Zake2JmQ=="}}}`}, + {name: "header/owner/value/checksum mismatch", err: "invalid header: invalid owner: checksum mismatch", + j: `{"header":{"ownerID":{"value":"NTsPBTSD/8YIYim45e2M1zSB09Zake2Jmg=="}}}`}, + {name: "header/container/empty value", err: "invalid header: invalid container: invalid length 0", + j: `{"header":{"containerID":{}}}`}, + {name: "header/container/undersize", err: "invalid header: invalid container: invalid length 31", + j: `{"header":{"containerID":{"value":"9V6kz9npr0t7ma4IFIdgzLNdt/q0/6K23txjfYh1zg=="}}}`}, + {name: "header/container/oversize", err: "invalid header: invalid container: invalid length 33", + j: `{"header":{"containerID":{"value":"9V6kz9npr0t7ma4IFIdgzLNdt/q0/6K23txjfYh1ziIB"}}}`}, + {name: "header/container/zero", err: "invalid header: invalid container: zero container ID", + j: `{"header":{"containerID":{"value":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="}}}`}, + {name: "header/payload checksum/missing value", err: "invalid header: invalid payload checksum: missing value", + j: `{"header":{"payloadHash":{}}}`}, + {name: "header/payload homomorphic checksum/missing value", err: "invalid header: invalid payload homomorphic checksum: missing value", + j: `{"header":{"homomorphicHash":{}}}`}, + {name: "header/session/body/ID/undersize", err: "invalid header: invalid session token: invalid session ID: invalid UUID (got 15 bytes)", + j: `{"header":{"sessionToken":{"body":{"id":"dhfb+XVGQCGd5Wb9jjQR"}}}}`}, + {name: "header/session/body/ID/oversize", err: "invalid header: invalid session token: invalid session ID: invalid UUID (got 17 bytes)", + j: `{"header":{"sessionToken":{"body":{"id":"dhfb+XVGQCGd5Wb9jjQRkAE="}}}}`}, + {name: "header/session/body/ID/wrong UUID version", err: "invalid header: invalid session token: invalid session UUID version 3", + j: `{"header":{"sessionToken":{"body":{"id":"dhfb+XVGMCGd5Wb9jjQRkA=="}}}}`}, + {name: "header/session/body/issuer/value/empty", err: "invalid header: invalid session token: invalid session issuer: invalid length 0, expected 25", + j: `{"header":{"sessionToken":{"body":{"id":"dhfb+XVGQCGd5Wb9jjQRkA==", "ownerID":{}}}}}`}, + {name: "header/session/body/issuer/value/undersize", err: "invalid header: invalid session token: invalid session issuer: invalid length 24, expected 25", + j: `{"header":{"sessionToken":{"body":{"id":"dhfb+XVGQCGd5Wb9jjQRkA==", "ownerID":{"value":"NTsPBTSD/8YIYim45e2M1zSB09Zake2J"}}}}}`}, + {name: "header/session/body/issuer/value/oversize", err: "invalid header: invalid session token: invalid session issuer: invalid length 26, expected 25", + j: `{"header":{"sessionToken":{"body":{"id":"dhfb+XVGQCGd5Wb9jjQRkA==", "ownerID":{"value":"NTsPBTSD/8YIYim45e2M1zSB09Zake2JmQE="}}}}}`}, + {name: "header/session/body/issuer/value/wrong prefix", err: "invalid header: invalid session token: invalid session issuer: invalid prefix byte 0x42, expected 0x35", + j: `{"header":{"sessionToken":{"body":{"id":"dhfb+XVGQCGd5Wb9jjQRkA==", "ownerID":{"value":"QjsPBTSD/8YIYim45e2M1zSB09Zake2JmQ=="}}}}}`}, + {name: "header/session/body/issuer/value/checksum mismatch", err: "invalid header: invalid session token: invalid session issuer: checksum mismatch", + j: `{"header":{"sessionToken":{"body":{"id":"dhfb+XVGQCGd5Wb9jjQRkA==", "ownerID":{"value":"NTsPBTSD/8YIYim45e2M1zSB09Zake2Jmg=="}}}}}`}, + {name: "header/session/body/context/wrong oneof", err: "invalid header: invalid session token: invalid context: invalid context *session.ContainerSessionContext", + j: ` +{ + "header": { + "sessionToken": { + "body": { + "id": "dhfb+XVGQCGd5Wb9jjQRkA==", + "ownerID": { + "value": "NfjDD8T+fBepxtAP2+U+lpef3Ung5Woq3g==" + }, + "lifetime": { + "exp": "16429376563136800338", + "nbf": "17237208928641773338", + "iat": "7956510363313998522" + }, + "sessionKey": "ApqQg8XWmqMjXYVrI20D2hQA/xrAIewA8Lf9TLuI16TZ", + "container": {} + } + } + } +} +`}, + {name: "header/session/body/context/container/empty value", err: "invalid header: invalid session token: invalid context: invalid container ID: invalid length 0", + j: ` +{ + "header": { + "sessionToken": { + "body": { + "id": "dhfb+XVGQCGd5Wb9jjQRkA==", + "ownerID": { + "value": "NfjDD8T+fBepxtAP2+U+lpef3Ung5Woq3g==" + }, + "lifetime": { + "exp": "16429376563136800338", + "nbf": "17237208928641773338", + "iat": "7956510363313998522" + }, + "sessionKey": "ApqQg8XWmqMjXYVrI20D2hQA/xrAIewA8Lf9TLuI16TZ", + "object": { + "target": { + "container": {} + } + } + } + } + } +} +`}, + {name: "header/session/body/context/container/undersize", err: "invalid header: invalid session token: invalid context: invalid container ID: invalid length 31", + j: ` +{ + "header": { + "sessionToken": { + "body": { + "id": "dhfb+XVGQCGd5Wb9jjQRkA==", + "ownerID": { + "value": "NfjDD8T+fBepxtAP2+U+lpef3Ung5Woq3g==" + }, + "lifetime": { + "exp": "16429376563136800338", + "nbf": "17237208928641773338", + "iat": "7956510363313998522" + }, + "sessionKey": "ApqQg8XWmqMjXYVrI20D2hQA/xrAIewA8Lf9TLuI16TZ", + "object": { + "target": { + "container": { + "value": "9V6kz9npr0t7ma4IFIdgzLNdt/q0/6K23txjfYh1zg==" + } + } + } + } + } + } +} +`}, + {name: "header/session/body/context/container/oversize", err: "invalid header: invalid session token: invalid context: invalid container ID: invalid length 33", + j: ` +{ + "header": { + "sessionToken": { + "body": { + "id": "dhfb+XVGQCGd5Wb9jjQRkA==", + "ownerID": { + "value": "NfjDD8T+fBepxtAP2+U+lpef3Ung5Woq3g==" + }, + "lifetime": { + "exp": "16429376563136800338", + "nbf": "17237208928641773338", + "iat": "7956510363313998522" + }, + "sessionKey": "ApqQg8XWmqMjXYVrI20D2hQA/xrAIewA8Lf9TLuI16TZ", + "object": { + "target": { + "container": { + "value": "9V6kz9npr0t7ma4IFIdgzLNdt/q0/6K23txjfYh1ziIB" + } + } + } + } + } + } +} +`}, + {name: "header/session/body/context/object/empty value", err: "invalid header: invalid session token: invalid context: invalid target object: invalid length 0", + j: ` +{ + "header": { + "sessionToken": { + "body": { + "id": "dhfb+XVGQCGd5Wb9jjQRkA==", + "ownerID": { + "value": "NfjDD8T+fBepxtAP2+U+lpef3Ung5Woq3g==" + }, + "lifetime": { + "exp": "16429376563136800338", + "nbf": "17237208928641773338", + "iat": "7956510363313998522" + }, + "sessionKey": "ApqQg8XWmqMjXYVrI20D2hQA/xrAIewA8Lf9TLuI16TZ", + "object": { + "target": { + "container": { + "value": "h1mV27nR6Yng041Gwc34/uIecrH1qx1a1A8zVo5lm40=" + }, + "objects": [ + {"value": "sko62y4Dbn3cUe4jGwbkwb7gTSwSOHWtRvYIi/euNTw="}, + {}, + {"value": "zuT32Sn3n9dP4jWZhRBmaALqI9zscGUY636t5aHKxfI="} + ] + } + } + } + } + } +} +`}, + {name: "header/session/body/context/object/undersize", err: "invalid header: invalid session token: invalid context: invalid target object: invalid length 31", + j: ` +{ + "header": { + "sessionToken": { + "body": { + "id": "dhfb+XVGQCGd5Wb9jjQRkA==", + "ownerID": { + "value": "NfjDD8T+fBepxtAP2+U+lpef3Ung5Woq3g==" + }, + "lifetime": { + "exp": "16429376563136800338", + "nbf": "17237208928641773338", + "iat": "7956510363313998522" + }, + "sessionKey": "ApqQg8XWmqMjXYVrI20D2hQA/xrAIewA8Lf9TLuI16TZ", + "object": { + "target": { + "container": { + "value": "h1mV27nR6Yng041Gwc34/uIecrH1qx1a1A8zVo5lm40=" + }, + "objects": [ + {"value": "sko62y4Dbn3cUe4jGwbkwb7gTSwSOHWtRvYIi/euNTw="}, + {"value": "5U0/6wIJpXt0ey9BFiLWTC3hFS6HIHSsQ9XzOf1/sw=="}, + {"value": "zuT32Sn3n9dP4jWZhRBmaALqI9zscGUY636t5aHKxfI="} + ] + } + } + } + } + } +} +`}, + {name: "header/session/body/context/object/oversize", err: "invalid header: invalid session token: invalid context: invalid target object: invalid length 33", + j: ` +{ + "header": { + "sessionToken": { + "body": { + "id": "dhfb+XVGQCGd5Wb9jjQRkA==", + "ownerID": { + "value": "NfjDD8T+fBepxtAP2+U+lpef3Ung5Woq3g==" + }, + "lifetime": { + "exp": "16429376563136800338", + "nbf": "17237208928641773338", + "iat": "7956510363313998522" + }, + "sessionKey": "ApqQg8XWmqMjXYVrI20D2hQA/xrAIewA8Lf9TLuI16TZ", + "object": { + "target": { + "container": { + "value": "h1mV27nR6Yng041Gwc34/uIecrH1qx1a1A8zVo5lm40=" + }, + "objects": [ + {"value": "sko62y4Dbn3cUe4jGwbkwb7gTSwSOHWtRvYIi/euNTw="}, + {"value": "5U0/6wIJpXt0ey9BFiLWTC3hFS6HIHSsQ9XzOf1/s+sB"}, + {"value": "zuT32Sn3n9dP4jWZhRBmaALqI9zscGUY636t5aHKxfI="} + ] + } + } + } + } + } +} +`}, + {name: "header/session/signature/scheme/overflow", err: "invalid header: invalid session token: invalid body signature: scheme 2147483648 overflows int32", + j: ` +{ + "header": { + "sessionToken": { + "body": { + "id": "dhfb+XVGQCGd5Wb9jjQRkA==", + "ownerID": { + "value": "NfjDD8T+fBepxtAP2+U+lpef3Ung5Woq3g==" + }, + "lifetime": { + "exp": "16429376563136800338", + "nbf": "17237208928641773338", + "iat": "7956510363313998522" + }, + "sessionKey": "ApqQg8XWmqMjXYVrI20D2hQA/xrAIewA8Lf9TLuI16TZ", + "object": { + "target": { + "container": { + "value": "h1mV27nR6Yng041Gwc34/uIecrH1qx1a1A8zVo5lm40=" + }, + "objects": [ + { + "value": "OattKWkZkuCkWT2ys55DKCCaeq513Yqoh5XuPUQ6Ir0=" + }, + { + "value": "bulm6IhE6RaeZDEUtV/bjzX67XFAGTALNs84YmOIzxU=" + } + ] + } + } + }, + "signature": { + "key": "c2Vzc2lvbl9zaWduZXI=", + "signature": "c2Vzc2lvbl9zaWduYXR1cmU=", + "scheme": -2147483648 + } + } + } +} +`}, + {name: "attributes/no key", err: "invalid header: empty key of the attribute #1", + j: `{"header": {"attributes": [{"key": "k1","value": "v1"},{"value": "v2"},{"key": "k3","value": "v3"}]}}`}, + {name: "attributes/no value", err: "invalid header: empty value of the attribute #1 (k2)", + j: `{"header": {"attributes": [{"key": "k1","value": "v1"},{"key": "k2"},{"key": "k3","value": "v3"}]}}`}, + {name: "attributes/duplicated", err: "invalid header: duplicated attribute k1", + j: `{"header": {"attributes": [{"key": "k1","value": "v1"},{"key": "k2", "value": "v2"},{"key": "k1","value": "v3"}]}}`}, + {name: "attributes/expiration", err: "invalid header: invalid expiration attribute (must be a uint): strconv.ParseUint: parsing \"foo\": invalid syntax", + j: `{"header": {"attributes": [{"key": "k1","value": "v1"},{"key": "__NEOFS__EXPIRATION_EPOCH", "value": "foo"}]}}`}, + {name: "header/split/parent ID/empty value", err: "invalid header: invalid split header: invalid parent split member ID: invalid length 0", + j: `{"header": {"split":{"parent":{}}}}`}, + {name: "header/split/parent ID/undersize", err: "invalid header: invalid split header: invalid parent split member ID: invalid length 31", + j: `{"header": {"split":{"parent":{"value":"sko62y4Dbn3cUe4jGwbkwb7gTSwSOHWtRvYIi/euNQ=="}}}}`}, + {name: "header/split/parent ID/oversize", err: "invalid header: invalid split header: invalid parent split member ID: invalid length 33", + j: `{"header": {"split":{"parent":{"value":"sko62y4Dbn3cUe4jGwbkwb7gTSwSOHWtRvYIi/euNTwB"}}}}`}, + {name: "header/split/parent ID/zero", err: "invalid header: invalid split header: invalid parent split member ID: zero object ID", + j: `{"header": {"split":{"parent":{"value":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="}}}}`}, + {name: "header/split/previous/empty value", err: "invalid header: invalid split header: invalid previous split member ID: invalid length 0", + j: `{"header": {"split":{"previous":{}}}}`}, + {name: "header/split/previous/undersize", err: "invalid header: invalid split header: invalid previous split member ID: invalid length 31", + j: `{"header": {"split":{"previous":{"value":"sko62y4Dbn3cUe4jGwbkwb7gTSwSOHWtRvYIi/euNQ=="}}}}`}, + {name: "header/split/previous/oversize", err: "invalid header: invalid split header: invalid previous split member ID: invalid length 33", + j: `{"header": {"split":{"previous":{"value":"sko62y4Dbn3cUe4jGwbkwb7gTSwSOHWtRvYIi/euNTwB"}}}}`}, + {name: "header/split/previous/zero", err: "invalid header: invalid split header: invalid previous split member ID: zero object ID", + j: `{"header": {"split":{"previous":{"value":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="}}}}`}, + {name: "header/split/first/empty value", err: "invalid header: invalid split header: invalid first split member ID: invalid length 0", + j: `{"header": {"split":{"first":{}}}}`}, + {name: "header/split/first/undersize", err: "invalid header: invalid split header: invalid first split member ID: invalid length 31", + j: `{"header": {"split":{"first":{"value":"sko62y4Dbn3cUe4jGwbkwb7gTSwSOHWtRvYIi/euNQ=="}}}}`}, + {name: "header/split/first/oversize", err: "invalid header: invalid split header: invalid first split member ID: invalid length 33", + j: `{"header": {"split":{"first":{"value":"sko62y4Dbn3cUe4jGwbkwb7gTSwSOHWtRvYIi/euNTwB"}}}}`}, + {name: "header/split/first/zero", err: "invalid header: invalid split header: invalid first split member ID: zero object ID", + j: `{"header": {"split":{"first":{"value":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="}}}}`}, + {name: "header/split/ID/undersize", err: "invalid header: invalid split header: invalid split UUID: invalid UUID (got 15 bytes)", + j: `{"header": {"split":{"splitID":"4IQDUCAsRbi5IOLJzsST"}}}`}, + {name: "header/split/ID/oversize", err: "invalid header: invalid split header: invalid split UUID: invalid UUID (got 17 bytes)", + j: `{"header": {"split":{"splitID":"4IQDUCAsRbi5IOLJzsSTKQE="}}}`}, + {name: "header/split/ID/wrong UUID version", err: "invalid header: invalid split header: invalid split UUID version 3", + j: `{"header": {"split":{"splitID":"4IQDUCAsMLi5IOLJzsSTKQ=="}}}`}, + {name: "header/split/children/empty value", err: "invalid header: invalid split header: invalid child split member ID #1: invalid length 0", + j: `{"header": {"split":{"children":[{"value":"sko62y4Dbn3cUe4jGwbkwb7gTSwSOHWtRvYIi/euNTw="}, {}, {"value":"zuT32Sn3n9dP4jWZhRBmaALqI9zscGUY636t5aHKxfI="}]}}}`}, + {name: "header/split/children/undersize", err: "invalid header: invalid split header: invalid child split member ID #1: invalid length 31", + j: `{"header": {"split":{"children":[{"value":"sko62y4Dbn3cUe4jGwbkwb7gTSwSOHWtRvYIi/euNTw="}, {"value":"5U0/6wIJpXt0ey9BFiLWTC3hFS6HIHSsQ9XzOf1/sw=="}, {"value":"zuT32Sn3n9dP4jWZhRBmaALqI9zscGUY636t5aHKxfI="}]}}}`}, + {name: "header/split/children/oversize", err: "invalid header: invalid split header: invalid child split member ID #1: invalid length 33", + j: `{"header": {"split":{"children":[{"value":"sko62y4Dbn3cUe4jGwbkwb7gTSwSOHWtRvYIi/euNTw="}, {"value":"5U0/6wIJpXt0ey9BFiLWTC3hFS6HIHSsQ9XzOf1/s+sB"}, {"value":"zuT32Sn3n9dP4jWZhRBmaALqI9zscGUY636t5aHKxfI="}]}}}`}, + {name: "header/split/children/zero", err: "invalid header: invalid split header: invalid child split member ID #1: zero object ID", + j: `{"header": {"split":{"children":[{"value":"sko62y4Dbn3cUe4jGwbkwb7gTSwSOHWtRvYIi/euNTw="}, {"value":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="}, {"value":"zuT32Sn3n9dP4jWZhRBmaALqI9zscGUY636t5aHKxfI="}]}}}`}, + {name: "header/split/parent signature/scheme/overflow", err: "invalid header: invalid split header: invalid parent signature: scheme 2147483648 overflows int32", + j: `{"header": {"split":{"parentSignature":{"key":"cHViXzE=","signature":"c2lnXzE=","scheme":-2147483648}}}}`}, + {name: "header/split/parent/owner/value/empty", err: "invalid header: invalid split header: invalid parent header: invalid owner: invalid length 0, expected 25", + j: `{"header": {"split": {"parentHeader": {"ownerID": {}}}}}`}, + {name: "header/split/parent/owner/value/undersize", err: "invalid header: invalid split header: invalid parent header: invalid owner: invalid length 24, expected 25", + j: `{"header": {"split": {"parentHeader": {"ownerID": {"value": "NTsPBTSD/8YIYim45e2M1zSB09Zake2J"}}}}}`}, + {name: "header/split/parent/owner/value/oversize", err: "invalid header: invalid split header: invalid parent header: invalid owner: invalid length 26, expected 25", + j: `{"header": {"split": {"parentHeader": {"ownerID": {"value": "NTsPBTSD/8YIYim45e2M1zSB09Zake2JmQE="}}}}}`}, + {name: "header/split/parent/owner/value/wrong prefix", err: "invalid header: invalid split header: invalid parent header: invalid owner: invalid prefix byte 0x42, expected 0x35", + j: `{"header": {"split": {"parentHeader": {"ownerID": {"value": "QjsPBTSD/8YIYim45e2M1zSB09Zake2JmQ=="}}}}}`}, + {name: "header/split/parent/owner/value/checksum mismatch", err: "invalid header: invalid split header: invalid parent header: invalid owner: checksum mismatch", + j: `{"header": {"split": {"parentHeader": {"ownerID": {"value": "NTsPBTSD/8YIYim45e2M1zSB09Zake2Jmg=="}}}}}`}, + {name: "header/split/parent/container/empty value", err: "invalid header: invalid split header: invalid parent header: invalid container: invalid length 0", + j: `{"header": {"split": {"parentHeader": {"containerID": {}}}}}`}, + {name: "header/split/parent/container/undersize", err: "invalid header: invalid split header: invalid parent header: invalid container: invalid length 31", + j: `{"header": {"split": {"parentHeader": {"containerID": {"value": "9V6kz9npr0t7ma4IFIdgzLNdt/q0/6K23txjfYh1zg=="}}}}}`}, + {name: "header/split/parent/container/oversize", err: "invalid header: invalid split header: invalid parent header: invalid container: invalid length 33", + j: `{"header": {"split": {"parentHeader": {"containerID": {"value": "9V6kz9npr0t7ma4IFIdgzLNdt/q0/6K23txjfYh1ziIB"}}}}}`}, + {name: "header/split/parent/container/zero", err: "invalid header: invalid split header: invalid parent header: invalid container: zero container ID", + j: `{"header": {"split": {"parentHeader": {"containerID": {"value": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="}}}}}`}, + {name: "header/split/parent/payload checksum/missing value", err: "invalid header: invalid split header: invalid parent header: invalid payload checksum: missing value", + j: `{"header": {"split": {"parentHeader": {"payloadHash":{"type":1974315742}}}}}`}, + {name: "header/split/parent/payload homomorphic checksum/missing value", err: "invalid header: invalid split header: invalid parent header: invalid payload homomorphic checksum: missing value", + j: `{"header": {"split": {"parentHeader": {"homomorphicHash":{"type":1974315742}}}}}`}, + {name: "header/split/parent/split/parent ID/empty value", err: "invalid header: invalid split header: invalid parent header: invalid split header: invalid parent split member ID: invalid length 0", + j: `{"header": {"split": {"parentHeader": {"split":{"parent":{}}}}}}`}, + {name: "header/split/parent/split/parent ID/undersize", err: "invalid header: invalid split header: invalid parent header: invalid split header: invalid parent split member ID: invalid length 31", + j: `{"header": {"split": {"parentHeader": {"split":{"parent":{"value":"sko62y4Dbn3cUe4jGwbkwb7gTSwSOHWtRvYIi/euNQ=="}}}}}}`}, + {name: "header/split/parent/split/parent ID/oversize", err: "invalid header: invalid split header: invalid parent header: invalid split header: invalid parent split member ID: invalid length 33", + j: `{"header": {"split": {"parentHeader": {"split":{"parent":{"value":"sko62y4Dbn3cUe4jGwbkwb7gTSwSOHWtRvYIi/euNTwB"}}}}}}`}, + {name: "header/split/parent/split/parent ID/zero", err: "invalid header: invalid split header: invalid parent header: invalid split header: invalid parent split member ID: zero object ID", + j: `{"header": {"split": {"parentHeader": {"split":{"parent":{"value":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="}}}}}}`}, + {name: "header/split/parent/split/previous/empty value", err: "invalid header: invalid split header: invalid parent header: invalid split header: invalid previous split member ID: invalid length 0", + j: `{"header": {"split": {"parentHeader": {"split":{"previous":{}}}}}}`}, + {name: "header/split/parent/split/previous/undersize", err: "invalid header: invalid split header: invalid parent header: invalid split header: invalid previous split member ID: invalid length 31", + j: `{"header": {"split": {"parentHeader": {"split":{"previous":{"value":"sko62y4Dbn3cUe4jGwbkwb7gTSwSOHWtRvYIi/euNQ=="}}}}}}`}, + {name: "header/split/parent/split/previous/oversize", err: "invalid header: invalid split header: invalid parent header: invalid split header: invalid previous split member ID: invalid length 33", + j: `{"header": {"split": {"parentHeader": {"split":{"previous":{"value":"sko62y4Dbn3cUe4jGwbkwb7gTSwSOHWtRvYIi/euNTwB"}}}}}}`}, + {name: "header/split/parent/split/previous/zero", err: "invalid header: invalid split header: invalid parent header: invalid split header: invalid previous split member ID: zero object ID", + j: `{"header": {"split": {"parentHeader": {"split":{"previous":{"value":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="}}}}}}`}, + {name: "header/split/parent/split/first/empty value", err: "invalid header: invalid split header: invalid parent header: invalid split header: invalid first split member ID: invalid length 0", + j: `{"header": {"split": {"parentHeader": {"split":{"first":{}}}}}}`}, + {name: "header/split/parent/split/first/undersize", err: "invalid header: invalid split header: invalid parent header: invalid split header: invalid first split member ID: invalid length 31", + j: `{"header": {"split": {"parentHeader": {"split":{"first":{"value":"sko62y4Dbn3cUe4jGwbkwb7gTSwSOHWtRvYIi/euNQ=="}}}}}}`}, + {name: "header/split/parent/split/first/oversize", err: "invalid header: invalid split header: invalid parent header: invalid split header: invalid first split member ID: invalid length 33", + j: `{"header": {"split": {"parentHeader": {"split":{"first":{"value":"sko62y4Dbn3cUe4jGwbkwb7gTSwSOHWtRvYIi/euNTwB"}}}}}}`}, + {name: "header/split/parent/split/first/zero", err: "invalid header: invalid split header: invalid parent header: invalid split header: invalid first split member ID: zero object ID", + j: `{"header": {"split": {"parentHeader": {"split":{"first":{"value":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="}}}}}}`}, + {name: "header/split/parent/split/ID/undersize", err: "invalid header: invalid split header: invalid parent header: invalid split header: invalid split UUID: invalid UUID (got 15 bytes)", + j: `{"header": {"split": {"parentHeader": {"split":{"splitID":"4IQDUCAsRbi5IOLJzsST"}}}}}`}, + {name: "header/split/parent/split/ID/oversize", err: "invalid header: invalid split header: invalid parent header: invalid split header: invalid split UUID: invalid UUID (got 17 bytes)", + j: `{"header": {"split": {"parentHeader": {"split":{"splitID":"4IQDUCAsRbi5IOLJzsSTKQE="}}}}}`}, + {name: "header/split/parent/split/ID/wrong UUID version", err: "invalid header: invalid split header: invalid parent header: invalid split header: invalid split UUID version 3", + j: `{"header": {"split": {"parentHeader": {"split":{"splitID":"4IQDUCAsMLi5IOLJzsSTKQ=="}}}}}`}, + {name: "header/split/parent/split/children/empty value", err: "invalid header: invalid split header: invalid parent header: invalid split header: invalid child split member ID #1: invalid length 0", + j: `{"header": {"split": {"parentHeader": {"split":{"children":[{"value":"sko62y4Dbn3cUe4jGwbkwb7gTSwSOHWtRvYIi/euNTw="},{},{"value":"zuT32Sn3n9dP4jWZhRBmaALqI9zscGUY636t5aHKxfI="}]}}}}}`}, + {name: "header/split/parent/split/children/undersize", err: "invalid header: invalid split header: invalid parent header: invalid split header: invalid child split member ID #1: invalid length 31", + j: `{"header": {"split": {"parentHeader": {"split":{"children":[{"value":"sko62y4Dbn3cUe4jGwbkwb7gTSwSOHWtRvYIi/euNTw="},{"value":"5U0/6wIJpXt0ey9BFiLWTC3hFS6HIHSsQ9XzOf1/sw=="},{"value":"zuT32Sn3n9dP4jWZhRBmaALqI9zscGUY636t5aHKxfI="}]}}}}}`}, + {name: "header/split/parent/split/children/oversize", err: "invalid header: invalid split header: invalid parent header: invalid split header: invalid child split member ID #1: invalid length 33", + j: `{"header": {"split": {"parentHeader": {"split":{"children":[{"value":"sko62y4Dbn3cUe4jGwbkwb7gTSwSOHWtRvYIi/euNTw="},{"value":"5U0/6wIJpXt0ey9BFiLWTC3hFS6HIHSsQ9XzOf1/s+sB"},{"value":"zuT32Sn3n9dP4jWZhRBmaALqI9zscGUY636t5aHKxfI="}]}}}}}`}, + {name: "header/split/parent/split/children/zero", err: "invalid header: invalid split header: invalid parent header: invalid split header: invalid child split member ID #1: zero object ID", + j: `{"header": {"split": {"parentHeader": {"split":{"children":[{"value":"sko62y4Dbn3cUe4jGwbkwb7gTSwSOHWtRvYIi/euNTw="},{"value":"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="},{"value":"zuT32Sn3n9dP4jWZhRBmaALqI9zscGUY636t5aHKxfI="}]}}}}}`}, + {name: "header/split/parent/split/parent signature/scheme/overflow", err: "invalid header: invalid split header: invalid parent header: invalid split header: invalid parent signature: scheme 2147483648 overflows int32", + j: `{"header": {"split": {"parentHeader": {"split":{"parentSignature":{"key":"cHViXzE=","signature":"c2lnXzE=","scheme":-2147483648}}}}}}`}, + } { + t.Run(tc.name, func(t *testing.T) { + require.EqualError(t, new(object.Object).UnmarshalJSON([]byte(tc.j)), tc.err) + }) + } + }) + + var obj object.Object + // zero + require.NoError(t, obj.UnmarshalJSON([]byte("{}"))) + require.Zero(t, obj) + + // filled + require.NoError(t, obj.UnmarshalJSON([]byte(validJSONObject))) + t.Skip("https://github.com/nspcc-dev/neofs-sdk-go/issues/606") + require.Equal(t, validObject, obj) +} - require.Empty(t, mUsr) +func TestObject_HeaderLen(t *testing.T) { + require.EqualValues(t, 0, object.Object{}.HeaderLen()) + require.EqualValues(t, 1047, validObject.HeaderLen()) } diff --git a/object/search_test.go b/object/search_test.go index f118811b7..c113f4b14 100644 --- a/object/search_test.go +++ b/object/search_test.go @@ -56,10 +56,10 @@ func init() { validSearchFilters.AddFilter("k6", "v6", object.MatchNumGE) validSearchFilters.AddFilter("k7", "v7", object.MatchNumLT) validSearchFilters.AddFilter("k8", "v8", object.MatchNumLE) - validSearchFilters.AddObjectVersionFilter(100, anyValidVersion) + validSearchFilters.AddObjectVersionFilter(100, anyValidVersions[0]) validSearchFilters.AddObjectIDFilter(101, anyValidIDs[0]) - validSearchFilters.AddObjectContainerIDFilter(102, anyValidContainer) - validSearchFilters.AddObjectOwnerIDFilter(103, anyValidUser) + validSearchFilters.AddObjectContainerIDFilter(102, anyValidContainers[0]) + validSearchFilters.AddObjectOwnerIDFilter(103, anyValidUsers[0]) validSearchFilters.AddCreationEpochFilter(104, anyValidCreationEpoch) validSearchFilters.AddPayloadSizeFilter(105, anyValidPayloadSize) validSearchFilters.AddPayloadHashFilter(106, anySHA256Hash) @@ -278,14 +278,14 @@ func TestSearchFilters_AddFilter(t *testing.T) { func TestSearchFilters_AddObjectVersionFilter(t *testing.T) { var fs object.SearchFilters - fs.AddObjectVersionFilter(anyValidSearchMatcher, anyValidVersion) + fs.AddObjectVersionFilter(anyValidSearchMatcher, anyValidVersions[0]) require.Len(t, fs, 1) assertSearchFilter(t, fs, 0, object.FilterVersion, anyValidSearchMatcher, "v88789927.2018985309", true) } func TestSearchFilters_AddObjectContainerIDFilter(t *testing.T) { var fs object.SearchFilters - fs.AddObjectContainerIDFilter(anyValidSearchMatcher, anyValidContainer) + fs.AddObjectContainerIDFilter(anyValidSearchMatcher, anyValidContainers[0]) require.Len(t, fs, 1) assertSearchFilter(t, fs, 0, object.FilterContainerID, anyValidSearchMatcher, "HWpbBkyxCi7nhDnn4W3v5rYt2mDfH2wedknQzRkTwquj", true) @@ -293,7 +293,7 @@ func TestSearchFilters_AddObjectContainerIDFilter(t *testing.T) { func TestSearchFilters_AddObjectOwnerIDFilter(t *testing.T) { var fs object.SearchFilters - fs.AddObjectOwnerIDFilter(anyValidSearchMatcher, anyValidUser) + fs.AddObjectOwnerIDFilter(anyValidSearchMatcher, anyValidUsers[0]) require.Len(t, fs, 1) assertSearchFilter(t, fs, 0, object.FilterOwnerID, anyValidSearchMatcher, "NRJF3hqhZAe4NeWpABW6Q3ajkfhFUkY2ek", true) diff --git a/object/tombstone_test.go b/object/tombstone_test.go index 824788c96..ec6224d1d 100644 --- a/object/tombstone_test.go +++ b/object/tombstone_test.go @@ -18,7 +18,7 @@ var validTombstone object.Tombstone // set by init. func init() { validTombstone.SetExpirationEpoch(anyValidExpirationEpoch) validTombstone.SetSplitID(anyValidSplitID) - validTombstone.SetMembers(anyValidIDs) + validTombstone.SetMembers(anyValidIDs[:3]) } // corresponds to validTombstone. @@ -138,7 +138,7 @@ func TestTombstone_WriteToV2(t *testing.T) { require.EqualValues(t, anyValidExpirationEpoch, m.GetExpirationEpoch()) require.EqualValues(t, anyValidSplitIDBytes, m.GetSplitID()) ms := m.GetMembers() - require.Len(t, ms, len(anyValidIDs)) + require.Len(t, ms, 3) for i := range ms { require.Equal(t, anyValidIDs[i][:], ms[i].GetValue()) } diff --git a/object/util_test.go b/object/util_test.go index c8881d4f6..64cf2c39e 100644 --- a/object/util_test.go +++ b/object/util_test.go @@ -4,7 +4,9 @@ import ( "crypto/sha256" "github.com/nspcc-dev/neofs-api-go/v2/refs" + "github.com/nspcc-dev/neofs-sdk-go/checksum" cid "github.com/nspcc-dev/neofs-sdk-go/container/id" + neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto" "github.com/nspcc-dev/neofs-sdk-go/object" oid "github.com/nspcc-dev/neofs-sdk-go/object/id" "github.com/nspcc-dev/neofs-sdk-go/user" @@ -29,17 +31,59 @@ var ( 57, 253, 127, 179, 235}, {206, 228, 247, 217, 41, 247, 159, 215, 79, 226, 53, 153, 133, 16, 102, 104, 2, 234, 35, 220, 236, 112, 101, 24, 235, 126, 173, 229, 161, 202, 197, 242}, + {173, 160, 45, 58, 200, 168, 116, 142, 235, 209, 231, 80, 235, 186, 6, 132, 99, 95, 14, 39, 237, 139, 87, 66, 244, 72, 96, + 69, 13, 83, 81, 172}, + {238, 167, 85, 68, 91, 254, 165, 81, 182, 145, 16, 91, 35, 224, 17, 46, 164, 138, 86, 50, 196, 148, 215, 210, 247, 29, 44, + 153, 203, 20, 137, 169}, + {226, 165, 123, 249, 146, 166, 187, 202, 244, 12, 156, 43, 207, 204, 40, 230, 145, 34, 212, 152, 148, 112, 44, 21, 195, + 207, 249, 112, 34, 81, 145, 194}, + {119, 231, 221, 167, 7, 141, 50, 77, 49, 23, 194, 169, 82, 56, 150, 162, 103, 20, 124, 174, 16, 64, 169, 172, 79, 238, 242, + 146, 87, 88, 5, 147}, + {139, 94, 241, 189, 238, 91, 251, 21, 52, 85, 200, 121, 189, 78, 16, 173, 74, 174, 20, 47, 223, 68, 172, 82, 113, 185, + 171, 241, 195, 191, 186, 87}, + {57, 171, 109, 41, 105, 25, 146, 224, 164, 89, 61, 178, 179, 158, 67, 40, 32, 154, 122, 174, 117, 221, 138, 168, 135, 149, 238, + 61, 68, 58, 34, 189}, + {110, 233, 102, 232, 136, 68, 233, 22, 158, 100, 49, 20, 181, 95, 219, 143, 53, 250, 237, 113, 64, 25, 48, 11, 54, 207, + 56, 98, 99, 136, 207, 21}, + } + anyValidVersions = []version.Version{ + version.New(88789927, 2018985309), + version.New(525747025, 171993162), + } + anyValidContainers = []cid.ID{ + {245, 94, 164, 207, 217, 233, 175, 75, 123, 153, 174, 8, 20, 135, 96, 204, 179, 93, 183, 250, 180, 255, 162, 182, 222, + 220, 99, 125, 136, 117, 206, 34}, + {217, 213, 19, 152, 91, 248, 2, 180, 17, 177, 248, 226, 163, 200, 56, 31, 123, 24, 182, 144, 148, 180, 248, 192, 155, + 253, 104, 220, 69, 102, 174, 5}, + {135, 89, 149, 219, 185, 209, 233, 137, 224, 211, 141, 70, 193, 205, 248, 254, 226, 30, 114, 177, 245, 171, 29, 90, 212, + 15, 51, 86, 142, 101, 155, 141}, + } + anyValidUsers = []user.ID{ + {53, 59, 15, 5, 52, 131, 255, 198, 8, 98, 41, 184, 229, 237, 140, 215, 52, 129, 211, 214, 90, 145, 237, 137, 153}, + {53, 214, 113, 220, 69, 70, 98, 242, 115, 99, 188, 86, 53, 223, 243, 238, 11, 245, 251, 169, 115, 202, 247, 184, 221}, + {53, 248, 195, 15, 196, 254, 124, 23, 169, 198, 208, 15, 219, 229, 62, 150, 151, 159, 221, 73, 224, 229, 106, 42, 222}, } - anyValidVersion = version.New(88789927, 2018985309) - anyValidContainer = cid.ID{245, 94, 164, 207, 217, 233, 175, 75, 123, 153, 174, 8, 20, 135, 96, 204, 179, 93, 183, 250, 180, - 255, 162, 182, 222, 220, 99, 125, 136, 117, 206, 34} - anyValidUser = user.ID{53, 59, 15, 5, 52, 131, 255, 198, 8, 98, 41, 184, 229, 237, 140, 215, 52, 129, 211, 214, 90, 145, 237, - 137, 153} anySHA256Hash = [sha256.Size]byte{233, 204, 37, 189, 15, 146, 210, 138, 178, 74, 213, 141, 199, 249, 94, 20, 73, 133, 16, 154, 241, 152, 3, 205, 101, 210, 153, 141, 139, 30, 216, 125} anyTillichZemorHash = [tz.Size]byte{160, 149, 6, 167, 41, 70, 29, 61, 190, 154, 30, 117, 180, 150, 91, 146, 24, 16, 195, 213, 216, 106, 119, 203, 178, 159, 37, 1, 252, 208, 87, 23, 165, 19, 22, 96, 50, 28, 145, 235, 127, 107, 86, 216, 51, 226, 84, 242, 94, 186, 90, 81, 184, 236, 118, 65, 58, 69, 110, 232, 22, 249, 131, 173} + anyValidSignatures = []neofscrypto.Signature{ + neofscrypto.NewSignatureFromRawKey(1277002296, []byte("pub_1"), []byte("sig_1")), + neofscrypto.NewSignatureFromRawKey(1242896683, []byte("pub_2"), []byte("sig_2")), + } + anyValidChecksums = []checksum.Checksum{ + checksum.New(1974315742, []byte("checksum_1")), + checksum.New(1922538608, []byte("checksum_2")), + checksum.New(126384577, []byte("checksum_3")), + checksum.New(1001923429, []byte("checksum_4")), + } + anyValidRegularPayload = []byte("Hello, world!") + // corresponds to anyValidRegularPayload. + anyValidPayloadChecksum = [sha256.Size]byte{49, 95, 91, 219, 118, 208, 120, 196, 59, 138, 192, 6, 78, 74, 1, 100, 97, 43, 31, 206, + 119, 200, 105, 52, 91, 252, 148, 199, 88, 148, 237, 211} + emptySHA256Hash = [sha256.Size]byte{227, 176, 196, 66, 152, 252, 28, 20, 154, 251, 244, 200, 153, 111, 185, 36, 39, 174, 65, + 228, 100, 155, 147, 76, 164, 149, 153, 27, 120, 82, 184, 85} ) func protoIDFromBytes(b []byte) *refs.ObjectID { @@ -47,3 +91,15 @@ func protoIDFromBytes(b []byte) *refs.ObjectID { id.SetValue(b) return &id } + +func protoUserIDFromBytes(b []byte) *refs.OwnerID { + var id refs.OwnerID + id.SetValue(b) + return &id +} + +func protoContainerIDFromBytes(b []byte) *refs.ContainerID { + var id refs.ContainerID + id.SetValue(b) + return &id +}