storj/storagenode/payouts/estimatedpayouts/service.go

161 lines
5.3 KiB
Go
Raw Normal View History

// Copyright (C) 2020 Storj Labs, Inc.
// See LICENSE for copying information.
package estimatedpayouts
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/payouts"
"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("estimationservice")
mon = monkit.Package()
)
// Service is handling storage node estimation payouts 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 payouts for current and previous months from specific satellite with current level of load.
func (s *Service) GetSatelliteEstimatedPayout(ctx context.Context, satelliteID storj.NodeID, now time.Time) (payout EstimatedPayout, err error) {
defer mon.Task()(&ctx)(&err)
stats, err := s.reputationDB.Get(ctx, satelliteID)
if err != nil {
return EstimatedPayout{}, EstimationServiceErr.Wrap(err)
}
if stats.DisqualifiedAt != nil {
return EstimatedPayout{}, EstimationServiceErr.New("node was disqualified on satellite")
}
currentMonthPayout, previousMonthPayout, err := s.estimatedPayout(ctx, satelliteID, now)
if err != nil {
return EstimatedPayout{}, EstimationServiceErr.Wrap(err)
}
payout.Set(currentMonthPayout, previousMonthPayout, now, stats.JoinedAt)
return payout, nil
}
// GetAllSatellitesEstimatedPayout returns estimated payouts for current and previous months from all satellites with current level of load.
func (s *Service) GetAllSatellitesEstimatedPayout(ctx context.Context, now time.Time) (payout EstimatedPayout, err error) {
defer mon.Task()(&ctx)(&err)
satelliteIDs := s.trust.GetSatellites(ctx)
for i := 0; i < len(satelliteIDs); i++ {
var satellitePayout EstimatedPayout
stats, err := s.reputationDB.Get(ctx, satelliteIDs[i])
if err != nil {
return EstimatedPayout{}, EstimationServiceErr.Wrap(err)
}
if stats.DisqualifiedAt != nil {
continue
}
current, previous, err := s.estimatedPayout(ctx, satelliteIDs[i], now)
if err != nil {
return EstimatedPayout{}, EstimationServiceErr.Wrap(err)
}
satellitePayout.Set(current, previous, now, stats.JoinedAt)
payout.Add(satellitePayout)
}
return payout, nil
}
// estimatedPayout returns estimated payouts data for current and previous months from specific satellite.
func (s *Service) estimatedPayout(ctx context.Context, satelliteID storj.NodeID, now time.Time) (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, now.UTC(), stats.JoinedAt, priceModel)
previousMonthPayout, err = s.estimationUsagePeriod(ctx, 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 := payouts.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 30 to show tbm instead of tb.
payout.DiskSpace /= 30
payout.SetDiskSpacePayout(priceModel.DiskSpace)
payout.SetHeldAmount()
payout.SetPayout()
return payout, EstimationServiceErr.Wrap(err)
}