diff --git a/v2/container/marshal.go b/v2/container/marshal.go new file mode 100644 index 0000000..a9fdd02 --- /dev/null +++ b/v2/container/marshal.go @@ -0,0 +1,512 @@ +package container + +import ( + "github.com/nspcc-dev/neofs-api-go/util/proto" +) + +const ( + attributeKeyField = 1 + attributeValueField = 2 + + containerVersionField = 1 + containerOwnerField = 2 + containerNonceField = 3 + containerBasicACLField = 4 + containerAttributesField = 5 + containerPlacementField = 6 + + putReqBodyContainerField = 1 + putReqBodySignatureField = 2 + + putRespBodyIDField = 1 + + deleteReqBodyIDField = 1 + deleteReqBodySignatureField = 2 + + getReqBodyIDField = 1 + + getRespBodyContainerField = 1 + + listReqBodyOwnerField = 1 + + listRespBodyIDsField = 1 + + setEACLReqBodyTableField = 1 + setEACLReqBodySignatureField = 2 + + getEACLReqBodyIDField = 1 + + getEACLRespBodyTableField = 1 + getEACLRespBodySignatureField = 2 +) + +func (a *Attribute) StableMarshal(buf []byte) ([]byte, error) { + if a == nil { + return []byte{}, nil + } + + if buf == nil { + buf = make([]byte, a.StableSize()) + } + + var ( + offset, n int + err error + ) + + n, err = proto.StringMarshal(attributeKeyField, buf[offset:], a.key) + if err != nil { + return nil, err + } + + offset += n + + _, err = proto.StringMarshal(attributeValueField, buf[offset:], a.val) + if err != nil { + return nil, err + } + + return buf, nil +} + +func (a *Attribute) StableSize() (size int) { + if a == nil { + return 0 + } + + size += proto.StringSize(attributeKeyField, a.key) + size += proto.StringSize(attributeValueField, a.val) + + return size +} + +func (c *Container) StableMarshal(buf []byte) ([]byte, error) { + if c == nil { + return []byte{}, nil + } + + if buf == nil { + buf = make([]byte, c.StableSize()) + } + + var ( + offset, n int + err error + ) + + n, err = proto.NestedStructureMarshal(containerVersionField, buf[offset:], c.version) + if err != nil { + return nil, err + } + + offset += n + + n, err = proto.NestedStructureMarshal(containerOwnerField, buf[offset:], c.ownerID) + if err != nil { + return nil, err + } + + offset += n + + n, err = proto.BytesMarshal(containerNonceField, buf[offset:], c.nonce) + if err != nil { + return nil, err + } + + offset += n + + n, err = proto.UInt32Marshal(containerBasicACLField, buf[offset:], c.basicACL) + if err != nil { + return nil, err + } + + offset += n + + for i := range c.attr { + n, err = proto.NestedStructureMarshal(containerAttributesField, buf[offset:], c.attr[i]) + if err != nil { + return nil, err + } + + offset += n + } + + _, err = proto.NestedStructureMarshal(containerPlacementField, buf[offset:], c.policy) + if err != nil { + return nil, err + } + + return buf, nil +} + +func (c *Container) StableSize() (size int) { + if c == nil { + return 0 + } + + size += proto.NestedStructureSize(containerVersionField, c.version) + size += proto.NestedStructureSize(containerOwnerField, c.ownerID) + size += proto.BytesSize(containerNonceField, c.nonce) + size += proto.UInt32Size(containerBasicACLField, c.basicACL) + + for i := range c.attr { + size += proto.NestedStructureSize(containerAttributesField, c.attr[i]) + } + + size += proto.NestedStructureSize(containerPlacementField, c.policy) + + return size +} + +func (r *PutRequestBody) StableMarshal(buf []byte) ([]byte, error) { + if r == nil { + return []byte{}, nil + } + + if buf == nil { + buf = make([]byte, r.StableSize()) + } + + var ( + offset, n int + err error + ) + + n, err = proto.NestedStructureMarshal(putReqBodyContainerField, buf[offset:], r.cnr) + if err != nil { + return nil, err + } + + offset += n + + _, err = proto.NestedStructureMarshal(putReqBodySignatureField, buf[offset:], r.sig) + if err != nil { + return nil, err + } + + return buf, nil +} + +func (r *PutRequestBody) StableSize() (size int) { + if r == nil { + return 0 + } + + size += proto.NestedStructureSize(putReqBodyContainerField, r.cnr) + size += proto.NestedStructureSize(putReqBodySignatureField, r.sig) + + return size +} + +func (r *PutResponseBody) StableMarshal(buf []byte) ([]byte, error) { + if r == nil { + return []byte{}, nil + } + + if buf == nil { + buf = make([]byte, r.StableSize()) + } + + var ( + err error + ) + + _, err = proto.NestedStructureMarshal(putRespBodyIDField, buf, r.cid) + if err != nil { + return nil, err + } + + return buf, nil +} + +func (r *PutResponseBody) StableSize() (size int) { + if r == nil { + return 0 + } + + size += proto.NestedStructureSize(putRespBodyIDField, r.cid) + + return size +} + +func (r *DeleteRequestBody) StableMarshal(buf []byte) ([]byte, error) { + if r == nil { + return []byte{}, nil + } + + if buf == nil { + buf = make([]byte, r.StableSize()) + } + + var ( + offset, n int + err error + ) + + n, err = proto.NestedStructureMarshal(deleteReqBodyIDField, buf[offset:], r.cid) + if err != nil { + return nil, err + } + + offset += n + + _, err = proto.NestedStructureMarshal(deleteReqBodySignatureField, buf[offset:], r.sig) + if err != nil { + return nil, err + } + + return buf, nil +} + +func (r *DeleteRequestBody) StableSize() (size int) { + if r == nil { + return 0 + } + + size += proto.NestedStructureSize(deleteReqBodyIDField, r.cid) + size += proto.NestedStructureSize(deleteReqBodySignatureField, r.sig) + + return size +} + +func (r *DeleteResponseBody) StableMarshal(buf []byte) ([]byte, error) { + return nil, nil +} + +func (r *DeleteResponseBody) StableSize() (size int) { + return 0 +} + +func (r *GetRequestBody) StableMarshal(buf []byte) ([]byte, error) { + if r == nil { + return []byte{}, nil + } + + if buf == nil { + buf = make([]byte, r.StableSize()) + } + + _, err := proto.NestedStructureMarshal(getReqBodyIDField, buf, r.cid) + if err != nil { + return nil, err + } + + return buf, nil +} + +func (r *GetRequestBody) StableSize() (size int) { + if r == nil { + return 0 + } + + size += proto.NestedStructureSize(getReqBodyIDField, r.cid) + + return size +} + +func (r *GetResponseBody) StableMarshal(buf []byte) ([]byte, error) { + if r == nil { + return []byte{}, nil + } + + if buf == nil { + buf = make([]byte, r.StableSize()) + } + + _, err := proto.NestedStructureMarshal(getRespBodyContainerField, buf, r.cnr) + if err != nil { + return nil, err + } + + return buf, nil +} + +func (r *GetResponseBody) StableSize() (size int) { + if r == nil { + return 0 + } + + size += proto.NestedStructureSize(getRespBodyContainerField, r.cnr) + + return size +} + +func (r *ListRequestBody) StableMarshal(buf []byte) ([]byte, error) { + if r == nil { + return []byte{}, nil + } + + if buf == nil { + buf = make([]byte, r.StableSize()) + } + + _, err := proto.NestedStructureMarshal(listReqBodyOwnerField, buf, r.ownerID) + if err != nil { + return nil, err + } + + return buf, nil +} + +func (r *ListRequestBody) StableSize() (size int) { + if r == nil { + return 0 + } + + size += proto.NestedStructureSize(listReqBodyOwnerField, r.ownerID) + + return size +} + +func (r *ListResponseBody) StableMarshal(buf []byte) ([]byte, error) { + if r == nil { + return []byte{}, nil + } + + if buf == nil { + buf = make([]byte, r.StableSize()) + } + + var ( + n, offset int + err error + ) + + for i := range r.cidList { + n, err = proto.NestedStructureMarshal(listRespBodyIDsField, buf[offset:], r.cidList[i]) + if err != nil { + return nil, err + } + + offset += n + } + + return buf, nil +} + +func (r *ListResponseBody) StableSize() (size int) { + if r == nil { + return 0 + } + + for i := range r.cidList { + size += proto.NestedStructureSize(listRespBodyIDsField, r.cidList[i]) + } + + return size +} + +func (r *SetExtendedACLRequestBody) StableMarshal(buf []byte) ([]byte, error) { + if r == nil { + return []byte{}, nil + } + + if buf == nil { + buf = make([]byte, r.StableSize()) + } + + var ( + offset, n int + err error + ) + + n, err = proto.NestedStructureMarshal(setEACLReqBodyTableField, buf[offset:], r.eacl) + if err != nil { + return nil, err + } + + offset += n + + _, err = proto.NestedStructureMarshal(setEACLReqBodySignatureField, buf[offset:], r.sig) + if err != nil { + return nil, err + } + + return buf, nil +} + +func (r *SetExtendedACLRequestBody) StableSize() (size int) { + if r == nil { + return 0 + } + + size += proto.NestedStructureSize(setEACLReqBodyTableField, r.eacl) + size += proto.NestedStructureSize(setEACLReqBodySignatureField, r.sig) + + return size +} + +func (r *SetExtendedACLResponseBody) StableMarshal(buf []byte) ([]byte, error) { + return nil, nil +} + +func (r *SetExtendedACLResponseBody) StableSize() (size int) { + return 0 +} + +func (r *GetExtendedACLRequestBody) StableMarshal(buf []byte) ([]byte, error) { + if r == nil { + return []byte{}, nil + } + + if buf == nil { + buf = make([]byte, r.StableSize()) + } + + _, err := proto.NestedStructureMarshal(getEACLReqBodyIDField, buf, r.cid) + if err != nil { + return nil, err + } + + return buf, nil +} + +func (r *GetExtendedACLRequestBody) StableSize() (size int) { + if r == nil { + return 0 + } + + size += proto.NestedStructureSize(getEACLReqBodyIDField, r.cid) + + return size +} + +func (r *GetExtendedACLResponseBody) StableMarshal(buf []byte) ([]byte, error) { + if r == nil { + return []byte{}, nil + } + + if buf == nil { + buf = make([]byte, r.StableSize()) + } + + var ( + offset, n int + err error + ) + + n, err = proto.NestedStructureMarshal(getEACLRespBodyTableField, buf[offset:], r.eacl) + if err != nil { + return nil, err + } + + offset += n + + _, err = proto.NestedStructureMarshal(getEACLRespBodySignatureField, buf[offset:], r.sig) + if err != nil { + return nil, err + } + + return buf, nil +} + +func (r *GetExtendedACLResponseBody) StableSize() (size int) { + if r == nil { + return 0 + } + + size += proto.NestedStructureSize(getEACLRespBodyTableField, r.eacl) + size += proto.NestedStructureSize(getEACLRespBodySignatureField, r.sig) + + return size +} diff --git a/v2/container/marshal_test.go b/v2/container/marshal_test.go new file mode 100644 index 0000000..f46bb31 --- /dev/null +++ b/v2/container/marshal_test.go @@ -0,0 +1,414 @@ +package container_test + +import ( + "fmt" + "testing" + + "github.com/nspcc-dev/neofs-api-go/v2/acl" + "github.com/nspcc-dev/neofs-api-go/v2/container" + grpc "github.com/nspcc-dev/neofs-api-go/v2/container/grpc" + "github.com/nspcc-dev/neofs-api-go/v2/refs" + "github.com/nspcc-dev/neofs-api-go/v2/service" + "github.com/stretchr/testify/require" +) + +func TestAttribute_StableMarshal(t *testing.T) { + attributeFrom := generateAttribute("key", "value") + transport := new(grpc.Container_Attribute) + + t.Run("non empty", func(t *testing.T) { + wire, err := attributeFrom.StableMarshal(nil) + require.NoError(t, err) + + err = transport.Unmarshal(wire) + require.NoError(t, err) + + attributeTo := container.AttributeFromGRPCMessage(transport) + require.Equal(t, attributeFrom, attributeTo) + }) +} + +func TestContainer_StableMarshal(t *testing.T) { + cnrFrom := generateContainer("nonce") + transport := new(grpc.Container) + + t.Run("non empty", func(t *testing.T) { + wire, err := cnrFrom.StableMarshal(nil) + require.NoError(t, err) + + err = transport.Unmarshal(wire) + require.NoError(t, err) + + cnrTo := container.ContainerFromGRPCMessage(transport) + require.Equal(t, cnrFrom, cnrTo) + }) +} + +func TestPutRequestBody_StableMarshal(t *testing.T) { + requestFrom := generatePutRequestBody("nonce") + transport := new(grpc.PutRequest_Body) + + t.Run("non empty", func(t *testing.T) { + wire, err := requestFrom.StableMarshal(nil) + require.NoError(t, err) + + err = transport.Unmarshal(wire) + require.NoError(t, err) + + requestTo := container.PutRequestBodyFromGRPCMessage(transport) + require.Equal(t, requestFrom, requestTo) + }) +} + +func TestPutResponseBody_StableMarshal(t *testing.T) { + responseFrom := generatePutResponseBody("Container ID") + transport := new(grpc.PutResponse_Body) + + t.Run("non empty", func(t *testing.T) { + wire, err := responseFrom.StableMarshal(nil) + require.NoError(t, err) + + err = transport.Unmarshal(wire) + require.NoError(t, err) + + responseTo := container.PutResponseBodyFromGRPCMessage(transport) + require.Equal(t, responseFrom, responseTo) + }) +} + +func TestDeleteRequestBody_StableMarshal(t *testing.T) { + requestFrom := generateDeleteRequestBody("Container ID") + transport := new(grpc.DeleteRequest_Body) + + t.Run("non empty", func(t *testing.T) { + wire, err := requestFrom.StableMarshal(nil) + require.NoError(t, err) + + err = transport.Unmarshal(wire) + require.NoError(t, err) + + requestTo := container.DeleteRequestBodyFromGRPCMessage(transport) + require.Equal(t, requestFrom, requestTo) + }) +} + +func TestDeleteResponseBody_StableMarshal(t *testing.T) { + responseFrom := generateDeleteResponseBody() + transport := new(grpc.DeleteResponse_Body) + + t.Run("non empty", func(t *testing.T) { + wire, err := responseFrom.StableMarshal(nil) + require.NoError(t, err) + + err = transport.Unmarshal(wire) + require.NoError(t, err) + + responseTo := container.DeleteResponseBodyFromGRPCMessage(transport) + require.Equal(t, responseFrom, responseTo) + }) +} + +func TestGetRequestBody_StableMarshal(t *testing.T) { + requestFrom := generateGetRequestBody("Container ID") + transport := new(grpc.GetRequest_Body) + + t.Run("non empty", func(t *testing.T) { + wire, err := requestFrom.StableMarshal(nil) + require.NoError(t, err) + + err = transport.Unmarshal(wire) + require.NoError(t, err) + + requestTo := container.GetRequestBodyFromGRPCMessage(transport) + require.Equal(t, requestFrom, requestTo) + }) +} + +func TestGetResponseBody_StableMarshal(t *testing.T) { + responseFrom := generateGetResponseBody("nonce") + transport := new(grpc.GetResponse_Body) + + t.Run("non empty", func(t *testing.T) { + wire, err := responseFrom.StableMarshal(nil) + require.NoError(t, err) + + err = transport.Unmarshal(wire) + require.NoError(t, err) + + responseTo := container.GetResponseBodyFromGRPCMessage(transport) + require.Equal(t, responseFrom, responseTo) + }) +} + +func TestListRequestBody_StableMarshal(t *testing.T) { + requestFrom := generateListRequestBody("Owner ID") + transport := new(grpc.ListRequest_Body) + + t.Run("non empty", func(t *testing.T) { + wire, err := requestFrom.StableMarshal(nil) + require.NoError(t, err) + + err = transport.Unmarshal(wire) + require.NoError(t, err) + + requestTo := container.ListRequestBodyFromGRPCMessage(transport) + require.Equal(t, requestFrom, requestTo) + }) +} + +func TestListResponseBody_StableMarshal(t *testing.T) { + responseFrom := generateListResponseBody(3) + transport := new(grpc.ListResponse_Body) + + t.Run("non empty", func(t *testing.T) { + wire, err := responseFrom.StableMarshal(nil) + require.NoError(t, err) + + err = transport.Unmarshal(wire) + require.NoError(t, err) + + responseTo := container.ListResponseBodyFromGRPCMessage(transport) + require.Equal(t, responseFrom, responseTo) + }) +} + +func TestSetEACLRequestBody_StableMarshal(t *testing.T) { + requestFrom := generateSetEACLRequestBody(4, "Filter Key", "Filter Value") + transport := new(grpc.SetExtendedACLRequest_Body) + + t.Run("non empty", func(t *testing.T) { + wire, err := requestFrom.StableMarshal(nil) + require.NoError(t, err) + + err = transport.Unmarshal(wire) + require.NoError(t, err) + + requestTo := container.SetExtendedACLRequestBodyFromGRPCMessage(transport) + require.Equal(t, requestFrom, requestTo) + }) +} + +func TestSetEACLResponseBody_StableMarshal(t *testing.T) { + responseFrom := generateSetEACLResponseBody() + transport := new(grpc.SetExtendedACLResponse_Body) + + t.Run("non empty", func(t *testing.T) { + wire, err := responseFrom.StableMarshal(nil) + require.NoError(t, err) + + err = transport.Unmarshal(wire) + require.NoError(t, err) + + responseTo := container.SetExtendedACLResponseBodyFromGRPCMessage(transport) + require.Equal(t, responseFrom, responseTo) + }) +} + +func TestGetEACLRequestBody_StableMarshal(t *testing.T) { + requestFrom := generateGetEACLRequestBody("Container ID") + transport := new(grpc.GetExtendedACLRequest_Body) + + t.Run("non empty", func(t *testing.T) { + wire, err := requestFrom.StableMarshal(nil) + require.NoError(t, err) + + err = transport.Unmarshal(wire) + require.NoError(t, err) + + requestTo := container.GetExtendedACLRequestBodyFromGRPCMessage(transport) + require.Equal(t, requestFrom, requestTo) + }) +} + +func TestGetEACLResponseBody_StableMarshal(t *testing.T) { + responseFrom := generateGetEACLResponseBody(3, "Filter Key", "Filter Value") + transport := new(grpc.GetExtendedACLResponse_Body) + + t.Run("non empty", func(t *testing.T) { + wire, err := responseFrom.StableMarshal(nil) + require.NoError(t, err) + + err = transport.Unmarshal(wire) + require.NoError(t, err) + + responseTo := container.GetExtendedACLResponseBodyFromGRPCMessage(transport) + require.Equal(t, responseFrom, responseTo) + }) +} + +func generateAttribute(k, v string) *container.Attribute { + attr := new(container.Attribute) + attr.SetKey(k) + attr.SetValue(v) + + return attr +} + +func generateContainer(n string) *container.Container { + owner := new(refs.OwnerID) + owner.SetValue([]byte("Owner ID")) + + version := new(service.Version) + version.SetMajor(2) + version.SetMinor(0) + + // todo: add placement rule + + cnr := new(container.Container) + cnr.SetOwnerID(owner) + cnr.SetVersion(version) + cnr.SetAttributes([]*container.Attribute{ + generateAttribute("one", "two"), + generateAttribute("three", "four"), + }) + cnr.SetBasicACL(100) + cnr.SetNonce([]byte(n)) + + return cnr +} + +func generateSignature(k, v string) *service.Signature { + sig := new(service.Signature) + sig.SetKey([]byte(k)) + sig.SetSign([]byte(v)) + + return sig +} + +func generatePutRequestBody(n string) *container.PutRequestBody { + req := new(container.PutRequestBody) + req.SetContainer(generateContainer(n)) + req.SetSignature(generateSignature("public key", "signature")) + + return req +} + +func generatePutResponseBody(id string) *container.PutResponseBody { + cid := new(refs.ContainerID) + cid.SetValue([]byte(id)) + + resp := new(container.PutResponseBody) + resp.SetContainerID(cid) + + return resp +} + +func generateDeleteRequestBody(id string) *container.DeleteRequestBody { + cid := new(refs.ContainerID) + cid.SetValue([]byte(id)) + + req := new(container.DeleteRequestBody) + req.SetContainerID(cid) + req.SetSignature(generateSignature("public key", "signature")) + + return req +} + +func generateDeleteResponseBody() *container.DeleteResponseBody { + return new(container.DeleteResponseBody) +} + +func generateGetRequestBody(id string) *container.GetRequestBody { + cid := new(refs.ContainerID) + cid.SetValue([]byte(id)) + + req := new(container.GetRequestBody) + req.SetContainerID(cid) + + return req +} + +func generateGetResponseBody(n string) *container.GetResponseBody { + resp := new(container.GetResponseBody) + resp.SetContainer(generateContainer(n)) + + return resp +} + +func generateListRequestBody(id string) *container.ListRequestBody { + owner := new(refs.OwnerID) + owner.SetValue([]byte(id)) + + req := new(container.ListRequestBody) + req.SetOwnerID(owner) + + return req +} + +func generateListResponseBody(n int) *container.ListResponseBody { + resp := new(container.ListResponseBody) + + ids := make([]*refs.ContainerID, n) + for i := 0; i < n; i++ { + cid := new(refs.ContainerID) + cid.SetValue([]byte(fmt.Sprintf("Container ID %d", n+1))) + ids[i] = cid + } + + resp.SetContainerIDs(ids) + + return resp +} + +func generateEACL(n int, k, v string) *acl.Table { + target := new(acl.TargetInfo) + target.SetTarget(acl.TargetUser) + + keys := make([][]byte, n) + + for i := 0; i < n; i++ { + s := fmt.Sprintf("Public Key %d", i+1) + keys[i] = []byte(s) + } + + filter := new(acl.HeaderFilter) + filter.SetHeaderType(acl.HeaderTypeObject) + filter.SetMatchType(acl.MatchTypeStringEqual) + filter.SetName(k) + filter.SetValue(v) + + record := new(acl.Record) + record.SetOperation(acl.OperationHead) + record.SetAction(acl.ActionDeny) + record.SetTargets([]*acl.TargetInfo{target}) + record.SetFilters([]*acl.HeaderFilter{filter}) + + table := new(acl.Table) + cid := new(refs.ContainerID) + cid.SetValue([]byte("Container ID")) + + table.SetContainerID(cid) + table.SetRecords([]*acl.Record{record}) + + return table +} + +func generateSetEACLRequestBody(n int, k, v string) *container.SetExtendedACLRequestBody { + req := new(container.SetExtendedACLRequestBody) + req.SetEACL(generateEACL(n, k, v)) + req.SetSignature(generateSignature("public key", "signature")) + + return req +} + +func generateSetEACLResponseBody() *container.SetExtendedACLResponseBody { + return new(container.SetExtendedACLResponseBody) +} + +func generateGetEACLRequestBody(id string) *container.GetExtendedACLRequestBody { + cid := new(refs.ContainerID) + cid.SetValue([]byte(id)) + + req := new(container.GetExtendedACLRequestBody) + req.SetContainerID(cid) + + return req +} + +func generateGetEACLResponseBody(n int, k, v string) *container.GetExtendedACLResponseBody { + resp := new(container.GetExtendedACLResponseBody) + resp.SetEACL(generateEACL(n, k, v)) + resp.SetSignature(generateSignature("public key", "signature")) + + return resp +} diff --git a/v2/netmap/marshal.go b/v2/netmap/marshal.go new file mode 100644 index 0000000..99a24b4 --- /dev/null +++ b/v2/netmap/marshal.go @@ -0,0 +1,11 @@ +package netmap + +func (p *PlacementPolicy) StableMarshal(buf []byte) ([]byte, error) { + // todo: implement me + return nil, nil +} + +func (p *PlacementPolicy) StableSize() (size int) { + // todo: implement me + return 0 +}