Skip to content

Commit

Permalink
autopilot: update renterFundsToExpectedStorage to use rhpv3 pricing a…
Browse files Browse the repository at this point in the history
…nd cap it using the host's remaining storage
  • Loading branch information
ChrisSchinnerl committed Sep 14, 2023
1 parent c041b8e commit 93e0422
Show file tree
Hide file tree
Showing 5 changed files with 40 additions and 23 deletions.
7 changes: 4 additions & 3 deletions api/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,10 @@ type RHPPriceTableRequest struct {

// RHPScanResponse is the response type for the /rhp/scan endpoint.
type RHPScanResponse struct {
Ping ParamDuration `json:"ping"`
ScanError string `json:"scanError,omitempty"`
Settings rhpv2.HostSettings `json:"settings,omitempty"`
Ping ParamDuration `json:"ping"`
ScanError string `json:"scanError,omitempty"`
Settings rhpv2.HostSettings `json:"settings,omitempty"`
PriceTable rhpv3.HostPriceTable `json:"priceTable,omitempty"`
}

// RHPFormRequest is the request type for the /rhp/form endpoint.
Expand Down
24 changes: 13 additions & 11 deletions autopilot/contractor.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
rhpv2 "go.sia.tech/core/rhp/v2"
rhpv3 "go.sia.tech/core/rhp/v3"
"go.sia.tech/core/types"
"go.sia.tech/renterd/alerts"
"go.sia.tech/renterd/api"
Expand Down Expand Up @@ -111,6 +112,7 @@ type (
contractInfo struct {
contract api.Contract
settings rhpv2.HostSettings
priceTable rhpv3.HostPriceTable
usable bool
recoverable bool
}
Expand Down Expand Up @@ -728,7 +730,7 @@ func (c *contractor) runContractChecks(ctx context.Context, w Worker, contracts
}

// decide whether the contract is still good
ci := contractInfo{contract: contract, settings: host.Settings}
ci := contractInfo{contract: contract, priceTable: host.PriceTable.HostPriceTable, settings: host.Settings}
renterFunds, err := c.renewFundingEstimate(ctx, ci, state.fee, false)
if err != nil {
c.logger.Errorw(fmt.Sprintf("failed to compute renterFunds for contract: %v", err))
Expand Down Expand Up @@ -1359,7 +1361,7 @@ func (c *contractor) renewContract(ctx context.Context, w Worker, ci contractInf
}

// calculate the host collateral
expectedStorage := renterFundsToExpectedStorage(renterFunds, endHeight-cs.BlockHeight, settings)
expectedStorage := renterFundsToExpectedStorage(renterFunds, endHeight-cs.BlockHeight, ci.settings, ci.priceTable)
newCollateral := rhpv2.ContractRenewalCollateral(rev.FileContract, expectedStorage, settings, cs.BlockHeight, endHeight)

// renew the contract
Expand Down Expand Up @@ -1435,7 +1437,7 @@ func (c *contractor) refreshContract(ctx context.Context, w Worker, ci contractI
}

// calculate the new collateral
expectedStorage := renterFundsToExpectedStorage(renterFunds, contract.EndHeight()-cs.BlockHeight, settings)
expectedStorage := renterFundsToExpectedStorage(renterFunds, contract.EndHeight()-cs.BlockHeight, ci.settings, ci.priceTable)
newCollateral := rhpv2.ContractRenewalCollateral(rev.FileContract, expectedStorage, settings, cs.BlockHeight, contract.EndHeight())

// do not refresh if the contract's updated collateral will fall below the threshold anyway
Expand Down Expand Up @@ -1518,7 +1520,7 @@ func (c *contractor) formContract(ctx context.Context, w Worker, host hostdb.Hos

// calculate the host collateral
endHeight := endHeight(state.cfg, state.period)
expectedStorage := renterFundsToExpectedStorage(renterFunds, endHeight-cs.BlockHeight, scan.Settings)
expectedStorage := renterFundsToExpectedStorage(renterFunds, endHeight-cs.BlockHeight, scan.Settings, scan.PriceTable)
hostCollateral := rhpv2.ContractFormationCollateral(state.cfg.Contracts.Period, expectedStorage, scan.Settings)

// form contract
Expand Down Expand Up @@ -1612,16 +1614,16 @@ func endHeight(cfg api.AutopilotConfig, currentPeriod uint64) uint64 {

// renterFundsToExpectedStorage returns how much storage a renter is expected to
// be able to afford given the provided 'renterFunds'.
func renterFundsToExpectedStorage(renterFunds types.Currency, duration uint64, host rhpv2.HostSettings) uint64 {
costPerByte := host.UploadBandwidthPrice.Add(host.StoragePrice.Mul64(duration)).Add(host.DownloadBandwidthPrice)
// If storage is free, we can afford 'unlimited' data.
if costPerByte.IsZero() {
return math.MaxUint64
func renterFundsToExpectedStorage(renterFunds types.Currency, duration uint64, settings rhpv2.HostSettings, pt rhpv3.HostPriceTable) uint64 {
costPerSector := sectorUploadCost(pt, duration)
// Handle free storage.
if costPerSector.IsZero() {
costPerSector = types.NewCurrency64(1)
}
// Catch overflow.
expectedStorage := renterFunds.Div(costPerByte)
expectedStorage := renterFunds.Div(costPerSector).Mul64(rhpv2.SectorSize)
if expectedStorage.Cmp(types.NewCurrency64(math.MaxUint64)) > 0 {
return math.MaxUint64
expectedStorage = types.NewCurrency64(math.MaxUint64)
}
return expectedStorage.Big().Uint64()
}
15 changes: 11 additions & 4 deletions autopilot/hostfilter.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"strings"

rhpv2 "go.sia.tech/core/rhp/v2"
rhpv3 "go.sia.tech/core/rhp/v3"
"go.sia.tech/core/types"
"go.sia.tech/renterd/api"
"go.sia.tech/renterd/hostdb"
Expand Down Expand Up @@ -222,7 +223,7 @@ func isUsableHost(cfg api.AutopilotConfig, rs api.RedundancySettings, gc worker.
// - refresh -> should be refreshed
// - renew -> should be renewed
func isUsableContract(cfg api.AutopilotConfig, ci contractInfo, bh uint64, renterFunds types.Currency, f *ipFilter) (usable, recoverable, refresh, renew bool, reasons []string) {
c, s := ci.contract, ci.settings
c, s, pt := ci.contract, ci.settings, ci.priceTable

usable = true
if bh > c.EndHeight() {
Expand All @@ -238,7 +239,7 @@ func isUsableContract(cfg api.AutopilotConfig, ci contractInfo, bh uint64, rente
refresh = false
renew = false
} else {
if isOutOfCollateral(c, s, renterFunds, bh) {
if isOutOfCollateral(c, s, pt, renterFunds, bh) {
reasons = append(reasons, errContractOutOfCollateral.Error())
usable = false
recoverable = true
Expand Down Expand Up @@ -287,8 +288,14 @@ func isOutOfFunds(cfg api.AutopilotConfig, s rhpv2.HostSettings, c api.Contract)
// isOutOfCollateral returns 'true' if the remaining/unallocated collateral in
// the contract is below a certain threshold of the collateral we would try to
// put into a contract upon renew.
func isOutOfCollateral(c api.Contract, s rhpv2.HostSettings, renterFunds types.Currency, blockHeight uint64) bool {
expectedStorage := renterFundsToExpectedStorage(renterFunds, c.EndHeight()-blockHeight, s)
func isOutOfCollateral(c api.Contract, s rhpv2.HostSettings, pt rhpv3.HostPriceTable, renterFunds types.Currency, blockHeight uint64) bool {
expectedStorage := renterFundsToExpectedStorage(renterFunds, c.EndHeight()-blockHeight, s, pt)
// Cap the expected storage at the remaining storage of the host. If the
// host doesn't have any storage left, there is no point in adding
// collateral.
if expectedStorage > s.RemainingStorage {
expectedStorage = s.RemainingStorage
}
expectedCollateral := rhpv2.ContractRenewalCollateral(c.Revision.FileContract, expectedStorage, s, blockHeight, c.EndHeight())
return isBelowCollateralThreshold(expectedCollateral, c.RemainingCollateral(s))
}
Expand Down
10 changes: 8 additions & 2 deletions autopilot/hostscore.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"time"

rhpv2 "go.sia.tech/core/rhp/v2"
rhpv3 "go.sia.tech/core/rhp/v3"
"go.sia.tech/core/types"
"go.sia.tech/renterd/api"
"go.sia.tech/renterd/hostdb"
Expand Down Expand Up @@ -292,9 +293,14 @@ func bytesToSectors(bytes uint64) uint64 {
return numSectors
}

func uploadCostForScore(cfg api.AutopilotConfig, h hostdb.Host, bytes uint64) types.Currency {
asc := h.PriceTable.BaseCost().Add(h.PriceTable.AppendSectorCost(cfg.Contracts.Period))
func sectorUploadCost(pt rhpv3.HostPriceTable, duration uint64) types.Currency {
asc := pt.BaseCost().Add(pt.AppendSectorCost(duration))
uploadSectorCostRHPv3, _ := asc.Total()
return uploadSectorCostRHPv3
}

func uploadCostForScore(cfg api.AutopilotConfig, h hostdb.Host, bytes uint64) types.Currency {
uploadSectorCostRHPv3 := sectorUploadCost(h.PriceTable.HostPriceTable, cfg.Contracts.Period)
numSectors := bytesToSectors(bytes)
return uploadSectorCostRHPv3.Mul64(numSectors)
}
Expand Down
7 changes: 4 additions & 3 deletions worker/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -396,9 +396,10 @@ func (w *worker) rhpScanHandler(jc jape.Context) {
// TODO: record metric

jc.Encode(api.RHPScanResponse{
Ping: api.ParamDuration(elapsed),
ScanError: errStr,
Settings: settings,
Ping: api.ParamDuration(elapsed),
PriceTable: priceTable,
ScanError: errStr,
Settings: settings,
})
}

Expand Down

0 comments on commit 93e0422

Please sign in to comment.