[#473] Implement EigenTrust calculations

Signed-off-by: Leonard Lyubich <leonard@nspcc.ru>
This commit is contained in:
Leonard Lyubich 2021-04-10 15:02:51 +03:00 committed by Alex Vanin
parent 0ec8bcf6b4
commit a97e08cfd7
14 changed files with 1112 additions and 3 deletions

View file

@ -0,0 +1,69 @@
package eigentrustctrl
import (
"context"
"github.com/nspcc-dev/neofs-node/pkg/services/reputation/eigentrust"
"go.uber.org/zap"
)
// ContinuePrm groups the required parameters of Continue operation.
type ContinuePrm struct {
epoch uint64
}
type iterContext struct {
context.Context
eigentrust.EpochIteration
last bool
}
func (x iterContext) Last() bool {
return x.last
}
type iterContextCancel struct {
iterContext
cancel context.CancelFunc
}
// Continue moves the global reputation calculator to the next iteration.
func (c *Controller) Continue(prm ContinuePrm) {
c.mtx.Lock()
{
iterCtx, ok := c.mCtx[prm.epoch]
if !ok {
iterCtx := new(iterContextCancel)
c.mCtx[prm.epoch] = iterCtx
iterCtx.Context, iterCtx.cancel = context.WithCancel(context.Background())
} else {
iterCtx.cancel()
}
iterCtx.last = iterCtx.I() == c.prm.IterationNumber
err := c.prm.WorkerPool.Submit(func() {
c.prm.DaughtersTrustCalculator.Calculate(iterCtx.iterContext)
})
if err != nil {
c.opts.log.Debug("iteration submit failure",
zap.String("error", err.Error()),
)
}
if iterCtx.last {
delete(c.mCtx, prm.epoch)
// In this case and worker pool failure we can mark epoch
// number as already processed, but in any case it grows up
// during normal operation of the system. Also, such information
// will only live while the application is alive.
}
}
c.mtx.Unlock()
}

View file

@ -0,0 +1,85 @@
package eigentrustctrl
import (
"fmt"
"sync"
"github.com/nspcc-dev/neofs-node/pkg/util"
)
// Prm groups the required parameters of the Controller'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).
type Prm struct {
// Number of iterations
IterationNumber uint32
// Component of computing iteration of EigenTrust algorithm.
//
// Must not be nil.
DaughtersTrustCalculator DaughtersTrustCalculator
// Routine execution pool for single epoch iteration.
WorkerPool util.WorkerPool
}
// Controller represents EigenTrust algorithm transient controller.
//
// Controller's main goal is to separate the two main stages of
// the calculation:
// 1.reporting local values to manager nodes
// 2.calculating global trusts of child nodes
//
// Calculation stages are controlled based on external signals
// that come from the application through the Controller's API.
//
// For correct operation, the controller must be created
// using the constructor (New) based on the required parameters
// and optional components. After successful creation,
// the constructor is immediately ready to work through
// API of external control of calculations and data transfer.
type Controller struct {
prm Prm
opts *options
mtx sync.Mutex
mCtx map[uint64]*iterContextCancel
}
const invalidPrmValFmt = "invalid parameter %s (%T):%v"
func panicOnPrmValue(n string, v interface{}) {
panic(fmt.Sprintf(invalidPrmValFmt, n, v, v))
}
// New creates a new instance of the Controller.
//
// Panics if at least one value of the parameters is invalid.
//
// The created Controller does not require additional
// initialization and is completely ready for work.
func New(prm Prm, opts ...Option) *Controller {
switch {
case prm.IterationNumber == 0:
panicOnPrmValue("IterationNumber", prm.IterationNumber)
case prm.WorkerPool == nil:
panicOnPrmValue("WorkerPool", prm.WorkerPool)
case prm.DaughtersTrustCalculator == nil:
panicOnPrmValue("DaughtersTrustCalculator", prm.DaughtersTrustCalculator)
}
o := defaultOpts()
for _, opt := range opts {
opt(o)
}
return &Controller{
prm: prm,
opts: o,
mCtx: make(map[uint64]*iterContextCancel),
}
}

View file

@ -0,0 +1,35 @@
package eigentrustctrl
import (
"context"
)
// IterationContext is a context of the i-th
// stage of iterative EigenTrust algorithm.
type IterationContext interface {
context.Context
// Must return epoch number to select the values
// for global trust calculation.
Epoch() uint64
// Must return the sequence number of the iteration.
I() uint32
// Must return true if I() is the last iteration.
Last() bool
}
// DaughtersTrustCalculator is an interface of entity
// responsible for calculating the global trust of
// daughter nodes in terms of EigenTrust algorithm.
type DaughtersTrustCalculator interface {
// Must perform the iteration step of the loop
// for computing the global trust of all daughter
// nodes and sending intermediate values
// according to EigenTrust description
// http://ilpubs.stanford.edu:8090/562/1/2002-56.pdf Ch.5.1.
//
// Execution should be interrupted if ctx.Last().
Calculate(ctx IterationContext)
}

View file

@ -0,0 +1,30 @@
package eigentrustctrl
import (
"github.com/nspcc-dev/neofs-node/pkg/util/logger"
"go.uber.org/zap"
)
// Option sets an optional parameter of Controller.
type Option func(*options)
type options struct {
log *logger.Logger
}
func defaultOpts() *options {
return &options{
log: zap.L(),
}
}
// WithLogger returns option to specify logging component.
//
// Ignores nil values.
func WithLogger(l *logger.Logger) Option {
return func(o *options) {
if l != nil {
o.log = l
}
}
}