storj/storagenode/estimatedpayout/service.go

175 lines
6.0 KiB
Go
Raw Normal View History

// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
package estimatedpayout
import (
"context"
"time"
"github.com/spacemonkeygo/monkit/v3"
"github.com/zeebo/errs"
"storj.io/common/storj"
"storj.io/storj/private/date"
"storj.io/storj/storagenode/bandwidth"
"storj.io/storj/storagenode/pricing"
"storj.io/storj/storagenode/reputation"
"storj.io/storj/storagenode/satellites"
"storj.io/storj/storagenode/storageusage"
"storj.io/storj/storagenode/trust"
)
var (
// EstimationServiceErr defines sno service error.
EstimationServiceErr = errs.Class("storage node estimation payout service error")
mon = monkit.Package()
)
// Service is handling storage node estimation payout logic.
//
// architecture: Service
type Service struct {
bandwidthDB bandwidth.DB
reputationDB reputation.DB
storageUsageDB storageusage.DB
pricingDB pricing.DB
satelliteDB satellites.DB
trust *trust.Pool
}
// NewService returns new instance of Service.
func NewService(bandwidthDB bandwidth.DB, reputationDB reputation.DB, storageUsageDB storageusage.DB, pricingDB pricing.DB, satelliteDB satellites.DB, trust *trust.Pool) *Service {
return &Service{
bandwidthDB: bandwidthDB,
reputationDB: reputationDB,
storageUsageDB: storageUsageDB,
pricingDB: pricingDB,
satelliteDB: satelliteDB,
trust: trust,
}
}
// GetSatelliteEstimatedPayout returns estimated payout for current and previous months from specific satellite with current level of load.
func (s *Service) GetSatelliteEstimatedPayout(ctx context.Context, satelliteID storj.NodeID) (payout EstimatedPayout, err error) {
defer mon.Task()(&ctx)(&err)
currentMonthPayout, previousMonthPayout, err := s.estimatedPayout(ctx, satelliteID)
if err != nil {
return EstimatedPayout{}, EstimationServiceErr.Wrap(err)
}
payout.CurrentMonth = currentMonthPayout
payout.PreviousMonth = previousMonthPayout
return payout, nil
}
// GetAllSatellitesEstimatedPayout returns estimated payout for current and previous months from all satellites with current level of load.
func (s *Service) GetAllSatellitesEstimatedPayout(ctx context.Context) (payout EstimatedPayout, err error) {
defer mon.Task()(&ctx)(&err)
satelliteIDs := s.trust.GetSatellites(ctx)
for i := 0; i < len(satelliteIDs); i++ {
current, previous, err := s.estimatedPayout(ctx, satelliteIDs[i])
if err != nil {
return EstimatedPayout{}, EstimationServiceErr.Wrap(err)
}
payout.CurrentMonth.Payout += current.Payout
payout.CurrentMonth.EgressRepairAuditPayout += current.EgressRepairAuditPayout
payout.CurrentMonth.DiskSpacePayout += current.DiskSpacePayout
payout.CurrentMonth.DiskSpace += current.DiskSpace
payout.CurrentMonth.EgressBandwidth += current.EgressBandwidth
payout.CurrentMonth.EgressBandwidthPayout += current.EgressBandwidthPayout
payout.CurrentMonth.EgressRepairAudit += current.EgressRepairAudit
payout.CurrentMonth.Held += current.Held
payout.PreviousMonth.Payout += previous.Payout
payout.PreviousMonth.DiskSpacePayout += previous.DiskSpacePayout
payout.PreviousMonth.DiskSpace += previous.DiskSpace
payout.PreviousMonth.EgressBandwidth += previous.EgressBandwidth
payout.PreviousMonth.EgressBandwidthPayout += previous.EgressBandwidthPayout
payout.PreviousMonth.EgressRepairAuditPayout += previous.EgressRepairAuditPayout
payout.PreviousMonth.EgressRepairAudit += previous.EgressRepairAudit
payout.PreviousMonth.Held += previous.Held
}
return payout, nil
}
// estimatedPayout returns estimated payout data for current and previous months from specific satellite.
func (s *Service) estimatedPayout(ctx context.Context, satelliteID storj.NodeID) (currentMonthPayout PayoutMonthly, previousMonthPayout PayoutMonthly, err error) {
defer mon.Task()(&ctx)(&err)
priceModel, err := s.pricingDB.Get(ctx, satelliteID)
if err != nil {
return PayoutMonthly{}, PayoutMonthly{}, EstimationServiceErr.Wrap(err)
}
stats, err := s.reputationDB.Get(ctx, satelliteID)
if err != nil {
return PayoutMonthly{}, PayoutMonthly{}, EstimationServiceErr.Wrap(err)
}
currentMonthPayout, err = s.estimationUsagePeriod(ctx, time.Now().UTC(), stats.JoinedAt, priceModel)
previousMonthPayout, err = s.estimationUsagePeriod(ctx, time.Now().UTC().AddDate(0, -1, 0), stats.JoinedAt, priceModel)
return currentMonthPayout, previousMonthPayout, nil
}
// estimationUsagePeriod returns PayoutMonthly for current satellite and current or previous month.
func (s *Service) estimationUsagePeriod(ctx context.Context, period time.Time, joinedAt time.Time, priceModel *pricing.Pricing) (payout PayoutMonthly, err error) {
var from, to time.Time
heldRate := getHeldRate(joinedAt, period)
payout.HeldRate = heldRate
from, to = date.MonthBoundary(period)
bandwidthDaily, err := s.bandwidthDB.GetDailySatelliteRollups(ctx, priceModel.SatelliteID, from, to)
if err != nil {
return PayoutMonthly{}, EstimationServiceErr.Wrap(err)
}
for i := 0; i < len(bandwidthDaily); i++ {
payout.EgressBandwidth += bandwidthDaily[i].Egress.Usage
payout.EgressRepairAudit += bandwidthDaily[i].Egress.Audit + bandwidthDaily[i].Egress.Repair
}
payout.SetEgressBandwidthPayout(priceModel.EgressBandwidth)
payout.SetEgressRepairAuditPayout(priceModel.AuditBandwidth)
storageDaily, err := s.storageUsageDB.GetDaily(ctx, priceModel.SatelliteID, from, to)
if err != nil {
return PayoutMonthly{}, EstimationServiceErr.Wrap(err)
}
for j := 0; j < len(storageDaily); j++ {
payout.DiskSpace += storageDaily[j].AtRestTotal
}
// dividing by 720 to show tbm instead of tbh.
payout.DiskSpace /= 720
payout.SetDiskSpacePayout(priceModel.DiskSpace)
payout.SetHeldAmount()
payout.SetPayout()
return payout, EstimationServiceErr.Wrap(err)
}
// getHeldRate returns held rate for specific period from join date of node.
func getHeldRate(joinTime time.Time, requestTime time.Time) (heldRate int64) {
monthsSinceJoin := date.MonthsBetweenDates(joinTime, requestTime)
switch monthsSinceJoin {
case 0, 1, 2:
heldRate = 75
case 3, 4, 5:
heldRate = 50
case 6, 7, 8:
heldRate = 25
default:
heldRate = 0
}
return heldRate
}