diff --git a/accounting/decimal.go b/accounting/decimal.go index 9cea6618f..a4e560df3 100644 --- a/accounting/decimal.go +++ b/accounting/decimal.go @@ -1,36 +1,41 @@ package accounting import ( - "github.com/nspcc-dev/neofs-api-go/v2/accounting" + neofsproto "github.com/nspcc-dev/neofs-sdk-go/internal/proto" + protoaccounting "github.com/nspcc-dev/neofs-sdk-go/proto/accounting" + "google.golang.org/protobuf/proto" ) // Decimal represents decimal number for accounting operations. // -// Decimal is mutually compatible with github.com/nspcc-dev/neofs-api-go/v2/accounting.Decimal -// message. See ReadFromV2 / WriteToV2 methods. +// Decimal is mutually compatible with [protoaccounting.Decimal] message. See +// [Decimal.FromProtoMessage] / [Decimal.FromProtoMessage] methods. // // Instances can be created using built-in var declaration. -// -// Note that direct typecast is not safe and may result in loss of compatibility: -// -// _ = Decimal(accounting.Decimal{}) // not recommended -type Decimal accounting.Decimal +type Decimal struct { + val int64 + prec uint32 +} -// ReadFromV2 reads Decimal from the accounting.Decimal message. Checks if the -// message conforms to NeoFS API V2 protocol. +// FromProtoMessage validates m according to the NeoFS API protocol and restores +// d from it. // -// See also WriteToV2. -func (d *Decimal) ReadFromV2(m accounting.Decimal) error { - *d = Decimal(m) +// See also [Decimal.ProtoMessage]. +func (d *Decimal) FromProtoMessage(m *protoaccounting.Decimal) error { + d.val = m.Value + d.prec = m.Precision return nil } -// WriteToV2 writes Decimal to the accounting.Decimal message. -// The message must not be nil. +// ProtoMessage converts d into message to transmit using the NeoFS API +// protocol. // -// See also ReadFromV2. -func (d Decimal) WriteToV2(m *accounting.Decimal) { - *m = (accounting.Decimal)(d) +// See also [Decimal.FromProtoMessage]. +func (d Decimal) ProtoMessage() *protoaccounting.Decimal { + return &protoaccounting.Decimal{ + Value: d.val, + Precision: d.prec, + } } // Value returns value of the decimal number. @@ -39,14 +44,14 @@ func (d Decimal) WriteToV2(m *accounting.Decimal) { // // See also SetValue. func (d Decimal) Value() int64 { - return (*accounting.Decimal)(&d).GetValue() + return d.val } // SetValue sets value of the decimal number. // // See also Value. func (d *Decimal) SetValue(v int64) { - (*accounting.Decimal)(d).SetValue(v) + d.val = v } // Precision returns precision of the decimal number. @@ -55,14 +60,14 @@ func (d *Decimal) SetValue(v int64) { // // See also SetPrecision. func (d Decimal) Precision() uint32 { - return (*accounting.Decimal)(&d).GetPrecision() + return d.prec } // SetPrecision sets precision of the decimal number. // // See also Precision. func (d *Decimal) SetPrecision(p uint32) { - (*accounting.Decimal)(d).SetPrecision(p) + d.prec = p } // Marshal encodes Decimal into a binary format of the NeoFS API protocol @@ -70,10 +75,7 @@ func (d *Decimal) SetPrecision(p uint32) { // // See also Unmarshal. func (d Decimal) Marshal() []byte { - var m accounting.Decimal - d.WriteToV2(&m) - - return m.StableMarshal(nil) + return neofsproto.MarshalMessage(d.ProtoMessage()) } // Unmarshal decodes NeoFS API protocol binary format into the Decimal @@ -82,12 +84,12 @@ func (d Decimal) Marshal() []byte { // // See also Marshal. func (d *Decimal) Unmarshal(data []byte) error { - var m accounting.Decimal + var m protoaccounting.Decimal - err := m.Unmarshal(data) + err := proto.Unmarshal(data, &m) if err != nil { return err } - return d.ReadFromV2(m) + return d.FromProtoMessage(&m) } diff --git a/accounting/decimal_test.go b/accounting/decimal_test.go index 47c0eac69..4b09f6a5e 100644 --- a/accounting/decimal_test.go +++ b/accounting/decimal_test.go @@ -4,8 +4,8 @@ import ( "math/rand/v2" "testing" - apiaccounting "github.com/nspcc-dev/neofs-api-go/v2/accounting" "github.com/nspcc-dev/neofs-sdk-go/accounting" + protoaccounting "github.com/nspcc-dev/neofs-sdk-go/proto/accounting" "github.com/stretchr/testify/require" ) @@ -48,31 +48,30 @@ func TestDecimal_SetPrecision(t *testing.T) { testDecimalField(t, accounting.Decimal.Precision, (*accounting.Decimal).SetPrecision) } -func TestDecimal_ReadFromV2(t *testing.T) { - var m apiaccounting.Decimal - m.SetValue(anyValidValue) - m.SetPrecision(anyValidPrecision) +func TestDecimal_FromProtoMessage(t *testing.T) { + var m protoaccounting.Decimal + m.Value = anyValidValue + m.Precision = anyValidPrecision var val accounting.Decimal - require.NoError(t, val.ReadFromV2(m)) + require.NoError(t, val.FromProtoMessage(&m)) require.EqualValues(t, anyValidValue, val.Value()) require.EqualValues(t, anyValidPrecision, val.Precision()) } -func TestDecimal_WriteToV2(t *testing.T) { +func TestDecimal_ProtoMessage(t *testing.T) { var val accounting.Decimal - var m apiaccounting.Decimal // zero - val.WriteToV2(&m) - require.Zero(t, m.GetValue()) + m := val.ProtoMessage() require.Zero(t, m.GetValue()) + require.Zero(t, m.GetPrecision()) // filled val.SetValue(anyValidValue) val.SetPrecision(anyValidPrecision) - val.WriteToV2(&m) + m = val.ProtoMessage() require.EqualValues(t, anyValidValue, val.Value()) require.EqualValues(t, anyValidPrecision, val.Precision()) } diff --git a/accounting/test/decimal_test.go b/accounting/test/decimal_test.go index 8801f37a6..a8f3dcdaa 100644 --- a/accounting/test/decimal_test.go +++ b/accounting/test/decimal_test.go @@ -3,7 +3,6 @@ package accountingtest_test import ( "testing" - apiaccounting "github.com/nspcc-dev/neofs-api-go/v2/accounting" "github.com/nspcc-dev/neofs-sdk-go/accounting" accountingtest "github.com/nspcc-dev/neofs-sdk-go/accounting/test" "github.com/stretchr/testify/require" @@ -13,10 +12,9 @@ func TestDecimal(t *testing.T) { d := accountingtest.Decimal() require.NotEqual(t, d, accountingtest.Decimal()) - var m apiaccounting.Decimal - d.WriteToV2(&m) + m := d.ProtoMessage() var d2 accounting.Decimal - require.NoError(t, d2.ReadFromV2(m)) + require.NoError(t, d2.FromProtoMessage(m)) require.Equal(t, d, d2) require.NoError(t, new(accounting.Decimal).Unmarshal(d.Marshal())) diff --git a/checksum/checksum.go b/checksum/checksum.go index cbc32ce2e..d6876b575 100644 --- a/checksum/checksum.go +++ b/checksum/checksum.go @@ -7,25 +7,24 @@ import ( "fmt" "hash" - "github.com/nspcc-dev/neofs-api-go/v2/refs" + "github.com/nspcc-dev/neofs-sdk-go/proto/refs" "github.com/nspcc-dev/tzhash/tz" ) // Checksum represents checksum of some digital data. // -// Checksum is mutually compatible with github.com/nspcc-dev/neofs-api-go/v2/refs.Checksum -// message. See ReadFromV2 / WriteToV2 methods. +// Checksum is mutually compatible with [refs.Checksum] message. See +// [Checksum.FromProtoMessage] / [Checksum.ProtoMessage] methods. // // Instances must be created using one of the constructors. -// -// Note that direct typecast is not safe and may result in loss of compatibility: -// -// _ = Checksum(refs.Checksum{}) // not recommended -type Checksum refs.Checksum +type Checksum struct { + typ Type + val []byte +} // Type represents the enumeration // of checksum types. -type Type uint32 +type Type int32 const ( Unknown Type = iota // Deprecated: use 0 instead. @@ -42,19 +41,27 @@ func typeToProto(t Type) refs.ChecksumType { default: return refs.ChecksumType(t) case SHA256: - return refs.SHA256 + return refs.ChecksumType_SHA256 case TillichZemor: - return refs.TillichZemor + return refs.ChecksumType_TZ + } +} + +func typeFromProto(t refs.ChecksumType) Type { + switch t { + default: + return Type(t) + case refs.ChecksumType_SHA256: + return SHA256 + case refs.ChecksumType_TZ: + return TillichZemor } } // New constructs new Checksum instance. It is the caller's responsibility to // ensure that the hash matches the type. func New(typ Type, hsh []byte) Checksum { - var res refs.Checksum - res.SetType(typeToProto(typ)) - res.SetSum(hsh) - return Checksum(res) + return Checksum{typ: typ, val: hsh} } // NewSHA256 constructs new Checksum from SHA-256 hash. @@ -87,26 +94,33 @@ func NewFromData(typ Type, data []byte) (Checksum, error) { } } -// ReadFromV2 reads Checksum from the refs.Checksum message. Checks if the -// message conforms to NeoFS API V2 protocol. +// FromProtoMessage validates m according to the NeoFS API protocol and restores +// c from it. // -// See also WriteToV2. -func (c *Checksum) ReadFromV2(m refs.Checksum) error { - if len(m.GetSum()) == 0 { +// See also [Checksum.ProtoMessage]. +func (c *Checksum) FromProtoMessage(m *refs.Checksum) error { + if m.Type < 0 { + return fmt.Errorf("negative type %d", m.Type) + } + if len(m.Sum) == 0 { return errors.New("missing value") } - *c = Checksum(m) + c.typ = typeFromProto(m.Type) + c.val = m.Sum return nil } -// WriteToV2 writes Checksum to the refs.Checksum message. -// The message must not be nil. +// ProtoMessage converts c into message to transmit using the NeoFS API +// protocol. // -// See also ReadFromV2. -func (c Checksum) WriteToV2(m *refs.Checksum) { - *m = (refs.Checksum)(c) +// See also [Checksum.FromProtoMessage]. +func (c Checksum) ProtoMessage() *refs.Checksum { + return &refs.Checksum{ + Type: typeToProto(c.typ), + Sum: c.val, + } } // Type returns checksum type. @@ -115,15 +129,7 @@ func (c Checksum) WriteToV2(m *refs.Checksum) { // // See also [NewTillichZemor], [NewSHA256]. func (c Checksum) Type() Type { - v2 := (refs.Checksum)(c) - switch typ := v2.GetType(); typ { - case refs.SHA256: - return SHA256 - case refs.TillichZemor: - return TillichZemor - default: - return Type(typ) - } + return c.typ } // Value returns checksum bytes. Return value @@ -136,8 +142,7 @@ func (c Checksum) Type() Type { // // See also [NewTillichZemor], [NewSHA256]. func (c Checksum) Value() []byte { - v2 := (refs.Checksum)(c) - return v2.GetSum() + return c.val } // SetSHA256 sets checksum to SHA256 hash. @@ -174,8 +179,7 @@ func (c *Checksum) SetTillichZemor(v [tz.Size]byte) { *c = NewTillichZemor(v) } // String is designed to be human-readable, and its format MAY differ between // SDK versions. func (c Checksum) String() string { - v2 := (refs.Checksum)(c) - return fmt.Sprintf("%s:%s", c.Type(), hex.EncodeToString(v2.GetSum())) + return fmt.Sprintf("%s:%s", c.Type(), hex.EncodeToString(c.Value())) } // String implements fmt.Stringer. diff --git a/checksum/checksum_test.go b/checksum/checksum_test.go index 830952cca..4d7d78d4a 100644 --- a/checksum/checksum_test.go +++ b/checksum/checksum_test.go @@ -7,8 +7,8 @@ import ( "math/rand" "testing" - "github.com/nspcc-dev/neofs-api-go/v2/refs" "github.com/nspcc-dev/neofs-sdk-go/checksum" + "github.com/nspcc-dev/neofs-sdk-go/proto/refs" "github.com/nspcc-dev/tzhash/tz" "github.com/stretchr/testify/require" ) @@ -48,23 +48,22 @@ func TestChecksumDecodingFailures(t *testing.T) { name, err string corrupt func(*refs.Checksum) }{ - {name: "value/nil", err: "missing value", corrupt: func(cs *refs.Checksum) { cs.SetSum(nil) }}, - {name: "value/empty", err: "missing value", corrupt: func(cs *refs.Checksum) { cs.SetSum([]byte{}) }}, + {name: "value/nil", err: "missing value", corrupt: func(cs *refs.Checksum) { cs.Sum = nil }}, + {name: "value/empty", err: "missing value", corrupt: func(cs *refs.Checksum) { cs.Sum = []byte{} }}, } { t.Run(tc.name, func(t *testing.T) { var src, dst checksum.Checksum - var m refs.Checksum - src.WriteToV2(&m) - tc.corrupt(&m) - require.ErrorContains(t, dst.ReadFromV2(m), tc.err) + m := src.ProtoMessage() + tc.corrupt(m) + require.ErrorContains(t, dst.FromProtoMessage(m), tc.err) }) } }) } func TestNew(t *testing.T) { - typ := checksum.Type(rand.Uint32()) + typ := checksum.Type(rand.Int31()) val := make([]byte, 128) //nolint:staticcheck rand.Read(val) @@ -72,7 +71,7 @@ func TestNew(t *testing.T) { require.Equal(t, typ, cs.Type()) require.Equal(t, val, cs.Value()) - otherTyp := checksum.Type(rand.Uint32()) + otherTyp := checksum.Type(rand.Int31()) otherVal := make([]byte, 128) //nolint:staticcheck rand.Read(otherVal) @@ -84,19 +83,18 @@ func TestNew(t *testing.T) { t.Run("api", func(t *testing.T) { src := checksum.New(typ, val) var dst checksum.Checksum - var m refs.Checksum - src.WriteToV2(&m) + m := src.ProtoMessage() switch actual := m.GetType(); typ { default: require.EqualValues(t, typ, actual) case checksum.TillichZemor: - require.Equal(t, refs.TillichZemor, actual) + require.Equal(t, refs.ChecksumType_TZ, actual) case checksum.SHA256: - require.Equal(t, refs.SHA256, actual) + require.Equal(t, refs.ChecksumType_SHA256, actual) } require.Equal(t, val, m.GetSum()) - require.NoError(t, dst.ReadFromV2(m)) + require.NoError(t, dst.FromProtoMessage(m)) require.Equal(t, typ, dst.Type()) require.Equal(t, val, dst.Value()) }) @@ -131,13 +129,12 @@ func testTypeConstructor[T [sha256.Size]byte | [tz.Size]byte]( t.Run("api", func(t *testing.T) { src := cons(val) var dst checksum.Checksum - var m refs.Checksum - src.WriteToV2(&m) + m := src.ProtoMessage() require.Equal(t, typAPI, m.GetType()) require.Len(t, m.GetSum(), len(val)) require.Equal(t, val, T(m.GetSum())) - require.NoError(t, dst.ReadFromV2(m)) + require.NoError(t, dst.FromProtoMessage(m)) require.Equal(t, typ, dst.Type()) require.Len(t, dst.Value(), len(val)) require.Equal(t, val, T(dst.Value())) @@ -146,11 +143,11 @@ func testTypeConstructor[T [sha256.Size]byte | [tz.Size]byte]( } func TestNewSHA256(t *testing.T) { - testTypeConstructor(t, checksum.SHA256, refs.SHA256, checksum.NewSHA256) + testTypeConstructor(t, checksum.SHA256, refs.ChecksumType_SHA256, checksum.NewSHA256) } func TestNewTZ(t *testing.T) { - testTypeConstructor(t, checksum.TillichZemor, refs.TillichZemor, checksum.NewTillichZemor) + testTypeConstructor(t, checksum.TillichZemor, refs.ChecksumType_TZ, checksum.NewTillichZemor) } func TestNewFromHash(t *testing.T) { @@ -158,7 +155,7 @@ func TestNewFromHash(t *testing.T) { h.Write([]byte("Hello, world!")) hb := []byte{32, 94, 4, 138} - typ := checksum.Type(rand.Uint32()) + typ := checksum.Type(rand.Int31()) cs := checksum.NewFromHash(typ, h) require.Equal(t, typ, cs.Type()) require.Equal(t, hb, cs.Value()) @@ -167,19 +164,18 @@ func TestNewFromHash(t *testing.T) { t.Run("api", func(t *testing.T) { src := checksum.NewFromHash(typ, h) var dst checksum.Checksum - var m refs.Checksum - src.WriteToV2(&m) + m := src.ProtoMessage() switch actual := m.GetType(); typ { default: require.EqualValues(t, typ, actual) case checksum.TillichZemor: - require.Equal(t, refs.TillichZemor, actual) + require.Equal(t, refs.ChecksumType_TZ, actual) case checksum.SHA256: - require.Equal(t, refs.SHA256, actual) + require.Equal(t, refs.ChecksumType_SHA256, actual) } require.Equal(t, hb, m.GetSum()) - require.NoError(t, dst.ReadFromV2(m)) + require.NoError(t, dst.FromProtoMessage(m)) require.Equal(t, typ, dst.Type()) require.Equal(t, hb, dst.Value()) }) @@ -228,9 +224,9 @@ func TestNewFromData(t *testing.T) { } func TestChecksum_SetSHA256(t *testing.T) { - testTypeConstructor(t, checksum.SHA256, refs.SHA256, func(b [sha256.Size]byte) (c checksum.Checksum) { c.SetSHA256(b); return }) + testTypeConstructor(t, checksum.SHA256, refs.ChecksumType_SHA256, func(b [sha256.Size]byte) (c checksum.Checksum) { c.SetSHA256(b); return }) } func TestChecksum_SetTillichZemor(t *testing.T) { - testTypeConstructor(t, checksum.TillichZemor, refs.TillichZemor, func(b [tz.Size]byte) (c checksum.Checksum) { c.SetTillichZemor(b); return }) + testTypeConstructor(t, checksum.TillichZemor, refs.ChecksumType_TZ, func(b [tz.Size]byte) (c checksum.Checksum) { c.SetTillichZemor(b); return }) } diff --git a/checksum/test/generate_test.go b/checksum/test/generate_test.go index de6dc2150..16fb9d52c 100644 --- a/checksum/test/generate_test.go +++ b/checksum/test/generate_test.go @@ -3,7 +3,6 @@ package checksumtest_test import ( "testing" - "github.com/nspcc-dev/neofs-api-go/v2/refs" "github.com/nspcc-dev/neofs-sdk-go/checksum" checksumtest "github.com/nspcc-dev/neofs-sdk-go/checksum/test" "github.com/stretchr/testify/require" @@ -13,9 +12,8 @@ func TestChecksum(t *testing.T) { cs := checksumtest.Checksum() require.NotEqual(t, cs, checksumtest.Checksum()) - var m refs.Checksum - cs.WriteToV2(&m) + m := cs.ProtoMessage() var cs2 checksum.Checksum - require.NoError(t, cs2.ReadFromV2(m)) + require.NoError(t, cs2.FromProtoMessage(m)) require.Equal(t, cs, cs2) } diff --git a/container/id/id.go b/container/id/id.go index 963dd520d..70ff4a1cd 100644 --- a/container/id/id.go +++ b/container/id/id.go @@ -6,7 +6,7 @@ import ( "fmt" "github.com/mr-tron/base58" - "github.com/nspcc-dev/neofs-api-go/v2/refs" + "github.com/nspcc-dev/neofs-sdk-go/proto/refs" ) // Size is the size of an [ID] in bytes. @@ -17,8 +17,8 @@ const Size = sha256.Size // // ID implements built-in comparable interface. // -// ID is mutually compatible with github.com/nspcc-dev/neofs-api-go/v2/refs.ContainerID -// message. See ReadFromV2 / WriteToV2 methods. +// ID is mutually compatible with [refs.ContainerID] message. See +// [ID.FromProtoMessage] / [ID.ProtoMessage] methods. type ID [Size]byte // ErrZero is an error returned on zero [ID] encounter. @@ -42,25 +42,24 @@ func DecodeString(s string) (ID, error) { return id, id.DecodeString(s) } -// ReadFromV2 reads ID from the refs.ContainerID message. -// Returns an error if the message is malformed according -// to the NeoFS API V2 protocol. +// FromProtoMessage validates m according to the NeoFS API protocol and restores +// id from it. // -// See also WriteToV2. -func (id *ID) ReadFromV2(m refs.ContainerID) error { - err := id.Decode(m.GetValue()) +// See also [ID.ProtoMessage]. +func (id *ID) FromProtoMessage(m *refs.ContainerID) error { + err := id.Decode(m.Value) if err == nil && id.IsZero() { err = ErrZero } return err } -// WriteToV2 writes ID to the refs.ContainerID message. -// The message must not be nil. +// ProtoMessage converts id into message to transmit using the NeoFS API +// protocol. // -// See also ReadFromV2. -func (id ID) WriteToV2(m *refs.ContainerID) { - m.SetValue(id[:]) +// See also [ID.FromProtoMessage]. +func (id ID) ProtoMessage() *refs.ContainerID { + return &refs.ContainerID{Value: id[:]} } // Encode encodes ID into [Size] bytes of dst. Panics if diff --git a/container/id/id_test.go b/container/id/id_test.go index 67c01a47c..218eb17d6 100644 --- a/container/id/id_test.go +++ b/container/id/id_test.go @@ -6,9 +6,9 @@ import ( "math/rand" "testing" - "github.com/nspcc-dev/neofs-api-go/v2/refs" cid "github.com/nspcc-dev/neofs-sdk-go/container/id" cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test" + "github.com/nspcc-dev/neofs-sdk-go/proto/refs" "github.com/stretchr/testify/require" ) @@ -31,11 +31,10 @@ var invalidValueTestcases = []invalidValueTestCase{ {name: "oversized value", err: "invalid length 33", val: make([]byte, 33)}, } -func TestID_ReadFromV2(t *testing.T) { - var m refs.ContainerID - m.SetValue(validBytes[:]) +func TestID_FromProtoMessage(t *testing.T) { + m := &refs.ContainerID{Value: validBytes[:]} var id cid.ID - require.NoError(t, id.ReadFromV2(m)) + require.NoError(t, id.FromProtoMessage(m)) require.EqualValues(t, validBytes, id) t.Run("invalid", func(t *testing.T) { @@ -43,9 +42,8 @@ func TestID_ReadFromV2(t *testing.T) { name: "zero value", err: "zero container ID", val: make([]byte, cid.Size), }) { t.Run(tc.name, func(t *testing.T) { - var m refs.ContainerID - m.SetValue(tc.val) - require.EqualError(t, new(cid.ID).ReadFromV2(m), tc.err) + m := &refs.ContainerID{Value: tc.val} + require.EqualError(t, new(cid.ID).FromProtoMessage(m), tc.err) }) } }) @@ -102,10 +100,9 @@ func TestID_DecodeString(t *testing.T) { }) } -func TestID_WriteToV2(t *testing.T) { +func TestID_ProtoMessage(t *testing.T) { id := cidtest.ID() - var m refs.ContainerID - id.WriteToV2(&m) + m := id.ProtoMessage() require.Equal(t, id[:], m.GetValue()) } diff --git a/container/id/test/id_test.go b/container/id/test/id_test.go index 4ec59669c..7a4bc51f9 100644 --- a/container/id/test/id_test.go +++ b/container/id/test/id_test.go @@ -4,7 +4,6 @@ import ( "math/rand/v2" "testing" - "github.com/nspcc-dev/neofs-api-go/v2/refs" cid "github.com/nspcc-dev/neofs-sdk-go/container/id" cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test" "github.com/stretchr/testify/require" @@ -14,10 +13,9 @@ func TestID(t *testing.T) { id := cidtest.ID() require.NotEqual(t, id, cidtest.ID()) - var m refs.ContainerID - id.WriteToV2(&m) + m := id.ProtoMessage() var id2 cid.ID - require.NoError(t, id2.ReadFromV2(m)) + require.NoError(t, id2.FromProtoMessage(m)) } func TestNIDs(t *testing.T) { diff --git a/crypto/crypto_test.go b/crypto/crypto_test.go index 4549cc55e..8b9dcd2f6 100644 --- a/crypto/crypto_test.go +++ b/crypto/crypto_test.go @@ -5,7 +5,6 @@ import ( "testing" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" - "github.com/nspcc-dev/neofs-api-go/v2/refs" neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto" neofsecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa" "github.com/stretchr/testify/require" @@ -20,7 +19,6 @@ func TestSignature(t *testing.T) { require.NoError(t, err) var s neofscrypto.Signature - var m refs.Signature for _, f := range []func() neofscrypto.Signer{ func() neofscrypto.Signer { @@ -38,9 +36,9 @@ func TestSignature(t *testing.T) { err := s.Calculate(signer, data) require.NoError(t, err) - s.WriteToV2(&m) + m := s.ProtoMessage() - require.NoError(t, s.ReadFromV2(m)) + require.NoError(t, s.FromProtoMessage(m)) valid := s.Verify(data) require.True(t, valid, "type %T", signer) diff --git a/crypto/ecdsa/wallet_connect.go b/crypto/ecdsa/wallet_connect.go index 5c46f69ad..2eb739fa2 100644 --- a/crypto/ecdsa/wallet_connect.go +++ b/crypto/ecdsa/wallet_connect.go @@ -3,14 +3,20 @@ package neofsecdsa import ( "crypto/ecdsa" "crypto/elliptic" + "crypto/rand" + "crypto/sha256" "encoding/base64" + "encoding/hex" "fmt" + "math/big" "github.com/nspcc-dev/neo-go/pkg/crypto/keys" - "github.com/nspcc-dev/neofs-api-go/v2/util/signature/walletconnect" + "github.com/nspcc-dev/neo-go/pkg/io" neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto" ) +const saltLen = 16 + // SignerWalletConnect is similar to SignerRFC6979 with 2 changes: // 1. The data is base64 encoded before signing/verifying. // 2. The signature is a concatenation of the signature itself and 16-byte salt. @@ -29,7 +35,16 @@ func (x SignerWalletConnect) Scheme() neofscrypto.Scheme { func (x SignerWalletConnect) Sign(data []byte) ([]byte, error) { b64 := make([]byte, base64.StdEncoding.EncodedLen(len(data))) base64.StdEncoding.Encode(b64, data) - return walletconnect.Sign((*ecdsa.PrivateKey)(&x), b64) + var salt [saltLen]byte + _, err := rand.Read(salt[:]) + if err != nil { + return nil, fmt.Errorf("randomize salt: %w", err) + } + sig, err := SignerRFC6979(x).Sign(saltMessageWalletConnect(b64, salt[:])) + if err != nil { + return nil, err + } + return append(sig, salt[:]...), nil } // Public initializes PublicKey and returns it as neofscrypto.PublicKey. @@ -79,7 +94,27 @@ func (x *PublicKeyWalletConnect) Decode(data []byte) error { // Verify verifies data signature calculated by ECDSA algorithm with SHA-512 hashing. func (x PublicKeyWalletConnect) Verify(data, signature []byte) bool { + if len(signature) != keys.SignatureLen+saltLen { + return false + } b64 := make([]byte, base64.StdEncoding.EncodedLen(len(data))) base64.StdEncoding.Encode(b64, data) - return walletconnect.Verify((*ecdsa.PublicKey)(&x), b64, signature) + sig, salt := signature[:keys.SignatureLen], signature[keys.SignatureLen:] + h := sha256.Sum256(saltMessageWalletConnect(b64, salt)) + r := new(big.Int).SetBytes(sig[:keys.SignatureLen/2]) + s := new(big.Int).SetBytes(sig[keys.SignatureLen/2:]) + return ecdsa.Verify((*ecdsa.PublicKey)(&x), h[:], r, s) +} + +// saltMessageWalletConnect calculates signed message for given data and salt +// according to WalletConnect. +func saltMessageWalletConnect(data, salt []byte) []byte { + saltedLen := hex.EncodedLen(len(salt)) + len(data) + b := make([]byte, 4+io.GetVarSize(saltedLen)+saltedLen+2) + b[0], b[1], b[2], b[3] = 0x01, 0x00, 0x01, 0xf0 + n := 4 + io.PutVarUint(b[4:], uint64(saltedLen)) + n += hex.Encode(b[n:], salt) + n += copy(b[n:], data) + b[n], b[n+1] = 0x00, 0x00 + return b } diff --git a/crypto/signature.go b/crypto/signature.go index b19ae0830..e50ff534a 100644 --- a/crypto/signature.go +++ b/crypto/signature.go @@ -2,9 +2,8 @@ package neofscrypto import ( "fmt" - "math" - "github.com/nspcc-dev/neofs-api-go/v2/refs" + "github.com/nspcc-dev/neofs-sdk-go/proto/refs" ) // StablyMarshallable describes structs which can be marshalled transparently. @@ -16,8 +15,8 @@ type StablyMarshallable interface { // Signature represents a confirmation of data integrity received by the // digital signature mechanism. // -// Signature is mutually compatible with github.com/nspcc-dev/neofs-api-go/v2/refs.Signature -// message. See ReadFromV2 / WriteToV2 methods. +// Signature is mutually compatible with [refs.Signature] message. See +// [Signature.FromProtoMessage] / [Signature.ProtoMessage] methods. // // Instances should be constructed using one of the constructors. type Signature struct { @@ -36,29 +35,30 @@ func NewSignature(scheme Scheme, publicKey PublicKey, value []byte) Signature { return NewSignatureFromRawKey(scheme, PublicKeyBytes(publicKey), value) } -// ReadFromV2 reads Signature from the refs.Signature message. Checks if the -// message conforms to NeoFS API V2 protocol. +// FromProtoMessage validates m according to the NeoFS API protocol and restores +// x from it. // -// See also WriteToV2. -func (x *Signature) ReadFromV2(m refs.Signature) error { - scheme := m.GetScheme() - if scheme > math.MaxInt32 { // max value of Scheme type - return fmt.Errorf("scheme %d overflows int32", scheme) +// See also [Signature.ProtoMessage]. +func (x *Signature) FromProtoMessage(m *refs.Signature) error { + if m.Scheme < 0 { + return fmt.Errorf("negative scheme %d", m.Scheme) } - x.scheme = Scheme(scheme) - x.pub = m.GetKey() - x.val = m.GetSign() + x.scheme = Scheme(m.Scheme) + x.pub = m.Key + x.val = m.Sign return nil } -// WriteToV2 writes Signature to the refs.Signature message. -// The message must not be nil. +// ProtoMessage converts x into message to transmit using the NeoFS API +// protocol. // -// See also ReadFromV2. -func (x Signature) WriteToV2(m *refs.Signature) { - m.SetScheme(refs.SignatureScheme(x.scheme)) - m.SetKey(x.pub) - m.SetSign(x.val) +// See also [Signature.FromProtoMessage]. +func (x Signature) ProtoMessage() *refs.Signature { + return &refs.Signature{ + Key: x.pub, + Sign: x.val, + Scheme: refs.SignatureScheme(x.scheme), + } } // Calculate signs data using Signer and encodes public key for subsequent @@ -119,8 +119,8 @@ func (x Signature) Verify(data []byte) bool { // Scheme returns signature scheme used by signer to calculate the signature. // -// Scheme MUST NOT be called before [NewSignature], [Signature.ReadFromV2] or -// [Signature.Calculate] methods. +// Scheme MUST NOT be called before [NewSignature], [Signature.FromProtoMessage] +// or [Signature.Calculate] methods. func (x Signature) Scheme() Scheme { return x.scheme } @@ -132,8 +132,8 @@ func (x *Signature) SetScheme(s Scheme) { // PublicKey returns public key of the signer which calculated the signature. // -// PublicKey MUST NOT be called before [NewSignature], [Signature.ReadFromV2] or -// [Signature.Calculate] methods. +// PublicKey MUST NOT be called before [NewSignature], +// [Signature.FromProtoMessage] or [Signature.Calculate] methods. // // See also [Signature.PublicKeyBytes]. func (x Signature) PublicKey() PublicKey { @@ -151,7 +151,7 @@ func (x *Signature) SetPublicKeyBytes(pub []byte) { // calculated the signature. // // PublicKeyBytes MUST NOT be called before [NewSignature], -// [Signature.ReadFromV2] or [Signature.Calculate] methods. +// [Signature.FromProtoMessage] or [Signature.Calculate] methods. // // The value returned shares memory with the structure itself, so changing it can lead to data corruption. // Make a copy if you need to change it. @@ -171,8 +171,8 @@ func (x *Signature) SetValue(v []byte) { // The value returned shares memory with the structure itself, so changing it can lead to data corruption. // Make a copy if you need to change it. // -// Value MUST NOT be called before [NewSignature], [Signature.ReadFromV2] or -// [Signature.Calculate] methods. +// Value MUST NOT be called before [NewSignature], [Signature.FromProtoMessage] +// or [Signature.Calculate] methods. func (x Signature) Value() []byte { return x.val } diff --git a/crypto/signature_test.go b/crypto/signature_test.go index 78c8e4617..08baecbac 100644 --- a/crypto/signature_test.go +++ b/crypto/signature_test.go @@ -1,16 +1,16 @@ package neofscrypto_test import ( - "math" "testing" - "github.com/nspcc-dev/neofs-api-go/v2/refs" neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto" neofscryptotest "github.com/nspcc-dev/neofs-sdk-go/crypto/test" + "github.com/nspcc-dev/neofs-sdk-go/proto/refs" "github.com/stretchr/testify/require" + "google.golang.org/protobuf/proto" ) -const anyUnsupportedScheme = math.MaxInt32 + 1 +const anyUnsupportedScheme = -1 func TestSignatureLifecycle(t *testing.T) { data := []byte("Hello, world!") @@ -34,30 +34,29 @@ func TestSignatureLifecycle(t *testing.T) { testSig(clientSig) - var sigV2 refs.Signature - clientSig.WriteToV2(&sigV2) + m := clientSig.ProtoMessage() - require.Equal(t, refs.SignatureScheme(scheme), sigV2.GetScheme()) - require.Equal(t, bPubKey, sigV2.GetKey()) - require.Equal(t, clientSig.Value(), sigV2.GetSign()) + require.Equal(t, refs.SignatureScheme(scheme), m.GetScheme()) + require.Equal(t, bPubKey, m.GetKey()) + require.Equal(t, clientSig.Value(), m.GetSign()) - // sigV2 transmitted to server over the network + // m transmitted to server over the network var serverSig neofscrypto.Signature - err = serverSig.ReadFromV2(sigV2) + err = serverSig.FromProtoMessage(m) require.NoError(t, err) testSig(serverSig) // break the message in different ways for i, breakSig := range []func(*refs.Signature){ - func(sigV2 *refs.Signature) { sigV2.SetScheme(refs.SignatureScheme(anyUnsupportedScheme)) }, + func(sigV2 *refs.Signature) { sigV2.Scheme = refs.SignatureScheme(anyUnsupportedScheme) }, } { - sigV2Cp := sigV2 - breakSig(&sigV2Cp) + m := proto.Clone(m).(*refs.Signature) + breakSig(m) - err = serverSig.ReadFromV2(sigV2Cp) + err = serverSig.FromProtoMessage(m) require.Errorf(t, err, "break func #%d", i) } } @@ -79,12 +78,11 @@ func TestNewSignature(t *testing.T) { checkFields(sig) - var sigMsg refs.Signature - sig.WriteToV2(&sigMsg) + m := sig.ProtoMessage() var sig2 neofscrypto.Signature - err := sig2.ReadFromV2(sigMsg) + err := sig2.FromProtoMessage(m) require.NoError(t, err) checkFields(sig2) diff --git a/crypto/signer.go b/crypto/signer.go index 2d671c677..aeb0c62f7 100644 --- a/crypto/signer.go +++ b/crypto/signer.go @@ -4,7 +4,7 @@ import ( "errors" "fmt" - "github.com/nspcc-dev/neofs-api-go/v2/refs" + "github.com/nspcc-dev/neofs-sdk-go/proto/refs" ) // ErrIncorrectSigner is returned from function when the signer passed to it diff --git a/crypto/test/tests_test.go b/crypto/test/tests_test.go index 04bcacdb6..f2be9e438 100644 --- a/crypto/test/tests_test.go +++ b/crypto/test/tests_test.go @@ -3,7 +3,6 @@ package neofscryptotest_test import ( "testing" - "github.com/nspcc-dev/neofs-api-go/v2/refs" neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto" neofsecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa" neofscryptotest "github.com/nspcc-dev/neofs-sdk-go/crypto/test" @@ -14,10 +13,9 @@ func TestSignature(t *testing.T) { s := neofscryptotest.Signature() require.NotEqual(t, s, neofscryptotest.Signature()) - var m refs.Signature - s.WriteToV2(&m) + m := s.ProtoMessage() var s2 neofscrypto.Signature - require.NoError(t, s2.ReadFromV2(m)) + require.NoError(t, s2.FromProtoMessage(m)) require.Equal(t, s, s2) } diff --git a/internal/proto/encoding.go b/internal/proto/encoding.go index 31c70565d..b8121a7fd 100644 --- a/internal/proto/encoding.go +++ b/internal/proto/encoding.go @@ -7,7 +7,9 @@ import ( "encoding/binary" "math" + "google.golang.org/protobuf/encoding/protojson" "google.golang.org/protobuf/encoding/protowire" + "google.golang.org/protobuf/proto" ) // Message is provided by protobuf 'message' types used in NeoFS for so-called @@ -21,6 +23,19 @@ type Message interface { MarshalStable(b []byte) } +// MarshalMessage returns m encoded to dynamically allocated buffer. +func MarshalMessage(m Message) []byte { + b := make([]byte, m.MarshaledSize()) + m.MarshalStable(b) + return b +} + +var jOpts = protojson.MarshalOptions{EmitUnpopulated: true} + +// MarshalMessageJSON encodes m into [protojson] with unpopulated fields +// emission. +func MarshalMessageJSON(m proto.Message) ([]byte, error) { return jOpts.Marshal(m) } + // Bytes is a type parameter constraint for any byte arrays. type Bytes interface{ ~[]byte | ~string } diff --git a/netmap/context.go b/netmap/context.go index bb280b1f2..7a0e04d03 100644 --- a/netmap/context.go +++ b/netmap/context.go @@ -4,7 +4,6 @@ import ( "errors" "github.com/nspcc-dev/hrw/v2" - "github.com/nspcc-dev/neofs-api-go/v2/netmap" ) // context of a placement build process. @@ -13,10 +12,10 @@ type context struct { netMap NetMap // cache of processed filters - processedFilters map[string]*netmap.Filter + processedFilters map[string]*Filter // cache of processed selectors - processedSelectors map[string]*netmap.Selector + processedSelectors map[string]*Selector // stores results of selector processing selections map[string][]nodes @@ -55,8 +54,8 @@ var ( func newContext(nm NetMap) *context { return &context{ netMap: nm, - processedFilters: make(map[string]*netmap.Filter), - processedSelectors: make(map[string]*netmap.Selector), + processedFilters: make(map[string]*Filter), + processedSelectors: make(map[string]*Selector), selections: make(map[string][]nodes), numCache: make(map[string]uint64), diff --git a/netmap/example_test.go b/netmap/example_test.go index bb87378f8..6648b0a69 100644 --- a/netmap/example_test.go +++ b/netmap/example_test.go @@ -3,24 +3,21 @@ package netmap_test import ( "fmt" - apiGoNetmap "github.com/nspcc-dev/neofs-api-go/v2/netmap" "github.com/nspcc-dev/neofs-sdk-go/netmap" ) // Instances can be also used to process NeoFS API V2 protocol messages with [https://github.com/nspcc-dev/neofs-api] package. func ExampleNodeInfo_marshalling() { - // import apiGoNetmap "github.com/nspcc-dev/neofs-api-go/v2/netmap" // On the client side. var info netmap.NodeInfo - var msg apiGoNetmap.NodeInfo - info.WriteToV2(&msg) + msg := info.ProtoMessage() // *send message* // On the server side. - _ = info.ReadFromV2(msg) + _ = info.FromProtoMessage(msg) } // When forming information about storage node to be registered the NeoFS diff --git a/netmap/filter.go b/netmap/filter.go index bd229cad6..126d743ff 100644 --- a/netmap/filter.go +++ b/netmap/filter.go @@ -3,8 +3,6 @@ package netmap import ( "fmt" "strconv" - - "github.com/nspcc-dev/neofs-api-go/v2/netmap" ) // mainFilterName is a name of the filter @@ -15,15 +13,15 @@ const mainFilterName = "*" func (c *context) processFilters(p PlacementPolicy) error { for i := range p.filters { if err := c.processFilter(p.filters[i], true); err != nil { - return fmt.Errorf("process filter #%d (%s): %w", i, p.filters[i].GetName(), err) + return fmt.Errorf("process filter #%d (%s): %w", i, p.filters[i].Name(), err) } } return nil } -func (c *context) processFilter(f netmap.Filter, top bool) error { - fName := f.GetName() +func (c *context) processFilter(f Filter, top bool) error { + fName := f.Name() if fName == mainFilterName { return fmt.Errorf("%w: '%s' is reserved", errInvalidFilterName, mainFilterName) } @@ -36,10 +34,10 @@ func (c *context) processFilter(f netmap.Filter, top bool) error { return errFilterNotFound } - inner := f.GetFilters() + inner := f.SubFilters() - switch op := f.GetOp(); op { - case netmap.AND, netmap.OR: + switch op := f.Op(); op { + case FilterOpAND, FilterOpOR: for i := range inner { if err := c.processFilter(inner[i], false); err != nil { return fmt.Errorf("process inner filter #%d: %w", i, err) @@ -53,12 +51,12 @@ func (c *context) processFilter(f netmap.Filter, top bool) error { } switch op { - case netmap.EQ, netmap.NE: - case netmap.GT, netmap.GE, netmap.LT, netmap.LE: - val := f.GetValue() + case FilterOpEQ, FilterOpNE: + case FilterOpGT, FilterOpGE, FilterOpLT, FilterOpLE: + val := f.Value() n, err := strconv.ParseUint(val, 10, 64) if err != nil { - return fmt.Errorf("%w: '%s'", errInvalidNumber, f.GetValue()) + return fmt.Errorf("%w: '%s'", errInvalidNumber, val) } c.numCache[val] = n @@ -77,38 +75,41 @@ func (c *context) processFilter(f netmap.Filter, top bool) error { // match matches f against b. It returns no errors because // filter should have been parsed during context creation // and missing node properties are considered as a regular fail. -func (c *context) match(f *netmap.Filter, b NodeInfo) bool { - switch f.GetOp() { - case netmap.AND, netmap.OR: - inner := f.GetFilters() +func (c *context) match(f *Filter, b NodeInfo) bool { + if f == nil { + return false + } + switch f.Op() { + case FilterOpAND, FilterOpOR: + inner := f.SubFilters() for i := range inner { fSub := &inner[i] - if name := inner[i].GetName(); name != "" { + if name := inner[i].Name(); name != "" { fSub = c.processedFilters[name] } ok := c.match(fSub, b) - if ok == (f.GetOp() == netmap.OR) { + if ok == (f.Op() == FilterOpOR) { return ok } } - return f.GetOp() == netmap.AND + return f.Op() == FilterOpAND default: - return c.matchKeyValue(f, b) + return c.matchKeyValue(*f, b) } } -func (c *context) matchKeyValue(f *netmap.Filter, b NodeInfo) bool { - switch op := f.GetOp(); op { - case netmap.EQ: - return b.Attribute(f.GetKey()) == f.GetValue() - case netmap.NE: - return b.Attribute(f.GetKey()) != f.GetValue() +func (c *context) matchKeyValue(f Filter, b NodeInfo) bool { + switch op := f.Op(); op { + case FilterOpEQ: + return b.Attribute(f.Key()) == f.Value() + case FilterOpNE: + return b.Attribute(f.Key()) != f.Value() default: var attr uint64 - switch f.GetKey() { + switch f.Key() { case attrPrice: attr = b.Price() case attrCapacity: @@ -116,7 +117,7 @@ func (c *context) matchKeyValue(f *netmap.Filter, b NodeInfo) bool { default: var err error - attr, err = strconv.ParseUint(b.Attribute(f.GetKey()), 10, 64) + attr, err = strconv.ParseUint(b.Attribute(f.Key()), 10, 64) if err != nil { // Note: because filters are somewhat independent from nodes attributes, // We don't report an error here, and fail filter instead. @@ -125,14 +126,14 @@ func (c *context) matchKeyValue(f *netmap.Filter, b NodeInfo) bool { } switch op { - case netmap.GT: - return attr > c.numCache[f.GetValue()] - case netmap.GE: - return attr >= c.numCache[f.GetValue()] - case netmap.LT: - return attr < c.numCache[f.GetValue()] - case netmap.LE: - return attr <= c.numCache[f.GetValue()] + case FilterOpGT: + return attr > c.numCache[f.Value()] + case FilterOpGE: + return attr >= c.numCache[f.Value()] + case FilterOpLT: + return attr < c.numCache[f.Value()] + case FilterOpLE: + return attr <= c.numCache[f.Value()] default: // do nothing and return false } diff --git a/netmap/filter_test.go b/netmap/filter_test.go index fc3007379..bb057d089 100644 --- a/netmap/filter_test.go +++ b/netmap/filter_test.go @@ -4,17 +4,16 @@ import ( "errors" "testing" - "github.com/nspcc-dev/neofs-api-go/v2/netmap" "github.com/stretchr/testify/require" ) func TestContext_ProcessFilters(t *testing.T) { fs := []Filter{ - newFilter("StorageSSD", "Storage", "SSD", netmap.EQ), - newFilter("GoodRating", "Rating", "4", netmap.GE), - newFilter("Main", "", "", netmap.AND, + newFilter("StorageSSD", "Storage", "SSD", FilterOpEQ), + newFilter("GoodRating", "Rating", "4", FilterOpGE), + newFilter("Main", "", "", FilterOpAND, newFilter("StorageSSD", "", "", 0), - newFilter("", "IntField", "123", netmap.LT), + newFilter("", "IntField", "123", FilterOpLT), newFilter("GoodRating", "", "", 0)), } @@ -23,11 +22,11 @@ func TestContext_ProcessFilters(t *testing.T) { require.NoError(t, c.processFilters(p)) require.Equal(t, 3, len(c.processedFilters)) for _, f := range fs { - require.Equal(t, f.m, *c.processedFilters[f.m.GetName()]) + require.Equal(t, f, *c.processedFilters[f.Name()]) } - require.Equal(t, uint64(4), c.numCache[fs[1].m.GetValue()]) - require.Equal(t, uint64(123), c.numCache[fs[2].m.GetFilters()[1].GetValue()]) + require.Equal(t, uint64(4), c.numCache[fs[1].Value()]) + require.Equal(t, uint64(123), c.numCache[fs[2].SubFilters()[1].Value()]) } func TestContext_ProcessFiltersInvalid(t *testing.T) { @@ -38,24 +37,24 @@ func TestContext_ProcessFiltersInvalid(t *testing.T) { }{ { "UnnamedTop", - newFilter("", "Storage", "SSD", netmap.EQ), + newFilter("", "Storage", "SSD", FilterOpEQ), errUnnamedTopFilter, }, { "InvalidReference", - newFilter("Main", "", "", netmap.AND, + newFilter("Main", "", "", FilterOpAND, newFilter("StorageSSD", "", "", 0)), errFilterNotFound, }, { "NonEmptyKeyed", - newFilter("Main", "Storage", "SSD", netmap.EQ, + newFilter("Main", "Storage", "SSD", FilterOpEQ, newFilter("StorageSSD", "", "", 0)), errNonEmptyFilters, }, { "InvalidNumber", - newFilter("Main", "Rating", "three", netmap.GE), + newFilter("Main", "Rating", "three", FilterOpGE), errInvalidNumber, }, { @@ -65,7 +64,7 @@ func TestContext_ProcessFiltersInvalid(t *testing.T) { }, { "InvalidName", - newFilter("*", "Rating", "3", netmap.GE), + newFilter("*", "Rating", "3", FilterOpGE), errInvalidFilterName, }, } @@ -84,12 +83,12 @@ func TestFilter_MatchSimple_InvalidOp(t *testing.T) { b.SetAttribute("Rating", "4") b.SetAttribute("Country", "Germany") - f := newFilter("Main", "Rating", "5", netmap.EQ) + f := newFilter("Main", "Rating", "5", FilterOpEQ) c := newContext(NetMap{}) p := newPlacementPolicy(1, nil, nil, []Filter{f}) require.NoError(t, c.processFilters(p)) // just for the coverage - f.m.SetOp(0) - require.False(t, c.match(&f.m, b)) + f.op = 0 + require.False(t, c.match(&f, b)) } diff --git a/netmap/helper_test.go b/netmap/helper_test.go index 0eff22af2..e84ff3db3 100644 --- a/netmap/helper_test.go +++ b/netmap/helper_test.go @@ -1,19 +1,11 @@ package netmap -import ( - "github.com/nspcc-dev/neofs-api-go/v2/netmap" -) - -func newFilter(name string, k, v string, op netmap.Operation, fs ...Filter) (f Filter) { +func newFilter(name string, k, v string, op FilterOp, fs ...Filter) (f Filter) { f.SetName(name) - f.m.SetKey(k) - f.m.SetOp(op) - f.m.SetValue(v) - inner := make([]netmap.Filter, len(fs)) - for i := range fs { - inner[i] = fs[i].m - } - f.m.SetFilters(inner) + f.key = k + f.op = op + f.val = v + f.subs = fs return f } diff --git a/netmap/netmap.go b/netmap/netmap.go index 0f3249a2c..b69080a73 100644 --- a/netmap/netmap.go +++ b/netmap/netmap.go @@ -5,16 +5,16 @@ import ( "slices" "github.com/nspcc-dev/hrw/v2" - "github.com/nspcc-dev/neofs-api-go/v2/netmap" cid "github.com/nspcc-dev/neofs-sdk-go/container/id" oid "github.com/nspcc-dev/neofs-sdk-go/object/id" + protonetmap "github.com/nspcc-dev/neofs-sdk-go/proto/netmap" ) // NetMap represents NeoFS network map. It includes information about all // storage nodes registered in NeoFS the network. // -// NetMap is mutually compatible with github.com/nspcc-dev/neofs-api-go/v2/netmap.NetMap -// message. See ReadFromV2 / WriteToV2 methods. +// NetMap is mutually compatible with [protonetmap.Netmap] message. See +// [NetMap.FromProtoMessage] / [NetMap.ProtoMessage] methods. // // Instances can be created using built-in var declaration. type NetMap struct { @@ -23,50 +23,51 @@ type NetMap struct { nodes []NodeInfo } -// ReadFromV2 reads NetMap from the netmap.NetMap message. Checks if the -// message conforms to NeoFS API V2 protocol. +// FromProtoMessage validates msg according to the NeoFS API protocol and +// restores m from it. // -// See also WriteToV2. -func (m *NetMap) ReadFromV2(msg netmap.NetMap) error { +// See also [NetMap.ProtoMessage]. +func (m *NetMap) FromProtoMessage(msg *protonetmap.Netmap) error { var err error - nodes := msg.Nodes() - if nodes == nil { + if msg.Nodes == nil { m.nodes = nil } else { - m.nodes = make([]NodeInfo, len(nodes)) + m.nodes = make([]NodeInfo, len(msg.Nodes)) - for i := range nodes { - err = m.nodes[i].ReadFromV2(nodes[i]) + for i := range msg.Nodes { + if msg.Nodes[i] == nil { + return fmt.Errorf("nil node info #%d", i) + } + err = m.nodes[i].FromProtoMessage(msg.Nodes[i]) if err != nil { return fmt.Errorf("invalid node info: %w", err) } } } - m.epoch = msg.Epoch() + m.epoch = msg.Epoch return nil } -// WriteToV2 writes NetMap to the netmap.NetMap message. The message -// MUST NOT be nil. +// ProtoMessage converts m into message to transmit using the NeoFS API +// protocol. // -// See also ReadFromV2. -func (m NetMap) WriteToV2(msg *netmap.NetMap) { - var nodes []netmap.NodeInfo - +// See also [NetMap.FromProtoMessage]. +func (m NetMap) ProtoMessage() *protonetmap.Netmap { + msg := &protonetmap.Netmap{ + Epoch: m.epoch, + } if m.nodes != nil { - nodes = make([]netmap.NodeInfo, len(m.nodes)) + msg.Nodes = make([]*protonetmap.NodeInfo, len(m.nodes)) for i := range m.nodes { - m.nodes[i].WriteToV2(&nodes[i]) + msg.Nodes[i] = m.nodes[i].ProtoMessage() } - - msg.SetNodes(nodes) } - msg.SetEpoch(m.epoch) + return msg } // SetNodes sets information list about all storage nodes from the NeoFS network. @@ -196,12 +197,12 @@ func (m NetMap) ContainerNodes(p PlacementPolicy, containerID cid.ID) ([][]NodeI result := make([][]NodeInfo, len(p.replicas)) for i := range p.replicas { - sName := p.replicas[i].GetSelector() + sName := p.replicas[i].SelectorName() if sName == "" { if len(p.selectors) == 0 { - var s netmap.Selector - s.SetCount(p.replicas[i].GetCount()) - s.SetFilter(mainFilterName) + var s Selector + s.SetNumberOfNodes(p.replicas[i].NumberOfObjects()) + s.SetFilterName(mainFilterName) nodes, err := c.getSelection(p, s) if err != nil { @@ -212,7 +213,7 @@ func (m NetMap) ContainerNodes(p PlacementPolicy, containerID cid.ID) ([][]NodeI } for i := range p.selectors { - result[i] = append(result[i], flattenNodes(c.selections[p.selectors[i].GetName()])...) + result[i] = append(result[i], flattenNodes(c.selections[p.selectors[i].Name()])...) } continue diff --git a/netmap/netmap_test.go b/netmap/netmap_test.go index 9513d6e7c..623bc8fef 100644 --- a/netmap/netmap_test.go +++ b/netmap/netmap_test.go @@ -3,8 +3,8 @@ package netmap_test import ( "testing" - apinetmap "github.com/nspcc-dev/neofs-api-go/v2/netmap" "github.com/nspcc-dev/neofs-sdk-go/netmap" + protonetmap "github.com/nspcc-dev/neofs-sdk-go/proto/netmap" "github.com/stretchr/testify/require" ) @@ -56,32 +56,33 @@ func init() { validNetmap.SetNodes(anyValidNodes) } -func TestNetMap_ReadFromV2(t *testing.T) { - mns := make([]apinetmap.NodeInfo, 2) - mns[0].SetPublicKey([]byte("public_key_0")) - mns[1].SetPublicKey([]byte("public_key_1")) - mns[0].SetAddresses("endpoint_0_0", "endpoint_0_1") - mns[1].SetAddresses("endpoint_1_0", "endpoint_1_1") - mns[0].SetState(apinetmap.Offline) - mns[1].SetState(apinetmap.Maintenance) - - addAttr := func(m *apinetmap.NodeInfo, k, v string) { - var a apinetmap.Attribute - a.SetKey(k) - a.SetValue(v) - m.SetAttributes(append(m.GetAttributes(), a)) +func TestNetMap_FromProtoMessage(t *testing.T) { + m := &protonetmap.Netmap{ + Epoch: anyValidCurrentEpoch, + Nodes: []*protonetmap.NodeInfo{ + { + PublicKey: []byte("public_key_0"), + Addresses: []string{"endpoint_0_0", "endpoint_0_1"}, + Attributes: []*protonetmap.NodeInfo_Attribute{ + {Key: "k_0_0", Value: "v_0_0"}, + {Key: "k_0_1", Value: "v_0_1"}, + }, + State: protonetmap.NodeInfo_OFFLINE, + }, + { + PublicKey: []byte("public_key_1"), + Addresses: []string{"endpoint_1_0", "endpoint_1_1"}, + Attributes: []*protonetmap.NodeInfo_Attribute{ + {Key: "k_1_0", Value: "v_1_0"}, + {Key: "k_1_1", Value: "v_1_1"}, + }, + State: protonetmap.NodeInfo_MAINTENANCE, + }, + }, } - addAttr(&mns[0], "k_0_0", "v_0_0") - addAttr(&mns[0], "k_0_1", "v_0_1") - addAttr(&mns[1], "k_1_0", "v_1_0") - addAttr(&mns[1], "k_1_1", "v_1_1") - - var m apinetmap.NetMap - m.SetEpoch(anyValidCurrentEpoch) - m.SetNodes(mns) var val netmap.NetMap - require.NoError(t, val.ReadFromV2(m)) + require.NoError(t, val.FromProtoMessage(m)) require.EqualValues(t, anyValidCurrentEpoch, val.Epoch()) ns := val.Nodes() @@ -123,59 +124,59 @@ func TestNetMap_ReadFromV2(t *testing.T) { }, collectedAttrs) // reset optional fields - m.SetEpoch(0) - m.SetNodes(nil) + m.Epoch = 0 + m.Nodes = nil val2 := val - require.NoError(t, val2.ReadFromV2(m)) + require.NoError(t, val2.FromProtoMessage(m)) require.Zero(t, val2.Epoch()) require.Zero(t, val2.Nodes()) t.Run("invalid", func(t *testing.T) { for _, tc := range []struct { name, err string - corrupt func(netMap *apinetmap.NetMap) + corrupt func(netMap *protonetmap.Netmap) }{ + {name: "nodes/nil", err: "nil node info #1", + corrupt: func(m *protonetmap.Netmap) { m.Nodes[1] = nil }}, {name: "nodes/public key/nil", err: "invalid node info: missing public key", - corrupt: func(m *apinetmap.NetMap) { m.Nodes()[1].SetPublicKey(nil) }}, + corrupt: func(m *protonetmap.Netmap) { m.Nodes[1].PublicKey = nil }}, {name: "nodes/public key/empty", err: "invalid node info: missing public key", - corrupt: func(m *apinetmap.NetMap) { m.Nodes()[1].SetPublicKey([]byte{}) }}, + corrupt: func(m *protonetmap.Netmap) { m.Nodes[1].PublicKey = []byte{} }}, {name: "nodes/endpoints/empty", err: "invalid node info: missing network endpoints", - corrupt: func(m *apinetmap.NetMap) { m.Nodes()[1].SetAddresses() }}, + corrupt: func(m *protonetmap.Netmap) { m.Nodes[1].Addresses = nil }}, {name: "nodes/attributes/no key", err: "invalid node info: empty key of the attribute #1", - corrupt: func(m *apinetmap.NetMap) { setNodeAttributes(&m.Nodes()[1], "k1", "v1", "", "v2") }}, + corrupt: func(m *protonetmap.Netmap) { setNodeAttributes(m.Nodes[1], "k1", "v1", "", "v2") }}, {name: "nodes/attributes/no value", err: "invalid node info: empty value of the attribute k2", - corrupt: func(m *apinetmap.NetMap) { setNodeAttributes(&m.Nodes()[1], "k1", "v1", "k2", "") }}, + corrupt: func(m *protonetmap.Netmap) { setNodeAttributes(m.Nodes[1], "k1", "v1", "k2", "") }}, {name: "nodes/attributes/duplicated", err: "invalid node info: duplicated attribute k1", - corrupt: func(m *apinetmap.NetMap) { setNodeAttributes(&m.Nodes()[1], "k1", "v1", "k2", "v2", "k1", "v3") }}, + corrupt: func(m *protonetmap.Netmap) { setNodeAttributes(m.Nodes[1], "k1", "v1", "k2", "v2", "k1", "v3") }}, {name: "nodes/attributes/capacity", err: "invalid node info: invalid Capacity attribute: strconv.ParseUint: parsing \"foo\": invalid syntax", - corrupt: func(m *apinetmap.NetMap) { setNodeAttributes(&m.Nodes()[1], "Capacity", "foo") }}, + corrupt: func(m *protonetmap.Netmap) { setNodeAttributes(m.Nodes[1], "Capacity", "foo") }}, {name: "nodes/attributes/price", err: "invalid node info: invalid Price attribute: strconv.ParseUint: parsing \"foo\": invalid syntax", - corrupt: func(m *apinetmap.NetMap) { setNodeAttributes(&m.Nodes()[1], "Price", "foo") }}, + corrupt: func(m *protonetmap.Netmap) { setNodeAttributes(m.Nodes[1], "Price", "foo") }}, } { t.Run(tc.name, func(t *testing.T) { st := val - var m apinetmap.NetMap - st.WriteToV2(&m) - tc.corrupt(&m) - require.EqualError(t, new(netmap.NetMap).ReadFromV2(m), tc.err) + m := st.ProtoMessage() + tc.corrupt(m) + require.EqualError(t, new(netmap.NetMap).FromProtoMessage(m), tc.err) }) } }) } -func TestNetMap_WriteToV2(t *testing.T) { +func TestNetMap_ProtoMessage(t *testing.T) { var val netmap.NetMap - var m apinetmap.NetMap // zero - val.WriteToV2(&m) - require.Zero(t, m.Epoch()) - require.Zero(t, m.Nodes()) + m := val.ProtoMessage() + require.Zero(t, m.GetEpoch()) + require.Zero(t, m.GetNodes()) // filled - validNetmap.WriteToV2(&m) - require.EqualValues(t, anyValidCurrentEpoch, m.Epoch()) - ns := m.Nodes() + m = validNetmap.ProtoMessage() + require.EqualValues(t, anyValidCurrentEpoch, m.GetEpoch()) + ns := m.GetNodes() require.Len(t, ns, 2) require.EqualValues(t, "public_key_0", ns[0].GetPublicKey()) require.EqualValues(t, "public_key_1", ns[1].GetPublicKey()) diff --git a/netmap/network_info.go b/netmap/network_info.go index 762e1c36c..bb74e1380 100644 --- a/netmap/network_info.go +++ b/netmap/network_info.go @@ -1,50 +1,64 @@ package netmap import ( - "bytes" "encoding/binary" "errors" "fmt" "math" "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" - "github.com/nspcc-dev/neofs-api-go/v2/netmap" + neofsproto "github.com/nspcc-dev/neofs-sdk-go/internal/proto" + protonetmap "github.com/nspcc-dev/neofs-sdk-go/proto/netmap" + "google.golang.org/protobuf/proto" ) // NetworkInfo groups information about the NeoFS network state. Mainly used to // describe the current state of the network. // -// NetworkInfo is mutually compatible with github.com/nspcc-dev/neofs-api-go/v2/netmap.NetworkInfo -// message. See ReadFromV2 / WriteToV2 methods. +// NetworkInfo is mutually compatible with [protonetmap.NetworkInfo] +// message. See [NetworkInfo.FromProtoMessage] / [NetworkInfo.ProtoMessage] methods. // // Instances can be created using built-in var declaration. type NetworkInfo struct { - m netmap.NetworkInfo + curEpoch uint64 + netMagic uint64 + msPerBlock int64 + prms [][2][]byte } // reads NetworkInfo from netmap.NetworkInfo message. If checkFieldPresence is set, // returns an error on absence of any protocol-required field. Verifies format of any // presented field according to NeoFS API V2 protocol. -func (x *NetworkInfo) readFromV2(m netmap.NetworkInfo, checkFieldPresence bool) error { +func (x *NetworkInfo) fromProtoMessage(m *protonetmap.NetworkInfo, checkFieldPresence bool) error { c := m.GetNetworkConfig() if checkFieldPresence && c == nil { return errors.New("missing network config") } - if checkFieldPresence && c.NumberOfParameters() <= 0 { + if checkFieldPresence && len(c.Parameters) == 0 { return errors.New("missing network parameters") } + if c == nil { + x.curEpoch = m.CurrentEpoch + x.netMagic = m.MagicNumber + x.msPerBlock = m.MsPerBlock + return nil + } + var err error - mNames := make(map[string]struct{}, c.NumberOfParameters()) + mNames := make(map[string]struct{}, len(c.Parameters)) + prms := make([][2][]byte, len(c.Parameters)) - c.IterateParameters(func(prm *netmap.NetworkParameter) bool { + for i, prm := range c.Parameters { + if prm == nil { + return fmt.Errorf("nil parameter #%d", i) + } name := string(prm.GetKey()) _, was := mNames[name] if was { - err = fmt.Errorf("duplicated parameter name: %s", name) - return true + return fmt.Errorf("duplicated parameter name: %s", name) } mNames[name] = struct{}{} @@ -52,8 +66,7 @@ func (x *NetworkInfo) readFromV2(m netmap.NetworkInfo, checkFieldPresence bool) switch name { default: if len(prm.GetValue()) == 0 { - err = fmt.Errorf("empty attribute value %s", name) - return true + return fmt.Errorf("empty attribute value %s", name) } case configEigenTrustAlpha: var num uint64 @@ -81,35 +94,47 @@ func (x *NetworkInfo) readFromV2(m netmap.NetworkInfo, checkFieldPresence bool) } if err != nil { - err = fmt.Errorf("invalid %s parameter: %w", name, err) + return fmt.Errorf("invalid %s parameter: %w", name, err) } - return err != nil - }) - - if err != nil { - return err + prms[i] = [2][]byte{prm.Key, prm.Value} } - x.m = m + x.curEpoch = m.CurrentEpoch + x.netMagic = m.MagicNumber + x.msPerBlock = m.MsPerBlock + x.prms = prms return nil } -// ReadFromV2 reads NetworkInfo from the netmap.NetworkInfo message. Checks if the -// message conforms to NeoFS API V2 protocol. +// FromProtoMessage validates m according to the NeoFS API protocol and restores +// x from it. // -// See also WriteToV2. -func (x *NetworkInfo) ReadFromV2(m netmap.NetworkInfo) error { - return x.readFromV2(m, true) +// See also [NetworkInfo.ProtoMessage]. +func (x *NetworkInfo) FromProtoMessage(m *protonetmap.NetworkInfo) error { + return x.fromProtoMessage(m, true) } -// WriteToV2 writes NetworkInfo to the netmap.NetworkInfo message. The message -// MUST NOT be nil. +// ProtoMessage converts x into message to transmit using the NeoFS API +// protocol. // -// See also ReadFromV2. -func (x NetworkInfo) WriteToV2(m *netmap.NetworkInfo) { - *m = x.m +// See also [NetworkInfo.FromProtoMessage]. +func (x NetworkInfo) ProtoMessage() *protonetmap.NetworkInfo { + m := &protonetmap.NetworkInfo{ + CurrentEpoch: x.curEpoch, + MagicNumber: x.netMagic, + MsPerBlock: x.msPerBlock, + } + if len(x.prms) > 0 { + m.NetworkConfig = &protonetmap.NetworkConfig{ + Parameters: make([]*protonetmap.NetworkConfig_Parameter, len(x.prms)), + } + for i := range x.prms { + m.NetworkConfig.Parameters[i] = &protonetmap.NetworkConfig_Parameter{Key: x.prms[i][0], Value: x.prms[i][1]} + } + } + return m } // Marshal encodes NetworkInfo into a binary format of the NeoFS API protocol @@ -117,10 +142,7 @@ func (x NetworkInfo) WriteToV2(m *netmap.NetworkInfo) { // // See also Unmarshal. func (x NetworkInfo) Marshal() []byte { - var m netmap.NetworkInfo - x.WriteToV2(&m) - - return m.StableMarshal(nil) + return neofsproto.MarshalMessage(x.ProtoMessage()) } // Unmarshal decodes NeoFS API protocol binary format into the NetworkInfo @@ -129,105 +151,72 @@ func (x NetworkInfo) Marshal() []byte { // // See also Marshal. func (x *NetworkInfo) Unmarshal(data []byte) error { - var m netmap.NetworkInfo + var m protonetmap.NetworkInfo - err := m.Unmarshal(data) + err := proto.Unmarshal(data, &m) if err != nil { return err } - return x.readFromV2(m, false) + return x.fromProtoMessage(&m, false) } // CurrentEpoch returns epoch set using SetCurrentEpoch. // // Zero NetworkInfo has zero current epoch. func (x NetworkInfo) CurrentEpoch() uint64 { - return x.m.GetCurrentEpoch() + return x.curEpoch } // SetCurrentEpoch sets current epoch of the NeoFS network. func (x *NetworkInfo) SetCurrentEpoch(epoch uint64) { - x.m.SetCurrentEpoch(epoch) + x.curEpoch = epoch } // MagicNumber returns magic number set using SetMagicNumber. // // Zero NetworkInfo has zero magic. func (x NetworkInfo) MagicNumber() uint64 { - return x.m.GetMagicNumber() + return x.netMagic } // SetMagicNumber sets magic number of the NeoFS Sidechain. // // See also MagicNumber. func (x *NetworkInfo) SetMagicNumber(epoch uint64) { - x.m.SetMagicNumber(epoch) + x.netMagic = epoch } // MsPerBlock returns network parameter set using SetMsPerBlock. func (x NetworkInfo) MsPerBlock() int64 { - return x.m.GetMsPerBlock() + return x.msPerBlock } // SetMsPerBlock sets MillisecondsPerBlock network parameter of the NeoFS Sidechain. // // See also MsPerBlock. func (x *NetworkInfo) SetMsPerBlock(v int64) { - x.m.SetMsPerBlock(v) + x.msPerBlock = v } func (x *NetworkInfo) setConfig(name string, val []byte) { - c := x.m.GetNetworkConfig() - if c == nil { - c = new(netmap.NetworkConfig) - - var prm netmap.NetworkParameter - prm.SetKey([]byte(name)) - prm.SetValue(val) - - c.SetParameters(prm) - - x.m.SetNetworkConfig(c) - - return - } - - found := false - prms := make([]netmap.NetworkParameter, 0, c.NumberOfParameters()) - - c.IterateParameters(func(prm *netmap.NetworkParameter) bool { - found = bytes.Equal(prm.GetKey(), []byte(name)) - if found { - prm.SetValue(val) - } else { - prms = append(prms, *prm) + for i := range x.prms { + if string(x.prms[i][0]) == name { + x.prms[i][1] = val + return } - - return found - }) - - if !found { - prms = append(prms, netmap.NetworkParameter{}) - prms[len(prms)-1].SetKey([]byte(name)) - prms[len(prms)-1].SetValue(val) - - c.SetParameters(prms...) } -} -func (x NetworkInfo) configValue(name string) (res []byte) { - x.m.GetNetworkConfig().IterateParameters(func(prm *netmap.NetworkParameter) bool { - if string(prm.GetKey()) == name { - res = prm.GetValue() + x.prms = append(x.prms, [2][]byte{[]byte(name), val}) +} - return true +func (x NetworkInfo) configValue(name string) []byte { + for i := range x.prms { + if string(x.prms[i][0]) == name { + return x.prms[i][1] } - - return false - }) - - return + } + return nil } // SetRawNetworkParameter sets named NeoFS network parameter whose value is @@ -258,13 +247,11 @@ func (x *NetworkInfo) RawNetworkParameter(name string) []byte { // // Zero NetworkInfo has no network parameters. func (x *NetworkInfo) IterateRawNetworkParameters(f func(name string, value []byte)) { - c := x.m.GetNetworkConfig() - - c.IterateParameters(func(prm *netmap.NetworkParameter) bool { - name := string(prm.GetKey()) + for i := range x.prms { + name := string(x.prms[i][0]) switch name { default: - f(name, prm.GetValue()) + f(name, x.prms[i][1]) case configEigenTrustAlpha, configAuditFee, @@ -279,9 +266,7 @@ func (x *NetworkInfo) IterateRawNetworkParameters(f func(name string, value []by configHomomorphicHashingDisabled, configMaintenanceModeAllowed: } - - return false - }) + } } func (x *NetworkInfo) setConfigUint64(name string, num uint64) { @@ -332,7 +317,7 @@ func (x NetworkInfo) configUint64(name string) uint64 { res, err := decodeConfigValueUint64(val) if err != nil { // potential panic is OK since value MUST be correct since it is - // verified in ReadFromV2 or set by provided method. + // verified in FromProtoMessage or set by provided method. panic(err) } @@ -348,7 +333,7 @@ func (x NetworkInfo) configBool(name string) bool { res, err := decodeConfigValueBool(val) if err != nil { // potential panic is OK since value MUST be correct since it is - // verified in ReadFromV2 or set by provided method. + // verified in FromProtoMessage or set by provided method. panic(err) } diff --git a/netmap/network_info_test.go b/netmap/network_info_test.go index ceb972985..aedeff89b 100644 --- a/netmap/network_info_test.go +++ b/netmap/network_info_test.go @@ -3,8 +3,8 @@ package netmap_test import ( "testing" - apinetmap "github.com/nspcc-dev/neofs-api-go/v2/netmap" "github.com/nspcc-dev/neofs-sdk-go/netmap" + protonetmap "github.com/nspcc-dev/neofs-sdk-go/proto/netmap" "github.com/stretchr/testify/require" ) @@ -213,52 +213,46 @@ func TestNetworkInfo_AllowMaintenanceMode(t *testing.T) { require.True(t, x.MaintenanceModeAllowed()) } -func setNetworkPrms[T string | []byte](ni *apinetmap.NetworkInfo, els ...T) { +func setNetworkPrms[T string | []byte](ni *protonetmap.NetworkInfo, els ...T) { if len(els)%2 != 0 { panic("must be even") } - mps := make([]apinetmap.NetworkParameter, len(els)/2) + ni.NetworkConfig.Parameters = make([]*protonetmap.NetworkConfig_Parameter, len(els)/2) for i := range len(els) / 2 { - mps[i].SetKey([]byte(els[2*i])) - mps[i].SetValue([]byte(els[2*i+1])) + ni.NetworkConfig.Parameters[i] = &protonetmap.NetworkConfig_Parameter{ + Key: []byte(els[2*i]), + Value: []byte(els[2*i+1]), + } } - var mc apinetmap.NetworkConfig - mc.SetParameters(mps...) - ni.SetNetworkConfig(&mc) } -func TestNetworkInfo_ReadFromV2(t *testing.T) { - var mps []apinetmap.NetworkParameter - addP := func(k string, v []byte) { - var m apinetmap.NetworkParameter - m.SetKey([]byte(k)) - m.SetValue(v) - mps = append(mps, m) +func TestNetworkInfo_FromProtoMessage(t *testing.T) { + m := &protonetmap.NetworkInfo{ + CurrentEpoch: anyValidCurrentEpoch, + MagicNumber: anyValidMagicNumber, + MsPerBlock: anyValidMSPerBlock, + NetworkConfig: &protonetmap.NetworkConfig{ + Parameters: []*protonetmap.NetworkConfig_Parameter{ + {Key: []byte("k1"), Value: []byte("v1")}, + {Key: []byte("k2"), Value: []byte("v2")}, + {Key: []byte("AuditFee"), Value: anyValidBinAuditFee}, + {Key: []byte("BasicIncomeRate"), Value: anyValidBinStoragePrice}, + {Key: []byte("ContainerAliasFee"), Value: anyValidBinNamedContainerFee}, + {Key: []byte("ContainerFee"), Value: anyValidBinContainerFee}, + {Key: []byte("EigenTrustAlpha"), Value: anyValidBinEigenTrustAlpha}, + {Key: []byte("EigenTrustIterations"), Value: anyValidBinEigenTrustIterations}, + {Key: []byte("EpochDuration"), Value: anyValidBinEpochDuration}, + {Key: []byte("HomomorphicHashingDisabled"), Value: anyValidBinHomoHashDisabled}, + {Key: []byte("InnerRingCandidateFee"), Value: anyValidBinIRCandidateFee}, + {Key: []byte("MaintenanceModeAllowed"), Value: anyValidBinMaintenanceModeAllowed}, + {Key: []byte("MaxObjectSize"), Value: anyValidBinMaxObjectSize}, + {Key: []byte("WithdrawFee"), Value: anyValidBinWithdrawalFee}, + }, + }, } - addP("k1", []byte("v1")) - addP("k2", []byte("v2")) - addP("AuditFee", anyValidBinAuditFee) - addP("BasicIncomeRate", anyValidBinStoragePrice) - addP("ContainerAliasFee", anyValidBinNamedContainerFee) - addP("ContainerFee", anyValidBinContainerFee) - addP("EigenTrustAlpha", anyValidBinEigenTrustAlpha) - addP("EigenTrustIterations", anyValidBinEigenTrustIterations) - addP("EpochDuration", anyValidBinEpochDuration) - addP("HomomorphicHashingDisabled", anyValidBinHomoHashDisabled) - addP("InnerRingCandidateFee", anyValidBinIRCandidateFee) - addP("MaintenanceModeAllowed", anyValidBinMaintenanceModeAllowed) - addP("MaxObjectSize", anyValidBinMaxObjectSize) - addP("WithdrawFee", anyValidBinWithdrawalFee) - var mc apinetmap.NetworkConfig - mc.SetParameters(mps...) - var m apinetmap.NetworkInfo - m.SetCurrentEpoch(anyValidCurrentEpoch) - m.SetMagicNumber(anyValidMagicNumber) - m.SetMsPerBlock(anyValidMSPerBlock) - m.SetNetworkConfig(&mc) var val netmap.NetworkInfo - require.NoError(t, val.ReadFromV2(m)) + require.NoError(t, val.FromProtoMessage(m)) require.EqualValues(t, "v1", val.RawNetworkParameter("k1")) require.EqualValues(t, "v2", val.RawNetworkParameter("k2")) require.Equal(t, anyValidCurrentEpoch, val.CurrentEpoch()) @@ -278,12 +272,12 @@ func TestNetworkInfo_ReadFromV2(t *testing.T) { require.Equal(t, anyValidWithdrawalFee, val.WithdrawalFee()) // reset optional fields - mc.SetParameters(mps[0]) - m.SetCurrentEpoch(0) - m.SetMagicNumber(0) - m.SetMsPerBlock(0) + m.NetworkConfig.Parameters = m.NetworkConfig.Parameters[:1] + m.CurrentEpoch = 0 + m.MagicNumber = 0 + m.MsPerBlock = 0 val2 := val - require.NoError(t, val2.ReadFromV2(m)) + require.NoError(t, val2.FromProtoMessage(m)) require.EqualValues(t, "v1", val.RawNetworkParameter("k1")) require.Zero(t, val2.RawNetworkParameter("k2")) require.Zero(t, val2.CurrentEpoch()) @@ -305,89 +299,85 @@ func TestNetworkInfo_ReadFromV2(t *testing.T) { t.Run("invalid", func(t *testing.T) { for _, tc := range []struct { name, err string - corrupt func(*apinetmap.NetworkInfo) + corrupt func(*protonetmap.NetworkInfo) }{ {name: "netconfig/missing", err: "missing network config", - corrupt: func(m *apinetmap.NetworkInfo) { m.SetNetworkConfig(nil) }}, + corrupt: func(m *protonetmap.NetworkInfo) { m.NetworkConfig = nil }}, {name: "netconfig/prms/missing", err: "missing network parameters", - corrupt: func(m *apinetmap.NetworkInfo) { m.SetNetworkConfig(new(apinetmap.NetworkConfig)) }}, + corrupt: func(m *protonetmap.NetworkInfo) { m.NetworkConfig = new(protonetmap.NetworkConfig) }}, + {name: "netconfig/prms/nil", err: "nil parameter #1", + corrupt: func(m *protonetmap.NetworkInfo) { + m.NetworkConfig.Parameters[1] = nil + }}, {name: "netconfig/prms/no value", err: "empty attribute value k1", - corrupt: func(m *apinetmap.NetworkInfo) { setNetworkPrms(m, "k1", "") }}, + corrupt: func(m *protonetmap.NetworkInfo) { setNetworkPrms(m, "k1", "") }}, {name: "netconfig/prms/duplicated", err: "duplicated parameter name: k1", - corrupt: func(m *apinetmap.NetworkInfo) { setNetworkPrms(m, "k1", "v1", "k2", "v2", "k1", "v3") }}, + corrupt: func(m *protonetmap.NetworkInfo) { setNetworkPrms(m, "k1", "v1", "k2", "v2", "k1", "v3") }}, {name: "netconfig/prms/eigen trust alpha/overflow", err: "invalid EigenTrustAlpha parameter: invalid uint64 parameter length 9", - corrupt: func(m *apinetmap.NetworkInfo) { setNetworkPrms(m, "EigenTrustAlpha", "123456789") }}, + corrupt: func(m *protonetmap.NetworkInfo) { setNetworkPrms(m, "EigenTrustAlpha", "123456789") }}, {name: "netconfig/prms/eigen trust alpha/negative", err: "invalid EigenTrustAlpha parameter: EigenTrust alpha value -0.50 is out of range [0, 1]", - corrupt: func(m *apinetmap.NetworkInfo) { + corrupt: func(m *protonetmap.NetworkInfo) { setNetworkPrms(m, []byte("EigenTrustAlpha"), []byte{0, 0, 0, 0, 0, 0, 224, 191}) }}, {name: "netconfig/prms/eigen trust alpha/too big", err: "invalid EigenTrustAlpha parameter: EigenTrust alpha value 1.50 is out of range [0, 1]", - corrupt: func(m *apinetmap.NetworkInfo) { + corrupt: func(m *protonetmap.NetworkInfo) { setNetworkPrms(m, []byte("EigenTrustAlpha"), []byte{0, 0, 0, 0, 0, 0, 248, 63}) }}, {name: "netconfig/prms/homo hash disabled/overflow", err: "invalid HomomorphicHashingDisabled parameter: invalid bool parameter contract format too big: integer", - corrupt: func(m *apinetmap.NetworkInfo) { + corrupt: func(m *protonetmap.NetworkInfo) { setNetworkPrms(m, []byte("HomomorphicHashingDisabled"), make([]byte, 33)) }}, {name: "netconfig/prms/maintenance allowed/overflow", err: "invalid MaintenanceModeAllowed parameter: invalid bool parameter contract format too big: integer", - corrupt: func(m *apinetmap.NetworkInfo) { + corrupt: func(m *protonetmap.NetworkInfo) { setNetworkPrms(m, []byte("MaintenanceModeAllowed"), make([]byte, 33)) }}, {name: "netconfig/prms/audit fee/overflow", err: "invalid AuditFee parameter: invalid uint64 parameter length 9", - corrupt: func(m *apinetmap.NetworkInfo) { setNetworkPrms(m, []byte("AuditFee"), make([]byte, 9)) }}, + corrupt: func(m *protonetmap.NetworkInfo) { setNetworkPrms(m, []byte("AuditFee"), make([]byte, 9)) }}, {name: "netconfig/prms/storage price/overflow", err: "invalid BasicIncomeRate parameter: invalid uint64 parameter length 9", - corrupt: func(m *apinetmap.NetworkInfo) { setNetworkPrms(m, []byte("BasicIncomeRate"), make([]byte, 9)) }}, + corrupt: func(m *protonetmap.NetworkInfo) { setNetworkPrms(m, []byte("BasicIncomeRate"), make([]byte, 9)) }}, {name: "netconfig/prms/container fee/overflow", err: "invalid ContainerFee parameter: invalid uint64 parameter length 9", - corrupt: func(m *apinetmap.NetworkInfo) { setNetworkPrms(m, []byte("ContainerFee"), make([]byte, 9)) }}, + corrupt: func(m *protonetmap.NetworkInfo) { setNetworkPrms(m, []byte("ContainerFee"), make([]byte, 9)) }}, {name: "netconfig/prms/named container fee/overflow", err: "invalid ContainerAliasFee parameter: invalid uint64 parameter length 9", - corrupt: func(m *apinetmap.NetworkInfo) { setNetworkPrms(m, []byte("ContainerAliasFee"), make([]byte, 9)) }}, + corrupt: func(m *protonetmap.NetworkInfo) { setNetworkPrms(m, []byte("ContainerAliasFee"), make([]byte, 9)) }}, {name: "netconfig/prms/eigen trust iterations/overflow", err: "invalid EigenTrustIterations parameter: invalid uint64 parameter length 9", - corrupt: func(m *apinetmap.NetworkInfo) { setNetworkPrms(m, []byte("EigenTrustIterations"), make([]byte, 9)) }}, + corrupt: func(m *protonetmap.NetworkInfo) { setNetworkPrms(m, []byte("EigenTrustIterations"), make([]byte, 9)) }}, {name: "netconfig/prms/epoch duration/overflow", err: "invalid EpochDuration parameter: invalid uint64 parameter length 9", - corrupt: func(m *apinetmap.NetworkInfo) { setNetworkPrms(m, []byte("EpochDuration"), make([]byte, 9)) }}, + corrupt: func(m *protonetmap.NetworkInfo) { setNetworkPrms(m, []byte("EpochDuration"), make([]byte, 9)) }}, {name: "netconfig/prms/ir candidate fee/overflow", err: "invalid InnerRingCandidateFee parameter: invalid uint64 parameter length 9", - corrupt: func(m *apinetmap.NetworkInfo) { setNetworkPrms(m, []byte("InnerRingCandidateFee"), make([]byte, 9)) }}, + corrupt: func(m *protonetmap.NetworkInfo) { setNetworkPrms(m, []byte("InnerRingCandidateFee"), make([]byte, 9)) }}, {name: "netconfig/prms/max object size/overflow", err: "invalid MaxObjectSize parameter: invalid uint64 parameter length 9", - corrupt: func(m *apinetmap.NetworkInfo) { setNetworkPrms(m, []byte("MaxObjectSize"), make([]byte, 9)) }}, + corrupt: func(m *protonetmap.NetworkInfo) { setNetworkPrms(m, []byte("MaxObjectSize"), make([]byte, 9)) }}, {name: "netconfig/prms/withdrawal fee/overflow", err: "invalid WithdrawFee parameter: invalid uint64 parameter length 9", - corrupt: func(m *apinetmap.NetworkInfo) { setNetworkPrms(m, []byte("WithdrawFee"), make([]byte, 9)) }}, + corrupt: func(m *protonetmap.NetworkInfo) { setNetworkPrms(m, []byte("WithdrawFee"), make([]byte, 9)) }}, } { t.Run(tc.name, func(t *testing.T) { st := val - var m apinetmap.NetworkInfo - st.WriteToV2(&m) - tc.corrupt(&m) - require.EqualError(t, new(netmap.NetworkInfo).ReadFromV2(m), tc.err) + m := st.ProtoMessage() + tc.corrupt(m) + require.EqualError(t, new(netmap.NetworkInfo).FromProtoMessage(m), tc.err) }) } }) } -func TestNetworkInfo_WriteToV2(t *testing.T) { +func TestNetworkInfo_ProtoMessage(t *testing.T) { var val netmap.NetworkInfo - var m apinetmap.NetworkInfo // zero - val.WriteToV2(&m) + m := val.ProtoMessage() require.Zero(t, m.GetCurrentEpoch()) require.Zero(t, m.GetMagicNumber()) require.Zero(t, m.GetMsPerBlock()) require.Zero(t, m.GetNetworkConfig()) // filled - validNetworkInfo.WriteToV2(&m) + m = validNetworkInfo.ProtoMessage() require.Equal(t, anyValidCurrentEpoch, m.GetCurrentEpoch()) require.Equal(t, anyValidMagicNumber, m.GetMagicNumber()) require.Equal(t, anyValidMSPerBlock, m.GetMsPerBlock()) mc := m.GetNetworkConfig() require.NotNil(t, mc) - require.EqualValues(t, 14, mc.NumberOfParameters()) - var collected [][2][]byte - mc.IterateParameters(func(p *apinetmap.NetworkParameter) bool { - collected = append(collected, [2][]byte{p.GetKey(), p.GetValue()}) - return false - }) - require.Len(t, collected, 14) + require.Len(t, mc.Parameters, 14) for i, pair := range [][2]any{ {"k1", "v1"}, {"k2", "v2"}, @@ -404,8 +394,8 @@ func TestNetworkInfo_WriteToV2(t *testing.T) { {"MaxObjectSize", anyValidBinMaxObjectSize}, {"WithdrawFee", anyValidBinWithdrawalFee}, } { - require.EqualValues(t, pair[0], collected[i][0]) - require.EqualValues(t, pair[1], collected[i][1]) + require.EqualValues(t, pair[0], mc.Parameters[i].Key) + require.EqualValues(t, pair[1], mc.Parameters[i].Value) } } diff --git a/netmap/node_info.go b/netmap/node_info.go index d852cb88c..d501a696e 100644 --- a/netmap/node_info.go +++ b/netmap/node_info.go @@ -8,8 +8,11 @@ import ( "strings" "github.com/nspcc-dev/hrw/v2" - "github.com/nspcc-dev/neofs-api-go/v2/netmap" neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto" + neofsproto "github.com/nspcc-dev/neofs-sdk-go/internal/proto" + protonetmap "github.com/nspcc-dev/neofs-sdk-go/proto/netmap" + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/proto" ) // NodeInfo groups information about NeoFS storage node which is reflected @@ -18,18 +21,24 @@ import ( // about the nodes is available to all network participants to work with the network // map (mainly to comply with container storage policies). // -// NodeInfo is mutually compatible with github.com/nspcc-dev/neofs-api-go/v2/netmap.NodeInfo -// message. See ReadFromV2 / WriteToV2 methods. +// NodeInfo is mutually compatible with [protonetmap.NodeInfo] message. See +// [NodeInfo.FromProtoMessage] / [NodeInfo.ProtoMessage] methods. // // Instances can be created using built-in var declaration. type NodeInfo struct { - m netmap.NodeInfo + state protonetmap.NodeInfo_State + pub []byte + addrs []string + attrs [][2]string } // reads NodeInfo from netmap.NodeInfo message. If checkFieldPresence is set, // returns an error on absence of any protocol-required field. Verifies format of any // presented field according to NeoFS API V2 protocol. -func (x *NodeInfo) readFromV2(m netmap.NodeInfo, checkFieldPresence bool) error { +func (x *NodeInfo) fromProtoMessage(m *protonetmap.NodeInfo, checkFieldPresence bool) error { + if m.State < 0 { + return fmt.Errorf("negative state %d", m.State) + } var err error binPublicKey := m.GetPublicKey() @@ -37,13 +46,17 @@ func (x *NodeInfo) readFromV2(m netmap.NodeInfo, checkFieldPresence bool) error return errors.New("missing public key") } - if checkFieldPresence && m.NumberOfAddresses() <= 0 { + if checkFieldPresence && len(m.Addresses) == 0 { return errors.New("missing network endpoints") } attributes := m.GetAttributes() mAttr := make(map[string]struct{}, len(attributes)) + attrs := make([][2]string, len(attributes)) for i := range attributes { + if attributes[i] == nil { + return fmt.Errorf("nil attribute #%d", i) + } key := attributes[i].GetKey() if key == "" { return fmt.Errorf("empty key of the attribute #%d", i) @@ -51,46 +64,62 @@ func (x *NodeInfo) readFromV2(m netmap.NodeInfo, checkFieldPresence bool) error return fmt.Errorf("duplicated attribute %s", key) } + val := attributes[i].GetValue() switch { case key == attrCapacity: - _, err = strconv.ParseUint(attributes[i].GetValue(), 10, 64) + _, err = strconv.ParseUint(val, 10, 64) if err != nil { return fmt.Errorf("invalid %s attribute: %w", attrCapacity, err) } case key == attrPrice: var err error - _, err = strconv.ParseUint(attributes[i].GetValue(), 10, 64) + _, err = strconv.ParseUint(val, 10, 64) if err != nil { return fmt.Errorf("invalid %s attribute: %w", attrPrice, err) } default: - if attributes[i].GetValue() == "" { + if val == "" { return fmt.Errorf("empty value of the attribute %s", key) } } mAttr[key] = struct{}{} + attrs[i][0], attrs[i][1] = key, val } - x.m = m + x.state = m.State + x.pub = m.PublicKey + x.addrs = m.Addresses + x.attrs = attrs return nil } -// ReadFromV2 reads NodeInfo from the netmap.NodeInfo message. Checks if the -// message conforms to NeoFS API V2 protocol. +// FromProtoMessage validates m according to the NeoFS API protocol and restores +// x from it. // -// See also WriteToV2. -func (x *NodeInfo) ReadFromV2(m netmap.NodeInfo) error { - return x.readFromV2(m, true) +// See also [NodeInfo.ProtoMessage]. +func (x *NodeInfo) FromProtoMessage(m *protonetmap.NodeInfo) error { + return x.fromProtoMessage(m, true) } -// WriteToV2 writes NodeInfo to the netmap.NodeInfo message. The message MUST NOT -// be nil. +// ProtoMessage converts x into message to transmit using the NeoFS API +// protocol. // -// See also ReadFromV2. -func (x NodeInfo) WriteToV2(m *netmap.NodeInfo) { - *m = x.m +// See also [NodeInfo.FromProtoMessage]. +func (x NodeInfo) ProtoMessage() *protonetmap.NodeInfo { + m := &protonetmap.NodeInfo{ + PublicKey: x.pub, + Addresses: x.addrs, + State: x.state, + } + if len(x.attrs) > 0 { + m.Attributes = make([]*protonetmap.NodeInfo_Attribute, len(x.attrs)) + for i := range x.attrs { + m.Attributes[i] = &protonetmap.NodeInfo_Attribute{Key: x.attrs[i][0], Value: x.attrs[i][1]} + } + } + return m } // Marshal encodes NodeInfo into a binary format of the NeoFS API protocol @@ -98,10 +127,7 @@ func (x NodeInfo) WriteToV2(m *netmap.NodeInfo) { // // See also Unmarshal. func (x NodeInfo) Marshal() []byte { - var m netmap.NodeInfo - x.WriteToV2(&m) - - return m.StableMarshal(nil) + return neofsproto.MarshalMessage(x.ProtoMessage()) } // Unmarshal decodes NeoFS API protocol binary format into the NodeInfo @@ -110,14 +136,14 @@ func (x NodeInfo) Marshal() []byte { // // See also Marshal. func (x *NodeInfo) Unmarshal(data []byte) error { - var m netmap.NodeInfo + var m protonetmap.NodeInfo - err := m.Unmarshal(data) + err := proto.Unmarshal(data, &m) if err != nil { return err } - return x.readFromV2(m, false) + return x.fromProtoMessage(&m, false) } // MarshalJSON encodes NodeInfo into a JSON format of the NeoFS API protocol @@ -125,10 +151,7 @@ func (x *NodeInfo) Unmarshal(data []byte) error { // // See also UnmarshalJSON. func (x NodeInfo) MarshalJSON() ([]byte, error) { - var m netmap.NodeInfo - x.WriteToV2(&m) - - return m.MarshalJSON() + return neofsproto.MarshalMessageJSON(x.ProtoMessage()) } // UnmarshalJSON decodes NeoFS API protocol JSON format into the NodeInfo @@ -136,14 +159,14 @@ func (x NodeInfo) MarshalJSON() ([]byte, error) { // // See also MarshalJSON. func (x *NodeInfo) UnmarshalJSON(data []byte) error { - var m netmap.NodeInfo + var m protonetmap.NodeInfo - err := m.UnmarshalJSON(data) + err := protojson.Unmarshal(data, &m) if err != nil { return err } - return x.readFromV2(m, false) + return x.fromProtoMessage(&m, false) } // SetPublicKey sets binary-encoded public key bound to the node. The key @@ -155,7 +178,7 @@ func (x *NodeInfo) UnmarshalJSON(data []byte) error { // // See also [NodeInfo.PublicKey]. func (x *NodeInfo) SetPublicKey(key []byte) { - x.m.SetPublicKey(key) + x.pub = key } // PublicKey returns value set using [NodeInfo.SetPublicKey]. @@ -169,7 +192,7 @@ func (x *NodeInfo) SetPublicKey(key []byte) { // The value returned shares memory with the structure itself, so changing it can lead to data corruption. // Make a copy if you need to change it. func (x NodeInfo) PublicKey() []byte { - return x.m.GetPublicKey() + return x.pub } // StringifyPublicKey returns HEX representation of PublicKey. @@ -187,14 +210,14 @@ func StringifyPublicKey(node NodeInfo) string { // // See also IterateNetworkEndpoints. func (x *NodeInfo) SetNetworkEndpoints(v ...string) { - x.m.SetAddresses(v...) + x.addrs = v } // NumberOfNetworkEndpoints returns number of network endpoints announced by the node. // // See also SetNetworkEndpoints. func (x NodeInfo) NumberOfNetworkEndpoints() int { - return x.m.NumberOfAddresses() + return len(x.addrs) } // IterateNetworkEndpoints iterates over network endpoints announced by the @@ -206,7 +229,11 @@ func (x NodeInfo) NumberOfNetworkEndpoints() int { // // See also SetNetworkEndpoints. func (x NodeInfo) IterateNetworkEndpoints(f func(string) bool) { - x.m.IterateAddresses(f) + for i := range x.addrs { + if f(x.addrs[i]) { + return + } + } } // IterateNetworkEndpoints is an extra-sugared function over IterateNetworkEndpoints @@ -226,7 +253,7 @@ var _ hrw.Hashable = NodeInfo{} // Hash is needed to support weighted HRW therefore sort function sorts nodes // based on their public key. Hash isn't expected to be used directly. func (x NodeInfo) Hash() uint64 { - return hrw.Hash(x.m.GetPublicKey()) + return hrw.Hash(x.PublicKey()) } // less declares "less than" comparison between two NodeInfo instances: @@ -448,15 +475,14 @@ func (x NodeInfo) ExternalAddresses() []string { // // See also SetAttribute. func (x NodeInfo) NumberOfAttributes() int { - return len(x.m.GetAttributes()) + return len(x.attrs) } // IterateAttributes iterates over all node attributes and passes the into f. // Handler MUST NOT be nil. func (x NodeInfo) IterateAttributes(f func(key, value string)) { - a := x.m.GetAttributes() - for i := range a { - f(a[i].GetKey(), a[i].GetValue()) + for i := range x.attrs { + f(x.attrs[i][0], x.attrs[i][1]) } } @@ -465,11 +491,7 @@ func (x NodeInfo) IterateAttributes(f func(key, value string)) { // // See also Attribute, IterateAttributes. func (x NodeInfo) GetAttributes() [][2]string { - attrs := make([][2]string, len(x.m.GetAttributes())) - for i, attr := range x.m.GetAttributes() { - attrs[i] = [2]string{attr.GetKey(), attr.GetValue()} - } - return attrs + return x.attrs } // SetAttributes sets list of node attributes. @@ -478,7 +500,6 @@ func (x NodeInfo) GetAttributes() [][2]string { // // See also SetAttribute. func (x *NodeInfo) SetAttributes(attrs [][2]string) { - netmapAttrs := make([]netmap.Attribute, 0, len(attrs)) for _, attr := range attrs { if attr[0] == "" { panic("empty key in SetAttributes") @@ -486,13 +507,9 @@ func (x *NodeInfo) SetAttributes(attrs [][2]string) { if attr[1] == "" { panic(fmt.Errorf("empty value in SetAttributes for key: %s", attr[0])) } - - netmapAttrs = append(netmapAttrs, netmap.Attribute{}) - netmapAttrs[len(netmapAttrs)-1].SetKey(attr[0]) - netmapAttrs[len(netmapAttrs)-1].SetValue(attr[1]) } - x.m.SetAttributes(netmapAttrs) + x.attrs = attrs } // SetAttribute sets value of the node attribute value by the given key. @@ -504,28 +521,22 @@ func (x *NodeInfo) SetAttribute(key, value string) { panic("empty value in SetAttribute") } - a := x.m.GetAttributes() - for i := range a { - if a[i].GetKey() == key { - a[i].SetValue(value) + for i := range x.attrs { + if x.attrs[i][0] == key { + x.attrs[i][1] = value return } } - a = append(a, netmap.Attribute{}) - a[len(a)-1].SetKey(key) - a[len(a)-1].SetValue(value) - - x.m.SetAttributes(a) + x.attrs = append(x.attrs, [2]string{key, value}) } // Attribute returns value of the node attribute set using SetAttribute by the // given key. Returns empty string if attribute is missing. func (x NodeInfo) Attribute(key string) string { - a := x.m.GetAttributes() - for i := range a { - if a[i].GetKey() == key { - return a[i].GetValue() + for i := range x.attrs { + if x.attrs[i][0] == key { + return x.attrs[i][1] } } @@ -535,30 +546,27 @@ func (x NodeInfo) Attribute(key string) string { // SortAttributes sorts node attributes set using SetAttribute lexicographically. // The method is only needed to make NodeInfo consistent, e.g. for signing. func (x *NodeInfo) SortAttributes() { - as := x.m.GetAttributes() - if len(as) == 0 { + if len(x.attrs) == 0 { return } - sort.Slice(as, func(i, j int) bool { - switch strings.Compare(as[i].GetKey(), as[j].GetKey()) { + sort.Slice(x.attrs, func(i, j int) bool { + switch strings.Compare(x.attrs[i][0], x.attrs[j][0]) { case -1: return true case 1: return false default: - return as[i].GetValue() < as[j].GetValue() + return x.attrs[i][1] < x.attrs[j][1] } }) - - x.m.SetAttributes(as) } // SetOffline sets the state of the node to "offline". When a node updates // information about itself in the network map, this action is interpreted as // an intention to leave the network. func (x *NodeInfo) SetOffline() { - x.m.SetState(netmap.Offline) + x.state = protonetmap.NodeInfo_OFFLINE } // IsOffline checks if the node is in the "offline" state. @@ -568,7 +576,7 @@ func (x *NodeInfo) SetOffline() { // // See also SetOffline. func (x NodeInfo) IsOffline() bool { - return x.m.GetState() == netmap.Offline + return x.state == protonetmap.NodeInfo_OFFLINE } // SetOnline sets the state of the node to "online". When a node updates @@ -577,7 +585,7 @@ func (x NodeInfo) IsOffline() bool { // // See also IsOnline. func (x *NodeInfo) SetOnline() { - x.m.SetState(netmap.Online) + x.state = protonetmap.NodeInfo_ONLINE } // IsOnline checks if the node is in the "online" state. @@ -587,7 +595,7 @@ func (x *NodeInfo) SetOnline() { // // See also SetOnline. func (x NodeInfo) IsOnline() bool { - return x.m.GetState() == netmap.Online + return x.state == protonetmap.NodeInfo_ONLINE } // SetMaintenance sets the state of the node to "maintenance". When a node updates @@ -596,7 +604,7 @@ func (x NodeInfo) IsOnline() bool { // // See also IsMaintenance. func (x *NodeInfo) SetMaintenance() { - x.m.SetState(netmap.Maintenance) + x.state = protonetmap.NodeInfo_MAINTENANCE } // IsMaintenance checks if the node is in the "maintenance" state. @@ -605,7 +613,7 @@ func (x *NodeInfo) SetMaintenance() { // // See also SetMaintenance. func (x NodeInfo) IsMaintenance() bool { - return x.m.GetState() == netmap.Maintenance + return x.state == protonetmap.NodeInfo_MAINTENANCE } const attrVerifiedNodesDomain = "VerifiedNodesDomain" diff --git a/netmap/node_info_test.go b/netmap/node_info_test.go index 39c70e1f8..9218742f1 100644 --- a/netmap/node_info_test.go +++ b/netmap/node_info_test.go @@ -5,8 +5,8 @@ import ( "strings" "testing" - apinetmap "github.com/nspcc-dev/neofs-api-go/v2/netmap" "github.com/nspcc-dev/neofs-sdk-go/netmap" + protonetmap "github.com/nspcc-dev/neofs-sdk-go/proto/netmap" "github.com/stretchr/testify/require" ) @@ -488,48 +488,41 @@ func TestNodeInfo_SetVerifiedNodesDomain(t *testing.T) { require.Equal(t, anyValidVerifiedNodesDomain, n.VerifiedNodesDomain()) } -func setNodeAttributes(ni *apinetmap.NodeInfo, els ...string) { +func setNodeAttributes(ni *protonetmap.NodeInfo, els ...string) { if len(els)%2 != 0 { panic("must be even") } - mas := make([]apinetmap.Attribute, len(els)/2) + ni.Attributes = make([]*protonetmap.NodeInfo_Attribute, len(els)/2) for i := range len(els) / 2 { - mas[i].SetKey(els[2*i]) - mas[i].SetValue(els[2*i+1]) + ni.Attributes[i] = &protonetmap.NodeInfo_Attribute{Key: els[2*i], Value: els[2*i+1]} } - ni.SetAttributes(mas) } -func TestNodeInfo_ReadFromV2(t *testing.T) { - var mas []apinetmap.Attribute - addAttr := func(k, v string) { - var a apinetmap.Attribute - a.SetKey(k) - a.SetValue(v) - mas = append(mas, a) +func TestNodeInfo_FromProtoMessage(t *testing.T) { + m := &protonetmap.NodeInfo{ + PublicKey: anyValidPublicKey, + Addresses: anyValidNetworkEndpoints, + Attributes: []*protonetmap.NodeInfo_Attribute{ + {Key: "k1", Value: "v1"}, + {Key: "k2", Value: "v2"}, + {Key: "Capacity", Value: "9010937245406684209"}, + {Key: "Price", Value: "10993309018040354285"}, + {Key: "UN-LOCODE", Value: anyValidLOCODE}, + {Key: "CountryCode", Value: anyValidCountryCode}, + {Key: "Country", Value: anyValidCountryName}, + {Key: "Location", Value: anyValidLocationName}, + {Key: "SubDivCode", Value: anyValidSubdivCode}, + {Key: "SubDiv", Value: anyValidSubdivName}, + {Key: "SubDivName", Value: anyValidSubdivName}, + {Key: "Continent", Value: anyValidContinentName}, + {Key: "ExternalAddr", Value: strings.Join(anyValidExternalNetworkEndpoints, ",")}, + {Key: "Version", Value: anyValidNodeVersion}, + {Key: "VerifiedNodesDomain", Value: anyValidVerifiedNodesDomain}, + }, } - addAttr("k1", "v1") - addAttr("k2", "v2") - addAttr("Capacity", "9010937245406684209") - addAttr("Price", "10993309018040354285") - addAttr("UN-LOCODE", anyValidLOCODE) - addAttr("CountryCode", anyValidCountryCode) - addAttr("Country", anyValidCountryName) - addAttr("Location", anyValidLocationName) - addAttr("SubDivCode", anyValidSubdivCode) - addAttr("SubDiv", anyValidSubdivName) - addAttr("SubDivName", anyValidSubdivName) - addAttr("Continent", anyValidContinentName) - addAttr("ExternalAddr", strings.Join(anyValidExternalNetworkEndpoints, ",")) - addAttr("Version", anyValidNodeVersion) - addAttr("VerifiedNodesDomain", anyValidVerifiedNodesDomain) - var m apinetmap.NodeInfo - m.SetPublicKey(anyValidPublicKey) - m.SetAddresses(anyValidNetworkEndpoints...) - m.SetAttributes(mas) var val netmap.NodeInfo - require.NoError(t, val.ReadFromV2(m)) + require.NoError(t, val.FromProtoMessage(m)) require.Equal(t, anyValidPublicKey, val.PublicKey()) var i int val.IterateNetworkEndpoints(func(el string) bool { @@ -562,23 +555,23 @@ func TestNodeInfo_ReadFromV2(t *testing.T) { require.Equal(t, anyValidVerifiedNodesDomain, val.VerifiedNodesDomain()) for _, tc := range []struct { - st apinetmap.NodeState + st protonetmap.NodeInfo_State check func(netmap.NodeInfo) bool }{ - {st: apinetmap.Online, check: netmap.NodeInfo.IsOnline}, - {st: apinetmap.Offline, check: netmap.NodeInfo.IsOffline}, - {st: apinetmap.Maintenance, check: netmap.NodeInfo.IsMaintenance}, + {st: protonetmap.NodeInfo_ONLINE, check: netmap.NodeInfo.IsOnline}, + {st: protonetmap.NodeInfo_OFFLINE, check: netmap.NodeInfo.IsOffline}, + {st: protonetmap.NodeInfo_MAINTENANCE, check: netmap.NodeInfo.IsMaintenance}, } { - m.SetState(tc.st) - require.NoError(t, val.ReadFromV2(m), tc.st) + m.State = tc.st + require.NoError(t, val.FromProtoMessage(m), tc.st) require.True(t, tc.check(val)) } // reset optional fields - m.SetAttributes(nil) - m.SetState(0) + m.Attributes = nil + m.State = 0 val2 := val - require.NoError(t, val2.ReadFromV2(m)) + require.NoError(t, val2.FromProtoMessage(m)) require.Zero(t, val2.NumberOfAttributes()) val2.IterateAttributes(func(string, string) { t.Fatal("handler must not be called") @@ -594,57 +587,56 @@ func TestNodeInfo_ReadFromV2(t *testing.T) { t.Run("invalid", func(t *testing.T) { for _, tc := range []struct { name, err string - corrupt func(info *apinetmap.NodeInfo) + corrupt func(info *protonetmap.NodeInfo) }{ {name: "public key/nil", err: "missing public key", - corrupt: func(m *apinetmap.NodeInfo) { m.SetPublicKey(nil) }}, + corrupt: func(m *protonetmap.NodeInfo) { m.PublicKey = nil }}, {name: "public key/empty", err: "missing public key", - corrupt: func(m *apinetmap.NodeInfo) { m.SetPublicKey([]byte{}) }}, + corrupt: func(m *protonetmap.NodeInfo) { m.PublicKey = []byte{} }}, + {name: "endpoints/nil", err: "missing network endpoints", + corrupt: func(m *protonetmap.NodeInfo) { m.Addresses = nil }}, {name: "endpoints/empty", err: "missing network endpoints", - corrupt: func(m *apinetmap.NodeInfo) { m.SetAddresses() }}, + corrupt: func(m *protonetmap.NodeInfo) { m.Addresses = []string{} }}, + {name: "attributes/nil", err: "nil attribute #1", + corrupt: func(m *protonetmap.NodeInfo) { m.Attributes[1] = nil }}, {name: "attributes/no key", err: "empty key of the attribute #1", - corrupt: func(m *apinetmap.NodeInfo) { setNodeAttributes(m, "k1", "v1", "", "v2") }}, + corrupt: func(m *protonetmap.NodeInfo) { setNodeAttributes(m, "k1", "v1", "", "v2") }}, {name: "attributes/no value", err: "empty value of the attribute k2", - corrupt: func(m *apinetmap.NodeInfo) { setNodeAttributes(m, "k1", "v1", "k2", "") }}, + corrupt: func(m *protonetmap.NodeInfo) { setNodeAttributes(m, "k1", "v1", "k2", "") }}, {name: "attributes/duplicated", err: "duplicated attribute k1", - corrupt: func(m *apinetmap.NodeInfo) { setNodeAttributes(m, "k1", "v1", "k2", "v2", "k1", "v3") }}, + corrupt: func(m *protonetmap.NodeInfo) { setNodeAttributes(m, "k1", "v1", "k2", "v2", "k1", "v3") }}, {name: "attributes/capacity", err: "invalid Capacity attribute: strconv.ParseUint: parsing \"foo\": invalid syntax", - corrupt: func(m *apinetmap.NodeInfo) { setNodeAttributes(m, "Capacity", "foo") }}, + corrupt: func(m *protonetmap.NodeInfo) { setNodeAttributes(m, "Capacity", "foo") }}, {name: "attributes/price", err: "invalid Price attribute: strconv.ParseUint: parsing \"foo\": invalid syntax", - corrupt: func(m *apinetmap.NodeInfo) { setNodeAttributes(m, "Price", "foo") }}, + corrupt: func(m *protonetmap.NodeInfo) { setNodeAttributes(m, "Price", "foo") }}, + {name: "state/negative", err: "negative state -1", + corrupt: func(m *protonetmap.NodeInfo) { m.State = -1 }}, } { t.Run(tc.name, func(t *testing.T) { st := val - var m apinetmap.NodeInfo - st.WriteToV2(&m) - tc.corrupt(&m) - require.EqualError(t, new(netmap.NodeInfo).ReadFromV2(m), tc.err) + m := st.ProtoMessage() + tc.corrupt(m) + require.EqualError(t, new(netmap.NodeInfo).FromProtoMessage(m), tc.err) }) } }) } -func TestNodeInfo_WriteToV2(t *testing.T) { +func TestNodeInfo_ProtoMessage(t *testing.T) { var val netmap.NodeInfo - var m apinetmap.NodeInfo // zero - val.WriteToV2(&m) + m := val.ProtoMessage() require.Zero(t, m.GetPublicKey()) - require.Zero(t, m.NumberOfAddresses()) - m.IterateAddresses(func(string) bool { t.Fatal("handler must not be called"); return false }) + require.Zero(t, m.GetAddresses()) require.Zero(t, m.GetAttributes()) require.Zero(t, m.GetState()) // filled - validNodeInfo.WriteToV2(&m) + m = validNodeInfo.ProtoMessage() require.Equal(t, anyValidPublicKey, m.GetPublicKey()) - require.EqualValues(t, 3, m.NumberOfAddresses()) - var collected []string - m.IterateAddresses(func(el string) bool { - collected = append(collected, el) - return false - }) + require.Equal(t, anyValidNetworkEndpoints, m.GetAddresses()) + mas := m.GetAttributes() require.Len(t, mas, 14) for i, pair := range [][2]string{ @@ -670,15 +662,15 @@ func TestNodeInfo_WriteToV2(t *testing.T) { for _, tc := range []struct { setState func(*netmap.NodeInfo) - exp apinetmap.NodeState + exp protonetmap.NodeInfo_State }{ - {setState: (*netmap.NodeInfo).SetOnline, exp: apinetmap.Online}, - {setState: (*netmap.NodeInfo).SetOffline, exp: apinetmap.Offline}, - {setState: (*netmap.NodeInfo).SetMaintenance, exp: apinetmap.Maintenance}, + {setState: (*netmap.NodeInfo).SetOnline, exp: protonetmap.NodeInfo_ONLINE}, + {setState: (*netmap.NodeInfo).SetOffline, exp: protonetmap.NodeInfo_OFFLINE}, + {setState: (*netmap.NodeInfo).SetMaintenance, exp: protonetmap.NodeInfo_MAINTENANCE}, } { val2 := validNodeInfo tc.setState(&val2) - val2.WriteToV2(&m) + m := val2.ProtoMessage() require.Equal(t, tc.exp, m.GetState(), tc.exp) } } diff --git a/netmap/policy.go b/netmap/policy.go index a2e6b7732..dcd8b2707 100644 --- a/netmap/policy.go +++ b/netmap/policy.go @@ -6,33 +6,35 @@ import ( "io" "slices" "strconv" - "strings" "github.com/antlr4-go/antlr/v4" - "github.com/nspcc-dev/neofs-api-go/v2/netmap" + neofsproto "github.com/nspcc-dev/neofs-sdk-go/internal/proto" "github.com/nspcc-dev/neofs-sdk-go/netmap/parser" + protonetmap "github.com/nspcc-dev/neofs-sdk-go/proto/netmap" + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/proto" ) // PlacementPolicy declares policy to store objects in the NeoFS container. // Within itself, PlacementPolicy represents a set of rules to select a subset // of nodes from NeoFS network map - node-candidates for object storage. // -// PlacementPolicy is mutually compatible with github.com/nspcc-dev/neofs-api-go/v2/netmap.PlacementPolicy -// message. See ReadFromV2 / WriteToV2 methods. +// PlacementPolicy is mutually compatible with [protonetmap.PlacementPolicy] +// message. See [PlacementPolicy.FromProtoMessage] / [PlacementPolicy.ProtoMessage] methods. // // Instances can be created using built-in var declaration. type PlacementPolicy struct { backupFactor uint32 - filters []netmap.Filter + filters []Filter - selectors []netmap.Selector + selectors []Selector - replicas []netmap.Replica + replicas []ReplicaDescriptor } // FilterOp defines the matching property. -type FilterOp uint32 +type FilterOp int32 // Supported FilterOp values. const ( @@ -71,24 +73,15 @@ func (x FilterOp) String() string { } } -func copyFilter(f netmap.Filter) netmap.Filter { - var filter netmap.Filter +func copyFilter(f Filter) Filter { + filter := f - filter.SetName(f.GetName()) - filter.SetKey(f.GetKey()) - filter.SetOp(f.GetOp()) - filter.SetValue(f.GetValue()) + if len(f.subs) > 0 { + filter.subs = make([]Filter, len(f.subs)) - if f.GetFilters() != nil { - filters := make([]netmap.Filter, len(f.GetFilters())) - - for i, internalFilter := range f.GetFilters() { - filters[i] = copyFilter(internalFilter) + for i := range f.subs { + filter.subs[i] = copyFilter(f.subs[i]) } - - filter.SetFilters(filters) - } else { - filter.SetFilters(nil) } return filter @@ -98,27 +91,52 @@ func copyFilter(f netmap.Filter) netmap.Filter { func (p PlacementPolicy) CopyTo(dst *PlacementPolicy) { dst.SetContainerBackupFactor(p.backupFactor) - dst.filters = make([]netmap.Filter, len(p.filters)) - for i, f := range p.filters { - dst.filters[i] = copyFilter(f) + dst.filters = make([]Filter, len(p.filters)) + for i := range p.filters { + dst.filters[i] = copyFilter(p.filters[i]) } - // netmap.Selector is a struct with simple types, no links inside. Just create a new slice and copy all items inside. + // protonetmap.Selector is a struct with simple types, no links inside. Just create a new slice and copy all items inside. dst.selectors = slices.Clone(p.selectors) - // netmap.Replica is a struct with simple types, no links inside. Just create a new slice and copy all items inside. + // protonetmap.Replica is a struct with simple types, no links inside. Just create a new slice and copy all items inside. dst.replicas = slices.Clone(p.replicas) } -func (p *PlacementPolicy) readFromV2(m netmap.PlacementPolicy, checkFieldPresence bool) error { - p.replicas = m.GetReplicas() - if checkFieldPresence && len(p.replicas) == 0 { +func (p *PlacementPolicy) fromProtoMessage(m *protonetmap.PlacementPolicy, checkFieldPresence bool) error { + if checkFieldPresence && len(m.Replicas) == 0 { return errors.New("missing replicas") } + p.replicas = make([]ReplicaDescriptor, len(m.Replicas)) + for i, r := range m.Replicas { + if r == nil { + return fmt.Errorf("nil replica #%d", i) + } + p.replicas[i].fromProtoMessage(r) + } + + p.selectors = make([]Selector, len(m.Selectors)) + for i, s := range m.Selectors { + if s == nil { + return fmt.Errorf("nil selector #%d", i) + } + if err := p.selectors[i].fromProtoMessage(s); err != nil { + return fmt.Errorf("invalid selector #%d: %w", i, err) + } + } + + p.filters = make([]Filter, len(m.Filters)) + for i, f := range m.Filters { + if f == nil { + return fmt.Errorf("nil filter #%d", i) + } + if err := p.filters[i].fromProtoMessage(f); err != nil { + return fmt.Errorf("invalid filter #%d: %w", i, err) + } + } + p.backupFactor = m.GetContainerBackupFactor() - p.selectors = m.GetSelectors() - p.filters = m.GetFilters() return nil } @@ -128,10 +146,7 @@ func (p *PlacementPolicy) readFromV2(m netmap.PlacementPolicy, checkFieldPresenc // // See also Unmarshal. func (p PlacementPolicy) Marshal() []byte { - var m netmap.PlacementPolicy - p.WriteToV2(&m) - - return m.StableMarshal(nil) + return neofsproto.MarshalMessage(p.ProtoMessage()) } // Unmarshal decodes NeoFS API protocol binary format into the PlacementPolicy @@ -140,14 +155,14 @@ func (p PlacementPolicy) Marshal() []byte { // // See also Marshal. func (p *PlacementPolicy) Unmarshal(data []byte) error { - var m netmap.PlacementPolicy + var m protonetmap.PlacementPolicy - err := m.Unmarshal(data) + err := proto.Unmarshal(data, &m) if err != nil { return err } - return p.readFromV2(m, false) + return p.fromProtoMessage(&m, false) } // MarshalJSON encodes PlacementPolicy into a JSON format of the NeoFS API @@ -155,10 +170,7 @@ func (p *PlacementPolicy) Unmarshal(data []byte) error { // // See also UnmarshalJSON. func (p PlacementPolicy) MarshalJSON() ([]byte, error) { - var m netmap.PlacementPolicy - p.WriteToV2(&m) - - return m.MarshalJSON() + return neofsproto.MarshalMessageJSON(p.ProtoMessage()) } // UnmarshalJSON decodes NeoFS API protocol JSON format into the PlacementPolicy @@ -166,51 +178,86 @@ func (p PlacementPolicy) MarshalJSON() ([]byte, error) { // // See also MarshalJSON. func (p *PlacementPolicy) UnmarshalJSON(data []byte) error { - var m netmap.PlacementPolicy + var m protonetmap.PlacementPolicy - err := m.UnmarshalJSON(data) + err := protojson.Unmarshal(data, &m) if err != nil { return err } - return p.readFromV2(m, false) + return p.fromProtoMessage(&m, false) } -// ReadFromV2 reads PlacementPolicy from the netmap.PlacementPolicy message. -// Checks if the message conforms to NeoFS API V2 protocol. +// FromProtoMessage validates m according to the NeoFS API protocol and restores +// p from it. // -// See also WriteToV2. -func (p *PlacementPolicy) ReadFromV2(m netmap.PlacementPolicy) error { - return p.readFromV2(m, true) +// See also [PlacementPolicy.ProtoMessage]. +func (p *PlacementPolicy) FromProtoMessage(m *protonetmap.PlacementPolicy) error { + return p.fromProtoMessage(m, true) } -// WriteToV2 writes PlacementPolicy to the netmap.PlacementPolicy message. -// The message must not be nil. +// ProtoMessage converts p into message to transmit using the NeoFS API +// protocol. // -// See also ReadFromV2. -func (p PlacementPolicy) WriteToV2(m *netmap.PlacementPolicy) { - m.SetContainerBackupFactor(p.backupFactor) - m.SetFilters(p.filters) - m.SetSelectors(p.selectors) - m.SetReplicas(p.replicas) +// See also [PlacementPolicy.FromProtoMessage]. +func (p PlacementPolicy) ProtoMessage() *protonetmap.PlacementPolicy { + m := &protonetmap.PlacementPolicy{ + ContainerBackupFactor: p.backupFactor, + } + if len(p.replicas) > 0 { + m.Replicas = make([]*protonetmap.Replica, len(p.replicas)) + for i := range p.replicas { + m.Replicas[i] = p.replicas[i].protoMessage() + } + } + if len(p.selectors) > 0 { + m.Selectors = make([]*protonetmap.Selector, len(p.selectors)) + for i := range p.selectors { + m.Selectors[i] = p.selectors[i].protoMessage() + } + } + if len(p.filters) > 0 { + m.Filters = make([]*protonetmap.Filter, len(p.filters)) + for i := range p.filters { + m.Filters[i] = p.filters[i].protoMessage() + } + } + return m } // ReplicaDescriptor replica descriptor characterizes replicas of objects from // the subset selected by a particular Selector. type ReplicaDescriptor struct { - m netmap.Replica + count uint32 + selector string +} + +// fromProtoMessage validates m according to the NeoFS API protocol and restores +// r from it. +func (r *ReplicaDescriptor) fromProtoMessage(m *protonetmap.Replica) { + r.count = m.Count + r.selector = m.Selector +} + +// protoMessage converts r into message to transmit using the NeoFS API +// protocol. +func (r ReplicaDescriptor) protoMessage() *protonetmap.Replica { + return &protonetmap.Replica{ + Count: r.count, + Selector: r.selector, + } } // SetNumberOfObjects sets number of object replicas. func (r *ReplicaDescriptor) SetNumberOfObjects(c uint32) { - r.m.SetCount(c) + r.count = c } // NumberOfObjects returns number set using SetNumberOfObjects. // // Zero ReplicaDescriptor has zero number of objects. func (r ReplicaDescriptor) NumberOfObjects() uint32 { - return r.m.GetCount() + return r.count } // SetSelectorName sets name of the related Selector. @@ -220,7 +267,7 @@ func (r ReplicaDescriptor) NumberOfObjects() uint32 { // // See also [ReplicaDescriptor.SelectorName]. func (r *ReplicaDescriptor) SetSelectorName(s string) { - r.m.SetSelector(s) + r.selector = s } // SelectorName returns name of the related Selector. @@ -230,7 +277,7 @@ func (r *ReplicaDescriptor) SetSelectorName(s string) { // // See also [ReplicaDescriptor.SetSelectorName]. func (r ReplicaDescriptor) SelectorName() string { - return r.m.GetSelector() + return r.selector } // SetReplicas sets list of object replica's characteristics. @@ -238,11 +285,7 @@ func (r ReplicaDescriptor) SelectorName() string { // See also [PlacementPolicy.Replicas], [PlacementPolicy.NumberOfReplicas], // [PlacementPolicy.ReplicaNumberByIndex]. func (p *PlacementPolicy) SetReplicas(rs []ReplicaDescriptor) { - p.replicas = make([]netmap.Replica, len(rs)) - - for i := range rs { - p.replicas[i] = rs[i].m - } + p.replicas = rs } // Replicas returns list of object replica characteristics. @@ -250,11 +293,7 @@ func (p *PlacementPolicy) SetReplicas(rs []ReplicaDescriptor) { // See also [PlacementPolicy.SetReplicas], [PlacementPolicy.NumberOfReplicas], // [PlacementPolicy.ReplicaNumberByIndex]. func (p PlacementPolicy) Replicas() []ReplicaDescriptor { - rs := make([]ReplicaDescriptor, len(p.replicas)) - for i := range p.replicas { - rs[i].m = p.replicas[i] - } - return rs + return p.replicas } // NumberOfReplicas returns number of replica descriptors set using SetReplicas. @@ -270,7 +309,7 @@ func (p PlacementPolicy) NumberOfReplicas() int { // // Zero PlacementPolicy has no replicas. func (p PlacementPolicy) ReplicaNumberByIndex(i int) uint32 { - return p.replicas[i].GetCount() + return p.replicas[i].NumberOfObjects() } // SetContainerBackupFactor sets container backup factor: it controls how deep @@ -296,14 +335,44 @@ func (p *PlacementPolicy) ContainerBackupFactor() uint32 { // Selector describes the bucket selection operator: choose a number of nodes // from the bucket taking the nearest nodes to the related container by hash distance. type Selector struct { - m netmap.Selector + name string + count uint32 + clause protonetmap.Clause + attr string + filter string +} + +// fromProtoMessage validates m according to the NeoFS API protocol and restores +// s from it. +func (s *Selector) fromProtoMessage(m *protonetmap.Selector) error { + if m.Clause < 0 { + return fmt.Errorf("negative clause %d", m.Clause) + } + s.name = m.Name + s.count = m.Count + s.clause = m.Clause + s.attr = m.Attribute + s.filter = m.Filter + return nil +} + +// protoMessage converts s into message to transmit using the NeoFS API +// protocol. +func (s Selector) protoMessage() *protonetmap.Selector { + return &protonetmap.Selector{ + Name: s.name, + Count: s.count, + Clause: s.clause, + Attribute: s.attr, + Filter: s.filter, + } } // SetName sets name with which the Selector can be referenced. // // Zero Selector is unnamed. func (s *Selector) SetName(name string) { - s.m.SetName(name) + s.name = name } // Name returns name with which the Selector can be referenced. @@ -312,7 +381,7 @@ func (s *Selector) SetName(name string) { // // See also [Selector.Name]. func (s Selector) Name() string { - return s.m.GetName() + return s.name } // SetNumberOfNodes sets number of nodes to select from the bucket. @@ -321,7 +390,7 @@ func (s Selector) Name() string { // // See also [Selector.NumberOfNodes]. func (s *Selector) SetNumberOfNodes(num uint32) { - s.m.SetCount(num) + s.count = num } // NumberOfNodes returns number of nodes to select from the bucket. @@ -330,7 +399,7 @@ func (s *Selector) SetNumberOfNodes(num uint32) { // // See also [Selector.SetNumberOfNodes]. func (s Selector) NumberOfNodes() uint32 { - return s.m.GetCount() + return s.count } // SelectByBucketAttribute sets attribute of the bucket to select nodes from. @@ -339,7 +408,7 @@ func (s Selector) NumberOfNodes() uint32 { // // See also [Selector.BucketAttribute]. func (s *Selector) SelectByBucketAttribute(bucket string) { - s.m.SetAttribute(bucket) + s.attr = bucket } // BucketAttribute returns attribute of the bucket to select nodes from. @@ -348,7 +417,7 @@ func (s *Selector) SelectByBucketAttribute(bucket string) { // // See also [Selector.SelectByBucketAttribute]. func (s *Selector) BucketAttribute() string { - return s.m.GetAttribute() + return s.attr } // SelectSame makes selection algorithm to select only nodes having the same values @@ -358,7 +427,7 @@ func (s *Selector) BucketAttribute() string { // // See also [Selector.SelectByBucketAttribute], [Selector.IsSame]. func (s *Selector) SelectSame() { - s.m.SetClause(netmap.Same) + s.clause = protonetmap.Clause_SAME } // IsSame checks whether selection algorithm is set to select only nodes having @@ -366,7 +435,7 @@ func (s *Selector) SelectSame() { // // See also [Selector.SelectSame]. func (s *Selector) IsSame() bool { - return s.m.GetClause() == netmap.Same + return s.clause == protonetmap.Clause_SAME } // SelectDistinct makes selection algorithm to select only nodes having the different values @@ -376,7 +445,7 @@ func (s *Selector) IsSame() bool { // // See also [Selector.SelectByBucketAttribute], [Selector.IsDistinct]. func (s *Selector) SelectDistinct() { - s.m.SetClause(netmap.Distinct) + s.clause = protonetmap.Clause_DISTINCT } // IsDistinct checks whether selection algorithm is set to select only nodes @@ -384,7 +453,7 @@ func (s *Selector) SelectDistinct() { // // See also [Selector.SelectByBucketAttribute], [Selector.SelectDistinct]. func (s *Selector) IsDistinct() bool { - return s.m.GetClause() == netmap.Distinct + return s.clause == protonetmap.Clause_DISTINCT } // SetFilterName sets reference to pre-filtering nodes for selection. @@ -393,7 +462,7 @@ func (s *Selector) IsDistinct() bool { // // See also Filter.SetName. func (s *Selector) SetFilterName(f string) { - s.m.SetFilter(f) + s.filter = f } // FilterName returns reference to pre-filtering nodes for selection. @@ -402,7 +471,7 @@ func (s *Selector) SetFilterName(f string) { // // See also [Filter.SetName], [Selector.SetFilterName]. func (s *Selector) FilterName() string { - return s.m.GetFilter() + return s.filter } // SetSelectors sets list of Selector to form the subset of the nodes to store @@ -412,11 +481,7 @@ func (s *Selector) FilterName() string { // // See also [PlacementPolicy.Selectors]. func (p *PlacementPolicy) SetSelectors(ss []Selector) { - p.selectors = make([]netmap.Selector, len(ss)) - - for i := range ss { - p.selectors[i] = ss[i].m - } + p.selectors = ss } // Selectors returns list of Selector to form the subset of the nodes to store @@ -426,16 +491,55 @@ func (p *PlacementPolicy) SetSelectors(ss []Selector) { // // See also [PlacementPolicy.SetSelectors]. func (p PlacementPolicy) Selectors() []Selector { - ss := make([]Selector, len(p.selectors)) - for i := range p.selectors { - ss[i].m = p.selectors[i] - } - return ss + return p.selectors } // Filter contains rules for filtering the node sets. type Filter struct { - m netmap.Filter + name string + key string + op FilterOp + val string + subs []Filter +} + +// fromProtoMessage validates m according to the NeoFS API protocol and restores +// x from it. +func (x *Filter) fromProtoMessage(m *protonetmap.Filter) error { + if m.Op < 0 { + return fmt.Errorf("negative op %d", m.Op) + } + var subs []Filter + if len(m.Filters) > 0 { + subs = make([]Filter, len(m.Filters)) + for i := range m.Filters { + if err := subs[i].fromProtoMessage(m.Filters[i]); err != nil { + return fmt.Errorf("invalid sub-filter #%d: %w", i, err) + } + } + } + x.name = m.Name + x.setAttribute(m.Key, FilterOp(m.Op), m.Value) + x.subs = subs + return nil +} + +// protoMessage converts x into message to transmit using the NeoFS API +// protocol. +func (x Filter) protoMessage() *protonetmap.Filter { + m := &protonetmap.Filter{ + Name: x.name, + Key: x.key, + Op: protonetmap.Operation(x.op), + Value: x.val, + } + if len(x.subs) > 0 { + m.Filters = make([]*protonetmap.Filter, len(x.subs)) + for i := range x.subs { + m.Filters[i] = x.subs[i].protoMessage() + } + } + return m } // SetName sets name with which the Filter can be referenced or, for inner filters, @@ -446,7 +550,7 @@ type Filter struct { // // See also [Filter.Name]. func (x *Filter) SetName(name string) { - x.m.SetName(name) + x.name = name } // Name returns name with which the Filter can be referenced or, for inner @@ -457,57 +561,47 @@ func (x *Filter) SetName(name string) { // // See also [Filter.SetName]. func (x Filter) Name() string { - return x.m.GetName() + return x.name } // Key returns key to the property. func (x Filter) Key() string { - return x.m.GetKey() + return x.key } // Op returns operator to match the property. func (x Filter) Op() FilterOp { - return FilterOp(x.m.GetOp()) + return x.op } // Value returns value to check the property against. func (x Filter) Value() string { - return x.m.GetValue() + return x.val } // SubFilters returns list of sub-filters when Filter is complex. func (x Filter) SubFilters() []Filter { - fsm := x.m.GetFilters() - if len(fsm) == 0 { - return nil - } - - fs := make([]Filter, len(fsm)) - for i := range fsm { - fs[i] = Filter{m: fsm[i]} - } - - return fs + return x.subs } -func (x *Filter) setAttribute(key string, op netmap.Operation, val string) { - x.m.SetKey(key) - x.m.SetOp(op) - x.m.SetValue(val) +func (x *Filter) setAttribute(key string, op FilterOp, val string) { + x.key = key + x.op = op + x.val = val } // Equal applies the rule to accept only nodes with the same attribute value. // // Method SHOULD NOT be called along with other similar methods. func (x *Filter) Equal(key, value string) { - x.setAttribute(key, netmap.EQ, value) + x.setAttribute(key, FilterOpEQ, value) } // NotEqual applies the rule to accept only nodes with the distinct attribute value. // // Method SHOULD NOT be called along with other similar methods. func (x *Filter) NotEqual(key, value string) { - x.setAttribute(key, netmap.NE, value) + x.setAttribute(key, FilterOpNE, value) } // NumericGT applies the rule to accept only nodes with the numeric attribute @@ -515,7 +609,7 @@ func (x *Filter) NotEqual(key, value string) { // // Method SHOULD NOT be called along with other similar methods. func (x *Filter) NumericGT(key string, num int64) { - x.setAttribute(key, netmap.GT, strconv.FormatInt(num, 10)) + x.setAttribute(key, FilterOpGT, strconv.FormatInt(num, 10)) } // NumericGE applies the rule to accept only nodes with the numeric attribute @@ -523,7 +617,7 @@ func (x *Filter) NumericGT(key string, num int64) { // // Method SHOULD NOT be called along with other similar methods. func (x *Filter) NumericGE(key string, num int64) { - x.setAttribute(key, netmap.GE, strconv.FormatInt(num, 10)) + x.setAttribute(key, FilterOpGE, strconv.FormatInt(num, 10)) } // NumericLT applies the rule to accept only nodes with the numeric attribute @@ -531,7 +625,7 @@ func (x *Filter) NumericGE(key string, num int64) { // // Method SHOULD NOT be called along with other similar methods. func (x *Filter) NumericLT(key string, num int64) { - x.setAttribute(key, netmap.LT, strconv.FormatInt(num, 10)) + x.setAttribute(key, FilterOpLT, strconv.FormatInt(num, 10)) } // NumericLE applies the rule to accept only nodes with the numeric attribute @@ -539,22 +633,12 @@ func (x *Filter) NumericLT(key string, num int64) { // // Method SHOULD NOT be called along with other similar methods. func (x *Filter) NumericLE(key string, num int64) { - x.setAttribute(key, netmap.LE, strconv.FormatInt(num, 10)) + x.setAttribute(key, FilterOpLE, strconv.FormatInt(num, 10)) } -func (x *Filter) setInnerFilters(op netmap.Operation, filters []Filter) { +func (x *Filter) setInnerFilters(op FilterOp, filters []Filter) { x.setAttribute("", op, "") - - inner := x.m.GetFilters() - if rem := len(filters) - len(inner); rem > 0 { - inner = append(inner, make([]netmap.Filter, rem)...) - } - - for i := range filters { - inner[i] = filters[i].m - } - - x.m.SetFilters(inner) + x.subs = filters } // LogicalOR applies the rule to accept only nodes which satisfy at least one @@ -562,7 +646,7 @@ func (x *Filter) setInnerFilters(op netmap.Operation, filters []Filter) { // // Method SHOULD NOT be called along with other similar methods. func (x *Filter) LogicalOR(filters ...Filter) { - x.setInnerFilters(netmap.OR, filters) + x.setInnerFilters(FilterOpOR, filters) } // LogicalAND applies the rule to accept only nodes which satisfy all the given @@ -570,7 +654,7 @@ func (x *Filter) LogicalOR(filters ...Filter) { // // Method SHOULD NOT be called along with other similar methods. func (x *Filter) LogicalAND(filters ...Filter) { - x.setInnerFilters(netmap.AND, filters) + x.setInnerFilters(FilterOpAND, filters) } // Filters returns list of Filter that will be applied when selecting nodes. @@ -579,11 +663,7 @@ func (x *Filter) LogicalAND(filters ...Filter) { // // See also [PlacementPolicy.SetFilters]. func (p PlacementPolicy) Filters() []Filter { - fs := make([]Filter, len(p.filters)) - for i := range p.filters { - fs[i] = Filter{m: p.filters[i]} - } - return fs + return p.filters } // SetFilters sets list of Filter that will be applied when selecting nodes. @@ -592,11 +672,7 @@ func (p PlacementPolicy) Filters() []Filter { // // See also [PlacementPolicy.Filters]. func (p *PlacementPolicy) SetFilters(fs []Filter) { - p.filters = make([]netmap.Filter, len(fs)) - - for i := range fs { - p.filters[i] = fs[i].m - } + p.filters = fs } // WriteStringTo encodes PlacementPolicy into human-readably query and writes @@ -623,8 +699,8 @@ func (p PlacementPolicy) WriteStringTo(w io.StringWriter) (err error) { return err } - c := p.replicas[i].GetCount() - s := p.replicas[i].GetSelector() + c := p.replicas[i].NumberOfObjects() + s := p.replicas[i].SelectorName() if s != "" { _, err = w.WriteString(fmt.Sprintf("REP %d IN %s", c, s)) @@ -657,18 +733,18 @@ func (p PlacementPolicy) WriteStringTo(w io.StringWriter) (err error) { return err } - _, err = w.WriteString(fmt.Sprintf("SELECT %d", p.selectors[i].GetCount())) + _, err = w.WriteString(fmt.Sprintf("SELECT %d", p.selectors[i].NumberOfNodes())) if err != nil { return err } - if s = p.selectors[i].GetAttribute(); s != "" { + if s = p.selectors[i].BucketAttribute(); s != "" { var clause string - switch p.selectors[i].GetClause() { - case netmap.Same: + switch p.selectors[i].clause { + case protonetmap.Clause_SAME: clause = "SAME " - case netmap.Distinct: + case protonetmap.Clause_DISTINCT: clause = "DISTINCT " default: clause = "" @@ -680,14 +756,14 @@ func (p PlacementPolicy) WriteStringTo(w io.StringWriter) (err error) { } } - if s = p.selectors[i].GetFilter(); s != "" { + if s = p.selectors[i].FilterName(); s != "" { _, err = w.WriteString(" FROM " + s) if err != nil { return err } } - if s = p.selectors[i].GetName(); s != "" { + if s = p.selectors[i].Name(); s != "" { _, err = w.WriteString(" AS " + s) if err != nil { return err @@ -715,25 +791,25 @@ func (p PlacementPolicy) WriteStringTo(w io.StringWriter) (err error) { return nil } -func writeFilterStringTo(w io.StringWriter, f netmap.Filter) error { +func writeFilterStringTo(w io.StringWriter, f Filter) error { var err error var s string - op := f.GetOp() + op := f.Op() unspecified := op == 0 - if s = f.GetKey(); s != "" { - _, err = w.WriteString(fmt.Sprintf("%s %s %s", s, op, f.GetValue())) + if s = f.Key(); s != "" { + _, err = w.WriteString(fmt.Sprintf("%s %s %s", s, op, f.Value())) if err != nil { return err } - } else if s = f.GetName(); unspecified && s != "" { + } else if s = f.Name(); unspecified && s != "" { _, err = w.WriteString(fmt.Sprintf("@%s", s)) if err != nil { return err } } - inner := f.GetFilters() + inner := f.SubFilters() for i := range inner { if i != 0 { _, err = w.WriteString(" " + op.String() + " ") @@ -748,7 +824,7 @@ func writeFilterStringTo(w io.StringWriter, f netmap.Filter) error { } } - if s = f.GetName(); s != "" && !unspecified { + if s = f.Name(); s != "" && !unspecified { _, err = w.WriteString(" AS " + s) if err != nil { return err @@ -828,15 +904,14 @@ func (p *policyVisitor) VisitPolicy(ctx *parser.PolicyContext) any { pl := new(PlacementPolicy) repStmts := ctx.AllRepStmt() - pl.replicas = make([]netmap.Replica, 0, len(repStmts)) + pl.replicas = make([]ReplicaDescriptor, len(repStmts)) - for _, r := range repStmts { - res, ok := r.Accept(p).(*netmap.Replica) + for i, r := range repStmts { + res, ok := r.Accept(p).(*protonetmap.Replica) if !ok { return nil } - - pl.replicas = append(pl.replicas, *res) + pl.replicas[i].fromProtoMessage(res) } if cbfStmt := ctx.CbfStmt(); cbfStmt != nil { @@ -848,22 +923,29 @@ func (p *policyVisitor) VisitPolicy(ctx *parser.PolicyContext) any { } selStmts := ctx.AllSelectStmt() - pl.selectors = make([]netmap.Selector, 0, len(selStmts)) + pl.selectors = make([]Selector, len(selStmts)) - for _, s := range selStmts { - res, ok := s.Accept(p).(*netmap.Selector) + for i, s := range selStmts { + res, ok := s.Accept(p).(*protonetmap.Selector) if !ok { return nil } - - pl.selectors = append(pl.selectors, *res) + if err := pl.selectors[i].fromProtoMessage(res); err != nil { + return fmt.Errorf("invalid selector #%d: %w", i, err) + } } filtStmts := ctx.AllFilterStmt() - pl.filters = make([]netmap.Filter, 0, len(filtStmts)) + pl.filters = make([]Filter, len(filtStmts)) - for _, f := range filtStmts { - pl.filters = append(pl.filters, *f.Accept(p).(*netmap.Filter)) + for i, f := range filtStmts { + res, ok := f.Accept(p).(*protonetmap.Filter) + if !ok { + return nil + } + if err := pl.filters[i].fromProtoMessage(res); err != nil { + return fmt.Errorf("invalid filter #%d: %w", i, err) + } } return pl @@ -885,11 +967,11 @@ func (p *policyVisitor) VisitRepStmt(ctx *parser.RepStmtContext) any { return p.reportError(errInvalidNumber) } - rs := new(netmap.Replica) - rs.SetCount(uint32(num)) + rs := new(protonetmap.Replica) + rs.Count = uint32(num) if sel := ctx.GetSelector(); sel != nil { - rs.SetSelector(sel.GetText()) + rs.Selector = sel.GetText() } return rs @@ -902,29 +984,29 @@ func (p *policyVisitor) VisitSelectStmt(ctx *parser.SelectStmtContext) any { return p.reportError(errInvalidNumber) } - s := new(netmap.Selector) - s.SetCount(uint32(res)) + s := new(protonetmap.Selector) + s.Count = uint32(res) if clStmt := ctx.Clause(); clStmt != nil { - s.SetClause(clauseFromString(clStmt.GetText())) + s.Clause = clauseFromString(clStmt.GetText()) } if bStmt := ctx.GetBucket(); bStmt != nil { - s.SetAttribute(ctx.GetBucket().GetText()) + s.Attribute = ctx.GetBucket().GetText() } - s.SetFilter(ctx.GetFilter().GetText()) // either ident or wildcard + s.Filter = ctx.GetFilter().GetText() // either ident or wildcard if ctx.AS() != nil { - s.SetName(ctx.GetName().GetText()) + s.Name = ctx.GetName().GetText() } return s } // VisitFilterStmt implements parser.QueryVisitor interface. func (p *policyVisitor) VisitFilterStmt(ctx *parser.FilterStmtContext) any { - f := p.VisitFilterExpr(ctx.GetExpr().(*parser.FilterExprContext)).(*netmap.Filter) - f.SetName(ctx.GetName().GetText()) + f := p.VisitFilterExpr(ctx.GetExpr().(*parser.FilterExprContext)).(*protonetmap.Filter) + f.Name = ctx.GetName().GetText() return f } @@ -937,21 +1019,21 @@ func (p *policyVisitor) VisitFilterExpr(ctx *parser.FilterExprContext) any { return inner.Accept(p) } - f := new(netmap.Filter) + f := new(protonetmap.Filter) op := operationFromString(ctx.GetOp().GetText()) - f.SetOp(op) + f.Op = op - f1 := *ctx.GetF1().Accept(p).(*netmap.Filter) - f2 := *ctx.GetF2().Accept(p).(*netmap.Filter) + f1 := ctx.GetF1().Accept(p).(*protonetmap.Filter) + f2 := ctx.GetF2().Accept(p).(*protonetmap.Filter) // Consider f1=(.. AND ..) AND f2. This can be merged because our AND operation // is of arbitrary arity. ANTLR generates left-associative parse-tree by default. if f1.GetOp() == op { - f.SetFilters(append(f1.GetFilters(), f2)) + f.Filters = append(f1.GetFilters(), f2) return f } - f.SetFilters([]netmap.Filter{f1, f2}) + f.Filters = []*protonetmap.Filter{f1, f2} return f } @@ -981,9 +1063,9 @@ func (p *policyVisitor) VisitFilterValue(ctx *parser.FilterValueContext) any { // VisitExpr implements parser.QueryVisitor interface. func (p *policyVisitor) VisitExpr(ctx *parser.ExprContext) any { - f := new(netmap.Filter) + f := new(protonetmap.Filter) if flt := ctx.GetFilter(); flt != nil { - f.SetName(flt.GetText()) + f.Name = flt.GetText() return f } @@ -991,9 +1073,9 @@ func (p *policyVisitor) VisitExpr(ctx *parser.ExprContext) any { opStr := ctx.SIMPLE_OP().GetText() value := ctx.GetValue().Accept(p) - f.SetKey(key.(string)) - f.SetOp(operationFromString(opStr)) - f.SetValue(value.(string)) + f.Key = key.(string) + f.Op = operationFromString(opStr) + f.Value = value.(string) return f } @@ -1004,21 +1086,21 @@ func validatePolicy(p PlacementPolicy) error { seenFilters := map[string]bool{} for i := range p.filters { - seenFilters[p.filters[i].GetName()] = true + seenFilters[p.filters[i].Name()] = true } seenSelectors := map[string]bool{} for i := range p.selectors { - if flt := p.selectors[i].GetFilter(); flt != mainFilterName && !seenFilters[flt] { + if flt := p.selectors[i].FilterName(); flt != mainFilterName && !seenFilters[flt] { return fmt.Errorf("%w: '%s'", errUnknownFilter, flt) } - seenSelectors[p.selectors[i].GetName()] = true + seenSelectors[p.selectors[i].Name()] = true } for i := range p.replicas { - if sel := p.replicas[i].GetSelector(); sel != "" && !seenSelectors[sel] { + if sel := p.replicas[i].SelectorName(); sel != "" && !seenSelectors[sel] { return fmt.Errorf("%w: '%s'", errUnknownSelector, sel) } } @@ -1026,20 +1108,42 @@ func validatePolicy(p PlacementPolicy) error { return nil } -func clauseFromString(s string) (c netmap.Clause) { - if !c.FromString(strings.ToUpper(s)) { +func clauseFromString(s string) protonetmap.Clause { + switch s { + default: // Such errors should be handled by ANTLR code thus this panic. - panic(fmt.Errorf("BUG: invalid clause: %s", c)) + panic(fmt.Errorf("BUG: invalid clause: %s", s)) + case "CLAUSE_UNSPECIFIED": + return protonetmap.Clause_CLAUSE_UNSPECIFIED + case "SAME": + return protonetmap.Clause_SAME + case "DISTINCT": + return protonetmap.Clause_DISTINCT } - - return } -func operationFromString(s string) (op netmap.Operation) { - if !op.FromString(strings.ToUpper(s)) { +func operationFromString(s string) protonetmap.Operation { + switch s { + default: // Such errors should be handled by ANTLR code thus this panic. - panic(fmt.Errorf("BUG: invalid operation: %s", op)) + panic(fmt.Errorf("BUG: invalid operation: %s", s)) + case "OPERATION_UNSPECIFIED": + return protonetmap.Operation_OPERATION_UNSPECIFIED + case "EQ": + return protonetmap.Operation_EQ + case "NE": + return protonetmap.Operation_NE + case "GT": + return protonetmap.Operation_GT + case "GE": + return protonetmap.Operation_GE + case "LT": + return protonetmap.Operation_LT + case "LE": + return protonetmap.Operation_LE + case "OR": + return protonetmap.Operation_OR + case "AND": + return protonetmap.Operation_AND } - - return } diff --git a/netmap/policy_internal_test.go b/netmap/policy_internal_test.go index 9c48fad9a..97aa622ad 100644 --- a/netmap/policy_internal_test.go +++ b/netmap/policy_internal_test.go @@ -4,7 +4,6 @@ import ( "bytes" "testing" - "github.com/nspcc-dev/neofs-api-go/v2/netmap" "github.com/stretchr/testify/require" ) @@ -37,15 +36,15 @@ func TestPlacementPolicy_CopyTo(t *testing.T) { var dst PlacementPolicy pp.CopyTo(&dst) - var f2 netmap.Filter + var f2 Filter f2.SetName("filter2") - require.Equal(t, pp.filters[0].GetName(), dst.filters[0].GetName()) + require.Equal(t, pp.filters[0].Name(), dst.filters[0].Name()) dst.filters[0].SetName("f2") - require.NotEqual(t, pp.filters[0].GetName(), dst.filters[0].GetName()) + require.NotEqual(t, pp.filters[0].Name(), dst.filters[0].Name()) dst.filters[0] = f2 - require.NotEqual(t, pp.filters[0].GetName(), dst.filters[0].GetName()) + require.NotEqual(t, pp.filters[0].Name(), dst.filters[0].Name()) }) t.Run("internal filters", func(t *testing.T) { @@ -54,7 +53,7 @@ func TestPlacementPolicy_CopyTo(t *testing.T) { var topFilter Filter topFilter.SetName("topFilter") - topFilter.setInnerFilters(netmap.EQ, []Filter{includedFilter}) + topFilter.setInnerFilters(FilterOpEQ, []Filter{includedFilter}) var policy PlacementPolicy policy.SetFilters([]Filter{topFilter}) @@ -64,13 +63,13 @@ func TestPlacementPolicy_CopyTo(t *testing.T) { require.True(t, bytes.Equal(policy.Marshal(), dst.Marshal())) t.Run("change extra filter", func(t *testing.T) { - require.Equal(t, topFilter.m.GetName(), dst.filters[0].GetName()) - require.Equal(t, topFilter.m.GetFilters()[0].GetName(), dst.filters[0].GetFilters()[0].GetName()) + require.Equal(t, topFilter.Name(), dst.filters[0].Name()) + require.Equal(t, topFilter.SubFilters()[0].Name(), dst.filters[0].SubFilters()[0].Name()) - dst.filters[0].GetFilters()[0].SetName("someInternalFilterName") + dst.filters[0].SubFilters()[0].SetName("someInternalFilterName") - require.Equal(t, topFilter.m.GetName(), dst.filters[0].GetName()) - require.NotEqual(t, topFilter.m.GetFilters()[0].GetName(), dst.filters[0].GetFilters()[0].GetName()) + require.Equal(t, topFilter.Name(), dst.filters[0].Name()) + require.NotEqual(t, topFilter.SubFilters()[0].Name(), dst.filters[0].SubFilters()[0].Name()) }) }) @@ -88,23 +87,23 @@ func TestPlacementPolicy_CopyTo(t *testing.T) { var dst PlacementPolicy pp.CopyTo(&dst) - require.Equal(t, pp.selectors[0].GetName(), dst.selectors[0].GetName()) + require.Equal(t, pp.selectors[0].Name(), dst.selectors[0].Name()) dst.selectors[0].SetName("s2") - require.NotEqual(t, pp.selectors[0].GetName(), dst.selectors[0].GetName()) + require.NotEqual(t, pp.selectors[0].Name(), dst.selectors[0].Name()) - var s2 netmap.Selector + var s2 Selector s2.SetName("selector2") dst.selectors[0] = s2 - require.NotEqual(t, pp.selectors[0].GetName(), dst.selectors[0].GetName()) + require.NotEqual(t, pp.selectors[0].Name(), dst.selectors[0].Name()) }) t.Run("change replica", func(t *testing.T) { var dst PlacementPolicy pp.CopyTo(&dst) - require.Equal(t, pp.replicas[0].GetSelector(), dst.replicas[0].GetSelector()) - dst.replicas[0].SetSelector("s2") - require.NotEqual(t, pp.replicas[0].GetSelector(), dst.replicas[0].GetSelector()) + require.Equal(t, pp.replicas[0].SelectorName(), dst.replicas[0].SelectorName()) + dst.replicas[0].SetSelectorName("s2") + require.NotEqual(t, pp.replicas[0].SelectorName(), dst.replicas[0].SelectorName()) }) } diff --git a/netmap/policy_test.go b/netmap/policy_test.go index 8b67306d5..c1b1e5912 100644 --- a/netmap/policy_test.go +++ b/netmap/policy_test.go @@ -5,8 +5,8 @@ import ( "strings" "testing" - apinetmap "github.com/nspcc-dev/neofs-api-go/v2/netmap" "github.com/nspcc-dev/neofs-sdk-go/netmap" + protonetmap "github.com/nspcc-dev/neofs-sdk-go/proto/netmap" "github.com/stretchr/testify/require" ) @@ -254,54 +254,33 @@ func TestPlacementPolicy_SetFilters(t *testing.T) { require.Equal(t, anyValidFilters, p.Filters()) } -func TestPlacementPolicy_ReadFromV2(t *testing.T) { - var m apinetmap.PlacementPolicy - m.SetContainerBackupFactor(anyValidBackupFactor) - mrs := make([]apinetmap.Replica, 2) - mrs[0].SetSelector("selector_0") - mrs[0].SetCount(2583748530) - mrs[1].SetSelector("selector_1") - mrs[1].SetCount(358755354) - m.SetReplicas(mrs) - mss := make([]apinetmap.Selector, 2) - mss[0].SetName("selector_0") - mss[0].SetCount(1814781076) - mss[0].SetClause(apinetmap.Same) - mss[0].SetFilter("filter_0") - mss[0].SetAttribute("attribute_0") - mss[1].SetName("selector_1") - mss[1].SetCount(1814781076) - mss[1].SetClause(apinetmap.Distinct) - mss[1].SetFilter("filter_1") - mss[1].SetAttribute("attribute_1") - m.SetSelectors(mss) - msubs := make([]apinetmap.Filter, 0, 2) - addSub := func(name, key string, op apinetmap.Operation, val string) { - var f apinetmap.Filter - f.SetName(name) - f.SetKey(key) - f.SetOp(op) - f.SetValue(val) - msubs = append(msubs, f) +func TestPlacementPolicy_FromProtoMessage(t *testing.T) { + m := &protonetmap.PlacementPolicy{ + Replicas: []*protonetmap.Replica{ + {Count: 2583748530, Selector: "selector_0"}, + {Count: 358755354, Selector: "selector_1"}, + }, + ContainerBackupFactor: anyValidBackupFactor, + Selectors: []*protonetmap.Selector{ + {Name: "selector_0", Count: 1814781076, Clause: protonetmap.Clause_SAME, Attribute: "attribute_0", Filter: "filter_0"}, + {Name: "selector_1", Count: 1814781076, Clause: protonetmap.Clause_DISTINCT, Attribute: "attribute_1", Filter: "filter_1"}, + }, + Filters: []*protonetmap.Filter{ + {Name: "filter_0", Op: protonetmap.Operation_AND, Filters: []*protonetmap.Filter{ + {Name: "filter_0_0", Key: "key_0_0", Op: protonetmap.Operation_EQ, Value: "val_0_0"}, + {Name: "filter_0_1", Key: "key_0_1", Op: protonetmap.Operation_NE, Value: "val_0_1"}, + }}, + {Name: "filter_1", Key: "", Op: protonetmap.Operation_OR, Value: "", Filters: []*protonetmap.Filter{ + {Name: "filter_1_0", Key: "key_1_0", Op: protonetmap.Operation_GT, Value: "1889407708985023116"}, + {Name: "filter_1_1", Key: "key_1_1", Op: protonetmap.Operation_GE, Value: "1429243097315344888"}, + {Name: "filter_1_2", Key: "key_1_2", Op: protonetmap.Operation_LT, Value: "3722656060317482335"}, + {Name: "filter_1_3", Key: "key_1_3", Op: protonetmap.Operation_LE, Value: "1950504987705284805"}, + }}, + }, } - addSub("filter_0_0", "key_0_0", apinetmap.EQ, "val_0_0") - addSub("filter_0_1", "key_0_1", apinetmap.NE, "val_0_1") - mfs := make([]apinetmap.Filter, 2) - mfs[0].SetName("filter_0") - mfs[0].SetOp(apinetmap.AND) - mfs[0].SetFilters(msubs) - msubs = make([]apinetmap.Filter, 0, 4) - addSub("filter_1_0", "key_1_0", apinetmap.GT, "1889407708985023116") - addSub("filter_1_1", "key_1_1", apinetmap.GE, "1429243097315344888") - addSub("filter_1_2", "key_1_2", apinetmap.LT, "3722656060317482335") - addSub("filter_1_3", "key_1_3", apinetmap.LE, "1950504987705284805") - mfs[1].SetName("filter_1") - mfs[1].SetOp(apinetmap.OR) - mfs[1].SetFilters(msubs) - m.SetFilters(mfs) var val netmap.PlacementPolicy - require.NoError(t, val.ReadFromV2(m)) + require.NoError(t, val.FromProtoMessage(m)) require.EqualValues(t, anyValidBackupFactor, val.ContainerBackupFactor()) rs := val.Replicas() require.Len(t, rs, 2) @@ -365,48 +344,50 @@ func TestPlacementPolicy_ReadFromV2(t *testing.T) { require.Empty(t, subs[3].SubFilters()) // reset optional fields - m.SetSelectors(nil) - m.SetFilters(nil) + m.Selectors = nil + m.Filters = nil val2 := val - require.NoError(t, val2.ReadFromV2(m)) + require.NoError(t, val2.FromProtoMessage(m)) require.Empty(t, val2.Selectors()) require.Empty(t, val2.Filters()) t.Run("invalid", func(t *testing.T) { for _, tc := range []struct { name, err string - corrupt func(*apinetmap.PlacementPolicy) + corrupt func(*protonetmap.PlacementPolicy) }{ {name: "replicas/nil", err: "missing replicas", - corrupt: func(m *apinetmap.PlacementPolicy) { m.SetReplicas(nil) }}, + corrupt: func(m *protonetmap.PlacementPolicy) { m.Replicas = nil }}, {name: "replicas/empty", err: "missing replicas", - corrupt: func(m *apinetmap.PlacementPolicy) { m.SetReplicas([]apinetmap.Replica{}) }}, + corrupt: func(m *protonetmap.PlacementPolicy) { m.Replicas = []*protonetmap.Replica{} }}, + {name: "selectors/negative clause", err: "invalid selector #1: negative clause -1", + corrupt: func(m *protonetmap.PlacementPolicy) { m.Selectors[1].Clause = -1 }}, + {name: "filters/negative op", err: "invalid filter #1: negative op -1", + corrupt: func(m *protonetmap.PlacementPolicy) { m.Filters[1].Op = -1 }}, } { t.Run(tc.name, func(t *testing.T) { st := val - var m apinetmap.PlacementPolicy - st.WriteToV2(&m) - tc.corrupt(&m) - require.EqualError(t, new(netmap.PlacementPolicy).ReadFromV2(m), tc.err) + m := st.ProtoMessage() + tc.corrupt(m) + require.EqualError(t, new(netmap.PlacementPolicy).FromProtoMessage(m), tc.err) }) } }) } -func TestPlacementPolicy_WriteToV2(t *testing.T) { +func TestPlacementPolicy_ProtoMessage(t *testing.T) { var val netmap.PlacementPolicy - var m apinetmap.PlacementPolicy // zero - val.WriteToV2(&m) + m := val.ProtoMessage() require.Zero(t, m.GetContainerBackupFactor()) require.Zero(t, m.GetReplicas()) require.Zero(t, m.GetSelectors()) require.Zero(t, m.GetFilters()) - require.Zero(t, m.GetSubnetID()) + require.Zero(t, m.GetSubnetId()) // filled - validPlacementPolicy.WriteToV2(&m) + m = validPlacementPolicy.ProtoMessage() require.EqualValues(t, anyValidBackupFactor, m.GetContainerBackupFactor()) mrs := m.GetReplicas() @@ -420,12 +401,12 @@ func TestPlacementPolicy_WriteToV2(t *testing.T) { require.Len(t, mss, 2) require.Equal(t, "selector_0", mss[0].GetName()) require.EqualValues(t, 1814781076, mss[0].GetCount()) - require.Equal(t, apinetmap.Same, mss[0].GetClause()) + require.Equal(t, protonetmap.Clause_SAME, mss[0].GetClause()) require.Equal(t, "filter_0", mss[0].GetFilter()) require.Equal(t, "attribute_0", mss[0].GetAttribute()) require.Equal(t, "selector_1", mss[1].GetName()) require.EqualValues(t, 1505136737, mss[1].GetCount()) - require.Equal(t, apinetmap.Distinct, mss[1].GetClause()) + require.Equal(t, protonetmap.Clause_DISTINCT, mss[1].GetClause()) require.Equal(t, "filter_1", mss[1].GetFilter()) require.Equal(t, "attribute_1", mss[1].GetAttribute()) @@ -434,51 +415,51 @@ func TestPlacementPolicy_WriteToV2(t *testing.T) { // filter#0 require.Equal(t, "filter_0", mfs[0].GetName()) require.Zero(t, mfs[0].GetKey()) - require.Equal(t, apinetmap.AND, mfs[0].GetOp()) + require.Equal(t, protonetmap.Operation_AND, mfs[0].GetOp()) require.Zero(t, mfs[0].GetValue()) msubs := mfs[0].GetFilters() require.Len(t, msubs, 2) // sub#0 require.Equal(t, "filter_0_0", msubs[0].GetName()) require.Equal(t, "key_0_0", msubs[0].GetKey()) - require.Equal(t, apinetmap.EQ, msubs[0].GetOp()) + require.Equal(t, protonetmap.Operation_EQ, msubs[0].GetOp()) require.Equal(t, "val_0_0", msubs[0].GetValue()) require.Zero(t, msubs[0].GetFilters()) // sub#1 require.Equal(t, "filter_0_1", msubs[1].GetName()) require.Equal(t, "key_0_1", msubs[1].GetKey()) - require.Equal(t, apinetmap.NE, msubs[1].GetOp()) + require.Equal(t, protonetmap.Operation_NE, msubs[1].GetOp()) require.Equal(t, "val_0_1", msubs[1].GetValue()) require.Zero(t, msubs[1].GetFilters()) // filter#1 require.Equal(t, "filter_1", mfs[1].GetName()) require.Zero(t, mfs[1].GetKey()) - require.Equal(t, apinetmap.OR, mfs[1].GetOp()) + require.Equal(t, protonetmap.Operation_OR, mfs[1].GetOp()) require.Zero(t, mfs[1].GetValue()) msubs = mfs[1].GetFilters() require.Len(t, msubs, 4) // sub#0 require.Equal(t, "filter_1_0", msubs[0].GetName()) require.Equal(t, "key_1_0", msubs[0].GetKey()) - require.Equal(t, apinetmap.GT, msubs[0].GetOp()) + require.Equal(t, protonetmap.Operation_GT, msubs[0].GetOp()) require.Equal(t, "1889407708985023116", msubs[0].GetValue()) require.Zero(t, msubs[0].GetFilters()) // sub#1 require.Equal(t, "filter_1_1", msubs[1].GetName()) require.Equal(t, "key_1_1", msubs[1].GetKey()) - require.Equal(t, apinetmap.GE, msubs[1].GetOp()) + require.Equal(t, protonetmap.Operation_GE, msubs[1].GetOp()) require.Equal(t, "1429243097315344888", msubs[1].GetValue()) require.Zero(t, msubs[1].GetFilters()) // sub#2 require.Equal(t, "filter_1_2", msubs[2].GetName()) require.Equal(t, "key_1_2", msubs[2].GetKey()) - require.Equal(t, apinetmap.LT, msubs[2].GetOp()) + require.Equal(t, protonetmap.Operation_LT, msubs[2].GetOp()) require.Equal(t, "3722656060317482335", msubs[2].GetValue()) require.Zero(t, msubs[2].GetFilters()) // sub#3 require.Equal(t, "filter_1_3", msubs[3].GetName()) require.Equal(t, "key_1_3", msubs[3].GetKey()) - require.Equal(t, apinetmap.LE, msubs[3].GetOp()) + require.Equal(t, protonetmap.Operation_LE, msubs[3].GetOp()) require.Equal(t, "1950504987705284805", msubs[3].GetValue()) require.Zero(t, msubs[3].GetFilters()) } diff --git a/netmap/selector.go b/netmap/selector.go index 24bd90d97..13bda6c85 100644 --- a/netmap/selector.go +++ b/netmap/selector.go @@ -5,21 +5,20 @@ import ( "sort" "github.com/nspcc-dev/hrw/v2" - "github.com/nspcc-dev/neofs-api-go/v2/netmap" ) // processSelectors processes selectors and returns error is any of them is invalid. func (c *context) processSelectors(p PlacementPolicy) error { for i := range p.selectors { - fName := p.selectors[i].GetFilter() + fName := p.selectors[i].FilterName() if fName != mainFilterName { - _, ok := c.processedFilters[p.selectors[i].GetFilter()] + _, ok := c.processedFilters[p.selectors[i].FilterName()] if !ok { return fmt.Errorf("%w: SELECT FROM '%s'", errFilterNotFound, fName) } } - sName := p.selectors[i].GetName() + sName := p.selectors[i].Name() c.processedSelectors[sName] = &p.selectors[i] @@ -36,13 +35,12 @@ func (c *context) processSelectors(p PlacementPolicy) error { // calcNodesCount returns number of buckets and minimum number of nodes in every bucket // for the given selector. -func calcNodesCount(s netmap.Selector) (int, int) { - switch s.GetClause() { - case netmap.Same: - return 1, int(s.GetCount()) - default: - return int(s.GetCount()), 1 +func calcNodesCount(s Selector) (int, int) { + n := int(s.NumberOfNodes()) + if s.IsSame() { + return 1, n } + return n, 1 } // calcBucketWeight computes weight for a node bucket. @@ -56,12 +54,12 @@ func calcBucketWeight(ns nodes, a aggregator, wf weightFunc) float64 { // getSelection returns nodes grouped by s.attribute. // Last argument specifies if more buckets can be used to fulfill CBF. -func (c *context) getSelection(_ PlacementPolicy, s netmap.Selector) ([]nodes, error) { +func (c *context) getSelection(_ PlacementPolicy, s Selector) ([]nodes, error) { bucketCount, nodesInBucket := calcNodesCount(s) buckets := c.getSelectionBase(s) if len(buckets) < bucketCount { - return nil, fmt.Errorf("%w: '%s'", ErrNotEnoughNodes, s.GetName()) + return nil, fmt.Errorf("%w: '%s'", ErrNotEnoughNodes, s.Name()) } // We need deterministic output in case there is no pivot. @@ -69,7 +67,7 @@ func (c *context) getSelection(_ PlacementPolicy, s netmap.Selector) ([]nodes, e // However, because initial order influences HRW order for buckets with equal weights, // we also need to have deterministic input to HRW sorting routine. if len(c.hrwSeed) == 0 { - if s.GetAttribute() == "" { + if s.BucketAttribute() == "" { sort.Slice(buckets, func(i, j int) bool { return less(buckets[i].nodes[0], buckets[j].nodes[0]) }) @@ -97,7 +95,7 @@ func (c *context) getSelection(_ PlacementPolicy, s netmap.Selector) ([]nodes, e // Fallback to using minimum allowed backup factor (1). res = append(res, fallback...) if len(res) < bucketCount { - return nil, fmt.Errorf("%w: '%s'", ErrNotEnoughNodes, s.GetName()) + return nil, fmt.Errorf("%w: '%s'", ErrNotEnoughNodes, s.Name()) } } @@ -110,7 +108,7 @@ func (c *context) getSelection(_ PlacementPolicy, s netmap.Selector) ([]nodes, e hrw.SortWeighted(res, weights, c.hrwSeedHash) } - if s.GetAttribute() == "" { + if s.BucketAttribute() == "" { res, fallback = res[:bucketCount], res[bucketCount:] for i := range fallback { index := i % bucketCount @@ -131,13 +129,13 @@ type nodeAttrPair struct { // getSelectionBase returns nodes grouped by selector attribute. // It it guaranteed that each pair will contain at least one node. -func (c *context) getSelectionBase(s netmap.Selector) []nodeAttrPair { - fName := s.GetFilter() +func (c *context) getSelectionBase(s Selector) []nodeAttrPair { + fName := s.FilterName() f := c.processedFilters[fName] isMain := fName == mainFilterName result := []nodeAttrPair{} nodeMap := map[string][]NodeInfo{} - attr := s.GetAttribute() + attr := s.BucketAttribute() for i := range c.netMap.nodes { if isMain || c.match(f, c.netMap.nodes[i]) { diff --git a/netmap/selector_test.go b/netmap/selector_test.go index 4ab9ac401..20781428a 100644 --- a/netmap/selector_test.go +++ b/netmap/selector_test.go @@ -7,8 +7,8 @@ import ( "testing" "github.com/nspcc-dev/hrw/v2" - "github.com/nspcc-dev/neofs-api-go/v2/netmap" cid "github.com/nspcc-dev/neofs-sdk-go/container/id" + protonetmap "github.com/nspcc-dev/neofs-sdk-go/proto/netmap" "github.com/stretchr/testify/require" ) @@ -73,8 +73,8 @@ func BenchmarkPolicyHRWType(b *testing.B) { newSelector("loc1", "Location", 1, "loc1", (*Selector).SelectSame), newSelector("loc2", "Location", 1, "loc2", (*Selector).SelectSame)}, []Filter{ - newFilter("loc1", "Location", "Shanghai", netmap.EQ), - newFilter("loc2", "Location", "Shanghai", netmap.NE), + newFilter("loc1", "Location", "Shanghai", FilterOpEQ), + newFilter("loc2", "Location", "Shanghai", FilterOpNE), }) nodes := make([]NodeInfo, netmapSize) @@ -118,8 +118,8 @@ func TestPlacementPolicy_DeterministicOrder(t *testing.T) { newSelector("loc1", "Location", 1, "loc1", (*Selector).SelectSame), newSelector("loc2", "Location", 1, "loc2", (*Selector).SelectSame)}, []Filter{ - newFilter("loc1", "Location", "Shanghai", netmap.EQ), - newFilter("loc2", "Location", "Shanghai", netmap.NE), + newFilter("loc1", "Location", "Shanghai", FilterOpEQ), + newFilter("loc2", "Location", "Shanghai", FilterOpNE), }) nodeList := make([]NodeInfo, netmapSize) @@ -174,8 +174,8 @@ func TestPlacementPolicy_ProcessSelectors(t *testing.T) { newSelector("Main", "Country", 3, "*", (*Selector).SelectDistinct), }, []Filter{ - newFilter("FromRU", "Country", "Russia", netmap.EQ), - newFilter("Good", "Rating", "4", netmap.GE), + newFilter("FromRU", "Country", "Russia", FilterOpEQ), + newFilter("Good", "Rating", "4", FilterOpGE), }) nodes := []NodeInfo{ nodeInfoFromAttributes("Country", "Russia", "Rating", "1", "City", "SPB"), @@ -199,13 +199,13 @@ func TestPlacementPolicy_ProcessSelectors(t *testing.T) { require.NoError(t, c.processSelectors(p)) for _, s := range p.selectors { - sel := c.selections[s.GetName()] - s := c.processedSelectors[s.GetName()] + sel := c.selections[s.Name()] + s := c.processedSelectors[s.Name()] bucketCount, nodesInBucket := calcNodesCount(*s) nodesInBucket *= int(c.cbf) - targ := fmt.Sprintf("selector '%s'", s.GetName()) + targ := fmt.Sprintf("selector '%s'", s.Name()) require.Equal(t, bucketCount, len(sel), targ) - fName := s.GetFilter() + fName := s.FilterName() for _, res := range sel { require.Equal(t, nodesInBucket, len(res), targ) for j := range res { @@ -219,11 +219,9 @@ func TestSelector_SetName(t *testing.T) { const name = "some name" var s Selector - require.Zero(t, s.m.GetName()) require.Zero(t, s.Name()) s.SetName(name) - require.Equal(t, name, s.m.GetName()) require.Equal(t, name, s.Name()) } @@ -231,29 +229,27 @@ func TestSelector_SetNumberOfNodes(t *testing.T) { const num = 3 var s Selector - require.Zero(t, s.m.GetCount()) require.Zero(t, s.NumberOfNodes()) s.SetNumberOfNodes(num) - require.EqualValues(t, num, s.m.GetCount()) require.EqualValues(t, num, s.NumberOfNodes()) } func TestSelectorClauses(t *testing.T) { var s Selector - require.Equal(t, netmap.UnspecifiedClause, s.m.GetClause()) + require.Equal(t, protonetmap.Clause_CLAUSE_UNSPECIFIED, s.clause) require.False(t, s.IsSame()) require.False(t, s.IsDistinct()) s.SelectDistinct() - require.Equal(t, netmap.Distinct, s.m.GetClause()) + require.Equal(t, protonetmap.Clause_DISTINCT, s.clause) require.False(t, s.IsSame()) require.True(t, s.IsDistinct()) s.SelectSame() - require.Equal(t, netmap.Same, s.m.GetClause()) + require.Equal(t, protonetmap.Clause_SAME, s.clause) require.True(t, s.IsSame()) require.False(t, s.IsDistinct()) } @@ -262,18 +258,18 @@ func TestSelector_SelectByBucketAttribute(t *testing.T) { const attr = "some attribute" var s Selector - require.Zero(t, s.m.GetAttribute()) + require.Zero(t, s.BucketAttribute()) s.SelectByBucketAttribute(attr) - require.Equal(t, attr, s.m.GetAttribute()) + require.Equal(t, attr, s.BucketAttribute()) } func TestSelector_SetFilterName(t *testing.T) { const fName = "some filter" var s Selector - require.Zero(t, s.m.GetFilter()) + require.Zero(t, s.FilterName()) s.SetFilterName(fName) - require.Equal(t, fName, s.m.GetFilter()) + require.Equal(t, fName, s.FilterName()) } diff --git a/object/id/address.go b/object/id/address.go index 9dfe754b0..6c3ea94af 100644 --- a/object/id/address.go +++ b/object/id/address.go @@ -5,8 +5,10 @@ import ( "fmt" "strings" - "github.com/nspcc-dev/neofs-api-go/v2/refs" cid "github.com/nspcc-dev/neofs-sdk-go/container/id" + neofsproto "github.com/nspcc-dev/neofs-sdk-go/internal/proto" + "github.com/nspcc-dev/neofs-sdk-go/proto/refs" + "google.golang.org/protobuf/encoding/protojson" ) // Address represents global object identifier in NeoFS network. Each object @@ -15,8 +17,8 @@ import ( // // ID implements built-in comparable interface. // -// Address is mutually compatible with github.com/nspcc-dev/neofs-api-go/v2/refs.Address -// message. See ReadFromV2 / WriteToV2 methods. +// Address is mutually compatible with [refs.Address] message. See +// [Address.FromProtoMessage] / [Address.ProtoMessage] methods. type Address struct { cnr cid.ID @@ -35,27 +37,25 @@ func DecodeAddressString(s string) (Address, error) { return id, id.DecodeString(s) } -// ReadFromV2 reads Address from the refs.Address message. Returns an error if -// the message is malformed according to the NeoFS API V2 protocol. +// FromProtoMessage validates m according to the NeoFS API protocol and restores +// x from it. // -// See also WriteToV2. -func (x *Address) ReadFromV2(m refs.Address) error { - cnr := m.GetContainerID() - if cnr == nil { +// See also [Address.ProtoMessage]. +func (x *Address) FromProtoMessage(m *refs.Address) error { + if m.ContainerId == nil { return errors.New("missing container ID") } - obj := m.GetObjectID() - if obj == nil { + if m.ObjectId == nil { return errors.New("missing object ID") } - err := x.cnr.ReadFromV2(*cnr) + err := x.cnr.FromProtoMessage(m.ContainerId) if err != nil { return fmt.Errorf("invalid container ID: %w", err) } - err = x.obj.ReadFromV2(*obj) + err = x.obj.FromProtoMessage(m.ObjectId) if err != nil { return fmt.Errorf("invalid object ID: %w", err) } @@ -63,19 +63,15 @@ func (x *Address) ReadFromV2(m refs.Address) error { return nil } -// WriteToV2 writes Address to the refs.Address message. -// The message must not be nil. +// ProtoMessage converts x into message to transmit using the NeoFS API +// protocol. // -// See also ReadFromV2. -func (x Address) WriteToV2(m *refs.Address) { - var obj refs.ObjectID - x.obj.WriteToV2(&obj) - - var cnr refs.ContainerID - x.cnr.WriteToV2(&cnr) - - m.SetObjectID(&obj) - m.SetContainerID(&cnr) +// See also [Address.FromProtoMessage]. +func (x Address) ProtoMessage() *refs.Address { + return &refs.Address{ + ContainerId: x.cnr.ProtoMessage(), + ObjectId: x.obj.ProtoMessage(), + } } // MarshalJSON encodes Address into a JSON format of the NeoFS API protocol @@ -83,10 +79,7 @@ func (x Address) WriteToV2(m *refs.Address) { // // See also UnmarshalJSON. func (x Address) MarshalJSON() ([]byte, error) { - var m refs.Address - x.WriteToV2(&m) - - return m.MarshalJSON() + return neofsproto.MarshalMessageJSON(x.ProtoMessage()) } // UnmarshalJSON decodes NeoFS API protocol JSON format into the Address @@ -96,12 +89,12 @@ func (x Address) MarshalJSON() ([]byte, error) { func (x *Address) UnmarshalJSON(data []byte) error { var m refs.Address - err := m.UnmarshalJSON(data) + err := protojson.Unmarshal(data, &m) if err != nil { return err } - return x.ReadFromV2(m) + return x.FromProtoMessage(&m) } // Container returns unique identifier of the NeoFS object container. diff --git a/object/id/address_test.go b/object/id/address_test.go index 4217a8333..0890ef21c 100644 --- a/object/id/address_test.go +++ b/object/id/address_test.go @@ -5,11 +5,12 @@ import ( "strings" "testing" - "github.com/nspcc-dev/neofs-api-go/v2/refs" cid "github.com/nspcc-dev/neofs-sdk-go/container/id" cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test" + neofsproto "github.com/nspcc-dev/neofs-sdk-go/internal/proto" oid "github.com/nspcc-dev/neofs-sdk-go/object/id" oidtest "github.com/nspcc-dev/neofs-sdk-go/object/id/test" + "github.com/nspcc-dev/neofs-sdk-go/proto/refs" "github.com/stretchr/testify/require" ) @@ -30,46 +31,34 @@ var invalidAddressTestcases = []struct { corrupt func(*refs.Address) }{ {name: "empty", err: "missing container ID", corrupt: func(a *refs.Address) { - a.SetContainerID(nil) - a.SetObjectID(nil) + a.ContainerId = nil + a.ObjectId = nil }}, - {name: "container/missing", err: "missing container ID", corrupt: func(a *refs.Address) { a.SetContainerID(nil) }}, + {name: "container/missing", err: "missing container ID", corrupt: func(a *refs.Address) { a.ContainerId = nil }}, {name: "container/nil value", err: "invalid container ID: invalid length 0", corrupt: func(a *refs.Address) { - a.SetContainerID(new(refs.ContainerID)) + a.ContainerId = new(refs.ContainerID) }}, {name: "container/empty value", err: "invalid container ID: invalid length 0", corrupt: func(a *refs.Address) { - var m refs.ContainerID - m.SetValue([]byte{}) - a.SetContainerID(&m) + a.ContainerId.Value = []byte{} }}, {name: "container/undersize", err: "invalid container ID: invalid length 31", corrupt: func(a *refs.Address) { - var m refs.ContainerID - m.SetValue(make([]byte, 31)) - a.SetContainerID(&m) + a.ContainerId.Value = make([]byte, 31) }}, {name: "container/oversize", err: "invalid container ID: invalid length 33", corrupt: func(a *refs.Address) { - var m refs.ContainerID - m.SetValue(make([]byte, 33)) - a.SetContainerID(&m) + a.ContainerId.Value = make([]byte, 33) }}, - {name: "object/missing", err: "missing object ID", corrupt: func(a *refs.Address) { a.SetObjectID(nil) }}, + {name: "object/missing", err: "missing object ID", corrupt: func(a *refs.Address) { a.ObjectId = nil }}, {name: "object/nil value", err: "invalid object ID: invalid length 0", corrupt: func(a *refs.Address) { - a.SetObjectID(new(refs.ObjectID)) + a.ObjectId = new(refs.ObjectID) }}, {name: "object/empty value", err: "invalid object ID: invalid length 0", corrupt: func(a *refs.Address) { - var m refs.ObjectID - m.SetValue([]byte{}) - a.SetObjectID(&m) + a.ObjectId.Value = []byte{} }}, {name: "object/undersize", err: "invalid object ID: invalid length 31", corrupt: func(a *refs.Address) { - var m refs.ObjectID - m.SetValue(make([]byte, 31)) - a.SetObjectID(&m) + a.ObjectId.Value = make([]byte, 31) }}, {name: "object/oversize", err: "invalid object ID: invalid length 33", corrupt: func(a *refs.Address) { - var m refs.ObjectID - m.SetValue(make([]byte, 33)) - a.SetObjectID(&m) + a.ObjectId.Value = make([]byte, 33) }}, } @@ -102,12 +91,11 @@ func testAddressIDField[T ~[32]byte]( t.Run("api", func(t *testing.T) { src := oidtest.Address() var dst oid.Address - var msg refs.Address set(&src, val) - src.WriteToV2(&msg) - require.EqualValues(t, val[:], getAPI(&msg)) - require.NoError(t, dst.ReadFromV2(msg)) + msg := src.ProtoMessage() + require.EqualValues(t, val[:], getAPI(msg)) + require.NoError(t, dst.FromProtoMessage(msg)) require.Equal(t, val, get(dst)) }) t.Run("json", func(t *testing.T) { @@ -125,36 +113,32 @@ func testAddressIDField[T ~[32]byte]( func TestAddress_SetContainer(t *testing.T) { testAddressIDField(t, cidtest.OtherID, oid.Address.Container, (*oid.Address).SetContainer, func(m *refs.Address) []byte { - return m.GetContainerID().GetValue() + return m.GetContainerId().GetValue() }) } func TestAddress_SetObject(t *testing.T) { testAddressIDField(t, oidtest.OtherID, oid.Address.Object, (*oid.Address).SetObject, func(m *refs.Address) []byte { - return m.GetObjectID().GetValue() + return m.GetObjectId().GetValue() }) } -func TestAddress_ReadFromV2(t *testing.T) { - var mc refs.ContainerID - mc.SetValue(validContainerBytes[:]) - var mo refs.ObjectID - mo.SetValue(validIDBytes[:]) - var m refs.Address - m.SetContainerID(&mc) - m.SetObjectID(&mo) +func TestAddress_FromProtoMessage(t *testing.T) { + m := &refs.Address{ + ContainerId: &refs.ContainerID{Value: validContainerBytes[:]}, + ObjectId: &refs.ObjectID{Value: validIDBytes[:]}, + } var a oid.Address - require.NoError(t, a.ReadFromV2(m)) + require.NoError(t, a.FromProtoMessage(m)) require.EqualValues(t, validContainerBytes, a.Container()) require.EqualValues(t, validIDBytes, a.Object()) t.Run("invalid", func(t *testing.T) { for _, tc := range invalidAddressTestcases { t.Run(tc.name, func(t *testing.T) { - var m refs.Address - oidtest.Address().WriteToV2(&m) - tc.corrupt(&m) - require.EqualError(t, new(oid.Address).ReadFromV2(m), tc.err) + m := oidtest.Address().ProtoMessage() + tc.corrupt(m) + require.EqualError(t, new(oid.Address).FromProtoMessage(m), tc.err) }) } }) @@ -250,10 +234,9 @@ func TestAddress_UnmarshalJSON(t *testing.T) { t.Run("protocol violation", func(t *testing.T) { for _, tc := range invalidAddressTestcases { t.Run(tc.name, func(t *testing.T) { - var m refs.Address - oidtest.Address().WriteToV2(&m) - tc.corrupt(&m) - b, err := m.MarshalJSON() + m := oidtest.Address().ProtoMessage() + tc.corrupt(m) + b, err := neofsproto.MarshalMessageJSON(m) require.NoError(t, err) require.EqualError(t, new(oid.Address).UnmarshalJSON(b), tc.err) }) diff --git a/object/id/id.go b/object/id/id.go index 2b24c69f3..b7ebbdac7 100644 --- a/object/id/id.go +++ b/object/id/id.go @@ -6,8 +6,11 @@ import ( "fmt" "github.com/mr-tron/base58" - "github.com/nspcc-dev/neofs-api-go/v2/refs" neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto" + neofsproto "github.com/nspcc-dev/neofs-sdk-go/internal/proto" + "github.com/nspcc-dev/neofs-sdk-go/proto/refs" + "google.golang.org/protobuf/encoding/protojson" + "google.golang.org/protobuf/proto" ) // Size is the size of an [ID] in bytes. @@ -18,8 +21,8 @@ const Size = sha256.Size // // ID implements built-in comparable interface. // -// ID is mutually compatible with github.com/nspcc-dev/neofs-api-go/v2/refs.ObjectID -// message. See ReadFromV2 / WriteToV2 methods. +// ID is mutually compatible with [refs.ObjectID] message. See +// [ID.FromProtoMessage] / [ID.ProtoMessage] methods. type ID [Size]byte // ErrZero is an error returned on zero [ID] encounter. @@ -42,24 +45,24 @@ func DecodeString(s string) (ID, error) { return id, id.DecodeString(s) } -// ReadFromV2 reads ID from the refs.ObjectID message. Returns an error if -// the message is malformed according to the NeoFS API V2 protocol. +// FromProtoMessage validates m according to the NeoFS API protocol and restores +// id from it. // -// See also WriteToV2. -func (id *ID) ReadFromV2(m refs.ObjectID) error { - err := id.Decode(m.GetValue()) +// See also [ID.ProtoMessage]. +func (id *ID) FromProtoMessage(m *refs.ObjectID) error { + err := id.Decode(m.Value) if err == nil && id.IsZero() { err = ErrZero } return err } -// WriteToV2 writes ID to the refs.ObjectID message. -// The message must not be nil. +// ProtoMessage converts id into message to transmit using the NeoFS API +// protocol. // -// See also ReadFromV2. -func (id ID) WriteToV2(m *refs.ObjectID) { - m.SetValue(id[:]) +// See also [ID.FromProtoMessage]. +func (id ID) ProtoMessage() *refs.ObjectID { + return &refs.ObjectID{Value: id[:]} } // Encode encodes ID into [Size] bytes of dst. Panics if @@ -149,38 +152,32 @@ func (id ID) CalculateIDSignature(signer neofscrypto.Signer) (neofscrypto.Signat // Marshal marshals ID into a protobuf binary form. func (id ID) Marshal() []byte { - var v2 refs.ObjectID - v2.SetValue(id[:]) - - return v2.StableMarshal(nil) + return neofsproto.MarshalMessage(id.ProtoMessage()) } // Unmarshal unmarshals protobuf binary representation of ID. func (id *ID) Unmarshal(data []byte) error { - var v2 refs.ObjectID - if err := v2.Unmarshal(data); err != nil { + var m refs.ObjectID + if err := proto.Unmarshal(data, &m); err != nil { return err } - return id.ReadFromV2(v2) + return id.FromProtoMessage(&m) } // MarshalJSON encodes ID to protobuf JSON format. func (id ID) MarshalJSON() ([]byte, error) { - var v2 refs.ObjectID - v2.SetValue(id[:]) - - return v2.MarshalJSON() + return neofsproto.MarshalMessageJSON(id.ProtoMessage()) } // UnmarshalJSON decodes ID from protobuf JSON format. func (id *ID) UnmarshalJSON(data []byte) error { - var v2 refs.ObjectID - if err := v2.UnmarshalJSON(data); err != nil { + var m refs.ObjectID + if err := protojson.Unmarshal(data, &m); err != nil { return err } - return id.ReadFromV2(v2) + return id.FromProtoMessage(&m) } // IsZero checks whether ID is zero. diff --git a/object/id/id_test.go b/object/id/id_test.go index 190edefbe..8e9337ed9 100644 --- a/object/id/id_test.go +++ b/object/id/id_test.go @@ -7,12 +7,13 @@ import ( "math/rand" "testing" - "github.com/nspcc-dev/neofs-api-go/v2/refs" cid "github.com/nspcc-dev/neofs-sdk-go/container/id" neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto" neofscryptotest "github.com/nspcc-dev/neofs-sdk-go/crypto/test" + neofsproto "github.com/nspcc-dev/neofs-sdk-go/internal/proto" oid "github.com/nspcc-dev/neofs-sdk-go/object/id" oidtest "github.com/nspcc-dev/neofs-sdk-go/object/id/test" + "github.com/nspcc-dev/neofs-sdk-go/proto/refs" usertest "github.com/nspcc-dev/neofs-sdk-go/user/test" "github.com/stretchr/testify/require" "google.golang.org/protobuf/encoding/protowire" @@ -45,20 +46,17 @@ var invalidValueTestcases = []invalidValueTestCase{ func toProtoBytes(b []byte) []byte { return protowire.AppendBytes([]byte{10}, b) } func toProtoJSON(b []byte) []byte { - var m refs.ObjectID - m.SetValue(b) - b, err := m.MarshalJSON() + b, err := neofsproto.MarshalMessageJSON(&refs.ObjectID{Value: b}) if err != nil { panic(fmt.Sprintf("unexpected MarshalJSON error: %v", err)) } return b } -func TestID_ReadFromV2(t *testing.T) { - var m refs.ObjectID - m.SetValue(validIDBytes[:]) +func TestID_FromProtoMessage(t *testing.T) { + m := &refs.ObjectID{Value: validIDProtoBytes[:]} var id oid.ID - require.NoError(t, id.ReadFromV2(m)) + require.NoError(t, id.FromProtoMessage(m)) require.EqualValues(t, validIDBytes, id) t.Run("invalid", func(t *testing.T) { @@ -66,9 +64,8 @@ func TestID_ReadFromV2(t *testing.T) { name: "zero value", err: "zero object ID", val: make([]byte, cid.Size), }) { t.Run(tc.name, func(t *testing.T) { - var m refs.ObjectID - m.SetValue(tc.val) - require.EqualError(t, new(oid.ID).ReadFromV2(m), tc.err) + m := &refs.ObjectID{Value: tc.val} + require.EqualError(t, new(oid.ID).FromProtoMessage(m), tc.err) }) } }) @@ -90,10 +87,9 @@ func TestID_Decode(t *testing.T) { }) } -func TestID_WriteToV2(t *testing.T) { +func TestID_ProtoMessage(t *testing.T) { id := oidtest.ID() - var m refs.ObjectID - id.WriteToV2(&m) + m := id.ProtoMessage() require.Equal(t, id[:], m.GetValue()) } diff --git a/object/id/test/generate_test.go b/object/id/test/generate_test.go index 1313b12d5..4e18cb3ad 100644 --- a/object/id/test/generate_test.go +++ b/object/id/test/generate_test.go @@ -4,7 +4,6 @@ import ( "math/rand/v2" "testing" - "github.com/nspcc-dev/neofs-api-go/v2/refs" oid "github.com/nspcc-dev/neofs-sdk-go/object/id" oidtest "github.com/nspcc-dev/neofs-sdk-go/object/id/test" "github.com/stretchr/testify/require" @@ -14,10 +13,9 @@ func TestID(t *testing.T) { id := oidtest.ID() require.NotEqual(t, id, oidtest.ID()) - var m refs.ObjectID - id.WriteToV2(&m) + m := id.ProtoMessage() var id2 oid.ID - require.NoError(t, id2.ReadFromV2(m)) + require.NoError(t, id2.FromProtoMessage(m)) } func TestNIDs(t *testing.T) { @@ -34,10 +32,9 @@ func TestAddress(t *testing.T) { a := oidtest.Address() require.NotEqual(t, a, oidtest.Address()) - var m refs.Address - a.WriteToV2(&m) + m := a.ProtoMessage() var id2 oid.Address - require.NoError(t, id2.ReadFromV2(m)) + require.NoError(t, id2.FromProtoMessage(m)) } func TestChangeAddress(t *testing.T) { diff --git a/user/example_test.go b/user/example_test.go index 523bea638..cc28b9936 100644 --- a/user/example_test.go +++ b/user/example_test.go @@ -2,7 +2,6 @@ package user_test import ( "github.com/nspcc-dev/neo-go/pkg/util" - apiGoRefs "github.com/nspcc-dev/neofs-api-go/v2/refs" "github.com/nspcc-dev/neofs-sdk-go/user" ) @@ -34,16 +33,13 @@ func ExampleID_DecodeString() { // Instances can be also used to process NeoFS API V2 protocol messages with [https://github.com/nspcc-dev/neofs-api] package. func ExampleID_marshalling() { - // import apiGoRefs "github.com/nspcc-dev/neofs-api-go/v2/refs" - // On the client side. var id user.ID - var msg apiGoRefs.OwnerID - id.WriteToV2(&msg) + msg := id.ProtoMessage() // *send message* // On the server side. - _ = id.ReadFromV2(msg) + _ = id.FromProtoMessage(msg) } diff --git a/user/id.go b/user/id.go index f33d419d3..59f53fbdc 100644 --- a/user/id.go +++ b/user/id.go @@ -11,7 +11,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/crypto/keys" "github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/util" - "github.com/nspcc-dev/neofs-api-go/v2/refs" + "github.com/nspcc-dev/neofs-sdk-go/proto/refs" ) // IDSize is the size of an [ID] in bytes. @@ -22,8 +22,8 @@ const IDSize = 25 // // ID implements built-in comparable interface. // -// ID is mutually compatible with github.com/nspcc-dev/neofs-api-go/v2/refs.OwnerID -// message. See ReadFromV2 / WriteToV2 methods. +// ID is mutually compatible with [refs.OwnerID] message. See +// [ID.FromProtoMessage] / [ID.ProtoMessage] methods. type ID [IDSize]byte // ErrZeroID is an error returned on zero [ID] encounter. @@ -64,24 +64,24 @@ func (x *ID) decodeBytes(b []byte) error { return nil } -// ReadFromV2 reads ID from the refs.OwnerID message. Returns an error if -// the message is malformed according to the NeoFS API V2 protocol. +// FromProtoMessage validates m according to the NeoFS API protocol and restores +// x from it. // -// See also WriteToV2. -func (x *ID) ReadFromV2(m refs.OwnerID) error { - err := x.decodeBytes(m.GetValue()) +// See also [ID.ProtoMessage]. +func (x *ID) FromProtoMessage(m *refs.OwnerID) error { + err := x.decodeBytes(m.Value) if err == nil && x.IsZero() { err = ErrZeroID } return err } -// WriteToV2 writes ID to the refs.OwnerID message. -// The message must not be nil. +// ProtoMessage converts x into message to transmit using the NeoFS API +// protocol. // -// See also ReadFromV2. -func (x ID) WriteToV2(m *refs.OwnerID) { - m.SetValue(x[:]) +// See also [ID.FromProtoMessage]. +func (x ID) ProtoMessage() *refs.OwnerID { + return &refs.OwnerID{Value: x[:]} } // SetScriptHash forms user ID from wallet address scripthash. diff --git a/user/id_test.go b/user/id_test.go index eada4d85c..5da6ef462 100644 --- a/user/id_test.go +++ b/user/id_test.go @@ -7,7 +7,7 @@ import ( "testing" "github.com/nspcc-dev/neo-go/pkg/util" - "github.com/nspcc-dev/neofs-api-go/v2/refs" + "github.com/nspcc-dev/neofs-sdk-go/proto/refs" "github.com/nspcc-dev/neofs-sdk-go/user" usertest "github.com/nspcc-dev/neofs-sdk-go/user/test" "github.com/stretchr/testify/require" @@ -31,11 +31,10 @@ func TestID_WalletBytes(t *testing.T) { require.Equal(t, id[:], id.WalletBytes()) } -func TestID_ReadFromV2(t *testing.T) { - var m refs.OwnerID - m.SetValue(validBytes[:]) +func TestID_FromProtoMessage(t *testing.T) { + m := &refs.OwnerID{Value: validBytes[:]} var id user.ID - require.NoError(t, id.ReadFromV2(m)) + require.NoError(t, id.FromProtoMessage(m)) require.EqualValues(t, validBytes, id) t.Run("invalid", func(t *testing.T) { @@ -51,18 +50,16 @@ func TestID_ReadFromV2(t *testing.T) { {name: "zero value", err: "invalid prefix byte 0x0, expected 0x35", val: make([]byte, user.IDSize)}, } { t.Run(tc.name, func(t *testing.T) { - var m refs.OwnerID - m.SetValue(tc.val) - require.EqualError(t, new(user.ID).ReadFromV2(m), tc.err) + m := &refs.OwnerID{Value: tc.val} + require.EqualError(t, new(user.ID).FromProtoMessage(m), tc.err) }) } }) } -func TestID_WriteToV2(t *testing.T) { +func TestID_ProtoMessage(t *testing.T) { id := usertest.ID() - var m refs.OwnerID - id.WriteToV2(&m) + m := id.ProtoMessage() require.Equal(t, id[:], m.GetValue()) } diff --git a/user/test/id_test.go b/user/test/id_test.go index 3d5e696a2..7c18c1b18 100644 --- a/user/test/id_test.go +++ b/user/test/id_test.go @@ -4,7 +4,6 @@ import ( "math/rand/v2" "testing" - "github.com/nspcc-dev/neofs-api-go/v2/refs" cidtest "github.com/nspcc-dev/neofs-sdk-go/container/id/test" neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto" neofsecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa" @@ -17,10 +16,9 @@ func TestID(t *testing.T) { id := usertest.ID() require.NotEqual(t, id, usertest.ID()) - var m refs.OwnerID - id.WriteToV2(&m) + m := id.ProtoMessage() var id2 user.ID - require.NoError(t, id2.ReadFromV2(m)) + require.NoError(t, id2.FromProtoMessage(m)) } func TestNIDs(t *testing.T) { diff --git a/version/version.go b/version/version.go index f79fb25fa..d714b1e6d 100644 --- a/version/version.go +++ b/version/version.go @@ -3,15 +3,17 @@ package version import ( "fmt" - "github.com/nspcc-dev/neofs-api-go/v2/refs" + neofsproto "github.com/nspcc-dev/neofs-sdk-go/internal/proto" + "github.com/nspcc-dev/neofs-sdk-go/proto/refs" + "google.golang.org/protobuf/encoding/protojson" ) // Version represents revision number in SemVer scheme. // // ID implements built-in comparable interface. // -// Version is mutually compatible with github.com/nspcc-dev/neofs-api-go/v2/refs.Version -// message. See ReadFromV2 / WriteToV2 methods. +// Version is mutually compatible with [refs.Version] message. See +// [Version.FromProtoMessage] / [Version.ProtoMessage] methods. // // Instances should be created using one of the constructors. type Version struct{ mjr, mnr uint32 } @@ -58,22 +60,24 @@ func (v *Version) SetMinor(val uint32) { v.mnr = val } -// WriteToV2 writes Version to the refs.Version message. -// The message must not be nil. +// ProtoMessage converts v into message to transmit using the NeoFS API +// protocol. // -// See also ReadFromV2. -func (v Version) WriteToV2(m *refs.Version) { - m.SetMajor(v.mjr) - m.SetMinor(v.mnr) +// See also [Version.FromProtoMessage]. +func (v Version) ProtoMessage() *refs.Version { + return &refs.Version{ + Major: v.mjr, + Minor: v.mnr, + } } -// ReadFromV2 reads Version from the refs.Version message. Checks if the message -// conforms to NeoFS API V2 protocol. +// FromProtoMessage validates m according to the NeoFS API protocol and restores +// v from it. // -// See also WriteToV2. -func (v *Version) ReadFromV2(m refs.Version) error { - v.mjr = m.GetMajor() - v.mnr = m.GetMinor() +// See also [Version.ProtoMessage]. +func (v *Version) FromProtoMessage(m *refs.Version) error { + v.mjr = m.Major + v.mnr = m.Minor return nil } @@ -103,10 +107,7 @@ func (v Version) Equal(v2 Version) bool { // // See also UnmarshalJSON. func (v Version) MarshalJSON() ([]byte, error) { - var m refs.Version - v.WriteToV2(&m) - - return m.MarshalJSON() + return neofsproto.MarshalMessageJSON(v.ProtoMessage()) } // UnmarshalJSON decodes NeoFS API protocol JSON format into the Version @@ -117,10 +118,10 @@ func (v Version) MarshalJSON() ([]byte, error) { func (v *Version) UnmarshalJSON(data []byte) error { var m refs.Version - err := m.UnmarshalJSON(data) + err := protojson.Unmarshal(data, &m) if err != nil { return err } - return v.ReadFromV2(m) + return v.FromProtoMessage(&m) } diff --git a/version/version_test.go b/version/version_test.go index ca1336415..8d2f169f0 100644 --- a/version/version_test.go +++ b/version/version_test.go @@ -4,7 +4,7 @@ import ( "math/rand/v2" "testing" - "github.com/nspcc-dev/neofs-api-go/v2/refs" + "github.com/nspcc-dev/neofs-sdk-go/proto/refs" "github.com/nspcc-dev/neofs-sdk-go/version" versiontest "github.com/nspcc-dev/neofs-sdk-go/version/test" "github.com/stretchr/testify/require" @@ -35,18 +35,17 @@ func testVersionField( t.Run("encoding", func(t *testing.T) { t.Run("api", func(t *testing.T) { var src, dst version.Version - var msg refs.Version set(&dst, val) - src.WriteToV2(&msg) - require.Zero(t, getAPI(&msg)) - require.NoError(t, dst.ReadFromV2(msg)) + msg := src.ProtoMessage() + require.Zero(t, getAPI(msg)) + require.NoError(t, dst.FromProtoMessage(msg)) require.Zero(t, get(dst)) set(&src, val) - src.WriteToV2(&msg) - require.EqualValues(t, val, getAPI(&msg)) - err := dst.ReadFromV2(msg) + msg = src.ProtoMessage() + require.EqualValues(t, val, getAPI(msg)) + err := dst.FromProtoMessage(msg) require.NoError(t, err) require.EqualValues(t, val, get(dst)) })