gitlab.com/SkynetLabs/skyd@v1.6.9/skymodules/renter/renter.go (about) 1 // Package renter is responsible for uploading and downloading files on the sia 2 // network. 3 package renter 4 5 // TODO: Allow the 'baseMemory' to be set by the user. 6 // 7 // TODO: The repair loop currently receives new upload jobs through a channel. 8 // The download loop has a better model, a heap that can be pushed to and popped 9 // from concurrently without needing complex channel communication. Migrating 10 // the renter to this model should clean up some of the places where uploading 11 // bottlenecks, and reduce the amount of channel-ninjitsu required to make the 12 // uploading function. 13 // 14 // TODO: Allow user to configure the packet size when ratelimiting the renter. 15 // Currently the default is set to 16kb. That's going to require updating the 16 // API and extending the settings object, and then tweaking the 17 // setBandwidthLimits function. 18 // 19 // TODO: Currently 'callUpdate()' is used after setting the allowance, though 20 // this doesn't guarantee that anything interesting will happen because the 21 // contractor's 'threadedContractMaintenance' will run in the background and 22 // choose to update the hosts and contracts. Really, we should have the 23 // contractor notify the renter whenever there has been a change in the contract 24 // set so that 'callUpdate()' can be used. Implementation in renter.SetSettings. 25 26 import ( 27 "fmt" 28 "io" 29 "net" 30 "os" 31 "path/filepath" 32 "reflect" 33 "strings" 34 "sync" 35 "sync/atomic" 36 "time" 37 38 "gitlab.com/NebulousLabs/errors" 39 "gitlab.com/NebulousLabs/ratelimit" 40 "gitlab.com/NebulousLabs/siamux" 41 "gitlab.com/NebulousLabs/threadgroup" 42 "gitlab.com/NebulousLabs/writeaheadlog" 43 44 "gitlab.com/SkynetLabs/skyd/build" 45 "gitlab.com/SkynetLabs/skyd/persist" 46 "gitlab.com/SkynetLabs/skyd/skykey" 47 "gitlab.com/SkynetLabs/skyd/skymodules" 48 "gitlab.com/SkynetLabs/skyd/skymodules/renter/contractor" 49 "gitlab.com/SkynetLabs/skyd/skymodules/renter/filesystem" 50 "gitlab.com/SkynetLabs/skyd/skymodules/renter/hostdb" 51 "gitlab.com/SkynetLabs/skyd/skymodules/renter/skynetblocklist" 52 "gitlab.com/SkynetLabs/skyd/skymodules/renter/skynetportals" 53 "go.sia.tech/siad/crypto" 54 "go.sia.tech/siad/modules" 55 siasync "go.sia.tech/siad/sync" 56 "go.sia.tech/siad/types" 57 ) 58 59 // OnDiskCacheFolderName is the name of the folder that skyd uses for 60 // caching. 61 const OnDiskCacheFolderName = "cache" 62 63 var ( 64 // defaultCacheSize is the default cache size used for the on-disk cache 65 // unless overwritten. 66 defaultCacheSize = build.Select(build.Var{ 67 Dev: uint64(1 << 27), // 128MiB 68 Standard: uint64(1 << 30), // 1GiB 69 Testing: uint64(10 * 1 << 20), // 10MiB 70 }).(uint64) 71 72 // defaultMinCacheHits is the number of times a section of a datasource 73 // needs to be accessed before being cached. 74 defaultMinCacheHits = build.Select(build.Var{ 75 Dev: uint(1), 76 Standard: uint(3), 77 Testing: uint(1), 78 }).(uint) 79 80 // defaultCachePeriod is the timeframe in which minCacheHits need to 81 // occur for a section within a datasource to get cached. 82 defaultCachePeriod = build.Select(build.Var{ 83 Dev: time.Hour, 84 Standard: 24 * time.Hour, 85 Testing: time.Minute, 86 }).(time.Duration) 87 88 // skynetFeePayoutMultiplier is a factor that we multiply the fee estimation 89 // with to determine the skynet fee payout threshold. 90 skynetFeePayoutMultiplier = build.Select(build.Var{ 91 Dev: uint64(100 * 1024), // 100 * 1kib txn 92 Standard: uint64(100 * 1024), // 100 * 1kib txn 93 Testing: uint64(1), // threshold == fee estimate 94 }).(uint64) 95 ) 96 97 var ( 98 errNilContractor = errors.New("cannot create renter with nil contractor") 99 errNilCS = errors.New("cannot create renter with nil consensus set") 100 errNilGateway = errors.New("cannot create hostdb with nil gateway") 101 errNilHdb = errors.New("cannot create renter with nil hostdb") 102 errNilTpool = errors.New("cannot create renter with nil transaction pool") 103 errNilWallet = errors.New("cannot create renter with nil wallet") 104 ) 105 106 // A hostContractor negotiates, revises, renews, and provides access to file 107 // contracts. 108 type hostContractor interface { 109 modules.Alerter 110 111 // SetAllowance sets the amount of money the contractor is allowed to 112 // spend on contracts over a given time period, divided among the number 113 // of hosts specified. Note that contractor can start forming contracts as 114 // soon as SetAllowance is called; that is, it may block. 115 SetAllowance(skymodules.Allowance) error 116 117 // Allowance returns the current allowance 118 Allowance() skymodules.Allowance 119 120 // Close closes the hostContractor. 121 Close() error 122 123 // CancelContract cancels the Renter's contract 124 CancelContract(id types.FileContractID) error 125 126 // Contracts returns the staticContracts of the renter's hostContractor. 127 Contracts() []skymodules.RenterContract 128 129 // ContractByPublicKey returns the contract associated with the host key. 130 ContractByPublicKey(types.SiaPublicKey) (skymodules.RenterContract, bool) 131 132 // ContractPublicKey returns the public key capable of verifying the renter's 133 // signature on a contract. 134 ContractPublicKey(pk types.SiaPublicKey) (crypto.PublicKey, bool) 135 136 // ChurnStatus returns contract churn stats for the current period. 137 ChurnStatus() skymodules.ContractorChurnStatus 138 139 // ContractUtility returns the utility field for a given contract, along 140 // with a bool indicating if it exists. 141 ContractUtility(types.SiaPublicKey) (skymodules.ContractUtility, bool) 142 143 // ContractStatus returns the status of the given contract within the 144 // watchdog. 145 ContractStatus(fcID types.FileContractID) (skymodules.ContractWatchStatus, bool) 146 147 // CurrentPeriod returns the height at which the current allowance period 148 // began. 149 CurrentPeriod() types.BlockHeight 150 151 // InitRecoveryScan starts scanning the whole blockchain for recoverable 152 // contracts within a separate thread. 153 InitRecoveryScan() error 154 155 // PeriodSpending returns the amount spent on contracts during the current 156 // billing period. 157 PeriodSpending() (skymodules.ContractorSpending, error) 158 159 // ProvidePayment takes a stream and a set of payment details and handles 160 // the payment for an RPC by sending and processing payment request and 161 // response objects to the host. It returns an error in case of failure. 162 ProvidePayment(stream io.ReadWriter, pt *modules.RPCPriceTable, details contractor.PaymentDetails) error 163 164 // OldContracts returns the oldContracts of the renter's hostContractor. 165 OldContracts() []skymodules.RenterContract 166 167 // Editor creates an Editor from the specified contract ID, allowing the 168 // insertion, deletion, and modification of sectors. 169 Editor(types.SiaPublicKey, <-chan struct{}) (contractor.Editor, error) 170 171 // IsOffline reports whether the specified host is considered offline. 172 IsOffline(types.SiaPublicKey) bool 173 174 // Downloader creates a Downloader from the specified contract ID, 175 // allowing the retrieval of sectors. 176 Downloader(types.SiaPublicKey, <-chan struct{}) (contractor.Downloader, error) 177 178 // Session creates a Session from the specified contract ID. 179 Session(types.SiaPublicKey, <-chan struct{}) (contractor.Session, error) 180 181 // RecoverableContracts returns the contracts that the contractor deems 182 // recoverable. That means they are not expired yet and also not part of the 183 // active contracts. Usually this should return an empty slice unless the host 184 // isn't available for recovery or something went wrong. 185 RecoverableContracts() []skymodules.RecoverableContract 186 187 // RecoveryScanStatus returns a bool indicating if a scan for recoverable 188 // contracts is in progress and if it is, the current progress of the scan. 189 RecoveryScanStatus() (bool, types.BlockHeight) 190 191 // RefreshedContract checks if the contract was previously refreshed 192 RefreshedContract(fcid types.FileContractID) bool 193 194 // RenewContract takes an established connection to a host and renews the 195 // given contract with that host. 196 RenewContract(conn net.Conn, fcid types.FileContractID, params skymodules.ContractParams, txnBuilder modules.TransactionBuilder, tpool modules.TransactionPool, hdb skymodules.HostDB, pt *modules.RPCPriceTable) (skymodules.RenterContract, []types.Transaction, error) 197 198 // ResetContractUtilities will reset all bad and locked contracts, as well 199 // as trigger a contract maintenance. 200 ResetContractUtilities() error 201 202 // Synced returns a channel that is closed when the contractor is fully 203 // synced with the peer-to-peer network. 204 Synced() <-chan struct{} 205 206 // UpdateWorkerPool updates the workerpool currently in use by the contractor. 207 UpdateWorkerPool(skymodules.WorkerPool) 208 } 209 210 type renterFuseManager interface { 211 // Mount mounts the files under the specified siapath under the 'mountPoint' folder on 212 // the local filesystem. 213 Mount(mountPoint string, sp skymodules.SiaPath, opts skymodules.MountOptions) (err error) 214 215 // MountInfo returns the list of currently mounted fuse filesystems. 216 MountInfo() []skymodules.MountInfo 217 218 // Unmount unmounts the fuse filesystem currently mounted at mountPoint. 219 Unmount(mountPoint string) error 220 } 221 222 // A siacoinSender is an object capable of sending siacoins to an address. 223 type siacoinSender interface { 224 // SendSiacoins sends the specified amount of siacoins to the provided 225 // address. 226 SendSiacoins(types.Currency, types.UnlockHash) ([]types.Transaction, error) 227 } 228 229 // cachedUtilities contains the cached utilities used when bubbling file and 230 // folder metadata. 231 type cachedUtilities struct { 232 offline map[string]bool 233 goodForRenew map[string]bool 234 contracts map[string]skymodules.RenterContract 235 used []types.SiaPublicKey 236 } 237 238 // A Renter is responsible for tracking all of the files that a user has 239 // uploaded to Sia, as well as the locations and health of these files. 240 type Renter struct { 241 // An atomic variable to export the estimated system scan duration from the 242 // health loop code to the renter. We use a uint64 because that's what's 243 // friendly to the atomic package, but actually it's a time.Duration. 244 atomicSystemHealthScanDuration uint64 245 246 // Account. 247 staticAccountBalanceTarget types.Currency 248 249 // Skynet Management 250 staticSkylinkManager *skylinkManager 251 staticSkynetBlocklist *skynetblocklist.SkynetBlocklist 252 staticSkynetPortals *skynetportals.SkynetPortals 253 staticSpendingHistory *spendingHistory 254 staticSkynetTUSUploader *skynetTUSUploader 255 256 // Download management. 257 staticDownloadHeap *downloadHeap 258 newDownloads chan struct{} // Used to notify download loop that new downloads are available. 259 260 // Download history. 261 // 262 // TODO: Currently the download history doesn't include repair-initiated 263 // downloads, and instead only contains user-initiated downloads. 264 staticDownloadHistory *downloadHistory 265 266 // Upload and repair management. 267 staticDirectoryHeap directoryHeap 268 staticStuckStack stuckStack 269 staticUploadHeap uploadHeap 270 271 // Registry repair related fields. 272 ongoingRegistryRepairs map[modules.RegistryEntryID]struct{} 273 ongoingRegistryRepairsMu sync.Mutex 274 275 // Cache the hosts from the last price estimation result. 276 lastEstimationHosts []skymodules.HostDBEntry 277 278 // Cached fields from the consensus set to be used by workers and 279 // updated in ProcessConsensusChange. 280 csBlockHeight types.BlockHeight 281 csSynced bool 282 csMu sync.RWMutex 283 284 // cachedUtilities contain contract information used when calculating 285 // metadata information about the filesystem, such as health. This 286 // information is used in various functions such as listing filesystem 287 // information and updating directory metadata. These values are cached to 288 // prevent recomputing them too often. 289 cachedUtilities cachedUtilities 290 291 repairingChunksMu sync.Mutex 292 repairingChunks map[uploadChunkID]*unfinishedUploadChunk 293 294 // staticSubscriptionManager is the global manager of registry 295 // subscriptions. 296 staticSubscriptionManager *registrySubscriptionManager 297 298 // The renter's bandwidth ratelimit. 299 staticRL *ratelimit.RateLimit 300 301 // stats cache related fields. 302 statsChan chan struct{} 303 304 // various performance stats 305 staticBaseSectorDownloadStats *skymodules.DownloadOverdriveStats 306 staticBaseSectorUploadStats *skymodules.DistributionTracker 307 staticChunkUploadStats *skymodules.DistributionTracker 308 staticFanoutSectorDownloadStats *skymodules.DownloadOverdriveStats 309 staticRegistryReadStats *skymodules.DistributionTracker 310 staticRegWriteStats *skymodules.DistributionTracker 311 staticStreamBufferStats *skymodules.DistributionTracker 312 313 // Memory management 314 // 315 // staticRegistryMemoryManager is used for updating registry entries and reading 316 // them. 317 // 318 // staticUserUploadManager is used for user-initiated uploads 319 // 320 // staticUserDownloadMemoryManager is used for user-initiated downloads 321 // 322 // staticRepairMemoryManager is used for repair work scheduled by siad 323 // 324 staticRegistryMemoryManager *memoryManager 325 staticRepairMemoryManager *memoryManager 326 staticUserDownloadMemoryManager *memoryManager 327 staticUserUploadMemoryManager *memoryManager 328 329 // Modules and subsystems 330 staticAccountManager *accountManager 331 staticAlerter *modules.GenericAlerter 332 staticConsensusSet modules.ConsensusSet 333 staticDirUpdateBatcher *dirUpdateBatcher 334 staticFileSystem *filesystem.FileSystem 335 staticFuseManager renterFuseManager 336 staticGateway modules.Gateway 337 staticHostContractor hostContractor 338 staticHostDB skymodules.HostDB 339 staticSkykeyManager *skykey.SkykeyManager 340 staticStreamBufferSet *streamBufferSet 341 staticTPool modules.TransactionPool 342 staticUploadChunkDistributionQueue *uploadChunkDistributionQueue 343 staticWallet modules.Wallet 344 staticWorkerPool *workerPool 345 346 // Utilities 347 persist persistence 348 persistDir string 349 mu *siasync.RWMutex 350 staticDeps skymodules.SkydDependencies 351 staticLog *persist.Logger 352 staticMux *siamux.SiaMux 353 staticRepairLog *persist.Logger 354 staticWAL *writeaheadlog.WAL 355 tg threadgroup.ThreadGroup 356 } 357 358 // Close closes the Renter and its dependencies 359 func (r *Renter) Close() error { 360 // TODO: Is this check needed? 361 if r == nil { 362 return nil 363 } 364 365 return errors.Compose(r.tg.Stop(), r.staticHostDB.Close(), r.staticHostContractor.Close(), r.staticSkynetBlocklist.Close(), r.staticSkynetPortals.Close()) 366 } 367 368 // MemoryStatus returns the current status of the memory manager 369 func (r *Renter) MemoryStatus() (skymodules.MemoryStatus, error) { 370 if err := r.tg.Add(); err != nil { 371 return skymodules.MemoryStatus{}, err 372 } 373 defer r.tg.Done() 374 375 repairStatus := r.staticRepairMemoryManager.callStatus() 376 userDownloadStatus := r.staticUserDownloadMemoryManager.callStatus() 377 userUploadStatus := r.staticUserUploadMemoryManager.callStatus() 378 registryStatus := r.staticRegistryMemoryManager.callStatus() 379 total := repairStatus.Add(userDownloadStatus).Add(userUploadStatus).Add(registryStatus) 380 return skymodules.MemoryStatus{ 381 MemoryManagerStatus: total, 382 383 Registry: registryStatus, 384 System: repairStatus, 385 UserDownload: userDownloadStatus, 386 UserUpload: userUploadStatus, 387 }, nil 388 } 389 390 // PriceEstimation estimates the cost in siacoins of performing various storage 391 // and data operations. The estimation will be done using the provided 392 // allowance, if an empty allowance is provided then the renter's current 393 // allowance will be used if one is set. The final allowance used will be 394 // returned. 395 func (r *Renter) PriceEstimation(allowance skymodules.Allowance) (skymodules.RenterPriceEstimation, skymodules.Allowance, error) { 396 if err := r.tg.Add(); err != nil { 397 return skymodules.RenterPriceEstimation{}, skymodules.Allowance{}, err 398 } 399 defer r.tg.Done() 400 // Use provide allowance. If no allowance provided use the existing 401 // allowance. If no allowance exists, use a sane default allowance. 402 if reflect.DeepEqual(allowance, skymodules.Allowance{}) { 403 rs, err := r.Settings() 404 if err != nil { 405 return skymodules.RenterPriceEstimation{}, skymodules.Allowance{}, errors.AddContext(err, "error getting renter settings:") 406 } 407 allowance = rs.Allowance 408 if reflect.DeepEqual(allowance, skymodules.Allowance{}) { 409 allowance = skymodules.DefaultAllowance 410 } 411 } 412 413 // Get hosts for estimate 414 var hosts []skymodules.HostDBEntry 415 hostmap := make(map[string]struct{}) 416 417 // Start by grabbing hosts from contracts 418 // Get host pubkeys from contracts 419 contracts := r.Contracts() 420 var pks []types.SiaPublicKey 421 for _, c := range contracts { 422 u, ok := r.ContractUtility(c.HostPublicKey) 423 if !ok { 424 continue 425 } 426 // Check for active contracts only 427 if !u.GoodForRenew { 428 continue 429 } 430 pks = append(pks, c.HostPublicKey) 431 } 432 // Get hosts from pubkeys 433 for _, pk := range pks { 434 host, ok, err := r.staticHostDB.Host(pk) 435 if !ok || host.Filtered || err != nil { 436 continue 437 } 438 // confirm host wasn't already added 439 if _, ok := hostmap[host.PublicKey.String()]; ok { 440 continue 441 } 442 hosts = append(hosts, host) 443 hostmap[host.PublicKey.String()] = struct{}{} 444 } 445 // Add hosts from previous estimate cache if needed 446 if len(hosts) < int(allowance.Hosts) { 447 id := r.mu.Lock() 448 cachedHosts := r.lastEstimationHosts 449 r.mu.Unlock(id) 450 for _, host := range cachedHosts { 451 // confirm host wasn't already added 452 if _, ok := hostmap[host.PublicKey.String()]; ok { 453 continue 454 } 455 hosts = append(hosts, host) 456 hostmap[host.PublicKey.String()] = struct{}{} 457 } 458 } 459 // Add random hosts if needed 460 if len(hosts) < int(allowance.Hosts) { 461 // Re-initialize the list with SiaPublicKeys to hold the public keys from the current 462 // set of hosts. This list will be used as address filter when requesting random hosts. 463 var pks []types.SiaPublicKey 464 for _, host := range hosts { 465 pks = append(pks, host.PublicKey) 466 } 467 // Grab hosts to perform the estimation. 468 var err error 469 randHosts, err := r.staticHostDB.RandomHostsWithAllowance(int(allowance.Hosts)-len(hosts), pks, pks, allowance) 470 if err != nil { 471 return skymodules.RenterPriceEstimation{}, allowance, errors.AddContext(err, "could not generate estimate, could not get random hosts") 472 } 473 // As the returned random hosts are checked for IP violations and double entries against the current 474 // slice of hosts, the returned hosts can be safely added to the current slice. 475 hosts = append(hosts, randHosts...) 476 } 477 // Check if there are zero hosts, which means no estimation can be made. 478 if len(hosts) == 0 { 479 return skymodules.RenterPriceEstimation{}, allowance, errors.New("estimate cannot be made, there are no hosts") 480 } 481 482 // Add up the costs for each host. 483 var totalContractCost types.Currency 484 var totalDownloadCost types.Currency 485 var totalStorageCost types.Currency 486 var totalUploadCost types.Currency 487 for _, host := range hosts { 488 totalContractCost = totalContractCost.Add(host.ContractPrice) 489 totalDownloadCost = totalDownloadCost.Add(host.DownloadBandwidthPrice) 490 totalStorageCost = totalStorageCost.Add(host.StoragePrice) 491 totalUploadCost = totalUploadCost.Add(host.UploadBandwidthPrice) 492 } 493 494 // Convert values to being human-scale. 495 totalDownloadCost = totalDownloadCost.Mul(modules.BytesPerTerabyte) 496 totalStorageCost = totalStorageCost.Mul(modules.BlockBytesPerMonthTerabyte) 497 totalUploadCost = totalUploadCost.Mul(modules.BytesPerTerabyte) 498 499 // Factor in redundancy. 500 totalStorageCost = totalStorageCost.Mul64(3) // TODO: follow file settings? 501 totalUploadCost = totalUploadCost.Mul64(3) // TODO: follow file settings? 502 503 // Perform averages. 504 totalContractCost = totalContractCost.Div64(uint64(len(hosts))) 505 totalDownloadCost = totalDownloadCost.Div64(uint64(len(hosts))) 506 totalStorageCost = totalStorageCost.Div64(uint64(len(hosts))) 507 totalUploadCost = totalUploadCost.Div64(uint64(len(hosts))) 508 509 // Take the average of the host set to estimate the overall cost of the 510 // contract forming. This is to protect against the case where less hosts 511 // were gathered for the estimate that the allowance requires 512 totalContractCost = totalContractCost.Mul64(allowance.Hosts) 513 514 // Add the cost of paying the transaction fees and then double the contract 515 // costs to account for renewing a full set of contracts. 516 _, feePerByte := r.staticTPool.FeeEstimation() 517 txnsFees := feePerByte.Mul64(skymodules.EstimatedFileContractTransactionSetSize).Mul64(uint64(allowance.Hosts)) 518 totalContractCost = totalContractCost.Add(txnsFees) 519 totalContractCost = totalContractCost.Mul64(2) 520 521 // Determine host collateral to be added to siafund fee 522 var hostCollateral types.Currency 523 contractCostPerHost := totalContractCost.Div64(allowance.Hosts) 524 fundingPerHost := allowance.Funds.Div64(allowance.Hosts) 525 numHosts := uint64(0) 526 for _, host := range hosts { 527 // Assume that the ContractPrice equals contractCostPerHost and that 528 // the txnFee was zero. It doesn't matter since RenterPayoutsPreTax 529 // simply subtracts both values from the funding. 530 host.ContractPrice = contractCostPerHost 531 expectedStorage := allowance.ExpectedStorage / uint64(len(hosts)) 532 _, _, collateral, err := skymodules.RenterPayoutsPreTax(host, fundingPerHost, types.ZeroCurrency, types.ZeroCurrency, types.ZeroCurrency, allowance.Period, expectedStorage) 533 if err != nil { 534 continue 535 } 536 hostCollateral = hostCollateral.Add(collateral) 537 numHosts++ 538 } 539 540 // Divide by zero check. The only way to get 0 numHosts is if 541 // RenterPayoutsPreTax errors for every host. This would happen if the 542 // funding of the allowance is not enough as that would cause the 543 // fundingPerHost to be less than the contract price 544 if numHosts == 0 { 545 return skymodules.RenterPriceEstimation{}, allowance, errors.New("funding insufficient for number of hosts") 546 } 547 // Calculate average collateral and determine collateral for allowance 548 hostCollateral = hostCollateral.Div64(numHosts) 549 hostCollateral = hostCollateral.Mul64(allowance.Hosts) 550 551 // Add in siafund fee. which should be around 10%. The 10% siafund fee 552 // accounts for paying 3.9% siafund on transactions and host collateral. We 553 // estimate the renter to spend all of it's allowance so the siafund fee 554 // will be calculated on the sum of the allowance and the hosts collateral 555 totalPayout := allowance.Funds.Add(hostCollateral) 556 siafundFee := types.Tax(r.staticConsensusSet.Height(), totalPayout) 557 totalContractCost = totalContractCost.Add(siafundFee) 558 559 // Increase estimates by a factor of safety to account for host churn and 560 // any potential missed additions 561 totalContractCost = totalContractCost.MulFloat(PriceEstimationSafetyFactor) 562 totalDownloadCost = totalDownloadCost.MulFloat(PriceEstimationSafetyFactor) 563 totalStorageCost = totalStorageCost.MulFloat(PriceEstimationSafetyFactor) 564 totalUploadCost = totalUploadCost.MulFloat(PriceEstimationSafetyFactor) 565 566 est := skymodules.RenterPriceEstimation{ 567 FormContracts: totalContractCost, 568 DownloadTerabyte: totalDownloadCost, 569 StorageTerabyteMonth: totalStorageCost, 570 UploadTerabyte: totalUploadCost, 571 } 572 573 id := r.mu.Lock() 574 r.lastEstimationHosts = hosts 575 r.mu.Unlock(id) 576 577 return est, allowance, nil 578 } 579 580 // callRenterContractsAndUtilities returns the cached contracts and utilities 581 // from the renter. They can be updated by calling 582 // managedUpdateRenterContractsAndUtilities. 583 func (r *Renter) callRenterContractsAndUtilities() (offline map[string]bool, goodForRenew map[string]bool, contracts map[string]skymodules.RenterContract, used []types.SiaPublicKey) { 584 id := r.mu.Lock() 585 defer r.mu.Unlock(id) 586 cu := r.cachedUtilities 587 return cu.offline, cu.goodForRenew, cu.contracts, cu.used 588 } 589 590 // managedUpdateRenterContractsAndUtilities grabs the pubkeys of the hosts that 591 // the file(s) have been uploaded to and then generates maps of the contract's 592 // utilities showing which hosts are GoodForRenew and which hosts are Offline. 593 // Additionally a map of host pubkeys to renter contract is created. The offline 594 // and goodforrenew maps are needed for calculating redundancy and other file 595 // metrics. All of that information is cached within the renter. 596 func (r *Renter) managedUpdateRenterContractsAndUtilities() { 597 allContracts := r.staticHostContractor.Contracts() 598 cu := updateRenterContractsAndUtilities(allContracts, r.staticHostContractor.IsOffline) 599 600 // Update cache. 601 id := r.mu.Lock() 602 r.cachedUtilities = cu 603 r.mu.Unlock(id) 604 } 605 606 // updateRenterContractsAndUtilities grabs the pubkeys of the hosts that the 607 // file(s) have been uploaded to and then generates maps of the contract's 608 // utilities showing which hosts are GoodForRenew and which hosts are Offline. 609 // Additionally a map of host pubkeys to renter contract is created. The offline 610 // and goodforrenew maps are needed for calculating redundancy and other file 611 // metrics. All of that information is cached within the renter. 612 func updateRenterContractsAndUtilities(allContracts []skymodules.RenterContract, isOffline func(types.SiaPublicKey) bool) cachedUtilities { 613 var used []types.SiaPublicKey 614 usedMap := make(map[string]struct{}) 615 goodForRenew := make(map[string]bool) 616 offline := make(map[string]bool) 617 contracts := make(map[string]skymodules.RenterContract) 618 for _, contract := range allContracts { 619 pk := contract.HostPublicKey 620 pkStr := pk.String() 621 cu := contract.Utility 622 goodForRenew[pkStr] = goodForRenew[pkStr] || cu.GoodForRenew 623 offline[pkStr] = isOffline(pk) 624 contracts[pkStr] = contract 625 _, exists := usedMap[pkStr] 626 if !exists && cu.GoodForRenew { 627 used = append(used, pk) 628 usedMap[pkStr] = struct{}{} 629 } 630 } 631 return cachedUtilities{ 632 offline: offline, 633 goodForRenew: goodForRenew, 634 contracts: contracts, 635 used: used, 636 } 637 } 638 639 // staticSetBandwidthLimits will change the bandwidth limits of the renter based 640 // on the persist values for the bandwidth. 641 func (r *Renter) staticSetBandwidthLimits(downloadSpeed int64, uploadSpeed int64) error { 642 // Input validation. 643 if downloadSpeed < 0 || uploadSpeed < 0 { 644 return errors.New("download/upload rate limit can't be below 0") 645 } 646 647 // Check for sentinel "no limits" value. 648 if downloadSpeed == 0 && uploadSpeed == 0 { 649 r.staticRL.SetLimits(0, 0, 0) 650 } else { 651 // Set the rate limits according to the provided values. 652 r.staticRL.SetLimits(downloadSpeed, uploadSpeed, 4*4096) 653 } 654 return nil 655 } 656 657 // SetSettings will update the settings for the renter. 658 // 659 // NOTE: This function can't be atomic. Typically we try to have user requests 660 // be atomic, so that either everything changes or nothing changes, but since 661 // these changes happen progressively, it's possible for some of the settings 662 // (like the allowance) to succeed, but then if the bandwidth limits for example 663 // are bad, then the allowance will update but the bandwidth will not update. 664 func (r *Renter) SetSettings(s skymodules.RenterSettings) error { 665 if err := r.tg.Add(); err != nil { 666 return err 667 } 668 defer r.tg.Done() 669 // Early input validation. 670 if s.MaxDownloadSpeed < 0 || s.MaxUploadSpeed < 0 { 671 return errors.New("bandwidth limits cannot be negative") 672 } 673 674 // Set allowance. 675 err := r.staticHostContractor.SetAllowance(s.Allowance) 676 if err != nil { 677 return err 678 } 679 680 // Set IPViolationsCheck 681 r.staticHostDB.SetIPViolationCheck(s.IPViolationCheck) 682 683 // Set the bandwidth limits. 684 err = r.staticSetBandwidthLimits(s.MaxDownloadSpeed, s.MaxUploadSpeed) 685 if err != nil { 686 return err 687 } 688 689 // Save the changes. 690 id := r.mu.Lock() 691 r.persist.MaxDownloadSpeed = s.MaxDownloadSpeed 692 r.persist.MaxUploadSpeed = s.MaxUploadSpeed 693 err = r.saveSync() 694 r.mu.Unlock(id) 695 if err != nil { 696 return err 697 } 698 699 // Update the worker pool so that the changes are immediately apparent to 700 // users. 701 r.staticWorkerPool.callUpdate(r) 702 return nil 703 } 704 705 // SetFileTrackingPath sets the on-disk location of an uploaded file to a new 706 // value. Useful if files need to be moved on disk. SetFileTrackingPath will 707 // check that a file exists at the new location and it ensures that it has the 708 // right size, but it can't check that the content is the same. Therefore the 709 // caller is responsible for not accidentally corrupting the uploaded file by 710 // providing a different file with the same size. 711 func (r *Renter) SetFileTrackingPath(siaPath skymodules.SiaPath, newPath string) (err error) { 712 if err := r.tg.Add(); err != nil { 713 return err 714 } 715 defer r.tg.Done() 716 // Check if file exists and is being tracked. 717 entry, err := r.staticFileSystem.OpenSiaFile(siaPath) 718 if err != nil { 719 return err 720 } 721 defer func() { 722 err = errors.Compose(err, entry.Close()) 723 }() 724 725 // Sanity check that a file with the correct size exists at the new 726 // location. 727 fi, err := os.Stat(newPath) 728 if err != nil { 729 return errors.AddContext(err, "failed to get fileinfo of the file") 730 } 731 if uint64(fi.Size()) != entry.Size() { 732 return fmt.Errorf("file sizes don't match - want %v but got %v", entry.Size(), fi.Size()) 733 } 734 735 // Set the new path on disk. 736 return entry.SetLocalPath(newPath) 737 } 738 739 // ActiveHosts returns an array of hostDB's active hosts 740 func (r *Renter) ActiveHosts() ([]skymodules.HostDBEntry, error) { return r.staticHostDB.ActiveHosts() } 741 742 // AllHosts returns an array of all hosts 743 func (r *Renter) AllHosts() ([]skymodules.HostDBEntry, error) { return r.staticHostDB.AllHosts() } 744 745 // BlockDomains blocks all hosts with matching domains 746 func (r *Renter) BlockDomains(domains []string) error { return r.staticHostDB.BlockDomains(domains) } 747 748 // BlockedDomains returns a list of all the blocked domains 749 func (r *Renter) BlockedDomains() ([]string, error) { return r.staticHostDB.BlockedDomains() } 750 751 // UnblockDomains removes domains from the blocked domains 752 func (r *Renter) UnblockDomains(domains []string) error { 753 return r.staticHostDB.UnblockDomains(domains) 754 } 755 756 // Filter returns the renter's hostdb's filterMode and filteredHosts 757 func (r *Renter) Filter() (skymodules.FilterMode, map[string]types.SiaPublicKey, error) { 758 var fm skymodules.FilterMode 759 hosts := make(map[string]types.SiaPublicKey) 760 if err := r.tg.Add(); err != nil { 761 return fm, hosts, err 762 } 763 defer r.tg.Done() 764 fm, hosts, err := r.staticHostDB.Filter() 765 if err != nil { 766 return fm, hosts, errors.AddContext(err, "error getting hostdb filter:") 767 } 768 return fm, hosts, nil 769 } 770 771 // SetFilterMode sets the renter's hostdb filter mode 772 func (r *Renter) SetFilterMode(lm skymodules.FilterMode, hosts []types.SiaPublicKey) error { 773 if err := r.tg.Add(); err != nil { 774 return err 775 } 776 defer r.tg.Done() 777 // Check to see how many hosts are needed for the allowance 778 settings, err := r.Settings() 779 if err != nil { 780 return errors.AddContext(err, "error getting renter settings:") 781 } 782 minHosts := settings.Allowance.Hosts 783 if len(hosts) < int(minHosts) && lm == skymodules.HostDBActiveWhitelist { 784 r.staticLog.Printf("WARN: There are fewer whitelisted hosts than the allowance requires. Have %v whitelisted hosts, need %v to support allowance\n", len(hosts), minHosts) 785 } 786 787 // Set list mode filter for the hostdb 788 if err := r.staticHostDB.SetFilterMode(lm, hosts); err != nil { 789 return err 790 } 791 792 return nil 793 } 794 795 // Host returns the host associated with the given public key 796 func (r *Renter) Host(spk types.SiaPublicKey) (skymodules.HostDBEntry, bool, error) { 797 return r.staticHostDB.Host(spk) 798 } 799 800 // InitialScanComplete returns a boolean indicating if the initial scan of the 801 // hostdb is completed. 802 func (r *Renter) InitialScanComplete() (bool, error) { return r.staticHostDB.InitialScanComplete() } 803 804 // ScoreBreakdown returns the score breakdown 805 func (r *Renter) ScoreBreakdown(e skymodules.HostDBEntry) (skymodules.HostScoreBreakdown, error) { 806 return r.staticHostDB.ScoreBreakdown(e) 807 } 808 809 // EstimateHostScore returns the estimated host score 810 func (r *Renter) EstimateHostScore(e skymodules.HostDBEntry, a skymodules.Allowance) (skymodules.HostScoreBreakdown, error) { 811 if reflect.DeepEqual(a, skymodules.Allowance{}) { 812 settings, err := r.Settings() 813 if err != nil { 814 return skymodules.HostScoreBreakdown{}, errors.AddContext(err, "error getting renter settings:") 815 } 816 a = settings.Allowance 817 } 818 if reflect.DeepEqual(a, skymodules.Allowance{}) { 819 a = skymodules.DefaultAllowance 820 } 821 return r.staticHostDB.EstimateHostScore(e, a) 822 } 823 824 // CancelContract cancels a renter's contract by ID by setting goodForRenew and goodForUpload to false 825 func (r *Renter) CancelContract(id types.FileContractID) error { 826 return r.staticHostContractor.CancelContract(id) 827 } 828 829 // Contracts returns an array of host contractor's staticContracts 830 func (r *Renter) Contracts() []skymodules.RenterContract { return r.staticHostContractor.Contracts() } 831 832 // CurrentPeriod returns the host contractor's current period 833 func (r *Renter) CurrentPeriod() types.BlockHeight { return r.staticHostContractor.CurrentPeriod() } 834 835 // ContractUtility returns the utility field for a given contract, along 836 // with a bool indicating if it exists. 837 func (r *Renter) ContractUtility(pk types.SiaPublicKey) (skymodules.ContractUtility, bool) { 838 return r.staticHostContractor.ContractUtility(pk) 839 } 840 841 // ContractStatus returns the status of the given contract within the watchdog, 842 // and a bool indicating whether or not it is being monitored. 843 func (r *Renter) ContractStatus(fcID types.FileContractID) (skymodules.ContractWatchStatus, bool) { 844 return r.staticHostContractor.ContractStatus(fcID) 845 } 846 847 // ContractorChurnStatus returns contract churn stats for the current period. 848 func (r *Renter) ContractorChurnStatus() skymodules.ContractorChurnStatus { 849 return r.staticHostContractor.ChurnStatus() 850 } 851 852 // ForceAccountsSync schedules a forced sync of the worker's ephemeral account 853 // with the host's balance. 854 func (r *Renter) ForceAccountsSync() { 855 for _, w := range r.staticWorkerPool.callWorkers() { 856 w.callScheduleForcedAccountSync() 857 } 858 } 859 860 // InitRecoveryScan starts scanning the whole blockchain for recoverable 861 // contracts within a separate thread. 862 func (r *Renter) InitRecoveryScan() error { 863 return r.staticHostContractor.InitRecoveryScan() 864 } 865 866 // RecoveryScanStatus returns a bool indicating if a scan for recoverable 867 // contracts is in progress and if it is, the current progress of the scan. 868 func (r *Renter) RecoveryScanStatus() (bool, types.BlockHeight) { 869 return r.staticHostContractor.RecoveryScanStatus() 870 } 871 872 // OldContracts returns an array of host contractor's oldContracts 873 func (r *Renter) OldContracts() []skymodules.RenterContract { 874 return r.staticHostContractor.OldContracts() 875 } 876 877 // Performance is a function call that returns all of the performance 878 // information about the renter. 879 func (r *Renter) Performance() (skymodules.RenterPerformance, error) { 880 healthDuration := time.Duration(atomic.LoadUint64(&r.atomicSystemHealthScanDuration)) 881 return skymodules.RenterPerformance{ 882 SystemHealthScanDuration: healthDuration, 883 884 BaseSectorDownloadOverdriveStats: r.staticBaseSectorDownloadStats, 885 BaseSectorUploadStats: r.staticBaseSectorUploadStats.Stats(), 886 ChunkUploadStats: r.staticChunkUploadStats.Stats(), 887 FanoutSectorDownloadOverdriveStats: r.staticFanoutSectorDownloadStats, 888 RegistryReadStats: r.staticRegistryReadStats.Stats(), 889 RegistryWriteStats: r.staticRegWriteStats.Stats(), 890 StreamBufferReadStats: r.staticStreamBufferStats.Stats(), 891 }, nil 892 } 893 894 // PeriodSpending returns the host contractor's period spending 895 func (r *Renter) PeriodSpending() (skymodules.FinancialMetrics, error) { 896 contractorSpending, err := r.staticHostContractor.PeriodSpending() 897 if err != nil { 898 return skymodules.FinancialMetrics{}, errors.AddContext(err, "failed to get contractor spending") 899 } 900 901 return skymodules.FinancialMetrics{ 902 ContractorSpending: contractorSpending, 903 EphemeralAccountSpending: r.staticAccountManager.EphemeralAccountSpending(), 904 SkynetFee: contractorSpending.SkynetFee(), 905 }, nil 906 } 907 908 // RecoverableContracts returns the host contractor's recoverable contracts. 909 func (r *Renter) RecoverableContracts() []skymodules.RecoverableContract { 910 return r.staticHostContractor.RecoverableContracts() 911 } 912 913 // RefreshedContract returns a bool indicating if the contract was previously 914 // refreshed 915 func (r *Renter) RefreshedContract(fcid types.FileContractID) bool { 916 return r.staticHostContractor.RefreshedContract(fcid) 917 } 918 919 // ResetContractUtilities will reset all bad and locked contracts, as well 920 // as trigger a contract maintenance. 921 func (r *Renter) ResetContractUtilities() error { 922 return r.staticHostContractor.ResetContractUtilities() 923 } 924 925 // ResetStats resets the renter stats. 926 func (r *Renter) ResetStats(statsType types.Specifier) error { 927 switch statsType { 928 case skymodules.OverdriveStats: 929 r.staticBaseSectorDownloadStats.Reset() 930 r.staticFanoutSectorDownloadStats.Reset() 931 return nil 932 default: 933 return fmt.Errorf("invalid stats type '%v', the only supported type is '%v'", statsType, skymodules.OverdriveStats) 934 } 935 } 936 937 // Settings returns the Renter's current settings. 938 func (r *Renter) Settings() (skymodules.RenterSettings, error) { 939 if err := r.tg.Add(); err != nil { 940 return skymodules.RenterSettings{}, err 941 } 942 defer r.tg.Done() 943 download, upload, _ := r.staticRL.Limits() 944 enabled, err := r.staticHostDB.IPViolationsCheck() 945 if err != nil { 946 return skymodules.RenterSettings{}, errors.AddContext(err, "error getting IPViolationsCheck:") 947 } 948 paused, endTime := r.staticUploadHeap.managedPauseStatus() 949 return skymodules.RenterSettings{ 950 Allowance: r.staticHostContractor.Allowance(), 951 IPViolationCheck: enabled, 952 MaxDownloadSpeed: download, 953 MaxUploadSpeed: upload, 954 UploadsStatus: skymodules.UploadsStatus{ 955 Paused: paused, 956 PauseEndTime: endTime, 957 }, 958 }, nil 959 } 960 961 // ProcessConsensusChange returns the process consensus change 962 func (r *Renter) ProcessConsensusChange(cc modules.ConsensusChange) { 963 id := r.mu.Lock() 964 r.lastEstimationHosts = []skymodules.HostDBEntry{} 965 r.mu.Unlock(id) 966 r.csMu.Lock() 967 r.csBlockHeight = cc.BlockHeight 968 r.csSynced = cc.Synced 969 r.csMu.Unlock() 970 971 if cc.Synced { 972 _ = r.tg.Launch(func() { 973 r.staticWorkerPool.callUpdate(r) 974 }) 975 } 976 } 977 978 // threadedPaySkynetFee pays the accumulated skynet fee every 24 hours. 979 func (r *Renter) threadedPaySkynetFee() { 980 // Pay periodically. 981 ticker := time.NewTicker(skymodules.SkynetFeePayoutCheckInterval) 982 for { 983 na := r.staticDeps.SkynetAddress() 984 985 // Compute the threshold. 986 _, max := r.staticTPool.FeeEstimation() 987 threshold := max.Mul64(skynetFeePayoutMultiplier) 988 989 err := paySkynetFee(r.staticSpendingHistory, r.staticWallet, append(r.Contracts(), r.OldContracts()...), na, threshold, r.staticLog) 990 if err != nil { 991 r.staticLog.Print(err) 992 } 993 select { 994 case <-r.tg.StopChan(): 995 return // shutdown 996 case <-ticker.C: 997 } 998 } 999 } 1000 1001 // paySkynetFee pays the accumulated skynet fee every 24 hours. 1002 // TODO: once we pay for monetized content, that also needs to be part of the 1003 // total spending. 1004 func paySkynetFee(sh *spendingHistory, w siacoinSender, contracts []skymodules.RenterContract, addr types.UnlockHash, threshold types.Currency, log *persist.Logger) error { 1005 // Get the last spending. 1006 lastSpending, lastSpendingHeight := sh.LastSpending() 1007 1008 // Only pay fees once per day. 1009 if time.Since(lastSpendingHeight) < skymodules.SkynetFeePayoutInterval { 1010 return nil 1011 } 1012 1013 // Compute the total spending at this point in time. 1014 var totalSpending types.Currency 1015 for _, contract := range contracts { 1016 totalSpending = totalSpending.Add(contract.SkynetSpending()) 1017 } 1018 1019 // Check by how much it increased since the last time. 1020 if totalSpending.Cmp(lastSpending) <= 0 { 1021 return nil // Spending didn't increase 1022 } 1023 1024 // Compute the fee. 1025 fee := totalSpending.Sub(lastSpending).Div64(skymodules.SkynetFeeDivider) 1026 1027 // Check if we are above a payout threshold. 1028 if fee.Cmp(threshold) < 0 { 1029 log.Printf("Not paying fee of %v since it's below the threshold of %v", fee, threshold) 1030 return nil // Don't pay if we are below the threshold. 1031 } 1032 1033 // Log that we are about to pay the fee. 1034 log.Printf("Paying fee of %v to %v after spending increased from %v to %v", fee, addr, lastSpending, totalSpending) 1035 1036 // Send the fee. 1037 txn, err := w.SendSiacoins(fee, addr) 1038 if err != nil { 1039 return errors.AddContext(err, "Failed to send siacoins for skynet fee. Will retry again in an hour") 1040 } 1041 1042 // Mark the totalSpending in the history. 1043 err = sh.AddSpending(totalSpending, txn, time.Now()) 1044 if err != nil { 1045 err = errors.AddContext(err, "failed to persist paid skynet fees") 1046 build.Critical(err) 1047 return err 1048 } 1049 return nil 1050 } 1051 1052 // SetIPViolationCheck is a passthrough method to the hostdb's method of the 1053 // same name. 1054 func (r *Renter) SetIPViolationCheck(enabled bool) { 1055 r.staticHostDB.SetIPViolationCheck(enabled) 1056 } 1057 1058 // MountInfo returns the list of currently mounted fusefilesystems. 1059 func (r *Renter) MountInfo() []skymodules.MountInfo { 1060 return r.staticFuseManager.MountInfo() 1061 } 1062 1063 // Mount mounts the files under the specified siapath under the 'mountPoint' folder on 1064 // the local filesystem. 1065 func (r *Renter) Mount(mountPoint string, sp skymodules.SiaPath, opts skymodules.MountOptions) error { 1066 return r.staticFuseManager.Mount(mountPoint, sp, opts) 1067 } 1068 1069 // Unmount unmounts the fuse filesystem currently mounted at mountPoint. 1070 func (r *Renter) Unmount(mountPoint string) error { 1071 return r.staticFuseManager.Unmount(mountPoint) 1072 } 1073 1074 // AddSkykey adds the skykey with the given name, cipher type, and entropy to 1075 // the renter's skykey manager. 1076 func (r *Renter) AddSkykey(sk skykey.Skykey) error { 1077 if err := r.tg.Add(); err != nil { 1078 return err 1079 } 1080 defer r.tg.Done() 1081 return r.staticSkykeyManager.AddKey(sk) 1082 } 1083 1084 // DeleteSkykeyByID deletes the Skykey with the given ID from the renter's skykey 1085 // manager if it exists. 1086 func (r *Renter) DeleteSkykeyByID(id skykey.SkykeyID) error { 1087 if err := r.tg.Add(); err != nil { 1088 return err 1089 } 1090 defer r.tg.Done() 1091 return r.staticSkykeyManager.DeleteKeyByID(id) 1092 } 1093 1094 // DeleteSkykeyByName deletes the Skykey with the given name from the renter's skykey 1095 // manager if it exists. 1096 func (r *Renter) DeleteSkykeyByName(name string) error { 1097 if err := r.tg.Add(); err != nil { 1098 return err 1099 } 1100 defer r.tg.Done() 1101 return r.staticSkykeyManager.DeleteKeyByName(name) 1102 } 1103 1104 // SkykeyByName gets the Skykey with the given name from the renter's skykey 1105 // manager if it exists. 1106 func (r *Renter) SkykeyByName(name string) (skykey.Skykey, error) { 1107 if err := r.tg.Add(); err != nil { 1108 return skykey.Skykey{}, err 1109 } 1110 defer r.tg.Done() 1111 return r.staticSkykeyManager.KeyByName(name) 1112 } 1113 1114 // CreateSkykey creates a new Skykey with the given name and ciphertype. 1115 func (r *Renter) CreateSkykey(name string, skType skykey.SkykeyType) (skykey.Skykey, error) { 1116 if err := r.tg.Add(); err != nil { 1117 return skykey.Skykey{}, err 1118 } 1119 defer r.tg.Done() 1120 return r.staticSkykeyManager.CreateKey(name, skType) 1121 } 1122 1123 // NewRegistrySubscriber creates a new registry subscriber. 1124 func (r *Renter) NewRegistrySubscriber(notifyFunc func(entry skymodules.LatestRegistryEntry) error) (skymodules.RegistrySubscriber, error) { 1125 if err := r.tg.Add(); err != nil { 1126 return nil, err 1127 } 1128 defer r.tg.Done() 1129 return r.staticSubscriptionManager.NewSubscriber(notifyFunc), nil 1130 } 1131 1132 // SkykeyByID gets the Skykey with the given ID from the renter's skykey 1133 // manager if it exists. 1134 func (r *Renter) SkykeyByID(id skykey.SkykeyID) (skykey.Skykey, error) { 1135 if err := r.tg.Add(); err != nil { 1136 return skykey.Skykey{}, err 1137 } 1138 defer r.tg.Done() 1139 return r.staticSkykeyManager.KeyByID(id) 1140 } 1141 1142 // SkykeyIDByName gets the SkykeyID of the key with the given name if it 1143 // exists. 1144 func (r *Renter) SkykeyIDByName(name string) (skykey.SkykeyID, error) { 1145 if err := r.tg.Add(); err != nil { 1146 return skykey.SkykeyID{}, err 1147 } 1148 defer r.tg.Done() 1149 return r.staticSkykeyManager.IDByName(name) 1150 } 1151 1152 // Skykeys returns a slice containing each Skykey being stored by the renter. 1153 func (r *Renter) Skykeys() ([]skykey.Skykey, error) { 1154 if err := r.tg.Add(); err != nil { 1155 return nil, err 1156 } 1157 defer r.tg.Done() 1158 1159 return r.staticSkykeyManager.Skykeys(), nil 1160 } 1161 1162 // Enforce that Renter satisfies the skymodules.Renter interface. 1163 var _ skymodules.Renter = (*Renter)(nil) 1164 1165 // renterBlockingStartup handles the blocking portion of NewCustomRenter. 1166 func renterBlockingStartup(g modules.Gateway, cs modules.ConsensusSet, tpool modules.TransactionPool, hdb skymodules.HostDB, w modules.Wallet, hc hostContractor, mux *siamux.SiaMux, tus skymodules.SkynetTUSUploadStore, persistDir string, rl *ratelimit.RateLimit, deps skymodules.SkydDependencies) (*Renter, error) { 1167 if g == nil { 1168 return nil, errNilGateway 1169 } 1170 if cs == nil { 1171 return nil, errNilCS 1172 } 1173 if tpool == nil { 1174 return nil, errNilTpool 1175 } 1176 if hc == nil { 1177 return nil, errNilContractor 1178 } 1179 if hdb == nil && build.Release != "testing" { 1180 return nil, errNilHdb 1181 } 1182 if w == nil { 1183 return nil, errNilWallet 1184 } 1185 1186 r := &Renter{ 1187 // Initialize cached cs fields 1188 csBlockHeight: cs.Height(), 1189 csSynced: cs.Synced(), 1190 1191 // Initiate skynet resources 1192 staticSkylinkManager: newSkylinkManager(), 1193 1194 repairingChunks: make(map[uploadChunkID]*unfinishedUploadChunk), 1195 1196 // Making newDownloads a buffered channel means that most of the time, a 1197 // new download will trigger an unnecessary extra iteration of the 1198 // download heap loop, searching for a chunk that's not there. This is 1199 // preferable to the alternative, where in rare cases the download heap 1200 // will miss work altogether. 1201 newDownloads: make(chan struct{}, 1), 1202 staticDownloadHeap: &downloadHeap{}, 1203 1204 staticBaseSectorDownloadStats: skymodules.NewSectorDownloadStats(), 1205 staticFanoutSectorDownloadStats: skymodules.NewSectorDownloadStats(), 1206 1207 staticUploadHeap: uploadHeap{ 1208 stuckHeapChunks: make(map[uploadChunkID]*unfinishedUploadChunk), 1209 unstuckHeapChunks: make(map[uploadChunkID]*unfinishedUploadChunk), 1210 1211 newUploads: make(chan struct{}, 1), 1212 repairNeeded: make(chan struct{}, 1), 1213 stuckChunkFound: make(chan struct{}, 1), 1214 stuckChunkSuccess: make(chan struct{}, 1), 1215 1216 pauseChan: make(chan struct{}), 1217 }, 1218 staticDirectoryHeap: directoryHeap{ 1219 heapDirectories: make(map[skymodules.SiaPath]*directory), 1220 }, 1221 1222 staticDownloadHistory: newDownloadHistory(), 1223 1224 ongoingRegistryRepairs: make(map[modules.RegistryEntryID]struct{}), 1225 1226 staticConsensusSet: cs, 1227 staticDeps: deps, 1228 staticGateway: g, 1229 staticWallet: w, 1230 staticHostDB: hdb, 1231 staticHostContractor: hc, 1232 persistDir: persistDir, 1233 staticRL: rl, 1234 staticAlerter: skymodules.NewAlerter("renter"), 1235 staticMux: mux, 1236 mu: siasync.New(modules.SafeMutexDelay, 1), 1237 staticTPool: tpool, 1238 } 1239 1240 // Set the balance target to 1SC 1241 // 1242 // TODO: check that the balance target makes sense in function of the 1243 // amount of MDM programs it can run with that amount of money 1244 if !deps.Disrupt("DisableFunding") { 1245 r.staticAccountBalanceTarget = types.SiacoinPrecision 1246 } 1247 1248 r.staticSkynetTUSUploader = newSkynetTUSUploader(r, tus) 1249 if err := r.tg.AfterStop(r.staticSkynetTUSUploader.Close); err != nil { 1250 return nil, err 1251 } 1252 r.staticUploadChunkDistributionQueue = newUploadChunkDistributionQueue(r) 1253 close(r.staticUploadHeap.pauseChan) 1254 1255 // Init the spending history. 1256 sh, err := NewSpendingHistory(r.persistDir, skymodules.SkynetSpendingHistoryFilename) 1257 if err != nil { 1258 return nil, err 1259 } 1260 r.staticSpendingHistory = sh 1261 1262 // Init the statsChan and close it right away to signal that no scan is 1263 // going on. 1264 r.statsChan = make(chan struct{}) 1265 close(r.statsChan) 1266 1267 // Initialize the loggers so that they are available for the components as 1268 // the components start up. 1269 r.staticLog, err = persist.NewFileLogger(filepath.Join(r.persistDir, logFile)) 1270 if err != nil { 1271 return nil, err 1272 } 1273 if err := r.tg.AfterStop(r.staticLog.Close); err != nil { 1274 return nil, err 1275 } 1276 r.staticRepairLog, err = persist.NewFileLogger(filepath.Join(r.persistDir, repairLogFile)) 1277 if err != nil { 1278 return nil, err 1279 } 1280 if err := r.tg.AfterStop(r.staticRepairLog.Close); err != nil { 1281 return nil, err 1282 } 1283 1284 // Initialize the dirUpdateBatcher. 1285 r.staticDirUpdateBatcher, err = r.newDirUpdateBatcher() 1286 if err != nil { 1287 return nil, errors.AddContext(err, "unable to create new health update batcher") 1288 } 1289 1290 // Initialize some of the components. 1291 err = r.newAccountManager() 1292 if err != nil { 1293 return nil, errors.AddContext(err, "unable to create account manager") 1294 } 1295 1296 r.staticRegistryMemoryManager = newMemoryManager(registryMemoryDefault, registryMemoryPriorityDefault, r.tg.StopChan()) 1297 r.staticUserUploadMemoryManager = newMemoryManager(userUploadMemoryDefault, userUploadMemoryPriorityDefault, r.tg.StopChan()) 1298 r.staticUserDownloadMemoryManager = newMemoryManager(userDownloadMemoryDefault, userDownloadMemoryPriorityDefault, r.tg.StopChan()) 1299 r.staticRepairMemoryManager = newMemoryManager(repairMemoryDefault, repairMemoryPriorityDefault, r.tg.StopChan()) 1300 1301 r.staticFuseManager = newFuseManager(r) 1302 r.staticStuckStack = callNewStuckStack() 1303 1304 // Add SkynetBlocklist 1305 sb, err := skynetblocklist.New(r.persistDir) 1306 if err != nil { 1307 return nil, errors.AddContext(err, "unable to create new skynet blocklist") 1308 } 1309 r.staticSkynetBlocklist = sb 1310 1311 // Add SkynetPortals 1312 sp, err := skynetportals.New(r.persistDir) 1313 if err != nil { 1314 return nil, errors.AddContext(err, "unable to create new skynet portal list") 1315 } 1316 r.staticSkynetPortals = sp 1317 1318 // Load all saved data. 1319 err = r.managedInitPersist() 1320 if err != nil { 1321 return nil, err 1322 } 1323 1324 // Init the download cache. 1325 lru, err := r.managedInitDownloadCache() 1326 if err != nil { 1327 return nil, err 1328 } 1329 1330 // Init stream buffer now that the stats are initialised. 1331 r.staticStreamBufferSet = newStreamBufferSet(r.staticStreamBufferStats, &r.tg, lru) 1332 1333 // Create the subscription manager and launch the thread that updates 1334 // the workers. This needs to be done before the creation of the 1335 // workerpool because workers need references to the subscription manager. 1336 r.staticSubscriptionManager = newSubscriptionManager(r) 1337 1338 // After persist is initialized, create the worker pool. 1339 r.staticWorkerPool = r.newWorkerPool() 1340 1341 // Now that the pool is created, start the background thread to update 1342 // the workers in the subscription manager. 1343 err = r.tg.Launch(r.staticSubscriptionManager.threadedUpdateWorkers) 1344 if err != nil { 1345 return nil, err 1346 } 1347 1348 // Set the worker pool on the contractor. 1349 r.staticHostContractor.UpdateWorkerPool(r.staticWorkerPool) 1350 1351 // Create the skykey manager. 1352 // In testing, keep the skykeys with the rest of the renter data. 1353 skykeyManDir := build.SkynetDir() 1354 if build.Release == "testing" { 1355 skykeyManDir = persistDir 1356 } 1357 r.staticSkykeyManager, err = skykey.NewSkykeyManager(skykeyManDir) 1358 if err != nil { 1359 return nil, err 1360 } 1361 1362 // Calculate the initial cached utilities and kick off a thread that updates 1363 // the utilities regularly. 1364 r.managedUpdateRenterContractsAndUtilities() 1365 go r.threadedUpdateRenterContractsAndUtilities() 1366 1367 // Launch the stat persisting thread. 1368 go r.threadedStatsPersister() 1369 1370 // Spin up background threads which are not depending on the renter being 1371 // up-to-date with consensus. 1372 if !r.staticDeps.Disrupt("DisableRepairAndHealthLoops") { 1373 // Push the root directory onto the directory heap for the repair process. 1374 err = r.managedPushUnexploredDirectory(skymodules.RootSiaPath()) 1375 if err != nil { 1376 return nil, err 1377 } 1378 go r.threadedHealthLoop() 1379 } 1380 1381 // If the spending history didn't exist before, manually init it with the 1382 // current spending. We don't want portals to pay a huge fee right after 1383 // upgrading for pre-skynet license spendings. 1384 _, lastSpendingTime := sh.LastSpending() 1385 if lastSpendingTime.IsZero() { 1386 var totalSpending types.Currency 1387 for _, c := range append(r.Contracts(), r.OldContracts()...) { 1388 totalSpending = totalSpending.Add(c.SkynetSpending()) 1389 } 1390 err = sh.AddSpending(totalSpending, []types.Transaction{}, time.Now()) 1391 if err != nil { 1392 return nil, errors.AddContext(err, "failed to add initial spending") 1393 } 1394 } 1395 1396 // Spin up the skynet fee paying goroutine. 1397 if err := r.tg.Launch(r.threadedPaySkynetFee); err != nil { 1398 return nil, err 1399 } 1400 1401 // Spin up the tus pruning goroutine. 1402 if err := r.tg.Launch(r.threadedPruneTUSUploads); err != nil { 1403 return nil, err 1404 } 1405 1406 // Unsubscribe on shutdown. 1407 err = r.tg.OnStop(func() error { 1408 cs.Unsubscribe(r) 1409 return nil 1410 }) 1411 if err != nil { 1412 return nil, err 1413 } 1414 return r, nil 1415 } 1416 1417 // renterAsyncStartup handles the non-blocking portion of NewCustomRenter. 1418 func renterAsyncStartup(r *Renter, cs modules.ConsensusSet) error { 1419 if r.staticDeps.Disrupt("BlockAsyncStartup") { 1420 return nil 1421 } 1422 // Subscribe to the consensus set in a separate goroutine. 1423 done := make(chan struct{}) 1424 defer close(done) 1425 err := cs.ConsensusSetSubscribe(r, modules.ConsensusChangeRecent, r.tg.StopChan()) 1426 if err != nil && strings.Contains(err.Error(), threadgroup.ErrStopped.Error()) { 1427 return err 1428 } 1429 if err != nil { 1430 return err 1431 } 1432 // Spin up the remaining background threads once we are caught up with the 1433 // consensus set. 1434 // Spin up the workers for the work pool. 1435 go r.threadedDownloadLoop() 1436 if !r.staticDeps.Disrupt("DisableRepairAndHealthLoops") { 1437 go r.threadedUploadAndRepair() 1438 go r.threadedStuckFileLoop() 1439 } 1440 // Spin up the snapshot synchronization thread. 1441 if !r.staticDeps.Disrupt("DisableSnapshotSync") { 1442 go r.threadedSynchronizeSnapshots() 1443 } 1444 return nil 1445 } 1446 1447 // threadedUpdateRenterContractsAndUtilities periodically calls 1448 // managedUpdateRenterContractsAndUtilities. 1449 func (r *Renter) threadedUpdateRenterContractsAndUtilities() { 1450 err := r.tg.Add() 1451 if err != nil { 1452 return 1453 } 1454 defer r.tg.Done() 1455 for { 1456 select { 1457 case <-r.tg.StopChan(): 1458 return 1459 case <-time.After(cachedUtilitiesUpdateInterval): 1460 } 1461 r.managedUpdateRenterContractsAndUtilities() 1462 } 1463 } 1464 1465 // NewCustomRenter initializes a renter and returns it. 1466 func NewCustomRenter(g modules.Gateway, cs modules.ConsensusSet, tpool modules.TransactionPool, hdb skymodules.HostDB, w modules.Wallet, hc hostContractor, mux *siamux.SiaMux, tus skymodules.SkynetTUSUploadStore, persistDir string, rl *ratelimit.RateLimit, deps skymodules.SkydDependencies) (*Renter, <-chan error) { 1467 errChan := make(chan error, 1) 1468 1469 // Blocking startup. 1470 r, err := renterBlockingStartup(g, cs, tpool, hdb, w, hc, mux, tus, persistDir, rl, deps) 1471 if err != nil { 1472 errChan <- err 1473 return nil, errChan 1474 } 1475 1476 // non-blocking startup 1477 go func() { 1478 defer close(errChan) 1479 if err := r.tg.Add(); err != nil { 1480 errChan <- err 1481 return 1482 } 1483 defer r.tg.Done() 1484 err := renterAsyncStartup(r, cs) 1485 if err != nil { 1486 errChan <- err 1487 } 1488 }() 1489 return r, errChan 1490 } 1491 1492 // New returns an initialized renter. 1493 func New(g modules.Gateway, cs modules.ConsensusSet, wallet modules.Wallet, tpool modules.TransactionPool, mux *siamux.SiaMux, tus skymodules.SkynetTUSUploadStore, rl *ratelimit.RateLimit, persistDir string) (*Renter, <-chan error) { 1494 errChan := make(chan error, 1) 1495 hdb, errChanHDB := hostdb.New(g, cs, tpool, mux, persistDir) 1496 if err := modules.PeekErr(errChanHDB); err != nil { 1497 errChan <- err 1498 return nil, errChan 1499 } 1500 hc, errChanContractor := contractor.New(cs, wallet, tpool, hdb, rl, persistDir) 1501 if err := modules.PeekErr(errChanContractor); err != nil { 1502 errChan <- err 1503 return nil, errChan 1504 } 1505 renter, errChanRenter := NewCustomRenter(g, cs, tpool, hdb, wallet, hc, mux, tus, persistDir, rl, skymodules.SkydProdDependencies) 1506 if err := modules.PeekErr(errChanRenter); err != nil { 1507 errChan <- err 1508 return nil, errChan 1509 } 1510 go func() { 1511 errChan <- errors.Compose(<-errChanHDB, <-errChanContractor, <-errChanRenter) 1512 close(errChan) 1513 }() 1514 return renter, errChan 1515 } 1516 1517 // HostsForRegistryUpdate returns a list of hosts that the renter would be using 1518 // for updating the registry. 1519 func (r *Renter) HostsForRegistryUpdate() ([]skymodules.HostForRegistryUpdate, error) { 1520 if err := r.tg.Add(); err != nil { 1521 return nil, err 1522 } 1523 defer r.tg.Done() 1524 1525 var hosts []skymodules.HostForRegistryUpdate 1526 for _, w := range r.staticWorkerPool.callWorkers() { 1527 if !isWorkerGoodForRegistryUpdate(w) { 1528 continue 1529 } 1530 hosts = append(hosts, skymodules.HostForRegistryUpdate{ 1531 Pubkey: w.staticHostPubKey, 1532 }) 1533 } 1534 return hosts, nil 1535 } 1536 1537 // managedInitDownloadCache initialises the download cache using either defaults 1538 // or the values provided by environment variables. 1539 func (r *Renter) managedInitDownloadCache() (PersistedLRU, error) { 1540 // Check if the cache is disabled. 1541 enabled, err := build.DiskCacheEnabled() 1542 if err != nil { 1543 return nil, err 1544 } 1545 if !enabled { 1546 return newNoOpLRU(), nil 1547 } 1548 // Get the max size of the cache. 1549 cacheSize, set, err := build.MaxDownloadDiskCacheSize() 1550 if err != nil { 1551 return nil, err 1552 } 1553 if set { 1554 fmt.Printf("Setting download cache size to %v\n", cacheSize) 1555 } else { 1556 cacheSize = defaultCacheSize 1557 } 1558 // Get the min hits. 1559 minHits, set, err := build.MinDownloadDiskCacheHits() 1560 if err != nil { 1561 return nil, err 1562 } 1563 if set { 1564 fmt.Printf("Setting min download cache hits to %v\n", minHits) 1565 } else { 1566 minHits = defaultMinCacheHits 1567 } 1568 // Get the cache period. 1569 cachePeriod, set, err := build.DownloadDiskCacheHitDuration() 1570 if err != nil { 1571 return nil, err 1572 } 1573 if set { 1574 fmt.Printf("Setting download cache hit period to %v\n", cachePeriod) 1575 } else { 1576 cachePeriod = defaultCachePeriod 1577 } 1578 // Create LRU. 1579 lru, err := newPersistedLRU(filepath.Join(r.persistDir, OnDiskCacheFolderName), cacheSize, minHits, cachePeriod) 1580 if err != nil { 1581 return nil, err 1582 } 1583 // Stop it upon shutdown. 1584 err = r.tg.OnStop(lru.Close) 1585 if err != nil { 1586 return nil, errors.Compose(err, lru.Close()) 1587 } 1588 return lru, nil 1589 }