[#428] reputation: Implement local trust storage

Implement in-memory `Storage` which is going to be used to submit the
results of interactions with network members. `Storage` also provides an
iterator interface, so the component can be used in `Controller`.

Signed-off-by: Leonard Lyubich <leonard@nspcc.ru>
This commit is contained in:
Leonard Lyubich 2021-03-23 21:33:37 +03:00 committed by Leonard Lyubich
parent 29e974df93
commit f25253738a
2 changed files with 216 additions and 0 deletions

View file

@ -0,0 +1,173 @@
package truststorage
import (
"errors"
"sync"
"github.com/nspcc-dev/neofs-node/pkg/services/reputation"
)
// UpdatePrm groups the parameters of Storage's Update operation.
type UpdatePrm struct {
sat bool
epoch uint64
peer reputation.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 reputation.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 reputation.PeerID) string {
return string(id.Bytes())
}
func peerIDFromString(str string) reputation.PeerID {
return reputation.PeerIDFromBytes([]byte(str))
}
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)
if err = h(t); err != nil {
break
}
}
}
}
s.mtx.RUnlock()
return
}

View file

@ -0,0 +1,43 @@
package truststorage
import (
"sync"
)
// Prm groups the required parameters of the Storage's constructor.
//
// All values must comply with the requirements imposed on them.
// Passing incorrect parameter values will result in constructor
// failure (error or panic depending on the implementation).
//
// The component is not parameterizable at the moment.
type Prm struct{}
// Storage represents in-memory storage of
// local reputation values.
//
// Storage provides access to normalized local trust
// values through iterator interface.
//
// For correct operation, Storage must be created
// using the constructor (New) based on the required parameters
// and optional components. After successful creation,
// Storage is immediately ready to work through API.
type Storage struct {
prm Prm
mtx sync.RWMutex
mItems map[uint64]*EpochTrustValueStorage
}
// New creates a new instance of the Storage.
//
// The created Storage does not require additional
// initialization and is completely ready for work.
func New(prm Prm) *Storage {
return &Storage{
prm: prm,
mItems: make(map[uint64]*EpochTrustValueStorage),
}
}