gitlab.com/SiaPrime/SiaPrime@v1.4.1/modules/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/big"
    10  	"reflect"
    11  
    12  	"gitlab.com/NebulousLabs/errors"
    13  	"gitlab.com/NebulousLabs/fastrand"
    14  
    15  	"gitlab.com/SiaPrime/SiaPrime/build"
    16  	"gitlab.com/SiaPrime/SiaPrime/modules"
    17  	"gitlab.com/SiaPrime/SiaPrime/modules/renter/proto"
    18  	"gitlab.com/SiaPrime/SiaPrime/types"
    19  )
    20  
    21  var (
    22  	// ErrInsufficientAllowance indicates that the renter's allowance is less
    23  	// than the amount necessary to store at least one sector
    24  	ErrInsufficientAllowance = errors.New("allowance is not large enough to cover fees of contract creation")
    25  	errTooExpensive          = errors.New("host price was too high")
    26  )
    27  
    28  type (
    29  	// fileContractRenewal is an instruction to renew a file contract.
    30  	fileContractRenewal struct {
    31  		id     types.FileContractID
    32  		amount types.Currency
    33  	}
    34  )
    35  
    36  // managedCheckForDuplicates checks for static contracts that have the same host
    37  // key and moves the older one to old contracts.
    38  func (c *Contractor) managedCheckForDuplicates() {
    39  	// Build map for comparison.
    40  	pubkeys := make(map[string]types.FileContractID)
    41  	var newContract, oldContract modules.RenterContract
    42  	for _, contract := range c.staticContracts.ViewAll() {
    43  		id, exists := pubkeys[contract.HostPublicKey.String()]
    44  		if !exists {
    45  			pubkeys[contract.HostPublicKey.String()] = contract.ID
    46  			continue
    47  		}
    48  
    49  		// Duplicate contract found, determine older contract to delete.
    50  		if rc, ok := c.staticContracts.View(id); ok {
    51  			if rc.StartHeight >= contract.StartHeight {
    52  				newContract, oldContract = rc, contract
    53  			} else {
    54  				newContract, oldContract = contract, rc
    55  			}
    56  			c.log.Printf("Duplicate contract found. New conract is %x and old contract is %v", newContract.ID, oldContract.ID)
    57  
    58  			// Get SafeContract
    59  			oldSC, ok := c.staticContracts.Acquire(oldContract.ID)
    60  			if !ok {
    61  				// Update map
    62  				pubkeys[contract.HostPublicKey.String()] = newContract.ID
    63  				continue
    64  			}
    65  
    66  			// Link the contracts to each other and then store the old contract
    67  			// in the record of historic contracts.
    68  			c.mu.Lock()
    69  			c.renewedFrom[newContract.ID] = oldContract.ID
    70  			c.renewedTo[oldContract.ID] = newContract.ID
    71  			c.oldContracts[oldContract.ID] = oldSC.Metadata()
    72  			c.pubKeysToContractID[string(newContract.HostPublicKey.Key)] = newContract.ID
    73  
    74  			// Save the contractor and delete the contract.
    75  			//
    76  			// TODO: Ideally these two things would happen atomically, but I'm
    77  			// not completely certain that's feasible with our current
    78  			// architecture.
    79  			err := c.save()
    80  			if err != nil {
    81  				c.log.Println("Failed to save the contractor after updating renewed maps.")
    82  			}
    83  			c.mu.Unlock()
    84  			c.staticContracts.Delete(oldSC)
    85  
    86  			// Update the pubkeys map to contain the newest contract id.
    87  			//
    88  			// TODO: This means that if there are multiple duplicates, say 3
    89  			// contracts that all share the same host, then the ordering may not
    90  			// be perfect. If in reality the renewal order was A<->B<->C, it's
    91  			// possible for the contractor to end up with A->C and B<->C in the
    92  			// mapping.
    93  			pubkeys[contract.HostPublicKey.String()] = newContract.ID
    94  		}
    95  	}
    96  }
    97  
    98  // managedEstimateRenewFundingRequirements estimates the amount of money that a
    99  // contract is going to need in the next billing cycle by looking at how much
   100  // storage is in the contract and what the historic usage pattern of the
   101  // contract has been.
   102  func (c *Contractor) managedEstimateRenewFundingRequirements(contract modules.RenterContract, blockHeight types.BlockHeight, allowance modules.Allowance) (types.Currency, error) {
   103  	// Fetch the host pricing to use in the estimate.
   104  	host, exists := c.hdb.Host(contract.HostPublicKey)
   105  	if !exists {
   106  		return types.ZeroCurrency, errors.New("could not find host in hostdb")
   107  	}
   108  	if host.Filtered {
   109  		return types.ZeroCurrency, errors.New("host is blacklisted")
   110  	}
   111  
   112  	// Estimate the amount of money that's going to be needed for existing
   113  	// storage.
   114  	dataStored := contract.Transaction.FileContractRevisions[0].NewFileSize
   115  	maintenanceCost := types.NewCurrency64(dataStored).Mul64(uint64(allowance.Period)).Mul(host.StoragePrice)
   116  
   117  	// For the upload and download estimates, we're going to need to know the
   118  	// amount of money that was spent on upload and download by this contract
   119  	// line in this period. That's going to require iterating over the renew
   120  	// history of the contract to get all the spending across any refreshes that
   121  	// occurred this period.
   122  	prevUploadSpending := contract.UploadSpending
   123  	prevDownloadSpending := contract.DownloadSpending
   124  	c.mu.Lock()
   125  	currentID := contract.ID
   126  	for i := 0; i < 10e3; i++ { // prevent an infinite loop if there's an [impossible] contract cycle
   127  		// If there is no previous contract, nothing to do.
   128  		var exists bool
   129  		currentID, exists = c.renewedFrom[currentID]
   130  		if !exists {
   131  			break
   132  		}
   133  
   134  		// If the contract is not in oldContracts, that's probably a bug, but
   135  		// nothing to do otherwise.
   136  		currentContract, exists := c.oldContracts[currentID]
   137  		if !exists {
   138  			c.log.Println("WARN: A known previous contract is not found in c.oldContracts")
   139  			break
   140  		}
   141  
   142  		// If the contract did not start in the current period, then it is not
   143  		// relevant, and none of the previous contracts will be relevant either.
   144  		if currentContract.StartHeight < c.currentPeriod {
   145  			break
   146  		}
   147  
   148  		// Add the upload and download spending.
   149  		prevUploadSpending = prevUploadSpending.Add(currentContract.UploadSpending)
   150  		prevDownloadSpending = prevDownloadSpending.Add(currentContract.DownloadSpending)
   151  	}
   152  	c.mu.Unlock()
   153  
   154  	// Estimate the amount of money that's going to be needed for new storage
   155  	// based on the amount of new storage added in the previous period. Account
   156  	// for both the storage price as well as the upload price.
   157  	prevUploadDataEstimate := prevUploadSpending
   158  	if !host.UploadBandwidthPrice.IsZero() {
   159  		// TODO: Because the host upload bandwidth price can change, this is not
   160  		// the best way to estimate the amount of data that was uploaded to this
   161  		// contract. Better would be to look at the amount of data stored in the
   162  		// contract from the previous cycle and use that to determine how much
   163  		// total data.
   164  		prevUploadDataEstimate = prevUploadDataEstimate.Div(host.UploadBandwidthPrice)
   165  	}
   166  	// Sanity check - the host may have changed prices, make sure we aren't
   167  	// assuming an unreasonable amount of data.
   168  	if types.NewCurrency64(dataStored).Cmp(prevUploadDataEstimate) < 0 {
   169  		prevUploadDataEstimate = types.NewCurrency64(dataStored)
   170  	}
   171  	// The estimated cost for new upload spending is the previous upload
   172  	// bandwidth plus the implied storage cost for all of the new data.
   173  	newUploadsCost := prevUploadSpending.Add(prevUploadDataEstimate.Mul64(uint64(allowance.Period)).Mul(host.StoragePrice))
   174  
   175  	// The download cost is assumed to be the same. Even if the user is
   176  	// uploading more data, the expectation is that the download amounts will be
   177  	// relatively constant. Add in the contract price as well.
   178  	newDownloadsCost := prevDownloadSpending
   179  	contractPrice := host.ContractPrice
   180  
   181  	// Aggregate all estimates so far to compute the estimated siafunds fees.
   182  	// The transaction fees are not included in the siafunds estimate because
   183  	// users are not charged siafund fees on money that doesn't go into the file
   184  	// contract (and the transaction fee goes to the miners, not the file
   185  	// contract).
   186  	beforeSiafundFeesEstimate := maintenanceCost.Add(newUploadsCost).Add(newDownloadsCost).Add(contractPrice)
   187  	afterSiafundFeesEstimate := types.Tax(blockHeight, beforeSiafundFeesEstimate).Add(beforeSiafundFeesEstimate)
   188  
   189  	// Get an estimate for how much money we will be charged before going into
   190  	// the transaction pool.
   191  	_, maxTxnFee := c.tpool.FeeEstimation()
   192  	txnFees := maxTxnFee.Mul64(modules.EstimatedFileContractTransactionSetSize)
   193  
   194  	// Add them all up and then return the estimate plus 33% for error margin
   195  	// and just general volatility of usage pattern.
   196  	estimatedCost := afterSiafundFeesEstimate.Add(txnFees)
   197  	estimatedCost = estimatedCost.Add(estimatedCost.Div64(3))
   198  
   199  	// Check for a sane minimum. The contractor should not be forming contracts
   200  	// with less than 'fileContractMinimumFunding / (num contracts)' of the
   201  	// value of the allowance.
   202  	minimum := allowance.Funds.MulFloat(fileContractMinimumFunding).Div64(allowance.Hosts)
   203  	if estimatedCost.Cmp(minimum) < 0 {
   204  		estimatedCost = minimum
   205  	}
   206  	return estimatedCost, nil
   207  }
   208  
   209  // managedInterruptContractMaintenance will issue an interrupt signal to any
   210  // running maintenance, stopping that maintenance. If there are multiple threads
   211  // running maintenance, they will all be stopped.
   212  func (c *Contractor) managedInterruptContractMaintenance() {
   213  	// Spin up a thread to grab the maintenance lock. Signal that the lock was
   214  	// acquired after the lock is acquired.
   215  	gotLock := make(chan struct{})
   216  	go func() {
   217  		c.maintenanceLock.Lock()
   218  		close(gotLock)
   219  		c.maintenanceLock.Unlock()
   220  	}()
   221  
   222  	// There may be multiple threads contending for the maintenance lock. Issue
   223  	// interrupts repeatedly until we get a signal that the maintenance lock has
   224  	// been acquired.
   225  	for {
   226  		select {
   227  		case <-gotLock:
   228  			return
   229  		case c.interruptMaintenance <- struct{}{}:
   230  			c.log.Debugln("Signal sent to interrupt contract maintenance")
   231  		}
   232  	}
   233  }
   234  
   235  // managedMarkContractsUtility checks every active contract in the contractor and
   236  // figures out whether the contract is useful for uploading, and whether the
   237  // contract should be renewed.
   238  func (c *Contractor) managedMarkContractsUtility() error {
   239  	// Pull a new set of hosts from the hostdb that could be used as a new set
   240  	// to match the allowance. The lowest scoring host of these new hosts will
   241  	// be used as a baseline for determining whether our existing contracts are
   242  	// worthwhile.
   243  	c.mu.RLock()
   244  	hostCount := int(c.allowance.Hosts)
   245  	period := c.allowance.Period
   246  	height := c.blockHeight
   247  	c.mu.RUnlock()
   248  	// TODO Check for Hosts Subnet violation
   249  	// hosts, err := c.hdb.RandomHosts(hostCount+randomHostsBufferForScore, nil, nil, false)
   250  	hosts, err := c.hdb.RandomHosts(hostCount+randomHostsBufferForScore, nil, nil)
   251  	if err != nil {
   252  		return err
   253  	}
   254  
   255  	// Find the minimum score that a host is allowed to have to be considered
   256  	// good for upload.
   257  	var minScoreGFR types.Currency
   258  	var minScoreGFU types.Currency
   259  	if len(hosts) > 0 {
   260  		sb, err := c.hdb.ScoreBreakdown(hosts[0])
   261  		if err != nil {
   262  			return err
   263  		}
   264  		lowestScore := sb.Score
   265  		for i := 1; i < len(hosts); i++ {
   266  			score, err := c.hdb.ScoreBreakdown(hosts[i])
   267  			if err != nil {
   268  				return err
   269  			}
   270  			if score.Score.Cmp(lowestScore) < 0 {
   271  				lowestScore = score.Score
   272  			}
   273  		}
   274  		// Set the minimum acceptable score to a factor of the lowest score.
   275  		minScoreGFR = lowestScore.Div(scoreLeewayGoodForRenew)
   276  		minScoreGFU = lowestScore.Div(scoreLeewayGoodForUpload)
   277  	}
   278  
   279  	// Update utility fields for each contract.
   280  	for _, contract := range c.staticContracts.ViewAll() {
   281  		utility, err := func() (u modules.ContractUtility, err error) {
   282  			u = contract.Utility
   283  
   284  			// If the utility is locked, do nothing.
   285  			if u.Locked {
   286  				return u, nil
   287  			}
   288  
   289  			// Start the contract in good standing if the utility isn't locked
   290  			// but don't completely ignore the utility. A locked utility can
   291  			// always get worse but not better.
   292  			// TODO recheck if this is the right way.
   293  			u.GoodForUpload = true
   294  			u.GoodForRenew = true
   295  
   296  			host, exists := c.hdb.Host(contract.HostPublicKey)
   297  			// Contract has no utility if the host is not in the database. Or is
   298  			// filtered by the blacklist or whitelist.
   299  			if !exists || host.Filtered {
   300  				// Log if the utility has changed.
   301  				if u.GoodForUpload || u.GoodForRenew {
   302  					c.log.Printf("Marking contract as having no utility because found in hostDB: %v, or host is Filtered: %v - %v", exists, host.Filtered, contract.ID)
   303  				}
   304  				u.GoodForUpload = false
   305  				u.GoodForRenew = false
   306  				return u, nil
   307  			}
   308  
   309  			// Contract has no utility if the score is poor.
   310  			sb, err := c.hdb.ScoreBreakdown(host)
   311  			if err != nil {
   312  				return u, err
   313  			}
   314  			if !minScoreGFR.IsZero() && sb.Score.Cmp(minScoreGFR) < 0 {
   315  				// Log if the utility has changed.
   316  				if u.GoodForUpload || u.GoodForRenew {
   317  					c.log.Printf("Marking contract as having no utility because of host score: %v", contract.ID)
   318  					c.log.Println("Min Score:", minScoreGFR)
   319  					c.log.Println("Score:    ", sb.Score)
   320  					c.log.Println("Age Adjustment:        ", sb.AgeAdjustment)
   321  					c.log.Println("Burn Adjustment:       ", sb.BurnAdjustment)
   322  					c.log.Println("Collateral Adjustment: ", sb.CollateralAdjustment)
   323  					c.log.Println("Duration Adjustment:   ", sb.DurationAdjustment)
   324  					c.log.Println("Interaction Adjustment:", sb.InteractionAdjustment)
   325  					c.log.Println("Price Adjustment:      ", sb.PriceAdjustment)
   326  					c.log.Println("Storage Adjustment:    ", sb.StorageRemainingAdjustment)
   327  					c.log.Println("Uptime Adjustment:     ", sb.UptimeAdjustment)
   328  					c.log.Println("Version Adjustment:    ", sb.VersionAdjustment)
   329  				}
   330  				u.GoodForUpload = false
   331  				u.GoodForRenew = false
   332  				return u, nil
   333  			}
   334  
   335  			// Contract has no utility if the host is offline.
   336  			if isOffline(host) {
   337  				// Log if the utility has changed.
   338  				if u.GoodForUpload || u.GoodForRenew {
   339  					c.log.Println("Marking contract as having no utility because of host being offline", contract.ID)
   340  				}
   341  				u.GoodForUpload = false
   342  				u.GoodForRenew = false
   343  				return u, nil
   344  			}
   345  
   346  			// Contract should not be used for uplodaing if the score is poor.
   347  			if !minScoreGFU.IsZero() && sb.Score.Cmp(minScoreGFU) < 0 {
   348  				if u.GoodForUpload {
   349  					c.log.Println("Marking contract as not good for upload because of a poor score", contract.ID)
   350  					c.log.Println("Min Score:", minScoreGFU)
   351  					c.log.Println("Score:    ", sb.Score)
   352  					c.log.Println("Age Adjustment:        ", sb.AgeAdjustment)
   353  					c.log.Println("Burn Adjustment:       ", sb.BurnAdjustment)
   354  					c.log.Println("Collateral Adjustment: ", sb.CollateralAdjustment)
   355  					c.log.Println("Duration Adjustment:   ", sb.DurationAdjustment)
   356  					c.log.Println("Interaction Adjustment:", sb.InteractionAdjustment)
   357  					c.log.Println("Price Adjustment:      ", sb.PriceAdjustment)
   358  					c.log.Println("Storage Adjustment:    ", sb.StorageRemainingAdjustment)
   359  					c.log.Println("Uptime Adjustment:     ", sb.UptimeAdjustment)
   360  					c.log.Println("Version Adjustment:    ", sb.VersionAdjustment)
   361  				}
   362  				if !u.GoodForRenew {
   363  					c.log.Println("Marking contract as being good for renew", contract.ID)
   364  				}
   365  				u.GoodForUpload = false
   366  				u.GoodForRenew = true
   367  				return u, nil
   368  			}
   369  
   370  			// Contract should not be used for uploading if the time has come to
   371  			// renew the contract.
   372  			c.mu.RLock()
   373  			blockHeight := c.blockHeight
   374  			renewWindow := c.allowance.RenewWindow
   375  			c.mu.RUnlock()
   376  			if blockHeight+renewWindow >= contract.EndHeight {
   377  				if u.GoodForUpload {
   378  					c.log.Println("Marking contract as not good for upload because it is time to renew the contract", contract.ID)
   379  				}
   380  				if !u.GoodForRenew {
   381  					c.log.Println("Marking contract as being good for renew:", contract.ID)
   382  				}
   383  				u.GoodForUpload = false
   384  				u.GoodForRenew = true
   385  				return u, nil
   386  			}
   387  
   388  			// Contract should not be used for uploading if the contract does
   389  			// not have enough money remaining to perform the upload.
   390  			blockBytes := types.NewCurrency64(modules.SectorSize * uint64(period))
   391  			sectorStoragePrice := host.StoragePrice.Mul(blockBytes)
   392  			sectorUploadBandwidthPrice := host.UploadBandwidthPrice.Mul64(modules.SectorSize)
   393  			sectorDownloadBandwidthPrice := host.DownloadBandwidthPrice.Mul64(modules.SectorSize)
   394  			sectorBandwidthPrice := sectorUploadBandwidthPrice.Add(sectorDownloadBandwidthPrice)
   395  			sectorPrice := sectorStoragePrice.Add(sectorBandwidthPrice)
   396  			percentRemaining, _ := big.NewRat(0, 1).SetFrac(contract.RenterFunds.Big(), contract.TotalCost.Big()).Float64()
   397  			if contract.RenterFunds.Cmp(sectorPrice.Mul64(3)) < 0 || percentRemaining < MinContractFundUploadThreshold {
   398  				if u.GoodForUpload {
   399  					c.log.Printf("Marking contract as not good for upload because of insufficient funds: %v vs. %v - %v", contract.RenterFunds.Cmp(sectorPrice.Mul64(3)) < 0, percentRemaining, contract.ID)
   400  				}
   401  				if !u.GoodForRenew {
   402  					c.log.Println("Marking contract as being good for renew:", contract.ID)
   403  				}
   404  				u.GoodForUpload = false
   405  				u.GoodForRenew = true
   406  				return u, nil
   407  			}
   408  
   409  			// Contract should not be used for uploading if the host is out of storage.
   410  			if height-u.LastOOSErr <= oosRetryInterval {
   411  				if u.GoodForUpload {
   412  					c.log.Println("Marking contract as not being good for upload due to the host running out of storage:", contract.ID)
   413  				}
   414  				if !u.GoodForRenew {
   415  					c.log.Println("Marking contract as being good for renew:", contract.ID)
   416  				}
   417  				u.GoodForUpload = false
   418  				u.GoodForRenew = true
   419  				return u, nil
   420  			}
   421  
   422  			if !u.GoodForUpload || !u.GoodForRenew {
   423  				c.log.Println("Marking contract as being both GoodForUpload and GoodForRenew", u.GoodForUpload, u.GoodForRenew, contract.ID)
   424  			}
   425  			u.GoodForUpload = true
   426  			u.GoodForRenew = true
   427  			return u, nil
   428  		}()
   429  		if err != nil {
   430  			return err
   431  		}
   432  
   433  		// Apply changes.
   434  		err = c.managedUpdateContractUtility(contract.ID, utility)
   435  		if err != nil {
   436  			return err
   437  		}
   438  	}
   439  	return nil
   440  }
   441  
   442  // managedNewContract negotiates an initial file contract with the specified
   443  // host, saves it, and returns it.
   444  func (c *Contractor) managedNewContract(host modules.HostDBEntry, contractFunding types.Currency, endHeight types.BlockHeight) (types.Currency, modules.RenterContract, error) {
   445  	// reject hosts that are too expensive
   446  	if host.StoragePrice.Cmp(maxStoragePrice) > 0 {
   447  		return types.ZeroCurrency, modules.RenterContract{}, errTooExpensive
   448  	}
   449  	// Determine if host settings align with allowance period
   450  	c.mu.Lock()
   451  	if reflect.DeepEqual(c.allowance, modules.Allowance{}) {
   452  		c.mu.Unlock()
   453  		return types.ZeroCurrency, modules.RenterContract{}, errors.New("called managedNewContract but allowance wasn't set")
   454  	}
   455  	period := c.allowance.Period
   456  	c.mu.Unlock()
   457  
   458  	if host.MaxDuration < period {
   459  		err := errors.New("unable to form contract with host due to insufficient MaxDuration of host")
   460  		return types.ZeroCurrency, modules.RenterContract{}, err
   461  	}
   462  	// cap host.MaxCollateral
   463  	if host.MaxCollateral.Cmp(maxCollateral) > 0 {
   464  		host.MaxCollateral = maxCollateral
   465  	}
   466  
   467  	// get an address to use for negotiation
   468  	uc, err := c.wallet.NextAddress()
   469  	if err != nil {
   470  		return types.ZeroCurrency, modules.RenterContract{}, err
   471  	}
   472  
   473  	// get the wallet seed.
   474  	seed, _, err := c.wallet.PrimarySeed()
   475  	if err != nil {
   476  		return types.ZeroCurrency, modules.RenterContract{}, err
   477  	}
   478  	// derive the renter seed and wipe it once we are done with it.
   479  	renterSeed := proto.DeriveRenterSeed(seed)
   480  	defer fastrand.Read(renterSeed[:])
   481  
   482  	// create contract params
   483  	c.mu.RLock()
   484  	params := proto.ContractParams{
   485  		Allowance:     c.allowance,
   486  		Host:          host,
   487  		Funding:       contractFunding,
   488  		StartHeight:   c.blockHeight,
   489  		EndHeight:     endHeight,
   490  		RefundAddress: uc.UnlockHash(),
   491  		RenterSeed:    renterSeed.EphemeralRenterSeed(endHeight),
   492  	}
   493  	c.mu.RUnlock()
   494  
   495  	// wipe the renter seed once we are done using it.
   496  	defer fastrand.Read(params.RenterSeed[:])
   497  
   498  	// create transaction builder and trigger contract formation.
   499  	txnBuilder, err := c.wallet.StartTransaction()
   500  	if err != nil {
   501  		return types.ZeroCurrency, modules.RenterContract{}, err
   502  	}
   503  	contract, err := c.staticContracts.FormContract(params, txnBuilder, c.tpool, c.hdb, c.tg.StopChan())
   504  	if err != nil {
   505  		txnBuilder.Drop()
   506  		return types.ZeroCurrency, modules.RenterContract{}, err
   507  	}
   508  
   509  	// Add a mapping from the contract's id to the public key of the host.
   510  	c.mu.Lock()
   511  	_, exists := c.pubKeysToContractID[contract.HostPublicKey.String()]
   512  	if exists {
   513  		c.mu.Unlock()
   514  		txnBuilder.Drop()
   515  		// We need to return a funding value because money was spent on this
   516  		// host, even though the full process could not be completed.
   517  		c.log.Println("WARN: Attempted to form a new contract with a host that we already have a contrat with.")
   518  		return contractFunding, modules.RenterContract{}, fmt.Errorf("We already have a contract with host %v", contract.HostPublicKey)
   519  	}
   520  	c.pubKeysToContractID[contract.HostPublicKey.String()] = contract.ID
   521  	c.mu.Unlock()
   522  
   523  	contractValue := contract.RenterFunds
   524  	c.log.Printf("Formed contract %v with %v for %v", contract.ID, host.NetAddress, contractValue.HumanString())
   525  	return contractFunding, contract, nil
   526  }
   527  
   528  // managedPrunePubkeyMap will delete any pubkeys in the pubKeysToContractID map
   529  // that no longer map to an active contract.
   530  func (c *Contractor) managedPrunePubkeyMap() {
   531  	allContracts := c.staticContracts.ViewAll()
   532  	pks := make(map[string]struct{})
   533  	for _, c := range allContracts {
   534  		pks[c.HostPublicKey.String()] = struct{}{}
   535  	}
   536  	c.mu.Lock()
   537  	for pk := range c.pubKeysToContractID {
   538  		if _, exists := pks[pk]; !exists {
   539  			delete(c.pubKeysToContractID, pk)
   540  		}
   541  	}
   542  	c.mu.Unlock()
   543  }
   544  
   545  // managedPrunedRedundantAddressRange uses the hostdb to find hosts that
   546  // violate the rules about address ranges and cancels them.
   547  func (c *Contractor) managedPrunedRedundantAddressRange() {
   548  	// Get all contracts which are not canceled.
   549  	allContracts := c.staticContracts.ViewAll()
   550  	var contracts []modules.RenterContract
   551  	for _, contract := range allContracts {
   552  		if contract.Utility.Locked && !contract.Utility.GoodForRenew && !contract.Utility.GoodForUpload {
   553  			// contract is canceled
   554  			continue
   555  		}
   556  		contracts = append(contracts, contract)
   557  	}
   558  
   559  	// Get all the public keys and map them to contract ids.
   560  	pks := make([]types.SiaPublicKey, 0, len(allContracts))
   561  	cids := make(map[string]types.FileContractID)
   562  	for _, contract := range contracts {
   563  		pks = append(pks, contract.HostPublicKey)
   564  		cids[contract.HostPublicKey.String()] = contract.ID
   565  	}
   566  
   567  	// Let the hostdb filter out bad hosts and cancel contracts with those
   568  	// hosts.
   569  	badHosts := c.hdb.CheckForIPViolations(pks)
   570  	for _, host := range badHosts {
   571  		if err := c.managedCancelContract(cids[host.String()]); err != nil {
   572  			c.log.Print("WARNING: Wasn't able to cancel contract in managedPrunedRedundantAddressRange", err)
   573  		}
   574  	}
   575  }
   576  
   577  // managedRenew negotiates a new contract for data already stored with a host.
   578  // It returns the new contract. This is a blocking call that performs network
   579  // I/O.
   580  func (c *Contractor) managedRenew(sc *proto.SafeContract, contractFunding types.Currency, newEndHeight types.BlockHeight) (modules.RenterContract, error) {
   581  	// For convenience
   582  	contract := sc.Metadata()
   583  	// Sanity check - should not be renewing a bad contract.
   584  	utility, ok := c.managedContractUtility(contract.ID)
   585  	if !ok || !utility.GoodForRenew {
   586  		c.log.Critical(fmt.Sprintf("Renewing a contract that has been marked as !GoodForRenew %v/%v",
   587  			ok, utility.GoodForRenew))
   588  	}
   589  
   590  	// Fetch the host associated with this contract.
   591  	host, ok := c.hdb.Host(contract.HostPublicKey)
   592  	c.mu.Lock()
   593  	if reflect.DeepEqual(c.allowance, modules.Allowance{}) {
   594  		c.mu.Unlock()
   595  		return modules.RenterContract{}, errors.New("called managedRenew but allowance isn't set")
   596  	}
   597  	period := c.allowance.Period
   598  	c.mu.Unlock()
   599  	if !ok {
   600  		return modules.RenterContract{}, errors.New("no record of that host")
   601  	} else if host.Filtered {
   602  		return modules.RenterContract{}, errors.New("host is blacklisted")
   603  	} else if host.StoragePrice.Cmp(maxStoragePrice) > 0 {
   604  		return modules.RenterContract{}, errTooExpensive
   605  	} else if host.MaxDuration < period {
   606  		return modules.RenterContract{}, errors.New("insufficient MaxDuration of host")
   607  	}
   608  
   609  	// cap host.MaxCollateral
   610  	if host.MaxCollateral.Cmp(maxCollateral) > 0 {
   611  		host.MaxCollateral = maxCollateral
   612  	}
   613  
   614  	// get an address to use for negotiation
   615  	uc, err := c.wallet.NextAddress()
   616  	if err != nil {
   617  		return modules.RenterContract{}, err
   618  	}
   619  
   620  	// get the wallet seed
   621  	seed, _, err := c.wallet.PrimarySeed()
   622  	if err != nil {
   623  		return modules.RenterContract{}, err
   624  	}
   625  	// derive the renter seed and wipe it after we are done with it.
   626  	renterSeed := proto.DeriveRenterSeed(seed)
   627  	defer fastrand.Read(renterSeed[:])
   628  
   629  	// create contract params
   630  	c.mu.RLock()
   631  	params := proto.ContractParams{
   632  		Allowance:     c.allowance,
   633  		Host:          host,
   634  		Funding:       contractFunding,
   635  		StartHeight:   c.blockHeight,
   636  		EndHeight:     newEndHeight,
   637  		RefundAddress: uc.UnlockHash(),
   638  		RenterSeed:    renterSeed.EphemeralRenterSeed(newEndHeight),
   639  	}
   640  	c.mu.RUnlock()
   641  
   642  	// wipe the renter seed once we are done using it.
   643  	defer fastrand.Read(params.RenterSeed[:])
   644  
   645  	// execute negotiation protocol
   646  	txnBuilder, err := c.wallet.StartTransaction()
   647  	if err != nil {
   648  		return modules.RenterContract{}, err
   649  	}
   650  	newContract, err := c.staticContracts.Renew(sc, params, txnBuilder, c.tpool, c.hdb, c.tg.StopChan())
   651  	if err != nil {
   652  		txnBuilder.Drop() // return unused outputs to wallet
   653  		return modules.RenterContract{}, err
   654  	}
   655  
   656  	// Add a mapping from the contract's id to the public key of the host. This
   657  	// will destroy the previous mapping from pubKey to contract id but other
   658  	// modules are only interested in the most recent contract anyway.
   659  	c.mu.Lock()
   660  	c.pubKeysToContractID[newContract.HostPublicKey.String()] = newContract.ID
   661  	c.mu.Unlock()
   662  
   663  	return newContract, nil
   664  }
   665  
   666  // managedRenewContract will use the renew instructions to renew a contract,
   667  // returning the amount of money that was put into the contract for renewal.
   668  func (c *Contractor) managedRenewContract(renewInstructions fileContractRenewal, currentPeriod types.BlockHeight, allowance modules.Allowance, blockHeight, endHeight types.BlockHeight) (fundsSpent types.Currency, err error) {
   669  	// Pull the variables out of the renewal.
   670  	id := renewInstructions.id
   671  	amount := renewInstructions.amount
   672  
   673  	// Mark the contract as being renewed, and defer logic to unmark it
   674  	// once renewing is complete.
   675  	c.log.Debugln("Marking a contract for renew:", id)
   676  	c.mu.Lock()
   677  	c.renewing[id] = true
   678  	c.mu.Unlock()
   679  	defer func() {
   680  		c.log.Debugln("Unmarking the contract for renew", id)
   681  		c.mu.Lock()
   682  		delete(c.renewing, id)
   683  		c.mu.Unlock()
   684  	}()
   685  
   686  	// Wait for any active editors/downloaders/sessions to finish for this
   687  	// contract, and then grab the latest revision.
   688  	c.mu.RLock()
   689  	e, eok := c.editors[id]
   690  	d, dok := c.downloaders[id]
   691  	s, sok := c.sessions[id]
   692  	c.mu.RUnlock()
   693  	if eok {
   694  		c.log.Debugln("Waiting for editor invalidation")
   695  		e.invalidate()
   696  		c.log.Debugln("Got editor invalidation")
   697  	}
   698  	if dok {
   699  		c.log.Debugln("Waiting for downloader invalidation")
   700  		d.invalidate()
   701  		c.log.Debugln("Got downloader invalidation")
   702  	}
   703  	if sok {
   704  		c.log.Debugln("Waiting for session invalidation")
   705  		s.invalidate()
   706  		c.log.Debugln("Got session invalidation")
   707  	}
   708  
   709  	// Fetch the contract that we are renewing.
   710  	c.log.Debugln("Acquiring contract from the contract set", id)
   711  	oldContract, exists := c.staticContracts.Acquire(id)
   712  	if !exists {
   713  		c.log.Debugln("Contract does not seem to exist")
   714  		return types.ZeroCurrency, errors.New("contract no longer exists")
   715  	}
   716  	// Return the contract if it's not useful for renewing.
   717  	oldUtility, ok := c.managedContractUtility(id)
   718  	if !ok || !oldUtility.GoodForRenew {
   719  		c.log.Printf("Contract %v slated for renew is marked not good for renew: %v /%v",
   720  			id, ok, oldUtility.GoodForRenew)
   721  		c.staticContracts.Return(oldContract)
   722  		return types.ZeroCurrency, errors.New("contract is marked not good for renew")
   723  	}
   724  
   725  	// Perform the actual renew. If the renew fails, return the
   726  	// contract. If the renew fails we check how often it has failed
   727  	// before. Once it has failed for a certain number of blocks in a
   728  	// row and reached its second half of the renew window, we give up
   729  	// on renewing it and set goodForRenew to false.
   730  	c.log.Debugln("calling managedRenew on contract", id)
   731  	newContract, errRenew := c.managedRenew(oldContract, amount, endHeight)
   732  	c.log.Debugln("managedRenew has returned with error:", errRenew)
   733  	if errRenew != nil {
   734  		// Increment the number of failed renews for the contract if it
   735  		// was the host's fault.
   736  		if modules.IsHostsFault(errRenew) {
   737  			c.mu.Lock()
   738  			c.numFailedRenews[oldContract.Metadata().ID]++
   739  			totalFailures := c.numFailedRenews[oldContract.Metadata().ID]
   740  			c.mu.Unlock()
   741  			c.log.Debugln("remote host determined to be at fault, tallying up failed renews", totalFailures, id)
   742  		}
   743  
   744  		// Check if contract has to be replaced.
   745  		md := oldContract.Metadata()
   746  		c.mu.RLock()
   747  		numRenews, failedBefore := c.numFailedRenews[md.ID]
   748  		c.mu.RUnlock()
   749  		secondHalfOfWindow := blockHeight+allowance.RenewWindow/2 >= md.EndHeight
   750  		replace := numRenews >= consecutiveRenewalsBeforeReplacement
   751  		if failedBefore && secondHalfOfWindow && replace {
   752  			oldUtility.GoodForRenew = false
   753  			oldUtility.GoodForUpload = false
   754  			oldUtility.Locked = true
   755  			err := oldContract.UpdateUtility(oldUtility)
   756  			if err != nil {
   757  				c.log.Println("WARN: failed to mark contract as !goodForRenew:", err)
   758  			}
   759  			c.log.Printf("WARN: consistently failed to renew %v, marked as bad and locked: %v\n",
   760  				oldContract.Metadata().HostPublicKey, errRenew)
   761  			c.staticContracts.Return(oldContract)
   762  			return types.ZeroCurrency, errors.AddContext(errRenew, "contract marked as bad for too many consecutive failed renew attempts")
   763  		}
   764  
   765  		// Seems like it doesn't have to be replaced yet. Log the
   766  		// failure and number of renews that have failed so far.
   767  		c.log.Printf("WARN: failed to renew contract %v [%v]: %v\n",
   768  			oldContract.Metadata().HostPublicKey, numRenews, errRenew)
   769  		c.staticContracts.Return(oldContract)
   770  		return types.ZeroCurrency, errors.AddContext(errRenew, "contract renewal with host was unsuccessful")
   771  	}
   772  	c.log.Printf("Renewed contract %v\n", id)
   773  
   774  	// Update the utility values for the new contract, and for the old
   775  	// contract.
   776  	newUtility := modules.ContractUtility{
   777  		GoodForUpload: true,
   778  		GoodForRenew:  true,
   779  	}
   780  	if err := c.managedUpdateContractUtility(newContract.ID, newUtility); err != nil {
   781  		c.log.Println("Failed to update the contract utilities", err)
   782  		c.staticContracts.Return(oldContract)
   783  		return amount, nil // Error is not returned because the renew succeeded.
   784  	}
   785  	oldUtility.GoodForRenew = false
   786  	oldUtility.GoodForUpload = false
   787  	oldUtility.Locked = true
   788  	if err := oldContract.UpdateUtility(oldUtility); err != nil {
   789  		c.log.Println("Failed to update the contract utilities", err)
   790  		c.staticContracts.Return(oldContract)
   791  		return amount, nil // Error is not returned because the renew succeeded.
   792  	}
   793  
   794  	if c.staticDeps.Disrupt("InterruptContractSaveToDiskAfterDeletion") {
   795  		c.staticContracts.Return(oldContract)
   796  		return amount, errors.New("InterruptContractSaveToDiskAfterDeletion disrupt")
   797  	}
   798  	// Lock the contractor as we update it to use the new contract
   799  	// instead of the old contract.
   800  	c.mu.Lock()
   801  	// Link Contracts
   802  	c.renewedFrom[newContract.ID] = id
   803  	c.renewedTo[id] = newContract.ID
   804  	// Store the contract in the record of historic contracts.
   805  	c.oldContracts[id] = oldContract.Metadata()
   806  	// Save the contractor.
   807  	err = c.save()
   808  	if err != nil {
   809  		c.log.Println("Failed to save the contractor after creating a new contract.")
   810  	}
   811  	c.mu.Unlock()
   812  	// Delete the old contract.
   813  	c.staticContracts.Delete(oldContract)
   814  	return amount, nil
   815  }
   816  
   817  // managedFindRecoverableContracts will spawn a thread to rescan parts of the
   818  // blockchain for recoverable contracts if the wallet has been locked during the
   819  // last scan.
   820  func (c *Contractor) managedFindRecoverableContracts() {
   821  	if c.staticDeps.Disrupt("disableAutomaticContractRecoveryScan") {
   822  		return
   823  	}
   824  	c.mu.RLock()
   825  	cc := c.recentRecoveryChange
   826  	c.mu.RUnlock()
   827  	if err := c.managedInitRecoveryScan(cc); err != nil {
   828  		c.log.Debug(err)
   829  		return
   830  	}
   831  }
   832  
   833  // managedUpdateContractUtility is a helper function that acquires a contract, updates
   834  // its ContractUtility and returns the contract again.
   835  func (c *Contractor) managedUpdateContractUtility(id types.FileContractID, utility modules.ContractUtility) error {
   836  	safeContract, ok := c.staticContracts.Acquire(id)
   837  	if !ok {
   838  		return errors.New("failed to acquire contract for update")
   839  	}
   840  	defer c.staticContracts.Return(safeContract)
   841  	return safeContract.UpdateUtility(utility)
   842  }
   843  
   844  // threadedContractMaintenance checks the set of contracts that the contractor
   845  // has against the allownace, renewing any contracts that need to be renewed,
   846  // dropping contracts which are no longer worthwhile, and adding contracts if
   847  // there are not enough.
   848  //
   849  // Between each network call, the thread checks whether a maintenance interrupt
   850  // signal is being sent. If so, maintenance returns, yielding to whatever thread
   851  // issued the interrupt.
   852  func (c *Contractor) threadedContractMaintenance() {
   853  	err := c.tg.Add()
   854  	if err != nil {
   855  		return
   856  	}
   857  	defer c.tg.Done()
   858  
   859  	// Only one instance of this thread should be running at a time. Under
   860  	// normal conditions, fine to return early if another thread is already
   861  	// doing maintenance. The next block will trigger another round. Under
   862  	// testing, control is insufficient if the maintenance loop isn't guaranteed
   863  	// to run.
   864  	if build.Release == "testing" {
   865  		c.maintenanceLock.Lock()
   866  	} else if !c.maintenanceLock.TryLock() {
   867  		c.log.Debugln("maintenance lock could not be obtained")
   868  		return
   869  	}
   870  	defer c.maintenanceLock.Unlock()
   871  
   872  	// Perform general cleanup of the contracts. This includes recovering lost
   873  	// contracts, archiving contracts, and other cleanup work. This should all
   874  	// happen before the rest of the maintenance.
   875  	c.managedFindRecoverableContracts()
   876  	c.managedRecoverContracts()
   877  	c.managedArchiveContracts()
   878  	c.managedCheckForDuplicates()
   879  	c.managedPrunePubkeyMap()
   880  
   881  	//Save the work if CheckForIPViolation set to false
   882  	if c.hdb.IPViolationsCheck() {
   883  		c.managedPrunedRedundantAddressRange()
   884  	}
   885  
   886  	err = c.managedMarkContractsUtility()
   887  	if err != nil {
   888  		c.log.Debugln("Unable to mark contract utilities:", err)
   889  		return
   890  	}
   891  	err = c.hdb.UpdateContracts(c.staticContracts.ViewAll())
   892  	if err != nil {
   893  		c.log.Debugln("Unable to update hostdb contracts:", err)
   894  		return
   895  	}
   896  
   897  	// If there are no hosts requested by the allowance, there is no remaining
   898  	// work.
   899  	c.mu.RLock()
   900  	wantedHosts := c.allowance.Hosts
   901  	c.mu.RUnlock()
   902  	if wantedHosts <= 0 {
   903  		c.log.Debugln("Exiting contract maintenance because the number of desired hosts is <= zero.")
   904  		return
   905  	}
   906  
   907  	// The rest of this function needs to know a few of the stateful variables
   908  	// from the contractor, build those up under a lock so that the rest of the
   909  	// function can execute without lock contention.
   910  	c.mu.Lock()
   911  	allowance := c.allowance
   912  	blockHeight := c.blockHeight
   913  	currentPeriod := c.currentPeriod
   914  	endHeight := c.contractEndHeight()
   915  	c.mu.Unlock()
   916  
   917  	// Create the renewSet and refreshSet. Each is a list of contracts that need
   918  	// to be renewed, paired with the amount of money to use in each renewal.
   919  	//
   920  	// The renewSet is specifically contracts which are being renewed because
   921  	// they are about to expire. And the refreshSet is contracts that are being
   922  	// renewed because they are out of money.
   923  	//
   924  	// The contractor will prioritize contracts in the renewSet over contracts
   925  	// in the refreshSet. If the wallet does not have enough money, or if the
   926  	// allowance does not have enough money, the contractor will prefer to save
   927  	// data in the long term rather than renew a contract.
   928  	var renewSet []fileContractRenewal
   929  	var refreshSet []fileContractRenewal
   930  
   931  	// Iterate through the contracts again, figuring out which contracts to
   932  	// renew and how much extra funds to renew them with.
   933  	for _, contract := range c.staticContracts.ViewAll() {
   934  		c.log.Debugln("Examining a contract:", contract.HostPublicKey, contract.ID)
   935  		// Skip any host that does not match our whitelist/blacklist filter
   936  		// settings.
   937  		host, _ := c.hdb.Host(contract.HostPublicKey)
   938  		if host.Filtered {
   939  			c.log.Debugln("Contract skipped because it is filtered")
   940  			continue
   941  		}
   942  
   943  		// Skip any contracts which do not exist or are otherwise unworthy for
   944  		// renewal.
   945  		utility, ok := c.managedContractUtility(contract.ID)
   946  		if !ok || !utility.GoodForRenew {
   947  			if uint64(blockHeight-contract.StartHeight) < types.BlocksPerWeek {
   948  				c.log.Debugln("Contract did not last 1 week and is not being renewed", contract.ID)
   949  			}
   950  			c.log.Debugln("Contract skipped because it is not good for renew (utility.GoodForRenew, exists)", utility.GoodForRenew, ok)
   951  			continue
   952  		}
   953  
   954  		// If the contract needs to be renewed because it is about to expire,
   955  		// calculate a spending for the contract that is proportional to how
   956  		// much money was spend on the contract throughout this billing cycle
   957  		// (which is now ending).
   958  		if blockHeight+allowance.RenewWindow >= contract.EndHeight && !c.staticDeps.Disrupt("disableRenew") {
   959  			renewAmount, err := c.managedEstimateRenewFundingRequirements(contract, blockHeight, allowance)
   960  			if err != nil {
   961  				c.log.Debugln("Contract skipped because there was an error estimating renew funding requirements", renewAmount, err)
   962  				continue
   963  			}
   964  			renewSet = append(renewSet, fileContractRenewal{
   965  				id:     contract.ID,
   966  				amount: renewAmount,
   967  			})
   968  			c.log.Debugln("Contract has been added to the renew set for being past the renew height")
   969  			continue
   970  		}
   971  
   972  		// Check if the contract is empty. We define a contract as being empty
   973  		// if less than 'minContractFundRenewalThreshold' funds are remaining
   974  		// (3% at time of writing), or if there is less than 3 sectors worth of
   975  		// storage+upload+download remaining.
   976  		blockBytes := types.NewCurrency64(modules.SectorSize * uint64(allowance.Period))
   977  		sectorStoragePrice := host.StoragePrice.Mul(blockBytes)
   978  		sectorUploadBandwidthPrice := host.UploadBandwidthPrice.Mul64(modules.SectorSize)
   979  		sectorDownloadBandwidthPrice := host.DownloadBandwidthPrice.Mul64(modules.SectorSize)
   980  		sectorBandwidthPrice := sectorUploadBandwidthPrice.Add(sectorDownloadBandwidthPrice)
   981  		sectorPrice := sectorStoragePrice.Add(sectorBandwidthPrice)
   982  		percentRemaining, _ := big.NewRat(0, 1).SetFrac(contract.RenterFunds.Big(), contract.TotalCost.Big()).Float64()
   983  		if contract.RenterFunds.Cmp(sectorPrice.Mul64(3)) < 0 || percentRemaining < MinContractFundRenewalThreshold && !c.staticDeps.Disrupt("disableRenew") {
   984  			// Renew the contract with double the amount of funds that the
   985  			// contract had previously. The reason that we double the funding
   986  			// instead of doing anything more clever is that we don't know what
   987  			// the usage pattern has been. The spending could have all occurred
   988  			// in one burst recently, and the user might need a contract that
   989  			// has substantially more money in it.
   990  			//
   991  			// We double so that heavily used contracts can grow in funding
   992  			// quickly without consuming too many transaction fees, however this
   993  			// does mean that a larger percentage of funds get locked away from
   994  			// the user in the event that the user stops uploading immediately
   995  			// after the renew.
   996  			refreshSet = append(refreshSet, fileContractRenewal{
   997  				id:     contract.ID,
   998  				amount: contract.TotalCost.Mul64(2),
   999  			})
  1000  			c.log.Debugln("Contract identified as needing to be added to refresh set", contract.RenterFunds, sectorPrice.Mul64(3), percentRemaining, MinContractFundRenewalThreshold)
  1001  		} else {
  1002  			c.log.Debugln("Contract did not get added to the refresh set", contract.RenterFunds, sectorPrice.Mul64(3), percentRemaining, MinContractFundRenewalThreshold)
  1003  		}
  1004  	}
  1005  	if len(renewSet) != 0 || len(refreshSet) != 0 {
  1006  		c.log.Printf("renewing %v contracts and refreshing %v contracts", len(renewSet), len(refreshSet))
  1007  	}
  1008  
  1009  	// Update the failed renew map so that it only contains contracts which we
  1010  	// are currently trying to renew or refresh. The failed renew map is a map
  1011  	// that we use to track how many times consecutively we failed to renew a
  1012  	// contract with a host, so that we know if we need to abandon that host.
  1013  	c.mu.Lock()
  1014  	newFirstFailedRenew := make(map[types.FileContractID]types.BlockHeight)
  1015  	for _, r := range renewSet {
  1016  		if _, exists := c.numFailedRenews[r.id]; exists {
  1017  			newFirstFailedRenew[r.id] = c.numFailedRenews[r.id]
  1018  		}
  1019  	}
  1020  	for _, r := range refreshSet {
  1021  		if _, exists := c.numFailedRenews[r.id]; exists {
  1022  			newFirstFailedRenew[r.id] = c.numFailedRenews[r.id]
  1023  		}
  1024  	}
  1025  	c.numFailedRenews = newFirstFailedRenew
  1026  	c.mu.Unlock()
  1027  
  1028  	// Depend on the PeriodSpending function to get a breakdown of spending in
  1029  	// the contractor. Then use that to determine how many funds remain
  1030  	// available in the allowance for renewals.
  1031  	spending := c.PeriodSpending()
  1032  	var fundsRemaining types.Currency
  1033  	// Check for an underflow. This can happen if the user reduced their
  1034  	// allowance at some point to less than what we've already spent.
  1035  	if spending.TotalAllocated.Cmp(allowance.Funds) < 0 {
  1036  		fundsRemaining = allowance.Funds.Sub(spending.TotalAllocated)
  1037  	}
  1038  	c.log.Debugln("The allowance has this many remaning funds:", fundsRemaining)
  1039  
  1040  	// Go through the contracts we've assembled for renewal. Any contracts that
  1041  	// need to be renewed because they are expiring (renewSet) get priority over
  1042  	// contracts that need to be renewed because they have exhausted their funds
  1043  	// (refreshSet). If there is not enough money available, the more expensive
  1044  	// contracts will be skipped.
  1045  	for _, renewal := range renewSet {
  1046  		unlocked, err := c.wallet.Unlocked()
  1047  		if !unlocked || err != nil {
  1048  			c.log.Println("contractor is attempting to renew contracts that are about to expire, however the wallet is locked")
  1049  			return
  1050  		}
  1051  
  1052  		c.log.Println("Attempting to perform a renewal:", renewal.id)
  1053  		// Skip this renewal if we don't have enough funds remaining.
  1054  		if renewal.amount.Cmp(fundsRemaining) > 0 {
  1055  			c.log.Debugln("Skipping renewal because there are not enough funds remaining in the allowance", renewal.id, renewal.amount, fundsRemaining)
  1056  			continue
  1057  		}
  1058  
  1059  		// Renew one contract. The error is ignored because the renew function
  1060  		// already will have logged the error, and in the event of an error,
  1061  		// 'fundsSpent' will return '0'.
  1062  		fundsSpent, err := c.managedRenewContract(renewal, currentPeriod, allowance, blockHeight, endHeight)
  1063  		if err != nil {
  1064  			c.log.Println("Error renewing a contract", renewal.id, err)
  1065  		} else {
  1066  			c.log.Println("Renewal completed without error")
  1067  		}
  1068  		fundsRemaining = fundsRemaining.Sub(fundsSpent)
  1069  
  1070  		// Return here if an interrupt or kill signal has been sent.
  1071  		select {
  1072  		case <-c.tg.StopChan():
  1073  			c.log.Println("returning because the renter was stopped")
  1074  			return
  1075  		case <-c.interruptMaintenance:
  1076  			c.log.Println("returning because maintenance was interrupted")
  1077  			return
  1078  		default:
  1079  		}
  1080  	}
  1081  	for _, renewal := range refreshSet {
  1082  
  1083  		// Skip this renewal if we don't have enough funds remaining.
  1084  		c.log.Debugln("Attempting to perform a contract refresh:", renewal.id)
  1085  		if renewal.amount.Cmp(fundsRemaining) > 0 {
  1086  			c.log.Println("skipping refresh because there are not enough funds remaining in the allowance", renewal.amount, fundsRemaining)
  1087  			continue
  1088  		}
  1089  
  1090  		// Renew one contract. The error is ignored because the renew function
  1091  		// already will have logged the error, and in the event of an error,
  1092  		// 'fundsSpent' will return '0'.
  1093  		fundsSpent, err := c.managedRenewContract(renewal, currentPeriod, allowance, blockHeight, endHeight)
  1094  		if err != nil {
  1095  			c.log.Println("Error refreshing a contract", renewal.id, err)
  1096  		}
  1097  		fundsRemaining = fundsRemaining.Sub(fundsSpent)
  1098  
  1099  		// Return here if an interrupt or kill signal has been sent.
  1100  		select {
  1101  		case <-c.tg.StopChan():
  1102  			c.log.Println("returning because the renter was stopped")
  1103  			return
  1104  		case <-c.interruptMaintenance:
  1105  			c.log.Println("returning because maintenance was interrupted")
  1106  			return
  1107  		default:
  1108  		}
  1109  	}
  1110  
  1111  	// Count the number of contracts which are good for uploading, and then make
  1112  	// more as needed to fill the gap.
  1113  	uploadContracts := 0
  1114  	for _, id := range c.staticContracts.IDs() {
  1115  		if cu, ok := c.managedContractUtility(id); ok && cu.GoodForUpload {
  1116  			uploadContracts++
  1117  		}
  1118  	}
  1119  	c.mu.RLock()
  1120  	neededContracts := int(c.allowance.Hosts) - uploadContracts
  1121  	c.mu.RUnlock()
  1122  	if neededContracts <= 0 {
  1123  		c.log.Debugln("do not seem to need more contracts")
  1124  		return
  1125  	}
  1126  	c.log.Println("need more contracts:", neededContracts)
  1127  
  1128  	// Assemble two exclusion lists. The first one includes all hosts that we
  1129  	// already have contracts with and the second one includes all hosts we
  1130  	// have active contracts with. Then select a new batch of hosts to attempt
  1131  	// contract formation with.
  1132  	allContracts := c.staticContracts.ViewAll()
  1133  	c.mu.RLock()
  1134  	var blacklist []types.SiaPublicKey
  1135  	var addressBlacklist []types.SiaPublicKey
  1136  
  1137  	for _, contract := range allContracts {
  1138  		blacklist = append(blacklist, contract.HostPublicKey)
  1139  		if c.hdb.IPViolationsCheck() {
  1140  			//NOTE: FilterHostsSubnet
  1141  			if !contract.Utility.Locked || contract.Utility.GoodForRenew || contract.Utility.GoodForUpload {
  1142  				addressBlacklist = append(addressBlacklist, contract.HostPublicKey)
  1143  			}
  1144  		}
  1145  	}
  1146  
  1147  	// Add the hosts we have recoverable contracts with to the blacklist to
  1148  	// avoid losing existing data by forming a new/empty contract.
  1149  	for _, contract := range c.recoverableContracts {
  1150  		blacklist = append(blacklist, contract.HostPublicKey)
  1151  	}
  1152  
  1153  	initialContractFunds := c.allowance.Funds.Div64(c.allowance.Hosts).Div64(3)
  1154  	c.mu.RUnlock()
  1155  	hosts, err := c.hdb.RandomHosts(neededContracts*4+randomHostsBufferForScore, blacklist, addressBlacklist)
  1156  	if err != nil {
  1157  		c.log.Println("WARN: not forming new contracts:", err)
  1158  		return
  1159  	}
  1160  	c.log.Debugln("trying to form contracts with hosts, pulled this many hosts from hostdb:", len(hosts))
  1161  
  1162  	// Form contracts with the hosts one at a time, until we have enough
  1163  	// contracts.
  1164  	for _, host := range hosts {
  1165  		unlocked, err := c.wallet.Unlocked()
  1166  		if !unlocked || err != nil {
  1167  			c.log.Println("contractor is attempting to establish new contracts with hosts, however the wallet is locked")
  1168  			return
  1169  		}
  1170  
  1171  		// Determine if we have enough money to form a new contract.
  1172  		if fundsRemaining.Cmp(initialContractFunds) < 0 {
  1173  			c.log.Println("WARN: need to form new contracts, but unable to because of a low allowance")
  1174  			break
  1175  		}
  1176  
  1177  		// If we are using a custom resolver we need to replace the domain name
  1178  		// with 127.0.0.1 to be able to form contracts.
  1179  		if c.staticDeps.Disrupt("customResolver") {
  1180  			port := host.NetAddress.Port()
  1181  			host.NetAddress = modules.NetAddress(fmt.Sprintf("127.0.0.1:%s", port))
  1182  		}
  1183  
  1184  		// Attempt forming a contract with this host.
  1185  		fundsSpent, newContract, err := c.managedNewContract(host, initialContractFunds, endHeight)
  1186  		if err != nil {
  1187  			c.log.Printf("Attempted to form a contract with %v, but negotiation failed: %v\n", host.NetAddress, err)
  1188  			continue
  1189  		}
  1190  		fundsRemaining = fundsRemaining.Sub(fundsSpent)
  1191  
  1192  		sb, err := c.hdb.ScoreBreakdown(host)
  1193  		if err == nil {
  1194  			c.log.Println("A new contract has been formed with a host:", newContract.ID)
  1195  			c.log.Println("Score:    ", sb.Score)
  1196  			c.log.Println("Age Adjustment:        ", sb.AgeAdjustment)
  1197  			c.log.Println("Burn Adjustment:       ", sb.BurnAdjustment)
  1198  			c.log.Println("Collateral Adjustment: ", sb.CollateralAdjustment)
  1199  			c.log.Println("Duration Adjustment:   ", sb.DurationAdjustment)
  1200  			c.log.Println("Interaction Adjustment:", sb.InteractionAdjustment)
  1201  			c.log.Println("Price Adjustment:      ", sb.PriceAdjustment)
  1202  			c.log.Println("Storage Adjustment:    ", sb.StorageRemainingAdjustment)
  1203  			c.log.Println("Uptime Adjustment:     ", sb.UptimeAdjustment)
  1204  			c.log.Println("Version Adjustment:    ", sb.VersionAdjustment)
  1205  		}
  1206  
  1207  		// Add this contract to the contractor and save.
  1208  		err = c.managedUpdateContractUtility(newContract.ID, modules.ContractUtility{
  1209  			GoodForUpload: true,
  1210  			GoodForRenew:  true,
  1211  		})
  1212  		if err != nil {
  1213  			c.log.Println("Failed to update the contract utilities", err)
  1214  			return
  1215  		}
  1216  		c.mu.Lock()
  1217  		err = c.save()
  1218  		c.mu.Unlock()
  1219  		if err != nil {
  1220  			c.log.Println("Unable to save the contractor:", err)
  1221  		}
  1222  
  1223  		// Quit the loop if we've replaced all needed contracts.
  1224  		neededContracts--
  1225  		if neededContracts <= 0 {
  1226  			break
  1227  		}
  1228  
  1229  		// Soft sleep before making the next contract.
  1230  		select {
  1231  		case <-c.tg.StopChan():
  1232  			return
  1233  		case <-c.interruptMaintenance:
  1234  			return
  1235  		default:
  1236  		}
  1237  	}
  1238  }