[#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