gitlab.com/SkynetLabs/skyd@v1.6.9/skymodules/renter/contractor/contractmaintenance.go (about)

     1  package contractor
     2  
     3  // contractmaintenance.go handles forming and renewing contracts for the
     4  // contractor. This includes deciding when new contracts need to be formed, when
     5  // contracts need to be renewed, and if contracts need to be blacklisted.
     6  
     7  import (
     8  	"fmt"
     9  	"math"
    10  	"math/big"
    11  	"reflect"
    12  	"time"
    13  
    14  	"gitlab.com/NebulousLabs/errors"
    15  	"gitlab.com/NebulousLabs/fastrand"
    16  
    17  	"gitlab.com/SkynetLabs/skyd/build"
    18  	"gitlab.com/SkynetLabs/skyd/skymodules"
    19  	"gitlab.com/SkynetLabs/skyd/skymodules/renter/proto"
    20  	"go.sia.tech/siad/modules"
    21  	"go.sia.tech/siad/persist"
    22  	"go.sia.tech/siad/types"
    23  )
    24  
    25  const (
    26  	// MaxCriticalRenewFailThreshold is the maximum number of contracts failing
    27  	// to renew as fraction of the total gfr contracts in the allowance before
    28  	// renew alerts are made critical.
    29  	MaxCriticalRenewFailThreshold = 0.2
    30  
    31  	// renewWindowLeewayDivisor controls how early we want to raise a renew
    32  	// error from severe to critical. Taking a divisor of 4, means that we raise
    33  	// a critical if a certain amount of renewals failed and if one fourth of
    34  	// the renewal window has passed. This seems like a sane value as we delete
    35  	// a contract after half the window has passed and the renew failed 12
    36  	// consecutive times, so it should give the error some time to resolve
    37  	// itself before becoming a critical.
    38  	renewWindowLeewayDivisor = 4
    39  )
    40  
    41  var (
    42  	// ErrInsufficientAllowance indicates that the renter's allowance is less
    43  	// than the amount necessary to store at least one sector
    44  	ErrInsufficientAllowance = errors.New("allowance is not large enough to cover fees of contract creation")
    45  	errTooExpensive          = errors.New("host price was too high")
    46  
    47  	// errContractEnded is the error returned when the contract has already ended
    48  	errContractEnded = errors.New("contract has already ended")
    49  
    50  	// errContractNotGFR is used to indicate that a contract renewal failed
    51  	// because the contract was marked !GFR.
    52  	errContractNotGFR = errors.New("contract is not GoodForRenew")
    53  
    54  	// errHostBlocked is the error returned when the host is blocked
    55  	errHostBlocked = errors.New("host is blocked")
    56  )
    57  
    58  type (
    59  	// decoratedFileContractRenewal wraps a fileContractRenewal struct and
    60  	// decorates it with a boolean that indicates whether the contract renewal
    61  	// is already a certain amount of time into the renew window
    62  	decoratedFileContractRenewal struct {
    63  		fileContractRenewal
    64  
    65  		lateRenewal bool
    66  	}
    67  
    68  	// fileContractRenewal is an instruction to renew a file contract.
    69  	fileContractRenewal struct {
    70  		id         types.FileContractID
    71  		amount     types.Currency
    72  		data       uint64
    73  		hostPubKey types.SiaPublicKey
    74  	}
    75  )
    76  
    77  // hostsForPortalFormation returns the hosts to form contracts with for a
    78  // portal. In contrast to regular renters, portals form contracts with every
    79  // host. It only ignores hosts that fail the gouging, have a bad score or hosts
    80  // that we have recoverable contracts with.
    81  func hostsForPortalFormation(allowance skymodules.Allowance, allContracts []skymodules.RenterContract, recoverableContracts []skymodules.RecoverableContract, activeHosts []skymodules.HostDBEntry, l *persist.Logger, scoreBreakdown func(skymodules.HostDBEntry) (skymodules.HostScoreBreakdown, error)) (int, []skymodules.HostDBEntry) {
    82  	if !allowance.PortalMode() {
    83  		build.Critical("hostsForPortalFormation was called on a non-portal")
    84  		return 0, nil
    85  	}
    86  	// Get a list of all current contracts.
    87  	currentContracts := make(map[string]skymodules.RenterContract)
    88  	for _, contract := range allContracts {
    89  		currentContracts[contract.HostPublicKey.String()] = contract
    90  	}
    91  	// Get a map of hosts we have recoverable contracts with.
    92  	recoverable := make(map[string]struct{})
    93  	for _, contract := range recoverableContracts {
    94  		recoverable[contract.HostPublicKey.String()] = struct{}{}
    95  	}
    96  
    97  	var hosts []skymodules.HostDBEntry
    98  	for _, host := range activeHosts {
    99  		// Check if there is already a contract with this host.
   100  		_, exists := currentContracts[host.PublicKey.String()]
   101  		if exists {
   102  			continue
   103  		}
   104  
   105  		// Skip host if it has a dead score.
   106  		sb, err := scoreBreakdown(host)
   107  		if err != nil || sb.Score.Cmp(types.NewCurrency64(1)) <= 0 {
   108  			l.Debugf("skipping host %v due to dead or unknown score (%v)", host.PublicKey, err)
   109  			continue
   110  		}
   111  
   112  		// Skip host if we have a recoverable contract with it.
   113  		_, recoverableContract := recoverable[host.PublicKey.String()]
   114  		if recoverableContract {
   115  			l.Debugf("skipping host %v due to having a recoverable contract with it", host.PublicKey)
   116  			continue
   117  		}
   118  
   119  		// Check that the price settings of the host are acceptable.
   120  		hostSettings := host.HostExternalSettings
   121  		err = staticCheckFormPaymentContractGouging(allowance, hostSettings)
   122  		if err != nil {
   123  			l.Debugf("payment contract loop igorning host %v for gouging: %v", hostSettings, err)
   124  			continue
   125  		}
   126  
   127  		// Append host if it passed all checks.
   128  		hosts = append(hosts, host)
   129  	}
   130  	return len(hosts), hosts
   131  }
   132  
   133  // hostsForRegularFormation returns the number of hosts needed for
   134  // non-portal contract formation plus a set of hosts to use.
   135  func hostsForRegularFormation(allowance skymodules.Allowance, allContracts []skymodules.RenterContract, recoverableContracts []skymodules.RecoverableContract, randomHosts func(_ int, _, _ []types.SiaPublicKey) ([]skymodules.HostDBEntry, error), l *persist.Logger) (int, []skymodules.HostDBEntry) {
   136  	if allowance.PortalMode() {
   137  		build.Critical("hostsForRegularFormation was called on a portal")
   138  		return 0, nil
   139  	}
   140  	// Count the number of contracts which are good for uploading, and then make
   141  	// more as needed to fill the gap.
   142  	uploadContracts := 0
   143  	for _, c := range allContracts {
   144  		if c.Utility.GoodForUpload {
   145  			uploadContracts++
   146  		}
   147  	}
   148  	neededContracts := int(allowance.Hosts) - uploadContracts
   149  	if neededContracts <= 0 {
   150  		l.Debugln("do not seem to need more contracts")
   151  		return 0, nil
   152  	}
   153  	if neededContracts > 0 {
   154  		l.Println("need more contracts:", neededContracts)
   155  	}
   156  
   157  	// Assemble two exclusion lists. The first one excludes all hosts that we
   158  	// already have contracts with and the second one excludes all hosts we have
   159  	// active contracts with. Then select a new batch of hosts to attempt
   160  	// contract formation with.
   161  	var blacklist []types.SiaPublicKey
   162  	var addressBlacklist []types.SiaPublicKey
   163  	for _, contract := range allContracts {
   164  		blacklist = append(blacklist, contract.HostPublicKey)
   165  		if !contract.Utility.Locked || contract.Utility.GoodForRenew || contract.Utility.GoodForUpload {
   166  			addressBlacklist = append(addressBlacklist, contract.HostPublicKey)
   167  		}
   168  	}
   169  	// Add the hosts we have recoverable contracts with to the blacklist to
   170  	// avoid losing existing data by forming a new/empty contract.
   171  	for _, contract := range recoverableContracts {
   172  		blacklist = append(blacklist, contract.HostPublicKey)
   173  	}
   174  
   175  	hosts, err := randomHosts(neededContracts*4+randomHostsBufferForScore, blacklist, addressBlacklist)
   176  	if err != nil {
   177  		l.Println("WARN: not forming new contracts:", err)
   178  		return 0, nil
   179  	}
   180  	l.Debugln("trying to form contracts with hosts, pulled this many hosts from hostdb:", len(hosts))
   181  	return neededContracts, hosts
   182  }
   183  
   184  // initialContractFunding computes the amount of money to put into the first
   185  // contract formed with a host.
   186  func initialContractFunding(a skymodules.Allowance, host skymodules.HostDBEntry, txnFee, min, max types.Currency) types.Currency {
   187  	if a.PortalMode() {
   188  		return a.PaymentContractInitialFunding
   189  	}
   190  
   191  	// Calculate the contract funding with host
   192  	contractFunds := host.ContractPrice.Add(txnFee).Mul64(ContractFeeFundingMulFactor)
   193  
   194  	// Check that the contract funding is reasonable compared to the max and
   195  	// min initial funding. This is to protect against increases to
   196  	// allowances being used up to fast and not being able to spread the
   197  	// funds across new contracts properly, as well as protecting against
   198  	// contracts renewing too quickly
   199  	if min.Cmp(max) > 0 && !max.IsZero() {
   200  		build.Critical(fmt.Sprintf("WARN: initialContractFunding min > max (%v > %v)", min, max))
   201  	}
   202  	if contractFunds.Cmp(max) > 0 && !max.IsZero() {
   203  		return max
   204  	}
   205  	if contractFunds.Cmp(min) < 0 {
   206  		return min
   207  	}
   208  	return contractFunds
   209  }
   210  
   211  // callNotifyDoubleSpend is used by the watchdog to alert the contractor
   212  // whenever a monitored file contract input is double-spent. This function
   213  // marks down the host score, and marks the contract as !GoodForRenew and
   214  // !GoodForUpload.
   215  func (c *Contractor) callNotifyDoubleSpend(fcID types.FileContractID, blockHeight types.BlockHeight) {
   216  	c.staticLog.Println("Watchdog found a double-spend: ", fcID, blockHeight)
   217  
   218  	// Mark the contract as double-spent. This will cause the contract to be
   219  	// excluded in period spending.
   220  	c.mu.Lock()
   221  	c.doubleSpentContracts[fcID] = blockHeight
   222  	c.mu.Unlock()
   223  
   224  	err := c.MarkContractBad(fcID)
   225  	if err != nil {
   226  		c.staticLog.Println("callNotifyDoubleSpend error in MarkContractBad", err)
   227  	}
   228  }
   229  
   230  // managedCheckForDuplicates checks for static contracts that have the same host
   231  // key and moves the older one to old contracts.
   232  func (c *Contractor) managedCheckForDuplicates() {
   233  	// Build map for comparison.
   234  	pubkeys := make(map[string]types.FileContractID)
   235  	var newContract, oldContract skymodules.RenterContract
   236  	for _, contract := range c.staticContracts.ViewAll() {
   237  		id, exists := pubkeys[contract.HostPublicKey.String()]
   238  		if !exists {
   239  			pubkeys[contract.HostPublicKey.String()] = contract.ID
   240  			continue
   241  		}
   242  
   243  		// Duplicate contract found, determine older contract to delete.
   244  		if rc, ok := c.staticContracts.View(id); ok {
   245  			if rc.StartHeight >= contract.StartHeight {
   246  				newContract, oldContract = rc, contract
   247  			} else {
   248  				newContract, oldContract = contract, rc
   249  			}
   250  			c.staticLog.Printf("Duplicate contract found. New contract is %x and old contract is %v", newContract.ID, oldContract.ID)
   251  
   252  			// Get SafeContract
   253  			oldSC, ok := c.staticContracts.Acquire(oldContract.ID)
   254  			if !ok {
   255  				// Update map
   256  				pubkeys[contract.HostPublicKey.String()] = newContract.ID
   257  				continue
   258  			}
   259  
   260  			// Link the contracts to each other and then store the old contract
   261  			// in the record of historic contracts.
   262  			//
   263  			// Note: This means that if there are multiple duplicates, say 3
   264  			// contracts that all share the same host, then the ordering may not
   265  			// be perfect. If in reality the renewal order was A<->B<->C, it's
   266  			// possible for the contractor to end up with A->C and B<->C in the
   267  			// mapping.
   268  			c.mu.Lock()
   269  			c.renewedFrom[newContract.ID] = oldContract.ID
   270  			c.renewedTo[oldContract.ID] = newContract.ID
   271  			c.oldContracts[oldContract.ID] = oldSC.Metadata()
   272  
   273  			// Save the contractor and delete the contract.
   274  			//
   275  			// TODO: Ideally these two things would happen atomically, but I'm
   276  			// not completely certain that's feasible with our current
   277  			// architecture.
   278  			//
   279  			// TODO: This should revert the in memory state in the event of an
   280  			// error and continue
   281  			err := c.save()
   282  			if err != nil {
   283  				c.staticLog.Println("Failed to save the contractor after updating renewed maps.")
   284  			}
   285  			c.mu.Unlock()
   286  			c.staticContracts.Delete(oldSC)
   287  
   288  			// Update the pubkeys map to contain the newest contract id.
   289  			pubkeys[contract.HostPublicKey.String()] = newContract.ID
   290  		}
   291  	}
   292  }
   293  
   294  // managedEstimateRenewFundingRequirements estimates the amount of money that a
   295  // contract is going to need in the next billing cycle by looking at how much
   296  // storage is in the contract and what the historic usage pattern of the
   297  // contract has been.
   298  func (c *Contractor) managedEstimateRenewFundingRequirements(contract skymodules.RenterContract, gfrContracts uint64, blockHeight types.BlockHeight, allowance skymodules.Allowance) (types.Currency, error) {
   299  	// Fetch the host pricing to use in the estimate.
   300  	host, exists, err := c.staticHDB.Host(contract.HostPublicKey)
   301  	if err != nil {
   302  		return types.ZeroCurrency, errors.AddContext(err, "error getting host from hostdb:")
   303  	}
   304  	if !exists {
   305  		return types.ZeroCurrency, errors.New("could not find host in hostdb")
   306  	}
   307  	if host.Filtered {
   308  		return types.ZeroCurrency, errHostBlocked
   309  	}
   310  
   311  	// Estimate the amount of money that's going to be needed for existing
   312  	// storage.
   313  	dataStored := contract.Transaction.FileContractRevisions[0].NewFileSize
   314  	storageCost := types.NewCurrency64(dataStored).Mul64(uint64(allowance.Period)).Mul(host.StoragePrice)
   315  
   316  	// For the spending estimates, we're going to need to know the amount of
   317  	// money that was spent on upload and download by this contract line in this
   318  	// period. That's going to require iterating over the renew history of the
   319  	// contract to get all the spending across any refreshes that occurred this
   320  	// period.
   321  	prevUploadSpending := contract.UploadSpending
   322  	prevDownloadSpending := contract.DownloadSpending
   323  	prevFundAccountSpending := contract.FundAccountSpending
   324  	prevMaintenanceSpending := contract.MaintenanceSpending
   325  	c.mu.Lock()
   326  	currentID := contract.ID
   327  	for i := 0; i < 10e3; i++ { // prevent an infinite loop if there's an [impossible] contract cycle
   328  		// If there is no previous contract, nothing to do.
   329  		var exists bool
   330  		currentID, exists = c.renewedFrom[currentID]
   331  		if !exists {
   332  			break
   333  		}
   334  
   335  		// If the contract is not in oldContracts, that's probably a bug, but
   336  		// nothing to do otherwise.
   337  		currentContract, exists := c.oldContracts[currentID]
   338  		if !exists {
   339  			c.staticLog.Println("WARN: A known previous contract is not found in c.oldContracts")
   340  			break
   341  		}
   342  
   343  		// If the contract did not start in the current period, then it is not
   344  		// relevant, and none of the previous contracts will be relevant either.
   345  		if currentContract.StartHeight < c.currentPeriod {
   346  			break
   347  		}
   348  
   349  		// Add the historical spending metrics.
   350  		prevUploadSpending = prevUploadSpending.Add(currentContract.UploadSpending)
   351  		prevDownloadSpending = prevDownloadSpending.Add(currentContract.DownloadSpending)
   352  		prevFundAccountSpending = prevFundAccountSpending.Add(currentContract.FundAccountSpending)
   353  		prevMaintenanceSpending = prevMaintenanceSpending.Add(currentContract.MaintenanceSpending)
   354  	}
   355  	c.mu.Unlock()
   356  
   357  	// Estimate the amount of money that's going to be needed for new storage
   358  	// based on the amount of new storage added in the previous period. Account
   359  	// for both the storage price as well as the upload price.
   360  	prevUploadDataEstimate := prevUploadSpending
   361  	if !host.UploadBandwidthPrice.IsZero() {
   362  		// TODO: Because the host upload bandwidth price can change, this is not
   363  		// the best way to estimate the amount of data that was uploaded to this
   364  		// contract. Better would be to look at the amount of data stored in the
   365  		// contract from the previous cycle and use that to determine how much
   366  		// total data.
   367  		prevUploadDataEstimate = prevUploadDataEstimate.Div(host.UploadBandwidthPrice)
   368  	}
   369  	// Sanity check - the host may have changed prices, make sure we aren't
   370  	// assuming an unreasonable amount of data.
   371  	if types.NewCurrency64(dataStored).Cmp(prevUploadDataEstimate) < 0 {
   372  		prevUploadDataEstimate = types.NewCurrency64(dataStored)
   373  	}
   374  	// The estimated cost for new upload spending is the previous upload
   375  	// bandwidth plus the implied storage cost for all of the new data.
   376  	newUploadsCost := prevUploadSpending.Add(prevUploadDataEstimate.Mul64(uint64(allowance.Period)).Mul(host.StoragePrice))
   377  
   378  	// The download cost is assumed to be the same. Even if the user is
   379  	// uploading more data, the expectation is that the download amounts will be
   380  	// relatively constant. Add in the contract price as well.
   381  	newDownloadsCost := prevDownloadSpending
   382  
   383  	// The estimated cost for funding ephemeral accounts and performing RHP3
   384  	// maintenance such as updating price tables and syncing the ephemeral
   385  	// account balance is expected to remain identical.
   386  	newFundAccountCost := prevFundAccountSpending
   387  	newMaintenanceCost := prevMaintenanceSpending.Sum()
   388  
   389  	contractPrice := host.ContractPrice
   390  
   391  	// Aggregate all estimates so far to compute the estimated siafunds fees.
   392  	// The transaction fees are not included in the siafunds estimate because
   393  	// users are not charged siafund fees on money that doesn't go into the file
   394  	// contract (and the transaction fee goes to the miners, not the file
   395  	// contract).
   396  	beforeSiafundFeesEstimate := storageCost.Add(newUploadsCost).Add(newDownloadsCost).Add(newFundAccountCost).Add(newMaintenanceCost).Add(contractPrice)
   397  	afterSiafundFeesEstimate := types.Tax(blockHeight, beforeSiafundFeesEstimate).Add(beforeSiafundFeesEstimate)
   398  
   399  	// Get an estimate for how much money we will be charged before going into
   400  	// the transaction pool.
   401  	_, maxTxnFee := c.staticTPool.FeeEstimation()
   402  	txnFees := maxTxnFee.Mul64(skymodules.EstimatedFileContractTransactionSetSize)
   403  
   404  	// Add them all up and then return the estimate plus 33% for error margin
   405  	// and just general volatility of usage pattern.
   406  	estimatedCost := afterSiafundFeesEstimate.Add(txnFees)
   407  	estimatedCost = estimatedCost.Add(estimatedCost.Div64(3))
   408  
   409  	// Check for a sane minimum that is equal to the initial contract funding
   410  	// but without an upper cap.
   411  	minInitialContractFunds := allowance.Funds.Div64(allowance.Hosts).Div64(MinInitialContractFundingDivFactor)
   412  	minimum := initialContractFunding(allowance, host, txnFees, minInitialContractFunds, types.ZeroCurrency)
   413  	if estimatedCost.Cmp(minimum) < 0 {
   414  		c.staticLog.Printf("Contract renew amount %v below minimum amount %v", estimatedCost, minimum)
   415  		estimatedCost = minimum
   416  	}
   417  	return estimatedCost, nil
   418  }
   419  
   420  // callInterruptContractMaintenance will issue an interrupt signal to any
   421  // running maintenance, stopping that maintenance. If there are multiple threads
   422  // running maintenance, they will all be stopped.
   423  func (c *Contractor) callInterruptContractMaintenance() {
   424  	// Spin up a thread to grab the maintenance lock. Signal that the lock was
   425  	// acquired after the lock is acquired.
   426  	gotLock := make(chan struct{})
   427  	go func() {
   428  		c.maintenanceLock.Lock()
   429  		close(gotLock)
   430  		c.maintenanceLock.Unlock()
   431  	}()
   432  
   433  	// There may be multiple threads contending for the maintenance lock. Issue
   434  	// interrupts repeatedly until we get a signal that the maintenance lock has
   435  	// been acquired.
   436  	for {
   437  		select {
   438  		case <-gotLock:
   439  			return
   440  		case c.staticInterruptMaintenance <- struct{}{}:
   441  			c.staticLog.Debugln("Signal sent to interrupt contract maintenance")
   442  		}
   443  	}
   444  }
   445  
   446  // managedAddPreferredHosts adds toAdd hosts to the preferredHosts set from the
   447  // potentialHosts set and removes them from the potentialHosts.
   448  func (c *Contractor) managedAddPreferredHosts(toAdd int, preferredHosts, potentialHosts map[string]struct{}) {
   449  	// Grab random hosts from the potential set that are not already in the
   450  	// preferred set. To do so, we use the preferred hosts as the blacklist and
   451  	// the potential hosts as the whitelist.
   452  	var blacklist []types.SiaPublicKey
   453  	for host := range preferredHosts {
   454  		var spk types.SiaPublicKey
   455  		err := spk.LoadString(host)
   456  		if err != nil {
   457  			build.Critical("managedLimitGFUHosts: failed to marshal hostkey", err)
   458  			delete(preferredHosts, host)
   459  			continue
   460  		}
   461  		// Ignore hosts that are already in the preferred set.
   462  		blacklist = append(blacklist, spk)
   463  	}
   464  	hosts, err := c.staticHDB.RandomHostsWithWhitelist(toAdd, blacklist, blacklist, potentialHosts)
   465  	if err != nil {
   466  		c.staticLog.Print("managedLimitGFUHosts: failed to get random hosts:", err)
   467  		return
   468  	}
   469  
   470  	// Add hosts to fill the set.
   471  	for i := 0; i < len(hosts) && toAdd > 0; i++ {
   472  		host := hosts[i].PublicKey.String()
   473  		_, exists := potentialHosts[host]
   474  		if !exists {
   475  			continue // try next
   476  		}
   477  		preferredHosts[host] = struct{}{}
   478  		delete(potentialHosts, host)
   479  		toAdd--
   480  	}
   481  }
   482  
   483  // managedFindMinAllowedHostScores uses a set of random hosts from the hostdb to
   484  // calculate minimum acceptable score for a host to be marked GFR and GFU.
   485  func (c *Contractor) managedFindMinAllowedHostScores(hostCount uint64) (types.Currency, types.Currency, error) {
   486  	// Pull a new set of hosts from the hostdb that could be used as a new set
   487  	// to match the allowance. The lowest scoring host of these new hosts will
   488  	// be used as a baseline for determining whether our existing contracts are
   489  	// worthwhile.
   490  	hosts, err := c.staticHDB.RandomHosts(int(hostCount)+randomHostsBufferForScore, nil, nil)
   491  	if err != nil {
   492  		return types.Currency{}, types.Currency{}, err
   493  	}
   494  
   495  	if len(hosts) == 0 {
   496  		return types.Currency{}, types.Currency{}, errors.New("No hosts returned in RandomHosts")
   497  	}
   498  
   499  	// Find the minimum score that a host is allowed to have to be considered
   500  	// good for upload.
   501  	var minScoreGFR, minScoreGFU types.Currency
   502  	sb, err := c.staticHDB.ScoreBreakdown(hosts[0])
   503  	if err != nil {
   504  		return types.Currency{}, types.Currency{}, err
   505  	}
   506  
   507  	lowestScore := sb.Score
   508  	for i := 1; i < len(hosts); i++ {
   509  		score, err := c.staticHDB.ScoreBreakdown(hosts[i])
   510  		if err != nil {
   511  			return types.Currency{}, types.Currency{}, err
   512  		}
   513  		if score.Score.Cmp(lowestScore) < 0 {
   514  			lowestScore = score.Score
   515  		}
   516  	}
   517  	// Set the minimum acceptable score to a factor of the lowest score.
   518  	minScoreGFR = lowestScore.Div(scoreLeewayGoodForRenew)
   519  	minScoreGFU = lowestScore.Div(scoreLeewayGoodForUpload)
   520  
   521  	// Set min score to the max score seen times 2.
   522  	if c.staticDeps.Disrupt("HighMinHostScore") {
   523  		var maxScore types.Currency
   524  		for i := 1; i < len(hosts); i++ {
   525  			score, err := c.staticHDB.ScoreBreakdown(hosts[i])
   526  			if err != nil {
   527  				return types.Currency{}, types.Currency{}, err
   528  			}
   529  			if score.Score.Cmp(maxScore) > 0 {
   530  				maxScore = score.Score
   531  			}
   532  		}
   533  		minScoreGFR = maxScore.Mul64(2)
   534  	}
   535  
   536  	return minScoreGFR, minScoreGFU, nil
   537  }
   538  
   539  // managedNewContract negotiates an initial file contract with the specified
   540  // host, saves it, and returns it.
   541  func (c *Contractor) managedNewContract(host skymodules.HostDBEntry, allowance skymodules.Allowance, contractFunding types.Currency, endHeight types.BlockHeight) (_ types.Currency, _ skymodules.RenterContract, err error) {
   542  	// reject hosts that are too expensive
   543  	if host.StoragePrice.Cmp(maxStoragePrice) > 0 {
   544  		return types.ZeroCurrency, skymodules.RenterContract{}, errTooExpensive
   545  	}
   546  	// Determine if host settings align with allowance period
   547  	c.mu.Lock()
   548  	if reflect.DeepEqual(allowance, skymodules.Allowance{}) {
   549  		c.mu.Unlock()
   550  		return types.ZeroCurrency, skymodules.RenterContract{}, errors.New("called managedNewContract but allowance wasn't set")
   551  	}
   552  	hostSettings := host.HostExternalSettings
   553  	period := allowance.Period
   554  	c.mu.Unlock()
   555  
   556  	if host.MaxDuration < period {
   557  		err := errors.New("unable to form contract with host due to insufficient MaxDuration of host")
   558  		return types.ZeroCurrency, skymodules.RenterContract{}, err
   559  	}
   560  	// cap host.MaxCollateral
   561  	if host.MaxCollateral.Cmp(maxCollateral) > 0 {
   562  		host.MaxCollateral = maxCollateral
   563  	}
   564  
   565  	// Check for price gouging.
   566  	err = checkFormContractGouging(allowance, hostSettings)
   567  	if err != nil {
   568  		return types.ZeroCurrency, skymodules.RenterContract{}, errors.AddContext(err, "unable to form a contract due to price gouging detection")
   569  	}
   570  
   571  	// get an address to use for negotiation
   572  	uc, err := c.staticWallet.NextAddress()
   573  	if err != nil {
   574  		return types.ZeroCurrency, skymodules.RenterContract{}, err
   575  	}
   576  	defer func() {
   577  		if err != nil {
   578  			err = errors.Compose(err, c.staticWallet.MarkAddressUnused(uc))
   579  		}
   580  	}()
   581  
   582  	// get the wallet seed.
   583  	seed, _, err := c.staticWallet.PrimarySeed()
   584  	if err != nil {
   585  		return types.ZeroCurrency, skymodules.RenterContract{}, err
   586  	}
   587  	// derive the renter seed and wipe it once we are done with it.
   588  	renterSeed := skymodules.DeriveRenterSeed(seed)
   589  	defer fastrand.Read(renterSeed[:])
   590  
   591  	// create contract params
   592  	c.mu.RLock()
   593  	params := skymodules.ContractParams{
   594  		Allowance:     allowance,
   595  		Host:          host,
   596  		Funding:       contractFunding,
   597  		StartHeight:   c.blockHeight,
   598  		EndHeight:     endHeight,
   599  		RefundAddress: uc.UnlockHash(),
   600  		RenterSeed:    renterSeed.EphemeralRenterSeed(endHeight),
   601  	}
   602  	c.mu.RUnlock()
   603  
   604  	// wipe the renter seed once we are done using it.
   605  	defer fastrand.Read(params.RenterSeed[:])
   606  
   607  	// create transaction builder and trigger contract formation.
   608  	txnBuilder, err := c.staticWallet.StartTransaction()
   609  	if err != nil {
   610  		return types.ZeroCurrency, skymodules.RenterContract{}, err
   611  	}
   612  
   613  	contract, formationTxnSet, sweepTxn, sweepParents, err := c.staticContracts.FormContract(params, txnBuilder, c.staticTPool, c.staticHDB, c.staticTG.StopChan())
   614  	if err != nil {
   615  		txnBuilder.Drop()
   616  		return types.ZeroCurrency, skymodules.RenterContract{}, err
   617  	}
   618  
   619  	monitorContractArgs := monitorContractArgs{
   620  		false,
   621  		contract.ID,
   622  		contract.Transaction,
   623  		formationTxnSet,
   624  		sweepTxn,
   625  		sweepParents,
   626  		params.StartHeight,
   627  	}
   628  	err = c.staticWatchdog.callMonitorContract(monitorContractArgs)
   629  	if err != nil {
   630  		return types.ZeroCurrency, skymodules.RenterContract{}, err
   631  	}
   632  
   633  	// Add a mapping from the contract's id to the public key of the host.
   634  	c.mu.Lock()
   635  	_, exists := c.pubKeysToContractID[contract.HostPublicKey.String()]
   636  	if exists {
   637  		c.mu.Unlock()
   638  		txnBuilder.Drop()
   639  		// We need to return a funding value because money was spent on this
   640  		// host, even though the full process could not be completed.
   641  		c.staticLog.Println("WARN: Attempted to form a new contract with a host that we already have a contrat with.")
   642  		return contractFunding, skymodules.RenterContract{}, fmt.Errorf("We already have a contract with host %v", contract.HostPublicKey)
   643  	}
   644  	c.pubKeysToContractID[contract.HostPublicKey.String()] = contract.ID
   645  	c.mu.Unlock()
   646  
   647  	contractValue := contract.RenterFunds
   648  	c.staticLog.Printf("Formed contract %v with %v for %v", contract.ID, host.NetAddress, contractValue.HumanString())
   649  
   650  	// Update the hostdb to include the new contract.
   651  	err = c.staticHDB.UpdateContracts(c.staticContracts.ViewAll())
   652  	if err != nil {
   653  		c.staticLog.Println("Unable to update hostdb contracts:", err)
   654  	}
   655  	return contractFunding, contract, nil
   656  }
   657  
   658  // managedPrunedRedundantAddressRange uses the hostdb to find hosts that
   659  // violate the rules about address ranges and cancels them.
   660  func (c *Contractor) managedPrunedRedundantAddressRange() {
   661  	// Get all contracts which are not canceled.
   662  	allContracts := c.staticContracts.ViewAll()
   663  	var contracts []skymodules.RenterContract
   664  	for _, contract := range allContracts {
   665  		if contract.Utility.Locked && !contract.Utility.GoodForRenew && !contract.Utility.GoodForUpload {
   666  			// contract is canceled
   667  			continue
   668  		}
   669  		contracts = append(contracts, contract)
   670  	}
   671  
   672  	// Get all the public keys and map them to contract ids.
   673  	pks := make([]types.SiaPublicKey, 0, len(allContracts))
   674  	cids := make(map[string]types.FileContractID)
   675  	for _, contract := range contracts {
   676  		pks = append(pks, contract.HostPublicKey)
   677  		cids[contract.HostPublicKey.String()] = contract.ID
   678  	}
   679  
   680  	// Let the hostdb filter out bad hosts and cancel contracts with those
   681  	// hosts.
   682  	badHosts, err := c.staticHDB.CheckForIPViolations(pks)
   683  	if err != nil {
   684  		c.staticLog.Println("WARN: error checking for IP violations:", err)
   685  		return
   686  	}
   687  	for _, host := range badHosts {
   688  		if err := c.managedCancelContract(cids[host.String()]); err != nil {
   689  			c.staticLog.Print("WARNING: Wasn't able to cancel contract in managedPrunedRedundantAddressRange", err)
   690  		}
   691  	}
   692  }
   693  
   694  // managedLimitGFUHosts caps the number of GFU hosts to allowance.Hosts.
   695  func (c *Contractor) managedLimitGFUHosts(contracts []skymodules.RenterContract, updates map[types.FileContractID]skymodules.ContractUtility, wantedHosts uint64) {
   696  	c.mu.Lock()
   697  	// Store the preferred contracts in a temporary map. If we have too many
   698  	// of them for the current allowance, we delete some.
   699  	preferredHosts := make(map[string]struct{})
   700  	for hk := range c.preferredHosts {
   701  		if uint64(len(preferredHosts)) < wantedHosts {
   702  			preferredHosts[hk] = struct{}{}
   703  		}
   704  	}
   705  	c.mu.Unlock()
   706  
   707  	potentialHosts := make(map[string]struct{})
   708  	for _, contract := range contracts {
   709  		// If the contract is !gfu ignore it.
   710  		if !contract.Utility.GoodForUpload {
   711  			continue
   712  		}
   713  		// If it is gfu, mark the corresponding host as a potential candidate.
   714  		potentialHosts[contract.HostPublicKey.String()] = struct{}{}
   715  	}
   716  
   717  	// If a preferred host is not part of the potential hosts, it's no longer
   718  	// preferred so we delete it. We also delete hosts that are in the preferred
   719  	// hosts from the potential ones.
   720  	for host := range preferredHosts {
   721  		_, exists := potentialHosts[host]
   722  		delete(potentialHosts, host)
   723  		if !exists {
   724  			delete(preferredHosts, host)
   725  		}
   726  	}
   727  
   728  	// Now we are only left with the preferred hosts that are gfu.
   729  	// If they are too many, trim them.
   730  	toTrim := len(preferredHosts) - int(wantedHosts)
   731  	for host := range preferredHosts {
   732  		if toTrim <= 0 {
   733  			break
   734  		}
   735  		delete(preferredHosts, host)
   736  		toTrim--
   737  	}
   738  	// If there are too few, add some.
   739  	toAdd := int(wantedHosts) - len(preferredHosts)
   740  	if toAdd > 0 {
   741  		c.managedAddPreferredHosts(toAdd, preferredHosts, potentialHosts)
   742  	}
   743  
   744  	// Sanity check length of set.
   745  	if uint64(len(preferredHosts)) > wantedHosts {
   746  		build.Critical("too many contracts in the set of preferred contracts")
   747  	}
   748  
   749  	// Mark all contracts that are not in the preferred set as !gfu.
   750  	for _, contract := range contracts {
   751  		_, isPreferred := preferredHosts[contract.HostPublicKey.String()]
   752  		if isPreferred {
   753  			continue // nothing to do
   754  		}
   755  		if !contract.Utility.GoodForUpload {
   756  			continue // nothing to do
   757  		}
   758  		c.staticLog.Printf("[CONTRACTUTILITY][%v] marking contract as !GFU to take into accounts the cap defined by the allowance (update not applied yet), GFU: true -> false\n", contract.ID)
   759  		u := contract.Utility
   760  		u.GoodForUpload = false
   761  		updates[contract.ID] = u
   762  	}
   763  
   764  	// Update the preferred contracts.
   765  	c.mu.Lock()
   766  	c.preferredHosts = preferredHosts
   767  	c.mu.Unlock()
   768  }
   769  
   770  // staticCheckFormPaymentContractGouging will check whether the pricing from the
   771  // host for forming a payment contract is too high to justify forming a contract
   772  // with this host.
   773  func staticCheckFormPaymentContractGouging(allowance skymodules.Allowance, hostSettings modules.HostExternalSettings) error {
   774  	// Check whether the RPC base price is too high.
   775  	if !allowance.MaxRPCPrice.IsZero() && allowance.MaxRPCPrice.Cmp(hostSettings.BaseRPCPrice) <= 0 {
   776  		return errors.New("rpc base price of host is too high - extortion protection enabled")
   777  	}
   778  	// Check whether the form contract price is too high.
   779  	if !allowance.MaxContractPrice.IsZero() && allowance.MaxContractPrice.Cmp(hostSettings.ContractPrice) <= 0 {
   780  		return errors.New("contract price of host is too high - extortion protection enabled")
   781  	}
   782  	// Check whether the sector access price is too high.
   783  	if !allowance.MaxSectorAccessPrice.IsZero() && allowance.MaxSectorAccessPrice.Cmp(hostSettings.SectorAccessPrice) <= 0 {
   784  		return errors.New("sector access price of host is too high - extortion protection enabled")
   785  	}
   786  
   787  	// Check whether the form contract price does not leave enough room for
   788  	// uploads and downloads. At least half of the payment contract's funds need
   789  	// to remain for download bandwidth.
   790  	if allowance.PaymentContractInitialFunding.Div64(2).Cmp(hostSettings.ContractPrice) <= 0 {
   791  		return errors.New("contract price of host is too high - extortion protection enabled")
   792  	}
   793  	return nil
   794  }
   795  
   796  // checkFormContractGouging will check whether the pricing for forming
   797  // this contract triggers any price gouging warnings.
   798  func checkFormContractGouging(allowance skymodules.Allowance, hostSettings modules.HostExternalSettings) error {
   799  	// Check whether the RPC base price is too high.
   800  	if !allowance.MaxRPCPrice.IsZero() && allowance.MaxRPCPrice.Cmp(hostSettings.BaseRPCPrice) < 0 {
   801  		return errors.New("rpc base price of host is too high - price gouging protection enabled")
   802  	}
   803  	// Check whether the form contract price is too high.
   804  	if !allowance.MaxContractPrice.IsZero() && allowance.MaxContractPrice.Cmp(hostSettings.ContractPrice) < 0 {
   805  		return errors.New("contract price of host is too high - price gouging protection enabled")
   806  	}
   807  
   808  	return nil
   809  }
   810  
   811  // managedRenew negotiates a new contract for data already stored with a host.
   812  // It returns the new contract. This is a blocking call that performs network
   813  // I/O.
   814  func (c *Contractor) managedRenew(id types.FileContractID, a skymodules.Allowance, hpk types.SiaPublicKey, contractFunding types.Currency, newEndHeight types.BlockHeight, hostSettings modules.HostExternalSettings) (_ skymodules.RenterContract, err error) {
   815  	// Fetch the host associated with this contract.
   816  	host, ok, err := c.staticHDB.Host(hpk)
   817  	if err != nil {
   818  		return skymodules.RenterContract{}, errors.AddContext(err, "error getting host from hostdb:")
   819  	}
   820  	// Use the most recent hostSettings, along with the host db entry.
   821  	host.HostExternalSettings = hostSettings
   822  
   823  	if c.staticDeps.Disrupt("DefaultRenewSettings") {
   824  		c.staticLog.Debugln("Using default host settings")
   825  		host.HostExternalSettings = modules.DefaultHostExternalSettings()
   826  		// Reset some specific settings, not available through the default.
   827  		host.HostExternalSettings.NetAddress = hostSettings.NetAddress
   828  		host.HostExternalSettings.RemainingStorage = hostSettings.RemainingStorage
   829  		host.HostExternalSettings.TotalStorage = hostSettings.TotalStorage
   830  		host.HostExternalSettings.UnlockHash = hostSettings.UnlockHash
   831  		host.HostExternalSettings.RevisionNumber = hostSettings.RevisionNumber
   832  		host.HostExternalSettings.SiaMuxPort = hostSettings.SiaMuxPort
   833  	}
   834  
   835  	if reflect.DeepEqual(a, skymodules.Allowance{}) {
   836  		return skymodules.RenterContract{}, errors.New("called managedRenew but allowance isn't set")
   837  	}
   838  	period := a.Period
   839  
   840  	if !ok {
   841  		return skymodules.RenterContract{}, errHostNotFound
   842  	} else if host.Filtered {
   843  		return skymodules.RenterContract{}, errHostBlocked
   844  	} else if host.StoragePrice.Cmp(maxStoragePrice) > 0 {
   845  		return skymodules.RenterContract{}, errTooExpensive
   846  	} else if host.MaxDuration < period {
   847  		return skymodules.RenterContract{}, errors.New("insufficient MaxDuration of host")
   848  	}
   849  
   850  	// cap host.MaxCollateral
   851  	if host.MaxCollateral.Cmp(maxCollateral) > 0 {
   852  		host.MaxCollateral = maxCollateral
   853  	}
   854  
   855  	// Check for price gouging on the renewal.
   856  	err = checkFormContractGouging(a, host.HostExternalSettings)
   857  	if err != nil {
   858  		return skymodules.RenterContract{}, errors.AddContext(err, "unable to renew - price gouging protection enabled")
   859  	}
   860  
   861  	// get an address to use for negotiation
   862  	uc, err := c.staticWallet.NextAddress()
   863  	if err != nil {
   864  		return skymodules.RenterContract{}, err
   865  	}
   866  	defer func() {
   867  		if err != nil {
   868  			err = errors.Compose(err, c.staticWallet.MarkAddressUnused(uc))
   869  		}
   870  	}()
   871  
   872  	// get the wallet seed
   873  	seed, _, err := c.staticWallet.PrimarySeed()
   874  	if err != nil {
   875  		return skymodules.RenterContract{}, err
   876  	}
   877  	// derive the renter seed and wipe it after we are done with it.
   878  	renterSeed := skymodules.DeriveRenterSeed(seed)
   879  	defer fastrand.Read(renterSeed[:])
   880  
   881  	// create contract params
   882  	c.mu.RLock()
   883  	params := skymodules.ContractParams{
   884  		Allowance:     a,
   885  		Host:          host,
   886  		Funding:       contractFunding,
   887  		StartHeight:   c.blockHeight,
   888  		EndHeight:     newEndHeight,
   889  		RefundAddress: uc.UnlockHash(),
   890  		RenterSeed:    renterSeed.EphemeralRenterSeed(newEndHeight),
   891  	}
   892  	c.mu.RUnlock()
   893  
   894  	// wipe the renter seed once we are done using it.
   895  	defer fastrand.Read(params.RenterSeed[:])
   896  
   897  	// create a transaction builder with the correct amount of funding for the renewal.
   898  	txnBuilder, err := c.staticWallet.StartTransaction()
   899  	if err != nil {
   900  		return skymodules.RenterContract{}, err
   901  	}
   902  	err = txnBuilder.FundSiacoins(params.Funding)
   903  	if err != nil {
   904  		txnBuilder.Drop() // return unused outputs to wallet
   905  		return skymodules.RenterContract{}, err
   906  	}
   907  	// Add an output that sends all fund back to the refundAddress.
   908  	// Note that in order to send this transaction, a miner fee will have to be subtracted.
   909  	output := types.SiacoinOutput{
   910  		Value:      params.Funding,
   911  		UnlockHash: params.RefundAddress,
   912  	}
   913  	sweepTxn, sweepParents := txnBuilder.Sweep(output)
   914  
   915  	var newContract skymodules.RenterContract
   916  	var formationTxnSet []types.Transaction
   917  	if c.staticDeps.Disrupt("LegacyRenew") || build.VersionCmp(host.Version, "1.5.4") < 0 {
   918  		// Acquire the SafeContract.
   919  		oldContract, ok := c.staticContracts.Acquire(id)
   920  		if !ok {
   921  			return skymodules.RenterContract{}, errContractNotFound
   922  		}
   923  		if !oldContract.Utility().GoodForRenew {
   924  			return skymodules.RenterContract{}, errContractNotGFR
   925  		}
   926  		// RHP2 renewal.
   927  		newContract, formationTxnSet, err = c.staticContracts.Renew(oldContract, params, txnBuilder, c.staticTPool, c.staticHDB, c.staticTG.StopChan())
   928  		c.staticContracts.Return(oldContract)
   929  	} else {
   930  		var w skymodules.Worker
   931  		w, err = c.staticWorkerPool.Worker(hpk)
   932  		if err != nil {
   933  			txnBuilder.Drop() // return unused outputs to wallet
   934  			return skymodules.RenterContract{}, err
   935  		}
   936  		newContract, formationTxnSet, err = w.RenewContract(c.staticTG.StopCtx(), id, params, txnBuilder)
   937  	}
   938  	if err != nil {
   939  		txnBuilder.Drop() // return unused outputs to wallet
   940  		return skymodules.RenterContract{}, err
   941  	}
   942  
   943  	monitorContractArgs := monitorContractArgs{
   944  		false,
   945  		newContract.ID,
   946  		newContract.Transaction,
   947  		formationTxnSet,
   948  		sweepTxn,
   949  		sweepParents,
   950  		params.StartHeight,
   951  	}
   952  	err = c.staticWatchdog.callMonitorContract(monitorContractArgs)
   953  	if err != nil {
   954  		return skymodules.RenterContract{}, err
   955  	}
   956  
   957  	// Add a mapping from the contract's id to the public key of the host. This
   958  	// will destroy the previous mapping from pubKey to contract id but other
   959  	// modules are only interested in the most recent contract anyway.
   960  	c.mu.Lock()
   961  	c.pubKeysToContractID[newContract.HostPublicKey.String()] = newContract.ID
   962  	c.mu.Unlock()
   963  
   964  	// Update the hostdb to include the new contract.
   965  	err = c.staticHDB.UpdateContracts(c.staticContracts.ViewAll())
   966  	if err != nil {
   967  		c.staticLog.Println("Unable to update hostdb contracts:", err)
   968  	}
   969  
   970  	return newContract, nil
   971  }
   972  
   973  // managedRenewContract will use the renew instructions to renew a contract,
   974  // returning the amount of money that was put into the contract for renewal.
   975  func (c *Contractor) managedRenewContract(renewInstructions fileContractRenewal, currentPeriod types.BlockHeight, allowance skymodules.Allowance, blockHeight, endHeight types.BlockHeight) (fundsSpent types.Currency, err error) {
   976  	if c.staticDeps.Disrupt("ContractRenewFail") {
   977  		err = errors.New("Renew failure due to dependency")
   978  		return
   979  	}
   980  	// Pull the variables out of the renewal.
   981  	id := renewInstructions.id
   982  	amount := renewInstructions.amount
   983  	hostPubKey := renewInstructions.hostPubKey
   984  
   985  	// Get a session with the host, before marking it as being renewed.
   986  	hs, err := c.Session(hostPubKey, c.staticTG.StopChan())
   987  	if err != nil {
   988  		err = errors.AddContext(err, "Unable to establish session with host")
   989  		return
   990  	}
   991  	s := hs.(*hostSession)
   992  
   993  	// Mark the contract as being renewed, and defer logic to unmark it
   994  	// once renewing is complete.
   995  	c.staticLog.Debugln("Marking a contract for renew:", id)
   996  	c.mu.Lock()
   997  	c.renewing[id] = true
   998  	c.mu.Unlock()
   999  	defer func() {
  1000  		c.staticLog.Debugln("Unmarking the contract for renew", id)
  1001  		c.mu.Lock()
  1002  		delete(c.renewing, id)
  1003  		c.mu.Unlock()
  1004  	}()
  1005  
  1006  	// Wait for any active editors/downloaders/sessions to finish for this
  1007  	// contract, and then grab the latest host settings.
  1008  	var hostSettings modules.HostExternalSettings
  1009  	c.mu.RLock()
  1010  	e, eok := c.editors[id]
  1011  	d, dok := c.downloaders[id]
  1012  	c.mu.RUnlock()
  1013  	if eok {
  1014  		c.staticLog.Debugln("Waiting for editor invalidation")
  1015  		e.callInvalidate()
  1016  		c.staticLog.Debugln("Got editor invalidation")
  1017  	}
  1018  	if dok {
  1019  		c.staticLog.Debugln("Waiting for downloader invalidation")
  1020  		d.callInvalidate()
  1021  		c.staticLog.Debugln("Got downloader invalidation")
  1022  	}
  1023  
  1024  	// Use the Settings RPC with the host and then invalidate the session.
  1025  	hostSettings, err = s.Settings()
  1026  	if err != nil {
  1027  		err = errors.AddContext(err, "Unable to get host settings")
  1028  		return
  1029  	}
  1030  	c.staticLog.Debugln("Waiting for session invalidation")
  1031  	s.callInvalidate()
  1032  	c.staticLog.Debugln("Got session invalidation")
  1033  
  1034  	// Perform the actual renew. If the renew fails, return the
  1035  	// contract. If the renew fails we check how often it has failed
  1036  	// before. Once it has failed for a certain number of blocks in a
  1037  	// row and reached its second half of the renew window, we give up
  1038  	// on renewing it and set goodForRenew to false.
  1039  	c.staticLog.Debugln("calling managedRenew on contract", id)
  1040  	newContract, errRenew := c.managedRenew(id, allowance, hostPubKey, amount, endHeight, hostSettings)
  1041  	c.staticLog.Debugln("managedRenew has returned with error:", errRenew)
  1042  	oldContract, exists := c.staticContracts.Acquire(id)
  1043  	if !exists {
  1044  		return types.ZeroCurrency, errors.AddContext(errContractNotFound, "failed to acquire oldContract after renewal")
  1045  	}
  1046  	oldUtility := oldContract.Utility()
  1047  	md := oldContract.Metadata()
  1048  	if errRenew != nil {
  1049  		// Increment the number of failed renews for the contract if it
  1050  		// was the host's fault.
  1051  		if skymodules.IsHostsFault(errRenew) {
  1052  			c.mu.Lock()
  1053  			c.numFailedRenews[oldContract.Metadata().ID]++
  1054  			totalFailures := c.numFailedRenews[oldContract.Metadata().ID]
  1055  			c.mu.Unlock()
  1056  			c.staticLog.Debugln("remote host determined to be at fault, tallying up failed renews", totalFailures, id)
  1057  		}
  1058  
  1059  		// Check if contract has to be replaced.
  1060  		c.mu.RLock()
  1061  		numRenews, failedBefore := c.numFailedRenews[md.ID]
  1062  		c.mu.RUnlock()
  1063  		secondHalfOfWindow := blockHeight+allowance.RenewWindow/2 >= md.EndHeight
  1064  		replace := numRenews >= ConsecutiveRenewalsBeforeReplacement
  1065  		if failedBefore && secondHalfOfWindow && replace {
  1066  			errorMsg := fmt.Sprintf("[CONTRACTUTILITY][%v] WARN: consistently failed to renew contract for host %v, marked as bad and locked: %v, GFU: %v -> false, GFR: %v -> false, GFRef: %v -> false",
  1067  				md.ID, md.HostPublicKey, errRenew, oldUtility.GoodForUpload, oldUtility.GoodForRenew, oldUtility.GoodForRefresh)
  1068  
  1069  			oldUtility.GoodForRefresh = false
  1070  			oldUtility.GoodForRenew = false
  1071  			oldUtility.GoodForUpload = false
  1072  			oldUtility.Locked = true
  1073  			err := c.callUpdateUtility(oldContract, oldUtility, true)
  1074  			if err != nil {
  1075  				errorMsg += fmt.Sprintf(", failed to update utility: %v", err)
  1076  			}
  1077  			c.staticLog.Println(errorMsg)
  1078  			c.staticContracts.Return(oldContract)
  1079  			return types.ZeroCurrency, errors.AddContext(errRenew, "contract marked as bad for too many consecutive failed renew attempts")
  1080  		}
  1081  
  1082  		// Seems like it doesn't have to be replaced yet. Log the
  1083  		// failure and number of renews that have failed so far.
  1084  		c.staticLog.Printf("WARN: failed to renew contract %v [%v]: '%v', current height: %v, proposed end height: %v, max duration: %v",
  1085  			oldContract.Metadata().HostPublicKey, numRenews, errRenew, blockHeight, endHeight, hostSettings.MaxDuration)
  1086  		c.staticContracts.Return(oldContract)
  1087  		return types.ZeroCurrency, errors.AddContext(errRenew, "contract renewal with host was unsuccessful")
  1088  	}
  1089  	c.staticLog.Printf("Renewed contract %v\n", id)
  1090  
  1091  	// Skip the deletion of the old contract if required and delete the new
  1092  	// contract to make sure we keep using the old one even though it has been
  1093  	// finalized.
  1094  	if c.staticDeps.Disrupt("SkipContractDeleteAfterRenew") {
  1095  		c.staticContracts.Return(oldContract)
  1096  		newSC, ok := c.staticContracts.Acquire(newContract.ID)
  1097  		if ok {
  1098  			c.staticContracts.Delete(newSC)
  1099  		}
  1100  		return amount, nil
  1101  	}
  1102  
  1103  	// Update the utility values for the old contract. We ignore the churn
  1104  	// here because we renewed the contract successfully. So we don't want
  1105  	// to consider this churn.
  1106  	msg := fmt.Sprintf("[CONTRACTUTILITY][%v] updating old contract utility after successful renew, locked: %v -> true, GFU: %v -> false, GFR: %v -> false, GFRef: %v -> false", md.ID, oldUtility.Locked, oldUtility.GoodForUpload, oldUtility.GoodForRenew, oldUtility.GoodForRefresh)
  1107  	oldUtility.GoodForRefresh = false
  1108  	oldUtility.GoodForRenew = false
  1109  	oldUtility.GoodForUpload = false
  1110  	oldUtility.Locked = true
  1111  	if err := c.callUpdateUtility(oldContract, oldUtility, true); err != nil {
  1112  		c.staticLog.Println("Failed to update the contract utilities", err)
  1113  		c.staticContracts.Return(oldContract)
  1114  		return amount, nil // Error is not returned because the renew succeeded.
  1115  	}
  1116  	c.staticLog.Println(msg)
  1117  
  1118  	if c.staticDeps.Disrupt("InterruptContractSaveToDiskAfterDeletion") {
  1119  		c.staticContracts.Return(oldContract)
  1120  		return amount, errors.New("InterruptContractSaveToDiskAfterDeletion disrupt")
  1121  	}
  1122  	// Lock the contractor as we update it to use the new contract
  1123  	// instead of the old contract.
  1124  	c.mu.Lock()
  1125  	// Link Contracts
  1126  	c.renewedFrom[newContract.ID] = id
  1127  	c.renewedTo[id] = newContract.ID
  1128  	// Store the contract in the record of historic contracts.
  1129  	c.oldContracts[id] = oldContract.Metadata()
  1130  	// Save the contractor.
  1131  	err = c.save()
  1132  	if err != nil {
  1133  		c.staticLog.Println("Failed to save the contractor after creating a new contract.")
  1134  	}
  1135  	c.mu.Unlock()
  1136  	// Delete the old contract.
  1137  	c.staticContracts.Delete(oldContract)
  1138  
  1139  	// Signal to the watchdog that it should immediately post the last
  1140  	// revision for this contract.
  1141  	go c.staticWatchdog.threadedSendMostRecentRevision(oldContract.Metadata())
  1142  	return amount, nil
  1143  }
  1144  
  1145  // managedFindRecoverableContracts will spawn a thread to rescan parts of the
  1146  // blockchain for recoverable contracts if the wallet has been locked during the
  1147  // last scan.
  1148  func (c *Contractor) managedFindRecoverableContracts() {
  1149  	if c.staticDeps.Disrupt("disableAutomaticContractRecoveryScan") {
  1150  		return
  1151  	}
  1152  	c.mu.RLock()
  1153  	cc := c.recentRecoveryChange
  1154  	c.mu.RUnlock()
  1155  	if err := c.callInitRecoveryScan(cc); err != nil {
  1156  		c.staticLog.Debug(err)
  1157  		return
  1158  	}
  1159  }
  1160  
  1161  // managedAcquireAndUpdateContractUtility is a helper function that acquires a
  1162  // contract, updates its ContractUtility and returns the contract again.
  1163  // 'notChurn' needs to be 'true' for contracts which changed to !gfr but
  1164  // shouldn't be considered churn in the churnLimiter.  e.g. if a user canceled a
  1165  // contract manually.
  1166  func (c *Contractor) managedAcquireAndUpdateContractUtility(id types.FileContractID, utility skymodules.ContractUtility, notChurn bool) error {
  1167  	safeContract, ok := c.staticContracts.Acquire(id)
  1168  	if !ok {
  1169  		return errors.New("failed to acquire contract for update")
  1170  	}
  1171  	defer c.staticContracts.Return(safeContract)
  1172  
  1173  	return c.managedUpdateContractUtilityWithNotChurn(safeContract, utility, notChurn)
  1174  }
  1175  
  1176  // managedUpdateContractUtility is a helper function that updates the contract
  1177  // with the given utility. 'notChurn' needs to be 'true' for contracts which
  1178  // changed to !gfr but shouldn't be considered churn in the churnLimiter.  e.g.
  1179  // if a user canceled a contract manually.
  1180  func (c *Contractor) managedUpdateContractUtilityWithNotChurn(safeContract *proto.SafeContract, utility skymodules.ContractUtility, notChurn bool) error {
  1181  	// Sanity check to verify that we aren't attempting to set a good utility on
  1182  	// a contract that has been renewed.
  1183  	c.mu.Lock()
  1184  	_, exists := c.renewedTo[safeContract.Metadata().ID]
  1185  	c.mu.Unlock()
  1186  	if exists && (utility.GoodForRenew || utility.GoodForUpload) {
  1187  		c.staticLog.Critical("attempting to update contract utility on a contract that has been renewed")
  1188  	}
  1189  
  1190  	return c.callUpdateUtility(safeContract, utility, notChurn)
  1191  }
  1192  
  1193  // managedUpdateContractUtility is a helper function that updates the contract
  1194  // with the given utility.
  1195  func (c *Contractor) managedUpdateContractUtility(safeContract *proto.SafeContract, utility skymodules.ContractUtility) error {
  1196  	// Default behaviour for updating the utility is to consider it churn.
  1197  	return c.managedUpdateContractUtilityWithNotChurn(safeContract, utility, false)
  1198  }
  1199  
  1200  // callUpdateUtility updates the utility of a contract and notifies the
  1201  // churnLimiter of churn if necessary. This method should *always* be used as
  1202  // opposed to calling UpdateUtility directly on a safe contract from the
  1203  // contractor. Pass in renewed as true if the contract has been renewed and is
  1204  // not churn.
  1205  func (c *Contractor) callUpdateUtility(safeContract *proto.SafeContract, newUtility skymodules.ContractUtility, notChurn bool) error {
  1206  	contract := safeContract.Metadata()
  1207  
  1208  	// If the contract is going from GFR to !GFR, notify the churn limiter.
  1209  	if !notChurn && contract.Utility.GoodForRenew && !newUtility.GoodForRenew {
  1210  		c.staticChurnLimiter.callNotifyChurnedContract(contract)
  1211  	}
  1212  	return safeContract.UpdateUtility(newUtility)
  1213  }
  1214  
  1215  // threadedContractMaintenance checks the set of contracts that the contractor
  1216  // has against the allownace, renewing any contracts that need to be renewed,
  1217  // dropping contracts which are no longer worthwhile, and adding contracts if
  1218  // there are not enough.
  1219  //
  1220  // Between each network call, the thread checks whether a maintenance interrupt
  1221  // signal is being sent. If so, maintenance returns, yielding to whatever thread
  1222  // issued the interrupt.
  1223  func (c *Contractor) threadedContractMaintenance() {
  1224  	err := c.staticTG.Add()
  1225  	if err != nil {
  1226  		return
  1227  	}
  1228  	defer c.staticTG.Done()
  1229  
  1230  	// No contract maintenance unless contractor is synced.
  1231  	if !c.managedSynced() {
  1232  		c.staticLog.Debugln("Skipping contract maintenance since consensus isn't synced yet")
  1233  		return
  1234  	}
  1235  	c.staticLog.Debugln("starting contract maintenance")
  1236  
  1237  	// Only one instance of this thread should be running at a time. Under
  1238  	// normal conditions, fine to return early if another thread is already
  1239  	// doing maintenance. The next block will trigger another round. Under
  1240  	// testing, control is insufficient if the maintenance loop isn't guaranteed
  1241  	// to run.
  1242  	if build.Release == "testing" {
  1243  		c.maintenanceLock.Lock()
  1244  	} else if !c.maintenanceLock.TryLock() {
  1245  		c.staticLog.Debugln("maintenance lock could not be obtained")
  1246  		return
  1247  	}
  1248  	defer c.maintenanceLock.Unlock()
  1249  
  1250  	// Register the WalletLockedDuringMaintenance alert if necessary.
  1251  	var registerWalletLockedDuringMaintenance bool
  1252  	defer func() {
  1253  		if registerWalletLockedDuringMaintenance {
  1254  			c.staticAlerter.RegisterAlert(modules.AlertIDWalletLockedDuringMaintenance, AlertMSGWalletLockedDuringMaintenance, modules.ErrLockedWallet.Error(), modules.SeverityWarning)
  1255  		} else {
  1256  			c.staticAlerter.UnregisterAlert(modules.AlertIDWalletLockedDuringMaintenance)
  1257  		}
  1258  	}()
  1259  
  1260  	// Fetch the allowance in the beginning of the contract maintenance and
  1261  	// make sure we are using this one during all of it to avoid race
  1262  	// conditions.
  1263  	c.mu.RLock()
  1264  	allowance := c.allowance
  1265  	blockHeight := c.blockHeight
  1266  	currentPeriod := c.currentPeriod
  1267  	endHeight := c.contractEndHeight()
  1268  	c.mu.RUnlock()
  1269  
  1270  	// Perform general cleanup of the contracts. This includes recovering lost
  1271  	// contracts, archiving contracts, and other cleanup work. This should all
  1272  	// happen before the rest of the maintenance.
  1273  	c.managedFindRecoverableContracts()
  1274  	c.callRecoverContracts()
  1275  	c.managedArchiveContracts()
  1276  	c.managedCheckForDuplicates()
  1277  	c.managedUpdatePubKeyToContractIDMap()
  1278  	c.managedPrunedRedundantAddressRange()
  1279  	err = c.managedMarkContractsUtility(allowance.Hosts, endHeight)
  1280  	if err != nil {
  1281  		c.staticLog.Println("Unable to mark contract utilities:", err)
  1282  		return
  1283  	}
  1284  	err = c.staticHDB.UpdateContracts(c.staticContracts.ViewAll())
  1285  	if err != nil {
  1286  		c.staticLog.Println("Unable to update hostdb contracts:", err)
  1287  		return
  1288  	}
  1289  
  1290  	// If there are no hosts requested by the allowance, there is no remaining
  1291  	// work.
  1292  	wantedHosts := allowance.Hosts
  1293  	if wantedHosts <= 0 {
  1294  		c.staticLog.Debugln("Exiting contract maintenance because the number of desired hosts is <= zero.")
  1295  		return
  1296  	}
  1297  
  1298  	// Get the number of gfrContracts we have as well as the data they hold.
  1299  	var gfrContracts uint64
  1300  	var gfrData uint64
  1301  	for _, c := range c.staticContracts.ViewAll() {
  1302  		if c.Utility.GoodForRenew {
  1303  			gfrContracts++
  1304  			gfrData += c.Size()
  1305  		}
  1306  	}
  1307  
  1308  	// Calculate the anticipated transaction fee.
  1309  	_, maxFee := c.staticTPool.FeeEstimation()
  1310  	txnFee := maxFee.Mul64(skymodules.EstimatedFileContractTransactionSetSize)
  1311  
  1312  	// Create the renewSet and refreshSet. Each is a list of contracts that need
  1313  	// to be renewed, paired with the amount of money to use in each renewal.
  1314  	//
  1315  	// The renewSet is specifically contracts which are being renewed because
  1316  	// they are about to expire. And the refreshSet is contracts that are being
  1317  	// renewed because they are out of money.
  1318  	//
  1319  	// The contractor will prioritize contracts in the renewSet over contracts
  1320  	// in the refreshSet. If the wallet does not have enough money, or if the
  1321  	// allowance does not have enough money, the contractor will prefer to save
  1322  	// data in the long term rather than renew a contract.
  1323  	var renewSet []decoratedFileContractRenewal
  1324  	var refreshSet []decoratedFileContractRenewal
  1325  
  1326  	// Sanity check that by the end of the contract maintenance, we don't
  1327  	// have more than allowance.Hosts gfu contracts.
  1328  	if build.Release == "testing" {
  1329  		defer func() {
  1330  			var gfuContracts uint64
  1331  			for _, c := range c.staticContracts.ViewAll() {
  1332  				if c.Utility.GoodForUpload {
  1333  					gfuContracts++
  1334  				}
  1335  			}
  1336  			if gfuContracts > allowance.Hosts {
  1337  				build.Critical(fmt.Sprintf("threadedContractMaintenance: got %v > %v gfu hosts after maintenance (renew/refresh %v/%v)", gfuContracts, allowance.Hosts, len(renewSet), len(refreshSet)))
  1338  			}
  1339  		}()
  1340  	}
  1341  
  1342  	// Iterate through the contracts again, figuring out which contracts to
  1343  	// renew and how much extra funds to renew them with.
  1344  	for _, contract := range c.staticContracts.ViewAll() {
  1345  		c.staticLog.Debugln("Examining a contract:", contract.HostPublicKey, contract.ID)
  1346  		// Skip any host that does not match our whitelist/blacklist filter
  1347  		// settings.
  1348  		host, _, err := c.staticHDB.Host(contract.HostPublicKey)
  1349  		if err != nil {
  1350  			c.staticLog.Println("WARN: error getting host", err)
  1351  			continue
  1352  		}
  1353  		if host.Filtered {
  1354  			c.staticLog.Debugln("Contract skipped because it is filtered")
  1355  			continue
  1356  		}
  1357  		// Skip hosts that can't use the current renter-host protocol.
  1358  		if build.VersionCmp(host.Version, modules.MinimumSupportedRenterHostProtocolVersion) < 0 {
  1359  			c.staticLog.Debugln("Contract skipped because host is using an outdated version", host.Version)
  1360  			continue
  1361  		}
  1362  
  1363  		// Skip any contracts which do not exist or are otherwise unworthy for
  1364  		// renewal.
  1365  		utility, ok := c.managedContractUtility(contract.ID)
  1366  		if !ok || (!utility.GoodForRenew && !utility.GoodForRefresh) {
  1367  			if blockHeight-contract.StartHeight < types.BlocksPerWeek {
  1368  				c.staticLog.Debugln("Contract did not last 1 week and is not being renewed", contract.ID)
  1369  			}
  1370  			c.staticLog.Debugln("Contract skipped because it is not good for renew nor refresh (utility.GoodForRenew, utility.GoodForRefresh exists)", utility.GoodForRenew, utility.GoodForRefresh, ok)
  1371  			continue
  1372  		}
  1373  		goodForRenew := utility.GoodForRenew
  1374  		goodForRefresh := utility.GoodForRefresh
  1375  
  1376  		// If the contract needs to be renewed because it is about to expire,
  1377  		// calculate a spending for the contract that is proportional to how
  1378  		// much money was spend on the contract throughout this billing cycle
  1379  		// (which is now ending).
  1380  		if goodForRenew && blockHeight+allowance.RenewWindow >= contract.EndHeight && !c.staticDeps.Disrupt("disableRenew") {
  1381  			renewAmount, err := c.managedEstimateRenewFundingRequirements(contract, gfrContracts, blockHeight, allowance)
  1382  			if err != nil {
  1383  				c.staticLog.Debugln("Contract skipped because there was an error estimating renew funding requirements", renewAmount, err)
  1384  				continue
  1385  			}
  1386  
  1387  			renewSet = append(renewSet, decoratedFileContractRenewal{
  1388  				fileContractRenewal{
  1389  					id:         contract.ID,
  1390  					amount:     renewAmount,
  1391  					data:       contract.Size(),
  1392  					hostPubKey: contract.HostPublicKey,
  1393  				},
  1394  				isLateRenewal(allowance, contract, blockHeight),
  1395  			})
  1396  			c.staticLog.Debugln("Contract has been added to the renew set for being past the renew height")
  1397  			continue
  1398  		}
  1399  
  1400  		// Check if the contract is empty. We define a contract as being empty
  1401  		// if less than 'minContractFundRenewalThreshold' funds are remaining
  1402  		// (3% at time of writing), or if there is less than 3 sectors worth of
  1403  		// storage+upload+download remaining.
  1404  		blockBytes := types.NewCurrency64(modules.SectorSize * uint64(allowance.Period))
  1405  		sectorStoragePrice := host.StoragePrice.Mul(blockBytes)
  1406  		sectorUploadBandwidthPrice := host.UploadBandwidthPrice.Mul64(modules.SectorSize)
  1407  		sectorDownloadBandwidthPrice := host.DownloadBandwidthPrice.Mul64(modules.SectorSize)
  1408  		sectorBandwidthPrice := sectorUploadBandwidthPrice.Add(sectorDownloadBandwidthPrice)
  1409  		sectorPrice := sectorStoragePrice.Add(sectorBandwidthPrice)
  1410  		percentRemaining, _ := big.NewRat(0, 1).SetFrac(contract.RenterFunds.Big(), contract.TotalCost.Big()).Float64()
  1411  		lowFundsRefresh := c.staticDeps.Disrupt("LowFundsRefresh")
  1412  		if goodForRefresh && (lowFundsRefresh || ((contract.RenterFunds.Cmp(sectorPrice.Mul64(3)) < 0 || percentRemaining < MinContractFundRenewalThreshold) && !c.staticDeps.Disrupt("disableRenew"))) {
  1413  			// Renew the contract with double the amount of funds that the
  1414  			// contract had previously. The reason that we double the funding
  1415  			// instead of doing anything more clever is that we don't know what
  1416  			// the usage pattern has been. The spending could have all occurred
  1417  			// in one burst recently, and the user might need a contract that
  1418  			// has substantially more money in it.
  1419  			//
  1420  			// We double so that heavily used contracts can grow in funding
  1421  			// quickly without consuming too many transaction fees, however this
  1422  			// does mean that a larger percentage of funds get locked away from
  1423  			// the user in the event that the user stops uploading immediately
  1424  			// after the renew.
  1425  			refreshAmount := contract.TotalCost.Mul64(2)
  1426  			minInitialContractFunds := allowance.Funds.Div64(allowance.Hosts).Div64(MinInitialContractFundingDivFactor)
  1427  			minimum := initialContractFunding(allowance, host, txnFee, minInitialContractFunds, types.ZeroCurrency)
  1428  			if refreshAmount.Cmp(minimum) < 0 {
  1429  				refreshAmount = minimum
  1430  				c.staticLog.Printf("Contract refresh amount %v below minimum amount %v", refreshAmount, minimum)
  1431  			}
  1432  			refreshSet = append(refreshSet, decoratedFileContractRenewal{
  1433  				fileContractRenewal{
  1434  					id:         contract.ID,
  1435  					amount:     refreshAmount,
  1436  					data:       contract.Size(),
  1437  					hostPubKey: contract.HostPublicKey,
  1438  				},
  1439  				isLateRenewal(allowance, contract, blockHeight),
  1440  			})
  1441  			c.staticLog.Debugln("Contract identified as needing to be added to refresh set", contract.RenterFunds, sectorPrice.Mul64(3), percentRemaining, MinContractFundRenewalThreshold)
  1442  		} else {
  1443  			c.staticLog.Debugln("Contract did not get added to the refresh set", contract.RenterFunds, sectorPrice.Mul64(3), percentRemaining, MinContractFundRenewalThreshold)
  1444  		}
  1445  	}
  1446  	if len(renewSet) != 0 || len(refreshSet) != 0 {
  1447  		c.staticLog.Printf("renewing %v contracts and refreshing %v contracts", len(renewSet), len(refreshSet))
  1448  	}
  1449  
  1450  	// Update the failed renew map so that it only contains contracts which we
  1451  	// are currently trying to renew or refresh. The failed renew map is a map
  1452  	// that we use to track how many times consecutively we failed to renew a
  1453  	// contract with a host, so that we know if we need to abandon that host.
  1454  	c.mu.Lock()
  1455  	newFirstFailedRenew := make(map[types.FileContractID]types.BlockHeight)
  1456  	for _, r := range renewSet {
  1457  		if _, exists := c.numFailedRenews[r.id]; exists {
  1458  			newFirstFailedRenew[r.id] = c.numFailedRenews[r.id]
  1459  		}
  1460  	}
  1461  	for _, r := range refreshSet {
  1462  		if _, exists := c.numFailedRenews[r.id]; exists {
  1463  			newFirstFailedRenew[r.id] = c.numFailedRenews[r.id]
  1464  		}
  1465  	}
  1466  	c.numFailedRenews = newFirstFailedRenew
  1467  	c.mu.Unlock()
  1468  
  1469  	// Depend on the PeriodSpending function to get a breakdown of spending in
  1470  	// the contractor. Then use that to determine how many funds remain
  1471  	// available in the allowance for renewals.
  1472  	spending, err := c.PeriodSpending()
  1473  	if err != nil {
  1474  		// This should only error if the contractor is shutting down
  1475  		c.staticLog.Println("WARN: error getting period spending:", err)
  1476  		return
  1477  	}
  1478  	var fundsRemaining types.Currency
  1479  	// Check for an underflow. This can happen if the user reduced their
  1480  	// allowance at some point to less than what we've already spent.
  1481  	if spending.TotalAllocated.Cmp(allowance.Funds) < 0 {
  1482  		fundsRemaining = allowance.Funds.Sub(spending.TotalAllocated)
  1483  	}
  1484  	c.staticLog.Debugln("Remaining funds in allowance:", fundsRemaining.HumanString())
  1485  
  1486  	// Keep track of the amount of data that failed to renew.
  1487  	//
  1488  	// NOTE: we only count the failed renew data if the renew window leeway has
  1489  	// passed already
  1490  	var numRenewFails int
  1491  	var failedRenewData uint64
  1492  
  1493  	// Register or unregister and alerts related to contract renewal or
  1494  	// formation.
  1495  	var registerLowFundsAlert bool
  1496  	var renewErr error
  1497  	defer func() {
  1498  		if registerLowFundsAlert {
  1499  			c.staticAlerter.RegisterAlert(modules.AlertIDRenterAllowanceLowFunds, AlertMSGAllowanceLowFunds, AlertCauseInsufficientAllowanceFunds, modules.SeverityWarning)
  1500  		} else {
  1501  			c.staticAlerter.UnregisterAlert(modules.AlertIDRenterAllowanceLowFunds)
  1502  		}
  1503  
  1504  		alertSeverity := modules.SeverityError
  1505  		// Increase the alert severity for renewal fails to critical if the
  1506  		// number of data which might be lost is more than 20% of the total data
  1507  		// in gfr contracts.
  1508  		if float64(failedRenewData) > math.Ceil(float64(gfrData)*MaxCriticalRenewFailThreshold) {
  1509  			alertSeverity = modules.SeverityCritical
  1510  		}
  1511  		if renewErr != nil {
  1512  			c.staticLog.Debugln("SEVERE", numRenewFails, float64(allowance.Hosts)*MaxCriticalRenewFailThreshold)
  1513  			c.staticLog.Debugln("alert err: ", renewErr)
  1514  			alertMSG := fmt.Sprintf("%v out of %v renewals failed (%v bytes out of %v) - number of total gfr contracts is %v - see contractor.log for details", numRenewFails, len(renewSet)+len(refreshSet), failedRenewData, gfrData, gfrContracts)
  1515  			c.staticAlerter.RegisterAlert(modules.AlertIDRenterContractRenewalError, AlertMSGFailedContractRenewal, alertMSG, modules.AlertSeverity(alertSeverity))
  1516  		} else {
  1517  			c.staticAlerter.UnregisterAlert(modules.AlertIDRenterContractRenewalError)
  1518  		}
  1519  	}()
  1520  	// Go through the contracts we've assembled for renewal. Any contracts that
  1521  	// need to be renewed because they are expiring (renewSet) get priority over
  1522  	// contracts that need to be renewed because they have exhausted their funds
  1523  	// (refreshSet). If there is not enough money available, the more expensive
  1524  	// contracts will be skipped.
  1525  	for _, renewal := range renewSet {
  1526  		// Return here if an interrupt or kill signal has been sent.
  1527  		select {
  1528  		case <-c.staticTG.StopChan():
  1529  			c.staticLog.Println("returning because the renter was stopped")
  1530  			return
  1531  		case <-c.staticInterruptMaintenance:
  1532  			c.staticLog.Println("returning because maintenance was interrupted")
  1533  			return
  1534  		default:
  1535  		}
  1536  
  1537  		unlocked, err := c.staticWallet.Unlocked()
  1538  		if !unlocked || err != nil {
  1539  			registerWalletLockedDuringMaintenance = true
  1540  			c.staticLog.Println("Contractor is attempting to renew contracts that are about to expire, however the wallet is locked")
  1541  			return
  1542  		}
  1543  
  1544  		c.staticLog.Println("Attempting to perform a renewal:", renewal.id)
  1545  		// Skip this renewal if we don't have enough funds remaining.
  1546  		if renewal.amount.Cmp(fundsRemaining) > 0 || c.staticDeps.Disrupt("LowFundsRenewal") {
  1547  			c.staticLog.Println("Skipping renewal because there are not enough funds remaining in the allowance", renewal.id, renewal.amount, fundsRemaining)
  1548  			registerLowFundsAlert = true
  1549  			continue
  1550  		}
  1551  
  1552  		// Renew one contract. The error is ignored because the renew function
  1553  		// already will have logged the error, and in the event of an error,
  1554  		// 'fundsSpent' will return '0'.
  1555  		fundsSpent, err := c.managedRenewContract(renewal.fileContractRenewal, currentPeriod, allowance, blockHeight, endHeight)
  1556  		if errors.Contains(err, errContractNotGFR) {
  1557  			// Do not add a renewal error.
  1558  			c.staticLog.Debugln("Contract skipped because it is not good for renew", renewal.id)
  1559  		} else if err != nil {
  1560  			c.staticLog.Println("Error renewing a contract", renewal.id, err)
  1561  			renewErr = errors.Compose(renewErr, err)
  1562  			numRenewFails++
  1563  			if renewal.lateRenewal {
  1564  				failedRenewData += renewal.data
  1565  			}
  1566  		} else {
  1567  			c.staticLog.Println("Renewal completed without error")
  1568  		}
  1569  		fundsRemaining = fundsRemaining.Sub(fundsSpent)
  1570  	}
  1571  	for _, renewal := range refreshSet {
  1572  		// Return here if an interrupt or kill signal has been sent.
  1573  		select {
  1574  		case <-c.staticTG.StopChan():
  1575  			c.staticLog.Println("returning because the renter was stopped")
  1576  			return
  1577  		case <-c.staticInterruptMaintenance:
  1578  			c.staticLog.Println("returning because maintenance was interrupted")
  1579  			return
  1580  		default:
  1581  		}
  1582  
  1583  		unlocked, err := c.staticWallet.Unlocked()
  1584  		if !unlocked || err != nil {
  1585  			registerWalletLockedDuringMaintenance = true
  1586  			c.staticLog.Println("contractor is attempting to refresh contracts that have run out of funds, however the wallet is locked")
  1587  			return
  1588  		}
  1589  
  1590  		// Skip this renewal if we don't have enough funds remaining.
  1591  		c.staticLog.Debugln("Attempting to perform a contract refresh:", renewal.id)
  1592  		if renewal.amount.Cmp(fundsRemaining) > 0 || c.staticDeps.Disrupt("LowFundsRefresh") {
  1593  			c.staticLog.Println("skipping refresh because there are not enough funds remaining in the allowance", renewal.amount.HumanString(), fundsRemaining.HumanString())
  1594  			registerLowFundsAlert = true
  1595  			continue
  1596  		}
  1597  
  1598  		// Renew one contract. The error is ignored because the renew function
  1599  		// already will have logged the error, and in the event of an error,
  1600  		// 'fundsSpent' will return '0'.
  1601  		fundsSpent, err := c.managedRenewContract(renewal.fileContractRenewal, currentPeriod, allowance, blockHeight, endHeight)
  1602  		if err != nil {
  1603  			c.staticLog.Println("Error refreshing a contract", renewal.id, err)
  1604  			renewErr = errors.Compose(renewErr, err)
  1605  			numRenewFails++
  1606  			if renewal.lateRenewal {
  1607  				failedRenewData += renewal.data
  1608  			}
  1609  		} else {
  1610  			c.staticLog.Println("Refresh completed without error")
  1611  		}
  1612  		fundsRemaining = fundsRemaining.Sub(fundsSpent)
  1613  	}
  1614  
  1615  	// Get Hosts for contract formation.
  1616  	var hosts []skymodules.HostDBEntry
  1617  	var neededContracts int
  1618  	if allowance.PortalMode() {
  1619  		neededContracts, hosts = c.managedHostsForPortalFormation(allowance)
  1620  	} else {
  1621  		neededContracts, hosts = c.managedHostsForRegularFormation(allowance)
  1622  	}
  1623  
  1624  	// Form contracts.
  1625  	lf, wl := c.managedFormContracts(fundsRemaining, hosts, neededContracts, allowance, endHeight)
  1626  
  1627  	// Register alerts if necessary.
  1628  	registerLowFundsAlert = registerLowFundsAlert || lf
  1629  	registerWalletLockedDuringMaintenance = registerWalletLockedDuringMaintenance || wl
  1630  }
  1631  
  1632  // managedHostsForPortalFormation returns the hosts to form contracts with for a
  1633  // portal.
  1634  func (c *Contractor) managedHostsForPortalFormation(allowance skymodules.Allowance) (int, []skymodules.HostDBEntry) {
  1635  	hosts, err := c.staticHDB.ActiveHosts()
  1636  	if err != nil {
  1637  		c.staticLog.Printf("Error fetching list of active hosts when attempting to form view contracts: %v", err)
  1638  		return 0, nil
  1639  	}
  1640  	return hostsForPortalFormation(allowance, c.staticContracts.ViewAll(), c.RecoverableContracts(), hosts, c.staticLog, c.staticHDB.ScoreBreakdown)
  1641  }
  1642  
  1643  // managedHostsForRegularFormation returns the number of hosts needed for
  1644  // non-portal contract formation plus a set of hosts to use.
  1645  func (c *Contractor) managedHostsForRegularFormation(allowance skymodules.Allowance) (int, []skymodules.HostDBEntry) {
  1646  	return hostsForRegularFormation(allowance, c.staticContracts.ViewAll(), c.RecoverableContracts(), c.staticHDB.RandomHosts, c.staticLog)
  1647  }
  1648  
  1649  // managedFormContracts tries to form up to neededContracts with the hosts given
  1650  // by hosts and the provided budget, allowance and endHeight.
  1651  func (c *Contractor) managedFormContracts(budget types.Currency, hosts []skymodules.HostDBEntry, neededContracts int, allowance skymodules.Allowance, endHeight types.BlockHeight) (lowFunds, walletLocked bool) {
  1652  	// Calculate the anticipated transaction fee.
  1653  	_, maxFee := c.staticTPool.FeeEstimation()
  1654  	txnFee := maxFee.Mul64(skymodules.EstimatedFileContractTransactionSetSize)
  1655  
  1656  	// Determine the max and min initial contract funding based on the allowance
  1657  	// settings
  1658  	maxInitialContractFunds := allowance.Funds.Div64(allowance.Hosts).Mul64(MaxInitialContractFundingMulFactor).Div64(MaxInitialContractFundingDivFactor)
  1659  	minInitialContractFunds := allowance.Funds.Div64(allowance.Hosts).Div64(MinInitialContractFundingDivFactor)
  1660  
  1661  	// Get a list of all current contracts.
  1662  	currentContracts := make(map[string]skymodules.RenterContract)
  1663  	gfuContracts := 0
  1664  	for _, contract := range c.staticContracts.ViewAll() {
  1665  		currentContracts[contract.HostPublicKey.String()] = contract
  1666  		if contract.Utility.GoodForUpload {
  1667  			gfuContracts++
  1668  		}
  1669  	}
  1670  
  1671  	// Compute how many gfu contracts we still need.
  1672  	remainingGFU := 0
  1673  	if gfuContracts < int(allowance.Hosts) {
  1674  		remainingGFU = int(allowance.Hosts) - gfuContracts
  1675  	}
  1676  
  1677  	// Form contracts with the hosts one at a time, until we have enough
  1678  	// contracts.
  1679  	for _, host := range hosts {
  1680  		// Return here if an interrupt or kill signal has been sent.
  1681  		select {
  1682  		case <-c.staticTG.StopChan():
  1683  			c.staticLog.Println("returning because the renter was stopped")
  1684  			return
  1685  		case <-c.staticInterruptMaintenance:
  1686  			c.staticLog.Println("returning because maintenance was interrupted")
  1687  			return
  1688  		default:
  1689  		}
  1690  
  1691  		// If no more contracts are needed, break.
  1692  		if neededContracts <= 0 {
  1693  			break
  1694  		}
  1695  
  1696  		// Calculate the contract funding with host
  1697  		contractFunds := initialContractFunding(allowance, host, txnFee, minInitialContractFunds, maxInitialContractFunds)
  1698  
  1699  		// Confirm the wallet is still unlocked
  1700  		unlocked, err := c.staticWallet.Unlocked()
  1701  		if !unlocked || err != nil {
  1702  			walletLocked = true
  1703  			c.staticLog.Println("contractor is attempting to establish new contracts with hosts, however the wallet is locked")
  1704  			return
  1705  		}
  1706  
  1707  		// Determine if we have enough money to form a new contract.
  1708  		if budget.Cmp(contractFunds) < 0 || c.staticDeps.Disrupt("LowFundsFormation") {
  1709  			lowFunds = true
  1710  			c.staticLog.Println("WARN: need to form new contracts, but unable to because of a low allowance")
  1711  			break
  1712  		}
  1713  
  1714  		// If we are using a custom resolver we need to replace the domain name
  1715  		// with 127.0.0.1 to be able to form contracts.
  1716  		if c.staticDeps.Disrupt("customResolver") {
  1717  			port := host.NetAddress.Port()
  1718  			host.NetAddress = modules.NetAddress(fmt.Sprintf("127.0.0.1:%s", port))
  1719  		}
  1720  
  1721  		// Attempt forming a contract with this host.
  1722  		start := time.Now()
  1723  		fundsSpent, newContract, err := c.managedNewContract(host, allowance, contractFunds, endHeight)
  1724  		if err != nil {
  1725  			c.staticLog.Printf("Attempted to form a contract with %v, time spent %v, but negotiation failed: %v\n", host.NetAddress, time.Since(start).Round(time.Millisecond), err)
  1726  			continue
  1727  		}
  1728  		budget = budget.Sub(fundsSpent)
  1729  		neededContracts--
  1730  
  1731  		sb, err := c.staticHDB.ScoreBreakdown(host)
  1732  		if err == nil {
  1733  			c.staticLog.Println("A new contract has been formed with a host:", newContract.ID)
  1734  			c.staticLog.Println("Score:    ", sb.Score)
  1735  			c.staticLog.Println("Age Adjustment:        ", sb.AgeAdjustment)
  1736  			c.staticLog.Println("Base Price Adjustment: ", sb.BasePriceAdjustment)
  1737  			c.staticLog.Println("Burn Adjustment:       ", sb.BurnAdjustment)
  1738  			c.staticLog.Println("Collateral Adjustment: ", sb.CollateralAdjustment)
  1739  			c.staticLog.Println("Duration Adjustment:   ", sb.DurationAdjustment)
  1740  			c.staticLog.Println("Interaction Adjustment:", sb.InteractionAdjustment)
  1741  			c.staticLog.Println("Price Adjustment:      ", sb.PriceAdjustment)
  1742  			c.staticLog.Println("Storage Adjustment:    ", sb.StorageRemainingAdjustment)
  1743  			c.staticLog.Println("Uptime Adjustment:     ", sb.UptimeAdjustment)
  1744  			c.staticLog.Println("Version Adjustment:    ", sb.VersionAdjustment)
  1745  		}
  1746  
  1747  		// Add this contract to the contractor and save.
  1748  		err = c.managedAcquireAndUpdateContractUtility(newContract.ID, skymodules.ContractUtility{
  1749  			GoodForUpload:  remainingGFU > 0,
  1750  			GoodForRefresh: true,
  1751  			GoodForRenew:   true,
  1752  		}, false)
  1753  		if err != nil {
  1754  			c.staticLog.Println("Failed to update the contract utilities", err)
  1755  			return
  1756  		}
  1757  		remainingGFU--
  1758  
  1759  		c.mu.Lock()
  1760  		err = c.save()
  1761  		c.mu.Unlock()
  1762  		if err != nil {
  1763  			c.staticLog.Println("Unable to save the contractor:", err)
  1764  		}
  1765  	}
  1766  	return
  1767  }
  1768  
  1769  // isLateRenewal returns true if a the contract is already a certain amount of
  1770  // time into the renewal window and it hasn't been renewed yet due to a series
  1771  // of failed renewals
  1772  func isLateRenewal(allowance skymodules.Allowance, contract skymodules.RenterContract, blockHeight types.BlockHeight) bool {
  1773  	return blockHeight+(allowance.RenewWindow-allowance.RenewWindow/renewWindowLeewayDivisor) > contract.EndHeight
  1774  }