func validateSiapath(siapath string) error {
	if siapath == "" {
		return ErrEmptyFilename
	}
	if siapath == ".." {
		return errors.New("siapath cannot be '..'")
	}
	if siapath == "." {
		return errors.New("siapath cannot be '.'")
	}
	if strings.HasPrefix(siapath, "/") {
		return errors.New("siapath cannot begin with /")
	}
	if strings.HasPrefix(siapath, "../") {
		return errors.New("siapath cannot begin with ../")
	}
	if strings.HasPrefix(siapath, "./") {
		return errors.New("siapath connot begin with ./")
	}
	for _, pathElem := range strings.Split(siapath, "/") {
		if pathElem == "." || pathElem == ".." {
			return errors.New("siapath cannot contain . or .. elements")
		}
	}
	return nil
}
validateSiapath checks that a Siapath is a legal filename.
../ is disallowed to prevent directory traversal, and paths must not begin
with / or be empty.
package renter
import (
	"errors"
	"reflect"
	"strings"
	"sync"
	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/modules/renter/contractor"
	"github.com/NebulousLabs/Sia/modules/renter/hostdb"
	"github.com/NebulousLabs/Sia/persist"
	siasync "github.com/NebulousLabs/Sia/sync"
	"github.com/NebulousLabs/Sia/types"
	"github.com/NebulousLabs/threadgroup"
)
var (
	errNilContractor = errors.New("cannot create renter with nil contractor")
	errNilCS         = errors.New("cannot create renter with nil consensus set")
	errNilGateway    = errors.New("cannot create hostdb with nil gateway")
	errNilHdb        = errors.New("cannot create renter with nil hostdb")
	errNilTpool      = errors.New("cannot create renter with nil transaction pool")
)
var (
	priceEstimationScope = build.Select(build.Var{
		Standard: int(50),
		Dev:      int(12),
		Testing:  int(4),
	}).(int)
)
type hostDB interface {
	ActiveHosts() []modules.HostDBEntry
	AllHosts() []modules.HostDBEntry
	AverageContractPrice() types.Currency
	Close() error
	Host(types.SiaPublicKey) (modules.HostDBEntry, bool)
	InitialScanComplete() (bool, error)
	RandomHosts(int, []types.SiaPublicKey) ([]modules.HostDBEntry, error)
	ScoreBreakdown(modules.HostDBEntry) modules.HostScoreBreakdown
	EstimateHostScore(modules.HostDBEntry) modules.HostScoreBreakdown
}
type hostContractor interface {
	SetAllowance(modules.Allowance) error
	Allowance() modules.Allowance
	Close() error
	Contracts() []modules.RenterContract
	OldContracts() []modules.RenterContract
	ContractByPublicKey(types.SiaPublicKey) (modules.RenterContract, bool)
	ContractUtility(types.SiaPublicKey) (modules.ContractUtility, bool)
	CurrentPeriod() types.BlockHeight
	PeriodSpending() modules.ContractorSpending
	Editor(types.SiaPublicKey, <-chan struct{}) (contractor.Editor, error)
	IsOffline(types.SiaPublicKey) bool
	Downloader(types.SiaPublicKey, <-chan struct{}) (contractor.Downloader, error)
	ResolveIDToPubKey(types.FileContractID) types.SiaPublicKey
	RateLimits() (readBPS int64, writeBPS int64, packetSize uint64)
	SetRateLimits(int64, int64, uint64)
}
type trackedFile struct {
	RepairPath string
}
type Renter struct {
	files map[string]*file
	downloadHeapMu sync.Mutex          
	downloadHeap   *downloadChunkHeap  
	newDownloads   chan struct{}       
	downloadHistory   []*download
	downloadHistoryMu sync.Mutex
	uploadHeap uploadHeap
	memoryManager *memoryManager
	workerPool    map[types.FileContractID]*worker
	lastEstimation modules.RenterPriceEstimation
	staticStreamCache *streamCache
	cs                modules.ConsensusSet
	deps              modules.Dependencies
	g                 modules.Gateway
	hostContractor    hostContractor
	hostDB            hostDB
	log               *persist.Logger
	persist           persistence
	persistDir        string
	mu                *siasync.RWMutex
	tg                threadgroup.ThreadGroup
	tpool             modules.TransactionPool
}
func (r *Renter) Close() error {
	r.tg.Stop()
	r.hostDB.Close()
	return r.hostContractor.Close()
}
func (r *Renter) PriceEstimation() modules.RenterPriceEstimation {
	id := r.mu.RLock()
	lastEstimation := r.lastEstimation
	r.mu.RUnlock(id)
	if !reflect.DeepEqual(lastEstimation, modules.RenterPriceEstimation{}) {
		return lastEstimation
	}
	hosts, err := r.hostDB.RandomHosts(priceEstimationScope, nil)
	if err != nil {
		return modules.RenterPriceEstimation{}
	}
	if len(hosts) == 0 {
		return modules.RenterPriceEstimation{}
	}
	var totalContractCost types.Currency
	var totalDownloadCost types.Currency
	var totalStorageCost types.Currency
	var totalUploadCost types.Currency
	for _, host := range hosts {
		totalContractCost = totalContractCost.Add(host.ContractPrice)
		totalDownloadCost = totalDownloadCost.Add(host.DownloadBandwidthPrice)
		totalStorageCost = totalStorageCost.Add(host.StoragePrice)
		totalUploadCost = totalUploadCost.Add(host.UploadBandwidthPrice)
	}
	totalDownloadCost = totalDownloadCost.Mul(modules.BytesPerTerabyte)
	totalStorageCost = totalStorageCost.Mul(modules.BlockBytesPerMonthTerabyte)
	totalUploadCost = totalUploadCost.Mul(modules.BytesPerTerabyte)
	totalStorageCost = totalStorageCost.Mul64(3)  
	totalUploadCost = totalUploadCost.Mul64(3)    
	totalContractCost = totalContractCost.Div64(uint64(len(hosts)))
	totalDownloadCost = totalDownloadCost.Div64(uint64(len(hosts)))
	totalStorageCost = totalStorageCost.Div64(uint64(len(hosts)))
	totalUploadCost = totalUploadCost.Div64(uint64(len(hosts)))
	totalContractCost = totalContractCost.Mul64(uint64(priceEstimationScope))
	_, feePerByte := r.tpool.FeeEstimation()
	totalContractCost = totalContractCost.Add(feePerByte.Mul64(1000).Mul64(uint64(priceEstimationScope)))
	est := modules.RenterPriceEstimation{
		FormContracts:        totalContractCost,
		DownloadTerabyte:     totalDownloadCost,
		StorageTerabyteMonth: totalStorageCost,
		UploadTerabyte:       totalUploadCost,
	}
	id = r.mu.Lock()
	r.lastEstimation = est
	r.mu.Unlock(id)
	return est
}
func (r *Renter) setBandwidthLimits(downloadSpeed int64, uploadSpeed int64) error {
	if downloadSpeed < 0 || uploadSpeed < 0 {
		return errors.New("download/upload rate limit can't be below 0")
	}
	if downloadSpeed == 0 && uploadSpeed == 0 {
		r.hostContractor.SetRateLimits(0, 0, 0)
	} else {
		r.hostContractor.SetRateLimits(downloadSpeed, uploadSpeed, 4*4096)
	}
	return nil
}
func (r *Renter) SetSettings(s modules.RenterSettings) error {
	if s.MaxDownloadSpeed < 0 || s.MaxUploadSpeed < 0 {
		return errors.New("bandwidth limits cannot be negative")
	}
	if s.StreamCacheSize <= 0 {
		return errors.New("stream cache size needs to be 1 or larger")
	}
	err := r.hostContractor.SetAllowance(s.Allowance)
	if err != nil {
		return err
	}
	err = r.setBandwidthLimits(s.MaxDownloadSpeed, s.MaxUploadSpeed)
	if err != nil {
		return err
	}
	r.persist.MaxDownloadSpeed = s.MaxDownloadSpeed
	r.persist.MaxUploadSpeed = s.MaxUploadSpeed
	err = r.staticStreamCache.SetStreamingCacheSize(s.StreamCacheSize)
	if err != nil {
		return err
	}
	r.persist.StreamCacheSize = s.StreamCacheSize
	err = r.saveSync()
	if err != nil {
		return err
	}
	r.managedUpdateWorkerPool()
	return nil
}
func (r *Renter) ActiveHosts() []modules.HostDBEntry { return r.hostDB.ActiveHosts() }
func (r *Renter) AllHosts() []modules.HostDBEntry { return r.hostDB.AllHosts() }
func (r *Renter) Host(spk types.SiaPublicKey) (modules.HostDBEntry, bool) { return r.hostDB.Host(spk) }
func (r *Renter) InitialScanComplete() (bool, error) { return r.hostDB.InitialScanComplete() }
func (r *Renter) ScoreBreakdown(e modules.HostDBEntry) modules.HostScoreBreakdown {
	return r.hostDB.ScoreBreakdown(e)
}
func (r *Renter) EstimateHostScore(e modules.HostDBEntry) modules.HostScoreBreakdown {
	return r.hostDB.EstimateHostScore(e)
}
func (r *Renter) Contracts() []modules.RenterContract { return r.hostContractor.Contracts() }
func (r *Renter) OldContracts() []modules.RenterContract {
	return r.hostContractor.OldContracts()
}
func (r *Renter) CurrentPeriod() types.BlockHeight { return r.hostContractor.CurrentPeriod() }
func (r *Renter) ContractUtility(pk types.SiaPublicKey) (modules.ContractUtility, bool) {
	return r.hostContractor.ContractUtility(pk)
}
func (r *Renter) PeriodSpending() modules.ContractorSpending { return r.hostContractor.PeriodSpending() }
func (r *Renter) Settings() modules.RenterSettings {
	download, upload, _ := r.hostContractor.RateLimits()
	return modules.RenterSettings{
		Allowance:        r.hostContractor.Allowance(),
		MaxDownloadSpeed: download,
		MaxUploadSpeed:   upload,
		StreamCacheSize:  r.staticStreamCache.cacheSize,
	}
}
func (r *Renter) ProcessConsensusChange(cc modules.ConsensusChange) {
	id := r.mu.Lock()
	r.lastEstimation = modules.RenterPriceEstimation{}
	r.mu.Unlock(id)
}