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  }