923f84722a
Signed-off-by: Pavel Karpy <p.karpy@yadro.com>
175 lines
3.4 KiB
Go
175 lines
3.4 KiB
Go
package truststorage
|
|
|
|
import (
|
|
"errors"
|
|
"sync"
|
|
|
|
"github.com/TrueCloudLab/frostfs-node/pkg/services/reputation"
|
|
apireputation "github.com/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
|
|
}
|