[#233] Implement data audit result type with basic methods

Signed-off-by: Leonard Lyubich <leonard@nspcc.ru>
This commit is contained in:
Leonard Lyubich 2020-12-21 10:52:34 +03:00
parent 3039aa20c7
commit 9986a4ecd1
9 changed files with 959 additions and 0 deletions

258
pkg/audit/result.go Normal file
View file

@ -0,0 +1,258 @@
package audit
import (
"github.com/nspcc-dev/neofs-api-go/pkg/container"
"github.com/nspcc-dev/neofs-api-go/pkg/object"
"github.com/nspcc-dev/neofs-api-go/v2/audit"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
)
// Result represents v2-compatible data audit result.
type Result audit.DataAuditResult
// NewFromV2 wraps v2 DataAuditResult message to Result.
func NewResultFromV2(aV2 *audit.DataAuditResult) *Result {
return (*Result)(aV2)
}
// New creates and initializes blank Result.
func NewResult() *Result {
return NewResultFromV2(new(audit.DataAuditResult))
}
// ToV2 converts Result to v2 DataAuditResult message.
func (r *Result) ToV2() *audit.DataAuditResult {
return (*audit.DataAuditResult)(r)
}
// Marshal marshals Result into a protobuf binary form.
//
// Buffer is allocated when the argument is empty.
// Otherwise, the first buffer is used.
func (r *Result) Marshal(b ...[]byte) ([]byte, error) {
var buf []byte
if len(b) > 0 {
buf = b[0]
}
return (*audit.DataAuditResult)(r).
StableMarshal(buf)
}
// 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)
}
// 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() *container.ID {
return container.NewIDFromV2(
(*audit.DataAuditResult)(r).
GetContainerID(),
)
}
// SetContainerID sets container under audit.
func (r *Result) SetContainerID(id *container.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)
}
// 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)
}

109
pkg/audit/result_test.go Normal file
View file

@ -0,0 +1,109 @@
package audit_test
import (
"crypto/rand"
"crypto/sha256"
"testing"
"github.com/nspcc-dev/neofs-api-go/pkg/audit"
"github.com/nspcc-dev/neofs-api-go/pkg/container"
"github.com/nspcc-dev/neofs-api-go/pkg/object"
"github.com/stretchr/testify/require"
)
func testSHA256() (cs [sha256.Size]byte) {
_, _ = rand.Read(cs[:])
return
}
func testCID() *container.ID {
cid := container.NewID()
cid.SetSHA256(testSHA256())
return cid
}
func testOID() *object.ID {
id := object.NewID()
id.SetSHA256(testSHA256())
return id
}
func TestResult(t *testing.T) {
r := audit.NewResult()
epoch := uint64(13)
r.SetAuditEpoch(epoch)
require.Equal(t, epoch, r.AuditEpoch())
cid := testCID()
r.SetContainerID(cid)
require.Equal(t, cid, r.ContainerID())
key := []byte{1, 2, 3}
r.SetPublicKey(key)
require.Equal(t, key, r.PublicKey())
passSG := []*object.ID{testOID(), testOID()}
r.SetPassSG(passSG)
require.Equal(t, passSG, r.PassSG())
failSG := []*object.ID{testOID(), testOID()}
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 := audit.NewResult()
r.SetAuditEpoch(13)
r.SetContainerID(testCID())
r.SetPublicKey([]byte{1, 2, 3})
r.SetPassSG([]*object.ID{testOID(), testOID()})
r.SetFailSG([]*object.ID{testOID(), testOID()})
r.SetHit(1)
r.SetMiss(2)
r.SetFail(3)
r.SetPassNodes([][]byte{{1}, {2}})
r.SetFailNodes([][]byte{{3}, {4}})
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)
})
}

76
v2/audit/convert.go Normal file
View file

@ -0,0 +1,76 @@
package audit
import (
audit "github.com/nspcc-dev/neofs-api-go/v2/audit/grpc"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
)
// DataAuditResultToGRPCMessage converts unified DataAuditResult structure
// into gRPC DataAuditResult message.
func DataAuditResultToGRPCMessage(a *DataAuditResult) *audit.DataAuditResult {
if a == nil {
return nil
}
m := new(audit.DataAuditResult)
m.SetAuditEpoch(a.GetAuditEpoch())
m.SetContainerId(
refs.ContainerIDToGRPCMessage(a.GetContainerID()),
)
m.SetPublicKey(a.GetPublicKey())
m.SetPassSg(
refs.ObjectIDListToGRPCMessage(a.GetPassSG()),
)
m.SetFailSg(
refs.ObjectIDListToGRPCMessage(a.GetFailSG()),
)
m.SetHit(a.GetHit())
m.SetMiss(a.GetMiss())
m.SetFail(a.GetFail())
m.SetPassNodes(a.GetPassNodes())
m.SetFailNodes(a.GetFailNodes())
return m
}
// DataAuditResultFromGRPCMessage converts gRPC message DataAuditResult
// into unified DataAuditResult structure.
func DataAuditResultFromGRPCMessage(m *audit.DataAuditResult) *DataAuditResult {
if m == nil {
return nil
}
a := new(DataAuditResult)
a.SetAuditEpoch(m.GetAuditEpoch())
a.SetContainerID(
refs.ContainerIDFromGRPCMessage(m.GetContainerId()),
)
a.SetPublicKey(m.GetPublicKey())
a.SetPassSG(
refs.ObjectIDListFromGRPCMessage(m.GetPassSg()),
)
a.SetFailSG(
refs.ObjectIDListFromGRPCMessage(m.GetFailSg()),
)
a.SetHit(m.GetHit())
a.SetMiss(m.GetMiss())
a.SetFail(m.GetFail())
a.SetPassNodes(m.GetPassNodes())
a.SetFailNodes(m.GetFailNodes())
return a
}

75
v2/audit/grpc/types.go Normal file
View file

@ -0,0 +1,75 @@
package audit
import (
refs "github.com/nspcc-dev/neofs-api-go/v2/refs/grpc"
)
// SetAuditEpoch is an AuditEpoch field setter.
func (x *DataAuditResult) SetAuditEpoch(v uint64) {
if x != nil {
x.AuditEpoch = v
}
}
// SetContainerId is a ContainerId field setter.
func (x *DataAuditResult) SetContainerId(v *refs.ContainerID) {
if x != nil {
x.ContainerId = v
}
}
// SetPublicKey is a PublicKey field setter.
func (x *DataAuditResult) SetPublicKey(v []byte) {
if x != nil {
x.PublicKey = v
}
}
// SetPassSg is a PassSg field setter.
func (x *DataAuditResult) SetPassSg(v []*refs.ObjectID) {
if x != nil {
x.PassSg = v
}
}
// SetFailSg is a FailSg field setter.
func (x *DataAuditResult) SetFailSg(v []*refs.ObjectID) {
if x != nil {
x.FailSg = v
}
}
// SetHit is a Hit field setter.
func (x *DataAuditResult) SetHit(v uint32) {
if x != nil {
x.Hit = v
}
}
// SetMiss is a Miss field setter.
func (x *DataAuditResult) SetMiss(v uint32) {
if x != nil {
x.Miss = v
}
}
// SetFail is a Fail field setter.
func (x *DataAuditResult) SetFail(v uint32) {
if x != nil {
x.Fail = v
}
}
// SetPassNodes is a PassNodes field setter.
func (x *DataAuditResult) SetPassNodes(v [][]byte) {
if x != nil {
x.PassNodes = v
}
}
// SetFailNodes is a FailNodes field setter.
func (x *DataAuditResult) SetFailNodes(v [][]byte) {
if x != nil {
x.FailNodes = v
}
}

26
v2/audit/json.go Normal file
View file

@ -0,0 +1,26 @@
package audit
import (
audit "github.com/nspcc-dev/neofs-api-go/v2/audit/grpc"
"google.golang.org/protobuf/encoding/protojson"
)
func (a *DataAuditResult) MarshalJSON() ([]byte, error) {
return protojson.MarshalOptions{
EmitUnpopulated: true,
}.Marshal(
DataAuditResultToGRPCMessage(a),
)
}
func (a *DataAuditResult) UnmarshalJSON(data []byte) error {
msg := new(audit.DataAuditResult)
if err := protojson.Unmarshal(data, msg); err != nil {
return err
}
*a = *DataAuditResultFromGRPCMessage(msg)
return nil
}

20
v2/audit/json_test.go Normal file
View file

@ -0,0 +1,20 @@
package audit_test
import (
"testing"
"github.com/nspcc-dev/neofs-api-go/v2/audit"
"github.com/stretchr/testify/require"
)
func TestDataAuditResultJSON(t *testing.T) {
a := generateDataAuditResult()
data, err := a.MarshalJSON()
require.NoError(t, err)
a2 := new(audit.DataAuditResult)
require.NoError(t, a2.UnmarshalJSON(data))
require.Equal(t, a, a2)
}

143
v2/audit/marshal.go Normal file
View file

@ -0,0 +1,143 @@
package audit
import (
"github.com/nspcc-dev/neofs-api-go/util/proto"
audit "github.com/nspcc-dev/neofs-api-go/v2/audit/grpc"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
goproto "google.golang.org/protobuf/proto"
)
const (
_ = iota
auditEpochFNum
cidFNum
pubKeyFNum
passSGFNum
failSGFNum
hitFNum
missFNum
failFNum
passNodesFNum
failNodesFNum
)
// StableMarshal marshals unified DataAuditResult structure into a protobuf
// binary format without field order shuffle.
func (a *DataAuditResult) 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.Fixed64Marshal(auditEpochFNum, buf[offset:], a.auditEpoch)
if err != nil {
return nil, err
}
offset += n
n, err = proto.NestedStructureMarshal(cidFNum, buf[offset:], a.cid)
if err != nil {
return nil, err
}
offset += n
n, err = proto.BytesMarshal(pubKeyFNum, buf[offset:], a.pubKey)
if err != nil {
return nil, err
}
offset += n
n, err = refs.ObjectIDNestedListMarshal(passSGFNum, buf[offset:], a.passSG)
if err != nil {
return nil, err
}
offset += n
n, err = refs.ObjectIDNestedListMarshal(failSGFNum, buf[offset:], a.failSG)
if err != nil {
return nil, err
}
offset += n
n, err = proto.UInt32Marshal(hitFNum, buf[offset:], a.hit)
if err != nil {
return nil, err
}
offset += n
n, err = proto.UInt32Marshal(missFNum, buf[offset:], a.miss)
if err != nil {
return nil, err
}
offset += n
n, err = proto.UInt32Marshal(failFNum, buf[offset:], a.fail)
if err != nil {
return nil, err
}
offset += n
n, err = proto.RepeatedBytesMarshal(passNodesFNum, buf[offset:], a.passNodes)
if err != nil {
return nil, err
}
offset += n
n, err = proto.RepeatedBytesMarshal(failNodesFNum, buf[offset:], a.failNodes)
if err != nil {
return nil, err
}
return buf, nil
}
// StableSize returns byte length of DataAuditResult structure
// marshaled by StableMarshal function.
func (a *DataAuditResult) StableSize() (size int) {
if a == nil {
return 0
}
size += proto.Fixed64Size(auditEpochFNum, a.auditEpoch)
size += proto.NestedStructureSize(cidFNum, a.cid)
size += proto.BytesSize(pubKeyFNum, a.pubKey)
size += refs.ObjectIDNestedListSize(passSGFNum, a.passSG)
size += refs.ObjectIDNestedListSize(failSGFNum, a.failSG)
size += proto.UInt32Size(hitFNum, a.hit)
size += proto.UInt32Size(missFNum, a.miss)
size += proto.UInt32Size(failFNum, a.fail)
size += proto.RepeatedBytesSize(passNodesFNum, a.passNodes)
size += proto.RepeatedBytesSize(failNodesFNum, a.failNodes)
return size
}
// Unmarshal unmarshals DataAuditResult structure from its protobuf
// binary representation.
func (a *DataAuditResult) Unmarshal(data []byte) error {
m := new(audit.DataAuditResult)
if err := goproto.Unmarshal(data, m); err != nil {
return err
}
*a = *DataAuditResultFromGRPCMessage(m)
return nil
}

55
v2/audit/marshal_test.go Normal file
View file

@ -0,0 +1,55 @@
package audit_test
import (
"testing"
"github.com/nspcc-dev/neofs-api-go/v2/audit"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
"github.com/stretchr/testify/require"
)
func TestDataAuditResult_StableMarshal(t *testing.T) {
from := generateDataAuditResult()
t.Run("non empty", func(t *testing.T) {
wire, err := from.StableMarshal(nil)
require.NoError(t, err)
to := new(audit.DataAuditResult)
require.NoError(t, to.Unmarshal(wire))
require.Equal(t, from, to)
})
}
func generateDataAuditResult() *audit.DataAuditResult {
a := new(audit.DataAuditResult)
oid1 := new(refs.ObjectID)
oid1.SetValue([]byte("Object ID 1"))
oid2 := new(refs.ObjectID)
oid2.SetValue([]byte("Object ID 2"))
cid := new(refs.ContainerID)
cid.SetValue([]byte("Container ID"))
a.SetAuditEpoch(13)
a.SetContainerID(cid)
a.SetPublicKey([]byte("Public key"))
a.SetPassSG([]*refs.ObjectID{oid1, oid2})
a.SetFailSG([]*refs.ObjectID{oid2, oid1})
a.SetHit(1)
a.SetMiss(2)
a.SetFail(3)
a.SetPassNodes([][]byte{
{1, 2},
{3, 4},
})
a.SetFailNodes([][]byte{
{5, 6},
{7, 8},
})
return a
}

197
v2/audit/types.go Normal file
View file

@ -0,0 +1,197 @@
package audit
import (
"github.com/nspcc-dev/neofs-api-go/v2/refs"
)
// DataAuditResult is a unified structure of
// DataAuditResult message from proto definition.
type DataAuditResult struct {
auditEpoch uint64
hit, miss, fail uint32
cid *refs.ContainerID
pubKey []byte
passSG, failSG []*refs.ObjectID
failNodes, passNodes [][]byte
}
// GetAuditEpoch returns epoch number when the Data Audit was conducted.
func (a *DataAuditResult) GetAuditEpoch() uint64 {
if a != nil {
return a.auditEpoch
}
return 0
}
// SetAuditEpoch sets epoch number when the Data Audit was conducted.
func (a *DataAuditResult) SetAuditEpoch(v uint64) {
if a != nil {
a.auditEpoch = v
}
}
// GetContainerID returns container under audit.
func (a *DataAuditResult) GetContainerID() *refs.ContainerID {
if a != nil {
return a.cid
}
return nil
}
// SetContainerID sets container under audit.
func (a *DataAuditResult) SetContainerID(v *refs.ContainerID) {
if a != nil {
a.cid = v
}
}
// GetPublicKey returns public key of the auditing InnerRing node in a binary format.
func (a *DataAuditResult) GetPublicKey() []byte {
if a != nil {
return a.pubKey
}
return nil
}
// SetPublicKey sets public key of the auditing InnerRing node in a binary format.
func (a *DataAuditResult) SetPublicKey(v []byte) {
if a != nil {
a.pubKey = v
}
}
// GetPassSG returns list of Storage Groups that passed audit PoR stage.
func (a *DataAuditResult) GetPassSG() []*refs.ObjectID {
if a != nil {
return a.passSG
}
return nil
}
// SetPassSG sets list of Storage Groups that passed audit PoR stage.
func (a *DataAuditResult) SetPassSG(v []*refs.ObjectID) {
if a != nil {
a.passSG = v
}
}
// GetFailSG returns list of Storage Groups that failed audit PoR stage.
func (a *DataAuditResult) GetFailSG() []*refs.ObjectID {
if a != nil {
return a.failSG
}
return nil
}
// SetFailSG sets list of Storage Groups that failed audit PoR stage.
func (a *DataAuditResult) SetFailSG(v []*refs.ObjectID) {
if a != nil {
a.failSG = v
}
}
// GetHit returns number of sampled objects under audit placed
// in an optimal way according to the containers placement policy
// when checking PoP.
func (a *DataAuditResult) GetHit() uint32 {
if a != nil {
return a.hit
}
return 0
}
// SetHit sets number of sampled objects under audit placed
// in an optimal way according to the containers placement policy
// when checking PoP.
func (a *DataAuditResult) SetHit(v uint32) {
if a != nil {
a.hit = v
}
}
// GetMiss 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 (a *DataAuditResult) GetMiss() uint32 {
if a != nil {
return a.miss
}
return 0
}
// 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 (a *DataAuditResult) SetMiss(v uint32) {
if a != nil {
a.miss = v
}
}
// GetFail returns number of sampled objects under audit stored
// in a way not confirming placement policy or not found at all
// when checking PoP.
func (a *DataAuditResult) GetFail() uint32 {
if a != nil {
return a.fail
}
return 0
}
// 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 (a *DataAuditResult) SetFail(v uint32) {
if a != nil {
a.fail = v
}
}
// GetPassNodes returns list of storage node public keys that
// passed at least one PDP.
func (a *DataAuditResult) GetPassNodes() [][]byte {
if a != nil {
return a.passNodes
}
return nil
}
// SetPassNodes sets list of storage node public keys that
// passed at least one PDP.
func (a *DataAuditResult) SetPassNodes(v [][]byte) {
if a != nil {
a.passNodes = v
}
}
// GetFailNodes returns list of storage node public keys that
// failed at least one PDP.
func (a *DataAuditResult) GetFailNodes() [][]byte {
if a != nil {
return a.failNodes
}
return nil
}
// SetFailNodes sets list of storage node public keys that
// failed at least one PDP.
func (a *DataAuditResult) SetFailNodes(v [][]byte) {
if a != nil {
a.failNodes = v
}
}