package reputation import ( "errors" "fmt" "github.com/TrueCloudLab/frostfs-api-go/v2/refs" "github.com/TrueCloudLab/frostfs-api-go/v2/reputation" frostfscrypto "github.com/TrueCloudLab/frostfs-sdk-go/crypto" "github.com/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 github.com/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 github.com/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 github.com/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) }