[#360] Implement basic settlement context

Basic settlement context is a main structure that
implement logic of basic settlement phases: collecting
assets from container owners and then distributing them
to storage nodes.

Signed-off-by: Alex Vanin <alexey@nspcc.ru>
This commit is contained in:
Alex Vanin 2021-02-01 19:17:16 +03:00 committed by Alex Vanin
parent f45675b7a2
commit a624bb881d
3 changed files with 186 additions and 0 deletions

View file

@ -0,0 +1,94 @@
package basic
import (
"math/big"
"github.com/nspcc-dev/neofs-node/pkg/innerring/processors/settlement/common"
"github.com/nspcc-dev/neofs-node/pkg/morph/client/container/wrapper"
"go.uber.org/zap"
)
var (
bigGB = big.NewInt(1 << 30)
bigZero = big.NewInt(0)
bigOne = big.NewInt(1)
)
func (inc *IncomeSettlementContext) Collect() {
inc.mu.Lock()
defer inc.mu.Unlock()
// todo: save state of bank wallet
cachedRate := inc.rate.BasicRate()
cnrEstimations, err := inc.estimations.Estimations(inc.epoch)
if err != nil {
inc.log.Warn("can't fetch container size estimations",
zap.Uint64("epoch", inc.epoch))
return
}
for i := range cnrEstimations {
owner, err := inc.container.ContainerInfo(cnrEstimations[i].ContainerID)
if err != nil {
inc.log.Warn("can't fetch container info",
zap.Uint64("epoch", inc.epoch),
zap.Stringer("container_id", cnrEstimations[i].ContainerID))
continue
}
cnrNodes, err := inc.placement.ContainerNodes(inc.epoch, cnrEstimations[i].ContainerID)
if err != nil {
inc.log.Debug("can't fetch container info",
zap.Uint64("epoch", inc.epoch),
zap.Stringer("container_id", cnrEstimations[i].ContainerID))
continue
}
avg := inc.avgEstimation(cnrEstimations[i]) // average container size per node
total := calculateBasicSum(avg, cachedRate, len(cnrNodes))
inc.txTable.Transfer(&common.TransferTx{
From: owner.Owner(),
To: inc.bankOwner,
Amount: total,
})
}
common.TransferAssets(inc.exchange, inc.txTable)
}
// avgEstimation returns estimation value for single container. Right now it
// simply calculates average of all announcements, however it can be smarter and
// base result on reputation of announcers and clever math.
func (inc *IncomeSettlementContext) avgEstimation(e *wrapper.Estimations) (avg uint64) {
if len(e.Values) == 0 {
return 0
}
for i := range e.Values {
avg += e.Values[i].Size
}
return avg / uint64(len(e.Values))
}
func calculateBasicSum(size, rate uint64, ln int) *big.Int {
bigRate := big.NewInt(int64(rate))
total := size * uint64(ln)
price := new(big.Int).SetUint64(total)
price.Mul(price, bigRate)
price.Div(price, bigGB)
if price.Cmp(bigZero) == 0 {
price.Add(price, bigOne)
}
return price
}

View file

@ -0,0 +1,86 @@
package basic
import (
"sync"
"github.com/mr-tron/base58"
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neofs-api-go/pkg/owner"
"github.com/nspcc-dev/neofs-node/pkg/innerring/processors/settlement/common"
"github.com/nspcc-dev/neofs-node/pkg/morph/client/container/wrapper"
"go.uber.org/zap"
)
type (
EstimationFetcher interface {
Estimations(uint64) ([]*wrapper.Estimations, error)
}
RateFetcher interface {
BasicRate() uint64
}
IncomeSettlementContext struct {
mu sync.Mutex // lock to prevent collection and distribution in the same time
log *zap.Logger
epoch uint64
rate RateFetcher
estimations EstimationFetcher
container common.ContainerStorage
placement common.PlacementCalculator
exchange common.Exchanger
txTable *common.TransferTable
bankOwner *owner.ID
}
IncomeSettlementContextPrms struct {
Log *zap.Logger
Epoch uint64
Rate RateFetcher
Estimations EstimationFetcher
Container common.ContainerStorage
Placement common.PlacementCalculator
Exchange common.Exchanger
}
)
func NewIncomeSettlementContext(p *IncomeSettlementContextPrms) (*IncomeSettlementContext, error) {
bankingAccount, err := bankOwnerID()
if err != nil {
return nil, err // should never happen
}
return &IncomeSettlementContext{
log: p.Log,
epoch: p.Epoch,
rate: p.Rate,
estimations: p.Estimations,
container: p.Container,
placement: p.Placement,
exchange: p.Exchange,
txTable: common.NewTransferTable(),
bankOwner: bankingAccount,
}, nil
}
func bankOwnerID() (*owner.ID, error) {
u := util.Uint160{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // todo: define const
1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
d, err := base58.Decode(address.Uint160ToString(u))
if err != nil {
return nil, err
}
var w owner.NEO3Wallet
copy(w[:], d)
o := owner.NewID()
o.SetNeo3Wallet(&w)
return o, nil
}

View file

@ -0,0 +1,6 @@
package basic
func (inc *IncomeSettlementContext) Distribute() {
inc.mu.Lock()
defer inc.mu.Unlock()
}