[#170] storagegroup: Refactor and document package functionality

Signed-off-by: Pavel Karpy <carpawell@nspcc.ru>
This commit is contained in:
Pavel Karpy 2021-03-22 18:04:16 +03:00 committed by LeL
parent 1186f2f703
commit 9b63c07c59
5 changed files with 244 additions and 55 deletions

36
storagegroup/doc.go Normal file
View file

@ -0,0 +1,36 @@
/*
Package storagegroup provides features to work with information that is
used for proof of storage in NeoFS system.
StorageGroup type groups verification values for Data Audit sessions:
// receive sg info
sg.ExpirationEpoch() // expiration of the storage group
sg.Members() // objects in the group
sg.ValidationDataHash() // hash for objects validation
sg.ValidationDataSize() // total objects' payload size
Instances can be also used to process NeoFS API V2 protocol messages
(see neo.fs.v2.storagegroup package in https://github.com/nspcc-dev/neofs-api).
On client side:
import "github.com/nspcc-dev/neofs-api-go/v2/storagegroup"
var msg storagegroup.StorageGroup
sg.WriteToV2(&msg)
// send msg
On server side:
// recv msg
var sg StorageGroupDecimal
sg.ReadFromV2(msg)
// process sg
Using package types in an application is recommended to potentially work with
different protocol versions with which these types are compatible.
*/
package storagegroup

View file

@ -7,35 +7,47 @@ import (
oid "github.com/nspcc-dev/neofs-sdk-go/object/id" oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
) )
// StorageGroup represents v2-compatible storage group. // StorageGroup represents storage group of the NeoFS objects.
//
// StorageGroup is mutually compatible with github.com/nspcc-dev/neofs-api-go/v2/storagegroup.StorageGroup
// message. See ReadFromMessageV2 / WriteToMessageV2 methods.
//
// Instances can be created using built-in var declaration.
//
// Note that direct typecast is not safe and may result in loss of compatibility:
// _ = StorageGroup(storagegroup.StorageGroup) // not recommended
type StorageGroup storagegroup.StorageGroup type StorageGroup storagegroup.StorageGroup
// NewFromV2 wraps v2 StorageGroup message to StorageGroup. // ReadFromV2 reads StorageGroup from the storagegroup.StorageGroup message.
// //
// Nil storagegroup.StorageGroup converts to nil. // See also WriteToV2.
func NewFromV2(aV2 *storagegroup.StorageGroup) *StorageGroup { func (sg *StorageGroup) ReadFromV2(m storagegroup.StorageGroup) {
return (*StorageGroup)(aV2) *sg = StorageGroup(m)
} }
// New creates and initializes blank StorageGroup. // WriteToV2 writes StorageGroup to the storagegroup.StorageGroup message.
// The message must not be nil.
// //
// Defaults: // See also ReadFromV2.
// - size: 0; func (sg StorageGroup) WriteToV2(m *storagegroup.StorageGroup) {
// - exp: 0; *m = (storagegroup.StorageGroup)(sg)
// - members: nil;
// - hash: nil.
func New() *StorageGroup {
return NewFromV2(new(storagegroup.StorageGroup))
} }
// ValidationDataSize returns total size of the payloads // ValidationDataSize returns total size of the payloads
// of objects in the storage group. // of objects in the storage group.
func (sg *StorageGroup) ValidationDataSize() uint64 { //
return (*storagegroup.StorageGroup)(sg).GetValidationDataSize() // Zero StorageGroup has 0 data size.
//
// See also SetValidationDataSize.
func (sg StorageGroup) ValidationDataSize() uint64 {
v2 := (storagegroup.StorageGroup)(sg)
return v2.GetValidationDataSize()
} }
// SetValidationDataSize sets total size of the payloads // SetValidationDataSize sets total size of the payloads
// of objects in the storage group. // of objects in the storage group.
//
// See also ValidationDataSize.
func (sg *StorageGroup) SetValidationDataSize(epoch uint64) { func (sg *StorageGroup) SetValidationDataSize(epoch uint64) {
(*storagegroup.StorageGroup)(sg).SetValidationDataSize(epoch) (*storagegroup.StorageGroup)(sg).SetValidationDataSize(epoch)
} }
@ -71,20 +83,32 @@ func (sg *StorageGroup) SetValidationDataHash(hash checksum.Checksum) {
// ExpirationEpoch returns last NeoFS epoch number // ExpirationEpoch returns last NeoFS epoch number
// of the storage group lifetime. // of the storage group lifetime.
func (sg *StorageGroup) ExpirationEpoch() uint64 { //
return (*storagegroup.StorageGroup)(sg).GetExpirationEpoch() // Zero StorageGroup has 0 expiration epoch.
//
// See also SetExpirationEpoch.
func (sg StorageGroup) ExpirationEpoch() uint64 {
v2 := (storagegroup.StorageGroup)(sg)
return v2.GetExpirationEpoch()
} }
// SetExpirationEpoch sets last NeoFS epoch number // SetExpirationEpoch sets last NeoFS epoch number
// of the storage group lifetime. // of the storage group lifetime.
//
// See also ExpirationEpoch.
func (sg *StorageGroup) SetExpirationEpoch(epoch uint64) { func (sg *StorageGroup) SetExpirationEpoch(epoch uint64) {
(*storagegroup.StorageGroup)(sg).SetExpirationEpoch(epoch) (*storagegroup.StorageGroup)(sg).SetExpirationEpoch(epoch)
} }
// Members returns strictly ordered list of // Members returns strictly ordered list of
// storage group member objects. // storage group member objects.
func (sg *StorageGroup) Members() []oid.ID { //
mV2 := (*storagegroup.StorageGroup)(sg).GetMembers() // Zero StorageGroup has nil members value.
//
// See also SetMembers.
func (sg StorageGroup) Members() []oid.ID {
v2 := (storagegroup.StorageGroup)(sg)
mV2 := v2.GetMembers()
if mV2 == nil { if mV2 == nil {
return nil return nil
@ -101,6 +125,8 @@ func (sg *StorageGroup) Members() []oid.ID {
// SetMembers sets strictly ordered list of // SetMembers sets strictly ordered list of
// storage group member objects. // storage group member objects.
//
// See also Members.
func (sg *StorageGroup) SetMembers(members []oid.ID) { func (sg *StorageGroup) SetMembers(members []oid.ID) {
mV2 := (*storagegroup.StorageGroup)(sg).GetMembers() mV2 := (*storagegroup.StorageGroup)(sg).GetMembers()
@ -126,29 +152,57 @@ func (sg *StorageGroup) SetMembers(members []oid.ID) {
(*storagegroup.StorageGroup)(sg).SetMembers(mV2) (*storagegroup.StorageGroup)(sg).SetMembers(mV2)
} }
// ToV2 converts StorageGroup to v2 StorageGroup message.
//
// Nil StorageGroup converts to nil.
func (sg *StorageGroup) ToV2() *storagegroup.StorageGroup {
return (*storagegroup.StorageGroup)(sg)
}
// Marshal marshals StorageGroup into a protobuf binary form. // Marshal marshals StorageGroup into a protobuf binary form.
func (sg *StorageGroup) Marshal() ([]byte, error) { //
return (*storagegroup.StorageGroup)(sg).StableMarshal(nil) // See also Unmarshal.
func (sg StorageGroup) Marshal() ([]byte, error) {
v2 := (storagegroup.StorageGroup)(sg)
return v2.StableMarshal(nil)
} }
// Unmarshal unmarshals protobuf binary representation of StorageGroup. // Unmarshal unmarshals protobuf binary representation of StorageGroup.
//
// See also Marshal.
func (sg *StorageGroup) Unmarshal(data []byte) error { func (sg *StorageGroup) Unmarshal(data []byte) error {
return (*storagegroup.StorageGroup)(sg).Unmarshal(data) v2 := (*storagegroup.StorageGroup)(sg)
err := v2.Unmarshal(data)
if err != nil {
return err
}
return formatCheck(v2)
} }
// MarshalJSON encodes StorageGroup to protobuf JSON format. // MarshalJSON encodes StorageGroup to protobuf JSON format.
func (sg *StorageGroup) MarshalJSON() ([]byte, error) { //
return (*storagegroup.StorageGroup)(sg).MarshalJSON() // See also UnmarshalJSON.
func (sg StorageGroup) MarshalJSON() ([]byte, error) {
v2 := (storagegroup.StorageGroup)(sg)
return v2.MarshalJSON()
} }
// UnmarshalJSON decodes StorageGroup from protobuf JSON format. // UnmarshalJSON decodes StorageGroup from protobuf JSON format.
//
// See also MarshalJSON.
func (sg *StorageGroup) UnmarshalJSON(data []byte) error { func (sg *StorageGroup) UnmarshalJSON(data []byte) error {
return (*storagegroup.StorageGroup)(sg).UnmarshalJSON(data) v2 := (*storagegroup.StorageGroup)(sg)
err := v2.UnmarshalJSON(data)
if err != nil {
return err
}
return formatCheck(v2)
}
func formatCheck(v2 *storagegroup.StorageGroup) error {
var oID oid.ID
for _, m := range v2.GetMembers() {
err := oID.ReadFromV2(m)
if err != nil {
return err
}
}
return nil
} }

View file

@ -1,9 +1,13 @@
package storagegroup_test package storagegroup_test
import ( import (
"crypto/sha256"
"testing" "testing"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
storagegroupV2 "github.com/nspcc-dev/neofs-api-go/v2/storagegroup" storagegroupV2 "github.com/nspcc-dev/neofs-api-go/v2/storagegroup"
storagegroupV2test "github.com/nspcc-dev/neofs-api-go/v2/storagegroup/test"
"github.com/nspcc-dev/neofs-sdk-go/checksum"
checksumtest "github.com/nspcc-dev/neofs-sdk-go/checksum/test" checksumtest "github.com/nspcc-dev/neofs-sdk-go/checksum/test"
oid "github.com/nspcc-dev/neofs-sdk-go/object/id" oid "github.com/nspcc-dev/neofs-sdk-go/object/id"
oidtest "github.com/nspcc-dev/neofs-sdk-go/object/id/test" oidtest "github.com/nspcc-dev/neofs-sdk-go/object/id/test"
@ -13,7 +17,7 @@ import (
) )
func TestStorageGroup(t *testing.T) { func TestStorageGroup(t *testing.T) {
sg := storagegroup.New() var sg storagegroup.StorageGroup
sz := uint64(13) sz := uint64(13)
sg.SetValidationDataSize(sz) sg.SetValidationDataSize(sz)
@ -35,6 +39,56 @@ func TestStorageGroup(t *testing.T) {
require.Equal(t, members, sg.Members()) require.Equal(t, members, sg.Members())
} }
func TestStorageGroup_ReadFromV2(t *testing.T) {
t.Run("from zero", func(t *testing.T) {
var (
x storagegroup.StorageGroup
v2 storagegroupV2.StorageGroup
)
x.ReadFromV2(v2)
require.Zero(t, x.ExpirationEpoch())
require.Zero(t, x.ValidationDataSize())
_, set := x.ValidationDataHash()
require.False(t, set)
require.Zero(t, x.Members())
})
t.Run("from non-zero", func(t *testing.T) {
var (
x storagegroup.StorageGroup
v2 = storagegroupV2test.GenerateStorageGroup(false)
)
// https://github.com/nspcc-dev/neofs-api-go/issues/394
v2.SetMembers(generateOIDList())
size := v2.GetValidationDataSize()
epoch := v2.GetExpirationEpoch()
mm := v2.GetMembers()
hashV2 := v2.GetValidationHash()
x.ReadFromV2(*v2)
require.Equal(t, epoch, x.ExpirationEpoch())
require.Equal(t, size, x.ValidationDataSize())
var hash checksum.Checksum
hash.ReadFromV2(*hashV2)
h, set := x.ValidationDataHash()
require.True(t, set)
require.Equal(t, hash, h)
var oidV2 refs.ObjectID
for i, m := range mm {
x.Members()[i].WriteToV2(&oidV2)
require.Equal(t, m, oidV2)
}
})
}
func TestStorageGroupEncoding(t *testing.T) { func TestStorageGroupEncoding(t *testing.T) {
sg := storagegrouptest.StorageGroup() sg := storagegrouptest.StorageGroup()
@ -42,7 +96,7 @@ func TestStorageGroupEncoding(t *testing.T) {
data, err := sg.Marshal() data, err := sg.Marshal()
require.NoError(t, err) require.NoError(t, err)
sg2 := storagegroup.New() var sg2 storagegroup.StorageGroup
require.NoError(t, sg2.Unmarshal(data)) require.NoError(t, sg2.Unmarshal(data))
require.Equal(t, sg, sg2) require.Equal(t, sg, sg2)
@ -52,32 +106,58 @@ func TestStorageGroupEncoding(t *testing.T) {
data, err := sg.MarshalJSON() data, err := sg.MarshalJSON()
require.NoError(t, err) require.NoError(t, err)
sg2 := storagegroup.New() var sg2 storagegroup.StorageGroup
require.NoError(t, sg2.UnmarshalJSON(data)) require.NoError(t, sg2.UnmarshalJSON(data))
require.Equal(t, sg, sg2) require.Equal(t, sg, sg2)
}) })
} }
func TestNewFromV2(t *testing.T) { func TestStorageGroup_WriteToV2(t *testing.T) {
t.Run("from nil", func(t *testing.T) { t.Run("zero to v2", func(t *testing.T) {
var x *storagegroupV2.StorageGroup var (
x storagegroup.StorageGroup
v2 storagegroupV2.StorageGroup
)
require.Nil(t, storagegroup.NewFromV2(x)) x.WriteToV2(&v2)
require.Nil(t, v2.GetValidationHash())
require.Nil(t, v2.GetMembers())
require.Zero(t, v2.GetValidationDataSize())
require.Zero(t, v2.GetExpirationEpoch())
}) })
t.Run("non-zero to v2", func(t *testing.T) {
var (
x = storagegrouptest.StorageGroup()
v2 storagegroupV2.StorageGroup
)
x.WriteToV2(&v2)
require.Equal(t, x.ExpirationEpoch(), v2.GetExpirationEpoch())
require.Equal(t, x.ValidationDataSize(), v2.GetValidationDataSize())
var hash checksum.Checksum
hash.ReadFromV2(*v2.GetValidationHash())
h, set := x.ValidationDataHash()
require.True(t, set)
require.Equal(t, h, hash)
var oidV2 refs.ObjectID
for i, m := range x.Members() {
m.WriteToV2(&oidV2)
require.Equal(t, oidV2, v2.GetMembers()[i])
} }
func TestStorageGroup_ToV2(t *testing.T) {
t.Run("nil", func(t *testing.T) {
var x *storagegroup.StorageGroup
require.Nil(t, x.ToV2())
}) })
} }
func TestNew(t *testing.T) { func TestNew(t *testing.T) {
t.Run("default values", func(t *testing.T) { t.Run("default values", func(t *testing.T) {
sg := storagegroup.New() var sg storagegroup.StorageGroup
// check initial values // check initial values
require.Nil(t, sg.Members()) require.Nil(t, sg.Members())
@ -85,13 +165,19 @@ func TestNew(t *testing.T) {
require.False(t, set) require.False(t, set)
require.Zero(t, sg.ExpirationEpoch()) require.Zero(t, sg.ExpirationEpoch())
require.Zero(t, sg.ValidationDataSize()) require.Zero(t, sg.ValidationDataSize())
// convert to v2 message
sgV2 := sg.ToV2()
require.Nil(t, sgV2.GetMembers())
require.Nil(t, sgV2.GetValidationHash())
require.Zero(t, sgV2.GetExpirationEpoch())
require.Zero(t, sgV2.GetValidationDataSize())
}) })
} }
func generateOIDList() []refs.ObjectID {
const size = 3
mmV2 := make([]refs.ObjectID, size)
for i := 0; i < size; i++ {
oidV2 := make([]byte, sha256.Size)
oidV2[i] = byte(i)
mmV2[i].SetValue(oidV2)
}
return mmV2
}

13
storagegroup/test/doc.go Normal file
View file

@ -0,0 +1,13 @@
/*
Package storagegrouptest provides functions for convenient testing of storagegroup package API.
Note that importing the package into source files is highly discouraged.
Random instance generation functions can be useful when testing expects any value, e.g.:
import storagegrouptest "github.com/nspcc-dev/neofs-sdk-go/storagegroup/test"
val := storagegrouptest.StorageGroup()
// test the value
*/
package storagegrouptest

View file

@ -8,8 +8,8 @@ import (
) )
// StorageGroup returns random storagegroup.StorageGroup. // StorageGroup returns random storagegroup.StorageGroup.
func StorageGroup() *storagegroup.StorageGroup { func StorageGroup() storagegroup.StorageGroup {
x := storagegroup.New() var x storagegroup.StorageGroup
x.SetExpirationEpoch(66) x.SetExpirationEpoch(66)
x.SetValidationDataSize(322) x.SetValidationDataSize(322)