[#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:
parent
29e974df93
commit
f25253738a
2 changed files with 216 additions and 0 deletions
173
pkg/services/reputation/local/storage/calls.go
Normal file
173
pkg/services/reputation/local/storage/calls.go
Normal 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
|
||||
}
|
43
pkg/services/reputation/local/storage/storage.go
Normal file
43
pkg/services/reputation/local/storage/storage.go
Normal 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),
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue