From 6861de042b0275040154dae3718e97da6544b01e Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Thu, 17 Dec 2020 17:37:58 +0300 Subject: [PATCH] [#230] pkg: Implement storage group type with basic methods Signed-off-by: Leonard Lyubich --- pkg/storagegroup/storagegroup.go | 147 ++++++++++++++++++++++++++ pkg/storagegroup/storagegroup_test.go | 79 ++++++++++++++ 2 files changed, 226 insertions(+) create mode 100644 pkg/storagegroup/storagegroup.go create mode 100644 pkg/storagegroup/storagegroup_test.go diff --git a/pkg/storagegroup/storagegroup.go b/pkg/storagegroup/storagegroup.go new file mode 100644 index 0000000..0ae52ab --- /dev/null +++ b/pkg/storagegroup/storagegroup.go @@ -0,0 +1,147 @@ +package storagegroup + +import ( + "github.com/nspcc-dev/neofs-api-go/pkg" + "github.com/nspcc-dev/neofs-api-go/pkg/object" + "github.com/nspcc-dev/neofs-api-go/v2/refs" + "github.com/nspcc-dev/neofs-api-go/v2/storagegroup" +) + +// StorageGroup represents v2-compatible storage group. +type StorageGroup storagegroup.StorageGroup + +// NewFromV2 wraps v2 StorageGroup message to StorageGroup. +func NewFromV2(aV2 *storagegroup.StorageGroup) *StorageGroup { + return (*StorageGroup)(aV2) +} + +// New creates and initializes blank StorageGroup. +func New() *StorageGroup { + return NewFromV2(new(storagegroup.StorageGroup)) +} + +// ValidationDataSize returns total size of the payloads +// of objects in the storage group +func (sg *StorageGroup) ValidationDataSize() uint64 { + return (*storagegroup.StorageGroup)(sg). + GetValidationDataSize() +} + +// SetValidationDataSize sets total size of the payloads +// of objects in the storage group. +func (sg *StorageGroup) SetValidationDataSize(epoch uint64) { + (*storagegroup.StorageGroup)(sg). + SetValidationDataSize(epoch) +} + +// ValidationDataHash returns homomorphic hash from the +// concatenation of the payloads of the storage group members. +func (sg *StorageGroup) ValidationDataHash() *pkg.Checksum { + return pkg.NewChecksumFromV2( + (*storagegroup.StorageGroup)(sg). + GetValidationHash(), + ) +} + +// SetValidationDataHash sets homomorphic hash from the +// concatenation of the payloads of the storage group members. +func (sg *StorageGroup) SetValidationDataHash(hash *pkg.Checksum) { + (*storagegroup.StorageGroup)(sg). + SetValidationHash(hash.ToV2()) +} + +// ExpirationEpoch returns last NeoFS epoch number +// of the storage group lifetime. +func (sg *StorageGroup) ExpirationEpoch() uint64 { + return (*storagegroup.StorageGroup)(sg). + GetExpirationEpoch() +} + +// SetExpirationEpoch sets last NeoFS epoch number +// of the storage group lifetime. +func (sg *StorageGroup) SetExpirationEpoch(epoch uint64) { + (*storagegroup.StorageGroup)(sg). + SetExpirationEpoch(epoch) +} + +// Members returns strictly ordered list of +// storage group member objects. +func (sg *StorageGroup) Members() []*object.ID { + mV2 := (*storagegroup.StorageGroup)(sg). + GetMembers() + + if mV2 == nil { + return nil + } + + m := make([]*object.ID, len(mV2)) + + for i := range mV2 { + m[i] = object.NewIDFromV2(mV2[i]) + } + + return m +} + +// SetMembers sets strictly ordered list of +// storage group member objects. +func (sg *StorageGroup) SetMembers(members []*object.ID) { + mV2 := (*storagegroup.StorageGroup)(sg). + GetMembers() + + if members == nil { + mV2 = nil + } else { + ln := len(members) + + if cap(mV2) >= ln { + mV2 = mV2[:0] + } else { + mV2 = make([]*refs.ObjectID, 0, ln) + } + + for i := 0; i < ln; i++ { + mV2 = append(mV2, members[i].ToV2()) + } + } + + (*storagegroup.StorageGroup)(sg). + SetMembers(mV2) +} + +// ToV2 converts StorageGroup to v2 StorageGroup message. +func (sg *StorageGroup) ToV2() *storagegroup.StorageGroup { + return (*storagegroup.StorageGroup)(sg) +} + +// Marshal marshals StorageGroup into a protobuf binary form. +// +// Buffer is allocated when the argument is empty. +// Otherwise, the first buffer is used. +func (sg *StorageGroup) Marshal(b ...[]byte) ([]byte, error) { + var buf []byte + if len(b) > 0 { + buf = b[0] + } + + return (*storagegroup.StorageGroup)(sg). + StableMarshal(buf) +} + +// Unmarshal unmarshals protobuf binary representation of StorageGroup. +func (sg *StorageGroup) Unmarshal(data []byte) error { + return (*storagegroup.StorageGroup)(sg). + Unmarshal(data) +} + +// MarshalJSON encodes StorageGroup to protobuf JSON format. +func (sg *StorageGroup) MarshalJSON() ([]byte, error) { + return (*storagegroup.StorageGroup)(sg). + MarshalJSON() +} + +// UnmarshalJSON decodes StorageGroup from protobuf JSON format. +func (sg *StorageGroup) UnmarshalJSON(data []byte) error { + return (*storagegroup.StorageGroup)(sg). + UnmarshalJSON(data) +} diff --git a/pkg/storagegroup/storagegroup_test.go b/pkg/storagegroup/storagegroup_test.go new file mode 100644 index 0000000..027c0f6 --- /dev/null +++ b/pkg/storagegroup/storagegroup_test.go @@ -0,0 +1,79 @@ +package storagegroup_test + +import ( + "crypto/rand" + "crypto/sha256" + "testing" + + "github.com/nspcc-dev/neofs-api-go/pkg" + "github.com/nspcc-dev/neofs-api-go/pkg/object" + "github.com/nspcc-dev/neofs-api-go/pkg/storagegroup" + "github.com/stretchr/testify/require" +) + +func testSHA256() (cs [sha256.Size]byte) { + _, _ = rand.Read(cs[:]) + return +} + +func testChecksum() *pkg.Checksum { + h := pkg.NewChecksum() + h.SetSHA256(testSHA256()) + + return h +} + +func testOID() *object.ID { + id := object.NewID() + id.SetSHA256(testSHA256()) + + return id +} + +func TestStorageGroup(t *testing.T) { + sg := storagegroup.New() + + sz := uint64(13) + sg.SetValidationDataSize(sz) + require.Equal(t, sz, sg.ValidationDataSize()) + + cs := testChecksum() + sg.SetValidationDataHash(cs) + require.Equal(t, cs, sg.ValidationDataHash()) + + exp := uint64(33) + sg.SetExpirationEpoch(exp) + require.Equal(t, exp, sg.ExpirationEpoch()) + + members := []*object.ID{testOID(), testOID()} + sg.SetMembers(members) + require.Equal(t, members, sg.Members()) +} + +func TestStorageGroupEncoding(t *testing.T) { + sg := storagegroup.New() + sg.SetValidationDataSize(13) + sg.SetValidationDataHash(testChecksum()) + sg.SetExpirationEpoch(33) + sg.SetMembers([]*object.ID{testOID(), testOID()}) + + t.Run("binary", func(t *testing.T) { + data, err := sg.Marshal() + require.NoError(t, err) + + sg2 := storagegroup.New() + require.NoError(t, sg2.Unmarshal(data)) + + require.Equal(t, sg, sg2) + }) + + t.Run("json", func(t *testing.T) { + data, err := sg.MarshalJSON() + require.NoError(t, err) + + sg2 := storagegroup.New() + require.NoError(t, sg2.UnmarshalJSON(data)) + + require.Equal(t, sg, sg2) + }) +}