[#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 := new(v2reputation.AnnounceLocalTrustRequestBody)
reqBody.SetEpoch(prm.epoch) reqBody.SetEpoch(prm.epoch)
trusts := make([]reputation.Trust, len(prm.trusts)) trusts := make([]v2reputation.Trust, len(prm.trusts))
copy(trusts, prm.trusts)
reqBody.SetTrusts(reputation.TrustsToV2(trusts)) for i := range prm.trusts {
prm.trusts[i].WriteToV2(&trusts[i])
}
reqBody.SetTrusts(trusts)
// form request // form request
var req v2reputation.AnnounceLocalTrustRequest var req v2reputation.AnnounceLocalTrustRequest
@ -159,11 +162,14 @@ func (c *Client) AnnounceIntermediateTrust(ctx context.Context, prm PrmAnnounceI
panic("current trust value not set") panic("current trust value not set")
} }
var trust v2reputation.PeerToPeerTrust
prm.trust.WriteToV2(&trust)
// form request body // form request body
reqBody := new(v2reputation.AnnounceIntermediateResultRequestBody) reqBody := new(v2reputation.AnnounceIntermediateResultRequestBody)
reqBody.SetEpoch(prm.epoch) reqBody.SetEpoch(prm.epoch)
reqBody.SetIteration(prm.iter) reqBody.SetIteration(prm.iter)
reqBody.SetTrust(prm.trust.ToV2()) reqBody.SetTrust(&trust)
// form request // form request
var req v2reputation.AnnounceIntermediateResultRequest 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 ( import (
"bytes" "bytes"
"errors"
"fmt"
"github.com/mr-tron/base58" "github.com/mr-tron/base58"
"github.com/nspcc-dev/neofs-api-go/v2/reputation" "github.com/nspcc-dev/neofs-api-go/v2/reputation"
) )
// PeerID represents peer ID compatible with NeoFS API v2. // PeerID represents unique identifier of the peer participating in the NeoFS
type PeerID reputation.PeerID // reputation system.
// NewPeerID creates and returns blank PeerID.
// //
// Defaults: // ID is mutually compatible with github.com/nspcc-dev/neofs-api-go/v2/reputation.PeerID
// - publicKey: nil. // message. See ReadFromV2 / WriteToV2 methods.
func NewPeerID() *PeerID {
return PeerIDFromV2(new(reputation.PeerID))
}
// PeerIDFromV2 converts NeoFS API v2 reputation.PeerID message to PeerID.
// //
// Nil reputation.PeerID converts to nil. // Instances can be created using built-in var declaration.
func PeerIDFromV2(id *reputation.PeerID) *PeerID { type PeerID struct {
return (*PeerID)(id) m reputation.PeerID
} }
// SetPublicKey sets peer ID as a compressed public key. // ReadFromV2 reads PeerID from the reputation.PeerID message. Returns an
func (x *PeerID) SetPublicKey(v [33]byte) { // error if the message is malformed according to the NeoFS API V2 protocol.
(*reputation.PeerID)(x).SetPublicKey(v[:])
}
// ToV2 converts PeerID to NeoFS API v2 reputation.PeerID message.
// //
// Nil PeerID converts to nil. // See also WriteToV2.
func (x *PeerID) ToV2() *reputation.PeerID { func (x *PeerID) ReadFromV2(m reputation.PeerID) error {
return (*reputation.PeerID)(x) val := m.GetPublicKey()
} if len(val) == 0 {
return errors.New("missing ID bytes")
// 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
} }
(*reputation.PeerID)(x).SetPublicKey(data) x.m = m
return nil return nil
} }
// String returns base58 string representation of PeerID. // WriteToV2 writes PeerID to the reputation.PeerID message.
func (x *PeerID) String() string { // The message must not be nil.
return base58.Encode((*reputation.PeerID)(x).GetPublicKey()) //
// See also ReadFromV2.
func (x PeerID) WriteToV2(m *reputation.PeerID) {
*m = x.m
} }
// Marshal marshals PeerID into a protobuf binary form. // SetPublicKey sets PeerID as a binary-encoded public key which authenticates
func (x *PeerID) Marshal() ([]byte, error) { // the participant of the NeoFS reputation system.
return (*reputation.PeerID)(x).StableMarshal(nil), nil //
// 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. // PublicKey return public key set using SetPublicKey.
func (x *PeerID) Unmarshal(data []byte) error { //
return (*reputation.PeerID)(x).Unmarshal(data) // 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. // ComparePeerKey checks if the given PeerID corresponds to the party
func (x *PeerID) MarshalJSON() ([]byte, error) { // authenticated by the given binary public key.
return (*reputation.PeerID)(x).MarshalJSON() func ComparePeerKey(peer PeerID, key []byte) bool {
return bytes.Equal(peer.PublicKey(), key)
} }
// UnmarshalJSON decodes PeerID from protobuf JSON format. // EncodeToString encodes ID into NeoFS API protocol string.
func (x *PeerID) UnmarshalJSON(data []byte) error { //
return (*reputation.PeerID)(x).UnmarshalJSON(data) // 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 ( import (
"testing" "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" "github.com/nspcc-dev/neofs-sdk-go/reputation"
reputationtest "github.com/nspcc-dev/neofs-sdk-go/reputation/test" reputationtest "github.com/nspcc-dev/neofs-sdk-go/reputation/test"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestPeerID_ToV2(t *testing.T) { func TestPeerID_PublicKey(t *testing.T) {
t.Run("nil", func(t *testing.T) { var val reputation.PeerID
var x *reputation.PeerID
require.Nil(t, x.ToV2()) require.Zero(t, val.PublicKey())
})
t.Run("nil", func(t *testing.T) { key := []byte{3, 2, 1}
peerID := reputationtest.PeerID()
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) { func TestPeerID_EncodeToString(t *testing.T) {
t.Run("Parse/String", func(t *testing.T) { val := reputationtest.PeerID()
id := reputationtest.PeerID() var val2 reputation.PeerID
strID := id.String() require.NoError(t, val2.DecodeString(val.EncodeToString()))
require.Equal(t, val, val2)
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())
})
} }

View file

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

View file

@ -1,327 +1,416 @@
package reputation package reputation
import ( import (
"crypto/ecdsa"
"errors" "errors"
"fmt" "fmt"
"github.com/nspcc-dev/neofs-api-go/v2/refs" "github.com/nspcc-dev/neofs-api-go/v2/refs"
"github.com/nspcc-dev/neofs-api-go/v2/reputation" "github.com/nspcc-dev/neofs-api-go/v2/reputation"
neofscrypto "github.com/nspcc-dev/neofs-sdk-go/crypto" 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" "github.com/nspcc-dev/neofs-sdk-go/version"
) )
// Trust represents peer's trust compatible with NeoFS API v2. // Trust represents quantitative assessment of the trust of a participant in the
type Trust reputation.Trust // NeoFS reputation system.
// NewTrust creates and returns blank Trust.
// //
// Defaults: // Trust is mutually compatible with github.com/nspcc-dev/neofs-api-go/v2/reputation.Trust
// - value: 0; // message. See ReadFromV2 / WriteToV2 methods.
// - PeerID: nil. //
func NewTrust() *Trust { // Instances can be created using built-in var declaration.
return TrustFromV2(new(reputation.Trust)) type Trust struct {
m reputation.Trust
} }
// TrustFromV2 converts NeoFS API v2 // ReadFromV2 reads Trust from the reputation.Trust message. Returns an
// reputation.Trust message structure to Trust. // error if the message is malformed according to the NeoFS API V2 protocol.
// //
// Nil reputation.Trust converts to nil. // See also WriteToV2.
func TrustFromV2(t *reputation.Trust) *Trust { func (x *Trust) ReadFromV2(m reputation.Trust) error {
return (*Trust)(t) 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 // WriteToV2 writes Trust to the reputation.Trust message.
// reputation.Trust message structure. // The message must not be nil.
// //
// Nil Trust converts to nil. // See also ReadFromV2.
func (x *Trust) ToV2() *reputation.Trust { func (x Trust) WriteToV2(m *reputation.Trust) {
return (*reputation.Trust)(x) *m = x.m
} }
// TrustsToV2 converts slice of Trust's to slice of // SetPeer specifies identifier of the participant of the NeoFS reputation system
// NeoFS API v2 reputation.Trust message structures. // to which the Trust relates.
func TrustsToV2(xs []Trust) (res []reputation.Trust) { //
if xs != nil { // See also Peer.
res = make([]reputation.Trust, len(xs)) func (x *Trust) SetPeer(id PeerID) {
var m reputation.PeerID
id.WriteToV2(&m)
for i := range xs { x.m.SetPeer(&m)
res[i] = *xs[i].ToV2() }
// 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 return
} }
// SetPeer sets trusted peer ID. // SetValue sets the Trust value. Value MUST be in range [0;1].
func (x *Trust) SetPeer(id *PeerID) { //
(*reputation.Trust)(x).SetPeer(id.ToV2()) // See also Value.
}
// Peer returns trusted peer ID.
func (x *Trust) Peer() *PeerID {
return PeerIDFromV2(
(*reputation.Trust)(x).GetPeer())
}
// SetValue sets trust value.
func (x *Trust) SetValue(val float64) { func (x *Trust) SetValue(val float64) {
(*reputation.Trust)(x).SetValue(val) if val < 0 || val > 1 {
} panic(fmt.Sprintf("trust value is out-of-range %v", 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)
} }
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)) { func (x *GlobalTrust) setBodyField(setter func(*reputation.GlobalTrustBody)) {
if x != nil { if x != nil {
v2 := (*reputation.GlobalTrust)(x) body := x.m.GetBody()
body := v2.GetBody()
if body == nil { if body == nil {
body = new(reputation.GlobalTrustBody) body = new(reputation.GlobalTrustBody)
v2.SetBody(body) x.m.SetBody(body)
} }
setter(body) setter(body)
} }
} }
// SetManager sets node manager ID. // SetManager sets identifier of the NeoFS reputation system's participant which
func (x *GlobalTrust) SetManager(id *PeerID) { // 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) { x.setBodyField(func(body *reputation.GlobalTrustBody) {
body.SetManager(id.ToV2()) body.SetManager(&m)
}) })
} }
// Manager returns node manager ID. // Manager returns peer set using SetManager.
func (x *GlobalTrust) Manager() *PeerID { //
return PeerIDFromV2( // Zero GlobalTrust has zero manager which is incorrect according to the
(*reputation.GlobalTrust)(x). // NeoFS API protocol.
GetBody(). func (x GlobalTrust) Manager() (res PeerID) {
GetManager(), m := x.m.GetBody().GetManager()
) if m != nil {
} err := res.ReadFromV2(*m)
if err != nil {
// SetTrust sets global trust value. panic(fmt.Sprintf("unexpected error from ReadFromV2: %v", err))
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")
} }
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 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 { if err != nil {
return fmt.Errorf("calculate signature: %w", err) return fmt.Errorf("calculate signature: %w", err)
} }
var sigv2 refs.Signature var sigv2 refs.Signature
sig.WriteToV2(&sigv2) sig.WriteToV2(&sigv2)
m.SetSignature(&sigv2) x.m.SetSignature(&sigv2)
return nil return nil
} }
// VerifySignature verifies global trust signature. // VerifySignature checks if GlobalTrust signature is presented and valid.
func (x *GlobalTrust) VerifySignature() error { //
m := (*reputation.GlobalTrust)(x) // Zero GlobalTrust fails the check.
//
sigV2 := m.GetSignature() // See also Sign.
func (x GlobalTrust) VerifySignature() bool {
sigV2 := x.m.GetSignature()
if sigV2 == nil { if sigV2 == nil {
return errors.New("missing signature") return false
} }
var sig neofscrypto.Signature var sig neofscrypto.Signature
sig.ReadFromV2(*sigV2) sig.ReadFromV2(*sigV2)
if !sig.Verify(m.GetBody().StableMarshal(nil)) { return sig.Verify(x.m.GetBody().StableMarshal(nil))
return errors.New("wrong signature")
}
return nil
} }
// Marshal marshals GlobalTrust into a protobuf binary form. // Marshal encodes GlobalTrust into a binary format of the NeoFS API protocol
func (x *GlobalTrust) Marshal() ([]byte, error) { // (Protocol Buffers with direct field order).
return (*reputation.GlobalTrust)(x).StableMarshal(nil), nil //
// 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 { func (x *GlobalTrust) Unmarshal(data []byte) error {
return (*reputation.GlobalTrust)(x).Unmarshal(data) return x.m.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)
} }

View file

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