frostfs-sdk-go/reputation/trust.go

417 lines
10 KiB
Go
Raw Permalink Normal View History

package reputation
import (
"errors"
"fmt"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/refs"
"git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/reputation"
frostfscrypto "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/crypto"
"git.frostfs.info/TrueCloudLab/frostfs-sdk-go/version"
)
// Trust represents quantitative assessment of the trust of a participant in the
// FrostFS reputation system.
//
// Trust is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-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
}
// ReadFromV2 reads Trust from the reputation.Trust message. Returns an
// error if the message is malformed according to the FrostFS API V2 protocol.
//
// 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
}
// WriteToV2 writes Trust to the reputation.Trust message.
// The message must not be nil.
//
// See also ReadFromV2.
func (x Trust) WriteToV2(m *reputation.Trust) {
*m = x.m
}
// SetPeer specifies identifier of the participant of the FrostFS reputation system
// to which the Trust relates.
//
// See also Peer.
func (x *Trust) SetPeer(id PeerID) {
var m reputation.PeerID
id.WriteToV2(&m)
x.m.SetPeer(&m)
}
// Peer returns peer identifier set using SetPeer.
//
// Zero Trust returns zero PeerID which is incorrect according to the FrostFS 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
}
// SetValue sets the Trust value. Value MUST be in range [0;1].
//
// See also Value.
func (x *Trust) SetValue(val float64) {
if val < 0 || val > 1 {
panic(fmt.Sprintf("trust value is out-of-range %v", val))
}
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 FrostFS reputation
// system to another one.
//
// Trust is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-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 FrostFS 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
// FrostFS 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 FrostFS 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 FrostFS 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
// FrostFS 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 FrostFS reputation system obtained taking into account all other participants.
//
// GlobalTrust is mutually compatible with git.frostfs.info/TrueCloudLab/frostfs-api-go/v2/reputation.GlobalTrust
// message. See ReadFromV2 / WriteToV2 methods.
//
// To submit GlobalTrust value in FrostFS zero instance SHOULD be declared,
// initialized using Init method and filled using dedicated methods.
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 FrostFS 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 FrostFS 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 {
body := x.m.GetBody()
if body == nil {
body = new(reputation.GlobalTrustBody)
x.m.SetBody(body)
}
setter(body)
}
}
// SetManager sets identifier of the FrostFS 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(&m)
})
}
// Manager returns peer set using SetManager.
//
// Zero GlobalTrust has zero manager which is incorrect according to the
// FrostFS 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))
}
}
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
// FrostFS 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 frostfscrypto.Signer) error {
var sig frostfscrypto.Signature
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)
x.m.SetSignature(&sigv2)
return nil
}
// 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 false
}
var sig frostfscrypto.Signature
return sig.ReadFromV2(*sigV2) == nil && sig.Verify(x.m.GetBody().StableMarshal(nil))
}
// Marshal encodes GlobalTrust into a binary format of the FrostFS API protocol
// (Protocol Buffers with direct field order).
//
// See also Unmarshal.
func (x GlobalTrust) Marshal() []byte {
return x.m.StableMarshal(nil)
}
// Unmarshal decodes FrostFS 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 x.m.Unmarshal(data)
}