github.com/dmmcquay/sia@v1.3.1-0.20180712220038-9f8d535311b9/modules/renter/contractor/contracts.go (about)

     1  package contractor
     2  
     3  // contracts.go handles forming and renewing contracts for the contractor. This
     4  // includes deciding when new contracts need to be formed, when contracts need
     5  // to be renewed, and if contracts need to be blacklisted.
     6  
     7  import (
     8  	"errors"
     9  	"fmt"
    10  	"math/big"
    11  
    12  	"github.com/NebulousLabs/Sia/build"
    13  	"github.com/NebulousLabs/Sia/modules"
    14  	"github.com/NebulousLabs/Sia/modules/renter/proto"
    15  	"github.com/NebulousLabs/Sia/types"
    16  )
    17  
    18  var (
    19  	// ErrInsufficientAllowance indicates that the renter's allowance is less
    20  	// than the amount necessary to store at least one sector
    21  	ErrInsufficientAllowance = errors.New("allowance is not large enough to cover fees of contract creation")
    22  	errTooExpensive          = errors.New("host price was too high")
    23  )
    24  
    25  // contractEndHeight returns the height at which the Contractor's contracts
    26  // end. If there are no contracts, it returns zero.
    27  func (c *Contractor) contractEndHeight() types.BlockHeight {
    28  	return c.currentPeriod + c.allowance.Period
    29  }
    30  
    31  // managedContractUtility returns the ContractUtility for a contract with a given id.
    32  func (c *Contractor) managedContractUtility(id types.FileContractID) (modules.ContractUtility, bool) {
    33  	rc, exists := c.staticContracts.View(id)
    34  	if !exists {
    35  		return modules.ContractUtility{}, false
    36  	}
    37  	return rc.Utility, true
    38  }
    39  
    40  // managedInterruptContractMaintenance will issue an interrupt signal to any
    41  // running maintenance, stopping that maintenance. If there are multiple threads
    42  // running maintenance, they will all be stopped.
    43  func (c *Contractor) managedInterruptContractMaintenance() {
    44  	// Spin up a thread to grab the maintenance lock. Signal that the lock was
    45  	// acquired after the lock is acquired.
    46  	gotLock := make(chan struct{})
    47  	go func() {
    48  		c.maintenanceLock.Lock()
    49  		close(gotLock)
    50  		c.maintenanceLock.Unlock()
    51  	}()
    52  
    53  	// There may be multiple threads contending for the maintenance lock. Issue
    54  	// interrupts repeatedly until we get a signal that the maintenance lock has
    55  	// been acquired.
    56  	for {
    57  		select {
    58  		case <-gotLock:
    59  			return
    60  		case c.interruptMaintenance <- struct{}{}:
    61  		}
    62  	}
    63  }
    64  
    65  // managedMarkContractsUtility checks every active contract in the contractor and
    66  // figures out whether the contract is useful for uploading, and whether the
    67  // contract should be renewed.
    68  func (c *Contractor) managedMarkContractsUtility() error {
    69  	// Pull a new set of hosts from the hostdb that could be used as a new set
    70  	// to match the allowance. The lowest scoring host of these new hosts will
    71  	// be used as a baseline for determining whether our existing contracts are
    72  	// worthwhile.
    73  	c.mu.RLock()
    74  	hostCount := int(c.allowance.Hosts)
    75  	c.mu.RUnlock()
    76  	hosts, err := c.hdb.RandomHosts(hostCount+randomHostsBufferForScore, nil)
    77  	if err != nil {
    78  		return err
    79  	}
    80  
    81  	// Find the minimum score that a host is allowed to have to be considered
    82  	// good for upload.
    83  	var minScore types.Currency
    84  	if len(hosts) > 0 {
    85  		lowestScore := c.hdb.ScoreBreakdown(hosts[0]).Score
    86  		for i := 1; i < len(hosts); i++ {
    87  			score := c.hdb.ScoreBreakdown(hosts[i]).Score
    88  			if score.Cmp(lowestScore) < 0 {
    89  				lowestScore = score
    90  			}
    91  		}
    92  		// Set the minimum acceptable score to a factor of the lowest score.
    93  		minScore = lowestScore.Div(scoreLeeway)
    94  	}
    95  
    96  	// Update utility fields for each contract.
    97  	for _, contract := range c.staticContracts.ViewAll() {
    98  		utility := func() (u modules.ContractUtility) {
    99  			// Start the contract in good standing if the utility wasn't
   100  			// locked.
   101  			if !u.Locked {
   102  				u.GoodForUpload = true
   103  				u.GoodForRenew = true
   104  			}
   105  
   106  			host, exists := c.hdb.Host(contract.HostPublicKey)
   107  			// Contract has no utility if the host is not in the database.
   108  			if !exists {
   109  				u.GoodForUpload = false
   110  				u.GoodForRenew = false
   111  				return
   112  			}
   113  			// Contract has no utility if the score is poor.
   114  			if !minScore.IsZero() && c.hdb.ScoreBreakdown(host).Score.Cmp(minScore) < 0 {
   115  				u.GoodForUpload = false
   116  				u.GoodForRenew = false
   117  				return
   118  			}
   119  			// Contract has no utility if the host is offline.
   120  			if isOffline(host) {
   121  				u.GoodForUpload = false
   122  				u.GoodForRenew = false
   123  				return
   124  			}
   125  			// Contract should not be used for uploading if the time has come to
   126  			// renew the contract.
   127  			c.mu.RLock()
   128  			blockHeight := c.blockHeight
   129  			renewWindow := c.allowance.RenewWindow
   130  			c.mu.RUnlock()
   131  			if blockHeight+renewWindow >= contract.EndHeight {
   132  				u.GoodForUpload = false
   133  				return
   134  			}
   135  			return
   136  		}()
   137  
   138  		// Apply changes.
   139  		err := c.managedUpdateContractUtility(contract.ID, utility)
   140  		if err != nil {
   141  			return err
   142  		}
   143  	}
   144  	return nil
   145  }
   146  
   147  // managedNewContract negotiates an initial file contract with the specified
   148  // host, saves it, and returns it.
   149  func (c *Contractor) managedNewContract(host modules.HostDBEntry, contractFunding types.Currency, endHeight types.BlockHeight) (modules.RenterContract, error) {
   150  	// reject hosts that are too expensive
   151  	if host.StoragePrice.Cmp(maxStoragePrice) > 0 {
   152  		return modules.RenterContract{}, errTooExpensive
   153  	}
   154  	// cap host.MaxCollateral
   155  	if host.MaxCollateral.Cmp(maxCollateral) > 0 {
   156  		host.MaxCollateral = maxCollateral
   157  	}
   158  
   159  	// get an address to use for negotiation
   160  	uc, err := c.wallet.NextAddress()
   161  	if err != nil {
   162  		return modules.RenterContract{}, err
   163  	}
   164  
   165  	// create contract params
   166  	c.mu.RLock()
   167  	params := proto.ContractParams{
   168  		Host:          host,
   169  		Funding:       contractFunding,
   170  		StartHeight:   c.blockHeight,
   171  		EndHeight:     endHeight,
   172  		RefundAddress: uc.UnlockHash(),
   173  	}
   174  	c.mu.RUnlock()
   175  
   176  	// create transaction builder
   177  	txnBuilder, err := c.wallet.StartTransaction()
   178  	if err != nil {
   179  		return modules.RenterContract{}, err
   180  	}
   181  
   182  	contract, err := c.staticContracts.FormContract(params, txnBuilder, c.tpool, c.hdb, c.tg.StopChan())
   183  	if err != nil {
   184  		txnBuilder.Drop()
   185  		return modules.RenterContract{}, err
   186  	}
   187  
   188  	// Add a mapping from the contract's id to the public key of the host.
   189  	c.mu.Lock()
   190  	c.contractIDToPubKey[contract.ID] = contract.HostPublicKey
   191  	_, exists := c.pubKeysToContractID[string(contract.HostPublicKey.Key)]
   192  	if exists {
   193  		c.mu.Unlock()
   194  		txnBuilder.Drop()
   195  		return modules.RenterContract{}, fmt.Errorf("We already have a contract with host %v", contract.HostPublicKey)
   196  	}
   197  	c.pubKeysToContractID[string(contract.HostPublicKey.Key)] = contract.ID
   198  	c.mu.Unlock()
   199  
   200  	contractValue := contract.RenterFunds
   201  	c.log.Printf("Formed contract %v with %v for %v", contract.ID, host.NetAddress, contractValue.HumanString())
   202  	return contract, nil
   203  }
   204  
   205  // managedPrunePubkeyMap will delete any pubkeys in the pubKeysToContractID map
   206  // that no longer map to an active contract.
   207  func (c *Contractor) managedPrunePubkeyMap() {
   208  	allContracts := c.staticContracts.ViewAll()
   209  	pks := make(map[string]struct{})
   210  	for _, c := range allContracts {
   211  		pks[string(c.HostPublicKey.Key)] = struct{}{}
   212  	}
   213  	c.mu.Lock()
   214  	for pk := range c.pubKeysToContractID {
   215  		if _, exists := pks[pk]; !exists {
   216  			delete(c.pubKeysToContractID, pk)
   217  		}
   218  	}
   219  	c.mu.Unlock()
   220  }
   221  
   222  // managedRenew negotiates a new contract for data already stored with a host.
   223  // It returns the new contract. This is a blocking call that performs network
   224  // I/O.
   225  func (c *Contractor) managedRenew(sc *proto.SafeContract, contractFunding types.Currency, newEndHeight types.BlockHeight) (modules.RenterContract, error) {
   226  	// For convenience
   227  	contract := sc.Metadata()
   228  	// Sanity check - should not be renewing a bad contract.
   229  	utility, ok := c.managedContractUtility(contract.ID)
   230  	if !ok || !utility.GoodForRenew {
   231  		c.log.Critical(fmt.Sprintf("Renewing a contract that has been marked as !GoodForRenew %v/%v",
   232  			ok, utility.GoodForRenew))
   233  	}
   234  
   235  	// Fetch the host associated with this contract.
   236  	host, ok := c.hdb.Host(contract.HostPublicKey)
   237  	if !ok {
   238  		return modules.RenterContract{}, errors.New("no record of that host")
   239  	} else if host.StoragePrice.Cmp(maxStoragePrice) > 0 {
   240  		return modules.RenterContract{}, errTooExpensive
   241  	}
   242  	// cap host.MaxCollateral
   243  	if host.MaxCollateral.Cmp(maxCollateral) > 0 {
   244  		host.MaxCollateral = maxCollateral
   245  	}
   246  
   247  	// get an address to use for negotiation
   248  	uc, err := c.wallet.NextAddress()
   249  	if err != nil {
   250  		return modules.RenterContract{}, err
   251  	}
   252  
   253  	// create contract params
   254  	c.mu.RLock()
   255  	params := proto.ContractParams{
   256  		Host:          host,
   257  		Funding:       contractFunding,
   258  		StartHeight:   c.blockHeight,
   259  		EndHeight:     newEndHeight,
   260  		RefundAddress: uc.UnlockHash(),
   261  	}
   262  	c.mu.RUnlock()
   263  
   264  	// execute negotiation protocol
   265  	txnBuilder, err := c.wallet.StartTransaction()
   266  	if err != nil {
   267  		return modules.RenterContract{}, err
   268  	}
   269  	newContract, err := c.staticContracts.Renew(sc, params, txnBuilder, c.tpool, c.hdb, c.tg.StopChan())
   270  	if err != nil {
   271  		txnBuilder.Drop() // return unused outputs to wallet
   272  		return modules.RenterContract{}, err
   273  	}
   274  
   275  	// Add a mapping from the contract's id to the public key of the host. This
   276  	// will destroy the previous mapping from pubKey to contract id but other
   277  	// modules are only interested in the most recent contract anyway.
   278  	c.mu.Lock()
   279  	c.contractIDToPubKey[newContract.ID] = newContract.HostPublicKey
   280  	c.pubKeysToContractID[string(newContract.HostPublicKey.Key)] = newContract.ID
   281  	c.mu.Unlock()
   282  
   283  	return newContract, nil
   284  }
   285  
   286  // threadedContractMaintenance checks the set of contracts that the contractor
   287  // has against the allownace, renewing any contracts that need to be renewed,
   288  // dropping contracts which are no longer worthwhile, and adding contracts if
   289  // there are not enough.
   290  //
   291  // Between each network call, the thread checks whether a maintenance interrupt
   292  // signal is being sent. If so, maintenance returns, yielding to whatever thread
   293  // issued the interrupt.
   294  func (c *Contractor) threadedContractMaintenance() {
   295  	// Threading protection.
   296  	err := c.tg.Add()
   297  	if err != nil {
   298  		return
   299  	}
   300  	defer c.tg.Done()
   301  
   302  	// Archive contracts that need to be archived before doing additional
   303  	// maintenance, and then prune the pubkey map.
   304  	c.managedArchiveContracts()
   305  	c.managedPrunePubkeyMap()
   306  
   307  	// Nothing to do if there are no hosts.
   308  	c.mu.RLock()
   309  	wantedHosts := c.allowance.Hosts
   310  	c.mu.RUnlock()
   311  	if wantedHosts <= 0 {
   312  		return
   313  	}
   314  
   315  	// Only one instance of this thread should be running at a time. Under
   316  	// normal conditions, fine to return early if another thread is already
   317  	// doing maintenance. The next block will trigger another round. Under
   318  	// testing, control is insufficient if the maintenance loop isn't guaranteed
   319  	// to run.
   320  	if build.Release == "testing" {
   321  		c.maintenanceLock.Lock()
   322  	} else if !c.maintenanceLock.TryLock() {
   323  		return
   324  	}
   325  	defer c.maintenanceLock.Unlock()
   326  
   327  	// Update the utility fields for this contract based on the most recent
   328  	// hostdb.
   329  	if err := c.managedMarkContractsUtility(); err != nil {
   330  		c.log.Println("WARNING: wasn't able to mark contracts", err)
   331  		return
   332  	}
   333  
   334  	// Figure out which contracts need to be renewed, and while we have the
   335  	// lock, figure out the end height for the new contracts and also the amount
   336  	// to spend on each contract.
   337  	//
   338  	// refreshSet is used to mark contracts that need to be refreshed because
   339  	// they have run out of money. The refreshSet indicates how much currency
   340  	// was used previously in the contract line, and is used to figure out how
   341  	// much additional money to add in the refreshed contract.
   342  	//
   343  	// The actions inside this RLock are complex enough to merit wrapping them
   344  	// in a function where we can defer the unlock.
   345  	type renewal struct {
   346  		id     types.FileContractID
   347  		amount types.Currency
   348  	}
   349  	var endHeight types.BlockHeight
   350  	var fundsAvailable types.Currency
   351  	var renewSet []renewal
   352  
   353  	c.mu.RLock()
   354  	currentPeriod := c.currentPeriod
   355  	allowance := c.allowance
   356  	blockHeight := c.blockHeight
   357  	c.mu.RUnlock()
   358  
   359  	// Grab the end height that should be used for the contracts created
   360  	// in the current period.
   361  	endHeight = currentPeriod + allowance.Period
   362  
   363  	// Determine how many funds have been used already in this billing cycle,
   364  	// and how many funds are remaining. We have to calculate these numbers
   365  	// separately to avoid underflow, and then re-join them later to get the
   366  	// full picture for how many funds are available.
   367  	var fundsUsed types.Currency
   368  	for _, contract := range c.staticContracts.ViewAll() {
   369  		// Calculate the cost of the contract line.
   370  		contractLineCost := contract.TotalCost
   371  
   372  		// Check if the contract is expiring. The funds in the contract are
   373  		// handled differently based on this information.
   374  		if blockHeight+allowance.RenewWindow >= contract.EndHeight {
   375  			// The contract is expiring. Some of the funds are locked down to
   376  			// renew the contract, and then the remaining funds can be allocated
   377  			// to 'availableFunds'.
   378  			fundsUsed = fundsUsed.Add(contractLineCost).Sub(contract.RenterFunds)
   379  			fundsAvailable = fundsAvailable.Add(contract.RenterFunds)
   380  		} else {
   381  			// The contract is not expiring. None of the funds in the contract
   382  			// are available to renew or form contracts.
   383  			fundsUsed = fundsUsed.Add(contractLineCost)
   384  		}
   385  	}
   386  
   387  	// Add any unspent funds from the allowance to the available funds. If the
   388  	// allowance has been decreased, it's possible that we actually need to
   389  	// reduce the number of funds available to compensate.
   390  	if fundsAvailable.Add(allowance.Funds).Cmp(fundsUsed) > 0 {
   391  		fundsAvailable = fundsAvailable.Add(allowance.Funds).Sub(fundsUsed)
   392  	} else {
   393  		// Figure out how much we need to remove from fundsAvailable to clear
   394  		// the allowance.
   395  		overspend := fundsUsed.Sub(allowance.Funds).Sub(fundsAvailable)
   396  		if fundsAvailable.Cmp(overspend) > 0 {
   397  			// We still have some funds available.
   398  			fundsAvailable = fundsAvailable.Sub(overspend)
   399  		} else {
   400  			// The overspend exceeds the available funds, set available funds to
   401  			// zero.
   402  			fundsAvailable = types.ZeroCurrency
   403  		}
   404  	}
   405  
   406  	// Iterate through the contracts again, figuring out which contracts to
   407  	// renew and how much extra funds to renew them with.
   408  	for _, contract := range c.staticContracts.ViewAll() {
   409  		utility, ok := c.managedContractUtility(contract.ID)
   410  		if !ok || !utility.GoodForRenew {
   411  			continue
   412  		}
   413  		if blockHeight+allowance.RenewWindow >= contract.EndHeight {
   414  			// This contract needs to be renewed because it is going to expire
   415  			// soon. First step is to calculate how much money should be used in
   416  			// the renewal, based on how much of the contract funds (including
   417  			// previous contracts this billing cycle due to financial resets)
   418  			// were spent throughout this billing cycle.
   419  			//
   420  			// The amount we care about is the total amount that was spent on
   421  			// uploading, downloading, and storage throughout the billing cycle.
   422  			// This is calculated by starting with the total cost and
   423  			// subtracting out all of the fees, and then all of the unused money
   424  			// that was allocated (the RenterFunds).
   425  			//
   426  			// In order to accurately fund contracts based on variable spending,
   427  			// the cost per block is calculated based on the total spent over
   428  			// the length of time that the contract was active before renewal.
   429  			oldContractSpent := contract.TotalCost.Sub(contract.ContractFee).Sub(contract.TxnFee).Sub(contract.SiafundFee).Sub(contract.RenterFunds)
   430  			oldContractLength := blockHeight - contract.StartHeight
   431  			if oldContractLength == 0 {
   432  				oldContractLength = types.BlockHeight(1)
   433  			}
   434  			spentPerBlock := oldContractSpent.Div64(uint64(oldContractLength))
   435  			renewAmount := spentPerBlock.Mul64(uint64(allowance.Period))
   436  
   437  			// Get an estimate for how much the fees will cost. Txn Fee
   438  			_, maxTxnFee := c.tpool.FeeEstimation()
   439  
   440  			// SiafundFee
   441  			siafundFee := types.Tax(blockHeight, renewAmount)
   442  
   443  			// Contract Fee
   444  			host, ok := c.hdb.Host(contract.HostPublicKey)
   445  			if !ok {
   446  				c.log.Println("Could not find contract host in hostdb")
   447  				return
   448  			}
   449  
   450  			estimatedFees := host.ContractPrice.Add(maxTxnFee).Add(siafundFee)
   451  			renewAmount = renewAmount.Add(estimatedFees)
   452  
   453  			// Determine if there is enough funds available to supplement with a
   454  			// 33% bonus, and if there is, add a 33% bonus.
   455  			moneyBuffer := renewAmount.Div64(3)
   456  			if moneyBuffer.Cmp(fundsAvailable) < 0 {
   457  				renewAmount = renewAmount.Add(moneyBuffer)
   458  				fundsAvailable = fundsAvailable.Sub(moneyBuffer)
   459  			} else {
   460  				c.log.Println("WARN: performing a limited renew due to low allowance")
   461  			}
   462  
   463  			// The contract needs to be renewed because it is going to expire
   464  			// soon, and we need to refresh the time.
   465  			renewSet = append(renewSet, renewal{
   466  				id:     contract.ID,
   467  				amount: renewAmount,
   468  			})
   469  		} else {
   470  			// Check if the contract has exhausted its funding and requires
   471  			// premature renewal.
   472  			host, _ := c.hdb.Host(contract.HostPublicKey)
   473  
   474  			// Skip this host if its prices are too high.
   475  			// managedMarkContractsUtility should make this redundant, but this
   476  			// is here for extra safety.
   477  			if host.StoragePrice.Cmp(maxStoragePrice) > 0 || host.UploadBandwidthPrice.Cmp(maxUploadPrice) > 0 {
   478  				continue
   479  			}
   480  
   481  			blockBytes := types.NewCurrency64(modules.SectorSize * uint64(contract.EndHeight-blockHeight))
   482  			sectorStoragePrice := host.StoragePrice.Mul(blockBytes)
   483  			sectorBandwidthPrice := host.UploadBandwidthPrice.Mul64(modules.SectorSize)
   484  			sectorPrice := sectorStoragePrice.Add(sectorBandwidthPrice)
   485  			percentRemaining, _ := big.NewRat(0, 1).SetFrac(contract.RenterFunds.Big(), contract.TotalCost.Big()).Float64()
   486  			if contract.RenterFunds.Cmp(sectorPrice.Mul64(3)) < 0 || percentRemaining < minContractFundRenewalThreshold {
   487  				// This contract does need to be refreshed. Make sure there are
   488  				// enough funds available to perform the refresh, and then
   489  				// execute.
   490  				oldDuration := blockHeight - contract.StartHeight
   491  				newDuration := endHeight - blockHeight
   492  				spendPerBlock := contract.TotalCost.Div64(uint64(oldDuration))
   493  				refreshAmount := spendPerBlock.Mul64(uint64(newDuration))
   494  
   495  				if refreshAmount.Cmp(fundsAvailable) < 0 {
   496  					renewSet = append(renewSet, renewal{
   497  						id:     contract.ID,
   498  						amount: refreshAmount,
   499  					})
   500  				} else {
   501  					c.log.Println("WARN: cannot refresh empty contract due to low allowance.")
   502  				}
   503  			}
   504  		}
   505  	}
   506  	if len(renewSet) != 0 {
   507  		c.log.Printf("renewing %v contracts", len(renewSet))
   508  	}
   509  
   510  	// Remove contracts that are not scheduled for renew from firstFailedRenew.
   511  	c.mu.Lock()
   512  	newFirstFailedRenew := make(map[types.FileContractID]types.BlockHeight)
   513  	for _, r := range renewSet {
   514  		if _, exists := c.numFailedRenews[r.id]; exists {
   515  			newFirstFailedRenew[r.id] = c.numFailedRenews[r.id]
   516  		}
   517  	}
   518  	c.numFailedRenews = newFirstFailedRenew
   519  	c.mu.Unlock()
   520  
   521  	// Loop through the contracts and renew them one-by-one.
   522  	for _, renewal := range renewSet {
   523  		// Pull the variables out of the renewal.
   524  		id := renewal.id
   525  		amount := renewal.amount
   526  
   527  		// Renew one contract.
   528  		func() {
   529  			// Mark the contract as being renewed, and defer logic to unmark it
   530  			// once renewing is complete.
   531  			c.mu.Lock()
   532  			c.renewing[id] = true
   533  			c.mu.Unlock()
   534  			defer func() {
   535  				c.mu.Lock()
   536  				delete(c.renewing, id)
   537  				c.mu.Unlock()
   538  			}()
   539  
   540  			// Wait for any active editors and downloaders to finish for this
   541  			// contract, and then grab the latest revision.
   542  			c.mu.RLock()
   543  			e, eok := c.editors[id]
   544  			d, dok := c.downloaders[id]
   545  			c.mu.RUnlock()
   546  			if eok {
   547  				e.invalidate()
   548  			}
   549  			if dok {
   550  				d.invalidate()
   551  			}
   552  
   553  			// Fetch the contract that we are renewing.
   554  			oldContract, exists := c.staticContracts.Acquire(id)
   555  			if !exists {
   556  				return
   557  			}
   558  			// Return the contract if it's not useful for renewing.
   559  			oldUtility, ok := c.managedContractUtility(id)
   560  			if !ok || !oldUtility.GoodForRenew {
   561  				c.log.Printf("Contract %v slated for renew is marked not good for renew %v/%v",
   562  					id, ok, oldUtility.GoodForRenew)
   563  				c.staticContracts.Return(oldContract)
   564  				return
   565  			}
   566  
   567  			// Calculate endHeight for renewed contracts
   568  			endHeight = currentPeriod + allowance.Period
   569  
   570  			// Perform the actual renew. If the renew fails, return the
   571  			// contract. If the renew fails we check how often it has failed
   572  			// before. Once it has failed for a certain number of blocks in a
   573  			// row and reached its second half of the renew window, we give up
   574  			// on renewing it and set goodForRenew to false.
   575  			newContract, errRenew := c.managedRenew(oldContract, amount, endHeight)
   576  			if errRenew != nil {
   577  				// Increment the number of failed renews for the contract if it
   578  				// was the host's fault.
   579  				if modules.IsHostsFault(errRenew) {
   580  					c.mu.Lock()
   581  					c.numFailedRenews[oldContract.Metadata().ID]++
   582  					c.mu.Unlock()
   583  				}
   584  
   585  				// Check if contract has to be replaced.
   586  				md := oldContract.Metadata()
   587  				c.mu.RLock()
   588  				numRenews, failedBefore := c.numFailedRenews[md.ID]
   589  				c.mu.RUnlock()
   590  				secondHalfOfWindow := blockHeight+allowance.RenewWindow/2 >= md.EndHeight
   591  				replace := numRenews >= consecutiveRenewalsBeforeReplacement
   592  				if failedBefore && secondHalfOfWindow && replace {
   593  					oldUtility.GoodForRenew = false
   594  					oldUtility.GoodForUpload = false
   595  					oldUtility.Locked = true
   596  					err := oldContract.UpdateUtility(oldUtility)
   597  					if err != nil {
   598  						c.log.Println("WARN: failed to mark contract as !goodForRenew:", err)
   599  					}
   600  					c.log.Printf("WARN: failed to renew %v, marked as bad: %v\n",
   601  						oldContract.Metadata().HostPublicKey, errRenew)
   602  					c.staticContracts.Return(oldContract)
   603  					return
   604  				}
   605  
   606  				// Seems like it doesn't have to be replaced yet. Log the
   607  				// failure and number of renews that have failed so far.
   608  				c.log.Printf("WARN: failed to renew contract %v [%v]: %v\n",
   609  					oldContract.Metadata().HostPublicKey, numRenews, errRenew)
   610  				c.staticContracts.Return(oldContract)
   611  				return
   612  			}
   613  			c.log.Printf("Renewed contract %v\n", id)
   614  
   615  			// Update the utility values for the new contract, and for the old
   616  			// contract.
   617  			newUtility := modules.ContractUtility{
   618  				GoodForUpload: true,
   619  				GoodForRenew:  true,
   620  			}
   621  			if err := c.managedUpdateContractUtility(newContract.ID, newUtility); err != nil {
   622  				c.log.Println("Failed to update the contract utilities", err)
   623  				return
   624  			}
   625  			oldUtility.GoodForRenew = false
   626  			oldUtility.GoodForUpload = false
   627  			if err := oldContract.UpdateUtility(oldUtility); err != nil {
   628  				c.log.Println("Failed to update the contract utilities", err)
   629  				return
   630  			}
   631  
   632  			// Lock the contractor as we update it to use the new contract
   633  			// instead of the old contract.
   634  			c.mu.Lock()
   635  			defer c.mu.Unlock()
   636  			// Delete the old contract.
   637  			c.staticContracts.Delete(oldContract)
   638  			// Store the contract in the record of historic contracts.
   639  			c.oldContracts[id] = oldContract.Metadata()
   640  			// Save the contractor.
   641  			err = c.saveSync()
   642  			if err != nil {
   643  				c.log.Println("Failed to save the contractor after creating a new contract.")
   644  			}
   645  		}()
   646  
   647  		// Soft sleep for a minute to allow all of the transactions to propagate
   648  		// the network.
   649  		select {
   650  		case <-c.tg.StopChan():
   651  			return
   652  		case <-c.interruptMaintenance:
   653  			return
   654  		default:
   655  		}
   656  	}
   657  
   658  	// Quit in the event of shutdown.
   659  	select {
   660  	case <-c.tg.StopChan():
   661  		return
   662  	case <-c.interruptMaintenance:
   663  		return
   664  	default:
   665  	}
   666  
   667  	// Count the number of contracts which are good for uploading, and then make
   668  	// more as needed to fill the gap.
   669  	uploadContracts := 0
   670  	for _, id := range c.staticContracts.IDs() {
   671  		if cu, ok := c.managedContractUtility(id); ok && cu.GoodForUpload {
   672  			uploadContracts++
   673  		}
   674  	}
   675  	c.mu.RLock()
   676  	neededContracts := int(c.allowance.Hosts) - uploadContracts
   677  	c.mu.RUnlock()
   678  	if neededContracts <= 0 {
   679  		return
   680  	}
   681  
   682  	// Assemble an exclusion list that includes all of the hosts that we already
   683  	// have contracts with, then select a new batch of hosts to attempt contract
   684  	// formation with.
   685  	c.mu.RLock()
   686  	var exclude []types.SiaPublicKey
   687  	for _, contract := range c.staticContracts.ViewAll() {
   688  		exclude = append(exclude, contract.HostPublicKey)
   689  	}
   690  	initialContractFunds := c.allowance.Funds.Div64(c.allowance.Hosts).Div64(3)
   691  	c.mu.RUnlock()
   692  	hosts, err := c.hdb.RandomHosts(neededContracts*2+randomHostsBufferForScore, exclude)
   693  	if err != nil {
   694  		c.log.Println("WARN: not forming new contracts:", err)
   695  		return
   696  	}
   697  
   698  	// Form contracts with the hosts one at a time, until we have enough
   699  	// contracts.
   700  	for _, host := range hosts {
   701  		// Determine if we have enough money to form a new contract.
   702  		if fundsAvailable.Cmp(initialContractFunds) < 0 {
   703  			c.log.Println("WARN: need to form new contracts, but unable to because of a low allowance")
   704  			break
   705  		}
   706  
   707  		// Attempt forming a contract with this host.
   708  		newContract, err := c.managedNewContract(host, initialContractFunds, endHeight)
   709  		if err != nil {
   710  			c.log.Printf("Attempted to form a contract with %v, but negotiation failed: %v\n", host.NetAddress, err)
   711  			continue
   712  		}
   713  
   714  		// Add this contract to the contractor and save.
   715  		err = c.managedUpdateContractUtility(newContract.ID, modules.ContractUtility{
   716  			GoodForUpload: true,
   717  			GoodForRenew:  true,
   718  		})
   719  		if err != nil {
   720  			c.log.Println("Failed to update the contract utilities", err)
   721  			return
   722  		}
   723  		c.mu.Lock()
   724  		err = c.saveSync()
   725  		c.mu.Unlock()
   726  		if err != nil {
   727  			c.log.Println("Unable to save the contractor:", err)
   728  		}
   729  
   730  		// Quit the loop if we've replaced all needed contracts.
   731  		neededContracts--
   732  		if neededContracts <= 0 {
   733  			break
   734  		}
   735  
   736  		// Soft sleep before making the next contract.
   737  		select {
   738  		case <-c.tg.StopChan():
   739  			return
   740  		case <-c.interruptMaintenance:
   741  			return
   742  		default:
   743  		}
   744  	}
   745  }
   746  
   747  // managedUpdateContractUtility is a helper function that acquires a contract, updates
   748  // its ContractUtility and returns the contract again.
   749  func (c *Contractor) managedUpdateContractUtility(id types.FileContractID, utility modules.ContractUtility) error {
   750  	safeContract, ok := c.staticContracts.Acquire(id)
   751  	if !ok {
   752  		return errors.New("failed to acquire contract for update")
   753  	}
   754  	defer c.staticContracts.Return(safeContract)
   755  	return safeContract.UpdateUtility(utility)
   756  }