Commit 5a799ac5 authored by Luke Champine's avatar Luke Champine Committed by GitHub

Merge pull request #2152 from NebulousLabs/host-interactivity

add host interaction weight to the hostdb
parents 696d403f a318c47d
......@@ -630,6 +630,7 @@ overall.
"ageadjustment": 0.1234,
"burnadjustment": 0.1234,
"collateraladjustment": 23.456,
"interactionadjustment": 0.1234,
"priceadjustment": 0.1234,
"storageremainingadjustment": 0.1234,
"uptimeadjustment": 0.1234,
......
......@@ -277,6 +277,14 @@ overall.
// a point it can be detrimental.
"collateraladjustment": 23.456,
// The multipler that gets applied to a host based on previous interactions
// with the host. A high ratio of successful interactions will improve this
// hosts score, and a high ratio of failed interactions will hurt this
// hosts score. This adjustment helps account for hosts that are on
// unstable connections, don't keep their wallets unlocked, ran out of
// funds, etc.
"interactionadjustment": 0.1234,
// The multiplier that gets applied to a host based on the host's price.
// Lower prices are almost always better. Below a certain, very low price,
// there is no advantage.
......
......@@ -98,10 +98,10 @@ type HostDBEntry struct {
HistoricUptime time.Duration `json:"historicuptime"`
ScanHistory HostDBScans `json:"scanhistory"`
HistoricSuccessfulInteractions uint64 `json:"historicSuccessfulInteractions"`
HistoricFailedInteractions uint64 `json:"historicFailedInteractions"`
RecentSuccessfulInteractions uint64 `json:"recentSuccessfulInteractions"`
RecentFailedInteractions uint64 `json:"recentFailedInteractions"`
HistoricFailedInteractions float64 `json:"historicfailedinteractions"`
HistoricSuccessfulInteractions float64 `json:"historicsuccessfulinteractions"`
RecentFailedInteractions float64 `json:"recentfailedinteractions"`
RecentSuccessfulInteractions float64 `json:"recentsuccessfulinteractions"`
LastHistoricUpdate types.BlockHeight
......@@ -130,6 +130,7 @@ type HostScoreBreakdown struct {
AgeAdjustment float64 `json:"ageadjustment"`
BurnAdjustment float64 `json:"burnadjustment"`
CollateralAdjustment float64 `json:"collateraladjustment"`
InteractionAdjustment float64 `json:"interactionadjustment"`
PriceAdjustment float64 `json:"pricesmultiplier"`
StorageRemainingAdjustment float64 `json:"storageremainingadjustment"`
UptimeAdjustment float64 `json:"uptimeadjustment"`
......
......@@ -7,32 +7,45 @@ import (
)
const (
// historicInteractionDecay defines the decay of the HistoricSuccessfulInteractions
// and HistoricFailedInteractions after every block for a host entry.
historicInteractionDecay = 0.9995
// historicInteractionDecalLimit defines the number of historic
// interactions required before decay is applied.
historicInteractionDecayLimit = 500
// hostRequestTimeout indicates how long a host has to respond to a dial.
hostRequestTimeout = 2 * time.Minute
// hostScanDeadline indicates how long a host has to complete an entire
// scan.
hostScanDeadline = 4 * time.Minute
// maxHostDowntime specifies the maximum amount of time that a host is
// allowed to be offline while still being in the hostdb.
maxHostDowntime = 10 * 24 * time.Hour
// minScans specifies the number of scans that a host should have before the
// scans start getting compressed.
minScans = 12
// maxSettingsLen indicates how long in bytes the host settings field is
// allowed to be before being ignored as a DoS attempt.
maxSettingsLen = 10e3
// hostRequestTimeout indicates how long a host has to respond to a dial.
hostRequestTimeout = 2 * time.Minute
// minScans specifies the number of scans that a host should have before the
// scans start getting compressed.
minScans = 12
// hostScanDeadline indicates how long a host has to complete an entire
// scan.
hostScanDeadline = 4 * time.Minute
// recentInteractionWeightLimit caps the number of recent interactions as a
// percentage of the historic interactions, to be certain that a large
// amount of activity in a short period of time does not overwhelm the
// score for a host.
//
// Non-stop heavy interactions for half a day can result in gaining more
// than half the total weight at this limit.
recentInteractionWeightLimit = 0.01
// saveFrequency defines how frequently the hostdb will save to disk. Hostdb
// will also save immediately prior to shutdown.
saveFrequency = 2 * time.Minute
// historicInteractionDecay defines the decay of the HistoricSuccessfulInteractions
// and HistoricFailedInteractions after every block
historicInteractionDecay = 0.999
)
var (
......
......@@ -7,7 +7,6 @@ package hostdb
import (
"errors"
"fmt"
"math"
"os"
"path/filepath"
"sync"
......@@ -184,50 +183,6 @@ func newHostDB(g modules.Gateway, cs modules.ConsensusSet, persistDir string, de
return hdb, nil
}
// updateHostDBEntry updates a HostDBEntries's historic interactions if more
// than one block passed since the last update. This should be called every time
// before the recent interactions are updated. if passedTime is e.g. 10, this
// means that the recent interactions were updated 10 blocks ago but never
// since. So we need to apply the decay of 1 block before we append the recent
// interactions from 10 blocks ago and then apply the decay of 9 more blocks in
// which the recent interactions have been 0
func updateHostHistoricInteractions(host *modules.HostDBEntry, bh types.BlockHeight) {
passedTime := bh - host.LastHistoricUpdate
if passedTime == 0 {
// no time passed. nothing to do.
return
}
// tmp float64 values for more accurate decay
hsi := float64(host.HistoricSuccessfulInteractions)
hfi := float64(host.HistoricFailedInteractions)
// Apply the decay of a single block
decay := historicInteractionDecay
hsi *= decay
hfi *= decay
// Apply the recent interactions of that single block
hsi += float64(host.RecentSuccessfulInteractions)
hfi += float64(host.RecentFailedInteractions)
// Apply the decay of the rest of the blocks
if passedTime > 1 {
decay = math.Pow(historicInteractionDecay, float64(passedTime-1))
hsi *= decay
hfi *= decay
}
// Set new values
host.HistoricSuccessfulInteractions = uint64(hsi)
host.HistoricFailedInteractions = uint64(hfi)
host.RecentSuccessfulInteractions = 0
host.RecentFailedInteractions = 0
// Update the time of the last update
host.LastHistoricUpdate = bh
}
// ActiveHosts returns a list of hosts that are currently online, sorted by
// weight.
func (hdb *HostDB) ActiveHosts() (activeHosts []modules.HostDBEntry) {
......@@ -274,7 +229,14 @@ func (hdb *HostDB) Close() error {
// Host returns the HostSettings associated with the specified NetAddress. If
// no matching host is found, Host returns false.
func (hdb *HostDB) Host(spk types.SiaPublicKey) (modules.HostDBEntry, bool) {
return hdb.hostTree.Select(spk)
host, exists := hdb.hostTree.Select(spk)
if !exists {
return host, exists
}
hdb.mu.RLock()
updateHostHistoricInteractions(&host, hdb.blockHeight)
hdb.mu.RUnlock()
return host, exists
}
// RandomHosts implements the HostDB interface's RandomHosts() method. It takes
......@@ -283,44 +245,3 @@ func (hdb *HostDB) Host(spk types.SiaPublicKey) (modules.HostDBEntry, bool) {
func (hdb *HostDB) RandomHosts(n int, excludeKeys []types.SiaPublicKey) []modules.HostDBEntry {
return hdb.hostTree.SelectRandom(n, excludeKeys)
}
// IncrementSuccessfulInteractions increments the number of successful
// interactions with a host for a given key
func (hdb *HostDB) IncrementSuccessfulInteractions(key types.SiaPublicKey) {
hdb.mu.Lock()
defer hdb.mu.Unlock()
// Fetch the host.
host, haveHost := hdb.hostTree.Select(key)
if !haveHost {
return
}
// Update historic values if necessary
updateHostHistoricInteractions(&host, hdb.blockHeight)
// Increment the successful interactions
host.RecentSuccessfulInteractions++
hdb.hostTree.Modify(host)
}
// IncrementFailedInteractions increments the number of failed interactions with
// a host for a given key
func (hdb *HostDB) IncrementFailedInteractions(key types.SiaPublicKey) {
hdb.mu.Lock()
defer hdb.mu.Unlock()
// Fetch the host.
host, haveHost := hdb.hostTree.Select(key)
if !haveHost || !hdb.online {
// If we are offline it probably wasn't the host's fault
return
}
// Update historic values if necessary
updateHostHistoricInteractions(&host, hdb.blockHeight)
// Increment the failed interactions
host.RecentFailedInteractions++
hdb.hostTree.Modify(host)
}
......@@ -435,8 +435,8 @@ func TestUpdateHistoricInteractions(t *testing.T) {
}
// increment successful and failed interactions by 100
interactions := uint64(100)
for i := uint64(0); i < interactions; i++ {
interactions := 100.0
for i := 0.0; i < interactions; i++ {
hdbt.hdb.IncrementSuccessfulInteractions(host.PublicKey)
hdbt.hdb.IncrementFailedInteractions(host.PublicKey)
}
......@@ -458,10 +458,13 @@ func TestUpdateHistoricInteractions(t *testing.T) {
}
// add single block to consensus
hdbt.miner.AddBlock()
_, err = hdbt.miner.AddBlock()
if err != nil {
t.Fatal(err)
}
// increment interactions again by 100
for i := uint64(0); i < interactions; i++ {
for i := 0.0; i < interactions; i++ {
hdbt.hdb.IncrementSuccessfulInteractions(host.PublicKey)
hdbt.hdb.IncrementFailedInteractions(host.PublicKey)
}
......@@ -472,24 +475,34 @@ func TestUpdateHistoricInteractions(t *testing.T) {
t.Fatal("Modified host not found in hostdb")
}
// check that recent interactions are exactly 100 again and historic interactions are also exactly 100
// historic actions should have incremented slightly, due to the clamp the
// full interactions should not have made it into the historic group.
if host.RecentFailedInteractions != interactions || host.RecentSuccessfulInteractions != interactions {
t.Errorf("Interactions should be %v but were %v and %v", interactions,
host.RecentFailedInteractions, host.RecentSuccessfulInteractions)
}
if host.HistoricFailedInteractions != interactions || host.HistoricSuccessfulInteractions != interactions {
t.Errorf("Historic Interactions should be %v but were %v and %v", interactions,
host.HistoricFailedInteractions, host.HistoricSuccessfulInteractions)
if host.HistoricFailedInteractions == 0 || host.HistoricSuccessfulInteractions == 0 {
t.Error("historic actions should have updated")
}
// add 10 blocks to consensus
for i := 0; i < 10; i++ {
hdbt.miner.AddBlock()
// add 200 blocks to consensus, adding large numbers of historic actions
// each time, so that the clamp does not need to be in effect anymore.
for i := 0; i < 200; i++ {
for j := uint64(0); j < 10; j++ {
hdbt.hdb.IncrementSuccessfulInteractions(host.PublicKey)
hdbt.hdb.IncrementFailedInteractions(host.PublicKey)
}
_, err = hdbt.miner.AddBlock()
if err != nil {
t.Fatal(err)
}
}
// add a single interaction
hdbt.hdb.IncrementSuccessfulInteractions(host.PublicKey)
hdbt.hdb.IncrementFailedInteractions(host.PublicKey)
// Add five interactions
for i := 0; i < 5; i++ {
hdbt.hdb.IncrementSuccessfulInteractions(host.PublicKey)
hdbt.hdb.IncrementFailedInteractions(host.PublicKey)
}
// get updated host from hostdb
host, ok = hdbt.hdb.Host(host.PublicKey)
......@@ -497,14 +510,49 @@ func TestUpdateHistoricInteractions(t *testing.T) {
t.Fatal("Modified host not found in hostdb")
}
// check that recent interactions are exactly 1 and historic interactions are (100*0.997 + 100)*0.997^9
if host.RecentFailedInteractions != 1 || host.RecentSuccessfulInteractions != 1 {
// check that recent interactions are exactly 5. Save the historic actions
// to check that decay is being handled correctly, and that the recent
// interactions are moved over correctly.
if host.RecentFailedInteractions != 5 || host.RecentSuccessfulInteractions != 5 {
t.Errorf("Interactions should be %v but were %v and %v", interactions,
host.RecentFailedInteractions, host.RecentSuccessfulInteractions)
}
historicFailed := host.HistoricFailedInteractions
if host.HistoricFailedInteractions != host.HistoricSuccessfulInteractions {
t.Error("historic failed and successful should have the same values")
}
// Those two lines need to be split because
expected := uint64((100.0*historicInteractionDecay + 100.0) * math.Pow(historicInteractionDecay, 9))
// Add a single block to apply one round of decay.
_, err = hdbt.miner.AddBlock()
if err != nil {
t.Fatal(err)
}
host, ok = hdbt.hdb.Host(host.PublicKey)
if !ok {
t.Fatal("Modified host not found in hostdb")
}
// Get the historic successful and failed interactions, and see that they
// are decaying properly.
expected := historicFailed*math.Pow(historicInteractionDecay, 1) + 5
if host.HistoricFailedInteractions != expected || host.HistoricSuccessfulInteractions != expected {
t.Errorf("Historic Interactions should be %v but were %v and %v", expected,
host.HistoricFailedInteractions, host.HistoricSuccessfulInteractions)
}
// Add 10 more blocks and check the decay again, make sure it's being
// applied correctly.
for i := 0; i < 10; i++ {
_, err := hdbt.miner.AddBlock()
if err != nil {
t.Fatal(err)
}
}
host, ok = hdbt.hdb.Host(host.PublicKey)
if !ok {
t.Fatal("Modified host not found in hostdb")
}
expected = expected * math.Pow(historicInteractionDecay, 10)
if host.HistoricFailedInteractions != expected || host.HistoricSuccessfulInteractions != expected {
t.Errorf("Historic Interactions should be %v but were %v and %v", expected,
host.HistoricFailedInteractions, host.HistoricSuccessfulInteractions)
......
package hostdb
import (
"math"
"github.com/NebulousLabs/Sia/modules"
"github.com/NebulousLabs/Sia/types"
)
// updateHostDBEntry updates a HostDBEntries's historic interactions if more
// than one block passed since the last update. This should be called every time
// before the recent interactions are updated. if passedTime is e.g. 10, this
// means that the recent interactions were updated 10 blocks ago but never
// since. So we need to apply the decay of 1 block before we append the recent
// interactions from 10 blocks ago and then apply the decay of 9 more blocks in
// which the recent interactions have been 0
func updateHostHistoricInteractions(host *modules.HostDBEntry, bh types.BlockHeight) {
// Check that the last historic update was not in the future.
if host.LastHistoricUpdate >= bh {
// The hostdb may be performing a rescan, or maybe no time has passed
// since the last update, so there is nothing to do.
return
}
passedTime := bh - host.LastHistoricUpdate
// tmp float64 values for more accurate decay
hsi := host.HistoricSuccessfulInteractions
hfi := host.HistoricFailedInteractions
// Apply the decay of a single block.
decay := historicInteractionDecay
hsi *= decay
hfi *= decay
// Apply the recent interactions of that single block. Recent interactions
// cannot represent more than recentInteractionWeightLimit of historic
// interactions, unless there are less than historicInteractionDecayLimit
// total interactions, and then the recent interactions cannot count for
// more than recentInteractionWeightLimit of the decay limit.
rsi := float64(host.RecentSuccessfulInteractions)
rfi := float64(host.RecentFailedInteractions)
if hsi+hfi > historicInteractionDecayLimit {
if rsi+rfi > recentInteractionWeightLimit*(hsi+hfi) {
adjustment := recentInteractionWeightLimit * (hsi + hfi) / (rsi + rfi)
rsi *= adjustment
rfi *= adjustment
}
} else {
if rsi+rfi > recentInteractionWeightLimit*historicInteractionDecayLimit {
adjustment := recentInteractionWeightLimit * historicInteractionDecayLimit / (rsi + rfi)
rsi *= adjustment
rfi *= adjustment
}
}
hsi += rsi
hfi += rfi
// Apply the decay of the rest of the blocks
if passedTime > 1 && hsi+hfi > historicInteractionDecayLimit {
decay := math.Pow(historicInteractionDecay, float64(passedTime-1))
hsi *= decay
hfi *= decay
}
// Set new values
host.HistoricSuccessfulInteractions = hsi
host.HistoricFailedInteractions = hfi
host.RecentSuccessfulInteractions = 0
host.RecentFailedInteractions = 0
// Update the time of the last update
host.LastHistoricUpdate = bh
}
// IncrementSuccessfulInteractions increments the number of successful
// interactions with a host for a given key
func (hdb *HostDB) IncrementSuccessfulInteractions(key types.SiaPublicKey) {
hdb.mu.Lock()
defer hdb.mu.Unlock()
// Fetch the host.
host, haveHost := hdb.hostTree.Select(key)
if !haveHost {
return
}
// Update historic values if necessary
updateHostHistoricInteractions(&host, hdb.blockHeight)
// Increment the successful interactions
host.RecentSuccessfulInteractions++
hdb.hostTree.Modify(host)
}
// IncrementFailedInteractions increments the number of failed interactions with
// a host for a given key
func (hdb *HostDB) IncrementFailedInteractions(key types.SiaPublicKey) {
hdb.mu.Lock()
defer hdb.mu.Unlock()
// Fetch the host.
host, haveHost := hdb.hostTree.Select(key)
if !haveHost || !hdb.online {
// If we are offline it probably wasn't the host's fault
return
}
// Update historic values if necessary
updateHostHistoricInteractions(&host, hdb.blockHeight)
// Increment the failed interactions
host.RecentFailedInteractions++
hdb.hostTree.Modify(host)
}
......@@ -92,6 +92,28 @@ func (hdb *HostDB) collateralAdjustments(entry modules.HostDBEntry) float64 {
return weight
}
// interactionAdjustments determine the penalty to be applied to a host for the
// historic and currnet interactions with that host. This function focuses on
// historic interactions and ignores recent interactions.
func (hdb *HostDB) interactionAdjustments(entry modules.HostDBEntry) float64 {
// Give the host a baseline of 30 successful interactions and two failed
// interactions. This gives the host a baseline if we've had few
// interactions with them. The two failed interactions will become
// irrelevant after sufficient interactions with the host.
hsi := entry.HistoricSuccessfulInteractions + 30
hfi := entry.HistoricFailedInteractions + 1
// Determine the intraction ratio based off of the historic interactions.
ratio := float64(hsi) / float64(hsi+hfi)
// Raise the ratio to the 15th power and return that. The exponentiation is
// very high because the renter will already intentionally avoid hosts that
// do not have many successful interactions, meaning that the bad points do
// not rack up very quickly. We want to signal a bad score for the host
// nonetheless.
return math.Pow(ratio, 15)
}
// priceAdjustments will adjust the weight of the entry according to the prices
// that it has set.
func (hdb *HostDB) priceAdjustments(entry modules.HostDBEntry) float64 {
......@@ -345,14 +367,16 @@ func (hdb *HostDB) uptimeAdjustments(entry modules.HostDBEntry) float64 {
// the host database entry.
func (hdb *HostDB) calculateHostWeight(entry modules.HostDBEntry) types.Currency {
collateralReward := hdb.collateralAdjustments(entry)
interactionPenalty := hdb.interactionAdjustments(entry)
lifetimePenalty := hdb.lifetimeAdjustments(entry)
pricePenalty := hdb.priceAdjustments(entry)
storageRemainingPenalty := storageRemainingAdjustments(entry)
versionPenalty := versionAdjustments(entry)
lifetimePenalty := hdb.lifetimeAdjustments(entry)
uptimePenalty := hdb.uptimeAdjustments(entry)
versionPenalty := versionAdjustments(entry)
// Combine the adjustments.
fullPenalty := collateralReward * pricePenalty * storageRemainingPenalty * versionPenalty * lifetimePenalty * uptimePenalty
fullPenalty := collateralReward * interactionPenalty * lifetimePenalty *
pricePenalty * storageRemainingPenalty * uptimePenalty * versionPenalty
// Return a types.Currency.
weight := baseWeight.MulFloat(fullPenalty)
......@@ -384,17 +408,22 @@ func (hdb *HostDB) calculateConversionRate(score types.Currency) float64 {
// EstimateHostScore takes a HostExternalSettings and returns the estimated
// score of that host in the hostdb, assuming no penalties for age or uptime.
func (hdb *HostDB) EstimateHostScore(entry modules.HostDBEntry) modules.HostScoreBreakdown {
// Grab the adjustments. Age, and uptime penalties are set to '1', to
// assume best behavior from the host.
collateralReward := hdb.collateralAdjustments(entry)
pricePenalty := hdb.priceAdjustments(entry)
storageRemainingPenalty := storageRemainingAdjustments(entry)
versionPenalty := versionAdjustments(entry)
fullPenalty := collateralReward * pricePenalty * storageRemainingPenalty * versionPenalty
// Combine into a full penalty, then determine the resulting estimated
// score.
fullPenalty := collateralReward * pricePenalty * storageRemainingPenalty * versionPenalty
estimatedScore := baseWeight.MulFloat(fullPenalty)
if estimatedScore.IsZero() {
estimatedScore = types.NewCurrency64(1)
}
// Compile the estimates into a host score breakdown.
return modules.HostScoreBreakdown{
Score: estimatedScore,
ConversionRate: hdb.calculateConversionRate(estimatedScore),
......@@ -423,6 +452,7 @@ func (hdb *HostDB) ScoreBreakdown(entry modules.HostDBEntry) modules.HostScoreBr
AgeAdjustment: hdb.lifetimeAdjustments(entry),
BurnAdjustment: 1,
CollateralAdjustment: hdb.collateralAdjustments(entry),
InteractionAdjustment: hdb.interactionAdjustments(entry),
PriceAdjustment: hdb.priceAdjustments(entry),
StorageRemainingAdjustment: storageRemainingAdjustments(entry),
UptimeAdjustment: hdb.uptimeAdjustments(entry),
......
......@@ -35,6 +35,21 @@ var (
}
)
// printScoreBreakdown prints the score breakdown of a host, provided the info.
func printScoreBreakdown(info *api.HostdbHostsGET) {
fmt.Println("\n Score Breakdown:")
w := tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
fmt.Fprintf(w, "\t\tAge:\t %.3f\n", info.ScoreBreakdown.AgeAdjustment)
fmt.Fprintf(w, "\t\tBurn:\t %.3f\n", info.ScoreBreakdown.BurnAdjustment)
fmt.Fprintf(w, "\t\tCollateral:\t %.3f\n", info.ScoreBreakdown.CollateralAdjustment)
fmt.Fprintf(w, "\t\tInteraction:\t %.3f\n", info.ScoreBreakdown.InteractionAdjustment)
fmt.Fprintf(w, "\t\tPrice:\t %.3f\n", info.ScoreBreakdown.PriceAdjustment*1e6)
fmt.Fprintf(w, "\t\tStorage:\t %.3f\n", info.ScoreBreakdown.StorageRemainingAdjustment)
fmt.Fprintf(w, "\t\tUptime:\t %.3f\n", info.ScoreBreakdown.UptimeAdjustment)
fmt.Fprintf(w, "\t\tVersion:\t %.3f\n", info.ScoreBreakdown.VersionAdjustment)
w.Flush()
}
func hostdbcmd() {
if !hostdbVerbose {
info := new(api.HostdbActiveGET)
......@@ -285,16 +300,7 @@ func hostdbviewcmd(pubkey string) {
fmt.Fprintln(w, "\t\tVersion:\t", info.Entry.Version)
w.Flush()
fmt.Println("\n Score Breakdown:")
w = tabwriter.NewWriter(os.Stdout, 0, 0, 2, ' ', 0)
fmt.Fprintf(w, "\t\tAge:\t %.3f\n", info.ScoreBreakdown.AgeAdjustment)
fmt.Fprintf(w, "\t\tBurn:\t %.3f\n", info.ScoreBreakdown.BurnAdjustment)
fmt.Fprintf(w, "\t\tCollateral:\t %.3f\n", info.ScoreBreakdown.CollateralAdjustment)
fmt.Fprintf(w, "\t\tPrice:\t %.3f\n", info.ScoreBreakdown.PriceAdjustment*1e6)
fmt.Fprintf(w, "\t\tStorage:\t %.3f\n", info.ScoreBreakdown.StorageRemainingAdjustment)
fmt.Fprintf(w, "\t\tUptime:\t %.3f\n", info.ScoreBreakdown.UptimeAdjustment)
fmt.Fprintf(w, "\t\tVersion:\t %.3f\n", info.ScoreBreakdown.VersionAdjustment)
w.Flush()
printScoreBreakdown(info)
// Compute the total measured uptime and total measured downtime for this
// host.
......
......@@ -335,22 +335,27 @@ func rentercontractsviewcmd(cid string) {
for _, rc := range rc.Contracts {
if rc.ID.String() == cid {
var hostInfo api.HostdbHostsGET
err = getAPI("/hostdb/hosts/"+rc.HostPublicKey.String(), &hostInfo)
if err != nil {
die("Could not fetch details of host: ", err)
}
fmt.Printf(`
Contract %v
Host: %v (Public Key: %v)
Host: %v (Public Key: %v)
Start Height: %v
End Height: %v
Start Height: %v
End Height: %v
Total cost: %v (Fees: %v)
Funds Allocated: %v
Upload Spending: %v
Storage Spending: %v
Download Spending: %v
Remaining Funds: %v
Total cost: %v (Fees: %v)
Funds Allocated: %v
Upload Spending: %v
Storage Spending: %v
Download Spending: %v
Remaining Funds: %v
File Size: %v
`, rc.ID, rc.NetAddress, rc.HostPublicKey, rc.StartHeight, rc.EndHeight,
File Size: %v
`, rc.ID, rc.NetAddress, rc.HostPublicKey.String(), rc.StartHeight, rc.EndHeight,
currencyUnits(rc.TotalCost),
currencyUnits(rc.Fees),
currencyUnits(rc.TotalCost.Sub(rc.Fees)),
......@@ -359,6 +364,8 @@ File Size: %v
currencyUnits(rc.DownloadSpending),
currencyUnits(rc.RenterFunds),
filesizeUnits(int64(rc.Size)))
printScoreBreakdown(&hostInfo)
return
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment