github.com/avahowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/modules/renter/contractor/renew.go (about)

     1  package contractor
     2  
     3  import (
     4  	"errors"
     5  
     6  	"github.com/NebulousLabs/Sia/modules"
     7  	"github.com/NebulousLabs/Sia/modules/renter/proto"
     8  	"github.com/NebulousLabs/Sia/types"
     9  )
    10  
    11  // managedRenew negotiates a new contract for data already stored with a host.
    12  // It returns the ID of the new contract. This is a blocking call that
    13  // performs network I/O.
    14  // TODO: take an allowance and renew with those parameters
    15  func (c *Contractor) managedRenew(contract modules.RenterContract, filesize uint64, newEndHeight types.BlockHeight) (types.FileContractID, error) {
    16  	c.mu.RLock()
    17  	height := c.blockHeight
    18  	c.mu.RUnlock()
    19  	if newEndHeight < height {
    20  		return types.FileContractID{}, errors.New("cannot renew below current height")
    21  	}
    22  	host, ok := c.hdb.Host(contract.NetAddress)
    23  	if !ok {
    24  		return types.FileContractID{}, errors.New("no record of that host")
    25  	} else if host.StoragePrice.Cmp(maxStoragePrice) > 0 {
    26  		return types.FileContractID{}, errTooExpensive
    27  	}
    28  
    29  	// get an address to use for negotiation
    30  	uc, err := c.wallet.NextAddress()
    31  	if err != nil {
    32  		return types.FileContractID{}, err
    33  	}
    34  
    35  	// create contract params
    36  	c.mu.RLock()
    37  	params := proto.ContractParams{
    38  		Host:          host,
    39  		Filesize:      filesize,
    40  		StartHeight:   c.blockHeight,
    41  		EndHeight:     newEndHeight,
    42  		RefundAddress: uc.UnlockHash(),
    43  	}
    44  	c.mu.RUnlock()
    45  
    46  	txnBuilder := c.wallet.StartTransaction()
    47  
    48  	// execute negotiation protocol
    49  	newContract, err := proto.Renew(contract, params, txnBuilder, c.tpool)
    50  	if err != nil {
    51  		txnBuilder.Drop() // return unused outputs to wallet
    52  		return types.FileContractID{}, err
    53  	}
    54  
    55  	// update host contract
    56  	c.mu.Lock()
    57  	c.contracts[newContract.ID] = newContract
    58  	err = c.saveSync()
    59  	c.mu.Unlock()
    60  	if err != nil {
    61  		c.log.Println("WARN: failed to save the contractor:", err)
    62  	}
    63  
    64  	return newContract.ID, nil
    65  }
    66  
    67  // threadedRenewContracts renews the Contractor's contracts according to the
    68  // specified allowance and at the specified height.
    69  func (c *Contractor) threadedRenewContracts(allowance modules.Allowance, newHeight types.BlockHeight) {
    70  	// calculate filesize using new allowance
    71  	contracts := c.Contracts()
    72  	var sum types.Currency
    73  	var numHosts uint64
    74  	for _, contract := range contracts {
    75  		if h, ok := c.hdb.Host(contract.NetAddress); ok {
    76  			sum = sum.Add(h.StoragePrice)
    77  			numHosts++
    78  		}
    79  	}
    80  	if numHosts == 0 || numHosts < allowance.Hosts {
    81  		// ??? get more
    82  		return
    83  	}
    84  	avgPrice := sum.Div64(numHosts)
    85  
    86  	costPerSector := avgPrice.Mul64(allowance.Hosts).Mul64(modules.SectorSize).Mul64(uint64(allowance.Period))
    87  
    88  	if allowance.Funds.Cmp(costPerSector) < 0 {
    89  		// errors.New("insufficient funds")
    90  	}
    91  
    92  	// Calculate the filesize of the contracts by using the average host price
    93  	// and rounding down to the nearest sector.
    94  	numSectors, err := allowance.Funds.Div(costPerSector).Uint64()
    95  	if err != nil {
    96  		// errors.New("allowance resulted in unexpectedly large contract size")
    97  	}
    98  	filesize := numSectors * modules.SectorSize
    99  
   100  	for _, contract := range contracts {
   101  		if contract.FileContract.WindowStart < newHeight {
   102  			_, err := c.managedRenew(contract, filesize, newHeight)
   103  			if err != nil {
   104  				c.log.Println("WARN: failed to renew contract", contract.ID, ":", err)
   105  			}
   106  		}
   107  	}
   108  
   109  	// TODO: reset renewHeight if too many rewewals failed.
   110  	// TODO: form more contracts if numRenewed < allowance.Hosts
   111  }