[#286] reputation: Refactor and document package functionality

Signed-off-by: Leonard Lyubich <leonard@nspcc.ru>
This commit is contained in:
Leonard Lyubich 2022-06-30 19:08:34 +03:00 committed by LeL
parent 30d27c3050
commit 041e1ef2b6
7 changed files with 655 additions and 596 deletions

View file

@ -65,10 +65,13 @@ func (c *Client) AnnounceLocalTrust(ctx context.Context, prm PrmAnnounceLocalTru
reqBody := new(v2reputation.AnnounceLocalTrustRequestBody)
reqBody.SetEpoch(prm.epoch)
trusts := make([]reputation.Trust, len(prm.trusts))
copy(trusts, prm.trusts)
trusts := make([]v2reputation.Trust, len(prm.trusts))
reqBody.SetTrusts(reputation.TrustsToV2(trusts))
for i := range prm.trusts {
prm.trusts[i].WriteToV2(&trusts[i])
}
reqBody.SetTrusts(trusts)
// form request
var req v2reputation.AnnounceLocalTrustRequest
@ -159,11 +162,14 @@ func (c *Client) AnnounceIntermediateTrust(ctx context.Context, prm PrmAnnounceI
panic("current trust value not set")
}
var trust v2reputation.PeerToPeerTrust
prm.trust.WriteToV2(&trust)
// form request body
reqBody := new(v2reputation.AnnounceIntermediateResultRequestBody)
reqBody.SetEpoch(prm.epoch)
reqBody.SetIteration(prm.iter)
reqBody.SetTrust(prm.trust.ToV2())
reqBody.SetTrust(&trust)
// form request
var req v2reputation.AnnounceIntermediateResultRequest

34
reputation/doc.go Normal file
View file

@ -0,0 +1,34 @@
/*
Package reputation collects functionality related to the NeoFS reputation system.
The functionality is based on the system described in the NeoFS specification.
Trust type represents simple instances of trust values. PeerToPeerTrust extends
Trust to support the direction of trust, i.e. from whom to whom. GlobalTrust
is designed as a global measure of trust in a network member. See the docs
for each type for details.
Instances can be also used to process NeoFS API V2 protocol messages
(see neo.fs.v2.reputation package in https://github.com/nspcc-dev/neofs-api).
On client side:
import "github.com/nspcc-dev/neofs-api-go/v2/reputation"
var msg reputation.GlobalTrust
trust.WriteToV2(&msg)
// send trust
On server side:
// recv msg
var trust reputation.GlobalTrust
trust.ReadFromV2(msg)
// process trust
Using package types in an application is recommended to potentially work with
different protocol versions with which these types are compatible.
*/
package reputation

View file

@ -2,82 +2,102 @@ package reputation
import (
"bytes"
"errors"
"fmt"
"github.com/mr-tron/base58"
"github.com/nspcc-dev/neofs-api-go/v2/reputation"
)
// PeerID represents peer ID compatible with NeoFS API v2.
type PeerID reputation.PeerID
// NewPeerID creates and returns blank PeerID.
// PeerID represents unique identifier of the peer participating in the NeoFS
// reputation system.
//
// Defaults:
// - publicKey: nil.
func NewPeerID() *PeerID {
return PeerIDFromV2(new(reputation.PeerID))
}
// PeerIDFromV2 converts NeoFS API v2 reputation.PeerID message to PeerID.
// ID is mutually compatible with github.com/nspcc-dev/neofs-api-go/v2/reputation.PeerID
// message. See ReadFromV2 / WriteToV2 methods.
//
// Nil reputation.PeerID converts to nil.
func PeerIDFromV2(id *reputation.PeerID) *PeerID {
return (*PeerID)(id)
// Instances can be created using built-in var declaration.
type PeerID struct {
m reputation.PeerID
}
// SetPublicKey sets peer ID as a compressed public key.
func (x *PeerID) SetPublicKey(v [33]byte) {
(*reputation.PeerID)(x).SetPublicKey(v[:])
}
// ToV2 converts PeerID to NeoFS API v2 reputation.PeerID message.
// ReadFromV2 reads PeerID from the reputation.PeerID message. Returns an
// error if the message is malformed according to the NeoFS API V2 protocol.
//
// Nil PeerID converts to nil.
func (x *PeerID) ToV2() *reputation.PeerID {
return (*reputation.PeerID)(x)
}
// Equal returns true if identifiers are identical.
func (x *PeerID) Equal(x2 *PeerID) bool {
return bytes.Equal(
(*reputation.PeerID)(x).GetPublicKey(),
(*reputation.PeerID)(x2).GetPublicKey(),
)
}
// Parse parses PeerID from base58 string.
func (x *PeerID) Parse(s string) error {
data, err := base58.Decode(s)
if err != nil {
return err
// See also WriteToV2.
func (x *PeerID) ReadFromV2(m reputation.PeerID) error {
val := m.GetPublicKey()
if len(val) == 0 {
return errors.New("missing ID bytes")
}
(*reputation.PeerID)(x).SetPublicKey(data)
x.m = m
return nil
}
// String returns base58 string representation of PeerID.
func (x *PeerID) String() string {
return base58.Encode((*reputation.PeerID)(x).GetPublicKey())
// WriteToV2 writes PeerID to the reputation.PeerID message.
// The message must not be nil.
//
// See also ReadFromV2.
func (x PeerID) WriteToV2(m *reputation.PeerID) {
*m = x.m
}
// Marshal marshals PeerID into a protobuf binary form.
func (x *PeerID) Marshal() ([]byte, error) {
return (*reputation.PeerID)(x).StableMarshal(nil), nil
// SetPublicKey sets PeerID as a binary-encoded public key which authenticates
// the participant of the NeoFS reputation system.
//
// Argument MUST NOT be mutated, make a copy first.
//
// See also CompareKey.
func (x *PeerID) SetPublicKey(key []byte) {
x.m.SetPublicKey(key)
}
// Unmarshal unmarshals protobuf binary representation of PeerID.
func (x *PeerID) Unmarshal(data []byte) error {
return (*reputation.PeerID)(x).Unmarshal(data)
// PublicKey return public key set using SetPublicKey.
//
// Zero PeerID has zero key which is incorrect according to NeoFS API
// protocol.
//
// Return value MUST NOT be mutated, make a copy first.
func (x PeerID) PublicKey() []byte {
return x.m.GetPublicKey()
}
// MarshalJSON encodes PeerID to protobuf JSON format.
func (x *PeerID) MarshalJSON() ([]byte, error) {
return (*reputation.PeerID)(x).MarshalJSON()
// ComparePeerKey checks if the given PeerID corresponds to the party
// authenticated by the given binary public key.
func ComparePeerKey(peer PeerID, key []byte) bool {
return bytes.Equal(peer.PublicKey(), key)
}
// UnmarshalJSON decodes PeerID from protobuf JSON format.
func (x *PeerID) UnmarshalJSON(data []byte) error {
return (*reputation.PeerID)(x).UnmarshalJSON(data)
// EncodeToString encodes ID into NeoFS API protocol string.
//
// Zero PeerID is base58 encoding of PeerIDSize zeros.
//
// See also DecodeString.
func (x PeerID) EncodeToString() string {
return base58.Encode(x.m.GetPublicKey())
}
// DecodeString decodes string into PeerID according to NeoFS API protocol.
// Returns an error if s is malformed.
//
// See also DecodeString.
func (x *PeerID) DecodeString(s string) error {
data, err := base58.Decode(s)
if err != nil {
return fmt.Errorf("decode base58: %w", err)
}
x.m.SetPublicKey(data)
return nil
}
// String implements fmt.Stringer.
//
// String is designed to be human-readable, and its format MAY differ between
// SDK versions. String MAY return same result as EncodeToString. String MUST NOT
// be used to encode ID into NeoFS protocol string.
func (x PeerID) String() string {
return x.EncodeToString()
}

View file

@ -3,86 +3,38 @@ package reputation_test
import (
"testing"
reputationV2 "github.com/nspcc-dev/neofs-api-go/v2/reputation"
v2reputation "github.com/nspcc-dev/neofs-api-go/v2/reputation"
"github.com/nspcc-dev/neofs-sdk-go/reputation"
reputationtest "github.com/nspcc-dev/neofs-sdk-go/reputation/test"
"github.com/stretchr/testify/require"
)
func TestPeerID_ToV2(t *testing.T) {
t.Run("nil", func(t *testing.T) {
var x *reputation.PeerID
func TestPeerID_PublicKey(t *testing.T) {
var val reputation.PeerID
require.Nil(t, x.ToV2())
})
require.Zero(t, val.PublicKey())
t.Run("nil", func(t *testing.T) {
peerID := reputationtest.PeerID()
key := []byte{3, 2, 1}
require.Equal(t, peerID, reputation.PeerIDFromV2(peerID.ToV2()))
})
val.SetPublicKey(key)
var m v2reputation.PeerID
val.WriteToV2(&m)
require.Equal(t, key, m.GetPublicKey())
var val2 reputation.PeerID
require.NoError(t, val2.ReadFromV2(m))
require.Equal(t, key, val.PublicKey())
require.True(t, reputation.ComparePeerKey(val, key))
}
func TestPeerID_String(t *testing.T) {
t.Run("Parse/String", func(t *testing.T) {
id := reputationtest.PeerID()
func TestPeerID_EncodeToString(t *testing.T) {
val := reputationtest.PeerID()
var val2 reputation.PeerID
strID := id.String()
id2 := reputation.NewPeerID()
err := id2.Parse(strID)
require.NoError(t, err)
require.Equal(t, id, id2)
})
t.Run("nil", func(t *testing.T) {
id := reputation.NewPeerID()
require.Empty(t, id.String())
})
}
func TestPeerIDEncoding(t *testing.T) {
id := reputationtest.PeerID()
t.Run("binary", func(t *testing.T) {
data, err := id.Marshal()
require.NoError(t, err)
id2 := reputation.NewPeerID()
require.NoError(t, id2.Unmarshal(data))
require.Equal(t, id, id2)
})
t.Run("json", func(t *testing.T) {
data, err := id.MarshalJSON()
require.NoError(t, err)
id2 := reputation.NewPeerID()
require.NoError(t, id2.UnmarshalJSON(data))
require.Equal(t, id, id2)
})
}
func TestPeerIDFromV2(t *testing.T) {
t.Run("from nil", func(t *testing.T) {
var x *reputationV2.PeerID
require.Nil(t, reputation.PeerIDFromV2(x))
})
}
func TestNewPeerID(t *testing.T) {
t.Run("default values", func(t *testing.T) {
id := reputation.NewPeerID()
// convert to v2 message
idV2 := id.ToV2()
require.Nil(t, idV2.GetPublicKey())
})
require.NoError(t, val2.DecodeString(val.EncodeToString()))
require.Equal(t, val, val2)
}

View file

@ -1,58 +1,58 @@
package reputationtest
import (
"testing"
"fmt"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
neofsecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa"
"github.com/nspcc-dev/neofs-sdk-go/reputation"
"github.com/stretchr/testify/require"
)
func PeerID() *reputation.PeerID {
v := reputation.NewPeerID()
func PeerID() (v reputation.PeerID) {
p, err := keys.NewPrivateKey()
if err != nil {
panic(err)
}
key := [33]byte{}
copy(key[:], p.Bytes())
v.SetPublicKey(key)
v.SetPublicKey(p.PublicKey().Bytes())
return v
return
}
func Trust() *reputation.Trust {
v := reputation.NewTrust()
func Trust() (v reputation.Trust) {
v.SetPeer(PeerID())
v.SetValue(1.5)
v.SetValue(0.5)
return v
return
}
func PeerToPeerTrust() *reputation.PeerToPeerTrust {
v := reputation.NewPeerToPeerTrust()
func PeerToPeerTrust() (v reputation.PeerToPeerTrust) {
v.SetTrustingPeer(PeerID())
v.SetTrust(Trust())
return v
return
}
func GlobalTrust() *reputation.GlobalTrust {
v := reputation.NewGlobalTrust()
func GlobalTrust() (v reputation.GlobalTrust) {
v.Init()
v.SetManager(PeerID())
v.SetTrust(Trust())
return v
return
}
func SignedGlobalTrust(t testing.TB) *reputation.GlobalTrust {
func SignedGlobalTrust() reputation.GlobalTrust {
gt := GlobalTrust()
p, err := keys.NewPrivateKey()
require.NoError(t, err)
require.NoError(t, gt.Sign(&p.PrivateKey))
if err != nil {
panic(fmt.Sprintf("unexpected error from key creator: %v", err))
}
err = gt.Sign(neofsecdsa.Signer(p.PrivateKey))
if err != nil {
panic(fmt.Sprintf("unexpected error from GlobalTrust.Sign: %v", err))
}
return gt
}

View file

@ -1,327 +1,416 @@
package reputation
import (
"crypto/ecdsa"
"errors"
"fmt"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
"github.com/nspcc-dev/neofs-api-go/v2/reputation"
neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto"
neofsecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa"
"github.com/nspcc-dev/neofs-sdk-go/version"
)
// Trust represents peer's trust compatible with NeoFS API v2.
type Trust reputation.Trust
// NewTrust creates and returns blank Trust.
// Trust represents quantitative assessment of the trust of a participant in the
// NeoFS reputation system.
//
// Defaults:
// - value: 0;
// - PeerID: nil.
func NewTrust() *Trust {
return TrustFromV2(new(reputation.Trust))
// Trust is mutually compatible with github.com/nspcc-dev/neofs-api-go/v2/reputation.Trust
// message. See ReadFromV2 / WriteToV2 methods.
//
// Instances can be created using built-in var declaration.
type Trust struct {
m reputation.Trust
}
// TrustFromV2 converts NeoFS API v2
// reputation.Trust message structure to Trust.
// ReadFromV2 reads Trust from the reputation.Trust message. Returns an
// error if the message is malformed according to the NeoFS API V2 protocol.
//
// Nil reputation.Trust converts to nil.
func TrustFromV2(t *reputation.Trust) *Trust {
return (*Trust)(t)
// See also WriteToV2.
func (x *Trust) ReadFromV2(m reputation.Trust) error {
if val := m.GetValue(); val < 0 || val > 1 {
return fmt.Errorf("invalid trust value %v", val)
}
peerV2 := m.GetPeer()
if peerV2 == nil {
return errors.New("missing peer field")
}
var peer PeerID
err := peer.ReadFromV2(*peerV2)
if err != nil {
return fmt.Errorf("invalid peer field: %w", err)
}
x.m = m
return nil
}
// ToV2 converts Trust to NeoFS API v2
// reputation.Trust message structure.
// WriteToV2 writes Trust to the reputation.Trust message.
// The message must not be nil.
//
// Nil Trust converts to nil.
func (x *Trust) ToV2() *reputation.Trust {
return (*reputation.Trust)(x)
// See also ReadFromV2.
func (x Trust) WriteToV2(m *reputation.Trust) {
*m = x.m
}
// TrustsToV2 converts slice of Trust's to slice of
// NeoFS API v2 reputation.Trust message structures.
func TrustsToV2(xs []Trust) (res []reputation.Trust) {
if xs != nil {
res = make([]reputation.Trust, len(xs))
// SetPeer specifies identifier of the participant of the NeoFS reputation system
// to which the Trust relates.
//
// See also Peer.
func (x *Trust) SetPeer(id PeerID) {
var m reputation.PeerID
id.WriteToV2(&m)
for i := range xs {
res[i] = *xs[i].ToV2()
x.m.SetPeer(&m)
}
// Peer returns peer identifier set using SetPeer.
//
// Zero Trust returns zero PeerID which is incorrect according to the NeoFS API
// protocol.
func (x Trust) Peer() (res PeerID) {
m := x.m.GetPeer()
if m != nil {
err := res.ReadFromV2(*m)
if err != nil {
panic(fmt.Sprintf("unexpected error from ReadFromV2: %v", err))
}
}
return
}
// SetPeer sets trusted peer ID.
func (x *Trust) SetPeer(id *PeerID) {
(*reputation.Trust)(x).SetPeer(id.ToV2())
}
// Peer returns trusted peer ID.
func (x *Trust) Peer() *PeerID {
return PeerIDFromV2(
(*reputation.Trust)(x).GetPeer())
}
// SetValue sets trust value.
// SetValue sets the Trust value. Value MUST be in range [0;1].
//
// See also Value.
func (x *Trust) SetValue(val float64) {
(*reputation.Trust)(x).SetValue(val)
}
// Value returns trust value.
func (x *Trust) Value() float64 {
return (*reputation.Trust)(x).GetValue()
}
// Marshal marshals Trust into a protobuf binary form.
func (x *Trust) Marshal() ([]byte, error) {
return (*reputation.Trust)(x).StableMarshal(nil), nil
}
// Unmarshal unmarshals protobuf binary representation of Trust.
func (x *Trust) Unmarshal(data []byte) error {
return (*reputation.Trust)(x).Unmarshal(data)
}
// MarshalJSON encodes Trust to protobuf JSON format.
func (x *Trust) MarshalJSON() ([]byte, error) {
return (*reputation.Trust)(x).MarshalJSON()
}
// UnmarshalJSON decodes Trust from protobuf JSON format.
func (x *Trust) UnmarshalJSON(data []byte) error {
return (*reputation.Trust)(x).UnmarshalJSON(data)
}
// PeerToPeerTrust represents directed peer-to-peer trust
// compatible with NeoFS API v2.
type PeerToPeerTrust reputation.PeerToPeerTrust
// NewPeerToPeerTrust creates and returns blank PeerToPeerTrust.
//
// Defaults:
// - trusting: nil;
// - trust: nil.
func NewPeerToPeerTrust() *PeerToPeerTrust {
return PeerToPeerTrustFromV2(new(reputation.PeerToPeerTrust))
}
// PeerToPeerTrustFromV2 converts NeoFS API v2
// reputation.PeerToPeerTrust message structure to PeerToPeerTrust.
//
// Nil reputation.PeerToPeerTrust converts to nil.
func PeerToPeerTrustFromV2(t *reputation.PeerToPeerTrust) *PeerToPeerTrust {
return (*PeerToPeerTrust)(t)
}
// ToV2 converts PeerToPeerTrust to NeoFS API v2
// reputation.PeerToPeerTrust message structure.
//
// Nil PeerToPeerTrust converts to nil.
func (x *PeerToPeerTrust) ToV2() *reputation.PeerToPeerTrust {
return (*reputation.PeerToPeerTrust)(x)
}
// SetTrustingPeer sets trusting peer ID.
func (x *PeerToPeerTrust) SetTrustingPeer(id *PeerID) {
(*reputation.PeerToPeerTrust)(x).SetTrustingPeer(id.ToV2())
}
// TrustingPeer returns trusting peer ID.
func (x *PeerToPeerTrust) TrustingPeer() *PeerID {
return PeerIDFromV2(
(*reputation.PeerToPeerTrust)(x).
GetTrustingPeer(),
)
}
// SetTrust sets trust value of the trusting peer to the trusted one.
func (x *PeerToPeerTrust) SetTrust(t *Trust) {
(*reputation.PeerToPeerTrust)(x).SetTrust(t.ToV2())
}
// Trust returns trust value of the trusting peer to the trusted one.
func (x *PeerToPeerTrust) Trust() *Trust {
return TrustFromV2(
(*reputation.PeerToPeerTrust)(x).GetTrust())
}
// Marshal marshals PeerToPeerTrust into a protobuf binary form.
func (x *PeerToPeerTrust) Marshal() ([]byte, error) {
return (*reputation.PeerToPeerTrust)(x).StableMarshal(nil), nil
}
// Unmarshal unmarshals protobuf binary representation of PeerToPeerTrust.
func (x *PeerToPeerTrust) Unmarshal(data []byte) error {
return (*reputation.PeerToPeerTrust)(x).Unmarshal(data)
}
// MarshalJSON encodes PeerToPeerTrust to protobuf JSON format.
func (x *PeerToPeerTrust) MarshalJSON() ([]byte, error) {
return (*reputation.PeerToPeerTrust)(x).MarshalJSON()
}
// UnmarshalJSON decodes PeerToPeerTrust from protobuf JSON format.
func (x *PeerToPeerTrust) UnmarshalJSON(data []byte) error {
return (*reputation.PeerToPeerTrust)(x).UnmarshalJSON(data)
}
// GlobalTrust represents peer's global trust compatible with NeoFS API v2.
type GlobalTrust reputation.GlobalTrust
// NewGlobalTrust creates and returns blank GlobalTrust.
//
// Defaults:
// - version: version.Current();
// - manager: nil;
// - trust: nil.
func NewGlobalTrust() *GlobalTrust {
gt := GlobalTrustFromV2(new(reputation.GlobalTrust))
ver := version.Current()
gt.SetVersion(&ver)
return gt
}
// GlobalTrustFromV2 converts NeoFS API v2
// reputation.GlobalTrust message structure to GlobalTrust.
//
// Nil reputation.GlobalTrust converts to nil.
func GlobalTrustFromV2(t *reputation.GlobalTrust) *GlobalTrust {
return (*GlobalTrust)(t)
}
// ToV2 converts GlobalTrust to NeoFS API v2
// reputation.GlobalTrust message structure.
//
// Nil GlobalTrust converts to nil.
func (x *GlobalTrust) ToV2() *reputation.GlobalTrust {
return (*reputation.GlobalTrust)(x)
}
// SetVersion sets GlobalTrust's protocol version.
func (x *GlobalTrust) SetVersion(version *version.Version) {
var verV2 refs.Version
version.WriteToV2(&verV2)
(*reputation.GlobalTrust)(x).SetVersion(&verV2)
}
// Version returns GlobalTrust's protocol version.
func (x *GlobalTrust) Version() *version.Version {
var ver version.Version
if verV2 := (*reputation.GlobalTrust)(x).GetVersion(); verV2 != nil {
ver.ReadFromV2(*verV2)
if val < 0 || val > 1 {
panic(fmt.Sprintf("trust value is out-of-range %v", val))
}
return &ver
x.m.SetValue(val)
}
// Value returns value set using SetValue.
//
// Zero Trust has zero value.
func (x Trust) Value() float64 {
return x.m.GetValue()
}
// PeerToPeerTrust represents trust of one participant of the NeoFS reputation
// system to another one.
//
// Trust is mutually compatible with github.com/nspcc-dev/neofs-api-go/v2/reputation.PeerToPeerTrust
// message. See ReadFromV2 / WriteToV2 methods.
//
// Instances can be created using built-in var declaration.
type PeerToPeerTrust struct {
m reputation.PeerToPeerTrust
}
// ReadFromV2 reads PeerToPeerTrust from the reputation.PeerToPeerTrust message.
// Returns an error if the message is malformed according to the NeoFS API V2
// protocol.
//
// See also WriteToV2.
func (x *PeerToPeerTrust) ReadFromV2(m reputation.PeerToPeerTrust) error {
trustingV2 := m.GetTrustingPeer()
if trustingV2 == nil {
return errors.New("missing trusting peer")
}
var trusting PeerID
err := trusting.ReadFromV2(*trustingV2)
if err != nil {
return fmt.Errorf("invalid trusting peer: %w", err)
}
trustV2 := m.GetTrust()
if trustV2 == nil {
return errors.New("missing trust")
}
var trust Trust
err = trust.ReadFromV2(*trustV2)
if err != nil {
return fmt.Errorf("invalid trust: %w", err)
}
x.m = m
return nil
}
// WriteToV2 writes PeerToPeerTrust to the reputation.PeerToPeerTrust message.
// The message must not be nil.
//
// See also ReadFromV2.
func (x PeerToPeerTrust) WriteToV2(m *reputation.PeerToPeerTrust) {
*m = x.m
}
// SetTrustingPeer specifies the peer from which trust comes in terms of the
// NeoFS reputation system.
//
// See also TrustingPeer.
func (x *PeerToPeerTrust) SetTrustingPeer(id PeerID) {
var m reputation.PeerID
id.WriteToV2(&m)
x.m.SetTrustingPeer(&m)
}
// TrustingPeer returns peer set using SetTrustingPeer.
//
// Zero PeerToPeerTrust has no trusting peer which is incorrect according
// to the NeoFS API protocol.
func (x PeerToPeerTrust) TrustingPeer() (res PeerID) {
m := x.m.GetTrustingPeer()
if m != nil {
err := res.ReadFromV2(*m)
if err != nil {
panic(fmt.Sprintf("unexpected error from PeerID.ReadFromV2: %v", err))
}
}
return
}
// SetTrust sets trust value of the trusting peer to another participant
// of the NeoFS reputation system.
//
// See also Trust.
func (x *PeerToPeerTrust) SetTrust(t Trust) {
var tV2 reputation.Trust
t.WriteToV2(&tV2)
x.m.SetTrust(&tV2)
}
// Trust returns trust set using SetTrust.
//
// Zero PeerToPeerTrust returns zero Trust which is incorect according to the
// NeoFS API protocol.
func (x PeerToPeerTrust) Trust() (res Trust) {
m := x.m.GetTrust()
if m != nil {
err := res.ReadFromV2(*m)
if err != nil {
panic(fmt.Sprintf("unexpected error from Trust.ReadFromV2: %v", err))
}
}
return
}
// GlobalTrust represents the final assessment of trust in the participant of
// the NeoFS reputation system obtained taking into account all other participants.
//
// GlobalTrust is mutually compatible with github.com/nspcc-dev/neofs-api-go/v2/reputation.GlobalTrust
// message. See ReadFromV2 / WriteToV2 methods.
//
// Instances can be created using built-in var declaration.
type GlobalTrust struct {
m reputation.GlobalTrust
}
// ReadFromV2 reads GlobalTrust from the reputation.GlobalTrust message.
// Returns an error if the message is malformed according to the NeoFS API V2
// protocol.
//
// See also WriteToV2.
func (x *GlobalTrust) ReadFromV2(m reputation.GlobalTrust) error {
if m.GetVersion() == nil {
return errors.New("missing version")
}
if m.GetSignature() == nil {
return errors.New("missing signature")
}
body := m.GetBody()
if body == nil {
return errors.New("missing body")
}
managerV2 := body.GetManager()
if managerV2 == nil {
return errors.New("missing manager")
}
var manager PeerID
err := manager.ReadFromV2(*managerV2)
if err != nil {
return fmt.Errorf("invalid manager: %w", err)
}
trustV2 := body.GetTrust()
if trustV2 == nil {
return errors.New("missing trust")
}
var trust Trust
err = trust.ReadFromV2(*trustV2)
if err != nil {
return fmt.Errorf("invalid trust: %w", err)
}
x.m = m
return nil
}
// WriteToV2 writes GlobalTrust to the reputation.GlobalTrust message.
// The message must not be nil.
//
// See also ReadFromV2.
func (x GlobalTrust) WriteToV2(m *reputation.GlobalTrust) {
*m = x.m
}
// Init initializes all internal data of the GlobalTrust required by NeoFS API
// protocol. Init MUST be called when creating a new global trust instance.
// Init SHOULD NOT be called multiple times. Init SHOULD NOT be called if
// the GlobalTrust instance is used for decoding only.
func (x *GlobalTrust) Init() {
var ver refs.Version
version.Current().WriteToV2(&ver)
x.m.SetVersion(&ver)
}
func (x *GlobalTrust) setBodyField(setter func(*reputation.GlobalTrustBody)) {
if x != nil {
v2 := (*reputation.GlobalTrust)(x)
body := v2.GetBody()
body := x.m.GetBody()
if body == nil {
body = new(reputation.GlobalTrustBody)
v2.SetBody(body)
x.m.SetBody(body)
}
setter(body)
}
}
// SetManager sets node manager ID.
func (x *GlobalTrust) SetManager(id *PeerID) {
// SetManager sets identifier of the NeoFS reputation system's participant which
// performed trust estimation.
//
// See also Manager.
func (x *GlobalTrust) SetManager(id PeerID) {
var m reputation.PeerID
id.WriteToV2(&m)
x.setBodyField(func(body *reputation.GlobalTrustBody) {
body.SetManager(id.ToV2())
body.SetManager(&m)
})
}
// Manager returns node manager ID.
func (x *GlobalTrust) Manager() *PeerID {
return PeerIDFromV2(
(*reputation.GlobalTrust)(x).
GetBody().
GetManager(),
)
}
// SetTrust sets global trust value.
func (x *GlobalTrust) SetTrust(trust *Trust) {
x.setBodyField(func(body *reputation.GlobalTrustBody) {
body.SetTrust(trust.ToV2())
})
}
// Trust returns global trust value.
func (x *GlobalTrust) Trust() *Trust {
return TrustFromV2(
(*reputation.GlobalTrust)(x).
GetBody().
GetTrust(),
)
}
// Sign signs global trust value with key.
func (x *GlobalTrust) Sign(key *ecdsa.PrivateKey) error {
if key == nil {
return errors.New("nil private key")
// Manager returns peer set using SetManager.
//
// Zero GlobalTrust has zero manager which is incorrect according to the
// NeoFS API protocol.
func (x GlobalTrust) Manager() (res PeerID) {
m := x.m.GetBody().GetManager()
if m != nil {
err := res.ReadFromV2(*m)
if err != nil {
panic(fmt.Sprintf("unexpected error from ReadFromV2: %v", err))
}
}
m := (*reputation.GlobalTrust)(x)
return
}
// SetTrust sets the global trust score of the network to a specific network
// member.
//
// See also Trust.
func (x *GlobalTrust) SetTrust(trust Trust) {
var m reputation.Trust
trust.WriteToV2(&m)
x.setBodyField(func(body *reputation.GlobalTrustBody) {
body.SetTrust(&m)
})
}
// Trust returns trust set using SetTrust.
//
// Zero GlobalTrust return zero Trust which is incorrect according to the
// NeoFS API protocol.
func (x GlobalTrust) Trust() (res Trust) {
m := x.m.GetBody().GetTrust()
if m != nil {
err := res.ReadFromV2(*m)
if err != nil {
panic(fmt.Sprintf("unexpected error from ReadFromV2: %v", err))
}
}
return
}
// Sign calculates and writes signature of the GlobalTrust data. Returns
// signature calculation errors.
//
// Zero GlobalTrust is unsigned.
//
// Note that any GlobalTrust mutation is likely to break the signature, so it is
// expected to be calculated as a final stage of GlobalTrust formation.
//
// See also VerifySignature.
func (x *GlobalTrust) Sign(signer neofscrypto.Signer) error {
var sig neofscrypto.Signature
err := sig.Calculate(neofsecdsa.Signer(*key), m.GetBody().StableMarshal(nil))
err := sig.Calculate(signer, x.m.GetBody().StableMarshal(nil))
if err != nil {
return fmt.Errorf("calculate signature: %w", err)
}
var sigv2 refs.Signature
sig.WriteToV2(&sigv2)
m.SetSignature(&sigv2)
x.m.SetSignature(&sigv2)
return nil
}
// VerifySignature verifies global trust signature.
func (x *GlobalTrust) VerifySignature() error {
m := (*reputation.GlobalTrust)(x)
sigV2 := m.GetSignature()
// VerifySignature checks if GlobalTrust signature is presented and valid.
//
// Zero GlobalTrust fails the check.
//
// See also Sign.
func (x GlobalTrust) VerifySignature() bool {
sigV2 := x.m.GetSignature()
if sigV2 == nil {
return errors.New("missing signature")
return false
}
var sig neofscrypto.Signature
sig.ReadFromV2(*sigV2)
if !sig.Verify(m.GetBody().StableMarshal(nil)) {
return errors.New("wrong signature")
}
return nil
return sig.Verify(x.m.GetBody().StableMarshal(nil))
}
// Marshal marshals GlobalTrust into a protobuf binary form.
func (x *GlobalTrust) Marshal() ([]byte, error) {
return (*reputation.GlobalTrust)(x).StableMarshal(nil), nil
// Marshal encodes GlobalTrust into a binary format of the NeoFS API protocol
// (Protocol Buffers with direct field order).
//
// See also Unmarshal.
func (x GlobalTrust) Marshal() []byte {
return x.m.StableMarshal(nil)
}
// Unmarshal unmarshals protobuf binary representation of GlobalTrust.
// Unmarshal decodes NeoFS API protocol binary format into the GlobalTrust
// (Protocol Buffers with direct field order). Returns an error describing
// a format violation.
//
// See also Marshal.
func (x *GlobalTrust) Unmarshal(data []byte) error {
return (*reputation.GlobalTrust)(x).Unmarshal(data)
}
// MarshalJSON encodes GlobalTrust to protobuf JSON format.
func (x *GlobalTrust) MarshalJSON() ([]byte, error) {
return (*reputation.GlobalTrust)(x).MarshalJSON()
}
// UnmarshalJSON decodes GlobalTrust from protobuf JSON format.
func (x *GlobalTrust) UnmarshalJSON(data []byte) error {
return (*reputation.GlobalTrust)(x).UnmarshalJSON(data)
return x.m.Unmarshal(data)
}

View file

@ -3,248 +3,206 @@ package reputation_test
import (
"testing"
reputationV2 "github.com/nspcc-dev/neofs-api-go/v2/reputation"
reputationtestV2 "github.com/nspcc-dev/neofs-api-go/v2/reputation/test"
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
"github.com/nspcc-dev/neofs-api-go/v2/refs"
v2reputation "github.com/nspcc-dev/neofs-api-go/v2/reputation"
neofsecdsa "github.com/nspcc-dev/neofs-sdk-go/crypto/ecdsa"
"github.com/nspcc-dev/neofs-sdk-go/reputation"
reputationtest "github.com/nspcc-dev/neofs-sdk-go/reputation/test"
"github.com/nspcc-dev/neofs-sdk-go/version"
"github.com/stretchr/testify/require"
)
func TestTrust(t *testing.T) {
trust := reputation.NewTrust()
func TestTrust_Peer(t *testing.T) {
var trust reputation.Trust
id := reputationtest.PeerID()
trust.SetPeer(id)
require.Equal(t, id, trust.Peer())
require.Zero(t, trust.Peer())
val := 1.5
trust.SetValue(val)
require.Equal(t, val, trust.Value())
trust = reputationtest.Trust()
t.Run("binary encoding", func(t *testing.T) {
trust := reputationtest.Trust()
data, err := trust.Marshal()
require.NoError(t, err)
peer := reputationtest.PeerID()
trust2 := reputation.NewTrust()
require.NoError(t, trust2.Unmarshal(data))
require.Equal(t, trust, trust2)
})
trust.SetPeer(peer)
t.Run("JSON encoding", func(t *testing.T) {
trust := reputationtest.Trust()
data, err := trust.MarshalJSON()
require.NoError(t, err)
var peerV2 v2reputation.PeerID
peer.WriteToV2(&peerV2)
trust2 := reputation.NewTrust()
require.NoError(t, trust2.UnmarshalJSON(data))
require.Equal(t, trust, trust2)
})
var trustV2 v2reputation.Trust
trust.WriteToV2(&trustV2)
require.Equal(t, &peerV2, trustV2.GetPeer())
var val2 reputation.Trust
require.NoError(t, val2.ReadFromV2(trustV2))
require.Equal(t, peer, val2.Peer())
}
func TestPeerToPeerTrust(t *testing.T) {
t.Run("v2", func(t *testing.T) {
p2ptV2 := reputationtestV2.GeneratePeerToPeerTrust(false)
func TestTrust_Value(t *testing.T) {
var val reputation.Trust
p2pt := reputation.PeerToPeerTrustFromV2(p2ptV2)
require.Zero(t, val.Value())
require.Equal(t, p2ptV2, p2pt.ToV2())
})
val = reputationtest.Trust()
t.Run("getters+setters", func(t *testing.T) {
p2pt := reputation.NewPeerToPeerTrust()
const value = 0.75
require.Nil(t, p2pt.TrustingPeer())
require.Nil(t, p2pt.Trust())
val.SetValue(value)
trusting := reputationtest.PeerID()
p2pt.SetTrustingPeer(trusting)
require.Equal(t, trusting, p2pt.TrustingPeer())
var trustV2 v2reputation.Trust
val.WriteToV2(&trustV2)
require.EqualValues(t, value, trustV2.GetValue())
var val2 reputation.Trust
require.NoError(t, val2.ReadFromV2(trustV2))
require.EqualValues(t, value, val2.Value())
}
func TestPeerToPeerTrust_TrustingPeer(t *testing.T) {
var val reputation.PeerToPeerTrust
require.Zero(t, val.TrustingPeer())
val = reputationtest.PeerToPeerTrust()
peer := reputationtest.PeerID()
val.SetTrustingPeer(peer)
var peerV2 v2reputation.PeerID
peer.WriteToV2(&peerV2)
var trustV2 v2reputation.PeerToPeerTrust
val.WriteToV2(&trustV2)
require.Equal(t, &peerV2, trustV2.GetTrustingPeer())
var val2 reputation.PeerToPeerTrust
require.NoError(t, val2.ReadFromV2(trustV2))
require.Equal(t, peer, val2.TrustingPeer())
}
func TestPeerToPeerTrust_Trust(t *testing.T) {
var val reputation.PeerToPeerTrust
require.Zero(t, val.Trust())
val = reputationtest.PeerToPeerTrust()
trust := reputationtest.Trust()
p2pt.SetTrust(trust)
require.Equal(t, trust, p2pt.Trust())
})
t.Run("encoding", func(t *testing.T) {
p2pt := reputationtest.PeerToPeerTrust()
val.SetTrust(trust)
var trustV2 v2reputation.Trust
trust.WriteToV2(&trustV2)
var valV2 v2reputation.PeerToPeerTrust
val.WriteToV2(&valV2)
require.Equal(t, &trustV2, valV2.GetTrust())
var val2 reputation.PeerToPeerTrust
require.NoError(t, val2.ReadFromV2(valV2))
require.Equal(t, trust, val2.Trust())
}
func TestGlobalTrust_Init(t *testing.T) {
var val reputation.GlobalTrust
val.Init()
var valV2 v2reputation.GlobalTrust
val.WriteToV2(&valV2)
var verV2 refs.Version
version.Current().WriteToV2(&verV2)
require.Equal(t, &verV2, valV2.GetVersion())
}
func TestGlobalTrust_Manager(t *testing.T) {
var val reputation.GlobalTrust
require.Zero(t, val.Manager())
val = reputationtest.SignedGlobalTrust()
peer := reputationtest.PeerID()
val.SetManager(peer)
var peerV2 v2reputation.PeerID
peer.WriteToV2(&peerV2)
var trustV2 v2reputation.GlobalTrust
val.WriteToV2(&trustV2)
require.Equal(t, &peerV2, trustV2.GetBody().GetManager())
var val2 reputation.GlobalTrust
require.NoError(t, val2.ReadFromV2(trustV2))
require.Equal(t, peer, val2.Manager())
}
func TestGlobalTrust_Trust(t *testing.T) {
var val reputation.GlobalTrust
require.Zero(t, val.Trust())
val = reputationtest.SignedGlobalTrust()
trust := reputationtest.Trust()
val.SetTrust(trust)
var trustV2 v2reputation.Trust
trust.WriteToV2(&trustV2)
var valV2 v2reputation.GlobalTrust
val.WriteToV2(&valV2)
require.Equal(t, &trustV2, valV2.GetBody().GetTrust())
var val2 reputation.GlobalTrust
require.NoError(t, val2.ReadFromV2(valV2))
require.Equal(t, trust, val2.Trust())
}
func TestGlobalTrust_Sign(t *testing.T) {
k, err := keys.NewPrivateKey()
require.NoError(t, err)
val := reputationtest.GlobalTrust()
require.False(t, val.VerifySignature())
require.NoError(t, val.Sign(neofsecdsa.Signer(k.PrivateKey)))
var valV2 v2reputation.GlobalTrust
val.WriteToV2(&valV2)
require.NotZero(t, valV2.GetSignature())
var val2 reputation.GlobalTrust
require.NoError(t, val2.ReadFromV2(valV2))
require.True(t, val2.VerifySignature())
}
func TestGlobalTrustEncoding(t *testing.T) {
val := reputationtest.SignedGlobalTrust()
t.Run("binary", func(t *testing.T) {
data, err := p2pt.Marshal()
require.NoError(t, err)
data := val.Marshal()
p2pt2 := reputation.NewPeerToPeerTrust()
require.NoError(t, p2pt2.Unmarshal(data))
require.Equal(t, p2pt, p2pt2)
})
var val2 reputation.GlobalTrust
require.NoError(t, val2.Unmarshal(data))
t.Run("JSON", func(t *testing.T) {
data, err := p2pt.MarshalJSON()
require.NoError(t, err)
p2pt2 := reputation.NewPeerToPeerTrust()
require.NoError(t, p2pt2.UnmarshalJSON(data))
require.Equal(t, p2pt, p2pt2)
})
})
}
func TestGlobalTrust(t *testing.T) {
t.Run("v2", func(t *testing.T) {
gtV2 := reputationtestV2.GenerateGlobalTrust(false)
gt := reputation.GlobalTrustFromV2(gtV2)
require.Equal(t, gtV2, gt.ToV2())
})
t.Run("getters+setters", func(t *testing.T) {
gt := reputation.NewGlobalTrust()
require.Equal(t, version.Current(), *gt.Version())
require.Nil(t, gt.Manager())
require.Nil(t, gt.Trust())
var ver version.Version
ver.SetMajor(13)
ver.SetMinor(31)
gt.SetVersion(&ver)
require.Equal(t, ver, *gt.Version())
mngr := reputationtest.PeerID()
gt.SetManager(mngr)
require.Equal(t, mngr, gt.Manager())
trust := reputationtest.Trust()
gt.SetTrust(trust)
require.Equal(t, trust, gt.Trust())
})
t.Run("sign+verify", func(t *testing.T) {
gt := reputationtest.SignedGlobalTrust(t)
err := gt.VerifySignature()
require.NoError(t, err)
})
t.Run("encoding", func(t *testing.T) {
t.Run("binary", func(t *testing.T) {
gt := reputationtest.SignedGlobalTrust(t)
data, err := gt.Marshal()
require.NoError(t, err)
gt2 := reputation.NewGlobalTrust()
require.NoError(t, gt2.Unmarshal(data))
require.Equal(t, gt, gt2)
})
t.Run("JSON", func(t *testing.T) {
gt := reputationtest.SignedGlobalTrust(t)
data, err := gt.MarshalJSON()
require.NoError(t, err)
gt2 := reputation.NewGlobalTrust()
require.NoError(t, gt2.UnmarshalJSON(data))
require.Equal(t, gt, gt2)
})
})
}
func TestTrustFromV2(t *testing.T) {
t.Run("from nil", func(t *testing.T) {
var x *reputationV2.Trust
require.Nil(t, reputation.TrustFromV2(x))
})
}
func TestPeerToPeerTrustFromV2(t *testing.T) {
t.Run("from nil", func(t *testing.T) {
var x *reputationV2.PeerToPeerTrust
require.Nil(t, reputation.PeerToPeerTrustFromV2(x))
})
}
func TestGlobalTrustFromV2(t *testing.T) {
t.Run("from nil", func(t *testing.T) {
var x *reputationV2.GlobalTrust
require.Nil(t, reputation.GlobalTrustFromV2(x))
})
}
func TestTrust_ToV2(t *testing.T) {
t.Run("nil", func(t *testing.T) {
var x *reputation.Trust
require.Nil(t, x.ToV2())
})
}
func TestPeerToPeerTrust_ToV2(t *testing.T) {
t.Run("nil", func(t *testing.T) {
var x *reputation.PeerToPeerTrust
require.Nil(t, x.ToV2())
})
}
func TestGlobalTrust_ToV2(t *testing.T) {
t.Run("nil", func(t *testing.T) {
var x *reputation.GlobalTrust
require.Nil(t, x.ToV2())
})
}
func TestNewTrust(t *testing.T) {
t.Run("default values", func(t *testing.T) {
trust := reputation.NewTrust()
// check initial values
require.Zero(t, trust.Value())
require.Nil(t, trust.Peer())
// convert to v2 message
trustV2 := trust.ToV2()
require.Zero(t, trustV2.GetValue())
require.Nil(t, trustV2.GetPeer())
})
}
func TestNewPeerToPeerTrust(t *testing.T) {
t.Run("default values", func(t *testing.T) {
trust := reputation.NewPeerToPeerTrust()
// check initial values
require.Nil(t, trust.Trust())
require.Nil(t, trust.TrustingPeer())
// convert to v2 message
trustV2 := trust.ToV2()
require.Nil(t, trustV2.GetTrust())
require.Nil(t, trustV2.GetTrustingPeer())
})
}
func TestNewGlobalTrust(t *testing.T) {
t.Run("default values", func(t *testing.T) {
trust := reputation.NewGlobalTrust()
// check initial values
require.Nil(t, trust.Manager())
require.Nil(t, trust.Trust())
require.Equal(t, version.Current(), *trust.Version())
// convert to v2 message
trustV2 := trust.ToV2()
require.Nil(t, trustV2.GetBody())
require.Equal(t, val, val2)
})
}