diff --git a/audit/result.go b/audit/result.go new file mode 100644 index 0000000..c4773cc --- /dev/null +++ b/audit/result.go @@ -0,0 +1,291 @@ +package audit + +import ( + "github.com/nspcc-dev/neofs-api-go/v2/audit" + "github.com/nspcc-dev/neofs-api-go/v2/refs" + cid "github.com/nspcc-dev/neofs-sdk-go/container/id" + "github.com/nspcc-dev/neofs-sdk-go/object" + "github.com/nspcc-dev/neofs-sdk-go/version" +) + +// Result represents v2-compatible data audit result. +type Result audit.DataAuditResult + +// NewFromV2 wraps v2 DataAuditResult message to Result. +// +// Nil audit.DataAuditResult converts to nil. +func NewResultFromV2(aV2 *audit.DataAuditResult) *Result { + return (*Result)(aV2) +} + +// New creates and initializes blank Result. +// +// Defaults: +// - version: version.Current(); +// - complete: false; +// - cid: nil; +// - pubKey: nil; +// - passSG, failSG: nil; +// - failNodes, passNodes: nil; +// - hit, miss, fail: 0; +// - requests, retries: 0; +// - auditEpoch: 0. +func NewResult() *Result { + r := NewResultFromV2(new(audit.DataAuditResult)) + r.SetVersion(version.Current()) + + return r +} + +// ToV2 converts Result to v2 DataAuditResult message. +// +// Nil Result converts to nil. +func (r *Result) ToV2() *audit.DataAuditResult { + return (*audit.DataAuditResult)(r) +} + +// Marshal marshals Result into a protobuf binary form. +func (r *Result) Marshal() ([]byte, error) { + return (*audit.DataAuditResult)(r).StableMarshal(nil) +} + +// Unmarshal unmarshals protobuf binary representation of Result. +func (r *Result) Unmarshal(data []byte) error { + return (*audit.DataAuditResult)(r).Unmarshal(data) +} + +// MarshalJSON encodes Result to protobuf JSON format. +func (r *Result) MarshalJSON() ([]byte, error) { + return (*audit.DataAuditResult)(r).MarshalJSON() +} + +// UnmarshalJSON decodes Result from protobuf JSON format. +func (r *Result) UnmarshalJSON(data []byte) error { + return (*audit.DataAuditResult)(r).UnmarshalJSON(data) +} + +// Version returns Data Audit structure version. +func (r *Result) Version() *version.Version { + return version.NewFromV2( + (*audit.DataAuditResult)(r).GetVersion()) +} + +// SetVersion sets Data Audit structure version. +func (r *Result) SetVersion(v *version.Version) { + (*audit.DataAuditResult)(r).SetVersion(v.ToV2()) +} + +// AuditEpoch returns epoch number when the Data Audit was conducted. +func (r *Result) AuditEpoch() uint64 { + return (*audit.DataAuditResult)(r).GetAuditEpoch() +} + +// SetAuditEpoch sets epoch number when the Data Audit was conducted. +func (r *Result) SetAuditEpoch(epoch uint64) { + (*audit.DataAuditResult)(r).SetAuditEpoch(epoch) +} + +// ContainerID returns container under audit. +func (r *Result) ContainerID() *cid.ID { + return cid.NewFromV2( + (*audit.DataAuditResult)(r).GetContainerID()) +} + +// SetContainerID sets container under audit. +func (r *Result) SetContainerID(id *cid.ID) { + (*audit.DataAuditResult)(r).SetContainerID(id.ToV2()) +} + +// PublicKey returns public key of the auditing InnerRing node in a binary format. +func (r *Result) PublicKey() []byte { + return (*audit.DataAuditResult)(r).GetPublicKey() +} + +// SetPublicKey sets public key of the auditing InnerRing node in a binary format. +func (r *Result) SetPublicKey(key []byte) { + (*audit.DataAuditResult)(r).SetPublicKey(key) +} + +// Complete returns completion state of audit result. +func (r *Result) Complete() bool { + return (*audit.DataAuditResult)(r).GetComplete() +} + +// SetComplete sets completion state of audit result. +func (r *Result) SetComplete(v bool) { + (*audit.DataAuditResult)(r).SetComplete(v) +} + +// Requests returns number of requests made by PoR audit check to get +// all headers of the objects inside storage groups. +func (r *Result) Requests() uint32 { + return (*audit.DataAuditResult)(r).GetRequests() +} + +// SetRequests sets number of requests made by PoR audit check to get +// all headers of the objects inside storage groups. +func (r *Result) SetRequests(v uint32) { + (*audit.DataAuditResult)(r).SetRequests(v) +} + +// Retries returns number of retries made by PoR audit check to get +// all headers of the objects inside storage groups. +func (r *Result) Retries() uint32 { + return (*audit.DataAuditResult)(r).GetRetries() +} + +// SetRetries sets number of retries made by PoR audit check to get +// all headers of the objects inside storage groups. +func (r *Result) SetRetries(v uint32) { + (*audit.DataAuditResult)(r).SetRetries(v) +} + +// PassSG returns list of Storage Groups that passed audit PoR stage. +func (r *Result) PassSG() []*object.ID { + mV2 := (*audit.DataAuditResult)(r). + GetPassSG() + + if mV2 == nil { + return nil + } + + m := make([]*object.ID, len(mV2)) + + for i := range mV2 { + m[i] = object.NewIDFromV2(mV2[i]) + } + + return m +} + +// SetPassSG sets list of Storage Groups that passed audit PoR stage. +func (r *Result) SetPassSG(list []*object.ID) { + mV2 := (*audit.DataAuditResult)(r). + GetPassSG() + + if list == nil { + mV2 = nil + } else { + ln := len(list) + + if cap(mV2) >= ln { + mV2 = mV2[:0] + } else { + mV2 = make([]*refs.ObjectID, 0, ln) + } + + for i := 0; i < ln; i++ { + mV2 = append(mV2, list[i].ToV2()) + } + } + + (*audit.DataAuditResult)(r).SetPassSG(mV2) +} + +// FailSG returns list of Storage Groups that failed audit PoR stage. +func (r *Result) FailSG() []*object.ID { + mV2 := (*audit.DataAuditResult)(r). + GetFailSG() + + if mV2 == nil { + return nil + } + + m := make([]*object.ID, len(mV2)) + + for i := range mV2 { + m[i] = object.NewIDFromV2(mV2[i]) + } + + return m +} + +// SetFailSG sets list of Storage Groups that failed audit PoR stage. +func (r *Result) SetFailSG(list []*object.ID) { + mV2 := (*audit.DataAuditResult)(r). + GetFailSG() + + if list == nil { + mV2 = nil + } else { + ln := len(list) + + if cap(mV2) >= ln { + mV2 = mV2[:0] + } else { + mV2 = make([]*refs.ObjectID, 0, ln) + } + + for i := 0; i < ln; i++ { + mV2 = append(mV2, list[i].ToV2()) + } + } + + (*audit.DataAuditResult)(r).SetFailSG(mV2) +} + +// Hit returns number of sampled objects under audit placed +// in an optimal way according to the containers placement policy +// when checking PoP. +func (r *Result) Hit() uint32 { + return (*audit.DataAuditResult)(r).GetHit() +} + +// SetHit sets number of sampled objects under audit placed +// in an optimal way according to the containers placement policy +// when checking PoP. +func (r *Result) SetHit(hit uint32) { + (*audit.DataAuditResult)(r).SetHit(hit) +} + +// Miss returns number of sampled objects under audit placed +// in suboptimal way according to the containers placement policy, +// but still at a satisfactory level when checking PoP. +func (r *Result) Miss() uint32 { + return (*audit.DataAuditResult)(r).GetMiss() +} + +// SetMiss sets number of sampled objects under audit placed +// in suboptimal way according to the containers placement policy, +// but still at a satisfactory level when checking PoP. +func (r *Result) SetMiss(miss uint32) { + (*audit.DataAuditResult)(r).SetMiss(miss) +} + +// Fail returns number of sampled objects under audit stored +// in a way not confirming placement policy or not found at all +// when checking PoP. +func (r *Result) Fail() uint32 { + return (*audit.DataAuditResult)(r).GetFail() +} + +// SetFail sets number of sampled objects under audit stored +// in a way not confirming placement policy or not found at all +// when checking PoP. +func (r *Result) SetFail(fail uint32) { + (*audit.DataAuditResult)(r).SetFail(fail) +} + +// PassNodes returns list of storage node public keys that +// passed at least one PDP. +func (r *Result) PassNodes() [][]byte { + return (*audit.DataAuditResult)(r).GetPassNodes() +} + +// SetPassNodes sets list of storage node public keys that +// passed at least one PDP. +func (r *Result) SetPassNodes(list [][]byte) { + (*audit.DataAuditResult)(r).SetPassNodes(list) +} + +// FailNodes returns list of storage node public keys that +// failed at least one PDP. +func (r *Result) FailNodes() [][]byte { + return (*audit.DataAuditResult)(r).GetFailNodes() +} + +// SetFailNodes sets list of storage node public keys that +// failed at least one PDP. +func (r *Result) SetFailNodes(list [][]byte) { + (*audit.DataAuditResult)(r).SetFailNodes(list) +} diff --git a/audit/result_test.go b/audit/result_test.go new file mode 100644 index 0000000..a5d13d5 --- /dev/null +++ b/audit/result_test.go @@ -0,0 +1,154 @@ +package audit_test + +import ( + "testing" + + auditv2 "github.com/nspcc-dev/neofs-api-go/v2/audit" + "github.com/nspcc-dev/neofs-sdk-go/audit" + audittest "github.com/nspcc-dev/neofs-sdk-go/audit/test" + cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test" + "github.com/nspcc-dev/neofs-sdk-go/object" + objecttest "github.com/nspcc-dev/neofs-sdk-go/object/test" + "github.com/nspcc-dev/neofs-sdk-go/version" + "github.com/stretchr/testify/require" +) + +func TestResult(t *testing.T) { + r := audit.NewResult() + require.Equal(t, version.Current(), r.Version()) + + epoch := uint64(13) + r.SetAuditEpoch(epoch) + require.Equal(t, epoch, r.AuditEpoch()) + + cid := cidtest.GenerateID() + r.SetContainerID(cid) + require.Equal(t, cid, r.ContainerID()) + + key := []byte{1, 2, 3} + r.SetPublicKey(key) + require.Equal(t, key, r.PublicKey()) + + r.SetComplete(true) + require.True(t, r.Complete()) + + requests := uint32(2) + r.SetRequests(requests) + require.Equal(t, requests, r.Requests()) + + retries := uint32(1) + r.SetRetries(retries) + require.Equal(t, retries, r.Retries()) + + passSG := []*object.ID{objecttest.ID(), objecttest.ID()} + r.SetPassSG(passSG) + require.Equal(t, passSG, r.PassSG()) + + failSG := []*object.ID{objecttest.ID(), objecttest.ID()} + r.SetFailSG(failSG) + require.Equal(t, failSG, r.FailSG()) + + hit := uint32(1) + r.SetHit(hit) + require.Equal(t, hit, r.Hit()) + + miss := uint32(2) + r.SetMiss(miss) + require.Equal(t, miss, r.Miss()) + + fail := uint32(3) + r.SetFail(fail) + require.Equal(t, fail, r.Fail()) + + passNodes := [][]byte{{1}, {2}} + r.SetPassNodes(passNodes) + require.Equal(t, passNodes, r.PassNodes()) + + failNodes := [][]byte{{3}, {4}} + r.SetFailNodes(failNodes) + require.Equal(t, failNodes, r.FailNodes()) +} + +func TestStorageGroupEncoding(t *testing.T) { + r := audittest.Generate() + + t.Run("binary", func(t *testing.T) { + data, err := r.Marshal() + require.NoError(t, err) + + r2 := audit.NewResult() + require.NoError(t, r2.Unmarshal(data)) + + require.Equal(t, r, r2) + }) + + t.Run("json", func(t *testing.T) { + data, err := r.MarshalJSON() + require.NoError(t, err) + + r2 := audit.NewResult() + require.NoError(t, r2.UnmarshalJSON(data)) + + require.Equal(t, r, r2) + }) +} + +func TestResult_ToV2(t *testing.T) { + t.Run("nil", func(t *testing.T) { + var x *audit.Result + + require.Nil(t, x.ToV2()) + }) + + t.Run("default values", func(t *testing.T) { + result := audit.NewResult() + + // check initial values + require.Equal(t, version.Current(), result.Version()) + + require.False(t, result.Complete()) + + require.Nil(t, result.ContainerID()) + require.Nil(t, result.PublicKey()) + require.Nil(t, result.PassSG()) + require.Nil(t, result.FailSG()) + require.Nil(t, result.PassNodes()) + require.Nil(t, result.FailNodes()) + + require.Zero(t, result.Hit()) + require.Zero(t, result.Miss()) + require.Zero(t, result.Fail()) + require.Zero(t, result.Requests()) + require.Zero(t, result.Retries()) + require.Zero(t, result.AuditEpoch()) + + // convert to v2 message + resultV2 := result.ToV2() + + require.Equal(t, version.Current().ToV2(), resultV2.GetVersion()) + + require.False(t, resultV2.GetComplete()) + + require.Nil(t, resultV2.GetContainerID()) + require.Nil(t, resultV2.GetPublicKey()) + require.Nil(t, resultV2.GetPassSG()) + require.Nil(t, resultV2.GetFailSG()) + require.Nil(t, resultV2.GetPassNodes()) + require.Nil(t, resultV2.GetFailNodes()) + + require.Zero(t, resultV2.GetHit()) + require.Zero(t, resultV2.GetMiss()) + require.Zero(t, resultV2.GetFail()) + require.Zero(t, resultV2.GetRequests()) + require.Zero(t, resultV2.GetRetries()) + require.Zero(t, resultV2.GetAuditEpoch()) + }) +} + +func TestNewResultFromV2(t *testing.T) { + t.Run("from nil", func(t *testing.T) { + var x *auditv2.DataAuditResult + + require.Nil(t, audit.NewResultFromV2(x)) + }) +} diff --git a/audit/test/generate.go b/audit/test/generate.go new file mode 100644 index 0000000..813dffd --- /dev/null +++ b/audit/test/generate.go @@ -0,0 +1,37 @@ +package audittest + +import ( + "github.com/nspcc-dev/neofs-sdk-go/audit" + cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test" + "github.com/nspcc-dev/neofs-sdk-go/object" + objecttest "github.com/nspcc-dev/neofs-sdk-go/object/test" + "github.com/nspcc-dev/neofs-sdk-go/version" +) + +// Generate returns random audit.Result. +func Generate() *audit.Result { + x := audit.NewResult() + + x.SetVersion(version.Current()) + x.SetContainerID(cidtest.GenerateID()) + x.SetPublicKey([]byte("key")) + x.SetComplete(true) + x.SetAuditEpoch(44) + x.SetHit(55) + x.SetMiss(66) + x.SetFail(77) + x.SetRetries(88) + x.SetRequests(99) + x.SetFailNodes([][]byte{ + []byte("node1"), + []byte("node2"), + }) + x.SetPassNodes([][]byte{ + []byte("node3"), + []byte("node4"), + }) + x.SetPassSG([]*object.ID{objecttest.ID(), objecttest.ID()}) + x.SetFailSG([]*object.ID{objecttest.ID(), objecttest.ID()}) + + return x +}