github.com/avahowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/modules/renter/contractor/contractor.go (about) 1 package contractor 2 3 import ( 4 "errors" 5 "os" 6 "path/filepath" 7 "sync" 8 9 "github.com/NebulousLabs/Sia/modules" 10 "github.com/NebulousLabs/Sia/persist" 11 "github.com/NebulousLabs/Sia/types" 12 ) 13 14 var ( 15 errNilCS = errors.New("cannot create contractor with nil consensus set") 16 errNilWallet = errors.New("cannot create contractor with nil wallet") 17 errNilTpool = errors.New("cannot create contractor with nil transaction pool") 18 ) 19 20 // A Contractor negotiates, revises, renews, and provides access to file 21 // contracts. 22 type Contractor struct { 23 // dependencies 24 hdb hostDB 25 log *persist.Logger 26 persist persister 27 tpool transactionPool 28 wallet wallet 29 30 allowance modules.Allowance 31 blockHeight types.BlockHeight 32 contracts map[types.FileContractID]modules.RenterContract 33 lastChange modules.ConsensusChangeID 34 renewHeight types.BlockHeight // height at which to renew contracts 35 36 // metrics 37 downloadSpending types.Currency 38 storageSpending types.Currency 39 uploadSpending types.Currency 40 41 mu sync.RWMutex 42 } 43 44 // Allowance returns the current allowance. 45 func (c *Contractor) Allowance() modules.Allowance { 46 c.mu.RLock() 47 defer c.mu.RUnlock() 48 return c.allowance 49 } 50 51 // FinancialMetrics returns the financial metrics of the Contractor. 52 func (c *Contractor) FinancialMetrics() modules.RenterFinancialMetrics { 53 c.mu.RLock() 54 defer c.mu.RUnlock() 55 // calculate contract spending 56 var contractSpending types.Currency 57 for _, contract := range c.contracts { 58 contractSpending = contractSpending.Add(contract.FileContract.Payout) 59 } 60 return modules.RenterFinancialMetrics{ 61 ContractSpending: contractSpending, 62 DownloadSpending: c.downloadSpending, 63 StorageSpending: c.storageSpending, 64 UploadSpending: c.uploadSpending, 65 } 66 } 67 68 // SetAllowance sets the amount of money the Contractor is allowed to spend on 69 // contracts over a given time period, divided among the number of hosts 70 // specified. Note that Contractor can start forming contracts as soon as 71 // SetAllowance is called; that is, it may block. 72 func (c *Contractor) SetAllowance(a modules.Allowance) error { 73 // sanity checks 74 if a.Hosts == 0 { 75 return errors.New("hosts must be non-zero") 76 } else if a.Period == 0 { 77 return errors.New("period must be non-zero") 78 } else if a.RenewWindow == 0 { 79 return errors.New("renew window must be non-zero") 80 } else if a.RenewWindow >= a.Period { 81 return errors.New("renew window must be less than period") 82 } 83 84 err := c.formContracts(a) 85 if err != nil { 86 return err 87 } 88 89 // Set the allowance. 90 c.mu.Lock() 91 c.allowance = a 92 err = c.saveSync() 93 c.mu.Unlock() 94 95 return err 96 97 /* 98 // If this is the first time the allowance has been set, form contracts 99 // immediately. 100 if old.Hosts == 0 { 101 return c.formContracts(a) 102 } 103 104 // Otherwise, if the new allowance is "significantly different" (to be 105 // defined more precisely later), form intermediary contracts. 106 if a.Funds.Cmp(old.Funds) > 0 { 107 // TODO: implement 108 // c.formContracts(diff(a, old)) 109 } 110 111 return nil 112 */ 113 } 114 115 // Contracts returns the contracts formed by the contractor. 116 func (c *Contractor) Contracts() (cs []modules.RenterContract) { 117 c.mu.RLock() 118 defer c.mu.RUnlock() 119 for _, c := range c.contracts { 120 cs = append(cs, c) 121 } 122 return 123 } 124 125 // New returns a new Contractor. 126 func New(cs consensusSet, wallet walletShim, tpool transactionPool, hdb hostDB, persistDir string) (*Contractor, error) { 127 // Check for nil inputs. 128 if cs == nil { 129 return nil, errNilCS 130 } 131 if wallet == nil { 132 return nil, errNilWallet 133 } 134 if tpool == nil { 135 return nil, errNilTpool 136 } 137 138 // Create the persist directory if it does not yet exist. 139 err := os.MkdirAll(persistDir, 0700) 140 if err != nil { 141 return nil, err 142 } 143 // Create the logger. 144 logger, err := persist.NewFileLogger(filepath.Join(persistDir, "contractor.log")) 145 if err != nil { 146 return nil, err 147 } 148 149 // Create Contractor using production dependencies. 150 return newContractor(cs, &walletBridge{w: wallet}, tpool, hdb, newPersist(persistDir), logger) 151 } 152 153 // newContractor creates a Contractor using the provided dependencies. 154 func newContractor(cs consensusSet, w wallet, tp transactionPool, hdb hostDB, p persister, l *persist.Logger) (*Contractor, error) { 155 // Create the Contractor object. 156 c := &Contractor{ 157 hdb: hdb, 158 log: l, 159 persist: p, 160 tpool: tp, 161 wallet: w, 162 163 contracts: make(map[types.FileContractID]modules.RenterContract), 164 } 165 166 // Load the prior persistence structures. 167 err := c.load() 168 if err != nil && !os.IsNotExist(err) { 169 return nil, err 170 } 171 172 err = cs.ConsensusSetSubscribe(c, c.lastChange) 173 if err == modules.ErrInvalidConsensusChangeID { 174 c.lastChange = modules.ConsensusChangeBeginning 175 // ??? fix things ??? 176 // subscribe again using the new ID 177 err = cs.ConsensusSetSubscribe(c, c.lastChange) 178 } 179 if err != nil { 180 return nil, errors.New("contractor subscription failed: " + err.Error()) 181 } 182 183 return c, nil 184 }