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 }