package truststorage import ( "errors" "sync" "git.frostfs.info/TrueCloudLab/frostfs-node/pkg/services/reputation" apireputation "git.frostfs.info/TrueCloudLab/frostfs-sdk-go/reputation" ) // UpdatePrm groups the parameters of Storage's Update operation. type UpdatePrm struct { sat bool epoch uint64 peer apireputation.PeerID } // SetEpoch sets number of the epoch // when the interaction happened. func (p *UpdatePrm) SetEpoch(e uint64) { p.epoch = e } // SetPeer sets identifier of the peer // with which the local node interacted. func (p *UpdatePrm) SetPeer(id apireputation.PeerID) { p.peer = id } // SetSatisfactory sets successful completion status. func (p *UpdatePrm) SetSatisfactory(sat bool) { p.sat = sat } type trustValue struct { sat, all int } // EpochTrustValueStorage represents storage of // the trust values by particular epoch. type EpochTrustValueStorage struct { mtx sync.RWMutex mItems map[string]*trustValue } func newTrustValueStorage() *EpochTrustValueStorage { return &EpochTrustValueStorage{ mItems: make(map[string]*trustValue, 1), } } func stringifyPeerID(id apireputation.PeerID) string { return string(id.PublicKey()) } func peerIDFromString(str string) (res apireputation.PeerID) { res.SetPublicKey([]byte(str)) return } func (s *EpochTrustValueStorage) update(prm UpdatePrm) { s.mtx.Lock() { strID := stringifyPeerID(prm.peer) val, ok := s.mItems[strID] if !ok { val = new(trustValue) s.mItems[strID] = val } if prm.sat { val.sat++ } val.all++ } s.mtx.Unlock() } // Update updates the number of satisfactory transactions with peer. func (s *Storage) Update(prm UpdatePrm) { var trustStorage *EpochTrustValueStorage s.mtx.Lock() { var ( ok bool epoch = prm.epoch ) trustStorage, ok = s.mItems[epoch] if !ok { trustStorage = newTrustValueStorage() s.mItems[epoch] = trustStorage } } s.mtx.Unlock() trustStorage.update(prm) } // ErrNoPositiveTrust is returned by iterator when // there is no positive number of successful transactions. var ErrNoPositiveTrust = errors.New("no positive trust") // DataForEpoch returns EpochValueStorage for epoch. // // If there is no data for the epoch, ErrNoPositiveTrust returns. func (s *Storage) DataForEpoch(epoch uint64) (*EpochTrustValueStorage, error) { s.mtx.RLock() trustStorage, ok := s.mItems[epoch] s.mtx.RUnlock() if !ok { return nil, ErrNoPositiveTrust } return trustStorage, nil } // Iterate iterates over normalized trust values and passes them to parameterized handler. // // Values are normalized according to http://ilpubs.stanford.edu:8090/562/1/2002-56.pdf Chapter 4.5. // If divisor in formula is zero, ErrNoPositiveTrust returns. func (s *EpochTrustValueStorage) Iterate(h reputation.TrustHandler) (err error) { s.mtx.RLock() { var ( sum reputation.TrustValue mVals = make(map[string]reputation.TrustValue, len(s.mItems)) ) // iterate first time to calculate normalizing divisor for strID, val := range s.mItems { if val.all > 0 { num := reputation.TrustValueFromInt(val.sat) denom := reputation.TrustValueFromInt(val.all) v := num.Div(denom) mVals[strID] = v sum.Add(v) } } err = ErrNoPositiveTrust if !sum.IsZero() { for strID, val := range mVals { t := reputation.Trust{} t.SetPeer(peerIDFromString(strID)) t.SetValue(val.Div(sum)) if err = h(t); err != nil { break } } } } s.mtx.RUnlock() return }