func (hdb *HostDB) managedScanHost(entry modules.HostDBEntry) {
	netAddr := entry.NetAddress
	pubKey := entry.PublicKey
	hdb.log.Debugf("Scanning host %v at %v", pubKey, netAddr)
	hdb.mu.RLock()
	updateHostHistoricInteractions(&entry, hdb.blockHeight)
	hdb.mu.RUnlock()
	var settings modules.HostExternalSettings
	var latency time.Duration
	err := func() error {
		timeout := hostRequestTimeout
		hdb.mu.RLock()
		if len(hdb.initialScanLatencies) > minScansForSpeedup {
			build.Critical("initialScanLatencies should never be greater than minScansForSpeedup")
		}
		if !hdb.initialScanComplete && len(hdb.initialScanLatencies) == minScansForSpeedup {
			timeout = hdb.initialScanLatencies[len(hdb.initialScanLatencies)/2]
			timeout *= scanSpeedupMedianMultiplier
			if hostRequestTimeout < timeout {
				timeout = hostRequestTimeout
			}
		}
		hdb.mu.RUnlock()
		dialer := &net.Dialer{
			Cancel:  hdb.tg.StopChan(),
			Timeout: timeout,
		}
		start := time.Now()
		conn, err := dialer.Dial("tcp", string(netAddr))
		latency = time.Since(start)
		if err != nil {
			return err
		}
		connCloseChan := make(chan struct{})
		go func() {
			select {
			case <-hdb.tg.StopChan():
			case <-connCloseChan:
			}
			conn.Close()
		}()
		defer close(connCloseChan)
		conn.SetDeadline(time.Now().Add(hostScanDeadline))
		err = encoding.WriteObject(conn, modules.RPCSettings)
		if err != nil {
			return err
		}
		var pubkey crypto.PublicKey
		copy(pubkey[:], pubKey.Key)
		return crypto.ReadSignedObject(conn, &settings, maxSettingsLen, pubkey)
	}()
	if err != nil {
		hdb.log.Debugf("Scan of host at %v failed: %v", netAddr, err)
	} else {
		hdb.log.Debugf("Scan of host at %v succeeded.", netAddr)
		entry.HostExternalSettings = settings
	}
	success := err == nil
	hdb.mu.Lock()
	defer hdb.mu.Unlock()
	hdb.updateEntry(entry, err)
	if success && len(hdb.initialScanLatencies) < minScansForSpeedup {
		hdb.initialScanLatencies = append(hdb.initialScanLatencies, latency)
		if len(hdb.initialScanLatencies) == minScansForSpeedup {
			sort.Slice(hdb.initialScanLatencies, func(i, j int) bool {
				return hdb.initialScanLatencies[i] < hdb.initialScanLatencies[j]
			})
		}
	}
}
managedScanHost will connect to a host and grab the settings, verifying
uptime and updating to the host's preferences.
package hostdb
import (
	"net"
	"sort"
	"time"
	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/crypto"
	"github.com/NebulousLabs/Sia/encoding"
	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/fastrand"
)
func (hdb *HostDB) queueScan(entry modules.HostDBEntry) {
	_, exists := hdb.scanMap[entry.PublicKey.String()]
	if exists {
		return
	}
	hdb.scanMap[entry.PublicKey.String()] = struct{}{}
	hdb.scanList = append(hdb.scanList, entry)
	if len(hdb.scanList) > 1 {
		i := len(hdb.scanList) - 1
		j := fastrand.Intn(i)
		hdb.scanList[i], hdb.scanList[j] = hdb.scanList[j], hdb.scanList[i]
	}
	if hdb.scanWait {
		return
	}
	if build.DEBUG && len(hdb.scanMap) > len(hdb.scanList)+maxScanningThreads {
		hdb.log.Critical("The hostdb scan map has seemingly grown too large:", len(hdb.scanMap), len(hdb.scanList), maxScanningThreads)
	}
	hdb.scanWait = true
	go func() {
		scanPool := make(chan modules.HostDBEntry)
		defer close(scanPool)
		if hdb.tg.Add() != nil {
			return
		}
		defer hdb.tg.Done()
		hdb.deps.Disrupt("BlockScan")
		starterThread := false
		for {
			hdb.mu.Lock()
			if len(hdb.scanList) == 0 {
				hdb.scanWait = false
				hdb.mu.Unlock()
				return
			}
			entry := hdb.scanList[0]
			hdb.scanList = hdb.scanList[1:]
			delete(hdb.scanMap, entry.PublicKey.String())
			scansRemaining := len(hdb.scanList)
			recentEntry, exists := hdb.hostTree.Select(entry.PublicKey)
			if exists {
				entry = recentEntry
			}
			select {
			case scanPool <- entry:
				hdb.log.Debugf("Sending host %v for scan, %v hosts remain", entry.PublicKey.String(), scansRemaining)
				hdb.mu.Unlock()
				continue
			default:
			}
			if hdb.scanningThreads < maxScanningThreads || !starterThread {
				starterThread = true
				hdb.scanningThreads++
				if err := hdb.tg.Add(); err != nil {
					hdb.mu.Unlock()
					return
				}
				go func() {
					defer hdb.tg.Done()
					hdb.threadedProbeHosts(scanPool)
					hdb.mu.Lock()
					hdb.scanningThreads--
					hdb.mu.Unlock()
				}()
			}
			hdb.mu.Unlock()
			hdb.log.Debugf("Sending host %v for scan, %v hosts remain", entry.PublicKey.String(), scansRemaining)
			select {
			case scanPool <- entry:
			case <-hdb.tg.StopChan():
				return
			}
		}
	}()
}
func (hdb *HostDB) updateEntry(entry modules.HostDBEntry, netErr error) {
	if netErr != nil && !hdb.gateway.Online() {
		return
	}
	newEntry, exists := hdb.hostTree.Select(entry.PublicKey)
	if exists {
		newEntry.HostExternalSettings = entry.HostExternalSettings
	} else {
		newEntry = entry
	}
	if netErr == nil {
		newEntry.RecentSuccessfulInteractions++
	} else {
		newEntry.RecentFailedInteractions++
	}
	if len(newEntry.ScanHistory) < 2 {
		earliestStartTime := time.Now().Add(time.Hour * 7 * 24 * -1)                                                    
		suggestedStartTime := time.Now().Add(time.Minute * 10 * time.Duration(hdb.blockHeight-entry.FirstSeen+1) * -1)  
		if suggestedStartTime.Before(earliestStartTime) {
			suggestedStartTime = earliestStartTime
		}
		newEntry.ScanHistory = modules.HostDBScans{
			{Timestamp: suggestedStartTime, Success: netErr == nil},
			{Timestamp: time.Now(), Success: netErr == nil},
		}
	} else {
		if newEntry.ScanHistory[len(newEntry.ScanHistory)-1].Success && netErr != nil {
			hdb.log.Debugf("Host %v is being downgraded from an online host to an offline host: %v\n", newEntry.PublicKey.String(), netErr)
		}
		newTimestamp := time.Now()
		prevTimestamp := newEntry.ScanHistory[len(newEntry.ScanHistory)-1].Timestamp
		if !newTimestamp.After(prevTimestamp) {
			newTimestamp = prevTimestamp.Add(time.Second)
		}
		newEntry.ScanHistory = append(newEntry.ScanHistory, modules.HostDBScan{Timestamp: newTimestamp, Success: netErr == nil})
	}
	var recentUptime bool
	for _, scan := range newEntry.ScanHistory {
		if scan.Success {
			recentUptime = true
		}
	}
	if time.Now().Sub(newEntry.ScanHistory[0].Timestamp) > maxHostDowntime && !recentUptime && len(newEntry.ScanHistory) >= minScans {
		err := hdb.hostTree.Remove(newEntry.PublicKey)
		if err != nil {
			hdb.log.Println("ERROR: unable to remove host newEntry which has had a ton of downtime:", err)
		}
		return
	}
	for len(newEntry.ScanHistory) > minScans && time.Now().Sub(newEntry.ScanHistory[0].Timestamp) > maxHostDowntime {
		timePassed := newEntry.ScanHistory[1].Timestamp.Sub(newEntry.ScanHistory[0].Timestamp)
		if newEntry.ScanHistory[0].Success {
			newEntry.HistoricUptime += timePassed
		} else {
			newEntry.HistoricDowntime += timePassed
		}
		newEntry.ScanHistory = newEntry.ScanHistory[1:]
	}
	if !exists {
		err := hdb.hostTree.Insert(newEntry)
		if err != nil {
			hdb.log.Println("ERROR: unable to insert entry which is was thought to be new:", err)
		} else {
			hdb.log.Debugf("Adding host %v to the hostdb. Net error: %v\n", newEntry.PublicKey.String(), netErr)
		}
	} else {
		err := hdb.hostTree.Modify(newEntry)
		if err != nil {
			hdb.log.Println("ERROR: unable to modify entry which is thought to exist:", err)
		} else {
			hdb.log.Debugf("Adding host %v to the hostdb. Net error: %v\n", newEntry.PublicKey.String(), netErr)
		}
	}
}